UNPKG

vex-scss

Version:

Beautiful, functional dialogs in vanilla JavaScript and SCSS

1,587 lines (1,346 loc) 51 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){ // get successful control from form and assemble into object // http://www.w3.org/TR/html401/interact/forms.html#h-17.13.2 // types which indicate a submit action and are not successful controls // these will be ignored var k_r_submitter = /^(?:submit|button|image|reset|file)$/i; // node names which could be successful controls var k_r_success_contrls = /^(?:input|select|textarea|keygen)/i; // Matches bracket notation. var brackets = /(\[[^\[\]]*\])/g; // serializes form fields // @param form MUST be an HTMLForm element // @param options is an optional argument to configure the serialization. Default output // with no options specified is a url encoded string // - hash: [true | false] Configure the output type. If true, the output will // be a js object. // - serializer: [function] Optional serializer function to override the default one. // The function takes 3 arguments (result, key, value) and should return new result // hash and url encoded str serializers are provided with this module // - disabled: [true | false]. If true serialize disabled fields. // - empty: [true | false]. If true serialize empty fields function serialize(form, options) { if (typeof options != 'object') { options = { hash: !!options }; } else if (options.hash === undefined) { options.hash = true; } var result = (options.hash) ? {} : ''; var serializer = options.serializer || ((options.hash) ? hash_serializer : str_serialize); var elements = form && form.elements ? form.elements : []; //Object store each radio and set if it's empty or not var radio_store = Object.create(null); for (var i=0 ; i<elements.length ; ++i) { var element = elements[i]; // ingore disabled fields if ((!options.disabled && element.disabled) || !element.name) { continue; } // ignore anyhting that is not considered a success field if (!k_r_success_contrls.test(element.nodeName) || k_r_submitter.test(element.type)) { continue; } var key = element.name; var val = element.value; // we can't just use element.value for checkboxes cause some browsers lie to us // they say "on" for value when the box isn't checked if ((element.type === 'checkbox' || element.type === 'radio') && !element.checked) { val = undefined; } // If we want empty elements if (options.empty) { // for checkbox if (element.type === 'checkbox' && !element.checked) { val = ''; } // for radio if (element.type === 'radio') { if (!radio_store[element.name] && !element.checked) { radio_store[element.name] = false; } else if (element.checked) { radio_store[element.name] = true; } } // if options empty is true, continue only if its radio if (!val && element.type == 'radio') { continue; } } else { // value-less fields are ignored unless options.empty is true if (!val) { continue; } } // multi select boxes if (element.type === 'select-multiple') { val = []; var selectOptions = element.options; var isSelectedOptions = false; for (var j=0 ; j<selectOptions.length ; ++j) { var option = selectOptions[j]; var allowedEmpty = options.empty && !option.value; var hasValue = (option.value || allowedEmpty); if (option.selected && hasValue) { isSelectedOptions = true; // If using a hash serializer be sure to add the // correct notation for an array in the multi-select // context. Here the name attribute on the select element // might be missing the trailing bracket pair. Both names // "foo" and "foo[]" should be arrays. if (options.hash && key.slice(key.length - 2) !== '[]') { result = serializer(result, key + '[]', option.value); } else { result = serializer(result, key, option.value); } } } // Serialize if no selected options and options.empty is true if (!isSelectedOptions && options.empty) { result = serializer(result, key, ''); } continue; } result = serializer(result, key, val); } // Check for all empty radio buttons and serialize them with key="" if (options.empty) { for (var key in radio_store) { if (!radio_store[key]) { result = serializer(result, key, ''); } } } return result; } function parse_keys(string) { var keys = []; var prefix = /^([^\[\]]*)/; var children = new RegExp(brackets); var match = prefix.exec(string); if (match[1]) { keys.push(match[1]); } while ((match = children.exec(string)) !== null) { keys.push(match[1]); } return keys; } function hash_assign(result, keys, value) { if (keys.length === 0) { result = value; return result; } var key = keys.shift(); var between = key.match(/^\[(.+?)\]$/); if (key === '[]') { result = result || []; if (Array.isArray(result)) { result.push(hash_assign(null, keys, value)); } else { // This might be the result of bad name attributes like "[][foo]", // in this case the original `result` object will already be // assigned to an object literal. Rather than coerce the object to // an array, or cause an exception the attribute "_values" is // assigned as an array. result._values = result._values || []; result._values.push(hash_assign(null, keys, value)); } return result; } // Key is an attribute name and can be assigned directly. if (!between) { result[key] = hash_assign(result[key], keys, value); } else { var string = between[1]; // +var converts the variable into a number // better than parseInt because it doesn't truncate away trailing // letters and actually fails if whole thing is not a number var index = +string; // If the characters between the brackets is not a number it is an // attribute name and can be assigned directly. if (isNaN(index)) { result = result || {}; result[string] = hash_assign(result[string], keys, value); } else { result = result || []; result[index] = hash_assign(result[index], keys, value); } } return result; } // Object/hash encoding serializer. function hash_serializer(result, key, value) { var matches = key.match(brackets); // Has brackets? Use the recursive assignment function to walk the keys, // construct any missing objects in the result tree and make the assignment // at the end of the chain. if (matches) { var keys = parse_keys(key); hash_assign(result, keys, value); } else { // Non bracket notation can make assignments directly. var existing = result[key]; // If the value has been assigned already (for instance when a radio and // a checkbox have the same name attribute) convert the previous value // into an array before pushing into it. // // NOTE: If this requirement were removed all hash creation and // assignment could go through `hash_assign`. if (existing) { if (!Array.isArray(existing)) { result[key] = [ existing ]; } result[key].push(value); } else { result[key] = value; } } return result; } // urlform encoding serializer function str_serialize(result, key, value) { // encode newlines as \r\n cause the html spec says so value = value.replace(/(\r)?\n/g, '\r\n'); value = encodeURIComponent(value); // spaces should be '+' rather than '%20'. value = value.replace(/%20/g, '+'); return result + (result ? '&' : '') + encodeURIComponent(key) + '=' + value; } module.exports = serialize; },{}],5:[function(require,module,exports){ (function (global){ (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.vexDialog = 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){ /** * 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; } },{}],2:[function(require,module,exports){ // get successful control from form and assemble into object // http://www.w3.org/TR/html401/interact/forms.html#h-17.13.2 // types which indicate a submit action and are not successful controls // these will be ignored var k_r_submitter = /^(?:submit|button|image|reset|file)$/i; // node names which could be successful controls var k_r_success_contrls = /^(?:input|select|textarea|keygen)/i; // Matches bracket notation. var brackets = /(\[[^\[\]]*\])/g; // serializes form fields // @param form MUST be an HTMLForm element // @param options is an optional argument to configure the serialization. Default output // with no options specified is a url encoded string // - hash: [true | false] Configure the output type. If true, the output will // be a js object. // - serializer: [function] Optional serializer function to override the default one. // The function takes 3 arguments (result, key, value) and should return new result // hash and url encoded str serializers are provided with this module // - disabled: [true | false]. If true serialize disabled fields. // - empty: [true | false]. If true serialize empty fields function serialize(form, options) { if (typeof options != 'object') { options = { hash: !!options }; } else if (options.hash === undefined) { options.hash = true; } var result = (options.hash) ? {} : ''; var serializer = options.serializer || ((options.hash) ? hash_serializer : str_serialize); var elements = form && form.elements ? form.elements : []; //Object store each radio and set if it's empty or not var radio_store = Object.create(null); for (var i=0 ; i<elements.length ; ++i) { var element = elements[i]; // ingore disabled fields if ((!options.disabled && element.disabled) || !element.name) { continue; } // ignore anyhting that is not considered a success field if (!k_r_success_contrls.test(element.nodeName) || k_r_submitter.test(element.type)) { continue; } var key = element.name; var val = element.value; // we can't just use element.value for checkboxes cause some browsers lie to us // they say "on" for value when the box isn't checked if ((element.type === 'checkbox' || element.type === 'radio') && !element.checked) { val = undefined; } // If we want empty elements if (options.empty) { // for checkbox if (element.type === 'checkbox' && !element.checked) { val = ''; } // for radio if (element.type === 'radio') { if (!radio_store[element.name] && !element.checked) { radio_store[element.name] = false; } else if (element.checked) { radio_store[element.name] = true; } } // if options empty is true, continue only if its radio if (!val && element.type == 'radio') { continue; } } else { // value-less fields are ignored unless options.empty is true if (!val) { continue; } } // multi select boxes if (element.type === 'select-multiple') { val = []; var selectOptions = element.options; var isSelectedOptions = false; for (var j=0 ; j<selectOptions.length ; ++j) { var option = selectOptions[j]; var allowedEmpty = options.empty && !option.value; var hasValue = (option.value || allowedEmpty); if (option.selected && hasValue) { isSelectedOptions = true; // If using a hash serializer be sure to add the // correct notation for an array in the multi-select // context. Here the name attribute on the select element // might be missing the trailing bracket pair. Both names // "foo" and "foo[]" should be arrays. if (options.hash && key.slice(key.length - 2) !== '[]') { result = serializer(result, key + '[]', option.value); } else { result = serializer(result, key, option.value); } } } // Serialize if no selected options and options.empty is true if (!isSelectedOptions && options.empty) { result = serializer(result, key, ''); } continue; } result = serializer(result, key, val); } // Check for all empty radio buttons and serialize them with key="" if (options.empty) { for (var key in radio_store) { if (!radio_store[key]) { result = serializer(result, key, ''); } } } return result; } function parse_keys(string) { var keys = []; var prefix = /^([^\[\]]*)/; var children = new RegExp(brackets); var match = prefix.exec(string); if (match[1]) { keys.push(match[1]); } while ((match = children.exec(string)) !== null) { keys.push(match[1]); } return keys; } function hash_assign(result, keys, value) { if (keys.length === 0) { result = value; return result; } var key = keys.shift(); var between = key.match(/^\[(.+?)\]$/); if (key === '[]') { result = result || []; if (Array.isArray(result)) { result.push(hash_assign(null, keys, value)); } else { // This might be the result of bad name attributes like "[][foo]", // in this case the original `result` object will already be // assigned to an object literal. Rather than coerce the object to // an array, or cause an exception the attribute "_values" is // assigned as an array. result._values = result._values || []; result._values.push(hash_assign(null, keys, value)); } return result; } // Key is an attribute name and can be assigned directly. if (!between) { result[key] = hash_assign(result[key], keys, value); } else { var string = between[1]; // +var converts the variable into a number // better than parseInt because it doesn't truncate away trailing // letters and actually fails if whole thing is not a number var index = +string; // If the characters between the brackets is not a number it is an // attribute name and can be assigned directly. if (isNaN(index)) { result = result || {}; result[string] = hash_assign(result[string], keys, value); } else { result = result || []; result[index] = hash_assign(result[index], keys, value); } } return result; } // Object/hash encoding serializer. function hash_serializer(result, key, value) { var matches = key.match(brackets); // Has brackets? Use the recursive assignment function to walk the keys, // construct any missing objects in the result tree and make the assignment // at the end of the chain. if (matches) { var keys = parse_keys(key); hash_assign(result, keys, value); } else { // Non bracket notation can make assignments directly. var existing = result[key]; // If the value has been assigned already (for instance when a radio and // a checkbox have the same name attribute) convert the previous value // into an array before pushing into it. // // NOTE: If this requirement were removed all hash creation and // assignment could go through `hash_assign`. if (existing) { if (!Array.isArray(existing)) { result[key] = [ existing ]; } result[key].push(value); } else { result[key] = value; } } return result; } // urlform encoding serializer function str_serialize(result, key, value) { // encode newlines as \r\n cause the html spec says so value = value.replace(/(\r)?\n/g, '\r\n'); value = encodeURIComponent(value); // spaces should be '+' rather than '%20'. value = value.replace(/%20/g, '+'); return result + (result ? '&' : '') + encodeURIComponent(key) + '=' + value; } module.exports = serialize; },{}],3:[function(require,module,exports){ var domify = require('domify') var serialize = require('form-serialize') // Build DOM elements for the structure of the dialog var buildDialogForm = function buildDialogForm (options) { var form = document.createElement('form') form.classList.add('vex-dialog-form') var message = document.createElement('div') message.classList.add('vex-dialog-message') message.appendChild(options.message instanceof window.Node ? options.message : domify(options.message)) var input = document.createElement('div') input.classList.add('vex-dialog-input') input.appendChild(options.input instanceof window.Node ? options.input : domify(options.input)) form.appendChild(message) form.appendChild(input) return form } // Take an array of buttons (see the default buttons below) and turn them into DOM elements var buttonsToDOM = function buttonsToDOM (buttons) { var domButtons = document.createElement('div') domButtons.classList.add('vex-dialog-buttons') for (var i = 0; i < buttons.length; i++) { var button = buttons[i] var domButton = document.createElement('button') domButton.type = button.type domButton.textContent = button.text domButton.className = button.className domButton.classList.add('vex-dialog-button') if (i === 0) { domButton.classList.add('vex-first') } else if (i === buttons.length - 1) { domButton.classList.add('vex-last') } // Attach click listener to button with closure (function (button) { domButton.addEventListener('click', function (e) { if (button.click) { button.click.call(this, e) } }.bind(this)) }.bind(this)(button)) domButtons.appendChild(domButton) } return domButtons } var plugin = function plugin (vex) { // Define the API first var dialog = { // Plugin name name: 'dialog', // Open open: function open (opts) { var options = Object.assign({}, this.defaultOptions, opts) // `message` is unsafe internally, so translate // safe default: HTML-escape the message before passing it through if (options.unsafeMessage && !options.message) { options.message = options.unsafeMessage } else if (options.message) { options.message = vex._escapeHtml(options.message) } // Build the form from the options var form = options.unsafeContent = buildDialogForm(options) // Open the dialog var dialogInstance = vex.open(options) // Quick comment - these options and appending buttons and everything // would preferably be done _before_ opening the dialog. However, since // they rely on the context of the vex instance, we have to do them // after. A potential future fix would be to differentiate between // a "created" vex instance and an "opened" vex instance, so any actions // that rely on the specific context of the instance can do their stuff // before opening the dialog on the page. // Override the before close callback to also pass the value of the form var beforeClose = options.beforeClose && options.beforeClose.bind(dialogInstance) dialogInstance.options.beforeClose = function dialogBeforeClose () { // Only call the callback once - when the validation in beforeClose, if present, is true var shouldClose = beforeClose ? beforeClose() : true if (shouldClose) { options.callback(this.value || false) } // Return the result of beforeClose() to vex return shouldClose }.bind(dialogInstance) // Append buttons to form with correct context form.appendChild(buttonsToDOM.call(dialogInstance, options.buttons)) // Attach form to instance dialogInstance.form = form // Add submit listener to form form.addEventListener('submit', options.onSubmit.bind(dialogInstance)) // Optionally focus the first input in the form if (options.focusFirstInput) { var el = dialogInstance.contentEl.querySelector('button, input, select, textarea') if (el) { el.focus() } } // For chaining return dialogInstance }, // Alert alert: function (options) { // Allow string as message if (typeof options === 'string') { options = { message: options } } options = Object.assign({}, this.defaultOptions, this.defaultAlertOptions, options) return this.open(options) }, // Confirm confirm: function (options) { if (typeof options !== 'object' || typeof options.callback !== 'function') { throw new Error('dialog.confirm(options) requires options.callback.') } options = Object.assign({}, this.defaultOptions, this.defaultConfirmOptions, options) return this.open(options) }, // Prompt prompt: function (options) { if (typeof options !== 'object' || typeof options.callback !== 'function') { throw new Error('dialog.prompt(options) requires options.callback.') } var defaults = Object.assign({}, this.defaultOptions, this.defaultPromptOptions) var dynamicDefaults = { unsafeMessage: '<label for="vex">' + vex._escapeHtml(options.label || defaults.label) + '</label>', input: '<input name="vex" type="text" class="vex-dialog-prompt-input" placeholder="' + vex._escapeHtml(options.placeholder || defaults.placeholder) + '" value="' + vex._escapeHtml(options.value || defaults.value) + '" />' } options = Object.assign(defaults, dynamicDefaults, options) // Pluck the value of the "vex" input field as the return value for prompt's callback // More closely mimics "window.prompt" in that a single string is returned var callback = options.callback options.callback = function promptCallback (value) { if (typeof value === 'object') { var keys = Object.keys(value) value = keys.length ? value[keys[0]] : '' } callback(value) } return this.open(options) } } // Now define any additional data that's not the direct dialog API dialog.buttons = { YES: { text: 'OK', type: 'submit', className: 'vex-dialog-button-primary', click: function yesClick () { this.value = true } }, NO: { text: 'Cancel', type: 'button', className: 'vex-dialog-button-secondary', click: function noClick () { this.value = false this.close() } } } dialog.defaultOptions = { callback: function () {}, afterOpen: function () {}, message: '', input: '', buttons: [ dialog.buttons.YES, dialog.buttons.NO ], showCloseButton: false, onSubmit: function onDialogSubmit (e) { e.preventDefault() if (this.options.input) { this.value = serialize(this.form, { hash: true }) } return this.close() }, focusFirstInput: true } dialog.defaultAlertOptions = { buttons: [ dialog.buttons.YES ] } dialog.defaultPromptOptions = { label: 'Prompt:', placeholder: '', value: '' } dialog.defaultConfirmOptions = {} return dialog } module.exports = plugin },{"domify":1,"form-serialize":2}]},{},[3])(3) }); }).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) },{"domify":2,"form-serialize":4}],6:[function(require,module,exports){ var vex = require('./vex') vex.registerPlugin(require('vex-dialog')) module.exports = vex },{"./vex":7,"vex-dialog":5}],7:[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.a