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