material_design_lite-sass 1.0.6.3 → 1.1.0

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 (44) hide show
  1. checksums.yaml +4 -4
  2. data/.travis.yml +5 -2
  3. data/CHANGELOG.md +9 -0
  4. data/README.md +41 -32
  5. data/lib/material_design_lite/sass/version.rb +1 -1
  6. data/vendor/assets/javascripts/material.js +377 -301
  7. data/vendor/assets/javascripts/material/button.js +0 -24
  8. data/vendor/assets/javascripts/material/checkbox.js +0 -26
  9. data/vendor/assets/javascripts/material/data-table.js +18 -10
  10. data/vendor/assets/javascripts/material/icon-toggle.js +0 -26
  11. data/vendor/assets/javascripts/material/layout.js +104 -6
  12. data/vendor/assets/javascripts/material/mdlComponentHandler.js +15 -36
  13. data/vendor/assets/javascripts/material/menu.js +16 -36
  14. data/vendor/assets/javascripts/material/progress.js +0 -22
  15. data/vendor/assets/javascripts/material/radio.js +0 -28
  16. data/vendor/assets/javascripts/material/ripple.js +6 -35
  17. data/vendor/assets/javascripts/material/slider.js +0 -23
  18. data/vendor/assets/javascripts/material/snackbar.js +189 -0
  19. data/vendor/assets/javascripts/material/switch.js +0 -26
  20. data/vendor/assets/javascripts/material/tabs.js +2 -2
  21. data/vendor/assets/javascripts/material/textfield.js +32 -25
  22. data/vendor/assets/javascripts/material/tooltip.js +39 -49
  23. data/vendor/assets/stylesheets/_material.scss +3 -0
  24. data/vendor/assets/stylesheets/material/_badge.scss +6 -0
  25. data/vendor/assets/stylesheets/material/_button.scss +3 -3
  26. data/vendor/assets/stylesheets/material/_checkbox.scss +5 -0
  27. data/vendor/assets/stylesheets/material/_data-table.scss +17 -9
  28. data/vendor/assets/stylesheets/material/_dialog.scss +57 -0
  29. data/vendor/assets/stylesheets/material/_grid.scss +60 -0
  30. data/vendor/assets/stylesheets/material/_layout.scss +77 -3
  31. data/vendor/assets/stylesheets/material/_list.scss +103 -0
  32. data/vendor/assets/stylesheets/material/_mega_footer.scss +1 -1
  33. data/vendor/assets/stylesheets/material/_menu.scss +5 -1
  34. data/vendor/assets/stylesheets/material/_mixins.scss +33 -0
  35. data/vendor/assets/stylesheets/material/_progress.scss +5 -0
  36. data/vendor/assets/stylesheets/material/_radio.scss +5 -0
  37. data/vendor/assets/stylesheets/material/_shadow.scss +4 -0
  38. data/vendor/assets/stylesheets/material/_snackbar.scss +81 -0
  39. data/vendor/assets/stylesheets/material/_switch.scss +5 -1
  40. data/vendor/assets/stylesheets/material/_tabs.scss +0 -1
  41. data/vendor/assets/stylesheets/material/_textfield.scss +13 -3
  42. data/vendor/assets/stylesheets/material/_typography.scss +4 -0
  43. data/vendor/assets/stylesheets/material/_variables.scss +102 -81
  44. metadata +7 -3
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: bde71fb69be64bab2e676984fd29561d22e68cf0
4
- data.tar.gz: 21e57b3dabbc1861ac8e6e3612fbbd63cbfbbb93
3
+ metadata.gz: a2fef800435b17f9b16f3513e34f59dbea3eb7e8
4
+ data.tar.gz: d61e25399084c0bb39602b3ed0414c33458df540
5
5
  SHA512:
6
- metadata.gz: 3bc5efd0f6e2af4db8ead43d21425d5094eda77fbedffe7eb756ce520d870d18aecd6e969da5e0f4a3ef8b9985aadb9f083d7d0aa181254f40fff6d8296d4f4a
7
- data.tar.gz: c3f69aa2c502acf170adae9fc1b2958420551a83c47d1abd6c09446488443e3faa0232079c444cdc31479484d263d8d7a2757a8bbcc454ea232df85024c5a7c3
6
+ metadata.gz: 45dd2286e27638b6cd459e554b71a22bbcf2adc6401868f8ac37ded573a7a4d640ee0fcde1ca2f93ca1e50b9d2d2c2d177cc17783cde52c37d20169119b961e4
7
+ data.tar.gz: 365db8127f5295c925fd96fd0db0d4daba62e5ab32c17c1b82dd8159a7dc3c179a2a067f8ac880aa653349fb3e6d590619bf6d8c61c23e073f458a9d6ea9d45d
@@ -2,9 +2,12 @@ language: ruby
2
2
 
3
3
  rvm:
4
4
  - 2.0.0
5
- - 2.1.7
6
- - 2.2.3
5
+ - 2.1.8
6
+ - 2.2.4
7
+ - 2.3.0
7
8
 
8
9
  before_install: gem install bundler
9
10
 
10
11
  sudo: false
