draggable.js 35 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257
  1. /*!
  2. * jQuery UI Draggable 1.14.1
  3. * https://jqueryui.com
  4. *
  5. * Copyright OpenJS Foundation and other contributors
  6. * Released under the MIT license.
  7. * https://jquery.org/license
  8. */
  9. //>>label: Draggable
  10. //>>group: Interactions
  11. //>>description: Enables dragging functionality for any element.
  12. //>>docs: https://api.jqueryui.com/draggable/
  13. //>>demos: https://jqueryui.com/draggable/
  14. //>>css.structure: ../../themes/base/draggable.css
  15. ( function( factory ) {
  16. "use strict";
  17. if ( typeof define === "function" && define.amd ) {
  18. // AMD. Register as an anonymous module.
  19. define( [
  20. "jquery",
  21. "./mouse",
  22. "../data",
  23. "../plugin",
  24. "../scroll-parent",
  25. "../version",
  26. "../widget"
  27. ], factory );
  28. } else {
  29. // Browser globals
  30. factory( jQuery );
  31. }
  32. } )( function( $ ) {
  33. "use strict";
  34. $.widget( "ui.draggable", $.ui.mouse, {
  35. version: "1.14.1",
  36. widgetEventPrefix: "drag",
  37. options: {
  38. addClasses: true,
  39. appendTo: "parent",
  40. axis: false,
  41. connectToSortable: false,
  42. containment: false,
  43. cursor: "auto",
  44. cursorAt: false,
  45. grid: false,
  46. handle: false,
  47. helper: "original",
  48. iframeFix: false,
  49. opacity: false,
  50. refreshPositions: false,
  51. revert: false,
  52. revertDuration: 500,
  53. scope: "default",
  54. scroll: true,
  55. scrollSensitivity: 20,
  56. scrollSpeed: 20,
  57. snap: false,
  58. snapMode: "both",
  59. snapTolerance: 20,
  60. stack: false,
  61. zIndex: false,
  62. // Callbacks
  63. drag: null,
  64. start: null,
  65. stop: null
  66. },
  67. _create: function() {
  68. if ( this.options.helper === "original" ) {
  69. this._setPositionRelative();
  70. }
  71. if ( this.options.addClasses ) {
  72. this._addClass( "ui-draggable" );
  73. }
  74. this._setHandleClassName();
  75. this._mouseInit();
  76. },
  77. _setOption: function( key, value ) {
  78. this._super( key, value );
  79. if ( key === "handle" ) {
  80. this._removeHandleClassName();
  81. this._setHandleClassName();
  82. }
  83. },
  84. _destroy: function() {
  85. if ( ( this.helper || this.element ).is( ".ui-draggable-dragging" ) ) {
  86. this.destroyOnClear = true;
  87. return;
  88. }
  89. this._removeHandleClassName();
  90. this._mouseDestroy();
  91. },
  92. _mouseCapture: function( event ) {
  93. var o = this.options;
  94. // Among others, prevent a drag on a resizable-handle
  95. if ( this.helper || o.disabled ||
  96. $( event.target ).closest( ".ui-resizable-handle" ).length > 0 ) {
  97. return false;
  98. }
  99. //Quit if we're not on a valid handle
  100. this.handle = this._getHandle( event );
  101. if ( !this.handle ) {
  102. return false;
  103. }
  104. this._blurActiveElement( event );
  105. this._blockFrames( o.iframeFix === true ? "iframe" : o.iframeFix );
  106. return true;
  107. },
  108. _blockFrames: function( selector ) {
  109. this.iframeBlocks = this.document.find( selector ).map( function() {
  110. var iframe = $( this );
  111. return $( "<div>" )
  112. .css( "position", "absolute" )
  113. .appendTo( iframe.parent() )
  114. .outerWidth( iframe.outerWidth() )
  115. .outerHeight( iframe.outerHeight() )
  116. .offset( iframe.offset() )[ 0 ];
  117. } );
  118. },
  119. _unblockFrames: function() {
  120. if ( this.iframeBlocks ) {
  121. this.iframeBlocks.remove();
  122. delete this.iframeBlocks;
  123. }
  124. },
  125. _blurActiveElement: function( event ) {
  126. var activeElement = this.document[ 0 ].activeElement,
  127. target = $( event.target );
  128. // Don't blur if the event occurred on an element that is within
  129. // the currently focused element
  130. // See #10527, #12472
  131. if ( target.closest( activeElement ).length ) {
  132. return;
  133. }
  134. // Blur any element that currently has focus, see #4261
  135. $( activeElement ).trigger( "blur" );
  136. },
  137. _mouseStart: function( event ) {
  138. var o = this.options;
  139. //Create and append the visible helper
  140. this.helper = this._createHelper( event );
  141. this._addClass( this.helper, "ui-draggable-dragging" );
  142. //Cache the helper size
  143. this._cacheHelperProportions();
  144. //If ddmanager is used for droppables, set the global draggable
  145. if ( $.ui.ddmanager ) {
  146. $.ui.ddmanager.current = this;
  147. }
  148. /*
  149. * - Position generation -
  150. * This block generates everything position related - it's the core of draggables.
  151. */
  152. //Cache the margins of the original element
  153. this._cacheMargins();
  154. //Store the helper's css position
  155. this.cssPosition = this.helper.css( "position" );
  156. this.scrollParent = this.helper.scrollParent( true );
  157. this.offsetParent = this.helper.offsetParent();
  158. this.hasFixedAncestor = this.helper.parents().filter( function() {
  159. return $( this ).css( "position" ) === "fixed";
  160. } ).length > 0;
  161. //The element's absolute position on the page minus margins
  162. this.positionAbs = this.element.offset();
  163. this._refreshOffsets( event );
  164. //Generate the original position
  165. this.originalPosition = this.position = this._generatePosition( event, false );
  166. this.originalPageX = event.pageX;
  167. this.originalPageY = event.pageY;
  168. //Adjust the mouse offset relative to the helper if "cursorAt" is supplied
  169. if ( o.cursorAt ) {
  170. this._adjustOffsetFromHelper( o.cursorAt );
  171. }
  172. //Set a containment if given in the options
  173. this._setContainment();
  174. //Trigger event + callbacks
  175. if ( this._trigger( "start", event ) === false ) {
  176. this._clear();
  177. return false;
  178. }
  179. //Recache the helper size
  180. this._cacheHelperProportions();
  181. //Prepare the droppable offsets
  182. if ( $.ui.ddmanager && !o.dropBehaviour ) {
  183. $.ui.ddmanager.prepareOffsets( this, event );
  184. }
  185. // Execute the drag once - this causes the helper not to be visible before getting its
  186. // correct position
  187. this._mouseDrag( event, true );
  188. // If the ddmanager is used for droppables, inform the manager that dragging has started
  189. // (see #5003)
  190. if ( $.ui.ddmanager ) {
  191. $.ui.ddmanager.dragStart( this, event );
  192. }
  193. return true;
  194. },
  195. _refreshOffsets: function( event ) {
  196. this.offset = {
  197. top: this.positionAbs.top - this.margins.top,
  198. left: this.positionAbs.left - this.margins.left,
  199. scroll: false,
  200. parent: this._getParentOffset(),
  201. relative: this._getRelativeOffset()
  202. };
  203. this.offset.click = {
  204. left: event.pageX - this.offset.left,
  205. top: event.pageY - this.offset.top
  206. };
  207. },
  208. _mouseDrag: function( event, noPropagation ) {
  209. // reset any necessary cached properties (see #5009)
  210. if ( this.hasFixedAncestor ) {
  211. this.offset.parent = this._getParentOffset();
  212. }
  213. //Compute the helpers position
  214. this.position = this._generatePosition( event, true );
  215. this.positionAbs = this._convertPositionTo( "absolute" );
  216. //Call plugins and callbacks and use the resulting position if something is returned
  217. if ( !noPropagation ) {
  218. var ui = this._uiHash();
  219. if ( this._trigger( "drag", event, ui ) === false ) {
  220. this._mouseUp( new $.Event( "mouseup", event ) );
  221. return false;
  222. }
  223. this.position = ui.position;
  224. }
  225. this.helper[ 0 ].style.left = this.position.left + "px";
  226. this.helper[ 0 ].style.top = this.position.top + "px";
  227. if ( $.ui.ddmanager ) {
  228. $.ui.ddmanager.drag( this, event );
  229. }
  230. return false;
  231. },
  232. _mouseStop: function( event ) {
  233. //If we are using droppables, inform the manager about the drop
  234. var that = this,
  235. dropped = false;
  236. if ( $.ui.ddmanager && !this.options.dropBehaviour ) {
  237. dropped = $.ui.ddmanager.drop( this, event );
  238. }
  239. //if a drop comes from outside (a sortable)
  240. if ( this.dropped ) {
  241. dropped = this.dropped;
  242. this.dropped = false;
  243. }
  244. if ( ( this.options.revert === "invalid" && !dropped ) ||
  245. ( this.options.revert === "valid" && dropped ) ||
  246. this.options.revert === true || ( typeof this.options.revert === "function" &&
  247. this.options.revert.call( this.element, dropped ) )
  248. ) {
  249. $( this.helper ).animate(
  250. this.originalPosition,
  251. parseInt( this.options.revertDuration, 10 ),
  252. function() {
  253. if ( that._trigger( "stop", event ) !== false ) {
  254. that._clear();
  255. }
  256. }
  257. );
  258. } else {
  259. if ( this._trigger( "stop", event ) !== false ) {
  260. this._clear();
  261. }
  262. }
  263. return false;
  264. },
  265. _mouseUp: function( event ) {
  266. this._unblockFrames();
  267. // If the ddmanager is used for droppables, inform the manager that dragging has stopped
  268. // (see #5003)
  269. if ( $.ui.ddmanager ) {
  270. $.ui.ddmanager.dragStop( this, event );
  271. }
  272. // Only need to focus if the event occurred on the draggable itself, see #10527
  273. if ( this.handleElement.is( event.target ) ) {
  274. // The interaction is over; whether or not the click resulted in a drag,
  275. // focus the element
  276. this.element.trigger( "focus" );
  277. }
  278. return $.ui.mouse.prototype._mouseUp.call( this, event );
  279. },
  280. cancel: function() {
  281. if ( this.helper.is( ".ui-draggable-dragging" ) ) {
  282. this._mouseUp( new $.Event( "mouseup", { target: this.element[ 0 ] } ) );
  283. } else {
  284. this._clear();
  285. }
  286. return this;
  287. },
  288. _getHandle: function( event ) {
  289. return this.options.handle ?
  290. !!$( event.target ).closest( this.element.find( this.options.handle ) ).length :
  291. true;
  292. },
  293. _setHandleClassName: function() {
  294. this.handleElement = this.options.handle ?
  295. this.element.find( this.options.handle ) : this.element;
  296. this._addClass( this.handleElement, "ui-draggable-handle" );
  297. },
  298. _removeHandleClassName: function() {
  299. this._removeClass( this.handleElement, "ui-draggable-handle" );
  300. },
  301. _createHelper: function( event ) {
  302. var o = this.options,
  303. helperIsFunction = typeof o.helper === "function",
  304. helper = helperIsFunction ?
  305. $( o.helper.apply( this.element[ 0 ], [ event ] ) ) :
  306. ( o.helper === "clone" ?
  307. this.element.clone().removeAttr( "id" ) :
  308. this.element );
  309. if ( !helper.parents( "body" ).length ) {
  310. helper.appendTo( ( o.appendTo === "parent" ?
  311. this.element[ 0 ].parentNode :
  312. o.appendTo ) );
  313. }
  314. // https://bugs.jqueryui.com/ticket/9446
  315. // a helper function can return the original element
  316. // which wouldn't have been set to relative in _create
  317. if ( helperIsFunction && helper[ 0 ] === this.element[ 0 ] ) {
  318. this._setPositionRelative();
  319. }
  320. if ( helper[ 0 ] !== this.element[ 0 ] &&
  321. !( /(fixed|absolute)/ ).test( helper.css( "position" ) ) ) {
  322. helper.css( "position", "absolute" );
  323. }
  324. return helper;
  325. },
  326. _setPositionRelative: function() {
  327. if ( !( /^(?:r|a|f)/ ).test( this.element.css( "position" ) ) ) {
  328. this.element[ 0 ].style.position = "relative";
  329. }
  330. },
  331. _adjustOffsetFromHelper: function( obj ) {
  332. if ( typeof obj === "string" ) {
  333. obj = obj.split( " " );
  334. }
  335. if ( Array.isArray( obj ) ) {
  336. obj = { left: +obj[ 0 ], top: +obj[ 1 ] || 0 };
  337. }
  338. if ( "left" in obj ) {
  339. this.offset.click.left = obj.left + this.margins.left;
  340. }
  341. if ( "right" in obj ) {
  342. this.offset.click.left = this.helperProportions.width - obj.right + this.margins.left;
  343. }
  344. if ( "top" in obj ) {
  345. this.offset.click.top = obj.top + this.margins.top;
  346. }
  347. if ( "bottom" in obj ) {
  348. this.offset.click.top = this.helperProportions.height - obj.bottom + this.margins.top;
  349. }
  350. },
  351. _isRootNode: function( element ) {
  352. return ( /(html|body)/i ).test( element.tagName ) || element === this.document[ 0 ];
  353. },
  354. _getParentOffset: function() {
  355. //Get the offsetParent and cache its position
  356. var po = this.offsetParent.offset(),
  357. document = this.document[ 0 ];
  358. // This is a special case where we need to modify a offset calculated on start, since the
  359. // following happened:
  360. // 1. The position of the helper is absolute, so it's position is calculated based on the
  361. // next positioned parent
  362. // 2. The actual offset parent is a child of the scroll parent, and the scroll parent isn't
  363. // the document, which means that the scroll is included in the initial calculation of the
  364. // offset of the parent, and never recalculated upon drag
  365. if ( this.cssPosition === "absolute" && this.scrollParent[ 0 ] !== document &&
  366. $.contains( this.scrollParent[ 0 ], this.offsetParent[ 0 ] ) ) {
  367. po.left += this.scrollParent.scrollLeft();
  368. po.top += this.scrollParent.scrollTop();
  369. }
  370. if ( this._isRootNode( this.offsetParent[ 0 ] ) ) {
  371. po = { top: 0, left: 0 };
  372. }
  373. return {
  374. top: po.top + ( parseInt( this.offsetParent.css( "borderTopWidth" ), 10 ) || 0 ),
  375. left: po.left + ( parseInt( this.offsetParent.css( "borderLeftWidth" ), 10 ) || 0 )
  376. };
  377. },
  378. _getRelativeOffset: function() {
  379. if ( this.cssPosition !== "relative" ) {
  380. return { top: 0, left: 0 };
  381. }
  382. var p = this.element.position(),
  383. scrollIsRootNode = this._isRootNode( this.scrollParent[ 0 ] );
  384. return {
  385. top: p.top - ( parseInt( this.helper.css( "top" ), 10 ) || 0 ) +
  386. ( !scrollIsRootNode ? this.scrollParent.scrollTop() : 0 ),
  387. left: p.left - ( parseInt( this.helper.css( "left" ), 10 ) || 0 ) +
  388. ( !scrollIsRootNode ? this.scrollParent.scrollLeft() : 0 )
  389. };
  390. },
  391. _cacheMargins: function() {
  392. this.margins = {
  393. left: ( parseInt( this.element.css( "marginLeft" ), 10 ) || 0 ),
  394. top: ( parseInt( this.element.css( "marginTop" ), 10 ) || 0 ),
  395. right: ( parseInt( this.element.css( "marginRight" ), 10 ) || 0 ),
  396. bottom: ( parseInt( this.element.css( "marginBottom" ), 10 ) || 0 )
  397. };
  398. },
  399. _cacheHelperProportions: function() {
  400. this.helperProportions = {
  401. width: this.helper.outerWidth(),
  402. height: this.helper.outerHeight()
  403. };
  404. },
  405. _setContainment: function() {
  406. var isUserScrollable, c, ce,
  407. o = this.options,
  408. document = this.document[ 0 ];
  409. this.relativeContainer = null;
  410. if ( !o.containment ) {
  411. this.containment = null;
  412. return;
  413. }
  414. if ( o.containment === "window" ) {
  415. this.containment = [
  416. $( window ).scrollLeft() - this.offset.relative.left - this.offset.parent.left,
  417. $( window ).scrollTop() - this.offset.relative.top - this.offset.parent.top,
  418. $( window ).scrollLeft() + $( window ).width() -
  419. this.helperProportions.width - this.margins.left,
  420. $( window ).scrollTop() +
  421. ( $( window ).height() || document.body.parentNode.scrollHeight ) -
  422. this.helperProportions.height - this.margins.top
  423. ];
  424. return;
  425. }
  426. if ( o.containment === "document" ) {
  427. this.containment = [
  428. 0,
  429. 0,
  430. $( document ).width() - this.helperProportions.width - this.margins.left,
  431. ( $( document ).height() || document.body.parentNode.scrollHeight ) -
  432. this.helperProportions.height - this.margins.top
  433. ];
  434. return;
  435. }
  436. if ( o.containment.constructor === Array ) {
  437. this.containment = o.containment;
  438. return;
  439. }
  440. if ( o.containment === "parent" ) {
  441. o.containment = this.helper[ 0 ].parentNode;
  442. }
  443. c = $( o.containment );
  444. ce = c[ 0 ];
  445. if ( !ce ) {
  446. return;
  447. }
  448. isUserScrollable = /(scroll|auto)/.test( c.css( "overflow" ) );
  449. this.containment = [
  450. ( parseInt( c.css( "borderLeftWidth" ), 10 ) || 0 ) +
  451. ( parseInt( c.css( "paddingLeft" ), 10 ) || 0 ),
  452. ( parseInt( c.css( "borderTopWidth" ), 10 ) || 0 ) +
  453. ( parseInt( c.css( "paddingTop" ), 10 ) || 0 ),
  454. ( isUserScrollable ? Math.max( ce.scrollWidth, ce.offsetWidth ) : ce.offsetWidth ) -
  455. ( parseInt( c.css( "borderRightWidth" ), 10 ) || 0 ) -
  456. ( parseInt( c.css( "paddingRight" ), 10 ) || 0 ) -
  457. this.helperProportions.width -
  458. this.margins.left -
  459. this.margins.right,
  460. ( isUserScrollable ? Math.max( ce.scrollHeight, ce.offsetHeight ) : ce.offsetHeight ) -
  461. ( parseInt( c.css( "borderBottomWidth" ), 10 ) || 0 ) -
  462. ( parseInt( c.css( "paddingBottom" ), 10 ) || 0 ) -
  463. this.helperProportions.height -
  464. this.margins.top -
  465. this.margins.bottom
  466. ];
  467. this.relativeContainer = c;
  468. },
  469. _convertPositionTo: function( d, pos ) {
  470. if ( !pos ) {
  471. pos = this.position;
  472. }
  473. var mod = d === "absolute" ? 1 : -1,
  474. scrollIsRootNode = this._isRootNode( this.scrollParent[ 0 ] );
  475. return {
  476. top: (
  477. // The absolute mouse position
  478. pos.top +
  479. // Only for relative positioned nodes: Relative offset from element to offset parent
  480. this.offset.relative.top * mod +
  481. // The offsetParent's offset without borders (offset + border)
  482. this.offset.parent.top * mod -
  483. ( ( this.cssPosition === "fixed" ?
  484. -this.offset.scroll.top :
  485. ( scrollIsRootNode ? 0 : this.offset.scroll.top ) ) * mod )
  486. ),
  487. left: (
  488. // The absolute mouse position
  489. pos.left +
  490. // Only for relative positioned nodes: Relative offset from element to offset parent
  491. this.offset.relative.left * mod +
  492. // The offsetParent's offset without borders (offset + border)
  493. this.offset.parent.left * mod -
  494. ( ( this.cssPosition === "fixed" ?
  495. -this.offset.scroll.left :
  496. ( scrollIsRootNode ? 0 : this.offset.scroll.left ) ) * mod )
  497. )
  498. };
  499. },
  500. _generatePosition: function( event, constrainPosition ) {
  501. var containment, co, top, left,
  502. o = this.options,
  503. scrollIsRootNode = this._isRootNode( this.scrollParent[ 0 ] ),
  504. pageX = event.pageX,
  505. pageY = event.pageY;
  506. // Cache the scroll
  507. if ( !scrollIsRootNode || !this.offset.scroll ) {
  508. this.offset.scroll = {
  509. top: this.scrollParent.scrollTop(),
  510. left: this.scrollParent.scrollLeft()
  511. };
  512. }
  513. /*
  514. * - Position constraining -
  515. * Constrain the position to a mix of grid, containment.
  516. */
  517. // If we are not dragging yet, we won't check for options
  518. if ( constrainPosition ) {
  519. if ( this.containment ) {
  520. if ( this.relativeContainer ) {
  521. co = this.relativeContainer.offset();
  522. containment = [
  523. this.containment[ 0 ] + co.left,
  524. this.containment[ 1 ] + co.top,
  525. this.containment[ 2 ] + co.left,
  526. this.containment[ 3 ] + co.top
  527. ];
  528. } else {
  529. containment = this.containment;
  530. }
  531. if ( event.pageX - this.offset.click.left < containment[ 0 ] ) {
  532. pageX = containment[ 0 ] + this.offset.click.left;
  533. }
  534. if ( event.pageY - this.offset.click.top < containment[ 1 ] ) {
  535. pageY = containment[ 1 ] + this.offset.click.top;
  536. }
  537. if ( event.pageX - this.offset.click.left > containment[ 2 ] ) {
  538. pageX = containment[ 2 ] + this.offset.click.left;
  539. }
  540. if ( event.pageY - this.offset.click.top > containment[ 3 ] ) {
  541. pageY = containment[ 3 ] + this.offset.click.top;
  542. }
  543. }
  544. if ( o.grid ) {
  545. //Check for grid elements set to 0 to prevent divide by 0 error causing invalid
  546. // argument errors in IE (see ticket #6950)
  547. top = o.grid[ 1 ] ? this.originalPageY + Math.round( ( pageY -
  548. this.originalPageY ) / o.grid[ 1 ] ) * o.grid[ 1 ] : this.originalPageY;
  549. pageY = containment ? ( ( top - this.offset.click.top >= containment[ 1 ] ||
  550. top - this.offset.click.top > containment[ 3 ] ) ?
  551. top :
  552. ( ( top - this.offset.click.top >= containment[ 1 ] ) ?
  553. top - o.grid[ 1 ] : top + o.grid[ 1 ] ) ) : top;
  554. left = o.grid[ 0 ] ? this.originalPageX +
  555. Math.round( ( pageX - this.originalPageX ) / o.grid[ 0 ] ) * o.grid[ 0 ] :
  556. this.originalPageX;
  557. pageX = containment ? ( ( left - this.offset.click.left >= containment[ 0 ] ||
  558. left - this.offset.click.left > containment[ 2 ] ) ?
  559. left :
  560. ( ( left - this.offset.click.left >= containment[ 0 ] ) ?
  561. left - o.grid[ 0 ] : left + o.grid[ 0 ] ) ) : left;
  562. }
  563. if ( o.axis === "y" ) {
  564. pageX = this.originalPageX;
  565. }
  566. if ( o.axis === "x" ) {
  567. pageY = this.originalPageY;
  568. }
  569. }
  570. return {
  571. top: (
  572. // The absolute mouse position
  573. pageY -
  574. // Click offset (relative to the element)
  575. this.offset.click.top -
  576. // Only for relative positioned nodes: Relative offset from element to offset parent
  577. this.offset.relative.top -
  578. // The offsetParent's offset without borders (offset + border)
  579. this.offset.parent.top +
  580. ( this.cssPosition === "fixed" ?
  581. -this.offset.scroll.top :
  582. ( scrollIsRootNode ? 0 : this.offset.scroll.top ) )
  583. ),
  584. left: (
  585. // The absolute mouse position
  586. pageX -
  587. // Click offset (relative to the element)
  588. this.offset.click.left -
  589. // Only for relative positioned nodes: Relative offset from element to offset parent
  590. this.offset.relative.left -
  591. // The offsetParent's offset without borders (offset + border)
  592. this.offset.parent.left +
  593. ( this.cssPosition === "fixed" ?
  594. -this.offset.scroll.left :
  595. ( scrollIsRootNode ? 0 : this.offset.scroll.left ) )
  596. )
  597. };
  598. },
  599. _clear: function() {
  600. this._removeClass( this.helper, "ui-draggable-dragging" );
  601. if ( this.helper[ 0 ] !== this.element[ 0 ] && !this.cancelHelperRemoval ) {
  602. this.helper.remove();
  603. }
  604. this.helper = null;
  605. this.cancelHelperRemoval = false;
  606. if ( this.destroyOnClear ) {
  607. this.destroy();
  608. }
  609. },
  610. // From now on bulk stuff - mainly helpers
  611. _trigger: function( type, event, ui ) {
  612. ui = ui || this._uiHash();
  613. $.ui.plugin.call( this, type, [ event, ui, this ], true );
  614. // Absolute position and offset (see #6884 ) have to be recalculated after plugins
  615. if ( /^(drag|start|stop)/.test( type ) ) {
  616. this.positionAbs = this._convertPositionTo( "absolute" );
  617. ui.offset = this.positionAbs;
  618. }
  619. return $.Widget.prototype._trigger.call( this, type, event, ui );
  620. },
  621. plugins: {},
  622. _uiHash: function() {
  623. return {
  624. helper: this.helper,
  625. position: this.position,
  626. originalPosition: this.originalPosition,
  627. offset: this.positionAbs
  628. };
  629. }
  630. } );
  631. $.ui.plugin.add( "draggable", "connectToSortable", {
  632. start: function( event, ui, draggable ) {
  633. var uiSortable = $.extend( {}, ui, {
  634. item: draggable.element
  635. } );
  636. draggable.sortables = [];
  637. $( draggable.options.connectToSortable ).each( function() {
  638. var sortable = $( this ).sortable( "instance" );
  639. if ( sortable && !sortable.options.disabled ) {
  640. draggable.sortables.push( sortable );
  641. // RefreshPositions is called at drag start to refresh the containerCache
  642. // which is used in drag. This ensures it's initialized and synchronized
  643. // with any changes that might have happened on the page since initialization.
  644. sortable.refreshPositions();
  645. sortable._trigger( "activate", event, uiSortable );
  646. }
  647. } );
  648. },
  649. stop: function( event, ui, draggable ) {
  650. var uiSortable = $.extend( {}, ui, {
  651. item: draggable.element
  652. } );
  653. draggable.cancelHelperRemoval = false;
  654. $.each( draggable.sortables, function() {
  655. var sortable = this;
  656. if ( sortable.isOver ) {
  657. sortable.isOver = 0;
  658. // Allow this sortable to handle removing the helper
  659. draggable.cancelHelperRemoval = true;
  660. sortable.cancelHelperRemoval = false;
  661. // Use _storedCSS To restore properties in the sortable,
  662. // as this also handles revert (#9675) since the draggable
  663. // may have modified them in unexpected ways (#8809)
  664. sortable._storedCSS = {
  665. position: sortable.placeholder.css( "position" ),
  666. top: sortable.placeholder.css( "top" ),
  667. left: sortable.placeholder.css( "left" )
  668. };
  669. sortable._mouseStop( event );
  670. // Once drag has ended, the sortable should return to using
  671. // its original helper, not the shared helper from draggable
  672. sortable.options.helper = sortable.options._helper;
  673. } else {
  674. // Prevent this Sortable from removing the helper.
  675. // However, don't set the draggable to remove the helper
  676. // either as another connected Sortable may yet handle the removal.
  677. sortable.cancelHelperRemoval = true;
  678. sortable._trigger( "deactivate", event, uiSortable );
  679. }
  680. } );
  681. },
  682. drag: function( event, ui, draggable ) {
  683. $.each( draggable.sortables, function() {
  684. var innermostIntersecting = false,
  685. sortable = this;
  686. // Copy over variables that sortable's _intersectsWith uses
  687. sortable.positionAbs = draggable.positionAbs;
  688. sortable.helperProportions = draggable.helperProportions;
  689. sortable.offset.click = draggable.offset.click;
  690. if ( sortable._intersectsWith( sortable.containerCache ) ) {
  691. innermostIntersecting = true;
  692. $.each( draggable.sortables, function() {
  693. // Copy over variables that sortable's _intersectsWith uses
  694. this.positionAbs = draggable.positionAbs;
  695. this.helperProportions = draggable.helperProportions;
  696. this.offset.click = draggable.offset.click;
  697. if ( this !== sortable &&
  698. this._intersectsWith( this.containerCache ) &&
  699. $.contains( sortable.element[ 0 ], this.element[ 0 ] ) ) {
  700. innermostIntersecting = false;
  701. }
  702. return innermostIntersecting;
  703. } );
  704. }
  705. if ( innermostIntersecting ) {
  706. // If it intersects, we use a little isOver variable and set it once,
  707. // so that the move-in stuff gets fired only once.
  708. if ( !sortable.isOver ) {
  709. sortable.isOver = 1;
  710. // Store draggable's parent in case we need to reappend to it later.
  711. draggable._parent = ui.helper.parent();
  712. sortable.currentItem = ui.helper
  713. .appendTo( sortable.element )
  714. .data( "ui-sortable-item", true );
  715. // Store helper option to later restore it
  716. sortable.options._helper = sortable.options.helper;
  717. sortable.options.helper = function() {
  718. return ui.helper[ 0 ];
  719. };
  720. // Fire the start events of the sortable with our passed browser event,
  721. // and our own helper (so it doesn't create a new one)
  722. event.target = sortable.currentItem[ 0 ];
  723. sortable._mouseCapture( event, true );
  724. sortable._mouseStart( event, true, true );
  725. // Because the browser event is way off the new appended portlet,
  726. // modify necessary variables to reflect the changes
  727. sortable.offset.click.top = draggable.offset.click.top;
  728. sortable.offset.click.left = draggable.offset.click.left;
  729. sortable.offset.parent.left -= draggable.offset.parent.left -
  730. sortable.offset.parent.left;
  731. sortable.offset.parent.top -= draggable.offset.parent.top -
  732. sortable.offset.parent.top;
  733. draggable._trigger( "toSortable", event );
  734. // Inform draggable that the helper is in a valid drop zone,
  735. // used solely in the revert option to handle "valid/invalid".
  736. draggable.dropped = sortable.element;
  737. // Need to refreshPositions of all sortables in the case that
  738. // adding to one sortable changes the location of the other sortables (#9675)
  739. $.each( draggable.sortables, function() {
  740. this.refreshPositions();
  741. } );
  742. // Hack so receive/update callbacks work (mostly)
  743. draggable.currentItem = draggable.element;
  744. sortable.fromOutside = draggable;
  745. }
  746. if ( sortable.currentItem ) {
  747. sortable._mouseDrag( event );
  748. // Copy the sortable's position because the draggable's can potentially reflect
  749. // a relative position, while sortable is always absolute, which the dragged
  750. // element has now become. (#8809)
  751. ui.position = sortable.position;
  752. }
  753. } else {
  754. // If it doesn't intersect with the sortable, and it intersected before,
  755. // we fake the drag stop of the sortable, but make sure it doesn't remove
  756. // the helper by using cancelHelperRemoval.
  757. if ( sortable.isOver ) {
  758. sortable.isOver = 0;
  759. sortable.cancelHelperRemoval = true;
  760. // Calling sortable's mouseStop would trigger a revert,
  761. // so revert must be temporarily false until after mouseStop is called.
  762. sortable.options._revert = sortable.options.revert;
  763. sortable.options.revert = false;
  764. sortable._trigger( "out", event, sortable._uiHash( sortable ) );
  765. sortable._mouseStop( event, true );
  766. // Restore sortable behaviors that were modfied
  767. // when the draggable entered the sortable area (#9481)
  768. sortable.options.revert = sortable.options._revert;
  769. sortable.options.helper = sortable.options._helper;
  770. if ( sortable.placeholder ) {
  771. sortable.placeholder.remove();
  772. }
  773. // Restore and recalculate the draggable's offset considering the sortable
  774. // may have modified them in unexpected ways. (#8809, #10669)
  775. ui.helper.appendTo( draggable._parent );
  776. draggable._refreshOffsets( event );
  777. ui.position = draggable._generatePosition( event, true );
  778. draggable._trigger( "fromSortable", event );
  779. // Inform draggable that the helper is no longer in a valid drop zone
  780. draggable.dropped = false;
  781. // Need to refreshPositions of all sortables just in case removing
  782. // from one sortable changes the location of other sortables (#9675)
  783. $.each( draggable.sortables, function() {
  784. this.refreshPositions();
  785. } );
  786. }
  787. }
  788. } );
  789. }
  790. } );
  791. $.ui.plugin.add( "draggable", "cursor", {
  792. start: function( event, ui, instance ) {
  793. var t = $( "body" ),
  794. o = instance.options;
  795. if ( t.css( "cursor" ) ) {
  796. o._cursor = t.css( "cursor" );
  797. }
  798. t.css( "cursor", o.cursor );
  799. },
  800. stop: function( event, ui, instance ) {
  801. var o = instance.options;
  802. if ( o._cursor ) {
  803. $( "body" ).css( "cursor", o._cursor );
  804. }
  805. }
  806. } );
  807. $.ui.plugin.add( "draggable", "opacity", {
  808. start: function( event, ui, instance ) {
  809. var t = $( ui.helper ),
  810. o = instance.options;
  811. if ( t.css( "opacity" ) ) {
  812. o._opacity = t.css( "opacity" );
  813. }
  814. t.css( "opacity", o.opacity );
  815. },
  816. stop: function( event, ui, instance ) {
  817. var o = instance.options;
  818. if ( o._opacity ) {
  819. $( ui.helper ).css( "opacity", o._opacity );
  820. }
  821. }
  822. } );
  823. $.ui.plugin.add( "draggable", "scroll", {
  824. start: function( event, ui, i ) {
  825. if ( !i.scrollParentNotHidden ) {
  826. i.scrollParentNotHidden = i.helper.scrollParent( false );
  827. }
  828. if ( i.scrollParentNotHidden[ 0 ] !== i.document[ 0 ] &&
  829. i.scrollParentNotHidden[ 0 ].tagName !== "HTML" ) {
  830. i.overflowOffset = i.scrollParentNotHidden.offset();
  831. }
  832. },
  833. drag: function( event, ui, i ) {
  834. var o = i.options,
  835. scrolled = false,
  836. scrollParent = i.scrollParentNotHidden[ 0 ],
  837. document = i.document[ 0 ];
  838. if ( scrollParent !== document && scrollParent.tagName !== "HTML" ) {
  839. if ( !o.axis || o.axis !== "x" ) {
  840. if ( ( i.overflowOffset.top + scrollParent.offsetHeight ) - event.pageY <
  841. o.scrollSensitivity ) {
  842. scrollParent.scrollTop = scrolled = scrollParent.scrollTop + o.scrollSpeed;
  843. } else if ( event.pageY - i.overflowOffset.top < o.scrollSensitivity ) {
  844. scrollParent.scrollTop = scrolled = scrollParent.scrollTop - o.scrollSpeed;
  845. }
  846. }
  847. if ( !o.axis || o.axis !== "y" ) {
  848. if ( ( i.overflowOffset.left + scrollParent.offsetWidth ) - event.pageX <
  849. o.scrollSensitivity ) {
  850. scrollParent.scrollLeft = scrolled = scrollParent.scrollLeft + o.scrollSpeed;
  851. } else if ( event.pageX - i.overflowOffset.left < o.scrollSensitivity ) {
  852. scrollParent.scrollLeft = scrolled = scrollParent.scrollLeft - o.scrollSpeed;
  853. }
  854. }
  855. } else {
  856. if ( !o.axis || o.axis !== "x" ) {
  857. if ( event.pageY - $( document ).scrollTop() < o.scrollSensitivity ) {
  858. scrolled = $( document ).scrollTop( $( document ).scrollTop() - o.scrollSpeed );
  859. } else if ( $( window ).height() - ( event.pageY - $( document ).scrollTop() ) <
  860. o.scrollSensitivity ) {
  861. scrolled = $( document ).scrollTop( $( document ).scrollTop() + o.scrollSpeed );
  862. }
  863. }
  864. if ( !o.axis || o.axis !== "y" ) {
  865. if ( event.pageX - $( document ).scrollLeft() < o.scrollSensitivity ) {
  866. scrolled = $( document ).scrollLeft(
  867. $( document ).scrollLeft() - o.scrollSpeed
  868. );
  869. } else if ( $( window ).width() - ( event.pageX - $( document ).scrollLeft() ) <
  870. o.scrollSensitivity ) {
  871. scrolled = $( document ).scrollLeft(
  872. $( document ).scrollLeft() + o.scrollSpeed
  873. );
  874. }
  875. }
  876. }
  877. if ( scrolled !== false && $.ui.ddmanager && !o.dropBehaviour ) {
  878. $.ui.ddmanager.prepareOffsets( i, event );
  879. }
  880. }
  881. } );
  882. $.ui.plugin.add( "draggable", "snap", {
  883. start: function( event, ui, i ) {
  884. var o = i.options;
  885. i.snapElements = [];
  886. $( o.snap.constructor !== String ? ( o.snap.items || ":data(ui-draggable)" ) : o.snap )
  887. .each( function() {
  888. var $t = $( this ),
  889. $o = $t.offset();
  890. if ( this !== i.element[ 0 ] ) {
  891. i.snapElements.push( {
  892. item: this,
  893. width: $t.outerWidth(), height: $t.outerHeight(),
  894. top: $o.top, left: $o.left
  895. } );
  896. }
  897. } );
  898. },
  899. drag: function( event, ui, inst ) {
  900. var ts, bs, ls, rs, l, r, t, b, i, first,
  901. o = inst.options,
  902. d = o.snapTolerance,
  903. x1 = ui.offset.left, x2 = x1 + inst.helperProportions.width,
  904. y1 = ui.offset.top, y2 = y1 + inst.helperProportions.height;
  905. for ( i = inst.snapElements.length - 1; i >= 0; i-- ) {
  906. l = inst.snapElements[ i ].left - inst.margins.left;
  907. r = l + inst.snapElements[ i ].width;
  908. t = inst.snapElements[ i ].top - inst.margins.top;
  909. b = t + inst.snapElements[ i ].height;
  910. if ( x2 < l - d || x1 > r + d || y2 < t - d || y1 > b + d ||
  911. !$.contains( inst.snapElements[ i ].item.ownerDocument,
  912. inst.snapElements[ i ].item ) ) {
  913. if ( inst.snapElements[ i ].snapping ) {
  914. if ( inst.options.snap.release ) {
  915. inst.options.snap.release.call(
  916. inst.element,
  917. event,
  918. $.extend( inst._uiHash(), { snapItem: inst.snapElements[ i ].item } )
  919. );
  920. }
  921. }
  922. inst.snapElements[ i ].snapping = false;
  923. continue;
  924. }
  925. if ( o.snapMode !== "inner" ) {
  926. ts = Math.abs( t - y2 ) <= d;
  927. bs = Math.abs( b - y1 ) <= d;
  928. ls = Math.abs( l - x2 ) <= d;
  929. rs = Math.abs( r - x1 ) <= d;
  930. if ( ts ) {
  931. ui.position.top = inst._convertPositionTo( "relative", {
  932. top: t - inst.helperProportions.height,
  933. left: 0
  934. } ).top;
  935. }
  936. if ( bs ) {
  937. ui.position.top = inst._convertPositionTo( "relative", {
  938. top: b,
  939. left: 0
  940. } ).top;
  941. }
  942. if ( ls ) {
  943. ui.position.left = inst._convertPositionTo( "relative", {
  944. top: 0,
  945. left: l - inst.helperProportions.width
  946. } ).left;
  947. }
  948. if ( rs ) {
  949. ui.position.left = inst._convertPositionTo( "relative", {
  950. top: 0,
  951. left: r
  952. } ).left;
  953. }
  954. }
  955. first = ( ts || bs || ls || rs );
  956. if ( o.snapMode !== "outer" ) {
  957. ts = Math.abs( t - y1 ) <= d;
  958. bs = Math.abs( b - y2 ) <= d;
  959. ls = Math.abs( l - x1 ) <= d;
  960. rs = Math.abs( r - x2 ) <= d;
  961. if ( ts ) {
  962. ui.position.top = inst._convertPositionTo( "relative", {
  963. top: t,
  964. left: 0
  965. } ).top;
  966. }
  967. if ( bs ) {
  968. ui.position.top = inst._convertPositionTo( "relative", {
  969. top: b - inst.helperProportions.height,
  970. left: 0
  971. } ).top;
  972. }
  973. if ( ls ) {
  974. ui.position.left = inst._convertPositionTo( "relative", {
  975. top: 0,
  976. left: l
  977. } ).left;
  978. }
  979. if ( rs ) {
  980. ui.position.left = inst._convertPositionTo( "relative", {
  981. top: 0,
  982. left: r - inst.helperProportions.width
  983. } ).left;
  984. }
  985. }
  986. if ( !inst.snapElements[ i ].snapping && ( ts || bs || ls || rs || first ) ) {
  987. if ( inst.options.snap.snap ) {
  988. inst.options.snap.snap.call(
  989. inst.element,
  990. event,
  991. $.extend( inst._uiHash(), {
  992. snapItem: inst.snapElements[ i ].item
  993. } ) );
  994. }
  995. }
  996. inst.snapElements[ i ].snapping = ( ts || bs || ls || rs || first );
  997. }
  998. }
  999. } );
  1000. $.ui.plugin.add( "draggable", "stack", {
  1001. start: function( event, ui, instance ) {
  1002. var min,
  1003. o = instance.options,
  1004. group = $.makeArray( $( o.stack ) ).sort( function( a, b ) {
  1005. return ( parseInt( $( a ).css( "zIndex" ), 10 ) || 0 ) -
  1006. ( parseInt( $( b ).css( "zIndex" ), 10 ) || 0 );
  1007. } );
  1008. if ( !group.length ) {
  1009. return;
  1010. }
  1011. min = parseInt( $( group[ 0 ] ).css( "zIndex" ), 10 ) || 0;
  1012. $( group ).each( function( i ) {
  1013. $( this ).css( "zIndex", min + i );
  1014. } );
  1015. this.css( "zIndex", ( min + group.length ) );
  1016. }
  1017. } );
  1018. $.ui.plugin.add( "draggable", "zIndex", {
  1019. start: function( event, ui, instance ) {
  1020. var t = $( ui.helper ),
  1021. o = instance.options;
  1022. if ( t.css( "zIndex" ) ) {
  1023. o._zIndex = t.css( "zIndex" );
  1024. }
  1025. t.css( "zIndex", o.zIndex );
  1026. },
  1027. stop: function( event, ui, instance ) {
  1028. var o = instance.options;
  1029. if ( o._zIndex ) {
  1030. $( ui.helper ).css( "zIndex", o._zIndex );
  1031. }
  1032. }
  1033. } );
  1034. return $.ui.draggable;
  1035. } );