material_design_lite-sass 1.0.2.1 → 1.0.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (38) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +6 -0
  3. data/lib/material_design_lite/sass/version.rb +1 -1
  4. data/vendor/assets/javascripts/material.js +2562 -2993
  5. data/vendor/assets/javascripts/material/button.js +114 -112
  6. data/vendor/assets/javascripts/material/checkbox.js +255 -260
  7. data/vendor/assets/javascripts/material/data-table.js +140 -124
  8. data/vendor/assets/javascripts/material/icon-toggle.js +239 -243
  9. data/vendor/assets/javascripts/material/layout.js +392 -388
  10. data/vendor/assets/javascripts/material/mdlComponentHandler.js +68 -27
  11. data/vendor/assets/javascripts/material/menu.js +430 -414
  12. data/vendor/assets/javascripts/material/progress.js +110 -97
  13. data/vendor/assets/javascripts/material/radio.js +244 -247
  14. data/vendor/assets/javascripts/material/ripple.js +220 -211
  15. data/vendor/assets/javascripts/material/slider.js +228 -228
  16. data/vendor/assets/javascripts/material/spinner.js +122 -119
  17. data/vendor/assets/javascripts/material/switch.js +246 -250
  18. data/vendor/assets/javascripts/material/tabs.js +129 -127
  19. data/vendor/assets/javascripts/material/textfield.js +221 -222
  20. data/vendor/assets/javascripts/material/tooltip.js +126 -122
  21. data/vendor/assets/stylesheets/material/_badge.scss +1 -1
  22. data/vendor/assets/stylesheets/material/_button.scss +15 -8
  23. data/vendor/assets/stylesheets/material/_card.scss +1 -1
  24. data/vendor/assets/stylesheets/material/_checkbox.scss +1 -1
  25. data/vendor/assets/stylesheets/material/_data-table.scss +5 -3
  26. data/vendor/assets/stylesheets/material/_functions.scss +16 -0
  27. data/vendor/assets/stylesheets/material/_grid.scss +11 -20
  28. data/vendor/assets/stylesheets/material/_layout.scss +7 -5
  29. data/vendor/assets/stylesheets/material/_menu.scss +4 -1
  30. data/vendor/assets/stylesheets/material/_radio.scss +1 -1
  31. data/vendor/assets/stylesheets/material/_slider.scss +1 -0
  32. data/vendor/assets/stylesheets/material/_switch.scss +1 -1
  33. data/vendor/assets/stylesheets/material/_tabs.scss +1 -1
  34. data/vendor/assets/stylesheets/material/_textfield.scss +15 -5
  35. data/vendor/assets/stylesheets/material/_tooltip.scss +2 -2
  36. data/vendor/assets/stylesheets/material/_variables.scss +18 -43
  37. data/vendor/assets/stylesheets/material/resets/_h5bp.scss +28 -21
  38. metadata +1 -1
@@ -19,23 +19,29 @@
19
19
  * A component handler interface using the revealing module design pattern.
20
20
  * More details on this design pattern here:
21
21
  * https://github.com/jasonmayes/mdl-component-design-pattern
22
+ *
22
23
  * @author Jason Mayes.
23
24
  */
24
25
  /* exported componentHandler */
