diff --git a/README.md b/README.md index 61dadc6..fc198b7 100644 --- a/README.md +++ b/README.md @@ -1,11 +1,27 @@ -# c3r38r17ools +# c3tools Collection of personal sets of functions to speed up web development. ## TO DO -- Finish TO DO list - Debug new changes introduced. -- Add version number -- Get a CDN. +- Change SqS's cantidad parameter. +- Add version number. - Minified version. -- Add JSDocs -- Document functions here. +- ~~Get a CDN.~~ Publish to NPM. +- Translate SqS completely. +- Try to get rid of `// TODO`s. +- Document functions here (with **examples**). +- The "Soon!" stuff. + +## Soon! +`download` function. +
+Modal messages with premises. +
+Easy way to add functions to the 'DOMContentLoaded' event. +
+Module version. +
+Reacting to DOM changes. + +--- +Any suggestion or feedback is welcome. \ No newline at end of file diff --git a/c3r38r17ools.js b/c3r38r17ools.js deleted file mode 100644 index b62bdda..0000000 --- a/c3r38r17ools.js +++ /dev/null @@ -1,240 +0,0 @@ -//Utilities - -const W=window,D=document - ,ALL=true,ONLY_ONE=false - ,OTHER=-1,NUMBER=0,STRING=1,ARRAY=2,OBJECT=3,BOOLEAN=4,NULL=5; -var B; - -addEventListener('DOMContentLoaded',()=>{ - B=D.body; -}); - -function is(variable,type){ - if(variable==null && type==NULL) - return true; - let types={ - [NUMBER]:['number',Number] - ,[STRING]:['string',String] - ,[BOOLEAN]:['boolean',Boolean] - } - switch(type){ - case 0: - case 1: - case 4: - return types[type][0]==typeof variable || variable instanceof types[type][1]; - case 2: - return Array.isArray(variable); - case 3: - return 'object'==typeof variable && !Array.isArray(variable); - } - return type==OTHER; -} - -function whatIs(variable){ - switch(typeof variable){ - case 'number': - return NUMBER; - case 'string': - return STRING; - case 'object': - switch(true){ - case Array.isArray(variable): - return ARRAY; - case variable instanceof String: - return STRING; - case variable instanceof Number: - return NUMBER; - case variable==null: - return NULL; - default: - return OBJECT; - } - default: - return OTHER; - } -} - -const gEt=id=>D.getElementById(id); - -function SqS(selector,{cantidad=ONLY_ONE,ancestroComun=D}={}){ - if(selector instanceof Node)//??? Node vs Element - return selector; - if(is(selector,STRING)){ - let resultados, restoDeSelector=selector.slice(1); - if(/[ :\[\.#,+~]/.test(restoDeSelector)) - if(!cantidad||cantidad===1) - return ancestroComun.querySelector(selector) - else if(cantidad===true) - return ancestroComun.querySelectorAll(selector); - else resultados=ancestroComun.querySelectorAll(selector); - else switch(selector[0]){ - case '#': - return D.getElementById(restoDeSelector); - case '.': - resultados=ancestroComun.getElementsByClassName(restoDeSelector); - break; - case '[': - let nameMatch=/^\[name="([^"]*)"\]$/.exec(selector); - if(nameMatch) - resultados=D.getElementsByName(nameMatch[1]); - break; - case ':': - break; - default: - resultados=ancestroComun.getElementsByTagName(selector); - } - if(!cantidad||cantidad===1) - return resultados?resultados[0]:D.querySelector(selector); - else if(cantidad===true) - return resultados?resultados:D.querySelectorAll(selector); - else{ - if(!resultados) - resultados=D.querySelectorAll(selector); - if(cantidad>=resultados.length) - return resultados; - let respuesta=[]; - for(let i=0;i1?results:results[0]; -} - -//fetching - -function sendJSON(url,JSONdata,otherOptions=null){ - let defaultOptions={ - credentials:'include' - ,method:'POST' - ,headers:{'Content-Type':'application/json'} - ,body:JSON.stringify(JSONdata) - }; - return fetch(url,otherOptions?Object.assign(defaultOptions,otherOptions):defaultOptions); -} - -function* JSONAsURLEncodedStringIterator(obj,prefix=null){ - let pairs=Array.isArray(obj)? - obj.map(el=>['',el]) - :Object.entries(obj); - for (let [k,v] of pairs){ - k = prefix ? prefix + "[" + k + "]" : k; - if(v != null && typeof v == "object") - yield* JSONAsURLEncodedStringIterator(v, k); - else yield [k,v]; - } -} - -function JSONAsFormData(obj){ - if(!(is(obj,ARRAY) || is(obj,OBJECT))) - return; - - let fd=new FormData; - for(let key in obj){ - let value=obj[key]; - if(value != null && !(value instanceof File) && typeof value == "object"){ // objects and arrays - for(let pair of JSONAsURLEncodedStringIterator(value,key)) - fd.append(...pair); - }else fd.append(key,value); - } - return fd; -} - -function sendPOST(url,data,{returnType=null,otherOptions=null}={}){ - if(!(data instanceof FormData)) - data=JSONAsFormData(data); - - let options={ - credentials:'include' - ,method:'POST' - ,body:data - }; - if(otherOptions) - Object.assign(options,otherOptions); - - let f=fetch(url,options); - return returnType? - f.then(r=>r[returnType]()) - :f; -} - -function fetchConCredentials(url,options,...rest){ - return fetch(url,Object.assign({credentials:'include'},options),...rest); -} \ No newline at end of file diff --git a/c3tools.js b/c3tools.js new file mode 100644 index 0000000..aa63497 --- /dev/null +++ b/c3tools.js @@ -0,0 +1,354 @@ +//Utilities + +const W=window,D=document + ,ALL=true,ONLY_ONE=false; +/** @const {HTMLBodyElement} */ +var B; +/** + * Enum for types of variables. + * @readonly + * @enum {number} + */ +var Types={ + OTHER:-1 + ,NUMBER:0 + ,STRING:1 + ,ARRAY:2 + ,OBJECT:3 + ,BOOLEAN:4 + ,NULL:5 +}; + +addEventListener('DOMContentLoaded',()=>{ + B=D.body; +}); + +/** + * Checks if the variable is of certain type + * @param {*} variable - Variable to check type. + * @param {Types} type - Type to check. + * @returns {boolean} If variable is of said type. + */ +function is(variable,type){ + if(variable==null && type==Types.NULL) + return true; + let types={ + [Types.NUMBER]:['number',Number] + ,[Types.STRING]:['string',String] + ,[Types.BOOLEAN]:['boolean',Boolean] + } + switch(type){ + case 0: + case 1: + case 4: + return types[type][0]==typeof variable || variable instanceof types[type][1]; + case 2: + return Array.isArray(variable); + case 3: + return 'object'==typeof variable && !Array.isArray(variable); + } + return type==Types.OTHER; +} + +/** + * Checks and returns the type of the variable. + * @param {*} variable - Variable to check type + * @returns {Types} Type of the variable. + */ +function whatIs(variable){ + switch(typeof variable){ + case 'number': + return Types.NUMBER; + case 'string': + return Types.STRING; + case 'object': + switch(true){ + case Array.isArray(variable): + return Types.ARRAY; + case variable instanceof String: + return Types.STRING; + case variable instanceof Number: + return Types.NUMBER; + case variable==null: + return Types.NULL; + default: + return Types.OBJECT; + } + default: + return Types.OTHER; + } +} + +/** + * Wrapper for getElementById. + * @param {string} id - id to search for. + * @returns {?HTMLElement} Matching element or null if none exists. + */ +const gEt=id=>D.getElementById(id); + +/** + * Wrapper for querySelector, querySelectorAll, and any other DOM query methods. + * @param {string} selector - CSS selector to look for. + * @param {object} [obj]={} - Wrapper + * @param {(number|boolean)} [obj.cantidad]=ONLY_ONE - Ammount of Nodes to return, defaults to false (ONLY_ONE). + * @param {HTMLElement} [obj.ancestroComun]=D - DOM element on which the query will be done. + * @returns {(HTMLElement|NodeList|boolean)} The element or false if cantidad was 1 or false, a NodeList if cantidad was more than 1 or true. + */ +// TODO translate +// TODO cambiar cantidad por n +function SqS(selector,{cantidad=ONLY_ONE,ancestroComun=D}={}){ + if(selector instanceof Node)//??? Node vs HTMLElement + return selector; + if(is(selector,Types.STRING)){ + let resultados, restoDeSelector=selector.slice(1); + if(/[ :\[\.#,+~]/.test(restoDeSelector)) + if(!cantidad||cantidad===1) + return ancestroComun.querySelector(selector) + else if(cantidad===true) + return ancestroComun.querySelectorAll(selector); + else resultados=ancestroComun.querySelectorAll(selector); + else switch(selector[0]){ + case '#': + // TODO reconsider + let resultado = D.getElementById(restoDeSelector); + return resultado.closest(selector) + ?resultado + :false; + case '.': + resultados=ancestroComun.getElementsByClassName(restoDeSelector); + break; + case '[': + let nameMatch=/^\[name="([^"]*)"\]$/.exec(selector); + if(nameMatch) + resultados=D.getElementsByName(nameMatch[1]); + break; + case ':': + break; + default: + resultados=ancestroComun.getElementsByTagName(selector); + } + if(!cantidad||cantidad===1) + return resultados?resultados[0]:D.querySelector(selector); + else if(cantidad===true) + return resultados?resultados:D.querySelectorAll(selector); + else{ + if(!resultados) + resultados=D.querySelectorAll(selector); + if(cantidad>=resultados.length) + return resultados; + let respuesta=[]; + for(let i=0;i1?results:results[0]; +} + +//fetching + +/** + * Sends JSON as POST request. + * @param {string} url - The target URL. + * @param {object} JSONdata - The request payload. + * @param {object} otherOptions - Options for the fetch operation. + * @returns {Promise} The resulting Promise from the fetch operation. + */ +function sendJSON(url,JSONdata,otherOptions=null){ + let defaultOptions={ + credentials:'include' + ,method:'POST' + ,headers:{'Content-Type':'application/json'} + ,body:JSON.stringify(JSONdata) + }; + return fetch(url,otherOptions?Object.assign(defaultOptions,otherOptions):defaultOptions); +} + +/** + * A recursive iterator that turns a JSON object into URL string key-value pairs. + * @param {object} obj - The object to parse. + * @param {string} prefix - The path to this object. + * @yields {[string, string]} Key-value pair. + */ +function* JSONAsURLEncodedStringIterator(obj,prefix=null){ + let pairs=Array.isArray(obj)? + obj.map(el=>['',el]) + :Object.entries(obj); + for (let [k,v] of pairs){ + k = prefix ? prefix + "[" + k + "]" : k; + if(v != null && typeof v == "object") + yield* JSONAsURLEncodedStringIterator(v, k); + else yield [k,v]; + } +} + +/** + * Turns a regular object into a FormData object. + * @param {(Array|object)} obj - Original object. + * @returns {FormData} The data as a FormData. + */ +function JSONAsFormData(obj){ + if(!(is(obj,Types.ARRAY) || is(obj,Types.OBJECT))) + return; + + let fd=new FormData; + for(let key in obj){ + let value=obj[key]; + if(value != null && !(value instanceof File) && typeof value == "object"){ // objects and arrays + for(let pair of JSONAsURLEncodedStringIterator(value,key)) + fd.append(...pair); + }else fd.append(key,value); + } + return fd; +} + +/** + * Sends JSON as FormData. This differs from sendJSON in that this can send files. + * @param {string} url - The URL where to send the data + * @param {(object|FormData)} data - The data to be sent. + * @param {string} [returnType=null] - If provided, the Promise returned will not be the one returned by the fetch call, but the function of this name on the response. Example values: "json", "text" + * @param {object} [otherOptions=null] - Other fetch options that should be applied. + * @return {Promise} The Promise of the fetch request or its response. + */ +function sendPOST(url,data,{returnType=null,otherOptions=null}={}){ + if(!(data instanceof FormData)) + data=JSONAsFormData(data); + + let options={ + credentials:'include' + ,method:'POST' + ,body:data + }; + if(otherOptions) + Object.assign(options,otherOptions); + + let f=fetch(url,options); + return returnType? + f.then(r=>r[returnType]()) + :f; +} + +/** + * Just like a regular fetch but with credentials:'include'. + * @param {string} url - URL to fetch. + * @param {object} options - Options to pass to the fetch function. + * @param {...*} rest - More parameters to pass to the fetch function. + * @returns {Promise} The promise returned from the fetch call. + */ +function fetchConCredentials(url,options,...rest){ + return fetch(url,Object.assign({credentials:'include'},options),...rest); +} \ No newline at end of file diff --git a/index.html b/index.html new file mode 100644 index 0000000..89a4440 --- /dev/null +++ b/index.html @@ -0,0 +1,26 @@ + + + + + + + + Document + + + + + \ No newline at end of file