toolkit/content/widgets/datetimepicker.xml

Sat, 03 Jan 2015 20:18:00 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Sat, 03 Jan 2015 20:18:00 +0100
branch
TOR_BUG_3246
changeset 7
129ffea94266
permissions
-rw-r--r--

Conditionally enable double key logic according to:
private browsing mode or privacy.thirdparty.isolate preference and
implement in GetCookieStringCommon and FindCookie where it counts...
With some reservations of how to convince FindCookie users to test
condition and pass a nullptr when disabling double key logic.

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

mercurial