Mapeate = function(entradas){ const self = this, configuracion_por_defecto = { nulos : false, valor_por_defecto : null, autoiniciar : true, archivos_de_configuracion_por_defecto : [ "/json/Mapeate.configuracion.json", "/json/Mapeate.configuracion.secretos.json" ], texto_por_defecto : "", configuracion_sobreescribir : false, i18n_sobreescribir : false, formato_terminal : "[{tipo}] {yyyy}{mm}{dd} {hh}{ii}{ss} [{linea}]{archivo}({metodo}): {mensaje}", terminal_tipos_por_defecto : [ ["unkn", "unknown", "desconocido"], [" ok ", "ok", "si", "correcto"], ["info", "informacion", "information"], ["note", "nota", "comment", "comentario"], ["warn", "warning", "aviso", "advertencia"], ["erro", "error", "incrrecto"], ["fail", "failure", "fallo"], ["exce", "excepcion"] ], archivos_de_i18n_por_defecto : [ "/json/i18n/Mapeate.i18n.espanol.json" ], ajax_timeout : 2000 }, configuracion = {}, textos = {}, terminal_tipos = []; let iniciado = false, configuracion_sobreescribir = false, idioma_por_defecto = null, idioma = null, modo_dispositivo = "por_defecto", modo_gui = "por_defecto", terminal_unkn_oscuro, terminal_unkn_claro, terminal_ok_oscuro, terminal_ok_claro, terminal_info_oscuro, terminal_info_claro, terminal_note_oscuro, terminal_note_claro, modo_gui_oscuro = window.matchMedia && window.matchMedia("(prefers-color-scheme: dark)").matches, formato_terminal = "[{tipo}] {yyyy}{mm}{dd} {hh}{ii}{ss} [{linea}]{archivo}({metodo}): {mensaje}"; const re_bloque_de_traza = new RegExp("^(" + [ /\s*at\s+(([^\s]+)\s+\()?(([^\(\)\:]+\:)?[^\(\)\:]+)(\:([0-9]+)\:[0-9]+)?\)?/.source, // Webkit /([^\@]+)\@([^:]+\:[^\:]+)\:([0-9]+)\:[0-9]+/.source, // Gecko ].join("|") + ")$") const constructor = () => { configuracion_por_defecto.terminal_tipos_por_defecto.forEach(entrada => terminal_tipos.push(entrada)); self.print("info", "mapeate_construyendose"); self.configuracion("autoiniciar") && self.iniciar(() => { self.print("ok", "mapeate_construido"); }); }; const establecer_variables_comunes = () => { configuracion_sobreescribir = self.configuracion(["configuracion_sobreescribir", "sobreescribir"]); formato_terminal = self.configuracion("formato_terminal"); terminal_unkn_oscuro = self.configuracion("terminal_unkn_oscuro"); terminal_unkn_claro = self.configuracion("terminal_ounkn_claro"); terminal_ok_oscuro = self.configuracion("terminal_ok_oscuro"); terminal_ok_claro = self.configuracion("terminal_ok_claro"); terminal_info_oscuro = self.configuracion("terminal_info_oscuro"); terminal_info_claro = self.configuracion("terminal_info_claro"); terminal_note_oscuro = self.configuracion("terminal_note_oscuro"); terminal_note_claro = self.configuracion("terminal_note_claro"); }; this.iniciar = callback => { const terminar = estado => typeof callback == "function" && callback(estado); if(iniciado){ terminar(false); return false; }; iniciado = true; establecer_variables_comunes(); self.configuracion_anadir(self.configuracion("archivos_de_configuracion_por_defecto"), null, () => { establecer_variables_comunes(); self.configuracion_anadir(self.configuracion("archivos_de_configuracion"), null, () => { establecer_variables_comunes(); self.terminal_tipos_anadir(self.configuracion("terminal_tipos_por_defecto"), () => { self.terminal_tipos_anadir(self.configuracion("terminal_tipos"), () => { i18n_sobreescribir = self.configuracion(["i18n_sobreescribir", "sobreescribir"]); idioma_por_defecto = self.configuracion(["idioma_por_defecto", "idioma"]); idioma = self.configuracion(["idioma", "idioma_por_defecto"]); self.i18n_anadir(self.configuracion("archivos_de_i18n_por_defecto"), null, () => { self.i18n_anadir(self.configuracion("archivos_de_i18n"), null,() => { terminar(true); console.log("PASA"); }); }); }); }); }); }); return true; }; this.nulos = nulos => typeof nulos != "boolean" ? self.configuracion("nulos", null, false, false) : nulos; this.valor_por_defecto = (valor_por_defecto, nulos) => ( valor_por_defecto !== undefined && (self.nulos(nulos) || valor_por_defecto !== null) ? valor_por_defecto : self.configuracion("valor_por_defecto", null, null, true) ); this.coger_array_de_diccionarios = valor => valor && typeof valor == "object" ? valor instanceof Array ? valor : [valor] : []; this.coger_array_de_strings = valor => ( valor && typeof valor == "object" && valor instanceof Array ? valor : [valor] ).filter(elemento => typeof elemento == "string" && elemento.trim()).map(elemento => elemento.trim()); this.configuracion = (claves, entradas, valor_por_defecto, nulos) => { const m = (claves = self.coger_array_de_strings(claves)).length; if(m){ const l = (entradas = [].concat( self.coger_array_de_diccionarios(entradas), [entradas, configuracion, configuracion_por_defecto] )).length; nulos = self.nulos(nulos); for(let i = 0; i < l; i ++) if(entradas[i] && typeof entradas[i] == "object") for(let j = 0; j < m; j ++) if(entradas[i][claves[j]] !== undefined && (nulos || entradas[i][claves[j]] !== null)) return entradas[i][claves[j]]; }; return self.valor_por_defecto(valor_por_defecto, nulos); }; this.leer_archivo = (url, entradas) => { let terminado = false; const ajax = new XMLHttpRequest(), callback = self.configuracion("callback", typeof entradas == "function" ? (entradas = {callback : entradas}) : entradas && typeof entradas == "object" ? entradas : {}), timeout = self.configuracion(["ajax_timeout", "timeout"], entradas), terminar = codigo => ( !terminado && (terminado = true) && typeof callback == "function" && callback(ajax.responseText, ajax.status, ajax.readyState, codigo) ), fecha = Date.now(); ajax.open("get", url, true); ajax.timeout = timeout; ajax.onreadystatechange = () => { if(terminado) return; if(ajax.readyState == 4) terminar( (ajax.status >= 200 && ajax.status < 300) || [301, 302, 304].includes(ajax.status) ? "OK" : "HTTP_ERROR"); else if(Date.now() - fecha > timeout) terminar("TIMEOUT_FORZADO"); }; ajax.send(null); ajax.onabort = () => terminar("ABORTADO"); ajax.onerror = () => terminar("ERROR"); ajax.ontimeout = () => terminar("TIMEOUT"); return ajax; }; const configuracion_anadir = (entradas, sobreescribir, callback, i) => { if(i >= entradas.length){ callback(); return; }; const terminar = () => configuracion_anadir(entradas, sobreescribir, callback, i + 1); if(!entradas[i]){ terminar(); return; }; if(typeof entradas[i] == "object"){ if(entradas[i] instanceof Array) configuracion_anadir(entradas[i], sobreescribir, terminar, 0); else{ for(const clave in entradas[i]) if(sobreescribir || configuracion[clave] === undefined) configuracion[clave] = entradas[i][clave]; terminar(); }; }else if(typeof entradas[i] == "string"){ if(!(entradas[i] = entradas[i].trim())){ terminar(); return; }; let json; if(entradas[i].match(/^(\{(.|[\r\n])+\}|\[(.|[\r\n])+\])$/)){ try{ json = JSON.parse(entradas[i]); }catch(excepcion){}; if(json) configuracion_anadir(json instanceof Array ? json : [json], sobreescribir, terminar, 0); else terminar(); }else self.leer_archivo(entradas[i], salida => { try{ json = JSON.parse(salida); }catch(excepcion){}; if(json) configuracion_anadir(json instanceof Array ? json : [json], sobreescribir, terminar, 0); else terminar(); }); }else terminar(); }; this.configuracion_anadir = (entradas, sobreescribir, callback) => configuracion_anadir( entradas && typeof entradas == "object" && entradas instanceof Array ? entradas : [entradas], typeof sobreescribir == "boolean" ? sobreescribir : configuracion_sobreescribir, callback, 0 ); this.string_variables = (string, variables, por_defecto) => { const l = (variables = self.coger_array_de_diccionarios(variables)).length; return (string || "" + string).replace(/\{([^\{\}]+)\}/g, (crudo, clave) => { for(let i = 0; i < l; i ++) if(variables[i][clave] !== undefined) return variables[i][clave]; return por_defecto === undefined ? crudo : por_defecto; }); }; const i18n = (claves, por_defecto) => { const m = (claves = self.coger_array_de_strings(claves)).length, idiomas = [idioma_por_defecto, idioma].concat(Object.keys(textos)), l = idiomas.length; for(let i = 0; i < l; i ++) if(typeof idiomas[i] == "string" && textos[idiomas[i]]) for(let j = 0; j < m; j ++) if(textos[idiomas[i]][claves[j]] !== undefined) return textos[idiomas[i]][claves[j]]; return ( por_defecto !== undefined ? por_defecto : m ? claves[0] : self.configuracion("texto_por_defecto")); }; this.i18n = (claves, variables, por_defecto) => self.string_variables(i18n(claves, por_defecto), variables); const i18n_anadir = (entradas, sobreescribir, callback, i) => { if(i >= entradas.length){ typeof callback == "function" && callback(); return; }; const terminar = () => i18n_anadir(entradas, sobreescribir, callback, i + 1); if(!entradas[i]){ terminar(); return; }; if(typeof entradas[i] == "object"){ if(entradas[i] instanceof Array) i18n_anadir(entradas[i], sobreescribir, terminar, 0); else{ for(const lenguage in entradas[i]){ textos[lenguage] === undefined && (textos[lenguage] = {}); if(entradas[i][lenguage] && typeof entradas[i][lenguage] == "object") for(const clave in entradas[i][lenguage]) if(sobreescribir || textos[lenguage][clave] === undefined) textos[lenguage][clave] = entradas[i][lenguage][clave]; }; terminar(); }; }else if(typeof entradas[i] == "string"){ if(!(entradas[i] = entradas[i].trim())){ terminar(); return; }; let json; if(entradas[i].match(/^(\{(.|[\r\n])+\}|\[(.|[\r\n])+\])$/)){ try{ json = JSON.parse(entradas[i]); }catch(excepcion){}; if(json) i18n_anadir(json instanceof Array ? json : [json], sobreescribir, terminar, 0); else terminar(); }else self.leer_archivo(entradas[i], salida => { try{ json = JSON.parse(salida); }catch(excepcion){}; if(json) i18n_anadir(json instanceof Array ? json : [json], sobreescribir, terminar, 0); else terminar(); }); }else terminar(); }; this.i18n_anadir = (entradas, sobreescribir, callback) => i18n_anadir( entradas && typeof entradas == "object" && entradas instanceof Array ? entradas : [entradas], typeof sobreescribir == "boolean" ? sobreescribir : i18n_sobreescribir, callback, 0 ); const terminal_tipos_anadir = (entradas, callback, i, clave) => { if(i > 10) return; if(!entradas || i >= entradas.length){ typeof callback == "function" && callback(); return; }; const terminar = () => terminal_tipos_anadir(entradas, callback, i + 1, clave); if(!entradas[i]){ terminar(); return; }; if(typeof entradas[i] == "string"){ if(/^ *[a-z0-9]+ *$/i.test(entradas[i])){ if(clave === undefined && !terminal_tipos.some((tipos, j) => tipos.includes(entradas[i]) && !isNaN(clave = j))){ clave = terminal_tipos.length; terminal_tipos.push([entradas[i]]); }else !terminal_tipos[clave].includes(entradas[i]) && terminal_tipos[clave].push(entradas[i]); terminar(); return; }; let json; if(/^(\[(.|[\r\n])+\])$/.test(entradas[i])){ try{ json = JSON.parse(entradas[i]); }catch(excepcion){}; if(json) terminal_tipos_anadir(json, terminar, 0, clave); else terminar(); }else self.leer_archivo(entradas[i], salida => { try{ json = JSON.parse(salida); }catch(excepcion){}; if(json) terminal_tipos_anadir(json, terminar, 0, clave); else terminar(); }); }else if(typeof entradas[i] == "object" && entradas[i] instanceof Array) terminal_tipos_anadir(entradas[i], terminar, 0); else terminar(); }; this.es_dispositivo_movil = () => modo_dispositivo == "movil" || (modo_dispositivo != "escritorio" && es_movil); this.modo_gui_oscuro = () => modo_gui == "oscuro" || (modo_gui != "claro" && modo_gui_oscuro); this.terminal_tipos_anadir = (entradas, callback) => terminal_tipos_anadir(entradas, callback, 0); this.coger_id_tipo_terminal = tipo => { const l = terminal_tipos.length; if(typeof tipo == "string" && (tipo = tipo.trim().toLowerCase())) for(let i = 0; i < l; i ++) if(terminal_tipos[i].includes(tipo)) return i; return 0; }; this.coger_tipo_terminal = tipo => terminal_tipos[self.coger_id_tipo_terminal(tipo)][0].toUpperCase(); this.coger_traza = i => (new Error()).stack.replace(/^Error\s*?[\r\n]+/, "").trim().split(/[\r\n]+/).slice(1 + (i || 0)).map(linea => { const matches = linea.match(re_bloque_de_traza); return matches ? { archivo : matches[4] || matches[9], metodo : matches[3] || matches[8] || "", linea : Number(matches[7] || matches[10]) } : null; }); this.print = (tipo, mensaje, variables, i) => { const fecha = new Date(), conjunto = { tipo_crudo : tipo, tipo : self.coger_tipo_terminal(tipo), i18n : mensaje, ...(typeof variables == "object" ? variables : {}), ...(self.coger_traza((isNaN(i) ? 1 : i) + 1) || { linea : -1, metodo : "UNKNOWN", archivo : "UNKNOWN" }) }, clave_tipo = conjunto.tipo.trim().toLowerCase(); let mensaje_procesado; conjunto.line = conjunto.linea; conjunto.method = conjunto.metodo; conjunto.file = conjunto.archivo; ["year", "month", "date", "hours", "minutes", "seconds"].forEach(clave => { let k = clave.substring(0, 1); const valor = fecha["get" + (clave == "year" ? "FullYear" : k.toUpperCase() + clave.substring(1))](); conjunto[clave == "date" ? "day" : clave] = valor; conjunto[clave == "minutes" ? k = "i" : k] = valor; conjunto[k + k] = ("00" + valor).slice(-2); }); conjunto["yyyy"] = conjunto["year"]; conjunto.mensaje = self.i18n(mensaje, conjunto); mensaje_procesado = self.string_variables(formato_terminal, conjunto); console.log(conjunto); switch(conjunto.tipo){ case " OK ": console.log("%c" + mensaje_procesado, self.modo_gui_oscuro() ? terminal_ok_oscuro : terminal_ok_claro); break; case "INFO": console.log("%c" + mensaje_procesado, self.modo_gui_oscuro() ? terminal_info_oscuro : terminal_info_claro); break; case "UNKN": console.log("%c" + mensaje_procesado, self.modo_gui_oscuro() ? terminal_note_oscuro : terminal_note_claro); break; case "ERRO": case "FAIL": case "EXCE": console.error(mensaje_procesado); break; case "WARN": console.warn(mensaje_procesado); break; case "UNKN": default: console.log("%c" + mensaje_procesado, self.modo_gui_oscuro() ? terminal_unkn_oscuro : terminal_unkn_claro); break; }; }; constructor(); };