1 |
|
2 |
|
3 |
|
4 |
|
5 |
|
6 |
|
7 |
|
8 |
|
9 |
|
10 |
|
11 |
|
12 |
|
13 |
|
14 |
|
15 |
|
16 |
|
17 |
|
18 | (function() {
|
19 | 'use strict';
|
20 |
|
21 | |
22 |
|
23 |
|
24 |
|
25 |
|
26 |
|
27 |
|
28 |
|
29 | var MaterialMenu = function MaterialMenu(element) {
|
30 | this.element_ = element;
|
31 |
|
32 |
|
33 | this.init();
|
34 | };
|
35 | window['MaterialMenu'] = MaterialMenu;
|
36 |
|
37 | |
38 |
|
39 |
|
40 |
|
41 |
|
42 |
|
43 | MaterialMenu.prototype.Constant_ = {
|
44 |
|
45 | TRANSITION_DURATION_SECONDS: 0.3,
|
46 |
|
47 | TRANSITION_DURATION_FRACTION: 0.8,
|
48 |
|
49 |
|
50 | CLOSE_TIMEOUT: 150
|
51 | };
|
52 |
|
53 | |
54 |
|
55 |
|
56 |
|
57 |
|
58 |
|
59 | MaterialMenu.prototype.Keycodes_ = {
|
60 | ENTER: 13,
|
61 | ESCAPE: 27,
|
62 | SPACE: 32,
|
63 | UP_ARROW: 38,
|
64 | DOWN_ARROW: 40
|
65 | };
|
66 |
|
67 | |
68 |
|
69 |
|
70 |
|
71 |
|
72 |
|
73 |
|
74 |
|
75 | MaterialMenu.prototype.CssClasses_ = {
|
76 | CONTAINER: 'mdl-menu__container',
|
77 | OUTLINE: 'mdl-menu__outline',
|
78 | ITEM: 'mdl-menu__item',
|
79 | ITEM_RIPPLE_CONTAINER: 'mdl-menu__item-ripple-container',
|
80 | RIPPLE_EFFECT: 'mdl-js-ripple-effect',
|
81 | RIPPLE_IGNORE_EVENTS: 'mdl-js-ripple-effect--ignore-events',
|
82 | RIPPLE: 'mdl-ripple',
|
83 |
|
84 | IS_UPGRADED: 'is-upgraded',
|
85 | IS_VISIBLE: 'is-visible',
|
86 | IS_ANIMATING: 'is-animating',
|
87 |
|
88 | BOTTOM_LEFT: 'mdl-menu--bottom-left',
|
89 | BOTTOM_RIGHT: 'mdl-menu--bottom-right',
|
90 | TOP_LEFT: 'mdl-menu--top-left',
|
91 | TOP_RIGHT: 'mdl-menu--top-right',
|
92 | UNALIGNED: 'mdl-menu--unaligned'
|
93 | };
|
94 |
|
95 | |
96 |
|
97 |
|
98 | MaterialMenu.prototype.init = function() {
|
99 | if (this.element_) {
|
100 |
|
101 | var container = document.createElement('div');
|
102 | container.classList.add(this.CssClasses_.CONTAINER);
|
103 | this.element_.parentElement.insertBefore(container, this.element_);
|
104 | this.element_.parentElement.removeChild(this.element_);
|
105 | container.appendChild(this.element_);
|
106 | this.container_ = container;
|
107 |
|
108 |
|
109 | var outline = document.createElement('div');
|
110 | outline.classList.add(this.CssClasses_.OUTLINE);
|
111 | this.outline_ = outline;
|
112 | container.insertBefore(outline, this.element_);
|
113 |
|
114 |
|
115 | var forElId = this.element_.getAttribute('for') ||
|
116 | this.element_.getAttribute('data-mdl-for');
|
117 | var forEl = null;
|
118 | if (forElId) {
|
119 | forEl = document.getElementById(forElId);
|
120 | if (forEl) {
|
121 | this.forElement_ = forEl;
|
122 | forEl.addEventListener('click', this.handleForClick_.bind(this));
|
123 | forEl.addEventListener('keydown',
|
124 | this.handleForKeyboardEvent_.bind(this));
|
125 | }
|
126 | }
|
127 |
|
128 | var items = this.element_.querySelectorAll('.' + this.CssClasses_.ITEM);
|
129 | this.boundItemKeydown_ = this.handleItemKeyboardEvent_.bind(this);
|
130 | this.boundItemClick_ = this.handleItemClick_.bind(this);
|
131 | for (var i = 0; i < items.length; i++) {
|
132 |
|
133 | items[i].addEventListener('click', this.boundItemClick_);
|
134 |
|
135 | items[i].tabIndex = '-1';
|
136 |
|
137 | items[i].addEventListener('keydown', this.boundItemKeydown_);
|
138 | }
|
139 |
|
140 |
|
141 | if (this.element_.classList.contains(this.CssClasses_.RIPPLE_EFFECT)) {
|
142 | this.element_.classList.add(this.CssClasses_.RIPPLE_IGNORE_EVENTS);
|
143 |
|
144 | for (i = 0; i < items.length; i++) {
|
145 | var item = items[i];
|
146 |
|
147 | var rippleContainer = document.createElement('span');
|
148 | rippleContainer.classList.add(this.CssClasses_.ITEM_RIPPLE_CONTAINER);
|
149 |
|
150 | var ripple = document.createElement('span');
|
151 | ripple.classList.add(this.CssClasses_.RIPPLE);
|
152 | rippleContainer.appendChild(ripple);
|
153 |
|
154 | item.appendChild(rippleContainer);
|
155 | item.classList.add(this.CssClasses_.RIPPLE_EFFECT);
|
156 | }
|
157 | }
|
158 |
|
159 |
|
160 | if (this.element_.classList.contains(this.CssClasses_.BOTTOM_LEFT)) {
|
161 | this.outline_.classList.add(this.CssClasses_.BOTTOM_LEFT);
|
162 | }
|
163 | if (this.element_.classList.contains(this.CssClasses_.BOTTOM_RIGHT)) {
|
164 | this.outline_.classList.add(this.CssClasses_.BOTTOM_RIGHT);
|
165 | }
|
166 | if (this.element_.classList.contains(this.CssClasses_.TOP_LEFT)) {
|
167 | this.outline_.classList.add(this.CssClasses_.TOP_LEFT);
|
168 | }
|
169 | if (this.element_.classList.contains(this.CssClasses_.TOP_RIGHT)) {
|
170 | this.outline_.classList.add(this.CssClasses_.TOP_RIGHT);
|
171 | }
|
172 | if (this.element_.classList.contains(this.CssClasses_.UNALIGNED)) {
|
173 | this.outline_.classList.add(this.CssClasses_.UNALIGNED);
|
174 | }
|
175 |
|
176 | container.classList.add(this.CssClasses_.IS_UPGRADED);
|
177 | }
|
178 | };
|
179 |
|
180 | |
181 |
|
182 |
|
183 |
|
184 |
|
185 |
|
186 |
|
187 | MaterialMenu.prototype.handleForClick_ = function(evt) {
|
188 | if (this.element_ && this.forElement_) {
|
189 | var rect = this.forElement_.getBoundingClientRect();
|
190 | var forRect = this.forElement_.parentElement.getBoundingClientRect();
|
191 |
|
192 | if (this.element_.classList.contains(this.CssClasses_.UNALIGNED)) {
|
193 |
|
194 |
|
195 | } else if (this.element_.classList.contains(
|
196 | this.CssClasses_.BOTTOM_RIGHT)) {
|
197 |
|
198 | this.container_.style.right = (forRect.right - rect.right) + 'px';
|
199 | this.container_.style.top =
|
200 | this.forElement_.offsetTop + this.forElement_.offsetHeight + 'px';
|
201 | } else if (this.element_.classList.contains(this.CssClasses_.TOP_LEFT)) {
|
202 |
|
203 | this.container_.style.left = this.forElement_.offsetLeft + 'px';
|
204 | this.container_.style.bottom = (forRect.bottom - rect.top) + 'px';
|
205 | } else if (this.element_.classList.contains(this.CssClasses_.TOP_RIGHT)) {
|
206 |
|
207 | this.container_.style.right = (forRect.right - rect.right) + 'px';
|
208 | this.container_.style.bottom = (forRect.bottom - rect.top) + 'px';
|
209 | } else {
|
210 |
|
211 | this.container_.style.left = this.forElement_.offsetLeft + 'px';
|
212 | this.container_.style.top =
|
213 | this.forElement_.offsetTop + this.forElement_.offsetHeight + 'px';
|
214 | }
|
215 | }
|
216 |
|
217 | this.toggle(evt);
|
218 | };
|
219 |
|
220 | |
221 |
|
222 |
|
223 |
|
224 |
|
225 |
|
226 | MaterialMenu.prototype.handleForKeyboardEvent_ = function(evt) {
|
227 | if (this.element_ && this.container_ && this.forElement_) {
|
228 | var items = this.element_.querySelectorAll('.' + this.CssClasses_.ITEM +
|
229 | ':not([disabled])');
|
230 |
|
231 | if (items && items.length > 0 &&
|
232 | this.container_.classList.contains(this.CssClasses_.IS_VISIBLE)) {
|
233 | if (evt.keyCode === this.Keycodes_.UP_ARROW) {
|
234 | evt.preventDefault();
|
235 | items[items.length - 1].focus();
|
236 | } else if (evt.keyCode === this.Keycodes_.DOWN_ARROW) {
|
237 | evt.preventDefault();
|
238 | items[0].focus();
|
239 | }
|
240 | }
|
241 | }
|
242 | };
|
243 |
|
244 | |
245 |
|
246 |
|
247 |
|
248 |
|
249 |
|
250 | MaterialMenu.prototype.handleItemKeyboardEvent_ = function(evt) {
|
251 | if (this.element_ && this.container_) {
|
252 | var items = this.element_.querySelectorAll('.' + this.CssClasses_.ITEM +
|
253 | ':not([disabled])');
|
254 |
|
255 | if (items && items.length > 0 &&
|
256 | this.container_.classList.contains(this.CssClasses_.IS_VISIBLE)) {
|
257 | var currentIndex = Array.prototype.slice.call(items).indexOf(evt.target);
|
258 |
|
259 | if (evt.keyCode === this.Keycodes_.UP_ARROW) {
|
260 | evt.preventDefault();
|
261 | if (currentIndex > 0) {
|
262 | items[currentIndex - 1].focus();
|
263 | } else {
|
264 | items[items.length - 1].focus();
|
265 | }
|
266 | } else if (evt.keyCode === this.Keycodes_.DOWN_ARROW) {
|
267 | evt.preventDefault();
|
268 | if (items.length > currentIndex + 1) {
|
269 | items[currentIndex + 1].focus();
|
270 | } else {
|
271 | items[0].focus();
|
272 | }
|
273 | } else if (evt.keyCode === this.Keycodes_.SPACE ||
|
274 | evt.keyCode === this.Keycodes_.ENTER) {
|
275 | evt.preventDefault();
|
276 |
|
277 | var e = new MouseEvent('mousedown');
|
278 | evt.target.dispatchEvent(e);
|
279 | e = new MouseEvent('mouseup');
|
280 | evt.target.dispatchEvent(e);
|
281 |
|
282 | evt.target.click();
|
283 | } else if (evt.keyCode === this.Keycodes_.ESCAPE) {
|
284 | evt.preventDefault();
|
285 | this.hide();
|
286 | }
|
287 | }
|
288 | }
|
289 | };
|
290 |
|
291 | |
292 |
|
293 |
|
294 |
|
295 |
|
296 |
|
297 | MaterialMenu.prototype.handleItemClick_ = function(evt) {
|
298 | if (evt.target.hasAttribute('disabled')) {
|
299 | evt.stopPropagation();
|
300 | } else {
|
301 |
|
302 | this.closing_ = true;
|
303 | window.setTimeout(function(evt) {
|
304 | this.hide();
|
305 | this.closing_ = false;
|
306 | }.bind(this), (this.Constant_.CLOSE_TIMEOUT));
|
307 | }
|
308 | };
|
309 |
|
310 | |
311 |
|
312 |
|
313 |
|
314 |
|
315 |
|
316 |
|
317 |
|
318 |
|
319 | MaterialMenu.prototype.applyClip_ = function(height, width) {
|
320 | if (this.element_.classList.contains(this.CssClasses_.UNALIGNED)) {
|
321 |
|
322 | this.element_.style.clip = '';
|
323 | } else if (this.element_.classList.contains(this.CssClasses_.BOTTOM_RIGHT)) {
|
324 |
|
325 | this.element_.style.clip =
|
326 | 'rect(0 ' + width + 'px ' + '0 ' + width + 'px)';
|
327 | } else if (this.element_.classList.contains(this.CssClasses_.TOP_LEFT)) {
|
328 |
|
329 | this.element_.style.clip =
|
330 | 'rect(' + height + 'px 0 ' + height + 'px 0)';
|
331 | } else if (this.element_.classList.contains(this.CssClasses_.TOP_RIGHT)) {
|
332 |
|
333 | this.element_.style.clip = 'rect(' + height + 'px ' + width + 'px ' +
|
334 | height + 'px ' + width + 'px)';
|
335 | } else {
|
336 |
|
337 | this.element_.style.clip = '';
|
338 | }
|
339 | };
|
340 |
|
341 | |
342 |
|
343 |
|
344 |
|
345 |
|
346 |
|
347 |
|
348 | MaterialMenu.prototype.removeAnimationEndListener_ = function(evt) {
|
349 | evt.target.classList.remove(MaterialMenu.prototype.CssClasses_.IS_ANIMATING);
|
350 | };
|
351 |
|
352 | |
353 |
|
354 |
|
355 |
|
356 |
|
357 | MaterialMenu.prototype.addAnimationEndListener_ = function() {
|
358 | this.element_.addEventListener('transitionend', this.removeAnimationEndListener_);
|
359 | this.element_.addEventListener('webkitTransitionEnd', this.removeAnimationEndListener_);
|
360 | };
|
361 |
|
362 | |
363 |
|
364 |
|
365 |
|
366 |
|
367 | MaterialMenu.prototype.show = function(evt) {
|
368 | if (this.element_ && this.container_ && this.outline_) {
|
369 |
|
370 | var height = this.element_.getBoundingClientRect().height;
|
371 | var width = this.element_.getBoundingClientRect().width;
|
372 |
|
373 |
|
374 | this.container_.style.width = width + 'px';
|
375 | this.container_.style.height = height + 'px';
|
376 | this.outline_.style.width = width + 'px';
|
377 | this.outline_.style.height = height + 'px';
|
378 |
|
379 | var transitionDuration = this.Constant_.TRANSITION_DURATION_SECONDS *
|
380 | this.Constant_.TRANSITION_DURATION_FRACTION;
|
381 |
|
382 |
|
383 |
|
384 | var items = this.element_.querySelectorAll('.' + this.CssClasses_.ITEM);
|
385 | for (var i = 0; i < items.length; i++) {
|
386 | var itemDelay = null;
|
387 | if (this.element_.classList.contains(this.CssClasses_.TOP_LEFT) ||
|
388 | this.element_.classList.contains(this.CssClasses_.TOP_RIGHT)) {
|
389 | itemDelay = ((height - items[i].offsetTop - items[i].offsetHeight) /
|
390 | height * transitionDuration) + 's';
|
391 | } else {
|
392 | itemDelay = (items[i].offsetTop / height * transitionDuration) + 's';
|
393 | }
|
394 | items[i].style.transitionDelay = itemDelay;
|
395 | }
|
396 |
|
397 |
|
398 | this.applyClip_(height, width);
|
399 |
|
400 |
|
401 |
|
402 | window.requestAnimationFrame(function() {
|
403 | this.element_.classList.add(this.CssClasses_.IS_ANIMATING);
|
404 | this.element_.style.clip = 'rect(0 ' + width + 'px ' + height + 'px 0)';
|
405 | this.container_.classList.add(this.CssClasses_.IS_VISIBLE);
|
406 | }.bind(this));
|
407 |
|
408 |
|
409 | this.addAnimationEndListener_();
|
410 |
|
411 |
|
412 | var callback = function(e) {
|
413 |
|
414 |
|
415 |
|
416 |
|
417 |
|
418 |
|
419 | if (e !== evt && !this.closing_ && e.target.parentNode !== this.element_) {
|
420 | document.removeEventListener('click', callback);
|
421 | this.hide();
|
422 | }
|
423 | }.bind(this);
|
424 | document.addEventListener('click', callback);
|
425 | }
|
426 | };
|
427 | MaterialMenu.prototype['show'] = MaterialMenu.prototype.show;
|
428 |
|
429 | |
430 |
|
431 |
|
432 |
|
433 |
|
434 | MaterialMenu.prototype.hide = function() {
|
435 | if (this.element_ && this.container_ && this.outline_) {
|
436 | var items = this.element_.querySelectorAll('.' + this.CssClasses_.ITEM);
|
437 |
|
438 |
|
439 | for (var i = 0; i < items.length; i++) {
|
440 | items[i].style.removeProperty('transition-delay');
|
441 | }
|
442 |
|
443 |
|
444 | var rect = this.element_.getBoundingClientRect();
|
445 | var height = rect.height;
|
446 | var width = rect.width;
|
447 |
|
448 |
|
449 |
|
450 | this.element_.classList.add(this.CssClasses_.IS_ANIMATING);
|
451 | this.applyClip_(height, width);
|
452 | this.container_.classList.remove(this.CssClasses_.IS_VISIBLE);
|
453 |
|
454 |
|
455 | this.addAnimationEndListener_();
|
456 | }
|
457 | };
|
458 | MaterialMenu.prototype['hide'] = MaterialMenu.prototype.hide;
|
459 |
|
460 | |
461 |
|
462 |
|
463 |
|
464 |
|
465 | MaterialMenu.prototype.toggle = function(evt) {
|
466 | if (this.container_.classList.contains(this.CssClasses_.IS_VISIBLE)) {
|
467 | this.hide();
|
468 | } else {
|
469 | this.show(evt);
|
470 | }
|
471 | };
|
472 | MaterialMenu.prototype['toggle'] = MaterialMenu.prototype.toggle;
|
473 |
|
474 |
|
475 |
|
476 | componentHandler.register({
|
477 | constructor: MaterialMenu,
|
478 | classAsString: 'MaterialMenu',
|
479 | cssClass: 'mdl-js-menu',
|
480 | widget: true
|
481 | });
|
482 | })();
|