<!– @license Copyright © 2016 The Polymer Project Authors. All rights reserved. This code may only be used under the BSD style license found at polymer.github.io/LICENSE.txt The complete set of authors may be found at polymer.github.io/AUTHORS.txt The complete set of contributors may be found at polymer.github.io/CONTRIBUTORS.txt Code distributed by Google as part of the polymer project is also subject to an additional IP rights grant found at polymer.github.io/PATENTS.txt –>

<link rel=“import” href=“../../polymer/polymer.html”>

<script>

Polymer.AppLayout = Polymer.AppLayout || {};

Polymer.AppLayout._scrollEffects = Polymer.AppLayout._scrollEffects || {};

Polymer.AppLayout.scrollTimingFunction = function easeOutQuad(t, b, c, d) {
  t /= d;
  return -c * t*(t-2) + b;
};

/**
 * Registers a scroll effect to be used in elements that implement the
 * `Polymer.AppScrollEffectsBehavior` behavior.
 *
 * @param {string} effectName The effect name.
 * @param {Object} effectDef The effect definition.
 */
Polymer.AppLayout.registerEffect = function registerEffect(effectName, effectDef) {
  if (Polymer.AppLayout._scrollEffects[effectName] != null) {
    throw new Error('effect `'+ effectName + '` is already registered.');
  }
  Polymer.AppLayout._scrollEffects[effectName] = effectDef;
};

/**
 * Scrolls to a particular set of coordinates in a scroll target.
 * If the scroll target is not defined, then it would use the main document as the target.
 *
 * To scroll in a smooth fashion, you can set the option `behavior: 'smooth'`. e.g.
 *
 * ```js
 * Polymer.AppLayout.scroll({top: 0, behavior: 'smooth'});
 * ```
 *
 * To scroll in a silent mode, without notifying scroll changes to any app-layout elements,
 * you can set the option `behavior: 'silent'`. This is particularly useful we you are using
 * `app-header` and you desire to scroll to the top of a scrolling region without running
 * scroll effects. e.g.
 *
 * ```js
 * Polymer.AppLayout.scroll({top: 0, behavior: 'silent'});
 * ```
 *
 * @param {Object} options {top: Number, left: Number, behavior: String(smooth | silent)}
 */
Polymer.AppLayout.scroll = function scroll(options) {
  options = options || {};

  var docEl = document.documentElement;
  var target = options.target || docEl;
  var hasNativeScrollBehavior = 'scrollBehavior' in target.style && target.scroll;
  var scrollClassName = 'app-layout-silent-scroll';
  var scrollTop = options.top || 0;
  var scrollLeft = options.left || 0;
  var scrollTo = target === docEl ? window.scrollTo :
    function scrollTo(scrollLeft, scrollTop) {
      target.scrollLeft = scrollLeft;
      target.scrollTop = scrollTop;
    };

  if (options.behavior === 'smooth') {

    if (hasNativeScrollBehavior) {

      target.scroll(options);

    } else {

      var timingFn = Polymer.AppLayout.scrollTimingFunction;
      var startTime = Date.now();
      var currentScrollTop = target === docEl ? window.pageYOffset : target.scrollTop;
      var currentScrollLeft = target === docEl ? window.pageXOffset : target.scrollLeft;
      var deltaScrollTop = scrollTop - currentScrollTop;
      var deltaScrollLeft = scrollLeft - currentScrollLeft;
      var duration = 300;
      var updateFrame = (function updateFrame() {
        var now = Date.now();
        var elapsedTime = now - startTime;

        if (elapsedTime < duration) {
          scrollTo(timingFn(elapsedTime, currentScrollLeft, deltaScrollLeft, duration),
              timingFn(elapsedTime, currentScrollTop, deltaScrollTop, duration));
          requestAnimationFrame(updateFrame);
        } else {
          scrollTo(scrollLeft, scrollTop);
        }
      }).bind(this);

      updateFrame();
    }

  } else if (options.behavior === 'silent') {

    docEl.classList.add(scrollClassName);

    // Browsers keep the scroll momentum even if the bottom of the scrolling content
    // was reached. This means that calling scroll({top: 0, behavior: 'silent'}) when
    // the momentum is still going will result in more scroll events and thus scroll effects.
    // This seems to only apply when using document scrolling.
    // Therefore, when should we remove the class from the document element?

    clearInterval(Polymer.AppLayout._scrollTimer);

    Polymer.AppLayout._scrollTimer = setTimeout(function() {
      docEl.classList.remove(scrollClassName);
      Polymer.AppLayout._scrollTimer = null;
    }, 100);

    scrollTo(scrollLeft, scrollTop);

  } else {

    scrollTo(scrollLeft, scrollTop);

  }
};

</script>