cacert-testmgr/external/ZendFramework-1.9.5/externals/dojo/dijit/_tree/dndSource.js
Markus Warg 8398c9048d initially import ZendFramework-1.9.5 into repository
code was modified slightly, so the code differs from the original downloadable 1.9.5 version
2010-03-31 10:12:32 +02:00

502 lines
15 KiB
JavaScript

dojo.provide("dijit._tree.dndSource");
dojo.require("dijit._tree.dndSelector");
dojo.require("dojo.dnd.Manager");
/*=====
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"]
// 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;
}
=====*/
dojo.declare("dijit._tree.dndSource", dijit._tree.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"],
// 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; 0 by default
dragThreshold: 0,
// betweenThreshold: Integer
// Distance from upper/lower edge of node to allow drop to reorder nodes
betweenThreshold: 0,
// skipForm: [private] Boolean
// TODO: apparently unused, should be removed
skipForm: false,
constructor: function(/*dijit.Tree*/ tree, /*dijit._tree.__SourceArgs*/ params){
// summary:
// a constructor of the Tree DnD Source
// tags:
// private
if(!params){ params = {}; }
dojo.mixin(this, params);
this.isSource = typeof params.isSource == "undefined" ? true : params.isSource;
var type = params.accept instanceof Array ? params.accept : ["text"];
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){
dojo.addClass(this.node, "dojoDndSource");
}
this.targetState = "";
if(this.accept){
dojo.addClass(this.node, "dojoDndTarget");
}
// set up events
this.topics = [
dojo.subscribe("/dnd/source/over", this, "onDndSourceOver"),
dojo.subscribe("/dnd/start", this, "onDndStart"),
dojo.subscribe("/dnd/drop", this, "onDndDrop"),
dojo.subscribe("/dnd/cancel", this, "onDndCancel")
];
this.events.push(
// monitor mouse movements to tell when drag starts etc.
dojo.connect(this.node, "onmousemove", this, "onMouseMove")
);
},
startup: function(){
// summary:
// Apparently unused. TODO: remove
// tags:
// private
},
// 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("destroy",arguments);
dojo.forEach(this.topics, dojo.unsubscribe);
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 = dojo.dnd.manager(),
oldTarget = this.targetAnchor, // the DOMNode corresponding to TreeNode mouse was previously over
newTarget = this.current, // DOMNode corresponding to TreeNode mouse is currently over
newTargetWidget = this.currentWidget, // the TreeNode itself
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 = {
xy: dojo.coords(newTarget, true),
w: newTarget.offsetWidth,
h: newTarget.offsetHeight
};
}
if((e.pageY - this.targetBox.xy.y) <= this.betweenThreshold){
newDropPosition = "Before";
}else if((e.pageY - this.targetBox.xy.y) >= (this.targetBox.h - this.betweenThreshold)){
newDropPosition = "After";
}
}
if(newTarget != oldTarget || newDropPosition != oldDropPosition){
if(oldTarget){
this._removeItemClass(oldTarget, oldDropPosition);
}
if(newTarget){
this._addItemClass(newTarget, newDropPosition);
}
// Check if it's ok to drop the dragged node on/before/after the target node.
if(!newTarget){
m.canDrop(false);
}else if(newTargetWidget == 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 if(m.source == this && (newTarget.id in this.selection)){
// Guard against dropping onto yourself (TODO: guard against dropping onto your descendant, #7140)
m.canDrop(false);
}else if(this.checkItemAcceptance(newTarget, m.source, newDropPosition.toLowerCase())){
m.canDrop(true);
}else{
m.canDrop(false);
}
this.targetAnchor = newTarget;
this.dropPosition = newDropPosition;
}
},
onMouseMove: function(e){
// summary:
// Called for any onmousemove events over the Tree
// e: Event
// onmousemouse event
// tags:
// private
if(this.isDragging && this.targetState == "Disabled"){ return; }
var m = dojo.dnd.manager();
if(this.isDragging){
if(this.betweenThreshold > 0){
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 n = this.getSelectedNodes();
var nodes=[];
for (var i in n){
nodes.push(n[i]);
}
if(nodes.length){
m.startDrag(this, nodes, this.copyState(dojo.dnd.getCopyKeyState(e)));
}
}
}
},
onMouseDown: function(e){
// summary:
// Event processor for onmousedown
// e: Event
// onmousedown event
// tags:
// private
this.mouseDown = true;
this.mouseButton = e.button;
this._lastX = e.pageX;
this._lastY = e.pageY;
this.inherited("onMouseDown",arguments);
},
onMouseUp: function(e){
// summary:
// Event processor for onmouseup
// e: Event
// onmouseup event
// tags:
// private
if(this.mouseDown){
this.mouseDown = false;
this.inherited("onMouseUp",arguments);
}
},
onMouseOver: function(/*TreeNode*/ widget, /*Event*/ e){
// summary:
// Event processor for when mouse is moved over a TreeNode
// tags:
// private
this.inherited(arguments);
if(this.isDragging){
this._onDragMouse(e);
}
},
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 = dojo.dnd.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(accepted){
dojo.dnd.manager().overSource(this);
}
this.isDragging = true;
},
itemCreator: function(nodes){
// summary:
// Returns the "item" passed to the drop target
// (which is not necessarily a Tree, could be anything)
// tags:
// protected
return dojo.map(nodes, function(node){
return {
"id": node.id,
"name": node.textContent || node.innerText || ""
};
});
},
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,
requeryRoot = false; // set to true iff top level items change
this.isDragging = false;
// Compute the new parent item
var targetWidget = dijit.getEnclosingWidget(target);
var newParentItem;
var insertIndex;
newParentItem = (targetWidget && targetWidget.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 = (targetWidget.getParent() && targetWidget.getParent().item) || tree.item;
// Compute the insert index for reordering
insertIndex = targetWidget.getIndexInParent();
if(this.dropPosition == "After"){
insertIndex = targetWidget.getIndexInParent() + 1;
}
}else{
newParentItem = (targetWidget && targetWidget.item) || tree.item;
}
// If we are dragging from another source (or at least, another source
// that points to a different data store), then we need to make new data
// store items for each element in nodes[]. This call get the parameters
// to pass to store.newItem()
var newItemsParams;
if(source != this){
newItemsParams = this.itemCreator(nodes, target);
}
dojo.forEach(nodes, function(node, idx){
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.
var childTreeNode = dijit.getEnclosingWidget(node),
childItem = childTreeNode.item,
oldParentItem = childTreeNode.getParent().item;
if(typeof insertIndex == "number"){
if(newParentItem == oldParentItem && childTreeNode.getIndexInParent() < insertIndex){
insertIndex -= 1;
}
}
model.pasteItem(childItem, oldParentItem, newParentItem, copy, insertIndex);
}else{
model.newItem(newItemsParams[idx], newParentItem);
}
}, 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(targetWidget);
}
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);
dojo.dnd.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 = dojo.dnd.manager();
if(this.isDragging){
m.canDrop(false);
}
m.outSource(this);
this.inherited(arguments);
},
_unmarkTargetAnchor: function(){
// summary:
// Removes hover class of the current target anchor
// tags:
// private
if(!this.targetAnchor){ return; }
this._removeItemClass(this.targetAnchor, 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");
}
});
dojo.declare("dijit._tree.dndTarget", dijit._tree.dndSource, {
// summary:
// A Target object, which can be used as a DnD target
// TODO: Remove. dijit._tree.dndSource can also be used as a DnD target,
// so this class doesn't seem useful.
constructor: function(node, params){
// summary:
// A constructor of the Target --- see the Source constructor for details
this.isSource = false;
dojo.removeClass(this.node, "dojoDndSource");
}
});