| 
									
										
										
										
											2022-04-07 17:23:36 +03:00
										 |  |  | /********************************************************************** | 
					
						
							|  |  |  | *  | 
					
						
							|  |  |  | * object.js | 
					
						
							|  |  |  | * | 
					
						
							|  |  |  | * This is a set of tools and abstractions to create and manage  | 
					
						
							|  |  |  | * constructors, objects and prototype chains in idiomatic JavaScript. | 
					
						
							|  |  |  | * | 
					
						
							|  |  |  | * Motivation: | 
					
						
							|  |  |  | * 	This package was originally written to unify low level object  | 
					
						
							|  |  |  | * 	definitios within a large project and from there evolved to be a  | 
					
						
							|  |  |  | * 	full functional alternative to the ES6 class notation with all of  | 
					
						
							|  |  |  | * 	its inconsistencies, hoops, "the same but slightly different" ways  | 
					
						
							|  |  |  | * 	to do things and "magic" (hidden) functionality. | 
					
						
							|  |  |  | * | 
					
						
							|  |  |  | * Repo and docs: | 
					
						
							|  |  |  | * 	https://github.com/flynx/object.js
 | 
					
						
							|  |  |  | * | 
					
						
							|  |  |  | * | 
					
						
							|  |  |  | ***********************************************/ /* c8 ignore next 2 */ | 
					
						
							|  |  |  | ((typeof define)[0]=='u'?function(f){module.exports=f(require)}:define) | 
					
						
							|  |  |  | (function(require){ var module={} // make module AMD/node compatible...
 | 
					
						
							|  |  |  | /*********************************************************************/ | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-08-03 01:57:57 +03:00
										 |  |  | var doc = require('ig-doc') | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | module.doc = doc.doc | 
					
						
							|  |  |  | module.text = doc.text | 
					
						
							|  |  |  | module.normalizeIndent = doc.normalizeIndent | 
					
						
							|  |  |  | module.normalizeTextIndent = doc.normalizeTextIndent | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | var STOP = | 
					
						
							|  |  |  | module.STOP =  | 
					
						
							|  |  |  | 	require('ig-stoppable').STOP | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | //---------------------------------------------------------------------
 | 
					
						
							| 
									
										
										
										
											2022-04-07 17:23:36 +03:00
										 |  |  | 
 | 
					
						
							|  |  |  | // Function methods to link into a constructor producing a callable 
 | 
					
						
							|  |  |  | // defined via .__call__(..)
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | // These are needed to support the expected popular function API in a 
 | 
					
						
							|  |  |  | // callable potentially not related to a function.
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | // see: Constructor(..) for details.
 | 
					
						
							|  |  |  | module.LINK_FUNCTION_METHODS = [ | 
					
						
							|  |  |  | 	'call', | 
					
						
							|  |  |  | 	'apply', | 
					
						
							|  |  |  | 	'bind', | 
					
						
							|  |  |  | ] | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | //---------------------------------------------------------------------
 | 
					
						
							|  |  |  | // Helpers...
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Bootstrapping utility...
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | // Since we can face chicken-egg issues here, this should keep things 
 | 
					
						
							|  |  |  | // both consistent in terms of doc flow and in terms of actual logical 
 | 
					
						
							|  |  |  | // consistency...
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | var BOOTSTRAP =  | 
					
						
							|  |  |  | function(func){ | 
					
						
							| 
									
										
										
										
											2022-08-03 01:57:57 +03:00
										 |  |  | 	var b = BOOTSTRAP.__delayed =  | 
					
						
							|  |  |  | 		BOOTSTRAP.__delayed  | 
					
						
							|  |  |  | 			|| [] | 
					
						
							| 
									
										
										
										
											2022-04-07 17:23:36 +03:00
										 |  |  | 	func ? | 
					
						
							|  |  |  | 		b.push(func) | 
					
						
							|  |  |  | 		: b.map(function(f){ f() }) } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Get keys from prototype chain...
 | 
					
						
							|  |  |  | //
 | 
					
						
							| 
									
										
										
										
											2022-08-03 01:57:57 +03:00
										 |  |  | // 	deepKeys(obj[, all])
 | 
					
						
							|  |  |  | // 	deepKeys(obj, stop[, all])
 | 
					
						
							|  |  |  | // 		-> keys
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | // 	List all keys incuding non-enumerable...
 | 
					
						
							|  |  |  | // 	deepKeys(obj, true)
 | 
					
						
							|  |  |  | // 	deepKeys(obj, stop, true)
 | 
					
						
							| 
									
										
										
										
											2022-04-07 17:23:36 +03:00
										 |  |  | // 		-> keys
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | // NOTE: this is like Object.keys(..) but will get keys for all levels 
 | 
					
						
							|  |  |  | // 		till stop if given...
 | 
					
						
							| 
									
										
										
										
											2022-08-03 01:57:57 +03:00
										 |  |  | // NOTE: by default this lists only enumerable keys...
 | 
					
						
							| 
									
										
										
										
											2022-04-07 17:23:36 +03:00
										 |  |  | var deepKeys = | 
					
						
							|  |  |  | module.deepKeys = | 
					
						
							| 
									
										
										
										
											2022-08-03 01:57:57 +03:00
										 |  |  | function(obj, stop, all){ | 
					
						
							|  |  |  | 	all = arguments[arguments.length-1] | 
					
						
							|  |  |  | 	all = (all === true || all === false) ? | 
					
						
							|  |  |  | 		all | 
					
						
							|  |  |  | 		: false | 
					
						
							|  |  |  | 	stop = (stop === true || stop === false) ?  | 
					
						
							|  |  |  | 		undefined  | 
					
						
							|  |  |  | 		: stop | 
					
						
							| 
									
										
										
										
											2022-04-07 17:23:36 +03:00
										 |  |  | 	var res = [] | 
					
						
							|  |  |  | 	while(obj != null){ | 
					
						
							| 
									
										
										
										
											2022-08-03 01:57:57 +03:00
										 |  |  | 		res.push(Object.keys(all ? | 
					
						
							|  |  |  | 			Object.getOwnPropertyDescriptors(obj) | 
					
						
							|  |  |  | 			: obj)) | 
					
						
							| 
									
										
										
										
											2022-04-07 17:23:36 +03:00
										 |  |  | 		if(obj === stop){ | 
					
						
							|  |  |  | 			break } | 
					
						
							|  |  |  | 		obj = obj.__proto__ } | 
					
						
							|  |  |  | 	return [...(new Set(res.flat()))] } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Match two objects...
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | // 	match(a, b)
 | 
					
						
							|  |  |  | // 		-> bool
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | // This will match objects iff:
 | 
					
						
							|  |  |  | // 	- if they are identical or
 | 
					
						
							|  |  |  | // 	- attr count is the same and,
 | 
					
						
							|  |  |  | // 	- attr names are the same and,
 | 
					
						
							|  |  |  | // 	- attr values are identical.
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | // 	Non-strict match...
 | 
					
						
							|  |  |  | // 	match(a, b, true)
 | 
					
						
							|  |  |  | //
 | 
					
						
							| 
									
										
										
										
											2022-08-03 01:57:57 +03:00
										 |  |  | // This is similar to the default case but uses equality rather than 
 | 
					
						
							|  |  |  | // identity to match values.
 | 
					
						
							| 
									
										
										
										
											2022-04-07 17:23:36 +03:00
										 |  |  | //
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | // NOTE: this will do a shallow test using Object.keys(..) thus .__proto__
 | 
					
						
							|  |  |  | // 		attributes are ignored...
 | 
					
						
							|  |  |  | var match =  | 
					
						
							|  |  |  | module.match = | 
					
						
							|  |  |  | function(base, obj, non_strict){ | 
					
						
							|  |  |  | 	// identity...
 | 
					
						
							|  |  |  | 	if(base === obj){ | 
					
						
							|  |  |  | 		return true } | 
					
						
							|  |  |  | 	// typeof -- sanity check...
 | 
					
						
							|  |  |  | 	if(typeof(base) != typeof(obj)){ | 
					
						
							|  |  |  | 		return false } | 
					
						
							|  |  |  | 	// attr count...
 | 
					
						
							|  |  |  | 	var o = Object.keys(Object.getOwnPropertyDescriptors(obj)) | 
					
						
							| 
									
										
										
										
											2022-08-03 01:57:57 +03:00
										 |  |  | 	if(Object.keys(Object.getOwnPropertyDescriptors(base)).length  | 
					
						
							|  |  |  | 			!= o.length){ | 
					
						
							| 
									
										
										
										
											2022-04-07 17:23:36 +03:00
										 |  |  | 		return false } | 
					
						
							|  |  |  | 	// names and values...
 | 
					
						
							|  |  |  | 	o = o.map(function(k){ | 
					
						
							|  |  |  | 			return [k, obj[k]] }) | 
					
						
							|  |  |  | 	while(o.length > 0){ | 
					
						
							|  |  |  | 		var [k, v] = o.pop() | 
					
						
							|  |  |  | 		if(!base.hasOwnProperty(k)  | 
					
						
							|  |  |  | 				|| (non_strict ?  | 
					
						
							|  |  |  | 					base[k] != v  | 
					
						
							|  |  |  | 					: base[k] !== v)){ | 
					
						
							|  |  |  | 			return false } } | 
					
						
							|  |  |  | 	return true } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Like .match(..) but will test if obj is a non-strict subset of base...
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | // NOTE: this will only check direct attributes of both base and obj.
 | 
					
						
							|  |  |  | var matchPartial = | 
					
						
							|  |  |  | module.matchPartial =  | 
					
						
							|  |  |  | function(base, obj, non_strict){ | 
					
						
							|  |  |  | 	return base === obj  | 
					
						
							|  |  |  | 		|| Object.entries(obj) | 
					
						
							|  |  |  | 			.filter(function([n, v]){ | 
					
						
							|  |  |  | 				return !base.hasOwnProperty(n)  | 
					
						
							|  |  |  | 					|| (non_strict ? | 
					
						
							|  |  |  | 						base[n] != v | 
					
						
							|  |  |  | 						: base[n] !== v) }) | 
					
						
							|  |  |  | 			.length == 0 } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // like Object.create(..) but also handles callable objects correctly...
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | // 	create(obj)
 | 
					
						
							|  |  |  | // 		-> obj
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | // 	create(func)
 | 
					
						
							|  |  |  | // 	create(name, func)
 | 
					
						
							|  |  |  | // 		-> func
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | // XXX would be nice to re-use RawInstance... or use create(..) from 
 | 
					
						
							|  |  |  | // 		RawInstance...
 | 
					
						
							|  |  |  | // XXX should we autogenerate a descriptive name???
 | 
					
						
							|  |  |  | // XXX revise .toString(..) creation...
 | 
					
						
							|  |  |  | var create = | 
					
						
							|  |  |  | module.create = | 
					
						
							|  |  |  | function(obj){ | 
					
						
							|  |  |  | 	// name given...
 | 
					
						
							|  |  |  | 	var name = '' | 
					
						
							| 
									
										
										
										
											2022-08-03 01:57:57 +03:00
										 |  |  | 	if(typeof(obj) == 'string'  | 
					
						
							|  |  |  | 			&& arguments.length > 1){ | 
					
						
							| 
									
										
										
										
											2022-04-07 17:23:36 +03:00
										 |  |  | 		;[name, obj] = arguments  | 
					
						
							|  |  |  | 		// sanity check...
 | 
					
						
							|  |  |  | 		if(!/^[a-zA-Z_][a-zA-Z0-9_]*$/.test(name.trim())){ | 
					
						
							|  |  |  | 			throw new Error(`create(..): invalid name: "${name}"`) } } | 
					
						
							|  |  |  | 	// calable...
 | 
					
						
							|  |  |  | 	if(typeof(obj) == 'function'){ | 
					
						
							| 
									
										
										
										
											2022-08-03 01:57:57 +03:00
										 |  |  | 		// NOTE: this is ignored by c8 as we will never run this directly 
 | 
					
						
							|  |  |  | 		// 		as it will immediately get eval(..)'ed...
 | 
					
						
							| 
									
										
										
										
											2022-04-07 17:23:36 +03:00
										 |  |  |         /* c8 ignore next 9 */ | 
					
						
							|  |  |  | 		var func = function(){ | 
					
						
							|  |  |  | 			return '__call__' in func ? | 
					
						
							|  |  |  | 					func.__call__(this, ...arguments) | 
					
						
							|  |  |  | 				: 'call' in obj ? | 
					
						
							|  |  |  | 					obj.call(func, ...arguments) | 
					
						
							|  |  |  | 				// NOTE: if obj does not inherit from Function .call(..)
 | 
					
						
							|  |  |  | 				// 		might not be available directly so it is saver to
 | 
					
						
							|  |  |  | 				// 		use Reflect.apply(..)...
 | 
					
						
							|  |  |  | 				: Reflect.apply(obj, func, [...arguments]) } | 
					
						
							|  |  |  | 		// rename...
 | 
					
						
							|  |  |  | 		// NOTE: we just created func(..) so no need to sanitize it, the 
 | 
					
						
							|  |  |  | 		// 		only potential vector of atack (AFAIK) here is name and 
 | 
					
						
							|  |  |  | 		// 		that is checked above...
 | 
					
						
							| 
									
										
										
										
											2022-08-03 01:57:57 +03:00
										 |  |  | 		Object.defineProperty(func, 'name', {value: name}) | 
					
						
							|  |  |  | 		/* XXX NAME... | 
					
						
							|  |  |  | 		//func.name = name
 | 
					
						
							| 
									
										
										
										
											2022-04-07 17:23:36 +03:00
										 |  |  | 		func.name != name | 
					
						
							|  |  |  | 			&& (func = eval('('+  | 
					
						
							|  |  |  | 				func | 
					
						
							|  |  |  | 					.toString() | 
					
						
							|  |  |  | 					.replace(/function\(/, `function ${name}(`) +')')) | 
					
						
							| 
									
										
										
										
											2022-08-03 01:57:57 +03:00
										 |  |  | 		//*/
 | 
					
						
							| 
									
										
										
										
											2022-04-07 17:23:36 +03:00
										 |  |  | 		func.__proto__ = obj | 
					
						
							|  |  |  | 		__toStringProxy(func) | 
					
						
							|  |  |  | 		return func } | 
					
						
							|  |  |  | 	// normal object...
 | 
					
						
							|  |  |  | 	return Object.create(obj) } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | //---------------------------------------------------------------------
 | 
					
						
							|  |  |  | // Helper objects/constructors...
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | BOOTSTRAP(function(){ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Error with some JS quirks fixed...
 | 
					
						
							|  |  |  | 	//
 | 
					
						
							|  |  |  | 	// XXX EXPERIMENTAL
 | 
					
						
							|  |  |  | 	module.Error = | 
					
						
							| 
									
										
										
										
											2022-08-03 01:57:57 +03:00
										 |  |  | 		Constructor('Error', Error, { | 
					
						
							|  |  |  | 			get name(){ | 
					
						
							|  |  |  | 				return this.constructor.name }, | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			// XXX BUG? is this an error that with this everything seems
 | 
					
						
							|  |  |  | 			// 		to work while without this instances of this work
 | 
					
						
							|  |  |  | 			// 		fine while instances of "sub-classes" do not set 
 | 
					
						
							|  |  |  | 			// 		the .stack correctly???
 | 
					
						
							|  |  |  | 			// 		...is this a JS quirk or am I missing something???
 | 
					
						
							|  |  |  | 			__new__: function(context, ...args){ | 
					
						
							|  |  |  | 				return Reflect.construct( | 
					
						
							|  |  |  | 					module.Error.__proto__, args, this.constructor) }, | 
					
						
							|  |  |  | 				//return Reflect.construct(Error, args, this.constructor) },
 | 
					
						
							|  |  |  | 		}) | 
					
						
							| 
									
										
										
										
											2022-04-07 17:23:36 +03:00
										 |  |  | 
 | 
					
						
							|  |  |  | }) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-08-03 01:57:57 +03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-04-07 17:23:36 +03:00
										 |  |  | //---------------------------------------------------------------------
 | 
					
						
							|  |  |  | // Prototype chain content access...
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-08-03 01:57:57 +03:00
										 |  |  | // Get source objects for a prop/attr name...
 | 
					
						
							| 
									
										
										
										
											2022-04-07 17:23:36 +03:00
										 |  |  | //
 | 
					
						
							|  |  |  | // 	sources(obj, name)
 | 
					
						
							| 
									
										
										
										
											2022-08-03 01:57:57 +03:00
										 |  |  | // 		-> iterator
 | 
					
						
							| 
									
										
										
										
											2022-04-07 17:23:36 +03:00
										 |  |  | //
 | 
					
						
							|  |  |  | // 	Get full chain...
 | 
					
						
							|  |  |  | // 	sources(obj)
 | 
					
						
							| 
									
										
										
										
											2022-08-03 01:57:57 +03:00
										 |  |  | // 		-> iterator
 | 
					
						
							| 
									
										
										
										
											2022-04-07 17:23:36 +03:00
										 |  |  | //
 | 
					
						
							| 
									
										
										
										
											2022-08-03 01:57:57 +03:00
										 |  |  | // 	Get callables or objects defining .__call__ (special-case)
 | 
					
						
							|  |  |  | // 	sources(obj, '__call__')
 | 
					
						
							|  |  |  | // 		-> iterator
 | 
					
						
							| 
									
										
										
										
											2022-04-07 17:23:36 +03:00
										 |  |  | //
 | 
					
						
							|  |  |  | //
 | 
					
						
							| 
									
										
										
										
											2022-08-03 01:57:57 +03:00
										 |  |  | // NOTE: this goes up the prototype chain, not caring about any role (
 | 
					
						
							| 
									
										
										
										
											2022-04-07 17:23:36 +03:00
										 |  |  | // 		instance/class or instance/prototype) bounderies and depends 
 | 
					
						
							|  |  |  | // 		only on the object given as the starting point.
 | 
					
						
							|  |  |  | // 		It is possible to start the search from this, thus checking
 | 
					
						
							|  |  |  | // 		for any overloading in the instance, though this approach is 
 | 
					
						
							|  |  |  | // 		not very reusable....
 | 
					
						
							|  |  |  | // NOTE: this will not trigger any props...
 | 
					
						
							|  |  |  | var sources = | 
					
						
							|  |  |  | module.sources = | 
					
						
							| 
									
										
										
										
											2022-08-03 01:57:57 +03:00
										 |  |  | function*(obj, name=undefined){ | 
					
						
							| 
									
										
										
										
											2022-04-07 17:23:36 +03:00
										 |  |  | 	while(obj != null){ | 
					
						
							|  |  |  | 		if(name === undefined | 
					
						
							| 
									
										
										
										
											2022-08-03 01:57:57 +03:00
										 |  |  | 				|| obj.hasOwnProperty(name) | 
					
						
							|  |  |  | 				|| (name == '__call__'  | 
					
						
							|  |  |  | 					&& typeof(obj) == 'function')){ | 
					
						
							|  |  |  | 			yield obj } | 
					
						
							|  |  |  | 		obj = obj.__proto__ } } | 
					
						
							| 
									
										
										
										
											2022-04-07 17:23:36 +03:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-08-03 01:57:57 +03:00
										 |  |  | // Get object-value/prop pairs set in source objects for a prop/attr name...
 | 
					
						
							|  |  |  | // 	
 | 
					
						
							|  |  |  | // 	entries(obj, name)
 | 
					
						
							|  |  |  | // 		-> iterator
 | 
					
						
							| 
									
										
										
										
											2022-04-07 17:23:36 +03:00
										 |  |  | //
 | 
					
						
							|  |  |  | // 	Get propery descriptors...
 | 
					
						
							| 
									
										
										
										
											2022-08-03 01:57:57 +03:00
										 |  |  | // 	entries(obj, name, true)
 | 
					
						
							|  |  |  | // 		-> iterator
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | // Item format:
 | 
					
						
							|  |  |  | // 	[
 | 
					
						
							|  |  |  | // 		source,
 | 
					
						
							|  |  |  | // 		value,
 | 
					
						
							|  |  |  | // 	]
 | 
					
						
							|  |  |  | //
 | 
					
						
							| 
									
										
										
										
											2022-04-07 17:23:36 +03:00
										 |  |  | //
 | 
					
						
							|  |  |  | // Special case: name is given as '__call__'
 | 
					
						
							|  |  |  | // 		This will return either the value the object if it is callable 
 | 
					
						
							|  |  |  | // 		or the value of .__call__ attribute...
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | //
 | 
					
						
							| 
									
										
										
										
											2022-08-03 01:57:57 +03:00
										 |  |  | // NOTE: for more docs see sources(..)
 | 
					
						
							|  |  |  | var entries = | 
					
						
							|  |  |  | module.entries = | 
					
						
							|  |  |  | function*(obj, name, props=false){ | 
					
						
							|  |  |  | 	for(var obj of sources(obj, name)){ | 
					
						
							|  |  |  | 		yield [ | 
					
						
							|  |  |  | 			obj,  | 
					
						
							|  |  |  | 			props ? | 
					
						
							| 
									
										
										
										
											2022-04-07 17:23:36 +03:00
										 |  |  | 				Object.getOwnPropertyDescriptor(obj, name) | 
					
						
							|  |  |  | 			// handle callable instance...
 | 
					
						
							|  |  |  | 			: !(name in obj)  | 
					
						
							|  |  |  | 					&& name == '__call__'  | 
					
						
							|  |  |  | 					&& typeof(obj) == 'function' ? | 
					
						
							|  |  |  | 				obj | 
					
						
							| 
									
										
										
										
											2022-08-03 01:57:57 +03:00
										 |  |  | 			: obj[name], | 
					
						
							|  |  |  | 		] }} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Get values/props set in source objects for a prop/attr name...
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | // NOTE: this is specialization of entries(..), see that for more info.
 | 
					
						
							|  |  |  | var values = | 
					
						
							|  |  |  | module.values = | 
					
						
							|  |  |  | function*(obj, name, props=false){ | 
					
						
							|  |  |  | 	for(var [_, value] of entries(...arguments)){ | 
					
						
							|  |  |  | 		yield value }} | 
					
						
							| 
									
										
										
										
											2022-04-07 17:23:36 +03:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Find the next parent attribute in the prototype chain.
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | // 	Get parent attribute value...
 | 
					
						
							|  |  |  | // 	parent(proto, name)
 | 
					
						
							|  |  |  | // 		-> value
 | 
					
						
							|  |  |  | // 		-> undefined
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | // 	Get parent callable or .__call__ value (special-case)
 | 
					
						
							|  |  |  | // 	parent(proto, '__call__')
 | 
					
						
							|  |  |  | // 		-> value
 | 
					
						
							|  |  |  | // 		-> undefined
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | // 	Get parent method...
 | 
					
						
							|  |  |  | // 	parent(method, this)
 | 
					
						
							|  |  |  | // 		-> meth
 | 
					
						
							|  |  |  | // 		-> undefined
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | // 	Get parent object...
 | 
					
						
							|  |  |  | // 	parent(this)
 | 
					
						
							|  |  |  | // 		-> parent
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | // 
 | 
					
						
							|  |  |  | // The two forms differ in:
 | 
					
						
							|  |  |  | // 	- in parent(method, ..) a method's .name attr is used for name.
 | 
					
						
							|  |  |  | // 	- in parent(method, ..) the containing prototype is inferred.
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | // NOTE: there are cases where method.name is not set (e.g. anonymous 
 | 
					
						
							|  |  |  | // 		function), so there a name should be passed explicitly...
 | 
					
						
							|  |  |  | // NOTE: when passing a method it is recommended to pass an explicit 
 | 
					
						
							|  |  |  | // 		reference to it relative to the constructor, i.e.:
 | 
					
						
							|  |  |  | // 			Constructor.prototype.method
 | 
					
						
							|  |  |  | // 		this will avoid relative resolution loops, for example: 
 | 
					
						
							|  |  |  | // 			this.method 
 | 
					
						
							|  |  |  | // 		deep in a chain will resolve to the first .method value visible 
 | 
					
						
							|  |  |  | // 		from 'this', i.e. the top most value and not the value visible
 | 
					
						
							|  |  |  | // 		from that particular level...
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | // Example:
 | 
					
						
							|  |  |  | // 		var X = object.Constructor('X', {
 | 
					
						
							|  |  |  | //			__proto__: Y.prototype,
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | //			attr: 123,
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | // 			method: function(){
 | 
					
						
							|  |  |  | // 				// get attribute...
 | 
					
						
							|  |  |  | // 				var a = object.parent(X.prototype, 'attr')
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | // 				// get method...
 | 
					
						
							|  |  |  | // 				var ret = object.parent(X.prototype.method, this)
 | 
					
						
							|  |  |  | // 					.call(this, ...arguments)
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | // 				// ...
 | 
					
						
							|  |  |  | // 			}
 | 
					
						
							|  |  |  | // 		})
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | // NOTE: in the general case this will get the value of the returned 
 | 
					
						
							|  |  |  | // 		property/attribute, the rest of the way passive to props.
 | 
					
						
							|  |  |  | // 		The method case will get the value of every method from 'this' 
 | 
					
						
							|  |  |  | // 		and to the method after the match.
 | 
					
						
							|  |  |  | // NOTE: this is super(..) replacement, usable in any context without 
 | 
					
						
							|  |  |  | // 		restriction -- super(..) is restricted to class methods only...
 | 
					
						
							|  |  |  | // NOTE: contrary to sources(..) in the .__call__ case, this will skip 
 | 
					
						
							|  |  |  | // 		the base callable instance, this will make both the following
 | 
					
						
							|  |  |  | // 		cases identical:
 | 
					
						
							|  |  |  | // 			parent(C.prototype.__call__, obj)
 | 
					
						
							|  |  |  | // 		and:
 | 
					
						
							|  |  |  | // 			parent(C.prototype, '__call__')
 | 
					
						
							|  |  |  | var parent = | 
					
						
							|  |  |  | module.parent = | 
					
						
							|  |  |  | function(proto, name){ | 
					
						
							|  |  |  | 	// special case: get parent...
 | 
					
						
							|  |  |  | 	if(arguments.length == 1){ | 
					
						
							|  |  |  | 		return proto.__proto__ } | 
					
						
							|  |  |  | 	// special case: get method...
 | 
					
						
							|  |  |  | 	if(typeof(name) != typeof('str')){ | 
					
						
							|  |  |  | 		var that = name | 
					
						
							|  |  |  | 		name = proto.name | 
					
						
							|  |  |  | 		// sanity check...
 | 
					
						
							|  |  |  | 		if(name == ''){ | 
					
						
							|  |  |  | 			throw new  Error('parent(..): need a method with non-empty .name') } | 
					
						
							|  |  |  | 		// get first matching source...
 | 
					
						
							| 
									
										
										
										
											2022-08-03 01:57:57 +03:00
										 |  |  | 		var i = 0 | 
					
						
							|  |  |  | 		for(var obj of sources(that, name)){ | 
					
						
							|  |  |  | 			// NOTE: the .hasOwnProperty(..) test is here so as 
 | 
					
						
							|  |  |  | 			// 		to skip the base callable when searching for 
 | 
					
						
							|  |  |  | 			// 		.__call__ that is returned as a special case 
 | 
					
						
							|  |  |  | 			// 		by source(..) and this should have no effect 
 | 
					
						
							|  |  |  | 			// 		or other cases...
 | 
					
						
							|  |  |  | 			// NOTE: this will only skip the root callable...
 | 
					
						
							|  |  |  | 			if((i++ > 0 || obj.hasOwnProperty(name)) | 
					
						
							|  |  |  | 					&& obj[name] === proto){ | 
					
						
							|  |  |  | 				proto = obj | 
					
						
							|  |  |  | 				break }}} | 
					
						
							| 
									
										
										
										
											2022-04-07 17:23:36 +03:00
										 |  |  | 	// get first source...
 | 
					
						
							| 
									
										
										
										
											2022-08-03 01:57:57 +03:00
										 |  |  | 	var res = sources(proto, name) | 
					
						
							|  |  |  | 	res.next() | 
					
						
							|  |  |  | 	res = res.next().value | 
					
						
							| 
									
										
										
										
											2022-04-07 17:23:36 +03:00
										 |  |  | 	return !res ? | 
					
						
							|  |  |  | 			undefined | 
					
						
							|  |  |  | 		:(!(name in res) && typeof(res) == 'function') ? | 
					
						
							|  |  |  | 			res | 
					
						
							|  |  |  | 		: res[name] } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Find the next parent property descriptor in the prototype chain...
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | // 	parentProperty(proto, name)
 | 
					
						
							|  |  |  | // 		-> prop-descriptor
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | // This is like parent(..) but will get a property descriptor...
 | 
					
						
							|  |  |  | var parentProperty = | 
					
						
							|  |  |  | module.parentProperty = | 
					
						
							|  |  |  | function(proto, name){ | 
					
						
							|  |  |  | 	// get second source...
 | 
					
						
							| 
									
										
										
										
											2022-08-03 01:57:57 +03:00
										 |  |  | 	var res = sources(proto, name) | 
					
						
							|  |  |  | 	res.next() | 
					
						
							|  |  |  | 	res = res.next().value | 
					
						
							| 
									
										
										
										
											2022-04-07 17:23:36 +03:00
										 |  |  | 	return res ? | 
					
						
							|  |  |  | 		// get next value...
 | 
					
						
							|  |  |  | 		Object.getOwnPropertyDescriptor(res, name) | 
					
						
							|  |  |  | 		: undefined } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Find the next parent method and call it...
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | // 	parentCall(proto, name, this, ..)
 | 
					
						
							|  |  |  | // 	parentCall(meth, this, ..)
 | 
					
						
							|  |  |  | // 		-> res
 | 
					
						
							|  |  |  | // 		-> undefined
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | // This also gracefully handles the case when no higher level definition 
 | 
					
						
							|  |  |  | // is found, i.e. the corresponding parent(..) call will return undefined
 | 
					
						
							|  |  |  | // or a non-callable.
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | // NOTE: this is just like parent(..) but will call the retrieved method,
 | 
					
						
							|  |  |  | // 		essentially this is a shorthand to:
 | 
					
						
							|  |  |  | // 			parent(proto, name).call(this, ...)
 | 
					
						
							|  |  |  | // 		or:
 | 
					
						
							|  |  |  | // 			parent(method, this).call(this, ...)
 | 
					
						
							|  |  |  | // NOTE: for more docs see parent(..)
 | 
					
						
							|  |  |  | var parentCall = | 
					
						
							|  |  |  | module.parentCall = | 
					
						
							|  |  |  | function(proto, name, that, ...args){ | 
					
						
							|  |  |  | 	var meth = parent(proto, name) | 
					
						
							|  |  |  | 	return typeof(meth) == 'function' ? | 
					
						
							|  |  |  | 		meth.call(...( typeof(name) == typeof('str') ? | 
					
						
							|  |  |  | 			[...arguments].slice(2) | 
					
						
							|  |  |  | 			: [...arguments].slice(1) )) | 
					
						
							|  |  |  | 		: undefined } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Test if child is related to parent...
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | // 	parentOf(parent, child)
 | 
					
						
							|  |  |  | // 		-> bool
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | // NOTE: this is like a instanceof b but within the prototype chain
 | 
					
						
							|  |  |  | var parentOf = | 
					
						
							|  |  |  | module.parentOf = | 
					
						
							|  |  |  | function(parent, child){ | 
					
						
							| 
									
										
										
										
											2022-08-03 01:57:57 +03:00
										 |  |  | 	return new Set([...sources(child)]).has(parent) } | 
					
						
							| 
									
										
										
										
											2022-04-07 17:23:36 +03:00
										 |  |  | 
 | 
					
						
							|  |  |  | // Reverse of parentOf(..)
 | 
					
						
							|  |  |  | var childOf = | 
					
						
							|  |  |  | module.childOf = | 
					
						
							|  |  |  | function(child, parent){ | 
					
						
							|  |  |  | 	return parentOf(parent, child) } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | var related = | 
					
						
							|  |  |  | module.related = | 
					
						
							|  |  |  | function(a, b){ | 
					
						
							|  |  |  | 	return parentOf(a, b)  | 
					
						
							|  |  |  | 		|| parentOf(b, a) } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | //---------------------------------------------------------------------
 | 
					
						
							|  |  |  | // Constructor...
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Create a .toString(..) proxy
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | // This is needed to show the user code instead of the library code that 
 | 
					
						
							|  |  |  | // callas it...
 | 
					
						
							|  |  |  | var __toStringProxy =  | 
					
						
							|  |  |  | //module.__toStringProxy =
 | 
					
						
							|  |  |  | function(func){ | 
					
						
							|  |  |  | 	Object.defineProperty(func, 'toString', { | 
					
						
							|  |  |  | 		value: function toString(...args){ | 
					
						
							|  |  |  | 			var f = ( | 
					
						
							|  |  |  | 				// explicitly defined .toString(..)
 | 
					
						
							|  |  |  | 				this.__proto__.toString !== Function.prototype.toString  | 
					
						
							|  |  |  | 						&& this.__proto__.toString !== Object.prototype.toString ? | 
					
						
							|  |  |  | 					this.__proto__ | 
					
						
							|  |  |  | 				// use .__call__...
 | 
					
						
							|  |  |  | 				: '__call__' in this ? | 
					
						
							|  |  |  | 					this.__call__ | 
					
						
							|  |  |  | 				: this.__proto__) | 
					
						
							| 
									
										
										
										
											2022-08-03 01:57:57 +03:00
										 |  |  | 			return doc.normalizeIndent(f.toString(...args)) }, | 
					
						
							| 
									
										
										
										
											2022-04-07 17:23:36 +03:00
										 |  |  | 		enumerable: false, | 
					
						
							|  |  |  | 	}) | 
					
						
							|  |  |  | 	return func } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Make an uninitialized instance from a constructor...
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | // 	RawInstance(context, constructor, ...)
 | 
					
						
							|  |  |  | // 		-> instance
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | // This will:
 | 
					
						
							|  |  |  | // 	- construct an object
 | 
					
						
							|  |  |  | // 		- if .__new__(..) is defined
 | 
					
						
							|  |  |  | // 			-> call and use its return value
 | 
					
						
							|  |  |  | //		- if prototype is a function or if .__call__(..) is defined
 | 
					
						
							|  |  |  | //			-> use a wrapper function
 | 
					
						
							|  |  |  | //		- if construct.__proto__ has .__rawinstance__(..)
 | 
					
						
							|  |  |  | //			-> use it to create an instance
 | 
					
						
							|  |  |  | //		- if constructor.__proto__ is a constructor
 | 
					
						
							|  |  |  | //			-> use it to create an instance
 | 
					
						
							|  |  |  | //		- else
 | 
					
						
							|  |  |  | //			-> use {}
 | 
					
						
							|  |  |  | // 	- link the object into the prototype chain
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | // This will not call .__init__(..), hence the "uninitialized".
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | // NOTE: "context" is only used when passeding to .__new__(..) if defined, 
 | 
					
						
							|  |  |  | // 		and is ignored otherwise...
 | 
					
						
							|  |  |  | // NOTE: as this is simply an extension to the base JavaScript protocol this
 | 
					
						
							|  |  |  | // 		can be used to construct any object...
 | 
					
						
							|  |  |  | // 		Example:
 | 
					
						
							|  |  |  | // 			// new is optional...
 | 
					
						
							|  |  |  | // 			var l = new RawInstance(null, Array, 'a', 'b', 'c')
 | 
					
						
							|  |  |  | // NOTE: the following are not the same in structure but functionally 
 | 
					
						
							|  |  |  | // 		are identical:
 | 
					
						
							|  |  |  | // 			var C = Constructor('C', function(){ .. })
 | 
					
						
							|  |  |  | // 		and
 | 
					
						
							|  |  |  | // 			var C2 = Constructor('C2', { __call__: function(){ .. } })
 | 
					
						
							|  |  |  | // 		the difference is in C.prototype vs. C2.prototype, the first 
 | 
					
						
							|  |  |  | // 		being a function while the second is an object with a call 
 | 
					
						
							|  |  |  | // 		method...
 | 
					
						
							|  |  |  | // NOTE: essentially this is an extended version of Reflect.construct(..)
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | // XXX need a way to set .name of a callable instance...
 | 
					
						
							|  |  |  | // XXX need to auto-generate .name for callable instances...
 | 
					
						
							|  |  |  | var RawInstance =  | 
					
						
							|  |  |  | module.RawInstance = | 
					
						
							|  |  |  | function(context, constructor, ...args){ | 
					
						
							|  |  |  | 	var obj = | 
					
						
							|  |  |  | 		// prototype defines .__new__(..)...
 | 
					
						
							|  |  |  | 		constructor.prototype.__new__ instanceof Function ? | 
					
						
							|  |  |  | 			constructor.prototype.__new__(context, ...args) | 
					
						
							|  |  |  | 		// native constructor...
 | 
					
						
							|  |  |  | 		: /\[native code\]/.test(constructor.toString()) ? | 
					
						
							|  |  |  | 			Reflect.construct(constructor, args) | 
					
						
							|  |  |  | 		// callable instance...
 | 
					
						
							|  |  |  | 		// NOTE: we need to isolate the callable from instances, thus we
 | 
					
						
							|  |  |  | 		// 		reference 'constructor' directly rather than using 
 | 
					
						
							|  |  |  | 		// 		'this.constructor'...
 | 
					
						
							|  |  |  | 	// XXX autogenerate/set .name ...
 | 
					
						
							|  |  |  | 		: (typeof(constructor.prototype) == 'function' | 
					
						
							|  |  |  | 				|| constructor.prototype.__call__ instanceof Function) ? | 
					
						
							|  |  |  | 			__toStringProxy( | 
					
						
							|  |  |  | 				function(){ | 
					
						
							|  |  |  | 					return ( | 
					
						
							|  |  |  | 						// .prototype is a function...
 | 
					
						
							|  |  |  | 						typeof(constructor.prototype) == 'function' ? | 
					
						
							|  |  |  | 							// NOTE: we are not using .call(..) here as it
 | 
					
						
							|  |  |  | 							// 		may not be accesible through the prototype
 | 
					
						
							|  |  |  | 							// 		chain, this can occur when creating a 
 | 
					
						
							|  |  |  | 							// 		callable instance from a non-callable 
 | 
					
						
							|  |  |  | 							// 		parent...
 | 
					
						
							|  |  |  | 							Reflect.apply( | 
					
						
							|  |  |  | 								constructor.prototype, obj, [this, ...arguments]) | 
					
						
							|  |  |  | 						// .__call__(..) or fail semi-gracefully...
 | 
					
						
							|  |  |  | 						: constructor.prototype.__call__ | 
					
						
							|  |  |  | 							.call(obj, this, ...arguments)) }) | 
					
						
							|  |  |  | 		// recursively call .__rawinstance__(..)
 | 
					
						
							|  |  |  | 		: constructor.__proto__.__rawinstance__ ? | 
					
						
							|  |  |  | 			constructor.__proto__.__rawinstance__(context, ...args) | 
					
						
							|  |  |  | 		// use parent's constructor...
 | 
					
						
							|  |  |  | 		: (typeof(constructor.__proto__) == 'function' | 
					
						
							|  |  |  | 				// XXX for some reason if using (function(){}).__proto__ 
 | 
					
						
							|  |  |  | 				// 		instead of Function.prototype below, coverage is 
 | 
					
						
							|  |  |  | 				// 		not counted for the condition....
 | 
					
						
							|  |  |  | 				&& constructor.__proto__ !== Function.prototype) ? | 
					
						
							|  |  |  | 			Reflect.construct(constructor.__proto__, args, constructor) | 
					
						
							|  |  |  | 		// default object base...
 | 
					
						
							|  |  |  | 		: Reflect.construct(Object, [], constructor) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// link to prototype chain, if not done already...
 | 
					
						
							|  |  |  | 	obj.__proto__ !== constructor.prototype | 
					
						
							|  |  |  | 		&& (obj.__proto__ = constructor.prototype) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return obj } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Make an object constructor function...
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | // 	Make a constructor with an object prototype...
 | 
					
						
							|  |  |  | // 		Constructor(name, proto)
 | 
					
						
							|  |  |  | // 			-> constructor
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | // 	Make a constructor with a prototype and a constructor prototype...
 | 
					
						
							|  |  |  | // 		Constructor(name, constructor-mixin, proto)
 | 
					
						
							|  |  |  | // 			-> constructor
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | // 	Make a constructor with prototype extending parent-constructor...
 | 
					
						
							|  |  |  | // 		Constructor(name, parent-constructor, proto)
 | 
					
						
							|  |  |  | // 		Constructor(name, parent-constructor, constructor-mixin, proto)
 | 
					
						
							|  |  |  | // 			-> constructor
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | // The resulting constructor can produce objects in one of these ways:
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | // 	Create instance...
 | 
					
						
							|  |  |  | // 		constructor(..)
 | 
					
						
							|  |  |  | // 		new constructor
 | 
					
						
							|  |  |  | // 		new constructor(..)
 | 
					
						
							|  |  |  | // 			-> instance
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | //	Create raw/uninitialized instance...
 | 
					
						
							|  |  |  | //		constructor.__rawinstance__(..)
 | 
					
						
							|  |  |  | //		RawInstance(null, constructor, ..)
 | 
					
						
							|  |  |  | //			-> raw-instance
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | // All produced objects are instances of the constructor
 | 
					
						
							|  |  |  | // 		instance instanceof constructor
 | 
					
						
							|  |  |  | // 			-> true
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | // Create and initialization protocol:
 | 
					
						
							|  |  |  | // 	1) raw instance is created:
 | 
					
						
							|  |  |  | // 		a) constructor.__rawinstance__(..) / RawInstance(..) called:
 | 
					
						
							|  |  |  | // 			- call .__new__(..) if defined and get return value as 
 | 
					
						
							|  |  |  | // 				instance, or
 | 
					
						
							|  |  |  | // 			- if .__call__(..) defined or prototype is a function, wrap 
 | 
					
						
							|  |  |  | // 				it and use the wrapper function as instance, or
 | 
					
						
							|  |  |  | // 			- create an empty object
 | 
					
						
							|  |  |  | // 		b) instance linked to prototype chain
 | 
					
						
							|  |  |  | // 			set .__proto__ to constructor.prototype
 | 
					
						
							|  |  |  | // 	2) instance is initialized: 
 | 
					
						
							|  |  |  | // 		call .__init__(..) if defined
 | 
					
						
							|  |  |  | // 
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | // Special attributes:
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | // 	Sets parent constructor
 | 
					
						
							|  |  |  | // 	.__extends__ = constructor
 | 
					
						
							|  |  |  | // 		NOTE: this can be set on either constructor-mixin or proto but 
 | 
					
						
							|  |  |  | // 			not on both...
 | 
					
						
							|  |  |  | // 		NOTE: if .__proto__ is not set in the proto, then it will be 
 | 
					
						
							|  |  |  | // 			set to .__extends__.prototype by default.
 | 
					
						
							|  |  |  | // 		NOTE: setting this and proto.__proto__ to can be used to link the
 | 
					
						
							|  |  |  | // 			constructor and instance object to different prototype chains
 | 
					
						
							|  |  |  | // 		NOTE: this attr is only used if explicitly defined, inherited 
 | 
					
						
							|  |  |  | // 			values are ignored.
 | 
					
						
							|  |  |  | // 		XXX this may get removed in future versions.
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | // 	If true do not link function methods if .__call__(..) is defined
 | 
					
						
							|  |  |  | // 	.__skip_call_attrs__ = bool
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | // Special methods (constructor):
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | //  Handle uninitialized instance construction
 | 
					
						
							|  |  |  | // 	.__rawinstance__(context, ...)
 | 
					
						
							|  |  |  | // 		-> instance
 | 
					
						
							|  |  |  | // 		NOTE: This is a shorthand to RawInstance(..) see it for 
 | 
					
						
							|  |  |  | // 			details.
 | 
					
						
							|  |  |  | // 
 | 
					
						
							|  |  |  | // 
 | 
					
						
							|  |  |  | // Special methods (.prototype):
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | // 	Create new instance object...
 | 
					
						
							|  |  |  | // 	.__new__(context, ..)
 | 
					
						
							|  |  |  | // 		-> object
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | // 	Handle instance call...
 | 
					
						
							|  |  |  | // 	.__call__(context, ..)
 | 
					
						
							|  |  |  | // 		-> ..
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | // 	Initialize instance object...
 | 
					
						
							|  |  |  | // 	.__init__(..)
 | 
					
						
							|  |  |  | // 		-> ..
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | // NOTE: raw instance creation is defined by RawInstance(..) so see 
 | 
					
						
							|  |  |  | // 		it for more info.
 | 
					
						
							|  |  |  | // NOTE: raw instance creation can be completely overloaded by defining
 | 
					
						
							|  |  |  | // 		.__rawinstance__(..) on the constructor.
 | 
					
						
							|  |  |  | // NOTE: if constructor-mixin's .__proto__ is set it will also be copied
 | 
					
						
							|  |  |  | // 		 to the created constructor...
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | // Inheritance:
 | 
					
						
							|  |  |  | // 	A simple way to build C -> B -> A chain would be:
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | // 		// NOTE: new is optional...
 | 
					
						
							|  |  |  | // 		var A = new Constructor('A')
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | // 		var B = Constructor('B', A, {})
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | // 		var C = Constructor('C', B, {})
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | // 		var c = C()
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | // 		c instanceof C		// -> true
 | 
					
						
							|  |  |  | // 		c instanceof B		// -> true
 | 
					
						
							|  |  |  | // 		c instanceof A		// -> true
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | // 		A.prototype.x = 123
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | // 		c.x 				// -> 123
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | // NOTE: this sets the proto's .constructor attribute, thus rendering it
 | 
					
						
							|  |  |  | // 		not reusable, to use the same prototype for multiple objects 
 | 
					
						
							|  |  |  | // 		clone it via. Object.create(..) or copy it...
 | 
					
						
							|  |  |  | // NOTE: to disable .__rawinstance__(..) handling set it to false in the 
 | 
					
						
							|  |  |  | // 		class prototype...
 | 
					
						
							|  |  |  | // NOTE: it is currently not possible to mix native unrelated types, for 
 | 
					
						
							|  |  |  | // 		example a callable array constructor will produce inconsistent 
 | 
					
						
							|  |  |  | // 		instance objects that in general will not work as expected...
 | 
					
						
							|  |  |  | // 			Reflect.construct(Array, [], Function)
 | 
					
						
							|  |  |  | // 		or
 | 
					
						
							|  |  |  | // 			Reflect.construct(Function, [], Array)
 | 
					
						
							|  |  |  | // 		will either initialize internal/hidden state for either one or 
 | 
					
						
							|  |  |  | // 		the other producing a semi-broken instance.
 | 
					
						
							|  |  |  | // 		It is however possible to mix related types as we are doing for 
 | 
					
						
							|  |  |  | // 		callable instances (Function + Object -- a function is an object).
 | 
					
						
							|  |  |  | // 		See README.md for more info.
 | 
					
						
							|  |  |  | // NOTE: making an object callable does not guarantee that it will pass
 | 
					
						
							|  |  |  | // 		the instanceof Function test, for that the prototype chain needs
 | 
					
						
							|  |  |  | // 		to be rooted in Function.
 | 
					
						
							|  |  |  | // 		though the typeof(..) == 'function' will always work.
 | 
					
						
							|  |  |  | // NOTE: this will fail with non-identifier names...
 | 
					
						
							| 
									
										
										
										
											2022-08-03 01:57:57 +03:00
										 |  |  | // NOTE: a bit more obvious syntax could be something like:
 | 
					
						
							|  |  |  | // 			var A = Constructor('A', {
 | 
					
						
							|  |  |  | // 				__proto__: B,
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | // 				// constructor stuff...
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | // 				prototype: {
 | 
					
						
							|  |  |  | // 					// instance stuff...
 | 
					
						
							|  |  |  | // 				}
 | 
					
						
							|  |  |  | // 			})
 | 
					
						
							|  |  |  | // 		vs:
 | 
					
						
							|  |  |  | // 			var A = Constructor('A', B, {
 | 
					
						
							|  |  |  | // 				// constructor stuff...
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | // 			}, {
 | 
					
						
							|  |  |  | // 				// instance stuff...
 | 
					
						
							|  |  |  | // 			})
 | 
					
						
							|  |  |  | // 		this though a bit more obvious is in the general case can be 
 | 
					
						
							|  |  |  | // 		more verbose, i.e. on the top level we define the constructor 
 | 
					
						
							|  |  |  | // 		stuff which in the general case secondary while .prototype is a 
 | 
					
						
							|  |  |  | // 		level below that while in use it's the main functionality... so,
 | 
					
						
							|  |  |  | // 		this is a question of ease of use vs. mirroring the JS structure,
 | 
					
						
							|  |  |  | // 		the answer chosen here is to prioritize simplicity and conciseness
 | 
					
						
							|  |  |  | // 		over verbose mirroring...
 | 
					
						
							|  |  |  | //		XXX needs revision from time to time...
 | 
					
						
							|  |  |  | //		XXX might be a good idea to implement the above syntax and test 
 | 
					
						
							|  |  |  | //			it out...
 | 
					
						
							|  |  |  | //				Constructor(<name>[, <parent>], <mirror-block>)
 | 
					
						
							|  |  |  | //				Constructor(<name>[, <parent>], <prototype-block>)
 | 
					
						
							|  |  |  | //				Constructor(<name>[, <parent>], <prototype-block>, <prototype-block>)
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | //				<mirror-block> ::=
 | 
					
						
							|  |  |  | //					{
 | 
					
						
							|  |  |  | //						...
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | //						prototype: {
 | 
					
						
							|  |  |  | //							...
 | 
					
						
							|  |  |  | //						},
 | 
					
						
							|  |  |  | //					}
 | 
					
						
							|  |  |  | //			The only questions here is weather a set .prototype is a good 
 | 
					
						
							|  |  |  | //			enough indicator and should we use .__proto__?
 | 
					
						
							| 
									
										
										
										
											2022-04-07 17:23:36 +03:00
										 |  |  | var Constructor =  | 
					
						
							|  |  |  | module.Constructor = | 
					
						
							|  |  |  | // shorthand...
 | 
					
						
							|  |  |  | module.C = | 
					
						
							|  |  |  | function Constructor(name, a, b, c){ | 
					
						
							|  |  |  | 	// sanity check...
 | 
					
						
							|  |  |  | 	if(!/^[a-zA-Z_][a-zA-Z0-9_]*$/.test(name.trim())){ | 
					
						
							|  |  |  | 		throw new Error(`Constructor(..): invalid name: "${name}"`) } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// parse args...
 | 
					
						
							| 
									
										
										
										
											2022-08-03 01:57:57 +03:00
										 |  |  | 	var args = [...arguments].slice(1, 4) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-04-07 17:23:36 +03:00
										 |  |  | 	// 	Constructor(name[[, constructor[, mixin]], proto])
 | 
					
						
							|  |  |  | 	var proto = args.pop() || {} | 
					
						
							| 
									
										
										
										
											2022-08-03 01:57:57 +03:00
										 |  |  | 	var constructor_proto =  | 
					
						
							|  |  |  | 		typeof(args[0]) == 'function' ? | 
					
						
							|  |  |  | 			args.shift() | 
					
						
							|  |  |  | 			: undefined | 
					
						
							| 
									
										
										
										
											2022-04-07 17:23:36 +03:00
										 |  |  | 	var constructor_mixin = args.pop() | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-08-03 01:57:57 +03:00
										 |  |  | 	/* XXX EXPERIMENTAL... | 
					
						
							|  |  |  | 	// handle: Constructor(name[, constructor], { prototype: { .. }, .. })
 | 
					
						
							|  |  |  | 	//
 | 
					
						
							|  |  |  | 	if(proto.hasOwnProperty('prototype')  | 
					
						
							|  |  |  | 			&& proto.prototype){ | 
					
						
							|  |  |  | 		console.log('>>>>', name) | 
					
						
							|  |  |  | 		constructor_mixin = proto | 
					
						
							|  |  |  | 		proto = proto.prototype } | 
					
						
							|  |  |  | 	//*/
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// handle: Constructor(name, constructor, ..)
 | 
					
						
							| 
									
										
										
										
											2022-04-07 17:23:36 +03:00
										 |  |  | 	//
 | 
					
						
							|  |  |  | 	// NOTE: this is a bit too functional in style by an if-tree would 
 | 
					
						
							|  |  |  | 	// 		be more bulky and less readable...
 | 
					
						
							|  |  |  | 	constructor_proto | 
					
						
							|  |  |  | 		// XXX need a better test -- need to test if .__proto__ was set 
 | 
					
						
							|  |  |  | 		// 		manually and not mess it up...
 | 
					
						
							|  |  |  | 		&& (proto.__proto__ === Object.prototype | 
					
						
							|  |  |  | 				|| proto.__proto__ === Function.prototype) | 
					
						
							|  |  |  | 			&& (proto.__proto__ = constructor_proto.prototype) | 
					
						
							|  |  |  | 			// restore func .toString(..) that was replaced to object's .toString(..) 
 | 
					
						
							|  |  |  | 			// in the previous op but only if it was not set by user...
 | 
					
						
							|  |  |  | 			&& (typeof(proto) == 'function' | 
					
						
							|  |  |  | 					&& proto.toString === Object.prototype.toString) | 
					
						
							|  |  |  | 				// XXX should we wrap this in normalizeIndent(..) ???
 | 
					
						
							|  |  |  | 				&& (proto.toString = Function.prototype.toString) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// handle: .__extends__
 | 
					
						
							|  |  |  | 	if(!constructor_proto){ | 
					
						
							|  |  |  | 		// handle .__extends__
 | 
					
						
							|  |  |  | 		a = Object.hasOwnProperty.call(proto, '__extends__') | 
					
						
							|  |  |  | 				&& proto.__extends__ | 
					
						
							|  |  |  | 		b = constructor_mixin != null | 
					
						
							|  |  |  | 				&& Object.hasOwnProperty.call(constructor_mixin, '__extends__') | 
					
						
							|  |  |  | 				&& constructor_mixin.__extends__ | 
					
						
							|  |  |  | 		// sanity check...
 | 
					
						
							|  |  |  | 		if(!!a && !!b){ | 
					
						
							|  |  |  | 			throw new Error('Constructor(..): ' | 
					
						
							|  |  |  | 				+'only one  of prototype.__extends__ or constructor.__extends__ ' | 
					
						
							|  |  |  | 				+'can exist.') } | 
					
						
							|  |  |  | 		constructor_proto = !!a ? a : b | 
					
						
							|  |  |  | 		// cleanup...
 | 
					
						
							|  |  |  | 		if(!!b){ | 
					
						
							|  |  |  | 			constructor_mixin = mixinFlat({}, constructor_mixin) | 
					
						
							|  |  |  | 			delete constructor_mixin.__extends__ } | 
					
						
							|  |  |  | 		!!constructor_proto | 
					
						
							|  |  |  | 			&& (proto.__proto__ = constructor_proto.prototype) } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// the constructor base... 
 | 
					
						
							| 
									
										
										
										
											2022-08-03 01:57:57 +03:00
										 |  |  | 	// NOTE: this is ignored by c8 as we will never run this directly as
 | 
					
						
							|  |  |  | 	// 		it will immediately get eval(..)'ed...
 | 
					
						
							| 
									
										
										
										
											2022-04-07 17:23:36 +03:00
										 |  |  | 	/* c8 ignore next 9 */ | 
					
						
							|  |  |  | 	var _constructor = function Constructor(){ | 
					
						
							|  |  |  | 		// create raw instance...
 | 
					
						
							|  |  |  | 		var obj = _constructor.__rawinstance__ ?  | 
					
						
							|  |  |  | 			_constructor.__rawinstance__(this, ...arguments) | 
					
						
							|  |  |  | 			: RawInstance(this, _constructor, ...arguments) | 
					
						
							|  |  |  | 		// initialize...
 | 
					
						
							|  |  |  | 		obj.__init__ instanceof Function | 
					
						
							|  |  |  | 			&& obj.__init__(...arguments) | 
					
						
							|  |  |  | 		return obj } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// constructor naming...
 | 
					
						
							|  |  |  | 	// NOTE: we are not using:
 | 
					
						
							|  |  |  | 	//			Object.defineProperty(_constructor, 'name', { value: name })
 | 
					
						
							|  |  |  | 	//		because this does not affect the name displayed by the Chrome
 | 
					
						
							|  |  |  | 	//		DevTools. FF does not seem to care about either version of code...
 | 
					
						
							| 
									
										
										
										
											2022-08-03 01:57:57 +03:00
										 |  |  | 	Object.defineProperty(_constructor, 'name', {value: name}) | 
					
						
							|  |  |  | 	/* XXX NAME... | 
					
						
							|  |  |  | 	//_constructor.name = name
 | 
					
						
							| 
									
										
										
										
											2022-04-07 17:23:36 +03:00
										 |  |  | 	// just in case the browser/node refuses to change the name, we'll make
 | 
					
						
							|  |  |  | 	// them a different offer ;)
 | 
					
						
							|  |  |  | 	// NOTE: it is not possible to abstract this eval(..) into something 
 | 
					
						
							|  |  |  | 	// 		like renameFunction(..) as reconstructing the function will
 | 
					
						
							|  |  |  | 	// 		lose it's closure that we depend on here...
 | 
					
						
							|  |  |  | 	// NOTE: this eval(..) should not be a risk as its inputs are
 | 
					
						
							|  |  |  | 	// 		static and never infuenced by external inputs...
 | 
					
						
							|  |  |  | 	_constructor.name != name | 
					
						
							|  |  |  | 		&& (_constructor = eval('('+  | 
					
						
							|  |  |  | 			_constructor | 
					
						
							|  |  |  | 				.toString() | 
					
						
							|  |  |  | 				.replace(/Constructor/g, name) +')')) | 
					
						
							| 
									
										
										
										
											2022-08-03 01:57:57 +03:00
										 |  |  | 	//*/
 | 
					
						
							| 
									
										
										
										
											2022-04-07 17:23:36 +03:00
										 |  |  | 	// set .toString(..)...
 | 
					
						
							|  |  |  | 	// NOTE: this test is here to enable mixinFlat(..) to overwrite 
 | 
					
						
							|  |  |  | 	// 		.toString(..) below...
 | 
					
						
							|  |  |  | 	// 		XXX not sure if this is the correct approach...
 | 
					
						
							|  |  |  | 	// 		XXX might be a good idea to create a common base class and 
 | 
					
						
							|  |  |  | 	// 			keep this there...
 | 
					
						
							|  |  |  | 	;((constructor_mixin || {}).toString === Function.prototype.toString | 
					
						
							|  |  |  | 			|| (constructor_mixin || {}).toString === Object.prototype.toString) | 
					
						
							|  |  |  | 		&& Object.defineProperty(_constructor, 'toString', { | 
					
						
							|  |  |  | 			value: function toString(){  | 
					
						
							|  |  |  | 				var args = proto.__init__ ? | 
					
						
							|  |  |  | 					proto.__init__ | 
					
						
							|  |  |  | 						.toString() | 
					
						
							|  |  |  | 						.split(/\n/)[0] | 
					
						
							|  |  |  | 							.replace(/function\(([^)]*)\){.*/, '$1') | 
					
						
							|  |  |  | 					: '' | 
					
						
							|  |  |  | 				var code = proto.__init__ ? | 
					
						
							|  |  |  | 					proto.__init__ | 
					
						
							|  |  |  | 						.toString() | 
					
						
							|  |  |  | 						.replace(/[^{]*{/, '{') | 
					
						
							|  |  |  | 					: '{ .. }' | 
					
						
							| 
									
										
										
										
											2022-08-03 01:57:57 +03:00
										 |  |  | 				return `${this.name}(${args})${doc.normalizeIndent(code)}` }, | 
					
						
							| 
									
										
										
										
											2022-04-07 17:23:36 +03:00
										 |  |  | 			enumerable: false, | 
					
						
							|  |  |  | 		}) | 
					
						
							|  |  |  | 	// set generic raw instance constructor...
 | 
					
						
							|  |  |  | 	_constructor.__rawinstance__ instanceof Function | 
					
						
							|  |  |  | 		|| Object.defineProperty(_constructor, '__rawinstance__', { | 
					
						
							|  |  |  | 				value: function __rawinstance__(context, ...args){ | 
					
						
							|  |  |  | 					return RawInstance(context, this, ...args) }, | 
					
						
							|  |  |  | 				enumerable: false, | 
					
						
							|  |  |  | 			}) | 
					
						
							|  |  |  | 	!!constructor_proto | 
					
						
							|  |  |  | 		&& (_constructor.__proto__ = constructor_proto) | 
					
						
							|  |  |  | 	_constructor.prototype = proto | 
					
						
							|  |  |  | 	_constructor.prototype.constructor = _constructor | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// NOTE: this is intentionally last, this enables the user to override
 | 
					
						
							|  |  |  | 	// 		any of the system methods...
 | 
					
						
							|  |  |  | 	// NOTE: place the non-overridable definitions after this...
 | 
					
						
							|  |  |  | 	!!constructor_mixin | 
					
						
							|  |  |  | 		&& mixinFlat( | 
					
						
							|  |  |  | 			_constructor, | 
					
						
							|  |  |  | 			constructor_mixin) | 
					
						
							|  |  |  | 		// also transfer non-default constructor_mixin.__proto__
 | 
					
						
							|  |  |  | 		&& constructor_mixin.__proto__ !== Object.prototype | 
					
						
							|  |  |  | 			&& (_constructor.__proto__ = constructor_mixin.__proto__) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// link function stuff for convenience...
 | 
					
						
							|  |  |  | 	proto.__call__ && !(proto instanceof Function) | 
					
						
							|  |  |  | 		&& _constructor.__skip_call_attrs__ !== true | 
					
						
							|  |  |  | 		&& module.LINK_FUNCTION_METHODS | 
					
						
							|  |  |  | 			.forEach(function(n){ | 
					
						
							|  |  |  | 				proto[n]  | 
					
						
							|  |  |  | 					|| Object.defineProperty(proto, n,  | 
					
						
							|  |  |  | 						Object.assign( | 
					
						
							|  |  |  | 							Object.getOwnPropertyDescriptor(Function.prototype, n), | 
					
						
							|  |  |  | 							// NOTE: we can't use Function[n] directly because
 | 
					
						
							|  |  |  | 							// 		they in general test this for relation to
 | 
					
						
							|  |  |  | 							// 		function which will fail here...
 | 
					
						
							|  |  |  | 							{ value: function(){ | 
					
						
							|  |  |  | 								return this.__call__[n](this, ...arguments) }, })) }) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return _constructor } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Complete the constructor...
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | // NOTE: currently this is a complement to the top level functions.
 | 
					
						
							|  |  |  | Object.assign(Constructor, { | 
					
						
							|  |  |  | 	sources, | 
					
						
							|  |  |  | 	values, | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	parent, | 
					
						
							|  |  |  | 	parentProperty, | 
					
						
							|  |  |  | 	parentCall, | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	parentOf, | 
					
						
							|  |  |  | 	childOf, | 
					
						
							|  |  |  | 	related, | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	match, | 
					
						
							|  |  |  | 	matchPartial, | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	deepKeys, | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	create, | 
					
						
							|  |  |  | }) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | //---------------------------------------------------------------------
 | 
					
						
							|  |  |  | // Mixin utils...
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Mix a set of methods/props/attrs into an object...
 | 
					
						
							|  |  |  | // 
 | 
					
						
							|  |  |  | //	Mix objects into base...
 | 
					
						
							|  |  |  | //	mixinFlat(base, object, ...)
 | 
					
						
							|  |  |  | //		-> base
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | //	Soft mix objects into base...
 | 
					
						
							|  |  |  | //	mixinFlat('soft', base, object, ...)
 | 
					
						
							|  |  |  | //		-> base
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | // 'soft' mode only mixies in props if base does not define them already
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | // NOTE: essentially this is just like Object.assign(..) but copies 
 | 
					
						
							|  |  |  | // 		properties directly rather than copying property values...
 | 
					
						
							|  |  |  | // NOTE: this will not transfer several the special variables not listed
 | 
					
						
							|  |  |  | // 		by Object.keys(..).
 | 
					
						
							|  |  |  | // 		This includes things like .__proto__
 | 
					
						
							|  |  |  | // NOTE: this can and will overwrite attributes...
 | 
					
						
							|  |  |  | var mixinFlat =  | 
					
						
							|  |  |  | module.mixinFlat =  | 
					
						
							|  |  |  | function(base, ...objects){ | 
					
						
							|  |  |  | 	var soft = base === 'soft' | 
					
						
							|  |  |  | 	if(soft){ | 
					
						
							|  |  |  | 		base = objects.shift()  | 
					
						
							|  |  |  | 		objects = objects | 
					
						
							|  |  |  | 			.slice() | 
					
						
							|  |  |  | 			.reverse() } | 
					
						
							|  |  |  | 	return objects | 
					
						
							|  |  |  | 		.reduce(function(base, cur){ | 
					
						
							|  |  |  | 			Object.keys(cur) | 
					
						
							|  |  |  | 				.map(function(k){ | 
					
						
							|  |  |  | 					;(!soft || !base.hasOwnProperty(k)) | 
					
						
							|  |  |  | 						&& Object.defineProperty(base, k, | 
					
						
							|  |  |  | 							Object.getOwnPropertyDescriptor(cur, k)) }) | 
					
						
							|  |  |  | 			return base }, base) } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Mix sets of methods/props/attrs into an object as prototypes...
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | // 	mixin(base, object, ..)
 | 
					
						
							|  |  |  | // 		-> base
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | // This will create a new object per set of methods given and 
 | 
					
						
							|  |  |  | // mixinFlat(..) the method set into this object leaving the 
 | 
					
						
							|  |  |  | // original objects intact.
 | 
					
						
							|  |  |  | // 
 | 
					
						
							|  |  |  | // 		base <-- object1_copy <-- .. <-- objectN_copy <- base.__proto__
 | 
					
						
							|  |  |  | // 				
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | // NOTE: this will only mix in non-empty objects...
 | 
					
						
							|  |  |  | // NOTE: mixing into a constructor will break object creation via new...
 | 
					
						
							|  |  |  | // 		Example:
 | 
					
						
							|  |  |  | // 			class A {}
 | 
					
						
							|  |  |  | // 			class B extends A {}
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | // 			mixin(B, {x: 123})
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | // 			var b = new B()			// will break...
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | // 		This does not affect object.Constructor(..) chains...
 | 
					
						
							|  |  |  | // NOTE: mixin(Object.prototype, ..) will fail because Object.prototype.__proto__ 
 | 
					
						
							|  |  |  | // 		is imutable...
 | 
					
						
							|  |  |  | var mixin =  | 
					
						
							|  |  |  | module.mixin =  | 
					
						
							|  |  |  | function(base, ...objects){ | 
					
						
							|  |  |  | 	base.__proto__ = objects | 
					
						
							|  |  |  | 		.reduce(function(res, cur){ | 
					
						
							|  |  |  | 			return Object.keys(cur).length > 0 ? | 
					
						
							|  |  |  | 				module.mixinFlat(Object.create(res), cur)  | 
					
						
							|  |  |  | 				: res }, base.__proto__)  | 
					
						
							|  |  |  | 	return base } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Get matching mixins...
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | // 	mixins(base, object[, callback])
 | 
					
						
							|  |  |  | // 	mixins(base, list[, callback])
 | 
					
						
							|  |  |  | // 		-> list
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | //	callback(base, obj, parent)
 | 
					
						
							|  |  |  | //		-> STOP
 | 
					
						
							|  |  |  | //		-> undefined
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | // NOTE: this will also match base...
 | 
					
						
							|  |  |  | // NOTE: if base matches directly callback(..) will get undefined as parent
 | 
					
						
							|  |  |  | // NOTE: for more docs on the callback(..) see sources(..)
 | 
					
						
							| 
									
										
										
										
											2022-08-03 01:57:57 +03:00
										 |  |  | //
 | 
					
						
							|  |  |  | // XXX should this be a generator???
 | 
					
						
							| 
									
										
										
										
											2022-04-07 17:23:36 +03:00
										 |  |  | var mixins = | 
					
						
							|  |  |  | module.mixins = | 
					
						
							|  |  |  | function(base, object, callback){ | 
					
						
							|  |  |  | 	object = object instanceof Array ? | 
					
						
							|  |  |  | 		object | 
					
						
							|  |  |  | 		: [object] | 
					
						
							|  |  |  | 	var res = [] | 
					
						
							|  |  |  | 	var o | 
					
						
							|  |  |  | 	var parent | 
					
						
							|  |  |  | 	while(base != null){ | 
					
						
							|  |  |  | 		// match each object...
 | 
					
						
							|  |  |  | 		for(var obj of object){ | 
					
						
							|  |  |  | 			if(match(base, obj)){ | 
					
						
							|  |  |  | 				o = callback  | 
					
						
							|  |  |  | 					&& callback(base, obj, parent) | 
					
						
							|  |  |  | 				// manage results...
 | 
					
						
							|  |  |  | 				res.push( | 
					
						
							|  |  |  | 					(o === undefined || o === module.STOP) ?  | 
					
						
							|  |  |  | 						[base] | 
					
						
							|  |  |  | 					: o instanceof module.STOP ? | 
					
						
							|  |  |  | 						o.value | 
					
						
							|  |  |  | 					: o ) | 
					
						
							|  |  |  | 				if(o === module.STOP  | 
					
						
							|  |  |  | 						|| o instanceof module.STOP){ | 
					
						
							|  |  |  | 					return res.flat() }  | 
					
						
							|  |  |  | 				// match found, no need to test further...
 | 
					
						
							|  |  |  | 				break } } | 
					
						
							|  |  |  | 		parent = base | 
					
						
							|  |  |  | 		base = base.__proto__ } | 
					
						
							|  |  |  | 	return res.flat() } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Check of base has mixin...
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | // 	hasMixin(base, mixin)
 | 
					
						
							|  |  |  | // 		-> bool
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | // NOTE: to test for a flat mixin directly use .matchPartial(base, object)
 | 
					
						
							|  |  |  | var hasMixin = | 
					
						
							|  |  |  | module.hasMixin = | 
					
						
							|  |  |  | function(base, object){ | 
					
						
							|  |  |  | 	return ( | 
					
						
							|  |  |  | 		// normal mixin...
 | 
					
						
							|  |  |  | 		mixins(base, object, function(){ return module.STOP }) | 
					
						
							|  |  |  | 			.length > 0 | 
					
						
							|  |  |  | 		// flat mixin search...
 | 
					
						
							| 
									
										
										
										
											2022-08-03 01:57:57 +03:00
										 |  |  | 		|| [...sources(base)] | 
					
						
							|  |  |  | 			.some(function(p){  | 
					
						
							|  |  |  | 				return matchPartial(p, object) }) )} | 
					
						
							| 
									
										
										
										
											2022-04-07 17:23:36 +03:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Mix-out sets of methods/props/attrs out of an object prototype chain...
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | // 	Mix-out first occurrence of each matching object...
 | 
					
						
							|  |  |  | // 	mixout(base, object, ..)
 | 
					
						
							|  |  |  | // 	mixout(base, 'first', object, ..)
 | 
					
						
							|  |  |  | // 		-> base
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | // 	Mix-out all occurrences of each matching object...
 | 
					
						
							|  |  |  | // 	mixout(base, 'all', object, ..)
 | 
					
						
							|  |  |  | // 		-> base
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | // NOTE: this is the opposite to mixin(..)
 | 
					
						
							|  |  |  | // NOTE: this used mixins(..) / match(..) to find the relevant mixins, 
 | 
					
						
							|  |  |  | // 		see those for more info...
 | 
					
						
							|  |  |  | var mixout = | 
					
						
							|  |  |  | module.mixout = | 
					
						
							|  |  |  | function(base, ...objects){ | 
					
						
							|  |  |  | 	var all = objects[0] == 'all' ? | 
					
						
							|  |  |  | 			!!objects.shift() | 
					
						
							|  |  |  | 		: objects[0] == 'first' ? | 
					
						
							|  |  |  | 			!objects.shift() | 
					
						
							|  |  |  | 		: false | 
					
						
							|  |  |  | 	var remove = [] | 
					
						
							|  |  |  | 	mixins(base, objects, function(match, obj, parent){ | 
					
						
							|  |  |  | 		parent && remove.push(parent) | 
					
						
							|  |  |  | 		// when removing the first occurrence, don't check for obj again...
 | 
					
						
							|  |  |  | 		all || objects.splice(objects.indexOf(obj), 1) }) | 
					
						
							|  |  |  | 	// NOTE: we are removing on a separate stage so as not to mess with
 | 
					
						
							|  |  |  | 	// 		mixins(..) iterating...
 | 
					
						
							|  |  |  | 	remove | 
					
						
							|  |  |  | 		// XXX not sure why this is needed, needs thought...
 | 
					
						
							|  |  |  | 		.reverse() | 
					
						
							|  |  |  | 		.forEach(function(p){ | 
					
						
							|  |  |  | 			p.__proto__ = p.__proto__.__proto__ }) | 
					
						
							|  |  |  | 	return base } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Mixin wrapper/object...
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | //	Create a new mixin...
 | 
					
						
							|  |  |  | //	Mixin(name, data, ..)
 | 
					
						
							|  |  |  | //		-> mixin
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | //	Create a new mixin setting the default mode...
 | 
					
						
							|  |  |  | //	Mixin(name, mode, data, ..)
 | 
					
						
							|  |  |  | //		-> mixin
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | //	Apply mixin in the prototype chain (default)...
 | 
					
						
							|  |  |  | //	mixin(obj)
 | 
					
						
							|  |  |  | //	mixin('proto', obj)
 | 
					
						
							|  |  |  | //		-> obj
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | //	Copy date from mixin into obj directly...
 | 
					
						
							|  |  |  | //	mixin('flat', obj)
 | 
					
						
							|  |  |  | //		-> obj
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | // Example:
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | // 		var BasicMixin = Mixin('BasicMixin', {
 | 
					
						
							|  |  |  | // 			...
 | 
					
						
							|  |  |  | // 		})
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | // 		...
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | //		var o = {
 | 
					
						
							|  |  |  | //			...
 | 
					
						
							|  |  |  | //		}
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | //		BasicMixin(o)
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | // NOTE: the constructor will allways create a new .data object but will 
 | 
					
						
							|  |  |  | // 		not do a deep copy into it -- see mixin(..) / mixinFlat(..)
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | // XXX should this move to its own modue???
 | 
					
						
							|  |  |  | var Mixin = | 
					
						
							|  |  |  | module.Mixin = | 
					
						
							|  |  |  | Constructor('Mixin', { | 
					
						
							|  |  |  | 	// static methods...
 | 
					
						
							|  |  |  | 	//
 | 
					
						
							|  |  |  | 	// NOTE: currently this is a complement to the top level functions.
 | 
					
						
							|  |  |  | 	mixin, | 
					
						
							|  |  |  | 	mixinFlat, | 
					
						
							|  |  |  | 	mixout, | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	mixins, | 
					
						
							|  |  |  | 	hasMixin, | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | }, { | 
					
						
							|  |  |  | 	name: null, | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// mixin data...
 | 
					
						
							|  |  |  | 	data: null, | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// data "copy" mode...
 | 
					
						
							|  |  |  | 	//
 | 
					
						
							|  |  |  | 	// This can be:
 | 
					
						
							|  |  |  | 	// 	'proto'		- mix data into prototype chain (default)
 | 
					
						
							|  |  |  | 	// 	'flat'		- use mixinFlat(..) to copy data
 | 
					
						
							|  |  |  | 	// 	'soft'	- like 'flat' but uses mixinFlat('soft', ..)
 | 
					
						
							|  |  |  | 	mode: 'proto', | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// base API...
 | 
					
						
							|  |  |  | 	//
 | 
					
						
							|  |  |  | 	isMixed: function(target){ | 
					
						
							|  |  |  | 		return this.constructor.hasMixin(target, this.data) }, | 
					
						
							|  |  |  | 	mixout: function(target){ | 
					
						
							|  |  |  | 		return this.constructor.mixout(target, this.data) }, | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// mix into target...
 | 
					
						
							|  |  |  | 	__call__: function(_, target, mode=this.mode){ | 
					
						
							|  |  |  | 		typeof(target) == typeof('str') | 
					
						
							|  |  |  | 			&& ([_, mode, target] = arguments) | 
					
						
							|  |  |  | 		return mode == 'flat' ? | 
					
						
							|  |  |  | 				this.constructor.mixinFlat(target, this.data) | 
					
						
							|  |  |  | 			: mode == 'soft' ? | 
					
						
							|  |  |  | 				this.constructor.mixinFlat('soft', target, this.data) | 
					
						
							|  |  |  | 			: this.constructor.mixin(target, this.data) }, | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	__init__: function(name, ...data){ | 
					
						
							|  |  |  | 		// Mixin(name, mode, ...) -- handle default mode...
 | 
					
						
							|  |  |  | 		typeof(data[0]) == typeof('str') | 
					
						
							|  |  |  | 			&& (this.mode = data.shift()) | 
					
						
							|  |  |  | 		// name...
 | 
					
						
							|  |  |  | 		// NOTE: .defineProperty(..) is used because this is a function
 | 
					
						
							|  |  |  | 		// 		and function's .name is not too configurable...
 | 
					
						
							|  |  |  | 		// NOTE: we do not need to configure this any more, .defineProperty(..)
 | 
					
						
							|  |  |  | 		// 		merges the descriptor into the original keeping any values not
 | 
					
						
							|  |  |  | 		// 		explicitly overwritten...
 | 
					
						
							|  |  |  | 		Object.defineProperty(this, 'name', { value: name }) | 
					
						
							|  |  |  | 		// create/merge .data...
 | 
					
						
							|  |  |  | 		this.data = this.constructor.mixinFlat({},  | 
					
						
							|  |  |  | 			...data.map(function(e){  | 
					
						
							|  |  |  | 				// handle bare objects and mixins differently...
 | 
					
						
							|  |  |  | 				return e instanceof Mixin ?  | 
					
						
							|  |  |  | 					e.data  | 
					
						
							|  |  |  | 					: e })) }, | 
					
						
							|  |  |  | }) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | //---------------------------------------------------------------------
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | BOOTSTRAP() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /********************************************************************** | 
					
						
							| 
									
										
										
										
											2022-08-03 01:57:57 +03:00
										 |  |  | * vim:set ts=4 sw=4 nowrap :                        */ return module }) |