mirror of
				https://github.com/flynx/ImageGrid.git
				synced 2025-10-31 03:10:07 +00:00 
			
		
		
		
	moved things to their logical locations...
Signed-off-by: Alex A. Naanou <alex.nanou@gmail.com>
This commit is contained in:
		
							parent
							
								
									0ef0630463
								
							
						
					
					
						commit
						98412ab28c
					
				| @ -482,6 +482,359 @@ var BaseBrowserPrototype = { | |||||||
| 			+ Date.now() }, | 			+ Date.now() }, | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  | 	//
 | ||||||
|  | 	// 	.walk(item_handler[, options])
 | ||||||
|  | 	// 		-> result
 | ||||||
|  | 	//
 | ||||||
|  | 	// 	.walk(item_handler, nested_handler[, options])
 | ||||||
|  | 	// 		-> result
 | ||||||
|  | 	//
 | ||||||
|  | 	//
 | ||||||
|  | 	// 	item_handler(path, elem, index, nested, sublist)
 | ||||||
|  | 	// 		-> array
 | ||||||
|  | 	//
 | ||||||
|  | 	// 	nested(list[, options])
 | ||||||
|  | 	// 		-> items
 | ||||||
|  | 	//
 | ||||||
|  | 	//
 | ||||||
|  | 	// 	XXX
 | ||||||
|  | 	// 	nested_handler(..)
 | ||||||
|  | 	// 		-> 
 | ||||||
|  | 	//
 | ||||||
|  | 	//
 | ||||||
|  | 	// 	Request manual iteration...
 | ||||||
|  | 	// 	nested(false)
 | ||||||
|  | 	// 		-> undefined
 | ||||||
|  | 	//
 | ||||||
|  | 	//
 | ||||||
|  | 	// options format:
 | ||||||
|  | 	// 	{
 | ||||||
|  | 	// 		// Iterate ALL items...
 | ||||||
|  | 	// 		//
 | ||||||
|  | 	// 		// NOTE: this if true overrides all other iteration coverage 
 | ||||||
|  | 	// 		//		options... 
 | ||||||
|  | 	// 		iterateAll: <bool>,
 | ||||||
|  | 	//
 | ||||||
|  | 	// 		// If true do not skip items with .noniterable set to true...
 | ||||||
|  | 	// 		iterateNonIterable: <bool>,
 | ||||||
|  | 	// 		// If true do not skip item.sublist of items with .collapsed 
 | ||||||
|  | 	// 		// set to true...
 | ||||||
|  | 	// 		iterateCollapsed: <bool>,
 | ||||||
|  | 	// 		// If true skip iterating nested items...
 | ||||||
|  | 	// 		skipNested: <bool>,
 | ||||||
|  | 	//
 | ||||||
|  | 	// 		// If true, reverse iteration order...
 | ||||||
|  | 	// 		// NOTE: containing items will still precede the contained,
 | ||||||
|  | 	// 		//		i.e. this will reverse the level order but not 
 | ||||||
|  | 	// 		//		nesting order...
 | ||||||
|  | 	// 		reverseIteration: <bool>,
 | ||||||
|  | 	//
 | ||||||
|  | 	// 		// If true include inlined parent id in path...
 | ||||||
|  | 	// 		// XXX not implemented yet -- can we implement this???...
 | ||||||
|  | 	// 		inlinedPaths: <bool>,
 | ||||||
|  | 	// 	}
 | ||||||
|  | 	//
 | ||||||
|  | 	//
 | ||||||
|  | 	// XXX make sublist test customizable...
 | ||||||
