cubicweb/web/data/jquery-treeview/jquery.treeview.sortable.js
changeset 11057 0b59724cb3f2
parent 10100 6718c03f8938
equal deleted inserted replaced
11052:058bb3dc685f 11057:0b59724cb3f2
       
     1 /*
       
     2  * jQuery UI Sortable
       
     3  *
       
     4  * Copyright (c) 2008 Paul Bakaus
       
     5  * Dual licensed under the MIT (MIT-LICENSE.txt)
       
     6  * and GPL (GPL-LICENSE.txt) licenses.
       
     7  *
       
     8  * http://docs.jquery.com/UI/Sortables
       
     9  *
       
    10  * Depends:
       
    11  *   ui.base.js
       
    12  *
       
    13  * Revision: $Id: ui.sortable.js 5262 2008-04-17 13:13:51Z paul.bakaus $
       
    14  */
       
    15 ;(function($) {
       
    16 
       
    17 	if (window.Node && Node.prototype && !Node.prototype.contains) {
       
    18 		Node.prototype.contains = function (arg) {
       
    19 			return !!(this.compareDocumentPosition(arg) & 16);
       
    20 		};
       
    21 	}
       
    22 
       
    23 
       
    24 	$.widget("ui.sortableTree", $.extend($.ui.mouse, {
       
    25 		init: function() {
       
    26 
       
    27 			//Initialize needed constants
       
    28 			var self = this, o = this.options;
       
    29 			this.containerCache = {};
       
    30 			this.element.addClass("ui-sortableTree");
       
    31 
       
    32 			//Get the items
       
    33 			this.refresh();
       
    34 
       
    35 			//Let's determine the parent's offset
       
    36 			if(!(/(relative|absolute|fixed)/).test(this.element.css('position'))) this.element.css('position', 'relative');
       
    37 			this.offset = this.element.offset();
       
    38 
       
    39 			//Initialize mouse events for interaction
       
    40 			this.mouseInit();
       
    41 
       
    42 			//Prepare cursorAt
       
    43 			if(o.cursorAt && o.cursorAt.constructor == Array)
       
    44 				o.cursorAt = { left: o.cursorAt[0], top: o.cursorAt[1] };
       
    45 
       
    46 		},
       
    47 		plugins: {},
       
    48 		ui: function(inst) {
       
    49 			return {
       
    50 				helper: (inst || this)["helper"],
       
    51 				position: (inst || this)["position"].current,
       
    52 				absolutePosition: (inst || this)["position"].absolute,
       
    53 				instance: this,
       
    54 				options: this.options,
       
    55 				element: this.element,
       
    56 				item: (inst || this)["currentItem"],
       
    57 				sender: inst ? inst.element : null
       
    58 			};
       
    59 		},
       
    60 		propagate: function(n,e,inst) {
       
    61 			$.ui.plugin.call(this, n, [e, this.ui(inst)]);
       
    62 			this.element.triggerHandler(n == "sort" ? n : "sort"+n, [e, this.ui(inst)], this.options[n]);
       
    63 		},
       
    64 		serialize: function(o) {
       
    65 
       
    66 			var items = $(this.options.items, this.element).not('.ui-sortableTree-helper'); //Only the items of the sortable itself
       
    67 			var str = []; o = o || {};
       
    68 
       
    69 			items.each(function() {
       
    70 				var res = ($(this).attr(o.attribute || 'id') || '').match(o.expression || (/(.+)[-=_](.+)/));
       
    71 				if(res) str.push((o.key || res[1])+'[]='+(o.key ? res[1] : res[2]));
       
    72 			});
       
    73 
       
    74 			return str.join('&');
       
    75 
       
    76 		},
       
    77 		toArray: function(attr) {
       
    78 			var items = $(this.options.items, this.element).not('.ui-sortableTree-helper'); //Only the items of the sortable itself
       
    79 			var ret = [];
       
    80 
       
    81 			items.each(function() { ret.push($(this).attr(attr || 'id')); });
       
    82 			return ret;
       
    83 		},
       
    84 		enable: function() {
       
    85 			this.element.removeClass("ui-sortableTree-disabled");
       
    86 			this.options.disabled = false;
       
    87 		},
       
    88 		disable: function() {
       
    89 			this.element.addClass("ui-sortableTree-disabled");
       
    90 			this.options.disabled = true;
       
    91 		},
       
    92 		/* Be careful with the following core functions */
       
    93 		intersectsWith: function(item) {
       
    94 
       
    95 			var x1 = this.position.absolute.left - 10, x2 = x1 + 10,
       
    96 			    y1 = this.position.absolute.top - 10, y2 = y1 + 10;
       
    97 			var l = item.left, r = l + item.width,
       
    98 			    t = item.top,  b = t + item.height;
       
    99 
       
   100 			return (   l < x1 + (this.helperProportions.width  / 2)    // Right Half
       
   101 				&&     x2 - (this.helperProportions.width  / 2) < r    // Left Half
       
   102 				&& t < y1 + (this.helperProportions.height / 2)        // Bottom Half
       
   103 				&&     y2 - (this.helperProportions.height / 2) < b ); // Top Half
       
   104 
       
   105 		},
       
   106 		intersectsWithEdge: function(item) {
       
   107 			var y1 = this.position.absolute.top - 10, y2 = y1 + 10;
       
   108 			var t = item.top,  b = t + item.height;
       
   109 
       
   110 			if(!this.intersectsWith(item.item.parents(".ui-sortableTree").data("sortableTree").containerCache)) return false;
       
   111 
       
   112 			if (!( t < y1 + (this.helperProportions.height / 2)        // Bottom Half
       
   113 				&&     y2 - (this.helperProportions.height / 2) < b )) return false; // Top Half
       
   114 
       
   115 			if(y2 > t && y1 < t) return 1; //Crosses top edge
       
   116 			if(y1 < b && y2 > b) return 2; //Crosses bottom edge
       
   117 
       
   118 			return false;
       
   119 
       
   120 		},
       
   121 		refresh: function() {
       
   122 			this.refreshItems();
       
   123 			this.refreshPositions();
       
   124 		},
       
   125 		refreshItems: function() {
       
   126 
       
   127 			this.items = [];
       
   128 			this.containers = [this];
       
   129 			var items = this.items;
       
   130 			var queries = [$(this.options.items, this.element)];
       
   131 
       
   132 			if(this.options.connectWith) {
       
   133 				for (var i = this.options.connectWith.length - 1; i >= 0; i--){
       
   134 					var cur = $(this.options.connectWith[i]);
       
   135 					for (var j = cur.length - 1; j >= 0; j--){
       
   136 						var inst = $.data(cur[j], 'sortableTree');
       
   137 						if(inst && !inst.options.disabled) {
       
   138 							queries.push($(inst.options.items, inst.element));
       
   139 							this.containers.push(inst);
       
   140 						}
       
   141 					};
       
   142 				};
       
   143 			}
       
   144 
       
   145 			for (var i = queries.length - 1; i >= 0; i--){
       
   146 				queries[i].each(function() {
       
   147 					$.data(this, 'sortableTree-item', true); // Data for target checking (mouse manager)
       
   148 					items.push({
       
   149 						item: $(this),
       
   150 						width: 0, height: 0,
       
   151 						left: 0, top: 0
       
   152 					});
       
   153 				});
       
   154 			};
       
   155 
       
   156 		},
       
   157 		refreshPositions: function(fast) {
       
   158 			for (var i = this.items.length - 1; i >= 0; i--){
       
   159 				if(!fast) this.items[i].height 			= this.items[i].item.outerHeight();
       
   160 				this.items[i].top 						= this.items[i].item.offset().top;
       
   161 			};
       
   162 			for (var i = this.containers.length - 1; i >= 0; i--){
       
   163 				var p =this.containers[i].element.offset();
       
   164 				this.containers[i].containerCache.left 	= p.left;
       
   165 				this.containers[i].containerCache.top 	= p.top;
       
   166 				this.containers[i].containerCache.width	= this.containers[i].element.outerWidth();
       
   167 				this.containers[i].containerCache.height= this.containers[i].element.outerHeight();
       
   168 			};
       
   169 		},
       
   170 		destroy: function() {
       
   171 
       
   172 			this.element
       
   173 				.removeClass("ui-sortableTree ui-sortableTree-disabled")
       
   174 				.removeData("sortableTree")
       
   175 				.unbind(".sortableTree");
       
   176 			this.mouseDestroy();
       
   177 
       
   178 			for ( var i = this.items.length - 1; i >= 0; i-- )
       
   179 				this.items[i].item.removeData("sortableTree-item");
       
   180 
       
   181 		},
       
   182 		contactContainers: function(e) {
       
   183 			for (var i = this.containers.length - 1; i >= 0; i--){
       
   184 
       
   185 				if(this.intersectsWith(this.containers[i].containerCache)) {
       
   186 					if(!this.containers[i].containerCache.over) {
       
   187 
       
   188 						if(this.currentContainer != this.containers[i]) {
       
   189 
       
   190 							//When entering a new container, we will find the item with the least distance and append our item near it
       
   191 							var dist = 10000; var itemWithLeastDistance = null; var base = this.position.absolute.top;
       
   192 							for (var j = this.items.length - 1; j >= 0; j--) {
       
   193 								if(!this.containers[i].element[0].contains(this.items[j].item[0])) continue;
       
   194 								var cur = this.items[j].top;
       
   195 								if(Math.abs(cur - base) < dist) {
       
   196 									dist = Math.abs(cur - base); itemWithLeastDistance = this.items[j];
       
   197 								}
       
   198 							}
       
   199 
       
   200 							itemWithLeastDistance ? this.rearrange(e, itemWithLeastDistance) : this.rearrange(e, null, this.containers[i].element);
       
   201 							this.propagate("change", e); //Call plugins and callbacks
       
   202 							this.containers[i].propagate("change", e, this); //Call plugins and callbacks
       
   203 							this.currentContainer = this.containers[i];
       
   204 
       
   205 						}
       
   206 
       
   207 						this.containers[i].propagate("over", e, this);
       
   208 						this.containers[i].containerCache.over = 1;
       
   209 					}
       
   210 				} else {
       
   211 					if(this.containers[i].containerCache.over) {
       
   212 						this.containers[i].propagate("out", e, this);
       
   213 						this.containers[i].containerCache.over = 0;
       
   214 					}
       
   215 				}
       
   216 
       
   217 			};
       
   218 		},
       
   219 		mouseStart: function(e,el) {
       
   220 
       
   221 			if(this.options.disabled || this.options.type == 'static') return false;
       
   222 
       
   223 			//Find out if the clicked node (or one of its parents) is a actual item in this.items
       
   224 			var currentItem = null, nodes = $(e.target).parents().each(function() {
       
   225 				if($.data(this, 'sortableTree-item')) {
       
   226 					currentItem = $(this);
       
   227 					return false;
       
   228 				}
       
   229 			});
       
   230 			if($.data(e.target, 'sortableTree-item')) currentItem = $(e.target);
       
   231 
       
   232 			if(!currentItem) return false;
       
   233 			if(this.options.handle) {
       
   234 				var validHandle = false;
       
   235 				$(this.options.handle, currentItem).each(function() { if(this == e.target) validHandle = true; });
       
   236 				if(!validHandle) return false;
       
   237 			}
       
   238 
       
   239 			this.currentItem = currentItem;
       
   240 
       
   241 			var o = this.options;
       
   242 			this.currentContainer = this;
       
   243 			this.refresh();
       
   244 
       
   245 			//Create and append the visible helper
       
   246 			this.helper = typeof o.helper == 'function' ? $(o.helper.apply(this.element[0], [e, this.currentItem])) : this.currentItem.clone();
       
   247 			if(!this.helper.parents('body').length) this.helper.appendTo("body"); //Add the helper to the DOM if that didn't happen already
       
   248 			this.helper.css({ position: 'absolute', clear: 'both' }).addClass('ui-sortableTree-helper'); //Position it absolutely and add a helper class
       
   249 
       
   250 			//Prepare variables for position generation
       
   251 			$.extend(this, {
       
   252 				offsetParent: this.helper.offsetParent(),
       
   253 				offsets: { absolute: this.currentItem.offset() }
       
   254 			});
       
   255 
       
   256 			//Save the first time position
       
   257 			$.extend(this, {
       
   258 				position: {
       
   259 					current: { left: e.pageX, top: e.pageY },
       
   260 					absolute: { left: e.pageX, top: e.pageY },
       
   261 					dom: this.currentItem.prev()[0]
       
   262 				},
       
   263 				clickOffset: { left: -5, top: -5 }
       
   264 			});
       
   265 
       
   266 			this.propagate("start", e); //Call plugins and callbacks
       
   267 			this.helperProportions = { width: this.helper.outerWidth(), height: this.helper.outerHeight() }; //Save and store the helper proportions
       
   268 
       
   269 			for (var i = this.containers.length - 1; i >= 0; i--) {
       
   270 				this.containers[i].propagate("activate", e, this);
       
   271 			} //Post 'activate' events to possible containers
       
   272 
       
   273 			//Prepare possible droppables
       
   274 			if($.ui.ddmanager) $.ui.ddmanager.current = this;
       
   275 			if ($.ui.ddmanager && !o.dropBehaviour) $.ui.ddmanager.prepareOffsets(this, e);
       
   276 
       
   277 			this.dragging = true;
       
   278 			return true;
       
   279 
       
   280 		},
       
   281 		mouseStop: function(e) {
       
   282 
       
   283 			if(this.newPositionAt) this.options.sortIndication.remove.call(this.currentItem, this.newPositionAt); //remove sort indicator
       
   284 			this.propagate("stop", e); //Call plugins and trigger callbacks
       
   285 
       
   286 			//If we are using droppables, inform the manager about the drop
       
   287 			var dropped = ($.ui.ddmanager && !this.options.dropBehaviour) ? $.ui.ddmanager.drop(this, e) : false;
       
   288 			if(!dropped && this.newPositionAt) this.newPositionAt[this.direction == 'down' ? 'before' : 'after'](this.currentItem); //Append to element to its new position
       
   289 
       
   290 			if(this.position.dom != this.currentItem.prev()[0]) this.propagate("update", e); //Trigger update callback if the DOM position has changed
       
   291 			if(!this.element[0].contains(this.currentItem[0])) { //Node was moved out of the current element
       
   292 				this.propagate("remove", e);
       
   293 				for (var i = this.containers.length - 1; i >= 0; i--){
       
   294 					if(this.containers[i].element[0].contains(this.currentItem[0])) {
       
   295 						this.containers[i].propagate("update", e, this);
       
   296 						this.containers[i].propagate("receive", e, this);
       
   297 					}
       
   298 				};
       
   299 			};
       
   300 
       
   301 			//Post events to containers
       
   302 			for (var i = this.containers.length - 1; i >= 0; i--){
       
   303 				this.containers[i].propagate("deactivate", e, this);
       
   304 				if(this.containers[i].containerCache.over) {
       
   305 					this.containers[i].propagate("out", e, this);
       
   306 					this.containers[i].containerCache.over = 0;
       
   307 				}
       
   308 			}
       
   309 
       
   310 			this.dragging = false;
       
   311 			if(this.cancelHelperRemoval) return false;
       
   312 			this.helper.remove();
       
   313 
       
   314 			return false;
       
   315 
       
   316 		},
       
   317 		mouseDrag: function(e) {
       
   318 
       
   319 			//Compute the helpers position
       
   320 			this.position.current = { top: e.pageY + 5, left: e.pageX + 5 };
       
   321 			this.position.absolute = { left: e.pageX + 5, top: e.pageY + 5 };
       
   322 
       
   323 			//Interconnect with droppables
       
   324 			if($.ui.ddmanager) $.ui.ddmanager.drag(this, e);
       
   325 			var intersectsWithDroppable = false;
       
   326 			$.each($.ui.ddmanager.droppables, function() {
       
   327 				if(this.isover) intersectsWithDroppable = true;
       
   328 			});
       
   329 
       
   330 			//Rearrange
       
   331 			if(intersectsWithDroppable) {
       
   332 				if(this.newPositionAt) this.options.sortIndication.remove.call(this.currentItem, this.newPositionAt);
       
   333 			} else {
       
   334 				for (var i = this.items.length - 1; i >= 0; i--) {
       
   335 
       
   336 					if(this.currentItem[0].contains(this.items[i].item[0])) continue;
       
   337 
       
   338 					var intersection = this.intersectsWithEdge(this.items[i]);
       
   339 					if(!intersection) continue;
       
   340 
       
   341 					this.direction = intersection == 1 ? "down" : "up";
       
   342 					this.rearrange(e, this.items[i]);
       
   343 					this.propagate("change", e); //Call plugins and callbacks
       
   344 					break;
       
   345 				}
       
   346 			}
       
   347 
       
   348 			//Post events to containers
       
   349 			this.contactContainers(e);
       
   350 
       
   351 			this.propagate("sort", e); //Call plugins and callbacks
       
   352 			this.helper.css({ left: this.position.current.left+'px', top: this.position.current.top+'px' }); // Stick the helper to the cursor
       
   353 			return false;
       
   354 
       
   355 		},
       
   356 		rearrange: function(e, i, a) {
       
   357 			if(i) {
       
   358 				if(this.newPositionAt) this.options.sortIndication.remove.call(this.currentItem, this.newPositionAt);
       
   359 				this.newPositionAt = i.item;
       
   360 				this.options.sortIndication[this.direction].call(this.currentItem, this.newPositionAt);
       
   361 			} else {
       
   362 				//Append
       
   363 			}
       
   364 		}
       
   365 	}));
       
   366 
       
   367 	$.extend($.ui.sortableTree, {
       
   368 		defaults: {
       
   369 			items: '> *',
       
   370 			zIndex: 1000,
       
   371 			distance: 1
       
   372 		},
       
   373 		getter: "serialize toArray"
       
   374 	});
       
   375 
       
   376 
       
   377 
       
   378 })(jQuery);