source: Dev/trunk/src/client/dojox/mobile/_ItemBase.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: 14.0 KB
Line 
1define([
2        "dojo/_base/array",
3        "dojo/_base/declare",
4        "dojo/_base/lang",
5        "dojo/_base/window",
6        "dojo/dom-class",
7        "dojo/touch",
8        "dijit/registry",
9        "dijit/_Contained",
10        "dijit/_Container",
11        "dijit/_WidgetBase",
12        "./TransitionEvent",
13        "./iconUtils",
14        "./sniff",
15        "dojo/has!dojo-bidi?dojox/mobile/bidi/_ItemBase"
16], function(array, declare, lang, win, domClass, touch, registry, Contained, Container, WidgetBase, TransitionEvent, iconUtils, has, BidiItemBase){
17
18        // module:
19        //              dojox/mobile/_ItemBase
20
21        var _ItemBase = declare(has("dojo-bidi") ? "dojox.mobile._NonBidiItemBase" : "dojox.mobile._ItemBase", [WidgetBase, Container, Contained], {
22                // summary:
23                //              A base class for item classes (e.g. ListItem, IconItem, etc.).
24                // description:
25                //              _ItemBase is a base class for widgets that have capability to
26                //              make a view transition when clicked.
27
28                // icon: String
29                //              An icon image to display. The value can be either a path for an
30                //              image file or a class name of a DOM button. If icon is not
31                //              specified, the iconBase parameter of the parent widget is used.
32                icon: "",
33
34                // iconPos: String
35                //              The position of an aggregated icon. IconPos is comma separated
36                //              values like top,left,width,height (ex. "0,0,29,29"). If iconPos
37                //              is not specified, the iconPos parameter of the parent widget is
38                //              used.
39                iconPos: "", // top,left,width,height (ex. "0,0,29,29")
40
41                // alt: String
42                //              An alternate text for the icon image.
43                alt: "",
44
45                // href: String
46                //              A URL of another web page to go to.
47                href: "",
48
49                // hrefTarget: String
50                //              A target that specifies where to open a page specified by
51                //              href. The value will be passed to the 2nd argument of
52                //              window.open().
53                hrefTarget: "",
54
55                // moveTo: String
56                //              The id of the transition destination view which resides in the
57                //              current page.
58                //
59                //              If the value has a hash sign ('#') before the id (e.g. #view1)
60                //              and the dojo/hash module is loaded by the user application, the
61                //              view transition updates the hash in the browser URL so that the
62                //              user can bookmark the destination view. In this case, the user
63                //              can also use the browser's back/forward button to navigate
64                //              through the views in the browser history.
65                //
66                //              If null, transitions to a blank view.
67                //              If '#', returns immediately without transition.
68                moveTo: "",
69
70                // scene: String
71                //              The name of a scene. Used from dojox/mobile/app.
72                scene: "",
73
74                // clickable: Boolean
75                //              If true, this item becomes clickable even if a transition
76                //              destination (moveTo, etc.) is not specified.
77                clickable: false,
78
79                // url: String
80                //              A URL of an html fragment page or JSON data that represents a
81                //              new view content. The view content is loaded with XHR and
82                //              inserted in the current page. Then a view transition occurs to
83                //              the newly created view. The view is cached so that subsequent
84                //              requests would not load the content again.
85                url: "",
86
87                // urlTarget: String
88                //              Node id under which a new view will be created according to the
89                //              url parameter. If not specified, The new view will be created as
90                //              a sibling of the current view.
91                urlTarget: "",
92
93                // back: Boolean
94                //              If true, history.back() is called when clicked.
95                back: false,
96
97                // transition: String
98                //              A type of animated transition effect. You can choose from the
99                //              standard transition types, "slide", "fade", "flip", or from the
100                //              extended transition types, "cover", "coverv", "dissolve",
101                //              "reveal", "revealv", "scaleIn", "scaleOut", "slidev",
102                //              "swirl", "zoomIn", "zoomOut", "cube", and "swap". If "none" is
103                //              specified, transition occurs immediately without animation.
104                transition: "",
105
106                // transitionDir: Number
107                //              The transition direction. If 1, transition forward. If -1,
108                //              transition backward. For example, the slide transition slides
109                //              the view from right to left when dir == 1, and from left to
110                //              right when dir == -1.
111                transitionDir: 1,
112
113                // transitionOptions: Object
114                //              A hash object that holds transition options.
115                transitionOptions: null,
116
117                // callback: Function|String
118                //              A callback function that is called when the transition has been
119                //              finished. A function reference, or name of a function in
120                //              context.
121                callback: null,
122
123                // label: String
124                //              A label of the item. If the label is not specified, innerHTML is
125                //              used as a label.
126                label: "",
127
128                // toggle: Boolean
129                //              If true, the item acts like a toggle button.
130                toggle: false,
131
132                // selected: Boolean
133                //              If true, the item is highlighted to indicate it is selected.
134                selected: false,
135
136                // tabIndex: String
137                //              Tabindex setting for the item so users can hit the tab key to
138                //              focus on it.
139                tabIndex: "0",
140               
141                // _setTabIndexAttr: [private] String
142                //              Sets tabIndex to domNode.
143                _setTabIndexAttr: "",
144
145                /* internal properties */       
146
147                // paramsToInherit: String
148                //              Comma separated parameters to inherit from the parent.
149                paramsToInherit: "transition,icon",
150
151                // _selStartMethod: String
152                //              Specifies how the item enters the selected state.
153                //
154                //              - "touch": Use touch events to enter the selected state.
155                //              - "none": Do not change the selected state.
156                _selStartMethod: "none", // touch or none
157
158                // _selEndMethod: String
159                //              Specifies how the item leaves the selected state.
160                //
161                //              - "touch": Use touch events to leave the selected state.
162                //              - "timer": Use setTimeout to leave the selected state.
163                //              - "none": Do not change the selected state.
164                _selEndMethod: "none", // touch, timer, or none
165
166                // _delayedSelection: Boolean
167                //              If true, selection is delayed 100ms and canceled if dragged in
168                //              order to avoid selection when flick operation is performed.
169                _delayedSelection: false,
170
171                // _duration: Number
172                //              Duration of selection, milliseconds.
173                _duration: 800,
174
175                // _handleClick: Boolean
176                //              If true, this widget listens to touch events.
177                _handleClick: true,
178
179                buildRendering: function(){
180                        this.inherited(arguments);
181                        this._isOnLine = this.inheritParams();
182                },
183
184                startup: function(){
185                        if(this._started){ return; }
186                        if(!this._isOnLine){
187                                this.inheritParams();
188                        }
189                        this._updateHandles();
190                        this.inherited(arguments);
191                },
192
193                inheritParams: function(){
194                        // summary:
195                        //              Copies from the parent the values of parameters specified
196                        //              by the property paramsToInherit.
197                        var parent = this.getParent();
198                        if(parent){
199                                array.forEach(this.paramsToInherit.split(/,/), function(p){
200                                        if(p.match(/icon/i)){
201                                                var base = p + "Base", pos = p + "Pos";
202                                                if(this[p] && parent[base] &&
203                                                        parent[base].charAt(parent[base].length - 1) === '/'){
204                                                        this[p] = parent[base] + this[p];
205                                                }
206                                                if(!this[p]){ this[p] = parent[base]; }
207                                                if(!this[pos]){ this[pos] = parent[pos]; }
208                                        }
209                                        if(!this[p]){ this[p] = parent[p]; }
210                                }, this);
211                        }
212                        return !!parent;
213                },
214
215                _updateHandles: function(){
216                        // tags:
217                        //              private
218                        if(this._handleClick && this._selStartMethod === "touch"){
219                                if(!this._onTouchStartHandle){
220                                        this._onTouchStartHandle = this.connect(this.domNode, touch.press, "_onTouchStart");
221                                }
222                        }else{
223                                if(this._onTouchStartHandle){
224                                        this.disconnect(this._onTouchStartHandle);
225                                        this._onTouchStartHandle = null;
226                                }
227                        }
228                },
229               
230                getTransOpts: function(){
231                        // summary:
232                        //              Copies from the parent and returns the values of parameters 
233                        //              specified by the property paramsToInherit.
234                        var opts = this.transitionOptions || {};
235                        array.forEach(["moveTo", "href", "hrefTarget", "url", "target",
236                                "urlTarget", "scene", "transition", "transitionDir"], function(p){
237                                opts[p] = opts[p] || this[p];
238                        }, this);
239                        return opts; // Object
240                },
241
242                userClickAction: function(/*Event*/ /*===== e =====*/){
243                        // summary:
244                        //              User-defined click action.
245                },
246
247                defaultClickAction: function(/*Event*/e){
248                        // summary:
249                        //              The default action of this item.
250                        this.handleSelection(e);
251                        if(this.userClickAction(e) === false){ return; } // user's click action
252                        this.makeTransition(e);
253                },
254
255                handleSelection: function(/*Event*/e){
256                        // summary:
257                        //              Handles this items selection state.
258
259                        // Before transitioning, we want the visual effect of selecting the item.
260                        // To ensure this effect happens even if _delayedSelection is true:
261                        if(this._delayedSelection){
262                                this.set("selected", true);
263                        } // the item will be deselected after transition.
264
265                        if(this._onTouchEndHandle){
266                                this.disconnect(this._onTouchEndHandle);
267                                this._onTouchEndHandle = null;
268                        }
269
270                        var p = this.getParent();
271                        if(this.toggle){
272                                this.set("selected", !this._currentSel);
273                        }else if(p && p.selectOne){
274                                this.set("selected", true);
275                        }else{
276                                if(this._selEndMethod === "touch"){
277                                        this.set("selected", false);
278                                }else if(this._selEndMethod === "timer"){
279                                        this.defer(function(){
280                                                this.set("selected", false);
281                                        }, this._duration);
282                                }
283                        }
284                },
285
286                makeTransition: function(/*Event*/e){
287                        // summary:
288                        //              Makes a transition.
289                        if(this.back && history){
290                                history.back();
291                                return;
292                        }       
293                        if (this.href && this.hrefTarget && this.hrefTarget != "_self") {
294                                win.global.open(this.href, this.hrefTarget || "_blank");
295                                this._onNewWindowOpened(e);
296                                return;
297                        }
298                        var opts = this.getTransOpts();
299                        var doTransition =
300                                !!(opts.moveTo || opts.href || opts.url || opts.target || opts.scene);
301                        if(this._prepareForTransition(e, doTransition ? opts : null) === false){ return; }
302                        if(doTransition){
303                                this.setTransitionPos(e);
304                                new TransitionEvent(this.domNode, opts, e).dispatch();
305                        }
306                },
307
308                _onNewWindowOpened: function(/*Event*/ /*===== e =====*/){
309                        // summary:
310                        //              Subclasses may want to implement it.
311                },
312
313                _prepareForTransition: function(/*Event*/e, /*Object*/transOpts){
314                        // summary:
315                        //              Subclasses may want to implement it.
316                },
317
318                _onTouchStart: function(e){
319                        // tags:
320                        //              private
321                        if(this.getParent().isEditing || this.onTouchStart(e) === false){ return; } // user's touchStart action
322                        if(!this._onTouchEndHandle && this._selStartMethod === "touch"){
323                                // Connect to the entire window. Otherwise, fail to receive
324                                // events if operation is performed outside this widget.
325                                // Expose both connect handlers in case the user has interest.
326                                this._onTouchMoveHandle = this.connect(win.body(), touch.move, "_onTouchMove");
327                                this._onTouchEndHandle = this.connect(win.body(), touch.release, "_onTouchEnd");
328                        }
329                        this.touchStartX = e.touches ? e.touches[0].pageX : e.clientX;
330                        this.touchStartY = e.touches ? e.touches[0].pageY : e.clientY;
331                        this._currentSel = this.selected;
332
333                        if(this._delayedSelection){
334                                // so as not to make selection when the user flicks on ScrollableView
335                                this._selTimer = this.defer(function(){
336                                        this.set("selected", true);
337                                }, 100);
338                        }else{
339                                this.set("selected", true);
340                        }
341                },
342
343                onTouchStart: function(/*Event*/ /*===== e =====*/){
344                        // summary:
345                        //              User-defined function to handle touchStart events.
346                        // tags:
347                        //              callback
348                },
349
350                _onTouchMove: function(e){
351                        // tags:
352                        //              private
353                        var x = e.touches ? e.touches[0].pageX : e.clientX;
354                        var y = e.touches ? e.touches[0].pageY : e.clientY;
355                        if(Math.abs(x - this.touchStartX) >= 4 ||
356                           Math.abs(y - this.touchStartY) >= 4){ // dojox/mobile/scrollable.threshold
357                                this.cancel();
358                                var p = this.getParent();
359                                if(p && p.selectOne){
360                                        this._prevSel && this._prevSel.set("selected", true);
361                                }else{
362                                        this.set("selected", false);
363                                }
364                        }
365                },
366
367                _disconnect: function(){
368                        // tags:
369                        //              private
370                        this.disconnect(this._onTouchMoveHandle);
371                        this.disconnect(this._onTouchEndHandle);
372                        this._onTouchMoveHandle = this._onTouchEndHandle = null;
373                },
374
375                cancel: function(){
376                        // summary:
377                        //              Cancels an ongoing selection (if any).
378                        if(this._selTimer){
379                                this._selTimer.remove();
380                                this._selTimer = null;
381                        }
382                        this._disconnect();
383                },
384
385                _onTouchEnd: function(e){
386                        // tags:
387                        //              private
388                        if(!this._selTimer && this._delayedSelection){ return; }
389                        this.cancel();
390                        this._onClick(e);
391                },
392
393                setTransitionPos: function(e){
394                        // summary:
395                        //              Stores the clicked position for later use.
396                        // description:
397                        //              Some of the transition animations (e.g. ScaleIn) need the
398                        //              clicked position.
399                        var w = this;
400                        while(true){
401                                w = w.getParent();
402                                if(!w || domClass.contains(w.domNode, "mblView")){ break; }
403                        }
404                        if(w){
405                                w.clickedPosX = e.clientX;
406                                w.clickedPosY = e.clientY;
407                        }
408                },
409
410                transitionTo: function(/*String|Object*/moveTo, /*String*/href, /*String*/url, /*String*/scene){
411                        // summary:
412                        //              Performs a view transition.
413                        // description:
414                        //              Given a transition destination, this method performs a view
415                        //              transition. This method is typically called when this item
416                        //              is clicked.
417                        var opts = (moveTo && typeof(moveTo) === "object") ? moveTo :
418                                {moveTo: moveTo, href: href, url: url, scene: scene,
419                                 transition: this.transition, transitionDir: this.transitionDir};
420                        new TransitionEvent(this.domNode, opts).dispatch();
421                },
422
423                _setIconAttr: function(icon){
424                        // tags:
425                        //              private
426                        if(!this._isOnLine){
427                                // record the value to be able to reapply it (see the code in the startup method)
428                                this._pendingIcon = icon; 
429                                return;
430                        } // icon may be invalid because inheritParams is not called yet
431                        this._set("icon", icon);
432                        this.iconNode = iconUtils.setIcon(icon, this.iconPos, this.iconNode, this.alt, this.iconParentNode, this.refNode, this.position);
433                },
434
435                _setLabelAttr: function(/*String*/text){
436                        // tags:
437                        //              private
438                        this._set("label", text);
439                        this.labelNode.innerHTML = this._cv ? this._cv(text) : text;
440                },
441
442                _setSelectedAttr: function(/*Boolean*/selected){
443                        // summary:
444                        //              Makes this widget in the selected or unselected state.
445                        // description:
446                        //              Subclass should override.
447                        // tags:
448                        //              private
449                        if(selected){
450                                var p = this.getParent();
451                                if(p && p.selectOne){
452                                        // deselect the currently selected item
453                                        var arr = array.filter(p.getChildren(), function(w){
454                                                return w.selected;
455                                        });
456                                        array.forEach(arr, function(c){
457                                                this._prevSel = c;
458                                                c.set("selected", false);
459                                        }, this);
460                                }
461                        }
462                        this._set("selected", selected);
463                }
464        });
465        return has("dojo-bidi") ? declare("dojox.mobile._ItemBase", [_ItemBase, BidiItemBase]) : _ItemBase;
466});
Note: See TracBrowser for help on using the repository browser.