ui.sortable.js 7.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262
  1. if (window.Node && Node.prototype && !Node.prototype.contains) {
  2. Node.prototype.contains = function (arg) {
  3. return !!(this.compareDocumentPosition(arg) & 16)
  4. }
  5. }
  6. (function($) {
  7. //Make nodes selectable by expression
  8. $.extend($.expr[':'], { sortable: "(' '+a.className+' ').indexOf(' ui-sortable ')" });
  9. $.fn.sortable = function(o) {
  10. return this.each(function() {
  11. new $.ui.sortable(this,o);
  12. });
  13. }
  14. //Macros for external methods that support chaining
  15. var methods = "destroy,enable,disable,refresh".split(",");
  16. for(var i=0;i<methods.length;i++) {
  17. var cur = methods[i], f;
  18. eval('f = function() { var a = arguments; return this.each(function() { if(jQuery(this).is(".ui-sortable")) jQuery.data(this, "ui-sortable")["'+cur+'"](a); }); }');
  19. $.fn["sortable"+cur.substr(0,1).toUpperCase()+cur.substr(1)] = f;
  20. };
  21. //get instance method
  22. $.fn.sortableInstance = function() {
  23. if($(this[0]).is(".ui-sortable")) return $.data(this[0], "ui-sortable");
  24. return false;
  25. };
  26. $.ui.sortable = function(el,o) {
  27. this.element = el;
  28. this.set = [];
  29. var options = {};
  30. var self = this;
  31. $.data(this.element, "ui-sortable", this);
  32. $(el).addClass("ui-sortable");
  33. $.extend(options, o);
  34. $.extend(options, {
  35. items: options.items || '> li',
  36. smooth: options.smooth != undefined ? options.smooth : true,
  37. helper: 'clone',
  38. containment: options.containment ? (options.containment == 'sortable' ? el : options.containment) : null,
  39. zIndex: options.zIndex || 1000,
  40. _start: function(h,p,c,t,e) {
  41. self.start.apply(t, [self, e]); // Trigger the onStart callback
  42. },
  43. _beforeStop: function(h,p,c,t,e) {
  44. self.stop.apply(t, [self, e]); // Trigger the onStart callback
  45. },
  46. _drag: function(h,p,c,t,e) {
  47. self.drag.apply(t, [self, e]); // Trigger the onStart callback
  48. },
  49. startCondition: function() {
  50. return !self.disabled;
  51. }
  52. });
  53. //Get the items
  54. var items = $(options.items, el);
  55. //Let's determine the floating mode
  56. options.floating = /left|right/.test(items.css('float'));
  57. //Let's determine the parent's offset
  58. if($(el).css('position') == 'static') $(el).css('position', 'relative');
  59. options.offset = $(el).offset({ border: false });
  60. items.each(function() {
  61. new $.ui.mouseInteraction(this,options);
  62. });
  63. //Add current items to the set
  64. items.each(function() {
  65. self.set.push([this,null]);
  66. });
  67. this.options = options;
  68. }
  69. $.extend($.ui.sortable.prototype, {
  70. plugins: {},
  71. currentTarget: null,
  72. lastTarget: null,
  73. prepareCallbackObj: function(self, that) {
  74. return {
  75. helper: self.helper,
  76. position: { left: self.pos[0], top: self.pos[1] },
  77. offset: self.options.cursorAt,
  78. draggable: self,
  79. current: that,
  80. options: self.options
  81. }
  82. },
  83. refresh: function() {
  84. //Get the items
  85. var self = this;
  86. var items = $(this.options.items, this.element);
  87. var unique = [];
  88. items.each(function() {
  89. old = false;
  90. for(var i=0;i<self.set.length;i++) { if(self.set[i][0] == this) old = true; }
  91. if(!old) unique.push(this);
  92. });
  93. for(var i=0;i<unique.length;i++) {
  94. new $.ui.mouseInteraction(unique[i],self.options);
  95. }
  96. //Add current items to the set
  97. this.set = [];
  98. items.each(function() {
  99. self.set.push([this,null]);
  100. });
  101. },
  102. destroy: function() {
  103. $(this.element).removeClass("ui-sortable").removeClass("ui-sortable-disabled");
  104. $(this.options.items, this.element).mouseInteractionDestroy();
  105. },
  106. enable: function() {
  107. $(this.element).removeClass("ui-sortable-disabled");
  108. this.disabled = false;
  109. },
  110. disable: function() {
  111. $(this.element).addClass("ui-sortable-disabled");
  112. this.disabled = true;
  113. },
  114. start: function(that, e) {
  115. var o = this.options;
  116. if(o.hoverClass) {
  117. that.helper = $('<div class="'+o.hoverClass+'"></div>').appendTo('body').css({
  118. height: this.element.offsetHeight+'px',
  119. width: this.element.offsetWidth+'px',
  120. position: 'absolute'
  121. });
  122. }
  123. if(o.zIndex) {
  124. if($(this.helper).css("zIndex")) o.ozIndex = $(this.helper).css("zIndex");
  125. $(this.helper).css('zIndex', o.zIndex);
  126. }
  127. that.firstSibling = $(this.element).prev()[0];
  128. $(this.element).triggerHandler("sortstart", [e, that.prepareCallbackObj(this)], o.start);
  129. $(this.element).css('visibility', 'hidden');
  130. return false;
  131. },
  132. stop: function(that, e) {
  133. var o = this.options;
  134. var self = this;
  135. if(o.smooth) {
  136. var os = $(this.element).offset();
  137. o.beQuietAtEnd = true;
  138. $(this.helper).animate({ left: os.left - o.po.left, top: os.top - o.po.top }, 500, stopIt);
  139. } else {
  140. stopIt();
  141. }
  142. function stopIt() {
  143. $(self.element).css('visibility', 'visible');
  144. if(that.helper) that.helper.remove();
  145. if(self.helper != self.element) $(self.helper).remove();
  146. if(o.ozIndex)
  147. $(self.helper).css('zIndex', o.ozIndex);
  148. //Let's see if the position in DOM has changed
  149. if($(self.element).prev()[0] != that.firstSibling) {
  150. //$(self.element).triggerHandler("sortupdate", [e, that.prepareCallbackObj(self, that)], o.update);
  151. }
  152. }
  153. return false;
  154. },
  155. drag: function(that, e) {
  156. var o = this.options;
  157. this.pos = [this.pos[0]-(o.cursorAt.left ? o.cursorAt.left : 0), this.pos[1]-(o.cursorAt.top ? o.cursorAt.top : 0)];
  158. var nv = $(this.element).triggerHandler("sort", [e, that.prepareCallbackObj(this)], o.sort);
  159. var nl = (nv && nv.left) ? nv.left : this.pos[0];
  160. var nt = (nv && nv.top) ? nv.top : this.pos[1];
  161. var m = that.set;
  162. var p = this.pos[1];
  163. for(var i=0;i<m.length;i++) {
  164. var ci = $(m[i][0]); var cio = m[i][0];
  165. if(this.element.contains(cio)) continue;
  166. var cO = ci.offset(); //TODO: Caching
  167. cO = { top: cO.top, left: cO.left };
  168. var mb = function(e) { if(true || o.lba != cio) { ci.before(e); o.lba = cio; } }
  169. var ma = function(e) { if(true || o.laa != cio) { ci.after(e); o.laa = cio; } }
  170. if(o.floating) {
  171. var overlap = ((cO.left - (this.pos[0]+(this.options.po ? this.options.po.left : 0)))/this.helper.offsetWidth);
  172. if(!(cO.top < this.pos[1]+(this.options.po ? this.options.po.top : 0) + cio.offsetHeight/2 && cO.top + cio.offsetHeight > this.pos[1]+(this.options.po ? this.options.po.top : 0) + cio.offsetHeight/2)) continue;
  173. } else {
  174. var overlap = ((cO.top - (this.pos[1]+(this.options.po ? this.options.po.top : 0)))/this.helper.offsetHeight);
  175. if(!(cO.left < this.pos[0]+(this.options.po ? this.options.po.left : 0) + cio.offsetWidth/2 && cO.left + cio.offsetWidth > this.pos[0]+(this.options.po ? this.options.po.left : 0) + cio.offsetWidth/2)) continue;
  176. }
  177. if(overlap >= 0 && overlap <= 0.5) { //Overlapping at top
  178. ci.prev().length ? ma(this.element) : mb(this.element);
  179. }
  180. if(overlap < 0 && overlap > -0.5) { //Overlapping at bottom
  181. ci.next()[0] == this.element ? mb(this.element) : ma(this.element);
  182. }
  183. }
  184. //Let's see if the position in DOM has changed
  185. if($(this.element).prev()[0] != that.lastSibling) {
  186. $(this.element).triggerHandler("sortchange", [e, that.prepareCallbackObj(this, that)], this.options.change);
  187. that.lastSibling = $(this.element).prev()[0];
  188. }
  189. if(that.helper) { //reposition helper if available
  190. var to = $(this.element).offset();
  191. that.helper.css({
  192. top: to.top+'px',
  193. left: to.left+'px'
  194. });
  195. }
  196. $(this.helper).css('left', nl+'px').css('top', nt+'px'); // Stick the helper to the cursor
  197. return false;
  198. }
  199. });
  200. })($);