diff options
Diffstat (limited to 'toolkit/content/widgets/datetimepicker.xml')
-rw-r--r-- | toolkit/content/widgets/datetimepicker.xml | 1301 |
1 files changed, 1301 insertions, 0 deletions
diff --git a/toolkit/content/widgets/datetimepicker.xml b/toolkit/content/widgets/datetimepicker.xml new file mode 100644 index 000000000..5f16f1ff0 --- /dev/null +++ b/toolkit/content/widgets/datetimepicker.xml @@ -0,0 +1,1301 @@ +<?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://global/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"> + + <resources> + <stylesheet src="chrome://global/content/textbox.css"/> + <stylesheet src="chrome://global/skin/textbox.css"/> + <stylesheet src="chrome://global/skin/dropmarker.css"/> + <stylesheet src="chrome://global/skin/datetimepicker.css"/> + </resources> + + <content align="center"> + <xul:hbox class="datetimepicker-input-box" align="center" + xbl:inherits="context,disabled,readonly"> + <xul:hbox 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:hbox> + <xul:label anonid="sep-first" class="datetimepicker-separator" value=":"/> + <xul:hbox 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:hbox> + <xul:label anonid="sep-second" class="datetimepicker-separator" value=":"/> + <xul:hbox 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:hbox> + <xul:hbox 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:hbox> + </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" + extends="chrome://global/content/bindings/datetimepicker.xml#datetimepicker-base"> + + <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) { + if (aValue > 12) + aValue -= 12; + this._fieldAMPM.value = this.pmIndicator; + } + else { + if (aValue == 0) + aValue = 12; + this._fieldAMPM.value = 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 = (aField == this.hourField) ? 24 : 60; + 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[ + this.hourField = this._fieldOne; + this.minuteField = this._fieldTwo; + this.secondField = this._fieldThree; + + var numberOrder = /^(\D*)\s*(\d+)(\D*)(\d+)(\D*)(\d+)\s*(\D*)$/; + + var locale = Intl.DateTimeFormat().resolvedOptions().locale + "-u-ca-gregory-nu-latn"; + + var pmTime = new Date(2000, 0, 1, 16, 7, 9).toLocaleTimeString(locale); + 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 = new Date(2000, 0, 1, 1, 7, 9).toLocaleTimeString(locale); + 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://global/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[ + // 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*)$/; + + var locale = Intl.DateTimeFormat().resolvedOptions().locale + "-u-ca-gregory-nu-latn"; + + var dt = new Date(2002, 9, 4).toLocaleDateString(locale); + 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, 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://global/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[ + var locale = Intl.DateTimeFormat().resolvedOptions().locale + "-u-ca-gregory"; + var dtfMonth = Intl.DateTimeFormat(locale, {month: "long"}); + var dtfWeekday = Intl.DateTimeFormat(locale, {weekday: "narrow"}); + + var monthLabel = this.monthField.firstChild; + var tempDate = new Date(2005, 0, 1); + for (var month = 0; month < 12; month++) { + tempDate.setMonth(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); + if (this.hasAttribute("monthchange")) { + var fn = new Function("event", aTarget.getAttribute("onmonthchange")); + fn.call(aTarget, event); + } + ]]> + </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://global/content/bindings/datetimepicker.xml#datepicker"> + <content align="center"> + <xul:hbox 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:hbox> + <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.boxObject instanceof MenuBoxObject) + this.boxObject.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> |