adhara
Version:
foundation for any kind of website: microframework
511 lines (489 loc) • 17 kB
JavaScript
function handleForm(form){
if(form.submitting){
return;
}else{
form.submitting = true;
}
let hasFiles = !!form.querySelector('input[type="file"]');
let apiData;
if(hasFiles){
apiData = new FormData(form);
}else{
let formData = jQuery(form).serializeArray();
apiData = {};
jQuery.each(formData, function (i, fieldData) {
apiData[fieldData.name] = fieldData.value;
});
}
let format_fn = form.getAttribute('format-data');
if(format_fn){
apiData = call_fn(format_fn, apiData);
}
if(apiData===false){return;}
RestAPI[form.getAttribute('api-method')]({
url: form.action.split(window.location.host)[1],
data: apiData,
successMessage: form.getAttribute('success-message'),
handleError: form.getAttribute('handle-error')!=="false",
success: function(data){
if(form.getAttribute('form-clear')==="true") {
form.reset();
}
jQuery(form).trigger('success', data);
},
failure: function(message){
jQuery(form).trigger('failure', message);
}
});
}
function registerAdharaUtils(){
//Register templateEngine helpers
Adhara.templateEngine.helpersHandler.registerHelpers();
//Form listeners
jQuery(document).on('submit', 'form.api-form', function (event) {
event.preventDefault();
handleForm(this);
});
//Form listeners
jQuery(document).on('success', 'form.dialog-form', function (/*e, d*/) {
this.close.click();
});
}
/**
* @function
* @global
* @param {String|Number|Boolean} param1
* @param {String} operator - Operator can be one among these.
* "in", "not_in", "contains", "not_contains", "has", "||", "&&", "|", "&", "==", "===", "!=", "!==", ">", "<", ">=", "<=", "equals", "and", "or".
* @param {String|Number|Boolean} param2
* @Returns {Boolean}
*
* "in" - param2 must be an {Array}. Whether param1's existence in that array will be checked for
*
* "not_in" - param2 must be an {Array} and param1's non-existence in that array will be checked for
*
* "contains" - param1 must be an {Array} and param2's existence in that array will be checked for
*
* "not_contains" - param1 must be an {Array} and param2's non-existence in that array will be checked for
*
* "has" - param1 must be an {Object}. Whether param2 is a key of param1
*
* All other operations will be applied as javaScript evaluates them.
* `"equals" is equivalent to "=="`,
* `"and" is equivalent to "&&"`
* and `"or" is equivalent to "||"`.
*
* */
function evaluateLogic(param1, operator, param2){
if(operator === "in"){
return param2 && param2.indexOf(param1) !== -1;
}else if(operator === "not_in"){
return param2 && param2.indexOf(param1) === -1;
}else if(operator === "contains"){
return param1 && param1.indexOf(param2) !== -1;
}else if(operator === "not_contains"){
return param1 && param1.indexOf(param2) === -1;
}else if(operator === "has"){
return param1 && param1.hasOwnProperty(param2);
}else{
return {
"||" : param1||param2,
"&&" : param1&¶m2,
"|" : param1|param2,
"&" : param1¶m2,
"==" : param1==param2,
"===": param1===param2,
"!=" : param1!=param2,
"!==": param1!==param2,
">" : param1>param2,
"<" : param1<param2,
">=" : param1>=param2,
"<=" : param1<=param2,
"equals" : param1==param2,
"and" : param1&¶m2,
"or" : param1||param2
}[operator];
}
}
/**
* @function
* @global
* @param {Object} object - object in which to lookup.
* @param {String} path - dot separated keys to be looked up for in depth.
* @param {String} [identifier=null] - token that helps to lookup inside arrays.
* @returns {String|Number|Boolean|Object|Array} - Value for the key.
* @description
* Looks up a JSON object in depth and returns the value for the key provided.
* @example
* let obj = {
* task: {
* id: "12341234",
* status: {
* color: "#fff"
* }
* }
* };
* getValueFromJSON(obj, "task.status.color"); //returns "#fff"
*
* let objX = {
* tasks: [
* {
* id: "12341234",
* status: {
* color: "#fff"
* }
* },
* {
* id: "55424",
* status: {
* color: "#f5f5f5"
* }
* },
* {
* id: "90898080",
* status: {
* color: "#eee"
* }
* },
* ]
* };
* getValueFromJSON(objX, "task[1].status.color"); //returns "#f5f5f5"
* getValueFromJSON(objX, "task[$, ].status.color", "$"); //returns "#fff, #f5f5f5, #eee"
* // Elaborately,
* // In `[$, ]` --split into identifier and separator
* // '$'=identifier(from params).
* // Remaining part inside [] is the separator i.e., `', '` will be used to join the results from the array
* */
function getValueFromJSON(object, path, identifier){
try{
if(!path){
return object;
}
let keys = path.split('.');
for(let i=0; i<keys.length; i++){
let key_in = keys[i];
if(key_in.indexOf('[') !== -1){
let arr = key_in.split('[');
let arrName = arr[0];
let index = arr[1].split(']')[0];
if(isNaN(index)){
let rite = object[arrName];
let separator = index.substring(identifier.length, index.length);
let formattedVal = '';
for(let j=0; j<rite.length; j++){
if(j>0){
formattedVal += separator;
}
formattedVal += getValueFromJSON(rite[j], path.split('.').splice(i+1).join('.'), identifier);
}
object = formattedVal;
}else{
object = object[arrName][index];
}
break;
}else{
object = object[key_in];
}
}
return object;
}catch (e) {
return undefined;
}
}
/**
* @function
* @global
* @param {Object} object - object to set value to
* @param {String} path - dot separated path.
* @param {String|Number|Boolean|Object|Array} value - value to be set fot the given key
* @description
* Sets value to provided object at specified depth in the path be using dot separators.
* Creates objects at depths if no object already exists at specified path.
* @example
* let kit = {'has_bat': true}
* setValueToJson(kit, 'has_ball', true); //let kit = {'has_bat': true, 'has_ball': true}
* setValueToJson(kit, 'has_bat', false); //let kit = {'has_bat': false, 'has_ball': true}
*
* let aeroplane = {'name': 'B747'}
* setValueToJson(aeroplane, 'tank.capacity', '4000'); //aeroplane = {'name': 'B747', 'tank': { 'capacity': '4000' }}
* setValueToJson(aeroplane, 'tank.shape', 'amoeba'); //aeroplane = {'name': 'B747', 'tank': { 'capacity': '4000', 'shape': 'amoeba' }}
* */
function setValueToJson(object, path, value){
let keys = path.split('.');
loop(keys, function(i, key){
if(i+1 < keys.length){
if(!object.hasOwnProperty(key) || !object[key] || typeof object[key] !== "object"){
object[key]={}
}
object=object[key];
}else{
object[key]=value
}
});
}
/**
* @typedef {Object|Array|String|Boolean|Number|null|undefined} _MultiParams - numerous arguments of any kind
* @description
* A function if accepts multiple params, can use this as param type.
* @example
* //def
* function mpFn({String} param1, {_MultiParams} m_params){}
* //call
* mpFn("Str_param1", "p1", "p2", 1, {'key':'val'}, ['m1', 'm2', ...], ...);
* //def2
* function mp2Fn({String} param1, {_MultiParams} m_params, {String} param2){}
* //call2
* mp2Fn("Str_param1", "p1", "p2", 1, {'key':'val'}, ['m1', 'm2', ...], ..., "StrX_param2");
*
* */
/**
* @function
* @global
* @param {Function|String} fn - function to be called or global path to a function
* @param {_MultiParams} params - any params that to be passed to function
* @description
* calls the function with params passed
* @example
* let gv = {}; //global_letiable
* gv.sample_fn = function(param1, param2, param3){
* //Do Something...
* return "Hello from SampleFN"
* };
* call_fn('gv.sample_fn', param1, param2, param3); //returns "Hello from SampleFN"
* */
function call_fn(fn){
if(!fn){return;}
let args = Array.prototype.slice.call(arguments);
args.splice(0,1);
if(typeof(fn) === "function"){
return fn.apply(fn, args);
}else{
try{
fn = getValueFromJSON(window, fn);
if(fn && fn !== window){
return fn.apply(window[fn], args);
}
}catch(e){
if(e.name === "TypeError" && e.message.indexOf("is not a function") !== -1){
throw new Error("error executing function : "+fn);
}else{
throw e;
}
}
}
}
/**
* Can be used if required to call a function, and if it is unavailable, call a default fn
* Usage : call_fn_or_def(fn, arg1, arg2, arg3, def_fn)
* Result : fn(arg1, arg2, arg3) if fn is not undefined else def_fn(arg1, arg2, arg3)
* */
/**
* @function
* @global
* @param {Function|String} fn - function to be called or global path to a function
* @param {_MultiParams} params - any params that to be passed to function
* @param {Function|String} fn - function to be called or global path to a function
* @description
* calls the function with params passed
* @example
* let gv = {}; //global_letiable
* gv.sample_fn = function(param1, param2, param3){
* //Do Something...
* return "Hello from SampleFN"
* };
* call_fn('gv.sample_fn', param1, param2, param3, defaultFunction); //returns "Hello from SampleFN"
* */
function call_fn_or_def(fn){
let args = Array.prototype.slice.call(arguments);
let def_fn = args.pop();
if( ! ( typeof(def_fn) === "function" || typeof(window[def_fn]) === "function" ) ){
args.push(def_fn);
def_fn = undefined;
}
if(fn){
return call_fn.apply(call_fn, args);
}else if(def_fn){
args[0] = def_fn;
return call_fn.apply(call_fn, args);
}
}
/**
* Loop - looper iterator function
* @callback LoopIteratorCallback
* @param {String|Number|RegExp} key - key will be string in case of an object and Number will be an Array while looping through an Array.
* @param {*} value - Value of iterable for key.
* @returns {Boolean}
* return `false` to stop iteration.
* */
/**
* @function
* @global
* @param {Object|Array} object - Iterable.
* @param {LoopIteratorCallback} cbk - callback for each value in iterable.
* @param {Boolean} [reverseIterate=false] - iterate in reverse if it is an array.
* */
function loop(object, cbk, reverseIterate) {
let i, loop_size;
if(object instanceof Array){
loop_size = object.length;
if(reverseIterate){
for(i=loop_size-1; i>=0; i--){
if(cbk(i, object[i]) === false){
break;
}
}
}else{
for(i=0; i<loop_size; i++){
if(cbk(i, object[i]) === false){
break;
}
}
}
}else{
let keys = Object.keys(object);
loop_size = keys.length;
for(i=0; i<loop_size; i++){
if(cbk(keys[i], object[keys[i]]) === false){
break;
}
}
}
}
/**
* @typedef {String} HandlebarTemplate
* @description handlebar template can be an inline template or a pre compiled template that is created in a HBS file.
* */
/**
* @namespace
* @description
* TemplateUtils is a set of utilities related to handlebars
* */
let TemplateUtils = {};
(function(){
let preCompiledCache = {};
/**
* @function
* @static
* @param {HandlebarTemplate} template_or_template_string - Handlebar template name or template string.
* @param {Object} context - content to be passed t provided handlebar template/string.
* @param {Boolean} [cache=true] - whether to cache if the provided template is a Handlebar string template.
* @returns {String} compiled adn executed handlebar template
* @example
* //using template
* TemplateUtils.execute('template-file-name', {context_key1: "value1", ...});
*
* //using template string
* TemplateUtils.execute('Hello {{name}}', {name: "Adhara"}); //returns "Hello Adhara"
* */
TemplateUtils.execute = function(template_or_template_string, context, cache){
//Check if it is a pre compiled hbs template
let template = window.Handlebars.templates[template_or_template_string];
if(!template && window.Handlebars.hasOwnProperty("compile")){
//If template not found in precompiled hbs list => template is a handlebar string template
// check if cacheable
if(cache !== false){
//check if the template is already cached.
template = preCompiledCache[template_or_template_string];
if(!template){
//else compile, store it in cache and proceed to execution
template = preCompiledCache[template_or_template_string] = window.Handlebars.compile(template_or_template_string);
}
}else{
//else compile and proceed to execution
template = window.Handlebars.compile(template_or_template_string);
}
}
//execute with the provided context and return the output content...
return template(context);
};
})();
class Internationalize{
/**
* @constructor
* @param {Object<String, String>} key_map - i18n key map
* */
constructor(key_map){
this.key_map = key_map;
}
/**
* @instance
* @function
* @param {String} key - key
* @param {Array<String>} subs - substitutes
* @param {String} default_value - will be returned if key is not availalbe in the keymap
* */
getValue(key, subs, default_value){
if(!key){return;}
let value = this.key_map[key];
if(value===undefined){
value = getValueFromJSON(this.key_map, key);
}
if(value===undefined){
return default_value;
}
subs = subs || [];
let placeholders = value.match(/{[0-9]+}/g) || [];
for(let i=0; i<placeholders.length; i++){
let sub = subs[i] || "";
try {
if (sub.indexOf('.') !== -1) {
sub = this.get(sub);
}
}catch(e){/*Do Nothing*/}
value = value.replace( new RegExp( "\\{"+i+"\\}","g"), sub );
}
return value.trim();
}
/**
* @instance
* @function
* @param {String} key - key
* @param {Array<String>} [subs=[]] - substitutes
* */
get(key, subs){
return this.getValue(key, subs, key);
}
}
// Enable multi extend functionality https://stackoverflow.com/a/45332959
function MutateViews(baseClass, ...mixins){
class base extends baseClass {
constructor (...args) {
super(...args);
mixins.forEach((mixin) => {
copyProps(this,(new mixin(...args)));
});
}
}
function copyProps(target, source){ // this function copies all properties and symbols, filtering out some special ones
//TODO "Object.getOwnPropertyNames" and "Object.getOwnPropertySymbols" Doesn't work in IE even after transpilation to ES5...
Object.getOwnPropertyNames(source)
.concat(Object.getOwnPropertySymbols(source))
.forEach((prop) => {
if (!prop.match(/^(?:constructor|prototype|arguments|caller|name|bind|call|apply|toString|length)$/))
Object.defineProperty(target, prop, Object.getOwnPropertyDescriptor(source, prop));
})
}
mixins.forEach((mixin) => { // outside contructor() to allow aggregation(A,B,C).staticFunction() to be called etc.
copyProps(base.prototype, mixin.prototype);
copyProps(base, mixin);
});
return base;
}
function cloneObject(obj) {
let clone = (obj instanceof Array)?[]:{};
for(let i in obj) {
if(obj[i] != null && typeof(obj[i])==="object"){
clone[i] = cloneObject(obj[i]);
}else{
clone[i] = obj[i];
}
}
return clone;
}
class Time{
static async sleep(millis){
return new Promise((resolve, reject)=>{
setTimeout(resolve, millis);
})
}
}