//>>built define("dojox/grid/enhanced/plugins/DnD", [ "dojo/_base/kernel", "dojo/_base/declare", "dojo/_base/connect", "dojo/_base/array", "dojo/_base/lang", "dojo/_base/html", "dojo/_base/json", "dojo/_base/window", "dojo/query", "dojo/keys", "dojo/dnd/Source", "dojo/dnd/Avatar", "../_Plugin", "../../EnhancedGrid", "./Selector", "./Rearrange", "dojo/dnd/Manager" ], function(dojo, declare, connect, array, lang, html, json, win, query, keys, Source, Avatar, _Plugin, EnhancedGrid){ var _devideToArrays = function(a){ a.sort(function(v1, v2){ return v1 - v2; }); var arr = [[a[0]]]; for(var i = 1, j = 0; i < a.length; ++i){ if(a[i] == a[i-1] + 1){ arr[j].push(a[i]); }else{ arr[++j] = [a[i]]; } } return arr; }, _joinToArray = function(arrays){ var a = arrays[0]; for(var i = 1; i < arrays.length; ++i){ a = a.concat(arrays[i]); } return a; }; var GridDnDElement = declare("dojox.grid.enhanced.plugins.GridDnDElement", null, { constructor: function(dndPlugin){ this.plugin = dndPlugin; this.node = html.create("div"); this._items = {}; }, destroy: function(){ this.plugin = null; html.destroy(this.node); this.node = null; this._items = null; }, createDnDNodes: function(dndRegion){ this.destroyDnDNodes(); var acceptType = ["grid/" + dndRegion.type + "s"]; var itemNodeIdBase = this.plugin.grid.id + "_dndItem"; array.forEach(dndRegion.selected, function(range, i){ var id = itemNodeIdBase + i; this._items[id] = { "type": acceptType, "data": range, "dndPlugin": this.plugin }; this.node.appendChild(html.create("div", { "id": id })); }, this); }, getDnDNodes: function(){ return array.map(this.node.childNodes, function(node){ return node; }); }, destroyDnDNodes: function(){ html.empty(this.node); this._items = {}; }, getItem: function(nodeId){ return this._items[nodeId]; } }); var GridDnDSource = declare("dojox.grid.enhanced.plugins.GridDnDSource", Source,{ accept: ["grid/cells", "grid/rows", "grid/cols"], constructor: function(node, param){ this.grid = param.grid; this.dndElem = param.dndElem; this.dndPlugin = param.dnd; this.sourcePlugin = null; }, destroy: function(){ this.inherited(arguments); this.grid = null; this.dndElem = null; this.dndPlugin = null; this.sourcePlugin = null; }, getItem: function(nodeId){ return this.dndElem.getItem(nodeId); }, checkAcceptance: function(source, nodes){ if(this != source && nodes[0]){ var item = source.getItem(nodes[0].id); if(item.dndPlugin){ var type = item.type; for(var j = 0; j < type.length; ++j){ if(type[j] in this.accept){ if(this.dndPlugin._canAccept(item.dndPlugin)){ this.sourcePlugin = item.dndPlugin; }else{ return false; } break; } } }else if("grid/rows" in this.accept){ var rows = []; array.forEach(nodes, function(node){ var item = source.getItem(node.id); if(item.data && array.indexOf(item.type, "grid/rows") >= 0){ var rowData = item.data; if(typeof item.data == "string"){ rowData = json.fromJson(item.data); } if(rowData){ rows.push(rowData); } } }); if(rows.length){ this.sourcePlugin = { _dndRegion: { type: "row", selected: [rows] } }; }else{ return false; } } } return this.inherited(arguments); }, onDraggingOver: function(){ this.dndPlugin.onDraggingOver(this.sourcePlugin); }, onDraggingOut: function(){ this.dndPlugin.onDraggingOut(this.sourcePlugin); }, onDndDrop: function(source, nodes, copy, target){ //this.inherited(arguments); this.onDndCancel(); if(this != source && this == target){ this.dndPlugin.onDragIn(this.sourcePlugin, copy); } } }); var GridDnDAvatar = declare("dojox.grid.enhanced.plugins.GridDnDAvatar", Avatar, { construct: function(){ // summary: // constructor function; // it is separate so it can be (dynamically) overwritten in case of need this._itemType = this.manager._dndPlugin._dndRegion.type; this._itemCount = this._getItemCount(); this.isA11y = html.hasClass(win.body(), "dijit_a11y"); var a = html.create("table", { "border": "0", "cellspacing": "0", "class": "dojoxGridDndAvatar", "style": { position: "absolute", zIndex: "1999", margin: "0px" } }), source = this.manager.source, b = html.create("tbody", null, a), tr = html.create("tr", null, b), td = html.create("td", { "class": "dojoxGridDnDIcon" }, tr); if(this.isA11y){ html.create("span", { "id" : "a11yIcon", "innerHTML" : this.manager.copy ? '+' : "<" }, td); } td = html.create("td", { "class" : "dojoxGridDnDItemIcon " + this._getGridDnDIconClass() }, tr); td = html.create("td", null, tr); html.create("span", { "class": "dojoxGridDnDItemCount", "innerHTML": source.generateText ? this._generateText() : "" }, td); // we have to set the opacity on IE only after the node is live html.style(tr, { "opacity": 0.9 }); this.node = a; }, _getItemCount: function(){ var selected = this.manager._dndPlugin._dndRegion.selected, count = 0; switch(this._itemType){ case "cell": selected = selected[0]; var cells = this.manager._dndPlugin.grid.layout.cells, colCount = selected.max.col - selected.min.col + 1, rowCount = selected.max.row - selected.min.row + 1; if(colCount > 1){ for(var i = selected.min.col; i <= selected.max.col; ++i){ if(cells[i].hidden){ --colCount; } } } count = colCount * rowCount; break; case "row": case "col": count = _joinToArray(selected).length; } return count; }, _getGridDnDIconClass: function(){ return { "row": ["dojoxGridDnDIconRowSingle", "dojoxGridDnDIconRowMulti"], "col": ["dojoxGridDnDIconColSingle", "dojoxGridDnDIconColMulti"], "cell": ["dojoxGridDnDIconCellSingle", "dojoxGridDnDIconCellMulti"] }[this._itemType][this._itemCount == 1 ? 0 : 1]; }, _generateText: function(){ // summary: // generates a proper text to reflect copying or moving of items return "(" + this._itemCount + ")"; } }); var DnD = declare("dojox.grid.enhanced.plugins.DnD", _Plugin, { // summary: // Provide drag and drop for grid columns/rows/cells within grid and out of grid. // The store of grid must implement dojo.data.api.Write. // DnD selected columns: // Support moving within grid, moving/copying out of grid to a non-grid DnD target. // DnD selected rows: // Support moving within grid, moving/copying out of grid to any DnD target. // DnD selected cells (in rectangle shape only): // Support moving/copying within grid, moving/copying out of grid to any DnD target. // // name: String, // plugin name; name: "dnd", _targetAnchorBorderWidth: 2, _copyOnly: false, _config: { "row":{ "within":true, "in":true, "out":true }, "col":{ "within":true, "in":true, "out":true }, "cell":{ "within":true, "in":true, "out":true } }, constructor: function(grid, args){ this.grid = grid; this._config = lang.clone(this._config); args = lang.isObject(args) ? args : {}; this.setupConfig(args.dndConfig); this._copyOnly = !!args.copyOnly; //Get the plugins we are dependent on. this._mixinGrid(); this.selector = grid.pluginMgr.getPlugin("selector"); this.rearranger = grid.pluginMgr.getPlugin("rearrange"); //TODO: waiting for a better plugin framework to pass args to dependent plugins. this.rearranger.setArgs(args); //Initialized the components we need. this._clear(); this._elem = new GridDnDElement(this); this._source = new GridDnDSource(this._elem.node, { "grid": grid, "dndElem": this._elem, "dnd": this }); this._container = query(".dojoxGridMasterView", this.grid.domNode)[0]; this._initEvents(); }, destroy: function(){ this.inherited(arguments); this._clear(); this._source.destroy(); this._elem.destroy(); this._container = null; this.grid = null; this.selector = null; this.rearranger = null; this._config = null; }, _mixinGrid: function(){ // summary: // Provide APIs for grid. this.grid.setupDnDConfig = lang.hitch(this, "setupConfig"); this.grid.dndCopyOnly = lang.hitch(this, "copyOnly"); }, setupConfig: function(config){ // summary: // Configure which DnD functionalities are needed. // Combination of any item from type set ("row", "col", "cell") // and any item from mode set("within", "in", "out") is configurable. // // "row", "col", "cell" are straitforward, while the other 3 are explained below: // "within": DnD within grid, that is, column/row reordering and cell moving/copying. // "in": Whether allowed to accept rows/cells (currently not support columns) from another grid. // "out": Whether allowed to drag out of grid, to another grid or even to any other DnD target. // // If not provided in the config, will use the default. // When declared together, Mode set has higher priority than type set. // config: Object // DnD configuration object. // See the examples below. // example: // The following code disables row DnD within grid, // but still can drag rows out of grid or drag rows from other gird. // | setUpConfig({ // | "row": { // | "within": false // | } // | }); // // The opposite way is also okay: // | setUpConfig({ // | "within": { // | "row": false // | } // | }); // // And if you'd like to disable/enable a whole set, here's a shortcut: // | setUpConfig({ // | "cell", true, // | "out": false // | }); // // Because mode has higher priority than type, the following will disable row dnd within grid: // | setUpConfig({ // | "within", { // | "row": false; // | }, // | "row", { // | "within": true // | } // | }); if(config && lang.isObject(config)){ var firstLevel = ["row", "col", "cell"], secondLevel = ["within", "in", "out"], cfg = this._config; array.forEach(firstLevel, function(type){ if(type in config){ var t = config[type]; if(t && lang.isObject(t)){ array.forEach(secondLevel, function(mode){ if(mode in t){ cfg[type][mode] = !!t[mode]; } }); }else{ array.forEach(secondLevel, function(mode){ cfg[type][mode] = !!t; }); } } }); array.forEach(secondLevel, function(mode){ if(mode in config){ var m = config[mode]; if(m && lang.isObject(m)){ array.forEach(firstLevel, function(type){ if(type in m){ cfg[type][mode] = !!m[type]; } }); }else{ array.forEach(firstLevel, function(type){ cfg[type][mode] = !!m; }); } } }); } }, copyOnly: function(isCopyOnly){ // summary: // Setter/getter of this._copyOnly. if(typeof isCopyOnly != "undefined"){ this._copyOnly = !!isCopyOnly; } return this._copyOnly; }, _isOutOfGrid: function(evt){ var gridPos = html.position(this.grid.domNode), x = evt.clientX, y = evt.clientY; return y < gridPos.y || y > gridPos.y + gridPos.h || x < gridPos.x || x > gridPos.x + gridPos.w; }, _onMouseMove: function(evt){ if(this._dndRegion && !this._dnding && !this._externalDnd){ this._dnding = true; this._startDnd(evt); }else{ if(this._isMouseDown && !this._dndRegion){ delete this._isMouseDown; this._oldCursor = html.style(win.body(), "cursor"); html.style(win.body(), "cursor", "not-allowed"); } //TODO: should implement as mouseenter/mouseleave //But we have an avatar under mouse when dnd, and this will cause a lot of mouseenter in FF. var isOut = this._isOutOfGrid(evt); if(!this._alreadyOut && isOut){ this._alreadyOut = true; if(this._dnding){ this._destroyDnDUI(true, false); } this._moveEvent = evt; this._source.onOutEvent(); }else if(this._alreadyOut && !isOut){ this._alreadyOut = false; if(this._dnding){ this._createDnDUI(evt, true); } this._moveEvent = evt; this._source.onOverEvent(); } } }, _onMouseUp: function(){ if(!this._extDnding && !this._isSource){ var isInner = this._dnding && !this._alreadyOut; if(isInner && this._config[this._dndRegion.type]["within"]){ this._rearrange(); } this._endDnd(isInner); } html.style(win.body(), "cursor", this._oldCursor || ""); delete this._isMouseDown; }, _initEvents: function(){ var g = this.grid, s = this.selector; this.connect(win.doc, "onmousemove", "_onMouseMove"); this.connect(win.doc, "onmouseup", "_onMouseUp"); this.connect(g, "onCellMouseOver", function(evt){ if(!this._dnding && !s.isSelecting() && !evt.ctrlKey){ this._dndReady = s.isSelected("cell", evt.rowIndex, evt.cell.index); s.selectEnabled(!this._dndReady); } }); this.connect(g, "onHeaderCellMouseOver", function(evt){ if(this._dndReady){ s.selectEnabled(true); } }); this.connect(g, "onRowMouseOver", function(evt){ if(this._dndReady && !evt.cell){ s.selectEnabled(true); } }); this.connect(g, "onCellMouseDown", function(evt){ if(!evt.ctrlKey && this._dndReady){ this._dndRegion = this._getDnDRegion(evt.rowIndex, evt.cell.index); this._isMouseDown = true; } }); this.connect(g, "onCellMouseUp", function(evt){ if(!this._dndReady && !s.isSelecting() && evt.cell){ this._dndReady = s.isSelected("cell", evt.rowIndex, evt.cell.index); s.selectEnabled(!this._dndReady); } }); this.connect(g, "onCellClick", function(evt){ if(this._dndReady && !evt.ctrlKey && !evt.shiftKey){ s.select("cell", evt.rowIndex, evt.cell.index); } }); this.connect(g, "onEndAutoScroll", function(isVertical, isForward, view, target, evt){ if(this._dnding){ this._markTargetAnchor(evt); } }); this.connect(win.doc, "onkeydown", function(evt){ if(evt.keyCode == keys.ESCAPE){ this._endDnd(false); }else if(evt.keyCode == keys.CTRL){ s.selectEnabled(true); this._isCopy = true; } }); this.connect(win.doc, "onkeyup", function(evt){ if(evt.keyCode == keys.CTRL){ s.selectEnabled(!this._dndReady); this._isCopy = false; } }); }, _clear: function(){ this._dndRegion = null; this._target = null; this._moveEvent = null; this._targetAnchor = {}; this._dnding = false; this._externalDnd = false; this._isSource = false; this._alreadyOut = false; this._extDnding = false; }, _getDnDRegion: function(rowIndex, colIndex){ var s = this.selector, selected = s._selected, flag = (!!selected.cell.length) | (!!selected.row.length << 1) | (!!selected.col.length << 2), type; switch(flag){ case 1: type = "cell"; if(!this._config[type]["within"] && !this._config[type]["out"]){ return null; } var cells = this.grid.layout.cells, getCount = function(range){ var hiddenColCnt = 0; for(var i = range.min.col; i <= range.max.col; ++i){ if(cells[i].hidden){ ++hiddenColCnt; } } return (range.max.row - range.min.row + 1) * (range.max.col - range.min.col + 1 - hiddenColCnt); }, inRange = function(item, range){ return item.row >= range.min.row && item.row <= range.max.row && item.col >= range.min.col && item.col <= range.max.col; }, range = { max: { row: -1, col: -1 }, min: { row: Infinity, col: Infinity } }; array.forEach(selected[type], function(item){ if(item.row < range.min.row){ range.min.row = item.row; } if(item.row > range.max.row){ range.max.row = item.row; } if(item.col < range.min.col){ range.min.col = item.col; } if(item.col > range.max.col){ range.max.col = item.col; } }); if(array.some(selected[type], function(item){ return item.row == rowIndex && item.col == colIndex; })){ if(getCount(range) == selected[type].length && array.every(selected[type], function(item){ return inRange(item, range); })){ return { "type": type, "selected": [range], "handle": { "row": rowIndex, "col": colIndex } }; } } return null; case 2: case 4: type = flag == 2 ? "row" : "col"; if(!this._config[type]["within"] && !this._config[type]["out"]){ return null; } var res = s.getSelected(type); if(res.length){ return { "type": type, "selected": _devideToArrays(res), "handle": flag == 2 ? rowIndex : colIndex }; } return null; } return null; }, _startDnd: function(evt){ this._createDnDUI(evt); }, _endDnd: function(destroySource){ this._destroyDnDUI(false, destroySource); this._clear(); }, _createDnDUI: function(evt, isMovingIn){ //By default the master view of grid do not have height, because the children in it are all positioned absolutely. //But we need it to contain avatars. var viewPos = html.position(this.grid.views.views[0].domNode); html.style(this._container, "height", viewPos.h + "px"); try{ //If moving in from out side, dnd source is already created. if(!isMovingIn){ this._createSource(evt); } this._createMoveable(evt); this._oldCursor = html.style(win.body(), "cursor"); html.style(win.body(), "cursor", "default"); }catch(e){ console.warn("DnD._createDnDUI() error:", e); } }, _destroyDnDUI: function(isMovingOut, destroySource){ try{ if(destroySource){ this._destroySource(); } this._unmarkTargetAnchor(); if(!isMovingOut){ this._destroyMoveable(); } html.style(win.body(), "cursor", this._oldCursor); }catch(e){ console.warn("DnD._destroyDnDUI() error:", this.grid.id, e); } }, _createSource: function(evt){ this._elem.createDnDNodes(this._dndRegion); var m = dojo.dnd.manager(); var oldMakeAvatar = m.makeAvatar; m._dndPlugin = this; m.makeAvatar = function(){ var avatar = new GridDnDAvatar(m); delete m._dndPlugin; return avatar; }; m.startDrag(this._source, this._elem.getDnDNodes(), evt.ctrlKey); m.makeAvatar = oldMakeAvatar; m.onMouseMove(evt); }, _destroySource: function(){ connect.publish("/dnd/cancel"); }, _createMoveable: function(evt){ if(!this._markTagetAnchorHandler){ this._markTagetAnchorHandler = this.connect(win.doc, "onmousemove", "_markTargetAnchor"); } }, _destroyMoveable: function(){ this.disconnect(this._markTagetAnchorHandler); delete this._markTagetAnchorHandler; }, _calcColTargetAnchorPos: function(evt, containerPos){ // summary: // Calculate the position of the column DnD avatar var i, headPos, left, target, ex = evt.clientX, cells = this.grid.layout.cells, ltr = html._isBodyLtr(), headers = this._getVisibleHeaders(); for(i = 0; i < headers.length; ++i){ headPos = html.position(headers[i].node); if(ltr ? ((i === 0 || ex >= headPos.x) && ex < headPos.x + headPos.w) : ((i === 0 || ex < headPos.x + headPos.w) && ex >= headPos.x)){ left = headPos.x + (ltr ? 0 : headPos.w); break; }else if(ltr ? (i === headers.length - 1 && ex >= headPos.x + headPos.w) : (i === headers.length - 1 && ex < headPos.x)){ ++i; left = headPos.x + (ltr ? headPos.w : 0); break; } } if(i < headers.length){ target = headers[i].cell.index; if(this.selector.isSelected("col", target) && this.selector.isSelected("col", target - 1)){ var ranges = this._dndRegion.selected; for(i = 0; i < ranges.length; ++i){ if(array.indexOf(ranges[i], target) >= 0){ target = ranges[i][0]; headPos = html.position(cells[target].getHeaderNode()); left = headPos.x + (ltr ? 0 : headPos.w); break; } } } }else{ target = cells.length; } this._target = target; return left - containerPos.x; }, _calcRowTargetAnchorPos: function(evt, containerPos){ // summary: // Calculate the position of the row DnD avatar var g = this.grid, top, i = 0, cells = g.layout.cells; while(cells[i].hidden){ ++i; } var cell = g.layout.cells[i], rowIndex = g.scroller.firstVisibleRow, cellNode = cell.getNode(rowIndex); if(!cellNode){ //if the target grid is empty, set to -1 //which will be processed in Rearrange this._target = -1; return 0; //position of the insert bar } var nodePos = html.position(cellNode); while(nodePos.y + nodePos.h < evt.clientY){ if(++rowIndex >= g.rowCount){ break; } nodePos = html.position(cell.getNode(rowIndex)); } if(rowIndex < g.rowCount){ if(this.selector.isSelected("row", rowIndex) && this.selector.isSelected("row", rowIndex - 1)){ var ranges = this._dndRegion.selected; for(i = 0; i < ranges.length; ++i){ if(array.indexOf(ranges[i], rowIndex) >= 0){ rowIndex = ranges[i][0]; nodePos = html.position(cell.getNode(rowIndex)); break; } } } top = nodePos.y; }else{ top = nodePos.y + nodePos.h; } this._target = rowIndex; return top - containerPos.y; }, _calcCellTargetAnchorPos: function(evt, containerPos, targetAnchor){ // summary: // Calculate the position of the cell DnD avatar var s = this._dndRegion.selected[0], origin = this._dndRegion.handle, g = this.grid, ltr = html._isBodyLtr(), cells = g.layout.cells, headPos, minPos, maxPos, headers, height, width, left, top, minCol, maxCol, i, preSpan = origin.col - s.min.col, postSpan = s.max.col - origin.col, leftTopDiv, rightBottomDiv; if(!targetAnchor.childNodes.length){ leftTopDiv = html.create("div", { "class": "dojoxGridCellBorderLeftTopDIV" }, targetAnchor); rightBottomDiv = html.create("div", { "class": "dojoxGridCellBorderRightBottomDIV" }, targetAnchor); }else{ leftTopDiv = query(".dojoxGridCellBorderLeftTopDIV", targetAnchor)[0]; rightBottomDiv = query(".dojoxGridCellBorderRightBottomDIV", targetAnchor)[0]; } for(i = s.min.col + 1; i < origin.col; ++i){ if(cells[i].hidden){ --preSpan; } } for(i = origin.col + 1; i < s.max.col; ++i){ if(cells[i].hidden){ --postSpan; } } headers = this._getVisibleHeaders(); //calc width for(i = preSpan; i < headers.length - postSpan; ++i){ headPos = html.position(headers[i].node); if((evt.clientX >= headPos.x && evt.clientX < headPos.x + headPos.w) || //within in this column //prior to this column, but within range (i == preSpan && (ltr ? evt.clientX < headPos.x : evt.clientX >= headPos.x + headPos.w)) || //post to this column, but within range (i == headers.length - postSpan - 1 && (ltr ? evt.clientX >= headPos.x + headPos.w : evt < headPos.x))){ minCol = headers[i - preSpan]; maxCol = headers[i + postSpan]; minPos = html.position(minCol.node); maxPos = html.position(maxCol.node); minCol = minCol.cell.index; maxCol = maxCol.cell.index; left = ltr ? minPos.x : maxPos.x; width = ltr ? (maxPos.x + maxPos.w - minPos.x) : (minPos.x + minPos.w - maxPos.x); break; } } //calc height i = 0; while(cells[i].hidden){ ++i; } var cell = cells[i], rowIndex = g.scroller.firstVisibleRow, nodePos = html.position(cell.getNode(rowIndex)); while(nodePos.y + nodePos.h < evt.clientY){ if(++rowIndex < g.rowCount){ nodePos = html.position(cell.getNode(rowIndex)); }else{ break; } } var minRow = rowIndex >= origin.row - s.min.row ? rowIndex - origin.row + s.min.row : 0; var maxRow = minRow + s.max.row - s.min.row; if(maxRow >= g.rowCount){ maxRow = g.rowCount - 1; minRow = maxRow - s.max.row + s.min.row; } minPos = html.position(cell.getNode(minRow)); maxPos = html.position(cell.getNode(maxRow)); top = minPos.y; height = maxPos.y + maxPos.h - minPos.y; this._target = { "min":{ "row": minRow, "col": minCol }, "max":{ "row": maxRow, "col": maxCol } }; var anchorBorderSize = (html.marginBox(leftTopDiv).w - html.contentBox(leftTopDiv).w) / 2; var leftTopCellPos = html.position(cells[minCol].getNode(minRow)); html.style(leftTopDiv, { "width": (leftTopCellPos.w - anchorBorderSize) + "px", "height": (leftTopCellPos.h - anchorBorderSize) + "px" }); var rightBottomCellPos = html.position(cells[maxCol].getNode(maxRow)); html.style(rightBottomDiv, { "width": (rightBottomCellPos.w - anchorBorderSize) + "px", "height": (rightBottomCellPos.h - anchorBorderSize) + "px" }); return { h: height, w: width, l: left - containerPos.x, t: top - containerPos.y }; }, _markTargetAnchor: function(evt){ try{ var t = this._dndRegion.type; if(this._alreadyOut || (this._dnding && !this._config[t]["within"]) || (this._extDnding && !this._config[t]["in"])){ return; } var height, width, left, top, targetAnchor = this._targetAnchor[t], pos = html.position(this._container); if(!targetAnchor){ targetAnchor = this._targetAnchor[t] = html.create("div", { "class": (t == "cell") ? "dojoxGridCellBorderDIV" : "dojoxGridBorderDIV" }); html.style(targetAnchor, "display", "none"); this._container.appendChild(targetAnchor); } switch(t){ case "col": height = pos.h; width = this._targetAnchorBorderWidth; left = this._calcColTargetAnchorPos(evt, pos); top = 0; break; case "row": height = this._targetAnchorBorderWidth; width = pos.w; left = 0; top = this._calcRowTargetAnchorPos(evt, pos); break; case "cell": var cellPos = this._calcCellTargetAnchorPos(evt, pos, targetAnchor); height = cellPos.h; width = cellPos.w; left = cellPos.l; top = cellPos.t; } if(typeof height == "number" && typeof width == "number" && typeof left == "number" && typeof top == "number"){ html.style(targetAnchor, { "height": height + "px", "width": width + "px", "left": left + "px", "top": top + "px" }); html.style(targetAnchor, "display", ""); }else{ this._target = null; } }catch(e){ console.warn("DnD._markTargetAnchor() error:",e); } }, _unmarkTargetAnchor: function(){ if(this._dndRegion){ var targetAnchor = this._targetAnchor[this._dndRegion.type]; if(targetAnchor){ html.style(this._targetAnchor[this._dndRegion.type], "display", "none"); } } }, _getVisibleHeaders: function(){ return array.map(array.filter(this.grid.layout.cells, function(cell){ return !cell.hidden; }), function(cell){ return { "node": cell.getHeaderNode(), "cell": cell }; }); }, _rearrange: function(){ if(this._target === null){ return; } var t = this._dndRegion.type; var ranges = this._dndRegion.selected; if(t === "cell"){ this.rearranger[(this._isCopy || this._copyOnly) ? "copyCells" : "moveCells"](ranges[0], this._target === -1 ? null : this._target); }else{ this.rearranger[t == "col" ? "moveColumns" : "moveRows"](_joinToArray(ranges), this._target === -1 ? null: this._target); } this._target = null; }, onDraggingOver: function(sourcePlugin){ if(!this._dnding && sourcePlugin){ sourcePlugin._isSource = true; this._extDnding = true; if(!this._externalDnd){ this._externalDnd = true; this._dndRegion = this._mapRegion(sourcePlugin.grid, sourcePlugin._dndRegion); } this._createDnDUI(this._moveEvent,true); this.grid.pluginMgr.getPlugin("autoScroll").readyForAutoScroll = true; } }, _mapRegion: function(srcGrid, dndRegion){ if(dndRegion.type === "cell"){ var srcRange = dndRegion.selected[0]; var cells = this.grid.layout.cells; var srcCells = srcGrid.layout.cells; var c, cnt = 0; for(c = srcRange.min.col; c <= srcRange.max.col; ++c){ if(!srcCells[c].hidden){ ++cnt; } } for(c = 0; cnt > 0; ++c){ if(!cells[c].hidden){ --cnt; } } var region = lang.clone(dndRegion); region.selected[0].min.col = 0; region.selected[0].max.col = c - 1; for(c = srcRange.min.col; c <= dndRegion.handle.col; ++c){ if(!srcCells[c].hidden){ ++cnt; } } for(c = 0; cnt > 0; ++c){ if(!cells[c].hidden){ --cnt; } } region.handle.col = c; } return dndRegion; }, onDraggingOut: function(sourcePlugin){ if(this._externalDnd){ this._extDnding = false; this._destroyDnDUI(true, false); if(sourcePlugin){ sourcePlugin._isSource = false; } } }, onDragIn: function(sourcePlugin, isCopy){ var success = false; if(this._target !== null){ var type = sourcePlugin._dndRegion.type; var ranges = sourcePlugin._dndRegion.selected; switch(type){ case "cell": this.rearranger.changeCells(sourcePlugin.grid, ranges[0], this._target); break; case "row": var range = _joinToArray(ranges); this.rearranger.insertRows(sourcePlugin.grid, range, this._target); break; } success = true; } this._endDnd(true); if(sourcePlugin.onDragOut){ sourcePlugin.onDragOut(success && !isCopy); } }, onDragOut: function(isMove){ if(isMove && !this._copyOnly){ var type = this._dndRegion.type; var ranges = this._dndRegion.selected; switch(type){ case "cell": this.rearranger.clearCells(ranges[0]); break; case "row": this.rearranger.removeRows(_joinToArray(ranges)); break; } } this._endDnd(true); }, _canAccept: function(sourcePlugin){ if(!sourcePlugin){ return false; } var srcRegion = sourcePlugin._dndRegion; var type = srcRegion.type; if(!this._config[type]["in"] || !sourcePlugin._config[type]["out"]){ return false; } var g = this.grid; var ranges = srcRegion.selected; var colCnt = array.filter(g.layout.cells, function(cell){ return !cell.hidden; }).length; var rowCnt = g.rowCount; var res = true; switch(type){ case "cell": ranges = ranges[0]; res = g.store.getFeatures()["dojo.data.api.Write"] && (ranges.max.row - ranges.min.row) <= rowCnt && array.filter(sourcePlugin.grid.layout.cells, function(cell){ return cell.index >= ranges.min.col && cell.index <= ranges.max.col && !cell.hidden; }).length <= colCnt; //intentional drop through - don't break case "row": if(sourcePlugin._allDnDItemsLoaded()){ return res; } } return false; }, _allDnDItemsLoaded: function(){ if(this._dndRegion){ var type = this._dndRegion.type, ranges = this._dndRegion.selected, rows = []; switch(type){ case "cell": for(var i = ranges[0].min.row, max = ranges[0].max.row; i <= max; ++i){ rows.push(i); } break; case "row": rows = _joinToArray(ranges); break; default: return false; } var cache = this.grid._by_idx; return array.every(rows, function(rowIndex){ return !!cache[rowIndex]; }); } return false; } }); EnhancedGrid.registerPlugin(DnD/*name:'dnd'*/, { "dependency": ["selector", "rearrange"] }); return DnD; });