358 lines
8.8 KiB
JavaScript
Raw Normal View History

/**********************************************************************
*
* 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, callback){
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,
// XXX should we focus image by default here???
callback || function(action){ action != null && this.focusImage() })
}
/*********************************************************************/
var ImageGridFeatures =
module.ImageGridFeatures = Object.create(features.FeatureSet)
// nw or node...
if(typeof(process) != 'undefined'){
// nw.js 0.13+
if(typeof(nw) != 'undefined'){
ImageGridFeatures.runtime = 'nw'
// node...
} else {
ImageGridFeatures.runtime = 'node'
}
// 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'
}
/*********************************************************************/
// 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,
})
//---------------------------------------------------------------------
//
// 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() }],
],
})
/**********************************************************************
* vim:set ts=4 sw=4 : */
return module })