jquery-sortable.js 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694
  1. /* ===================================================
  2. * jquery-sortable.js v0.9.13
  3. * http://johnny.github.com/jquery-sortable/
  4. * ===================================================
  5. * Copyright (c) 2012 Jonas von Andrian
  6. * All rights reserved.
  7. *
  8. * Redistribution and use in source and binary forms, with or without
  9. * modification, are permitted provided that the following conditions are met:
  10. * * Redistributions of source code must retain the above copyright
  11. * notice, this list of conditions and the following disclaimer.
  12. * * Redistributions in binary form must reproduce the above copyright
  13. * notice, this list of conditions and the following disclaimer in the
  14. * documentation and/or other materials provided with the distribution.
  15. * * The name of the author may not be used to endorse or promote products
  16. * derived from this software without specific prior written permission.
  17. *
  18. * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
  19. * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
  20. * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
  21. * DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
  22. * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
  23. * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
  24. * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
  25. * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  26. * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
  27. * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  28. * ========================================================== */
  29. !function ( $, window, pluginName, undefined){
  30. var containerDefaults = {
  31. // If true, items can be dragged from this container
  32. drag: true,
  33. // If true, items can be droped onto this container
  34. drop: true,
  35. // Exclude items from being draggable, if the
  36. // selector matches the item
  37. exclude: "",
  38. // If true, search for nested containers within an item.If you nest containers,
  39. // either the original selector with which you call the plugin must only match the top containers,
  40. // or you need to specify a group (see the bootstrap nav example)
  41. nested: true,
  42. // If true, the items are assumed to be arranged vertically
  43. vertical: true
  44. }, // end container defaults
  45. groupDefaults = {
  46. // This is executed after the placeholder has been moved.
  47. // $closestItemOrContainer contains the closest item, the placeholder
  48. // has been put at or the closest empty Container, the placeholder has
  49. // been appended to.
  50. afterMove: function ($placeholder, container, $closestItemOrContainer) {
  51. },
  52. // The exact css path between the container and its items, e.g. "> tbody"
  53. containerPath: "",
  54. // The css selector of the containers
  55. containerSelector: "ol, ul",
  56. // Distance the mouse has to travel to start dragging
  57. distance: 0,
  58. // Time in milliseconds after mousedown until dragging should start.
  59. // This option can be used to prevent unwanted drags when clicking on an element.
  60. delay: 0,
  61. // The css selector of the drag handle
  62. handle: "",
  63. // The exact css path between the item and its subcontainers.
  64. // It should only match the immediate items of a container.
  65. // No item of a subcontainer should be matched. E.g. for ol>div>li the itemPath is "> div"
  66. itemPath: "",
  67. // The css selector of the items
  68. itemSelector: "li",
  69. // The class given to "body" while an item is being dragged
  70. bodyClass: "dragging",
  71. // The class giving to an item while being dragged
  72. draggedClass: "dragged",
  73. // Check if the dragged item may be inside the container.
  74. // Use with care, since the search for a valid container entails a depth first search
  75. // and may be quite expensive.
  76. isValidTarget: function ($item, container) {
  77. return true
  78. },
  79. // Executed before onDrop if placeholder is detached.
  80. // This happens if pullPlaceholder is set to false and the drop occurs outside a container.
  81. onCancel: function ($item, container, _super, event) {
  82. },
  83. // Executed at the beginning of a mouse move event.
  84. // The Placeholder has not been moved yet.
  85. onDrag: function ($item, position, _super, event) {
  86. $item.css(position)
  87. },
  88. // Called after the drag has been started,
  89. // that is the mouse button is being held down and
  90. // the mouse is moving.
  91. // The container is the closest initialized container.
  92. // Therefore it might not be the container, that actually contains the item.
  93. onDragStart: function ($item, container, _super, event) {
  94. $item.css({
  95. height: $item.outerHeight(),
  96. width: $item.outerWidth()
  97. })
  98. $item.addClass(container.group.options.draggedClass)
  99. $("body").addClass(container.group.options.bodyClass)
  100. },
  101. // Called when the mouse button is being released
  102. onDrop: function ($item, container, _super, event) {
  103. $item.removeClass(container.group.options.draggedClass).removeAttr("style")
  104. $("body").removeClass(container.group.options.bodyClass)
  105. },
  106. // Called on mousedown. If falsy value is returned, the dragging will not start.
  107. // Ignore if element clicked is input, select or textarea
  108. onMousedown: function ($item, _super, event) {
  109. if (!event.target.nodeName.match(/^(input|select|textarea)$/i)) {
  110. event.preventDefault()
  111. return true
  112. }
  113. },
  114. // The class of the placeholder (must match placeholder option markup)
  115. placeholderClass: "placeholder",
  116. // Template for the placeholder. Can be any valid jQuery input
  117. // e.g. a string, a DOM element.
  118. // The placeholder must have the class "placeholder"
  119. placeholder: '<li class="placeholder"></li>',
  120. // If true, the position of the placeholder is calculated on every mousemove.
  121. // If false, it is only calculated when the mouse is above a container.
  122. pullPlaceholder: true,
  123. // Specifies serialization of the container group.
  124. // The pair $parent/$children is either container/items or item/subcontainers.
  125. serialize: function ($parent, $children, parentIsContainer) {
  126. var result = $.extend({}, $parent.data())
  127. if(parentIsContainer)
  128. return [$children]
  129. else if ($children[0]){
  130. result.children = $children
  131. }
  132. delete result.subContainers
  133. delete result.sortable
  134. return result
  135. },
  136. // Set tolerance while dragging. Positive values decrease sensitivity,
  137. // negative values increase it.
  138. tolerance: 0
  139. }, // end group defaults
  140. containerGroups = {},
  141. groupCounter = 0,
  142. emptyBox = {
  143. left: 0,
  144. top: 0,
  145. bottom: 0,
  146. right:0
  147. },
  148. eventNames = {
  149. start: "touchstart.sortable mousedown.sortable",
  150. drop: "touchend.sortable touchcancel.sortable mouseup.sortable",
  151. drag: "touchmove.sortable mousemove.sortable",
  152. scroll: "scroll.sortable"
  153. },
  154. subContainerKey = "subContainers"
  155. /*
  156. * a is Array [left, right, top, bottom]
  157. * b is array [left, top]
  158. */
  159. function d(a,b) {
  160. var x = Math.max(0, a[0] - b[0], b[0] - a[1]),
  161. y = Math.max(0, a[2] - b[1], b[1] - a[3])
  162. return x+y;
  163. }
  164. function setDimensions(array, dimensions, tolerance, useOffset) {
  165. var i = array.length,
  166. offsetMethod = useOffset ? "offset" : "position"
  167. tolerance = tolerance || 0
  168. while(i--){
  169. var el = array[i].el ? array[i].el : $(array[i]),
  170. // use fitting method
  171. pos = el[offsetMethod]()
  172. pos.left += parseInt(el.css('margin-left'), 10)
  173. pos.top += parseInt(el.css('margin-top'),10)
  174. dimensions[i] = [
  175. pos.left - tolerance,
  176. pos.left + el.outerWidth() + tolerance,
  177. pos.top - tolerance,
  178. pos.top + el.outerHeight() + tolerance
  179. ]
  180. }
  181. }
  182. function getRelativePosition(pointer, element) {
  183. var offset = element.offset()
  184. return {
  185. left: pointer.left - offset.left,
  186. top: pointer.top - offset.top
  187. }
  188. }
  189. function sortByDistanceDesc(dimensions, pointer, lastPointer) {
  190. pointer = [pointer.left, pointer.top]
  191. lastPointer = lastPointer && [lastPointer.left, lastPointer.top]
  192. var dim,
  193. i = dimensions.length,
  194. distances = []
  195. while(i--){
  196. dim = dimensions[i]
  197. distances[i] = [i,d(dim,pointer), lastPointer && d(dim, lastPointer)]
  198. }
  199. distances = distances.sort(function (a,b) {
  200. return b[1] - a[1] || b[2] - a[2] || b[0] - a[0]
  201. })
  202. // last entry is the closest
  203. return distances
  204. }
  205. function ContainerGroup(options) {
  206. this.options = $.extend({}, groupDefaults, options)
  207. this.containers = []
  208. if(!this.options.rootGroup){
  209. this.scrollProxy = $.proxy(this.scroll, this)
  210. this.dragProxy = $.proxy(this.drag, this)
  211. this.dropProxy = $.proxy(this.drop, this)
  212. this.placeholder = $(this.options.placeholder)
  213. if(!options.isValidTarget)
  214. this.options.isValidTarget = undefined
  215. }
  216. }
  217. ContainerGroup.get = function (options) {
  218. if(!containerGroups[options.group]) {
  219. if(options.group === undefined)
  220. options.group = groupCounter ++
  221. containerGroups[options.group] = new ContainerGroup(options)
  222. }
  223. return containerGroups[options.group]
  224. }
  225. ContainerGroup.prototype = {
  226. dragInit: function (e, itemContainer) {
  227. this.$document = $(itemContainer.el[0].ownerDocument)
  228. // get item to drag
  229. var closestItem = $(e.target).closest(this.options.itemSelector);
  230. // using the length of this item, prevents the plugin from being started if there is no handle being clicked on.
  231. // this may also be helpful in instantiating multidrag.
  232. if (closestItem.length) {
  233. this.item = closestItem;
  234. this.itemContainer = itemContainer;
  235. if (this.item.is(this.options.exclude) || !this.options.onMousedown(this.item, groupDefaults.onMousedown, e)) {
  236. return;
  237. }
  238. this.setPointer(e);
  239. this.toggleListeners('on');
  240. this.setupDelayTimer();
  241. this.dragInitDone = true;
  242. }
  243. },
  244. drag: function (e) {
  245. if(!this.dragging){
  246. if(!this.distanceMet(e) || !this.delayMet)
  247. return
  248. this.options.onDragStart(this.item, this.itemContainer, groupDefaults.onDragStart, e)
  249. this.item.before(this.placeholder)
  250. this.dragging = true
  251. }
  252. this.setPointer(e)
  253. // place item under the cursor
  254. this.options.onDrag(this.item,
  255. getRelativePosition(this.pointer, this.item.offsetParent()),
  256. groupDefaults.onDrag,
  257. e)
  258. var p = this.getPointer(e),
  259. box = this.sameResultBox,
  260. t = this.options.tolerance
  261. if(!box || box.top - t > p.top || box.bottom + t < p.top || box.left - t > p.left || box.right + t < p.left)
  262. if(!this.searchValidTarget()){
  263. this.placeholder.detach()
  264. this.lastAppendedItem = undefined
  265. }
  266. },
  267. drop: function (e) {
  268. this.toggleListeners('off')
  269. this.dragInitDone = false
  270. if(this.dragging){
  271. // processing Drop, check if placeholder is detached
  272. if(this.placeholder.closest("html")[0]){
  273. this.placeholder.before(this.item).detach()
  274. } else {
  275. this.options.onCancel(this.item, this.itemContainer, groupDefaults.onCancel, e)
  276. }
  277. this.options.onDrop(this.item, this.getContainer(this.item), groupDefaults.onDrop, e)
  278. // cleanup
  279. this.clearDimensions()
  280. this.clearOffsetParent()
  281. this.lastAppendedItem = this.sameResultBox = undefined
  282. this.dragging = false
  283. }
  284. },
  285. searchValidTarget: function (pointer, lastPointer) {
  286. if(!pointer){
  287. pointer = this.relativePointer || this.pointer
  288. lastPointer = this.lastRelativePointer || this.lastPointer
  289. }
  290. var distances = sortByDistanceDesc(this.getContainerDimensions(),
  291. pointer,
  292. lastPointer),
  293. i = distances.length
  294. while(i--){
  295. var index = distances[i][0],
  296. distance = distances[i][1]
  297. if(!distance || this.options.pullPlaceholder){
  298. var container = this.containers[index]
  299. if(!container.disabled){
  300. if(!this.$getOffsetParent()){
  301. var offsetParent = container.getItemOffsetParent()
  302. pointer = getRelativePosition(pointer, offsetParent)
  303. lastPointer = getRelativePosition(lastPointer, offsetParent)
  304. }
  305. if(container.searchValidTarget(pointer, lastPointer))
  306. return true
  307. }
  308. }
  309. }
  310. if(this.sameResultBox)
  311. this.sameResultBox = undefined
  312. },
  313. movePlaceholder: function (container, item, method, sameResultBox) {
  314. var lastAppendedItem = this.lastAppendedItem
  315. if(!sameResultBox && lastAppendedItem && lastAppendedItem[0] === item[0])
  316. return;
  317. item[method](this.placeholder)
  318. this.lastAppendedItem = item
  319. this.sameResultBox = sameResultBox
  320. this.options.afterMove(this.placeholder, container, item)
  321. },
  322. getContainerDimensions: function () {
  323. if(!this.containerDimensions)
  324. setDimensions(this.containers, this.containerDimensions = [], this.options.tolerance, !this.$getOffsetParent())
  325. return this.containerDimensions
  326. },
  327. getContainer: function (element) {
  328. return element.closest(this.options.containerSelector).data(pluginName)
  329. },
  330. $getOffsetParent: function () {
  331. if(this.offsetParent === undefined){
  332. var i = this.containers.length - 1,
  333. offsetParent = this.containers[i].getItemOffsetParent()
  334. if(!this.options.rootGroup){
  335. while(i--){
  336. if(offsetParent[0] != this.containers[i].getItemOffsetParent()[0]){
  337. // If every container has the same offset parent,
  338. // use position() which is relative to this parent,
  339. // otherwise use offset()
  340. // compare #setDimensions
  341. offsetParent = false
  342. break;
  343. }
  344. }
  345. }
  346. this.offsetParent = offsetParent
  347. }
  348. return this.offsetParent
  349. },
  350. setPointer: function (e) {
  351. var pointer = this.getPointer(e)
  352. if(this.$getOffsetParent()){
  353. var relativePointer = getRelativePosition(pointer, this.$getOffsetParent())
  354. this.lastRelativePointer = this.relativePointer
  355. this.relativePointer = relativePointer
  356. }
  357. this.lastPointer = this.pointer
  358. this.pointer = pointer
  359. },
  360. distanceMet: function (e) {
  361. var currentPointer = this.getPointer(e)
  362. return (Math.max(
  363. Math.abs(this.pointer.left - currentPointer.left),
  364. Math.abs(this.pointer.top - currentPointer.top)
  365. ) >= this.options.distance)
  366. },
  367. getPointer: function(e) {
  368. var o = e.originalEvent || e.originalEvent.touches && e.originalEvent.touches[0]
  369. return {
  370. left: e.pageX || o.pageX,
  371. top: e.pageY || o.pageY
  372. }
  373. },
  374. setupDelayTimer: function () {
  375. var that = this
  376. this.delayMet = !this.options.delay
  377. // init delay timer if needed
  378. if (!this.delayMet) {
  379. clearTimeout(this._mouseDelayTimer);
  380. this._mouseDelayTimer = setTimeout(function() {
  381. that.delayMet = true
  382. }, this.options.delay)
  383. }
  384. },
  385. scroll: function (e) {
  386. this.clearDimensions()
  387. this.clearOffsetParent() // TODO is this needed?
  388. },
  389. toggleListeners: function (method) {
  390. var that = this,
  391. events = ['drag','drop','scroll']
  392. $.each(events,function (i,event) {
  393. that.$document[method](eventNames[event], that[event + 'Proxy'])
  394. })
  395. },
  396. clearOffsetParent: function () {
  397. this.offsetParent = undefined
  398. },
  399. // Recursively clear container and item dimensions
  400. clearDimensions: function () {
  401. this.traverse(function(object){
  402. object._clearDimensions()
  403. })
  404. },
  405. traverse: function(callback) {
  406. callback(this)
  407. var i = this.containers.length
  408. while(i--){
  409. this.containers[i].traverse(callback)
  410. }
  411. },
  412. _clearDimensions: function(){
  413. this.containerDimensions = undefined
  414. },
  415. _destroy: function () {
  416. containerGroups[this.options.group] = undefined
  417. }
  418. }
  419. function Container(element, options) {
  420. this.el = element
  421. this.options = $.extend( {}, containerDefaults, options)
  422. this.group = ContainerGroup.get(this.options)
  423. this.rootGroup = this.options.rootGroup || this.group
  424. this.handle = this.rootGroup.options.handle || this.rootGroup.options.itemSelector
  425. var itemPath = this.rootGroup.options.itemPath
  426. this.target = itemPath ? this.el.find(itemPath) : this.el
  427. this.target.on(eventNames.start, this.handle, $.proxy(this.dragInit, this))
  428. if(this.options.drop)
  429. this.group.containers.push(this)
  430. }
  431. Container.prototype = {
  432. dragInit: function (e) {
  433. var rootGroup = this.rootGroup
  434. if( !this.disabled &&
  435. !rootGroup.dragInitDone &&
  436. this.options.drag &&
  437. this.isValidDrag(e)) {
  438. rootGroup.dragInit(e, this)
  439. }
  440. },
  441. isValidDrag: function(e) {
  442. return e.which == 1 ||
  443. e.type == "touchstart" && e.originalEvent.touches.length == 1
  444. },
  445. searchValidTarget: function (pointer, lastPointer) {
  446. var distances = sortByDistanceDesc(this.getItemDimensions(),
  447. pointer,
  448. lastPointer),
  449. i = distances.length,
  450. rootGroup = this.rootGroup,
  451. validTarget = !rootGroup.options.isValidTarget ||
  452. rootGroup.options.isValidTarget(rootGroup.item, this)
  453. if(!i && validTarget){
  454. rootGroup.movePlaceholder(this, this.target, "append")
  455. return true
  456. } else
  457. while(i--){
  458. var index = distances[i][0],
  459. distance = distances[i][1]
  460. if(!distance && this.hasChildGroup(index)){
  461. var found = this.getContainerGroup(index).searchValidTarget(pointer, lastPointer)
  462. if(found)
  463. return true
  464. }
  465. else if(validTarget){
  466. this.movePlaceholder(index, pointer)
  467. return true
  468. }
  469. }
  470. },
  471. movePlaceholder: function (index, pointer) {
  472. var item = $(this.items[index]),
  473. dim = this.itemDimensions[index],
  474. method = "after",
  475. width = item.outerWidth(),
  476. height = item.outerHeight(),
  477. offset = item.offset(),
  478. sameResultBox = {
  479. left: offset.left,
  480. right: offset.left + width,
  481. top: offset.top,
  482. bottom: offset.top + height
  483. }
  484. if(this.options.vertical){
  485. var yCenter = (dim[2] + dim[3]) / 2,
  486. inUpperHalf = pointer.top <= yCenter
  487. if(inUpperHalf){
  488. method = "before"
  489. sameResultBox.bottom -= height / 2
  490. } else
  491. sameResultBox.top += height / 2
  492. } else {
  493. var xCenter = (dim[0] + dim[1]) / 2,
  494. inLeftHalf = pointer.left <= xCenter
  495. if(inLeftHalf){
  496. method = "before"
  497. sameResultBox.right -= width / 2
  498. } else
  499. sameResultBox.left += width / 2
  500. }
  501. if(this.hasChildGroup(index))
  502. sameResultBox = emptyBox
  503. this.rootGroup.movePlaceholder(this, item, method, sameResultBox)
  504. },
  505. getItemDimensions: function () {
  506. if(!this.itemDimensions){
  507. this.items = this.$getChildren(this.el, "item").filter(
  508. ":not(." + this.group.options.placeholderClass + ", ." + this.group.options.draggedClass + ")"
  509. ).get()
  510. setDimensions(this.items, this.itemDimensions = [], this.options.tolerance)
  511. }
  512. return this.itemDimensions
  513. },
  514. getItemOffsetParent: function () {
  515. var offsetParent,
  516. el = this.el
  517. // Since el might be empty we have to check el itself and
  518. // can not do something like el.children().first().offsetParent()
  519. if(el.css("position") === "relative" || el.css("position") === "absolute" || el.css("position") === "fixed")
  520. offsetParent = el
  521. else
  522. offsetParent = el.offsetParent()
  523. return offsetParent
  524. },
  525. hasChildGroup: function (index) {
  526. return this.options.nested && this.getContainerGroup(index)
  527. },
  528. getContainerGroup: function (index) {
  529. var childGroup = $.data(this.items[index], subContainerKey)
  530. if( childGroup === undefined){
  531. var childContainers = this.$getChildren(this.items[index], "container")
  532. childGroup = false
  533. if(childContainers[0]){
  534. var options = $.extend({}, this.options, {
  535. rootGroup: this.rootGroup,
  536. group: groupCounter ++
  537. })
  538. childGroup = childContainers[pluginName](options).data(pluginName).group
  539. }
  540. $.data(this.items[index], subContainerKey, childGroup)
  541. }
  542. return childGroup
  543. },
  544. $getChildren: function (parent, type) {
  545. var options = this.rootGroup.options,
  546. path = options[type + "Path"],
  547. selector = options[type + "Selector"]
  548. parent = $(parent)
  549. if(path)
  550. parent = parent.find(path)
  551. return parent.children(selector)
  552. },
  553. _serialize: function (parent, isContainer) {
  554. var that = this,
  555. childType = isContainer ? "item" : "container",
  556. children = this.$getChildren(parent, childType).not(this.options.exclude).map(function () {
  557. return that._serialize($(this), !isContainer)
  558. }).get()
  559. return this.rootGroup.options.serialize(parent, children, isContainer)
  560. },
  561. traverse: function(callback) {
  562. $.each(this.items || [], function(item){
  563. var group = $.data(this, subContainerKey)
  564. if(group)
  565. group.traverse(callback)
  566. });
  567. callback(this)
  568. },
  569. _clearDimensions: function () {
  570. this.itemDimensions = undefined
  571. },
  572. _destroy: function() {
  573. var that = this;
  574. this.target.off(eventNames.start, this.handle);
  575. this.el.removeData(pluginName)
  576. if(this.options.drop)
  577. this.group.containers = $.grep(this.group.containers, function(val){
  578. return val != that
  579. })
  580. $.each(this.items || [], function(){
  581. $.removeData(this, subContainerKey)
  582. })
  583. }
  584. }
  585. var API = {
  586. enable: function() {
  587. this.traverse(function(object){
  588. object.disabled = false
  589. })
  590. },
  591. disable: function (){
  592. this.traverse(function(object){
  593. object.disabled = true
  594. })
  595. },
  596. serialize: function () {
  597. return this._serialize(this.el, true)
  598. },
  599. refresh: function() {
  600. this.traverse(function(object){
  601. object._clearDimensions()
  602. })
  603. },
  604. destroy: function () {
  605. this.traverse(function(object){
  606. object._destroy();
  607. })
  608. }
  609. }
  610. $.extend(Container.prototype, API)
  611. /**
  612. * jQuery API
  613. *
  614. * Parameters are
  615. * either options on init
  616. * or a method name followed by arguments to pass to the method
  617. */
  618. $.fn[pluginName] = function(methodOrOptions) {
  619. var args = Array.prototype.slice.call(arguments, 1)
  620. return this.map(function(){
  621. var $t = $(this),
  622. object = $t.data(pluginName)
  623. if(object && API[methodOrOptions])
  624. return API[methodOrOptions].apply(object, args) || this
  625. else if(!object && (methodOrOptions === undefined ||
  626. typeof methodOrOptions === "object"))
  627. $t.data(pluginName, new Container($t, methodOrOptions))
  628. return this
  629. });
  630. };
  631. }(jQuery, window, 'sortable');