/** * This class is used to unify information for a specific drag instance. * This object is passed to template methods and events to obtain * details about the current operation. * * It is not expected that this class will be created by user code. */Ext.define('Ext.drag.Info', { requires: ['Ext.Promise'], constructor: function(source, e) { // Internally we will call the constructor empty when we want to clone. if (!source) { return; } var me = this, local = source.getLocal(), el, proxyEl, proxy, x, xy, y, pageXY, elPageXY; me.source = source; me.local = local; xy = me.getEventXY(e); pageXY = e.getXY(); el = source.getElement(); elPageXY = el.getXY(); xy = local ? el.getLocalXY() : elPageXY; x = xy[0]; y = xy[1]; me.initialEvent = e; me.eventTarget = e.target; me.cursor = { current: { x: x, y: y }, delta: { x: 0, y: 0 }, initial: { x: pageXY[0], y: pageXY[1] }, offset: { x: pageXY[0] - elPageXY[0], y: pageXY[1] - elPageXY[1] } }; me.element = { current: { x: x, y: y }, delta: { x: 0, y: 0 }, initial: { x: x, y: y } }; me.proxy = { instance: source.getProxy(), current: { x: x, y: y }, delta: { x: 0, y: 0 }, initial: { x: x, y: y }, element: el, isUnderCursor: false, isElement: true }; me.types = []; me.data = {}; source.describe(me); proxy = me.proxy; proxyEl = proxy.instance.setupElement(me); proxy.isElement = proxyEl === source.getElement(); proxy.element = proxyEl; if (proxyEl) { proxy.width = proxyEl.getWidth(); proxy.height = proxyEl.getHeight(); } if (proxy.isElement) { // If they are the same we don't need to keep track of both el = me.element; el.current = proxy.current; el.delta = proxy.delta; } me.needsCursorCheck = proxy.element && source.manager && source.manager.pointerBug; }, /** * @property {Object} cursor * Information about the cursor position. Not available when * {@link #isNative} is `true`. * * * @property {Object} cursor.current * The current cursor position. * * @property {Number} cursor.current.x * The current x position. * * @property {Number} cursor.current.y * The current y position. * * * @property {Object} cursor.delta * The change in cursor position. * * @property {Number} cursor.delta.x * The change in x position. * * @property {Number} cursor.delta.y * The change in y position. * * * @property {Object} cursor.initial * The intial cursor position. * * @property {Number} cursor.initial.x * The initial x position. * * @property {Number} cursor.initial.y * The initial y position. * * * @property {Object} cursor.offset * The offset from the cursor to the top/left of * the {@link Ext.drag.Source#element element}. * * @property {Number} cursor.offset.x * The x offset. * * @property {Number} cursor.offset.y * The y offset. */ cursor: null, /** * @property {Object} element * Information about the {@link Ext.drag.Source#element} position. * Not available when {@link #isNative} is `true`. * * * @property {Object} element.current * The current element position. * * @property {Number} element.current.x * The current x position. * * @property {Number} element.current.y * The current y position. * * * @property {Object} element.delta * The change in element position. * * @property {Number} element.delta.x * The change in x position. * * @property {Number} element.delta.y * The change in y position. * * * @property {Object} element.initial * The intial element position. * * @property {Number} element.initial.x * The initial x position. * * @property {Number} element.initial.y * The initial y position. */ element: null, /** * @property {HTMLElement} eventTarget * The event target that the drag started on. * * Not available when {@link #isNative} is `true`. */ eventTarget: null, /** * @property {FileList} files * A list of files included for this drag. See: * https://developer.mozilla.org/en/docs/Web/API/FileList * * Only available when {@link #isNative} is `true`. */ files: null, /** * @property {Boolean} isNative * `true` if the drag is a native drag event, for example * a file draggedi nto the browser. */ isNative: false, /** * @property {Object} proxy * Information about the {@link Ext.drag.Source#proxy} position. * This may be the actual {@link Ext.drag.Source#element}. * Not available when {@link #isNative} is `true`. * * * @property {Object} proxy.current * The current proxy position. * * @property {Number} proxy.current.x * The current x position. * * @property {Number} proxy.current.y * The current y position. * * * @property {Object} proxy.delta * The change in proxy position. * * @property {Number} proxy.delta.x * The change in x position. * * @property {Number} proxy.delta.y * The change in y position. * * * @property {Object} proxy.initial * The intial proxy position. * * @property {Number} proxy.initial.x * The initial x position. * * @property {Number} proxy.initial.y * The initial y position. * * @property {Ext.dom.Element} proxy.element * The proxy element. * * @property {Boolean} proxy.isElement * `true` if the proxy is the {@link Ext.drag.Source#element}. * * @property {Boolean} proxy.isUnderCursor * `true` if the alignment causes the proxy to be under the cursor. */ proxy: null, /** * @property {Ext.drag.Source} source * The drag source. Not available when {@link #isNative} is `true`. */ source: null, /** * @property {Ext.drag.Target} target * The active target. `null` if not over a target. */ target: null, /** * @property {String[]} types * The data types this drag provides. Added via {@link #setData}. */ types: null, /** * @property {Boolean} valid * `true` if the {@link #target} is valid. See {@link Ext.drag.Target} for * information about validity. `false` if there is no target. */ valid: false, /** * Clear the data for a particular type. * @param {String} type The type. */ clearData: function(type) { Ext.Array.remove(this.types, type); delete this.data[type]; }, /** * Create a copy of this object with the current state. * @return {Ext.drag.Info} A copy of this object. */ clone: function() { var me = this, ret = new Ext.drag.Info(); ret.cursor = Ext.merge({}, me.cursor); ret.data = Ext.apply({}, me.data); ret.element = Ext.merge({}, me.element); ret.eventTarget = me.eventTarget; ret.proxy = Ext.merge({}, me.proxy); ret.source = me.source; ret.target = me.target; ret.types = Ext.Array.clone(me.types); ret.valid = me.valid; return ret; }, /** * Get data for this drag. This method may only be called once the drop completes. * * @param {String} type The type of data to retrieve. Must be in the {@link #types}. * See also {@link #setData}. * * @return {Ext.Promise} The data. If the produced data is not a {@link Ext.Promise}, * it will be wrapped in one. */ getData: function(type) { var me = this, data = me.data, dt = me.dataTransfer, ret; if (dt) { ret = dt.getData(type); } else { //<debug> if (!me.finalized) { Ext.raise('Unable to call getData until the drop is complete'); } //</debug> ret = data[type]; if (typeof ret === 'function') { data[type] = ret = ret.call(me.source, me); } if (!ret && ret !== 0) { ret = ''; } } return Ext.Promise.resolve(ret); }, /** * Set data for this drag. Multiple types may be registered. Each type will be * added to {@link #types}. * * @param {String} type The type of data being registered. * @param {Object/Function} value The value being registered. If a function * is provided it will be evaluated if requested when the drop completes. The * function should return a value or a {@link Ext.Promise} that will produce a value. */ setData: function(type, value) { Ext.Array.include(this.types, type); this.data[type] = value; }, destroy: function() { var me = this; me.eventTarget = me.data = me.proxy = me.targetMap = me.targetMap = me.types = me.elementMap = me.possibleTargets = me.target = null; me.callParent(); }, privates: { /** * @property {Object} data * The underlying data for this drag. Keyed by type, the value * can be a value or a function to return a value. * * @private */ data: null, /** * @property {DataTransfer} dataTransfer * The browser native dataTransfer object, if available. * * @private */ dataTransfer: null, /** * @property {Object} elementMap * A map of targets that is keyed by the element * id for each drag target. Only provided in point mode. * * @private */ elementMap: null, /** * @property {Object} possibleTargets * The map of targets that this drag can interact with, meaning * those with not matching groups and disabled items are excluded. * * @private */ possibleTargets: null, /** * @property {Object} targetMap * A map of targets that is keyed by id when the drag began. * * @private */ targetMap: null, copyNativeData: function(target, e) { var dt = e.browserEvent.dataTransfer; this.target = target; this.dataTransfer = dt; this.files = dt.files; }, /** * Notify that the drag is finished and final processing can occur. * * @private */ finalize: function() { var me = this, target = me.target; me.finalized = true; if (target) { target.info = null; target.handleDrop(me); } }, /** * Calculate the current position of the proxy element, taking * into account constraints. * @param {Number} x The cursor x position. * @param {Number} y The cursor y position. * * @return {Number[]} The position. * * @private */ getAlignXY: function(x, y) { var me = this, source = me.source, cursorOffset = me.cursor.offset, proxy = source.getProxy(), proxyEl = me.proxy.element, constrain = source.getConstrain(), xy = [x, y]; if (proxyEl) { if (me.proxy.isElement) { xy[0] -= cursorOffset.x; xy[1] -= cursorOffset.y; } else { xy = proxy.adjustCursorOffset(me, xy); } if (constrain) { xy = constrain.constrain(xy, me); } } return xy; }, getEventXY: function (e) { var xy = e.getXY(), // page coordinates source = this.source; if (this.local) { xy = source.convertToLocalXY(xy); } return xy; }, onNativeDragEnter: function(target, e) { var me = this; me.valid = target.accepts(me); target.info = me; me.copyNativeData(target, e); }, onNativeDragLeave: function(target, e) { var me = this; // With native events, enter fires before leave, so when the leave fires // check that we are the current target, another target may have already // taken over here if (me.target === target) { target.info = null; me.valid = false; me.target = me.dataTransfer = me.files = null; } }, onNativeDragMove: function(target, e) { this.copyNativeData(target, e); }, onNativeDrop: function(target, e) { this.copyNativeData(target, e); target.info = null; }, /** * Mark the target as active for the drag. * @param {Ext.drag.Target} target The target. * * @private */ setActive: function(target) { var me = this, source = me.source, current = me.target, changed = current !== target; if (current && changed) { current.handleDragLeave(me); current.info = null; } me.target = target; if (target) { if (changed) { me.valid = !!me.possibleTargets[target.getId()] && target.accepts(me) !== false; target.handleDragEnter(me); target.info = me; } target.handleDragMove(me); } else { me.valid = false; } if (changed) { source.getProxy().update(me); } }, /** * Update with the current position information. * @param {Ext.event.Event} event The event. * @param {Boolean} beforeStart `true` if the update is occurring * before the drag starts. * * @private */ update: function(event, beforeStart) { var me = this, xy = me.getEventXY(event), x = xy[0], y = xy[1], alignXY = me.getAlignXY(x, y), alignX = alignXY[0], alignY = alignXY[1], proxyData = me.proxy, cursor = me.cursor, current = cursor.current, delta = cursor.delta, initial = cursor.initial, proxy = proxyData.instance; current.x = x; current.y = y; delta.x = x - initial.x; delta.y = y - initial.y; current = proxyData.current; delta = proxyData.delta; initial = proxyData.initial; current.x = alignX; current.y = alignY; delta.x = alignX - initial.x; delta.y = alignY - initial.y; if (me.needsCursorCheck) { proxyData.isUnderCursor = !(x < alignX || y < alignY || x > proxyData.width + alignX || y > proxyData.height + alignY); } if (!beforeStart && proxy) { proxy.setXY(me, alignXY); } } }});