| 
									
										
										
										
											2017-12-20 04:39:03 +03:00
										 |  |  | /********************************************************************** | 
					
						
							|  |  |  | *  | 
					
						
							|  |  |  | * | 
					
						
							|  |  |  | * | 
					
						
							|  |  |  | **********************************************************************/ | 
					
						
							|  |  |  | ((typeof define)[0]=='u'?function(f){module.exports=f(require)}:define) | 
					
						
							|  |  |  | (function(require){ var module={} // make module AMD/node compatible...
 | 
					
						
							|  |  |  | /*********************************************************************/ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | var toggler = require('../toggler') | 
					
						
							|  |  |  | var keyboard = require('../keyboard') | 
					
						
							|  |  |  | var object = require('../object') | 
					
						
							|  |  |  | var widget = require('./widget') | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-05-03 19:43:22 +03:00
										 |  |  | // XXX
 | 
					
						
							|  |  |  | //var walk = require('lib/walk')
 | 
					
						
							|  |  |  | var walk = require('../../node_modules/generic-walk/walk').walk | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-12-20 04:39:03 +03:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /*********************************************************************/ | 
					
						
							| 
									
										
										
										
											2019-02-05 18:25:23 +03:00
										 |  |  | // Helpers...
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-02-05 22:12:33 +03:00
										 |  |  | // Collect a list of literal values and "make(..) calls" into an array...
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | //	collectItems(context, items)
 | 
					
						
							|  |  |  | //		-> values
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | // items format:
 | 
					
						
							|  |  |  | // 	[
 | 
					
						
							|  |  |  | // 		// explicit value...
 | 
					
						
							|  |  |  | // 		value,
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | // 		// literal make call...
 | 
					
						
							|  |  |  | // 		make(..),
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | // 		...
 | 
					
						
							|  |  |  | // 	]
 | 
					
						
							|  |  |  | //
 | 
					
						
							| 
									
										
										
										
											2019-02-05 18:25:23 +03:00
										 |  |  | // NOTE: this will remove the made via make(..) items from .items thus the
 | 
					
						
							|  |  |  | // 		caller is responsible for adding them back...
 | 
					
						
							| 
									
										
										
										
											2019-02-05 22:12:33 +03:00
										 |  |  | // NOTE: this uses the make(..) return value to implicitly infer the items
 | 
					
						
							|  |  |  | // 		to collect, thus the items must already be constructed and in 
 | 
					
						
							|  |  |  | // 		the same order as they are present in .items
 | 
					
						
							|  |  |  | // 		...also, considering that this implicitly identifies the items 
 | 
					
						
							|  |  |  | // 		passing the make function without calling it can trick the system
 | 
					
						
							|  |  |  | // 		and lead to unexpected results.
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | // XXX would be nice to have a better check/test...
 | 
					
						
							|  |  |  | // 		...this could be done by chaining instances of make instead of 
 | 
					
						
							|  |  |  | // 		returning an actual function, i.e. each make call would return 
 | 
					
						
							|  |  |  | // 		a "new" function that would reference the actual item (.item())
 | 
					
						
							|  |  |  | // 		and the previous item created (.prevItem()), ... etc.
 | 
					
						
							|  |  |  | // 		...this would enable us to uniquely identify the actual items 
 | 
					
						
							|  |  |  | // 		and prevent allot of specific errors...
 | 
					
						
							| 
									
										
										
										
											2019-03-11 19:40:41 +03:00
										 |  |  | var collectItems = function(make, items){ | 
					
						
							| 
									
										
										
										
											2019-02-05 18:25:23 +03:00
										 |  |  | 	var made = items | 
					
						
							|  |  |  | 		.filter(function(e){ | 
					
						
							| 
									
										
										
										
											2019-03-11 19:40:41 +03:00
										 |  |  | 			return e === make }) | 
					
						
							| 
									
										
										
										
											2019-02-05 18:25:23 +03:00
										 |  |  | 	// constructed item list...
 | 
					
						
							| 
									
										
										
										
											2019-02-05 22:12:33 +03:00
										 |  |  | 	// ...remove each instance from .items
 | 
					
						
							| 
									
										
										
										
											2019-03-11 19:40:41 +03:00
										 |  |  | 	made = make.items.splice( | 
					
						
							|  |  |  | 		make.items.length - made.length,  | 
					
						
							| 
									
										
										
										
											2019-02-05 22:12:33 +03:00
										 |  |  | 		made.length) | 
					
						
							| 
									
										
										
										
											2019-02-05 18:25:23 +03:00
										 |  |  | 	// get the actual item values...
 | 
					
						
							|  |  |  | 	return items | 
					
						
							|  |  |  | 		.map(function(e){ | 
					
						
							| 
									
										
										
										
											2019-03-11 19:40:41 +03:00
										 |  |  | 			return e === make ? | 
					
						
							| 
									
										
										
										
											2019-02-05 18:25:23 +03:00
										 |  |  | 				made.shift() | 
					
						
							| 
									
										
										
										
											2019-03-11 19:40:41 +03:00
										 |  |  | 				// raw item -> make(..)
 | 
					
						
							|  |  |  | 				: (make(e)  | 
					
						
							|  |  |  | 					&& make.items.pop()) }) } | 
					
						
							| 
									
										
										
										
											2019-02-05 18:25:23 +03:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | //---------------------------------------------------------------------
 | 
					
						
							| 
									
										
										
										
											2017-12-20 04:39:03 +03:00
										 |  |  | // XXX general design:
 | 
					
						
							|  |  |  | // 		- each of these can take either a value or a function (constructor)
 | 
					
						
							|  |  |  | //		- the function has access to Items.* and context
 | 
					
						
							|  |  |  | //		- the constructor can be called from two contexts:
 | 
					
						
							|  |  |  | //			- external
 | 
					
						
							|  |  |  | //				called from the module or as a function...
 | 
					
						
							|  |  |  | //				calls the passed constructor (passing context)
 | 
					
						
							|  |  |  | //				builds the container
 | 
					
						
							|  |  |  | //			- nested
 | 
					
						
							|  |  |  | //				called from constructor function...
 | 
					
						
							|  |  |  | //				calls constructor (if applicable)
 | 
					
						
							|  |  |  | //				builds item(s)
 | 
					
						
							|  |  |  | // XXX need a way to pass container constructors (a-la ui-widgets dialog containers)
 | 
					
						
							|  |  |  | // 		- passing through the context (this) makes this more flexible...
 | 
					
						
							|  |  |  | // 		- passing via args fixes the signature which is a good thing...
 | 
					
						
							|  |  |  | //		
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // XXX
 | 
					
						
							| 
									
										
										
										
											2019-02-10 18:09:08 +03:00
										 |  |  | // XXX can't use Object.assign(..) here as it will not copy props...
 | 
					
						
							| 
									
										
										
										
											2017-12-20 04:39:03 +03:00
										 |  |  | var Items = module.items = function(){} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-01-26 21:32:14 +03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-02-05 22:12:33 +03:00
										 |  |  | // placeholders...
 | 
					
						
							| 
									
										
										
										
											2019-01-26 21:32:14 +03:00
										 |  |  | Items.dialog = null | 
					
						
							|  |  |  | Items.items = null | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-02-04 14:18:00 +03:00
										 |  |  | // Last item created...
 | 
					
						
							|  |  |  | // XXX not sure about this...
 | 
					
						
							|  |  |  | // XXX should this be a prop???
 | 
					
						
							|  |  |  | Items.last = function(){ | 
					
						
							|  |  |  | 	return (this.items || [])[this.items.length - 1] } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-01-26 21:32:14 +03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-01-22 07:10:49 +03:00
										 |  |  | // Focus last created item...
 | 
					
						
							|  |  |  | Items.focus = function(){ | 
					
						
							| 
									
										
										
										
											2019-02-10 18:09:08 +03:00
										 |  |  | 	this.last().current = true } | 
					
						
							| 
									
										
										
										
											2019-01-23 05:45:50 +03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-01-26 21:32:14 +03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-02-04 14:18:00 +03:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-02-05 22:12:33 +03:00
										 |  |  | // Group a set of items...
 | 
					
						
							| 
									
										
										
										
											2019-01-26 21:32:14 +03:00
										 |  |  | //
 | 
					
						
							|  |  |  | //	.group(make(..), ..)
 | 
					
						
							| 
									
										
										
										
											2019-02-05 22:12:33 +03:00
										 |  |  | //	.group([make(..), ..])
 | 
					
						
							| 
									
										
										
										
											2019-01-26 21:32:14 +03:00
										 |  |  | //		-> make
 | 
					
						
							|  |  |  | //
 | 
					
						
							| 
									
										
										
										
											2019-02-05 22:12:33 +03:00
										 |  |  | //
 | 
					
						
							| 
									
										
										
										
											2019-01-26 21:32:14 +03:00
										 |  |  | // Example:
 | 
					
						
							|  |  |  | // 	make.group(
 | 
					
						
							| 
									
										
										
										
											2019-02-02 18:45:14 +03:00
										 |  |  | // 		make('made item'),
 | 
					
						
							| 
									
										
										
										
											2019-02-05 18:25:23 +03:00
										 |  |  | // 		'literal item',
 | 
					
						
							|  |  |  | // 		...)
 | 
					
						
							| 
									
										
										
										
											2019-01-23 05:45:50 +03:00
										 |  |  | //
 | 
					
						
							| 
									
										
										
										
											2019-02-05 22:12:33 +03:00
										 |  |  | //
 | 
					
						
							|  |  |  | // NOTE: see notes to collectItems(..) for more info...
 | 
					
						
							|  |  |  | //
 | 
					
						
							| 
									
										
										
										
											2019-01-26 21:32:14 +03:00
										 |  |  | // XXX do we need to pass options to groups???
 | 
					
						
							|  |  |  | Items.group = function(...items){ | 
					
						
							| 
									
										
										
										
											2019-01-26 22:34:56 +03:00
										 |  |  | 	var that = this | 
					
						
							| 
									
										
										
										
											2019-02-04 14:18:00 +03:00
										 |  |  | 	items = items.length == 1 && items[0] instanceof Array ? | 
					
						
							|  |  |  | 		items[0] | 
					
						
							|  |  |  | 		: items | 
					
						
							| 
									
										
										
										
											2019-01-26 22:34:56 +03:00
										 |  |  | 	// replace the items with the group...
 | 
					
						
							| 
									
										
										
										
											2019-04-20 17:08:10 +03:00
										 |  |  | 	this.items.splice(this.items.length, 0, collectItems(this, items)) | 
					
						
							| 
									
										
										
										
											2019-01-26 21:32:14 +03:00
										 |  |  | 	return this | 
					
						
							| 
									
										
										
										
											2019-01-23 05:45:50 +03:00
										 |  |  | } | 
					
						
							| 
									
										
										
										
											2019-01-22 07:10:49 +03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-02-05 22:12:33 +03:00
										 |  |  | 
 | 
					
						
							|  |  |  | // Place list in a sub-list of item...
 | 
					
						
							|  |  |  | //
 | 
					
						
							| 
									
										
										
										
											2019-03-20 16:54:10 +03:00
										 |  |  | // XXX options???
 | 
					
						
							| 
									
										
										
										
											2019-01-26 21:32:14 +03:00
										 |  |  | Items.nest = function(item, list, options){ | 
					
						
							|  |  |  | 	options = options || {} | 
					
						
							| 
									
										
										
										
											2019-03-20 16:54:10 +03:00
										 |  |  | 	//options = Object.assign(Object.create(this.options || {}), options || {})
 | 
					
						
							| 
									
										
										
										
											2019-05-04 04:46:29 +03:00
										 |  |  | 	options.children = list instanceof Array ? | 
					
						
							| 
									
										
										
										
											2019-02-05 22:12:33 +03:00
										 |  |  | 		collectItems(this, list) | 
					
						
							| 
									
										
										
										
											2019-02-04 14:18:00 +03:00
										 |  |  | 		: list | 
					
						
							| 
									
										
										
										
											2019-01-26 21:32:14 +03:00
										 |  |  | 	return this(item, options) | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2019-01-24 05:32:21 +03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-01-22 07:10:49 +03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-02-05 22:12:33 +03:00
										 |  |  | 
 | 
					
						
							|  |  |  | //---------------------------------------------------------------------
 | 
					
						
							|  |  |  | // wrappers...
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-02-05 18:11:36 +03:00
										 |  |  | Items.Item = function(value, options){} | 
					
						
							|  |  |  | Items.Action = function(value, options){} | 
					
						
							|  |  |  | Items.Heading = function(value, options){} | 
					
						
							| 
									
										
										
										
											2019-01-20 05:39:47 +03:00
										 |  |  | Items.Empty = function(value){} | 
					
						
							|  |  |  | Items.Separator = function(value){} | 
					
						
							|  |  |  | Items.Spinner = function(value){} | 
					
						
							|  |  |  | Items.Selected = function(value){} | 
					
						
							|  |  |  | Items.Editable = function(value){} | 
					
						
							|  |  |  | Items.ConfirmAction = function(value){} | 
					
						
							| 
									
										
										
										
											2017-12-20 04:39:03 +03:00
										 |  |  | 
 | 
					
						
							|  |  |  | // lists...
 | 
					
						
							| 
									
										
										
										
											2019-02-05 18:11:36 +03:00
										 |  |  | Items.List = function(values){} | 
					
						
							| 
									
										
										
										
											2019-01-20 05:39:47 +03:00
										 |  |  | Items.EditableList = function(values){} | 
					
						
							|  |  |  | Items.EditablePinnedList = function(values){} | 
					
						
							| 
									
										
										
										
											2017-12-20 04:39:03 +03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-01-20 05:39:47 +03:00
										 |  |  | // Special list components...
 | 
					
						
							|  |  |  | Items.ListPath = function(){} | 
					
						
							|  |  |  | Items.ListTitle = function(){} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-12-20 04:39:03 +03:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-01-21 05:29:26 +03:00
										 |  |  | //---------------------------------------------------------------------
 | 
					
						
							| 
									
										
										
										
											2019-05-22 23:28:38 +03:00
										 |  |  | // Event system...
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | // XXX might be a good idea to make this a generic module...
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-01-21 05:29:26 +03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-02-25 03:34:21 +03:00
										 |  |  | // Generate an event method...
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | //	Trigger an event
 | 
					
						
							|  |  |  | //	.event()
 | 
					
						
							|  |  |  | //	.event(arg, ..)
 | 
					
						
							|  |  |  | //		-> this
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | //	Bind an event handler...
 | 
					
						
							|  |  |  | //	.event(func)
 | 
					
						
							|  |  |  | //		-> this
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | //
 | 
					
						
							| 
									
										
										
										
											2019-05-15 21:45:20 +03:00
										 |  |  | // XXX make the event object customizable...
 | 
					
						
							|  |  |  | // XXX STUB event object...
 | 
					
						
							| 
									
										
										
										
											2019-02-24 14:51:35 +03:00
										 |  |  | var makeEventMethod = function(event, handler){ | 
					
						
							| 
									
										
										
										
											2019-05-18 02:58:38 +03:00
										 |  |  | 	return Object.assign( | 
					
						
							|  |  |  | 		function(item){ | 
					
						
							|  |  |  | 			// register handler...
 | 
					
						
							|  |  |  | 			if(item instanceof Function){ | 
					
						
							|  |  |  | 				return this.on(event, item)  | 
					
						
							|  |  |  | 			} | 
					
						
							| 
									
										
										
										
											2019-02-24 14:51:35 +03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-05-18 02:58:38 +03:00
										 |  |  | 			// XXX STUB: event object...
 | 
					
						
							|  |  |  | 			// XXX can we generate this in one spot???
 | 
					
						
							|  |  |  | 			// 		...currently it is generated here and in .trigger(..)
 | 
					
						
							|  |  |  | 			var evt = { | 
					
						
							|  |  |  | 				name: event, | 
					
						
							|  |  |  | 				// XXX
 | 
					
						
							|  |  |  | 				//stopPropagation: function(){
 | 
					
						
							|  |  |  | 				//},
 | 
					
						
							|  |  |  | 			} | 
					
						
							| 
									
										
										
										
											2019-02-26 04:20:05 +03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-05-18 02:58:38 +03:00
										 |  |  | 			handler | 
					
						
							|  |  |  | 				&& handler.call(this, evt, ...arguments) | 
					
						
							| 
									
										
										
										
											2019-02-24 14:51:35 +03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-05-18 02:58:38 +03:00
										 |  |  | 			return this | 
					
						
							|  |  |  | 				.trigger(evt, ...arguments) | 
					
						
							|  |  |  | 		}, | 
					
						
							|  |  |  | 		{ | 
					
						
							|  |  |  | 			event: event, | 
					
						
							|  |  |  | 		}) } | 
					
						
							| 
									
										
										
										
											2019-02-24 14:51:35 +03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-05-16 05:00:15 +03:00
										 |  |  | // Call item event handlers...
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | // 	callItemEventHandlers(item, event_name, event_object, ...)
 | 
					
						
							|  |  |  | // 		-> null
 | 
					
						
							|  |  |  | //
 | 
					
						
							| 
									
										
										
										
											2019-05-19 15:38:36 +03:00
										 |  |  | // XXX should this call item.parent.trigger(..) ???
 | 
					
						
							| 
									
										
										
										
											2019-05-15 21:45:20 +03:00
										 |  |  | var callItemEventHandlers = function(item, event, evt, ...args){ | 
					
						
							| 
									
										
										
										
											2019-03-02 17:29:26 +03:00
										 |  |  | 	;(item[event] ? | 
					
						
							|  |  |  | 			[item[event]] | 
					
						
							|  |  |  | 			: []) | 
					
						
							|  |  |  | 		.concat((item.events || {})[event] || []) | 
					
						
							|  |  |  | 		.forEach(function(handler){ | 
					
						
							|  |  |  | 			// XXX revise call signature...
 | 
					
						
							|  |  |  | 			handler.call(item, evt, item, ...args) }) } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-05-16 05:00:15 +03:00
										 |  |  | // Generate item event method...
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | // This extends makeEventMethod(..) by adding an option to pass an item
 | 
					
						
							|  |  |  | // when triggering the event, the rest of the signature is identical.
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | // 	Trigger an event on item(s)...
 | 
					
						
							|  |  |  | // 	.event(item, ..)
 | 
					
						
							|  |  |  | // 	.event([item, ..], ..)
 | 
					
						
							|  |  |  | // 		-> this
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | // NOTE: item is compatible to .search(item, ..) spec, see that for more 
 | 
					
						
							|  |  |  | // 		details...
 | 
					
						
							| 
									
										
										
										
											2019-05-19 14:11:58 +03:00
										 |  |  | //
 | 
					
						
							|  |  |  | // XXX need to trigger item parent's event too...
 | 
					
						
							|  |  |  | // 		Q: trigger once, per call or once per item???
 | 
					
						
							|  |  |  | // 		Q: should this be done here or in the client???
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | // XXX problems with event propagation: 
 | 
					
						
							|  |  |  | // 		- the item can be within a nested browser/list and that 
 | 
					
						
							|  |  |  | // 			container's events will not be triggered from here,
 | 
					
						
							|  |  |  | // 			just the item's, this and parent's...
 | 
					
						
							|  |  |  | // 		- if we get item.parent and trigger it's event directly
 | 
					
						
							|  |  |  | // 			then that event may propogate up and this' and this.parent's
 | 
					
						
							|  |  |  | // 			events may get triggered more than once...
 | 
					
						
							|  |  |  | // 		- we can not rely on full bottom/up propagation as some
 | 
					
						
							|  |  |  | // 			containers in the path may be basic Arrays...
 | 
					
						
							| 
									
										
										
										
											2019-05-19 15:38:36 +03:00
										 |  |  | // 		- need to make this work with .trigger(..) for non-item events
 | 
					
						
							|  |  |  | // 			i.e. currently .trigger(..) propagates the event to parent and
 | 
					
						
							|  |  |  | // 			this may conflict with us triggering events on the path...
 | 
					
						
							| 
									
										
										
										
											2019-05-21 04:27:12 +03:00
										 |  |  | // XXX one idea to do event propagation is to use the actual .search(..)
 | 
					
						
							|  |  |  | // 		mechanic to handle each found item and collect their direct 
 | 
					
						
							|  |  |  | // 		parents...
 | 
					
						
							|  |  |  | // 		we need:
 | 
					
						
							|  |  |  | // 			- trigger each item event
 | 
					
						
							|  |  |  | // 			- propagate the event through the path for each item
 | 
					
						
							|  |  |  | // 			- trigger each parent only once, passing it a list of only 
 | 
					
						
							|  |  |  | // 				relevant items (i.e. items in its sub-tree only)
 | 
					
						
							|  |  |  | // 			- handle .stopPropagation(..) correnctly
 | 
					
						
							|  |  |  | // 				- stop propagation up but finish the level???
 | 
					
						
							|  |  |  | // 			- trigger the root.parent's event when done
 | 
					
						
							| 
									
										
										
										
											2019-05-23 04:20:59 +03:00
										 |  |  | // XXX Q: do we need item event grouping???
 | 
					
						
							|  |  |  | // 		...e.g. should .select('*') trigger a 'select' on each item and 
 | 
					
						
							|  |  |  | // 		then on groups of contained items per container, or should it be
 | 
					
						
							|  |  |  | // 		triggered once per item per relevant container???
 | 
					
						
							|  |  |  | // 		....considering that I see no obvious way to implement grouping
 | 
					
						
							|  |  |  | // 		up the tree in a recursive manner I'm starting to think that we 
 | 
					
						
							|  |  |  | // 		should go the simple route and trigger 1:1 from each leaf and up...
 | 
					
						
							|  |  |  | // 		NOTE: this approach would mean that .trigger(..) itself would NOT
 | 
					
						
							|  |  |  | // 			call any handlers on the current level, it would just propagate
 | 
					
						
							|  |  |  | // 			the event down, and the handlers would get called on the way 
 | 
					
						
							|  |  |  | // 			back up (unless .stopPropagation(..) is called)
 | 
					
						
							|  |  |  | // 		NOTE: this would also make item and container events behave in 
 | 
					
						
							|  |  |  | // 			a different manner:
 | 
					
						
							|  |  |  | // 				- container event calls handlers here and propagates up
 | 
					
						
							|  |  |  | // 				- item event propagates down then triggers container events up
 | 
					
						
							|  |  |  | // 					XXX how do we distinguish these (down/up) events???
 | 
					
						
							|  |  |  | // 						...different events, event state/mode, ...???
 | 
					
						
							| 
									
										
										
										
											2019-05-13 18:58:17 +03:00
										 |  |  | var makeItemEventMethod = function(event, handler, options){ | 
					
						
							| 
									
										
										
										
											2019-05-15 21:45:20 +03:00
										 |  |  | 	options = Object.assign( | 
					
						
							| 
									
										
										
										
											2019-05-16 05:00:15 +03:00
										 |  |  | 		// NOTE: we need to be able to pass item objects, so we can not
 | 
					
						
							|  |  |  | 		// 		use queries at the same time as there is not way to 
 | 
					
						
							|  |  |  | 		// 		distinguish one from the other...
 | 
					
						
							| 
									
										
										
										
											2019-05-15 21:45:20 +03:00
										 |  |  | 		{ noQueryCheck: true }, | 
					
						
							|  |  |  | 		options || {}) | 
					
						
							| 
									
										
										
										
											2019-05-22 23:28:38 +03:00
										 |  |  | 	// base event method...
 | 
					
						
							| 
									
										
										
										
											2019-05-14 16:36:28 +03:00
										 |  |  | 	// NOTE: this is not returned directly as we need to query the items
 | 
					
						
							|  |  |  | 	// 		and pass those on to the handlers rather than the arguments 
 | 
					
						
							|  |  |  | 	// 		as-is...
 | 
					
						
							| 
									
										
										
										
											2019-05-22 23:28:38 +03:00
										 |  |  | 	var base = makeEventMethod(event,  | 
					
						
							| 
									
										
										
										
											2019-05-14 16:36:28 +03:00
										 |  |  | 		function(evt, item, ...args){ | 
					
						
							|  |  |  | 			handler | 
					
						
							| 
									
										
										
										
											2019-05-15 21:45:20 +03:00
										 |  |  | 				&& handler.call(this, evt, item.slice(), ...args) | 
					
						
							| 
									
										
										
										
											2019-05-14 16:36:28 +03:00
										 |  |  | 			item.forEach(function(item){ | 
					
						
							| 
									
										
										
										
											2019-05-15 21:45:20 +03:00
										 |  |  | 				callItemEventHandlers(item, event, evt, ...args) }) })  | 
					
						
							| 
									
										
										
										
											2019-05-18 02:58:38 +03:00
										 |  |  | 	return Object.assign( | 
					
						
							| 
									
										
										
										
											2019-05-22 23:28:38 +03:00
										 |  |  | 		// the actual method we return...
 | 
					
						
							| 
									
										
										
										
											2019-05-18 02:58:38 +03:00
										 |  |  | 		function(item, ...args){ | 
					
						
							|  |  |  | 			var that = this | 
					
						
							| 
									
										
										
										
											2019-05-22 23:28:38 +03:00
										 |  |  | 			return base.call(this,  | 
					
						
							| 
									
										
										
										
											2019-05-18 02:58:38 +03:00
										 |  |  | 				// event handler...
 | 
					
						
							|  |  |  | 				item instanceof Function ? | 
					
						
							|  |  |  | 					item | 
					
						
							|  |  |  | 				// array of queries...
 | 
					
						
							|  |  |  | 				: item instanceof Array ? | 
					
						
							|  |  |  | 					item | 
					
						
							|  |  |  | 						.map(function(e){ | 
					
						
							|  |  |  | 							return that.search(e, options) }) | 
					
						
							|  |  |  | 						.flat() | 
					
						
							|  |  |  | 						.unique() | 
					
						
							|  |  |  | 				// explicit item or query...
 | 
					
						
							|  |  |  | 				: item != null ?  | 
					
						
							|  |  |  | 					this.search(item, options)  | 
					
						
							|  |  |  | 				: [], | 
					
						
							|  |  |  | 				...args) }, | 
					
						
							| 
									
										
										
										
											2019-05-22 23:28:38 +03:00
										 |  |  | 			// get base method attributes -- keep the event method format...
 | 
					
						
							|  |  |  |    			base)	} | 
					
						
							| 
									
										
										
										
											2019-03-02 17:29:26 +03:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-02-25 03:34:21 +03:00
										 |  |  | 
 | 
					
						
							|  |  |  | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-01-24 05:56:37 +03:00
										 |  |  | var BaseBrowserClassPrototype = { | 
					
						
							| 
									
										
										
										
											2019-01-24 05:32:21 +03:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-02-10 03:02:08 +03:00
										 |  |  | // XXX need a way to identify items...
 | 
					
						
							| 
									
										
										
										
											2019-01-24 05:56:37 +03:00
										 |  |  | var BaseBrowserPrototype = { | 
					
						
							|  |  |  | 	// XXX should we mix item/list options or separate them into sub-objects???
 | 
					
						
							| 
									
										
										
										
											2019-02-28 20:26:22 +03:00
										 |  |  | 	options: { | 
					
						
							|  |  |  | 		noDuplicateValues: false, | 
					
						
							|  |  |  | 	}, | 
					
						
							| 
									
										
										
										
											2019-01-24 05:32:21 +03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-03-06 15:25:58 +03:00
										 |  |  | 	// parent widget object...
 | 
					
						
							|  |  |  | 	//
 | 
					
						
							| 
									
										
										
										
											2019-03-06 15:38:37 +03:00
										 |  |  | 	// NOTE: this may or may not be a Browser object.
 | 
					
						
							| 
									
										
										
										
											2019-03-05 18:15:49 +03:00
										 |  |  | 	parent: null, | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-02-16 04:25:16 +03:00
										 |  |  | 	//
 | 
					
						
							| 
									
										
										
										
											2019-01-24 05:56:37 +03:00
										 |  |  | 	// Format:
 | 
					
						
							|  |  |  | 	// 	[
 | 
					
						
							|  |  |  | 	// 		<item> | <browser>,
 | 
					
						
							|  |  |  | 	// 		...
 | 
					
						
							|  |  |  | 	// 	]
 | 
					
						
							|  |  |  | 	//
 | 
					
						
							|  |  |  | 	// <item> format:
 | 
					
						
							|  |  |  | 	// 	{
 | 
					
						
							| 
									
										
										
										
											2019-01-25 20:19:33 +03:00
										 |  |  | 	// 		value: ...,
 | 
					
						
							| 
									
										
										
										
											2019-01-24 05:56:37 +03:00
										 |  |  | 	//
 | 
					
						
							|  |  |  | 	// 		...
 | 
					
						
							|  |  |  | 	// 	}
 | 
					
						
							|  |  |  | 	//
 | 
					
						
							| 
									
										
										
										
											2019-02-16 04:25:16 +03:00
										 |  |  | 	// NOTE: this can't be a map/dict as we need both order manipulation 
 | 
					
						
							|  |  |  | 	// 		and nested structures which would overcomplicate things, as 
 | 
					
						
							| 
									
										
										
										
											2019-05-16 05:00:15 +03:00
										 |  |  | 	// 		a compromise we use .index below for item identification.
 | 
					
						
							| 
									
										
										
										
											2019-01-26 21:32:14 +03:00
										 |  |  | 	__items: null, | 
					
						
							|  |  |  | 	get items(){ | 
					
						
							|  |  |  | 		this.__items | 
					
						
							|  |  |  | 			|| this.make() | 
					
						
							| 
									
										
										
										
											2019-02-05 22:12:33 +03:00
										 |  |  | 		return this.__items }, | 
					
						
							| 
									
										
										
										
											2019-01-26 21:32:14 +03:00
										 |  |  | 	set items(value){ | 
					
						
							|  |  |  | 		this.__items = value }, | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-02-16 04:25:16 +03:00
										 |  |  | 	//
 | 
					
						
							|  |  |  | 	// Format:
 | 
					
						
							|  |  |  | 	// 	{
 | 
					
						
							|  |  |  | 	// 		<key>: <item>,
 | 
					
						
							|  |  |  | 	// 		...
 | 
					
						
							|  |  |  | 	// 	}
 | 
					
						
							|  |  |  | 	//
 | 
					
						
							| 
									
										
										
										
											2019-03-06 15:25:58 +03:00
										 |  |  | 	// NOTE: this will get overwritten each tume .make(..) is called.
 | 
					
						
							|  |  |  | 	//
 | 
					
						
							| 
									
										
										
										
											2019-02-16 04:25:16 +03:00
										 |  |  | 	// XXX need to maintain this over item add/remove/change...
 | 
					
						
							|  |  |  | 	// XXX Q: should we be able to add/remove/change items outside of .__list__(..)???
 | 
					
						
							|  |  |  | 	// 		...only some item updates (how .collapsed is handled) make 
 | 
					
						
							| 
									
										
										
										
											2019-02-20 16:30:40 +03:00
										 |  |  | 	// 		sense at this time -- need to think about this more 
 | 
					
						
							| 
									
										
										
										
											2019-02-16 04:25:16 +03:00
										 |  |  | 	// 		carefully + strictly document the result...
 | 
					
						
							| 
									
										
										
										
											2019-03-06 15:25:58 +03:00
										 |  |  | 	// XXX can we make the format here simpler with less level 
 | 
					
						
							|  |  |  | 	// 		of indirection??
 | 
					
						
							|  |  |  | 	// 		...currently to go down a path we need to:
 | 
					
						
							| 
									
										
										
										
											2019-05-16 05:00:15 +03:00
										 |  |  | 	//			this.index.A.children.index.B.children...
 | 
					
						
							| 
									
										
										
										
											2019-03-06 15:25:58 +03:00
										 |  |  | 	//		would be nice to be closer to:
 | 
					
						
							|  |  |  | 	//			this.A.B...
 | 
					
						
							|  |  |  | 	__item_index: null, | 
					
						
							| 
									
										
										
										
											2019-05-16 05:00:15 +03:00
										 |  |  | 	get index(){ | 
					
						
							| 
									
										
										
										
											2019-03-06 15:25:58 +03:00
										 |  |  | 		this.__item_index | 
					
						
							|  |  |  | 			|| this.make() | 
					
						
							|  |  |  | 		return this.__item_index }, | 
					
						
							| 
									
										
										
										
											2019-05-16 05:00:15 +03:00
										 |  |  | 	// XXX should this exist???
 | 
					
						
							|  |  |  | 	set index(value){ | 
					
						
							| 
									
										
										
										
											2019-03-06 15:25:58 +03:00
										 |  |  | 		this.__item_index = value }, | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-02-16 04:25:16 +03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-05-13 18:58:17 +03:00
										 |  |  | 	// XXX should we cache the value here???? 
 | 
					
						
							| 
									
										
										
										
											2019-02-28 20:13:01 +03:00
										 |  |  | 	get focused(){ | 
					
						
							| 
									
										
										
										
											2019-05-13 18:58:17 +03:00
										 |  |  | 		return this.get('focused') }, | 
					
						
							| 
									
										
										
										
											2019-02-28 20:13:01 +03:00
										 |  |  | 	set focused(value){ | 
					
						
							| 
									
										
										
										
											2019-05-13 18:58:17 +03:00
										 |  |  | 		this.focus(value) }, | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// XXX should we cache the value here???? 
 | 
					
						
							| 
									
										
										
										
											2019-02-28 20:13:01 +03:00
										 |  |  | 	get selected(){ | 
					
						
							| 
									
										
										
										
											2019-05-13 18:58:17 +03:00
										 |  |  | 		return this.search('selected') }, | 
					
						
							| 
									
										
										
										
											2019-02-28 20:13:01 +03:00
										 |  |  | 	set selected(value){ | 
					
						
							| 
									
										
										
										
											2019-05-13 18:58:17 +03:00
										 |  |  | 		this.deselect('selected') | 
					
						
							|  |  |  | 		this.select(value) }, | 
					
						
							| 
									
										
										
										
											2019-02-28 20:13:01 +03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-01-24 05:32:21 +03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-03-17 21:54:26 +03:00
										 |  |  | 	// Length...
 | 
					
						
							|  |  |  | 	//
 | 
					
						
							|  |  |  | 	// visible only...
 | 
					
						
							| 
									
										
										
										
											2019-03-14 00:45:33 +03:00
										 |  |  | 	get length(){ | 
					
						
							| 
									
										
										
										
											2019-05-09 02:28:06 +03:00
										 |  |  | 		return this.map().length }, | 
					
						
							| 
									
										
										
										
											2019-05-10 21:07:17 +03:00
										 |  |  | 	// include collapsed elements...
 | 
					
						
							| 
									
										
										
										
											2019-03-17 21:54:26 +03:00
										 |  |  | 	get lengthTree(){ | 
					
						
							| 
									
										
										
										
											2019-05-09 02:28:06 +03:00
										 |  |  | 		return this.map({iterateCollapsed: true}).length }, | 
					
						
							| 
									
										
										
										
											2019-05-10 21:07:17 +03:00
										 |  |  | 	// include non-iterable elements...
 | 
					
						
							| 
									
										
										
										
											2019-03-17 21:54:26 +03:00
										 |  |  | 	get lengthAll(){ | 
					
						
							| 
									
										
										
										
											2019-05-09 02:28:06 +03:00
										 |  |  | 		return this.map({iterateAll: true}).length }, | 
					
						
							| 
									
										
										
										
											2019-03-14 00:45:33 +03:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-02-16 04:25:16 +03:00
										 |  |  | 	// Item list constructor...
 | 
					
						
							| 
									
										
										
										
											2019-01-24 05:32:21 +03:00
										 |  |  | 	//
 | 
					
						
							| 
									
										
										
										
											2019-01-26 21:32:14 +03:00
										 |  |  | 	// 	.__list__(make, options)
 | 
					
						
							| 
									
										
										
										
											2019-01-26 20:09:36 +03:00
										 |  |  | 	// 		-> undefined
 | 
					
						
							|  |  |  | 	// 		-> list
 | 
					
						
							| 
									
										
										
										
											2019-01-24 05:32:21 +03:00
										 |  |  | 	//
 | 
					
						
							| 
									
										
										
										
											2019-01-26 21:32:14 +03:00
										 |  |  | 	//
 | 
					
						
							| 
									
										
										
										
											2019-02-16 04:25:16 +03:00
										 |  |  | 	// 	Item constructor:
 | 
					
						
							|  |  |  | 	// 		make(value)
 | 
					
						
							|  |  |  | 	// 		make(value, options)
 | 
					
						
							|  |  |  | 	// 			-> make
 | 
					
						
							| 
									
										
										
										
											2019-01-26 21:32:14 +03:00
										 |  |  | 	//
 | 
					
						
							|  |  |  | 	//
 | 
					
						
							|  |  |  | 	// There are two modes of operation:
 | 
					
						
							|  |  |  | 	// 	1) call make(..) to create items
 | 
					
						
							|  |  |  | 	// 	2) return a list of items
 | 
					
						
							|  |  |  | 	//
 | 
					
						
							|  |  |  | 	//
 | 
					
						
							| 
									
										
										
										
											2019-02-16 04:25:16 +03:00
										 |  |  | 	// The if make(..) is called at least once the return value is 
 | 
					
						
							|  |  |  | 	// ignored (mode #1), otherwise, the returned list is used as the 
 | 
					
						
							|  |  |  | 	// .items structure.
 | 
					
						
							|  |  |  | 	//
 | 
					
						
							|  |  |  | 	//
 | 
					
						
							|  |  |  | 	// When calling make(..) (mode #1) the item is built by combining 
 | 
					
						
							|  |  |  | 	// the following in order:
 | 
					
						
							|  |  |  | 	// 	- original item (.items[key]) if present,
 | 
					
						
							|  |  |  | 	// 	- options passed to .make(<options>) method calling .__list__(..),
 | 
					
						
							|  |  |  | 	// 	- options passed to make(.., <options>) constructing the item,
 | 
					
						
							|  |  |  | 	// 	- {value: <value>} where <value> passed to make(<value>, ..)
 | 
					
						
							|  |  |  | 	//
 | 
					
						
							|  |  |  | 	// Each of the above will override values of the previous sections.
 | 
					
						
							|  |  |  | 	//
 | 
					
						
							|  |  |  | 	// The resulting item is stored in:
 | 
					
						
							|  |  |  | 	// 	.items
 | 
					
						
							| 
									
										
										
										
											2019-05-16 05:00:15 +03:00
										 |  |  | 	// 	.index (keyed via .id or JSONified .value)
 | 
					
						
							| 
									
										
										
										
											2019-02-16 04:25:16 +03:00
										 |  |  | 	//
 | 
					
						
							|  |  |  | 	// Each of the above structures is reset on each call to .make(..)
 | 
					
						
							| 
									
										
										
										
											2019-01-26 21:32:14 +03:00
										 |  |  | 	//
 | 
					
						
							| 
									
										
										
										
											2019-03-12 16:05:51 +03:00
										 |  |  | 	// options format:
 | 
					
						
							|  |  |  | 	// 	{
 | 
					
						
							|  |  |  | 	// 		id: <string>,
 | 
					
						
							|  |  |  | 	// 		value: <string> | <array>,
 | 
					
						
							|  |  |  | 	//
 | 
					
						
							| 
									
										
										
										
											2019-05-04 04:46:29 +03:00
										 |  |  | 	// 		children: <browser> | <array>,
 | 
					
						
							| 
									
										
										
										
											2019-03-12 16:05:51 +03:00
										 |  |  | 	//
 | 
					
						
							|  |  |  | 	// 		focused: <bool>,
 | 
					
						
							|  |  |  | 	// 		selected: <bool>,
 | 
					
						
							|  |  |  | 	// 		disabled: <bool>,
 | 
					
						
							|  |  |  | 	// 		noniterable: <bool>,
 | 
					
						
							|  |  |  | 	//
 | 
					
						
							|  |  |  | 	// 		// Set automatically...
 | 
					
						
							|  |  |  | 	// 		parent: <browser>,
 | 
					
						
							|  |  |  | 	// 		// XXX move this to the appropriate object...
 | 
					
						
							|  |  |  | 	// 		dom: <dom>,
 | 
					
						
							|  |  |  | 	// 	}
 | 
					
						
							|  |  |  | 	//
 | 
					
						
							|  |  |  | 	//
 | 
					
						
							| 
									
										
										
										
											2019-01-26 21:32:14 +03:00
										 |  |  | 	// Example:
 | 
					
						
							|  |  |  | 	// 	XXX
 | 
					
						
							|  |  |  | 	//
 | 
					
						
							|  |  |  | 	//
 | 
					
						
							| 
									
										
										
										
											2019-02-16 04:25:16 +03:00
										 |  |  | 	// In mode #2 XXX
 | 
					
						
							|  |  |  | 	//
 | 
					
						
							|  |  |  | 	//
 | 
					
						
							| 
									
										
										
										
											2019-01-26 21:32:14 +03:00
										 |  |  | 	// NOTE: this is not designed to be called directly...
 | 
					
						
							| 
									
										
										
										
											2019-01-26 20:09:36 +03:00
										 |  |  | 	__list__: function(make, options){ | 
					
						
							|  |  |  | 		throw new Error('.__list__(..): Not implemented.') }, | 
					
						
							| 
									
										
										
										
											2019-01-24 05:32:21 +03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-03-02 20:38:08 +03:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// XXX need a better key/path API...
 | 
					
						
							|  |  |  | 	//
 | 
					
						
							|  |  |  | 	__value2key__: function(key){ | 
					
						
							| 
									
										
										
										
											2019-03-03 20:33:48 +03:00
										 |  |  | 		//return JSON.stringify(key)
 | 
					
						
							|  |  |  | 		return key instanceof Array ? | 
					
						
							|  |  |  | 			key.join(' ') | 
					
						
							| 
									
										
										
										
											2019-03-06 15:25:58 +03:00
										 |  |  | 			: key }, | 
					
						
							| 
									
										
										
										
											2019-03-02 20:38:08 +03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-03-02 01:50:04 +03:00
										 |  |  | 	// Key getter/generator...
 | 
					
						
							|  |  |  | 	//
 | 
					
						
							|  |  |  | 	// XXX should these include the path???
 | 
					
						
							|  |  |  | 	// XXX is JSON the best key format???
 | 
					
						
							|  |  |  | 	__key__: function(item){ | 
					
						
							|  |  |  | 		return item.id  | 
					
						
							|  |  |  | 			// value is a browser -> generate an unique id...
 | 
					
						
							|  |  |  | 			// XXX identify via structure...
 | 
					
						
							|  |  |  | 			|| (item.value instanceof Browser  | 
					
						
							|  |  |  | 				&& this.__id__()) | 
					
						
							| 
									
										
										
										
											2019-03-02 20:38:08 +03:00
										 |  |  | 			|| this.__value2key__(item.value) }, | 
					
						
							| 
									
										
										
										
											2019-03-02 01:50:04 +03:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	// ID generator...
 | 
					
						
							|  |  |  | 	//
 | 
					
						
							|  |  |  | 	// Format:
 | 
					
						
							|  |  |  | 	// 	"<date>"
 | 
					
						
							|  |  |  | 	// 	"<prefix> <date>"
 | 
					
						
							|  |  |  | 	//
 | 
					
						
							|  |  |  | 	// XXX do a better id...
 | 
					
						
							| 
									
										
										
										
											2019-03-10 01:47:28 +03:00
										 |  |  | 	// 		Ex:
 | 
					
						
							|  |  |  | 	//			"abc"
 | 
					
						
							|  |  |  | 	// 			"abc (1)"
 | 
					
						
							|  |  |  | 	// 			"abc (2)"
 | 
					
						
							| 
									
										
										
										
											2019-03-02 01:50:04 +03:00
										 |  |  | 	// XXX not sure about the logic of this, should this take an item as 
 | 
					
						
							|  |  |  | 	// 		input and return an id???
 | 
					
						
							|  |  |  | 	// 		...should this check for uniqueness???
 | 
					
						
							| 
									
										
										
										
											2019-03-07 18:15:25 +03:00
										 |  |  | 	// 		think merging this with any of the actual ID generators would be best...
 | 
					
						
							| 
									
										
										
										
											2019-03-02 01:50:04 +03:00
										 |  |  | 	__id__: function(prefix){ | 
					
						
							|  |  |  | 		// id prefix...
 | 
					
						
							|  |  |  | 		return (prefix || '')  | 
					
						
							|  |  |  | 			// separator...
 | 
					
						
							|  |  |  | 			+ (prefix ? ' ' : '')  | 
					
						
							|  |  |  | 			// date...
 | 
					
						
							|  |  |  | 			+ Date.now() }, | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-01-24 06:12:04 +03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-04-25 17:40:28 +03:00
										 |  |  | 	// Walk the browser...
 | 
					
						
							| 
									
										
										
										
											2019-01-24 05:32:21 +03:00
										 |  |  | 	//
 | 
					
						
							| 
									
										
										
										
											2019-05-11 03:05:16 +03:00
										 |  |  | 	//	Get list of nodes in tree...
 | 
					
						
							|  |  |  | 	//	.walk()
 | 
					
						
							|  |  |  | 	//		-> list
 | 
					
						
							| 
									
										
										
										
											2019-02-16 04:25:16 +03:00
										 |  |  | 	//
 | 
					
						
							| 
									
										
										
										
											2019-05-11 03:05:16 +03:00
										 |  |  | 	//	Walk the tree passing each node to func(..)
 | 
					
						
							|  |  |  | 	//	.walk(func(..)[, options])
 | 
					
						
							|  |  |  | 	//		-> list
 | 
					
						
							| 
									
										
										
										
											2019-02-16 04:25:16 +03:00
										 |  |  | 	//
 | 
					
						
							| 
									
										
										
										
											2019-05-11 03:05:16 +03:00
										 |  |  | 	//	Walk tree passing each node to func(..) using method name to 
 | 
					
						
							|  |  |  | 	//	walk nested browsers...
 | 
					
						
							|  |  |  | 	//	NOTE: 'walk' is used as name if name is not present in the object...
 | 
					
						
							|  |  |  | 	//	.walk(func(..), name, args(..)[, options])
 | 
					
						
							|  |  |  | 	//	.walk(func(..), name, args(..), walkable(..)[, options])
 | 
					
						
							|  |  |  | 	//		-> list
 | 
					
						
							| 
									
										
										
										
											2019-04-25 17:40:28 +03:00
										 |  |  | 	//
 | 
					
						
							| 
									
										
										
										
											2019-05-11 03:05:16 +03:00
										 |  |  | 	//	Walk tree passign each node to func(..) and handle nested browser 
 | 
					
						
							|  |  |  | 	//	walking in recursion(..) optionally testing if walkable with walkable(..)
 | 
					
						
							|  |  |  | 	//	.walk(func(..), recursion(..)[, options])
 | 
					
						
							|  |  |  | 	//		-> list
 | 
					
						
							| 
									
										
										
										
											2019-02-16 04:25:16 +03:00
										 |  |  | 	//
 | 
					
						
							| 
									
										
										
										
											2019-01-26 04:48:36 +03:00
										 |  |  | 	//
 | 
					
						
							| 
									
										
										
										
											2019-05-11 05:24:36 +03:00
										 |  |  | 	//	Item handler...
 | 
					
						
							| 
									
										
										
										
											2019-05-11 03:05:16 +03:00
										 |  |  | 	//	func(node, index, path, next(..), stop(..), children)
 | 
					
						
							|  |  |  | 	//		-> list
 | 
					
						
							| 
									
										
										
										
											2019-02-05 22:12:33 +03:00
										 |  |  | 	//
 | 
					
						
							| 
									
										
										
										
											2019-05-11 05:24:36 +03:00
										 |  |  | 	//	Trigger next/nested item handling...
 | 
					
						
							| 
									
										
										
										
											2019-05-11 03:05:16 +03:00
										 |  |  | 	//	next(children)
 | 
					
						
							|  |  |  | 	//		-> list
 | 
					
						
							| 
									
										
										
										
											2019-02-05 22:12:33 +03:00
										 |  |  | 	//
 | 
					
						
							| 
									
										
										
										
											2019-05-11 05:24:36 +03:00
										 |  |  | 	//	Stop handling...
 | 
					
						
							| 
									
										
										
										
											2019-05-11 03:05:16 +03:00
										 |  |  | 	//	stop(result)
 | 
					
						
							| 
									
										
										
										
											2019-04-25 17:40:28 +03:00
										 |  |  | 	//
 | 
					
						
							|  |  |  | 	//
 | 
					
						
							| 
									
										
										
										
											2019-05-11 03:05:16 +03:00
										 |  |  | 	//	Handle walkable node children (recursively)...
 | 
					
						
							|  |  |  | 	//	recursion(children, index, path, options, context, func(..), stop(..), walk())
 | 
					
						
							|  |  |  | 	//		-> list
 | 
					
						
							| 
									
										
										
										
											2019-04-25 17:40:28 +03:00
										 |  |  | 	//
 | 
					
						
							|  |  |  | 	//
 | 
					
						
							| 
									
										
										
										
											2019-05-11 03:05:16 +03:00
										 |  |  | 	//	Prepare arguments for call of name function on nested browser...
 | 
					
						
							|  |  |  | 	//	args(list, index, path, options, context, func(..), stop(..))
 | 
					
						
							|  |  |  | 	//		-> list
 | 
					
						
							| 
									
										
										
										
											2019-04-25 17:40:28 +03:00
										 |  |  | 	//
 | 
					
						
							|  |  |  | 	//
 | 
					
						
							| 
									
										
										
										
											2019-05-11 03:05:16 +03:00
										 |  |  | 	//	Test if node is walkable...
 | 
					
						
							|  |  |  | 	//	walkable(node)
 | 
					
						
							| 
									
										
										
										
											2019-04-25 17:40:28 +03:00
										 |  |  | 	//		-> bool
 | 
					
						
							| 
									
										
										
										
											2019-01-24 05:32:21 +03:00
										 |  |  | 	//
 | 
					
						
							|  |  |  | 	//
 | 
					
						
							| 
									
										
										
										
											2019-05-11 03:05:16 +03:00
										 |  |  | 	// For examples see: .text(..), .paths(..) and .map(..)
 | 
					
						
							|  |  |  | 	//
 | 
					
						
							| 
									
										
										
										
											2019-01-26 04:38:11 +03:00
										 |  |  | 	//
 | 
					
						
							| 
									
										
										
										
											2019-04-24 16:10:26 +03:00
										 |  |  | 	// options format:
 | 
					
						
							| 
									
										
										
										
											2019-02-05 22:12:33 +03:00
										 |  |  | 	// 	{
 | 
					
						
							| 
									
										
										
										
											2019-04-26 02:28:58 +03:00
										 |  |  | 	// 		// Partial walking...
 | 
					
						
							|  |  |  | 	// 		//
 | 
					
						
							|  |  |  | 	// 		// XXX not implemented yet...
 | 
					
						
							|  |  |  | 	// 		start: <index> | <path>,
 | 
					
						
							|  |  |  | 	// 		count: <number>,
 | 
					
						
							|  |  |  | 	// 		end: <index> | <path>,
 | 
					
						
							|  |  |  | 	//
 | 
					
						
							|  |  |  | 	//
 | 
					
						
							| 
									
										
										
										
											2019-04-24 16:10:26 +03:00
										 |  |  | 	// 		// Iterate ALL items...
 | 
					
						
							|  |  |  | 	// 		//
 | 
					
						
							|  |  |  | 	// 		// NOTE: this if true overrides all other iteration coverage 
 | 
					
						
							|  |  |  | 	// 		//		options... 
 | 
					
						
							|  |  |  | 	// 		iterateAll: <bool>,
 | 
					
						
							| 
									
										
										
										
											2019-02-05 22:12:33 +03:00
										 |  |  | 	//
 | 
					
						
							| 
									
										
										
										
											2019-04-24 16:10:26 +03:00
										 |  |  | 	// 		// If true do not skip items with .noniterable set to true...
 | 
					
						
							|  |  |  | 	// 		iterateNonIterable: <bool>,
 | 
					
						
							| 
									
										
										
										
											2019-05-04 04:46:29 +03:00
										 |  |  | 	// 		// If true do not skip item.children of items with .collapsed 
 | 
					
						
							| 
									
										
										
										
											2019-04-24 16:10:26 +03:00
										 |  |  | 	// 		// set to true...
 | 
					
						
							|  |  |  | 	// 		iterateCollapsed: <bool>,
 | 
					
						
							|  |  |  | 	// 		// If true skip iterating nested items...
 | 
					
						
							|  |  |  | 	// 		skipNested: <bool>,
 | 
					
						
							| 
									
										
										
										
											2019-02-05 22:12:33 +03:00
										 |  |  | 	//
 | 
					
						
							| 
									
										
										
										
											2019-05-03 03:25:57 +03:00
										 |  |  | 	// 		// XXX not yet supported...
 | 
					
						
							|  |  |  | 	// 		skipInlined: <bool>,
 | 
					
						
							|  |  |  | 	//
 | 
					
						
							| 
									
										
										
										
											2019-04-25 17:40:28 +03:00
										 |  |  | 	// 		// Reverse iteration order...
 | 
					
						
							|  |  |  | 	//		//
 | 
					
						
							|  |  |  | 	//		// modes:
 | 
					
						
							|  |  |  | 	//		//	false | null		- normal order (default)
 | 
					
						
							| 
									
										
										
										
											2019-04-25 17:50:32 +03:00
										 |  |  | 	//		//	true | 'tree'		- reverse order of levels but keep 
 | 
					
						
							| 
									
										
										
										
											2019-04-25 17:40:28 +03:00
										 |  |  | 	//		//							topology order, i.e. containers
 | 
					
						
							|  |  |  | 	//		//							will precede contained elements.
 | 
					
						
							|  |  |  | 	//		//	'flat'				- full flat reverse
 | 
					
						
							|  |  |  | 	//		//
 | 
					
						
							|  |  |  | 	//		// NOTE: in 'flat' mode the client loses control over the 
 | 
					
						
							|  |  |  | 	//		//		order of processing via doNested(..) as it will be 
 | 
					
						
							|  |  |  | 	//		//		called before handleItem(..)
 | 
					
						
							| 
									
										
										
										
											2019-04-25 18:05:17 +03:00
										 |  |  | 	// 		reverse: <bool> | 'flat' | 'tree',
 | 
					
						
							|  |  |  | 	//
 | 
					
						
							|  |  |  | 	// 		// The value to be used if .reverse is set to true...
 | 
					
						
							|  |  |  | 	// 		defaultReverse: 'tree' (default) | 'flat',
 | 
					
						
							|  |  |  | 	//
 | 
					
						
							| 
									
										
										
										
											2019-03-25 21:32:30 +03:00
										 |  |  | 	//
 | 
					
						
							| 
									
										
										
										
											2019-04-24 16:10:26 +03:00
										 |  |  | 	// 		// If true include inlined parent id in path...
 | 
					
						
							|  |  |  | 	// 		// XXX not implemented yet -- can we implement this???...
 | 
					
						
							| 
									
										
										
										
											2019-04-25 03:25:03 +03:00
										 |  |  | 	// 		// XXX do we need this??
 | 
					
						
							| 
									
										
										
										
											2019-04-24 16:10:26 +03:00
										 |  |  | 	// 		inlinedPaths: <bool>,
 | 
					
						
							|  |  |  | 	// 	}
 | 
					
						
							|  |  |  | 	//
 | 
					
						
							|  |  |  | 	//
 | 
					
						
							| 
									
										
										
										
											2019-05-11 03:02:42 +03:00
										 |  |  | 	// NOTE: if recursion(..) is not given then .walk(..) is used to 
 | 
					
						
							| 
									
										
										
										
											2019-05-11 05:24:36 +03:00
										 |  |  | 	// 		handle all the nested elements (children)...
 | 
					
						
							| 
									
										
										
										
											2019-05-11 03:02:42 +03:00
										 |  |  | 	// NOTE: if walkable(..) is not given then we check for .walk(..)
 | 
					
						
							| 
									
										
										
										
											2019-05-04 17:56:02 +03:00
										 |  |  | 	// 		availability...
 | 
					
						
							|  |  |  | 	// NOTE: children arrays are handled internally...
 | 
					
						
							|  |  |  | 	//
 | 
					
						
							|  |  |  | 	//
 | 
					
						
							| 
									
										
										
										
											2019-05-06 03:42:19 +03:00
										 |  |  | 	// XXX which of the forms should be documented in the signature???
 | 
					
						
							|  |  |  | 	// 		NOTE: it does not matter which is used as we manually
 | 
					
						
							|  |  |  | 	// 		parse arguments...
 | 
					
						
							| 
									
										
										
										
											2019-05-07 18:17:03 +03:00
										 |  |  | 	// XXX passing both index directly and context containing index 
 | 
					
						
							|  |  |  | 	// 		(context.index) feels excessive...
 | 
					
						
							| 
									
										
										
										
											2019-05-07 19:19:23 +03:00
										 |  |  | 	// 			+ this is done so as to provide the user a simpler 
 | 
					
						
							|  |  |  | 	// 				.map(..)-like form...
 | 
					
						
							|  |  |  | 	// 				Ex:
 | 
					
						
							| 
									
										
										
										
											2019-05-11 03:02:42 +03:00
										 |  |  | 	// 					.walk((e, i, p, next, stop) => p.join('/'))
 | 
					
						
							| 
									
										
										
										
											2019-05-07 19:19:23 +03:00
										 |  |  | 	// 					// vs.
 | 
					
						
							| 
									
										
										
										
											2019-05-11 03:02:42 +03:00
										 |  |  | 	// 					.walk((e, c, next, stop) => c.path.join('/'))
 | 
					
						
							| 
									
										
										
										
											2019-05-07 19:19:23 +03:00
										 |  |  | 	// 			- two ways to get index and one to update it...
 | 
					
						
							| 
									
										
										
										
											2019-05-07 18:17:03 +03:00
										 |  |  | 	// 		...if this can produce errors we need to simplify...
 | 
					
						
							| 
									
										
										
										
											2019-05-07 19:09:05 +03:00
										 |  |  | 	// XXX add docs:
 | 
					
						
							|  |  |  | 	// 		- maintaining context to implement/extend walkers...
 | 
					
						
							|  |  |  | 	// 		- correctly stopping recursive calls (call root stop(..))
 | 
					
						
							| 
									
										
										
										
											2019-05-04 04:46:29 +03:00
										 |  |  | 	// XXX can this be simpler???
 | 
					
						
							| 
									
										
										
										
											2019-05-11 03:02:42 +03:00
										 |  |  | 	walk: function(func, recursion, walkable, options){ | 
					
						
							| 
									
										
										
										
											2019-05-04 04:46:29 +03:00
										 |  |  | 		var that = this | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		// parse args...
 | 
					
						
							|  |  |  | 		var args = [...arguments] | 
					
						
							| 
									
										
										
										
											2019-05-04 15:46:04 +03:00
										 |  |  | 		func = (args[0] instanceof Function  | 
					
						
							|  |  |  | 				|| args[0] == null) ?  | 
					
						
							| 
									
										
										
										
											2019-05-04 04:46:29 +03:00
										 |  |  | 			args.shift()  | 
					
						
							|  |  |  | 			: undefined | 
					
						
							|  |  |  | 		var recursion = (args[0] instanceof Function  | 
					
						
							|  |  |  | 				|| typeof(args[0]) == typeof('str') | 
					
						
							|  |  |  | 				|| args[0] == null) ?  | 
					
						
							|  |  |  | 			args.shift()  | 
					
						
							|  |  |  | 			: undefined | 
					
						
							| 
									
										
										
										
											2019-05-06 03:34:40 +03:00
										 |  |  | 		var formArgs = (typeof(recursion) == typeof('str') | 
					
						
							|  |  |  | 				&& args[0] instanceof Function) ? | 
					
						
							|  |  |  | 			args.shift() | 
					
						
							|  |  |  | 			: null | 
					
						
							|  |  |  | 		// sanity check...
 | 
					
						
							|  |  |  | 		if(formArgs == null && typeof(recursion) == typeof('str')){ | 
					
						
							| 
									
										
										
										
											2019-05-11 03:02:42 +03:00
										 |  |  | 			throw new Error(`.walk(func, name, formArgs, ..): ` | 
					
						
							| 
									
										
										
										
											2019-05-06 03:34:40 +03:00
										 |  |  | 				+`expected function as third argument, got: ${formArgs}.`) } | 
					
						
							| 
									
										
										
										
											2019-05-06 16:57:06 +03:00
										 |  |  | 		var walkable = (!formArgs  | 
					
						
							|  |  |  | 				&& (args[0] instanceof Function  | 
					
						
							|  |  |  | 					|| args[0] == null)) ? | 
					
						
							|  |  |  | 			args.shift()  | 
					
						
							|  |  |  | 			: null  | 
					
						
							|  |  |  | 		options = args.shift() || {}  | 
					
						
							| 
									
										
										
										
											2019-05-06 03:34:40 +03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-05-07 19:09:05 +03:00
										 |  |  | 		// get/build context...
 | 
					
						
							| 
									
										
										
										
											2019-05-06 16:57:06 +03:00
										 |  |  | 		var context = args.shift() | 
					
						
							|  |  |  | 		context = context instanceof Array ?  | 
					
						
							| 
									
										
										
										
											2019-05-07 19:09:05 +03:00
										 |  |  | 			{ path: context }  | 
					
						
							| 
									
										
										
										
											2019-05-06 16:57:06 +03:00
										 |  |  | 			: (context || {}) | 
					
						
							|  |  |  | 		context.root = context.root || this | 
					
						
							| 
									
										
										
										
											2019-05-06 19:21:22 +03:00
										 |  |  | 		context.index = context.index || 0 | 
					
						
							| 
									
										
										
										
											2019-05-04 04:46:29 +03:00
										 |  |  | 
 | 
					
						
							|  |  |  | 		// options specifics...
 | 
					
						
							|  |  |  | 		var iterateNonIterable = options.iterateAll || options.iterateNonIterable | 
					
						
							|  |  |  | 		var iterateCollapsed = options.iterateAll || options.iterateCollapsed | 
					
						
							|  |  |  | 		var skipNested = !options.iterateAll && options.skipNested | 
					
						
							| 
									
										
										
										
											2019-05-09 00:20:11 +03:00
										 |  |  | 		var skipInlined = !options.iterateAll && options.skipInlined | 
					
						
							| 
									
										
										
										
											2019-05-04 04:46:29 +03:00
										 |  |  | 		var reverse = options.reverse === true ? | 
					
						
							|  |  |  | 			(options.defaultReverse || 'tree') | 
					
						
							|  |  |  | 			: options.reverse | 
					
						
							| 
									
										
										
										
											2019-05-03 19:43:22 +03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-05-04 04:46:29 +03:00
										 |  |  | 		var isWalkable = walkable ? | 
					
						
							|  |  |  | 			function(node){ | 
					
						
							|  |  |  | 				return node instanceof Array || walkable(node) } | 
					
						
							|  |  |  | 			: function(node){ | 
					
						
							| 
									
										
										
										
											2019-05-06 03:34:40 +03:00
										 |  |  | 				return node  | 
					
						
							|  |  |  | 					&& (node instanceof Array  | 
					
						
							|  |  |  | 						// requested method name is available...
 | 
					
						
							|  |  |  | 						|| (typeof(recursion) == typeof('str')  | 
					
						
							|  |  |  | 							&& node[recursion]) | 
					
						
							| 
									
										
										
										
											2019-05-11 03:02:42 +03:00
										 |  |  | 						|| node.walk ) } | 
					
						
							| 
									
										
										
										
											2019-05-03 19:43:22 +03:00
										 |  |  | 
 | 
					
						
							|  |  |  | 		return walk( | 
					
						
							|  |  |  | 			function(state, node, next, stop){ | 
					
						
							| 
									
										
										
										
											2019-05-07 19:09:05 +03:00
										 |  |  | 				// keep only the root stop(..) -> stop the entire call tree...
 | 
					
						
							|  |  |  | 				stop = context.stop = context.stop || stop | 
					
						
							| 
									
										
										
										
											2019-05-06 19:21:22 +03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-05-04 04:46:29 +03:00
										 |  |  | 				// skip non-iterable items...
 | 
					
						
							|  |  |  | 				if(!iterateNonIterable && node.noniterable){ | 
					
						
							| 
									
										
										
										
											2019-05-07 19:09:05 +03:00
										 |  |  | 					return state } | 
					
						
							| 
									
										
										
										
											2019-05-03 19:43:22 +03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-05-04 04:46:29 +03:00
										 |  |  | 				var nested = false | 
					
						
							|  |  |  | 				var doNested = function(list){ | 
					
						
							| 
									
										
										
										
											2019-05-07 19:09:05 +03:00
										 |  |  | 					// this can be called only once -> return cached results...
 | 
					
						
							| 
									
										
										
										
											2019-05-04 04:46:29 +03:00
										 |  |  | 					if(nested !== false){ | 
					
						
							| 
									
										
										
										
											2019-05-07 19:09:05 +03:00
										 |  |  | 						return nested } | 
					
						
							| 
									
										
										
										
											2019-05-11 05:24:36 +03:00
										 |  |  | 					// calling this on a node without .children is a no-op...
 | 
					
						
							| 
									
										
										
										
											2019-05-11 03:02:42 +03:00
										 |  |  | 					if(children == null){ | 
					
						
							|  |  |  | 						return [] } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-05-04 04:46:29 +03:00
										 |  |  | 					// normalize...
 | 
					
						
							| 
									
										
										
										
											2019-05-09 02:28:06 +03:00
										 |  |  | 					list = list === true ? | 
					
						
							| 
									
										
										
										
											2019-05-04 04:46:29 +03:00
										 |  |  | 							children	 | 
					
						
							|  |  |  | 						: (!iterateCollapsed && node.collapsed) ? | 
					
						
							|  |  |  | 							[] | 
					
						
							| 
									
										
										
										
											2019-05-09 02:28:06 +03:00
										 |  |  | 						: list == null ? | 
					
						
							| 
									
										
										
										
											2019-05-10 21:07:17 +03:00
										 |  |  | 							children | 
					
						
							| 
									
										
										
										
											2019-05-04 04:46:29 +03:00
										 |  |  | 						: list | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-05-11 03:02:42 +03:00
										 |  |  | 					// call .walk(..) recursively...
 | 
					
						
							| 
									
										
										
										
											2019-05-06 03:34:40 +03:00
										 |  |  | 					var useWalk = function(){ | 
					
						
							| 
									
										
										
										
											2019-05-11 03:02:42 +03:00
										 |  |  | 						return list.walk( | 
					
						
							| 
									
										
										
										
											2019-05-06 16:57:06 +03:00
										 |  |  | 							func,  | 
					
						
							|  |  |  | 							recursion,  | 
					
						
							|  |  |  | 							...(formArgs instanceof Function ?  | 
					
						
							|  |  |  | 								[formArgs]  | 
					
						
							| 
									
										
										
										
											2019-05-11 14:54:48 +03:00
										 |  |  | 								: [walkable]),  | 
					
						
							| 
									
										
										
										
											2019-05-07 19:09:05 +03:00
										 |  |  | 							options, context) } | 
					
						
							| 
									
										
										
										
											2019-05-06 03:34:40 +03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-05-04 04:46:29 +03:00
										 |  |  | 					return (list === false ? | 
					
						
							|  |  |  | 								[]	 | 
					
						
							| 
									
										
										
										
											2019-05-07 19:09:05 +03:00
										 |  |  | 							// handle arrays internally...
 | 
					
						
							| 
									
										
										
										
											2019-05-04 04:46:29 +03:00
										 |  |  | 							: list instanceof Array ? | 
					
						
							| 
									
										
										
										
											2019-05-04 15:46:04 +03:00
										 |  |  | 								// NOTE: this gets the path and i from context...
 | 
					
						
							| 
									
										
										
										
											2019-05-11 03:02:42 +03:00
										 |  |  | 								next('do', [],  | 
					
						
							| 
									
										
										
										
											2019-05-07 18:17:03 +03:00
										 |  |  | 									...(reverse ?  | 
					
						
							|  |  |  | 										list.slice().reverse()  | 
					
						
							|  |  |  | 										: list)) | 
					
						
							| 
									
										
										
										
											2019-05-04 04:46:29 +03:00
										 |  |  | 							// user-defined recursion...
 | 
					
						
							|  |  |  | 							: recursion instanceof Function ? | 
					
						
							| 
									
										
										
										
											2019-05-07 18:17:03 +03:00
										 |  |  | 								recursion.call(that,  | 
					
						
							|  |  |  | 									list, context.index, p,  | 
					
						
							|  |  |  | 									options, context,  | 
					
						
							|  |  |  | 									func, useWalk) | 
					
						
							| 
									
										
										
										
											2019-05-06 03:34:40 +03:00
										 |  |  | 							// method with arg forming...
 | 
					
						
							|  |  |  | 							: formArgs instanceof Function  | 
					
						
							|  |  |  | 									&& list[recursion] ? | 
					
						
							| 
									
										
										
										
											2019-05-06 19:21:22 +03:00
										 |  |  | 								list[recursion]( | 
					
						
							| 
									
										
										
										
											2019-05-07 18:17:03 +03:00
										 |  |  | 									...(formArgs( | 
					
						
							|  |  |  | 										list, context.index, p,  | 
					
						
							|  |  |  | 										options, context,  | 
					
						
							|  |  |  | 										func, useWalk) || [])) | 
					
						
							| 
									
										
										
										
											2019-05-11 03:02:42 +03:00
										 |  |  | 							// .walk(..)
 | 
					
						
							| 
									
										
										
										
											2019-05-06 03:34:40 +03:00
										 |  |  | 							: useWalk()) | 
					
						
							| 
									
										
										
										
											2019-05-07 19:09:05 +03:00
										 |  |  | 						// normalize and merge to state...
 | 
					
						
							| 
									
										
										
										
											2019-05-04 04:46:29 +03:00
										 |  |  | 						.run(function(){ | 
					
						
							| 
									
										
										
										
											2019-05-11 03:02:42 +03:00
										 |  |  | 							return (nested = this instanceof Array ? | 
					
						
							| 
									
										
										
										
											2019-05-04 04:46:29 +03:00
										 |  |  | 								this | 
					
						
							| 
									
										
										
										
											2019-05-11 03:02:42 +03:00
										 |  |  | 								: [this]) }) } | 
					
						
							| 
									
										
										
										
											2019-05-04 04:46:29 +03:00
										 |  |  | 
 | 
					
						
							|  |  |  | 				// prepare context...
 | 
					
						
							|  |  |  | 				var id = node.id || node.value | 
					
						
							| 
									
										
										
										
											2019-05-07 19:09:05 +03:00
										 |  |  | 				var path = context.path = context.path || [] | 
					
						
							| 
									
										
										
										
											2019-05-04 04:46:29 +03:00
										 |  |  | 				var [inline, p, children] =  | 
					
						
							|  |  |  | 					// inline...
 | 
					
						
							|  |  |  | 					isWalkable(node) ? | 
					
						
							|  |  |  | 						[true, path, node] | 
					
						
							| 
									
										
										
										
											2019-05-03 19:43:22 +03:00
										 |  |  | 					// nested...
 | 
					
						
							| 
									
										
										
										
											2019-05-04 04:46:29 +03:00
										 |  |  | 					: (!skipNested && isWalkable(node.children)) ? | 
					
						
							|  |  |  | 						[false,  | 
					
						
							| 
									
										
										
										
											2019-05-07 19:09:05 +03:00
										 |  |  | 							// update context for nested items...
 | 
					
						
							| 
									
										
										
										
											2019-05-07 18:17:03 +03:00
										 |  |  | 							path.push(id)  | 
					
						
							|  |  |  | 								&& path,  | 
					
						
							| 
									
										
										
										
											2019-05-04 04:46:29 +03:00
										 |  |  | 							node.children] | 
					
						
							|  |  |  | 					// leaf...
 | 
					
						
							|  |  |  | 					: [false, path.concat([id]), undefined] | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-05-09 00:20:11 +03:00
										 |  |  | 				if(inline && skipInlined){ | 
					
						
							| 
									
										
										
										
											2019-05-11 05:24:36 +03:00
										 |  |  | 					return state } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 				// go through the elements...
 | 
					
						
							| 
									
										
										
										
											2019-05-04 04:46:29 +03:00
										 |  |  | 				state.splice(state.length, 0, | 
					
						
							| 
									
										
										
										
											2019-05-11 05:24:36 +03:00
										 |  |  | 					...[ | 
					
						
							|  |  |  | 						// reverse -> do children...
 | 
					
						
							|  |  |  | 						reverse == 'flat'  | 
					
						
							|  |  |  | 							&& children | 
					
						
							|  |  |  | 							&& doNested()  | 
					
						
							|  |  |  | 							|| [], | 
					
						
							|  |  |  | 						// do element...
 | 
					
						
							|  |  |  | 						func ?  | 
					
						
							|  |  |  | 							(func.call(that,  | 
					
						
							|  |  |  | 								...(inline ?  | 
					
						
							|  |  |  | 									[null, context.index]  | 
					
						
							|  |  |  | 									: [node, context.index++]), | 
					
						
							|  |  |  | 								p,  | 
					
						
							|  |  |  | 								// NOTE: when calling this it is the 
 | 
					
						
							|  |  |  | 								// 		responsibility of the caller to return
 | 
					
						
							|  |  |  | 								// 		the result to be added to state...
 | 
					
						
							|  |  |  | 								doNested,  | 
					
						
							|  |  |  | 								stop, | 
					
						
							|  |  |  | 								children) || [])  | 
					
						
							|  |  |  | 							: [node], | 
					
						
							|  |  |  | 						// normal order -> do children...
 | 
					
						
							|  |  |  | 						children | 
					
						
							|  |  |  | 							&& nested === false | 
					
						
							|  |  |  | 							&& doNested()  | 
					
						
							|  |  |  | 							|| [], | 
					
						
							|  |  |  | 				   	].flat()) | 
					
						
							| 
									
										
										
										
											2019-05-11 03:02:42 +03:00
										 |  |  | 
 | 
					
						
							|  |  |  | 				// restore path context...
 | 
					
						
							|  |  |  | 				children | 
					
						
							|  |  |  | 					&& context.path.pop() | 
					
						
							| 
									
										
										
										
											2019-05-04 04:46:29 +03:00
										 |  |  | 
 | 
					
						
							|  |  |  | 				return state | 
					
						
							| 
									
										
										
										
											2019-05-03 19:43:22 +03:00
										 |  |  | 			},  | 
					
						
							|  |  |  | 			[],  | 
					
						
							| 
									
										
										
										
											2019-05-07 19:09:05 +03:00
										 |  |  | 			// input items...
 | 
					
						
							| 
									
										
										
										
											2019-05-04 04:46:29 +03:00
										 |  |  | 			...(reverse ?  | 
					
						
							| 
									
										
										
										
											2019-05-07 19:09:05 +03:00
										 |  |  | 				this.items | 
					
						
							|  |  |  | 					.slice() | 
					
						
							|  |  |  | 					.reverse()  | 
					
						
							|  |  |  | 				: this.items)) }, | 
					
						
							| 
									
										
										
										
											2019-05-03 19:43:22 +03:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-05-11 14:54:48 +03:00
										 |  |  | 	// Test/Example Text renders...
 | 
					
						
							| 
									
										
										
										
											2019-04-24 16:10:26 +03:00
										 |  |  | 	//
 | 
					
						
							| 
									
										
										
										
											2019-05-11 14:54:48 +03:00
										 |  |  | 	//	Recursively render the browser as text tree...
 | 
					
						
							|  |  |  | 	//	._test_texttree(..)
 | 
					
						
							|  |  |  | 	//		-> string
 | 
					
						
							| 
									
										
										
										
											2019-04-24 16:10:26 +03:00
										 |  |  | 	//
 | 
					
						
							| 
									
										
										
										
											2019-05-11 14:54:48 +03:00
										 |  |  | 	//	Recursively render the browser as text tree with manual nesting...
 | 
					
						
							|  |  |  | 	//	._test_texttree_manual(..)
 | 
					
						
							|  |  |  | 	//		-> string
 | 
					
						
							|  |  |  | 	//
 | 
					
						
							|  |  |  | 	//	Build a nested object tree from the browser...
 | 
					
						
							|  |  |  | 	//	._test_tree(..)
 | 
					
						
							|  |  |  | 	//		-> object
 | 
					
						
							|  |  |  | 	//
 | 
					
						
							|  |  |  | 	_test_texttree: function(options, context){ | 
					
						
							|  |  |  | 		// NOTE: here we do not care about the topology (other than path 
 | 
					
						
							|  |  |  | 		// 		depth) and just handle items...
 | 
					
						
							| 
									
										
										
										
											2019-05-04 17:56:02 +03:00
										 |  |  | 		return this | 
					
						
							| 
									
										
										
										
											2019-05-11 03:02:42 +03:00
										 |  |  | 			.walk( | 
					
						
							| 
									
										
										
										
											2019-05-04 17:56:02 +03:00
										 |  |  | 				function(node, i, path){ | 
					
						
							|  |  |  | 					return node ?  | 
					
						
							| 
									
										
										
										
											2019-05-06 16:57:06 +03:00
										 |  |  | 						path.slice(1) | 
					
						
							|  |  |  | 							.map(e => '  ') | 
					
						
							|  |  |  | 							.join('') + (node.value || node) | 
					
						
							| 
									
										
										
										
											2019-04-25 17:40:28 +03:00
										 |  |  | 						: [] }, | 
					
						
							| 
									
										
										
										
											2019-05-11 14:54:48 +03:00
										 |  |  | 				'_test_texttree', | 
					
						
							| 
									
										
										
										
											2019-05-06 16:57:06 +03:00
										 |  |  | 				function(func, i, path, options, context){ | 
					
						
							|  |  |  | 					return [options, context] }, | 
					
						
							| 
									
										
										
										
											2019-05-07 19:19:23 +03:00
										 |  |  | 				options, context) | 
					
						
							| 
									
										
										
										
											2019-05-06 16:57:06 +03:00
										 |  |  | 			.join('\n') }, | 
					
						
							| 
									
										
										
										
											2019-05-11 14:54:48 +03:00
										 |  |  | 	_test_texttree_manual: function(options, context){ | 
					
						
							|  |  |  | 		// NOTE: here we do basic topology -- append children to their 
 | 
					
						
							|  |  |  | 		// 		respective node...
 | 
					
						
							| 
									
										
										
										
											2019-05-11 03:02:42 +03:00
										 |  |  | 		return this | 
					
						
							|  |  |  | 			.walk( | 
					
						
							|  |  |  | 				function(node, i, path, next){ | 
					
						
							|  |  |  | 					return node == null ?  | 
					
						
							|  |  |  | 							[] | 
					
						
							| 
									
										
										
										
											2019-05-11 14:54:48 +03:00
										 |  |  | 						// make a node...
 | 
					
						
							| 
									
										
										
										
											2019-05-11 03:02:42 +03:00
										 |  |  | 						: [path.slice(1) | 
					
						
							|  |  |  | 							.map(e => '  ') | 
					
						
							|  |  |  | 							.join('') + (node.value || node)] | 
					
						
							| 
									
										
										
										
											2019-05-11 14:54:48 +03:00
										 |  |  | 							// append child nodes if present...
 | 
					
						
							| 
									
										
										
										
											2019-05-11 03:02:42 +03:00
										 |  |  | 			   				.concat(node.children ? | 
					
						
							|  |  |  | 								next() | 
					
						
							|  |  |  | 								: []) }, | 
					
						
							| 
									
										
										
										
											2019-05-11 14:54:48 +03:00
										 |  |  | 				'_test_texttree_manual', | 
					
						
							| 
									
										
										
										
											2019-05-11 03:02:42 +03:00
										 |  |  | 				function(func, i, path, options, context){ | 
					
						
							|  |  |  | 					return [options, context] }, | 
					
						
							|  |  |  | 				options, context) | 
					
						
							|  |  |  | 			.join('\n') }, | 
					
						
							| 
									
										
										
										
											2019-05-11 14:54:48 +03:00
										 |  |  | 	_test_tree: function(options, context){ | 
					
						
							|  |  |  | 		var toObject = function(res, e){ | 
					
						
							|  |  |  | 			if(e == null || e[0] == null){ | 
					
						
							|  |  |  | 				return res | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			res[e[0]] = e[1] instanceof Array ?  | 
					
						
							|  |  |  | 				// handle nested arrays...
 | 
					
						
							|  |  |  | 				// NOTE: these did not get through the .reduce(..) below
 | 
					
						
							|  |  |  | 				// 		as they are simple arrays that do not implement
 | 
					
						
							|  |  |  | 				// 		either .walk(..) or ._test_tree(..)
 | 
					
						
							|  |  |  | 				e.slice(1).reduce(toObject, {})  | 
					
						
							|  |  |  | 				: e[1] | 
					
						
							|  |  |  | 			return res | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		return this | 
					
						
							|  |  |  | 			// build [key, children] pairs...
 | 
					
						
							|  |  |  | 			.walk( | 
					
						
							|  |  |  | 				function(node, i, path, next){ | 
					
						
							|  |  |  | 					return node == null ?  | 
					
						
							|  |  |  | 							[] | 
					
						
							|  |  |  | 						// make a node...
 | 
					
						
							|  |  |  | 						: [[(node.value || node)] | 
					
						
							|  |  |  | 							// append child nodes if present...
 | 
					
						
							|  |  |  | 			   				.concat(node.children ? | 
					
						
							|  |  |  | 								next() | 
					
						
							|  |  |  | 								: null) ] }, | 
					
						
							|  |  |  | 				'_test_tree', | 
					
						
							|  |  |  | 				function(func, i, path, options, context){ | 
					
						
							|  |  |  | 					return [options, context] }, | 
					
						
							|  |  |  | 				options, context) | 
					
						
							|  |  |  | 			// construct the object...
 | 
					
						
							|  |  |  |    			.reduce(toObject, {}) }, | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-05-11 05:24:36 +03:00
										 |  |  | 	paths: function(options, context){ | 
					
						
							|  |  |  | 		return this.walk( | 
					
						
							|  |  |  | 			function(n, i, p){ | 
					
						
							|  |  |  | 				return n  | 
					
						
							|  |  |  | 					&& [(options || {}).joinPaths !== false ?  | 
					
						
							|  |  |  | 						p.join('/')  | 
					
						
							|  |  |  | 						: p] },  | 
					
						
							|  |  |  | 			'paths', | 
					
						
							|  |  |  | 			function(_, i, path, options, context){ | 
					
						
							|  |  |  | 				// NOTE: for paths and indexes to be consistent between
 | 
					
						
							|  |  |  | 				// 		levels we need to thread the context on, here and
 | 
					
						
							|  |  |  | 				// 		into the base .walk(..) call below...
 | 
					
						
							|  |  |  | 				return [options, context] }, | 
					
						
							|  |  |  | 			options, context) }, | 
					
						
							| 
									
										
										
										
											2019-04-24 16:10:26 +03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-05-11 14:54:48 +03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-04-24 16:10:26 +03:00
										 |  |  | 	// Extended map...
 | 
					
						
							| 
									
										
										
										
											2019-03-17 23:30:27 +03:00
										 |  |  | 	//
 | 
					
						
							| 
									
										
										
										
											2019-04-24 16:10:26 +03:00
										 |  |  | 	//	Get all items...
 | 
					
						
							|  |  |  | 	//	.map([options])
 | 
					
						
							|  |  |  | 	//		-> items
 | 
					
						
							| 
									
										
										
										
											2019-03-17 23:30:27 +03:00
										 |  |  | 	//
 | 
					
						
							| 
									
										
										
										
											2019-04-24 16:10:26 +03:00
										 |  |  | 	//	Map func to items...
 | 
					
						
							|  |  |  | 	//	.map(func[, options])
 | 
					
						
							|  |  |  | 	//		-> items
 | 
					
						
							| 
									
										
										
										
											2019-03-20 16:54:10 +03:00
										 |  |  | 	//
 | 
					
						
							|  |  |  | 	//
 | 
					
						
							|  |  |  | 	//
 | 
					
						
							| 
									
										
										
										
											2019-04-24 16:10:26 +03:00
										 |  |  | 	//	func(item, index, path, browser)
 | 
					
						
							|  |  |  | 	//		-> result
 | 
					
						
							| 
									
										
										
										
											2019-03-17 23:30:27 +03:00
										 |  |  | 	//
 | 
					
						
							| 
									
										
										
										
											2019-04-24 16:10:26 +03:00
										 |  |  | 	//
 | 
					
						
							|  |  |  | 	//
 | 
					
						
							| 
									
										
										
										
											2019-04-25 17:50:32 +03:00
										 |  |  | 	// options format:
 | 
					
						
							|  |  |  | 	// 	{
 | 
					
						
							| 
									
										
										
										
											2019-04-25 18:05:17 +03:00
										 |  |  | 	// 		// The value used if .reverse is set to true...
 | 
					
						
							|  |  |  | 	// 		//
 | 
					
						
							|  |  |  | 	// 		// NOTE: the default is different from .walk(..)
 | 
					
						
							|  |  |  | 	// 		defaultReverse: 'flat' (default) | 'tree',
 | 
					
						
							| 
									
										
										
										
											2019-04-25 17:50:32 +03:00
										 |  |  | 	//
 | 
					
						
							|  |  |  | 	// 		// For other supported options see docs for .walk(..)
 | 
					
						
							|  |  |  | 	// 		...
 | 
					
						
							|  |  |  | 	// 	}
 | 
					
						
							|  |  |  | 	//
 | 
					
						
							| 
									
										
										
										
											2019-04-24 16:10:26 +03:00
										 |  |  | 	//
 | 
					
						
							|  |  |  | 	// 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
 | 
					
						
							|  |  |  | 	//
 | 
					
						
							|  |  |  | 	//
 | 
					
						
							| 
									
										
										
										
											2019-04-27 04:15:03 +03:00
										 |  |  | 	// XXX should we move the defaults to .config???
 | 
					
						
							| 
									
										
										
										
											2019-04-24 16:10:26 +03:00
										 |  |  | 	// XXX Q: should we have an option to treat groups as elements???
 | 
					
						
							|  |  |  | 	map: function(func, options){ | 
					
						
							|  |  |  | 		var that = this | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		// parse args...
 | 
					
						
							|  |  |  | 		var args = [...arguments] | 
					
						
							| 
									
										
										
										
											2019-04-28 02:38:54 +03:00
										 |  |  | 		func = (args[0] instanceof Function  | 
					
						
							|  |  |  | 				|| args[0] === undefined) ?  | 
					
						
							| 
									
										
										
										
											2019-04-24 16:10:26 +03:00
										 |  |  | 			args.shift()  | 
					
						
							|  |  |  | 			: undefined | 
					
						
							| 
									
										
										
										
											2019-05-06 16:57:06 +03:00
										 |  |  | 		options = args.shift() || {} | 
					
						
							| 
									
										
										
										
											2019-04-25 18:05:17 +03:00
										 |  |  | 		options = !options.defaultReverse ? | 
					
						
							| 
									
										
										
										
											2019-04-25 17:50:32 +03:00
										 |  |  | 			Object.assign({}, | 
					
						
							|  |  |  | 				options,  | 
					
						
							| 
									
										
										
										
											2019-04-25 18:05:17 +03:00
										 |  |  | 				{ defaultReverse: 'flat' }) | 
					
						
							| 
									
										
										
										
											2019-04-25 17:50:32 +03:00
										 |  |  | 			: options | 
					
						
							| 
									
										
										
										
											2019-05-06 16:57:06 +03:00
										 |  |  | 		var context = args.shift() | 
					
						
							| 
									
										
										
										
											2019-04-25 17:50:32 +03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-05-11 03:02:42 +03:00
										 |  |  | 		return this.walk( | 
					
						
							| 
									
										
										
										
											2019-05-06 16:57:06 +03:00
										 |  |  | 			function(elem, i, path){ | 
					
						
							| 
									
										
										
										
											2019-04-24 16:10:26 +03:00
										 |  |  | 				return elem != null ? | 
					
						
							| 
									
										
										
										
											2019-05-06 16:57:06 +03:00
										 |  |  | 					[func === undefined ? | 
					
						
							|  |  |  | 						elem | 
					
						
							|  |  |  | 						// XXX should this pass the current or the root 
 | 
					
						
							|  |  |  | 						// 		container to func???
 | 
					
						
							|  |  |  | 						: func.call(that, elem, i, path, that)] | 
					
						
							|  |  |  | 					: [] },  | 
					
						
							|  |  |  | 			'map', | 
					
						
							|  |  |  | 			function(_, i, p, options, context){ | 
					
						
							|  |  |  | 				return [func, options, context] }, | 
					
						
							| 
									
										
										
										
											2019-05-07 19:19:23 +03:00
										 |  |  | 			options, context) }, | 
					
						
							| 
									
										
										
										
											2019-04-24 16:10:26 +03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-03-20 16:54:10 +03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-04-27 20:59:56 +03:00
										 |  |  | 	//
 | 
					
						
							| 
									
										
										
										
											2019-05-02 00:29:09 +03:00
										 |  |  | 	// 	Get list of matching elements...
 | 
					
						
							|  |  |  | 	// 	NOTE: this is similar to .filter(..)
 | 
					
						
							|  |  |  | 	// 	.search(test[, options])
 | 
					
						
							|  |  |  | 	// 		-> items
 | 
					
						
							|  |  |  | 	//
 | 
					
						
							|  |  |  | 	// 	Map func to list of matching elements and return results...
 | 
					
						
							|  |  |  | 	// 	NOTE: this is similar to .filter(..).map(func)
 | 
					
						
							|  |  |  | 	// 	.search(test, func[, options])
 | 
					
						
							|  |  |  | 	// 		-> items
 | 
					
						
							|  |  |  | 	//
 | 
					
						
							|  |  |  | 	//
 | 
					
						
							|  |  |  | 	// test can be:
 | 
					
						
							|  |  |  | 	// 	predicate(..)	- function returning true or false
 | 
					
						
							|  |  |  | 	// 	index			- element index
 | 
					
						
							|  |  |  | 	// 						NOTE: index can be positive or negative to 
 | 
					
						
							|  |  |  | 	// 							access items from the end.
 | 
					
						
							|  |  |  | 	// 	path			- array of path elements or '*' (matches any element)
 | 
					
						
							|  |  |  | 	// 	regexp			- regexp object to test item path
 | 
					
						
							|  |  |  | 	// 	query			- object to test against the element 
 | 
					
						
							| 
									
										
										
										
											2019-05-02 04:25:58 +03:00
										 |  |  | 	// 	keyword			- 
 | 
					
						
							| 
									
										
										
										
											2019-05-02 00:29:09 +03:00
										 |  |  | 	//
 | 
					
						
							|  |  |  | 	//
 | 
					
						
							|  |  |  | 	// 	predicate(elem, i, path)
 | 
					
						
							|  |  |  | 	// 		-> bool
 | 
					
						
							|  |  |  | 	//
 | 
					
						
							|  |  |  | 	//
 | 
					
						
							|  |  |  | 	// query format:
 | 
					
						
							|  |  |  | 	// 	{
 | 
					
						
							| 
									
										
										
										
											2019-05-02 04:25:58 +03:00
										 |  |  | 	// 		// match if <attr-name> exists and is true...
 | 
					
						
							|  |  |  | 	// 		// XXX revise...
 | 
					
						
							| 
									
										
										
										
											2019-05-02 00:29:09 +03:00
										 |  |  | 	// 		<attr-name>: true,
 | 
					
						
							|  |  |  | 	//
 | 
					
						
							| 
									
										
										
										
											2019-05-02 04:25:58 +03:00
										 |  |  | 	// 		// match if <attr-name> does not exist or is false...
 | 
					
						
							|  |  |  | 	// 		// XXX revise...
 | 
					
						
							| 
									
										
										
										
											2019-05-02 00:29:09 +03:00
										 |  |  | 	// 		<attr-name>: false,
 | 
					
						
							|  |  |  | 	//
 | 
					
						
							|  |  |  | 	// 		// match if <attr-name> equals value...
 | 
					
						
							|  |  |  | 	// 		<attr-name>: <value>,
 | 
					
						
							|  |  |  | 	//
 | 
					
						
							|  |  |  | 	// 		// match if func(<attr-value>) return true...
 | 
					
						
							|  |  |  | 	// 		<attr-name>: <func>,
 | 
					
						
							|  |  |  | 	//
 | 
					
						
							|  |  |  | 	// 		...
 | 
					
						
							|  |  |  | 	// 	}
 | 
					
						
							| 
									
										
										
										
											2019-04-27 20:59:56 +03:00
										 |  |  | 	//
 | 
					
						
							|  |  |  | 	//
 | 
					
						
							| 
									
										
										
										
											2019-05-02 04:25:58 +03:00
										 |  |  | 	// supported keywords:
 | 
					
						
							|  |  |  | 	// 	'first'		- get first item (same as 0)
 | 
					
						
							|  |  |  | 	// 	'last'		- get last item (same as -1)
 | 
					
						
							| 
									
										
										
										
											2019-05-02 18:18:13 +03:00
										 |  |  | 	// 	'selected'	- get selected items (shorthand to {selected: true})
 | 
					
						
							|  |  |  | 	// 	'focused'	- get focused items (shorthand to {focused: true})
 | 
					
						
							| 
									
										
										
										
											2019-05-02 04:25:58 +03:00
										 |  |  | 	//
 | 
					
						
							|  |  |  | 	//
 | 
					
						
							| 
									
										
										
										
											2019-05-09 00:20:11 +03:00
										 |  |  | 	// options format:
 | 
					
						
							|  |  |  | 	// 	{
 | 
					
						
							|  |  |  | 	// 		noIdentityCheck: <bool>,
 | 
					
						
							|  |  |  | 	//
 | 
					
						
							|  |  |  | 	// 		noQueryCheck: <bool>,
 | 
					
						
							|  |  |  | 	//
 | 
					
						
							|  |  |  | 	// 		...
 | 
					
						
							|  |  |  | 	// 	}
 | 
					
						
							|  |  |  | 	//
 | 
					
						
							| 
									
										
										
										
											2019-05-07 19:56:46 +03:00
										 |  |  | 	//
 | 
					
						
							|  |  |  | 	//
 | 
					
						
							|  |  |  | 	// __search_test_generators__ format:
 | 
					
						
							|  |  |  | 	// 	{
 | 
					
						
							|  |  |  | 	// 		// NOTE: generator order is significant as patterns are testen 
 | 
					
						
							|  |  |  | 	// 		//		in order the generators are defined...
 | 
					
						
							| 
									
										
										
										
											2019-05-08 04:14:29 +03:00
										 |  |  | 	// 		// NOTE: testGenerator(..) is called in the context of 
 | 
					
						
							|  |  |  | 	// 		//		__search_test_generators__ (XXX ???)
 | 
					
						
							| 
									
										
										
										
											2019-05-07 19:56:46 +03:00
										 |  |  | 	// 		// NOTE: <key> is only used for documentation...
 | 
					
						
							|  |  |  | 	// 		<key>: testGenerator(..),
 | 
					
						
							|  |  |  | 	//
 | 
					
						
							|  |  |  | 	// 		...
 | 
					
						
							|  |  |  | 	// 	}
 | 
					
						
							|  |  |  | 	//
 | 
					
						
							|  |  |  | 	//	testGenerator(pattern)
 | 
					
						
							|  |  |  | 	//		-> test(elem, i, path)
 | 
					
						
							|  |  |  | 	//		-> false
 | 
					
						
							|  |  |  | 	//
 | 
					
						
							|  |  |  | 	//
 | 
					
						
							| 
									
										
										
										
											2019-05-02 04:25:58 +03:00
										 |  |  | 	// XXX add support for 'next'/'prev', ... keywords... (here or in .get(..)???)
 | 
					
						
							| 
									
										
										
										
											2019-05-18 03:28:53 +03:00
										 |  |  | 	// XXX add support for fuzzy match search -- match substring by default 
 | 
					
						
							|  |  |  | 	// 		and exact title if using quotes...
 | 
					
						
							| 
									
										
										
										
											2019-04-30 17:24:04 +03:00
										 |  |  | 	// XXX do we actually need to stop this as soon as we find something, 
 | 
					
						
							|  |  |  | 	// 		i.e. options.firstOnly???
 | 
					
						
							| 
									
										
										
										
											2019-05-07 19:56:46 +03:00
										 |  |  | 	// XXX add diff support...
 | 
					
						
							|  |  |  | 	__search_test_generators__: { | 
					
						
							|  |  |  | 		// regexp path test...
 | 
					
						
							|  |  |  | 		regexp: function(pattern){ | 
					
						
							|  |  |  | 			return pattern instanceof RegExp | 
					
						
							|  |  |  | 				&& function(elem, i, path){ | 
					
						
							|  |  |  | 					return pattern.test(elem.value) | 
					
						
							|  |  |  | 						|| pattern.test('/'+ path.join('/')) } }, | 
					
						
							| 
									
										
										
										
											2019-05-18 03:23:43 +03:00
										 |  |  | 		// string path test...
 | 
					
						
							|  |  |  | 		// XXX should 'B' be equivalent to '/B' or should it be more like '**/B'?
 | 
					
						
							|  |  |  | 		strPath: function(pattern){ | 
					
						
							|  |  |  | 			if(typeof(pattern) == typeof('str')){ | 
					
						
							| 
									
										
										
										
											2019-05-18 03:15:03 +03:00
										 |  |  | 				pattern = pattern instanceof Array ? | 
					
						
							|  |  |  | 					pattern | 
					
						
							|  |  |  | 					: pattern | 
					
						
							|  |  |  | 						.split(/[\\\/]/g) | 
					
						
							|  |  |  | 						.filter(function(e){ return e.trim().length > 0 }) | 
					
						
							| 
									
										
										
										
											2019-05-18 03:23:43 +03:00
										 |  |  | 				return this.path(pattern) | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			return false | 
					
						
							|  |  |  | 		}, | 
					
						
							|  |  |  | 		// path test...
 | 
					
						
							|  |  |  | 		// NOTE: this does not go down branches that do not match the path...
 | 
					
						
							|  |  |  | 		path: function(pattern){ | 
					
						
							|  |  |  | 			if(pattern instanceof Array){ | 
					
						
							| 
									
										
										
										
											2019-05-11 00:14:10 +03:00
										 |  |  | 				// XXX add support for '**' ???
 | 
					
						
							|  |  |  | 				var cmp = function(a, b){ | 
					
						
							|  |  |  | 					return a.length == b.length | 
					
						
							|  |  |  | 						&& !a | 
					
						
							|  |  |  | 							.reduce(function(res, e, i){ | 
					
						
							|  |  |  | 								return res || !( | 
					
						
							|  |  |  | 									e == '*'  | 
					
						
							|  |  |  | 										|| (e instanceof RegExp  | 
					
						
							|  |  |  | 											&& e.test(b[i])) | 
					
						
							|  |  |  | 										|| e == b[i]) }, false) } | 
					
						
							|  |  |  | 				var onPath = function(path){ | 
					
						
							|  |  |  | 					return pattern.length >= path.length  | 
					
						
							|  |  |  | 						&& cmp( | 
					
						
							|  |  |  | 							pattern.slice(0, path.length),  | 
					
						
							|  |  |  | 							path) } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 				return function(elem, i, path, next){ | 
					
						
							| 
									
										
										
										
											2019-05-10 21:07:17 +03:00
										 |  |  | 					// do not go down branches beyond pattern length or 
 | 
					
						
							|  |  |  | 					// ones that are not on path...
 | 
					
						
							|  |  |  | 					;(pattern.length == path.length | 
					
						
							|  |  |  | 							|| !onPath(path)) | 
					
						
							|  |  |  | 						&& next(false) | 
					
						
							|  |  |  | 					// do the test...
 | 
					
						
							| 
									
										
										
										
											2019-05-07 19:56:46 +03:00
										 |  |  | 					return path.length > 0 | 
					
						
							|  |  |  | 						&& pattern.length == path.length | 
					
						
							| 
									
										
										
										
											2019-05-11 00:14:10 +03:00
										 |  |  | 						&& cmp(pattern, path) }  | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			return false | 
					
						
							|  |  |  | 		}, | 
					
						
							| 
									
										
										
										
											2019-05-07 19:56:46 +03:00
										 |  |  | 		// item index test...
 | 
					
						
							|  |  |  | 		index: function(pattern){ | 
					
						
							|  |  |  | 			return typeof(pattern) == typeof(123) | 
					
						
							|  |  |  | 				&& function(elem, i, path){ | 
					
						
							|  |  |  | 					return i == pattern } }, | 
					
						
							|  |  |  | 		// XXX add diff support...
 | 
					
						
							|  |  |  | 		// object query..
 | 
					
						
							|  |  |  | 		// NOTE: this must be last as it will return a test unconditionally...
 | 
					
						
							|  |  |  | 		query: function(pattern){  | 
					
						
							| 
									
										
										
										
											2019-05-15 21:45:20 +03:00
										 |  |  | 			var that = this | 
					
						
							| 
									
										
										
										
											2019-05-07 19:56:46 +03:00
										 |  |  | 			return function(elem){ | 
					
						
							|  |  |  | 				return Object.entries(pattern) | 
					
						
							|  |  |  | 					.reduce(function(res, [key, pattern]){ | 
					
						
							|  |  |  | 						return res  | 
					
						
							|  |  |  | 							&& (elem[key] == pattern | 
					
						
							|  |  |  | 								// bool...
 | 
					
						
							|  |  |  | 								|| ((pattern === true || pattern === false) | 
					
						
							|  |  |  | 									&& pattern === !!elem[key]) | 
					
						
							|  |  |  | 								// predicate...
 | 
					
						
							|  |  |  | 								|| (pattern instanceof Function  | 
					
						
							|  |  |  | 									&& pattern.call(that, elem[key])) | 
					
						
							|  |  |  | 								// regexp...
 | 
					
						
							|  |  |  | 								|| (pattern instanceof RegExp | 
					
						
							|  |  |  | 									&& pattern.test(elem[key])) | 
					
						
							|  |  |  | 								// type...
 | 
					
						
							|  |  |  | 								// XXX problem, we can't distinguish this 
 | 
					
						
							|  |  |  | 								// 		and a predicate...
 | 
					
						
							|  |  |  | 								// 		...so for now use:
 | 
					
						
							|  |  |  | 								// 			.search(v => v instanceof Array)
 | 
					
						
							|  |  |  | 								//|| (typeof(pattern) == typeof({})
 | 
					
						
							|  |  |  | 								//	&& pattern instanceof Function
 | 
					
						
							|  |  |  | 								//	&& elem[key] instanceof pattern)
 | 
					
						
							|  |  |  | 							) }, true) } }, | 
					
						
							|  |  |  | 	}, | 
					
						
							| 
									
										
										
										
											2019-05-02 00:29:09 +03:00
										 |  |  | 	search: function(pattern, func, options){ | 
					
						
							| 
									
										
										
										
											2019-04-27 04:15:03 +03:00
										 |  |  | 		var that = this | 
					
						
							| 
									
										
										
										
											2019-05-02 00:29:09 +03:00
										 |  |  | 
 | 
					
						
							|  |  |  | 		// parse args...
 | 
					
						
							| 
									
										
										
										
											2019-04-27 04:15:03 +03:00
										 |  |  | 		var args = [...arguments] | 
					
						
							| 
									
										
										
										
											2019-05-11 00:14:10 +03:00
										 |  |  | 		pattern = args.length == 0 ?  | 
					
						
							| 
									
										
										
										
											2019-05-06 16:57:06 +03:00
										 |  |  | 			true  | 
					
						
							| 
									
										
										
										
											2019-05-11 00:14:10 +03:00
										 |  |  | 			: args.shift()  | 
					
						
							| 
									
										
										
										
											2019-05-02 00:29:09 +03:00
										 |  |  | 		func = (args[0] instanceof Function  | 
					
						
							|  |  |  | 				|| args[0] === undefined) ?  | 
					
						
							|  |  |  | 			args.shift()  | 
					
						
							|  |  |  | 			: undefined | 
					
						
							| 
									
										
										
										
											2019-05-06 16:57:06 +03:00
										 |  |  | 		options = args.shift() || {} | 
					
						
							| 
									
										
										
										
											2019-05-06 19:21:22 +03:00
										 |  |  | 		var context = args.shift() | 
					
						
							| 
									
										
										
										
											2019-05-06 16:57:06 +03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-05-08 04:14:29 +03:00
										 |  |  | 		// pattern -- normalize and do pattern keywords...
 | 
					
						
							| 
									
										
										
										
											2019-05-06 16:57:06 +03:00
										 |  |  | 		pattern = options.ignoreKeywords ? | 
					
						
							|  |  |  | 				pattern | 
					
						
							| 
									
										
										
										
											2019-05-11 00:14:10 +03:00
										 |  |  | 			: typeof(pattern) == typeof('str') ? | 
					
						
							|  |  |  | 				((pattern === 'all' || pattern == '*') ? | 
					
						
							|  |  |  | 					true | 
					
						
							|  |  |  | 				: pattern == 'first' ? | 
					
						
							|  |  |  | 					0 | 
					
						
							|  |  |  | 				: pattern == 'last' ? | 
					
						
							|  |  |  | 					-1 | 
					
						
							|  |  |  | 				: pattern == 'selected' ? | 
					
						
							|  |  |  | 					{selected: true} | 
					
						
							|  |  |  | 				: pattern == 'focused' ? | 
					
						
							|  |  |  | 					{focused: true} | 
					
						
							|  |  |  | 				: pattern) | 
					
						
							| 
									
										
										
										
											2019-04-30 17:24:04 +03:00
										 |  |  | 			: pattern | 
					
						
							|  |  |  | 		// normalize negative index...
 | 
					
						
							|  |  |  | 		if(typeof(pattern) == typeof(123) && pattern < 0){ | 
					
						
							|  |  |  | 			pattern = -pattern - 1 | 
					
						
							|  |  |  | 			options.reverse = 'flat' | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		// normalize/build the test predicate...
 | 
					
						
							| 
									
										
										
										
											2019-05-02 00:29:09 +03:00
										 |  |  | 		var test = ( | 
					
						
							| 
									
										
										
										
											2019-05-06 16:57:06 +03:00
										 |  |  | 			// all...
 | 
					
						
							| 
									
										
										
										
											2019-05-02 04:25:58 +03:00
										 |  |  | 			pattern === true ? | 
					
						
							|  |  |  | 				pattern | 
					
						
							| 
									
										
										
										
											2019-04-27 17:02:36 +03:00
										 |  |  | 			// predicate...
 | 
					
						
							| 
									
										
										
										
											2019-05-02 04:25:58 +03:00
										 |  |  | 			: pattern instanceof Function ? | 
					
						
							| 
									
										
										
										
											2019-04-27 17:02:36 +03:00
										 |  |  | 				pattern | 
					
						
							| 
									
										
										
										
											2019-05-07 19:56:46 +03:00
										 |  |  | 			// other -> get a compatible test function...
 | 
					
						
							| 
									
										
										
										
											2019-05-09 00:20:11 +03:00
										 |  |  | 			: Object.entries(this.__search_test_generators__) | 
					
						
							| 
									
										
										
										
											2019-05-11 00:14:10 +03:00
										 |  |  | 				.filter(function([key, _]){ | 
					
						
							| 
									
										
										
										
											2019-05-09 00:20:11 +03:00
										 |  |  | 					return !(options.noQueryCheck  | 
					
						
							|  |  |  | 						&& key == 'query') }) | 
					
						
							|  |  |  | 				.reduce(function(res, [_, get]){ | 
					
						
							| 
									
										
										
										
											2019-05-07 19:56:46 +03:00
										 |  |  | 					return res  | 
					
						
							| 
									
										
										
										
											2019-05-18 03:23:43 +03:00
										 |  |  | 						|| get.call(that.__search_test_generators__, pattern) }, false) ) | 
					
						
							| 
									
										
										
										
											2019-04-27 17:02:36 +03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-05-11 03:02:42 +03:00
										 |  |  | 		return this.walk( | 
					
						
							| 
									
										
										
										
											2019-05-10 21:07:17 +03:00
										 |  |  | 			function(elem, i, path, next, stop){ | 
					
						
							| 
									
										
										
										
											2019-04-30 17:24:04 +03:00
										 |  |  | 				// match...
 | 
					
						
							| 
									
										
										
										
											2019-05-06 19:21:22 +03:00
										 |  |  | 				var res = (elem | 
					
						
							| 
									
										
										
										
											2019-05-06 16:57:06 +03:00
										 |  |  | 						&& (test === true  | 
					
						
							| 
									
										
										
										
											2019-05-09 00:20:11 +03:00
										 |  |  | 							// identity check...
 | 
					
						
							|  |  |  | 							|| (!options.noIdentityCheck  | 
					
						
							|  |  |  | 								&& pattern === elem) | 
					
						
							|  |  |  | 							// test...
 | 
					
						
							|  |  |  | 							|| (test  | 
					
						
							| 
									
										
										
										
											2019-05-10 21:07:17 +03:00
										 |  |  | 								// NOTE: we pass next here to provide the 
 | 
					
						
							|  |  |  | 								// 		test with the option to filter out
 | 
					
						
							|  |  |  | 								// 		branches that it knows will not 
 | 
					
						
							|  |  |  | 								// 		match...
 | 
					
						
							|  |  |  | 								&& test.call(this, elem, i, path, next)))) ? | 
					
						
							| 
									
										
										
										
											2019-05-09 00:20:11 +03:00
										 |  |  | 					// handle the passed items...
 | 
					
						
							| 
									
										
										
										
											2019-05-06 16:57:06 +03:00
										 |  |  | 					[ func ? | 
					
						
							| 
									
										
										
										
											2019-05-08 04:14:29 +03:00
										 |  |  | 						func.call(this, elem, i, path, stop) | 
					
						
							| 
									
										
										
										
											2019-05-06 16:57:06 +03:00
										 |  |  | 						: elem ] | 
					
						
							|  |  |  | 					: []  | 
					
						
							|  |  |  | 				return ((options.firstMatch  | 
					
						
							|  |  |  | 							|| typeof(pattern) == typeof(123))  | 
					
						
							|  |  |  | 						&& res.length > 0) ?  | 
					
						
							| 
									
										
										
										
											2019-05-06 19:21:22 +03:00
										 |  |  | 					stop(res) | 
					
						
							| 
									
										
										
										
											2019-05-06 16:57:06 +03:00
										 |  |  | 					: res }, | 
					
						
							|  |  |  | 			'search', | 
					
						
							|  |  |  | 			function(_, i, p, options, context){ | 
					
						
							|  |  |  | 				return [pattern, func, options, context] }, | 
					
						
							| 
									
										
										
										
											2019-05-07 19:19:23 +03:00
										 |  |  | 			options, context) }, | 
					
						
							| 
									
										
										
										
											2019-04-27 04:15:03 +03:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-05-02 04:25:58 +03:00
										 |  |  | 	// XXX EXPERIMENTAL...
 | 
					
						
							|  |  |  | 	//
 | 
					
						
							| 
									
										
										
										
											2019-05-03 03:25:57 +03:00
										 |  |  | 	// 	Get focused item...
 | 
					
						
							| 
									
										
										
										
											2019-05-02 04:25:58 +03:00
										 |  |  | 	// 	.get()
 | 
					
						
							| 
									
										
										
										
											2019-05-08 04:14:29 +03:00
										 |  |  | 	// 	.get('focused'[, func])
 | 
					
						
							| 
									
										
										
										
											2019-05-02 04:25:58 +03:00
										 |  |  | 	// 		-> item
 | 
					
						
							|  |  |  | 	// 		-> undefined
 | 
					
						
							|  |  |  | 	//
 | 
					
						
							| 
									
										
										
										
											2019-05-03 03:25:57 +03:00
										 |  |  | 	// 	Get next/prev item relative to focused...
 | 
					
						
							| 
									
										
										
										
											2019-05-08 04:14:29 +03:00
										 |  |  | 	// 	.get('prev'[, offset][, func][, options])
 | 
					
						
							|  |  |  | 	// 	.get('next'[, offset][, func][, options])
 | 
					
						
							| 
									
										
										
										
											2019-05-02 18:18:13 +03:00
										 |  |  | 	// 		-> item
 | 
					
						
							|  |  |  | 	// 		-> undefined
 | 
					
						
							|  |  |  | 	//
 | 
					
						
							| 
									
										
										
										
											2019-05-03 03:25:57 +03:00
										 |  |  | 	// 	Get first item matching pattern...
 | 
					
						
							| 
									
										
										
										
											2019-05-08 04:14:29 +03:00
										 |  |  | 	// 	.get(pattern[, func][, options])
 | 
					
						
							| 
									
										
										
										
											2019-05-03 03:25:57 +03:00
										 |  |  | 	// 		-> item
 | 
					
						
							|  |  |  | 	// 		-> undefined
 | 
					
						
							| 
									
										
										
										
											2019-05-02 18:18:13 +03:00
										 |  |  | 	//
 | 
					
						
							| 
									
										
										
										
											2019-05-03 03:25:57 +03:00
										 |  |  | 	// pattern mostly follows the same scheme as in .select(..) so see 
 | 
					
						
							|  |  |  | 	// docs for that for more info.
 | 
					
						
							| 
									
										
										
										
											2019-05-02 18:18:13 +03:00
										 |  |  | 	//
 | 
					
						
							| 
									
										
										
										
											2019-05-02 04:25:58 +03:00
										 |  |  | 	//
 | 
					
						
							|  |  |  | 	// NOTE: this is just like a lazy .search(..) that will return the 
 | 
					
						
							|  |  |  | 	// 		first result only.
 | 
					
						
							|  |  |  | 	//
 | 
					
						
							| 
									
										
										
										
											2019-05-02 18:18:13 +03:00
										 |  |  | 	// XXX should we be able to get offset values relative to any match?
 | 
					
						
							| 
									
										
										
										
											2019-05-08 04:14:29 +03:00
										 |  |  | 	// XXX should we use .wald2(..) here???
 | 
					
						
							| 
									
										
										
										
											2019-05-02 04:25:58 +03:00
										 |  |  | 	// XXX revise return value...
 | 
					
						
							|  |  |  | 	get: function(pattern, options){ | 
					
						
							| 
									
										
										
										
											2019-05-02 18:18:13 +03:00
										 |  |  | 		var args = [...arguments] | 
					
						
							|  |  |  | 		pattern = args.shift() | 
					
						
							| 
									
										
										
										
											2019-05-02 04:25:58 +03:00
										 |  |  | 		pattern = pattern === undefined ?  | 
					
						
							| 
									
										
										
										
											2019-05-02 18:18:13 +03:00
										 |  |  | 			'focused'  | 
					
						
							| 
									
										
										
										
											2019-05-02 04:25:58 +03:00
										 |  |  | 			: pattern | 
					
						
							| 
									
										
										
										
											2019-05-02 18:18:13 +03:00
										 |  |  | 		var offset = (pattern == 'next' || pattern == 'prev') | 
					
						
							|  |  |  | 				&& typeof(args[0]) == typeof(123) ? | 
					
						
							|  |  |  | 			args.shift() | 
					
						
							|  |  |  | 			: 1 | 
					
						
							| 
									
										
										
										
											2019-05-08 04:14:29 +03:00
										 |  |  | 		var func = args[0] instanceof Function ? | 
					
						
							|  |  |  | 			args.shift()  | 
					
						
							|  |  |  | 			// XXX return format...
 | 
					
						
							|  |  |  | 			: function(e, i, p){ return e } | 
					
						
							| 
									
										
										
										
											2019-05-02 18:18:13 +03:00
										 |  |  | 		options = args.pop() || {} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		// sanity checks...
 | 
					
						
							|  |  |  | 		if(offset <= 0){ | 
					
						
							| 
									
										
										
										
											2019-05-08 04:14:29 +03:00
										 |  |  | 			throw new Error(`.get(..): offset must be a positive number, got: ${offset}.`) } | 
					
						
							| 
									
										
										
										
											2019-05-02 04:25:58 +03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-05-08 04:14:29 +03:00
										 |  |  | 		// NOTE: we do not care about return values here as we'll return 
 | 
					
						
							|  |  |  | 		// 		via stop(..)...
 | 
					
						
							| 
									
										
										
										
											2019-05-02 18:18:13 +03:00
										 |  |  | 		var res = [] | 
					
						
							| 
									
										
										
										
											2019-05-08 04:14:29 +03:00
										 |  |  | 		return [ | 
					
						
							| 
									
										
										
										
											2019-05-02 18:18:13 +03:00
										 |  |  | 			// next + offset...
 | 
					
						
							|  |  |  | 			pattern == 'next' ? | 
					
						
							| 
									
										
										
										
											2019-05-02 04:25:58 +03:00
										 |  |  | 				this.search(true,  | 
					
						
							| 
									
										
										
										
											2019-05-08 04:14:29 +03:00
										 |  |  | 					function(elem, i, path, stop){ | 
					
						
							| 
									
										
										
										
											2019-05-02 18:18:13 +03:00
										 |  |  | 						if(elem.focused == true){ | 
					
						
							|  |  |  | 							res = offset + 1 | 
					
						
							| 
									
										
										
										
											2019-05-02 04:25:58 +03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-05-02 18:18:13 +03:00
										 |  |  | 						// get the offset item...
 | 
					
						
							|  |  |  | 						} else if(res <= 0){ | 
					
						
							| 
									
										
										
										
											2019-05-08 04:14:29 +03:00
										 |  |  | 							stop([func(elem, i, path)]) | 
					
						
							| 
									
										
										
										
											2019-05-02 04:25:58 +03:00
										 |  |  | 						} | 
					
						
							| 
									
										
										
										
											2019-05-02 18:18:13 +03:00
										 |  |  | 						// countdown to offset...
 | 
					
						
							|  |  |  | 						res = typeof(res) == typeof(123) ?  | 
					
						
							|  |  |  | 							res - 1  | 
					
						
							| 
									
										
										
										
											2019-05-08 04:14:29 +03:00
										 |  |  | 							: res }, | 
					
						
							|  |  |  | 					options) | 
					
						
							| 
									
										
										
										
											2019-05-02 18:18:13 +03:00
										 |  |  | 			// prev + offset...
 | 
					
						
							|  |  |  | 			: pattern == 'prev' ? | 
					
						
							| 
									
										
										
										
											2019-05-02 04:25:58 +03:00
										 |  |  | 				this.search(true,  | 
					
						
							| 
									
										
										
										
											2019-05-08 04:14:29 +03:00
										 |  |  | 					function(elem, i, path, stop){ | 
					
						
							|  |  |  | 						elem.focused == true | 
					
						
							|  |  |  | 							&& stop([func(res.length >= offset ?  | 
					
						
							| 
									
										
										
										
											2019-05-02 18:18:13 +03:00
										 |  |  | 								res[0]  | 
					
						
							| 
									
										
										
										
											2019-05-08 04:14:29 +03:00
										 |  |  | 								: undefined)]) | 
					
						
							| 
									
										
										
										
											2019-05-02 18:18:13 +03:00
										 |  |  | 						// buffer the previous offset items...
 | 
					
						
							| 
									
										
										
										
											2019-05-08 04:14:29 +03:00
										 |  |  | 						res.push((elem, i, path)) | 
					
						
							| 
									
										
										
										
											2019-05-02 18:18:13 +03:00
										 |  |  | 						res.length > offset | 
					
						
							| 
									
										
										
										
											2019-05-08 04:14:29 +03:00
										 |  |  | 							&& res.shift() }, | 
					
						
							|  |  |  | 					options) | 
					
						
							| 
									
										
										
										
											2019-05-02 18:18:13 +03:00
										 |  |  | 			// base case -> get first match...
 | 
					
						
							|  |  |  | 			: this.search(pattern,  | 
					
						
							| 
									
										
										
										
											2019-05-08 04:14:29 +03:00
										 |  |  | 				function(elem, i, path, stop){ | 
					
						
							|  |  |  | 					stop([func(elem, i, path)]) },  | 
					
						
							|  |  |  | 				options) ].flat()[0] }, | 
					
						
							| 
									
										
										
										
											2019-05-02 04:25:58 +03:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-04-25 03:25:03 +03:00
										 |  |  | 	// XXX BROKEN...
 | 
					
						
							| 
									
										
										
										
											2019-04-24 16:10:26 +03:00
										 |  |  | 	// Sublist map functions...
 | 
					
						
							| 
									
										
										
										
											2019-04-25 03:25:03 +03:00
										 |  |  | 	//
 | 
					
						
							| 
									
										
										
										
											2019-05-09 00:20:11 +03:00
										 |  |  | 	// XXX should these return a sparse array... ???
 | 
					
						
							| 
									
										
										
										
											2019-05-10 21:07:17 +03:00
										 |  |  | 	// XXX this does not include inlined sections, should it???
 | 
					
						
							| 
									
										
										
										
											2019-04-24 16:10:26 +03:00
										 |  |  | 	sublists: function(func, options){ | 
					
						
							| 
									
										
										
										
											2019-05-04 04:46:29 +03:00
										 |  |  | 		return this.search({children: true}, func, options) }, | 
					
						
							| 
									
										
										
										
											2019-03-17 23:30:27 +03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-04-24 16:10:26 +03:00
										 |  |  | 	next: function(){}, | 
					
						
							|  |  |  | 	prev: function(){}, | 
					
						
							| 
									
										
										
										
											2019-03-20 16:54:10 +03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-04-24 16:10:26 +03:00
										 |  |  | 	// 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 }, | 
					
						
							| 
									
										
										
										
											2019-05-09 00:20:11 +03:00
										 |  |  | 	filter: function(func, options, context){ | 
					
						
							| 
									
										
										
										
											2019-05-11 03:02:42 +03:00
										 |  |  | 		return this.walk( | 
					
						
							| 
									
										
										
										
											2019-05-09 00:20:11 +03:00
										 |  |  | 			function(e, i, p){ | 
					
						
							|  |  |  | 				return e && func.call(this, e, i, p) ? [e] : [] }, | 
					
						
							|  |  |  | 			'filter', | 
					
						
							|  |  |  | 			function(_, i, p, options, context){ | 
					
						
							|  |  |  | 				return [func, options, context] }, | 
					
						
							|  |  |  | 			options, context) }, | 
					
						
							| 
									
										
										
										
											2019-05-10 21:07:17 +03:00
										 |  |  | 	reduce: function(func, start, options){ | 
					
						
							|  |  |  | 		var that = this | 
					
						
							|  |  |  | 		var context = arguments[3] || {result: start} | 
					
						
							| 
									
										
										
										
											2019-05-11 03:02:42 +03:00
										 |  |  | 		this.walk( | 
					
						
							| 
									
										
										
										
											2019-05-10 21:07:17 +03:00
										 |  |  | 			function(e, i, p){ | 
					
						
							|  |  |  | 				context.result = e ?  | 
					
						
							|  |  |  | 					func.call(that, context.result, e, i, p)  | 
					
						
							|  |  |  | 					: context.result | 
					
						
							|  |  |  | 				return context.result  | 
					
						
							|  |  |  | 			}, | 
					
						
							|  |  |  | 			'reduce', | 
					
						
							|  |  |  | 			function(_, i, p, options, context){ | 
					
						
							|  |  |  | 				return [func, context.result, options, context] | 
					
						
							|  |  |  | 			}, | 
					
						
							|  |  |  | 			options, context) | 
					
						
							|  |  |  | 		return context.result | 
					
						
							|  |  |  | 	}, | 
					
						
							| 
									
										
										
										
											2019-03-17 21:54:26 +03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-05-09 00:20:11 +03:00
										 |  |  | 	positionOf: function(item, options){ | 
					
						
							|  |  |  | 		return this.search(item,  | 
					
						
							|  |  |  | 			function(_, i, p){  | 
					
						
							|  |  |  | 				return [i, p] },  | 
					
						
							|  |  |  | 			Object.assign( | 
					
						
							|  |  |  | 				{ | 
					
						
							|  |  |  | 					firstMatch: true,  | 
					
						
							|  |  |  | 					noQueryCheck: true, | 
					
						
							|  |  |  | 				}, | 
					
						
							|  |  |  | 				options || {})).concat([[-1, undefined]]).shift() }, | 
					
						
							| 
									
										
										
										
											2019-04-24 16:15:41 +03:00
										 |  |  | 	indexOf: function(item, options){ | 
					
						
							| 
									
										
										
										
											2019-05-09 00:20:11 +03:00
										 |  |  | 		return this.positionOf(item, options)[0] }, | 
					
						
							| 
									
										
										
										
											2019-04-24 16:15:41 +03:00
										 |  |  | 	pathOf: function(item, options){ | 
					
						
							| 
									
										
										
										
											2019-05-09 00:20:11 +03:00
										 |  |  | 		return this.positionOf(item, options)[1] }, | 
					
						
							| 
									
										
										
										
											2019-04-24 16:15:41 +03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-05-11 03:02:42 +03:00
										 |  |  | 	// Like .select(.., {iterateCollapsed: true}) but will expand all the 
 | 
					
						
							| 
									
										
										
										
											2019-04-24 16:15:41 +03:00
										 |  |  | 	// path items to reveal the target...
 | 
					
						
							| 
									
										
										
										
											2019-05-11 03:02:42 +03:00
										 |  |  | 	//
 | 
					
						
							| 
									
										
										
										
											2019-04-24 16:15:41 +03:00
										 |  |  | 	// XXX should this return the item or this???
 | 
					
						
							| 
									
										
										
										
											2019-05-11 00:14:10 +03:00
										 |  |  | 	// XXX make .reveal('all'/'*') only do the actual nodes that need expanding...
 | 
					
						
							|  |  |  | 	// 		...currently for path 'a/b/c/d' we'll go through:
 | 
					
						
							|  |  |  | 	// 			'a/b'
 | 
					
						
							|  |  |  | 	// 			'a/b/c'
 | 
					
						
							|  |  |  | 	// 			'a/b/c/d'
 | 
					
						
							|  |  |  | 	// XXX need a universal item name/value comparison / getter...
 | 
					
						
							| 
									
										
										
										
											2019-04-24 16:15:41 +03:00
										 |  |  | 	reveal: function(key, options){ | 
					
						
							| 
									
										
										
										
											2019-05-10 21:07:17 +03:00
										 |  |  | 		var that = this | 
					
						
							| 
									
										
										
										
											2019-05-11 00:14:10 +03:00
										 |  |  | 		var seen = new Set() | 
					
						
							|  |  |  | 		return this.search(key,  | 
					
						
							|  |  |  | 				function(e, i, path){ | 
					
						
							|  |  |  | 					return [path, e] },  | 
					
						
							|  |  |  | 				Object.assign( | 
					
						
							|  |  |  | 					{ | 
					
						
							|  |  |  | 						iterateCollapsed: true, | 
					
						
							|  |  |  | 						reverse: 'flat', | 
					
						
							|  |  |  | 					},  | 
					
						
							|  |  |  | 					options || {})) | 
					
						
							|  |  |  | 			// sort paths long to short...
 | 
					
						
							|  |  |  | 			//.sort(function(a, b){
 | 
					
						
							|  |  |  | 			//	return b[0].length - a[0].length })
 | 
					
						
							|  |  |  | 			.map(function([path, e]){ | 
					
						
							|  |  |  | 				// skip paths we have already seen...
 | 
					
						
							|  |  |  | 				if(seen.has(e.id)){ | 
					
						
							|  |  |  | 					return e | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 				seen.add(e.id) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-05-10 21:07:17 +03:00
										 |  |  | 				var cur = that | 
					
						
							| 
									
										
										
										
											2019-05-11 00:14:10 +03:00
										 |  |  | 				path.length > 1 | 
					
						
							|  |  |  | 					&& path | 
					
						
							|  |  |  | 						.slice(0, -1) | 
					
						
							|  |  |  | 						.forEach(function(n){ | 
					
						
							|  |  |  | 							// array children...
 | 
					
						
							|  |  |  | 							if(cur instanceof Array){ | 
					
						
							|  |  |  | 								var e = cur | 
					
						
							|  |  |  | 									.filter(function(e){  | 
					
						
							|  |  |  | 										// XXX need a universal item name test...
 | 
					
						
							|  |  |  | 										return n == (e.value || e.id) }) | 
					
						
							|  |  |  | 									.pop() | 
					
						
							|  |  |  | 								delete e.collapsed | 
					
						
							|  |  |  | 								cur = e.children | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 							} else { | 
					
						
							| 
									
										
										
										
											2019-05-16 05:00:15 +03:00
										 |  |  | 								// XXX .index feels ugly...
 | 
					
						
							|  |  |  | 								delete cur.index[n].collapsed | 
					
						
							|  |  |  | 								cur = cur.index[n].children | 
					
						
							| 
									
										
										
										
											2019-05-11 00:14:10 +03:00
										 |  |  | 							} | 
					
						
							|  |  |  | 						}) | 
					
						
							| 
									
										
										
										
											2019-05-10 21:07:17 +03:00
										 |  |  | 				return e | 
					
						
							| 
									
										
										
										
											2019-05-11 00:14:10 +03:00
										 |  |  | 			}) | 
					
						
							|  |  |  | 			.run(function(){ | 
					
						
							|  |  |  | 				that.render() }) }, | 
					
						
							| 
									
										
										
										
											2019-04-24 16:15:41 +03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-03-17 21:54:26 +03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-04-24 16:10:26 +03:00
										 |  |  | 	// 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...
 | 
					
						
							| 
									
										
										
										
											2019-03-17 21:54:26 +03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-03-20 02:00:43 +03:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-05-16 05:00:15 +03:00
										 |  |  | 	// Make .items and .index...
 | 
					
						
							| 
									
										
										
										
											2019-04-24 16:10:26 +03:00
										 |  |  | 	//
 | 
					
						
							|  |  |  | 	// 	.make()
 | 
					
						
							|  |  |  | 	// 	.make(options)
 | 
					
						
							|  |  |  | 	// 		-> this
 | 
					
						
							|  |  |  | 	//
 | 
					
						
							|  |  |  | 	// The items are constructed by passing a make function to .__list__(..)
 | 
					
						
							|  |  |  | 	// which in turn will call this make(..) per item created.
 | 
					
						
							|  |  |  | 	//
 | 
					
						
							|  |  |  | 	// For more doc on item construction see: .__init__(..)
 | 
					
						
							|  |  |  | 	//
 | 
					
						
							|  |  |  | 	//
 | 
					
						
							| 
									
										
										
										
											2019-05-16 05:00:15 +03:00
										 |  |  | 	// NOTE: each call to this will reset both .items and .index
 | 
					
						
							| 
									
										
										
										
											2019-04-24 16:10:26 +03:00
										 |  |  | 	// NOTE: for items with repeating values there is no way to correctly 
 | 
					
						
							|  |  |  | 	// 		identify an item thus no state is maintained between .make(..)
 | 
					
						
							|  |  |  | 	// 		calls for such items...
 | 
					
						
							|  |  |  | 	//
 | 
					
						
							|  |  |  | 	// XXX revise options handling for .__list__(..)
 | 
					
						
							|  |  |  | 	make: function(options){ | 
					
						
							|  |  |  | 		options = Object.assign(Object.create(this.options || {}), options || {}) | 
					
						
							| 
									
										
										
										
											2019-03-20 02:00:43 +03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-04-24 16:10:26 +03:00
										 |  |  | 		var items = this.items = [] | 
					
						
							|  |  |  | 		var old_index = this.__item_index || {} | 
					
						
							|  |  |  | 		var new_index = this.__item_index = {}  | 
					
						
							| 
									
										
										
										
											2019-03-20 02:00:43 +03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-04-24 16:10:26 +03:00
										 |  |  | 		// item constructor...
 | 
					
						
							|  |  |  | 		//
 | 
					
						
							|  |  |  | 		// 	Make an item...
 | 
					
						
							|  |  |  | 		// 	make(value[, options])
 | 
					
						
							|  |  |  | 		// 	make(value, func[, options])
 | 
					
						
							|  |  |  | 		// 		-> make
 | 
					
						
							|  |  |  | 		//
 | 
					
						
							|  |  |  | 		// 	Inline a browser instance...
 | 
					
						
							|  |  |  | 		// 	make(browser)
 | 
					
						
							|  |  |  | 		// 		-> make
 | 
					
						
							|  |  |  | 		//
 | 
					
						
							|  |  |  | 		//
 | 
					
						
							|  |  |  | 		// NOTE: when inlining a browser, options are ignored.
 | 
					
						
							|  |  |  | 		// NOTE: when inlining a browser it's .parent will be set this 
 | 
					
						
							|  |  |  | 		// 		reusing the inlined object browser may mess up this 
 | 
					
						
							|  |  |  | 		// 		property...
 | 
					
						
							|  |  |  | 		//
 | 
					
						
							|  |  |  | 		// XXX problem: make(Browser(..), ..) and make.group(...) produce 
 | 
					
						
							|  |  |  | 		// 		different formats -- the first stores {value: browser, ...}
 | 
					
						
							|  |  |  | 		// 		while the latter stores a list of items.
 | 
					
						
							|  |  |  | 		// 		...would be more logical to store the object (i.e. browser/list)
 | 
					
						
							|  |  |  | 		// 		directly as the element...
 | 
					
						
							|  |  |  | 		var make_called = false | 
					
						
							|  |  |  | 		var make = function(value, opts){ | 
					
						
							|  |  |  | 			make_called = true | 
					
						
							| 
									
										
										
										
											2019-03-20 02:00:43 +03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-04-24 16:10:26 +03:00
										 |  |  | 			// special-case: inlined browser...
 | 
					
						
							|  |  |  | 			//
 | 
					
						
							|  |  |  | 			// NOTE: we ignore opts here...
 | 
					
						
							|  |  |  | 			// XXX not sure if this is the right way to go...
 | 
					
						
							|  |  |  | 			// 		...for removal just remove the if statement and its
 | 
					
						
							|  |  |  | 			// 		first branch...
 | 
					
						
							|  |  |  | 			if(value instanceof Browser){ | 
					
						
							|  |  |  | 				var item = value | 
					
						
							|  |  |  | 				item.parent = this | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			// normal item...
 | 
					
						
							|  |  |  | 			} else { | 
					
						
							|  |  |  | 				var args = [...arguments] | 
					
						
							|  |  |  | 				opts = opts || {} | 
					
						
							|  |  |  | 				// handle: make(.., func, ..)
 | 
					
						
							|  |  |  | 				opts = opts instanceof Function ? | 
					
						
							|  |  |  | 					{open: opts} | 
					
						
							|  |  |  | 					: opts | 
					
						
							|  |  |  | 				// handle trailing options...
 | 
					
						
							|  |  |  | 				opts = args.length > 2 ? | 
					
						
							|  |  |  | 					Object.assign({}, | 
					
						
							|  |  |  | 						args.pop(), | 
					
						
							|  |  |  | 						opts) | 
					
						
							|  |  |  | 					: opts | 
					
						
							|  |  |  | 				opts = Object.assign( | 
					
						
							|  |  |  | 					{}, | 
					
						
							|  |  |  | 					opts,  | 
					
						
							|  |  |  | 					{value: value}) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 				// item id...
 | 
					
						
							|  |  |  | 				var key = this.__key__(opts) | 
					
						
							|  |  |  | 				var id_changed = (old_index[key] || {}).id_changed | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 				// handle duplicate ids -> err if found...
 | 
					
						
							|  |  |  | 				if(opts.id && opts.id in new_index){ | 
					
						
							|  |  |  | 					throw new Error(`make(..): duplicate id "${key}": ` | 
					
						
							|  |  |  | 						+`can't create multiple items with the same key.`) } | 
					
						
							|  |  |  | 				// handle duplicate keys...
 | 
					
						
							|  |  |  | 				// NOTE: we can't reuse an old copy when re-making the list
 | 
					
						
							| 
									
										
										
										
											2019-05-17 19:53:33 +03:00
										 |  |  | 				// 		because there is no way to correctly identify an 
 | 
					
						
							| 
									
										
										
										
											2019-04-24 16:10:26 +03:00
										 |  |  | 				// 		object when it's id is tweaked (and we can not rely
 | 
					
						
							|  |  |  | 				// 		on item order)...
 | 
					
						
							|  |  |  | 				// 		...for this reason all "persistent" state for such 
 | 
					
						
							|  |  |  | 				// 		an element will be lost when calling .make(..) again
 | 
					
						
							|  |  |  | 				// 		and re-making the list...
 | 
					
						
							|  |  |  | 				// 		a solution to this would be to manually assign an .id 
 | 
					
						
							|  |  |  | 				// 		to such elements in .__list__(..)...
 | 
					
						
							|  |  |  | 				// 		XXX can we go around this without requiring the user 
 | 
					
						
							|  |  |  | 				// 			to manage ids???
 | 
					
						
							|  |  |  | 				var k = key | 
					
						
							|  |  |  | 				while(k in new_index){ | 
					
						
							|  |  |  | 					// duplicate keys disabled...
 | 
					
						
							|  |  |  | 					if(options.noDuplicateValues){ | 
					
						
							|  |  |  | 						throw new Error(`make(..): duplicate key "${key}": ` | 
					
						
							|  |  |  | 							+`can't create multiple items with the same key.`) } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 					// mark both the current and the first items as id-mutated...
 | 
					
						
							|  |  |  | 					opts.id_changed = true | 
					
						
							|  |  |  | 					new_index[key].id_changed = true | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 					// create a new key...
 | 
					
						
							|  |  |  | 					k = this.__id__(key) | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 				key = opts.id = k | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 				// build the item...
 | 
					
						
							|  |  |  | 				var item = Object.assign( | 
					
						
							|  |  |  | 					Object.create(options || {}),  | 
					
						
							|  |  |  | 					// get the old item values (only for non duplicate items)...
 | 
					
						
							|  |  |  | 					id_changed ? | 
					
						
							|  |  |  | 						{} | 
					
						
							|  |  |  | 						: old_index[key] || {}, | 
					
						
							|  |  |  | 					// XXX inherit from this...
 | 
					
						
							|  |  |  | 					opts, | 
					
						
							|  |  |  | 					{ | 
					
						
							|  |  |  | 						parent: this, | 
					
						
							|  |  |  | 					}) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 				// XXX do we need both this and the above ref???
 | 
					
						
							| 
									
										
										
										
											2019-05-04 04:46:29 +03:00
										 |  |  | 				item.children instanceof Browser | 
					
						
							|  |  |  | 					&& (item.children.parent = this) | 
					
						
							| 
									
										
										
										
											2019-03-20 02:00:43 +03:00
										 |  |  | 			} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-04-24 16:10:26 +03:00
										 |  |  | 			// store the item...
 | 
					
						
							|  |  |  | 			items.push(item) | 
					
						
							|  |  |  | 			new_index[key] = item | 
					
						
							| 
									
										
										
										
											2019-03-17 23:30:27 +03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-04-24 16:10:26 +03:00
										 |  |  | 			return make | 
					
						
							|  |  |  | 		}.bind(this) | 
					
						
							|  |  |  | 		make.__proto__ = Items | 
					
						
							|  |  |  | 		make.dialog = this | 
					
						
							|  |  |  | 		make.items = items | 
					
						
							| 
									
										
										
										
											2019-03-17 23:30:27 +03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-04-24 16:10:26 +03:00
										 |  |  | 		//var res = this.__list__(make)
 | 
					
						
							|  |  |  | 		// XXX not sure about this -- options handling...
 | 
					
						
							|  |  |  | 		var res = this.__list__(make,  | 
					
						
							|  |  |  | 			options ?  | 
					
						
							|  |  |  | 				Object.assign( | 
					
						
							|  |  |  | 					Object.create(this.options || {}),  | 
					
						
							|  |  |  | 					options || {})  | 
					
						
							|  |  |  | 				: null) | 
					
						
							| 
									
										
										
										
											2019-03-17 23:30:27 +03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-04-24 16:10:26 +03:00
										 |  |  | 		// if make was not called use the .__list__(..) return value...
 | 
					
						
							|  |  |  | 		this.items = make_called ?  | 
					
						
							|  |  |  | 			this.items  | 
					
						
							|  |  |  | 			: res | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		return this | 
					
						
							| 
									
										
										
										
											2019-03-17 23:30:27 +03:00
										 |  |  | 	}, | 
					
						
							| 
									
										
										
										
											2019-03-17 21:54:26 +03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-03-20 02:00:43 +03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-04-24 16:10:26 +03:00
										 |  |  | 	// Renderers...
 | 
					
						
							| 
									
										
										
										
											2019-03-18 03:58:23 +03:00
										 |  |  | 	//
 | 
					
						
							| 
									
										
										
										
											2019-04-25 03:25:03 +03:00
										 |  |  | 	// 	.renderFinalize(items, context)
 | 
					
						
							| 
									
										
										
										
											2019-04-24 16:10:26 +03:00
										 |  |  | 	// 	.renderList(items, context)
 | 
					
						
							| 
									
										
										
										
											2019-05-04 04:46:29 +03:00
										 |  |  | 	// 	.renderNested(header, children, item, context)
 | 
					
						
							| 
									
										
										
										
											2019-04-24 16:10:26 +03:00
										 |  |  | 	// 	.renderNestedHeader(item, i, context)
 | 
					
						
							|  |  |  | 	// 	.renderItem(item, i, context)
 | 
					
						
							|  |  |  | 	// 	.renderGroup(items, context)
 | 
					
						
							| 
									
										
										
										
											2019-03-18 03:58:23 +03:00
										 |  |  | 	//
 | 
					
						
							| 
									
										
										
										
											2019-04-24 16:10:26 +03:00
										 |  |  | 	//
 | 
					
						
							| 
									
										
										
										
											2019-04-25 03:25:03 +03:00
										 |  |  | 	renderFinalize: function(items, context){ | 
					
						
							| 
									
										
										
										
											2019-04-24 17:01:57 +03:00
										 |  |  | 		return this.renderList(items, context) }, | 
					
						
							| 
									
										
										
										
											2019-04-24 16:10:26 +03:00
										 |  |  | 	renderList: function(items, context){ | 
					
						
							|  |  |  | 		return items }, | 
					
						
							|  |  |  | 	// NOTE: to skip rendering an item/list return null...
 | 
					
						
							| 
									
										
										
										
											2019-05-04 04:46:29 +03:00
										 |  |  | 	// XXX should this take an empty children???
 | 
					
						
							| 
									
										
										
										
											2019-04-24 16:10:26 +03:00
										 |  |  | 	// 		...this would make it simpler to expand/collapse without 
 | 
					
						
							|  |  |  | 	// 		re-rendering the whole list...
 | 
					
						
							| 
									
										
										
										
											2019-05-04 04:46:29 +03:00
										 |  |  | 	renderNested: function(header, children, item, context){ | 
					
						
							| 
									
										
										
										
											2019-04-24 16:10:26 +03:00
										 |  |  | 		return header ?  | 
					
						
							|  |  |  | 			this.renderGroup([ | 
					
						
							|  |  |  | 				header,  | 
					
						
							| 
									
										
										
										
											2019-05-04 04:46:29 +03:00
										 |  |  | 				children, | 
					
						
							| 
									
										
										
										
											2019-04-24 16:10:26 +03:00
										 |  |  | 			]) | 
					
						
							| 
									
										
										
										
											2019-05-04 04:46:29 +03:00
										 |  |  |    			: children }, | 
					
						
							| 
									
										
										
										
											2019-04-24 16:10:26 +03:00
										 |  |  | 	renderNestedHeader: function(item, i, context){ | 
					
						
							|  |  |  | 		return this.renderItem(item, i, context) }, | 
					
						
							|  |  |  | 	// NOTE: to skip rendering an item/list return null...
 | 
					
						
							|  |  |  | 	renderItem: function(item, i, context){ | 
					
						
							|  |  |  | 		return item }, | 
					
						
							|  |  |  | 	renderGroup: function(items, context){ | 
					
						
							|  |  |  | 		return items }, | 
					
						
							| 
									
										
										
										
											2019-03-18 03:58:23 +03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-04-25 03:25:03 +03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-04-24 16:10:26 +03:00
										 |  |  | 	// Render state...
 | 
					
						
							| 
									
										
										
										
											2019-04-22 02:16:30 +03:00
										 |  |  | 	//
 | 
					
						
							| 
									
										
										
										
											2019-04-24 16:10:26 +03:00
										 |  |  | 	//	.render()
 | 
					
						
							| 
									
										
										
										
											2019-05-12 12:59:48 +03:00
										 |  |  | 	//	.render(options[, renderer[, context]])
 | 
					
						
							| 
									
										
										
										
											2019-04-24 16:10:26 +03:00
										 |  |  | 	//		-> state
 | 
					
						
							| 
									
										
										
										
											2019-04-21 16:54:19 +03:00
										 |  |  | 	//
 | 
					
						
							| 
									
										
										
										
											2019-04-22 02:16:30 +03:00
										 |  |  | 	//
 | 
					
						
							| 
									
										
										
										
											2019-04-24 16:10:26 +03:00
										 |  |  | 	// context format:
 | 
					
						
							| 
									
										
										
										
											2019-04-22 02:16:30 +03:00
										 |  |  | 	// 	{
 | 
					
						
							| 
									
										
										
										
											2019-04-24 16:10:26 +03:00
										 |  |  | 	// 		root: <root-browser>,
 | 
					
						
							|  |  |  | 	// 		options: <options>,
 | 
					
						
							| 
									
										
										
										
											2019-04-22 02:16:30 +03:00
										 |  |  | 	// 	}
 | 
					
						
							|  |  |  | 	//
 | 
					
						
							|  |  |  | 	//
 | 
					
						
							| 
									
										
										
										
											2019-04-25 03:25:03 +03:00
										 |  |  | 	// options:
 | 
					
						
							|  |  |  | 	// 	{
 | 
					
						
							|  |  |  | 	// 		nonFinalized: <bool>,
 | 
					
						
							|  |  |  | 	//
 | 
					
						
							|  |  |  | 	//		// for more supported options see: .walk(..)
 | 
					
						
							|  |  |  | 	// 		...
 | 
					
						
							|  |  |  | 	// 	}
 | 
					
						
							|  |  |  | 	//
 | 
					
						
							|  |  |  | 	//
 | 
					
						
							|  |  |  | 	// NOTE: it is not recommended to extend this. all the responsibility
 | 
					
						
							|  |  |  | 	// 		of actual rendering should lay on the renderer methods...
 | 
					
						
							|  |  |  | 	// NOTE: calling this will re-render the existing state. to re-make 
 | 
					
						
							|  |  |  | 	// 		the state anew that use .update(..)...
 | 
					
						
							| 
									
										
										
										
											2019-04-24 16:10:26 +03:00
										 |  |  | 	// NOTE: currently options and context are distinguished only via 
 | 
					
						
							|  |  |  | 	// 		the .options attribute...
 | 
					
						
							| 
									
										
										
										
											2019-05-11 03:02:42 +03:00
										 |  |  | 	render: function(options, renderer, context){ | 
					
						
							|  |  |  | 		context = context || {} | 
					
						
							| 
									
										
										
										
											2019-04-22 02:16:30 +03:00
										 |  |  | 		renderer = renderer || this | 
					
						
							| 
									
										
										
										
											2019-05-11 03:02:42 +03:00
										 |  |  | 		options = context.options  | 
					
						
							|  |  |  | 			|| Object.assign( | 
					
						
							|  |  |  | 				Object.create(this.options || {}), | 
					
						
							|  |  |  | 				{ iterateNonIterable: true },  | 
					
						
							|  |  |  | 				options || {}) | 
					
						
							|  |  |  | 		context.options = context.options || options | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-05-11 04:44:50 +03:00
										 |  |  | 		// do the walk...
 | 
					
						
							| 
									
										
										
										
											2019-05-11 03:02:42 +03:00
										 |  |  | 		var elems = this.walk( | 
					
						
							| 
									
										
										
										
											2019-05-11 04:44:50 +03:00
										 |  |  | 			function(elem, i, path, nested){ | 
					
						
							| 
									
										
										
										
											2019-05-11 03:02:42 +03:00
										 |  |  | 				return ( | 
					
						
							|  |  |  | 					// inline...
 | 
					
						
							|  |  |  | 					elem == null ? | 
					
						
							|  |  |  | 						// NOTE: here we are forcing rendering of the 
 | 
					
						
							|  |  |  | 						// 		inline browser/list, i.e. ignoring 
 | 
					
						
							|  |  |  | 						// 		options.skipNested for inline stuff...
 | 
					
						
							|  |  |  | 						[ renderer.renderGroup(nested(true), context) ] | 
					
						
							|  |  |  | 					// nested...
 | 
					
						
							|  |  |  | 					: elem.children ? | 
					
						
							|  |  |  | 						[ renderer.renderNested( | 
					
						
							|  |  |  | 							renderer.renderNestedHeader(elem, i, context), | 
					
						
							|  |  |  | 							nested(), | 
					
						
							|  |  |  | 							elem,  | 
					
						
							|  |  |  | 							context) ] | 
					
						
							|  |  |  | 					// normal elem...
 | 
					
						
							|  |  |  | 					: [ renderer.renderItem(elem, i, context) ] ) }, | 
					
						
							|  |  |  | 			'render', | 
					
						
							|  |  |  | 			function(_, i, p, options, context){ | 
					
						
							|  |  |  | 				return [options, renderer, context] }, | 
					
						
							|  |  |  | 			options, context) | 
					
						
							| 
									
										
										
										
											2019-04-22 02:16:30 +03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-05-11 04:44:50 +03:00
										 |  |  | 		// finalize depending on render mode...
 | 
					
						
							| 
									
										
										
										
											2019-04-25 03:25:03 +03:00
										 |  |  | 		return (!options.nonFinalized && context.root === this) ? | 
					
						
							| 
									
										
										
										
											2019-04-24 16:10:26 +03:00
										 |  |  | 			// root context -> render list and return this...
 | 
					
						
							| 
									
										
										
										
											2019-05-11 03:02:42 +03:00
										 |  |  | 			renderer.renderFinalize(elems, context) | 
					
						
							| 
									
										
										
										
											2019-04-24 16:10:26 +03:00
										 |  |  | 			// nested context -> return item list...
 | 
					
						
							| 
									
										
										
										
											2019-05-11 03:02:42 +03:00
										 |  |  | 			: elems | 
					
						
							| 
									
										
										
										
											2019-04-22 02:16:30 +03:00
										 |  |  | 	}, | 
					
						
							| 
									
										
										
										
											2019-04-24 16:10:26 +03:00
										 |  |  | 	 | 
					
						
							| 
									
										
										
										
											2019-04-24 04:17:07 +03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-04-24 16:10:26 +03:00
										 |  |  | 	// Update state (make then render)...
 | 
					
						
							|  |  |  | 	//
 | 
					
						
							|  |  |  | 	// 	.update()
 | 
					
						
							|  |  |  | 	// 		-> state
 | 
					
						
							|  |  |  | 	//
 | 
					
						
							| 
									
										
										
										
											2019-05-21 04:27:12 +03:00
										 |  |  | 	// XXX should this be an event???
 | 
					
						
							| 
									
										
										
										
											2019-04-24 16:10:26 +03:00
										 |  |  | 	update: function(options){ | 
					
						
							|  |  |  | 		return this | 
					
						
							|  |  |  | 			.make(options) | 
					
						
							|  |  |  | 			.render(options) }, | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-02-27 03:35:03 +03:00
										 |  |  | 	// Events...
 | 
					
						
							| 
									
										
										
										
											2019-02-26 04:40:38 +03:00
										 |  |  | 	//
 | 
					
						
							|  |  |  | 	// Format:
 | 
					
						
							|  |  |  | 	// 	{
 | 
					
						
							|  |  |  | 	// 		// XXX add tagged event support...
 | 
					
						
							| 
									
										
										
										
											2019-05-17 19:53:33 +03:00
										 |  |  | 	// 		//		...i.e. event markers/tags that would enable the user
 | 
					
						
							|  |  |  | 	// 		//		to specifically manipulate event sets....
 | 
					
						
							| 
									
										
										
										
											2019-02-26 04:40:38 +03:00
										 |  |  | 	// 		<event-name>: [
 | 
					
						
							|  |  |  | 	// 			<handler>,
 | 
					
						
							|  |  |  | 	// 			...
 | 
					
						
							|  |  |  | 	// 		],
 | 
					
						
							|  |  |  | 	// 		...
 | 
					
						
							|  |  |  | 	// 	}
 | 
					
						
							|  |  |  | 	//
 | 
					
						
							| 
									
										
										
										
											2019-02-26 04:13:00 +03:00
										 |  |  | 	// XXX
 | 
					
						
							|  |  |  | 	__event_handlers: null, | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-02-26 04:40:38 +03:00
										 |  |  | 	// generic event infrastructure...
 | 
					
						
							|  |  |  | 	// XXX add support for item events...
 | 
					
						
							|  |  |  | 	// 		e.g. item.focus(..) -> root.focus(..)
 | 
					
						
							| 
									
										
										
										
											2019-02-27 03:35:03 +03:00
										 |  |  | 	// XXX also need to design a means for this system to interact both 
 | 
					
						
							|  |  |  | 	// 		ways with DOM events...
 | 
					
						
							| 
									
										
										
										
											2019-03-02 17:29:26 +03:00
										 |  |  | 	// XXX need to bubble the event up through the nested browsers...
 | 
					
						
							| 
									
										
										
										
											2019-05-17 19:53:33 +03:00
										 |  |  | 	on: function(evt, handler, tag){ | 
					
						
							| 
									
										
										
										
											2019-02-26 04:13:00 +03:00
										 |  |  | 		var handlers = this.__event_handlers = this.__event_handlers || {} | 
					
						
							|  |  |  | 		handlers = handlers[evt] = handlers[evt] || [] | 
					
						
							|  |  |  | 		handlers.push(handler) | 
					
						
							| 
									
										
										
										
											2019-05-17 19:53:33 +03:00
										 |  |  | 		tag | 
					
						
							|  |  |  | 			&& (handler.tag = tag) | 
					
						
							| 
									
										
										
										
											2019-02-24 14:51:35 +03:00
										 |  |  | 		return this | 
					
						
							|  |  |  | 	}, | 
					
						
							|  |  |  | 	one: function(evt, handler){ | 
					
						
							| 
									
										
										
										
											2019-02-26 04:13:00 +03:00
										 |  |  | 		var func = function(...args){ | 
					
						
							|  |  |  | 			handler.call(this, ...args) | 
					
						
							|  |  |  | 			this.off(evt, func) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		this.on(evt, func) | 
					
						
							| 
									
										
										
										
											2019-02-24 14:51:35 +03:00
										 |  |  | 		return this | 
					
						
							|  |  |  | 	}, | 
					
						
							| 
									
										
										
										
											2019-05-17 19:53:33 +03:00
										 |  |  | 	//
 | 
					
						
							|  |  |  | 	//	Clear all event handlers...
 | 
					
						
							|  |  |  | 	//	.off('*')
 | 
					
						
							|  |  |  | 	//
 | 
					
						
							|  |  |  | 	//	Clear all event handlers from evt(s)...
 | 
					
						
							|  |  |  | 	//	.off(evt)
 | 
					
						
							|  |  |  | 	//	.off([evt, ..])
 | 
					
						
							|  |  |  | 	//	.off(evt, '*')
 | 
					
						
							|  |  |  | 	//	.off([evt, ..], '*')
 | 
					
						
							|  |  |  | 	//
 | 
					
						
							|  |  |  | 	//	Clear handler of evt(s)...
 | 
					
						
							|  |  |  | 	//	.off(evt, handler)
 | 
					
						
							|  |  |  | 	//	.off([evt, ..], handler)
 | 
					
						
							|  |  |  | 	//
 | 
					
						
							|  |  |  | 	//	Clear all handlers tagged with tag of evt(s)...
 | 
					
						
							|  |  |  | 	//	.off(evt, tag)
 | 
					
						
							|  |  |  | 	//	.off([evt, ..], tag)
 | 
					
						
							|  |  |  | 	//
 | 
					
						
							|  |  |  | 	// NOTE: evt can be '*' or 'all' to indicate all events.
 | 
					
						
							| 
									
										
										
										
											2019-02-24 14:51:35 +03:00
										 |  |  | 	off: function(evt, handler){ | 
					
						
							| 
									
										
										
										
											2019-05-17 19:53:33 +03:00
										 |  |  | 		if(arguments.length == 0){ | 
					
						
							|  |  |  | 			return | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-05-18 02:58:38 +03:00
										 |  |  | 		var handlers = this.__event_handlers || {} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-05-17 19:53:33 +03:00
										 |  |  | 		// parse args...
 | 
					
						
							|  |  |  | 		handler = handler || '*' | 
					
						
							|  |  |  | 		evt =  | 
					
						
							|  |  |  | 			// all events / direct handler...
 | 
					
						
							|  |  |  | 			(!(evt in handlers)  | 
					
						
							|  |  |  | 					|| evt == '*'  | 
					
						
							|  |  |  | 					|| evt == 'all') ?  | 
					
						
							|  |  |  | 				Object.keys(handlers)  | 
					
						
							|  |  |  | 			// list of events...
 | 
					
						
							|  |  |  | 			: evt instanceof Array ? | 
					
						
							|  |  |  | 				evt | 
					
						
							|  |  |  | 			// explicit event...
 | 
					
						
							|  |  |  | 			: [evt] | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-02-26 04:13:00 +03:00
										 |  |  | 		// remove all handlers
 | 
					
						
							| 
									
										
										
										
											2019-05-17 19:53:33 +03:00
										 |  |  | 		handler == '*' || handler == 'all' ? | 
					
						
							|  |  |  | 			evt | 
					
						
							|  |  |  | 				.forEach(function(evt){ | 
					
						
							|  |  |  | 					delete handlers[evt] }) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		// remove tagged handlers...
 | 
					
						
							|  |  |  | 		: typeof(handler) == typeof('str') ? | 
					
						
							|  |  |  | 			evt | 
					
						
							|  |  |  | 				.forEach(function(evt){ | 
					
						
							|  |  |  | 					var h = handlers[evt] || [] | 
					
						
							|  |  |  | 					var l = h.length | 
					
						
							|  |  |  | 					h | 
					
						
							|  |  |  | 						.slice() | 
					
						
							|  |  |  | 						.reverse() | 
					
						
							|  |  |  | 						.forEach(function(e, i){  | 
					
						
							|  |  |  | 							e.tag == handler | 
					
						
							|  |  |  | 								&& h.splice(l-i-1, 1) }) }) | 
					
						
							| 
									
										
										
										
											2019-02-26 04:13:00 +03:00
										 |  |  | 
 | 
					
						
							|  |  |  | 		// remove only the specific handler...
 | 
					
						
							| 
									
										
										
										
											2019-05-17 19:53:33 +03:00
										 |  |  | 		: evt | 
					
						
							|  |  |  | 			.forEach(function(evt){ | 
					
						
							|  |  |  | 				var h = handlers[evt] || [] | 
					
						
							|  |  |  | 				do{ | 
					
						
							|  |  |  | 					var i = h.indexOf(handler) | 
					
						
							|  |  |  | 					i > -1 | 
					
						
							|  |  |  | 						&& h.splice(i, 1) | 
					
						
							|  |  |  | 				} while(i > -1) }) | 
					
						
							| 
									
										
										
										
											2019-02-24 14:51:35 +03:00
										 |  |  | 		return this | 
					
						
							|  |  |  | 	}, | 
					
						
							| 
									
										
										
										
											2019-05-18 02:58:38 +03:00
										 |  |  | 	// 
 | 
					
						
							|  |  |  | 	// 	Trigger an event by name...
 | 
					
						
							|  |  |  | 	// 	.trigger(<event-name>, ..)
 | 
					
						
							|  |  |  | 	// 		-> this
 | 
					
						
							|  |  |  | 	//
 | 
					
						
							|  |  |  | 	// 	Trigger an event...
 | 
					
						
							|  |  |  | 	// 	.trigger(<event-object>, ..)
 | 
					
						
							|  |  |  | 	// 		-> this
 | 
					
						
							|  |  |  | 	//
 | 
					
						
							| 
									
										
										
										
											2019-05-18 03:15:03 +03:00
										 |  |  | 	//
 | 
					
						
							| 
									
										
										
										
											2019-05-18 02:58:38 +03:00
										 |  |  | 	// Passing an <event-name> will do the following:
 | 
					
						
							|  |  |  | 	// 	- if an <event-name> handler is available call it and return
 | 
					
						
							|  |  |  | 	// 		- the handler should:
 | 
					
						
							|  |  |  | 	// 			- do any specifics that it needs
 | 
					
						
							|  |  |  | 	// 			- create an <event-object>
 | 
					
						
							|  |  |  | 	// 			- call trigger with <event-object>
 | 
					
						
							|  |  |  | 	//  - if <event-name> has no handler:
 | 
					
						
							|  |  |  | 	//  	- create an <event-object>
 | 
					
						
							|  |  |  | 	//  	- call the event handlers passing them <event-object> and args
 | 
					
						
							|  |  |  | 	//  	- call parent's .trigger(<event-name>, ..)
 | 
					
						
							|  |  |  | 	//
 | 
					
						
							|  |  |  | 	// <event-object> format:
 | 
					
						
							|  |  |  | 	// 	{
 | 
					
						
							|  |  |  | 	// 		name: <name>,
 | 
					
						
							|  |  |  | 	//
 | 
					
						
							|  |  |  | 	// 		propagationStopped: <bool>,
 | 
					
						
							|  |  |  | 	// 		stopPropagation: <func>,
 | 
					
						
							|  |  |  | 	// 	}
 | 
					
						
							|  |  |  | 	//
 | 
					
						
							|  |  |  | 	//
 | 
					
						
							|  |  |  | 	// XXX need to make stopPropagation(..) work even if we got an 
 | 
					
						
							|  |  |  | 	// 		externally made event object...
 | 
					
						
							| 
									
										
										
										
											2019-05-18 03:15:03 +03:00
										 |  |  | 	// XXX need to make this workable with DOM events... (???)
 | 
					
						
							|  |  |  | 	// XXX construct the event in one spot...
 | 
					
						
							| 
									
										
										
										
											2019-02-24 14:51:35 +03:00
										 |  |  | 	trigger: function(evt, ...args){ | 
					
						
							| 
									
										
										
										
											2019-02-26 04:13:00 +03:00
										 |  |  | 		var that = this | 
					
						
							| 
									
										
										
										
											2019-05-18 02:58:38 +03:00
										 |  |  | 
 | 
					
						
							|  |  |  | 		// trigger the appropriate event handler if available...
 | 
					
						
							|  |  |  | 		// NOTE: this makes .someEvent(..) and .trigger('someEvent', ..)
 | 
					
						
							|  |  |  | 		// 		do the same thing by always triggering .someEvent(..) 
 | 
					
						
							|  |  |  | 		// 		first and letting it decide how to call .trigger(..)...
 | 
					
						
							|  |  |  | 		// NOTE: the event method should pass a fully formed event object
 | 
					
						
							|  |  |  | 		// 		into trigger when it requires to call the handlers...
 | 
					
						
							|  |  |  | 		if(typeof(evt) == typeof('str')  | 
					
						
							|  |  |  | 				&& this[evt] instanceof Function | 
					
						
							|  |  |  | 				&& this[evt].event == evt){ | 
					
						
							|  |  |  | 			this[evt](...args) | 
					
						
							|  |  |  | 			return this | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		// XXX need to make stopPropagation(..) work even if we got an 
 | 
					
						
							|  |  |  | 		// 		externally made event object...
 | 
					
						
							| 
									
										
										
										
											2019-03-06 16:06:48 +03:00
										 |  |  | 		var stopPropagation = false | 
					
						
							| 
									
										
										
										
											2019-02-26 04:20:05 +03:00
										 |  |  | 		var evt = typeof(evt) == typeof('str') ? | 
					
						
							| 
									
										
										
										
											2019-02-26 04:40:38 +03:00
										 |  |  | 			// XXX construct this in one place...
 | 
					
						
							|  |  |  | 			// 		...currently it is constructed here and in makeEventMethod(..)
 | 
					
						
							| 
									
										
										
										
											2019-02-26 04:20:05 +03:00
										 |  |  | 			{ | 
					
						
							| 
									
										
										
										
											2019-02-26 04:40:38 +03:00
										 |  |  | 				name: evt, | 
					
						
							| 
									
										
										
										
											2019-03-06 16:06:48 +03:00
										 |  |  | 				stopPropagation: function(){ | 
					
						
							| 
									
										
										
										
											2019-05-18 02:58:38 +03:00
										 |  |  | 					this.propagationStopped = stopPropagation = true }, | 
					
						
							| 
									
										
										
										
											2019-02-26 04:20:05 +03:00
										 |  |  | 			} | 
					
						
							|  |  |  | 			: evt | 
					
						
							| 
									
										
										
										
											2019-03-06 16:06:48 +03:00
										 |  |  | 
 | 
					
						
							|  |  |  | 		// call the main set of handlers...
 | 
					
						
							| 
									
										
										
										
											2019-02-26 04:20:05 +03:00
										 |  |  | 		;((this.__event_handlers || {})[evt.name] || []) | 
					
						
							| 
									
										
										
										
											2019-02-26 04:40:38 +03:00
										 |  |  | 			// prevent .off(..) from affecting the call loop...
 | 
					
						
							|  |  |  | 			.slice() | 
					
						
							| 
									
										
										
										
											2019-02-26 04:13:00 +03:00
										 |  |  | 			.forEach(function(handler){ | 
					
						
							| 
									
										
										
										
											2019-02-26 04:20:05 +03:00
										 |  |  | 				handler.call(that, evt, ...args) }) | 
					
						
							| 
									
										
										
										
											2019-03-06 16:06:48 +03:00
										 |  |  | 
 | 
					
						
							|  |  |  | 		// trigger the parent's event...
 | 
					
						
							|  |  |  | 		!stopPropagation | 
					
						
							| 
									
										
										
										
											2019-05-18 02:58:38 +03:00
										 |  |  | 			&& !evt.propagationStopped | 
					
						
							| 
									
										
										
										
											2019-03-06 16:06:48 +03:00
										 |  |  | 			&& this.parent | 
					
						
							| 
									
										
										
										
											2019-05-18 02:58:38 +03:00
										 |  |  | 			&& this.parent.trigger instanceof Function | 
					
						
							|  |  |  | 			// XXX should we trigger with and event object or an event 
 | 
					
						
							|  |  |  | 			// 		name???
 | 
					
						
							|  |  |  | 			//&& this.parent.trigger(evt, ...args)
 | 
					
						
							|  |  |  | 			&& this.parent.trigger(evt.name, ...args) | 
					
						
							| 
									
										
										
										
											2019-03-06 16:06:48 +03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-02-24 14:51:35 +03:00
										 |  |  | 		return this | 
					
						
							|  |  |  | 	}, | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-05-20 13:53:54 +03:00
										 |  |  | 	// List events...
 | 
					
						
							|  |  |  | 	// XXX avoid expensive props...
 | 
					
						
							|  |  |  | 	get events(){ | 
					
						
							|  |  |  | 		var that = this | 
					
						
							|  |  |  | 		return Object.deepKeys(this) | 
					
						
							|  |  |  | 			.map(function(key){ | 
					
						
							|  |  |  | 				return (key != 'events'  | 
					
						
							|  |  |  | 						&& that[key] instanceof Function  | 
					
						
							|  |  |  | 						&& that[key].event) ?  | 
					
						
							|  |  |  | 					that[key].event  | 
					
						
							|  |  |  | 					: [] }) | 
					
						
							|  |  |  | 			.flat() }, | 
					
						
							| 
									
										
										
										
											2019-04-25 03:25:03 +03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-02-26 04:40:38 +03:00
										 |  |  | 	// domain events/actions...
 | 
					
						
							| 
									
										
										
										
											2019-03-02 17:29:26 +03:00
										 |  |  | 	// XXX need a way to extend these to:
 | 
					
						
							|  |  |  | 	// 		- be able to trigger an external (DOM) event...
 | 
					
						
							|  |  |  | 	// 		- be able to be triggered from an external (DOM) event...
 | 
					
						
							| 
									
										
										
										
											2019-05-19 14:11:58 +03:00
										 |  |  | 	// XXX should we trigger direct item's parent event???
 | 
					
						
							| 
									
										
										
										
											2019-03-02 17:29:26 +03:00
										 |  |  | 	focus: makeItemEventMethod('focus', function(evt, items){ | 
					
						
							|  |  |  | 		// blur .focused...
 | 
					
						
							|  |  |  | 		this.focused | 
					
						
							|  |  |  | 			&& this.blur(this.focused) | 
					
						
							| 
									
										
										
										
											2019-05-19 14:11:58 +03:00
										 |  |  | 		// NOTE: if we got multiple matches we care only about the first one...
 | 
					
						
							|  |  |  | 		var item = items.shift() | 
					
						
							| 
									
										
										
										
											2019-05-13 18:58:17 +03:00
										 |  |  | 		item != null | 
					
						
							|  |  |  | 			&& (item.focused = true) | 
					
						
							| 
									
										
										
										
											2019-02-26 01:38:47 +03:00
										 |  |  | 	}), | 
					
						
							| 
									
										
										
										
											2019-03-02 17:29:26 +03:00
										 |  |  | 	blur: makeItemEventMethod('blur', function(evt, items){ | 
					
						
							|  |  |  | 		items.forEach(function(item){ | 
					
						
							|  |  |  | 			delete item.focused }) }), | 
					
						
							|  |  |  | 	select: makeItemEventMethod('select', function(evt, items){ | 
					
						
							|  |  |  | 		items.forEach(function(item){ | 
					
						
							| 
									
										
										
										
											2019-05-13 18:58:17 +03:00
										 |  |  | 			item.selected = true }) }), | 
					
						
							|  |  |  | 	deselect: makeItemEventMethod('deselect', function(evt, items){ | 
					
						
							| 
									
										
										
										
											2019-03-02 17:29:26 +03:00
										 |  |  | 		items.forEach(function(item){ | 
					
						
							| 
									
										
										
										
											2019-05-13 18:58:17 +03:00
										 |  |  | 			delete item.selected }) }), | 
					
						
							| 
									
										
										
										
											2019-03-02 17:29:26 +03:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	open: makeItemEventMethod('open', function(evt, item){}), | 
					
						
							|  |  |  | 	enter: makeItemEventMethod('enter', function(evt, item){}), | 
					
						
							| 
									
										
										
										
											2019-02-26 04:40:38 +03:00
										 |  |  | 	// XXX can/should we unify these???
 | 
					
						
							| 
									
										
										
										
											2019-03-02 17:29:26 +03:00
										 |  |  | 	collapse: makeItemEventMethod('collapse', function(evt, item){}), | 
					
						
							|  |  |  | 	expand: makeItemEventMethod('expand', function(evt, item){}), | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-02-23 06:38:46 +03:00
										 |  |  | 	// XXX target can be item or path...
 | 
					
						
							| 
									
										
										
										
											2019-02-26 04:40:38 +03:00
										 |  |  | 	load: makeEventMethod('load', function(evt, item){}), | 
					
						
							| 
									
										
										
										
											2019-03-02 17:29:26 +03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-02-26 04:40:38 +03:00
										 |  |  | 	close: makeEventMethod('close', function(evt, reason){}), | 
					
						
							| 
									
										
										
										
											2019-02-23 06:38:46 +03:00
										 |  |  | 	 | 
					
						
							| 
									
										
										
										
											2019-04-25 03:25:03 +03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-02-10 03:02:08 +03:00
										 |  |  | 	// XXX should we update on on init....
 | 
					
						
							| 
									
										
										
										
											2019-01-24 05:56:37 +03:00
										 |  |  | 	__init__: function(func, options){ | 
					
						
							| 
									
										
										
										
											2019-01-26 20:09:36 +03:00
										 |  |  | 		this.__list__ = func | 
					
						
							| 
									
										
										
										
											2019-01-24 05:56:37 +03:00
										 |  |  | 		this.options = Object.assign( | 
					
						
							|  |  |  | 			{},  | 
					
						
							|  |  |  | 			this.options || {},  | 
					
						
							|  |  |  | 			options || {}) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-02-10 03:02:08 +03:00
										 |  |  | 		// XXX should this be here or should this be optional???
 | 
					
						
							| 
									
										
										
										
											2019-02-09 01:39:13 +03:00
										 |  |  | 		//this.update()
 | 
					
						
							| 
									
										
										
										
											2019-01-24 05:56:37 +03:00
										 |  |  | 	}, | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | var BaseBrowser =  | 
					
						
							|  |  |  | module.BaseBrowser =  | 
					
						
							|  |  |  | object.makeConstructor('BaseBrowser',  | 
					
						
							|  |  |  | 		BaseBrowserClassPrototype,  | 
					
						
							|  |  |  | 		BaseBrowserPrototype) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-01-25 20:19:33 +03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-01-24 05:56:37 +03:00
										 |  |  | //---------------------------------------------------------------------
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | var BrowserClassPrototype = { | 
					
						
							|  |  |  | 	__proto__: BaseBrowser, | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-02-10 03:02:08 +03:00
										 |  |  | // XXX TODO:
 | 
					
						
							| 
									
										
										
										
											2019-02-10 18:27:08 +03:00
										 |  |  | // 		- need a way to update some stuff on .update() / .make() -- a way 
 | 
					
						
							|  |  |  | // 			to selectively merge the old state with the new...
 | 
					
						
							| 
									
										
										
										
											2019-02-10 03:02:08 +03:00
										 |  |  | // 		- event handler signature -- pass the item + optionally render...
 | 
					
						
							|  |  |  | // 		- keyboard handling...
 | 
					
						
							|  |  |  | // XXX render of nested lists does not affect the parent list(s)...
 | 
					
						
							| 
									
										
										
										
											2019-02-20 16:30:40 +03:00
										 |  |  | // 		...need to render lists and items both as a whole or independently...
 | 
					
						
							| 
									
										
										
										
											2019-02-08 03:21:50 +03:00
										 |  |  | // XXX should this use vanilla DOM or jQuery???
 | 
					
						
							| 
									
										
										
										
											2019-03-28 02:16:33 +03:00
										 |  |  | // XXX add a left button type/option -- expand/collapse and friends...
 | 
					
						
							| 
									
										
										
										
											2019-01-24 05:56:37 +03:00
										 |  |  | var BrowserPrototype = { | 
					
						
							|  |  |  | 	__proto__: BaseBrowser.prototype, | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	options: { | 
					
						
							| 
									
										
										
										
											2019-02-07 01:30:30 +03:00
										 |  |  | 		hideListHeader: false, | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-02-05 22:12:33 +03:00
										 |  |  | 		renderHidden: false, | 
					
						
							| 
									
										
										
										
											2019-01-24 05:56:37 +03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-02-10 03:02:08 +03:00
										 |  |  | 		localEvents: [ | 
					
						
							| 
									
										
										
										
											2019-02-10 19:28:10 +03:00
										 |  |  | 			// XXX STUB???
 | 
					
						
							| 
									
										
										
										
											2019-02-10 03:02:08 +03:00
										 |  |  | 			'click', | 
					
						
							| 
									
										
										
										
											2019-02-10 19:28:10 +03:00
										 |  |  | 
 | 
					
						
							|  |  |  | 			// XXX keyboard stuff...
 | 
					
						
							|  |  |  | 			// XXX
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			// XXX custom events...
 | 
					
						
							|  |  |  | 			// XXX
 | 
					
						
							| 
									
										
										
										
											2019-02-10 03:02:08 +03:00
										 |  |  | 		], | 
					
						
							| 
									
										
										
										
											2019-02-11 22:06:36 +03:00
										 |  |  | 		//buttonLocalEvents: [
 | 
					
						
							|  |  |  | 		//],
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		// Format:
 | 
					
						
							|  |  |  | 		// 	[
 | 
					
						
							|  |  |  | 		// 		['html', <handler>],
 | 
					
						
							|  |  |  | 		// 		...
 | 
					
						
							|  |  |  | 		// 	]
 | 
					
						
							|  |  |  | 		itemButtons: [ | 
					
						
							|  |  |  | 		], | 
					
						
							| 
									
										
										
										
											2019-02-12 02:34:11 +03:00
										 |  |  | 		// XXX need to mix these into the header only...
 | 
					
						
							|  |  |  | 		headerItemButtons: [ | 
					
						
							|  |  |  | 		], | 
					
						
							| 
									
										
										
										
											2019-02-27 03:35:03 +03:00
										 |  |  | 
 | 
					
						
							|  |  |  | 		// Shorthand elements...
 | 
					
						
							|  |  |  | 		//
 | 
					
						
							|  |  |  | 		// Format:
 | 
					
						
							|  |  |  | 		// 	{
 | 
					
						
							|  |  |  | 		// 		<key>: {
 | 
					
						
							|  |  |  | 		// 			class: <element-class-str>,
 | 
					
						
							|  |  |  | 		// 			html: <element-html-str>,
 | 
					
						
							|  |  |  | 		// 		},
 | 
					
						
							|  |  |  | 		// 		...
 | 
					
						
							|  |  |  | 		// 	}
 | 
					
						
							|  |  |  | 		//
 | 
					
						
							|  |  |  | 		// If make(..) gets passed <key> it will construct and element
 | 
					
						
							|  |  |  | 		// via <element-html-str> with an optional <element-class-str>
 | 
					
						
							|  |  |  | 		//
 | 
					
						
							|  |  |  | 		// NOTE: .class is optional...
 | 
					
						
							|  |  |  | 		// NOTE: set this to null to disable shorthands...
 | 
					
						
							| 
									
										
										
										
											2019-03-12 16:05:51 +03:00
										 |  |  | 		// NOTE: currently the options in the template will override 
 | 
					
						
							|  |  |  | 		// 		anything explicitly given by item options... (XXX revise)
 | 
					
						
							| 
									
										
										
										
											2019-02-27 03:35:03 +03:00
										 |  |  | 		elementShorthand: { | 
					
						
							|  |  |  | 			'---': { | 
					
						
							|  |  |  | 				'class': 'separator', | 
					
						
							| 
									
										
										
										
											2019-03-12 16:05:51 +03:00
										 |  |  | 				'html': '<hr>', | 
					
						
							|  |  |  | 				noniterable: true, | 
					
						
							| 
									
										
										
										
											2019-02-27 03:35:03 +03:00
										 |  |  | 			}, | 
					
						
							|  |  |  | 			'...': { | 
					
						
							|  |  |  | 				'class': 'separator', | 
					
						
							|  |  |  | 				'html': '<center><div class="loader"/></center>', | 
					
						
							| 
									
										
										
										
											2019-03-12 16:05:51 +03:00
										 |  |  | 				noniterable: true, | 
					
						
							| 
									
										
										
										
											2019-02-27 03:35:03 +03:00
										 |  |  | 			}, | 
					
						
							|  |  |  | 		}, | 
					
						
							| 
									
										
										
										
											2019-01-24 05:56:37 +03:00
										 |  |  | 	}, | 
					
						
							| 
									
										
										
										
											2019-02-09 02:20:31 +03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-02-09 03:42:34 +03:00
										 |  |  | 	// parent element (optional)...
 | 
					
						
							| 
									
										
										
										
											2019-03-05 18:15:49 +03:00
										 |  |  | 	// XXX rename???
 | 
					
						
							|  |  |  | 	// 		... should this be .containerDom or .parentDom???
 | 
					
						
							|  |  |  | 	get container(){ | 
					
						
							|  |  |  | 		return this.__container  | 
					
						
							| 
									
										
										
										
											2019-02-10 03:02:08 +03:00
										 |  |  | 			|| (this.__dom ?  | 
					
						
							|  |  |  | 				this.__dom.parentElement  | 
					
						
							|  |  |  | 				: undefined) }, | 
					
						
							| 
									
										
										
										
											2019-03-05 18:15:49 +03:00
										 |  |  | 	set container(value){ | 
					
						
							| 
									
										
										
										
											2019-02-09 02:20:31 +03:00
										 |  |  | 		var dom = this.dom | 
					
						
							| 
									
										
										
										
											2019-03-05 18:15:49 +03:00
										 |  |  | 		this.__container = value | 
					
						
							| 
									
										
										
										
											2019-02-09 02:20:31 +03:00
										 |  |  | 		// transfer the dom to the new parent...
 | 
					
						
							|  |  |  | 		dom && (this.dom = dom) | 
					
						
							|  |  |  | 	}, | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-02-09 03:42:34 +03:00
										 |  |  | 	// browser dom...
 | 
					
						
							| 
									
										
										
										
											2019-02-09 01:39:13 +03:00
										 |  |  | 	get dom(){ | 
					
						
							| 
									
										
										
										
											2019-02-10 03:02:08 +03:00
										 |  |  | 		return this.__dom }, | 
					
						
							| 
									
										
										
										
											2019-02-09 01:39:13 +03:00
										 |  |  | 	set dom(value){ | 
					
						
							| 
									
										
										
										
											2019-03-05 18:15:49 +03:00
										 |  |  | 		this.container  | 
					
						
							| 
									
										
										
										
											2019-02-10 03:02:08 +03:00
										 |  |  | 			&& (this.__dom ? | 
					
						
							| 
									
										
										
										
											2019-03-05 18:15:49 +03:00
										 |  |  | 				this.container.replaceChild(value, this.__dom)  | 
					
						
							|  |  |  | 				: this.container.appendChild(value)) | 
					
						
							| 
									
										
										
										
											2019-02-10 03:02:08 +03:00
										 |  |  | 		this.__dom = value }, | 
					
						
							| 
									
										
										
										
											2019-02-09 02:20:31 +03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-01-24 05:56:37 +03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-02-12 05:19:57 +03:00
										 |  |  | 	// Element renderers...
 | 
					
						
							| 
									
										
										
										
											2019-02-12 02:34:11 +03:00
										 |  |  | 	//
 | 
					
						
							| 
									
										
										
										
											2019-04-24 17:01:57 +03:00
										 |  |  | 	// This does tow additional things:
 | 
					
						
							|  |  |  | 	// 	- save the rendered state to .dom 
 | 
					
						
							|  |  |  | 	// 	- wrap a list of nodes (nested list) in a div
 | 
					
						
							|  |  |  | 	//
 | 
					
						
							|  |  |  | 	// Format:
 | 
					
						
							| 
									
										
										
										
											2019-04-24 18:58:16 +03:00
										 |  |  | 	// 	if list of items passed:
 | 
					
						
							|  |  |  | 	// 		<div>
 | 
					
						
							|  |  |  | 	// 			<!-- items -->
 | 
					
						
							|  |  |  | 	// 			...
 | 
					
						
							|  |  |  | 	// 		</div>
 | 
					
						
							|  |  |  | 	// 	or same as .renderList(..)
 | 
					
						
							| 
									
										
										
										
											2019-04-24 17:01:57 +03:00
										 |  |  | 	//
 | 
					
						
							|  |  |  | 	// XXX revise...
 | 
					
						
							| 
									
										
										
										
											2019-04-25 03:25:03 +03:00
										 |  |  | 	renderFinalize: function(items, context){ | 
					
						
							| 
									
										
										
										
											2019-04-24 17:01:57 +03:00
										 |  |  | 		var d = this.renderList(items, context) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		// wrap the list (nested list) of nodes in a div...
 | 
					
						
							|  |  |  | 		if(d instanceof Array){ | 
					
						
							|  |  |  | 			var c = document.createElement('div') | 
					
						
							|  |  |  | 			d.forEach(function(e){ | 
					
						
							|  |  |  | 				c.appendChild(e) }) | 
					
						
							|  |  |  | 			d = c | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		this.dom = d | 
					
						
							|  |  |  | 		return this.dom | 
					
						
							|  |  |  | 	}, | 
					
						
							|  |  |  | 	//
 | 
					
						
							| 
									
										
										
										
											2019-02-12 02:34:11 +03:00
										 |  |  | 	// Foramt:
 | 
					
						
							|  |  |  | 	// 	<div class="browse-widget" tabindex="0">
 | 
					
						
							|  |  |  | 	// 		<!-- header -->
 | 
					
						
							|  |  |  | 	// 		...
 | 
					
						
							|  |  |  | 	//
 | 
					
						
							|  |  |  | 	// 		<!-- list -->
 | 
					
						
							|  |  |  | 	// 		<div class="list v-block">
 | 
					
						
							|  |  |  | 	// 			<!-- items -->
 | 
					
						
							|  |  |  | 	// 			...
 | 
					
						
							|  |  |  | 	// 		</div>
 | 
					
						
							|  |  |  | 	// 	</div>
 | 
					
						
							|  |  |  | 	//
 | 
					
						
							| 
									
										
										
										
											2019-02-07 01:30:30 +03:00
										 |  |  | 	// XXX instrument interactions...
 | 
					
						
							| 
									
										
										
										
											2019-02-12 05:19:57 +03:00
										 |  |  | 	// XXX register event handlers...
 | 
					
						
							| 
									
										
										
										
											2019-02-11 22:06:36 +03:00
										 |  |  | 	renderList: function(items, context){ | 
					
						
							| 
									
										
										
										
											2019-02-05 22:12:33 +03:00
										 |  |  | 		var that = this | 
					
						
							| 
									
										
										
										
											2019-02-11 22:06:36 +03:00
										 |  |  | 		var options = context.options || this.options | 
					
						
							| 
									
										
										
										
											2019-02-05 22:12:33 +03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-02-08 21:21:36 +03:00
										 |  |  | 		// dialog (container)...
 | 
					
						
							| 
									
										
										
										
											2019-02-07 01:30:30 +03:00
										 |  |  | 		var dialog = document.createElement('div') | 
					
						
							|  |  |  | 		dialog.classList.add('browse-widget') | 
					
						
							| 
									
										
										
										
											2019-02-09 01:39:13 +03:00
										 |  |  | 		dialog.setAttribute('tabindex', '0') | 
					
						
							| 
									
										
										
										
											2019-02-05 22:12:33 +03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-02-07 01:30:30 +03:00
										 |  |  | 		// header...
 | 
					
						
							|  |  |  | 		options.hideListHeader | 
					
						
							| 
									
										
										
										
											2019-02-11 22:06:36 +03:00
										 |  |  | 			|| dialog.appendChild(this.renderListHeader(context)) | 
					
						
							| 
									
										
										
										
											2019-02-07 01:30:30 +03:00
										 |  |  | 
 | 
					
						
							|  |  |  | 		// list...
 | 
					
						
							|  |  |  | 		var list = document.createElement('div') | 
					
						
							|  |  |  | 		list.classList.add('list', 'v-block') | 
					
						
							| 
									
										
										
										
											2019-02-05 22:12:33 +03:00
										 |  |  | 		items | 
					
						
							|  |  |  | 			.forEach(function(item){ | 
					
						
							| 
									
										
										
										
											2019-02-07 01:30:30 +03:00
										 |  |  | 				list.appendChild(item instanceof Array ?  | 
					
						
							| 
									
										
										
										
											2019-02-05 22:12:33 +03:00
										 |  |  | 					that.renderGroup(item)  | 
					
						
							|  |  |  | 					: item) }) | 
					
						
							| 
									
										
										
										
											2019-02-07 01:30:30 +03:00
										 |  |  | 		dialog.appendChild(list) | 
					
						
							| 
									
										
										
										
											2019-02-05 22:12:33 +03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-02-12 05:19:57 +03:00
										 |  |  | 		// XXX event handlers...
 | 
					
						
							|  |  |  | 		// XXX
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-02-07 01:30:30 +03:00
										 |  |  | 		return dialog  | 
					
						
							|  |  |  | 	}, | 
					
						
							| 
									
										
										
										
											2019-02-12 02:34:11 +03:00
										 |  |  | 	//
 | 
					
						
							|  |  |  | 	// Foramt:
 | 
					
						
							|  |  |  | 	//	<div class="path v-block">
 | 
					
						
							|  |  |  | 	//		<div class="dir" tabindex="0">dir</div>
 | 
					
						
							|  |  |  | 	//		...
 | 
					
						
							|  |  |  | 	//		<div class="dir cur" tabindex="0">dir</div>
 | 
					
						
							|  |  |  | 	//	</div>
 | 
					
						
							|  |  |  | 	// 	
 | 
					
						
							| 
									
										
										
										
											2019-02-07 01:30:30 +03:00
										 |  |  | 	// XXX populate this...
 | 
					
						
							| 
									
										
										
										
											2019-02-09 03:42:34 +03:00
										 |  |  | 	// XXX make this an item???
 | 
					
						
							| 
									
										
										
										
											2019-02-11 22:06:36 +03:00
										 |  |  | 	renderListHeader: function(context){ | 
					
						
							| 
									
										
										
										
											2019-02-07 01:30:30 +03:00
										 |  |  | 		var header = document.createElement('div') | 
					
						
							|  |  |  | 		header.classList.add('path', 'v-block') | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-02-08 21:21:36 +03:00
										 |  |  | 		// XXX path/search...
 | 
					
						
							| 
									
										
										
										
											2019-02-08 13:04:08 +03:00
										 |  |  | 		var dir = document.createElement('div') | 
					
						
							|  |  |  | 		dir.classList.add('dir', 'cur') | 
					
						
							| 
									
										
										
										
											2019-02-09 01:39:13 +03:00
										 |  |  | 		dir.setAttribute('tabindex', '0') | 
					
						
							| 
									
										
										
										
											2019-02-08 13:04:08 +03:00
										 |  |  | 		header.appendChild(dir) | 
					
						
							| 
									
										
										
										
											2019-02-07 01:30:30 +03:00
										 |  |  | 
 | 
					
						
							|  |  |  | 		return header | 
					
						
							|  |  |  | 	}, | 
					
						
							| 
									
										
										
										
											2019-02-12 02:34:11 +03:00
										 |  |  | 	//
 | 
					
						
							|  |  |  | 	// Format:
 | 
					
						
							|  |  |  | 	// 	<div class="list">
 | 
					
						
							|  |  |  | 	// 		<!-- header (optional) -->
 | 
					
						
							|  |  |  | 	// 		...
 | 
					
						
							|  |  |  | 	//
 | 
					
						
							| 
									
										
										
										
											2019-05-04 04:46:29 +03:00
										 |  |  | 	// 		<!-- children (optional) -->
 | 
					
						
							| 
									
										
										
										
											2019-02-12 02:34:11 +03:00
										 |  |  | 	// 		...
 | 
					
						
							|  |  |  | 	// 	</div>
 | 
					
						
							|  |  |  | 	//
 | 
					
						
							| 
									
										
										
										
											2019-02-12 05:19:57 +03:00
										 |  |  | 	// XXX register event handlers...
 | 
					
						
							| 
									
										
										
										
											2019-05-04 04:46:29 +03:00
										 |  |  | 	renderNested: function(header, children, item, context){ | 
					
						
							| 
									
										
										
										
											2019-02-10 03:02:08 +03:00
										 |  |  | 		var that = this | 
					
						
							| 
									
										
										
										
											2019-02-11 22:06:36 +03:00
										 |  |  | 		var options = context.options || this.options | 
					
						
							| 
									
										
										
										
											2019-02-10 03:02:08 +03:00
										 |  |  | 
 | 
					
						
							|  |  |  | 		// container...
 | 
					
						
							| 
									
										
										
										
											2019-02-05 22:12:33 +03:00
										 |  |  | 		var e = document.createElement('div') | 
					
						
							|  |  |  | 		e.classList.add('list') | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-02-10 03:02:08 +03:00
										 |  |  | 		// localize events...
 | 
					
						
							| 
									
										
										
										
											2019-02-10 17:44:42 +03:00
										 |  |  | 		var stopPropagation = function(evt){ evt.stopPropagation() } | 
					
						
							| 
									
										
										
										
											2019-02-11 22:06:36 +03:00
										 |  |  | 		;(options.localEvents || []) | 
					
						
							| 
									
										
										
										
											2019-02-10 03:02:08 +03:00
										 |  |  | 			.forEach(function(evt){ | 
					
						
							| 
									
										
										
										
											2019-02-10 17:44:42 +03:00
										 |  |  | 				e.addEventListener(evt, stopPropagation) }) | 
					
						
							| 
									
										
										
										
											2019-02-10 03:02:08 +03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-02-05 22:12:33 +03:00
										 |  |  | 		// header...
 | 
					
						
							| 
									
										
										
										
											2019-02-13 16:11:14 +03:00
										 |  |  | 		header | 
					
						
							|  |  |  | 			&& e.appendChild(header) | 
					
						
							| 
									
										
										
										
											2019-02-05 22:12:33 +03:00
										 |  |  | 
 | 
					
						
							|  |  |  | 		// items...
 | 
					
						
							| 
									
										
										
										
											2019-05-04 04:46:29 +03:00
										 |  |  | 		children instanceof Node ? | 
					
						
							|  |  |  | 			e.appendChild(children) | 
					
						
							| 
									
										
										
										
											2019-02-12 02:34:11 +03:00
										 |  |  | 		// XXX should this add the items to a container???
 | 
					
						
							| 
									
										
										
										
											2019-05-04 04:46:29 +03:00
										 |  |  | 		: children instanceof Array ? | 
					
						
							|  |  |  | 			children | 
					
						
							| 
									
										
										
										
											2019-02-05 22:12:33 +03:00
										 |  |  | 				.forEach(function(item){ | 
					
						
							|  |  |  | 					e.appendChild(item) }) | 
					
						
							| 
									
										
										
										
											2019-02-10 03:02:08 +03:00
										 |  |  | 		: null | 
					
						
							| 
									
										
										
										
											2019-02-05 22:12:33 +03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-02-12 05:19:57 +03:00
										 |  |  | 		// XXX event handlers... (???)
 | 
					
						
							|  |  |  | 		// XXX
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-02-05 22:12:33 +03:00
										 |  |  | 		item.dom = e | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		return e | 
					
						
							|  |  |  | 	}, | 
					
						
							| 
									
										
										
										
											2019-02-13 16:11:14 +03:00
										 |  |  | 	// NOTE: this is the similar to .renderItem(..)
 | 
					
						
							|  |  |  | 	// XXX make collapse action overloadable....
 | 
					
						
							|  |  |  | 	renderNestedHeader: function(item, i, context){ | 
					
						
							|  |  |  | 		var that = this | 
					
						
							|  |  |  | 		return this.renderItem(item, i, context) | 
					
						
							|  |  |  | 			// update dom...
 | 
					
						
							|  |  |  | 			.run(function(){ | 
					
						
							|  |  |  | 				// class...
 | 
					
						
							|  |  |  | 				// XXX should be done here or in the config???
 | 
					
						
							| 
									
										
										
										
											2019-03-28 02:16:33 +03:00
										 |  |  | 				this.classList.add('sub-list-header', 'traversable') | 
					
						
							| 
									
										
										
										
											2019-02-13 16:11:14 +03:00
										 |  |  | 				item.collapsed | 
					
						
							|  |  |  | 					&& this.classList.add('collapsed') | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 				// collapse action handler...
 | 
					
						
							|  |  |  | 				// XXX make this overloadable...
 | 
					
						
							|  |  |  | 				$(this).on('open', function(evt){ | 
					
						
							|  |  |  | 					item.collapsed = !item.collapsed | 
					
						
							|  |  |  | 					that.render(context) | 
					
						
							|  |  |  | 				}) | 
					
						
							|  |  |  | 			}) }, | 
					
						
							| 
									
										
										
										
											2019-02-12 02:34:11 +03:00
										 |  |  | 	//
 | 
					
						
							|  |  |  | 	// Format:
 | 
					
						
							|  |  |  | 	// 	<div class="group">
 | 
					
						
							|  |  |  | 	// 		..
 | 
					
						
							|  |  |  | 	// 	</div>
 | 
					
						
							|  |  |  | 	//
 | 
					
						
							| 
									
										
										
										
											2019-02-05 22:12:33 +03:00
										 |  |  | 	// XXX this does not seem to get called by .render(..)...
 | 
					
						
							| 
									
										
										
										
											2019-02-11 22:06:36 +03:00
										 |  |  | 	renderGroup: function(items, context){ | 
					
						
							| 
									
										
										
										
											2019-02-05 22:12:33 +03:00
										 |  |  | 		var e = document.createElement('div') | 
					
						
							|  |  |  | 		e.classList.add('group') | 
					
						
							|  |  |  | 		items | 
					
						
							|  |  |  | 			// XXX is this wrong???
 | 
					
						
							|  |  |  | 			.flat(Infinity) | 
					
						
							|  |  |  | 			.forEach(function(item){ | 
					
						
							|  |  |  | 				e.appendChild(item) }) | 
					
						
							|  |  |  | 		return e }, | 
					
						
							| 
									
										
										
										
											2019-02-12 02:34:11 +03:00
										 |  |  | 	//
 | 
					
						
							|  |  |  | 	// Format:
 | 
					
						
							|  |  |  | 	// 	<div value="value_json" class="item .." tabindex="0" ..>
 | 
					
						
							|  |  |  | 	// 		<!-- value -->
 | 
					
						
							|  |  |  | 	// 		<div class="text">value_a</div>
 | 
					
						
							|  |  |  | 	// 		<div class="text">value_b</div>
 | 
					
						
							|  |  |  | 	// 		...
 | 
					
						
							|  |  |  | 	//
 | 
					
						
							|  |  |  | 	// 		<!-- buttons (optional) -->
 | 
					
						
							|  |  |  | 	// 		<div class="button">button_a_html</div>
 | 
					
						
							|  |  |  | 	// 		<div class="button">button_b_html</div>
 | 
					
						
							|  |  |  | 	// 		...
 | 
					
						
							|  |  |  | 	// 	</div>
 | 
					
						
							|  |  |  | 	//
 | 
					
						
							| 
									
										
										
										
											2019-02-10 03:02:08 +03:00
										 |  |  | 	// XXX add custom events:
 | 
					
						
							|  |  |  | 	// 		- open
 | 
					
						
							|  |  |  | 	// 		- select
 | 
					
						
							| 
									
										
										
										
											2019-02-11 22:06:36 +03:00
										 |  |  | 	// 		- update
 | 
					
						
							|  |  |  | 	renderItem: function(item, i, context){ | 
					
						
							|  |  |  | 		var options = context.options || this.options | 
					
						
							| 
									
										
										
										
											2019-02-05 22:12:33 +03:00
										 |  |  | 		if(options.hidden && !options.renderHidden){ | 
					
						
							|  |  |  | 			return null | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2019-02-27 03:35:03 +03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-02-27 06:52:10 +03:00
										 |  |  | 		// special-case: item shorthands...
 | 
					
						
							| 
									
										
										
										
											2019-02-27 06:47:41 +03:00
										 |  |  | 		if(item.value in options.elementShorthand){ | 
					
						
							| 
									
										
										
										
											2019-03-12 16:05:51 +03:00
										 |  |  | 			// XXX need to merge and not overwrite -- revise...
 | 
					
						
							|  |  |  | 			Object.assign(item, options.elementShorthand[item.value]) | 
					
						
							| 
									
										
										
										
											2019-02-27 06:47:41 +03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-02-27 06:52:10 +03:00
										 |  |  | 			// NOTE: this is a bit of a cheat, but it saves us from either 
 | 
					
						
							|  |  |  | 			// 		parsing or restricting the format...
 | 
					
						
							| 
									
										
										
										
											2019-03-12 16:05:51 +03:00
										 |  |  | 			var elem = item.dom = $(item.html)[0] | 
					
						
							| 
									
										
										
										
											2019-02-27 06:52:10 +03:00
										 |  |  | 			elem.classList.add( | 
					
						
							| 
									
										
										
										
											2019-03-12 16:05:51 +03:00
										 |  |  | 				...(item['class'] instanceof Array ? | 
					
						
							|  |  |  | 					item['class'] | 
					
						
							|  |  |  | 					: item['class'].split(/\s+/g))) | 
					
						
							| 
									
										
										
										
											2019-02-27 06:47:41 +03:00
										 |  |  | 
 | 
					
						
							|  |  |  | 			return elem  | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2019-02-27 03:35:03 +03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-02-27 06:52:10 +03:00
										 |  |  | 		// Base DOM...
 | 
					
						
							| 
									
										
										
										
											2019-02-08 21:21:36 +03:00
										 |  |  | 		var elem = document.createElement('div') | 
					
						
							| 
									
										
										
										
											2019-03-02 20:38:08 +03:00
										 |  |  | 		var text = this.__value2key__(item.value || item) | 
					
						
							| 
									
										
										
										
											2019-02-27 06:47:41 +03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-02-05 22:12:33 +03:00
										 |  |  | 		// classes...
 | 
					
						
							| 
									
										
										
										
											2019-02-08 21:21:36 +03:00
										 |  |  | 		elem.classList.add(...['item'] | 
					
						
							|  |  |  | 			// user classes...
 | 
					
						
							| 
									
										
										
										
											2019-02-27 06:47:41 +03:00
										 |  |  | 			.concat(item['class'] || item.cls || []) | 
					
						
							| 
									
										
										
										
											2019-02-08 21:21:36 +03:00
										 |  |  | 			// special classes...
 | 
					
						
							|  |  |  | 			.concat([ | 
					
						
							|  |  |  | 				'selected', | 
					
						
							|  |  |  | 				'disabled', | 
					
						
							|  |  |  | 				'hidden', | 
					
						
							|  |  |  | 			].filter(function(cls){  | 
					
						
							| 
									
										
										
										
											2019-02-09 01:39:13 +03:00
										 |  |  | 				return !!item[cls] }))) | 
					
						
							| 
									
										
										
										
											2019-02-05 22:12:33 +03:00
										 |  |  | 
 | 
					
						
							|  |  |  | 		// attrs...
 | 
					
						
							| 
									
										
										
										
											2019-02-12 02:34:11 +03:00
										 |  |  | 		item.disabled | 
					
						
							|  |  |  | 			|| elem.setAttribute('tabindex', '0') | 
					
						
							| 
									
										
										
										
											2019-02-09 01:39:13 +03:00
										 |  |  | 		Object.entries(item.attrs || {}) | 
					
						
							|  |  |  | 			.forEach(function([key, value]){ | 
					
						
							| 
									
										
										
										
											2019-02-08 21:21:36 +03:00
										 |  |  | 				elem.setAttribute(key, value) }) | 
					
						
							| 
									
										
										
										
											2019-02-12 05:19:57 +03:00
										 |  |  | 		elem.setAttribute('value', text) | 
					
						
							| 
									
										
										
										
											2019-02-05 22:12:33 +03:00
										 |  |  | 
 | 
					
						
							|  |  |  | 		// values...
 | 
					
						
							| 
									
										
										
										
											2019-02-27 06:47:41 +03:00
										 |  |  | 		text | 
					
						
							|  |  |  | 			&& (item.value instanceof Array ? item.value : [item.value]) | 
					
						
							|  |  |  | 				// XXX handle $keys and other stuff...
 | 
					
						
							|  |  |  | 				.map(function(v){ | 
					
						
							|  |  |  | 					var value = document.createElement('span') | 
					
						
							|  |  |  | 					value.classList.add('text') | 
					
						
							|  |  |  | 					value.innerHTML = v || item || '' | 
					
						
							|  |  |  | 					elem.appendChild(value) | 
					
						
							|  |  |  | 				}) | 
					
						
							| 
									
										
										
										
											2019-02-05 22:12:33 +03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-02-08 21:21:36 +03:00
										 |  |  | 		// events...
 | 
					
						
							| 
									
										
										
										
											2019-02-12 05:19:57 +03:00
										 |  |  | 		// XXX revise signature...
 | 
					
						
							|  |  |  | 		elem.addEventListener('click',  | 
					
						
							|  |  |  | 			function(){ $(elem).trigger('open', [text, item, elem]) }) | 
					
						
							|  |  |  | 		//elem.addEventListener('tap', function(){ $(elem).trigger('open', [text, item, elem]) })
 | 
					
						
							| 
									
										
										
										
											2019-02-09 01:39:13 +03:00
										 |  |  | 		Object.entries(item.events || {}) | 
					
						
							|  |  |  | 			// shorthand events...
 | 
					
						
							| 
									
										
										
										
											2019-02-08 21:21:36 +03:00
										 |  |  | 			.concat([ | 
					
						
							| 
									
										
										
										
											2019-02-09 01:39:13 +03:00
										 |  |  | 					'click', | 
					
						
							|  |  |  | 				].map(function(evt){  | 
					
						
							|  |  |  | 					return [evt, item[evt]] })) | 
					
						
							| 
									
										
										
										
											2019-02-08 21:21:36 +03:00
										 |  |  | 			// setup the handlers...
 | 
					
						
							| 
									
										
										
										
											2019-02-09 01:39:13 +03:00
										 |  |  | 			.forEach(function([evt, handler]){ | 
					
						
							| 
									
										
										
										
											2019-02-08 21:21:36 +03:00
										 |  |  | 				handler | 
					
						
							| 
									
										
										
										
											2019-02-09 01:39:13 +03:00
										 |  |  | 					&& elem.addEventListener(evt, handler) }) | 
					
						
							| 
									
										
										
										
											2019-02-08 21:21:36 +03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-02-11 22:06:36 +03:00
										 |  |  | 		// buttons...
 | 
					
						
							|  |  |  | 		// XXX migrate the default buttons functionality and button inheritance...
 | 
					
						
							|  |  |  | 		var buttons = (item.buttons || options.itemButtons || []) | 
					
						
							|  |  |  | 			.slice() | 
					
						
							|  |  |  | 			// NOTE: keep the order unsurprising...
 | 
					
						
							|  |  |  | 			.reverse() | 
					
						
							|  |  |  | 		var stopPropagation = function(evt){ evt.stopPropagation() } | 
					
						
							|  |  |  | 		buttons | 
					
						
							|  |  |  | 			.forEach(function([html, handler]){ | 
					
						
							|  |  |  | 				var button = document.createElement('div') | 
					
						
							|  |  |  | 				button.classList.add('button') | 
					
						
							|  |  |  | 				button.innerHTML = html | 
					
						
							|  |  |  | 				if(!item.disabled){ | 
					
						
							|  |  |  | 					button.setAttribute('tabindex', '0') | 
					
						
							|  |  |  | 					;(options.buttonLocalEvents || options.localEvents || []) | 
					
						
							|  |  |  | 						.forEach(function(evt){ | 
					
						
							|  |  |  | 							button.addEventListener(evt, stopPropagation) }) | 
					
						
							|  |  |  | 					handler | 
					
						
							|  |  |  | 						&& button.addEventListener('click', handler) | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 				elem.appendChild(button) | 
					
						
							|  |  |  | 			}) | 
					
						
							| 
									
										
										
										
											2019-02-05 22:12:33 +03:00
										 |  |  | 		 | 
					
						
							| 
									
										
										
										
											2019-02-08 21:21:36 +03:00
										 |  |  | 		item.dom = elem | 
					
						
							| 
									
										
										
										
											2019-02-05 22:12:33 +03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-02-08 21:21:36 +03:00
										 |  |  | 		return elem  | 
					
						
							|  |  |  | 	}, | 
					
						
							| 
									
										
										
										
											2019-01-24 05:56:37 +03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-01-24 06:12:04 +03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-02-12 05:19:57 +03:00
										 |  |  | 	// Custom events...
 | 
					
						
							|  |  |  | 	// XXX do we use jQuery event handling or vanilla?
 | 
					
						
							|  |  |  | 	// 		...feels like jQuery here wins as it provides a far simpler
 | 
					
						
							|  |  |  | 	// 		API + it's a not time critical area...
 | 
					
						
							| 
									
										
										
										
											2019-03-29 03:19:16 +03:00
										 |  |  | 	// 		....another idea is to force the user to use the provided API
 | 
					
						
							|  |  |  | 	// 		by not implementing ANY direct functionality in DOM -- I do
 | 
					
						
							|  |  |  | 	// 		not like this idea at this point as it violates POLS...
 | 
					
						
							| 
									
										
										
										
											2019-03-02 20:38:08 +03:00
										 |  |  | 	//open: function(func){},
 | 
					
						
							| 
									
										
										
										
											2019-02-12 05:19:57 +03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-03-02 20:38:08 +03:00
										 |  |  | 	//filter: function(){},
 | 
					
						
							| 
									
										
										
										
											2019-01-21 05:29:26 +03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-03-02 20:38:08 +03:00
										 |  |  | 	//select: function(){},
 | 
					
						
							|  |  |  | 	//get: function(){},
 | 
					
						
							| 
									
										
										
										
											2019-02-26 04:40:38 +03:00
										 |  |  | 	//focus: function(){},
 | 
					
						
							| 
									
										
										
										
											2019-01-21 05:29:26 +03:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	// Navigation...
 | 
					
						
							|  |  |  | 	//
 | 
					
						
							|  |  |  | 	up: function(){}, | 
					
						
							|  |  |  | 	down: function(){}, | 
					
						
							|  |  |  | 	left: function(){}, | 
					
						
							|  |  |  | 	right: function(){}, | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	next: function(){}, | 
					
						
							|  |  |  | 	prev: function(){}, | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-02-13 16:11:14 +03:00
										 |  |  | 	collapse: function(){}, | 
					
						
							| 
									
										
										
										
											2019-01-21 05:29:26 +03:00
										 |  |  | 	// XXX scroll...
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-01-24 05:56:37 +03:00
										 |  |  | // XXX should this be a Widget too???
 | 
					
						
							| 
									
										
										
										
											2019-01-24 05:32:21 +03:00
										 |  |  | var Browser =  | 
					
						
							|  |  |  | module.Browser =  | 
					
						
							|  |  |  | object.makeConstructor('Browser',  | 
					
						
							|  |  |  | 		BrowserClassPrototype,  | 
					
						
							|  |  |  | 		BrowserPrototype) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-01-21 05:29:26 +03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-01-28 18:10:01 +03:00
										 |  |  | //---------------------------------------------------------------------
 | 
					
						
							|  |  |  | // Text tree renderer...
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | // This is mainly designed for testing.
 | 
					
						
							|  |  |  | //
 | 
					
						
							| 
									
										
										
										
											2019-01-30 05:51:18 +03:00
										 |  |  | // XXX Q: how should the header item and it's sub-list be linked???
 | 
					
						
							| 
									
										
										
										
											2019-01-28 18:10:01 +03:00
										 |  |  | 
 | 
					
						
							|  |  |  | var TextBrowserClassPrototype = { | 
					
						
							|  |  |  | 	__proto__: BaseBrowser, | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | var TextBrowserPrototype = { | 
					
						
							|  |  |  | 	__proto__: BaseBrowser.prototype, | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	options: { | 
					
						
							| 
									
										
										
										
											2019-04-24 17:01:57 +03:00
										 |  |  | 		valueSeparator: ' ', | 
					
						
							| 
									
										
										
										
											2019-01-28 18:10:01 +03:00
										 |  |  | 		renderIndent: '\t', | 
					
						
							|  |  |  | 	}, | 
					
						
							|  |  |  | 	 | 
					
						
							| 
									
										
										
										
											2019-01-29 04:35:39 +03:00
										 |  |  | 	// NOTE: we do not need .renderGroup(..) here as a group is not 
 | 
					
						
							|  |  |  | 	// 		visible in text...
 | 
					
						
							| 
									
										
										
										
											2019-01-28 18:10:01 +03:00
										 |  |  | 	renderList: function(items, options){ | 
					
						
							|  |  |  | 		var that = this | 
					
						
							| 
									
										
										
										
											2019-02-10 03:02:08 +03:00
										 |  |  | 		return this.renderNested(null, items, null, null, options) | 
					
						
							| 
									
										
										
										
											2019-01-28 18:10:01 +03:00
										 |  |  | 			.join('\n') }, | 
					
						
							| 
									
										
										
										
											2019-01-30 06:04:55 +03:00
										 |  |  | 	renderItem: function(item, i, options){ | 
					
						
							|  |  |  | 		var value = item.value || item | 
					
						
							| 
									
										
										
										
											2019-04-24 17:01:57 +03:00
										 |  |  | 		value = value instanceof Array ?  | 
					
						
							|  |  |  | 			value.join(this.options.valueSeparator || ' ') | 
					
						
							|  |  |  | 			: value | 
					
						
							| 
									
										
										
										
											2019-01-30 06:04:55 +03:00
										 |  |  | 		return item.current ? | 
					
						
							|  |  |  | 			`[ ${value} ]` | 
					
						
							|  |  |  |    			: value }, | 
					
						
							| 
									
										
										
										
											2019-05-04 04:46:29 +03:00
										 |  |  | 	renderNested: function(header, children, context, item, options){ | 
					
						
							| 
									
										
										
										
											2019-01-28 18:10:01 +03:00
										 |  |  | 		var that = this | 
					
						
							| 
									
										
										
										
											2019-05-04 04:46:29 +03:00
										 |  |  | 		var nested = children  | 
					
						
							|  |  |  | 			&& children | 
					
						
							| 
									
										
										
										
											2019-02-01 06:08:48 +03:00
										 |  |  | 				.flat() | 
					
						
							|  |  |  | 				.map(function(e){ | 
					
						
							|  |  |  | 					return e instanceof Array ? | 
					
						
							|  |  |  | 						e.map(function(e){  | 
					
						
							|  |  |  | 							return (that.options.renderIndent || '  ') + e }) | 
					
						
							|  |  |  | 						: e }) | 
					
						
							|  |  |  | 				.flat()  | 
					
						
							|  |  |  | 		return ( | 
					
						
							|  |  |  | 			// expanded...
 | 
					
						
							|  |  |  | 			header && nested ? | 
					
						
							|  |  |  | 				[ | 
					
						
							| 
									
										
										
										
											2019-02-23 22:06:42 +03:00
										 |  |  | 					'- ' + header, | 
					
						
							| 
									
										
										
										
											2019-02-01 06:08:48 +03:00
										 |  |  | 					nested, | 
					
						
							|  |  |  | 				] | 
					
						
							|  |  |  | 			// collapsed...
 | 
					
						
							|  |  |  | 			: header ? | 
					
						
							| 
									
										
										
										
											2019-02-23 22:06:42 +03:00
										 |  |  | 				[ '+ ' + header ] | 
					
						
							| 
									
										
										
										
											2019-02-01 06:08:48 +03:00
										 |  |  | 			// headerless...
 | 
					
						
							|  |  |  | 			: nested )}, | 
					
						
							| 
									
										
										
										
											2019-01-28 18:10:01 +03:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | var TextBrowser =  | 
					
						
							|  |  |  | module.TextBrowser =  | 
					
						
							|  |  |  | object.makeConstructor('TextBrowser',  | 
					
						
							|  |  |  | 		TextBrowserClassPrototype,  | 
					
						
							|  |  |  | 		TextBrowserPrototype) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-01-21 05:29:26 +03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-04-24 16:15:41 +03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-12-20 04:39:03 +03:00
										 |  |  | /********************************************************************** | 
					
						
							|  |  |  | * vim:set ts=4 sw=4 :                               */ return module }) |