summaryrefslogtreecommitdiffstats
path: root/comm/suite/components/bindings/datetimepicker.xml
diff options
context:
space:
mode:
Diffstat (limited to 'comm/suite/components/bindings/datetimepicker.xml')
-rw-r--r--comm/suite/components/bindings/datetimepicker.xml1316
1 files changed, 1316 insertions, 0 deletions
diff --git a/comm/suite/components/bindings/datetimepicker.xml b/comm/suite/components/bindings/datetimepicker.xml
new file mode 100644
index 0000000000..7475b6c04e
--- /dev/null
+++ b/comm/suite/components/bindings/datetimepicker.xml
@@ -0,0 +1,1316 @@
+<?xml version="1.0"?>
+
+<!-- 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 bindings [
+<!ENTITY % datetimepickerDTD SYSTEM
+ "chrome://communicator/locale/datetimepicker.dtd">
+ %datetimepickerDTD;
+]>
+
+<bindings id="timepickerBindings"
+ xmlns="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"
+ xmlns:xbl="http://www.mozilla.org/xbl">
+
+ <binding id="datetimepicker-base"
+ extends="chrome://global/content/bindings/general.xml#basecontrol">
+
+ <content align="center">
+ <xul:hbox class="datetimepicker-input-box" align="center"
+ xbl:inherits="context,disabled,readonly">
+ <xul:moz-input-box class="textbox-input-box datetimepicker-input-subbox"
+ align="center">
+ <html:input class="datetimepicker-input textbox-input" anonid="input-one"
+ size="2" maxlength="2"
+ xbl:inherits="disabled,readonly"/>
+ </xul:moz-input-box>
+ <xul:label anonid="sep-first" class="datetimepicker-separator" value=":"/>
+ <xul:moz-input-box class="textbox-input-box datetimepicker-input-subbox"
+ align="center">
+ <html:input class="datetimepicker-input textbox-input" anonid="input-two"
+ size="2" maxlength="2"
+ xbl:inherits="disabled,readonly"/>
+ </xul:moz-input-box>
+ <xul:label anonid="sep-second" class="datetimepicker-separator" value=":"/>
+ <xul:moz-input-box class="textbox-input-box datetimepicker-input-subbox"
+ align="center">
+ <html:input class="datetimepicker-input textbox-input" anonid="input-three"
+ size="2" maxlength="2"
+ xbl:inherits="disabled,readonly"/>
+ </xul:moz-input-box>
+ <xul:moz-input-box class="textbox-input-box datetimepicker-input-subbox"
+ align="center">
+ <html:input class="datetimepicker-input textbox-input" anonid="input-ampm"
+ size="2" maxlength="2"
+ xbl:inherits="disabled,readonly"/>
+ </xul:moz-input-box>
+ </xul:hbox>
+ <xul:spinbuttons anonid="buttons" xbl:inherits="disabled"
+ onup="this.parentNode._increaseOrDecrease(1);"
+ ondown="this.parentNode._increaseOrDecrease(-1);"/>
+ </content>
+
+ <implementation>
+ <field name="_dateValue">null</field>
+ <field name="_fieldOne">
+ document.getAnonymousElementByAttribute(this, "anonid", "input-one");
+ </field>
+ <field name="_fieldTwo">
+ document.getAnonymousElementByAttribute(this, "anonid", "input-two");
+ </field>
+ <field name="_fieldThree">
+ document.getAnonymousElementByAttribute(this, "anonid", "input-three");
+ </field>
+ <field name="_fieldAMPM">
+ document.getAnonymousElementByAttribute(this, "anonid", "input-ampm");
+ </field>
+ <field name="_separatorFirst">
+ document.getAnonymousElementByAttribute(this, "anonid", "sep-first");
+ </field>
+ <field name="_separatorSecond">
+ document.getAnonymousElementByAttribute(this, "anonid", "sep-second");
+ </field>
+ <field name="_lastFocusedField">null</field>
+ <field name="_hasEntry">true</field>
+ <field name="_valueEntered">false</field>
+ <field name="attachedControl">null</field>
+
+ <property name="_currentField" readonly="true">
+ <getter>
+ var focusedInput = document.activeElement;
+ if (focusedInput == this._fieldOne ||
+ focusedInput == this._fieldTwo ||
+ focusedInput == this._fieldThree ||
+ focusedInput == this._fieldAMPM)
+ return focusedInput;
+ return this._lastFocusedField || this._fieldOne;
+ </getter>
+ </property>
+
+ <property name="dateValue" onget="return new Date(this._dateValue);">
+ <setter>
+ <![CDATA[
+ if (!(val instanceof Date))
+ throw "Invalid Date";
+
+ this._setValueNoSync(val);
+ if (this.attachedControl)
+ this.attachedControl._setValueNoSync(val);
+ return val;
+ ]]>
+ </setter>
+ </property>
+
+ <property name="readOnly" onset="if (val) this.setAttribute('readonly', 'true');
+ else this.removeAttribute('readonly'); return val;"
+ onget="return this.getAttribute('readonly') == 'true';"/>
+
+ <method name="_fireEvent">
+ <parameter name="aEventName"/>
+ <parameter name="aTarget"/>
+ <body>
+ var event = document.createEvent("Events");
+ event.initEvent(aEventName, true, true);
+ return !aTarget.dispatchEvent(event);
+ </body>
+ </method>
+
+ <method name="_setValueOnChange">
+ <parameter name="aField"/>
+ <body>
+ <![CDATA[
+ if (!this._hasEntry)
+ return;
+
+ if (aField == this._fieldOne ||
+ aField == this._fieldTwo ||
+ aField == this._fieldThree) {
+ var value = Number(aField.value);
+ if (isNaN(value))
+ value = 0;
+
+ value = this._constrainValue(aField, value, true);
+ this._setFieldValue(aField, value);
+ }
+ ]]>
+ </body>
+ </method>
+
+ <method name="_init">
+ <body/>
+ </method>
+
+ <constructor>
+ this._init();
+
+ var cval = this.getAttribute("value");
+ if (cval) {
+ try {
+ this.value = cval;
+ return;
+ } catch (ex) { }
+ }
+ this.dateValue = new Date();
+ </constructor>
+
+ <destructor>
+ if (this.attachedControl) {
+ this.attachedControl.attachedControl = null;
+ this.attachedControl = null;
+ }
+ </destructor>
+
+ </implementation>
+
+ <handlers>
+ <handler event="focus" phase="capturing">
+ <![CDATA[
+ var target = event.originalTarget;
+ if (target == this._fieldOne ||
+ target == this._fieldTwo ||
+ target == this._fieldThree ||
+ target == this._fieldAMPM)
+ this._lastFocusedField = target;
+ ]]>
+ </handler>
+
+ <handler event="keypress">
+ <![CDATA[
+ if (this._hasEntry && event.charCode &&
+ this._currentField != this._fieldAMPM &&
+ !(event.altKey || event.ctrlKey || event.metaKey) &&
+ (event.charCode < 48 || event.charCode > 57))
+ event.preventDefault();
+ ]]>
+ </handler>
+
+ <handler event="keypress" keycode="VK_UP">
+ if (this._hasEntry)
+ this._increaseOrDecrease(1);
+ </handler>
+ <handler event="keypress" keycode="VK_DOWN">
+ if (this._hasEntry)
+ this._increaseOrDecrease(-1);
+ </handler>
+
+ <handler event="input">
+ this._valueEntered = true;
+ </handler>
+
+ <handler event="change">
+ this._setValueOnChange(event.originalTarget);
+ </handler>
+ </handlers>
+
+ </binding>
+
+ <binding id="timepicker"
+#ifdef MOZ_SUITE
+ extends="chrome://communicator/content/bindings/datetimepicker.xml#datetimepicker-base">
+#else
+ extends="chrome://messenger/content/datetimepicker.xml#datetimepicker-base">
+#endif
+ <implementation>
+ <field name="is24HourClock">false</field>
+ <field name="hourLeadingZero">false</field>
+ <field name="minuteLeadingZero">true</field>
+ <field name="secondLeadingZero">true</field>
+ <field name="amIndicator">"AM"</field>
+ <field name="pmIndicator">"PM"</field>
+
+ <field name="hourField">null</field>
+ <field name="minuteField">null</field>
+ <field name="secondField">null</field>
+
+ <property name="value">
+ <getter>
+ <![CDATA[
+ var minute = this._dateValue.getMinutes();
+ if (minute < 10)
+ minute = "0" + minute;
+
+ var second = this._dateValue.getSeconds();
+ if (second < 10)
+ second = "0" + second;
+ return this._dateValue.getHours() + ":" + minute + ":" + second;
+ ]]>
+ </getter>
+ <setter>
+ <![CDATA[
+ var items = val.match(/^([0-9]{1,2})\:([0-9]{1,2})\:?([0-9]{1,2})?$/);
+ if (!items)
+ throw "Invalid Time";
+
+ var dt = this.dateValue;
+ dt.setHours(items[1]);
+ dt.setMinutes(items[2]);
+ dt.setSeconds(items[3] ? items[3] : 0);
+ this.dateValue = dt;
+ return val;
+ ]]>
+ </setter>
+ </property>
+ <property name="hour" onget="return this._dateValue.getHours();">
+ <setter>
+ <![CDATA[
+ var valnum = Number(val);
+ if (isNaN(valnum) || valnum < 0 || valnum > 23)
+ throw "Invalid Hour";
+ this._setFieldValue(this.hourField, valnum);
+ return val;
+ ]]>
+ </setter>
+ </property>
+ <property name="minute" onget="return this._dateValue.getMinutes();">
+ <setter>
+ <![CDATA[
+ var valnum = Number(val);
+ if (isNaN(valnum) || valnum < 0 || valnum > 59)
+ throw "Invalid Minute";
+ this._setFieldValue(this.minuteField, valnum);
+ return val;
+ ]]>
+ </setter>
+ </property>
+ <property name="second" onget="return this._dateValue.getSeconds();">
+ <setter>
+ <![CDATA[
+ var valnum = Number(val);
+ if (isNaN(valnum) || valnum < 0 || valnum > 59)
+ throw "Invalid Second";
+ this._setFieldValue(this.secondField, valnum);
+ return val;
+ ]]>
+ </setter>
+ </property>
+ <property name="isPM">
+ <getter>
+ <![CDATA[
+ return (this.hour >= 12);
+ ]]>
+ </getter>
+ <setter>
+ <![CDATA[
+ if (val) {
+ if (this.hour < 12)
+ this.hour += 12;
+ } else if (this.hour >= 12) {
+ this.hour -= 12;
+ }
+ return val;
+ ]]>
+ </setter>
+ </property>
+ <property name="hideSeconds">
+ <getter>
+ return (this.getAttribute("hideseconds") == "true");
+ </getter>
+ <setter>
+ if (val)
+ this.setAttribute("hideseconds", "true");
+ else
+ this.removeAttribute("hideseconds");
+ if (this.secondField)
+ this.secondField.parentNode.collapsed = val;
+ this._separatorSecond.collapsed = val;
+ return val;
+ </setter>
+ </property>
+ <property name="increment">
+ <getter>
+ <![CDATA[
+ var increment = this.getAttribute("increment");
+ increment = Number(increment);
+ if (isNaN(increment) || increment <= 0 || increment >= 60)
+ return 1;
+ return increment;
+ ]]>
+ </getter>
+ <setter>
+ <![CDATA[
+ if (typeof val == "number")
+ this.setAttribute("increment", val);
+ return val;
+ ]]>
+ </setter>
+ </property>
+
+ <method name="_setValueNoSync">
+ <parameter name="aValue"/>
+ <body>
+ <![CDATA[
+ var dt = new Date(aValue);
+ if (!isNaN(dt)) {
+ this._dateValue = dt;
+ this.setAttribute("value", this.value);
+ this._updateUI(this.hourField, this.hour);
+ this._updateUI(this.minuteField, this.minute);
+ this._updateUI(this.secondField, this.second);
+ }
+ ]]>
+ </body>
+ </method>
+ <method name="_increaseOrDecrease">
+ <parameter name="aDir"/>
+ <body>
+ <![CDATA[
+ if (this.disabled || this.readOnly)
+ return;
+
+ var field = this._currentField;
+ if (this._valueEntered)
+ this._setValueOnChange(field);
+
+ if (field == this._fieldAMPM) {
+ this.isPM = !this.isPM;
+ this._fireEvent("change", this);
+ } else {
+ var oldval;
+ var change = aDir;
+ if (field == this.hourField) {
+ oldval = this.hour;
+ } else if (field == this.minuteField) {
+ oldval = this.minute;
+ change *= this.increment;
+ } else if (field == this.secondField) {
+ oldval = this.second;
+ }
+
+ var newval = this._constrainValue(field, oldval + change, false);
+
+ if (field == this.hourField)
+ this.hour = newval;
+ else if (field == this.minuteField)
+ this.minute = newval;
+ else if (field == this.secondField)
+ this.second = newval;
+
+ if (oldval != newval)
+ this._fireEvent("change", this);
+ }
+ field.select();
+ ]]>
+ </body>
+ </method>
+ <method name="_setFieldValue">
+ <parameter name="aField"/>
+ <parameter name="aValue"/>
+ <body>
+ <![CDATA[
+ if (aField == this.hourField)
+ this._dateValue.setHours(aValue);
+ else if (aField == this.minuteField)
+ this._dateValue.setMinutes(aValue);
+ else if (aField == this.secondField)
+ this._dateValue.setSeconds(aValue);
+
+ this.setAttribute("value", this.value);
+ this._updateUI(aField, aValue);
+
+ if (this.attachedControl)
+ this.attachedControl._setValueNoSync(this._dateValue);
+ ]]>
+ </body>
+ </method>
+ <method name="_updateUI">
+ <parameter name="aField"/>
+ <parameter name="aValue"/>
+ <body>
+ <![CDATA[
+ this._valueEntered = false;
+
+ var prependZero = false;
+ if (aField == this.hourField) {
+ prependZero = this.hourLeadingZero;
+ if (!this.is24HourClock) {
+ if (aValue > 12)
+ aValue -= 12;
+ else if (aValue == 0)
+ aValue = 12;
+ this._fieldAMPM.value = this.isPM ? this.pmIndicator :
+ this.amIndicator;
+ }
+ } else if (aField == this.minuteField) {
+ prependZero = this.minuteLeadingZero;
+ } else if (aField == this.secondField) {
+ prependZero = this.secondLeadingZero;
+ }
+
+ if (prependZero && aValue < 10)
+ aField.value = "0" + aValue;
+ else
+ aField.value = aValue;
+ ]]>
+ </body>
+ </method>
+ <method name="_constrainValue">
+ <parameter name="aField"/>
+ <parameter name="aValue"/>
+ <parameter name="aNoWrap"/>
+ <body>
+ <![CDATA[
+ // aNoWrap is true when the user entered a value, so just
+ // constrain within limits. If false, the value is being
+ // incremented or decremented, so wrap around values
+ var max = 60;
+ if (aField == this.hourField) {
+ max = 24;
+ // User input in the hour field should be adjusted as
+ // needed for 12-hour vs. 24-hour time.
+ if (aNoWrap && !this.is24HourClock) {
+ if (aValue && aValue < 12 && this.isPM)
+ aValue += 12;
+ else if (aValue == 12 && !this.isPM)
+ aValue = 0;
+ }
+ }
+ if (aValue < 0)
+ return aNoWrap ? 0 : max + aValue;
+ if (aValue >= max)
+ return aNoWrap ? max - 1 : aValue - max;
+ return aValue;
+ ]]>
+ </body>
+ </method>
+ <method name="_init">
+ <body>
+ <![CDATA[
+ const {Services} = ChromeUtils.import("resource://gre/modules/Services.jsm");
+
+ this.hourField = this._fieldOne;
+ this.minuteField = this._fieldTwo;
+ this.secondField = this._fieldThree;
+
+ var numberOrder = /^(\D*)\s*(\d+)(\D*)(\d+)(\D*)(\d+)\s*(\D*)$/;
+
+ // XXX TODO: The following hack should be fixed once Intl.Locale arrives in bug 1433303.
+ var locale = Services.locale.regionalPrefsLocales[0];
+ if (locale.includes("-u-"))
+ locale += "-ca-gregory-nu-latn";
+ else
+ locale += "-u-ca-gregory-nu-latn";
+ var dtf = new Services.intl.DateTimeFormat(locale, { timeStyle: "long" });
+
+ var pmTime = dtf.format(new Date(2000, 0, 1, 16, 7, 9));
+ var numberFields = pmTime.match(numberOrder);
+ if (numberFields) {
+ this._separatorFirst.value = numberFields[3];
+ this._separatorSecond.value = numberFields[5];
+ if (Number(numberFields[2]) > 12)
+ this.is24HourClock = true;
+ else
+ this.pmIndicator = numberFields[1] || numberFields[7];
+ }
+
+ var amTime = dtf.format(new Date(2000, 0, 1, 1, 7, 9));
+ numberFields = amTime.match(numberOrder);
+ if (numberFields) {
+ this.hourLeadingZero = (numberFields[2].length > 1);
+ this.minuteLeadingZero = (numberFields[4].length > 1);
+ this.secondLeadingZero = (numberFields[6].length > 1);
+
+ if (!this.is24HourClock) {
+ this.amIndicator = numberFields[1] || numberFields[7];
+ if (numberFields[1]) {
+ var mfield = this._fieldAMPM.parentNode;
+ var mcontainer = mfield.parentNode;
+ mcontainer.insertBefore(mfield, mcontainer.firstChild);
+ }
+ var size = (numberFields[1] || numberFields[7]).length;
+ if (this.pmIndicator.length > size)
+ size = this.pmIndicator.length;
+ this._fieldAMPM.size = size;
+ this._fieldAMPM.maxLength = size;
+ } else {
+ this._fieldAMPM.parentNode.collapsed = true;
+ }
+ }
+
+ this.hideSeconds = this.hideSeconds;
+ ]]>
+ </body>
+ </method>
+ </implementation>
+
+ <handlers>
+ <handler event="keypress">
+ <![CDATA[
+ // just allow any printable character to switch the AM/PM state
+ if (event.charCode && !this.disabled && !this.readOnly &&
+ this._currentField == this._fieldAMPM) {
+ this.isPM = !this.isPM;
+ this._fieldAMPM.select();
+ this._fireEvent("change", this);
+ event.preventDefault();
+ }
+ ]]>
+ </handler>
+ </handlers>
+
+ </binding>
+
+ <binding id="datepicker"
+ extends="chrome://communicator/content/bindings/datetimepicker.xml#datetimepicker-base">
+ <implementation>
+ <field name="yearLeadingZero">false</field>
+ <field name="monthLeadingZero">true</field>
+ <field name="dateLeadingZero">true</field>
+
+ <field name="yearField"/>
+ <field name="monthField"/>
+ <field name="dateField"/>
+
+ <property name="value">
+ <getter>
+ <![CDATA[
+ var month = this._dateValue.getMonth();
+ month = (month < 9) ? month = "0" + ++month : month + 1;
+
+ var date = this._dateValue.getDate();
+ if (date < 10)
+ date = "0" + date;
+ return this._dateValue.getFullYear() + "-" + month + "-" + date;
+ ]]>
+
+ </getter>
+ <setter>
+ <![CDATA[
+ var results = val.match(/^([0-9]{1,4})\-([0-9]{1,2})\-([0-9]{1,2})$/);
+ if (!results)
+ throw "Invalid Date";
+
+ this.dateValue = new Date(results[1] + "/" + results[2] + "/" + results[3]);
+ this.setAttribute("value", this.value);
+ return val;
+ ]]>
+ </setter>
+ </property>
+ <property name="year" onget="return this._dateValue.getFullYear();">
+ <setter>
+ <![CDATA[
+ var valnum = Number(val);
+ if (isNaN(valnum) || valnum < 1 || valnum > 9999)
+ throw "Invalid Year";
+ this._setFieldValue(this.yearField, valnum);
+ return val;
+ ]]>
+ </setter>
+ </property>
+ <property name="month" onget="return this._dateValue.getMonth();">
+ <setter>
+ <![CDATA[
+ var valnum = Number(val);
+ if (isNaN(valnum) || valnum < 0 || valnum > 11)
+ throw "Invalid Month";
+ this._setFieldValue(this.monthField, valnum);
+ return val;
+ ]]>
+ </setter>
+ </property>
+ <property name="date" onget="return this._dateValue.getDate();">
+ <setter>
+ <![CDATA[
+ var valnum = Number(val);
+ if (isNaN(valnum) || valnum < 1 || valnum > 31)
+ throw "Invalid Date";
+ this._setFieldValue(this.dateField, valnum);
+ return val;
+ ]]>
+ </setter>
+ </property>
+ <property name="open" onget="return false;" onset="return val;"/>
+
+ <property name="displayedMonth" onget="return this.month;"
+ onset="this.month = val; return val;"/>
+ <property name="displayedYear" onget="return this.year;"
+ onset="this.year = val; return val;"/>
+
+ <method name="_setValueNoSync">
+ <parameter name="aValue"/>
+ <body>
+ <![CDATA[
+ var dt = new Date(aValue);
+ if (!isNaN(dt)) {
+ this._dateValue = dt;
+ this.setAttribute("value", this.value);
+ this._updateUI(this.yearField, this.year);
+ this._updateUI(this.monthField, this.month);
+ this._updateUI(this.dateField, this.date);
+ }
+ ]]>
+ </body>
+ </method>
+ <method name="_increaseOrDecrease">
+ <parameter name="aDir"/>
+ <body>
+ <![CDATA[
+ if (this.disabled || this.readOnly)
+ return;
+
+ var field = this._currentField;
+ if (this._valueEntered)
+ this._setValueOnChange(field);
+
+ var oldval;
+ if (field == this.yearField)
+ oldval = this.year;
+ else if (field == this.monthField)
+ oldval = this.month;
+ else if (field == this.dateField)
+ oldval = this.date;
+
+ var newval = this._constrainValue(field, oldval + aDir, false);
+
+ if (field == this.yearField)
+ this.year = newval;
+ else if (field == this.monthField)
+ this.month = newval;
+ else if (field == this.dateField)
+ this.date = newval;
+
+ if (oldval != newval)
+ this._fireEvent("change", this);
+ field.select();
+ ]]>
+ </body>
+ </method>
+ <method name="_setFieldValue">
+ <parameter name="aField"/>
+ <parameter name="aValue"/>
+ <body>
+ <![CDATA[
+ if (aField == this.yearField) {
+ let oldDate = this.date;
+ this._dateValue.setFullYear(aValue);
+ if (oldDate != this.date) {
+ this._dateValue.setDate(0);
+ this._updateUI(this.dateField, this.date);
+ }
+ } else if (aField == this.monthField) {
+ let oldDate = this.date;
+ this._dateValue.setMonth(aValue);
+ if (oldDate != this.date) {
+ this._dateValue.setDate(0);
+ this._updateUI(this.dateField, this.date);
+ }
+ } else if (aField == this.dateField) {
+ this._dateValue.setDate(aValue);
+ }
+
+ this.setAttribute("value", this.value);
+ this._updateUI(aField, aValue);
+
+ if (this.attachedControl)
+ this.attachedControl._setValueNoSync(this._dateValue);
+ ]]>
+ </body>
+ </method>
+ <method name="_updateUI">
+ <parameter name="aField"/>
+ <parameter name="aValue"/>
+ <body>
+ <![CDATA[
+ this._valueEntered = false;
+
+ var prependZero = false;
+ if (aField == this.yearField) {
+ if (this.yearLeadingZero) {
+ aField.value = ("000" + aValue).slice(-4);
+ return;
+ }
+ } else if (aField == this.monthField) {
+ aValue++;
+ prependZero = this.monthLeadingZero;
+ } else if (aField == this.dateField) {
+ prependZero = this.dateLeadingZero;
+ }
+ if (prependZero && aValue < 10)
+ aField.value = "0" + aValue;
+ else
+ aField.value = aValue;
+ ]]>
+ </body>
+ </method>
+ <method name="_constrainValue">
+ <parameter name="aField"/>
+ <parameter name="aValue"/>
+ <parameter name="aNoWrap"/>
+ <body>
+ <![CDATA[
+ // the month will be 1 to 12 if entered by the user, so subtract 1
+ if (aNoWrap && aField == this.monthField)
+ aValue--;
+
+ if (aField == this.dateField) {
+ if (aValue < 1)
+ return new Date(this.year, this.month + 1, 0).getDate();
+
+ var currentMonth = this.month;
+ var dt = new Date(this.year, currentMonth, aValue);
+ return (dt.getMonth() != currentMonth ? 1 : aValue);
+ }
+ var min = (aField == this.monthField) ? 0 : 1;
+ var max = (aField == this.monthField) ? 11 : 9999;
+ if (aValue < min)
+ return aNoWrap ? min : max;
+ if (aValue > max)
+ return aNoWrap ? max : min;
+ return aValue;
+ ]]>
+ </body>
+ </method>
+ <method name="_init">
+ <body>
+ <![CDATA[
+ const {Services} = ChromeUtils.import("resource://gre/modules/Services.jsm");
+
+ // We'll default to YYYY/MM/DD to start.
+ var yfield = "input-one";
+ var mfield = "input-two";
+ var dfield = "input-three";
+ var twoDigitYear = false;
+ this.yearLeadingZero = true;
+ this.monthLeadingZero = true;
+ this.dateLeadingZero = true;
+
+ var numberOrder = /^(\D*)\s*(\d+)(\D*)(\d+)(\D*)(\d+)\s*(\D*)$/;
+
+ // XXX TODO: The following hack should be fixed once Intl.Locale arrives in bug 1433303.
+ var locale = Services.locale.regionalPrefsLocales[0];
+ if (locale.includes("-u-"))
+ locale += "-ca-gregory-nu-latn";
+ else
+ locale += "-u-ca-gregory-nu-latn";
+ var dtf = new Services.intl.DateTimeFormat(locale, { dateStyle: "short" });
+
+ var dt = dtf.format(new Date(2002, 9, 4));
+ var numberFields = dt.match(numberOrder);
+ if (numberFields) {
+ this._separatorFirst.value = numberFields[3];
+ this._separatorSecond.value = numberFields[5];
+
+ var yi = 2, mi = 4, di = 6;
+
+ function fieldForNumber(i) {
+ if (i == 2)
+ return "input-one";
+ if (i == 4)
+ return "input-two";
+ return "input-three";
+ }
+
+ for (var i = 1; i < numberFields.length; i++) {
+ switch (Number(numberFields[i])) {
+ case 2:
+ twoDigitYear = true; // fall through
+ case 2002:
+ yi = i;
+ yfield = fieldForNumber(i);
+ break;
+ case 9:
+ case 10:
+ mi = i;
+ mfield = fieldForNumber(i);
+ break;
+ case 4:
+ di = i;
+ dfield = fieldForNumber(i);
+ break;
+ }
+ }
+
+ this.yearLeadingZero = (numberFields[yi].length > 1);
+ this.monthLeadingZero = (numberFields[mi].length > 1);
+ this.dateLeadingZero = (numberFields[di].length > 1);
+ }
+
+ this.yearField = document.getAnonymousElementByAttribute(this, "anonid", yfield);
+ if (!twoDigitYear)
+ this.yearField.parentNode.classList.add("datetimepicker-input-subbox", "datetimepicker-year");
+ this.monthField = document.getAnonymousElementByAttribute(this, "anonid", mfield);
+ this.dateField = document.getAnonymousElementByAttribute(this, "anonid", dfield);
+
+ this._fieldAMPM.parentNode.collapsed = true;
+ this.yearField.size = twoDigitYear ? 2 : 4;
+ this.yearField.maxLength = twoDigitYear ? 2 : 4;
+ ]]>
+ </body>
+ </method>
+ </implementation>
+
+ </binding>
+
+ <binding id="datepicker-grid"
+ extends="chrome://communicator/content/bindings/datetimepicker.xml#datepicker">
+ <content>
+ <vbox class="datepicker-mainbox"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+ <hbox class="datepicker-monthbox" align="center">
+ <button class="datepicker-previous datepicker-button" type="repeat"
+ xbl:inherits="disabled"
+ oncommand="document.getBindingParent(this)._increaseOrDecreaseMonth(-1);"/>
+ <spacer flex="1"/>
+ <deck anonid="monthlabeldeck">
+ <label class="datepicker-gridlabel" value=""/>
+ <label class="datepicker-gridlabel" value=""/>
+ <label class="datepicker-gridlabel" value=""/>
+ <label class="datepicker-gridlabel" value=""/>
+ <label class="datepicker-gridlabel" value=""/>
+ <label class="datepicker-gridlabel" value=""/>
+ <label class="datepicker-gridlabel" value=""/>
+ <label class="datepicker-gridlabel" value=""/>
+ <label class="datepicker-gridlabel" value=""/>
+ <label class="datepicker-gridlabel" value=""/>
+ <label class="datepicker-gridlabel" value=""/>
+ <label class="datepicker-gridlabel" value=""/>
+ </deck>
+ <label anonid="yearlabel" class="datepicker-gridlabel"/>
+ <spacer flex="1"/>
+ <button class="datepicker-next datepicker-button" type="repeat"
+ xbl:inherits="disabled"
+ oncommand="document.getBindingParent(this)._increaseOrDecreaseMonth(1);"/>
+ </hbox>
+ <grid class="datepicker-grid" role="grid">
+ <columns>
+ <column class="datepicker-gridrow" flex="1"/>
+ <column class="datepicker-gridrow" flex="1"/>
+ <column class="datepicker-gridrow" flex="1"/>
+ <column class="datepicker-gridrow" flex="1"/>
+ <column class="datepicker-gridrow" flex="1"/>
+ <column class="datepicker-gridrow" flex="1"/>
+ <column class="datepicker-gridrow" flex="1"/>
+ </columns>
+ <rows anonid="datebox">
+ <row anonid="dayofweekbox">
+ <label class="datepicker-weeklabel" role="columnheader"/>
+ <label class="datepicker-weeklabel" role="columnheader"/>
+ <label class="datepicker-weeklabel" role="columnheader"/>
+ <label class="datepicker-weeklabel" role="columnheader"/>
+ <label class="datepicker-weeklabel" role="columnheader"/>
+ <label class="datepicker-weeklabel" role="columnheader"/>
+ <label class="datepicker-weeklabel" role="columnheader"/>
+ </row>
+ <row>
+ <label class="datepicker-gridlabel" role="gridcell"/>
+ <label class="datepicker-gridlabel" role="gridcell"/>
+ <label class="datepicker-gridlabel" role="gridcell"/>
+ <label class="datepicker-gridlabel" role="gridcell"/>
+ <label class="datepicker-gridlabel" role="gridcell"/>
+ <label class="datepicker-gridlabel" role="gridcell"/>
+ <label class="datepicker-gridlabel" role="gridcell"/>
+ </row>
+ <row>
+ <label class="datepicker-gridlabel" role="gridcell"/>
+ <label class="datepicker-gridlabel" role="gridcell"/>
+ <label class="datepicker-gridlabel" role="gridcell"/>
+ <label class="datepicker-gridlabel" role="gridcell"/>
+ <label class="datepicker-gridlabel" role="gridcell"/>
+ <label class="datepicker-gridlabel" role="gridcell"/>
+ <label class="datepicker-gridlabel" role="gridcell"/>
+ </row>
+ <row>
+ <label class="datepicker-gridlabel" role="gridcell"/>
+ <label class="datepicker-gridlabel" role="gridcell"/>
+ <label class="datepicker-gridlabel" role="gridcell"/>
+ <label class="datepicker-gridlabel" role="gridcell"/>
+ <label class="datepicker-gridlabel" role="gridcell"/>
+ <label class="datepicker-gridlabel" role="gridcell"/>
+ <label class="datepicker-gridlabel" role="gridcell"/>
+ </row>
+ <row>
+ <label class="datepicker-gridlabel" role="gridcell"/>
+ <label class="datepicker-gridlabel" role="gridcell"/>
+ <label class="datepicker-gridlabel" role="gridcell"/>
+ <label class="datepicker-gridlabel" role="gridcell"/>
+ <label class="datepicker-gridlabel" role="gridcell"/>
+ <label class="datepicker-gridlabel" role="gridcell"/>
+ <label class="datepicker-gridlabel" role="gridcell"/>
+ </row>
+ <row>
+ <label class="datepicker-gridlabel" role="gridcell"/>
+ <label class="datepicker-gridlabel" role="gridcell"/>
+ <label class="datepicker-gridlabel" role="gridcell"/>
+ <label class="datepicker-gridlabel" role="gridcell"/>
+ <label class="datepicker-gridlabel" role="gridcell"/>
+ <label class="datepicker-gridlabel" role="gridcell"/>
+ <label class="datepicker-gridlabel" role="gridcell"/>
+ </row>
+ <row>
+ <label class="datepicker-gridlabel" role="gridcell"/>
+ <label class="datepicker-gridlabel" role="gridcell"/>
+ <label class="datepicker-gridlabel" role="gridcell"/>
+ <label class="datepicker-gridlabel" role="gridcell"/>
+ <label class="datepicker-gridlabel" role="gridcell"/>
+ <label class="datepicker-gridlabel" role="gridcell"/>
+ <label class="datepicker-gridlabel" role="gridcell"/>
+ </row>
+ </rows>
+ </grid>
+ </vbox>
+ </content>
+
+ <implementation>
+ <field name="_hasEntry">false</field>
+ <field name="_weekStart">&firstdayofweek.default;</field>
+ <field name="_displayedDate">null</field>
+ <field name="_todayItem">null</field>
+
+ <field name="yearField">
+ document.getAnonymousElementByAttribute(this, "anonid", "yearlabel");
+ </field>
+ <field name="monthField">
+ document.getAnonymousElementByAttribute(this, "anonid", "monthlabeldeck");
+ </field>
+ <field name="dateField">
+ document.getAnonymousElementByAttribute(this, "anonid", "datebox");
+ </field>
+
+ <field name="_selectedItem">null</field>
+
+ <property name="selectedItem" onget="return this._selectedItem">
+ <setter>
+ <![CDATA[
+ if (!val.value)
+ return val;
+ if (val.parentNode.parentNode != this.dateField)
+ return val;
+
+ if (this._selectedItem)
+ this._selectedItem.removeAttribute("selected");
+ this._selectedItem = val;
+ val.setAttribute("selected", "true");
+ this._displayedDate.setDate(val.value);
+ return val;
+ ]]>
+ </setter>
+ </property>
+
+ <property name="displayedMonth">
+ <getter>
+ return this._displayedDate.getMonth();
+ </getter>
+ <setter>
+ this._updateUI(this.monthField, val, true);
+ return val;
+ </setter>
+ </property>
+ <property name="displayedYear">
+ <getter>
+ return this._displayedDate.getFullYear();
+ </getter>
+ <setter>
+ this._updateUI(this.yearField, val, true);
+ return val;
+ </setter>
+ </property>
+
+ <method name="_init">
+ <body>
+ <![CDATA[
+ const {Services} = ChromeUtils.import("resource://gre/modules/Services.jsm");
+
+ // XXX TODO: The following hack should be fixed once Intl.Locale arrives in bug 1433303.
+ var locale = Services.locale.regionalPrefsLocales[0];
+ if (locale.includes("-u-"))
+ locale += "-ca-gregory";
+ else
+ locale += "-u-ca-gregory";
+ var dtfMonth = new Services.intl.DateTimeFormat(locale, {month: "long", timeZone: "UTC"});
+ var dtfWeekday = new Services.intl.DateTimeFormat(locale, {weekday: "narrow"});
+
+ var monthLabel = this.monthField.firstChild;
+ var tempDate = new Date(Date.UTC(2005, 0, 1));
+ for (var month = 0; month < 12; month++) {
+ tempDate.setUTCMonth(month);
+ monthLabel.setAttribute("value", dtfMonth.format(tempDate));
+ monthLabel = monthLabel.nextSibling;
+ }
+
+ var fdow = Number(this.getAttribute("firstdayofweek"));
+ if (!isNaN(fdow) && fdow >= 0 && fdow <= 6)
+ this._weekStart = fdow;
+
+ var weekbox = document.getAnonymousElementByAttribute(this, "anonid", "dayofweekbox").childNodes;
+ var date = new Date();
+ date.setDate(date.getDate() - (date.getDay() - this._weekStart));
+ for (var i = 0; i < weekbox.length; i++) {
+ weekbox[i].value = dtfWeekday.format(date);
+ date.setDate(date.getDate() + 1);
+ }
+ ]]>
+ </body>
+ </method>
+ <method name="_setValueNoSync">
+ <parameter name="aValue"/>
+ <body>
+ <![CDATA[
+ var dt = new Date(aValue);
+ if (!isNaN(dt)) {
+ this._dateValue = dt;
+ this.setAttribute("value", this.value);
+ this._updateUI();
+ }
+ ]]>
+ </body>
+ </method>
+ <method name="_updateUI">
+ <parameter name="aField"/>
+ <parameter name="aValue"/>
+ <parameter name="aCheckMonth"/>
+ <body>
+ <![CDATA[
+ var date;
+ var currentMonth;
+ if (aCheckMonth) {
+ if (!this._displayedDate)
+ this._displayedDate = this.dateValue;
+
+ var expectedMonth = aValue;
+ if (aField == this.monthField) {
+ this._displayedDate.setMonth(aValue);
+ } else {
+ expectedMonth = this._displayedDate.getMonth();
+ this._displayedDate.setFullYear(aValue);
+ }
+
+ if (expectedMonth != -1 && expectedMonth != 12 &&
+ expectedMonth != this._displayedDate.getMonth()) {
+ // If the month isn't what was expected, then the month overflowed.
+ // Setting the date to 0 will go back to the last day of the right month.
+ this._displayedDate.setDate(0);
+ }
+
+ date = new Date(this._displayedDate);
+ currentMonth = this._displayedDate.getMonth();
+ } else {
+ var samemonth = (this._displayedDate &&
+ this._displayedDate.getMonth() == this.month &&
+ this._displayedDate.getFullYear() == this.year);
+ if (samemonth) {
+ var items = this.dateField.getElementsByAttribute("value", this.date);
+ if (items.length)
+ this.selectedItem = items[0];
+ return;
+ }
+
+ date = this.dateValue;
+ this._displayedDate = new Date(date);
+ currentMonth = this.month;
+ }
+
+ if (this._todayItem) {
+ this._todayItem.removeAttribute("today");
+ this._todayItem = null;
+ }
+
+ if (this._selectedItem) {
+ this._selectedItem.removeAttribute("selected");
+ this._selectedItem = null;
+ }
+
+ // Update the month and year title
+ this.monthField.selectedIndex = currentMonth;
+ this.yearField.setAttribute("value", date.getFullYear());
+
+ date.setDate(1);
+ var firstWeekday = (7 + date.getDay() - this._weekStart) % 7;
+ date.setDate(date.getDate() - firstWeekday);
+
+ var today = new Date();
+ var datebox = this.dateField;
+ for (var k = 1; k < datebox.childNodes.length; k++) {
+ var row = datebox.childNodes[k];
+ for (var i = 0; i < 7; i++) {
+ var item = row.childNodes[i];
+
+ if (currentMonth == date.getMonth()) {
+ item.value = date.getDate();
+
+ // highlight today
+ if (this._isSameDay(today, date)) {
+ this._todayItem = item;
+ item.setAttribute("today", "true");
+ }
+
+ // highlight the selected date
+ if (this._isSameDay(this._dateValue, date)) {
+ this._selectedItem = item;
+ item.setAttribute("selected", "true");
+ }
+ } else {
+ item.value = "";
+ }
+
+ date.setDate(date.getDate() + 1);
+ }
+ }
+
+ this._fireEvent("monthchange", this);
+ ]]>
+ </body>
+ </method>
+ <method name="_increaseOrDecreaseDateFromEvent">
+ <parameter name="aEvent"/>
+ <parameter name="aDiff"/>
+ <body>
+ <![CDATA[
+ if (aEvent.originalTarget == this && !this.disabled && !this.readOnly) {
+ var newdate = this.dateValue;
+ newdate.setDate(newdate.getDate() + aDiff);
+ this.dateValue = newdate;
+ this._fireEvent("change", this);
+ }
+ aEvent.stopPropagation();
+ aEvent.preventDefault();
+ ]]>
+ </body>
+ </method>
+ <method name="_increaseOrDecreaseMonth">
+ <parameter name="aDir"/>
+ <body>
+ <![CDATA[
+ if (!this.disabled) {
+ var month = this._displayedDate ? this._displayedDate.getMonth() :
+ this.month;
+ this._updateUI(this.monthField, month + aDir, true);
+ }
+ ]]>
+ </body>
+ </method>
+ <method name="_isSameDay">
+ <parameter name="aDate1"/>
+ <parameter name="aDate2"/>
+ <body>
+ <![CDATA[
+ return (aDate1 && aDate2 &&
+ aDate1.getDate() == aDate2.getDate() &&
+ aDate1.getMonth() == aDate2.getMonth() &&
+ aDate1.getFullYear() == aDate2.getFullYear());
+ ]]>
+ </body>
+ </method>
+
+ </implementation>
+
+ <handlers>
+ <handler event="click">
+ <![CDATA[
+ if (event.button != 0 || this.disabled || this.readOnly)
+ return;
+
+ var target = event.originalTarget;
+ if (target.classList.contains("datepicker-gridlabel") &&
+ target != this.selectedItem) {
+ this.selectedItem = target;
+ this._dateValue = new Date(this._displayedDate);
+ if (this.attachedControl)
+ this.attachedControl._setValueNoSync(this._dateValue);
+ this._fireEvent("change", this);
+
+ if (this.attachedControl && "open" in this.attachedControl)
+ this.attachedControl.open = false; // close the popup
+ }
+ ]]>
+ </handler>
+ <handler event="MozMousePixelScroll" preventdefault="true"/>
+ <handler event="DOMMouseScroll" preventdefault="true">
+ <![CDATA[
+ this._increaseOrDecreaseMonth(event.detail < 0 ? -1 : 1);
+ ]]>
+ </handler>
+ <handler event="keypress" keycode="VK_LEFT"
+ action="this._increaseOrDecreaseDateFromEvent(event, -1);"/>
+ <handler event="keypress" keycode="VK_RIGHT"
+ action="this._increaseOrDecreaseDateFromEvent(event, 1);"/>
+ <handler event="keypress" keycode="VK_UP"
+ action="this._increaseOrDecreaseDateFromEvent(event, -7);"/>
+ <handler event="keypress" keycode="VK_DOWN"
+ action="this._increaseOrDecreaseDateFromEvent(event, 7);"/>
+ <handler event="keypress" keycode="VK_PAGE_UP" preventdefault="true"
+ action="this._increaseOrDecreaseMonth(-1);"/>
+ <handler event="keypress" keycode="VK_PAGE_DOWN" preventdefault="true"
+ action="this._increaseOrDecreaseMonth(1);"/>
+ </handlers>
+ </binding>
+
+ <binding id="datepicker-popup" display="xul:menu"
+ extends="chrome://communicator/content/bindings/datetimepicker.xml#datepicker">
+ <content align="center">
+ <xul:moz-input-box class="textbox-input-box datetimepicker-input-box"
+ align="center"
+ allowevents="true"
+ xbl:inherits="context,disabled,readonly">
+ <xul:hbox class="datetimepicker-input-subbox" align="baseline">
+ <html:input class="datetimepicker-input textbox-input" anonid="input-one"
+ size="2" maxlength="2"
+ xbl:inherits="disabled,readonly"/>
+ </xul:hbox>
+ <xul:label anonid="sep-first" class="datetimepicker-separator" value=":"/>
+ <xul:hbox class="datetimepicker-input-subbox" align="baseline">
+ <html:input class="datetimepicker-input textbox-input" anonid="input-two"
+ size="2" maxlength="2"
+ xbl:inherits="disabled,readonly"/>
+ </xul:hbox>
+ <xul:label anonid="sep-second" class="datetimepicker-separator" value=":"/>
+ <xul:hbox class="datetimepicker-input-subbox" align="center">
+ <html:input class="datetimepicker-input textbox-input" anonid="input-three"
+ size="2" maxlength="2"
+ xbl:inherits="disabled,readonly"/>
+ </xul:hbox>
+ <xul:hbox class="datetimepicker-input-subbox" align="center">
+ <html:input class="datetimepicker-input textbox-input" anonid="input-ampm"
+ size="2" maxlength="2"
+ xbl:inherits="disabled,readonly"/>
+ </xul:hbox>
+ </xul:moz-input-box>
+ <xul:spinbuttons anonid="buttons" xbl:inherits="disabled" allowevents="true"
+ onup="this.parentNode._increaseOrDecrease(1);"
+ ondown="this.parentNode._increaseOrDecrease(-1);"/>
+ <xul:dropmarker class="datepicker-dropmarker" xbl:inherits="disabled"/>
+ <xul:panel onpopupshown="this.firstChild.focus();" level="top">
+ <xul:datepicker anonid="grid" type="grid" class="datepicker-popupgrid"
+ xbl:inherits="disabled,readonly,firstdayofweek"/>
+ </xul:panel>
+ </content>
+ <implementation>
+ <constructor>
+ var grid = document.getAnonymousElementByAttribute(this, "anonid", "grid");
+ this.attachedControl = grid;
+ grid.attachedControl = this;
+ grid._setValueNoSync(this._dateValue);
+ </constructor>
+ <property name="open" onget="return this.hasAttribute('open');">
+ <setter>
+ <![CDATA[
+ if (this.hasMenu())
+ this.openMenu(val);
+ return val;
+ ]]>
+ </setter>
+ </property>
+ <property name="displayedMonth">
+ <getter>
+ return document.getAnonymousElementByAttribute(this, "anonid", "grid").displayedMonth;
+ </getter>
+ <setter>
+ document.getAnonymousElementByAttribute(this, "anonid", "grid").displayedMonth = val;
+ return val;
+ </setter>
+ </property>
+ <property name="displayedYear">
+ <getter>
+ return document.getAnonymousElementByAttribute(this, "anonid", "grid").displayedYear;
+ </getter>
+ <setter>
+ document.getAnonymousElementByAttribute(this, "anonid", "grid").displayedYear = val;
+ return val;
+ </setter>
+ </property>
+ </implementation>
+ </binding>
+
+</bindings>