getmdl_rails 0.1.0 → 0.2.0

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