| 
									
										
										
										
											2024-10-27 11:01:12 +03:00
										 |  |  | /********************************************************************** | 
					
						
							|  |  |  | *  | 
					
						
							|  |  |  | * | 
					
						
							|  |  |  | * | 
					
						
							|  |  |  | **********************************************************************/ | 
					
						
							|  |  |  | ((typeof define)[0]=='u'?function(f){module.exports=f(require)}:define) | 
					
						
							|  |  |  | (function(require){ var module={} // make module AMD/node compatible...
 | 
					
						
							|  |  |  | /*********************************************************************/ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | var util = require('lib/util') | 
					
						
							|  |  |  | var object = require('lib/object') | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | var actions = require('lib/actions') | 
					
						
							|  |  |  | var features = require('lib/features') | 
					
						
							|  |  |  | var toggler = require('lib/toggler') | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | var core = require('features/core') | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /*********************************************************************/ | 
					
						
							|  |  |  | // XXX experimental...
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // XXX need the other .location stuff to be visible/accessible...
 | 
					
						
							|  |  |  | // 		...now this only shows path...
 | 
					
						
							|  |  |  | var Location =  | 
					
						
							|  |  |  | //module.Location =
 | 
					
						
							|  |  |  | object.Constructor('Location', { | 
					
						
							|  |  |  | 	get path(){ | 
					
						
							|  |  |  | 		return this.__actions.__location.path }, | 
					
						
							|  |  |  | 	set path(value){ | 
					
						
							|  |  |  | 		this.__actions.location = value }, | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	__init__: function(actions){ | 
					
						
							|  |  |  | 		this.__actions = actions }, | 
					
						
							|  |  |  | }) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /*********************************************************************/ | 
					
						
							|  |  |  | // XXX add url scheme support...
 | 
					
						
							|  |  |  | // 		<method>://<path>#<current>?<other>
 | 
					
						
							|  |  |  | // XXX add .hash support for in-location .current setting when no index
 | 
					
						
							|  |  |  | // 		available... 
 | 
					
						
							|  |  |  | // XXX this should provide mechaincs to define location handlers, i.e.
 | 
					
						
							|  |  |  | // 		a set for loader/saver per location type (.method)
 | 
					
						
							|  |  |  | // XXX revise the wording...
 | 
					
						
							|  |  |  | // 		.path or .url
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | var LocationActions = actions.Actions({ | 
					
						
							|  |  |  | 	config: { | 
					
						
							|  |  |  | 		'default-load-method': null, | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		'location-stored-attrs': [ | 
					
						
							|  |  |  | 			'sync', | 
					
						
							|  |  |  | 		], | 
					
						
							|  |  |  | 	}, | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Format:
 | 
					
						
							|  |  |  | 	// 	{
 | 
					
						
							|  |  |  | 	// 		current: <current-gid>,
 | 
					
						
							|  |  |  | 	// 		load: <load-method>,
 | 
					
						
							|  |  |  | 	// 		sync: <sync-method>,
 | 
					
						
							|  |  |  | 	// 		path: <base-path>,
 | 
					
						
							|  |  |  | 	// 		...
 | 
					
						
							|  |  |  | 	// 	}
 | 
					
						
							|  |  |  | 	//
 | 
					
						
							|  |  |  | 	// NOTE: these will remove the trailing '/' (or '\') from .path 
 | 
					
						
							|  |  |  | 	// 		unless the path is root (i.e. "/")...
 | 
					
						
							|  |  |  | 	// 		...this is mainly to facilitate better browse support, i.e.
 | 
					
						
							|  |  |  | 	// 		to open the dir (open parent + select current) and not 
 | 
					
						
							|  |  |  | 	// 		within the dir
 | 
					
						
							|  |  |  | 	__location: null, | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	get location(){ | 
					
						
							|  |  |  | 		this.__location = this.__location || {} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		var b = this.__location.path | 
					
						
							|  |  |  | 		if(b && b != '/' && b != '\\'){ | 
					
						
							|  |  |  | 			b = util.normalizePath(b) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		if(b){ | 
					
						
							|  |  |  | 			this.__location.path = b | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		this.__location.current = this.current | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		return this.__location | 
					
						
							|  |  |  | 		//return Location(this) 
 | 
					
						
							|  |  |  | 	}, | 
					
						
							|  |  |  | 	// NOTE: this is a shorthand for .loadLocation(..)
 | 
					
						
							|  |  |  | 	// NOTE: the method is needed to enable us to get the action return
 | 
					
						
							|  |  |  | 	// 		value...
 | 
					
						
							|  |  |  | 	set location(value){ | 
					
						
							|  |  |  | 		this.loadLocation(value) }, | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Load location...
 | 
					
						
							|  |  |  | 	//
 | 
					
						
							|  |  |  | 	// 	Reload current location...
 | 
					
						
							|  |  |  | 	// 	.loadLocation()
 | 
					
						
							|  |  |  | 	// 		-> result
 | 
					
						
							|  |  |  | 	//
 | 
					
						
							|  |  |  | 	//	Load new path using current location method and data...
 | 
					
						
							|  |  |  | 	//	.loadLocation(path)
 | 
					
						
							|  |  |  | 	// 		-> result
 | 
					
						
							|  |  |  | 	//
 | 
					
						
							|  |  |  | 	//	Load new location...
 | 
					
						
							|  |  |  | 	//	.loadLocation(location)
 | 
					
						
							|  |  |  | 	// 		-> result
 | 
					
						
							|  |  |  | 	// 		NOTE: this is almost the same as .location = location but
 | 
					
						
							|  |  |  | 	// 			here we can access the call return value.
 | 
					
						
							|  |  |  | 	//
 | 
					
						
							|  |  |  | 	// NOTE: .location will be set by the .load handler...
 | 
					
						
							|  |  |  | 	//
 | 
					
						
							|  |  |  | 	// XXX not sure about where to set the .__location -- see inside...
 | 
					
						
							|  |  |  | 	loadLocation: ['- File/Load location', | 
					
						
							|  |  |  | 		function(location){ | 
					
						
							|  |  |  | 			location = location || this.location | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			// got a path -> load using current location data...
 | 
					
						
							|  |  |  | 			if(typeof(location) == typeof('str')){ | 
					
						
							|  |  |  | 				location = { | 
					
						
							|  |  |  | 					path: path, | 
					
						
							|  |  |  | 					load: (this.__location && this.__location.load)  | 
					
						
							|  |  |  | 						|| this.config['default-load-method'], | 
					
						
							|  |  |  | 					current: this.current, | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			// got an object...
 | 
					
						
							|  |  |  | 			} else { | 
					
						
							|  |  |  | 				// clone the location...
 | 
					
						
							|  |  |  | 				location = JSON.parse(JSON.stringify(location)) | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			var load = location.load  | 
					
						
							|  |  |  | 				|| this.location.load  | 
					
						
							|  |  |  | 				|| this.config['default-load-method'] | 
					
						
							|  |  |  | 			var cur = location.current | 
					
						
							|  |  |  | 			var path = location.path | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			// normalize path if it's not root...
 | 
					
						
							|  |  |  | 			if(path != '/' && path != '\\'){ | 
					
						
							|  |  |  | 				path = location.path = util.normalizePath(path) | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			// XXX ???
 | 
					
						
							|  |  |  | 			//this.__location = location 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			// NOTE: the method should set the proper location if it uses .clear()...
 | 
					
						
							|  |  |  | 			var res = load && this[load](path) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			// load current...
 | 
					
						
							|  |  |  | 			if(cur){ | 
					
						
							|  |  |  | 				if(res && res.then != null){ | 
					
						
							|  |  |  | 					var that = this | 
					
						
							|  |  |  | 					res.then(function(){ | 
					
						
							|  |  |  | 						that.current = cur | 
					
						
							|  |  |  | 					}) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 				} else { | 
					
						
							|  |  |  | 					this.current = cur | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			return res | 
					
						
							|  |  |  | 		}], | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	clearLoaction: ['File/Clear location', | 
					
						
							|  |  |  | 		function(){ delete this.__location }], | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// XXX
 | 
					
						
							|  |  |  | 	// XXX should these have the same effect as .dispatch('location:*:load', location)???
 | 
					
						
							|  |  |  | 	// 		...another way to put it is should we call this from dispatch?
 | 
					
						
							|  |  |  | 	// 			...feels like yes -- this will turn into another Action-like
 | 
					
						
							|  |  |  | 	// 			protocol...
 | 
					
						
							|  |  |  | 	// 				...or we can use this to wrap the actual matching action...
 | 
					
						
							|  |  |  | 	_loadLocation: ['- File/Save location', | 
					
						
							|  |  |  | 		{protocol: 'location:*:load'}, | 
					
						
							|  |  |  | 		function(location){ | 
					
						
							|  |  |  | 			this.location.load = this.locationMethod(location) | 
					
						
							|  |  |  | 			this.dispatch('location:*:load', location)  | 
					
						
							|  |  |  | 		}], | 
					
						
							|  |  |  | 	_saveLocation: ['- File/Save location', | 
					
						
							|  |  |  | 		{protocol: 'location:*:save'}, | 
					
						
							|  |  |  | 		function(location){ | 
					
						
							|  |  |  | 			this.location.load = this.locationMethod(location) | 
					
						
							|  |  |  | 			this.dispatch('location:*:save', location) }], | 
					
						
							|  |  |  | 	_locationMethod: ['- File/', | 
					
						
							|  |  |  | 		{protocol: 'location:?'}, | 
					
						
							|  |  |  | 		function(location){  | 
					
						
							|  |  |  | 			return (location || this.location).load || null }], | 
					
						
							|  |  |  | 	// 
 | 
					
						
							|  |  |  | 	// format:
 | 
					
						
							|  |  |  | 	// 	{
 | 
					
						
							|  |  |  | 	// 		'protocol:method': 'actionName',
 | 
					
						
							|  |  |  | 	//
 | 
					
						
							|  |  |  | 	// 		'family:protocol:method': 'actionName',
 | 
					
						
							|  |  |  | 	//
 | 
					
						
							|  |  |  | 	// 		'family:*': 'actionName',
 | 
					
						
							|  |  |  | 	// 		...
 | 
					
						
							|  |  |  | 	// 	}
 | 
					
						
							|  |  |  | 	get protocols(){ | 
					
						
							|  |  |  | 		var cache = this.__location_protocol_cache = this.__location_protocol_cache  | 
					
						
							|  |  |  | 			|| this.cacheProtocols() | 
					
						
							|  |  |  | 		return cache }, | 
					
						
							|  |  |  | 	cacheProtocols: ['- File/', | 
					
						
							|  |  |  | 		function(){ | 
					
						
							|  |  |  | 			var that = this | 
					
						
							|  |  |  | 			var res = {} | 
					
						
							|  |  |  | 			this.actions.forEach(function(n){ | 
					
						
							|  |  |  | 				var proto = that.getActionAttr(n, 'protocol') | 
					
						
							|  |  |  | 				if(proto){ | 
					
						
							|  |  |  | 					res[proto] = n } }) | 
					
						
							|  |  |  | 			return res }], | 
					
						
							|  |  |  | 	// XXX how do we call the dispatched actions and all the matching 
 | 
					
						
							|  |  |  | 	// 		pattern actions???
 | 
					
						
							|  |  |  | 	// 			One way to go would be:
 | 
					
						
							|  |  |  | 	// 				.dispatch('location:*:save', ..)
 | 
					
						
							|  |  |  | 	// 					-> 'location:?'					- get the default for '*'
 | 
					
						
							|  |  |  | 	// 					-> 'location:*:save' (pre)
 | 
					
						
							|  |  |  | 	// 					-> 'location:file:save'			- forms the return value
 | 
					
						
							|  |  |  | 	// 					-> 'location:*:save' (post)
 | 
					
						
							|  |  |  | 	//
 | 
					
						
							|  |  |  | 	// 			...this should the same as calling (???):
 | 
					
						
							|  |  |  | 	// 				.loadLocation(..)
 | 
					
						
							|  |  |  | 	// XXX sanity check: how is this different for what Action(..) does???
 | 
					
						
							|  |  |  | 	// 		...the only thing this adds is a way not to call some of the 
 | 
					
						
							|  |  |  | 	// 		overloading actions via a simple pattern matching mechanism...
 | 
					
						
							|  |  |  | 	// 			Example:
 | 
					
						
							|  |  |  | 	// 				.dispatch('location:file:save', ..)
 | 
					
						
							|  |  |  | 	// 					-> calls only the "save" actions that match the 
 | 
					
						
							|  |  |  | 	// 						location:file protocol ignoring all other 
 | 
					
						
							|  |  |  | 	// 						implementations...
 | 
					
						
							|  |  |  | 	// 		...for pure actions this is also possible by manually checking
 | 
					
						
							|  |  |  | 	// 		some condition and doing nothing if not matched...
 | 
					
						
							|  |  |  | 	dispatch: ['- File/', | 
					
						
							|  |  |  | 		core.doc`
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			Execute command in specific protocol... | 
					
						
							|  |  |  | 			.dispatch('protocol:command', ..) | 
					
						
							|  |  |  | 			.dispatch('family:protocol:command', ..) | 
					
						
							|  |  |  | 				-> result | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			XXX defaults... | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			XXX introspection... | 
					
						
							|  |  |  | 		`,
 | 
					
						
							|  |  |  | 		function(spec){ | 
					
						
							|  |  |  | 			var args = [...arguments].slice(1) | 
					
						
							|  |  |  | 			spec = spec instanceof Array ? spec : spec.split(':') | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			var cache = this.protocols | 
					
						
							|  |  |  | 			var protocols = Object.keys(cache) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			// get all matching paths...
 | 
					
						
							|  |  |  | 			var matches = protocols.slice() | 
					
						
							|  |  |  | 				.map(function(p){ return p.split(':') }) | 
					
						
							|  |  |  | 			spec.forEach(function(e, i){ | 
					
						
							|  |  |  | 				matches = matches | 
					
						
							|  |  |  | 					.filter(function(p){ return e == '*' || p[i] == e }) }) | 
					
						
							|  |  |  | 			matches = matches | 
					
						
							|  |  |  | 				// remove matches ending with '*'... (XXX ???)
 | 
					
						
							|  |  |  | 				.filter(function(p){ return p.slice(-1)[0] != '*' }) | 
					
						
							|  |  |  | 				.map(function(p){ return p.join(':') }) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			// fill in the gaps...
 | 
					
						
							|  |  |  | 			var i = spec.indexOf('*') | 
					
						
							|  |  |  | 			while(spec.indexOf('*') >= 0){ | 
					
						
							|  |  |  | 				var handler = cache[spec.slice(0, i).concat('?').join(':')] | 
					
						
							|  |  |  | 				if(handler){ | 
					
						
							|  |  |  | 					spec[i] = this[handler].apply(this, args) | 
					
						
							|  |  |  | 					i = spec.indexOf('*') | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 				// error...
 | 
					
						
							|  |  |  | 				// XXX how do we break out of this???
 | 
					
						
							|  |  |  | 				} else { | 
					
						
							|  |  |  | 					throw ('No default defined for: '+ spec.slice(0, i+1).join(':')) | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			// introspection...
 | 
					
						
							|  |  |  | 			// XXX this supports only one '??'
 | 
					
						
							|  |  |  | 			var i = spec.indexOf('??') | 
					
						
							|  |  |  | 			if(i >= 0){ | 
					
						
							|  |  |  | 				var head = spec.slice(0, i).join(':') | 
					
						
							|  |  |  | 				var tail = spec.slice(i+1).join(':') | 
					
						
							|  |  |  | 				console.log(head, tail) | 
					
						
							|  |  |  | 				return protocols | 
					
						
							|  |  |  | 					.filter(function(p){ | 
					
						
							|  |  |  | 						return p.startsWith(head)  | 
					
						
							|  |  |  | 							&& (tail == ''  | 
					
						
							|  |  |  | 								|| (p.endsWith(tail) | 
					
						
							|  |  |  | 									&& p.length > (head.length + tail.length + 2))) }) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			// call method...
 | 
					
						
							|  |  |  | 			} else { | 
					
						
							|  |  |  | 				var m = spec.join(':') | 
					
						
							|  |  |  | 				console.log('>>>', m) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 				// XXX take all the matches and chain call them...
 | 
					
						
							|  |  |  | 				return this[cache[m]].apply(this, args) | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		}], | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// sync API...
 | 
					
						
							|  |  |  | 	//
 | 
					
						
							|  |  |  | 	get location_sync_methods(){ | 
					
						
							|  |  |  | 		var that = this | 
					
						
							|  |  |  | 		return (this.__location_sync_methods_cache =  | 
					
						
							|  |  |  | 			this.__location_sync_methods_cache | 
					
						
							|  |  |  | 				|| this.actions | 
					
						
							|  |  |  | 					.filter(function(n){ | 
					
						
							|  |  |  | 						return that.getActionAttr(n, 'locationSync') }) | 
					
						
							|  |  |  | 					.reduce(function(res, n){ | 
					
						
							|  |  |  | 						res[n] =  | 
					
						
							|  |  |  | 							(that.getActionAttr(n, 'doc') || '') | 
					
						
							|  |  |  | 								.split(/[\\\/]/) | 
					
						
							|  |  |  | 								.pop() | 
					
						
							|  |  |  | 						res[n] = res[n] == '' ?  | 
					
						
							|  |  |  | 							n  | 
					
						
							|  |  |  | 							: res[n]  | 
					
						
							|  |  |  | 						return res | 
					
						
							|  |  |  | 					}, {})) }, | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	sync: ['- System/Synchronize index', | 
					
						
							|  |  |  | 		core.doc`Synchronize index...
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			.sync() | 
					
						
							|  |  |  | 				-> promise | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		NOTE: it is up to the client to detect and implement the actual  | 
					
						
							|  |  |  | 			sync mechanics. | 
					
						
							|  |  |  | 		NOTE: this expects the return value of the sync handler to be  | 
					
						
							|  |  |  | 			a promise... | 
					
						
							|  |  |  | 		`,
 | 
					
						
							|  |  |  | 		function(){ | 
					
						
							|  |  |  | 			var method = this.location.sync | 
					
						
							|  |  |  | 			return method in this ? | 
					
						
							|  |  |  | 				// NOTE: this should return a promise...
 | 
					
						
							|  |  |  | 				this[method](...arguments) | 
					
						
							|  |  |  | 				: Promise.resolve() }], | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	toggleSyncMethod: ['File/Index synchronization method',  | 
					
						
							|  |  |  | 		core.doc`Toggle index synchronization method
 | 
					
						
							|  |  |  | 		 | 
					
						
							|  |  |  | 		NOTE: this will not show disabled methods.`,
 | 
					
						
							|  |  |  | 		{mode: 'advancedBrowseModeAction'}, | 
					
						
							|  |  |  | 		toggler.Toggler(null,  | 
					
						
							|  |  |  | 			function(_, state){ | 
					
						
							|  |  |  | 				var dict = this.location_sync_methods | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 				// get...
 | 
					
						
							|  |  |  | 				if(state == null){ | 
					
						
							|  |  |  | 					return dict[this.location.sync]  | 
					
						
							|  |  |  | 						|| this.location.sync  | 
					
						
							|  |  |  | 						|| 'none' }	 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 				// clear... 
 | 
					
						
							|  |  |  | 				if(state == 'none'){ | 
					
						
							|  |  |  | 					delete this.location.sync  | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 				// set...
 | 
					
						
							|  |  |  | 				} else { | 
					
						
							|  |  |  | 					// reverse dict...
 | 
					
						
							|  |  |  | 					var rdict = new Map( | 
					
						
							|  |  |  | 						Object.entries(dict) | 
					
						
							|  |  |  | 							.map(function([k, v]){  | 
					
						
							|  |  |  | 								return [v, k] })) | 
					
						
							|  |  |  | 					// normalize state to action name...
 | 
					
						
							|  |  |  | 					state = state in dict ?  | 
					
						
							|  |  |  | 						state | 
					
						
							|  |  |  | 						: (rdict.get(state) || state) | 
					
						
							|  |  |  | 					this.location.sync = state  | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 				this.markChanged | 
					
						
							|  |  |  | 					&& this.markChanged('config') | 
					
						
							|  |  |  | 			}, | 
					
						
							|  |  |  | 			function(){  | 
					
						
							|  |  |  | 				var that = this | 
					
						
							|  |  |  | 				return ['none',  | 
					
						
							|  |  |  | 					...Object.entries(this.location_sync_methods) | 
					
						
							|  |  |  | 						.filter(function([n, d]){ | 
					
						
							|  |  |  | 							// do not list disabled methods...
 | 
					
						
							|  |  |  | 							return that.getActionMode(n) != 'disabled' }) | 
					
						
							|  |  |  | 						.map(function([n, d]){ return d }) ] })], | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// 1) store .location
 | 
					
						
							|  |  |  | 	// 2) cleanup .images[..].base_path
 | 
					
						
							|  |  |  | 	//
 | 
					
						
							|  |  |  | 	// XXX might be good to make the .base_path relative to location 
 | 
					
						
							|  |  |  | 	// 		if possible...
 | 
					
						
							|  |  |  | 	// XXX not sure if this is the right place for .images[..].base_path 
 | 
					
						
							|  |  |  | 	// 		handling...
 | 
					
						
							|  |  |  | 	json: [function(){  | 
					
						
							|  |  |  | 		return function(res){ | 
					
						
							|  |  |  | 			if(this.location){ | 
					
						
							|  |  |  | 				var l = res.location = JSON.parse(JSON.stringify(this.location)) | 
					
						
							|  |  |  | 				 | 
					
						
							|  |  |  | 				// cleanup base_path...
 | 
					
						
							|  |  |  | 				Object.keys(res.images || {}).forEach(function(gid){ | 
					
						
							|  |  |  | 					var img = res.images[gid] | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 					if(l.path == img.base_path){ | 
					
						
							|  |  |  | 						delete img.base_path | 
					
						
							|  |  |  | 					} | 
					
						
							|  |  |  | 				}) } }}], | 
					
						
							|  |  |  | 	load: [function(){ | 
					
						
							|  |  |  | 		return function(_, data){ | 
					
						
							|  |  |  | 			var that = this | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			// NOTE: we are setting this after the load because the 
 | 
					
						
							|  |  |  | 			// 		loader may .clear() the viewer, thus clearing the
 | 
					
						
							|  |  |  | 			// 		.location too...
 | 
					
						
							|  |  |  | 			var l = this.__location = data.location | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			// set default image .base_path
 | 
					
						
							|  |  |  | 			// XXX not sure we need this...
 | 
					
						
							|  |  |  | 			data.location | 
					
						
							|  |  |  | 				&& Object.keys(this.images || {}).forEach(function(gid){ | 
					
						
							|  |  |  | 					var img = that.images[gid] | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 					if(img.base_path == null){ | 
					
						
							|  |  |  | 						img.base_path = l.path | 
					
						
							|  |  |  | 					} | 
					
						
							|  |  |  | 				}) }}], | 
					
						
							|  |  |  | 	clone: [function(){ | 
					
						
							|  |  |  | 		return function(res){ | 
					
						
							|  |  |  | 			if(this.location){ | 
					
						
							|  |  |  | 				res.__location = JSON.parse(JSON.stringify(this.__location)) | 
					
						
							|  |  |  | 			} }}], | 
					
						
							|  |  |  | 	clear: [function(){  | 
					
						
							|  |  |  | 		this.clearLoaction() }], | 
					
						
							|  |  |  | }) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | module.Location = core.ImageGridFeatures.Feature({ | 
					
						
							|  |  |  | 	title: '', | 
					
						
							|  |  |  | 	doc: '', | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	tag: 'location', | 
					
						
							|  |  |  | 	depends: [ | 
					
						
							|  |  |  | 		'base', | 
					
						
							|  |  |  | 	], | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	actions: LocationActions, | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	handlers: [ | 
					
						
							|  |  |  | 		// handle:
 | 
					
						
							|  |  |  | 		// 	- local configuration...
 | 
					
						
							|  |  |  | 		// 		.location <-> .config
 | 
					
						
							|  |  |  | 		// XXX should this handle image .base_path ???
 | 
					
						
							|  |  |  | 		['prepareIndexForWrite',  | 
					
						
							|  |  |  | 			function(res){ | 
					
						
							|  |  |  | 				if(res.changes === true || res.changes.config){ | 
					
						
							|  |  |  | 					var data = {} | 
					
						
							|  |  |  | 					;(this.config['location-stored-attrs'] || []) | 
					
						
							|  |  |  | 						.forEach(function(attr){ | 
					
						
							|  |  |  | 							attr in (res.raw.location || {}) | 
					
						
							|  |  |  | 								&& (data[attr] = res.raw.location[attr]) }) | 
					
						
							|  |  |  | 					Object.keys(data).length > 0 | 
					
						
							|  |  |  | 						&& (res.index.config =  | 
					
						
							|  |  |  | 							Object.assign( | 
					
						
							|  |  |  | 								res.index.config || {}, | 
					
						
							|  |  |  | 								data)) } }], | 
					
						
							|  |  |  | 		['prepareIndexForLoad', | 
					
						
							|  |  |  | 			function(res, json, base_path){ | 
					
						
							|  |  |  | 				if(json.config){ | 
					
						
							|  |  |  | 					var data = {} | 
					
						
							|  |  |  | 					;(this.config['location-stored-attrs'] || []) | 
					
						
							|  |  |  | 						.forEach(function(attr){ | 
					
						
							|  |  |  | 							attr in json.config | 
					
						
							|  |  |  | 								&& (data[attr] = json.config[attr]) }) | 
					
						
							|  |  |  | 					Object.keys(data).length > 0 | 
					
						
							|  |  |  | 						&& (res.location =  | 
					
						
							|  |  |  | 							Object.assign( | 
					
						
							|  |  |  | 								res.location || {}, | 
					
						
							|  |  |  | 								data)) } }], | 
					
						
							|  |  |  | 	], | 
					
						
							|  |  |  | }) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /********************************************************************** | 
					
						
							|  |  |  | * vim:set ts=4 sw=4 :                               */ return module }) |