source: Dev/branches/rest-dojo-ui/client/dojox/layout/dnd/PlottedDnd.js @ 256

Last change on this file since 256 was 256, checked in by hendrikvanantwerpen, 13 years ago

Reworked project structure based on REST interaction and Dojo library. As
soon as this is stable, the old jQueryUI branch can be removed (it's
kept for reference).

File size: 14.5 KB
Line 
1dojo.provide("dojox.layout.dnd.PlottedDnd");
2
3dojo.require("dojo.dnd.Source");
4dojo.require("dojo.dnd.Manager");
5dojo.require("dojox.layout.dnd.Avatar");
6
7dojo.declare("dojox.layout.dnd.PlottedDnd", [dojo.dnd.Source], {
8        // summary:
9        //              dnd source handling plotted zone to show the dropping area
10        GC_OFFSET_X: dojo.dnd.manager().OFFSET_X,
11        GC_OFFSET_Y: dojo.dnd.manager().OFFSET_Y,
12       
13        constructor: function(/*Node*/node, /*Object*/params){
14               
15                this.childBoxes                 = null;
16                this.dropIndicator              = new dojox.layout.dnd.DropIndicator("dndDropIndicator", "div");
17                this.withHandles                = params.withHandles;
18                this.handleClasses              = params.handleClasses;
19                this.opacity                    = params.opacity;
20                this.allowAutoScroll    = params.allowAutoScroll;//MODIF MYS
21                this.dom                = params.dom;
22                this.singular                   = true;
23                this.skipForm                   = true;
24                this._over                              = false;
25                this.defaultHandleClass = "GcDndHandle";
26                this.isDropped                  = false;
27                this._timer                             = null;
28                //Initialize the params to calculate offset
29                this.isOffset = (params.isOffset)?true:false;
30                this.offsetDrag = (params.offsetDrag) ? params.offsetDrag : {x:0,y:0};
31                this.hideSource = params.hideSource ? params.hideSource : true;
32                this._drop = this.dropIndicator.create();
33               
34        },
35       
36        _calculateCoords : function(/*Boolean*/height){
37                // summary: Calculate each position of children
38                dojo.forEach(this.node.childNodes, function(child){
39                        var c = dojo.coords(child, true);
40                        child.coords = {
41                                xy: c,
42                                w: child.offsetWidth / 2,
43                                h: child.offsetHeight / 2,
44                                mw: c.w
45                        };
46                        if(height){
47                                child.coords.mh = c.h;
48                        }
49                }, this);
50        },
51       
52        _legalMouseDown: function(/*Event*/e){
53                // summary: Checks if user clicked on "approved" items.
54                if(!this.withHandles){ return true; }
55                for(var node = (e.target); node && node != this.node; node = node.parentNode){
56                        if(dojo.hasClass(node, this.defaultHandleClass)){
57                                return true;
58                        }
59                }
60                return false;   // Boolean
61        },
62       
63        setDndItemSelectable: function(/*Node*/node, /*Boolean*/isSelectable) {
64                // summary: set an item as selectable
65                for(var _node = node; _node && node != this.node; _node = _node.parentNode) {
66                        if (dojo.hasClass(_node,"dojoDndItem")) {
67                                dojo.setSelectable(_node, isSelectable);
68                                return;
69                        }
70                }
71        },
72       
73        getDraggedWidget: function(/*Node*/node) {
74                // summary: Return one or more widget selected during the drag.
75                var _node = node;
76                while (_node && _node.nodeName.toLowerCase()!="body" && !dojo.hasClass(_node,"dojoDndItem")) {
77                        _node = _node.parentNode;
78                }
79                return (_node) ? dijit.byNode(_node) : null;
80        },
81       
82        isAccepted: function(/*Node*/ node) {
83                // summary: test if this node can be accepted
84                var _dndType = (node) ? node.getAttribute("dndtype") : null;
85                return (_dndType && _dndType in this.accept);
86        },
87       
88        onDndStart:function(/*Object*/source, /*Array*/nodes, /*Object*/copy){
89                // summary: Called to initiate the DnD operation.
90
91                this.firstIndicator = (source == this);
92                this._calculateCoords(true);
93                //this.isDropped = true;
94                var m = dojo.dnd.manager();
95                if(nodes[0].coords){
96                        this._drop.style.height = nodes[0].coords.mh + "px";
97                        dojo.style(m.avatar.node, "width", nodes[0].coords.mw + "px");
98                }else{
99                        this._drop.style.height = m.avatar.node.clientHeight+"px";
100                }
101                this.dndNodes = nodes;
102                dojox.layout.dnd.PlottedDnd.superclass.onDndStart.call(this,source, nodes, copy);
103                if(source == this && this.hideSource){
104                        dojo.forEach(nodes, function(n){
105                                dojo.style(n, "display","none");
106                        });
107                }
108                       
109        },
110
111        onDndCancel:function(){
112                // summary: Called to cancel the DnD operation.
113                var m = dojo.dnd.manager();
114                if(m.source == this && this.hideSource){
115                        var nodes = this.getSelectedNodes();
116                        dojo.forEach(nodes, function(n){
117                                dojo.style(n, "display","");
118                        });
119                }
120                dojox.layout.dnd.PlottedDnd.superclass.onDndCancel.call(this);
121                this.deleteDashedZone();
122        },
123       
124        onDndDrop: function(source,nodes,copy,target) {
125                // summary: Called to finish the DnD operation
126                try{
127                        if(!this.isAccepted(nodes[0])){
128                                this.onDndCancel();
129                        }else{
130                                if(source == this && this._over && this.dropObject){
131                                        this.current = this.dropObject.c;
132                                }
133                                dojox.layout.dnd.PlottedDnd.superclass.onDndDrop.call(this, source, nodes, copy, target);
134                                this._calculateCoords(true);
135                        }
136                }catch(e){
137                        console.warn(e);
138                }
139        },
140                       
141        onMouseDown: function(/*Event*/e) {
142                // summary: Event processor for onmousedown.
143                if(this.current == null){
144                        this.selection = {};
145                }else{
146                        if(this.current == this.anchor){
147                                this.anchor = null;
148                        }
149                }
150                if(this.current !== null){
151                        var c = dojo.coords(this.current, true);
152                        this.current.coords = {
153                                xy: c,
154                                w: this.current.offsetWidth / 2,
155                                h: this.current.offsetHeight / 2,
156                                mh: c.h,
157                                mw: c.w
158                        };
159                        this._drop.style.height = this.current.coords.mh + "px";
160                       
161                        if(this.isOffset){
162                                if(this.offsetDrag.x == 0 && this.offsetDrag.y == 0){
163                                        var NoOffsetDrag = true;
164                                        var coords = dojo.coords(this._getChildByEvent(e));
165                                        this.offsetDrag.x = coords.x - e.pageX;
166                                        this.offsetDrag.y = coords.y - e.clientY;
167                                }
168                                if(this.offsetDrag.y < 16 && this.current != null){
169                                        this.offsetDrag.y = this.GC_OFFSET_Y;
170                                }
171
172                                var m = dojo.dnd.manager();
173                                m.OFFSET_X = this.offsetDrag.x;
174                                m.OFFSET_Y = this.offsetDrag.y;
175                                if (NoOffsetDrag) {
176                                        this.offsetDrag.x = 0;
177                                        this.offsetDrag.y = 0;
178                                }
179                        }
180                }
181               
182                if(dojo.dnd.isFormElement(e)){
183                        this.setDndItemSelectable(e.target,true);
184                }else{
185                        this.containerSource = true;
186                        var _draggedWidget = this.getDraggedWidget(e.target);
187                        if(_draggedWidget && _draggedWidget.dragRestriction){
188                        // FIXME: not clear what this was supposed to mean ... this code needs more cleanups.
189                        //      dragRestriction = true;
190                        }else{
191                                dojox.layout.dnd.PlottedDnd.superclass.onMouseDown.call(this,e);
192                        }
193                }
194        },
195
196        onMouseUp: function(/*Event*/e) {
197                // summary: Event processor for onmouseup.
198                dojox.layout.dnd.PlottedDnd.superclass.onMouseUp.call(this,e);
199                this.containerSource = false;
200                if (!dojo.isIE && this.mouseDown){
201                        this.setDndItemSelectable(e.target,true);
202                }
203                var m = dojo.dnd.manager();
204                m.OFFSET_X = this.GC_OFFSET_X;
205                m.OFFSET_Y = this.GC_OFFSET_Y;
206        },
207       
208        onMouseMove: function(e) {
209                // summary: Event processor for onmousemove
210                var m = dojo.dnd.manager();
211                if(this.isDragging) {
212                        var before = false;
213                        if(this.current != null || (this.current == null && !this.dropObject)){
214                                if(this.isAccepted(m.nodes[0]) || this.containerSource){
215                                        before = this.setIndicatorPosition(e);
216                                }
217                        }
218                        if(this.current != this.targetAnchor || before != this.before){
219                                this._markTargetAnchor(before);
220                                m.canDrop(!this.current || m.source != this || !(this.current.id in this.selection));
221                        }
222
223                        if(this.allowAutoScroll){
224                                this._checkAutoScroll(e);
225                        }
226                }else{
227                        if(this.mouseDown && this.isSource){
228                                var nodes = this.getSelectedNodes();
229                                if(nodes.length){
230                                        m.startDrag(this, nodes, this.copyState(dojo.isCopyKey(e)));
231                                }
232                        }
233
234                        if(this.allowAutoScroll){
235                                this._stopAutoScroll();
236                        }
237                }
238        },
239       
240        _markTargetAnchor: function(/*Boolean*/before){
241                // summary: Assigns a class to the current target anchor based on "before" status
242                if(this.current == this.targetAnchor && this.before == before){ return; }
243                this.targetAnchor = this.current;
244                this.targetBox = null;
245                this.before = before;
246        },
247       
248        _unmarkTargetAnchor: function(){
249                // summary: Removes a class of the current target anchor based on "before" status.
250                if(!this.targetAnchor){ return; }
251                this.targetAnchor = null;
252                this.targetBox = null;
253                this.before = true;
254        },
255       
256        setIndicatorPosition: function(/*Event*/e) {
257                // summary: set the position of the drop indicator
258                var before = false;
259                if(this.current){
260                        if (!this.current.coords || this.allowAutoScroll) {
261                                this.current.coords = {
262                                        xy: dojo.coords(this.current, true),
263                                        w: this.current.offsetWidth / 2,
264                                        h: this.current.offsetHeight / 2
265                                };
266                        }
267                        before = this.horizontal ?
268                                (e.pageX - this.current.coords.xy.x) < this.current.coords.w :
269                                (e.pageY - this.current.coords.xy.y) < this.current.coords.h
270                        this.insertDashedZone(before);
271                }else{
272                        if(!this.dropObject /*|| dojo.isIE*/){ this.insertDashedZone(false); }
273                }
274                return before;
275        },
276       
277
278        onOverEvent:function(){
279                this._over = true;
280                dojox.layout.dnd.PlottedDnd.superclass.onOverEvent.call(this);
281                if (this.isDragging) {
282                        var m = dojo.dnd.manager();
283                        if (!this.current && !this.dropObject && this.getSelectedNodes()[0] && this.isAccepted(m.nodes[0]))
284                                this.insertDashedZone(false);
285                }
286        },
287       
288        onOutEvent: function() {
289                this._over = false;
290                this.containerSource = false;
291                dojox.layout.dnd.PlottedDnd.superclass.onOutEvent.call(this);
292                if (this.dropObject) this.deleteDashedZone();
293        },
294       
295        deleteDashedZone: function() {
296                // summary: hide the dashed zone
297                this._drop.style.display = "none";
298                        var next = this._drop.nextSibling;
299                        while (next != null) {
300                                next.coords.xy.y -= parseInt(this._drop.style.height);
301                                next = next.nextSibling;
302                        }
303                delete this.dropObject;
304        },
305       
306        insertDashedZone: function(/*Boolean*/before) {
307                // summary: Insert the dashed zone at the right place
308                if(this.dropObject){
309                        if( before == this.dropObject.b &&
310                                ((this.current && this.dropObject.c == this.current.id) ||
311                                (!this.current && !this.dropObject.c))
312                        ){
313                                return;
314                        }else{
315                                this.deleteDashedZone();
316                        }
317                }
318                this.dropObject = { n: this._drop, c: this.current ? this.current.id : null, b: before};
319                if(this.current){
320                        dojo.place(this._drop, this.current, before ? "before" : "after");
321                        if(!this.firstIndicator){
322                                var next = this._drop.nextSibling;
323                                while(next != null){
324                                        next.coords.xy.y += parseInt(this._drop.style.height);
325                                        next = next.nextSibling;
326                                }
327                        }else{
328                                this.firstIndicator = false;
329                        }
330                }else{
331                        this.node.appendChild(this._drop);
332                }
333                this._drop.style.display = "";
334        },
335       
336        insertNodes: function(/*Boolean*/addSelected, /*Array*/data, /*Boolean*/before, /*Node*/anchor){
337                // summary: Inserts new data items (see Dojo Container's insertNodes method for details).
338                if(this.dropObject){
339                        dojo.style(this.dropObject.n,"display","none");
340                        dojox.layout.dnd.PlottedDnd.superclass.insertNodes.call(this,true,data,true,this.dropObject.n);
341                        this.deleteDashedZone();
342                }else{
343                         return dojox.layout.dnd.PlottedDnd.superclass.insertNodes.call(this,addSelected,data,before,anchor);
344                }
345                var _widget = dijit.byId(data[0].getAttribute("widgetId"));
346                if (_widget) {
347                        dojox.layout.dnd._setGcDndHandle(_widget, this.withHandles, this.handleClasses);
348                        if(this.hideSource)
349                                dojo.style(_widget.domNode, "display", "");
350                }
351        },
352       
353        _checkAutoScroll: function(e){
354                if(this._timer){
355                        clearTimeout(this._timer);
356                }
357                this._stopAutoScroll();
358                var node = this.dom,
359                        y = this._sumAncestorProperties(node,"offsetTop")
360                ;
361                //Down
362                if( (e.pageY - node.offsetTop +30 ) > node.clientHeight ){
363                        this.autoScrollActive = true;
364                        this._autoScrollDown(node);
365                } else if ( (node.scrollTop > 0) && (e.pageY - y) < 30){
366                        //Up
367                        this.autoScrollActive = true;
368                        this._autoScrollUp(node);
369                }
370        },
371
372        _autoScrollUp: function(node){
373                if( this.autoScrollActive && node.scrollTop > 0) {
374                        node.scrollTop -= 30;
375                        this._timer = setTimeout(dojo.hitch(this,"_autoScrollUp",node), 100);
376                }
377        },
378
379        _autoScrollDown: function(node){
380                if( this.autoScrollActive && (node.scrollTop < (node.scrollHeight-node.clientHeight))){
381                        node.scrollTop += 30;
382                        this._timer = setTimeout(dojo.hitch(this, "_autoScrollDown",node), 100);
383                }
384        },
385
386        _stopAutoScroll: function(){
387                this.autoScrollActive = false;
388        },
389
390        _sumAncestorProperties: function(node, prop){
391                //      summary
392                //      Returns the sum of the passed property on all ancestors of node.
393                node = dojo.byId(node);
394                if(!node){ return 0; }
395               
396                var retVal = 0;
397                while(node){
398                        var val = node[prop];
399                        if(val){
400                                retVal += val - 0;
401                                if(node == dojo.body()){ break; }// opera and khtml #body & #html has the same values, we only need one value
402                        }
403                        node = node.parentNode;
404                }
405                return retVal;  //      integer
406        }
407       
408});
409
410dojox.layout.dnd._setGcDndHandle = function(service,withHandles,handleClasses, first) {
411        var cls = "GcDndHandle";
412        if(!first){
413                dojo.query(".GcDndHandle", service.domNode).removeClass(cls);
414        }
415        if(!withHandles){
416                dojo.addClass(service.domNode, cls);
417        }else{
418                var _hasHandle = false;
419                for(var i = handleClasses.length - 1; i >= 0; i--){
420                        var _node = dojo.query("." + handleClasses[i], service.domNode)[0];
421                        if(_node){
422                                _hasHandle = true;
423                                if(handleClasses[i] != cls){
424                                        var _gripNode = dojo.query("." + cls, service.domNode);
425                                        if(_gripNode.length == 0){
426                                                dojo.removeClass(service.domNode, cls);
427                                        }else{
428                                                _gripNode.removeClass(cls);
429                                        }
430                                        dojo.addClass(_node, cls);
431                                }
432                        }
433                }
434                if(!_hasHandle){
435                        dojo.addClass(service.domNode, cls);
436                }
437        }
438};
439
440dojo.declare("dojox.layout.dnd.DropIndicator", null, {
441        // summary: An empty widget to show at the user the drop zone of the widget.
442        constructor: function(/*String*/cn, /*String*/tag) {
443                this.tag = tag || "div";
444                this.style = cn || null;
445        },
446
447        isInserted : function(){
448                return (this.node.parentNode && this.node.parentNode.nodeType==1);
449        },
450
451        create : function(/*Node*//*nodeRef*/){
452                if(this.node && this.isInserted()){ return this.node; }
453                var h = "90px",
454                        el = dojo.doc.createElement(this.tag);
455                       
456                if(this.style){
457                        el.className = this.style;
458                        el.style.height = h;
459                }else{
460                        // FIXME: allow this to be done mostly in CSS?
461                        dojo.style(el, {
462                                position:"relative",
463                                border:"1px dashed #F60",
464                                margin:"2px",
465                                height: h
466                        })
467                }
468                this.node = el;
469                return el;
470        },
471       
472        destroy : function(){
473                if(!this.node || !this.isInserted()){ return; }
474                this.node.parentNode.removeChild(this.node);
475                this.node = null;
476        }
477});
478
479dojo.extend(dojo.dnd.Manager, {
480       
481        canDrop: function(flag){
482                var canDropFlag = this.target && flag;
483                if(this.canDropFlag != canDropFlag){
484                        this.canDropFlag = canDropFlag;
485                        if(this.avatar){ this.avatar.update(); }
486                }
487               
488        },
489       
490        makeAvatar: function(){
491                //summary: Makes the avatar, it is separate to be overwritten dynamically, if needed.
492                return (this.source.declaredClass == "dojox.layout.dnd.PlottedDnd") ?
493                        new dojox.layout.dnd.Avatar(this, this.source.opacity) :
494                        new dojo.dnd.Avatar(this)
495                ;
496        }
497});
498
499if(dojo.isIE){
500        dojox.layout.dnd.handdleIE = [
501                dojo.subscribe("/dnd/start", null, function(){
502                        IEonselectstart = document.body.onselectstart;
503                        document.body.onselectstart = function(){ return false; };
504                }),
505                dojo.subscribe("/dnd/cancel", null, function(){
506                        document.body.onselectstart = IEonselectstart;
507                }),
508                dojo.subscribe("/dnd/drop", null, function(){
509                        document.body.onselectstart = IEonselectstart;
510                })
511        ];
512        dojo.addOnWindowUnload(function(){
513                dojo.forEach(dojox.layout.dnd.handdleIE, dojo.unsubscribe);
514        });
515}
Note: See TracBrowser for help on using the repository browser.