material_design_lite-sass 1.0.1

Sign up to get free protection for your applications and to get access to all the features.
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
+ });