/* Copyright (c) 2004-2006, The Dojo Foundation All Rights Reserved. Licensed under the Academic Free License version 2.1 or above OR the modified BSD license. For more information on Dojo licensing, see: http://dojotoolkit.org/community/licensing.shtml */ dojo.provide("dojo.widget.TreeNodeV3"); dojo.require("dojo.html.*"); dojo.require("dojo.event.*"); dojo.require("dojo.io.*"); dojo.require("dojo.widget.TreeWithNode"); dojo.widget.defineWidget( "dojo.widget.TreeNodeV3", [dojo.widget.HtmlWidget, dojo.widget.TreeWithNode], function() { this.actionsDisabled = []; this.object = {}; }, { tryLazyInit: true, /* * Basic actions one can perform on nodes and, some(addchild) on trees */ actions: { MOVE: "MOVE", DETACH: "DETACH", EDIT: "EDIT", ADDCHILD: "ADDCHILD", SELECT: "SELECT" }, labelClass: "", contentClass: "", expandNode: null, labelNode: null, /** * can't call it nodeType cause of IE problems */ nodeDocType: "", selected: false, getnodeDocType: function() { return this.nodeDocType; }, cloneProperties: ["actionsDisabled","tryLazyInit","nodeDocType","objectId","object", "title","isFolder","isExpanded","state"], /** * copy cloneProperties with recursion into them * contains "copy constructor" */ clone: function(deep) { var ret = new this.constructor(); //dojo.debug("start cloning props "+this); for(var i=0; i registerChild or from postInitialize->registerChild * not called in batch procession * HTML & widget.createWidget only * Layout MUST be removed when node is detached * */ addedTo: function(parent, index, dontPublishEvent) { //dojo.profile.start("addedTo"); //dojo.debug(this + " addedTo "+parent+" index "+index); //dojo.debug(parent.children); //dojo.debug(parent.containerNode.innerHTML); //dojo.debug((new Error()).stack); if (this.tree !== parent.tree) { this.updateTree(parent.tree); } if (parent.isTreeNode) { if (!parent.isFolder) { //dojo.debug("folderize parent "+parent); parent.setFolder(); parent.state = parent.loadStates.LOADED; } } var siblingsCount = parent.children.length; // setFolder works BEFORE insertNode this.insertNode(parent, index); this.viewAddLayout(); //dojo.debug("siblings "+parent.children); if (siblingsCount > 1) { if (index == 0 && parent.children[1] instanceof dojo.widget.Widget) { parent.children[1].viewUpdateLayout(); } if (index == siblingsCount-1 && parent.children[siblingsCount-2] instanceof dojo.widget.Widget) { parent.children[siblingsCount-2].viewUpdateLayout(); } } else if (parent.isTreeNode) { // added as the first child //dojo.debug("added as first"); parent.viewSetHasChildren(); } if (!dontPublishEvent) { var message = { child: this, index: index, parent: parent } dojo.event.topic.publish(this.tree.eventNames.afterAddChild, message); } //dojo.profile.end("addedTo"); }, /** * Fast program-only hacky creation of widget * */ createSimple: function(args, parent) { // I pass no args and ignore default controller //dojo.profile.start(this.widgetType+" createSimple"); //dojo.profile.start(this.widgetType+" createSimple constructor"); if (args.tree) { var tree = args.tree; } else if (parent) { var tree = parent.tree; } else { dojo.raise("createSimple: can't evaluate tree"); } tree = dojo.widget.byId(tree); //dojo.debug(tree); var treeNode = new tree.defaultChildWidget(); //dojo.profile.end(this.widgetType+" createSimple constructor"); //dojo.profile.start(this.widgetType+" createSimple mixin"); for(var x in args){ // fastMixIn treeNode[x] = args[x]; } //dojo.profile.end(this.widgetType+" createSimple mixin"); // HtmlWidget.postMixIn treeNode.toggleObj = dojo.lfx.toggle[treeNode.toggle.toLowerCase()] || dojo.lfx.toggle.plain; //dojo.profile.start(this.widgetType + " manager"); dojo.widget.manager.add(treeNode); //dojo.profile.end(this.widgetType + " manager"); //dojo.profile.start(this.widgetType + " buildRendering"); treeNode.buildRendering(args, {}, parent); //dojo.profile.end(this.widgetType + " buildRendering"); treeNode.initialize(args, {}, parent); //dojo.profile.end(this.widgetType+"createSimple"); if (treeNode.parent) { delete dojo.widget.manager.topWidgets[treeNode.widgetId]; } return treeNode; }, // can override e.g for case of div with +- text inside viewUpdateLayout: function() { //dojo.profile.start("viewUpdateLayout"); //dojo.debug("UpdateLayout in "+this); this.viewRemoveLayout(); this.viewAddLayout(); //dojo.profile.end("viewUpdateLayout"); }, viewAddContainer: function() { // make controller only if children exist this.containerNode = this.tree.containerNodeTemplate.cloneNode(true); this.domNode.appendChild(this.containerNode); }, /* viewRemoveContainer: function() { // make controller only if children exist this.domNode.removeChild(this.containerNode); this.containerNode = null; }, */ viewAddLayout: function() { //dojo.profile.start("viewAddLayout"); //dojo.debug("viewAddLayout in "+this); if (this.parent["isTree"]) { //dojo.debug("Parent isTree => add isTreeRoot"); // use setClass, not addClass for speed dojo.html.setClass(this.domNode, dojo.html.getClass(this.domNode) + ' '+this.tree.classPrefix+'IsRoot') } //dojo.debug(this.parent.children.length); //dojo.debug(this.parent.children[this.parent.children.length-1]); if (this.isLastChild()) { //dojo.debug("Checked last node for "+this); //dojo.debug("Parent last is "+this.parent.children[this.parent.children.length-1]); //dojo.debug("last node => add isTreeLast for "+this); dojo.html.setClass(this.domNode, dojo.html.getClass(this.domNode) + ' '+this.tree.classPrefix+'IsLast') } //dojo.profile.end("viewAddLayout"); //dojo.debug("viewAddLayout out"); }, viewRemoveLayout: function() { //dojo.debug("viewRemoveLayout in "+this); //dojo.profile.start("viewRemoveLayout"); //dojo.debug((new Error()).stack); dojo.html.removeClass(this.domNode, this.tree.classPrefix+"IsRoot"); dojo.html.removeClass(this.domNode, this.tree.classPrefix+"IsLast"); //dojo.profile.end("viewRemoveLayout"); }, viewGetExpandClass: function() { if (this.isFolder) { return this.isExpanded ? "ExpandOpen" : "ExpandClosed"; } else { return "ExpandLeaf"; } }, viewSetExpand: function() { //dojo.profile.start("viewSetExpand"); //dojo.debug("viewSetExpand in "+this); var expand = this.tree.classPrefix+this.viewGetExpandClass(); var reg = new RegExp("(^|\\s)"+this.tree.classPrefix+"Expand\\w+",'g'); dojo.html.setClass(this.domNode, dojo.html.getClass(this.domNode).replace(reg,'') + ' '+expand); //dojo.debug(dojo.html.getClass(this.domNode)) //dojo.profile.end("viewSetExpand"); this.viewSetHasChildrenAndExpand(); }, viewGetChildrenClass: function() { return 'Children'+(this.children.length ? 'Yes' : 'No'); }, viewSetHasChildren: function() { //dojo.debug(this+' '+this.children.length) var clazz = this.tree.classPrefix+this.viewGetChildrenClass(); var reg = new RegExp("(^|\\s)"+this.tree.classPrefix+"Children\\w+",'g'); dojo.html.setClass(this.domNode, dojo.html.getClass(this.domNode).replace(reg,'') + ' '+clazz); this.viewSetHasChildrenAndExpand(); }, /** * set TreeStateChildrenYes-ExpandClosed pair * needed for IE, because IE reads only last class from .TreeChildrenYes.TreeExpandClosed pair */ viewSetHasChildrenAndExpand: function() { var clazz = this.tree.classPrefix+'State'+this.viewGetChildrenClass()+'-'+this.viewGetExpandClass(); var reg = new RegExp("(^|\\s)"+this.tree.classPrefix+"State[\\w-]+",'g'); dojo.html.setClass(this.domNode, dojo.html.getClass(this.domNode).replace(reg,'') + ' '+clazz); }, viewUnfocus: function() { dojo.html.removeClass(this.labelNode, this.tree.classPrefix+"LabelFocused"); }, viewFocus: function() { dojo.html.addClass(this.labelNode, this.tree.classPrefix+"LabelFocused"); }, viewEmphasize: function() { dojo.html.clearSelection(this.labelNode); dojo.html.addClass(this.labelNode, this.tree.classPrefix+'NodeEmphasized'); }, viewUnemphasize: function() { dojo.html.removeClass(this.labelNode, this.tree.classPrefix+'NodeEmphasized'); }, // ================================ detach from parent =================================== detach: function() { if (!this.parent) return; var parent = this.parent; var index = this.getParentIndex(); this.doDetach.apply(this, arguments); dojo.event.topic.publish(this.tree.eventNames.afterDetach, { child: this, parent: parent, index:index } ); }, /* node does not leave tree */ doDetach: function() { //dojo.debug("doDetach in "+this+" parent "+this.parent+" class "+dojo.html.getClass(this.domNode)); var parent = this.parent; //dojo.debug(parent.containerNode.style.display) if (!parent) return; var index = this.getParentIndex(); this.viewRemoveLayout(); dojo.widget.DomWidget.prototype.removeChild.call(parent, this); var siblingsCount = parent.children.length; //dojo.debug("siblingsCount "+siblingsCount); if (siblingsCount > 0) { if (index == 0) { // deleted first node => update new first parent.children[0].viewUpdateLayout(); } if (index == siblingsCount) { // deleted last node parent.children[siblingsCount-1].viewUpdateLayout(); } } else { if (parent.isTreeNode) { parent.viewSetHasChildren(); } } if (this.tree.unsetFolderOnEmpty && !parent.children.length && parent.isTreeNode) { parent.unsetFolder(); } //dojo.debug(parent.containerNode.style.display) this.parent = null; }, /** * publish destruction event so that controller may unregister/unlisten */ destroy: function() { dojo.event.topic.publish(this.tree.eventNames.beforeNodeDestroy, { source: this } ); this.detach(); return dojo.widget.HtmlWidget.prototype.destroy.apply(this, arguments); }, expand: function(){ if (this.isExpanded) return; //dojo.profile.start("expand "+this); //dojo.debug("expand in "+this); //dojo.profile.start("expand - lazy init "+this); if (this.tryLazyInit) { this.setChildren(); this.tryLazyInit = false; } //dojo.profile.end("expand - lazy init "+this); this.isExpanded = true; this.viewSetExpand(); //dojo.profile.start("expand - showChildren "+this); /** * no matter if I have children or not. need to show/hide container anyway. * use case: empty folder is expanded => then child is added, container already shown all fine */ this.showChildren(); //dojo.profile.end("expand - showChildren "+this); //dojo.profile.end("expand "+this); }, collapse: function(){ if (!this.isExpanded) return; this.isExpanded = false; this.hideChildren(); }, hideChildren: function(){ this.tree.toggleObj.hide( this.containerNode, this.tree.toggleDuration, this.explodeSrc, dojo.lang.hitch(this, "onHideChildren") ); }, showChildren: function(){ //dojo.profile.start("showChildren"+this); this.tree.toggleObj.show( this.containerNode, this.tree.toggleDuration, this.explodeSrc, dojo.lang.hitch(this, "onShowChildren") ); //dojo.profile.end("showChildren"+this); }, onShowChildren: function() { //dojo.profile.start("onShowChildren"+this); this.onShow(); //dojo.profile.end("onShowChildren"+this); dojo.event.topic.publish(this.tree.eventNames.afterExpand, {source: this} ); }, onHideChildren: function() { this.viewSetExpand(); this.onHide(); dojo.event.topic.publish(this.tree.eventNames.afterCollapse, {source: this} ); }, /* Edit current node : change properties and update contents */ setTitle: function(title) { var oldTitle = this.title; this.labelNode.innerHTML = this.title = title; dojo.event.topic.publish(this.tree.eventNames.afterSetTitle, { source: this, oldTitle:oldTitle }); }, toString: function() { return '['+this.widgetType+', '+this.title+']'; } });