|  | 	walk: function(func, options){ | ||||||
|  | 		var that = this | ||||||
|  | 
 | ||||||
|  | 		// parse args...
 | ||||||
|  | 		var args = [...arguments] | ||||||
|  | 		func = args[0] instanceof Function ?  | ||||||
|  | 			args.shift()  | ||||||
|  | 			: undefined | ||||||
|  | 		var recursion = (args[0] instanceof Function  | ||||||
|  | 				|| typeof(args[0]) == typeof('str')) ?  | ||||||
|  | 			args.shift()  | ||||||
|  | 			: undefined | ||||||
|  | 		var i = typeof(args[0]) == typeof(123) ? | ||||||
|  | 			args.shift() | ||||||
|  | 			: 0 | ||||||
|  | 		var path = (args[0] instanceof Array  | ||||||
|  | 				|| typeof(args[0]) == typeof('str')) ? | ||||||
|  | 			args.shift() | ||||||
|  | 			: [] | ||||||
|  | 		path = path instanceof Array ? path : [path] | ||||||
|  | 		var options = args.pop() || {} | ||||||
|  | 
 | ||||||
|  | 		// options...
 | ||||||
|  | 		options = Object.assign(Object.create(this.options || {}), options || {}) | ||||||
|  | 		var iterateNonIterable = options.iterateAll || options.iterateNonIterable | ||||||
|  | 		var iterateCollapsed = options.iterateAll || options.iterateCollapsed | ||||||
|  | 		var skipNested = !options.iterateAll && options.skipNested | ||||||
|  | 		var reverse = !!options.reverseIteration | ||||||
|  | 
 | ||||||
|  | 		// level walk function...
 | ||||||
|  | 		var walk = function(i, path, list){ | ||||||
|  | 			return list | ||||||
|  | 				// reverse the items...
 | ||||||
|  | 				.run(function(){ | ||||||
|  | 					return reverse ? | ||||||
|  | 						// NOTE: we .slice() as we do not want to affect 
 | ||||||
|  | 						// 		the actual list...
 | ||||||
|  | 						this.slice().reverse()  | ||||||
|  | 						: this }) | ||||||
|  | 				.map(function(elem){ | ||||||
|  | 					// skip non-iterable items...
 | ||||||
|  | 					if(!iterateNonIterable && elem.noniterable){ | ||||||
|  | 						return [] | ||||||
|  | 					} | ||||||
|  | 
 | ||||||
|  | 					var elem_id = elem.id || elem.value | ||||||
|  | 					// these will be set in the return expression below...
 | ||||||
|  | 					var sublist | ||||||
|  | 					var p  | ||||||
|  | 
 | ||||||
|  | 					// nested browser/list handler...
 | ||||||
|  | 					var nested_called = false | ||||||
|  | 					var nested = function(list, opts){ | ||||||
|  | 						var skip = skipNested && !list | ||||||
|  | 						list = (!iterateCollapsed && elem.collapsed) ? | ||||||
|  | 							[] | ||||||
|  | 							: (list || sublist) | ||||||
|  | 						list = list === true ? sublist : list | ||||||
|  | 						nested_called = true | ||||||
|  | 
 | ||||||
|  | 						return ( | ||||||
|  | 								// request manual iteration...
 | ||||||
|  | 								(skip || list === false) ? | ||||||
|  | 									[] | ||||||
|  | 								:list instanceof Array ? | ||||||
|  | 									walk(i, p, list) | ||||||
|  | 								// user-defined recursion...
 | ||||||
|  | 								: recursion instanceof Function ? | ||||||
|  | 									recursion.call(that, func, i, p, list, opts || options) | ||||||
|  | 								: list[recursion || 'walk'](func, i, p, opts || options)) | ||||||
|  | 				   			.run(function(){ | ||||||
|  | 								var res = this instanceof Array ?  | ||||||
|  | 									this  | ||||||
|  | 									: [this]  | ||||||
|  | 								i += this.length  | ||||||
|  | 								return res | ||||||
|  | 							})} | ||||||
|  | 
 | ||||||
|  | 					return ( | ||||||
|  | 							// inline browser or array...
 | ||||||
|  | 							(elem instanceof Array  | ||||||
|  | 									|| elem instanceof Browser) ? | ||||||
|  | 								func.call(that,  | ||||||
|  | 									i, p = path,  | ||||||
|  | 									null, nested,  | ||||||
|  | 									sublist = elem) | ||||||
|  | 							// nested browser / array...
 | ||||||
|  | 							: (elem.sublist instanceof Browser  | ||||||
|  | 									|| elem.sublist instanceof Array) ? | ||||||
|  | 								func.call(that,  | ||||||
|  | 									i++, p = path.concat([elem_id]),  | ||||||
|  | 									elem, nested,  | ||||||
|  | 									sublist = elem.sublist) | ||||||
|  | 							// normal element...
 | ||||||
|  | 							: func.call(that,  | ||||||
|  | 								i++, p = path.concat([elem_id]),  | ||||||
|  | 								elem, null,  | ||||||
|  | 								sublist = null) ) | ||||||
|  | 						// append nested elements...
 | ||||||
|  | 						.concat((!sublist || nested_called) ?  | ||||||
|  | 							[]  | ||||||
|  | 							: nested(sublist)) | ||||||
|  | 				}) | ||||||
|  | 				.flat() } | ||||||
|  | 
 | ||||||
|  | 		return walk(i, path, this.items) | ||||||
|  | 	}, | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | 	// Text render...
 | ||||||
|  | 	//
 | ||||||
|  | 	// This is mainly here for doc/debug purposes...
 | ||||||
|  | 	//
 | ||||||
|  | 	// XXX rename this??
 | ||||||
|  | 	text: function(options, renderer){ | ||||||
|  | 		var that = this | ||||||
|  | 		// XXX Q: should options and context be distinguished only via 
 | ||||||
|  | 		// 		the .options attr as is the case now???
 | ||||||
|  | 		// 		...see no reason why not, though it does not feel right...
 | ||||||
|  | 		var context = (options == null || options.options == null) ? | ||||||
|  | 				{ | ||||||
|  | 					root: this, | ||||||
|  | 					// NOTE: we are not combining this with .options as nested 
 | ||||||
|  | 					// 		lists can have their own unique sets of options 
 | ||||||
|  | 					// 		independently of the root list...
 | ||||||
|  | 					options: Object.assign( | ||||||
|  | 						Object.create(this.options || {}), | ||||||
|  | 						// defaults...
 | ||||||
|  | 						// XXX is this the correct way to setup defaults???
 | ||||||
|  | 						{ | ||||||
|  | 							iterateNonIterable: true, | ||||||
|  | 						},  | ||||||
|  | 						options || {}), | ||||||
|  | 				} | ||||||
|  | 			: options | ||||||
|  | 		options = context.options | ||||||
|  | 		renderer = renderer || this | ||||||
|  | 
 | ||||||
|  | 		var getValue = function(item){ | ||||||
|  | 			return item.value || item } | ||||||
|  | 
 | ||||||
|  | 		var items = this | ||||||
|  | 			.walk( | ||||||
|  | 				function(i, path, item, nested, sublist){ | ||||||
|  | 					var indent = path.map(e => '  ').join('') | ||||||
|  | 					return ( | ||||||
|  | 						// inline...
 | ||||||
|  | 						(item == null && sublist) ? | ||||||
|  | 							// NOTE: here we are forcing rendering of the 
 | ||||||
|  | 							// 		inline browser/list, i.e. ignoring 
 | ||||||
|  | 							// 		options.skipNested for inline stuff...
 | ||||||
|  | 							nested(true) | ||||||
|  | 								.map(function(e){ | ||||||
|  | 									return indent + getValue(e) }) | ||||||
|  | 						// nested...
 | ||||||
|  | 						: sublist ? | ||||||
|  | 							[item.value] | ||||||
|  | 								.concat( | ||||||
|  | 									nested() | ||||||
|  | 										.map(function(e){ | ||||||
|  | 											return indent + getValue(e) })) | ||||||
|  | 						// single item...
 | ||||||
|  | 						: [getValue(item)] | ||||||
|  | 					) }, | ||||||
|  | 				function(func, i, path, sublist, options){ | ||||||
|  | 					return sublist.text(context) }, | ||||||
|  | 				options) | ||||||
|  | 
 | ||||||
|  | 		return context.root === this ? | ||||||
|  | 			items.join('\n') | ||||||
|  | 			: items | ||||||
|  | 	}, | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | 	// Extended map...
 | ||||||
