material_design_lite-rails 1.0.6 → 1.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 888cacd8f6ca6e422d87a623ff031eb0b497d830
4
- data.tar.gz: c6bbd3d84b83cf63d57c47c84e2f6bde31df80cb
3
+ metadata.gz: 42187789a96342b466a9a7fd07f3a14bdd98b438
4
+ data.tar.gz: 38e32557bf16437a6c615b79e1fdf71cbf58a873
5
5
  SHA512:
6
- metadata.gz: 571acdc71bb6a3a72f800fd8652a8986b3922cc00af796e906bbee3b153f2b0f62773ef4728d57b7b08ab7c62b02b4181d7a382f76488e96ed1466297c02027c
7
- data.tar.gz: e7872119f04d7d3d3654da3f5f1b88ebd2820af465869f0f8fc6b306f4fb13a4a7a3a38db8c28b965ffc8123de436ca6b9fed5b422625c7697027187f7648cfd
6
+ metadata.gz: ea222e995c019f868218cf9b2549a4c3e46d72497a69adca3ff7ef4730e959faa920b721f7c2ca7d059d626ce761c8dc2dd1050ca3f9a67dd7e04ad51aff5cb3
7
+ data.tar.gz: c76f3b0e97f81845ae95a000759f62a6afdd8a28f198f6d480ef0d579e84e647e4a98884ad5152fcdedcd04eb743a4ad55ce9fc9eccefcff604b3c14e67d7279
@@ -1,5 +1,5 @@
1
1
  module MaterialDesignLite
2
2
  module Rails
3
- VERSION = "1.0.6"
3
+ VERSION = "1.1.0"
4
4
  end
5
5
  end
