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