muicss
Version:
Lightweight CSS framework based on Google's Material Design guidelines
205 lines (150 loc) • 4.33 kB
JavaScript
/**
* MUI CSS/JS overlay module
* @module overlay
*/
;
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;