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