<?xml version="1.0" encoding="UTF-8"?>
<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.0. If a copy of the MPL was not distributed with this
   - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
<!DOCTYPE dialog [
  <!ENTITY % dtd1 SYSTEM "chrome://global/locale/global.dtd" > %dtd1;
  <!ENTITY % dtd2 SYSTEM "chrome://calendar/locale/calendar-event-dialog.dtd"> %dtd2;
]>

<bindings id="calendar-widgets"
          xmlns="http://www.mozilla.org/xbl"
          xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
          xmlns:xbl="http://www.mozilla.org/xbl">

  <!-- Unfortunately, the normal menulist binding doesn't allow a panel child
       This binding replaces the usual menulist to allow a panel -->
  <binding id="panel-menulist" extends="chrome://global/content/bindings/menulist.xml#menulist">
    <content sizetopopup="pref">
      <xul:hbox class="menulist-label-box" flex="1">
        <xul:image class="menulist-icon" xbl:inherits="src=image,src"/>
        <xul:label class="menulist-label" xbl:inherits="value=label,crop,accesskey" crop="right" flex="1"/>
      </xul:hbox>
      <xul:dropmarker class="menulist-dropmarker" type="menu" xbl:inherits="disabled,open"/>
      <children includes="menupopup|panel"/>
    </content>
  </binding>

   <binding id="category-panel" extends="chrome://global/content/bindings/popup.xml#panel">
    <resources>
      <stylesheet src="chrome://calendar/skin/widgets/calendar-widgets.css"/>
    </resources>
    <content>
        <xul:textbox  anonid="category-textbox"
                      class="categories-textbox"
                      type="search"
                      searchbutton="true"
                      placeholder="&event.categories.textbox.label;"
                      oncommand="document.getBindingParent(this).addNewCategory();"
                      flex="1"/>
        <xul:listbox anonid="categories-listbox"
                   class="categories-listbox"
                   onselect="document.getBindingParent(this).selectCategory()"
                   selType="multiple"
                   >
        <children/>
      </xul:listbox>
    </content>
    <implementation>
      <field name="_maxCount">0</field>
      <property name="categories" readonly="true">
        <getter><![CDATA[
            let categoryListbox = document.getAnonymousElementByAttribute(this, "anonid", "categories-listbox");
            if (this.maxCount == 1) {
                let selectedItem = categoryListbox.selectedItem;
                return selectedItem ? [selectedItem.getAttribute("value")] : [];
            } else {
                let checkedNodes = categoryListbox.getElementsByAttribute("checked", "true");
                let sliceEnd = this.maxCount > 0 ? this.maxCount : checkedNodes.length;
                return Array.slice(checkedNodes, 0, sliceEnd)
                            .map(x => x.getAttribute("value"));
            }
        ]]></getter>
      </property>
      <property name="maxCount">
        <getter><![CDATA[
            return this._maxCount;
        ]]></getter>
        <setter><![CDATA[
            if (this._maxCount != val) {
                this._maxCount = val;
                this.setupSelection();
            }
        ]]></setter>
      </property>
      <method name="selectCategory">
        <body><![CDATA[
            this.setupSelection();
            if (this.maxCount == 1) {
                this.hidePopup();
            }
        ]]></body>
      </method>
      <method name="setupSelection">
        <body><![CDATA[
            let categoryListbox = document.getAnonymousElementByAttribute(this, "anonid", "categories-listbox");
            categoryListbox.setAttribute("seltype", this.maxCount == 1 ? "single" : "multiple");

            if (this.maxCount == 1) {
                for (let node of categoryListbox.childNodes) {
                    // Single selection doesn't have checkboxes
                    node.removeAttribute("type");

                    // Even though we have single select, these may be checked
                    // in case the user switches between calendars that support
                    // one vs multiple categories. Uncheck the other nodes to
                    // make sure the UX is not weird.
                    setBooleanAttribute(node, "checked", node == categoryListbox.selectedItem);
                }
            } else {
                let categoryTextbox = document.getAnonymousElementByAttribute(this, "anonid", "category-textbox");
                let maxCountReached = this.maxCount > 0 && this.categories.length == this.maxCount;
                setBooleanAttribute(categoryTextbox, "disabled", maxCountReached);

                for (let node of categoryListbox.childNodes) {
                    // Multiselect has checkboxes
                    node.setAttribute("type", "checkbox");

                    if (maxCountReached && node.getAttribute("checked") != "true") {
                        // If the maxcount is reached, disable all unchecked items
                        node.setAttribute("disabled", "true");
                    } else if (!maxCountReached) {
                        // If its not reached, remove the disabled attribute
                        node.removeAttribute("disabled");
                    }
                }
            }
        ]]></body>
      </method>
      <method name="insertCategory">
        <parameter name="category" />
        <parameter name="categories" />
        <parameter name="categoryListbox" />
        <parameter name="compare" />
        <body><![CDATA[
            let newIndex = cal.binaryInsert(categories, category, compare, true);
            let item = categoryListbox.childNodes[Math.min(newIndex, categoryListbox.childNodes.length - 1)];

            if (!item || item.getAttribute("value") != category) {
                // The item doesn't exist, insert it at the correct spot.
                item = categoryListbox.insertItemAt(newIndex, category, category);

                if (this.maxCount != 1) {
                    item.setAttribute("type", "checkbox");
                }
            }

            item.setAttribute("checked", "true");
            return item;
          ]]></body>
      </method>
      <method name="addNewCategory">
      <body><![CDATA[
          let categoryListbox = document.getAnonymousElementByAttribute(this, "anonid", "categories-listbox");
          let categoryTextbox = document.getAnonymousElementByAttribute(this, "anonid", "category-textbox");
          let category = categoryTextbox.value;

          if (!category) {
            return;
          }

          let localeCollator = cal.createLocaleCollator();
          let compare = localeCollator.compareString.bind(localeCollator, 0);

          let children = categoryListbox.childNodes;
          let categories = [];
          for (var i = 0; i < children.length; i++) {
            categories.push(children[i].label);
          }

          let item = this.insertCategory(category, categories, categoryListbox, compare);
          categoryTextbox.value = "";

          if (this.maxCount == 1) {
            categoryListbox.selectedItem = item;
          } else {
            this.selectCategory();
          }

          categoryListbox.ensureElementIsVisible(item);
        ]]></body>
    </method>
      <method name="loadItem">
        <parameter name="aItem"/>
        <body><![CDATA[
          let categoryListbox = document.getAnonymousElementByAttribute(this, "anonid", "categories-listbox");
          let categoryList = getPrefCategoriesArray();

          cal.sortArrayByLocaleCollator(categoryList);

          removeChildren(categoryListbox);

          for (let cat of categoryList) {
              // First insert all categories from the prefs
              let item = categoryListbox.appendItem(cat, cat);
              item.setAttribute("type", "checkbox");
          }

          if (aItem) {
              let localeCollator = cal.createLocaleCollator();
              let compare = localeCollator.compareString.bind(localeCollator, 0);

              // Ensure the item's categories are in the list and they are checked.
              for (let cat of aItem.getCategories({})) {
                this.insertCategory(cat, categoryList, categoryListbox, compare);
              }
          }
        ]]></body>
      </method>
    </implementation>
  </binding>

  <binding id="doubleimage-toolbarbutton" extends="chrome://global/content/bindings/toolbarbutton.xml#toolbarbutton">
    <resources>
      <stylesheet src="chrome://calendar/skin/widgets/calendar-widgets.css"/>
    </resources>
    <content>
      <children includes="observes|template|menupopup|tooltip"/>
      <xul:image class="toolbarbutton-icon-begin" xbl:inherits="validate,src-begin=image,toolbarmode,buttonstyle"/>
      <xul:label class="toolbarbutton-text" crop="right" flex="1"
                 xbl:inherits="value=label,accesskey,crop,toolbarmode,buttonstyle"/>
      <xul:image class="toolbarbutton-icon-end" xbl:inherits="validate,src-end=image,toolbarmode,buttonstyle"/>
    </content>
  </binding>

  <binding id="todaypane-toolbarbutton" extends="chrome://calendar/content/widgets/calendar-widgets.xml#doubleimage-toolbarbutton">
    <content>
      <children includes="observes|template|menupopup|tooltip"/>
      <xul:stack pack="center" align="end">
        <xul:image class="toolbarbutton-icon-begin" xbl:inherits="validate,src-begin=image,toolbarmode,buttonstyle"/>
        <xul:label anonid="day-label" class="toolbarbutton-day-text"/>
      </xul:stack>
      <xul:label class="toolbarbutton-text" crop="right" flex="1"
                 xbl:inherits="value=label,accesskey,crop,toolbarmode,buttonstyle"/>
      <xul:image class="toolbarbutton-icon-end" xbl:inherits="validate,src-end=image,toolbarmode,buttonstyle"/>
    </content>
    <implementation>
      <constructor><![CDATA[
          this.setUpTodayDate();
      ]]></constructor>
      <method name="setUpTodayDate">
        <body><![CDATA[
            let dayNumber = calGetString("dateFormat", "day." + cal.now().day + ".number");
            document.getAnonymousElementByAttribute(this, "anonid", "day-label").value = dayNumber;
        ]]></body>
      </method>
    </implementation>
   </binding>

  <!-- this binding directly extends to a checkbox but is visualized as
       a treenode in a treecontrol-->
  <binding id="treenode-checkbox" extends="chrome://global/content/bindings/checkbox.xml#checkbox-baseline">
    <resources>
      <stylesheet src="chrome://calendar/skin/widgets/calendar-widgets.css"/>
    </resources>
  </binding>

  <!-- this binding directly extends to a xul:box element and automatically
       sets the "orient" attribute to "vertical" thus behaving like a vbox-->
  <binding id="modevbox" extends="chrome://calendar/content/widgets/calendar-widgets.xml#modebox">
    <resources>
      <stylesheet src="chrome://calendar/skin/widgets/calendar-widgets.css"/>
    </resources>
    <implementation>
      <constructor><![CDATA[
            this.setAttribute("orient", "vertical");
      ]]></constructor>
      </implementation>
  </binding>

  <!-- this binding directly extends to a xul:box element and automatically
       sets the "orient" attribute to "horizontal" thus behaving like a vbox-->
  <binding id="modehbox" extends="chrome://calendar/content/widgets/calendar-widgets.xml#modebox">
    <resources>
      <stylesheet src="chrome://calendar/skin/widgets/calendar-widgets.css"/>
    </resources>
    <implementation>
      <constructor><![CDATA[
         this.setAttribute("orient", "horizontal");
      ]]></constructor>
    </implementation>
  </binding>

  <!-- this binding directly extends to a xul:box element and enriches this with some functionality: It is designed
      to be displayed only 1) in given application modes (e.g "task" mode, "calendar" mode) and 2) only in relation
      to the "checked" attribute of command or a checkbox control.
    - The attribute "mode" denotes a coma-separated list of all modes that the binding should not be collapsed in,
      e.g. mode="calendar,task"
    - The attribute "broadcaster" points to the id of a broadcaster that is supposed to be notified (by the application)
      as soon as the mode changes. When this happens the modebox" will be notified and will check if it should
      collapse itself or not.
    - The attribute "refcontrol" points to a control either a "command", "checkbox" or a "treenode-checkbox" or other
      elements that support a "checked" attribute that is often used to denote whether a modebox is supposed to be
      displayed or not. If "refcontrol" is set to the id of a command you can there set the oncommend attribute like:
      "oncommand='document.getElementById('my-mode-pane').togglePane(event)'. In case it is a checkbox element or derived
      checkbox element this is done automatically by listening to the event "CheckboxChange";
      So if the current application mode is one of the modes listed in the "mode" attribute it is
      additionally verified if the xul-element denoted by "refcontrol" is checked or not. During runtime an attribute named
      "collapsedinmodes" with the collpsed modes comma-separated e.g. "mail,calendar,task. This attribute is also made
      persistent-->
  <binding id="modebox" extends="xul:box">
    <resources>
      <stylesheet src="chrome://calendar/skin/widgets/calendar-widgets.css"/>
    </resources>
    <implementation>
      <field name="mBroadcaster">null</field>;
      <field name="mModHandler">null</field>;
      <field name="mRefControl">null</field>;
      <field name="mControlHandler">null</field>;
      <constructor><![CDATA[
        if (this.hasAttribute("broadcaster")) {
            this.setAttribute("broadcaster", this.getAttribute("broadcaster"));
        }
        if (this.hasAttribute("refcontrol")) {
            this.mRefControl = document.getElementById(this.getAttribute("refcontrol"));
            if (this.mRefControl && ((this.mRefControl.localName == "treenode-checkbox") ||
                                  (this.mRefControl.localName == "checkbox"))){
                this.mControlHandler = {
                    binding: this,
                    handleEvent : function(aEvent, aHandled) {
                        return this.binding.onCheckboxStateChange(aEvent, this.binding);
                    }
                };
                this.mRefControl.addEventListener("CheckboxStateChange", this.mControlHandler, true);
            }
        }
      ]]>
      </constructor>
      <destructor><![CDATA[
          if (this.mBroadcaster) {
              this.mBroadcaster.removeEventListener("DOMAttrModified", this.mModHandler, true);
          }
          if (this.mRefControl) {
              this.mRefControl.removeEventListener("CheckboxStateChange", this.mControlHandler, true);
          }
      ]]></destructor>

      <property name="currentMode">
        <getter><![CDATA[
            if (this.mBroadcaster && this.mBroadcaster.hasAttribute("mode")) {
                return this.mBroadcaster.getAttribute("mode");
            } else {
                return "";
            }
        ]]></getter>
      </property>

      <method name="isVisible">
        <parameter name="aMode"/>
        <body><![CDATA[
            var lMode = aMode || this.currentMode;
            if (!this.isVisibleInMode(lMode)) {
                return false;
            }
            var collapsedModes = this.getAttribute("collapsedinmodes").split(",");
            return (!collapsedModes.includes(lMode));
        ]]></body>
      </method>

      <method name="setModeAttribute"> 
        <parameter name="aModeAttribute"/>
        <parameter name="aModeValue"/>
        <parameter name="amode"/>
        <body><![CDATA[
            if (this.hasAttribute(aModeAttribute)) {
                let lMode = amode || this.currentMode;
                let modeAttributeValues = this.getAttribute(aModeAttribute).split(",");
                let modes = this.getAttribute("mode").split(",");
                modeAttributeValues[modes.indexOf(lMode)] = aModeValue;
                this.setAttribute(aModeAttribute, modeAttributeValues.join(","));
            }
        ]]></body>
      </method>

      <method name="getModeAttribute">
        <parameter name="aModeAttribute"/>
        <parameter name="aAttribute"/>
        <parameter name="amode"/>
        <body><![CDATA[
            if (this.hasAttribute(aModeAttribute)) {
                let lMode = amode || this.currentMode;
                let modeAttributeValues = this.getAttribute(aModeAttribute).split(",");
                let modes = this.getAttribute("mode").split(",");
                return modeAttributeValues[modes.indexOf(lMode)];
            } else {
                return "";
            }
        ]]></body>
      </method>

      <method name="setVisible">
        <parameter name="aVisible"/>
        <parameter name="aPushModeCollapsedAttribute"/>
        <parameter name="aNotifyRefControl"/>
        <body><![CDATA[
            var notifyRefControl = ((aNotifyRefControl == null) || (aNotifyRefControl === true))
            var pushModeCollapsedAttribute = ((aPushModeCollapsedAttribute == null)
                                                 || (aPushModeCollapsedAttribute === true));
            var collapsedModes = [];
            var modeIndex = -1;
            var display = aVisible;
            var collapsedInMode = false;
            if (this.hasAttribute("collapsedinmodes")) {
                var collapsedModes = this.getAttribute("collapsedinmodes").split(",");
                var modeIndex = collapsedModes.indexOf(this.currentMode);
                collapsedInMode = (modeIndex > -1);
            }
            if ((aVisible === true) && (pushModeCollapsedAttribute == false)){
                display = (aVisible === true) && (!collapsedInMode);
            }

            setBooleanAttribute(this, "collapsed", (!display || !this.isVisibleInMode()));
            if (pushModeCollapsedAttribute) {
                if (!display) {
                    if (modeIndex == -1) {
                        collapsedModes.push(this.currentMode);
                        if (this.getAttribute("collapsedinmodes") == ",") {
                            collapsedModes.splice(0,2);
                        }
                    }
                } else {
                    if (modeIndex > -1) {
                        collapsedModes.splice(modeIndex, 1);
                        if (collapsedModes.join(",")  == "") {
                            collapsedModes[0]=",";
                        }
                    }
                }
                this.setAttribute("collapsedinmodes", collapsedModes.join(","));
                var id = this.getAttribute("id");
                if (id) {
                    document.persist(id, "collapsedinmodes");
                }
            }
            if (notifyRefControl === true) {
                if (this.hasAttribute("refcontrol")) {
                    var command =  document.getElementById(this.getAttribute("refcontrol"))
                    if (command) {
                        command.setAttribute("checked", display);
                        setBooleanAttribute(command, "disabled", !this.isVisibleInMode());
                    }
                }
            }
        ]]></body>
      </method>

      <method name="isVisibleInMode">
        <parameter name="aMode"/>
        <body><![CDATA[
            var lMode = aMode || this.currentMode;
            var display = true;
            var lModes = new Array();
            if (this.hasAttribute("mode")) {
                var modeString = this.getAttribute("mode");
                lModes = modeString.split(",");
            }
            if (lModes && lModes.length > 0) {
                display = lModes.includes(lMode);
            }
            return display;
        ]]></body>
      </method>

      <method name="onModeModified">
        <parameter name="aEvent"/>
        <parameter name="aBinding"/>
        <body><![CDATA[
            if (aEvent.attrName == "mode") {
                var display = aBinding.isVisibleInMode(aEvent.newValue);
                aBinding.setVisible(display, false, true);
            }
        ]]></body>
      </method>

      <method name="togglePane">
        <parameter name="aEvent"/>
        <body><![CDATA[
            var command = aEvent.target;
            var newValue = (command.getAttribute("checked") == "true" ? "false" : "true");
            command.setAttribute("checked", newValue);
            this.setVisible(newValue == "true", true, true);
        ]]></body>
      </method>

      <method name="onCheckboxStateChange">
        <parameter name="aEvent"/>
        <parameter name="aBinding"/>
        <body><![CDATA[
            var newValue = aEvent.target.checked;
            this.setVisible(newValue, true, true);
        ]]></body>
      </method>
      <method name="setAttribute">
        <parameter name="aAttr"/>
        <parameter name="aVal"/>
        <body><![CDATA[
          if (aAttr == "broadcaster") {
              this.mBroadcaster = document.getElementById(aVal);
              if (this.mBroadcaster) {
                  this.mModHandler = {
                  binding: this,
                  handleEvent : function(aEvent, aHandled) {
                      return this.binding.onModeModified(aEvent, this.binding);
                  }
              };
              this.mBroadcaster.addEventListener("DOMAttrModified", this.mModHandler, true);
            }
          }
          var ret = XULElement.prototype.setAttribute.call(this, aAttr, aVal);
        ]]></body>
      </method>
    </implementation>
  </binding>

