material_design_lite-rails 1.0.0 → 1.0.1

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 5bf88a29f0d72a6a219bb0408ab64bd9af6dbe1d
4
- data.tar.gz: 752835bb9eb957a4e6884137b166d036b8e68257
3
+ metadata.gz: 8739ee32c6207fa744cb15331f169ff19b5ea4f1
4
+ data.tar.gz: 756253978a0ee1af15b1b620c2c227fe5cdbd361
5
5
  SHA512:
6
- metadata.gz: 3243a595387c93b136527ede73339bebe46536f88749d17892e03c933e3dbcd3a81cf11bce524257e01666cf09dd65a637c09220d3b4c97f0054dcfaedf69747
7
- data.tar.gz: 890915daf79bfba8094e5f1752381df1560dd50d47ad3833dacf85f9779195c7621fa282e7975dd99e75d420fefa862c4095197f383f9aa8f55701b5842ad697
6
+ metadata.gz: b3f41f39b5d1f8b9ca1ce4c2809f065cf32ea2737b12658e2262e6bfad8f6475e38c4dd52da9c5cf564ba9a0e8d6f8976f02885c24d63885452cb442a8be70ca
7
+ data.tar.gz: 35222f293ab3dc0dc120f17bd754d9e05acbae092269f102ac0b48447ffca03f400a8faf1c66a7041201e05a9d419d4fdaaa93049b5a5e16d1e3ec7821017dca
data/README.md CHANGED
@@ -1,21 +1,20 @@
1
- # MaterialDesignLite for Rails
1
+ # Material Design Lite, for Rails!
2
2
 
