material-design-rails 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: ac94530f1297860deaa23a7ade840f506b65929f
4
+ data.tar.gz: 5aaac39664de92d91b95492c93ca7d09724b17f6
5
+ SHA512:
6
+ metadata.gz: aae27b9a576f9e6b69449136ff426790a2f48332bcd6e176aa3ae7c42ba99fabdb3a10448bfd85455bd1e0b65d032e2bfe203b10b76b222640a15be36883684e
7
+ data.tar.gz: 68cfba32f7c52d06693627ebecb7311195aae3d13c2bc80abd13657fd6966f87351ab451f6f331c7254f13d37842fc72f29f617bee238c468216393a84e7e343
@@ -0,0 +1,10 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
10
+ .DS_Store
@@ -0,0 +1,13 @@
1
+ # Contributor Code of Conduct
2
+
3
+ As contributors and maintainers of this project, we pledge to respect all people who contribute through reporting issues, posting feature requests, updating documentation, submitting pull requests or patches, and other activities.
4
+
5
+ We are committed to making participation in this project a harassment-free experience for everyone, regardless of level of experience, gender, gender identity and expression, sexual orientation, disability, personal appearance, body size, race, age, or religion.
6
+
7
+ Examples of unacceptable behavior by participants include the use of sexual language or imagery, derogatory comments or personal attacks, trolling, public or private harassment, insults, or other unprofessional conduct.
8
+
9
+ Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct. Project maintainers who do not follow the Code of Conduct may be removed from the project team.
10
+
11
+ Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by opening an issue or contacting one or more of the project maintainers.
12
+
13
+ This Code of Conduct is adapted from the [Contributor Covenant](http:contributor-covenant.org), version 1.0.0, available at [http://contributor-covenant.org/version/1/0/0/](http://contributor-covenant.org/version/1/0/0/)
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
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2015 Zaiste
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
@@ -0,0 +1,53 @@
1
+ # material-design-rails
2
+
3
+ ## Installation
4
+
5
+ Add this line to your application's Gemfile:
6
+
7
+ ```ruby
8
+ gem 'material-design-rails'
9
+ ```
10
+
11
+ And then execute:
12
+
13
+ $ bundle
14
+
15
+ Or install it yourself as:
16
+
17
+ $ gem install material-design-rails
18
+
19
+ ## Usage
20
+
21
+ ### JavaScript
22
+
23
+ Inside your `application.js` add
24
+
25
+ ```
26
+ *= require material
27
+ ```
28
+
29
+ **or**, for minified version
30
+
31
+ ```
32
+ *= require material.min
33
+ ```
34
+
35
+ ### Stylesheets
36
+
37
+ Inside your `application.css` add
38
+
39
+ ```
40
+ *= require material
41
+ ```
42
+
43
+ **or**, for minified version
44
+
45
+ ```
46
+ *= require material.min
47
+ ```
48
+
49
+ You can also use SASS's `@import` in your `application.scss`, i.e.
50
+
51
+ ```
52
+ @import "material.min";
53
+ ```
@@ -0,0 +1 @@
1
+ require "bundler/gem_tasks"
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "bundler/setup"
4
+ require "material/design/rails"
5
+
6
+ # You can add fixtures and/or initialization code here to make experimenting
7
+ # with your gem easier. You can also use a different console, if you like.
8
+
9
+ # (If you use this, don't forget to add pry to your Gemfile!)
10
+ # require "pry"
11
+ # Pry.start
12
+
13
+ require "irb"
14
+ IRB.start
@@ -0,0 +1,7 @@
1
+ #!/bin/bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+
5
+ bundle install
6
+
7
+ # Do any other automated setup that you need to do here
@@ -0,0 +1,10 @@
1
+ require "material/design/rails/version"
2
+
3
+ module Material
4
+ module Design
5
+ module Rails
6
+ class Engine < ::Rails::Engine
7
+ end
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,7 @@
1
+ module Material
2
+ module Design
3
+ module Rails
4
+ VERSION = "0.1.0"
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,24 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'material/design/rails/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "material-design-rails"
8
+ spec.version = Material::Design::Rails::VERSION
9
+ spec.authors = ["Zaiste"]
10
+ spec.email = ["oh@zaiste.net"]
11
+
12
+ spec.summary = %q{Material Design Lite for Rails}
13
+ spec.description = %q{Material Design Lite for Rails}
14
+ spec.homepage = "https://github.com/zaiste/material-design-rails"
15
+ spec.license = "MIT"
16
+
17
+ spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
18
+ spec.bindir = "exe"
19
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
20
+ spec.require_paths = ["lib"]
21
+
22
+ spec.add_development_dependency "bundler", "~> 1.9"
23
+ spec.add_development_dependency "rake", "~> 10.0"
24
+ end
@@ -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
+ });