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.