UNPKG

typescript-closure-tools

Version:

Command-line tools to convert closure-style JSDoc annotations to typescript, and to convert typescript sources to closure externs files

473 lines (408 loc) 13.7 kB
// 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 Definition of the Bubble class. * * * @see ../demos/bubble.html * * TODO: support decoration and addChild */ goog.provide('goog.ui.Bubble'); goog.require('goog.Timer'); goog.require('goog.events'); goog.require('goog.events.EventType'); goog.require('goog.math.Box'); goog.require('goog.positioning'); goog.require('goog.positioning.AbsolutePosition'); goog.require('goog.positioning.AnchoredPosition'); goog.require('goog.positioning.Corner'); goog.require('goog.positioning.CornerBit'); goog.require('goog.style'); goog.require('goog.ui.Component'); goog.require('goog.ui.Popup'); /** * The Bubble provides a general purpose bubble implementation that can be * anchored to a particular element and displayed for a period of time. * * @param {string|Element} message HTML string or an element to display inside * the bubble. * @param {Object=} opt_config The configuration * for the bubble. If not specified, the default configuration will be * used. {@see goog.ui.Bubble.defaultConfig}. * @param {goog.dom.DomHelper=} opt_domHelper Optional DOM helper. * @constructor * @extends {goog.ui.Component} */ goog.ui.Bubble = function(message, opt_config, opt_domHelper) { goog.ui.Component.call(this, opt_domHelper); /** * The HTML string or element to display inside the bubble. * * @type {string|Element} * @private */ this.message_ = message; /** * The Popup element used to position and display the bubble. * * @type {goog.ui.Popup} * @private */ this.popup_ = new goog.ui.Popup(); /** * Configuration map that contains bubble's UI elements. * * @type {Object} * @private */ this.config_ = opt_config || goog.ui.Bubble.defaultConfig; /** * Id of the close button for this bubble. * * @type {string} * @private */ this.closeButtonId_ = this.makeId('cb'); /** * Id of the div for the embedded element. * * @type {string} * @private */ this.messageId_ = this.makeId('mi'); }; goog.inherits(goog.ui.Bubble, goog.ui.Component); /** * In milliseconds, timeout after which the button auto-hides. Null means * infinite. * @type {?number} * @private */ goog.ui.Bubble.prototype.timeout_ = null; /** * Key returned by the bubble timer. * @type {number} * @private */ goog.ui.Bubble.prototype.timerId_ = 0; /** * Key returned by the listen function for the close button. * @type {goog.events.Key} * @private */ goog.ui.Bubble.prototype.listener_ = null; /** * Key returned by the listen function for the close button. * @type {Element} * @private */ goog.ui.Bubble.prototype.anchor_ = null; /** @override */ goog.ui.Bubble.prototype.createDom = function() { goog.ui.Bubble.superClass_.createDom.call(this); var element = this.getElement(); element.style.position = 'absolute'; element.style.visibility = 'hidden'; this.popup_.setElement(element); }; /** * Attaches the bubble to an anchor element. Computes the positioning and * orientation of the bubble. * * @param {Element} anchorElement The element to which we are attaching. */ goog.ui.Bubble.prototype.attach = function(anchorElement) { this.setAnchoredPosition_( anchorElement, this.computePinnedCorner_(anchorElement)); }; /** * Sets the corner of the bubble to used in the positioning algorithm. * * @param {goog.positioning.Corner} corner The bubble corner used for * positioning constants. */ goog.ui.Bubble.prototype.setPinnedCorner = function(corner) { this.popup_.setPinnedCorner(corner); }; /** * Sets the position of the bubble. Pass null for corner in AnchoredPosition * for corner to be computed automatically. * * @param {goog.positioning.AbstractPosition} position The position of the * bubble. */ goog.ui.Bubble.prototype.setPosition = function(position) { if (position instanceof goog.positioning.AbsolutePosition) { this.popup_.setPosition(position); } else if (position instanceof goog.positioning.AnchoredPosition) { this.setAnchoredPosition_(position.element, position.corner); } else { throw Error('Bubble only supports absolute and anchored positions!'); } }; /** * Sets the timeout after which bubble hides itself. * * @param {number} timeout Timeout of the bubble. */ goog.ui.Bubble.prototype.setTimeout = function(timeout) { this.timeout_ = timeout; }; /** * Sets whether the bubble should be automatically hidden whenever user clicks * outside the bubble element. * * @param {boolean} autoHide Whether to hide if user clicks outside the bubble. */ goog.ui.Bubble.prototype.setAutoHide = function(autoHide) { this.popup_.setAutoHide(autoHide); }; /** * Sets whether the bubble should be visible. * * @param {boolean} visible Desired visibility state. */ goog.ui.Bubble.prototype.setVisible = function(visible) { if (visible && !this.popup_.isVisible()) { this.configureElement_(); } this.popup_.setVisible(visible); if (!this.popup_.isVisible()) { this.unconfigureElement_(); } }; /** * @return {boolean} Whether the bubble is visible. */ goog.ui.Bubble.prototype.isVisible = function() { return this.popup_.isVisible(); }; /** @override */ goog.ui.Bubble.prototype.disposeInternal = function() { this.unconfigureElement_(); this.popup_.dispose(); this.popup_ = null; goog.ui.Bubble.superClass_.disposeInternal.call(this); }; /** * Creates element's contents and configures all timers. This is called on * setVisible(true). * @private */ goog.ui.Bubble.prototype.configureElement_ = function() { if (!this.isInDocument()) { throw Error('You must render the bubble before showing it!'); } var element = this.getElement(); var corner = this.popup_.getPinnedCorner(); element.innerHTML = this.computeHtmlForCorner_(corner); if (typeof this.message_ == 'object') { var messageDiv = this.getDomHelper().getElement(this.messageId_); this.getDomHelper().appendChild(messageDiv, this.message_); } var closeButton = this.getDomHelper().getElement(this.closeButtonId_); this.listener_ = goog.events.listen(closeButton, goog.events.EventType.CLICK, this.hideBubble_, false, this); if (this.timeout_) { this.timerId_ = goog.Timer.callOnce(this.hideBubble_, this.timeout_, this); } }; /** * Gets rid of the element's contents and all assoicated timers and listeners. * This is called on dispose as well as on setVisible(false). * @private */ goog.ui.Bubble.prototype.unconfigureElement_ = function() { if (this.listener_) { goog.events.unlistenByKey(this.listener_); this.listener_ = null; } if (this.timerId_) { goog.Timer.clear(this.timerId_); this.timerId = null; } var element = this.getElement(); if (element) { this.getDomHelper().removeChildren(element); element.innerHTML = ''; } }; /** * Computes bubble position based on anchored element. * * @param {Element} anchorElement The element to which we are attaching. * @param {goog.positioning.Corner} corner The bubble corner used for * positioning. * @private */ goog.ui.Bubble.prototype.setAnchoredPosition_ = function(anchorElement, corner) { this.popup_.setPinnedCorner(corner); var margin = this.createMarginForCorner_(corner); this.popup_.setMargin(margin); var anchorCorner = goog.positioning.flipCorner(corner); this.popup_.setPosition(new goog.positioning.AnchoredPosition( anchorElement, anchorCorner)); }; /** * Hides the bubble. This is called asynchronously by timer of event processor * for the mouse click on the close button. * @private */ goog.ui.Bubble.prototype.hideBubble_ = function() { this.setVisible(false); }; /** * Returns an AnchoredPosition that will position the bubble optimally * given the position of the anchor element and the size of the viewport. * * @param {Element} anchorElement The element to which the bubble is attached. * @return {!goog.ui.Popup.AnchoredPosition} The AnchoredPosition to give to * {@link #setPosition}. */ goog.ui.Bubble.prototype.getComputedAnchoredPosition = function(anchorElement) { return new goog.ui.Popup.AnchoredPosition( anchorElement, this.computePinnedCorner_(anchorElement)); }; /** * Computes the pinned corner for the bubble. * * @param {Element} anchorElement The element to which the button is attached. * @return {goog.positioning.Corner} The pinned corner. * @private */ goog.ui.Bubble.prototype.computePinnedCorner_ = function(anchorElement) { var doc = this.getDomHelper().getOwnerDocument(anchorElement); var viewportElement = goog.style.getClientViewportElement(doc); var viewportWidth = viewportElement.offsetWidth; var viewportHeight = viewportElement.offsetHeight; var anchorElementOffset = goog.style.getPageOffset(anchorElement); var anchorElementSize = goog.style.getSize(anchorElement); var anchorType = 0; // right margin or left? if (viewportWidth - anchorElementOffset.x - anchorElementSize.width > anchorElementOffset.x) { anchorType += 1; } // attaches to the top or to the bottom? if (viewportHeight - anchorElementOffset.y - anchorElementSize.height > anchorElementOffset.y) { anchorType += 2; } return goog.ui.Bubble.corners_[anchorType]; }; /** * Computes the right offset for a given bubble corner * and creates a margin element for it. This is done to have the * button anchor element on its frame rather than on the corner. * * @param {goog.positioning.Corner} corner The corner. * @return {!goog.math.Box} the computed margin. Only left or right fields are * non-zero, but they may be negative. * @private */ goog.ui.Bubble.prototype.createMarginForCorner_ = function(corner) { var margin = new goog.math.Box(0, 0, 0, 0); if (corner & goog.positioning.CornerBit.RIGHT) { margin.right -= this.config_.marginShift; } else { margin.left -= this.config_.marginShift; } return margin; }; /** * Computes the HTML string for a given bubble orientation. * * @param {goog.positioning.Corner} corner The corner. * @return {string} The HTML string to place inside the bubble's popup. * @private */ goog.ui.Bubble.prototype.computeHtmlForCorner_ = function(corner) { var bubbleTopClass; var bubbleBottomClass; switch (corner) { case goog.positioning.Corner.TOP_LEFT: bubbleTopClass = this.config_.cssBubbleTopLeftAnchor; bubbleBottomClass = this.config_.cssBubbleBottomNoAnchor; break; case goog.positioning.Corner.TOP_RIGHT: bubbleTopClass = this.config_.cssBubbleTopRightAnchor; bubbleBottomClass = this.config_.cssBubbleBottomNoAnchor; break; case goog.positioning.Corner.BOTTOM_LEFT: bubbleTopClass = this.config_.cssBubbleTopNoAnchor; bubbleBottomClass = this.config_.cssBubbleBottomLeftAnchor; break; case goog.positioning.Corner.BOTTOM_RIGHT: bubbleTopClass = this.config_.cssBubbleTopNoAnchor; bubbleBottomClass = this.config_.cssBubbleBottomRightAnchor; break; default: throw Error('This corner type is not supported by bubble!'); } var message = null; if (typeof this.message_ == 'object') { message = '<div id="' + this.messageId_ + '">'; } else { message = this.message_; } var html = '<table border=0 cellspacing=0 cellpadding=0 style="z-index:1"' + ' width=' + this.config_.bubbleWidth + '>' + '<tr><td colspan=4 class="' + bubbleTopClass + '">' + '<tr>' + '<td class="' + this.config_.cssBubbleLeft + '">' + '<td class="' + this.config_.cssBubbleFont + '"' + ' style="padding:0 4px;background:white">' + message + '<td id="' + this.closeButtonId_ + '"' + ' class="' + this.config_.cssCloseButton + '"/>' + '<td class="' + this.config_.cssBubbleRight + '">' + '<tr>' + '<td colspan=4 class="' + bubbleBottomClass + '">' + '</table>'; return html; }; /** * A default configuration for the bubble. * * @type {Object} */ goog.ui.Bubble.defaultConfig = { bubbleWidth: 147, marginShift: 60, cssBubbleFont: goog.getCssName('goog-bubble-font'), cssCloseButton: goog.getCssName('goog-bubble-close-button'), cssBubbleTopRightAnchor: goog.getCssName('goog-bubble-top-right-anchor'), cssBubbleTopLeftAnchor: goog.getCssName('goog-bubble-top-left-anchor'), cssBubbleTopNoAnchor: goog.getCssName('goog-bubble-top-no-anchor'), cssBubbleBottomRightAnchor: goog.getCssName('goog-bubble-bottom-right-anchor'), cssBubbleBottomLeftAnchor: goog.getCssName('goog-bubble-bottom-left-anchor'), cssBubbleBottomNoAnchor: goog.getCssName('goog-bubble-bottom-no-anchor'), cssBubbleLeft: goog.getCssName('goog-bubble-left'), cssBubbleRight: goog.getCssName('goog-bubble-right') }; /** * An auxiliary array optimizing the corner computation. * * @type {Array.<goog.positioning.Corner>} * @private */ goog.ui.Bubble.corners_ = [ goog.positioning.Corner.BOTTOM_RIGHT, goog.positioning.Corner.BOTTOM_LEFT, goog.positioning.Corner.TOP_RIGHT, goog.positioning.Corner.TOP_LEFT ];