1 <?xml version="1.0"?>
2
3 <!-- ***** BEGIN LICENSE BLOCK *****
4 - Version: MPL 1.1/GPL 2.0/LGPL 2.1
5 -
6 - The contents of this file are subject to the Mozilla Public License Version
7 - 1.1 (the "License"); you may not use this file except in compliance with
8 - the License. You may obtain a copy of the License at
9 - http://www.mozilla.org/MPL/
10 -
11 - Software distributed under the License is distributed on an "AS IS" basis,
12 - WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
13 - for the specific language governing rights and limitations under the
14 - License.
15 -
16 - The Original Code is Mozilla.org code.
17 -
18 - The Initial Developer of the Original Code is Mozilla Corporation.
19 - Portions created by the Initial Developer are Copyright (C) 2006
20 - the Initial Developer. All Rights Reserved.
21 -
22 - Contributor(s):
23 - Neil Deakin <enndeakin@sympatico.ca> (Original Author)
24 -
25 - Alternatively, the contents of this file may be used under the terms of
26 - either of the GNU General Public License Version 2 or later (the "GPL"),
27 - or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
28 - in which case the provisions of the GPL or the LGPL are applicable instead
29 - of those above. If you wish to allow use of your version of this file only
30 - under the terms of either the GPL or the LGPL, and not to allow others to
31 - use your version of this file under the terms of the MPL, indicate your
32 - decision by deleting the provisions above and replace them with the notice
33 - and other provisions required by the GPL or the LGPL. If you do not delete
34 - the provisions above, a recipient may use your version of this file under
35 - the terms of any one of the MPL, the GPL or the LGPL.
36 -
37 - ***** END LICENSE BLOCK ***** -->
38
39 <!DOCTYPE bindings [
40 <!ENTITY % datetimepickerDTD SYSTEM "chrome://global/locale/datetimepicker.dtd">
41 %datetimepickerDTD;
42 <!ENTITY % globalDTD SYSTEM "chrome://global/locale/global.dtd">
43 %globalDTD;
44 ]>
45
46 <bindings id="timepickerBindings"
47 xmlns="http://www.mozilla.org/xbl"
48 xmlns:html="http://www.w3.org/1999/xhtml"
49 xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
50 xmlns:xbl="http://www.mozilla.org/xbl">
51
52 <binding id="datetimepicker-base"
53 extends="chrome://global/content/bindings/general.xml#basecontrol">
54
55 <resources>
56 <stylesheet src="chrome://global/skin/textbox.css"/>
57 <stylesheet src="chrome://global/skin/dropmarker.css"/>
58 <stylesheet src="chrome://global/skin/datetimepicker.css"/>
59 </resources>
60
61 <content align="center">
62 <xul:hbox class="datetimepicker-input-box" align="center"
63 xbl:inherits="context">
64 <xul:hbox class="textbox-input-box datetimepicker-input-subbox" align="center">
65 <html:input class="datetimepicker-input textbox-input" anonid="input-one"
66 size="2" maxlength="2" flex="1" chromedir="&locale.dir;"
67 xbl:inherits="disabled,readonly"/>
68 </xul:hbox>
69 <xul:label anonid="sep-first" class="datetimepicker-separator" value=":"/>
70 <xul:hbox class="textbox-input-box datetimepicker-input-subbox" align="center">
71 <html:input class="datetimepicker-input textbox-input" anonid="input-two"
72 size="2" maxlength="2" flex="1" chromedir="&locale.dir;"
73 xbl:inherits="disabled,readonly"/>
74 </xul:hbox>
75 <xul:label anonid="sep-second" class="datetimepicker-separator" value=":"/>
76 <xul:hbox class="textbox-input-box datetimepicker-input-subbox" align="center">
77 <html:input class="datetimepicker-input textbox-input" anonid="input-three"
78 size="2" maxlength="2" flex="1" chromedir="&locale.dir;"
79 xbl:inherits="disabled,readonly"/>
80 </xul:hbox>
81 <xul:hbox class="textbox-input-box datetimepicker-input-subbox" align="center">
82 <html:input class="datetimepicker-input textbox-input" anonid="input-ampm"
83 size="2" maxlength="2" flex="1" chromedir="&locale.dir;"
84 xbl:inherits="disabled,readonly"/>
85 </xul:hbox>
86 </xul:hbox>
87 <xul:spinbuttons anonid="buttons" xbl:inherits="disabled"
88 onup="this.parentNode._increaseOrDecrease(1);"
89 ondown="this.parentNode._increaseOrDecrease(-1);"/>
90 </content>
91
92 <implementation>
93 <field name="_dateValue">null</field>
94 <field name="_fieldOne">
95 document.getAnonymousElementByAttribute(this, "anonid", "input-one");
96 </field>
97 <field name="_fieldTwo">
98 document.getAnonymousElementByAttribute(this, "anonid", "input-two");
99 </field>
100 <field name="_fieldThree">
101 document.getAnonymousElementByAttribute(this, "anonid", "input-three");
102 </field>
103 <field name="_fieldAMPM">
104 document.getAnonymousElementByAttribute(this, "anonid", "input-ampm");
105 </field>
106 <field name="_separatorFirst">
107 document.getAnonymousElementByAttribute(this, "anonid", "sep-first");
108 </field>
109 <field name="_separatorSecond">
110 document.getAnonymousElementByAttribute(this, "anonid", "sep-second");
111 </field>
112 <field name="_lastFocusedField">null</field>
113 <field name="_hasEntry">true</field>
114 <field name="_valueEntered">false</field>
115 <field name="attachedControl">null</field>
116
117 <property name="_currentField" readonly="true">
118 <getter>
119 var focusedInput = document.activeElement;
120 if (focusedInput == this._fieldOne ||
121 focusedInput == this._fieldTwo ||
122 focusedInput == this._fieldThree ||
123 focusedInput == this._fieldAMPM)
124 return focusedInput;
125 return this._lastFocusedField || this._fieldOne;
126 </getter>
127 </property>
128
129 <property name="dateValue" onget="return new Date(this._dateValue);">
130 <setter>
131 <![CDATA[
132 if (!(val instanceof Date))
133 throw "Invalid Date";
134
135 this._setValueNoSync(val);
136 if (this.attachedControl)
137 this.attachedControl._setValueNoSync(val);
138 return val;
139 ]]>
140 </setter>
141 </property>
142
143 <property name="readOnly" onset="if (val) this.setAttribute('readonly', 'true');
144 else this.removeAttribute('readonly'); return val;"
145 onget="return this.getAttribute('readonly') == 'true';"/>
146
147 <method name="_fireEvent">
148 <parameter name="aEventName"/>
149 <parameter name="aTarget"/>
150 <body>
151 var event = document.createEvent("Events");
152 event.initEvent(aEventName, true, true);
153 return !aTarget.dispatchEvent(event);
154 </body>
155 </method>
156
157 <method name="_setValueOnChange">
158 <parameter name="aField"/>
159 <body>
160 <![CDATA[
161 if (!this._hasEntry)
162 return;
163
164 if (aField == this._fieldOne ||
165 aField == this._fieldTwo ||
166 aField == this._fieldThree) {
167 var value = Number(aField.value);
168 if (isNaN(value))
169 value = 0;
170
171 value = this._constrainValue(aField, value, true);
172 this._setFieldValue(aField, value);
173 }
174 ]]>
175 </body>
176 </method>
177
178 <method name="_init">
179 <body/>
180 </method>
181
182 <constructor>
183 this._init();
184
185 var cval = this.getAttribute("value");
186 if (cval) {
187 try {
188 this.value = cval;
189 return;
190 } catch (ex) { }
191 }
192 this.dateValue = new Date();
193 </constructor>
194
195 <destructor>
196 if (this.attachedControl) {
197 this.attachedControl.attachedControl = null;
198 this.attachedControl = null;
199 }
200 </destructor>
201
202 </implementation>
203
204 <handlers>
205 <handler event="focus" phase="capturing">
206 <![CDATA[
207 var target = event.originalTarget;
208 if (target == this._fieldOne ||
209 target == this._fieldTwo ||
210 target == this._fieldThree ||
211 target == this._fieldAMPM)
212 this._lastFocusedField = target;
213 ]]>
214 </handler>
215
216 <handler event="keypress">
217 <![CDATA[
218 if (this._hasEntry && event.charCode &&
219 this._currentField != this._fieldAMPM &&
220 ! (event.altKey || event.ctrlKey || event.metaKey) &&
221 (event.charCode < 48 || event.charCode > 57))
222 event.preventDefault();
223 ]]>
224 </handler>
225
226 <handler event="keypress" keycode="VK_UP">
227 if (this._hasEntry)
228 this._increaseOrDecrease(1);
229 </handler>
230 <handler event="keypress" keycode="VK_DOWN">
231 if (this._hasEntry)
232 this._increaseOrDecrease(-1);
233 </handler>
234
235 <handler event="input">
236 this._valueEntered = true;
237 </handler>
238
239 <handler event="change">
240 this._setValueOnChange(event.originalTarget);
241 </handler>
242 </handlers>
243
244 </binding>
245
246 <binding id="timepicker"
247 extends="chrome://global/content/bindings/datetimepicker.xml#datetimepicker-base">
248
249 <implementation>
250 <field name="is24HourClock" readonly="true">false</field>
251 <field name="hourLeadingZero" readonly="true">false</field>
252 <field name="minuteLeadingZero" readonly="true">true</field>
253 <field name="secondLeadingZero" readonly="true">true</field>
254 <field name="amIndicator" readonly="true">"AM"</field>
255 <field name="pmIndicator" readonly="true">"PM"</field>
256
257 <field name="hourField">null</field>
258 <field name="minuteField">null</field>
259 <field name="secondField">null</field>
260
261 <property name="value">
262 <getter>
263 <![CDATA[
264 var minute = this._dateValue.getMinutes();
265 if (minute < 10)
266 minute = "0" + minute;
267
268 var second = this._dateValue.getSeconds();
269 if (second < 10)
270 second = "0" + second;
271 return this._dateValue.getHours() + ":" + minute + ":" + second;
272 ]]>
273 </getter>
274 <setter>
275 <![CDATA[
276 var items = val.match(/^([0-9]{1,2})\:([0-9]{1,2})\:?([0-9]{1,2})?$/);
277 if (!items)
278 throw "Invalid Time";
279
280 var dt = this.dateValue;
281 dt.setHours(items[1]);
282 dt.setMinutes(items[2]);
283 dt.setSeconds(items[3] ? items[3] : 0);
284 this.dateValue = dt;
285 return val;
286 ]]>
287 </setter>
288 </property>
289 <property name="hour" onget="return this._dateValue.getHours();">
290 <setter>
291 <![CDATA[
292 var valnum = Number(val);
293 if (isNaN(valnum) || valnum < 0 || valnum > 23)
294 throw "Invalid Hour";
295 this._setFieldValue(this.hourField, valnum);
296 return val;
297 ]]>
298 </setter>
299 </property>
300 <property name="minute" onget="return this._dateValue.getMinutes();">
301 <setter>
302 <![CDATA[
303 var valnum = Number(val);
304 if (isNaN(valnum) || valnum < 0 || valnum > 59)
305 throw "Invalid Minute";
306 this._setFieldValue(this.minuteField, valnum);
307 return val;
308 ]]>
309 </setter>
310 </property>
311 <property name="second" onget="return this._dateValue.getSeconds();">
312 <setter>
313 <![CDATA[
314 var valnum = Number(val);
315 if (isNaN(valnum) || valnum < 0 || valnum > 59)
316 throw "Invalid Second";
317 this._setFieldValue(this.secondField, valnum);
318 return val;
319 ]]>
320 </setter>
321 </property>
322 <property name="isPM">
323 <getter>
324 <![CDATA[
325 return (this.hour >= 12);
326 ]]>
327 </getter>
328 <setter>
329 <![CDATA[
330 if (val) {
331 if (this.hour < 12)
332 this.hour += 12;
333 }
334 else if (this.hour >= 12)
335 this.hour -= 12;
336 return val;
337 ]]>
338 </setter>
339 </property>
340 <property name="hideSeconds">
341 <getter>
342 return (this.getAttribute("hideseconds") == "true");
343 </getter>
344 <setter>
345 if (val)
346 this.setAttribute("hideseconds", "true");
347 else
348 this.removeAttribute("hideseconds");
349 if (this.secondField)
350 this.secondField.parentNode.collapsed = val;
351 this._separatorSecond.collapsed = val;
352 return val;
353 </setter>
354 </property>
355 <property name="increment">
356 <getter>
357 <![CDATA[
358 var increment = this.getAttribute("increment");
359 increment = Number(increment);
360 if (isNaN(increment) || increment <= 0 || increment >= 60)
361 return 1;
362 return increment;
363 ]]>
364 </getter>
365 <setter>
366 <![CDATA[
367 if (typeof val == "number")
368 this.setAttribute("increment", val);
369 return val;
370 ]]>
371 </setter>
372 </property>
373
374 <method name="_setValueNoSync">
375 <parameter name="aValue"/>
376 <body>
377 <![CDATA[
378 var dt = new Date(aValue);
379 if (!isNaN(dt)) {
380 this._dateValue = dt;
381 this.setAttribute("value", this.value);
382 this._updateUI(this.hourField, this.hour);
383 this._updateUI(this.minuteField, this.minute);
384 this._updateUI(this.secondField, this.second);
385 }
386 ]]>
387 </body>
388 </method>
389 <method name="_increaseOrDecrease">
390 <parameter name="aDir"/>
391 <body>
392 <![CDATA[
393 if (this.disabled || this.readOnly)
394 return;
395
396 var field = this._currentField;
397 if (this._valueEntered)
398 this._setValueOnChange(field);
399
400 if (field == this._fieldAMPM) {
401 this.isPM = !this.isPM;
402 this._fireEvent("change", this);
403 }
404 else {
405 var oldval;
406 var change = aDir;
407 if (field == this.hourField) {
408 oldval = this.hour;
409 }
410 else if (field == this.minuteField) {
411 oldval = this.minute;
412 change *= this.increment;
413 }
414 else if (field == this.secondField) {
415 oldval = this.second;
416 }
417
418 var newval = this._constrainValue(field, oldval + change, false);
419
420 if (field == this.hourField)
421 this.hour = newval;
422 else if (field == this.minuteField)
423 this.minute = newval;
424 else if (field == this.secondField)
425 this.second = newval;
426
427 if (oldval != newval)
428 this._fireEvent("change", this);
429 }
430 field.select();
431 ]]>
432 </body>
433 </method>
434 <method name="_setFieldValue">
435 <parameter name="aField"/>
436 <parameter name="aValue"/>
437 <body>
438 <![CDATA[
439 if (aField == this.hourField)
440 this._dateValue.setHours(aValue);
441 else if (aField == this.minuteField)
442 this._dateValue.setMinutes(aValue);
443 else if (aField == this.secondField)
444 this._dateValue.setSeconds(aValue);
445
446 this.setAttribute("value", this.value);
447 this._updateUI(aField, aValue);
448
449 if (this.attachedControl)
450 this.attachedControl._setValueNoSync(this._dateValue);
451 ]]>
452 </body>
453 </method>
454 <method name="_updateUI">
455 <parameter name="aField"/>
456 <parameter name="aValue"/>
457 <body>
458 <![CDATA[
459 this._valueEntered = false;
460
461 var prependZero = false;
462 if (aField == this.hourField) {
463 prependZero = this.hourLeadingZero;
464 if (!this.is24HourClock) {
465 if (aValue >= 12) {
466 if (aValue > 12)
467 aValue -= 12;
468 this._fieldAMPM.value = this.pmIndicator;
469 }
470 else {
471 if (aValue == 0)
472 aValue = 12;
473 this._fieldAMPM.value = this.amIndicator;
474 }
475 }
476 }
477 else if (aField == this.minuteField) {
478 prependZero = this.minuteLeadingZero;
479 }
480 else if (aField == this.secondField) {
481 prependZero = this.secondLeadingZero;
482 }
483
484 if (prependZero && aValue < 10)
485 aField.value = "0" + aValue;
486 else
487 aField.value = aValue;
488 ]]>
489 </body>
490 </method>
491 <method name="_constrainValue">
492 <parameter name="aField"/>
493 <parameter name="aValue"/>
494 <parameter name="aNoWrap"/>
495 <body>
496 <![CDATA[
497 // aNoWrap is true when the user entered a value, so just
498 // constrain within limits. If false, the value is being
499 // incremented or decremented, so wrap around values
500 var max = (aField == this.hourField) ? 24 : 60;
501 if (aValue < 0)
502 return aNoWrap ? 0 : max + aValue;
503 if (aValue >= max)
504 return aNoWrap ? max - 1 : aValue - max;
505 return aValue;
506 ]]>
507 </body>
508 </method>
509 <method name="_init">
510 <body>
511 <![CDATA[
512 this.hourField = this._fieldOne;
513 this.minuteField = this._fieldTwo;
514 this.secondField = this._fieldThree;
515
516 var numberOrder = /^(\D*)\s*(\d+)(\D*)(\d+)(\D*)(\d+)\s*(\D*)$/;
517
518 var pmTime = new Date(2000,0,1,16,7,9).toLocaleFormat("%X");
519 var numberFields = pmTime.match(numberOrder);
520 if (numberFields) {
521 this._separatorFirst.value = numberFields[3];
522 this._separatorSecond.value = numberFields[5];
523 if (Number(numberFields[2]) > 12)
524 this.is24HourClock = true;
525 else
526 this.pmIndicator = numberFields[1] || numberFields[7];
527 }
528
529 var amTime = new Date(2000,0,1,1,7,9).toLocaleFormat("%X");
530 numberFields = amTime.match(numberOrder);
531 if (numberFields) {
532 this.hourLeadingZero = (numberFields[2].length > 1);
533 this.minuteLeadingZero = (numberFields[4].length > 1);
534 this.secondLeadingZero = (numberFields[6].length > 1);
535
536 if (!this.is24HourClock) {
537 this.amIndicator = numberFields[1] || numberFields[7];
538 if (numberFields[1]) {
539 var mfield = this._fieldAMPM.parentNode;
540 var mcontainer = mfield.parentNode;
541 mcontainer.insertBefore(mfield, mcontainer.firstChild);
542 }
543 var size = (numberFields[1] || numberFields[7]).length;
544 if (this.pmIndicator.length > size)
545 size = this.pmIndicator.length;
546 this._fieldAMPM.size = size;
547 this._fieldAMPM.maxLength = size;
548 }
549 else {
550 this._fieldAMPM.parentNode.collapsed = true;
551 }
552 }
553
554 this.hideSeconds = this.hideSeconds;
555 ]]>
556 </body>
557 </method>
558 </implementation>
559
560 <handlers>
561 <handler event="keypress">
562 <![CDATA[
563 // just allow any printable character to switch the AM/PM state
564 if (event.charCode && !this.disabled && !this.readOnly &&
565 this._currentField == this._fieldAMPM) {
566 this.isPM = !this.isPM;
567 this._fieldAMPM.select();
568 this._fireEvent("change", this);
569 event.preventDefault();
570 }
571 ]]>
572 </handler>
573 </handlers>
574
575 </binding>
576
577 <binding id="datepicker"
578 extends="chrome://global/content/bindings/datetimepicker.xml#datetimepicker-base">
579
580 <implementation>
581 <field name="yearLeadingZero">false</field>
582 <field name="monthLeadingZero">true</field>
583 <field name="dateLeadingZero">true</field>
584
585 <field name="yearField"/>
586 <field name="monthField"/>
587 <field name="dateField"/>
588
589 <property name="value">
590 <getter>
591 <![CDATA[
592 var month = this._dateValue.getMonth();
593 month = (month < 9) ? month = "0" + ++month : month + 1;
594
595 var date = this._dateValue.getDate();
596 if (date < 10)
597 date = "0" + date;
598 return this._dateValue.getFullYear() + "-" + month + "-" + date;
599 ]]>
600
601 </getter>
602 <setter>
603 <![CDATA[
604 var results = val.match(/^([0-9]{1,4})\-([0-9]{1,2})\-([0-9]{1,2})$/);
605 if (!results)
606 throw "Invalid Date";
607
608 this.dateValue = new Date(results[1] + "/" + results[2] + "/" + results[3]);
609 this.setAttribute("value", this.value);
610 return val;
611 ]]>
612 </setter>
613 </property>
614 <property name="year" onget="return this._dateValue.getFullYear();">
615 <setter>
616 <![CDATA[
617 var valnum = Number(val);
618 if (isNaN(valnum) || valnum < 1 || valnum > 9999)
619 throw "Invalid Year";
620 this._setFieldValue(this.yearField, valnum);
621 return val;
622 ]]>
623 </setter>
624 </property>
625 <property name="month" onget="return this._dateValue.getMonth();">
626 <setter>
627 <![CDATA[
628 var valnum = Number(val);
629 if (isNaN(valnum) || valnum < 0 || valnum > 11)
630 throw "Invalid Month";
631 this._setFieldValue(this.monthField, valnum);
632 return val;
633 ]]>
634 </setter>
635 </property>
636 <property name="date" onget="return this._dateValue.getDate();">
637 <setter>
638 <![CDATA[
639 var valnum = Number(val);
640 if (isNaN(valnum) || valnum < 1 || valnum > 31)
641 throw "Invalid Date";
642 this._setFieldValue(this.dateField, valnum);
643 return val;
644 ]]>
645 </setter>
646 </property>
647 <property name="open" onget="return false;" onset="return val;"/>
648
649 <method name="_setValueNoSync">
650 <parameter name="aValue"/>
651 <body>
652 <![CDATA[
653 var dt = new Date(aValue);
654 if (!isNaN(dt)) {
655 this._dateValue = dt;
656 this.setAttribute("value", this.value);
657 this._updateUI(this.yearField, this.year);
658 this._updateUI(this.monthField, this.month);
659 this._updateUI(this.dateField, this.date);
660 }
661 ]]>
662 </body>
663 </method>
664 <method name="_increaseOrDecrease">
665 <parameter name="aDir"/>
666 <body>
667 <![CDATA[
668 if (this.disabled || this.readOnly)
669 return;
670
671 var field = this._currentField;
672 if (this._valueEntered)
673 this._setValueOnChange(field);
674
675 var oldval;
676 if (field == this.yearField)
677 oldval = this.year;
678 else if (field == this.monthField)
679 oldval = this.month;
680 else if (field == this.dateField)
681 oldval = this.date;
682
683 var newval = this._constrainValue(field, oldval + aDir, false);
684
685 if (field == this.yearField)
686 this.year = newval;
687 else if (field == this.monthField)
688 this.month = newval;
689 else if (field == this.dateField)
690 this.date = newval;
691
692 if (oldval != newval)
693 this._fireEvent("change", this);
694 field.select();
695 ]]>
696 </body>
697 </method>
698 <method name="_setFieldValue">
699 <parameter name="aField"/>
700 <parameter name="aValue"/>
701 <body>
702 <![CDATA[
703 if (aField == this.yearField) {
704 var oldDate = this.date;
705 this._dateValue.setFullYear(aValue);
706 if (oldDate != this.date) {
707 this._dateValue.setDate(0);
708 this._updateUI(this.dateField, this.date);
709 }
710 }
711 else if (aField == this.monthField) {
712 var oldDate = this.date;
713 this._dateValue.setMonth(aValue);
714 if (oldDate != this.date) {
715 this._dateValue.setDate(0);
716 this._updateUI(this.dateField, this.date);
717 }
718 }
719 else if (aField == this.dateField) {
720 this._dateValue.setDate(aValue);
721 }
722
723 this.setAttribute("value", this.value);
724 this._updateUI(aField, aValue);
725
726 if (this.attachedControl)
727 this.attachedControl._setValueNoSync(this._dateValue);
728 ]]>
729 </body>
730 </method>
731 <method name="_updateUI">
732 <parameter name="aField"/>
733 <parameter name="aValue"/>
734 <body>
735 <![CDATA[
736 this._valueEntered = false;
737
738 var prependZero = false;
739 if (aField == this.yearField) {
740 if (this.yearLeadingZero) {
741 aField.value = ("000" + aValue).slice(-4);
742 return;
743 }
744 }
745 else if (aField == this.monthField) {
746 aValue++;
747 prependZero = this.monthLeadingZero;
748 }
749 else if (aField == this.dateField) {
750 prependZero = this.dateLeadingZero;
751 }
752 if (prependZero && aValue < 10)
753 aField.value = "0" + aValue;
754 else
755 aField.value = aValue;
756 ]]>
757 </body>
758 </method>
759 <method name="_constrainValue">
760 <parameter name="aField"/>
761 <parameter name="aValue"/>
762 <parameter name="aNoWrap"/>
763 <body>
764 <![CDATA[
765 // the month will be 1 to 12 if entered by the user, so subtract 1
766 if (aNoWrap && aField == this.monthField)
767 aValue--;
768
769 if (aField == this.dateField) {
770 if (aValue < 1)
771 return new Date(this.year, this.month + 1, 0).getDate();
772
773 var currentMonth = this.month;
774 var dt = new Date(this.year, currentMonth, aValue);
775 return (dt.getMonth() != currentMonth ? 1 : aValue);
776 }
777 var min = (aField == this.monthField) ? 0 : 1;
778 var max = (aField == this.monthField) ? 11 : 9999;
779 if (aValue < min)
780 return aNoWrap ? min : max;
781 if (aValue > max)
782 return aNoWrap ? max : min;
783 return aValue;
784 ]]>
785 </body>
786 </method>
787 <method name="_init">
788 <body>
789 <![CDATA[
790 var numberOrder = /^(\D*)\s*(\d+)(\D*)(\d+)(\D*)(\d+)\s*(\D*)$/;
791
792 var dt = new Date(2002,9,4).toLocaleFormat("%x");
793 var numberFields = dt.match(numberOrder);
794 if (numberFields) {
795 this._separatorFirst.value = numberFields[3];
796 this._separatorSecond.value = numberFields[5];
797
798 var twoDigitYear = false;
799 var yfield = "input-one";
800 var mfield = "input-two";
801 var dfield = "input-three";
802 var yi = 2, mi = 4, di = 6;
803 for (var i = 1; i < numberFields.length; i++) {
804 switch (Number(numberFields[i])) {
805 case 2:
806 twoDigitYear = true; // fall through
807 case 2002:
808 yi = i;
809 yfield = (i == 2 ? "input-one" :
810 (i == 4 ? "input-two" : "input-three"));
811 break;
812 case 9, 10:
813 mi = i;
814 mfield = (i == 2 ? "input-one" :
815 (i == 4 ? "input-two" : "input-three"));
816 break;
817 case 4:
818 di = i;
819 dfield = (i == 2 ? "input-one" :
820 (i == 4 ? "input-two" : "input-three"));
821 break;
822 }
823 }
824 }
825
826 this.yearField = document.getAnonymousElementByAttribute(this, "anonid", yfield);
827 if (!twoDigitYear)
828 this.yearField.parentNode.className =
829 "datetimepicker-input-subbox datetimepicker-year";
830 this.monthField = document.getAnonymousElementByAttribute(this, "anonid", mfield);
831 this.dateField = document.getAnonymousElementByAttribute(this, "anonid", dfield);
832
833 this.yearLeadingZero = (numberFields[yi].length > 1);
834 this.monthLeadingZero = (numberFields[mi].length > 1);
835 this.dateLeadingZero = (numberFields[di].length > 1);
836
837 this._fieldAMPM.parentNode.collapsed = true;
838 this.yearField.size = twoDigitYear ? 2 : 4;
839 this.yearField.maxLength = twoDigitYear ? 2 : 4;
840 ]]>
841 </body>
842 </method>
843 </implementation>
844
845 </binding>
846
847 <binding id="datepicker-grid"
848 extends="chrome://global/content/bindings/datetimepicker.xml#datepicker">
849
850 <content>
851 <vbox class="datepicker-mainbox"
852 xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
853 <hbox class="datepicker-monthbox" align="center">
854 <button class="datepicker-previous datepicker-button" type="repeat"
855 chromedir="&locale.dir;" xbl:inherits="disabled"
856 oncommand="document.getBindingParent(this)._increaseOrDecreaseMonth(-1);"/>
857 <spacer flex="1"/>
858 <deck anonid="monthlabeldeck">
859 <label class="datepicker-gridlabel"/>
860 <label class="datepicker-gridlabel"/>
861 <label class="datepicker-gridlabel"/>
862 <label class="datepicker-gridlabel"/>
863 <label class="datepicker-gridlabel"/>
864 <label class="datepicker-gridlabel"/>
865 <label class="datepicker-gridlabel"/>
866 <label class="datepicker-gridlabel"/>
867 <label class="datepicker-gridlabel"/>
868 <label class="datepicker-gridlabel"/>
869 <label class="datepicker-gridlabel"/>
870 <label class="datepicker-gridlabel"/>
871 </deck>
872 <label anonid="yearlabel" class="datepicker-gridlabel"/>
873 <spacer flex="1"/>
874 <button class="datepicker-next datepicker-button" type="repeat"
875 chromedir="&locale.dir;" xbl:inherits="disabled"
876 oncommand="document.getBindingParent(this)._increaseOrDecreaseMonth(1);"/>
877 </hbox>
878 <grid class="datepicker-grid" role="grid">
879 <columns>
880 <column class="datepicker-gridrow" flex="1"/>
881 <column class="datepicker-gridrow" flex="1"/>
882 <column class="datepicker-gridrow" flex="1"/>
883 <column class="datepicker-gridrow" flex="1"/>
884 <column class="datepicker-gridrow" flex="1"/>
885 <column class="datepicker-gridrow" flex="1"/>
886 <column class="datepicker-gridrow" flex="1"/>
887 </columns>
888 <rows anonid="datebox">
889 <row anonid="dayofweekbox">
890 <label class="datepicker-weeklabel" role="columnheader"/>
891 <label class="datepicker-weeklabel" role="columnheader"/>
892 <label class="datepicker-weeklabel" role="columnheader"/>
893 <label class="datepicker-weeklabel" role="columnheader"/>
894 <label class="datepicker-weeklabel" role="columnheader"/>
895 <label class="datepicker-weeklabel" role="columnheader"/>
896 <label class="datepicker-weeklabel" role="columnheader"/>
897 </row>
898 <row>
899 <label class="datepicker-gridlabel" role="gridcell"/>
900 <label class="datepicker-gridlabel" role="gridcell"/>
901 <label class="datepicker-gridlabel" role="gridcell"/>
902 <label class="datepicker-gridlabel" role="gridcell"/>
903 <label class="datepicker-gridlabel" role="gridcell"/>
904 <label class="datepicker-gridlabel" role="gridcell"/>
905 <label class="datepicker-gridlabel" role="gridcell"/>
906 </row>
907 <row>
908 <label class="datepicker-gridlabel" role="gridcell"/>
909 <label class="datepicker-gridlabel" role="gridcell"/>
910 <label class="datepicker-gridlabel" role="gridcell"/>
911 <label class="datepicker-gridlabel" role="gridcell"/>
912 <label class="datepicker-gridlabel" role="gridcell"/>
913 <label class="datepicker-gridlabel" role="gridcell"/>
914 <label class="datepicker-gridlabel" role="gridcell"/>
915 </row>
916 <row>
917 <label class="datepicker-gridlabel" role="gridcell"/>
918 <label class="datepicker-gridlabel" role="gridcell"/>
919 <label class="datepicker-gridlabel" role="gridcell"/>
920 <label class="datepicker-gridlabel" role="gridcell"/>
921 <label class="datepicker-gridlabel" role="gridcell"/>
922 <label class="datepicker-gridlabel" role="gridcell"/>
923 <label class="datepicker-gridlabel" role="gridcell"/>
924 </row>
925 <row>
926 <label class="datepicker-gridlabel" role="gridcell"/>
927 <label class="datepicker-gridlabel" role="gridcell"/>
928 <label class="datepicker-gridlabel" role="gridcell"/>
929 <label class="datepicker-gridlabel" role="gridcell"/>
930 <label class="datepicker-gridlabel" role="gridcell"/>
931 <label class="datepicker-gridlabel" role="gridcell"/>
932 <label class="datepicker-gridlabel" role="gridcell"/>
933 </row>
934 <row>
935 <label class="datepicker-gridlabel" role="gridcell"/>
936 <label class="datepicker-gridlabel" role="gridcell"/>
937 <label class="datepicker-gridlabel" role="gridcell"/>
938 <label class="datepicker-gridlabel" role="gridcell"/>
939 <label class="datepicker-gridlabel" role="gridcell"/>
940 <label class="datepicker-gridlabel" role="gridcell"/>
941 <label class="datepicker-gridlabel" role="gridcell"/>
942 </row>
943 <row>
944 <label class="datepicker-gridlabel" role="gridcell"/>
945 <label class="datepicker-gridlabel" role="gridcell"/>
946 <label class="datepicker-gridlabel" role="gridcell"/>
947 <label class="datepicker-gridlabel" role="gridcell"/>
948 <label class="datepicker-gridlabel" role="gridcell"/>
949 <label class="datepicker-gridlabel" role="gridcell"/>
950 <label class="datepicker-gridlabel" role="gridcell"/>
951 </row>
952 </rows>
953 </grid>
954 </vbox>
955 </content>
956
957 <implementation>
958 <field name="_hasEntry">false</field>
959 <field name="_weekStart">&firstdayofweek.default;</field>
960 <field name="_displayedDate">null</field>
961 <field name="_todayItem">null</field>
962
963 <field name="yearField">
964 document.getAnonymousElementByAttribute(this, "anonid", "yearlabel");
965 </field>
966 <field name="monthField">
967 document.getAnonymousElementByAttribute(this, "anonid", "monthlabeldeck");
968 </field>
969 <field name="dateField">
970 document.getAnonymousElementByAttribute(this, "anonid", "datebox");
971 </field>
972
973 <field name="_selectedItem">null</field>
974
975 <property name="selectedItem" onget="return this._selectedItem">
976 <setter>
977 <![CDATA[
978 if (!val.value)
979 return val;
980 if (val.parentNode.parentNode != this.dateField)
981 return val;
982
983 if (this._selectedItem)
984 this._selectedItem.removeAttribute("selected");
985 this._selectedItem = val;
986 val.setAttribute("selected", "true");
987 this._displayedDate.setDate(val.value);
988 return val;
989 ]]>
990 </setter>
991 </property>
992
993 <method name="_init">
994 <body>
995 <![CDATA[
996 var monthLabel = this.monthField.firstChild;
997 var tempDate = new Date(2005, 0, 1);
998 for (var month = 0; month < 12; month++) {
999 tempDate.setMonth(month);
1000 monthLabel.setAttribute("value", tempDate.toLocaleFormat("%B"));
1001 monthLabel = monthLabel.nextSibling;
1002 }
1003
1004 var fdow = Number(this.getAttribute("firstdayofweek"));
1005 if (!isNaN(fdow) && fdow >= 0 && fdow <= 6)
1006 this._weekStart = fdow;
1007
1008 var weekbox = document.getAnonymousElementByAttribute(this, "anonid", "dayofweekbox").childNodes;
1009 var date = new Date();
1010 date.setDate(date.getDate() - (date.getDay() - this._weekStart));
1011 for (var i = 0; i < weekbox.length; i++) {
1012 weekbox[i].value = date.toLocaleFormat("%a").charAt(0);
1013 date.setDate(date.getDate() + 1);
1014 }
1015 ]]>
1016 </body>
1017 </method>
1018 <method name="_setValueNoSync">
1019 <parameter name="aValue"/>
1020 <body>
1021 <![CDATA[
1022 var dt = new Date(aValue);
1023 if (!isNaN(dt)) {
1024 this._dateValue = dt;
1025 this.setAttribute("value", this.value);
1026 this._updateUI();
1027 }
1028 ]]>
1029 </body>
1030 </method>
1031 <method name="_updateUI">
1032 <parameter name="aField"/>
1033 <parameter name="aValue"/>
1034 <parameter name="aIncDecMonth"/>
1035 <body>
1036 <![CDATA[
1037 var date;
1038 var currentMonth;
1039 if (aIncDecMonth) {
1040 if (!this._displayedDate)
1041 this._displayedDate = this.dateValue;
1042 this._displayedDate.setMonth(aValue);
1043
1044 date = new Date(this._displayedDate);
1045 currentMonth = this._displayedDate.getMonth();
1046 }
1047 else {
1048 var samemonth = (this._displayedDate &&
1049 this._displayedDate.getMonth() == this.month &&
1050 this._displayedDate.getFullYear() == this.year);
1051 if (samemonth) {
1052 var items = this.dateField.getElementsByAttribute("value", this.date);
1053 if (items.length)
1054 this.selectedItem = items[0];
1055 return;
1056 }
1057
1058 date = this.dateValue;
1059 this._displayedDate = new Date(date);
1060 currentMonth = this.month;
1061 }
1062
1063 if (this._todayItem) {
1064 this._todayItem.removeAttribute("today");
1065 this._todayItem = null;
1066 }
1067
1068 if (this._selectedItem) {
1069 this._selectedItem.removeAttribute("selected");
1070 this._selectedItem = null;
1071 }
1072
1073 // Update the month and year title
1074 this.monthField.selectedIndex = currentMonth;
1075 this.yearField.setAttribute("value", date.getFullYear());
1076
1077 date.setDate(1);
1078 var firstWeekday = (7 + date.getDay() - this._weekStart) % 7;
1079 date.setDate(date.getDate() - firstWeekday);
1080
1081 var today = new Date();
1082 var datebox = this.dateField;
1083 for (var k = 1; k < datebox.childNodes.length; k++) {
1084 var row = datebox.childNodes[k];
1085 for (var i = 0; i < 7; i++) {
1086 var item = row.childNodes[i];
1087
1088 if (currentMonth == date.getMonth()) {
1089 item.value = date.getDate();
1090
1091 // highlight today
1092 if (this._isSameDay(today, date)) {
1093 this._todayItem = item;
1094 item.setAttribute("today", "true");
1095 }
1096
1097 // highlight the selected date
1098 if (this._isSameDay(this._dateValue, date)) {
1099 this._selectedItem = item;
1100 item.setAttribute("selected", "true");
1101 }
1102 }
1103 else {
1104 item.value = "";
1105 }
1106
1107 date.setDate(date.getDate() + 1);
1108 }
1109 }
1110
1111 this._fireEvent("monthchange", this);
1112 if (this.hasAttribute("monthchange")) {
1113 var fn = new Function("event", aTarget.getAttribute("onmonthchange"));
1114 fn.call(aTarget, event);
1115 }
1116 ]]>
1117 </body>
1118 </method>
1119 <method name="_increaseOrDecreaseDateFromEvent">
1120 <parameter name="aEvent"/>
1121 <parameter name="aDiff"/>
1122 <body>
1123 <![CDATA[
1124 if (aEvent.originalTarget == this && !this.disabled && !this.readOnly) {
1125 var newdate = this.dateValue;
1126 newdate.setDate(newdate.getDate() + aDiff);
1127 this.dateValue = newdate;
1128 this._fireEvent("change", this);
1129 }
1130 aEvent.stopPropagation();
1131 aEvent.preventDefault();
1132 ]]>
1133 </body>
1134 </method>
1135 <method name="_increaseOrDecreaseMonth">
1136 <parameter name="aDir"/>
1137 <body>
1138 <![CDATA[
1139 if (!this.disabled) {
1140 var month = this._displayedDate ? this._displayedDate.getMonth() :
1141 this.month;
1142 this._updateUI(this.monthField, month + aDir, true);
1143 }
1144 ]]>
1145 </body>
1146 </method>
1147 <method name="_isSameDay">
1148 <parameter name="aDate1"/>
1149 <parameter name="aDate2"/>
1150 <body>
1151 <![CDATA[
1152 return (aDate1 && aDate2 &&
1153 aDate1.getDate() == aDate2.getDate() &&
1154 aDate1.getMonth() == aDate2.getMonth() &&
1155 aDate1.getFullYear() == aDate2.getFullYear());
1156 ]]>
1157 </body>
1158 </method>
1159
1160 </implementation>
1161
1162 <handlers>
1163 <handler event="click">
1164 <![CDATA[
1165 if (event.button != 0 || this.disabled || this.readOnly)
1166 return;
1167
1168 var target = event.originalTarget;
1169 if (target.className == "datepicker-gridlabel" &&
1170 target != this.selectedItem) {
1171 this.selectedItem = target;
1172 this._dateValue = new Date(this._displayedDate);
1173 if (this.attachedControl)
1174 this.attachedControl._setValueNoSync(this._dateValue);
1175 this._fireEvent("change", this);
1176
1177 if (this.attachedControl && "open" in this.attachedControl)
1178 this.attachedControl.open = false; // close the popup
1179 }
1180 ]]>
1181 </handler>
1182 <handler event="DOMMouseScroll">
1183 <![CDATA[
1184 this._increaseOrDecreaseMonth(event.detail < 0 ? -1 : 1);
1185 ]]>
1186 </handler>
1187 <handler event="keypress" keycode="VK_LEFT"
1188 action="this._increaseOrDecreaseDateFromEvent(event, -1);"/>
1189 <handler event="keypress" keycode="VK_RIGHT"
1190 action="this._increaseOrDecreaseDateFromEvent(event, 1);"/>
1191 <handler event="keypress" keycode="VK_UP"
1192 action="this._increaseOrDecreaseDateFromEvent(event, -7);"/>
1193 <handler event="keypress" keycode="VK_DOWN"
1194 action="this._increaseOrDecreaseDateFromEvent(event, 7);"/>
1195 <handler event="keypress" keycode="VK_PAGE_UP"
1196 action="this._increaseOrDecreaseMonth(-1);"/>
1197 <handler event="keypress" keycode="VK_PAGE_DOWN"
1198 action="this._increaseOrDecreaseMonth(1);"/>
1199 </handlers>
1200 </binding>
1201
1202 <binding id="datepicker-popup" display="xul:menu"
1203 extends="chrome://global/content/bindings/datetimepicker.xml#datepicker">
1204 <content align="center">
1205 <xul:hbox class="textbox-input-box datetimepicker-input-box" align="center"
1206 allowevents="true" xbl:inherits="context">
1207 <xul:hbox class="datetimepicker-input-subbox" align="baseline">
1208 <html:input class="datetimepicker-input textbox-input" anonid="input-one"
1209 size="2" maxlength="2" flex="1" chromedir="&locale.dir;"
1210 xbl:inherits="disabled,readonly"/>
1211 </xul:hbox>
1212 <xul:label anonid="sep-first" class="datetimepicker-separator" value=":"/>
1213 <xul:hbox class="datetimepicker-input-subbox" align="baseline">
1214 <html:input class="datetimepicker-input textbox-input" anonid="input-two"
1215 size="2" maxlength="2" flex="1" chromedir="&locale.dir;"
1216 xbl:inherits="disabled,readonly"/>
1217 </xul:hbox>
1218 <xul:label anonid="sep-second" class="datetimepicker-separator" value=":"/>
1219 <xul:hbox class="datetimepicker-input-subbox" align="center">
1220 <html:input class="datetimepicker-input textbox-input" anonid="input-three"
1221 size="2" maxlength="2" flex="1" chromedir="&locale.dir;"
1222 xbl:inherits="disabled,readonly"/>
1223 </xul:hbox>
1224 <xul:hbox class="datetimepicker-input-subbox" align="center">
1225 <html:input class="datetimepicker-input textbox-input" anonid="input-ampm"
1226 size="2" maxlength="2" flex="1" chromedir="&locale.dir;"
1227 xbl:inherits="disabled,readonly"/>
1228 </xul:hbox>
1229 </xul:hbox>
1230 <xul:spinbuttons anonid="buttons" xbl:inherits="disabled" allowevents="true"
1231 onup="this.parentNode._increaseOrDecrease(1);"
1232 ondown="this.parentNode._increaseOrDecrease(-1);"/>
1233 <xul:dropmarker class="datepicker-dropmarker" xbl:inherits="disabled"/>
1234 <xul:panel onpopupshown="this.firstChild.focus();">
1235 <xul:datepicker anonid="grid" type="grid" class="datepicker-popupgrid"
1236 xbl:inherits="disabled,readonly,firstdayofweek"/>
1237 </xul:panel>
1238 </content>
1239 <implementation>
1240 <constructor>
1241 var grid = document.getAnonymousElementByAttribute(this, "anonid", "grid");
1242 this.attachedControl = grid;
1243 grid.attachedControl = this;
1244 grid._setValueNoSync(this._dateValue);
1245 </constructor>
1246 <property name="open" onget="return this.hasAttribute('open');">
1247 <setter>
1248 <![CDATA[
1249 if (this.boxObject instanceof Components.interfaces.nsIMenuBoxObject)
1250 this.boxObject.openMenu(val);
1251 return val;
1252 ]]>
1253 </setter>
1254 </property>
1255 </implementation>
1256 </binding>
1257
1258 </bindings>