/static/scripts/jquery.ui.sortable.slider.js

https://bitbucket.org/cistrome/cistrome-harvard/ · JavaScript · 2086 lines · 1490 code · 389 blank · 207 comment · 485 complexity · 0fb94f061421dfd49684bdf234862a1d MD5 · raw file

Large files are truncated click here to view the full file

  1. /*!
  2. * jQuery UI 1.8.5
  3. *
  4. * Copyright 2010, AUTHORS.txt (http://jqueryui.com/about)
  5. * Dual licensed under the MIT or GPL Version 2 licenses.
  6. * http://jquery.org/license
  7. *
  8. * http://docs.jquery.com/UI
  9. */
  10. (function( $, undefined ) {
  11. // prevent duplicate loading
  12. // this is only a problem because we proxy existing functions
  13. // and we don't want to double proxy them
  14. $.ui = $.ui || {};
  15. if ( $.ui.version ) {
  16. return;
  17. }
  18. $.extend( $.ui, {
  19. version: "1.8.5",
  20. keyCode: {
  21. ALT: 18,
  22. BACKSPACE: 8,
  23. CAPS_LOCK: 20,
  24. COMMA: 188,
  25. COMMAND: 91,
  26. COMMAND_LEFT: 91, // COMMAND
  27. COMMAND_RIGHT: 93,
  28. CONTROL: 17,
  29. DELETE: 46,
  30. DOWN: 40,
  31. END: 35,
  32. ENTER: 13,
  33. ESCAPE: 27,
  34. HOME: 36,
  35. INSERT: 45,
  36. LEFT: 37,
  37. MENU: 93, // COMMAND_RIGHT
  38. NUMPAD_ADD: 107,
  39. NUMPAD_DECIMAL: 110,
  40. NUMPAD_DIVIDE: 111,
  41. NUMPAD_ENTER: 108,
  42. NUMPAD_MULTIPLY: 106,
  43. NUMPAD_SUBTRACT: 109,
  44. PAGE_DOWN: 34,
  45. PAGE_UP: 33,
  46. PERIOD: 190,
  47. RIGHT: 39,
  48. SHIFT: 16,
  49. SPACE: 32,
  50. TAB: 9,
  51. UP: 38,
  52. WINDOWS: 91 // COMMAND
  53. }
  54. });
  55. // plugins
  56. $.fn.extend({
  57. _focus: $.fn.focus,
  58. focus: function( delay, fn ) {
  59. return typeof delay === "number" ?
  60. this.each(function() {
  61. var elem = this;
  62. setTimeout(function() {
  63. $( elem ).focus();
  64. if ( fn ) {
  65. fn.call( elem );
  66. }
  67. }, delay );
  68. }) :
  69. this._focus.apply( this, arguments );
  70. },
  71. scrollParent: function() {
  72. var scrollParent;
  73. if (($.browser.msie && (/(static|relative)/).test(this.css('position'))) || (/absolute/).test(this.css('position'))) {
  74. scrollParent = this.parents().filter(function() {
  75. return (/(relative|absolute|fixed)/).test($.curCSS(this,'position',1)) && (/(auto|scroll)/).test($.curCSS(this,'overflow',1)+$.curCSS(this,'overflow-y',1)+$.curCSS(this,'overflow-x',1));
  76. }).eq(0);
  77. } else {
  78. scrollParent = this.parents().filter(function() {
  79. return (/(auto|scroll)/).test($.curCSS(this,'overflow',1)+$.curCSS(this,'overflow-y',1)+$.curCSS(this,'overflow-x',1));
  80. }).eq(0);
  81. }
  82. return (/fixed/).test(this.css('position')) || !scrollParent.length ? $(document) : scrollParent;
  83. },
  84. zIndex: function( zIndex ) {
  85. if ( zIndex !== undefined ) {
  86. return this.css( "zIndex", zIndex );
  87. }
  88. if ( this.length ) {
  89. var elem = $( this[ 0 ] ), position, value;
  90. while ( elem.length && elem[ 0 ] !== document ) {
  91. // Ignore z-index if position is set to a value where z-index is ignored by the browser
  92. // This makes behavior of this function consistent across browsers
  93. // WebKit always returns auto if the element is positioned
  94. position = elem.css( "position" );
  95. if ( position === "absolute" || position === "relative" || position === "fixed" ) {
  96. // IE returns 0 when zIndex is not specified
  97. // other browsers return a string
  98. // we ignore the case of nested elements with an explicit value of 0
  99. // <div style="z-index: -10;"><div style="z-index: 0;"></div></div>
  100. value = parseInt( elem.css( "zIndex" ) );
  101. if ( !isNaN( value ) && value != 0 ) {
  102. return value;
  103. }
  104. }
  105. elem = elem.parent();
  106. }
  107. }
  108. return 0;
  109. },
  110. disableSelection: function() {
  111. return this.bind(
  112. "mousedown.ui-disableSelection selectstart.ui-disableSelection",
  113. function( event ) {
  114. event.preventDefault();
  115. });
  116. },
  117. enableSelection: function() {
  118. return this.unbind( ".ui-disableSelection" );
  119. }
  120. });
  121. $.each( [ "Width", "Height" ], function( i, name ) {
  122. var side = name === "Width" ? [ "Left", "Right" ] : [ "Top", "Bottom" ],
  123. type = name.toLowerCase(),
  124. orig = {
  125. innerWidth: $.fn.innerWidth,
  126. innerHeight: $.fn.innerHeight,
  127. outerWidth: $.fn.outerWidth,
  128. outerHeight: $.fn.outerHeight
  129. };
  130. function reduce( elem, size, border, margin ) {
  131. $.each( side, function() {
  132. size -= parseFloat( $.curCSS( elem, "padding" + this, true) ) || 0;
  133. if ( border ) {
  134. size -= parseFloat( $.curCSS( elem, "border" + this + "Width", true) ) || 0;
  135. }
  136. if ( margin ) {
  137. size -= parseFloat( $.curCSS( elem, "margin" + this, true) ) || 0;
  138. }
  139. });
  140. return size;
  141. }
  142. $.fn[ "inner" + name ] = function( size ) {
  143. if ( size === undefined ) {
  144. return orig[ "inner" + name ].call( this );
  145. }
  146. return this.each(function() {
  147. $.style( this, type, reduce( this, size ) + "px" );
  148. });
  149. };
  150. $.fn[ "outer" + name] = function( size, margin ) {
  151. if ( typeof size !== "number" ) {
  152. return orig[ "outer" + name ].call( this, size );
  153. }
  154. return this.each(function() {
  155. $.style( this, type, reduce( this, size, true, margin ) + "px" );
  156. });
  157. };
  158. });
  159. // selectors
  160. function visible( element ) {
  161. return !$( element ).parents().andSelf().filter(function() {
  162. return $.curCSS( this, "visibility" ) === "hidden" ||
  163. $.expr.filters.hidden( this );
  164. }).length;
  165. }
  166. $.extend( $.expr[ ":" ], {
  167. data: function( elem, i, match ) {
  168. return !!$.data( elem, match[ 3 ] );
  169. },
  170. focusable: function( element ) {
  171. var nodeName = element.nodeName.toLowerCase(),
  172. tabIndex = $.attr( element, "tabindex" );
  173. if ( "area" === nodeName ) {
  174. var map = element.parentNode,
  175. mapName = map.name,
  176. img;
  177. if ( !element.href || !mapName || map.nodeName.toLowerCase() !== "map" ) {
  178. return false;
  179. }
  180. img = $( "img[usemap=#" + mapName + "]" )[0];
  181. return !!img && visible( img );
  182. }
  183. return ( /input|select|textarea|button|object/.test( nodeName )
  184. ? !element.disabled
  185. : "a" == nodeName
  186. ? element.href || !isNaN( tabIndex )
  187. : !isNaN( tabIndex ))
  188. // the element and all of its ancestors must be visible
  189. && visible( element );
  190. },
  191. tabbable: function( element ) {
  192. var tabIndex = $.attr( element, "tabindex" );
  193. return ( isNaN( tabIndex ) || tabIndex >= 0 ) && $( element ).is( ":focusable" );
  194. }
  195. });
  196. // support
  197. $(function() {
  198. var div = document.createElement( "div" ),
  199. body = document.body;
  200. $.extend( div.style, {
  201. minHeight: "100px",
  202. height: "auto",
  203. padding: 0,
  204. borderWidth: 0
  205. });
  206. $.support.minHeight = body.appendChild( div ).offsetHeight === 100;
  207. // set display to none to avoid a layout bug in IE
  208. // http://dev.jquery.com/ticket/4014
  209. body.removeChild( div ).style.display = "none";
  210. });
  211. // deprecated
  212. $.extend( $.ui, {
  213. // $.ui.plugin is deprecated. Use the proxy pattern instead.
  214. plugin: {
  215. add: function( module, option, set ) {
  216. var proto = $.ui[ module ].prototype;
  217. for ( var i in set ) {
  218. proto.plugins[ i ] = proto.plugins[ i ] || [];
  219. proto.plugins[ i ].push( [ option, set[ i ] ] );
  220. }
  221. },
  222. call: function( instance, name, args ) {
  223. var set = instance.plugins[ name ];
  224. if ( !set || !instance.element[ 0 ].parentNode ) {
  225. return;
  226. }
  227. for ( var i = 0; i < set.length; i++ ) {
  228. if ( instance.options[ set[ i ][ 0 ] ] ) {
  229. set[ i ][ 1 ].apply( instance.element, args );
  230. }
  231. }
  232. }
  233. },
  234. // will be deprecated when we switch to jQuery 1.4 - use jQuery.contains()
  235. contains: function( a, b ) {
  236. return document.compareDocumentPosition ?
  237. a.compareDocumentPosition( b ) & 16 :
  238. a !== b && a.contains( b );
  239. },
  240. // only used by resizable
  241. hasScroll: function( el, a ) {
  242. //If overflow is hidden, the element might have extra content, but the user wants to hide it
  243. if ( $( el ).css( "overflow" ) === "hidden") {
  244. return false;
  245. }
  246. var scroll = ( a && a === "left" ) ? "scrollLeft" : "scrollTop",
  247. has = false;
  248. if ( el[ scroll ] > 0 ) {
  249. return true;
  250. }
  251. // TODO: determine which cases actually cause this to happen
  252. // if the element doesn't have the scroll set, see if it's possible to
  253. // set the scroll
  254. el[ scroll ] = 1;
  255. has = ( el[ scroll ] > 0 );
  256. el[ scroll ] = 0;
  257. return has;
  258. },
  259. // these are odd functions, fix the API or move into individual plugins
  260. isOverAxis: function( x, reference, size ) {
  261. //Determines when x coordinate is over "b" element axis
  262. return ( x > reference ) && ( x < ( reference + size ) );
  263. },
  264. isOver: function( y, x, top, left, height, width ) {
  265. //Determines when x, y coordinates is over "b" element
  266. return $.ui.isOverAxis( y, top, height ) && $.ui.isOverAxis( x, left, width );
  267. }
  268. });
  269. })( jQuery );
  270. /*!
  271. * jQuery UI Widget 1.8.5
  272. *
  273. * Copyright 2010, AUTHORS.txt (http://jqueryui.com/about)
  274. * Dual licensed under the MIT or GPL Version 2 licenses.
  275. * http://jquery.org/license
  276. *
  277. * http://docs.jquery.com/UI/Widget
  278. */
  279. (function( $, undefined ) {
  280. // jQuery 1.4+
  281. if ( $.cleanData ) {
  282. var _cleanData = $.cleanData;
  283. $.cleanData = function( elems ) {
  284. for ( var i = 0, elem; (elem = elems[i]) != null; i++ ) {
  285. $( elem ).triggerHandler( "remove" );
  286. }
  287. _cleanData( elems );
  288. };
  289. } else {
  290. var _remove = $.fn.remove;
  291. $.fn.remove = function( selector, keepData ) {
  292. return this.each(function() {
  293. if ( !keepData ) {
  294. if ( !selector || $.filter( selector, [ this ] ).length ) {
  295. $( "*", this ).add( [ this ] ).each(function() {
  296. $( this ).triggerHandler( "remove" );
  297. });
  298. }
  299. }
  300. return _remove.call( $(this), selector, keepData );
  301. });
  302. };
  303. }
  304. $.widget = function( name, base, prototype ) {
  305. var namespace = name.split( "." )[ 0 ],
  306. fullName;
  307. name = name.split( "." )[ 1 ];
  308. fullName = namespace + "-" + name;
  309. if ( !prototype ) {
  310. prototype = base;
  311. base = $.Widget;
  312. }
  313. // create selector for plugin
  314. $.expr[ ":" ][ fullName ] = function( elem ) {
  315. return !!$.data( elem, name );
  316. };
  317. $[ namespace ] = $[ namespace ] || {};
  318. $[ namespace ][ name ] = function( options, element ) {
  319. // allow instantiation without initializing for simple inheritance
  320. if ( arguments.length ) {
  321. this._createWidget( options, element );
  322. }
  323. };
  324. var basePrototype = new base();
  325. // we need to make the options hash a property directly on the new instance
  326. // otherwise we'll modify the options hash on the prototype that we're
  327. // inheriting from
  328. // $.each( basePrototype, function( key, val ) {
  329. // if ( $.isPlainObject(val) ) {
  330. // basePrototype[ key ] = $.extend( {}, val );
  331. // }
  332. // });
  333. basePrototype.options = $.extend( true, {}, basePrototype.options );
  334. $[ namespace ][ name ].prototype = $.extend( true, basePrototype, {
  335. namespace: namespace,
  336. widgetName: name,
  337. widgetEventPrefix: $[ namespace ][ name ].prototype.widgetEventPrefix || name,
  338. widgetBaseClass: fullName
  339. }, prototype );
  340. $.widget.bridge( name, $[ namespace ][ name ] );
  341. };
  342. $.widget.bridge = function( name, object ) {
  343. $.fn[ name ] = function( options ) {
  344. var isMethodCall = typeof options === "string",
  345. args = Array.prototype.slice.call( arguments, 1 ),
  346. returnValue = this;
  347. // allow multiple hashes to be passed on init
  348. options = !isMethodCall && args.length ?
  349. $.extend.apply( null, [ true, options ].concat(args) ) :
  350. options;
  351. // prevent calls to internal methods
  352. if ( isMethodCall && options.substring( 0, 1 ) === "_" ) {
  353. return returnValue;
  354. }
  355. if ( isMethodCall ) {
  356. this.each(function() {
  357. var instance = $.data( this, name );
  358. if ( !instance ) {
  359. throw "cannot call methods on " + name + " prior to initialization; " +
  360. "attempted to call method '" + options + "'";
  361. }
  362. if ( !$.isFunction( instance[options] ) ) {
  363. throw "no such method '" + options + "' for " + name + " widget instance";
  364. }
  365. var methodValue = instance[ options ].apply( instance, args );
  366. if ( methodValue !== instance && methodValue !== undefined ) {
  367. returnValue = methodValue;
  368. return false;
  369. }
  370. });
  371. } else {
  372. this.each(function() {
  373. var instance = $.data( this, name );
  374. if ( instance ) {
  375. instance.option( options || {} )._init();
  376. } else {
  377. $.data( this, name, new object( options, this ) );
  378. }
  379. });
  380. }
  381. return returnValue;
  382. };
  383. };
  384. $.Widget = function( options, element ) {
  385. // allow instantiation without initializing for simple inheritance
  386. if ( arguments.length ) {
  387. this._createWidget( options, element );
  388. }
  389. };
  390. $.Widget.prototype = {
  391. widgetName: "widget",
  392. widgetEventPrefix: "",
  393. options: {
  394. disabled: false
  395. },
  396. _createWidget: function( options, element ) {
  397. // $.widget.bridge stores the plugin instance, but we do it anyway
  398. // so that it's stored even before the _create function runs
  399. $.data( element, this.widgetName, this );
  400. this.element = $( element );
  401. this.options = $.extend( true, {},
  402. this.options,
  403. $.metadata && $.metadata.get( element )[ this.widgetName ],
  404. options );
  405. var self = this;
  406. this.element.bind( "remove." + this.widgetName, function() {
  407. self.destroy();
  408. });
  409. this._create();
  410. this._init();
  411. },
  412. _create: function() {},
  413. _init: function() {},
  414. destroy: function() {
  415. this.element
  416. .unbind( "." + this.widgetName )
  417. .removeData( this.widgetName );
  418. this.widget()
  419. .unbind( "." + this.widgetName )
  420. .removeAttr( "aria-disabled" )
  421. .removeClass(
  422. this.widgetBaseClass + "-disabled " +
  423. "ui-state-disabled" );
  424. },
  425. widget: function() {
  426. return this.element;
  427. },
  428. option: function( key, value ) {
  429. var options = key,
  430. self = this;
  431. if ( arguments.length === 0 ) {
  432. // don't return a reference to the internal hash
  433. return $.extend( {}, self.options );
  434. }
  435. if (typeof key === "string" ) {
  436. if ( value === undefined ) {
  437. return this.options[ key ];
  438. }
  439. options = {};
  440. options[ key ] = value;
  441. }
  442. $.each( options, function( key, value ) {
  443. self._setOption( key, value );
  444. });
  445. return self;
  446. },
  447. _setOption: function( key, value ) {
  448. this.options[ key ] = value;
  449. if ( key === "disabled" ) {
  450. this.widget()
  451. [ value ? "addClass" : "removeClass"](
  452. this.widgetBaseClass + "-disabled" + " " +
  453. "ui-state-disabled" )
  454. .attr( "aria-disabled", value );
  455. }
  456. return this;
  457. },
  458. enable: function() {
  459. return this._setOption( "disabled", false );
  460. },
  461. disable: function() {
  462. return this._setOption( "disabled", true );
  463. },
  464. _trigger: function( type, event, data ) {
  465. var callback = this.options[ type ];
  466. event = $.Event( event );
  467. event.type = ( type === this.widgetEventPrefix ?
  468. type :
  469. this.widgetEventPrefix + type ).toLowerCase();
  470. data = data || {};
  471. // copy original event properties over to the new event
  472. // this would happen if we could call $.event.fix instead of $.Event
  473. // but we don't have a way to force an event to be fixed multiple times
  474. if ( event.originalEvent ) {
  475. for ( var i = $.event.props.length, prop; i; ) {
  476. prop = $.event.props[ --i ];
  477. event[ prop ] = event.originalEvent[ prop ];
  478. }
  479. }
  480. this.element.trigger( event, data );
  481. return !( $.isFunction(callback) &&
  482. callback.call( this.element[0], event, data ) === false ||
  483. event.isDefaultPrevented() );
  484. }
  485. };
  486. })( jQuery );
  487. /*!
  488. * jQuery UI Mouse 1.8.5
  489. *
  490. * Copyright 2010, AUTHORS.txt (http://jqueryui.com/about)
  491. * Dual licensed under the MIT or GPL Version 2 licenses.
  492. * http://jquery.org/license
  493. *
  494. * http://docs.jquery.com/UI/Mouse
  495. *
  496. * Depends:
  497. * jquery.ui.widget.js
  498. */
  499. (function( $, undefined ) {
  500. $.widget("ui.mouse", {
  501. options: {
  502. cancel: ':input,option',
  503. distance: 1,
  504. delay: 0
  505. },
  506. _mouseInit: function() {
  507. var self = this;
  508. this.element
  509. .bind('mousedown.'+this.widgetName, function(event) {
  510. return self._mouseDown(event);
  511. })
  512. .bind('click.'+this.widgetName, function(event) {
  513. if(self._preventClickEvent) {
  514. self._preventClickEvent = false;
  515. event.stopImmediatePropagation();
  516. return false;
  517. }
  518. });
  519. this.started = false;
  520. },
  521. // TODO: make sure destroying one instance of mouse doesn't mess with
  522. // other instances of mouse
  523. _mouseDestroy: function() {
  524. this.element.unbind('.'+this.widgetName);
  525. },
  526. _mouseDown: function(event) {
  527. // don't let more than one widget handle mouseStart
  528. // TODO: figure out why we have to use originalEvent
  529. event.originalEvent = event.originalEvent || {};
  530. if (event.originalEvent.mouseHandled) { return; }
  531. // we may have missed mouseup (out of window)
  532. (this._mouseStarted && this._mouseUp(event));
  533. this._mouseDownEvent = event;
  534. var self = this,
  535. btnIsLeft = (event.which == 1),
  536. elIsCancel = (typeof this.options.cancel == "string" ? $(event.target).parents().add(event.target).filter(this.options.cancel).length : false);
  537. if (!btnIsLeft || elIsCancel || !this._mouseCapture(event)) {
  538. return true;
  539. }
  540. this.mouseDelayMet = !this.options.delay;
  541. if (!this.mouseDelayMet) {
  542. this._mouseDelayTimer = setTimeout(function() {
  543. self.mouseDelayMet = true;
  544. }, this.options.delay);
  545. }
  546. if (this._mouseDistanceMet(event) && this._mouseDelayMet(event)) {
  547. this._mouseStarted = (this._mouseStart(event) !== false);
  548. if (!this._mouseStarted) {
  549. event.preventDefault();
  550. return true;
  551. }
  552. }
  553. // these delegates are required to keep context
  554. this._mouseMoveDelegate = function(event) {
  555. return self._mouseMove(event);
  556. };
  557. this._mouseUpDelegate = function(event) {
  558. return self._mouseUp(event);
  559. };
  560. $(document)
  561. .bind('mousemove.'+this.widgetName, this._mouseMoveDelegate)
  562. .bind('mouseup.'+this.widgetName, this._mouseUpDelegate);
  563. // preventDefault() is used to prevent the selection of text here -
  564. // however, in Safari, this causes select boxes not to be selectable
  565. // anymore, so this fix is needed
  566. ($.browser.safari || event.preventDefault());
  567. event.originalEvent.mouseHandled = true;
  568. return true;
  569. },
  570. _mouseMove: function(event) {
  571. // IE mouseup check - mouseup happened when mouse was out of window
  572. if ($.browser.msie && !event.button) {
  573. return this._mouseUp(event);
  574. }
  575. if (this._mouseStarted) {
  576. this._mouseDrag(event);
  577. return event.preventDefault();
  578. }
  579. if (this._mouseDistanceMet(event) && this._mouseDelayMet(event)) {
  580. this._mouseStarted =
  581. (this._mouseStart(this._mouseDownEvent, event) !== false);
  582. (this._mouseStarted ? this._mouseDrag(event) : this._mouseUp(event));
  583. }
  584. return !this._mouseStarted;
  585. },
  586. _mouseUp: function(event) {
  587. $(document)
  588. .unbind('mousemove.'+this.widgetName, this._mouseMoveDelegate)
  589. .unbind('mouseup.'+this.widgetName, this._mouseUpDelegate);
  590. if (this._mouseStarted) {
  591. this._mouseStarted = false;
  592. this._preventClickEvent = (event.target == this._mouseDownEvent.target);
  593. this._mouseStop(event);
  594. }
  595. return false;
  596. },
  597. _mouseDistanceMet: function(event) {
  598. return (Math.max(
  599. Math.abs(this._mouseDownEvent.pageX - event.pageX),
  600. Math.abs(this._mouseDownEvent.pageY - event.pageY)
  601. ) >= this.options.distance
  602. );
  603. },
  604. _mouseDelayMet: function(event) {
  605. return this.mouseDelayMet;
  606. },
  607. // These are placeholder methods, to be overriden by extending plugin
  608. _mouseStart: function(event) {},
  609. _mouseDrag: function(event) {},
  610. _mouseStop: function(event) {},
  611. _mouseCapture: function(event) { return true; }
  612. });
  613. })(jQuery);
  614. /*
  615. * jQuery UI Sortable 1.8.5
  616. *
  617. * Copyright 2010, AUTHORS.txt (http://jqueryui.com/about)
  618. * Dual licensed under the MIT or GPL Version 2 licenses.
  619. * http://jquery.org/license
  620. *
  621. * http://docs.jquery.com/UI/Sortables
  622. *
  623. * Depends:
  624. * jquery.ui.core.js
  625. * jquery.ui.mouse.js
  626. * jquery.ui.widget.js
  627. */
  628. (function( $, undefined ) {
  629. $.widget("ui.sortable", $.ui.mouse, {
  630. widgetEventPrefix: "sort",
  631. options: {
  632. appendTo: "parent",
  633. axis: false,
  634. connectWith: false,
  635. containment: false,
  636. cursor: 'auto',
  637. cursorAt: false,
  638. dropOnEmpty: true,
  639. forcePlaceholderSize: false,
  640. forceHelperSize: false,
  641. grid: false,
  642. handle: false,
  643. helper: "original",
  644. items: '> *',
  645. opacity: false,
  646. placeholder: false,
  647. revert: false,
  648. scroll: true,
  649. scrollSensitivity: 20,
  650. scrollSpeed: 20,
  651. scope: "default",
  652. tolerance: "intersect",
  653. zIndex: 1000
  654. },
  655. _create: function() {
  656. var o = this.options;
  657. this.containerCache = {};
  658. this.element.addClass("ui-sortable");
  659. //Get the items
  660. this.refresh();
  661. //Let's determine if the items are floating
  662. this.floating = this.items.length ? (/left|right/).test(this.items[0].item.css('float')) : false;
  663. //Let's determine the parent's offset
  664. this.offset = this.element.offset();
  665. //Initialize mouse events for interaction
  666. this._mouseInit();
  667. },
  668. destroy: function() {
  669. this.element
  670. .removeClass("ui-sortable ui-sortable-disabled")
  671. .removeData("sortable")
  672. .unbind(".sortable");
  673. this._mouseDestroy();
  674. for ( var i = this.items.length - 1; i >= 0; i-- )
  675. this.items[i].item.removeData("sortable-item");
  676. return this;
  677. },
  678. _setOption: function(key, value){
  679. if ( key === "disabled" ) {
  680. this.options[ key ] = value;
  681. this.widget()
  682. [ value ? "addClass" : "removeClass"]( "ui-sortable-disabled" );
  683. } else {
  684. // Don't call widget base _setOption for disable as it adds ui-state-disabled class
  685. $.Widget.prototype._setOption.apply(this, arguments);
  686. }
  687. },
  688. _mouseCapture: function(event, overrideHandle) {
  689. if (this.reverting) {
  690. return false;
  691. }
  692. if(this.options.disabled || this.options.type == 'static') return false;
  693. //We have to refresh the items data once first
  694. this._refreshItems(event);
  695. //Find out if the clicked node (or one of its parents) is a actual item in this.items
  696. var currentItem = null, self = this, nodes = $(event.target).parents().each(function() {
  697. if($.data(this, 'sortable-item') == self) {
  698. currentItem = $(this);
  699. return false;
  700. }
  701. });
  702. if($.data(event.target, 'sortable-item') == self) currentItem = $(event.target);
  703. if(!currentItem) return false;
  704. if(this.options.handle && !overrideHandle) {
  705. var validHandle = false;
  706. $(this.options.handle, currentItem).find("*").andSelf().each(function() { if(this == event.target) validHandle = true; });
  707. if(!validHandle) return false;
  708. }
  709. this.currentItem = currentItem;
  710. this._removeCurrentsFromItems();
  711. return true;
  712. },
  713. _mouseStart: function(event, overrideHandle, noActivation) {
  714. var o = this.options, self = this;
  715. this.currentContainer = this;
  716. //We only need to call refreshPositions, because the refreshItems call has been moved to mouseCapture
  717. this.refreshPositions();
  718. //Create and append the visible helper
  719. this.helper = this._createHelper(event);
  720. //Cache the helper size
  721. this._cacheHelperProportions();
  722. /*
  723. * - Position generation -
  724. * This block generates everything position related - it's the core of draggables.
  725. */
  726. //Cache the margins of the original element
  727. this._cacheMargins();
  728. //Get the next scrolling parent
  729. this.scrollParent = this.helper.scrollParent();
  730. //The element's absolute position on the page minus margins
  731. this.offset = this.currentItem.offset();
  732. this.offset = {
  733. top: this.offset.top - this.margins.top,
  734. left: this.offset.left - this.margins.left
  735. };
  736. // Only after we got the offset, we can change the helper's position to absolute
  737. // TODO: Still need to figure out a way to make relative sorting possible
  738. this.helper.css("position", "absolute");
  739. this.cssPosition = this.helper.css("position");
  740. $.extend(this.offset, {
  741. click: { //Where the click happened, relative to the element
  742. left: event.pageX - this.offset.left,
  743. top: event.pageY - this.offset.top
  744. },
  745. parent: this._getParentOffset(),
  746. relative: this._getRelativeOffset() //This is a relative to absolute position minus the actual position calculation - only used for relative positioned helper
  747. });
  748. //Generate the original position
  749. this.originalPosition = this._generatePosition(event);
  750. this.originalPageX = event.pageX;
  751. this.originalPageY = event.pageY;
  752. //Adjust the mouse offset relative to the helper if 'cursorAt' is supplied
  753. (o.cursorAt && this._adjustOffsetFromHelper(o.cursorAt));
  754. //Cache the former DOM position
  755. this.domPosition = { prev: this.currentItem.prev()[0], parent: this.currentItem.parent()[0] };
  756. //If the helper is not the original, hide the original so it's not playing any role during the drag, won't cause anything bad this way
  757. if(this.helper[0] != this.currentItem[0]) {
  758. this.currentItem.hide();
  759. }
  760. //Create the placeholder
  761. this._createPlaceholder();
  762. //Set a containment if given in the options
  763. if(o.containment)
  764. this._setContainment();
  765. if(o.cursor) { // cursor option
  766. if ($('body').css("cursor")) this._storedCursor = $('body').css("cursor");
  767. $('body').css("cursor", o.cursor);
  768. }
  769. if(o.opacity) { // opacity option
  770. if (this.helper.css("opacity")) this._storedOpacity = this.helper.css("opacity");
  771. this.helper.css("opacity", o.opacity);
  772. }
  773. if(o.zIndex) { // zIndex option
  774. if (this.helper.css("zIndex")) this._storedZIndex = this.helper.css("zIndex");
  775. this.helper.css("zIndex", o.zIndex);
  776. }
  777. //Prepare scrolling
  778. if(this.scrollParent[0] != document && this.scrollParent[0].tagName != 'HTML')
  779. this.overflowOffset = this.scrollParent.offset();
  780. //Call callbacks
  781. this._trigger("start", event, this._uiHash());
  782. //Recache the helper size
  783. if(!this._preserveHelperProportions)
  784. this._cacheHelperProportions();
  785. //Post 'activate' events to possible containers
  786. if(!noActivation) {
  787. for (var i = this.containers.length - 1; i >= 0; i--) { this.containers[i]._trigger("activate", event, self._uiHash(this)); }
  788. }
  789. //Prepare possible droppables
  790. if($.ui.ddmanager)
  791. $.ui.ddmanager.current = this;
  792. if ($.ui.ddmanager && !o.dropBehaviour)
  793. $.ui.ddmanager.prepareOffsets(this, event);
  794. this.dragging = true;
  795. this.helper.addClass("ui-sortable-helper");
  796. this._mouseDrag(event); //Execute the drag once - this causes the helper not to be visible before getting its correct position
  797. return true;
  798. },
  799. _mouseDrag: function(event) {
  800. //Compute the helpers position
  801. this.position = this._generatePosition(event);
  802. this.positionAbs = this._convertPositionTo("absolute");
  803. if (!this.lastPositionAbs) {
  804. this.lastPositionAbs = this.positionAbs;
  805. }
  806. //Do scrolling
  807. if(this.options.scroll) {
  808. var o = this.options, scrolled = false;
  809. if(this.scrollParent[0] != document && this.scrollParent[0].tagName != 'HTML') {
  810. if((this.overflowOffset.top + this.scrollParent[0].offsetHeight) - event.pageY < o.scrollSensitivity)
  811. this.scrollParent[0].scrollTop = scrolled = this.scrollParent[0].scrollTop + o.scrollSpeed;
  812. else if(event.pageY - this.overflowOffset.top < o.scrollSensitivity)
  813. this.scrollParent[0].scrollTop = scrolled = this.scrollParent[0].scrollTop - o.scrollSpeed;
  814. if((this.overflowOffset.left + this.scrollParent[0].offsetWidth) - event.pageX < o.scrollSensitivity)
  815. this.scrollParent[0].scrollLeft = scrolled = this.scrollParent[0].scrollLeft + o.scrollSpeed;
  816. else if(event.pageX - this.overflowOffset.left < o.scrollSensitivity)
  817. this.scrollParent[0].scrollLeft = scrolled = this.scrollParent[0].scrollLeft - o.scrollSpeed;
  818. } else {
  819. if(event.pageY - $(document).scrollTop() < o.scrollSensitivity)
  820. scrolled = $(document).scrollTop($(document).scrollTop() - o.scrollSpeed);
  821. else if($(window).height() - (event.pageY - $(document).scrollTop()) < o.scrollSensitivity)
  822. scrolled = $(document).scrollTop($(document).scrollTop() + o.scrollSpeed);
  823. if(event.pageX - $(document).scrollLeft() < o.scrollSensitivity)
  824. scrolled = $(document).scrollLeft($(document).scrollLeft() - o.scrollSpeed);
  825. else if($(window).width() - (event.pageX - $(document).scrollLeft()) < o.scrollSensitivity)
  826. scrolled = $(document).scrollLeft($(document).scrollLeft() + o.scrollSpeed);
  827. }
  828. if(scrolled !== false && $.ui.ddmanager && !o.dropBehaviour)
  829. $.ui.ddmanager.prepareOffsets(this, event);
  830. }
  831. //Regenerate the absolute position used for position checks
  832. this.positionAbs = this._convertPositionTo("absolute");
  833. //Set the helper position
  834. if(!this.options.axis || this.options.axis != "y") this.helper[0].style.left = this.position.left+'px';
  835. if(!this.options.axis || this.options.axis != "x") this.helper[0].style.top = this.position.top+'px';
  836. //Rearrange
  837. for (var i = this.items.length - 1; i >= 0; i--) {
  838. //Cache variables and intersection, continue if no intersection
  839. var item = this.items[i], itemElement = item.item[0], intersection = this._intersectsWithPointer(item);
  840. if (!intersection) continue;
  841. if(itemElement != this.currentItem[0] //cannot intersect with itself
  842. && this.placeholder[intersection == 1 ? "next" : "prev"]()[0] != itemElement //no useless actions that have been done before
  843. && !$.ui.contains(this.placeholder[0], itemElement) //no action if the item moved is the parent of the item checked
  844. && (this.options.type == 'semi-dynamic' ? !$.ui.contains(this.element[0], itemElement) : true)
  845. //&& itemElement.parentNode == this.placeholder[0].parentNode // only rearrange items within the same container
  846. ) {
  847. this.direction = intersection == 1 ? "down" : "up";
  848. if (this.options.tolerance == "pointer" || this._intersectsWithSides(item)) {
  849. this._rearrange(event, item);
  850. } else {
  851. break;
  852. }
  853. this._trigger("change", event, this._uiHash());
  854. break;
  855. }
  856. }
  857. //Post events to containers
  858. this._contactContainers(event);
  859. //Interconnect with droppables
  860. if($.ui.ddmanager) $.ui.ddmanager.drag(this, event);
  861. //Call callbacks
  862. this._trigger('sort', event, this._uiHash());
  863. this.lastPositionAbs = this.positionAbs;
  864. return false;
  865. },
  866. _mouseStop: function(event, noPropagation) {
  867. if(!event) return;
  868. //If we are using droppables, inform the manager about the drop
  869. if ($.ui.ddmanager && !this.options.dropBehaviour)
  870. $.ui.ddmanager.drop(this, event);
  871. if(this.options.revert) {
  872. var self = this;
  873. var cur = self.placeholder.offset();
  874. self.reverting = true;
  875. $(this.helper).animate({
  876. left: cur.left - this.offset.parent.left - self.margins.left + (this.offsetParent[0] == document.body ? 0 : this.offsetParent[0].scrollLeft),
  877. top: cur.top - this.offset.parent.top - self.margins.top + (this.offsetParent[0] == document.body ? 0 : this.offsetParent[0].scrollTop)
  878. }, parseInt(this.options.revert, 10) || 500, function() {
  879. self._clear(event);
  880. });
  881. } else {
  882. this._clear(event, noPropagation);
  883. }
  884. return false;
  885. },
  886. cancel: function() {
  887. var self = this;
  888. if(this.dragging) {
  889. this._mouseUp();
  890. if(this.options.helper == "original")
  891. this.currentItem.css(this._storedCSS).removeClass("ui-sortable-helper");
  892. else
  893. this.currentItem.show();
  894. //Post deactivating events to containers
  895. for (var i = this.containers.length - 1; i >= 0; i--){
  896. this.containers[i]._trigger("deactivate", null, self._uiHash(this));
  897. if(this.containers[i].containerCache.over) {
  898. this.containers[i]._trigger("out", null, self._uiHash(this));
  899. this.containers[i].containerCache.over = 0;
  900. }
  901. }
  902. }
  903. //$(this.placeholder[0]).remove(); would have been the jQuery way - unfortunately, it unbinds ALL events from the original node!
  904. if(this.placeholder[0].parentNode) this.placeholder[0].parentNode.removeChild(this.placeholder[0]);
  905. if(this.options.helper != "original" && this.helper && this.helper[0].parentNode) this.helper.remove();
  906. $.extend(this, {
  907. helper: null,
  908. dragging: false,
  909. reverting: false,
  910. _noFinalSort: null
  911. });
  912. if(this.domPosition.prev) {
  913. $(this.domPosition.prev).after(this.currentItem);
  914. } else {
  915. $(this.domPosition.parent).prepend(this.currentItem);
  916. }
  917. return this;
  918. },
  919. serialize: function(o) {
  920. var items = this._getItemsAsjQuery(o && o.connected);
  921. var str = []; o = o || {};
  922. $(items).each(function() {
  923. var res = ($(o.item || this).attr(o.attribute || 'id') || '').match(o.expression || (/(.+)[-=_](.+)/));
  924. if(res) str.push((o.key || res[1]+'[]')+'='+(o.key && o.expression ? res[1] : res[2]));
  925. });
  926. if(!str.length && o.key) {
  927. str.push(o.key + '=');
  928. }
  929. return str.join('&');
  930. },
  931. toArray: function(o) {
  932. var items = this._getItemsAsjQuery(o && o.connected);
  933. var ret = []; o = o || {};
  934. items.each(function() { ret.push($(o.item || this).attr(o.attribute || 'id') || ''); });
  935. return ret;
  936. },
  937. /* Be careful with the following core functions */
  938. _intersectsWith: function(item) {
  939. var x1 = this.positionAbs.left,
  940. x2 = x1 + this.helperProportions.width,
  941. y1 = this.positionAbs.top,
  942. y2 = y1 + this.helperProportions.height;
  943. var l = item.left,
  944. r = l + item.width,
  945. t = item.top,
  946. b = t + item.height;
  947. var dyClick = this.offset.click.top,
  948. dxClick = this.offset.click.left;
  949. var isOverElement = (y1 + dyClick) > t && (y1 + dyClick) < b && (x1 + dxClick) > l && (x1 + dxClick) < r;
  950. if( this.options.tolerance == "pointer"
  951. || this.options.forcePointerForContainers
  952. || (this.options.tolerance != "pointer" && this.helperProportions[this.floating ? 'width' : 'height'] > item[this.floating ? 'width' : 'height'])
  953. ) {
  954. return isOverElement;
  955. } else {
  956. return (l < x1 + (this.helperProportions.width / 2) // Right Half
  957. && x2 - (this.helperProportions.width / 2) < r // Left Half
  958. && t < y1 + (this.helperProportions.height / 2) // Bottom Half
  959. && y2 - (this.helperProportions.height / 2) < b ); // Top Half
  960. }
  961. },
  962. _intersectsWithPointer: function(item) {
  963. var isOverElementHeight = $.ui.isOverAxis(this.positionAbs.top + this.offset.click.top, item.top, item.height),
  964. isOverElementWidth = $.ui.isOverAxis(this.positionAbs.left + this.offset.click.left, item.left, item.width),
  965. isOverElement = isOverElementHeight && isOverElementWidth,
  966. verticalDirection = this._getDragVerticalDirection(),
  967. horizontalDirection = this._getDragHorizontalDirection();
  968. if (!isOverElement)
  969. return false;
  970. return this.floating ?
  971. ( ((horizontalDirection && horizontalDirection == "right") || verticalDirection == "down") ? 2 : 1 )
  972. : ( verticalDirection && (verticalDirection == "down" ? 2 : 1) );
  973. },
  974. _intersectsWithSides: function(item) {
  975. var isOverBottomHalf = $.ui.isOverAxis(this.positionAbs.top + this.offset.click.top, item.top + (item.height/2), item.height),
  976. isOverRightHalf = $.ui.isOverAxis(this.positionAbs.left + this.offset.click.left, item.left + (item.width/2), item.width),
  977. verticalDirection = this._getDragVerticalDirection(),
  978. horizontalDirection = this._getDragHorizontalDirection();
  979. if (this.floating && horizontalDirection) {
  980. return ((horizontalDirection == "right" && isOverRightHalf) || (horizontalDirection == "left" && !isOverRightHalf));
  981. } else {
  982. return verticalDirection && ((verticalDirection == "down" && isOverBottomHalf) || (verticalDirection == "up" && !isOverBottomHalf));
  983. }
  984. },
  985. _getDragVerticalDirection: function() {
  986. var delta = this.positionAbs.top - this.lastPositionAbs.top;
  987. return delta != 0 && (delta > 0 ? "down" : "up");
  988. },
  989. _getDragHorizontalDirection: function() {
  990. var delta = this.positionAbs.left - this.lastPositionAbs.left;
  991. return delta != 0 && (delta > 0 ? "right" : "left");
  992. },
  993. refresh: function(event) {
  994. this._refreshItems(event);
  995. this.refreshPositions();
  996. return this;
  997. },
  998. _connectWith: function() {
  999. var options = this.options;
  1000. return options.connectWith.constructor == String
  1001. ? [options.connectWith]
  1002. : options.connectWith;
  1003. },
  1004. _getItemsAsjQuery: function(connected) {
  1005. var self = this;
  1006. var items = [];
  1007. var queries = [];
  1008. var connectWith = this._connectWith();
  1009. if(connectWith && connected) {
  1010. for (var i = connectWith.length - 1; i >= 0; i--){
  1011. var cur = $(connectWith[i]);
  1012. for (var j = cur.length - 1; j >= 0; j--){
  1013. var inst = $.data(cur[j], 'sortable');
  1014. if(inst && inst != this && !inst.options.disabled) {
  1015. queries.push([$.isFunction(inst.options.items) ? inst.options.items.call(inst.element) : $(inst.options.items, inst.element).not(".ui-sortable-helper").not('.ui-sortable-placeholder'), inst]);
  1016. }
  1017. };
  1018. };
  1019. }
  1020. queries.push([$.isFunction(this.options.items) ? this.options.items.call(this.element, null, { options: this.options, item: this.currentItem }) : $(this.options.items, this.element).not(".ui-sortable-helper").not('.ui-sortable-placeholder'), this]);
  1021. for (var i = queries.length - 1; i >= 0; i--){
  1022. queries[i][0].each(function() {
  1023. items.push(this);
  1024. });
  1025. };
  1026. return $(items);
  1027. },
  1028. _removeCurrentsFromItems: function() {
  1029. var list = this.currentItem.find(":data(sortable-item)");
  1030. for (var i=0; i < this.items.length; i++) {
  1031. for (var j=0; j < list.length; j++) {
  1032. if(list[j] == this.items[i].item[0])
  1033. this.items.splice(i,1);
  1034. };
  1035. };
  1036. },
  1037. _refreshItems: function(event) {
  1038. this.items = [];
  1039. this.containers = [this];
  1040. var items = this.items;
  1041. var self = this;
  1042. var queries = [[$.isFunction(this.options.items) ? this.options.items.call(this.element[0], event, { item: this.currentItem }) : $(this.options.items, this.element), this]];
  1043. var connectWith = this._connectWith();
  1044. if(connectWith) {
  1045. for (var i = connectWith.length - 1; i >= 0; i--){
  1046. var cur = $(connectWith[i]);
  1047. for (var j = cur.length - 1; j >= 0; j--){
  1048. var inst = $.data(cur[j], 'sortable');
  1049. if(inst && inst != this && !inst.options.disabled) {
  1050. queries.push([$.isFunction(inst.options.items) ? inst.options.items.call(inst.element[0], event, { item: this.currentItem }) : $(inst.options.items, inst.element), inst]);
  1051. this.containers.push(inst);
  1052. }
  1053. };
  1054. };
  1055. }
  1056. for (var i = queries.length - 1; i >= 0; i--) {
  1057. var targetData = queries[i][1];
  1058. var _queries = queries[i][0];
  1059. for (var j=0, queriesLength = _queries.length; j < queriesLength; j++) {
  1060. var item = $(_queries[j]);
  1061. item.data('sortable-item', targetData); // Data for target checking (mouse manager)
  1062. items.push({
  1063. item: item,
  1064. instance: targetData,
  1065. width: 0, height: 0,
  1066. left: 0, top: 0
  1067. });
  1068. };
  1069. };
  1070. },
  1071. refreshPositions: function(fast) {
  1072. //This has to be redone because due to the item being moved out/into the offsetParent, the offsetParent's position will change
  1073. if(this.offsetParent && this.helper) {
  1074. this.offset.parent = this._getParentOffset();
  1075. }
  1076. for (var i = this.items.length - 1; i >= 0; i--){
  1077. var item = this.items[i];
  1078. var t = this.options.toleranceElement ? $(this.options.toleranceElement, item.item) : item.item;
  1079. if (!fast) {
  1080. item.width = t.outerWidth();
  1081. item.height = t.outerHeight();
  1082. }
  1083. var p = t.offset();
  1084. item.left = p.left;
  1085. item.top = p.top;
  1086. };
  1087. if(this.options.custom && this.options.custom.refreshContainers) {
  1088. this.options.custom.refreshContainers.call(this);
  1089. } else {
  1090. for (var i = this.containers.length - 1; i >= 0; i--){
  1091. var p = this.containers[i].element.offset();
  1092. this.containers[i].containerCache.left = p.left;
  1093. this.containers[i].containerCache.top = p.top;
  1094. this.containers[i].containerCache.width = this.containers[i].element.outerWidth();
  1095. this.containers[i].containerCache.height = this.containers[i].element.outerHeight();
  1096. };
  1097. }
  1098. return this;
  1099. },
  1100. _createPlaceholder: function(that) {
  1101. var self = that || this, o = self.options;
  1102. if(!o.placeholder || o.placeholder.constructor == String) {
  1103. var className = o.placeholder;
  1104. o.placeholder = {
  1105. element: function() {
  1106. var el = $(document.createElement(self.currentItem[0].nodeName))
  1107. .addClass(className || self.currentItem[0].className+" ui-sortable-placeholder")
  1108. .removeClass("ui-sortable-helper")[0];
  1109. if(!className)
  1110. el.style.visibility = "hidden";
  1111. return el;
  1112. },
  1113. update: function(container, p) {
  1114. // 1. If a className is set as 'placeholder option, we don't force sizes - the class is responsible for that
  1115. // 2. The option 'forcePlaceholderSize can be enabled to force it even if a class name is specified
  1116. if(className && !o.forcePlaceholderSize) return;
  1117. //If the element doesn't have a actual height by itself (without styles coming from a stylesheet), it receives the inline height from the dragged item
  1118. if(!p.height()) { p.height(self.currentItem.innerHeight() - parseInt(self.currentItem.css('paddingTop')||0, 10) - parseInt(self.currentItem.css('paddingBottom')||0, 10)); };
  1119. if(!p.width()) { p.width(self.currentItem.innerWidth() - parseInt(self.currentItem.css('paddingLeft')||0, 10) - parseInt(self.currentItem.css('paddingRight')||0, 10)); };
  1120. }
  1121. };
  1122. }
  1123. //Create the placeholder
  1124. self.placeholder = $(o.placeholder.element.call(self.element, self.currentItem));
  1125. //Append it after the actual current item
  1126. self.currentItem.after(self.placeholder);
  1127. //Update the size of the placeholder (TODO: Logic to fuzzy, see line 316/317)
  1128. o.placeholder.update(self, self.placeholder);
  1129. },
  1130. _contactContainers: function(event) {
  1131. // get innermost container that intersects with item
  1132. var innermostContainer = null, innermostIndex = null;
  1133. for (var i = this.containers.length - 1; i >= 0; i--){
  1134. // never consider a container that's located within the item itself
  1135. if($.ui.contains(this.currentItem[0], this.containers[i].element[0]))
  1136. continue;
  1137. if(this._intersectsWith(this.containers[i].containerCache)) {
  1138. // if we've already found a container and it's more "inner" than this, then continue
  1139. if(innermostContainer && $.ui.contains(this.containers[i].element[0], innermostContainer.element[0]))
  1140. continue;
  1141. innermostContainer = this.containers[i];
  1142. innermostIndex = i;
  1143. } else {
  1144. // container doesn't intersect. trigger "out" event if necessary
  1145. if(this.containers[i].containerCache.over) {
  1146. this.containers[i]._trigger("out", event, this._uiHash(this));
  1147. this.containers[i].containerCache.over = 0;
  1148. }
  1149. }
  1150. }
  1151. // if no intersecting containers found, return
  1152. if(!innermostContainer) return;
  1153. // move the item into the container if it's not there already
  1154. if(this.containers.length === 1) {
  1155. this.containers[innermostIndex]._trigger("over", event, this._uiHash(this));
  1156. this.containers[innermostIndex].containerCache.over = 1;
  1157. } else if(this.currentContainer != this.containers[innermostIndex]) {
  1158. //When entering a new container, we will find the item with the least distance and append our item near it
  1159. var dist = 10000; var itemWithLeastDistance = null; var base = this.positionAbs[this.containers[innermostIndex].floating ? 'left' : 'top'];
  1160. for (var j = this.items.length - 1; j >= 0; j--) {
  1161. if(!$.ui.contains(this.containers[innermostIndex].element[0], this.items[j].item[0])) continue;
  1162. var cur = this.items[j][this.containers[innermostIndex].floating ? 'left' : 'top'];
  1163. if(Math.abs(cur - base) < dist) {
  1164. dist = Math.abs(cur - base); itemWithLeastDistance = this.items[j];
  1165. }
  1166. }
  1167. if(!itemWithLeastDistance && !this.options.dropOnEmpty) //Check if dropOnEmpty is enabled
  1168. return;
  1169. this.currentContainer = this.containers[innermostIndex];
  1170. itemWithLeastDistance ? this._rearrange(event, itemWithLeastDistance, null, true) : this._rearrange(event, null, this.containers[innermostIndex].element, true);
  1171. this._trigger("change", event, this._uiHash());
  1172. this.containers[innermostIndex]._trigger("change", event, this._uiHash(this));
  1173. //Update the placeholder
  1174. this.options.placeholder.update(this.currentContainer, this.placeholder);
  1175. this.containers[innermostIndex]._trigger("over", event, this._uiHash(this));
  1176. this.containers[innermostIndex].containerCache.over = 1;
  1177. }
  1178. },
  1179. _createHelper: function(event) {
  1180. var o = this.options;
  1181. var helper = $.isFunction(o.helper) ? $(o.helper.apply(this.element[0], [event, this.currentItem])) : (o.helper == 'clone' ? this.currentItem.clone() : this.currentItem);
  1182. if(!helper.parents('body').length) //Add the helper to the DOM if that didn't happen already
  1183. $(o.appendTo != 'parent' ? o.appendTo : this.currentItem[0].parentNode)[0].appendChild(helper[0]);
  1184. if(helper[0] == this.currentItem[0])
  1185. this._storedCSS = { width: this.currentItem[0].style.width, height: this.currentItem[0].style.height, position: this.currentItem.css("position"), top: this.currentItem.css("top"), left: this.currentItem.css("left") };
  1186. if(helper[0].style.width == '' || o.forceHelperSize) helper.width(this.currentItem.width());
  1187. if(helper[0].style.height == '' || o.forceHelperSize) helper.height(this.currentItem.height());
  1188. return helper;
  1189. },
  1190. _adjustOffsetFromHelper: function(obj) {
  1191. if (typeof obj == 'string') {
  1192. obj = obj.split(' ');
  1193. }
  1194. if ($.isArray(obj)) {
  1195. obj = {left: +obj[0], top: +obj[1] || 0};
  1196. }
  1197. if ('left' in obj) {
  1198. this.offset.click.left = obj.left + this.margins.left;
  1199. }
  1200. if ('right' in obj) {
  1201. this.offset.click.left = this.helperProportions.width - obj.right + this.margins.left;
  1202. }
  1203. if ('top' in obj) {
  1204. this.offset.click.top = obj.top + this.margins.top;
  1205. }
  1206. if ('bottom' in obj) {
  1207. this.offset.click.top = this.helperProportions.height - obj.bottom + this.margins.top;
  1208. }
  1209. },
  1210. _getParentOffset: function() {
  1211. //Get the offsetParent and cache its position
  1212. this.offsetParent = this.helper.offsetParent();
  1213. var po = this.offsetParent.offset();
  1214. // This is a special case where we need to modify a offset calculated on start, since the following happened:
  1215. // 1. The position of the helper is absolute, so it's position is calculated based on the next positioned parent
  1216. // 2. The actual offset parent is a child of the scroll parent, and the scroll parent isn't the document, which means that
  1217. // the scroll is included in the initial calculation of the offset of the parent, and never recalculated upon drag
  1218. if(this.cssPosition == 'absolute' && this.scrollParent[0] != document && $.ui.contains(this.scrollParent[0], this.offsetParent[0])) {
  1219. po.left += this.scrollParent.scrollLeft();
  1220. po.top += this.scrollParent.scrollTop();
  1221. }
  1222. if((this.offsetParent[0] == document.body) //This needs to be actually done for all browsers, since pageX/pageY includes this information
  1223. || (this.offsetParent[0].tagName && this.offsetParent[0].tagName.toLowerCase() == 'html' && $.browser.msie)) //Ugly IE fix
  1224. po = { top: 0, left: 0 };
  1225. return {
  1226. top: po.top + (parseInt(this.offsetParent.css("borderTopWidth"),10) || 0),
  1227. left: po.left + (parseInt(this.offsetParent.css("borderLeftWidth"),10) || 0)
  1228. };
  1229. },
  1230. _getRelativeOffset: function() {
  1231. if(this.cssPosition == "relative") {
  1232. var p = this.currentItem.position();
  1233. return {
  1234. top: p.top - (parseInt(this.helper.css("top"),10) || 0) + this.scrollParent.scrollTop(),
  1235. left: p.left - (parseInt(this.helper.css("left"),10) || 0) + this.scrollParent.scrollLeft()
  1236. };
  1237. } else {
  1238. return { top: 0, left: 0 };
  1239. }
  1240. },
  1241. _cacheMargins: function() {
  1242. this.margins = {
  1243. left: (parseInt(this.currentItem.css("marginLeft"),10) || 0),
  1244. top: (parseInt(this.currentItem.css("marginTop"),10) || 0)
  1245. };
  1246. },
  1247. _cacheHelperProportions: function() {
  1248. this.helperProportions = {
  1249. width: this.helper.outerWidth(),
  1250. height: this.helper.outerHeight()
  1251. };
  1252. },
  1253. _setContainment: function() {
  1254. var o = this.options;
  1255. if(o.containment == 'parent') o.containment = this.helper[0].parentNode;
  1256. if(o.containment == 'document' || o.containment == 'window') this.containment = [
  1257. 0 - this.offset.relative.left - this.offset.parent.left,
  1258. 0 - this.offset.relative.top - this.offset.parent.top,
  1259. $(o.containment == 'document' ? document : window).width() - this.helperProportions.width - this.margins.left,
  1260. ($(o.containment == 'document' ? document : window).height() || document.body.parentNode.scrollHeight) - this.helperProportions.height - this.margins.top
  1261. ];
  1262. if(!(/^(document|window|parent)$/).test(o.containment)) {
  1263. var ce = $(o.containment)[0];
  1264. var co = $(o.containment).offset();
  1265. var over = ($(ce).css("overflow") != 'hidden');
  1266. this.containment = [
  1267. co.left + (parseInt($(ce).css("borderLeftWidth"),10) || 0) + (parseInt($(ce).css("paddingLeft"),10) || 0) - this.margins.left,
  1268. co.top + (parseInt($(ce).css("borderTopWidth"),10) || 0) + (parseInt($(ce).css("paddingTop"),10) || 0) - this.margins.top,
  1269. co.left+(over ? Math.max(ce.scrollWidth,ce.offsetWidth) : ce.offsetWidth) - (parseInt($(ce).css("borderLeftWidth"),10) || 0) - (parseInt($(ce).css("paddingRight"),10) || 0) - this.helperProportions.width - this.margins.left,
  1270. co.top+(over ? Math.max(ce.scrollHeight,ce.offsetHeight) : ce.offsetHeight) - (parseInt($(ce).css("borderTopWidth"),10) || 0) - (parseInt($(ce).css("paddingBottom"),10) || 0) - this.helperProportions.height - this.margins.top
  1271. ];
  1272. }
  1273. },
  1274. _convertPositionTo: function(d, pos) {
  1275. if(!pos) pos = this.position;
  1276. var mod = d == "absolute" ? 1 : -1;
  1277. var o = this.options, scroll = this.cssPosition == 'absolute' && !(this.scrollParent[0] != document && $.ui.contains(this.scrollParent[0], this.offsetParent[0])) ? this.offsetParent : this.scrollParent, scrollIsRootNode = (/(html|body)/i).test(scroll[0].tagName);
  1278. return {
  1279. top: (
  1280. pos.top // The absolute mouse position
  1281. + this.offset.relative.top * mod // Only for relative positioned nodes: Relative offset from element to offset parent
  1282. + this.offset.parent.top * mod // The offsetParent's offset without borders (offset + border)
  1283. - ($.browser.safari && this.cssPosition == 'fixed' ? 0 : ( this.cssPosition == 'fixed' ? -this.scrollParent.scrollTop() : ( scrollIsRootNode ? 0 : scroll.scrollTop() ) ) * mod)
  1284. ),
  1285. left: (
  1286. pos.left // The absolute mouse position
  1287. + this.offset.relative.left * mod // Only for relative positioned nodes: Relative offset from element to offset parent
  1288. + this.offset.parent.left * mod // The offsetParent's offset without borders (offset + border)
  1289. - ($.browser.safari && this.cssPosition == 'fixed' ? 0 : ( this.cssPosition == 'fixed' ? -this.scrollParent.scrollLeft() : scrollIsRootNode ? 0 : scroll.scrollLeft() ) * mod)
  1290. )
  1291. };
  1292. },
  1293. _generatePosition: function(event) {
  1294. var o = this.options, scroll = this.cssPosition == 'absolute' && !(this.scrollParent[0] != document && $.ui.contains(this.scrollParent[0], this.offsetParent[0])) ? this.offsetParent : this.scrollParent, scrollIsRootNode = (/(html|body)/i).test(scroll[0].tagName);
  1295. // This is another very weird special case that only happens for relative elements:
  1296. // 1. If the css position is relative
  1297. // 2. and the scroll parent is the document or similar to the offset pa…