Changeset View
Changeset View
Standalone View
Standalone View
externals/javelin/src/ext/view/View.js
- This file was added.
| /** | |||||
| * A View is a composable wrapper on JX.$N, allowing abstraction of higher-order | |||||
| * views and a consistent pattern of parameterization. It is intended | |||||
| * to be used either directly or as a building block for a syntactic sugar layer | |||||
| * for concise expression of markup patterns. | |||||
| * | |||||
| * @provides javelin-view | |||||
| * @requires javelin-install | |||||
| * javelin-util | |||||
| */ | |||||
| JX.install('View', { | |||||
| construct : function(attrs, children) { | |||||
| this._attributes = JX.copy({}, this.getDefaultAttributeValues()); | |||||
| JX.copy(this._attributes, attrs); | |||||
| this._rawChildren = {}; | |||||
| this._childKeys = []; | |||||
| if (children) { | |||||
| this.addChildren(JX.$AX(children)); | |||||
| } | |||||
| this.setName(this.__class__.__readable__); | |||||
| }, | |||||
| events: [ | |||||
| 'change' | |||||
| ], | |||||
| properties: { | |||||
| 'name': null | |||||
| }, | |||||
| members : { | |||||
| _attributes : null, | |||||
| _rawChildren : null, | |||||
| _childKeys: null, // keys of rawChildren, kept ordered. | |||||
| _nextChildKey: 0, // next key to use for a new child | |||||
| /* | |||||
| * Don't override. | |||||
| * TODO: Strongly typed attribute access (getIntAttr, getStringAttr...)? | |||||
| */ | |||||
| getAttr : function(attrName) { | |||||
| return this._attributes[attrName]; | |||||
| }, | |||||
| /* | |||||
| * Don't override. | |||||
| */ | |||||
| multisetAttr : function(attrs) { | |||||
| JX.copy(this._attributes, attrs); | |||||
| this.invoke('change'); | |||||
| return this; | |||||
| }, | |||||
| /* | |||||
| * Don't override. | |||||
| */ | |||||
| setAttr : function(attrName, value) { | |||||
| this._attributes[attrName] = value; | |||||
| this.invoke('change'); | |||||
| return this; | |||||
| }, | |||||
| /* | |||||
| * Child views can override to specify default values for attributes. | |||||
| */ | |||||
| getDefaultAttributeValues : function() { | |||||
| return {}; | |||||
| }, | |||||
| /** | |||||
| * Don't override. | |||||
| */ | |||||
| getAllAttributes: function() { | |||||
| return JX.copy({}, this._attributes); | |||||
| }, | |||||
| /** | |||||
| * Get the children. Don't override. | |||||
| */ | |||||
| getChildren : function() { | |||||
| var result = []; | |||||
| var should_repack = false; | |||||
| for(var ii = 0; ii < this._childKeys.length; ii++) { | |||||
| var key = this._childKeys[ii]; | |||||
| if (this._rawChildren[key] === undefined) { | |||||
| should_repack = true; | |||||
| } else { | |||||
| result.push(this._rawChildren[key]); | |||||
| } | |||||
| } | |||||
| if (should_repack) { | |||||
| var new_child_keys = []; | |||||
| for(var ii = 0; ii < this._childKeys.length; ii++) { | |||||
| var key = this._childKeys[ii]; | |||||
| if (this._rawChildren[key] !== undefined) { | |||||
| new_child_keys.push(key); | |||||
| } | |||||
| } | |||||
| this._childKeys = new_child_keys; | |||||
| } | |||||
| return result; | |||||
| }, | |||||
| /** | |||||
| * Add children to the view. Returns array of removal handles. | |||||
| * Don't override. | |||||
| */ | |||||
| addChildren : function(children) { | |||||
| var result = []; | |||||
| for (var ii = 0; ii < children.length; ii++) { | |||||
| result.push(this._addChild(children[ii])); | |||||
| } | |||||
| this.invoke('change'); | |||||
| return result; | |||||
| }, | |||||
| /** | |||||
| * Add a single child view to the view. | |||||
| * Returns a removal handle, i.e. an object that has a method remove(), | |||||
| * that removes the added child from the view. | |||||
| * | |||||
| * Don't override. | |||||
| */ | |||||
| addChild: function(child) { | |||||
| var result = this._addChild(child); | |||||
| this.invoke('change'); | |||||
| return result; | |||||
| }, | |||||
| _addChild: function(child) { | |||||
| var key = this._nextChildKey++; | |||||
| this._rawChildren[key] = child; | |||||
| this._childKeys.push(key); | |||||
| return { | |||||
| remove: JX.bind(this, this._removeChild, key) | |||||
| }; | |||||
| }, | |||||
| _removeChild: function(child_key) { | |||||
| delete this._rawChildren[child_key]; | |||||
| this.invoke('change'); | |||||
| }, | |||||
| /** | |||||
| * Accept visitors. This allows adding new behaviors to Views without | |||||
| * having to change View classes themselves. | |||||
| * | |||||
| * This implements a post-order traversal over the tree of views. Children | |||||
| * are processed before parents, and for convenience the results of the | |||||
| * visitor on the children are passed to it when processing the parent. | |||||
| * | |||||
| * The visitor parameter is a callable which receives two parameters. | |||||
| * The first parameter is the view to visit. The second parameter is an | |||||
| * array of the results of visiting the view's children. | |||||
| * | |||||
| * Don't override. | |||||
| */ | |||||
| accept: function(visitor) { | |||||
| var results = []; | |||||
| var children = this.getChildren(); | |||||
| for(var ii = 0; ii < children.length; ii++) { | |||||
| var result; | |||||
| if (children[ii].accept) { | |||||
| result = children[ii].accept(visitor); | |||||
| } else { | |||||
| result = children[ii]; | |||||
| } | |||||
| results.push(result); | |||||
| } | |||||
| return visitor(this, results); | |||||
| }, | |||||
| /** | |||||
| * Given the already-rendered children, return the rendered result of | |||||
| * this view. | |||||
| * By default, just pass the children through. | |||||
| */ | |||||
| render: function(rendered_children) { | |||||
| return rendered_children; | |||||
| } | |||||
| } | |||||
| }); | |||||