Project import generated by Copybara.
GitOrigin-RevId: 63746295f1a5ab5a619056791995793d65529e62
diff --git a/node_modules/mdl-ext/src/sticky-header/sticky-header.js b/node_modules/mdl-ext/src/sticky-header/sticky-header.js
new file mode 100644
index 0000000..9ed2e13
--- /dev/null
+++ b/node_modules/mdl-ext/src/sticky-header/sticky-header.js
@@ -0,0 +1,251 @@
+/**
+ * @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
+ */
+
+/**
+ * A sticky header makes site navigation easily accessible anywhere on the page and saves content space at the same.
+ * The header should auto-hide, i.e. hiding the header automatically when a user starts scrolling down the page and
+ * bringing the header back when a user might need it: they reach the bottom of the page or start scrolling up.
+ */
+
+import fullThrottle from '../utils/full-throttle';
+import { jsonStringToObject } from '../utils/json-utils';
+import {
+ IS_UPGRADED
+} from '../utils/constants';
+
+
+(function() {
+ 'use strict';
+ const MDL_LAYOUT_CONTENT = 'mdl-layout__content';
+ const IS_SCROLL_CLASS = 'mdlext-is-scroll';
+
+
+ /**
+ * @constructor
+ * @param {Element} element The element that will be upgraded.
+ */
+ const MaterialExtStickyHeader = function MaterialExtStickyHeader(element) {
+ // Stores the element.
+ this.header_ = element;
+
+ // Heder listens to scroll events from content
+ this.content_ = null;
+ this.lastScrollTop_ = 0;
+
+ // Default config
+ this.config_ = {
+ visibleAtScrollEnd: false
+ };
+
+ this.mutationObserver_ = null;
+
+ this.drawing_ = false;
+
+ // Initialize instance.
+ this.init();
+ };
+
+ window['MaterialExtStickyHeader'] = MaterialExtStickyHeader;
+
+
+ /**
+ * Update header width
+ * @private
+ */
+ MaterialExtStickyHeader.prototype.recalcWidth_ = function() {
+ this.header_.style.width = `${this.content_.clientWidth}px`;
+ };
+
+ const throttleResize = fullThrottle(self => self.recalcWidth_() );
+
+ /**
+ * Adjust header width when window resizes or oreientation changes
+ * @param event
+ * @private
+ */
+ MaterialExtStickyHeader.prototype.resizeHandler_ = function( /* event */ ) {
+ throttleResize(this);
+ };
+
+
+ /**
+ * Update header position
+ * @private
+ */
+ MaterialExtStickyHeader.prototype.reposition_ = function() {
+
+ const currentContentScrollTop = this.content_.scrollTop;
+ const scrollDiff = this.lastScrollTop_ - currentContentScrollTop;
+
+ if(currentContentScrollTop <= 0) {
+ // Scrolled to the top. Header sticks to the top
+ this.header_.style.top = '0';
+ this.header_.classList.remove(IS_SCROLL_CLASS);
+ }
+ else if(scrollDiff > 0) {
+
+ if(scrollDiff >= this.header_.offsetHeight) {
+
+ // Scrolled up. Header slides in
+ const headerTop = (parseInt( window.getComputedStyle( this.header_ ).getPropertyValue( 'top' ) ) || 0);
+ if(headerTop != 0) {
+ this.header_.style.top = '0';
+ this.header_.classList.add(IS_SCROLL_CLASS);
+ }
+ this.lastScrollTop_ = currentContentScrollTop;
+ }
+ return;
+ }
+ else if(scrollDiff < 0) {
+ // Scrolled down
+ this.header_.classList.add(IS_SCROLL_CLASS);
+ let headerTop = (parseInt( window.getComputedStyle( this.header_ ).getPropertyValue( 'top' ) ) || 0);
+
+ if (this.content_.scrollHeight - this.content_.scrollTop <= this.content_.offsetHeight) {
+ // Bottom of content
+ if(headerTop != 0) {
+ this.header_.style.top = this.config_.visibleAtScrollEnd ? '0' : `-${this.header_.offsetHeight}px`;
+ }
+ }
+ else {
+ headerTop += scrollDiff;
+ const offsetHeight = this.header_.offsetHeight;
+ this.header_.style.top = `${( Math.abs( headerTop ) > offsetHeight ? -offsetHeight : headerTop )}px`;
+ }
+ }
+
+ this.lastScrollTop_ = currentContentScrollTop;
+ };
+
+
+ const throttleScroll = fullThrottle((self) => self.reposition_());
+
+ /**
+ * Scroll header when content scrolls
+ * @param event
+ * @private
+ */
+ MaterialExtStickyHeader.prototype.scrollHandler_ = function( /* event */ ) {
+ throttleScroll(this);
+ };
+
+ /**
+ * Init header position
+ * @private
+ */
+ MaterialExtStickyHeader.prototype.updatePosition_ = function( /* event */ ) {
+ this.recalcWidth_();
+ this.reposition_();
+ };
+
+ /**
+ * Add mutation observer
+ * @private
+ */
+ MaterialExtStickyHeader.prototype.addMutationObserver_ = function() {
+
+ // jsdom does not support MutationObserver - so this is not testable
+ /* istanbul ignore next */
+ this.mutationObserver_ = new MutationObserver( ( /*mutations*/ ) => {
+ // Adjust header width if content changes (e.g. in a SPA)
+ this.updatePosition_();
+ });
+
+ this.mutationObserver_.observe( this.content_, {
+ attributes: false,
+ childList: true,
+ characterData: false,
+ subtree: true
+ });
+ };
+
+ /**
+ * Removes event listeners
+ * @private
+ */
+ MaterialExtStickyHeader.prototype.removeListeners_ = function() {
+
+ window.removeEventListener('resize', this.resizeHandler_);
+ window.removeEventListener('orientationchange', this.resizeHandler_);
+
+ if(this.content_) {
+ this.content_.removeEventListener('scroll', this.scrollHandler_);
+ }
+
+ if(this.mutationObserver_) {
+ this.mutationObserver_.disconnect();
+ this.mutationObserver_ = null;
+ }
+ };
+
+ /**
+ * Initialize component
+ */
+ MaterialExtStickyHeader.prototype.init = function() {
+
+ if (this.header_) {
+
+ this.removeListeners_();
+
+ if(this.header_.hasAttribute('data-config')) {
+ this.config_ = jsonStringToObject(this.header_.getAttribute('data-config'));
+ }
+
+ this.content_ = this.header_.parentNode.querySelector(`.${MDL_LAYOUT_CONTENT}`) || null;
+
+ if(this.content_) {
+ this.content_.style.paddingTop = `${this.header_.offsetHeight}px`; // Make room for sticky header
+ this.lastScrollTop_ = this.content_.scrollTop;
+
+ this.content_.addEventListener('scroll', this.scrollHandler_.bind(this));
+ window.addEventListener('resize', this.resizeHandler_.bind(this));
+ window.addEventListener('orientationchange', this.resizeHandler_.bind(this));
+
+ this.addMutationObserver_();
+ this.updatePosition_();
+
+ // Set upgraded flag
+ this.header_.classList.add(IS_UPGRADED);
+ }
+ }
+ };
+
+ /*
+ * Downgrade component
+ * E.g remove listeners and clean up resources
+ *
+ * Nothing to clean
+ *
+ MaterialExtStickyHeader.prototype.mdlDowngrade_ = function() {
+ 'use strict';
+ console.log('***** MaterialExtStickyHeader.prototype.mdlDowngrade_');
+ };
+ */
+
+
+ // The component registers itself. It can assume componentHandler is available
+ // in the global scope.
+ /* eslint no-undef: 0 */
+ componentHandler.register({
+ constructor: MaterialExtStickyHeader,
+ classAsString: 'MaterialExtStickyHeader',
+ cssClass: 'mdlext-js-sticky-header'
+ });
+})();