material_design_rails 0.0.1

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