/** * @class Ext.tree.TreeDropZone * @extends Ext.dd.DropZone * @constructor * @param {String/HTMLElement/Element} tree The {@link Ext.tree.TreePanel} for which to enable dropping * @param {Object} config */ if(Ext.dd.DropZone){ Ext.tree.TreeDropZone = function(tree, config){
/** * @cfg {Boolean} allowParentInsert * Allow inserting a dragged node between an expanded parent node and its first child that will become a * sibling of the parent when dropped (defaults to false) */ this.allowParentInsert = config.allowParentInsert || false;
/** * @cfg {String} allowContainerDrop * True if drops on the tree container (outside of a specific tree node) are allowed (defaults to false) */ this.allowContainerDrop = config.allowContainerDrop || false;
/** * @cfg {String} appendOnly * True if the tree should only allow append drops (use for trees which are sorted, defaults to false) */ this.appendOnly = config.appendOnly || false; Ext.tree.TreeDropZone.superclass.constructor.call(this, tree.getTreeEl(), config);
/** * The TreePanel for this drop zone * @type Ext.tree.TreePanel * @property */ this.tree = tree;
/** * Arbitrary data that can be associated with this tree and will be included in the event object that gets * passed to any nodedragover event handler (defaults to {}) * @type Ext.tree.TreePanel * @property */ this.dragOverData = {}; // private this.lastInsertClass = "x-tree-no-status"; }; Ext.extend(Ext.tree.TreeDropZone, Ext.dd.DropZone, {
/** * @cfg {String} ddGroup * A named drag drop group to which this object belongs. If a group is specified, then this object will only * interact with other drag drop objects in the same group (defaults to 'TreeDD'). */ ddGroup : "TreeDD",
/** * @cfg {String} expandDelay * The delay in milliseconds to wait before expanding a target tree node while dragging a droppable node * over the target (defaults to 1000) */ expandDelay : 1000, // private expandNode : function(node){ if(node.hasChildNodes() && !node.isExpanded()){ node.expand(false, null, this.triggerCacheRefresh.createDelegate(this)); } }, // private queueExpand : function(node){ this.expandProcId = this.expandNode.defer(this.expandDelay, this, [node]); }, // private cancelExpand : function(){ if(this.expandProcId){ clearTimeout(this.expandProcId); this.expandProcId = false; } }, // private isValidDropPoint : function(n, pt, dd, e, data){ if(!n || !data){ return false; } var targetNode = n.node; var dropNode = data.node; // default drop rules if(!(targetNode && targetNode.isTarget && pt)){ return false; } if(pt == "append" && targetNode.allowChildren === false){ return false; } if((pt == "above" || pt == "below") && (targetNode.parentNode && targetNode.parentNode.allowChildren === false)){ return false; } if(dropNode && (targetNode == dropNode || dropNode.contains(targetNode))){ return false; } // reuse the object var overEvent = this.dragOverData; overEvent.tree = this.tree; overEvent.target = targetNode; overEvent.data = data; overEvent.point = pt; overEvent.source = dd; overEvent.rawEvent = e; overEvent.dropNode = dropNode; overEvent.cancel = false; var result = this.tree.fireEvent("nodedragover", overEvent); return overEvent.cancel === false && result !== false; }, // private getDropPoint : function(e, n, dd){ var tn = n.node; if(tn.isRoot){ return tn.allowChildren !== false ? "append" : false; // always append for root } var dragEl = n.ddel; var t = Ext.lib.Dom.getY(dragEl), b = t + dragEl.offsetHeight; var y = Ext.lib.Event.getPageY(e); var noAppend = tn.allowChildren === false || tn.isLeaf(); if(this.appendOnly || tn.parentNode.allowChildren === false){ return noAppend ? false : "append"; } var noBelow = false; if(!this.allowParentInsert){ noBelow = tn.hasChildNodes() && tn.isExpanded(); } var q = (b - t) / (noAppend ? 2 : 3); if(y >= t && y < (t + q)){ return "above"; }else if(!noBelow && (noAppend || y >= b-q && y <= b)){ return "below"; }else{ return "append"; } }, // private onNodeEnter : function(n, dd, e, data){ this.cancelExpand(); }, onContainerOver : function(dd, e, data) { if (this.allowContainerDrop && this.isValidDropPoint({ ddel: this.tree.getRootNode().ui.elNode, node: this.tree.getRootNode() }, "append", dd, e, data)) { return this.dropAllowed; } return this.dropNotAllowed; }, // private onNodeOver : function(n, dd, e, data){ var pt = this.getDropPoint(e, n, dd); var node = n.node; // auto node expand check if(!this.expandProcId && pt == "append" && node.hasChildNodes() && !n.node.isExpanded()){ this.queueExpand(node); }else if(pt != "append"){ this.cancelExpand(); } // set the insert point style on the target node var returnCls = this.dropNotAllowed; if(this.isValidDropPoint(n, pt, dd, e, data)){ if(pt){ var el = n.ddel; var cls; if(pt == "above"){ returnCls = n.node.isFirst() ? "x-tree-drop-ok-above" : "x-tree-drop-ok-between"; cls = "x-tree-drag-insert-above"; }else if(pt == "below"){ returnCls = n.node.isLast() ? "x-tree-drop-ok-below" : "x-tree-drop-ok-between"; cls = "x-tree-drag-insert-below"; }else{ returnCls = "x-tree-drop-ok-append"; cls = "x-tree-drag-append"; } if(this.lastInsertClass != cls){ Ext.fly(el).replaceClass(this.lastInsertClass, cls); this.lastInsertClass = cls; } } } return returnCls; }, // private onNodeOut : function(n, dd, e, data){ this.cancelExpand(); this.removeDropIndicators(n); }, // private onNodeDrop : function(n, dd, e, data){ var point = this.getDropPoint(e, n, dd); var targetNode = n.node; targetNode.ui.startDrop(); if(!this.isValidDropPoint(n, point, dd, e, data)){ targetNode.ui.endDrop(); return false; } // first try to find the drop node var dropNode = data.node || (dd.getTreeNode ? dd.getTreeNode(data, targetNode, point, e) : null); return this.processDrop(targetNode, data, point, dd, e, dropNode); }, onContainerDrop : function(dd, e, data){ if (this.allowContainerDrop && this.isValidDropPoint({ ddel: this.tree.getRootNode().ui.elNode, node: this.tree.getRootNode() }, "append", dd, e, data)) { var targetNode = this.tree.getRootNode(); targetNode.ui.startDrop(); var dropNode = data.node || (dd.getTreeNode ? dd.getTreeNode(data, targetNode, 'append', e) : null); return this.processDrop(targetNode, data, 'append', dd, e, dropNode); } return false; }, // private processDrop: function(target, data, point, dd, e, dropNode){ var dropEvent = { tree : this.tree, target: target, data: data, point: point, source: dd, rawEvent: e, dropNode: dropNode, cancel: !dropNode, dropStatus: false }; var retval = this.tree.fireEvent("beforenodedrop", dropEvent); if(retval === false || dropEvent.cancel === true || !dropEvent.dropNode){ target.ui.endDrop(); return dropEvent.dropStatus; } target = dropEvent.target; if(point == 'append' && !target.isExpanded()){ target.expand(false, null, function(){ this.completeDrop(dropEvent); }.createDelegate(this)); }else{ this.completeDrop(dropEvent); } return true; }, // private completeDrop : function(de){ var ns = de.dropNode, p = de.point, t = de.target; if(!Ext.isArray(ns)){ ns = [ns]; } var n; for(var i = 0, len = ns.length; i < len; i++){ n = ns[i]; if(p == "above"){ t.parentNode.insertBefore(n, t); }else if(p == "below"){ t.parentNode.insertBefore(n, t.nextSibling); }else{ t.appendChild(n); } } n.ui.focus(); if(Ext.enableFx && this.tree.hlDrop){ n.ui.highlight(); } t.ui.endDrop(); this.tree.fireEvent("nodedrop", de); }, // private afterNodeMoved : function(dd, data, e, targetNode, dropNode){ if(Ext.enableFx && this.tree.hlDrop){ dropNode.ui.focus(); dropNode.ui.highlight(); } this.tree.fireEvent("nodedrop", this.tree, targetNode, data, dd, e); }, // private getTree : function(){ return this.tree; }, // private removeDropIndicators : function(n){ if(n && n.ddel){ var el = n.ddel; Ext.fly(el).removeClass([ "x-tree-drag-insert-above", "x-tree-drag-insert-below", "x-tree-drag-append"]); this.lastInsertClass = "_noclass"; } }, // private beforeDragDrop : function(target, e, id){ this.cancelExpand(); return true; }, // private afterRepair : function(data){ if(data && Ext.enableFx){ data.node.ui.highlight(); } this.hideProxy(); } }); }