animatedzippy.js 6.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232
  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 Animated zippy widget implementation.
  16. *
  17. * @author eae@google.com (Emil A Eklund)
  18. * @see ../demos/zippy.html
  19. */
  20. goog.provide('goog.ui.AnimatedZippy');
  21. goog.require('goog.dom');
  22. goog.require('goog.dom.TagName');
  23. goog.require('goog.events');
  24. goog.require('goog.fx.Animation');
  25. goog.require('goog.fx.Transition');
  26. goog.require('goog.fx.easing');
  27. goog.require('goog.ui.Zippy');
  28. goog.require('goog.ui.ZippyEvent');
  29. /**
  30. * Zippy widget. Expandable/collapsible container, clicking the header toggles
  31. * the visibility of the content.
  32. *
  33. * @param {Element|string|null} header Header element, either element
  34. * reference, string id or null if no header exists.
  35. * @param {Element|string} content Content element, either element reference or
  36. * string id.
  37. * @param {boolean=} opt_expanded Initial expanded/visibility state. Defaults to
  38. * false.
  39. * @param {goog.dom.DomHelper=} opt_domHelper An optional DOM helper.
  40. * @constructor
  41. * @extends {goog.ui.Zippy}
  42. */
  43. goog.ui.AnimatedZippy = function(header, content, opt_expanded, opt_domHelper) {
  44. var domHelper = opt_domHelper || goog.dom.getDomHelper();
  45. // Create wrapper element and move content into it.
  46. var elWrapper =
  47. domHelper.createDom(goog.dom.TagName.DIV, {'style': 'overflow:hidden'});
  48. var elContent = domHelper.getElement(content);
  49. elContent.parentNode.replaceChild(elWrapper, elContent);
  50. elWrapper.appendChild(elContent);
  51. /**
  52. * Content wrapper, used for animation.
  53. * @type {Element}
  54. * @private
  55. */
  56. this.elWrapper_ = elWrapper;
  57. /**
  58. * Reference to animation or null if animation is not active.
  59. * @type {goog.fx.Animation}
  60. * @private
  61. */
  62. this.anim_ = null;
  63. // Call constructor of super class.
  64. goog.ui.Zippy.call(
  65. this, header, elContent, opt_expanded, undefined, domHelper);
  66. // Set initial state.
  67. // NOTE: Set the class names as well otherwise animated zippys
  68. // start with empty class names.
  69. var expanded = this.isExpanded();
  70. this.elWrapper_.style.display = expanded ? '' : 'none';
  71. this.updateHeaderClassName(expanded);
  72. };
  73. goog.inherits(goog.ui.AnimatedZippy, goog.ui.Zippy);
  74. goog.tagUnsealableClass(goog.ui.AnimatedZippy);
  75. /**
  76. * Constants for event names.
  77. *
  78. * @const
  79. */
  80. goog.ui.AnimatedZippy.Events = {
  81. // The beginning of the animation when the zippy state toggles.
  82. TOGGLE_ANIMATION_BEGIN: goog.events.getUniqueId('toggleanimationbegin'),
  83. // The end of the animation when the zippy state toggles.
  84. TOGGLE_ANIMATION_END: goog.events.getUniqueId('toggleanimationend')
  85. };
  86. /**
  87. * Duration of expand/collapse animation, in milliseconds.
  88. * @type {number}
  89. */
  90. goog.ui.AnimatedZippy.prototype.animationDuration = 500;
  91. /**
  92. * Acceleration function for expand/collapse animation.
  93. * @type {!Function}
  94. */
  95. goog.ui.AnimatedZippy.prototype.animationAcceleration = goog.fx.easing.easeOut;
  96. /**
  97. * @return {boolean} Whether the zippy is in the process of being expanded or
  98. * collapsed.
  99. */
  100. goog.ui.AnimatedZippy.prototype.isBusy = function() {
  101. return this.anim_ != null;
  102. };
  103. /**
  104. * Sets expanded state.
  105. *
  106. * @param {boolean} expanded Expanded/visibility state.
  107. * @override
  108. */
  109. goog.ui.AnimatedZippy.prototype.setExpanded = function(expanded) {
  110. if (this.isExpanded() == expanded && !this.anim_) {
  111. return;
  112. }
  113. // Reset display property of wrapper to allow content element to be
  114. // measured.
  115. if (this.elWrapper_.style.display == 'none') {
  116. this.elWrapper_.style.display = '';
  117. }
  118. // Measure content element.
  119. var h = this.getContentElement().offsetHeight;
  120. // Stop active animation (if any) and determine starting height.
  121. var startH = 0;
  122. if (this.anim_) {
  123. expanded = this.isExpanded();
  124. goog.events.removeAll(this.anim_);
  125. this.anim_.stop(false);
  126. var marginTop = parseInt(this.getContentElement().style.marginTop, 10);
  127. startH = h - Math.abs(marginTop);
  128. } else {
  129. startH = expanded ? 0 : h;
  130. }
  131. // Updates header class name after the animation has been stopped.
  132. this.updateHeaderClassName(expanded);
  133. // Set up expand/collapse animation.
  134. this.anim_ = new goog.fx.Animation(
  135. [0, startH], [0, expanded ? h : 0], this.animationDuration,
  136. this.animationAcceleration);
  137. var events = [
  138. goog.fx.Transition.EventType.BEGIN, goog.fx.Animation.EventType.ANIMATE,
  139. goog.fx.Transition.EventType.END
  140. ];
  141. goog.events.listen(this.anim_, events, this.onAnimate_, false, this);
  142. goog.events.listen(
  143. this.anim_, goog.fx.Transition.EventType.BEGIN,
  144. goog.bind(this.onAnimationBegin_, this, expanded));
  145. goog.events.listen(
  146. this.anim_, goog.fx.Transition.EventType.END,
  147. goog.bind(this.onAnimationCompleted_, this, expanded));
  148. // Start animation.
  149. this.anim_.play(false);
  150. };
  151. /**
  152. * Called during animation
  153. *
  154. * @param {goog.events.Event} e The event.
  155. * @private
  156. */
  157. goog.ui.AnimatedZippy.prototype.onAnimate_ = function(e) {
  158. var contentElement = this.getContentElement();
  159. var h = contentElement.offsetHeight;
  160. contentElement.style.marginTop = (e.y - h) + 'px';
  161. };
  162. /**
  163. * Called once the expand/collapse animation has started.
  164. *
  165. * @param {boolean} expanding Expanded/visibility state.
  166. * @private
  167. */
  168. goog.ui.AnimatedZippy.prototype.onAnimationBegin_ = function(expanding) {
  169. this.dispatchEvent(new goog.ui.ZippyEvent(
  170. goog.ui.AnimatedZippy.Events.TOGGLE_ANIMATION_BEGIN, this, expanding));
  171. };
  172. /**
  173. * Called once the expand/collapse animation has completed.
  174. *
  175. * @param {boolean} expanded Expanded/visibility state.
  176. * @private
  177. */
  178. goog.ui.AnimatedZippy.prototype.onAnimationCompleted_ = function(expanded) {
  179. // Fix wrong end position if the content has changed during the animation.
  180. if (expanded) {
  181. this.getContentElement().style.marginTop = '0';
  182. }
  183. goog.events.removeAll(/** @type {!goog.fx.Animation} */ (this.anim_));
  184. this.setExpandedInternal(expanded);
  185. this.anim_ = null;
  186. if (!expanded) {
  187. this.elWrapper_.style.display = 'none';
  188. }
  189. // Fire toggle event.
  190. this.dispatchEvent(
  191. new goog.ui.ZippyEvent(goog.ui.Zippy.Events.TOGGLE, this, expanded));
  192. this.dispatchEvent(new goog.ui.ZippyEvent(
  193. goog.ui.AnimatedZippy.Events.TOGGLE_ANIMATION_END, this, expanded));
  194. };