diff options
Diffstat (limited to 'js/dojo-1.7.2/dijit/tree')
| -rw-r--r-- | js/dojo-1.7.2/dijit/tree/ForestStoreModel.js | 286 | ||||
| -rw-r--r-- | js/dojo-1.7.2/dijit/tree/TreeStoreModel.js | 384 | ||||
| -rw-r--r-- | js/dojo-1.7.2/dijit/tree/_dndContainer.js | 151 | ||||
| -rw-r--r-- | js/dojo-1.7.2/dijit/tree/_dndSelector.js | 327 | ||||
| -rw-r--r-- | js/dojo-1.7.2/dijit/tree/dndSource.js | 565 | ||||
| -rw-r--r-- | js/dojo-1.7.2/dijit/tree/model.js | 143 |
6 files changed, 1856 insertions, 0 deletions
diff --git a/js/dojo-1.7.2/dijit/tree/ForestStoreModel.js b/js/dojo-1.7.2/dijit/tree/ForestStoreModel.js new file mode 100644 index 0000000..0b77b02 --- /dev/null +++ b/js/dojo-1.7.2/dijit/tree/ForestStoreModel.js @@ -0,0 +1,286 @@ +//>>built +define("dijit/tree/ForestStoreModel", [ + "dojo/_base/array", // array.indexOf array.some + "dojo/_base/declare", // declare + "dojo/_base/lang", // lang.hitch + "dojo/_base/window", // win.global + "./TreeStoreModel" +], function(array, declare, lang, win, TreeStoreModel){ + +/*===== +var TreeStoreModel = dijit.tree.TreeStoreModel; +=====*/ + +// module: +// dijit/tree/ForestStoreModel +// 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. + +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?keywordArgs.scope:win.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(/* 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. + if(parent === this.root){ + this.onNewRootItem(args); + return this.store.newItem(args); + }else{ + return this.inherited(arguments); + } + }, + + onNewRootItem: function(/* dojo.dnd.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.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); + } + +}); + +}); diff --git a/js/dojo-1.7.2/dijit/tree/TreeStoreModel.js b/js/dojo-1.7.2/dijit/tree/TreeStoreModel.js new file mode 100644 index 0000000..235478d --- /dev/null +++ b/js/dojo-1.7.2/dijit/tree/TreeStoreModel.js @@ -0,0 +1,384 @@ +//>>built +define("dijit/tree/TreeStoreModel", [ + "dojo/_base/array", // array.filter array.forEach array.indexOf array.some + "dojo/aspect", // aspect.after + "dojo/_base/declare", // declare + "dojo/_base/json", // json.stringify + "dojo/_base/lang" // lang.hitch +], function(array, aspect, declare, json, lang){ + + // module: + // dijit/tree/TreeStoreModel + // summary: + // Implements dijit.Tree.model connecting to a dojo.data store with a single + // root item. + + return declare("dijit.tree.TreeStoreModel", null, { + // summary: + // Implements dijit.Tree.model connecting to a dojo.data 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 + + lang.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([ + aspect.after(store, "onNew", lang.hitch(this, "onNewItem"), true), + aspect.after(store, "onDelete", lang.hitch(this, "onDeleteItem"), true), + aspect.after(store, "onSet", lang.hitch(this, "onSetItem"), true) + ]); + } + }, + + destroy: function(){ + var h; + while(h = this.connects.pop()){ h.remove(); } + // 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: lang.hitch(this, function(items){ + if(items.length != 1){ + throw new Error(this.declaredClass + ": query " + json.stringify(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 array.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 = lang.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){ + array.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 + array.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]}, LnewItem; + + 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. + LnewItem=this.store.newItem(args, pInfo); + if(LnewItem && (insertIndex!=undefined)){ + // Move new item to desired position + this.pasteItem(LnewItem, parent, parent, false, insertIndex); + } + } + }}); + }else{ + // [as far as we know] there is no id so we must assume this is a new item + LnewItem=this.store.newItem(args, pInfo); + if(LnewItem && (insertIndex!=undefined)){ + // Move new item to desired position + this.pasteItem(LnewItem, parent, parent, false, insertIndex); + } + } + }, + + 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){ + array.forEach(this.childrenAttrs, function(attr){ + if(store.containsValue(oldParentItem, attr, childItem)){ + if(!bCopy){ + var values = array.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(/*===== parent, newChildrenList =====*/){ + // summary: + // Callback to do notifications about new, updated, or deleted items. + // parent: dojo.data.Item + // newChildrenList: dojo.data.Item[] + // tags: + // callback + }, + + onDelete: function(/*dojo.data.Item*/ /*===== item =====*/){ + // 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, lang.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, attribute /*===== , oldValue, 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. + // item: Item + // attribute: attribute-name-string + // oldValue: object | array + // newValue: object | array + // tags: + // extension + + if(array.indexOf(this.childrenAttrs, attribute) != -1){ + // item's children list changed + this.getChildren(item, lang.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); + } + } + }); +}); diff --git a/js/dojo-1.7.2/dijit/tree/_dndContainer.js b/js/dojo-1.7.2/dijit/tree/_dndContainer.js new file mode 100644 index 0000000..afe1d7c --- /dev/null +++ b/js/dojo-1.7.2/dijit/tree/_dndContainer.js @@ -0,0 +1,151 @@ +//>>built +define("dijit/tree/_dndContainer", [ + "dojo/aspect", // aspect.after + "dojo/_base/declare", // declare + "dojo/dom-class", // domClass.add domClass.remove domClass.replace + "dojo/_base/event", // event.stop + "dojo/_base/lang", // lang.getObject lang.mixin lang.hitch + "dojo/mouse", // mouse.enter, mouse.leave + "dojo/on" +], function(aspect, declare, domClass, event, lang, mouse, on){ + + // module: + // dijit/tree/_dndContainer + // summary: + // This is a base class for `dijit.tree._dndSelector`, and isn't meant to be used directly. + // It's modeled after `dojo.dnd.Container`. + + return declare("dijit.tree._dndContainer", null, { + + // summary: + // This is a base class for `dijit.tree._dndSelector`, and isn't meant to be used directly. + // It's modeled after `dojo.dnd.Container`. + // tags: + // protected + + /*===== + // current: DomNode + // The currently hovered TreeNode.rowNode (which is the DOM node + // associated w/a given node in the tree, excluding it's descendants) + current: null, + =====*/ + + constructor: function(tree, params){ + // summary: + // A constructor of the Container + // tree: Node + // Node or node's id to build the container on + // params: dijit.tree.__SourceArgs + // A dict of parameters, which gets mixed into the object + // tags: + // private + this.tree = tree; + this.node = tree.domNode; // TODO: rename; it's not a TreeNode but the whole Tree + lang.mixin(this, params); + + // class-specific variables + this.current = null; // current TreeNode's DOM node + + // states + this.containerState = ""; + domClass.add(this.node, "dojoDndContainer"); + + // set up events + this.events = [ + // container level events + on(this.node, mouse.enter, lang.hitch(this, "onOverEvent")), + on(this.node, mouse.leave, lang.hitch(this, "onOutEvent")), + + // switching between TreeNodes + aspect.after(this.tree, "_onNodeMouseEnter", lang.hitch(this, "onMouseOver"), true), + aspect.after(this.tree, "_onNodeMouseLeave", lang.hitch(this, "onMouseOut"), true), + + // cancel text selection and text dragging + on(this.node, "dragstart", lang.hitch(event, "stop")), + on(this.node, "selectstart", lang.hitch(event, "stop")) + ]; + }, + + destroy: function(){ + // summary: + // Prepares this object to be garbage-collected + + var h; + while(h = this.events.pop()){ h.remove(); } + + // this.clearItems(); + this.node = this.parent = null; + }, + + // mouse events + onMouseOver: function(widget /*===== , evt =====*/){ + // summary: + // Called when mouse is moved over a TreeNode + // widget: TreeNode + // evt: Event + // tags: + // protected + this.current = widget; + }, + + onMouseOut: function(/*===== widget, evt =====*/){ + // summary: + // Called when mouse is moved away from a TreeNode + // widget: TreeNode + // evt: Event + // tags: + // protected + this.current = null; + }, + + _changeState: function(type, newState){ + // summary: + // Changes a named state to new state value + // type: String + // A name of the state to change + // newState: String + // new state + var prefix = "dojoDnd" + type; + var state = type.toLowerCase() + "State"; + //domClass.replace(this.node, prefix + newState, prefix + this[state]); + domClass.replace(this.node, prefix + newState, prefix + this[state]); + this[state] = newState; + }, + + _addItemClass: function(node, type){ + // summary: + // Adds a class with prefix "dojoDndItem" + // node: Node + // A node + // type: String + // A variable suffix for a class name + domClass.add(node, "dojoDndItem" + type); + }, + + _removeItemClass: function(node, type){ + // summary: + // Removes a class with prefix "dojoDndItem" + // node: Node + // A node + // type: String + // A variable suffix for a class name + domClass.remove(node, "dojoDndItem" + type); + }, + + onOverEvent: function(){ + // summary: + // This function is called once, when mouse is over our container + // tags: + // protected + this._changeState("Container", "Over"); + }, + + onOutEvent: function(){ + // summary: + // This function is called once, when mouse is out of our container + // tags: + // protected + this._changeState("Container", ""); + } + }); +}); diff --git a/js/dojo-1.7.2/dijit/tree/_dndSelector.js b/js/dojo-1.7.2/dijit/tree/_dndSelector.js new file mode 100644 index 0000000..843ee2d --- /dev/null +++ b/js/dojo-1.7.2/dijit/tree/_dndSelector.js @@ -0,0 +1,327 @@ +//>>built +define("dijit/tree/_dndSelector", [ + "dojo/_base/array", // array.filter array.forEach array.map + "dojo/_base/connect", // connect.isCopyKey + "dojo/_base/declare", // declare + "dojo/_base/lang", // lang.hitch + "dojo/mouse", // mouse.isLeft + "dojo/on", + "dojo/touch", + "dojo/_base/window", // win.global + "./_dndContainer" +], function(array, connect, declare, lang, mouse, on, touch, win, _dndContainer){ + + // module: + // dijit/tree/_dndSelector + // summary: + // This is a base class for `dijit.tree.dndSource` , and isn't meant to be used directly. + // It's based on `dojo.dnd.Selector`. + + + return declare("dijit.tree._dndSelector", _dndContainer, { + // summary: + // This is a base class for `dijit.tree.dndSource` , and isn't meant to be used directly. + // It's based on `dojo.dnd.Selector`. + // tags: + // protected + + /*===== + // selection: Hash<String, DomNode> + // (id, DomNode) map for every TreeNode that's currently selected. + // The DOMNode is the TreeNode.rowNode. + selection: {}, + =====*/ + + constructor: function(){ + // summary: + // Initialization + // tags: + // private + + this.selection={}; + this.anchor = null; + + this.tree.domNode.setAttribute("aria-multiselect", !this.singular); + + this.events.push( + on(this.tree.domNode, touch.press, lang.hitch(this,"onMouseDown")), + on(this.tree.domNode, touch.release, lang.hitch(this,"onMouseUp")), + on(this.tree.domNode, touch.move, lang.hitch(this,"onMouseMove")) + ); + }, + + // singular: Boolean + // Allows selection of only one element, if true. + // Tree hasn't been tested in singular=true mode, unclear if it works. + singular: false, + + // methods + getSelectedTreeNodes: function(){ + // summary: + // Returns a list of selected node(s). + // Used by dndSource on the start of a drag. + // tags: + // protected + var nodes=[], sel = this.selection; + for(var i in sel){ + nodes.push(sel[i]); + } + return nodes; + }, + + selectNone: function(){ + // summary: + // Unselects all items + // tags: + // private + + this.setSelection([]); + return this; // self + }, + + destroy: function(){ + // summary: + // Prepares the object to be garbage-collected + this.inherited(arguments); + this.selection = this.anchor = null; + }, + addTreeNode: function(/*dijit._TreeNode*/node, /*Boolean?*/isAnchor){ + // summary: + // add node to current selection + // node: Node + // node to add + // isAnchor: Boolean + // Whether the node should become anchor. + + this.setSelection(this.getSelectedTreeNodes().concat( [node] )); + if(isAnchor){ this.anchor = node; } + return node; + }, + removeTreeNode: function(/*dijit._TreeNode*/node){ + // summary: + // remove node from current selection + // node: Node + // node to remove + this.setSelection(this._setDifference(this.getSelectedTreeNodes(), [node])); + return node; + }, + isTreeNodeSelected: function(/*dijit._TreeNode*/node){ + // summary: + // return true if node is currently selected + // node: Node + // the node to check whether it's in the current selection + + return node.id && !!this.selection[node.id]; + }, + setSelection: function(/*dijit._treeNode[]*/ newSelection){ + // summary: + // set the list of selected nodes to be exactly newSelection. All changes to the + // selection should be passed through this function, which ensures that derived + // attributes are kept up to date. Anchor will be deleted if it has been removed + // from the selection, but no new anchor will be added by this function. + // newSelection: Node[] + // list of tree nodes to make selected + var oldSelection = this.getSelectedTreeNodes(); + array.forEach(this._setDifference(oldSelection, newSelection), lang.hitch(this, function(node){ + node.setSelected(false); + if(this.anchor == node){ + delete this.anchor; + } + delete this.selection[node.id]; + })); + array.forEach(this._setDifference(newSelection, oldSelection), lang.hitch(this, function(node){ + node.setSelected(true); + this.selection[node.id] = node; + })); + this._updateSelectionProperties(); + }, + _setDifference: function(xs,ys){ + // summary: + // Returns a copy of xs which lacks any objects + // occurring in ys. Checks for membership by + // modifying and then reading the object, so it will + // not properly handle sets of numbers or strings. + + array.forEach(ys, function(y){ y.__exclude__ = true; }); + var ret = array.filter(xs, function(x){ return !x.__exclude__; }); + + // clean up after ourselves. + array.forEach(ys, function(y){ delete y['__exclude__'] }); + return ret; + }, + _updateSelectionProperties: function(){ + // summary: + // Update the following tree properties from the current selection: + // path[s], selectedItem[s], selectedNode[s] + + var selected = this.getSelectedTreeNodes(); + var paths = [], nodes = []; + array.forEach(selected, function(node){ + nodes.push(node); + paths.push(node.getTreePath()); + }); + var items = array.map(nodes,function(node){ return node.item; }); + this.tree._set("paths", paths); + this.tree._set("path", paths[0] || []); + this.tree._set("selectedNodes", nodes); + this.tree._set("selectedNode", nodes[0] || null); + this.tree._set("selectedItems", items); + this.tree._set("selectedItem", items[0] || null); + }, + // mouse events + onMouseDown: function(e){ + // summary: + // Event processor for onmousedown/ontouchstart + // e: Event + // onmousedown/ontouchstart event + // tags: + // protected + + // ignore click on expando node + if(!this.current || this.tree.isExpandoNode(e.target, this.current)){ return; } + + if(!mouse.isLeft(e)){ return; } // ignore right-click + + e.preventDefault(); + + var treeNode = this.current, + copy = connect.isCopyKey(e), id = treeNode.id; + + // if shift key is not pressed, and the node is already in the selection, + // delay deselection until onmouseup so in the case of DND, deselection + // will be canceled by onmousemove. + if(!this.singular && !e.shiftKey && this.selection[id]){ + this._doDeselect = true; + return; + }else{ + this._doDeselect = false; + } + this.userSelect(treeNode, copy, e.shiftKey); + }, + + onMouseUp: function(e){ + // summary: + // Event processor for onmouseup/ontouchend + // e: Event + // onmouseup/ontouchend event + // tags: + // protected + + // _doDeselect is the flag to indicate that the user wants to either ctrl+click on + // a already selected item (to deselect the item), or click on a not-yet selected item + // (which should remove all current selection, and add the clicked item). This can not + // be done in onMouseDown, because the user may start a drag after mousedown. By moving + // the deselection logic here, the user can drags an already selected item. + if(!this._doDeselect){ return; } + this._doDeselect = false; + this.userSelect(this.current, connect.isCopyKey(e), e.shiftKey); + }, + onMouseMove: function(/*===== e =====*/){ + // summary: + // event processor for onmousemove/ontouchmove + // e: Event + // onmousemove/ontouchmove event + this._doDeselect = false; + }, + + _compareNodes: function(n1, n2){ + if(n1 === n2){ + return 0; + } + + if('sourceIndex' in document.documentElement){ //IE + //TODO: does not yet work if n1 and/or n2 is a text node + return n1.sourceIndex - n2.sourceIndex; + }else if('compareDocumentPosition' in document.documentElement){ //FF, Opera + return n1.compareDocumentPosition(n2) & 2 ? 1: -1; + }else if(document.createRange){ //Webkit + var r1 = doc.createRange(); + r1.setStartBefore(n1); + + var r2 = doc.createRange(); + r2.setStartBefore(n2); + + return r1.compareBoundaryPoints(r1.END_TO_END, r2); + }else{ + throw Error("dijit.tree._compareNodes don't know how to compare two different nodes in this browser"); + } + }, + + userSelect: function(node, multi, range){ + // summary: + // Add or remove the given node from selection, responding + // to a user action such as a click or keypress. + // multi: Boolean + // Indicates whether this is meant to be a multi-select action (e.g. ctrl-click) + // range: Boolean + // Indicates whether this is meant to be a ranged action (e.g. shift-click) + // tags: + // protected + + if(this.singular){ + if(this.anchor == node && multi){ + this.selectNone(); + }else{ + this.setSelection([node]); + this.anchor = node; + } + }else{ + if(range && this.anchor){ + var cr = this._compareNodes(this.anchor.rowNode, node.rowNode), + begin, end, anchor = this.anchor; + + if(cr < 0){ //current is after anchor + begin = anchor; + end = node; + }else{ //current is before anchor + begin = node; + end = anchor; + } + var nodes = []; + //add everything betweeen begin and end inclusively + while(begin != end){ + nodes.push(begin); + begin = this.tree._getNextNode(begin); + } + nodes.push(end); + + this.setSelection(nodes); + }else{ + if( this.selection[ node.id ] && multi ){ + this.removeTreeNode( node ); + }else if(multi){ + this.addTreeNode(node, true); + }else{ + this.setSelection([node]); + this.anchor = node; + } + } + } + }, + + getItem: function(/*String*/ key){ + // summary: + // Returns the dojo.dnd.Item (representing a dragged node) by it's key (id). + // Called by dojo.dnd.Source.checkAcceptance(). + // tags: + // protected + + var widget = this.selection[key]; + return { + data: widget, + type: ["treeNode"] + }; // dojo.dnd.Item + }, + + forInSelectedItems: function(/*Function*/ f, /*Object?*/ o){ + // summary: + // Iterates over selected items; + // see `dojo.dnd.Container.forInItems()` for details + o = o || win.global; + for(var id in this.selection){ + // console.log("selected item id: " + id); + f.call(o, this.getItem(id), id, this); + } + } + }); +}); diff --git a/js/dojo-1.7.2/dijit/tree/dndSource.js b/js/dojo-1.7.2/dijit/tree/dndSource.js new file mode 100644 index 0000000..2986d5d --- /dev/null +++ b/js/dojo-1.7.2/dijit/tree/dndSource.js @@ -0,0 +1,565 @@ +//>>built +define("dijit/tree/dndSource", [ + "dojo/_base/array", // array.forEach array.indexOf array.map + "dojo/_base/connect", // isCopyKey + "dojo/_base/declare", // declare + "dojo/dom-class", // domClass.add + "dojo/dom-geometry", // domGeometry.position + "dojo/_base/lang", // lang.mixin lang.hitch + "dojo/on", // subscribe + "dojo/touch", + "dojo/topic", + "dojo/dnd/Manager", // DNDManager.manager + "./_dndSelector" +], function(array, connect, declare, domClass, domGeometry, lang, on, touch, topic, DNDManager, _dndSelector){ + +// module: +// dijit/tree/dndSource +// summary: +// Handles drag and drop operations (as a source or a target) for `dijit.Tree` + +/*===== +dijit.tree.__SourceArgs = function(){ + // summary: + // A dict of parameters for Tree source configuration. + // isSource: Boolean? + // Can be used as a DnD source. Defaults to true. + // accept: String[] + // List of accepted types (text strings) for a target; defaults to + // ["text", "treeNode"] + // copyOnly: Boolean? + // Copy items, if true, use a state of Ctrl key otherwise, + // dragThreshold: Number + // The move delay in pixels before detecting a drag; 0 by default + // betweenThreshold: Integer + // Distance from upper/lower edge of node to allow drop to reorder nodes + this.isSource = isSource; + this.accept = accept; + this.autoSync = autoSync; + this.copyOnly = copyOnly; + this.dragThreshold = dragThreshold; + this.betweenThreshold = betweenThreshold; +} +=====*/ + +return declare("dijit.tree.dndSource", _dndSelector, { + // summary: + // Handles drag and drop operations (as a source or a target) for `dijit.Tree` + + // isSource: [private] Boolean + // Can be used as a DnD source. + isSource: true, + + // accept: String[] + // List of accepted types (text strings) for the Tree; defaults to + // ["text"] + accept: ["text", "treeNode"], + + // copyOnly: [private] Boolean + // Copy items, if true, use a state of Ctrl key otherwise + copyOnly: false, + + // dragThreshold: Number + // The move delay in pixels before detecting a drag; 5 by default + dragThreshold: 5, + + // betweenThreshold: Integer + // Distance from upper/lower edge of node to allow drop to reorder nodes + betweenThreshold: 0, + + constructor: function(/*dijit.Tree*/ tree, /*dijit.tree.__SourceArgs*/ params){ + // summary: + // a constructor of the Tree DnD Source + // tags: + // private + if(!params){ params = {}; } + lang.mixin(this, params); + this.isSource = typeof params.isSource == "undefined" ? true : params.isSource; + var type = params.accept instanceof Array ? params.accept : ["text", "treeNode"]; + this.accept = null; + if(type.length){ + this.accept = {}; + for(var i = 0; i < type.length; ++i){ + this.accept[type[i]] = 1; + } + } + + // class-specific variables + this.isDragging = false; + this.mouseDown = false; + this.targetAnchor = null; // DOMNode corresponding to the currently moused over TreeNode + this.targetBox = null; // coordinates of this.targetAnchor + this.dropPosition = ""; // whether mouse is over/after/before this.targetAnchor + this._lastX = 0; + this._lastY = 0; + + // states + this.sourceState = ""; + if(this.isSource){ + domClass.add(this.node, "dojoDndSource"); + } + this.targetState = ""; + if(this.accept){ + domClass.add(this.node, "dojoDndTarget"); + } + + // set up events + this.topics = [ + topic.subscribe("/dnd/source/over", lang.hitch(this, "onDndSourceOver")), + topic.subscribe("/dnd/start", lang.hitch(this, "onDndStart")), + topic.subscribe("/dnd/drop", lang.hitch(this, "onDndDrop")), + topic.subscribe("/dnd/cancel", lang.hitch(this, "onDndCancel")) + ]; + }, + + // methods + checkAcceptance: function(/*===== source, nodes =====*/){ + // summary: + // Checks if the target can accept nodes from this source + // source: dijit.tree.dndSource + // The source which provides items + // nodes: DOMNode[] + // Array of DOM nodes corresponding to nodes being dropped, dijitTreeRow nodes if + // source is a dijit.Tree. + // tags: + // extension + return true; // Boolean + }, + + copyState: function(keyPressed){ + // summary: + // Returns true, if we need to copy items, false to move. + // It is separated to be overwritten dynamically, if needed. + // keyPressed: Boolean + // The "copy" control key was pressed + // tags: + // protected + return this.copyOnly || keyPressed; // Boolean + }, + destroy: function(){ + // summary: + // Prepares the object to be garbage-collected. + this.inherited(arguments); + var h; + while(h = this.topics.pop()){ h.remove(); } + this.targetAnchor = null; + }, + + _onDragMouse: function(e){ + // summary: + // Helper method for processing onmousemove/onmouseover events while drag is in progress. + // Keeps track of current drop target. + + var m = DNDManager.manager(), + oldTarget = this.targetAnchor, // the TreeNode corresponding to TreeNode mouse was previously over + newTarget = this.current, // TreeNode corresponding to TreeNode mouse is currently over + oldDropPosition = this.dropPosition; // the previous drop position (over/before/after) + + // calculate if user is indicating to drop the dragged node before, after, or over + // (i.e., to become a child of) the target node + var newDropPosition = "Over"; + if(newTarget && this.betweenThreshold > 0){ + // If mouse is over a new TreeNode, then get new TreeNode's position and size + if(!this.targetBox || oldTarget != newTarget){ + this.targetBox = domGeometry.position(newTarget.rowNode, true); + } + if((e.pageY - this.targetBox.y) <= this.betweenThreshold){ + newDropPosition = "Before"; + }else if((e.pageY - this.targetBox.y) >= (this.targetBox.h - this.betweenThreshold)){ + newDropPosition = "After"; + } + } + + if(newTarget != oldTarget || newDropPosition != oldDropPosition){ + if(oldTarget){ + this._removeItemClass(oldTarget.rowNode, oldDropPosition); + } + if(newTarget){ + this._addItemClass(newTarget.rowNode, newDropPosition); + } + + // Check if it's ok to drop the dragged node on/before/after the target node. + if(!newTarget){ + m.canDrop(false); + }else if(newTarget == this.tree.rootNode && newDropPosition != "Over"){ + // Can't drop before or after tree's root node; the dropped node would just disappear (at least visually) + m.canDrop(false); + }else{ + // Guard against dropping onto yourself (TODO: guard against dropping onto your descendant, #7140) + var model = this.tree.model, + sameId = false; + if(m.source == this){ + for(var dragId in this.selection){ + var dragNode = this.selection[dragId]; + if(dragNode.item === newTarget.item){ + sameId = true; + break; + } + } + } + if(sameId){ + m.canDrop(false); + }else if(this.checkItemAcceptance(newTarget.rowNode, m.source, newDropPosition.toLowerCase()) + && !this._isParentChildDrop(m.source, newTarget.rowNode)){ + m.canDrop(true); + }else{ + m.canDrop(false); + } + } + + this.targetAnchor = newTarget; + this.dropPosition = newDropPosition; + } + }, + + onMouseMove: function(e){ + // summary: + // Called for any onmousemove/ontouchmove events over the Tree + // e: Event + // onmousemouse/ontouchmove event + // tags: + // private + if(this.isDragging && this.targetState == "Disabled"){ return; } + this.inherited(arguments); + var m = DNDManager.manager(); + if(this.isDragging){ + this._onDragMouse(e); + }else{ + if(this.mouseDown && this.isSource && + (Math.abs(e.pageX-this._lastX)>=this.dragThreshold || Math.abs(e.pageY-this._lastY)>=this.dragThreshold)){ + var nodes = this.getSelectedTreeNodes(); + if(nodes.length){ + if(nodes.length > 1){ + //filter out all selected items which has one of their ancestor selected as well + var seen = this.selection, i = 0, r = [], n, p; + nextitem: while((n = nodes[i++])){ + for(p = n.getParent(); p && p !== this.tree; p = p.getParent()){ + if(seen[p.id]){ //parent is already selected, skip this node + continue nextitem; + } + } + //this node does not have any ancestors selected, add it + r.push(n); + } + nodes = r; + } + nodes = array.map(nodes, function(n){return n.domNode}); + m.startDrag(this, nodes, this.copyState(connect.isCopyKey(e))); + } + } + } + }, + + onMouseDown: function(e){ + // summary: + // Event processor for onmousedown/ontouchstart + // e: Event + // onmousedown/ontouchend event + // tags: + // private + this.mouseDown = true; + this.mouseButton = e.button; + this._lastX = e.pageX; + this._lastY = e.pageY; + this.inherited(arguments); + }, + + onMouseUp: function(e){ + // summary: + // Event processor for onmouseup/ontouchend + // e: Event + // onmouseup/ontouchend event + // tags: + // private + if(this.mouseDown){ + this.mouseDown = false; + this.inherited(arguments); + } + }, + + onMouseOut: function(){ + // summary: + // Event processor for when mouse is moved away from a TreeNode + // tags: + // private + this.inherited(arguments); + this._unmarkTargetAnchor(); + }, + + checkItemAcceptance: function(/*===== target, source, position =====*/){ + // summary: + // Stub function to be overridden if one wants to check for the ability to drop at the node/item level + // description: + // In the base case, this is called to check if target can become a child of source. + // When betweenThreshold is set, position="before" or "after" means that we + // are asking if the source node can be dropped before/after the target node. + // target: DOMNode + // The dijitTreeRoot DOM node inside of the TreeNode that we are dropping on to + // Use dijit.getEnclosingWidget(target) to get the TreeNode. + // source: dijit.tree.dndSource + // The (set of) nodes we are dropping + // position: String + // "over", "before", or "after" + // tags: + // extension + return true; + }, + + // topic event processors + onDndSourceOver: function(source){ + // summary: + // Topic event processor for /dnd/source/over, called when detected a current source. + // source: Object + // The dijit.tree.dndSource / dojo.dnd.Source which has the mouse over it + // tags: + // private + if(this != source){ + this.mouseDown = false; + this._unmarkTargetAnchor(); + }else if(this.isDragging){ + var m = DNDManager.manager(); + m.canDrop(false); + } + }, + onDndStart: function(source, nodes, copy){ + // summary: + // Topic event processor for /dnd/start, called to initiate the DnD operation + // source: Object + // The dijit.tree.dndSource / dojo.dnd.Source which is providing the items + // nodes: DomNode[] + // The list of transferred items, dndTreeNode nodes if dragging from a Tree + // copy: Boolean + // Copy items, if true, move items otherwise + // tags: + // private + + if(this.isSource){ + this._changeState("Source", this == source ? (copy ? "Copied" : "Moved") : ""); + } + var accepted = this.checkAcceptance(source, nodes); + + this._changeState("Target", accepted ? "" : "Disabled"); + + if(this == source){ + DNDManager.manager().overSource(this); + } + + this.isDragging = true; + }, + + itemCreator: function(nodes /*===== , target, source =====*/){ + // summary: + // Returns objects passed to `Tree.model.newItem()` based on DnD nodes + // dropped onto the tree. Developer must override this method to enable + // dropping from external sources onto this Tree, unless the Tree.model's items + // happen to look like {id: 123, name: "Apple" } with no other attributes. + // description: + // For each node in nodes[], which came from source, create a hash of name/value + // pairs to be passed to Tree.model.newItem(). Returns array of those hashes. + // nodes: DomNode[] + // target: DomNode + // source: dojo.dnd.Source + // returns: Object[] + // Array of name/value hashes for each new item to be added to the Tree, like: + // | [ + // | { id: 123, label: "apple", foo: "bar" }, + // | { id: 456, label: "pear", zaz: "bam" } + // | ] + // tags: + // extension + + // TODO: for 2.0 refactor so itemCreator() is called once per drag node, and + // make signature itemCreator(sourceItem, node, target) (or similar). + + return array.map(nodes, function(node){ + return { + "id": node.id, + "name": node.textContent || node.innerText || "" + }; + }); // Object[] + }, + + onDndDrop: function(source, nodes, copy){ + // summary: + // Topic event processor for /dnd/drop, called to finish the DnD operation. + // description: + // Updates data store items according to where node was dragged from and dropped + // to. The tree will then respond to those data store updates and redraw itself. + // source: Object + // The dijit.tree.dndSource / dojo.dnd.Source which is providing the items + // nodes: DomNode[] + // The list of transferred items, dndTreeNode nodes if dragging from a Tree + // copy: Boolean + // Copy items, if true, move items otherwise + // tags: + // protected + if(this.containerState == "Over"){ + var tree = this.tree, + model = tree.model, + target = this.targetAnchor; + + this.isDragging = false; + + // Compute the new parent item + var newParentItem; + var insertIndex; + newParentItem = (target && target.item) || tree.item; + if(this.dropPosition == "Before" || this.dropPosition == "After"){ + // TODO: if there is no parent item then disallow the drop. + // Actually this should be checked during onMouseMove too, to make the drag icon red. + newParentItem = (target.getParent() && target.getParent().item) || tree.item; + // Compute the insert index for reordering + insertIndex = target.getIndexInParent(); + if(this.dropPosition == "After"){ + insertIndex = target.getIndexInParent() + 1; + } + }else{ + newParentItem = (target && target.item) || tree.item; + } + + // If necessary, use this variable to hold array of hashes to pass to model.newItem() + // (one entry in the array for each dragged node). + var newItemsParams; + + array.forEach(nodes, function(node, idx){ + // dojo.dnd.Item representing the thing being dropped. + // Don't confuse the use of item here (meaning a DnD item) with the + // uses below where item means dojo.data item. + var sourceItem = source.getItem(node.id); + + // Information that's available if the source is another Tree + // (possibly but not necessarily this tree, possibly but not + // necessarily the same model as this Tree) + if(array.indexOf(sourceItem.type, "treeNode") != -1){ + var childTreeNode = sourceItem.data, + childItem = childTreeNode.item, + oldParentItem = childTreeNode.getParent().item; + } + + if(source == this){ + // This is a node from my own tree, and we are moving it, not copying. + // Remove item from old parent's children attribute. + // TODO: dijit.tree.dndSelector should implement deleteSelectedNodes() + // and this code should go there. + + if(typeof insertIndex == "number"){ + if(newParentItem == oldParentItem && childTreeNode.getIndexInParent() < insertIndex){ + insertIndex -= 1; + } + } + model.pasteItem(childItem, oldParentItem, newParentItem, copy, insertIndex); + }else if(model.isItem(childItem)){ + // Item from same model + // (maybe we should only do this branch if the source is a tree?) + model.pasteItem(childItem, oldParentItem, newParentItem, copy, insertIndex); + }else{ + // Get the hash to pass to model.newItem(). A single call to + // itemCreator() returns an array of hashes, one for each drag source node. + if(!newItemsParams){ + newItemsParams = this.itemCreator(nodes, target.rowNode, source); + } + + // Create new item in the tree, based on the drag source. + model.newItem(newItemsParams[idx], newParentItem, insertIndex); + } + }, this); + + // Expand the target node (if it's currently collapsed) so the user can see + // where their node was dropped. In particular since that node is still selected. + this.tree._expandNode(target); + } + this.onDndCancel(); + }, + + onDndCancel: function(){ + // summary: + // Topic event processor for /dnd/cancel, called to cancel the DnD operation + // tags: + // private + this._unmarkTargetAnchor(); + this.isDragging = false; + this.mouseDown = false; + delete this.mouseButton; + this._changeState("Source", ""); + this._changeState("Target", ""); + }, + + // When focus moves in/out of the entire Tree + onOverEvent: function(){ + // summary: + // This method is called when mouse is moved over our container (like onmouseenter) + // tags: + // private + this.inherited(arguments); + DNDManager.manager().overSource(this); + }, + onOutEvent: function(){ + // summary: + // This method is called when mouse is moved out of our container (like onmouseleave) + // tags: + // private + this._unmarkTargetAnchor(); + var m = DNDManager.manager(); + if(this.isDragging){ + m.canDrop(false); + } + m.outSource(this); + + this.inherited(arguments); + }, + + _isParentChildDrop: function(source, targetRow){ + // summary: + // Checks whether the dragged items are parent rows in the tree which are being + // dragged into their own children. + // + // source: + // The DragSource object. + // + // targetRow: + // The tree row onto which the dragged nodes are being dropped. + // + // tags: + // private + + // If the dragged object is not coming from the tree this widget belongs to, + // it cannot be invalid. + if(!source.tree || source.tree != this.tree){ + return false; + } + + + var root = source.tree.domNode; + var ids = source.selection; + + var node = targetRow.parentNode; + + // Iterate up the DOM hierarchy from the target drop row, + // checking of any of the dragged nodes have the same ID. + while(node != root && !ids[node.id]){ + node = node.parentNode; + } + + return node.id && ids[node.id]; + }, + + _unmarkTargetAnchor: function(){ + // summary: + // Removes hover class of the current target anchor + // tags: + // private + if(!this.targetAnchor){ return; } + this._removeItemClass(this.targetAnchor.rowNode, this.dropPosition); + this.targetAnchor = null; + this.targetBox = null; + this.dropPosition = null; + }, + + _markDndStatus: function(copy){ + // summary: + // Changes source's state based on "copy" status + this._changeState("Source", copy ? "Copied" : "Moved"); + } +}); + +}); diff --git a/js/dojo-1.7.2/dijit/tree/model.js b/js/dojo-1.7.2/dijit/tree/model.js new file mode 100644 index 0000000..57abdc6 --- /dev/null +++ b/js/dojo-1.7.2/dijit/tree/model.js @@ -0,0 +1,143 @@ +//>>built + +/*===== +declare( + "dijit.tree.model", + null, +{ + // summary: + // Contract for any data provider object for the tree. + // description: + // Tree passes in values to the constructor to specify the callbacks. + // "item" is typically a dojo.data.Item but it's just a black box so + // it could be anything. + // + // This (like `dojo.data.api.Read`) is just documentation, and not meant to be used. + + destroy: function(){ + // summary: + // Destroys this object, releasing connections to the store + // tags: + // extension + }, + + // ======================================================================= + // Methods for traversing hierarchy + + getRoot: function(onItem){ + // summary: + // Calls onItem with the root item for the tree, possibly a fabricated item. + // Throws exception on error. + // tags: + // extension + }, + + mayHaveChildren: function(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) + // item: dojo.data.Item + // tags: + // extension + }, + + getChildren: function(parentItem, onComplete){ + // summary: + // Calls onComplete() with array of child items of given parent item, all loaded. + // Throws exception on error. + // parentItem: dojo.data.Item + // onComplete: function(items) + // tags: + // extension + }, + + // ======================================================================= + // Inspecting items + + isItem: function(something){ + // summary: + // Returns true if *something* is an item and came from this model instance. + // Returns false if *something* is a literal, an item from another model instance, + // or is any object other than an item. + // tags: + // extension + }, + + fetchItemByIdentity: function(keywordArgs){ + // summary: + // Given the identity of an item, this method returns the item that has + // that identity through the onItem callback. Conforming implementations + // should return null if there is no item with the given identity. + // Implementations of fetchItemByIdentity() may sometimes return an item + // from a local cache and may sometimes fetch an item from a remote server. + // tags: + // extension + }, + + getIdentity: function(item){ + // summary: + // Returns identity for an item + // tags: + // extension + }, + + getLabel: function(item){ + // summary: + // Get the label for an item + // tags: + // extension + }, + + // ======================================================================= + // Write interface + + newItem: function(args, parent, insertIndex){ + // summary: + // Creates a new item. See `dojo.data.api.Write` for details on args. + // args: dojo.dnd.Item + // parent: Item + // insertIndex: int? + // tags: + // extension + }, + + pasteItem: function(childItem, oldParentItem, newParentItem, bCopy){ + // summary: + // Move or copy an item from one parent item to another. + // Used in drag & drop. + // If oldParentItem is specified and bCopy is false, childItem is removed from oldParentItem. + // If newParentItem is specified, childItem is attached to newParentItem. + // childItem: Item + // oldParentItem: Item + // newParentItem: Item + // bCopy: Boolean + // tags: + // extension + }, + + // ======================================================================= + // Callbacks + + onChange: function(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. + // item: dojo.data.Item + // tags: + // callback + }, + + onChildrenChange: function(parent, newChildrenList){ + // summary: + // Callback to do notifications about new, updated, or deleted items. + // parent: dojo.data.Item + // newChildrenList: dojo.data.Item[] + // tags: + // callback + } +}); +=====*/
\ No newline at end of file |