12
+
13
+ cache: bundler
@@ -1,3 +1,12 @@
1
+ ## 1.1.0 (2016-02-02)
2
+
3
+ - Update assets to match upstream version
4
+
5
+ Library version:
6
+
7
+ - Material Design Lite v1.1.0
8
+ - Material Icons v2.1
9
+
1
10
  ## 1.0.6.3 (2015-11-21)
2
11
 
3
12
  - Improvement: Support for sprockets-rails 3+
data/README.md CHANGED
@@ -62,6 +62,7 @@ Then include desired Material Design Lite JavaScript component:
62
62
  //= require material/radio
63
63
  //= require material/ripple
64
64
  //= require material/slider
65
+ //= require material/snackbar
65
66
  //= require material/spinner
66
67
  //= require material/switch
67
68
  //= require material/tabs
@@ -72,35 +73,43 @@ Then include desired Material Design Lite JavaScript component:
72
73
  Individual Sass components can be included like this:
73
74
 
74
75
  ```scss
75
- @import 'material/animation';
76
- @import 'material/badge';
77
- @import 'material/button';
78
- @import 'material/card';
79
- @import 'material/checkbox';
80
- @import 'material/color-definitions';
81
- @import 'material/data-table';
82
- @import 'material/functions';
83
- @import 'material/grid';
84
- @import 'material/icon-toggle';
85
- @import 'material/layout';
76
+ // Variables and mixins
77
+ @import "material/variables";
78
+ @import "material/mixins";
79
+
80
+ // Resets and dependencies
81
+ @import "material/resets";
82
+ @import "material/typography";
83
+
84
+ // Components
85
+ @import "material/palette";
86
+ @import "material/ripple";
87
+ @import "material/animation";
88
+ @import "material/badge";
89
+ @import "material/button";
90
+ @import "material/card";
91
+ @import "material/checkbox";
92
+ @import "material/data-table";
93
+ @import "material/dialog";
94
+ @import "material/mega_footer";
95
+ @import "material/mini_footer";
96
+ @import "material/icon-toggle";
97
+ @import "material/list";
98
+ @import "material/menu";
99
+ @import "material/progress";
100
+ @import "material/layout";
101
+ @import "material/radio";
102
+ @import "material/slider";
103
+ @import "material/snackbar";
104
+ @import "material/spinner";
105
+ @import "material/switch";
106
+ @import "material/tabs";
107
+ @import "material/textfield";
108
+ @import "material/tooltip";
109
+ @import "material/shadow";
110
+ @import "material/grid";
86
111
  @import "material/material-icons";
87
- @import 'material/mega_footer';
88
- @import 'material/mini_footer';
89
- @import 'material/menu';
90
- @import 'material/palette';
91
- @import 'material/progress';
92
- @import 'material/radio';
93
- @import 'material/resets';
94
- @import 'material/ripple';
95
- @import 'material/roboto';
96
- @import 'material/shadow';
97
- @import 'material/slider';
98
- @import 'material/spinner';
99
- @import 'material/switch';
100
- @import 'material/tabs';
101
- @import 'material/textfield';
102
- @import 'material/tooltip';
103
- @import 'material/typography';
112
+ @import "material/roboto";
104
113
  ```
105
114
 
106
115
  ### Material Icons
@@ -144,14 +153,14 @@ Anyone is welcome to contribute to Material Design Lite for Sass. Please [raise
144
153
 
145
154
  ## Credits
146
155
 
147
- Material Design Lite for Sass is inspired from [bootstrap-sass](https://github.com/twbs/bootstrap-sass) by Twitter Bootstrap team.
156
+ Material Design Lite for Sass is inspired from [bootstrap-sass](https://github.com/twbs/bootstrap-sass) by Bootstrap team.
148
157
 
149
158
  ## License
150
159
 
151
- Material Design Lite © Google, 2015. Licensed under the [Apache-2](https://github.com/google/material-design-lite/blob/master/LICENSE) license.
160
+ Material Design Lite © Google, 2015. Licensed under the [Apache-2](http://www.apache.org/licenses/LICENSE-2.0.html) license.
152
161
 
153
- Material Icons © Google, 2015. Released under the [Creative Common Attribution 4.0 International License (CC-BY 4.0)](https://github.com/rubysamurai/material_design_lite-sass/blob/master/vendor/assets/fonts/material-icons/LICENSE) license.
162
+ Material Icons © Google, 2015. Licensed under the [Creative Common Attribution 4.0 International License (CC-BY 4.0)](https://creativecommons.org/licenses/by/4.0/legalcode).
154
163
 
155
- Roboto © Google, 2015. Licensed under the [Apache-2](https://github.com/rubysamurai/material_design_lite-sass/blob/master/vendor/assets/fonts/roboto/LICENSE.txt) license.
164
+ Roboto © Google, 2015. Licensed under the [Apache-2](http://www.apache.org/licenses/LICENSE-2.0.html) license.
156
165
 
157
166
  `material_design_lite-sass` © Dmitriy Tarasov, 2015. Released under the [MIT](https://github.com/rubysamurai/material_design_lite-sass/blob/master/LICENSE.txt) license.
@@ -1,5 +1,5 @@
1
1
  module MaterialDesignLite
2
2
  module Sass
3
- VERSION = '1.0.6.3'
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({