<?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">
  
  <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>

#ifdef PLACES_QUERY_BUILDER
  <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 -->
#endif
  
</bindings>