3
- A gemified version of
4
- [`googe/material-design-lite`](https://github.com/google/material-design-lite).
3
+ A gemified version of [Google's Material Design Lite](http://www.getmdl.io/) library.
5
4
 
6
5
  ## Installation
7
6
 
8
- Add this line to your Rails application's Gemfile:
7
+ To your Rails application's Gemfile, add
9
8
 
10
9
  ```ruby
11
10
  gem 'material_design_lite-rails'
12
11
  ```
13
12
 
14
- And then execute:
13
+ And then run
15
14
 
16
15
  $ bundle
17
16
 
18
- ### Javascripts
17
+ #### Javascripts
19
18
 
20
19
  To your `application.js` file, add:
21
20
 
@@ -23,16 +22,18 @@ To your `application.js` file, add:
23
22
  //= require material
24
23
  ```
25
24
 
26
- ### Stylesheets
25
+ #### Stylesheets
27
26
 
28
27
  Do one of the following:
29
28
 
30
- To your `application.css` , add:
29
+ To your `application.css` , add
31
30
  ```
32
31
  *= require material
33
32
  ```
34
33
 
35
- or, if you're using sass, use sass's
34
+ **OR**
35
+
36
+ If you're using sass, use sass's
36
37
  [`@import`](https://github.com/rails/sass-rails#important-note)
37
38
  in your `application.scss`.
38
39
 
@@ -40,6 +41,23 @@ in your `application.scss`.
40
41
  @import "material";
41
42
  ```
42
43
 
44
+ #### Icons
45
+ Material Design Lite uses a font called 'Material Icons'.
46
+ You can either load this font from google, or host it yourself.
47
+
48
+ ##### Load font from google
49
+ Add the following line to your `application.html.erb` view layout file,
50
+ in the `<head>` section:
51
+
52
+ ```
53
+ <%= stylesheet_link_tag "https://fonts.googleapis.com/icon?family=Material+Icons" %>
54
+ ```
55
+
56
+ **OR**
57
+
58
+ ##### Host font locally
59
+ Use the `material_icons` gem to [host the font locally](https://github.com/Angelmmiguel/material_icons).
60
+
43
61
  ## Versioning
44
62
 
45
63
  This gem is versioned semantically,
@@ -48,12 +66,13 @@ in line with
48
66
 
49
67
  If there needs to be a release of this gem without a corresponding release to
50
68
  `google/material-design-lite'` to the repo, an additional digit will be added
51
- (so an additional release of this gem `1.0.0.1`).
69
+ (so if this gem's version is `1.0.0.1`, google's version would still be `1.0.0`).
70
+
52
71
  The first three digits will always be the same as `google/material-design-lite`.
53
72
 
54
73
  ## TODO:
55
74
 
56
- - [ ] Add tests (make sure CSS/JS loads, and check version?)
75
+ - [ ] Add tests (make sure CSS/JS loads, and check version)
57
76
  - [ ] Add view helpers, to ease burden of manually adding all the classes.
58
77
 
59
78
  ## Contributing
@@ -1,5 +1,5 @@
1
1
  module MaterialDesignLite
2
2
  module Rails
3
- VERSION = "1.0.0"
3
+ VERSION = "1.0.1"
4
4
  end
5
5
  end
@@ -17,7 +17,7 @@
17
17
 
18
18
  /**
19
19
  * A component handler interface using the revealing module design pattern.
20
- * More details on this pattern design here:
20
+ * More details on this design pattern here:
21
21
  * https://github.com/jasonmayes/mdl-component-design-pattern
22
22
  * @author Jason Mayes.
23
23
  */
@@ -34,8 +34,8 @@ var componentHandler = (function() {
34
34
  * Searches registered components for a class we are interested in using.
35
35
  * Optionally replaces a match with passed object if specified.
36
36
  * @param {string} name The name of a class we want to use.
37
- * @param {object} optReplace Optional object to replace match with.
38
- * @return {object | false}
37
+ * @param {Object=} optReplace Optional object to replace match with.
38
+ * @return {Object | boolean}
39
39
  * @private
40
40
  */
41
41
  function findRegisteredClass_(name, optReplace) {
@@ -50,29 +50,55 @@ var componentHandler = (function() {
50
50
  return false;
51
51
  }
52
52
 
53
+ /**
54
+ * Returns an array of the classNames of the upgraded classes on the element.
55
+ * @param {HTMLElement} element The element to fetch data from.
56
+ * @return {Array<string>}
57
+ * @private
58
+ */
59
+ function getUpgradedListOfElement_(element) {
60
+ var dataUpgraded = element.getAttribute('data-upgraded');
61
+ // Use `['']` as default value to conform the `,name,name...` style.
62
+ return dataUpgraded === null ? [''] : dataUpgraded.split(',');
63
+ }
64
+
65
+ /**
66
+ * Returns true if the given element has already been upgraded for the given
67
+ * class.
68
+ * @param {HTMLElement} element The element we want to check.
69
+ * @param {string} jsClass The class to check for.
70
+ * @return boolean
71
+ * @private
72
+ */
73
+ function isElementUpgraded_(element, jsClass) {
74
+ var upgradedList = getUpgradedListOfElement_(element);
75
+ return upgradedList.indexOf(jsClass) !== -1;
76
+ }
77
+
53
78
  /**
54
79
  * Searches existing DOM for elements of our component type and upgrades them
55
80
  * if they have not already been upgraded.
56
- * @param {string} jsClass the programatic name of the element class we need
57
- * to create a new instance of.
58
- * @param {string} cssClass the name of the CSS class elements of this type
59
- * will have.
81
+ * @param {!string=} optJsClass the programatic name of the element class we
82
+ * need to create a new instance of.
83
+ * @param {!string=} optCssClass the name of the CSS class elements of this
84
+ * type will have.
60
85
  */
61
- function upgradeDomInternal(jsClass, cssClass) {
62
- if (jsClass === undefined && cssClass === undefined) {
86
+ function upgradeDomInternal(optJsClass, optCssClass) {
87
+ if (optJsClass === undefined && optCssClass === undefined) {
63
88
  for (var i = 0; i < registeredComponents_.length; i++) {
64
89
  upgradeDomInternal(registeredComponents_[i].className,
65
90
  registeredComponents_[i].cssClass);
66
91
  }
67
92
  } else {
68
- if (cssClass === undefined) {
93
+ var jsClass = /** @type {!string} */ (optJsClass);
94
+ if (optCssClass === undefined) {
69
95
  var registeredClass = findRegisteredClass_(jsClass);
70
96
  if (registeredClass) {
71
- cssClass = registeredClass.cssClass;
97
+ optCssClass = registeredClass.cssClass;
72
98
  }
73
99
  }
74
100
 
75
- var elements = document.querySelectorAll('.' + cssClass);
101
+ var elements = document.querySelectorAll('.' + optCssClass);
76
102
  for (var n = 0; n < elements.length; n++) {
77
103
  upgradeElementInternal(elements[n], jsClass);
78
104
  }
@@ -82,36 +108,54 @@ var componentHandler = (function() {
82
108
  /**
83
109
  * Upgrades a specific element rather than all in the DOM.
84
110
  * @param {HTMLElement} element The element we wish to upgrade.
85
- * @param {string} jsClass The name of the class we want to upgrade
111
+ * @param {!string=} optJsClass Optional name of the class we want to upgrade
86
112
  * the element to.
87
113
  */
88
- function upgradeElementInternal(element, jsClass) {
89
- // Only upgrade elements that have not already been upgraded.
90
- var dataUpgraded = element.getAttribute('data-upgraded');
114
+ function upgradeElementInternal(element, optJsClass) {
115
+ // Verify argument type.
116
+ if (!(typeof element === 'object' && element instanceof Element)) {
117
+ throw new Error('Invalid argument provided to upgrade MDL element.');
118
+ }
119
+ var upgradedList = getUpgradedListOfElement_(element);
120
+ var classesToUpgrade = [];
121
+ // If jsClass is not provided scan the registered components to find the
122
+ // ones matching the element's CSS classList.
123
+ if (!optJsClass) {
124
+ var classList = element.classList;
125
+ registeredComponents_.forEach(function (component) {
126
+ // Match CSS & Not to be upgraded & Not upgraded.
127
+ if (classList.contains(component.cssClass) &&
128
+ classesToUpgrade.indexOf(component) === -1 &&
129
+ !isElementUpgraded_(element, component.className)) {
130
+ classesToUpgrade.push(component);
131
+ }
132
+ });
133
+ } else if (!isElementUpgraded_(element, optJsClass)) {
134
+ classesToUpgrade.push(findRegisteredClass_(optJsClass));
135
+ }
91
136
 
92
- if (dataUpgraded === null || dataUpgraded.indexOf(jsClass) === -1) {
93
- // Upgrade element.
94
- if (dataUpgraded === null) {
95
- dataUpgraded = '';
96
- }
97
- element.setAttribute('data-upgraded', dataUpgraded + ',' + jsClass);
98
- var registeredClass = findRegisteredClass_(jsClass);
137
+ // Upgrade the element for each classes.
138
+ for (var i = 0, n = classesToUpgrade.length, registeredClass; i < n; i++) {
139
+ registeredClass = classesToUpgrade[i];
99
140
  if (registeredClass) {
100
- // new
141
+ // Mark element as upgraded.
142
+ upgradedList.push(registeredClass.className);
143
+ element.setAttribute('data-upgraded', upgradedList.join(','));
101
144
  var instance = new registeredClass.classConstructor(element);
102
145
  instance[componentConfigProperty_] = registeredClass;
103
146
  createdComponents_.push(instance);
104
147
  // Call any callbacks the user has registered with this component type.
105
- registeredClass.callbacks.forEach(function(callback) {
106
- callback(element);
107
- });
148
+ for (var j = 0, m = registeredClass.callbacks.length; j < m; j++) {
149
+ registeredClass.callbacks[j](element);
150
+ }
108
151
 
109
152
  if (registeredClass.widget) {
110
153
  // Assign per element instance for control over API
111
- element[jsClass] = instance;
154
+ element[registeredClass.className] = instance;
112
155
  }
113
156
  } else {
114
- throw 'Unable to find a registered component for the given class.';
157
+ throw new Error(
158
+ 'Unable to find a registered component for the given class.');
115
159
  }
116
160
 
117
161
  var ev = document.createEvent('Events');
@@ -120,9 +164,33 @@ var componentHandler = (function() {
120
164
  }
121
165
  }
122
166
 
167
+ /**
168
+ * Upgrades a specific list of elements rather than all in the DOM.
169
+ * @param {HTMLElement | Array<HTMLElement> | NodeList | HTMLCollection} elements
170
+ * The elements we wish to upgrade.
171
+ */
172
+ function upgradeElementsInternal(elements) {
173
+ if (!Array.isArray(elements)) {
174
+ if (typeof elements.item === 'function') {
175
+ elements = Array.prototype.slice.call(elements);
176
+ } else {
177
+ elements = [elements];
178
+ }
179
+ }
180
+ for (var i = 0, n = elements.length, element; i < n; i++) {
181
+ element = elements[i];
182
+ if (element instanceof HTMLElement) {
183
+ if (element.children.length > 0) {
184
+ upgradeElementsInternal(element.children);
185
+ }
186
+ upgradeElementInternal(element);
187
+ }
188
+ }
189
+ }
190
+
123
191
  /**
124
192
  * Registers a class for future use and attempts to upgrade existing DOM.
125
- * @param {object} config An object containing:
193
+ * @param {Object} config An object containing:
126
194
  * {constructor: Constructor, classAsString: string, cssClass: string}
127
195
  */
128
196
  function registerInternal(config) {
@@ -136,17 +204,18 @@ var componentHandler = (function() {
136
204
 
137
205
  registeredComponents_.forEach(function(item) {
138
206
  if (item.cssClass === newConfig.cssClass) {
139
- throw 'The provided cssClass has already been registered.';
207
+ throw new Error('The provided cssClass has already been registered.');
140
208
  }
141
209
  if (item.className === newConfig.className) {
142
- throw 'The provided className has already been registered';
210
+ throw new Error('The provided className has already been registered');
143
211
  }
144
212
  });
145
213
 
146
214
  if (config.constructor.prototype
147
215
  .hasOwnProperty(componentConfigProperty_)) {
148
- throw 'MDL component classes must not have ' + componentConfigProperty_ +
149
- ' defined as a property.';
216
+ throw new Error(
217
+ 'MDL component classes must not have ' + componentConfigProperty_ +
218
+ ' defined as a property.');
150
219
  }
151
220
 
152
221
  var found = findRegisteredClass_(config.classAsString, newConfig);
@@ -161,7 +230,7 @@ var componentHandler = (function() {
161
230
  * component type
162
231
  * @param {string} jsClass The class name of the MDL component we wish
163
232
  * to hook into for any upgrades performed.
164
- * @param {function} callback The function to call upon an upgrade. This
233
+ * @param {!Function} callback The function to call upon an upgrade. This
165
234
  * function should expect 1 parameter - the HTMLElement which got upgraded.
166
235
  */
167
236
  function registerUpgradedCallbackInternal(jsClass, callback) {
@@ -240,7 +309,7 @@ var componentHandler = (function() {
240
309
  } else if (nodes instanceof Node) {
241
310
  downgradeNode(nodes);
242
311
  } else {
243
- throw 'Invalid argument provided to downgrade MDL nodes.';
312
+ throw new Error('Invalid argument provided to downgrade MDL nodes.');
244
313
  }
245
314
  }
246
315
 
@@ -249,6 +318,7 @@ var componentHandler = (function() {
249
318
  return {
250
319
  upgradeDom: upgradeDomInternal,
251
320
  upgradeElement: upgradeElementInternal,
321
+ upgradeElements: upgradeElementsInternal,
252
322
  upgradeAllRegistered: upgradeAllRegisteredInternal,
253
323
  registerUpgradedCallback: registerUpgradedCallbackInternal,
254
324
  register: registerInternal,
@@ -443,7 +513,8 @@ MaterialButton.prototype.mdlDowngrade_ = function() {
443
513
  componentHandler.register({
444
514
  constructor: MaterialButton,
445
515
  classAsString: 'MaterialButton',
446
- cssClass: 'mdl-js-button'
516
+ cssClass: 'mdl-js-button',
517
+ widget: true
447
518
  });
448
519
 
449
520
  /**
@@ -708,7 +779,8 @@ MaterialCheckbox.prototype.mdlDowngrade_ = function() {
708
779
  componentHandler.register({
709
780
  constructor: MaterialCheckbox,
710
781
  classAsString: 'MaterialCheckbox',
711
- cssClass: 'mdl-js-checkbox'
782
+ cssClass: 'mdl-js-checkbox',
783
+ widget: true
712
784
  });
713
785
 
714
786
  /**
@@ -956,7 +1028,8 @@ MaterialIconToggle.prototype.mdlDowngrade_ = function() {
956
1028
  componentHandler.register({
957
1029
  constructor: MaterialIconToggle,
958
1030
  classAsString: 'MaterialIconToggle',
959
- cssClass: 'mdl-js-icon-toggle'
1031
+ cssClass: 'mdl-js-icon-toggle',
1032
+ widget: true
960
1033
  });
961
1034
 
962
1035
  /**
@@ -1299,7 +1372,9 @@ MaterialMenu.prototype.applyClip_ = function(height, width) {
1299
1372
  MaterialMenu.prototype.addAnimationEndListener_ = function() {
1300
1373
  'use strict';
1301
1374
 
1302
- var cleanup = function() {
1375
+ var cleanup = function () {
1376
+ this.element_.removeEventListener('transitionend', cleanup);
1377
+ this.element_.removeEventListener('webkitTransitionEnd', cleanup);
1303
1378
  this.element_.classList.remove(this.CssClasses_.IS_ANIMATING);
1304
1379
  }.bind(this);
1305
1380
 
@@ -1422,7 +1497,8 @@ MaterialMenu.prototype.toggle = function(evt) {
1422
1497
  componentHandler.register({
1423
1498
  constructor: MaterialMenu,
1424
1499
  classAsString: 'MaterialMenu',
1425
- cssClass: 'mdl-js-menu'
1500
+ cssClass: 'mdl-js-menu',
1501
+ widget: true
1426
1502
  });
1427
1503
 
1428
1504
  /**
@@ -1523,12 +1599,23 @@ MaterialProgress.prototype.init = function() {
1523
1599
  }
1524
1600
  };
1525
1601
 
1602
+ /*
1603
+ * Downgrade the component
1604
+ */
1605
+ MaterialProgress.prototype.mdlDowngrade_ = function() {
1606
+ 'use strict';
1607
+ while (this.element_.firstChild) {
1608
+ this.element_.removeChild(this.element_.firstChild);
1609
+ }
1610
+ };
1611
+
1526
1612
  // The component registers itself. It can assume componentHandler is available
1527
1613
  // in the global scope.
1528
1614
  componentHandler.register({
1529
1615
  constructor: MaterialProgress,
1530
1616
  classAsString: 'MaterialProgress',
1531
- cssClass: 'mdl-js-progress'
1617
+ cssClass: 'mdl-js-progress',
1618
+ widget: true
1532
1619
  });
1533
1620
 
1534
1621
  /**
@@ -1785,7 +1872,8 @@ MaterialRadio.prototype.init = function() {
1785
1872
  componentHandler.register({
1786
1873
  constructor: MaterialRadio,
1787
1874
  classAsString: 'MaterialRadio',
1788
- cssClass: 'mdl-js-radio'
1875
+ cssClass: 'mdl-js-radio',
1876
+ widget: true
1789
1877
  });
1790
1878
 
1791
1879
  /**
@@ -2037,7 +2125,8 @@ MaterialSlider.prototype.mdlDowngrade_ = function() {
2037
2125
  componentHandler.register({
2038
2126
  constructor: MaterialSlider,
2039
2127
  classAsString: 'MaterialSlider',
2040
- cssClass: 'mdl-js-slider'
2128
+ cssClass: 'mdl-js-slider',
2129
+ widget: true
2041
2130
  });
2042
2131
 
2043
2132
  /**
@@ -2177,7 +2266,8 @@ MaterialSpinner.prototype.init = function() {
2177
2266
  componentHandler.register({
2178
2267
  constructor: MaterialSpinner,
2179
2268
  classAsString: 'MaterialSpinner',
2180
- cssClass: 'mdl-js-spinner'
2269
+ cssClass: 'mdl-js-spinner',
2270
+ widget: true
2181
2271
  });
2182
2272
 
2183
2273
  /**
@@ -2446,7 +2536,8 @@ MaterialSwitch.prototype.mdlDowngrade_ = function() {
2446
2536
  componentHandler.register({
2447
2537
  constructor: MaterialSwitch,
2448
2538
  classAsString: 'MaterialSwitch',
2449
- cssClass: 'mdl-js-switch'
2539
+ cssClass: 'mdl-js-switch',
2540
+ widget: true
2450
2541
  });
2451
2542
 
2452
2543
  /**
@@ -2707,19 +2798,45 @@ MaterialTextfield.prototype.onBlur_ = function(event) {
2707
2798
  */
2708
2799
  MaterialTextfield.prototype.updateClasses_ = function() {
2709
2800
  'use strict';
2801
+ this.checkDisabled();
2802
+ this.checkValidity();
2803
+ this.checkDirty();
2804
+ };
2805
+
2806
+ // Public methods.
2710
2807
 
2808
+ /**
2809
+ * Check the disabled state and update field accordingly.
2810
+ * @public
2811
+ */
2812
+ MaterialTextfield.prototype.checkDisabled = function() {
2813
+ 'use strict';
2711
2814
  if (this.input_.disabled) {
2712
2815
  this.element_.classList.add(this.CssClasses_.IS_DISABLED);
2713
2816
  } else {
2714
2817
  this.element_.classList.remove(this.CssClasses_.IS_DISABLED);
2715
2818
  }
2819
+ };
2716
2820
 
2821
+ /**
2822
+ * Check the validity state and update field accordingly.
2823
+ * @public
2824
+ */
2825
+ MaterialTextfield.prototype.checkValidity = function() {
2826
+ 'use strict';
2717
2827
  if (this.input_.validity.valid) {
2718
2828
  this.element_.classList.remove(this.CssClasses_.IS_INVALID);
2719
2829
  } else {
2720
2830
  this.element_.classList.add(this.CssClasses_.IS_INVALID);
2721
2831
  }
2832
+ };
2722
2833
 
2834
+ /**
2835
+ * Check the dirty state and update field accordingly.
2836
+ * @public
2837
+ */
2838
+ MaterialTextfield.prototype.checkDirty = function() {
2839
+ 'use strict';
2723
2840
  if (this.input_.value && this.input_.value.length > 0) {
2724
2841
  this.element_.classList.add(this.CssClasses_.IS_DIRTY);
2725
2842
  } else {
@@ -2727,8 +2844,6 @@ MaterialTextfield.prototype.updateClasses_ = function() {
2727
2844
  }
2728
2845
  };
2729
2846
 
2730
- // Public methods.
2731
-
2732
2847
  /**
2733
2848
  * Disable text field.
2734
2849
  * @public
@@ -2822,7 +2937,8 @@ MaterialTextfield.prototype.mdlDowngrade_ = function() {
2822
2937
  componentHandler.register({
2823
2938
  constructor: MaterialTextfield,
2824
2939
  classAsString: 'MaterialTextfield',
2825
- cssClass: 'mdl-js-textfield'
2940
+ cssClass: 'mdl-js-textfield',
2941
+ widget: true
2826
2942
  });
2827
2943
 
2828
2944
  /**
@@ -2887,10 +3003,21 @@ MaterialTooltip.prototype.handleMouseEnter_ = function(event) {
2887
3003
 
2888
3004
  event.stopPropagation();
2889
3005
  var props = event.target.getBoundingClientRect();
2890
- this.element_.style.left = props.left + (props.width / 2) + 'px';
2891
- this.element_.style.marginLeft = -1 * (this.element_.offsetWidth / 2) + 'px';
3006
+ var left = props.left + (props.width / 2);
3007
+ var marginLeft = -1 * (this.element_.offsetWidth / 2);
3008
+
3009
+ if (left + marginLeft < 0) {
3010
+ this.element_.style.left = 0;
3011
+ this.element_.style.marginLeft = 0;
3012
+ } else {
3013
+ this.element_.style.left = left + 'px';
3014
+ this.element_.style.marginLeft = marginLeft + 'px';
3015
+ }
3016
+
2892
3017
  this.element_.style.top = props.top + props.height + 10 + 'px';
2893
3018
  this.element_.classList.add(this.CssClasses_.IS_ACTIVE);
3019
+ window.addEventListener('scroll', this.boundMouseLeaveHandler, false);
3020
+ window.addEventListener('touchmove', this.boundMouseLeaveHandler, false);
2894
3021
  };
2895
3022
 
2896
3023
  /**
@@ -2903,6 +3030,8 @@ MaterialTooltip.prototype.handleMouseLeave_ = function(event) {
2903
3030
 
2904
3031
  event.stopPropagation();
2905
3032
  this.element_.classList.remove(this.CssClasses_.IS_ACTIVE);
3033
+ window.removeEventListener('scroll', this.boundMouseLeaveHandler);
3034
+ window.removeEventListener('touchmove', this.boundMouseLeaveHandler, false);
2906
3035
  };
2907
3036
 
2908
3037
  /**
@@ -2919,12 +3048,20 @@ MaterialTooltip.prototype.init = function() {
2919
3048
  }
2920
3049
 
2921
3050
  if (this.forElement_) {
3051
+ // Tabindex needs to be set for `blur` events to be emitted
3052
+ if (!this.forElement_.getAttribute('tabindex')) {
3053
+ this.forElement_.setAttribute('tabindex', '0');
3054
+ }
3055
+
2922
3056
  this.boundMouseEnterHandler = this.handleMouseEnter_.bind(this);
2923
3057
  this.boundMouseLeaveHandler = this.handleMouseLeave_.bind(this);
2924
3058
  this.forElement_.addEventListener('mouseenter', this.boundMouseEnterHandler,
2925
3059
  false);
2926
3060
  this.forElement_.addEventListener('click', this.boundMouseEnterHandler,
2927
3061
  false);
3062
+ this.forElement_.addEventListener('blur', this.boundMouseLeaveHandler);
3063
+ this.forElement_.addEventListener('touchstart', this.boundMouseEnterHandler,
3064
+ false);
2928
3065
  this.forElement_.addEventListener('mouseleave', this.boundMouseLeaveHandler);
2929
3066
  }
2930
3067
  }
@@ -2938,6 +3075,7 @@ MaterialTooltip.prototype.mdlDowngrade_ = function() {
2938
3075
  if (this.forElement_) {
2939
3076
  this.forElement_.removeEventListener('mouseenter', this.boundMouseEnterHandler, false);
2940
3077
  this.forElement_.removeEventListener('click', this.boundMouseEnterHandler, false);
3078
+ this.forElement_.removeEventListener('touchstart', this.boundMouseEnterHandler, false);
2941
3079
  this.forElement_.removeEventListener('mouseleave', this.boundMouseLeaveHandler);
2942
3080
  }
2943
3081
  };
@@ -3053,7 +3191,11 @@ MaterialLayout.prototype.CssClasses_ = {
3053
3191
  IS_DRAWER_OPEN: 'is-visible',
3054
3192
  IS_ACTIVE: 'is-active',
3055
3193
  IS_UPGRADED: 'is-upgraded',
3056
- IS_ANIMATING: 'is-animating'
3194
+ IS_ANIMATING: 'is-animating',
3195
+
3196
+ ON_LARGE_SCREEN : 'mdl-layout--large-screen-only',
3197
+ ON_SMALL_SCREEN : 'mdl-layout--small-screen-only'
3198
+
3057
3199
  };
3058
3200
 
3059
3201
  /**
@@ -3238,6 +3380,14 @@ MaterialLayout.prototype.init = function() {
3238
3380
  if (this.drawer_) {
3239
3381
  var drawerButton = document.createElement('div');
3240
3382
  drawerButton.classList.add(this.CssClasses_.DRAWER_BTN);
3383
+
3384
+ if (this.drawer_.classList.contains(this.CssClasses_.ON_LARGE_SCREEN)) {
3385
+ //If drawer has ON_LARGE_SCREEN class then add it to the drawer toggle button as well.
3386
+ drawerButton.classList.add(this.CssClasses_.ON_LARGE_SCREEN);
3387
+ } else if (this.drawer_.classList.contains(this.CssClasses_.ON_SMALL_SCREEN)) {
3388
+ //If drawer has ON_SMALL_SCREEN class then add it to the drawer toggle button as well.
3389
+ drawerButton.classList.add(this.CssClasses_.ON_SMALL_SCREEN);
3390
+ }
3241
3391
  var drawerButtonIcon = document.createElement('i');
3242
3392
  drawerButtonIcon.classList.add(this.CssClasses_.ICON);
3243
3393
  drawerButtonIcon.textContent = this.Constant_.MENU_ICON;