You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@flex.apache.org by ft...@apache.org on 2015/09/17 17:28:28 UTC

[13/51] [abbrv] [partial] git commit: [flex-falcon] [refs/heads/JsToAs] - Added GCL extern.

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 dgajda@google.com (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 arv@google.com (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 nnaze@google.com (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 chrishenry@google.com (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;