PageSourceSearch

http://c64.sk/js/inestedsortable.js

js c64.sk collected 2026-10-02 12:54:24 UTC 18,926 bytes, 561 lines download raw bytes

1/**
2 * 
3 * Nested Sortable Plugin for jQuery/Interface.
4 * 
5 * Version 1.0.1
6 *  
7 *Change Log:
8 * 1.0 
9 *       Initial Release
10 * 1.0.1
11 *       Added noNestingClass option to prevent nesting in some elements.
12 *
13 *
14 * Copyright (c) 2007 Bernardo de Padua dos Santos
15 * Dual licensed under the MIT (MIT-LICENSE.txt) 
16 * and GPL (GPL-LICENSE.txt) licenses.
17 * 
18 *  http://code.google.com/p/nestedsortables/
19 * 
20 */
21jQuery.iNestedSortable = {
22	/*
23	 * Called when an item is being dragged. Puts the sort helper in the proper place.
24	 * The "e" argument passed in is the Sortable element.
25	 */
26	checkHover : function(e,o) {
27		if(e.isNestedSortable){
28			//A brand new NestedSortable hovering check
29			jQuery.iNestedSortable.scroll(e);
30			return jQuery.iNestedSortable.newCheckHover(e);
31		} else {
32			//The legacy hovering check performed by plain Sortables
33			//I don't think "o" is even used, but it is in the function declaration
34			return jQuery.iNestedSortable.oldCheckHover(e,o);
35		}
36	},
37	oldCheckHover : jQuery.iSort.checkhover,
38	newCheckHover : function (e) {
39		if (!jQuery.iDrag.dragged) {
40			return;
41		}
42		if ( !(e.dropCfg.el.size() > 0) ) {
43			return;
44		}
45			
46		//we need to recalculate the position of the sortables, 
47		//or there will be some mismatches
48		if(!e.nestedSortCfg.remeasured) {
49			jQuery.iSort.measure(e);
50			e.nestedSortCfg.remeasured = true;
51		}
52		
53		//finds item that is on top of the one being dragged,
54		//and in a compatible nesting level
55		var precItem = jQuery.iNestedSortable.findPrecedingItem(e);
56		
57		var shouldNest = jQuery.iNestedSortable.shouldNestItem(e, precItem);
58		var touchingFirst = (!precItem) ? jQuery.iNestedSortable.isTouchingFirstItem(e) : false;
59		var quit = false;
60		
61		//avoids doing things more than once
62		if(precItem) {
63			if(e.nestedSortCfg.lastPrecedingItem === precItem && e.nestedSortCfg.lastShouldNest === shouldNest) {
64				quit = true;
65			}
66		}
67		else if(e.nestedSortCfg.lastPrecedingItem === precItem && e.nestedSortCfg.lastTouchingFirst === touchingFirst) {
68			quit = true;
69		}
70		e.nestedSortCfg.lastPrecedingItem = precItem;
71		e.nestedSortCfg.lastShouldNest = shouldNest;
72		e.nestedSortCfg.lastTouchingFirst = touchingFirst;
73		if(quit) {return;}
74		
75		if (precItem !== null) {
76			//there is an element on top of this one 
77			//on the same nesting, or smaller
78			if (shouldNest) {
79				jQuery.iNestedSortable.nestItem(e, precItem);
80			} else  {
81				jQuery.iNestedSortable.appendItem(e, precItem);
82			}
83		} else if (touchingFirst) {
84			//no element on top, but touches the first item on the list
85			jQuery.iNestedSortable.insertOnTop(e);	
86		}
87
88	},
89	/*
90	 * Auto scrolls the page when we are dragging an element.
91	 */
92	scroll: function(e) {
93
94		if(!e.nestedSortCfg.autoScroll) {
95			return false;
96		}
97		var sensitivity = e.nestedSortCfg.scrollSensitivity;
98		var speed = e.nestedSortCfg.scrollSpeed;
99		var pointer = jQuery.iDrag.dragged.dragCfg.currentPointer;
100		var docDim = jQuery.iUtil.getScroll();
101		if((pointer.y - docDim.ih) - docDim.t > -sensitivity) {
102			window.scrollBy(0,speed);
103		}
104		if(pointer.y - docDim.t < sensitivity) {
105			window.scrollBy(0,-speed);
106		}
107		//The two lines bellow are for horizontal scrolling. It is not needed.
108		//if((pointer.x - docDim.iw) - docDim.l > -sensitivity) {window.scrollBy(speed,0);}
109		//if(pointer.x - docDim.l < sensitivity) {window.scrollBy(-speed,0);}
110
111	},
112	/*
113	 * Called when the item is released after a drag. 
114	 * The argument passed in is the dragged element released.
115	 */
116	check : function(dragged) {
117		//A brand new NestedSortable release check
118		jQuery.iNestedSortable.newCheck(dragged);
119
120		//The legacy release check performed by plain Sortables
121		return jQuery.iNestedSortable.oldCheck(dragged);
122	},
123	oldCheck : jQuery.iSort.check,
124	newCheck : function(dragged) {
125		//removes nesting styling
126		if (jQuery.iNestedSortable.latestNestingClass &&
127			jQuery.iNestedSortable.currentNesting) 
128		{
129			jQuery.iNestedSortable.currentNesting
130				.removeClass(jQuery.iNestedSortable.latestNestingClass);
131			jQuery.iNestedSortable.currentNesting = null;
132			jQuery.iNestedSortable.latestNestingClass = "";
133		}
134		
135		if(jQuery.iDrop.overzone.isNestedSortable) {
136			jQuery.iDrop.overzone.nestedSortCfg.remeasured = false;
137		}
138		
139	},
140	/*
141	 * Called when there is a need to serialize the
142	 * NestedSortable, to send back to the server. The 
143	 * parameter is a string with the id of the NestedSortable
144	 * element, or an array of ids. If no parameter is passed,
145	 * all nested sortables in the page will be serialized. The
146	 * return value is an object with a 'hash' parameter, that
147	 * contains a string for use in GET or POST, and a 'o' parameter,
148	 * with contains a JavaScript object representation 
149	 * of the item order.
150	 */
151	serialize: function(s) {
152		if(jQuery('#' + s).get(0).isNestedSortable){
153			//A brand new NestedSortable serialization
154			return jQuery.iNestedSortable.newSerialize(s);
155		} else {
156			//The legacy serialization
157			return jQuery.iNestedSortable.oldSerialize(s);
158		}
159	},
160	oldSerialize: jQuery.iSort.serialize,
161	newSerialize: function (s) {
162		var i;
163		var h = ''; //url get string that represents the element order
164		var currentPath = ''; 	//used so the recursive function can build h
165		var o = {}; //json object that represents the element order
166		var e; //NestedSortable element being worked on
167		/*
168		 * This recursive function will build the get string (h)
169		 * and return the object (o) for a given NestedSortable.
170		 */
171		var buildHierarchySer = function(context) {
172			var retVal = [];
173			thisChildren = jQuery(context).children('.' + jQuery.iSort.collected[s]);
174			thisChildren.each( function(i) {
175				
176				//Extracts part of the HTML element ID that 
177				//will be shown in the serialization, using a RegExp.
178				var serId = jQuery.attr(this,'id');
179				if(serId && serId.match) {
180					serId = serId.match(e.nestedSortCfg.serializeRegExp)[0];
181				}
182				
183				
184				if (h.length > 0) {
185					h += '&';
186				}
187				h += s + currentPath + '['+i+'][id]=' + serId;
188				retVal[i] = {id: serId};
189				var newContext = jQuery(this).children(e.nestedSortCfg.nestingTag + "." + e.nestedSortCfg.nestingTagClass.split(" ").join(".")).get(0);
190				var oldPath = currentPath;
191				currentPath += '['+i+'][children]';
192				var thisChildren = buildHierarchySer(newContext);
193				if (thisChildren.length > 0) {
194					retVal[i].children = thisChildren;
195				}
196				currentPath = oldPath;
197			});
198			return retVal;
199		};
200
201		//analises the parameter passed in
202		if (s) {
203			if (jQuery.iSort.collected[s] ) {
204				//when only one NestedSortable id was passed in
205				e = jQuery('#' + s).get(0);
206				o[s] = buildHierarchySer(e);
207			} else {
208				for ( a in s) {
209					//when an array of NestedSortables ids was passed in
210					if (jQuery.iSort.collected[s[a]] ) {
211						e = jQuery('#' + s[a]).get(0);
212						o[s[a]] = buildHierarchySer(e);			
213					}
214				}
215			}
216		} else {
217			//when nothing was passed in (we will serialize all)
218			for ( i in jQuery.iSort.collected){
219				e = jQuery('#' + i).get(0);
220				o[i] = buildHierarchySer(e);
221			}
222		}
223		return {hash:h, o:o};
224	},
225	
226	/*
227	 * Finds the sortable item that is on top of the one being dragged,
228	 * and in a compatible nesting level. Returns null if none was found.
229	 */
230	findPrecedingItem : function (e) {
231		var largestY = 0;
232		var preceding = jQuery.grep(e.dropCfg.el, function(i) {
233			//needs to be on top of the one being dragged
234			var isOnTop = (i.pos.y < jQuery.iDrag.dragged.dragCfg.ny) && (i.pos.y > largestY);
235			if(!isOnTop) {
236				return false;
237			}
238			
239			//needs to be on the same nesting level or a "child" level
240			var isSameLevel;
241			if(e.nestedSortCfg.rightToLeft) {
242				isSameLevel = (i.pos.x + i.pos.wb + e.nestedSortCfg.snapTolerance > jQuery.iDrag.dragged.dragCfg.nx + jQuery.iDrag.dragged.dragCfg.oC.wb);
243			} else {
244				isSameLevel = (i.pos.x - e.nestedSortCfg.snapTolerance < jQuery.iDrag.dragged.dragCfg.nx);
245			}
246			if(!isSameLevel) {
247				return false;
248			}
249			
250			//can't be an element that is being dragged
251			var isBeingDragged = jQuery.iNestedSortable.isBeingDragged(e, i);
252			if(isBeingDragged) {
253				return false;
254			}
255			
256			//got here because it is a match
257			largestY = i.pos.y;
258			return true;
259		});
260		
261		if ( preceding.length > 0 ) {
262			//the last one should be it
263			return preceding[(preceding.length-1)];
264		} else {
265			//nothing above it
266			return null;
267		}
268	},
269	/*
270	 * Checks if the item being dragged is on top and touching the first item on the list.
271	 * Returns true or false.
272	 */
273	isTouchingFirstItem : function (e) {
274		var lowestY;
275		var firstItem = jQuery.grep(e.dropCfg.el, function(i) {
276			//needs to be on top of all elements already looked up
277			var isBefore = (lowestY === undefined || i.pos.y < lowestY);
278			if (!isBefore) {
279				return false;
280			}
281			
282			//can't be an element that is being dragged
283			var isBeingDragged = jQuery.iNestedSortable.isBeingDragged(e, i);
284			if(isBeingDragged) {
285				return false;
286			}
287			
288			//got here because it is a match 
289			lowestY = i.pos.y;
290			return true;
291		});
292		if ( firstItem.length > 0 ) {
293			//the last one should be it
294			firstItem = firstItem[(firstItem.length-1)];
295			return firstItem.pos.y < jQuery.iDrag.dragged.dragCfg.ny + jQuery.iDrag.dragged.dragCfg.oC.hb &&
296				firstItem.pos.y > jQuery.iDrag.dragged.dragCfg.ny;
297		} else {
298			return false;
299		}
300	},
301	/*
302	 * Checks to see if an element is being dragged. 
303	 * You may de dragging an element by itself or by one of his ancestors.
304	 * Returns true or false.
305	 */
306	isBeingDragged : function (e, elem) {
307		var dragged = jQuery.iDrag.dragged;
308		
309		//nothing being dragged
310		if(!dragged) {
311			return false;
312		}
313			
314		//trivial case
315		if(elem == dragged) {
316			return true;
317		}
318		
319		//looks for its ancestors
320		if ( 
321			jQuery(elem)
322				.parents("." + e.sortCfg.accept.split(" ").join("."))
323				.filter(function() {return this == dragged;}).length !== 0
324			) {
325			return true;
326		} else {
327			return false;
328		}
329	},
330	shouldNestItem : function(e, precedingItem ) {
331		//there should be a preceding item to be able to nest
332		if(!precedingItem) {return false;}
333		if(e.nestedSortCfg.noNestingClass &&
334			jQuery(precedingItem).filter("." + e.nestedSortCfg.noNestingClass).get(0) === precedingItem)
335			{return false;}
336		//This code is to limit the levels of nesting that can be achieved
337		//Development of this is currently halted.
338		/*
339		if(	e.nestedSortCfg.nestingLimit !== false) {
340			//nesting level of the preceding item
341			var nLevelPrec = jQuery(precedingItem)
342				.parents("." + e.sortCfg.accept.split(" ").join("."))
343				.length;
344			
345			//don't allow nesting
346			if (nLevelPrec >= e.nestedSortCfg.nestingLimit) return false;
347			
348			//will hold the maximum level of nesting in the element being dragged
349			var nLevelDrag = 0;
350			
351			//for each leaf element inside the dragged element (that have no elements nested to it)
352			jQuery(e.nestedSortCfg.nestingTag + "." + e.nestedSortCfg.nestingTagClass.split(" ").join(".") + " > ." + e.sortCfg.accept.split(" ").join(".") + ":first-child", jQuery.iDrag.dragged)
353				.not("*["+e.nestedSortCfg.nestingTag + "." + e.nestedSortCfg.nestingTagClass.split(" ").join(".")+"]")
354				.each( function(i) {
355					var draggedIndex = 0;
356					//finds out 
357					var parentLength = jQuery(this).parents("." + e.sortCfg.accept.split(" ").join(".")).each(
358						function(i) {
359							draggedIndex = i;
360							return this == jQuery.iDrag.dragged;
361						}
362						).length;
363					
364					//here dradraggedIndex holds the index 
365					//of the dragged element in the ancestors array of the leaf element
366					var thisLevel = parentLength - draggedIndex;
367					if (thisLevel > nLevelDrag )
368						nLevelDrag = thisLevel;
369					}
370				);
371				
372			//don't allow nesting
373			if (nLevelPrec + nLevelDrag > e.nestedSortCfg.nestingLimit) return false;
374		}*/
375		
376		if (e.nestedSortCfg.rightToLeft) {
377			return precedingItem.pos.x + precedingItem.pos.wb - (e.nestedSortCfg.nestingPxSpace - e.nestedSortCfg.snapTolerance) > jQuery.iDrag.dragged.dragCfg.nx + jQuery.iDrag.dragged.dragCfg.oC.wb;
378		} else {
379			return precedingItem.pos.x + (e.nestedSortCfg.nestingPxSpace - e.nestedSortCfg.snapTolerance) < jQuery.iDrag.dragged.dragCfg.nx;
380		} 
381	},
382	nestItem: function(e, parent) {
383		//selects the nesting tag inside the parent
384		var parentNesting = jQuery(parent).children(e.nestedSortCfg.nestingTag + "." + e.nestedSortCfg.nestingTagClass.split(" ").join(".") );
385		var helper = jQuery.iSort.helper;
386		styleHelper = helper.get(0).style;
387		styleHelper.width = 'auto'; //makes sure helper gets resized properly
388		
389		//if there is none, creates it
390		if ( !parentNesting.size()) {
391			var newUl = "<" + e.nestedSortCfg.nestingTag + " class='"+e.nestedSortCfg.nestingTagClass+"'></" + e.nestedSortCfg.nestingTag+">";
392			// Place new nesting tag and adds style
393			parentNesting = jQuery(parent).append(newUl).children(e.nestedSortCfg.nestingTag).css(e.nestedSortCfg.styleToAttach); 
394		}
395		
396		//styles the nesting
397		jQuery.iNestedSortable.updateCurrentNestingClass(e, parentNesting );
398		
399		//does stuff before the helper is removed
400		jQuery.iNestedSortable.beforeHelperRemove(e);
401		
402		//puts the helper in the proper place.
403		parentNesting.prepend(helper.get(0));
404		
405		//does stuff after the helper is inserted
406		jQuery.iNestedSortable.afterHelperInsert(e);
407	},
408	appendItem: function(e, itemBefore) {
409		jQuery.iNestedSortable.updateCurrentNestingClass(e, jQuery(itemBefore).parent() );
410		jQuery.iNestedSortable.beforeHelperRemove(e);
411		jQuery(itemBefore).after(jQuery.iSort.helper.get(0));
412		jQuery.iNestedSortable.afterHelperInsert(e);
413	},
414	insertOnTop: function (e) {
415		jQuery.iNestedSortable.updateCurrentNestingClass(e, e);
416		jQuery.iNestedSortable.beforeHelperRemove(e);
417		jQuery(e).prepend(jQuery.iSort.helper.get(0));
418		jQuery.iNestedSortable.afterHelperInsert(e);
419	},
420	beforeHelperRemove : function (e) {
421		//hides the nesting when it becomes empty
422		var parent = jQuery.iSort.helper.parent(e.nestedSortCfg.nestingTag + "." + e.nestedSortCfg.nestingTagClass.split(" ").join("."));
423		var numSiblings = parent
424			.children("." + e.sortCfg.accept.split(" ").join(".") + ":visible")
425			.size();	
426		if(numSiblings === 0 && parent.get(0) !== e) {
427			parent.hide();
428		}
429	},
430	afterHelperInsert : function (e) {
431		//displays the nesting after something is inserted
432		var parent = jQuery.iSort.helper.parent();
433		if(parent.get(0) !== e) {
434			parent.show();
435		}
436		e.nestedSortCfg.remeasured = false;
437	},
438	updateCurrentNestingClass : function(e, nestingElem) {
439		
440		var nesting = jQuery(nestingElem);
441		
442		if ((e.nestedSortCfg.currentNestingClass) && //makes sure a special class is desired
443			(!jQuery.iNestedSortable.currentNesting || nesting.get(0) != jQuery.iNestedSortable.currentNesting.get(0)) ){ //avoids doing it on the same elem
444			
445			//removes from last nesting
446			if(jQuery.iNestedSortable.currentNesting) {
447				jQuery.iNestedSortable.currentNesting
448					.removeClass(e.nestedSortCfg.currentNestingClass);
449			}		
450			
451			if(nesting.get(0) != e) { //not root nesting
452				jQuery.iNestedSortable.currentNesting = nesting;
453				
454				//adds to this one
455				nesting.addClass(e.nestedSortCfg.currentNestingClass);
456				
457				//this is need to remove the styling on "check"
458				jQuery.iNestedSortable.latestNestingClass = e.nestedSortCfg.currentNestingClass;
459			} else {
460				//don't style the "root nesting"
461				
462				//cleans up
463				jQuery.iNestedSortable.currentNesting = null;
464				jQuery.iNestedSortable.latestNestingClass = "";
465			}
466		}
467	},
468	destroy: function () {
469		return this.each(
470			function () {
471				if(this.isNestedSortable) {
472					this.nestedSortCfg = null;
473					this.isNestedSortable = null;
474					jQuery(this).SortableDestroy();
475				}
476			}
477		);
478	},
479	build: function(conf) {
480		if (conf.accept && jQuery.iUtil && jQuery.iDrag && jQuery.iDrop && jQuery.iSort) 
481		{
482			this.each(
483				function() {
484					this.isNestedSortable = true;
485					this.nestedSortCfg = {
486						noNestingClass : conf.noNestingClass ? conf.noNestingClass : false,
487						rightToLeft : conf.rightToLeft ? true : false , 
488						nestingPxSpace : parseInt(conf.nestingPxSpace, 10) || 30 ,
489						currentNestingClass :  conf.currentNestingClass ? conf.currentNestingClass : "",
490						nestingLimit : conf.nestingLimit ? conf.nestingLimit : false, //not implemented yet
491						autoScroll : conf.autoScroll !== undefined ? conf.autoScroll == true : true,
492						scrollSensitivity: conf.scrollSensitivity ? conf.scrollSensitivity : 20,
493						scrollSpeed: conf.scrollSpeed ? conf.scrollSpeed : 20,
494						serializeRegExp : conf.serializeRegExp ? conf.serializeRegExp : /[^\-]*$/
495					};
496										
497					//a "do nothing" tolerance when nesting/un-nesting, to stop things from jumping up too quickly
498					this.nestedSortCfg.snapTolerance = parseInt(this.nestedSortCfg.nestingPxSpace * 0.4, 10);
499						
500					//the tag that will be used to nest items to parent items
501					this.nestedSortCfg.nestingTag = this.tagName;
502					this.nestedSortCfg.nestingTagClass = this.className;
503					
504					//style that makes nested elements be padded to the right or to the left
505					this.nestedSortCfg.styleToAttach = (this.nestedSortCfg.rightToLeft) ? 
506						{"padding-left":0, "padding-right": this.nestedSortCfg.nestingPxSpace + 'px' }
507						:{"padding-left": this.nestedSortCfg.nestingPxSpace + 'px', "padding-right": 0 };
508					jQuery(this.nestedSortCfg.nestingTag, this)
509						.css(this.nestedSortCfg.styleToAttach);
510						
511				}
512			);
513			
514			//overides checkhover, check and serialize, without losing backwards compatilibity (eg. plain Sortables can stil be used) 
515			jQuery.iSort.checkhover = jQuery.iNestedSortable.checkHover;
516			jQuery.iSort.check = jQuery.iNestedSortable.check;
517			jQuery.iSort.serialize = jQuery.iNestedSortable.serialize;
518		}
519		
520		return this.Sortable(conf);
521	}
522};
523
524//Extends jQuery to add the plugin.
525jQuery.fn.extend(
526	{
527		NestedSortable : jQuery.iNestedSortable.build,
528		NestedSortableDestroy: jQuery.iNestedSortable.destroy
529	}
530);
531
532
533//Monkey patches interface with some corrections, which are not applied to
534//the 1.2 version yet. getScroll is needed by the autoScroll option.
535jQuery.iUtil.getScroll = function (e)
536	{
537		var t, l, w, h, iw, ih;
538		if (e && e.nodeName.toLowerCase() != 'body') {
539			t = e.scrollTop;
540			l = e.scrollLeft;
541			w = e.scrollWidth;
542			h = e.scrollHeight;
543			iw = 0;
544			ih = 0;
545		} else  {
546			if (document.documentElement && document.documentElement.scrollTop) {
547				t = document.documentElement.scrollTop;
548				l = document.documentElement.scrollLeft;
549				w = document.documentElement.scrollWidth;
550				h = document.documentElement.scrollHeight;
551			} else if (document.body) {
552				t = document.body.scrollTop;
553				l = document.body.scrollLeft;
554				w = document.body.scrollWidth;
555				h = document.body.scrollHeight;
556			}
557			iw = self.innerWidth||document.documentElement.clientWidth||document.body.clientWidth||0;
558			ih = self.innerHeight||document.documentElement.clientHeight||document.body.clientHeight||0;
559		}
560		return { t: t, l: l, w: w, h: h, iw: iw, ih: ih };
561	};

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.