http://git-wip-us.apache.org/repos/asf/flex-falcon/blob/e2cad6e6/externs/GCL/externs/goog/fx/dragger.js ---------------------------------------------------------------------- diff --git a/externs/GCL/externs/goog/fx/dragger.js b/externs/GCL/externs/goog/fx/dragger.js new file mode 100644 index 0000000..30a5a57 --- /dev/null +++ b/externs/GCL/externs/goog/fx/dragger.js @@ -0,0 +1,869 @@ +// Copyright 2006 The Closure Library Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS-IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/** + * @fileoverview Drag Utilities. + * + * Provides extensible functionality for drag & drop behaviour. + * + * @see ../demos/drag.html + * @see ../demos/dragger.html + */ + + +goog.provide('goog.fx.DragEvent'); +goog.provide('goog.fx.Dragger'); +goog.provide('goog.fx.Dragger.EventType'); + +goog.require('goog.dom'); +goog.require('goog.dom.TagName'); +goog.require('goog.events'); +goog.require('goog.events.Event'); +goog.require('goog.events.EventHandler'); +goog.require('goog.events.EventTarget'); +goog.require('goog.events.EventType'); +goog.require('goog.math.Coordinate'); +goog.require('goog.math.Rect'); +goog.require('goog.style'); +goog.require('goog.style.bidi'); +goog.require('goog.userAgent'); + + + +/** + * A class that allows mouse or touch-based dragging (moving) of an element + * + * @param {Element} target The element that will be dragged. + * @param {Element=} opt_handle An optional handle to control the drag, if null + * the target is used. + * @param {goog.math.Rect=} opt_limits Object containing left, top, width, + * and height. + * + * @extends {goog.events.EventTarget} + * @constructor + */ +goog.fx.Dragger = function(target, opt_handle, opt_limits) { + goog.events.EventTarget.call(this); + this.target = target; + this.handle = opt_handle || target; + this.limits = opt_limits || new goog.math.Rect(NaN, NaN, NaN, NaN); + + this.document_ = goog.dom.getOwnerDocument(target); + this.eventHandler_ = new goog.events.EventHandler(this); + this.registerDisposable(this.eventHandler_); + + // Add listener. Do not use the event handler here since the event handler is + // used for listeners added and removed during the drag operation. + goog.events.listen(this.handle, + [goog.events.EventType.TOUCHSTART, goog.events.EventType.MOUSEDOWN], + this.startDrag, false, this); +}; +goog.inherits(goog.fx.Dragger, goog.events.EventTarget); +// Dragger is meant to be extended, but defines most properties on its +// prototype, thus making it unsuitable for sealing. +goog.tagUnsealableClass(goog.fx.Dragger); + + +/** + * Whether setCapture is supported by the browser. + * @private {boolean} + * @const + */ +goog.fx.Dragger.HAS_SET_CAPTURE_ = + // IE (up to and including IE 11) and Gecko after 1.9.3 have setCapture + // WebKit does not yet: https://bugs.webkit.org/show_bug.cgi?id=27330 + (goog.userAgent.IE && !goog.userAgent.isVersionOrHigher('12')) || + (goog.userAgent.GECKO && goog.userAgent.isVersionOrHigher('1.9.3')); + + +/** + * Creates copy of node being dragged. This is a utility function to be used + * wherever it is inappropriate for the original source to follow the mouse + * cursor itself. + * + * @param {Element} sourceEl Element to copy. + * @return {!Element} The clone of {@code sourceEl}. + */ +goog.fx.Dragger.cloneNode = function(sourceEl) { + var clonedEl = /** @type {Element} */ (sourceEl.cloneNode(true)), + origTexts = sourceEl.getElementsByTagName(goog.dom.TagName.TEXTAREA), + dragTexts = clonedEl.getElementsByTagName(goog.dom.TagName.TEXTAREA); + // Cloning does not copy the current value of textarea elements, so correct + // this manually. + for (var i = 0; i < origTexts.length; i++) { + dragTexts[i].value = origTexts[i].value; + } + switch (sourceEl.tagName) { + case goog.dom.TagName.TR: + return goog.dom.createDom(goog.dom.TagName.TABLE, null, + goog.dom.createDom(goog.dom.TagName.TBODY, + null, clonedEl)); + case goog.dom.TagName.TD: + case goog.dom.TagName.TH: + return goog.dom.createDom( + goog.dom.TagName.TABLE, null, goog.dom.createDom( + goog.dom.TagName.TBODY, null, goog.dom.createDom( + goog.dom.TagName.TR, null, clonedEl))); + case goog.dom.TagName.TEXTAREA: + clonedEl.value = sourceEl.value; + default: + return clonedEl; + } +}; + + +/** + * Constants for event names. + * @enum {string} + */ +goog.fx.Dragger.EventType = { + // The drag action was canceled before the START event. Possible reasons: + // disabled dragger, dragging with the right mouse button or releasing the + // button before reaching the hysteresis distance. + EARLY_CANCEL: 'earlycancel', + START: 'start', + BEFOREDRAG: 'beforedrag', + DRAG: 'drag', + END: 'end' +}; + + +/** + * Reference to drag target element. + * @type {Element} + */ +goog.fx.Dragger.prototype.target; + + +/** + * Reference to the handler that initiates the drag. + * @type {Element} + */ +goog.fx.Dragger.prototype.handle; + + +/** + * Object representing the limits of the drag region. + * @type {goog.math.Rect} + */ +goog.fx.Dragger.prototype.limits; + + +/** + * Whether the element is rendered right-to-left. We initialize this lazily. + * @type {boolean|undefined}} + * @private + */ +goog.fx.Dragger.prototype.rightToLeft_; + + +/** + * Current x position of mouse or touch relative to viewport. + * @type {number} + */ +goog.fx.Dragger.prototype.clientX = 0; + + +/** + * Current y position of mouse or touch relative to viewport. + * @type {number} + */ +goog.fx.Dragger.prototype.clientY = 0; + + +/** + * Current x position of mouse or touch relative to screen. Deprecated because + * it doesn't take into affect zoom level or pixel density. + * @type {number} + * @deprecated Consider switching to clientX instead. + */ +goog.fx.Dragger.prototype.screenX = 0; + + +/** + * Current y position of mouse or touch relative to screen. Deprecated because + * it doesn't take into affect zoom level or pixel density. + * @type {number} + * @deprecated Consider switching to clientY instead. + */ +goog.fx.Dragger.prototype.screenY = 0; + + +/** + * The x position where the first mousedown or touchstart occurred. + * @type {number} + */ +goog.fx.Dragger.prototype.startX = 0; + + +/** + * The y position where the first mousedown or touchstart occurred. + * @type {number} + */ +goog.fx.Dragger.prototype.startY = 0; + + +/** + * Current x position of drag relative to target's parent. + * @type {number} + */ +goog.fx.Dragger.prototype.deltaX = 0; + + +/** + * Current y position of drag relative to target's parent. + * @type {number} + */ +goog.fx.Dragger.prototype.deltaY = 0; + + +/** + * The current page scroll value. + * @type {goog.math.Coordinate} + */ +goog.fx.Dragger.prototype.pageScroll; + + +/** + * Whether dragging is currently enabled. + * @type {boolean} + * @private + */ +goog.fx.Dragger.prototype.enabled_ = true; + + +/** + * Whether object is currently being dragged. + * @type {boolean} + * @private + */ +goog.fx.Dragger.prototype.dragging_ = false; + + +/** + * Whether mousedown should be default prevented. + * @private {boolean} + */ +goog.fx.Dragger.prototype.preventMouseDown_ = true; + + +/** + * The amount of distance, in pixels, after which a mousedown or touchstart is + * considered a drag. + * @type {number} + * @private + */ +goog.fx.Dragger.prototype.hysteresisDistanceSquared_ = 0; + + +/** + * Timestamp of when the mousedown or touchstart occurred. + * @type {number} + * @private + */ +goog.fx.Dragger.prototype.mouseDownTime_ = 0; + + +/** + * Reference to a document object to use for the events. + * @type {Document} + * @private + */ +goog.fx.Dragger.prototype.document_; + + +/** + * The SCROLL event target used to make drag element follow scrolling. + * @type {EventTarget} + * @private + */ +goog.fx.Dragger.prototype.scrollTarget_; + + +/** + * Whether IE drag events cancelling is on. + * @type {boolean} + * @private + */ +goog.fx.Dragger.prototype.ieDragStartCancellingOn_ = false; + + +/** + * Whether the dragger implements the changes described in http://b/6324964, + * making it truly RTL. This is a temporary flag to allow clients to transition + * to the new behavior at their convenience. At some point it will be the + * default. + * @type {boolean} + * @private + */ +goog.fx.Dragger.prototype.useRightPositioningForRtl_ = false; + + +/** + * Turns on/off true RTL behavior. This should be called immediately after + * construction. This is a temporary flag to allow clients to transition + * to the new component at their convenience. At some point true will be the + * default. + * @param {boolean} useRightPositioningForRtl True if "right" should be used for + * positioning, false if "left" should be used for positioning. + */ +goog.fx.Dragger.prototype.enableRightPositioningForRtl = + function(useRightPositioningForRtl) { + this.useRightPositioningForRtl_ = useRightPositioningForRtl; +}; + + +/** + * Returns the event handler, intended for subclass use. + * @return {!goog.events.EventHandler<T>} The event handler. + * @this T + * @template T + */ +goog.fx.Dragger.prototype.getHandler = function() { + // TODO(user): templated "this" values currently result in "this" being + // "unknown" in the body of the function. + var self = /** @type {goog.fx.Dragger} */ (this); + return self.eventHandler_; +}; + + +/** + * Sets (or reset) the Drag limits after a Dragger is created. + * @param {goog.math.Rect?} limits Object containing left, top, width, + * height for new Dragger limits. If target is right-to-left and + * enableRightPositioningForRtl(true) is called, then rect is interpreted as + * right, top, width, and height. + */ +goog.fx.Dragger.prototype.setLimits = function(limits) { + this.limits = limits || new goog.math.Rect(NaN, NaN, NaN, NaN); +}; + + +/** + * Sets the distance the user has to drag the element before a drag operation is + * started. + * @param {number} distance The number of pixels after which a mousedown and + * move is considered a drag. + */ +goog.fx.Dragger.prototype.setHysteresis = function(distance) { + this.hysteresisDistanceSquared_ = Math.pow(distance, 2); +}; + + +/** + * Gets the distance the user has to drag the element before a drag operation is + * started. + * @return {number} distance The number of pixels after which a mousedown and + * move is considered a drag. + */ +goog.fx.Dragger.prototype.getHysteresis = function() { + return Math.sqrt(this.hysteresisDistanceSquared_); +}; + + +/** + * Sets the SCROLL event target to make drag element follow scrolling. + * + * @param {EventTarget} scrollTarget The event target that dispatches SCROLL + * events. + */ +goog.fx.Dragger.prototype.setScrollTarget = function(scrollTarget) { + this.scrollTarget_ = scrollTarget; +}; + + +/** + * Enables cancelling of built-in IE drag events. + * @param {boolean} cancelIeDragStart Whether to enable cancelling of IE + * dragstart event. + */ +goog.fx.Dragger.prototype.setCancelIeDragStart = function(cancelIeDragStart) { + this.ieDragStartCancellingOn_ = cancelIeDragStart; +}; + + +/** + * @return {boolean} Whether the dragger is enabled. + */ +goog.fx.Dragger.prototype.getEnabled = function() { + return this.enabled_; +}; + + +/** + * Set whether dragger is enabled + * @param {boolean} enabled Whether dragger is enabled. + */ +goog.fx.Dragger.prototype.setEnabled = function(enabled) { + this.enabled_ = enabled; +}; + + +/** + * Set whether mousedown should be default prevented. + * @param {boolean} preventMouseDown Whether mousedown should be default + * prevented. + */ +goog.fx.Dragger.prototype.setPreventMouseDown = function(preventMouseDown) { + this.preventMouseDown_ = preventMouseDown; +}; + + +/** @override */ +goog.fx.Dragger.prototype.disposeInternal = function() { + goog.fx.Dragger.superClass_.disposeInternal.call(this); + goog.events.unlisten(this.handle, + [goog.events.EventType.TOUCHSTART, goog.events.EventType.MOUSEDOWN], + this.startDrag, false, this); + this.cleanUpAfterDragging_(); + + this.target = null; + this.handle = null; +}; + + +/** + * Whether the DOM element being manipulated is rendered right-to-left. + * @return {boolean} True if the DOM element is rendered right-to-left, false + * otherwise. + * @private + */ +goog.fx.Dragger.prototype.isRightToLeft_ = function() { + if (!goog.isDef(this.rightToLeft_)) { + this.rightToLeft_ = goog.style.isRightToLeft(this.target); + } + return this.rightToLeft_; +}; + + +/** + * Event handler that is used to start the drag + * @param {goog.events.BrowserEvent} e Event object. + */ +goog.fx.Dragger.prototype.startDrag = function(e) { + var isMouseDown = e.type == goog.events.EventType.MOUSEDOWN; + + // Dragger.startDrag() can be called by AbstractDragDrop with a mousemove + // event and IE does not report pressed mouse buttons on mousemove. Also, + // it does not make sense to check for the button if the user is already + // dragging. + + if (this.enabled_ && !this.dragging_ && + (!isMouseDown || e.isMouseActionButton())) { + this.maybeReinitTouchEvent_(e); + if (this.hysteresisDistanceSquared_ == 0) { + if (this.fireDragStart_(e)) { + this.dragging_ = true; + if (this.preventMouseDown_) { + e.preventDefault(); + } + } else { + // If the start drag is cancelled, don't setup for a drag. + return; + } + } else if (this.preventMouseDown_) { + // Need to preventDefault for hysteresis to prevent page getting selected. + e.preventDefault(); + } + this.setupDragHandlers(); + + this.clientX = this.startX = e.clientX; + this.clientY = this.startY = e.clientY; + this.screenX = e.screenX; + this.screenY = e.screenY; + this.computeInitialPosition(); + this.pageScroll = goog.dom.getDomHelper(this.document_).getDocumentScroll(); + + this.mouseDownTime_ = goog.now(); + } else { + this.dispatchEvent(goog.fx.Dragger.EventType.EARLY_CANCEL); + } +}; + + +/** + * Sets up event handlers when dragging starts. + * @protected + */ +goog.fx.Dragger.prototype.setupDragHandlers = function() { + var doc = this.document_; + var docEl = doc.documentElement; + // Use bubbling when we have setCapture since we got reports that IE has + // problems with the capturing events in combination with setCapture. + var useCapture = !goog.fx.Dragger.HAS_SET_CAPTURE_; + + this.eventHandler_.listen(doc, + [goog.events.EventType.TOUCHMOVE, goog.events.EventType.MOUSEMOVE], + this.handleMove_, useCapture); + this.eventHandler_.listen(doc, + [goog.events.EventType.TOUCHEND, goog.events.EventType.MOUSEUP], + this.endDrag, useCapture); + + if (goog.fx.Dragger.HAS_SET_CAPTURE_) { + docEl.setCapture(false); + this.eventHandler_.listen(docEl, + goog.events.EventType.LOSECAPTURE, + this.endDrag); + } else { + // Make sure we stop the dragging if the window loses focus. + // Don't use capture in this listener because we only want to end the drag + // if the actual window loses focus. Since blur events do not bubble we use + // a bubbling listener on the window. + this.eventHandler_.listen(goog.dom.getWindow(doc), + goog.events.EventType.BLUR, + this.endDrag); + } + + if (goog.userAgent.IE && this.ieDragStartCancellingOn_) { + // Cancel IE's 'ondragstart' event. + this.eventHandler_.listen(doc, goog.events.EventType.DRAGSTART, + goog.events.Event.preventDefault); + } + + if (this.scrollTarget_) { + this.eventHandler_.listen(this.scrollTarget_, goog.events.EventType.SCROLL, + this.onScroll_, useCapture); + } +}; + + +/** + * Fires a goog.fx.Dragger.EventType.START event. + * @param {goog.events.BrowserEvent} e Browser event that triggered the drag. + * @return {boolean} False iff preventDefault was called on the DragEvent. + * @private + */ +goog.fx.Dragger.prototype.fireDragStart_ = function(e) { + return this.dispatchEvent(new goog.fx.DragEvent( + goog.fx.Dragger.EventType.START, this, e.clientX, e.clientY, e)); +}; + + +/** + * Unregisters the event handlers that are only active during dragging, and + * releases mouse capture. + * @private + */ +goog.fx.Dragger.prototype.cleanUpAfterDragging_ = function() { + this.eventHandler_.removeAll(); + if (goog.fx.Dragger.HAS_SET_CAPTURE_) { + this.document_.releaseCapture(); + } +}; + + +/** + * Event handler that is used to end the drag. + * @param {goog.events.BrowserEvent} e Event object. + * @param {boolean=} opt_dragCanceled Whether the drag has been canceled. + */ +goog.fx.Dragger.prototype.endDrag = function(e, opt_dragCanceled) { + this.cleanUpAfterDragging_(); + + if (this.dragging_) { + this.maybeReinitTouchEvent_(e); + this.dragging_ = false; + + var x = this.limitX(this.deltaX); + var y = this.limitY(this.deltaY); + var dragCanceled = opt_dragCanceled || + e.type == goog.events.EventType.TOUCHCANCEL; + this.dispatchEvent(new goog.fx.DragEvent( + goog.fx.Dragger.EventType.END, this, e.clientX, e.clientY, e, x, y, + dragCanceled)); + } else { + this.dispatchEvent(goog.fx.Dragger.EventType.EARLY_CANCEL); + } +}; + + +/** + * Event handler that is used to end the drag by cancelling it. + * @param {goog.events.BrowserEvent} e Event object. + */ +goog.fx.Dragger.prototype.endDragCancel = function(e) { + this.endDrag(e, true); +}; + + +/** + * Re-initializes the event with the first target touch event or, in the case + * of a stop event, the last changed touch. + * @param {goog.events.BrowserEvent} e A TOUCH... event. + * @private + */ +goog.fx.Dragger.prototype.maybeReinitTouchEvent_ = function(e) { + var type = e.type; + + if (type == goog.events.EventType.TOUCHSTART || + type == goog.events.EventType.TOUCHMOVE) { + e.init(e.getBrowserEvent().targetTouches[0], e.currentTarget); + } else if (type == goog.events.EventType.TOUCHEND || + type == goog.events.EventType.TOUCHCANCEL) { + e.init(e.getBrowserEvent().changedTouches[0], e.currentTarget); + } +}; + + +/** + * Event handler that is used on mouse / touch move to update the drag + * @param {goog.events.BrowserEvent} e Event object. + * @private + */ +goog.fx.Dragger.prototype.handleMove_ = function(e) { + if (this.enabled_) { + this.maybeReinitTouchEvent_(e); + // dx in right-to-left cases is relative to the right. + var sign = this.useRightPositioningForRtl_ && + this.isRightToLeft_() ? -1 : 1; + var dx = sign * (e.clientX - this.clientX); + var dy = e.clientY - this.clientY; + this.clientX = e.clientX; + this.clientY = e.clientY; + this.screenX = e.screenX; + this.screenY = e.screenY; + + if (!this.dragging_) { + var diffX = this.startX - this.clientX; + var diffY = this.startY - this.clientY; + var distance = diffX * diffX + diffY * diffY; + if (distance > this.hysteresisDistanceSquared_) { + if (this.fireDragStart_(e)) { + this.dragging_ = true; + } else { + // DragListGroup disposes of the dragger if BEFOREDRAGSTART is + // canceled. + if (!this.isDisposed()) { + this.endDrag(e); + } + return; + } + } + } + + var pos = this.calculatePosition_(dx, dy); + var x = pos.x; + var y = pos.y; + + if (this.dragging_) { + + var rv = this.dispatchEvent(new goog.fx.DragEvent( + goog.fx.Dragger.EventType.BEFOREDRAG, this, e.clientX, e.clientY, + e, x, y)); + + // Only do the defaultAction and dispatch drag event if predrag didn't + // prevent default + if (rv) { + this.doDrag(e, x, y, false); + e.preventDefault(); + } + } + } +}; + + +/** + * Calculates the drag position. + * + * @param {number} dx The horizontal movement delta. + * @param {number} dy The vertical movement delta. + * @return {!goog.math.Coordinate} The newly calculated drag element position. + * @private + */ +goog.fx.Dragger.prototype.calculatePosition_ = function(dx, dy) { + // Update the position for any change in body scrolling + var pageScroll = goog.dom.getDomHelper(this.document_).getDocumentScroll(); + dx += pageScroll.x - this.pageScroll.x; + dy += pageScroll.y - this.pageScroll.y; + this.pageScroll = pageScroll; + + this.deltaX += dx; + this.deltaY += dy; + + var x = this.limitX(this.deltaX); + var y = this.limitY(this.deltaY); + return new goog.math.Coordinate(x, y); +}; + + +/** + * Event handler for scroll target scrolling. + * @param {goog.events.BrowserEvent} e The event. + * @private + */ +goog.fx.Dragger.prototype.onScroll_ = function(e) { + var pos = this.calculatePosition_(0, 0); + e.clientX = this.clientX; + e.clientY = this.clientY; + this.doDrag(e, pos.x, pos.y, true); +}; + + +/** + * @param {goog.events.BrowserEvent} e The closure object + * representing the browser event that caused a drag event. + * @param {number} x The new horizontal position for the drag element. + * @param {number} y The new vertical position for the drag element. + * @param {boolean} dragFromScroll Whether dragging was caused by scrolling + * the associated scroll target. + * @protected + */ +goog.fx.Dragger.prototype.doDrag = function(e, x, y, dragFromScroll) { + this.defaultAction(x, y); + this.dispatchEvent(new goog.fx.DragEvent( + goog.fx.Dragger.EventType.DRAG, this, e.clientX, e.clientY, e, x, y)); +}; + + +/** + * Returns the 'real' x after limits are applied (allows for some + * limits to be undefined). + * @param {number} x X-coordinate to limit. + * @return {number} The 'real' X-coordinate after limits are applied. + */ +goog.fx.Dragger.prototype.limitX = function(x) { + var rect = this.limits; + var left = !isNaN(rect.left) ? rect.left : null; + var width = !isNaN(rect.width) ? rect.width : 0; + var maxX = left != null ? left + width : Infinity; + var minX = left != null ? left : -Infinity; + return Math.min(maxX, Math.max(minX, x)); +}; + + +/** + * Returns the 'real' y after limits are applied (allows for some + * limits to be undefined). + * @param {number} y Y-coordinate to limit. + * @return {number} The 'real' Y-coordinate after limits are applied. + */ +goog.fx.Dragger.prototype.limitY = function(y) { + var rect = this.limits; + var top = !isNaN(rect.top) ? rect.top : null; + var height = !isNaN(rect.height) ? rect.height : 0; + var maxY = top != null ? top + height : Infinity; + var minY = top != null ? top : -Infinity; + return Math.min(maxY, Math.max(minY, y)); +}; + + +/** + * Overridable function for computing the initial position of the target + * before dragging begins. + * @protected + */ +goog.fx.Dragger.prototype.computeInitialPosition = function() { + this.deltaX = this.useRightPositioningForRtl_ ? + goog.style.bidi.getOffsetStart(this.target) : this.target.offsetLeft; + this.deltaY = this.target.offsetTop; +}; + + +/** + * Overridable function for handling the default action of the drag behaviour. + * Normally this is simply moving the element to x,y though in some cases it + * might be used to resize the layer. This is basically a shortcut to + * implementing a default ondrag event handler. + * @param {number} x X-coordinate for target element. In right-to-left, x this + * is the number of pixels the target should be moved to from the right. + * @param {number} y Y-coordinate for target element. + */ +goog.fx.Dragger.prototype.defaultAction = function(x, y) { + if (this.useRightPositioningForRtl_ && this.isRightToLeft_()) { + this.target.style.right = x + 'px'; + } else { + this.target.style.left = x + 'px'; + } + this.target.style.top = y + 'px'; +}; + + +/** + * @return {boolean} Whether the dragger is currently in the midst of a drag. + */ +goog.fx.Dragger.prototype.isDragging = function() { + return this.dragging_; +}; + + + +/** + * Object representing a drag event + * @param {string} type Event type. + * @param {goog.fx.Dragger} dragobj Drag object initiating event. + * @param {number} clientX X-coordinate relative to the viewport. + * @param {number} clientY Y-coordinate relative to the viewport. + * @param {goog.events.BrowserEvent} browserEvent The closure object + * representing the browser event that caused this drag event. + * @param {number=} opt_actX Optional actual x for drag if it has been limited. + * @param {number=} opt_actY Optional actual y for drag if it has been limited. + * @param {boolean=} opt_dragCanceled Whether the drag has been canceled. + * @constructor + * @extends {goog.events.Event} + */ +goog.fx.DragEvent = function(type, dragobj, clientX, clientY, browserEvent, + opt_actX, opt_actY, opt_dragCanceled) { + goog.events.Event.call(this, type); + + /** + * X-coordinate relative to the viewport + * @type {number} + */ + this.clientX = clientX; + + /** + * Y-coordinate relative to the viewport + * @type {number} + */ + this.clientY = clientY; + + /** + * The closure object representing the browser event that caused this drag + * event. + * @type {goog.events.BrowserEvent} + */ + this.browserEvent = browserEvent; + + /** + * The real x-position of the drag if it has been limited + * @type {number} + */ + this.left = goog.isDef(opt_actX) ? opt_actX : dragobj.deltaX; + + /** + * The real y-position of the drag if it has been limited + * @type {number} + */ + this.top = goog.isDef(opt_actY) ? opt_actY : dragobj.deltaY; + + /** + * Reference to the drag object for this event + * @type {goog.fx.Dragger} + */ + this.dragger = dragobj; + + /** + * Whether drag was canceled with this event. Used to differentiate between + * a legitimate drag END that can result in an action and a drag END which is + * a result of a drag cancelation. For now it can happen 1) with drag END + * event on FireFox when user drags the mouse out of the window, 2) with + * drag END event on IE7 which is generated on MOUSEMOVE event when user + * moves the mouse into the document after the mouse button has been + * released, 3) when TOUCHCANCEL is raised instead of TOUCHEND (on touch + * events). + * @type {boolean} + */ + this.dragCanceled = !!opt_dragCanceled; +}; +goog.inherits(goog.fx.DragEvent, goog.events.Event);
http://git-wip-us.apache.org/repos/asf/flex-falcon/blob/e2cad6e6/externs/GCL/externs/goog/fx/draglistgroup.js ---------------------------------------------------------------------- diff --git a/externs/GCL/externs/goog/fx/draglistgroup.js b/externs/GCL/externs/goog/fx/draglistgroup.js new file mode 100644 index 0000000..dc20ec0 --- /dev/null +++ b/externs/GCL/externs/goog/fx/draglistgroup.js @@ -0,0 +1,1312 @@ +// Copyright 2008 The Closure Library Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS-IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/** + * @fileoverview A DragListGroup is a class representing a group of one or more + * "drag lists" with items that can be dragged within them and between them. + * + * @see ../demos/draglistgroup.html + */ + + +goog.provide('goog.fx.DragListDirection'); +goog.provide('goog.fx.DragListGroup'); +goog.provide('goog.fx.DragListGroup.EventType'); +goog.provide('goog.fx.DragListGroupEvent'); + +goog.require('goog.array'); +goog.require('goog.asserts'); +goog.require('goog.dom'); +goog.require('goog.dom.classlist'); +goog.require('goog.events'); +goog.require('goog.events.Event'); +goog.require('goog.events.EventHandler'); +goog.require('goog.events.EventTarget'); +goog.require('goog.events.EventType'); +goog.require('goog.fx.Dragger'); +goog.require('goog.math.Coordinate'); +goog.require('goog.string'); +goog.require('goog.style'); + + + +/** + * A class representing a group of one or more "drag lists" with items that can + * be dragged within them and between them. + * + * Example usage: + * var dragListGroup = new goog.fx.DragListGroup(); + * dragListGroup.setDragItemHandleHoverClass(className1, className2); + * dragListGroup.setDraggerElClass(className3); + * dragListGroup.addDragList(vertList, goog.fx.DragListDirection.DOWN); + * dragListGroup.addDragList(horizList, goog.fx.DragListDirection.RIGHT); + * dragListGroup.init(); + * + * @extends {goog.events.EventTarget} + * @constructor + */ +goog.fx.DragListGroup = function() { + goog.events.EventTarget.call(this); + + /** + * The drag lists. + * @type {Array<Element>} + * @private + */ + this.dragLists_ = []; + + /** + * All the drag items. Set by init(). + * @type {Array<Element>} + * @private + */ + this.dragItems_ = []; + + /** + * Which drag item corresponds to a given handle. Set by init(). + * Specifically, this maps from the unique ID (as given by goog.getUid) + * of the handle to the drag item. + * @type {Object} + * @private + */ + this.dragItemForHandle_ = {}; + + /** + * The event handler for this instance. + * @type {goog.events.EventHandler<!goog.fx.DragListGroup>} + * @private + */ + this.eventHandler_ = new goog.events.EventHandler(this); + + /** + * Whether the setup has been done to make all items in all lists draggable. + * @type {boolean} + * @private + */ + this.isInitialized_ = false; + + /** + * Whether the currDragItem is always displayed. By default the list + * collapses, the currDragItem's display is set to none, when we do not + * hover over a draglist. + * @type {boolean} + * @private + */ + this.isCurrDragItemAlwaysDisplayed_ = false; + + /** + * Whether to update the position of the currDragItem as we drag, i.e., + * insert the currDragItem each time to the position where it would land if + * we were to end the drag at that point. Defaults to true. + * @type {boolean} + * @private + */ + this.updateWhileDragging_ = true; +}; +goog.inherits(goog.fx.DragListGroup, goog.events.EventTarget); + + +/** + * Enum to indicate the direction that a drag list grows. + * @enum {number} + */ +goog.fx.DragListDirection = { + DOWN: 0, // common + RIGHT: 2, // common + LEFT: 3, // uncommon (except perhaps for right-to-left interfaces) + RIGHT_2D: 4, // common + handles multiple lines if items are wrapped + LEFT_2D: 5 // for rtl languages +}; + + +/** + * Events dispatched by this class. + * @const + */ +goog.fx.DragListGroup.EventType = { + BEFOREDRAGSTART: 'beforedragstart', + DRAGSTART: 'dragstart', + BEFOREDRAGMOVE: 'beforedragmove', + DRAGMOVE: 'dragmove', + BEFOREDRAGEND: 'beforedragend', + DRAGEND: 'dragend' +}; + + +// The next 4 are user-supplied CSS classes. + + +/** + * The user-supplied CSS classes to add to a drag item on hover (not during a + * drag action). + * @type {Array|undefined} + * @private + */ +goog.fx.DragListGroup.prototype.dragItemHoverClasses_; + + +/** + * The user-supplied CSS classes to add to a drag item handle on hover (not + * during a drag action). + * @type {Array|undefined} + * @private + */ +goog.fx.DragListGroup.prototype.dragItemHandleHoverClasses_; + + +/** + * The user-supplied CSS classes to add to the current drag item (during a drag + * action). + * @type {Array|undefined} + * @private + */ +goog.fx.DragListGroup.prototype.currDragItemClasses_; + + +/** + * The user-supplied CSS classes to add to the clone of the current drag item + * that's actually being dragged around (during a drag action). + * @type {Array<string>|undefined} + * @private + */ +goog.fx.DragListGroup.prototype.draggerElClasses_; + + +// The next 5 are info applicable during a drag action. + + +/** + * The current drag item being moved. + * Note: This is only defined while a drag action is happening. + * @type {Element} + * @private + */ +goog.fx.DragListGroup.prototype.currDragItem_; + + +/** + * The drag list that {@code this.currDragItem_} is currently hovering over, or + * null if it is not hovering over a list. + * @type {Element} + * @private + */ +goog.fx.DragListGroup.prototype.currHoverList_; + + +/** + * The original drag list that the current drag item came from. We need to + * remember this in case the user drops the item outside of any lists, in which + * case we return the item to its original location. + * Note: This is only defined while a drag action is happening. + * @type {Element} + * @private + */ +goog.fx.DragListGroup.prototype.origList_; + + +/** + * The original next item in the original list that the current drag item came + * from. We need to remember this in case the user drops the item outside of + * any lists, in which case we return the item to its original location. + * Note: This is only defined while a drag action is happening. + * @type {Element} + * @private + */ +goog.fx.DragListGroup.prototype.origNextItem_; + + +/** + * The current item in the list we are hovering over. We need to remember + * this in case we do not update the position of the current drag item while + * dragging (see {@code updateWhileDragging_}). In this case the current drag + * item will be inserted into the list before this element when the drag ends. + * @type {Element} + * @private + */ +goog.fx.DragListGroup.prototype.currHoverItem_; + + +/** + * The clone of the current drag item that's actually being dragged around. + * Note: This is only defined while a drag action is happening. + * @type {Element} + * @private + */ +goog.fx.DragListGroup.prototype.draggerEl_; + + +/** + * The dragger object. + * Note: This is only defined while a drag action is happening. + * @type {goog.fx.Dragger} + * @private + */ +goog.fx.DragListGroup.prototype.dragger_; + + +/** + * The amount of distance, in pixels, after which a mousedown or touchstart is + * considered a drag. + * @type {number} + * @private + */ +goog.fx.DragListGroup.prototype.hysteresisDistance_ = 0; + + +/** + * Sets the property of the currDragItem that it is always displayed in the + * list. + */ +goog.fx.DragListGroup.prototype.setIsCurrDragItemAlwaysDisplayed = function() { + this.isCurrDragItemAlwaysDisplayed_ = true; +}; + + +/** + * Sets the private property updateWhileDragging_ to false. This disables the + * update of the position of the currDragItem while dragging. It will only be + * placed to its new location once the drag ends. + */ +goog.fx.DragListGroup.prototype.setNoUpdateWhileDragging = function() { + this.updateWhileDragging_ = false; +}; + + +/** + * Sets the distance the user has to drag the element before a drag operation + * is started. + * @param {number} distance The number of pixels after which a mousedown and + * move is considered a drag. + */ +goog.fx.DragListGroup.prototype.setHysteresis = function(distance) { + this.hysteresisDistance_ = distance; +}; + + +/** + * @return {number} distance The number of pixels after which a mousedown and + * move is considered a drag. + */ +goog.fx.DragListGroup.prototype.getHysteresis = function() { + return this.hysteresisDistance_; +}; + + +/** + * Adds a drag list to this DragListGroup. + * All calls to this method must happen before the call to init(). + * Remember that all child nodes (except text nodes) will be made draggable to + * any other drag list in this group. + * + * @param {Element} dragListElement Must be a container for a list of items + * that should all be made draggable. + * @param {goog.fx.DragListDirection} growthDirection The direction that this + * drag list grows in (i.e. if an item is appended to the DOM, the list's + * bounding box expands in this direction). + * @param {boolean=} opt_unused Unused argument. + * @param {string=} opt_dragHoverClass CSS class to apply to this drag list when + * the draggerEl hovers over it during a drag action. If present, must be a + * single, valid classname (not a string of space-separated classnames). + */ +goog.fx.DragListGroup.prototype.addDragList = function( + dragListElement, growthDirection, opt_unused, opt_dragHoverClass) { + goog.asserts.assert(!this.isInitialized_); + + dragListElement.dlgGrowthDirection_ = growthDirection; + dragListElement.dlgDragHoverClass_ = opt_dragHoverClass; + this.dragLists_.push(dragListElement); +}; + + +/** + * Sets a user-supplied function used to get the "handle" element for a drag + * item. The function must accept exactly one argument. The argument may be + * any drag item element. + * + * If not set, the default implementation uses the whole drag item as the + * handle. + * + * @param {function(Element): Element} getHandleForDragItemFn A function that, + * given any drag item, returns a reference to its "handle" element + * (which may be the drag item element itself). + */ +goog.fx.DragListGroup.prototype.setFunctionToGetHandleForDragItem = function( + getHandleForDragItemFn) { + goog.asserts.assert(!this.isInitialized_); + this.getHandleForDragItem_ = getHandleForDragItemFn; +}; + + +/** + * Sets a user-supplied CSS class to add to a drag item on hover (not during a + * drag action). + * @param {...!string} var_args The CSS class or classes. + */ +goog.fx.DragListGroup.prototype.setDragItemHoverClass = function(var_args) { + goog.asserts.assert(!this.isInitialized_); + this.dragItemHoverClasses_ = goog.array.slice(arguments, 0); +}; + + +/** + * Sets a user-supplied CSS class to add to a drag item handle on hover (not + * during a drag action). + * @param {...!string} var_args The CSS class or classes. + */ +goog.fx.DragListGroup.prototype.setDragItemHandleHoverClass = function( + var_args) { + goog.asserts.assert(!this.isInitialized_); + this.dragItemHandleHoverClasses_ = goog.array.slice(arguments, 0); +}; + + +/** + * Sets a user-supplied CSS class to add to the current drag item (during a + * drag action). + * + * If not set, the default behavior adds visibility:hidden to the current drag + * item so that it is a block of empty space in the hover drag list (if any). + * If this class is set by the user, then the default behavior does not happen + * (unless, of course, the class also contains visibility:hidden). + * + * @param {...!string} var_args The CSS class or classes. + */ +goog.fx.DragListGroup.prototype.setCurrDragItemClass = function(var_args) { + goog.asserts.assert(!this.isInitialized_); + this.currDragItemClasses_ = goog.array.slice(arguments, 0); +}; + + +/** + * Sets a user-supplied CSS class to add to the clone of the current drag item + * that's actually being dragged around (during a drag action). + * @param {string} draggerElClass The CSS class. + */ +goog.fx.DragListGroup.prototype.setDraggerElClass = function(draggerElClass) { + goog.asserts.assert(!this.isInitialized_); + // Split space-separated classes up into an array. + this.draggerElClasses_ = goog.string.trim(draggerElClass).split(' '); +}; + + +/** + * Performs the initial setup to make all items in all lists draggable. + */ +goog.fx.DragListGroup.prototype.init = function() { + if (this.isInitialized_) { + return; + } + + for (var i = 0, numLists = this.dragLists_.length; i < numLists; i++) { + var dragList = this.dragLists_[i]; + + var dragItems = goog.dom.getChildren(dragList); + for (var j = 0, numItems = dragItems.length; j < numItems; ++j) { + this.listenForDragEvents(dragItems[j]); + } + } + + this.isInitialized_ = true; +}; + + +/** + * Adds a single item to the given drag list and sets up the drag listeners for + * it. + * If opt_index is specified the item is inserted at this index, otherwise the + * item is added as the last child of the list. + * + * @param {!Element} list The drag list where to add item to. + * @param {!Element} item The new element to add. + * @param {number=} opt_index Index where to insert the item in the list. If not + * specified item is inserted as the last child of list. + */ +goog.fx.DragListGroup.prototype.addItemToDragList = function(list, item, + opt_index) { + if (goog.isDef(opt_index)) { + goog.dom.insertChildAt(list, item, opt_index); + } else { + goog.dom.appendChild(list, item); + } + this.listenForDragEvents(item); +}; + + +/** @override */ +goog.fx.DragListGroup.prototype.disposeInternal = function() { + this.eventHandler_.dispose(); + + for (var i = 0, n = this.dragLists_.length; i < n; i++) { + var dragList = this.dragLists_[i]; + // Note: IE doesn't allow 'delete' for fields on HTML elements (because + // they're not real JS objects in IE), so we just set them to undefined. + dragList.dlgGrowthDirection_ = undefined; + dragList.dlgDragHoverClass_ = undefined; + } + + this.dragLists_.length = 0; + this.dragItems_.length = 0; + this.dragItemForHandle_ = null; + + // In the case where a drag event is currently in-progress and dispose is + // called, this cleans up the extra state. + this.cleanupDragDom_(); + + goog.fx.DragListGroup.superClass_.disposeInternal.call(this); +}; + + +/** + * Caches the heights of each drag list and drag item, except for the current + * drag item. + * + * @param {Element} currDragItem The item currently being dragged. + * @private + */ +goog.fx.DragListGroup.prototype.recacheListAndItemBounds_ = function( + currDragItem) { + for (var i = 0, n = this.dragLists_.length; i < n; i++) { + var dragList = this.dragLists_[i]; + dragList.dlgBounds_ = goog.style.getBounds(dragList); + } + + for (var i = 0, n = this.dragItems_.length; i < n; i++) { + var dragItem = this.dragItems_[i]; + if (dragItem != currDragItem) { + dragItem.dlgBounds_ = goog.style.getBounds(dragItem); + } + } +}; + + +/** + * Listens for drag events on the given drag item. This method is currently used + * to initialize drag items. + * + * @param {Element} dragItem the element to initialize. This element has to be + * in one of the drag lists. + * @protected + */ +goog.fx.DragListGroup.prototype.listenForDragEvents = function(dragItem) { + var dragItemHandle = this.getHandleForDragItem_(dragItem); + var uid = goog.getUid(dragItemHandle); + this.dragItemForHandle_[uid] = dragItem; + + if (this.dragItemHoverClasses_) { + this.eventHandler_.listen( + dragItem, goog.events.EventType.MOUSEOVER, + this.handleDragItemMouseover_); + this.eventHandler_.listen( + dragItem, goog.events.EventType.MOUSEOUT, + this.handleDragItemMouseout_); + } + if (this.dragItemHandleHoverClasses_) { + this.eventHandler_.listen( + dragItemHandle, goog.events.EventType.MOUSEOVER, + this.handleDragItemHandleMouseover_); + this.eventHandler_.listen( + dragItemHandle, goog.events.EventType.MOUSEOUT, + this.handleDragItemHandleMouseout_); + } + + this.dragItems_.push(dragItem); + this.eventHandler_.listen(dragItemHandle, + [goog.events.EventType.MOUSEDOWN, goog.events.EventType.TOUCHSTART], + this.handlePotentialDragStart_); +}; + + +/** + * Handles mouse and touch events which may start a drag action. + * @param {!goog.events.BrowserEvent} e MOUSEDOWN or TOUCHSTART event. + * @private + */ +goog.fx.DragListGroup.prototype.handlePotentialDragStart_ = function(e) { + var uid = goog.getUid(/** @type {Node} */ (e.currentTarget)); + this.currDragItem_ = /** @type {Element} */ (this.dragItemForHandle_[uid]); + + this.draggerEl_ = this.createDragElementInternal(this.currDragItem_); + if (this.draggerElClasses_) { + // Add CSS class for the clone, if any. + goog.dom.classlist.addAll( + goog.asserts.assert(this.draggerEl_), this.draggerElClasses_ || []); + } + + // Place the clone (i.e. draggerEl) at the same position as the actual + // current drag item. This is a bit tricky since + // goog.style.getPageOffset() gets the left-top pos of the border, but + // goog.style.setPageOffset() sets the left-top pos of the margin. + // It's difficult to adjust for the margins of the clone because it's + // difficult to read it: goog.style.getComputedStyle() doesn't work for IE. + // Instead, our workaround is simply to set the clone's margins to 0px. + this.draggerEl_.style.margin = '0'; + this.draggerEl_.style.position = 'absolute'; + this.draggerEl_.style.visibility = 'hidden'; + var doc = goog.dom.getOwnerDocument(this.currDragItem_); + doc.body.appendChild(this.draggerEl_); + + // Important: goog.style.setPageOffset() only works correctly for IE when the + // element is already in the document. + var currDragItemPos = goog.style.getPageOffset(this.currDragItem_); + goog.style.setPageOffset(this.draggerEl_, currDragItemPos); + + this.dragger_ = new goog.fx.Dragger(this.draggerEl_); + this.dragger_.setHysteresis(this.hysteresisDistance_); + + // Listen to events on the dragger. These handlers will be unregistered at + // DRAGEND, when the dragger is disposed of. We can't use eventHandler_, + // because it creates new references to the handler functions at each + // dragging action, and keeps them until DragListGroup is disposed of. + goog.events.listen(this.dragger_, goog.fx.Dragger.EventType.START, + this.handleDragStart_, false, this); + goog.events.listen(this.dragger_, goog.fx.Dragger.EventType.END, + this.handleDragEnd_, false, this); + goog.events.listen(this.dragger_, goog.fx.Dragger.EventType.EARLY_CANCEL, + this.cleanup_, false, this); + this.dragger_.startDrag(e); +}; + + +/** + * Creates copy of node being dragged. + * + * @param {Element} sourceEl Element to copy. + * @return {!Element} The clone of {@code sourceEl}. + * @deprecated Use goog.fx.Dragger.cloneNode(). + * @private + */ +goog.fx.DragListGroup.prototype.cloneNode_ = function(sourceEl) { + return goog.fx.Dragger.cloneNode(sourceEl); +}; + + +/** + * Generates an element to follow the cursor during dragging, given a drag + * source element. The default behavior is simply to clone the source element, + * but this may be overridden in subclasses. This method is called by + * {@code createDragElement()} before the drag class is added. + * + * @param {Element} sourceEl Drag source element. + * @return {!Element} The new drag element. + * @protected + * @suppress {deprecated} + */ +goog.fx.DragListGroup.prototype.createDragElementInternal = + function(sourceEl) { + return this.cloneNode_(sourceEl); +}; + + +/** + * Handles the start of a drag action. + * @param {!goog.fx.DragEvent} e goog.fx.Dragger.EventType.START event. + * @private + */ +goog.fx.DragListGroup.prototype.handleDragStart_ = function(e) { + if (!this.dispatchEvent(new goog.fx.DragListGroupEvent( + goog.fx.DragListGroup.EventType.BEFOREDRAGSTART, this, e.browserEvent, + this.currDragItem_, null, null))) { + e.preventDefault(); + this.cleanup_(); + return; + } + + // Record the original location of the current drag item. + // Note: this.origNextItem_ may be null. + this.origList_ = /** @type {Element} */ (this.currDragItem_.parentNode); + this.origNextItem_ = goog.dom.getNextElementSibling(this.currDragItem_); + this.currHoverItem_ = this.origNextItem_; + this.currHoverList_ = this.origList_; + + // If there's a CSS class specified for the current drag item, add it. + // Otherwise, make the actual current drag item hidden (takes up space). + if (this.currDragItemClasses_) { + goog.dom.classlist.addAll( + goog.asserts.assert(this.currDragItem_), + this.currDragItemClasses_ || []); + } else { + this.currDragItem_.style.visibility = 'hidden'; + } + + // Precompute distances from top-left corner to center for efficiency. + var draggerElSize = goog.style.getSize(this.draggerEl_); + this.draggerEl_.halfWidth = draggerElSize.width / 2; + this.draggerEl_.halfHeight = draggerElSize.height / 2; + + this.draggerEl_.style.visibility = ''; + + // Record the bounds of all the drag lists and all the other drag items. This + // caching is for efficiency, so that we don't have to recompute the bounds on + // each drag move. Do this in the state where the current drag item is not in + // any of the lists, except when update while dragging is disabled, as in this + // case the current drag item does not get removed until drag ends. + if (this.updateWhileDragging_) { + this.currDragItem_.style.display = 'none'; + } + this.recacheListAndItemBounds_(this.currDragItem_); + this.currDragItem_.style.display = ''; + + // Listen to events on the dragger. + goog.events.listen(this.dragger_, goog.fx.Dragger.EventType.DRAG, + this.handleDragMove_, false, this); + + this.dispatchEvent( + new goog.fx.DragListGroupEvent( + goog.fx.DragListGroup.EventType.DRAGSTART, this, e.browserEvent, + this.currDragItem_, this.draggerEl_, this.dragger_)); +}; + + +/** + * Handles a drag movement (i.e. DRAG event fired by the dragger). + * + * @param {goog.fx.DragEvent} dragEvent Event object fired by the dragger. + * @return {boolean} The return value for the event. + * @private + */ +goog.fx.DragListGroup.prototype.handleDragMove_ = function(dragEvent) { + + // Compute the center of the dragger element (i.e. the cloned drag item). + var draggerElPos = goog.style.getPageOffset(this.draggerEl_); + var draggerElCenter = new goog.math.Coordinate( + draggerElPos.x + this.draggerEl_.halfWidth, + draggerElPos.y + this.draggerEl_.halfHeight); + + // Check whether the center is hovering over one of the drag lists. + var hoverList = this.getHoverDragList_(draggerElCenter); + + // If hovering over a list, find the next item (if drag were to end now). + var hoverNextItem = + hoverList ? this.getHoverNextItem_(hoverList, draggerElCenter) : null; + + var rv = this.dispatchEvent( + new goog.fx.DragListGroupEvent( + goog.fx.DragListGroup.EventType.BEFOREDRAGMOVE, this, dragEvent, + this.currDragItem_, this.draggerEl_, this.dragger_, + draggerElCenter, hoverList, hoverNextItem)); + if (!rv) { + return false; + } + + if (hoverList) { + if (this.updateWhileDragging_) { + this.insertCurrDragItem_(hoverList, hoverNextItem); + } else { + // If update while dragging is disabled do not insert + // the dragged item, but update the hovered item instead. + this.updateCurrHoverItem(hoverNextItem, draggerElCenter); + } + this.currDragItem_.style.display = ''; + // Add drag list's hover class (if any). + if (hoverList.dlgDragHoverClass_) { + goog.dom.classlist.add( + goog.asserts.assert(hoverList), hoverList.dlgDragHoverClass_); + } + + } else { + // Not hovering over a drag list, so remove the item altogether unless + // specified otherwise by the user. + if (!this.isCurrDragItemAlwaysDisplayed_) { + this.currDragItem_.style.display = 'none'; + } + + // Remove hover classes (if any) from all drag lists. + for (var i = 0, n = this.dragLists_.length; i < n; i++) { + var dragList = this.dragLists_[i]; + if (dragList.dlgDragHoverClass_) { + goog.dom.classlist.remove( + goog.asserts.assert(dragList), dragList.dlgDragHoverClass_); + } + } + } + + // If the current hover list is different than the last, the lists may have + // shrunk, so we should recache the bounds. + if (hoverList != this.currHoverList_) { + this.currHoverList_ = hoverList; + this.recacheListAndItemBounds_(this.currDragItem_); + } + + this.dispatchEvent( + new goog.fx.DragListGroupEvent( + goog.fx.DragListGroup.EventType.DRAGMOVE, this, dragEvent, + /** @type {Element} */ (this.currDragItem_), + this.draggerEl_, this.dragger_, + draggerElCenter, hoverList, hoverNextItem)); + + // Return false to prevent selection due to mouse drag. + return false; +}; + + +/** + * Clear all our temporary fields that are only defined while dragging, and + * all the bounds info stored on the drag lists and drag elements. + * @param {!goog.events.Event=} opt_e EARLY_CANCEL event from the dragger if + * cleanup_ was called as an event handler. + * @private + */ +goog.fx.DragListGroup.prototype.cleanup_ = function(opt_e) { + this.cleanupDragDom_(); + + this.currDragItem_ = null; + this.currHoverList_ = null; + this.origList_ = null; + this.origNextItem_ = null; + this.draggerEl_ = null; + this.dragger_ = null; + + // Note: IE doesn't allow 'delete' for fields on HTML elements (because + // they're not real JS objects in IE), so we just set them to null. + for (var i = 0, n = this.dragLists_.length; i < n; i++) { + this.dragLists_[i].dlgBounds_ = null; + } + for (var i = 0, n = this.dragItems_.length; i < n; i++) { + this.dragItems_[i].dlgBounds_ = null; + } +}; + + +/** + * Handles the end or the cancellation of a drag action, i.e. END or CLEANUP + * event fired by the dragger. + * + * @param {!goog.fx.DragEvent} dragEvent Event object fired by the dragger. + * @return {boolean} Whether the event was handled. + * @private + */ +goog.fx.DragListGroup.prototype.handleDragEnd_ = function(dragEvent) { + var rv = this.dispatchEvent( + new goog.fx.DragListGroupEvent( + goog.fx.DragListGroup.EventType.BEFOREDRAGEND, this, dragEvent, + /** @type {Element} */ (this.currDragItem_), + this.draggerEl_, this.dragger_)); + if (!rv) { + return false; + } + + // If update while dragging is disabled insert the current drag item into + // its intended location. + if (!this.updateWhileDragging_) { + this.insertCurrHoverItem(); + } + + // The DRAGEND handler may need the new order of the list items. Clean up the + // garbage. + // TODO(user): Regression test. + this.cleanupDragDom_(); + + this.dispatchEvent( + new goog.fx.DragListGroupEvent( + goog.fx.DragListGroup.EventType.DRAGEND, this, dragEvent, + this.currDragItem_, this.draggerEl_, this.dragger_)); + + this.cleanup_(); + + return true; +}; + + +/** + * Cleans up DOM changes that are made by the {@code handleDrag*} methods. + * @private + */ +goog.fx.DragListGroup.prototype.cleanupDragDom_ = function() { + // Disposes of the dragger and remove the cloned drag item. + goog.dispose(this.dragger_); + if (this.draggerEl_) { + goog.dom.removeNode(this.draggerEl_); + } + + // If the current drag item is not in any list, put it back in its original + // location. + if (this.currDragItem_ && this.currDragItem_.style.display == 'none') { + // Note: this.origNextItem_ may be null, but insertBefore() still works. + this.origList_.insertBefore(this.currDragItem_, this.origNextItem_); + this.currDragItem_.style.display = ''; + } + + // If there's a CSS class specified for the current drag item, remove it. + // Otherwise, make the current drag item visible (instead of empty space). + if (this.currDragItemClasses_ && this.currDragItem_) { + goog.dom.classlist.removeAll( + goog.asserts.assert(this.currDragItem_), + this.currDragItemClasses_ || []); + } else if (this.currDragItem_) { + this.currDragItem_.style.visibility = 'visible'; + } + + // Remove hover classes (if any) from all drag lists. + for (var i = 0, n = this.dragLists_.length; i < n; i++) { + var dragList = this.dragLists_[i]; + if (dragList.dlgDragHoverClass_) { + goog.dom.classlist.remove( + goog.asserts.assert(dragList), dragList.dlgDragHoverClass_); + } + } +}; + + +/** + * Default implementation of the function to get the "handle" element for a + * drag item. By default, we use the whole drag item as the handle. Users can + * change this by calling setFunctionToGetHandleForDragItem(). + * + * @param {Element} dragItem The drag item to get the handle for. + * @return {Element} The dragItem element itself. + * @private + */ +goog.fx.DragListGroup.prototype.getHandleForDragItem_ = function(dragItem) { + return dragItem; +}; + + +/** + * Handles a MOUSEOVER event fired on a drag item. + * @param {goog.events.BrowserEvent} e The event. + * @private + */ +goog.fx.DragListGroup.prototype.handleDragItemMouseover_ = function(e) { + var targetEl = goog.asserts.assertElement(e.currentTarget); + goog.dom.classlist.addAll(targetEl, this.dragItemHoverClasses_ || []); +}; + + +/** + * Handles a MOUSEOUT event fired on a drag item. + * @param {goog.events.BrowserEvent} e The event. + * @private + */ +goog.fx.DragListGroup.prototype.handleDragItemMouseout_ = function(e) { + var targetEl = goog.asserts.assertElement(e.currentTarget); + goog.dom.classlist.removeAll(targetEl, this.dragItemHoverClasses_ || []); +}; + + +/** + * Handles a MOUSEOVER event fired on the handle element of a drag item. + * @param {goog.events.BrowserEvent} e The event. + * @private + */ +goog.fx.DragListGroup.prototype.handleDragItemHandleMouseover_ = function(e) { + var targetEl = goog.asserts.assertElement(e.currentTarget); + goog.dom.classlist.addAll(targetEl, this.dragItemHandleHoverClasses_ || []); +}; + + +/** + * Handles a MOUSEOUT event fired on the handle element of a drag item. + * @param {goog.events.BrowserEvent} e The event. + * @private + */ +goog.fx.DragListGroup.prototype.handleDragItemHandleMouseout_ = function(e) { + var targetEl = goog.asserts.assertElement(e.currentTarget); + goog.dom.classlist.removeAll(targetEl, + this.dragItemHandleHoverClasses_ || []); +}; + + +/** + * Helper for handleDragMove_(). + * Given the position of the center of the dragger element, figures out whether + * it's currently hovering over any of the drag lists. + * + * @param {goog.math.Coordinate} draggerElCenter The center position of the + * dragger element. + * @return {Element} If currently hovering over a drag list, returns the drag + * list element. Else returns null. + * @private + */ +goog.fx.DragListGroup.prototype.getHoverDragList_ = function(draggerElCenter) { + + // If the current drag item was in a list last time we did this, then check + // that same list first. + var prevHoverList = null; + if (this.currDragItem_.style.display != 'none') { + prevHoverList = /** @type {Element} */ (this.currDragItem_.parentNode); + // Important: We can't use the cached bounds for this list because the + // cached bounds are based on the case where the current drag item is not + // in the list. Since the current drag item is known to be in this list, we + // must recompute the list's bounds. + var prevHoverListBounds = goog.style.getBounds(prevHoverList); + if (this.isInRect_(draggerElCenter, prevHoverListBounds)) { + return prevHoverList; + } + } + + for (var i = 0, n = this.dragLists_.length; i < n; i++) { + var dragList = this.dragLists_[i]; + if (dragList == prevHoverList) { + continue; + } + if (this.isInRect_(draggerElCenter, dragList.dlgBounds_)) { + return dragList; + } + } + + return null; +}; + + +/** + * Checks whether a coordinate position resides inside a rectangle. + * @param {goog.math.Coordinate} pos The coordinate position. + * @param {goog.math.Rect} rect The rectangle. + * @return {boolean} True if 'pos' is within the bounds of 'rect'. + * @private + */ +goog.fx.DragListGroup.prototype.isInRect_ = function(pos, rect) { + return pos.x > rect.left && pos.x < rect.left + rect.width && + pos.y > rect.top && pos.y < rect.top + rect.height; +}; + + +/** + * Updates the value of currHoverItem_. + * + * This method is used for insertion only when updateWhileDragging_ is false. + * The below implementation is the basic one. This method can be extended by + * a subclass to support changes to hovered item (eg: highlighting). Parametr + * opt_draggerElCenter can be used for more sophisticated effects. + * + * @param {Element} hoverNextItem element of the list that is hovered over. + * @param {goog.math.Coordinate=} opt_draggerElCenter current position of + * the dragged element. + * @protected + */ +goog.fx.DragListGroup.prototype.updateCurrHoverItem = function( + hoverNextItem, opt_draggerElCenter) { + if (hoverNextItem) { + this.currHoverItem_ = hoverNextItem; + } +}; + + +/** + * Inserts the currently dragged item in its new place. + * + * This method is used for insertion only when updateWhileDragging_ is false + * (otherwise there is no need for that). In the basic implementation + * the element is inserted before the currently hovered over item (this can + * be changed by overriding the method in subclasses). + * + * @protected + */ +goog.fx.DragListGroup.prototype.insertCurrHoverItem = function() { + this.origList_.insertBefore(this.currDragItem_, this.currHoverItem_); +}; + + +/** + * Helper for handleDragMove_(). + * Given the position of the center of the dragger element, plus the drag list + * that it's currently hovering over, figures out the next drag item in the + * list that follows the current position of the dragger element. (I.e. if + * the drag action ends right now, it would become the item after the current + * drag item.) + * + * @param {Element} hoverList The drag list that we're hovering over. + * @param {goog.math.Coordinate} draggerElCenter The center position of the + * dragger element. + * @return {Element} Returns the earliest item in the hover list that belongs + * after the current position of the dragger element. If all items in the + * list should come before the current drag item, then returns null. + * @private + */ +goog.fx.DragListGroup.prototype.getHoverNextItem_ = function( + hoverList, draggerElCenter) { + if (hoverList == null) { + throw Error('getHoverNextItem_ called with null hoverList.'); + } + + // The definition of what it means for the draggerEl to be "before" a given + // item in the hover drag list is not always the same. It changes based on + // the growth direction of the hover drag list in question. + /** @type {number} */ + var relevantCoord; + var getRelevantBoundFn; + var isBeforeFn; + var pickClosestRow = false; + var distanceToClosestRow = undefined; + switch (hoverList.dlgGrowthDirection_) { + case goog.fx.DragListDirection.DOWN: + // "Before" means draggerElCenter.y is less than item's bottom y-value. + relevantCoord = draggerElCenter.y; + getRelevantBoundFn = goog.fx.DragListGroup.getBottomBound_; + isBeforeFn = goog.fx.DragListGroup.isLessThan_; + break; + case goog.fx.DragListDirection.RIGHT_2D: + pickClosestRow = true; + case goog.fx.DragListDirection.RIGHT: + // "Before" means draggerElCenter.x is less than item's right x-value. + relevantCoord = draggerElCenter.x; + getRelevantBoundFn = goog.fx.DragListGroup.getRightBound_; + isBeforeFn = goog.fx.DragListGroup.isLessThan_; + break; + case goog.fx.DragListDirection.LEFT_2D: + pickClosestRow = true; + case goog.fx.DragListDirection.LEFT: + // "Before" means draggerElCenter.x is greater than item's left x-value. + relevantCoord = draggerElCenter.x; + getRelevantBoundFn = goog.fx.DragListGroup.getLeftBound_; + isBeforeFn = goog.fx.DragListGroup.isGreaterThan_; + break; + } + + // This holds the earliest drag item found so far that should come after + // this.currDragItem_ in the hover drag list (based on draggerElCenter). + var earliestAfterItem = null; + // This is the position of the relevant bound for the earliestAfterItem, + // where "relevant" is determined by the growth direction of hoverList. + var earliestAfterItemRelevantBound; + + var hoverListItems = goog.dom.getChildren(hoverList); + for (var i = 0, n = hoverListItems.length; i < n; i++) { + var item = hoverListItems[i]; + if (item == this.currDragItem_) { + continue; + } + + var relevantBound = getRelevantBoundFn(item.dlgBounds_); + // When the hoverlist is broken into multiple rows (i.e., in the case of + // LEFT_2D and RIGHT_2D) it is no longer enough to only look at the + // x-coordinate alone in order to find the {@earliestAfterItem} in the + // hoverlist. Make sure it is chosen from the row closest to the + // {@code draggerElCenter}. + if (pickClosestRow) { + var distanceToRow = goog.fx.DragListGroup.verticalDistanceFromItem_(item, + draggerElCenter); + // Initialize the distance to the closest row to the current value if + // undefined. + if (!goog.isDef(distanceToClosestRow)) { + distanceToClosestRow = distanceToRow; + } + if (isBeforeFn(relevantCoord, relevantBound) && + (earliestAfterItemRelevantBound == undefined || + (distanceToRow < distanceToClosestRow) || + ((distanceToRow == distanceToClosestRow) && + (isBeforeFn(relevantBound, earliestAfterItemRelevantBound) || + relevantBound == earliestAfterItemRelevantBound)))) { + earliestAfterItem = item; + earliestAfterItemRelevantBound = relevantBound; + } + // Update distance to closest row. + if (distanceToRow < distanceToClosestRow) { + distanceToClosestRow = distanceToRow; + } + } else if (isBeforeFn(relevantCoord, relevantBound) && + (earliestAfterItemRelevantBound == undefined || + isBeforeFn(relevantBound, earliestAfterItemRelevantBound))) { + earliestAfterItem = item; + earliestAfterItemRelevantBound = relevantBound; + } + } + // If we ended up picking an element that is not in the closest row it can + // only happen if we should have picked the last one in which case there is + // no consecutive element. + if (!goog.isNull(earliestAfterItem) && + goog.fx.DragListGroup.verticalDistanceFromItem_( + earliestAfterItem, draggerElCenter) > distanceToClosestRow) { + return null; + } else { + return earliestAfterItem; + } +}; + + +/** + * Private helper for getHoverNextItem(). + * Given an item and a target determine the vertical distance from the item's + * center to the target. + * @param {Element} item The item to measure the distance from. + * @param {goog.math.Coordinate} target The (x,y) coordinate of the target + * to measure the distance to. + * @return {number} The vertical distance between the center of the item and + * the target. + * @private + */ +goog.fx.DragListGroup.verticalDistanceFromItem_ = function(item, target) { + var itemBounds = item.dlgBounds_; + var itemCenterY = itemBounds.top + (itemBounds.height - 1) / 2; + return Math.abs(target.y - itemCenterY); +}; + + +/** + * Private helper for getHoverNextItem_(). + * Given the bounds of an item, computes the item's bottom y-value. + * @param {goog.math.Rect} itemBounds The bounds of the item. + * @return {number} The item's bottom y-value. + * @private + */ +goog.fx.DragListGroup.getBottomBound_ = function(itemBounds) { + return itemBounds.top + itemBounds.height - 1; +}; + + +/** + * Private helper for getHoverNextItem_(). + * Given the bounds of an item, computes the item's right x-value. + * @param {goog.math.Rect} itemBounds The bounds of the item. + * @return {number} The item's right x-value. + * @private + */ +goog.fx.DragListGroup.getRightBound_ = function(itemBounds) { + return itemBounds.left + itemBounds.width - 1; +}; + + +/** + * Private helper for getHoverNextItem_(). + * Given the bounds of an item, computes the item's left x-value. + * @param {goog.math.Rect} itemBounds The bounds of the item. + * @return {number} The item's left x-value. + * @private + */ +goog.fx.DragListGroup.getLeftBound_ = function(itemBounds) { + return itemBounds.left || 0; +}; + + +/** + * Private helper for getHoverNextItem_(). + * @param {number} a Number to compare. + * @param {number} b Number to compare. + * @return {boolean} Whether a is less than b. + * @private + */ +goog.fx.DragListGroup.isLessThan_ = function(a, b) { + return a < b; +}; + + +/** + * Private helper for getHoverNextItem_(). + * @param {number} a Number to compare. + * @param {number} b Number to compare. + * @return {boolean} Whether a is greater than b. + * @private + */ +goog.fx.DragListGroup.isGreaterThan_ = function(a, b) { + return a > b; +}; + + +/** + * Inserts the current drag item to the appropriate location in the drag list + * that we're hovering over (if the current drag item is not already there). + * + * @param {Element} hoverList The drag list we're hovering over. + * @param {Element} hoverNextItem The next item in the hover drag list. + * @private + */ +goog.fx.DragListGroup.prototype.insertCurrDragItem_ = function( + hoverList, hoverNextItem) { + if (this.currDragItem_.parentNode != hoverList || + goog.dom.getNextElementSibling(this.currDragItem_) != hoverNextItem) { + // The current drag item is not in the correct location, so we move it. + // Note: hoverNextItem may be null, but insertBefore() still works. + hoverList.insertBefore(this.currDragItem_, hoverNextItem); + } +}; + + + +/** + * The event object dispatched by DragListGroup. + * The fields draggerElCenter, hoverList, and hoverNextItem are only available + * for the BEFOREDRAGMOVE and DRAGMOVE events. + * + * @param {string} type The event type string. + * @param {goog.fx.DragListGroup} dragListGroup A reference to the associated + * DragListGroup object. + * @param {goog.events.BrowserEvent|goog.fx.DragEvent} event The event fired + * by the browser or fired by the dragger. + * @param {Element} currDragItem The current drag item being moved. + * @param {Element} draggerEl The clone of the current drag item that's actually + * being dragged around. + * @param {goog.fx.Dragger} dragger The dragger object. + * @param {goog.math.Coordinate=} opt_draggerElCenter The current center + * position of the draggerEl. + * @param {Element=} opt_hoverList The current drag list that's being hovered + * over, or null if the center of draggerEl is outside of any drag lists. + * If not null and the drag action ends right now, then currDragItem will + * end up in this list. + * @param {Element=} opt_hoverNextItem The current next item in the hoverList + * that the draggerEl is hovering over. (I.e. If the drag action ends + * right now, then this item would become the next item after the new + * location of currDragItem.) May be null if not applicable or if + * currDragItem would be added to the end of hoverList. + * @constructor + * @extends {goog.events.Event} + */ +goog.fx.DragListGroupEvent = function( + type, dragListGroup, event, currDragItem, draggerEl, dragger, + opt_draggerElCenter, opt_hoverList, opt_hoverNextItem) { + goog.events.Event.call(this, type); + + /** + * A reference to the associated DragListGroup object. + * @type {goog.fx.DragListGroup} + */ + this.dragListGroup = dragListGroup; + + /** + * The event fired by the browser or fired by the dragger. + * @type {goog.events.BrowserEvent|goog.fx.DragEvent} + */ + this.event = event; + + /** + * The current drag item being move. + * @type {Element} + */ + this.currDragItem = currDragItem; + + /** + * The clone of the current drag item that's actually being dragged around. + * @type {Element} + */ + this.draggerEl = draggerEl; + + /** + * The dragger object. + * @type {goog.fx.Dragger} + */ + this.dragger = dragger; + + /** + * The current center position of the draggerEl. + * @type {goog.math.Coordinate|undefined} + */ + this.draggerElCenter = opt_draggerElCenter; + + /** + * The current drag list that's being hovered over, or null if the center of + * draggerEl is outside of any drag lists. (I.e. If not null and the drag + * action ends right now, then currDragItem will end up in this list.) + * @type {Element|undefined} + */ + this.hoverList = opt_hoverList; + + /** + * The current next item in the hoverList that the draggerEl is hovering over. + * (I.e. If the drag action ends right now, then this item would become the + * next item after the new location of currDragItem.) May be null if not + * applicable or if currDragItem would be added to the end of hoverList. + * @type {Element|undefined} + */ + this.hoverNextItem = opt_hoverNextItem; +}; +goog.inherits(goog.fx.DragListGroupEvent, goog.events.Event); http://git-wip-us.apache.org/repos/asf/flex-falcon/blob/e2cad6e6/externs/GCL/externs/goog/fx/dragscrollsupport.js ---------------------------------------------------------------------- diff --git a/externs/GCL/externs/goog/fx/dragscrollsupport.js b/externs/GCL/externs/goog/fx/dragscrollsupport.js new file mode 100644 index 0000000..66072e8 --- /dev/null +++ b/externs/GCL/externs/goog/fx/dragscrollsupport.js @@ -0,0 +1,300 @@ +// Copyright 2008 The Closure Library Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS-IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/** + * @fileoverview Class to support scrollable containers for drag and drop. + * + * @author [email protected] (Damian Gajda) + */ + +goog.provide('goog.fx.DragScrollSupport'); + +goog.require('goog.Disposable'); +goog.require('goog.Timer'); +goog.require('goog.dom'); +goog.require('goog.events.EventHandler'); +goog.require('goog.events.EventType'); +goog.require('goog.math.Coordinate'); +goog.require('goog.style'); + + + +/** + * A scroll support class. Currently this class will automatically scroll + * a scrollable container node and scroll it by a fixed amount at a timed + * interval when the mouse is moved above or below the container or in vertical + * margin areas. Intended for use in drag and drop. This could potentially be + * made more general and could support horizontal scrolling. + * + * @param {Element} containerNode A container that can be scrolled. + * @param {number=} opt_margin Optional margin to use while scrolling. + * @param {boolean=} opt_externalMouseMoveTracking Whether mouse move events + * are tracked externally by the client object which calls the mouse move + * event handler, useful when events are generated for more than one source + * element and/or are not real mousemove events. + * @constructor + * @extends {goog.Disposable} + * @see ../demos/dragscrollsupport.html + */ +goog.fx.DragScrollSupport = function(containerNode, opt_margin, + opt_externalMouseMoveTracking) { + goog.Disposable.call(this); + + /** + * The container to be scrolled. + * @type {Element} + * @private + */ + this.containerNode_ = containerNode; + + /** + * Scroll timer that will scroll the container until it is stopped. + * It will scroll when the mouse is outside the scrolling area of the + * container. + * + * @type {goog.Timer} + * @private + */ + this.scrollTimer_ = new goog.Timer(goog.fx.DragScrollSupport.TIMER_STEP_); + + /** + * EventHandler used to set up and tear down listeners. + * @type {goog.events.EventHandler<!goog.fx.DragScrollSupport>} + * @private + */ + this.eventHandler_ = new goog.events.EventHandler(this); + + /** + * The current scroll delta. + * @type {goog.math.Coordinate} + * @private + */ + this.scrollDelta_ = new goog.math.Coordinate(); + + /** + * The container bounds. + * @type {goog.math.Rect} + * @private + */ + this.containerBounds_ = goog.style.getBounds(containerNode); + + /** + * The margin for triggering a scroll. + * @type {number} + * @private + */ + this.margin_ = opt_margin || 0; + + /** + * The bounding rectangle which if left triggers scrolling. + * @type {goog.math.Rect} + * @private + */ + this.scrollBounds_ = opt_margin ? + this.constrainBounds_(this.containerBounds_.clone()) : + this.containerBounds_; + + this.setupListeners_(!!opt_externalMouseMoveTracking); +}; +goog.inherits(goog.fx.DragScrollSupport, goog.Disposable); + + +/** + * The scroll timer step in ms. + * @type {number} + * @private + */ +goog.fx.DragScrollSupport.TIMER_STEP_ = 50; + + +/** + * The scroll step in pixels. + * @type {number} + * @private + */ +goog.fx.DragScrollSupport.SCROLL_STEP_ = 8; + + +/** + * The suggested scrolling margin. + * @type {number} + */ +goog.fx.DragScrollSupport.MARGIN = 32; + + +/** + * Whether scrolling should be constrained to happen only when the cursor is + * inside the container node. + * @type {boolean} + * @private + */ +goog.fx.DragScrollSupport.prototype.constrainScroll_ = false; + + +/** + * Whether horizontal scrolling is allowed. + * @type {boolean} + * @private + */ +goog.fx.DragScrollSupport.prototype.horizontalScrolling_ = true; + + +/** + * Sets whether scrolling should be constrained to happen only when the cursor + * is inside the container node. + * NOTE: If a margin is not set, then it does not make sense to + * contain the scroll, because in that case scroll will never be triggered. + * @param {boolean} constrain Whether scrolling should be constrained to happen + * only when the cursor is inside the container node. + */ +goog.fx.DragScrollSupport.prototype.setConstrainScroll = function(constrain) { + this.constrainScroll_ = !!this.margin_ && constrain; +}; + + +/** + * Sets whether horizontal scrolling is allowed. + * @param {boolean} scrolling Whether horizontal scrolling is allowed. + */ +goog.fx.DragScrollSupport.prototype.setHorizontalScrolling = + function(scrolling) { + this.horizontalScrolling_ = scrolling; +}; + + +/** + * Constrains the container bounds with respect to the margin. + * + * @param {goog.math.Rect} bounds The container element. + * @return {goog.math.Rect} The bounding rectangle used to calculate scrolling + * direction. + * @private + */ +goog.fx.DragScrollSupport.prototype.constrainBounds_ = function(bounds) { + var margin = this.margin_; + if (margin) { + var quarterHeight = bounds.height * 0.25; + var yMargin = Math.min(margin, quarterHeight); + bounds.top += yMargin; + bounds.height -= 2 * yMargin; + + var quarterWidth = bounds.width * 0.25; + var xMargin = Math.min(margin, quarterWidth); + bounds.top += xMargin; + bounds.height -= 2 * xMargin; + } + return bounds; +}; + + +/** + * Attaches listeners and activates automatic scrolling. + * @param {boolean} externalMouseMoveTracking Whether to enable internal + * mouse move event handling. + * @private + */ +goog.fx.DragScrollSupport.prototype.setupListeners_ = function( + externalMouseMoveTracking) { + if (!externalMouseMoveTracking) { + // Track mouse pointer position to determine scroll direction. + this.eventHandler_.listen(goog.dom.getOwnerDocument(this.containerNode_), + goog.events.EventType.MOUSEMOVE, this.onMouseMove); + } + + // Scroll with a constant speed. + this.eventHandler_.listen(this.scrollTimer_, goog.Timer.TICK, this.onTick_); +}; + + +/** + * Handler for timer tick event, scrolls the container by one scroll step if + * needed. + * @param {goog.events.Event} event Timer tick event. + * @private + */ +goog.fx.DragScrollSupport.prototype.onTick_ = function(event) { + this.containerNode_.scrollTop += this.scrollDelta_.y; + this.containerNode_.scrollLeft += this.scrollDelta_.x; +}; + + +/** + * Handler for mouse moves events. + * @param {goog.events.Event} event Mouse move event. + */ +goog.fx.DragScrollSupport.prototype.onMouseMove = function(event) { + var deltaX = this.horizontalScrolling_ ? this.calculateScrollDelta( + event.clientX, this.scrollBounds_.left, this.scrollBounds_.width) : 0; + var deltaY = this.calculateScrollDelta(event.clientY, + this.scrollBounds_.top, this.scrollBounds_.height); + this.scrollDelta_.x = deltaX; + this.scrollDelta_.y = deltaY; + + // If the scroll data is 0 or the event fired outside of the + // bounds of the container node. + if ((!deltaX && !deltaY) || + (this.constrainScroll_ && + !this.isInContainerBounds_(event.clientX, event.clientY))) { + this.scrollTimer_.stop(); + } else if (!this.scrollTimer_.enabled) { + this.scrollTimer_.start(); + } +}; + + +/** + * Gets whether the input coordinate is in the container bounds. + * @param {number} x The x coordinate. + * @param {number} y The y coordinate. + * @return {boolean} Whether the input coordinate is in the container bounds. + * @private + */ +goog.fx.DragScrollSupport.prototype.isInContainerBounds_ = function(x, y) { + var containerBounds = this.containerBounds_; + return containerBounds.left <= x && + containerBounds.left + containerBounds.width >= x && + containerBounds.top <= y && + containerBounds.top + containerBounds.height >= y; +}; + + +/** + * Calculates scroll delta. + * + * @param {number} coordinate Current mouse pointer coordinate. + * @param {number} min The coordinate value below which scrolling up should be + * started. + * @param {number} rangeLength The length of the range in which scrolling should + * be disabled and above which scrolling down should be started. + * @return {number} The calculated scroll delta. + * @protected + */ +goog.fx.DragScrollSupport.prototype.calculateScrollDelta = function( + coordinate, min, rangeLength) { + var delta = 0; + if (coordinate < min) { + delta = -goog.fx.DragScrollSupport.SCROLL_STEP_; + } else if (coordinate > min + rangeLength) { + delta = goog.fx.DragScrollSupport.SCROLL_STEP_; + } + return delta; +}; + + +/** @override */ +goog.fx.DragScrollSupport.prototype.disposeInternal = function() { + goog.fx.DragScrollSupport.superClass_.disposeInternal.call(this); + this.eventHandler_.dispose(); + this.scrollTimer_.dispose(); +}; http://git-wip-us.apache.org/repos/asf/flex-falcon/blob/e2cad6e6/externs/GCL/externs/goog/fx/easing.js ---------------------------------------------------------------------- diff --git a/externs/GCL/externs/goog/fx/easing.js b/externs/GCL/externs/goog/fx/easing.js new file mode 100644 index 0000000..fda5122 --- /dev/null +++ b/externs/GCL/externs/goog/fx/easing.js @@ -0,0 +1,85 @@ +// Copyright 2006 The Closure Library Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS-IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/** + * @fileoverview Easing functions for animations. + * + * @author [email protected] (Erik Arvidsson) + */ + +goog.provide('goog.fx.easing'); + + +/** + * Ease in - Start slow and speed up. + * @param {number} t Input between 0 and 1. + * @return {number} Output between 0 and 1. + */ +goog.fx.easing.easeIn = function(t) { + return goog.fx.easing.easeInInternal_(t, 3); +}; + + +/** + * Ease in with specifiable exponent. + * @param {number} t Input between 0 and 1. + * @param {number} exp Ease exponent. + * @return {number} Output between 0 and 1. + * @private + */ +goog.fx.easing.easeInInternal_ = function(t, exp) { + return Math.pow(t, exp); +}; + + +/** + * Ease out - Start fastest and slows to a stop. + * @param {number} t Input between 0 and 1. + * @return {number} Output between 0 and 1. + */ +goog.fx.easing.easeOut = function(t) { + return goog.fx.easing.easeOutInternal_(t, 3); +}; + + +/** + * Ease out with specifiable exponent. + * @param {number} t Input between 0 and 1. + * @param {number} exp Ease exponent. + * @return {number} Output between 0 and 1. + * @private + */ +goog.fx.easing.easeOutInternal_ = function(t, exp) { + return 1 - goog.fx.easing.easeInInternal_(1 - t, exp); +}; + + +/** + * Ease out long - Start fastest and slows to a stop with a long ease. + * @param {number} t Input between 0 and 1. + * @return {number} Output between 0 and 1. + */ +goog.fx.easing.easeOutLong = function(t) { + return goog.fx.easing.easeOutInternal_(t, 4); +}; + + +/** + * Ease in and out - Start slow, speed up, then slow down. + * @param {number} t Input between 0 and 1. + * @return {number} Output between 0 and 1. + */ +goog.fx.easing.inAndOut = function(t) { + return 3 * t * t - 2 * t * t * t; +}; http://git-wip-us.apache.org/repos/asf/flex-falcon/blob/e2cad6e6/externs/GCL/externs/goog/fx/fx.js ---------------------------------------------------------------------- diff --git a/externs/GCL/externs/goog/fx/fx.js b/externs/GCL/externs/goog/fx/fx.js new file mode 100644 index 0000000..10314b6 --- /dev/null +++ b/externs/GCL/externs/goog/fx/fx.js @@ -0,0 +1,34 @@ +// Copyright 2010 The Closure Library Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS-IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/** + * @fileoverview Legacy stub for the goog.fx namespace. Requires the moved + * namespaces. Animation and easing have been moved to animation.js and + * easing.js. Users of this stub should move off so we may remove it in the + * future. + * + * @author [email protected] (Nathan Naze) + * @suppress {extraRequire} All the requires in this file are "extra" + * because this file is not actually using them. + */ + +goog.provide('goog.fx'); + +goog.require('goog.asserts'); +goog.require('goog.fx.Animation'); +goog.require('goog.fx.Animation.EventType'); +goog.require('goog.fx.Animation.State'); +goog.require('goog.fx.AnimationEvent'); +goog.require('goog.fx.Transition.EventType'); +goog.require('goog.fx.easing'); http://git-wip-us.apache.org/repos/asf/flex-falcon/blob/e2cad6e6/externs/GCL/externs/goog/fx/transition.js ---------------------------------------------------------------------- diff --git a/externs/GCL/externs/goog/fx/transition.js b/externs/GCL/externs/goog/fx/transition.js new file mode 100644 index 0000000..57c4304 --- /dev/null +++ b/externs/GCL/externs/goog/fx/transition.js @@ -0,0 +1,76 @@ +// Copyright 2011 The Closure Library Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS-IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/** + * @fileoverview An interface for transition animation. This is a simple + * interface that allows for playing and stopping a transition. It adds + * a simple event model with BEGIN and END event. + * + * @author [email protected] (Chris Henry) + */ + +goog.provide('goog.fx.Transition'); +goog.provide('goog.fx.Transition.EventType'); + + + +/** + * An interface for programmatic transition. Must extend + * {@code goog.events.EventTarget}. + * @interface + */ +goog.fx.Transition = function() {}; + + +/** + * Transition event types. + * @enum {string} + */ +goog.fx.Transition.EventType = { + /** Dispatched when played for the first time OR when it is resumed. */ + PLAY: 'play', + + /** Dispatched only when the animation starts from the beginning. */ + BEGIN: 'begin', + + /** Dispatched only when animation is restarted after a pause. */ + RESUME: 'resume', + + /** + * Dispatched when animation comes to the end of its duration OR stop + * is called. + */ + END: 'end', + + /** Dispatched only when stop is called. */ + STOP: 'stop', + + /** Dispatched only when animation comes to its end naturally. */ + FINISH: 'finish', + + /** Dispatched when an animation is paused. */ + PAUSE: 'pause' +}; + + +/** + * Plays the transition. + */ +goog.fx.Transition.prototype.play; + + +/** + * Stops the transition. + */ +goog.fx.Transition.prototype.stop;