|  | 	//
 | ||||||
|  | 	//	Get all items...
 | ||||||
|  | 	//	.map([options])
 | ||||||
|  | 	//		-> items
 | ||||||
|  | 	//
 | ||||||
|  | 	//	Map func to items...
 | ||||||
|  | 	//	.map(func[, options])
 | ||||||
|  | 	//		-> items
 | ||||||
|  | 	//
 | ||||||
|  | 	//
 | ||||||
|  | 	//
 | ||||||
|  | 	//	func(item, index, path, browser)
 | ||||||
|  | 	//		-> result
 | ||||||
|  | 	//
 | ||||||
|  | 	//
 | ||||||
|  | 	//
 | ||||||
|  | 	// For supported options see docs for .walk(..)
 | ||||||
|  | 	//
 | ||||||
|  | 	// By default this will not iterate items that are:
 | ||||||
|  | 	// 	- non-iterable (item.noniterable is true)
 | ||||||
|  | 	// 	- collapsed sub-items (item.collapsed is true)
 | ||||||
|  | 	//
 | ||||||
|  | 	// This extends the Array .map(..) by adding:
 | ||||||
|  | 	// 	- ability to run without arguments
 | ||||||
|  | 	// 	- support for options
 | ||||||
|  | 	//
 | ||||||
|  | 	//
 | ||||||
|  | 	// XXX make item access by index lazy... 
 | ||||||
|  | 	// 		- index nested stuff and lengths... (.sublist_length)
 | ||||||
|  | 	// 		- stop when target reached... (control callback???)
 | ||||||
|  | 	// XXX Q: should we have an option to treat groups as elements???
 | ||||||
|  | 	map: function(func, options){ | ||||||
|  | 		var that = this | ||||||
|  | 
 | ||||||
|  | 		// parse args...
 | ||||||
|  | 		//
 | ||||||
|  | 		// NOTE: in addition to the user signatures documented above this
 | ||||||
|  | 		// 		also supports two variants used internally:
 | ||||||
|  | 		// 			.map(func, path[, options])
 | ||||||
|  | 		// 			.map(func, i, path[, options])
 | ||||||
|  | 		// 		these set the "base" path and index passed to func...
 | ||||||
|  | 		// XXX can we avoid argument parsing here???
 | ||||||
|  | 		var args = [...arguments] | ||||||
|  | 		func = args[0] instanceof Function ?  | ||||||
|  | 			args.shift()  | ||||||
|  | 			: undefined | ||||||
|  | 		var i = typeof(args[0]) == typeof(123) ? | ||||||
|  | 			args.shift() | ||||||
|  | 			: 0 | ||||||
|  | 		var path = (args[0] instanceof Array  | ||||||
|  | 				|| typeof(args[0]) == typeof('str')) ? | ||||||
|  | 			args.shift() | ||||||
|  | 			: [] | ||||||
|  | 		var options = args.pop() || {} | ||||||
|  | 
 | ||||||
|  | 		return this.walk( | ||||||
|  | 			function(i, path, elem){ | ||||||
|  | 				return elem != null ? | ||||||
|  | 					[func === undefined ? | ||||||
|  | 						elem | ||||||
|  | 						// XXX should this pass the current or the root 
 | ||||||
|  | 						// 		container to func???
 | ||||||
|  | 						: func.call(that, elem, i, path, that)] | ||||||
|  | 					: [] },  | ||||||
|  | 			function(_, i, path, sublist, options){ | ||||||
|  | 				// NOTE: this needs to call the actual func that the user
 | ||||||
|  | 				// 		gave us and not the constructed function that we 
 | ||||||
|  | 				// 		pass to .walk(..) above...
 | ||||||
|  | 				return sublist.map(func, i, path, options) }, | ||||||
|  | 			i, | ||||||
|  | 			path,  | ||||||
|  | 			options) | ||||||
|  | 	}, | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | 	// Sublist map functions...
 | ||||||
|  | 	// NOTE: there are different from .map(..) in that instead of paths 
 | ||||||
|  | 	// 		func(..) will get indexes in the current browser...
 | ||||||
|  | 	// NOTE: these will return a sparse array...
 | ||||||
|  | 	sublists: function(func, options){ | ||||||
|  | 		var that = this | ||||||
|  | 		//options = options || {}
 | ||||||
|  | 		options = Object.assign(Object.create(this.options || {}), options || {}) | ||||||
|  | 		var skipNested = options.skipNested | ||||||
|  | 		var skipInlined = options.skipInlined | ||||||
|  | 
 | ||||||
|  | 		var res = [] | ||||||
|  | 		this.items | ||||||
|  | 			.forEach(function(elem, i){ | ||||||
|  | 				if((!skipInlined && elem.value instanceof Browser) | ||||||
|  | 							|| (!skipNested && elem.sublist)){ | ||||||
|  | 					res[i] = func ? | ||||||
|  | 						func.call(that, elem, i, that) | ||||||
|  | 						: elem  | ||||||
|  | 				} }) | ||||||
|  | 		return res  | ||||||
|  | 	}, | ||||||
|  | 	nested: function(func){ | ||||||
|  | 		return this.sublists(func, {skipInlined: true}) }, | ||||||
|  | 	inlined: function(func){ | ||||||
|  | 		return this.sublists(func, {skipNested: true}) }, | ||||||
|  | 
 | ||||||
