source: Dev/trunk/src/client/dojox/mobile/ValuePickerSlot.js @ 529

Last change on this file since 529 was 483, checked in by hendrikvanantwerpen, 11 years ago

Added Dojo 1.9.3 release.

File size: 11.3 KB
Line 
1define([
2        "dojo/_base/array",
3        "dojo/_base/declare",
4        "dojo/_base/event",
5        "dojo/_base/lang",
6        "dojo/_base/window",
7        "dojo/dom-class",
8        "dojo/dom-construct",
9        "dojo/dom-attr",
10        "dojo/touch",
11        "dijit/_WidgetBase",
12        "./iconUtils",
13        "dojo/has",
14        "dojo/has!dojo-bidi?dojox/mobile/bidi/ValuePickerSlot"
15], function(array, declare, event, lang, win, domClass, domConstruct, domAttr, touch, WidgetBase, iconUtils, has, BidiValuePickerSlot){
16
17        // module:
18        //              dojox/mobile/ValuePickerSlot
19
20        var ValuePickerSlot = declare(has("dojo-bidi") ?  "dojox.mobile.NonBidiValuePickerSlot" : "dojox.mobile.ValuePickerSlot", WidgetBase, {
21                // summary:
22                //              A widget representing one slot of a ValuePicker widget.
23               
24                // items: Array
25                //              An array of array of key-label pairs
26                //              (e.g. [[0, "Jan"], [1,"Feb"], ...]). If key values for each label
27                //              are not necessary, labels can be used instead.
28                items: [],
29
30                // labels: String[]
31                //              An array of labels to be displayed on the value picker
32                //              (e.g. ["Jan","Feb",...]). This is a simplified version of the
33                //              items property.
34                labels: [],
35
36                // labelFrom: Number
37                //              The start value of display values of the value picker. This
38                //              parameter is especially useful when value picker has serial
39                //              values.
40                labelFrom: 0,
41
42                // labelTo: Number
43                //              The end value of display values of the value picker.
44                labelTo: 0,
45
46                // zeroPad: Number
47                //              Length of zero padding numbers.
48                //              Ex. zeroPad=2 -> "00", "01", ...
49                //              Ex. zeroPad=3 -> "000", "001", ...
50                zeroPad: 0,
51
52                // value: String
53                //              The initial value of the value picker.
54                value: "",
55
56                // step: Number
57                //              The steps between labelFrom and labelTo.
58                step: 1,
59
60                // readOnly: [const] Boolean
61                //              A flag used to indicate if the input field is readonly or not.
62                //              Note that changing the value of the property after the widget
63                //              creation has no effect.
64                readOnly: false,
65
66                // tabIndex: String
67                //              Tabindex setting for this widget so users can hit the tab key to
68                //              focus on it.
69                tabIndex: "0",
70
71                // key: Object
72                //              The key of the currently selected value in the items array. This is a read-only property.
73                //              Warning: Do not use this property directly, make sure to call the get() method.
74                /*=====
75                key: null,
76                =====*/
77               
78                // plusBtnLabel: String
79                //              (Accessibility) Text label for plus button
80                plusBtnLabel: "",
81               
82                // plusBtnLabelRef: String
83                //              (Accessibility) Reference to a node id containing text label for plus button
84                plusBtnLabelRef: "",
85               
86                // minusBtnLabel: String
87                //              (Accessibility) Text label for minus button
88                minusBtnLabel: "",
89               
90                // minusBtnLabelRef: String
91                //              (Accessibility) Reference to a node id containing text label for minus button
92                minusBtnLabelRef: "",
93
94                /* internal properties */       
95                baseClass: "mblValuePickerSlot",
96
97                buildRendering: function(){
98                        this.inherited(arguments);
99
100                        this.initLabels();
101                        if(this.labels.length > 0){
102                                this.items = [];
103                                for(var i = 0; i < this.labels.length; i++){
104                                        this.items.push([i, this.labels[i]]);
105                                }
106                        }
107
108                        this.plusBtnNode = domConstruct.create("div", {
109                                className: "mblValuePickerSlotPlusButton mblValuePickerSlotButton",
110                                title: "+"
111                        }, this.domNode);
112                       
113                        this.plusIconNode = domConstruct.create("div", {
114                                className: "mblValuePickerSlotIcon"
115                        }, this.plusBtnNode);
116                        iconUtils.createIcon("mblDomButtonGrayPlus", null, this.plusIconNode);
117
118                        this.inputAreaNode = domConstruct.create("div", {
119                                className: "mblValuePickerSlotInputArea"
120                        }, this.domNode);
121                        this.inputNode = domConstruct.create("input", {
122                                className: "mblValuePickerSlotInput",
123                                readonly: this.readOnly
124                        }, this.inputAreaNode);
125                       
126                        this.minusBtnNode = domConstruct.create("div", {
127                                className: "mblValuePickerSlotMinusButton mblValuePickerSlotButton",
128                                title: "-"
129                        }, this.domNode);
130                        this.minusIconNode = domConstruct.create("div", {
131                                className: "mblValuePickerSlotIcon"
132                        }, this.minusBtnNode);
133                        iconUtils.createIcon("mblDomButtonGrayMinus", null, this.minusIconNode);
134                       
135                        domAttr.set(this.plusBtnNode, "role", "button"); //a11y
136                        this._setPlusBtnLabelAttr(this.plusBtnLabel)
137                        this._setPlusBtnLabelRefAttr(this.plusBtnLabelRef);
138                       
139                        domAttr.set(this.inputNode, "role", "textbox");
140                        var registry = require("dijit/registry");
141                        var inputAreaNodeId =  registry.getUniqueId("dojo_mobile__mblValuePickerSlotInput");
142                        domAttr.set(this.inputNode, "id", inputAreaNodeId);
143                        domAttr.set(this.plusBtnNode, "aria-controls", inputAreaNodeId);
144                       
145                        domAttr.set(this.minusBtnNode, "role", "button");
146                        domAttr.set(this.minusBtnNode, "aria-controls", inputAreaNodeId);
147                        this._setMinusBtnLabelAttr(this.minusBtnLabel);
148                        this._setMinusBtnLabelRefAttr(this.minusBtnLabelRef);
149
150                        if(this.value === "" && this.items.length > 0){
151                                this.value = this.items[0][1];
152                        }
153                        this._initialValue = this.value;
154                },
155
156                startup: function(){
157                        if(this._started){ return; }
158                        this._handlers = [
159                                this.connect(this.plusBtnNode, touch.press, "_onTouchStart"),
160                                this.connect(this.minusBtnNode, touch.press, "_onTouchStart"),
161                                this.connect(this.plusBtnNode, "onkeydown", "_onClick"), // for desktop browsers
162                                this.connect(this.minusBtnNode, "onkeydown", "_onClick"), // for desktop browsers
163                                this.connect(this.inputNode, "onchange", lang.hitch(this, function(e){
164                                        this._onChange(e);
165                                }))
166                        ];
167                        this.inherited(arguments);
168                        this._set(this.plusBtnLabel);
169                },
170
171                initLabels: function(){
172                        // summary:
173                        //              Initializes the labels of this slot according to the labelFrom and labelTo properties.
174                        // tags:
175                        //              private
176                        if(this.labelFrom !== this.labelTo){
177                                var a = this.labels = [],
178                                        zeros = this.zeroPad && Array(this.zeroPad).join("0");
179                                for(var i = this.labelFrom; i <= this.labelTo; i += this.step){
180                                        a.push(this.zeroPad ? (zeros + i).slice(-this.zeroPad) : i + "");
181                                }
182                        }
183                },
184
185                spin: function(/*Number*/steps){
186                        // summary:
187                        //              Spins the slot as specified by steps.
188
189                        // find the position of the current value
190                        var pos = -1,
191                                v = this.get("value"),
192                                len = this.items.length;
193                        for(var i = 0; i < len; i++){
194                                if(this.items[i][1] === v){
195                                        pos = i;
196                                        break;
197                                }
198                        }
199                        if(v == -1){ return; }
200                        pos += steps;
201                        if(pos < 0){ // shift to positive
202                                pos += (Math.abs(Math.ceil(pos / len)) + 1) * len;
203                        }
204                        var newItem = this.items[pos % len];
205                        this.set("value", newItem[1]);
206                },
207
208                setInitialValue: function(){
209                        // summary:
210                        //              Sets the initial value using this.value or the first item.
211                        this.set("value", this._initialValue);
212                },
213
214                _onClick: function(e){
215                        // summary:
216                        //              Internal handler for click events.
217                        // tags:
218                        //              private
219                        if(e && e.type === "keydown" && e.keyCode !== 13){ return; }
220                        if(this.onClick(e) === false){ return; } // user's click action
221                        var node = e.currentTarget;
222                        if(node === this.plusBtnNode || node === this.minusBtnNode){
223                                this._btn = node;
224                        }
225                        this.spin(this._btn === this.plusBtnNode ? 1 : -1);
226                },
227
228                onClick: function(/*Event*/ /*===== e =====*/){
229                        // summary:
230                        //              User defined function to handle clicks
231                        // tags:
232                        //              callback
233                },
234
235                _onChange: function(e){
236                        // summary:
237                        //              Internal handler for the input field's value change events
238                        // tags:
239                        //              callback
240                        if(this.onChange(e) === false){ return; } // user's click action
241                        var v = this.get("value"), // text in the input field
242                                a = this.validate(v);
243                        this.set("value", a.length ? a[0][1] : this.value);
244                },
245
246                onChange: function(/*Event*/ /*===== e =====*/){
247                        // summary:
248                        //              User defined function to handle value changes
249                        // tags:
250                        //              callback
251                },
252
253                validate: function(value){
254                        return array.filter(this.items, function(a){
255                                return (a[1] + "").toLowerCase() == (value + "").toLowerCase();
256                        });
257                },
258
259                _onTouchStart: function(e){
260                        this._conn = [
261                                this.connect(win.body(), touch.move, "_onTouchMove"),
262                                this.connect(win.body(), touch.release, "_onTouchEnd")
263                        ];
264                        this.touchStartX = e.touches ? e.touches[0].pageX : e.clientX;
265                        this.touchStartY = e.touches ? e.touches[0].pageY : e.clientY;
266                        domClass.add(e.currentTarget, "mblValuePickerSlotButtonSelected");
267                        this._btn = e.currentTarget;
268                        if(this._timer){
269                                this._timer.remove(); // fail safe
270                                this._timer = null;
271                        }
272                        if(this._interval){
273                                clearInterval(this._interval); // fail safe
274                                this._interval = null;
275                        }
276                        this._timer = this.defer(function(){
277                                this._interval = setInterval(lang.hitch(this, function(){
278                                        this.spin(this._btn === this.plusBtnNode ? 1 : -1);
279                                }), 60);
280                                this._timer = null;
281                        }, 1000);
282                        event.stop(e);
283                },
284
285                _onTouchMove: function(e){
286                        var x = e.touches ? e.touches[0].pageX : e.clientX;
287                        var y = e.touches ? e.touches[0].pageY : e.clientY;
288                        if(Math.abs(x - this.touchStartX) >= 4 ||
289                           Math.abs(y - this.touchStartY) >= 4){ // dojox/mobile/scrollable.threshold
290                                if(this._timer){
291                                        this._timer.remove(); // fail safe
292                                        this._timer = null;
293                                }
294                                if(this._interval){
295                                        clearInterval(this._interval); // fail safe
296                                        this._interval = null;
297                                }
298                                array.forEach(this._conn, this.disconnect, this);
299                                domClass.remove(this._btn, "mblValuePickerSlotButtonSelected");
300                        }
301                },
302
303                _onTouchEnd: function(e){
304                        if(this._timer){
305                                this._timer.remove();
306                                this._timer = null;
307                        }
308                        array.forEach(this._conn, this.disconnect, this);
309                        domClass.remove(this._btn, "mblValuePickerSlotButtonSelected");
310                        if(this._interval){
311                                clearInterval(this._interval);
312                                this._interval = null;
313                        }else{
314                                this._onClick(e);
315                        }
316                },
317
318                _getKeyAttr: function(){
319                        var val = this.get("value");
320                        var item = array.filter(this.items, function(item){
321                                return item[1] === val;
322                        })[0];
323                        return item ? item[0] : null;
324                },
325
326                _getValueAttr: function(){
327                        // summary:
328                        //              Gets the currently selected value.
329                        return this.inputNode.value;
330                },
331
332                _setValueAttr: function(value){
333                        // summary:
334                        //              Sets a new value to this slot.
335                        this._spinToValue(value, true);
336                },
337               
338                _spinToValue: function(value, applyValue){
339                        // summary:
340                        //              Sets a new value to this slot.
341                        // tags:
342                        //              private
343                        if(this.get("value") == value){
344                                return; // no change; avoid notification
345                        }
346                        this.inputNode.value = value;
347                        // to avoid unnecessary notifications, applyValue is undefined when
348                        // _spinToValue is called by _DatePickerMixin.
349                        if(applyValue){
350                                this._set("value", value);
351                        }
352                        var parent = this.getParent();
353                        if(parent && parent.onValueChanged){
354                                parent.onValueChanged(this);
355                        }
356                },
357
358                _setTabIndexAttr: function(/*String*/ tabIndex){
359                        this.plusBtnNode.setAttribute("tabIndex", tabIndex);
360                        this.minusBtnNode.setAttribute("tabIndex", tabIndex);
361                },
362               
363                _setAria: function(node, attr, value){
364                        if(value){
365                                domAttr.set(node, attr, value);
366                        }else{
367                                domAttr.remove(node, attr);
368                        }
369                },
370               
371                _setPlusBtnLabelAttr: function(/*String*/ plusBtnLabel){
372                        this._setAria(this.plusBtnNode, "aria-label", plusBtnLabel);
373                },
374               
375                _setPlusBtnLabelRefAttr: function(/*String*/ plusBtnLabelRef){
376                        this._setAria(this.plusBtnNode, "aria-labelledby", plusBtnLabelRef);
377                },
378               
379                _setMinusBtnLabelAttr: function(/*String*/ minusBtnLabel){
380                        this._setAria(this.minusBtnNode, "aria-label", minusBtnLabel);
381                },
382               
383                _setMinusBtnLabelRefAttr: function(/*String*/ minusBtnLabelRef){
384                        this._setAria(this.minusBtnNode, "aria-labelledby", minusBtnLabelRef);
385                }
386        });
387       
388        return has("dojo-bidi") ? declare("dojox.mobile.ValuePickerSlot", [ValuePickerSlot, BidiValuePickerSlot]) : ValuePickerSlot;
389});
Note: See TracBrowser for help on using the repository browser.