25
- var componentHandler = (function() {
26
+ window.componentHandler = (function() {
26
27
  'use strict';
27
28
 
29
+ /** @type {!Array<componentHandler.ComponentConfig>} */
28
30
  var registeredComponents_ = [];
31
+
32
+ /** @type {!Array<componentHandler.Component>} */
29
33
  var createdComponents_ = [];
34
+
30
35
  var downgradeMethod_ = 'mdlDowngrade_';
31
36
  var componentConfigProperty_ = 'mdlComponentConfigInternal_';
32
37
 
33
38
  /**
34
39
  * Searches registered components for a class we are interested in using.
35
40
  * Optionally replaces a match with passed object if specified.
36
- * @param {string} name The name of a class we want to use.
37
- * @param {Object=} optReplace Optional object to replace match with.
38
- * @return {Object | boolean}
41
+ *
42
+ * @param {String} name The name of a class we want to use.
43
+ * @param {componentHandler.ComponentConfig=} optReplace Optional object to replace match with.
44
+ * @return {!Object|Boolean}
39
45
  * @private
40
46
  */
41
47
  function findRegisteredClass_(name, optReplace) {
@@ -52,8 +58,9 @@ var componentHandler = (function() {
52
58
 
53
59
  /**
54
60
  * Returns an array of the classNames of the upgraded classes on the element.
55
- * @param {HTMLElement} element The element to fetch data from.
56
- * @return {Array<string>}
61
+ *
62
+ * @param {!HTMLElement} element The element to fetch data from.
63
+ * @return {!Array<String>}
57
64
  * @private
58
65
  */
59
66
  function getUpgradedListOfElement_(element) {
@@ -65,9 +72,10 @@ var componentHandler = (function() {
65
72
  /**
66
73
  * Returns true if the given element has already been upgraded for the given
67
74
  * class.
68
- * @param {HTMLElement} element The element we want to check.
69
- * @param {string} jsClass The class to check for.
70
- * @return boolean
75
+ *
76
+ * @param {!HTMLElement} element The element we want to check.
77
+ * @param {String} jsClass The class to check for.
78
+ * @returns {Boolean}
71
79
  * @private
72
80
  */
73
81
  function isElementUpgraded_(element, jsClass) {
@@ -78,9 +86,10 @@ var componentHandler = (function() {
78
86
  /**
79
87
  * Searches existing DOM for elements of our component type and upgrades them
80
88
  * if they have not already been upgraded.
81
- * @param {!string=} optJsClass the programatic name of the element class we
89
+ *
90
+ * @param {String=} optJsClass the programatic name of the element class we
82
91
  * need to create a new instance of.
83
- * @param {!string=} optCssClass the name of the CSS class elements of this
92
+ * @param {String=} optCssClass the name of the CSS class elements of this
84
93
  * type will have.
85
94
  */
86
95
  function upgradeDomInternal(optJsClass, optCssClass) {
@@ -90,7 +99,7 @@ var componentHandler = (function() {
90
99
  registeredComponents_[i].cssClass);
91
100
  }
92
101
  } else {
93
- var jsClass = /** @type {!string} */ (optJsClass);
102
+ var jsClass = /** @type {String} */ (optJsClass);
94
103
  if (optCssClass === undefined) {
95
104
  var registeredClass = findRegisteredClass_(jsClass);
96
105
  if (registeredClass) {
@@ -107,8 +116,9 @@ var componentHandler = (function() {
107
116
 
108
117
  /**
109
118
  * Upgrades a specific element rather than all in the DOM.
110
- * @param {HTMLElement} element The element we wish to upgrade.
111
- * @param {!string=} optJsClass Optional name of the class we want to upgrade
119
+ *
120
+ * @param {!HTMLElement} element The element we wish to upgrade.
121
+ * @param {String=} optJsClass Optional name of the class we want to upgrade
112
122
  * the element to.
113
123
  */
114
124
  function upgradeElementInternal(element, optJsClass) {
@@ -122,7 +132,7 @@ var componentHandler = (function() {
122
132
  // ones matching the element's CSS classList.
123
133
  if (!optJsClass) {
124
134
  var classList = element.classList;
125
- registeredComponents_.forEach(function (component) {
135
+ registeredComponents_.forEach(function(component) {
126
136
  // Match CSS & Not to be upgraded & Not upgraded.
127
137
  if (classList.contains(component.cssClass) &&
128
138
  classesToUpgrade.indexOf(component) === -1 &&
@@ -166,13 +176,14 @@ var componentHandler = (function() {
166
176
 
167
177
  /**
168
178
  * Upgrades a specific list of elements rather than all in the DOM.
169
- * @param {HTMLElement | Array<HTMLElement> | NodeList | HTMLCollection} elements
179
+ *
180
+ * @param {!HTMLElement|!Array<!HTMLElement>|!NodeList|!HTMLCollection} elements
170
181
  * The elements we wish to upgrade.
171
182
  */
172
183
  function upgradeElementsInternal(elements) {
173
184
  if (!Array.isArray(elements)) {
174
185
  if (typeof elements.item === 'function') {
175
- elements = Array.prototype.slice.call(elements);
186
+ elements = Array.prototype.slice.call(/** @type {Array} */ (elements));
176
187
  } else {
177
188
  elements = [elements];
178
189
  }
@@ -190,17 +201,17 @@ var componentHandler = (function() {
190
201
 
191
202
  /**
192
203
  * Registers a class for future use and attempts to upgrade existing DOM.
193
- * @param {Object} config An object containing:
194
- * {constructor: Constructor, classAsString: string, cssClass: string}
204
+ *
205
+ * @param {{constructor: !Function, classAsString: String, cssClass: String, widget: String}} config
195
206
  */
196
207
  function registerInternal(config) {
197
- var newConfig = {
208
+ var newConfig = /** @type {componentHandler.ComponentConfig} */ ({
198
209
  'classConstructor': config.constructor,
199
210
  'className': config.classAsString,
200
211
  'cssClass': config.cssClass,
201
212
  'widget': config.widget === undefined ? true : config.widget,
202
213
  'callbacks': []
203
- };
214
+ });
204
215
 
205
216
  registeredComponents_.forEach(function(item) {
206
217
  if (item.cssClass === newConfig.cssClass) {
@@ -214,7 +225,7 @@ var componentHandler = (function() {
214
225
  if (config.constructor.prototype
215
226
  .hasOwnProperty(componentConfigProperty_)) {
216
227
  throw new Error(
217
- 'MDL component classes must not have ' + componentConfigProperty_ +
228
+ 'MDL component classes must not have ' + componentConfigProperty_ +
218
229
  ' defined as a property.');
219
230
  }
220
231
 
@@ -228,10 +239,12 @@ var componentHandler = (function() {
228
239
  /**
229
240
  * Allows user to be alerted to any upgrades that are performed for a given
230
241
  * component type
231
- * @param {string} jsClass The class name of the MDL component we wish
242
+ *
243
+ * @param {String} jsClass The class name of the MDL component we wish
232
244
  * to hook into for any upgrades performed.
233
- * @param {!Function} callback The function to call upon an upgrade. This
234
- * function should expect 1 parameter - the HTMLElement which got upgraded.
245
+ * @param {function(!HTMLElement)} callback The function to call upon an
246
+ * upgrade. This function should expect 1 parameter - the HTMLElement which
247
+ * got upgraded.
235
248
  */
236
249
  function registerUpgradedCallbackInternal(jsClass, callback) {
237
250
  var regClass = findRegisteredClass_(jsClass);
@@ -253,7 +266,7 @@ var componentHandler = (function() {
253
266
  /**
254
267
  * Finds a created component by a given DOM node.
255
268
  *
256
- * @param {!Element} node
269
+ * @param {!Node} node
257
270
  * @return {*}
258
271
  */
259
272
  function findCreatedComponentByNodeInternal(node) {
@@ -296,7 +309,7 @@ var componentHandler = (function() {
296
309
  /**
297
310
  * Downgrade either a given node, an array of nodes, or a NodeList.
298
311
  *
299
- * @param {*} nodes
312
+ * @param {!Node|!Array<!Node>|!NodeList} nodes
300
313
  */
301
314
  function downgradeNodesInternal(nodes) {
302
315
  var downgradeNode = function(node) {
@@ -344,3 +357,31 @@ window.addEventListener('load', function() {
344
357
  componentHandler.register = function() {};
345
358
  }
346
359
  });
360
+
361
+ /**
362
+ * Describes the type of a registered component type managed by
363
+ * componentHandler. Provided for benefit of the Closure compiler.
364
+ *
365
+ * @typedef {{
366
+ * constructor: !Function,
367
+ * className: String,
368
+ * cssClass: String,
369
+ * widget: String,
370
+ * callbacks: !Array<function(!HTMLElement)>
371
+ * }}
372
+ */
373
+ componentHandler.ComponentConfig; // jshint ignore:line
374
+
375
+ /**
376
+ * Created component (i.e., upgraded element) type as managed by
377
+ * componentHandler. Provided for benefit of the Closure compiler.
378
+ *
379
+ * @typedef {{
380
+ * element_: !HTMLElement,
381
+ * className: String,
382
+ * classAsString: String,
383
+ * cssClass: String,
384
+ * widget: String
385
+ * }}
386
+ */
387
+ componentHandler.Component; // jshint ignore:line
@@ -15,454 +15,470 @@
15
15
  * limitations under the License.
16
16
  */
17
17
 
18
- /**
19
- * Class constructor for dropdown MDL component.
20
- * Implements MDL component design pattern defined at:
21
- * https://github.com/jasonmayes/mdl-component-design-pattern
22
- * @param {HTMLElement} element The element that will be upgraded.
23
- */
24
- function MaterialMenu(element) {
18
+ (function() {
25
19
  'use strict';
26
20
 
27
- this.element_ = element;
21
+ /**
22
+ * Class constructor for dropdown MDL component.
23
+ * Implements MDL component design pattern defined at:
24
+ * https://github.com/jasonmayes/mdl-component-design-pattern
25
+ *
26
+ * @param {HTMLElement} element The element that will be upgraded.
27
+ */
28
+ var MaterialMenu = function MaterialMenu(element) {
29
+ this.element_ = element;
30
+
31
+ // Initialize instance.
32
+ this.init();
33
+ };
34
+ window.MaterialMenu = MaterialMenu;
35
+
36
+ /**
37
+ * Store constants in one place so they can be updated easily.
38
+ *
39
+ * @enum {String | Number}
40
+ * @private
41
+ */
42
+ MaterialMenu.prototype.Constant_ = {
43
+ // Total duration of the menu animation.
44
+ TRANSITION_DURATION_SECONDS: 0.3,
45
+ // The fraction of the total duration we want to use for menu item animations.
46
+ TRANSITION_DURATION_FRACTION: 0.8,
47
+ // How long the menu stays open after choosing an option (so the user can see
48
+ // the ripple).
49
+ CLOSE_TIMEOUT: 150
50
+ };
51
+
52
+ /**
53
+ * Keycodes, for code readability.
54
+ *
55
+ * @enum {Number}
56
+ * @private
57
+ */
58
+ MaterialMenu.prototype.Keycodes_ = {
59
+ ENTER: 13,
60
+ ESCAPE: 27,
61
+ SPACE: 32,
62
+ UP_ARROW: 38,
63
+ DOWN_ARROW: 40
64
+ };
65
+
66
+ /**
67
+ * Store strings for class names defined by this component that are used in
68
+ * JavaScript. This allows us to simply change it in one place should we
69
+ * decide to modify at a later date.
70
+ *
71
+ * @enum {String}
72
+ * @private
73
+ */
74
+ MaterialMenu.prototype.CssClasses_ = {
75
+ CONTAINER: 'mdl-menu__container',
76
+ OUTLINE: 'mdl-menu__outline',
77
+ ITEM: 'mdl-menu__item',
78
+ ITEM_RIPPLE_CONTAINER: 'mdl-menu__item-ripple-container',
79
+ RIPPLE_EFFECT: 'mdl-js-ripple-effect',
80
+ RIPPLE_IGNORE_EVENTS: 'mdl-js-ripple-effect--ignore-events',
81
+ RIPPLE: 'mdl-ripple',
82
+ // Statuses
83
+ IS_UPGRADED: 'is-upgraded',
84
+ IS_VISIBLE: 'is-visible',
85
+ IS_ANIMATING: 'is-animating',
86
+ // Alignment options
87
+ BOTTOM_LEFT: 'mdl-menu--bottom-left', // This is the default.
88
+ BOTTOM_RIGHT: 'mdl-menu--bottom-right',
89
+ TOP_LEFT: 'mdl-menu--top-left',
90
+ TOP_RIGHT: 'mdl-menu--top-right',
91
+ UNALIGNED: 'mdl-menu--unaligned'
92
+ };
93
+
94
+ /**
95
+ * Initialize element.
96
+ */
97
+ MaterialMenu.prototype.init = function() {
98
+ if (this.element_) {
99
+ // Create container for the menu.
100
+ var container = document.createElement('div');
101
+ container.classList.add(this.CssClasses_.CONTAINER);
102
+ this.element_.parentElement.insertBefore(container, this.element_);
103
+ this.element_.parentElement.removeChild(this.element_);
104
+ container.appendChild(this.element_);
105
+ this.container_ = container;
106
+
107
+ // Create outline for the menu (shadow and background).
108
+ var outline = document.createElement('div');
109
+ outline.classList.add(this.CssClasses_.OUTLINE);
110
+ this.outline_ = outline;
111
+ container.insertBefore(outline, this.element_);
112
+
113
+ // Find the "for" element and bind events to it.
114
+ var forElId = this.element_.getAttribute('for');
115
+ var forEl = null;
116
+ if (forElId) {
117
+ forEl = document.getElementById(forElId);
118
+ if (forEl) {
119
+ this.forElement_ = forEl;
120
+ forEl.addEventListener('click', this.handleForClick_.bind(this));
121
+ forEl.addEventListener('keydown',
122
+ this.handleForKeyboardEvent_.bind(this));
123
+ }
124
+ }
28
125
 
29
- // Initialize instance.
30
- this.init();
31
- }
126
+ var items = this.element_.querySelectorAll('.' + this.CssClasses_.ITEM);
127
+ this.boundItemKeydown = this.handleItemKeyboardEvent_.bind(this);
128
+ this.boundItemClick = this.handleItemClick_.bind(this);
129
+ for (var i = 0; i < items.length; i++) {
130
+ // Add a listener to each menu item.
131
+ items[i].addEventListener('click', this.boundItemClick);
132
+ // Add a tab index to each menu item.
133
+ items[i].tabIndex = '-1';
134
+ // Add a keyboard listener to each menu item.
135
+ items[i].addEventListener('keydown', this.boundItemKeydown);
136
+ }
32
137
 
33
- /**
34
- * Store constants in one place so they can be updated easily.
35
- * @enum {string | number}
36
- * @private
37
- */
38
- MaterialMenu.prototype.Constant_ = {
39
- // Total duration of the menu animation.
40
- TRANSITION_DURATION_SECONDS: 0.3,
41
- // The fraction of the total duration we want to use for menu item animations.
42
- TRANSITION_DURATION_FRACTION: 0.8,
43
- // How long the menu stays open after choosing an option (so the user can see
44
- // the ripple).
45
- CLOSE_TIMEOUT: 150
46
- };
138
+ // Add ripple classes to each item, if the user has enabled ripples.
139
+ if (this.element_.classList.contains(this.CssClasses_.RIPPLE_EFFECT)) {
140
+ this.element_.classList.add(this.CssClasses_.RIPPLE_IGNORE_EVENTS);
47
141
 
48
- /**
49
- * Keycodes, for code readability.
50
- * @enum {number}
51
- * @private
52
- */
53
- MaterialMenu.prototype.Keycodes_ = {
54
- ENTER: 13,
55
- ESCAPE: 27,
56
- SPACE: 32,
57
- UP_ARROW: 38,
58
- DOWN_ARROW: 40
59
- };
142
+ for (i = 0; i < items.length; i++) {
143
+ var item = items[i];
60
144
 
61
- /**
62
- * Store strings for class names defined by this component that are used in
63
- * JavaScript. This allows us to simply change it in one place should we
64
- * decide to modify at a later date.
65
- * @enum {string}
66
- * @private
67
- */
68
- MaterialMenu.prototype.CssClasses_ = {
69
- CONTAINER: 'mdl-menu__container',
70
- OUTLINE: 'mdl-menu__outline',
71
- ITEM: 'mdl-menu__item',
72
- ITEM_RIPPLE_CONTAINER: 'mdl-menu__item-ripple-container',
73
- RIPPLE_EFFECT: 'mdl-js-ripple-effect',
74
- RIPPLE_IGNORE_EVENTS: 'mdl-js-ripple-effect--ignore-events',
75
- RIPPLE: 'mdl-ripple',
76
- // Statuses
77
- IS_UPGRADED: 'is-upgraded',
78
- IS_VISIBLE: 'is-visible',
79
- IS_ANIMATING: 'is-animating',
80
- // Alignment options
81
- BOTTOM_LEFT: 'mdl-menu--bottom-left', // This is the default.
82
- BOTTOM_RIGHT: 'mdl-menu--bottom-right',
83
- TOP_LEFT: 'mdl-menu--top-left',
84
- TOP_RIGHT: 'mdl-menu--top-right',
85
- UNALIGNED: 'mdl-menu--unaligned'
86
- };
145
+ var rippleContainer = document.createElement('span');
146
+ rippleContainer.classList.add(this.CssClasses_.ITEM_RIPPLE_CONTAINER);
87
147
 
88
- /**
89
- * Initialize element.
90
- */
91
- MaterialMenu.prototype.init = function() {
92
- 'use strict';
148
+ var ripple = document.createElement('span');
149
+ ripple.classList.add(this.CssClasses_.RIPPLE);
150
+ rippleContainer.appendChild(ripple);
93
151
 
94
- if (this.element_) {
95
- // Create container for the menu.
96
- var container = document.createElement('div');
97
- container.classList.add(this.CssClasses_.CONTAINER);
98
- this.element_.parentElement.insertBefore(container, this.element_);
99
- this.element_.parentElement.removeChild(this.element_);
100
- container.appendChild(this.element_);
101
- this.container_ = container;
102
-
103
- // Create outline for the menu (shadow and background).
104
- var outline = document.createElement('div');
105
- outline.classList.add(this.CssClasses_.OUTLINE);
106
- this.outline_ = outline;
107
- container.insertBefore(outline, this.element_);
108
-
109
- // Find the "for" element and bind events to it.
110
- var forElId = this.element_.getAttribute('for');
111
- var forEl = null;
112
- if (forElId) {
113
- forEl = document.getElementById(forElId);
114
- if (forEl) {
115
- this.forElement_ = forEl;
116
- forEl.addEventListener('click', this.handleForClick_.bind(this));
117
- forEl.addEventListener('keydown',
118
- this.handleForKeyboardEvent_.bind(this));
152
+ item.appendChild(rippleContainer);
153
+ item.classList.add(this.CssClasses_.RIPPLE_EFFECT);
154
+ }
119
155
  }
120
- }
121
156
 
122
- var items = this.element_.querySelectorAll('.' + this.CssClasses_.ITEM);
157
+ // Copy alignment classes to the container, so the outline can use them.
158
+ if (this.element_.classList.contains(this.CssClasses_.BOTTOM_LEFT)) {
159
+ this.outline_.classList.add(this.CssClasses_.BOTTOM_LEFT);
160
+ }
161
+ if (this.element_.classList.contains(this.CssClasses_.BOTTOM_RIGHT)) {
162
+ this.outline_.classList.add(this.CssClasses_.BOTTOM_RIGHT);
163
+ }
164
+ if (this.element_.classList.contains(this.CssClasses_.TOP_LEFT)) {
165
+ this.outline_.classList.add(this.CssClasses_.TOP_LEFT);
166
+ }
167
+ if (this.element_.classList.contains(this.CssClasses_.TOP_RIGHT)) {
168
+ this.outline_.classList.add(this.CssClasses_.TOP_RIGHT);
169
+ }
170
+ if (this.element_.classList.contains(this.CssClasses_.UNALIGNED)) {
171
+ this.outline_.classList.add(this.CssClasses_.UNALIGNED);
172
+ }
123
173
 
124
- for (var i = 0; i < items.length; i++) {
125
- // Add a listener to each menu item.
126
- items[i].addEventListener('click', this.handleItemClick_.bind(this));
127
- // Add a tab index to each menu item.
128
- items[i].tabIndex = '-1';
129
- // Add a keyboard listener to each menu item.
130
- items[i].addEventListener('keydown',
131
- this.handleItemKeyboardEvent_.bind(this));
174
+ container.classList.add(this.CssClasses_.IS_UPGRADED);
132
175
  }
133
-
134
- // Add ripple classes to each item, if the user has enabled ripples.
135
- if (this.element_.classList.contains(this.CssClasses_.RIPPLE_EFFECT)) {
136
- this.element_.classList.add(this.CssClasses_.RIPPLE_IGNORE_EVENTS);
137
-
138
- for (i = 0; i < items.length; i++) {
139
- var item = items[i];
140
-
141
- var rippleContainer = document.createElement('span');
142
- rippleContainer.classList.add(this.CssClasses_.ITEM_RIPPLE_CONTAINER);
143
-
144
- var ripple = document.createElement('span');
145
- ripple.classList.add(this.CssClasses_.RIPPLE);
146
- rippleContainer.appendChild(ripple);
147
-
148
- item.appendChild(rippleContainer);
149
- item.classList.add(this.CssClasses_.RIPPLE_EFFECT);
176
+ };
177
+
178
+ /**
179
+ * Handles a click on the "for" element, by positioning the menu and then
180
+ * toggling it.
181
+ *
182
+ * @param {Event} evt The event that fired.
183
+ * @private
184
+ */
185
+ MaterialMenu.prototype.handleForClick_ = function(evt) {
186
+ if (this.element_ && this.forElement_) {
187
+ var rect = this.forElement_.getBoundingClientRect();
188
+ var forRect = this.forElement_.parentElement.getBoundingClientRect();
189
+
190
+ if (this.element_.classList.contains(this.CssClasses_.UNALIGNED)) {
191
+ // Do not position the menu automatically. Requires the developer to
192
+ // manually specify position.
193
+ } else if (this.element_.classList.contains(
194
+ this.CssClasses_.BOTTOM_RIGHT)) {
195
+ // Position below the "for" element, aligned to its right.
196
+ this.container_.style.right = (forRect.right - rect.right) + 'px';
197
+ this.container_.style.top =
198
+ this.forElement_.offsetTop + this.forElement_.offsetHeight + 'px';
199
+ } else if (this.element_.classList.contains(this.CssClasses_.TOP_LEFT)) {
200
+ // Position above the "for" element, aligned to its left.
201
+ this.container_.style.left = this.forElement_.offsetLeft + 'px';
202
+ this.container_.style.bottom = (forRect.bottom - rect.top) + 'px';
203
+ } else if (this.element_.classList.contains(this.CssClasses_.TOP_RIGHT)) {
204
+ // Position above the "for" element, aligned to its right.
205
+ this.container_.style.right = (forRect.right - rect.right) + 'px';
206
+ this.container_.style.bottom = (forRect.bottom - rect.top) + 'px';
207
+ } else {
208
+ // Default: position below the "for" element, aligned to its left.
209
+ this.container_.style.left = this.forElement_.offsetLeft + 'px';
210
+ this.container_.style.top =
211
+ this.forElement_.offsetTop + this.forElement_.offsetHeight + 'px';
150
212
  }
151
213
  }
152
214
 
153
- // Copy alignment classes to the container, so the outline can use them.
154
- if (this.element_.classList.contains(this.CssClasses_.BOTTOM_LEFT)) {
155
- this.outline_.classList.add(this.CssClasses_.BOTTOM_LEFT);
156
- }
157
- if (this.element_.classList.contains(this.CssClasses_.BOTTOM_RIGHT)) {
158
- this.outline_.classList.add(this.CssClasses_.BOTTOM_RIGHT);
215
+ this.toggle(evt);
216
+ };
217
+
218
+ /**
219
+ * Handles a keyboard event on the "for" element.
220
+ *
221
+ * @param {Event} evt The event that fired.
222
+ * @private
223
+ */
224
+ MaterialMenu.prototype.handleForKeyboardEvent_ = function(evt) {
225
+ if (this.element_ && this.container_ && this.forElement_) {
226
+ var items = this.element_.querySelectorAll('.' + this.CssClasses_.ITEM +
227
+ ':not([disabled])');
228
+
229
+ if (items && items.length > 0 &&
230
+ this.container_.classList.contains(this.CssClasses_.IS_VISIBLE)) {
231
+ if (evt.keyCode === this.Keycodes_.UP_ARROW) {
232
+ evt.preventDefault();
233
+ items[items.length - 1].focus();
234
+ } else if (evt.keyCode === this.Keycodes_.DOWN_ARROW) {
235
+ evt.preventDefault();
236
+ items[0].focus();
237
+ }
238
+ }
159
239
  }
160
- if (this.element_.classList.contains(this.CssClasses_.TOP_LEFT)) {
161
- this.outline_.classList.add(this.CssClasses_.TOP_LEFT);
240
+ };
241
+
242
+ /**
243
+ * Handles a keyboard event on an item.
244
+ *
245
+ * @param {Event} evt The event that fired.
246
+ * @private
247
+ */
248
+ MaterialMenu.prototype.handleItemKeyboardEvent_ = function(evt) {
249
+ if (this.element_ && this.container_) {
250
+ var items = this.element_.querySelectorAll('.' + this.CssClasses_.ITEM +
251
+ ':not([disabled])');
252
+
253
+ if (items && items.length > 0 &&
254
+ this.container_.classList.contains(this.CssClasses_.IS_VISIBLE)) {
255
+ var currentIndex = Array.prototype.slice.call(items).indexOf(evt.target);
256
+
257
+ if (evt.keyCode === this.Keycodes_.UP_ARROW) {
258
+ evt.preventDefault();
259
+ if (currentIndex > 0) {
260
+ items[currentIndex - 1].focus();
261
+ } else {
262
+ items[items.length - 1].focus();
263
+ }
264
+ } else if (evt.keyCode === this.Keycodes_.DOWN_ARROW) {
265
+ evt.preventDefault();
266
+ if (items.length > currentIndex + 1) {
267
+ items[currentIndex + 1].focus();
268
+ } else {
269
+ items[0].focus();
270
+ }
271
+ } else if (evt.keyCode === this.Keycodes_.SPACE ||
272
+ evt.keyCode === this.Keycodes_.ENTER) {
273
+ evt.preventDefault();
274
+ // Send mousedown and mouseup to trigger ripple.
275
+ var e = new MouseEvent('mousedown');
276
+ evt.target.dispatchEvent(e);
277
+ e = new MouseEvent('mouseup');
278
+ evt.target.dispatchEvent(e);
279
+ // Send click.
280
+ evt.target.click();
281
+ } else if (evt.keyCode === this.Keycodes_.ESCAPE) {
282
+ evt.preventDefault();
283
+ this.hide();
284
+ }
285
+ }
162
286
  }
163
- if (this.element_.classList.contains(this.CssClasses_.TOP_RIGHT)) {
164
- this.outline_.classList.add(this.CssClasses_.TOP_RIGHT);
287
+ };
288
+
289
+ /**
290
+ * Handles a click event on an item.
291
+ *
292
+ * @param {Event} evt The event that fired.
293
+ * @private
294
+ */
295
+ MaterialMenu.prototype.handleItemClick_ = function(evt) {
296
+ if (evt.target.getAttribute('disabled') !== null) {
297
+ evt.stopPropagation();
298
+ } else {
299
+ // Wait some time before closing menu, so the user can see the ripple.
300
+ this.closing_ = true;
301
+ window.setTimeout(function(evt) {
302
+ this.hide();
303
+ this.closing_ = false;
304
+ }.bind(this), this.Constant_.CLOSE_TIMEOUT);
165
305
  }
306
+ };
307
+
308
+ /**
309
+ * Calculates the initial clip (for opening the menu) or final clip (for closing
310
+ * it), and applies it. This allows us to animate from or to the correct point,
311
+ * that is, the point it's aligned to in the "for" element.
312
+ *
313
+ * @param {Number} height Height of the clip rectangle
314
+ * @param {Number} width Width of the clip rectangle
315
+ * @private
316
+ */
317
+ MaterialMenu.prototype.applyClip_ = function(height, width) {
166
318
  if (this.element_.classList.contains(this.CssClasses_.UNALIGNED)) {
167
- this.outline_.classList.add(this.CssClasses_.UNALIGNED);
168
- }
169
-
170
- container.classList.add(this.CssClasses_.IS_UPGRADED);
171
- }
172
- };
173
-
174
- /**
175
- * Handles a click on the "for" element, by positioning the menu and then
176
- * toggling it.
177
- * @private
178
- */
179
- MaterialMenu.prototype.handleForClick_ = function(evt) {
180
- 'use strict';
181
-
182
- if (this.element_ && this.forElement_) {
183
- var rect = this.forElement_.getBoundingClientRect();
184
- var forRect = this.forElement_.parentElement.getBoundingClientRect();
185
-
186
- if (this.element_.classList.contains(this.CssClasses_.UNALIGNED)) {
187
- // Do not position the menu automatically. Requires the developer to
188
- // manually specify position.
189
- } else if (this.element_.classList.contains(
190
- this.CssClasses_.BOTTOM_RIGHT)) {
191
- // Position below the "for" element, aligned to its right.
192
- this.container_.style.right = (forRect.right - rect.right) + 'px';
193
- this.container_.style.top =
194
- this.forElement_.offsetTop + this.forElement_.offsetHeight + 'px';
319
+ // Do not clip.
320
+ this.element_.style.clip = null;
321
+ } else if (this.element_.classList.contains(this.CssClasses_.BOTTOM_RIGHT)) {
322
+ // Clip to the top right corner of the menu.
323
+ this.element_.style.clip =
324
+ 'rect(0 ' + width + 'px ' + '0 ' + width + 'px)';
195
325
  } else if (this.element_.classList.contains(this.CssClasses_.TOP_LEFT)) {
196
- // Position above the "for" element, aligned to its left.
197
- this.container_.style.left = this.forElement_.offsetLeft + 'px';
198
- this.container_.style.bottom = (forRect.bottom - rect.top) + 'px';
326
+ // Clip to the bottom left corner of the menu.
327
+ this.element_.style.clip =
328
+ 'rect(' + height + 'px 0 ' + height + 'px 0)';
199
329
  } else if (this.element_.classList.contains(this.CssClasses_.TOP_RIGHT)) {
200
- // Position above the "for" element, aligned to its right.
201
- this.container_.style.right = (forRect.right - rect.right) + 'px';
202
- this.container_.style.bottom = (forRect.bottom - rect.top) + 'px';
330
+ // Clip to the bottom right corner of the menu.
331
+ this.element_.style.clip = 'rect(' + height + 'px ' + width + 'px ' +
332
+ height + 'px ' + width + 'px)';
203
333
  } else {
204
- // Default: position below the "for" element, aligned to its left.
205
- this.container_.style.left = this.forElement_.offsetLeft + 'px';
206
- this.container_.style.top =
207
- this.forElement_.offsetTop + this.forElement_.offsetHeight + 'px';
208
- }
209
- }
210
-
211
- this.toggle(evt);
212
- };
213
-
214
- /**
215
- * Handles a keyboard event on the "for" element.
216
- * @private
217
- */
218
- MaterialMenu.prototype.handleForKeyboardEvent_ = function(evt) {
219
- 'use strict';
220
-
221
- if (this.element_ && this.container_ && this.forElement_) {
222
- var items = this.element_.querySelectorAll('.' + this.CssClasses_.ITEM +
223
- ':not([disabled])');
224
-
225
- if (items && items.length > 0 &&
226
- this.container_.classList.contains(this.CssClasses_.IS_VISIBLE)) {
227
- if (evt.keyCode === this.Keycodes_.UP_ARROW) {
228
- evt.preventDefault();
229
- items[items.length - 1].focus();
230
- } else if (evt.keyCode === this.Keycodes_.DOWN_ARROW) {
231
- evt.preventDefault();
232
- items[0].focus();
233
- }
334
+ // Default: do not clip (same as clipping to the top left corner).
335
+ this.element_.style.clip = null;
234
336
  }
235
- }
236
- };
237
-
238
- /**
239
- * Handles a keyboard event on an item.
240
- * @private
241
- */
242
- MaterialMenu.prototype.handleItemKeyboardEvent_ = function(evt) {
243
- 'use strict';
244
-
245
- if (this.element_ && this.container_) {
246
- var items = this.element_.querySelectorAll('.' + this.CssClasses_.ITEM +
247
- ':not([disabled])');
248
-
249
- if (items && items.length > 0 &&
250
- this.container_.classList.contains(this.CssClasses_.IS_VISIBLE)) {
251
- var currentIndex = Array.prototype.slice.call(items).indexOf(evt.target);
337
+ };
338
+
339
+ /**
340
+ * Adds an event listener to clean up after the animation ends.
341
+ *
342
+ * @private
343
+ */
344
+ MaterialMenu.prototype.addAnimationEndListener_ = function() {
345
+ var cleanup = function() {
346
+ this.element_.removeEventListener('transitionend', cleanup);
347
+ this.element_.removeEventListener('webkitTransitionEnd', cleanup);
348
+ this.element_.classList.remove(this.CssClasses_.IS_ANIMATING);
349
+ }.bind(this);
252
350
 
253
- if (evt.keyCode === this.Keycodes_.UP_ARROW) {
254
- evt.preventDefault();
255
- if (currentIndex > 0) {
256
- items[currentIndex - 1].focus();
257
- } else {
258
- items[items.length - 1].focus();
259
- }
260
- } else if (evt.keyCode === this.Keycodes_.DOWN_ARROW) {
261
- evt.preventDefault();
262
- if (items.length > currentIndex + 1) {
263
- items[currentIndex + 1].focus();
351
+ // Remove animation class once the transition is done.
352
+ this.element_.addEventListener('transitionend', cleanup);
353
+ this.element_.addEventListener('webkitTransitionEnd', cleanup);
354
+ };
355
+
356
+ /**
357
+ * Displays the menu.
358
+ *
359
+ * @public
360
+ */
361
+ MaterialMenu.prototype.show = function(evt) {
362
+ if (this.element_ && this.container_ && this.outline_) {
363
+ // Measure the inner element.
364
+ var height = this.element_.getBoundingClientRect().height;
365
+ var width = this.element_.getBoundingClientRect().width;
366
+
367
+ // Apply the inner element's size to the container and outline.
368
+ this.container_.style.width = width + 'px';
369
+ this.container_.style.height = height + 'px';
370
+ this.outline_.style.width = width + 'px';
371
+ this.outline_.style.height = height + 'px';
372
+
373
+ var transitionDuration = this.Constant_.TRANSITION_DURATION_SECONDS *
374
+ this.Constant_.TRANSITION_DURATION_FRACTION;
375
+
376
+ // Calculate transition delays for individual menu items, so that they fade
377
+ // in one at a time.
378
+ var items = this.element_.querySelectorAll('.' + this.CssClasses_.ITEM);
379
+ for (var i = 0; i < items.length; i++) {
380
+ var itemDelay = null;
381
+ if (this.element_.classList.contains(this.CssClasses_.TOP_LEFT) ||
382
+ this.element_.classList.contains(this.CssClasses_.TOP_RIGHT)) {
383
+ itemDelay = ((height - items[i].offsetTop - items[i].offsetHeight) /
384
+ height * transitionDuration) + 's';
264
385
  } else {
265
- items[0].focus();
386
+ itemDelay = (items[i].offsetTop / height * transitionDuration) + 's';
266
387
  }
267
- } else if (evt.keyCode === this.Keycodes_.SPACE ||
268
- evt.keyCode === this.Keycodes_.ENTER) {
269
- evt.preventDefault();
270
- // Send mousedown and mouseup to trigger ripple.
271
- var e = new MouseEvent('mousedown');
272
- evt.target.dispatchEvent(e);
273
- e = new MouseEvent('mouseup');
274
- evt.target.dispatchEvent(e);
275
- // Send click.
276
- evt.target.click();
277
- } else if (evt.keyCode === this.Keycodes_.ESCAPE) {
278
- evt.preventDefault();
279
- this.hide();
388
+ items[i].style.transitionDelay = itemDelay;
280
389
  }
281
- }
282
- }
283
- };
284
-
285
- /**
286
- * Handles a click event on an item.
287
- * @private
288
- */
289
- MaterialMenu.prototype.handleItemClick_ = function(evt) {
290
- 'use strict';
291
-
292
- if (evt.target.getAttribute('disabled') !== null) {
293
- evt.stopPropagation();
294
- } else {
295
- // Wait some time before closing menu, so the user can see the ripple.
296
- this.closing_ = true;
297
- window.setTimeout(function(evt) {
298
- this.hide();
299
- this.closing_ = false;
300
- }.bind(this), this.Constant_.CLOSE_TIMEOUT);
301
- }
302
- };
303
-
304
- /**
305
- * Calculates the initial clip (for opening the menu) or final clip (for closing
306
- * it), and applies it. This allows us to animate from or to the correct point,
307
- * that is, the point it's aligned to in the "for" element.
308
- * @private
309
- */
310
- MaterialMenu.prototype.applyClip_ = function(height, width) {
311
- 'use strict';
312
-
313
- if (this.element_.classList.contains(this.CssClasses_.UNALIGNED)) {
314
- // Do not clip.
315
- this.element_.style.clip = null;
316
- } else if (this.element_.classList.contains(this.CssClasses_.BOTTOM_RIGHT)) {
317
- // Clip to the top right corner of the menu.
318
- this.element_.style.clip =
319
- 'rect(0 ' + width + 'px ' + '0 ' + width + 'px)';
320
- } else if (this.element_.classList.contains(this.CssClasses_.TOP_LEFT)) {
321
- // Clip to the bottom left corner of the menu.
322
- this.element_.style.clip =
323
- 'rect(' + height + 'px 0 ' + height + 'px 0)';
324
- } else if (this.element_.classList.contains(this.CssClasses_.TOP_RIGHT)) {
325
- // Clip to the bottom right corner of the menu.
326
- this.element_.style.clip = 'rect(' + height + 'px ' + width + 'px ' +
327
- height + 'px ' + width + 'px)';
328
- } else {
329
- // Default: do not clip (same as clipping to the top left corner).
330
- this.element_.style.clip = null;
331
- }
332
- };
333
-
334
- /**
335
- * Adds an event listener to clean up after the animation ends.
336
- * @private
337
- */
338
- MaterialMenu.prototype.addAnimationEndListener_ = function() {
339
- 'use strict';
340
-
341
- var cleanup = function () {
342
- this.element_.removeEventListener('transitionend', cleanup);
343
- this.element_.removeEventListener('webkitTransitionEnd', cleanup);
344
- this.element_.classList.remove(this.CssClasses_.IS_ANIMATING);
345
- }.bind(this);
346
-
347
- // Remove animation class once the transition is done.
348
- this.element_.addEventListener('transitionend', cleanup);
349
- this.element_.addEventListener('webkitTransitionEnd', cleanup);
350
- };
351
-
352
- /**
353
- * Displays the menu.
354
- * @public
355
- */
356
- MaterialMenu.prototype.show = function(evt) {
357
- 'use strict';
358
-
359
- if (this.element_ && this.container_ && this.outline_) {
360
- // Measure the inner element.
361
- var height = this.element_.getBoundingClientRect().height;
362
- var width = this.element_.getBoundingClientRect().width;
363
-
364
- // Apply the inner element's size to the container and outline.
365
- this.container_.style.width = width + 'px';
366
- this.container_.style.height = height + 'px';
367
- this.outline_.style.width = width + 'px';
368
- this.outline_.style.height = height + 'px';
369
-
370
- var transitionDuration = this.Constant_.TRANSITION_DURATION_SECONDS *
371
- this.Constant_.TRANSITION_DURATION_FRACTION;
372
390
 
373
- // Calculate transition delays for individual menu items, so that they fade
374
- // in one at a time.
375
- var items = this.element_.querySelectorAll('.' + this.CssClasses_.ITEM);
376
- for (var i = 0; i < items.length; i++) {
377
- var itemDelay = null;
378
- if (this.element_.classList.contains(this.CssClasses_.TOP_LEFT) ||
379
- this.element_.classList.contains(this.CssClasses_.TOP_RIGHT)) {
380
- itemDelay = ((height - items[i].offsetTop - items[i].offsetHeight) /
381
- height * transitionDuration) + 's';
382
- } else {
383
- itemDelay = (items[i].offsetTop / height * transitionDuration) + 's';
384
- }
385
- items[i].style.transitionDelay = itemDelay;
391
+ // Apply the initial clip to the text before we start animating.
392
+ this.applyClip_(height, width);
393
+
394
+ // Wait for the next frame, turn on animation, and apply the final clip.
395
+ // Also make it visible. This triggers the transitions.
396
+ window.requestAnimationFrame(function() {
397
+ this.element_.classList.add(this.CssClasses_.IS_ANIMATING);
398
+ this.element_.style.clip = 'rect(0 ' + width + 'px ' + height + 'px 0)';
399
+ this.container_.classList.add(this.CssClasses_.IS_VISIBLE);
400
+ }.bind(this));
401
+
402
+ // Clean up after the animation is complete.
403
+ this.addAnimationEndListener_();
404
+
405
+ // Add a click listener to the document, to close the menu.
406
+ var callback = function(e) {
407
+ // Check to see if the document is processing the same event that
408
+ // displayed the menu in the first place. If so, do nothing.
409
+ // Also check to see if the menu is in the process of closing itself, and
410
+ // do nothing in that case.
411
+ if (e !== evt && !this.closing_) {
412
+ document.removeEventListener('click', callback);
413
+ this.hide();
414
+ }
415
+ }.bind(this);
416
+ document.addEventListener('click', callback);
386
417
  }
418
+ };
419
+
420
+ /**
421
+ * Hides the menu.
422
+ *
423
+ * @public
424
+ */
425
+ MaterialMenu.prototype.hide = function() {
426
+ if (this.element_ && this.container_ && this.outline_) {
427
+ var items = this.element_.querySelectorAll('.' + this.CssClasses_.ITEM);
428
+
429
+ // Remove all transition delays; menu items fade out concurrently.
430
+ for (var i = 0; i < items.length; i++) {
431
+ items[i].style.transitionDelay = null;
432
+ }
387
433
 
388
- // Apply the initial clip to the text before we start animating.
389
- this.applyClip_(height, width);
434
+ // Measure the inner element.
435
+ var height = this.element_.getBoundingClientRect().height;
436
+ var width = this.element_.getBoundingClientRect().width;
390
437
 
391
- // Wait for the next frame, turn on animation, and apply the final clip.
392
- // Also make it visible. This triggers the transitions.
393
- window.requestAnimationFrame(function() {
438
+ // Turn on animation, and apply the final clip. Also make invisible.
439
+ // This triggers the transitions.
394
440
  this.element_.classList.add(this.CssClasses_.IS_ANIMATING);
395
- this.element_.style.clip = 'rect(0 ' + width + 'px ' + height + 'px 0)';
396
- this.container_.classList.add(this.CssClasses_.IS_VISIBLE);
397
- }.bind(this));
398
-
399
- // Clean up after the animation is complete.
400
- this.addAnimationEndListener_();
401
-
402
- // Add a click listener to the document, to close the menu.
403
- var callback = function(e) {
404
- // Check to see if the document is processing the same event that
405
- // displayed the menu in the first place. If so, do nothing.
406
- // Also check to see if the menu is in the process of closing itself, and
407
- // do nothing in that case.
408
- if (e !== evt && !this.closing_) {
409
- document.removeEventListener('click', callback);
410
- this.hide();
411
- }
412
- }.bind(this);
413
- document.addEventListener('click', callback);
414
- }
415
- };
441
+ this.applyClip_(height, width);
442
+ this.container_.classList.remove(this.CssClasses_.IS_VISIBLE);
416
443
 
417
- /**
418
- * Hides the menu.
419
- * @public
420
- */
421
- MaterialMenu.prototype.hide = function() {
422
- 'use strict';
423
-
424
- if (this.element_ && this.container_ && this.outline_) {
444
+ // Clean up after the animation is complete.
445
+ this.addAnimationEndListener_();
446
+ }
447
+ };
448
+
449
+ /**
450
+ * Displays or hides the menu, depending on current state.
451
+ *
452
+ * @public
453
+ */
454
+ MaterialMenu.prototype.toggle = function(evt) {
455
+ if (this.container_.classList.contains(this.CssClasses_.IS_VISIBLE)) {
456
+ this.hide();
457
+ } else {
458
+ this.show(evt);
459
+ }
460
+ };
461
+
462
+ /**
463
+ * Downgrade the component.
464
+ *
465
+ * @private
466
+ */
467
+ MaterialMenu.prototype.mdlDowngrade_ = function() {
425
468
  var items = this.element_.querySelectorAll('.' + this.CssClasses_.ITEM);
426
469
 
427
- // Remove all transition delays; menu items fade out concurrently.
428
470
  for (var i = 0; i < items.length; i++) {
429
- items[i].style.transitionDelay = null;
471
+ items[i].removeEventListener('click', this.boundItemClick);
472
+ items[i].removeEventListener('keydown', this.boundItemKeydown);
430
473
  }
431
-
432
- // Measure the inner element.
433
- var height = this.element_.getBoundingClientRect().height;
434
- var width = this.element_.getBoundingClientRect().width;
435
-
436
- // Turn on animation, and apply the final clip. Also make invisible.
437
- // This triggers the transitions.
438
- this.element_.classList.add(this.CssClasses_.IS_ANIMATING);
439
- this.applyClip_(height, width);
440
- this.container_.classList.remove(this.CssClasses_.IS_VISIBLE);
441
-
442
- // Clean up after the animation is complete.
443
- this.addAnimationEndListener_();
444
- }
445
- };
446
-
447
- /**
448
- * Displays or hides the menu, depending on current state.
449
- * @public
450
- */
451
- MaterialMenu.prototype.toggle = function(evt) {
452
- 'use strict';
453
-
454
- if (this.container_.classList.contains(this.CssClasses_.IS_VISIBLE)) {
455
- this.hide();
456
- } else {
457
- this.show(evt);
458
- }
459
- };
460
-
461
- // The component registers itself. It can assume componentHandler is available
462
- // in the global scope.
463
- componentHandler.register({
464
- constructor: MaterialMenu,
465
- classAsString: 'MaterialMenu',
466
- cssClass: 'mdl-js-menu',
467
- widget: true
468
- });
474
+ };
475
+
476
+ // The component registers itself. It can assume componentHandler is available
477
+ // in the global scope.
478
+ componentHandler.register({
479
+ constructor: MaterialMenu,
480
+ classAsString: 'MaterialMenu',
481
+ cssClass: 'mdl-js-menu',
482
+ widget: true
483
+ });
484
+ })();