PageSourceSearch

https://www.uwindsor.ca/news/sites/all/modules/uwindsor/uw_gridster_module/js/jquery.draggable.js

js uwindsor.ca collected 2026-10-01 09:09:52 UTC 14,464 bytes, 456 lines download raw bytes

1/*
2 * jquery.draggable
3 * https://github.com/ducksboard/gridster.js
4 *
5 * Copyright (c) 2012 ducksboard
6 * Licensed under the MIT licenses.
7 */
8
9;(function(root, factory) {
10    'use strict';
11    if(typeof exports === 'object') {
12        module.exports = factory(require('jquery'));
13    }
14    else if (typeof define === 'function' && define.amd) {
15        define('gridster-draggable', ['jquery'], factory);
16    } else {
17        root.GridsterDraggable = factory(root.$ || root.jQuery);
18    }
19
20}(this, function($) {
21    'use strict';
22    var defaults = {
23        items: 'none',
24        distance: 1,
25        limit: true,
26        offset_left: 0,
27        autoscroll: true,
28        ignore_dragging: ['INPUT', 'TEXTAREA', 'SELECT', 'BUTTON'], // or function
29        handle: null,
30        container_width: 0,  // 0 == auto
31        move_element: true,
32        helper: false,  // or 'clone'
33        remove_helper: true
34        // drag: function(e) {},
35        // start : function(e, ui) {},
36        // stop : function(e) {}
37    };
38
39    var $window = $(window);
40    var dir_map = { x : 'left', y : 'top' };
41    var isTouch = !!('ontouchstart' in window);
42
43    var capitalize = function(str) {
44        return str.charAt(0).toUpperCase() + str.slice(1);
45    };
46
47    var idCounter = 0;
48    var uniqId = function() {
49        return ++idCounter + '';
50    };
51
52    /**
53     * Basic drag implementation for DOM elements inside a container.
54     * Provide start/stop/drag callbacks.
55     *
56     * @class Draggable
57     * @param {HTMLElement} el The HTMLelement that contains all the widgets
58     *  to be dragged.
59     * @param {Object} [options] An Object with all options you want to
60     *        overwrite:
61     *    @param {HTMLElement|String} [options.items] Define who will
62     *     be the draggable items. Can be a CSS Selector String or a
63     *     collection of HTMLElements.
64     *    @param {Number} [options.distance] Distance in pixels after mousedown
65     *     the mouse must move before dragging should start.
66     *    @param {Boolean} [options.limit] Constrains dragging to the width of
67     *     the container
68     *    @param {Object|Function} [options.ignore_dragging] Array of node names
69     *      that sould not trigger dragging, by default is `['INPUT', 'TEXTAREA',
70     *      'SELECT', 'BUTTON']`. If a function is used return true to ignore dragging.
71     *    @param {offset_left} [options.offset_left] Offset added to the item
72     *     that is being dragged.
73     *    @param {Number} [options.drag] Executes a callback when the mouse is
74     *     moved during the dragging.
75     *    @param {Number} [options.start] Executes a callback when the drag
76     *     starts.
77     *    @param {Number} [options.stop] Executes a callback when the drag stops.
78     * @return {Object} Returns `el`.
79     * @constructor
80     */
81    function Draggable(el, options) {
82        this.options = $.extend({}, defaults, options);
83        this.$document = $(document);
84        this.$container = $(el);
85        this.$scroll_container = this.options.scroll_container === window ?
86            $(window) : this.$container.closest(this.options.scroll_container);
87        this.is_dragging = false;
88        this.player_min_left = 0 + this.options.offset_left;
89        this.id = uniqId();
90        this.ns = '.gridster-draggable-' + this.id;
91        this.init();
92    }
93
94    Draggable.defaults = defaults;
95
96    var fn = Draggable.prototype;
97
98    fn.init = function() {
99        var pos = this.$container.css('position');
100        this.calculate_dimensions();
101        this.$container.css('position', pos === 'static' ? 'relative' : pos);
102        this.disabled = false;
103        this.events();
104
105        $window.bind(this.nsEvent('resize'),
106            throttle($.proxy(this.calculate_dimensions, this), 200));
107    };
108
109    fn.nsEvent = function(ev) {
110        return (ev || '') + this.ns;
111    };
112
113    fn.events = function() {
114        this.pointer_events = {
115            start: this.nsEvent('touchstart') + ' ' + this.nsEvent('mousedown'),
116            move: this.nsEvent('touchmove') + ' ' + this.nsEvent('mousemove'),
117            end: this.nsEvent('touchend') + ' ' + this.nsEvent('mouseup')
118        };
119
120        this.$container.on(this.nsEvent('selectstart'),
121            $.proxy(this.on_select_start, this));
122
123        this.$container.on(this.pointer_events.start, this.options.items,
124            $.proxy(this.drag_handler, this));
125
126        this.$document.on(this.pointer_events.end, $.proxy(function(e) {
127            this.is_dragging = false;
128            if (this.disabled) { return; }
129            this.$document.off(this.pointer_events.move);
130            if (this.drag_start) {
131                this.on_dragstop(e);
132            }
133        }, this));
134    };
135
136    fn.get_actual_pos = function($el) {
137        return $el.position();
138    };
139
140
141    fn.get_mouse_pos = function(e) {
142        if (e.originalEvent && e.originalEvent.touches) {
143            var oe = e.originalEvent;
144            e = oe.touches.length ? oe.touches[0] : oe.changedTouches[0];
145        }
146
147        return {
148            left: e.clientX,
149            top: e.clientY
150        };
151    };
152
153
154    fn.get_offset = function(e) {
155        e.preventDefault();
156        var mouse_actual_pos = this.get_mouse_pos(e);
157        var diff_x = Math.round(
158            mouse_actual_pos.left - this.mouse_init_pos.left);
159        var diff_y = Math.round(mouse_actual_pos.top - this.mouse_init_pos.top);
160
161        var left = Math.round(this.el_init_offset.left +
162            diff_x - this.baseX +
163            this.$scroll_container.scrollLeft() -
164            this.scroll_container_offset_x);
165        var top = Math.round(this.el_init_offset.top +
166            diff_y - this.baseY +
167            this.$scroll_container.scrollTop() -
168            this.scroll_container_offset_y);
169
170        if (this.options.limit) {
171            if (left > this.player_max_left) {
172                left = this.player_max_left;
173            } else if(left < this.player_min_left) {
174                left = this.player_min_left;
175            }
176        }
177
178        return {
179            position: {
180                left: left,
181                top: top
182            },
183            pointer: {
184                left: mouse_actual_pos.left,
185                top: mouse_actual_pos.top,
186                diff_left: diff_x + (this.$scroll_container.scrollLeft() -
187                this.scroll_container_offset_x),
188                diff_top: diff_y + (this.$scroll_container.scrollTop() -
189                this.scroll_container_offset_y)
190            }
191        };
192    };
193
194
195    fn.get_drag_data = function(e) {
196        var offset = this.get_offset(e);
197        offset.$player = this.$player;
198        offset.$helper = this.helper ? this.$helper : this.$player;
199
200        return offset;
201    };
202
203
204    fn.set_limits = function(container_width) {
205        container_width || (container_width = this.$container.width());
206        this.player_max_left = (container_width - this.player_width +
207        - this.options.offset_left);
208
209        this.options.container_width = container_width;
210
211        return this;
212    };
213
214
215    fn.scroll_in = function(axis, data) {
216        var dir_prop = dir_map[axis];
217
218        var area_size = 50;
219        var scroll_inc = 30;
220        var scrollDir = 'scroll' + capitalize(dir_prop);
221
222        var is_x = axis === 'x';
223        var scroller_size = is_x ? this.scroller_width : this.scroller_height;
224        var doc_size;
225        if (this.$scroll_container === window){
226            doc_size = is_x ? this.$scroll_container.width() :
227                this.$scroll_container.height();
228        }else{
229            doc_size = is_x ? this.$scroll_container[0].scrollWidth :
230                this.$scroll_container[0].scrollHeight;
231        }
232        var player_size = is_x ? this.$player.width() : this.$player.height();
233
234        var next_scroll;
235        var scroll_offset = this.$scroll_container[scrollDir]();
236        var min_scroll_pos = scroll_offset;
237        var max_scroll_pos = min_scroll_pos + scroller_size;
238
239        var mouse_next_zone = max_scroll_pos - area_size;  // down/right
240        var mouse_prev_zone = min_scroll_pos + area_size;  // up/left
241
242        var abs_mouse_pos = min_scroll_pos + data.pointer[dir_prop];
243
244        var max_player_pos = (doc_size - scroller_size + player_size);
245
246        if (abs_mouse_pos >= mouse_next_zone) {
247            next_scroll = scroll_offset + scroll_inc;
248            if (next_scroll < max_player_pos) {
249                this.$scroll_container[scrollDir](next_scroll);
250                this['scroll_offset_' + axis] += scroll_inc;
251            }
252        }
253
254        if (abs_mouse_pos <= mouse_prev_zone) {
255            next_scroll = scroll_offset - scroll_inc;
256            if (next_scroll > 0) {
257                this.$scroll_container[scrollDir](next_scroll);
258                this['scroll_offset_' + axis] -= scroll_inc;
259            }
260        }
261
262        return this;
263    };
264
265
266    fn.manage_scroll = function(data) {
267        this.scroll_in('x', data);
268        this.scroll_in('y', data);
269    };
270
271
272    fn.calculate_dimensions = function() {
273        this.scroller_height = this.$scroll_container.height();
274        this.scroller_width = this.$scroll_container.width();
275    };
276
277
278    fn.drag_handler = function(e) {
279        // skip if drag is disabled, or click was not done with the mouse primary button
280        if (this.disabled || e.which !== 1 && !isTouch) {
281            return;
282        }
283
284        if (this.ignore_drag(e)) {
285            return;
286        }
287
288        var self = this;
289        var first = true;
290        this.$player = $(e.currentTarget);
291
292        this.el_init_pos = this.get_actual_pos(this.$player);
293        this.mouse_init_pos = this.get_mouse_pos(e);
294        this.offsetY = this.mouse_init_pos.top - this.el_init_pos.top;
295
296        this.$document.on(this.pointer_events.move, function(mme) {
297            var mouse_actual_pos = self.get_mouse_pos(mme);
298            var diff_x = Math.abs(
299                mouse_actual_pos.left - self.mouse_init_pos.left);
300            var diff_y = Math.abs(
301                mouse_actual_pos.top - self.mouse_init_pos.top);
302            if (!(diff_x > self.options.distance ||
303                diff_y > self.options.distance)
304            ) {
305                return false;
306            }
307
308            if (first) {
309                first = false;
310                self.on_dragstart.call(self, mme);
311                return false;
312            }
313
314            if (self.is_dragging === true) {
315                self.on_dragmove.call(self, mme);
316            }
317
318            return false;
319        });
320
321        if (!isTouch) { return false; }
322    };
323
324
325    fn.on_dragstart = function(e) {
326        e.preventDefault();
327
328        if (this.is_dragging) { return this; }
329
330        this.drag_start = this.is_dragging = true;
331        var offset = this.$container.offset();
332        this.baseX = Math.round(offset.left);
333        this.baseY = Math.round(offset.top);
334
335        if (this.options.helper === 'clone') {
336            this.$helper = this.$player.clone()
337                .appendTo(this.$container).addClass('helper');
338            this.helper = true;
339        } else {
340            this.helper = false;
341        }
342
343        this.scroll_container_offset_y = this.$scroll_container.scrollTop();
344        this.scroll_container_offset_x = this.$scroll_container.scrollLeft();
345        this.el_init_offset = this.$player.offset();
346        this.player_width = this.$player.width();
347
348        this.set_limits(this.options.container_width);
349
350        if (this.options.start) {
351            this.options.start.call(this.$player, e, this.get_drag_data(e));
352        }
353        return false;
354    };
355
356
357    fn.on_dragmove = function(e) {
358        var data = this.get_drag_data(e);
359
360        this.options.autoscroll && this.manage_scroll(data);
361
362        if (this.options.move_element) {
363            (this.helper ? this.$helper : this.$player).css({
364                'position': 'absolute',
365                'left' : data.position.left,
366                'top' : data.position.top
367            });
368        }
369
370        var last_position = this.last_position || data.position;
371        data.prev_position = last_position;
372
373        if (this.options.drag) {
374            this.options.drag.call(this.$player, e, data);
375        }
376
377        this.last_position = data.position;
378        return false;
379    };
380
381
382    fn.on_dragstop = function(e) {
383        var data = this.get_drag_data(e);
384        this.drag_start = false;
385
386        if (this.options.stop) {
387            this.options.stop.call(this.$player, e, data);
388        }
389
390        if (this.helper && this.options.remove_helper) {
391            this.$helper.remove();
392        }
393
394        return false;
395    };
396
397    fn.on_select_start = function(e) {
398        if (this.disabled) { return; }
399
400        if (this.ignore_drag(e)) {
401            return;
402        }
403
404        return false;
405    };
406
407    fn.enable = function() {
408        this.disabled = false;
409    };
410
411    fn.disable = function() {
412        this.disabled = true;
413    };
414
415    fn.destroy = function() {
416        this.disable();
417
418        this.$container.off(this.ns);
419        this.$document.off(this.ns);
420        $window.off(this.ns);
421
422        $.removeData(this.$container, 'drag');
423    };
424
425    fn.ignore_drag = function(event) {
426        if (this.options.handle) {
427            return !$(event.target).is(this.options.handle);
428        }
429
430        if ($.isFunction(this.options.ignore_dragging)) {
431            return this.options.ignore_dragging(event);
432        }
433
434        if (this.options.resize) {
435            return ! $(event.target).is(this.options.items);
436        }
437
438        return $(event.target).is(this.options.ignore_dragging.join(', '));
439    };
440
441    //jQuery adapter
442    $.fn.gridDraggable = function ( options ) {
443        return new Draggable(this, options);
444    };
445
446    $.fn.dragg = function (options) {
447        return this.each(function () {
448            if (!$.data(this, 'drag')) {
449                $.data(this, 'drag', new Draggable(this, options));
450            }
451        });
452    };
453
454    return Draggable;
455
456}));

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.