UNPKG

vex-scss

Version:

Beautiful, functional dialogs in vanilla JavaScript and SCSS

754 lines (649 loc) 21.8 kB
(function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.vex = f()}})(function(){var define,module,exports;return (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(require,module,exports){ /* * classList.js: Cross-browser full element.classList implementation. * 2014-07-23 * * By Eli Grey, http://eligrey.com * Public Domain. * NO WARRANTY EXPRESSED OR IMPLIED. USE AT YOUR OWN RISK. */ /*global self, document, DOMException */ /*! @source http://purl.eligrey.com/github/classList.js/blob/master/classList.js*/ /* Copied from MDN: * https://developer.mozilla.org/en-US/docs/Web/API/Element/classList */ if ("document" in window.self) { // Full polyfill for browsers with no classList support // Including IE < Edge missing SVGElement.classList if (!("classList" in document.createElement("_")) || document.createElementNS && !("classList" in document.createElementNS("http://www.w3.org/2000/svg","g"))) { (function (view) { "use strict"; if (!('Element' in view)) return; var classListProp = "classList" , protoProp = "prototype" , elemCtrProto = view.Element[protoProp] , objCtr = Object , strTrim = String[protoProp].trim || function () { return this.replace(/^\s+|\s+$/g, ""); } , arrIndexOf = Array[protoProp].indexOf || function (item) { var i = 0 , len = this.length ; for (; i < len; i++) { if (i in this && this[i] === item) { return i; } } return -1; } // Vendors: please allow content code to instantiate DOMExceptions , DOMEx = function (type, message) { this.name = type; this.code = DOMException[type]; this.message = message; } , checkTokenAndGetIndex = function (classList, token) { if (token === "") { throw new DOMEx( "SYNTAX_ERR" , "An invalid or illegal string was specified" ); } if (/\s/.test(token)) { throw new DOMEx( "INVALID_CHARACTER_ERR" , "String contains an invalid character" ); } return arrIndexOf.call(classList, token); } , ClassList = function (elem) { var trimmedClasses = strTrim.call(elem.getAttribute("class") || "") , classes = trimmedClasses ? trimmedClasses.split(/\s+/) : [] , i = 0 , len = classes.length ; for (; i < len; i++) { this.push(classes[i]); } this._updateClassName = function () { elem.setAttribute("class", this.toString()); }; } , classListProto = ClassList[protoProp] = [] , classListGetter = function () { return new ClassList(this); } ; // Most DOMException implementations don't allow calling DOMException's toString() // on non-DOMExceptions. Error's toString() is sufficient here. DOMEx[protoProp] = Error[protoProp]; classListProto.item = function (i) { return this[i] || null; }; classListProto.contains = function (token) { token += ""; return checkTokenAndGetIndex(this, token) !== -1; }; classListProto.add = function () { var tokens = arguments , i = 0 , l = tokens.length , token , updated = false ; do { token = tokens[i] + ""; if (checkTokenAndGetIndex(this, token) === -1) { this.push(token); updated = true; } } while (++i < l); if (updated) { this._updateClassName(); } }; classListProto.remove = function () { var tokens = arguments , i = 0 , l = tokens.length , token , updated = false , index ; do { token = tokens[i] + ""; index = checkTokenAndGetIndex(this, token); while (index !== -1) { this.splice(index, 1); updated = true; index = checkTokenAndGetIndex(this, token); } } while (++i < l); if (updated) { this._updateClassName(); } }; classListProto.toggle = function (token, force) { token += ""; var result = this.contains(token) , method = result ? force !== true && "remove" : force !== false && "add" ; if (method) { this[method](token); } if (force === true || force === false) { return force; } else { return !result; } }; classListProto.toString = function () { return this.join(" "); }; if (objCtr.defineProperty) { var classListPropDesc = { get: classListGetter , enumerable: true , configurable: true }; try { objCtr.defineProperty(elemCtrProto, classListProp, classListPropDesc); } catch (ex) { // IE 8 doesn't support enumerable:true if (ex.number === -0x7FF5EC54) { classListPropDesc.enumerable = false; objCtr.defineProperty(elemCtrProto, classListProp, classListPropDesc); } } } else if (objCtr[protoProp].__defineGetter__) { elemCtrProto.__defineGetter__(classListProp, classListGetter); } }(window.self)); } else { // There is full or partial native classList support, so just check if we need // to normalize the add/remove and toggle APIs. (function () { "use strict"; var testElement = document.createElement("_"); testElement.classList.add("c1", "c2"); // Polyfill for IE 10/11 and Firefox <26, where classList.add and // classList.remove exist but support only one argument at a time. if (!testElement.classList.contains("c2")) { var createMethod = function(method) { var original = DOMTokenList.prototype[method]; DOMTokenList.prototype[method] = function(token) { var i, len = arguments.length; for (i = 0; i < len; i++) { token = arguments[i]; original.call(this, token); } }; }; createMethod('add'); createMethod('remove'); } testElement.classList.toggle("c3", false); // Polyfill for IE 10 and Firefox <24, where classList.toggle does not // support the second argument. if (testElement.classList.contains("c3")) { var _toggle = DOMTokenList.prototype.toggle; DOMTokenList.prototype.toggle = function(token, force) { if (1 in arguments && !this.contains(token) === !force) { return force; } else { return _toggle.call(this, token); } }; } testElement = null; }()); } } },{}],2:[function(require,module,exports){ /** * Expose `parse`. */ module.exports = parse; /** * Tests for browser support. */ var innerHTMLBug = false; var bugTestDiv; if (typeof document !== 'undefined') { bugTestDiv = document.createElement('div'); // Setup bugTestDiv.innerHTML = ' <link/><table></table><a href="/a">a</a><input type="checkbox"/>'; // Make sure that link elements get serialized correctly by innerHTML // This requires a wrapper element in IE innerHTMLBug = !bugTestDiv.getElementsByTagName('link').length; bugTestDiv = undefined; } /** * Wrap map from jquery. */ var map = { legend: [1, '<fieldset>', '</fieldset>'], tr: [2, '<table><tbody>', '</tbody></table>'], col: [2, '<table><tbody></tbody><colgroup>', '</colgroup></table>'], // for script/link/style tags to work in IE6-8, you have to wrap // in a div with a non-whitespace character in front, ha! _default: innerHTMLBug ? [1, 'X<div>', '</div>'] : [0, '', ''] }; map.td = map.th = [3, '<table><tbody><tr>', '</tr></tbody></table>']; map.option = map.optgroup = [1, '<select multiple="multiple">', '</select>']; map.thead = map.tbody = map.colgroup = map.caption = map.tfoot = [1, '<table>', '</table>']; map.polyline = map.ellipse = map.polygon = map.circle = map.text = map.line = map.path = map.rect = map.g = [1, '<svg xmlns="http://www.w3.org/2000/svg" version="1.1">','</svg>']; /** * Parse `html` and return a DOM Node instance, which could be a TextNode, * HTML DOM Node of some kind (<div> for example), or a DocumentFragment * instance, depending on the contents of the `html` string. * * @param {String} html - HTML string to "domify" * @param {Document} doc - The `document` instance to create the Node for * @return {DOMNode} the TextNode, DOM Node, or DocumentFragment instance * @api private */ function parse(html, doc) { if ('string' != typeof html) throw new TypeError('String expected'); // default to the global `document` object if (!doc) doc = document; // tag name var m = /<([\w:]+)/.exec(html); if (!m) return doc.createTextNode(html); html = html.replace(/^\s+|\s+$/g, ''); // Remove leading/trailing whitespace var tag = m[1]; // body support if (tag == 'body') { var el = doc.createElement('html'); el.innerHTML = html; return el.removeChild(el.lastChild); } // wrap map var wrap = map[tag] || map._default; var depth = wrap[0]; var prefix = wrap[1]; var suffix = wrap[2]; var el = doc.createElement('div'); el.innerHTML = prefix + html + suffix; while (depth--) el = el.lastChild; // one element if (el.firstChild == el.lastChild) { return el.removeChild(el.firstChild); } // several elements var fragment = doc.createDocumentFragment(); while (el.firstChild) { fragment.appendChild(el.removeChild(el.firstChild)); } return fragment; } },{}],3:[function(require,module,exports){ /** * Code refactored from Mozilla Developer Network: * https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/assign */ 'use strict'; function assign(target, firstSource) { if (target === undefined || target === null) { throw new TypeError('Cannot convert first argument to object'); } var to = Object(target); for (var i = 1; i < arguments.length; i++) { var nextSource = arguments[i]; if (nextSource === undefined || nextSource === null) { continue; } var keysArray = Object.keys(Object(nextSource)); for (var nextIndex = 0, len = keysArray.length; nextIndex < len; nextIndex++) { var nextKey = keysArray[nextIndex]; var desc = Object.getOwnPropertyDescriptor(nextSource, nextKey); if (desc !== undefined && desc.enumerable) { to[nextKey] = nextSource[nextKey]; } } } return to; } function polyfill() { if (!Object.assign) { Object.defineProperty(Object, 'assign', { enumerable: false, configurable: true, writable: true, value: assign }); } } module.exports = { assign: assign, polyfill: polyfill }; },{}],4:[function(require,module,exports){ // classList polyfill for old browsers require('classlist-polyfill') // Object.assign polyfill require('es6-object-assign').polyfill() // String to DOM function var domify = require('domify') // Use the DOM's HTML parsing to escape any dangerous strings var escapeHtml = function escapeHtml (str) { if (typeof str !== 'undefined') { var div = document.createElement('div') div.appendChild(document.createTextNode(str)) return div.innerHTML } else { return '' } } // Utility function to add space-delimited class strings to a DOM element's classList var addClasses = function addClasses (el, classStr) { if (typeof classStr !== 'string' || classStr.length === 0) { return } var classes = classStr.split(' ') for (var i = 0; i < classes.length; i++) { var className = classes[i] if (className.length) { el.classList.add(className) } } } // Detect CSS Animation End Support // https://github.com/limonte/sweetalert2/blob/99bd539f85e15ac170f69d35001d12e092ef0054/src/utils/dom.js#L194 var animationEndEvent = (function detectAnimationEndEvent () { var el = document.createElement('div') var eventNames = { 'WebkitAnimation': 'webkitAnimationEnd', 'MozAnimation': 'animationend', 'OAnimation': 'oanimationend', 'msAnimation': 'MSAnimationEnd', 'animation': 'animationend' } for (var i in eventNames) { if (el.style[i] !== undefined) { return eventNames[i] } } return false })() // vex base CSS classes var baseClassNames = { vex: 'vex', content: 'vex-content', overlay: 'vex-overlay', close: 'vex-close', closing: 'vex-closing', open: 'vex-open' } // Private lookup table of all open vex objects, keyed by id var vexes = {} var globalId = 1 // Private boolean to assist the escapeButtonCloses option var isEscapeActive = false // vex itself is an object that exposes a simple API to open and close vex objects in various ways var vex = { open: function open (opts) { // Check for usage of deprecated options, and log a warning var warnDeprecated = function warnDeprecated (prop) { console.warn('The "' + prop + '" property is deprecated in vex 3. Use CSS classes and the appropriate "ClassName" options, instead.') console.warn('See http://github.hubspot.com/vex/api/advanced/#options') } if (opts.css) { warnDeprecated('css') } if (opts.overlayCSS) { warnDeprecated('overlayCSS') } if (opts.contentCSS) { warnDeprecated('contentCSS') } if (opts.closeCSS) { warnDeprecated('closeCSS') } // The dialog instance var vexInstance = {} // Set id vexInstance.id = globalId++ // Store internally vexes[vexInstance.id] = vexInstance // Set state vexInstance.isOpen = true // Close function on the vex instance // This is how all API functions should close individual vexes vexInstance.close = function instanceClose () { // Check state if (!this.isOpen) { return true } var options = this.options // escapeButtonCloses is checked first if (isEscapeActive && !options.escapeButtonCloses) { return false } // Allow the user to validate any info or abort the close with the beforeClose callback var shouldClose = (function shouldClose () { // Call before close callback if (options.beforeClose) { return options.beforeClose.call(this) } // Otherwise indicate that it's ok to continue with close return true }.bind(this)()) // If beforeClose() fails, abort the close if (shouldClose === false) { return false } // Update state this.isOpen = false // Detect if the content el has any CSS animations defined var style = window.getComputedStyle(this.contentEl) function hasAnimationPre (prefix) { return style.getPropertyValue(prefix + 'animation-name') !== 'none' && style.getPropertyValue(prefix + 'animation-duration') !== '0s' } var hasAnimation = hasAnimationPre('') || hasAnimationPre('-webkit-') || hasAnimationPre('-moz-') || hasAnimationPre('-o-') // Define the function that will actually close the instance var close = function close () { if (!this.rootEl.parentNode) { return } // Run once this.rootEl.removeEventListener(animationEndEvent, close) this.overlayEl.removeEventListener(animationEndEvent, close) // Remove from lookup table (prevent memory leaks) delete vexes[this.id] // Remove the dialog from the DOM this.rootEl.parentNode.removeChild(this.rootEl) // Remove the overlay from the DOM this.bodyEl.removeChild(this.overlayEl); // Call after close callback if (options.afterClose) { options.afterClose.call(this) } // Remove styling from the body, if no more vexes are open if (Object.keys(vexes).length === 0) { document.body.classList.remove(baseClassNames.open) } }.bind(this) // Close the vex if (animationEndEvent && hasAnimation) { // Setup the end event listener, to remove the el from the DOM this.rootEl.addEventListener(animationEndEvent, close) this.overlayEl.addEventListener(animationEndEvent, close) // Add the closing class to the dialog, showing the close animation this.rootEl.classList.add(baseClassNames.closing) this.overlayEl.classList.add(baseClassNames.closing) } else { close() } return true } // Allow strings as content if (typeof opts === 'string') { opts = { content: opts } } // `content` is unsafe internally, so translate // safe default: HTML-escape the content before passing it through if (opts.unsafeContent && !opts.content) { opts.content = opts.unsafeContent } else if (opts.content) { opts.content = escapeHtml(opts.content) } // Store options on instance for future reference var options = vexInstance.options = Object.assign({}, vex.defaultOptions, opts) // Get Body Element var bodyEl = vexInstance.bodyEl = document.getElementsByTagName('body')[0]; // vex root var rootEl = vexInstance.rootEl = document.createElement('div') rootEl.classList.add(baseClassNames.vex) addClasses(rootEl, options.className) // Overlay var overlayEl = vexInstance.overlayEl = document.createElement('div') overlayEl.classList.add(baseClassNames.overlay) addClasses(overlayEl, options.overlayClassName) if (options.overlayClosesOnClick) { rootEl.addEventListener('click', function overlayClickListener (e) { if (e.target === rootEl) { vexInstance.close() } }) } bodyEl.appendChild(overlayEl) // Content var contentEl = vexInstance.contentEl = document.createElement('div') contentEl.classList.add(baseClassNames.content) addClasses(contentEl, options.contentClassName) contentEl.appendChild(options.content instanceof window.Node ? options.content : domify(options.content)) rootEl.appendChild(contentEl) // Close button if (options.showCloseButton) { var closeEl = vexInstance.closeEl = document.createElement('div') closeEl.classList.add(baseClassNames.close) addClasses(closeEl, options.closeClassName) closeEl.addEventListener('click', vexInstance.close.bind(vexInstance)) contentEl.appendChild(closeEl) } // Add to DOM document.querySelector(options.appendLocation).appendChild(rootEl) // Call after open callback if (options.afterOpen) { options.afterOpen.call(vexInstance) } // Apply styling to the body document.body.classList.add(baseClassNames.open) // Return the created vex instance return vexInstance }, // A top-level vex.close function to close dialogs by reference or id close: function close (vexOrId) { var id if (vexOrId.id) { id = vexOrId.id } else if (typeof vexOrId === 'string') { id = vexOrId } else { throw new TypeError('close requires a vex object or id string') } if (!vexes[id]) { return false } return vexes[id].close() }, // Close the most recently created/opened vex closeTop: function closeTop () { var ids = Object.keys(vexes) if (!ids.length) { return false } return vexes[ids[ids.length - 1]].close() }, // Close every vex! closeAll: function closeAll () { for (var id in vexes) { this.close(id) } return true }, // A getter for the internal lookup table getAll: function getAll () { return vexes }, // A getter for the internal lookup table getById: function getById (id) { return vexes[id] } } // Close top vex on escape window.addEventListener('keyup', function vexKeyupListener (e) { if (e.keyCode === 27) { isEscapeActive = true vex.closeTop() isEscapeActive = false } }) // Close all vexes on history pop state (useful in single page apps) window.addEventListener('popstate', function () { if (vex.defaultOptions.closeAllOnPopState) { vex.closeAll() } }) vex.defaultOptions = { content: '', showCloseButton: true, escapeButtonCloses: true, overlayClosesOnClick: true, appendLocation: 'body', className: '', overlayClassName: '', contentClassName: '', closeClassName: '', closeAllOnPopState: true } // TODO Loading symbols? // Include escapeHtml function on the library object Object.defineProperty(vex, '_escapeHtml', { configurable: false, enumerable: false, writable: false, value: escapeHtml }) // Plugin system! vex.registerPlugin = function registerPlugin (pluginFn, name) { var plugin = pluginFn(vex) var pluginName = name || plugin.name if (vex[pluginName]) { throw new Error('Plugin ' + name + ' is already registered.') } vex[pluginName] = plugin } module.exports = vex },{"classlist-polyfill":1,"domify":2,"es6-object-assign":3}]},{},[4])(4) });