mirror of
				https://github.com/flynx/ImageGrid.git
				synced 2025-10-30 19:00:09 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			742 lines
		
	
	
		
			20 KiB
		
	
	
	
		
			JavaScript
		
	
	
		
			Executable File
		
	
	
	
	
			
		
		
	
	
			742 lines
		
	
	
		
			20 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 data = require('imagegrid/data')
 | |
| 
 | |
| var toggler = require('lib/toggler')
 | |
| var actions = require('lib/actions')
 | |
| var features = require('lib/features')
 | |
| 
 | |
| var browse = require('lib/widget/browse')
 | |
| 
 | |
| var core = require('features/core')
 | |
| var widgets = require('features/ui-widgets')
 | |
| 
 | |
| 
 | |
| 
 | |
| /*********************************************************************/
 | |
| // XXX should collections be in the Crop menu????
 | |
| // 		...essentially a collection is a saved crop, so this would be 
 | |
| // 		logical, would simplify control, etc.
 | |
| // 		
 | |
| 
 | |
| // XXX things we need to do to collections:
 | |
| // 		- auto-collections
 | |
| // 			- tags -- adding/removing images adds/removes tags
 | |
| // 			- ribbons -- top / bottom / n-m / top+2 / ..
 | |
| // XXX might be a good idea to make collection loading part of the 
 | |
| // 		.load(..) protocol...
 | |
| // 		...this could be done via a url suffix, as a shorthand.
 | |
| // 		something like:
 | |
| // 			/path/to/index:collection
 | |
| // 				-> /path/to/index/sub/path/.ImageGrid/collections/collection
 | |
| // XXX loading collections by direct path would require us to look 
 | |
| // 		in the containing index for missing parts (*images.json, ...)
 | |
| // XXX saving a local collection would require us to save to two 
 | |
| // 		locations:
 | |
| // 			- collection specific stuff (data) to collection path
 | |
| // 			- global stuff (images, tags, ...) to base index...
 | |
| // XXX handle tags here???
 | |
| // 		...keep them global or local to collection???
 | |
| // 		global sounds better...
 | |
| // XXX undo...
 | |
