Path: blob/trunk/third_party/closure/goog/fx/dragger.js
4113 views
/**1* @license2* Copyright The Closure Library Authors.3* SPDX-License-Identifier: Apache-2.04*/56/**7* @fileoverview Drag Utilities.8*9* Provides extensible functionality for drag & drop behaviour.10*11* @see ../demos/drag.html12* @see ../demos/dragger.html13*/141516goog.provide('goog.fx.DragEvent');17goog.provide('goog.fx.Dragger');18goog.provide('goog.fx.Dragger.EventType');1920goog.require('goog.dom');21goog.require('goog.dom.TagName');22goog.require('goog.events');23goog.require('goog.events.Event');24goog.require('goog.events.EventHandler');25goog.require('goog.events.EventTarget');26goog.require('goog.events.EventType');27goog.require('goog.math.Coordinate');28goog.require('goog.math.Rect');29goog.require('goog.style');30goog.require('goog.style.bidi');31goog.require('goog.userAgent');32goog.requireType('goog.events.BrowserEvent');33343536/**37* A class that allows mouse or touch-based dragging (moving) of an element38*39* @param {Element} target The element that will be dragged.40* @param {Element=} opt_handle An optional handle to control the drag, if null41* the target is used.42* @param {goog.math.Rect=} opt_limits Object containing left, top, width,43* and height.44*45* @extends {goog.events.EventTarget}46* @constructor47* @struct48*/49goog.fx.Dragger = function(target, opt_handle, opt_limits) {50'use strict';51goog.fx.Dragger.base(this, 'constructor');5253/**54* Reference to drag target element.55* @type {?Element}56*/57this.target = target;5859/**60* Reference to the handler that initiates the drag.61* @type {?Element}62*/63this.handle = opt_handle || target;6465/**66* Object representing the limits of the drag region.67* @type {goog.math.Rect}68*/69this.limits = opt_limits || new goog.math.Rect(NaN, NaN, NaN, NaN);7071/**72* Reference to a document object to use for the events.73* @private {Document}74*/75this.document_ = goog.dom.getOwnerDocument(target);7677/** @private {!goog.events.EventHandler} */78this.eventHandler_ = new goog.events.EventHandler(this);79this.registerDisposable(this.eventHandler_);8081/**82* Whether the element is rendered right-to-left. We initialize this lazily.83* @private {boolean|undefined}}84*/85this.rightToLeft_;8687/**88* Current x position of mouse or touch relative to viewport.89* @type {number}90*/91this.clientX = 0;9293/**94* Current y position of mouse or touch relative to viewport.95* @type {number}96*/97this.clientY = 0;9899/**100* Current x position of mouse or touch relative to screen. Deprecated because101* it doesn't take into affect zoom level or pixel density.102* @type {number}103* @deprecated Consider switching to clientX instead.104*/105this.screenX = 0;106107/**108* Current y position of mouse or touch relative to screen. Deprecated because109* it doesn't take into affect zoom level or pixel density.110* @type {number}111* @deprecated Consider switching to clientY instead.112*/113this.screenY = 0;114115/**116* The x position where the first mousedown or touchstart occurred.117* @type {number}118*/119this.startX = 0;120121/**122* The y position where the first mousedown or touchstart occurred.123* @type {number}124*/125this.startY = 0;126127/**128* Current x position of drag relative to target's parent.129* @type {number}130*/131this.deltaX = 0;132133/**134* Current y position of drag relative to target's parent.135* @type {number}136*/137this.deltaY = 0;138139/**140* The current page scroll value.141* @type {?goog.math.Coordinate}142*/143this.pageScroll;144145/**146* Whether dragging is currently enabled.147* @private {boolean}148*/149this.enabled_ = true;150151/**152* Whether object is currently being dragged.153* @private {boolean}154*/155this.dragging_ = false;156157/**158* Whether mousedown should be default prevented.159* @private {boolean}160**/161this.preventMouseDown_ = true;162163/**164* The amount of distance, in pixels, after which a mousedown or touchstart is165* considered a drag.166* @private {number}167*/168this.hysteresisDistanceSquared_ = 0;169170/**171* The SCROLL event target used to make drag element follow scrolling.172* @private {?EventTarget}173*/174this.scrollTarget_;175176/**177* Whether IE drag events cancelling is on.178* @private {boolean}179*/180this.ieDragStartCancellingOn_ = false;181182/**183* Whether the dragger implements the changes described in http://b/6324964,184* making it truly RTL. This is a temporary flag to allow clients to185* transition to the new behavior at their convenience. At some point it will186* be the default.187* @private {boolean}188*/189this.useRightPositioningForRtl_ = false;190191// Add listener. Do not use the event handler here since the event handler is192// used for listeners added and removed during the drag operation.193goog.events.listen(194this.handle,195[goog.events.EventType.TOUCHSTART, goog.events.EventType.MOUSEDOWN],196this.startDrag, false, this);197198/** @private {boolean} Avoids setCapture() calls to fix click handlers. */199this.useSetCapture_ = goog.fx.Dragger.HAS_SET_CAPTURE_;200};201goog.inherits(goog.fx.Dragger, goog.events.EventTarget);202// Dragger is meant to be extended, but defines most properties on its203// prototype, thus making it unsuitable for sealing.204205206/**207* Whether setCapture is supported by the browser.208* IE and Gecko after 1.9.3 have setCapture. MS Edge and WebKit209* (https://bugs.webkit.org/show_bug.cgi?id=27330) don't.210* @type {boolean}211* @private212*/213goog.fx.Dragger.HAS_SET_CAPTURE_ = goog.global.document &&214goog.global.document.documentElement &&215!!goog.global.document.documentElement.setCapture &&216!!goog.global.document.releaseCapture;217218219/**220* Creates copy of node being dragged. This is a utility function to be used221* wherever it is inappropriate for the original source to follow the mouse222* cursor itself.223*224* @param {Element} sourceEl Element to copy.225* @return {!Element} The clone of `sourceEl`.226*/227goog.fx.Dragger.cloneNode = function(sourceEl) {228'use strict';229var clonedEl = sourceEl.cloneNode(true),230origTexts =231goog.dom.getElementsByTagName(goog.dom.TagName.TEXTAREA, sourceEl),232dragTexts =233goog.dom.getElementsByTagName(goog.dom.TagName.TEXTAREA, clonedEl);234// Cloning does not copy the current value of textarea elements, so correct235// this manually.236for (var i = 0; i < origTexts.length; i++) {237dragTexts[i].value = origTexts[i].value;238}239switch (sourceEl.tagName) {240case String(goog.dom.TagName.TR):241return goog.dom.createDom(242goog.dom.TagName.TABLE, null,243goog.dom.createDom(goog.dom.TagName.TBODY, null, clonedEl));244case String(goog.dom.TagName.TD):245case String(goog.dom.TagName.TH):246return goog.dom.createDom(247goog.dom.TagName.TABLE, null,248goog.dom.createDom(249goog.dom.TagName.TBODY, null,250goog.dom.createDom(goog.dom.TagName.TR, null, clonedEl)));251case String(goog.dom.TagName.TEXTAREA):252/**253* @suppress {strictMissingProperties} Added to tighten compiler checks254*/255clonedEl.value = sourceEl.value;256default:257return clonedEl;258}259};260261262/**263* Constants for event names.264* @enum {string}265*/266goog.fx.Dragger.EventType = {267// The drag action was canceled before the START event. Possible reasons:268// disabled dragger, dragging with the right mouse button or releasing the269// button before reaching the hysteresis distance.270EARLY_CANCEL: 'earlycancel',271START: 'start',272BEFOREDRAG: 'beforedrag',273DRAG: 'drag',274END: 'end'275};276277278/**279* Prevents the dragger from calling setCapture(), even in browsers that support280* it. If the draggable item has click handlers, setCapture() can break them.281* @param {boolean} allow True to use setCapture if the browser supports it.282*/283goog.fx.Dragger.prototype.setAllowSetCapture = function(allow) {284'use strict';285this.useSetCapture_ = allow && goog.fx.Dragger.HAS_SET_CAPTURE_;286};287288289/**290* Turns on/off true RTL behavior. This should be called immediately after291* construction. This is a temporary flag to allow clients to transition292* to the new component at their convenience. At some point true will be the293* default.294* @param {boolean} useRightPositioningForRtl True if "right" should be used for295* positioning, false if "left" should be used for positioning.296*/297goog.fx.Dragger.prototype.enableRightPositioningForRtl = function(298useRightPositioningForRtl) {299'use strict';300this.useRightPositioningForRtl_ = useRightPositioningForRtl;301};302303304/**305* Returns the event handler, intended for subclass use.306* @return {!goog.events.EventHandler<T>} The event handler.307* @this {T}308* @template T309*/310goog.fx.Dragger.prototype.getHandler = function() {311'use strict';312// TODO(user): templated "this" values currently result in "this" being313// "unknown" in the body of the function.314var self = /** @type {goog.fx.Dragger} */ (this);315return self.eventHandler_;316};317318319/**320* Sets (or reset) the Drag limits after a Dragger is created.321* @param {goog.math.Rect?} limits Object containing left, top, width,322* height for new Dragger limits. If target is right-to-left and323* enableRightPositioningForRtl(true) is called, then rect is interpreted as324* right, top, width, and height.325*/326goog.fx.Dragger.prototype.setLimits = function(limits) {327'use strict';328this.limits = limits || new goog.math.Rect(NaN, NaN, NaN, NaN);329};330331332/**333* Sets the distance the user has to drag the element before a drag operation is334* started.335* @param {number} distance The number of pixels after which a mousedown and336* move is considered a drag.337*/338goog.fx.Dragger.prototype.setHysteresis = function(distance) {339'use strict';340this.hysteresisDistanceSquared_ = Math.pow(distance, 2);341};342343344/**345* Gets the distance the user has to drag the element before a drag operation is346* started.347* @return {number} distance The number of pixels after which a mousedown and348* move is considered a drag.349*/350goog.fx.Dragger.prototype.getHysteresis = function() {351'use strict';352return Math.sqrt(this.hysteresisDistanceSquared_);353};354355356/**357* Sets the SCROLL event target to make drag element follow scrolling.358*359* @param {EventTarget} scrollTarget The event target that dispatches SCROLL360* events.361*/362goog.fx.Dragger.prototype.setScrollTarget = function(scrollTarget) {363'use strict';364this.scrollTarget_ = scrollTarget;365};366367368/**369* Enables cancelling of built-in IE drag events.370* @param {boolean} cancelIeDragStart Whether to enable cancelling of IE371* dragstart event.372*/373goog.fx.Dragger.prototype.setCancelIeDragStart = function(cancelIeDragStart) {374'use strict';375this.ieDragStartCancellingOn_ = cancelIeDragStart;376};377378379/**380* @return {boolean} Whether the dragger is enabled.381*/382goog.fx.Dragger.prototype.getEnabled = function() {383'use strict';384return this.enabled_;385};386387388/**389* Set whether dragger is enabled390* @param {boolean} enabled Whether dragger is enabled.391*/392goog.fx.Dragger.prototype.setEnabled = function(enabled) {393'use strict';394this.enabled_ = enabled;395};396397398/**399* Set whether mousedown should be default prevented.400* @param {boolean} preventMouseDown Whether mousedown should be default401* prevented.402*/403goog.fx.Dragger.prototype.setPreventMouseDown = function(preventMouseDown) {404'use strict';405this.preventMouseDown_ = preventMouseDown;406};407408409/** @override */410goog.fx.Dragger.prototype.disposeInternal = function() {411'use strict';412goog.fx.Dragger.superClass_.disposeInternal.call(this);413goog.events.unlisten(414this.handle,415[goog.events.EventType.TOUCHSTART, goog.events.EventType.MOUSEDOWN],416this.startDrag, false, this);417this.cleanUpAfterDragging_();418419this.target = null;420this.handle = null;421};422423424/**425* Whether the DOM element being manipulated is rendered right-to-left.426* @return {boolean} True if the DOM element is rendered right-to-left, false427* otherwise.428* @private429*/430goog.fx.Dragger.prototype.isRightToLeft_ = function() {431'use strict';432if (this.rightToLeft_ === undefined) {433this.rightToLeft_ = goog.style.isRightToLeft(this.target);434}435return this.rightToLeft_;436};437438439/**440* Event handler that is used to start the drag441* @param {goog.events.BrowserEvent} e Event object.442*/443goog.fx.Dragger.prototype.startDrag = function(e) {444'use strict';445var isMouseDown = e.type == goog.events.EventType.MOUSEDOWN;446447// Dragger.startDrag() can be called by AbstractDragDrop with a mousemove448// event and IE does not report pressed mouse buttons on mousemove. Also,449// it does not make sense to check for the button if the user is already450// dragging.451452if (this.enabled_ && !this.dragging_ &&453(!isMouseDown || e.isMouseActionButton())) {454if (this.hysteresisDistanceSquared_ == 0) {455if (this.fireDragStart_(e)) {456this.dragging_ = true;457if (this.preventMouseDown_ && isMouseDown) {458e.preventDefault();459}460} else {461// If the start drag is cancelled, don't setup for a drag.462return;463}464} else if (this.preventMouseDown_ && isMouseDown) {465// Need to preventDefault for hysteresis to prevent page getting selected.466e.preventDefault();467}468this.setupDragHandlers();469470this.clientX = this.startX = e.clientX;471this.clientY = this.startY = e.clientY;472this.screenX = e.screenX;473this.screenY = e.screenY;474this.computeInitialPosition();475this.pageScroll = goog.dom.getDomHelper(this.document_).getDocumentScroll();476} else {477this.dispatchEvent(goog.fx.Dragger.EventType.EARLY_CANCEL);478}479};480481482/**483* Sets up event handlers when dragging starts.484* @protected485*/486goog.fx.Dragger.prototype.setupDragHandlers = function() {487'use strict';488var doc = this.document_;489var docEl = doc.documentElement;490// Use bubbling when we have setCapture since we got reports that IE has491// problems with the capturing events in combination with setCapture.492var useCapture = !this.useSetCapture_;493494this.eventHandler_.listen(495doc, [goog.events.EventType.TOUCHMOVE, goog.events.EventType.MOUSEMOVE],496this.handleMove_, {capture: useCapture, passive: false});497this.eventHandler_.listen(498doc, [goog.events.EventType.TOUCHEND, goog.events.EventType.MOUSEUP],499this.endDrag, useCapture);500501if (this.useSetCapture_) {502docEl.setCapture(false);503this.eventHandler_.listen(504docEl, goog.events.EventType.LOSECAPTURE, this.endDrag);505} else {506// Make sure we stop the dragging if the window loses focus.507// Don't use capture in this listener because we only want to end the drag508// if the actual window loses focus. Since blur events do not bubble we use509// a bubbling listener on the window.510this.eventHandler_.listen(511goog.dom.getWindow(doc), goog.events.EventType.BLUR, this.endDrag);512}513514if (goog.userAgent.IE && this.ieDragStartCancellingOn_) {515// Cancel IE's 'ondragstart' event.516this.eventHandler_.listen(517doc, goog.events.EventType.DRAGSTART, goog.events.Event.preventDefault);518}519520if (this.scrollTarget_) {521this.eventHandler_.listen(522this.scrollTarget_, goog.events.EventType.SCROLL, this.onScroll_,523useCapture);524}525};526527528/**529* Fires a goog.fx.Dragger.EventType.START event.530* @param {goog.events.BrowserEvent} e Browser event that triggered the drag.531* @return {boolean} False iff preventDefault was called on the DragEvent.532* @private533*/534goog.fx.Dragger.prototype.fireDragStart_ = function(e) {535'use strict';536return this.dispatchEvent(new goog.fx.DragEvent(537goog.fx.Dragger.EventType.START, this, e.clientX, e.clientY, e));538};539540541/**542* Unregisters the event handlers that are only active during dragging, and543* releases mouse capture.544* @private545*/546goog.fx.Dragger.prototype.cleanUpAfterDragging_ = function() {547'use strict';548this.eventHandler_.removeAll();549if (this.useSetCapture_) {550this.document_.releaseCapture();551}552};553554555/**556* Event handler that is used to end the drag.557* @param {goog.events.BrowserEvent} e Event object.558* @param {boolean=} opt_dragCanceled Whether the drag has been canceled.559*/560goog.fx.Dragger.prototype.endDrag = function(e, opt_dragCanceled) {561'use strict';562this.cleanUpAfterDragging_();563564if (this.dragging_) {565this.dragging_ = false;566567var x = this.limitX(this.deltaX);568var y = this.limitY(this.deltaY);569var dragCanceled =570opt_dragCanceled || e.type == goog.events.EventType.TOUCHCANCEL;571this.dispatchEvent(572new goog.fx.DragEvent(573goog.fx.Dragger.EventType.END, this, e.clientX, e.clientY, e, x, y,574dragCanceled));575} else {576this.dispatchEvent(goog.fx.Dragger.EventType.EARLY_CANCEL);577}578};579580581/**582* Event handler that is used to end the drag by cancelling it.583* @param {goog.events.BrowserEvent} e Event object.584*/585goog.fx.Dragger.prototype.endDragCancel = function(e) {586'use strict';587this.endDrag(e, true);588};589590591/**592* Event handler that is used on mouse / touch move to update the drag593* @param {goog.events.BrowserEvent} e Event object.594* @private595*/596goog.fx.Dragger.prototype.handleMove_ = function(e) {597'use strict';598if (this.enabled_) {599// dx in right-to-left cases is relative to the right.600var sign =601this.useRightPositioningForRtl_ && this.isRightToLeft_() ? -1 : 1;602var dx = sign * (e.clientX - this.clientX);603var dy = e.clientY - this.clientY;604this.clientX = e.clientX;605this.clientY = e.clientY;606this.screenX = e.screenX;607this.screenY = e.screenY;608609if (!this.dragging_) {610var diffX = this.startX - this.clientX;611var diffY = this.startY - this.clientY;612var distance = diffX * diffX + diffY * diffY;613if (distance > this.hysteresisDistanceSquared_) {614if (this.fireDragStart_(e)) {615this.dragging_ = true;616} else {617// DragListGroup disposes of the dragger if BEFOREDRAGSTART is618// canceled.619if (!this.isDisposed()) {620this.endDrag(e);621}622return;623}624}625}626627var pos = this.calculatePosition_(dx, dy);628var x = pos.x;629var y = pos.y;630631if (this.dragging_) {632var rv = this.dispatchEvent(633new goog.fx.DragEvent(634goog.fx.Dragger.EventType.BEFOREDRAG, this, e.clientX, e.clientY,635e, x, y));636637// Only do the defaultAction and dispatch drag event if predrag didn't638// prevent default639if (rv) {640this.doDrag(e, x, y, false);641e.preventDefault();642}643}644}645};646647648/**649* Calculates the drag position.650*651* @param {number} dx The horizontal movement delta.652* @param {number} dy The vertical movement delta.653* @return {!goog.math.Coordinate} The newly calculated drag element position.654* @private655*/656goog.fx.Dragger.prototype.calculatePosition_ = function(dx, dy) {657'use strict';658// Update the position for any change in body scrolling659var pageScroll = goog.dom.getDomHelper(this.document_).getDocumentScroll();660dx += pageScroll.x - this.pageScroll.x;661dy += pageScroll.y - this.pageScroll.y;662this.pageScroll = pageScroll;663664this.deltaX += dx;665this.deltaY += dy;666667var x = this.limitX(this.deltaX);668var y = this.limitY(this.deltaY);669return new goog.math.Coordinate(x, y);670};671672673/**674* Event handler for scroll target scrolling.675* @param {goog.events.BrowserEvent} e The event.676* @private677*/678goog.fx.Dragger.prototype.onScroll_ = function(e) {679'use strict';680var pos = this.calculatePosition_(0, 0);681e.clientX = this.clientX;682e.clientY = this.clientY;683this.doDrag(e, pos.x, pos.y, true);684};685686687/**688* @param {goog.events.BrowserEvent} e The closure object689* representing the browser event that caused a drag event.690* @param {number} x The new horizontal position for the drag element.691* @param {number} y The new vertical position for the drag element.692* @param {boolean} dragFromScroll Whether dragging was caused by scrolling693* the associated scroll target.694* @protected695*/696goog.fx.Dragger.prototype.doDrag = function(e, x, y, dragFromScroll) {697'use strict';698this.defaultAction(x, y);699this.dispatchEvent(700new goog.fx.DragEvent(701goog.fx.Dragger.EventType.DRAG, this, e.clientX, e.clientY, e, x, y));702};703704705/**706* Returns the 'real' x after limits are applied (allows for some707* limits to be undefined).708* @param {number} x X-coordinate to limit.709* @return {number} The 'real' X-coordinate after limits are applied.710*/711goog.fx.Dragger.prototype.limitX = function(x) {712'use strict';713var rect = this.limits;714var left = !isNaN(rect.left) ? rect.left : null;715var width = !isNaN(rect.width) ? rect.width : 0;716var maxX = left != null ? left + width : Infinity;717var minX = left != null ? left : -Infinity;718return Math.min(maxX, Math.max(minX, x));719};720721722/**723* Returns the 'real' y after limits are applied (allows for some724* limits to be undefined).725* @param {number} y Y-coordinate to limit.726* @return {number} The 'real' Y-coordinate after limits are applied.727*/728goog.fx.Dragger.prototype.limitY = function(y) {729'use strict';730var rect = this.limits;731var top = !isNaN(rect.top) ? rect.top : null;732var height = !isNaN(rect.height) ? rect.height : 0;733var maxY = top != null ? top + height : Infinity;734var minY = top != null ? top : -Infinity;735return Math.min(maxY, Math.max(minY, y));736};737738739/**740* Overridable function for computing the initial position of the target741* before dragging begins.742* @protected743*/744goog.fx.Dragger.prototype.computeInitialPosition = function() {745'use strict';746this.deltaX = this.useRightPositioningForRtl_ ?747goog.style.bidi.getOffsetStart(this.target) :748/** @type {!HTMLElement} */ (this.target).offsetLeft;749this.deltaY = /** @type {!HTMLElement} */ (this.target).offsetTop;750};751752753/**754* Overridable function for handling the default action of the drag behaviour.755* Normally this is simply moving the element to x,y though in some cases it756* might be used to resize the layer. This is basically a shortcut to757* implementing a default ondrag event handler.758* @param {number} x X-coordinate for target element. In right-to-left, x this759* is the number of pixels the target should be moved to from the right.760* @param {number} y Y-coordinate for target element.761*/762goog.fx.Dragger.prototype.defaultAction = function(x, y) {763'use strict';764if (this.useRightPositioningForRtl_ && this.isRightToLeft_()) {765this.target.style.right = x + 'px';766} else {767this.target.style.left = x + 'px';768}769this.target.style.top = y + 'px';770};771772773/**774* @return {boolean} Whether the dragger is currently in the midst of a drag.775*/776goog.fx.Dragger.prototype.isDragging = function() {777'use strict';778return this.dragging_;779};780781782783/**784* Object representing a drag event785* @param {string} type Event type.786* @param {goog.fx.Dragger} dragobj Drag object initiating event.787* @param {number} clientX X-coordinate relative to the viewport.788* @param {number} clientY Y-coordinate relative to the viewport.789* @param {goog.events.BrowserEvent} browserEvent The closure object790* representing the browser event that caused this drag event.791* @param {number=} opt_actX Optional actual x for drag if it has been limited.792* @param {number=} opt_actY Optional actual y for drag if it has been limited.793* @param {boolean=} opt_dragCanceled Whether the drag has been canceled.794* @constructor795* @struct796* @extends {goog.events.Event}797*/798goog.fx.DragEvent = function(799type, dragobj, clientX, clientY, browserEvent, opt_actX, opt_actY,800opt_dragCanceled) {801'use strict';802goog.events.Event.call(this, type);803804/**805* X-coordinate relative to the viewport806* @type {number}807*/808this.clientX = clientX;809810/**811* Y-coordinate relative to the viewport812* @type {number}813*/814this.clientY = clientY;815816/**817* The closure object representing the browser event that caused this drag818* event.819* @type {goog.events.BrowserEvent}820*/821this.browserEvent = browserEvent;822823/**824* The real x-position of the drag if it has been limited825* @type {number}826*/827this.left = (opt_actX !== undefined) ? opt_actX : dragobj.deltaX;828829/**830* The real y-position of the drag if it has been limited831* @type {number}832*/833this.top = (opt_actY !== undefined) ? opt_actY : dragobj.deltaY;834835/**836* Reference to the drag object for this event837* @type {goog.fx.Dragger}838*/839this.dragger = dragobj;840841/**842* Whether drag was canceled with this event. Used to differentiate between843* a legitimate drag END that can result in an action and a drag END which is844* a result of a drag cancelation. For now it can happen 1) with drag END845* event on FireFox when user drags the mouse out of the window, 2) with846* drag END event on IE7 which is generated on MOUSEMOVE event when user847* moves the mouse into the document after the mouse button has been848* released, 3) when TOUCHCANCEL is raised instead of TOUCHEND (on touch849* events).850* @type {boolean}851*/852this.dragCanceled = !!opt_dragCanceled;853};854goog.inherits(goog.fx.DragEvent, goog.events.Event);855856857