liquid_feedback_frontend
view static/mdl/material.js @ 1361:324e01ab8ecf
Show sequential number as read only
| author | bsw | 
|---|---|
| date | Mon Aug 06 14:00:45 2018 +0200 (2018-08-06) | 
| parents | 32cc544d5a5b | 
| children | 
 line source
     1 ;(function() {
     2 "use strict";
     4 /**
     5  * @license
     6  * Copyright 2015 Google Inc. All Rights Reserved.
     7  *
     8  * Licensed under the Apache License, Version 2.0 (the "License");
     9  * you may not use this file except in compliance with the License.
    10  * You may obtain a copy of the License at
    11  *
    12  *      http://www.apache.org/licenses/LICENSE-2.0
    13  *
    14  * Unless required by applicable law or agreed to in writing, software
    15  * distributed under the License is distributed on an "AS IS" BASIS,
    16  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    17  * See the License for the specific language governing permissions and
    18  * limitations under the License.
    19  */
    21 /**
    22  * A component handler interface using the revealing module design pattern.
    23  * More details on this design pattern here:
    24  * https://github.com/jasonmayes/mdl-component-design-pattern
    25  *
    26  * @author Jason Mayes.
    27  */
    28 /* exported componentHandler */
    30 // Pre-defining the componentHandler interface, for closure documentation and
    31 // static verification.
    32 var componentHandler = {
    33   /**
    34    * Searches existing DOM for elements of our component type and upgrades them
    35    * if they have not already been upgraded.
    36    *
    37    * @param {string=} optJsClass the programatic name of the element class we
    38    * need to create a new instance of.
    39    * @param {string=} optCssClass the name of the CSS class elements of this
    40    * type will have.
    41    */
    42   upgradeDom: function(optJsClass, optCssClass) {},
    43   /**
    44    * Upgrades a specific element rather than all in the DOM.
    45    *
    46    * @param {!Element} element The element we wish to upgrade.
    47    * @param {string=} optJsClass Optional name of the class we want to upgrade
    48    * the element to.
    49    */
    50   upgradeElement: function(element, optJsClass) {},
    51   /**
    52    * Upgrades a specific list of elements rather than all in the DOM.
    53    *
    54    * @param {!Element|!Array<!Element>|!NodeList|!HTMLCollection} elements
    55    * The elements we wish to upgrade.
    56    */
    57   upgradeElements: function(elements) {},
    58   /**
    59    * Upgrades all registered components found in the current DOM. This is
    60    * automatically called on window load.
    61    */
    62   upgradeAllRegistered: function() {},
    63   /**
    64    * Allows user to be alerted to any upgrades that are performed for a given
    65    * component type
    66    *
    67    * @param {string} jsClass The class name of the MDL component we wish
    68    * to hook into for any upgrades performed.
    69    * @param {function(!HTMLElement)} callback The function to call upon an
    70    * upgrade. This function should expect 1 parameter - the HTMLElement which
    71    * got upgraded.
    72    */
    73   registerUpgradedCallback: function(jsClass, callback) {},
    74   /**
    75    * Registers a class for future use and attempts to upgrade existing DOM.
    76    *
    77    * @param {componentHandler.ComponentConfigPublic} config the registration configuration
    78    */
    79   register: function(config) {},
    80   /**
    81    * Downgrade either a given node, an array of nodes, or a NodeList.
    82    *
    83    * @param {!Node|!Array<!Node>|!NodeList} nodes
    84    */
    85   downgradeElements: function(nodes) {}
    86 };
    88 componentHandler = (function() {
    89   'use strict';
    91   /** @type {!Array<componentHandler.ComponentConfig>} */
    92   var registeredComponents_ = [];
    94   /** @type {!Array<componentHandler.Component>} */
    95   var createdComponents_ = [];
    97   var componentConfigProperty_ = 'mdlComponentConfigInternal_';
    99   /**
   100    * Searches registered components for a class we are interested in using.
   101    * Optionally replaces a match with passed object if specified.
   102    *
   103    * @param {string} name The name of a class we want to use.
   104    * @param {componentHandler.ComponentConfig=} optReplace Optional object to replace match with.
   105    * @return {!Object|boolean}
   106    * @private
   107    */
   108   function findRegisteredClass_(name, optReplace) {
   109     for (var i = 0; i < registeredComponents_.length; i++) {
   110       if (registeredComponents_[i].className === name) {
   111         if (typeof optReplace !== 'undefined') {
   112           registeredComponents_[i] = optReplace;
   113         }
   114         return registeredComponents_[i];
   115       }
   116     }
   117     return false;
   118   }
   120   /**
   121    * Returns an array of the classNames of the upgraded classes on the element.
   122    *
   123    * @param {!Element} element The element to fetch data from.
   124    * @return {!Array<string>}
   125    * @private
   126    */
   127   function getUpgradedListOfElement_(element) {
   128     var dataUpgraded = element.getAttribute('data-upgraded');
   129     // Use `['']` as default value to conform the `,name,name...` style.
   130     return dataUpgraded === null ? [''] : dataUpgraded.split(',');
   131   }
   133   /**
   134    * Returns true if the given element has already been upgraded for the given
   135    * class.
   136    *
   137    * @param {!Element} element The element we want to check.
   138    * @param {string} jsClass The class to check for.
   139    * @returns {boolean}
   140    * @private
   141    */
   142   function isElementUpgraded_(element, jsClass) {
   143     var upgradedList = getUpgradedListOfElement_(element);
   144     return upgradedList.indexOf(jsClass) !== -1;
   145   }
   147   /**
   148    * Searches existing DOM for elements of our component type and upgrades them
   149    * if they have not already been upgraded.
   150    *
   151    * @param {string=} optJsClass the programatic name of the element class we
   152    * need to create a new instance of.
   153    * @param {string=} optCssClass the name of the CSS class elements of this
   154    * type will have.
   155    */
   156   function upgradeDomInternal(optJsClass, optCssClass) {
   157     if (typeof optJsClass === 'undefined' &&
   158         typeof optCssClass === 'undefined') {
   159       for (var i = 0; i < registeredComponents_.length; i++) {
   160         upgradeDomInternal(registeredComponents_[i].className,
   161             registeredComponents_[i].cssClass);
   162       }
   163     } else {
   164       var jsClass = /** @type {string} */ (optJsClass);
   165       if (typeof optCssClass === 'undefined') {
   166         var registeredClass = findRegisteredClass_(jsClass);
   167         if (registeredClass) {
   168           optCssClass = registeredClass.cssClass;
   169         }
   170       }
   172       var elements = document.querySelectorAll('.' + optCssClass);
   173       for (var n = 0; n < elements.length; n++) {
   174         upgradeElementInternal(elements[n], jsClass);
   175       }
   176     }
   177   }
   179   /**
   180    * Upgrades a specific element rather than all in the DOM.
   181    *
   182    * @param {!Element} element The element we wish to upgrade.
   183    * @param {string=} optJsClass Optional name of the class we want to upgrade
   184    * the element to.
   185    */
   186   function upgradeElementInternal(element, optJsClass) {
   187     // Verify argument type.
   188     if (!(typeof element === 'object' && element instanceof Element)) {
   189       throw new Error('Invalid argument provided to upgrade MDL element.');
   190     }
   191     var upgradedList = getUpgradedListOfElement_(element);
   192     var classesToUpgrade = [];
   193     // If jsClass is not provided scan the registered components to find the
   194     // ones matching the element's CSS classList.
   195     if (!optJsClass) {
   196       var classList = element.classList;
   197       registeredComponents_.forEach(function(component) {
   198         // Match CSS & Not to be upgraded & Not upgraded.
   199         if (classList.contains(component.cssClass) &&
   200             classesToUpgrade.indexOf(component) === -1 &&
   201             !isElementUpgraded_(element, component.className)) {
   202           classesToUpgrade.push(component);
   203         }
   204       });
   205     } else if (!isElementUpgraded_(element, optJsClass)) {
   206       classesToUpgrade.push(findRegisteredClass_(optJsClass));
   207     }
   209     // Upgrade the element for each classes.
   210     for (var i = 0, n = classesToUpgrade.length, registeredClass; i < n; i++) {
   211       registeredClass = classesToUpgrade[i];
   212       if (registeredClass) {
   213         // Mark element as upgraded.
   214         upgradedList.push(registeredClass.className);
   215         element.setAttribute('data-upgraded', upgradedList.join(','));
   216         var instance = new registeredClass.classConstructor(element);
   217         instance[componentConfigProperty_] = registeredClass;
   218         createdComponents_.push(instance);
   219         // Call any callbacks the user has registered with this component type.
   220         for (var j = 0, m = registeredClass.callbacks.length; j < m; j++) {
   221           registeredClass.callbacks[j](element);
   222         }
   224         if (registeredClass.widget) {
   225           // Assign per element instance for control over API
   226           element[registeredClass.className] = instance;
   227         }
   228       } else {
   229         throw new Error(
   230           'Unable to find a registered component for the given class.');
   231       }
   233       var ev;
   234       if ('CustomEvent' in window && typeof window.CustomEvent === 'function') {
   235         ev = new CustomEvent('mdl-componentupgraded', {
   236           bubbles: true, cancelable: false
   237         });
   238       } else {
   239         ev = document.createEvent('Events');
   240         ev.initEvent('mdl-componentupgraded', true, true);
   241       }
   242       element.dispatchEvent(ev);
   243     }
   244   }
   246   /**
   247    * Upgrades a specific list of elements rather than all in the DOM.
   248    *
   249    * @param {!Element|!Array<!Element>|!NodeList|!HTMLCollection} elements
   250    * The elements we wish to upgrade.
   251    */
   252   function upgradeElementsInternal(elements) {
   253     if (!Array.isArray(elements)) {
   254       if (elements instanceof Element) {
   255         elements = [elements];
   256       } else {
   257         elements = Array.prototype.slice.call(elements);
   258       }
   259     }
   260     for (var i = 0, n = elements.length, element; i < n; i++) {
   261       element = elements[i];
   262       if (element instanceof HTMLElement) {
   263         upgradeElementInternal(element);
   264         if (element.children.length > 0) {
   265           upgradeElementsInternal(element.children);
   266         }
   267       }
   268     }
   269   }
   271   /**
   272    * Registers a class for future use and attempts to upgrade existing DOM.
   273    *
   274    * @param {componentHandler.ComponentConfigPublic} config
   275    */
   276   function registerInternal(config) {
   277     // In order to support both Closure-compiled and uncompiled code accessing
   278     // this method, we need to allow for both the dot and array syntax for
   279     // property access. You'll therefore see the `foo.bar || foo['bar']`
   280     // pattern repeated across this method.
   281     var widgetMissing = (typeof config.widget === 'undefined' &&
   282         typeof config['widget'] === 'undefined');
   283     var widget = true;
   285     if (!widgetMissing) {
   286       widget = config.widget || config['widget'];
   287     }
   289     var newConfig = /** @type {componentHandler.ComponentConfig} */ ({
   290       classConstructor: config.constructor || config['constructor'],
   291       className: config.classAsString || config['classAsString'],
   292       cssClass: config.cssClass || config['cssClass'],
   293       widget: widget,
   294       callbacks: []
   295     });
   297     registeredComponents_.forEach(function(item) {
   298       if (item.cssClass === newConfig.cssClass) {
   299         throw new Error('The provided cssClass has already been registered: ' + item.cssClass);
   300       }
   301       if (item.className === newConfig.className) {
   302         throw new Error('The provided className has already been registered');
   303       }
   304     });
   306     if (config.constructor.prototype
   307         .hasOwnProperty(componentConfigProperty_)) {
   308       throw new Error(
   309           'MDL component classes must not have ' + componentConfigProperty_ +
   310           ' defined as a property.');
   311     }
   313     var found = findRegisteredClass_(config.classAsString, newConfig);
   315     if (!found) {
   316       registeredComponents_.push(newConfig);
   317     }
   318   }
   320   /**
   321    * Allows user to be alerted to any upgrades that are performed for a given
   322    * component type
   323    *
   324    * @param {string} jsClass The class name of the MDL component we wish
   325    * to hook into for any upgrades performed.
   326    * @param {function(!HTMLElement)} callback The function to call upon an
   327    * upgrade. This function should expect 1 parameter - the HTMLElement which
   328    * got upgraded.
   329    */
   330   function registerUpgradedCallbackInternal(jsClass, callback) {
   331     var regClass = findRegisteredClass_(jsClass);
   332     if (regClass) {
   333       regClass.callbacks.push(callback);
   334     }
   335   }
   337   /**
   338    * Upgrades all registered components found in the current DOM. This is
   339    * automatically called on window load.
   340    */
   341   function upgradeAllRegisteredInternal() {
   342     for (var n = 0; n < registeredComponents_.length; n++) {
   343       upgradeDomInternal(registeredComponents_[n].className);
   344     }
   345   }
   347   /**
   348    * Check the component for the downgrade method.
   349    * Execute if found.
   350    * Remove component from createdComponents list.
   351    *
   352    * @param {?componentHandler.Component} component
   353    */
   354   function deconstructComponentInternal(component) {
   355     if (component) {
   356       var componentIndex = createdComponents_.indexOf(component);
   357       createdComponents_.splice(componentIndex, 1);
   359       var upgrades = component.element_.getAttribute('data-upgraded').split(',');
   360       var componentPlace = upgrades.indexOf(component[componentConfigProperty_].classAsString);
   361       upgrades.splice(componentPlace, 1);
   362       component.element_.setAttribute('data-upgraded', upgrades.join(','));
   364       var ev;
   365       if ('CustomEvent' in window && typeof window.CustomEvent === 'function') {
   366         ev = new CustomEvent('mdl-componentdowngraded', {
   367           bubbles: true, cancelable: false
   368         });
   369       } else {
   370         ev = document.createEvent('Events');
   371         ev.initEvent('mdl-componentdowngraded', true, true);
   372       }
   373       component.element_.dispatchEvent(ev);
   374     }
   375   }
   377   /**
   378    * Downgrade either a given node, an array of nodes, or a NodeList.
   379    *
   380    * @param {!Node|!Array<!Node>|!NodeList} nodes
   381    */
   382   function downgradeNodesInternal(nodes) {
   383     /**
   384      * Auxiliary function to downgrade a single node.
   385      * @param  {!Node} node the node to be downgraded
   386      */
   387     var downgradeNode = function(node) {
   388       createdComponents_.filter(function(item) {
   389         return item.element_ === node;
   390       }).forEach(deconstructComponentInternal);
   391     };
   392     if (nodes instanceof Array || nodes instanceof NodeList) {
   393       for (var n = 0; n < nodes.length; n++) {
   394         downgradeNode(nodes[n]);
   395       }
   396     } else if (nodes instanceof Node) {
   397       downgradeNode(nodes);
   398     } else {
   399       throw new Error('Invalid argument provided to downgrade MDL nodes.');
   400     }
   401   }
   403   // Now return the functions that should be made public with their publicly
   404   // facing names...
   405   return {
   406     upgradeDom: upgradeDomInternal,
   407     upgradeElement: upgradeElementInternal,
   408     upgradeElements: upgradeElementsInternal,
   409     upgradeAllRegistered: upgradeAllRegisteredInternal,
   410     registerUpgradedCallback: registerUpgradedCallbackInternal,
   411     register: registerInternal,
   412     downgradeElements: downgradeNodesInternal
   413   };
   414 })();
   416 /**
   417  * Describes the type of a registered component type managed by
   418  * componentHandler. Provided for benefit of the Closure compiler.
   419  *
   420  * @typedef {{
   421  *   constructor: Function,
   422  *   classAsString: string,
   423  *   cssClass: string,
   424  *   widget: (string|boolean|undefined)
   425  * }}
   426  */
   427 componentHandler.ComponentConfigPublic;  // jshint ignore:line
   429 /**
   430  * Describes the type of a registered component type managed by
   431  * componentHandler. Provided for benefit of the Closure compiler.
   432  *
   433  * @typedef {{
   434  *   constructor: !Function,
   435  *   className: string,
   436  *   cssClass: string,
   437  *   widget: (string|boolean),
   438  *   callbacks: !Array<function(!HTMLElement)>
   439  * }}
   440  */
   441 componentHandler.ComponentConfig;  // jshint ignore:line
   443 /**
   444  * Created component (i.e., upgraded element) type as managed by
   445  * componentHandler. Provided for benefit of the Closure compiler.
   446  *
   447  * @typedef {{
   448  *   element_: !HTMLElement,
   449  *   className: string,
   450  *   classAsString: string,
   451  *   cssClass: string,
   452  *   widget: string
   453  * }}
   454  */
   455 componentHandler.Component;  // jshint ignore:line
   457 // Export all symbols, for the benefit of Closure compiler.
   458 // No effect on uncompiled code.
   459 componentHandler['upgradeDom'] = componentHandler.upgradeDom;
   460 componentHandler['upgradeElement'] = componentHandler.upgradeElement;
   461 componentHandler['upgradeElements'] = componentHandler.upgradeElements;
   462 componentHandler['upgradeAllRegistered'] =
   463     componentHandler.upgradeAllRegistered;
   464 componentHandler['registerUpgradedCallback'] =
   465     componentHandler.registerUpgradedCallback;
   466 componentHandler['register'] = componentHandler.register;
   467 componentHandler['downgradeElements'] = componentHandler.downgradeElements;
   468 window.componentHandler = componentHandler;
   469 window['componentHandler'] = componentHandler;
   471 window.addEventListener('load', function() {
   472   'use strict';
   474   /**
   475    * Performs a "Cutting the mustard" test. If the browser supports the features
   476    * tested, adds a mdl-js class to the <html> element. It then upgrades all MDL
   477    * components requiring JavaScript.
   478    */
   479   if ('classList' in document.createElement('div') &&
   480       'querySelector' in document &&
   481       'addEventListener' in window && Array.prototype.forEach) {
   482     document.documentElement.classList.add('mdl-js');
   483     componentHandler.upgradeAllRegistered();
   484   } else {
   485     /**
   486      * Dummy function to avoid JS errors.
   487      */
   488     componentHandler.upgradeElement = function() {};
   489     /**
   490      * Dummy function to avoid JS errors.
   491      */
   492     componentHandler.register = function() {};
   493   }
   494 });
   496 // Source: https://github.com/darius/requestAnimationFrame/blob/master/requestAnimationFrame.js
   497 // Adapted from https://gist.github.com/paulirish/1579671 which derived from
   498 // http://paulirish.com/2011/requestanimationframe-for-smart-animating/
   499 // http://my.opera.com/emoller/blog/2011/12/20/requestanimationframe-for-smart-er-animating
   500 // requestAnimationFrame polyfill by Erik Möller.
   501 // Fixes from Paul Irish, Tino Zijdel, Andrew Mao, Klemen Slavič, Darius Bacon
   502 // MIT license
   503 if (!Date.now) {
   504     /**
   505    * Date.now polyfill.
   506    * @return {number} the current Date
   507    */
   508     Date.now = function () {
   509         return new Date().getTime();
   510     };
   511     Date['now'] = Date.now;
   512 }
   513 var vendors = [
   514     'webkit',
   515     'moz'
   516 ];
   517 for (var i = 0; i < vendors.length && !window.requestAnimationFrame; ++i) {
   518     var vp = vendors[i];
   519     window.requestAnimationFrame = window[vp + 'RequestAnimationFrame'];
   520     window.cancelAnimationFrame = window[vp + 'CancelAnimationFrame'] || window[vp + 'CancelRequestAnimationFrame'];
   521     window['requestAnimationFrame'] = window.requestAnimationFrame;
   522     window['cancelAnimationFrame'] = window.cancelAnimationFrame;
   523 }
   524 if (/iP(ad|hone|od).*OS 6/.test(window.navigator.userAgent) || !window.requestAnimationFrame || !window.cancelAnimationFrame) {
   525     var lastTime = 0;
   526     /**
   527    * requestAnimationFrame polyfill.
   528    * @param  {!Function} callback the callback function.
   529    */
   530     window.requestAnimationFrame = function (callback) {
   531         var now = Date.now();
   532         var nextTime = Math.max(lastTime + 16, now);
   533         return setTimeout(function () {
   534             callback(lastTime = nextTime);
   535         }, nextTime - now);
   536     };
   537     window.cancelAnimationFrame = clearTimeout;
   538     window['requestAnimationFrame'] = window.requestAnimationFrame;
   539     window['cancelAnimationFrame'] = window.cancelAnimationFrame;
   540 }
   541 /**
   542  * @license
   543  * Copyright 2015 Google Inc. All Rights Reserved.
   544  *
   545  * Licensed under the Apache License, Version 2.0 (the "License");
   546  * you may not use this file except in compliance with the License.
   547  * You may obtain a copy of the License at
   548  *
   549  *      http://www.apache.org/licenses/LICENSE-2.0
   550  *
   551  * Unless required by applicable law or agreed to in writing, software
   552  * distributed under the License is distributed on an "AS IS" BASIS,
   553  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   554  * See the License for the specific language governing permissions and
   555  * limitations under the License.
   556  */
   557 /**
   558    * Class constructor for Button MDL component.
   559    * Implements MDL component design pattern defined at:
   560    * https://github.com/jasonmayes/mdl-component-design-pattern
   561    *
   562    * @param {HTMLElement} element The element that will be upgraded.
   563    */
   564 var MaterialButton = function MaterialButton(element) {
   565     this.element_ = element;
   566     // Initialize instance.
   567     this.init();
   568 };
   569 window['MaterialButton'] = MaterialButton;
   570 /**
   571    * Store constants in one place so they can be updated easily.
   572    *
   573    * @enum {string | number}
   574    * @private
   575    */
   576 MaterialButton.prototype.Constant_ = {};
   577 /**
   578    * Store strings for class names defined by this component that are used in
   579    * JavaScript. This allows us to simply change it in one place should we
   580    * decide to modify at a later date.
   581    *
   582    * @enum {string}
   583    * @private
   584    */
   585 MaterialButton.prototype.CssClasses_ = {
   586     RIPPLE_EFFECT: 'mdl-js-ripple-effect',
   587     RIPPLE_CONTAINER: 'mdl-button__ripple-container',
   588     RIPPLE: 'mdl-ripple'
   589 };
   590 /**
   591    * Handle blur of element.
   592    *
   593    * @param {Event} event The event that fired.
   594    * @private
   595    */
   596 MaterialButton.prototype.blurHandler_ = function (event) {
   597     if (event) {
   598         this.element_.blur();
   599     }
   600 };
   601 // Public methods.
   602 /**
   603    * Disable button.
   604    *
   605    * @public
   606    */
   607 MaterialButton.prototype.disable = function () {
   608     this.element_.disabled = true;
   609 };
   610 MaterialButton.prototype['disable'] = MaterialButton.prototype.disable;
   611 /**
   612    * Enable button.
   613    *
   614    * @public
   615    */
   616 MaterialButton.prototype.enable = function () {
   617     this.element_.disabled = false;
   618 };
   619 MaterialButton.prototype['enable'] = MaterialButton.prototype.enable;
   620 /**
   621    * Initialize element.
   622    */
   623 MaterialButton.prototype.init = function () {
   624     if (this.element_) {
   625         if (this.element_.classList.contains(this.CssClasses_.RIPPLE_EFFECT)) {
   626             var rippleContainer = document.createElement('span');
   627             rippleContainer.classList.add(this.CssClasses_.RIPPLE_CONTAINER);
   628             this.rippleElement_ = document.createElement('span');
   629             this.rippleElement_.classList.add(this.CssClasses_.RIPPLE);
   630             rippleContainer.appendChild(this.rippleElement_);
   631             this.boundRippleBlurHandler = this.blurHandler_.bind(this);
   632             this.rippleElement_.addEventListener('mouseup', this.boundRippleBlurHandler);
   633             this.element_.appendChild(rippleContainer);
   634         }
   635         this.boundButtonBlurHandler = this.blurHandler_.bind(this);
   636         this.element_.addEventListener('mouseup', this.boundButtonBlurHandler);
   637         this.element_.addEventListener('mouseleave', this.boundButtonBlurHandler);
   638     }
   639 };
   640 // The component registers itself. It can assume componentHandler is available
   641 // in the global scope.
   642 componentHandler.register({
   643     constructor: MaterialButton,
   644     classAsString: 'MaterialButton',
   645     cssClass: 'mdl-js-button',
   646     widget: true
   647 });
   648 /**
   649  * @license
   650  * Copyright 2015 Google Inc. All Rights Reserved.
   651  *
   652  * Licensed under the Apache License, Version 2.0 (the "License");
   653  * you may not use this file except in compliance with the License.
   654  * You may obtain a copy of the License at
   655  *
   656  *      http://www.apache.org/licenses/LICENSE-2.0
   657  *
   658  * Unless required by applicable law or agreed to in writing, software
   659  * distributed under the License is distributed on an "AS IS" BASIS,
   660  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   661  * See the License for the specific language governing permissions and
   662  * limitations under the License.
   663  */
   664 /**
   665    * Class constructor for Checkbox MDL component.
   666    * Implements MDL component design pattern defined at:
   667    * https://github.com/jasonmayes/mdl-component-design-pattern
   668    *
   669    * @constructor
   670    * @param {HTMLElement} element The element that will be upgraded.
   671    */
   672 var MaterialCheckbox = function MaterialCheckbox(element) {
   673     this.element_ = element;
   674     // Initialize instance.
   675     this.init();
   676 };
   677 window['MaterialCheckbox'] = MaterialCheckbox;
   678 /**
   679    * Store constants in one place so they can be updated easily.
   680    *
   681    * @enum {string | number}
   682    * @private
   683    */
   684 MaterialCheckbox.prototype.Constant_ = { TINY_TIMEOUT: 0.001 };
   685 /**
   686    * Store strings for class names defined by this component that are used in
   687    * JavaScript. This allows us to simply change it in one place should we
   688    * decide to modify at a later date.
   689    *
   690    * @enum {string}
   691    * @private
   692    */
   693 MaterialCheckbox.prototype.CssClasses_ = {
   694     INPUT: 'mdl-checkbox__input',
   695     BOX_OUTLINE: 'mdl-checkbox__box-outline',
   696     FOCUS_HELPER: 'mdl-checkbox__focus-helper',
   697     TICK_OUTLINE: 'mdl-checkbox__tick-outline',
   698     RIPPLE_EFFECT: 'mdl-js-ripple-effect',
   699     RIPPLE_IGNORE_EVENTS: 'mdl-js-ripple-effect--ignore-events',
   700     RIPPLE_CONTAINER: 'mdl-checkbox__ripple-container',
   701     RIPPLE_CENTER: 'mdl-ripple--center',
   702     RIPPLE: 'mdl-ripple',
   703     IS_FOCUSED: 'is-focused',
   704     IS_DISABLED: 'is-disabled',
   705     IS_CHECKED: 'is-checked',
   706     IS_UPGRADED: 'is-upgraded'
   707 };
   708 /**
   709    * Handle change of state.
   710    *
   711    * @param {Event} event The event that fired.
   712    * @private
   713    */
   714 MaterialCheckbox.prototype.onChange_ = function (event) {
   715     this.updateClasses_();
   716 };
   717 /**
   718    * Handle focus of element.
   719    *
   720    * @param {Event} event The event that fired.
   721    * @private
   722    */
   723 MaterialCheckbox.prototype.onFocus_ = function (event) {
   724     this.element_.classList.add(this.CssClasses_.IS_FOCUSED);
   725 };
   726 /**
   727    * Handle lost focus of element.
   728    *
   729    * @param {Event} event The event that fired.
   730    * @private
   731    */
   732 MaterialCheckbox.prototype.onBlur_ = function (event) {
   733     this.element_.classList.remove(this.CssClasses_.IS_FOCUSED);
   734 };
   735 /**
   736    * Handle mouseup.
   737    *
   738    * @param {Event} event The event that fired.
   739    * @private
   740    */
   741 MaterialCheckbox.prototype.onMouseUp_ = function (event) {
   742     this.blur_();
   743 };
   744 /**
   745    * Handle class updates.
   746    *
   747    * @private
   748    */
   749 MaterialCheckbox.prototype.updateClasses_ = function () {
   750     this.checkDisabled();
   751     this.checkToggleState();
   752 };
   753 /**
   754    * Add blur.
   755    *
   756    * @private
   757    */
   758 MaterialCheckbox.prototype.blur_ = function () {
   759     // TODO: figure out why there's a focus event being fired after our blur,
   760     // so that we can avoid this hack.
   761     window.setTimeout(function () {
   762         this.inputElement_.blur();
   763     }.bind(this), this.Constant_.TINY_TIMEOUT);
   764 };
   765 // Public methods.
   766 /**
   767    * Check the inputs toggle state and update display.
   768    *
   769    * @public
   770    */
   771 MaterialCheckbox.prototype.checkToggleState = function () {
   772     if (this.inputElement_.checked) {
   773         this.element_.classList.add(this.CssClasses_.IS_CHECKED);
   774     } else {
   775         this.element_.classList.remove(this.CssClasses_.IS_CHECKED);
   776     }
   777 };
   778 MaterialCheckbox.prototype['checkToggleState'] = MaterialCheckbox.prototype.checkToggleState;
   779 /**
   780    * Check the inputs disabled state and update display.
   781    *
   782    * @public
   783    */
   784 MaterialCheckbox.prototype.checkDisabled = function () {
   785     if (this.inputElement_.disabled) {
   786         this.element_.classList.add(this.CssClasses_.IS_DISABLED);
   787     } else {
   788         this.element_.classList.remove(this.CssClasses_.IS_DISABLED);
   789     }
   790 };
   791 MaterialCheckbox.prototype['checkDisabled'] = MaterialCheckbox.prototype.checkDisabled;
   792 /**
   793    * Disable checkbox.
   794    *
   795    * @public
   796    */
   797 MaterialCheckbox.prototype.disable = function () {
   798     this.inputElement_.disabled = true;
   799     this.updateClasses_();
   800 };
   801 MaterialCheckbox.prototype['disable'] = MaterialCheckbox.prototype.disable;
   802 /**
   803    * Enable checkbox.
   804    *
   805    * @public
   806    */
   807 MaterialCheckbox.prototype.enable = function () {
   808     this.inputElement_.disabled = false;
   809     this.updateClasses_();
   810 };
   811 MaterialCheckbox.prototype['enable'] = MaterialCheckbox.prototype.enable;
   812 /**
   813    * Check checkbox.
   814    *
   815    * @public
   816    */
   817 MaterialCheckbox.prototype.check = function () {
   818     this.inputElement_.checked = true;
   819     this.updateClasses_();
   820 };
   821 MaterialCheckbox.prototype['check'] = MaterialCheckbox.prototype.check;
   822 /**
   823    * Uncheck checkbox.
   824    *
   825    * @public
   826    */
   827 MaterialCheckbox.prototype.uncheck = function () {
   828     this.inputElement_.checked = false;
   829     this.updateClasses_();
   830 };
   831 MaterialCheckbox.prototype['uncheck'] = MaterialCheckbox.prototype.uncheck;
   832 /**
   833    * Initialize element.
   834    */
   835 MaterialCheckbox.prototype.init = function () {
   836     if (this.element_) {
   837         this.inputElement_ = this.element_.querySelector('.' + this.CssClasses_.INPUT);
   838         var boxOutline = document.createElement('span');
   839         boxOutline.classList.add(this.CssClasses_.BOX_OUTLINE);
   840         var tickContainer = document.createElement('span');
   841         tickContainer.classList.add(this.CssClasses_.FOCUS_HELPER);
   842         var tickOutline = document.createElement('span');
   843         tickOutline.classList.add(this.CssClasses_.TICK_OUTLINE);
   844         boxOutline.appendChild(tickOutline);
   845         this.element_.appendChild(tickContainer);
   846         this.element_.appendChild(boxOutline);
   847         if (this.element_.classList.contains(this.CssClasses_.RIPPLE_EFFECT)) {
   848             this.element_.classList.add(this.CssClasses_.RIPPLE_IGNORE_EVENTS);
   849             this.rippleContainerElement_ = document.createElement('span');
   850             this.rippleContainerElement_.classList.add(this.CssClasses_.RIPPLE_CONTAINER);
   851             this.rippleContainerElement_.classList.add(this.CssClasses_.RIPPLE_EFFECT);
   852             this.rippleContainerElement_.classList.add(this.CssClasses_.RIPPLE_CENTER);
   853             this.boundRippleMouseUp = this.onMouseUp_.bind(this);
   854             this.rippleContainerElement_.addEventListener('mouseup', this.boundRippleMouseUp);
   855             var ripple = document.createElement('span');
   856             ripple.classList.add(this.CssClasses_.RIPPLE);
   857             this.rippleContainerElement_.appendChild(ripple);
   858             this.element_.appendChild(this.rippleContainerElement_);
   859         }
   860         this.boundInputOnChange = this.onChange_.bind(this);
   861         this.boundInputOnFocus = this.onFocus_.bind(this);
   862         this.boundInputOnBlur = this.onBlur_.bind(this);
   863         this.boundElementMouseUp = this.onMouseUp_.bind(this);
   864         this.inputElement_.addEventListener('change', this.boundInputOnChange);
   865         this.inputElement_.addEventListener('focus', this.boundInputOnFocus);
   866         this.inputElement_.addEventListener('blur', this.boundInputOnBlur);
   867         this.element_.addEventListener('mouseup', this.boundElementMouseUp);
   868         this.updateClasses_();
   869         this.element_.classList.add(this.CssClasses_.IS_UPGRADED);
   870     }
   871 };
   872 // The component registers itself. It can assume componentHandler is available
   873 // in the global scope.
   874 componentHandler.register({
   875     constructor: MaterialCheckbox,
   876     classAsString: 'MaterialCheckbox',
   877     cssClass: 'mdl-js-checkbox',
   878     widget: true
   879 });
   880 /**
   881  * @license
   882  * Copyright 2015 Google Inc. All Rights Reserved.
   883  *
   884  * Licensed under the Apache License, Version 2.0 (the "License");
   885  * you may not use this file except in compliance with the License.
   886  * You may obtain a copy of the License at
   887  *
   888  *      http://www.apache.org/licenses/LICENSE-2.0
   889  *
   890  * Unless required by applicable law or agreed to in writing, software
   891  * distributed under the License is distributed on an "AS IS" BASIS,
   892  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   893  * See the License for the specific language governing permissions and
   894  * limitations under the License.
   895  */
   896 /**
   897    * Class constructor for icon toggle MDL component.
   898    * Implements MDL component design pattern defined at:
   899    * https://github.com/jasonmayes/mdl-component-design-pattern
   900    *
   901    * @constructor
   902    * @param {HTMLElement} element The element that will be upgraded.
   903    */
   904 var MaterialIconToggle = function MaterialIconToggle(element) {
   905     this.element_ = element;
   906     // Initialize instance.
   907     this.init();
   908 };
   909 window['MaterialIconToggle'] = MaterialIconToggle;
   910 /**
   911    * Store constants in one place so they can be updated easily.
   912    *
   913    * @enum {string | number}
   914    * @private
   915    */
   916 MaterialIconToggle.prototype.Constant_ = { TINY_TIMEOUT: 0.001 };
   917 /**
   918    * Store strings for class names defined by this component that are used in
   919    * JavaScript. This allows us to simply change it in one place should we
   920    * decide to modify at a later date.
   921    *
   922    * @enum {string}
   923    * @private
   924    */
   925 MaterialIconToggle.prototype.CssClasses_ = {
   926     INPUT: 'mdl-icon-toggle__input',
   927     JS_RIPPLE_EFFECT: 'mdl-js-ripple-effect',
   928     RIPPLE_IGNORE_EVENTS: 'mdl-js-ripple-effect--ignore-events',
   929     RIPPLE_CONTAINER: 'mdl-icon-toggle__ripple-container',
   930     RIPPLE_CENTER: 'mdl-ripple--center',
   931     RIPPLE: 'mdl-ripple',
   932     IS_FOCUSED: 'is-focused',
   933     IS_DISABLED: 'is-disabled',
   934     IS_CHECKED: 'is-checked'
   935 };
   936 /**
   937    * Handle change of state.
   938    *
   939    * @param {Event} event The event that fired.
   940    * @private
   941    */
   942 MaterialIconToggle.prototype.onChange_ = function (event) {
   943     this.updateClasses_();
   944 };
   945 /**
   946    * Handle focus of element.
   947    *
   948    * @param {Event} event The event that fired.
   949    * @private
   950    */
   951 MaterialIconToggle.prototype.onFocus_ = function (event) {
   952     this.element_.classList.add(this.CssClasses_.IS_FOCUSED);
   953 };
   954 /**
   955    * Handle lost focus of element.
   956    *
   957    * @param {Event} event The event that fired.
   958    * @private
   959    */
   960 MaterialIconToggle.prototype.onBlur_ = function (event) {
   961     this.element_.classList.remove(this.CssClasses_.IS_FOCUSED);
   962 };
   963 /**
   964    * Handle mouseup.
   965    *
   966    * @param {Event} event The event that fired.
   967    * @private
   968    */
   969 MaterialIconToggle.prototype.onMouseUp_ = function (event) {
   970     this.blur_();
   971 };
   972 /**
   973    * Handle class updates.
   974    *
   975    * @private
   976    */
   977 MaterialIconToggle.prototype.updateClasses_ = function () {
   978     this.checkDisabled();
   979     this.checkToggleState();
   980 };
   981 /**
   982    * Add blur.
   983    *
   984    * @private
   985    */
   986 MaterialIconToggle.prototype.blur_ = function () {
   987     // TODO: figure out why there's a focus event being fired after our blur,
   988     // so that we can avoid this hack.
   989     window.setTimeout(function () {
   990         this.inputElement_.blur();
   991     }.bind(this), this.Constant_.TINY_TIMEOUT);
   992 };
   993 // Public methods.
   994 /**
   995    * Check the inputs toggle state and update display.
   996    *
   997    * @public
   998    */
   999 MaterialIconToggle.prototype.checkToggleState = function () {
  1000     if (this.inputElement_.checked) {
  1001         this.element_.classList.add(this.CssClasses_.IS_CHECKED);
  1002     } else {
  1003         this.element_.classList.remove(this.CssClasses_.IS_CHECKED);
  1004     }
  1005 };
  1006 MaterialIconToggle.prototype['checkToggleState'] = MaterialIconToggle.prototype.checkToggleState;
  1007 /**
  1008    * Check the inputs disabled state and update display.
  1009    *
  1010    * @public
  1011    */
  1012 MaterialIconToggle.prototype.checkDisabled = function () {
  1013     if (this.inputElement_.disabled) {
  1014         this.element_.classList.add(this.CssClasses_.IS_DISABLED);
  1015     } else {
  1016         this.element_.classList.remove(this.CssClasses_.IS_DISABLED);
  1017     }
  1018 };
  1019 MaterialIconToggle.prototype['checkDisabled'] = MaterialIconToggle.prototype.checkDisabled;
  1020 /**
  1021    * Disable icon toggle.
  1022    *
  1023    * @public
  1024    */
  1025 MaterialIconToggle.prototype.disable = function () {
  1026     this.inputElement_.disabled = true;
  1027     this.updateClasses_();
  1028 };
  1029 MaterialIconToggle.prototype['disable'] = MaterialIconToggle.prototype.disable;
  1030 /**
  1031    * Enable icon toggle.
  1032    *
  1033    * @public
  1034    */
  1035 MaterialIconToggle.prototype.enable = function () {
  1036     this.inputElement_.disabled = false;
  1037     this.updateClasses_();
  1038 };
  1039 MaterialIconToggle.prototype['enable'] = MaterialIconToggle.prototype.enable;
  1040 /**
  1041    * Check icon toggle.
  1042    *
  1043    * @public
  1044    */
  1045 MaterialIconToggle.prototype.check = function () {
  1046     this.inputElement_.checked = true;
  1047     this.updateClasses_();
  1048 };
  1049 MaterialIconToggle.prototype['check'] = MaterialIconToggle.prototype.check;
  1050 /**
  1051    * Uncheck icon toggle.
  1052    *
  1053    * @public
  1054    */
  1055 MaterialIconToggle.prototype.uncheck = function () {
  1056     this.inputElement_.checked = false;
  1057     this.updateClasses_();
  1058 };
  1059 MaterialIconToggle.prototype['uncheck'] = MaterialIconToggle.prototype.uncheck;
  1060 /**
  1061    * Initialize element.
  1062    */
  1063 MaterialIconToggle.prototype.init = function () {
  1064     if (this.element_) {
  1065         this.inputElement_ = this.element_.querySelector('.' + this.CssClasses_.INPUT);
  1066         if (this.element_.classList.contains(this.CssClasses_.JS_RIPPLE_EFFECT)) {
  1067             this.element_.classList.add(this.CssClasses_.RIPPLE_IGNORE_EVENTS);
  1068             this.rippleContainerElement_ = document.createElement('span');
  1069             this.rippleContainerElement_.classList.add(this.CssClasses_.RIPPLE_CONTAINER);
  1070             this.rippleContainerElement_.classList.add(this.CssClasses_.JS_RIPPLE_EFFECT);
  1071             this.rippleContainerElement_.classList.add(this.CssClasses_.RIPPLE_CENTER);
  1072             this.boundRippleMouseUp = this.onMouseUp_.bind(this);
  1073             this.rippleContainerElement_.addEventListener('mouseup', this.boundRippleMouseUp);
  1074             var ripple = document.createElement('span');
  1075             ripple.classList.add(this.CssClasses_.RIPPLE);
  1076             this.rippleContainerElement_.appendChild(ripple);
  1077             this.element_.appendChild(this.rippleContainerElement_);
  1078         }
  1079         this.boundInputOnChange = this.onChange_.bind(this);
  1080         this.boundInputOnFocus = this.onFocus_.bind(this);
  1081         this.boundInputOnBlur = this.onBlur_.bind(this);
  1082         this.boundElementOnMouseUp = this.onMouseUp_.bind(this);
  1083         this.inputElement_.addEventListener('change', this.boundInputOnChange);
  1084         this.inputElement_.addEventListener('focus', this.boundInputOnFocus);
  1085         this.inputElement_.addEventListener('blur', this.boundInputOnBlur);
  1086         this.element_.addEventListener('mouseup', this.boundElementOnMouseUp);
  1087         this.updateClasses_();
  1088         this.element_.classList.add('is-upgraded');
  1089     }
  1090 };
  1091 // The component registers itself. It can assume componentHandler is available
  1092 // in the global scope.
  1093 componentHandler.register({
  1094     constructor: MaterialIconToggle,
  1095     classAsString: 'MaterialIconToggle',
  1096     cssClass: 'mdl-js-icon-toggle',
  1097     widget: true
  1098 });
  1099 /**
  1100  * @license
  1101  * Copyright 2015 Google Inc. All Rights Reserved.
  1102  *
  1103  * Licensed under the Apache License, Version 2.0 (the "License");
  1104  * you may not use this file except in compliance with the License.
  1105  * You may obtain a copy of the License at
  1106  *
  1107  *      http://www.apache.org/licenses/LICENSE-2.0
  1108  *
  1109  * Unless required by applicable law or agreed to in writing, software
  1110  * distributed under the License is distributed on an "AS IS" BASIS,
  1111  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  1112  * See the License for the specific language governing permissions and
  1113  * limitations under the License.
  1114  */
  1115 /**
  1116    * Class constructor for dropdown MDL component.
  1117    * Implements MDL component design pattern defined at:
  1118    * https://github.com/jasonmayes/mdl-component-design-pattern
  1119    *
  1120    * @constructor
  1121    * @param {HTMLElement} element The element that will be upgraded.
  1122    */
  1123 var MaterialMenu = function MaterialMenu(element) {
  1124     this.element_ = element;
  1125     // Initialize instance.
  1126     this.init();
  1127 };
  1128 window['MaterialMenu'] = MaterialMenu;
  1129 /**
  1130    * Store constants in one place so they can be updated easily.
  1131    *
  1132    * @enum {string | number}
  1133    * @private
  1134    */
  1135 MaterialMenu.prototype.Constant_ = {
  1136     // Total duration of the menu animation.
  1137     TRANSITION_DURATION_SECONDS: 0.3,
  1138     // The fraction of the total duration we want to use for menu item animations.
  1139     TRANSITION_DURATION_FRACTION: 0.8,
  1140     // How long the menu stays open after choosing an option (so the user can see
  1141     // the ripple).
  1142     CLOSE_TIMEOUT: 150
  1143 };
  1144 /**
  1145    * Keycodes, for code readability.
  1146    *
  1147    * @enum {number}
  1148    * @private
  1149    */
  1150 MaterialMenu.prototype.Keycodes_ = {
  1151     ENTER: 13,
  1152     ESCAPE: 27,
  1153     SPACE: 32,
  1154     UP_ARROW: 38,
  1155     DOWN_ARROW: 40
  1156 };
  1157 /**
  1158    * Store strings for class names defined by this component that are used in
  1159    * JavaScript. This allows us to simply change it in one place should we
  1160    * decide to modify at a later date.
  1161    *
  1162    * @enum {string}
  1163    * @private
  1164    */
  1165 MaterialMenu.prototype.CssClasses_ = {
  1166     CONTAINER: 'mdl-menu__container',
  1167     OUTLINE: 'mdl-menu__outline',
  1168     ITEM: 'mdl-menu__item',
  1169     ITEM_RIPPLE_CONTAINER: 'mdl-menu__item-ripple-container',
  1170     RIPPLE_EFFECT: 'mdl-js-ripple-effect',
  1171     RIPPLE_IGNORE_EVENTS: 'mdl-js-ripple-effect--ignore-events',
  1172     RIPPLE: 'mdl-ripple',
  1173     // Statuses
  1174     IS_UPGRADED: 'is-upgraded',
  1175     IS_VISIBLE: 'is-visible',
  1176     IS_ANIMATING: 'is-animating',
  1177     // Alignment options
  1178     BOTTOM_LEFT: 'mdl-menu--bottom-left',
  1179     // This is the default.
  1180     BOTTOM_RIGHT: 'mdl-menu--bottom-right',
  1181     TOP_LEFT: 'mdl-menu--top-left',
  1182     TOP_RIGHT: 'mdl-menu--top-right',
  1183     UNALIGNED: 'mdl-menu--unaligned'
  1184 };
  1185 /**
  1186    * Initialize element.
  1187    */
  1188 MaterialMenu.prototype.init = function () {
  1189     if (this.element_) {
  1190         // Create container for the menu.
  1191         var container = document.createElement('div');
  1192         container.classList.add(this.CssClasses_.CONTAINER);
  1193         this.element_.parentElement.insertBefore(container, this.element_);
  1194         this.element_.parentElement.removeChild(this.element_);
  1195         container.appendChild(this.element_);
  1196         this.container_ = container;
  1197         // Create outline for the menu (shadow and background).
  1198         var outline = document.createElement('div');
  1199         outline.classList.add(this.CssClasses_.OUTLINE);
  1200         this.outline_ = outline;
  1201         container.insertBefore(outline, this.element_);
  1202         // Find the "for" element and bind events to it.
  1203         var forElId = this.element_.getAttribute('for') || this.element_.getAttribute('data-mdl-for');
  1204         var forEl = null;
  1205         if (forElId) {
  1206             forEl = document.getElementById(forElId);
  1207             if (forEl) {
  1208                 this.forElement_ = forEl;
  1209                 forEl.addEventListener('click', this.handleForClick_.bind(this));
  1210                 forEl.addEventListener('keydown', this.handleForKeyboardEvent_.bind(this));
  1211             }
  1212         }
  1213         var items = this.element_.querySelectorAll('.' + this.CssClasses_.ITEM);
  1214         this.boundItemKeydown_ = this.handleItemKeyboardEvent_.bind(this);
  1215         this.boundItemClick_ = this.handleItemClick_.bind(this);
  1216         for (var i = 0; i < items.length; i++) {
  1217             // Add a listener to each menu item.
  1218             items[i].addEventListener('click', this.boundItemClick_);
  1219             // Add a tab index to each menu item.
  1220             items[i].tabIndex = '-1';
  1221             // Add a keyboard listener to each menu item.
  1222             items[i].addEventListener('keydown', this.boundItemKeydown_);
  1223         }
  1224         // Add ripple classes to each item, if the user has enabled ripples.
  1225         if (this.element_.classList.contains(this.CssClasses_.RIPPLE_EFFECT)) {
  1226             this.element_.classList.add(this.CssClasses_.RIPPLE_IGNORE_EVENTS);
  1227             for (i = 0; i < items.length; i++) {
  1228                 var item = items[i];
  1229                 var rippleContainer = document.createElement('span');
  1230                 rippleContainer.classList.add(this.CssClasses_.ITEM_RIPPLE_CONTAINER);
  1231                 var ripple = document.createElement('span');
  1232                 ripple.classList.add(this.CssClasses_.RIPPLE);
  1233                 rippleContainer.appendChild(ripple);
  1234                 item.appendChild(rippleContainer);
  1235                 item.classList.add(this.CssClasses_.RIPPLE_EFFECT);
  1236             }
  1237         }
  1238         // Copy alignment classes to the container, so the outline can use them.
  1239         if (this.element_.classList.contains(this.CssClasses_.BOTTOM_LEFT)) {
  1240             this.outline_.classList.add(this.CssClasses_.BOTTOM_LEFT);
  1241         }
  1242         if (this.element_.classList.contains(this.CssClasses_.BOTTOM_RIGHT)) {
  1243             this.outline_.classList.add(this.CssClasses_.BOTTOM_RIGHT);
  1244         }
  1245         if (this.element_.classList.contains(this.CssClasses_.TOP_LEFT)) {
  1246             this.outline_.classList.add(this.CssClasses_.TOP_LEFT);
  1247         }
  1248         if (this.element_.classList.contains(this.CssClasses_.TOP_RIGHT)) {
  1249             this.outline_.classList.add(this.CssClasses_.TOP_RIGHT);
  1250         }
  1251         if (this.element_.classList.contains(this.CssClasses_.UNALIGNED)) {
  1252             this.outline_.classList.add(this.CssClasses_.UNALIGNED);
  1253         }
  1254         container.classList.add(this.CssClasses_.IS_UPGRADED);
  1255     }
  1256 };
  1257 /**
  1258    * Handles a click on the "for" element, by positioning the menu and then
  1259    * toggling it.
  1260    *
  1261    * @param {Event} evt The event that fired.
  1262    * @private
  1263    */
  1264 MaterialMenu.prototype.handleForClick_ = function (evt) {
  1265     if (this.element_ && this.forElement_) {
  1266         var rect = this.forElement_.getBoundingClientRect();
  1267         var forRect = this.forElement_.parentElement.getBoundingClientRect();
  1268         if (this.element_.classList.contains(this.CssClasses_.UNALIGNED)) {
  1269         } else if (this.element_.classList.contains(this.CssClasses_.BOTTOM_RIGHT)) {
  1270             // Position below the "for" element, aligned to its right.
  1271             this.container_.style.right = forRect.right - rect.right + 'px';
  1272             this.container_.style.top = this.forElement_.offsetTop + this.forElement_.offsetHeight + 'px';
  1273         } else if (this.element_.classList.contains(this.CssClasses_.TOP_LEFT)) {
  1274             // Position above the "for" element, aligned to its left.
  1275             this.container_.style.left = this.forElement_.offsetLeft + 'px';
  1276             this.container_.style.bottom = forRect.bottom - rect.top + 'px';
  1277         } else if (this.element_.classList.contains(this.CssClasses_.TOP_RIGHT)) {
  1278             // Position above the "for" element, aligned to its right.
  1279             this.container_.style.right = forRect.right - rect.right + 'px';
  1280             this.container_.style.bottom = forRect.bottom - rect.top + 'px';
  1281         } else {
  1282             // Default: position below the "for" element, aligned to its left.
  1283             this.container_.style.left = this.forElement_.offsetLeft + 'px';
  1284             this.container_.style.top = this.forElement_.offsetTop + this.forElement_.offsetHeight + 'px';
  1285         }
  1286     }
  1287     this.toggle(evt);
  1288 };
  1289 /**
  1290    * Handles a keyboard event on the "for" element.
  1291    *
  1292    * @param {Event} evt The event that fired.
  1293    * @private
  1294    */
  1295 MaterialMenu.prototype.handleForKeyboardEvent_ = function (evt) {
  1296     if (this.element_ && this.container_ && this.forElement_) {
  1297         var items = this.element_.querySelectorAll('.' + this.CssClasses_.ITEM + ':not([disabled])');
  1298         if (items && items.length > 0 && this.container_.classList.contains(this.CssClasses_.IS_VISIBLE)) {
  1299             if (evt.keyCode === this.Keycodes_.UP_ARROW) {
  1300                 evt.preventDefault();
  1301                 items[items.length - 1].focus();
  1302             } else if (evt.keyCode === this.Keycodes_.DOWN_ARROW) {
  1303                 evt.preventDefault();
  1304                 items[0].focus();
  1305             }
  1306         }
  1307     }
  1308 };
  1309 /**
  1310    * Handles a keyboard event on an item.
  1311    *
  1312    * @param {Event} evt The event that fired.
  1313    * @private
  1314    */
  1315 MaterialMenu.prototype.handleItemKeyboardEvent_ = function (evt) {
  1316     if (this.element_ && this.container_) {
  1317         var items = this.element_.querySelectorAll('.' + this.CssClasses_.ITEM + ':not([disabled])');
  1318         if (items && items.length > 0 && this.container_.classList.contains(this.CssClasses_.IS_VISIBLE)) {
  1319             var currentIndex = Array.prototype.slice.call(items).indexOf(evt.target);
  1320             if (evt.keyCode === this.Keycodes_.UP_ARROW) {
  1321                 evt.preventDefault();
  1322                 if (currentIndex > 0) {
  1323                     items[currentIndex - 1].focus();
  1324                 } else {
  1325                     items[items.length - 1].focus();
  1326                 }
  1327             } else if (evt.keyCode === this.Keycodes_.DOWN_ARROW) {
  1328                 evt.preventDefault();
  1329                 if (items.length > currentIndex + 1) {
  1330                     items[currentIndex + 1].focus();
  1331                 } else {
  1332                     items[0].focus();
  1333                 }
  1334             } else if (evt.keyCode === this.Keycodes_.SPACE || evt.keyCode === this.Keycodes_.ENTER) {
  1335                 evt.preventDefault();
  1336                 // Send mousedown and mouseup to trigger ripple.
  1337                 var e = new MouseEvent('mousedown');
  1338                 evt.target.dispatchEvent(e);
  1339                 e = new MouseEvent('mouseup');
  1340                 evt.target.dispatchEvent(e);
  1341                 // Send click.
  1342                 evt.target.click();
  1343             } else if (evt.keyCode === this.Keycodes_.ESCAPE) {
  1344                 evt.preventDefault();
  1345                 this.hide();
  1346             }
  1347         }
  1348     }
  1349 };
  1350 /**
  1351    * Handles a click event on an item.
  1352    *
  1353    * @param {Event} evt The event that fired.
  1354    * @private
  1355    */
  1356 MaterialMenu.prototype.handleItemClick_ = function (evt) {
  1357     if (evt.target.hasAttribute('disabled')) {
  1358         evt.stopPropagation();
  1359     } else {
  1360         // Wait some time before closing menu, so the user can see the ripple.
  1361         this.closing_ = true;
  1362         window.setTimeout(function (evt) {
  1363             this.hide();
  1364             this.closing_ = false;
  1365         }.bind(this), this.Constant_.CLOSE_TIMEOUT);
  1366     }
  1367 };
  1368 /**
  1369    * Calculates the initial clip (for opening the menu) or final clip (for closing
  1370    * it), and applies it. This allows us to animate from or to the correct point,
  1371    * that is, the point it's aligned to in the "for" element.
  1372    *
  1373    * @param {number} height Height of the clip rectangle
  1374    * @param {number} width Width of the clip rectangle
  1375    * @private
  1376    */
  1377 MaterialMenu.prototype.applyClip_ = function (height, width) {
  1378     if (this.element_.classList.contains(this.CssClasses_.UNALIGNED)) {
  1379         // Do not clip.
  1380         this.element_.style.clip = '';
  1381     } else if (this.element_.classList.contains(this.CssClasses_.BOTTOM_RIGHT)) {
  1382         // Clip to the top right corner of the menu.
  1383         this.element_.style.clip = 'rect(0 ' + width + 'px ' + '0 ' + width + 'px)';
  1384     } else if (this.element_.classList.contains(this.CssClasses_.TOP_LEFT)) {
  1385         // Clip to the bottom left corner of the menu.
  1386         this.element_.style.clip = 'rect(' + height + 'px 0 ' + height + 'px 0)';
  1387     } else if (this.element_.classList.contains(this.CssClasses_.TOP_RIGHT)) {
  1388         // Clip to the bottom right corner of the menu.
  1389         this.element_.style.clip = 'rect(' + height + 'px ' + width + 'px ' + height + 'px ' + width + 'px)';
  1390     } else {
  1391         // Default: do not clip (same as clipping to the top left corner).
  1392         this.element_.style.clip = '';
  1393     }
  1394 };
  1395 /**
  1396    * Cleanup function to remove animation listeners.
  1397    *
  1398    * @param {Event} evt
  1399    * @private
  1400    */
  1401 MaterialMenu.prototype.removeAnimationEndListener_ = function (evt) {
  1402     evt.target.classList.remove(MaterialMenu.prototype.CssClasses_.IS_ANIMATING);
  1403 };
  1404 /**
  1405    * Adds an event listener to clean up after the animation ends.
  1406    *
  1407    * @private
  1408    */
  1409 MaterialMenu.prototype.addAnimationEndListener_ = function () {
  1410     this.element_.addEventListener('transitionend', this.removeAnimationEndListener_);
  1411     this.element_.addEventListener('webkitTransitionEnd', this.removeAnimationEndListener_);
  1412 };
  1413 /**
  1414    * Displays the menu.
  1415    *
  1416    * @public
  1417    */
  1418 MaterialMenu.prototype.show = function (evt) {
  1419     if (this.element_ && this.container_ && this.outline_) {
  1420         // Measure the inner element.
  1421         var height = this.element_.getBoundingClientRect().height;
  1422         var width = this.element_.getBoundingClientRect().width;
  1423         // Apply the inner element's size to the container and outline.
  1424         this.container_.style.width = width + 'px';
  1425         this.container_.style.height = height + 'px';
  1426         this.outline_.style.width = width + 'px';
  1427         this.outline_.style.height = height + 'px';
  1428         var transitionDuration = this.Constant_.TRANSITION_DURATION_SECONDS * this.Constant_.TRANSITION_DURATION_FRACTION;
  1429         // Calculate transition delays for individual menu items, so that they fade
  1430         // in one at a time.
  1431         var items = this.element_.querySelectorAll('.' + this.CssClasses_.ITEM);
  1432         for (var i = 0; i < items.length; i++) {
  1433             var itemDelay = null;
  1434             if (this.element_.classList.contains(this.CssClasses_.TOP_LEFT) || this.element_.classList.contains(this.CssClasses_.TOP_RIGHT)) {
  1435                 itemDelay = (height - items[i].offsetTop - items[i].offsetHeight) / height * transitionDuration + 's';
  1436             } else {
  1437                 itemDelay = items[i].offsetTop / height * transitionDuration + 's';
  1438             }
  1439             items[i].style.transitionDelay = itemDelay;
  1440         }
  1441         // Apply the initial clip to the text before we start animating.
  1442         this.applyClip_(height, width);
  1443         // Wait for the next frame, turn on animation, and apply the final clip.
  1444         // Also make it visible. This triggers the transitions.
  1445         window.requestAnimationFrame(function () {
  1446             this.element_.classList.add(this.CssClasses_.IS_ANIMATING);
  1447             this.element_.style.clip = 'rect(0 ' + width + 'px ' + height + 'px 0)';
  1448             this.container_.classList.add(this.CssClasses_.IS_VISIBLE);
  1449         }.bind(this));
  1450         // Clean up after the animation is complete.
  1451         this.addAnimationEndListener_();
  1452         // Add a click listener to the document, to close the menu.
  1453         var callback = function (e) {
  1454             // Check to see if the document is processing the same event that
  1455             // displayed the menu in the first place. If so, do nothing.
  1456             // Also check to see if the menu is in the process of closing itself, and
  1457             // do nothing in that case.
  1458             // Also check if the clicked element is a menu item
  1459             // if so, do nothing.
  1460             if (e !== evt && !this.closing_ && e.target.parentNode !== this.element_) {
  1461                 document.removeEventListener('click', callback);
  1462                 this.hide();
  1463             }
  1464         }.bind(this);
  1465         document.addEventListener('click', callback);
  1466     }
  1467 };
  1468 MaterialMenu.prototype['show'] = MaterialMenu.prototype.show;
  1469 /**
  1470    * Hides the menu.
  1471    *
  1472    * @public
  1473    */
  1474 MaterialMenu.prototype.hide = function () {
  1475     if (this.element_ && this.container_ && this.outline_) {
  1476         var items = this.element_.querySelectorAll('.' + this.CssClasses_.ITEM);
  1477         // Remove all transition delays; menu items fade out concurrently.
  1478         for (var i = 0; i < items.length; i++) {
  1479             items[i].style.removeProperty('transition-delay');
  1480         }
  1481         // Measure the inner element.
  1482         var rect = this.element_.getBoundingClientRect();
  1483         var height = rect.height;
  1484         var width = rect.width;
  1485         // Turn on animation, and apply the final clip. Also make invisible.
  1486         // This triggers the transitions.
  1487         this.element_.classList.add(this.CssClasses_.IS_ANIMATING);
  1488         this.applyClip_(height, width);
  1489         this.container_.classList.remove(this.CssClasses_.IS_VISIBLE);
  1490         // Clean up after the animation is complete.
  1491         this.addAnimationEndListener_();
  1492     }
  1493 };
  1494 MaterialMenu.prototype['hide'] = MaterialMenu.prototype.hide;
  1495 /**
  1496    * Displays or hides the menu, depending on current state.
  1497    *
  1498    * @public
  1499    */
  1500 MaterialMenu.prototype.toggle = function (evt) {
  1501     if (this.container_.classList.contains(this.CssClasses_.IS_VISIBLE)) {
  1502         this.hide();
  1503     } else {
  1504         this.show(evt);
  1505     }
  1506 };
  1507 MaterialMenu.prototype['toggle'] = MaterialMenu.prototype.toggle;
  1508 // The component registers itself. It can assume componentHandler is available
  1509 // in the global scope.
  1510 componentHandler.register({
  1511     constructor: MaterialMenu,
  1512     classAsString: 'MaterialMenu',
  1513     cssClass: 'mdl-js-menu',
  1514     widget: true
  1515 });
  1516 /**
  1517  * @license
  1518  * Copyright 2015 Google Inc. All Rights Reserved.
  1519  *
  1520  * Licensed under the Apache License, Version 2.0 (the "License");
  1521  * you may not use this file except in compliance with the License.
  1522  * You may obtain a copy of the License at
  1523  *
  1524  *      http://www.apache.org/licenses/LICENSE-2.0
  1525  *
  1526  * Unless required by applicable law or agreed to in writing, software
  1527  * distributed under the License is distributed on an "AS IS" BASIS,
  1528  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  1529  * See the License for the specific language governing permissions and
  1530  * limitations under the License.
  1531  */
  1532 /**
  1533    * Class constructor for Progress MDL component.
  1534    * Implements MDL component design pattern defined at:
  1535    * https://github.com/jasonmayes/mdl-component-design-pattern
  1536    *
  1537    * @constructor
  1538    * @param {HTMLElement} element The element that will be upgraded.
  1539    */
  1540 var MaterialProgress = function MaterialProgress(element) {
  1541     this.element_ = element;
  1542     // Initialize instance.
  1543     this.init();
  1544 };
  1545 window['MaterialProgress'] = MaterialProgress;
  1546 /**
  1547    * Store constants in one place so they can be updated easily.
  1548    *
  1549    * @enum {string | number}
  1550    * @private
  1551    */
  1552 MaterialProgress.prototype.Constant_ = {};
  1553 /**
  1554    * Store strings for class names defined by this component that are used in
  1555    * JavaScript. This allows us to simply change it in one place should we
  1556    * decide to modify at a later date.
  1557    *
  1558    * @enum {string}
  1559    * @private
  1560    */
  1561 MaterialProgress.prototype.CssClasses_ = { INDETERMINATE_CLASS: 'mdl-progress__indeterminate' };
  1562 /**
  1563    * Set the current progress of the progressbar.
  1564    *
  1565    * @param {number} p Percentage of the progress (0-100)
  1566    * @public
  1567    */
  1568 MaterialProgress.prototype.setProgress = function (p) {
  1569     if (this.element_.classList.contains(this.CssClasses_.INDETERMINATE_CLASS)) {
  1570         return;
  1571     }
  1572     this.progressbar_.style.width = p + '%';
  1573 };
  1574 MaterialProgress.prototype['setProgress'] = MaterialProgress.prototype.setProgress;
  1575 /**
  1576    * Set the current progress of the buffer.
  1577    *
  1578    * @param {number} p Percentage of the buffer (0-100)
  1579    * @public
  1580    */
  1581 MaterialProgress.prototype.setBuffer = function (p) {
  1582     this.bufferbar_.style.width = p + '%';
  1583     this.auxbar_.style.width = 100 - p + '%';
  1584 };
  1585 MaterialProgress.prototype['setBuffer'] = MaterialProgress.prototype.setBuffer;
  1586 /**
  1587    * Initialize element.
  1588    */
  1589 MaterialProgress.prototype.init = function () {
  1590     if (this.element_) {
  1591         var el = document.createElement('div');
  1592         el.className = 'progressbar bar bar1';
  1593         this.element_.appendChild(el);
  1594         this.progressbar_ = el;
  1595         el = document.createElement('div');
  1596         el.className = 'bufferbar bar bar2';
  1597         this.element_.appendChild(el);
  1598         this.bufferbar_ = el;
  1599         el = document.createElement('div');
  1600         el.className = 'auxbar bar bar3';
  1601         this.element_.appendChild(el);
  1602         this.auxbar_ = el;
  1603         this.progressbar_.style.width = '0%';
  1604         this.bufferbar_.style.width = '100%';
  1605         this.auxbar_.style.width = '0%';
  1606         this.element_.classList.add('is-upgraded');
  1607     }
  1608 };
  1609 // The component registers itself. It can assume componentHandler is available
  1610 // in the global scope.
  1611 componentHandler.register({
  1612     constructor: MaterialProgress,
  1613     classAsString: 'MaterialProgress',
  1614     cssClass: 'mdl-js-progress',
  1615     widget: true
  1616 });
  1617 /**
  1618  * @license
  1619  * Copyright 2015 Google Inc. All Rights Reserved.
  1620  *
  1621  * Licensed under the Apache License, Version 2.0 (the "License");
  1622  * you may not use this file except in compliance with the License.
  1623  * You may obtain a copy of the License at
  1624  *
  1625  *      http://www.apache.org/licenses/LICENSE-2.0
  1626  *
  1627  * Unless required by applicable law or agreed to in writing, software
  1628  * distributed under the License is distributed on an "AS IS" BASIS,
  1629  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  1630  * See the License for the specific language governing permissions and
  1631  * limitations under the License.
  1632  */
  1633 /**
  1634    * Class constructor for Radio MDL component.
  1635    * Implements MDL component design pattern defined at:
  1636    * https://github.com/jasonmayes/mdl-component-design-pattern
  1637    *
  1638    * @constructor
  1639    * @param {HTMLElement} element The element that will be upgraded.
  1640    */
  1641 var MaterialRadio = function MaterialRadio(element) {
  1642     this.element_ = element;
  1643     // Initialize instance.
  1644     this.init();
  1645 };
  1646 window['MaterialRadio'] = MaterialRadio;
  1647 /**
  1648    * Store constants in one place so they can be updated easily.
  1649    *
  1650    * @enum {string | number}
  1651    * @private
  1652    */
  1653 MaterialRadio.prototype.Constant_ = { TINY_TIMEOUT: 0.001 };
  1654 /**
  1655    * Store strings for class names defined by this component that are used in
  1656    * JavaScript. This allows us to simply change it in one place should we
  1657    * decide to modify at a later date.
  1658    *
  1659    * @enum {string}
  1660    * @private
  1661    */
  1662 MaterialRadio.prototype.CssClasses_ = {
  1663     IS_FOCUSED: 'is-focused',
  1664     IS_DISABLED: 'is-disabled',
  1665     IS_CHECKED: 'is-checked',
  1666     IS_UPGRADED: 'is-upgraded',
  1667     JS_RADIO: 'mdl-js-radio',
  1668     RADIO_BTN: 'mdl-radio__button',
  1669     RADIO_OUTER_CIRCLE: 'mdl-radio__outer-circle',
  1670     RADIO_INNER_CIRCLE: 'mdl-radio__inner-circle',
  1671     RIPPLE_EFFECT: 'mdl-js-ripple-effect',
  1672     RIPPLE_IGNORE_EVENTS: 'mdl-js-ripple-effect--ignore-events',
  1673     RIPPLE_CONTAINER: 'mdl-radio__ripple-container',
  1674     RIPPLE_CENTER: 'mdl-ripple--center',
  1675     RIPPLE: 'mdl-ripple'
  1676 };
  1677 /**
  1678    * Handle change of state.
  1679    *
  1680    * @param {Event} event The event that fired.
  1681    * @private
  1682    */
  1683 MaterialRadio.prototype.onChange_ = function (event) {
  1684     // Since other radio buttons don't get change events, we need to look for
  1685     // them to update their classes.
  1686     var radios = document.getElementsByClassName(this.CssClasses_.JS_RADIO);
  1687     for (var i = 0; i < radios.length; i++) {
  1688         var button = radios[i].querySelector('.' + this.CssClasses_.RADIO_BTN);
  1689         // Different name == different group, so no point updating those.
  1690         if (button.getAttribute('name') === this.btnElement_.getAttribute('name')) {
  1691             radios[i]['MaterialRadio'].updateClasses_();
  1692         }
  1693     }
  1694 };
  1695 /**
  1696    * Handle focus.
  1697    *
  1698    * @param {Event} event The event that fired.
  1699    * @private
  1700    */
  1701 MaterialRadio.prototype.onFocus_ = function (event) {
  1702     this.element_.classList.add(this.CssClasses_.IS_FOCUSED);
  1703 };
  1704 /**
  1705    * Handle lost focus.
  1706    *
  1707    * @param {Event} event The event that fired.
  1708    * @private
  1709    */
  1710 MaterialRadio.prototype.onBlur_ = function (event) {
  1711     this.element_.classList.remove(this.CssClasses_.IS_FOCUSED);
  1712 };
  1713 /**
  1714    * Handle mouseup.
  1715    *
  1716    * @param {Event} event The event that fired.
  1717    * @private
  1718    */
  1719 MaterialRadio.prototype.onMouseup_ = function (event) {
  1720     this.blur_();
  1721 };
  1722 /**
  1723    * Update classes.
  1724    *
  1725    * @private
  1726    */
  1727 MaterialRadio.prototype.updateClasses_ = function () {
  1728     this.checkDisabled();
  1729     this.checkToggleState();
  1730 };
  1731 /**
  1732    * Add blur.
  1733    *
  1734    * @private
  1735    */
  1736 MaterialRadio.prototype.blur_ = function () {
  1737     // TODO: figure out why there's a focus event being fired after our blur,
  1738     // so that we can avoid this hack.
  1739     window.setTimeout(function () {
  1740         this.btnElement_.blur();
  1741     }.bind(this), this.Constant_.TINY_TIMEOUT);
  1742 };
  1743 // Public methods.
  1744 /**
  1745    * Check the components disabled state.
  1746    *
  1747    * @public
  1748    */
  1749 MaterialRadio.prototype.checkDisabled = function () {
  1750     if (this.btnElement_.disabled) {
  1751         this.element_.classList.add(this.CssClasses_.IS_DISABLED);
  1752     } else {
  1753         this.element_.classList.remove(this.CssClasses_.IS_DISABLED);
  1754     }
  1755 };
  1756 MaterialRadio.prototype['checkDisabled'] = MaterialRadio.prototype.checkDisabled;
  1757 /**
  1758    * Check the components toggled state.
  1759    *
  1760    * @public
  1761    */
  1762 MaterialRadio.prototype.checkToggleState = function () {
  1763     if (this.btnElement_.checked) {
  1764         this.element_.classList.add(this.CssClasses_.IS_CHECKED);
  1765     } else {
  1766         this.element_.classList.remove(this.CssClasses_.IS_CHECKED);
  1767     }
  1768 };
  1769 MaterialRadio.prototype['checkToggleState'] = MaterialRadio.prototype.checkToggleState;
  1770 /**
  1771    * Disable radio.
  1772    *
  1773    * @public
  1774    */
  1775 MaterialRadio.prototype.disable = function () {
  1776     this.btnElement_.disabled = true;
  1777     this.updateClasses_();
  1778 };
  1779 MaterialRadio.prototype['disable'] = MaterialRadio.prototype.disable;
  1780 /**
  1781    * Enable radio.
  1782    *
  1783    * @public
  1784    */
  1785 MaterialRadio.prototype.enable = function () {
  1786     this.btnElement_.disabled = false;
  1787     this.updateClasses_();
  1788 };
  1789 MaterialRadio.prototype['enable'] = MaterialRadio.prototype.enable;
  1790 /**
  1791    * Check radio.
  1792    *
  1793    * @public
  1794    */
  1795 MaterialRadio.prototype.check = function () {
  1796     this.btnElement_.checked = true;
  1797     this.onChange_(null);
  1798 };
  1799 MaterialRadio.prototype['check'] = MaterialRadio.prototype.check;
  1800 /**
  1801    * Uncheck radio.
  1802    *
  1803    * @public
  1804    */
  1805 MaterialRadio.prototype.uncheck = function () {
  1806     this.btnElement_.checked = false;
  1807     this.onChange_(null);
  1808 };
  1809 MaterialRadio.prototype['uncheck'] = MaterialRadio.prototype.uncheck;
  1810 /**
  1811    * Initialize element.
  1812    */
  1813 MaterialRadio.prototype.init = function () {
  1814     if (this.element_) {
  1815         this.btnElement_ = this.element_.querySelector('.' + this.CssClasses_.RADIO_BTN);
  1816         this.boundChangeHandler_ = this.onChange_.bind(this);
  1817         this.boundFocusHandler_ = this.onChange_.bind(this);
  1818         this.boundBlurHandler_ = this.onBlur_.bind(this);
  1819         this.boundMouseUpHandler_ = this.onMouseup_.bind(this);
  1820         var outerCircle = document.createElement('span');
  1821         outerCircle.classList.add(this.CssClasses_.RADIO_OUTER_CIRCLE);
  1822         var innerCircle = document.createElement('span');
  1823         innerCircle.classList.add(this.CssClasses_.RADIO_INNER_CIRCLE);
  1824         this.element_.appendChild(outerCircle);
  1825         this.element_.appendChild(innerCircle);
  1826         var rippleContainer;
  1827         if (this.element_.classList.contains(this.CssClasses_.RIPPLE_EFFECT)) {
  1828             this.element_.classList.add(this.CssClasses_.RIPPLE_IGNORE_EVENTS);
  1829             rippleContainer = document.createElement('span');
  1830             rippleContainer.classList.add(this.CssClasses_.RIPPLE_CONTAINER);
  1831             rippleContainer.classList.add(this.CssClasses_.RIPPLE_EFFECT);
  1832             rippleContainer.classList.add(this.CssClasses_.RIPPLE_CENTER);
  1833             rippleContainer.addEventListener('mouseup', this.boundMouseUpHandler_);
  1834             var ripple = document.createElement('span');
  1835             ripple.classList.add(this.CssClasses_.RIPPLE);
  1836             rippleContainer.appendChild(ripple);
  1837             this.element_.appendChild(rippleContainer);
  1838         }
  1839         this.btnElement_.addEventListener('change', this.boundChangeHandler_);
  1840         this.btnElement_.addEventListener('focus', this.boundFocusHandler_);
  1841         this.btnElement_.addEventListener('blur', this.boundBlurHandler_);
  1842         this.element_.addEventListener('mouseup', this.boundMouseUpHandler_);
  1843         this.updateClasses_();
  1844         this.element_.classList.add(this.CssClasses_.IS_UPGRADED);
  1845     }
  1846 };
  1847 // The component registers itself. It can assume componentHandler is available
  1848 // in the global scope.
  1849 componentHandler.register({
  1850     constructor: MaterialRadio,
  1851     classAsString: 'MaterialRadio',
  1852     cssClass: 'mdl-js-radio',
  1853     widget: true
  1854 });
  1855 /**
  1856  * @license
  1857  * Copyright 2015 Google Inc. All Rights Reserved.
  1858  *
  1859  * Licensed under the Apache License, Version 2.0 (the "License");
  1860  * you may not use this file except in compliance with the License.
  1861  * You may obtain a copy of the License at
  1862  *
  1863  *      http://www.apache.org/licenses/LICENSE-2.0
  1864  *
  1865  * Unless required by applicable law or agreed to in writing, software
  1866  * distributed under the License is distributed on an "AS IS" BASIS,
  1867  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  1868  * See the License for the specific language governing permissions and
  1869  * limitations under the License.
  1870  */
  1871 /**
  1872    * Class constructor for Slider MDL component.
  1873    * Implements MDL component design pattern defined at:
  1874    * https://github.com/jasonmayes/mdl-component-design-pattern
  1875    *
  1876    * @constructor
  1877    * @param {HTMLElement} element The element that will be upgraded.
  1878    */
  1879 var MaterialSlider = function MaterialSlider(element) {
  1880     this.element_ = element;
  1881     // Browser feature detection.
  1882     this.isIE_ = window.navigator.msPointerEnabled;
  1883     // Initialize instance.
  1884     this.init();
  1885 };
  1886 window['MaterialSlider'] = MaterialSlider;
  1887 /**
  1888    * Store constants in one place so they can be updated easily.
  1889    *
  1890    * @enum {string | number}
  1891    * @private
  1892    */
  1893 MaterialSlider.prototype.Constant_ = {};
  1894 /**
  1895    * Store strings for class names defined by this component that are used in
  1896    * JavaScript. This allows us to simply change it in one place should we
  1897    * decide to modify at a later date.
  1898    *
  1899    * @enum {string}
  1900    * @private
  1901    */
  1902 MaterialSlider.prototype.CssClasses_ = {
  1903     IE_CONTAINER: 'mdl-slider__ie-container',
  1904     SLIDER_CONTAINER: 'mdl-slider__container',
  1905     BACKGROUND_FLEX: 'mdl-slider__background-flex',
  1906     BACKGROUND_LOWER: 'mdl-slider__background-lower',
  1907     BACKGROUND_UPPER: 'mdl-slider__background-upper',
  1908     IS_LOWEST_VALUE: 'is-lowest-value',
  1909     IS_UPGRADED: 'is-upgraded'
  1910 };
  1911 /**
  1912    * Handle input on element.
  1913    *
  1914    * @param {Event} event The event that fired.
  1915    * @private
  1916    */
  1917 MaterialSlider.prototype.onInput_ = function (event) {
  1918     this.updateValueStyles_();
  1919 };
  1920 /**
  1921    * Handle change on element.
  1922    *
  1923    * @param {Event} event The event that fired.
  1924    * @private
  1925    */
  1926 MaterialSlider.prototype.onChange_ = function (event) {
  1927     this.updateValueStyles_();
  1928 };
  1929 /**
  1930    * Handle mouseup on element.
  1931    *
  1932    * @param {Event} event The event that fired.
  1933    * @private
  1934    */
  1935 MaterialSlider.prototype.onMouseUp_ = function (event) {
  1936     event.target.blur();
  1937 };
  1938 /**
  1939    * Handle mousedown on container element.
  1940    * This handler is purpose is to not require the use to click
  1941    * exactly on the 2px slider element, as FireFox seems to be very
  1942    * strict about this.
  1943    *
  1944    * @param {Event} event The event that fired.
  1945    * @private
  1946    * @suppress {missingProperties}
  1947    */
  1948 MaterialSlider.prototype.onContainerMouseDown_ = function (event) {
  1949     // If this click is not on the parent element (but rather some child)
  1950     // ignore. It may still bubble up.
  1951     if (event.target !== this.element_.parentElement) {
  1952         return;
  1953     }
  1954     // Discard the original event and create a new event that
  1955     // is on the slider element.
  1956     event.preventDefault();
  1957     var newEvent = new MouseEvent('mousedown', {
  1958         target: event.target,
  1959         buttons: event.buttons,
  1960         clientX: event.clientX,
  1961         clientY: this.element_.getBoundingClientRect().y
  1962     });
  1963     this.element_.dispatchEvent(newEvent);
  1964 };
  1965 /**
  1966    * Handle updating of values.
  1967    *
  1968    * @private
  1969    */
  1970 MaterialSlider.prototype.updateValueStyles_ = function () {
  1971     // Calculate and apply percentages to div structure behind slider.
  1972     var fraction = (this.element_.value - this.element_.min) / (this.element_.max - this.element_.min);
  1973     if (fraction === 0) {
  1974         this.element_.classList.add(this.CssClasses_.IS_LOWEST_VALUE);
  1975     } else {
  1976         this.element_.classList.remove(this.CssClasses_.IS_LOWEST_VALUE);
  1977     }
  1978     if (!this.isIE_) {
  1979         this.backgroundLower_.style.flex = fraction;
  1980         this.backgroundLower_.style.webkitFlex = fraction;
  1981         this.backgroundUpper_.style.flex = 1 - fraction;
  1982         this.backgroundUpper_.style.webkitFlex = 1 - fraction;
  1983     }
  1984 };
  1985 // Public methods.
  1986 /**
  1987    * Disable slider.
  1988    *
  1989    * @public
  1990    */
  1991 MaterialSlider.prototype.disable = function () {
  1992     this.element_.disabled = true;
  1993 };
  1994 MaterialSlider.prototype['disable'] = MaterialSlider.prototype.disable;
  1995 /**
  1996    * Enable slider.
  1997    *
  1998    * @public
  1999    */
  2000 MaterialSlider.prototype.enable = function () {
  2001     this.element_.disabled = false;
  2002 };
  2003 MaterialSlider.prototype['enable'] = MaterialSlider.prototype.enable;
  2004 /**
  2005    * Update slider value.
  2006    *
  2007    * @param {number} value The value to which to set the control (optional).
  2008    * @public
  2009    */
  2010 MaterialSlider.prototype.change = function (value) {
  2011     if (typeof value !== 'undefined') {
  2012         this.element_.value = value;
  2013     }
  2014     this.updateValueStyles_();
  2015 };
  2016 MaterialSlider.prototype['change'] = MaterialSlider.prototype.change;
  2017 /**
  2018    * Initialize element.
  2019    */
  2020 MaterialSlider.prototype.init = function () {
  2021     if (this.element_) {
  2022         if (this.isIE_) {
  2023             // Since we need to specify a very large height in IE due to
  2024             // implementation limitations, we add a parent here that trims it down to
  2025             // a reasonable size.
  2026             var containerIE = document.createElement('div');
  2027             containerIE.classList.add(this.CssClasses_.IE_CONTAINER);
  2028             this.element_.parentElement.insertBefore(containerIE, this.element_);
  2029             this.element_.parentElement.removeChild(this.element_);
  2030             containerIE.appendChild(this.element_);
  2031         } else {
  2032             // For non-IE browsers, we need a div structure that sits behind the
  2033             // slider and allows us to style the left and right sides of it with
  2034             // different colors.
  2035             var container = document.createElement('div');
  2036             container.classList.add(this.CssClasses_.SLIDER_CONTAINER);
  2037             this.element_.parentElement.insertBefore(container, this.element_);
  2038             this.element_.parentElement.removeChild(this.element_);
  2039             container.appendChild(this.element_);
  2040             var backgroundFlex = document.createElement('div');
  2041             backgroundFlex.classList.add(this.CssClasses_.BACKGROUND_FLEX);
  2042             container.appendChild(backgroundFlex);
  2043             this.backgroundLower_ = document.createElement('div');
  2044             this.backgroundLower_.classList.add(this.CssClasses_.BACKGROUND_LOWER);
  2045             backgroundFlex.appendChild(this.backgroundLower_);
  2046             this.backgroundUpper_ = document.createElement('div');
  2047             this.backgroundUpper_.classList.add(this.CssClasses_.BACKGROUND_UPPER);
  2048             backgroundFlex.appendChild(this.backgroundUpper_);
  2049         }
  2050         this.boundInputHandler = this.onInput_.bind(this);
  2051         this.boundChangeHandler = this.onChange_.bind(this);
  2052         this.boundMouseUpHandler = this.onMouseUp_.bind(this);
  2053         this.boundContainerMouseDownHandler = this.onContainerMouseDown_.bind(this);
  2054         this.element_.addEventListener('input', this.boundInputHandler);
  2055         this.element_.addEventListener('change', this.boundChangeHandler);
  2056         this.element_.addEventListener('mouseup', this.boundMouseUpHandler);
  2057         this.element_.parentElement.addEventListener('mousedown', this.boundContainerMouseDownHandler);
  2058         this.updateValueStyles_();
  2059         this.element_.classList.add(this.CssClasses_.IS_UPGRADED);
  2060     }
  2061 };
  2062 // The component registers itself. It can assume componentHandler is available
  2063 // in the global scope.
  2064 componentHandler.register({
  2065     constructor: MaterialSlider,
  2066     classAsString: 'MaterialSlider',
  2067     cssClass: 'mdl-js-slider',
  2068     widget: true
  2069 });
  2070 /**
  2071  * Copyright 2015 Google Inc. All Rights Reserved.
  2072  *
  2073  * Licensed under the Apache License, Version 2.0 (the "License");
  2074  * you may not use this file except in compliance with the License.
  2075  * You may obtain a copy of the License at
  2076  *
  2077  *      http://www.apache.org/licenses/LICENSE-2.0
  2078  *
  2079  * Unless required by applicable law or agreed to in writing, software
  2080  * distributed under the License is distributed on an "AS IS" BASIS,
  2081  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  2082  * See the License for the specific language governing permissions and
  2083  * limitations under the License.
  2084  */
  2085 /**
  2086    * Class constructor for Snackbar MDL component.
  2087    * Implements MDL component design pattern defined at:
  2088    * https://github.com/jasonmayes/mdl-component-design-pattern
  2089    *
  2090    * @constructor
  2091    * @param {HTMLElement} element The element that will be upgraded.
  2092    */
  2093 var MaterialSnackbar = function MaterialSnackbar(element) {
  2094     this.element_ = element;
  2095     this.textElement_ = this.element_.querySelector('.' + this.cssClasses_.MESSAGE);
  2096     this.actionElement_ = this.element_.querySelector('.' + this.cssClasses_.ACTION);
  2097     if (!this.textElement_) {
  2098         throw new Error('There must be a message element for a snackbar.');
  2099     }
  2100     if (!this.actionElement_) {
  2101         throw new Error('There must be an action element for a snackbar.');
  2102     }
  2103     this.active = false;
  2104     this.actionHandler_ = undefined;
  2105     this.message_ = undefined;
  2106     this.actionText_ = undefined;
  2107     this.queuedNotifications_ = [];
  2108     this.setActionHidden_(true);
  2109 };
  2110 window['MaterialSnackbar'] = MaterialSnackbar;
  2111 /**
  2112    * Store constants in one place so they can be updated easily.
  2113    *
  2114    * @enum {string | number}
  2115    * @private
  2116    */
  2117 MaterialSnackbar.prototype.Constant_ = {
  2118     // The duration of the snackbar show/hide animation, in ms.
  2119     ANIMATION_LENGTH: 250
  2120 };
  2121 /**
  2122    * Store strings for class names defined by this component that are used in
  2123    * JavaScript. This allows us to simply change it in one place should we
  2124    * decide to modify at a later date.
  2125    *
  2126    * @enum {string}
  2127    * @private
  2128    */
  2129 MaterialSnackbar.prototype.cssClasses_ = {
  2130     SNACKBAR: 'mdl-snackbar',
  2131     MESSAGE: 'mdl-snackbar__text',
  2132     ACTION: 'mdl-snackbar__action',
  2133     ACTIVE: 'mdl-snackbar--active'
  2134 };
  2135 /**
  2136    * Display the snackbar.
  2137    *
  2138    * @private
  2139    */
  2140 MaterialSnackbar.prototype.displaySnackbar_ = function () {
  2141     this.element_.setAttribute('aria-hidden', 'true');
  2142     if (this.actionHandler_) {
  2143         this.actionElement_.textContent = this.actionText_;
  2144         this.actionElement_.addEventListener('click', this.actionHandler_);
  2145         this.setActionHidden_(false);
  2146     }
  2147     this.textElement_.textContent = this.message_;
  2148     this.element_.classList.add(this.cssClasses_.ACTIVE);
  2149     this.element_.setAttribute('aria-hidden', 'false');
  2150     setTimeout(this.cleanup_.bind(this), this.timeout_);
  2151 };
  2152 /**
  2153    * Show the snackbar.
  2154    *
  2155    * @param {Object} data The data for the notification.
  2156    * @public
  2157    */
  2158 MaterialSnackbar.prototype.showSnackbar = function (data) {
  2159     if (data === undefined) {
  2160         throw new Error('Please provide a data object with at least a message to display.');
  2161     }
  2162     if (data['message'] === undefined) {
  2163         throw new Error('Please provide a message to be displayed.');
  2164     }
  2165     if (data['actionHandler'] && !data['actionText']) {
  2166         throw new Error('Please provide action text with the handler.');
  2167     }
  2168     if (this.active) {
  2169         this.queuedNotifications_.push(data);
  2170     } else {
  2171         this.active = true;
  2172         this.message_ = data['message'];
  2173         if (data['timeout']) {
  2174             this.timeout_ = data['timeout'];
  2175         } else {
  2176             this.timeout_ = 2750;
  2177         }
  2178         if (data['actionHandler']) {
  2179             this.actionHandler_ = data['actionHandler'];
  2180         }
  2181         if (data['actionText']) {
  2182             this.actionText_ = data['actionText'];
  2183         }
  2184         this.displaySnackbar_();
  2185     }
  2186 };
  2187 MaterialSnackbar.prototype['showSnackbar'] = MaterialSnackbar.prototype.showSnackbar;
  2188 /**
  2189    * Check if the queue has items within it.
  2190    * If it does, display the next entry.
  2191    *
  2192    * @private
  2193    */
  2194 MaterialSnackbar.prototype.checkQueue_ = function () {
  2195     if (this.queuedNotifications_.length > 0) {
  2196         this.showSnackbar(this.queuedNotifications_.shift());
  2197     }
  2198 };
  2199 /**
  2200    * Cleanup the snackbar event listeners and accessiblity attributes.
  2201    *
  2202    * @private
  2203    */
  2204 MaterialSnackbar.prototype.cleanup_ = function () {
  2205     this.element_.classList.remove(this.cssClasses_.ACTIVE);
  2206     setTimeout(function () {
  2207         this.element_.setAttribute('aria-hidden', 'true');
  2208         this.textElement_.textContent = '';
  2209         if (!Boolean(this.actionElement_.getAttribute('aria-hidden'))) {
  2210             this.setActionHidden_(true);
  2211             this.actionElement_.textContent = '';
  2212             this.actionElement_.removeEventListener('click', this.actionHandler_);
  2213         }
  2214         this.actionHandler_ = undefined;
  2215         this.message_ = undefined;
  2216         this.actionText_ = undefined;
  2217         this.active = false;
  2218         this.checkQueue_();
  2219     }.bind(this), this.Constant_.ANIMATION_LENGTH);
  2220 };
  2221 /**
  2222    * Set the action handler hidden state.
  2223    *
  2224    * @param {boolean} value
  2225    * @private
  2226    */
  2227 MaterialSnackbar.prototype.setActionHidden_ = function (value) {
  2228     if (value) {
  2229         this.actionElement_.setAttribute('aria-hidden', 'true');
  2230     } else {
  2231         this.actionElement_.removeAttribute('aria-hidden');
  2232     }
  2233 };
  2234 // The component registers itself. It can assume componentHandler is available
  2235 // in the global scope.
  2236 componentHandler.register({
  2237     constructor: MaterialSnackbar,
  2238     classAsString: 'MaterialSnackbar',
  2239     cssClass: 'mdl-js-snackbar',
  2240     widget: true
  2241 });
  2242 /**
  2243  * @license
  2244  * Copyright 2015 Google Inc. All Rights Reserved.
  2245  *
  2246  * Licensed under the Apache License, Version 2.0 (the "License");
  2247  * you may not use this file except in compliance with the License.
  2248  * You may obtain a copy of the License at
  2249  *
  2250  *      http://www.apache.org/licenses/LICENSE-2.0
  2251  *
  2252  * Unless required by applicable law or agreed to in writing, software
  2253  * distributed under the License is distributed on an "AS IS" BASIS,
  2254  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  2255  * See the License for the specific language governing permissions and
  2256  * limitations under the License.
  2257  */
  2258 /**
  2259    * Class constructor for Spinner MDL component.
  2260    * Implements MDL component design pattern defined at:
  2261    * https://github.com/jasonmayes/mdl-component-design-pattern
  2262    *
  2263    * @param {HTMLElement} element The element that will be upgraded.
  2264    * @constructor
  2265    */
  2266 var MaterialSpinner = function MaterialSpinner(element) {
  2267     this.element_ = element;
  2268     // Initialize instance.
  2269     this.init();
  2270 };
  2271 window['MaterialSpinner'] = MaterialSpinner;
  2272 /**
  2273    * Store constants in one place so they can be updated easily.
  2274    *
  2275    * @enum {string | number}
  2276    * @private
  2277    */
  2278 MaterialSpinner.prototype.Constant_ = { MDL_SPINNER_LAYER_COUNT: 4 };
  2279 /**
  2280    * Store strings for class names defined by this component that are used in
  2281    * JavaScript. This allows us to simply change it in one place should we
  2282    * decide to modify at a later date.
  2283    *
  2284    * @enum {string}
  2285    * @private
  2286    */
  2287 MaterialSpinner.prototype.CssClasses_ = {
  2288     MDL_SPINNER_LAYER: 'mdl-spinner__layer',
  2289     MDL_SPINNER_CIRCLE_CLIPPER: 'mdl-spinner__circle-clipper',
  2290     MDL_SPINNER_CIRCLE: 'mdl-spinner__circle',
  2291     MDL_SPINNER_GAP_PATCH: 'mdl-spinner__gap-patch',
  2292     MDL_SPINNER_LEFT: 'mdl-spinner__left',
  2293     MDL_SPINNER_RIGHT: 'mdl-spinner__right'
  2294 };
  2295 /**
  2296    * Auxiliary method to create a spinner layer.
  2297    *
  2298    * @param {number} index Index of the layer to be created.
  2299    * @public
  2300    */
  2301 MaterialSpinner.prototype.createLayer = function (index) {
  2302     var layer = document.createElement('div');
  2303     layer.classList.add(this.CssClasses_.MDL_SPINNER_LAYER);
  2304     layer.classList.add(this.CssClasses_.MDL_SPINNER_LAYER + '-' + index);
  2305     var leftClipper = document.createElement('div');
  2306     leftClipper.classList.add(this.CssClasses_.MDL_SPINNER_CIRCLE_CLIPPER);
  2307     leftClipper.classList.add(this.CssClasses_.MDL_SPINNER_LEFT);
  2308     var gapPatch = document.createElement('div');
  2309     gapPatch.classList.add(this.CssClasses_.MDL_SPINNER_GAP_PATCH);
  2310     var rightClipper = document.createElement('div');
  2311     rightClipper.classList.add(this.CssClasses_.MDL_SPINNER_CIRCLE_CLIPPER);
  2312     rightClipper.classList.add(this.CssClasses_.MDL_SPINNER_RIGHT);
  2313     var circleOwners = [
  2314         leftClipper,
  2315         gapPatch,
  2316         rightClipper
  2317     ];
  2318     for (var i = 0; i < circleOwners.length; i++) {
  2319         var circle = document.createElement('div');
  2320         circle.classList.add(this.CssClasses_.MDL_SPINNER_CIRCLE);
  2321         circleOwners[i].appendChild(circle);
  2322     }
  2323     layer.appendChild(leftClipper);
  2324     layer.appendChild(gapPatch);
  2325     layer.appendChild(rightClipper);
  2326     this.element_.appendChild(layer);
  2327 };
  2328 MaterialSpinner.prototype['createLayer'] = MaterialSpinner.prototype.createLayer;
  2329 /**
  2330    * Stops the spinner animation.
  2331    * Public method for users who need to stop the spinner for any reason.
  2332    *
  2333    * @public
  2334    */
  2335 MaterialSpinner.prototype.stop = function () {
  2336     this.element_.classList.remove('is-active');
  2337 };
  2338 MaterialSpinner.prototype['stop'] = MaterialSpinner.prototype.stop;
  2339 /**
  2340    * Starts the spinner animation.
  2341    * Public method for users who need to manually start the spinner for any reason
  2342    * (instead of just adding the 'is-active' class to their markup).
  2343    *
  2344    * @public
  2345    */
  2346 MaterialSpinner.prototype.start = function () {
  2347     this.element_.classList.add('is-active');
  2348 };
  2349 MaterialSpinner.prototype['start'] = MaterialSpinner.prototype.start;
  2350 /**
  2351    * Initialize element.
  2352    */
  2353 MaterialSpinner.prototype.init = function () {
  2354     if (this.element_) {
  2355         for (var i = 1; i <= this.Constant_.MDL_SPINNER_LAYER_COUNT; i++) {
  2356             this.createLayer(i);
  2357         }
  2358         this.element_.classList.add('is-upgraded');
  2359     }
  2360 };
  2361 // The component registers itself. It can assume componentHandler is available
  2362 // in the global scope.
  2363 componentHandler.register({
  2364     constructor: MaterialSpinner,
  2365     classAsString: 'MaterialSpinner',
  2366     cssClass: 'mdl-js-spinner',
  2367     widget: true
  2368 });
  2369 /**
  2370  * @license
  2371  * Copyright 2015 Google Inc. All Rights Reserved.
  2372  *
  2373  * Licensed under the Apache License, Version 2.0 (the "License");
  2374  * you may not use this file except in compliance with the License.
  2375  * You may obtain a copy of the License at
  2376  *
  2377  *      http://www.apache.org/licenses/LICENSE-2.0
  2378  *
  2379  * Unless required by applicable law or agreed to in writing, software
  2380  * distributed under the License is distributed on an "AS IS" BASIS,
  2381  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  2382  * See the License for the specific language governing permissions and
  2383  * limitations under the License.
  2384  */
  2385 /**
  2386    * Class constructor for Checkbox MDL component.
  2387    * Implements MDL component design pattern defined at:
  2388    * https://github.com/jasonmayes/mdl-component-design-pattern
  2389    *
  2390    * @constructor
  2391    * @param {HTMLElement} element The element that will be upgraded.
  2392    */
  2393 var MaterialSwitch = function MaterialSwitch(element) {
  2394     this.element_ = element;
  2395     // Initialize instance.
  2396     this.init();
  2397 };
  2398 window['MaterialSwitch'] = MaterialSwitch;
  2399 /**
  2400    * Store constants in one place so they can be updated easily.
  2401    *
  2402    * @enum {string | number}
  2403    * @private
  2404    */
  2405 MaterialSwitch.prototype.Constant_ = { TINY_TIMEOUT: 0.001 };
  2406 /**
  2407    * Store strings for class names defined by this component that are used in
  2408    * JavaScript. This allows us to simply change it in one place should we
  2409    * decide to modify at a later date.
  2410    *
  2411    * @enum {string}
  2412    * @private
  2413    */
  2414 MaterialSwitch.prototype.CssClasses_ = {
  2415     INPUT: 'mdl-switch__input',
  2416     TRACK: 'mdl-switch__track',
  2417     THUMB: 'mdl-switch__thumb',
  2418     FOCUS_HELPER: 'mdl-switch__focus-helper',
  2419     RIPPLE_EFFECT: 'mdl-js-ripple-effect',
  2420     RIPPLE_IGNORE_EVENTS: 'mdl-js-ripple-effect--ignore-events',
  2421     RIPPLE_CONTAINER: 'mdl-switch__ripple-container',
  2422     RIPPLE_CENTER: 'mdl-ripple--center',
  2423     RIPPLE: 'mdl-ripple',
  2424     IS_FOCUSED: 'is-focused',
  2425     IS_DISABLED: 'is-disabled',
  2426     IS_CHECKED: 'is-checked'
  2427 };
  2428 /**
  2429    * Handle change of state.
  2430    *
  2431    * @param {Event} event The event that fired.
  2432    * @private
  2433    */
  2434 MaterialSwitch.prototype.onChange_ = function (event) {
  2435     this.updateClasses_();
  2436 };
  2437 /**
  2438    * Handle focus of element.
  2439    *
  2440    * @param {Event} event The event that fired.
  2441    * @private
  2442    */
  2443 MaterialSwitch.prototype.onFocus_ = function (event) {
  2444     this.element_.classList.add(this.CssClasses_.IS_FOCUSED);
  2445 };
  2446 /**
  2447    * Handle lost focus of element.
  2448    *
  2449    * @param {Event} event The event that fired.
  2450    * @private
  2451    */
  2452 MaterialSwitch.prototype.onBlur_ = function (event) {
  2453     this.element_.classList.remove(this.CssClasses_.IS_FOCUSED);
  2454 };
  2455 /**
  2456    * Handle mouseup.
  2457    *
  2458    * @param {Event} event The event that fired.
  2459    * @private
  2460    */
  2461 MaterialSwitch.prototype.onMouseUp_ = function (event) {
  2462     this.blur_();
  2463 };
  2464 /**
  2465    * Handle class updates.
  2466    *
  2467    * @private
  2468    */
  2469 MaterialSwitch.prototype.updateClasses_ = function () {
  2470     this.checkDisabled();
  2471     this.checkToggleState();
  2472 };
  2473 /**
  2474    * Add blur.
  2475    *
  2476    * @private
  2477    */
  2478 MaterialSwitch.prototype.blur_ = function () {
  2479     // TODO: figure out why there's a focus event being fired after our blur,
  2480     // so that we can avoid this hack.
  2481     window.setTimeout(function () {
  2482         this.inputElement_.blur();
  2483     }.bind(this), this.Constant_.TINY_TIMEOUT);
  2484 };
  2485 // Public methods.
  2486 /**
  2487    * Check the components disabled state.
  2488    *
  2489    * @public
  2490    */
  2491 MaterialSwitch.prototype.checkDisabled = function () {
  2492     if (this.inputElement_.disabled) {
  2493         this.element_.classList.add(this.CssClasses_.IS_DISABLED);
  2494     } else {
  2495         this.element_.classList.remove(this.CssClasses_.IS_DISABLED);
  2496     }
  2497 };
  2498 MaterialSwitch.prototype['checkDisabled'] = MaterialSwitch.prototype.checkDisabled;
  2499 /**
  2500    * Check the components toggled state.
  2501    *
  2502    * @public
  2503    */
  2504 MaterialSwitch.prototype.checkToggleState = function () {
  2505     if (this.inputElement_.checked) {
  2506         this.element_.classList.add(this.CssClasses_.IS_CHECKED);
  2507     } else {
  2508         this.element_.classList.remove(this.CssClasses_.IS_CHECKED);
  2509     }
  2510 };
  2511 MaterialSwitch.prototype['checkToggleState'] = MaterialSwitch.prototype.checkToggleState;
  2512 /**
  2513    * Disable switch.
  2514    *
  2515    * @public
  2516    */
  2517 MaterialSwitch.prototype.disable = function () {
  2518     this.inputElement_.disabled = true;
  2519     this.updateClasses_();
  2520 };
  2521 MaterialSwitch.prototype['disable'] = MaterialSwitch.prototype.disable;
  2522 /**
  2523    * Enable switch.
  2524    *
  2525    * @public
  2526    */
  2527 MaterialSwitch.prototype.enable = function () {
  2528     this.inputElement_.disabled = false;
  2529     this.updateClasses_();
  2530 };
  2531 MaterialSwitch.prototype['enable'] = MaterialSwitch.prototype.enable;
  2532 /**
  2533    * Activate switch.
  2534    *
  2535    * @public
  2536    */
  2537 MaterialSwitch.prototype.on = function () {
  2538     this.inputElement_.checked = true;
  2539     this.updateClasses_();
  2540 };
  2541 MaterialSwitch.prototype['on'] = MaterialSwitch.prototype.on;
  2542 /**
  2543    * Deactivate switch.
  2544    *
  2545    * @public
  2546    */
  2547 MaterialSwitch.prototype.off = function () {
  2548     this.inputElement_.checked = false;
  2549     this.updateClasses_();
  2550 };
  2551 MaterialSwitch.prototype['off'] = MaterialSwitch.prototype.off;
  2552 /**
  2553    * Initialize element.
  2554    */
  2555 MaterialSwitch.prototype.init = function () {
  2556     if (this.element_) {
  2557         this.inputElement_ = this.element_.querySelector('.' + this.CssClasses_.INPUT);
  2558         var track = document.createElement('div');
  2559         track.classList.add(this.CssClasses_.TRACK);
  2560         var thumb = document.createElement('div');
  2561         thumb.classList.add(this.CssClasses_.THUMB);
  2562         var focusHelper = document.createElement('span');
  2563         focusHelper.classList.add(this.CssClasses_.FOCUS_HELPER);
  2564         thumb.appendChild(focusHelper);
  2565         this.element_.appendChild(track);
  2566         this.element_.appendChild(thumb);
  2567         this.boundMouseUpHandler = this.onMouseUp_.bind(this);
  2568         if (this.element_.classList.contains(this.CssClasses_.RIPPLE_EFFECT)) {
  2569             this.element_.classList.add(this.CssClasses_.RIPPLE_IGNORE_EVENTS);
  2570             this.rippleContainerElement_ = document.createElement('span');
  2571             this.rippleContainerElement_.classList.add(this.CssClasses_.RIPPLE_CONTAINER);
  2572             this.rippleContainerElement_.classList.add(this.CssClasses_.RIPPLE_EFFECT);
  2573             this.rippleContainerElement_.classList.add(this.CssClasses_.RIPPLE_CENTER);
  2574             this.rippleContainerElement_.addEventListener('mouseup', this.boundMouseUpHandler);
  2575             var ripple = document.createElement('span');
  2576             ripple.classList.add(this.CssClasses_.RIPPLE);
  2577             this.rippleContainerElement_.appendChild(ripple);
  2578             this.element_.appendChild(this.rippleContainerElement_);
  2579         }
  2580         this.boundChangeHandler = this.onChange_.bind(this);
  2581         this.boundFocusHandler = this.onFocus_.bind(this);
  2582         this.boundBlurHandler = this.onBlur_.bind(this);
  2583         this.inputElement_.addEventListener('change', this.boundChangeHandler);
  2584         this.inputElement_.addEventListener('focus', this.boundFocusHandler);
  2585         this.inputElement_.addEventListener('blur', this.boundBlurHandler);
  2586         this.element_.addEventListener('mouseup', this.boundMouseUpHandler);
  2587         this.updateClasses_();
  2588         this.element_.classList.add('is-upgraded');
  2589     }
  2590 };
  2591 // The component registers itself. It can assume componentHandler is available
  2592 // in the global scope.
  2593 componentHandler.register({
  2594     constructor: MaterialSwitch,
  2595     classAsString: 'MaterialSwitch',
  2596     cssClass: 'mdl-js-switch',
  2597     widget: true
  2598 });
  2599 /**
  2600  * @license
  2601  * Copyright 2015 Google Inc. All Rights Reserved.
  2602  *
  2603  * Licensed under the Apache License, Version 2.0 (the "License");
  2604  * you may not use this file except in compliance with the License.
  2605  * You may obtain a copy of the License at
  2606  *
  2607  *      http://www.apache.org/licenses/LICENSE-2.0
  2608  *
  2609  * Unless required by applicable law or agreed to in writing, software
  2610  * distributed under the License is distributed on an "AS IS" BASIS,
  2611  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  2612  * See the License for the specific language governing permissions and
  2613  * limitations under the License.
  2614  */
  2615 /**
  2616    * Class constructor for Tabs MDL component.
  2617    * Implements MDL component design pattern defined at:
  2618    * https://github.com/jasonmayes/mdl-component-design-pattern
  2619    *
  2620    * @constructor
  2621    * @param {Element} element The element that will be upgraded.
  2622    */
  2623 var MaterialTabs = function MaterialTabs(element) {
  2624     // Stores the HTML element.
  2625     this.element_ = element;
  2626     // Initialize instance.
  2627     this.init();
  2628 };
  2629 window['MaterialTabs'] = MaterialTabs;
  2630 /**
  2631    * Store constants in one place so they can be updated easily.
  2632    *
  2633    * @enum {string}
  2634    * @private
  2635    */
  2636 MaterialTabs.prototype.Constant_ = {};
  2637 /**
  2638    * Store strings for class names defined by this component that are used in
  2639    * JavaScript. This allows us to simply change it in one place should we
  2640    * decide to modify at a later date.
  2641    *
  2642    * @enum {string}
  2643    * @private
  2644    */
  2645 MaterialTabs.prototype.CssClasses_ = {
  2646     TAB_CLASS: 'mdl-tabs__tab',
  2647     PANEL_CLASS: 'mdl-tabs__panel',
  2648     ACTIVE_CLASS: 'is-active',
  2649     UPGRADED_CLASS: 'is-upgraded',
  2650     MDL_JS_RIPPLE_EFFECT: 'mdl-js-ripple-effect',
  2651     MDL_RIPPLE_CONTAINER: 'mdl-tabs__ripple-container',
  2652     MDL_RIPPLE: 'mdl-ripple',
  2653     MDL_JS_RIPPLE_EFFECT_IGNORE_EVENTS: 'mdl-js-ripple-effect--ignore-events'
  2654 };
  2655 /**
  2656    * Handle clicks to a tabs component
  2657    *
  2658    * @private
  2659    */
  2660 MaterialTabs.prototype.initTabs_ = function () {
  2661     if (this.element_.classList.contains(this.CssClasses_.MDL_JS_RIPPLE_EFFECT)) {
  2662         this.element_.classList.add(this.CssClasses_.MDL_JS_RIPPLE_EFFECT_IGNORE_EVENTS);
  2663     }
  2664     // Select element tabs, document panels
  2665     this.tabs_ = this.element_.querySelectorAll('.' + this.CssClasses_.TAB_CLASS);
  2666     this.panels_ = this.element_.querySelectorAll('.' + this.CssClasses_.PANEL_CLASS);
  2667     // Create new tabs for each tab element
  2668     for (var i = 0; i < this.tabs_.length; i++) {
  2669         new MaterialTab(this.tabs_[i], this);
  2670     }
  2671     this.element_.classList.add(this.CssClasses_.UPGRADED_CLASS);
  2672 };
  2673 /**
  2674    * Reset tab state, dropping active classes
  2675    *
  2676    * @private
  2677    */
  2678 MaterialTabs.prototype.resetTabState_ = function () {
  2679     for (var k = 0; k < this.tabs_.length; k++) {
  2680         this.tabs_[k].classList.remove(this.CssClasses_.ACTIVE_CLASS);
  2681     }
  2682 };
  2683 /**
  2684    * Reset panel state, droping active classes
  2685    *
  2686    * @private
  2687    */
  2688 MaterialTabs.prototype.resetPanelState_ = function () {
  2689     for (var j = 0; j < this.panels_.length; j++) {
  2690         this.panels_[j].classList.remove(this.CssClasses_.ACTIVE_CLASS);
  2691     }
  2692 };
  2693 /**
  2694    * Initialize element.
  2695    */
  2696 MaterialTabs.prototype.init = function () {
  2697     if (this.element_) {
  2698         this.initTabs_();
  2699     }
  2700 };
  2701 /**
  2702    * Constructor for an individual tab.
  2703    *
  2704    * @constructor
  2705    * @param {Element} tab The HTML element for the tab.
  2706    * @param {MaterialTabs} ctx The MaterialTabs object that owns the tab.
  2707    */
  2708 function MaterialTab(tab, ctx) {
  2709     if (tab) {
  2710         if (ctx.element_.classList.contains(ctx.CssClasses_.MDL_JS_RIPPLE_EFFECT)) {
  2711             var rippleContainer = document.createElement('span');
  2712             rippleContainer.classList.add(ctx.CssClasses_.MDL_RIPPLE_CONTAINER);
  2713             rippleContainer.classList.add(ctx.CssClasses_.MDL_JS_RIPPLE_EFFECT);
  2714             var ripple = document.createElement('span');
  2715             ripple.classList.add(ctx.CssClasses_.MDL_RIPPLE);
  2716             rippleContainer.appendChild(ripple);
  2717             tab.appendChild(rippleContainer);
  2718         }
  2719         tab.addEventListener('click', function (e) {
  2720 	    if (tab.getAttribute('href').charAt(0) === '#') {
  2721 		e.preventDefault();
  2722 	    }
  2723             var href = tab.href.split('#')[1];
  2724             var panel = ctx.element_.querySelector('#' + href);
  2725             ctx.resetTabState_();
  2726             ctx.resetPanelState_();
  2727             tab.classList.add(ctx.CssClasses_.ACTIVE_CLASS);
  2728 	    if (panel) {
  2729 	      panel.classList.add(ctx.CssClasses_.ACTIVE_CLASS);
  2730 	    }
  2731         });
  2732     }
  2733 }
  2734 // The component registers itself. It can assume componentHandler is available
  2735 // in the global scope.
  2736 componentHandler.register({
  2737     constructor: MaterialTabs,
  2738     classAsString: 'MaterialTabs',
  2739     cssClass: 'mdl-js-tabs'
  2740 });
  2741 /**
  2742  * @license
  2743  * Copyright 2015 Google Inc. All Rights Reserved.
  2744  *
  2745  * Licensed under the Apache License, Version 2.0 (the "License");
  2746  * you may not use this file except in compliance with the License.
  2747  * You may obtain a copy of the License at
  2748  *
  2749  *      http://www.apache.org/licenses/LICENSE-2.0
  2750  *
  2751  * Unless required by applicable law or agreed to in writing, software
  2752  * distributed under the License is distributed on an "AS IS" BASIS,
  2753  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  2754  * See the License for the specific language governing permissions and
  2755  * limitations under the License.
  2756  */
  2757 /**
  2758    * Class constructor for Textfield MDL component.
  2759    * Implements MDL component design pattern defined at:
  2760    * https://github.com/jasonmayes/mdl-component-design-pattern
  2761    *
  2762    * @constructor
  2763    * @param {HTMLElement} element The element that will be upgraded.
  2764    */
  2765 var MaterialTextfield = function MaterialTextfield(element) {
  2766     this.element_ = element;
  2767     this.maxRows = this.Constant_.NO_MAX_ROWS;
  2768     // Initialize instance.
  2769     this.init();
  2770 };
  2771 window['MaterialTextfield'] = MaterialTextfield;
  2772 /**
  2773    * Store constants in one place so they can be updated easily.
  2774    *
  2775    * @enum {string | number}
  2776    * @private
  2777    */
  2778 MaterialTextfield.prototype.Constant_ = {
  2779     NO_MAX_ROWS: -1,
  2780     MAX_ROWS_ATTRIBUTE: 'maxrows'
  2781 };
  2782 /**
  2783    * Store strings for class names defined by this component that are used in
  2784    * JavaScript. This allows us to simply change it in one place should we
  2785    * decide to modify at a later date.
  2786    *
  2787    * @enum {string}
  2788    * @private
  2789    */
  2790 MaterialTextfield.prototype.CssClasses_ = {
  2791     LABEL: 'mdl-textfield__label',
  2792     INPUT: 'mdl-textfield__input',
  2793     IS_DIRTY: 'is-dirty',
  2794     IS_FOCUSED: 'is-focused',
  2795     IS_DISABLED: 'is-disabled',
  2796     IS_INVALID: 'is-invalid',
  2797     IS_UPGRADED: 'is-upgraded',
  2798     HAS_PLACEHOLDER: 'has-placeholder'
  2799 };
  2800 /**
  2801    * Handle input being entered.
  2802    *
  2803    * @param {Event} event The event that fired.
  2804    * @private
  2805    */
  2806 MaterialTextfield.prototype.onKeyDown_ = function (event) {
  2807     var currentRowCount = event.target.value.split('\n').length;
  2808     if (event.keyCode === 13) {
  2809         if (currentRowCount >= this.maxRows) {
  2810             event.preventDefault();
  2811         }
  2812     }
  2813 };
  2814 /**
  2815    * Handle focus.
  2816    *
  2817    * @param {Event} event The event that fired.
  2818    * @private
  2819    */
  2820 MaterialTextfield.prototype.onFocus_ = function (event) {
  2821     this.element_.classList.add(this.CssClasses_.IS_FOCUSED);
  2822 };
  2823 /**
  2824    * Handle lost focus.
  2825    *
  2826    * @param {Event} event The event that fired.
  2827    * @private
  2828    */
  2829 MaterialTextfield.prototype.onBlur_ = function (event) {
  2830     this.element_.classList.remove(this.CssClasses_.IS_FOCUSED);
  2831 };
  2832 /**
  2833    * Handle reset event from out side.
  2834    *
  2835    * @param {Event} event The event that fired.
  2836    * @private
  2837    */
  2838 MaterialTextfield.prototype.onReset_ = function (event) {
  2839     this.updateClasses_();
  2840 };
  2841 /**
  2842    * Handle class updates.
  2843    *
  2844    * @private
  2845    */
  2846 MaterialTextfield.prototype.updateClasses_ = function () {
  2847     this.checkDisabled();
  2848     this.checkValidity();
  2849     this.checkDirty();
  2850     this.checkFocus();
  2851 };
  2852 // Public methods.
  2853 /**
  2854    * Check the disabled state and update field accordingly.
  2855    *
  2856    * @public
  2857    */
  2858 MaterialTextfield.prototype.checkDisabled = function () {
  2859     if (this.input_.disabled) {
  2860         this.element_.classList.add(this.CssClasses_.IS_DISABLED);
  2861     } else {
  2862         this.element_.classList.remove(this.CssClasses_.IS_DISABLED);
  2863     }
  2864 };
  2865 MaterialTextfield.prototype['checkDisabled'] = MaterialTextfield.prototype.checkDisabled;
  2866 /**
  2867   * Check the focus state and update field accordingly.
  2868   *
  2869   * @public
  2870   */
  2871 MaterialTextfield.prototype.checkFocus = function () {
  2872     if (Boolean(this.element_.querySelector(':focus'))) {
  2873         this.element_.classList.add(this.CssClasses_.IS_FOCUSED);
  2874     } else {
  2875         this.element_.classList.remove(this.CssClasses_.IS_FOCUSED);
  2876     }
  2877 };
  2878 MaterialTextfield.prototype['checkFocus'] = MaterialTextfield.prototype.checkFocus;
  2879 /**
  2880    * Check the validity state and update field accordingly.
  2881    *
  2882    * @public
  2883    */
  2884 MaterialTextfield.prototype.checkValidity = function () {
  2885     if (this.input_.validity) {
  2886         if (this.input_.validity.valid) {
  2887             this.element_.classList.remove(this.CssClasses_.IS_INVALID);
  2888         } else {
  2889             this.element_.classList.add(this.CssClasses_.IS_INVALID);
  2890         }
  2891     }
  2892 };
  2893 MaterialTextfield.prototype['checkValidity'] = MaterialTextfield.prototype.checkValidity;
  2894 /**
  2895    * Check the dirty state and update field accordingly.
  2896    *
  2897    * @public
  2898    */
  2899 MaterialTextfield.prototype.checkDirty = function () {
  2900     if (this.input_.value && this.input_.value.length > 0) {
  2901         this.element_.classList.add(this.CssClasses_.IS_DIRTY);
  2902     } else {
  2903         this.element_.classList.remove(this.CssClasses_.IS_DIRTY);
  2904     }
  2905 };
  2906 MaterialTextfield.prototype['checkDirty'] = MaterialTextfield.prototype.checkDirty;
  2907 /**
  2908    * Disable text field.
  2909    *
  2910    * @public
  2911    */
  2912 MaterialTextfield.prototype.disable = function () {
  2913     this.input_.disabled = true;
  2914     this.updateClasses_();
  2915 };
  2916 MaterialTextfield.prototype['disable'] = MaterialTextfield.prototype.disable;
  2917 /**
  2918    * Enable text field.
  2919    *
  2920    * @public
  2921    */
  2922 MaterialTextfield.prototype.enable = function () {
  2923     this.input_.disabled = false;
  2924     this.updateClasses_();
  2925 };
  2926 MaterialTextfield.prototype['enable'] = MaterialTextfield.prototype.enable;
  2927 /**
  2928    * Update text field value.
  2929    *
  2930    * @param {string} value The value to which to set the control (optional).
  2931    * @public
  2932    */
  2933 MaterialTextfield.prototype.change = function (value) {
  2934     this.input_.value = value || '';
  2935     this.updateClasses_();
  2936 };
  2937 MaterialTextfield.prototype['change'] = MaterialTextfield.prototype.change;
  2938 /**
  2939    * Initialize element.
  2940    */
  2941 MaterialTextfield.prototype.init = function () {
  2942     if (this.element_) {
  2943         this.label_ = this.element_.querySelector('.' + this.CssClasses_.LABEL);
  2944         this.input_ = this.element_.querySelector('.' + this.CssClasses_.INPUT);
  2945         if (this.input_) {
  2946             if (this.input_.hasAttribute(this.Constant_.MAX_ROWS_ATTRIBUTE)) {
  2947                 this.maxRows = parseInt(this.input_.getAttribute(this.Constant_.MAX_ROWS_ATTRIBUTE), 10);
  2948                 if (isNaN(this.maxRows)) {
  2949                     this.maxRows = this.Constant_.NO_MAX_ROWS;
  2950                 }
  2951             }
  2952             if (this.input_.hasAttribute('placeholder')) {
  2953                 this.element_.classList.add(this.CssClasses_.HAS_PLACEHOLDER);
  2954             }
  2955             this.boundUpdateClassesHandler = this.updateClasses_.bind(this);
  2956             this.boundFocusHandler = this.onFocus_.bind(this);
  2957             this.boundBlurHandler = this.onBlur_.bind(this);
  2958             this.boundResetHandler = this.onReset_.bind(this);
  2959             this.input_.addEventListener('input', this.boundUpdateClassesHandler);
  2960             this.input_.addEventListener('focus', this.boundFocusHandler);
  2961             this.input_.addEventListener('blur', this.boundBlurHandler);
  2962             this.input_.addEventListener('reset', this.boundResetHandler);
  2963             if (this.maxRows !== this.Constant_.NO_MAX_ROWS) {
  2964                 // TODO: This should handle pasting multi line text.
  2965                 // Currently doesn't.
  2966                 this.boundKeyDownHandler = this.onKeyDown_.bind(this);
  2967                 this.input_.addEventListener('keydown', this.boundKeyDownHandler);
  2968             }
  2969             var invalid = this.element_.classList.contains(this.CssClasses_.IS_INVALID);
  2970             this.updateClasses_();
  2971             this.element_.classList.add(this.CssClasses_.IS_UPGRADED);
  2972             if (invalid) {
  2973                 this.element_.classList.add(this.CssClasses_.IS_INVALID);
  2974             }
  2975             if (this.input_.hasAttribute('autofocus')) {
  2976                 this.element_.focus();
  2977                 this.checkFocus();
  2978             }
  2979         }
  2980     }
  2981 };
  2982 // The component registers itself. It can assume componentHandler is available
  2983 // in the global scope.
  2984 componentHandler.register({
  2985     constructor: MaterialTextfield,
  2986     classAsString: 'MaterialTextfield',
  2987     cssClass: 'mdl-js-textfield',
  2988     widget: true
  2989 });
  2990 /**
  2991  * @license
  2992  * Copyright 2015 Google Inc. All Rights Reserved.
  2993  *
  2994  * Licensed under the Apache License, Version 2.0 (the "License");
  2995  * you may not use this file except in compliance with the License.
  2996  * You may obtain a copy of the License at
  2997  *
  2998  *      http://www.apache.org/licenses/LICENSE-2.0
  2999  *
  3000  * Unless required by applicable law or agreed to in writing, software
  3001  * distributed under the License is distributed on an "AS IS" BASIS,
  3002  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  3003  * See the License for the specific language governing permissions and
  3004  * limitations under the License.
  3005  */
  3006 /**
  3007    * Class constructor for Tooltip MDL component.
  3008    * Implements MDL component design pattern defined at:
  3009    * https://github.com/jasonmayes/mdl-component-design-pattern
  3010    *
  3011    * @constructor
  3012    * @param {HTMLElement} element The element that will be upgraded.
  3013    */
  3014 var MaterialTooltip = function MaterialTooltip(element) {
  3015     this.element_ = element;
  3016     // Initialize instance.
  3017     this.init();
  3018 };
  3019 window['MaterialTooltip'] = MaterialTooltip;
  3020 /**
  3021    * Store constants in one place so they can be updated easily.
  3022    *
  3023    * @enum {string | number}
  3024    * @private
  3025    */
  3026 MaterialTooltip.prototype.Constant_ = {};
  3027 /**
  3028    * Store strings for class names defined by this component that are used in
  3029    * JavaScript. This allows us to simply change it in one place should we
  3030    * decide to modify at a later date.
  3031    *
  3032    * @enum {string}
  3033    * @private
  3034    */
  3035 MaterialTooltip.prototype.CssClasses_ = {
  3036     IS_ACTIVE: 'is-active',
  3037     BOTTOM: 'mdl-tooltip--bottom',
  3038     LEFT: 'mdl-tooltip--left',
  3039     RIGHT: 'mdl-tooltip--right',
  3040     TOP: 'mdl-tooltip--top'
  3041 };
  3042 /**
  3043    * Handle mouseenter for tooltip.
  3044    *
  3045    * @param {Event} event The event that fired.
  3046    * @private
  3047    */
  3048 MaterialTooltip.prototype.handleMouseEnter_ = function (event) {
  3049     var props = event.target.getBoundingClientRect();
  3050     var left = props.left + props.width / 2;
  3051     var top = props.top + props.height / 2;
  3052     var marginLeft = -1 * (this.element_.offsetWidth / 2);
  3053     var marginTop = -1 * (this.element_.offsetHeight / 2);
  3054     if (this.element_.classList.contains(this.CssClasses_.LEFT) || this.element_.classList.contains(this.CssClasses_.RIGHT)) {
  3055         left = props.width / 2;
  3056         if (top + marginTop < 0) {
  3057             this.element_.style.top = '0';
  3058             this.element_.style.marginTop = '0';
  3059         } else {
  3060             this.element_.style.top = top + 'px';
  3061             this.element_.style.marginTop = marginTop + 'px';
  3062         }
  3063     } else {
  3064         if (left + marginLeft < 0) {
  3065             this.element_.style.left = '0';
  3066             this.element_.style.marginLeft = '0';
  3067         } else {
  3068             this.element_.style.left = left + 'px';
  3069             this.element_.style.marginLeft = marginLeft + 'px';
  3070         }
  3071     }
  3072     if (this.element_.classList.contains(this.CssClasses_.TOP)) {
  3073         this.element_.style.top = props.top - this.element_.offsetHeight - 10 + 'px';
  3074     } else if (this.element_.classList.contains(this.CssClasses_.RIGHT)) {
  3075         this.element_.style.left = props.left + props.width + 10 + 'px';
  3076     } else if (this.element_.classList.contains(this.CssClasses_.LEFT)) {
  3077         this.element_.style.left = props.left - this.element_.offsetWidth - 10 + 'px';
  3078     } else {
  3079         this.element_.style.top = props.top + props.height + 10 + 'px';
  3080     }
  3081     this.element_.classList.add(this.CssClasses_.IS_ACTIVE);
  3082 };
  3083 /**
  3084    * Hide tooltip on mouseleave or scroll
  3085    *
  3086    * @private
  3087    */
  3088 MaterialTooltip.prototype.hideTooltip_ = function () {
  3089     this.element_.classList.remove(this.CssClasses_.IS_ACTIVE);
  3090 };
  3091 /**
  3092    * Initialize element.
  3093    */
  3094 MaterialTooltip.prototype.init = function () {
  3095     if (this.element_) {
  3096         var forElId = this.element_.getAttribute('for') || this.element_.getAttribute('data-mdl-for');
  3097         if (forElId) {
  3098             this.forElement_ = document.getElementById(forElId);
  3099         }
  3100         if (this.forElement_) {
  3101             // It's left here because it prevents accidental text selection on Android
  3102             if (!this.forElement_.hasAttribute('tabindex')) {
  3103                 this.forElement_.setAttribute('tabindex', '0');
  3104             }
  3105             this.boundMouseEnterHandler = this.handleMouseEnter_.bind(this);
  3106             this.boundMouseLeaveAndScrollHandler = this.hideTooltip_.bind(this);
  3107             this.forElement_.addEventListener('mouseenter', this.boundMouseEnterHandler, false);
  3108             this.forElement_.addEventListener('touchend', this.boundMouseEnterHandler, false);
  3109             this.forElement_.addEventListener('mouseleave', this.boundMouseLeaveAndScrollHandler, false);
  3110             window.addEventListener('scroll', this.boundMouseLeaveAndScrollHandler, true);
  3111             window.addEventListener('touchstart', this.boundMouseLeaveAndScrollHandler);
  3112         }
  3113     }
  3114 };
  3115 // The component registers itself. It can assume componentHandler is available
  3116 // in the global scope.
  3117 componentHandler.register({
  3118     constructor: MaterialTooltip,
  3119     classAsString: 'MaterialTooltip',
  3120     cssClass: 'mdl-tooltip'
  3121 });
  3122 /**
  3123  * @license
  3124  * Copyright 2015 Google Inc. All Rights Reserved.
  3125  *
  3126  * Licensed under the Apache License, Version 2.0 (the "License");
  3127  * you may not use this file except in compliance with the License.
  3128  * You may obtain a copy of the License at
  3129  *
  3130  *      http://www.apache.org/licenses/LICENSE-2.0
  3131  *
  3132  * Unless required by applicable law or agreed to in writing, software
  3133  * distributed under the License is distributed on an "AS IS" BASIS,
  3134  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  3135  * See the License for the specific language governing permissions and
  3136  * limitations under the License.
  3137  */
  3138 /**
  3139    * Class constructor for Layout MDL component.
  3140    * Implements MDL component design pattern defined at:
  3141    * https://github.com/jasonmayes/mdl-component-design-pattern
  3142    *
  3143    * @constructor
  3144    * @param {HTMLElement} element The element that will be upgraded.
  3145    */
  3146 var MaterialLayout = function MaterialLayout(element) {
  3147     this.element_ = element;
  3148     // Initialize instance.
  3149     this.init();
  3150 };
  3151 window['MaterialLayout'] = MaterialLayout;
  3152 /**
  3153    * Store constants in one place so they can be updated easily.
  3154    *
  3155    * @enum {string | number}
  3156    * @private
  3157    */
  3158 MaterialLayout.prototype.Constant_ = {
  3159     MAX_WIDTH: '(max-width: 1024px)',
  3160     TAB_SCROLL_PIXELS: 100,
  3161     RESIZE_TIMEOUT: 100,
  3162     MENU_ICON: '',
  3163     CHEVRON_LEFT: 'chevron_left',
  3164     CHEVRON_RIGHT: 'chevron_right'
  3165 };
  3166 /**
  3167    * Keycodes, for code readability.
  3168    *
  3169    * @enum {number}
  3170    * @private
  3171    */
  3172 MaterialLayout.prototype.Keycodes_ = {
  3173     ENTER: 13,
  3174     ESCAPE: 27,
  3175     SPACE: 32
  3176 };
  3177 /**
  3178    * Modes.
  3179    *
  3180    * @enum {number}
  3181    * @private
  3182    */
  3183 MaterialLayout.prototype.Mode_ = {
  3184     STANDARD: 0,
  3185     SEAMED: 1,
  3186     WATERFALL: 2,
  3187     SCROLL: 3
  3188 };
  3189 /**
  3190    * Store strings for class names defined by this component that are used in
  3191    * JavaScript. This allows us to simply change it in one place should we
  3192    * decide to modify at a later date.
  3193    *
  3194    * @enum {string}
  3195    * @private
  3196    */
  3197 MaterialLayout.prototype.CssClasses_ = {
  3198     CONTAINER: 'mdl-layout__container',
  3199     HEADER: 'mdl-layout__header',
  3200     DRAWER: 'mdl-layout__drawer',
  3201     CONTENT: 'mdl-layout__content',
  3202     DRAWER_BTN: 'mdl-layout__drawer-button',
  3203     ICON: 'material-icons',
  3204     JS_RIPPLE_EFFECT: 'mdl-js-ripple-effect',
  3205     RIPPLE_CONTAINER: 'mdl-layout__tab-ripple-container',
  3206     RIPPLE: 'mdl-ripple',
  3207     RIPPLE_IGNORE_EVENTS: 'mdl-js-ripple-effect--ignore-events',
  3208     HEADER_SEAMED: 'mdl-layout__header--seamed',
  3209     HEADER_WATERFALL: 'mdl-layout__header--waterfall',
  3210     HEADER_SCROLL: 'mdl-layout__header--scroll',
  3211     FIXED_HEADER: 'mdl-layout--fixed-header',
  3212     OBFUSCATOR: 'mdl-layout__obfuscator',
  3213     TAB_BAR: 'mdl-layout__tab-bar',
  3214     TAB_CONTAINER: 'mdl-layout__tab-bar-container',
  3215     TAB: 'mdl-layout__tab',
  3216     TAB_BAR_BUTTON: 'mdl-layout__tab-bar-button',
  3217     TAB_BAR_LEFT_BUTTON: 'mdl-layout__tab-bar-left-button',
  3218     TAB_BAR_RIGHT_BUTTON: 'mdl-layout__tab-bar-right-button',
  3219     PANEL: 'mdl-layout__tab-panel',
  3220     HAS_DRAWER: 'has-drawer',
  3221     HAS_TABS: 'has-tabs',
  3222     HAS_SCROLLING_HEADER: 'has-scrolling-header',
  3223     CASTING_SHADOW: 'is-casting-shadow',
  3224     IS_COMPACT: 'is-compact',
  3225     IS_SMALL_SCREEN: 'is-small-screen',
  3226     IS_DRAWER_OPEN: 'is-visible',
  3227     IS_ACTIVE: 'is-active',
  3228     IS_UPGRADED: 'is-upgraded',
  3229     IS_ANIMATING: 'is-animating',
  3230     ON_LARGE_SCREEN: 'mdl-layout--large-screen-only',
  3231     ON_SMALL_SCREEN: 'mdl-layout--small-screen-only'
  3232 };
  3233 /**
  3234    * Handles scrolling on the content.
  3235    *
  3236    * @private
  3237    */
  3238 MaterialLayout.prototype.contentScrollHandler_ = function () {
  3239     if (this.header_.classList.contains(this.CssClasses_.IS_ANIMATING)) {
  3240         return;
  3241     }
  3242     var headerVisible = !this.element_.classList.contains(this.CssClasses_.IS_SMALL_SCREEN) || this.element_.classList.contains(this.CssClasses_.FIXED_HEADER);
  3243     if (this.content_.scrollTop > 0 && !this.header_.classList.contains(this.CssClasses_.IS_COMPACT)) {
  3244         this.header_.classList.add(this.CssClasses_.CASTING_SHADOW);
  3245         this.header_.classList.add(this.CssClasses_.IS_COMPACT);
  3246         if (headerVisible) {
  3247             this.header_.classList.add(this.CssClasses_.IS_ANIMATING);
  3248         }
  3249     } else if (this.content_.scrollTop <= 0 && this.header_.classList.contains(this.CssClasses_.IS_COMPACT)) {
  3250         this.header_.classList.remove(this.CssClasses_.CASTING_SHADOW);
  3251         this.header_.classList.remove(this.CssClasses_.IS_COMPACT);
  3252         if (headerVisible) {
  3253             this.header_.classList.add(this.CssClasses_.IS_ANIMATING);
  3254         }
  3255     }
  3256 };
  3257 /**
  3258    * Handles a keyboard event on the drawer.
  3259    *
  3260    * @param {Event} evt The event that fired.
  3261    * @private
  3262    */
  3263 MaterialLayout.prototype.keyboardEventHandler_ = function (evt) {
  3264     // Only react when the drawer is open.
  3265     if (evt.keyCode === this.Keycodes_.ESCAPE && this.drawer_.classList.contains(this.CssClasses_.IS_DRAWER_OPEN)) {
  3266         this.toggleDrawer();
  3267     }
  3268 };
  3269 /**
  3270    * Handles changes in screen size.
  3271    *
  3272    * @private
  3273    */
  3274 MaterialLayout.prototype.screenSizeHandler_ = function () {
  3275     if (this.screenSizeMediaQuery_.matches) {
  3276         this.element_.classList.add(this.CssClasses_.IS_SMALL_SCREEN);
  3277     } else {
  3278         this.element_.classList.remove(this.CssClasses_.IS_SMALL_SCREEN);
  3279         // Collapse drawer (if any) when moving to a large screen size.
  3280         if (this.drawer_) {
  3281             this.drawer_.classList.remove(this.CssClasses_.IS_DRAWER_OPEN);
  3282             this.obfuscator_.classList.remove(this.CssClasses_.IS_DRAWER_OPEN);
  3283         }
  3284     }
  3285 };
  3286 /**
  3287    * Handles events of drawer button.
  3288    *
  3289    * @param {Event} evt The event that fired.
  3290    * @private
  3291    */
  3292 MaterialLayout.prototype.drawerToggleHandler_ = function (evt) {
  3293     if (evt && evt.type === 'keydown') {
  3294         if (evt.keyCode === this.Keycodes_.SPACE || evt.keyCode === this.Keycodes_.ENTER) {
  3295             // prevent scrolling in drawer nav
  3296             evt.preventDefault();
  3297         } else {
  3298             // prevent other keys
  3299             return;
  3300         }
  3301     }
  3302     this.toggleDrawer();
  3303 };
  3304 /**
  3305    * Handles (un)setting the `is-animating` class
  3306    *
  3307    * @private
  3308    */
  3309 MaterialLayout.prototype.headerTransitionEndHandler_ = function () {
  3310     this.header_.classList.remove(this.CssClasses_.IS_ANIMATING);
  3311 };
  3312 /**
  3313    * Handles expanding the header on click
  3314    *
  3315    * @private
  3316    */
  3317 MaterialLayout.prototype.headerClickHandler_ = function () {
  3318     if (this.header_.classList.contains(this.CssClasses_.IS_COMPACT)) {
  3319         this.header_.classList.remove(this.CssClasses_.IS_COMPACT);
  3320         this.header_.classList.add(this.CssClasses_.IS_ANIMATING);
  3321     }
  3322 };
  3323 /**
  3324    * Reset tab state, dropping active classes
  3325    *
  3326    * @private
  3327    */
  3328 MaterialLayout.prototype.resetTabState_ = function (tabBar) {
  3329     for (var k = 0; k < tabBar.length; k++) {
  3330         tabBar[k].classList.remove(this.CssClasses_.IS_ACTIVE);
  3331     }
  3332 };
  3333 /**
  3334    * Reset panel state, droping active classes
  3335    *
  3336    * @private
  3337    */
  3338 MaterialLayout.prototype.resetPanelState_ = function (panels) {
  3339     for (var j = 0; j < panels.length; j++) {
  3340         panels[j].classList.remove(this.CssClasses_.IS_ACTIVE);
  3341     }
  3342 };
  3343 /**
  3344   * Toggle drawer state
  3345   *
  3346   * @public
  3347   */
  3348 MaterialLayout.prototype.toggleDrawer = function () {
  3349     var drawerButton = this.element_.querySelector('.' + this.CssClasses_.DRAWER_BTN);
  3350     this.drawer_.classList.toggle(this.CssClasses_.IS_DRAWER_OPEN);
  3351     this.obfuscator_.classList.toggle(this.CssClasses_.IS_DRAWER_OPEN);
  3352     // Set accessibility properties.
  3353     if (this.drawer_.classList.contains(this.CssClasses_.IS_DRAWER_OPEN)) {
  3354         this.drawer_.setAttribute('aria-hidden', 'false');
  3355         drawerButton.setAttribute('aria-expanded', 'true');
  3356     } else {
  3357         this.drawer_.setAttribute('aria-hidden', 'true');
  3358         drawerButton.setAttribute('aria-expanded', 'false');
  3359     }
  3360 };
  3361 MaterialLayout.prototype['toggleDrawer'] = MaterialLayout.prototype.toggleDrawer;
  3362 /**
  3363    * Initialize element.
  3364    */
  3365 MaterialLayout.prototype.init = function () {
  3366     if (this.element_) {
  3367         var container = document.createElement('div');
  3368         container.classList.add(this.CssClasses_.CONTAINER);
  3369         var focusedElement = this.element_.querySelector(':focus');
  3370         this.element_.parentElement.insertBefore(container, this.element_);
  3371         this.element_.parentElement.removeChild(this.element_);
  3372         container.appendChild(this.element_);
  3373         if (focusedElement) {
  3374             focusedElement.focus();
  3375         }
  3376         var directChildren = this.element_.childNodes;
  3377         var numChildren = directChildren.length;
  3378         for (var c = 0; c < numChildren; c++) {
  3379             var child = directChildren[c];
  3380             if (child.classList && child.classList.contains(this.CssClasses_.HEADER)) {
  3381                 this.header_ = child;
  3382             }
  3383             if (child.classList && child.classList.contains(this.CssClasses_.DRAWER)) {
  3384                 this.drawer_ = child;
  3385             }
  3386             if (child.classList && child.classList.contains(this.CssClasses_.CONTENT)) {
  3387                 this.content_ = child;
  3388             }
  3389         }
  3390         window.addEventListener('pageshow', function (e) {
  3391             if (e.persisted) {
  3392                 // when page is loaded from back/forward cache
  3393                 // trigger repaint to let layout scroll in safari
  3394                 this.element_.style.overflowY = 'hidden';
  3395                 requestAnimationFrame(function () {
  3396                     this.element_.style.overflowY = '';
  3397                 }.bind(this));
  3398             }
  3399         }.bind(this), false);
  3400         if (this.header_) {
  3401             this.tabBar_ = this.header_.querySelector('.' + this.CssClasses_.TAB_BAR);
  3402         }
  3403         var mode = this.Mode_.STANDARD;
  3404         if (this.header_) {
  3405             if (this.header_.classList.contains(this.CssClasses_.HEADER_SEAMED)) {
  3406                 mode = this.Mode_.SEAMED;
  3407             } else if (this.header_.classList.contains(this.CssClasses_.HEADER_WATERFALL)) {
  3408                 mode = this.Mode_.WATERFALL;
  3409                 this.header_.addEventListener('transitionend', this.headerTransitionEndHandler_.bind(this));
  3410                 this.header_.addEventListener('click', this.headerClickHandler_.bind(this));
  3411             } else if (this.header_.classList.contains(this.CssClasses_.HEADER_SCROLL)) {
  3412                 mode = this.Mode_.SCROLL;
  3413                 container.classList.add(this.CssClasses_.HAS_SCROLLING_HEADER);
  3414             }
  3415             if (mode === this.Mode_.STANDARD) {
  3416                 this.header_.classList.add(this.CssClasses_.CASTING_SHADOW);
  3417                 if (this.tabBar_) {
  3418                     this.tabBar_.classList.add(this.CssClasses_.CASTING_SHADOW);
  3419                 }
  3420             } else if (mode === this.Mode_.SEAMED || mode === this.Mode_.SCROLL) {
  3421                 this.header_.classList.remove(this.CssClasses_.CASTING_SHADOW);
  3422                 if (this.tabBar_) {
  3423                     this.tabBar_.classList.remove(this.CssClasses_.CASTING_SHADOW);
  3424                 }
  3425             } else if (mode === this.Mode_.WATERFALL) {
  3426                 // Add and remove shadows depending on scroll position.
  3427                 // Also add/remove auxiliary class for styling of the compact version of
  3428                 // the header.
  3429                 this.content_.addEventListener('scroll', this.contentScrollHandler_.bind(this));
  3430                 this.contentScrollHandler_();
  3431             }
  3432         }
  3433         // Add drawer toggling button to our layout, if we have an openable drawer.
  3434         if (this.drawer_) {
  3435             var drawerButton = this.element_.querySelector('.' + this.CssClasses_.DRAWER_BTN);
  3436             if (!drawerButton) {
  3437                 drawerButton = document.createElement('div');
  3438                 drawerButton.setAttribute('aria-expanded', 'false');
  3439                 drawerButton.setAttribute('role', 'button');
  3440                 drawerButton.setAttribute('tabindex', '0');
  3441                 drawerButton.classList.add(this.CssClasses_.DRAWER_BTN);
  3442                 var drawerButtonIcon = document.createElement('i');
  3443                 drawerButtonIcon.classList.add(this.CssClasses_.ICON);
  3444                 drawerButtonIcon.innerHTML = this.Constant_.MENU_ICON;
  3445                 drawerButton.appendChild(drawerButtonIcon);
  3446             }
  3447             if (this.drawer_.classList.contains(this.CssClasses_.ON_LARGE_SCREEN)) {
  3448                 //If drawer has ON_LARGE_SCREEN class then add it to the drawer toggle button as well.
  3449                 drawerButton.classList.add(this.CssClasses_.ON_LARGE_SCREEN);
  3450             } else if (this.drawer_.classList.contains(this.CssClasses_.ON_SMALL_SCREEN)) {
  3451                 //If drawer has ON_SMALL_SCREEN class then add it to the drawer toggle button as well.
  3452                 drawerButton.classList.add(this.CssClasses_.ON_SMALL_SCREEN);
  3453             }
  3454             drawerButton.addEventListener('click', this.drawerToggleHandler_.bind(this));
  3455             drawerButton.addEventListener('keydown', this.drawerToggleHandler_.bind(this));
  3456             // Add a class if the layout has a drawer, for altering the left padding.
  3457             // Adds the HAS_DRAWER to the elements since this.header_ may or may
  3458             // not be present.
  3459             this.element_.classList.add(this.CssClasses_.HAS_DRAWER);
  3460             // If we have a fixed header, add the button to the header rather than
  3461             // the layout.
  3462             if (this.element_.classList.contains(this.CssClasses_.FIXED_HEADER)) {
  3463                 this.header_.insertBefore(drawerButton, this.header_.firstChild);
  3464             } else {
  3465                 this.element_.insertBefore(drawerButton, this.content_);
  3466             }
  3467             var obfuscator = document.createElement('div');
  3468             obfuscator.classList.add(this.CssClasses_.OBFUSCATOR);
  3469             this.element_.appendChild(obfuscator);
  3470             obfuscator.addEventListener('click', this.drawerToggleHandler_.bind(this));
  3471             this.obfuscator_ = obfuscator;
  3472             this.drawer_.addEventListener('keydown', this.keyboardEventHandler_.bind(this));
  3473             this.drawer_.setAttribute('aria-hidden', 'true');
  3474         }
  3475         // Keep an eye on screen size, and add/remove auxiliary class for styling
  3476         // of small screens.
  3477         this.screenSizeMediaQuery_ = window.matchMedia(this.Constant_.MAX_WIDTH);
  3478         this.screenSizeMediaQuery_.addListener(this.screenSizeHandler_.bind(this));
  3479         this.screenSizeHandler_();
  3480         // Initialize tabs, if any.
  3481         if (this.header_ && this.tabBar_) {
  3482             this.element_.classList.add(this.CssClasses_.HAS_TABS);
  3483             var tabContainer = document.createElement('div');
  3484             tabContainer.classList.add(this.CssClasses_.TAB_CONTAINER);
  3485             this.header_.insertBefore(tabContainer, this.tabBar_);
  3486             this.header_.removeChild(this.tabBar_);
  3487             var leftButton = document.createElement('div');
  3488             leftButton.classList.add(this.CssClasses_.TAB_BAR_BUTTON);
  3489             leftButton.classList.add(this.CssClasses_.TAB_BAR_LEFT_BUTTON);
  3490             var leftButtonIcon = document.createElement('i');
  3491             leftButtonIcon.classList.add(this.CssClasses_.ICON);
  3492             leftButtonIcon.textContent = this.Constant_.CHEVRON_LEFT;
  3493             leftButton.appendChild(leftButtonIcon);
  3494             leftButton.addEventListener('click', function () {
  3495                 this.tabBar_.scrollLeft -= this.Constant_.TAB_SCROLL_PIXELS;
  3496             }.bind(this));
  3497             var rightButton = document.createElement('div');
  3498             rightButton.classList.add(this.CssClasses_.TAB_BAR_BUTTON);
  3499             rightButton.classList.add(this.CssClasses_.TAB_BAR_RIGHT_BUTTON);
  3500             var rightButtonIcon = document.createElement('i');
  3501             rightButtonIcon.classList.add(this.CssClasses_.ICON);
  3502             rightButtonIcon.textContent = this.Constant_.CHEVRON_RIGHT;
  3503             rightButton.appendChild(rightButtonIcon);
  3504             rightButton.addEventListener('click', function () {
  3505                 this.tabBar_.scrollLeft += this.Constant_.TAB_SCROLL_PIXELS;
  3506             }.bind(this));
  3507             tabContainer.appendChild(leftButton);
  3508             tabContainer.appendChild(this.tabBar_);
  3509             tabContainer.appendChild(rightButton);
  3510             // Add and remove tab buttons depending on scroll position and total
  3511             // window size.
  3512             var tabUpdateHandler = function () {
  3513                 if (this.tabBar_.scrollLeft > 0) {
  3514                     leftButton.classList.add(this.CssClasses_.IS_ACTIVE);
  3515                 } else {
  3516                     leftButton.classList.remove(this.CssClasses_.IS_ACTIVE);
  3517                 }
  3518                 if (this.tabBar_.scrollLeft < this.tabBar_.scrollWidth - this.tabBar_.offsetWidth) {
  3519                     rightButton.classList.add(this.CssClasses_.IS_ACTIVE);
  3520                 } else {
  3521                     rightButton.classList.remove(this.CssClasses_.IS_ACTIVE);
  3522                 }
  3523             }.bind(this);
  3524             this.tabBar_.addEventListener('scroll', tabUpdateHandler);
  3525             tabUpdateHandler();
  3526             // Update tabs when the window resizes.
  3527             var windowResizeHandler = function () {
  3528                 // Use timeouts to make sure it doesn't happen too often.
  3529                 if (this.resizeTimeoutId_) {
  3530                     clearTimeout(this.resizeTimeoutId_);
  3531                 }
  3532                 this.resizeTimeoutId_ = setTimeout(function () {
  3533                     tabUpdateHandler();
  3534                     this.resizeTimeoutId_ = null;
  3535                 }.bind(this), this.Constant_.RESIZE_TIMEOUT);
  3536             }.bind(this);
  3537             window.addEventListener('resize', windowResizeHandler);
  3538             if (this.tabBar_.classList.contains(this.CssClasses_.JS_RIPPLE_EFFECT)) {
  3539                 this.tabBar_.classList.add(this.CssClasses_.RIPPLE_IGNORE_EVENTS);
  3540             }
  3541             // Select element tabs, document panels
  3542             var tabs = this.tabBar_.querySelectorAll('.' + this.CssClasses_.TAB);
  3543             var panels = this.content_.querySelectorAll('.' + this.CssClasses_.PANEL);
  3544             // Create new tabs for each tab element
  3545             for (var i = 0; i < tabs.length; i++) {
  3546                 new MaterialLayoutTab(tabs[i], tabs, panels, this);
  3547             }
  3548         }
  3549         this.element_.classList.add(this.CssClasses_.IS_UPGRADED);
  3550     }
  3551 };
  3552 /**
  3553    * Constructor for an individual tab.
  3554    *
  3555    * @constructor
  3556    * @param {HTMLElement} tab The HTML element for the tab.
  3557    * @param {!Array<HTMLElement>} tabs Array with HTML elements for all tabs.
  3558    * @param {!Array<HTMLElement>} panels Array with HTML elements for all panels.
  3559    * @param {MaterialLayout} layout The MaterialLayout object that owns the tab.
  3560    */
  3561 function MaterialLayoutTab(tab, tabs, panels, layout) {
  3562     /**
  3563      * Auxiliary method to programmatically select a tab in the UI.
  3564      */
  3565     function selectTab() {
  3566         var href = tab.href.split('#')[1];
  3567         var panel = layout.content_.querySelector('#' + href);
  3568         layout.resetTabState_(tabs);
  3569         layout.resetPanelState_(panels);
  3570         tab.classList.add(layout.CssClasses_.IS_ACTIVE);
  3571         panel.classList.add(layout.CssClasses_.IS_ACTIVE);
  3572     }
  3573     if (layout.tabBar_.classList.contains(layout.CssClasses_.JS_RIPPLE_EFFECT)) {
  3574         var rippleContainer = document.createElement('span');
  3575         rippleContainer.classList.add(layout.CssClasses_.RIPPLE_CONTAINER);
  3576         rippleContainer.classList.add(layout.CssClasses_.JS_RIPPLE_EFFECT);
  3577         var ripple = document.createElement('span');
  3578         ripple.classList.add(layout.CssClasses_.RIPPLE);
  3579         rippleContainer.appendChild(ripple);
  3580         tab.appendChild(rippleContainer);
  3581     }
  3582     tab.addEventListener('click', function (e) {
  3583         if (tab.getAttribute('href').charAt(0) === '#') {
  3584             e.preventDefault();
  3585             selectTab();
  3586         }
  3587     });
  3588     tab.show = selectTab;
  3589 }
  3590 window['MaterialLayoutTab'] = MaterialLayoutTab;
  3591 // The component registers itself. It can assume componentHandler is available
  3592 // in the global scope.
  3593 componentHandler.register({
  3594     constructor: MaterialLayout,
  3595     classAsString: 'MaterialLayout',
  3596     cssClass: 'mdl-js-layout'
  3597 });
  3598 /**
  3599  * @license
  3600  * Copyright 2015 Google Inc. All Rights Reserved.
  3601  *
  3602  * Licensed under the Apache License, Version 2.0 (the "License");
  3603  * you may not use this file except in compliance with the License.
  3604  * You may obtain a copy of the License at
  3605  *
  3606  *      http://www.apache.org/licenses/LICENSE-2.0
  3607  *
  3608  * Unless required by applicable law or agreed to in writing, software
  3609  * distributed under the License is distributed on an "AS IS" BASIS,
  3610  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  3611  * See the License for the specific language governing permissions and
  3612  * limitations under the License.
  3613  */
  3614 /**
  3615    * Class constructor for Data Table Card MDL component.
  3616    * Implements MDL component design pattern defined at:
  3617    * https://github.com/jasonmayes/mdl-component-design-pattern
  3618    *
  3619    * @constructor
  3620    * @param {Element} element The element that will be upgraded.
  3621    */
  3622 var MaterialDataTable = function MaterialDataTable(element) {
  3623     this.element_ = element;
  3624     // Initialize instance.
  3625     this.init();
  3626 };
  3627 window['MaterialDataTable'] = MaterialDataTable;
  3628 /**
  3629    * Store constants in one place so they can be updated easily.
  3630    *
  3631    * @enum {string | number}
  3632    * @private
  3633    */
  3634 MaterialDataTable.prototype.Constant_ = {};
  3635 /**
  3636    * Store strings for class names defined by this component that are used in
  3637    * JavaScript. This allows us to simply change it in one place should we
  3638    * decide to modify at a later date.
  3639    *
  3640    * @enum {string}
  3641    * @private
  3642    */
  3643 MaterialDataTable.prototype.CssClasses_ = {
  3644     DATA_TABLE: 'mdl-data-table',
  3645     SELECTABLE: 'mdl-data-table--selectable',
  3646     SELECT_ELEMENT: 'mdl-data-table__select',
  3647     IS_SELECTED: 'is-selected',
  3648     IS_UPGRADED: 'is-upgraded'
  3649 };
  3650 /**
  3651    * Generates and returns a function that toggles the selection state of a
  3652    * single row (or multiple rows).
  3653    *
  3654    * @param {Element} checkbox Checkbox that toggles the selection state.
  3655    * @param {Element} row Row to toggle when checkbox changes.
  3656    * @param {(Array<Object>|NodeList)=} opt_rows Rows to toggle when checkbox changes.
  3657    * @private
  3658    */
  3659 MaterialDataTable.prototype.selectRow_ = function (checkbox, row, opt_rows) {
  3660     if (row) {
  3661         return function () {
  3662             if (checkbox.checked) {
  3663                 row.classList.add(this.CssClasses_.IS_SELECTED);
  3664             } else {
  3665                 row.classList.remove(this.CssClasses_.IS_SELECTED);
  3666             }
  3667         }.bind(this);
  3668     }
  3669     if (opt_rows) {
  3670         return function () {
  3671             var i;
  3672             var el;
  3673             if (checkbox.checked) {
  3674                 for (i = 0; i < opt_rows.length; i++) {
  3675                     el = opt_rows[i].querySelector('td').querySelector('.mdl-checkbox');
  3676                     el['MaterialCheckbox'].check();
  3677                     opt_rows[i].classList.add(this.CssClasses_.IS_SELECTED);
  3678                 }
  3679             } else {
  3680                 for (i = 0; i < opt_rows.length; i++) {
  3681                     el = opt_rows[i].querySelector('td').querySelector('.mdl-checkbox');
  3682                     el['MaterialCheckbox'].uncheck();
  3683                     opt_rows[i].classList.remove(this.CssClasses_.IS_SELECTED);
  3684                 }
  3685             }
  3686         }.bind(this);
  3687     }
  3688 };
  3689 /**
  3690    * Creates a checkbox for a single or or multiple rows and hooks up the
  3691    * event handling.
  3692    *
  3693    * @param {Element} row Row to toggle when checkbox changes.
  3694    * @param {(Array<Object>|NodeList)=} opt_rows Rows to toggle when checkbox changes.
  3695    * @private
  3696    */
  3697 MaterialDataTable.prototype.createCheckbox_ = function (row, opt_rows) {
  3698     var label = document.createElement('label');
  3699     var labelClasses = [
  3700         'mdl-checkbox',
  3701         'mdl-js-checkbox',
  3702         'mdl-js-ripple-effect',
  3703         this.CssClasses_.SELECT_ELEMENT
  3704     ];
  3705     label.className = labelClasses.join(' ');
  3706     var checkbox = document.createElement('input');
  3707     checkbox.type = 'checkbox';
  3708     checkbox.classList.add('mdl-checkbox__input');
  3709     if (row) {
  3710         checkbox.checked = row.classList.contains(this.CssClasses_.IS_SELECTED);
  3711         checkbox.addEventListener('change', this.selectRow_(checkbox, row));
  3712     } else if (opt_rows) {
  3713         checkbox.addEventListener('change', this.selectRow_(checkbox, null, opt_rows));
  3714     }
  3715     label.appendChild(checkbox);
  3716     componentHandler.upgradeElement(label, 'MaterialCheckbox');
  3717     return label;
  3718 };
  3719 /**
  3720    * Initialize element.
  3721    */
  3722 MaterialDataTable.prototype.init = function () {
  3723     if (this.element_) {
  3724         var firstHeader = this.element_.querySelector('th');
  3725         var bodyRows = Array.prototype.slice.call(this.element_.querySelectorAll('tbody tr'));
  3726         var footRows = Array.prototype.slice.call(this.element_.querySelectorAll('tfoot tr'));
  3727         var rows = bodyRows.concat(footRows);
  3728         if (this.element_.classList.contains(this.CssClasses_.SELECTABLE)) {
  3729             var th = document.createElement('th');
  3730             var headerCheckbox = this.createCheckbox_(null, rows);
  3731             th.appendChild(headerCheckbox);
  3732             firstHeader.parentElement.insertBefore(th, firstHeader);
  3733             for (var i = 0; i < rows.length; i++) {
  3734                 var firstCell = rows[i].querySelector('td');
  3735                 if (firstCell) {
  3736                     var td = document.createElement('td');
  3737                     if (rows[i].parentNode.nodeName.toUpperCase() === 'TBODY') {
  3738                         var rowCheckbox = this.createCheckbox_(rows[i]);
  3739                         td.appendChild(rowCheckbox);
  3740                     }
  3741                     rows[i].insertBefore(td, firstCell);
  3742                 }
  3743             }
  3744             this.element_.classList.add(this.CssClasses_.IS_UPGRADED);
  3745         }
  3746     }
  3747 };
  3748 // The component registers itself. It can assume componentHandler is available
  3749 // in the global scope.
  3750 componentHandler.register({
  3751     constructor: MaterialDataTable,
  3752     classAsString: 'MaterialDataTable',
  3753     cssClass: 'mdl-js-data-table'
  3754 });
  3755 /**
  3756  * @license
  3757  * Copyright 2015 Google Inc. All Rights Reserved.
  3758  *
  3759  * Licensed under the Apache License, Version 2.0 (the "License");
  3760  * you may not use this file except in compliance with the License.
  3761  * You may obtain a copy of the License at
  3762  *
  3763  *      http://www.apache.org/licenses/LICENSE-2.0
  3764  *
  3765  * Unless required by applicable law or agreed to in writing, software
  3766  * distributed under the License is distributed on an "AS IS" BASIS,
  3767  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  3768  * See the License for the specific language governing permissions and
  3769  * limitations under the License.
  3770  */
  3771 /**
  3772    * Class constructor for Ripple MDL component.
  3773    * Implements MDL component design pattern defined at:
  3774    * https://github.com/jasonmayes/mdl-component-design-pattern
  3775    *
  3776    * @constructor
  3777    * @param {HTMLElement} element The element that will be upgraded.
  3778    */
  3779 var MaterialRipple = function MaterialRipple(element) {
  3780     this.element_ = element;
  3781     // Initialize instance.
  3782     this.init();
  3783 };
  3784 window['MaterialRipple'] = MaterialRipple;
  3785 /**
  3786    * Store constants in one place so they can be updated easily.
  3787    *
  3788    * @enum {string | number}
  3789    * @private
  3790    */
  3791 MaterialRipple.prototype.Constant_ = {
  3792     INITIAL_SCALE: 'scale(0.0001, 0.0001)',
  3793     INITIAL_SIZE: '1px',
  3794     INITIAL_OPACITY: '0.4',
  3795     FINAL_OPACITY: '0',
  3796     FINAL_SCALE: ''
  3797 };
  3798 /**
  3799    * Store strings for class names defined by this component that are used in
  3800    * JavaScript. This allows us to simply change it in one place should we
  3801    * decide to modify at a later date.
  3802    *
  3803    * @enum {string}
  3804    * @private
  3805    */
  3806 MaterialRipple.prototype.CssClasses_ = {
  3807     RIPPLE_CENTER: 'mdl-ripple--center',
  3808     RIPPLE_EFFECT_IGNORE_EVENTS: 'mdl-js-ripple-effect--ignore-events',
  3809     RIPPLE: 'mdl-ripple',
  3810     IS_ANIMATING: 'is-animating',
  3811     IS_VISIBLE: 'is-visible'
  3812 };
  3813 /**
  3814    * Handle mouse / finger down on element.
  3815    *
  3816    * @param {Event} event The event that fired.
  3817    * @private
  3818    */
  3819 MaterialRipple.prototype.downHandler_ = function (event) {
  3820     if (!this.rippleElement_.style.width && !this.rippleElement_.style.height) {
  3821         var rect = this.element_.getBoundingClientRect();
  3822         this.boundHeight = rect.height;
  3823         this.boundWidth = rect.width;
  3824         this.rippleSize_ = Math.sqrt(rect.width * rect.width + rect.height * rect.height) * 2 + 2;
  3825         this.rippleElement_.style.width = this.rippleSize_ + 'px';
  3826         this.rippleElement_.style.height = this.rippleSize_ + 'px';
  3827     }
  3828     this.rippleElement_.classList.add(this.CssClasses_.IS_VISIBLE);
  3829     if (event.type === 'mousedown' && this.ignoringMouseDown_) {
  3830         this.ignoringMouseDown_ = false;
  3831     } else {
  3832         if (event.type === 'touchstart') {
  3833             this.ignoringMouseDown_ = true;
  3834         }
  3835         var frameCount = this.getFrameCount();
  3836         if (frameCount > 0) {
  3837             return;
  3838         }
  3839         this.setFrameCount(1);
  3840         var bound = event.currentTarget.getBoundingClientRect();
  3841         var x;
  3842         var y;
  3843         // Check if we are handling a keyboard click.
  3844         if (event.clientX === 0 && event.clientY === 0) {
  3845             x = Math.round(bound.width / 2);
  3846             y = Math.round(bound.height / 2);
  3847         } else {
  3848             var clientX = event.clientX ? event.clientX : event.touches[0].clientX;
  3849             var clientY = event.clientY ? event.clientY : event.touches[0].clientY;
  3850             x = Math.round(clientX - bound.left);
  3851             y = Math.round(clientY - bound.top);
  3852         }
  3853         this.setRippleXY(x, y);
  3854         this.setRippleStyles(true);
  3855         window.requestAnimationFrame(this.animFrameHandler.bind(this));
  3856     }
  3857 };
  3858 /**
  3859    * Handle mouse / finger up on element.
  3860    *
  3861    * @param {Event} event The event that fired.
  3862    * @private
  3863    */
  3864 MaterialRipple.prototype.upHandler_ = function (event) {
  3865     // Don't fire for the artificial "mouseup" generated by a double-click.
  3866     if (event && event.detail !== 2) {
  3867         // Allow a repaint to occur before removing this class, so the animation
  3868         // shows for tap events, which seem to trigger a mouseup too soon after
  3869         // mousedown.
  3870         window.setTimeout(function () {
  3871             this.rippleElement_.classList.remove(this.CssClasses_.IS_VISIBLE);
  3872         }.bind(this), 0);
  3873     }
  3874 };
  3875 /**
  3876    * Initialize element.
  3877    */
  3878 MaterialRipple.prototype.init = function () {
  3879     if (this.element_) {
  3880         var recentering = this.element_.classList.contains(this.CssClasses_.RIPPLE_CENTER);
  3881         if (!this.element_.classList.contains(this.CssClasses_.RIPPLE_EFFECT_IGNORE_EVENTS)) {
  3882             this.rippleElement_ = this.element_.querySelector('.' + this.CssClasses_.RIPPLE);
  3883             this.frameCount_ = 0;
  3884             this.rippleSize_ = 0;
  3885             this.x_ = 0;
  3886             this.y_ = 0;
  3887             // Touch start produces a compat mouse down event, which would cause a
  3888             // second ripples. To avoid that, we use this property to ignore the first
  3889             // mouse down after a touch start.
  3890             this.ignoringMouseDown_ = false;
  3891             this.boundDownHandler = this.downHandler_.bind(this);
  3892             this.element_.addEventListener('mousedown', this.boundDownHandler);
  3893             this.element_.addEventListener('touchstart', this.boundDownHandler);
  3894             this.boundUpHandler = this.upHandler_.bind(this);
  3895             this.element_.addEventListener('mouseup', this.boundUpHandler);
  3896             this.element_.addEventListener('mouseleave', this.boundUpHandler);
  3897             this.element_.addEventListener('touchend', this.boundUpHandler);
  3898             this.element_.addEventListener('blur', this.boundUpHandler);
  3899             /**
  3900          * Getter for frameCount_.
  3901          * @return {number} the frame count.
  3902          */
  3903             this.getFrameCount = function () {
  3904                 return this.frameCount_;
  3905             };
  3906             /**
  3907          * Setter for frameCount_.
  3908          * @param {number} fC the frame count.
  3909          */
  3910             this.setFrameCount = function (fC) {
  3911                 this.frameCount_ = fC;
  3912             };
  3913             /**
  3914          * Getter for rippleElement_.
  3915          * @return {Element} the ripple element.
  3916          */
  3917             this.getRippleElement = function () {
  3918                 return this.rippleElement_;
  3919             };
  3920             /**
  3921          * Sets the ripple X and Y coordinates.
  3922          * @param  {number} newX the new X coordinate
  3923          * @param  {number} newY the new Y coordinate
  3924          */
  3925             this.setRippleXY = function (newX, newY) {
  3926                 this.x_ = newX;
  3927                 this.y_ = newY;
  3928             };
  3929             /**
  3930          * Sets the ripple styles.
  3931          * @param  {boolean} start whether or not this is the start frame.
  3932          */
  3933             this.setRippleStyles = function (start) {
  3934                 if (this.rippleElement_ !== null) {
  3935                     var transformString;
  3936                     var scale;
  3937                     var size;
  3938                     var offset = 'translate(' + this.x_ + 'px, ' + this.y_ + 'px)';
  3939                     if (start) {
  3940                         scale = this.Constant_.INITIAL_SCALE;
  3941                         size = this.Constant_.INITIAL_SIZE;
  3942                     } else {
  3943                         scale = this.Constant_.FINAL_SCALE;
  3944                         size = this.rippleSize_ + 'px';
  3945                         if (recentering) {
  3946                             offset = 'translate(' + this.boundWidth / 2 + 'px, ' + this.boundHeight / 2 + 'px)';
  3947                         }
  3948                     }
  3949                     transformString = 'translate(-50%, -50%) ' + offset + scale;
  3950                     this.rippleElement_.style.webkitTransform = transformString;
  3951                     this.rippleElement_.style.msTransform = transformString;
  3952                     this.rippleElement_.style.transform = transformString;
  3953                     if (start) {
  3954                         this.rippleElement_.classList.remove(this.CssClasses_.IS_ANIMATING);
  3955                     } else {
  3956                         this.rippleElement_.classList.add(this.CssClasses_.IS_ANIMATING);
  3957                     }
  3958                 }
  3959             };
  3960             /**
  3961          * Handles an animation frame.
  3962          */
  3963             this.animFrameHandler = function () {
  3964                 if (this.frameCount_-- > 0) {
  3965                     window.requestAnimationFrame(this.animFrameHandler.bind(this));
  3966                 } else {
  3967                     this.setRippleStyles(false);
  3968                 }
  3969             };
  3970         }
  3971     }
  3972 };
  3973 // The component registers itself. It can assume componentHandler is available
  3974 // in the global scope.
  3975 componentHandler.register({
  3976     constructor: MaterialRipple,
  3977     classAsString: 'MaterialRipple',
  3978     cssClass: 'mdl-js-ripple-effect',
  3979     widget: false
  3980 });
  3981 }());
