UNPKG

muicss

Version:

Lightweight CSS framework based on Google's Material Design guidelines

2,024 lines (1,590 loc) 54.1 kB
(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){ /** * MUI CSS/JS main module * @module main */ (function(win) { 'use strict'; // return if library has been loaded already if (win._muiLoadedJS) return; else win._muiLoadedJS = true; // load dependencies var jqLite = require('src/js/lib/jqLite'), dropdown = require('src/js/dropdown'), overlay = require('src/js/overlay'), ripple = require('src/js/ripple'), select = require('src/js/select'), tabs = require('src/js/tabs'), textfield = require('src/js/textfield'); // expose api win.mui = { overlay: overlay, tabs: tabs.api }; // init libraries jqLite.ready(function() { textfield.initListeners(); select.initListeners(); ripple.initListeners(); dropdown.initListeners(); tabs.initListeners(); }); })(window); },{"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){ /** * MUI config module * @module config */ /** Define module API */ module.exports = { /** Use debug mode */ debug: true }; },{}],3:[function(require,module,exports){ /** * MUI CSS/JS dropdown module * @module dropdowns */ 'use strict'; var jqLite = require('./lib/jqLite'), util = require('./lib/util'), animationHelpers = require('./lib/animationHelpers'), attrKey = 'data-mui-toggle', attrSelector = '[data-mui-toggle="dropdown"]', openClass = 'mui--is-open', menuClass = 'mui-dropdown__menu', upClass = 'mui-dropdown--up', rightClass = 'mui-dropdown--right', leftClass = 'mui-dropdown--left', bottomClass = 'mui-dropdown__menu--bottom'; /** * Initialize toggle element. * @param {Element} toggleEl - The toggle element. */ function initialize(toggleEl) { // check flag if (toggleEl._muiDropdown === true) return; else toggleEl._muiDropdown = true; // use type "button" to prevent form submission by default var tagName = toggleEl.tagName; if ((tagName === 'INPUT' || tagName === 'BUTTON') && !toggleEl.hasAttribute('type')) { toggleEl.type = 'button'; } // attach click handler jqLite.on(toggleEl, 'click', clickHandler); } /** * Handle click events on dropdown toggle element. * @param {Event} ev - The DOM event */ function clickHandler(ev) { // only left clicks if (ev.button !== 0) return; var toggleEl = this; // exit if toggle button is disabled if (toggleEl.getAttribute('disabled') !== null) return; // toggle dropdown toggleDropdown(toggleEl); } /** * Toggle the dropdown. * @param {Element} toggleEl - The dropdown toggle element. */ function toggleDropdown(toggleEl) { var wrapperEl = toggleEl.parentNode, menuEl = toggleEl.nextElementSibling, doc = wrapperEl.ownerDocument; // exit if no menu element if (!menuEl || !jqLite.hasClass(menuEl, menuClass)) { return util.raiseError('Dropdown menu element not found'); } // method to close dropdown function closeDropdownFn() { jqLite.removeClass(menuEl, openClass); // remove event handlers jqLite.off(doc, 'click', closeDropdownFn); jqLite.off(doc, 'keydown', handleKeyDownFn); } // close dropdown on escape key press function handleKeyDownFn(ev) { var key = ev.key; if (key === 'Escape' || key === 'Esc') closeDropdownFn(); } // method to open dropdown function openDropdownFn() { // position menu element below toggle button var wrapperRect = wrapperEl.getBoundingClientRect(), toggleRect = toggleEl.getBoundingClientRect(); // menu position if (jqLite.hasClass(wrapperEl, upClass)) { // up jqLite.css(menuEl, { 'bottom': toggleRect.height + toggleRect.top - wrapperRect.top + 'px' }); } else if (jqLite.hasClass(wrapperEl, rightClass)) { // right jqLite.css(menuEl, { 'left': toggleRect.width + 'px', 'top': toggleRect.top - wrapperRect.top + 'px' }); } else if (jqLite.hasClass(wrapperEl, leftClass)) { // left jqLite.css(menuEl, { 'right': toggleRect.width + 'px', 'top': toggleRect.top - wrapperRect.top + 'px' }); } else { // down jqLite.css(menuEl, { 'top': toggleRect.top - wrapperRect.top + toggleRect.height + 'px' }); } // menu alignment if (jqLite.hasClass(menuEl, bottomClass)) { jqLite.css(menuEl, { 'top': 'auto', 'bottom': toggleRect.top - wrapperRect.top + 'px' }); } // add open class to wrapper jqLite.addClass(menuEl, openClass); setTimeout(function() { // close dropdown when user clicks outside of menu or hits escape key jqLite.on(doc, 'click', closeDropdownFn); jqLite.on(doc, 'keydown', handleKeyDownFn); }, 0); } // toggle dropdown if (jqLite.hasClass(menuEl, openClass)) closeDropdownFn(); else openDropdownFn(); } /** Define module API */ module.exports = { /** Initialize module listeners */ initListeners: function() { // markup elements available when method is called var elList = document.querySelectorAll(attrSelector), i = elList.length; while (i--) {initialize(elList[i]);} // listen for new elements animationHelpers.onAnimationStart('mui-dropdown-inserted', function(ev) { initialize(ev.target); }); } }; },{"./lib/animationHelpers":4,"./lib/jqLite":6,"./lib/util":7}],4:[function(require,module,exports){ /** * MUI CSS/JS animation helper module * @module lib/animationHelpers */ 'use strict'; var jqLite = require('./jqLite'), util = require('./util'), animationEvents = 'animationstart mozAnimationStart webkitAnimationStart', animationCallbacks = {}; /** * Register callbacks * @param {String} name - The animation name * @param {Function} callbackFn = The callback function */ function onAnimationStartFn(name, callbackFn) { // get/set callback function var callbacks = animationCallbacks[name]; if (!callbacks) callbacks = animationCallbacks[name] = []; callbacks.push(callbackFn); // initialize listeners if (!this.init) { // add css classes loadCss(); // add listener jqLite.on(document, animationEvents, animationStartHandler, true); // set flag this.init = true; } } /** * Animation start handler * @param {Event} ev - The DOM event */ function animationStartHandler(ev) { var callbacks = animationCallbacks[ev.animationName] || [], i = callbacks.length; // exit if a callback hasn't been registered if (!i) return; // stop other callbacks from firing ev.stopImmediatePropagation(); // iterate through callbacks while (i--) callbacks[i](ev); } /** * Load animation css */ function loadCss() { // define rules var rules = [ ['.mui-btn', 'mui-btn-inserted'], ['[data-mui-toggle="dropdown"]', 'mui-dropdown-inserted'], [ '.mui-btn[data-mui-toggle="dropdown"]', 'mui-btn-inserted,mui-dropdown-inserted' ], ['[data-mui-toggle="tab"]', 'mui-tab-inserted'], ['.mui-textfield > input', 'mui-textfield-inserted'], ['.mui-textfield > textarea', 'mui-textfield-inserted'], ['.mui-select > select', 'mui-select-inserted'], ['.mui-select > select ~ .mui-event-trigger', 'mui-node-inserted'], ['.mui-select > select:disabled ~ .mui-event-trigger', 'mui-node-disabled'] ]; // build css var css = '', rule; for (var i=0, m=rules.length; i < m; i++) { rule = rules[i]; css += '@keyframes ' + rule[1]; css += '{from{transform:none;}to{transform:none;}}'; css += rule[0]; css += '{animation-duration:0.0001s;animation-name:' + rule[1] + ';}'; } // add CSS to DOM util.loadStyle(css); } /** * Define module API */ module.exports = { animationEvents: animationEvents, onAnimationStart: onAnimationStartFn } },{"./jqLite":6,"./util":7}],5:[function(require,module,exports){ /** * MUI CSS/JS form helpers module * @module lib/forms.py */ 'use strict'; var jqLite = require('./jqLite'); /** * Menu position/size/scroll helper * @returns {Object} Object with keys 'height', 'top', 'scrollTop' */ function getMenuPositionalCSSFn(wrapperEl, menuEl, selectedRow) { var viewHeight = document.documentElement.clientHeight, numRows = menuEl.children.length; // determine menu height var h = parseInt(menuEl.offsetHeight), height = Math.min(h, viewHeight); // determine row height var p = parseInt(jqLite.css(menuEl, 'padding-top')), rowHeight = (h - 2 * p) / numRows; // determine 'top' var top, initTop, minTop, maxTop; initTop = -1 * selectedRow * rowHeight; minTop = -1 * wrapperEl.getBoundingClientRect().top; maxTop = (viewHeight - height) + minTop; top = Math.min(Math.max(initTop, minTop), maxTop); // determine 'scrollTop' var scrollTop = 0, scrollIdeal, scrollMax; if (h > viewHeight) { scrollIdeal = top + p + selectedRow * rowHeight; scrollMax = numRows * rowHeight + 2 * p - height; scrollTop = Math.min(scrollIdeal, scrollMax); } return { 'height': height + 'px', 'top': top + 'px', 'scrollTop': scrollTop }; } /** Define module API */ module.exports = { getMenuPositionalCSS: getMenuPositionalCSSFn }; },{"./jqLite":6}],6:[function(require,module,exports){ /** * MUI CSS/JS jqLite module * @module lib/jqLite */ 'use strict'; /** * Add a class to an element. * @param {Element} element - The DOM element. * @param {string} cssClasses - Space separated list of class names. */ function jqLiteAddClass(element, cssClasses) { if (!cssClasses || !element.setAttribute) return; var existingClasses = _getExistingClasses(element), splitClasses = cssClasses.split(' '), cssClass; for (var i=0; i < splitClasses.length; i++) { cssClass = splitClasses[i].trim(); if (existingClasses.indexOf(' ' + cssClass + ' ') === -1) { existingClasses += cssClass + ' '; } } element.setAttribute('class', existingClasses.trim()); } /** * Get or set CSS properties. * @param {Element} element - The DOM element. * @param {string} [name] - The property name. * @param {string} [value] - The property value. */ function jqLiteCss(element, name, value) { // Return full style object if (name === undefined) { return getComputedStyle(element); } var nameType = jqLiteType(name); // Set multiple values if (nameType === 'object') { for (var key in name) element.style[_camelCase(key)] = name[key]; return; } // Set a single value if (nameType === 'string' && value !== undefined) { element.style[_camelCase(name)] = value; } var styleObj = getComputedStyle(element), isArray = (jqLiteType(name) === 'array'); // Read single value if (!isArray) return _getCurrCssProp(element, name, styleObj); // Read multiple values var outObj = {}, key; for (var i=0; i < name.length; i++) { key = name[i]; outObj[key] = _getCurrCssProp(element, key, styleObj); } return outObj; } /** * Check if element has class. * @param {Element} element - The DOM element. * @param {string} cls - The class name string. */ function jqLiteHasClass(element, cls) { if (!cls || !element.getAttribute) return false; return (_getExistingClasses(element).indexOf(' ' + cls + ' ') > -1); } /** * Return the type of a variable. * @param {} somevar - The JavaScript variable. */ function jqLiteType(somevar) { // handle undefined if (somevar === undefined) return 'undefined'; // handle others (of type [object <Type>]) var typeStr = Object.prototype.toString.call(somevar); if (typeStr.indexOf('[object ') === 0) { return typeStr.slice(8, -1).toLowerCase(); } else { throw new Error("MUI: Could not understand type: " + typeStr); } } /** * Attach an event handler to a DOM element * @param {Element} element - The DOM element. * @param {string} events - Space separated event names. * @param {Function} callback - The callback function. * @param {Boolean} useCapture - Use capture flag. */ function jqLiteOn(element, events, callback, useCapture) { useCapture = (useCapture === undefined) ? false : useCapture; var cache = element._muiEventCache = element._muiEventCache || {}; events.split(' ').map(function(event) { // add to DOM element.addEventListener(event, callback, useCapture); // add to cache cache[event] = cache[event] || []; cache[event].push([callback, useCapture]); }); } /** * Remove an event handler from a DOM element * @param {Element} element - The DOM element. * @param {string} events - Space separated event names. * @param {Function} callback - The callback function. * @param {Boolean} useCapture - Use capture flag. */ function jqLiteOff(element, events, callback, useCapture) { useCapture = (useCapture === undefined) ? false : useCapture; // remove from cache var cache = element._muiEventCache = element._muiEventCache || {}, argsList, args, i; events.split(' ').map(function(event) { argsList = cache[event] || []; i = argsList.length; while (i--) { args = argsList[i]; // remove all events if callback is undefined if (callback === undefined || (args[0] === callback && args[1] === useCapture)) { // remove from cache argsList.splice(i, 1); // remove from DOM element.removeEventListener(event, args[0], args[1]); } } }); } /** * Attach an event hander which will only execute once per element per event * @param {Element} element - The DOM element. * @param {string} events - Space separated event names. * @param {Function} callback - The callback function. * @param {Boolean} useCapture - Use capture flag. */ function jqLiteOne(element, events, callback, useCapture) { events.split(' ').map(function(event) { jqLiteOn(element, event, function onFn(ev) { // execute callback if (callback) callback.apply(this, arguments); // remove wrapper jqLiteOff(element, event, onFn, useCapture); }, useCapture); }); } /** * Get or set horizontal scroll position * @param {Element} element - The DOM element * @param {number} [value] - The scroll position */ function jqLiteScrollLeft(element, value) { var win = window; // get if (value === undefined) { if (element === win) { var docEl = document.documentElement; return (win.pageXOffset || docEl.scrollLeft) - (docEl.clientLeft || 0); } else { return element.scrollLeft; } } // set if (element === win) win.scrollTo(value, jqLiteScrollTop(win)); else element.scrollLeft = value; } /** * Get or set vertical scroll position * @param {Element} element - The DOM element * @param {number} value - The scroll position */ function jqLiteScrollTop(element, value) { var win = window; // get if (value === undefined) { if (element === win) { var docEl = document.documentElement; return (win.pageYOffset || docEl.scrollTop) - (docEl.clientTop || 0); } else { return element.scrollTop; } } // set if (element === win) win.scrollTo(jqLiteScrollLeft(win), value); else element.scrollTop = value; } /** * Return object representing top/left offset and element height/width. * @param {Element} element - The DOM element. */ function jqLiteOffset(element) { var win = window, rect = element.getBoundingClientRect(), scrollTop = jqLiteScrollTop(win), scrollLeft = jqLiteScrollLeft(win); return { top: rect.top + scrollTop, left: rect.left + scrollLeft, height: rect.height, width: rect.width }; } /** * Attach a callback to the DOM ready event listener * @param {Function} fn - The callback function. */ function jqLiteReady(fn) { var done = false, top = true, doc = document, win = doc.defaultView, root = doc.documentElement, add = doc.addEventListener ? 'addEventListener' : 'attachEvent', rem = doc.addEventListener ? 'removeEventListener' : 'detachEvent', pre = doc.addEventListener ? '' : 'on'; var init = function(e) { if (e.type == 'readystatechange' && doc.readyState != 'complete') { return; } (e.type == 'load' ? win : doc)[rem](pre + e.type, init, false); if (!done && (done = true)) fn.call(win, e.type || e); }; var poll = function() { try { root.doScroll('left'); } catch(e) { setTimeout(poll, 50); return; } init('poll'); }; if (doc.readyState == 'complete') { fn.call(win, 'lazy'); } else { if (doc.createEventObject && root.doScroll) { try { top = !win.frameElement; } catch(e) { } if (top) poll(); } doc[add](pre + 'DOMContentLoaded', init, false); doc[add](pre + 'readystatechange', init, false); win[add](pre + 'load', init, false); } } /** * Remove classes from a DOM element * @param {Element} element - The DOM element. * @param {string} cssClasses - Space separated list of class names. */ function jqLiteRemoveClass(element, cssClasses) { if (!cssClasses || !element.setAttribute) return; var existingClasses = _getExistingClasses(element), splitClasses = cssClasses.split(' '), cssClass; for (var i=0; i < splitClasses.length; i++) { cssClass = splitClasses[i].trim(); while (existingClasses.indexOf(' ' + cssClass + ' ') >= 0) { existingClasses = existingClasses.replace(' ' + cssClass + ' ', ' '); } } element.setAttribute('class', existingClasses.trim()); } // ------------------------------ // Utilities // ------------------------------ var SPECIAL_CHARS_REGEXP = /([\:\-\_]+(.))/g, MOZ_HACK_REGEXP = /^moz([A-Z])/, ESCAPE_REGEXP = /([.*+?^=!:${}()|\[\]\/\\])/g; function _getExistingClasses(element) { var classes = (element.getAttribute('class') || '').replace(/[\n\t]/g, ''); return ' ' + classes + ' '; } function _camelCase(name) { return name. replace(SPECIAL_CHARS_REGEXP, function(_, separator, letter, offset) { return offset ? letter.toUpperCase() : letter; }). replace(MOZ_HACK_REGEXP, 'Moz$1'); } function _escapeRegExp(string) { return string.replace(ESCAPE_REGEXP, "\\$1"); } function _getCurrCssProp(elem, name, computed) { var ret; // try computed style ret = computed.getPropertyValue(name); // try style attribute (if element is not attached to document) if (ret === '' && !elem.ownerDocument) ret = elem.style[_camelCase(name)]; return ret; } /** * Module API */ module.exports = { /** Add classes */ addClass: jqLiteAddClass, /** Get or set CSS properties */ css: jqLiteCss, /** Check for class */ hasClass: jqLiteHasClass, /** Remove event handlers */ off: jqLiteOff, /** Return offset values */ offset: jqLiteOffset, /** Add event handlers */ on: jqLiteOn, /** Add an execute-once event handler */ one: jqLiteOne, /** DOM ready event handler */ ready: jqLiteReady, /** Remove classes */ removeClass: jqLiteRemoveClass, /** Check JavaScript variable instance type */ type: jqLiteType, /** Get or set horizontal scroll position */ scrollLeft: jqLiteScrollLeft, /** Get or set vertical scroll position */ scrollTop: jqLiteScrollTop }; },{}],7:[function(require,module,exports){ /** * MUI CSS/JS utilities module * @module lib/util */ 'use strict'; var config = require('../config'), jqLite = require('./jqLite'), scrollLock = 0, scrollLockCls = 'mui-scroll-lock', scrollLockPos, scrollStyleEl, scrollEventHandler, _scrollBarWidth, _supportsPointerEvents; scrollEventHandler = function(ev) { // stop propagation on window scroll events if (!ev.target.tagName) ev.stopImmediatePropagation(); } /** * Logging function */ function logFn() { var win = window; if (config.debug && typeof win.console !== "undefined") { try { win.console.log.apply(win.console, arguments); } catch (a) { var e = Array.prototype.slice.call(arguments); win.console.log(e.join("\n")); } } } /** * Load CSS text in new stylesheet * @param {string} cssText - The css text. */ function loadStyleFn(cssText) { var doc = document, head; // copied from jQuery head = doc.head || doc.getElementsByTagName('head')[0] || doc.documentElement; var e = doc.createElement('style'); e.type = 'text/css'; if (e.styleSheet) e.styleSheet.cssText = cssText; else e.appendChild(doc.createTextNode(cssText)); // add to document head.insertBefore(e, head.firstChild); return e; } /** * Raise an error * @param {string} msg - The error message. */ function raiseErrorFn(msg, useConsole) { if (useConsole) { if (typeof console !== 'undefined') console.warn('MUI Warning: ' + msg); } else { throw new Error('MUI: ' + msg); } } /** * Convert Classname object, with class as key and true/false as value, to an * class string. * @param {Object} classes The classes * @return {String} class string */ function classNamesFn(classes) { var cs = ''; for (var i in classes) { cs += (classes[i]) ? i + ' ' : ''; } return cs.trim(); } /** * Check if client supports pointer events. */ function supportsPointerEventsFn() { // check cache if (_supportsPointerEvents !== undefined) return _supportsPointerEvents; var element = document.createElement('x'); element.style.cssText = 'pointer-events:auto'; _supportsPointerEvents = (element.style.pointerEvents === 'auto'); return _supportsPointerEvents; } /** * Create callback closure. * @param {Object} instance - The object instance. * @param {String} funcName - The name of the callback function. */ function callbackFn(instance, funcName) { return function() {instance[funcName].apply(instance, arguments);}; } /** * Dispatch event. * @param {Element} element - The DOM element. * @param {String} eventType - The event type. * @param {Boolean} bubbles=true - If true, event bubbles. * @param {Boolean} cancelable=true = If true, event is cancelable * @param {Object} [data] - Data to add to event object */ function dispatchEventFn(element, eventType, bubbles, cancelable, data) { var ev = document.createEvent('HTMLEvents'), bubbles = (bubbles !== undefined) ? bubbles : true, cancelable = (cancelable !== undefined) ? cancelable : true, k; ev.initEvent(eventType, bubbles, cancelable); // add data to event object if (data) for (k in data) ev[k] = data[k]; // dispatch if (element) element.dispatchEvent(ev); return ev; } /** * Turn on window scroll lock. */ function enableScrollLockFn() { // increment counter scrollLock += 1; // add lock if (scrollLock === 1) { var doc = document, win = window, htmlEl = doc.documentElement, bodyEl = doc.body, scrollBarWidth = getScrollBarWidth(), cssProps, cssStr, x; // define scroll lock class dynamically cssProps = ['overflow:hidden']; if (scrollBarWidth) { // scrollbar-y if (htmlEl.scrollHeight > htmlEl.clientHeight) { x = parseInt(jqLite.css(bodyEl, 'padding-right')) + scrollBarWidth; cssProps.push('padding-right:' + x + 'px'); } // scrollbar-x if (htmlEl.scrollWidth > htmlEl.clientWidth) { x = parseInt(jqLite.css(bodyEl, 'padding-bottom')) + scrollBarWidth; cssProps.push('padding-bottom:' + x + 'px'); } } // define css class dynamically cssStr = '.' + scrollLockCls + '{'; cssStr += cssProps.join(' !important;') + ' !important;}'; scrollStyleEl = loadStyleFn(cssStr); // cancel 'scroll' event listener callbacks jqLite.on(win, 'scroll', scrollEventHandler, true); // add scroll lock scrollLockPos = {left: jqLite.scrollLeft(win), top: jqLite.scrollTop(win)}; jqLite.addClass(bodyEl, scrollLockCls); } } /** * Turn off window scroll lock. * @param {Boolean} resetPos - Reset scroll position to original value. */ function disableScrollLockFn(resetPos) { // ignore if (scrollLock === 0) return; // decrement counter scrollLock -= 1; // remove lock if (scrollLock === 0) { // remove scroll lock and delete style element jqLite.removeClass(document.body, scrollLockCls); // restore scroll position if (resetPos) window.scrollTo(scrollLockPos.left, scrollLockPos.top); // restore scroll event listeners jqLite.off(window, 'scroll', scrollEventHandler, true); // delete style element (deferred for Firefox Quantum bugfix) setTimeout(function() { scrollStyleEl.parentNode.removeChild(scrollStyleEl); }, 0); } } /** * Return scroll bar width. */ var getScrollBarWidth = function() { // check cache if (_scrollBarWidth !== undefined) return _scrollBarWidth; // calculate scroll bar width var doc = document, bodyEl = doc.body, el = doc.createElement('div'); el.innerHTML = '<div style="width:50px;height:50px;position:absolute;' + 'left:-50px;top:-50px;overflow:auto;"><div style="width:1px;' + 'height:100px;"></div></div>'; el = el.firstChild; bodyEl.appendChild(el); _scrollBarWidth = el.offsetWidth - el.clientWidth; bodyEl.removeChild(el); return _scrollBarWidth; } /** * requestAnimationFrame polyfilled * @param {Function} callback - The callback function */ function requestAnimationFrameFn(callback) { var fn = window.requestAnimationFrame; if (fn) fn(callback); else setTimeout(callback, 0); } /** * Define the module API */ module.exports = { /** Create callback closures */ callback: callbackFn, /** Classnames object to string */ classNames: classNamesFn, /** Disable scroll lock */ disableScrollLock: disableScrollLockFn, /** Dispatch event */ dispatchEvent: dispatchEventFn, /** Enable scroll lock */ enableScrollLock: enableScrollLockFn, /** Log messages to the console when debug is turned on */ log: logFn, /** Load CSS text as new stylesheet */ loadStyle: loadStyleFn, /** Raise MUI error */ raiseError: raiseErrorFn, /** Request animation frame */ requestAnimationFrame: requestAnimationFrameFn, /** Support Pointer Events check */ supportsPointerEvents: supportsPointerEventsFn }; },{"../config":2,"./jqLite":6}],8:[function(require,module,exports){ /** * MUI CSS/JS overlay module * @module overlay */ 'use strict'; var util = require('./lib/util'), jqLite = require('./lib/jqLite'), overlayId = 'mui-overlay', bodyClass = 'mui--overflow-hidden', iosRegex = /(iPad|iPhone|iPod)/g, activeElement; /** * Turn overlay on/off. * @param {string} action - Turn overlay "on"/"off". * @param {object} [options] * @config {boolean} [keyboard] - If true, close when escape key is pressed. * @config {boolean} [static] - If false, close when backdrop is clicked. * @config {Function} [onclose] - Callback function to execute on close * @param {Element} [childElement] - Child element to add to overlay. */ function overlayFn(action) { var overlayEl; if (action === 'on') { // extract arguments var arg, options, childElement; // pull options and childElement from arguments for (var i=arguments.length - 1; i > 0; i--) { arg = arguments[i]; if (jqLite.type(arg) === 'object') options = arg; if (arg instanceof Element && arg.nodeType === 1) childElement = arg; } // option defaults options = options || {}; if (options.keyboard === undefined) options.keyboard = true; if (options.static === undefined) options.static = false; // execute method overlayEl = overlayOn(options, childElement); } else if (action === 'off') { overlayEl = overlayOff(); } else { // raise error util.raiseError("Expecting 'on' or 'off'"); } return overlayEl; } /** * Turn on overlay. * @param {object} options - Overlay options. * @param {Element} childElement - The child element. */ function overlayOn(options, childElement) { var doc = document, bodyEl = doc.body, overlayEl = doc.getElementById(overlayId); // cache activeElement if (doc.activeElement) activeElement = doc.activeElement; // add overlay util.enableScrollLock(); if (!overlayEl) { // create overlayEl overlayEl = doc.createElement('div'); overlayEl.setAttribute('id', overlayId); overlayEl.setAttribute('tabindex', '-1'); // add child element if (childElement) overlayEl.appendChild(childElement); bodyEl.appendChild(overlayEl); } else { // remove existing children while (overlayEl.firstChild) overlayEl.removeChild(overlayEl.firstChild); // add child element if (childElement) overlayEl.appendChild(childElement); } // iOS bugfix if (iosRegex.test(navigator.userAgent)) { jqLite.css(overlayEl, 'cursor', 'pointer'); } // handle options if (options.keyboard) addKeyupHandler(); else removeKeyupHandler(); if (options.static) removeClickHandler(overlayEl); else addClickHandler(overlayEl); // attach options overlayEl.muiOptions = options; // focus overlay element overlayEl.focus(); return overlayEl; } /** * Turn off overlay. */ function overlayOff() { var overlayEl = document.getElementById(overlayId), callbackFn; if (overlayEl) { // remove children while (overlayEl.firstChild) overlayEl.removeChild(overlayEl.firstChild); // remove overlay element overlayEl.parentNode.removeChild(overlayEl); // callback reference callbackFn = overlayEl.muiOptions.onclose; // remove click handler removeClickHandler(overlayEl); } util.disableScrollLock(); // remove keyup handler removeKeyupHandler(); // return focus to activeElement if (activeElement) activeElement.focus(); // execute callback if (callbackFn) callbackFn(); return overlayEl; } /** * Add keyup handler. */ function addKeyupHandler() { jqLite.on(document, 'keyup', onKeyup); } /** * Remove keyup handler. */ function removeKeyupHandler() { jqLite.off(document, 'keyup', onKeyup); } /** * Teardown overlay when escape key is pressed. */ function onKeyup(ev) { if (ev.keyCode === 27) overlayOff(); } /** * Add click handler. */ function addClickHandler(overlayEl) { jqLite.on(overlayEl, 'click', onClick); } /** * Remove click handler. */ function removeClickHandler(overlayEl) { jqLite.off(overlayEl, 'click', onClick); } /** * Teardown overlay when backdrop is clicked. */ function onClick(ev) { if (ev.target.id === overlayId) overlayOff(); } /** Define module API */ module.exports = overlayFn; },{"./lib/jqLite":6,"./lib/util":7}],9:[function(require,module,exports){ /** * MUI CSS/JS ripple module * @module ripple */ 'use strict'; var jqLite = require('./lib/jqLite'), util = require('./lib/util'), animationHelpers = require('./lib/animationHelpers'), supportsTouch = 'ontouchstart' in document.documentElement, mouseDownEvents = (supportsTouch) ? 'touchstart' : 'mousedown', mouseUpEvents = (supportsTouch) ? 'touchend' : 'mouseup mouseleave'; /** * Add ripple effects to button element. * @param {Element} buttonEl - The button element. */ function initialize(buttonEl) { // check flag if (buttonEl._muiRipple === true) return; else buttonEl._muiRipple = true; // exit if element is INPUT (doesn't support absolute positioned children) if (buttonEl.tagName === 'INPUT') return; // attach event handler jqLite.on(buttonEl, mouseDownEvents, mouseDownHandler); } /** * MouseDown Event handler. * @param {Event} ev - The DOM event */ function mouseDownHandler(ev) { // only left clicks if (ev.type === 'mousedown' && ev.button !== 0) return; var buttonEl = this, rippleEl = buttonEl._rippleEl; // exit if button is disabled if (buttonEl.disabled) return; if (!rippleEl) { // add ripple container (to avoid https://github.com/muicss/mui/issues/169) var el = document.createElement('span'); el.className = 'mui-btn__ripple-container'; el.innerHTML = '<span class="mui-ripple"></span>'; buttonEl.appendChild(el); // cache reference to ripple element rippleEl = buttonEl._rippleEl = el.children[0]; // add mouseup handler on first-click jqLite.on(buttonEl, mouseUpEvents, mouseUpHandler); } // get ripple element offset values and (x, y) position of click var offset = jqLite.offset(buttonEl), clickEv = (ev.type === 'touchstart') ? ev.touches[0] : ev, radius, diameter; // calculate radius radius = Math.sqrt(offset.height * offset.height + offset.width * offset.width); diameter = radius * 2 + 'px'; // set position and dimensions jqLite.css(rippleEl, { width: diameter, height: diameter, top: Math.round(clickEv.pageY - offset.top - radius) + 'px', left: Math.round(clickEv.pageX - offset.left - radius) + 'px' }); jqLite.removeClass(rippleEl, 'mui--is-animating'); jqLite.addClass(rippleEl, 'mui--is-visible'); // start animation util.requestAnimationFrame(function() { jqLite.addClass(rippleEl, 'mui--is-animating'); }); } /** * MouseUp event handler. * @param {Event} ev - The DOM event */ function mouseUpHandler(ev) { // get ripple element var rippleEl = this._rippleEl; // allow a repaint to occur before removing class so animation shows for // tap events util.requestAnimationFrame(function() { jqLite.removeClass(rippleEl, 'mui--is-visible'); }); } /** Define module API */ module.exports = { /** Initialize module listeners */ initListeners: function() { // markup elements available when method is called var elList = document.getElementsByClassName('mui-btn'), i = elList.length; while (i--) initialize(elList[i]); // listen for new elements animationHelpers.onAnimationStart('mui-btn-inserted', function(ev) { initialize(ev.target); }); } }; },{"./lib/animationHelpers":4,"./lib/jqLite":6,"./lib/util":7}],10:[function(require,module,exports){ /** * MUI CSS/JS select module * @module forms/select */ 'use strict'; var jqLite = require('./lib/jqLite'), util = require('./lib/util'), animationHelpers = require('./lib/animationHelpers'), formlib = require('./lib/forms'), wrapperClass = 'mui-select', cssSelector = '.mui-select > select', menuClass = 'mui-select__menu', selectedClass = 'mui--is-selected', disabledClass = 'mui--is-disabled', doc = document, win = window; /** * Initialize select element. * @param {Element} selectEl - The select element. */ function initialize(selectEl) { // check flag if (selectEl._muiSelect === true) return; else selectEl._muiSelect = true; // use default behavior on touch devices if ('ontouchstart' in doc.documentElement) return; // NOTE: To get around cross-browser issues with <select> behavior we will // defer focus to the parent element and handle events there var wrapperEl = selectEl.parentNode; // exit if use-default if (jqLite.hasClass(wrapperEl, 'mui-select--use-default')) return; // initialize variables wrapperEl._selectEl = selectEl; wrapperEl._menu = null; wrapperEl._q = ''; wrapperEl._qTimeout = null; // make wrapper tab focusable, remove tab focus from <select> if (!selectEl.disabled) wrapperEl.tabIndex = 0; selectEl.tabIndex = -1; // prevent built-in menu from opening on <select> jqLite.on(selectEl, 'mousedown', onInnerMouseDown); // attach event listeners for custom menu jqLite.on(wrapperEl, 'click', onWrapperClick); jqLite.on(wrapperEl, 'blur focus', onWrapperBlurOrFocus); jqLite.on(wrapperEl, 'keydown', onWrapperKeyDown); jqLite.on(wrapperEl, 'keypress', onWrapperKeyPress); // add element to detect 'disabled' change (using sister element due to // IE/Firefox issue var el = document.createElement('div'); el.className = 'mui-event-trigger'; wrapperEl.appendChild(el); // handle 'disabled' add/remove jqLite.on(el, animationHelpers.animationEvents, function(ev) { var parentEl = ev.target.parentNode; // no need to propagate ev.stopPropagation(); if (ev.animationName === 'mui-node-disabled') { parentEl.removeAttribute('tabIndex'); } else { parentEl.tabIndex = 0; } }); } /** * Disable default dropdown on mousedown. * @param {Event} ev - The DOM event */ function onInnerMouseDown(ev) { // only left clicks if (ev.button !== 0) return; // prevent built-in menu from opening ev.preventDefault(); } /** * Dispatch focus and blur events on inner <select> element. * @param {Event} ev - The DOM event */ function onWrapperBlurOrFocus(ev) { util.dispatchEvent(this._selectEl, ev.type, false, false); } /** * Handle keydown events when wrapper is focused **/ function onWrapperKeyDown(ev) { if (ev.defaultPrevented) return; var keyCode = ev.keyCode, menu = this._menu; if (!menu) { // spacebar, down, up if (keyCode === 32 || keyCode === 38 || keyCode === 40) { ev.preventDefault(); // open custom menu renderMenu(this); } } else { // tab if (keyCode === 9) return menu.destroy(); // escape | up | down | enter if (keyCode === 27 || keyCode === 40 || keyCode === 38 || keyCode === 13) { ev.preventDefault(); } if (keyCode === 27) { // escape menu.destroy(); } else if (keyCode === 40) { // up menu.increment(); } else if (keyCode === 38) { // down menu.decrement(); } else if (keyCode === 13) { // enter menu.selectCurrent(); menu.destroy(); } } } /** * */ function onWrapperKeyPress(ev) { var menu = this._menu; // exit if default prevented or menu is closed if (ev.defaultPrevented || !menu) return; // handle query timer var self = this; clearTimeout(this._qTimeout); this._q += ev.key; this._qTimeout = setTimeout(function() {self._q = '';}, 300); // select first match alphabetically var prefixRegex = new RegExp('^' + this._q, 'i'), itemArray = menu.itemArray, pos; for (pos in itemArray) { if (prefixRegex.test(itemArray[pos].innerText)) { menu.selectPos(pos); break; } } } /** * Handle click events on wrapper element. * @param {Event} ev - The DOM event */ function onWrapperClick(ev) { // only left clicks, check default and disabled flags if (ev.button !== 0 || this._selectEl.disabled) return; // focus wrapper this.focus(); // open menu renderMenu(this); } /** * Render options menu */ function renderMenu(wrapperEl) { // check instance if (wrapperEl._menu) return; // render custom menu wrapperEl._menu = new Menu(wrapperEl, wrapperEl._selectEl, function() { wrapperEl._menu = null; // de-reference instance wrapperEl.focus(); }); } /** * Creates a new Menu * @class */ function Menu(wrapperEl, selectEl, wrapperCallbackFn) { // add scroll lock util.enableScrollLock(); // instance variables this.itemArray = []; this.origPos = null; this.currentPos = null; this.selectEl = selectEl; this.wrapperEl = wrapperEl; var res = this._createMenuEl(wrapperEl, selectEl), menuEl = this.menuEl = res[0]; var cb = util.callback; this.onClickCB = cb(this, 'onClick'); this.destroyCB = cb(this, 'destroy'); this.wrapperCallbackFn = wrapperCallbackFn; // add to DOM wrapperEl.appendChild(this.menuEl); // set position var props = formlib.getMenuPositionalCSS( wrapperEl, menuEl, res[1] ); jqLite.css(menuEl, props); jqLite.scrollTop(menuEl, props.scrollTop); // attach event handlers var destroyCB = this.destroyCB; jqLite.on(menuEl, 'click', this.onClickCB); jqLite.on(win, 'resize', destroyCB); // attach event handler after current event loop exits setTimeout(function() {jqLite.on(doc, 'click', destroyCB);}, 0); } /** * Create menu element * @param {Element} selectEl - The select element */ Menu.prototype._createMenuEl = function(wrapperEl, selectEl) { var menuEl = doc.createElement('div'), childEls = selectEl.children, itemArray = this.itemArray, itemPos = 0, origPos = -1, selectedPos = 0, selectedRow = 0, numRows = 0, docFrag = document.createDocumentFragment(), // for speed loopEl, rowEl, optionEls, inGroup, i, iMax, j, jMax; menuEl.className = menuClass; for (i=0, iMax=childEls.length; i < iMax; i++) { loopEl = childEls[i]; if (loopEl.tagName === 'OPTGROUP') { // add row item to menu rowEl = doc.createElement('div'); rowEl.textContent = loopEl.label; rowEl.className = 'mui-optgroup__label'; docFrag.appendChild(rowEl); inGroup = true; optionEls = loopEl.children; } else { inGroup = false; optionEls = [loopEl]; } // loop through option elements for (j=0, jMax=optionEls.length; j < jMax; j++) { loopEl = optionEls[j]; // add row item to menu rowEl = doc.createElement('div'); rowEl.textContent = loopEl.textContent; // handle optgroup options if (inGroup) jqLite.addClass(rowEl, 'mui-optgroup__option'); if (loopEl.hidden) { continue; } else if (loopEl.disabled) { // do not attach muiIndex to disable <option> elements to make them // unselectable. jqLite.addClass(rowEl, disabledClass); } else { rowEl._muiIndex = loopEl.index; rowEl._muiPos = itemPos; // handle selected options if (loopEl.selected) { selectedRow = numRows; origPos = itemPos; selectedPos = itemPos; } // add to item array itemArray.push(rowEl); itemPos += 1; } docFrag.appendChild(rowEl); numRows += 1; } } // add rows to menu menuEl.appendChild(docFrag); // save indices this.origPos = origPos; this.currentPos = selectedPos; // paint selectedPos if (itemArray.length) jqLite.addClass(itemArray[selectedPos], selectedClass); return [menuEl, selectedRow]; } /** * Handle click events on menu element. * @param {Event} ev - The DOM event */ Menu.prototype.onClick = function(ev) { // don't allow events to bubble ev.stopPropagation(); var item = ev.target, index = item._muiIndex; // ignore clicks on non-items if (index === undefined) return; // select option this.currentPos = item._muiPos; this.selectCurrent(); // destroy menu this.destroy(); } /** * Increment selected item */ Menu.prototype.increment = function() { if (this.currentPos === this.itemArray.length - 1) return; // un-select old row jqLite.removeClass(this.itemArray[this.currentPos], selectedClass); // select new row this.currentPos += 1; jqLite.addClass(this.itemArray[this.currentPos], selectedClass); } /** * Decrement selected item */ Menu.prototype.decrement = function() { if (this.currentPos === 0) return; // un-select old row jqLite.removeClass(this.itemArray[this.currentPos], selectedClass); // select new row this.currentPos -= 1; jqLite.addClass(this.itemArray[this.currentPos], selectedClass); } /** * Select current item */ Menu.prototype.selectCurrent = function() { if (this.currentPos !== this.origPos) { this.selectEl.selectedIndex = this.itemArray[this.currentPos]._muiIndex; // trigger change and input events util.dispatchEvent(this.selectEl, 'change', true, false); util.dispatchEvent(this.selectEl, 'input', true, false); } } /** * Select item at position */ Menu.prototype.selectPos = function(pos) { // un-select old row jqLite.removeClass(this.itemArray[this.currentPos], selectedClass); // select new row this.currentPos = pos; var itemEl = this.itemArray[pos]; jqLite.addClass(itemEl, selectedClass); // scroll (if necessary) var menuEl = this.menuEl, itemRect = itemEl.getBoundingClientRect(); if (itemRect.top < 0) { // menu item is hidden above visible window menuEl.scrollTop = menuEl.scrollTop + itemRect.top - 5; } else if (itemRect.top > window.innerHeight) { // menu item is hidden below visible window menuEl.scrollTop = menuEl.scrollTop + (itemRect.top + itemRect.height - window.innerHeight) + 5; } } /** * Destroy menu and detach event handlers */ Menu.prototype.destroy = function() { // remove scroll lock util.disableScrollLock(true); // remove event handlers jqLite.off(this.menuEl, 'click', this.clickCallbackFn); jqLite.off(doc, 'click', this.destroyCB); jqLite.off(win, 'resize', this.destroyCB); // remove element and execute wrapper callback var parentNode = this.menuEl.parentNode; if (parentNode) { parentNode.removeChild(this.menuEl); this.wrapperCallbackFn(); } } /** Define module API */ module.exports = { /** Initialize module listeners */ initListeners: function() { // markup elements available when method is called var elList = doc.querySelectorAll(cssSelector), i = elList.length; while (i--) initialize(elList[i]); // listen for mui-node-inserted events animationHelpers.onAnimationStart('mui-select-inserted', function(ev) { initialize(ev.target); }); } }; },{"./lib/animationHelpers":4,"./lib/forms":5,"./lib/jqLite":6,"./lib/util":7}],11:[function(require,module,exports){ /** * MUI CSS/JS tabs module * @module tabs */ 'use strict'; var jqLite = require('./lib/jqLite'), util = require('./lib/util'), animationHelpers = require('./lib/animationHelpers'), attrKey = 'data-mui-toggle', attrSelector = '[' + attrKey + '="tab"]', controlsAttrKey = 'data-mui-controls', activeClass = 'mui--is-active', showstartKey = 'mui.tabs.showstart', showendKey = 'mui.tabs.showend', hidestartKey = 'mui.tabs.hidestart', hideendKey = 'mui.tabs.hideend'; /** * Initialize the toggle element * @param {Element} toggleEl - The toggle element. */ function initialize(toggleEl) { // check flag if (toggleEl._muiTabs === true) return; else toggleEl._muiTabs = true; // attach click handler jqLite.on(toggleEl, 'click', clickHandler); } /** * Handle clicks on the toggle element. * @param {Event} ev - The DOM event. */ function clickHandler(ev) { // only left clicks if (ev.button !== 0) return; var toggleEl = this; // exit if toggle element is disabled if (toggleEl.getAttribute('disabled') !== null) return; activateTab(toggleEl); } /** * Activate the tab controlled by the toggle element. * @param {Element} toggleEl - The toggle element. */ function activateTab(currToggleEl) { var currTabEl = currToggleEl.parentNode, currPaneId = currToggleEl.getAttribute(controlsAttrKey), currPaneEl = document.getElementById(currPaneId), prevTabEl, prevPaneEl, prevPaneId, prevToggleEl, currData, prevData, ev1, ev2, cssSelector; // exit if already active if (jqLite.hasClass(currTabEl, activeClass)) return; // raise error if pane doesn't exist if (!currPaneEl) util.raiseError('Tab pane "' + currPaneId + '" not found'); // get previous pane prevPaneEl = getActiveSibling(currPaneEl); if (prevPaneEl) { prevPaneId = prevPaneEl.id; // get previous toggle and tab elements cssSelector = '[' + controlsAttrKey + '="' + prevPaneId + '"]'; prevToggleEl = document.querySelectorAll(cssSelector)[0]; prevTabEl = prevToggleEl.parentNode; } // define event data currData = {paneId: currPaneId, relatedPaneId: prevPaneId}; prevData = {paneId: prevPaneId, relatedPaneId: currPaneId}; // dispatch 'hidestart', 'showstart' events ev1 = util.dispatchEvent(prevToggleEl, hidestartKey, true, true, prevData); ev2 = util.dispatchEvent(currToggleEl, showstartKey, true, true, currData); // let events bubble setTimeout(function() { // exit if either event was canceled if (ev1.defaultPrevented || ev2.defaultPrevented) return; // de-activate previous if (prevTabEl) jqLite.removeClass(prevTabEl, activeClass); if (prevPaneEl) jqLite.removeClass(prevPaneEl, activeClass); // activate current jqLite.addClass(currTabEl, activeClass); jqLite.addClass(currPaneEl, activeClass); // dispatch 'hideend', 'showend' events util.dispatchEvent(prevToggleEl, hideendKey, true, false, prevData); util.dispatchEvent(currToggleEl, showendKey, true, false, currData); }, 0); } /** * Get previous active sibling. * @param {Element} el - The anchor element. */ function getActiveSibling(el) { var elList = el.parentNode.children, q = elList.length, activeEl = null, tmpEl; while (q-- && !activeEl) { tmpEl = elList[q]; if (tmpEl !== el && jqLite.hasClass(tmpEl, activeClass)) activeEl = tmpEl } return activeEl; } /** Define module API */ module.exports = { /** Initialize module listeners */ initListeners: function() { // markup elements available when method is called var elList = document.querySelectorAll(attrSelecto