/loggerhead/trunk

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/loggerhead/trunk

« back to all changes in this revision

Viewing changes to loggerhead/static/javascript/yui/build/dd/dd-drag-debug.js

[rs=mwhudson] Merged the latest and greatest from trunk.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
Copyright (c) 2008, Yahoo! Inc. All rights reserved.
 
3
Code licensed under the BSD License:
 
4
http://developer.yahoo.net/yui/license.txt
 
5
version: 3.0.0pr1
 
6
*/
 
7
YUI.add('dd-drag', function(Y) {
 
8
 
 
9
    /**
 
10
     * The Drag & Drop Utility allows you to create a draggable interface efficiently, buffering you from browser-level abnormalities and enabling you to focus on the interesting logic surrounding your particular implementation. This component enables you to create a variety of standard draggable objects with just a few lines of code and then, using its extensive API, add your own specific implementation logic.
 
11
     * @module dd
 
12
     * @submodule dd-drag
 
13
     */     
 
14
    /**
 
15
     * This class provides the ability to drag a Node.
 
16
     * @class Drag
 
17
     * @extends Base
 
18
     * @constructor
 
19
     */
 
20
 
 
21
    var DDM = Y.DD.DDM,
 
22
        NODE = 'node',
 
23
        DRAG_NODE = 'dragNode',
 
24
        OFFSET_HEIGHT = 'offsetHeight',
 
25
        OFFSET_WIDTH = 'offsetWidth',        
 
26
        MOUSE_UP = 'mouseup',
 
27
        MOUSE_DOWN = 'mousedown',
 
28
        /**
 
29
        * @event drag:mouseDown
 
30
        * @description Handles the mousedown DOM event, checks to see if you have a valid handle then starts the drag timers.
 
31
        * @preventable _handleMouseDown
 
32
        * @param {Event} ev The mousedown event.
 
33
        * @bubbles DDM
 
34
        * @type Event.Custom
 
35
        */
 
36
        EV_MOUSE_DOWN = 'drag:mouseDown',
 
37
        /**
 
38
        * @event drag:afterMouseDown
 
39
        * @description Fires after the mousedown event has been cleared.
 
40
        * @param {Event} ev The mousedown event.
 
41
        * @bubbles DDM
 
42
        * @type Event.Custom
 
43
        */
 
44
        EV_AFTER_MOUSE_DOWN = 'drag:afterMouseDown',
 
45
        /**
 
46
        * @event drag:removeHandle
 
47
        * @description Fires after a handle is removed.
 
48
        * @bubbles DDM
 
49
        * @type Event.Custom
 
50
        */
 
51
        EV_REMOVE_HANDLE = 'drag:removeHandle',
 
52
        /**
 
53
        * @event drag:addHandle
 
54
        * @description Fires after a handle is added.
 
55
        * @bubbles DDM
 
56
        * @type Event.Custom
 
57
        */
 
58
        EV_ADD_HANDLE = 'drag:addHandle',
 
59
        /**
 
60
        * @event drag:removeInvalid
 
61
        * @description Fires after an invalid selector is removed.
 
62
        * @bubbles DDM
 
63
        * @type Event.Custom
 
64
        */
 
65
        EV_REMOVE_INVALID = 'drag:removeInvalid',
 
66
        /**
 
67
        * @event drag:addInvalid
 
68
        * @description Fires after an invalid selector is added.
 
69
        * @bubbles DDM
 
70
        * @type Event.Custom
 
71
        */
 
72
        EV_ADD_INVALID = 'drag:addInvalid',
 
73
        /**
 
74
        * @event drag:start
 
75
        * @description Fires at the start of a drag operation.
 
76
        * @bubbles DDM
 
77
        * @type Event.Custom
 
78
        */
 
79
        EV_START = 'drag:start',
 
80
        /**
 
81
        * @event drag:end
 
82
        * @description Fires at the end of a drag operation.
 
83
        * @bubbles DDM
 
84
        * @type Event.Custom
 
85
        */
 
86
        EV_END = 'drag:end',
 
87
        /**
 
88
        * @event drag:drag
 
89
        * @description Fires every mousemove during a drag operation.
 
90
        * @bubbles DDM
 
91
        * @type Event.Custom
 
92
        */
 
93
        EV_DRAG = 'drag:drag';
 
94
 
 
95
 
 
96
        /**
 
97
        * @event drag:over
 
98
        * @description Fires when this node is over a Drop Target. (Fired from dd-drop)
 
99
        * @bubbles DDM
 
100
        * @type Event.Custom
 
101
        */
 
102
        /**
 
103
        * @event drag:enter
 
104
        * @description Fires when this node enters a Drop Target. (Fired from dd-drop)
 
105
        * @bubbles DDM
 
106
        * @type Event.Custom
 
107
        */
 
108
        /**
 
109
        * @event drag:exit
 
110
        * @description Fires when this node exits a Drop Target. (Fired from dd-drop)
 
111
        * @bubbles DDM
 
112
        * @type Event.Custom
 
113
        */
 
114
        /**
 
115
        * @event drag:drophit
 
116
        * @description Fires when this node is dropped on a valid Drop Target. (Fired from dd-ddm-drop)
 
117
        * @bubbles DDM
 
118
        * @type Event.Custom
 
119
        */
 
120
        /**
 
121
        * @event drag:dropmiss
 
122
        * @description Fires when this node is dropped on an invalid Drop Target. (Fired from dd-ddm-drop)
 
123
        * @bubbles DDM
 
124
        * @type Event.Custom
 
125
        */
 
126
    
 
127
    var Drag = function() {
 
128
        Drag.superclass.constructor.apply(this, arguments);
 
129
 
 
130
        DDM._regDrag(this);
 
131
    };
 
132
    Drag.NAME = 'drag';
 
133
 
 
134
    Drag.ATTRS = {
 
135
        /**
 
136
        * @attribute node
 
137
        * @description Y.Node instanace to use as the element to initiate a drag operation
 
138
        * @type Node
 
139
        */
 
140
        node: {
 
141
            set: function(node) {
 
142
                var n = Y.Node.get(node);
 
143
                if (!n) {
 
144
                    Y.fail('DD.Drag: Invalid Node Given: ' + node);
 
145
                }
 
146
                return n;
 
147
            }
 
148
        },
 
149
        /**
 
150
        * @attribute dragNode
 
151
        * @description Y.Node instanace to use as the draggable element, defaults to node
 
152
        * @type Node
 
153
        */
 
154
        dragNode: {
 
155
            set: function(node) {
 
156
                var n = Y.Node.get(node);
 
157
                if (!n) {
 
158
                    Y.fail('DD.Drag: Invalid dragNode Given: ' + node);
 
159
                }
 
160
                return n;
 
161
            }
 
162
        },
 
163
        /**
 
164
        * @attribute offsetNode
 
165
        * @description Offset the drag element by the difference in cursor position: default true
 
166
        * @type Boolean
 
167
        */
 
168
        offsetNode: {
 
169
            value: true
 
170
        },
 
171
        /**
 
172
        * @attribute clickPixelThresh
 
173
        * @description The number of pixels to move to start a drag operation, default is 3.
 
174
        * @type Number
 
175
        */
 
176
        clickPixelThresh: {
 
177
            value: DDM.get('clickPixelThresh')
 
178
        },
 
179
        /**
 
180
        * @attribute clickTimeThresh
 
181
        * @description The number of milliseconds a mousedown has to pass to start a drag operation, default is 1000.
 
182
        * @type Number
 
183
        */
 
184
        clickTimeThresh: {
 
185
            value: DDM.get('clickTimeThresh')
 
186
        },
 
187
        /**
 
188
        * @attribute lock
 
189
        * @description Set to lock this drag element so that it can't be dragged: default false.
 
190
        * @type Boolean
 
191
        */
 
192
        lock: {
 
193
            value: false,
 
194
            set: function(lock) {
 
195
                if (lock) {
 
196
                    this.get(NODE).addClass(DDM.CSS_PREFIX + '-locked');
 
197
                } else {
 
198
                    this.get(NODE).removeClass(DDM.CSS_PREFIX + '-locked');
 
199
                }
 
200
            }
 
201
        },
 
202
        /**
 
203
        * @attribute data
 
204
        * @description A payload holder to store arbitrary data about this drag object, can be used to store any value.
 
205
        * @type Mixed
 
206
        */
 
207
        data: {
 
208
            value: false
 
209
        },
 
210
        /**
 
211
        * @attribute move
 
212
        * @description If this is false, the drag element will not move with the cursor: default true. Can be used to "resize" the element.
 
213
        * @type Boolean
 
214
        */
 
215
        move: {
 
216
            value: true
 
217
        },
 
218
        /**
 
219
        * @attribute useShim
 
220
        * @description Use the protective shim on all drag operations: default true. Only works with dd-ddm, not dd-ddm-base.
 
221
        * @type Boolean
 
222
        */
 
223
        useShim: {
 
224
            value: true
 
225
        },
 
226
        /**
 
227
        * @attribute activeHandle
 
228
        * @description This config option is set by Drag to inform you of which handle fired the drag event (in the case that there are several handles): default false.
 
229
        * @type Node
 
230
        */
 
231
        activeHandle: {
 
232
            value: false
 
233
        },
 
234
        /**
 
235
        * @attribute primaryButtonOnly
 
236
        * @description By default a drag operation will only begin if the mousedown occurred with the primary mouse button. Setting this to false will allow for all mousedown events to trigger a drag.
 
237
        * @type Boolean
 
238
        */
 
239
        primaryButtonOnly: {
 
240
            value: true
 
241
        },
 
242
        /**
 
243
        * @attribute dragging
 
244
        * @description This attribute is not meant to be used by the implementor, it is meant to be used as an Event tracker so you can listen for it to change.
 
245
        * @type Boolean
 
246
        */
 
247
        dragging: {
 
248
            value: false
 
249
        },
 
250
        /**
 
251
        * @attribute target
 
252
        * @description This attribute only works if the dd-drop module has been loaded. It will make this node a drop target as well as draggable.
 
253
        * @type Boolean
 
254
        */
 
255
        target: {
 
256
            value: false,
 
257
            set: function(config) {
 
258
                this._handleTarget(config);
 
259
            }
 
260
        },
 
261
        /**
 
262
        * @attribute dragMode
 
263
        * @description This attribute only works if the dd-drop module is active. It will set the dragMode (point, intersect, strict) of this Drag instance.
 
264
        * @type String
 
265
        */
 
266
        dragMode: {
 
267
            value: null,
 
268
            set: function(mode) {
 
269
                return DDM._setDragMode(mode);
 
270
            }
 
271
        },
 
272
        /**
 
273
        * @attribute groups
 
274
        * @description Array of groups to add this drag into.
 
275
        * @type Array
 
276
        */
 
277
        groups: {
 
278
            value: ['default'],
 
279
            get: function() {
 
280
                if (!this._groups) {
 
281
                    this._groups = {};
 
282
                }
 
283
                var ret = [];
 
284
                Y.each(this._groups, function(v, k) {
 
285
                    ret[ret.length] = k;
 
286
                });
 
287
                return ret;
 
288
            },
 
289
            set: function(g) {
 
290
                this._groups = {};
 
291
                Y.each(g, function(v, k) {
 
292
                    this._groups[v] = true;
 
293
                }, this);
 
294
            }
 
295
        },
 
296
        /**
 
297
        * @attribute handles
 
298
        * @description Array of valid handles to add. Adding something here will set all handles, even if previously added with addHandle
 
299
        * @type Array
 
300
        */
 
301
        handles: {
 
302
            value: null,
 
303
            set: function(g) {
 
304
                if (g) {
 
305
                    this._handles = {};
 
306
                    Y.each(g, function(v, k) {
 
307
                        this._handles[v] = true;
 
308
                    }, this);
 
309
                } else {
 
310
                    this._handles = null;
 
311
                }
 
312
                return g;
 
313
            }
 
314
        }
 
315
    };
 
316
 
 
317
    Y.extend(Drag, Y.Base, {
 
318
        /**
 
319
        * @method addToGroup
 
320
        * @description Add this Drag instance to a group, this should be used for on-the-fly group additions.
 
321
        * @param {String} g The group to add this Drag Instance to.
 
322
        * @return {Self}
 
323
        * @chainable
 
324
        */
 
325
        addToGroup: function(g) {
 
326
            this._groups[g] = true;
 
327
            DDM._activateTargets();
 
328
            return this;
 
329
        },
 
330
        /**
 
331
        * @method removeFromGroup
 
332
        * @description Remove this Drag instance from a group, this should be used for on-the-fly group removals.
 
333
        * @param {String} g The group to remove this Drag Instance from.
 
334
        * @return {Self}
 
335
        * @chainable
 
336
        */
 
337
        removeFromGroup: function(g) {
 
338
            delete this._groups[g];
 
339
            DDM._activateTargets();
 
340
            return this;
 
341
        },
 
342
        /**
 
343
        * @property target
 
344
        * @description This will be a reference to the Drop instance associated with this drag if the target: true config attribute is set..
 
345
        * @type {Object}
 
346
        */
 
347
        target: null,
 
348
        /**
 
349
        * @private
 
350
        * @method _handleTarget
 
351
        * @description Attribute handler for the target config attribute.
 
352
        * @param {Boolean/Object}
 
353
        * @return {Boolean/Object}
 
354
        */
 
355
        _handleTarget: function(config) {
 
356
            if (Y.DD.Drop) {
 
357
                if (config === false) {
 
358
                    if (this.target) {
 
359
                        DDM._unregTarget(this.target);
 
360
                        this.target = null;
 
361
                    }
 
362
                    return false;
 
363
                } else {
 
364
                    if (!Y.Lang.isObject(config)) {
 
365
                        config = {};
 
366
                    }
 
367
                    config.node = this.get(NODE);
 
368
                    this.target = new Y.DD.Drop(config);
 
369
                }
 
370
            } else {
 
371
                return false;
 
372
            }
 
373
        },
 
374
        /**
 
375
        * @private
 
376
        * @property _groups
 
377
        * @description Storage Array for the groups this drag belongs to.
 
378
        * @type {Array}
 
379
        */
 
380
        _groups: null,
 
381
        /**
 
382
        * @private
 
383
        * @method _createEvents
 
384
        * @description This method creates all the events for this Event Target and publishes them so we get Event Bubbling.
 
385
        */
 
386
        _createEvents: function() {
 
387
            
 
388
            this.publish(EV_MOUSE_DOWN, {
 
389
                defaultFn: this._handleMouseDown,
 
390
                queuable: true,
 
391
                emitFacade: true,
 
392
                bubbles: true
 
393
            });
 
394
            
 
395
            var ev = [
 
396
                EV_AFTER_MOUSE_DOWN,
 
397
                EV_REMOVE_HANDLE,
 
398
                EV_ADD_HANDLE,
 
399
                EV_REMOVE_INVALID,
 
400
                EV_ADD_INVALID,
 
401
                EV_START,
 
402
                EV_END,
 
403
                EV_DRAG,
 
404
                'drag:drophit',
 
405
                'drag:dropmiss',
 
406
                'drag:over',
 
407
                'drag:enter',
 
408
                'drag:exit'
 
409
            ];
 
410
            
 
411
            Y.each(ev, function(v, k) {
 
412
                this.publish(v, {
 
413
                    type: v,
 
414
                    emitFacade: true,
 
415
                    bubbles: true,
 
416
                    preventable: false,
 
417
                    queuable: true
 
418
                });
 
419
            }, this);
 
420
            this.addTarget(DDM);
 
421
            
 
422
           
 
423
        },
 
424
        /**
 
425
        * @private
 
426
        * @property _ev_md
 
427
        * @description A private reference to the mousedown DOM event
 
428
        * @type {Event}
 
429
        */
 
430
        _ev_md: null,
 
431
        /**
 
432
        * @private
 
433
        * @property _startTime
 
434
        * @description The getTime of the mousedown event. Not used, just here in case someone wants/needs to use it.
 
435
        * @type Date
 
436
        */
 
437
        _startTime: null,
 
438
        /**
 
439
        * @private
 
440
        * @property _endTime
 
441
        * @description The getTime of the mouseup event. Not used, just here in case someone wants/needs to use it.
 
442
        * @type Date
 
443
        */
 
444
        _endTime: null,
 
445
        /**
 
446
        * @private
 
447
        * @property _handles
 
448
        * @description A private hash of the valid drag handles
 
449
        * @type {Object}
 
450
        */
 
451
        _handles: null,
 
452
        /**
 
453
        * @private
 
454
        * @property _invalids
 
455
        * @description A private hash of the invalid selector strings
 
456
        * @type {Object}
 
457
        */
 
458
        _invalids: null,
 
459
        /**
 
460
        * @private
 
461
        * @property _invalidsDefault
 
462
        * @description A private hash of the default invalid selector strings: {'textarea': true, 'input': true, 'a': true, 'button': true}
 
463
        * @type {Object}
 
464
        */
 
465
        _invalidsDefault: {'textarea': true, 'input': true, 'a': true, 'button': true},
 
466
        /**
 
467
        * @private
 
468
        * @property _dragThreshMet
 
469
        * @description Private flag to see if the drag threshhold was met
 
470
        * @type {Boolean}
 
471
        */
 
472
        _dragThreshMet: null,
 
473
        /**
 
474
        * @private
 
475
        * @property _fromTimeout
 
476
        * @description Flag to determine if the drag operation came from a timeout
 
477
        * @type {Boolean}
 
478
        */
 
479
        _fromTimeout: null,
 
480
        /**
 
481
        * @private
 
482
        * @property _clickTimeout
 
483
        * @description Holder for the setTimeout call
 
484
        * @type {Boolean}
 
485
        */
 
486
        _clickTimeout: null,
 
487
        /**
 
488
        * @property deltaXY
 
489
        * @description The offset of the mouse position to the element's position
 
490
        * @type {Array}
 
491
        */
 
492
        deltaXY: null,
 
493
        /**
 
494
        * @property startXY
 
495
        * @description The initial mouse position
 
496
        * @type {Array}
 
497
        */
 
498
        startXY: null,
 
499
        /**
 
500
        * @property nodeXY
 
501
        * @description The initial element position
 
502
        * @type {Array}
 
503
        */
 
504
        nodeXY: null,
 
505
        /**
 
506
        * @property lastXY
 
507
        * @description The position of the element as it's moving (for offset calculations)
 
508
        * @type {Array}
 
509
        */
 
510
        lastXY: null,
 
511
        /**
 
512
        * @property mouseXY
 
513
        * @description The XY coords of the mousemove
 
514
        * @type {Array}
 
515
        */
 
516
        mouseXY: null,
 
517
        /**
 
518
        * @property region
 
519
        * @description A region object associated with this drag, used for checking regions while dragging.
 
520
        * @type Object
 
521
        */
 
522
        region: null,       
 
523
        /**
 
524
        * @private
 
525
        * @method _handleMouseUp
 
526
        * @description Handler for the mouseup DOM event
 
527
        * @param {Event}
 
528
        */
 
529
        _handleMouseUp: function(ev) {
 
530
            this._fixIEMouseUp();
 
531
            if (DDM.activeDrag) {
 
532
                DDM._end();
 
533
            }
 
534
        },
 
535
        /** 
 
536
        * @private
 
537
        * @method _ieSelectFix
 
538
        * @description The function we use as the onselectstart handler when we start a drag in Internet Explorer
 
539
        */
 
540
        _ieSelectFix: function() {
 
541
            return false;
 
542
        },
 
543
        /** 
 
544
        * @private
 
545
        * @property _ieSelectBack
 
546
        * @description We will hold a copy of the current "onselectstart" method on this property, and reset it after we are done using it.
 
547
        */
 
548
        _ieSelectBack: null,
 
549
        /**
 
550
        * @private
 
551
        * @method _fixIEMouseDown
 
552
        * @description This method copies the onselectstart listner on the document to the _ieSelectFix property
 
553
        */
 
554
        _fixIEMouseDown: function() {
 
555
            if (Y.UA.ie) {
 
556
                this._ieSelectBack = Y.config.doc.body.onselectstart;
 
557
                Y.config.doc.body.onselectstart = this._ieSelectFix;
 
558
            }           
 
559
        },
 
560
        /**
 
561
        * @private
 
562
        * @method _fixIEMouseUp
 
563
        * @description This method copies the _ieSelectFix property back to the onselectstart listner on the document.
 
564
        */
 
565
        _fixIEMouseUp: function() {
 
566
            if (Y.UA.ie) {
 
567
                Y.config.doc.body.onselectstart = this._ieSelectBack;
 
568
            }           
 
569
        },
 
570
        /**
 
571
        * @private
 
572
        * @method _handleMouseDownEvent
 
573
        * @description Handler for the mousedown DOM event
 
574
        * @param {Event}
 
575
        */
 
576
        _handleMouseDownEvent: function(ev) {
 
577
            this.fire(EV_MOUSE_DOWN, { ev: ev });
 
578
        },
 
579
        /**
 
580
        * @private
 
581
        * @method _handleMouseDown
 
582
        * @description Handler for the mousedown DOM event
 
583
        * @param {Event}
 
584
        */
 
585
        _handleMouseDown: function(e) {
 
586
            var ev = e.ev;
 
587
            Y.log('_handleMouseDown', 'info', 'dd-drag');
 
588
            this._dragThreshMet = false;
 
589
            this._ev_md = ev;
 
590
            
 
591
            if (this.get('primaryButtonOnly') && ev.button > 1) {
 
592
                Y.log('Mousedown was not produced by the primary button', 'warn', 'dd-drag');
 
593
                return false;
 
594
            }
 
595
            if (this.validClick(ev)) {
 
596
                this._fixIEMouseDown();
 
597
                ev.halt();
 
598
                this._setStartPosition([ev.pageX, ev.pageY]);
 
599
 
 
600
                DDM.activeDrag = this;
 
601
 
 
602
                var self = this;
 
603
                this._clickTimeout = setTimeout(function() {
 
604
                    self._timeoutCheck.call(self);
 
605
                }, this.get('clickTimeThresh'));
 
606
            }
 
607
            this.fire(EV_AFTER_MOUSE_DOWN, { ev: ev });
 
608
        },
 
609
        /**
 
610
        * @method validClick
 
611
        * @description Method first checks to see if we have handles, if so it validates the click against the handle. Then if it finds a valid handle, it checks it against the invalid handles list. Returns true if a good handle was used, false otherwise.
 
612
        * @param {Event}
 
613
        * @return {Boolean}
 
614
        */
 
615
        validClick: function(ev) {
 
616
            var r = false,
 
617
            tar = ev.target,
 
618
            hTest = null;
 
619
            if (this._handles) {
 
620
                Y.log('validClick: We have handles', 'info', 'dd-drag');
 
621
                Y.each(this._handles, function(i, n) {
 
622
                    if (Y.Lang.isString(n)) {
 
623
                        //Am I this or am I inside this
 
624
                        if (tar.test(n + ', ' + n + ' *')) {
 
625
                            Y.log('Valid Selector found: ' + n, 'info', 'dd-drag');
 
626
                            hTest = n;
 
627
                            r = true;
 
628
                        }
 
629
                    }
 
630
                });
 
631
            } else {
 
632
                if (this.get(NODE).contains(tar) || this.get(NODE).compareTo(tar)) {
 
633
                    Y.log('validClick: We have a valid click', 'info', 'dd-drag');
 
634
                    r = true;
 
635
                }
 
636
            }
 
637
            if (r) {
 
638
                Y.log('validClick: Check invalid selectors', 'info', 'dd-drag');
 
639
                if (this._invalids) {
 
640
                    Y.each(this._invalids, function(i, n) {
 
641
                        if (Y.Lang.isString(n)) {
 
642
                            //Am I this or am I inside this
 
643
                            if (tar.test(n + ', ' + n + ' *')) {
 
644
                                Y.log('Invalid Selector found: (' + (n + ', ' + n + ' *') + ')', 'warn', 'dd-drag');
 
645
                                r = false;
 
646
                            }
 
647
                        }
 
648
                    });
 
649
                }
 
650
            }
 
651
            if (r) {
 
652
                if (hTest) {
 
653
                    var els = ev.currentTarget.queryAll(hTest);
 
654
                    els.each(function(n, i) {
 
655
                        if (n.contains(tar) || n.compareTo(tar)) {
 
656
                            this.set('activeHandle', els.item(i));
 
657
                        }
 
658
                    }, this);
 
659
                } else {
 
660
                    this.set('activeHandle', this.get(NODE));
 
661
                }
 
662
            }
 
663
            return r;
 
664
        },
 
665
        /**
 
666
        * @private
 
667
        * @method _setStartPosition
 
668
        * @description Sets the current position of the Element and calculates the offset
 
669
        * @param {Array} xy The XY coords to set the position to.
 
670
        */
 
671
        _setStartPosition: function(xy) {
 
672
            this.startXY = xy;
 
673
            
 
674
            this.nodeXY = this.get(NODE).getXY();
 
675
            this.lastXY = this.nodeXY;
 
676
 
 
677
            if (this.get('offsetNode')) {
 
678
                this.deltaXY = [(this.startXY[0] - this.nodeXY[0]), (this.startXY[1] - this.nodeXY[1])];
 
679
            } else {
 
680
                this.deltaXY = [0, 0];
 
681
            }
 
682
        },
 
683
        /**
 
684
        * @private
 
685
        * @method _timeoutCheck
 
686
        * @description The method passed to setTimeout to determine if the clickTimeThreshold was met.
 
687
        */
 
688
        _timeoutCheck: function() {
 
689
            if (!this.get('lock')) {
 
690
                Y.log("timeout threshold met", "info", "dd-drag");
 
691
                this._fromTimeout = true;
 
692
                this._dragThreshMet = true;
 
693
                this.start();
 
694
                this._moveNode([this._ev_md.pageX, this._ev_md.pageY], true);
 
695
            }
 
696
        },
 
697
        /**
 
698
        * @method removeHandle
 
699
        * @description Remove a Selector added by addHandle
 
700
        * @param {String} str The selector for the handle to be removed. 
 
701
        * @return {Self}
 
702
        * @chainable
 
703
        */
 
704
        removeHandle: function(str) {
 
705
            if (this._handles[str]) {
 
706
                delete this._handles[str];
 
707
                this.fire(EV_REMOVE_HANDLE, { handle: str });
 
708
            }
 
709
            return this;
 
710
        },
 
711
        /**
 
712
        * @method addHandle
 
713
        * @description Add a handle to a drag element. Drag only initiates when a mousedown happens on this element.
 
714
        * @param {String} str The selector to test for a valid handle. Must be a child of the element.
 
715
        * @return {Self}
 
716
        * @chainable
 
717
        */
 
718
        addHandle: function(str) {
 
719
            if (!this._handles) {
 
720
                this._handles = {};
 
721
            }
 
722
            if (Y.Lang.isString(str)) {
 
723
                this._handles[str] = true;
 
724
                this.fire(EV_ADD_HANDLE, { handle: str });
 
725
            }
 
726
            return this;
 
727
        },
 
728
        /**
 
729
        * @method removeInvalid
 
730
        * @description Remove an invalid handle added by addInvalid
 
731
        * @param {String} str The invalid handle to remove from the internal list.
 
732
        * @return {Self}
 
733
        * @chainable
 
734
        */
 
735
        removeInvalid: function(str) {
 
736
            if (this._invalids[str]) {
 
737
                delete this._handles[str];
 
738
                this.fire(EV_REMOVE_INVALID, { handle: str });
 
739
            }
 
740
            return this;
 
741
        },
 
742
        /**
 
743
        * @method addInvalid
 
744
        * @description Add a selector string to test the handle against. If the test passes the drag operation will not continue.
 
745
        * @param {String} str The selector to test against to determine if this is an invalid drag handle.
 
746
        * @return {Self}
 
747
        * @chainable
 
748
        */
 
749
        addInvalid: function(str) {
 
750
            if (Y.Lang.isString(str)) {
 
751
                this._invalids[str] = true;
 
752
                this.fire(EV_ADD_INVALID, { handle: str });
 
753
            } else {
 
754
                Y.log('Selector needs to be a string..', 'warn', 'dd-drag');
 
755
            }
 
756
            return this;
 
757
        },
 
758
        /**
 
759
        * @private
 
760
        * @method initializer
 
761
        * @description Internal init handler
 
762
        */
 
763
        initializer: function() {
 
764
            //TODO give the node instance a copy of this object
 
765
            //Not supported in PR1 due to Y.Node.get calling a new under the hood.
 
766
            //this.get(NODE).dd = this;
 
767
 
 
768
            if (!this.get(NODE).get('id')) {
 
769
                var id = Y.stamp(this.get(NODE));
 
770
                this.get(NODE).set('id', id);
 
771
            }
 
772
            
 
773
 
 
774
            this._invalids = this._invalidsDefault;
 
775
 
 
776
            this._createEvents();
 
777
            
 
778
            if (!this.get(DRAG_NODE)) {
 
779
                this.set(DRAG_NODE, this.get(NODE));
 
780
            }
 
781
            this._prep();
 
782
            this._dragThreshMet = false;
 
783
        },
 
784
        /**
 
785
        * @private
 
786
        * @method _prep
 
787
        * @description Attach event listners and add classname
 
788
        */
 
789
        _prep: function() {
 
790
            var node = this.get(NODE);
 
791
            node.addClass(DDM.CSS_PREFIX + '-draggable');
 
792
            node.on(MOUSE_DOWN, this._handleMouseDownEvent, this, true);
 
793
            node.on(MOUSE_UP, this._handleMouseUp, this, true);
 
794
        },
 
795
        /**
 
796
        * @private
 
797
        * @method _unprep
 
798
        * @description Detach event listners and remove classname
 
799
        */
 
800
        _unprep: function() {
 
801
            var node = this.get(NODE);
 
802
            node.removeClass(DDM.CSS_PREFIX + '-draggable');
 
803
            node.detach(MOUSE_DOWN, this._handleMouseDownEvent, this, true);
 
804
            node.detach(MOUSE_UP, this._handleMouseUp, this, true);
 
805
        },
 
806
        /**
 
807
        * @method start
 
808
        * @description Starts the drag operation
 
809
        * @return {Self}
 
810
        * @chainable
 
811
        */
 
812
        start: function() {
 
813
            if (!this.get('lock') && !this.get('dragging')) {
 
814
                this.set('dragging', true);
 
815
                DDM._start(this.deltaXY, [this.get(NODE).get(OFFSET_HEIGHT), this.get(NODE).get(OFFSET_WIDTH)]);
 
816
                Y.log('startDrag', 'info', 'dd-drag');
 
817
                this.get(NODE).addClass(DDM.CSS_PREFIX + '-dragging');
 
818
                this.fire(EV_START, { pageX: this.nodeXY[0], pageY: this.nodeXY[1] });
 
819
                this.get(DRAG_NODE).on(MOUSE_UP, this._handleMouseUp, this, true);
 
820
                var xy = this.nodeXY;
 
821
 
 
822
                this._startTime = (new Date()).getTime();
 
823
                
 
824
                this.region = {
 
825
                    '0': xy[0], 
 
826
                    '1': xy[1],
 
827
                    area: 0,
 
828
                    top: xy[1],
 
829
                    right: xy[0] + this.get(NODE).get(OFFSET_WIDTH),
 
830
                    bottom: xy[1] + this.get(NODE).get(OFFSET_HEIGHT),
 
831
                    left: xy[0]
 
832
                };
 
833
                
 
834
            }
 
835
            return this;
 
836
        },
 
837
        /**
 
838
        * @method end
 
839
        * @description Ends the drag operation
 
840
        * @return {Self}
 
841
        * @chainable
 
842
        */
 
843
        end: function() {
 
844
            this._endTime = (new Date()).getTime();
 
845
            clearTimeout(this._clickTimeout);
 
846
            this._dragThreshMet = false;
 
847
            this._fromTimeout = false;
 
848
            if (!this.get('lock') && this.get('dragging')) {
 
849
                Y.log('endDrag', 'info', 'dd-drag');
 
850
                this.fire(EV_END, { pageX: this.lastXY[0], pageY: this.lastXY[1] });
 
851
            }
 
852
            this.get(NODE).removeClass(DDM.CSS_PREFIX + '-dragging');
 
853
            this.set('dragging', false);
 
854
            this.deltaXY = [0, 0];
 
855
            this.get(DRAG_NODE).detach(MOUSE_UP, this._handleMouseUp, this, true);
 
856
 
 
857
            return this;
 
858
        },
 
859
        /**
 
860
        * @private
 
861
        * @method _align
 
862
        * @description Calculates the offsets and set's the XY that the element will move to.
 
863
        * @param {Array} xy The xy coords to align with.
 
864
        * @return Array
 
865
        * @type {Array}
 
866
        */
 
867
        _align: function(xy) {
 
868
            return [xy[0] - this.deltaXY[0], xy[1] - this.deltaXY[1]];
 
869
        },
 
870
        /**
 
871
        * @private
 
872
        * @method _moveNode
 
873
        * @description This method performs the actual element move.
 
874
        * @param {Array} eXY The XY to move the element to, usually comes from the mousemove DOM event.
 
875
        * @param {Boolean} noFire If true, the drag:drag event will not fire.
 
876
        */
 
877
        _moveNode: function(eXY, noFire) {
 
878
            var xy = this._align(eXY), diffXY = [], diffXY2 = [];
 
879
 
 
880
            diffXY[0] = (xy[0] - this.lastXY[0]);
 
881
            diffXY[1] = (xy[1] - this.lastXY[1]);
 
882
 
 
883
            diffXY2[0] = (xy[0] - this.nodeXY[0]);
 
884
            diffXY2[1] = (xy[1] - this.nodeXY[1]);
 
885
 
 
886
            if (this.get('move')) {
 
887
                if (Y.UA.opera) {
 
888
                    this.get(DRAG_NODE).setXY(xy);
 
889
                } else {
 
890
                    DDM.setXY(this.get(DRAG_NODE), diffXY);
 
891
                }
 
892
            }
 
893
 
 
894
            this.region = {
 
895
                '0': xy[0], 
 
896
                '1': xy[1],
 
897
                area: 0,
 
898
                top: xy[1],
 
899
                right: xy[0] + this.get(NODE).get(OFFSET_WIDTH),
 
900
                bottom: xy[1] + this.get(NODE).get(OFFSET_HEIGHT),
 
901
                left: xy[0]
 
902
            };
 
903
 
 
904
            var startXY = this.nodeXY;
 
905
            if (!noFire) {
 
906
                this.fire(EV_DRAG, {
 
907
                    pageX: xy[0],
 
908
                    pageY: xy[1],
 
909
                    info: {
 
910
                        start: startXY,
 
911
                        xy: xy,
 
912
                        delta: diffXY,
 
913
                        offset: diffXY2
 
914
                    } 
 
915
                });
 
916
            }
 
917
            
 
918
            this.lastXY = xy;
 
919
        },
 
920
        /**
 
921
        * @private
 
922
        * @method _move
 
923
        * @description Fired from DragDropMgr (DDM) on mousemove.
 
924
        * @param {Event} ev The mousemove DOM event
 
925
        */
 
926
        _move: function(ev) {
 
927
            if (this.get('lock')) {
 
928
                Y.log('Drag Locked', 'warn', 'dd-drag');
 
929
                return false;
 
930
            } else {
 
931
                this.mouseXY = [ev.pageX, ev.pageY];
 
932
                if (!this._dragThreshMet) {
 
933
                    var diffX = Math.abs(this.startXY[0] - ev.pageX);
 
934
                    var diffY = Math.abs(this.startXY[1] - ev.pageY);
 
935
                    Y.log("diffX: " + diffX + ", diffY: " + diffY, 'info', 'dd-drag');
 
936
                    if (diffX > this.get('clickPixelThresh') || diffY > this.get('clickPixelThresh')) {
 
937
                        Y.log("pixel threshold met", "info", "dd-drag");
 
938
                        this._dragThreshMet = true;
 
939
                        this.start();
 
940
                        this._moveNode([ev.pageX, ev.pageY]);
 
941
                    }
 
942
                
 
943
                } else {
 
944
                    clearTimeout(this._clickTimeout);
 
945
                    this._moveNode([ev.pageX, ev.pageY]);
 
946
                }
 
947
            }
 
948
        },
 
949
        /**
 
950
        * @method stopDrag
 
951
        * @description Method will forcefully stop a drag operation. For example calling this from inside an ESC keypress handler will stop this drag.
 
952
        * @return {Self}
 
953
        * @chainable
 
954
        */
 
955
        stopDrag: function() {
 
956
            if (this.get('dragging')) {
 
957
                Y.log('stopDrag called', 'warn', 'dd-drag');
 
958
                DDM._end();
 
959
            }
 
960
            return this;
 
961
        },
 
962
        /**
 
963
        * @private
 
964
        * @method destructor
 
965
        * @description Lifecycle destructor, unreg the drag from the DDM and remove listeners
 
966
        */
 
967
        destructor: function() {
 
968
            DDM._unregDrag(this);
 
969
 
 
970
            this._unprep();
 
971
            if (this.target) {
 
972
                this.target.destroy();
 
973
            }
 
974
        }
 
975
    });
 
976
    Y.namespace('DD');    
 
977
    Y.DD.Drag = Drag;
 
978
 
 
979
 
 
980
 
 
981
}, '3.0.0pr1' ,{requires:['dd-ddm-base'], skinnable:false});