From e44a7e37b6c7b5961adaffc62b9042b8d442938e Mon Sep 17 00:00:00 2001 From: mensonge Date: Thu, 13 Nov 2008 09:49:11 +0000 Subject: New feature: basic Ajax suggestion for tags and implementation of Dojo toolkit git-svn-id: https://semanticscuttle.svn.sourceforge.net/svnroot/semanticscuttle/trunk@151 b3834d28-1941-0410-a4f8-b48e95affb8f --- includes/js/dijit/Tree.js | 1336 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 1336 insertions(+) create mode 100644 includes/js/dijit/Tree.js (limited to 'includes/js/dijit/Tree.js') diff --git a/includes/js/dijit/Tree.js b/includes/js/dijit/Tree.js new file mode 100644 index 0000000..fc9be8b --- /dev/null +++ b/includes/js/dijit/Tree.js @@ -0,0 +1,1336 @@ +if(!dojo._hasResource["dijit.Tree"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code. +dojo._hasResource["dijit.Tree"] = true; +dojo.provide("dijit.Tree"); + +dojo.require("dojo.fx"); + +dojo.require("dijit._Widget"); +dojo.require("dijit._Templated"); +dojo.require("dijit._Container"); +dojo.require("dojo.cookie"); + +dojo.declare( + "dijit._TreeNode", + [dijit._Widget, dijit._Templated, dijit._Container, dijit._Contained], +{ + // summary + // Single node within a tree + + // item: dojo.data.Item + // the dojo.data entry this tree represents + item: null, + + isTreeNode: true, + + // label: String + // Text of this tree node + label: "", + + isExpandable: null, // show expando node + + isExpanded: false, + + // state: String + // dynamic loading-related stuff. + // When an empty folder node appears, it is "UNCHECKED" first, + // then after dojo.data query it becomes "LOADING" and, finally "LOADED" + state: "UNCHECKED", + + templateString:"
\n\t\t\t
\n\t\t\t\n\t\t
\n
\n", + + postCreate: function(){ + // set label, escaping special characters + this.setLabelNode(this.label); + + // set expand icon for leaf + this._setExpando(); + + // set icon and label class based on item + this._updateItemClasses(this.item); + + if(this.isExpandable){ + dijit.setWaiState(this.labelNode, "expanded", this.isExpanded); + } + }, + + markProcessing: function(){ + // summary: visually denote that tree is loading data, etc. + this.state = "LOADING"; + this._setExpando(true); + }, + + unmarkProcessing: function(){ + // summary: clear markup from markProcessing() call + this._setExpando(false); + }, + + _updateItemClasses: function(item){ + // summary: set appropriate CSS classes for icon and label dom node (used to allow for item updates to change respective CSS) + var tree = this.tree, model = tree.model; + if(tree._v10Compat && item === model.root){ + // For back-compat with 1.0, need to use null to specify root item (TODO: remove in 2.0) + item = null; + } + this.iconNode.className = "dijitInline dijitTreeIcon " + tree.getIconClass(item, this.isExpanded); + this.labelNode.className = "dijitTreeLabel " + tree.getLabelClass(item, this.isExpanded); + }, + + _updateLayout: function(){ + // summary: set appropriate CSS classes for this.domNode + var parent = this.getParent(); + if(!parent || parent.rowNode.style.display == "none"){ + /* if we are hiding the root node then make every first level child look like a root node */ + dojo.addClass(this.domNode, "dijitTreeIsRoot"); + }else{ + dojo.toggleClass(this.domNode, "dijitTreeIsLast", !this.getNextSibling()); + } + }, + + _setExpando: function(/*Boolean*/ processing){ + // summary: set the right image for the expando node + + // apply the appropriate class to the expando node + var styles = ["dijitTreeExpandoLoading", "dijitTreeExpandoOpened", + "dijitTreeExpandoClosed", "dijitTreeExpandoLeaf"]; + var idx = processing ? 0 : (this.isExpandable ? (this.isExpanded ? 1 : 2) : 3); + dojo.forEach(styles, + function(s){ + dojo.removeClass(this.expandoNode, s); + }, this + ); + dojo.addClass(this.expandoNode, styles[idx]); + + // provide a non-image based indicator for images-off mode + this.expandoNodeText.innerHTML = + processing ? "*" : + (this.isExpandable ? + (this.isExpanded ? "-" : "+") : "*"); + }, + + expand: function(){ + // summary: show my children + if(this.isExpanded){ return; } + // cancel in progress collapse operation + if(this._wipeOut.status() == "playing"){ + this._wipeOut.stop(); + } + + this.isExpanded = true; + dijit.setWaiState(this.labelNode, "expanded", "true"); + dijit.setWaiRole(this.containerNode, "group"); + this.contentNode.className = "dijitTreeContent dijitTreeContentExpanded"; + this._setExpando(); + this._updateItemClasses(this.item); + + this._wipeIn.play(); + }, + + collapse: function(){ + if(!this.isExpanded){ return; } + + // cancel in progress expand operation + if(this._wipeIn.status() == "playing"){ + this._wipeIn.stop(); + } + + this.isExpanded = false; + dijit.setWaiState(this.labelNode, "expanded", "false"); + this.contentNode.className = "dijitTreeContent"; + this._setExpando(); + this._updateItemClasses(this.item); + + this._wipeOut.play(); + }, + + setLabelNode: function(label){ + this.labelNode.innerHTML=""; + this.labelNode.appendChild(dojo.doc.createTextNode(label)); + }, + + setChildItems: function(/* Object[] */ items){ + // summary: + // Sets the child items of this node, removing/adding nodes + // from current children to match specified items[] array. + + var tree = this.tree, + model = tree.model; + + // Orphan all my existing children. + // If items contains some of the same items as before then we will reattach them. + // Don't call this.removeChild() because that will collapse the tree etc. + this.getChildren().forEach(function(child){ + dijit._Container.prototype.removeChild.call(this, child); + }, this); + + this.state = "LOADED"; + + if(items && items.length > 0){ + this.isExpandable = true; + if(!this.containerNode){ // maybe this node was unfolderized and still has container + this.containerNode = this.tree.containerNodeTemplate.cloneNode(true); + this.domNode.appendChild(this.containerNode); + } + + // Create _TreeNode widget for each specified tree node, unless one already + // exists and isn't being used (presumably it's from a DnD move and was recently + // released + dojo.forEach(items, function(item){ + var id = model.getIdentity(item), + existingNode = tree._itemNodeMap[id], + node = + ( existingNode && !existingNode.getParent() ) ? + existingNode : + new dijit._TreeNode({ + item: item, + tree: tree, + isExpandable: model.mayHaveChildren(item), + label: tree.getLabel(item) + }); + this.addChild(node); + // note: this won't work if there are two nodes for one item (multi-parented items); will be fixed later + tree._itemNodeMap[id] = node; + if(this.tree.persist){ + if(tree._openedItemIds[id]){ + tree._expandNode(node); + } + } + }, this); + + // note that updateLayout() needs to be called on each child after + // _all_ the children exist + dojo.forEach(this.getChildren(), function(child, idx){ + child._updateLayout(); + }); + }else{ + this.isExpandable=false; + } + + if(this._setExpando){ + // change expando to/from dot or + icon, as appropriate + this._setExpando(false); + } + + // On initial tree show, put focus on either the root node of the tree, + // or the first child, if the root node is hidden + if(!this.parent){ + var fc = this.tree.showRoot ? this : this.getChildren()[0], + tabnode = fc ? fc.labelNode : this.domNode; + tabnode.setAttribute("tabIndex", "0"); + } + + // create animations for showing/hiding the children (if children exist) + if(this.containerNode && !this._wipeIn){ + this._wipeIn = dojo.fx.wipeIn({node: this.containerNode, duration: 150}); + this._wipeOut = dojo.fx.wipeOut({node: this.containerNode, duration: 150}); + } + }, + + removeChild: function(/* treeNode */ node){ + this.inherited(arguments); + + var children = this.getChildren(); + if(children.length == 0){ + this.isExpandable = false; + this.collapse(); + } + + dojo.forEach(children, function(child){ + child._updateLayout(); + }); + }, + + makeExpandable: function(){ + //summary + // if this node wasn't already showing the expando node, + // turn it into one and call _setExpando() + this.isExpandable = true; + this._setExpando(false); + }, + + _onNodeFocus: function(evt){ + var node = dijit.getEnclosingWidget(evt.target); + this.tree._onTreeFocus(node); + } +}); + +dojo.declare( + "dijit.Tree", + [dijit._Widget, dijit._Templated], +{ + // summary + // This widget displays hierarchical data from a store. A query is specified + // to get the "top level children" from a data store, and then those items are + // queried for their children and so on (but lazily, as the user clicks the expand node). + // + // Thus in the default mode of operation this widget is technically a forest, not a tree, + // in that there can be multiple "top level children". However, if you specify label, + // then a special top level node (not corresponding to any item in the datastore) is + // created, to father all the top level children. + + // store: String||dojo.data.Store + // The store to get data to display in the tree. + // May remove for 2.0 in favor of "model". + store: null, + + // model: dijit.Tree.model + // Alternate interface from store to access data (and changes to data) in the tree + model: null, + + // query: anything + // Specifies datastore query to return the root item for the tree. + // + // Deprecated functionality: if the query returns multiple items, the tree is given + // a fake root node (not corresponding to any item in the data store), + // whose children are the items that match this query. + // + // The root node is shown or hidden based on whether a label is specified. + // + // Having a query return multiple items is deprecated. + // If your store doesn't have a root item, wrap the store with + // dijit.tree.ForestStoreModel, and specify model=myModel + // + // example: + // {type:'continent'} + query: null, + + // label: String + // Deprecated. Use dijit.tree.ForestStoreModel directly instead. + // Used in conjunction with query parameter. + // If a query is specified (rather than a root node id), and a label is also specified, + // then a fake root node is created and displayed, with this label. + label: "", + + // showRoot: Boolean + // Should the root node be displayed, or hidden? + showRoot: true, + + // childrenAttr: String[] + // one ore more attributes that holds children of a tree node + childrenAttr: ["children"], + + // openOnClick: Boolean + // If true, clicking a folder node's label will open it, rather than calling onClick() + openOnClick: false, + + templateString:"
\n
\n", + + isExpandable: true, + + isTree: true, + + // persist: Boolean + // enables/disables use of cookies for state saving. + persist: true, + + // dndController: String + // class name to use as as the dnd controller + dndController: null, + + //parameters to pull off of the tree and pass on to the dndController as its params + dndParams: ["onDndDrop","itemCreator","onDndCancel","checkAcceptance", "checkItemAcceptance"], + + //declare the above items so they can be pulled from the tree's markup + onDndDrop:null, + itemCreator:null, + onDndCancel:null, + checkAcceptance:null, + checkItemAcceptance:null, + + _publish: function(/*String*/ topicName, /*Object*/ message){ + // summary: + // Publish a message for this widget/topic + dojo.publish(this.id, [dojo.mixin({tree: this, event: topicName}, message||{})]); + }, + + postMixInProperties: function(){ + this.tree = this; + + this._itemNodeMap={}; + + if(!this.cookieName){ + this.cookieName = this.id + "SaveStateCookie"; + } + }, + + postCreate: function(){ + // load in which nodes should be opened automatically + if(this.persist){ + var cookie = dojo.cookie(this.cookieName); + this._openedItemIds = {}; + if(cookie){ + dojo.forEach(cookie.split(','), function(item){ + this._openedItemIds[item] = true; + }, this); + } + } + + // make template for container node (we will clone this and insert it into + // any nodes that have children) + var div = dojo.doc.createElement('div'); + div.style.display = 'none'; + div.className = "dijitTreeContainer"; + dijit.setWaiRole(div, "presentation"); + this.containerNodeTemplate = div; + + // Create glue between store and Tree, if not specified directly by user + if(!this.model){ + this._store2model(); + } + + // monitor changes to items + this.connect(this.model, "onChange", "_onItemChange"); + this.connect(this.model, "onChildrenChange", "_onItemChildrenChange"); + // TODO: monitor item deletes so we don't end up w/orphaned nodes? + + this._load(); + + this.inherited("postCreate", arguments); + + if(this.dndController){ + if(dojo.isString(this.dndController)){ + this.dndController= dojo.getObject(this.dndController); + } + var params={}; + for (var i=0; i