Home | History | Annotate | Download | only in print_preview
      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 var animationEventTracker_ = new EventTracker();
      9 
     10 function addAnimation(code) {
     11   var name = 'anim' + animationCounter;
     12   animationCounter++;
     13   var rules = document.createTextNode(
     14       '@-webkit-keyframes ' + name + ' {' + code + '}');
     15   var el = document.createElement('style');
     16   el.type = 'text/css';
     17   el.appendChild(rules);
     18   el.setAttribute('id', name);
     19   document.body.appendChild(el);
     20 
     21   return name;
     22 }
     23 
     24 /**
     25  * Generates css code for fading in an element by animating the height.
     26  * @param {number} targetHeight The desired height in pixels after the animation
     27  *     ends.
     28  * @return {string} The css code for the fade in animation.
     29  */
     30 function getFadeInAnimationCode(targetHeight) {
     31   return '0% { opacity: 0; height: 0; } ' +
     32       '80% { opacity: 0.5; height: ' + (targetHeight + 4) + 'px; }' +
     33       '100% { opacity: 1; height: ' + targetHeight + 'px; }';
     34 }
     35 
     36 /**
     37  * Fades in an element. Used for both printing options and error messages
     38  * appearing underneath the textfields.
     39  * @param {HTMLElement} el The element to be faded in.
     40  * @param {boolean=} opt_justShow Whether {@code el} should be shown with no
     41  *     animation.
     42  */
     43 function fadeInElement(el, opt_justShow) {
     44   if (el.classList.contains('visible'))
     45     return;
     46   el.classList.remove('closing');
     47   el.hidden = false;
     48   el.setAttribute('aria-hidden', 'false');
     49   el.style.height = 'auto';
     50   var height = el.offsetHeight;
     51   if (opt_justShow) {
     52     el.style.height = '';
     53     el.style.opacity = '';
     54   } else {
     55     el.style.height = height + 'px';
     56     var animName = addAnimation(getFadeInAnimationCode(height));
     57     animationEventTracker_.add(
     58         el, 'webkitAnimationEnd', onFadeInAnimationEnd.bind(el), false);
     59     el.style.webkitAnimationName = animName;
     60   }
     61   el.classList.add('visible');
     62 }
     63 
     64 /**
     65  * Fades out an element. Used for both printing options and error messages
     66  * appearing underneath the textfields.
     67  * @param {HTMLElement} el The element to be faded out.
     68  */
     69 function fadeOutElement(el) {
     70   if (!el.classList.contains('visible'))
     71     return;
     72   fadeInAnimationCleanup(el);
     73   el.style.height = 'auto';
     74   var height = el.offsetHeight;
     75   el.style.height = height + 'px';
     76   el.offsetHeight;  // Should force an update of the computed style.
     77   animationEventTracker_.add(
     78       el, 'webkitTransitionEnd', onFadeOutTransitionEnd.bind(el), false);
     79   el.classList.add('closing');
     80   el.classList.remove('visible');
     81   el.setAttribute('aria-hidden', 'true');
     82 }
     83 
     84 /**
     85  * Executes when a fade out animation ends.
     86  * @param {WebkitTransitionEvent} event The event that triggered this listener.
     87  * @this {HTMLElement} The element where the transition occurred.
     88  */
     89 function onFadeOutTransitionEnd(event) {
     90   if (event.propertyName != 'height')
     91     return;
     92   animationEventTracker_.remove(this, 'webkitTransitionEnd');
     93   this.hidden = true;
     94 }
     95 
     96 /**
     97  * Executes when a fade in animation ends.
     98  * @param {WebkitAnimationEvent} event The event that triggered this listener.
     99  * @this {HTMLElement} The element where the transition occurred.
    100  */
    101 function onFadeInAnimationEnd(event) {
    102   this.style.height = '';
    103   fadeInAnimationCleanup(this);
    104 }
    105 
    106 /**
    107  * Removes the <style> element corresponding to |animationName| from the DOM.
    108  * @param {HTMLElement} element The animated element.
    109  */
    110 function fadeInAnimationCleanup(element) {
    111   if (element.style.webkitAnimationName) {
    112     var animEl = document.getElementById(element.style.webkitAnimationName);
    113     if (animEl)
    114       animEl.parentNode.removeChild(animEl);
    115     element.style.webkitAnimationName = '';
    116     animationEventTracker_.remove(element, 'webkitAnimationEnd');
    117   }
    118 }
    119 
    120 /**
    121  * Fades in a printing option existing under |el|.
    122  * @param {HTMLElement} el The element to hide.
    123  * @param {boolean=} opt_justShow Whether {@code el} should be hidden with no
    124  *     animation.
    125  */
    126 function fadeInOption(el, opt_justShow) {
    127   if (el.classList.contains('visible'))
    128     return;
    129   // To make the option visible during the first fade in.
    130   el.hidden = false;
    131 
    132   var leftColumn = el.querySelector('.left-column');
    133   wrapContentsInDiv(leftColumn, ['invisible']);
    134   var rightColumn = el.querySelector('.right-column');
    135   wrapContentsInDiv(rightColumn, ['invisible']);
    136 
    137   var toAnimate = el.querySelectorAll('.collapsible');
    138   for (var i = 0; i < toAnimate.length; i++)
    139     fadeInElement(toAnimate[i], opt_justShow);
    140   el.classList.add('visible');
    141 }
    142 
    143 /**
    144  * Fades out a printing option existing under |el|.
    145  * @param {HTMLElement} el The element to hide.
    146  * @param {boolean=} opt_justHide Whether {@code el} should be hidden with no
    147  *     animation.
    148  */
    149 function fadeOutOption(el, opt_justHide) {
    150   if (!el.classList.contains('visible'))
    151     return;
    152 
    153   var leftColumn = el.querySelector('.left-column');
    154   wrapContentsInDiv(leftColumn, ['visible']);
    155   var rightColumn = el.querySelector('.right-column');
    156   wrapContentsInDiv(rightColumn, ['visible']);
    157 
    158   var toAnimate = el.querySelectorAll('.collapsible');
    159   for (var i = 0; i < toAnimate.length; i++) {
    160     if (opt_justHide) {
    161       toAnimate[i].hidden = true;
    162       toAnimate[i].classList.add('closing');
    163       toAnimate[i].classList.remove('visible');
    164     } else {
    165       fadeOutElement(toAnimate[i]);
    166     }
    167   }
    168   el.classList.remove('visible');
    169 }
    170 
    171 /**
    172  * Wraps the contents of |el| in a div element and attaches css classes
    173  * |classes| in the new div, only if has not been already done. It is necessary
    174  * for animating the height of table cells.
    175  * @param {HTMLElement} el The element to be processed.
    176  * @param {array} classes The css classes to add.
    177  */
    178 function wrapContentsInDiv(el, classes) {
    179   var div = el.querySelector('div');
    180   if (!div || !div.classList.contains('collapsible')) {
    181     div = document.createElement('div');
    182     while (el.childNodes.length > 0)
    183       div.appendChild(el.firstChild);
    184     el.appendChild(div);
    185   }
    186 
    187   div.className = '';
    188   div.classList.add('collapsible');
    189   for (var i = 0; i < classes.length; i++)
    190     div.classList.add(classes[i]);
    191 }
    192