|  | 	next: function(){}, | ||||||
|  | 	prev: function(){}, | ||||||
|  | 
 | ||||||
|  | 	// XXX should there return an array or a .constructor(..) instance??
 | ||||||
|  | 	// XXX should these call respective methods (.forEach(..), .filter(..), 
 | ||||||
|  | 	// 		.reduce(..)) on the nested browsers???
 | ||||||
|  | 	forEach: function(func, options){ | ||||||
|  | 		this.map(...arguments) | ||||||
|  | 		return this }, | ||||||
|  | 	filter: function(func, options){ | ||||||
|  | 		return this.map(function(e, i, p, b){ | ||||||
|  | 			return func.call(this, e, i, p, b) ? [e] : [] }) | ||||||
|  | 		.flat() }, | ||||||
|  | 	reduce: function(){}, | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | 	// XXX do we need edit ability here? 
 | ||||||
|  | 	// 		i.e. .set(..), .remove(..), .sort(..), ...
 | ||||||
|  | 	// 		...if we are going to implement editing then we'll need to 
 | ||||||
|  | 	// 		callback the user code or update the user state...
 | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
| 
 | 
 | ||||||
| 	// Make .items and .item_key_index...
 | 	// Make .items and .item_key_index...
 | ||||||
| 	//
 | 	//
 | ||||||
| @ -766,6 +1119,90 @@ var BaseBrowserPrototype = { | |||||||
| 	}, | 	}, | ||||||
| 	//*/
 | 	//*/
 | ||||||
| 
 | 
 | ||||||
|  | 
 | ||||||
|  | 	// Render state...
 | ||||||
|  | 	//
 | ||||||
|  | 	//	.render()
 | ||||||
|  | 	//	.render(options)
 | ||||||
|  | 	//	.render(context)
 | ||||||
|  | 	//		-> state
 | ||||||
|  | 	//
 | ||||||
|  | 	//
 | ||||||
|  | 	// context format:
 | ||||||
|  | 	// 	{
 | ||||||
|  | 	// 		root: <root-browser>,
 | ||||||
|  | 	// 		options: <options>,
 | ||||||
|  | 	// 	}
 | ||||||
|  | 	//
 | ||||||
|  | 	//
 | ||||||
|  | 	// NOTE: currently options and context are distinguished only via 
 | ||||||
|  | 	// 		the .options attribute...
 | ||||||
|  | 	//
 | ||||||
|  | 	// XXX test sublist via .render rather than instanceof...
 | ||||||
|  | 	render: function(options, renderer){ | ||||||
|  | 		var that = this | ||||||
|  | 		// XXX Q: should options and context be distinguished only via 
 | ||||||
|  | 		// 		the .options attr as is the case now???
 | ||||||
|  | 		// 		...see no reason why not, though it does not feel right...
 | ||||||
|  | 		var context = (options == null || options.options == null) ? | ||||||
|  | 				{ | ||||||
|  | 					root: this, | ||||||
|  | 					// NOTE: we are not combining this with .options as nested 
 | ||||||
|  | 					// 		lists can have their own unique sets of options 
 | ||||||
|  | 					// 		independently of the root list...
 | ||||||
|  | 					//options: options || this.options || {},
 | ||||||
|  | 					options: Object.assign( | ||||||
|  | 						Object.create(this.options || {}), | ||||||
|  | 						// defaults...
 | ||||||
|  | 						// XXX is this the correct way to setup defaults???
 | ||||||
|  | 						{ | ||||||
|  | 							iterateNonIterable: true, | ||||||
|  | 						},  | ||||||
|  | 						options || {}), | ||||||
|  | 				} | ||||||
|  | 			: options | ||||||
|  | 		options = context.options | ||||||
|  | 		renderer = renderer || this | ||||||
|  | 
 | ||||||
|  | 		// XXX should we control render parameters (range, start, end, ...)
 | ||||||
|  | 		// 		from outside render and pass this info down to nested lists???
 | ||||||
|  | 		// 		...if yes how??
 | ||||||
|  | 		// 			- options
 | ||||||
|  | 		// 			- arg threading
 | ||||||
|  | 		var items = this | ||||||
|  | 			.walk( | ||||||
|  | 				function(i, path, item, nested, sublist){ | ||||||
|  | 					return ( | ||||||
|  | 						// inline...
 | ||||||
|  | 						(item == null && sublist) ? | ||||||
|  | 							// NOTE: here we are forcing rendering of the 
 | ||||||
|  | 							// 		inline browser/list, i.e. ignoring 
 | ||||||
|  | 							// 		options.skipNested for inline stuff...
 | ||||||
|  | 							// NOTE: we here do not distinguish between
 | ||||||
|  | 							// 		inlined lists and browsers... (XXX ???)
 | ||||||
|  | 							[ renderer.renderGroup(nested(true), context) ] | ||||||
|  | 						// nested...
 | ||||||
|  | 						: sublist ? | ||||||
|  | 							[ renderer.renderNested( | ||||||
|  | 								renderer.renderNestedHeader(item, i, context), | ||||||
|  | 								nested(), | ||||||
|  | 								item,  | ||||||
|  | 								context) ] | ||||||
|  | 						// normal item...
 | ||||||
|  | 						: [ renderer.renderItem(item, i, context) ] ) }, | ||||||
|  | 				function(func, i, path, sublist, options){ | ||||||
|  | 					return sublist.render(context, renderer) }, | ||||||
|  | 				options) | ||||||
|  | 
 | ||||||
|  | 		// determine the render mode...
 | ||||||
|  | 		return context.root === this ? | ||||||
|  | 			// root context -> render list and return this...
 | ||||||
|  | 			renderer.renderList(items, context) | ||||||
|  | 			// nested context -> return item list...
 | ||||||
|  | 			: items | ||||||
|  | 	}, | ||||||
|  | 	 | ||||||
|  | 
 | ||||||
