browser/components/places/content/places.xml
author hg@mozilla.com
Thu, 22 Mar 2007 10:30:00 -0700
changeset 1 9b2a99adc05e53cd4010de512f50118594756650
child 2499 f0e4a903375d418df5a46ab2d483ba1c430772d1
permissions -rwxr-xr-x
Free the (distributed) Lizard! Automatic merge from CVS: Module mozilla: tag HG_REPO_INITIAL_IMPORT at 22 Mar 2007 10:30 PDT,

<?xml version="1.0"?>

<bindings id="placesBindings"
          xmlns="http://www.mozilla.org/xbl"
          xmlns:xbl="http://www.mozilla.org/xbl"
          xmlns:html="http://www.w3.org/1999/xhtml"
          xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
  
  <!-- XXXben - replace this upon checkin by allowing generic textboxes to show
                arbitrary content before the html:input itself using a 
                <children/> -->
  <binding id="textbox-timed-arbitrary" 
           extends="chrome://global/content/bindings/textbox.xml#timed-textbox">
    <resources>
      <stylesheet src="chrome://browser/skin/places/places.css"/>
    </resources>
    <content>
      <children/>
      <xul:hbox class="textbox-input-box" flex="1" xbl:inherits="context">
        <html:input class="textbox-input" flex="1" anonid="input"
                    xbl:inherits="onfocus,onblur,value,type,maxlength,disabled,size,readonly,tabindex,accesskey"/>
      </xul:hbox>
      <xul:hbox>
        <xul:button class="textbox-input-closebutton"/>
      </xul:hbox>
    </content>
    <implementation>
      <property name="grayText" 
                onget="return this.getAttribute('grayText');"
                onset="this.setAttribute('grayText', val); return val;"/>
      <method name="reset">
        <body><![CDATA[ 
          this.setAttribute("empty", "true");
          this.removeAttribute("filtered");
          this.value = this.grayText;
        ]]></body>
      </method>
      <method name="_resetInternal">
        <body><![CDATA[ 
          this.value = "";
          this.removeAttribute("empty");
          this.removeAttribute("filtered");
        ]]></body>
      </method>
      <method name="_fireEvent">
        <parameter name="type"/>
        <body><![CDATA[ 
          var event = document.createEvent("Events");
          event.initEvent(type, true, true);
          
          var cancel = !this.dispatchEvent(event);
          var handler = this.getAttribute("on" + type);
          if (handler) {
            var handlerFunction = new Function("event", handler);
            var handlerCanceled = handlerFunction(event) === false;
            if (handlerCanceled)
              cancel = handlerCanceled;
          }
          return !cancel;
        ]]></body>
      </method>
      <method name="_clearReliably">
        <body><![CDATA[ 
          // This method exists to clear the field's text. The reason this does
          // not work reliably is unknown... appears to be some kind of event
          // handling synchronization issue. We need to clear the field 
          // asynchronously. 
          var field = this;
          setTimeout(function() {
                      field.value = "";
                      field.removeAttribute("empty");
                     }, 0);
        ]]></body>
      </method>
      <method name="onFocus">
        <body><![CDATA[
          if (this.hasAttribute("empty"))
            this._clearReliably();
          else
            this.select();
        ]]></body>
      </method>
      <method name="onBlur">
        <body><![CDATA[ 
        if (this.hasAttribute("empty") || !this.value)
          this.reset();
        ]]></body>
      </method>
    </implementation>
    <handlers>
      <handler event="focus" phase="capturing"><![CDATA[ 
        if (event.originalTarget.className != "textbox-input-closebutton")
          this.onFocus();
      ]]></handler>
      <handler event="blur" phase="capturing"><![CDATA[ 
        if (event.originalTarget.className != "textbox-input-closebutton")
          this.onBlur();
      ]]></handler>
      <handler event="click"><![CDATA[ 
        if (event.originalTarget.className == "textbox-input-closebutton") {
          this._resetInternal();
          this._fireEvent("reset");
        }
      ]]></handler>
      <handler event="keypress" keycode="VK_ESCAPE"><![CDATA[ 
        this._resetInternal();
        this._fireEvent("reset");
      ]]></handler>
    </handlers>
  </binding>
  
  <binding id="command-button" extends="chrome://global/content/bindings/button.xml#button">
    <implementation>
      <method name="updateActiveView">
        <body><![CDATA[ 
          if (this.hasAttribute("view"))
            PlacesController.activeView = document.getElementById(this.getAttribute("view"));
        ]]></body>
      </method>
    </implementation>
    <handlers>
      <handler event="click" button="0" action="this.updateActiveView();"/>
      <handler event="keypress" keycode="VK_SPACE" action="this.updateActiveView();"/>
    </handlers>
  </binding>
  
  <binding id="filter-button" extends="chrome://global/content/bindings/button.xml#menu">
    <content>
      <xul:stack flex="1" class="box-inherit button-box">
        <xul:image class="button-icon" xbl:inherits="src=image"/>
        <xul:dropmarker class="button-menu-dropmarker" xbl:inherits="open,disabled"/>
      </xul:stack>
      <children includes="menupopup"/>
    </content>
    <handlers>
      <handler event="command"><![CDATA[
        PlacesSearchBox.filterCollection = event.target.getAttribute("value");
      ]]></handler>
    </handlers>
  </binding>

  <binding id="calendar">
    <content>
      <xul:vbox class="calendar-box">
        <xul:hbox class="calendar-header">
          <xul:label anonid="prevmonth" class="calendar-month-jump">&#xab;</xul:label>
          <xul:label anonid="monthtitle" class="calendar-month-title" flex="1"/>
          <xul:label anonid="nextmonth" class="calendar-month-jump">&#xbb;</xul:label>
        </xul:hbox>
        <xul:tooltip anonid="calendartooltip">
          <!-- FIXME -->
        </xul:tooltip>
        <xul:grid anonid="calendargrid" class="calendar-grid">
         <xul:columns><xul:column flex="1"/><xul:column flex="1"/><xul:column flex="1"/><xul:column flex="1"/><xul:column flex="1"/><xul:column flex="1"/><xul:column flex="1"/></xul:columns>
          <xul:rows>
            <xul:row class="calendar-day-header">
              <xul:label anonid="calendarhead0"></xul:label>
              <xul:label anonid="calendarhead1"></xul:label>
              <xul:label anonid="calendarhead2"></xul:label>
              <xul:label anonid="calendarhead3"></xul:label>
              <xul:label anonid="calendarhead4"></xul:label>
              <xul:label anonid="calendarhead5"></xul:label>
              <xul:label anonid="calendarhead6"></xul:label>
            </xul:row>
            <!-- The "00" is so that the table has the correct dimensions (most
                 numbers are the same width) when it is first displayed. The
                 default definition for "calendar-day" should have the same color
                 fore- and background so you won't see this. -->
            <xul:row>
              <xul:label anonid="calendar0" class="calendar-day" tooltip="calendartooltip">00</xul:label>
              <xul:label anonid="calendar1" class="calendar-day" tooltip="calendartooltip">00</xul:label>
              <xul:label anonid="calendar2" class="calendar-day" tooltip="calendartooltip">00</xul:label>
              <xul:label anonid="calendar3" class="calendar-day" tooltip="calendartooltip">00</xul:label>
              <xul:label anonid="calendar4" class="calendar-day" tooltip="calendartooltip">00</xul:label>
              <xul:label anonid="calendar5" class="calendar-day" tooltip="calendartooltip">00</xul:label>
              <xul:label anonid="calendar6" class="calendar-day" tooltip="calendartooltip">00</xul:label>
            </xul:row>
            <xul:row>
              <xul:label anonid="calendar7" class="calendar-day" tooltip="calendartooltip">00</xul:label>
              <xul:label anonid="calendar8" class="calendar-day" tooltip="calendartooltip">00</xul:label>
              <xul:label anonid="calendar9" class="calendar-day" tooltip="calendartooltip">00</xul:label>
              <xul:label anonid="calendar10" class="calendar-day" tooltip="calendartooltip">00</xul:label>
              <xul:label anonid="calendar11" class="calendar-day" tooltip="calendartooltip">00</xul:label>
              <xul:label anonid="calendar12" class="calendar-day" tooltip="calendartooltip">00</xul:label>
              <xul:label anonid="calendar13" class="calendar-day" tooltip="calendartooltip">00</xul:label>
            </xul:row>
            <xul:row>
              <xul:label anonid="calendar14" class="calendar-day" tooltip="calendartooltip">00</xul:label>
              <xul:label anonid="calendar15" class="calendar-day" tooltip="calendartooltip">00</xul:label>
              <xul:label anonid="calendar16" class="calendar-day" tooltip="calendartooltip">00</xul:label>
              <xul:label anonid="calendar17" class="calendar-day" tooltip="calendartooltip">00</xul:label>
              <xul:label anonid="calendar18" class="calendar-day" tooltip="calendartooltip">00</xul:label>
              <xul:label anonid="calendar19" class="calendar-day" tooltip="calendartooltip">00</xul:label>
              <xul:label anonid="calendar20" class="calendar-day" tooltip="calendartooltip">00</xul:label>
            </xul:row>
            <xul:row>
              <xul:label anonid="calendar21" class="calendar-day" tooltip="calendartooltip">00</xul:label>
              <xul:label anonid="calendar22" class="calendar-day" tooltip="calendartooltip">00</xul:label>
              <xul:label anonid="calendar23" class="calendar-day" tooltip="calendartooltip">00</xul:label>
              <xul:label anonid="calendar24" class="calendar-day" tooltip="calendartooltip">00</xul:label>
              <xul:label anonid="calendar25" class="calendar-day" tooltip="calendartooltip">00</xul:label>
              <xul:label anonid="calendar26" class="calendar-day" tooltip="calendartooltip">00</xul:label>
              <xul:label anonid="calendar27" class="calendar-day" tooltip="calendartooltip">00</xul:label>
            </xul:row>
            <xul:row>
              <xul:label anonid="calendar28" class="calendar-day" tooltip="calendartooltip">00</xul:label>
              <xul:label anonid="calendar29" class="calendar-day" tooltip="calendartooltip">00</xul:label>
              <xul:label anonid="calendar30" class="calendar-day" tooltip="calendartooltip">00</xul:label>
              <xul:label anonid="calendar31" class="calendar-day" tooltip="calendartooltip">00</xul:label>
              <xul:label anonid="calendar32" class="calendar-day" tooltip="calendartooltip">00</xul:label>
              <xul:label anonid="calendar33" class="calendar-day" tooltip="calendartooltip">00</xul:label>
              <xul:label anonid="calendar34" class="calendar-day" tooltip="calendartooltip">00</xul:label>
            </xul:row>
            <xul:row>
              <xul:label anonid="calendar35" class="calendar-day" tooltip="calendartooltip">00</xul:label>
              <xul:label anonid="calendar36" class="calendar-day" tooltip="calendartooltip">00</xul:label>
              <xul:label anonid="calendar37" class="calendar-day" tooltip="calendartooltip">00</xul:label>
              <xul:label anonid="calendar38" class="calendar-day" tooltip="calendartooltip">00</xul:label>
              <xul:label anonid="calendar39" class="calendar-day" tooltip="calendartooltip">00</xul:label>
              <xul:label anonid="calendar40" class="calendar-day" tooltip="calendartooltip">00</xul:label>
              <xul:label anonid="calendar41" class="calendar-day" tooltip="calendartooltip">00</xul:label>
            </xul:row>
          </xul:rows>
        </xul:grid>
      </xul:vbox>
    </content>
    <implementation>
      <constructor><![CDATA[
        var grid = document.getAnonymousElementByAttribute(this, "anonid",
                                                           "calendargrid");
        this._numCells = 42; // max number of cells displayable in the calendar
        this._cellPrefix = "calendar"; // value before the number in the ID of cells

        this._currentMonth = -1;
        this._currentYear = -1;
        this._cell0Date = null; // date for top left of calendar
        this._selectNothing = false;
        this._selectBegin = null;
        this._selectEnd = null;

        // localized stuff, FIXME: move somewhere else
        this._pref_firstDayOfWeek = 0; // 0 = Sunday, 1 = Monday
        this._pref_dayHeaders = ["S", "M", "T", "W", "T", "F", "S"];
        this._pref_shortMonthNames = ["Jan", "Feb", "Mar", "Apr", "May", "Jun",
                            "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"];

        // day headers
        for (var i = 0; i < 7; i ++) {
          var cell = document.getAnonymousElementByAttribute(this, "anonid",
                                                             "calendarhead" + i);
          cell.value = this._pref_dayHeaders[i];
        }

        // cell item
        var calendargrid = document.getAnonymousElementByAttribute(this, "anonid", "calendargrid");
        this._days = new Array(this._numCells);
        this._selected = new Array(this._numCells);
        for (var i = 0; i < this._numCells; i ++) {
          this._days[i] = document.getAnonymousElementByAttribute(this, "anonid", this._cellPrefix + i);
          this._selected[i] = false;
        }

        // month navigation hooks
        var myself = this;
        document.getAnonymousElementByAttribute(this, "anonid", "prevmonth").
          addEventListener("click", function() { myself.jumpMonth(-1); }, false);
        document.getAnonymousElementByAttribute(this, "anonid", "nextmonth").
          addEventListener("click", function() { myself.jumpMonth(1); }, false);

        // day selection hooks
        calendargrid.addEventListener("mousedown", function(event) { myself.mouseDown(event); }, false);
        calendargrid.addEventListener("mouseup", function(event) { myself.mouseUp(event); }, false);
        calendargrid.addEventListener("mousemove", function(event) { myself.mouseMove(event); }, false);

        this.visibleMonth = new Date(); // today
      ]]></constructor>

      <property name="visibleMonth">
        <getter>
          return new Date(this._visibleMonth);
        </getter>
        <setter>
          this._visibleMonth = new Date(val.getFullYear(), val.getMonth(), 1);
          this.drawMonth();
        </setter>
      </property>

      <property name="beginrange">
        <getter>
          if (! this._selectBegin)
            return null;
          else
            return new Date(this._selectBegin);
        </getter>
        <setter>
          this._selectNothing = false;
          this._selectBegin = val;
          this.updateSelection(this._selectBegin, this._selectEnd);
          this.fireRangeEvent();
        </setter>
      </property>
      <property name="endrange">
        <getter>
          if (! this._selectEnd)
            return null;
          else
            return new Date(this._selectEnd);
        </getter>
        <setter>
          this._selectNothing = false;
          this._selectEnd = val;
          this.updateSelection(this._selectBegin, this._selectEnd);
          this.fireRangeEvent();
        </setter>
      </property>

      <!-- Use this to set the range at once. It will be more efficient than
           setting begin and end independently since there will be only one
           redraw. Set updateVisible to have the calendar change the active
           month if necessary. -->
      <method name="setRange">
        <parameter name="begin"/>
        <parameter name="end"/>
        <parameter name="updateVisible"/>
        <body><![CDATA[
          if (updateVisible && end) {
            // this just tries to make the end range visible. If there is no
            // end range, we don't do anything. We might want to set the visible
            // month to the begin range in this case, but I don't think this
            // situation arises in practice.
            var daysToEnd = this.daysBetweenDates(end, this._cell0Date);
            if (daysToEnd < 0 || daysToEnd >= this._numCells) {
              // reselect month for end range
              this.visibleMonth = end;
            }
          }
          this._selectNothing = false;
          this.updateSelection(begin, end);
          this.fireRangeEvent();
        ]]></body>
      </method>

      <!-- Normally, null begin and end means select all, so you can set
           this if you don't want anything selected.-->
      <property name="selectNothing">
        <getter>
          return this._selectNothing;
        </getter>
        <setter>
          this._selectNothing = val;
        </setter>
      </property>

      <!--===== jumpMonth =====-->
      <method name="jumpMonth">
        <parameter name="relative"/>
        <body><![CDATA[
          var newMonth = this._visibleMonth.getMonth() + relative;
          var newYear = this._visibleMonth.getFullYear() + Math.floor(newMonth / 12);
          newMonth = (newMonth + 12) % 12;
          this._visibleMonth = new Date(newYear, newMonth, 1);
          this.drawMonth();
        ]]></body>
      </method>

      <!--===== mouseDown =====-->
      <method name="mouseDown">
        <parameter name="event"/>
        <body><![CDATA[
          var date = this.cellToDate(event.target.getAttribute("anonid"));
          if (! date)
            return;
          this._selectNothing = false;
          this._dragging = true;
          this._mouseDownOn = date;
          this.updateSelection(date, date);
        ]]></body>
      </method>

      <!--===== mouseUp =====-->
      <method name="mouseUp">
        <parameter name="event"/>
        <body><![CDATA[
          this._dragging = false;
          this.fireRangeEvent();
        ]]></body>
      </method>

      <!--===== mouseMove =====-->
      <method name="mouseMove">
        <parameter name="event"/>
        <body><![CDATA[
          if (! this._dragging)
            return;
          var date = this.cellToDate(event.target.getAttribute("anonid"));
          if (! date)
            return;
          this.updateSelection(this._mouseDownOn, date);
        ]]></body>
      </method>

      <!--===== updateSelection =====-->
      <method name="updateSelection">
        <parameter name="begin"/>
        <parameter name="end"/>
        <body><![CDATA[
          var realBegin = begin;
          var realEnd = end;
          if (begin && end && begin.getTime() > end.getTime()) {
            this._selectBegin = end;
            this._selectEnd = begin;
          } else {
            this._selectBegin = begin;
            this._selectEnd = end;
          }
          this.drawSelection();
        ]]></body>
      </method>

      <!--===== daysBetweenDates =====-->
      <method name="daysBetweenDates">
        <parameter name="a"/>
        <parameter name="b"/>
        <body>
          var msDiff = a.getTime() - b.getTime();
          return Math.floor(msDiff / 86400000); // 1000ms/s * 60s/m * 60m/h * 24h/d = 86400000ms/d
        </body>
      </method>

      <!--===== suppressRangeEvents =====-->
      <field name="suppressRangeEvents">false</field>
      
      <!--===== fireRangeEvent =====-->
      <method name="fireRangeEvent"><body><![CDATA[
        if (this.suppressRangeEvents)
          return true;
        var event = document.createEvent("Events");
        event.initEvent("selectionchanged", false, true);

        // handle dom event handlers
        var noCancel = this.dispatchEvent(event);

        // handle any xml attribute event handlers
        var handler = this.getAttribute("onselectionchanged");
        if (handler != "") {
          var fn = new Function("event", handler);
          var returned = fn(event);
          if (returned == false)
            noCancel = false;
        }
        return noCancel;
      ]]></body></method>

      <!--===== drawSelection =====-->
      <method name="drawSelection"><body><![CDATA[
        var beginIndex;
        var endIndex;
        if (this._selectNothing) {
          beginIndex = -1;
          endIndex = -1;
        } else {
          if (! this._selectBegin) {
            beginIndex = 0;
          } else {
            beginIndex = this.daysBetweenDates(this._selectBegin, this._cell0Date);
          }
          if (! this._selectEnd) {
            endIndex = this._numCells - 1;
          } else {
            endIndex = this.daysBetweenDates(this._selectEnd, this._cell0Date);
          }
        }

        for (var i = 0; i < this._numCells; i ++) {
          var sel = (i >= beginIndex && i <= endIndex);
          if (sel != this._selected[i]) {
            this._days[i].setAttribute("selected", sel ? "true" : "");
            this._selected[i] = sel;
          }
        }
      ]]></body></method>

      <!--===== cellToDate =====-->
      <method name="cellToDate">
        <parameter name="cellName"/>
        <body><![CDATA[
          if (! cellName)
            return null;
          var tail = cellName.substring(this._cellPrefix.length);
          if (tail[0] < "0" || tail[0] > '9') {
            return null;
          }
          var cellNumber = Number(tail);
          var d = new Date(this._cell0Date);
          d.setDate(d.getDate() + cellNumber);
          return d;
        ]]></body>
      </method>

      <!--===== drawMonth =====-->
      <method name="drawMonth"><body><![CDATA[

        var curCell = 0;
        var monthIndex = this._visibleMonth.getMonth();
        var yearNumber = this._visibleMonth.getFullYear();
        var firstOfMonth = new Date(yearNumber, monthIndex, 1);

        // update title
        document.getAnonymousElementByAttribute(this, "anonid", "monthtitle").value =
          this._pref_shortMonthNames[monthIndex] + " " + yearNumber;

        // first, fill in any days of the previous month in the first week
        var numPrefixDays = firstOfMonth.getDay() - this._pref_firstDayOfWeek;
        var curDay = firstOfMonth;
        curDay.setDate(firstOfMonth.getDate() - numPrefixDays);
        this._cell0Date = new Date(curDay); // save the first cell
        for (var i = 0; i < numPrefixDays; i ++) {
          var cell = document.getAnonymousElementByAttribute(this, "anonid", this._cellPrefix + curCell);
          cell.setAttribute("month", "other");
          cell.value = curDay.getDate();
          curCell ++;
          curDay.setDate(curDay.getDate() + 1);
        }

        // now fill in the rest of this month
        while (curDay.getMonth() == monthIndex) {
          var cell = document.getAnonymousElementByAttribute(this, "anonid", this._cellPrefix + curCell);
          cell.setAttribute("month", "this");
          cell.value = curDay.getDate();
          curCell ++;
          curDay.setDate(curDay.getDate() + 1);
        }

        // fill out the end of this week with next month
        while (curDay.getDay() != this._pref_firstDayOfWeek) {
          var cell = document.getAnonymousElementByAttribute(this, "anonid", this._cellPrefix + curCell);
          cell.setAttribute("month", "other");
          cell.value = curDay.getDate();
          curCell ++;
          curDay.setDate(curDay.getDate() + 1);
        }

        // fill the bottom row with days from the next month
        while (curCell < this._numCells) {
          var cell = document.getAnonymousElementByAttribute(this, "anonid", this._cellPrefix + curCell);
          cell.setAttribute("month", "other");
          cell.value = curDay.getDate();
          curCell ++;
          curDay.setDate(curDay.getDate() + 1);
        }

        this.drawSelection();
      ]]></body></method>

    </implementation>
  </binding> <!-- end calendar -->

  
</bindings>