| 
									
										
										
										
											2022-04-12 01:31:28 +03:00
										 |  |  | /********************************************************************** | 
					
						
							|  |  |  | *  | 
					
						
							|  |  |  | * | 
					
						
							| 
									
										
										
										
											2022-07-10 15:38:56 +03:00
										 |  |  | * | 
					
						
							|  |  |  | * Architecture: | 
					
						
							|  |  |  | * 	store | 
					
						
							|  |  |  | * 	page | 
					
						
							|  |  |  | * 	renderer | 
					
						
							|  |  |  | * | 
					
						
							|  |  |  | * | 
					
						
							| 
									
										
										
										
											2022-07-11 18:14:11 +03:00
										 |  |  | * XXX weaknesses to review: | 
					
						
							|  |  |  | * 		- <store>.paths() as an index... | 
					
						
							|  |  |  | * 			+ decouples the search logic from the store backend | 
					
						
							|  |  |  | * 			- requires the whole list of pages to be in memory | 
					
						
							|  |  |  | * 				...need to be independent of the number of pages if at  | 
					
						
							|  |  |  | * 				all possible -- otherwise this will hinder long-term use... | 
					
						
							|  |  |  | * 		-  | 
					
						
							|  |  |  | * | 
					
						
							|  |  |  | * | 
					
						
							|  |  |  | * TODO: | 
					
						
							| 
									
										
										
										
											2022-07-17 15:49:05 +03:00
										 |  |  | * 	- async parser does not work... | 
					
						
							| 
									
										
										
										
											2022-07-11 18:14:11 +03:00
										 |  |  | * 	- .load(..) / .json(..) -- for most stores... | 
					
						
							|  |  |  | * 		might be a good idea to keep a unified format... | 
					
						
							|  |  |  | * 	- <page>.then() -- resolve when all pending write operations done ??? | 
					
						
							| 
									
										
										
										
											2022-07-25 23:58:23 +03:00
										 |  |  | * 	- an async REPL??? | 
					
						
							| 
									
										
										
										
											2022-07-11 18:14:11 +03:00
										 |  |  | * | 
					
						
							|  |  |  | * | 
					
						
							|  |  |  | * | 
					
						
							|  |  |  | *********************************************************************** | 
					
						
							| 
									
										
										
										
											2022-07-10 15:38:56 +03:00
										 |  |  | * | 
					
						
							| 
									
										
										
										
											2022-04-16 10:53:41 +03:00
										 |  |  | * XXX might be a good idea to try signature based security: | 
					
						
							|  |  |  | * 		- sign changes | 
					
						
							|  |  |  | * 		- sign sync session | 
					
						
							|  |  |  | * 		- refuse changes with wrong signatures | 
					
						
							|  |  |  | * 		- public keys available on client and on server | 
					
						
							|  |  |  | * 			- check signatures localy | 
					
						
							|  |  |  | * 			- check signatures remotely | 
					
						
							|  |  |  | * 		- private key available only with author | 
					
						
							|  |  |  | * 		- keep both the last signed and superceding unsigned version | 
					
						
							|  |  |  | * 		- on sync ask to overwrite unsigned with signed | 
					
						
							|  |  |  | * 		- check if we can use the same mechanics as ssh... | 
					
						
							|  |  |  | * 		- in this view a user in the system is simply a set of keys and  | 
					
						
							|  |  |  | * 			a signature (a page =)) | 
					
						
							| 
									
										
										
										
											2022-04-12 01:31:28 +03:00
										 |  |  | * | 
					
						
							| 
									
										
										
										
											2022-07-26 11:09:12 +03:00
										 |  |  | * XXX split this into modules (???) | 
					
						
							| 
									
										
										
										
											2022-05-17 01:26:39 +03:00
										 |  |  | * | 
					
						
							|  |  |  | * XXX add action to reset overloaded (bootstrap) pages... | 
					
						
							|  |  |  | * 		- per page | 
					
						
							|  |  |  | * 		- global | 
					
						
							|  |  |  | * | 
					
						
							| 
									
										
										
										
											2022-05-17 17:36:42 +03:00
										 |  |  | * XXX need to think about search -- page function argument syntax??? | 
					
						
							| 
									
										
										
										
											2022-05-17 01:26:39 +03:00
										 |  |  | * | 
					
						
							|  |  |  | * XXX might need to get all the links (macro-level) from a page... | 
					
						
							|  |  |  | * 		...would be useful for caching... | 
					
						
							|  |  |  | * | 
					
						
							| 
									
										
										
										
											2022-06-17 12:15:37 +03:00
										 |  |  | * XXX .__paths__(..) may be a bottleneck... | 
					
						
							|  |  |  | * 		need to either think of a way around it or cache the path index | 
					
						
							|  |  |  | * 		in a sync way... | 
					
						
							|  |  |  | * | 
					
						
							| 
									
										
										
										
											2022-05-04 18:26:02 +03:00
										 |  |  | * | 
					
						
							|  |  |  | * | 
					
						
							| 
									
										
										
										
											2022-04-12 01:31:28 +03:00
										 |  |  | **********************************************************************/ | 
					
						
							|  |  |  | ((typeof define)[0]=='u'?function(f){module.exports=f(require)}:define) | 
					
						
							|  |  |  | (function(require){ var module={} // make module AMD/node compatible...
 | 
					
						
							|  |  |  | /*********************************************************************/ | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-04-21 18:33:08 +03:00
										 |  |  | // XXX
 | 
					
						
							|  |  |  | //var object = require('lib/object')
 | 
					
						
							|  |  |  | var object = require('ig-object') | 
					
						
							| 
									
										
										
										
											2022-04-25 16:00:12 +03:00
										 |  |  | var types = require('ig-types') | 
					
						
							| 
									
										
										
										
											2022-04-21 18:33:08 +03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-04-12 01:31:28 +03:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /*********************************************************************/ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-05-15 03:04:29 +03:00
										 |  |  | //---------------------------------------------------------------------
 | 
					
						
							|  |  |  | // Path...
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-04-16 12:28:06 +03:00
										 |  |  | var path =  | 
					
						
							|  |  |  | module.path = { | 
					
						
							| 
									
										
										
										
											2022-04-12 01:31:28 +03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-05-20 15:03:24 +03:00
										 |  |  | 	// Page returned when listing a path ending with '/'...
 | 
					
						
							| 
									
										
										
										
											2022-04-12 01:31:28 +03:00
										 |  |  | 	//
 | 
					
						
							|  |  |  | 	// If set to false treat dirs the same as pages (default)
 | 
					
						
							| 
									
										
										
										
											2022-05-20 10:56:44 +03:00
										 |  |  | 	//INDEX_PAGE: 'index',
 | 
					
						
							|  |  |  | 	INDEX_PAGE: false, | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// The page returned when getting the '/' path...
 | 
					
						
							|  |  |  | 	//
 | 
					
						
							|  |  |  | 	// NOTE: this is the same as .INDEX_PAGE but only for '/'
 | 
					
						
							|  |  |  | 	ROOT_PAGE: 'WikiHome', | 
					
						
							| 
									
										
										
										
											2022-04-12 01:31:28 +03:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	ALTERNATIVE_PAGES: [ | 
					
						
							|  |  |  | 		'EmptyPage', | 
					
						
							|  |  |  | 		'NotFound', | 
					
						
							|  |  |  | 	], | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-07-10 15:38:56 +03:00
										 |  |  | 	// Default alternate search locations...
 | 
					
						
							|  |  |  | 	//
 | 
					
						
							| 
									
										
										
										
											2022-05-20 10:56:44 +03:00
										 |  |  | 	// NOTE: if a path here is relative it is also searched relative to 
 | 
					
						
							|  |  |  | 	// 		the target path.
 | 
					
						
							| 
									
										
										
										
											2022-04-12 01:31:28 +03:00
										 |  |  | 	SEARCH_PATHS: [ | 
					
						
							| 
									
										
										
										
											2022-05-20 10:56:44 +03:00
										 |  |  | 		//'./Theme/CLI',
 | 
					
						
							| 
									
										
										
										
											2022-04-12 01:31:28 +03:00
										 |  |  | 		'./Templates', | 
					
						
							|  |  |  | 		'/System', | 
					
						
							|  |  |  | 	], | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-07-10 15:38:56 +03:00
										 |  |  | 	// Path utils...
 | 
					
						
							|  |  |  | 	//
 | 
					
						
							|  |  |  | 	// Path can be in one of two formats:
 | 
					
						
							|  |  |  | 	// 	string
 | 
					
						
							|  |  |  | 	// 	array
 | 
					
						
							|  |  |  | 	//
 | 
					
						
							| 
									
										
										
										
											2022-04-12 01:31:28 +03:00
										 |  |  | 	// NOTE: trailing/leading '/' are represented by '' at end/start of 
 | 
					
						
							|  |  |  | 	// 		path list...
 | 
					
						
							|  |  |  | 	normalize: function(path='.', format='auto'){ | 
					
						
							|  |  |  | 		format = format == 'auto' ? | 
					
						
							|  |  |  | 			(path instanceof Array ? | 
					
						
							|  |  |  | 				'array' | 
					
						
							|  |  |  | 				: 'string') | 
					
						
							|  |  |  | 			: format | 
					
						
							| 
									
										
										
										
											2022-05-19 04:07:11 +03:00
										 |  |  | 		var root = path[0] == ''  | 
					
						
							|  |  |  | 			|| path[0] == '/' | 
					
						
							| 
									
										
										
										
											2022-04-12 01:31:28 +03:00
										 |  |  | 		path = (path instanceof Array ? | 
					
						
							|  |  |  | 				path | 
					
						
							|  |  |  | 				// NOTE: this will also trim the path elements...
 | 
					
						
							|  |  |  | 				: path.split(/\s*[\\\/]+\s*/)) | 
					
						
							| 
									
										
										
										
											2022-04-22 00:14:41 +03:00
										 |  |  | 			.reduce(function(res, e, i, L){ | 
					
						
							| 
									
										
										
										
											2022-04-12 01:31:28 +03:00
										 |  |  | 				// special case: leading '..' / '.'
 | 
					
						
							|  |  |  | 				if(res.length == 0  | 
					
						
							|  |  |  | 						&& e == '..'){ | 
					
						
							|  |  |  | 					return [e] } | 
					
						
							| 
									
										
										
										
											2022-04-22 00:14:41 +03:00
										 |  |  | 				;(e == '.'  | 
					
						
							|  |  |  | 						// keep explicit '/' only at start/end of path...
 | 
					
						
							|  |  |  | 						|| (e == ''  | 
					
						
							|  |  |  | 							&& i != 0  | 
					
						
							|  |  |  | 							&& i != L.length-1)) ? | 
					
						
							| 
									
										
										
										
											2022-04-12 01:31:28 +03:00
										 |  |  | 					undefined | 
					
						
							|  |  |  | 				: e == '..'  | 
					
						
							|  |  |  | 						|| res[res.length-1] == '>>' ? | 
					
						
							| 
									
										
										
										
											2022-05-19 03:43:34 +03:00
										 |  |  | 					((res.length > 1  | 
					
						
							|  |  |  | 							|| res[0] != '') | 
					
						
							|  |  |  | 						&& res.pop()) | 
					
						
							| 
									
										
										
										
											2022-04-12 01:31:28 +03:00
										 |  |  | 				// NOTE: the last '>>' will be retained...
 | 
					
						
							|  |  |  | 				: res.push(e) | 
					
						
							|  |  |  | 				return res }, [])  | 
					
						
							|  |  |  | 		return format == 'string' ? | 
					
						
							| 
									
										
										
										
											2022-05-19 04:07:11 +03:00
										 |  |  | 			// special case: root -> keep '/'
 | 
					
						
							|  |  |  | 			((root  | 
					
						
							|  |  |  | 					&& path.length == 1  | 
					
						
							|  |  |  | 					&& path[0] == '') ? | 
					
						
							| 
									
										
										
										
											2022-05-19 03:43:34 +03:00
										 |  |  | 				('/'+ path.join('/')) | 
					
						
							|  |  |  | 				: path.join('/')) | 
					
						
							| 
									
										
										
										
											2022-04-12 01:31:28 +03:00
										 |  |  | 			: path }, | 
					
						
							| 
									
										
										
										
											2022-05-20 10:56:44 +03:00
										 |  |  | 	split: function(path){ | 
					
						
							|  |  |  | 		return this.normalize(path, 'array') }, | 
					
						
							|  |  |  | 	join: function(...parts){ | 
					
						
							|  |  |  | 		return this.normalize( | 
					
						
							|  |  |  | 			(parts[0] instanceof Array ? | 
					
						
							|  |  |  | 					parts[0] | 
					
						
							|  |  |  | 					: parts) | 
					
						
							|  |  |  | 				.join('/'),  | 
					
						
							|  |  |  | 			'string') }, | 
					
						
							|  |  |  | 	basename: function(path){ | 
					
						
							|  |  |  | 		return this.split(path).pop() }, | 
					
						
							|  |  |  | 	dirname: function(path){ | 
					
						
							|  |  |  | 		return this.relative(path, '..', 'string') }, | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-04-12 01:31:28 +03:00
										 |  |  | 	relative: function(parent, path, format='auto'){ | 
					
						
							|  |  |  | 		format = format == 'auto' ? | 
					
						
							|  |  |  | 			(path instanceof Array ? | 
					
						
							|  |  |  | 				'array' | 
					
						
							|  |  |  | 				: 'string') | 
					
						
							|  |  |  | 			: format | 
					
						
							|  |  |  | 		// root path...
 | 
					
						
							| 
									
										
										
										
											2022-05-19 04:07:11 +03:00
										 |  |  | 		if(path[0] == '' || path[0] == '/'){ | 
					
						
							|  |  |  | 			return this.normalize(path, format) } | 
					
						
							|  |  |  | 		// unify parent/path types...
 | 
					
						
							|  |  |  | 		parent = parent instanceof Array ? | 
					
						
							|  |  |  | 			parent | 
					
						
							|  |  |  | 			: parent.split(/\s*[\\\/]+\s*/) | 
					
						
							|  |  |  | 		path = path instanceof Array ? | 
					
						
							|  |  |  | 			path | 
					
						
							|  |  |  | 			: path.split(/\s*[\\\/]+\s*/) | 
					
						
							|  |  |  | 		return this.normalize([...parent, ...path], format) }, | 
					
						
							| 
									
										
										
										
											2022-04-12 01:31:28 +03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-05-20 10:56:44 +03:00
										 |  |  | 	// Build alternative paths for page acquisition...
 | 
					
						
							|  |  |  | 	//
 | 
					
						
							|  |  |  | 	// NOTE: if seen is given (when called recursively) this will not 
 | 
					
						
							|  |  |  | 	// 		search for .ALTERNATIVE_PAGES...
 | 
					
						
							| 
									
										
										
										
											2022-05-24 11:01:51 +03:00
										 |  |  | 	// XXX should we search for each path element or just the last one (current)??? 
 | 
					
						
							| 
									
										
										
										
											2022-05-20 15:03:24 +03:00
										 |  |  | 	// XXX should we keep the trailing '/'???
 | 
					
						
							| 
									
										
										
										
											2022-05-20 10:56:44 +03:00
										 |  |  | 	paths: function*(path='/', seen){ | 
					
						
							|  |  |  | 		var alt_pages = !seen | 
					
						
							|  |  |  | 		seen = seen  | 
					
						
							|  |  |  | 			?? new Set() | 
					
						
							|  |  |  | 		path = this.normalize(path, 'string') | 
					
						
							|  |  |  | 		// special case: root...
 | 
					
						
							|  |  |  | 		if(path == '/' || path == ''){ | 
					
						
							| 
									
										
										
										
											2022-05-20 15:03:24 +03:00
										 |  |  | 			// normalize...
 | 
					
						
							|  |  |  | 			path = '/' | 
					
						
							| 
									
										
										
										
											2022-05-20 10:56:44 +03:00
										 |  |  | 			// as-is...
 | 
					
						
							|  |  |  | 			seen.add(path) | 
					
						
							|  |  |  | 			yield path | 
					
						
							|  |  |  | 			// special case: root page...
 | 
					
						
							|  |  |  | 			if(this.ROOT_PAGE){ | 
					
						
							|  |  |  | 				yield* this.paths(this.normalize('/'+ this.ROOT_PAGE, 'string'), seen) }} | 
					
						
							|  |  |  | 		// NOTE: since path is already normalized we can trust the delimiter...
 | 
					
						
							|  |  |  | 		path = path.split(/\//g) | 
					
						
							| 
									
										
										
										
											2022-04-12 01:31:28 +03:00
										 |  |  | 		// normalize relative paths to root...
 | 
					
						
							|  |  |  | 		path[0] != '' | 
					
						
							|  |  |  | 			&& path.unshift('') | 
					
						
							| 
									
										
										
										
											2022-05-20 15:03:24 +03:00
										 |  |  | 		// paths ending in '/'...
 | 
					
						
							| 
									
										
										
										
											2022-04-12 01:31:28 +03:00
										 |  |  | 		if(path[path.length-1] == ''){ | 
					
						
							|  |  |  | 			path.pop() | 
					
						
							| 
									
										
										
										
											2022-05-20 10:56:44 +03:00
										 |  |  | 			this.INDEX_PAGE | 
					
						
							|  |  |  | 				&& path.push(this.INDEX_PAGE) } | 
					
						
							|  |  |  | 		// search for page...
 | 
					
						
							|  |  |  | 		var page = path.pop() | 
					
						
							|  |  |  | 		for(var tpl of ['.', ...this.SEARCH_PATHS]){ | 
					
						
							|  |  |  | 			// search for page up the path...
 | 
					
						
							|  |  |  | 			var p = path.slice() | 
					
						
							|  |  |  | 			while(p.length > 0){ | 
					
						
							|  |  |  | 				var cur = this.relative(p, tpl +'/'+ page, 'string') | 
					
						
							|  |  |  | 				if(!seen.has(cur)){ | 
					
						
							|  |  |  | 					seen.add(cur) | 
					
						
							|  |  |  | 					yield cur } | 
					
						
							|  |  |  | 				// special case: non-relative template/page path...
 | 
					
						
							|  |  |  | 				if(tpl[0] == '/'){ | 
					
						
							|  |  |  | 					break } | 
					
						
							|  |  |  | 				p.pop() } } | 
					
						
							|  |  |  | 		// alternative pages...
 | 
					
						
							|  |  |  | 		if(alt_pages){ | 
					
						
							|  |  |  | 			for(var page of [...this.ALTERNATIVE_PAGES]){ | 
					
						
							|  |  |  | 				yield* this.paths(path.concat(page), seen) }} }, | 
					
						
							| 
									
										
										
										
											2022-04-12 01:31:28 +03:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | //---------------------------------------------------------------------
 | 
					
						
							| 
									
										
										
										
											2022-05-15 03:04:29 +03:00
										 |  |  | // Store...
 | 
					
						
							| 
									
										
										
										
											2022-04-12 01:31:28 +03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-05-04 16:50:07 +03:00
										 |  |  | //
 | 
					
						
							|  |  |  | // To create a store adapter:
 | 
					
						
							|  |  |  | // 		- inherit from BaseStore
 | 
					
						
							|  |  |  | // 		- overload:
 | 
					
						
							| 
									
										
										
										
											2022-05-06 18:27:41 +03:00
										 |  |  | // 			.__paths__()
 | 
					
						
							|  |  |  | // 				-> <keys>
 | 
					
						
							| 
									
										
										
										
											2022-05-04 18:26:02 +03:00
										 |  |  | // 			.__exists__(..)
 | 
					
						
							| 
									
										
										
										
											2022-05-06 18:27:41 +03:00
										 |  |  | // 				-> <path>
 | 
					
						
							| 
									
										
										
										
											2022-05-17 01:26:39 +03:00
										 |  |  | // 				-> false
 | 
					
						
							| 
									
										
										
										
											2022-05-04 16:50:07 +03:00
										 |  |  | // 			.__get__(..)
 | 
					
						
							| 
									
										
										
										
											2022-05-06 18:27:41 +03:00
										 |  |  | // 				-> <data>
 | 
					
						
							| 
									
										
										
										
											2022-05-04 16:50:07 +03:00
										 |  |  | // 		- optionally (for writable stores)
 | 
					
						
							|  |  |  | // 			.__update__(..)
 | 
					
						
							| 
									
										
										
										
											2022-05-06 23:08:53 +03:00
										 |  |  | // 			.__delete__(..)
 | 
					
						
							| 
									
										
										
										
											2022-05-04 16:50:07 +03:00
										 |  |  | // 			.load(..)
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | //
 | 
					
						
							| 
									
										
										
										
											2022-07-10 15:38:56 +03:00
										 |  |  | // NOTE: store keys must be normalized to avoid conditions where two
 | 
					
						
							|  |  |  | // 		forms of the same path exist at the same time...
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | // XXX potential architectural problems:
 | 
					
						
							|  |  |  | // 		- .paths()
 | 
					
						
							|  |  |  | // 			external index -- is this good???
 | 
					
						
							|  |  |  | // 			bottleneck??
 | 
					
						
							|  |  |  | // 			cache/index???
 | 
					
						
							|  |  |  | // 			...can we avoid this??
 | 
					
						
							| 
									
										
										
										
											2022-04-12 22:35:06 +03:00
										 |  |  | //
 | 
					
						
							| 
									
										
										
										
											2022-04-15 11:11:02 +03:00
										 |  |  | // XXX LEADING_SLASH should this be strict about leading '/' in paths???
 | 
					
						
							|  |  |  | // 		...this may lead to duplicate paths created -- '/a/b' and 'a/b'
 | 
					
						
							| 
									
										
										
										
											2022-04-12 22:35:06 +03:00
										 |  |  | // XXX should we support page symlinking???
 | 
					
						
							| 
									
										
										
										
											2022-06-12 12:04:44 +03:00
										 |  |  | // XXX async: not sure if we need to return this from async methods...
 | 
					
						
							| 
									
										
										
										
											2022-05-03 12:28:03 +03:00
										 |  |  | var BaseStore =  | 
					
						
							|  |  |  | module.BaseStore = { | 
					
						
							| 
									
										
										
										
											2022-05-03 16:15:16 +03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-05-29 23:31:34 +03:00
										 |  |  | 	// XXX NEXT revise naming...
 | 
					
						
							| 
									
										
										
										
											2022-05-04 16:50:07 +03:00
										 |  |  | 	next: undefined, | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-05-19 11:11:48 +03:00
										 |  |  | 	// NOTE: .data is not part of the spec and can be implementation-specific,
 | 
					
						
							|  |  |  | 	// 		only .__<name>__(..) use it internally... (XXX check this)
 | 
					
						
							| 
									
										
										
										
											2022-05-11 15:27:15 +03:00
										 |  |  | 	__data: undefined, | 
					
						
							|  |  |  | 	get data(){ | 
					
						
							|  |  |  | 		return this.__data  | 
					
						
							|  |  |  | 			?? (this.__data = {}) }, | 
					
						
							|  |  |  | 	set data(value){ | 
					
						
							|  |  |  | 		this.__data = value }, | 
					
						
							| 
									
										
										
										
											2022-05-03 16:15:16 +03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-05-03 16:47:43 +03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-05-19 02:13:17 +03:00
										 |  |  | 	// XXX might be a good idea to cache this...
 | 
					
						
							| 
									
										
										
										
											2022-06-12 12:04:44 +03:00
										 |  |  | 	__paths__: async function(){ | 
					
						
							| 
									
										
										
										
											2022-05-04 18:26:02 +03:00
										 |  |  | 		return Object.keys(this.data) }, | 
					
						
							| 
									
										
										
										
											2022-06-12 12:04:44 +03:00
										 |  |  | 	paths: async function(local=false){ | 
					
						
							|  |  |  | 		return this.__paths__() | 
					
						
							|  |  |  | 			.iter() | 
					
						
							| 
									
										
										
										
											2022-05-04 18:26:02 +03:00
										 |  |  | 			// XXX NEXT
 | 
					
						
							|  |  |  | 			.concat((!local && (this.next || {}).paths) ?  | 
					
						
							|  |  |  | 				this.next.paths()  | 
					
						
							|  |  |  | 				: []) }, | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-05-08 10:53:03 +03:00
										 |  |  | 	//
 | 
					
						
							|  |  |  | 	// 	.exists(<path>)
 | 
					
						
							|  |  |  | 	// 		-> <normalized-path>
 | 
					
						
							|  |  |  | 	// 		-> false
 | 
					
						
							|  |  |  | 	//
 | 
					
						
							| 
									
										
										
										
											2022-05-04 16:14:57 +03:00
										 |  |  | 	// XXX might be a good idea to cache this...
 | 
					
						
							| 
									
										
										
										
											2022-06-12 12:04:44 +03:00
										 |  |  | 	__exists__: async function(path){ | 
					
						
							| 
									
										
										
										
											2022-05-11 15:27:15 +03:00
										 |  |  | 		return path in this.data | 
					
						
							|  |  |  | 				&& path }, | 
					
						
							| 
									
										
										
										
											2022-06-12 12:04:44 +03:00
										 |  |  | 	exists: async function(path){ | 
					
						
							| 
									
										
										
										
											2022-05-06 18:27:41 +03:00
										 |  |  | 		path = module.path.normalize(path, 'string') | 
					
						
							| 
									
										
										
										
											2022-06-12 12:04:44 +03:00
										 |  |  | 		return (await this.__exists__(path, 'string')) | 
					
						
							| 
									
										
										
										
											2022-05-06 18:27:41 +03:00
										 |  |  | 			// NOTE: all paths at this point and in store are 
 | 
					
						
							|  |  |  | 			// 		absolute, so we check both with the leading 
 | 
					
						
							|  |  |  | 			// 		'/' and without it to make things a bit more 
 | 
					
						
							|  |  |  | 			// 		relaxed and return the actual matching path...
 | 
					
						
							| 
									
										
										
										
											2022-06-12 12:04:44 +03:00
										 |  |  | 			|| (await this.__exists__( | 
					
						
							| 
									
										
										
										
											2022-05-06 18:27:41 +03:00
										 |  |  | 				path[0] == '/' ?  | 
					
						
							|  |  |  | 					path.slice(1)  | 
					
						
							| 
									
										
										
										
											2022-06-12 12:04:44 +03:00
										 |  |  | 					: ('/'+ path))) | 
					
						
							| 
									
										
										
										
											2022-05-04 16:14:57 +03:00
										 |  |  | 			// XXX NEXT
 | 
					
						
							| 
									
										
										
										
											2022-05-06 18:27:41 +03:00
										 |  |  | 			// delegate to .next...
 | 
					
						
							| 
									
										
										
										
											2022-05-04 18:26:02 +03:00
										 |  |  | 			|| ((this.next || {}).__exists__ | 
					
						
							| 
									
										
										
										
											2022-06-12 12:04:44 +03:00
										 |  |  | 				&& (await this.next.__exists__(path) | 
					
						
							|  |  |  | 					|| await this.next.__exists__( | 
					
						
							| 
									
										
										
										
											2022-05-06 18:27:41 +03:00
										 |  |  | 						path[0] == '/' ? | 
					
						
							|  |  |  | 							path.slice(1) | 
					
						
							| 
									
										
										
										
											2022-05-08 10:53:03 +03:00
										 |  |  | 							: ('/'+ path))))  | 
					
						
							|  |  |  | 			// normalize the output...
 | 
					
						
							|  |  |  | 			|| false }, | 
					
						
							| 
									
										
										
										
											2022-05-28 23:34:57 +03:00
										 |  |  | 	// find the closest existing alternative path...
 | 
					
						
							| 
									
										
										
										
											2022-06-12 12:04:44 +03:00
										 |  |  | 	find: async function(path){ | 
					
						
							|  |  |  | 		for(var p of await module.path.paths(path)){ | 
					
						
							|  |  |  | 			p = await this.exists(p) | 
					
						
							| 
									
										
										
										
											2022-05-25 01:01:22 +03:00
										 |  |  | 			if(p){ | 
					
						
							|  |  |  | 				return p } } }, | 
					
						
							| 
									
										
										
										
											2022-04-15 23:32:49 +03:00
										 |  |  | 	// 
 | 
					
						
							|  |  |  | 	// 	Resolve page for path
 | 
					
						
							|  |  |  | 	// 	.match(<path>)
 | 
					
						
							|  |  |  | 	// 		-> <path>
 | 
					
						
							|  |  |  | 	//
 | 
					
						
							|  |  |  | 	// 	Match paths (non-strict mode)
 | 
					
						
							|  |  |  | 	// 	.match(<pattern>)
 | 
					
						
							|  |  |  | 	// 	.match(<pattern>, false)
 | 
					
						
							|  |  |  | 	// 		-> [<path>, ...]
 | 
					
						
							| 
									
										
										
										
											2022-05-22 14:23:21 +03:00
										 |  |  | 	// 		-> []
 | 
					
						
							| 
									
										
										
										
											2022-04-15 23:32:49 +03:00
										 |  |  | 	//
 | 
					
						
							|  |  |  | 	// 	Match pages (paths in strict mode)
 | 
					
						
							|  |  |  | 	// 	.match(<pattern>, true)
 | 
					
						
							|  |  |  | 	// 		-> [<path>, ...]
 | 
					
						
							| 
									
										
										
										
											2022-05-22 14:23:21 +03:00
										 |  |  | 	// 		-> []
 | 
					
						
							| 
									
										
										
										
											2022-04-15 23:32:49 +03:00
										 |  |  | 	//
 | 
					
						
							|  |  |  | 	// In strict mode the trailing star in the pattern will only match 
 | 
					
						
							|  |  |  | 	// actual existing pages, while in non-strict mode the pattern will 
 | 
					
						
							|  |  |  | 	// match all sub-paths.
 | 
					
						
							|  |  |  | 	//
 | 
					
						
							| 
									
										
										
										
											2022-06-12 12:04:44 +03:00
										 |  |  | 	match: async function(path, strict=false){ | 
					
						
							| 
									
										
										
										
											2022-04-12 01:31:28 +03:00
										 |  |  | 		// pattern match * / **
 | 
					
						
							|  |  |  | 		if(path.includes('*')  | 
					
						
							|  |  |  | 				|| path.includes('**')){ | 
					
						
							| 
									
										
										
										
											2022-05-19 02:13:17 +03:00
										 |  |  | 			var order = (this.metadata(path) ?? {}).order || [] | 
					
						
							| 
									
										
										
										
											2022-04-21 11:43:53 +03:00
										 |  |  | 			// NOTE: we are matching full paths only here so leading and 
 | 
					
						
							|  |  |  | 			// 		trainling '/' are optional...
 | 
					
						
							| 
									
										
										
										
											2022-05-21 17:39:26 +03:00
										 |  |  | 			// NOTE: we ensure that we match full names and always split 
 | 
					
						
							|  |  |  | 			// 		at '/' only...
 | 
					
						
							| 
									
										
										
										
											2022-04-15 11:11:02 +03:00
										 |  |  | 			var pattern = new RegExp(`^\\/?${ | 
					
						
							| 
									
										
										
										
											2022-05-21 17:39:26 +03:00
										 |  |  | 					module.path.normalize(path, 'string') | 
					
						
							|  |  |  | 						.replace(/^\/|\/$/g, '') | 
					
						
							|  |  |  | 						.replace(/\//g, '\\/') | 
					
						
							|  |  |  | 						.replace(/\*\*/g, '.+') | 
					
						
							|  |  |  | 						.replace(/\*/g, '[^\\/]+')  | 
					
						
							|  |  |  | 				}(?=[\\\\\/]|$)`)
 | 
					
						
							| 
									
										
										
										
											2022-06-12 12:04:44 +03:00
										 |  |  | 			return [...(await this.paths()) | 
					
						
							| 
									
										
										
										
											2022-05-21 17:39:26 +03:00
										 |  |  | 					// NOTE: we are not using .filter(..) here as wee 
 | 
					
						
							|  |  |  | 					// 		need to keep parts of the path only and not 
 | 
					
						
							|  |  |  | 					// 		return the whole thing...
 | 
					
						
							| 
									
										
										
										
											2022-05-19 01:57:41 +03:00
										 |  |  | 					.reduce(function(res, p){ | 
					
						
							|  |  |  | 						// skip metadata paths...
 | 
					
						
							|  |  |  | 						if(p.includes('*')){ | 
					
						
							|  |  |  | 							return res } | 
					
						
							|  |  |  | 						var m = p.match(pattern) | 
					
						
							|  |  |  | 						m | 
					
						
							|  |  |  | 							&& (!strict  | 
					
						
							|  |  |  | 								|| m[0] == p)  | 
					
						
							|  |  |  | 							&& res.add(m[0]) | 
					
						
							|  |  |  | 						return res }, new Set())] | 
					
						
							|  |  |  | 			   .sortAs(order) } | 
					
						
							| 
									
										
										
										
											2022-05-25 01:01:22 +03:00
										 |  |  | 		// direct search...
 | 
					
						
							|  |  |  | 		return this.find(path) }, | 
					
						
							| 
									
										
										
										
											2022-05-22 14:23:21 +03:00
										 |  |  | 	//
 | 
					
						
							|  |  |  | 	// 	.resolve(<path>)
 | 
					
						
							|  |  |  | 	// 		-> <path>
 | 
					
						
							|  |  |  | 	//
 | 
					
						
							|  |  |  | 	// 	.resolve(<pattern>)
 | 
					
						
							|  |  |  | 	// 		-> [<path>, ...]
 | 
					
						
							|  |  |  | 	// 		-> []
 | 
					
						
							|  |  |  | 	//
 | 
					
						
							|  |  |  | 	// This is like .match(..) for non-pattern paths and paths ending 
 | 
					
						
							|  |  |  | 	// with '/'; When patterns end with a non-pattern then match the 
 | 
					
						
							|  |  |  | 	// basedir and add the basename to each resulting path...
 | 
					
						
							|  |  |  | 	//
 | 
					
						
							|  |  |  | 	// XXX should this be used by .get(..) instead of .match(..)???
 | 
					
						
							| 
									
										
										
										
											2022-05-21 17:39:26 +03:00
										 |  |  | 	// XXX EXPERIMENTAL 
 | 
					
						
							| 
									
										
										
										
											2022-06-12 12:04:44 +03:00
										 |  |  | 	resolve: async function(path, strict){ | 
					
						
							| 
									
										
										
										
											2022-05-21 17:39:26 +03:00
										 |  |  | 		// pattern match * / **
 | 
					
						
							|  |  |  | 		if(path.includes('*')  | 
					
						
							|  |  |  | 				|| path.includes('**')){ | 
					
						
							|  |  |  | 			path = module.path.split(path) | 
					
						
							|  |  |  | 			// match basedir and addon basename to the result...
 | 
					
						
							|  |  |  | 			var name = path[path.length-1] | 
					
						
							|  |  |  | 			if(name  | 
					
						
							|  |  |  | 					&& name != ''  | 
					
						
							|  |  |  | 					&& !name.includes('*')){ | 
					
						
							|  |  |  | 				path.pop() | 
					
						
							|  |  |  | 				path.push('') | 
					
						
							| 
									
										
										
										
											2022-06-12 12:04:44 +03:00
										 |  |  | 				return (await this.match(path.join('/'), strict)) | 
					
						
							| 
									
										
										
										
											2022-05-21 17:39:26 +03:00
										 |  |  | 					.map(function(p){ | 
					
						
							| 
									
										
										
										
											2022-05-22 13:51:29 +03:00
										 |  |  | 						return module.path.join(p, name) }) } } | 
					
						
							| 
									
										
										
										
											2022-05-21 17:39:26 +03:00
										 |  |  | 		// direct...
 | 
					
						
							| 
									
										
										
										
											2022-05-22 15:06:30 +03:00
										 |  |  | 		return this.match(path, strict) }, | 
					
						
							| 
									
										
										
										
											2022-04-15 23:32:49 +03:00
										 |  |  | 	// 
 | 
					
						
							|  |  |  | 	// 	Resolve page
 | 
					
						
							|  |  |  | 	// 	.get(<path>)
 | 
					
						
							|  |  |  | 	// 		-> <value>
 | 
					
						
							|  |  |  | 	//
 | 
					
						
							|  |  |  | 	// 	Resolve pages (non-strict mode)
 | 
					
						
							|  |  |  | 	// 	.get(<pattern>)
 | 
					
						
							|  |  |  | 	// 	.get(<pattern>, false)
 | 
					
						
							|  |  |  | 	// 		-> [<value>, .. ]
 | 
					
						
							|  |  |  | 	//
 | 
					
						
							|  |  |  | 	// 	Get pages (strict mode)
 | 
					
						
							|  |  |  | 	// 	.get(<pattern>, true)
 | 
					
						
							|  |  |  | 	// 		-> [<value>, .. ]
 | 
					
						
							|  |  |  | 	//
 | 
					
						
							|  |  |  | 	// In strict mode this will not try to resolve pages and will not 
 | 
					
						
							|  |  |  | 	// return pages at paths that do not explicitly exist.
 | 
					
						
							|  |  |  | 	//
 | 
					
						
							| 
									
										
										
										
											2022-04-12 02:03:33 +03:00
										 |  |  | 	// XXX should this call actions???
 | 
					
						
							| 
									
										
										
										
											2022-04-21 11:43:53 +03:00
										 |  |  | 	// XXX should this return a map for pattern matches???
 | 
					
						
							| 
									
										
										
										
											2022-06-12 12:04:44 +03:00
										 |  |  | 	__get__: async function(key){ | 
					
						
							| 
									
										
										
										
											2022-05-04 18:26:02 +03:00
										 |  |  | 		return this.data[key] }, | 
					
						
							| 
									
										
										
										
											2022-06-12 12:04:44 +03:00
										 |  |  | 	get: async function(path, strict=false){ | 
					
						
							| 
									
										
										
										
											2022-04-12 01:31:28 +03:00
										 |  |  | 		var that = this | 
					
						
							| 
									
										
										
										
											2022-05-22 15:06:30 +03:00
										 |  |  | 		//path = this.match(path, strict)
 | 
					
						
							| 
									
										
										
										
											2022-06-12 12:04:44 +03:00
										 |  |  | 		path = await this.resolve(path, strict) | 
					
						
							| 
									
										
										
										
											2022-04-12 01:31:28 +03:00
										 |  |  | 		return path instanceof Array ? | 
					
						
							| 
									
										
										
										
											2022-04-21 11:43:53 +03:00
										 |  |  | 			// XXX should we return matched paths???
 | 
					
						
							| 
									
										
										
										
											2022-06-15 16:10:16 +03:00
										 |  |  |    			Promise.iter(path) | 
					
						
							|  |  |  | 				.map(function(p){ | 
					
						
							|  |  |  | 					// NOTE: p can match a non existing page at this point, 
 | 
					
						
							|  |  |  | 					// 		this can be the result of matching a/* in a a/b/c
 | 
					
						
							|  |  |  | 					// 		and returning a a/b which can be undefined...
 | 
					
						
							|  |  |  | 					return that.get(p) }) | 
					
						
							| 
									
										
										
										
											2022-06-12 12:04:44 +03:00
										 |  |  | 			: (await this.__get__(path)  | 
					
						
							| 
									
										
										
										
											2022-05-04 18:26:02 +03:00
										 |  |  | 				// XXX NEXT
 | 
					
						
							|  |  |  | 				?? ((this.next || {}).__get__  | 
					
						
							|  |  |  | 					&& this.next.__get__(path))) }, | 
					
						
							| 
									
										
										
										
											2022-04-12 01:31:28 +03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-05-19 02:13:17 +03:00
										 |  |  | 	//
 | 
					
						
							|  |  |  | 	// 	Get metadata...
 | 
					
						
							|  |  |  | 	// 	.metadata(<path>)
 | 
					
						
							|  |  |  | 	// 		-> <metadata>
 | 
					
						
							|  |  |  | 	// 		-> undefined 
 | 
					
						
							|  |  |  | 	//
 | 
					
						
							|  |  |  | 	//	Set metadata...
 | 
					
						
							|  |  |  | 	//	.metadata(<path>, <data>[, <mode>])
 | 
					
						
							|  |  |  | 	//	.update(<path>, <data>[, <mode>])
 | 
					
						
							|  |  |  | 	//
 | 
					
						
							|  |  |  | 	//	Delete metadata...
 | 
					
						
							|  |  |  | 	//	.delete(<path>)
 | 
					
						
							|  |  |  | 	//
 | 
					
						
							|  |  |  | 	// NOTE: .metadata(..) is the same as .data but supports pattern paths 
 | 
					
						
							|  |  |  | 	// 		and does not try to acquire a target page.
 | 
					
						
							|  |  |  | 	// NOTE: setting/removing metadata is done via .update(..) / .delete(..)
 | 
					
						
							|  |  |  | 	// NOTE: this uses .__get__(..) internally...
 | 
					
						
							| 
									
										
										
										
											2022-06-12 12:04:44 +03:00
										 |  |  | 	metadata: async function(path, ...args){ | 
					
						
							| 
									
										
										
										
											2022-05-19 02:13:17 +03:00
										 |  |  | 		// set...
 | 
					
						
							|  |  |  | 		if(args.length > 0){ | 
					
						
							|  |  |  | 			return this.update(path, ...args) } | 
					
						
							|  |  |  | 		// get...
 | 
					
						
							| 
									
										
										
										
											2022-06-12 12:04:44 +03:00
										 |  |  | 		path = await this.exists(path) | 
					
						
							| 
									
										
										
										
											2022-05-19 01:57:41 +03:00
										 |  |  | 		return path  | 
					
						
							| 
									
										
										
										
											2022-06-12 12:04:44 +03:00
										 |  |  | 			&& await this.__get__(path)  | 
					
						
							| 
									
										
										
										
											2022-05-19 02:13:17 +03:00
										 |  |  | 			|| undefined }, | 
					
						
							| 
									
										
										
										
											2022-05-19 01:57:41 +03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-04-12 01:31:28 +03:00
										 |  |  | 	// NOTE: deleting and updating only applies to explicit matching 
 | 
					
						
							|  |  |  | 	// 		paths -- no page acquisition is performed...
 | 
					
						
							| 
									
										
										
										
											2022-05-04 16:14:57 +03:00
										 |  |  | 	// NOTE: edit methods are local-only...
 | 
					
						
							| 
									
										
										
										
											2022-04-15 23:32:49 +03:00
										 |  |  | 	//
 | 
					
						
							| 
									
										
										
										
											2022-05-09 00:49:05 +03:00
										 |  |  | 	// XXX do we copy the data here or modify it????
 | 
					
						
							| 
									
										
										
										
											2022-06-12 12:04:44 +03:00
										 |  |  | 	__update__: async function(key, data, mode='update'){ | 
					
						
							|  |  |  | 		this.data[key] = data }, | 
					
						
							|  |  |  | 	update: async function(path, data, mode='update'){ | 
					
						
							|  |  |  | 		var exists = await this.exists(path)  | 
					
						
							| 
									
										
										
										
											2022-05-10 23:58:13 +03:00
										 |  |  | 		path = exists | 
					
						
							| 
									
										
										
										
											2022-05-06 23:08:53 +03:00
										 |  |  | 			|| module.path.normalize(path, 'string') | 
					
						
							| 
									
										
										
										
											2022-06-12 12:04:44 +03:00
										 |  |  | 		data = data instanceof Promise ? | 
					
						
							|  |  |  | 			await data | 
					
						
							|  |  |  | 			: data | 
					
						
							| 
									
										
										
										
											2022-05-11 15:27:15 +03:00
										 |  |  | 		data =  | 
					
						
							| 
									
										
										
										
											2022-05-22 15:06:30 +03:00
										 |  |  | 			typeof(data) == 'function' ? | 
					
						
							| 
									
										
										
										
											2022-05-11 15:27:15 +03:00
										 |  |  | 				data | 
					
						
							|  |  |  | 				: Object.assign( | 
					
						
							|  |  |  | 					{ | 
					
						
							|  |  |  | 						__proto__: data.__proto__, | 
					
						
							|  |  |  | 						ctime: Date.now(), | 
					
						
							|  |  |  | 					}, | 
					
						
							|  |  |  | 					(mode == 'update' && exists) ? | 
					
						
							| 
									
										
										
										
											2022-06-12 12:04:44 +03:00
										 |  |  | 						await this.get(path) | 
					
						
							| 
									
										
										
										
											2022-05-11 15:27:15 +03:00
										 |  |  | 						: {}, | 
					
						
							|  |  |  | 					data, | 
					
						
							|  |  |  | 					{mtime: Date.now()}) | 
					
						
							| 
									
										
										
										
											2022-06-12 12:04:44 +03:00
										 |  |  | 		await this.__update__(path, data, mode) | 
					
						
							| 
									
										
										
										
											2022-04-12 01:31:28 +03:00
										 |  |  | 		return this }, | 
					
						
							| 
									
										
										
										
											2022-06-12 12:04:44 +03:00
										 |  |  | 	__delete__: async function(path){ | 
					
						
							|  |  |  | 		delete this.data[path] }, | 
					
						
							|  |  |  | 	delete: async function(path){ | 
					
						
							|  |  |  | 		path = await this.exists(path) | 
					
						
							| 
									
										
										
										
											2022-05-06 23:08:53 +03:00
										 |  |  | 		path | 
					
						
							| 
									
										
										
										
											2022-06-12 12:04:44 +03:00
										 |  |  | 			&& await this.__delete__(path) | 
					
						
							| 
									
										
										
										
											2022-05-03 16:15:16 +03:00
										 |  |  | 		return this }, | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-05-04 16:14:57 +03:00
										 |  |  | 	// XXX NEXT might be a good idea to have an API to move pages from 
 | 
					
						
							|  |  |  | 	// 		current store up the chain...
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-05-03 16:15:16 +03:00
										 |  |  | 	// XXX do we need this???
 | 
					
						
							| 
									
										
										
										
											2022-06-12 12:04:44 +03:00
										 |  |  | 	load: async function(...data){ | 
					
						
							| 
									
										
										
										
											2022-05-11 15:27:15 +03:00
										 |  |  | 		this.data = Object.assign(this.data, ...data) | 
					
						
							| 
									
										
										
										
											2022-04-12 01:31:28 +03:00
										 |  |  | 		return this }, | 
					
						
							| 
									
										
										
										
											2022-06-12 12:04:44 +03:00
										 |  |  | 	json: function(asstring=false){ | 
					
						
							|  |  |  | 		// XXX
 | 
					
						
							|  |  |  | 	}, | 
					
						
							| 
									
										
										
										
											2022-05-04 16:50:07 +03:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	// XXX NEXT EXPERIMENTAL...
 | 
					
						
							|  |  |  | 	nest: function(base){ | 
					
						
							|  |  |  | 		return { | 
					
						
							|  |  |  | 			__proto__: base  | 
					
						
							|  |  |  | 				?? module.BaseStore, | 
					
						
							|  |  |  | 			next: this, | 
					
						
							|  |  |  | 			data: {} | 
					
						
							|  |  |  | 		} }, | 
					
						
							| 
									
										
										
										
											2022-04-12 01:31:28 +03:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-04-12 02:03:33 +03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-05-08 23:36:59 +03:00
										 |  |  | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 
 | 
					
						
							| 
									
										
										
										
											2022-05-09 08:52:34 +03:00
										 |  |  | //
 | 
					
						
							| 
									
										
										
										
											2022-05-05 10:50:12 +03:00
										 |  |  | // XXX stores to experiment with:
 | 
					
						
							|  |  |  | // 		- cache
 | 
					
						
							|  |  |  | // 		- fs
 | 
					
						
							|  |  |  | // 		- PouchDB
 | 
					
						
							| 
									
										
										
										
											2022-05-09 08:52:34 +03:00
										 |  |  | //
 | 
					
						
							|  |  |  | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 
 | 
					
						
							|  |  |  | // Meta-Store
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | // Extends BaseStore to handle other stores as pages. i.e. sub-paths can 
 | 
					
						
							|  |  |  | // be handled by nested stores.
 | 
					
						
							|  |  |  | //
 | 
					
						
							| 
									
										
										
										
											2022-05-05 10:50:12 +03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-05-08 00:28:25 +03:00
										 |  |  | // XXX might be a good idea to normalize args...
 | 
					
						
							| 
									
										
										
										
											2022-05-07 03:09:04 +03:00
										 |  |  | var metaProxy =  | 
					
						
							| 
									
										
										
										
											2022-05-08 23:36:59 +03:00
										 |  |  | function(meth, drop_cache=false, post){ | 
					
						
							|  |  |  | 	var target = meth.replace(/__/g, '') | 
					
						
							| 
									
										
										
										
											2022-05-22 15:06:30 +03:00
										 |  |  | 	if(typeof(drop_cache) == 'function'){ | 
					
						
							| 
									
										
										
										
											2022-05-08 23:36:59 +03:00
										 |  |  | 		post = drop_cache | 
					
						
							| 
									
										
										
										
											2022-05-08 10:53:03 +03:00
										 |  |  | 		drop_cache = false } | 
					
						
							| 
									
										
										
										
											2022-06-12 12:04:44 +03:00
										 |  |  | 	return async function(path, ...args){ | 
					
						
							| 
									
										
										
										
											2022-05-07 03:09:04 +03:00
										 |  |  | 		var store = this.substore(path) | 
					
						
							| 
									
										
										
										
											2022-05-10 23:58:13 +03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-05-08 10:53:03 +03:00
										 |  |  | 		var res = store == null ? | 
					
						
							| 
									
										
										
										
											2022-05-07 03:09:04 +03:00
										 |  |  | 			object.parentCall(MetaStore, meth, this, path, ...args) | 
					
						
							| 
									
										
										
										
											2022-05-08 23:36:59 +03:00
										 |  |  | 			//: this.data[store][meth](path.slice(store.length), ...args) 
 | 
					
						
							|  |  |  | 			: this.data[store][target](path.slice(store.length), ...args)  | 
					
						
							| 
									
										
										
										
											2022-05-10 23:58:13 +03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-05-08 10:53:03 +03:00
										 |  |  | 		if(drop_cache){ | 
					
						
							|  |  |  | 			delete this.__substores } | 
					
						
							| 
									
										
										
										
											2022-05-08 23:36:59 +03:00
										 |  |  | 		post  | 
					
						
							| 
									
										
										
										
											2022-06-12 12:04:44 +03:00
										 |  |  | 			&& (res = post.call(this, await res, store, path, ...args)) | 
					
						
							| 
									
										
										
										
											2022-05-08 10:53:03 +03:00
										 |  |  | 		return res} } | 
					
						
							| 
									
										
										
										
											2022-05-07 03:09:04 +03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-05-08 23:36:59 +03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-05-09 00:49:05 +03:00
										 |  |  | // XXX not sure about the name...
 | 
					
						
							| 
									
										
										
										
											2022-05-07 01:02:44 +03:00
										 |  |  | // XXX should this be a mixin???
 | 
					
						
							|  |  |  | var MetaStore = | 
					
						
							|  |  |  | module.MetaStore = { | 
					
						
							|  |  |  | 	__proto__: BaseStore, | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	//data: undefined,
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	__substores: undefined, | 
					
						
							|  |  |  | 	get substores(){ | 
					
						
							|  |  |  | 		return this.__substores  | 
					
						
							|  |  |  | 			?? (this.__substores = Object.entries(this.data) | 
					
						
							|  |  |  | 				.filter(function([path, value]){ | 
					
						
							| 
									
										
										
										
											2022-05-08 00:28:25 +03:00
										 |  |  | 					return object.childOf(value, module.BaseStore) }) | 
					
						
							| 
									
										
										
										
											2022-05-07 01:02:44 +03:00
										 |  |  | 				.map(function([path, _]){ | 
					
						
							|  |  |  | 					return path })) }, | 
					
						
							|  |  |  | 	substore: function(path){ | 
					
						
							|  |  |  | 		path = module.path.normalize(path, 'string') | 
					
						
							| 
									
										
										
										
											2022-05-07 03:09:04 +03:00
										 |  |  | 		var store = this.substores | 
					
						
							| 
									
										
										
										
											2022-05-07 01:02:44 +03:00
										 |  |  | 			.filter(function(p){ | 
					
						
							|  |  |  | 				return path.startsWith(p) }) | 
					
						
							|  |  |  | 			.sort(function(a, b){ | 
					
						
							|  |  |  | 				return a.length - b.length }) | 
					
						
							|  |  |  | 			.pop()  | 
					
						
							|  |  |  | 		return store == path ? | 
					
						
							|  |  |  | 			// the actual store is not stored within itself...
 | 
					
						
							|  |  |  | 			undefined | 
					
						
							|  |  |  | 			: store }, | 
					
						
							|  |  |  | 	 | 
					
						
							| 
									
										
										
										
											2022-06-12 12:04:44 +03:00
										 |  |  | 	// XXX this depends on .data having keys...
 | 
					
						
							|  |  |  | 	__paths__: async function(){ | 
					
						
							| 
									
										
										
										
											2022-05-07 01:02:44 +03:00
										 |  |  | 		var that = this | 
					
						
							|  |  |  | 		var data = this.data | 
					
						
							| 
									
										
										
										
											2022-06-12 12:04:44 +03:00
										 |  |  | 		//return Object.keys(data)
 | 
					
						
							|  |  |  | 		return Promise.iter(Object.keys(data) | 
					
						
							| 
									
										
										
										
											2022-05-07 01:02:44 +03:00
										 |  |  | 			.map(function(path){ | 
					
						
							| 
									
										
										
										
											2022-05-08 00:28:25 +03:00
										 |  |  | 				return object.childOf(data[path], module.BaseStore) ? | 
					
						
							| 
									
										
										
										
											2022-06-12 12:04:44 +03:00
										 |  |  | 					data[path].paths() | 
					
						
							|  |  |  | 						.iter() | 
					
						
							| 
									
										
										
										
											2022-05-09 00:18:45 +03:00
										 |  |  | 						.map(function(s){ | 
					
						
							|  |  |  | 							return module.path.join(path, s) }) | 
					
						
							| 
									
										
										
										
											2022-06-12 12:04:44 +03:00
										 |  |  | 				: path })) | 
					
						
							| 
									
										
										
										
											2022-05-07 01:02:44 +03:00
										 |  |  | 			.flat() }, | 
					
						
							| 
									
										
										
										
											2022-05-08 23:36:59 +03:00
										 |  |  | 	// XXX revise...
 | 
					
						
							|  |  |  | 	__exists__: metaProxy('__exists__',  | 
					
						
							|  |  |  | 		function(res, store, path){ | 
					
						
							|  |  |  | 			return store ? | 
					
						
							|  |  |  | 				// XXX which way should we go???
 | 
					
						
							|  |  |  | 				//module.path.join(store, res)
 | 
					
						
							|  |  |  | 				path | 
					
						
							|  |  |  | 	   			: res }), | 
					
						
							| 
									
										
										
										
											2022-05-07 03:09:04 +03:00
										 |  |  | 	__get__: metaProxy('__get__'), | 
					
						
							| 
									
										
										
										
											2022-05-08 00:28:25 +03:00
										 |  |  | 	__delete__: metaProxy('__delete__', true), | 
					
						
							| 
									
										
										
										
											2022-05-08 10:53:03 +03:00
										 |  |  | 	__update__: metaProxy('__update__', true), | 
					
						
							| 
									
										
										
										
											2022-06-12 12:04:44 +03:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	json: function(asstring=false){ | 
					
						
							|  |  |  | 		// XXX
 | 
					
						
							|  |  |  | 	}, | 
					
						
							| 
									
										
										
										
											2022-05-07 01:02:44 +03:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-05-08 23:36:59 +03:00
										 |  |  | 
 | 
					
						
							|  |  |  | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-05-07 01:02:44 +03:00
										 |  |  | // XXX EXPERIMENTAL, needs testing in browser...
 | 
					
						
							| 
									
										
										
										
											2022-05-04 16:50:07 +03:00
										 |  |  | var localStorageStore = | 
					
						
							|  |  |  | module.localStorageStore = { | 
					
						
							| 
									
										
										
										
											2022-05-06 18:27:41 +03:00
										 |  |  | 	__proto__: BaseStore, | 
					
						
							|  |  |  | 	__prefix__: '--pwiki:', | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-05-07 01:02:44 +03:00
										 |  |  | 	// XXX add caching of unserialized data???
 | 
					
						
							| 
									
										
										
										
											2022-05-06 18:27:41 +03:00
										 |  |  | 	data: | 
					
						
							| 
									
										
										
										
											2022-05-04 18:26:02 +03:00
										 |  |  | 		typeof(localStorage) != 'undefined' ? | 
					
						
							|  |  |  | 			localStorage | 
					
						
							|  |  |  | 			: undefined, | 
					
						
							|  |  |  | 	 | 
					
						
							| 
									
										
										
										
											2022-06-15 16:10:16 +03:00
										 |  |  | 	__paths__: function(){ | 
					
						
							| 
									
										
										
										
											2022-05-06 18:27:41 +03:00
										 |  |  | 		var that = this | 
					
						
							|  |  |  | 		return Object.keys(this.data) | 
					
						
							|  |  |  | 			.map(function(k){  | 
					
						
							|  |  |  | 				return k.startsWith(that.__prefix__) ? | 
					
						
							|  |  |  | 					k.slice((that.__prefix__ ?? '').length)  | 
					
						
							|  |  |  | 					: [] })  | 
					
						
							|  |  |  | 			.flat() }, | 
					
						
							| 
									
										
										
										
											2022-06-15 16:10:16 +03:00
										 |  |  | 	__exists__: function(path){ | 
					
						
							| 
									
										
										
										
											2022-05-06 18:27:41 +03:00
										 |  |  | 		return ((this.__prefix__ ?? '')+ path) in this.data  | 
					
						
							|  |  |  | 			&& path }, | 
					
						
							| 
									
										
										
										
											2022-06-15 16:10:16 +03:00
										 |  |  | 	__get__: function(path){ | 
					
						
							| 
									
										
										
										
											2022-05-06 18:27:41 +03:00
										 |  |  | 		path = (this.__prefix__ ?? '')+ path | 
					
						
							|  |  |  | 		return path in this.data ? | 
					
						
							|  |  |  | 			JSON.parse(this.data[path])  | 
					
						
							|  |  |  | 			: undefined }, | 
					
						
							| 
									
										
										
										
											2022-06-15 16:10:16 +03:00
										 |  |  | 	__update__: function(path, data={}){ | 
					
						
							| 
									
										
										
										
											2022-05-06 23:08:53 +03:00
										 |  |  | 		this.data[(this.__prefix__ ?? '')+ path] =  | 
					
						
							| 
									
										
										
										
											2022-06-12 12:04:44 +03:00
										 |  |  | 			JSON.stringify(data) }, | 
					
						
							| 
									
										
										
										
											2022-06-15 16:10:16 +03:00
										 |  |  | 	__delete__: function(path){ | 
					
						
							| 
									
										
										
										
											2022-06-12 12:04:44 +03:00
										 |  |  | 		delete this.data[(this.__prefix__ ?? '')+ path] }, | 
					
						
							| 
									
										
										
										
											2022-05-04 18:26:02 +03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-05-07 01:02:44 +03:00
										 |  |  | 	// XXX 
 | 
					
						
							| 
									
										
										
										
											2022-06-15 16:10:16 +03:00
										 |  |  | 	load: function(){ | 
					
						
							| 
									
										
										
										
											2022-05-06 23:08:53 +03:00
										 |  |  | 	}, | 
					
						
							| 
									
										
										
										
											2022-05-04 16:50:07 +03:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-05-07 01:02:44 +03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-05-08 23:36:59 +03:00
										 |  |  | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-05-06 18:27:41 +03:00
										 |  |  | var localStorageNestedStore = | 
					
						
							|  |  |  | module.localStorageNestedStore = { | 
					
						
							|  |  |  | 	__proto__: BaseStore, | 
					
						
							|  |  |  | 	__data__: '__pwiki_data__', | 
					
						
							|  |  |  | 	__cache__: '__pwiki_cache__', | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	__data: undefined, | 
					
						
							|  |  |  | 	get data(){ | 
					
						
							|  |  |  | 		return this.__data  | 
					
						
							|  |  |  | 			?? (this.__data = | 
					
						
							|  |  |  | 				Object.assign( | 
					
						
							|  |  |  | 					{ __proto__: JSON.parse(localStorage[this.__data__] || '{}') }, | 
					
						
							|  |  |  | 					JSON.parse(localStorage[this.__cache__] || '{}') )) }, | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// XXX do partials saves -> cache + write cache...
 | 
					
						
							|  |  |  | 	// XXX on full save merge cache and save...
 | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-05-08 23:36:59 +03:00
										 |  |  | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-06-12 12:04:44 +03:00
										 |  |  | var fs = require('fs') | 
					
						
							|  |  |  | var glob = require('glob') | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // XXX add monitor API...
 | 
					
						
							|  |  |  | // XXX backup files on write/delete...
 | 
					
						
							|  |  |  | // XXX do a r/o version...
 | 
					
						
							| 
									
										
										
										
											2022-05-07 01:02:44 +03:00
										 |  |  | var FileStore = | 
					
						
							|  |  |  | module.FileStore = { | 
					
						
							|  |  |  | 	__proto__: BaseStore, | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// XXX
 | 
					
						
							| 
									
										
										
										
											2022-06-12 12:04:44 +03:00
										 |  |  | 	__path__: 'store/fs', | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// XXX do we remove the extension???
 | 
					
						
							|  |  |  | 	// XXX cache???
 | 
					
						
							|  |  |  | 	__paths__: async function(){ | 
					
						
							|  |  |  | 		var that = this | 
					
						
							|  |  |  | 		return new Promise(function(resolve, reject){ | 
					
						
							|  |  |  | 			glob(module.path.join(that.__path__, '/**/*')) | 
					
						
							|  |  |  | 				.on('end', function(paths){ | 
					
						
							|  |  |  | 					resolve(paths | 
					
						
							|  |  |  | 						.map(function(path){  | 
					
						
							|  |  |  | 							// XXX need better .__path__ removal...
 | 
					
						
							|  |  |  | 							return path | 
					
						
							|  |  |  | 								.slice(that.__path__.length) })) }) }) }, | 
					
						
							|  |  |  | 	__exists__: async function(path){ | 
					
						
							|  |  |  | 		return !!fs.existsSync(module.path.join(this.__path__, path)) ? | 
					
						
							|  |  |  | 			path | 
					
						
							|  |  |  | 			: false }, | 
					
						
							|  |  |  | 	__get__: async function(path){ | 
					
						
							|  |  |  | 		var p = module.path.join(this.__path__, path) | 
					
						
							|  |  |  | 		var {atimeMs, mtimeMs, ctimeMs, birthtimeMs} = await fs.promises.stat(p) | 
					
						
							|  |  |  | 		return { | 
					
						
							|  |  |  | 			atime: atimeMs, | 
					
						
							|  |  |  | 			mtime: mtimeMs, | 
					
						
							|  |  |  | 			ctime: ctimeMs, | 
					
						
							|  |  |  | 			text: fs.readFileSync(p).toString(), | 
					
						
							|  |  |  | 		} }, | 
					
						
							|  |  |  | 	// XXX do we write all the data or only the .text???
 | 
					
						
							|  |  |  | 	__update__: async function(path, data, mode='update'){ | 
					
						
							|  |  |  | 		var p = module.path.join(this.__path__, path) | 
					
						
							|  |  |  | 		var f = await fs.promises.open(p, 'w') | 
					
						
							|  |  |  | 		var size = await f.writeFile(data.text) | 
					
						
							|  |  |  | 		f.close() | 
					
						
							|  |  |  | 		// XXX check size...
 | 
					
						
							|  |  |  | 		// XXX
 | 
					
						
							|  |  |  | 	}, | 
					
						
							|  |  |  |     __delete__: async function(path){ | 
					
						
							|  |  |  | 		var p = module.path.join(this.__path__, path) | 
					
						
							|  |  |  | 		// XXX
 | 
					
						
							|  |  |  | 	}, | 
					
						
							|  |  |  | 	load: function(data){ | 
					
						
							| 
									
										
										
										
											2022-06-15 16:10:16 +03:00
										 |  |  | 		// XXX
 | 
					
						
							| 
									
										
										
										
											2022-06-12 12:04:44 +03:00
										 |  |  | 	}, | 
					
						
							|  |  |  | 	json: function(asstring=false){ | 
					
						
							| 
									
										
										
										
											2022-06-15 16:10:16 +03:00
										 |  |  | 		// XXX
 | 
					
						
							| 
									
										
										
										
											2022-06-12 12:04:44 +03:00
										 |  |  | 	}, | 
					
						
							| 
									
										
										
										
											2022-05-07 01:02:44 +03:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-05-08 23:36:59 +03:00
										 |  |  | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-05-27 17:13:43 +03:00
										 |  |  | 
 | 
					
						
							|  |  |  | // XXX 
 | 
					
						
							|  |  |  | module.PouchDB = undefined | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-05-07 01:02:44 +03:00
										 |  |  | var PouchDBStore = | 
					
						
							|  |  |  | module.PouchDBStore = { | 
					
						
							|  |  |  | 	__proto__: BaseStore, | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-05-27 17:13:43 +03:00
										 |  |  | 	__name__: 'pWiki-test-store', | 
					
						
							|  |  |  | 	__key_prefix__: 'pwiki:', | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	__data: undefined, | 
					
						
							|  |  |  | 	get data(){ | 
					
						
							|  |  |  | 		if(!this.__data){ | 
					
						
							|  |  |  | 			var PouchDB =  | 
					
						
							|  |  |  | 			module.PouchDB =  | 
					
						
							|  |  |  | 				require('PouchDB') | 
					
						
							|  |  |  | 			return (this.__data = new PouchDB(this.__name__)) } | 
					
						
							|  |  |  | 		return this.__data }, | 
					
						
							|  |  |  | 	set data(value){ | 
					
						
							|  |  |  | 		this.__data = value }, | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// XXX cache???
 | 
					
						
							|  |  |  | 	__paths__: async function(){ | 
					
						
							|  |  |  | 		var that = this | 
					
						
							|  |  |  | 		// XXX not sure if this is a good idea...
 | 
					
						
							|  |  |  | 		return (await this.data.allDocs()).rows | 
					
						
							|  |  |  | 			.map(function(e){  | 
					
						
							|  |  |  | 				return e.id.slice(that.__key_prefix__.length) }) }, | 
					
						
							|  |  |  | 	// XXX use an index...
 | 
					
						
							|  |  |  | 	__exists__: async function(key){ | 
					
						
							|  |  |  | 		return !! await this.__get__(key) }, | 
					
						
							|  |  |  | 	__get__: async function(key){ | 
					
						
							|  |  |  | 		try{ | 
					
						
							|  |  |  | 			return await this.data.get(this.__key_prefix__ + key)  | 
					
						
							|  |  |  | 		}catch(err){ | 
					
						
							|  |  |  | 			return undefined } }, | 
					
						
							|  |  |  | 	__update__: async function(key, data, mode='update'){ | 
					
						
							|  |  |  | 		var {_id, _rev, ...rest} = await this.__get__(key) ?? {} | 
					
						
							|  |  |  | 		await this.data.put({ | 
					
						
							|  |  |  | 			// original data...
 | 
					
						
							|  |  |  | 			...( (mode == 'update') ? | 
					
						
							|  |  |  | 				rest | 
					
						
							|  |  |  | 				: {}), | 
					
						
							|  |  |  | 			// new data...
 | 
					
						
							|  |  |  | 			...data, | 
					
						
							|  |  |  | 			// system...
 | 
					
						
							|  |  |  | 			_id: _id  | 
					
						
							|  |  |  | 				?? (this.__key_prefix__ + key), | 
					
						
							|  |  |  | 			...(_rev ?  | 
					
						
							|  |  |  | 				{_rev}  | 
					
						
							|  |  |  | 				: {}), | 
					
						
							|  |  |  | 		}) | 
					
						
							|  |  |  | 		return this }, | 
					
						
							|  |  |  |     __delete__: async function(key){ | 
					
						
							|  |  |  | 		var doc = await this.__get__(key) | 
					
						
							|  |  |  | 		doc  | 
					
						
							|  |  |  | 			&& (await this.data.remove(doc)) | 
					
						
							|  |  |  | 		return this }, | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // XXX overload...
 | 
					
						
							|  |  |  | //    .load(..)
 | 
					
						
							| 
									
										
										
										
											2022-05-07 01:02:44 +03:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-05-03 12:28:03 +03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-05-08 23:36:59 +03:00
										 |  |  | //---------------------------------------------------------------------
 | 
					
						
							| 
									
										
										
										
											2022-07-11 18:14:11 +03:00
										 |  |  | // Page...
 | 
					
						
							| 
									
										
										
										
											2022-04-12 02:07:19 +03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-04-12 02:03:33 +03:00
										 |  |  | var relProxy =  | 
					
						
							|  |  |  | function(name){ | 
					
						
							|  |  |  | 	return function(path='.', ...args){ | 
					
						
							|  |  |  | 		return this.store[name]( | 
					
						
							| 
									
										
										
										
											2022-04-22 12:00:48 +03:00
										 |  |  | 			module.path.relative(this.location, path),  | 
					
						
							| 
									
										
										
										
											2022-04-12 02:03:33 +03:00
										 |  |  | 			...args) } }  | 
					
						
							| 
									
										
										
										
											2022-05-22 15:06:30 +03:00
										 |  |  | var relMatchProxy =  | 
					
						
							|  |  |  | function(name){ | 
					
						
							|  |  |  | 	return function(path='.', strict=this.strict){ | 
					
						
							|  |  |  | 		if(path === true || path === false){ | 
					
						
							|  |  |  | 			strict = path | 
					
						
							|  |  |  | 			path = '.' } | 
					
						
							|  |  |  | 		return this.store[name]( | 
					
						
							|  |  |  | 			module.path.relative(this.location, path),  | 
					
						
							|  |  |  | 			strict) } } | 
					
						
							| 
									
										
										
										
											2022-04-12 02:03:33 +03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-05-26 00:26:28 +03:00
										 |  |  | var __HANDLE_NAVIGATE = | 
					
						
							|  |  |  | module.__HANDLE_NAVIGATE =  | 
					
						
							|  |  |  | 	types.event.EventCommand('HANDLE_NAVIGATE') | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-05-15 03:04:29 +03:00
										 |  |  | // XXX PATH_VARS
 | 
					
						
							| 
									
										
										
										
											2022-04-23 11:07:30 +03:00
										 |  |  | // XXX HISTORY do we need history management??? 
 | 
					
						
							|  |  |  | // XXX FUNC need to handle functions in store...
 | 
					
						
							| 
									
										
										
										
											2022-04-21 18:33:08 +03:00
										 |  |  | var BasePage = | 
					
						
							|  |  |  | module.BasePage =  | 
					
						
							|  |  |  | object.Constructor('BasePage', { | 
					
						
							| 
									
										
										
										
											2022-04-22 00:14:41 +03:00
										 |  |  | 	// NOTE: this can be inherited...
 | 
					
						
							|  |  |  | 	//store: undefined,
 | 
					
						
							|  |  |  | 	 | 
					
						
							|  |  |  | 	// root page used to clone new instances via the .clone(..) method...
 | 
					
						
							|  |  |  | 	//root: undefined,
 | 
					
						
							| 
									
										
										
										
											2022-05-24 10:45:53 +03:00
										 |  |  | 	 | 
					
						
							| 
									
										
										
										
											2022-05-12 09:04:33 +03:00
										 |  |  | 	// Path variables...
 | 
					
						
							|  |  |  | 	//
 | 
					
						
							| 
									
										
										
										
											2022-05-15 03:04:29 +03:00
										 |  |  | 	// XXX PATH_VARS should these be here???
 | 
					
						
							| 
									
										
										
										
											2022-05-12 09:04:33 +03:00
										 |  |  | 	// 		other places path variables can be resolved:
 | 
					
						
							|  |  |  | 	// 			- navigation (below)
 | 
					
						
							|  |  |  | 	// 			- macro expansion...
 | 
					
						
							| 
									
										
										
										
											2022-05-15 03:04:29 +03:00
										 |  |  | 	// XXX EXPERIMENTAL...
 | 
					
						
							| 
									
										
										
										
											2022-05-12 09:04:33 +03:00
										 |  |  | 	path_vars: { | 
					
						
							|  |  |  | 		NOW: function(){ | 
					
						
							|  |  |  | 			return Date.now() }, | 
					
						
							|  |  |  | 		PATH: function(){ | 
					
						
							|  |  |  | 			return this.path }, | 
					
						
							|  |  |  | 		NAME: function(){ | 
					
						
							|  |  |  | 			return this.name }, | 
					
						
							|  |  |  | 		DIR: function(){ | 
					
						
							|  |  |  | 			return this.dir }, | 
					
						
							|  |  |  | 		//TITLE: function(){
 | 
					
						
							|  |  |  | 		//	return this.title },
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		/*/ XXX this needs: | 
					
						
							|  |  |  | 		// 		- macro context...
 | 
					
						
							|  |  |  | 		// 		- sort order...
 | 
					
						
							|  |  |  | 		INDEX: function(context){ | 
					
						
							|  |  |  | 			return context.index }, | 
					
						
							|  |  |  | 		//*/
 | 
					
						
							|  |  |  | 	}, | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	resolvePathVars: function(path, context={}){ | 
					
						
							|  |  |  | 		var that = this | 
					
						
							|  |  |  | 		return Object.entries(this.path_vars) | 
					
						
							|  |  |  | 			.reduce(function(res, [key, func]){ | 
					
						
							|  |  |  | 				return res | 
					
						
							|  |  |  | 					.replace( | 
					
						
							|  |  |  | 						new RegExp('(\\${'+key+'}|\\$'+key+')', 'g'),  | 
					
						
							|  |  |  | 						func.call(that, context)) | 
					
						
							|  |  |  | 			}, path) }, | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-04-22 00:14:41 +03:00
										 |  |  | 	// page location...
 | 
					
						
							|  |  |  | 	//
 | 
					
						
							| 
									
										
										
										
											2022-05-12 09:04:33 +03:00
										 |  |  | 	// NOTE: path variables are resolved relative to the page BEFORE 
 | 
					
						
							|  |  |  | 	// 		navigation...
 | 
					
						
							| 
									
										
										
										
											2022-05-26 00:26:28 +03:00
										 |  |  | 	// NOTE: the actual work is done by the .onNavigate(..) method...
 | 
					
						
							| 
									
										
										
										
											2022-04-22 00:14:41 +03:00
										 |  |  | 	__location: undefined, | 
					
						
							|  |  |  | 	get location(){ | 
					
						
							|  |  |  | 		return this.__location ?? '/' }, | 
					
						
							|  |  |  | 	set location(path){ | 
					
						
							| 
									
										
										
										
											2022-05-26 00:26:28 +03:00
										 |  |  | 		// trigger the event...
 | 
					
						
							|  |  |  | 		this.onNavigate(path) }, | 
					
						
							| 
									
										
										
										
											2022-04-22 00:14:41 +03:00
										 |  |  | 	// referrer -- a previous page location...
 | 
					
						
							| 
									
										
										
										
											2022-04-14 20:58:09 +03:00
										 |  |  | 	referrer: undefined, | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-05-26 00:26:28 +03:00
										 |  |  | 	// events...
 | 
					
						
							|  |  |  | 	//
 | 
					
						
							|  |  |  | 	// XXX revise naming...
 | 
					
						
							|  |  |  | 	// XXX should this be able to prevent navigation???
 | 
					
						
							|  |  |  | 	onBeforeNavigate: types.event.Event('beforeNavigate'), | 
					
						
							|  |  |  | 	onNavigate: types.event.Event('navigate', | 
					
						
							|  |  |  | 		function(handle, path){ | 
					
						
							|  |  |  | 			// special case: we are triggering handlers only...
 | 
					
						
							|  |  |  | 			// NOTE: this usually means that we are setting .__location 
 | 
					
						
							|  |  |  | 			// 		externally...
 | 
					
						
							|  |  |  | 			// XXX HISTORY this is only used for history at this point...
 | 
					
						
							|  |  |  | 			if(path === module.__HANDLE_NAVIGATE){ | 
					
						
							|  |  |  | 				handle() | 
					
						
							|  |  |  | 				return } | 
					
						
							|  |  |  | 			this.onBeforeNavigate(path) | 
					
						
							|  |  |  | 			this.referrer = this.location | 
					
						
							|  |  |  | 			var cur = this.__location =  | 
					
						
							|  |  |  | 				this.resolvePathVars( | 
					
						
							|  |  |  | 					module.path.relative( | 
					
						
							|  |  |  | 						this.location,  | 
					
						
							|  |  |  | 						path)) | 
					
						
							|  |  |  | 			//* XXX HISTORY...
 | 
					
						
							|  |  |  | 			if(this.history !== false){ | 
					
						
							|  |  |  | 				this.history.includes(this.__location) | 
					
						
							|  |  |  | 					&& this.history.splice( | 
					
						
							|  |  |  | 						this.history.indexOf(this.__location)+1,  | 
					
						
							|  |  |  | 						this.history.length) | 
					
						
							|  |  |  | 				this.history.push(cur) }  | 
					
						
							|  |  |  | 			// trigger handlers...
 | 
					
						
							|  |  |  | 			handle() }), | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-05-02 10:40:32 +03:00
										 |  |  | 	// .path is a proxy to .location
 | 
					
						
							|  |  |  | 	// XXX do we need this???
 | 
					
						
							|  |  |  | 	get path(){ | 
					
						
							|  |  |  | 		return this.location }, | 
					
						
							|  |  |  | 	set path(value){ | 
					
						
							|  |  |  | 		this.location = value }, | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-07-17 15:49:05 +03:00
										 |  |  | 	// XXX do we need this...
 | 
					
						
							|  |  |  | 	get resolvedPath(){ | 
					
						
							|  |  |  | 		return this.match() }, | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-07-11 18:14:11 +03:00
										 |  |  | 	// XXX should these be writable???
 | 
					
						
							| 
									
										
										
										
											2022-05-03 12:28:03 +03:00
										 |  |  | 	get name(){ | 
					
						
							|  |  |  | 		return module.path.split(this.path).pop() }, | 
					
						
							|  |  |  | 	//set name(value){ },
 | 
					
						
							|  |  |  | 	get dir(){ | 
					
						
							|  |  |  | 		return module.path.relative(this.location, '..') }, | 
					
						
							| 
									
										
										
										
											2022-05-03 12:45:00 +03:00
										 |  |  | 	//set dir(value){ },
 | 
					
						
							| 
									
										
										
										
											2022-05-22 15:06:30 +03:00
										 |  |  | 	get isPattern(){ | 
					
						
							|  |  |  | 		return this.location.includes('*') }, | 
					
						
							| 
									
										
										
										
											2022-05-03 12:28:03 +03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-05-15 03:04:29 +03:00
										 |  |  | 	// history...
 | 
					
						
							|  |  |  | 	//
 | 
					
						
							| 
									
										
										
										
											2022-04-22 00:14:41 +03:00
										 |  |  | 	//* XXX HISTORY...
 | 
					
						
							| 
									
										
										
										
											2022-04-22 01:49:11 +03:00
										 |  |  | 	// NOTE: set this to false to disable history...
 | 
					
						
							| 
									
										
										
										
											2022-04-22 00:14:41 +03:00
										 |  |  | 	__history: undefined, | 
					
						
							|  |  |  | 	get history(){ | 
					
						
							| 
									
										
										
										
											2022-04-22 01:49:11 +03:00
										 |  |  | 		if(this.__history === false){ | 
					
						
							|  |  |  | 			return false } | 
					
						
							| 
									
										
										
										
											2022-04-22 00:14:41 +03:00
										 |  |  | 		if(!this.hasOwnProperty('__history')){ | 
					
						
							| 
									
										
										
										
											2022-04-22 12:00:48 +03:00
										 |  |  | 			this.__history = [] } | 
					
						
							|  |  |  | 			//this.__history = (this.__history ?? []).slice() }
 | 
					
						
							| 
									
										
										
										
											2022-04-22 00:14:41 +03:00
										 |  |  | 		return this.__history }, | 
					
						
							|  |  |  | 	back: function(offset=1){ | 
					
						
							|  |  |  | 		var h = this.history | 
					
						
							| 
									
										
										
										
											2022-04-22 01:49:11 +03:00
										 |  |  | 		if(h === false  | 
					
						
							|  |  |  | 				|| h.length <= 1){ | 
					
						
							| 
									
										
										
										
											2022-04-22 00:14:41 +03:00
										 |  |  | 			return this } | 
					
						
							|  |  |  | 		// get position in history...
 | 
					
						
							|  |  |  | 		var p = h.indexOf(this.location) | 
					
						
							| 
									
										
										
										
											2022-04-22 01:49:11 +03:00
										 |  |  | 		// if outside of history go to last element...
 | 
					
						
							| 
									
										
										
										
											2022-04-22 00:14:41 +03:00
										 |  |  | 		p = p < 0 ?  | 
					
						
							|  |  |  | 			h.length | 
					
						
							|  |  |  | 			: p | 
					
						
							|  |  |  | 		p = Math.max( | 
					
						
							|  |  |  | 			Math.min( | 
					
						
							|  |  |  | 				h.length-1  | 
					
						
							|  |  |  | 					- p  | 
					
						
							|  |  |  | 					+ offset, | 
					
						
							|  |  |  | 				h.length-1),  | 
					
						
							|  |  |  | 			0) | 
					
						
							| 
									
										
										
										
											2022-05-26 00:26:28 +03:00
										 |  |  | 		this.onBeforeNavigate(this.path) | 
					
						
							| 
									
										
										
										
											2022-04-22 00:14:41 +03:00
										 |  |  | 		this.referrer = this.location | 
					
						
							| 
									
										
										
										
											2022-05-26 00:26:28 +03:00
										 |  |  | 		var path = this.__location = h[h.length-1 - p] | 
					
						
							|  |  |  | 		this.onNavigate(module.__HANDLE_NAVIGATE, path) | 
					
						
							| 
									
										
										
										
											2022-04-22 00:14:41 +03:00
										 |  |  | 		return this }, | 
					
						
							|  |  |  | 	forward: function(offset=1){ | 
					
						
							|  |  |  | 		return this.back(-offset) }, | 
					
						
							|  |  |  | 	//*/
 | 
					
						
							|  |  |  | 	 | 
					
						
							| 
									
										
										
										
											2022-05-24 10:45:53 +03:00
										 |  |  | 	// store interface...
 | 
					
						
							|  |  |  | 	//
 | 
					
						
							|  |  |  | 	// XXX we are only doing modifiers here...
 | 
					
						
							| 
									
										
										
										
											2022-05-25 01:59:32 +03:00
										 |  |  | 	// 		...these ar mainly used to disable writing in .ro(..)
 | 
					
						
							| 
									
										
										
										
											2022-06-15 16:10:16 +03:00
										 |  |  | 	__update__: function(data){ | 
					
						
							| 
									
										
										
										
											2022-06-12 12:04:44 +03:00
										 |  |  | 		return this.store.update(this.location, data) }, | 
					
						
							| 
									
										
										
										
											2022-06-15 16:10:16 +03:00
										 |  |  | 	__delete__: function(path='.'){ | 
					
						
							| 
									
										
										
										
											2022-06-12 12:04:44 +03:00
										 |  |  | 		return this.store.delete(module.path.relative(this.location, path)) }, | 
					
						
							| 
									
										
										
										
											2022-05-24 10:45:53 +03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-04-22 00:14:41 +03:00
										 |  |  | 	// page data...
 | 
					
						
							|  |  |  | 	//
 | 
					
						
							| 
									
										
										
										
											2022-05-20 15:03:24 +03:00
										 |  |  | 	strict: undefined, | 
					
						
							| 
									
										
										
										
											2022-04-21 18:33:08 +03:00
										 |  |  | 	get data(){ | 
					
						
							| 
									
										
										
										
											2022-05-22 15:06:30 +03:00
										 |  |  | 		// NOTE: we need to make sure each page gets the chance to handle 
 | 
					
						
							|  |  |  | 		// 		its context....
 | 
					
						
							|  |  |  | 		if(this.isPattern){ | 
					
						
							|  |  |  | 			return this | 
					
						
							|  |  |  | 				.map(function(page){ | 
					
						
							|  |  |  | 					return page.data }) } | 
					
						
							|  |  |  | 		// single page...
 | 
					
						
							|  |  |  | 		var res = this.store.get(this.location, !!this.strict) | 
					
						
							|  |  |  | 		return typeof(res) == 'function' ? | 
					
						
							|  |  |  | 			res.bind(this) | 
					
						
							|  |  |  | 			: res }, | 
					
						
							|  |  |  | 		//return this.store.get(this.location, !!this.strict) },
 | 
					
						
							| 
									
										
										
										
											2022-04-21 18:33:08 +03:00
										 |  |  | 	set data(value){ | 
					
						
							| 
									
										
										
										
											2022-05-24 10:45:53 +03:00
										 |  |  | 		this.__update__(value) }, | 
					
						
							| 
									
										
										
										
											2022-04-12 02:03:33 +03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-05-19 01:57:41 +03:00
										 |  |  | 	// metadata...
 | 
					
						
							|  |  |  | 	//
 | 
					
						
							|  |  |  | 	// NOTE: in the general case this is the same as .data but in also allows
 | 
					
						
							|  |  |  | 	// 		storing of data (metadata) for pattern paths...
 | 
					
						
							|  |  |  | 	get metadata(){ | 
					
						
							|  |  |  | 		return this.store.metadata(this.location) }, | 
					
						
							|  |  |  | 	set metadata(value){ | 
					
						
							| 
									
										
										
										
											2022-05-24 10:45:53 +03:00
										 |  |  | 		this.__update__(value) }, | 
					
						
							| 
									
										
										
										
											2022-05-19 01:57:41 +03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-04-30 01:39:23 +03:00
										 |  |  | 	// number of matching pages...
 | 
					
						
							| 
									
										
										
										
											2022-06-15 16:10:16 +03:00
										 |  |  | 	// NOTE: this can be both sync and async...
 | 
					
						
							| 
									
										
										
										
											2022-04-30 01:39:23 +03:00
										 |  |  | 	get length(){ | 
					
						
							| 
									
										
										
										
											2022-05-22 15:06:30 +03:00
										 |  |  | 		var p = this.resolve(this.location) | 
					
						
							| 
									
										
										
										
											2022-04-30 01:39:23 +03:00
										 |  |  | 		return p instanceof Array ? | 
					
						
							| 
									
										
										
										
											2022-06-15 16:10:16 +03:00
										 |  |  | 				p.length | 
					
						
							|  |  |  | 			: p instanceof Promise ? | 
					
						
							|  |  |  | 				p.then(function(res){ | 
					
						
							|  |  |  | 					return res instanceof Array ? | 
					
						
							|  |  |  | 						res.length | 
					
						
							|  |  |  | 						: 1 }) | 
					
						
							| 
									
										
										
										
											2022-04-30 01:39:23 +03:00
										 |  |  | 			: 1 }, | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-04-14 20:58:09 +03:00
										 |  |  | 	// relative proxies to store...
 | 
					
						
							| 
									
										
										
										
											2022-04-12 02:03:33 +03:00
										 |  |  | 	exists: relProxy('exists'),  | 
					
						
							| 
									
										
										
										
											2022-05-25 01:01:22 +03:00
										 |  |  | 	find: relProxy('find'),  | 
					
						
							| 
									
										
										
										
											2022-05-22 15:06:30 +03:00
										 |  |  | 	match: relMatchProxy('match'),  | 
					
						
							|  |  |  | 	resolve: relMatchProxy('resolve'), | 
					
						
							| 
									
										
										
										
											2022-05-03 16:15:16 +03:00
										 |  |  | 	delete: function(path='.'){ | 
					
						
							| 
									
										
										
										
											2022-05-24 10:45:53 +03:00
										 |  |  | 		return this.__delete__() }, | 
					
						
							| 
									
										
										
										
											2022-04-12 02:03:33 +03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-05-20 15:03:24 +03:00
										 |  |  | 	//
 | 
					
						
							|  |  |  | 	// 	.get(<path>[, <data>])
 | 
					
						
							|  |  |  | 	// 	.get(<path>, <strict>[, <data>])
 | 
					
						
							|  |  |  | 	// 		-> <page>
 | 
					
						
							|  |  |  | 	//
 | 
					
						
							|  |  |  | 	get: function(path, strict, data={}){ | 
					
						
							|  |  |  | 		if(strict instanceof Object){ | 
					
						
							|  |  |  | 			data = strict | 
					
						
							|  |  |  | 			strict = undefined } | 
					
						
							| 
									
										
										
										
											2022-04-22 00:14:41 +03:00
										 |  |  | 		return this.clone({ | 
					
						
							|  |  |  | 				location: path,  | 
					
						
							| 
									
										
										
										
											2022-05-20 15:03:24 +03:00
										 |  |  | 				...data, | 
					
						
							|  |  |  | 				referrer: data.referrer  | 
					
						
							| 
									
										
										
										
											2022-04-22 00:14:41 +03:00
										 |  |  | 					?? this.location, | 
					
						
							| 
									
										
										
										
											2022-05-20 15:03:24 +03:00
										 |  |  | 				strict, | 
					
						
							| 
									
										
										
										
											2022-04-22 00:14:41 +03:00
										 |  |  | 			}) }, | 
					
						
							| 
									
										
										
										
											2022-04-21 18:33:08 +03:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	// XXX should this be an iterator???
 | 
					
						
							|  |  |  | 	each: function(path){ | 
					
						
							|  |  |  | 		var that = this | 
					
						
							| 
									
										
										
										
											2022-05-25 01:59:32 +03:00
										 |  |  | 		// NOTE: we are trying to avoid resolving non-pattern paths unless 
 | 
					
						
							|  |  |  | 		// 		we really have to...
 | 
					
						
							| 
									
										
										
										
											2022-05-25 01:15:05 +03:00
										 |  |  | 		path = path ? | 
					
						
							|  |  |  | 			module.path.relative(this.path, path) | 
					
						
							|  |  |  | 			: this.path | 
					
						
							| 
									
										
										
										
											2022-05-22 15:06:30 +03:00
										 |  |  | 		//var paths = this.match(path)
 | 
					
						
							| 
									
										
										
										
											2022-05-25 01:15:05 +03:00
										 |  |  | 		var paths = path.includes('*') ? | 
					
						
							|  |  |  | 			this.resolve(path) | 
					
						
							|  |  |  | 			: path | 
					
						
							| 
									
										
										
										
											2022-04-21 18:33:08 +03:00
										 |  |  | 		paths = paths instanceof Array ?  | 
					
						
							| 
									
										
										
										
											2022-06-15 16:10:16 +03:00
										 |  |  | 				paths  | 
					
						
							|  |  |  | 			: paths instanceof Promise ? | 
					
						
							|  |  |  | 				paths.iter() | 
					
						
							| 
									
										
										
										
											2022-04-21 18:33:08 +03:00
										 |  |  | 			: [paths] | 
					
						
							|  |  |  | 		return paths | 
					
						
							|  |  |  | 			.map(function(path){ | 
					
						
							| 
									
										
										
										
											2022-04-22 12:00:48 +03:00
										 |  |  | 				return that.get('/'+ path) }) }, | 
					
						
							| 
									
										
										
										
											2022-04-21 18:33:08 +03:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	map: function(func){ | 
					
						
							|  |  |  | 		return this.each().map(func) }, | 
					
						
							|  |  |  | 	filter: function(func){ | 
					
						
							|  |  |  | 		return this.each().filter(func) }, | 
					
						
							|  |  |  | 	reduce: function(func, dfl){ | 
					
						
							|  |  |  | 		return this.each().reduce(func, dfl) }, | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-05-19 01:57:41 +03:00
										 |  |  | 	// sorting...
 | 
					
						
							|  |  |  | 	//
 | 
					
						
							| 
									
										
										
										
											2022-05-19 02:13:17 +03:00
										 |  |  | 	// XXX should this be page-level (current) store level???
 | 
					
						
							| 
									
										
										
										
											2022-06-15 16:10:16 +03:00
										 |  |  | 	// XXX when this is async, should this return a promise????
 | 
					
						
							|  |  |  | 	sort: async function(cmp){ | 
					
						
							| 
									
										
										
										
											2022-05-19 01:57:41 +03:00
										 |  |  | 		// not sorting single pages...
 | 
					
						
							|  |  |  | 		if(this.length <= 1){ | 
					
						
							|  |  |  | 			return this } | 
					
						
							|  |  |  | 		// sort...
 | 
					
						
							| 
									
										
										
										
											2022-06-15 16:10:16 +03:00
										 |  |  | 		this.metadata = { order: await this.each() | 
					
						
							|  |  |  | 			.sort(...arguments) | 
					
						
							|  |  |  | 			.map(function(p){ | 
					
						
							|  |  |  | 				return p.path }) } | 
					
						
							| 
									
										
										
										
											2022-05-19 01:57:41 +03:00
										 |  |  | 		return this }, | 
					
						
							| 
									
										
										
										
											2022-06-15 16:10:16 +03:00
										 |  |  | 	reverse: async function(){ | 
					
						
							| 
									
										
										
										
											2022-05-19 01:57:41 +03:00
										 |  |  | 		// not sorting single pages...
 | 
					
						
							|  |  |  | 		if(this.length <= 1){ | 
					
						
							|  |  |  | 			return this } | 
					
						
							| 
									
										
										
										
											2022-06-15 16:10:16 +03:00
										 |  |  | 		this.metadata = { order: (await this.match()).reverse() } | 
					
						
							| 
									
										
										
										
											2022-05-19 01:57:41 +03:00
										 |  |  | 		return this }, | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-04-22 00:14:41 +03:00
										 |  |  | 	//
 | 
					
						
							|  |  |  | 	// 	Clone a page optionally asigning data into it...
 | 
					
						
							|  |  |  | 	// 	.clone()
 | 
					
						
							| 
									
										
										
										
											2022-04-22 12:00:48 +03:00
										 |  |  | 	// 	.clone({ .. }[, <clone-history>])
 | 
					
						
							| 
									
										
										
										
											2022-04-22 00:14:41 +03:00
										 |  |  | 	// 		-> <page>
 | 
					
						
							|  |  |  | 	//
 | 
					
						
							|  |  |  | 	// 	Fully clone a page optionally asigning data into it...
 | 
					
						
							| 
									
										
										
										
											2022-04-22 12:00:48 +03:00
										 |  |  | 	// 	.clone(true[, <clone-history>])
 | 
					
						
							|  |  |  | 	// 	.clone(true, { .. }[, <clone-history>])
 | 
					
						
							| 
									
										
										
										
											2022-04-22 00:14:41 +03:00
										 |  |  | 	// 		-> <page>
 | 
					
						
							|  |  |  | 	//
 | 
					
						
							|  |  |  | 	//
 | 
					
						
							|  |  |  | 	// Normal cloning will inherit all the "clones" from the original 
 | 
					
						
							|  |  |  | 	// page overloading .location and .referrer
 | 
					
						
							|  |  |  | 	//
 | 
					
						
							| 
									
										
										
										
											2022-04-22 12:00:48 +03:00
										 |  |  | 	// NOTE: <clone-history> by default is false unless fully cloning
 | 
					
						
							|  |  |  | 	//
 | 
					
						
							|  |  |  | 	// XXX HISTORY should we clear history by default...
 | 
					
						
							|  |  |  | 	clone: function(data={}, history=false){ | 
					
						
							|  |  |  | 		var [data, ...args] = [...arguments] | 
					
						
							| 
									
										
										
										
											2022-04-22 00:14:41 +03:00
										 |  |  | 		var full = data === true | 
					
						
							| 
									
										
										
										
											2022-04-22 12:00:48 +03:00
										 |  |  | 		history =  | 
					
						
							|  |  |  | 			typeof(args[args.length-1]) == 'boolean' ?  | 
					
						
							|  |  |  | 				args.pop()  | 
					
						
							|  |  |  | 				: full | 
					
						
							| 
									
										
										
										
											2022-04-22 00:14:41 +03:00
										 |  |  | 		data = full ?  | 
					
						
							| 
									
										
										
										
											2022-04-22 12:00:48 +03:00
										 |  |  | 			args[0] ?? {}  | 
					
						
							| 
									
										
										
										
											2022-04-22 00:14:41 +03:00
										 |  |  | 			: data | 
					
						
							|  |  |  | 		return Object.assign( | 
					
						
							|  |  |  | 			full ? | 
					
						
							|  |  |  | 				// full copy...
 | 
					
						
							|  |  |  | 				this.constructor(this.path, this.referrer, this.store) | 
					
						
							|  |  |  | 				// NOTE: this will restrict all the clones to the first 
 | 
					
						
							|  |  |  | 				// 		generation maintaining the original (.root) page as 
 | 
					
						
							|  |  |  | 				// 		the common root...
 | 
					
						
							|  |  |  | 				// 		this will make all the non-shadowed attrs set on the
 | 
					
						
							|  |  |  | 				// 		root visible to all sub-pages.
 | 
					
						
							|  |  |  | 				: Object.create(this.root ?? this), | 
					
						
							|  |  |  | 			{ | 
					
						
							|  |  |  | 				root: this.root ?? this, | 
					
						
							|  |  |  | 				location: this.location,  | 
					
						
							|  |  |  | 				referrer: this.referrer, | 
					
						
							|  |  |  | 			}, | 
					
						
							| 
									
										
										
										
											2022-04-22 12:00:48 +03:00
										 |  |  | 			// XXX HISTORY...
 | 
					
						
							|  |  |  | 			this.__history !== false ? | 
					
						
							|  |  |  | 				{ __history:  | 
					
						
							|  |  |  | 					history ? | 
					
						
							|  |  |  | 						(this.__history ?? []).slice()  | 
					
						
							|  |  |  | 						: [] } | 
					
						
							|  |  |  | 				:{}, | 
					
						
							|  |  |  | 			//*/
 | 
					
						
							| 
									
										
										
										
											2022-04-22 00:14:41 +03:00
										 |  |  | 			data) }, | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-05-25 02:14:12 +03:00
										 |  |  | 	// Create a read-only page...
 | 
					
						
							|  |  |  | 	//
 | 
					
						
							|  |  |  | 	// NOTE: all pages that are created via a read-only page are also 
 | 
					
						
							|  |  |  | 	// 		read-only.
 | 
					
						
							| 
									
										
										
										
											2022-05-25 01:59:32 +03:00
										 |  |  | 	// XXX EXPERIMENTAL...
 | 
					
						
							|  |  |  | 	ro: function(data={}){ | 
					
						
							|  |  |  | 		return Object.assign({ | 
					
						
							|  |  |  | 			__proto__: this, | 
					
						
							|  |  |  | 			__update__: function(){ return this }, | 
					
						
							|  |  |  | 			__delete__: function(){ return this }, | 
					
						
							|  |  |  | 		}, | 
					
						
							|  |  |  | 		data) }, | 
					
						
							| 
									
										
										
										
											2022-05-25 02:14:12 +03:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	// Create a virtual page at current path...
 | 
					
						
							|  |  |  | 	//
 | 
					
						
							|  |  |  | 	// Virtual pages do not affect store data in any way but behave like 
 | 
					
						
							|  |  |  | 	// normal pages.
 | 
					
						
							|  |  |  | 	//
 | 
					
						
							|  |  |  | 	// NOTE: .get(..) / .clone(..) will return normal non-virtual pages
 | 
					
						
							|  |  |  | 	// 		unless the target path is the same as the virtual page .path...
 | 
					
						
							| 
									
										
										
										
											2022-05-25 09:37:38 +03:00
										 |  |  | 	// NOTE: changing .path/.location is not supported.
 | 
					
						
							| 
									
										
										
										
											2022-05-25 01:59:32 +03:00
										 |  |  | 	// XXX EXPERIMENTAL...
 | 
					
						
							|  |  |  | 	virtual: function(data={}){ | 
					
						
							|  |  |  | 		var that = this | 
					
						
							|  |  |  | 		return { | 
					
						
							|  |  |  | 			__proto__: this, | 
					
						
							|  |  |  | 			// make the location read-only...
 | 
					
						
							|  |  |  | 			get location(){ | 
					
						
							|  |  |  | 				// NOTE: since we are not providing this as a basis for 
 | 
					
						
							|  |  |  | 				// 		inheritance we do not need to properly access 
 | 
					
						
							|  |  |  | 				// 		the parent prop...
 | 
					
						
							|  |  |  | 				// 		...otherwise use:
 | 
					
						
							|  |  |  | 				// 			object.parentProperty(..)
 | 
					
						
							|  |  |  | 				return this.__proto__.location }, | 
					
						
							|  |  |  | 			__update__: function(data){  | 
					
						
							|  |  |  | 				Object.assign(this.data, data) | 
					
						
							|  |  |  | 				return this }, | 
					
						
							|  |  |  | 			__delete__: function(){ return this }, | 
					
						
							|  |  |  | 			// NOTE: we need to proxy .clone(..) back to parent so as to 
 | 
					
						
							|  |  |  | 			// 		avoid overloading .data in the children too...
 | 
					
						
							| 
									
										
										
										
											2022-05-25 02:02:22 +03:00
										 |  |  | 			// NOTE: we are also keeping all first level queries resolving 
 | 
					
						
							|  |  |  | 			// 		to current path also virtual...
 | 
					
						
							| 
									
										
										
										
											2022-05-25 01:59:32 +03:00
										 |  |  | 			clone: function(...args){ | 
					
						
							|  |  |  | 				var res = that.clone(...args)  | 
					
						
							|  |  |  | 				return res.path == this.path ? | 
					
						
							| 
									
										
										
										
											2022-05-25 02:02:22 +03:00
										 |  |  | 					that.virtual(this.data) | 
					
						
							| 
									
										
										
										
											2022-05-25 01:59:32 +03:00
										 |  |  | 					: res }, | 
					
						
							|  |  |  | 			data: Object.assign( | 
					
						
							|  |  |  | 				{ | 
					
						
							|  |  |  | 					ctime: Date.now(), | 
					
						
							|  |  |  | 					mtime: Date.now(), | 
					
						
							|  |  |  | 				}, | 
					
						
							|  |  |  | 				data), | 
					
						
							|  |  |  | 		} }, | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-05-17 01:26:39 +03:00
										 |  |  | 	// XXX should this be update or assign???
 | 
					
						
							|  |  |  | 	// XXX how should this work on multiple pages...
 | 
					
						
							| 
									
										
										
										
											2022-05-26 00:26:28 +03:00
										 |  |  | 	// 		...right now this will write what-ever is given, even if it
 | 
					
						
							|  |  |  | 	// 		will never be explicitly be accessible...
 | 
					
						
							| 
									
										
										
										
											2022-06-15 16:10:16 +03:00
										 |  |  | 	// XXX sync/async???
 | 
					
						
							| 
									
										
										
										
											2022-04-25 20:53:20 +03:00
										 |  |  | 	update: function(...data){ | 
					
						
							|  |  |  | 		return Object.assign(this, ...data) }, | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-04-22 00:14:41 +03:00
										 |  |  | 	__init__: function(path, referrer, store){ | 
					
						
							|  |  |  | 		// NOTE: this will allow inheriting .store from the prototype
 | 
					
						
							|  |  |  | 		if(store){ | 
					
						
							|  |  |  | 			this.store = store } | 
					
						
							|  |  |  | 		this.location = path | 
					
						
							| 
									
										
										
										
											2022-04-21 18:33:08 +03:00
										 |  |  | 		this.referrer = referrer }, | 
					
						
							|  |  |  | }) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-05-26 00:26:28 +03:00
										 |  |  | // pepper in event functionality...
 | 
					
						
							|  |  |  | types.event.EventMixin(BasePage.prototype) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-04-12 02:03:33 +03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-04-12 01:31:28 +03:00
										 |  |  | 
 | 
					
						
							|  |  |  | //---------------------------------------------------------------------
 | 
					
						
							| 
									
										
										
										
											2022-05-02 01:53:11 +03:00
										 |  |  | // Parser...
 | 
					
						
							| 
									
										
										
										
											2022-04-12 01:31:28 +03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-05-02 02:01:53 +03:00
										 |  |  | // XXX should we warn about stuff like <macro src=/moo/> -- currently 
 | 
					
						
							|  |  |  | // 		this will simply be ignored, i.e. passed trough the parser 
 | 
					
						
							|  |  |  | // 		without change...
 | 
					
						
							| 
									
										
										
										
											2022-05-01 16:59:43 +03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-05-02 02:18:11 +03:00
										 |  |  | var BaseParser = | 
					
						
							|  |  |  | module.BaseParser = { | 
					
						
							| 
									
										
										
										
											2022-04-28 15:53:21 +03:00
										 |  |  | 	// patterns...
 | 
					
						
							|  |  |  | 	//
 | 
					
						
							| 
									
										
										
										
											2022-05-02 10:40:32 +03:00
										 |  |  | 	// The way the patterns are organized might seem a bit overcomplicated
 | 
					
						
							|  |  |  | 	// and it has to be to be able to reuse the same pattern in different 
 | 
					
						
							|  |  |  | 	// contexts, e.g. the arguments pattern...
 | 
					
						
							| 
									
										
										
										
											2022-05-02 02:16:17 +03:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	//
 | 
					
						
							|  |  |  | 	// needs:
 | 
					
						
							|  |  |  | 	// 	STOP -- '\\>' or ')'
 | 
					
						
							|  |  |  | 	// 	PREFIX -- 'inline' or 'elem'
 | 
					
						
							|  |  |  | 	//
 | 
					
						
							|  |  |  | 	// XXX quote escaping???
 | 
					
						
							| 
									
										
										
										
											2022-05-02 10:40:32 +03:00
										 |  |  | 	// 		/(?<quote>['"])(\\\k<quote>|[^\1])*\k<quote>/
 | 
					
						
							|  |  |  | 	// 		...this will work but we'll also need to remove the \ in the 
 | 
					
						
							|  |  |  | 	// 		final string...
 | 
					
						
							| 
									
										
										
										
											2022-05-02 23:56:32 +03:00
										 |  |  | 	MACRO_ARGS: ['(\\s*(',[ | 
					
						
							| 
									
										
										
										
											2022-05-02 02:16:17 +03:00
										 |  |  | 				// arg='val' | arg="val" | arg=val
 | 
					
						
							| 
									
										
										
										
											2022-05-03 00:01:30 +03:00
										 |  |  | 				'(?<PREFIXArgName>[a-z-]+)\\s*=\\s*(?<PREFIXArgValue>'+([ | 
					
						
							| 
									
										
										
										
											2022-05-02 02:16:17 +03:00
										 |  |  | 					// XXX CHROME/NODE BUG: this does not work yet...
 | 
					
						
							|  |  |  | 					//'\\s+(?<quote>[\'"])[^\\k<quote>]*\\k<quote>',
 | 
					
						
							|  |  |  | 					"'(?<PREFIXSingleQuotedValue>[^']*)'", | 
					
						
							|  |  |  | 					'"(?<PREFIXDoubleQuotedValue>[^"]*)"', | 
					
						
							|  |  |  | 					'(?<PREFIXValue>[^\\sSTOP\'"]+)', | 
					
						
							|  |  |  | 				].join('|'))+')', | 
					
						
							|  |  |  | 				// "arg" | 'arg'
 | 
					
						
							|  |  |  | 				// XXX CHROME/NODE BUG: this does not work yet...
 | 
					
						
							|  |  |  | 				//'\\s+(?<quote>[\'"])[^\\k<quote>]*\\k<quote>',
 | 
					
						
							| 
									
										
										
										
											2022-05-02 23:56:32 +03:00
										 |  |  | 				'"(?<PREFIXDoubleQuotedArg>[^"]*)"', | 
					
						
							|  |  |  | 				"'(?<PREFIXSingleQuotedArg>[^']*)'", | 
					
						
							| 
									
										
										
										
											2022-05-02 02:16:17 +03:00
										 |  |  | 				// arg
 | 
					
						
							|  |  |  | 				// NOTE: this is last because it could eat up parts of the above 
 | 
					
						
							|  |  |  | 				// 		alternatives...
 | 
					
						
							|  |  |  | 				//'|\\s+[^\\s\\/>\'"]+',
 | 
					
						
							| 
									
										
										
										
											2022-05-02 23:56:32 +03:00
										 |  |  | 				'(?<PREFIXArg>[^\\sSTOP\'"]+)', | 
					
						
							| 
									
										
										
										
											2022-05-02 02:16:17 +03:00
										 |  |  | 			].join('|'), | 
					
						
							| 
									
										
										
										
											2022-05-02 23:56:32 +03:00
										 |  |  | 		'))'].join(''), | 
					
						
							| 
									
										
										
										
											2022-05-02 02:16:17 +03:00
										 |  |  | 	MACRO_ARGS_PATTERN: undefined, | 
					
						
							|  |  |  | 	//
 | 
					
						
							|  |  |  | 	// 	.buildArgsPattern(<prefix>[, <stop>[, <flags>]])
 | 
					
						
							|  |  |  | 	// 		-> <pattern>
 | 
					
						
							|  |  |  | 	//
 | 
					
						
							|  |  |  | 	// 	.buildArgsPattern(<prefix>[, <stop>[, false]])
 | 
					
						
							|  |  |  | 	// 		-> <string>
 | 
					
						
							|  |  |  | 	//
 | 
					
						
							|  |  |  | 	buildArgsPattern: function(prefix='elem', stop='', regexp='smig'){ | 
					
						
							|  |  |  | 		var pattern = this.MACRO_ARGS | 
					
						
							|  |  |  | 			.replace(/PREFIX/g, prefix) | 
					
						
							|  |  |  | 			.replace(/STOP/g, stop) | 
					
						
							|  |  |  | 		return regexp ? | 
					
						
							|  |  |  | 			new RegExp(pattern, regexp)  | 
					
						
							|  |  |  | 			: pattern }, | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	//
 | 
					
						
							|  |  |  | 	// needs:
 | 
					
						
							|  |  |  | 	// 	MACROS
 | 
					
						
							|  |  |  | 	// 	INLINE_ARGS -- MACRO_ARGS.replace(/STOP/, ')') 
 | 
					
						
							|  |  |  | 	// 	ARGS -- MACRO_ARGS.replace(/STOP/, '\\/>') 
 | 
					
						
							| 
									
										
										
										
											2022-05-02 23:56:32 +03:00
										 |  |  | 	//
 | 
					
						
							|  |  |  | 	// XXX BUG: this fails to match inline macros with non-empty args @moo(a)
 | 
					
						
							|  |  |  | 	// 		...the problem seems to be with the lack of whitespace 
 | 
					
						
							|  |  |  | 	// 		between ( and the first arg -- @moo( a) is matched fine...
 | 
					
						
							| 
									
										
										
										
											2022-05-02 02:16:17 +03:00
										 |  |  | 	MACRO: '('+([ | 
					
						
							|  |  |  | 			// @macro(arg ..)
 | 
					
						
							|  |  |  | 			'\\\\?@(?<nameInline>MACROS)\\((?<argsInline>INLINE_ARGS)\\)', | 
					
						
							|  |  |  | 			// <macro ..> | <macro ../>
 | 
					
						
							|  |  |  | 			'<\\s*(?<nameOpen>MACROS)(?<argsOpen>ARGS)?\\s*/?>', | 
					
						
							|  |  |  | 			// </macro>
 | 
					
						
							|  |  |  | 			'</\\s*(?<nameClose>MACROS)\\s*>', | 
					
						
							|  |  |  | 		].join('|'))+')', | 
					
						
							|  |  |  | 	MACRO_PATTERN: undefined, | 
					
						
							|  |  |  | 	MACRO_PATTERN_GROUPS: undefined, | 
					
						
							|  |  |  | 	//
 | 
					
						
							|  |  |  | 	// 	.buildMacroPattern(<macros>[, <flags>])
 | 
					
						
							|  |  |  | 	// 		-> <pattern>
 | 
					
						
							|  |  |  | 	//
 | 
					
						
							|  |  |  | 	// 	.buildMacroPattern(<macros>[, false])
 | 
					
						
							|  |  |  | 	// 		-> <string>
 | 
					
						
							|  |  |  | 	//
 | 
					
						
							|  |  |  | 	buildMacroPattern: function(macros=['MACROS'], regexp='smig'){ | 
					
						
							|  |  |  | 		var pattern = this.MACRO | 
					
						
							|  |  |  | 			.replace(/MACROS/g, macros.join('|')) | 
					
						
							|  |  |  | 			.replace(/INLINE_ARGS/g, | 
					
						
							|  |  |  | 				this.buildArgsPattern('inline', ')', false) +'*') | 
					
						
							|  |  |  | 			.replace(/ARGS/g,  | 
					
						
							|  |  |  | 				this.buildArgsPattern('elem', '\\/>', false) +'*') | 
					
						
							|  |  |  | 		return regexp ? | 
					
						
							|  |  |  | 			new RegExp(pattern, regexp)  | 
					
						
							|  |  |  | 			: pattern }, | 
					
						
							|  |  |  | 	countMacroPatternGroups: function(){ | 
					
						
							|  |  |  | 		// NOTE: the -2 here is to compensate for the leading and trailing ""'s...
 | 
					
						
							|  |  |  | 		return '<MACROS>'.split(this.buildMacroPattern()).length - 2 }, | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-05-02 10:40:32 +03:00
										 |  |  | 	// XXX should this be closer to .stripComments(..)
 | 
					
						
							| 
									
										
										
										
											2022-04-26 16:20:25 +03:00
										 |  |  | 	// XXX do we need basic inline and block commets a-la lisp???
 | 
					
						
							|  |  |  | 	COMMENT_PATTERN: RegExp('('+[ | 
					
						
							|  |  |  | 			// <!--[pwiki[ .. ]]-->
 | 
					
						
							|  |  |  | 			'<!--\\[pwiki\\[(?<uncomment>.*)\\]\\]-->', | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			// <pwiki-comment> .. </pwiki-comment>
 | 
					
						
							|  |  |  | 			'<\\s*pwiki-comment[^>]*>.*<\\/\\s*pwiki-comment\\s*>', | 
					
						
							|  |  |  | 			// <pwiki-comment .. />
 | 
					
						
							|  |  |  | 			'<\\s*pwiki-comment[^\\/>]*\\/>', | 
					
						
							|  |  |  | 		].join('|') +')', 'smig'), | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-05-02 10:40:32 +03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-04-28 15:53:21 +03:00
										 |  |  | 	// helpers...
 | 
					
						
							|  |  |  | 	//
 | 
					
						
							|  |  |  | 	normalizeFilters: function(filters){ | 
					
						
							|  |  |  | 		var skip = new Set() | 
					
						
							|  |  |  | 		return filters | 
					
						
							|  |  |  | 			.flat() | 
					
						
							|  |  |  | 			.tailUnique() | 
					
						
							|  |  |  | 			.filter(function(filter){ | 
					
						
							|  |  |  | 				filter[0] == '-' | 
					
						
							|  |  |  | 					&& skip.add(filter.slice(1)) | 
					
						
							|  |  |  | 				return filter[0] != '-' })  | 
					
						
							|  |  |  | 			.filter(function(filter){ | 
					
						
							|  |  |  | 				return !skip.has(filter) })}, | 
					
						
							| 
									
										
										
										
											2022-05-15 03:04:29 +03:00
										 |  |  | 	//
 | 
					
						
							|  |  |  | 	// Spec format:
 | 
					
						
							|  |  |  | 	// 	[<orderd>, ... [<keyword>, ...]]
 | 
					
						
							|  |  |  | 	//
 | 
					
						
							| 
									
										
										
										
											2022-05-16 11:02:19 +03:00
										 |  |  | 	// NOTE: the input to this is formatted by .lex(..)
 | 
					
						
							|  |  |  | 	// NOTE: arg pre-parsing is dome by .lex(..) but at that stage we do not
 | 
					
						
							|  |  |  | 	// 		yet touch the actual macros (we need them to get the .arg_spec)
 | 
					
						
							|  |  |  | 	// 		so the actual parsing is done in .expand(..)
 | 
					
						
							| 
									
										
										
										
											2022-05-18 18:13:19 +03:00
										 |  |  | 	parseArgs: function(spec, args, state){ | 
					
						
							|  |  |  | 		var that = this | 
					
						
							| 
									
										
										
										
											2022-05-16 11:02:19 +03:00
										 |  |  | 		// spec...
 | 
					
						
							|  |  |  | 		var order = spec.slice() | 
					
						
							|  |  |  | 		var bools = new Set( | 
					
						
							|  |  |  | 			order[order.length-1] instanceof Array ? | 
					
						
							|  |  |  | 				order.pop() | 
					
						
							| 
									
										
										
										
											2022-05-15 03:04:29 +03:00
										 |  |  | 				: []) | 
					
						
							| 
									
										
										
										
											2022-05-16 11:02:19 +03:00
										 |  |  | 		order = order | 
					
						
							|  |  |  | 			.filter(function(k){ | 
					
						
							|  |  |  | 				return !(k in args) }) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		var res = {} | 
					
						
							|  |  |  | 		var pos = Object.entries(args) | 
					
						
							|  |  |  | 			// stage 1: populate res with explicit data and place the rest in pos...
 | 
					
						
							|  |  |  | 			.reduce(function(pos, [key, value]){ | 
					
						
							| 
									
										
										
										
											2022-05-18 18:13:19 +03:00
										 |  |  | 				;/^[0-9]+$/.test(key) ? | 
					
						
							| 
									
										
										
										
											2022-05-16 11:02:19 +03:00
										 |  |  | 					(bools.has(value) ? | 
					
						
							|  |  |  | 						// bool...
 | 
					
						
							|  |  |  | 						(res[value] = true) | 
					
						
							|  |  |  | 						// positional...
 | 
					
						
							|  |  |  | 						: (pos[key*1] = value)) | 
					
						
							|  |  |  | 					// keyword...
 | 
					
						
							|  |  |  | 					: (res[key] = value) | 
					
						
							|  |  |  | 				return pos }, []) | 
					
						
							|  |  |  | 			// stage 2: populate implicit values from pos...
 | 
					
						
							|  |  |  | 			.forEach(function(e, i){ | 
					
						
							|  |  |  | 				order.length == 0 ? | 
					
						
							|  |  |  | 					(res[e] = true) | 
					
						
							|  |  |  | 					: (res[order.shift()] = e) }) | 
					
						
							|  |  |  | 		return res }, | 
					
						
							| 
									
										
										
										
											2022-04-26 16:20:25 +03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-07-25 23:57:44 +03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-04-26 20:58:09 +03:00
										 |  |  | 	// Strip comments...
 | 
					
						
							| 
									
										
										
										
											2022-04-26 16:20:25 +03:00
										 |  |  | 	//
 | 
					
						
							| 
									
										
										
										
											2022-04-26 20:58:09 +03:00
										 |  |  | 	stripComments: function(str){ | 
					
						
							| 
									
										
										
										
											2022-04-26 16:20:25 +03:00
										 |  |  | 		return str | 
					
						
							|  |  |  | 			.replace(this.COMMENT_PATTERN,  | 
					
						
							|  |  |  | 				function(...a){ | 
					
						
							|  |  |  | 					return a.pop().uncomment  | 
					
						
							|  |  |  | 						|| '' }) }, | 
					
						
							| 
									
										
										
										
											2022-04-13 13:53:33 +03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-04-26 20:58:09 +03:00
										 |  |  | 	// Lexically split the string...
 | 
					
						
							|  |  |  | 	//
 | 
					
						
							| 
									
										
										
										
											2022-04-26 16:20:25 +03:00
										 |  |  | 	// 	<item> ::=
 | 
					
						
							|  |  |  | 	// 		<string>
 | 
					
						
							|  |  |  | 	// 		| {
 | 
					
						
							|  |  |  | 	// 			name: <string>,
 | 
					
						
							|  |  |  | 	// 			type: 'inline'
 | 
					
						
							|  |  |  | 	// 				| 'element'
 | 
					
						
							|  |  |  | 	// 				| 'opening'
 | 
					
						
							|  |  |  | 	// 				| 'closing',
 | 
					
						
							|  |  |  | 	// 			args: {
 | 
					
						
							|  |  |  | 	// 				<index>: <value>,
 | 
					
						
							|  |  |  | 	// 				<key>: <value>,
 | 
					
						
							|  |  |  | 	// 				...
 | 
					
						
							|  |  |  | 	// 			}
 | 
					
						
							|  |  |  | 	// 			match: <string>,
 | 
					
						
							|  |  |  | 	// 		}
 | 
					
						
							|  |  |  | 	//
 | 
					
						
							|  |  |  | 	//
 | 
					
						
							|  |  |  | 	// NOTE: this internally uses macros' keys to generate the lexing pattern.
 | 
					
						
							|  |  |  | 	lex: function*(page, str){ | 
					
						
							| 
									
										
										
										
											2022-06-15 16:10:16 +03:00
										 |  |  | 		//str = str 
 | 
					
						
							|  |  |  | 		//	?? page.raw
 | 
					
						
							| 
									
										
										
										
											2022-04-26 16:20:25 +03:00
										 |  |  | 		// NOTE: we are doing a separate pass for comments to completely 
 | 
					
						
							|  |  |  | 		// 		decouple them from the base macro syntax, making them fully 
 | 
					
						
							|  |  |  | 		// 		transparent...
 | 
					
						
							| 
									
										
										
										
											2022-04-26 20:58:09 +03:00
										 |  |  | 		str = this.stripComments(str) | 
					
						
							| 
									
										
										
										
											2022-04-26 16:20:25 +03:00
										 |  |  | 
 | 
					
						
							|  |  |  | 		// XXX should this be cached???
 | 
					
						
							| 
									
										
										
										
											2022-05-02 02:16:17 +03:00
										 |  |  | 		var macro_pattern = this.MACRO_PATTERN  | 
					
						
							|  |  |  | 			?? this.buildMacroPattern(Object.keys(page.macros)) | 
					
						
							|  |  |  | 		var macro_pattern_groups = this.MACRO_PATTERN_GROUPS  | 
					
						
							|  |  |  | 			?? this.countMacroPatternGroups() | 
					
						
							|  |  |  | 		var macro_args_pattern = this.MACRO_ARGS_PATTERN  | 
					
						
							|  |  |  | 			?? this.buildArgsPattern() | 
					
						
							| 
									
										
										
										
											2022-04-26 16:20:25 +03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-05-02 02:16:17 +03:00
										 |  |  | 		var lst = str.split(macro_pattern) | 
					
						
							| 
									
										
										
										
											2022-04-26 16:20:25 +03:00
										 |  |  | 
 | 
					
						
							|  |  |  | 		var macro = false | 
					
						
							|  |  |  | 		while(lst.length > 0){ | 
					
						
							|  |  |  | 			if(macro){ | 
					
						
							| 
									
										
										
										
											2022-05-02 02:16:17 +03:00
										 |  |  | 				var match = lst.splice(0, macro_pattern_groups)[0] | 
					
						
							| 
									
										
										
										
											2022-04-26 16:20:25 +03:00
										 |  |  | 				// NOTE: we essentially are parsing the detected macro a 
 | 
					
						
							|  |  |  | 				// 		second time here, this gives us access to named groups
 | 
					
						
							|  |  |  | 				// 		avoiding maintaining match indexes with the .split(..) 
 | 
					
						
							|  |  |  | 				// 		output...
 | 
					
						
							|  |  |  | 				// XXX for some reason .match(..) here returns a list with a string...
 | 
					
						
							| 
									
										
										
										
											2022-05-02 02:16:17 +03:00
										 |  |  | 				var cur = [...match.matchAll(macro_pattern)][0].groups | 
					
						
							| 
									
										
										
										
											2022-04-26 16:20:25 +03:00
										 |  |  | 				// special case: escaped inline macro -> keep as text...
 | 
					
						
							|  |  |  | 				if(match.startsWith('\\@')){ | 
					
						
							|  |  |  | 					yield match | 
					
						
							|  |  |  | 					macro = false  | 
					
						
							|  |  |  | 					continue } | 
					
						
							|  |  |  | 				// args...
 | 
					
						
							|  |  |  | 				var args = {} | 
					
						
							|  |  |  | 				var i = -1 | 
					
						
							|  |  |  | 				for(var {groups}  | 
					
						
							|  |  |  | 						of (cur.argsInline ?? cur.argsOpen ?? '') | 
					
						
							| 
									
										
										
										
											2022-05-02 02:16:17 +03:00
										 |  |  | 							.matchAll(macro_args_pattern)){ | 
					
						
							| 
									
										
										
										
											2022-04-26 16:20:25 +03:00
										 |  |  | 					i++ | 
					
						
							| 
									
										
										
										
											2022-05-02 18:29:59 +03:00
										 |  |  | 					args[groups.elemArgName  | 
					
						
							|  |  |  | 							?? groups.inlineArgName  | 
					
						
							|  |  |  | 							?? i] = | 
					
						
							|  |  |  | 						groups.elemSingleQuotedValue  | 
					
						
							|  |  |  | 							?? groups.inlineSingleQuotedValue | 
					
						
							| 
									
										
										
										
											2022-05-02 01:53:11 +03:00
										 |  |  | 							?? groups.elemDoubleQuotedValue | 
					
						
							| 
									
										
										
										
											2022-05-02 18:29:59 +03:00
										 |  |  | 							?? groups.inlineDoubleQuotedValue | 
					
						
							| 
									
										
										
										
											2022-05-02 01:53:11 +03:00
										 |  |  | 							?? groups.elemValue | 
					
						
							| 
									
										
										
										
											2022-05-02 18:29:59 +03:00
										 |  |  | 							?? groups.inlineValue | 
					
						
							| 
									
										
										
										
											2022-05-02 01:53:11 +03:00
										 |  |  | 							?? groups.elemSingleQuotedArg | 
					
						
							| 
									
										
										
										
											2022-05-02 18:29:59 +03:00
										 |  |  | 							?? groups.inlineSingleQuotedArg | 
					
						
							| 
									
										
										
										
											2022-05-02 01:53:11 +03:00
										 |  |  | 							?? groups.elemDoubleQuotedArg | 
					
						
							| 
									
										
										
										
											2022-05-02 18:29:59 +03:00
										 |  |  | 							?? groups.inlineDoubleQuotedArg | 
					
						
							|  |  |  | 							?? groups.elemArg | 
					
						
							|  |  |  | 							?? groups.inlineArg } | 
					
						
							| 
									
										
										
										
											2022-05-02 01:53:11 +03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-04-26 16:20:25 +03:00
										 |  |  | 				// macro-spec...
 | 
					
						
							|  |  |  | 				yield { | 
					
						
							|  |  |  | 					name: (cur.nameInline  | 
					
						
							|  |  |  | 							?? cur.nameOpen  | 
					
						
							|  |  |  | 							?? cur.nameClose) | 
					
						
							|  |  |  | 						.toLowerCase(), | 
					
						
							|  |  |  | 					type: match[0] == '@' ? | 
					
						
							|  |  |  | 							'inline' | 
					
						
							|  |  |  | 						: match[1] == '/' ? | 
					
						
							|  |  |  | 							'closing' | 
					
						
							|  |  |  | 						: match[match.length-2] == '/' ? | 
					
						
							|  |  |  | 							'element' | 
					
						
							|  |  |  | 						: 'opening', | 
					
						
							|  |  |  | 					args,  | 
					
						
							|  |  |  | 					match, | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 				macro = false | 
					
						
							|  |  |  | 			// normal text...
 | 
					
						
							|  |  |  | 			} else { | 
					
						
							|  |  |  | 				var str = lst.shift() | 
					
						
							|  |  |  | 				// skip empty strings from output...
 | 
					
						
							|  |  |  | 				if(str != ''){ | 
					
						
							|  |  |  | 					yield str } | 
					
						
							|  |  |  | 				macro = true } } }, | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Group block elements...
 | 
					
						
							|  |  |  | 	//
 | 
					
						
							|  |  |  | 	// 	<item> ::=
 | 
					
						
							|  |  |  | 	// 		<string>
 | 
					
						
							|  |  |  | 	// 		| {
 | 
					
						
							|  |  |  | 	// 			type: 'inline'
 | 
					
						
							|  |  |  | 	// 				| 'element'
 | 
					
						
							|  |  |  | 	// 				| 'block',
 | 
					
						
							|  |  |  | 	// 			body: [
 | 
					
						
							|  |  |  | 	// 				<item>,
 | 
					
						
							|  |  |  | 	// 				...
 | 
					
						
							|  |  |  | 	// 			],
 | 
					
						
							|  |  |  | 	//
 | 
					
						
							|  |  |  | 	//			// rest of items are the same as for lex(..)
 | 
					
						
							|  |  |  | 	// 			...
 | 
					
						
							|  |  |  | 	// 		}
 | 
					
						
							|  |  |  | 	//
 | 
					
						
							|  |  |  | 	// NOTE: this internaly uses macros to check for propper nesting
 | 
					
						
							| 
									
										
										
										
											2022-04-30 01:39:23 +03:00
										 |  |  | 	//group: function*(page, lex, to=false){
 | 
					
						
							|  |  |  | 	group: function*(page, lex, to=false, parent){ | 
					
						
							| 
									
										
										
										
											2022-06-15 16:10:16 +03:00
										 |  |  | 		//lex = lex
 | 
					
						
							|  |  |  | 		//	?? this.lex(page) 
 | 
					
						
							| 
									
										
										
										
											2022-04-26 16:20:25 +03:00
										 |  |  | 		lex = typeof(lex) == 'string' ? | 
					
						
							|  |  |  | 			this.lex(page, lex) | 
					
						
							|  |  |  | 			: lex | 
					
						
							| 
									
										
										
										
											2022-05-02 16:56:43 +03:00
										 |  |  | 
 | 
					
						
							|  |  |  | 		var quoting = to  | 
					
						
							| 
									
										
										
										
											2022-05-03 12:28:03 +03:00
										 |  |  | 			&& (page.QUOTING_MACROS ?? []).includes(to) | 
					
						
							| 
									
										
										
										
											2022-05-02 16:56:43 +03:00
										 |  |  | 			&& [] | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-04-26 16:20:25 +03:00
										 |  |  | 		// NOTE: we are not using for .. of .. here as it depletes the 
 | 
					
						
							|  |  |  | 		// 		generator even if the end is not reached...
 | 
					
						
							|  |  |  | 		while(true){ | 
					
						
							|  |  |  | 			var {value, done} = lex.next() | 
					
						
							|  |  |  | 			// check if unclosed blocks remaining...
 | 
					
						
							|  |  |  | 			if(done){ | 
					
						
							|  |  |  | 				if(to){ | 
					
						
							|  |  |  | 					throw new Error( | 
					
						
							| 
									
										
										
										
											2022-05-02 18:29:59 +03:00
										 |  |  | 						'Premature end of input: Expected closing "'+ to +'"') } | 
					
						
							| 
									
										
										
										
											2022-04-26 16:20:25 +03:00
										 |  |  | 				return } | 
					
						
							| 
									
										
										
										
											2022-05-02 16:56:43 +03:00
										 |  |  | 
 | 
					
						
							|  |  |  | 			// special case: quoting -> collect text...
 | 
					
						
							|  |  |  | 			// NOTE: we do not care about nesting here...
 | 
					
						
							|  |  |  | 			if(quoting !== false){ | 
					
						
							|  |  |  | 				if(value.name == to  | 
					
						
							|  |  |  | 						&& value.type == 'closing'){ | 
					
						
							|  |  |  | 					yield quoting.join('') | 
					
						
							|  |  |  | 					return | 
					
						
							|  |  |  | 				} else { | 
					
						
							|  |  |  | 					quoting.push( | 
					
						
							|  |  |  | 						typeof(value) == 'string' ? | 
					
						
							|  |  |  | 							value | 
					
						
							|  |  |  | 							: value.match ) } | 
					
						
							|  |  |  | 				continue } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-04-26 16:20:25 +03:00
										 |  |  | 			// assert nesting rules...
 | 
					
						
							| 
									
										
										
										
											2022-04-30 01:39:23 +03:00
										 |  |  | 			// NOTE: we only check for direct nesting...
 | 
					
						
							|  |  |  | 			// XXX might be a good idea to link nested block to the parent...
 | 
					
						
							| 
									
										
										
										
											2022-04-26 16:20:25 +03:00
										 |  |  | 			if(page.macros[value.name] instanceof Array | 
					
						
							| 
									
										
										
										
											2022-04-30 01:39:23 +03:00
										 |  |  | 					&& !page.macros[value.name].includes(to) | 
					
						
							|  |  |  | 					// do not complain about closing nestable tags...
 | 
					
						
							|  |  |  | 					&& !(value.name == to  | 
					
						
							|  |  |  | 						&& value.type == 'closing')){ | 
					
						
							| 
									
										
										
										
											2022-04-13 20:24:08 +03:00
										 |  |  | 				throw new Error( | 
					
						
							| 
									
										
										
										
											2022-04-26 16:20:25 +03:00
										 |  |  | 					'Unexpected "'+ value.name +'" macro'  | 
					
						
							|  |  |  | 						+(to ?  | 
					
						
							|  |  |  | 							' in "'+to+'"'  | 
					
						
							|  |  |  | 							: '')) } | 
					
						
							|  |  |  | 			// open block...
 | 
					
						
							|  |  |  | 			if(value.type == 'opening'){ | 
					
						
							| 
									
										
										
										
											2022-04-30 01:39:23 +03:00
										 |  |  | 				//value.body = [...this.group(page, lex, value.name)]
 | 
					
						
							|  |  |  | 				value.body = [...this.group(page, lex, value.name, value)] | 
					
						
							| 
									
										
										
										
											2022-04-26 16:20:25 +03:00
										 |  |  | 				value.type = 'block' | 
					
						
							|  |  |  | 			// close block...
 | 
					
						
							|  |  |  | 			} else if(value.type == 'closing'){ | 
					
						
							|  |  |  | 				if(value.name != to){ | 
					
						
							|  |  |  | 					throw new Error('Unexpected closing "'+ value.name +'"') } | 
					
						
							|  |  |  | 				// NOTE: we are intentionally not yielding the value here...
 | 
					
						
							|  |  |  | 				return }  | 
					
						
							| 
									
										
										
										
											2022-05-02 16:56:43 +03:00
										 |  |  | 			// normal value...
 | 
					
						
							| 
									
										
										
										
											2022-04-26 16:20:25 +03:00
										 |  |  | 			yield value } },  | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Expand macros...
 | 
					
						
							|  |  |  | 	//
 | 
					
						
							| 
									
										
										
										
											2022-04-26 20:58:09 +03:00
										 |  |  | 	// 	<item> ::=
 | 
					
						
							|  |  |  | 	// 		<string>
 | 
					
						
							|  |  |  | 	// 		// returned by .macros.filter(..)
 | 
					
						
							|  |  |  | 	// 		| {
 | 
					
						
							|  |  |  | 	// 			filters: [
 | 
					
						
							|  |  |  | 	// 				'<filter>'
 | 
					
						
							|  |  |  | 	// 					| '-<filter>',
 | 
					
						
							|  |  |  | 	// 				...
 | 
					
						
							|  |  |  | 	// 			],
 | 
					
						
							|  |  |  | 	// 			data: [ <item>, .. ],
 | 
					
						
							|  |  |  | 	// 		}
 | 
					
						
							|  |  |  | 	//
 | 
					
						
							| 
									
										
										
										
											2022-06-15 16:10:16 +03:00
										 |  |  | 	expand: async function*(page, ast, state={}){ | 
					
						
							| 
									
										
										
										
											2022-04-26 16:20:25 +03:00
										 |  |  | 		ast = ast == null ? | 
					
						
							| 
									
										
										
										
											2022-06-15 16:10:16 +03:00
										 |  |  | 				//this.group(page)
 | 
					
						
							|  |  |  | 				this.group(page, await page.raw) | 
					
						
							| 
									
										
										
										
											2022-04-26 16:20:25 +03:00
										 |  |  | 			: typeof(ast) == 'string' ? | 
					
						
							|  |  |  | 				this.group(page, ast) | 
					
						
							|  |  |  | 			: ast instanceof types.Generator ? | 
					
						
							|  |  |  | 				ast | 
					
						
							|  |  |  | 			: ast.iter() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		while(true){ | 
					
						
							|  |  |  | 			var {value, done} = ast.next() | 
					
						
							|  |  |  | 			if(done){ | 
					
						
							|  |  |  | 				return } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			// text block...
 | 
					
						
							|  |  |  | 			if(typeof(value) == 'string'){ | 
					
						
							|  |  |  | 				yield value  | 
					
						
							|  |  |  | 				continue } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			// macro...
 | 
					
						
							|  |  |  | 			var {name, args, body} = value | 
					
						
							| 
									
										
										
										
											2022-05-02 16:56:43 +03:00
										 |  |  | 			// nested macro -- skip...
 | 
					
						
							| 
									
										
										
										
											2022-05-22 15:06:30 +03:00
										 |  |  | 			if(typeof(page.macros[name]) != 'function'){ | 
					
						
							| 
									
										
										
										
											2022-05-02 16:56:43 +03:00
										 |  |  | 				continue } | 
					
						
							| 
									
										
										
										
											2022-05-16 11:02:19 +03:00
										 |  |  | 			// args...
 | 
					
						
							|  |  |  | 			args = this.parseArgs.call(page, | 
					
						
							|  |  |  | 				page.macros[name].arg_spec  | 
					
						
							|  |  |  | 					?? [],  | 
					
						
							| 
									
										
										
										
											2022-05-18 18:13:19 +03:00
										 |  |  | 				args, | 
					
						
							|  |  |  | 				state) | 
					
						
							| 
									
										
										
										
											2022-05-16 11:02:19 +03:00
										 |  |  | 			// call...
 | 
					
						
							| 
									
										
										
										
											2022-04-26 16:20:25 +03:00
										 |  |  | 			var res =  | 
					
						
							| 
									
										
										
										
											2022-06-15 16:10:16 +03:00
										 |  |  | 				await page.macros[name].call(page, args, body, state, value) | 
					
						
							| 
									
										
										
										
											2022-04-26 16:20:25 +03:00
										 |  |  | 					?? '' | 
					
						
							| 
									
										
										
										
											2022-05-16 11:02:19 +03:00
										 |  |  | 			// result...
 | 
					
						
							| 
									
										
										
										
											2022-04-26 16:20:25 +03:00
										 |  |  | 			if(res instanceof Array  | 
					
						
							|  |  |  | 					|| page.macros[name] instanceof types.Generator){ | 
					
						
							|  |  |  | 				yield* res | 
					
						
							|  |  |  | 			} else { | 
					
						
							|  |  |  | 				yield res } } }, | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Fully parse a page...
 | 
					
						
							|  |  |  | 	//
 | 
					
						
							|  |  |  | 	// This runs in two stages:
 | 
					
						
							| 
									
										
										
										
											2022-04-26 20:58:09 +03:00
										 |  |  | 	// 	- expand the page
 | 
					
						
							|  |  |  | 	// 		- lex the page -- .lex(..)
 | 
					
						
							|  |  |  | 	// 		- group block elements -- .group(..)
 | 
					
						
							|  |  |  | 	// 		- expand macros -- .expand(..)
 | 
					
						
							| 
									
										
										
										
											2022-04-26 16:20:25 +03:00
										 |  |  | 	// 	- apply filters
 | 
					
						
							|  |  |  | 	//
 | 
					
						
							|  |  |  | 	// XXX add a special filter to clear pending filters... (???)
 | 
					
						
							| 
									
										
										
										
											2022-06-15 16:10:16 +03:00
										 |  |  | 	parse: async function(page, ast, state={}){ | 
					
						
							| 
									
										
										
										
											2022-04-26 16:20:25 +03:00
										 |  |  | 		var that = this | 
					
						
							|  |  |  | 		// XXX should we handle strings as input???
 | 
					
						
							|  |  |  | 		ast = ast  | 
					
						
							| 
									
										
										
										
											2022-06-15 16:10:16 +03:00
										 |  |  | 			?? await this.expand(page, null, state) | 
					
						
							| 
									
										
										
										
											2022-04-26 20:58:09 +03:00
										 |  |  | 		ast = typeof(ast) == 'string' ? | 
					
						
							| 
									
										
										
										
											2022-06-15 16:10:16 +03:00
										 |  |  | 			await this.expand(page, ast, state) | 
					
						
							| 
									
										
										
										
											2022-04-26 20:58:09 +03:00
										 |  |  | 			: ast | 
					
						
							| 
									
										
										
										
											2022-04-26 16:20:25 +03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-06-15 16:10:16 +03:00
										 |  |  | 		//* XXX this is quite ugly...
 | 
					
						
							|  |  |  | 		var blocks = [] | 
					
						
							|  |  |  | 		for await (var a of ast){ | 
					
						
							|  |  |  | 			blocks.push(a) } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		return blocks | 
					
						
							|  |  |  | 		/*/ | 
					
						
							| 
									
										
										
										
											2022-04-26 16:20:25 +03:00
										 |  |  | 		return [...ast] | 
					
						
							| 
									
										
										
										
											2022-06-15 16:10:16 +03:00
										 |  |  | 		//*/
 | 
					
						
							| 
									
										
										
										
											2022-04-27 14:28:19 +03:00
										 |  |  | 			// post handlers...
 | 
					
						
							| 
									
										
										
										
											2022-04-26 16:20:25 +03:00
										 |  |  | 			.map(function(section){ | 
					
						
							| 
									
										
										
										
											2022-05-22 15:06:30 +03:00
										 |  |  | 				return typeof(section) == 'function' ?  | 
					
						
							| 
									
										
										
										
											2022-04-27 14:28:19 +03:00
										 |  |  | 					section.call(page, state) | 
					
						
							| 
									
										
										
										
											2022-04-27 13:06:40 +03:00
										 |  |  | 					: section }) | 
					
						
							|  |  |  | 			.flat() | 
					
						
							|  |  |  | 			// filters...
 | 
					
						
							|  |  |  | 			.map(function(section){ | 
					
						
							| 
									
										
										
										
											2022-04-27 14:28:19 +03:00
										 |  |  | 				return ( | 
					
						
							|  |  |  | 					// expand section...
 | 
					
						
							|  |  |  | 					typeof(section) != 'string' ? | 
					
						
							|  |  |  | 						section.data | 
					
						
							|  |  |  | 					// global filters... 
 | 
					
						
							|  |  |  | 					: state.filters ? | 
					
						
							| 
									
										
										
										
											2022-04-27 13:06:40 +03:00
										 |  |  | 						that.normalizeFilters(state.filters) | 
					
						
							| 
									
										
										
										
											2022-04-26 16:20:25 +03:00
										 |  |  | 							.reduce(function(res, filter){ | 
					
						
							|  |  |  | 								if(page.filters[filter] == null){ | 
					
						
							| 
									
										
										
										
											2022-05-25 01:01:22 +03:00
										 |  |  | 									/* XXX | 
					
						
							| 
									
										
										
										
											2022-04-26 16:20:25 +03:00
										 |  |  | 									throw new Error( | 
					
						
							|  |  |  | 										'.parse(..): unsupported filter: '+ filter) } | 
					
						
							| 
									
										
										
										
											2022-05-25 01:01:22 +03:00
										 |  |  | 									/*/ | 
					
						
							|  |  |  | 									console.warn( | 
					
						
							|  |  |  | 										'.parse(..): unsupported filter: '+ filter)  | 
					
						
							|  |  |  | 									return res } | 
					
						
							|  |  |  | 									//*/
 | 
					
						
							| 
									
										
										
										
											2022-04-26 16:20:25 +03:00
										 |  |  | 								return page.filters[filter].call(page, res)  | 
					
						
							|  |  |  | 									?? res }, section) | 
					
						
							| 
									
										
										
										
											2022-04-27 14:28:19 +03:00
										 |  |  | 					// no global filters...
 | 
					
						
							|  |  |  | 					: section ) }) | 
					
						
							| 
									
										
										
										
											2022-04-26 16:20:25 +03:00
										 |  |  | 			.flat() | 
					
						
							|  |  |  | 			.join('') }, | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2022-04-13 03:14:21 +03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-05-02 02:18:11 +03:00
										 |  |  | var parser = | 
					
						
							|  |  |  | module.parser = { | 
					
						
							|  |  |  | 	__proto__: BaseParser, | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-04-13 03:14:21 +03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-05-03 12:28:03 +03:00
										 |  |  | // -  - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 
 | 
					
						
							| 
									
										
										
										
											2022-05-16 11:54:21 +03:00
										 |  |  | // XXX should these be something more generic like Object.assign(..) ???
 | 
					
						
							| 
									
										
										
										
											2022-05-03 12:28:03 +03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-05-03 01:36:27 +03:00
										 |  |  | // XXX revise...
 | 
					
						
							|  |  |  | var Filter =  | 
					
						
							|  |  |  | module.Filter = | 
					
						
							|  |  |  | function(...args){ | 
					
						
							|  |  |  | 	var func = args.pop() | 
					
						
							|  |  |  | 	args.length > 0 | 
					
						
							|  |  |  | 		&& Object.assign(func, args.pop()) | 
					
						
							|  |  |  | 	return func } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-05-16 11:54:21 +03:00
										 |  |  | // XXX do we need anything else like .doc, attrs???
 | 
					
						
							|  |  |  | var Macro = | 
					
						
							|  |  |  | module.Macro =  | 
					
						
							|  |  |  | function(spec, func){ | 
					
						
							|  |  |  | 	var args = [...arguments] | 
					
						
							|  |  |  | 	// function...
 | 
					
						
							|  |  |  | 	func = args.pop() | 
					
						
							|  |  |  | 	// arg sepc...
 | 
					
						
							|  |  |  | 	;(args.length > 0 && args[args.length-1] instanceof Array) | 
					
						
							|  |  |  | 		&& (func.arg_spec = args.pop()) | 
					
						
							|  |  |  | 	// XXX do we need anything else like .doc, attrs???
 | 
					
						
							|  |  |  | 	return func } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-05-03 01:36:27 +03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-05-03 12:28:03 +03:00
										 |  |  | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-05-18 02:55:46 +03:00
										 |  |  | module.PAGE_NOT_FOUND = '404: PAGE NOT FOUND: $PATH' | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-04-25 20:53:20 +03:00
										 |  |  | // XXX PATH_VARS need to handle path variables...
 | 
					
						
							| 
									
										
										
										
											2022-05-25 09:37:38 +03:00
										 |  |  | // XXX filters (and macros?) should be features for simpler plugin handlng (???)
 | 
					
						
							|  |  |  | // XXX STUB filters...
 | 
					
						
							| 
									
										
										
										
											2022-04-21 18:33:08 +03:00
										 |  |  | var Page = | 
					
						
							|  |  |  | module.Page =  | 
					
						
							|  |  |  | object.Constructor('Page', BasePage, { | 
					
						
							| 
									
										
										
										
											2022-05-03 12:28:03 +03:00
										 |  |  | 	// Filter that will isolate the page/include/.. from parent filters...
 | 
					
						
							| 
									
										
										
										
											2022-04-26 03:52:28 +03:00
										 |  |  | 	ISOLATED_FILTERS: 'isolated', | 
					
						
							| 
									
										
										
										
											2022-04-25 20:53:20 +03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-05-03 12:28:03 +03:00
										 |  |  | 	// list of macros that will get raw text of their content...
 | 
					
						
							| 
									
										
										
										
											2022-05-02 16:56:43 +03:00
										 |  |  | 	QUOTING_MACROS: ['quote'], | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-05-25 01:01:22 +03:00
										 |  |  | 	// templates used to render a page via .text
 | 
					
						
							|  |  |  | 	PAGE_TPL: '_text', | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-05-18 02:37:55 +03:00
										 |  |  | 	// NOTE: comment this out to make the system fail when nothing is 
 | 
					
						
							|  |  |  | 	// 		resolved, not even the System/NotFound page...
 | 
					
						
							| 
									
										
										
										
											2022-05-18 02:55:46 +03:00
										 |  |  | 	// NOTE: we can't use any of the page actions here (like @source(./path)) 
 | 
					
						
							|  |  |  | 	// 		as if we reach this it's likely all the bootstrap is either also
 | 
					
						
							|  |  |  | 	// 		not present or broken.
 | 
					
						
							|  |  |  | 	// NOTE: to force the system to fail set this to undefined.
 | 
					
						
							|  |  |  | 	PAGE_NOT_FOUND: module.PAGE_NOT_FOUND, | 
					
						
							| 
									
										
										
										
											2022-05-17 20:30:26 +03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-04-26 14:42:01 +03:00
										 |  |  | 	//
 | 
					
						
							|  |  |  | 	// 	<filter>(<source>)
 | 
					
						
							|  |  |  | 	// 		-> <result>
 | 
					
						
							|  |  |  | 	// 		-> undefined
 | 
					
						
							|  |  |  | 	//
 | 
					
						
							| 
									
										
										
										
											2022-04-25 20:53:20 +03:00
										 |  |  | 	// XXX might be a good idea to fix filter order...
 | 
					
						
							| 
									
										
										
										
											2022-04-22 00:14:41 +03:00
										 |  |  | 	filters: { | 
					
						
							| 
									
										
										
										
											2022-04-26 14:42:01 +03:00
										 |  |  | 		// placeholders...
 | 
					
						
							|  |  |  | 		nofilters: function(){}, | 
					
						
							|  |  |  | 		isolated: function(){}, | 
					
						
							| 
									
										
										
										
											2022-04-25 20:53:20 +03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-04-26 14:42:01 +03:00
										 |  |  | 		// XXX TESTING...
 | 
					
						
							|  |  |  | 		dummy: function(){}, | 
					
						
							| 
									
										
										
										
											2022-04-25 20:53:20 +03:00
										 |  |  | 		test: function(source){ | 
					
						
							|  |  |  | 			return source  | 
					
						
							| 
									
										
										
										
											2022-05-02 17:18:31 +03:00
										 |  |  | 				.replace(/test/g, 'TEST') }, | 
					
						
							| 
									
										
										
										
											2022-04-25 20:53:20 +03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-05-03 01:36:27 +03:00
										 |  |  | 		wikiword: Filter( | 
					
						
							|  |  |  | 			{quote: 'quote-wikiword'}, | 
					
						
							|  |  |  | 			function(source){ | 
					
						
							|  |  |  | 				// XXX
 | 
					
						
							|  |  |  | 				return source }), | 
					
						
							|  |  |  | 		'quote-wikiword': function(source){ | 
					
						
							| 
									
										
										
										
											2022-04-26 14:42:01 +03:00
										 |  |  | 			// XXX
 | 
					
						
							| 
									
										
										
										
											2022-04-25 20:53:20 +03:00
										 |  |  | 			return source }, | 
					
						
							| 
									
										
										
										
											2022-05-03 01:36:27 +03:00
										 |  |  | 
 | 
					
						
							|  |  |  | 		markdown: Filter( | 
					
						
							|  |  |  | 			{quote: 'quote-markdown'}, | 
					
						
							|  |  |  | 			function(source){ | 
					
						
							| 
									
										
										
										
											2022-05-18 02:37:55 +03:00
										 |  |  | 				// XXX
 | 
					
						
							| 
									
										
										
										
											2022-05-03 01:36:27 +03:00
										 |  |  | 				return source }), | 
					
						
							|  |  |  | 		'quote-markdown': function(source){ | 
					
						
							| 
									
										
										
										
											2022-04-26 14:42:01 +03:00
										 |  |  | 			// XXX
 | 
					
						
							| 
									
										
										
										
											2022-04-25 20:53:20 +03:00
										 |  |  | 			return source }, | 
					
						
							| 
									
										
										
										
											2022-04-22 00:14:41 +03:00
										 |  |  | 	}, | 
					
						
							| 
									
										
										
										
											2022-04-26 03:52:28 +03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-05-03 12:28:03 +03:00
										 |  |  | 	//
 | 
					
						
							|  |  |  | 	// 	<macro>(<args>, <body>, <state>){ .. }
 | 
					
						
							|  |  |  | 	// 		-> undefined
 | 
					
						
							|  |  |  | 	// 		-> <text>
 | 
					
						
							|  |  |  | 	// 		-> <array>
 | 
					
						
							|  |  |  | 	// 		-> <iterator>
 | 
					
						
							|  |  |  | 	// 		-> <func>(<state>)
 | 
					
						
							|  |  |  | 	// 			-> ...
 | 
					
						
							|  |  |  | 	//
 | 
					
						
							| 
									
										
										
										
											2022-05-18 02:37:55 +03:00
										 |  |  | 	// XXX ASYNC make these support async page getters...
 | 
					
						
							| 
									
										
										
										
											2022-04-22 00:14:41 +03:00
										 |  |  | 	macros: { | 
					
						
							| 
									
										
										
										
											2022-05-18 02:37:55 +03:00
										 |  |  | 		// 
 | 
					
						
							|  |  |  | 		// 	<now/>
 | 
					
						
							|  |  |  | 		//
 | 
					
						
							| 
									
										
										
										
											2022-04-22 00:14:41 +03:00
										 |  |  | 		now: function(){ | 
					
						
							| 
									
										
										
										
											2022-04-25 16:00:12 +03:00
										 |  |  | 			return ''+ Date.now() }, | 
					
						
							| 
									
										
										
										
											2022-04-26 03:52:28 +03:00
										 |  |  | 		//
 | 
					
						
							|  |  |  | 		// 	@filter(<filter-spec>)
 | 
					
						
							|  |  |  | 		// 	<filter <filter-spec>/>
 | 
					
						
							|  |  |  | 		//
 | 
					
						
							|  |  |  | 		// 	<filter <filter-spec>>
 | 
					
						
							|  |  |  | 		// 		...
 | 
					
						
							|  |  |  | 		// 	</filter>
 | 
					
						
							|  |  |  | 		//
 | 
					
						
							|  |  |  | 		// 	<filter-spec> ::=
 | 
					
						
							|  |  |  | 		// 		<filter> <filter-spec>
 | 
					
						
							|  |  |  | 		// 		| -<filter> <filter-spec>
 | 
					
						
							|  |  |  | 		//
 | 
					
						
							| 
									
										
										
										
											2022-05-03 01:36:27 +03:00
										 |  |  | 		filter: function(args, body, state, expand=true){ | 
					
						
							| 
									
										
										
										
											2022-05-02 23:56:32 +03:00
										 |  |  | 			var that = this | 
					
						
							| 
									
										
										
										
											2022-04-25 20:53:20 +03:00
										 |  |  | 			var filters = state.filters =  | 
					
						
							| 
									
										
										
										
											2022-04-27 23:31:11 +03:00
										 |  |  | 				state.filters ?? [] | 
					
						
							|  |  |  | 			// separate local filters...
 | 
					
						
							| 
									
										
										
										
											2022-04-25 16:00:12 +03:00
										 |  |  | 			if(body){ | 
					
						
							| 
									
										
										
										
											2022-04-27 23:31:11 +03:00
										 |  |  | 				var outer_filters = filters | 
					
						
							|  |  |  | 				filters = state.filters = | 
					
						
							|  |  |  | 					[outer_filters] } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			// merge in new filters...
 | 
					
						
							| 
									
										
										
										
											2022-05-16 11:54:21 +03:00
										 |  |  | 			var local = Object.keys(args) | 
					
						
							| 
									
										
										
										
											2022-05-03 01:36:27 +03:00
										 |  |  | 			filters.splice(filters.length, 0, ...local) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			// trigger quote-filter...
 | 
					
						
							|  |  |  | 			var quote = local | 
					
						
							|  |  |  | 				.map(function(filter){ | 
					
						
							| 
									
										
										
										
											2022-05-03 12:28:03 +03:00
										 |  |  | 					return (that.filters[filter] ?? {})['quote'] ?? [] }) | 
					
						
							| 
									
										
										
										
											2022-05-03 01:36:27 +03:00
										 |  |  | 				.flat() | 
					
						
							|  |  |  | 			quote.length > 0 | 
					
						
							|  |  |  | 				&& this.macros['quote-filter'] | 
					
						
							|  |  |  | 					.call(this, Object.fromEntries(Object.entries(quote)), null, state) | 
					
						
							| 
									
										
										
										
											2022-04-27 23:31:11 +03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-04-25 20:53:20 +03:00
										 |  |  | 			// local filters...
 | 
					
						
							|  |  |  | 			if(body){ | 
					
						
							| 
									
										
										
										
											2022-04-26 03:52:28 +03:00
										 |  |  | 				// isolate from parent...
 | 
					
						
							|  |  |  | 				state.filters.includes(this.ISOLATED_FILTERS) | 
					
						
							|  |  |  | 					&& state.filters[0] instanceof Array | 
					
						
							|  |  |  | 					&& state.filters.shift() | 
					
						
							| 
									
										
										
										
											2022-04-27 14:28:19 +03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-04-27 23:31:11 +03:00
										 |  |  | 				// expand the body...
 | 
					
						
							| 
									
										
										
										
											2022-05-02 18:29:59 +03:00
										 |  |  | 				var ast = expand ? | 
					
						
							|  |  |  | 						[...this.__parser__.expand(this, body, state)] | 
					
						
							|  |  |  | 					: body instanceof Array ? | 
					
						
							|  |  |  | 						body | 
					
						
							| 
									
										
										
										
											2022-05-02 23:56:32 +03:00
										 |  |  | 					// NOTE: wrapping the body in an array effectively 
 | 
					
						
							|  |  |  | 					// 		escapes it from parsing...
 | 
					
						
							| 
									
										
										
										
											2022-05-02 18:29:59 +03:00
										 |  |  | 					: [body] | 
					
						
							| 
									
										
										
										
											2022-04-27 14:28:19 +03:00
										 |  |  | 				filters = state.filters | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-04-27 23:31:11 +03:00
										 |  |  | 				state.filters = outer_filters | 
					
						
							| 
									
										
										
										
											2022-04-27 14:28:19 +03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-04-27 23:31:11 +03:00
										 |  |  | 				// parse the body after we are done expanding...
 | 
					
						
							| 
									
										
										
										
											2022-06-15 16:10:16 +03:00
										 |  |  | 				return async function(state){ | 
					
						
							| 
									
										
										
										
											2022-04-27 23:31:11 +03:00
										 |  |  | 					var outer_filters = state.filters | 
					
						
							| 
									
										
										
										
											2022-04-27 14:28:19 +03:00
										 |  |  | 					state.filters = this.__parser__.normalizeFilters(filters) | 
					
						
							| 
									
										
										
										
											2022-05-02 23:56:32 +03:00
										 |  |  | 					var res = | 
					
						
							| 
									
										
										
										
											2022-06-15 16:10:16 +03:00
										 |  |  | 						[...await this.parse(ast, state)] | 
					
						
							| 
									
										
										
										
											2022-05-02 23:56:32 +03:00
										 |  |  | 							.flat() | 
					
						
							|  |  |  | 							.join('')  | 
					
						
							| 
									
										
										
										
											2022-04-27 23:31:11 +03:00
										 |  |  | 					state.filters = outer_filters | 
					
						
							|  |  |  | 					return { data: res } } } }, | 
					
						
							| 
									
										
										
										
											2022-04-27 13:06:40 +03:00
										 |  |  | 		//
 | 
					
						
							|  |  |  | 		// 	@include(<path>)
 | 
					
						
							|  |  |  | 		//
 | 
					
						
							|  |  |  | 		// 	@include(<path> isolated recursive=<text>)
 | 
					
						
							|  |  |  | 		// 	@include(src=<path> isolated recursive=<text>)
 | 
					
						
							|  |  |  | 		//
 | 
					
						
							|  |  |  | 		// 	<include src=<path> .. >
 | 
					
						
							|  |  |  | 		// 		<text>
 | 
					
						
							|  |  |  | 		// 	</include>
 | 
					
						
							|  |  |  | 		//
 | 
					
						
							| 
									
										
										
										
											2022-05-24 10:45:53 +03:00
										 |  |  | 		// XXX RECURSION recursion detection is still a bit off...
 | 
					
						
							| 
									
										
										
										
											2022-04-26 20:58:09 +03:00
										 |  |  | 		// XXX 'text' argument is changed to 'recursive'...
 | 
					
						
							| 
									
										
										
										
											2022-05-18 18:13:19 +03:00
										 |  |  | 		// XXX revise recursion checks.... 
 | 
					
						
							| 
									
										
										
										
											2022-04-26 20:58:09 +03:00
										 |  |  | 		// XXX should this be lazy???
 | 
					
						
							| 
									
										
										
										
											2022-05-16 11:54:21 +03:00
										 |  |  | 		include: Macro( | 
					
						
							|  |  |  | 			['src', 'recursive', ['isolated']], | 
					
						
							| 
									
										
										
										
											2022-06-15 16:10:16 +03:00
										 |  |  | 			async function(args, body, state, key='included', handler){ | 
					
						
							| 
									
										
										
										
											2022-05-24 10:45:53 +03:00
										 |  |  | 				var macro = 'include' | 
					
						
							|  |  |  | 				if(typeof(args) == 'string'){ | 
					
						
							|  |  |  | 					var [macro, args, body, state, key="included", handler] = arguments } | 
					
						
							| 
									
										
										
										
											2022-05-16 11:54:21 +03:00
										 |  |  | 				// positional args...
 | 
					
						
							|  |  |  | 				var src = args.src | 
					
						
							|  |  |  | 				var recursive = args.recursive || body | 
					
						
							|  |  |  | 				var isolated = args.isolated  | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 				if(!src){ | 
					
						
							| 
									
										
										
										
											2022-05-18 18:13:19 +03:00
										 |  |  | 					return } | 
					
						
							|  |  |  | 				// parse arg values...
 | 
					
						
							| 
									
										
										
										
											2022-06-15 16:10:16 +03:00
										 |  |  | 				src = await this.parse(src, state) | 
					
						
							| 
									
										
										
										
											2022-05-16 11:54:21 +03:00
										 |  |  | 
 | 
					
						
							|  |  |  | 				handler = handler  | 
					
						
							|  |  |  | 					?? function(){ | 
					
						
							|  |  |  | 						return this.get(src) | 
					
						
							|  |  |  | 							.parse( | 
					
						
							|  |  |  | 								isolated ?  | 
					
						
							|  |  |  | 									{[key]: state[key]}  | 
					
						
							|  |  |  | 									: state) } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 				// handle recursion...
 | 
					
						
							|  |  |  | 				var parent_seen = state[key] | 
					
						
							|  |  |  | 				var seen = state[key] =  | 
					
						
							|  |  |  | 					(state[key] ?? [this.location]).slice() | 
					
						
							|  |  |  | 				// recursion detected...
 | 
					
						
							| 
									
										
										
										
											2022-05-25 01:01:22 +03:00
										 |  |  | 				// XXX RECURSION revise this...
 | 
					
						
							|  |  |  | 				if(seen.includes(src)){ | 
					
						
							| 
									
										
										
										
											2022-05-16 11:54:21 +03:00
										 |  |  | 					if(!recursive){ | 
					
						
							|  |  |  | 						throw new Error( | 
					
						
							| 
									
										
										
										
											2022-05-24 10:45:53 +03:00
										 |  |  | 							macro +': recursion detected: ' | 
					
						
							| 
									
										
										
										
											2022-05-18 18:13:19 +03:00
										 |  |  | 								+ seen.concat([src]).join(' -> ')) } | 
					
						
							| 
									
										
										
										
											2022-05-16 11:54:21 +03:00
										 |  |  | 					// have the 'recursive' arg...
 | 
					
						
							| 
									
										
										
										
											2022-05-18 18:13:19 +03:00
										 |  |  | 					return this.parse(recursive, state) } | 
					
						
							|  |  |  | 				seen.push(src) | 
					
						
							| 
									
										
										
										
											2022-05-16 11:54:21 +03:00
										 |  |  | 
 | 
					
						
							|  |  |  | 				// load the included page...
 | 
					
						
							| 
									
										
										
										
											2022-06-15 16:10:16 +03:00
										 |  |  | 				var res = await handler.call(this) | 
					
						
							| 
									
										
										
										
											2022-05-16 11:54:21 +03:00
										 |  |  | 
 | 
					
						
							|  |  |  | 				// restore previous include chain...
 | 
					
						
							|  |  |  | 				if(parent_seen){ | 
					
						
							|  |  |  | 					state[key] = parent_seen | 
					
						
							|  |  |  | 				} else { | 
					
						
							|  |  |  | 					delete state[key] } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 				return res }), | 
					
						
							|  |  |  | 		source: Macro( | 
					
						
							|  |  |  | 			['src'], | 
					
						
							| 
									
										
										
										
											2022-06-15 16:10:16 +03:00
										 |  |  | 			async function(args, body, state){ | 
					
						
							| 
									
										
										
										
											2022-05-16 11:54:21 +03:00
										 |  |  | 				var src = args.src | 
					
						
							| 
									
										
										
										
											2022-05-18 18:13:19 +03:00
										 |  |  | 				// parse arg values...
 | 
					
						
							|  |  |  | 				src = src ?  | 
					
						
							| 
									
										
										
										
											2022-06-15 16:10:16 +03:00
										 |  |  | 					await this.parse(src, state)  | 
					
						
							| 
									
										
										
										
											2022-05-18 18:13:19 +03:00
										 |  |  | 					: src | 
					
						
							| 
									
										
										
										
											2022-05-16 11:54:21 +03:00
										 |  |  | 				return this.macros.include.call(this,  | 
					
						
							| 
									
										
										
										
											2022-05-24 10:45:53 +03:00
										 |  |  | 					'source', | 
					
						
							| 
									
										
										
										
											2022-05-16 11:54:21 +03:00
										 |  |  | 					args, body, state, 'sources',  | 
					
						
							| 
									
										
										
										
											2022-06-15 16:10:16 +03:00
										 |  |  | 					async function(){ | 
					
						
							|  |  |  | 						return await this.parse(await this.get(src).raw +'', state) }) }), | 
					
						
							| 
									
										
										
										
											2022-05-18 02:37:55 +03:00
										 |  |  | 		//
 | 
					
						
							| 
									
										
										
										
											2022-05-02 16:56:43 +03:00
										 |  |  | 		// 	@quote(<src>)
 | 
					
						
							|  |  |  | 		//
 | 
					
						
							|  |  |  | 		// 	<quote src=<src>[ filter="<filter> ..."]/>
 | 
					
						
							|  |  |  | 		//
 | 
					
						
							|  |  |  | 		// 	<quote text=" .. "[ filter="<filter> ..."]/>
 | 
					
						
							|  |  |  | 		//
 | 
					
						
							|  |  |  | 		// 	<quote[ filter="<filter> ..."]>
 | 
					
						
							|  |  |  | 		// 		..
 | 
					
						
							|  |  |  | 		// 	</quote>
 | 
					
						
							|  |  |  | 		//
 | 
					
						
							|  |  |  | 		//
 | 
					
						
							|  |  |  | 		// NOTE: src ant text arguments are mutually exclusive, src takes 
 | 
					
						
							|  |  |  | 		// 		priority.
 | 
					
						
							| 
									
										
										
										
											2022-05-02 18:29:59 +03:00
										 |  |  | 		// NOTE: the filter argument has the same semantics as the filter 
 | 
					
						
							|  |  |  | 		// 		macro with one exception, when used in quote, the body is 
 | 
					
						
							|  |  |  | 		// 		not expanded...
 | 
					
						
							|  |  |  | 		//
 | 
					
						
							| 
									
										
										
										
											2022-05-03 01:36:27 +03:00
										 |  |  | 		// XXX need a way to escape macros -- i.e. include </quote> in a quoted text...
 | 
					
						
							| 
									
										
										
										
											2022-05-16 11:54:21 +03:00
										 |  |  | 		quote: Macro( | 
					
						
							|  |  |  | 			['src', 'filter', 'text'], | 
					
						
							| 
									
										
										
										
											2022-06-15 16:10:16 +03:00
										 |  |  | 			async function(args, body, state){ | 
					
						
							| 
									
										
										
										
											2022-05-16 11:54:21 +03:00
										 |  |  | 				var src = args.src //|| args[0]
 | 
					
						
							|  |  |  | 				var text = args.text  | 
					
						
							|  |  |  | 					?? body  | 
					
						
							|  |  |  | 					?? [] | 
					
						
							| 
									
										
										
										
											2022-05-18 18:13:19 +03:00
										 |  |  | 				// parse arg values...
 | 
					
						
							|  |  |  | 				src = src ?  | 
					
						
							| 
									
										
										
										
											2022-06-15 16:10:16 +03:00
										 |  |  | 					await this.parse(src, state) | 
					
						
							| 
									
										
										
										
											2022-05-18 18:13:19 +03:00
										 |  |  | 					: src | 
					
						
							| 
									
										
										
										
											2022-05-16 11:54:21 +03:00
										 |  |  | 				text = src ? | 
					
						
							|  |  |  | 						// source page...
 | 
					
						
							| 
									
										
										
										
											2022-06-15 16:10:16 +03:00
										 |  |  | 						await this.get(src).raw | 
					
						
							| 
									
										
										
										
											2022-05-16 11:54:21 +03:00
										 |  |  | 					: text instanceof Array ? | 
					
						
							|  |  |  | 						text.join('') | 
					
						
							|  |  |  | 					: text | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 				// empty...
 | 
					
						
							|  |  |  | 				if(!text){ | 
					
						
							|  |  |  | 					return } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 				var filters =  | 
					
						
							|  |  |  | 					args.filter  | 
					
						
							|  |  |  | 						&& args.filter | 
					
						
							|  |  |  | 							.trim() | 
					
						
							|  |  |  | 							.split(/\s+/g) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 				// NOTE: we are delaying .quote_filters handling here to 
 | 
					
						
							|  |  |  | 				// 		make their semantics the same as general filters...
 | 
					
						
							|  |  |  | 				// 		...and since we are internally calling .filter(..)
 | 
					
						
							|  |  |  | 				// 		macro we need to dance around it's architecture too...
 | 
					
						
							|  |  |  | 				// NOTE: since the body of quote(..) only has filters applied 
 | 
					
						
							|  |  |  | 				// 		to it doing the first stage of .filter(..) as late 
 | 
					
						
							|  |  |  | 				// 		as the second stage here will have no ill effect...
 | 
					
						
							|  |  |  | 				return function(state){ | 
					
						
							|  |  |  | 					// add global quote-filters...
 | 
					
						
							|  |  |  | 					filters = | 
					
						
							|  |  |  | 						(state.quote_filters  | 
					
						
							|  |  |  | 								&& !(filters ?? []).includes(this.ISOLATED_FILTERS)) ? | 
					
						
							|  |  |  | 							[...state.quote_filters, ...(filters ?? [])] | 
					
						
							|  |  |  | 							: filters | 
					
						
							|  |  |  | 					if(filters){ | 
					
						
							|  |  |  | 						filters = Object.fromEntries(Object.entries(filters)) | 
					
						
							|  |  |  | 						return this.macros.filter | 
					
						
							|  |  |  | 							.call(this, filters, text, state, false) | 
					
						
							|  |  |  | 							.call(this, state) } | 
					
						
							|  |  |  | 					return text } }), | 
					
						
							| 
									
										
										
										
											2022-05-03 01:36:27 +03:00
										 |  |  | 		// very similar to @filter(..) but will affect @quote(..) filters...
 | 
					
						
							|  |  |  | 		'quote-filter': function(args, body, state){ | 
					
						
							|  |  |  | 			var filters = state.quote_filters =  | 
					
						
							|  |  |  | 				state.quote_filters ?? [] | 
					
						
							| 
									
										
										
										
											2022-05-16 11:54:21 +03:00
										 |  |  | 			filters.splice(filters.length, 0, ...Object.keys(args)) }, | 
					
						
							| 
									
										
										
										
											2022-04-28 15:53:21 +03:00
										 |  |  | 		//
 | 
					
						
							|  |  |  | 		//	<slot name=<name>/>
 | 
					
						
							|  |  |  | 		//
 | 
					
						
							|  |  |  | 		//	<slot name=<name> text=<text>/>
 | 
					
						
							|  |  |  | 		//
 | 
					
						
							|  |  |  | 		//	<slot name=<name>>
 | 
					
						
							|  |  |  | 		//		...
 | 
					
						
							|  |  |  | 		//	</slot>
 | 
					
						
							|  |  |  | 		//
 | 
					
						
							|  |  |  | 		//	Force show a slot...
 | 
					
						
							|  |  |  | 		//	<slot shown ... />
 | 
					
						
							|  |  |  | 		//
 | 
					
						
							|  |  |  | 		//	Force hide a slot...
 | 
					
						
							|  |  |  | 		//	<slot hidden ... />
 | 
					
						
							|  |  |  | 		//
 | 
					
						
							|  |  |  | 		//
 | 
					
						
							|  |  |  | 		// NOTE: by default only the first slot with <name> is visible, 
 | 
					
						
							|  |  |  | 		// 		all other slot with <name> will replace its content, unless
 | 
					
						
							|  |  |  | 		// 		explicit shown/hidden arguments are given.
 | 
					
						
							|  |  |  | 		// NOTE: hidden has precedence over shown if both are given.
 | 
					
						
							|  |  |  | 		//
 | 
					
						
							| 
									
										
										
										
											2022-04-27 13:06:40 +03:00
										 |  |  | 		// XXX how do we handle a slot defined within a slot????
 | 
					
						
							| 
									
										
										
										
											2022-04-28 18:01:03 +03:00
										 |  |  | 		// 		...seems that we'll fall into recursion on definition...
 | 
					
						
							| 
									
										
										
										
											2022-05-16 11:54:21 +03:00
										 |  |  | 		slot: Macro( | 
					
						
							|  |  |  | 			['name', 'text', ['shown', 'hidden']], | 
					
						
							| 
									
										
										
										
											2022-06-15 16:10:16 +03:00
										 |  |  | 			async function(args, body, state){ | 
					
						
							| 
									
										
										
										
											2022-05-16 11:54:21 +03:00
										 |  |  | 				var name = args.name | 
					
						
							|  |  |  | 				var text = args.text  | 
					
						
							|  |  |  | 					?? body  | 
					
						
							|  |  |  | 					// NOTE: this can't be undefined for .expand(..) to work 
 | 
					
						
							|  |  |  | 					// 		correctly...
 | 
					
						
							|  |  |  | 					?? [] | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 				var slots = state.slots =  | 
					
						
							|  |  |  | 					state.slots  | 
					
						
							|  |  |  | 						?? {} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-05-18 18:13:19 +03:00
										 |  |  | 				// parse arg values...
 | 
					
						
							|  |  |  | 				name = name ? | 
					
						
							| 
									
										
										
										
											2022-06-15 16:10:16 +03:00
										 |  |  | 					await this.parse(name, state) | 
					
						
							| 
									
										
										
										
											2022-05-18 18:13:19 +03:00
										 |  |  | 					: name | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-05-16 11:54:21 +03:00
										 |  |  | 				//var hidden = name in slots
 | 
					
						
							|  |  |  | 				// XXX EXPERIMENTAL
 | 
					
						
							|  |  |  | 				var hidden =  | 
					
						
							|  |  |  | 					// 'hidden' has priority... 
 | 
					
						
							|  |  |  | 					args.hidden | 
					
						
							|  |  |  | 						// explicitly show... ()
 | 
					
						
							|  |  |  | 						|| (args.shown ? | 
					
						
							|  |  |  | 							false | 
					
						
							|  |  |  | 							// show first instance...
 | 
					
						
							|  |  |  | 							: name in slots) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-06-15 16:10:16 +03:00
										 |  |  | 				slots[name] = [...await this.__parser__.expand(this, text, state)] | 
					
						
							| 
									
										
										
										
											2022-05-16 11:54:21 +03:00
										 |  |  | 
 | 
					
						
							|  |  |  | 				return hidden ? | 
					
						
							|  |  |  | 					'' | 
					
						
							|  |  |  | 					: function(state){ | 
					
						
							|  |  |  | 						return state.slots[name] } }),  | 
					
						
							| 
									
										
										
										
											2022-04-30 01:39:23 +03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-05-18 02:37:55 +03:00
										 |  |  | 		// 	
 | 
					
						
							|  |  |  | 		// 	<macro src=<url>> .. </macro>
 | 
					
						
							|  |  |  | 		//
 | 
					
						
							|  |  |  | 		// 	<macro name=<name> src=<url> sort=<sort-spec>> .. </macro>
 | 
					
						
							|  |  |  | 		//
 | 
					
						
							|  |  |  | 		// 	<macro ...> ... </macro>
 | 
					
						
							|  |  |  | 		// 	<macro ... text=<text>/>
 | 
					
						
							|  |  |  | 		//
 | 
					
						
							|  |  |  | 		// 	<macro ... else=<text>> ... </macro>
 | 
					
						
							|  |  |  | 		// 	<macro ...>
 | 
					
						
							|  |  |  | 		// 		...
 | 
					
						
							|  |  |  | 		//
 | 
					
						
							| 
									
										
										
										
											2022-05-25 01:01:22 +03:00
										 |  |  | 		//
 | 
					
						
							|  |  |  | 		// 		<join>
 | 
					
						
							|  |  |  | 		// 			...
 | 
					
						
							|  |  |  | 		// 		</join>
 | 
					
						
							|  |  |  | 		//
 | 
					
						
							| 
									
										
										
										
											2022-05-18 02:37:55 +03:00
										 |  |  | 		// 		<else>
 | 
					
						
							|  |  |  | 		// 			...
 | 
					
						
							|  |  |  | 		// 		</else>
 | 
					
						
							|  |  |  | 		// 	</macro>
 | 
					
						
							|  |  |  | 		//
 | 
					
						
							| 
									
										
										
										
											2022-05-20 15:03:24 +03:00
										 |  |  | 		// NOTE: if both strict and nonstrict are given the later takes 
 | 
					
						
							|  |  |  | 		// 		precedence.
 | 
					
						
							| 
									
										
										
										
											2022-05-18 02:37:55 +03:00
										 |  |  | 		// XXX ELSE_PRIO should else attr take priority over the <else> tag???
 | 
					
						
							|  |  |  | 		// 		...currently as with text=... the attr takes priority...
 | 
					
						
							|  |  |  | 		// XXX SORT sorting not implemented yet....
 | 
					
						
							| 
									
										
										
										
											2022-05-20 15:22:39 +03:00
										 |  |  | 		// XXX should support arrays...
 | 
					
						
							|  |  |  | 		// 		e.g. 
 | 
					
						
							|  |  |  | 		// 			<macro src="/test/*/resolved"> ... </macro>
 | 
					
						
							|  |  |  | 		// 		...does not work yet...
 | 
					
						
							| 
									
										
										
										
											2022-05-16 11:54:21 +03:00
										 |  |  | 		macro: Macro( | 
					
						
							| 
									
										
										
										
											2022-05-25 01:01:22 +03:00
										 |  |  | 			['name', 'src', 'sort', 'text', 'join', 'else', ['strict', 'nonstrict']], | 
					
						
							| 
									
										
										
										
											2022-06-15 16:10:16 +03:00
										 |  |  | 			async function(args, body, state){ | 
					
						
							| 
									
										
										
										
											2022-05-16 11:54:21 +03:00
										 |  |  | 				var that = this | 
					
						
							|  |  |  | 				var name = args.name //?? args[0]
 | 
					
						
							|  |  |  | 				var src = args.src | 
					
						
							|  |  |  | 				var sort = (args.sort ?? '') | 
					
						
							|  |  |  | 					.split(/\s+/g) | 
					
						
							|  |  |  | 					.filter(function(e){  | 
					
						
							|  |  |  | 						return e != '' }) | 
					
						
							| 
									
										
										
										
											2022-05-18 02:37:55 +03:00
										 |  |  | 				// NOTE: args.text will need parsing...
 | 
					
						
							| 
									
										
										
										
											2022-05-16 11:54:21 +03:00
										 |  |  | 				var text = args.text  | 
					
						
							|  |  |  | 					?? body  | 
					
						
							|  |  |  | 					?? [] | 
					
						
							| 
									
										
										
										
											2022-05-18 02:37:55 +03:00
										 |  |  | 				text = typeof(text) == 'string' ? | 
					
						
							|  |  |  | 					[...this.__parser__.group(this, text+'</macro>', 'macro')] | 
					
						
							|  |  |  | 					: text | 
					
						
							| 
									
										
										
										
											2022-05-20 15:03:24 +03:00
										 |  |  | 				var strict = args.strict | 
					
						
							|  |  |  | 					&& !args.nonstrict | 
					
						
							| 
									
										
										
										
											2022-05-16 11:54:21 +03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-05-25 01:01:22 +03:00
										 |  |  | 				var _getBlock = function(name){ | 
					
						
							|  |  |  | 					var block = args[name] ? | 
					
						
							|  |  |  | 						[{ | 
					
						
							|  |  |  | 							args: {}, | 
					
						
							|  |  |  | 							body: args[name], | 
					
						
							|  |  |  | 						}] | 
					
						
							|  |  |  | 						: (text ?? []) | 
					
						
							|  |  |  | 							.filter(function(e){  | 
					
						
							|  |  |  | 								return typeof(e) != 'string'  | 
					
						
							|  |  |  | 									&& e.name == name }) | 
					
						
							|  |  |  | 					if(block.length == 0){ | 
					
						
							|  |  |  | 						return } | 
					
						
							|  |  |  | 					// NOTE: when multiple blocks are present the 
 | 
					
						
							|  |  |  | 					// 		last one is used...
 | 
					
						
							|  |  |  | 					block = block.pop() | 
					
						
							|  |  |  | 					block =  | 
					
						
							|  |  |  | 						block.args.text  | 
					
						
							|  |  |  | 							?? block.body | 
					
						
							|  |  |  | 					return block } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-05-16 11:54:21 +03:00
										 |  |  | 				if(name){ | 
					
						
							| 
									
										
										
										
											2022-06-15 16:10:16 +03:00
										 |  |  | 					name = await this.parse(name, state) | 
					
						
							| 
									
										
										
										
											2022-05-16 11:54:21 +03:00
										 |  |  | 					// define new named macro...
 | 
					
						
							|  |  |  | 					if(text){ | 
					
						
							|  |  |  | 						;(state.macros = state.macros ?? {})[name] = text | 
					
						
							|  |  |  | 					// use existing macro...
 | 
					
						
							|  |  |  | 					} else if(state.macros  | 
					
						
							|  |  |  | 							&& name in state.macros){ | 
					
						
							|  |  |  | 						text = state.macros[name] } } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 				if(src){ | 
					
						
							| 
									
										
										
										
											2022-06-15 16:10:16 +03:00
										 |  |  | 					src = await this.parse(src, state) | 
					
						
							| 
									
										
										
										
											2022-05-24 10:45:53 +03:00
										 |  |  | 					/* XXX ARRAY page... | 
					
						
							| 
									
										
										
										
											2022-05-20 15:03:24 +03:00
										 |  |  | 					var pages = this.get(src, strict).each() | 
					
						
							| 
									
										
										
										
											2022-05-24 10:45:53 +03:00
										 |  |  | 					/*/ | 
					
						
							|  |  |  | 					var pages = this.get(src, strict) | 
					
						
							| 
									
										
										
										
											2022-06-15 16:10:16 +03:00
										 |  |  | 					pages = await pages.isArray ? | 
					
						
							| 
									
										
										
										
											2022-05-24 10:45:53 +03:00
										 |  |  | 						// XXX should we wrap this in pages...
 | 
					
						
							| 
									
										
										
										
											2022-06-15 16:10:16 +03:00
										 |  |  | 						(await pages.raw) | 
					
						
							| 
									
										
										
										
											2022-05-24 10:45:53 +03:00
										 |  |  | 							.map(function(data){ | 
					
						
							| 
									
										
										
										
											2022-05-25 01:59:32 +03:00
										 |  |  | 								return that.virtual({text: data}) }) | 
					
						
							| 
									
										
										
										
											2022-06-15 16:10:16 +03:00
										 |  |  | 						: await pages.each() | 
					
						
							| 
									
										
										
										
											2022-05-24 10:45:53 +03:00
										 |  |  | 					//*/
 | 
					
						
							| 
									
										
										
										
											2022-05-16 11:54:21 +03:00
										 |  |  | 					// no matching pages -> get the else block...
 | 
					
						
							| 
									
										
										
										
											2022-05-18 02:37:55 +03:00
										 |  |  | 					if(pages.length == 0  | 
					
						
							|  |  |  | 							&& (text || args['else'])){ | 
					
						
							| 
									
										
										
										
											2022-05-25 01:01:22 +03:00
										 |  |  | 						var else_block = _getBlock('else') | 
					
						
							| 
									
										
										
										
											2022-05-16 11:54:21 +03:00
										 |  |  | 						return else_block ? | 
					
						
							| 
									
										
										
										
											2022-06-15 16:10:16 +03:00
										 |  |  | 							[...await this.__parser__.expand(this, else_block, state)] | 
					
						
							| 
									
										
										
										
											2022-05-16 11:54:21 +03:00
										 |  |  | 							: undefined } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 					// sort pages...
 | 
					
						
							| 
									
										
										
										
											2022-05-20 15:22:39 +03:00
										 |  |  | 					// XXX SORT
 | 
					
						
							| 
									
										
										
										
											2022-05-16 11:54:21 +03:00
										 |  |  | 					if(sort.length > 0){ | 
					
						
							| 
									
										
										
										
											2022-05-20 15:03:24 +03:00
										 |  |  | 						console.log('XXX: macro sort: not implemented') | 
					
						
							| 
									
										
										
										
											2022-05-16 11:54:21 +03:00
										 |  |  | 					} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-05-25 01:01:22 +03:00
										 |  |  | 					var join_block = _getBlock('join')  | 
					
						
							| 
									
										
										
										
											2022-05-16 11:54:21 +03:00
										 |  |  | 					// apply macro text...
 | 
					
						
							| 
									
										
										
										
											2022-06-15 16:10:16 +03:00
										 |  |  | 					return Promise.iter(pages) | 
					
						
							|  |  |  | 						.map(async function(page, i){ | 
					
						
							|  |  |  | 							//* XXX really ugly...
 | 
					
						
							|  |  |  | 							var res = [] | 
					
						
							|  |  |  | 							for await (var c of that.__parser__.expand(page, text, state)){ | 
					
						
							|  |  |  | 								res.push(c) } | 
					
						
							|  |  |  | 							if(join_block && i < pages.length-1){ | 
					
						
							|  |  |  | 								for await (var c of that.__parser__.expand(page, join_block, state)){ | 
					
						
							|  |  |  | 									res.push(c) } } | 
					
						
							|  |  |  | 							return res | 
					
						
							|  |  |  | 							/*/ | 
					
						
							| 
									
										
										
										
											2022-05-25 01:01:22 +03:00
										 |  |  | 							return [ | 
					
						
							| 
									
										
										
										
											2022-06-15 16:10:16 +03:00
										 |  |  | 								...await that.__parser__.expand(page, text, state), | 
					
						
							| 
									
										
										
										
											2022-05-25 01:01:22 +03:00
										 |  |  | 								...((join_block && i < pages.length-1) ? | 
					
						
							| 
									
										
										
										
											2022-06-15 16:10:16 +03:00
										 |  |  | 									await that.__parser__.expand(page, join_block, state) | 
					
						
							| 
									
										
										
										
											2022-05-25 01:01:22 +03:00
										 |  |  | 									: []), | 
					
						
							| 
									
										
										
										
											2022-06-15 16:10:16 +03:00
										 |  |  | 							]  | 
					
						
							|  |  |  | 							//*/
 | 
					
						
							|  |  |  | 						}) | 
					
						
							| 
									
										
										
										
											2022-05-16 11:54:21 +03:00
										 |  |  | 						.flat() } }), | 
					
						
							| 
									
										
										
										
											2022-04-27 13:06:40 +03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-04-22 00:14:41 +03:00
										 |  |  | 		// nesting rules...
 | 
					
						
							|  |  |  | 		'else': ['macro'], | 
					
						
							| 
									
										
										
										
											2022-05-25 01:01:22 +03:00
										 |  |  | 		'join': ['macro'], | 
					
						
							| 
									
										
										
										
											2022-04-22 00:14:41 +03:00
										 |  |  | 	}, | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-05-26 00:26:28 +03:00
										 |  |  | 	// events...
 | 
					
						
							|  |  |  | 	//
 | 
					
						
							|  |  |  | 	// NOTE: textUpdate event will not get triggered if text is updated 
 | 
					
						
							|  |  |  | 	// 		directly via .data or .__update__(..)
 | 
					
						
							|  |  |  | 	/*/ XXX EVENTS do we need this??? | 
					
						
							|  |  |  | 	onTextUpdate: types.event.Event('textUpdate', | 
					
						
							|  |  |  | 		function(handle, text){ | 
					
						
							|  |  |  | 			this.__update__({text}) }), | 
					
						
							|  |  |  | 	// XXX EVENTS not sure where to trigger this???
 | 
					
						
							|  |  |  | 	// 		...on .parse(..) is a bit too granular, something like .text??
 | 
					
						
							|  |  |  | 	// XXX not triggered yet...
 | 
					
						
							|  |  |  | 	//onParse: types.event.Event('parse'),
 | 
					
						
							|  |  |  | 	//*/
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-04-26 20:58:09 +03:00
										 |  |  | 	// page parser...
 | 
					
						
							|  |  |  | 	//
 | 
					
						
							|  |  |  | 	__parser__: module.parser, | 
					
						
							| 
									
										
										
										
											2022-06-15 16:10:16 +03:00
										 |  |  | 	parse: async function(text, state){ | 
					
						
							|  |  |  | 		var that = this | 
					
						
							| 
									
										
										
										
											2022-05-18 18:13:19 +03:00
										 |  |  | 		// .parser(<state>)
 | 
					
						
							|  |  |  | 		if(arguments.length == 1  | 
					
						
							|  |  |  | 				&& text instanceof Object | 
					
						
							|  |  |  | 				&& !(text instanceof Array)){ | 
					
						
							|  |  |  | 			state = text | 
					
						
							|  |  |  | 			text = null } | 
					
						
							| 
									
										
										
										
											2022-05-20 15:03:24 +03:00
										 |  |  | 		state = state ?? {} | 
					
						
							| 
									
										
										
										
											2022-06-15 16:10:16 +03:00
										 |  |  | 		text = text  | 
					
						
							|  |  |  | 			?? await this.raw | 
					
						
							| 
									
										
										
										
											2022-05-26 00:26:28 +03:00
										 |  |  | 		return text instanceof Array ? | 
					
						
							| 
									
										
										
										
											2022-06-15 16:10:16 +03:00
										 |  |  | 			Promise.iter(text) | 
					
						
							|  |  |  | 				.map(function(text){ | 
					
						
							|  |  |  | 					return that.__parser__.parse(that, text, state) }) | 
					
						
							| 
									
										
										
										
											2022-05-26 00:26:28 +03:00
										 |  |  | 			: this.__parser__.parse(this, text, state) }, | 
					
						
							| 
									
										
										
										
											2022-04-26 03:52:28 +03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-05-25 09:37:38 +03:00
										 |  |  | 	// true if page has an array value but is not a pattern page...
 | 
					
						
							|  |  |  | 	//
 | 
					
						
							|  |  |  | 	// XXX the split into pattern and array pages feels a bit overcomplicated...
 | 
					
						
							|  |  |  | 	// 		...can we merge the two and simplify things???
 | 
					
						
							| 
									
										
										
										
											2022-05-24 10:45:53 +03:00
										 |  |  | 	// XXX EXPERIMENTAL
 | 
					
						
							| 
									
										
										
										
											2022-06-15 16:10:16 +03:00
										 |  |  | 	get isArray(){ return (async function(){ | 
					
						
							| 
									
										
										
										
											2022-05-24 10:45:53 +03:00
										 |  |  | 		return !this.isPattern  | 
					
						
							| 
									
										
										
										
											2022-05-25 09:37:38 +03:00
										 |  |  | 			// NOTE: we can't only use .data here as it can be a function 
 | 
					
						
							|  |  |  | 			// 		that will return an array...
 | 
					
						
							| 
									
										
										
										
											2022-06-15 16:10:16 +03:00
										 |  |  | 			&& await this.raw instanceof Array }).call(this) }, | 
					
						
							| 
									
										
										
										
											2022-04-25 16:00:12 +03:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	// raw page text...
 | 
					
						
							|  |  |  | 	//
 | 
					
						
							|  |  |  | 	// NOTE: writing to .raw is the same as writing to .text...
 | 
					
						
							| 
									
										
										
										
											2022-05-20 15:03:24 +03:00
										 |  |  | 	// NOTE: when matching multiple pages this will return a list...
 | 
					
						
							| 
									
										
										
										
											2022-06-15 16:10:16 +03:00
										 |  |  | 	get raw(){ return (async function(){ | 
					
						
							| 
									
										
										
										
											2022-05-17 01:26:39 +03:00
										 |  |  | 		var that = this | 
					
						
							| 
									
										
										
										
											2022-06-15 16:10:16 +03:00
										 |  |  | 		var data = await this.data | 
					
						
							| 
									
										
										
										
											2022-05-18 02:55:46 +03:00
										 |  |  | 		// no data...
 | 
					
						
							|  |  |  | 		// NOTE: if we hit this it means that nothing was resolved, 
 | 
					
						
							|  |  |  | 		// 		not even the System/NotFound page, i.e. something 
 | 
					
						
							|  |  |  | 		// 		went really wrong...
 | 
					
						
							|  |  |  | 		if(data == null){ | 
					
						
							|  |  |  | 			var msg = (this.PAGE_NOT_FOUND  | 
					
						
							|  |  |  | 					|| module.PAGE_NOT_FOUND) | 
					
						
							|  |  |  | 				.replace(/\$PATH/, this.path) | 
					
						
							|  |  |  | 			if(this.PAGE_NOT_FOUND){ | 
					
						
							|  |  |  | 				return msg } | 
					
						
							|  |  |  | 			throw new Error(msg) } | 
					
						
							|  |  |  | 		// get the data...
 | 
					
						
							| 
									
										
										
										
											2022-06-17 11:31:45 +03:00
										 |  |  | 		return ( | 
					
						
							|  |  |  | 			// action...
 | 
					
						
							|  |  |  | 			typeof(data) == 'function' ? | 
					
						
							| 
									
										
										
										
											2022-05-17 01:26:39 +03:00
										 |  |  | 				data.call(this) | 
					
						
							|  |  |  | 			// multiple matches...
 | 
					
						
							|  |  |  | 			: data instanceof Array ? | 
					
						
							|  |  |  | 				data | 
					
						
							|  |  |  | 					.map(function(d){ | 
					
						
							| 
									
										
										
										
											2022-05-22 15:06:30 +03:00
										 |  |  | 						return typeof(d) == 'function'? | 
					
						
							| 
									
										
										
										
											2022-05-17 01:26:39 +03:00
										 |  |  | 							d.call(that) | 
					
						
							|  |  |  | 							: d.text }) | 
					
						
							| 
									
										
										
										
											2022-05-20 15:03:24 +03:00
										 |  |  | 					.flat() | 
					
						
							| 
									
										
										
										
											2022-06-17 11:31:45 +03:00
										 |  |  |    			: data.text )}).call(this) }, | 
					
						
							| 
									
										
										
										
											2022-04-25 16:00:12 +03:00
										 |  |  | 	set raw(value){ | 
					
						
							| 
									
										
										
										
											2022-05-24 10:45:53 +03:00
										 |  |  | 		this.__update__({text: value}) }, | 
					
						
							| 
									
										
										
										
											2022-05-26 00:26:28 +03:00
										 |  |  | 		//this.onTextUpdate(value) },
 | 
					
						
							| 
									
										
										
										
											2022-04-25 16:00:12 +03:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	// expanded page text...
 | 
					
						
							|  |  |  | 	//
 | 
					
						
							| 
									
										
										
										
											2022-05-25 01:01:22 +03:00
										 |  |  | 	// NOTE: this uses .PAGE_TPL to render the page.
 | 
					
						
							| 
									
										
										
										
											2022-04-25 16:00:12 +03:00
										 |  |  | 	// NOTE: writing to .raw is the same as writing to .text...
 | 
					
						
							| 
									
										
										
										
											2022-06-15 16:10:16 +03:00
										 |  |  | 	get text(){ return (async function(){ | 
					
						
							|  |  |  | 		var tpl = '/'+ await this.find('./'+ this.PAGE_TPL) | 
					
						
							|  |  |  | 		return [await this.parse( | 
					
						
							| 
									
										
										
										
											2022-05-25 09:37:38 +03:00
										 |  |  | 				tpl.endsWith(this.PAGE_TPL.split(/[\\\/]/).pop()) ? | 
					
						
							| 
									
										
										
										
											2022-06-15 16:10:16 +03:00
										 |  |  | 					[await this.get(tpl).raw] | 
					
						
							| 
									
										
										
										
											2022-05-25 01:01:22 +03:00
										 |  |  | 					: [] )] | 
					
						
							| 
									
										
										
										
											2022-05-20 15:03:24 +03:00
										 |  |  | 			.flat() | 
					
						
							| 
									
										
										
										
											2022-06-15 16:10:16 +03:00
										 |  |  | 			.join('\n') }).call(this) },  | 
					
						
							| 
									
										
										
										
											2022-04-25 16:00:12 +03:00
										 |  |  | 	set text(value){ | 
					
						
							| 
									
										
										
										
											2022-05-24 10:45:53 +03:00
										 |  |  | 		this.__update__({text: value}) }, | 
					
						
							| 
									
										
										
										
											2022-05-26 00:26:28 +03:00
										 |  |  | 		//this.onTextUpdate(value) },
 | 
					
						
							| 
									
										
										
										
											2022-04-21 18:33:08 +03:00
										 |  |  | }) | 
					
						
							| 
									
										
										
										
											2022-04-12 01:31:28 +03:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | //---------------------------------------------------------------------
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-04-13 03:14:21 +03:00
										 |  |  | var WIKIWORD_PATTERN = | 
					
						
							|  |  |  | 	RegExp('('+[ | 
					
						
							|  |  |  | 		//'\\\\?(\\/|\\./|\\.\\./|>>|[A-Z][_a-z0-9]+[A-Z/])[_a-zA-Z0-9/]*',
 | 
					
						
							|  |  |  | 		'\\\\?\\/?(\\./|\\.\\./|>>|[A-Z][_a-z0-9]+[A-Z/])[_a-zA-Z0-9/]*', | 
					
						
							|  |  |  | 		'\\\\?\\[[^\\]]+\\]', | 
					
						
							|  |  |  | 	].join('|') +')', 'g') | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-04-13 12:18:13 +03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-04-13 03:14:21 +03:00
										 |  |  | //---------------------------------------------------------------------
 | 
					
						
							| 
									
										
										
										
											2022-05-11 15:36:35 +03:00
										 |  |  | // Basic setup...
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | //
 | 
					
						
							| 
									
										
										
										
											2022-05-11 16:46:27 +03:00
										 |  |  | // Store topology:
 | 
					
						
							| 
									
										
										
										
											2022-05-11 15:36:35 +03:00
										 |  |  | // 		
 | 
					
						
							|  |  |  | // 		root (BaseStore) ---next--- main (MetaStore)
 | 
					
						
							|  |  |  | // 										|
 | 
					
						
							|  |  |  | // 										+-- System/... (BaseStore)
 | 
					
						
							|  |  |  | //
 | 
					
						
							| 
									
										
										
										
											2022-05-11 16:46:27 +03:00
										 |  |  | // Alternative store topology:
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | // 		root (BaseStore) ---next--- main (MetaStore)
 | 
					
						
							|  |  |  | // 			System/... (static)
 | 
					
						
							|  |  |  | //
 | 
					
						
							| 
									
										
										
										
											2022-05-11 15:36:35 +03:00
										 |  |  | //
 | 
					
						
							| 
									
										
										
										
											2022-04-22 12:00:48 +03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-05-10 23:58:13 +03:00
										 |  |  | var store =  | 
					
						
							|  |  |  | module.store =  | 
					
						
							| 
									
										
										
										
											2022-05-11 15:27:15 +03:00
										 |  |  | 	//BaseStore.nest()
 | 
					
						
							| 
									
										
										
										
											2022-05-10 23:58:13 +03:00
										 |  |  | 	BaseStore.nest(MetaStore) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-05-11 15:27:15 +03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-06-12 12:04:44 +03:00
										 |  |  | var System =  | 
					
						
							|  |  |  | module.System = { | 
					
						
							| 
									
										
										
										
											2022-05-25 09:37:38 +03:00
										 |  |  | 	// base templates...
 | 
					
						
							|  |  |  | 	//
 | 
					
						
							|  |  |  | 	_text: {  | 
					
						
							|  |  |  | 		text: '<macro src="." join="\n">@source(.)</macro>' }, | 
					
						
							|  |  |  | 	NotFound: {  | 
					
						
							|  |  |  | 		text: module.PAGE_NOT_FOUND | 
					
						
							|  |  |  | 			.replace('$PATH', '@source(./path)') }, | 
					
						
							| 
									
										
										
										
											2022-05-25 01:01:22 +03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-05-24 10:45:53 +03:00
										 |  |  | 	// XXX tests...
 | 
					
						
							|  |  |  | 	test_list: function(){ | 
					
						
							|  |  |  | 		return 'abcdef'.split('') }, | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-05-10 23:58:13 +03:00
										 |  |  | 	// metadata...
 | 
					
						
							|  |  |  | 	//
 | 
					
						
							|  |  |  | 	path: function(){ | 
					
						
							|  |  |  | 		return this.get('..').path }, | 
					
						
							|  |  |  | 	location: function(){ | 
					
						
							|  |  |  | 		return this.get('..').path }, | 
					
						
							|  |  |  | 	dir: function(){ | 
					
						
							|  |  |  | 		return this.get('..').dir }, | 
					
						
							|  |  |  | 	name: function(){ | 
					
						
							|  |  |  | 		return this.get('..').name }, | 
					
						
							|  |  |  | 	ctime: function(){ | 
					
						
							|  |  |  | 		return this.get('..').data.ctime }, | 
					
						
							|  |  |  | 	mtime: function(){ | 
					
						
							|  |  |  | 		return this.get('..').data.mtime }, | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-05-20 15:03:24 +03:00
										 |  |  | 	// XXX this can be a list for pattern paths...
 | 
					
						
							|  |  |  | 	resolved: function(){ | 
					
						
							| 
									
										
										
										
											2022-05-23 00:00:40 +03:00
										 |  |  | 		return this.get('..').resolve() }, | 
					
						
							| 
									
										
										
										
											2022-05-20 15:03:24 +03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-05-10 23:58:13 +03:00
										 |  |  | 	title: function(){ | 
					
						
							|  |  |  | 		var p = this.get('..') | 
					
						
							|  |  |  | 		return p.title  | 
					
						
							|  |  |  | 			?? p.name }, | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// utils...
 | 
					
						
							|  |  |  | 	//
 | 
					
						
							|  |  |  | 	// XXX System/subpaths
 | 
					
						
							| 
									
										
										
										
											2022-05-24 10:45:53 +03:00
										 |  |  | 	// XXX
 | 
					
						
							|  |  |  | 	links: function(){ | 
					
						
							|  |  |  | 		// XXX
 | 
					
						
							|  |  |  | 		return '' }, | 
					
						
							| 
									
										
										
										
											2022-05-24 22:52:30 +03:00
										 |  |  | 	// XXX links to pages...
 | 
					
						
							| 
									
										
										
										
											2022-05-24 10:45:53 +03:00
										 |  |  | 	to: function(){ | 
					
						
							| 
									
										
										
										
											2022-05-24 22:52:30 +03:00
										 |  |  | 		return (this.get('..').data || {}).to ?? [] }, | 
					
						
							|  |  |  | 	// XXX pages linking to us...
 | 
					
						
							| 
									
										
										
										
											2022-05-24 10:45:53 +03:00
										 |  |  | 	'from': function(){ | 
					
						
							| 
									
										
										
										
											2022-05-24 22:52:30 +03:00
										 |  |  | 		return (this.get('..').data || {})['from'] ?? [] }, | 
					
						
							| 
									
										
										
										
											2022-05-10 23:58:13 +03:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// actions...
 | 
					
						
							|  |  |  | 	//
 | 
					
						
							|  |  |  | 	delete: function(){ | 
					
						
							|  |  |  | 		this.location = '..' | 
					
						
							|  |  |  | 		this.delete() | 
					
						
							|  |  |  | 		return this.text }, | 
					
						
							|  |  |  | 	// XXX System/back
 | 
					
						
							|  |  |  | 	// XXX System/forward
 | 
					
						
							|  |  |  | 	// XXX System/sort
 | 
					
						
							|  |  |  | 	// XXX System/reverse
 | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2022-05-11 15:27:15 +03:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-06-12 12:04:44 +03:00
										 |  |  | // XXX note sure how to organize the system actions -- there can be two 
 | 
					
						
							| 
									
										
										
										
											2022-05-11 15:27:15 +03:00
										 |  |  | // 		options:
 | 
					
						
							|  |  |  | // 			- a root ram store with all the static stuff and nest the rest
 | 
					
						
							|  |  |  | // 			- a nested store (as is the case here)
 | 
					
						
							|  |  |  | // XXX nested system store...
 | 
					
						
							| 
									
										
										
										
											2022-05-12 09:06:38 +03:00
										 |  |  | store.update('System',  | 
					
						
							|  |  |  | 	Object.create(BaseStore).load(System)) | 
					
						
							| 
									
										
										
										
											2022-05-11 15:27:15 +03:00
										 |  |  | /*/ // XXX chained system store...
 | 
					
						
							| 
									
										
										
										
											2022-05-11 15:36:35 +03:00
										 |  |  | store.next.load( | 
					
						
							|  |  |  | 	// Create a new system action-set with paths starting with 'System/'
 | 
					
						
							| 
									
										
										
										
											2022-05-11 15:27:15 +03:00
										 |  |  | 	Object.entries(System) | 
					
						
							|  |  |  | 		.reduce(function(res, [key, func]){ | 
					
						
							|  |  |  | 			res[module.path.join('System', key)] = func | 
					
						
							| 
									
										
										
										
											2022-05-11 15:36:35 +03:00
										 |  |  | 			return res }, {})) | 
					
						
							| 
									
										
										
										
											2022-05-10 23:58:13 +03:00
										 |  |  | //*/
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-04-23 10:56:24 +03:00
										 |  |  | // NOTE: in general the root wiki api is simply a page instance.
 | 
					
						
							| 
									
										
										
										
											2022-05-03 16:47:43 +03:00
										 |  |  | // XXX not yet sure how to organize the actual client -- UI, hooks, .. etc
 | 
					
						
							| 
									
										
										
										
											2022-04-25 20:53:20 +03:00
										 |  |  | var pwiki = | 
					
						
							| 
									
										
										
										
											2022-04-22 12:00:48 +03:00
										 |  |  | module.pwiki =  | 
					
						
							| 
									
										
										
										
											2022-05-03 16:15:16 +03:00
										 |  |  | Page('/', '/', store) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-04-13 12:18:13 +03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-04-13 03:14:21 +03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-05-11 15:36:35 +03:00
										 |  |  | //---------------------------------------------------------------------
 | 
					
						
							|  |  |  | // XXX experiments and testing...
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-07-16 11:18:55 +03:00
										 |  |  | // XXX for persistent stores test if the data is already loaded here...
 | 
					
						
							| 
									
										
										
										
											2022-05-11 15:36:35 +03:00
										 |  |  | store.load(require('./bootstrap')) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-07-15 15:30:52 +03:00
										 |  |  | // XXX TEST...
 | 
					
						
							| 
									
										
										
										
											2022-04-27 13:06:40 +03:00
										 |  |  | // XXX add filter tests...
 | 
					
						
							| 
									
										
										
										
											2022-04-25 20:53:20 +03:00
										 |  |  | console.log('loading test page...') | 
					
						
							| 
									
										
										
										
											2022-04-26 20:58:09 +03:00
										 |  |  | pwiki | 
					
						
							| 
									
										
										
										
											2022-05-20 15:03:24 +03:00
										 |  |  | 	.update({ | 
					
						
							|  |  |  | 		location: '/test/a', | 
					
						
							|  |  |  | 		text: 'a', | 
					
						
							|  |  |  | 	}) | 
					
						
							|  |  |  | 	.update({ | 
					
						
							|  |  |  | 		location: '/test/b', | 
					
						
							|  |  |  | 		text: 'b', | 
					
						
							|  |  |  | 	}) | 
					
						
							|  |  |  | 	.update({ | 
					
						
							|  |  |  | 		location: '/test/c', | 
					
						
							|  |  |  | 		text: 'c', | 
					
						
							|  |  |  | 	}) | 
					
						
							|  |  |  | 	.update({ | 
					
						
							|  |  |  | 		location: '/test/c/x', | 
					
						
							|  |  |  | 		text: 'x', | 
					
						
							|  |  |  | 	}) | 
					
						
							|  |  |  | 	.update({ | 
					
						
							|  |  |  | 		location: '/test/c/y', | 
					
						
							|  |  |  | 		text: 'y', | 
					
						
							|  |  |  | 	}) | 
					
						
							|  |  |  | 	.update({ | 
					
						
							|  |  |  | 		location: '/test/d/z', | 
					
						
							|  |  |  | 		text: 'z', | 
					
						
							|  |  |  | 	}) | 
					
						
							| 
									
										
										
										
											2022-04-26 20:58:09 +03:00
										 |  |  | 	.update({ | 
					
						
							|  |  |  | 		location: '/page', | 
					
						
							|  |  |  | 		text: 'PAGE\n' | 
					
						
							|  |  |  | 			+'\n' | 
					
						
							| 
									
										
										
										
											2022-05-20 15:03:24 +03:00
										 |  |  | 			+'@include(/test recursive="Recursion type 2 (<now/>)")\n' | 
					
						
							| 
									
										
										
										
											2022-04-27 13:06:40 +03:00
										 |  |  | 			+'\n' | 
					
						
							|  |  |  | 			+'@slot(name=b text="filled slot")\n', | 
					
						
							| 
									
										
										
										
											2022-04-26 20:58:09 +03:00
										 |  |  | 	}) | 
					
						
							|  |  |  | 	.update({ | 
					
						
							|  |  |  | 		location: '/other', | 
					
						
							|  |  |  | 		text: 'OTHER', | 
					
						
							|  |  |  | 	}) | 
					
						
							|  |  |  | 	.update({ | 
					
						
							|  |  |  | 		location: '/test', | 
					
						
							|  |  |  | 		text: 'TEST\n' | 
					
						
							| 
									
										
										
										
											2022-04-27 13:06:40 +03:00
										 |  |  | 			+'\n' | 
					
						
							|  |  |  | 			+'globally filtered test text...\n' | 
					
						
							|  |  |  | 			+'\n' | 
					
						
							|  |  |  | 			+'<filter -test>...unfiltered test text</filter>\n' | 
					
						
							|  |  |  | 			+'\n' | 
					
						
							| 
									
										
										
										
											2022-04-27 14:28:19 +03:00
										 |  |  | 			//+'<filter test>locally filtered test text</filter>\n'
 | 
					
						
							|  |  |  | 			+'\n' | 
					
						
							| 
									
										
										
										
											2022-04-27 13:06:40 +03:00
										 |  |  | 			+'@slot(name=a text="non-filled slot")\n' | 
					
						
							|  |  |  | 			+'\n' | 
					
						
							|  |  |  | 			+'@slot(name=b text="non-filled slot")\n' | 
					
						
							|  |  |  | 			+'\n' | 
					
						
							| 
									
										
										
										
											2022-04-26 20:58:09 +03:00
										 |  |  | 			+'Including /other #1: @include(/other)\n' | 
					
						
							|  |  |  | 			+'Including /other #2: @include(/other)\n' | 
					
						
							|  |  |  | 			+'\n' | 
					
						
							| 
									
										
										
										
											2022-05-20 15:03:24 +03:00
										 |  |  | 			+'Including /test: @include(/test recursive="Recursion type 1 (<now/>)")\n' | 
					
						
							| 
									
										
										
										
											2022-04-26 20:58:09 +03:00
										 |  |  | 			+'\n' | 
					
						
							| 
									
										
										
										
											2022-05-20 15:03:24 +03:00
										 |  |  | 			+'Including /page: @include(/page recursive="...")\n' | 
					
						
							| 
									
										
										
										
											2022-04-26 20:58:09 +03:00
										 |  |  | 			+'\n' | 
					
						
							|  |  |  | 			+'Including /: \\@include(/)\n' | 
					
						
							|  |  |  | 			+'\n' | 
					
						
							|  |  |  | 			+'@filter(test)', | 
					
						
							|  |  |  | 	}) | 
					
						
							| 
									
										
										
										
											2022-05-03 16:15:16 +03:00
										 |  |  | //*/
 | 
					
						
							| 
									
										
										
										
											2022-04-25 20:53:20 +03:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-04-12 01:31:28 +03:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /********************************************************************** | 
					
						
							| 
									
										
										
										
											2022-04-28 14:12:02 +03:00
										 |  |  | * vim:set ts=4 sw=4 nowrap :                        */ return module }) |