Merge pull request #2754 from ExpressLRS/merge-3.4.2-into-master
[ExpressLRS.git] / src / html / mui.js
blobee06ca543d12918efc5a0639df67c3a19f77a77b
1 (function(){function r(e,n,t){function o(i,f){if(!n[i]){if(!e[i]){var c="function"==typeof require&&require;if(!f&&c)return c(i,!0);if(u)return u(i,!0);var a=new Error("Cannot find module '"+i+"'");throw a.code="MODULE_NOT_FOUND",a}var p=n[i]={exports:{}};e[i][0].call(p.exports,function(r){var n=e[i][1][r];return o(n||r)},p,p.exports,r,e,n,t)}return n[i].exports}for(var u="function"==typeof require&&require,i=0;i<t.length;i++)o(t[i]);return o}return r})()({1:[function(require,module,exports){
2 /**
3  * MUI CSS/JS main module
4  * @module main
5  */
7 (function(win) {
8   'use strict';
10   // return if library has been loaded already
11   if (win._muiLoadedJS) return;
12   else win._muiLoadedJS = true;
13   
14   // load dependencies
15   var jqLite = require('src/js/lib/jqLite'),
16       dropdown = require('src/js/dropdown'),
17       overlay = require('src/js/overlay'),
18       ripple = require('src/js/ripple'),      
19       select = require('src/js/select'),
20       tabs = require('src/js/tabs'),
21       textfield = require('src/js/textfield');
23   // expose api
24   win.mui = {
25     overlay: overlay,
26     tabs: tabs.api
27   };
28   
29   // init libraries
30   jqLite.ready(function() {
31     textfield.initListeners();
32     select.initListeners();
33     ripple.initListeners();
34     dropdown.initListeners();
35     tabs.initListeners();
36   });
37 })(window);
39 },{"src/js/dropdown":3,"src/js/lib/jqLite":6,"src/js/overlay":8,"src/js/ripple":9,"src/js/select":10,"src/js/tabs":11,"src/js/textfield":12}],2:[function(require,module,exports){
40 /**
41  * MUI config module
42  * @module config
43  */
45 /** Define module API */
46 module.exports = {
47   /** Use debug mode */
48   debug: true
51 },{}],3:[function(require,module,exports){
52 /**
53  * MUI CSS/JS dropdown module
54  * @module dropdowns
55  */
57 'use strict';
60 var jqLite = require('./lib/jqLite'),
61     util = require('./lib/util'),
62     animationHelpers = require('./lib/animationHelpers'),
63     attrKey = 'data-mui-toggle',
64     attrSelector = '[data-mui-toggle="dropdown"]',
65     openClass = 'mui--is-open',
66     menuClass = 'mui-dropdown__menu',
67     upClass = 'mui-dropdown--up',
68     rightClass = 'mui-dropdown--right',
69     leftClass = 'mui-dropdown--left',
70     bottomClass = 'mui-dropdown__menu--bottom';
73 /**
74  * Initialize toggle element.
75  * @param {Element} toggleEl - The toggle element.
76  */
77 function initialize(toggleEl) {
78   // check flag
79   if (toggleEl._muiDropdown === true) return;
80   else toggleEl._muiDropdown = true;
82   // use type "button" to prevent form submission by default
83   var tagName = toggleEl.tagName;
84   if ((tagName === 'INPUT' || tagName === 'BUTTON')
85       && !toggleEl.hasAttribute('type')) {
86     toggleEl.type = 'button';
87   }
89   // attach click handler
90   jqLite.on(toggleEl, 'click', clickHandler);
94 /**
95  * Handle click events on dropdown toggle element.
96  * @param {Event} ev - The DOM event
97  */
98 function clickHandler(ev) {
99   // only left clicks
100   if (ev.button !== 0) return;
102   var toggleEl = this;
103   
104   // exit if toggle button is disabled
105   if (toggleEl.getAttribute('disabled') !== null) return;
107   // toggle dropdown
108   toggleDropdown(toggleEl);
113  * Toggle the dropdown.
114  * @param {Element} toggleEl - The dropdown toggle element.
115  */
116 function toggleDropdown(toggleEl) {
117   var wrapperEl = toggleEl.parentNode,
118       menuEl = toggleEl.nextElementSibling,
119       doc = wrapperEl.ownerDocument;
121   // exit if no menu element
122   if (!menuEl || !jqLite.hasClass(menuEl, menuClass)) {
123     return util.raiseError('Dropdown menu element not found');
124   }
126   // method to close dropdown
127   function closeDropdownFn() {
128     jqLite.removeClass(menuEl, openClass);
129       
130     // remove event handlers
131     jqLite.off(doc, 'click', closeDropdownFn);
132     jqLite.off(doc, 'keydown', handleKeyDownFn);
133   }
135   // close dropdown on escape key press
136   function handleKeyDownFn(ev) {
137     var key = ev.key;
138     if (key === 'Escape' || key === 'Esc') closeDropdownFn();
139   }
141   // method to open dropdown
142   function openDropdownFn() {
143     // position menu element below toggle button
144     var wrapperRect = wrapperEl.getBoundingClientRect(),
145         toggleRect = toggleEl.getBoundingClientRect();
147     // menu position
148     if (jqLite.hasClass(wrapperEl, upClass)) {
149       // up
150       jqLite.css(menuEl, {
151         'bottom': toggleRect.height + toggleRect.top - wrapperRect.top + 'px'
152       });
153     } else if (jqLite.hasClass(wrapperEl, rightClass)) {
154       // right
155       jqLite.css(menuEl, {
156         'left': toggleRect.width + 'px',
157         'top': toggleRect.top - wrapperRect.top + 'px'
158       });
159     } else if (jqLite.hasClass(wrapperEl, leftClass)) {
160       // left
161       jqLite.css(menuEl, {
162         'right': toggleRect.width + 'px',
163         'top': toggleRect.top - wrapperRect.top + 'px'
164       });
165     } else {
166       // down
167       jqLite.css(menuEl, {
168         'top': toggleRect.top - wrapperRect.top + toggleRect.height + 'px'
169       });
170     }
172     // menu alignment
173     if (jqLite.hasClass(menuEl, bottomClass)) {
174       jqLite.css(menuEl, {
175         'top': 'auto',
176         'bottom': toggleRect.top - wrapperRect.top + 'px'
177       });
178     }
179     
180     // add open class to wrapper
181     jqLite.addClass(menuEl, openClass);
183     setTimeout(function() {
184       // close dropdown when user clicks outside of menu or hits escape key
185       jqLite.on(doc, 'click', closeDropdownFn);
186       jqLite.on(doc, 'keydown', handleKeyDownFn);
187     }, 0);
188   }
190   // toggle dropdown
191   if (jqLite.hasClass(menuEl, openClass)) closeDropdownFn();
192   else openDropdownFn();
195   
196 /** Define module API */
197 module.exports = {
198   /** Initialize module listeners */
199   initListeners: function() {
200     // markup elements available when method is called
201     var elList = document.querySelectorAll(attrSelector),
202         i = elList.length;
203     while (i--) {initialize(elList[i]);}
205     // listen for new elements
206     animationHelpers.onAnimationStart('mui-dropdown-inserted', function(ev) {
207       initialize(ev.target);
208     });
209   }
212 },{"./lib/animationHelpers":4,"./lib/jqLite":6,"./lib/util":7}],4:[function(require,module,exports){
214  * MUI CSS/JS animation helper module
215  * @module lib/animationHelpers
216  */
218 'use strict';
220 var jqLite = require('./jqLite'),
221     util = require('./util'),
222     animationEvents = 'animationstart mozAnimationStart webkitAnimationStart',
223     animationCallbacks = {};
227  * Register callbacks
228  * @param {String} name - The animation name
229  * @param {Function} callbackFn = The callback function
230  */
231 function onAnimationStartFn(name, callbackFn) {
232   // get/set callback function
233   var callbacks = animationCallbacks[name];
234   if (!callbacks) callbacks = animationCallbacks[name] = [];
235   callbacks.push(callbackFn);
237   // initialize listeners
238   if (!this.init) {
239     // add css classes
240     loadCss();
242     // add listener
243     jqLite.on(document, animationEvents, animationStartHandler, true);
245     // set flag
246     this.init = true;
247   }
252  * Animation start handler
253  * @param {Event} ev - The DOM event
254  */
255 function animationStartHandler(ev) {
256   var callbacks = animationCallbacks[ev.animationName] || [],
257       i = callbacks.length;
259   // exit if a callback hasn't been registered
260   if (!i) return;
261   
262   // stop other callbacks from firing
263   ev.stopImmediatePropagation();
265   // iterate through callbacks
266   while (i--) callbacks[i](ev);
271  * Load animation css
272  */
273 function loadCss() {
274   // define rules
275   var rules = [
276     ['.mui-btn', 'mui-btn-inserted'],
277     ['[data-mui-toggle="dropdown"]', 'mui-dropdown-inserted'],
278     [
279       '.mui-btn[data-mui-toggle="dropdown"]',
280       'mui-btn-inserted,mui-dropdown-inserted'
281     ],
282     ['[data-mui-toggle="tab"]', 'mui-tab-inserted'],
283     ['.mui-textfield > input', 'mui-textfield-inserted'],
284     ['.mui-textfield > textarea', 'mui-textfield-inserted'],
285     ['.mui-select > select', 'mui-select-inserted'],
286     ['.mui-select > select ~ .mui-event-trigger', 'mui-node-inserted'],
287     ['.mui-select > select:disabled ~ .mui-event-trigger', 'mui-node-disabled']
288   ];
290   // build css
291   var css = '',
292       rule;
294   for (var i=0, m=rules.length; i < m; i++) {
295     rule = rules[i];
296     css += '@keyframes ' + rule[1];
297     css += '{from{transform:none;}to{transform:none;}}';
298     css += rule[0];
299     css += '{animation-duration:0.0001s;animation-name:' + rule[1] + ';}';
300   }
301   
302   // add CSS to DOM
303   util.loadStyle(css);
308  * Define module API
309  */
310 module.exports = {
311   animationEvents: animationEvents,
312   onAnimationStart: onAnimationStartFn
315 },{"./jqLite":6,"./util":7}],5:[function(require,module,exports){
317  * MUI CSS/JS form helpers module
318  * @module lib/forms.py
319  */
321 'use strict';
323 var jqLite = require('./jqLite');
327  * Menu position/size/scroll helper
328  * @returns {Object} Object with keys 'height', 'top', 'scrollTop'
329  */
330 function getMenuPositionalCSSFn(wrapperEl, menuEl, selectedRow) {
331   var viewHeight = document.documentElement.clientHeight,
332       numRows = menuEl.children.length;
334   // determine menu height
335   var h = parseInt(menuEl.offsetHeight),
336       height = Math.min(h, viewHeight);
338   // determine row height
339   var p = parseInt(jqLite.css(menuEl, 'padding-top')),
340       rowHeight = (h - 2 * p) / numRows;
342   // determine 'top'
343   var top, initTop, minTop, maxTop;
345   initTop = -1 * selectedRow * rowHeight;
346   minTop = -1 * wrapperEl.getBoundingClientRect().top;
347   maxTop = (viewHeight - height) + minTop;
349   top = Math.min(Math.max(initTop, minTop), maxTop);
351   // determine 'scrollTop'
352   var scrollTop = 0,
353       scrollIdeal,
354       scrollMax;
356   if (h > viewHeight) {
357     scrollIdeal = top + p + selectedRow * rowHeight;
358     scrollMax = numRows * rowHeight + 2 * p - height;
359     scrollTop = Math.min(scrollIdeal, scrollMax);
360   }
362   return {
363     'height': height + 'px',
364     'top': top + 'px',
365     'scrollTop': scrollTop
366   };
370 /** Define module API */
371 module.exports = {
372   getMenuPositionalCSS: getMenuPositionalCSSFn
375 },{"./jqLite":6}],6:[function(require,module,exports){
377  * MUI CSS/JS jqLite module
378  * @module lib/jqLite
379  */
381 'use strict';
385  * Add a class to an element.
386  * @param {Element} element - The DOM element.
387  * @param {string} cssClasses - Space separated list of class names.
388  */
389 function jqLiteAddClass(element, cssClasses) {
390   if (!cssClasses || !element.setAttribute) return;
392   var existingClasses = _getExistingClasses(element),
393       splitClasses = cssClasses.split(' '),
394       cssClass;
396   for (var i=0; i < splitClasses.length; i++) {
397     cssClass = splitClasses[i].trim();
398     if (existingClasses.indexOf(' ' + cssClass + ' ') === -1) {
399       existingClasses += cssClass + ' ';
400     }
401   }
402   
403   element.setAttribute('class', existingClasses.trim());
408  * Get or set CSS properties.
409  * @param {Element} element - The DOM element.
410  * @param {string} [name] - The property name.
411  * @param {string} [value] - The property value.
412  */
413 function jqLiteCss(element, name, value) {
414   // Return full style object
415   if (name === undefined) {
416     return getComputedStyle(element);
417   }
419   var nameType = jqLiteType(name);
421   // Set multiple values
422   if (nameType === 'object') {
423     for (var key in name) element.style[_camelCase(key)] = name[key];
424     return;
425   }
427   // Set a single value
428   if (nameType === 'string' && value !== undefined) {
429     element.style[_camelCase(name)] = value;
430   }
432   var styleObj = getComputedStyle(element),
433       isArray = (jqLiteType(name) === 'array');
435   // Read single value
436   if (!isArray) return _getCurrCssProp(element, name, styleObj);
438   // Read multiple values
439   var outObj = {},
440       key;
442   for (var i=0; i < name.length; i++) {
443     key = name[i];
444     outObj[key] = _getCurrCssProp(element, key, styleObj);
445   }
447   return outObj;
452  * Check if element has class.
453  * @param {Element} element - The DOM element.
454  * @param {string} cls - The class name string.
455  */
456 function jqLiteHasClass(element, cls) {
457   if (!cls || !element.getAttribute) return false;
458   return (_getExistingClasses(element).indexOf(' ' + cls + ' ') > -1);
463  * Return the type of a variable.
464  * @param {} somevar - The JavaScript variable.
465  */
466 function jqLiteType(somevar) {
467   // handle undefined
468   if (somevar === undefined) return 'undefined';
470   // handle others (of type [object <Type>])
471   var typeStr = Object.prototype.toString.call(somevar);
472   if (typeStr.indexOf('[object ') === 0) {
473     return typeStr.slice(8, -1).toLowerCase();
474   } else {
475     throw new Error("MUI: Could not understand type: " + typeStr);
476   }    
481  * Attach an event handler to a DOM element
482  * @param {Element} element - The DOM element.
483  * @param {string} events - Space separated event names.
484  * @param {Function} callback - The callback function.
485  * @param {Boolean} useCapture - Use capture flag.
486  */
487 function jqLiteOn(element, events, callback, useCapture) {
488   useCapture = (useCapture === undefined) ? false : useCapture;
490   var cache = element._muiEventCache = element._muiEventCache || {};  
492   events.split(' ').map(function(event) {
493     // add to DOM
494     element.addEventListener(event, callback, useCapture);
496     // add to cache
497     cache[event] = cache[event] || [];
498     cache[event].push([callback, useCapture]);
499   });
504  * Remove an event handler from a DOM element
505  * @param {Element} element - The DOM element.
506  * @param {string} events - Space separated event names.
507  * @param {Function} callback - The callback function.
508  * @param {Boolean} useCapture - Use capture flag.
509  */
510 function jqLiteOff(element, events, callback, useCapture) {
511   useCapture = (useCapture === undefined) ? false : useCapture;
513   // remove from cache
514   var cache = element._muiEventCache = element._muiEventCache || {},
515       argsList,
516       args,
517       i;
519   events.split(' ').map(function(event) {
520     argsList = cache[event] || [];
522     i = argsList.length;
523     while (i--) {
524       args = argsList[i];
526       // remove all events if callback is undefined
527       if (callback === undefined ||
528           (args[0] === callback && args[1] === useCapture)) {
530         // remove from cache
531         argsList.splice(i, 1);
532         
533         // remove from DOM
534         element.removeEventListener(event, args[0], args[1]);
535       }
536     }
537   });
542  * Attach an event hander which will only execute once per element per event
543  * @param {Element} element - The DOM element.
544  * @param {string} events - Space separated event names.
545  * @param {Function} callback - The callback function.
546  * @param {Boolean} useCapture - Use capture flag.
547  */
548 function jqLiteOne(element, events, callback, useCapture) {
549   events.split(' ').map(function(event) {
550     jqLiteOn(element, event, function onFn(ev) {
551       // execute callback
552       if (callback) callback.apply(this, arguments);
554       // remove wrapper
555       jqLiteOff(element, event, onFn, useCapture);
556     }, useCapture);
557   });
562  * Get or set horizontal scroll position
563  * @param {Element} element - The DOM element
564  * @param {number} [value] - The scroll position
565  */
566 function jqLiteScrollLeft(element, value) {
567   var win = window;
569   // get
570   if (value === undefined) {
571     if (element === win) {
572       var docEl = document.documentElement;
573       return (win.pageXOffset || docEl.scrollLeft) - (docEl.clientLeft || 0);
574     } else {
575       return element.scrollLeft;
576     }
577   }
579   // set
580   if (element === win) win.scrollTo(value, jqLiteScrollTop(win));
581   else element.scrollLeft = value;
586  * Get or set vertical scroll position
587  * @param {Element} element - The DOM element
588  * @param {number} value - The scroll position
589  */
590 function jqLiteScrollTop(element, value) {
591   var win = window;
593   // get
594   if (value === undefined) {
595     if (element === win) {
596       var docEl = document.documentElement;
597       return (win.pageYOffset || docEl.scrollTop) - (docEl.clientTop || 0);
598     } else {
599       return element.scrollTop;
600     }
601   }
603   // set
604   if (element === win) win.scrollTo(jqLiteScrollLeft(win), value);
605   else element.scrollTop = value;
610  * Return object representing top/left offset and element height/width.
611  * @param {Element} element - The DOM element.
612  */
613 function jqLiteOffset(element) {
614   var win = window,
615       rect = element.getBoundingClientRect(),
616       scrollTop = jqLiteScrollTop(win),
617       scrollLeft = jqLiteScrollLeft(win);
619   return {
620     top: rect.top + scrollTop,
621     left: rect.left + scrollLeft,
622     height: rect.height,
623     width: rect.width
624   };
629  * Attach a callback to the DOM ready event listener
630  * @param {Function} fn - The callback function.
631  */
632 function jqLiteReady(fn) {
633   var done = false,
634       top = true,
635       doc = document,
636       win = doc.defaultView,
637       root = doc.documentElement,
638       add = doc.addEventListener ? 'addEventListener' : 'attachEvent',
639       rem = doc.addEventListener ? 'removeEventListener' : 'detachEvent',
640       pre = doc.addEventListener ? '' : 'on';
642   var init = function(e) {
643     if (e.type == 'readystatechange' && doc.readyState != 'complete') {
644       return;
645     }
647     (e.type == 'load' ? win : doc)[rem](pre + e.type, init, false);
648     if (!done && (done = true)) fn.call(win, e.type || e);
649   };
651   var poll = function() {
652     try { root.doScroll('left'); } catch(e) { setTimeout(poll, 50); return; }
653     init('poll');
654   };
656   if (doc.readyState == 'complete') {
657     fn.call(win, 'lazy');
658   } else {
659     if (doc.createEventObject && root.doScroll) {
660       try { top = !win.frameElement; } catch(e) { }
661       if (top) poll();
662     }
663     doc[add](pre + 'DOMContentLoaded', init, false);
664     doc[add](pre + 'readystatechange', init, false);
665     win[add](pre + 'load', init, false);
666   }
671  * Remove classes from a DOM element
672  * @param {Element} element - The DOM element.
673  * @param {string} cssClasses - Space separated list of class names.
674  */
675 function jqLiteRemoveClass(element, cssClasses) {
676   if (!cssClasses || !element.setAttribute) return;
678   var existingClasses = _getExistingClasses(element),
679       splitClasses = cssClasses.split(' '),
680       cssClass;
681   
682   for (var i=0; i < splitClasses.length; i++) {
683     cssClass = splitClasses[i].trim();
684     while (existingClasses.indexOf(' ' + cssClass + ' ') >= 0) {
685       existingClasses = existingClasses.replace(' ' + cssClass + ' ', ' ');
686     }
687   }
689   element.setAttribute('class', existingClasses.trim());
693 // ------------------------------
694 // Utilities
695 // ------------------------------
696 var SPECIAL_CHARS_REGEXP = /([\:\-\_]+(.))/g,
697     MOZ_HACK_REGEXP = /^moz([A-Z])/,
698     ESCAPE_REGEXP = /([.*+?^=!:${}()|\[\]\/\\])/g;
701 function _getExistingClasses(element) {
702   var classes = (element.getAttribute('class') || '').replace(/[\n\t]/g, '');
703   return ' ' + classes + ' ';
707 function _camelCase(name) {
708   return name.
709     replace(SPECIAL_CHARS_REGEXP, function(_, separator, letter, offset) {
710       return offset ? letter.toUpperCase() : letter;
711     }).
712     replace(MOZ_HACK_REGEXP, 'Moz$1');
716 function _escapeRegExp(string) {
717   return string.replace(ESCAPE_REGEXP, "\\$1");
721 function _getCurrCssProp(elem, name, computed) {
722   var ret;
724   // try computed style
725   ret = computed.getPropertyValue(name);
727   // try style attribute (if element is not attached to document)
728   if (ret === '' && !elem.ownerDocument) ret = elem.style[_camelCase(name)];
730   return ret;
735  * Module API
736  */
737 module.exports = {
738   /** Add classes */
739   addClass: jqLiteAddClass,
741   /** Get or set CSS properties */
742   css: jqLiteCss,
744   /** Check for class */
745   hasClass: jqLiteHasClass,
747   /** Remove event handlers */
748   off: jqLiteOff,
750   /** Return offset values */
751   offset: jqLiteOffset,
753   /** Add event handlers */
754   on: jqLiteOn,
756   /** Add an execute-once event handler */
757   one: jqLiteOne,
759   /** DOM ready event handler */
760   ready: jqLiteReady,
762   /** Remove classes */
763   removeClass: jqLiteRemoveClass,
765   /** Check JavaScript variable instance type */
766   type: jqLiteType,
768   /** Get or set horizontal scroll position */
769   scrollLeft: jqLiteScrollLeft,
771   /** Get or set vertical scroll position */
772   scrollTop: jqLiteScrollTop
775 },{}],7:[function(require,module,exports){
777  * MUI CSS/JS utilities module
778  * @module lib/util
779  */
781 'use strict';
784 var config = require('../config'),
785     jqLite = require('./jqLite'),
786     scrollLock = 0,
787     scrollLockCls = 'mui-scroll-lock',
788     scrollLockPos,
789     scrollStyleEl,
790     scrollEventHandler,
791     _scrollBarWidth,
792     _supportsPointerEvents;
795 scrollEventHandler = function(ev) {
796   // stop propagation on window scroll events
797   if (!ev.target.tagName) ev.stopImmediatePropagation();
802  * Logging function
803  */
804 function logFn() {
805   var win = window;
806   
807   if (config.debug && typeof win.console !== "undefined") {
808     try {
809       win.console.log.apply(win.console, arguments);
810     } catch (a) {
811       var e = Array.prototype.slice.call(arguments);
812       win.console.log(e.join("\n"));
813     }
814   }
819  * Load CSS text in new stylesheet
820  * @param {string} cssText - The css text.
821  */
822 function loadStyleFn(cssText) {
823   var doc = document,
824       head;
825   
826   // copied from jQuery 
827   head = doc.head ||
828     doc.getElementsByTagName('head')[0] ||
829     doc.documentElement;
830   
831   var e = doc.createElement('style');
832   e.type = 'text/css';
833   
834   if (e.styleSheet) e.styleSheet.cssText = cssText;
835   else e.appendChild(doc.createTextNode(cssText));
836   
837   // add to document
838   head.insertBefore(e, head.firstChild);
839   
840   return e;
845  * Raise an error
846  * @param {string} msg - The error message.
847  */
848 function raiseErrorFn(msg, useConsole) {
849   if (useConsole) {
850     if (typeof console !== 'undefined') console.warn('MUI Warning: ' + msg);
851   } else {
852     throw new Error('MUI: ' + msg);
853   }
858  * Convert Classname object, with class as key and true/false as value, to an
859  * class string.
860  * @param  {Object} classes The classes
861  * @return {String}         class string
862  */
863 function classNamesFn(classes) {
864   var cs = '';
865   for (var i in classes) {
866     cs += (classes[i]) ? i + ' ' : '';
867   }
868   return cs.trim();
873  * Check if client supports pointer events.
874  */
875 function supportsPointerEventsFn() {
876   // check cache
877   if (_supportsPointerEvents !== undefined) return _supportsPointerEvents;
878   
879   var element = document.createElement('x');
880   element.style.cssText = 'pointer-events:auto';
881   _supportsPointerEvents = (element.style.pointerEvents === 'auto');
882   return _supportsPointerEvents;
887  * Create callback closure.
888  * @param {Object} instance - The object instance.
889  * @param {String} funcName - The name of the callback function.
890  */
891 function callbackFn(instance, funcName) {
892   return function() {instance[funcName].apply(instance, arguments);};
897  * Dispatch event.
898  * @param {Element} element - The DOM element.
899  * @param {String} eventType - The event type.
900  * @param {Boolean} bubbles=true - If true, event bubbles.
901  * @param {Boolean} cancelable=true = If true, event is cancelable
902  * @param {Object} [data] - Data to add to event object
903  */
904 function dispatchEventFn(element, eventType, bubbles, cancelable, data) {
905   var ev = document.createEvent('HTMLEvents'),
906       bubbles = (bubbles !== undefined) ? bubbles : true,
907       cancelable = (cancelable !== undefined) ? cancelable : true,
908       k;
910   ev.initEvent(eventType, bubbles, cancelable);
911   
912   // add data to event object
913   if (data) for (k in data) ev[k] = data[k];
914   
915   // dispatch
916   if (element) element.dispatchEvent(ev);
917   
918   return ev;
923  * Turn on window scroll lock.
924  */
925 function enableScrollLockFn() {
926   // increment counter
927   scrollLock += 1;
928   
929   // add lock
930   if (scrollLock === 1) {
931     var doc = document,
932         win = window,
933         htmlEl = doc.documentElement,
934         bodyEl = doc.body,
935         scrollBarWidth = getScrollBarWidth(),
936         cssProps,
937         cssStr,
938         x;
940     // define scroll lock class dynamically
941     cssProps = ['overflow:hidden'];
943     if (scrollBarWidth) {
944       // scrollbar-y
945       if (htmlEl.scrollHeight > htmlEl.clientHeight) {
946         x = parseInt(jqLite.css(bodyEl, 'padding-right')) + scrollBarWidth;
947         cssProps.push('padding-right:' + x + 'px');
948       }
949     
950       // scrollbar-x
951       if (htmlEl.scrollWidth > htmlEl.clientWidth) {
952         x = parseInt(jqLite.css(bodyEl, 'padding-bottom')) + scrollBarWidth;
953         cssProps.push('padding-bottom:' + x + 'px');
954       }
955     }
957     // define css class dynamically
958     cssStr = '.' + scrollLockCls + '{';
959     cssStr += cssProps.join(' !important;') + ' !important;}';
960     scrollStyleEl = loadStyleFn(cssStr);
962     // cancel 'scroll' event listener callbacks
963     jqLite.on(win, 'scroll', scrollEventHandler, true);
965     // add scroll lock
966     scrollLockPos = {left: jqLite.scrollLeft(win), top: jqLite.scrollTop(win)};
967     jqLite.addClass(bodyEl, scrollLockCls);
968   }
973  * Turn off window scroll lock.
974  * @param {Boolean} resetPos - Reset scroll position to original value.
975  */
976 function disableScrollLockFn(resetPos) {
977   // ignore
978   if (scrollLock === 0) return;
980   // decrement counter
981   scrollLock -= 1;
983   // remove lock 
984   if (scrollLock === 0) {
985     // remove scroll lock and delete style element
986     jqLite.removeClass(document.body, scrollLockCls);
988     // restore scroll position
989     if (resetPos) window.scrollTo(scrollLockPos.left, scrollLockPos.top);
991     // restore scroll event listeners
992     jqLite.off(window, 'scroll', scrollEventHandler, true);
994     // delete style element (deferred for Firefox Quantum bugfix)
995     setTimeout(function() {
996       scrollStyleEl.parentNode.removeChild(scrollStyleEl);      
997     }, 0);
998   }
1002  * Return scroll bar width.
1003  */
1004 var getScrollBarWidth = function() {
1005   // check cache
1006   if (_scrollBarWidth !== undefined) return _scrollBarWidth;
1007   
1008   // calculate scroll bar width
1009   var doc = document,
1010       bodyEl = doc.body,
1011       el = doc.createElement('div');
1013   el.innerHTML = '<div style="width:50px;height:50px;position:absolute;' + 
1014     'left:-50px;top:-50px;overflow:auto;"><div style="width:1px;' + 
1015     'height:100px;"></div></div>';
1016   el = el.firstChild;
1017   bodyEl.appendChild(el);
1018   _scrollBarWidth = el.offsetWidth - el.clientWidth;
1019   bodyEl.removeChild(el);
1021   return _scrollBarWidth;
1026  * requestAnimationFrame polyfilled
1027  * @param {Function} callback - The callback function
1028  */
1029 function requestAnimationFrameFn(callback) {
1030   var fn = window.requestAnimationFrame;
1031   if (fn) fn(callback);
1032   else setTimeout(callback, 0);
1037  * Define the module API
1038  */
1039 module.exports = {
1040   /** Create callback closures */
1041   callback: callbackFn,
1042   
1043   /** Classnames object to string */
1044   classNames: classNamesFn,
1046   /** Disable scroll lock */
1047   disableScrollLock: disableScrollLockFn,
1049   /** Dispatch event */
1050   dispatchEvent: dispatchEventFn,
1051   
1052   /** Enable scroll lock */
1053   enableScrollLock: enableScrollLockFn,
1055   /** Log messages to the console when debug is turned on */
1056   log: logFn,
1058   /** Load CSS text as new stylesheet */
1059   loadStyle: loadStyleFn,
1061   /** Raise MUI error */
1062   raiseError: raiseErrorFn,
1064   /** Request animation frame */
1065   requestAnimationFrame: requestAnimationFrameFn,
1067   /** Support Pointer Events check */
1068   supportsPointerEvents: supportsPointerEventsFn
1071 },{"../config":2,"./jqLite":6}],8:[function(require,module,exports){
1073  * MUI CSS/JS overlay module
1074  * @module overlay
1075  */
1077 'use strict';
1080 var util = require('./lib/util'),
1081     jqLite = require('./lib/jqLite'),
1082     overlayId = 'mui-overlay',
1083     bodyClass = 'mui--overflow-hidden',
1084     iosRegex = /(iPad|iPhone|iPod)/g,
1085     activeElement;
1089  * Turn overlay on/off.
1090  * @param {string} action - Turn overlay "on"/"off".
1091  * @param {object} [options]
1092  * @config {boolean} [keyboard] - If true, close when escape key is pressed.
1093  * @config {boolean} [static] - If false, close when backdrop is clicked.
1094  * @config {Function} [onclose] - Callback function to execute on close
1095  * @param {Element} [childElement] - Child element to add to overlay.
1096  */
1097 function overlayFn(action) {
1098   var overlayEl;
1099   
1100   if (action === 'on') {
1101     // extract arguments
1102     var arg, options, childElement;
1103     
1104     // pull options and childElement from arguments
1105     for (var i=arguments.length - 1; i > 0; i--) {
1106       arg = arguments[i];
1108       if (jqLite.type(arg) === 'object') options = arg;
1109       if (arg instanceof Element && arg.nodeType === 1) childElement = arg;
1110     }
1112     // option defaults
1113     options = options || {};
1114     if (options.keyboard === undefined) options.keyboard = true;
1115     if (options.static === undefined) options.static = false;
1116     
1117     // execute method
1118     overlayEl = overlayOn(options, childElement);
1119     
1120   } else if (action === 'off') {
1121     overlayEl = overlayOff();
1123   } else {
1124     // raise error
1125     util.raiseError("Expecting 'on' or 'off'");
1127   }
1129   return overlayEl;
1134  * Turn on overlay.
1135  * @param {object} options - Overlay options.
1136  * @param {Element} childElement - The child element.
1137  */
1138 function overlayOn(options, childElement) {
1139   var doc = document,
1140       bodyEl = doc.body,
1141       overlayEl = doc.getElementById(overlayId);
1143   // cache activeElement
1144   if (doc.activeElement) activeElement = doc.activeElement;
1146   // add overlay
1147   util.enableScrollLock();
1149   if (!overlayEl) {
1150     // create overlayEl
1151     overlayEl = doc.createElement('div');
1152     overlayEl.setAttribute('id', overlayId);
1153     overlayEl.setAttribute('tabindex', '-1');
1154     
1155     // add child element
1156     if (childElement) overlayEl.appendChild(childElement);
1158     bodyEl.appendChild(overlayEl);
1159     
1160   } else {
1161     // remove existing children
1162     while (overlayEl.firstChild) overlayEl.removeChild(overlayEl.firstChild);
1163     
1164     // add child element
1165     if (childElement) overlayEl.appendChild(childElement);
1166   }
1168   // iOS bugfix
1169   if (iosRegex.test(navigator.userAgent)) {
1170     jqLite.css(overlayEl, 'cursor', 'pointer');
1171   }
1173   // handle options
1174   if (options.keyboard) addKeyupHandler();
1175   else removeKeyupHandler();
1177   if (options.static) removeClickHandler(overlayEl);
1178   else addClickHandler(overlayEl);
1180   // attach options
1181   overlayEl.muiOptions = options;
1183   // focus overlay element
1184   overlayEl.focus();
1186   return overlayEl;
1191  * Turn off overlay.
1192  */
1193 function overlayOff() {
1194   var overlayEl = document.getElementById(overlayId),
1195       callbackFn;
1197   if (overlayEl) {
1198     // remove children
1199     while (overlayEl.firstChild) overlayEl.removeChild(overlayEl.firstChild);
1201     // remove overlay element
1202     overlayEl.parentNode.removeChild(overlayEl);
1204     // callback reference
1205     callbackFn = overlayEl.muiOptions.onclose;
1207     // remove click handler
1208     removeClickHandler(overlayEl);
1209   }
1211   util.disableScrollLock();
1213   // remove keyup handler
1214   removeKeyupHandler();
1216   // return focus to activeElement
1217   if (activeElement) activeElement.focus();
1219   // execute callback
1220   if (callbackFn) callbackFn();
1222   return overlayEl;
1227  * Add keyup handler.
1228  */
1229 function addKeyupHandler() {
1230   jqLite.on(document, 'keyup', onKeyup);
1235  * Remove keyup handler.
1236  */
1237 function removeKeyupHandler() {
1238   jqLite.off(document, 'keyup', onKeyup);
1243  * Teardown overlay when escape key is pressed.
1244  */
1245 function onKeyup(ev) {
1246   if (ev.keyCode === 27) overlayOff();
1251  * Add click handler.
1252  */
1253 function addClickHandler(overlayEl) {
1254   jqLite.on(overlayEl, 'click', onClick);
1259  * Remove click handler.
1260  */
1261 function removeClickHandler(overlayEl) {
1262   jqLite.off(overlayEl, 'click', onClick);
1267  * Teardown overlay when backdrop is clicked.
1268  */
1269 function onClick(ev) {
1270   if (ev.target.id === overlayId) overlayOff();
1274 /** Define module API */
1275 module.exports = overlayFn;
1277 },{"./lib/jqLite":6,"./lib/util":7}],9:[function(require,module,exports){
1279  * MUI CSS/JS ripple module
1280  * @module ripple
1281  */
1283 'use strict';
1286 var jqLite = require('./lib/jqLite'),
1287     util = require('./lib/util'),
1288     animationHelpers = require('./lib/animationHelpers'),
1289     supportsTouch = 'ontouchstart' in document.documentElement,
1290     mouseDownEvents = (supportsTouch) ? 'touchstart' : 'mousedown',
1291     mouseUpEvents = (supportsTouch) ? 'touchend' : 'mouseup mouseleave';
1295  * Add ripple effects to button element.
1296  * @param {Element} buttonEl - The button element.
1297  */
1298 function initialize(buttonEl) {
1299   // check flag
1300   if (buttonEl._muiRipple === true) return;
1301   else buttonEl._muiRipple = true;
1303   // exit if element is INPUT (doesn't support absolute positioned children)
1304   if (buttonEl.tagName === 'INPUT') return;
1306   // attach event handler
1307   jqLite.on(buttonEl, mouseDownEvents, mouseDownHandler);
1312  * MouseDown Event handler.
1313  * @param {Event} ev - The DOM event
1314  */
1315 function mouseDownHandler(ev) {
1316   // only left clicks
1317   if (ev.type === 'mousedown' && ev.button !== 0) return;
1319   var buttonEl = this,
1320       rippleEl = buttonEl._rippleEl;
1322   // exit if button is disabled
1323   if (buttonEl.disabled) return;
1325   if (!rippleEl) {
1326     // add ripple container (to avoid https://github.com/muicss/mui/issues/169)
1327     var el = document.createElement('span');
1328     el.className = 'mui-btn__ripple-container';
1329     el.innerHTML = '<span class="mui-ripple"></span>';
1330     buttonEl.appendChild(el);
1332     // cache reference to ripple element
1333     rippleEl = buttonEl._rippleEl = el.children[0];
1335     // add mouseup handler on first-click
1336     jqLite.on(buttonEl, mouseUpEvents, mouseUpHandler);
1337   }
1339   // get ripple element offset values and (x, y) position of click
1340   var offset = jqLite.offset(buttonEl),
1341       clickEv = (ev.type === 'touchstart') ? ev.touches[0] : ev,
1342       radius,
1343       diameter;
1345   // calculate radius
1346   radius = Math.sqrt(offset.height * offset.height + 
1347                      offset.width * offset.width);
1349   diameter = radius * 2 + 'px';
1351   // set position and dimensions
1352   jqLite.css(rippleEl, {
1353     width: diameter,
1354     height: diameter,
1355     top: Math.round(clickEv.pageY - offset.top - radius) + 'px',
1356     left: Math.round(clickEv.pageX - offset.left - radius) + 'px'
1357   });
1359   jqLite.removeClass(rippleEl, 'mui--is-animating');
1360   jqLite.addClass(rippleEl, 'mui--is-visible');
1362   // start animation
1363   util.requestAnimationFrame(function() {
1364     jqLite.addClass(rippleEl, 'mui--is-animating');
1365   });
1370  * MouseUp event handler.
1371  * @param {Event} ev - The DOM event
1372  */
1373 function mouseUpHandler(ev) {
1374   // get ripple element
1375   var rippleEl = this._rippleEl;
1377   // allow a repaint to occur before removing class so animation shows for
1378   // tap events
1379   util.requestAnimationFrame(function() {
1380     jqLite.removeClass(rippleEl, 'mui--is-visible');
1381   });
1385 /** Define module API */
1386 module.exports = {
1387   /** Initialize module listeners */
1388   initListeners: function() {
1389     // markup elements available when method is called
1390     var elList = document.getElementsByClassName('mui-btn'),
1391         i = elList.length;
1392     while (i--) initialize(elList[i]);
1394     // listen for new elements
1395     animationHelpers.onAnimationStart('mui-btn-inserted', function(ev) {
1396       initialize(ev.target);
1397     });
1398   }
1401 },{"./lib/animationHelpers":4,"./lib/jqLite":6,"./lib/util":7}],10:[function(require,module,exports){
1403  * MUI CSS/JS select module
1404  * @module forms/select
1405  */
1407 'use strict';
1410 var jqLite = require('./lib/jqLite'),
1411     util = require('./lib/util'),
1412     animationHelpers = require('./lib/animationHelpers'),
1413     formlib = require('./lib/forms'),
1414     wrapperClass = 'mui-select',
1415     cssSelector = '.mui-select > select',
1416     menuClass = 'mui-select__menu',
1417     selectedClass = 'mui--is-selected',
1418     disabledClass = 'mui--is-disabled',
1419     doc = document,
1420     win = window;
1424  * Initialize select element.
1425  * @param {Element} selectEl - The select element.
1426  */
1427 function initialize(selectEl) {
1428   // check flag
1429   if (selectEl._muiSelect === true) return;
1430   else selectEl._muiSelect = true;
1432   // use default behavior on touch devices
1433   if ('ontouchstart' in doc.documentElement) return;
1435   // NOTE: To get around cross-browser issues with <select> behavior we will
1436   //       defer focus to the parent element and handle events there
1438   var wrapperEl = selectEl.parentNode;
1440   // exit if use-default
1441   if (jqLite.hasClass(wrapperEl, 'mui-select--use-default')) return;
1443   // initialize variables
1444   wrapperEl._selectEl = selectEl;
1445   wrapperEl._menu = null;
1446   wrapperEl._q = '';
1447   wrapperEl._qTimeout = null;
1449   // make wrapper tab focusable, remove tab focus from <select>
1450   if (!selectEl.disabled) wrapperEl.tabIndex = 0;
1451   selectEl.tabIndex = -1;
1453   // prevent built-in menu from opening on <select>
1454   jqLite.on(selectEl, 'mousedown', onInnerMouseDown);
1456   // attach event listeners for custom menu
1457   jqLite.on(wrapperEl, 'click', onWrapperClick);
1458   jqLite.on(wrapperEl, 'blur focus', onWrapperBlurOrFocus);
1459   jqLite.on(wrapperEl, 'keydown', onWrapperKeyDown);
1460   jqLite.on(wrapperEl, 'keypress', onWrapperKeyPress);
1462   // add element to detect 'disabled' change (using sister element due to 
1463   // IE/Firefox issue
1464   var el = document.createElement('div');
1465   el.className = 'mui-event-trigger';
1466   wrapperEl.appendChild(el);
1468   // handle 'disabled' add/remove
1469   jqLite.on(el, animationHelpers.animationEvents, function(ev) {
1470     var parentEl = ev.target.parentNode;
1472     // no need to propagate
1473     ev.stopPropagation();
1475     if (ev.animationName === 'mui-node-disabled') {
1476       parentEl.removeAttribute('tabIndex');
1477     } else {
1478       parentEl.tabIndex = 0;
1479     }    
1480   });
1485  * Disable default dropdown on mousedown.
1486  * @param {Event} ev - The DOM event
1487  */
1488 function onInnerMouseDown(ev) {
1489   // only left clicks
1490   if (ev.button !== 0) return;
1491   
1492   // prevent built-in menu from opening
1493   ev.preventDefault();
1498  * Dispatch focus and blur events on inner <select> element.
1499  * @param {Event} ev - The DOM event
1500  */
1501 function onWrapperBlurOrFocus(ev) {
1502   util.dispatchEvent(this._selectEl, ev.type, false, false);
1507  * Handle keydown events when wrapper is focused
1508  **/
1509 function onWrapperKeyDown(ev) {
1510   if (ev.defaultPrevented) return;
1512   var keyCode = ev.keyCode,
1513       menu = this._menu;
1515   if (!menu) {
1516     // spacebar, down, up
1517     if (keyCode === 32 || keyCode === 38 || keyCode === 40) {
1518       ev.preventDefault();
1520       // open custom menu
1521       renderMenu(this);
1522     }
1524   } else {
1525     // tab
1526     if (keyCode === 9) return menu.destroy();
1527   
1528     // escape | up | down | enter
1529     if (keyCode === 27 || keyCode === 40 || keyCode === 38 || keyCode === 13) {
1530       ev.preventDefault();
1531     }
1533     if (keyCode === 27) {
1534       // escape
1535       menu.destroy();
1536     } else if (keyCode === 40) {
1537       // up
1538       menu.increment();
1539     } else if (keyCode === 38) {
1540       // down
1541       menu.decrement();
1542     } else if (keyCode === 13) {
1543       // enter
1544       menu.selectCurrent();
1545       menu.destroy();
1546     }
1547   }
1553  */
1554 function onWrapperKeyPress(ev) {
1555   var menu = this._menu;
1557   // exit if default prevented or menu is closed
1558   if (ev.defaultPrevented || !menu) return;
1560   // handle query timer
1561   var self = this;
1562   clearTimeout(this._qTimeout);
1563   this._q += ev.key;
1564   this._qTimeout = setTimeout(function() {self._q = '';}, 300);
1566   // select first match alphabetically
1567   var prefixRegex = new RegExp('^' + this._q, 'i'),
1568       itemArray = menu.itemArray,
1569       pos;
1571   for (pos in itemArray) {
1572     if (prefixRegex.test(itemArray[pos].innerText)) {
1573       menu.selectPos(pos);
1574       break;
1575     }
1576   }
1581  * Handle click events on wrapper element.
1582  * @param {Event} ev - The DOM event
1583  */
1584 function onWrapperClick(ev) {
1585   // only left clicks, check default and disabled flags
1586   if (ev.button !== 0 || this._selectEl.disabled) return;
1588   // focus wrapper
1589   this.focus();
1591   // open menu
1592   renderMenu(this);
1597  * Render options menu
1598  */
1599 function renderMenu(wrapperEl) {
1600   // check instance
1601   if (wrapperEl._menu) return;
1603   // render custom menu
1604   wrapperEl._menu = new Menu(wrapperEl, wrapperEl._selectEl, function() {
1605     wrapperEl._menu = null;  // de-reference instance
1606     wrapperEl.focus();
1607   });
1612  * Creates a new Menu
1613  * @class
1614  */
1615 function Menu(wrapperEl, selectEl, wrapperCallbackFn) {
1616   // add scroll lock
1617   util.enableScrollLock();
1619   // instance variables
1620   this.itemArray = [];
1621   this.origPos = null;
1622   this.currentPos = null;
1623   this.selectEl = selectEl;
1624   this.wrapperEl = wrapperEl;
1626   var res = this._createMenuEl(wrapperEl, selectEl),
1627       menuEl = this.menuEl = res[0];
1629   var cb = util.callback;
1631   this.onClickCB = cb(this, 'onClick');
1632   this.destroyCB = cb(this, 'destroy');
1633   this.wrapperCallbackFn = wrapperCallbackFn;
1635   // add to DOM
1636   wrapperEl.appendChild(this.menuEl);
1638   // set position
1639   var props = formlib.getMenuPositionalCSS(
1640     wrapperEl,
1641     menuEl,
1642     res[1]
1643   );
1644   
1645   jqLite.css(menuEl, props);
1646   jqLite.scrollTop(menuEl, props.scrollTop);
1648   // attach event handlers
1649   var destroyCB = this.destroyCB;
1650   jqLite.on(menuEl, 'click', this.onClickCB);
1651   jqLite.on(win, 'resize', destroyCB);
1653   // attach event handler after current event loop exits
1654   setTimeout(function() {jqLite.on(doc, 'click', destroyCB);}, 0);
1659  * Create menu element
1660  * @param {Element} selectEl - The select element
1661  */
1662 Menu.prototype._createMenuEl = function(wrapperEl, selectEl) {
1663   var menuEl = doc.createElement('div'),
1664       childEls = selectEl.children,
1665       itemArray = this.itemArray,
1666       itemPos = 0,
1667       origPos = -1,
1668       selectedPos = 0,
1669       selectedRow = 0,
1670       numRows = 0,
1671       docFrag = document.createDocumentFragment(),  // for speed
1672       loopEl,
1673       rowEl,
1674       optionEls,
1675       inGroup,
1676       i,
1677       iMax,
1678       j,
1679       jMax;
1681   menuEl.className = menuClass;
1683   for (i=0, iMax=childEls.length; i < iMax; i++) {
1684     loopEl = childEls[i];
1686     if (loopEl.tagName === 'OPTGROUP') {
1687       // add row item to menu
1688       rowEl = doc.createElement('div');
1689       rowEl.textContent = loopEl.label;
1690       rowEl.className = 'mui-optgroup__label';
1691       docFrag.appendChild(rowEl);
1693       inGroup = true;
1694       optionEls = loopEl.children;
1695     } else {
1696       inGroup = false;
1697       optionEls = [loopEl];
1698     }
1700     // loop through option elements
1701     for (j=0, jMax=optionEls.length; j < jMax; j++) {
1702       loopEl = optionEls[j];
1704       // add row item to menu
1705       rowEl = doc.createElement('div');
1706       rowEl.textContent = loopEl.textContent;
1708       // handle optgroup options
1709       if (inGroup) jqLite.addClass(rowEl, 'mui-optgroup__option');
1711       if (loopEl.hidden) {
1712         continue;
1713       } else if (loopEl.disabled) {
1714         // do not attach muiIndex to disable <option> elements to make them
1715         // unselectable.
1716         jqLite.addClass(rowEl, disabledClass);
1717       } else {
1718         rowEl._muiIndex = loopEl.index;
1719         rowEl._muiPos = itemPos;
1721         // handle selected options
1722         if (loopEl.selected) {
1723           selectedRow = numRows;
1724           origPos = itemPos;
1725           selectedPos = itemPos;
1726         }
1728         // add to item array
1729         itemArray.push(rowEl);
1730         itemPos += 1;
1731       }
1733       docFrag.appendChild(rowEl);
1734       numRows += 1;
1735     }
1736   }
1738   // add rows to menu
1739   menuEl.appendChild(docFrag);
1741   // save indices
1742   this.origPos = origPos;
1743   this.currentPos = selectedPos;
1745   // paint selectedPos
1746   if (itemArray.length) jqLite.addClass(itemArray[selectedPos], selectedClass);
1748   return [menuEl, selectedRow];
1753  * Handle click events on menu element.
1754  * @param {Event} ev - The DOM event
1755  */
1756 Menu.prototype.onClick = function(ev) {
1757   // don't allow events to bubble
1758   ev.stopPropagation();
1760   var item = ev.target,
1761       index = item._muiIndex;
1763   // ignore clicks on non-items                                               
1764   if (index === undefined) return;
1766   // select option
1767   this.currentPos = item._muiPos;
1768   this.selectCurrent();
1770   // destroy menu
1771   this.destroy();
1776  * Increment selected item
1777  */
1778 Menu.prototype.increment = function() {
1779   if (this.currentPos === this.itemArray.length - 1) return;
1781   // un-select old row
1782   jqLite.removeClass(this.itemArray[this.currentPos], selectedClass);
1784   // select new row
1785   this.currentPos += 1;
1786   jqLite.addClass(this.itemArray[this.currentPos], selectedClass);
1791  * Decrement selected item
1792  */
1793 Menu.prototype.decrement = function() {
1794   if (this.currentPos === 0) return;
1796   // un-select old row
1797   jqLite.removeClass(this.itemArray[this.currentPos], selectedClass);
1799   // select new row
1800   this.currentPos -= 1;
1801   jqLite.addClass(this.itemArray[this.currentPos], selectedClass);
1806  * Select current item
1807  */
1808 Menu.prototype.selectCurrent = function() {
1809   if (this.currentPos !== this.origPos) {
1810     this.selectEl.selectedIndex = this.itemArray[this.currentPos]._muiIndex;
1812     // trigger change and input events
1813     util.dispatchEvent(this.selectEl, 'change', true, false);
1814     util.dispatchEvent(this.selectEl, 'input', true, false);
1815   }
1820  * Select item at position
1821  */
1822 Menu.prototype.selectPos = function(pos) {
1823   // un-select old row                                                      
1824   jqLite.removeClass(this.itemArray[this.currentPos], selectedClass);
1826   // select new row
1827   this.currentPos = pos;
1828   var itemEl = this.itemArray[pos];
1829   jqLite.addClass(itemEl, selectedClass);
1831   // scroll (if necessary)
1832   var menuEl = this.menuEl,
1833       itemRect = itemEl.getBoundingClientRect();
1835   if (itemRect.top < 0) {
1836     // menu item is hidden above visible window
1837     menuEl.scrollTop = menuEl.scrollTop + itemRect.top - 5;
1838   } else if (itemRect.top > window.innerHeight) {
1839     // menu item is hidden below visible window
1840     menuEl.scrollTop = menuEl.scrollTop + 
1841       (itemRect.top + itemRect.height - window.innerHeight) + 5;
1842   }
1847  * Destroy menu and detach event handlers
1848  */
1849 Menu.prototype.destroy = function() {
1850   // remove scroll lock
1851   util.disableScrollLock(true);
1853   // remove event handlers
1854   jqLite.off(this.menuEl, 'click', this.clickCallbackFn);
1855   jqLite.off(doc, 'click', this.destroyCB);
1856   jqLite.off(win, 'resize', this.destroyCB);
1858   // remove element and execute wrapper callback
1859   var parentNode = this.menuEl.parentNode;
1860   if (parentNode) {
1861     parentNode.removeChild(this.menuEl);
1862     this.wrapperCallbackFn();
1863   }
1867 /** Define module API */
1868 module.exports = {
1869   /** Initialize module listeners */
1870   initListeners: function() {
1871     // markup elements available when method is called
1872     var elList = doc.querySelectorAll(cssSelector),
1873         i = elList.length;
1874     while (i--) initialize(elList[i]);
1876     // listen for mui-node-inserted events
1877     animationHelpers.onAnimationStart('mui-select-inserted', function(ev) {
1878       initialize(ev.target);
1879     });
1880   }
1883 },{"./lib/animationHelpers":4,"./lib/forms":5,"./lib/jqLite":6,"./lib/util":7}],11:[function(require,module,exports){
1885  * MUI CSS/JS tabs module
1886  * @module tabs
1887  */
1889 'use strict';
1892 var jqLite = require('./lib/jqLite'),
1893     util = require('./lib/util'),
1894     animationHelpers = require('./lib/animationHelpers'),
1895     attrKey = 'data-mui-toggle',
1896     attrSelector = '[' + attrKey + '="tab"]',
1897     controlsAttrKey = 'data-mui-controls',
1898     activeClass = 'mui--is-active',
1899     showstartKey = 'mui.tabs.showstart',
1900     showendKey = 'mui.tabs.showend',
1901     hidestartKey = 'mui.tabs.hidestart',
1902     hideendKey = 'mui.tabs.hideend';
1906  * Initialize the toggle element
1907  * @param {Element} toggleEl - The toggle element.
1908  */
1909 function initialize(toggleEl) {
1910   // check flag
1911   if (toggleEl._muiTabs === true) return;
1912   else toggleEl._muiTabs = true;
1914   // attach click handler
1915   jqLite.on(toggleEl, 'click', clickHandler);
1920  * Handle clicks on the toggle element.
1921  * @param {Event} ev - The DOM event.
1922  */
1923 function clickHandler(ev) {
1924   // only left clicks
1925   if (ev.button !== 0) return;
1927   var toggleEl = this;
1929   // exit if toggle element is disabled
1930   if (toggleEl.getAttribute('disabled') !== null) return;
1932   activateTab(toggleEl);
1937  * Activate the tab controlled by the toggle element.
1938  * @param {Element} toggleEl - The toggle element.
1939  */
1940 function activateTab(currToggleEl) {
1941   var currTabEl = currToggleEl.parentNode,
1942       currPaneId = currToggleEl.getAttribute(controlsAttrKey),
1943       currPaneEl = document.getElementById(currPaneId),
1944       prevTabEl,
1945       prevPaneEl,
1946       prevPaneId,
1947       prevToggleEl,
1948       currData,
1949       prevData,
1950       ev1,
1951       ev2,
1952       cssSelector;
1954   // exit if already active
1955   if (jqLite.hasClass(currTabEl, activeClass)) return;
1957   // raise error if pane doesn't exist
1958   if (!currPaneEl) util.raiseError('Tab pane "' + currPaneId + '" not found');
1960   // get previous pane
1961   prevPaneEl = getActiveSibling(currPaneEl);
1963   if (prevPaneEl) {
1964     prevPaneId = prevPaneEl.id;
1966     // get previous toggle and tab elements
1967     cssSelector = '[' + controlsAttrKey + '="' + prevPaneId + '"]';
1968     prevToggleEl = document.querySelectorAll(cssSelector)[0];
1969     prevTabEl = prevToggleEl.parentNode;
1970   }
1971   
1972   // define event data
1973   currData = {paneId: currPaneId, relatedPaneId: prevPaneId};
1974   prevData = {paneId: prevPaneId, relatedPaneId: currPaneId};
1976   // dispatch 'hidestart', 'showstart' events
1977   ev1 = util.dispatchEvent(prevToggleEl, hidestartKey, true, true, prevData);
1978   ev2 = util.dispatchEvent(currToggleEl, showstartKey, true, true, currData);
1980   // let events bubble
1981   setTimeout(function() {
1982     // exit if either event was canceled
1983     if (ev1.defaultPrevented || ev2.defaultPrevented) return;
1985     // de-activate previous
1986     if (prevTabEl) jqLite.removeClass(prevTabEl, activeClass);
1987     if (prevPaneEl) jqLite.removeClass(prevPaneEl, activeClass);
1989     // activate current
1990     jqLite.addClass(currTabEl, activeClass);
1991     jqLite.addClass(currPaneEl, activeClass);
1993     // dispatch 'hideend', 'showend' events
1994     util.dispatchEvent(prevToggleEl, hideendKey, true, false, prevData);
1995     util.dispatchEvent(currToggleEl, showendKey, true, false, currData);
1996   }, 0);
2000 /** 
2001  * Get previous active sibling.
2002  * @param {Element} el - The anchor element.
2003  */
2004 function getActiveSibling(el) {
2005   var elList = el.parentNode.children,
2006       q = elList.length,
2007       activeEl = null,
2008       tmpEl;
2010   while (q-- && !activeEl) {
2011     tmpEl = elList[q];
2012     if (tmpEl !== el && jqLite.hasClass(tmpEl, activeClass)) activeEl = tmpEl
2013   }
2015   return activeEl;
2019 /** Define module API */
2020 module.exports = {
2021   /** Initialize module listeners */
2022   initListeners: function() {
2023     // markup elements available when method is called
2024     var elList = document.querySelectorAll(attrSelector),
2025         i = elList.length;
2026     while (i--) {initialize(elList[i]);}
2027     
2028     animationHelpers.onAnimationStart('mui-tab-inserted', function(ev) {
2029       initialize(ev.target);
2030     });
2031   },
2032   
2033   /** External API */
2034   api: {
2035     activate: function(paneId) {
2036       var cssSelector = '[' + controlsAttrKey + '=' + paneId + ']',
2037           toggleEl = document.querySelectorAll(cssSelector);
2039       if (!toggleEl.length) {
2040         util.raiseError('Tab control for pane "' + paneId + '" not found');
2041       }
2043       activateTab(toggleEl[0]);
2044     }
2045   }
2048 },{"./lib/animationHelpers":4,"./lib/jqLite":6,"./lib/util":7}],12:[function(require,module,exports){
2050  * MUI CSS/JS form-control module
2051  * @module forms/form-control
2052  */
2054 'use strict';
2057 var jqLite = require('./lib/jqLite'),
2058     util = require('./lib/util'),
2059     animlib = require('./lib/animationHelpers'),
2060     cssSelector = '.mui-textfield > input, .mui-textfield > textarea',
2061     floatingLabelClass = 'mui-textfield--float-label';
2064 var touchedClass = 'mui--is-touched',  // hasn't lost focus yet
2065     untouchedClass = 'mui--is-untouched',
2066     pristineClass = 'mui--is-pristine',  // user hasn't interacted yet 
2067     dirtyClass = 'mui--is-dirty',
2068     emptyClass = 'mui--is-empty',  // control is empty
2069     notEmptyClass = 'mui--is-not-empty';
2073  * Initialize input element.
2074  * @param {Element} inputEl - The input element.
2075  */
2076 function initialize(inputEl) {
2077   // check flag
2078   if (inputEl._muiTextfield === true) return;
2079   else inputEl._muiTextfield = true;
2081   // add initial control state classes
2082   if (inputEl.value.length) jqLite.addClass(inputEl, notEmptyClass);
2083   else jqLite.addClass(inputEl, emptyClass);
2085   jqLite.addClass(inputEl, untouchedClass + ' ' + pristineClass);
2087   // replace `untouched` with `touched` when control loses focus
2088   jqLite.on(inputEl, 'blur', function blurHandler () {
2089     // ignore if event is a window blur
2090     if (document.activeElement === inputEl) return;
2092     // replace class and remove event handler
2093     jqLite.removeClass(inputEl, untouchedClass);
2094     jqLite.addClass(inputEl, touchedClass);
2095     jqLite.off(inputEl, 'blur', blurHandler);
2096   });
2098   // replace `pristine` with `dirty` when user interacts with control
2099   jqLite.one(inputEl, 'input change', function() {
2100     jqLite.removeClass(inputEl, pristineClass);
2101     jqLite.addClass(inputEl, dirtyClass);
2102   });
2104   // add change handler
2105   jqLite.on(inputEl, 'input change', inputHandler);
2110  * Handle input events.
2111  */
2112 function inputHandler() {
2113   var inputEl = this;
2115   if (inputEl.value.length) {
2116     jqLite.removeClass(inputEl, emptyClass);
2117     jqLite.addClass(inputEl, notEmptyClass);
2118   } else {
2119     jqLite.removeClass(inputEl, notEmptyClass);
2120     jqLite.addClass(inputEl, emptyClass)
2121   }
2125 /** Define module API */
2126 module.exports = {
2127   /** Initialize input elements */
2128   initialize: initialize,
2129   
2130   /** Initialize module listeners */
2131   initListeners: function() {
2132     var doc = document;
2133     
2134     // markup elements available when method is called
2135     var elList = doc.querySelectorAll(cssSelector),
2136         i = elList.length;
2137     while (i--) initialize(elList[i]);
2139     // listen for new elements
2140     animlib.onAnimationStart('mui-textfield-inserted', function(ev) {
2141       initialize(ev.target);
2142     });
2144     // add transition css for floating labels
2145     setTimeout(function() {
2146       var css = '.mui-textfield.mui-textfield--float-label > label {' + [
2147         '-webkit-transition',
2148         '-moz-transition',
2149         '-o-transition',
2150         'transition',
2151         ''
2152       ].join(':all .15s ease-out;') + '}';
2153       
2154       util.loadStyle(css);
2155     }, 150);
2157     // pointer-events shim for floating labels
2158     if (util.supportsPointerEvents() === false) {
2159       jqLite.on(doc, 'click', function(ev) {
2160         var targetEl = ev.target;
2162         if (targetEl.tagName === 'LABEL' &&
2163             jqLite.hasClass(targetEl.parentNode, floatingLabelClass)) {
2164           var inputEl = targetEl.previousElementSibling;
2165           if (inputEl) inputEl.focus();
2166         }
2167       });
2168     }
2169   }
2172 },{"./lib/animationHelpers":4,"./lib/jqLite":6,"./lib/util":7}]},{},[1]);