| var CollectionActions = actions.Actions({
 | |
| 
 | |
| 	// Format:
 | |
| 	// 	{
 | |
| 	// 		<title>: {
 | |
| 	// 			title: <title>,
 | |
| 	// 			gid: <gid>,
 | |
| 	//
 | |
| 	// 			// base collection format -- raw data...
 | |
| 	// 			data: <data>,
 | |
| 	//
 | |
| 	// 			...
 | |
| 	// 		},
 | |
| 	// 		...
 | |
| 	// 	}
 | |
| 	collections: null,
 | |
| 
 | |
| 	get collection(){
 | |
| 		return this.location.collection },
 | |
| 	set collection(value){
 | |
| 		this.loadCollection(value) },
 | |
| 
 | |
| 	// Format:
 | |
| 	// 	{
 | |
| 	// 		// NOTE: this is always the first handler...
 | |
| 	// 		'data': <action-name>,
 | |
| 	//
 | |
| 	// 		<format>: <action-name>,
 | |
| 	// 		...
 | |
| 	// 	}
 | |
| 	get collection_handlers(){
 | |
| 		var handlers = this.__collection_handlers || {}
 | |
| 
 | |
| 		if(Object.keys(handlers).length == 0){
 | |
| 			var that = this
 | |
| 			handlers['data'] = null
 | |
| 			this.actions.forEach(function(action){
 | |
| 				var fmt = that.getActionAttr(action, 'collectionFormat')
 | |
| 				if(fmt){
 | |
| 					handlers[fmt] = action
 | |
| 				}
 | |
| 			})
 | |
| 		}
 | |
| 
 | |
| 		return handlers
 | |
| 	},
 | |
| 
 | |
| 	collectionDataLoader: ['- Collections/',
 | |
| 		core.doc`Collection data loader
 | |
| 		
 | |
| 			.collectionDataLoader(title, data)
 | |
| 				-> promise
 | |
| 		
 | |
| 		The resulting promise will resolve to a Data object that will get
 | |
| 		loaded as the collection.
 | |
| 
 | |
| 		data is of the .collections item format.
 | |
| 		`,
 | |
| 		{collectionFormat: 'data'},
 | |
| 		function(title, data){ 
 | |
| 			return new Promise(function(resolve){ resolve(data.data) }) }],
 | |
| 
 | |
| 	loadCollection: ['- Collections/',
 | |
| 		core.doc`Load collection...
 | |
| 
 | |
| 		This will get collection data and crop into it.
 | |
| 			
 | |
| 		If .data for a collection is not available this will do nothing, 
 | |
| 		this enables extending actions to handle the collection in 
 | |
| 		different ways.
 | |
| 
 | |
| 		Protocol:
 | |
| 		- collection format handlers: .collection_handlers
 | |
| 			- built from actions that define .collectionFormat attr to 
 | |
| 			  contain the target format string.
 | |
| 		- format is determined by matching it to a key in .collections[collection]
 | |
| 			e.g. 'data' is applicable if .collections[collection].data is not null
 | |
| 		- the format key's value is passed to the handler action
 | |
| 		- the handler is expected to return a promise
 | |
| 		- only the first matching handler is called
 | |
| 		- the data handler is always first to get checked
 | |
| 
 | |
| 		For an example handler see:
 | |
| 			.collectionDataLoader(..)
 | |
| 
 | |
| 
 | |
| 		The .data handler is always first to enable caching, i.e. once some
 | |
| 		non-data handler is done, it can set the .data which will be loaded
 | |
| 		directly the next time.
 | |
| 		To invalidate such a cache .data should simply be deleted.
 | |
| 		`,
 | |
| 		function(collection){
 | |
| 			var that = this
 | |
| 			if(collection == null 
 | |
| 					|| this.collections == null 
 | |
| 					|| !(collection in this.collections)){
 | |
| 				return
 | |
| 			}
 | |
| 
 | |
| 			var data = this.collections[collection]
 | |
| 			var handlers = this.collection_handlers
 | |
| 
 | |
| 			// XXX might be good to sort handlers...
 | |
| 			// XXX
 | |
| 
 | |
| 			for(var format in handlers){
 | |
| 				if(data[format]){
 | |
| 					return this[handlers[format]](collection, data)
 | |
| 						.then(function(data){
 | |
| 							data
 | |
| 								&& that.crop.chainCall(that, function(){
 | |
| 									// NOTE: the collection and .data may have different
 | |
| 									// 		orders and/or sets of elements, this we need 
 | |
| 									// 		to sync, and do it BEFORE all the rendering 
 | |
| 									// 		happens...
 | |
| 									that.data.updateImagePositions()
 | |
| 								}, data)
 | |
| 								// NOTE: we need this to sync the possible different 
 | |
| 								// 		states (order, ...) of the collection and .data...
 | |
| 								&& that.collectionLoaded(collection)	
 | |
| 						})
 | |
| 				}
 | |
| 			}
 | |
| 		}],
 | |
| 
 | |
| 	// events...
 | |
| 	collectionLoaded: ['- Collections/',
 | |
| 		core.doc`This is called by .loadCollection(..) or one of the 
 | |
| 		overloading actions when collection load is done...
 | |
| 		
 | |
| 		`,
 | |
| 		core.notUserCallable(function(collection){
 | |
| 			// This is the window resize event...
 | |
| 			//
 | |
| 			// Not for direct use.
 | |
| 			this.data.collection = this.location.collection = collection
 | |
| 		})],
 | |
| 	collectionUnloaded: ['- Collections/',
 | |
| 		core.doc`This is called when unloading a collection.
 | |
| 		`,
 | |
| 		core.notUserCallable(function(collection){
 | |
| 			// This is the window resize event...
 | |
| 			//
 | |
| 			// Not for direct use.
 | |
| 		})],
 | |
| 
 | |
| 	saveCollection: ['- Collections/',
 | |
| 		core.doc`Save current state to collection
 | |
| 
 | |
| 			Save Current state as collection
 | |
| 			.saveCollection(collection)
 | |
| 				-> this
 | |
| 
 | |
| 			Save new empty collection
 | |
| 			.saveCollection(collection, true)
 | |
| 				-> this
 | |
| 		`,
 | |
| 		function(collection, empty){
 | |
| 			var that = this
 | |
| 			collection = collection || this.collection
 | |
| 
 | |
| 			if(collection == null){
 | |
| 				return
 | |
| 			}
 | |
| 
 | |
| 			var collections = this.collections = this.collections || {}
 | |
| 
 | |
| 			collections[collection] = {
 | |
| 				title: collection,
 | |
| 
 | |
| 				// NOTE: we do not need to care about tags here as they 
 | |
| 				// 		will get overwritten on load...
 | |
| 				data: (empty ? 
 | |
| 						(new this.data.constructor())
 | |
| 						: this.data
 | |
| 							.clone()
 | |
| 							.removeUnloadedGIDs())
 | |
| 					.run(function(){
 | |
| 						this.collection = collection
 | |
| 						// NOTE: we are doing this manually after .removeUnloadedGIDs(..)
 | |
| 						// 		as the later will mess-up the structures 
 | |
| 						// 		inherited from the main .data, namely tags...
 | |
| 						this.tags = that.data.tags
 | |
| 					}),
 | |
| 			}
 | |
| 		}],
 | |
| 	newCollection: ['- Collections/',
 | |
| 		function(collection){ return this.saveCollection(collection, true) }],
 | |
| 	// XXX should we do anything special if collection is loaded???
 | |
| 	removeCollection: ['- Collections/',
 | |
| 		function(collection){
 | |
| 			delete this.collections[collection]
 | |
| 		}],
 | |
| 
 | |
| 	inCollections: ['- Image/',
 | |
| 		core.doc`Get list of collections containing item`,
 | |
| 		function(gid){
 | |
| 			var that = this
 | |
| 			gid = this.data.getImage(gid)
 | |
| 			return Object.keys(this.collections || {})
 | |
| 				.filter(function(c){
 | |
| 					return !gid 
 | |
| 						|| that.collections[c].data.getImage(gid) })
 | |
| 		}],
 | |
| 
 | |
| 	collect: ['- Collections/',
 | |
| 		core.doc`Add items to collection
 | |
| 
 | |
| 		NOTE: this will not account for item topology.`,
 | |
| 		function(gids, collection){
 | |
| 			var that = this
 | |
| 
 | |
| 			gids = gids == 'loaded' ? this.data.getImages('loaded')
 | |
| 				: gids instanceof Array ? gids 
 | |
| 				: [gids]
 | |
| 			gids = gids
 | |
| 				.map(function(gid){ 
 | |
| 					return gid in that.data.ribbons ? 
 | |
| 						// when adding a ribbon gid expand to images...
 | |
| 						that.data.ribbons[gid].compact()
 | |
| 						: [that.data.getImage(gid)] })
 | |
| 				.reduce(function(a, b){ return a.concat(b) }, [])
 | |
| 
 | |
| 			collection = collection || this.collection
 | |
| 
 | |
| 			if(collection == null){
 | |
| 				return
 | |
| 			}
 | |
| 
 | |
| 			// add to collection...
 | |
| 			var data = this.data.constructor.fromArray(gids)
 | |
| 
 | |
| 			return this.joinCollect(null, collection, data)
 | |
| 		}],
 | |
| 	joinCollect: ['- Collections/Merge to collection',
 | |
| 		core.doc`Merge/Join current state to collection
 | |
| 
 | |
| 			Join current state into collection
 | |
| 			.joinCollect(collection)
 | |
| 				-> this
 | |
| 
 | |
| 			Join current state with specific alignment into collection
 | |
| 			.joinCollect(align, collection)
 | |
| 				-> this
 | |
| 
 | |
| 			Join data to collection with specific alignment
 | |
| 			.joinCollect(align, collection, data)
 | |
| 				-> this
 | |
| 
 | |
| 		This is like .collect(..) but will preserve topology.
 | |
| 		
 | |
| 		NOTE: for align docs see Data.join(..)
 | |
| 		NOTE: if align is set to null or not given then it will be set 
 | |
| 			to default value.
 | |
| 		NOTE: this will join to the left (prepend) of the collections, this is 
 | |
| 			different from how basic .join(..) works (appends)
 | |
| 		`,
 | |
| 		function(align, collection, data){
 | |
| 			collection = arguments.length == 1 ? align : collection
 | |
| 			if(collection == null){
 | |
| 				return
 | |
| 			}
 | |
| 			// if only collection is given, reset align to null...
 | |
| 			align = align === collection ? null : align
 | |
| 
 | |
| 			if(this.collections && this.collections[collection]){
 | |
| 				//this.collections[collection].data.join(align, data || this.data.clone())
 | |
| 				this.collections[collection].data = (data || this.data)
 | |
| 					.clone()
 | |
| 					.join(align, this.collections[collection].data)
 | |
| 
 | |
| 			} else {
 | |
| 				this.saveCollection(collection)
 | |
| 			}
 | |
| 		}],
 | |
| 	uncollect: ['Collections|Image/$Uncollect image',
 | |
| 		{browseMode: function(){ return !this.collection && 'disabled' }},
 | |
| 		function(gids, collection){
 | |
| 			var that = this
 | |
| 
 | |
| 			gids = gids == 'loaded' ? this.data.getImages('loaded')
 | |
| 				: gids instanceof Array ? gids 
 | |
| 				: [gids]
 | |
| 			gids = gids
 | |
| 				.map(function(gid){ 
 | |
| 					return gid in that.data.ribbons ? 
 | |
| 						// when adding a ribbon gid expand to images...
 | |
| 						that.data.ribbons[gid].compact()
 | |
| 						: [that.data.getImage(gid)] })
 | |
| 				.reduce(function(a, b){ return a.concat(b) }, [])
 | |
| 
 | |
| 			collection = collection || this.collection
 | |
| 
 | |
| 			if(collection == null){
 | |
| 				return
 | |
| 			}
 | |
| 
 | |
| 			/*/ NOTE: we are not using .data.updateImagePositions(gids, 'hide') 
 | |
| 			// 		here because it will remove the gids from everything
 | |
| 			// 		while we need them removed only from ribbons...
 | |
| 			var hideGIDs = function(){
 | |
| 				var d = this
 | |
| 				gids.forEach(function(gid){
 | |
| 					var i = d.order.indexOf(gid)
 | |
| 					Object.keys(d.ribbons).forEach(function(r){
 | |
| 						delete d.ribbons[r][i]
 | |
| 					})
 | |
| 				})
 | |
| 			}
 | |
| 			//*/
 | |
| 
 | |
| 			if(this.collection == collection){
 | |
| 				this.data
 | |
| 					//.run(hideGIDs)
 | |
| 					.removeGIDs(gids)
 | |
| 					.removeEmptyRibbons()
 | |
| 			}
 | |
| 
 | |
| 			this.collections[collection].data
 | |
| 				//.run(hideGIDs)
 | |
| 				.removeGIDs(gids)
 | |
| 				.removeEmptyRibbons()
 | |
| 		}],
 | |
| 
 | |
| 	// manage serialization and loading...
 | |
| 	// XXX make this reflect the format automatically...
 | |
| 	load: [function(json){
 | |
| 		var that = this
 | |
| 		var collections = {}
 | |
| 		var c = json.collections || {}
 | |
| 
 | |
| 		Object.keys(c).forEach(function(title){
 | |
| 			var data = data.Data
 | |
| 				.fromJSON(c[title].data)
 | |
| 
 | |
| 			// XXX make this reflect the format automatically...
 | |
| 			collections[title] = {
 | |
| 				title: title,
 | |
| 
 | |
| 				data: data,
 | |
| 			}
 | |
| 		})
 | |
| 
 | |
| 		if(Object.keys(collections).length > 0){
 | |
| 			this.collections = collections
 | |
| 		}
 | |
| 	}],
 | |
| 	json: [function(){ return function(res){
 | |
| 		var collections = this.collections
 | |
| 		if(collections){
 | |
| 			res.collections = {}
 | |
| 			Object.keys(this.collections).forEach(function(title){
 | |
| 				var data = collections[title].data.dumpJSON()
 | |
| 				delete data.tags
 | |
| 
 | |
| 				// XXX make this reflect the format automatically...
 | |
| 				res.collections[title] = {
 | |
| 					title: title,
 | |
| 
 | |
| 					data: data, 
 | |
| 				}
 | |
| 			})
 | |
| 		}
 | |
| 	} }],
 | |
| 	clear: [function(){
 | |
| 		this.collection
 | |
| 			&& this.collectionUnloaded('*')
 | |
| 		delete this.collections
 | |
| 		delete this.location.collection
 | |
| 	}],
 | |
| })
 | |
