0byt3m1n1
Path:
/
data
/
applications
/
aps
/
phprojekt
/
6.0.6-0
/
standard
/
htdocs
/
htdocs
/
dojo
/
dijit
/
tree
/
[
Home
]
File: TreeStoreModel.js
/* Copyright (c) 2004-2010, The Dojo Foundation All Rights Reserved. Available via Academic Free License >= 2.1 OR the modified BSD license. see: http://dojotoolkit.org/license for details */ if(!dojo._hasResource["dijit.tree.TreeStoreModel"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code. dojo._hasResource["dijit.tree.TreeStoreModel"] = true; dojo.provide("dijit.tree.TreeStoreModel"); dojo.declare( "dijit.tree.TreeStoreModel", null, { // summary: // Implements dijit.Tree.model connecting to a store with a single // root item. Any methods passed into the constructor will override // the ones defined here. // store: dojo.data.Store // Underlying store store: null, // childrenAttrs: String[] // One or more attribute names (attributes in the dojo.data item) that specify that item's children childrenAttrs: ["children"], // newItemIdAttr: String // Name of attribute in the Object passed to newItem() that specifies the id. // // If newItemIdAttr is set then it's used when newItem() is called to see if an // item with the same id already exists, and if so just links to the old item // (so that the old item ends up with two parents). // // Setting this to null or "" will make every drop create a new item. newItemIdAttr: "id", // labelAttr: String // If specified, get label for tree node from this attribute, rather // than by calling store.getLabel() labelAttr: "", // root: [readonly] dojo.data.Item // Pointer to the root item (read only, not a parameter) root: null, // query: anything // Specifies datastore query to return the root item for the tree. // Must only return a single item. Alternately can just pass in pointer // to root item. // example: // | {id:'ROOT'} query: null, // deferItemLoadingUntilExpand: Boolean // Setting this to true will cause the TreeStoreModel to defer calling loadItem on nodes // until they are expanded. This allows for lazying loading where only one // loadItem (and generally one network call, consequently) per expansion // (rather than one for each child). // This relies on partial loading of the children items; each children item of a // fully loaded item should contain the label and info about having children. deferItemLoadingUntilExpand: false, constructor: function(/* Object */ args){ // summary: // Passed the arguments listed above (store, etc) // tags: // private dojo.mixin(this, args); this.connects = []; var store = this.store; if(!store.getFeatures()['dojo.data.api.Identity']){ throw new Error("dijit.Tree: store must support dojo.data.Identity"); } // if the store supports Notification, subscribe to the notification events if(store.getFeatures()['dojo.data.api.Notification']){ this.connects = this.connects.concat([ dojo.connect(store, "onNew", this, "onNewItem"), dojo.connect(store, "onDelete", this, "onDeleteItem"), dojo.connect(store, "onSet", this, "onSetItem") ]); } }, destroy: function(){ dojo.forEach(this.connects, dojo.disconnect); // TODO: should cancel any in-progress processing of getRoot(), getChildren() }, // ======================================================================= // Methods for traversing hierarchy getRoot: function(onItem, onError){ // summary: // Calls onItem with the root item for the tree, possibly a fabricated item. // Calls onError on error. if(this.root){ onItem(this.root); }else{ this.store.fetch({ query: this.query, onComplete: dojo.hitch(this, function(items){ if(items.length != 1){ throw new Error(this.declaredClass + ": query " + dojo.toJson(this.query) + " returned " + items.length + " items, but must return exactly one item"); } this.root = items[0]; onItem(this.root); }), onError: onError }); } }, mayHaveChildren: function(/*dojo.data.Item*/ item){ // summary: // Tells if an item has or may have children. Implementing logic here // avoids showing +/- expando icon for nodes that we know don't have children. // (For efficiency reasons we may not want to check if an element actually // has children until user clicks the expando node) return dojo.some(this.childrenAttrs, function(attr){ return this.store.hasAttribute(item, attr); }, this); }, getChildren: function(/*dojo.data.Item*/ parentItem, /*function(items)*/ onComplete, /*function*/ onError){ // summary: // Calls onComplete() with array of child items of given parent item, all loaded. var store = this.store; if(!store.isItemLoaded(parentItem)){ // The parent is not loaded yet, we must be in deferItemLoadingUntilExpand // mode, so we will load it and just return the children (without loading each // child item) var getChildren = dojo.hitch(this, arguments.callee); store.loadItem({ item: parentItem, onItem: function(parentItem){ getChildren(parentItem, onComplete, onError); }, onError: onError }); return; } // get children of specified item var childItems = []; for(var i=0; i<this.childrenAttrs.length; i++){ var vals = store.getValues(parentItem, this.childrenAttrs[i]); childItems = childItems.concat(vals); } // count how many items need to be loaded var _waitCount = 0; if(!this.deferItemLoadingUntilExpand){ dojo.forEach(childItems, function(item){ if(!store.isItemLoaded(item)){ _waitCount++; } }); } if(_waitCount == 0){ // all items are already loaded (or we aren't loading them). proceed... onComplete(childItems); }else{ // still waiting for some or all of the items to load dojo.forEach(childItems, function(item, idx){ if(!store.isItemLoaded(item)){ store.loadItem({ item: item, onItem: function(item){ childItems[idx] = item; if(--_waitCount == 0){ // all nodes have been loaded, send them to the tree onComplete(childItems); } }, onError: onError }); } }); } }, // ======================================================================= // Inspecting items isItem: function(/* anything */ something){ return this.store.isItem(something); // Boolean }, fetchItemByIdentity: function(/* object */ keywordArgs){ this.store.fetchItemByIdentity(keywordArgs); }, getIdentity: function(/* item */ item){ return this.store.getIdentity(item); // Object }, getLabel: function(/*dojo.data.Item*/ item){ // summary: // Get the label for an item if(this.labelAttr){ return this.store.getValue(item,this.labelAttr); // String }else{ return this.store.getLabel(item); // String } }, // ======================================================================= // Write interface newItem: function(/* dojo.dnd.Item */ args, /*Item*/ parent, /*int?*/ insertIndex){ // summary: // Creates a new item. See `dojo.data.api.Write` for details on args. // Used in drag & drop when item from external source dropped onto tree. // description: // Developers will need to override this method if new items get added // to parents with multiple children attributes, in order to define which // children attribute points to the new item. var pInfo = {parent: parent, attribute: this.childrenAttrs[0], insertIndex: insertIndex}; if(this.newItemIdAttr && args[this.newItemIdAttr]){ // Maybe there's already a corresponding item in the store; if so, reuse it. this.fetchItemByIdentity({identity: args[this.newItemIdAttr], scope: this, onItem: function(item){ if(item){ // There's already a matching item in store, use it this.pasteItem(item, null, parent, true, insertIndex); }else{ // Create new item in the tree, based on the drag source. this.store.newItem(args, pInfo); } }}); }else{ // [as far as we know] there is no id so we must assume this is a new item this.store.newItem(args, pInfo); } }, pasteItem: function(/*Item*/ childItem, /*Item*/ oldParentItem, /*Item*/ newParentItem, /*Boolean*/ bCopy, /*int?*/ insertIndex){ // summary: // Move or copy an item from one parent item to another. // Used in drag & drop var store = this.store, parentAttr = this.childrenAttrs[0]; // name of "children" attr in parent item // remove child from source item, and record the attribute that child occurred in if(oldParentItem){ dojo.forEach(this.childrenAttrs, function(attr){ if(store.containsValue(oldParentItem, attr, childItem)){ if(!bCopy){ var values = dojo.filter(store.getValues(oldParentItem, attr), function(x){ return x != childItem; }); store.setValues(oldParentItem, attr, values); } parentAttr = attr; } }); } // modify target item's children attribute to include this item if(newParentItem){ if(typeof insertIndex == "number"){ // call slice() to avoid modifying the original array, confusing the data store var childItems = store.getValues(newParentItem, parentAttr).slice(); childItems.splice(insertIndex, 0, childItem); store.setValues(newParentItem, parentAttr, childItems); }else{ store.setValues(newParentItem, parentAttr, store.getValues(newParentItem, parentAttr).concat(childItem)); } } }, // ======================================================================= // Callbacks onChange: function(/*dojo.data.Item*/ item){ // summary: // Callback whenever an item has changed, so that Tree // can update the label, icon, etc. Note that changes // to an item's children or parent(s) will trigger an // onChildrenChange() so you can ignore those changes here. // tags: // callback }, onChildrenChange: function(/*dojo.data.Item*/ parent, /*dojo.data.Item[]*/ newChildrenList){ // summary: // Callback to do notifications about new, updated, or deleted items. // tags: // callback }, onDelete: function(/*dojo.data.Item*/ parent, /*dojo.data.Item[]*/ newChildrenList){ // summary: // Callback when an item has been deleted. // description: // Note that there will also be an onChildrenChange() callback for the parent // of this item. // tags: // callback }, // ======================================================================= // Events from data store onNewItem: function(/* dojo.data.Item */ item, /* Object */ parentInfo){ // summary: // Handler for when new items appear in the store, either from a drop operation // or some other way. Updates the tree view (if necessary). // description: // If the new item is a child of an existing item, // calls onChildrenChange() with the new list of children // for that existing item. // // tags: // extension // We only care about the new item if it has a parent that corresponds to a TreeNode // we are currently displaying if(!parentInfo){ return; } // Call onChildrenChange() on parent (ie, existing) item with new list of children // In the common case, the new list of children is simply parentInfo.newValue or // [ parentInfo.newValue ], although if items in the store has multiple // child attributes (see `childrenAttr`), then it's a superset of parentInfo.newValue, // so call getChildren() to be sure to get right answer. this.getChildren(parentInfo.item, dojo.hitch(this, function(children){ this.onChildrenChange(parentInfo.item, children); })); }, onDeleteItem: function(/*Object*/ item){ // summary: // Handler for delete notifications from underlying store this.onDelete(item); }, onSetItem: function(/* item */ item, /* attribute-name-string */ attribute, /* object | array */ oldValue, /* object | array */ newValue){ // summary: // Updates the tree view according to changes in the data store. // description: // Handles updates to an item's children by calling onChildrenChange(), and // other updates to an item by calling onChange(). // // See `onNewItem` for more details on handling updates to an item's children. // tags: // extension if(dojo.indexOf(this.childrenAttrs, attribute) != -1){ // item's children list changed this.getChildren(item, dojo.hitch(this, function(children){ // See comments in onNewItem() about calling getChildren() this.onChildrenChange(item, children); })); }else{ // item's label/icon/etc. changed. this.onChange(item); } } }); }