anchoredviewportposition.js 6.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187
  1. // Copyright 2006 The Closure Library Authors. All Rights Reserved.
  2. //
  3. // Licensed under the Apache License, Version 2.0 (the "License");
  4. // you may not use this file except in compliance with the License.
  5. // You may obtain a copy of the License at
  6. //
  7. // http://www.apache.org/licenses/LICENSE-2.0
  8. //
  9. // Unless required by applicable law or agreed to in writing, software
  10. // distributed under the License is distributed on an "AS-IS" BASIS,
  11. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. // See the License for the specific language governing permissions and
  13. // limitations under the License.
  14. /**
  15. * @fileoverview Anchored viewport positioning class.
  16. *
  17. * @author eae@google.com (Emil A Eklund)
  18. */
  19. goog.provide('goog.positioning.AnchoredViewportPosition');
  20. goog.require('goog.positioning');
  21. goog.require('goog.positioning.AnchoredPosition');
  22. goog.require('goog.positioning.Overflow');
  23. goog.require('goog.positioning.OverflowStatus');
  24. /**
  25. * Encapsulates a popup position where the popup is anchored at a corner of
  26. * an element. The corners are swapped if dictated by the viewport. For instance
  27. * if a popup is anchored with its top left corner to the bottom left corner of
  28. * the anchor the popup is either displayed below the anchor (as specified) or
  29. * above it if there's not enough room to display it below.
  30. *
  31. * When using this positioning object it's recommended that the movable element
  32. * be absolutely positioned.
  33. *
  34. * @param {Element} anchorElement Element the movable element should be
  35. * anchored against.
  36. * @param {goog.positioning.Corner} corner Corner of anchored element the
  37. * movable element should be positioned at.
  38. * @param {boolean=} opt_adjust Whether the positioning should be adjusted until
  39. * the element fits inside the viewport even if that means that the anchored
  40. * corners are ignored.
  41. * @param {goog.math.Box=} opt_overflowConstraint Box object describing the
  42. * dimensions in which the movable element could be shown.
  43. * @constructor
  44. * @extends {goog.positioning.AnchoredPosition}
  45. */
  46. goog.positioning.AnchoredViewportPosition = function(
  47. anchorElement, corner, opt_adjust, opt_overflowConstraint) {
  48. goog.positioning.AnchoredPosition.call(this, anchorElement, corner);
  49. /**
  50. * The last resort algorithm to use if the algorithm can't fit inside
  51. * the viewport.
  52. *
  53. * IGNORE = do nothing, just display at the preferred position.
  54. *
  55. * ADJUST_X | ADJUST_Y = Adjust until the element fits, even if that means
  56. * that the anchored corners are ignored.
  57. *
  58. * @type {number}
  59. * @private
  60. */
  61. this.lastResortOverflow_ = opt_adjust ? (goog.positioning.Overflow.ADJUST_X |
  62. goog.positioning.Overflow.ADJUST_Y) :
  63. goog.positioning.Overflow.IGNORE;
  64. /**
  65. * The dimensions in which the movable element could be shown.
  66. * @type {goog.math.Box|undefined}
  67. * @private
  68. */
  69. this.overflowConstraint_ = opt_overflowConstraint || undefined;
  70. };
  71. goog.inherits(
  72. goog.positioning.AnchoredViewportPosition,
  73. goog.positioning.AnchoredPosition);
  74. /**
  75. * @return {goog.math.Box|undefined} The box object describing the
  76. * dimensions in which the movable element will be shown.
  77. */
  78. goog.positioning.AnchoredViewportPosition.prototype.getOverflowConstraint =
  79. function() {
  80. return this.overflowConstraint_;
  81. };
  82. /**
  83. * @param {goog.math.Box|undefined} overflowConstraint Box object describing the
  84. * dimensions in which the movable element could be shown.
  85. */
  86. goog.positioning.AnchoredViewportPosition.prototype.setOverflowConstraint =
  87. function(overflowConstraint) {
  88. this.overflowConstraint_ = overflowConstraint;
  89. };
  90. /**
  91. * @return {number} A bitmask for the "last resort" overflow.
  92. */
  93. goog.positioning.AnchoredViewportPosition.prototype.getLastResortOverflow =
  94. function() {
  95. return this.lastResortOverflow_;
  96. };
  97. /**
  98. * @param {number} lastResortOverflow A bitmask for the "last resort" overflow,
  99. * if we fail to fit the element on-screen.
  100. */
  101. goog.positioning.AnchoredViewportPosition.prototype.setLastResortOverflow =
  102. function(lastResortOverflow) {
  103. this.lastResortOverflow_ = lastResortOverflow;
  104. };
  105. /**
  106. * Repositions the movable element.
  107. *
  108. * @param {Element} movableElement Element to position.
  109. * @param {goog.positioning.Corner} movableCorner Corner of the movable element
  110. * that should be positioned adjacent to the anchored element.
  111. * @param {goog.math.Box=} opt_margin A margin specified in pixels.
  112. * @param {goog.math.Size=} opt_preferredSize The preferred size of the
  113. * movableElement.
  114. * @override
  115. */
  116. goog.positioning.AnchoredViewportPosition.prototype.reposition = function(
  117. movableElement, movableCorner, opt_margin, opt_preferredSize) {
  118. var status = goog.positioning.positionAtAnchor(
  119. this.element, this.corner, movableElement, movableCorner, null,
  120. opt_margin,
  121. goog.positioning.Overflow.FAIL_X | goog.positioning.Overflow.FAIL_Y,
  122. opt_preferredSize, this.overflowConstraint_);
  123. // If the desired position is outside the viewport try mirroring the corners
  124. // horizontally or vertically.
  125. if (status & goog.positioning.OverflowStatus.FAILED) {
  126. var cornerFallback = this.adjustCorner(status, this.corner);
  127. var movableCornerFallback = this.adjustCorner(status, movableCorner);
  128. status = goog.positioning.positionAtAnchor(
  129. this.element, cornerFallback, movableElement, movableCornerFallback,
  130. null, opt_margin,
  131. goog.positioning.Overflow.FAIL_X | goog.positioning.Overflow.FAIL_Y,
  132. opt_preferredSize, this.overflowConstraint_);
  133. if (status & goog.positioning.OverflowStatus.FAILED) {
  134. // If that also fails, pick the best corner from the two tries,
  135. // and adjust the position until it fits.
  136. cornerFallback = this.adjustCorner(status, cornerFallback);
  137. movableCornerFallback = this.adjustCorner(status, movableCornerFallback);
  138. goog.positioning.positionAtAnchor(
  139. this.element, cornerFallback, movableElement, movableCornerFallback,
  140. null, opt_margin, this.getLastResortOverflow(), opt_preferredSize,
  141. this.overflowConstraint_);
  142. }
  143. }
  144. };
  145. /**
  146. * Adjusts the corner if X or Y positioning failed.
  147. * @param {number} status The status of the last positionAtAnchor call.
  148. * @param {goog.positioning.Corner} corner The corner to adjust.
  149. * @return {goog.positioning.Corner} The adjusted corner.
  150. * @protected
  151. */
  152. goog.positioning.AnchoredViewportPosition.prototype.adjustCorner = function(
  153. status, corner) {
  154. if (status & goog.positioning.OverflowStatus.FAILED_HORIZONTAL) {
  155. corner = goog.positioning.flipCornerHorizontal(corner);
  156. }
  157. if (status & goog.positioning.OverflowStatus.FAILED_VERTICAL) {
  158. corner = goog.positioning.flipCornerVertical(corner);
  159. }
  160. return corner;
  161. };