| // Copyright 2006 The Closure Library Authors. All Rights Reserved. |
| // |
| // Licensed under the Apache License, Version 2.0 (the "License"); |
| // you may not use this file except in compliance with the License. |
| // You may obtain a copy of the License at |
| // |
| // http://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, software |
| // distributed under the License is distributed on an "AS-IS" BASIS, |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| // See the License for the specific language governing permissions and |
| // limitations under the License. |
| |
| /** |
| * @fileoverview Anchored viewport positioning class. |
| * |
| * @author eae@google.com (Emil A Eklund) |
| */ |
| |
| goog.provide('goog.positioning.AnchoredViewportPosition'); |
| |
| goog.require('goog.positioning'); |
| goog.require('goog.positioning.AnchoredPosition'); |
| goog.require('goog.positioning.Overflow'); |
| goog.require('goog.positioning.OverflowStatus'); |
| |
| |
| |
| /** |
| * Encapsulates a popup position where the popup is anchored at a corner of |
| * an element. The corners are swapped if dictated by the viewport. For instance |
| * if a popup is anchored with its top left corner to the bottom left corner of |
| * the anchor the popup is either displayed below the anchor (as specified) or |
| * above it if there's not enough room to display it below. |
| * |
| * When using this positioning object it's recommended that the movable element |
| * be absolutely positioned. |
| * |
| * @param {Element} anchorElement Element the movable element should be |
| * anchored against. |
| * @param {goog.positioning.Corner} corner Corner of anchored element the |
| * movable element should be positioned at. |
| * @param {boolean=} opt_adjust Whether the positioning should be adjusted until |
| * the element fits inside the viewport even if that means that the anchored |
| * corners are ignored. |
| * @param {goog.math.Box=} opt_overflowConstraint Box object describing the |
| * dimensions in which the movable element could be shown. |
| * @constructor |
| * @extends {goog.positioning.AnchoredPosition} |
| */ |
| goog.positioning.AnchoredViewportPosition = function(anchorElement, |
| corner, |
| opt_adjust, |
| opt_overflowConstraint) { |
| goog.positioning.AnchoredPosition.call(this, anchorElement, corner); |
| |
| /** |
| * The last resort algorithm to use if the algorithm can't fit inside |
| * the viewport. |
| * |
| * IGNORE = do nothing, just display at the preferred position. |
| * |
| * ADJUST_X | ADJUST_Y = Adjust until the element fits, even if that means |
| * that the anchored corners are ignored. |
| * |
| * @type {number} |
| * @private |
| */ |
| this.lastResortOverflow_ = opt_adjust ? |
| (goog.positioning.Overflow.ADJUST_X | |
| goog.positioning.Overflow.ADJUST_Y) : |
| goog.positioning.Overflow.IGNORE; |
| |
| /** |
| * The dimensions in which the movable element could be shown. |
| * @type {goog.math.Box|undefined} |
| * @private |
| */ |
| this.overflowConstraint_ = opt_overflowConstraint || undefined; |
| }; |
| goog.inherits(goog.positioning.AnchoredViewportPosition, |
| goog.positioning.AnchoredPosition); |
| |
| |
| /** |
| * @return {goog.math.Box|undefined} The box object describing the |
| * dimensions in which the movable element will be shown. |
| */ |
| goog.positioning.AnchoredViewportPosition.prototype.getOverflowConstraint = |
| function() { |
| return this.overflowConstraint_; |
| }; |
| |
| |
| /** |
| * @param {goog.math.Box|undefined} overflowConstraint Box object describing the |
| * dimensions in which the movable element could be shown. |
| */ |
| goog.positioning.AnchoredViewportPosition.prototype.setOverflowConstraint = |
| function(overflowConstraint) { |
| this.overflowConstraint_ = overflowConstraint; |
| }; |
| |
| |
| /** |
| * @return {number} A bitmask for the "last resort" overflow. |
| */ |
| goog.positioning.AnchoredViewportPosition.prototype.getLastResortOverflow = |
| function() { |
| return this.lastResortOverflow_; |
| }; |
| |
| |
| /** |
| * @param {number} lastResortOverflow A bitmask for the "last resort" overflow, |
| * if we fail to fit the element on-screen. |
| */ |
| goog.positioning.AnchoredViewportPosition.prototype.setLastResortOverflow = |
| function(lastResortOverflow) { |
| this.lastResortOverflow_ = lastResortOverflow; |
| }; |
| |
| |
| /** |
| * Repositions the movable element. |
| * |
| * @param {Element} movableElement Element to position. |
| * @param {goog.positioning.Corner} movableCorner Corner of the movable element |
| * that should be positioned adjacent to the anchored element. |
| * @param {goog.math.Box=} opt_margin A margin specified in pixels. |
| * @param {goog.math.Size=} opt_preferredSize The preferred size of the |
| * movableElement. |
| * @override |
| */ |
| goog.positioning.AnchoredViewportPosition.prototype.reposition = function( |
| movableElement, movableCorner, opt_margin, opt_preferredSize) { |
| var status = goog.positioning.positionAtAnchor(this.element, this.corner, |
| movableElement, movableCorner, null, opt_margin, |
| goog.positioning.Overflow.FAIL_X | goog.positioning.Overflow.FAIL_Y, |
| opt_preferredSize, this.overflowConstraint_); |
| |
| // If the desired position is outside the viewport try mirroring the corners |
| // horizontally or vertically. |
| if (status & goog.positioning.OverflowStatus.FAILED) { |
| var cornerFallback = this.adjustCorner(status, this.corner); |
| var movableCornerFallback = this.adjustCorner(status, movableCorner); |
| |
| status = goog.positioning.positionAtAnchor(this.element, cornerFallback, |
| movableElement, movableCornerFallback, null, opt_margin, |
| goog.positioning.Overflow.FAIL_X | goog.positioning.Overflow.FAIL_Y, |
| opt_preferredSize, this.overflowConstraint_); |
| |
| if (status & goog.positioning.OverflowStatus.FAILED) { |
| // If that also fails, pick the best corner from the two tries, |
| // and adjust the position until it fits. |
| cornerFallback = this.adjustCorner(status, cornerFallback); |
| movableCornerFallback = this.adjustCorner( |
| status, movableCornerFallback); |
| |
| goog.positioning.positionAtAnchor(this.element, cornerFallback, |
| movableElement, movableCornerFallback, null, opt_margin, |
| this.getLastResortOverflow(), opt_preferredSize, |
| this.overflowConstraint_); |
| } |
| } |
| }; |
| |
| |
| /** |
| * Adjusts the corner if X or Y positioning failed. |
| * @param {number} status The status of the last positionAtAnchor call. |
| * @param {goog.positioning.Corner} corner The corner to adjust. |
| * @return {goog.positioning.Corner} The adjusted corner. |
| * @protected |
| */ |
| goog.positioning.AnchoredViewportPosition.prototype.adjustCorner = function( |
| status, corner) { |
| if (status & goog.positioning.OverflowStatus.FAILED_HORIZONTAL) { |
| corner = goog.positioning.flipCornerHorizontal(corner); |
| } |
| |
| if (status & goog.positioning.OverflowStatus.FAILED_VERTICAL) { |
| corner = goog.positioning.flipCornerVertical(corner); |
| } |
| |
| return corner; |
| }; |
| |