material_design_lite-sass 1.0.1

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