web-apps/apps/common/main/lib/core/application.js

565 lines
18 KiB
JavaScript
Raw Normal View History

2016-03-11 00:48:53 +00:00
(function(){
var resolveNamespace = function(className, root) {
var parts = className.split('.'),
current = root || window;
for(var a = 0, b = parts.length; a < b; a++) {
current = current[parts[a]] || {};
}
return current;
};
/**
* @class Backbone.Application
* @cfg {Object} options The list of options available within Application
*/
var Application = function(options) {
_.extend(this, options || {});
this.eventbus = new EventBus({application: this});
this.createApplicationNamespace();
this.initialize.apply(this, arguments);
if (this.autoCreate !== false)
$($.proxy(this.onReady, this));
};
_.extend(Application.prototype, {
/**
* @cfg {Object} nameSpace (required)
* Define the application namespace
*/
nameSpace: 'Application',
models: {},
collections: {},
controllers: {},
/**
* @cfg allocationMap Define the inner structure of our application object
* @cfg allocationMap.model The key for models map
* @cfg allocationMap.collection The key for collections map
* @cfg allocationMap.controller The key for controllers map
* @cfg allocationMap.view The key for views map
*/
allocationMap: {
model: 'Models',
collection: 'Collections',
controller: 'Controllers',
view: 'Views'
},
/**
* Function to create inner structure of the application using {@link #allocationMap allocationMap} config
*/
createApplicationNamespace: function() {
var nameSpace = window;
// create global reference in the defined namespace
if(this.nameSpace) {
// if it wasn't already defined, create it
if(typeof nameSpace[this.nameSpace] == 'undefined') {
nameSpace[this.nameSpace] = {}
}
}
// let's have a link to the application namespace
// this way we will be able to get all references to Models, Collections and Controllers
// using given namespace
nameSpace[this.nameSpace] = this;
_.each(this.allocationMap, function(name, key) {
this[name] = this[name] || {};
}, this);
},
/**
* Function that will be called during application instance creation
*/
initialize: function() {},
/**
* Called when DOM is ready. This is global callback is used to:
* * Used to initialize controllers and execute {@link Backbone.Controller#onLaunch onLaunch} callback
* * Execute {@link Backbone.Application#launch launch} callback
*/
onReady: function() {
this.start();
},
start: function() {
// initialize controllers
this.initializeControllers(this.controllers || {});
// call to controller.onLaunch callback
this.launchControllers();
// call application.launch callback
this.launch.call(this);
},
/**
* Function that will convert string identifier into the instance reference
* @param {String} type Type of instance that should be resolved. See {@link #allocationMap} for valid values
* @param {String[]} classes The list of class references
* @return {Object} The objects map
*/
getClasseRefs: function(type, classes) {
var hashMap = {},
allocationMap = this.allocationMap[type],
root = this[allocationMap];
_.each(classes, function(cls) {
hashMap[cls] = resolveNamespace(cls, (cls.indexOf('.') > -1) ? window : root);
}, this);
return hashMap;
},
/**
* Function that will loop through all application controllers and create their instances
* Additionally, read the list of models and collections from each controller and save the reference within application
*/
initializeControllers: function(controllers) {
this.controllers = {};
_.each(controllers, function(ctrl) {
var root = (ctrl.indexOf('.') > -1) ? window : this[this.allocationMap.controller],
classReference = resolveNamespace(ctrl, root),
id = ctrl.split('.').pop();
var controller = new classReference({
id: ctrl,
application: this
});
controller.views = this.getClasseRefs('view', controller.views || []);
_.extend(this.models, this.getClasseRefs('model', controller.models || []));
_.extend(this.collections, this.getClasseRefs('collection', controller.collections || {}));
this.buildCollections();
this.controllers[ctrl] = controller;
}, this);
},
/**
* Launch all controllers using {@link Backbone.Controller#onLaunch callback}
*/
launchControllers: function() {
_.each(this.controllers, function(ctrl, id) {
ctrl.onLaunch(this);
}, this);
},
/**
* Called during application launch
* @template
*/
launch: function() {},
/**
* Function to add event listeners to the {@link #Backbone.EventBus EventBus}
*/
addListeners: function(listeners, controller) {
this.eventbus.addListeners(listeners, controller)
},
/**
* Getter to retreive link to the particular controller instance by name
* @param {String} name
* @return {Backbone.Controller} The controller instance
*/
getController: function(name) {
return this.controllers[name];
},
/**
* Getter to retrieve link to the particular model instance by name
* If model instance isn't created, create it
* @param {String} name
* @return {Backbone.Model} The model instance
*/
getModel: function(name) {
this._modelsCache = this._modelsCache || {};
var model = this._modelsCache[name],
modelClass = this.getModelConstructor(name);
if(!model && modelClass) {
model = this.createModel(name);
this._modelsCache[name] = model;
}
return model || null;
},
/**
* Getter to retrieve link to the particular model constructor by name
* @param {String} name
* @return {Backbone.Model} The model constructor
*/
getModelConstructor: function(name) {
return this.models[name];
},
/**
* Function to create new model instance
* @param {String} name The name of the model that needs to be created
* @param {Object} [options] The list of option that should be passed to the model constructor
*/
createModel: function(name, options) {
var modelClass = this.getModelConstructor(name),
model = null;
if (modelClass)
model = new modelClass(_.extend(options || {}));
return model;
},
/**
* Getter to retrieve link to the particular collection instance by name
* If collection instance isn't created, create it
* @param {String} name
* @return {Backbone.Collection} The collection instance
*/
getCollection: function(name) {
this._collectionsCache = this._collectionsCache || {};
var collection = this._collectionsCache[name],
collectionClass = this.getCollectionConstructor(name);
if(!collection && collectionClass) {
collection = this.createCollection(name);
this._collectionsCache[name] = collection;
}
return collection || null;
},
/**
* Getter to retrieve link to the particular collection constructor
* @param {String} name
* @return {Backbone.Collection} The collection constructor
*/
getCollectionConstructor: function(name) {
return this.collections[name];
},
/**
* Function to create new collection instance
* @param {String} name The name of the collection that needs to be created
*/
createCollection: function(name) {
var collectionClass = this.getCollectionConstructor(name),
collection = null;
if (collectionClass)
collection = new collectionClass();
return collection;
},
/**
* Function that will loop throught the list of collection constructors and create instances
*/
buildCollections: function() {
_.each(this.collections, function(collection, alias) {
this.getCollection(alias);
}, this);
}
});
// Since we are using Backbone let's make sure that there are no conflicts in namespaces
if(typeof Backbone.Application == 'undefined') {
Backbone.Application = Application;
/**
* @method extend
* Method to create new Backbone.Application class
* @static
*/
Backbone.Application.extend = Backbone.Model.extend;
}
else {
throw ('Native Backbone.Application instance already defined.')
}
/**
* @class Backbone.Controller
* @cfg {Object} options The list of options available within Controller
*/
var Controller = function(options) {
_.extend(this, options || {});
this.initialize.apply(this, arguments);
};
_.extend(Controller.prototype, {
name: null,
views: {},
models: {},
collections: {},
initialize: function(options) {
},
/**
* Add new listener to the application event bus
* Delegate to {@link Backbone.Application#addListeners addListeners} callback
*/
addListeners: function(listeners) {
this.getApplication().addListeners(listeners, this);
},
/**
* Called after application launch
* @template
*/
onLaunch: function(application) {
},
/**
* Getter that will return the reference to the application instance
*/
getApplication: function() {
return this.application;
},
/**
* Getter that will return the reference to the view instance
*/
getView: function(name) {
return this._viewsCache[name];
},
/**
* Getter that will return the reference to the view constructor by name
* @param {String} name
* @return {Backbone.View} The view constructor
*/
getViewConstructor: function(name) {
return this.views[name];
},
/**
* Function to create a new view instance
* All views are cached within _viewsCache hash map
* @param {String} name
* @param {Object} options Options to be passed within view constructor
* @return {Backbone.View} The view instance
*/
createView: function(name, options) {
var view = this.getViewConstructor(name),
viewOptions = _.extend(options || {}, {
alias: name
});
this._viewsCache = this._viewsCache || {};
this._viewsCache[name] = new view(viewOptions);
this._viewsCache[name].options = _.extend({}, viewOptions);
return this._viewsCache[name];
},
/**
* Method to get model instance reference by name
* Delegate to {@link Backbone.Application#getModel getModel} method
*/
getModel: function(name) {
return this.application.getModel(name);
},
/**
* Method to get model constructor reference by name
* Delegate to {@link Backbone.Application#getModelConstructor getModelConstructor} method
*/
getModelConstructor: function(name) {
return this.application.getModelConstructor(name);
},
/**
* Method to create model instance by name
* Delegate to {@link Backbone.Application#createModel createModel} method
*/
createModel: function(name, options) {
return this.application.createModel(name)
},
/**
* Delegate method to get collection instance reference by name
* Delegate to {@link Backbone.Application#getCollection getCollection} method
*/
getCollection: function(name) {
return this.application.getCollection(name);
},
/**
* Delegate method to get collection constructor reference by name
* Delegate to {@link Backbone.Application#getCollectionConstructor getCollectionConstructor} method
*/
getCollectionConstructor: function(name) {
return this.application.getCollectionConstructor(name);
},
/**
* Delegate method to create collection instance
* Delegate to {@link Backbone.Application#createCollection createCollection} method
*/
createCollection: function(name) {
return this.application.createCollection(name);
},
/**
* Method to fire cross-controller event
* Delegate to {@link Backbone.Application#fireEvent fireEvent} method
*/
fireEvent: function(selector, event, args) {
this.application.eventbus.fireEvent(selector, event, args);
},
/**
* Method to bind events from controlling view to controller
* probably isn't safe, testing needed
*/
bindViewEvents: function(view, events) {
this.unbindViewEvents(view);
events = _.isFunction(events) ? events.call(this) : events;
for (var key in events) {
var method = events[key];
if (!_.isFunction(method)) method = this[events[key]];
var match = key.match(/^(\S+)\s*(.*)$/);
var eventName = match[1], selector = match[2];
method = _.bind(method, this);
eventName += '.bindViewEvents' + view.cid;
view.$el.on(eventName, selector, method);
}
return this;
},
/**
* Method to unbind events from view to controller
*
*/
unbindViewEvents: function(view) {
view.$el.off('.bindViewEvents' + view.cid);
return this;
}
});
if(typeof Backbone.Controller == 'undefined') {
Backbone.Controller = Controller;
/**
* @method extend
* Method to create new Backbone.Controller class
* @static
*/
Backbone.Controller.extend = Backbone.Model.extend;
}
else {
throw ('Native Backbone.Controller instance already defined.')
}
/**
* @class Backbone.EventBus
* @cfg {Object} options The list of options available within Controller
* @private
*/
var EventBus = function(options) {
var me = this;
_.extend(this, options || {});
_.extend(Backbone.View.prototype, {
alias: null,
hidden: false,
getAlias: function() {
return this.options.alias;
},
/**
* Instead of calling View.trigger lets use custom function
* It will notify the EventBus about new event
*/
fireEvent: function(event, args) {
this.trigger.apply(this, arguments);
me.fireEvent(this.getAlias(), event, args);
},
hide: function() {
this.$el.hide();
this.hidden = true;
},
show: function() {
this.$el.show();
this.hidden = false;
}
});
};
_.extend(EventBus.prototype, {
pool: {},
/**
* Add new listeners to the event bus
*/
addListeners: function(selectors, controller) {
this.pool[controller.id] = this.pool[controller.id] || {};
var pool = this.pool[controller.id];
if(_.isArray(selectors)) {
_.each(selectors, function(selector) {
this.addListeners(selector, controller);
}, this)
}
else if(_.isObject(selectors)) {
_.each(selectors, function(listeners, selector) {
_.each(listeners, function(listener, event) {
pool[selector] = pool[selector] || {};
pool[selector][event] = pool[selector][event] || [];
pool[selector][event].push(listener);
}, this);
}, this)
}
},
/**
* Execute event listener by given selector and event name
*/
fireEvent: function(selector, event, args) {
var application = this.getApplication();
_.each(this.pool, function(eventsPoolByAlias, controllerId) {
var events = eventsPoolByAlias[selector];
if(events) {
var listeners = events[event],
controller = application.getController(controllerId);
_.each(listeners, function(fn) {
fn.apply(controller, args);
});
}
}, this);
},
getApplication: function() {
return this.application;
}
});
// Since we are using Backbone let's make sure that there are no conflicts in namespaces
if(typeof Backbone.EventBus == 'undefined') {
Backbone.EventBus = EventBus;
}
else {
throw ('Native Backbone.Application instance already defined.')
}
})();