| 
 | |
| 
 | |
| var Collection = 
 | |
| module.Collection = core.ImageGridFeatures.Feature({
 | |
| 	title: '',
 | |
| 	doc: '',
 | |
| 
 | |
| 	tag: 'collections',
 | |
| 	depends: [
 | |
| 		'base',
 | |
| 		'crop',
 | |
| 	],
 | |
| 	suggested: [
 | |
| 		'ui-collections',
 | |
| 		'fs-collections',
 | |
| 	],
 | |
| 
 | |
| 	actions: CollectionActions, 
 | |
| 
 | |
| 	handlers: [
 | |
| 		// maintain the .collection state...
 | |
| 		['uncrop.pre',
 | |
| 			function(){
 | |
| 				var collection = this.collection
 | |
| 				return function(){
 | |
| 					collection != null 
 | |
| 						&& collection != this.data.collection
 | |
| 						&& this.collectionUnloaded(collection) }
 | |
| 			}],
 | |
| 		['collectionLoaded',
 | |
| 			function(){
 | |
| 				console.log('COLLECTION: LOADED')
 | |
| 			}],
 | |
| 		['collectionUnloaded',
 | |
| 			function(_, collection){
 | |
| 				var collection = this.location.collection = this.data.collection
 | |
| 
 | |
| 				// cleanup...
 | |
| 				if(collection == null){
 | |
| 					delete this.location.collection
 | |
| 				}
 | |
| 
 | |
| 				console.log('COLLECTION: UNLOADED')
 | |
| 
 | |
| 				this.data.updateImagePositions()
 | |
| 			}],
 | |
| 	],
 | |
| })
 | |
