1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 // Counter used to give webkit animations unique names. 6 var animationCounter = 0; 7 8 function addAnimation(code) { 9 var name = 'anim' + animationCounter; 10 animationCounter++; 11 var rules = document.createTextNode( 12 '@-webkit-keyframes ' + name + ' {' + code + '}'); 13 var el = document.createElement('style'); 14 el.type = 'text/css'; 15 el.appendChild(rules); 16 el.setAttribute('id', name); 17 document.body.appendChild(el); 18 19 return name; 20 } 21 22 /** 23 * Generates css code for fading in an element by animating the height. 24 * @param {number} targetHeight The desired height in pixels after the animation 25 * ends. 26 * @return {string} The css code for the fade in animation. 27 */ 28 function getFadeInAnimationCode(targetHeight) { 29 return '0% { opacity: 0; height: 0; } ' + 30 '80% { height: ' + (targetHeight + 4) + 'px; }' + 31 '100% { opacity: 1; height: ' + targetHeight + 'px; }'; 32 } 33 34 /** 35 * Fades in an element. Used for both printing options and error messages 36 * appearing underneath the textfields. 37 * @param {HTMLElement} el The element to be faded in. 38 */ 39 function fadeInElement(el) { 40 if (el.classList.contains('visible')) 41 return; 42 el.classList.remove('closing'); 43 el.hidden = false; 44 el.style.height = 'auto'; 45 var height = el.offsetHeight; 46 el.style.height = height + 'px'; 47 var animName = addAnimation(getFadeInAnimationCode(height)); 48 var eventTracker = new EventTracker(); 49 eventTracker.add(el, 'webkitAnimationEnd', 50 onFadeInAnimationEnd.bind(el, eventTracker), 51 false); 52 el.style.webkitAnimationName = animName; 53 el.classList.add('visible'); 54 } 55 56 /** 57 * Fades out an element. Used for both printing options and error messages 58 * appearing underneath the textfields. 59 * @param {HTMLElement} el The element to be faded out. 60 */ 61 function fadeOutElement(el) { 62 if (!el.classList.contains('visible')) 63 return; 64 el.style.height = 'auto'; 65 var height = el.offsetHeight; 66 el.style.height = height + 'px'; 67 var animName = addAnimation(''); 68 var eventTracker = new EventTracker(); 69 eventTracker.add(el, 'webkitTransitionEnd', 70 onFadeOutTransitionEnd.bind(el, animName, eventTracker), 71 false); 72 el.classList.add('closing'); 73 el.classList.remove('visible'); 74 } 75 76 /** 77 * Executes when a fade out animation ends. 78 * @param {string} animationName The name of the animation to be removed. 79 * @param {EventTracker} eventTracker The |EventTracker| object that was used 80 * for adding this listener. 81 * @param {WebkitTransitionEvent} event The event that triggered this listener. 82 * @this {HTMLElement} The element where the transition occurred. 83 */ 84 function onFadeOutTransitionEnd(animationName, eventTracker, event) { 85 if (event.propertyName != 'height') 86 return; 87 fadeInOutCleanup(animationName); 88 eventTracker.remove(this, 'webkitTransitionEnd'); 89 this.hidden = true; 90 } 91 92 /** 93 * Executes when a fade in animation ends. 94 * @param {EventTracker} eventTracker The |EventTracker| object that was used 95 * for adding this listener. 96 * @param {WebkitAnimationEvent} event The event that triggered this listener. 97 * @this {HTMLElement} The element where the transition occurred. 98 */ 99 function onFadeInAnimationEnd(eventTracker, event) { 100 this.style.height = ''; 101 this.style.webkitAnimationName = ''; 102 fadeInOutCleanup(event.animationName); 103 eventTracker.remove(this, 'webkitAnimationEnd'); 104 } 105 106 /** 107 * Removes the <style> element corrsponding to |animationName| from the DOM. 108 * @param {string} animationName The name of the animation to be removed. 109 */ 110 function fadeInOutCleanup(animationName) { 111 var animEl = document.getElementById(animationName); 112 if (animEl) 113 animEl.parentNode.removeChild(animEl); 114 } 115 116 /** 117 * Fades in a printing option existing under |el|. 118 * @param {HTMLElement} el The element to hide. 119 */ 120 function fadeInOption(el) { 121 if (el.classList.contains('visible')) 122 return; 123 124 wrapContentsInDiv(el.querySelector('h1'), ['invisible']); 125 var rightColumn = el.querySelector('.right-column'); 126 wrapContentsInDiv(rightColumn, ['invisible']); 127 128 var toAnimate = el.querySelectorAll('.collapsible'); 129 for (var i = 0; i < toAnimate.length; i++) 130 fadeInElement(toAnimate[i]); 131 el.classList.add('visible'); 132 } 133 134 /** 135 * Fades out a printing option existing under |el|. 136 * @param {HTMLElement} el The element to hide. 137 */ 138 function fadeOutOption(el) { 139 if (!el.classList.contains('visible')) 140 return; 141 142 wrapContentsInDiv(el.querySelector('h1'), ['visible']); 143 var rightColumn = el.querySelector('.right-column'); 144 wrapContentsInDiv(rightColumn, ['visible']); 145 146 var toAnimate = el.querySelectorAll('.collapsible'); 147 for (var i = 0; i < toAnimate.length; i++) 148 fadeOutElement(toAnimate[i]); 149 el.classList.remove('visible'); 150 } 151 152 /** 153 * Wraps the contents of |el| in a div element and attaches css classes 154 * |classes| in the new div, only if has not been already done. It is neccesary 155 * for animating the height of table cells. 156 * @param {HTMLElement} el The element to be processed. 157 * @param {array} classes The css classes to add. 158 */ 159 function wrapContentsInDiv(el, classes) { 160 var div = el.querySelector('div'); 161 if (!div || !div.classList.contains('collapsible')) { 162 div = document.createElement('div'); 163 while (el.childNodes.length > 0) 164 div.appendChild(el.firstChild); 165 el.appendChild(div); 166 } 167 168 div.className = ''; 169 div.classList.add('collapsible'); 170 for (var i = 0; i < classes.length; i++) 171 div.classList.add(classes[i]); 172 } 173