565 lines
18 KiB
JavaScript
565 lines
18 KiB
JavaScript
|
(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.')
|
||
|
}
|
||
|
})();
|