NOBUG: Fixed file access permissions
[moodle.git] / lib / yuilib / 3.13.0 / sortable / sortable-debug.js
blob059edfd391f63857442e3b100022255068829264
1 /*
2 YUI 3.13.0 (build 508226d)
3 Copyright 2013 Yahoo! Inc. All rights reserved.
4 Licensed under the BSD License.
5 http://yuilibrary.com/license/
6 */
8 YUI.add('sortable', function (Y, NAME) {
11     /**
12      * The class allows you to create a Drag & Drop reordered list.
13      * @module sortable
14      */
15     /**
16      * The class allows you to create a Drag & Drop reordered list.
17      * @class Sortable
18      * @extends Base
19      * @constructor
20      */
23     var Sortable = function() {
24         Sortable.superclass.constructor.apply(this, arguments);
25     },
26     CURRENT_NODE = 'currentNode',
27     OPACITY_NODE = 'opacityNode',
28     CONT = 'container',
29     ID = 'id',
30     ZINDEX = 'zIndex',
31     OPACITY = 'opacity',
32     PARENT_NODE = 'parentNode',
33     NODES = 'nodes',
34     NODE = 'node';
37     Y.extend(Sortable, Y.Base, {
38         /**
39         * @property delegate
40         * @type DD.Delegate
41         * @description A reference to the DD.Delegate instance.
42         */
43         delegate: null,
44         /**
45         * @property drop
46         * @type DD.Drop
47         * @description A reference to the DD.Drop instance
48         */
49         drop: null,
50         initializer: function() {
51             var id = 'sortable-' + Y.guid(),
52                 delConfig = {
53                     container: this.get(CONT),
54                     nodes: this.get(NODES),
55                     target: true,
56                     invalid: this.get('invalid'),
57                     dragConfig: {
58                         groups: [ id ]
59                     }
60                 }, del;
62             if (this.get('handles')) {
63                 delConfig.handles = this.get('handles');
64             }
65             del = new Y.DD.Delegate(delConfig);
67             this.set(ID, id);
69             del.dd.plug(Y.Plugin.DDProxy, {
70                 moveOnEnd: false,
71                 cloneNode: true
72             });
74             this.drop =  new Y.DD.Drop({
75                 node: this.get(CONT),
76                 bubbleTarget: del,
77                 groups: del.dd.get('groups')
78             });
79             this.drop.on('drop:enter', Y.bind(this._onDropEnter, this));
81             del.on({
82                 'drag:start': Y.bind(this._onDragStart, this),
83                 'drag:end': Y.bind(this._onDragEnd, this),
84                 'drag:over': Y.bind(this._onDragOver, this),
85                 'drag:drag': Y.bind(this._onDrag, this)
86             });
88             this.delegate = del;
89             Sortable.reg(this, id);
90         },
91         _up: null,
92         _y: null,
93         _onDrag: function(e) {
94             if (e.pageY < this._y) {
95                 this._up = true;
96             } else if (e.pageY > this._y) {
97                 this._up = false;
98             }
100             this._y = e.pageY;
101         },
102         /**
103         * @private
104         * @method _onDropEnter
105         * @param Event e The Event Object
106         * @description Handles the DropEnter event to append a new node to a target.
107         */
108         _onDropEnter: function(e) {
109             var dropNode = e.drop.get(NODE),
110                 dragNode = e.drag.get(NODE);
112             if (!dropNode.test(this.get(NODES)) &&
113                 !dragNode.get(PARENT_NODE).compareTo(dropNode)) {
114                 dropNode.append(dragNode);
115             }
116         },
117         /**
118         * @private
119         * @method _onDragOver
120         * @param Event e The Event Object
121         * @description Handles the DragOver event that moves the object in the list or to another list.
122         */
123         _onDragOver: function(e) {
124             if (!e.drop.get(NODE).test(this.get(NODES))) {
125                 return;
126             }
127             if (e.drag.get(NODE) === e.drop.get(NODE)) {
128                 return;
129             }
130             // is drop a child of drag?
131             if (e.drag.get(NODE).contains(e.drop.get(NODE))) {
132                 return;
133             }
134             var same = false, dir, oldNode, newNode, dropsort, dropNode,
135                 moveType = this.get('moveType').toLowerCase();
137             if (e.drag.get(NODE).get(PARENT_NODE).contains(e.drop.get(NODE))) {
138                 same = true;
139             }
140             if (same && moveType === 'move') {
141                 moveType = 'insert';
142             }
143             switch (moveType) {
144                 case 'insert':
145                     dir = ((this._up) ? 'before' : 'after');
146                     dropNode = e.drop.get(NODE);
147                     if (Y.Sortable._test(dropNode, this.get(CONT))) {
148                         dropNode.append(e.drag.get(NODE));
149                     } else {
150                         dropNode.insert(e.drag.get(NODE), dir);
151                     }
152                     break;
153                 case 'swap':
154                     Y.DD.DDM.swapNode(e.drag, e.drop);
155                     break;
156                 case 'move':
157                 case 'copy':
158                     dropsort = Y.Sortable.getSortable(e.drop.get(NODE).get(PARENT_NODE));
160                     if (!dropsort) {
161                         Y.log('No delegate parent found', 'error', 'sortable');
162                         return;
163                     }
165                     Y.DD.DDM.getDrop(e.drag.get(NODE)).addToGroup(dropsort.get(ID));
167                     //Same List
168                     if (same) {
169                         Y.DD.DDM.swapNode(e.drag, e.drop);
170                     } else {
171                         if (this.get('moveType') === 'copy') {
172                             //New List
173                             oldNode = e.drag.get(NODE);
174                             newNode = oldNode.cloneNode(true);
176                             newNode.set(ID, '');
177                             e.drag.set(NODE, newNode);
178                             dropsort.delegate.createDrop(newNode, [dropsort.get(ID)]);
179                             oldNode.setStyles({
180                                 top: '',
181                                 left: ''
182                             });
183                         }
184                         e.drop.get(NODE).insert(e.drag.get(NODE), 'before');
185                     }
186                     break;
187             }
189             this.fire(moveType, { same: same, drag: e.drag, drop: e.drop });
190             this.fire('moved', { same: same, drag: e.drag, drop: e.drop });
191         },
192         /**
193         * @private
194         * @method _onDragStart
195         * @param Event e The Event Object
196         * @description Handles the DragStart event and initializes some settings.
197         */
198         _onDragStart: function() {
199             var del = this.delegate,
200                 lastNode = del.get('lastNode');
201             if (lastNode && lastNode.getDOMNode()) {
202                 lastNode.setStyle(ZINDEX, '');
203             }
204             del.get(this.get(OPACITY_NODE)).setStyle(OPACITY, this.get(OPACITY));
205             del.get(CURRENT_NODE).setStyle(ZINDEX, '999');
206         },
207         /**
208         * @private
209         * @method _onDragEnd
210         * @param Event e The Event Object
211         * @description Handles the DragEnd event that cleans up the settings in the drag:start event.
212         */
213         _onDragEnd: function() {
214             this.delegate.get(this.get(OPACITY_NODE)).setStyle(OPACITY, 1);
215             this.delegate.get(CURRENT_NODE).setStyles({
216                 top: '',
217                 left: ''
218             });
219             this.sync();
220         },
221         /**
222         * @method plug
223         * @param Class cls The class to plug
224         * @param Object config The class config
225         * @description Passthrough to the DD.Delegate.ddplug method
226         * @chainable
227         */
228         plug: function(cls, config) {
229             //I don't like this.. Not at all, need to discuss with the team
230             if (cls && cls.NAME.substring(0, 4).toLowerCase() === 'sort') {
231                 this.constructor.superclass.plug.call(this, cls, config);
232             } else {
233                 this.delegate.dd.plug(cls, config);
234             }
235             return this;
236         },
237         /**
238         * @method sync
239         * @description Passthrough to the DD.Delegate syncTargets method.
240         * @chainable
241         */
242         sync: function() {
243             this.delegate.syncTargets();
244             return this;
245         },
246         destructor: function() {
247             this.drop.destroy();
248             this.delegate.destroy();
249             Sortable.unreg(this, this.get(ID));
250         },
251         /**
252         * @method join
253         * @param Sortable sel The Sortable list to join with
254         * @param String type The type of join to do: full, inner, outer, none. Default: full
255         * @description Join this Sortable with another Sortable instance.
256         * <ul>
257         *   <li>full: Exchange nodes with both lists.</li>
258         *   <li>inner: Items can go into this list from the joined list.</li>
259         *   <li>outer: Items can go out of the joined list into this list.</li>
260         *   <li>none: Removes the join.</li>
261         * </ul>
262         * @chainable
263         */
264         join: function(sel, type) {
265             if (!(sel instanceof Y.Sortable)) {
266                 Y.error('Sortable: join needs a Sortable Instance');
267                 return this;
268             }
269             if (!type) {
270                 type = 'full';
271             }
272             type = type.toLowerCase();
273             var method = '_join_' + type;
275             if (this[method]) {
276                 this[method](sel);
277             }
279             return this;
280         },
281         /**
282         * @private
283         * @method _join_none
284         * @param Sortable sel The Sortable to remove the join from
285         * @description Removes the join with the passed Sortable.
286         */
287         _join_none: function(sel) {
288             this.delegate.dd.removeFromGroup(sel.get(ID));
289             sel.delegate.dd.removeFromGroup(this.get(ID));
290         },
291         /**
292         * @private
293         * @method _join_full
294         * @param Sortable sel The Sortable list to join with
295         * @description Joins both of the Sortables together.
296         */
297         _join_full: function(sel) {
298             this.delegate.dd.addToGroup(sel.get(ID));
299             sel.delegate.dd.addToGroup(this.get(ID));
300         },
301         /**
302         * @private
303         * @method _join_outer
304         * @param Sortable sel The Sortable list to join with
305         * @description Allows this Sortable to accept items from the passed Sortable.
306         */
307         _join_outer: function(sel) {
308             this.delegate.dd.addToGroup(sel.get(ID));
309         },
310         /**
311         * @private
312         * @method _join_inner
313         * @param Sortable sel The Sortable list to join with
314         * @description Allows this Sortable to give items to the passed Sortable.
315         */
316         _join_inner: function(sel) {
317             sel.delegate.dd.addToGroup(this.get(ID));
318         },
319         /**
320         * A custom callback to allow a user to extract some sort of id or any other data
321         * from the node to use in the "ordering list" and then that data should be returned from the callback.
322         * @method getOrdering
323         * @param Function callback
324         * @return Array
325         */
326         getOrdering: function(callback) {
327             var ordering = [];
329             if (!Y.Lang.isFunction(callback)) {
330                 callback = function (node) {
331                     return node;
332                 };
333             }
335             Y.one(this.get(CONT)).all(this.get(NODES)).each(function(node) {
336                 ordering.push(callback(node));
337             });
338             return ordering;
339        }
340     }, {
341         NAME: 'sortable',
342         ATTRS: {
343             /**
344             * @attribute handles
345             * @description Drag handles to pass on to the internal DD.Delegate instance.
346             * @type Array
347             */
348             handles: {
349                 value: false
350             },
351             /**
352             * @attribute container
353             * @description A selector query to get the container to listen for mousedown events on. All "nodes" should be a child of this container.
354             * @type String
355             */
356             container: {
357                 value: 'body'
358             },
359             /**
360             * @attribute nodes
361             * @description A selector query to get the children of the "container" to make draggable elements from.
362             * @type String
363             */
364             nodes: {
365                 value: '.dd-draggable'
366             },
367             /**
368             * @attribute opacity
369             * @description The opacity to change the proxy item to when dragging.
370             * @type String
371             */
372             opacity: {
373                 value: '.75'
374             },
375             /**
376             * @attribute opacityNode
377             * @description The node to set opacity on when dragging (dragNode or currentNode). Default: currentNode.
378             * @type String
379             */
380             opacityNode: {
381                 value: 'currentNode'
382             },
383             /**
384             * @attribute id
385             * @description The id of this Sortable, used to get a reference to this Sortable list from another list.
386             * @type String
387             */
388             id: {
389                 value: null
390             },
391             /**
392             * @attribute moveType
393             * @description How should an item move to another list: insert, swap, move, copy. Default: insert
394             * @type String
395             */
396             moveType: {
397                 value: 'insert'
398             },
399             /**
400             * @attribute invalid
401             * @description A selector string to test if a list item is invalid and not sortable
402             * @type String
403             */
404             invalid: {
405                 value: ''
406             }
407         },
408         /**
409         * @static
410         * @property _sortables
411         * @private
412         * @type Object
413         * @description Hash map of all Sortables on the page.
414         */
415         _sortables: {},
416         /**
417         * @static
418         * @method _test
419         * @param {Node} node The node instance to test.
420         * @param {String|Node} test The node instance or selector string to test against.
421         * @description Test a Node or a selector for the container
422         */
423         _test: function(node, test) {
424             var ret;
425             if (test instanceof Y.Node) {
426                 ret = (test === node);
427             } else {
428                 ret = node.test(test);
429             }
430             return ret;
431         },
432         /**
433         * @static
434         * @method getSortable
435         * @param {String|Node} node The node instance or selector string to use to find a Sortable instance.
436         * @description Get a Sortable instance back from a node reference or a selector string.
437         */
438         getSortable: function(node) {
439             var s = null,
440                 id = null;
441             node = Y.one(node);
442             id = node.get(ID);
443             if(id && Y.Sortable._sortables[id]) {
444                 return Y.Sortable._sortables[id];
445             }
446             Y.Object.each(Y.Sortable._sortables, function(v) {
447                 if (Y.Sortable._test(node, v.get(CONT))) {
448                     s = v;
449                 }
450             });
451             return s;
452         },
453         /**
454         * @static
455         * @method reg
456         * @param Sortable s A Sortable instance.
457         * @param String id (optional) The id of the sortable instance.
458         * @description Register a Sortable instance with the singleton to allow lookups later.
459         */
460         reg: function(s, id) {
461             if (!id) {
462                 id = s.get(ID);
463             }
464             Y.Sortable._sortables[id] = s;
465         },
466         /**
467         * @static
468         * @method unreg
469         * @param Sortable s A Sortable instance.
470         * @param String id (optional) The id of the sortable instance.
471         * @description Unregister a Sortable instance with the singleton.
472         */
473         unreg: function(s, id) {
474             if (!id) {
475                 id = s.get(ID);
476             }
477             if (id && Y.Sortable._sortables[id]) {
478                 delete Y.Sortable._sortables[id];
479                 return;
480             }
481             Y.Object.each(Y.Sortable._sortables, function(v, k) {
482                 if (v === s) {
483                     delete Sortable._sortables[k];
484                 }
485             });
486         }
487     });
489     Y.Sortable = Sortable;
491     /**
492     * @event copy
493     * @description A Sortable node was moved with a copy.
494     * @param {Event.Facade} event An Event Facade object
495     * @param {Boolean} event.same Moved to the same list.
496     * @param {DD.Drag} event.drag The drag instance.
497     * @param {DD.Drop} event.drop The drop instance.
498     * @type {Event.Custom}
499     */
500     /**
501     * @event move
502     * @description A Sortable node was moved with a move.
503     * @param {Event.Facade} event An Event Facade object with the following specific property added:
504     * @param {Boolean} event.same Moved to the same list.
505     * @param {DD.Drag} event.drag The drag instance.
506     * @param {DD.Drop} event.drop The drop instance.
507     * @type {Event.Custom}
508     */
509     /**
510     * @event insert
511     * @description A Sortable node was moved with an insert.
512     * @param {Event.Facade} event An Event Facade object with the following specific property added:
513     * @param {Boolean} event.same Moved to the same list.
514     * @param {DD.Drag} event.drag The drag instance.
515     * @param {DD.Drop} event.drop The drop instance.
516     * @type {Event.Custom}
517     */
518     /**
519     * @event swap
520     * @description A Sortable node was moved with a swap.
521     * @param {Event.Facade} event An Event Facade object with the following specific property added:
522     * @param {Boolean} event.same Moved to the same list.
523     * @param {DD.Drag} event.drag The drag instance.
524     * @param {DD.Drop} event.drop The drop instance.
525     * @type {Event.Custom}
526     */
527     /**
528     * @event moved
529     * @description A Sortable node was moved.
530     * @param {Event.Facade} event An Event Facade object with the following specific property added:
531     * @param {Boolean} event.same Moved to the same list.
532     * @param {DD.Drag} event.drag The drag instance.
533     * @param {DD.Drop} event.drop The drop instance.
534     * @type {Event.Custom}
535     */
539 }, '3.13.0', {"requires": ["dd-delegate", "dd-drop-plugin", "dd-proxy"]});