3
Copyright 2011 Yahoo! Inc. All rights reserved.
4
Licensed under the BSD License.
5
http://yuilibrary.com/license/
7
YUI.add('sortable', function(Y) {
11
* The class allows you to create a Drag & Drop reordered list.
15
* The class allows you to create a Drag & Drop reordered list.
22
var Sortable = function(o) {
23
Sortable.superclass.constructor.apply(this, arguments);
25
CURRENT_NODE = 'currentNode',
26
OPACITY_NODE = 'opacityNode',
31
PARENT_NODE = 'parentNode',
36
Y.extend(Sortable, Y.Base, {
40
* @description A reference to the DD.Delegate instance.
43
initializer: function() {
44
var id = 'sortable-' + Y.guid(), c,
46
container: this.get(CONT),
47
nodes: this.get(NODES),
49
invalid: this.get('invalid'),
55
if (this.get('handles')) {
56
delConfig.handles = this.get('handles');
58
del = new Y.DD.Delegate(delConfig);
62
del.dd.plug(Y.Plugin.DDProxy, {
70
groups: del.dd.get('groups')
71
}).on('drop:over', Y.bind(this._onDropOver, this));
74
'drag:start': Y.bind(this._onDragStart, this),
75
'drag:end': Y.bind(this._onDragEnd, this),
76
'drag:over': Y.bind(this._onDragOver, this),
77
'drag:drag': Y.bind(this._onDrag, this)
85
_onDrag: function(e) {
86
if (e.pageY < this._y) {
88
} else if (e.pageY > this._y) {
97
* @param Event e The Event Object
98
* @description Handles the DropOver event to append a drop node to an empty target
100
_onDropOver: function(e) {
101
if (!e.drop.get(NODE).test(this.get(NODES))) {
102
var nodes = e.drop.get(NODE).all(this.get(NODES));
103
if (nodes.size() === 0) {
104
e.drop.get(NODE).append(e.drag.get(NODE));
110
* @method _onDragOver
111
* @param Event e The Event Object
112
* @description Handles the DragOver event that moves the object in the list or to another list.
114
_onDragOver: function(e) {
115
if (!e.drop.get(NODE).test(this.get(NODES))) {
118
if (e.drag.get(NODE) == e.drop.get(NODE)) {
121
// is drop a child of drag?
122
if (e.drag.get(NODE).contains(e.drop.get(NODE))) {
125
var same = false, dir, oldNode, newNode, dropsort, dropNode,
126
moveType = this.get('moveType').toLowerCase();
128
if (e.drag.get(NODE).get(PARENT_NODE).contains(e.drop.get(NODE))) {
131
if (same && moveType == 'move') {
136
dir = ((this._up) ? 'before' : 'after');
137
dropNode = e.drop.get(NODE);
138
if (Y.Sortable._test(dropNode, this.get(CONT))) {
139
dropNode.append(e.drag.get(NODE));
141
dropNode.insert(e.drag.get(NODE), dir);
145
Y.DD.DDM.swapNode(e.drag, e.drop);
149
dropsort = Y.Sortable.getSortable(e.drop.get(NODE).get(PARENT_NODE));
152
Y.log('No delegate parent found', 'error', 'sortable');
156
Y.DD.DDM.getDrop(e.drag.get(NODE)).addToGroup(dropsort.get(ID));
160
Y.DD.DDM.swapNode(e.drag, e.drop);
162
if (this.get('moveType') == 'copy') {
164
oldNode = e.drag.get(NODE);
165
newNode = oldNode.cloneNode(true);
168
e.drag.set(NODE, newNode);
169
dropsort.delegate.createDrop(newNode, [dropsort.get(ID)]);
175
e.drop.get(NODE).insert(e.drag.get(NODE), 'before');
180
this.fire(moveType, { same: same, drag: e.drag, drop: e.drop });
181
this.fire('moved', { same: same, drag: e.drag, drop: e.drop });
185
* @method _onDragStart
186
* @param Event e The Event Object
187
* @description Handles the DragStart event and initializes some settings.
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');
197
* @param Event e The Event Object
198
* @description Handles the DragEnd event that cleans up the settings in the drag:start event.
200
_onDragEnd: function(e) {
201
this.delegate.get(this.get(OPACITY_NODE)).setStyle(OPACITY, 1);
202
this.delegate.get(CURRENT_NODE).setStyles({
210
* @param Class cls The class to plug
211
* @param Object config The class config
212
* @description Passthrough to the DD.Delegate.ddplug method
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);
220
this.delegate.dd.plug(cls, config);
226
* @description Passthrough to the DD.Delegate syncTargets method.
230
this.delegate.syncTargets();
233
destructor: function() {
234
this.delegate.destroy();
235
Sortable.unreg(this);
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.
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>
250
join: function(sel, type) {
251
if (!(sel instanceof Y.Sortable)) {
252
Y.error('Sortable: join needs a Sortable Instance');
258
type = type.toLowerCase();
259
var method = '_join_' + type;
270
* @param Sortable sel The Sortable to remove the join from
271
* @description Removes the join with the passed Sortable.
273
_join_none: function(sel) {
274
this.delegate.dd.removeFromGroup(sel.get(ID));
275
sel.delegate.dd.removeFromGroup(this.get(ID));
280
* @param Sortable sel The Sortable list to join with
281
* @description Joins both of the Sortables together.
283
_join_full: function(sel) {
284
this.delegate.dd.addToGroup(sel.get(ID));
285
sel.delegate.dd.addToGroup(this.get(ID));
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.
293
_join_outer: function(sel) {
294
this.delegate.dd.addToGroup(sel.get(ID));
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.
302
_join_inner: function(sel) {
303
sel.delegate.dd.addToGroup(this.get(ID));
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
311
getOrdering: function(callback) {
314
if (!Y.Lang.isFunction(callback)) {
315
callback = function (node) {
320
Y.one(this.get(CONT)).all(this.get(NODES)).each(function(node) {
321
ordering.push(callback(node));
330
* @description Drag handles to pass on to the internal DD.Delegate instance.
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.
346
* @description A selector query to get the children of the "container" to make draggable elements from.
350
value: '.dd-draggable'
354
* @description The opacity to change the proxy item to when dragging.
361
* @attribute opacityNode
362
* @description The node to set opacity on when dragging (dragNode or currentNode). Default: currentNode.
370
* @description The id of this Sortable, used to get a reference to this Sortable list from another list.
377
* @attribute moveType
378
* @description How should an item move to another list: insert, swap, move, copy. Default: insert
386
* @description A selector string to test if a list item is invalid and not sortable
395
* @property _sortables
398
* @description Hash map of all Sortables on the page.
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
408
_test: function(node, test) {
409
if (test instanceof Y.Node) {
410
return (test === node);
412
return node.test(test);
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.
421
getSortable: function(node) {
424
Y.each(Y.Sortable._sortables, function(v) {
425
if (Y.Sortable._test(node, v.get(CONT))) {
434
* @param Sortable s A Sortable instance.
435
* @description Register a Sortable instance with the singleton to allow lookups later.
438
Y.Sortable._sortables.push(s);
443
* @param Sortable s A Sortable instance.
444
* @description Unregister a Sortable instance with the singleton.
447
Y.each(Y.Sortable._sortables, function(v, k) {
449
Y.Sortable._sortables[k] = null;
450
delete Sortable._sortables[k];
456
Y.Sortable = Sortable;
460
* @description A Sortable node was moved.
461
* @param {Event.Facade} event An Event Facade object with the following specific property added:
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>
467
* @type {Event.Custom}
471
* @description A Sortable node was moved.
472
* @param {Event.Facade} event An Event Facade object with the following specific property added:
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>
478
* @type {Event.Custom}
482
* @description A Sortable node was moved.
483
* @param {Event.Facade} event An Event Facade object with the following specific property added:
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>
489
* @type {Event.Custom}
493
* @description A Sortable node was moved.
494
* @param {Event.Facade} event An Event Facade object with the following specific property added:
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>
500
* @type {Event.Custom}
504
* @description A Sortable node was moved.
505
* @param {Event.Facade} event An Event Facade object with the following specific property added:
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>
511
* @type {Event.Custom}
516
}, '3.4.1' ,{requires:['dd-delegate', 'dd-drop-plugin', 'dd-proxy']});