| // Copyright 2011 The Closure Library Authors. All Rights Reserved. |
| // |
| // Licensed under the Apache License, Version 2.0 (the "License"); |
| // you may not use this file except in compliance with the License. |
| // You may obtain a copy of the License at |
| // |
| // http://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, software |
| // distributed under the License is distributed on an "AS-IS" BASIS, |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| // See the License for the specific language governing permissions and |
| // limitations under the License. |
| |
| /** |
| * @fileoverview Class for showing simple modal popup. |
| * @author chrishenry@google.com (Chris Henry) |
| */ |
| |
| goog.provide('goog.ui.ModalPopup'); |
| |
| goog.require('goog.Timer'); |
| goog.require('goog.a11y.aria'); |
| goog.require('goog.a11y.aria.State'); |
| goog.require('goog.asserts'); |
| goog.require('goog.dom'); |
| goog.require('goog.dom.TagName'); |
| goog.require('goog.dom.classlist'); |
| goog.require('goog.dom.iframe'); |
| goog.require('goog.events'); |
| goog.require('goog.events.EventType'); |
| goog.require('goog.events.FocusHandler'); |
| goog.require('goog.fx.Transition'); |
| goog.require('goog.string'); |
| goog.require('goog.style'); |
| goog.require('goog.ui.Component'); |
| goog.require('goog.ui.PopupBase'); |
| goog.require('goog.userAgent'); |
| |
| |
| |
| /** |
| * Base class for modal popup UI components. This can also be used as |
| * a standalone component to render a modal popup with an empty div. |
| * |
| * WARNING: goog.ui.ModalPopup is only guaranteed to work when it is rendered |
| * directly in the 'body' element. |
| * |
| * The Html structure of the modal popup is: |
| * <pre> |
| * Element Function Class-name, goog-modalpopup = default |
| * ---------------------------------------------------------------------------- |
| * - iframe Iframe mask goog-modalpopup-bg |
| * - div Background mask goog-modalpopup-bg |
| * - div Modal popup area goog-modalpopup |
| * - span Tab catcher |
| * </pre> |
| * @constructor |
| * @param {boolean=} opt_useIframeMask Work around windowed controls z-index |
| * issue by using an iframe instead of a div for bg element. |
| * @param {goog.dom.DomHelper=} opt_domHelper Optional DOM helper; see {@link |
| * goog.ui.Component} for semantics. |
| * @extends {goog.ui.Component} |
| */ |
| goog.ui.ModalPopup = function(opt_useIframeMask, opt_domHelper) { |
| goog.ui.ModalPopup.base(this, 'constructor', opt_domHelper); |
| |
| /** |
| * Whether the modal popup should use an iframe as the background |
| * element to work around z-order issues. |
| * @type {boolean} |
| * @private |
| */ |
| this.useIframeMask_ = !!opt_useIframeMask; |
| |
| /** |
| * The element that had focus before the popup was displayed. |
| * @type {Element} |
| * @private |
| */ |
| this.lastFocus_ = null; |
| }; |
| goog.inherits(goog.ui.ModalPopup, goog.ui.Component); |
| goog.tagUnsealableClass(goog.ui.ModalPopup); |
| |
| |
| /** |
| * Focus handler. It will be initialized in enterDocument. |
| * @type {goog.events.FocusHandler} |
| * @private |
| */ |
| goog.ui.ModalPopup.prototype.focusHandler_ = null; |
| |
| |
| /** |
| * Whether the modal popup is visible. |
| * @type {boolean} |
| * @private |
| */ |
| goog.ui.ModalPopup.prototype.visible_ = false; |
| |
| |
| /** |
| * Element for the background which obscures the UI and blocks events. |
| * @type {Element} |
| * @private |
| */ |
| goog.ui.ModalPopup.prototype.bgEl_ = null; |
| |
| |
| /** |
| * Iframe element that is only used for IE as a workaround to keep select-type |
| * elements from burning through background. |
| * @type {Element} |
| * @private |
| */ |
| goog.ui.ModalPopup.prototype.bgIframeEl_ = null; |
| |
| |
| /** |
| * Element used to catch focus and prevent the user from tabbing out |
| * of the popup. |
| * @type {Element} |
| * @private |
| */ |
| goog.ui.ModalPopup.prototype.tabCatcherElement_ = null; |
| |
| |
| /** |
| * Whether the modal popup is in the process of wrapping focus from the top of |
| * the popup to the last tabbable element. |
| * @type {boolean} |
| * @private |
| */ |
| goog.ui.ModalPopup.prototype.backwardTabWrapInProgress_ = false; |
| |
| |
| /** |
| * Transition to show the popup. |
| * @type {goog.fx.Transition} |
| * @private |
| */ |
| goog.ui.ModalPopup.prototype.popupShowTransition_; |
| |
| |
| /** |
| * Transition to hide the popup. |
| * @type {goog.fx.Transition} |
| * @private |
| */ |
| goog.ui.ModalPopup.prototype.popupHideTransition_; |
| |
| |
| /** |
| * Transition to show the background. |
| * @type {goog.fx.Transition} |
| * @private |
| */ |
| goog.ui.ModalPopup.prototype.bgShowTransition_; |
| |
| |
| /** |
| * Transition to hide the background. |
| * @type {goog.fx.Transition} |
| * @private |
| */ |
| goog.ui.ModalPopup.prototype.bgHideTransition_; |
| |
| |
| /** |
| * The elements set to aria-hidden when the popup was made visible. |
| * @type {Array<!Element>} |
| * @private |
| */ |
| goog.ui.ModalPopup.prototype.hiddenElements_; |
| |
| |
| /** |
| * @return {string} Base CSS class for this component. |
| * @protected |
| */ |
| goog.ui.ModalPopup.prototype.getCssClass = function() { |
| return goog.getCssName('goog-modalpopup'); |
| }; |
| |
| |
| /** |
| * Returns the background iframe mask element, if any. |
| * @return {Element} The background iframe mask element, may return |
| * null/undefined if the modal popup does not use iframe mask. |
| */ |
| goog.ui.ModalPopup.prototype.getBackgroundIframe = function() { |
| return this.bgIframeEl_; |
| }; |
| |
| |
| /** |
| * Returns the background mask element. |
| * @return {Element} The background mask element. |
| */ |
| goog.ui.ModalPopup.prototype.getBackgroundElement = function() { |
| return this.bgEl_; |
| }; |
| |
| |
| /** |
| * Creates the initial DOM representation for the modal popup. |
| * @override |
| */ |
| goog.ui.ModalPopup.prototype.createDom = function() { |
| // Create the modal popup element, and make sure it's hidden. |
| goog.ui.ModalPopup.base(this, 'createDom'); |
| |
| var element = this.getElement(); |
| goog.asserts.assert(element); |
| var allClasses = goog.string.trim(this.getCssClass()).split(' '); |
| goog.dom.classlist.addAll(element, allClasses); |
| goog.dom.setFocusableTabIndex(element, true); |
| goog.style.setElementShown(element, false); |
| |
| // Manages the DOM for background mask elements. |
| this.manageBackgroundDom_(); |
| this.createTabCatcher_(); |
| }; |
| |
| |
| /** |
| * Creates and disposes of the DOM for background mask elements. |
| * @private |
| */ |
| goog.ui.ModalPopup.prototype.manageBackgroundDom_ = function() { |
| if (this.useIframeMask_ && !this.bgIframeEl_) { |
| // IE renders the iframe on top of the select elements while still |
| // respecting the z-index of the other elements on the page. See |
| // http://support.microsoft.com/kb/177378 for more information. |
| // Flash and other controls behave in similar ways for other browsers |
| this.bgIframeEl_ = goog.dom.iframe.createBlank(this.getDomHelper()); |
| this.bgIframeEl_.className = goog.getCssName(this.getCssClass(), 'bg'); |
| goog.style.setElementShown(this.bgIframeEl_, false); |
| goog.style.setOpacity(this.bgIframeEl_, 0); |
| } |
| |
| // Create the backgound mask, initialize its opacity, and make sure it's |
| // hidden. |
| if (!this.bgEl_) { |
| this.bgEl_ = this.getDomHelper().createDom( |
| 'div', goog.getCssName(this.getCssClass(), 'bg')); |
| goog.style.setElementShown(this.bgEl_, false); |
| } |
| }; |
| |
| |
| /** |
| * Creates the tab catcher element. |
| * @private |
| */ |
| goog.ui.ModalPopup.prototype.createTabCatcher_ = function() { |
| // Creates tab catcher element. |
| if (!this.tabCatcherElement_) { |
| this.tabCatcherElement_ = this.getDomHelper().createElement('span'); |
| goog.style.setElementShown(this.tabCatcherElement_, false); |
| goog.dom.setFocusableTabIndex(this.tabCatcherElement_, true); |
| this.tabCatcherElement_.style.position = 'absolute'; |
| } |
| }; |
| |
| |
| /** |
| * Allow a shift-tab from the top of the modal popup to the last tabbable |
| * element by moving focus to the tab catcher. This should be called after |
| * catching a wrapping shift-tab event and before allowing it to propagate, so |
| * that focus will land on the last tabbable element before the tab catcher. |
| * @protected |
| */ |
| goog.ui.ModalPopup.prototype.setupBackwardTabWrap = function() { |
| this.backwardTabWrapInProgress_ = true; |
| try { |
| this.tabCatcherElement_.focus(); |
| } catch (e) { |
| // Swallow this. IE can throw an error if the element can not be focused. |
| } |
| // Reset the flag on a timer in case anything goes wrong with the followup |
| // event. |
| goog.Timer.callOnce(this.resetBackwardTabWrap_, 0, this); |
| }; |
| |
| |
| /** |
| * Resets the backward tab wrap flag. |
| * @private |
| */ |
| goog.ui.ModalPopup.prototype.resetBackwardTabWrap_ = function() { |
| this.backwardTabWrapInProgress_ = false; |
| }; |
| |
| |
| /** |
| * Renders the background mask. |
| * @private |
| */ |
| goog.ui.ModalPopup.prototype.renderBackground_ = function() { |
| goog.asserts.assert(!!this.bgEl_, 'Background element must not be null.'); |
| if (this.bgIframeEl_) { |
| goog.dom.insertSiblingBefore(this.bgIframeEl_, this.getElement()); |
| } |
| goog.dom.insertSiblingBefore(this.bgEl_, this.getElement()); |
| }; |
| |
| |
| /** @override */ |
| goog.ui.ModalPopup.prototype.canDecorate = function(element) { |
| // Assume we can decorate any DIV. |
| return !!element && element.tagName == goog.dom.TagName.DIV; |
| }; |
| |
| |
| /** @override */ |
| goog.ui.ModalPopup.prototype.decorateInternal = function(element) { |
| // Decorate the modal popup area element. |
| goog.ui.ModalPopup.base(this, 'decorateInternal', element); |
| var allClasses = goog.string.trim(this.getCssClass()).split(' '); |
| |
| goog.dom.classlist.addAll( |
| goog.asserts.assert(this.getElement()), |
| allClasses); |
| |
| // Create the background mask... |
| this.manageBackgroundDom_(); |
| this.createTabCatcher_(); |
| |
| // Make sure the decorated modal popup is focusable and hidden. |
| goog.dom.setFocusableTabIndex(this.getElement(), true); |
| goog.style.setElementShown(this.getElement(), false); |
| }; |
| |
| |
| /** @override */ |
| goog.ui.ModalPopup.prototype.enterDocument = function() { |
| this.renderBackground_(); |
| goog.ui.ModalPopup.base(this, 'enterDocument'); |
| |
| goog.dom.insertSiblingAfter(this.tabCatcherElement_, this.getElement()); |
| |
| this.focusHandler_ = new goog.events.FocusHandler( |
| this.getDomHelper().getDocument()); |
| |
| // We need to watch the entire document so that we can detect when the |
| // focus is moved out of this modal popup. |
| this.getHandler().listen( |
| this.focusHandler_, goog.events.FocusHandler.EventType.FOCUSIN, |
| this.onFocus); |
| this.setA11YDetectBackground(false); |
| }; |
| |
| |
| /** @override */ |
| goog.ui.ModalPopup.prototype.exitDocument = function() { |
| if (this.isVisible()) { |
| this.setVisible(false); |
| } |
| |
| goog.dispose(this.focusHandler_); |
| |
| goog.ui.ModalPopup.base(this, 'exitDocument'); |
| goog.dom.removeNode(this.bgIframeEl_); |
| goog.dom.removeNode(this.bgEl_); |
| goog.dom.removeNode(this.tabCatcherElement_); |
| }; |
| |
| |
| /** |
| * Sets the visibility of the modal popup box and focus to the popup. |
| * @param {boolean} visible Whether the modal popup should be visible. |
| */ |
| goog.ui.ModalPopup.prototype.setVisible = function(visible) { |
| goog.asserts.assert( |
| this.isInDocument(), 'ModalPopup must be rendered first.'); |
| |
| if (visible == this.visible_) { |
| return; |
| } |
| |
| if (this.popupShowTransition_) this.popupShowTransition_.stop(); |
| if (this.bgShowTransition_) this.bgShowTransition_.stop(); |
| if (this.popupHideTransition_) this.popupHideTransition_.stop(); |
| if (this.bgHideTransition_) this.bgHideTransition_.stop(); |
| |
| if (this.isInDocument()) { |
| this.setA11YDetectBackground(visible); |
| } |
| if (visible) { |
| this.show_(); |
| } else { |
| this.hide_(); |
| } |
| }; |
| |
| |
| /** |
| * Sets aria-hidden on the rest of the page to restrict screen reader focus. |
| * Top-level elements with an explicit aria-hidden state are not altered. |
| * @param {boolean} hide Whether to hide or show the rest of the page. |
| * @protected |
| */ |
| goog.ui.ModalPopup.prototype.setA11YDetectBackground = function(hide) { |
| if (hide) { |
| if (!this.hiddenElements_) { |
| this.hiddenElements_ = []; |
| } |
| var dom = this.getDomHelper(); |
| var topLevelChildren = dom.getChildren(dom.getDocument().body); |
| for (var i = 0; i < topLevelChildren.length; i++) { |
| var child = topLevelChildren[i]; |
| if (child != this.getElementStrict() && |
| !goog.a11y.aria.getState(child, goog.a11y.aria.State.HIDDEN)) { |
| goog.a11y.aria.setState(child, goog.a11y.aria.State.HIDDEN, true); |
| this.hiddenElements_.push(child); |
| } |
| } |
| } else if (this.hiddenElements_) { |
| for (var i = 0; i < this.hiddenElements_.length; i++) { |
| goog.a11y.aria.removeState( |
| this.hiddenElements_[i], goog.a11y.aria.State.HIDDEN); |
| } |
| this.hiddenElements_ = null; |
| } |
| }; |
| |
| |
| /** |
| * Sets the transitions to show and hide the popup and background. |
| * @param {!goog.fx.Transition} popupShowTransition Transition to show the |
| * popup. |
| * @param {!goog.fx.Transition} popupHideTransition Transition to hide the |
| * popup. |
| * @param {!goog.fx.Transition} bgShowTransition Transition to show |
| * the background. |
| * @param {!goog.fx.Transition} bgHideTransition Transition to hide |
| * the background. |
| */ |
| goog.ui.ModalPopup.prototype.setTransition = function(popupShowTransition, |
| popupHideTransition, bgShowTransition, bgHideTransition) { |
| this.popupShowTransition_ = popupShowTransition; |
| this.popupHideTransition_ = popupHideTransition; |
| this.bgShowTransition_ = bgShowTransition; |
| this.bgHideTransition_ = bgHideTransition; |
| }; |
| |
| |
| /** |
| * Shows the popup. |
| * @private |
| */ |
| goog.ui.ModalPopup.prototype.show_ = function() { |
| if (!this.dispatchEvent(goog.ui.PopupBase.EventType.BEFORE_SHOW)) { |
| return; |
| } |
| |
| try { |
| this.lastFocus_ = this.getDomHelper().getDocument().activeElement; |
| } catch (e) { |
| // Focus-related actions often throw exceptions. |
| // Sample past issue: https://bugzilla.mozilla.org/show_bug.cgi?id=656283 |
| } |
| this.resizeBackground_(); |
| this.reposition(); |
| |
| // Listen for keyboard and resize events while the modal popup is visible. |
| this.getHandler().listen( |
| this.getDomHelper().getWindow(), goog.events.EventType.RESIZE, |
| this.resizeBackground_); |
| |
| this.showPopupElement_(true); |
| this.focus(); |
| this.visible_ = true; |
| |
| if (this.popupShowTransition_ && this.bgShowTransition_) { |
| goog.events.listenOnce( |
| /** @type {!goog.events.EventTarget} */ (this.popupShowTransition_), |
| goog.fx.Transition.EventType.END, this.onShow, false, this); |
| this.bgShowTransition_.play(); |
| this.popupShowTransition_.play(); |
| } else { |
| this.onShow(); |
| } |
| }; |
| |
| |
| /** |
| * Hides the popup. |
| * @private |
| */ |
| goog.ui.ModalPopup.prototype.hide_ = function() { |
| if (!this.dispatchEvent(goog.ui.PopupBase.EventType.BEFORE_HIDE)) { |
| return; |
| } |
| |
| // Stop listening for keyboard and resize events while the modal |
| // popup is hidden. |
| this.getHandler().unlisten( |
| this.getDomHelper().getWindow(), goog.events.EventType.RESIZE, |
| this.resizeBackground_); |
| |
| // Set visibility to hidden even if there is a transition. This |
| // reduces complexity in subclasses who may want to override |
| // setVisible (such as goog.ui.Dialog). |
| this.visible_ = false; |
| |
| if (this.popupHideTransition_ && this.bgHideTransition_) { |
| goog.events.listenOnce( |
| /** @type {!goog.events.EventTarget} */ (this.popupHideTransition_), |
| goog.fx.Transition.EventType.END, this.onHide, false, this); |
| this.bgHideTransition_.play(); |
| // The transition whose END event you are listening to must be played last |
| // to prevent errors when disposing on hide event, which occur on browsers |
| // that do not support CSS3 transitions. |
| this.popupHideTransition_.play(); |
| } else { |
| this.onHide(); |
| } |
| |
| this.returnFocus_(); |
| }; |
| |
| |
| /** |
| * Attempts to return the focus back to the element that had it before the popup |
| * was opened. |
| * @private |
| */ |
| goog.ui.ModalPopup.prototype.returnFocus_ = function() { |
| try { |
| var dom = this.getDomHelper(); |
| var body = dom.getDocument().body; |
| var active = dom.getDocument().activeElement || body; |
| if (!this.lastFocus_ || this.lastFocus_ == body) { |
| this.lastFocus_ = null; |
| return; |
| } |
| // We only want to move the focus if we actually have it, i.e.: |
| // - if we immediately hid the popup the focus should have moved to the |
| // body element |
| // - if there is a hiding transition in progress the focus would still be |
| // within the dialog and it is safe to move it if the current focused |
| // element is a child of the dialog |
| if (active == body || dom.contains(this.getElement(), active)) { |
| this.lastFocus_.focus(); |
| } |
| } catch (e) { |
| // Swallow this. IE can throw an error if the element can not be focused. |
| } |
| // Explicitly want to null this out even if there was an error focusing to |
| // avoid bleed over between dialog invocations. |
| this.lastFocus_ = null; |
| }; |
| |
| |
| /** |
| * Shows or hides the popup element. |
| * @param {boolean} visible Shows the popup element if true, hides if false. |
| * @private |
| */ |
| goog.ui.ModalPopup.prototype.showPopupElement_ = function(visible) { |
| if (this.bgIframeEl_) { |
| goog.style.setElementShown(this.bgIframeEl_, visible); |
| } |
| if (this.bgEl_) { |
| goog.style.setElementShown(this.bgEl_, visible); |
| } |
| goog.style.setElementShown(this.getElement(), visible); |
| goog.style.setElementShown(this.tabCatcherElement_, visible); |
| }; |
| |
| |
| /** |
| * Called after the popup is shown. If there is a transition, this |
| * will be called after the transition completed or stopped. |
| * @protected |
| */ |
| goog.ui.ModalPopup.prototype.onShow = function() { |
| this.dispatchEvent(goog.ui.PopupBase.EventType.SHOW); |
| }; |
| |
| |
| /** |
| * Called after the popup is hidden. If there is a transition, this |
| * will be called after the transition completed or stopped. |
| * @protected |
| */ |
| goog.ui.ModalPopup.prototype.onHide = function() { |
| this.showPopupElement_(false); |
| this.dispatchEvent(goog.ui.PopupBase.EventType.HIDE); |
| }; |
| |
| |
| /** |
| * @return {boolean} Whether the modal popup is visible. |
| */ |
| goog.ui.ModalPopup.prototype.isVisible = function() { |
| return this.visible_; |
| }; |
| |
| |
| /** |
| * Focuses on the modal popup. |
| */ |
| goog.ui.ModalPopup.prototype.focus = function() { |
| this.focusElement_(); |
| }; |
| |
| |
| /** |
| * Make the background element the size of the document. |
| * |
| * NOTE(user): We must hide the background element before measuring the |
| * document, otherwise the size of the background will stop the document from |
| * shrinking to fit a smaller window. This does cause a slight flicker in Linux |
| * browsers, but should not be a common scenario. |
| * @private |
| */ |
| goog.ui.ModalPopup.prototype.resizeBackground_ = function() { |
| if (this.bgIframeEl_) { |
| goog.style.setElementShown(this.bgIframeEl_, false); |
| } |
| if (this.bgEl_) { |
| goog.style.setElementShown(this.bgEl_, false); |
| } |
| |
| var doc = this.getDomHelper().getDocument(); |
| var win = goog.dom.getWindow(doc) || window; |
| |
| // Take the max of document height and view height, in case the document does |
| // not fill the viewport. Read from both the body element and the html element |
| // to account for browser differences in treatment of absolutely-positioned |
| // content. |
| var viewSize = goog.dom.getViewportSize(win); |
| var w = Math.max(viewSize.width, |
| Math.max(doc.body.scrollWidth, doc.documentElement.scrollWidth)); |
| var h = Math.max(viewSize.height, |
| Math.max(doc.body.scrollHeight, doc.documentElement.scrollHeight)); |
| |
| if (this.bgIframeEl_) { |
| goog.style.setElementShown(this.bgIframeEl_, true); |
| goog.style.setSize(this.bgIframeEl_, w, h); |
| } |
| if (this.bgEl_) { |
| goog.style.setElementShown(this.bgEl_, true); |
| goog.style.setSize(this.bgEl_, w, h); |
| } |
| }; |
| |
| |
| /** |
| * Centers the modal popup in the viewport, taking scrolling into account. |
| */ |
| goog.ui.ModalPopup.prototype.reposition = function() { |
| // TODO(chrishenry): Make this use goog.positioning as in goog.ui.PopupBase? |
| |
| // Get the current viewport to obtain the scroll offset. |
| var doc = this.getDomHelper().getDocument(); |
| var win = goog.dom.getWindow(doc) || window; |
| if (goog.style.getComputedPosition(this.getElement()) == 'fixed') { |
| var x = 0; |
| var y = 0; |
| } else { |
| var scroll = this.getDomHelper().getDocumentScroll(); |
| var x = scroll.x; |
| var y = scroll.y; |
| } |
| |
| var popupSize = goog.style.getSize(this.getElement()); |
| var viewSize = goog.dom.getViewportSize(win); |
| |
| // Make sure left and top are non-negatives. |
| var left = Math.max(x + viewSize.width / 2 - popupSize.width / 2, 0); |
| var top = Math.max(y + viewSize.height / 2 - popupSize.height / 2, 0); |
| goog.style.setPosition(this.getElement(), left, top); |
| |
| // We place the tab catcher at the same position as the dialog to |
| // prevent IE from scrolling when users try to tab out of the dialog. |
| goog.style.setPosition(this.tabCatcherElement_, left, top); |
| }; |
| |
| |
| /** |
| * Handles focus events. Makes sure that if the user tabs past the |
| * elements in the modal popup, the focus wraps back to the beginning, and that |
| * if the user shift-tabs past the front of the modal popup, focus wraps around |
| * to the end. |
| * @param {goog.events.BrowserEvent} e Browser's event object. |
| * @protected |
| */ |
| goog.ui.ModalPopup.prototype.onFocus = function(e) { |
| if (this.backwardTabWrapInProgress_) { |
| this.resetBackwardTabWrap_(); |
| } else if (e.target == this.tabCatcherElement_) { |
| goog.Timer.callOnce(this.focusElement_, 0, this); |
| } |
| }; |
| |
| |
| /** |
| * Returns the magic tab catcher element used to detect when the user has |
| * rolled focus off of the popup content. It is automatically created during |
| * the createDom method() and can be used by subclasses to implement custom |
| * tab-loop behavior. |
| * @return {Element} The tab catcher element. |
| * @protected |
| */ |
| goog.ui.ModalPopup.prototype.getTabCatcherElement = function() { |
| return this.tabCatcherElement_; |
| }; |
| |
| |
| /** |
| * Moves the focus to the modal popup. |
| * @private |
| */ |
| goog.ui.ModalPopup.prototype.focusElement_ = function() { |
| try { |
| if (goog.userAgent.IE) { |
| // In IE, we must first focus on the body or else focussing on a |
| // sub-element will not work. |
| this.getDomHelper().getDocument().body.focus(); |
| } |
| this.getElement().focus(); |
| } catch (e) { |
| // Swallow this. IE can throw an error if the element can not be focused. |
| } |
| }; |
| |
| |
| /** @override */ |
| goog.ui.ModalPopup.prototype.disposeInternal = function() { |
| goog.dispose(this.popupShowTransition_); |
| this.popupShowTransition_ = null; |
| |
| goog.dispose(this.popupHideTransition_); |
| this.popupHideTransition_ = null; |
| |
| goog.dispose(this.bgShowTransition_); |
| this.bgShowTransition_ = null; |
| |
| goog.dispose(this.bgHideTransition_); |
| this.bgHideTransition_ = null; |
| |
| goog.ui.ModalPopup.base(this, 'disposeInternal'); |
| }; |