| 
 | |
| 
 | |
| 
 | |
| //---------------------------------------------------------------------
 | |
| 
 | |
| // XXX do we need a collection button (like crop button?) ???
 | |
| // XXX show collections in image metadata...
 | |
| var UICollectionActions = actions.Actions({
 | |
| 	browseCollections: ['Collections|Crop/$Collec$tions...',
 | |
| 		widgets.makeUIDialog(function(action){
 | |
| 			var that = this
 | |
| 			var to_remove = []
 | |
| 
 | |
| 			return browse.makeLister(null, 
 | |
| 				function(path, make){
 | |
| 					var dialog = this
 | |
| 						.on('update', function(){
 | |
| 							that.collection
 | |
| 								&& dialog.filter(JSON.stringify(that.collection))
 | |
| 									.addClass('highlighted')
 | |
| 						})
 | |
| 
 | |
| 					var collections = Object.keys(that.collections || {})
 | |
| 
 | |
| 					make.EditableList(collections, 
 | |
| 						{
 | |
| 							unique: true,
 | |
| 							to_remove: to_remove,
 | |
| 							itemopen: function(title){
 | |
| 								var gid = that.current
 | |
| 								action ?
 | |
| 									action.call(that, title)
 | |
| 									: that.loadCollection(title)
 | |
| 								that.focusImage(gid)
 | |
| 								dialog.close()
 | |
| 							},
 | |
| 							normalize: function(title){ 
 | |
| 								return title.trim() },
 | |
| 							check: function(title){ 
 | |
| 								return title.length > 0 },
 | |
| 
 | |
| 							itemadded: function(title){
 | |
| 								action ?
 | |
| 									that.newCollection(title)
 | |
| 									: that.saveCollection(title) },
 | |
| 						})
 | |
| 				}, {
 | |
| 					// focus current collection...
 | |
| 					selected: that.collection,
 | |
| 				})
 | |
| 				.close(function(){
 | |
| 					to_remove.forEach(function(title){ 
 | |
| 						that.removeCollection(title) 
 | |
| 					}) 
 | |
| 				})
 | |
| 		})],
 | |
| 	browseImageCollections: ['Image/$Collections...',
 | |
| 		{dialogTitle: 'Image Collections...'},
 | |
| 		widgets.makeUIDialog(function(gid){
 | |
| 			var that = this
 | |
| 			gid = this.data.getImage(gid)
 | |
| 
 | |
| 			var all
 | |
| 			var collections
 | |
| 
 | |
| 			var to_remove
 | |
| 
 | |
| 			return browse.makeLister(null, 
 | |
| 				function(path, make){
 | |
| 					var dialog = this
 | |
| 						.on('update', function(){
 | |
| 							that.collection
 | |
| 								&& dialog.filter(JSON.stringify(that.collection))
 | |
| 									.addClass('highlighted')
 | |
| 						})
 | |
| 
 | |
| 					all = Object.keys(that.collections || {})
 | |
| 
 | |
| 					collections = collections 
 | |
| 						|| that.inCollections(gid || null)
 | |
| 
 | |
| 					// build the disabled list...
 | |
| 					if(!to_remove){
 | |
| 						to_remove = []
 | |
| 						all.forEach(function(title){
 | |
| 							collections.indexOf(title) < 0
 | |
| 								&& to_remove.push(title)
 | |
| 						})
 | |
| 					}
 | |
| 
 | |
| 					all.length > 0 ?
 | |
| 						make.EditableList(all, 
 | |
| 							{
 | |
| 								new_item: false,
 | |
| 								to_remove: to_remove,
 | |
| 								itemopen: function(title){
 | |
| 									var i = to_remove.indexOf(title)
 | |
| 
 | |
| 									i >= 0 ? 
 | |
| 										to_remove.splice(i, 1) 
 | |
| 										: to_remove.push(title)
 | |
| 
 | |
| 									dialog.update()
 | |
| 								},
 | |
| 							})
 | |
| 						: make.Empty()
 | |
| 				})
 | |
| 				.close(function(){
 | |
| 					all.forEach(function(title){
 | |
| 						collections.indexOf(title) < 0
 | |
| 							&& to_remove.indexOf(title) < 0
 | |
| 							&& that.collect(gid, title)
 | |
| 					})
 | |
| 					to_remove.forEach(function(title){ 
 | |
| 						that.uncollect(gid, title)
 | |
| 					}) 
 | |
| 				})
 | |
| 		})],
 | |
| 
 | |
| 	// Collections actions with collection selection...
 | |
| 	// XXX should we warn the user when overwriting???
 | |
| 	saveAsCollection: ['Collections|Crop/$Save as collection...',
 | |
| 		widgets.uiDialog(function(){
 | |
| 			return this.browseCollections(function(title){
 | |
| 				this.saveCollection(title) }) })],
 | |
| 	addToCollection: ['Collections|Crop|Image/Add $image to collection...',
 | |
| 		widgets.uiDialog(function(gids){
 | |
| 			return this.browseCollections(function(title){
 | |
| 				this.collect(gids || this.current, title) }) })],
 | |
| 	addLoadedToCollection: ['Collections|Crop/$Add loaded images to collection...',
 | |
| 		widgets.uiDialog(function(){ return this.addToCollection('loaded') })],
 | |
| 	joinToCollection: ['Collections|Crop/$Merge view to collection...',
 | |
| 		widgets.uiDialog(function(){
 | |
| 			return this.browseCollections(function(title){
 | |
| 				this.joinCollect(title) }) })],
 | |
| 
 | |
| 	/*/ XXX this is not used by metadata yet...
 | |
| 	metadataSection: ['- Image/',
 | |
| 		function(gid, make){
 | |
| 		}],
 | |
| 	//*/
 | |
| })
 | |
