mirror of
				https://github.com/flynx/ImageGrid.git
				synced 2025-10-31 03:10:07 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			439 lines
		
	
	
		
			10 KiB
		
	
	
	
		
			JavaScript
		
	
	
		
			Executable File
		
	
	
	
	
			
		
		
	
	
			439 lines
		
	
	
		
			10 KiB
		
	
	
	
		
			JavaScript
		
	
	
		
			Executable File
		
	
	
	
	
| /**********************************************************************
 | |
| * 
 | |
| * Core features that setup the life-cycle and the base interfaces for 
 | |
| * features to use...
 | |
| *
 | |
| *
 | |
| **********************************************************************/
 | |
| 
 | |
| define(function(require){ var module = {}
 | |
| 
 | |
| //var DEBUG = DEBUG != null ? DEBUG : true
 | |
| 
 | |
| var actions = require('lib/actions')
 | |
| var features = require('lib/features')
 | |
| var toggler = require('lib/toggler')
 | |
| 
 | |
| 
 | |
| 
 | |
| /*********************************************************************/
 | |
| 
 | |
| // NOTE: if not state is set this assumes that the first state is the 
 | |
| // 		default...
 | |
| var makeConfigToggler = 
 | |
| module.makeConfigToggler = 
 | |
| function(attr, states, a, b){
 | |
| 
 | |
| 	var pre = a
 | |
| 	var post = b || function(action){ action != null && this.focusImage() }
 | |
| 
 | |
| 	return toggler.Toggler(null,
 | |
| 		function(_, action){
 | |
| 			var lst = states.constructor === Array ? states : states.call(this)
 | |
| 
 | |
| 			//console.log('action', action)
 | |
| 
 | |
| 			if(action == null){
 | |
| 				return this.config[attr] || lst[lst.indexOf('none')] || lst[0]
 | |
| 
 | |
| 			} else {
 | |
| 				this.config[attr] = action
 | |
| 				//this.focusImage()
 | |
| 			}
 | |
| 		},
 | |
| 		states, pre, post)
 | |
| }
 | |
| 
 | |
| 
 | |
| 
 | |
| /*********************************************************************/
 | |
| 
 | |
| // Root ImageGrid.viewer object...
 | |
| //
 | |
| var ImageGridFeatures =
 | |
| module.ImageGridFeatures = Object.create(features.FeatureSet)
 | |
| 
 | |
| 
 | |
| //---------------------------------------------------------------------
 | |
| // Setup runtime info...
 | |
| 
 | |
| // nw or node...
 | |
| if(typeof(process) != 'undefined'){
 | |
| 
 | |
| 	// nw.js 0.13+
 | |
| 	if(typeof(nw) != 'undefined'){
 | |
| 		ImageGridFeatures.runtime = 'nw'
 | |
| 
 | |
| 		// NOTE: jli is patching the Date object and with two separate 
 | |
| 		// 		instances we'll need to sync things up...
 | |
| 		// XXX HACK: if node and chrome Date implementations ever 
 | |
| 		// 		significantly diverge this will break things + this is 
 | |
| 		// 		a potential data leak between contexts...
 | |
| 		//global.Date = window.Date
 | |
| 
 | |
| 		// XXX this is less of a hack but it is still an implicit
 | |
| 		patchDate(global.Date)
 | |
| 		patchDate(window.Date)
 | |
| 
 | |
| 	// node...
 | |
| 	} else {
 | |
| 		ImageGridFeatures.runtime = 'node'
 | |
| 
 | |
| 		// XXX patch Date...
 | |
| 		// XXX this will not work directly as we will need to explicitly
 | |
| 		// 		require jli...
 | |
| 		//patchDate(global.Date)
 | |
| 	}
 | |
| 
 | |
| // browser...
 | |
| // NOTE: we're avoiding detecting browser specifics for as long as possible,
 | |
| // 		this will minimize the headaches of supporting several non-standard
 | |
| // 		versions of code...
 | |
| } else if(typeof(window) != 'undefined'){
 | |
| 	ImageGridFeatures.runtime = 'browser'
 | |
| 
 | |
| // unknown...
 | |
| // XXX do we need to detect chrome app???
 | |
| } else {
 | |
| 	ImageGridFeatures.runtime = 'unknown'
 | |
| }
 | |
| 
 | |
| 
 | |
| 
 | |
| /*********************************************************************/
 | |
| // System life-cycle...
 | |
| 
 | |
| // XXX should this be a generic library thing???
 | |
| // XXX should his have state???
 | |
| // 		...if so, should this be a toggler???
 | |
| var LifeCycleActions = actions.Actions({
 | |
| 	start: ['- System/', 
 | |
| 		function(){
 | |
| 			var that = this
 | |
| 			this.logger && this.logger.emit('start')
 | |
| 
 | |
| 			// NOTE: jQuery currently provides no way to check if an event
 | |
| 			// 		is bound so we'll need to keep track manually...
 | |
| 			if(this.__stop_handler == null){
 | |
| 				var stop = this.__stop_handler = function(){ that.stop() }
 | |
| 
 | |
| 			} else {
 | |
| 				return
 | |
| 			}
 | |
| 
 | |
| 			// set the runtime...
 | |
| 			var runtime = this.runtime = ImageGridFeatures.runtime
 | |
| 
 | |
| 			// nw.js...
 | |
| 			if(runtime == 'nw'){
 | |
| 				// this handles both reload and close...
 | |
| 				$(window).on('beforeunload', stop)
 | |
| 
 | |
| 				// NOTE: we are using both events as some of them do not
 | |
| 				// 		get triggered in specific conditions and some do,
 | |
| 				// 		for example, this gets triggered when the window's
 | |
| 				// 		'X' is clicked while does not on reload...
 | |
| 				this.__nw_stop_handler = function(){
 | |
| 					var w = this
 | |
| 					try{
 | |
| 						that
 | |
| 							// wait till ALL the handlers finish before 
 | |
| 							// exiting...
 | |
| 							.on('stop.post', function(){
 | |
| 								// XXX might be broken in nw13 -- test!!!
 | |
| 								//w.close(true)
 | |
| 								nw.App.quit()
 | |
| 							})
 | |
| 							.stop()
 | |
| 
 | |
| 					// in case something breaks exit...
 | |
| 					// XXX not sure if this is correct...
 | |
| 					} catch(e){
 | |
| 						this.close(true)
 | |
| 					}
 | |
| 				}
 | |
| 				nw.Window.get().on('close', this.__nw_stop_handler)
 | |
| 
 | |
| 			// node.js...
 | |
| 			} else if(runtime == 'node'){
 | |
| 				process.on('exit', stop)
 | |
| 
 | |
| 			// browser...
 | |
| 			} else if(runtime == 'browser'){
 | |
| 				$(window).on('beforeunload', stop)
 | |
| 
 | |
| 			// other...
 | |
| 			} else {
 | |
| 				// XXX
 | |
| 			}
 | |
| 		}],
 | |
| 	// unbind events...
 | |
| 	stop: ['- System/', 
 | |
| 		function(){
 | |
| 			// browser & nw...
 | |
| 			if(this.__stop_handler 
 | |
| 					&& (this.runtime == 'browser' || this.runtime == 'nw')){
 | |
| 				$(window).off('beforeunload', this.__stop_handler)
 | |
| 			}
 | |
| 
 | |
| 			// nw...
 | |
| 			if(this.__nw_stop_handler && this.runtime == 'nw'){
 | |
| 				//nw.Window.get().off('close', this.__nw_stop_handler)
 | |
| 				delete this.__nw_stop_handler
 | |
| 			}
 | |
| 
 | |
| 			// node...
 | |
| 			/* XXX there's no process.off(...)
 | |
| 			if(this.__stop_handler && this.runtime == 'node'){
 | |
| 				process.off('exit', this.__stop_handler)
 | |
| 			}
 | |
| 			*/
 | |
| 
 | |
| 			delete this.__stop_handler
 | |
| 
 | |
| 			this.logger && this.logger.emit('stop')
 | |
| 		}],
 | |
| 
 | |
| 	/*
 | |
| 	// XXX need a clear protocol for this...
 | |
| 	// 		something like:
 | |
| 	// 			- clear state
 | |
| 	// 			- load state
 | |
| 	reset: ['System/',
 | |
| 		function(){
 | |
| 		}],
 | |
| 	*/
 | |
| })
 | |
| 
 | |
| var LifeCycle = 
 | |
| module.LifeCycle = ImageGridFeatures.Feature({
 | |
| 	title: '',
 | |
| 	doc: '',
 | |
| 
 | |
| 	tag: 'lifecycle',
 | |
| 	priority: 'high',
 | |
| 
 | |
| 	actions: LifeCycleActions,
 | |
| })
 | |
| 
 | |
| 
 | |
| 
 | |
| //---------------------------------------------------------------------
 | |
| // Workspace...
 | |
| //
 | |
| // Basic protocol:
 | |
| // 	A participating feature should:
 | |
| // 	- react to .saveWorkspace(..) by saving it's relevant state data to the 
 | |
| // 		object returned by the .saveWorkspace() action.
 | |
| // 		NOTE: it is recommended that a feature save its relevant .config
 | |
| // 			data as-is.
 | |
| // 		NOTE: no other action or state change should be triggered by this.
 | |
| // 	- react to .loadWorkspace(..) by loading it's state from the returned
 | |
| // 		object...
 | |
| // 		NOTE: this can be active, i.e. a feature may call actions when 
 | |
| // 			handling this.
 | |
| // 	- react to .toggleChrome(..) and switch on and off the chrome 
 | |
| // 		visibility... (XXX)
 | |
| //
 | |
| //
 | |
| 
 | |
| 
 | |
| // Helpers...
 | |
| var makeWorkspaceConfigWriter =
 | |
| module.makeWorkspaceConfigWriter = function(keys, callback){
 | |
| 	return function(workspace){
 | |
| 		var that = this
 | |
| 
 | |
| 		keys = typeof(keys) == typeof(function(){}) ? keys() : keys
 | |
| 
 | |
| 		// store statusbar data...
 | |
| 		keys.forEach(function(key){
 | |
| 			workspace[key] = JSON.parse(JSON.stringify(that.config[key]))
 | |
| 		})
 | |
| 
 | |
| 		callback && callback.call(this, workspace)
 | |
| 	}
 | |
| }
 | |
| 
 | |
| // XXX should this delete a prop if it's not in the loading workspace???
 | |
| var makeWorkspaceConfigLoader =
 | |
| module.makeWorkspaceConfigLoader = function(keys, callback){
 | |
| 	return function(workspace){
 | |
| 		var that = this
 | |
| 
 | |
| 		keys = typeof(keys) == typeof(function(){}) ? keys() : keys
 | |
| 
 | |
| 		// load statusbar data...
 | |
| 		keys.forEach(function(key){
 | |
| 			// the key exists...
 | |
| 			if(key in workspace){
 | |
| 				that.config[key] = JSON.parse(JSON.stringify(workspace[key]))
 | |
| 
 | |
| 			// no key set...
 | |
| 			// XXX is this the right way to go???
 | |
| 			} else {
 | |
| 				delete that.config[key]
 | |
| 			}
 | |
| 		})
 | |
| 
 | |
| 		callback && callback.call(this, workspace)
 | |
| 	}
 | |
| }
 | |
| 
 | |
| 
 | |
| 
 | |
| // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 
 | |
| 
 | |
| var WorkspaceActions = actions.Actions({
 | |
| 	config: {
 | |
| 		'workspace': 'default',
 | |
| 		'workspaces': {},
 | |
| 	},
 | |
| 
 | |
| 	get workspace(){
 | |
| 		return this.config.workspace
 | |
| 	},
 | |
| 	set workspace(value){
 | |
| 		this.loadWorkspace(value)
 | |
| 	},
 | |
| 
 | |
| 	get workspaces(){
 | |
| 		return this.config.workspaces
 | |
| 	},
 | |
| 
 | |
| 
 | |
| 	getWorkspace: ['- Workspace/',
 | |
| 		function(){ return this.saveWorkspace(null) }],
 | |
| 
 | |
| 	// NOTE: these are mainly triggers for other features to save/load
 | |
| 	// 		their specific states...
 | |
| 	// NOTE: handlers should only set data on the workspace object passively,
 | |
| 	// 		no activity is recommended.
 | |
| 	// NOTE: if null is passed this will only get the data, but will 
 | |
| 	// 		save nothing. this us useful for introspection and temporary
 | |
| 	// 		context storage.
 | |
| 	//
 | |
| 	// XXX for some reason this does not get saved with .config...
 | |
| 	saveWorkspace: ['Workspace/Save Workspace',
 | |
| 		function(name){
 | |
| 			if(!this.config.hasOwnProperty('workspaces')){
 | |
| 				this.config['workspaces'] = JSON.parse(JSON.stringify(this.config['workspaces']))
 | |
| 			}
 | |
| 
 | |
| 			var res = {}
 | |
| 
 | |
| 			if(name !== null){
 | |
| 				this.config['workspaces'][name || this.config.workspace] = res
 | |
| 			}
 | |
| 
 | |
| 			return res
 | |
| 		}],
 | |
| 	// NOTE: merging the state data is the responsibility of the feature
 | |
| 	// 		...this is done so as not to restrict the feature to one 
 | |
| 	// 		specific way to do stuff...
 | |
| 	loadWorkspace: ['Workspace/Load Workspace',
 | |
| 		function(name){
 | |
| 			name = name || this.config.workspace
 | |
| 
 | |
| 			// get a workspace by name and load it...
 | |
| 			if(typeof(name) == typeof('str')){
 | |
| 				this.config.workspace = name
 | |
| 
 | |
| 				return this.workspaces[name] || {}
 | |
| 
 | |
| 			// we got the workspace object...
 | |
| 			} else {
 | |
| 				return name
 | |
| 			}
 | |
| 		}],
 | |
| 
 | |
| 	// NOTE: this will not save the current workspace...
 | |
| 	toggleWorkspace: ['Workspace/Toggle Workspace',
 | |
| 		makeConfigToggler('workspace',
 | |
| 			function(){ return Object.keys(this.config['workspaces']) },
 | |
| 			function(state){ this.loadWorkspace(state) })],
 | |
| })
 | |
| 
 | |
| 
 | |
| var Workspace = 
 | |
| module.Workspace = ImageGridFeatures.Feature({
 | |
| 	title: '',
 | |
| 
 | |
| 	tag: 'workspace',
 | |
| 
 | |
| 	depends: [
 | |
| 		'lifecycle',
 | |
| 	],
 | |
| 
 | |
| 	actions: WorkspaceActions,
 | |
| 
 | |
| 	handlers: [
 | |
| 		['stop', 
 | |
| 			function(){ this.saveWorkspace() }],
 | |
| 	],
 | |
| })
 | |
| 
 | |
| 
 | |
| 
 | |
| //---------------------------------------------------------------------
 | |
| // Tasks...
 | |
| // XXX should this be a separate module???
 | |
| 
 | |
| var tasks = require('lib/tasks')
 | |
| 
 | |
| // XXX see if a protocol can be practical here to:
 | |
| // 		- serialize/restore jobs
 | |
| // 		- ...
 | |
| var TaskActions = actions.Actions({
 | |
| 	config: {
 | |
| 	},
 | |
| 
 | |
| 	get jobs(){
 | |
| 		return this.__jobs
 | |
| 	},
 | |
| 
 | |
| 	getJob: ['- Jobs/',
 | |
| 		function(name){
 | |
| 			name = name || this.data.newGid()
 | |
| 
 | |
| 			// get/init task dict...
 | |
| 			var t = this.__jobs = this.__jobs || {}
 | |
| 			// get/init task...
 | |
| 			var job = t[name] = t[name] || tasks.Queue()
 | |
| 			job.name = name
 | |
| 
 | |
| 			return job
 | |
| 		}],
 | |
| 
 | |
| 	// XXX stop
 | |
| })
 | |
| 
 | |
| 
 | |
| var Tasks = 
 | |
| module.Tasks = ImageGridFeatures.Feature({
 | |
| 	title: '',
 | |
| 
 | |
| 	tag: 'tasks',
 | |
| 
 | |
| 	depends: [ ],
 | |
| 
 | |
| 	actions: TaskActions,
 | |
| 
 | |
| 	handlers: [
 | |
| 		['start', 
 | |
| 			function(){ 
 | |
| 				// XXX prepare for recovery and recover...
 | |
| 			}],
 | |
| 		['stop', 
 | |
| 			function(){ 
 | |
| 				// XXX stop tasks and prepare for recovery...
 | |
| 			}],
 | |
| 	],
 | |
| })
 | |
| 
 | |
| 
 | |
| 
 | |
| /**********************************************************************
 | |
| * vim:set ts=4 sw=4 :                                                */
 | |
| return module })
 |