wigjs
Version:
Minimalistic, scalable, extensible, dependency-less Front-end factory for HTML5 applications
287 lines (246 loc) • 7.86 kB
JavaScript
/**
* @class
* @param {object} options
* @property {string} [id] - user defined or internal identifier
* @property {string} [css]
* @property {Node} [node]
*/
var View = wig.View = Class.extend({
constructor: function View(context) {
context = (context || {});
// assign the ID and register the View
this._ID = (context.id || env.generateID('v'));
env.viewRegistry.registerView(this);
this.css = (context.css || '');
this.node = (context.node || document.createElement(this.tagName));
this.context = {};
this.attached = false;
env.viewHelper.initializeWithContext(this, context);
},
// ////////// //
// Properties //
// ////////// //
// strings
tagName: 'div',
className: 'View',
// objects
defaults: {},
renderMap: {},
events: {},
/**
* @type {View}
*/
View: View,
/**
* @type {object|string[]}
*/
expects: {},
/**
* @type {string|string[]|function}
*/
template: '',
// //// //
// View //
// //// //
get: function (key) {
return (this.context[key] || this.defaults[key]);
},
getID: function () {
return this._ID;
},
getNode: function () {
return this.node;
},
/**
* Updates the View's context object - does not update the View itself
* @param {object} newContext
*/
set: function (newContext) {
var overrides;
if (newContext && typeof newContext === 'object') {
overrides = extend({}, this.defaults, this.context, newContext);
extend(this.context, (this.parseContext(overrides) || overrides));
}
},
/**
* Sets the View for another Element and reinitializes it.
* @param {Element} node
*/
setNode: function (node) {
if (node) {
this.node = node;
this.initialize();
}
},
/**
* Finds an Element within the View's DOM Element.
* @param {string} selector
* @returns {Node}
*/
find: function (selector) {
var node = this.getNode();
if (!selector) {
return node;
}
return env.getElement(node, selector);
},
/**
* Updates (rerenders) the View and its children.
* @param {object} [context] - context updates
*/
update: function (context) {
env.viewHelper.notifyDetach(this);
this.set(context);
env.viewManager.updateView(this);
env.viewHelper.notifyAttach(this);
},
// Removes (destroys) the children.
empty: function () {
env.viewManager.getChildViews(this.getID())
.forEach(this.removeView, this);
},
// Removes (destroys) the View and its children from the DOM.
remove: function () {
env.viewManager.removeViewFromParent(this);
},
// ///// ////////// //
// Child operations //
// ///// ////////// //
/**
* Creates and adds the child view specified by the child view's _ID attribute.
* @param {Function} [ViewClass] - child View type
* @param {object} [childOptions] - options to create the child with
* @returns {View}
*/
addView: function (ViewClass, childOptions) {
var parentID = this.getID(),
contextRegistry = env.viewRegistry.getContextRegistryForView(parentID),
oldChildContext, newChildContext,
options, childID, childView;
// resolve arguments
if (ViewClass && typeof ViewClass === 'object') {
childOptions = ViewClass;
ViewClass = (this.View || View);
}
childOptions = (childOptions || {});
// generate child id
childID = parentID + '.' + (childOptions.id || env.generateID('v'));
// apply previous context
oldChildContext = contextRegistry.get(childID);
newChildContext = extend({}, oldChildContext, childOptions);
// create child view
options = extend(newChildContext, { id: childID });
childView = env.viewHelper.createChildView(
this, ViewClass, options);
// render child view if parent (this) is attached
if (this.attached) {
env.viewHelper.paintChildView(this, childID);
}
return childView;
},
/**
* Returns the child view specified by the child view's _ID attribute.
* @param {string|number} childViewID
*/
getView: function (childViewID) {
var children = env.viewManager.getChildViews(this.getID());
// if id is an array index instead of a child's ID
if (typeof childViewID === 'number' && childViewID < children.length) {
childViewID = children[childViewID];
}
// if id is not an absolute id
if (children.indexOf(childViewID) === -1) {
childViewID = this.getID() + '.' + childViewID;
}
return env.viewManager.getView(childViewID);
},
/**
* Removes a child view specified by the child view's _ID attribute.
* @param {string} childViewID
*/
removeView: function (childViewID) {
var childView = this.getView(childViewID),
children = env.viewManager.getChildViews(this.getID()),
index;
if (childView) {
index = children.indexOf(childView.getID());
if (index > -1) {
env.viewHelper.destroy(childView);
children.splice(index, 1);
}
}
},
// /// ////// //
// DOM Events //
// /// ////// //
/**
* Delegate the UIEventProxy's listener to listen to
* non-bubbling events on a node instead of the document
* @param {string} type
* @param {string} selector
*/
delegate: function (type, selector) {
var viewID = this.getID(),
customEvents = env.viewRegistry.getCustomEventsForView(viewID),
node;
if (!customEvents[type]) {
customEvents[type] = [];
}
if (customEvents[type].indexOf(selector) === -1) {
node = this.find(selector);
env.uiEventProxy.addListener(node, type);
customEvents[type].push(selector || '');
}
},
/**
* UIEventProxy listening to the specified event type.
* @param {string} type
*/
listenFor: function (type) {
env.uiEventProxy.startListenTo(type);
},
// ///////// //
// Overrides //
// ///////// //
/**
* Returns additional, logic based CSS classes for the View's node.
* @returns {string}
*/
getCSS: function () {
return '';
},
/**
* Returns additional, logic based attributes for the View's node.
* @returns {string}
*/
getAttributes: function () {
return {};
},
/**
* Method contains logic to parse the new context for the View.
* @returns {string}
*/
parseContext: function (newContext) {
return newContext;
},
// Method will be executed after the View is attached to the DOM.
onAttach: NoOp,
// Method will be executed before the View is detached from the DOM.
onDetach: NoOp,
// Method will be executed to create the View structure within the current View.
render: NoOp
});
View.extend = function (proto, statik) {
statik = (statik || {});
statik.add = View.add;
proto.className = env.viewManager.inheritCSS(
this.prototype.className,
proto.className
);
return Class.extend.call(this, proto, statik);
};
View.add = function (options, parentView) {
env.insurer.exists.object(
parentView, 'Parent View cannot be undefined!');
return parentView.addView(this, options);
};