mirror of
				https://github.com/flynx/ImageGrid.git
				synced 2025-11-03 21:00:14 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			766 lines
		
	
	
		
			18 KiB
		
	
	
	
		
			JavaScript
		
	
	
		
			Executable File
		
	
	
	
	
			
		
		
	
	
			766 lines
		
	
	
		
			18 KiB
		
	
	
	
		
			JavaScript
		
	
	
		
			Executable File
		
	
	
	
	
/**********************************************************************
 | 
						|
* 
 | 
						|
*
 | 
						|
*
 | 
						|
**********************************************************************/
 | 
						|
((typeof define)[0]=='u'?function(f){module.exports=f(require)}:define)
 | 
						|
(function(require){ var module={} // make module AMD/node compatible...
 | 
						|
/*********************************************************************/
 | 
						|
 | 
						|
var actions = require('lib/actions')
 | 
						|
var features = require('lib/features')
 | 
						|
 | 
						|
var core = require('features/core')
 | 
						|
 | 
						|
 | 
						|
 | 
						|
/*********************************************************************/
 | 
						|
// XXX TODO:
 | 
						|
// 		- key syntax (path)
 | 
						|
// 			<store>:<path>
 | 
						|
// 		- path variables
 | 
						|
// 			$VAR or ${VAR}
 | 
						|
// 		- ability to store/load only a specific key from a specific store
 | 
						|
// 			Q: path patterns??
 | 
						|
// 				localstorage:*		- save/load everything on localstorage
 | 
						|
// 				*:config			- save load config from all stores...
 | 
						|
 | 
						|
// XXX should we unify this with the save/load API
 | 
						|
var StoreActions = actions.Actions({
 | 
						|
	config: {
 | 
						|
		// Storage mode...
 | 
						|
		//
 | 
						|
		// This can be:
 | 
						|
		// 	'read-only'
 | 
						|
		// 	'read-write'
 | 
						|
		// 	null			- ignore store
 | 
						|
		//
 | 
						|
		// NOTE: this only affects start/stop/timer event handling, manual
 | 
						|
		// 		call to .loadData(..) / .saveData(..) are not affected...
 | 
						|
		'store-mode': 'read-write',
 | 
						|
	},
 | 
						|
 | 
						|
	// Store handler dict...
 | 
						|
	//
 | 
						|
	// Format:
 | 
						|
	// 	{
 | 
						|
	// 		<store-tag>: <handler-action>,
 | 
						|
	// 		...
 | 
						|
	// 	}
 | 
						|
	//
 | 
						|
	// XXX this is almost the same as .collection_handlers...
 | 
						|
	// XXX might be a good idea to shift this from meaning "store handlers" 
 | 
						|
	// 		to "store APIs", i.e. mark all specific store actions with 
 | 
						|
	// 		store tag and collect/group them for reference...
 | 
						|
	// 			new format:
 | 
						|
	// 				{
 | 
						|
	// 					<store-tag>: [
 | 
						|
	// 						<action-name>,
 | 
						|
	// 						...
 | 
						|
	// 					],
 | 
						|
	// 					...
 | 
						|
	// 				}
 | 
						|
	// 		...might also be a good idea to split this to:
 | 
						|
	// 			.stores				- list of store tags (i.e. Object.keys(this.stores_actions))
 | 
						|
	// 			.stores_actions		- dict of store-specific actions
 | 
						|
	get stores(){
 | 
						|
		return this.cache('stores', function(d){
 | 
						|
			var res = {}
 | 
						|
 | 
						|
			this.actions.forEach(function(action){ 
 | 
						|
				var store = this.getActionAttr(action, 'handle_data_store')
 | 
						|
				res[store]
 | 
						|
					&& console.warn('Multiple handlers for store:', store)
 | 
						|
				if(store){
 | 
						|
					res[store] = action
 | 
						|
				}
 | 
						|
			}.bind(this))
 | 
						|
 | 
						|
			return res
 | 
						|
		}) },
 | 
						|
 | 
						|
	// events...
 | 
						|
	// XXX update signature -- see doc for: .loadStore(..)
 | 
						|
	storeDataLoaded: ['- Store/',
 | 
						|
		core.doc`Store data loaded event...
 | 
						|
 | 
						|
		This is tirggered as soon per store as soon as data is loaded, 
 | 
						|
		this is sync for sync stores.
 | 
						|
 | 
						|
		NOTE: only one store data set is included per call.`,
 | 
						|
		core.Event(function(store, data){
 | 
						|
			// Store data loaded event...
 | 
						|
			//
 | 
						|
			// Not intended for direct use, use .declareReady() to initiate.
 | 
						|
			return data
 | 
						|
		})],
 | 
						|
 | 
						|
 | 
						|
	// base API...
 | 
						|
	// XXX .stores: see .stores notes above for new semantics...
 | 
						|
	parseStoreQuery: ['- Store/',
 | 
						|
		core.doc`
 | 
						|
 | 
						|
			Get default query...
 | 
						|
			.parseStoreQuery()
 | 
						|
				-> defaults
 | 
						|
 | 
						|
			Get unexpanded default query...
 | 
						|
			.parseStoreQuery(true)
 | 
						|
				-> defaults
 | 
						|
 | 
						|
			Parse query...
 | 
						|
			.parseStoreQuery(query)
 | 
						|
				-> query
 | 
						|
 | 
						|
			Parse query without expanding...
 | 
						|
			.parseStoreQuery(query, true)
 | 
						|
				-> query
 | 
						|
 | 
						|
 | 
						|
		Query syntax:
 | 
						|
			<event>:<store>:<key>
 | 
						|
			<store>:<key>
 | 
						|
			<store>
 | 
						|
			<key>
 | 
						|
 | 
						|
		Format:
 | 
						|
			{
 | 
						|
				query: <input-query>,
 | 
						|
				date: <timestamp>,
 | 
						|
				event: 'manual' | <event>,
 | 
						|
				store: '*' | <store> | [<store>, ...]
 | 
						|
				key: '*' | <key> | [<key>, ...]
 | 
						|
			}
 | 
						|
 | 
						|
		`,
 | 
						|
		function(query, no_expand){
 | 
						|
			var defaults = {
 | 
						|
				date: Date.timeStamp(),
 | 
						|
				event: 'manual',
 | 
						|
				store: '*',
 | 
						|
				key: '*',
 | 
						|
			}
 | 
						|
			if(query === true || query === false){
 | 
						|
				no_expand = query
 | 
						|
				query = null
 | 
						|
			}
 | 
						|
 | 
						|
			// parse string...
 | 
						|
			if(typeof(query) == typeof('str')){
 | 
						|
				var res = {}
 | 
						|
				res.query = query
 | 
						|
 | 
						|
				query = query.split(/:/g)
 | 
						|
 | 
						|
				res.event = query.length > 2 ? 
 | 
						|
					query.shift()
 | 
						|
					: defaults.event
 | 
						|
				res.store = (this.stores[query[0]] || query.length > 1) ? 
 | 
						|
					query.shift().split(/\|/g) 
 | 
						|
					: defaults.store
 | 
						|
					//: Object.keys(this.stores)
 | 
						|
				res.key = query.length > 0 ? 
 | 
						|
					query.pop().split(/\|/g)
 | 
						|
					: defaults.key
 | 
						|
				res.date = defaults.date
 | 
						|
 | 
						|
				query = res
 | 
						|
 | 
						|
			// get the defaults...
 | 
						|
			} else if(query == null){
 | 
						|
				query = defaults
 | 
						|
 | 
						|
			// pass on the input...
 | 
						|
			} else {
 | 
						|
			}
 | 
						|
 | 
						|
			if(!no_expand){
 | 
						|
				// XXX 
 | 
						|
				var handlers = this.stores
 | 
						|
 | 
						|
				// expand store '*'...
 | 
						|
				query.store = (query.store.length == 1 && query.store[0] == '*') ? 
 | 
						|
					Object.keys(handlers) 
 | 
						|
					: query.store
 | 
						|
				// expand key '*'...
 | 
						|
				query.key = query.key.length == 1 && query.key[0] == '*' ? 
 | 
						|
					'*' 
 | 
						|
					: query.key
 | 
						|
			}
 | 
						|
 | 
						|
			return query
 | 
						|
		}],
 | 
						|
 | 
						|
 | 
						|
	// HL API...
 | 
						|
	//
 | 
						|
	// XXX sync or async or both???
 | 
						|
	// 		...should we add an .onStore(query, handler) event/promise???
 | 
						|
	// 		one way to do this is to place promises into res where async...
 | 
						|
	store: ['- Store/',
 | 
						|
		core.doc`
 | 
						|
 | 
						|
			Get stored key(s)...
 | 
						|
			.store(query)
 | 
						|
				-> value{s}
 | 
						|
 | 
						|
			Write value to key(s)...
 | 
						|
			.store(query, value)
 | 
						|
 | 
						|
			Remove key(s)...
 | 
						|
			.store(query, null)
 | 
						|
 | 
						|
 | 
						|
		Returns:
 | 
						|
			{
 | 
						|
				<store>: {
 | 
						|
					<key>: <value>,
 | 
						|
					...
 | 
						|
				},
 | 
						|
				...
 | 
						|
			}
 | 
						|
 | 
						|
 | 
						|
		NOTE: for query syntax see .parseStoreQuery(..)
 | 
						|
 | 
						|
 | 
						|
		Store API:
 | 
						|
			- High-level API...
 | 
						|
				.store(..)
 | 
						|
 | 
						|
			- Application-level API...
 | 
						|
				.prepareStoreToLoad(..)
 | 
						|
				.prepareStoreToSave(..)
 | 
						|
 | 
						|
			- Storage-level general API...
 | 
						|
				.loadStore(..)
 | 
						|
				.saveStore(..)
 | 
						|
				.clearStore(..)
 | 
						|
 | 
						|
			NOTE: .prepareStoreToSave(..) is triggered only when no data
 | 
						|
				is passed to .saveStore(..)...
 | 
						|
			NOTE: .prepareStoreToLoad(..) is called every time .loadStore(..)
 | 
						|
				is called...
 | 
						|
 | 
						|
		`,
 | 
						|
		function(query, value){
 | 
						|
			query = this.parseStoreQuery(query)
 | 
						|
			return (
 | 
						|
				// get...
 | 
						|
				arguments.length == 1 ?
 | 
						|
					this.loadStore(query)
 | 
						|
				// delete...
 | 
						|
				: (value === undefined && arguments.length == 2) ?
 | 
						|
					this.clearStore(query)
 | 
						|
				// set...
 | 
						|
				: (this.saveStore(query, value) && undefined) ) }],
 | 
						|
 | 
						|
	// Application API...
 | 
						|
	prepareStoreToSave: ['- Store/',
 | 
						|
		core.doc`
 | 
						|
 | 
						|
		Format:
 | 
						|
			{
 | 
						|
				// metadata...
 | 
						|
				mode: <mode>,
 | 
						|
				data: <timestamp>,
 | 
						|
 | 
						|
				// the actual data...
 | 
						|
				data: {
 | 
						|
					<data-type>: {
 | 
						|
						<data-key>: <data>,
 | 
						|
						...
 | 
						|
					},
 | 
						|
					...
 | 
						|
				},
 | 
						|
			}
 | 
						|
		`,
 | 
						|
		function(query, data){ 
 | 
						|
			var defaults = this.parseStoreQuery()
 | 
						|
			query = this.parseStoreQuery(query)
 | 
						|
			var stores = query.store || defaults.store
 | 
						|
 | 
						|
			data = data || {}
 | 
						|
			query.store.forEach(function(s){ data[s] = {} })
 | 
						|
 | 
						|
			return {
 | 
						|
				date: query.date || Date.timeStamp(),
 | 
						|
 | 
						|
				event: query.event || defaults.event,
 | 
						|
				key: query.key || defaults.key,
 | 
						|
 | 
						|
				data: data || {},
 | 
						|
			} 
 | 
						|
		}],
 | 
						|
	prepareStoreToLoad: ['- Store/',
 | 
						|
		core.doc`
 | 
						|
		
 | 
						|
		NOTE: this can be called multiple times, once per each store.
 | 
						|
		NOTE: only one store data set is included per call.`,
 | 
						|
		function(query, data){ 
 | 
						|
			return data || {} }],
 | 
						|
 | 
						|
	// Store API...
 | 
						|
	saveStore: ['- Store/',
 | 
						|
		core.doc`
 | 
						|
 | 
						|
			Build and store all/query data...
 | 
						|
			.saveStore()
 | 
						|
			.saveStore(query)
 | 
						|
				-> data
 | 
						|
 | 
						|
			Store query data...
 | 
						|
			.saveStore(query, data)
 | 
						|
 | 
						|
 | 
						|
		NOTE: this only calls .prepareStoreToSave(..) if no data is given...
 | 
						|
		NOTE: the returned data is the same format as returned by .store(..)
 | 
						|
		`,
 | 
						|
		core.notUserCallable(function(query, data){
 | 
						|
			// Extending action must:
 | 
						|
			// 	- save query/data to it's store...
 | 
						|
			return data == null ? 
 | 
						|
				this.prepareStoreToSave(this.parseStoreQuery(query), {}).data
 | 
						|
				: undefined
 | 
						|
		})],
 | 
						|
	loadStore: ['- Store/',
 | 
						|
		core.doc`
 | 
						|
 | 
						|
			.loadStore(query)
 | 
						|
				-> data
 | 
						|
 | 
						|
 | 
						|
		NOTE: async stores will write promises to .data
 | 
						|
		`,
 | 
						|
		core.notUserCallable(function(query){
 | 
						|
			// Extending action must:
 | 
						|
			// 	- populate query from it's store...
 | 
						|
			// 	- trigger .storeDataLoaded(store) event when done...
 | 
						|
			//
 | 
						|
			// NOTE: if query.store is a single value then the root object
 | 
						|
			// 		must be populated, otherwise the appropriate section 
 | 
						|
			// 		should be used...
 | 
						|
			// 		e.g.
 | 
						|
			// 			if(query.store.length == 1){
 | 
						|
			//				res = data
 | 
						|
			// 			} else {
 | 
						|
			// 				res['store-key'] = data
 | 
						|
			// 			}
 | 
						|
			var res = {}
 | 
						|
			var q = this.parseStoreQuery(query)
 | 
						|
			query
 | 
						|
				&& q.store.length >= 1
 | 
						|
				&& q.store.forEach(function(s){ res[s] = {} })
 | 
						|
			return this.prepareStoreToLoad(q, res)
 | 
						|
		})],
 | 
						|
	clearStore: ['- Store/',
 | 
						|
		core.doc`
 | 
						|
 | 
						|
			.clearStore(query)
 | 
						|
 | 
						|
 | 
						|
		`,
 | 
						|
		core.notUserCallable(function(query){
 | 
						|
			// Extending action:
 | 
						|
			// 	- must clear query from it's store...
 | 
						|
		})],
 | 
						|
 | 
						|
 | 
						|
 | 
						|
	// XXX LEGACY STUFF... (???)
 | 
						|
 | 
						|
 | 
						|
	// XXX REVISE API
 | 
						|
	// XXX this is different from .prepareIndexForWrite(..) in that there
 | 
						|
	// 		is no default data set...
 | 
						|
	// XXX async???
 | 
						|
	saveData: ['- Store/',
 | 
						|
		// XXX signature not final...
 | 
						|
		function(query, data){
 | 
						|
			var defaults = this.parseStoreQuery()
 | 
						|
			query = this.parseStoreQuery(query)
 | 
						|
 | 
						|
			var handlers = this.stores
 | 
						|
 | 
						|
			// save the given data...
 | 
						|
			// NOTE: we are not calling .prepareStoreToSave(..) here, thus
 | 
						|
			// 		we need not care about .key, .date, and other stuff...
 | 
						|
			if(data){
 | 
						|
				if(query.store == defaults.store || query.key == defaults.key){
 | 
						|
					console.error('saveData: at least "store" and "key" '
 | 
						|
						+'must be explicitly set in query...')
 | 
						|
					return
 | 
						|
				}
 | 
						|
 | 
						|
				var d = {
 | 
						|
					data: {}
 | 
						|
				}
 | 
						|
				var stores = query.store == '*' ? handlers : query.store
 | 
						|
				stores = stores instanceof Array ? stores : [stores]
 | 
						|
				stores.forEach(function(s){ 
 | 
						|
					d.data[s] = {
 | 
						|
						[query.key]: data,
 | 
						|
					} })
 | 
						|
 | 
						|
				data = d
 | 
						|
 | 
						|
			// build the data...
 | 
						|
			} else {
 | 
						|
				data = this.prepareStoreToSave(query)
 | 
						|
			}
 | 
						|
 | 
						|
			// iterate and handle stores...
 | 
						|
			Object.keys(data.data).forEach(function(store){
 | 
						|
				var handler = handlers[store]
 | 
						|
				handler 
 | 
						|
					&& this[handler].call(this, data.data[store])
 | 
						|
			}.bind(this))
 | 
						|
		}],
 | 
						|
	// XXX add query support... (???)
 | 
						|
	// 		...we can't support keys other than '*' until we register 
 | 
						|
	// 		store keys...
 | 
						|
	loadData: ['- Store/',
 | 
						|
		function(query){
 | 
						|
			var handlers = this.stores
 | 
						|
 | 
						|
			var defaults = this.parseStoreQuery()
 | 
						|
			query = this.parseStoreQuery(query)
 | 
						|
 | 
						|
			// XXX need to filter loading by query.key...
 | 
						|
			var data = {}
 | 
						|
			return Promise
 | 
						|
				.all(query.store
 | 
						|
					.map(function(s){
 | 
						|
						var res = this[handlers[s]]()
 | 
						|
						return res instanceof Promise ?
 | 
						|
							// async store...
 | 
						|
							res.then(function(d){ d 
 | 
						|
								&& (data[s] = d)
 | 
						|
								&& this.storeDataLoaded(s,
 | 
						|
									this.prepareStoreToLoad(query, {[s]: d})) }.bind(this))
 | 
						|
							// sync store...
 | 
						|
							: (res 
 | 
						|
								&& (data[s] = res)
 | 
						|
								&& this.storeDataLoaded(s,
 | 
						|
									this.prepareStoreToLoad(query, {[s]: res})))
 | 
						|
					}.bind(this))) 
 | 
						|
				.then(function(){ return data })}],
 | 
						|
	// XXX do we need to do a partial clear???
 | 
						|
	clearData: ['- Store/',
 | 
						|
		function(target){
 | 
						|
			var handlers = this.stores
 | 
						|
 | 
						|
			Object.keys(handlers).forEach(function(store){
 | 
						|
				var handler = handlers[store]
 | 
						|
				handler
 | 
						|
					&& this[handler].call(this, null)
 | 
						|
			}.bind(this))
 | 
						|
		}],
 | 
						|
})
 | 
						|
 | 
						|
var Store = 
 | 
						|
module.Store = core.ImageGridFeatures.Feature({
 | 
						|
	title: '',
 | 
						|
	doc: '',
 | 
						|
 | 
						|
	tag: 'store',
 | 
						|
	depends: [
 | 
						|
		'cache',
 | 
						|
	],
 | 
						|
	suggested: [
 | 
						|
		'store-localstorage',
 | 
						|
		'store-fs',
 | 
						|
	],
 | 
						|
	isApplicable: function(){ return typeof(localStorage) != 'undefined' },
 | 
						|
 | 
						|
	actions: StoreActions,
 | 
						|
 | 
						|
	handlers: [
 | 
						|
		['start.pre', 
 | 
						|
			function(){ 
 | 
						|
				if(this.config['store-mode'] != null){
 | 
						|
					this.requestReadyAnnounce()
 | 
						|
					this
 | 
						|
						.loadData() 
 | 
						|
						.then(function(){
 | 
						|
							this.declareReady() }.bind(this)) 
 | 
						|
				} }],
 | 
						|
		['stop', 
 | 
						|
			function(){ 
 | 
						|
				this.config['store-mode'] == 'read-write' && this.saveData() }],
 | 
						|
		// XXX timer???
 | 
						|
		// XXX
 | 
						|
	],
 | 
						|
})
 | 
						|
 | 
						|
 | 
						|
 | 
						|
//---------------------------------------------------------------------
 | 
						|
 | 
						|
// NOTE: the doc is reused for both localStorage and sessionStorage with 
 | 
						|
// 		appropriate automated changes...
 | 
						|
var __storageHandler_doc = 
 | 
						|
	core.doc`Handle localStorage store data...
 | 
						|
		
 | 
						|
		Write data to key...
 | 
						|
		.localStorageDataHandler(key, data)
 | 
						|
		
 | 
						|
		Get data from key...
 | 
						|
		.localStorageDataHandler(key)
 | 
						|
			-> value
 | 
						|
		
 | 
						|
		Remove key...
 | 
						|
		.localStorageDataHandler(key, null)
 | 
						|
		.localStorageDataHandler([key, ..], null)
 | 
						|
		
 | 
						|
 | 
						|
 | 
						|
		Get stored keys...
 | 
						|
		.localStorageDataHandler('?')
 | 
						|
			-> keys
 | 
						|
		
 | 
						|
		.localStorageDataHandler()
 | 
						|
		.localStorageDataHandler('*')
 | 
						|
		.localStorageDataHandler('??')
 | 
						|
			-> data
 | 
						|
		
 | 
						|
		.localStorageDataHandler([key, ..], '??')
 | 
						|
			-> data
 | 
						|
 | 
						|
		Expand data to store...
 | 
						|
		.localStorageDataHandler(data)
 | 
						|
		
 | 
						|
		Remove all data...
 | 
						|
		.localStorageDataHandler(null)
 | 
						|
		
 | 
						|
		
 | 
						|
	NOTE: when handling key expansion the unexpanded key should be used 
 | 
						|
		to access data, e.g. if writing '\${INSTANCE}/xyz' the same string
 | 
						|
		is returned via .localStorageDataHandler('??') and should be used 
 | 
						|
		to get the value, i.e. 
 | 
						|
			.localStorageDataHandler('\${INSTANCE}/xyz')
 | 
						|
	`
 | 
						|
function makeStorageHandler(storage){
 | 
						|
	var func = function(a, b){
 | 
						|
		// normalize a...
 | 
						|
		a = a == '??' ? '*' : a 
 | 
						|
 | 
						|
		storage = typeof(storage) == typeof('str') ? window[storage] : storage
 | 
						|
 | 
						|
		var instance = this.config['store-instance-key'] 
 | 
						|
		var resolvePath = function(p){
 | 
						|
			return p
 | 
						|
				.replace('${INSTANCE}', instance) }
 | 
						|
 | 
						|
		var keys = Object.keys(storage)
 | 
						|
 | 
						|
		var dict_key = '${INSTANCE}/__dict__'
 | 
						|
		var k = resolvePath(dict_key)
 | 
						|
		var dict = JSON.parse(storage[k] || '{}')
 | 
						|
		dict[k] = dict_key
 | 
						|
 | 
						|
		// get list of keys...
 | 
						|
		if(a == '?'){
 | 
						|
			return keys
 | 
						|
				.map(function(key){ return dict[key] || key })
 | 
						|
 | 
						|
		// get store contents...
 | 
						|
		} else if(a == '*' 
 | 
						|
				|| arguments.length == 0 
 | 
						|
				|| (a instanceof Array && (b == '??' || arguments.length == 1))){
 | 
						|
			var res = {}
 | 
						|
			keys = a instanceof Array ? a : keys
 | 
						|
			keys
 | 
						|
				// add parsed candidates...
 | 
						|
				.map(function(k){ return resolvePath(k) })
 | 
						|
				// clear keys not in store...
 | 
						|
				.filter(function(k){ 
 | 
						|
					return dict[k] in storage || k in storage })
 | 
						|
				.forEach(function(k){
 | 
						|
					res[dict[k] || k] = JSON.parse(storage[k]) })
 | 
						|
			return res
 | 
						|
 | 
						|
		// remove all keys...
 | 
						|
		} else if(a === null || (a instanceof Array && b === null)){
 | 
						|
			;(a || keys)
 | 
						|
				.forEach(function(key){
 | 
						|
					delete storage[key] })
 | 
						|
			dict = {}
 | 
						|
 | 
						|
		// expand data to store...
 | 
						|
		} else if(typeof(a) != typeof('str')){
 | 
						|
			Object.keys(a).forEach(function(key){
 | 
						|
				var k = resolvePath(key)
 | 
						|
				k != key
 | 
						|
					&& (dict[k] = key)
 | 
						|
				storage[k] = JSON.stringify(a[key]) })
 | 
						|
 | 
						|
		// remove key...
 | 
						|
		// XXX revise, should this be null or undefined???
 | 
						|
		} else if(b === null){
 | 
						|
			var k = resolvePath(a)
 | 
						|
			delete dict[k]
 | 
						|
			delete storage[k]
 | 
						|
 | 
						|
		// get keys...
 | 
						|
		// XXX revise, should this be null or undefined???
 | 
						|
		} else if(b === undefined){
 | 
						|
			var k = resolvePath(a)
 | 
						|
			return k in storage ? 
 | 
						|
				JSON.parse(storage[k]) 
 | 
						|
				: actions.UNDEFINED
 | 
						|
 | 
						|
		// write key...
 | 
						|
		} else {
 | 
						|
			var k = resolvePath(a)
 | 
						|
			k != a
 | 
						|
				&& (dict[k] = a)
 | 
						|
			storage[k] = JSON.stringify(b)
 | 
						|
		}
 | 
						|
 | 
						|
		storage[resolvePath('${INSTANCE}/__dict__')] = JSON.stringify(dict)
 | 
						|
	}
 | 
						|
 | 
						|
	if(typeof(storage) == typeof('str')){
 | 
						|
		func.long_doc = __storageHandler_doc.replace(/localStorage/g, storage)
 | 
						|
	}
 | 
						|
 | 
						|
	return func
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 
 | 
						|
 | 
						|
// XXX we should have a separate store config with settings of how to 
 | 
						|
// 		load the store... (???)
 | 
						|
var StoreLocalStorageActions = actions.Actions({
 | 
						|
	config: {
 | 
						|
		'store-instance-key': 'ImageGrid.Viewer.main',
 | 
						|
	},
 | 
						|
 | 
						|
	// NOTE: for docs see __storageHandler_doc...
 | 
						|
	localStorageDataHandler: ['- Store/',
 | 
						|
		{handle_data_store: 'storage',},
 | 
						|
		makeStorageHandler('localStorage')],
 | 
						|
	sessionStorageDataHandler: ['- Store/',
 | 
						|
		{handle_data_store: 'session',},
 | 
						|
		makeStorageHandler('sessionStorage')],
 | 
						|
})
 | 
						|
 | 
						|
var StoreLocalStorage = 
 | 
						|
module.StoreLocalStorage = core.ImageGridFeatures.Feature({
 | 
						|
	title: '',
 | 
						|
	doc: '',
 | 
						|
 | 
						|
	tag: 'store-localstorage',
 | 
						|
	depends: [
 | 
						|
		'store',
 | 
						|
	],
 | 
						|
	isApplicable: function(){ 
 | 
						|
		return typeof(localStorage) != 'undefined' 
 | 
						|
			&& typeof(sessionStorage) != 'undefined' },
 | 
						|
 | 
						|
	actions: StoreLocalStorageActions,
 | 
						|
 | 
						|
	handlers: [
 | 
						|
		// localStorage...
 | 
						|
		// XXX should we use .localStorageDataHandler(..) here???
 | 
						|
		// XXX the following two sections are almost identical...
 | 
						|
		['saveStore', 
 | 
						|
			function(res, query, value){
 | 
						|
				var q = this.parseStoreQuery(query)
 | 
						|
				query 
 | 
						|
					&& q.key instanceof Array
 | 
						|
					&& q.store.indexOf('storage') >= 0
 | 
						|
					&& q.key.forEach(function(k){
 | 
						|
						this.localStorageDataHandler(
 | 
						|
							k, 
 | 
						|
							res !== this ? res.data : value) }.bind(this)) }],
 | 
						|
		['loadStore', 
 | 
						|
			function(res, query){
 | 
						|
				query = this.parseStoreQuery(query)
 | 
						|
				res.storage
 | 
						|
					&& Object.assign(res.storage, this.localStorageDataHandler(query.key)) }],
 | 
						|
		['clearStore', 
 | 
						|
			function(_, query){
 | 
						|
				var q = this.parseStoreQuery(query)
 | 
						|
				query 
 | 
						|
					&& q.store.indexOf('storage') >= 0
 | 
						|
					&& this.localStorageDataHandler(q.key, null) }],
 | 
						|
 | 
						|
		// sessionStorage...
 | 
						|
		['saveStore', 
 | 
						|
			function(res, query, value){
 | 
						|
				var q = this.parseStoreQuery(query)
 | 
						|
				query 
 | 
						|
					&& q.key instanceof Array
 | 
						|
					&& q.store.indexOf('session') >= 0
 | 
						|
					&& q.key.forEach(function(k){
 | 
						|
						this.sessionStorageDataHandler(
 | 
						|
							k, 
 | 
						|
							res !== this ? res.data : value) }.bind(this)) }],
 | 
						|
		['loadStore', 
 | 
						|
			function(res, query){
 | 
						|
				query = this.parseStoreQuery(query)
 | 
						|
				res.session
 | 
						|
					&& Object.assign(res.session, this.sessionStorageDataHandler(query.key)) }],
 | 
						|
		['clearStore', 
 | 
						|
			function(_, query){
 | 
						|
				var q = this.parseStoreQuery(query)
 | 
						|
				query 
 | 
						|
					&& q.store.indexOf('session') >= 0
 | 
						|
					&& this.sessionStorageDataHandler(q.key, null) }],
 | 
						|
	],
 | 
						|
})
 | 
						|
 | 
						|
 | 
						|
 | 
						|
//---------------------------------------------------------------------
 | 
						|
 | 
						|
// XXX StoreFSJSONSync
 | 
						|
// 		Lookup order:
 | 
						|
// 			- app dir
 | 
						|
// 			- $HOME
 | 
						|
var StoreFSJSONActions = actions.Actions({
 | 
						|
	config: {
 | 
						|
	},
 | 
						|
 | 
						|
	// XXX
 | 
						|
	localStorageFSJSONSyncHandler: ['- Store/',
 | 
						|
		{handle_data_store: 'fileSync',},
 | 
						|
		function(a, b){
 | 
						|
			// XXX
 | 
						|
		}],
 | 
						|
	// XXX
 | 
						|
	localStorageFSJSONHandler: ['- Store/',
 | 
						|
		{handle_data_store: 'file',},
 | 
						|
		function(a, b){
 | 
						|
			// XXX
 | 
						|
			return new Promise(function(resolve, reject){
 | 
						|
				// XXX
 | 
						|
				resolve()
 | 
						|
			})
 | 
						|
		}],
 | 
						|
})
 | 
						|
 | 
						|
var StoreFSJSON = 
 | 
						|
module.StoreFSJSONSync = core.ImageGridFeatures.Feature({
 | 
						|
	title: '',
 | 
						|
	doc: '',
 | 
						|
 | 
						|
	tag: 'store-fs',
 | 
						|
	depends: [
 | 
						|
		'fs',
 | 
						|
		'store',
 | 
						|
	],
 | 
						|
 | 
						|
	actions: StoreFSJSONActions,
 | 
						|
})
 | 
						|
 | 
						|
 | 
						|
 | 
						|
 | 
						|
/**********************************************************************
 | 
						|
* vim:set ts=4 sw=4 :                               */ return module })
 |