ttrss/lib/dijit/tree/ForestStoreModel.js.uncompr...

280 lines
9.0 KiB
JavaScript

define("dijit/tree/ForestStoreModel", [
"dojo/_base/array", // array.indexOf array.some
"dojo/_base/declare", // declare
"dojo/_base/kernel", // global
"dojo/_base/lang", // lang.hitch
"./TreeStoreModel"
], function(array, declare, kernel, lang, TreeStoreModel){
// module:
// dijit/tree/ForestStoreModel
return declare("dijit.tree.ForestStoreModel", TreeStoreModel, {
// summary:
// Interface between a dijit.Tree and a dojo.data store that doesn't have a root item,
// a.k.a. a store that has multiple "top level" items.
//
// description:
// Use this class to wrap a dojo.data store, making all the items matching the specified query
// appear as children of a fabricated "root item". If no query is specified then all the
// items returned by fetch() on the underlying store become children of the root item.
// This class allows dijit.Tree to assume a single root item, even if the store doesn't have one.
//
// When using this class the developer must override a number of methods according to their app and
// data, including:
//
// - onNewRootItem
// - onAddToRoot
// - onLeaveRoot
// - onNewItem
// - onSetItem
// Parameters to constructor
// rootId: String
// ID of fabricated root item
rootId: "$root$",
// rootLabel: String
// Label of fabricated root item
rootLabel: "ROOT",
// query: String
// Specifies the set of children of the root item.
// example:
// | {type:'continent'}
query: null,
// End of parameters to constructor
constructor: function(params){
// summary:
// Sets up variables, etc.
// tags:
// private
// Make dummy root item
this.root = {
store: this,
root: true,
id: params.rootId,
label: params.rootLabel,
children: params.rootChildren // optional param
};
},
// =======================================================================
// Methods for traversing hierarchy
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)
// tags:
// extension
return item === this.root || this.inherited(arguments);
},
getChildren: function(/*dojo/data/Item*/ parentItem, /*function(items)*/ callback, /*function*/ onError){
// summary:
// Calls onComplete() with array of child items of given parent item, all loaded.
if(parentItem === this.root){
if(this.root.children){
// already loaded, just return
callback(this.root.children);
}else{
this.store.fetch({
query: this.query,
onComplete: lang.hitch(this, function(items){
this.root.children = items;
callback(items);
}),
onError: onError
});
}
}else{
this.inherited(arguments);
}
},
// =======================================================================
// Inspecting items
isItem: function(/* anything */ something){
return (something === this.root) ? true : this.inherited(arguments);
},
fetchItemByIdentity: function(/* object */ keywordArgs){
if(keywordArgs.identity == this.root.id){
var scope = keywordArgs.scope || kernel.global;
if(keywordArgs.onItem){
keywordArgs.onItem.call(scope, this.root);
}
}else{
this.inherited(arguments);
}
},
getIdentity: function(/* item */ item){
return (item === this.root) ? this.root.id : this.inherited(arguments);
},
getLabel: function(/* item */ item){
return (item === this.root) ? this.root.label : this.inherited(arguments);
},
// =======================================================================
// Write interface
newItem: function(/* dijit/tree/dndSource.__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.
if(parent === this.root){
this.onNewRootItem(args);
return this.store.newItem(args);
}else{
return this.inherited(arguments);
}
},
onNewRootItem: function(/* dijit/tree/dndSource.__Item */ /*===== args =====*/){
// summary:
// User can override this method to modify a new element that's being
// added to the root of the tree, for example to add a flag like root=true
},
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
if(oldParentItem === this.root){
if(!bCopy){
// It's onLeaveRoot()'s responsibility to modify the item so it no longer matches
// this.query... thus triggering an onChildrenChange() event to notify the Tree
// that this element is no longer a child of the root node
this.onLeaveRoot(childItem);
}
}
this.inherited(arguments, [childItem,
oldParentItem === this.root ? null : oldParentItem,
newParentItem === this.root ? null : newParentItem,
bCopy,
insertIndex
]);
if(newParentItem === this.root){
// It's onAddToRoot()'s responsibility to modify the item so it matches
// this.query... thus triggering an onChildrenChange() event to notify the Tree
// that this element is now a child of the root node
this.onAddToRoot(childItem);
}
},
// =======================================================================
// Handling for top level children
onAddToRoot: function(/* item */ item){
// summary:
// Called when item added to root of tree; user must override this method
// to modify the item so that it matches the query for top level items
// example:
// | store.setValue(item, "root", true);
// tags:
// extension
console.log(this, ": item ", item, " added to root");
},
onLeaveRoot: function(/* item */ item){
// summary:
// Called when item removed from root of tree; user must override this method
// to modify the item so it doesn't match the query for top level items
// example:
// | store.unsetAttribute(item, "root");
// tags:
// extension
console.log(this, ": item ", item, " removed from root");
},
// =======================================================================
// Events from data store
_requeryTop: function(){
// reruns the query for the children of the root node,
// sending out an onSet notification if those children have changed
var oldChildren = this.root.children || [];
this.store.fetch({
query: this.query,
onComplete: lang.hitch(this, function(newChildren){
this.root.children = newChildren;
// If the list of children or the order of children has changed...
if(oldChildren.length != newChildren.length ||
array.some(oldChildren, function(item, idx){ return newChildren[idx] != item;})){
this.onChildrenChange(this.root, newChildren);
}
})
});
},
onNewItem: function(/* dojo/data/api/Item */ item, /* Object */ parentInfo){
// summary:
// Handler for when new items appear in the store. Developers should override this
// method to be more efficient based on their app/data.
// description:
// Note that the default implementation requeries the top level items every time
// a new item is created, since any new item could be a top level item (even in
// addition to being a child of another item, since items can have multiple parents).
//
// If developers can detect which items are possible top level items (based on the item and the
// parentInfo parameters), they should override this method to only call _requeryTop() for top
// level items. Often all top level items have parentInfo==null, but
// that will depend on which store you use and what your data is like.
// tags:
// extension
this._requeryTop();
this.inherited(arguments);
},
onDeleteItem: function(/*Object*/ item){
// summary:
// Handler for delete notifications from underlying store
// check if this was a child of root, and if so send notification that root's children
// have changed
if(array.indexOf(this.root.children, item) != -1){
this._requeryTop();
}
this.inherited(arguments);
},
onSetItem: function(/* item */ item,
/* attribute-name-string */ attribute,
/* Object|Array */ oldValue,
/* Object|Array */ newValue){
// summary:
// Updates the tree view according to changes to an item in the data store.
// Developers should override this method to be more efficient based on their app/data.
// description:
// Handles updates to an item's children by calling onChildrenChange(), and
// other updates to an item by calling onChange().
//
// Also, any change to any item re-executes the query for the tree's top-level items,
// since this modified item may have started/stopped matching the query for top level items.
//
// If possible, developers should override this function to only call _requeryTop() when
// the change to the item has caused it to stop/start being a top level item in the tree.
// tags:
// extension
this._requeryTop();
this.inherited(arguments);
}
});
});