Project import generated by Copybara.

GitOrigin-RevId: 63746295f1a5ab5a619056791995793d65529e62
diff --git a/node_modules/mdl-ext/es/carousel/carousel.js b/node_modules/mdl-ext/es/carousel/carousel.js
new file mode 100644
index 0000000..51b9451
--- /dev/null
+++ b/node_modules/mdl-ext/es/carousel/carousel.js
@@ -0,0 +1,718 @@
+'use strict';
+
+var _isInteger = require('babel-runtime/core-js/number/is-integer');
+
+var _isInteger2 = _interopRequireDefault(_isInteger);
+
+var _toConsumableArray2 = require('babel-runtime/helpers/toConsumableArray');
+
+var _toConsumableArray3 = _interopRequireDefault(_toConsumableArray2);
+
+var _assign = require('babel-runtime/core-js/object/assign');
+
+var _assign2 = _interopRequireDefault(_assign);
+
+var _intervalFunction = require('../utils/interval-function');
+
+var _intervalFunction2 = _interopRequireDefault(_intervalFunction);
+
+var _easing = require('../utils/easing');
+
+var _jsonUtils = require('../utils/json-utils');
+
+var _constants = require('../utils/constants');
+
+function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
+
+/**
+ * @license
+ * Copyright 2016 Leif Olsen. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * This code is built with Google Material Design Lite,
+ * which is Licensed under the Apache License, Version 2.0
+ */
+
+/**
+ * Image carousel
+ */
+
+var MDL_RIPPLE_CONTAINER = 'mdlext-carousel__slide__ripple-container';
+
+(function () {
+  'use strict';
+
+  //const CAROUSEL = 'mdlext-carousel';
+
+  var SLIDE = 'mdlext-carousel__slide';
+  var ROLE = 'list';
+  var SLIDE_ROLE = 'listitem';
+
+  /**
+   * @constructor
+   * @param {Element} element The element that will be upgraded.
+   */
+  var MaterialExtCarousel = function MaterialExtCarousel(element) {
+    // Stores the element.
+    this.element_ = element;
+
+    // Default config
+    this.config_ = {
+      interactive: true,
+      autostart: false,
+      type: 'slide',
+      interval: 1000,
+      animationLoop: (0, _intervalFunction2.default)(1000)
+    };
+
+    this.scrollAnimation_ = (0, _intervalFunction2.default)(33);
+
+    // Initialize instance.
+    this.init();
+  };
+
+  window['MaterialExtCarousel'] = MaterialExtCarousel;
+
+  /**
+   * Start slideshow animation
+   * @private
+   */
+  MaterialExtCarousel.prototype.startSlideShow_ = function () {
+    var _this = this;
+
+    var nextSlide = function nextSlide() {
+      var slide = _this.element_.querySelector('.' + SLIDE + '[aria-selected]');
+      if (slide) {
+        slide.removeAttribute('aria-selected');
+        slide = slide.nextElementSibling;
+      }
+      if (!slide) {
+        slide = _this.element_.querySelector('.' + SLIDE + ':first-child');
+        _this.animateScroll_(0);
+      }
+      if (slide) {
+        _this.moveSlideIntoViewport_(slide);
+        slide.setAttribute('aria-selected', '');
+        _this.emitSelectEvent_('next', null, slide);
+        return true;
+      }
+      return false;
+    };
+
+    var nextScroll = function nextScroll(direction) {
+      var nextDirection = direction;
+
+      if ('next' === direction && _this.element_.scrollLeft === _this.element_.scrollWidth - _this.element_.clientWidth) {
+        nextDirection = 'prev';
+      } else if (_this.element_.scrollLeft === 0) {
+        nextDirection = 'next';
+      }
+      var x = 'next' === nextDirection ? Math.min(_this.element_.scrollLeft + _this.element_.clientWidth, _this.element_.scrollWidth - _this.element_.clientWidth) : Math.max(_this.element_.scrollLeft - _this.element_.clientWidth, 0);
+
+      _this.animateScroll_(x, 1000);
+      return nextDirection;
+    };
+
+    if (!this.config_.animationLoop.started) {
+      (function () {
+        _this.config_.animationLoop.interval = _this.config_.interval;
+        var direction = 'next';
+
+        if ('scroll' === _this.config_.type) {
+          _this.config_.animationLoop.start(function () {
+            direction = nextScroll(direction);
+            return true; // It runs until cancelSlideShow_ is triggered
+          });
+        } else {
+          nextSlide();
+          _this.config_.animationLoop.start(function () {
+            return nextSlide(); // It runs until cancelSlideShow_ is triggered
+          });
+        }
+      })();
+    }
+
+    // TODO: Pause animation when carousel is not in browser viewport or user changes tab
+  };
+
+  /**
+   * Cancel slideshow if running. Emmits a 'pause' event
+   * @private
+   */
+  MaterialExtCarousel.prototype.cancelSlideShow_ = function () {
+    if (this.config_.animationLoop.started) {
+      this.config_.animationLoop.stop();
+      this.emitSelectEvent_('pause', _constants.VK_ESC, this.element_.querySelector('.' + SLIDE + '[aria-selected]'));
+    }
+  };
+
+  /**
+   * Animate scroll
+   * @param newPosition
+   * @param newDuration
+   * @param completedCallback
+   * @private
+   */
+  MaterialExtCarousel.prototype.animateScroll_ = function (newPosition, newDuration, completedCallback) {
+    var _this2 = this;
+
+    var start = this.element_.scrollLeft;
+    var distance = newPosition - start;
+
+    if (distance !== 0) {
+      (function () {
+        var duration = Math.max(Math.min(Math.abs(distance), newDuration || 400), 100); // duration is between 100 and newDuration||400ms||distance
+        var t = 0;
+        _this2.scrollAnimation_.stop();
+        _this2.scrollAnimation_.start(function (timeElapsed) {
+          t += timeElapsed;
+          if (t < duration) {
+            _this2.element_.scrollLeft = (0, _easing.inOutQuintic)(t, start, distance, duration);
+            return true;
+          } else {
+            _this2.element_.scrollLeft = newPosition;
+            if (completedCallback) {
+              completedCallback();
+            }
+            return false;
+          }
+        });
+      })();
+    } else {
+      if (completedCallback) {
+        completedCallback();
+      }
+    }
+  };
+
+  /**
+   * Execute commend
+   * @param event
+   * @private
+   */
+  MaterialExtCarousel.prototype.command_ = function (event) {
+    var _this3 = this;
+
+    var x = 0;
+    var slide = null;
+    var a = event.detail.action.toLowerCase();
+
+    // Cancel slideshow if running
+    this.cancelSlideShow_();
+
+    switch (a) {
+      case 'first':
+        slide = this.element_.querySelector('.' + SLIDE + ':first-child');
+        break;
+
+      case 'last':
+        x = this.element_.scrollWidth - this.element_.clientWidth;
+        slide = this.element_.querySelector('.' + SLIDE + ':last-child');
+        break;
+
+      case 'scroll-prev':
+        x = Math.max(this.element_.scrollLeft - this.element_.clientWidth, 0);
+        break;
+
+      case 'scroll-next':
+        x = Math.min(this.element_.scrollLeft + this.element_.clientWidth, this.element_.scrollWidth - this.element_.clientWidth);
+        break;
+
+      case 'next':
+      case 'prev':
+        slide = this.element_.querySelector('.' + SLIDE + '[aria-selected]');
+        if (slide) {
+          slide = a === 'next' ? slide.nextElementSibling : slide.previousElementSibling;
+          this.setAriaSelected_(slide);
+          this.emitSelectEvent_(a, null, slide);
+        }
+        return;
+
+      case 'play':
+        (0, _assign2.default)(this.config_, event.detail);
+        this.startSlideShow_();
+        return;
+
+      case 'pause':
+        return;
+
+      default:
+        return;
+    }
+
+    this.animateScroll_(x, undefined, function () {
+      if ('scroll-next' === a || 'scroll-prev' === a) {
+        var slides = _this3.getSlidesInViewport_();
+        if (slides.length > 0) {
+          slide = 'scroll-next' === a ? slides[0] : slides[slides.length - 1];
+        }
+      }
+      _this3.setAriaSelected_(slide);
+      _this3.emitSelectEvent_(a, null, slide);
+    });
+  };
+
+  /**
+   * Handles custom command event, 'scroll-prev', 'scroll-next', 'first', 'last', next, prev, play, pause
+   * @param event. A custom event
+   * @private
+   */
+  MaterialExtCarousel.prototype.commandHandler_ = function (event) {
+    event.preventDefault();
+    event.stopPropagation();
+    if (event.detail && event.detail.action) {
+      this.command_(event);
+    }
+  };
+
+  /**
+   * Handle keypress
+   * @param event
+   * @private
+   */
+  MaterialExtCarousel.prototype.keyDownHandler_ = function (event) {
+
+    if (event && event.target && event.target !== this.element_) {
+
+      var action = 'first';
+
+      if (event.keyCode === _constants.VK_HOME || event.keyCode === _constants.VK_END || event.keyCode === _constants.VK_PAGE_UP || event.keyCode === _constants.VK_PAGE_DOWN) {
+
+        event.preventDefault();
+        if (event.keyCode === _constants.VK_END) {
+          action = 'last';
+        } else if (event.keyCode === _constants.VK_PAGE_UP) {
+          action = 'scroll-prev';
+        } else if (event.keyCode === _constants.VK_PAGE_DOWN) {
+          action = 'scroll-next';
+        }
+
+        var cmd = new CustomEvent('select', {
+          detail: {
+            action: action
+          }
+        });
+        this.command_(cmd);
+      } else if (event.keyCode === _constants.VK_TAB || event.keyCode === _constants.VK_ENTER || event.keyCode === _constants.VK_SPACE || event.keyCode === _constants.VK_ARROW_UP || event.keyCode === _constants.VK_ARROW_LEFT || event.keyCode === _constants.VK_ARROW_DOWN || event.keyCode === _constants.VK_ARROW_RIGHT) {
+
+        var slide = getSlide_(event.target);
+
+        if (!slide) {
+          return;
+        }
+
+        // Cancel slideshow if running
+        this.cancelSlideShow_();
+
+        switch (event.keyCode) {
+          case _constants.VK_ARROW_UP:
+          case _constants.VK_ARROW_LEFT:
+            action = 'prev';
+            slide = slide.previousElementSibling;
+            break;
+
+          case _constants.VK_ARROW_DOWN:
+          case _constants.VK_ARROW_RIGHT:
+            action = 'next';
+            slide = slide.nextElementSibling;
+            break;
+
+          case _constants.VK_TAB:
+            if (event.shiftKey) {
+              action = 'prev';
+              slide = slide.previousElementSibling;
+            } else {
+              action = 'next';
+              slide = slide.nextElementSibling;
+            }
+            break;
+
+          case _constants.VK_SPACE:
+          case _constants.VK_ENTER:
+            action = 'select';
+            break;
+        }
+
+        if (slide) {
+          event.preventDefault();
+          setFocus_(slide);
+          this.emitSelectEvent_(action, event.keyCode, slide);
+        }
+      }
+    }
+  };
+
+  /**
+   * Handle dragging
+   * @param event
+   * @private
+   */
+  MaterialExtCarousel.prototype.dragHandler_ = function (event) {
+    var _this4 = this;
+
+    event.preventDefault();
+
+    // Cancel slideshow if running
+    this.cancelSlideShow_();
+
+    var updating = false;
+    var rAFDragId = 0;
+
+    var startX = event.clientX || (event.touches !== undefined ? event.touches[0].clientX : 0);
+    var prevX = startX;
+    var targetElement = event.target;
+
+    var update = function update(e) {
+      var currentX = e.clientX || (e.touches !== undefined ? e.touches[0].clientX : 0);
+      var dx = prevX - currentX;
+
+      if (dx < 0) {
+        _this4.element_.scrollLeft = Math.max(_this4.element_.scrollLeft + dx, 0);
+      } else if (dx > 0) {
+        _this4.element_.scrollLeft = Math.min(_this4.element_.scrollLeft + dx, _this4.element_.scrollWidth - _this4.element_.clientWidth);
+      }
+
+      prevX = currentX;
+      updating = false;
+    };
+
+    // drag handler
+    var drag = function drag(e) {
+      e.preventDefault();
+
+      if (!updating) {
+        rAFDragId = window.requestAnimationFrame(function () {
+          return update(e);
+        });
+        updating = true;
+      }
+    };
+
+    // end drag handler
+    var endDrag = function endDrag(e) {
+      e.preventDefault();
+
+      _this4.element_.removeEventListener('mousemove', drag);
+      _this4.element_.removeEventListener('touchmove', drag);
+      window.removeEventListener('mouseup', endDrag);
+      window.removeEventListener('touchend', endDrag);
+
+      // cancel any existing drag rAF, see: http://www.html5rocks.com/en/tutorials/speed/animations/
+      window.cancelAnimationFrame(rAFDragId);
+
+      var slide = getSlide_(targetElement);
+      setFocus_(slide);
+      _this4.emitSelectEvent_('click', null, slide);
+    };
+
+    this.element_.addEventListener('mousemove', drag);
+    this.element_.addEventListener('touchmove', drag);
+    window.addEventListener('mouseup', endDrag);
+    window.addEventListener('touchend', endDrag);
+  };
+
+  /**
+   * Handle click
+   * @param event
+   * @private
+   */
+  MaterialExtCarousel.prototype.clickHandler_ = function (event) {
+    // Click is handled by drag
+    event.preventDefault();
+  };
+
+  /**
+   * Handle focus
+   * @param event
+   * @private
+   */
+  MaterialExtCarousel.prototype.focusHandler_ = function (event) {
+    var slide = getSlide_(event.target);
+    if (slide) {
+      // The last focused/selected slide has 'aria-selected', even if focus is lost
+      this.setAriaSelected_(slide);
+      slide.classList.add(_constants.IS_FOCUSED);
+    }
+  };
+
+  /**
+   * Handle blur
+   * @param event
+   * @private
+   */
+  MaterialExtCarousel.prototype.blurHandler_ = function (event) {
+    var slide = getSlide_(event.target);
+    if (slide) {
+      slide.classList.remove(_constants.IS_FOCUSED);
+    }
+  };
+
+  /**
+   * Emits a custeom 'select' event
+   * @param command
+   * @param keyCode
+   * @param slide
+   * @private
+   */
+  MaterialExtCarousel.prototype.emitSelectEvent_ = function (command, keyCode, slide) {
+
+    if (slide) {
+      this.moveSlideIntoViewport_(slide);
+
+      var evt = new CustomEvent('select', {
+        bubbles: true,
+        cancelable: true,
+        detail: {
+          command: command,
+          keyCode: keyCode,
+          source: slide
+        }
+      });
+      this.element_.dispatchEvent(evt);
+    }
+  };
+
+  /**
+   * Get the first visible slide in component viewport
+   * @private
+   */
+  MaterialExtCarousel.prototype.getSlidesInViewport_ = function () {
+    var carouselRect = this.element_.getBoundingClientRect();
+
+    var slidesInViewport = [].concat((0, _toConsumableArray3.default)(this.element_.querySelectorAll('.' + SLIDE))).filter(function (slide) {
+      var slideRect = slide.getBoundingClientRect();
+      return slideRect.left >= carouselRect.left && slideRect.right <= carouselRect.right;
+    });
+    return slidesInViewport;
+  };
+
+  /**
+   * Move slide into component viewport - if needed
+   * @param slide
+   * @private
+   */
+  MaterialExtCarousel.prototype.moveSlideIntoViewport_ = function (slide) {
+    var carouselRect = this.element_.getBoundingClientRect();
+    var slideRect = slide.getBoundingClientRect();
+
+    if (slideRect.left < carouselRect.left) {
+      var x = this.element_.scrollLeft - (carouselRect.left - slideRect.left);
+      this.animateScroll_(x);
+    } else if (slideRect.right > carouselRect.right) {
+      var _x = this.element_.scrollLeft - (carouselRect.right - slideRect.right);
+      this.animateScroll_(_x);
+    }
+  };
+
+  /**
+   * Removes 'aria-selected' from all slides in carousel
+   * @private
+   */
+  MaterialExtCarousel.prototype.setAriaSelected_ = function (slide) {
+    if (slide) {
+      [].concat((0, _toConsumableArray3.default)(this.element_.querySelectorAll('.' + SLIDE + '[aria-selected]'))).forEach(function (slide) {
+        return slide.removeAttribute('aria-selected');
+      });
+      slide.setAttribute('aria-selected', '');
+    }
+  };
+
+  /**
+   * Removes event listeners
+   * @private
+   */
+  MaterialExtCarousel.prototype.removeListeners_ = function () {
+    this.element_.removeEventListener('focus', this.focusHandler_);
+    this.element_.removeEventListener('blur', this.blurHandler_);
+    this.element_.removeEventListener('keydown', this.keyDownHandler_);
+    this.element_.removeEventListener('mousedown', this.dragHandler_);
+    this.element_.removeEventListener('touchstart', this.dragHandler_);
+    this.element_.removeEventListener('click', this.clickHandler_, false);
+    this.element_.removeEventListener('command', this.commandHandler_);
+    this.element_.removeEventListener('mdl-componentdowngraded', this.mdlDowngrade_);
+  };
+
+  // Helpers
+  var getSlide_ = function getSlide_(element) {
+    return element.closest('.' + SLIDE);
+  };
+
+  var setFocus_ = function setFocus_(slide) {
+    if (slide) {
+      slide.focus();
+    }
+  };
+
+  var addRipple_ = function addRipple_(slide) {
+    if (!slide.querySelector('.' + MDL_RIPPLE_CONTAINER)) {
+      var rippleContainer = document.createElement('span');
+      rippleContainer.classList.add(MDL_RIPPLE_CONTAINER);
+      rippleContainer.classList.add(_constants.MDL_RIPPLE_EFFECT);
+      var ripple = document.createElement('span');
+      ripple.classList.add(_constants.MDL_RIPPLE);
+      rippleContainer.appendChild(ripple);
+
+      var img = slide.querySelector('img');
+      if (img) {
+        // rippleContainer blocks image title
+        rippleContainer.title = img.title;
+      }
+      slide.appendChild(rippleContainer);
+      componentHandler.upgradeElement(rippleContainer, _constants.MDL_RIPPLE_COMPONENT);
+    }
+  };
+  // End helpers
+
+
+  // Public methods.
+
+  /**
+   * Cancel animation - if running.
+   *
+   * @public
+   */
+  MaterialExtCarousel.prototype.stopAnimation = function () {
+    this.config_.animationLoop.stop();
+  };
+  MaterialExtCarousel.prototype['stopAnimation'] = MaterialExtCarousel.prototype.stopAnimation;
+
+  /**
+   * Upgrade slides
+   * Use if more list elements are added later (dynamically)
+   *
+   * @public
+   */
+  MaterialExtCarousel.prototype.upgradeSlides = function () {
+    var _this5 = this;
+
+    var hasRippleEffect = this.element_.classList.contains(_constants.MDL_RIPPLE_EFFECT);
+
+    [].concat((0, _toConsumableArray3.default)(this.element_.querySelectorAll('.' + SLIDE))).forEach(function (slide) {
+
+      slide.setAttribute('role', SLIDE_ROLE);
+
+      if (_this5.config_.interactive) {
+        if (!slide.getAttribute('tabindex')) {
+          slide.setAttribute('tabindex', '0');
+        }
+        if (hasRippleEffect) {
+          addRipple_(slide);
+        }
+      } else {
+        slide.setAttribute('tabindex', '-1');
+      }
+    });
+  };
+  MaterialExtCarousel.prototype['upgradeSlides'] = MaterialExtCarousel.prototype.upgradeSlides;
+
+  /**
+   * Get config object
+   *
+   * @public
+   */
+  MaterialExtCarousel.prototype.getConfig = function () {
+    return this.config_;
+  };
+  MaterialExtCarousel.prototype['getConfig'] = MaterialExtCarousel.prototype.getConfig;
+
+  /**
+   * Initialize component
+   */
+  MaterialExtCarousel.prototype.init = function () {
+
+    if (this.element_) {
+      // Config
+      if (this.element_.hasAttribute('data-config')) {
+        this.config_ = (0, _jsonUtils.jsonStringToObject)(this.element_.getAttribute('data-config'), this.config_);
+      }
+
+      // Wai-Aria
+      this.element_.setAttribute('role', ROLE);
+
+      // Prefer tabindex -1
+      if (!(0, _isInteger2.default)(this.element_.getAttribute('tabindex'))) {
+        this.element_.setAttribute('tabindex', -1);
+      }
+
+      // Remove listeners, just in case ...
+      this.removeListeners_();
+
+      if (this.config_.interactive) {
+
+        // Ripple
+        var hasRippleEffect = this.element_.classList.contains(_constants.MDL_RIPPLE_EFFECT);
+        if (hasRippleEffect) {
+          this.element_.classList.add(_constants.MDL_RIPPLE_EFFECT_IGNORE_EVENTS);
+        }
+
+        // Listen to focus/blur events
+        this.element_.addEventListener('focus', this.focusHandler_.bind(this), true);
+        this.element_.addEventListener('blur', this.blurHandler_.bind(this), true);
+
+        // Listen to keyboard events
+        this.element_.addEventListener('keydown', this.keyDownHandler_.bind(this), false);
+
+        // Listen to drag events
+        this.element_.addEventListener('mousedown', this.dragHandler_.bind(this), false);
+        this.element_.addEventListener('touchstart', this.dragHandler_.bind(this), false);
+
+        // Listen to click events
+        this.element_.addEventListener('click', this.clickHandler_.bind(this), false);
+      }
+
+      // Listen to custom 'command' event
+      this.element_.addEventListener('command', this.commandHandler_.bind(this), false);
+
+      // Listen to 'mdl-componentdowngraded' event
+      this.element_.addEventListener('mdl-componentdowngraded', this.mdlDowngrade_.bind(this));
+
+      // Slides collection
+      this.upgradeSlides();
+
+      // Set upgraded flag
+      this.element_.classList.add(_constants.IS_UPGRADED);
+
+      if (this.config_.autostart) {
+        // Start slideshow
+        this.startSlideShow_();
+      }
+    }
+  };
+
+  /*
+   * Downgrade component
+   * E.g remove listeners and clean up resources
+   */
+  MaterialExtCarousel.prototype.mdlDowngrade_ = function () {
+    'use strict';
+    //console.log('***** MaterialExtCarousel.mdlDowngrade_');
+
+    // Stop animation - if any
+
+    this.stopAnimation();
+
+    // Remove listeners
+    this.removeListeners_();
+  };
+
+  // The component registers itself. It can assume componentHandler is available
+  // in the global scope.
+  /* eslint no-undef: 0 */
+  componentHandler.register({
+    constructor: MaterialExtCarousel,
+    classAsString: 'MaterialExtCarousel',
+    cssClass: 'mdlext-js-carousel',
+    widget: true
+  });
+})();
\ No newline at end of file