(function(window) { "use strict"; window.ls.view.add({ selector: "data-service", controller: function(element, view, container, form, alerts, expression, window) { let action = element.dataset["service"]; let service = element.dataset["name"] || null; let event = expression.parse(element.dataset["event"]); // load, click, change, submit let confirm = element.dataset["confirm"] || ""; // Free text let loading = element.dataset["loading"] || ""; // Free text let loaderId = null; let scope = element.dataset["scope"] || "sdk"; // Free text let success = element.dataset["success"] || ""; let failure = element.dataset["failure"] || ""; let running = false; let callbacks = { hide: function() { return function() { return element.style.opacity = '0'; }; }, reset: function() { return function() { if ("FORM" === element.tagName) { return element.reset(); } throw new Error("This callback is only valid for forms"); }; }, alert: function(text, classname) { return function(alerts) { alerts.add({ text: text, class: classname || "success" }, 3000); }; }, redirect: function(url) { return function(router) { //router.change(url || "/"); window.location = url || "/"; }; }, reload: function() { return function(router) { router.reload(); }; }, state: function(keys) { let updateQueryString = function(key, value, url) { var re = new RegExp("([?&])" + key + "=.*?(&|#|$)(.*)", "gi"), hash; if (re.test(url)) { if (typeof value !== "undefined" && value !== null) { return url.replace(re, "$1" + key + "=" + value + "$2$3"); } else { hash = url.split("#"); url = hash[0].replace(re, "$1$3").replace(/(&|\?)$/, ""); if (typeof hash[1] !== "undefined" && hash[1] !== null) { url += "#" + hash[1]; } return url; } } else { if (typeof value !== "undefined" && value !== null) { var separator = url.indexOf("?") !== -1 ? "&" : "?"; hash = url.split("#"); url = hash[0] + separator + key + "=" + value; if (typeof hash[1] !== "undefined" && hash[1] !== null) { url += "#" + hash[1]; } return url; } else { return url; } } }; keys = keys.split(",").map(element => element.trim()); return function(serviceForm, router, window) { let url = window.location.href; keys.map(node => { node = node.split("="); let key = node[0] || ""; let name = node[1] || key; let value = getValue(key, "param", serviceForm); url = updateQueryString(name, value ? value : null, url); }); if (url !== window.location.href) { window.history.pushState({}, "", url); router.reset(); } }; }, trigger: function(events) { return function(document) { events = events.trim().split(","); for (let i = 0; i < events.length; i++) { if ("" === events[i]) { continue; } document.dispatchEvent(new CustomEvent(events[i])); } }; }, setId: function name(params) { }, default: function() { let collection = container.get('project-collection'); let document = container.get('project-document'); if(collection && document && collection.$id === document.$id) { for (const [key, value] of Object.entries(document)) { delete document[key]; } if(collection.rules) { for (let index = 0; index < collection.rules.length; index++) { const element = collection.rules[index]; switch (element.type) { case 'text': case 'email': case 'url': case 'ip': document[element.key] = element.default || ''; break; case 'numeric': document[element.key] = element.default || '0'; break; case 'boolean': document[element.key] = element.default || false; break; case 'document': document[element.key] = element.default || {'$id': '', '$collection': '', '$permissions': {}}; break; default: document[element.key] = null; break; } if(element.array) { document[element.key] = []; } } } } } }; /** * Original Solution From: * @see https://stackoverflow.com/a/41322698/2299554 * Notice: this version add support for $ sign in arg name. * * Retrieve a function's parameter names and default values * Notes: * - parameters with default values will not show up in transpiler code (Babel) because the parameter is removed from the function. * - does NOT support inline arrow functions as default values * to clarify: ( name = "string", add = defaultAddFunction ) - is ok * ( name = "string", add = ( a )=> a + 1 ) - is NOT ok * - does NOT support default string value that are appended with a non-standard ( word characters or $ ) variable name * to clarify: ( name = "string" + b ) - is ok * ( name = "string" + $b ) - is ok * ( name = "string" + b + "!" ) - is ok * ( name = "string" + λ ) - is NOT ok * @param {function} func * @returns {Array} - An array of the given function's parameter [key, default value] pairs. */ let getParams = function getParams(func) { const REGEX_COMMENTS = /((\/\/.*$)|(\/\*[\s\S]*?\*\/))/gm; const REGEX_FUNCTION_PARAMS = /(?:\s*(?:function\s*[^(]*)?\s*)((?:[^'"]|(?:(?:(['"])(?:(?:.*?[^\\]\2)|\2))))*?)\s*(?=(?:=>)|{)/m; const REGEX_PARAMETERS_VALUES = /\s*([\w\\$]+)\s*(?:=\s*((?:(?:(['"])(?:\3|(?:.*?[^\\]\3)))((\s*\+\s*)(?:(?:(['"])(?:\6|(?:.*?[^\\]\6)))|(?:[\w$]*)))*)|.*?))?\s*(?:,|$)/gm; let functionAsString = func.toString(); let params = []; let match; functionAsString = functionAsString.replaceAll('={}', ""); functionAsString = functionAsString.replaceAll('=[]', ""); functionAsString = functionAsString.replace(REGEX_COMMENTS, ""); functionAsString = functionAsString.match(REGEX_FUNCTION_PARAMS)[1]; if (functionAsString.charAt(0) === "(") { functionAsString = functionAsString.slice(1, -1); } while ((match = REGEX_PARAMETERS_VALUES.exec(functionAsString))) { //params.push([match[1], match[2]]); // with default values params.push(match[1]); // only with arg name } return params; }; let getValue = function(key, prefix, data) { let result = null; if (!key) { return null; } let attrKey = prefix + key.charAt(0).toUpperCase() + key.slice(1); /** * 1. Get from element data-param-* (expression supported) * 2. Get from element form object-* */ if (element.dataset[attrKey]) { result = expression.parse(element.dataset[attrKey]); if (element.dataset[attrKey + "CastTo"] === "array") { result = result.split(","); } } if (typeof data[key] !== 'undefined') { result = data[key]; } if (typeof result === 'undefined') { result = ""; } return result; }; let resolve = function(target, prefix = "param", data = {}) { if (!target) { return function() {}; } let args = getParams(target); return target.apply( target, args.map(function(value) { let result = getValue(value, prefix, data); return result ?? undefined; }) ); }; let exec = function(event) { let parsedSuccess = expression.parse(success); let parsedFailure = expression.parse(failure); let parsedAction = expression.parse(action); parsedSuccess = parsedSuccess && parsedSuccess != "" ? parsedSuccess.split(",").map(element => element.trim()) : []; parsedFailure = parsedFailure && parsedFailure != "" ? parsedFailure.split(",").map(element => element.trim()) : []; element.$lsSkip = true; element.classList.add("load-service-start"); if (!document.body.contains(element)) { element = undefined; return false; } if (event) { event.preventDefault(); } if(running) { return false; } running = true; element.style.backgroud = 'red'; if (confirm) { if (window.confirm(confirm) !== true) { element.classList.add("load-service-end"); element.$lsSkip = false; running = false; return false; } } if (loading) { loaderId = alerts.add({ text: loading, class: "" }, 0); } let method = container.path(scope + "." + parsedAction); if (!method) { throw new Error('Method "' + scope + "." + parsedAction + '" not found'); } let formData = "FORM" === element.tagName ? form.toJson(element) : {}; let result = resolve(method, "param", formData); if (!result) { return; } if(Promise.resolve(result) != result) { result = new Promise((resolve, reject) => { resolve(result); }); } result.then( function(data) { if (loaderId !== null) { // Remove loader if needed alerts.remove(loaderId); } if (!element) { return; } running = false; element.style.backgroud = 'transparent'; element.classList.add("load-service-end"); if(service) { container.set(service.replace(".", "-"), data, true, true); } container.set("serviceData", data, true, true); container.set("serviceForm", formData, true, true); for (let i = 0; i < parsedSuccess.length; i++) { // Trigger success callbacks container.resolve( resolve( callbacks[parsedSuccess[i]], "successParam" + parsedSuccess[i].charAt(0).toUpperCase() + parsedSuccess[i].slice(1), {} ) ); } container.set("serviceData", null, true, true); container.set("serviceForm", null, true, true); element.$lsSkip = false; view.render(element); }, function(exception) { if (loaderId !== null) { // Remove loader if needed alerts.remove(loaderId); } if (!element) { return; } running = false; element.style.backgroud = 'transparent'; element.classList.add("load-service-end"); for (let i = 0; i < parsedFailure.length; i++) { // Trigger failure callbacks container.resolve( resolve( callbacks[parsedFailure[i]], "failureParam" + parsedFailure[i].charAt(0).toUpperCase() + parsedFailure[i].slice(1), {} ) ); } element.$lsSkip = false; view.render(element); } ); }; let events = event.trim().split(","); for (let y = 0; y < events.length; y++) { if ("" === events[y]) { continue; } switch (events[y].trim()) { case "load": exec(); break; case "none": break; case "click": case "change": case "keypress": case "keydown": case "keyup": case "input": case "submit": element.addEventListener(events[y], exec); break; default: document.addEventListener(events[y], exec); } } } }); })(window);