| 
 | |
| 
 | |
| var UICollection = 
 | |
| module.UICollection = core.ImageGridFeatures.Feature({
 | |
| 	title: '',
 | |
| 	doc: '',
 | |
| 
 | |
| 	tag: 'ui-collections',
 | |
| 	depends: [
 | |
| 		'ui',
 | |
| 		'collections',
 | |
| 	],
 | |
| 
 | |
| 	actions: UICollectionActions, 
 | |
| 
 | |
| 	handlers: [
 | |
| 		['uncollect',
 | |
| 			function(_, gids, collection){
 | |
| 				(collection == null || this.collection == collection)
 | |
| 					&& this.reload(true)
 | |
| 			}],
 | |
| 	],
 | |
| })
 | |
| 
 | |
| 
 | |
| 
 | |
| //---------------------------------------------------------------------
 | |
| // XXX Things to try/do:
 | |
| // 		- save collection on exit/write (?)
 | |
| // 		- lazy load collections (load list, lazy-load data)
 | |
| // 		- load directories as collections...
 | |
| // 		- export collections to directories...
 | |
| // 		- collection history...
 | |
| 
 | |
| var FileSystemCollectionActions = actions.Actions({
 | |
| 
 | |
| 	// Format:
 | |
| 	// 	{
 | |
| 	// 		path: <string>,
 | |
| 	// 		...
 | |
| 	// 	}
 | |
| 	collections: null,
 | |
| 
 | |
| 	collectionPathLoader: ['- Collections/',
 | |
| 		{collectionFormat: 'path'},
 | |
| 		function(data, loader){ 
 | |
| 			// XXX
 | |
| 		}],
 | |
| 
 | |
| 	importCollectionsFromPath: ['- Collections|File/Import collections from path',
 | |
| 		function(path){
 | |
| 			// XXX
 | |
| 		}],
 | |
| })
 | |
