Mypal/toolkit/content/widgets/datetimepicker.xml

1302 lines
47 KiB
XML

<?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", timeZone: "UTC"});
var dtfWeekday = 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);
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>