<!-- This binding may server as a droptarget container for arbitrary items
     it contains methods to add DropShadows. This binding is meant to be used
     as a parent binding. The methods may be overwritten.
-->
<binding id="dragndropContainer">
    <implementation>
      <field name="mDropShadows">[]</field>
      <field name="mCalendarView">null</field>

      <!-- The ViewController that supports the interface 'calICalendarView'-->
      <property name="calendarView"
                onget="return this.mCalendarView;"
                onset="return (this.mCalendarView = val);"/>

       <!-- method to add individual code e.g to set up the new item during
       'ondrop' -->
      <method name="onDropItem">
        <parameter name="aItem"/>
        <body><![CDATA[
            // method that may be overridden by derived bindings...
        ]]></body>
      </method>

      <method name="getDropShadows">
        <body><![CDATA[
            return this.mDropShadows;
        ]]></body>
      </method>

      <!-- Adds the dropshadows to the children of the binding. The dropshadows
           are added at the first position of the children -->
      <method name="addDropShadows">
        <body><![CDATA[
          if (this.mDropShadows) {
              if (this.getElementsByAttribute("class", "dropshadow").length == 0) {
                  let offset = this.calendarView.mShadowOffset;
                  let shadowStartDate = this.date.clone();
                  shadowStartDate.addDuration(offset);
                  this.calendarView.mDropShadows = [];
                  for (let i = 0; i < this.calendarView.mDropShadowsLength; i++) {
                      let box = this.calendarView.findDayBoxForDate(shadowStartDate);
                      if (!box) {
                          // Dragging to the end or beginning of a view
                          shadowStartDate.day += 1;
                          continue;
                      }
                      let dropshadow = createXULElement("box");
                      dropshadow.setAttribute("class", "dropshadow");
                      if (box.hasChildNodes()) {
                          box.insertBefore(dropshadow, box.firstChild);
                      } else {
                          box.appendChild(dropshadow);
                      }
                      shadowStartDate.day += 1;
                      this.calendarView.mDropShadows.push(box);
                  }
              }
          }
        ]]></body>
      </method>

      <!-- removes all dropShadows from the binding. Dropshadows are recognized
           as such by carrying an attribute "dropshadow" -->
      <method name="removeDropShadows">
        <body><![CDATA[
            // method that may be overwritten by derived bindings...
            if (this.calendarView.mDropShadows) {
                for (let shadow of this.calendarView.mDropShadows) {
                    cal.removeChildElementsByAttribute(shadow, "class", "dropshadow");
                }
            }
            this.calendarView.mDropShadows = null;
        ]]></body>
      </method>

      <!-- By setting the attribute "dropbox" to "true" or "false" the
           dropshadows are added or removed -->
      <method name="setAttribute">
        <parameter name="aAttr"/>
        <parameter name="aVal"/>
        <body><![CDATA[
          if (aAttr == "dropbox") {
              let session = cal.getDragService().getCurrentSession();
              let startingDayBox = session.sourceNode.mParentBox;
              if (session) {
                  session.canDrop = true;
                  // no shadows when dragging in the initial position
                  if (aVal == "true" && this != startingDayBox) {
                      this.mDropShadows = [session.sourceNode.sourceObject];
                      this.addDropShadows();
                  } else {
                      this.removeDropShadows();
                  }
              }
          }
          return XULElement.prototype.setAttribute.call(this, aAttr, aVal);
        ]]></body>
      </method>

    </implementation>

    <handlers>
      <handler event="dragstart" phase="capturing"><![CDATA[
         let draggedDOMNode = event.target;
         if (!draggedDOMNode || draggedDOMNode.parentNode != this) {
             return;
         }
         let item = draggedDOMNode.occurrence.clone();
         let beginMoveDate = draggedDOMNode.mParentBox.date;
         let itemStartDate = (item.startDate || item.entryDate || item.dueDate).getInTimezone(calendarView.mTimezone);
         let itemEndDate = (item.endDate || item.dueDate || item.entryDate).getInTimezone(calendarView.mTimezone);
         let oneMoreDay = (itemEndDate.hour > 0 || itemEndDate.minute > 0);
         itemStartDate.isDate = true;
         itemEndDate.isDate = true;
         let offsetDuration = itemStartDate.subtractDate(beginMoveDate);
         let lenDuration = itemEndDate.subtractDate(itemStartDate);
         let len = lenDuration.weeks * 7 + lenDuration.days;
         this.calendarView.mShadowOffset = offsetDuration;
         this.calendarView.mDropShadowsLength = oneMoreDay ? len + 1 : len;
      ]]></handler>

      <handler event="dragover"><![CDATA[
         let session = cal.getDragService().getCurrentSession();
         if (!session || !session.sourceNode || !session.sourceNode.sourceObject) {
            // No source item? Then this is not for us.
            return;
         }

         // We handled the event
         event.preventDefault();
      ]]></handler>

      <handler event="dragenter"><![CDATA[
       if (event.target.localName == this.localName) {
           let session = cal.getDragService().getCurrentSession();
           if (session) {
               if (!session.sourceNode || !session.sourceNode.sourceObject) {
                  // No source item? Then this is not for us.
                  return;
               }

               // We can drop now, tell the drag service.
               event.preventDefault();

               if (!this.hasAttribute("dropbox") || this.getAttribute("dropbox") == "false") {
                   // As it turned out it was not possible to remove the remaining dropshadows
                   // at the "dragleave" or "dragexit" event, majorly because it was not reliably
                   // fired. As the dragndropcontainer may be anonymous it is further on not
                   // possible to remove the dropshadows by something like
                   // "document.getElementsByAttribute('dropbox').removeDropShadows();";
                   // So we have to remove them at the currentView(). The restriction of course is
                   // that these containers so far may not be used for drag and drop from/to e.g.
                   // the today-pane.
                   currentView().removeDropShadows();
               }
               this.setAttribute("dropbox", "true");
           }
       }
      ]]></handler>

      <handler event="drop"><![CDATA[
         let session = cal.getDragService().getCurrentSession();
         if (!session || !session.sourceNode || !session.sourceNode.sourceObject) {
            // No source node? Not our drag.
            return;
         }
         let item = session.sourceNode.sourceObject.clone();
         this.setAttribute("dropbox", "false");
         let transfer = Components.classes["@mozilla.org/widget/transferable;1"].
                        createInstance(Components.interfaces.nsITransferable);
         transfer.init(null);

         if (isEvent(item)) {
             transfer.addDataFlavor("application/x-moz-cal-event");
         } else {
             transfer.addDataFlavor("application/x-moz-cal-task");
         }

         session.getData(transfer, 0);
         let flavor = {};
         let data = {};
         // nsITransferable sucks when it comes to trying to add extra flavors.
         // This will throw NS_ERROR_FAILURE, so as a workaround, we use the
         // sourceNode property and get the event that way
         //transfer.getAnyTransferData(flavor, data, {});

         item = session.sourceNode.sourceObject;
         let newItem = this.onDropItem(item).clone();
         let newStart = newItem.startDate || newItem.entryDate || newItem.dueDate;
         let newEnd = newItem.endDate || newItem.dueDate || newItem.entryDate;
         let offset = this.calendarView.mShadowOffset;
         newStart.addDuration(offset);
         newEnd.addDuration(offset);
         this.calendarView.controller.modifyOccurrence(item, newStart, newEnd);

         // We handled the event
         event.stopPropagation();
      ]]></handler>

      <handler event="dragend"><![CDATA[
          currentView().removeDropShadows();
      ]]></handler>

    </handlers>
  </binding>

  <binding id="view-tab" extends="chrome://global/content/bindings/tabbox.xml#tab">
    <resources>
      <stylesheet src="chrome://calendar/skin/widgets/calendar-widgets.css"/>
    </resources>
    <content>
      <xul:hbox class="tab-middle box-inherit" xbl:inherits="align,dir,pack,orient,selected" flex="1">
        <xul:image class="tab-icon" xbl:inherits="validate,src=image"/>
        <xul:stack>
          <xul:label class="tab-text unselected-text"
                     xbl:inherits="value=label,accesskey,crop,disabled,selected"
                     flex="1"/>
          <xul:label class="tab-text selected-text"
                     xbl:inherits="value=label,accesskey,crop,disabled,selected"
                     flex="1"/>
        </xul:stack>
      </xul:hbox>
    </content>
  </binding>
</bindings>