| 
 | |
| 
 | |
| // XXX manage format...
 | |
| // XXX manage changes...
 | |
| var FileSystemCollection = 
 | |
| module.FileSystemCollection = core.ImageGridFeatures.Feature({
 | |
| 	title: '',
 | |
| 	doc: '',
 | |
| 
 | |
| 	tag: 'fs-collections',
 | |
| 	depends: [
 | |
| 		'index-format',
 | |
| 		'fs',
 | |
| 		'collections',
 | |
| 	],
 | |
| 
 | |
| 	actions: FileSystemCollectionActions,
 | |
| 
 | |
| 	handlers: [
 | |
| 		// XXX maintain changes...
 | |
| 		// XXX
 | |
| 		[[
 | |
| 			'collect',
 | |
| 			'joinCollect',
 | |
| 			'uncollect',
 | |
| 
 | |
| 			'saveCollection',
 | |
| 
 | |
| 			'removeCollection',
 | |
| 		], 
 | |
| 			function(){
 | |
| 				// XXX mark changed collections...
 | |
| 				// XXX added/removed collection -> mark collection index as changed...
 | |
| 			}],
 | |
| 
 | |
| 		// XXX handle removed collections -- move to trash (???)
 | |
| 		// 		...might be a good idea to add something like index gc API...
 | |
| 		['prepareIndexForWrite',
 | |
| 			function(res, _, full){
 | |
| 				var changed = full == true 
 | |
| 					|| res.changes === true
 | |
| 					|| res.changes.collections
 | |
| 
 | |
| 				if(changed && res.raw.collections){
 | |
| 					// select the actual changed collection list...
 | |
| 					changed = changed === true ? 
 | |
| 						Object.keys(res.raw.collections)
 | |
| 						: changed
 | |
| 
 | |
| 					// collection index...
 | |
| 					res.index['collection-index'] = Object.keys(this.collections)
 | |
| 
 | |
| 					Object.keys(changed)
 | |
| 						// skip the raw field...
 | |
| 						.filter(function(k){ return changed.indexOf(k) >= 0 })
 | |
| 						.forEach(function(k){
 | |
| 							// XXX use collection gid...
 | |
| 							res.index['collections/' + k] = res.raw.collections[k]
 | |
| 						})
 | |
| 				}
 | |
| 			}],
 | |
| 		['prepareJSONForLoad',
 | |
| 			function(res, json){
 | |
| 				// XXX
 | |
| 			}],
 | |
| 	],
 | |
| })
 | |
| 
 | |
| 
 | |
| 
 | |
| //---------------------------------------------------------------------
 | |
| // XXX localstorage-collections (???)
 | |
| 
 | |
| 
 | |
| 
 | |
| 
 | |
| /**********************************************************************
 | |
| * vim:set ts=4 sw=4 :                               */ return module })
 |