UNPKG

mtt-simple

Version:

Biblioteca de componentes y helpers para desarrollo de formularios en SIMPLE digital

291 lines (259 loc) 8.69 kB
'use strict' const $ = require('jquery') /** * @typedef {Object} DatosUrlTramiteTypedef * @property {boolean} editable - indica si el formulario actual permite la edición y actualización de datos * @property {string} accion - ver | editar * @property {number} idEtapa - id instancia de la etapa asociada al trámite * @property {number} paso - número de paso dentro del flujo secuencial definido para la etapa */ /** * [jquery] Obtiene el título del formulario desde el 'h1' o 'legend' * dependiendo de la modalidad de vista/edición del formulario * en el flujo */ function obtenerTituloFormulario() { return $('fieldset legend, form > h1.title:first').text() } /** * Otiene el valor del token CrossSiteRequestForgery desde los meta */ function obtenerTokenCSRF() { return $('meta[name=csrf-token]').attr('content') || '' } /** * Transforma el contenido del objeto a un string de modo de hacer factible * su respaldo en los campos de texto. Como background, SIMPLE presenta * problemas al definir el contenido de campos input con objetos JSON serializados * @param {string | object} contenido - si es entregado un objeto, se prepara para guardar en el input * sin el último caracter * @returns string * @example * // returns "{ 'prop' : 'value' " * deshidratar({ 'prop' : 'value' }); * @example * // returns "[{ 'prop' : 'value' } " * deshidratar([{ 'prop' : 'value' }]); */ function deshidratar(contenido) { let c = contenido if (typeof contenido !== 'string') { const str = JSON.stringify(c) c = str.substr(0, str.length - 1) } return c } /** * Convierte el texto entregado en un objeto asumiendo que el * contenido fue deshidratado usando la función deshidratar actual * @param {string} contenido * @returns {Object} */ function hidratar(contenido) { if (typeof contenido !== 'string') { throw new Error('tipo del contenido debe ser un string') } if (contenido === '') return null if (!contenido.startsWith('{') && !contenido.startsWith('[')) { return JSON.parse(contenido) } const c = contenido + (contenido.startsWith('{') ? '}' : ']') return JSON.parse(c) } /** * Obtener html completo del elemento incluyendo la propia etiqueta */ function _outerHTML(selector) { return $('<div/>').append($(selector).clone()).html() } /** Transformar htmlEntities para agregarlas como contenido 'seguro' */ function _encode(str) { return $('<div />').text(str).html() } /** * Clonado de objetos usando JSON para solventar problemas de "deep copy" * @param {Object} obj * @returns {Object} */ function _clone(obj) { return JSON.parse(JSON.stringify(obj)) } /** * Formato numérico para corregir problemática de locale string en formato español * para cifras con 4 dígitos * @param {number} num - número a ser transformado */ function _format(num) { if (typeof num === 'number') { return num.toLocaleString('en', {}).replace(',', '.') } else { return '' } } /** * Tranforma la notación de tipo variable lowerCamelCase de modo de hacerla legible en * un texto tipo Title Case * @example * // returns 'Id Normativa' * camelToTitleCase('idNormativa') */ function camelToTitleCase(str) { return str .replace(/[0-9]{2,}/g, match => ` ${match} `) .replace(/[^A-Z0-9][A-Z]/g, match => `${match[0]} ${match[1]}`) .replace(/[A-Z][A-Z][^A-Z0-9]/g, match => `${match[0]} ${match[1]}${match[2]}`) .replace(/[ ]{2,}/g, _ => ' ') .replace(/\s./g, match => match.toUpperCase()) .replace(/^./, match => match.toUpperCase()) .trim(); } /** * Obtiene la fecha indicada en formato 'yyyy-MM-dd' * @param {Date} date */ function toStrDate(date) { const y = date.getFullYear() const m = (date.getMonth() + 1).toString().padStart(2, '0') const d = date.getDate().toString().padStart(2, '0') return `${y}-${m}-${d}` } /** * Obtiene la fecha indicada en formato 'hh:mm:ss' * @param {Date} date */ function toStrTime(date) { const hh = date.getHours().toString().padStart(2, '0') const mm = date.getMinutes().toString().padStart(2, '0') const ss = date.getSeconds().toString().padStart(2, '0') return `${hh}:${mm}:${ss}` } /** * Obtiene la fecha indicada en formato 'yyyy-MM-dd hh:mm:ss' * @param {Date} date */ function toStrDatetime(date) { return `${toStrDate(date)} ${toStrTime(date)}` } /** * Extrae desde la url la información de la Etapa actual * @param {string} url * @returns {DatosUrlTramiteTypedef} */ function etapaActual(url = window.location.href) { try { const regex = /\/etapas\/(ejecutar|ver|ejecutar_fin)\/(\d+)\/?(\d+)?/ const match = regex.exec(url) if (!match) { const msg = 'No fue posible identificar el id de etapa: ' + url // throw new Error('No fue posible identificar el id de etapa: ' + url) console.warn('etapaActual', msg) return {} } const datosUrl = { accion: match[1], editable: match[1] === 'ejecutar', idEtapa: match[2], paso: match[1] !== 'ejecutar_fin' ? (match.length > 3 ? match[3] : 0) : null } return datosUrl } catch (error) { console.error('obtenerIdEtapa: ' + url, error) return {} } } /** * @param {string} contenido - mensaje como contenido del alert * @param {string} tipo - asociados a componente alert: primary, secondary, success, danger, warning, info, light, dark */ function agregarNotificacion(contenido, tipo) { var tpl = $(`<div class="alert alert-${tipo || 'info'} alert-dismissible"><a href="#" class="close" data-dismiss="alert" aria-label="close">&times;</a>${contenido}</div>`) $('.validacion').append(tpl) $([document.documentElement, document.body]).animate({ scrollTop: $(".validacion").offset().top }, 1000); } /** * usando URI prueba si la el string entregado corresponde a uri * válida para evitar 404 en carga de archivos usando variables * @param {string} str */ function esUrlValida(str) { try { return new URL(str) } catch (_) { return null } } /** * @typedef {Object} CargarRegursoRespTypedef * @property {number} code - 0 en caso de exito * @property {string} message - exto descriptivo */ /** * carga de forma programática de un recurso css o js * @param {string} url - url del recurso para ser cargado * @param {string} tipo - js, css * @returns {Promise<CargarRegursoRespTypedef>} */ function cargarRecurso(url, tipo) { return new Promise((resolve) => { const resourceUrl = esUrlValida(url) if (resourceUrl === null) { resolve({ code: 1, message: `url '${url}' no válida para carga, no se inicia carga recurso` }) }; try { const tipoArchivo = tipo || resourceUrl.pathname.split('.').pop(); console.log(`Cargar archivo '${tipoArchivo}' desde ${url}`); let newNode = null; switch (tipo) { case 'js': newNode = document.createElement('script'); newNode.setAttribute('src', url); newNode.setAttribute('type', 'text/javascript'); break; case 'css': newNode = document.createElement('link'); newNode.setAttribute('href', url); newNode.setAttribute('rel', 'stylesheet'); newNode.setAttribute('type', 'text/css'); break; default: return; } newNode.onload = function () { resolve({ code: 0, message: `recurso '${url}' cargado` }) }; newNode.onerror = function (err) { resolve({ code: 2, message: `Error al cargar recurso '${url}' : ${err.message}` }) }; document.getElementsByTagName("head")[0].appendChild(newNode); } catch (error) { resolve({ code: 2, message: `Error no controlado al cargar recurso '${url}' : ${error.message}` }) } }) } /** * Entrega el identificador de campo SIMPLE en base al nombre del campo asignado * @param {string} nombreCampo * @returns 0 si el elemento no es encontrado */ function getIdCampo(nombreCampo) { const $c = $(`[name=${nombreCampo}]`).first() const $w = $c.closest('.campo.control-group') return parseInt($w.attr('data-id') || '0', 10) } module.exports = { obtenerTokenCSRF, agregarNotificacion, etapaActual, obtenerTituloFormulario, deshidratar, hidratar, _outerHTML, _encode, _clone, _format, camelToTitleCase, toStrDate, toStrTime, toStrDatetime, esUrlValida, cargarRecurso, getIdCampo }