/** _enyo.Node_ is a control that creates structured trees based on Enyo's child component hierarchy format, e.g.: {kind: "Node", icon: "images/folder-open.png", content: "Tree", expandable: true, expanded: true, components: [ {icon: "images/file.png", content: "Alpha"}, {icon: "images/folder-open.png", content: "Bravo", expandable: true, expanded: false, components: [ {icon: "images/file.png", content: "Bravo-Alpha"}, {icon: "images/file.png", content: "Bravo-Bravo"}, {icon: "images/file.png", content: "Bravo-Charlie"} ] } ] } The default kind of components within a node is itself _enyo.Node_, so only the top-level node of the tree needs to be explicitly defined as such. When an expandable tree node expands, an _onExpand_ event is sent; when it is tapped, a _nodeTap_ event is sent. When the optional property _onlyIconExpands_ is set to true, expandable nodes may only be opened by tapping the icon; tapping the content label will fire the _nodeTap_ event, but will not expand the node. */ enyo.kind({ name: "enyo.Node", published: { //* @public //* Whether or not the Node is expandable and has child branches expandable: false, //* Open/closed state of the current Node expanded: false, //* Path to image to be used as the icon for this Node icon: "", /** Optional flag that, when true, causes the Node to expand only when the icon is tapped; not when the contents are tapped */ onlyIconExpands: false, //* @protected //* Adds or removes the Enyo-selected CSS class. selected: false }, style: "padding: 0 0 0 16px;", content: "Node", defaultKind: "Node", classes: "enyo-node", components: [ {name: "icon", kind: "Image", showing: false}, {kind: "Control", name: "caption", Xtag: "span", style: "display: inline-block; padding: 4px;", allowHtml: true}, {kind: "Control", name: "extra", tag: 'span', allowHtml: true} ], childClient: [ {kind: "Control", name: "box", classes: "enyo-node-box", Xstyle: "border: 1px solid orange;", components: [ {kind: "Control", name: "client", classes: "enyo-node-client", Xstyle: "border: 1px solid lightblue;"} ]} ], handlers: { ondblclick: "dblclick" }, events: { //* @public //* Fired when the Node is tapped onNodeTap: "nodeTap", //* Fired when the Node is double-clicked onNodeDblClick: "nodeDblClick", /** Fired when the Node expands or contracts, as indicated by the 'expanded' property in the event data */ onExpand: "nodeExpand", //* Fired when the Node is destroyed onDestroyed: "nodeDestroyed" }, // //* @protected create: function() { this.inherited(arguments); //this.expandedChanged(); //this.levelChanged(); this.selectedChanged(); this.iconChanged(); }, destroy: function() { this.doDestroyed(); this.inherited(arguments); }, initComponents: function() { // TODO: optimize to create the childClient on demand //this.hasChildren = this.components; if (this.expandable) { this.kindComponents = this.kindComponents.concat(this.childClient); } this.inherited(arguments); }, // contentChanged: function() { //this.$.caption.setContent((this.expandable ? (this.expanded ? "-" : "+") : "") + this.content); this.$.caption.setContent(this.content); }, iconChanged: function() { this.$.icon.setSrc(this.icon); this.$.icon.setShowing(Boolean(this.icon)); }, selectedChanged: function() { this.addRemoveClass("enyo-selected", this.selected); }, rendered: function() { this.inherited(arguments); if (this.expandable && !this.expanded) { this.quickCollapse(); } }, // addNodes: function(inNodes) { this.destroyClientControls(); for (var i=0, n; n=inNodes[i]; i++) { this.createComponent(n); } this.$.client.render(); }, addTextNodes: function(inNodes) { this.destroyClientControls(); for (var i=0, n; n=inNodes[i]; i++) { this.createComponent({content: n}); } this.$.client.render(); }, // tap: function(inSender, inEvent) { if(!this.onlyIconExpands) { this.toggleExpanded(); this.doNodeTap(); } else { if((inEvent.target==this.$.icon.hasNode())) { this.toggleExpanded(); } else { this.doNodeTap(); } } return true; }, dblclick: function(inSender, inEvent) { this.doNodeDblClick(); return true; }, // toggleExpanded: function() { this.setExpanded(!this.expanded); }, quickCollapse: function() { this.removeClass("enyo-animate"); this.$.box.applyStyle("height", "0"); var h = this.$.client.getBounds().height; this.$.client.setBounds({top: -h}); }, _expand: function() { this.addClass("enyo-animate"); var h = this.$.client.getBounds().height; this.$.box.setBounds({height: h}); this.$.client.setBounds({top: 0}); setTimeout(enyo.bind(this, function() { // things may have happened in the interim, make sure // we only fix height if we are still expanded if (this.expanded) { this.removeClass("enyo-animate"); this.$.box.applyStyle("height", "auto"); } }), 225); }, _collapse: function() { // disable transitions this.removeClass("enyo-animate"); // fix the height of our box (rather than 'auto'), this // gives webkit something to lerp from var h = this.$.client.getBounds().height; this.$.box.setBounds({height: h}); // yield the thead so DOM can make those changes (without transitions) setTimeout(enyo.bind(this, function() { // enable transitions this.addClass("enyo-animate"); // shrink our box to 0 this.$.box.applyStyle("height", "0"); // slide the contents up this.$.client.setBounds({top: -h}); }), 25); }, expandedChanged: function(inOldExpanded) { if (!this.expandable) { this.expanded = false; } else { var event = {expanded: this.expanded}; this.doExpand(event); if (!event.wait) { this.effectExpanded(); } } }, effectExpanded: function() { if (this.$.client) { if (!this.expanded) { this._collapse(); } else { this._expand(); } } //this.contentChanged(); }/*, // // levelChanged: function() { this.applyStyle("padding-left", 16 + "px"); }, toggleChildren: function() { if (this.$.list) { this.$.list.setShowing(this.expanded); } }, renderNodes: function(inNodes) { var list = this.createComponent({name: "list", container: this}); for (var i=0, n; n=inNodes[i]; i++) { n.setLevel(this.level + 1); n.setContainer(list); n.render(); } list.render(); }, //* @public addNodes: function(inNodes) { this.renderNodes(inNodes); this.toggleChildren(); }, removeNodes: function() { if (this.$.list) { this.$.list.destroy(); } }, hasVisibleChildren: function() { return this.expanded && this.$.list && this.$.list.controls.length > 0; }, fetchParent: function() { return this.level > 0 && this.container.container; }, fetchChildren: function() { return this.$.list && this.$.list.controls; }, fetchFirstChild: function() { return this.$.list && this.$.list.controls[0]; }, fetchLastChild: function() { return this.$.list && this.$.list.controls[this.$.list.controls.length-1]; }, fetchPrevSibling: function() { var i = this.container.controls.indexOf(this); return this.level > 0 && this.container.controls[i-1]; }, fetchNextSibling: function() { var i = this.container.controls.indexOf(this); return this.level > 0 && this.container.controls[i+1]; }, getVisibleBounds: function() { return this.$.client.getBounds(); } */ });