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