--- /dev/null
+/*!
+ * Ext JS Library 3.4.0
+ * Copyright(c) 2006-2011 Sencha Inc.
+ * licensing@sencha.com
+ * http://www.sencha.com/license
+ */
+/**
+ * @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();
+ }
+});
+
+}
\ No newline at end of file