source: Dev/trunk/src/client/dojox/mobile/_EditableIconMixin.js @ 532

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

Added Dojo 1.9.3 release.

File size: 11.1 KB
Line 
1define([
2        "dojo/_base/array",
3        "dojo/_base/connect",
4        "dojo/_base/declare",
5        "dojo/_base/event",
6        "dojo/_base/lang",
7        "dojo/_base/window",
8        "dojo/dom-geometry",
9        "dojo/dom-style",
10        "dojo/touch",
11        "dijit/registry",
12        "./IconItem",
13        "./sniff",
14        "./viewRegistry",
15        "./_css3"
16], function(array, connect, declare, event, lang, win, domGeometry, domStyle, touch, registry, IconItem, has, viewRegistry, css3){
17
18        // module:
19        //              dojox/mobile/_EditableIconMixin
20
21        return declare("dojox.mobile._EditableIconMixin", null, {
22                // summary:
23                //              A mixin for IconContainer to make it editable.
24
25                deleteIconForEdit: "mblDomButtonBlackCircleCross",
26                threshold: 4, // drag threshold value in pixels
27
28                destroy: function(){
29                        // summary:
30                        //              Destroys the container.
31                        if(this._blankItem){
32                                this._blankItem.destroy();
33                        }
34                        this.inherited(arguments);
35                },
36
37                startEdit: function(){
38                        // summary:
39                        //              Starts the editing.
40                        if(!this.editable || this.isEditing){ return; }
41
42                        this.isEditing = true;
43                        if(!this._handles){
44                                this._handles = [
45                                        this.connect(this.domNode, css3.name("transitionStart"), "_onTransitionStart"),
46                                        this.connect(this.domNode, css3.name("transitionEnd"), "_onTransitionEnd")
47                                ];
48                        }
49
50                        var count = 0;
51                        array.forEach(this.getChildren(), function(w){
52                                this.defer(function(){
53                                        w.set("deleteIcon", this.deleteIconForEdit);
54                                        if(w.deleteIconNode){
55                                                w._deleteHandle = this.connect(w.deleteIconNode, "onclick", "_deleteIconClicked");
56                                        }
57                                        w.highlight(0);
58                                }, 15*count++);
59                        }, this);
60
61                        connect.publish("/dojox/mobile/startEdit", [this]); // pubsub
62                        this.onStartEdit(); // callback
63                },
64
65                endEdit: function(){
66                        // summary:
67                        //              Ends the editing.
68                        if(!this.isEditing){ return; }
69
70                        array.forEach(this.getChildren(), function(w){
71                                w.unhighlight();
72                                if(w._deleteHandle){
73                                        this.disconnect(w._deleteHandle);
74                                        w._deleteHandle = null;
75                                }
76                                w.set("deleteIcon", "");
77                        }, this);
78
79                        this._movingItem = null;
80                        if(this._handles){
81                                array.forEach(this._handles, this.disconnect, this);
82                                this._handles = null;
83                        }
84
85                        connect.publish("/dojox/mobile/endEdit", [this]); // pubsub
86                        this.onEndEdit(); // callback
87                        this.isEditing = false;
88                },
89
90                scaleItem: function(/*Widget*/widget, /*Number*/ratio){
91                        // summary:
92                        //              Scales an item according to the specified ratio.
93                        domStyle.set(widget.domNode, css3.add({}, {
94                                transition: has("android") ? "" : css3.name("transform", true) + " .1s ease-in-out",
95                                transform: ratio == 1 ? "" : "scale(" + ratio + ")"
96                        }));                   
97                },
98
99                _onTransitionStart: function(e){
100                        // tags:
101                        //              private
102                        event.stop(e);
103                },
104
105                _onTransitionEnd: function(e){
106                        // tags:
107                        //              private
108                        event.stop(e);
109                        var w = registry.getEnclosingWidget(e.target);
110                        w._moving = false;
111                        domStyle.set(w.domNode, css3.name("transition"), "");
112                },
113
114                _onTouchStart: function(e){
115                        // tags:
116                        //              private
117                        if(!this._blankItem){
118                                this._blankItem = new IconItem();
119                                this._blankItem.domNode.style.visibility = "hidden";
120                                this._blankItem._onClick = function(){};
121                        }
122                        var item = this._movingItem = registry.getEnclosingWidget(e.target);
123                        var iconPressed = false;
124                        var n;
125                        for(n = e.target; n !== item.domNode; n = n.parentNode){
126                                if(n === item.iconNode){
127                                        iconPressed = true;
128                                        break;
129                                }
130                        }
131                        if(!iconPressed){ return; }
132
133                        if(!this._conn){
134                                this._conn = [
135                                        this.connect(this.domNode, touch.move, "_onTouchMove"),
136                                        this.connect(win.doc, touch.release, "_onTouchEnd")
137                                ];
138                        }
139                        this._touchStartPosX = e.touches ? e.touches[0].pageX : e.pageX;
140                        this._touchStartPosY = e.touches ? e.touches[0].pageY : e.pageY;
141                        if(this.isEditing){
142                                this._onDragStart(e);
143                        }else{
144                                // set timer to detect long press
145                                this._pressTimer = this.defer(function(){
146                                        this.startEdit();
147                                        this._onDragStart(e);
148                                }, 1000);
149                        }
150                },
151
152                _onDragStart: function(e){
153                        // tags:
154                        //              private
155                        this._dragging = true;
156
157                        var movingItem = this._movingItem;
158                        if(movingItem.get("selected")){
159                                movingItem.set("selected", false);
160                        }
161                        this.scaleItem(movingItem, 1.1);
162
163                        var x = e.touches ? e.touches[0].pageX : e.pageX;
164                        var y = e.touches ? e.touches[0].pageY : e.pageY;
165                       
166                        var enclosingScrollable = viewRegistry.getEnclosingScrollable(movingItem.domNode);
167                        var dx = 0;
168                        var dy = 0;
169                        if(enclosingScrollable){ // this node is placed inside a scrollable
170                                var pos = enclosingScrollable.getPos();
171                                dx = pos.x;
172                                dy = pos.y;
173                                event.stop(e);
174                        }
175                       
176                        var startPos = this._startPos = domGeometry.position(movingItem.domNode, true);
177                        this._offsetPos = {
178                                x: startPos.x - x - dx,
179                                y: startPos.y - y - dy
180                        };
181
182                        this._startIndex = this.getIndexOfChild(movingItem);
183                        this.addChild(this._blankItem, this._startIndex);
184                        this.moveChild(movingItem, this.getChildren().length);
185                        domStyle.set(movingItem.domNode, {
186                                position: "absolute",
187                                top: (startPos.y - dy) + "px",
188                                left: (startPos.x - dx) + "px",
189                                zIndex: 100
190                        });
191                },
192
193                _onTouchMove: function(e){
194                        // tags:
195                        //              private
196                        var x = e.touches ? e.touches[0].pageX : e.pageX;
197                        var y = e.touches ? e.touches[0].pageY : e.pageY;
198                        if(this._dragging){
199                                domStyle.set(this._movingItem.domNode, {
200                                        top: (this._offsetPos.y + y) + "px",
201                                        left: (this._offsetPos.x + x) + "px"
202                                });
203                                this._detectOverlap({x: x, y: y});
204                                event.stop(e);
205                        }else{
206                                var dx = Math.abs(this._touchStartPosX - x);
207                                var dy = Math.abs(this._touchStartPosY - y);
208                                if (dx > this.threshold || dy > this.threshold) {
209                                        this._clearPressTimer();                                       
210                                }
211                        }
212                },
213
214                _onTouchEnd: function(e){
215                        // tags:
216                        //              private
217                        this._clearPressTimer();
218                        if(this._conn){
219                                array.forEach(this._conn, this.disconnect, this);
220                                this._conn = null;                             
221                        }
222
223                        if(this._dragging){
224                                this._dragging = false;
225
226                                var movingItem = this._movingItem;
227                                this.scaleItem(movingItem, 1.0);
228                                domStyle.set(movingItem.domNode, {
229                                        position: "",
230                                        top: "",
231                                        left: "",
232                                        zIndex: ""
233                                });
234                                var startIndex = this._startIndex;
235                                var endIndex = this.getIndexOfChild(this._blankItem);
236                                this.moveChild(movingItem, endIndex);
237                                this.removeChild(this._blankItem);
238                                connect.publish("/dojox/mobile/moveIconItem", [this, movingItem, startIndex, endIndex]); // pubsub
239                                this.onMoveItem(movingItem, startIndex, endIndex); // callback
240                        }
241                },
242
243                _clearPressTimer: function(){
244                        // tags:
245                        //              private
246                        if(this._pressTimer){
247                                this._pressTimer.remove();
248                                this._pressTimer = null;
249                        }
250                },
251
252                _detectOverlap: function(/*Object*/point){
253                        // tags:
254                        //              private
255                        var children = this.getChildren(),
256                                blankItem = this._blankItem,
257                                blankPos = domGeometry.position(blankItem.domNode, true),
258                                blankIndex = this.getIndexOfChild(blankItem),
259                                dir = 1,
260                                i, w, pos;
261                        if(this._contains(point, blankPos)){
262                                return;
263                        }else if(point.y < blankPos.y || (point.y <= blankPos.y + blankPos.h && point.x < blankPos.x)){
264                                dir = -1;
265                        }
266                        for(i = blankIndex + dir; i>=0 && i<children.length-1; i += dir){
267                                w = children[i];
268                                if(w._moving){ continue; }
269                                pos = domGeometry.position(w.domNode, true);
270                                if(this._contains(point, pos)){
271                                        this.defer(function(){
272                                                this.moveChildWithAnimation(blankItem, dir == 1 ? i+1 : i);
273                                        });
274                                        break;
275                                }else if((dir == 1 && pos.y > point.y) || (dir == -1 && pos.y + pos.h < point.y)){
276                                        break;
277                                }
278                        }
279                },
280
281                _contains: function(point, pos){
282                        // tags:
283                        //              private
284                        return pos.x < point.x && point.x < pos.x + pos.w && pos.y < point.y && point.y < pos.y + pos.h;
285                },
286
287                _animate: function(/*int*/from, /*int*/to){
288                        // tags:
289                        //              private
290                        if(from == to) { return; }
291                        var dir = from < to ? 1 : -1;
292                        var children = this.getChildren();
293                        var posArray = [];
294                        var i;
295                        for(i=from; i!=to; i+=dir){
296                                posArray.push({
297                                        t: (children[i+dir].domNode.offsetTop - children[i].domNode.offsetTop) + "px",
298                                        l: (children[i+dir].domNode.offsetLeft - children[i].domNode.offsetLeft) + "px"
299                                });
300                        }
301                        for(i=from, j=0; i!=to; i+=dir, j++){
302                                var w = children[i];
303                                w._moving = true;
304                                domStyle.set(w.domNode, {
305                                        top: posArray[j].t,
306                                        left: posArray[j].l
307                                });
308                                this.defer(lang.hitch(w, function(){
309                                        domStyle.set(this.domNode, css3.add({
310                                                top: "0px",
311                                                left: "0px"
312                                        }, {
313                                                transition: "top .3s ease-in-out, left .3s ease-in-out"
314                                        }));
315                                }), j*10);
316                        }
317                },
318
319                removeChildWithAnimation: function(/*Widget|Number*/widget){
320                        // summary:
321                        //              Removes the given child with animation.
322                        var index = (typeof widget === "number") ? widget : this.getIndexOfChild(widget);
323                        this.removeChild(widget);
324
325                        // Show remove animation
326                        if(this._blankItem){
327                                // #16868 - no _blankItem if calling deleteItem() programmatically, that is
328                                // without _onTouchStart() being called.
329                                this.addChild(this._blankItem);
330                        }
331                        this._animate(index, this.getChildren().length - 1);
332                        if(this._blankItem){
333                                this.removeChild(this._blankItem);
334                        }
335                },
336
337                moveChild: function(/*Widget|Number*/widget, /*Number?*/insertIndex){
338                        // summary:
339                        //              Moves a child without animation.
340                        this.addChild(widget, insertIndex);
341                        this.paneContainerWidget.addChild(widget.paneWidget, insertIndex);
342                },
343
344                moveChildWithAnimation: function(/*Widget|Number*/widget, /*Number?*/insertIndex){
345                        // summary:
346                        //              Moves a child with animation.   
347                        var index = this.getIndexOfChild(this._blankItem);
348                        this.moveChild(widget, insertIndex);
349
350                        // Show move animation
351                        this._animate(index, insertIndex);
352                },
353
354                _deleteIconClicked: function(e){
355                        // summary:
356                        //              Internal handler for click events.
357                        // tags:
358                        //              private
359                        if(this.deleteIconClicked(e) === false){ return; } // user's click action
360                        var item = registry.getEnclosingWidget(e.target);
361                        this.deleteItem(item);
362                },
363
364                deleteIconClicked: function(/*Event*/ /*===== e =====*/){
365                        // summary:
366                        //              User-defined function to handle clicks for the delete icon.
367                        // tags:
368                        //              callback
369                },
370
371                deleteItem: function(/*Widget*/item){
372                        // summary:
373                        //              Deletes the given item.
374                        if(item._deleteHandle){
375                                this.disconnect(item._deleteHandle);
376                        }
377                        this.removeChildWithAnimation(item);
378
379                        connect.publish("/dojox/mobile/deleteIconItem", [this, item]); // pubsub
380                        this.onDeleteItem(item); // callback
381
382                        item.destroy();
383                },
384
385                onDeleteItem: function(/*Widget*/item){
386                        // summary:
387                        //              Stub function to connect to from your application.
388                },
389
390                onMoveItem: function(/*Widget*/item, /*int*/from, /*int*/to){
391                        // summary:
392                        //              Stub function to connect to from your application.
393                },
394
395                onStartEdit: function(){
396                        // summary:
397                        //              Stub function to connect to from your application.
398                },
399
400                onEndEdit: function(){
401                        // summary:
402                        //              Stub function to connect to from your application.
403                },
404
405                _setEditableAttr: function(/*Boolean*/editable){
406                        // tags:
407                        //              private
408                        this._set("editable", editable);
409                        if(editable && !this._touchStartHandle){ // Allow users to start editing by long press on IconItems
410                                this._touchStartHandle = this.connect(this.domNode, touch.press, "_onTouchStart");
411                        }else if(!editable && this._touchStartHandle){
412                                this.disconnect(this._touchStartHandle);
413                                this._touchStartHandle = null;
414                        }
415                }
416        });
417});
Note: See TracBrowser for help on using the repository browser.