// Copyright 2008 The Closure Library Authors. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS-IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. /** * @fileoverview Iframe shims, to protect controls on the underlying page * from bleeding through popups. * * @author gboyer@google.com (Garrett Boyer) * @author nicksantos@google.com (Nick Santos) (Ported to Closure) */ goog.provide('goog.ui.IframeMask'); goog.require('goog.Disposable'); goog.require('goog.Timer'); goog.require('goog.dom'); goog.require('goog.dom.iframe'); goog.require('goog.events.EventHandler'); goog.require('goog.structs.Pool'); goog.require('goog.style'); /** * Controller for an iframe mask. The mask is only valid in the current * document, or else the document of the given DOM helper. * * @param {goog.dom.DomHelper=} opt_domHelper The DOM helper for the relevant * document. * @param {goog.structs.Pool=} opt_iframePool An optional source of iframes. * Iframes will be grabbed from the pool when they're needed and returned * to the pool (but still attached to the DOM) when they're done. * @constructor * @extends {goog.Disposable} */ goog.ui.IframeMask = function(opt_domHelper, opt_iframePool) { goog.Disposable.call(this); /** * The DOM helper for this document. * @type {goog.dom.DomHelper} * @private */ this.dom_ = opt_domHelper || goog.dom.getDomHelper(); /** * An Element to snap the mask to. If none is given, defaults to * a full-screen iframe mask. * @type {Element} * @private */ this.snapElement_ = this.dom_.getDocument().documentElement; /** * An event handler for listening to popups and the like. * @type {goog.events.EventHandler} * @private */ this.handler_ = new goog.events.EventHandler(this); /** * An iframe pool. * @type {goog.structs.Pool|undefined} * @private */ this.iframePool_ = opt_iframePool; }; goog.inherits(goog.ui.IframeMask, goog.Disposable); goog.tagUnsealableClass(goog.ui.IframeMask); /** * An iframe. * @type {HTMLIFrameElement} * @private */ goog.ui.IframeMask.prototype.iframe_; /** * The z-index of the iframe mask. * @type {number} * @private */ goog.ui.IframeMask.prototype.zIndex_ = 1; /** * The opacity of the iframe mask, expressed as a value between 0 and 1, with * 1 being totally opaque. * @type {number} * @private */ goog.ui.IframeMask.prototype.opacity_ = 0; /** * Removes the iframe from the DOM. * @override * @protected */ goog.ui.IframeMask.prototype.disposeInternal = function() { if (this.iframePool_) { this.iframePool_.releaseObject( /** @type {HTMLIFrameElement} */ (this.iframe_)); } else { goog.dom.removeNode(this.iframe_); } this.iframe_ = null; this.handler_.dispose(); this.handler_ = null; goog.ui.IframeMask.superClass_.disposeInternal.call(this); }; /** * CSS for a hidden iframe. * @type {string} * @private */ goog.ui.IframeMask.HIDDEN_CSS_TEXT_ = 'position:absolute;display:none;z-index:1'; /** * Removes the mask from the screen. */ goog.ui.IframeMask.prototype.hideMask = function() { if (this.iframe_) { this.iframe_.style.cssText = goog.ui.IframeMask.HIDDEN_CSS_TEXT_; if (this.iframePool_) { this.iframePool_.releaseObject(this.iframe_); this.iframe_ = null; } } }; /** * Gets the iframe to use as a mask. Creates a new one if one has not been * created yet. * @return {!HTMLIFrameElement} The iframe. * @private */ goog.ui.IframeMask.prototype.getIframe_ = function() { if (!this.iframe_) { this.iframe_ = this.iframePool_ ? /** @type {HTMLIFrameElement} */ (this.iframePool_.getObject()) : goog.dom.iframe.createBlank(this.dom_); this.iframe_.style.cssText = goog.ui.IframeMask.HIDDEN_CSS_TEXT_; this.dom_.getDocument().body.appendChild(this.iframe_); } return this.iframe_; }; /** * Applies the iframe mask to the screen. */ goog.ui.IframeMask.prototype.applyMask = function() { var iframe = this.getIframe_(); var bounds = goog.style.getBounds(this.snapElement_); iframe.style.cssText = 'position:absolute;' + 'left:' + bounds.left + 'px;' + 'top:' + bounds.top + 'px;' + 'width:' + bounds.width + 'px;' + 'height:' + bounds.height + 'px;' + 'z-index:' + this.zIndex_; goog.style.setOpacity(iframe, this.opacity_); iframe.style.display = 'block'; }; /** * Sets the opacity of the mask. Will take effect the next time the mask * is applied. * @param {number} opacity A value between 0 and 1, with 1 being * totally opaque. */ goog.ui.IframeMask.prototype.setOpacity = function(opacity) { this.opacity_ = opacity; }; /** * Sets the z-index of the mask. Will take effect the next time the mask * is applied. * @param {number} zIndex A z-index value. */ goog.ui.IframeMask.prototype.setZIndex = function(zIndex) { this.zIndex_ = zIndex; }; /** * Sets the element to use as the bounds of the mask. Takes effect immediately. * @param {Element} snapElement The snap element, which the iframe will be * "snapped" around. */ goog.ui.IframeMask.prototype.setSnapElement = function(snapElement) { this.snapElement_ = snapElement; if (this.iframe_ && goog.style.isElementShown(this.iframe_)) { this.applyMask(); } }; /** * Listens on the specified target, hiding and showing the iframe mask * when the given event types are dispatched. * @param {goog.events.EventTarget} target The event target to listen on. * @param {string} showEvent When this event fires, the mask will be applied. * @param {string} hideEvent When this event fires, the mask will be hidden. * @param {Element=} opt_snapElement When the mask is applied, it will * automatically snap to this element. If no element is specified, it will * use the default snap element. */ goog.ui.IframeMask.prototype.listenOnTarget = function( target, showEvent, hideEvent, opt_snapElement) { var timerKey; this.handler_.listen(target, showEvent, function() { if (opt_snapElement) { this.setSnapElement(opt_snapElement); } // Check out the iframe asynchronously, so we don't block the SHOW // event and cause a bounce. timerKey = goog.Timer.callOnce(this.applyMask, 0, this); }); this.handler_.listen(target, hideEvent, function() { if (timerKey) { goog.Timer.clear(timerKey); timerKey = null; } this.hideMask(); }); }; /** * Removes all handlers attached by listenOnTarget. */ goog.ui.IframeMask.prototype.removeHandlers = function() { this.handler_.removeAll(); };