liquid_feedback_frontend
diff static/mdl/material.js @ 1309:32cc544d5a5b
Cumulative patch for upcoming frontend version 4
author | bsw/jbe |
---|---|
date | Sun Jul 15 14:07:29 2018 +0200 (2018-07-15) |
parents | |
children |
line diff
1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/static/mdl/material.js Sun Jul 15 14:07:29 2018 +0200 1.3 @@ -0,0 +1,3981 @@ 1.4 +;(function() { 1.5 +"use strict"; 1.6 + 1.7 +/** 1.8 + * @license 1.9 + * Copyright 2015 Google Inc. All Rights Reserved. 1.10 + * 1.11 + * Licensed under the Apache License, Version 2.0 (the "License"); 1.12 + * you may not use this file except in compliance with the License. 1.13 + * You may obtain a copy of the License at 1.14 + * 1.15 + * http://www.apache.org/licenses/LICENSE-2.0 1.16 + * 1.17 + * Unless required by applicable law or agreed to in writing, software 1.18 + * distributed under the License is distributed on an "AS IS" BASIS, 1.19 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 1.20 + * See the License for the specific language governing permissions and 1.21 + * limitations under the License. 1.22 + */ 1.23 + 1.24 +/** 1.25 + * A component handler interface using the revealing module design pattern. 1.26 + * More details on this design pattern here: 1.27 + * https://github.com/jasonmayes/mdl-component-design-pattern 1.28 + * 1.29 + * @author Jason Mayes. 1.30 + */ 1.31 +/* exported componentHandler */ 1.32 + 1.33 +// Pre-defining the componentHandler interface, for closure documentation and 1.34 +// static verification. 1.35 +var componentHandler = { 1.36 + /** 1.37 + * Searches existing DOM for elements of our component type and upgrades them 1.38 + * if they have not already been upgraded. 1.39 + * 1.40 + * @param {string=} optJsClass the programatic name of the element class we 1.41 + * need to create a new instance of. 1.42 + * @param {string=} optCssClass the name of the CSS class elements of this 1.43 + * type will have. 1.44 + */ 1.45 + upgradeDom: function(optJsClass, optCssClass) {}, 1.46 + /** 1.47 + * Upgrades a specific element rather than all in the DOM. 1.48 + * 1.49 + * @param {!Element} element The element we wish to upgrade. 1.50 + * @param {string=} optJsClass Optional name of the class we want to upgrade 1.51 + * the element to. 1.52 + */ 1.53 + upgradeElement: function(element, optJsClass) {}, 1.54 + /** 1.55 + * Upgrades a specific list of elements rather than all in the DOM. 1.56 + * 1.57 + * @param {!Element|!Array<!Element>|!NodeList|!HTMLCollection} elements 1.58 + * The elements we wish to upgrade. 1.59 + */ 1.60 + upgradeElements: function(elements) {}, 1.61 + /** 1.62 + * Upgrades all registered components found in the current DOM. This is 1.63 + * automatically called on window load. 1.64 + */ 1.65 + upgradeAllRegistered: function() {}, 1.66 + /** 1.67 + * Allows user to be alerted to any upgrades that are performed for a given 1.68 + * component type 1.69 + * 1.70 + * @param {string} jsClass The class name of the MDL component we wish 1.71 + * to hook into for any upgrades performed. 1.72 + * @param {function(!HTMLElement)} callback The function to call upon an 1.73 + * upgrade. This function should expect 1 parameter - the HTMLElement which 1.74 + * got upgraded. 1.75 + */ 1.76 + registerUpgradedCallback: function(jsClass, callback) {}, 1.77 + /** 1.78 + * Registers a class for future use and attempts to upgrade existing DOM. 1.79 + * 1.80 + * @param {componentHandler.ComponentConfigPublic} config the registration configuration 1.81 + */ 1.82 + register: function(config) {}, 1.83 + /** 1.84 + * Downgrade either a given node, an array of nodes, or a NodeList. 1.85 + * 1.86 + * @param {!Node|!Array<!Node>|!NodeList} nodes 1.87 + */ 1.88 + downgradeElements: function(nodes) {} 1.89 +}; 1.90 + 1.91 +componentHandler = (function() { 1.92 + 'use strict'; 1.93 + 1.94 + /** @type {!Array<componentHandler.ComponentConfig>} */ 1.95 + var registeredComponents_ = []; 1.96 + 1.97 + /** @type {!Array<componentHandler.Component>} */ 1.98 + var createdComponents_ = []; 1.99 + 1.100 + var componentConfigProperty_ = 'mdlComponentConfigInternal_'; 1.101 + 1.102 + /** 1.103 + * Searches registered components for a class we are interested in using. 1.104 + * Optionally replaces a match with passed object if specified. 1.105 + * 1.106 + * @param {string} name The name of a class we want to use. 1.107 + * @param {componentHandler.ComponentConfig=} optReplace Optional object to replace match with. 1.108 + * @return {!Object|boolean} 1.109 + * @private 1.110 + */ 1.111 + function findRegisteredClass_(name, optReplace) { 1.112 + for (var i = 0; i < registeredComponents_.length; i++) { 1.113 + if (registeredComponents_[i].className === name) { 1.114 + if (typeof optReplace !== 'undefined') { 1.115 + registeredComponents_[i] = optReplace; 1.116 + } 1.117 + return registeredComponents_[i]; 1.118 + } 1.119 + } 1.120 + return false; 1.121 + } 1.122 + 1.123 + /** 1.124 + * Returns an array of the classNames of the upgraded classes on the element. 1.125 + * 1.126 + * @param {!Element} element The element to fetch data from. 1.127 + * @return {!Array<string>} 1.128 + * @private 1.129 + */ 1.130 + function getUpgradedListOfElement_(element) { 1.131 + var dataUpgraded = element.getAttribute('data-upgraded'); 1.132 + // Use `['']` as default value to conform the `,name,name...` style. 1.133 + return dataUpgraded === null ? [''] : dataUpgraded.split(','); 1.134 + } 1.135 + 1.136 + /** 1.137 + * Returns true if the given element has already been upgraded for the given 1.138 + * class. 1.139 + * 1.140 + * @param {!Element} element The element we want to check. 1.141 + * @param {string} jsClass The class to check for. 1.142 + * @returns {boolean} 1.143 + * @private 1.144 + */ 1.145 + function isElementUpgraded_(element, jsClass) { 1.146 + var upgradedList = getUpgradedListOfElement_(element); 1.147 + return upgradedList.indexOf(jsClass) !== -1; 1.148 + } 1.149 + 1.150 + /** 1.151 + * Searches existing DOM for elements of our component type and upgrades them 1.152 + * if they have not already been upgraded. 1.153 + * 1.154 + * @param {string=} optJsClass the programatic name of the element class we 1.155 + * need to create a new instance of. 1.156 + * @param {string=} optCssClass the name of the CSS class elements of this 1.157 + * type will have. 1.158 + */ 1.159 + function upgradeDomInternal(optJsClass, optCssClass) { 1.160 + if (typeof optJsClass === 'undefined' && 1.161 + typeof optCssClass === 'undefined') { 1.162 + for (var i = 0; i < registeredComponents_.length; i++) { 1.163 + upgradeDomInternal(registeredComponents_[i].className, 1.164 + registeredComponents_[i].cssClass); 1.165 + } 1.166 + } else { 1.167 + var jsClass = /** @type {string} */ (optJsClass); 1.168 + if (typeof optCssClass === 'undefined') { 1.169 + var registeredClass = findRegisteredClass_(jsClass); 1.170 + if (registeredClass) { 1.171 + optCssClass = registeredClass.cssClass; 1.172 + } 1.173 + } 1.174 + 1.175 + var elements = document.querySelectorAll('.' + optCssClass); 1.176 + for (var n = 0; n < elements.length; n++) { 1.177 + upgradeElementInternal(elements[n], jsClass); 1.178 + } 1.179 + } 1.180 + } 1.181 + 1.182 + /** 1.183 + * Upgrades a specific element rather than all in the DOM. 1.184 + * 1.185 + * @param {!Element} element The element we wish to upgrade. 1.186 + * @param {string=} optJsClass Optional name of the class we want to upgrade 1.187 + * the element to. 1.188 + */ 1.189 + function upgradeElementInternal(element, optJsClass) { 1.190 + // Verify argument type. 1.191 + if (!(typeof element === 'object' && element instanceof Element)) { 1.192 + throw new Error('Invalid argument provided to upgrade MDL element.'); 1.193 + } 1.194 + var upgradedList = getUpgradedListOfElement_(element); 1.195 + var classesToUpgrade = []; 1.196 + // If jsClass is not provided scan the registered components to find the 1.197 + // ones matching the element's CSS classList. 1.198 + if (!optJsClass) { 1.199 + var classList = element.classList; 1.200 + registeredComponents_.forEach(function(component) { 1.201 + // Match CSS & Not to be upgraded & Not upgraded. 1.202 + if (classList.contains(component.cssClass) && 1.203 + classesToUpgrade.indexOf(component) === -1 && 1.204 + !isElementUpgraded_(element, component.className)) { 1.205 + classesToUpgrade.push(component); 1.206 + } 1.207 + }); 1.208 + } else if (!isElementUpgraded_(element, optJsClass)) { 1.209 + classesToUpgrade.push(findRegisteredClass_(optJsClass)); 1.210 + } 1.211 + 1.212 + // Upgrade the element for each classes. 1.213 + for (var i = 0, n = classesToUpgrade.length, registeredClass; i < n; i++) { 1.214 + registeredClass = classesToUpgrade[i]; 1.215 + if (registeredClass) { 1.216 + // Mark element as upgraded. 1.217 + upgradedList.push(registeredClass.className); 1.218 + element.setAttribute('data-upgraded', upgradedList.join(',')); 1.219 + var instance = new registeredClass.classConstructor(element); 1.220 + instance[componentConfigProperty_] = registeredClass; 1.221 + createdComponents_.push(instance); 1.222 + // Call any callbacks the user has registered with this component type. 1.223 + for (var j = 0, m = registeredClass.callbacks.length; j < m; j++) { 1.224 + registeredClass.callbacks[j](element); 1.225 + } 1.226 + 1.227 + if (registeredClass.widget) { 1.228 + // Assign per element instance for control over API 1.229 + element[registeredClass.className] = instance; 1.230 + } 1.231 + } else { 1.232 + throw new Error( 1.233 + 'Unable to find a registered component for the given class.'); 1.234 + } 1.235 + 1.236 + var ev; 1.237 + if ('CustomEvent' in window && typeof window.CustomEvent === 'function') { 1.238 + ev = new CustomEvent('mdl-componentupgraded', { 1.239 + bubbles: true, cancelable: false 1.240 + }); 1.241 + } else { 1.242 + ev = document.createEvent('Events'); 1.243 + ev.initEvent('mdl-componentupgraded', true, true); 1.244 + } 1.245 + element.dispatchEvent(ev); 1.246 + } 1.247 + } 1.248 + 1.249 + /** 1.250 + * Upgrades a specific list of elements rather than all in the DOM. 1.251 + * 1.252 + * @param {!Element|!Array<!Element>|!NodeList|!HTMLCollection} elements 1.253 + * The elements we wish to upgrade. 1.254 + */ 1.255 + function upgradeElementsInternal(elements) { 1.256 + if (!Array.isArray(elements)) { 1.257 + if (elements instanceof Element) { 1.258 + elements = [elements]; 1.259 + } else { 1.260 + elements = Array.prototype.slice.call(elements); 1.261 + } 1.262 + } 1.263 + for (var i = 0, n = elements.length, element; i < n; i++) { 1.264 + element = elements[i]; 1.265 + if (element instanceof HTMLElement) { 1.266 + upgradeElementInternal(element); 1.267 + if (element.children.length > 0) { 1.268 + upgradeElementsInternal(element.children); 1.269 + } 1.270 + } 1.271 + } 1.272 + } 1.273 + 1.274 + /** 1.275 + * Registers a class for future use and attempts to upgrade existing DOM. 1.276 + * 1.277 + * @param {componentHandler.ComponentConfigPublic} config 1.278 + */ 1.279 + function registerInternal(config) { 1.280 + // In order to support both Closure-compiled and uncompiled code accessing 1.281 + // this method, we need to allow for both the dot and array syntax for 1.282 + // property access. You'll therefore see the `foo.bar || foo['bar']` 1.283 + // pattern repeated across this method. 1.284 + var widgetMissing = (typeof config.widget === 'undefined' && 1.285 + typeof config['widget'] === 'undefined'); 1.286 + var widget = true; 1.287 + 1.288 + if (!widgetMissing) { 1.289 + widget = config.widget || config['widget']; 1.290 + } 1.291 + 1.292 + var newConfig = /** @type {componentHandler.ComponentConfig} */ ({ 1.293 + classConstructor: config.constructor || config['constructor'], 1.294 + className: config.classAsString || config['classAsString'], 1.295 + cssClass: config.cssClass || config['cssClass'], 1.296 + widget: widget, 1.297 + callbacks: [] 1.298 + }); 1.299 + 1.300 + registeredComponents_.forEach(function(item) { 1.301 + if (item.cssClass === newConfig.cssClass) { 1.302 + throw new Error('The provided cssClass has already been registered: ' + item.cssClass); 1.303 + } 1.304 + if (item.className === newConfig.className) { 1.305 + throw new Error('The provided className has already been registered'); 1.306 + } 1.307 + }); 1.308 + 1.309 + if (config.constructor.prototype 1.310 + .hasOwnProperty(componentConfigProperty_)) { 1.311 + throw new Error( 1.312 + 'MDL component classes must not have ' + componentConfigProperty_ + 1.313 + ' defined as a property.'); 1.314 + } 1.315 + 1.316 + var found = findRegisteredClass_(config.classAsString, newConfig); 1.317 + 1.318 + if (!found) { 1.319 + registeredComponents_.push(newConfig); 1.320 + } 1.321 + } 1.322 + 1.323 + /** 1.324 + * Allows user to be alerted to any upgrades that are performed for a given 1.325 + * component type 1.326 + * 1.327 + * @param {string} jsClass The class name of the MDL component we wish 1.328 + * to hook into for any upgrades performed. 1.329 + * @param {function(!HTMLElement)} callback The function to call upon an 1.330 + * upgrade. This function should expect 1 parameter - the HTMLElement which 1.331 + * got upgraded. 1.332 + */ 1.333 + function registerUpgradedCallbackInternal(jsClass, callback) { 1.334 + var regClass = findRegisteredClass_(jsClass); 1.335 + if (regClass) { 1.336 + regClass.callbacks.push(callback); 1.337 + } 1.338 + } 1.339 + 1.340 + /** 1.341 + * Upgrades all registered components found in the current DOM. This is 1.342 + * automatically called on window load. 1.343 + */ 1.344 + function upgradeAllRegisteredInternal() { 1.345 + for (var n = 0; n < registeredComponents_.length; n++) { 1.346 + upgradeDomInternal(registeredComponents_[n].className); 1.347 + } 1.348 + } 1.349 + 1.350 + /** 1.351 + * Check the component for the downgrade method. 1.352 + * Execute if found. 1.353 + * Remove component from createdComponents list. 1.354 + * 1.355 + * @param {?componentHandler.Component} component 1.356 + */ 1.357 + function deconstructComponentInternal(component) { 1.358 + if (component) { 1.359 + var componentIndex = createdComponents_.indexOf(component); 1.360 + createdComponents_.splice(componentIndex, 1); 1.361 + 1.362 + var upgrades = component.element_.getAttribute('data-upgraded').split(','); 1.363 + var componentPlace = upgrades.indexOf(component[componentConfigProperty_].classAsString); 1.364 + upgrades.splice(componentPlace, 1); 1.365 + component.element_.setAttribute('data-upgraded', upgrades.join(',')); 1.366 + 1.367 + var ev; 1.368 + if ('CustomEvent' in window && typeof window.CustomEvent === 'function') { 1.369 + ev = new CustomEvent('mdl-componentdowngraded', { 1.370 + bubbles: true, cancelable: false 1.371 + }); 1.372 + } else { 1.373 + ev = document.createEvent('Events'); 1.374 + ev.initEvent('mdl-componentdowngraded', true, true); 1.375 + } 1.376 + component.element_.dispatchEvent(ev); 1.377 + } 1.378 + } 1.379 + 1.380 + /** 1.381 + * Downgrade either a given node, an array of nodes, or a NodeList. 1.382 + * 1.383 + * @param {!Node|!Array<!Node>|!NodeList} nodes 1.384 + */ 1.385 + function downgradeNodesInternal(nodes) { 1.386 + /** 1.387 + * Auxiliary function to downgrade a single node. 1.388 + * @param {!Node} node the node to be downgraded 1.389 + */ 1.390 + var downgradeNode = function(node) { 1.391 + createdComponents_.filter(function(item) { 1.392 + return item.element_ === node; 1.393 + }).forEach(deconstructComponentInternal); 1.394 + }; 1.395 + if (nodes instanceof Array || nodes instanceof NodeList) { 1.396 + for (var n = 0; n < nodes.length; n++) { 1.397 + downgradeNode(nodes[n]); 1.398 + } 1.399 + } else if (nodes instanceof Node) { 1.400 + downgradeNode(nodes); 1.401 + } else { 1.402 + throw new Error('Invalid argument provided to downgrade MDL nodes.'); 1.403 + } 1.404 + } 1.405 + 1.406 + // Now return the functions that should be made public with their publicly 1.407 + // facing names... 1.408 + return { 1.409 + upgradeDom: upgradeDomInternal, 1.410 + upgradeElement: upgradeElementInternal, 1.411 + upgradeElements: upgradeElementsInternal, 1.412 + upgradeAllRegistered: upgradeAllRegisteredInternal, 1.413 + registerUpgradedCallback: registerUpgradedCallbackInternal, 1.414 + register: registerInternal, 1.415 + downgradeElements: downgradeNodesInternal 1.416 + }; 1.417 +})(); 1.418 + 1.419 +/** 1.420 + * Describes the type of a registered component type managed by 1.421 + * componentHandler. Provided for benefit of the Closure compiler. 1.422 + * 1.423 + * @typedef {{ 1.424 + * constructor: Function, 1.425 + * classAsString: string, 1.426 + * cssClass: string, 1.427 + * widget: (string|boolean|undefined) 1.428 + * }} 1.429 + */ 1.430 +componentHandler.ComponentConfigPublic; // jshint ignore:line 1.431 + 1.432 +/** 1.433 + * Describes the type of a registered component type managed by 1.434 + * componentHandler. Provided for benefit of the Closure compiler. 1.435 + * 1.436 + * @typedef {{ 1.437 + * constructor: !Function, 1.438 + * className: string, 1.439 + * cssClass: string, 1.440 + * widget: (string|boolean), 1.441 + * callbacks: !Array<function(!HTMLElement)> 1.442 + * }} 1.443 + */ 1.444 +componentHandler.ComponentConfig; // jshint ignore:line 1.445 + 1.446 +/** 1.447 + * Created component (i.e., upgraded element) type as managed by 1.448 + * componentHandler. Provided for benefit of the Closure compiler. 1.449 + * 1.450 + * @typedef {{ 1.451 + * element_: !HTMLElement, 1.452 + * className: string, 1.453 + * classAsString: string, 1.454 + * cssClass: string, 1.455 + * widget: string 1.456 + * }} 1.457 + */ 1.458 +componentHandler.Component; // jshint ignore:line 1.459 + 1.460 +// Export all symbols, for the benefit of Closure compiler. 1.461 +// No effect on uncompiled code. 1.462 +componentHandler['upgradeDom'] = componentHandler.upgradeDom; 1.463 +componentHandler['upgradeElement'] = componentHandler.upgradeElement; 1.464 +componentHandler['upgradeElements'] = componentHandler.upgradeElements; 1.465 +componentHandler['upgradeAllRegistered'] = 1.466 + componentHandler.upgradeAllRegistered; 1.467 +componentHandler['registerUpgradedCallback'] = 1.468 + componentHandler.registerUpgradedCallback; 1.469 +componentHandler['register'] = componentHandler.register; 1.470 +componentHandler['downgradeElements'] = componentHandler.downgradeElements; 1.471 +window.componentHandler = componentHandler; 1.472 +window['componentHandler'] = componentHandler; 1.473 + 1.474 +window.addEventListener('load', function() { 1.475 + 'use strict'; 1.476 + 1.477 + /** 1.478 + * Performs a "Cutting the mustard" test. If the browser supports the features 1.479 + * tested, adds a mdl-js class to the <html> element. It then upgrades all MDL 1.480 + * components requiring JavaScript. 1.481 + */ 1.482 + if ('classList' in document.createElement('div') && 1.483 + 'querySelector' in document && 1.484 + 'addEventListener' in window && Array.prototype.forEach) { 1.485 + document.documentElement.classList.add('mdl-js'); 1.486 + componentHandler.upgradeAllRegistered(); 1.487 + } else { 1.488 + /** 1.489 + * Dummy function to avoid JS errors. 1.490 + */ 1.491 + componentHandler.upgradeElement = function() {}; 1.492 + /** 1.493 + * Dummy function to avoid JS errors. 1.494 + */ 1.495 + componentHandler.register = function() {}; 1.496 + } 1.497 +}); 1.498 + 1.499 +// Source: https://github.com/darius/requestAnimationFrame/blob/master/requestAnimationFrame.js 1.500 +// Adapted from https://gist.github.com/paulirish/1579671 which derived from 1.501 +// http://paulirish.com/2011/requestanimationframe-for-smart-animating/ 1.502 +// http://my.opera.com/emoller/blog/2011/12/20/requestanimationframe-for-smart-er-animating 1.503 +// requestAnimationFrame polyfill by Erik Möller. 1.504 +// Fixes from Paul Irish, Tino Zijdel, Andrew Mao, Klemen Slavič, Darius Bacon 1.505 +// MIT license 1.506 +if (!Date.now) { 1.507 + /** 1.508 + * Date.now polyfill. 1.509 + * @return {number} the current Date 1.510 + */ 1.511 + Date.now = function () { 1.512 + return new Date().getTime(); 1.513 + }; 1.514 + Date['now'] = Date.now; 1.515 +} 1.516 +var vendors = [ 1.517 + 'webkit', 1.518 + 'moz' 1.519 +]; 1.520 +for (var i = 0; i < vendors.length && !window.requestAnimationFrame; ++i) { 1.521 + var vp = vendors[i]; 1.522 + window.requestAnimationFrame = window[vp + 'RequestAnimationFrame']; 1.523 + window.cancelAnimationFrame = window[vp + 'CancelAnimationFrame'] || window[vp + 'CancelRequestAnimationFrame']; 1.524 + window['requestAnimationFrame'] = window.requestAnimationFrame; 1.525 + window['cancelAnimationFrame'] = window.cancelAnimationFrame; 1.526 +} 1.527 +if (/iP(ad|hone|od).*OS 6/.test(window.navigator.userAgent) || !window.requestAnimationFrame || !window.cancelAnimationFrame) { 1.528 + var lastTime = 0; 1.529 + /** 1.530 + * requestAnimationFrame polyfill. 1.531 + * @param {!Function} callback the callback function. 1.532 + */ 1.533 + window.requestAnimationFrame = function (callback) { 1.534 + var now = Date.now(); 1.535 + var nextTime = Math.max(lastTime + 16, now); 1.536 + return setTimeout(function () { 1.537 + callback(lastTime = nextTime); 1.538 + }, nextTime - now); 1.539 + }; 1.540 + window.cancelAnimationFrame = clearTimeout; 1.541 + window['requestAnimationFrame'] = window.requestAnimationFrame; 1.542 + window['cancelAnimationFrame'] = window.cancelAnimationFrame; 1.543 +} 1.544 +/** 1.545 + * @license 1.546 + * Copyright 2015 Google Inc. All Rights Reserved. 1.547 + * 1.548 + * Licensed under the Apache License, Version 2.0 (the "License"); 1.549 + * you may not use this file except in compliance with the License. 1.550 + * You may obtain a copy of the License at 1.551 + * 1.552 + * http://www.apache.org/licenses/LICENSE-2.0 1.553 + * 1.554 + * Unless required by applicable law or agreed to in writing, software 1.555 + * distributed under the License is distributed on an "AS IS" BASIS, 1.556 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 1.557 + * See the License for the specific language governing permissions and 1.558 + * limitations under the License. 1.559 + */ 1.560 +/** 1.561 + * Class constructor for Button MDL component. 1.562 + * Implements MDL component design pattern defined at: 1.563 + * https://github.com/jasonmayes/mdl-component-design-pattern 1.564 + * 1.565 + * @param {HTMLElement} element The element that will be upgraded. 1.566 + */ 1.567 +var MaterialButton = function MaterialButton(element) { 1.568 + this.element_ = element; 1.569 + // Initialize instance. 1.570 + this.init(); 1.571 +}; 1.572 +window['MaterialButton'] = MaterialButton; 1.573 +/** 1.574 + * Store constants in one place so they can be updated easily. 1.575 + * 1.576 + * @enum {string | number} 1.577 + * @private 1.578 + */ 1.579 +MaterialButton.prototype.Constant_ = {}; 1.580 +/** 1.581 + * Store strings for class names defined by this component that are used in 1.582 + * JavaScript. This allows us to simply change it in one place should we 1.583 + * decide to modify at a later date. 1.584 + * 1.585 + * @enum {string} 1.586 + * @private 1.587 + */ 1.588 +MaterialButton.prototype.CssClasses_ = { 1.589 + RIPPLE_EFFECT: 'mdl-js-ripple-effect', 1.590 + RIPPLE_CONTAINER: 'mdl-button__ripple-container', 1.591 + RIPPLE: 'mdl-ripple' 1.592 +}; 1.593 +/** 1.594 + * Handle blur of element. 1.595 + * 1.596 + * @param {Event} event The event that fired. 1.597 + * @private 1.598 + */ 1.599 +MaterialButton.prototype.blurHandler_ = function (event) { 1.600 + if (event) { 1.601 + this.element_.blur(); 1.602 + } 1.603 +}; 1.604 +// Public methods. 1.605 +/** 1.606 + * Disable button. 1.607 + * 1.608 + * @public 1.609 + */ 1.610 +MaterialButton.prototype.disable = function () { 1.611 + this.element_.disabled = true; 1.612 +}; 1.613 +MaterialButton.prototype['disable'] = MaterialButton.prototype.disable; 1.614 +/** 1.615 + * Enable button. 1.616 + * 1.617 + * @public 1.618 + */ 1.619 +MaterialButton.prototype.enable = function () { 1.620 + this.element_.disabled = false; 1.621 +}; 1.622 +MaterialButton.prototype['enable'] = MaterialButton.prototype.enable; 1.623 +/** 1.624 + * Initialize element. 1.625 + */ 1.626 +MaterialButton.prototype.init = function () { 1.627 + if (this.element_) { 1.628 + if (this.element_.classList.contains(this.CssClasses_.RIPPLE_EFFECT)) { 1.629 + var rippleContainer = document.createElement('span'); 1.630 + rippleContainer.classList.add(this.CssClasses_.RIPPLE_CONTAINER); 1.631 + this.rippleElement_ = document.createElement('span'); 1.632 + this.rippleElement_.classList.add(this.CssClasses_.RIPPLE); 1.633 + rippleContainer.appendChild(this.rippleElement_); 1.634 + this.boundRippleBlurHandler = this.blurHandler_.bind(this); 1.635 + this.rippleElement_.addEventListener('mouseup', this.boundRippleBlurHandler); 1.636 + this.element_.appendChild(rippleContainer); 1.637 + } 1.638 + this.boundButtonBlurHandler = this.blurHandler_.bind(this); 1.639 + this.element_.addEventListener('mouseup', this.boundButtonBlurHandler); 1.640 + this.element_.addEventListener('mouseleave', this.boundButtonBlurHandler); 1.641 + } 1.642 +}; 1.643 +// The component registers itself. It can assume componentHandler is available 1.644 +// in the global scope. 1.645 +componentHandler.register({ 1.646 + constructor: MaterialButton, 1.647 + classAsString: 'MaterialButton', 1.648 + cssClass: 'mdl-js-button', 1.649 + widget: true 1.650 +}); 1.651 +/** 1.652 + * @license 1.653 + * Copyright 2015 Google Inc. All Rights Reserved. 1.654 + * 1.655 + * Licensed under the Apache License, Version 2.0 (the "License"); 1.656 + * you may not use this file except in compliance with the License. 1.657 + * You may obtain a copy of the License at 1.658 + * 1.659 + * http://www.apache.org/licenses/LICENSE-2.0 1.660 + * 1.661 + * Unless required by applicable law or agreed to in writing, software 1.662 + * distributed under the License is distributed on an "AS IS" BASIS, 1.663 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 1.664 + * See the License for the specific language governing permissions and 1.665 + * limitations under the License. 1.666 + */ 1.667 +/** 1.668 + * Class constructor for Checkbox MDL component. 1.669 + * Implements MDL component design pattern defined at: 1.670 + * https://github.com/jasonmayes/mdl-component-design-pattern 1.671 + * 1.672 + * @constructor 1.673 + * @param {HTMLElement} element The element that will be upgraded. 1.674 + */ 1.675 +var MaterialCheckbox = function MaterialCheckbox(element) { 1.676 + this.element_ = element; 1.677 + // Initialize instance. 1.678 + this.init(); 1.679 +}; 1.680 +window['MaterialCheckbox'] = MaterialCheckbox; 1.681 +/** 1.682 + * Store constants in one place so they can be updated easily. 1.683 + * 1.684 + * @enum {string | number} 1.685 + * @private 1.686 + */ 1.687 +MaterialCheckbox.prototype.Constant_ = { TINY_TIMEOUT: 0.001 }; 1.688 +/** 1.689 + * Store strings for class names defined by this component that are used in 1.690 + * JavaScript. This allows us to simply change it in one place should we 1.691 + * decide to modify at a later date. 1.692 + * 1.693 + * @enum {string} 1.694 + * @private 1.695 + */ 1.696 +MaterialCheckbox.prototype.CssClasses_ = { 1.697 + INPUT: 'mdl-checkbox__input', 1.698 + BOX_OUTLINE: 'mdl-checkbox__box-outline', 1.699 + FOCUS_HELPER: 'mdl-checkbox__focus-helper', 1.700 + TICK_OUTLINE: 'mdl-checkbox__tick-outline', 1.701 + RIPPLE_EFFECT: 'mdl-js-ripple-effect', 1.702 + RIPPLE_IGNORE_EVENTS: 'mdl-js-ripple-effect--ignore-events', 1.703 + RIPPLE_CONTAINER: 'mdl-checkbox__ripple-container', 1.704 + RIPPLE_CENTER: 'mdl-ripple--center', 1.705 + RIPPLE: 'mdl-ripple', 1.706 + IS_FOCUSED: 'is-focused', 1.707 + IS_DISABLED: 'is-disabled', 1.708 + IS_CHECKED: 'is-checked', 1.709 + IS_UPGRADED: 'is-upgraded' 1.710 +}; 1.711 +/** 1.712 + * Handle change of state. 1.713 + * 1.714 + * @param {Event} event The event that fired. 1.715 + * @private 1.716 + */ 1.717 +MaterialCheckbox.prototype.onChange_ = function (event) { 1.718 + this.updateClasses_(); 1.719 +}; 1.720 +/** 1.721 + * Handle focus of element. 1.722 + * 1.723 + * @param {Event} event The event that fired. 1.724 + * @private 1.725 + */ 1.726 +MaterialCheckbox.prototype.onFocus_ = function (event) { 1.727 + this.element_.classList.add(this.CssClasses_.IS_FOCUSED); 1.728 +}; 1.729 +/** 1.730 + * Handle lost focus of element. 1.731 + * 1.732 + * @param {Event} event The event that fired. 1.733 + * @private 1.734 + */ 1.735 +MaterialCheckbox.prototype.onBlur_ = function (event) { 1.736 + this.element_.classList.remove(this.CssClasses_.IS_FOCUSED); 1.737 +}; 1.738 +/** 1.739 + * Handle mouseup. 1.740 + * 1.741 + * @param {Event} event The event that fired. 1.742 + * @private 1.743 + */ 1.744 +MaterialCheckbox.prototype.onMouseUp_ = function (event) { 1.745 + this.blur_(); 1.746 +}; 1.747 +/** 1.748 + * Handle class updates. 1.749 + * 1.750 + * @private 1.751 + */ 1.752 +MaterialCheckbox.prototype.updateClasses_ = function () { 1.753 + this.checkDisabled(); 1.754 + this.checkToggleState(); 1.755 +}; 1.756 +/** 1.757 + * Add blur. 1.758 + * 1.759 + * @private 1.760 + */ 1.761 +MaterialCheckbox.prototype.blur_ = function () { 1.762 + // TODO: figure out why there's a focus event being fired after our blur, 1.763 + // so that we can avoid this hack. 1.764 + window.setTimeout(function () { 1.765 + this.inputElement_.blur(); 1.766 + }.bind(this), this.Constant_.TINY_TIMEOUT); 1.767 +}; 1.768 +// Public methods. 1.769 +/** 1.770 + * Check the inputs toggle state and update display. 1.771 + * 1.772 + * @public 1.773 + */ 1.774 +MaterialCheckbox.prototype.checkToggleState = function () { 1.775 + if (this.inputElement_.checked) { 1.776 + this.element_.classList.add(this.CssClasses_.IS_CHECKED); 1.777 + } else { 1.778 + this.element_.classList.remove(this.CssClasses_.IS_CHECKED); 1.779 + } 1.780 +}; 1.781 +MaterialCheckbox.prototype['checkToggleState'] = MaterialCheckbox.prototype.checkToggleState; 1.782 +/** 1.783 + * Check the inputs disabled state and update display. 1.784 + * 1.785 + * @public 1.786 + */ 1.787 +MaterialCheckbox.prototype.checkDisabled = function () { 1.788 + if (this.inputElement_.disabled) { 1.789 + this.element_.classList.add(this.CssClasses_.IS_DISABLED); 1.790 + } else { 1.791 + this.element_.classList.remove(this.CssClasses_.IS_DISABLED); 1.792 + } 1.793 +}; 1.794 +MaterialCheckbox.prototype['checkDisabled'] = MaterialCheckbox.prototype.checkDisabled; 1.795 +/** 1.796 + * Disable checkbox. 1.797 + * 1.798 + * @public 1.799 + */ 1.800 +MaterialCheckbox.prototype.disable = function () { 1.801 + this.inputElement_.disabled = true; 1.802 + this.updateClasses_(); 1.803 +}; 1.804 +MaterialCheckbox.prototype['disable'] = MaterialCheckbox.prototype.disable; 1.805 +/** 1.806 + * Enable checkbox. 1.807 + * 1.808 + * @public 1.809 + */ 1.810 +MaterialCheckbox.prototype.enable = function () { 1.811 + this.inputElement_.disabled = false; 1.812 + this.updateClasses_(); 1.813 +}; 1.814 +MaterialCheckbox.prototype['enable'] = MaterialCheckbox.prototype.enable; 1.815 +/** 1.816 + * Check checkbox. 1.817 + * 1.818 + * @public 1.819 + */ 1.820 +MaterialCheckbox.prototype.check = function () { 1.821 + this.inputElement_.checked = true; 1.822 + this.updateClasses_(); 1.823 +}; 1.824 +MaterialCheckbox.prototype['check'] = MaterialCheckbox.prototype.check; 1.825 +/** 1.826 + * Uncheck checkbox. 1.827 + * 1.828 + * @public 1.829 + */ 1.830 +MaterialCheckbox.prototype.uncheck = function () { 1.831 + this.inputElement_.checked = false; 1.832 + this.updateClasses_(); 1.833 +}; 1.834 +MaterialCheckbox.prototype['uncheck'] = MaterialCheckbox.prototype.uncheck; 1.835 +/** 1.836 + * Initialize element. 1.837 + */ 1.838 +MaterialCheckbox.prototype.init = function () { 1.839 + if (this.element_) { 1.840 + this.inputElement_ = this.element_.querySelector('.' + this.CssClasses_.INPUT); 1.841 + var boxOutline = document.createElement('span'); 1.842 + boxOutline.classList.add(this.CssClasses_.BOX_OUTLINE); 1.843 + var tickContainer = document.createElement('span'); 1.844 + tickContainer.classList.add(this.CssClasses_.FOCUS_HELPER); 1.845 + var tickOutline = document.createElement('span'); 1.846 + tickOutline.classList.add(this.CssClasses_.TICK_OUTLINE); 1.847 + boxOutline.appendChild(tickOutline); 1.848 + this.element_.appendChild(tickContainer); 1.849 + this.element_.appendChild(boxOutline); 1.850 + if (this.element_.classList.contains(this.CssClasses_.RIPPLE_EFFECT)) { 1.851 + this.element_.classList.add(this.CssClasses_.RIPPLE_IGNORE_EVENTS); 1.852 + this.rippleContainerElement_ = document.createElement('span'); 1.853 + this.rippleContainerElement_.classList.add(this.CssClasses_.RIPPLE_CONTAINER); 1.854 + this.rippleContainerElement_.classList.add(this.CssClasses_.RIPPLE_EFFECT); 1.855 + this.rippleContainerElement_.classList.add(this.CssClasses_.RIPPLE_CENTER); 1.856 + this.boundRippleMouseUp = this.onMouseUp_.bind(this); 1.857 + this.rippleContainerElement_.addEventListener('mouseup', this.boundRippleMouseUp); 1.858 + var ripple = document.createElement('span'); 1.859 + ripple.classList.add(this.CssClasses_.RIPPLE); 1.860 + this.rippleContainerElement_.appendChild(ripple); 1.861 + this.element_.appendChild(this.rippleContainerElement_); 1.862 + } 1.863 + this.boundInputOnChange = this.onChange_.bind(this); 1.864 + this.boundInputOnFocus = this.onFocus_.bind(this); 1.865 + this.boundInputOnBlur = this.onBlur_.bind(this); 1.866 + this.boundElementMouseUp = this.onMouseUp_.bind(this); 1.867 + this.inputElement_.addEventListener('change', this.boundInputOnChange); 1.868 + this.inputElement_.addEventListener('focus', this.boundInputOnFocus); 1.869 + this.inputElement_.addEventListener('blur', this.boundInputOnBlur); 1.870 + this.element_.addEventListener('mouseup', this.boundElementMouseUp); 1.871 + this.updateClasses_(); 1.872 + this.element_.classList.add(this.CssClasses_.IS_UPGRADED); 1.873 + } 1.874 +}; 1.875 +// The component registers itself. It can assume componentHandler is available 1.876 +// in the global scope. 1.877 +componentHandler.register({ 1.878 + constructor: MaterialCheckbox, 1.879 + classAsString: 'MaterialCheckbox', 1.880 + cssClass: 'mdl-js-checkbox', 1.881 + widget: true 1.882 +}); 1.883 +/** 1.884 + * @license 1.885 + * Copyright 2015 Google Inc. All Rights Reserved. 1.886 + * 1.887 + * Licensed under the Apache License, Version 2.0 (the "License"); 1.888 + * you may not use this file except in compliance with the License. 1.889 + * You may obtain a copy of the License at 1.890 + * 1.891 + * http://www.apache.org/licenses/LICENSE-2.0 1.892 + * 1.893 + * Unless required by applicable law or agreed to in writing, software 1.894 + * distributed under the License is distributed on an "AS IS" BASIS, 1.895 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 1.896 + * See the License for the specific language governing permissions and 1.897 + * limitations under the License. 1.898 + */ 1.899 +/** 1.900 + * Class constructor for icon toggle MDL component. 1.901 + * Implements MDL component design pattern defined at: 1.902 + * https://github.com/jasonmayes/mdl-component-design-pattern 1.903 + * 1.904 + * @constructor 1.905 + * @param {HTMLElement} element The element that will be upgraded. 1.906 + */ 1.907 +var MaterialIconToggle = function MaterialIconToggle(element) { 1.908 + this.element_ = element; 1.909 + // Initialize instance. 1.910 + this.init(); 1.911 +}; 1.912 +window['MaterialIconToggle'] = MaterialIconToggle; 1.913 +/** 1.914 + * Store constants in one place so they can be updated easily. 1.915 + * 1.916 + * @enum {string | number} 1.917 + * @private 1.918 + */ 1.919 +MaterialIconToggle.prototype.Constant_ = { TINY_TIMEOUT: 0.001 }; 1.920 +/** 1.921 + * Store strings for class names defined by this component that are used in 1.922 + * JavaScript. This allows us to simply change it in one place should we 1.923 + * decide to modify at a later date. 1.924 + * 1.925 + * @enum {string} 1.926 + * @private 1.927 + */ 1.928 +MaterialIconToggle.prototype.CssClasses_ = { 1.929 + INPUT: 'mdl-icon-toggle__input', 1.930 + JS_RIPPLE_EFFECT: 'mdl-js-ripple-effect', 1.931 + RIPPLE_IGNORE_EVENTS: 'mdl-js-ripple-effect--ignore-events', 1.932 + RIPPLE_CONTAINER: 'mdl-icon-toggle__ripple-container', 1.933 + RIPPLE_CENTER: 'mdl-ripple--center', 1.934 + RIPPLE: 'mdl-ripple', 1.935 + IS_FOCUSED: 'is-focused', 1.936 + IS_DISABLED: 'is-disabled', 1.937 + IS_CHECKED: 'is-checked' 1.938 +}; 1.939 +/** 1.940 + * Handle change of state. 1.941 + * 1.942 + * @param {Event} event The event that fired. 1.943 + * @private 1.944 + */ 1.945 +MaterialIconToggle.prototype.onChange_ = function (event) { 1.946 + this.updateClasses_(); 1.947 +}; 1.948 +/** 1.949 + * Handle focus of element. 1.950 + * 1.951 + * @param {Event} event The event that fired. 1.952 + * @private 1.953 + */ 1.954 +MaterialIconToggle.prototype.onFocus_ = function (event) { 1.955 + this.element_.classList.add(this.CssClasses_.IS_FOCUSED); 1.956 +}; 1.957 +/** 1.958 + * Handle lost focus of element. 1.959 + * 1.960 + * @param {Event} event The event that fired. 1.961 + * @private 1.962 + */ 1.963 +MaterialIconToggle.prototype.onBlur_ = function (event) { 1.964 + this.element_.classList.remove(this.CssClasses_.IS_FOCUSED); 1.965 +}; 1.966 +/** 1.967 + * Handle mouseup. 1.968 + * 1.969 + * @param {Event} event The event that fired. 1.970 + * @private 1.971 + */ 1.972 +MaterialIconToggle.prototype.onMouseUp_ = function (event) { 1.973 + this.blur_(); 1.974 +}; 1.975 +/** 1.976 + * Handle class updates. 1.977 + * 1.978 + * @private 1.979 + */ 1.980 +MaterialIconToggle.prototype.updateClasses_ = function () { 1.981 + this.checkDisabled(); 1.982 + this.checkToggleState(); 1.983 +}; 1.984 +/** 1.985 + * Add blur. 1.986 + * 1.987 + * @private 1.988 + */ 1.989 +MaterialIconToggle.prototype.blur_ = function () { 1.990 + // TODO: figure out why there's a focus event being fired after our blur, 1.991 + // so that we can avoid this hack. 1.992 + window.setTimeout(function () { 1.993 + this.inputElement_.blur(); 1.994 + }.bind(this), this.Constant_.TINY_TIMEOUT); 1.995 +}; 1.996 +// Public methods. 1.997 +/** 1.998 + * Check the inputs toggle state and update display. 1.999 + * 1.1000 + * @public 1.1001 + */ 1.1002 +MaterialIconToggle.prototype.checkToggleState = function () { 1.1003 + if (this.inputElement_.checked) { 1.1004 + this.element_.classList.add(this.CssClasses_.IS_CHECKED); 1.1005 + } else { 1.1006 + this.element_.classList.remove(this.CssClasses_.IS_CHECKED); 1.1007 + } 1.1008 +}; 1.1009 +MaterialIconToggle.prototype['checkToggleState'] = MaterialIconToggle.prototype.checkToggleState; 1.1010 +/** 1.1011 + * Check the inputs disabled state and update display. 1.1012 + * 1.1013 + * @public 1.1014 + */ 1.1015 +MaterialIconToggle.prototype.checkDisabled = function () { 1.1016 + if (this.inputElement_.disabled) { 1.1017 + this.element_.classList.add(this.CssClasses_.IS_DISABLED); 1.1018 + } else { 1.1019 + this.element_.classList.remove(this.CssClasses_.IS_DISABLED); 1.1020 + } 1.1021 +}; 1.1022 +MaterialIconToggle.prototype['checkDisabled'] = MaterialIconToggle.prototype.checkDisabled; 1.1023 +/** 1.1024 + * Disable icon toggle. 1.1025 + * 1.1026 + * @public 1.1027 + */ 1.1028 +MaterialIconToggle.prototype.disable = function () { 1.1029 + this.inputElement_.disabled = true; 1.1030 + this.updateClasses_(); 1.1031 +}; 1.1032 +MaterialIconToggle.prototype['disable'] = MaterialIconToggle.prototype.disable; 1.1033 +/** 1.1034 + * Enable icon toggle. 1.1035 + * 1.1036 + * @public 1.1037 + */ 1.1038 +MaterialIconToggle.prototype.enable = function () { 1.1039 + this.inputElement_.disabled = false; 1.1040 + this.updateClasses_(); 1.1041 +}; 1.1042 +MaterialIconToggle.prototype['enable'] = MaterialIconToggle.prototype.enable; 1.1043 +/** 1.1044 + * Check icon toggle. 1.1045 + * 1.1046 + * @public 1.1047 + */ 1.1048 +MaterialIconToggle.prototype.check = function () { 1.1049 + this.inputElement_.checked = true; 1.1050 + this.updateClasses_(); 1.1051 +}; 1.1052 +MaterialIconToggle.prototype['check'] = MaterialIconToggle.prototype.check; 1.1053 +/** 1.1054 + * Uncheck icon toggle. 1.1055 + * 1.1056 + * @public 1.1057 + */ 1.1058 +MaterialIconToggle.prototype.uncheck = function () { 1.1059 + this.inputElement_.checked = false; 1.1060 + this.updateClasses_(); 1.1061 +}; 1.1062 +MaterialIconToggle.prototype['uncheck'] = MaterialIconToggle.prototype.uncheck; 1.1063 +/** 1.1064 + * Initialize element. 1.1065 + */ 1.1066 +MaterialIconToggle.prototype.init = function () { 1.1067 + if (this.element_) { 1.1068 + this.inputElement_ = this.element_.querySelector('.' + this.CssClasses_.INPUT); 1.1069 + if (this.element_.classList.contains(this.CssClasses_.JS_RIPPLE_EFFECT)) { 1.1070 + this.element_.classList.add(this.CssClasses_.RIPPLE_IGNORE_EVENTS); 1.1071 + this.rippleContainerElement_ = document.createElement('span'); 1.1072 + this.rippleContainerElement_.classList.add(this.CssClasses_.RIPPLE_CONTAINER); 1.1073 + this.rippleContainerElement_.classList.add(this.CssClasses_.JS_RIPPLE_EFFECT); 1.1074 + this.rippleContainerElement_.classList.add(this.CssClasses_.RIPPLE_CENTER); 1.1075 + this.boundRippleMouseUp = this.onMouseUp_.bind(this); 1.1076 + this.rippleContainerElement_.addEventListener('mouseup', this.boundRippleMouseUp); 1.1077 + var ripple = document.createElement('span'); 1.1078 + ripple.classList.add(this.CssClasses_.RIPPLE); 1.1079 + this.rippleContainerElement_.appendChild(ripple); 1.1080 + this.element_.appendChild(this.rippleContainerElement_); 1.1081 + } 1.1082 + this.boundInputOnChange = this.onChange_.bind(this); 1.1083 + this.boundInputOnFocus = this.onFocus_.bind(this); 1.1084 + this.boundInputOnBlur = this.onBlur_.bind(this); 1.1085 + this.boundElementOnMouseUp = this.onMouseUp_.bind(this); 1.1086 + this.inputElement_.addEventListener('change', this.boundInputOnChange); 1.1087 + this.inputElement_.addEventListener('focus', this.boundInputOnFocus); 1.1088 + this.inputElement_.addEventListener('blur', this.boundInputOnBlur); 1.1089 + this.element_.addEventListener('mouseup', this.boundElementOnMouseUp); 1.1090 + this.updateClasses_(); 1.1091 + this.element_.classList.add('is-upgraded'); 1.1092 + } 1.1093 +}; 1.1094 +// The component registers itself. It can assume componentHandler is available 1.1095 +// in the global scope. 1.1096 +componentHandler.register({ 1.1097 + constructor: MaterialIconToggle, 1.1098 + classAsString: 'MaterialIconToggle', 1.1099 + cssClass: 'mdl-js-icon-toggle', 1.1100 + widget: true 1.1101 +}); 1.1102 +/** 1.1103 + * @license 1.1104 + * Copyright 2015 Google Inc. All Rights Reserved. 1.1105 + * 1.1106 + * Licensed under the Apache License, Version 2.0 (the "License"); 1.1107 + * you may not use this file except in compliance with the License. 1.1108 + * You may obtain a copy of the License at 1.1109 + * 1.1110 + * http://www.apache.org/licenses/LICENSE-2.0 1.1111 + * 1.1112 + * Unless required by applicable law or agreed to in writing, software 1.1113 + * distributed under the License is distributed on an "AS IS" BASIS, 1.1114 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 1.1115 + * See the License for the specific language governing permissions and 1.1116 + * limitations under the License. 1.1117 + */ 1.1118 +/** 1.1119 + * Class constructor for dropdown MDL component. 1.1120 + * Implements MDL component design pattern defined at: 1.1121 + * https://github.com/jasonmayes/mdl-component-design-pattern 1.1122 + * 1.1123 + * @constructor 1.1124 + * @param {HTMLElement} element The element that will be upgraded. 1.1125 + */ 1.1126 +var MaterialMenu = function MaterialMenu(element) { 1.1127 + this.element_ = element; 1.1128 + // Initialize instance. 1.1129 + this.init(); 1.1130 +}; 1.1131 +window['MaterialMenu'] = MaterialMenu; 1.1132 +/** 1.1133 + * Store constants in one place so they can be updated easily. 1.1134 + * 1.1135 + * @enum {string | number} 1.1136 + * @private 1.1137 + */ 1.1138 +MaterialMenu.prototype.Constant_ = { 1.1139 + // Total duration of the menu animation. 1.1140 + TRANSITION_DURATION_SECONDS: 0.3, 1.1141 + // The fraction of the total duration we want to use for menu item animations. 1.1142 + TRANSITION_DURATION_FRACTION: 0.8, 1.1143 + // How long the menu stays open after choosing an option (so the user can see 1.1144 + // the ripple). 1.1145 + CLOSE_TIMEOUT: 150 1.1146 +}; 1.1147 +/** 1.1148 + * Keycodes, for code readability. 1.1149 + * 1.1150 + * @enum {number} 1.1151 + * @private 1.1152 + */ 1.1153 +MaterialMenu.prototype.Keycodes_ = { 1.1154 + ENTER: 13, 1.1155 + ESCAPE: 27, 1.1156 + SPACE: 32, 1.1157 + UP_ARROW: 38, 1.1158 + DOWN_ARROW: 40 1.1159 +}; 1.1160 +/** 1.1161 + * Store strings for class names defined by this component that are used in 1.1162 + * JavaScript. This allows us to simply change it in one place should we 1.1163 + * decide to modify at a later date. 1.1164 + * 1.1165 + * @enum {string} 1.1166 + * @private 1.1167 + */ 1.1168 +MaterialMenu.prototype.CssClasses_ = { 1.1169 + CONTAINER: 'mdl-menu__container', 1.1170 + OUTLINE: 'mdl-menu__outline', 1.1171 + ITEM: 'mdl-menu__item', 1.1172 + ITEM_RIPPLE_CONTAINER: 'mdl-menu__item-ripple-container', 1.1173 + RIPPLE_EFFECT: 'mdl-js-ripple-effect', 1.1174 + RIPPLE_IGNORE_EVENTS: 'mdl-js-ripple-effect--ignore-events', 1.1175 + RIPPLE: 'mdl-ripple', 1.1176 + // Statuses 1.1177 + IS_UPGRADED: 'is-upgraded', 1.1178 + IS_VISIBLE: 'is-visible', 1.1179 + IS_ANIMATING: 'is-animating', 1.1180 + // Alignment options 1.1181 + BOTTOM_LEFT: 'mdl-menu--bottom-left', 1.1182 + // This is the default. 1.1183 + BOTTOM_RIGHT: 'mdl-menu--bottom-right', 1.1184 + TOP_LEFT: 'mdl-menu--top-left', 1.1185 + TOP_RIGHT: 'mdl-menu--top-right', 1.1186 + UNALIGNED: 'mdl-menu--unaligned' 1.1187 +}; 1.1188 +/** 1.1189 + * Initialize element. 1.1190 + */ 1.1191 +MaterialMenu.prototype.init = function () { 1.1192 + if (this.element_) { 1.1193 + // Create container for the menu. 1.1194 + var container = document.createElement('div'); 1.1195 + container.classList.add(this.CssClasses_.CONTAINER); 1.1196 + this.element_.parentElement.insertBefore(container, this.element_); 1.1197 + this.element_.parentElement.removeChild(this.element_); 1.1198 + container.appendChild(this.element_); 1.1199 + this.container_ = container; 1.1200 + // Create outline for the menu (shadow and background). 1.1201 + var outline = document.createElement('div'); 1.1202 + outline.classList.add(this.CssClasses_.OUTLINE); 1.1203 + this.outline_ = outline; 1.1204 + container.insertBefore(outline, this.element_); 1.1205 + // Find the "for" element and bind events to it. 1.1206 + var forElId = this.element_.getAttribute('for') || this.element_.getAttribute('data-mdl-for'); 1.1207 + var forEl = null; 1.1208 + if (forElId) { 1.1209 + forEl = document.getElementById(forElId); 1.1210 + if (forEl) { 1.1211 + this.forElement_ = forEl; 1.1212 + forEl.addEventListener('click', this.handleForClick_.bind(this)); 1.1213 + forEl.addEventListener('keydown', this.handleForKeyboardEvent_.bind(this)); 1.1214 + } 1.1215 + } 1.1216 + var items = this.element_.querySelectorAll('.' + this.CssClasses_.ITEM); 1.1217 + this.boundItemKeydown_ = this.handleItemKeyboardEvent_.bind(this); 1.1218 + this.boundItemClick_ = this.handleItemClick_.bind(this); 1.1219 + for (var i = 0; i < items.length; i++) { 1.1220 + // Add a listener to each menu item. 1.1221 + items[i].addEventListener('click', this.boundItemClick_); 1.1222 + // Add a tab index to each menu item. 1.1223 + items[i].tabIndex = '-1'; 1.1224 + // Add a keyboard listener to each menu item. 1.1225 + items[i].addEventListener('keydown', this.boundItemKeydown_); 1.1226 + } 1.1227 + // Add ripple classes to each item, if the user has enabled ripples. 1.1228 + if (this.element_.classList.contains(this.CssClasses_.RIPPLE_EFFECT)) { 1.1229 + this.element_.classList.add(this.CssClasses_.RIPPLE_IGNORE_EVENTS); 1.1230 + for (i = 0; i < items.length; i++) { 1.1231 + var item = items[i]; 1.1232 + var rippleContainer = document.createElement('span'); 1.1233 + rippleContainer.classList.add(this.CssClasses_.ITEM_RIPPLE_CONTAINER); 1.1234 + var ripple = document.createElement('span'); 1.1235 + ripple.classList.add(this.CssClasses_.RIPPLE); 1.1236 + rippleContainer.appendChild(ripple); 1.1237 + item.appendChild(rippleContainer); 1.1238 + item.classList.add(this.CssClasses_.RIPPLE_EFFECT); 1.1239 + } 1.1240 + } 1.1241 + // Copy alignment classes to the container, so the outline can use them. 1.1242 + if (this.element_.classList.contains(this.CssClasses_.BOTTOM_LEFT)) { 1.1243 + this.outline_.classList.add(this.CssClasses_.BOTTOM_LEFT); 1.1244 + } 1.1245 + if (this.element_.classList.contains(this.CssClasses_.BOTTOM_RIGHT)) { 1.1246 + this.outline_.classList.add(this.CssClasses_.BOTTOM_RIGHT); 1.1247 + } 1.1248 + if (this.element_.classList.contains(this.CssClasses_.TOP_LEFT)) { 1.1249 + this.outline_.classList.add(this.CssClasses_.TOP_LEFT); 1.1250 + } 1.1251 + if (this.element_.classList.contains(this.CssClasses_.TOP_RIGHT)) { 1.1252 + this.outline_.classList.add(this.CssClasses_.TOP_RIGHT); 1.1253 + } 1.1254 + if (this.element_.classList.contains(this.CssClasses_.UNALIGNED)) { 1.1255 + this.outline_.classList.add(this.CssClasses_.UNALIGNED); 1.1256 + } 1.1257 + container.classList.add(this.CssClasses_.IS_UPGRADED); 1.1258 + } 1.1259 +}; 1.1260 +/** 1.1261 + * Handles a click on the "for" element, by positioning the menu and then 1.1262 + * toggling it. 1.1263 + * 1.1264 + * @param {Event} evt The event that fired. 1.1265 + * @private 1.1266 + */ 1.1267 +MaterialMenu.prototype.handleForClick_ = function (evt) { 1.1268 + if (this.element_ && this.forElement_) { 1.1269 + var rect = this.forElement_.getBoundingClientRect(); 1.1270 + var forRect = this.forElement_.parentElement.getBoundingClientRect(); 1.1271 + if (this.element_.classList.contains(this.CssClasses_.UNALIGNED)) { 1.1272 + } else if (this.element_.classList.contains(this.CssClasses_.BOTTOM_RIGHT)) { 1.1273 + // Position below the "for" element, aligned to its right. 1.1274 + this.container_.style.right = forRect.right - rect.right + 'px'; 1.1275 + this.container_.style.top = this.forElement_.offsetTop + this.forElement_.offsetHeight + 'px'; 1.1276 + } else if (this.element_.classList.contains(this.CssClasses_.TOP_LEFT)) { 1.1277 + // Position above the "for" element, aligned to its left. 1.1278 + this.container_.style.left = this.forElement_.offsetLeft + 'px'; 1.1279 + this.container_.style.bottom = forRect.bottom - rect.top + 'px'; 1.1280 + } else if (this.element_.classList.contains(this.CssClasses_.TOP_RIGHT)) { 1.1281 + // Position above the "for" element, aligned to its right. 1.1282 + this.container_.style.right = forRect.right - rect.right + 'px'; 1.1283 + this.container_.style.bottom = forRect.bottom - rect.top + 'px'; 1.1284 + } else { 1.1285 + // Default: position below the "for" element, aligned to its left. 1.1286 + this.container_.style.left = this.forElement_.offsetLeft + 'px'; 1.1287 + this.container_.style.top = this.forElement_.offsetTop + this.forElement_.offsetHeight + 'px'; 1.1288 + } 1.1289 + } 1.1290 + this.toggle(evt); 1.1291 +}; 1.1292 +/** 1.1293 + * Handles a keyboard event on the "for" element. 1.1294 + * 1.1295 + * @param {Event} evt The event that fired. 1.1296 + * @private 1.1297 + */ 1.1298 +MaterialMenu.prototype.handleForKeyboardEvent_ = function (evt) { 1.1299 + if (this.element_ && this.container_ && this.forElement_) { 1.1300 + var items = this.element_.querySelectorAll('.' + this.CssClasses_.ITEM + ':not([disabled])'); 1.1301 + if (items && items.length > 0 && this.container_.classList.contains(this.CssClasses_.IS_VISIBLE)) { 1.1302 + if (evt.keyCode === this.Keycodes_.UP_ARROW) { 1.1303 + evt.preventDefault(); 1.1304 + items[items.length - 1].focus(); 1.1305 + } else if (evt.keyCode === this.Keycodes_.DOWN_ARROW) { 1.1306 + evt.preventDefault(); 1.1307 + items[0].focus(); 1.1308 + } 1.1309 + } 1.1310 + } 1.1311 +}; 1.1312 +/** 1.1313 + * Handles a keyboard event on an item. 1.1314 + * 1.1315 + * @param {Event} evt The event that fired. 1.1316 + * @private 1.1317 + */ 1.1318 +MaterialMenu.prototype.handleItemKeyboardEvent_ = function (evt) { 1.1319 + if (this.element_ && this.container_) { 1.1320 + var items = this.element_.querySelectorAll('.' + this.CssClasses_.ITEM + ':not([disabled])'); 1.1321 + if (items && items.length > 0 && this.container_.classList.contains(this.CssClasses_.IS_VISIBLE)) { 1.1322 + var currentIndex = Array.prototype.slice.call(items).indexOf(evt.target); 1.1323 + if (evt.keyCode === this.Keycodes_.UP_ARROW) { 1.1324 + evt.preventDefault(); 1.1325 + if (currentIndex > 0) { 1.1326 + items[currentIndex - 1].focus(); 1.1327 + } else { 1.1328 + items[items.length - 1].focus(); 1.1329 + } 1.1330 + } else if (evt.keyCode === this.Keycodes_.DOWN_ARROW) { 1.1331 + evt.preventDefault(); 1.1332 + if (items.length > currentIndex + 1) { 1.1333 + items[currentIndex + 1].focus(); 1.1334 + } else { 1.1335 + items[0].focus(); 1.1336 + } 1.1337 + } else if (evt.keyCode === this.Keycodes_.SPACE || evt.keyCode === this.Keycodes_.ENTER) { 1.1338 + evt.preventDefault(); 1.1339 + // Send mousedown and mouseup to trigger ripple. 1.1340 + var e = new MouseEvent('mousedown'); 1.1341 + evt.target.dispatchEvent(e); 1.1342 + e = new MouseEvent('mouseup'); 1.1343 + evt.target.dispatchEvent(e); 1.1344 + // Send click. 1.1345 + evt.target.click(); 1.1346 + } else if (evt.keyCode === this.Keycodes_.ESCAPE) { 1.1347 + evt.preventDefault(); 1.1348 + this.hide(); 1.1349 + } 1.1350 + } 1.1351 + } 1.1352 +}; 1.1353 +/** 1.1354 + * Handles a click event on an item. 1.1355 + * 1.1356 + * @param {Event} evt The event that fired. 1.1357 + * @private 1.1358 + */ 1.1359 +MaterialMenu.prototype.handleItemClick_ = function (evt) { 1.1360 + if (evt.target.hasAttribute('disabled')) { 1.1361 + evt.stopPropagation(); 1.1362 + } else { 1.1363 + // Wait some time before closing menu, so the user can see the ripple. 1.1364 + this.closing_ = true; 1.1365 + window.setTimeout(function (evt) { 1.1366 + this.hide(); 1.1367 + this.closing_ = false; 1.1368 + }.bind(this), this.Constant_.CLOSE_TIMEOUT); 1.1369 + } 1.1370 +}; 1.1371 +/** 1.1372 + * Calculates the initial clip (for opening the menu) or final clip (for closing 1.1373 + * it), and applies it. This allows us to animate from or to the correct point, 1.1374 + * that is, the point it's aligned to in the "for" element. 1.1375 + * 1.1376 + * @param {number} height Height of the clip rectangle 1.1377 + * @param {number} width Width of the clip rectangle 1.1378 + * @private 1.1379 + */ 1.1380 +MaterialMenu.prototype.applyClip_ = function (height, width) { 1.1381 + if (this.element_.classList.contains(this.CssClasses_.UNALIGNED)) { 1.1382 + // Do not clip. 1.1383 + this.element_.style.clip = ''; 1.1384 + } else if (this.element_.classList.contains(this.CssClasses_.BOTTOM_RIGHT)) { 1.1385 + // Clip to the top right corner of the menu. 1.1386 + this.element_.style.clip = 'rect(0 ' + width + 'px ' + '0 ' + width + 'px)'; 1.1387 + } else if (this.element_.classList.contains(this.CssClasses_.TOP_LEFT)) { 1.1388 + // Clip to the bottom left corner of the menu. 1.1389 + this.element_.style.clip = 'rect(' + height + 'px 0 ' + height + 'px 0)'; 1.1390 + } else if (this.element_.classList.contains(this.CssClasses_.TOP_RIGHT)) { 1.1391 + // Clip to the bottom right corner of the menu. 1.1392 + this.element_.style.clip = 'rect(' + height + 'px ' + width + 'px ' + height + 'px ' + width + 'px)'; 1.1393 + } else { 1.1394 + // Default: do not clip (same as clipping to the top left corner). 1.1395 + this.element_.style.clip = ''; 1.1396 + } 1.1397 +}; 1.1398 +/** 1.1399 + * Cleanup function to remove animation listeners. 1.1400 + * 1.1401 + * @param {Event} evt 1.1402 + * @private 1.1403 + */ 1.1404 +MaterialMenu.prototype.removeAnimationEndListener_ = function (evt) { 1.1405 + evt.target.classList.remove(MaterialMenu.prototype.CssClasses_.IS_ANIMATING); 1.1406 +}; 1.1407 +/** 1.1408 + * Adds an event listener to clean up after the animation ends. 1.1409 + * 1.1410 + * @private 1.1411 + */ 1.1412 +MaterialMenu.prototype.addAnimationEndListener_ = function () { 1.1413 + this.element_.addEventListener('transitionend', this.removeAnimationEndListener_); 1.1414 + this.element_.addEventListener('webkitTransitionEnd', this.removeAnimationEndListener_); 1.1415 +}; 1.1416 +/** 1.1417 + * Displays the menu. 1.1418 + * 1.1419 + * @public 1.1420 + */ 1.1421 +MaterialMenu.prototype.show = function (evt) { 1.1422 + if (this.element_ && this.container_ && this.outline_) { 1.1423 + // Measure the inner element. 1.1424 + var height = this.element_.getBoundingClientRect().height; 1.1425 + var width = this.element_.getBoundingClientRect().width; 1.1426 + // Apply the inner element's size to the container and outline. 1.1427 + this.container_.style.width = width + 'px'; 1.1428 + this.container_.style.height = height + 'px'; 1.1429 + this.outline_.style.width = width + 'px'; 1.1430 + this.outline_.style.height = height + 'px'; 1.1431 + var transitionDuration = this.Constant_.TRANSITION_DURATION_SECONDS * this.Constant_.TRANSITION_DURATION_FRACTION; 1.1432 + // Calculate transition delays for individual menu items, so that they fade 1.1433 + // in one at a time. 1.1434 + var items = this.element_.querySelectorAll('.' + this.CssClasses_.ITEM); 1.1435 + for (var i = 0; i < items.length; i++) { 1.1436 + var itemDelay = null; 1.1437 + if (this.element_.classList.contains(this.CssClasses_.TOP_LEFT) || this.element_.classList.contains(this.CssClasses_.TOP_RIGHT)) { 1.1438 + itemDelay = (height - items[i].offsetTop - items[i].offsetHeight) / height * transitionDuration + 's'; 1.1439 + } else { 1.1440 + itemDelay = items[i].offsetTop / height * transitionDuration + 's'; 1.1441 + } 1.1442 + items[i].style.transitionDelay = itemDelay; 1.1443 + } 1.1444 + // Apply the initial clip to the text before we start animating. 1.1445 + this.applyClip_(height, width); 1.1446 + // Wait for the next frame, turn on animation, and apply the final clip. 1.1447 + // Also make it visible. This triggers the transitions. 1.1448 + window.requestAnimationFrame(function () { 1.1449 + this.element_.classList.add(this.CssClasses_.IS_ANIMATING); 1.1450 + this.element_.style.clip = 'rect(0 ' + width + 'px ' + height + 'px 0)'; 1.1451 + this.container_.classList.add(this.CssClasses_.IS_VISIBLE); 1.1452 + }.bind(this)); 1.1453 + // Clean up after the animation is complete. 1.1454 + this.addAnimationEndListener_(); 1.1455 + // Add a click listener to the document, to close the menu. 1.1456 + var callback = function (e) { 1.1457 + // Check to see if the document is processing the same event that 1.1458 + // displayed the menu in the first place. If so, do nothing. 1.1459 + // Also check to see if the menu is in the process of closing itself, and 1.1460 + // do nothing in that case. 1.1461 + // Also check if the clicked element is a menu item 1.1462 + // if so, do nothing. 1.1463 + if (e !== evt && !this.closing_ && e.target.parentNode !== this.element_) { 1.1464 + document.removeEventListener('click', callback); 1.1465 + this.hide(); 1.1466 + } 1.1467 + }.bind(this); 1.1468 + document.addEventListener('click', callback); 1.1469 + } 1.1470 +}; 1.1471 +MaterialMenu.prototype['show'] = MaterialMenu.prototype.show; 1.1472 +/** 1.1473 + * Hides the menu. 1.1474 + * 1.1475 + * @public 1.1476 + */ 1.1477 +MaterialMenu.prototype.hide = function () { 1.1478 + if (this.element_ && this.container_ && this.outline_) { 1.1479 + var items = this.element_.querySelectorAll('.' + this.CssClasses_.ITEM); 1.1480 + // Remove all transition delays; menu items fade out concurrently. 1.1481 + for (var i = 0; i < items.length; i++) { 1.1482 + items[i].style.removeProperty('transition-delay'); 1.1483 + } 1.1484 + // Measure the inner element. 1.1485 + var rect = this.element_.getBoundingClientRect(); 1.1486 + var height = rect.height; 1.1487 + var width = rect.width; 1.1488 + // Turn on animation, and apply the final clip. Also make invisible. 1.1489 + // This triggers the transitions. 1.1490 + this.element_.classList.add(this.CssClasses_.IS_ANIMATING); 1.1491 + this.applyClip_(height, width); 1.1492 + this.container_.classList.remove(this.CssClasses_.IS_VISIBLE); 1.1493 + // Clean up after the animation is complete. 1.1494 + this.addAnimationEndListener_(); 1.1495 + } 1.1496 +}; 1.1497 +MaterialMenu.prototype['hide'] = MaterialMenu.prototype.hide; 1.1498 +/** 1.1499 + * Displays or hides the menu, depending on current state. 1.1500 + * 1.1501 + * @public 1.1502 + */ 1.1503 +MaterialMenu.prototype.toggle = function (evt) { 1.1504 + if (this.container_.classList.contains(this.CssClasses_.IS_VISIBLE)) { 1.1505 + this.hide(); 1.1506 + } else { 1.1507 + this.show(evt); 1.1508 + } 1.1509 +}; 1.1510 +MaterialMenu.prototype['toggle'] = MaterialMenu.prototype.toggle; 1.1511 +// The component registers itself. It can assume componentHandler is available 1.1512 +// in the global scope. 1.1513 +componentHandler.register({ 1.1514 + constructor: MaterialMenu, 1.1515 + classAsString: 'MaterialMenu', 1.1516 + cssClass: 'mdl-js-menu', 1.1517 + widget: true 1.1518 +}); 1.1519 +/** 1.1520 + * @license 1.1521 + * Copyright 2015 Google Inc. All Rights Reserved. 1.1522 + * 1.1523 + * Licensed under the Apache License, Version 2.0 (the "License"); 1.1524 + * you may not use this file except in compliance with the License. 1.1525 + * You may obtain a copy of the License at 1.1526 + * 1.1527 + * http://www.apache.org/licenses/LICENSE-2.0 1.1528 + * 1.1529 + * Unless required by applicable law or agreed to in writing, software 1.1530 + * distributed under the License is distributed on an "AS IS" BASIS, 1.1531 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 1.1532 + * See the License for the specific language governing permissions and 1.1533 + * limitations under the License. 1.1534 + */ 1.1535 +/** 1.1536 + * Class constructor for Progress MDL component. 1.1537 + * Implements MDL component design pattern defined at: 1.1538 + * https://github.com/jasonmayes/mdl-component-design-pattern 1.1539 + * 1.1540 + * @constructor 1.1541 + * @param {HTMLElement} element The element that will be upgraded. 1.1542 + */ 1.1543 +var MaterialProgress = function MaterialProgress(element) { 1.1544 + this.element_ = element; 1.1545 + // Initialize instance. 1.1546 + this.init(); 1.1547 +}; 1.1548 +window['MaterialProgress'] = MaterialProgress; 1.1549 +/** 1.1550 + * Store constants in one place so they can be updated easily. 1.1551 + * 1.1552 + * @enum {string | number} 1.1553 + * @private 1.1554 + */ 1.1555 +MaterialProgress.prototype.Constant_ = {}; 1.1556 +/** 1.1557 + * Store strings for class names defined by this component that are used in 1.1558 + * JavaScript. This allows us to simply change it in one place should we 1.1559 + * decide to modify at a later date. 1.1560 + * 1.1561 + * @enum {string} 1.1562 + * @private 1.1563 + */ 1.1564 +MaterialProgress.prototype.CssClasses_ = { INDETERMINATE_CLASS: 'mdl-progress__indeterminate' }; 1.1565 +/** 1.1566 + * Set the current progress of the progressbar. 1.1567 + * 1.1568 + * @param {number} p Percentage of the progress (0-100) 1.1569 + * @public 1.1570 + */ 1.1571 +MaterialProgress.prototype.setProgress = function (p) { 1.1572 + if (this.element_.classList.contains(this.CssClasses_.INDETERMINATE_CLASS)) { 1.1573 + return; 1.1574 + } 1.1575 + this.progressbar_.style.width = p + '%'; 1.1576 +}; 1.1577 +MaterialProgress.prototype['setProgress'] = MaterialProgress.prototype.setProgress; 1.1578 +/** 1.1579 + * Set the current progress of the buffer. 1.1580 + * 1.1581 + * @param {number} p Percentage of the buffer (0-100) 1.1582 + * @public 1.1583 + */ 1.1584 +MaterialProgress.prototype.setBuffer = function (p) { 1.1585 + this.bufferbar_.style.width = p + '%'; 1.1586 + this.auxbar_.style.width = 100 - p + '%'; 1.1587 +}; 1.1588 +MaterialProgress.prototype['setBuffer'] = MaterialProgress.prototype.setBuffer; 1.1589 +/** 1.1590 + * Initialize element. 1.1591 + */ 1.1592 +MaterialProgress.prototype.init = function () { 1.1593 + if (this.element_) { 1.1594 + var el = document.createElement('div'); 1.1595 + el.className = 'progressbar bar bar1'; 1.1596 + this.element_.appendChild(el); 1.1597 + this.progressbar_ = el; 1.1598 + el = document.createElement('div'); 1.1599 + el.className = 'bufferbar bar bar2'; 1.1600 + this.element_.appendChild(el); 1.1601 + this.bufferbar_ = el; 1.1602 + el = document.createElement('div'); 1.1603 + el.className = 'auxbar bar bar3'; 1.1604 + this.element_.appendChild(el); 1.1605 + this.auxbar_ = el; 1.1606 + this.progressbar_.style.width = '0%'; 1.1607 + this.bufferbar_.style.width = '100%'; 1.1608 + this.auxbar_.style.width = '0%'; 1.1609 + this.element_.classList.add('is-upgraded'); 1.1610 + } 1.1611 +}; 1.1612 +// The component registers itself. It can assume componentHandler is available 1.1613 +// in the global scope. 1.1614 +componentHandler.register({ 1.1615 + constructor: MaterialProgress, 1.1616 + classAsString: 'MaterialProgress', 1.1617 + cssClass: 'mdl-js-progress', 1.1618 + widget: true 1.1619 +}); 1.1620 +/** 1.1621 + * @license 1.1622 + * Copyright 2015 Google Inc. All Rights Reserved. 1.1623 + * 1.1624 + * Licensed under the Apache License, Version 2.0 (the "License"); 1.1625 + * you may not use this file except in compliance with the License. 1.1626 + * You may obtain a copy of the License at 1.1627 + * 1.1628 + * http://www.apache.org/licenses/LICENSE-2.0 1.1629 + * 1.1630 + * Unless required by applicable law or agreed to in writing, software 1.1631 + * distributed under the License is distributed on an "AS IS" BASIS, 1.1632 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 1.1633 + * See the License for the specific language governing permissions and 1.1634 + * limitations under the License. 1.1635 + */ 1.1636 +/** 1.1637 + * Class constructor for Radio MDL component. 1.1638 + * Implements MDL component design pattern defined at: 1.1639 + * https://github.com/jasonmayes/mdl-component-design-pattern 1.1640 + * 1.1641 + * @constructor 1.1642 + * @param {HTMLElement} element The element that will be upgraded. 1.1643 + */ 1.1644 +var MaterialRadio = function MaterialRadio(element) { 1.1645 + this.element_ = element; 1.1646 + // Initialize instance. 1.1647 + this.init(); 1.1648 +}; 1.1649 +window['MaterialRadio'] = MaterialRadio; 1.1650 +/** 1.1651 + * Store constants in one place so they can be updated easily. 1.1652 + * 1.1653 + * @enum {string | number} 1.1654 + * @private 1.1655 + */ 1.1656 +MaterialRadio.prototype.Constant_ = { TINY_TIMEOUT: 0.001 }; 1.1657 +/** 1.1658 + * Store strings for class names defined by this component that are used in 1.1659 + * JavaScript. This allows us to simply change it in one place should we 1.1660 + * decide to modify at a later date. 1.1661 + * 1.1662 + * @enum {string} 1.1663 + * @private 1.1664 + */ 1.1665 +MaterialRadio.prototype.CssClasses_ = { 1.1666 + IS_FOCUSED: 'is-focused', 1.1667 + IS_DISABLED: 'is-disabled', 1.1668 + IS_CHECKED: 'is-checked', 1.1669 + IS_UPGRADED: 'is-upgraded', 1.1670 + JS_RADIO: 'mdl-js-radio', 1.1671 + RADIO_BTN: 'mdl-radio__button', 1.1672 + RADIO_OUTER_CIRCLE: 'mdl-radio__outer-circle', 1.1673 + RADIO_INNER_CIRCLE: 'mdl-radio__inner-circle', 1.1674 + RIPPLE_EFFECT: 'mdl-js-ripple-effect', 1.1675 + RIPPLE_IGNORE_EVENTS: 'mdl-js-ripple-effect--ignore-events', 1.1676 + RIPPLE_CONTAINER: 'mdl-radio__ripple-container', 1.1677 + RIPPLE_CENTER: 'mdl-ripple--center', 1.1678 + RIPPLE: 'mdl-ripple' 1.1679 +}; 1.1680 +/** 1.1681 + * Handle change of state. 1.1682 + * 1.1683 + * @param {Event} event The event that fired. 1.1684 + * @private 1.1685 + */ 1.1686 +MaterialRadio.prototype.onChange_ = function (event) { 1.1687 + // Since other radio buttons don't get change events, we need to look for 1.1688 + // them to update their classes. 1.1689 + var radios = document.getElementsByClassName(this.CssClasses_.JS_RADIO); 1.1690 + for (var i = 0; i < radios.length; i++) { 1.1691 + var button = radios[i].querySelector('.' + this.CssClasses_.RADIO_BTN); 1.1692 + // Different name == different group, so no point updating those. 1.1693 + if (button.getAttribute('name') === this.btnElement_.getAttribute('name')) { 1.1694 + radios[i]['MaterialRadio'].updateClasses_(); 1.1695 + } 1.1696 + } 1.1697 +}; 1.1698 +/** 1.1699 + * Handle focus. 1.1700 + * 1.1701 + * @param {Event} event The event that fired. 1.1702 + * @private 1.1703 + */ 1.1704 +MaterialRadio.prototype.onFocus_ = function (event) { 1.1705 + this.element_.classList.add(this.CssClasses_.IS_FOCUSED); 1.1706 +}; 1.1707 +/** 1.1708 + * Handle lost focus. 1.1709 + * 1.1710 + * @param {Event} event The event that fired. 1.1711 + * @private 1.1712 + */ 1.1713 +MaterialRadio.prototype.onBlur_ = function (event) { 1.1714 + this.element_.classList.remove(this.CssClasses_.IS_FOCUSED); 1.1715 +}; 1.1716 +/** 1.1717 + * Handle mouseup. 1.1718 + * 1.1719 + * @param {Event} event The event that fired. 1.1720 + * @private 1.1721 + */ 1.1722 +MaterialRadio.prototype.onMouseup_ = function (event) { 1.1723 + this.blur_(); 1.1724 +}; 1.1725 +/** 1.1726 + * Update classes. 1.1727 + * 1.1728 + * @private 1.1729 + */ 1.1730 +MaterialRadio.prototype.updateClasses_ = function () { 1.1731 + this.checkDisabled(); 1.1732 + this.checkToggleState(); 1.1733 +}; 1.1734 +/** 1.1735 + * Add blur. 1.1736 + * 1.1737 + * @private 1.1738 + */ 1.1739 +MaterialRadio.prototype.blur_ = function () { 1.1740 + // TODO: figure out why there's a focus event being fired after our blur, 1.1741 + // so that we can avoid this hack. 1.1742 + window.setTimeout(function () { 1.1743 + this.btnElement_.blur(); 1.1744 + }.bind(this), this.Constant_.TINY_TIMEOUT); 1.1745 +}; 1.1746 +// Public methods. 1.1747 +/** 1.1748 + * Check the components disabled state. 1.1749 + * 1.1750 + * @public 1.1751 + */ 1.1752 +MaterialRadio.prototype.checkDisabled = function () { 1.1753 + if (this.btnElement_.disabled) { 1.1754 + this.element_.classList.add(this.CssClasses_.IS_DISABLED); 1.1755 + } else { 1.1756 + this.element_.classList.remove(this.CssClasses_.IS_DISABLED); 1.1757 + } 1.1758 +}; 1.1759 +MaterialRadio.prototype['checkDisabled'] = MaterialRadio.prototype.checkDisabled; 1.1760 +/** 1.1761 + * Check the components toggled state. 1.1762 + * 1.1763 + * @public 1.1764 + */ 1.1765 +MaterialRadio.prototype.checkToggleState = function () { 1.1766 + if (this.btnElement_.checked) { 1.1767 + this.element_.classList.add(this.CssClasses_.IS_CHECKED); 1.1768 + } else { 1.1769 + this.element_.classList.remove(this.CssClasses_.IS_CHECKED); 1.1770 + } 1.1771 +}; 1.1772 +MaterialRadio.prototype['checkToggleState'] = MaterialRadio.prototype.checkToggleState; 1.1773 +/** 1.1774 + * Disable radio. 1.1775 + * 1.1776 + * @public 1.1777 + */ 1.1778 +MaterialRadio.prototype.disable = function () { 1.1779 + this.btnElement_.disabled = true; 1.1780 + this.updateClasses_(); 1.1781 +}; 1.1782 +MaterialRadio.prototype['disable'] = MaterialRadio.prototype.disable; 1.1783 +/** 1.1784 + * Enable radio. 1.1785 + * 1.1786 + * @public 1.1787 + */ 1.1788 +MaterialRadio.prototype.enable = function () { 1.1789 + this.btnElement_.disabled = false; 1.1790 + this.updateClasses_(); 1.1791 +}; 1.1792 +MaterialRadio.prototype['enable'] = MaterialRadio.prototype.enable; 1.1793 +/** 1.1794 + * Check radio. 1.1795 + * 1.1796 + * @public 1.1797 + */ 1.1798 +MaterialRadio.prototype.check = function () { 1.1799 + this.btnElement_.checked = true; 1.1800 + this.onChange_(null); 1.1801 +}; 1.1802 +MaterialRadio.prototype['check'] = MaterialRadio.prototype.check; 1.1803 +/** 1.1804 + * Uncheck radio. 1.1805 + * 1.1806 + * @public 1.1807 + */ 1.1808 +MaterialRadio.prototype.uncheck = function () { 1.1809 + this.btnElement_.checked = false; 1.1810 + this.onChange_(null); 1.1811 +}; 1.1812 +MaterialRadio.prototype['uncheck'] = MaterialRadio.prototype.uncheck; 1.1813 +/** 1.1814 + * Initialize element. 1.1815 + */ 1.1816 +MaterialRadio.prototype.init = function () { 1.1817 + if (this.element_) { 1.1818 + this.btnElement_ = this.element_.querySelector('.' + this.CssClasses_.RADIO_BTN); 1.1819 + this.boundChangeHandler_ = this.onChange_.bind(this); 1.1820 + this.boundFocusHandler_ = this.onChange_.bind(this); 1.1821 + this.boundBlurHandler_ = this.onBlur_.bind(this); 1.1822 + this.boundMouseUpHandler_ = this.onMouseup_.bind(this); 1.1823 + var outerCircle = document.createElement('span'); 1.1824 + outerCircle.classList.add(this.CssClasses_.RADIO_OUTER_CIRCLE); 1.1825 + var innerCircle = document.createElement('span'); 1.1826 + innerCircle.classList.add(this.CssClasses_.RADIO_INNER_CIRCLE); 1.1827 + this.element_.appendChild(outerCircle); 1.1828 + this.element_.appendChild(innerCircle); 1.1829 + var rippleContainer; 1.1830 + if (this.element_.classList.contains(this.CssClasses_.RIPPLE_EFFECT)) { 1.1831 + this.element_.classList.add(this.CssClasses_.RIPPLE_IGNORE_EVENTS); 1.1832 + rippleContainer = document.createElement('span'); 1.1833 + rippleContainer.classList.add(this.CssClasses_.RIPPLE_CONTAINER); 1.1834 + rippleContainer.classList.add(this.CssClasses_.RIPPLE_EFFECT); 1.1835 + rippleContainer.classList.add(this.CssClasses_.RIPPLE_CENTER); 1.1836 + rippleContainer.addEventListener('mouseup', this.boundMouseUpHandler_); 1.1837 + var ripple = document.createElement('span'); 1.1838 + ripple.classList.add(this.CssClasses_.RIPPLE); 1.1839 + rippleContainer.appendChild(ripple); 1.1840 + this.element_.appendChild(rippleContainer); 1.1841 + } 1.1842 + this.btnElement_.addEventListener('change', this.boundChangeHandler_); 1.1843 + this.btnElement_.addEventListener('focus', this.boundFocusHandler_); 1.1844 + this.btnElement_.addEventListener('blur', this.boundBlurHandler_); 1.1845 + this.element_.addEventListener('mouseup', this.boundMouseUpHandler_); 1.1846 + this.updateClasses_(); 1.1847 + this.element_.classList.add(this.CssClasses_.IS_UPGRADED); 1.1848 + } 1.1849 +}; 1.1850 +// The component registers itself. It can assume componentHandler is available 1.1851 +// in the global scope. 1.1852 +componentHandler.register({ 1.1853 + constructor: MaterialRadio, 1.1854 + classAsString: 'MaterialRadio', 1.1855 + cssClass: 'mdl-js-radio', 1.1856 + widget: true 1.1857 +}); 1.1858 +/** 1.1859 + * @license 1.1860 + * Copyright 2015 Google Inc. All Rights Reserved. 1.1861 + * 1.1862 + * Licensed under the Apache License, Version 2.0 (the "License"); 1.1863 + * you may not use this file except in compliance with the License. 1.1864 + * You may obtain a copy of the License at 1.1865 + * 1.1866 + * http://www.apache.org/licenses/LICENSE-2.0 1.1867 + * 1.1868 + * Unless required by applicable law or agreed to in writing, software 1.1869 + * distributed under the License is distributed on an "AS IS" BASIS, 1.1870 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 1.1871 + * See the License for the specific language governing permissions and 1.1872 + * limitations under the License. 1.1873 + */ 1.1874 +/** 1.1875 + * Class constructor for Slider MDL component. 1.1876 + * Implements MDL component design pattern defined at: 1.1877 + * https://github.com/jasonmayes/mdl-component-design-pattern 1.1878 + * 1.1879 + * @constructor 1.1880 + * @param {HTMLElement} element The element that will be upgraded. 1.1881 + */ 1.1882 +var MaterialSlider = function MaterialSlider(element) { 1.1883 + this.element_ = element; 1.1884 + // Browser feature detection. 1.1885 + this.isIE_ = window.navigator.msPointerEnabled; 1.1886 + // Initialize instance. 1.1887 + this.init(); 1.1888 +}; 1.1889 +window['MaterialSlider'] = MaterialSlider; 1.1890 +/** 1.1891 + * Store constants in one place so they can be updated easily. 1.1892 + * 1.1893 + * @enum {string | number} 1.1894 + * @private 1.1895 + */ 1.1896 +MaterialSlider.prototype.Constant_ = {}; 1.1897 +/** 1.1898 + * Store strings for class names defined by this component that are used in 1.1899 + * JavaScript. This allows us to simply change it in one place should we 1.1900 + * decide to modify at a later date. 1.1901 + * 1.1902 + * @enum {string} 1.1903 + * @private 1.1904 + */ 1.1905 +MaterialSlider.prototype.CssClasses_ = { 1.1906 + IE_CONTAINER: 'mdl-slider__ie-container', 1.1907 + SLIDER_CONTAINER: 'mdl-slider__container', 1.1908 + BACKGROUND_FLEX: 'mdl-slider__background-flex', 1.1909 + BACKGROUND_LOWER: 'mdl-slider__background-lower', 1.1910 + BACKGROUND_UPPER: 'mdl-slider__background-upper', 1.1911 + IS_LOWEST_VALUE: 'is-lowest-value', 1.1912 + IS_UPGRADED: 'is-upgraded' 1.1913 +}; 1.1914 +/** 1.1915 + * Handle input on element. 1.1916 + * 1.1917 + * @param {Event} event The event that fired. 1.1918 + * @private 1.1919 + */ 1.1920 +MaterialSlider.prototype.onInput_ = function (event) { 1.1921 + this.updateValueStyles_(); 1.1922 +}; 1.1923 +/** 1.1924 + * Handle change on element. 1.1925 + * 1.1926 + * @param {Event} event The event that fired. 1.1927 + * @private 1.1928 + */ 1.1929 +MaterialSlider.prototype.onChange_ = function (event) { 1.1930 + this.updateValueStyles_(); 1.1931 +}; 1.1932 +/** 1.1933 + * Handle mouseup on element. 1.1934 + * 1.1935 + * @param {Event} event The event that fired. 1.1936 + * @private 1.1937 + */ 1.1938 +MaterialSlider.prototype.onMouseUp_ = function (event) { 1.1939 + event.target.blur(); 1.1940 +}; 1.1941 +/** 1.1942 + * Handle mousedown on container element. 1.1943 + * This handler is purpose is to not require the use to click 1.1944 + * exactly on the 2px slider element, as FireFox seems to be very 1.1945 + * strict about this. 1.1946 + * 1.1947 + * @param {Event} event The event that fired. 1.1948 + * @private 1.1949 + * @suppress {missingProperties} 1.1950 + */ 1.1951 +MaterialSlider.prototype.onContainerMouseDown_ = function (event) { 1.1952 + // If this click is not on the parent element (but rather some child) 1.1953 + // ignore. It may still bubble up. 1.1954 + if (event.target !== this.element_.parentElement) { 1.1955 + return; 1.1956 + } 1.1957 + // Discard the original event and create a new event that 1.1958 + // is on the slider element. 1.1959 + event.preventDefault(); 1.1960 + var newEvent = new MouseEvent('mousedown', { 1.1961 + target: event.target, 1.1962 + buttons: event.buttons, 1.1963 + clientX: event.clientX, 1.1964 + clientY: this.element_.getBoundingClientRect().y 1.1965 + }); 1.1966 + this.element_.dispatchEvent(newEvent); 1.1967 +}; 1.1968 +/** 1.1969 + * Handle updating of values. 1.1970 + * 1.1971 + * @private 1.1972 + */ 1.1973 +MaterialSlider.prototype.updateValueStyles_ = function () { 1.1974 + // Calculate and apply percentages to div structure behind slider. 1.1975 + var fraction = (this.element_.value - this.element_.min) / (this.element_.max - this.element_.min); 1.1976 + if (fraction === 0) { 1.1977 + this.element_.classList.add(this.CssClasses_.IS_LOWEST_VALUE); 1.1978 + } else { 1.1979 + this.element_.classList.remove(this.CssClasses_.IS_LOWEST_VALUE); 1.1980 + } 1.1981 + if (!this.isIE_) { 1.1982 + this.backgroundLower_.style.flex = fraction; 1.1983 + this.backgroundLower_.style.webkitFlex = fraction; 1.1984 + this.backgroundUpper_.style.flex = 1 - fraction; 1.1985 + this.backgroundUpper_.style.webkitFlex = 1 - fraction; 1.1986 + } 1.1987 +}; 1.1988 +// Public methods. 1.1989 +/** 1.1990 + * Disable slider. 1.1991 + * 1.1992 + * @public 1.1993 + */ 1.1994 +MaterialSlider.prototype.disable = function () { 1.1995 + this.element_.disabled = true; 1.1996 +}; 1.1997 +MaterialSlider.prototype['disable'] = MaterialSlider.prototype.disable; 1.1998 +/** 1.1999 + * Enable slider. 1.2000 + * 1.2001 + * @public 1.2002 + */ 1.2003 +MaterialSlider.prototype.enable = function () { 1.2004 + this.element_.disabled = false; 1.2005 +}; 1.2006 +MaterialSlider.prototype['enable'] = MaterialSlider.prototype.enable; 1.2007 +/** 1.2008 + * Update slider value. 1.2009 + * 1.2010 + * @param {number} value The value to which to set the control (optional). 1.2011 + * @public 1.2012 + */ 1.2013 +MaterialSlider.prototype.change = function (value) { 1.2014 + if (typeof value !== 'undefined') { 1.2015 + this.element_.value = value; 1.2016 + } 1.2017 + this.updateValueStyles_(); 1.2018 +}; 1.2019 +MaterialSlider.prototype['change'] = MaterialSlider.prototype.change; 1.2020 +/** 1.2021 + * Initialize element. 1.2022 + */ 1.2023 +MaterialSlider.prototype.init = function () { 1.2024 + if (this.element_) { 1.2025 + if (this.isIE_) { 1.2026 + // Since we need to specify a very large height in IE due to 1.2027 + // implementation limitations, we add a parent here that trims it down to 1.2028 + // a reasonable size. 1.2029 + var containerIE = document.createElement('div'); 1.2030 + containerIE.classList.add(this.CssClasses_.IE_CONTAINER); 1.2031 + this.element_.parentElement.insertBefore(containerIE, this.element_); 1.2032 + this.element_.parentElement.removeChild(this.element_); 1.2033 + containerIE.appendChild(this.element_); 1.2034 + } else { 1.2035 + // For non-IE browsers, we need a div structure that sits behind the 1.2036 + // slider and allows us to style the left and right sides of it with 1.2037 + // different colors. 1.2038 + var container = document.createElement('div'); 1.2039 + container.classList.add(this.CssClasses_.SLIDER_CONTAINER); 1.2040 + this.element_.parentElement.insertBefore(container, this.element_); 1.2041 + this.element_.parentElement.removeChild(this.element_); 1.2042 + container.appendChild(this.element_); 1.2043 + var backgroundFlex = document.createElement('div'); 1.2044 + backgroundFlex.classList.add(this.CssClasses_.BACKGROUND_FLEX); 1.2045 + container.appendChild(backgroundFlex); 1.2046 + this.backgroundLower_ = document.createElement('div'); 1.2047 + this.backgroundLower_.classList.add(this.CssClasses_.BACKGROUND_LOWER); 1.2048 + backgroundFlex.appendChild(this.backgroundLower_); 1.2049 + this.backgroundUpper_ = document.createElement('div'); 1.2050 + this.backgroundUpper_.classList.add(this.CssClasses_.BACKGROUND_UPPER); 1.2051 + backgroundFlex.appendChild(this.backgroundUpper_); 1.2052 + } 1.2053 + this.boundInputHandler = this.onInput_.bind(this); 1.2054 + this.boundChangeHandler = this.onChange_.bind(this); 1.2055 + this.boundMouseUpHandler = this.onMouseUp_.bind(this); 1.2056 + this.boundContainerMouseDownHandler = this.onContainerMouseDown_.bind(this); 1.2057 + this.element_.addEventListener('input', this.boundInputHandler); 1.2058 + this.element_.addEventListener('change', this.boundChangeHandler); 1.2059 + this.element_.addEventListener('mouseup', this.boundMouseUpHandler); 1.2060 + this.element_.parentElement.addEventListener('mousedown', this.boundContainerMouseDownHandler); 1.2061 + this.updateValueStyles_(); 1.2062 + this.element_.classList.add(this.CssClasses_.IS_UPGRADED); 1.2063 + } 1.2064 +}; 1.2065 +// The component registers itself. It can assume componentHandler is available 1.2066 +// in the global scope. 1.2067 +componentHandler.register({ 1.2068 + constructor: MaterialSlider, 1.2069 + classAsString: 'MaterialSlider', 1.2070 + cssClass: 'mdl-js-slider', 1.2071 + widget: true 1.2072 +}); 1.2073 +/** 1.2074 + * Copyright 2015 Google Inc. All Rights Reserved. 1.2075 + * 1.2076 + * Licensed under the Apache License, Version 2.0 (the "License"); 1.2077 + * you may not use this file except in compliance with the License. 1.2078 + * You may obtain a copy of the License at 1.2079 + * 1.2080 + * http://www.apache.org/licenses/LICENSE-2.0 1.2081 + * 1.2082 + * Unless required by applicable law or agreed to in writing, software 1.2083 + * distributed under the License is distributed on an "AS IS" BASIS, 1.2084 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 1.2085 + * See the License for the specific language governing permissions and 1.2086 + * limitations under the License. 1.2087 + */ 1.2088 +/** 1.2089 + * Class constructor for Snackbar MDL component. 1.2090 + * Implements MDL component design pattern defined at: 1.2091 + * https://github.com/jasonmayes/mdl-component-design-pattern 1.2092 + * 1.2093 + * @constructor 1.2094 + * @param {HTMLElement} element The element that will be upgraded. 1.2095 + */ 1.2096 +var MaterialSnackbar = function MaterialSnackbar(element) { 1.2097 + this.element_ = element; 1.2098 + this.textElement_ = this.element_.querySelector('.' + this.cssClasses_.MESSAGE); 1.2099 + this.actionElement_ = this.element_.querySelector('.' + this.cssClasses_.ACTION); 1.2100 + if (!this.textElement_) { 1.2101 + throw new Error('There must be a message element for a snackbar.'); 1.2102 + } 1.2103 + if (!this.actionElement_) { 1.2104 + throw new Error('There must be an action element for a snackbar.'); 1.2105 + } 1.2106 + this.active = false; 1.2107 + this.actionHandler_ = undefined; 1.2108 + this.message_ = undefined; 1.2109 + this.actionText_ = undefined; 1.2110 + this.queuedNotifications_ = []; 1.2111 + this.setActionHidden_(true); 1.2112 +}; 1.2113 +window['MaterialSnackbar'] = MaterialSnackbar; 1.2114 +/** 1.2115 + * Store constants in one place so they can be updated easily. 1.2116 + * 1.2117 + * @enum {string | number} 1.2118 + * @private 1.2119 + */ 1.2120 +MaterialSnackbar.prototype.Constant_ = { 1.2121 + // The duration of the snackbar show/hide animation, in ms. 1.2122 + ANIMATION_LENGTH: 250 1.2123 +}; 1.2124 +/** 1.2125 + * Store strings for class names defined by this component that are used in 1.2126 + * JavaScript. This allows us to simply change it in one place should we 1.2127 + * decide to modify at a later date. 1.2128 + * 1.2129 + * @enum {string} 1.2130 + * @private 1.2131 + */ 1.2132 +MaterialSnackbar.prototype.cssClasses_ = { 1.2133 + SNACKBAR: 'mdl-snackbar', 1.2134 + MESSAGE: 'mdl-snackbar__text', 1.2135 + ACTION: 'mdl-snackbar__action', 1.2136 + ACTIVE: 'mdl-snackbar--active' 1.2137 +}; 1.2138 +/** 1.2139 + * Display the snackbar. 1.2140 + * 1.2141 + * @private 1.2142 + */ 1.2143 +MaterialSnackbar.prototype.displaySnackbar_ = function () { 1.2144 + this.element_.setAttribute('aria-hidden', 'true'); 1.2145 + if (this.actionHandler_) { 1.2146 + this.actionElement_.textContent = this.actionText_; 1.2147 + this.actionElement_.addEventListener('click', this.actionHandler_); 1.2148 + this.setActionHidden_(false); 1.2149 + } 1.2150 + this.textElement_.textContent = this.message_; 1.2151 + this.element_.classList.add(this.cssClasses_.ACTIVE); 1.2152 + this.element_.setAttribute('aria-hidden', 'false'); 1.2153 + setTimeout(this.cleanup_.bind(this), this.timeout_); 1.2154 +}; 1.2155 +/** 1.2156 + * Show the snackbar. 1.2157 + * 1.2158 + * @param {Object} data The data for the notification. 1.2159 + * @public 1.2160 + */ 1.2161 +MaterialSnackbar.prototype.showSnackbar = function (data) { 1.2162 + if (data === undefined) { 1.2163 + throw new Error('Please provide a data object with at least a message to display.'); 1.2164 + } 1.2165 + if (data['message'] === undefined) { 1.2166 + throw new Error('Please provide a message to be displayed.'); 1.2167 + } 1.2168 + if (data['actionHandler'] && !data['actionText']) { 1.2169 + throw new Error('Please provide action text with the handler.'); 1.2170 + } 1.2171 + if (this.active) { 1.2172 + this.queuedNotifications_.push(data); 1.2173 + } else { 1.2174 + this.active = true; 1.2175 + this.message_ = data['message']; 1.2176 + if (data['timeout']) { 1.2177 + this.timeout_ = data['timeout']; 1.2178 + } else { 1.2179 + this.timeout_ = 2750; 1.2180 + } 1.2181 + if (data['actionHandler']) { 1.2182 + this.actionHandler_ = data['actionHandler']; 1.2183 + } 1.2184 + if (data['actionText']) { 1.2185 + this.actionText_ = data['actionText']; 1.2186 + } 1.2187 + this.displaySnackbar_(); 1.2188 + } 1.2189 +}; 1.2190 +MaterialSnackbar.prototype['showSnackbar'] = MaterialSnackbar.prototype.showSnackbar; 1.2191 +/** 1.2192 + * Check if the queue has items within it. 1.2193 + * If it does, display the next entry. 1.2194 + * 1.2195 + * @private 1.2196 + */ 1.2197 +MaterialSnackbar.prototype.checkQueue_ = function () { 1.2198 + if (this.queuedNotifications_.length > 0) { 1.2199 + this.showSnackbar(this.queuedNotifications_.shift()); 1.2200 + } 1.2201 +}; 1.2202 +/** 1.2203 + * Cleanup the snackbar event listeners and accessiblity attributes. 1.2204 + * 1.2205 + * @private 1.2206 + */ 1.2207 +MaterialSnackbar.prototype.cleanup_ = function () { 1.2208 + this.element_.classList.remove(this.cssClasses_.ACTIVE); 1.2209 + setTimeout(function () { 1.2210 + this.element_.setAttribute('aria-hidden', 'true'); 1.2211 + this.textElement_.textContent = ''; 1.2212 + if (!Boolean(this.actionElement_.getAttribute('aria-hidden'))) { 1.2213 + this.setActionHidden_(true); 1.2214 + this.actionElement_.textContent = ''; 1.2215 + this.actionElement_.removeEventListener('click', this.actionHandler_); 1.2216 + } 1.2217 + this.actionHandler_ = undefined; 1.2218 + this.message_ = undefined; 1.2219 + this.actionText_ = undefined; 1.2220 + this.active = false; 1.2221 + this.checkQueue_(); 1.2222 + }.bind(this), this.Constant_.ANIMATION_LENGTH); 1.2223 +}; 1.2224 +/** 1.2225 + * Set the action handler hidden state. 1.2226 + * 1.2227 + * @param {boolean} value 1.2228 + * @private 1.2229 + */ 1.2230 +MaterialSnackbar.prototype.setActionHidden_ = function (value) { 1.2231 + if (value) { 1.2232 + this.actionElement_.setAttribute('aria-hidden', 'true'); 1.2233 + } else { 1.2234 + this.actionElement_.removeAttribute('aria-hidden'); 1.2235 + } 1.2236 +}; 1.2237 +// The component registers itself. It can assume componentHandler is available 1.2238 +// in the global scope. 1.2239 +componentHandler.register({ 1.2240 + constructor: MaterialSnackbar, 1.2241 + classAsString: 'MaterialSnackbar', 1.2242 + cssClass: 'mdl-js-snackbar', 1.2243 + widget: true 1.2244 +}); 1.2245 +/** 1.2246 + * @license 1.2247 + * Copyright 2015 Google Inc. All Rights Reserved. 1.2248 + * 1.2249 + * Licensed under the Apache License, Version 2.0 (the "License"); 1.2250 + * you may not use this file except in compliance with the License. 1.2251 + * You may obtain a copy of the License at 1.2252 + * 1.2253 + * http://www.apache.org/licenses/LICENSE-2.0 1.2254 + * 1.2255 + * Unless required by applicable law or agreed to in writing, software 1.2256 + * distributed under the License is distributed on an "AS IS" BASIS, 1.2257 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 1.2258 + * See the License for the specific language governing permissions and 1.2259 + * limitations under the License. 1.2260 + */ 1.2261 +/** 1.2262 + * Class constructor for Spinner MDL component. 1.2263 + * Implements MDL component design pattern defined at: 1.2264 + * https://github.com/jasonmayes/mdl-component-design-pattern 1.2265 + * 1.2266 + * @param {HTMLElement} element The element that will be upgraded. 1.2267 + * @constructor 1.2268 + */ 1.2269 +var MaterialSpinner = function MaterialSpinner(element) { 1.2270 + this.element_ = element; 1.2271 + // Initialize instance. 1.2272 + this.init(); 1.2273 +}; 1.2274 +window['MaterialSpinner'] = MaterialSpinner; 1.2275 +/** 1.2276 + * Store constants in one place so they can be updated easily. 1.2277 + * 1.2278 + * @enum {string | number} 1.2279 + * @private 1.2280 + */ 1.2281 +MaterialSpinner.prototype.Constant_ = { MDL_SPINNER_LAYER_COUNT: 4 }; 1.2282 +/** 1.2283 + * Store strings for class names defined by this component that are used in 1.2284 + * JavaScript. This allows us to simply change it in one place should we 1.2285 + * decide to modify at a later date. 1.2286 + * 1.2287 + * @enum {string} 1.2288 + * @private 1.2289 + */ 1.2290 +MaterialSpinner.prototype.CssClasses_ = { 1.2291 + MDL_SPINNER_LAYER: 'mdl-spinner__layer', 1.2292 + MDL_SPINNER_CIRCLE_CLIPPER: 'mdl-spinner__circle-clipper', 1.2293 + MDL_SPINNER_CIRCLE: 'mdl-spinner__circle', 1.2294 + MDL_SPINNER_GAP_PATCH: 'mdl-spinner__gap-patch', 1.2295 + MDL_SPINNER_LEFT: 'mdl-spinner__left', 1.2296 + MDL_SPINNER_RIGHT: 'mdl-spinner__right' 1.2297 +}; 1.2298 +/** 1.2299 + * Auxiliary method to create a spinner layer. 1.2300 + * 1.2301 + * @param {number} index Index of the layer to be created. 1.2302 + * @public 1.2303 + */ 1.2304 +MaterialSpinner.prototype.createLayer = function (index) { 1.2305 + var layer = document.createElement('div'); 1.2306 + layer.classList.add(this.CssClasses_.MDL_SPINNER_LAYER); 1.2307 + layer.classList.add(this.CssClasses_.MDL_SPINNER_LAYER + '-' + index); 1.2308 + var leftClipper = document.createElement('div'); 1.2309 + leftClipper.classList.add(this.CssClasses_.MDL_SPINNER_CIRCLE_CLIPPER); 1.2310 + leftClipper.classList.add(this.CssClasses_.MDL_SPINNER_LEFT); 1.2311 + var gapPatch = document.createElement('div'); 1.2312 + gapPatch.classList.add(this.CssClasses_.MDL_SPINNER_GAP_PATCH); 1.2313 + var rightClipper = document.createElement('div'); 1.2314 + rightClipper.classList.add(this.CssClasses_.MDL_SPINNER_CIRCLE_CLIPPER); 1.2315 + rightClipper.classList.add(this.CssClasses_.MDL_SPINNER_RIGHT); 1.2316 + var circleOwners = [ 1.2317 + leftClipper, 1.2318 + gapPatch, 1.2319 + rightClipper 1.2320 + ]; 1.2321 + for (var i = 0; i < circleOwners.length; i++) { 1.2322 + var circle = document.createElement('div'); 1.2323 + circle.classList.add(this.CssClasses_.MDL_SPINNER_CIRCLE); 1.2324 + circleOwners[i].appendChild(circle); 1.2325 + } 1.2326 + layer.appendChild(leftClipper); 1.2327 + layer.appendChild(gapPatch); 1.2328 + layer.appendChild(rightClipper); 1.2329 + this.element_.appendChild(layer); 1.2330 +}; 1.2331 +MaterialSpinner.prototype['createLayer'] = MaterialSpinner.prototype.createLayer; 1.2332 +/** 1.2333 + * Stops the spinner animation. 1.2334 + * Public method for users who need to stop the spinner for any reason. 1.2335 + * 1.2336 + * @public 1.2337 + */ 1.2338 +MaterialSpinner.prototype.stop = function () { 1.2339 + this.element_.classList.remove('is-active'); 1.2340 +}; 1.2341 +MaterialSpinner.prototype['stop'] = MaterialSpinner.prototype.stop; 1.2342 +/** 1.2343 + * Starts the spinner animation. 1.2344 + * Public method for users who need to manually start the spinner for any reason 1.2345 + * (instead of just adding the 'is-active' class to their markup). 1.2346 + * 1.2347 + * @public 1.2348 + */ 1.2349 +MaterialSpinner.prototype.start = function () { 1.2350 + this.element_.classList.add('is-active'); 1.2351 +}; 1.2352 +MaterialSpinner.prototype['start'] = MaterialSpinner.prototype.start; 1.2353 +/** 1.2354 + * Initialize element. 1.2355 + */ 1.2356 +MaterialSpinner.prototype.init = function () { 1.2357 + if (this.element_) { 1.2358 + for (var i = 1; i <= this.Constant_.MDL_SPINNER_LAYER_COUNT; i++) { 1.2359 + this.createLayer(i); 1.2360 + } 1.2361 + this.element_.classList.add('is-upgraded'); 1.2362 + } 1.2363 +}; 1.2364 +// The component registers itself. It can assume componentHandler is available 1.2365 +// in the global scope. 1.2366 +componentHandler.register({ 1.2367 + constructor: MaterialSpinner, 1.2368 + classAsString: 'MaterialSpinner', 1.2369 + cssClass: 'mdl-js-spinner', 1.2370 + widget: true 1.2371 +}); 1.2372 +/** 1.2373 + * @license 1.2374 + * Copyright 2015 Google Inc. All Rights Reserved. 1.2375 + * 1.2376 + * Licensed under the Apache License, Version 2.0 (the "License"); 1.2377 + * you may not use this file except in compliance with the License. 1.2378 + * You may obtain a copy of the License at 1.2379 + * 1.2380 + * http://www.apache.org/licenses/LICENSE-2.0 1.2381 + * 1.2382 + * Unless required by applicable law or agreed to in writing, software 1.2383 + * distributed under the License is distributed on an "AS IS" BASIS, 1.2384 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 1.2385 + * See the License for the specific language governing permissions and 1.2386 + * limitations under the License. 1.2387 + */ 1.2388 +/** 1.2389 + * Class constructor for Checkbox MDL component. 1.2390 + * Implements MDL component design pattern defined at: 1.2391 + * https://github.com/jasonmayes/mdl-component-design-pattern 1.2392 + * 1.2393 + * @constructor 1.2394 + * @param {HTMLElement} element The element that will be upgraded. 1.2395 + */ 1.2396 +var MaterialSwitch = function MaterialSwitch(element) { 1.2397 + this.element_ = element; 1.2398 + // Initialize instance. 1.2399 + this.init(); 1.2400 +}; 1.2401 +window['MaterialSwitch'] = MaterialSwitch; 1.2402 +/** 1.2403 + * Store constants in one place so they can be updated easily. 1.2404 + * 1.2405 + * @enum {string | number} 1.2406 + * @private 1.2407 + */ 1.2408 +MaterialSwitch.prototype.Constant_ = { TINY_TIMEOUT: 0.001 }; 1.2409 +/** 1.2410 + * Store strings for class names defined by this component that are used in 1.2411 + * JavaScript. This allows us to simply change it in one place should we 1.2412 + * decide to modify at a later date. 1.2413 + * 1.2414 + * @enum {string} 1.2415 + * @private 1.2416 + */ 1.2417 +MaterialSwitch.prototype.CssClasses_ = { 1.2418 + INPUT: 'mdl-switch__input', 1.2419 + TRACK: 'mdl-switch__track', 1.2420 + THUMB: 'mdl-switch__thumb', 1.2421 + FOCUS_HELPER: 'mdl-switch__focus-helper', 1.2422 + RIPPLE_EFFECT: 'mdl-js-ripple-effect', 1.2423 + RIPPLE_IGNORE_EVENTS: 'mdl-js-ripple-effect--ignore-events', 1.2424 + RIPPLE_CONTAINER: 'mdl-switch__ripple-container', 1.2425 + RIPPLE_CENTER: 'mdl-ripple--center', 1.2426 + RIPPLE: 'mdl-ripple', 1.2427 + IS_FOCUSED: 'is-focused', 1.2428 + IS_DISABLED: 'is-disabled', 1.2429 + IS_CHECKED: 'is-checked' 1.2430 +}; 1.2431 +/** 1.2432 + * Handle change of state. 1.2433 + * 1.2434 + * @param {Event} event The event that fired. 1.2435 + * @private 1.2436 + */ 1.2437 +MaterialSwitch.prototype.onChange_ = function (event) { 1.2438 + this.updateClasses_(); 1.2439 +}; 1.2440 +/** 1.2441 + * Handle focus of element. 1.2442 + * 1.2443 + * @param {Event} event The event that fired. 1.2444 + * @private 1.2445 + */ 1.2446 +MaterialSwitch.prototype.onFocus_ = function (event) { 1.2447 + this.element_.classList.add(this.CssClasses_.IS_FOCUSED); 1.2448 +}; 1.2449 +/** 1.2450 + * Handle lost focus of element. 1.2451 + * 1.2452 + * @param {Event} event The event that fired. 1.2453 + * @private 1.2454 + */ 1.2455 +MaterialSwitch.prototype.onBlur_ = function (event) { 1.2456 + this.element_.classList.remove(this.CssClasses_.IS_FOCUSED); 1.2457 +}; 1.2458 +/** 1.2459 + * Handle mouseup. 1.2460 + * 1.2461 + * @param {Event} event The event that fired. 1.2462 + * @private 1.2463 + */ 1.2464 +MaterialSwitch.prototype.onMouseUp_ = function (event) { 1.2465 + this.blur_(); 1.2466 +}; 1.2467 +/** 1.2468 + * Handle class updates. 1.2469 + * 1.2470 + * @private 1.2471 + */ 1.2472 +MaterialSwitch.prototype.updateClasses_ = function () { 1.2473 + this.checkDisabled(); 1.2474 + this.checkToggleState(); 1.2475 +}; 1.2476 +/** 1.2477 + * Add blur. 1.2478 + * 1.2479 + * @private 1.2480 + */ 1.2481 +MaterialSwitch.prototype.blur_ = function () { 1.2482 + // TODO: figure out why there's a focus event being fired after our blur, 1.2483 + // so that we can avoid this hack. 1.2484 + window.setTimeout(function () { 1.2485 + this.inputElement_.blur(); 1.2486 + }.bind(this), this.Constant_.TINY_TIMEOUT); 1.2487 +}; 1.2488 +// Public methods. 1.2489 +/** 1.2490 + * Check the components disabled state. 1.2491 + * 1.2492 + * @public 1.2493 + */ 1.2494 +MaterialSwitch.prototype.checkDisabled = function () { 1.2495 + if (this.inputElement_.disabled) { 1.2496 + this.element_.classList.add(this.CssClasses_.IS_DISABLED); 1.2497 + } else { 1.2498 + this.element_.classList.remove(this.CssClasses_.IS_DISABLED); 1.2499 + } 1.2500 +}; 1.2501 +MaterialSwitch.prototype['checkDisabled'] = MaterialSwitch.prototype.checkDisabled; 1.2502 +/** 1.2503 + * Check the components toggled state. 1.2504 + * 1.2505 + * @public 1.2506 + */ 1.2507 +MaterialSwitch.prototype.checkToggleState = function () { 1.2508 + if (this.inputElement_.checked) { 1.2509 + this.element_.classList.add(this.CssClasses_.IS_CHECKED); 1.2510 + } else { 1.2511 + this.element_.classList.remove(this.CssClasses_.IS_CHECKED); 1.2512 + } 1.2513 +}; 1.2514 +MaterialSwitch.prototype['checkToggleState'] = MaterialSwitch.prototype.checkToggleState; 1.2515 +/** 1.2516 + * Disable switch. 1.2517 + * 1.2518 + * @public 1.2519 + */ 1.2520 +MaterialSwitch.prototype.disable = function () { 1.2521 + this.inputElement_.disabled = true; 1.2522 + this.updateClasses_(); 1.2523 +}; 1.2524 +MaterialSwitch.prototype['disable'] = MaterialSwitch.prototype.disable; 1.2525 +/** 1.2526 + * Enable switch. 1.2527 + * 1.2528 + * @public 1.2529 + */ 1.2530 +MaterialSwitch.prototype.enable = function () { 1.2531 + this.inputElement_.disabled = false; 1.2532 + this.updateClasses_(); 1.2533 +}; 1.2534 +MaterialSwitch.prototype['enable'] = MaterialSwitch.prototype.enable; 1.2535 +/** 1.2536 + * Activate switch. 1.2537 + * 1.2538 + * @public 1.2539 + */ 1.2540 +MaterialSwitch.prototype.on = function () { 1.2541 + this.inputElement_.checked = true; 1.2542 + this.updateClasses_(); 1.2543 +}; 1.2544 +MaterialSwitch.prototype['on'] = MaterialSwitch.prototype.on; 1.2545 +/** 1.2546 + * Deactivate switch. 1.2547 + * 1.2548 + * @public 1.2549 + */ 1.2550 +MaterialSwitch.prototype.off = function () { 1.2551 + this.inputElement_.checked = false; 1.2552 + this.updateClasses_(); 1.2553 +}; 1.2554 +MaterialSwitch.prototype['off'] = MaterialSwitch.prototype.off; 1.2555 +/** 1.2556 + * Initialize element. 1.2557 + */ 1.2558 +MaterialSwitch.prototype.init = function () { 1.2559 + if (this.element_) { 1.2560 + this.inputElement_ = this.element_.querySelector('.' + this.CssClasses_.INPUT); 1.2561 + var track = document.createElement('div'); 1.2562 + track.classList.add(this.CssClasses_.TRACK); 1.2563 + var thumb = document.createElement('div'); 1.2564 + thumb.classList.add(this.CssClasses_.THUMB); 1.2565 + var focusHelper = document.createElement('span'); 1.2566 + focusHelper.classList.add(this.CssClasses_.FOCUS_HELPER); 1.2567 + thumb.appendChild(focusHelper); 1.2568 + this.element_.appendChild(track); 1.2569 + this.element_.appendChild(thumb); 1.2570 + this.boundMouseUpHandler = this.onMouseUp_.bind(this); 1.2571 + if (this.element_.classList.contains(this.CssClasses_.RIPPLE_EFFECT)) { 1.2572 + this.element_.classList.add(this.CssClasses_.RIPPLE_IGNORE_EVENTS); 1.2573 + this.rippleContainerElement_ = document.createElement('span'); 1.2574 + this.rippleContainerElement_.classList.add(this.CssClasses_.RIPPLE_CONTAINER); 1.2575 + this.rippleContainerElement_.classList.add(this.CssClasses_.RIPPLE_EFFECT); 1.2576 + this.rippleContainerElement_.classList.add(this.CssClasses_.RIPPLE_CENTER); 1.2577 + this.rippleContainerElement_.addEventListener('mouseup', this.boundMouseUpHandler); 1.2578 + var ripple = document.createElement('span'); 1.2579 + ripple.classList.add(this.CssClasses_.RIPPLE); 1.2580 + this.rippleContainerElement_.appendChild(ripple); 1.2581 + this.element_.appendChild(this.rippleContainerElement_); 1.2582 + } 1.2583 + this.boundChangeHandler = this.onChange_.bind(this); 1.2584 + this.boundFocusHandler = this.onFocus_.bind(this); 1.2585 + this.boundBlurHandler = this.onBlur_.bind(this); 1.2586 + this.inputElement_.addEventListener('change', this.boundChangeHandler); 1.2587 + this.inputElement_.addEventListener('focus', this.boundFocusHandler); 1.2588 + this.inputElement_.addEventListener('blur', this.boundBlurHandler); 1.2589 + this.element_.addEventListener('mouseup', this.boundMouseUpHandler); 1.2590 + this.updateClasses_(); 1.2591 + this.element_.classList.add('is-upgraded'); 1.2592 + } 1.2593 +}; 1.2594 +// The component registers itself. It can assume componentHandler is available 1.2595 +// in the global scope. 1.2596 +componentHandler.register({ 1.2597 + constructor: MaterialSwitch, 1.2598 + classAsString: 'MaterialSwitch', 1.2599 + cssClass: 'mdl-js-switch', 1.2600 + widget: true 1.2601 +}); 1.2602 +/** 1.2603 + * @license 1.2604 + * Copyright 2015 Google Inc. All Rights Reserved. 1.2605 + * 1.2606 + * Licensed under the Apache License, Version 2.0 (the "License"); 1.2607 + * you may not use this file except in compliance with the License. 1.2608 + * You may obtain a copy of the License at 1.2609 + * 1.2610 + * http://www.apache.org/licenses/LICENSE-2.0 1.2611 + * 1.2612 + * Unless required by applicable law or agreed to in writing, software 1.2613 + * distributed under the License is distributed on an "AS IS" BASIS, 1.2614 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 1.2615 + * See the License for the specific language governing permissions and 1.2616 + * limitations under the License. 1.2617 + */ 1.2618 +/** 1.2619 + * Class constructor for Tabs MDL component. 1.2620 + * Implements MDL component design pattern defined at: 1.2621 + * https://github.com/jasonmayes/mdl-component-design-pattern 1.2622 + * 1.2623 + * @constructor 1.2624 + * @param {Element} element The element that will be upgraded. 1.2625 + */ 1.2626 +var MaterialTabs = function MaterialTabs(element) { 1.2627 + // Stores the HTML element. 1.2628 + this.element_ = element; 1.2629 + // Initialize instance. 1.2630 + this.init(); 1.2631 +}; 1.2632 +window['MaterialTabs'] = MaterialTabs; 1.2633 +/** 1.2634 + * Store constants in one place so they can be updated easily. 1.2635 + * 1.2636 + * @enum {string} 1.2637 + * @private 1.2638 + */ 1.2639 +MaterialTabs.prototype.Constant_ = {}; 1.2640 +/** 1.2641 + * Store strings for class names defined by this component that are used in 1.2642 + * JavaScript. This allows us to simply change it in one place should we 1.2643 + * decide to modify at a later date. 1.2644 + * 1.2645 + * @enum {string} 1.2646 + * @private 1.2647 + */ 1.2648 +MaterialTabs.prototype.CssClasses_ = { 1.2649 + TAB_CLASS: 'mdl-tabs__tab', 1.2650 + PANEL_CLASS: 'mdl-tabs__panel', 1.2651 + ACTIVE_CLASS: 'is-active', 1.2652 + UPGRADED_CLASS: 'is-upgraded', 1.2653 + MDL_JS_RIPPLE_EFFECT: 'mdl-js-ripple-effect', 1.2654 + MDL_RIPPLE_CONTAINER: 'mdl-tabs__ripple-container', 1.2655 + MDL_RIPPLE: 'mdl-ripple', 1.2656 + MDL_JS_RIPPLE_EFFECT_IGNORE_EVENTS: 'mdl-js-ripple-effect--ignore-events' 1.2657 +}; 1.2658 +/** 1.2659 + * Handle clicks to a tabs component 1.2660 + * 1.2661 + * @private 1.2662 + */ 1.2663 +MaterialTabs.prototype.initTabs_ = function () { 1.2664 + if (this.element_.classList.contains(this.CssClasses_.MDL_JS_RIPPLE_EFFECT)) { 1.2665 + this.element_.classList.add(this.CssClasses_.MDL_JS_RIPPLE_EFFECT_IGNORE_EVENTS); 1.2666 + } 1.2667 + // Select element tabs, document panels 1.2668 + this.tabs_ = this.element_.querySelectorAll('.' + this.CssClasses_.TAB_CLASS); 1.2669 + this.panels_ = this.element_.querySelectorAll('.' + this.CssClasses_.PANEL_CLASS); 1.2670 + // Create new tabs for each tab element 1.2671 + for (var i = 0; i < this.tabs_.length; i++) { 1.2672 + new MaterialTab(this.tabs_[i], this); 1.2673 + } 1.2674 + this.element_.classList.add(this.CssClasses_.UPGRADED_CLASS); 1.2675 +}; 1.2676 +/** 1.2677 + * Reset tab state, dropping active classes 1.2678 + * 1.2679 + * @private 1.2680 + */ 1.2681 +MaterialTabs.prototype.resetTabState_ = function () { 1.2682 + for (var k = 0; k < this.tabs_.length; k++) { 1.2683 + this.tabs_[k].classList.remove(this.CssClasses_.ACTIVE_CLASS); 1.2684 + } 1.2685 +}; 1.2686 +/** 1.2687 + * Reset panel state, droping active classes 1.2688 + * 1.2689 + * @private 1.2690 + */ 1.2691 +MaterialTabs.prototype.resetPanelState_ = function () { 1.2692 + for (var j = 0; j < this.panels_.length; j++) { 1.2693 + this.panels_[j].classList.remove(this.CssClasses_.ACTIVE_CLASS); 1.2694 + } 1.2695 +}; 1.2696 +/** 1.2697 + * Initialize element. 1.2698 + */ 1.2699 +MaterialTabs.prototype.init = function () { 1.2700 + if (this.element_) { 1.2701 + this.initTabs_(); 1.2702 + } 1.2703 +}; 1.2704 +/** 1.2705 + * Constructor for an individual tab. 1.2706 + * 1.2707 + * @constructor 1.2708 + * @param {Element} tab The HTML element for the tab. 1.2709 + * @param {MaterialTabs} ctx The MaterialTabs object that owns the tab. 1.2710 + */ 1.2711 +function MaterialTab(tab, ctx) { 1.2712 + if (tab) { 1.2713 + if (ctx.element_.classList.contains(ctx.CssClasses_.MDL_JS_RIPPLE_EFFECT)) { 1.2714 + var rippleContainer = document.createElement('span'); 1.2715 + rippleContainer.classList.add(ctx.CssClasses_.MDL_RIPPLE_CONTAINER); 1.2716 + rippleContainer.classList.add(ctx.CssClasses_.MDL_JS_RIPPLE_EFFECT); 1.2717 + var ripple = document.createElement('span'); 1.2718 + ripple.classList.add(ctx.CssClasses_.MDL_RIPPLE); 1.2719 + rippleContainer.appendChild(ripple); 1.2720 + tab.appendChild(rippleContainer); 1.2721 + } 1.2722 + tab.addEventListener('click', function (e) { 1.2723 + if (tab.getAttribute('href').charAt(0) === '#') { 1.2724 + e.preventDefault(); 1.2725 + } 1.2726 + var href = tab.href.split('#')[1]; 1.2727 + var panel = ctx.element_.querySelector('#' + href); 1.2728 + ctx.resetTabState_(); 1.2729 + ctx.resetPanelState_(); 1.2730 + tab.classList.add(ctx.CssClasses_.ACTIVE_CLASS); 1.2731 + if (panel) { 1.2732 + panel.classList.add(ctx.CssClasses_.ACTIVE_CLASS); 1.2733 + } 1.2734 + }); 1.2735 + } 1.2736 +} 1.2737 +// The component registers itself. It can assume componentHandler is available 1.2738 +// in the global scope. 1.2739 +componentHandler.register({ 1.2740 + constructor: MaterialTabs, 1.2741 + classAsString: 'MaterialTabs', 1.2742 + cssClass: 'mdl-js-tabs' 1.2743 +}); 1.2744 +/** 1.2745 + * @license 1.2746 + * Copyright 2015 Google Inc. All Rights Reserved. 1.2747 + * 1.2748 + * Licensed under the Apache License, Version 2.0 (the "License"); 1.2749 + * you may not use this file except in compliance with the License. 1.2750 + * You may obtain a copy of the License at 1.2751 + * 1.2752 + * http://www.apache.org/licenses/LICENSE-2.0 1.2753 + * 1.2754 + * Unless required by applicable law or agreed to in writing, software 1.2755 + * distributed under the License is distributed on an "AS IS" BASIS, 1.2756 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 1.2757 + * See the License for the specific language governing permissions and 1.2758 + * limitations under the License. 1.2759 + */ 1.2760 +/** 1.2761 + * Class constructor for Textfield MDL component. 1.2762 + * Implements MDL component design pattern defined at: 1.2763 + * https://github.com/jasonmayes/mdl-component-design-pattern 1.2764 + * 1.2765 + * @constructor 1.2766 + * @param {HTMLElement} element The element that will be upgraded. 1.2767 + */ 1.2768 +var MaterialTextfield = function MaterialTextfield(element) { 1.2769 + this.element_ = element; 1.2770 + this.maxRows = this.Constant_.NO_MAX_ROWS; 1.2771 + // Initialize instance. 1.2772 + this.init(); 1.2773 +}; 1.2774 +window['MaterialTextfield'] = MaterialTextfield; 1.2775 +/** 1.2776 + * Store constants in one place so they can be updated easily. 1.2777 + * 1.2778 + * @enum {string | number} 1.2779 + * @private 1.2780 + */ 1.2781 +MaterialTextfield.prototype.Constant_ = { 1.2782 + NO_MAX_ROWS: -1, 1.2783 + MAX_ROWS_ATTRIBUTE: 'maxrows' 1.2784 +}; 1.2785 +/** 1.2786 + * Store strings for class names defined by this component that are used in 1.2787 + * JavaScript. This allows us to simply change it in one place should we 1.2788 + * decide to modify at a later date. 1.2789 + * 1.2790 + * @enum {string} 1.2791 + * @private 1.2792 + */ 1.2793 +MaterialTextfield.prototype.CssClasses_ = { 1.2794 + LABEL: 'mdl-textfield__label', 1.2795 + INPUT: 'mdl-textfield__input', 1.2796 + IS_DIRTY: 'is-dirty', 1.2797 + IS_FOCUSED: 'is-focused', 1.2798 + IS_DISABLED: 'is-disabled', 1.2799 + IS_INVALID: 'is-invalid', 1.2800 + IS_UPGRADED: 'is-upgraded', 1.2801 + HAS_PLACEHOLDER: 'has-placeholder' 1.2802 +}; 1.2803 +/** 1.2804 + * Handle input being entered. 1.2805 + * 1.2806 + * @param {Event} event The event that fired. 1.2807 + * @private 1.2808 + */ 1.2809 +MaterialTextfield.prototype.onKeyDown_ = function (event) { 1.2810 + var currentRowCount = event.target.value.split('\n').length; 1.2811 + if (event.keyCode === 13) { 1.2812 + if (currentRowCount >= this.maxRows) { 1.2813 + event.preventDefault(); 1.2814 + } 1.2815 + } 1.2816 +}; 1.2817 +/** 1.2818 + * Handle focus. 1.2819 + * 1.2820 + * @param {Event} event The event that fired. 1.2821 + * @private 1.2822 + */ 1.2823 +MaterialTextfield.prototype.onFocus_ = function (event) { 1.2824 + this.element_.classList.add(this.CssClasses_.IS_FOCUSED); 1.2825 +}; 1.2826 +/** 1.2827 + * Handle lost focus. 1.2828 + * 1.2829 + * @param {Event} event The event that fired. 1.2830 + * @private 1.2831 + */ 1.2832 +MaterialTextfield.prototype.onBlur_ = function (event) { 1.2833 + this.element_.classList.remove(this.CssClasses_.IS_FOCUSED); 1.2834 +}; 1.2835 +/** 1.2836 + * Handle reset event from out side. 1.2837 + * 1.2838 + * @param {Event} event The event that fired. 1.2839 + * @private 1.2840 + */ 1.2841 +MaterialTextfield.prototype.onReset_ = function (event) { 1.2842 + this.updateClasses_(); 1.2843 +}; 1.2844 +/** 1.2845 + * Handle class updates. 1.2846 + * 1.2847 + * @private 1.2848 + */ 1.2849 +MaterialTextfield.prototype.updateClasses_ = function () { 1.2850 + this.checkDisabled(); 1.2851 + this.checkValidity(); 1.2852 + this.checkDirty(); 1.2853 + this.checkFocus(); 1.2854 +}; 1.2855 +// Public methods. 1.2856 +/** 1.2857 + * Check the disabled state and update field accordingly. 1.2858 + * 1.2859 + * @public 1.2860 + */ 1.2861 +MaterialTextfield.prototype.checkDisabled = function () { 1.2862 + if (this.input_.disabled) { 1.2863 + this.element_.classList.add(this.CssClasses_.IS_DISABLED); 1.2864 + } else { 1.2865 + this.element_.classList.remove(this.CssClasses_.IS_DISABLED); 1.2866 + } 1.2867 +}; 1.2868 +MaterialTextfield.prototype['checkDisabled'] = MaterialTextfield.prototype.checkDisabled; 1.2869 +/** 1.2870 + * Check the focus state and update field accordingly. 1.2871 + * 1.2872 + * @public 1.2873 + */ 1.2874 +MaterialTextfield.prototype.checkFocus = function () { 1.2875 + if (Boolean(this.element_.querySelector(':focus'))) { 1.2876 + this.element_.classList.add(this.CssClasses_.IS_FOCUSED); 1.2877 + } else { 1.2878 + this.element_.classList.remove(this.CssClasses_.IS_FOCUSED); 1.2879 + } 1.2880 +}; 1.2881 +MaterialTextfield.prototype['checkFocus'] = MaterialTextfield.prototype.checkFocus; 1.2882 +/** 1.2883 + * Check the validity state and update field accordingly. 1.2884 + * 1.2885 + * @public 1.2886 + */ 1.2887 +MaterialTextfield.prototype.checkValidity = function () { 1.2888 + if (this.input_.validity) { 1.2889 + if (this.input_.validity.valid) { 1.2890 + this.element_.classList.remove(this.CssClasses_.IS_INVALID); 1.2891 + } else { 1.2892 + this.element_.classList.add(this.CssClasses_.IS_INVALID); 1.2893 + } 1.2894 + } 1.2895 +}; 1.2896 +MaterialTextfield.prototype['checkValidity'] = MaterialTextfield.prototype.checkValidity; 1.2897 +/** 1.2898 + * Check the dirty state and update field accordingly. 1.2899 + * 1.2900 + * @public 1.2901 + */ 1.2902 +MaterialTextfield.prototype.checkDirty = function () { 1.2903 + if (this.input_.value && this.input_.value.length > 0) { 1.2904 + this.element_.classList.add(this.CssClasses_.IS_DIRTY); 1.2905 + } else { 1.2906 + this.element_.classList.remove(this.CssClasses_.IS_DIRTY); 1.2907 + } 1.2908 +}; 1.2909 +MaterialTextfield.prototype['checkDirty'] = MaterialTextfield.prototype.checkDirty; 1.2910 +/** 1.2911 + * Disable text field. 1.2912 + * 1.2913 + * @public 1.2914 + */ 1.2915 +MaterialTextfield.prototype.disable = function () { 1.2916 + this.input_.disabled = true; 1.2917 + this.updateClasses_(); 1.2918 +}; 1.2919 +MaterialTextfield.prototype['disable'] = MaterialTextfield.prototype.disable; 1.2920 +/** 1.2921 + * Enable text field. 1.2922 + * 1.2923 + * @public 1.2924 + */ 1.2925 +MaterialTextfield.prototype.enable = function () { 1.2926 + this.input_.disabled = false; 1.2927 + this.updateClasses_(); 1.2928 +}; 1.2929 +MaterialTextfield.prototype['enable'] = MaterialTextfield.prototype.enable; 1.2930 +/** 1.2931 + * Update text field value. 1.2932 + * 1.2933 + * @param {string} value The value to which to set the control (optional). 1.2934 + * @public 1.2935 + */ 1.2936 +MaterialTextfield.prototype.change = function (value) { 1.2937 + this.input_.value = value || ''; 1.2938 + this.updateClasses_(); 1.2939 +}; 1.2940 +MaterialTextfield.prototype['change'] = MaterialTextfield.prototype.change; 1.2941 +/** 1.2942 + * Initialize element. 1.2943 + */ 1.2944 +MaterialTextfield.prototype.init = function () { 1.2945 + if (this.element_) { 1.2946 + this.label_ = this.element_.querySelector('.' + this.CssClasses_.LABEL); 1.2947 + this.input_ = this.element_.querySelector('.' + this.CssClasses_.INPUT); 1.2948 + if (this.input_) { 1.2949 + if (this.input_.hasAttribute(this.Constant_.MAX_ROWS_ATTRIBUTE)) { 1.2950 + this.maxRows = parseInt(this.input_.getAttribute(this.Constant_.MAX_ROWS_ATTRIBUTE), 10); 1.2951 + if (isNaN(this.maxRows)) { 1.2952 + this.maxRows = this.Constant_.NO_MAX_ROWS; 1.2953 + } 1.2954 + } 1.2955 + if (this.input_.hasAttribute('placeholder')) { 1.2956 + this.element_.classList.add(this.CssClasses_.HAS_PLACEHOLDER); 1.2957 + } 1.2958 + this.boundUpdateClassesHandler = this.updateClasses_.bind(this); 1.2959 + this.boundFocusHandler = this.onFocus_.bind(this); 1.2960 + this.boundBlurHandler = this.onBlur_.bind(this); 1.2961 + this.boundResetHandler = this.onReset_.bind(this); 1.2962 + this.input_.addEventListener('input', this.boundUpdateClassesHandler); 1.2963 + this.input_.addEventListener('focus', this.boundFocusHandler); 1.2964 + this.input_.addEventListener('blur', this.boundBlurHandler); 1.2965 + this.input_.addEventListener('reset', this.boundResetHandler); 1.2966 + if (this.maxRows !== this.Constant_.NO_MAX_ROWS) { 1.2967 + // TODO: This should handle pasting multi line text. 1.2968 + // Currently doesn't. 1.2969 + this.boundKeyDownHandler = this.onKeyDown_.bind(this); 1.2970 + this.input_.addEventListener('keydown', this.boundKeyDownHandler); 1.2971 + } 1.2972 + var invalid = this.element_.classList.contains(this.CssClasses_.IS_INVALID); 1.2973 + this.updateClasses_(); 1.2974 + this.element_.classList.add(this.CssClasses_.IS_UPGRADED); 1.2975 + if (invalid) { 1.2976 + this.element_.classList.add(this.CssClasses_.IS_INVALID); 1.2977 + } 1.2978 + if (this.input_.hasAttribute('autofocus')) { 1.2979 + this.element_.focus(); 1.2980 + this.checkFocus(); 1.2981 + } 1.2982 + } 1.2983 + } 1.2984 +}; 1.2985 +// The component registers itself. It can assume componentHandler is available 1.2986 +// in the global scope. 1.2987 +componentHandler.register({ 1.2988 + constructor: MaterialTextfield, 1.2989 + classAsString: 'MaterialTextfield', 1.2990 + cssClass: 'mdl-js-textfield', 1.2991 + widget: true 1.2992 +}); 1.2993 +/** 1.2994 + * @license 1.2995 + * Copyright 2015 Google Inc. All Rights Reserved. 1.2996 + * 1.2997 + * Licensed under the Apache License, Version 2.0 (the "License"); 1.2998 + * you may not use this file except in compliance with the License. 1.2999 + * You may obtain a copy of the License at 1.3000 + * 1.3001 + * http://www.apache.org/licenses/LICENSE-2.0 1.3002 + * 1.3003 + * Unless required by applicable law or agreed to in writing, software 1.3004 + * distributed under the License is distributed on an "AS IS" BASIS, 1.3005 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 1.3006 + * See the License for the specific language governing permissions and 1.3007 + * limitations under the License. 1.3008 + */ 1.3009 +/** 1.3010 + * Class constructor for Tooltip MDL component. 1.3011 + * Implements MDL component design pattern defined at: 1.3012 + * https://github.com/jasonmayes/mdl-component-design-pattern 1.3013 + * 1.3014 + * @constructor 1.3015 + * @param {HTMLElement} element The element that will be upgraded. 1.3016 + */ 1.3017 +var MaterialTooltip = function MaterialTooltip(element) { 1.3018 + this.element_ = element; 1.3019 + // Initialize instance. 1.3020 + this.init(); 1.3021 +}; 1.3022 +window['MaterialTooltip'] = MaterialTooltip; 1.3023 +/** 1.3024 + * Store constants in one place so they can be updated easily. 1.3025 + * 1.3026 + * @enum {string | number} 1.3027 + * @private 1.3028 + */ 1.3029 +MaterialTooltip.prototype.Constant_ = {}; 1.3030 +/** 1.3031 + * Store strings for class names defined by this component that are used in 1.3032 + * JavaScript. This allows us to simply change it in one place should we 1.3033 + * decide to modify at a later date. 1.3034 + * 1.3035 + * @enum {string} 1.3036 + * @private 1.3037 + */ 1.3038 +MaterialTooltip.prototype.CssClasses_ = { 1.3039 + IS_ACTIVE: 'is-active', 1.3040 + BOTTOM: 'mdl-tooltip--bottom', 1.3041 + LEFT: 'mdl-tooltip--left', 1.3042 + RIGHT: 'mdl-tooltip--right', 1.3043 + TOP: 'mdl-tooltip--top' 1.3044 +}; 1.3045 +/** 1.3046 + * Handle mouseenter for tooltip. 1.3047 + * 1.3048 + * @param {Event} event The event that fired. 1.3049 + * @private 1.3050 + */ 1.3051 +MaterialTooltip.prototype.handleMouseEnter_ = function (event) { 1.3052 + var props = event.target.getBoundingClientRect(); 1.3053 + var left = props.left + props.width / 2; 1.3054 + var top = props.top + props.height / 2; 1.3055 + var marginLeft = -1 * (this.element_.offsetWidth / 2); 1.3056 + var marginTop = -1 * (this.element_.offsetHeight / 2); 1.3057 + if (this.element_.classList.contains(this.CssClasses_.LEFT) || this.element_.classList.contains(this.CssClasses_.RIGHT)) { 1.3058 + left = props.width / 2; 1.3059 + if (top + marginTop < 0) { 1.3060 + this.element_.style.top = '0'; 1.3061 + this.element_.style.marginTop = '0'; 1.3062 + } else { 1.3063 + this.element_.style.top = top + 'px'; 1.3064 + this.element_.style.marginTop = marginTop + 'px'; 1.3065 + } 1.3066 + } else { 1.3067 + if (left + marginLeft < 0) { 1.3068 + this.element_.style.left = '0'; 1.3069 + this.element_.style.marginLeft = '0'; 1.3070 + } else { 1.3071 + this.element_.style.left = left + 'px'; 1.3072 + this.element_.style.marginLeft = marginLeft + 'px'; 1.3073 + } 1.3074 + } 1.3075 + if (this.element_.classList.contains(this.CssClasses_.TOP)) { 1.3076 + this.element_.style.top = props.top - this.element_.offsetHeight - 10 + 'px'; 1.3077 + } else if (this.element_.classList.contains(this.CssClasses_.RIGHT)) { 1.3078 + this.element_.style.left = props.left + props.width + 10 + 'px'; 1.3079 + } else if (this.element_.classList.contains(this.CssClasses_.LEFT)) { 1.3080 + this.element_.style.left = props.left - this.element_.offsetWidth - 10 + 'px'; 1.3081 + } else { 1.3082 + this.element_.style.top = props.top + props.height + 10 + 'px'; 1.3083 + } 1.3084 + this.element_.classList.add(this.CssClasses_.IS_ACTIVE); 1.3085 +}; 1.3086 +/** 1.3087 + * Hide tooltip on mouseleave or scroll 1.3088 + * 1.3089 + * @private 1.3090 + */ 1.3091 +MaterialTooltip.prototype.hideTooltip_ = function () { 1.3092 + this.element_.classList.remove(this.CssClasses_.IS_ACTIVE); 1.3093 +}; 1.3094 +/** 1.3095 + * Initialize element. 1.3096 + */ 1.3097 +MaterialTooltip.prototype.init = function () { 1.3098 + if (this.element_) { 1.3099 + var forElId = this.element_.getAttribute('for') || this.element_.getAttribute('data-mdl-for'); 1.3100 + if (forElId) { 1.3101 + this.forElement_ = document.getElementById(forElId); 1.3102 + } 1.3103 + if (this.forElement_) { 1.3104 + // It's left here because it prevents accidental text selection on Android 1.3105 + if (!this.forElement_.hasAttribute('tabindex')) { 1.3106 + this.forElement_.setAttribute('tabindex', '0'); 1.3107 + } 1.3108 + this.boundMouseEnterHandler = this.handleMouseEnter_.bind(this); 1.3109 + this.boundMouseLeaveAndScrollHandler = this.hideTooltip_.bind(this); 1.3110 + this.forElement_.addEventListener('mouseenter', this.boundMouseEnterHandler, false); 1.3111 + this.forElement_.addEventListener('touchend', this.boundMouseEnterHandler, false); 1.3112 + this.forElement_.addEventListener('mouseleave', this.boundMouseLeaveAndScrollHandler, false); 1.3113 + window.addEventListener('scroll', this.boundMouseLeaveAndScrollHandler, true); 1.3114 + window.addEventListener('touchstart', this.boundMouseLeaveAndScrollHandler); 1.3115 + } 1.3116 + } 1.3117 +}; 1.3118 +// The component registers itself. It can assume componentHandler is available 1.3119 +// in the global scope. 1.3120 +componentHandler.register({ 1.3121 + constructor: MaterialTooltip, 1.3122 + classAsString: 'MaterialTooltip', 1.3123 + cssClass: 'mdl-tooltip' 1.3124 +}); 1.3125 +/** 1.3126 + * @license 1.3127 + * Copyright 2015 Google Inc. All Rights Reserved. 1.3128 + * 1.3129 + * Licensed under the Apache License, Version 2.0 (the "License"); 1.3130 + * you may not use this file except in compliance with the License. 1.3131 + * You may obtain a copy of the License at 1.3132 + * 1.3133 + * http://www.apache.org/licenses/LICENSE-2.0 1.3134 + * 1.3135 + * Unless required by applicable law or agreed to in writing, software 1.3136 + * distributed under the License is distributed on an "AS IS" BASIS, 1.3137 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 1.3138 + * See the License for the specific language governing permissions and 1.3139 + * limitations under the License. 1.3140 + */ 1.3141 +/** 1.3142 + * Class constructor for Layout MDL component. 1.3143 + * Implements MDL component design pattern defined at: 1.3144 + * https://github.com/jasonmayes/mdl-component-design-pattern 1.3145 + * 1.3146 + * @constructor 1.3147 + * @param {HTMLElement} element The element that will be upgraded. 1.3148 + */ 1.3149 +var MaterialLayout = function MaterialLayout(element) { 1.3150 + this.element_ = element; 1.3151 + // Initialize instance. 1.3152 + this.init(); 1.3153 +}; 1.3154 +window['MaterialLayout'] = MaterialLayout; 1.3155 +/** 1.3156 + * Store constants in one place so they can be updated easily. 1.3157 + * 1.3158 + * @enum {string | number} 1.3159 + * @private 1.3160 + */ 1.3161 +MaterialLayout.prototype.Constant_ = { 1.3162 + MAX_WIDTH: '(max-width: 1024px)', 1.3163 + TAB_SCROLL_PIXELS: 100, 1.3164 + RESIZE_TIMEOUT: 100, 1.3165 + MENU_ICON: '', 1.3166 + CHEVRON_LEFT: 'chevron_left', 1.3167 + CHEVRON_RIGHT: 'chevron_right' 1.3168 +}; 1.3169 +/** 1.3170 + * Keycodes, for code readability. 1.3171 + * 1.3172 + * @enum {number} 1.3173 + * @private 1.3174 + */ 1.3175 +MaterialLayout.prototype.Keycodes_ = { 1.3176 + ENTER: 13, 1.3177 + ESCAPE: 27, 1.3178 + SPACE: 32 1.3179 +}; 1.3180 +/** 1.3181 + * Modes. 1.3182 + * 1.3183 + * @enum {number} 1.3184 + * @private 1.3185 + */ 1.3186 +MaterialLayout.prototype.Mode_ = { 1.3187 + STANDARD: 0, 1.3188 + SEAMED: 1, 1.3189 + WATERFALL: 2, 1.3190 + SCROLL: 3 1.3191 +}; 1.3192 +/** 1.3193 + * Store strings for class names defined by this component that are used in 1.3194 + * JavaScript. This allows us to simply change it in one place should we 1.3195 + * decide to modify at a later date. 1.3196 + * 1.3197 + * @enum {string} 1.3198 + * @private 1.3199 + */ 1.3200 +MaterialLayout.prototype.CssClasses_ = { 1.3201 + CONTAINER: 'mdl-layout__container', 1.3202 + HEADER: 'mdl-layout__header', 1.3203 + DRAWER: 'mdl-layout__drawer', 1.3204 + CONTENT: 'mdl-layout__content', 1.3205 + DRAWER_BTN: 'mdl-layout__drawer-button', 1.3206 + ICON: 'material-icons', 1.3207 + JS_RIPPLE_EFFECT: 'mdl-js-ripple-effect', 1.3208 + RIPPLE_CONTAINER: 'mdl-layout__tab-ripple-container', 1.3209 + RIPPLE: 'mdl-ripple', 1.3210 + RIPPLE_IGNORE_EVENTS: 'mdl-js-ripple-effect--ignore-events', 1.3211 + HEADER_SEAMED: 'mdl-layout__header--seamed', 1.3212 + HEADER_WATERFALL: 'mdl-layout__header--waterfall', 1.3213 + HEADER_SCROLL: 'mdl-layout__header--scroll', 1.3214 + FIXED_HEADER: 'mdl-layout--fixed-header', 1.3215 + OBFUSCATOR: 'mdl-layout__obfuscator', 1.3216 + TAB_BAR: 'mdl-layout__tab-bar', 1.3217 + TAB_CONTAINER: 'mdl-layout__tab-bar-container', 1.3218 + TAB: 'mdl-layout__tab', 1.3219 + TAB_BAR_BUTTON: 'mdl-layout__tab-bar-button', 1.3220 + TAB_BAR_LEFT_BUTTON: 'mdl-layout__tab-bar-left-button', 1.3221 + TAB_BAR_RIGHT_BUTTON: 'mdl-layout__tab-bar-right-button', 1.3222 + PANEL: 'mdl-layout__tab-panel', 1.3223 + HAS_DRAWER: 'has-drawer', 1.3224 + HAS_TABS: 'has-tabs', 1.3225 + HAS_SCROLLING_HEADER: 'has-scrolling-header', 1.3226 + CASTING_SHADOW: 'is-casting-shadow', 1.3227 + IS_COMPACT: 'is-compact', 1.3228 + IS_SMALL_SCREEN: 'is-small-screen', 1.3229 + IS_DRAWER_OPEN: 'is-visible', 1.3230 + IS_ACTIVE: 'is-active', 1.3231 + IS_UPGRADED: 'is-upgraded', 1.3232 + IS_ANIMATING: 'is-animating', 1.3233 + ON_LARGE_SCREEN: 'mdl-layout--large-screen-only', 1.3234 + ON_SMALL_SCREEN: 'mdl-layout--small-screen-only' 1.3235 +}; 1.3236 +/** 1.3237 + * Handles scrolling on the content. 1.3238 + * 1.3239 + * @private 1.3240 + */ 1.3241 +MaterialLayout.prototype.contentScrollHandler_ = function () { 1.3242 + if (this.header_.classList.contains(this.CssClasses_.IS_ANIMATING)) { 1.3243 + return; 1.3244 + } 1.3245 + var headerVisible = !this.element_.classList.contains(this.CssClasses_.IS_SMALL_SCREEN) || this.element_.classList.contains(this.CssClasses_.FIXED_HEADER); 1.3246 + if (this.content_.scrollTop > 0 && !this.header_.classList.contains(this.CssClasses_.IS_COMPACT)) { 1.3247 + this.header_.classList.add(this.CssClasses_.CASTING_SHADOW); 1.3248 + this.header_.classList.add(this.CssClasses_.IS_COMPACT); 1.3249 + if (headerVisible) { 1.3250 + this.header_.classList.add(this.CssClasses_.IS_ANIMATING); 1.3251 + } 1.3252 + } else if (this.content_.scrollTop <= 0 && this.header_.classList.contains(this.CssClasses_.IS_COMPACT)) { 1.3253 + this.header_.classList.remove(this.CssClasses_.CASTING_SHADOW); 1.3254 + this.header_.classList.remove(this.CssClasses_.IS_COMPACT); 1.3255 + if (headerVisible) { 1.3256 + this.header_.classList.add(this.CssClasses_.IS_ANIMATING); 1.3257 + } 1.3258 + } 1.3259 +}; 1.3260 +/** 1.3261 + * Handles a keyboard event on the drawer. 1.3262 + * 1.3263 + * @param {Event} evt The event that fired. 1.3264 + * @private 1.3265 + */ 1.3266 +MaterialLayout.prototype.keyboardEventHandler_ = function (evt) { 1.3267 + // Only react when the drawer is open. 1.3268 + if (evt.keyCode === this.Keycodes_.ESCAPE && this.drawer_.classList.contains(this.CssClasses_.IS_DRAWER_OPEN)) { 1.3269 + this.toggleDrawer(); 1.3270 + } 1.3271 +}; 1.3272 +/** 1.3273 + * Handles changes in screen size. 1.3274 + * 1.3275 + * @private 1.3276 + */ 1.3277 +MaterialLayout.prototype.screenSizeHandler_ = function () { 1.3278 + if (this.screenSizeMediaQuery_.matches) { 1.3279 + this.element_.classList.add(this.CssClasses_.IS_SMALL_SCREEN); 1.3280 + } else { 1.3281 + this.element_.classList.remove(this.CssClasses_.IS_SMALL_SCREEN); 1.3282 + // Collapse drawer (if any) when moving to a large screen size. 1.3283 + if (this.drawer_) { 1.3284 + this.drawer_.classList.remove(this.CssClasses_.IS_DRAWER_OPEN); 1.3285 + this.obfuscator_.classList.remove(this.CssClasses_.IS_DRAWER_OPEN); 1.3286 + } 1.3287 + } 1.3288 +}; 1.3289 +/** 1.3290 + * Handles events of drawer button. 1.3291 + * 1.3292 + * @param {Event} evt The event that fired. 1.3293 + * @private 1.3294 + */ 1.3295 +MaterialLayout.prototype.drawerToggleHandler_ = function (evt) { 1.3296 + if (evt && evt.type === 'keydown') { 1.3297 + if (evt.keyCode === this.Keycodes_.SPACE || evt.keyCode === this.Keycodes_.ENTER) { 1.3298 + // prevent scrolling in drawer nav 1.3299 + evt.preventDefault(); 1.3300 + } else { 1.3301 + // prevent other keys 1.3302 + return; 1.3303 + } 1.3304 + } 1.3305 + this.toggleDrawer(); 1.3306 +}; 1.3307 +/** 1.3308 + * Handles (un)setting the `is-animating` class 1.3309 + * 1.3310 + * @private 1.3311 + */ 1.3312 +MaterialLayout.prototype.headerTransitionEndHandler_ = function () { 1.3313 + this.header_.classList.remove(this.CssClasses_.IS_ANIMATING); 1.3314 +}; 1.3315 +/** 1.3316 + * Handles expanding the header on click 1.3317 + * 1.3318 + * @private 1.3319 + */ 1.3320 +MaterialLayout.prototype.headerClickHandler_ = function () { 1.3321 + if (this.header_.classList.contains(this.CssClasses_.IS_COMPACT)) { 1.3322 + this.header_.classList.remove(this.CssClasses_.IS_COMPACT); 1.3323 + this.header_.classList.add(this.CssClasses_.IS_ANIMATING); 1.3324 + } 1.3325 +}; 1.3326 +/** 1.3327 + * Reset tab state, dropping active classes 1.3328 + * 1.3329 + * @private 1.3330 + */ 1.3331 +MaterialLayout.prototype.resetTabState_ = function (tabBar) { 1.3332 + for (var k = 0; k < tabBar.length; k++) { 1.3333 + tabBar[k].classList.remove(this.CssClasses_.IS_ACTIVE); 1.3334 + } 1.3335 +}; 1.3336 +/** 1.3337 + * Reset panel state, droping active classes 1.3338 + * 1.3339 + * @private 1.3340 + */ 1.3341 +MaterialLayout.prototype.resetPanelState_ = function (panels) { 1.3342 + for (var j = 0; j < panels.length; j++) { 1.3343 + panels[j].classList.remove(this.CssClasses_.IS_ACTIVE); 1.3344 + } 1.3345 +}; 1.3346 +/** 1.3347 + * Toggle drawer state 1.3348 + * 1.3349 + * @public 1.3350 + */ 1.3351 +MaterialLayout.prototype.toggleDrawer = function () { 1.3352 + var drawerButton = this.element_.querySelector('.' + this.CssClasses_.DRAWER_BTN); 1.3353 + this.drawer_.classList.toggle(this.CssClasses_.IS_DRAWER_OPEN); 1.3354 + this.obfuscator_.classList.toggle(this.CssClasses_.IS_DRAWER_OPEN); 1.3355 + // Set accessibility properties. 1.3356 + if (this.drawer_.classList.contains(this.CssClasses_.IS_DRAWER_OPEN)) { 1.3357 + this.drawer_.setAttribute('aria-hidden', 'false'); 1.3358 + drawerButton.setAttribute('aria-expanded', 'true'); 1.3359 + } else { 1.3360 + this.drawer_.setAttribute('aria-hidden', 'true'); 1.3361 + drawerButton.setAttribute('aria-expanded', 'false'); 1.3362 + } 1.3363 +}; 1.3364 +MaterialLayout.prototype['toggleDrawer'] = MaterialLayout.prototype.toggleDrawer; 1.3365 +/** 1.3366 + * Initialize element. 1.3367 + */ 1.3368 +MaterialLayout.prototype.init = function () { 1.3369 + if (this.element_) { 1.3370 + var container = document.createElement('div'); 1.3371 + container.classList.add(this.CssClasses_.CONTAINER); 1.3372 + var focusedElement = this.element_.querySelector(':focus'); 1.3373 + this.element_.parentElement.insertBefore(container, this.element_); 1.3374 + this.element_.parentElement.removeChild(this.element_); 1.3375 + container.appendChild(this.element_); 1.3376 + if (focusedElement) { 1.3377 + focusedElement.focus(); 1.3378 + } 1.3379 + var directChildren = this.element_.childNodes; 1.3380 + var numChildren = directChildren.length; 1.3381 + for (var c = 0; c < numChildren; c++) { 1.3382 + var child = directChildren[c]; 1.3383 + if (child.classList && child.classList.contains(this.CssClasses_.HEADER)) { 1.3384 + this.header_ = child; 1.3385 + } 1.3386 + if (child.classList && child.classList.contains(this.CssClasses_.DRAWER)) { 1.3387 + this.drawer_ = child; 1.3388 + } 1.3389 + if (child.classList && child.classList.contains(this.CssClasses_.CONTENT)) { 1.3390 + this.content_ = child; 1.3391 + } 1.3392 + } 1.3393 + window.addEventListener('pageshow', function (e) { 1.3394 + if (e.persisted) { 1.3395 + // when page is loaded from back/forward cache 1.3396 + // trigger repaint to let layout scroll in safari 1.3397 + this.element_.style.overflowY = 'hidden'; 1.3398 + requestAnimationFrame(function () { 1.3399 + this.element_.style.overflowY = ''; 1.3400 + }.bind(this)); 1.3401 + } 1.3402 + }.bind(this), false); 1.3403 + if (this.header_) { 1.3404 + this.tabBar_ = this.header_.querySelector('.' + this.CssClasses_.TAB_BAR); 1.3405 + } 1.3406 + var mode = this.Mode_.STANDARD; 1.3407 + if (this.header_) { 1.3408 + if (this.header_.classList.contains(this.CssClasses_.HEADER_SEAMED)) { 1.3409 + mode = this.Mode_.SEAMED; 1.3410 + } else if (this.header_.classList.contains(this.CssClasses_.HEADER_WATERFALL)) { 1.3411 + mode = this.Mode_.WATERFALL; 1.3412 + this.header_.addEventListener('transitionend', this.headerTransitionEndHandler_.bind(this)); 1.3413 + this.header_.addEventListener('click', this.headerClickHandler_.bind(this)); 1.3414 + } else if (this.header_.classList.contains(this.CssClasses_.HEADER_SCROLL)) { 1.3415 + mode = this.Mode_.SCROLL; 1.3416 + container.classList.add(this.CssClasses_.HAS_SCROLLING_HEADER); 1.3417 + } 1.3418 + if (mode === this.Mode_.STANDARD) { 1.3419 + this.header_.classList.add(this.CssClasses_.CASTING_SHADOW); 1.3420 + if (this.tabBar_) { 1.3421 + this.tabBar_.classList.add(this.CssClasses_.CASTING_SHADOW); 1.3422 + } 1.3423 + } else if (mode === this.Mode_.SEAMED || mode === this.Mode_.SCROLL) { 1.3424 + this.header_.classList.remove(this.CssClasses_.CASTING_SHADOW); 1.3425 + if (this.tabBar_) { 1.3426 + this.tabBar_.classList.remove(this.CssClasses_.CASTING_SHADOW); 1.3427 + } 1.3428 + } else if (mode === this.Mode_.WATERFALL) { 1.3429 + // Add and remove shadows depending on scroll position. 1.3430 + // Also add/remove auxiliary class for styling of the compact version of 1.3431 + // the header. 1.3432 + this.content_.addEventListener('scroll', this.contentScrollHandler_.bind(this)); 1.3433 + this.contentScrollHandler_(); 1.3434 + } 1.3435 + } 1.3436 + // Add drawer toggling button to our layout, if we have an openable drawer. 1.3437 + if (this.drawer_) { 1.3438 + var drawerButton = this.element_.querySelector('.' + this.CssClasses_.DRAWER_BTN); 1.3439 + if (!drawerButton) { 1.3440 + drawerButton = document.createElement('div'); 1.3441 + drawerButton.setAttribute('aria-expanded', 'false'); 1.3442 + drawerButton.setAttribute('role', 'button'); 1.3443 + drawerButton.setAttribute('tabindex', '0'); 1.3444 + drawerButton.classList.add(this.CssClasses_.DRAWER_BTN); 1.3445 + var drawerButtonIcon = document.createElement('i'); 1.3446 + drawerButtonIcon.classList.add(this.CssClasses_.ICON); 1.3447 + drawerButtonIcon.innerHTML = this.Constant_.MENU_ICON; 1.3448 + drawerButton.appendChild(drawerButtonIcon); 1.3449 + } 1.3450 + if (this.drawer_.classList.contains(this.CssClasses_.ON_LARGE_SCREEN)) { 1.3451 + //If drawer has ON_LARGE_SCREEN class then add it to the drawer toggle button as well. 1.3452 + drawerButton.classList.add(this.CssClasses_.ON_LARGE_SCREEN); 1.3453 + } else if (this.drawer_.classList.contains(this.CssClasses_.ON_SMALL_SCREEN)) { 1.3454 + //If drawer has ON_SMALL_SCREEN class then add it to the drawer toggle button as well. 1.3455 + drawerButton.classList.add(this.CssClasses_.ON_SMALL_SCREEN); 1.3456 + } 1.3457 + drawerButton.addEventListener('click', this.drawerToggleHandler_.bind(this)); 1.3458 + drawerButton.addEventListener('keydown', this.drawerToggleHandler_.bind(this)); 1.3459 + // Add a class if the layout has a drawer, for altering the left padding. 1.3460 + // Adds the HAS_DRAWER to the elements since this.header_ may or may 1.3461 + // not be present. 1.3462 + this.element_.classList.add(this.CssClasses_.HAS_DRAWER); 1.3463 + // If we have a fixed header, add the button to the header rather than 1.3464 + // the layout. 1.3465 + if (this.element_.classList.contains(this.CssClasses_.FIXED_HEADER)) { 1.3466 + this.header_.insertBefore(drawerButton, this.header_.firstChild); 1.3467 + } else { 1.3468 + this.element_.insertBefore(drawerButton, this.content_); 1.3469 + } 1.3470 + var obfuscator = document.createElement('div'); 1.3471 + obfuscator.classList.add(this.CssClasses_.OBFUSCATOR); 1.3472 + this.element_.appendChild(obfuscator); 1.3473 + obfuscator.addEventListener('click', this.drawerToggleHandler_.bind(this)); 1.3474 + this.obfuscator_ = obfuscator; 1.3475 + this.drawer_.addEventListener('keydown', this.keyboardEventHandler_.bind(this)); 1.3476 + this.drawer_.setAttribute('aria-hidden', 'true'); 1.3477 + } 1.3478 + // Keep an eye on screen size, and add/remove auxiliary class for styling 1.3479 + // of small screens. 1.3480 + this.screenSizeMediaQuery_ = window.matchMedia(this.Constant_.MAX_WIDTH); 1.3481 + this.screenSizeMediaQuery_.addListener(this.screenSizeHandler_.bind(this)); 1.3482 + this.screenSizeHandler_(); 1.3483 + // Initialize tabs, if any. 1.3484 + if (this.header_ && this.tabBar_) { 1.3485 + this.element_.classList.add(this.CssClasses_.HAS_TABS); 1.3486 + var tabContainer = document.createElement('div'); 1.3487 + tabContainer.classList.add(this.CssClasses_.TAB_CONTAINER); 1.3488 + this.header_.insertBefore(tabContainer, this.tabBar_); 1.3489 + this.header_.removeChild(this.tabBar_); 1.3490 + var leftButton = document.createElement('div'); 1.3491 + leftButton.classList.add(this.CssClasses_.TAB_BAR_BUTTON); 1.3492 + leftButton.classList.add(this.CssClasses_.TAB_BAR_LEFT_BUTTON); 1.3493 + var leftButtonIcon = document.createElement('i'); 1.3494 + leftButtonIcon.classList.add(this.CssClasses_.ICON); 1.3495 + leftButtonIcon.textContent = this.Constant_.CHEVRON_LEFT; 1.3496 + leftButton.appendChild(leftButtonIcon); 1.3497 + leftButton.addEventListener('click', function () { 1.3498 + this.tabBar_.scrollLeft -= this.Constant_.TAB_SCROLL_PIXELS; 1.3499 + }.bind(this)); 1.3500 + var rightButton = document.createElement('div'); 1.3501 + rightButton.classList.add(this.CssClasses_.TAB_BAR_BUTTON); 1.3502 + rightButton.classList.add(this.CssClasses_.TAB_BAR_RIGHT_BUTTON); 1.3503 + var rightButtonIcon = document.createElement('i'); 1.3504 + rightButtonIcon.classList.add(this.CssClasses_.ICON); 1.3505 + rightButtonIcon.textContent = this.Constant_.CHEVRON_RIGHT; 1.3506 + rightButton.appendChild(rightButtonIcon); 1.3507 + rightButton.addEventListener('click', function () { 1.3508 + this.tabBar_.scrollLeft += this.Constant_.TAB_SCROLL_PIXELS; 1.3509 + }.bind(this)); 1.3510 + tabContainer.appendChild(leftButton); 1.3511 + tabContainer.appendChild(this.tabBar_); 1.3512 + tabContainer.appendChild(rightButton); 1.3513 + // Add and remove tab buttons depending on scroll position and total 1.3514 + // window size. 1.3515 + var tabUpdateHandler = function () { 1.3516 + if (this.tabBar_.scrollLeft > 0) { 1.3517 + leftButton.classList.add(this.CssClasses_.IS_ACTIVE); 1.3518 + } else { 1.3519 + leftButton.classList.remove(this.CssClasses_.IS_ACTIVE); 1.3520 + } 1.3521 + if (this.tabBar_.scrollLeft < this.tabBar_.scrollWidth - this.tabBar_.offsetWidth) { 1.3522 + rightButton.classList.add(this.CssClasses_.IS_ACTIVE); 1.3523 + } else { 1.3524 + rightButton.classList.remove(this.CssClasses_.IS_ACTIVE); 1.3525 + } 1.3526 + }.bind(this); 1.3527 + this.tabBar_.addEventListener('scroll', tabUpdateHandler); 1.3528 + tabUpdateHandler(); 1.3529 + // Update tabs when the window resizes. 1.3530 + var windowResizeHandler = function () { 1.3531 + // Use timeouts to make sure it doesn't happen too often. 1.3532 + if (this.resizeTimeoutId_) { 1.3533 + clearTimeout(this.resizeTimeoutId_); 1.3534 + } 1.3535 + this.resizeTimeoutId_ = setTimeout(function () { 1.3536 + tabUpdateHandler(); 1.3537 + this.resizeTimeoutId_ = null; 1.3538 + }.bind(this), this.Constant_.RESIZE_TIMEOUT); 1.3539 + }.bind(this); 1.3540 + window.addEventListener('resize', windowResizeHandler); 1.3541 + if (this.tabBar_.classList.contains(this.CssClasses_.JS_RIPPLE_EFFECT)) { 1.3542 + this.tabBar_.classList.add(this.CssClasses_.RIPPLE_IGNORE_EVENTS); 1.3543 + } 1.3544 + // Select element tabs, document panels 1.3545 + var tabs = this.tabBar_.querySelectorAll('.' + this.CssClasses_.TAB); 1.3546 + var panels = this.content_.querySelectorAll('.' + this.CssClasses_.PANEL); 1.3547 + // Create new tabs for each tab element 1.3548 + for (var i = 0; i < tabs.length; i++) { 1.3549 + new MaterialLayoutTab(tabs[i], tabs, panels, this); 1.3550 + } 1.3551 + } 1.3552 + this.element_.classList.add(this.CssClasses_.IS_UPGRADED); 1.3553 + } 1.3554 +}; 1.3555 +/** 1.3556 + * Constructor for an individual tab. 1.3557 + * 1.3558 + * @constructor 1.3559 + * @param {HTMLElement} tab The HTML element for the tab. 1.3560 + * @param {!Array<HTMLElement>} tabs Array with HTML elements for all tabs. 1.3561 + * @param {!Array<HTMLElement>} panels Array with HTML elements for all panels. 1.3562 + * @param {MaterialLayout} layout The MaterialLayout object that owns the tab. 1.3563 + */ 1.3564 +function MaterialLayoutTab(tab, tabs, panels, layout) { 1.3565 + /** 1.3566 + * Auxiliary method to programmatically select a tab in the UI. 1.3567 + */ 1.3568 + function selectTab() { 1.3569 + var href = tab.href.split('#')[1]; 1.3570 + var panel = layout.content_.querySelector('#' + href); 1.3571 + layout.resetTabState_(tabs); 1.3572 + layout.resetPanelState_(panels); 1.3573 + tab.classList.add(layout.CssClasses_.IS_ACTIVE); 1.3574 + panel.classList.add(layout.CssClasses_.IS_ACTIVE); 1.3575 + } 1.3576 + if (layout.tabBar_.classList.contains(layout.CssClasses_.JS_RIPPLE_EFFECT)) { 1.3577 + var rippleContainer = document.createElement('span'); 1.3578 + rippleContainer.classList.add(layout.CssClasses_.RIPPLE_CONTAINER); 1.3579 + rippleContainer.classList.add(layout.CssClasses_.JS_RIPPLE_EFFECT); 1.3580 + var ripple = document.createElement('span'); 1.3581 + ripple.classList.add(layout.CssClasses_.RIPPLE); 1.3582 + rippleContainer.appendChild(ripple); 1.3583 + tab.appendChild(rippleContainer); 1.3584 + } 1.3585 + tab.addEventListener('click', function (e) { 1.3586 + if (tab.getAttribute('href').charAt(0) === '#') { 1.3587 + e.preventDefault(); 1.3588 + selectTab(); 1.3589 + } 1.3590 + }); 1.3591 + tab.show = selectTab; 1.3592 +} 1.3593 +window['MaterialLayoutTab'] = MaterialLayoutTab; 1.3594 +// The component registers itself. It can assume componentHandler is available 1.3595 +// in the global scope. 1.3596 +componentHandler.register({ 1.3597 + constructor: MaterialLayout, 1.3598 + classAsString: 'MaterialLayout', 1.3599 + cssClass: 'mdl-js-layout' 1.3600 +}); 1.3601 +/** 1.3602 + * @license 1.3603 + * Copyright 2015 Google Inc. All Rights Reserved. 1.3604 + * 1.3605 + * Licensed under the Apache License, Version 2.0 (the "License"); 1.3606 + * you may not use this file except in compliance with the License. 1.3607 + * You may obtain a copy of the License at 1.3608 + * 1.3609 + * http://www.apache.org/licenses/LICENSE-2.0 1.3610 + * 1.3611 + * Unless required by applicable law or agreed to in writing, software 1.3612 + * distributed under the License is distributed on an "AS IS" BASIS, 1.3613 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 1.3614 + * See the License for the specific language governing permissions and 1.3615 + * limitations under the License. 1.3616 + */ 1.3617 +/** 1.3618 + * Class constructor for Data Table Card MDL component. 1.3619 + * Implements MDL component design pattern defined at: 1.3620 + * https://github.com/jasonmayes/mdl-component-design-pattern 1.3621 + * 1.3622 + * @constructor 1.3623 + * @param {Element} element The element that will be upgraded. 1.3624 + */ 1.3625 +var MaterialDataTable = function MaterialDataTable(element) { 1.3626 + this.element_ = element; 1.3627 + // Initialize instance. 1.3628 + this.init(); 1.3629 +}; 1.3630 +window['MaterialDataTable'] = MaterialDataTable; 1.3631 +/** 1.3632 + * Store constants in one place so they can be updated easily. 1.3633 + * 1.3634 + * @enum {string | number} 1.3635 + * @private 1.3636 + */ 1.3637 +MaterialDataTable.prototype.Constant_ = {}; 1.3638 +/** 1.3639 + * Store strings for class names defined by this component that are used in 1.3640 + * JavaScript. This allows us to simply change it in one place should we 1.3641 + * decide to modify at a later date. 1.3642 + * 1.3643 + * @enum {string} 1.3644 + * @private 1.3645 + */ 1.3646 +MaterialDataTable.prototype.CssClasses_ = { 1.3647 + DATA_TABLE: 'mdl-data-table', 1.3648 + SELECTABLE: 'mdl-data-table--selectable', 1.3649 + SELECT_ELEMENT: 'mdl-data-table__select', 1.3650 + IS_SELECTED: 'is-selected', 1.3651 + IS_UPGRADED: 'is-upgraded' 1.3652 +}; 1.3653 +/** 1.3654 + * Generates and returns a function that toggles the selection state of a 1.3655 + * single row (or multiple rows). 1.3656 + * 1.3657 + * @param {Element} checkbox Checkbox that toggles the selection state. 1.3658 + * @param {Element} row Row to toggle when checkbox changes. 1.3659 + * @param {(Array<Object>|NodeList)=} opt_rows Rows to toggle when checkbox changes. 1.3660 + * @private 1.3661 + */ 1.3662 +MaterialDataTable.prototype.selectRow_ = function (checkbox, row, opt_rows) { 1.3663 + if (row) { 1.3664 + return function () { 1.3665 + if (checkbox.checked) { 1.3666 + row.classList.add(this.CssClasses_.IS_SELECTED); 1.3667 + } else { 1.3668 + row.classList.remove(this.CssClasses_.IS_SELECTED); 1.3669 + } 1.3670 + }.bind(this); 1.3671 + } 1.3672 + if (opt_rows) { 1.3673 + return function () { 1.3674 + var i; 1.3675 + var el; 1.3676 + if (checkbox.checked) { 1.3677 + for (i = 0; i < opt_rows.length; i++) { 1.3678 + el = opt_rows[i].querySelector('td').querySelector('.mdl-checkbox'); 1.3679 + el['MaterialCheckbox'].check(); 1.3680 + opt_rows[i].classList.add(this.CssClasses_.IS_SELECTED); 1.3681 + } 1.3682 + } else { 1.3683 + for (i = 0; i < opt_rows.length; i++) { 1.3684 + el = opt_rows[i].querySelector('td').querySelector('.mdl-checkbox'); 1.3685 + el['MaterialCheckbox'].uncheck(); 1.3686 + opt_rows[i].classList.remove(this.CssClasses_.IS_SELECTED); 1.3687 + } 1.3688 + } 1.3689 + }.bind(this); 1.3690 + } 1.3691 +}; 1.3692 +/** 1.3693 + * Creates a checkbox for a single or or multiple rows and hooks up the 1.3694 + * event handling. 1.3695 + * 1.3696 + * @param {Element} row Row to toggle when checkbox changes. 1.3697 + * @param {(Array<Object>|NodeList)=} opt_rows Rows to toggle when checkbox changes. 1.3698 + * @private 1.3699 + */ 1.3700 +MaterialDataTable.prototype.createCheckbox_ = function (row, opt_rows) { 1.3701 + var label = document.createElement('label'); 1.3702 + var labelClasses = [ 1.3703 + 'mdl-checkbox', 1.3704 + 'mdl-js-checkbox', 1.3705 + 'mdl-js-ripple-effect', 1.3706 + this.CssClasses_.SELECT_ELEMENT 1.3707 + ]; 1.3708 + label.className = labelClasses.join(' '); 1.3709 + var checkbox = document.createElement('input'); 1.3710 + checkbox.type = 'checkbox'; 1.3711 + checkbox.classList.add('mdl-checkbox__input'); 1.3712 + if (row) { 1.3713 + checkbox.checked = row.classList.contains(this.CssClasses_.IS_SELECTED); 1.3714 + checkbox.addEventListener('change', this.selectRow_(checkbox, row)); 1.3715 + } else if (opt_rows) { 1.3716 + checkbox.addEventListener('change', this.selectRow_(checkbox, null, opt_rows)); 1.3717 + } 1.3718 + label.appendChild(checkbox); 1.3719 + componentHandler.upgradeElement(label, 'MaterialCheckbox'); 1.3720 + return label; 1.3721 +}; 1.3722 +/** 1.3723 + * Initialize element. 1.3724 + */ 1.3725 +MaterialDataTable.prototype.init = function () { 1.3726 + if (this.element_) { 1.3727 + var firstHeader = this.element_.querySelector('th'); 1.3728 + var bodyRows = Array.prototype.slice.call(this.element_.querySelectorAll('tbody tr')); 1.3729 + var footRows = Array.prototype.slice.call(this.element_.querySelectorAll('tfoot tr')); 1.3730 + var rows = bodyRows.concat(footRows); 1.3731 + if (this.element_.classList.contains(this.CssClasses_.SELECTABLE)) { 1.3732 + var th = document.createElement('th'); 1.3733 + var headerCheckbox = this.createCheckbox_(null, rows); 1.3734 + th.appendChild(headerCheckbox); 1.3735 + firstHeader.parentElement.insertBefore(th, firstHeader); 1.3736 + for (var i = 0; i < rows.length; i++) { 1.3737 + var firstCell = rows[i].querySelector('td'); 1.3738 + if (firstCell) { 1.3739 + var td = document.createElement('td'); 1.3740 + if (rows[i].parentNode.nodeName.toUpperCase() === 'TBODY') { 1.3741 + var rowCheckbox = this.createCheckbox_(rows[i]); 1.3742 + td.appendChild(rowCheckbox); 1.3743 + } 1.3744 + rows[i].insertBefore(td, firstCell); 1.3745 + } 1.3746 + } 1.3747 + this.element_.classList.add(this.CssClasses_.IS_UPGRADED); 1.3748 + } 1.3749 + } 1.3750 +}; 1.3751 +// The component registers itself. It can assume componentHandler is available 1.3752 +// in the global scope. 1.3753 +componentHandler.register({ 1.3754 + constructor: MaterialDataTable, 1.3755 + classAsString: 'MaterialDataTable', 1.3756 + cssClass: 'mdl-js-data-table' 1.3757 +}); 1.3758 +/** 1.3759 + * @license 1.3760 + * Copyright 2015 Google Inc. All Rights Reserved. 1.3761 + * 1.3762 + * Licensed under the Apache License, Version 2.0 (the "License"); 1.3763 + * you may not use this file except in compliance with the License. 1.3764 + * You may obtain a copy of the License at 1.3765 + * 1.3766 + * http://www.apache.org/licenses/LICENSE-2.0 1.3767 + * 1.3768 + * Unless required by applicable law or agreed to in writing, software 1.3769 + * distributed under the License is distributed on an "AS IS" BASIS, 1.3770 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 1.3771 + * See the License for the specific language governing permissions and 1.3772 + * limitations under the License. 1.3773 + */ 1.3774 +/** 1.3775 + * Class constructor for Ripple MDL component. 1.3776 + * Implements MDL component design pattern defined at: 1.3777 + * https://github.com/jasonmayes/mdl-component-design-pattern 1.3778 + * 1.3779 + * @constructor 1.3780 + * @param {HTMLElement} element The element that will be upgraded. 1.3781 + */ 1.3782 +var MaterialRipple = function MaterialRipple(element) { 1.3783 + this.element_ = element; 1.3784 + // Initialize instance. 1.3785 + this.init(); 1.3786 +}; 1.3787 +window['MaterialRipple'] = MaterialRipple; 1.3788 +/** 1.3789 + * Store constants in one place so they can be updated easily. 1.3790 + * 1.3791 + * @enum {string | number} 1.3792 + * @private 1.3793 + */ 1.3794 +MaterialRipple.prototype.Constant_ = { 1.3795 + INITIAL_SCALE: 'scale(0.0001, 0.0001)', 1.3796 + INITIAL_SIZE: '1px', 1.3797 + INITIAL_OPACITY: '0.4', 1.3798 + FINAL_OPACITY: '0', 1.3799 + FINAL_SCALE: '' 1.3800 +}; 1.3801 +/** 1.3802 + * Store strings for class names defined by this component that are used in 1.3803 + * JavaScript. This allows us to simply change it in one place should we 1.3804 + * decide to modify at a later date. 1.3805 + * 1.3806 + * @enum {string} 1.3807 + * @private 1.3808 + */ 1.3809 +MaterialRipple.prototype.CssClasses_ = { 1.3810 + RIPPLE_CENTER: 'mdl-ripple--center', 1.3811 + RIPPLE_EFFECT_IGNORE_EVENTS: 'mdl-js-ripple-effect--ignore-events', 1.3812 + RIPPLE: 'mdl-ripple', 1.3813 + IS_ANIMATING: 'is-animating', 1.3814 + IS_VISIBLE: 'is-visible' 1.3815 +}; 1.3816 +/** 1.3817 + * Handle mouse / finger down on element. 1.3818 + * 1.3819 + * @param {Event} event The event that fired. 1.3820 + * @private 1.3821 + */ 1.3822 +MaterialRipple.prototype.downHandler_ = function (event) { 1.3823 + if (!this.rippleElement_.style.width && !this.rippleElement_.style.height) { 1.3824 + var rect = this.element_.getBoundingClientRect(); 1.3825 + this.boundHeight = rect.height; 1.3826 + this.boundWidth = rect.width; 1.3827 + this.rippleSize_ = Math.sqrt(rect.width * rect.width + rect.height * rect.height) * 2 + 2; 1.3828 + this.rippleElement_.style.width = this.rippleSize_ + 'px'; 1.3829 + this.rippleElement_.style.height = this.rippleSize_ + 'px'; 1.3830 + } 1.3831 + this.rippleElement_.classList.add(this.CssClasses_.IS_VISIBLE); 1.3832 + if (event.type === 'mousedown' && this.ignoringMouseDown_) { 1.3833 + this.ignoringMouseDown_ = false; 1.3834 + } else { 1.3835 + if (event.type === 'touchstart') { 1.3836 + this.ignoringMouseDown_ = true; 1.3837 + } 1.3838 + var frameCount = this.getFrameCount(); 1.3839 + if (frameCount > 0) { 1.3840 + return; 1.3841 + } 1.3842 + this.setFrameCount(1); 1.3843 + var bound = event.currentTarget.getBoundingClientRect(); 1.3844 + var x; 1.3845 + var y; 1.3846 + // Check if we are handling a keyboard click. 1.3847 + if (event.clientX === 0 && event.clientY === 0) { 1.3848 + x = Math.round(bound.width / 2); 1.3849 + y = Math.round(bound.height / 2); 1.3850 + } else { 1.3851 + var clientX = event.clientX ? event.clientX : event.touches[0].clientX; 1.3852 + var clientY = event.clientY ? event.clientY : event.touches[0].clientY; 1.3853 + x = Math.round(clientX - bound.left); 1.3854 + y = Math.round(clientY - bound.top); 1.3855 + } 1.3856 + this.setRippleXY(x, y); 1.3857 + this.setRippleStyles(true); 1.3858 + window.requestAnimationFrame(this.animFrameHandler.bind(this)); 1.3859 + } 1.3860 +}; 1.3861 +/** 1.3862 + * Handle mouse / finger up on element. 1.3863 + * 1.3864 + * @param {Event} event The event that fired. 1.3865 + * @private 1.3866 + */ 1.3867 +MaterialRipple.prototype.upHandler_ = function (event) { 1.3868 + // Don't fire for the artificial "mouseup" generated by a double-click. 1.3869 + if (event && event.detail !== 2) { 1.3870 + // Allow a repaint to occur before removing this class, so the animation 1.3871 + // shows for tap events, which seem to trigger a mouseup too soon after 1.3872 + // mousedown. 1.3873 + window.setTimeout(function () { 1.3874 + this.rippleElement_.classList.remove(this.CssClasses_.IS_VISIBLE); 1.3875 + }.bind(this), 0); 1.3876 + } 1.3877 +}; 1.3878 +/** 1.3879 + * Initialize element. 1.3880 + */ 1.3881 +MaterialRipple.prototype.init = function () { 1.3882 + if (this.element_) { 1.3883 + var recentering = this.element_.classList.contains(this.CssClasses_.RIPPLE_CENTER); 1.3884 + if (!this.element_.classList.contains(this.CssClasses_.RIPPLE_EFFECT_IGNORE_EVENTS)) { 1.3885 + this.rippleElement_ = this.element_.querySelector('.' + this.CssClasses_.RIPPLE); 1.3886 + this.frameCount_ = 0; 1.3887 + this.rippleSize_ = 0; 1.3888 + this.x_ = 0; 1.3889 + this.y_ = 0; 1.3890 + // Touch start produces a compat mouse down event, which would cause a 1.3891 + // second ripples. To avoid that, we use this property to ignore the first 1.3892 + // mouse down after a touch start. 1.3893 + this.ignoringMouseDown_ = false; 1.3894 + this.boundDownHandler = this.downHandler_.bind(this); 1.3895 + this.element_.addEventListener('mousedown', this.boundDownHandler); 1.3896 + this.element_.addEventListener('touchstart', this.boundDownHandler); 1.3897 + this.boundUpHandler = this.upHandler_.bind(this); 1.3898 + this.element_.addEventListener('mouseup', this.boundUpHandler); 1.3899 + this.element_.addEventListener('mouseleave', this.boundUpHandler); 1.3900 + this.element_.addEventListener('touchend', this.boundUpHandler); 1.3901 + this.element_.addEventListener('blur', this.boundUpHandler); 1.3902 + /** 1.3903 + * Getter for frameCount_. 1.3904 + * @return {number} the frame count. 1.3905 + */ 1.3906 + this.getFrameCount = function () { 1.3907 + return this.frameCount_; 1.3908 + }; 1.3909 + /** 1.3910 + * Setter for frameCount_. 1.3911 + * @param {number} fC the frame count. 1.3912 + */ 1.3913 + this.setFrameCount = function (fC) { 1.3914 + this.frameCount_ = fC; 1.3915 + }; 1.3916 + /** 1.3917 + * Getter for rippleElement_. 1.3918 + * @return {Element} the ripple element. 1.3919 + */ 1.3920 + this.getRippleElement = function () { 1.3921 + return this.rippleElement_; 1.3922 + }; 1.3923 + /** 1.3924 + * Sets the ripple X and Y coordinates. 1.3925 + * @param {number} newX the new X coordinate 1.3926 + * @param {number} newY the new Y coordinate 1.3927 + */ 1.3928 + this.setRippleXY = function (newX, newY) { 1.3929 + this.x_ = newX; 1.3930 + this.y_ = newY; 1.3931 + }; 1.3932 + /** 1.3933 + * Sets the ripple styles. 1.3934 + * @param {boolean} start whether or not this is the start frame. 1.3935 + */ 1.3936 + this.setRippleStyles = function (start) { 1.3937 + if (this.rippleElement_ !== null) { 1.3938 + var transformString; 1.3939 + var scale; 1.3940 + var size; 1.3941 + var offset = 'translate(' + this.x_ + 'px, ' + this.y_ + 'px)'; 1.3942 + if (start) { 1.3943 + scale = this.Constant_.INITIAL_SCALE; 1.3944 + size = this.Constant_.INITIAL_SIZE; 1.3945 + } else { 1.3946 + scale = this.Constant_.FINAL_SCALE; 1.3947 + size = this.rippleSize_ + 'px'; 1.3948 + if (recentering) { 1.3949 + offset = 'translate(' + this.boundWidth / 2 + 'px, ' + this.boundHeight / 2 + 'px)'; 1.3950 + } 1.3951 + } 1.3952 + transformString = 'translate(-50%, -50%) ' + offset + scale; 1.3953 + this.rippleElement_.style.webkitTransform = transformString; 1.3954 + this.rippleElement_.style.msTransform = transformString; 1.3955 + this.rippleElement_.style.transform = transformString; 1.3956 + if (start) { 1.3957 + this.rippleElement_.classList.remove(this.CssClasses_.IS_ANIMATING); 1.3958 + } else { 1.3959 + this.rippleElement_.classList.add(this.CssClasses_.IS_ANIMATING); 1.3960 + } 1.3961 + } 1.3962 + }; 1.3963 + /** 1.3964 + * Handles an animation frame. 1.3965 + */ 1.3966 + this.animFrameHandler = function () { 1.3967 + if (this.frameCount_-- > 0) { 1.3968 + window.requestAnimationFrame(this.animFrameHandler.bind(this)); 1.3969 + } else { 1.3970 + this.setRippleStyles(false); 1.3971 + } 1.3972 + }; 1.3973 + } 1.3974 + } 1.3975 +}; 1.3976 +// The component registers itself. It can assume componentHandler is available 1.3977 +// in the global scope. 1.3978 +componentHandler.register({ 1.3979 + constructor: MaterialRipple, 1.3980 + classAsString: 'MaterialRipple', 1.3981 + cssClass: 'mdl-js-ripple-effect', 1.3982 + widget: false 1.3983 +}); 1.3984 +}());