hanami-material 0.0.1

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