@@ -94,7 +94,6 @@ componentHandler = (function() {
94
94
  /** @type {!Array<componentHandler.Component>} */
95
95
  var createdComponents_ = [];
96
96
 
97
- var downgradeMethod_ = 'mdlDowngrade';
98
97
  var componentConfigProperty_ = 'mdlComponentConfigInternal_';
99
98
 
100
99
  /**
@@ -228,7 +227,7 @@ componentHandler = (function() {
228
227
  }
229
228
  } else {
230
229
  throw new Error(
231
- 'Unable to find a registered component for the given class.');
230
+ 'Unable to find a registered component for the given class.');
232
231
  }
233
232
 
234
233
  var ev = document.createEvent('Events');
@@ -338,47 +337,25 @@ componentHandler = (function() {
338
337
  }
339
338
  }
340
339
 
341
- /**
342
- * Finds a created component by a given DOM node.
343
- *
344
- * @param {!Node} node
345
- * @return {*}
346
- */
347
- function findCreatedComponentByNodeInternal(node) {
348
- for (var n = 0; n < createdComponents_.length; n++) {
349
- var component = createdComponents_[n];
350
- if (component.element_ === node) {
351
- return component;
352
- }
353
- }
354
- }
355
-
356
340
  /**
357
341
  * Check the component for the downgrade method.
358
342
  * Execute if found.
359
343
  * Remove component from createdComponents list.
360
344
  *
361
- * @param {*} component
345
+ * @param {?componentHandler.Component} component
362
346
  */
363
347
  function deconstructComponentInternal(component) {
364
- if (component &&
365
- component[componentConfigProperty_]
366
- .classConstructor.prototype
367
- .hasOwnProperty(downgradeMethod_)) {
368
- component[downgradeMethod_]();
369
- var componentIndex = createdComponents_.indexOf(component);
370
- createdComponents_.splice(componentIndex, 1);
348
+ var componentIndex = createdComponents_.indexOf(component);
349
+ createdComponents_.splice(componentIndex, 1);
371
350
 
372
- var upgrades = component.element_.getAttribute('data-upgraded').split(',');
373
- var componentPlace = upgrades.indexOf(
374
- component[componentConfigProperty_].classAsString);
375
- upgrades.splice(componentPlace, 1);
376
- component.element_.setAttribute('data-upgraded', upgrades.join(','));
351
+ var upgrades = component.element_.getAttribute('data-upgraded').split(',');
352
+ var componentPlace = upgrades.indexOf(component[componentConfigProperty_].classAsString);
353
+ upgrades.splice(componentPlace, 1);
354
+ component.element_.setAttribute('data-upgraded', upgrades.join(','));
377
355
 
378
- var ev = document.createEvent('Events');
379
- ev.initEvent('mdl-componentdowngraded', true, true);
380
- component.element_.dispatchEvent(ev);
381
- }
356
+ var ev = document.createEvent('Events');
357
+ ev.initEvent('mdl-componentdowngraded', true, true);
358
+ component.element_.dispatchEvent(ev);
382
359
  }
383
360
 
384
361
  /**
@@ -392,7 +369,9 @@ componentHandler = (function() {
392
369
  * @param {!Node} node the node to be downgraded
393
370
  */
394
371
  var downgradeNode = function(node) {
395
- deconstructComponentInternal(findCreatedComponentByNodeInternal(node));
372
+ createdComponents_.filter(function(item) {
373
+ return item.element_ === node;
374
+ }).forEach(deconstructComponentInternal);
396
375
  };
397
376
  if (nodes instanceof Array || nodes instanceof NodeList) {
398
377
  for (var n = 0; n < nodes.length; n++) {
@@ -642,25 +621,6 @@ MaterialButton.prototype.init = function () {
642
621
  this.element_.addEventListener('mouseleave', this.boundButtonBlurHandler);
643
622
  }
644
623
  };
645
- /**
646
- * Downgrade the element.
647
- *
648
- * @private
649
- */
650
- MaterialButton.prototype.mdlDowngrade_ = function () {
651
- if (this.rippleElement_) {
652
- this.rippleElement_.removeEventListener('mouseup', this.boundRippleBlurHandler);
653
- }
654
- this.element_.removeEventListener('mouseup', this.boundButtonBlurHandler);
655
- this.element_.removeEventListener('mouseleave', this.boundButtonBlurHandler);
656
- };
657
- /**
658
- * Public alias for the downgrade method.
659
- *
660
- * @public
661
- */
662
- MaterialButton.prototype.mdlDowngrade = MaterialButton.prototype.mdlDowngrade_;
663
- MaterialButton.prototype['mdlDowngrade'] = MaterialButton.prototype.mdlDowngrade;
664
624
  // The component registers itself. It can assume componentHandler is available
665
625
  // in the global scope.
666
626
  componentHandler.register({
@@ -893,27 +853,6 @@ MaterialCheckbox.prototype.init = function () {
893
853
  this.element_.classList.add(this.CssClasses_.IS_UPGRADED);
894
854
  }
895
855
  };
896
- /**
897
- * Downgrade the component.
898
- *
899
- * @private
900
- */
901
- MaterialCheckbox.prototype.mdlDowngrade_ = function () {
902
- if (this.rippleContainerElement_) {
903
- this.rippleContainerElement_.removeEventListener('mouseup', this.boundRippleMouseUp);
904
- }
905
- this.inputElement_.removeEventListener('change', this.boundInputOnChange);
906
- this.inputElement_.removeEventListener('focus', this.boundInputOnFocus);
907
- this.inputElement_.removeEventListener('blur', this.boundInputOnBlur);
908
- this.element_.removeEventListener('mouseup', this.boundElementMouseUp);
909
- };
910
- /**
911
- * Public alias for the downgrade method.
912
- *
913
- * @public
914
- */
915
- MaterialCheckbox.prototype.mdlDowngrade = MaterialCheckbox.prototype.mdlDowngrade_;
916
- MaterialCheckbox.prototype['mdlDowngrade'] = MaterialCheckbox.prototype.mdlDowngrade;
917
856
  // The component registers itself. It can assume componentHandler is available
918
857
  // in the global scope.
919
858
  componentHandler.register({
@@ -1133,27 +1072,6 @@ MaterialIconToggle.prototype.init = function () {
1133
1072
  this.element_.classList.add('is-upgraded');
1134
1073
  }
1135
1074
  };
1136
- /**
1137
- * Downgrade the component
1138
- *
1139
- * @private
1140
- */
1141
- MaterialIconToggle.prototype.mdlDowngrade_ = function () {
1142
- if (this.rippleContainerElement_) {
1143
- this.rippleContainerElement_.removeEventListener('mouseup', this.boundRippleMouseUp);
1144
- }
1145
- this.inputElement_.removeEventListener('change', this.boundInputOnChange);
1146
- this.inputElement_.removeEventListener('focus', this.boundInputOnFocus);
1147
- this.inputElement_.removeEventListener('blur', this.boundInputOnBlur);
1148
- this.element_.removeEventListener('mouseup', this.boundElementOnMouseUp);
1149
- };
1150
- /**
1151
- * Public alias for the downgrade method.
1152
- *
1153
- * @public
1154
- */
1155
- MaterialIconToggle.prototype.mdlDowngrade = MaterialIconToggle.prototype.mdlDowngrade_;
1156
- MaterialIconToggle.prototype['mdlDowngrade'] = MaterialIconToggle.prototype.mdlDowngrade;
1157
1075
  // The component registers itself. It can assume componentHandler is available
1158
1076
  // in the global scope.
1159
1077
  componentHandler.register({
@@ -1266,7 +1184,7 @@ MaterialMenu.prototype.init = function () {
1266
1184
  this.outline_ = outline;
1267
1185
  container.insertBefore(outline, this.element_);
1268
1186
  // Find the "for" element and bind events to it.
1269
- var forElId = this.element_.getAttribute('for');
1187
+ var forElId = this.element_.getAttribute('for') || this.element_.getAttribute('data-mdl-for');
1270
1188
  var forEl = null;
1271
1189
  if (forElId) {
1272
1190
  forEl = document.getElementById(forElId);
@@ -1458,20 +1376,23 @@ MaterialMenu.prototype.applyClip_ = function (height, width) {
1458
1376
  this.element_.style.clip = '';
1459
1377
  }
1460
1378
  };
1379
+ /**
1380
+ * Cleanup function to remove animation listeners.
1381
+ *
1382
+ * @param {Event} evt
1383
+ * @private
1384
+ */
1385
+ MaterialMenu.prototype.removeAnimationEndListener_ = function (evt) {
1386
+ evt.target.classList.remove(MaterialMenu.prototype.CssClasses_.IS_ANIMATING);
1387
+ };
1461
1388
  /**
1462
1389
  * Adds an event listener to clean up after the animation ends.
1463
1390
  *
1464
1391
  * @private
1465
1392
  */
1466
1393
  MaterialMenu.prototype.addAnimationEndListener_ = function () {
1467
- var cleanup = function () {
1468
- this.element_.removeEventListener('transitionend', cleanup);
1469
- this.element_.removeEventListener('webkitTransitionEnd', cleanup);
1470
- this.element_.classList.remove(this.CssClasses_.IS_ANIMATING);
1471
- }.bind(this);
1472
- // Remove animation class once the transition is done.
1473
- this.element_.addEventListener('transitionend', cleanup);
1474
- this.element_.addEventListener('webkitTransitionEnd', cleanup);
1394
+ this.element_.addEventListener('transitionend', this.removeAnimationEndListener_);
1395
+ this.element_.addEventListener('webkitTransitionEnd', this.removeAnimationEndListener_);
1475
1396
  };
1476
1397
  /**
1477
1398
  * Displays the menu.
@@ -1539,7 +1460,7 @@ MaterialMenu.prototype.hide = function () {
1539
1460
  var items = this.element_.querySelectorAll('.' + this.CssClasses_.ITEM);
1540
1461
  // Remove all transition delays; menu items fade out concurrently.
1541
1462
  for (var i = 0; i < items.length; i++) {
1542
- items[i].style.transitionDelay = null;
1463
+ items[i].style.removeProperty('transition-delay');
1543
1464
  }
1544
1465
  // Measure the inner element.
1545
1466
  var rect = this.element_.getBoundingClientRect();
@@ -1568,25 +1489,6 @@ MaterialMenu.prototype.toggle = function (evt) {
1568
1489
  }
1569
1490
  };
1570
1491
  MaterialMenu.prototype['toggle'] = MaterialMenu.prototype.toggle;
1571
- /**
1572
- * Downgrade the component.
1573
- *
1574
- * @private
1575
- */
1576
- MaterialMenu.prototype.mdlDowngrade_ = function () {
1577
- var items = this.element_.querySelectorAll('.' + this.CssClasses_.ITEM);
1578
- for (var i = 0; i < items.length; i++) {
1579
- items[i].removeEventListener('click', this.boundItemClick_);
1580
- items[i].removeEventListener('keydown', this.boundItemKeydown_);
1581
- }
1582
- };
1583
- /**
1584
- * Public alias for the downgrade method.
1585
- *
1586
- * @public
1587
- */
1588
- MaterialMenu.prototype.mdlDowngrade = MaterialMenu.prototype.mdlDowngrade_;
1589
- MaterialMenu.prototype['mdlDowngrade'] = MaterialMenu.prototype.mdlDowngrade;
1590
1492
  // The component registers itself. It can assume componentHandler is available
1591
1493
  // in the global scope.
1592
1494
  componentHandler.register({
@@ -1688,23 +1590,6 @@ MaterialProgress.prototype.init = function () {
1688
1590
  this.element_.classList.add('is-upgraded');
1689
1591
  }
1690
1592
  };
1691
- /**
1692
- * Downgrade the component
1693
- *
1694
- * @private
1695
- */
1696
- MaterialProgress.prototype.mdlDowngrade_ = function () {
1697
- while (this.element_.firstChild) {
1698
- this.element_.removeChild(this.element_.firstChild);
1699
- }
1700
- };
1701
- /**
1702
- * Public alias for the downgrade method.
1703
- *
1704
- * @public
1705
- */
1706
- MaterialProgress.prototype.mdlDowngrade = MaterialProgress.prototype.mdlDowngrade_;
1707
- MaterialProgress.prototype['mdlDowngrade'] = MaterialProgress.prototype.mdlDowngrade;
1708
1593
  // The component registers itself. It can assume componentHandler is available
1709
1594
  // in the global scope.
1710
1595
  componentHandler.register({
@@ -1943,29 +1828,6 @@ MaterialRadio.prototype.init = function () {
1943
1828
  this.element_.classList.add(this.CssClasses_.IS_UPGRADED);
1944
1829
  }
1945
1830
  };
1946
- /**
1947
- * Downgrade the element.
1948
- *
1949
- * @private
1950
- */
1951
- MaterialRadio.prototype.mdlDowngrade_ = function () {
1952
- var rippleContainer = this.element_.querySelector('.' + this.CssClasses_.RIPPLE_CONTAINER);
1953
- this.btnElement_.removeEventListener('change', this.boundChangeHandler_);
1954
- this.btnElement_.removeEventListener('focus', this.boundFocusHandler_);
1955
- this.btnElement_.removeEventListener('blur', this.boundBlurHandler_);
1956
- this.element_.removeEventListener('mouseup', this.boundMouseUpHandler_);
1957
- if (rippleContainer) {
1958
- rippleContainer.removeEventListener('mouseup', this.boundMouseUpHandler_);
1959
- this.element_.removeChild(rippleContainer);
1960
- }
1961
- };
1962
- /**
1963
- * Public alias for the downgrade method.
1964
- *
1965
- * @public
1966
- */
1967
- MaterialRadio.prototype.mdlDowngrade = MaterialRadio.prototype.mdlDowngrade_;
1968
- MaterialRadio.prototype['mdlDowngrade'] = MaterialRadio.prototype.mdlDowngrade;
1969
1831
  // The component registers itself. It can assume componentHandler is available
1970
1832
  // in the global scope.
1971
1833
  componentHandler.register({
@@ -2181,30 +2043,184 @@ MaterialSlider.prototype.init = function () {
2181
2043
  this.element_.classList.add(this.CssClasses_.IS_UPGRADED);
2182
2044
  }
2183
2045
  };
2046
+ // The component registers itself. It can assume componentHandler is available
2047
+ // in the global scope.
2048
+ componentHandler.register({
2049
+ constructor: MaterialSlider,
2050
+ classAsString: 'MaterialSlider',
2051
+ cssClass: 'mdl-js-slider',
2052
+ widget: true
2053
+ });
2054
+ /**
2055
+ * Copyright 2015 Google Inc. All Rights Reserved.
2056
+ *
2057
+ * Licensed under the Apache License, Version 2.0 (the "License");
2058
+ * you may not use this file except in compliance with the License.
2059
+ * You may obtain a copy of the License at
2060
+ *
2061
+ * http://www.apache.org/licenses/LICENSE-2.0
2062
+ *
2063
+ * Unless required by applicable law or agreed to in writing, software
2064
+ * distributed under the License is distributed on an "AS IS" BASIS,
2065
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
2066
+ * See the License for the specific language governing permissions and
2067
+ * limitations under the License.
2068
+ */
2184
2069
  /**
2185
- * Downgrade the component
2070
+ * Class constructor for Snackbar MDL component.
2071
+ * Implements MDL component design pattern defined at:
2072
+ * https://github.com/jasonmayes/mdl-component-design-pattern
2186
2073
  *
2074
+ * @constructor
2075
+ * @param {HTMLElement} element The element that will be upgraded.
2076
+ */
2077
+ var MaterialSnackbar = function MaterialSnackbar(element) {
2078
+ this.element_ = element;
2079
+ this.textElement_ = this.element_.querySelector('.' + this.cssClasses_.MESSAGE);
2080
+ this.actionElement_ = this.element_.querySelector('.' + this.cssClasses_.ACTION);
2081
+ if (!this.textElement_) {
2082
+ throw new Error('There must be a message element for a snackbar.');
2083
+ }
2084
+ if (!this.actionElement_) {
2085
+ throw new Error('There must be an action element for a snackbar.');
2086
+ }
2087
+ this.active = false;
2088
+ this.actionHandler_ = undefined;
2089
+ this.message_ = undefined;
2090
+ this.actionText_ = undefined;
2091
+ this.queuedNotifications_ = [];
2092
+ this.setActionHidden_(true);
2093
+ };
2094
+ window['MaterialSnackbar'] = MaterialSnackbar;
2095
+ /**
2096
+ * Store constants in one place so they can be updated easily.
2097
+ *
2098
+ * @enum {string | number}
2187
2099
  * @private
2188
2100
  */
2189
- MaterialSlider.prototype.mdlDowngrade_ = function () {
2190
- this.element_.removeEventListener('input', this.boundInputHandler);
2191
- this.element_.removeEventListener('change', this.boundChangeHandler);
2192
- this.element_.removeEventListener('mouseup', this.boundMouseUpHandler);
2193
- this.element_.parentElement.removeEventListener('mousedown', this.boundContainerMouseDownHandler);
2101
+ MaterialSnackbar.prototype.Constant_ = {
2102
+ // The duration of the snackbar show/hide animation, in ms.
2103
+ ANIMATION_LENGTH: 250
2194
2104
  };
2195
2105
  /**
2196
- * Public alias for the downgrade method.
2106
+ * Store strings for class names defined by this component that are used in
2107
+ * JavaScript. This allows us to simply change it in one place should we
2108
+ * decide to modify at a later date.
2197
2109
  *
2110
+ * @enum {string}
2111
+ * @private
2112
+ */
2113
+ MaterialSnackbar.prototype.cssClasses_ = {
2114
+ SNACKBAR: 'mdl-snackbar',
2115
+ MESSAGE: 'mdl-snackbar__text',
2116
+ ACTION: 'mdl-snackbar__action',
2117
+ ACTIVE: 'mdl-snackbar--active'
2118
+ };
2119
+ /**
2120
+ * Display the snackbar.
2121
+ *
2122
+ * @private
2123
+ */
2124
+ MaterialSnackbar.prototype.displaySnackbar_ = function () {
2125
+ this.element_.setAttribute('aria-hidden', 'true');
2126
+ if (this.actionHandler_) {
2127
+ this.actionElement_.textContent = this.actionText_;
2128
+ this.actionElement_.addEventListener('click', this.actionHandler_);
2129
+ this.setActionHidden_(false);
2130
+ }
2131
+ this.textElement_.textContent = this.message_;
2132
+ this.element_.classList.add(this.cssClasses_.ACTIVE);
2133
+ this.element_.setAttribute('aria-hidden', 'false');
2134
+ setTimeout(this.cleanup_.bind(this), this.timeout_);
2135
+ };
2136
+ /**
2137
+ * Show the snackbar.
2138
+ *
2139
+ * @param {Object} data The data for the notification.
2198
2140
  * @public
2199
2141
  */
2200
- MaterialSlider.prototype.mdlDowngrade = MaterialSlider.prototype.mdlDowngrade_;
2201
- MaterialSlider.prototype['mdlDowngrade'] = MaterialSlider.prototype.mdlDowngrade;
2142
+ MaterialSnackbar.prototype.showSnackbar = function (data) {
2143
+ if (data === undefined) {
2144
+ throw new Error('Please provide a data object with at least a message to display.');
2145
+ }
2146
+ if (data['message'] === undefined) {
2147
+ throw new Error('Please provide a message to be displayed.');
2148
+ }
2149
+ if (data['actionHandler'] && !data['actionText']) {
2150
+ throw new Error('Please provide action text with the handler.');
2151
+ }
2152
+ if (this.active) {
2153
+ this.queuedNotifications_.push(data);
2154
+ } else {
2155
+ this.active = true;
2156
+ this.message_ = data['message'];
2157
+ if (data['timeout']) {
2158
+ this.timeout_ = data['timeout'];
2159
+ } else {
2160
+ this.timeout_ = 2750;
2161
+ }
2162
+ if (data['actionHandler']) {
2163
+ this.actionHandler_ = data['actionHandler'];
2164
+ }
2165
+ if (data['actionText']) {
2166
+ this.actionText_ = data['actionText'];
2167
+ }
2168
+ this.displaySnackbar_();
2169
+ }
2170
+ };
2171
+ MaterialSnackbar.prototype['showSnackbar'] = MaterialSnackbar.prototype.showSnackbar;
2172
+ /**
2173
+ * Check if the queue has items within it.
2174
+ * If it does, display the next entry.
2175
+ *
2176
+ * @private
2177
+ */
2178
+ MaterialSnackbar.prototype.checkQueue_ = function () {
2179
+ if (this.queuedNotifications_.length > 0) {
2180
+ this.showSnackbar(this.queuedNotifications_.shift());
2181
+ }
2182
+ };
2183
+ /**
2184
+ * Cleanup the snackbar event listeners and accessiblity attributes.
2185
+ *
2186
+ * @private
2187
+ */
2188
+ MaterialSnackbar.prototype.cleanup_ = function () {
2189
+ this.element_.classList.remove(this.cssClasses_.ACTIVE);
2190
+ setTimeout(function () {
2191
+ this.element_.setAttribute('aria-hidden', 'true');
2192
+ this.textElement_.textContent = '';
2193
+ if (!Boolean(this.actionElement_.getAttribute('aria-hidden'))) {
2194
+ this.setActionHidden_(true);
2195
+ this.actionElement_.textContent = '';
2196
+ this.actionElement_.removeEventListener('click', this.actionHandler_);
2197
+ }
2198
+ this.actionHandler_ = undefined;
2199
+ this.message_ = undefined;
2200
+ this.actionText_ = undefined;
2201
+ this.active = false;
2202
+ this.checkQueue_();
2203
+ }.bind(this), this.Constant_.ANIMATION_LENGTH);
2204
+ };
2205
+ /**
2206
+ * Set the action handler hidden state.
2207
+ *
2208
+ * @param {boolean} value
2209
+ * @private
2210
+ */
2211
+ MaterialSnackbar.prototype.setActionHidden_ = function (value) {
2212
+ if (value) {
2213
+ this.actionElement_.setAttribute('aria-hidden', 'true');
2214
+ } else {
2215
+ this.actionElement_.removeAttribute('aria-hidden');
2216
+ }
2217
+ };
2202
2218
  // The component registers itself. It can assume componentHandler is available
2203
2219
  // in the global scope.
2204
2220
  componentHandler.register({
2205
- constructor: MaterialSlider,
2206
- classAsString: 'MaterialSlider',
2207
- cssClass: 'mdl-js-slider',
2221
+ constructor: MaterialSnackbar,
2222
+ classAsString: 'MaterialSnackbar',
2223
+ cssClass: 'mdl-js-snackbar',
2208
2224
  widget: true
2209
2225
  });
2210
2226
  /**
@@ -2556,27 +2572,6 @@ MaterialSwitch.prototype.init = function () {
2556
2572
  this.element_.classList.add('is-upgraded');
2557
2573
  }
2558
2574
  };
2559
- /**
2560
- * Downgrade the component.
2561
- *
2562
- * @private
2563
- */
2564
- MaterialSwitch.prototype.mdlDowngrade_ = function () {
2565
- if (this.rippleContainerElement_) {
2566
- this.rippleContainerElement_.removeEventListener('mouseup', this.boundMouseUpHandler);
2567
- }
2568
- this.inputElement_.removeEventListener('change', this.boundChangeHandler);
2569
- this.inputElement_.removeEventListener('focus', this.boundFocusHandler);
2570
- this.inputElement_.removeEventListener('blur', this.boundBlurHandler);
2571
- this.element_.removeEventListener('mouseup', this.boundMouseUpHandler);
2572
- };
2573
- /**
2574
- * Public alias for the downgrade method.
2575
- *
2576
- * @public
2577
- */
2578
- MaterialSwitch.prototype.mdlDowngrade = MaterialSwitch.prototype.mdlDowngrade_;
2579
- MaterialSwitch.prototype['mdlDowngrade'] = MaterialSwitch.prototype.mdlDowngrade;
2580
2575
  // The component registers itself. It can assume componentHandler is available
2581
2576
  // in the global scope.
2582
2577
  componentHandler.register({
@@ -2607,7 +2602,7 @@ componentHandler.register({
2607
2602
  * https://github.com/jasonmayes/mdl-component-design-pattern
2608
2603
  *
2609
2604
  * @constructor
2610
- * @param {HTMLElement} element The element that will be upgraded.
2605
+ * @param {Element} element The element that will be upgraded.
2611
2606
  */
2612
2607
  var MaterialTabs = function MaterialTabs(element) {
2613
2608
  // Stores the HTML element.
@@ -2691,7 +2686,7 @@ MaterialTabs.prototype.init = function () {
2691
2686
  * Constructor for an individual tab.
2692
2687
  *
2693
2688
  * @constructor
2694
- * @param {HTMLElement} tab The HTML element for the tab.
2689
+ * @param {Element} tab The HTML element for the tab.
2695
2690
  * @param {MaterialTabs} ctx The MaterialTabs object that owns the tab.
2696
2691
  */
2697
2692
  function MaterialTab(tab, ctx) {
@@ -2813,6 +2808,15 @@ MaterialTextfield.prototype.onFocus_ = function (event) {
2813
2808
  MaterialTextfield.prototype.onBlur_ = function (event) {
2814
2809
  this.element_.classList.remove(this.CssClasses_.IS_FOCUSED);
2815
2810
  };
2811
+ /**
2812
+ * Handle reset event from out side.
2813
+ *
2814
+ * @param {Event} event The event that fired.
2815
+ * @private
2816
+ */
2817
+ MaterialTextfield.prototype.onReset_ = function (event) {
2818
+ this.updateClasses_();
2819
+ };
2816
2820
  /**
2817
2821
  * Handle class updates.
2818
2822
  *
@@ -2822,6 +2826,7 @@ MaterialTextfield.prototype.updateClasses_ = function () {
2822
2826
  this.checkDisabled();
2823
2827
  this.checkValidity();
2824
2828
  this.checkDirty();
2829
+ this.checkFocus();
2825
2830
  };
2826
2831
  // Public methods.
2827
2832
  /**
@@ -2837,6 +2842,19 @@ MaterialTextfield.prototype.checkDisabled = function () {
2837
2842
  }
2838
2843
  };
2839
2844
  MaterialTextfield.prototype['checkDisabled'] = MaterialTextfield.prototype.checkDisabled;
2845
+ /**
2846
+ * Check the focus state and update field accordingly.
2847
+ *
2848
+ * @public
2849
+ */
2850
+ MaterialTextfield.prototype.checkFocus = function () {
2851
+ if (Boolean(this.element_.querySelector(':focus'))) {
2852
+ this.element_.classList.add(this.CssClasses_.IS_FOCUSED);
2853
+ } else {
2854
+ this.element_.classList.remove(this.CssClasses_.IS_FOCUSED);
2855
+ }
2856
+ };
2857
+ MaterialTextfield.prototype['checkFocus'] = MaterialTextfield.prototype.checkFocus;
2840
2858
  /**
2841
2859
  * Check the validity state and update field accordingly.
2842
2860
  *
@@ -2913,9 +2931,11 @@ MaterialTextfield.prototype.init = function () {
2913
2931
  this.boundUpdateClassesHandler = this.updateClasses_.bind(this);
2914
2932
  this.boundFocusHandler = this.onFocus_.bind(this);
2915
2933
  this.boundBlurHandler = this.onBlur_.bind(this);
2934
+ this.boundResetHandler = this.onReset_.bind(this);
2916
2935
  this.input_.addEventListener('input', this.boundUpdateClassesHandler);
2917
2936
  this.input_.addEventListener('focus', this.boundFocusHandler);
2918
2937
  this.input_.addEventListener('blur', this.boundBlurHandler);
2938
+ this.input_.addEventListener('reset', this.boundResetHandler);
2919
2939
  if (this.maxRows !== this.Constant_.NO_MAX_ROWS) {
2920
2940
  // TODO: This should handle pasting multi line text.
2921
2941
  // Currently doesn't.
@@ -2928,29 +2948,13 @@ MaterialTextfield.prototype.init = function () {
2928
2948
  if (invalid) {
2929
2949
  this.element_.classList.add(this.CssClasses_.IS_INVALID);
2930
2950
  }
2951
+ if (this.input_.hasAttribute('autofocus')) {
2952
+ this.element_.focus();
2953
+ this.checkFocus();
2954
+ }
2931
2955
  }
2932
2956
  }
2933
2957
  };
2934
- /**
2935
- * Downgrade the component
2936
- *
2937
- * @private
2938
- */
2939
- MaterialTextfield.prototype.mdlDowngrade_ = function () {
2940
- this.input_.removeEventListener('input', this.boundUpdateClassesHandler);
2941
- this.input_.removeEventListener('focus', this.boundFocusHandler);
2942
- this.input_.removeEventListener('blur', this.boundBlurHandler);
2943
- if (this.boundKeyDownHandler) {
2944
- this.input_.removeEventListener('keydown', this.boundKeyDownHandler);
2945
- }
2946
- };
2947
- /**
2948
- * Public alias for the downgrade method.
2949
- *
2950
- * @public
2951
- */
2952
- MaterialTextfield.prototype.mdlDowngrade = MaterialTextfield.prototype.mdlDowngrade_;
2953
- MaterialTextfield.prototype['mdlDowngrade'] = MaterialTextfield.prototype.mdlDowngrade;
2954
2958
  // The component registers itself. It can assume componentHandler is available
2955
2959
  // in the global scope.
2956
2960
  componentHandler.register({
@@ -3004,7 +3008,13 @@ MaterialTooltip.prototype.Constant_ = {};
3004
3008
  * @enum {string}
3005
3009
  * @private
3006
3010
  */
3007
- MaterialTooltip.prototype.CssClasses_ = { IS_ACTIVE: 'is-active' };
3011
+ MaterialTooltip.prototype.CssClasses_ = {
3012
+ IS_ACTIVE: 'is-active',
3013
+ BOTTOM: 'mdl-tooltip--bottom',
3014
+ LEFT: 'mdl-tooltip--left',
3015
+ RIGHT: 'mdl-tooltip--right',
3016
+ TOP: 'mdl-tooltip--top'
3017
+ };
3008
3018
  /**
3009
3019
  * Handle mouseenter for tooltip.
3010
3020
  *
@@ -3012,33 +3022,47 @@ MaterialTooltip.prototype.CssClasses_ = { IS_ACTIVE: 'is-active' };
3012
3022
  * @private
3013
3023
  */
3014
3024
  MaterialTooltip.prototype.handleMouseEnter_ = function (event) {
3015
- event.stopPropagation();
3016
3025
  var props = event.target.getBoundingClientRect();
3017
3026
  var left = props.left + props.width / 2;
3027
+ var top = props.top + props.height / 2;
3018
3028
  var marginLeft = -1 * (this.element_.offsetWidth / 2);
3019
- if (left + marginLeft < 0) {
3020
- this.element_.style.left = 0;
3021
- this.element_.style.marginLeft = 0;
3029
+ var marginTop = -1 * (this.element_.offsetHeight / 2);
3030
+ if (this.element_.classList.contains(this.CssClasses_.LEFT) || this.element_.classList.contains(this.CssClasses_.RIGHT)) {
3031
+ left = props.width / 2;
3032
+ if (top + marginTop < 0) {
3033
+ this.element_.style.top = 0;
3034
+ this.element_.style.marginTop = 0;
3035
+ } else {
3036
+ this.element_.style.top = top + 'px';
3037
+ this.element_.style.marginTop = marginTop + 'px';
3038
+ }
3039
+ } else {
3040
+ if (left + marginLeft < 0) {
3041
+ this.element_.style.left = 0;
3042
+ this.element_.style.marginLeft = 0;
3043
+ } else {
3044
+ this.element_.style.left = left + 'px';
3045
+ this.element_.style.marginLeft = marginLeft + 'px';
3046
+ }
3047
+ }
3048
+ if (this.element_.classList.contains(this.CssClasses_.TOP)) {
3049
+ this.element_.style.top = props.top - this.element_.offsetHeight - 10 + 'px';
3050
+ } else if (this.element_.classList.contains(this.CssClasses_.RIGHT)) {
3051
+ this.element_.style.left = props.left + props.width + 10 + 'px';
3052
+ } else if (this.element_.classList.contains(this.CssClasses_.LEFT)) {
3053
+ this.element_.style.left = props.left - this.element_.offsetWidth - 10 + 'px';
3022
3054
  } else {
3023
- this.element_.style.left = left + 'px';
3024
- this.element_.style.marginLeft = marginLeft + 'px';
3055
+ this.element_.style.top = props.top + props.height + 10 + 'px';
3025
3056
  }
3026
- this.element_.style.top = props.top + props.height + 10 + 'px';
3027
3057
  this.element_.classList.add(this.CssClasses_.IS_ACTIVE);
3028
- window.addEventListener('scroll', this.boundMouseLeaveHandler, false);
3029
- window.addEventListener('touchmove', this.boundMouseLeaveHandler, false);
3030
3058
  };
3031
3059
  /**
3032
3060
  * Handle mouseleave for tooltip.
3033
3061
  *
3034
- * @param {Event} event The event that fired.
3035
3062
  * @private
3036
3063
  */
3037
- MaterialTooltip.prototype.handleMouseLeave_ = function (event) {
3038
- event.stopPropagation();
3064
+ MaterialTooltip.prototype.handleMouseLeave_ = function () {
3039
3065
  this.element_.classList.remove(this.CssClasses_.IS_ACTIVE);
3040
- window.removeEventListener('scroll', this.boundMouseLeaveHandler);
3041
- window.removeEventListener('touchmove', this.boundMouseLeaveHandler, false);
3042
3066
  };
3043
3067
  /**
3044
3068
  * Initialize element.
@@ -3050,40 +3074,19 @@ MaterialTooltip.prototype.init = function () {
3050
3074
  this.forElement_ = document.getElementById(forElId);
3051
3075
  }
3052
3076
  if (this.forElement_) {
3053
- // Tabindex needs to be set for `blur` events to be emitted
3077
+ // It's left here because it prevents accidental text selection on Android
3054
3078
  if (!this.forElement_.hasAttribute('tabindex')) {
3055
3079
  this.forElement_.setAttribute('tabindex', '0');
3056
3080
  }
3057
3081
  this.boundMouseEnterHandler = this.handleMouseEnter_.bind(this);
3058
3082
  this.boundMouseLeaveHandler = this.handleMouseLeave_.bind(this);
3059
3083
  this.forElement_.addEventListener('mouseenter', this.boundMouseEnterHandler, false);
3060
- this.forElement_.addEventListener('click', this.boundMouseEnterHandler, false);
3061
- this.forElement_.addEventListener('blur', this.boundMouseLeaveHandler);
3062
- this.forElement_.addEventListener('touchstart', this.boundMouseEnterHandler, false);
3063
- this.forElement_.addEventListener('mouseleave', this.boundMouseLeaveHandler);
3084
+ this.forElement_.addEventListener('touchend', this.boundMouseEnterHandler, false);
3085
+ this.forElement_.addEventListener('mouseleave', this.boundMouseLeaveHandler, false);
3086
+ window.addEventListener('touchstart', this.boundMouseLeaveHandler);
3064
3087
  }
3065
3088
  }
3066
3089
  };
3067
- /**
3068
- * Downgrade the component
3069
- *
3070
- * @private
3071
- */
3072
- MaterialTooltip.prototype.mdlDowngrade_ = function () {
3073
- if (this.forElement_) {
3074
- this.forElement_.removeEventListener('mouseenter', this.boundMouseEnterHandler, false);
3075
- this.forElement_.removeEventListener('click', this.boundMouseEnterHandler, false);
3076
- this.forElement_.removeEventListener('touchstart', this.boundMouseEnterHandler, false);
3077
- this.forElement_.removeEventListener('mouseleave', this.boundMouseLeaveHandler);
3078
- }
3079
- };
3080
- /**
3081
- * Public alias for the downgrade method.
3082
- *
3083
- * @public
3084
- */
3085
- MaterialTooltip.prototype.mdlDowngrade = MaterialTooltip.prototype.mdlDowngrade_;
3086
- MaterialTooltip.prototype['mdlDowngrade'] = MaterialTooltip.prototype.mdlDowngrade;
3087
3090
  // The component registers itself. It can assume componentHandler is available
3088
3091
  // in the global scope.
3089
3092
  componentHandler.register({
@@ -3130,10 +3133,21 @@ window['MaterialLayout'] = MaterialLayout;
3130
3133
  MaterialLayout.prototype.Constant_ = {
3131
3134
  MAX_WIDTH: '(max-width: 1024px)',
3132
3135
  TAB_SCROLL_PIXELS: 100,
3133
- MENU_ICON: 'menu',
3136
+ MENU_ICON: '&#xE5D2;',
3134
3137
  CHEVRON_LEFT: 'chevron_left',
3135
3138
  CHEVRON_RIGHT: 'chevron_right'
3136
3139
  };
3140
+ /**
3141
+ * Keycodes, for code readability.
3142
+ *
3143
+ * @enum {number}
3144
+ * @private
3145
+ */
3146
+ MaterialLayout.prototype.Keycodes_ = {
3147
+ ENTER: 13,
3148
+ ESCAPE: 27,
3149
+ SPACE: 32
3150
+ };
3137
3151
  /**
3138
3152
  * Modes.
3139
3153
  *
@@ -3209,6 +3223,17 @@ MaterialLayout.prototype.contentScrollHandler_ = function () {
3209
3223
  this.header_.classList.add(this.CssClasses_.IS_ANIMATING);
3210
3224
  }
3211
3225
  };
3226
+ /**
3227
+ * Handles a keyboard event on the drawer.
3228
+ *
3229
+ * @param {Event} evt The event that fired.
3230
+ * @private
3231
+ */
3232
+ MaterialLayout.prototype.keyboardEventHandler_ = function (evt) {
3233
+ if (evt.keyCode === this.Keycodes_.ESCAPE) {
3234
+ this.toggleDrawer();
3235
+ }
3236
+ };
3212
3237
  /**
3213
3238
  * Handles changes in screen size.
3214
3239
  *
@@ -3227,13 +3252,22 @@ MaterialLayout.prototype.screenSizeHandler_ = function () {
3227
3252
  }
3228
3253
  };
3229
3254
  /**
3230
- * Handles toggling of the drawer.
3255
+ * Handles events of drawer button.
3231
3256
  *
3257
+ * @param {Event} evt The event that fired.
3232
3258
  * @private
3233
3259
  */
3234
- MaterialLayout.prototype.drawerToggleHandler_ = function () {
3235
- this.drawer_.classList.toggle(this.CssClasses_.IS_DRAWER_OPEN);
3236
- this.obfuscator_.classList.toggle(this.CssClasses_.IS_DRAWER_OPEN);
3260
+ MaterialLayout.prototype.drawerToggleHandler_ = function (evt) {
3261
+ if (evt && evt.type === 'keydown') {
3262
+ if (evt.keyCode === this.Keycodes_.SPACE || evt.keyCode === this.Keycodes_.ENTER) {
3263
+ // prevent scrolling in drawer nav
3264
+ evt.preventDefault();
3265
+ } else {
3266
+ // prevent other keys
3267
+ return;
3268
+ }
3269
+ }
3270
+ this.toggleDrawer();
3237
3271
  };
3238
3272
  /**
3239
3273
  * Handles (un)setting the `is-animating` class
@@ -3274,6 +3308,25 @@ MaterialLayout.prototype.resetPanelState_ = function (panels) {
3274
3308
  panels[j].classList.remove(this.CssClasses_.IS_ACTIVE);
3275
3309
  }
3276
3310
  };
3311
+ /**
3312
+ * Toggle drawer state
3313
+ *
3314
+ * @public
3315
+ */
3316
+ MaterialLayout.prototype.toggleDrawer = function () {
3317
+ var drawerButton = this.element_.querySelector('.' + this.CssClasses_.DRAWER_BTN);
3318
+ this.drawer_.classList.toggle(this.CssClasses_.IS_DRAWER_OPEN);
3319
+ this.obfuscator_.classList.toggle(this.CssClasses_.IS_DRAWER_OPEN);
3320
+ // Set accessibility properties.
3321
+ if (this.drawer_.classList.contains(this.CssClasses_.IS_DRAWER_OPEN)) {
3322
+ this.drawer_.setAttribute('aria-hidden', 'false');
3323
+ drawerButton.setAttribute('aria-expanded', 'true');
3324
+ } else {
3325
+ this.drawer_.setAttribute('aria-hidden', 'true');
3326
+ drawerButton.setAttribute('aria-expanded', 'false');
3327
+ }
3328
+ };
3329
+ MaterialLayout.prototype['toggleDrawer'] = MaterialLayout.prototype.toggleDrawer;
3277
3330
  /**
3278
3331
  * Initialize element.
3279
3332
  */
@@ -3298,6 +3351,16 @@ MaterialLayout.prototype.init = function () {
3298
3351
  this.content_ = child;
3299
3352
  }
3300
3353
  }
3354
+ window.addEventListener('pageshow', function (e) {
3355
+ if (e.persisted) {
3356
+ // when page is loaded from back/forward cache
3357
+ // trigger repaint to let layout scroll in safari
3358
+ this.element_.style.overflowY = 'hidden';
3359
+ requestAnimationFrame(function () {
3360
+ this.element_.style.overflowY = '';
3361
+ }.bind(this));
3362
+ }
3363
+ }.bind(this), false);
3301
3364
  if (this.header_) {
3302
3365
  this.tabBar_ = this.header_.querySelector('.' + this.CssClasses_.TAB_BAR);
3303
3366
  }
@@ -3336,10 +3399,13 @@ MaterialLayout.prototype.init = function () {
3336
3399
  var drawerButton = this.element_.querySelector('.' + this.CssClasses_.DRAWER_BTN);
3337
3400
  if (!drawerButton) {
3338
3401
  drawerButton = document.createElement('div');
3402
+ drawerButton.setAttribute('aria-expanded', 'false');
3403
+ drawerButton.setAttribute('role', 'button');
3404
+ drawerButton.setAttribute('tabindex', '0');
3339
3405
  drawerButton.classList.add(this.CssClasses_.DRAWER_BTN);
3340
3406
  var drawerButtonIcon = document.createElement('i');
3341
3407
  drawerButtonIcon.classList.add(this.CssClasses_.ICON);
3342
- drawerButtonIcon.textContent = this.Constant_.MENU_ICON;
3408
+ drawerButtonIcon.innerHTML = this.Constant_.MENU_ICON;
3343
3409
  drawerButton.appendChild(drawerButtonIcon);
3344
3410
  }
3345
3411
  if (this.drawer_.classList.contains(this.CssClasses_.ON_LARGE_SCREEN)) {
@@ -3350,6 +3416,7 @@ MaterialLayout.prototype.init = function () {
3350
3416
  drawerButton.classList.add(this.CssClasses_.ON_SMALL_SCREEN);
3351
3417
  }
3352
3418
  drawerButton.addEventListener('click', this.drawerToggleHandler_.bind(this));
3419
+ drawerButton.addEventListener('keydown', this.drawerToggleHandler_.bind(this));
3353
3420
  // Add a class if the layout has a drawer, for altering the left padding.
3354
3421
  // Adds the HAS_DRAWER to the elements since this.header_ may or may
3355
3422
  // not be present.
@@ -3366,6 +3433,8 @@ MaterialLayout.prototype.init = function () {
3366
3433
  this.element_.appendChild(obfuscator);
3367
3434
  obfuscator.addEventListener('click', this.drawerToggleHandler_.bind(this));
3368
3435
  this.obfuscator_ = obfuscator;
3436
+ this.drawer_.addEventListener('keydown', this.keyboardEventHandler_.bind(this));
3437
+ this.drawer_.setAttribute('aria-hidden', 'true');
3369
3438
  }
3370
3439
  // Keep an eye on screen size, and add/remove auxiliary class for styling
3371
3440
  // of small screens.
@@ -3441,6 +3510,17 @@ MaterialLayout.prototype.init = function () {
3441
3510
  * @param {MaterialLayout} layout The MaterialLayout object that owns the tab.
3442
3511
  */
3443
3512
  function MaterialLayoutTab(tab, tabs, panels, layout) {
3513
+ /**
3514
+ * Auxiliary method to programmatically select a tab in the UI.
3515
+ */
3516
+ function selectTab() {
3517
+ var href = tab.href.split('#')[1];
3518
+ var panel = layout.content_.querySelector('#' + href);
3519
+ layout.resetTabState_(tabs);
3520
+ layout.resetPanelState_(panels);
3521
+ tab.classList.add(layout.CssClasses_.IS_ACTIVE);
3522
+ panel.classList.add(layout.CssClasses_.IS_ACTIVE);
3523
+ }
3444
3524
  if (layout.tabBar_.classList.contains(layout.CssClasses_.JS_RIPPLE_EFFECT)) {
3445
3525
  var rippleContainer = document.createElement('span');
3446
3526
  rippleContainer.classList.add(layout.CssClasses_.RIPPLE_CONTAINER);
@@ -3450,6 +3530,13 @@ function MaterialLayoutTab(tab, tabs, panels, layout) {
3450
3530
  rippleContainer.appendChild(ripple);
3451
3531
  tab.appendChild(rippleContainer);
3452
3532
  }
3533
+ tab.addEventListener('click', function (e) {
3534
+ if (tab.getAttribute('href').charAt(0) === '#') {
3535
+ e.preventDefault();
3536
+ selectTab();
3537
+ }
3538
+ });
3539
+ tab.show = selectTab;
3453
3540
  tab.addEventListener('click', function (e) {
3454
3541
  e.preventDefault();
3455
3542
  var href = tab.href.split('#')[1];
@@ -3460,6 +3547,7 @@ function MaterialLayoutTab(tab, tabs, panels, layout) {
3460
3547
  panel.classList.add(layout.CssClasses_.IS_ACTIVE);
3461
3548
  });
3462
3549
  }
3550
+ window['MaterialLayoutTab'] = MaterialLayoutTab;
3463
3551
  // The component registers itself. It can assume componentHandler is available
3464
3552
  // in the global scope.
3465
3553
  componentHandler.register({
@@ -3489,7 +3577,7 @@ componentHandler.register({
3489
3577
  * https://github.com/jasonmayes/mdl-component-design-pattern
3490
3578
  *
3491
3579
  * @constructor
3492
- * @param {HTMLElement} element The element that will be upgraded.
3580
+ * @param {Element} element The element that will be upgraded.
3493
3581
  */
3494
3582
  var MaterialDataTable = function MaterialDataTable(element) {
3495
3583
  this.element_ = element;
@@ -3524,7 +3612,7 @@ MaterialDataTable.prototype.CssClasses_ = {
3524
3612
  * single row (or multiple rows).
3525
3613
  *
3526
3614
  * @param {Element} checkbox Checkbox that toggles the selection state.
3527
- * @param {HTMLElement} row Row to toggle when checkbox changes.
3615
+ * @param {Element} row Row to toggle when checkbox changes.
3528
3616
  * @param {(Array<Object>|NodeList)=} opt_rows Rows to toggle when checkbox changes.
3529
3617
  * @private
3530
3618
  */
@@ -3562,7 +3650,7 @@ MaterialDataTable.prototype.selectRow_ = function (checkbox, row, opt_rows) {
3562
3650
  * Creates a checkbox for a single or or multiple rows and hooks up the
3563
3651
  * event handling.
3564
3652
  *
3565
- * @param {HTMLElement} row Row to toggle when checkbox changes.
3653
+ * @param {Element} row Row to toggle when checkbox changes.
3566
3654
  * @param {(Array<Object>|NodeList)=} opt_rows Rows to toggle when checkbox changes.
3567
3655
  * @private
3568
3656
  */
@@ -3578,7 +3666,12 @@ MaterialDataTable.prototype.createCheckbox_ = function (row, opt_rows) {
3578
3666
  var checkbox = document.createElement('input');
3579
3667
  checkbox.type = 'checkbox';
3580
3668
  checkbox.classList.add('mdl-checkbox__input');
3581
- checkbox.addEventListener('change', this.selectRow_(checkbox, row, opt_rows));
3669
+ if (row) {
3670
+ checkbox.checked = row.classList.contains(this.CssClasses_.IS_SELECTED);
3671
+ checkbox.addEventListener('change', this.selectRow_(checkbox, row));
3672
+ } else if (opt_rows) {
3673
+ checkbox.addEventListener('change', this.selectRow_(checkbox, null, opt_rows));
3674
+ }
3582
3675
  label.appendChild(checkbox);
3583
3676
  componentHandler.upgradeElement(label, 'MaterialCheckbox');
3584
3677
  return label;
@@ -3589,7 +3682,9 @@ MaterialDataTable.prototype.createCheckbox_ = function (row, opt_rows) {
3589
3682
  MaterialDataTable.prototype.init = function () {
3590
3683
  if (this.element_) {
3591
3684
  var firstHeader = this.element_.querySelector('th');
3592
- var rows = this.element_.querySelector('tbody').querySelectorAll('tr');
3685
+ var bodyRows = Array.prototype.slice.call(this.element_.querySelectorAll('tbody tr'));
3686
+ var footRows = Array.prototype.slice.call(this.element_.querySelectorAll('tfoot tr'));
3687
+ var rows = bodyRows.concat(footRows);
3593
3688
  if (this.element_.classList.contains(this.CssClasses_.SELECTABLE)) {
3594
3689
  var th = document.createElement('th');
3595
3690
  var headerCheckbox = this.createCheckbox_(null, rows);
@@ -3599,13 +3694,15 @@ MaterialDataTable.prototype.init = function () {
3599
3694
  var firstCell = rows[i].querySelector('td');
3600
3695
  if (firstCell) {
3601
3696
  var td = document.createElement('td');
3602
- var rowCheckbox = this.createCheckbox_(rows[i]);
3603
- td.appendChild(rowCheckbox);
3697
+ if (rows[i].parentNode.nodeName.toUpperCase() === 'TBODY') {
3698
+ var rowCheckbox = this.createCheckbox_(rows[i]);
3699
+ td.appendChild(rowCheckbox);
3700
+ }
3604
3701
  rows[i].insertBefore(td, firstCell);
3605
3702
  }
3606
3703
  }
3704
+ this.element_.classList.add(this.CssClasses_.IS_UPGRADED);
3607
3705
  }
3608
- this.element_.classList.add(this.CssClasses_.IS_UPGRADED);
3609
3706
  }
3610
3707
  };
3611
3708
  // The component registers itself. It can assume componentHandler is available
@@ -3727,14 +3824,13 @@ MaterialRipple.prototype.downHandler_ = function (event) {
3727
3824
  MaterialRipple.prototype.upHandler_ = function (event) {
3728
3825
  // Don't fire for the artificial "mouseup" generated by a double-click.
3729
3826
  if (event && event.detail !== 2) {
3730
- this.rippleElement_.classList.remove(this.CssClasses_.IS_VISIBLE);
3827
+ // Allow a repaint to occur before removing this class, so the animation
3828
+ // shows for tap events, which seem to trigger a mouseup too soon after
3829
+ // mousedown.
3830
+ window.setTimeout(function () {
3831
+ this.rippleElement_.classList.remove(this.CssClasses_.IS_VISIBLE);
3832
+ }.bind(this), 0);
3731
3833
  }
3732
- // Allow a repaint to occur before removing this class, so the animation
3733
- // shows for tap events, which seem to trigger a mouseup too soon after
3734
- // mousedown.
3735
- window.setTimeout(function () {
3736
- this.rippleElement_.classList.remove(this.CssClasses_.IS_VISIBLE);
3737
- }.bind(this), 0);
3738
3834
  };
3739
3835
  /**
3740
3836
  * Initialize element.
@@ -3834,26 +3930,6 @@ MaterialRipple.prototype.init = function () {
3834
3930
  }
3835
3931
  }
3836
3932
  };
3837
- /**
3838
- * Downgrade the component
3839
- *
3840
- * @private
3841
- */
3842
- MaterialRipple.prototype.mdlDowngrade_ = function () {
3843
- this.element_.removeEventListener('mousedown', this.boundDownHandler);
3844
- this.element_.removeEventListener('touchstart', this.boundDownHandler);
3845
- this.element_.removeEventListener('mouseup', this.boundUpHandler);
3846
- this.element_.removeEventListener('mouseleave', this.boundUpHandler);
3847
- this.element_.removeEventListener('touchend', this.boundUpHandler);
3848
- this.element_.removeEventListener('blur', this.boundUpHandler);
3849
- };
3850
- /**
3851
- * Public alias for the downgrade method.
3852
- *
3853
- * @public
3854
- */
3855
- MaterialRipple.prototype.mdlDowngrade = MaterialRipple.prototype.mdlDowngrade_;
3856
- MaterialRipple.prototype['mdlDowngrade'] = MaterialRipple.prototype.mdlDowngrade;
3857
3933
  // The component registers itself. It can assume componentHandler is available
3858
3934
  // in the global scope.
3859
3935
  componentHandler.register({