| 	// Update state (make then render)...
 | 	// Update state (make then render)...
 | ||||||
| 	//
 | 	//
 | ||||||
| 	// 	.update()
 | 	// 	.update()
 | ||||||
| @ -1270,415 +1707,6 @@ var BaseBrowserPrototype = { | |||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| 	//
 |  | ||||||
| 	// 	.walk(item_handler[, options])
 |  | ||||||
| 	// 		-> result
 |  | ||||||
| 	//
 |  | ||||||
| 	// 	.walk(item_handler, nested_handler[, options])
 |  | ||||||
| 	// 		-> result
 |  | ||||||
| 	//
 |  | ||||||
| 	//
 |  | ||||||
| 	// 	item_handler(path, elem, index, nested, sublist)
 |  | ||||||
| 	// 		-> array
 |  | ||||||
| 	//
 |  | ||||||
| 	// 	nested(list[, options])
 |  | ||||||
| 	// 		-> items
 |  | ||||||
| 	//
 |  | ||||||
| 	//
 |  | ||||||
| 	// 	XXX
 |  | ||||||
| 	// 	nested_handler(..)
 |  | ||||||
| 	// 		-> 
 |  | ||||||
| 	//
 |  | ||||||
| 	//
 |  | ||||||
| 	// 	Request manual iteration...
 |  | ||||||
| 	// 	nested(false)
 |  | ||||||
| 	// 		-> undefined
 |  | ||||||
| 	//
 |  | ||||||
| 	//
 |  | ||||||
| 	// options format:
 |  | ||||||
| 	// 	{
 |  | ||||||
| 	// 		// Iterate ALL items...
 |  | ||||||
| 	// 		//
 |  | ||||||
| 	// 		// NOTE: this if true overrides all other iteration coverage 
 |  | ||||||
| 	// 		//		options... 
 |  | ||||||
| 	// 		iterateAll: <bool>,
 |  | ||||||
| 	//
 |  | ||||||
| 	// 		// If true do not skip items with .noniterable set to true...
 |  | ||||||
| 	// 		iterateNonIterable: <bool>,
 |  | ||||||
| 	// 		// If true do not skip item.sublist of items with .collapsed 
 |  | ||||||
| 	// 		// set to true...
 |  | ||||||
| 	// 		iterateCollapsed: <bool>,
 |  | ||||||
| 	// 		// If true skip iterating nested items...
 |  | ||||||
| 	// 		skipNested: <bool>,
 |  | ||||||
| 	//
 |  | ||||||
| 	// 		// If true, reverse iteration order...
 |  | ||||||
| 	// 		// NOTE: containing items will still precede the contained,
 |  | ||||||
| 	// 		//		i.e. this will reverse the level order but not 
 |  | ||||||
| 	// 		//		nesting order...
 |  | ||||||
| 	// 		reverseIteration: <bool>,
 |  | ||||||
| 	//
 |  | ||||||
| 	// 		// If true include inlined parent id in path...
 |  | ||||||
| 	// 		// XXX not implemented yet -- can we implement this???...
 |  | ||||||
| 	// 		inlinedPaths: <bool>,
 |  | ||||||
| 	// 	}
 |  | ||||||
| 	//
 |  | ||||||
| 	//
 |  | ||||||
| 	// XXX make sublist test customizable...
 |  | ||||||
| 	walk: function(func, options){ |  | ||||||
| 		var that = this |  | ||||||
| 
 |  | ||||||
| 		// parse args...
 |  | ||||||
| 		var args = [...arguments] |  | ||||||
| 		func = args[0] instanceof Function ?  |  | ||||||
| 			args.shift()  |  | ||||||
| 			: undefined |  | ||||||
| 		var recursion = (args[0] instanceof Function  |  | ||||||
| 				|| typeof(args[0]) == typeof('str')) ?  |  | ||||||
| 			args.shift()  |  | ||||||
| 			: undefined |  | ||||||
| 		var i = typeof(args[0]) == typeof(123) ? |  | ||||||
| 			args.shift() |  | ||||||
| 			: 0 |  | ||||||
| 		var path = (args[0] instanceof Array  |  | ||||||
| 				|| typeof(args[0]) == typeof('str')) ? |  | ||||||
| 			args.shift() |  | ||||||
| 			: [] |  | ||||||
| 		path = path instanceof Array ? path : [path] |  | ||||||
| 		var options = args.pop() || {} |  | ||||||
| 
 |  | ||||||
| 		// options...
 |  | ||||||
| 		options = Object.assign(Object.create(this.options || {}), options || {}) |  | ||||||
| 		var iterateNonIterable = options.iterateAll || options.iterateNonIterable |  | ||||||
| 		var iterateCollapsed = options.iterateAll || options.iterateCollapsed |  | ||||||
| 		var skipNested = !options.iterateAll && options.skipNested |  | ||||||
| 		var reverse = !!options.reverseIteration |  | ||||||
| 
 |  | ||||||
| 		// level walk function...
 |  | ||||||
| 		var walk = function(i, path, list){ |  | ||||||
| 			return list |  | ||||||
| 				// reverse the items...
 |  | ||||||
| 				.run(function(){ |  | ||||||
| 					return reverse ? |  | ||||||
| 						// NOTE: we .slice() as we do not want to affect 
 |  | ||||||
| 						// 		the actual list...
 |  | ||||||
| 						this.slice().reverse()  |  | ||||||
| 						: this }) |  | ||||||
| 				.map(function(elem){ |  | ||||||
| 					// skip non-iterable items...
 |  | ||||||
| 					if(!iterateNonIterable && elem.noniterable){ |  | ||||||
| 						return [] |  | ||||||
| 					} |  | ||||||
| 
 |  | ||||||
| 					var elem_id = elem.id || elem.value |  | ||||||
| 					// these will be set in the return expression below...
 |  | ||||||
| 					var sublist |  | ||||||
| 					var p  |  | ||||||
| 
 |  | ||||||
| 					// nested browser/list handler...
 |  | ||||||
| 					var nested_called = false |  | ||||||
| 					var nested = function(list, opts){ |  | ||||||
| 						var skip = skipNested && !list |  | ||||||
| 						list = (!iterateCollapsed && elem.collapsed) ? |  | ||||||
| 							[] |  | ||||||
| 							: (list || sublist) |  | ||||||
| 						list = list === true ? sublist : list |  | ||||||
| 						nested_called = true |  | ||||||
| 
 |  | ||||||
| 						return ( |  | ||||||
| 								// request manual iteration...
 |  | ||||||
| 								(skip || list === false) ? |  | ||||||
| 									[] |  | ||||||
| 								:list instanceof Array ? |  | ||||||
| 									walk(i, p, list) |  | ||||||
| 								// user-defined recursion...
 |  | ||||||
| 								: recursion instanceof Function ? |  | ||||||
| 									recursion.call(that, func, i, p, list, opts || options) |  | ||||||
| 								: list[recursion || 'walk'](func, i, p, opts || options)) |  | ||||||
| 				   			.run(function(){ |  | ||||||
| 								var res = this instanceof Array ?  |  | ||||||
| 									this  |  | ||||||
| 									: [this]  |  | ||||||
| 								i += this.length  |  | ||||||
| 								return res |  | ||||||
| 							})} |  | ||||||
| 
 |  | ||||||
| 					return ( |  | ||||||
| 							// inline browser or array...
 |  | ||||||
| 							(elem instanceof Array  |  | ||||||
| 									|| elem instanceof Browser) ? |  | ||||||
| 								func.call(that,  |  | ||||||
| 									i, p = path,  |  | ||||||
| 									null, nested,  |  | ||||||
| 									sublist = elem) |  | ||||||
| 							// nested browser / array...
 |  | ||||||
| 							: (elem.sublist instanceof Browser  |  | ||||||
| 									|| elem.sublist instanceof Array) ? |  | ||||||
| 								func.call(that,  |  | ||||||
| 									i++, p = path.concat([elem_id]),  |  | ||||||
| 									elem, nested,  |  | ||||||
| 									sublist = elem.sublist) |  | ||||||
| 							// normal element...
 |  | ||||||
| 							: func.call(that,  |  | ||||||
| 								i++, p = path.concat([elem_id]),  |  | ||||||
| 								elem, null,  |  | ||||||
| 								sublist = null) ) |  | ||||||
| 						// append nested elements...
 |  | ||||||
| 						.concat((!sublist || nested_called) ?  |  | ||||||
| 							[]  |  | ||||||
| 							: nested(sublist)) |  | ||||||
| 				}) |  | ||||||
| 				.flat() } |  | ||||||
| 
 |  | ||||||
| 		return walk(i, path, this.items) |  | ||||||
| 	}, |  | ||||||
| 
 |  | ||||||
| 	// XXX rename this??
 |  | ||||||
| 	text: function(options, renderer){ |  | ||||||
| 		var that = this |  | ||||||
| 		// XXX Q: should options and context be distinguished only via 
 |  | ||||||
| 		// 		the .options attr as is the case now???
 |  | ||||||
| 		// 		...see no reason why not, though it does not feel right...
 |  | ||||||
| 		var context = (options == null || options.options == null) ? |  | ||||||
| 				{ |  | ||||||
| 					root: this, |  | ||||||
| 					// NOTE: we are not combining this with .options as nested 
 |  | ||||||
| 					// 		lists can have their own unique sets of options 
 |  | ||||||
| 					// 		independently of the root list...
 |  | ||||||
| 					options: Object.assign( |  | ||||||
| 						Object.create(this.options || {}), |  | ||||||
| 						// defaults...
 |  | ||||||
| 						// XXX is this the correct way to setup defaults???
 |  | ||||||
| 						{ |  | ||||||
| 							iterateNonIterable: true, |  | ||||||
| 						},  |  | ||||||
| 						options || {}), |  | ||||||
| 				} |  | ||||||
| 			: options |  | ||||||
| 		options = context.options |  | ||||||
| 		renderer = renderer || this |  | ||||||
| 
 |  | ||||||
| 		var getValue = function(item){ |  | ||||||
| 			return item.value || item } |  | ||||||
| 
 |  | ||||||
| 		var items = this |  | ||||||
| 			.walk( |  | ||||||
| 				function(i, path, item, nested, sublist){ |  | ||||||
| 					var indent = path.map(e => '  ').join('') |  | ||||||
| 					return ( |  | ||||||
| 						// inline...
 |  | ||||||
| 						(item == null && sublist) ? |  | ||||||
| 							// NOTE: here we are forcing rendering of the 
 |  | ||||||
| 							// 		inline browser/list, i.e. ignoring 
 |  | ||||||
| 							// 		options.skipNested for inline stuff...
 |  | ||||||
| 							nested(true) |  | ||||||
| 								.map(function(e){ |  | ||||||
| 									return indent + getValue(e) }) |  | ||||||
| 						// nested...
 |  | ||||||
| 						: sublist ? |  | ||||||
| 							[item.value] |  | ||||||
| 								.concat( |  | ||||||
| 									nested() |  | ||||||
| 										.map(function(e){ |  | ||||||
| 											return indent + getValue(e) })) |  | ||||||
| 						// single item...
 |  | ||||||
| 						: [getValue(item)] |  | ||||||
| 					) }, |  | ||||||
| 				function(func, i, path, sublist, options){ |  | ||||||
| 					return sublist.text(context) }, |  | ||||||
| 				options) |  | ||||||
| 
 |  | ||||||
| 		return context.root === this ? |  | ||||||
| 			items.join('\n') |  | ||||||
| 			: items |  | ||||||
| 	}, |  | ||||||
| 
 |  | ||||||
| 	// XXX test sublist via .render rather than instanceof...
 |  | ||||||
| 	render: function(options, renderer){ |  | ||||||
| 		var that = this |  | ||||||
| 		// XXX Q: should options and context be distinguished only via 
 |  | ||||||
| 		// 		the .options attr as is the case now???
 |  | ||||||
| 		// 		...see no reason why not, though it does not feel right...
 |  | ||||||
| 		var context = (options == null || options.options == null) ? |  | ||||||
| 				{ |  | ||||||
| 					root: this, |  | ||||||
| 					// NOTE: we are not combining this with .options as nested 
 |  | ||||||
| 					// 		lists can have their own unique sets of options 
 |  | ||||||
| 					// 		independently of the root list...
 |  | ||||||
| 					//options: options || this.options || {},
 |  | ||||||
| 					options: Object.assign( |  | ||||||
| 						Object.create(this.options || {}), |  | ||||||
| 						// defaults...
 |  | ||||||
| 						// XXX is this the correct way to setup defaults???
 |  | ||||||
| 						{ |  | ||||||
| 							iterateNonIterable: true, |  | ||||||
| 						},  |  | ||||||
| 						options || {}), |  | ||||||
| 				} |  | ||||||
| 			: options |  | ||||||
| 		options = context.options |  | ||||||
| 		renderer = renderer || this |  | ||||||
| 
 |  | ||||||
| 		// XXX should we control render parameters (range, start, end, ...)
 |  | ||||||
| 		// 		from outside render and pass this info down to nested lists???
 |  | ||||||
| 		// 		...if yes how??
 |  | ||||||
| 		// 			- options
 |  | ||||||
| 		// 			- arg threading
 |  | ||||||
| 		var items = this |  | ||||||
| 			.walk( |  | ||||||
| 				function(i, path, item, nested, sublist){ |  | ||||||
| 					return ( |  | ||||||
| 						// inline...
 |  | ||||||
| 						(item == null && sublist) ? |  | ||||||
| 							// NOTE: here we are forcing rendering of the 
 |  | ||||||
| 							// 		inline browser/list, i.e. ignoring 
 |  | ||||||
| 							// 		options.skipNested for inline stuff...
 |  | ||||||
| 							// NOTE: we here do not distinguish between
 |  | ||||||
| 							// 		inlined lists and browsers... (XXX ???)
 |  | ||||||
| 							[ renderer.renderGroup(nested(true), context) ] |  | ||||||
| 						// nested...
 |  | ||||||
| 						: sublist ? |  | ||||||
| 							[ renderer.renderNested( |  | ||||||
| 								renderer.renderNestedHeader(item, i, context), |  | ||||||
| 								nested(), |  | ||||||
| 								item,  |  | ||||||
| 								context) ] |  | ||||||
| 						// normal item...
 |  | ||||||
| 						: [ renderer.renderItem(item, i, context) ] ) }, |  | ||||||
| 				function(func, i, path, sublist, options){ |  | ||||||
| 					return sublist.render(context, renderer) }, |  | ||||||
| 				options) |  | ||||||
| 
 |  | ||||||
| 		// determine the render mode...
 |  | ||||||
| 		return context.root === this ? |  | ||||||
| 			// root context -> render list and return this...
 |  | ||||||
| 			renderer.renderList(items, context) |  | ||||||
| 			// nested context -> return item list...
 |  | ||||||
| 			: items |  | ||||||
| 	}, |  | ||||||
| 
 |  | ||||||
| 	// Extended map...
 |  | ||||||
| 	//
 |  | ||||||
| 	//	Get all items...
 |  | ||||||
| 	//	.map([options])
 |  | ||||||
| 	//		-> items
 |  | ||||||
| 	//
 |  | ||||||
| 	//	Map func to items...
 |  | ||||||
| 	//	.map(func[, options])
 |  | ||||||
| 	//		-> items
 |  | ||||||
| 	//
 |  | ||||||
| 	//
 |  | ||||||
| 	//
 |  | ||||||
| 	//	func(item, index, path, browser)
 |  | ||||||
| 	//		-> result
 |  | ||||||
| 	//
 |  | ||||||
| 	//
 |  | ||||||
| 	//
 |  | ||||||
| 	// For supported options see docs for .walk(..)
 |  | ||||||
| 	//
 |  | ||||||
| 	// By default this will not iterate items that are:
 |  | ||||||
| 	// 	- non-iterable (item.noniterable is true)
 |  | ||||||
| 	// 	- collapsed sub-items (item.collapsed is true)
 |  | ||||||
| 	//
 |  | ||||||
| 	// This extends the Array .map(..) by adding:
 |  | ||||||
| 	// 	- ability to run without arguments
 |  | ||||||
| 	// 	- support for options
 |  | ||||||
| 	//
 |  | ||||||
| 	//
 |  | ||||||
| 	// XXX make item access by index lazy... 
 |  | ||||||
| 	// 		- index nested stuff and lengths... (.sublist_length)
 |  | ||||||
| 	// 		- stop when target reached... (control callback???)
 |  | ||||||
| 	// XXX Q: should we have an option to treat groups as elements???
 |  | ||||||
| 	map: function(func, options){ |  | ||||||
| 		var that = this |  | ||||||
| 
 |  | ||||||
| 		// parse args...
 |  | ||||||
| 		//
 |  | ||||||
| 		// NOTE: in addition to the user signatures documented above this
 |  | ||||||
| 		// 		also supports two variants used internally:
 |  | ||||||
| 		// 			.map(func, path[, options])
 |  | ||||||
| 		// 			.map(func, i, path[, options])
 |  | ||||||
| 		// 		these set the "base" path and index passed to func...
 |  | ||||||
| 		// XXX can we avoid argument parsing here???
 |  | ||||||
| 		var args = [...arguments] |  | ||||||
| 		func = args[0] instanceof Function ?  |  | ||||||
| 			args.shift()  |  | ||||||
| 			: undefined |  | ||||||
| 		var i = typeof(args[0]) == typeof(123) ? |  | ||||||
| 			args.shift() |  | ||||||
| 			: 0 |  | ||||||
| 		var path = (args[0] instanceof Array  |  | ||||||
| 				|| typeof(args[0]) == typeof('str')) ? |  | ||||||
| 			args.shift() |  | ||||||
| 			: [] |  | ||||||
| 		var options = args.pop() || {} |  | ||||||
| 
 |  | ||||||
| 		return this.walk( |  | ||||||
| 			function(i, path, elem){ |  | ||||||
| 				return elem != null ? |  | ||||||
| 					[func === undefined ? |  | ||||||
| 						elem |  | ||||||
| 						// XXX should this pass the current or the root 
 |  | ||||||
| 						// 		container to func???
 |  | ||||||
| 						: func.call(that, elem, i, path, that)] |  | ||||||
| 					: [] },  |  | ||||||
| 			function(_, i, path, sublist, options){ |  | ||||||
| 				// NOTE: this needs to call the actual func that the user
 |  | ||||||
| 				// 		gave us and not the constructed function that we 
 |  | ||||||
| 				// 		pass to .walk(..) above...
 |  | ||||||
| 				return sublist.map(func, i, path, options) }, |  | ||||||
| 			i, |  | ||||||
| 			path,  |  | ||||||
| 			options) |  | ||||||
| 	}, |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| 	// Sublist map functions...
 |  | ||||||
| 	// NOTE: there are different from .map(..) in that instead of paths 
 |  | ||||||
| 	// 		func(..) will get indexes in the current browser...
 |  | ||||||
| 	// NOTE: these will return a sparse array...
 |  | ||||||
| 	sublists: function(func, options){ |  | ||||||
| 		var that = this |  | ||||||
| 		//options = options || {}
 |  | ||||||
| 		options = Object.assign(Object.create(this.options || {}), options || {}) |  | ||||||
| 		var skipNested = options.skipNested |  | ||||||
| 		var skipInlined = options.skipInlined |  | ||||||
| 
 |  | ||||||
| 		var res = [] |  | ||||||
| 		this.items |  | ||||||
| 			.forEach(function(elem, i){ |  | ||||||
| 				if((!skipInlined && elem.value instanceof Browser) |  | ||||||
| 							|| (!skipNested && elem.sublist)){ |  | ||||||
| 					res[i] = func ? |  | ||||||
| 						func.call(that, elem, i, that) |  | ||||||
| 						: elem  |  | ||||||
| 				} }) |  | ||||||
| 		return res  |  | ||||||
| 	}, |  | ||||||
| 	nested: function(func){ |  | ||||||
| 		return this.sublists(func, {skipInlined: true}) }, |  | ||||||
| 	inlined: function(func){ |  | ||||||
| 		return this.sublists(func, {skipNested: true}) }, |  | ||||||
| 
 |  | ||||||
| 	next: function(){}, |  | ||||||
| 	prev: function(){}, |  | ||||||
| 
 |  | ||||||
| 	// XXX should there return an array or a .constructor(..) instance??
 |  | ||||||
| 	// XXX should these call respective methods (.forEach(..), .filter(..), 
 |  | ||||||
| 	// 		.reduce(..)) on the nested browsers???
 |  | ||||||
| 	forEach: function(func, options){ |  | ||||||
| 		this.map(...arguments) |  | ||||||
| 		return this }, |  | ||||||
| 	filter: function(func, options){ |  | ||||||
| 		return this.map(function(e, i, p, b){ |  | ||||||
| 			return func.call(this, e, i, p, b) ? [e] : [] }) |  | ||||||
| 		.flat() }, |  | ||||||
| 	reduce: function(){}, |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| 	// XXX do we need edit ability here? 
 |  | ||||||
| 	// 		i.e. .set(..), .remove(..), .sort(..), ...
 |  | ||||||
| 	// 		...if we are going to implement editing then we'll need to 
 |  | ||||||
| 	// 		callback the user code or update the user state...
 |  | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| 	// Events...
 | 	// Events...
 | ||||||
|  | |||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user