Return-Path: X-Original-To: apmail-flex-commits-archive@www.apache.org Delivered-To: apmail-flex-commits-archive@www.apache.org Received: from mail.apache.org (hermes.apache.org [140.211.11.3]) by minotaur.apache.org (Postfix) with SMTP id DF6B2104B7 for ; Thu, 17 Sep 2015 15:28:17 +0000 (UTC) Received: (qmail 73869 invoked by uid 500); 17 Sep 2015 15:28:17 -0000 Delivered-To: apmail-flex-commits-archive@flex.apache.org Received: (qmail 73763 invoked by uid 500); 17 Sep 2015 15:28:17 -0000 Mailing-List: contact commits-help@flex.apache.org; run by ezmlm Precedence: bulk List-Help: List-Unsubscribe: List-Post: List-Id: Reply-To: dev@flex.apache.org Delivered-To: mailing list commits@flex.apache.org Received: (qmail 72911 invoked by uid 99); 17 Sep 2015 15:28:17 -0000 Received: from git1-us-west.apache.org (HELO git1-us-west.apache.org) (140.211.11.23) by apache.org (qpsmtpd/0.29) with ESMTP; Thu, 17 Sep 2015 15:28:17 +0000 Received: by git1-us-west.apache.org (ASF Mail Server at git1-us-west.apache.org, from userid 33) id CCE79E109C; Thu, 17 Sep 2015 15:28:16 +0000 (UTC) Content-Type: text/plain; charset="us-ascii" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit From: fthomas@apache.org To: commits@flex.apache.org Date: Thu, 17 Sep 2015 15:28:24 -0000 Message-Id: <60bf0aaf8e9c447ea5809575f96b8c44@git.apache.org> In-Reply-To: <761aedb1e31440a28bc60df134c87d98@git.apache.org> References: <761aedb1e31440a28bc60df134c87d98@git.apache.org> X-Mailer: ASF-Git Admin Mailer Subject: [09/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/graphics/vmlelement.js ---------------------------------------------------------------------- diff --git a/externs/GCL/externs/goog/graphics/vmlelement.js b/externs/GCL/externs/goog/graphics/vmlelement.js new file mode 100644 index 0000000..9e72b13 --- /dev/null +++ b/externs/GCL/externs/goog/graphics/vmlelement.js @@ -0,0 +1,438 @@ +// Copyright 2007 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 Thin wrappers around the DOM element returned from + * the different draw methods of the graphics. This is the VML implementation. + * @author arv@google.com (Erik Arvidsson) + */ + +goog.provide('goog.graphics.VmlEllipseElement'); +goog.provide('goog.graphics.VmlGroupElement'); +goog.provide('goog.graphics.VmlImageElement'); +goog.provide('goog.graphics.VmlPathElement'); +goog.provide('goog.graphics.VmlRectElement'); +goog.provide('goog.graphics.VmlTextElement'); + + +goog.require('goog.dom'); +goog.require('goog.graphics.EllipseElement'); +goog.require('goog.graphics.GroupElement'); +goog.require('goog.graphics.ImageElement'); +goog.require('goog.graphics.PathElement'); +goog.require('goog.graphics.RectElement'); +goog.require('goog.graphics.TextElement'); + + +/** + * Returns the VML element corresponding to this object. This method is added + * to several classes below. Note that the return value of this method may + * change frequently in IE8, so it should not be cached externally. + * @return {Element} The VML element corresponding to this object. + * @this {goog.graphics.VmlGroupElement|goog.graphics.VmlEllipseElement| + * goog.graphics.VmlRectElement|goog.graphics.VmlPathElement| + * goog.graphics.VmlTextElement|goog.graphics.VmlImageElement} + * @private + */ +goog.graphics.vmlGetElement_ = function() { + this.element_ = this.getGraphics().getVmlElement(this.id_) || this.element_; + return this.element_; +}; + + + +/** + * Thin wrapper for VML group elements. + * This is an implementation of the goog.graphics.GroupElement interface. + * You should not construct objects from this constructor. The graphics + * will return the object for you. + * @param {Element} element The DOM element to wrap. + * @param {goog.graphics.VmlGraphics} graphics The graphics creating + * this element. + * @constructor + * @extends {goog.graphics.GroupElement} + * @deprecated goog.graphics is deprecated. It existed to abstract over browser + * differences before the canvas tag was widely supported. See + * http://en.wikipedia.org/wiki/Canvas_element for details. + * @final + */ +goog.graphics.VmlGroupElement = function(element, graphics) { + this.id_ = element.id; + goog.graphics.GroupElement.call(this, element, graphics); +}; +goog.inherits(goog.graphics.VmlGroupElement, goog.graphics.GroupElement); + + +/** @override */ +goog.graphics.VmlGroupElement.prototype.getElement = + goog.graphics.vmlGetElement_; + + +/** + * Remove all drawing elements from the group. + * @override + */ +goog.graphics.VmlGroupElement.prototype.clear = function() { + goog.dom.removeChildren(this.getElement()); +}; + + +/** + * @return {boolean} True if this group is the root canvas element. + * @private + */ +goog.graphics.VmlGroupElement.prototype.isRootElement_ = function() { + return this.getGraphics().getCanvasElement() == this; +}; + + +/** + * Set the size of the group element. + * @param {number|string} width The width of the group element. + * @param {number|string} height The height of the group element. + * @override + */ +goog.graphics.VmlGroupElement.prototype.setSize = function(width, height) { + var element = this.getElement(); + + var style = element.style; + style.width = /** @suppress {missingRequire} */ ( + goog.graphics.VmlGraphics.toSizePx(width)); + style.height = /** @suppress {missingRequire} */ ( + goog.graphics.VmlGraphics.toSizePx(height)); + + element.coordsize = /** @suppress {missingRequire} */ + goog.graphics.VmlGraphics.toSizeCoord(width) + + ' ' + + /** @suppress {missingRequire} */ + goog.graphics.VmlGraphics.toSizeCoord(height); + + // Don't overwrite the root element's origin. + if (!this.isRootElement_()) { + element.coordorigin = '0 0'; + } +}; + + + +/** + * Thin wrapper for VML ellipse elements. + * This is an implementation of the goog.graphics.EllipseElement interface. + * You should not construct objects from this constructor. The graphics + * will return the object for you. + * @param {Element} element The DOM element to wrap. + * @param {goog.graphics.VmlGraphics} graphics The graphics creating + * this element. + * @param {number} cx Center X coordinate. + * @param {number} cy Center Y coordinate. + * @param {number} rx Radius length for the x-axis. + * @param {number} ry Radius length for the y-axis. + * @param {goog.graphics.Stroke?} stroke The stroke to use for this element. + * @param {goog.graphics.Fill?} fill The fill to use for this element. + * @constructor + * @extends {goog.graphics.EllipseElement} + * @deprecated goog.graphics is deprecated. It existed to abstract over browser + * differences before the canvas tag was widely supported. See + * http://en.wikipedia.org/wiki/Canvas_element for details. + * @final + */ +goog.graphics.VmlEllipseElement = function(element, graphics, + cx, cy, rx, ry, stroke, fill) { + this.id_ = element.id; + + goog.graphics.EllipseElement.call(this, element, graphics, stroke, fill); + + // Store center and radius for future calls to setRadius or setCenter. + + /** + * X coordinate of the ellipse center. + * @type {number} + */ + this.cx = cx; + + + /** + * Y coordinate of the ellipse center. + * @type {number} + */ + this.cy = cy; + + + /** + * Radius length for the x-axis. + * @type {number} + */ + this.rx = rx; + + + /** + * Radius length for the y-axis. + * @type {number} + */ + this.ry = ry; +}; +goog.inherits(goog.graphics.VmlEllipseElement, goog.graphics.EllipseElement); + + +/** @override */ +goog.graphics.VmlEllipseElement.prototype.getElement = + goog.graphics.vmlGetElement_; + + +/** + * Update the center point of the ellipse. + * @param {number} cx Center X coordinate. + * @param {number} cy Center Y coordinate. + * @override + */ +goog.graphics.VmlEllipseElement.prototype.setCenter = function(cx, cy) { + this.cx = cx; + this.cy = cy; + /** @suppress {missingRequire} */ + goog.graphics.VmlGraphics.setPositionAndSize(this.getElement(), + cx - this.rx, cy - this.ry, this.rx * 2, this.ry * 2); +}; + + +/** + * Update the radius of the ellipse. + * @param {number} rx Center X coordinate. + * @param {number} ry Center Y coordinate. + * @override + */ +goog.graphics.VmlEllipseElement.prototype.setRadius = function(rx, ry) { + this.rx = rx; + this.ry = ry; + /** @suppress {missingRequire} */ + goog.graphics.VmlGraphics.setPositionAndSize(this.getElement(), + this.cx - rx, this.cy - ry, rx * 2, ry * 2); +}; + + + +/** + * Thin wrapper for VML rectangle elements. + * This is an implementation of the goog.graphics.RectElement interface. + * You should not construct objects from this constructor. The graphics + * will return the object for you. + * @param {Element} element The DOM element to wrap. + * @param {goog.graphics.VmlGraphics} graphics The graphics creating + * this element. + * @param {goog.graphics.Stroke?} stroke The stroke to use for this element. + * @param {goog.graphics.Fill?} fill The fill to use for this element. + * @constructor + * @extends {goog.graphics.RectElement} + * @deprecated goog.graphics is deprecated. It existed to abstract over browser + * differences before the canvas tag was widely supported. See + * http://en.wikipedia.org/wiki/Canvas_element for details. + * @final + */ +goog.graphics.VmlRectElement = function(element, graphics, stroke, fill) { + this.id_ = element.id; + goog.graphics.RectElement.call(this, element, graphics, stroke, fill); +}; +goog.inherits(goog.graphics.VmlRectElement, goog.graphics.RectElement); + + +/** @override */ +goog.graphics.VmlRectElement.prototype.getElement = + goog.graphics.vmlGetElement_; + + +/** + * Update the position of the rectangle. + * @param {number} x X coordinate (left). + * @param {number} y Y coordinate (top). + * @override + */ +goog.graphics.VmlRectElement.prototype.setPosition = function(x, y) { + var style = this.getElement().style; + + style.left = /** @suppress {missingRequire} */ + goog.graphics.VmlGraphics.toPosPx(x); + style.top = /** @suppress {missingRequire} */ + goog.graphics.VmlGraphics.toPosPx(y); +}; + + +/** + * Update the size of the rectangle. + * @param {number} width Width of rectangle. + * @param {number} height Height of rectangle. + * @override + */ +goog.graphics.VmlRectElement.prototype.setSize = function(width, height) { + var style = this.getElement().style; + style.width = /** @suppress {missingRequire} */ + goog.graphics.VmlGraphics.toSizePx(width); + style.height = /** @suppress {missingRequire} */ + goog.graphics.VmlGraphics.toSizePx(height); +}; + + + +/** + * Thin wrapper for VML path elements. + * This is an implementation of the goog.graphics.PathElement interface. + * You should not construct objects from this constructor. The graphics + * will return the object for you. + * @param {Element} element The DOM element to wrap. + * @param {goog.graphics.VmlGraphics} graphics The graphics creating + * this element. + * @param {goog.graphics.Stroke?} stroke The stroke to use for this element. + * @param {goog.graphics.Fill?} fill The fill to use for this element. + * @constructor + * @extends {goog.graphics.PathElement} + * @deprecated goog.graphics is deprecated. It existed to abstract over browser + * differences before the canvas tag was widely supported. See + * http://en.wikipedia.org/wiki/Canvas_element for details. + * @final + */ +goog.graphics.VmlPathElement = function(element, graphics, stroke, fill) { + this.id_ = element.id; + goog.graphics.PathElement.call(this, element, graphics, stroke, fill); +}; +goog.inherits(goog.graphics.VmlPathElement, goog.graphics.PathElement); + + +/** @override */ +goog.graphics.VmlPathElement.prototype.getElement = + goog.graphics.vmlGetElement_; + + +/** + * Update the underlying path. + * @param {!goog.graphics.Path} path The path object to draw. + * @override + */ +goog.graphics.VmlPathElement.prototype.setPath = function(path) { + /** @suppress {missingRequire} */ + goog.graphics.VmlGraphics.setAttribute( + this.getElement(), 'path', + /** @suppress {missingRequire} */ + goog.graphics.VmlGraphics.getVmlPath(path)); +}; + + + +/** + * Thin wrapper for VML text elements. + * This is an implementation of the goog.graphics.TextElement interface. + * You should not construct objects from this constructor. The graphics + * will return the object for you. + * @param {Element} element The DOM element to wrap. + * @param {goog.graphics.VmlGraphics} graphics The graphics creating + * this element. + * @param {goog.graphics.Stroke?} stroke The stroke to use for this element. + * @param {goog.graphics.Fill?} fill The fill to use for this element. + * @constructor + * @extends {goog.graphics.TextElement} + * @deprecated goog.graphics is deprecated. It existed to abstract over browser + * differences before the canvas tag was widely supported. See + * http://en.wikipedia.org/wiki/Canvas_element for details. + * @final + */ +goog.graphics.VmlTextElement = function(element, graphics, stroke, fill) { + this.id_ = element.id; + goog.graphics.TextElement.call(this, element, graphics, stroke, fill); +}; +goog.inherits(goog.graphics.VmlTextElement, goog.graphics.TextElement); + + +/** @override */ +goog.graphics.VmlTextElement.prototype.getElement = + goog.graphics.vmlGetElement_; + + +/** + * Update the displayed text of the element. + * @param {string} text The text to draw. + * @override + */ +goog.graphics.VmlTextElement.prototype.setText = function(text) { + /** @suppress {missingRequire} */ + goog.graphics.VmlGraphics.setAttribute(this.getElement().childNodes[1], + 'string', text); +}; + + + +/** + * Thin wrapper for VML image elements. + * This is an implementation of the goog.graphics.ImageElement interface. + * You should not construct objects from this constructor. The graphics + * will return the object for you. + * @param {Element} element The DOM element to wrap. + * @param {goog.graphics.VmlGraphics} graphics The graphics creating + * this element. + * @constructor + * @extends {goog.graphics.ImageElement} + * @deprecated goog.graphics is deprecated. It existed to abstract over browser + * differences before the canvas tag was widely supported. See + * http://en.wikipedia.org/wiki/Canvas_element for details. + * @final + */ +goog.graphics.VmlImageElement = function(element, graphics) { + this.id_ = element.id; + goog.graphics.ImageElement.call(this, element, graphics); +}; +goog.inherits(goog.graphics.VmlImageElement, goog.graphics.ImageElement); + + +/** @override */ +goog.graphics.VmlImageElement.prototype.getElement = + goog.graphics.vmlGetElement_; + + +/** + * Update the position of the image. + * @param {number} x X coordinate (left). + * @param {number} y Y coordinate (top). + * @override + */ +goog.graphics.VmlImageElement.prototype.setPosition = function(x, y) { + var style = this.getElement().style; + + style.left = /** @suppress {missingRequire} */ + goog.graphics.VmlGraphics.toPosPx(x); + style.top = /** @suppress {missingRequire} */ + goog.graphics.VmlGraphics.toPosPx(y); +}; + + +/** + * Update the size of the image. + * @param {number} width Width of rectangle. + * @param {number} height Height of rectangle. + * @override + */ +goog.graphics.VmlImageElement.prototype.setSize = function(width, height) { + var style = this.getElement().style; + style.width = /** @suppress {missingRequire} */ + goog.graphics.VmlGraphics.toPosPx(width); + style.height = /** @suppress {missingRequire} */ + goog.graphics.VmlGraphics.toPosPx(height); +}; + + +/** + * Update the source of the image. + * @param {string} src Source of the image. + * @override + */ +goog.graphics.VmlImageElement.prototype.setSource = function(src) { + /** @suppress {missingRequire} */ + goog.graphics.VmlGraphics.setAttribute(this.getElement(), 'src', src); +}; http://git-wip-us.apache.org/repos/asf/flex-falcon/blob/e2cad6e6/externs/GCL/externs/goog/graphics/vmlgraphics.js ---------------------------------------------------------------------- diff --git a/externs/GCL/externs/goog/graphics/vmlgraphics.js b/externs/GCL/externs/goog/graphics/vmlgraphics.js new file mode 100644 index 0000000..5e0cd66 --- /dev/null +++ b/externs/GCL/externs/goog/graphics/vmlgraphics.js @@ -0,0 +1,948 @@ +// Copyright 2007 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 VmlGraphics sub class that uses VML to draw the graphics. + * @author arv@google.com (Erik Arvidsson) + */ + + +goog.provide('goog.graphics.VmlGraphics'); + + +goog.require('goog.array'); +goog.require('goog.dom.TagName'); +goog.require('goog.dom.safe'); +goog.require('goog.events'); +goog.require('goog.events.EventHandler'); +goog.require('goog.events.EventType'); +goog.require('goog.graphics.AbstractGraphics'); +goog.require('goog.graphics.LinearGradient'); +goog.require('goog.graphics.Path'); +goog.require('goog.graphics.SolidFill'); +goog.require('goog.graphics.VmlEllipseElement'); +goog.require('goog.graphics.VmlGroupElement'); +goog.require('goog.graphics.VmlImageElement'); +goog.require('goog.graphics.VmlPathElement'); +goog.require('goog.graphics.VmlRectElement'); +goog.require('goog.graphics.VmlTextElement'); +goog.require('goog.html.uncheckedconversions'); +goog.require('goog.math'); +goog.require('goog.math.Size'); +goog.require('goog.string'); +goog.require('goog.string.Const'); +goog.require('goog.style'); + + + +/** + * A Graphics implementation for drawing using VML. + * @param {string|number} width The (non-zero) width in pixels. Strings + * expressing percentages of parent with (e.g. '80%') are also accepted. + * @param {string|number} height The (non-zero) height in pixels. Strings + * expressing percentages of parent with (e.g. '80%') are also accepted. + * @param {?number=} opt_coordWidth The coordinate width - if + * omitted or null, defaults to same as width. + * @param {?number=} opt_coordHeight The coordinate height - if + * omitted or null, defaults to same as height. + * @param {goog.dom.DomHelper=} opt_domHelper The DOM helper object for the + * document we want to render in. + * @constructor + * @extends {goog.graphics.AbstractGraphics} + * @deprecated goog.graphics is deprecated. It existed to abstract over browser + * differences before the canvas tag was widely supported. See + * http://en.wikipedia.org/wiki/Canvas_element for details. + * @final + */ +goog.graphics.VmlGraphics = function(width, height, + opt_coordWidth, opt_coordHeight, + opt_domHelper) { + goog.graphics.AbstractGraphics.call(this, width, height, + opt_coordWidth, opt_coordHeight, + opt_domHelper); + this.handler_ = new goog.events.EventHandler(this); + this.registerDisposable(this.handler_); +}; +goog.inherits(goog.graphics.VmlGraphics, goog.graphics.AbstractGraphics); + + +/** + * The prefix to use for VML elements + * @private + * @type {string} + */ +goog.graphics.VmlGraphics.VML_PREFIX_ = 'g_vml_'; + + +/** + * The VML namespace URN + * @private + * @type {string} + */ +goog.graphics.VmlGraphics.VML_NS_ = 'urn:schemas-microsoft-com:vml'; + + +/** + * The VML behavior URL. + * @private + * @type {string} + */ +goog.graphics.VmlGraphics.VML_IMPORT_ = '#default#VML'; + + +/** + * Whether the document is using IE8 standards mode, and therefore needs hacks. + * @private + * @type {boolean} + */ +goog.graphics.VmlGraphics.IE8_MODE_ = + goog.global.document && goog.global.document.documentMode && + goog.global.document.documentMode >= 8; + + +/** + * The coordinate multiplier to allow sub-pixel rendering + * @type {number} + */ +goog.graphics.VmlGraphics.COORD_MULTIPLIER = 100; + + +/** + * Converts the given size to a css size. If it is a percentage, leaves it + * alone. Otherwise assumes px. + * + * @param {number|string} size The size to use. + * @return {string} The position adjusted for COORD_MULTIPLIER. + */ +goog.graphics.VmlGraphics.toCssSize = function(size) { + return goog.isString(size) && goog.string.endsWith(size, '%') ? + size : parseFloat(size.toString()) + 'px'; +}; + + +/** + * Multiplies positioning coordinates by COORD_MULTIPLIER to allow sub-pixel + * coordinates. Also adds a half pixel offset to match SVG. + * + * This function is internal for the VML supporting classes, and + * should not be used externally. + * + * @param {number|string} number A position in pixels. + * @return {number} The position adjusted for COORD_MULTIPLIER. + */ +goog.graphics.VmlGraphics.toPosCoord = function(number) { + return Math.round((parseFloat(number.toString()) - 0.5) * + goog.graphics.VmlGraphics.COORD_MULTIPLIER); +}; + + +/** + * Add a "px" suffix to a number of pixels, and multiplies all coordinates by + * COORD_MULTIPLIER to allow sub-pixel coordinates. + * + * This function is internal for the VML supporting classes, and + * should not be used externally. + * + * @param {number|string} number A position in pixels. + * @return {string} The position with suffix 'px'. + */ +goog.graphics.VmlGraphics.toPosPx = function(number) { + return goog.graphics.VmlGraphics.toPosCoord(number) + 'px'; +}; + + +/** + * Multiplies the width or height coordinate by COORD_MULTIPLIER to allow + * sub-pixel coordinates. + * + * This function is internal for the VML supporting classes, and + * should not be used externally. + * + * @param {string|number} number A size in units. + * @return {number} The size multiplied by the correct factor. + */ +goog.graphics.VmlGraphics.toSizeCoord = function(number) { + return Math.round(parseFloat(number.toString()) * + goog.graphics.VmlGraphics.COORD_MULTIPLIER); +}; + + +/** + * Add a "px" suffix to a number of pixels, and multiplies all coordinates by + * COORD_MULTIPLIER to allow sub-pixel coordinates. + * + * This function is internal for the VML supporting classes, and + * should not be used externally. + * + * @param {number|string} number A size in pixels. + * @return {string} The size with suffix 'px'. + */ +goog.graphics.VmlGraphics.toSizePx = function(number) { + return goog.graphics.VmlGraphics.toSizeCoord(number) + 'px'; +}; + + +/** + * Sets an attribute on the given VML element, in the way best suited to the + * current version of IE. Should only be used in the goog.graphics package. + * @param {Element} element The element to set an attribute + * on. + * @param {string} name The name of the attribute to set. + * @param {string} value The value to set it to. + */ +goog.graphics.VmlGraphics.setAttribute = function(element, name, value) { + if (goog.graphics.VmlGraphics.IE8_MODE_) { + element[name] = value; + } else { + element.setAttribute(name, value); + } +}; + + +/** + * Event handler. + * @type {goog.events.EventHandler} + * @private + */ +goog.graphics.VmlGraphics.prototype.handler_; + + +/** + * Creates a VML element. Used internally and by different VML classes. + * @param {string} tagName The type of element to create. + * @return {!Element} The created element. + */ +goog.graphics.VmlGraphics.prototype.createVmlElement = function(tagName) { + var element = + this.dom_.createElement(goog.graphics.VmlGraphics.VML_PREFIX_ + ':' + + tagName); + element.id = goog.string.createUniqueString(); + return element; +}; + + +/** + * Returns the VML element with the given id that is a child of this graphics + * object. + * Should be considered package private, and not used externally. + * @param {string} id The element id to find. + * @return {Element} The element with the given id, or null if none is found. + */ +goog.graphics.VmlGraphics.prototype.getVmlElement = function(id) { + return this.dom_.getElement(id); +}; + + +/** + * Resets the graphics so they will display properly on IE8. Noop in older + * versions. + * @private + */ +goog.graphics.VmlGraphics.prototype.updateGraphics_ = function() { + if (goog.graphics.VmlGraphics.IE8_MODE_ && this.isInDocument()) { + // There's a risk of mXSS here, as the browser is not guaranteed to + // return the HTML that was originally written, when innerHTML is read. + // However, given that this a deprecated API and affects only IE, it seems + // an acceptable risk. + var html = goog.html.uncheckedconversions + .safeHtmlFromStringKnownToSatisfyTypeContract( + goog.string.Const.from('Assign innerHTML to itself'), + this.getElement().innerHTML); + goog.dom.safe.setInnerHtml( + /** @type {!Element} */ (this.getElement()), html); + } +}; + + +/** + * Appends an element. + * + * @param {goog.graphics.Element} element The element wrapper. + * @param {goog.graphics.GroupElement=} opt_group The group wrapper element + * to append to. If not specified, appends to the main canvas. + * @private + */ +goog.graphics.VmlGraphics.prototype.append_ = function(element, opt_group) { + var parent = opt_group || this.canvasElement; + parent.getElement().appendChild(element.getElement()); + this.updateGraphics_(); +}; + + +/** + * Sets the fill for the given element. + * @param {goog.graphics.StrokeAndFillElement} element The element wrapper. + * @param {goog.graphics.Fill?} fill The fill object. + * @override + */ +goog.graphics.VmlGraphics.prototype.setElementFill = function(element, fill) { + var vmlElement = element.getElement(); + goog.graphics.VmlGraphics.removeFill_(vmlElement); + if (fill instanceof goog.graphics.SolidFill) { + // NOTE(arv): VML does not understand 'transparent' so hard code support + // for it. + if (fill.getColor() == 'transparent') { + vmlElement.filled = false; + } else if (fill.getOpacity() != 1) { + vmlElement.filled = true; + // Set opacity (number 0-1 is translated to percent) + var fillNode = this.createVmlElement('fill'); + fillNode.opacity = Math.round(fill.getOpacity() * 100) + '%'; + fillNode.color = fill.getColor(); + vmlElement.appendChild(fillNode); + } else { + vmlElement.filled = true; + vmlElement.fillcolor = fill.getColor(); + } + } else if (fill instanceof goog.graphics.LinearGradient) { + vmlElement.filled = true; + // Add a 'fill' element + var gradient = this.createVmlElement('fill'); + gradient.color = fill.getColor1(); + gradient.color2 = fill.getColor2(); + if (goog.isNumber(fill.getOpacity1())) { + gradient.opacity = fill.getOpacity1(); + } + if (goog.isNumber(fill.getOpacity2())) { + gradient.opacity2 = fill.getOpacity2(); + } + var angle = goog.math.angle(fill.getX1(), fill.getY1(), + fill.getX2(), fill.getY2()); + // Our angles start from 0 to the right, and grow clockwise. + // MSIE starts from 0 to top, and grows anti-clockwise. + angle = Math.round(goog.math.standardAngle(270 - angle)); + gradient.angle = angle; + gradient.type = 'gradient'; + vmlElement.appendChild(gradient); + } else { + vmlElement.filled = false; + } + this.updateGraphics_(); +}; + + +/** + * Sets the stroke for the given element. + * @param {goog.graphics.StrokeAndFillElement} element The element wrapper. + * @param {goog.graphics.Stroke?} stroke The stroke object. + * @override + */ +goog.graphics.VmlGraphics.prototype.setElementStroke = function(element, + stroke) { + var vmlElement = element.getElement(); + if (stroke) { + vmlElement.stroked = true; + + var width = stroke.getWidth(); + if (goog.isString(width) && width.indexOf('px') == -1) { + width = parseFloat(width); + } else { + width = width * this.getPixelScaleX(); + } + + var strokeElement = vmlElement.getElementsByTagName('stroke')[0]; + if (!strokeElement) { + strokeElement = strokeElement || this.createVmlElement('stroke'); + vmlElement.appendChild(strokeElement); + } + strokeElement.opacity = stroke.getOpacity(); + strokeElement.weight = width + 'px'; + strokeElement.color = stroke.getColor(); + } else { + vmlElement.stroked = false; + } + this.updateGraphics_(); +}; + + +/** + * Set the translation and rotation of an element. + * + * If a more general affine transform is needed than this provides + * (e.g. skew and scale) then use setElementAffineTransform. + * @param {number} x The x coordinate of the translation transform. + * @param {number} y The y coordinate of the translation transform. + * @param {number} angle The angle of the rotation transform. + * @param {number} centerX The horizontal center of the rotation transform. + * @param {number} centerY The vertical center of the rotation transform. + * @override + */ +goog.graphics.VmlGraphics.prototype.setElementTransform = function(element, x, + y, angle, centerX, centerY) { + var el = element.getElement(); + + el.style.left = goog.graphics.VmlGraphics.toPosPx(x); + el.style.top = goog.graphics.VmlGraphics.toPosPx(y); + if (angle || el.rotation) { + el.rotation = angle; + el.coordsize = goog.graphics.VmlGraphics.toSizeCoord(centerX * 2) + ' ' + + goog.graphics.VmlGraphics.toSizeCoord(centerY * 2); + } +}; + + +/** + * Set the transformation of an element. + * @param {!goog.graphics.Element} element The element wrapper. + * @param {!goog.graphics.AffineTransform} affineTransform The + * transformation applied to this element. + * @override + */ +goog.graphics.VmlGraphics.prototype.setElementAffineTransform = function( + element, affineTransform) { + var t = affineTransform; + var vmlElement = element.getElement(); + goog.graphics.VmlGraphics.removeSkew_(vmlElement); + var skewNode = this.createVmlElement('skew'); + skewNode.on = 'true'; + // Move the transform origin to 0px,0px of the graphics. + // In VML, 0,0 means the center of the element, -0.5,-0.5 left top conner of + // it. + skewNode.origin = + (-vmlElement.style.pixelLeft / vmlElement.style.pixelWidth - 0.5) + ',' + + (-vmlElement.style.pixelTop / vmlElement.style.pixelHeight - 0.5); + skewNode.offset = t.getTranslateX().toFixed(1) + 'px,' + + t.getTranslateY().toFixed(1) + 'px'; + skewNode.matrix = [t.getScaleX().toFixed(6), t.getShearX().toFixed(6), + t.getShearY().toFixed(6), t.getScaleY().toFixed(6), + 0, 0].join(','); + vmlElement.appendChild(skewNode); + this.updateGraphics_(); +}; + + +/** + * Removes the skew information from a dom element. + * @param {Element} element DOM element. + * @private + */ +goog.graphics.VmlGraphics.removeSkew_ = function(element) { + goog.array.forEach(element.childNodes, function(child) { + if (child.tagName == 'skew') { + element.removeChild(child); + } + }); +}; + + +/** + * Removes the fill information from a dom element. + * @param {Element} element DOM element. + * @private + */ +goog.graphics.VmlGraphics.removeFill_ = function(element) { + element.fillcolor = ''; + goog.array.forEach(element.childNodes, function(child) { + if (child.tagName == 'fill') { + element.removeChild(child); + } + }); +}; + + +/** + * Set top, left, width and height for an element. + * This function is internal for the VML supporting classes, and + * should not be used externally. + * + * @param {Element} element DOM element. + * @param {number} left Left ccordinate in pixels. + * @param {number} top Top ccordinate in pixels. + * @param {number} width Width in pixels. + * @param {number} height Height in pixels. + */ +goog.graphics.VmlGraphics.setPositionAndSize = function( + element, left, top, width, height) { + var style = element.style; + style.position = 'absolute'; + style.left = goog.graphics.VmlGraphics.toPosPx(left); + style.top = goog.graphics.VmlGraphics.toPosPx(top); + style.width = goog.graphics.VmlGraphics.toSizePx(width); + style.height = goog.graphics.VmlGraphics.toSizePx(height); + + if (element.tagName == 'shape') { + element.coordsize = goog.graphics.VmlGraphics.toSizeCoord(width) + ' ' + + goog.graphics.VmlGraphics.toSizeCoord(height); + } +}; + + +/** + * Creates an element spanning the surface. + * + * @param {string} type The type of element to create. + * @return {!Element} The created, positioned, and sized element. + * @private + */ +goog.graphics.VmlGraphics.prototype.createFullSizeElement_ = function(type) { + var element = this.createVmlElement(type); + var size = this.getCoordSize(); + goog.graphics.VmlGraphics.setPositionAndSize(element, 0, 0, size.width, + size.height); + return element; +}; + + +/** + * IE magic - if this "no-op" line is not here, the if statement below will + * fail intermittently. The eval is used to prevent the JsCompiler from + * stripping this piece of code, which it quite reasonably thinks is doing + * nothing. Put it in try-catch block to prevent "Unspecified Error" when + * this statement is executed in a defer JS in IE. + * More info here: + * http://www.mail-archive.com/users@openlayers.org/msg01838.html + */ +try { + eval('document.namespaces'); +} catch (ex) {} + + +/** + * Creates the DOM representation of the graphics area. + * @override + */ +goog.graphics.VmlGraphics.prototype.createDom = function() { + var doc = this.dom_.getDocument(); + + // Add the namespace. + if (!doc.namespaces[goog.graphics.VmlGraphics.VML_PREFIX_]) { + if (goog.graphics.VmlGraphics.IE8_MODE_) { + doc.namespaces.add(goog.graphics.VmlGraphics.VML_PREFIX_, + goog.graphics.VmlGraphics.VML_NS_, + goog.graphics.VmlGraphics.VML_IMPORT_); + } else { + doc.namespaces.add(goog.graphics.VmlGraphics.VML_PREFIX_, + goog.graphics.VmlGraphics.VML_NS_); + } + + // We assume that we only need to add the CSS if the namespace was not + // present + var ss = doc.createStyleSheet(); + ss.cssText = goog.graphics.VmlGraphics.VML_PREFIX_ + '\\:*' + + '{behavior:url(#default#VML)}'; + } + + // Outer a DIV with overflow hidden for clipping. + // All inner elements are absolutly positioned on-top of this div. + var pixelWidth = this.width; + var pixelHeight = this.height; + var divElement = this.dom_.createDom(goog.dom.TagName.DIV, { + 'style': 'overflow:hidden;position:relative;width:' + + goog.graphics.VmlGraphics.toCssSize(pixelWidth) + ';height:' + + goog.graphics.VmlGraphics.toCssSize(pixelHeight) + }); + + this.setElementInternal(divElement); + + var group = this.createVmlElement('group'); + var style = group.style; + + style.position = 'absolute'; + style.left = style.top = 0; + style.width = this.width; + style.height = this.height; + if (this.coordWidth) { + group.coordsize = + goog.graphics.VmlGraphics.toSizeCoord(this.coordWidth) + ' ' + + goog.graphics.VmlGraphics.toSizeCoord( + /** @type {number} */ (this.coordHeight)); + } else { + group.coordsize = goog.graphics.VmlGraphics.toSizeCoord(pixelWidth) + ' ' + + goog.graphics.VmlGraphics.toSizeCoord(pixelHeight); + } + + if (goog.isDef(this.coordLeft)) { + group.coordorigin = goog.graphics.VmlGraphics.toSizeCoord(this.coordLeft) + + ' ' + goog.graphics.VmlGraphics.toSizeCoord(this.coordTop); + } else { + group.coordorigin = '0 0'; + } + divElement.appendChild(group); + + this.canvasElement = new goog.graphics.VmlGroupElement(group, this); + + goog.events.listen(divElement, goog.events.EventType.RESIZE, goog.bind( + this.handleContainerResize_, this)); +}; + + +/** + * Changes the canvas element size to match the container element size. + * @private + */ +goog.graphics.VmlGraphics.prototype.handleContainerResize_ = function() { + var size = goog.style.getSize(this.getElement()); + var style = this.canvasElement.getElement().style; + + if (size.width) { + style.width = size.width + 'px'; + style.height = size.height + 'px'; + } else { + var current = this.getElement(); + while (current && current.currentStyle && + current.currentStyle.display != 'none') { + current = current.parentNode; + } + if (current && current.currentStyle) { + this.handler_.listen(current, 'propertychange', + this.handleContainerResize_); + } + } + + this.dispatchEvent(goog.events.EventType.RESIZE); +}; + + +/** + * Handle property changes on hidden ancestors. + * @param {goog.events.BrowserEvent} e The browser event. + * @private + */ +goog.graphics.VmlGraphics.prototype.handlePropertyChange_ = function(e) { + var prop = e.getBrowserEvent().propertyName; + if (prop == 'display' || prop == 'className') { + this.handler_.unlisten(/** @type {Element} */(e.target), + 'propertychange', this.handlePropertyChange_); + this.handleContainerResize_(); + } +}; + + +/** + * Changes the coordinate system position. + * @param {number} left The coordinate system left bound. + * @param {number} top The coordinate system top bound. + * @override + */ +goog.graphics.VmlGraphics.prototype.setCoordOrigin = function(left, top) { + this.coordLeft = left; + this.coordTop = top; + + this.canvasElement.getElement().coordorigin = + goog.graphics.VmlGraphics.toSizeCoord(this.coordLeft) + ' ' + + goog.graphics.VmlGraphics.toSizeCoord(this.coordTop); +}; + + +/** + * Changes the coordinate size. + * @param {number} coordWidth The coordinate width. + * @param {number} coordHeight The coordinate height. + * @override + */ +goog.graphics.VmlGraphics.prototype.setCoordSize = function(coordWidth, + coordHeight) { + goog.graphics.VmlGraphics.superClass_.setCoordSize.apply(this, arguments); + + this.canvasElement.getElement().coordsize = + goog.graphics.VmlGraphics.toSizeCoord(coordWidth) + ' ' + + goog.graphics.VmlGraphics.toSizeCoord(coordHeight); +}; + + +/** + * Change the size of the canvas. + * @param {number} pixelWidth The width in pixels. + * @param {number} pixelHeight The height in pixels. + * @override + */ +goog.graphics.VmlGraphics.prototype.setSize = function(pixelWidth, + pixelHeight) { + goog.style.setSize(this.getElement(), pixelWidth, pixelHeight); +}; + + +/** + * @return {!goog.math.Size} Returns the number of pixels spanned by the + * surface. + * @override + */ +goog.graphics.VmlGraphics.prototype.getPixelSize = function() { + var el = this.getElement(); + // The following relies on the fact that the size can never be 0. + return new goog.math.Size(el.style.pixelWidth || el.offsetWidth || 1, + el.style.pixelHeight || el.offsetHeight || 1); +}; + + +/** + * Remove all drawing elements from the graphics. + * @override + */ +goog.graphics.VmlGraphics.prototype.clear = function() { + this.canvasElement.clear(); +}; + + +/** + * Draw an ellipse. + * + * @param {number} cx Center X coordinate. + * @param {number} cy Center Y coordinate. + * @param {number} rx Radius length for the x-axis. + * @param {number} ry Radius length for the y-axis. + * @param {goog.graphics.Stroke?} stroke Stroke object describing the + * stroke. + * @param {goog.graphics.Fill?} fill Fill object describing the fill. + * @param {goog.graphics.GroupElement=} opt_group The group wrapper element + * to append to. If not specified, appends to the main canvas. + * + * @return {!goog.graphics.EllipseElement} The newly created element. + * @override + */ +goog.graphics.VmlGraphics.prototype.drawEllipse = function(cx, cy, rx, ry, + stroke, fill, opt_group) { + var element = this.createVmlElement('oval'); + goog.graphics.VmlGraphics.setPositionAndSize(element, cx - rx, cy - ry, + rx * 2, ry * 2); + var wrapper = new goog.graphics.VmlEllipseElement(element, this, + cx, cy, rx, ry, stroke, fill); + this.append_(wrapper, opt_group); + return wrapper; +}; + + +/** + * Draw a rectangle. + * + * @param {number} x X coordinate (left). + * @param {number} y Y coordinate (top). + * @param {number} width Width of rectangle. + * @param {number} height Height of rectangle. + * @param {goog.graphics.Stroke?} stroke Stroke object describing the + * stroke. + * @param {goog.graphics.Fill?} fill Fill object describing the fill. + * @param {goog.graphics.GroupElement=} opt_group The group wrapper element + * to append to. If not specified, appends to the main canvas. + * + * @return {!goog.graphics.RectElement} The newly created element. + * @override + */ +goog.graphics.VmlGraphics.prototype.drawRect = function(x, y, width, height, + stroke, fill, opt_group) { + var element = this.createVmlElement('rect'); + goog.graphics.VmlGraphics.setPositionAndSize(element, x, y, width, height); + var wrapper = new goog.graphics.VmlRectElement(element, this, stroke, fill); + this.append_(wrapper, opt_group); + return wrapper; +}; + + +/** + * Draw an image. + * + * @param {number} x X coordinate (left). + * @param {number} y Y coordinate (top). + * @param {number} width Width of image. + * @param {number} height Height of image. + * @param {string} src Source of the image. + * @param {goog.graphics.GroupElement=} opt_group The group wrapper element + * to append to. If not specified, appends to the main canvas. + * + * @return {!goog.graphics.ImageElement} The newly created element. + */ +goog.graphics.VmlGraphics.prototype.drawImage = function(x, y, width, height, + src, opt_group) { + var element = this.createVmlElement('image'); + goog.graphics.VmlGraphics.setPositionAndSize(element, x, y, width, height); + goog.graphics.VmlGraphics.setAttribute(element, 'src', src); + var wrapper = new goog.graphics.VmlImageElement(element, this); + this.append_(wrapper, opt_group); + return wrapper; +}; + + +/** + * Draw a text string vertically centered on a given line. + * + * @param {string} text The text to draw. + * @param {number} x1 X coordinate of start of line. + * @param {number} y1 Y coordinate of start of line. + * @param {number} x2 X coordinate of end of line. + * @param {number} y2 Y coordinate of end of line. + * @param {?string} align Horizontal alignment: left (default), center, right. + * @param {goog.graphics.Font} font Font describing the font properties. + * @param {goog.graphics.Stroke?} stroke Stroke object describing the stroke. + * @param {goog.graphics.Fill?} fill Fill object describing the fill. + * @param {goog.graphics.GroupElement=} opt_group The group wrapper element + * to append to. If not specified, appends to the main canvas. + * + * @return {!goog.graphics.TextElement} The newly created element. + * @override + */ +goog.graphics.VmlGraphics.prototype.drawTextOnLine = function( + text, x1, y1, x2, y2, align, font, stroke, fill, opt_group) { + var shape = this.createFullSizeElement_('shape'); + + var pathElement = this.createVmlElement('path'); + var path = 'M' + goog.graphics.VmlGraphics.toPosCoord(x1) + ',' + + goog.graphics.VmlGraphics.toPosCoord(y1) + 'L' + + goog.graphics.VmlGraphics.toPosCoord(x2) + ',' + + goog.graphics.VmlGraphics.toPosCoord(y2) + 'E'; + goog.graphics.VmlGraphics.setAttribute(pathElement, 'v', path); + goog.graphics.VmlGraphics.setAttribute(pathElement, 'textpathok', 'true'); + + var textPathElement = this.createVmlElement('textpath'); + textPathElement.setAttribute('on', 'true'); + var style = textPathElement.style; + style.fontSize = font.size * this.getPixelScaleX(); + style.fontFamily = font.family; + if (align != null) { + style['v-text-align'] = align; + } + if (font.bold) { + style.fontWeight = 'bold'; + } + if (font.italic) { + style.fontStyle = 'italic'; + } + goog.graphics.VmlGraphics.setAttribute(textPathElement, 'string', text); + + shape.appendChild(pathElement); + shape.appendChild(textPathElement); + var wrapper = new goog.graphics.VmlTextElement(shape, this, stroke, fill); + this.append_(wrapper, opt_group); + return wrapper; +}; + + +/** + * Draw a path. + * + * @param {!goog.graphics.Path} path The path object to draw. + * @param {goog.graphics.Stroke?} stroke Stroke object describing the stroke. + * @param {goog.graphics.Fill?} fill Fill object describing the fill. + * @param {goog.graphics.GroupElement=} opt_group The group wrapper element + * to append to. If not specified, appends to the main canvas. + * + * @return {!goog.graphics.PathElement} The newly created element. + * @override + */ +goog.graphics.VmlGraphics.prototype.drawPath = function(path, stroke, fill, + opt_group) { + var element = this.createFullSizeElement_('shape'); + goog.graphics.VmlGraphics.setAttribute(element, 'path', + goog.graphics.VmlGraphics.getVmlPath(path)); + + var wrapper = new goog.graphics.VmlPathElement(element, this, stroke, fill); + this.append_(wrapper, opt_group); + return wrapper; +}; + + +/** + * Returns a string representation of a logical path suitable for use in + * a VML element. + * + * @param {goog.graphics.Path} path The logical path. + * @return {string} The VML path representation. + * @suppress {deprecated} goog.graphics is deprecated. + */ +goog.graphics.VmlGraphics.getVmlPath = function(path) { + var list = []; + path.forEachSegment(function(segment, args) { + switch (segment) { + case goog.graphics.Path.Segment.MOVETO: + list.push('m'); + Array.prototype.push.apply(list, goog.array.map(args, + goog.graphics.VmlGraphics.toSizeCoord)); + break; + case goog.graphics.Path.Segment.LINETO: + list.push('l'); + Array.prototype.push.apply(list, goog.array.map(args, + goog.graphics.VmlGraphics.toSizeCoord)); + break; + case goog.graphics.Path.Segment.CURVETO: + list.push('c'); + Array.prototype.push.apply(list, goog.array.map(args, + goog.graphics.VmlGraphics.toSizeCoord)); + break; + case goog.graphics.Path.Segment.CLOSE: + list.push('x'); + break; + case goog.graphics.Path.Segment.ARCTO: + var toAngle = args[2] + args[3]; + var cx = goog.graphics.VmlGraphics.toSizeCoord( + args[4] - goog.math.angleDx(toAngle, args[0])); + var cy = goog.graphics.VmlGraphics.toSizeCoord( + args[5] - goog.math.angleDy(toAngle, args[1])); + var rx = goog.graphics.VmlGraphics.toSizeCoord(args[0]); + var ry = goog.graphics.VmlGraphics.toSizeCoord(args[1]); + // VML angles are in fd units (see http://www.w3.org/TR/NOTE-VML) and + // are positive counter-clockwise. + var fromAngle = Math.round(args[2] * -65536); + var extent = Math.round(args[3] * -65536); + list.push('ae', cx, cy, rx, ry, fromAngle, extent); + break; + } + }); + return list.join(' '); +}; + + +/** + * Create an empty group of drawing elements. + * + * @param {goog.graphics.GroupElement=} opt_group The group wrapper element + * to append to. If not specified, appends to the main canvas. + * + * @return {!goog.graphics.GroupElement} The newly created group. + * @override + */ +goog.graphics.VmlGraphics.prototype.createGroup = function(opt_group) { + var element = this.createFullSizeElement_('group'); + var parent = opt_group || this.canvasElement; + parent.getElement().appendChild(element); + return new goog.graphics.VmlGroupElement(element, this); +}; + + +/** + * Measure and return the width (in pixels) of a given text string. + * Text measurement is needed to make sure a text can fit in the allocated + * area. The way text length is measured is by writing it into a div that is + * after the visible area, measure the div width, and immediatly erase the + * written value. + * + * @param {string} text The text string to measure. + * @param {goog.graphics.Font} font The font object describing the font style. + * + * @return {number} The width in pixels of the text strings. + * @override + */ +goog.graphics.VmlGraphics.prototype.getTextWidth = function(text, font) { + // TODO(arv): Implement + return 0; +}; + + +/** @override */ +goog.graphics.VmlGraphics.prototype.enterDocument = function() { + goog.graphics.VmlGraphics.superClass_.enterDocument.call(this); + this.handleContainerResize_(); + this.updateGraphics_(); +}; + + +/** + * Disposes of the component by removing event handlers, detacing DOM nodes from + * the document body, and removing references to them. + * @override + * @protected + */ +goog.graphics.VmlGraphics.prototype.disposeInternal = function() { + this.canvasElement = null; + goog.graphics.VmlGraphics.superClass_.disposeInternal.call(this); +}; http://git-wip-us.apache.org/repos/asf/flex-falcon/blob/e2cad6e6/externs/GCL/externs/goog/history/event.js ---------------------------------------------------------------------- diff --git a/externs/GCL/externs/goog/history/event.js b/externs/GCL/externs/goog/history/event.js new file mode 100644 index 0000000..19250df --- /dev/null +++ b/externs/GCL/externs/goog/history/event.js @@ -0,0 +1,55 @@ +// 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 The event object dispatched when the history changes. + * + */ + + +goog.provide('goog.history.Event'); + +goog.require('goog.events.Event'); +goog.require('goog.history.EventType'); + + + +/** + * Event object dispatched after the history state has changed. + * @param {string} token The string identifying the new history state. + * @param {boolean} isNavigation True if the event was triggered by a browser + * action, such as forward or back, clicking on a link, editing the URL, or + * calling {@code window.history.(go|back|forward)}. + * False if the token has been changed by a {@code setToken} or + * {@code replaceToken} call. + * @constructor + * @extends {goog.events.Event} + * @final + */ +goog.history.Event = function(token, isNavigation) { + goog.events.Event.call(this, goog.history.EventType.NAVIGATE); + + /** + * The current history state. + * @type {string} + */ + this.token = token; + + /** + * Whether the event was triggered by browser navigation. + * @type {boolean} + */ + this.isNavigation = isNavigation; +}; +goog.inherits(goog.history.Event, goog.events.Event); http://git-wip-us.apache.org/repos/asf/flex-falcon/blob/e2cad6e6/externs/GCL/externs/goog/history/eventtype.js ---------------------------------------------------------------------- diff --git a/externs/GCL/externs/goog/history/eventtype.js b/externs/GCL/externs/goog/history/eventtype.js new file mode 100644 index 0000000..4268df3 --- /dev/null +++ b/externs/GCL/externs/goog/history/eventtype.js @@ -0,0 +1,30 @@ +// 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 Event types for goog.history. + * + */ + + +goog.provide('goog.history.EventType'); + + +/** + * Event types for goog.history. + * @enum {string} + */ +goog.history.EventType = { + NAVIGATE: 'navigate' +}; http://git-wip-us.apache.org/repos/asf/flex-falcon/blob/e2cad6e6/externs/GCL/externs/goog/history/history.js ---------------------------------------------------------------------- diff --git a/externs/GCL/externs/goog/history/history.js b/externs/GCL/externs/goog/history/history.js new file mode 100644 index 0000000..af82ba0 --- /dev/null +++ b/externs/GCL/externs/goog/history/history.js @@ -0,0 +1,1005 @@ +// Copyright 2007 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 Browser history stack management class. + * + * The goog.History object allows a page to create history state without leaving + * the current document. This allows users to, for example, hit the browser's + * back button without leaving the current page. + * + * The history object can be instantiated in one of two modes. In user visible + * mode, the current history state is shown in the browser address bar as a + * document location fragment (the portion of the URL after the '#'). These + * addresses can be bookmarked, copied and pasted into another browser, and + * modified directly by the user like any other URL. + * + * If the history object is created in invisible mode, the user can still + * affect the state using the browser forward and back buttons, but the current + * state is not displayed in the browser address bar. These states are not + * bookmarkable or editable. + * + * It is possible to use both types of history object on the same page, but not + * currently recommended due to browser deficiencies. + * + * Tested to work in: + *
    + *
  • Firefox 1.0-4.0 + *
  • Internet Explorer 5.5-9.0 + *
  • Opera 9+ + *
  • Safari 4+ + *
+ * + * @author brenneman@google.com (Shawn Brenneman) + * @see ../demos/history1.html + * @see ../demos/history2.html + */ + +/* Some browser specific implementation notes: + * + * Firefox (through version 2.0.0.1): + * + * Ideally, navigating inside the hidden iframe could be done using + * about:blank#state instead of a real page on the server. Setting the hash on + * about:blank creates history entries, but the hash is not recorded and is lost + * when the user hits the back button. This is true in Opera as well. A blank + * HTML page must be provided for invisible states to be recorded in the iframe + * hash. + * + * After leaving the page with the History object and returning to it (by + * hitting the back button from another site), the last state of the iframe is + * overwritten. The most recent state is saved in a hidden input field so the + * previous state can be restored. + * + * Firefox does not store the previous value of dynamically generated input + * elements. To save the state, the hidden element must be in the HTML document, + * either in the original source or added with document.write. If a reference + * to the input element is not provided as a constructor argument, then the + * history object creates one using document.write, in which case the history + * object must be created from a script in the body element of the page. + * + * Manually editing the address field to a different hash link prevents further + * updates to the address bar. The page continues to work as normal, but the + * address shown will be incorrect until the page is reloaded. + * + * NOTE(user): It should be noted that Firefox will URL encode any non-regular + * ascii character, along with |space|, ", <, and >, when added to the fragment. + * If you expect these characters in your tokens you should consider that + * setToken('') would result in the history fragment "%3Cb%3E", and + * "espére" would show "esp%E8re". (IE allows unicode characters in the + * fragment) + * + * TODO(user): Should we encapsulate this escaping into the API for visible + * history and encode all characters that aren't supported by Firefox? It also + * needs to be optional so apps can elect to handle the escaping themselves. + * + * + * Internet Explorer (through version 7.0): + * + * IE does not modify the history stack when the document fragment is changed. + * We create history entries instead by using document.open and document.write + * into a hidden iframe. + * + * IE destroys the history stack when navigating from /foo.html#someFragment to + * /foo.html. The workaround is to always append the # to the URL. This is + * somewhat unfortunate when loading the page without any # specified, because + * a second "click" sound will play on load as the fragment is automatically + * appended. If the hash is always present, this can be avoided. + * + * Manually editing the hash in the address bar in IE6 and then hitting the back + * button can replace the page with a blank page. This is a Bad User Experience, + * but probably not preventable. + * + * IE also has a bug when the page is loaded via a server redirect, setting + * a new hash value on the window location will force a page reload. This will + * happen the first time setToken is called with a new token. The only known + * workaround is to force a client reload early, for example by setting + * window.location.hash = window.location.hash, which will otherwise be a no-op. + * + * Internet Explorer 8.0, Webkit 532.1 and Gecko 1.9.2: + * + * IE8 has introduced the support to the HTML5 onhashchange event, which means + * we don't have to do any polling to detect fragment changes. Chrome and + * Firefox have added it on their newer builds, wekbit 532.1 and gecko 1.9.2. + * http://www.w3.org/TR/html5/history.html + * NOTE(goto): it is important to note that the document needs to have the + * tag to enable the IE8 HTML5 mode. If the tag is not present, + * IE8 will enter IE7 compatibility mode (which can also be enabled manually). + * + * Opera (through version 9.02): + * + * Navigating through pages at a rate faster than some threshhold causes Opera + * to cancel all outstanding timeouts and intervals, including the location + * polling loop. Since this condition cannot be detected, common input events + * are captured to cause the loop to restart. + * + * location.replace is adding a history entry inside setHash_, despite + * documentation that suggests it should not. + * + * + * Safari (through version 2.0.4): + * + * After hitting the back button, the location.hash property is no longer + * readable from JavaScript. This is fixed in later WebKit builds, but not in + * currently shipping Safari. For now, the only recourse is to disable history + * states in Safari. Pages are still navigable via the History object, but the + * back button cannot restore previous states. + * + * Safari sets history states on navigation to a hashlink, but doesn't allow + * polling of the hash, so following actual anchor links in the page will create + * useless history entries. Using location.replace does not seem to prevent + * this. Not a terribly good user experience, but fixed in later Webkits. + * + * + * WebKit (nightly version 420+): + * + * This almost works. Returning to a page with an invisible history object does + * not restore the old state, however, and there is no pageshow event that fires + * in this browser. Holding off on finding a solution for now. + * + * + * HTML5 capable browsers (Firefox 4, Chrome, Safari 5) + * + * No known issues. The goog.history.Html5History class provides a simpler + * implementation more suitable for recent browsers. These implementations + * should be merged so the history class automatically invokes the correct + * implementation. + */ + + +goog.provide('goog.History'); +goog.provide('goog.History.Event'); +goog.provide('goog.History.EventType'); + +goog.require('goog.Timer'); +goog.require('goog.asserts'); +goog.require('goog.dom'); +goog.require('goog.dom.InputType'); +goog.require('goog.dom.safe'); +/** @suppress {extraRequire} */ +goog.require('goog.events.Event'); +goog.require('goog.events.EventHandler'); +goog.require('goog.events.EventTarget'); +goog.require('goog.events.EventType'); +goog.require('goog.history.Event'); +goog.require('goog.history.EventType'); +goog.require('goog.html.SafeHtml'); +goog.require('goog.html.TrustedResourceUrl'); +goog.require('goog.html.legacyconversions'); +goog.require('goog.labs.userAgent.device'); +goog.require('goog.memoize'); +goog.require('goog.string'); +goog.require('goog.string.Const'); +goog.require('goog.userAgent'); + + + +/** + * A history management object. Can be instantiated in user-visible mode (uses + * the address fragment to manage state) or in hidden mode. This object should + * be created from a script in the document body before the document has + * finished loading. + * + * To store the hidden states in browsers other than IE, a hidden iframe is + * used. It must point to a valid html page on the same domain (which can and + * probably should be blank.) + * + * Sample instantiation and usage: + * + *
+ * // Instantiate history to use the address bar for state.
+ * var h = new goog.History();
+ * goog.events.listen(h, goog.history.EventType.NAVIGATE, navCallback);
+ * h.setEnabled(true);
+ *
+ * // Any changes to the location hash will call the following function.
+ * function navCallback(e) {
+ *   alert('Navigated to state "' + e.token + '"');
+ * }
+ *
+ * // The history token can also be set from code directly.
+ * h.setToken('foo');
+ * 
+ * + * @param {boolean=} opt_invisible True to use hidden history states instead of + * the user-visible location hash. + * @param {!goog.html.TrustedResourceUrl|string=} opt_blankPageUrl A URL to a + * blank page on the same server. Required if opt_invisible is true. If + * possible pass a TrustedResourceUrl; string is supported for + * backwards-compatibility only and uses goog.html.legacyconversions. + * This URL is also used as the src for the iframe used to track history + * state in IE (if not specified the iframe is not given a src attribute). + * Access is Denied error may occur in IE7 if the window's URL's scheme + * is https, and this URL is not specified. + * @param {HTMLInputElement=} opt_input The hidden input element to be used to + * store the history token. If not provided, a hidden input element will + * be created using document.write. + * @param {HTMLIFrameElement=} opt_iframe The hidden iframe that will be used by + * IE for pushing history state changes, or by all browsers if opt_invisible + * is true. If not provided, a hidden iframe element will be created using + * document.write. + * @constructor + * @extends {goog.events.EventTarget} + */ +goog.History = function(opt_invisible, opt_blankPageUrl, opt_input, + opt_iframe) { + goog.events.EventTarget.call(this); + + if (opt_invisible && !opt_blankPageUrl) { + throw Error('Can\'t use invisible history without providing a blank page.'); + } + + var input; + if (opt_input) { + input = opt_input; + } else { + var inputId = 'history_state' + goog.History.historyCount_; + var inputHtml = goog.html.SafeHtml.create('input', + {type: goog.dom.InputType.TEXT, name: inputId, id: inputId, + style: goog.string.Const.from('display:none')}); + goog.dom.safe.documentWrite(document, inputHtml); + input = goog.dom.getElement(inputId); + } + + /** + * An input element that stores the current iframe state. Used to restore + * the state when returning to the page on non-IE browsers. + * @type {HTMLInputElement} + * @private + */ + this.hiddenInput_ = /** @type {HTMLInputElement} */ (input); + + /** + * The window whose location contains the history token fragment. This is + * the window that contains the hidden input. It's typically the top window. + * It is not necessarily the same window that the js code is loaded in. + * @type {Window} + * @private + */ + this.window_ = opt_input ? + goog.dom.getWindow(goog.dom.getOwnerDocument(opt_input)) : window; + + var iframeSrc; + if (goog.isString(opt_blankPageUrl)) { + iframeSrc = goog.html.legacyconversions.trustedResourceUrlFromString( + opt_blankPageUrl); + } else { + iframeSrc = opt_blankPageUrl; + } + + /** + * The base URL for the hidden iframe. Must refer to a document in the + * same domain as the main page. + * @type {!goog.html.TrustedResourceUrl|undefined} + * @private + */ + this.iframeSrc_ = iframeSrc; + + if (goog.userAgent.IE && !opt_blankPageUrl) { + if (window.location.protocol == 'https') { + this.iframeSrc_ = goog.html.TrustedResourceUrl.fromConstant( + goog.string.Const.from('https:///')); + } else { + this.iframeSrc_ = goog.html.TrustedResourceUrl.fromConstant( + goog.string.Const.from('javascript:""')); + } + } + + /** + * A timer for polling the current history state for changes. + * @type {goog.Timer} + * @private + */ + this.timer_ = new goog.Timer(goog.History.PollingType.NORMAL); + this.registerDisposable(this.timer_); + + /** + * True if the state tokens are displayed in the address bar, false for hidden + * history states. + * @type {boolean} + * @private + */ + this.userVisible_ = !opt_invisible; + + /** + * An object to keep track of the history event listeners. + * @type {goog.events.EventHandler} + * @private + */ + this.eventHandler_ = new goog.events.EventHandler(this); + + if (opt_invisible || goog.History.LEGACY_IE) { + var iframe; + if (opt_iframe) { + iframe = opt_iframe; + } else { + var iframeId = 'history_iframe' + goog.History.historyCount_; + // Using a "sandbox" attribute on the iframe might be possible, but + // this HTML didn't initially have it and when it was refactored + // to SafeHtml it was kept without it. + var iframeHtml = goog.html.SafeHtml.createIframe(this.iframeSrc_, null, + {id: iframeId, style: goog.string.Const.from('display:none'), + sandbox: undefined}); + goog.dom.safe.documentWrite(document, iframeHtml); + iframe = goog.dom.getElement(iframeId); + } + + /** + * Internet Explorer uses a hidden iframe for all history changes. Other + * browsers use the iframe only for pushing invisible states. + * @type {HTMLIFrameElement} + * @private + */ + this.iframe_ = /** @type {HTMLIFrameElement} */ (iframe); + + /** + * Whether the hidden iframe has had a document written to it yet in this + * session. + * @type {boolean} + * @private + */ + this.unsetIframe_ = true; + } + + if (goog.History.LEGACY_IE) { + // IE relies on the hidden input to restore the history state from previous + // sessions, but input values are only restored after window.onload. Set up + // a callback to poll the value after the onload event. + this.eventHandler_.listen(this.window_, + goog.events.EventType.LOAD, + this.onDocumentLoaded); + + /** + * IE-only variable for determining if the document has loaded. + * @type {boolean} + * @protected + */ + this.documentLoaded = false; + + /** + * IE-only variable for storing whether the history object should be enabled + * once the document finishes loading. + * @type {boolean} + * @private + */ + this.shouldEnable_ = false; + } + + // Set the initial history state. + if (this.userVisible_) { + this.setHash_(this.getToken(), true); + } else { + this.setIframeToken_(this.hiddenInput_.value); + } + + goog.History.historyCount_++; +}; +goog.inherits(goog.History, goog.events.EventTarget); + + +/** + * Status of when the object is active and dispatching events. + * @type {boolean} + * @private + */ +goog.History.prototype.enabled_ = false; + + +/** + * Whether the object is performing polling with longer intervals. This can + * occur for instance when setting the location of the iframe when in invisible + * mode and the server that is hosting the blank html page is down. In FF, this + * will cause the location of the iframe to no longer be accessible, with + * permision denied exceptions being thrown on every access of the history + * token. When this occurs, the polling interval is elongated. This causes + * exceptions to be thrown at a lesser rate while allowing for the history + * object to resurrect itself when the html page becomes accessible. + * @type {boolean} + * @private + */ +goog.History.prototype.longerPolling_ = false; + + +/** + * The last token set by the history object, used to poll for changes. + * @type {?string} + * @private + */ +goog.History.prototype.lastToken_ = null; + + +/** + * Whether the browser supports HTML5 history management's onhashchange event. + * {@link http://www.w3.org/TR/html5/history.html}. IE 9 in compatibility mode + * indicates that onhashchange is in window, but testing reveals the event + * isn't actually fired. + * @return {boolean} Whether onhashchange is supported. + */ +goog.History.isOnHashChangeSupported = goog.memoize(function() { + return goog.userAgent.IE ? + goog.userAgent.isDocumentModeOrHigher(8) : + 'onhashchange' in goog.global; +}); + + +/** + * Whether the current browser is Internet Explorer prior to version 8. Many IE + * specific workarounds developed before version 8 are unnecessary in more + * current versions. + * @type {boolean} + */ +goog.History.LEGACY_IE = goog.userAgent.IE && + !goog.userAgent.isDocumentModeOrHigher(8); + + +/** + * Whether the browser always requires the hash to be present. Internet Explorer + * before version 8 will reload the HTML page if the hash is omitted. + * @type {boolean} + */ +goog.History.HASH_ALWAYS_REQUIRED = goog.History.LEGACY_IE; + + +/** + * If not null, polling in the user invisible mode will be disabled until this + * token is seen. This is used to prevent a race condition where the iframe + * hangs temporarily while the location is changed. + * @type {?string} + * @private + */ +goog.History.prototype.lockedToken_ = null; + + +/** @override */ +goog.History.prototype.disposeInternal = function() { + goog.History.superClass_.disposeInternal.call(this); + this.eventHandler_.dispose(); + this.setEnabled(false); +}; + + +/** + * Starts or stops the History polling loop. When enabled, the History object + * will immediately fire an event for the current location. The caller can set + * up event listeners between the call to the constructor and the call to + * setEnabled. + * + * On IE, actual startup may be delayed until the iframe and hidden input + * element have been loaded and can be polled. This behavior is transparent to + * the caller. + * + * @param {boolean} enable Whether to enable the history polling loop. + */ +goog.History.prototype.setEnabled = function(enable) { + + if (enable == this.enabled_) { + return; + } + + if (goog.History.LEGACY_IE && !this.documentLoaded) { + // Wait until the document has actually loaded before enabling the + // object or any saved state from a previous session will be lost. + this.shouldEnable_ = enable; + return; + } + + if (enable) { + if (goog.userAgent.OPERA) { + // Capture events for common user input so we can restart the timer in + // Opera if it fails. Yes, this is distasteful. See operaDefibrillator_. + this.eventHandler_.listen(this.window_.document, + goog.History.INPUT_EVENTS_, + this.operaDefibrillator_); + } else if (goog.userAgent.GECKO) { + // Firefox will not restore the correct state after navigating away from + // and then back to the page with the history object. This can be fixed + // by restarting the history object on the pageshow event. + this.eventHandler_.listen(this.window_, 'pageshow', this.onShow_); + } + + // TODO(user): make HTML5 and invisible history work by listening to the + // iframe # changes instead of the window. + if (goog.History.isOnHashChangeSupported() && + this.userVisible_) { + this.eventHandler_.listen( + this.window_, goog.events.EventType.HASHCHANGE, this.onHashChange_); + this.enabled_ = true; + this.dispatchEvent(new goog.history.Event(this.getToken(), false)); + } else if (!(goog.userAgent.IE && !goog.labs.userAgent.device.isMobile()) || + this.documentLoaded) { + // Start dispatching history events if all necessary loading has + // completed (always true for browsers other than IE.) + this.eventHandler_.listen(this.timer_, goog.Timer.TICK, + goog.bind(this.check_, this, true)); + + this.enabled_ = true; + + // Initialize last token at startup except on IE < 8, where the last token + // must only be set in conjunction with IFRAME updates, or the IFRAME will + // start out of sync and remove any pre-existing URI fragment. + if (!goog.History.LEGACY_IE) { + this.lastToken_ = this.getToken(); + this.dispatchEvent(new goog.history.Event(this.getToken(), false)); + } + + this.timer_.start(); + } + + } else { + this.enabled_ = false; + this.eventHandler_.removeAll(); + this.timer_.stop(); + } +}; + + +/** + * Callback for the window onload event in IE. This is necessary to read the + * value of the hidden input after restoring a history session. The value of + * input elements is not viewable until after window onload for some reason (the + * iframe state is similarly unavailable during the loading phase.) If + * setEnabled is called before the iframe has completed loading, the history + * object will actually be enabled at this point. + * @protected + */ +goog.History.prototype.onDocumentLoaded = function() { + this.documentLoaded = true; + + if (this.hiddenInput_.value) { + // Any saved value in the hidden input can only be read after the document + // has been loaded due to an IE limitation. Restore the previous state if + // it has been set. + this.setIframeToken_(this.hiddenInput_.value, true); + } + + this.setEnabled(this.shouldEnable_); +}; + + +/** + * Handler for the Gecko pageshow event. Restarts the history object so that the + * correct state can be restored in the hash or iframe. + * @param {goog.events.BrowserEvent} e The browser event. + * @private + */ +goog.History.prototype.onShow_ = function(e) { + // NOTE(user): persisted is a property passed in the pageshow event that + // indicates whether the page is being persisted from the cache or is being + // loaded for the first time. + if (e.getBrowserEvent()['persisted']) { + this.setEnabled(false); + this.setEnabled(true); + } +}; + + +/** + * Handles HTML5 onhashchange events on browsers where it is supported. + * This is very similar to {@link #check_}, except that it is not executed + * continuously. It is only used when + * {@code goog.History.isOnHashChangeSupported()} is true. + * @param {goog.events.BrowserEvent} e The browser event. + * @private + */ +goog.History.prototype.onHashChange_ = function(e) { + var hash = this.getLocationFragment_(this.window_); + if (hash != this.lastToken_) { + this.update_(hash, true); + } +}; + + +/** + * @return {string} The current token. + */ +goog.History.prototype.getToken = function() { + if (this.lockedToken_ != null) { + return this.lockedToken_; + } else if (this.userVisible_) { + return this.getLocationFragment_(this.window_); + } else { + return this.getIframeToken_() || ''; + } +}; + + +/** + * Sets the history state. When user visible states are used, the URL fragment + * will be set to the provided token. Sometimes it is necessary to set the + * history token before the document title has changed, in this case IE's + * history drop down can be out of sync with the token. To get around this + * problem, the app can pass in a title to use with the hidden iframe. + * @param {string} token The history state identifier. + * @param {string=} opt_title Optional title used when setting the hidden iframe + * title in IE. + */ +goog.History.prototype.setToken = function(token, opt_title) { + this.setHistoryState_(token, false, opt_title); +}; + + +/** + * Replaces the current history state without affecting the rest of the history + * stack. + * @param {string} token The history state identifier. + * @param {string=} opt_title Optional title used when setting the hidden iframe + * title in IE. + */ +goog.History.prototype.replaceToken = function(token, opt_title) { + this.setHistoryState_(token, true, opt_title); +}; + + +/** + * Gets the location fragment for the current URL. We don't use location.hash + * directly as the browser helpfully urlDecodes the string for us which can + * corrupt the tokens. For example, if we want to store: label/%2Froot it would + * be returned as label//root. + * @param {Window} win The window object to use. + * @return {string} The fragment. + * @private + */ +goog.History.prototype.getLocationFragment_ = function(win) { + var href = win.location.href; + var index = href.indexOf('#'); + return index < 0 ? '' : href.substring(index + 1); +}; + + +/** + * Sets the history state. When user visible states are used, the URL fragment + * will be set to the provided token. Setting opt_replace to true will cause the + * navigation to occur, but will replace the current history entry without + * affecting the length of the stack. + * + * @param {string} token The history state identifier. + * @param {boolean} replace Set to replace the current history entry instead of + * appending a new history state. + * @param {string=} opt_title Optional title used when setting the hidden iframe + * title in IE. + * @private + */ +goog.History.prototype.setHistoryState_ = function(token, replace, opt_title) { + if (this.getToken() != token) { + if (this.userVisible_) { + this.setHash_(token, replace); + + if (!goog.History.isOnHashChangeSupported()) { + if (goog.userAgent.IE && !goog.labs.userAgent.device.isMobile()) { + // IE must save state using the iframe. + this.setIframeToken_(token, replace, opt_title); + } + } + + // This condition needs to be called even if + // goog.History.isOnHashChangeSupported() is true so the NAVIGATE event + // fires sychronously. + if (this.enabled_) { + this.check_(false); + } + } else { + // Fire the event immediately so that setting history is synchronous, but + // set a suspendToken so that polling doesn't trigger a 'back'. + this.setIframeToken_(token, replace); + this.lockedToken_ = this.lastToken_ = this.hiddenInput_.value = token; + this.dispatchEvent(new goog.history.Event(token, false)); + } + } +}; + + +/** + * Sets or replaces the URL fragment. The token does not need to be URL encoded + * according to the URL specification, though certain characters (like newline) + * are automatically stripped. + * + * If opt_replace is not set, non-IE browsers will append a new entry to the + * history list. Setting the hash does not affect the history stack in IE + * (unless there is a pre-existing named anchor for that hash.) + * + * Older versions of Webkit cannot query the location hash, but it still can be + * set. If we detect one of these versions, always replace instead of creating + * new history entries. + * + * window.location.replace replaces the current state from the history stack. + * http://www.whatwg.org/specs/web-apps/current-work/#dom-location-replace + * http://www.whatwg.org/specs/web-apps/current-work/#replacement-enabled + * + * @param {string} token The new string to set. + * @param {boolean=} opt_replace Set to true to replace the current token + * without appending a history entry. + * @private + */ +goog.History.prototype.setHash_ = function(token, opt_replace) { + // If the page uses a BASE element, setting location.hash directly will + // navigate away from the current document. Also, the original URL path may + // possibly change from HTML5 history pushState. To account for these, the + // full path is always specified. + var loc = this.window_.location; + var url = loc.href.split('#')[0]; + + // If a hash has already been set, then removing it programmatically will + // reload the page. Once there is a hash, we won't remove it. + var hasHash = goog.string.contains(loc.href, '#'); + + if (goog.History.HASH_ALWAYS_REQUIRED || hasHash || token) { + url += '#' + token; + } + + if (url != loc.href) { + if (opt_replace) { + loc.replace(url); + } else { + loc.href = url; + } + } +}; + + +/** + * Sets the hidden iframe state. On IE, this is accomplished by writing a new + * document into the iframe. In Firefox, the iframe's URL fragment stores the + * state instead. + * + * Older versions of webkit cannot set the iframe, so ignore those browsers. + * + * @param {string} token The new string to set. + * @param {boolean=} opt_replace Set to true to replace the current iframe state + * without appending a new history entry. + * @param {string=} opt_title Optional title used when setting the hidden iframe + * title in IE. + * @private + */ +goog.History.prototype.setIframeToken_ = function(token, + opt_replace, + opt_title) { + if (this.unsetIframe_ || token != this.getIframeToken_()) { + + this.unsetIframe_ = false; + token = goog.string.urlEncode(token); + + if (goog.userAgent.IE) { + // Caching the iframe document results in document permission errors after + // leaving the page and returning. Access it anew each time instead. + var doc = goog.dom.getFrameContentDocument(this.iframe_); + + doc.open('text/html', opt_replace ? 'replace' : undefined); + var iframeSourceHtml = goog.html.SafeHtml.concat( + goog.html.SafeHtml.create('title', {}, + (opt_title || this.window_.document.title)), + goog.html.SafeHtml.create('body', {}, token)); + goog.dom.safe.documentWrite(doc, iframeSourceHtml); + doc.close(); + } else { + goog.asserts.assertInstanceof( + this.iframeSrc_, goog.html.TrustedResourceUrl, + 'this.iframeSrc_ must be set on calls to setIframeToken_'); + var url = goog.html.TrustedResourceUrl.unwrap( + /** @type {!goog.html.TrustedResourceUrl} */ (this.iframeSrc_)) + + '#' + token; + + // In Safari, it is possible for the contentWindow of the iframe to not + // be present when the page is loading after a reload. + var contentWindow = this.iframe_.contentWindow; + if (contentWindow) { + if (opt_replace) { + contentWindow.location.replace(url); + } else { + contentWindow.location.href = url; + } + } + } + } +}; + + +/** + * Return the current state string from the hidden iframe. On internet explorer, + * this is stored as a string in the document body. Other browsers use the + * location hash of the hidden iframe. + * + * Older versions of webkit cannot access the iframe location, so always return + * null in that case. + * + * @return {?string} The state token saved in the iframe (possibly null if the + * iframe has never loaded.). + * @private + */ +goog.History.prototype.getIframeToken_ = function() { + if (goog.userAgent.IE) { + var doc = goog.dom.getFrameContentDocument(this.iframe_); + return doc.body ? goog.string.urlDecode(doc.body.innerHTML) : null; + } else { + // In Safari, it is possible for the contentWindow of the iframe to not + // be present when the page is loading after a reload. + var contentWindow = this.iframe_.contentWindow; + if (contentWindow) { + var hash; + /** @preserveTry */ + try { + // Iframe tokens are urlEncoded + hash = goog.string.urlDecode(this.getLocationFragment_(contentWindow)); + } catch (e) { + // An exception will be thrown if the location of the iframe can not be + // accessed (permission denied). This can occur in FF if the the server + // that is hosting the blank html page goes down and then a new history + // token is set. The iframe will navigate to an error page, and the + // location of the iframe can no longer be accessed. Due to the polling, + // this will cause constant exceptions to be thrown. In this case, + // we enable longer polling. We do not have to attempt to reset the + // iframe token because (a) we already fired the NAVIGATE event when + // setting the token, (b) we can rely on the locked token for current + // state, and (c) the token is still in the history and + // accesible on forward/back. + if (!this.longerPolling_) { + this.setLongerPolling_(true); + } + + return null; + } + + // There was no exception when getting the hash so turn off longer polling + // if it is on. + if (this.longerPolling_) { + this.setLongerPolling_(false); + } + + return hash || null; + } else { + return null; + } + } +}; + + +/** + * Checks the state of the document fragment and the iframe title to detect + * navigation changes. If {@code goog.HistoryisOnHashChangeSupported()} is + * {@code false}, then this runs approximately twenty times per second. + * @param {boolean} isNavigation True if the event was initiated by a browser + * action, false if it was caused by a setToken call. See + * {@link goog.history.Event}. + * @private + */ +goog.History.prototype.check_ = function(isNavigation) { + if (this.userVisible_) { + var hash = this.getLocationFragment_(this.window_); + if (hash != this.lastToken_) { + this.update_(hash, isNavigation); + } + } + + // Old IE uses the iframe for both visible and non-visible versions. + if (!this.userVisible_ || goog.History.LEGACY_IE) { + var token = this.getIframeToken_() || ''; + if (this.lockedToken_ == null || token == this.lockedToken_) { + this.lockedToken_ = null; + if (token != this.lastToken_) { + this.update_(token, isNavigation); + } + } + } +}; + + +/** + * Updates the current history state with a given token. Called after a change + * to the location or the iframe state is detected by poll_. + * + * @param {string} token The new history state. + * @param {boolean} isNavigation True if the event was initiated by a browser + * action, false if it was caused by a setToken call. See + * {@link goog.history.Event}. + * @private + */ +goog.History.prototype.update_ = function(token, isNavigation) { + this.lastToken_ = this.hiddenInput_.value = token; + + if (this.userVisible_) { + if (goog.History.LEGACY_IE) { + this.setIframeToken_(token); + } + + this.setHash_(token); + } else { + this.setIframeToken_(token); + } + + this.dispatchEvent(new goog.history.Event(this.getToken(), isNavigation)); +}; + + +/** + * Sets if the history oject should use longer intervals when polling. + * + * @param {boolean} longerPolling Whether to enable longer polling. + * @private + */ +goog.History.prototype.setLongerPolling_ = function(longerPolling) { + if (this.longerPolling_ != longerPolling) { + this.timer_.setInterval(longerPolling ? + goog.History.PollingType.LONG : goog.History.PollingType.NORMAL); + } + this.longerPolling_ = longerPolling; +}; + + +/** + * Opera cancels all outstanding timeouts and intervals after any rapid + * succession of navigation events, including the interval used to detect + * navigation events. This function restarts the interval so that navigation can + * continue. Ideally, only events which would be likely to cause a navigation + * change (mousedown and keydown) would be bound to this function. Since Opera + * seems to ignore keydown events while the alt key is pressed (such as + * alt-left or right arrow), this function is also bound to the much more + * frequent mousemove event. This way, when the update loop freezes, it will + * unstick itself as the user wiggles the mouse in frustration. + * @private + */ +goog.History.prototype.operaDefibrillator_ = function() { + this.timer_.stop(); + this.timer_.start(); +}; + + +/** + * List of user input event types registered in Opera to restart the history + * timer (@see goog.History#operaDefibrillator_). + * @type {Array} + * @private + */ +goog.History.INPUT_EVENTS_ = [ + goog.events.EventType.MOUSEDOWN, + goog.events.EventType.KEYDOWN, + goog.events.EventType.MOUSEMOVE +]; + + +/** + * Counter for the number of goog.History objects that have been instantiated. + * Used to create unique IDs. + * @type {number} + * @private + */ +goog.History.historyCount_ = 0; + + +/** + * Types of polling. The values are in ms of the polling interval. + * @enum {number} + */ +goog.History.PollingType = { + NORMAL: 150, + LONG: 10000 +}; + + +/** + * Constant for the history change event type. + * @enum {string} + * @deprecated Use goog.history.EventType. + */ +goog.History.EventType = goog.history.EventType; + + + +/** + * Constant for the history change event type. + * @param {string} token The string identifying the new history state. + * @extends {goog.events.Event} + * @constructor + * @deprecated Use goog.history.Event. + * @final + */ +goog.History.Event = goog.history.Event;