loleaflet/.eslintignore | 1 loleaflet/Makefile.am | 9 loleaflet/js/vex.combined.js | 1628 +++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 1634 insertions(+), 4 deletions(-)
New commits: commit 05d433b500e76e2a55c044d4c747fc06d8571d9f Author: Henry Castro <hcas...@collabora.com> AuthorDate: Thu Feb 20 08:25:27 2020 -0400 Commit: Henry Castro <hcas...@collabora.com> CommitDate: Thu Feb 20 14:51:54 2020 +0100 loleaflet: import vex.combined.js library The file was imported from node_modules/vex-js/dist/js/vex.combined.js the main reason is to analyze the source code, debug and patch if necessary To get the source code from npm repository make libs Change-Id: I59e466e925d43bf4c599305898c656f69ec195d9 Reviewed-on: https://gerrit.libreoffice.org/c/online/+/89099 Tested-by: Jenkins CollaboraOffice <jenkinscollaboraoff...@gmail.com> Reviewed-by: Henry Castro <hcas...@collabora.com> diff --git a/loleaflet/.eslintignore b/loleaflet/.eslintignore index 04f898987..40ef65124 100644 --- a/loleaflet/.eslintignore +++ b/loleaflet/.eslintignore @@ -1,4 +1,5 @@ # We only directly edit toolbar.js in dist/ +**/js/vex.combined.js **/js/sanitize-url.js **/js/l10n.js **/js/w2ui-1.5.rc1.js diff --git a/loleaflet/Makefile.am b/loleaflet/Makefile.am index 8eee500ab..09e0c1e89 100644 --- a/loleaflet/Makefile.am +++ b/loleaflet/Makefile.am @@ -72,8 +72,9 @@ define npm_source endef NODE_MODULES_SRC =\ - @braintree/sanitize-url@3.0.0 \ - l10n-for-node@0.0.1 + vex-js@4.1.0 \ + l10n-for-node@0.0.1 \ + @braintree/sanitize-url@3.0.0 LOLEAFLET_CSS =\ $(builddir)/node_modules/select2/dist/css/select2.css \ @@ -122,10 +123,10 @@ NODE_MODULES_JS =\ node_modules/smartmenus/dist/jquery.smartmenus.js \ node_modules/autolinker/dist/Autolinker.js \ node_modules/json-js/json2.js \ - node_modules/select2/dist/js/select2.js \ - node_modules/vex-js/dist/js/vex.combined.js + node_modules/select2/dist/js/select2.js LOLEAFLET_LIBS_JS =\ + vex.combined.js \ sanitize-url.js if !ENABLE_MOBILEAPP diff --git a/loleaflet/js/vex.combined.js b/loleaflet/js/vex.combined.js new file mode 100644 index 000000000..2e09a5497 --- /dev/null +++ b/loleaflet/js/vex.combined.js @@ -0,0 +1,1628 @@ +(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(){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}return e})()({1:[function(require,module,exports){ +/* + * classList.js: Cross-browser full element.classList implementation. + * 1.1.20170427 + * + * By Eli Grey, http://eligrey.com + * License: Dedicated to the public domain. + * See https://github.com/eligrey/classList.js/blob/master/LICENSE.md + */ + +/*global self, document, DOMException */ + +/*! @source http://purl.eligrey.com/github/classList.js/blob/master/classList.js */ + +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 + // adding undefined to fight this issue https://github.com/eligrey/classList.js/issues/36 + // modernie IE8-MSW7 machine has IE8 8.0.6001.18702 and is affected + if (ex.number === undefined || ex.number === -0x7FF5EC54) { + classListPropDesc.enumerable = false; + objCtr.defineProperty(elemCtrProto, classListProp, classListPropDesc); + } + } +} else if (objCtr[protoProp].__defineGetter__) { + elemCtrProto.__defineGetter__(classListProp, classListGetter); +} + +}(window.self)); + +} + +// 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 = { + 'animation': 'animationend', + 'WebkitAnimation': 'webkitAnimationEnd', + 'MozAnimation': 'animationend', + 'OAnimation': 'oanimationend', + 'msAnimation': 'MSAnimationEnd' + } + 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}]},{},[6])(6) +}); \ No newline at end of file _______________________________________________ Libreoffice-commits mailing list libreoffice-comm...@lists.freedesktop.org https://lists.freedesktop.org/mailman/listinfo/libreoffice-commits