1
0
Fork 0
mirror of synced 2024-07-01 20:50:49 +12:00
appwrite/public/scripts/views/service.js
2019-08-20 20:35:30 +03:00

336 lines
15 KiB
JavaScript

(function (window) {
"use strict";
window.ls.view.add(
{
selector: 'data-service',
controller: function(element, view, container, form2json, alerts, expression, window) {
let action = element.dataset['service'];
let service = element.dataset['name'] || action;
let event = 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 debug = !!(element.dataset['debug']); // Free text
let success = (element.dataset['success'] || '');
let failure = (element.dataset['failure'] || '');
success = (success && success != '') ? success.split(',').map(element => element.trim()) : [];
failure = (failure && failure != '') ? failure.split(',').map(element => element.trim()) : [];
if (debug) console.log('%c[service init]: ' + action + ' (' + service + ')', 'color:red');
let callbacks = {
'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 || '/');
}
},
'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) {
console.log('UPDATE STATE');
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;
}
if (debug) console.log('%c[event triggered]: ' + events[i], 'color:green');
document.dispatchEvent(new CustomEvent(events[i]));
}
}
}
};
/**
* 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]*?\*\/))/mg;
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.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(data[key]) {
result = data[key];
}
if(!result) {
result = '';
}
return result;
}
let resolve = function(target, prefix = 'param', data = {}) {
if (!target) {
return function() {};
}
let args = getParams(target);
if (debug) console.log('%c[form data]: ', 'color:green', data);
return target.apply(target, args.map(function(value) {
let result = getValue(value, prefix, data);
if (debug) console.log('[param resolved]: (' + service + ') ' + value + '=', result);
return result;
}));
};
let exec = function(event) {
element.$lsSkip = true;
element.classList.add('load-service-start');
if (debug) console.log('%c[executed]: ' + scope + '.' + action, 'color:yellow', event, element, document.body.contains(element));
if(!document.body.contains(element)) {
element = undefined;
return false;
}
if(event) {
event.preventDefault();
}
if(confirm) {
if (window.confirm(confirm) !== true) {
return false;
}
}
if(loading) {
loaderId = alerts.add({text: loading, class: ''}, 0);
}
let method = container.path(scope + '.' + action);
if(!method) {
throw new Error('Method "' + scope + '.' + action + '" not found');
}
let formData = ('FORM' === element.tagName) ? form2json.toJson(element) : {};
let result = resolve(method, 'param', formData);
if(!result) {
return;
}
result
.then(function (data) {
if(loaderId !== null) { // Remove loader if needed
alerts.remove(loaderId);
}
if(!element) {
return;
}
element.classList.add('load-service-end');
container.set(service.replace('.', '-'), data, true, true);
container.set('serviceData', data, true, true);
container.set('serviceForm', formData, true, true);
if (debug) console.log('%cservice ready: "' + service.replace('.', '-') + '"', 'color:green');
if (debug) console.log('%cservice:', 'color:blue', container.get(service.replace('.', '-')));
for (let i = 0; i < success.length; i++) { // Trigger success callbacks
container.resolve(resolve(callbacks[success[i]], 'successParam' + success[i].charAt(0).toUpperCase() + success[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;
}
for (let i = 0; i < failure.length; i++) { // Trigger success callbacks
container.resolve(resolve(callbacks[failure[i]], 'failureParam' + failure[i].charAt(0).toUpperCase() + failure[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);
}
if (debug) console.log('%cregistered: "' + events[y].trim() + '" (' + service + ')', 'color:blue');
}
}
}
);
})(window);