xonecode 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +167 -0
- package/dist/agent/authEnDisco.js +56 -0
- package/dist/agent/authEnDisco.js.map +1 -0
- package/dist/agent/configEnDisco.js +109 -0
- package/dist/agent/configEnDisco.js.map +1 -0
- package/dist/agent/crearProyecto.js +34 -0
- package/dist/agent/crearProyecto.js.map +1 -0
- package/dist/agent/entorno.js +124 -0
- package/dist/agent/entorno.js.map +1 -0
- package/dist/agent/guionizado.js +53 -0
- package/dist/agent/guionizado.js.map +1 -0
- package/dist/agent/instantanea.js +129 -0
- package/dist/agent/instantanea.js.map +1 -0
- package/dist/agent/interrupts.js +83 -0
- package/dist/agent/interrupts.js.map +1 -0
- package/dist/agent/mensajes.js +24 -0
- package/dist/agent/mensajes.js.map +1 -0
- package/dist/agent/modelos.js +42 -0
- package/dist/agent/modelos.js.map +1 -0
- package/dist/agent/normalizar.js +20 -0
- package/dist/agent/normalizar.js.map +1 -0
- package/dist/agent/perfiles.js +103 -0
- package/dist/agent/perfiles.js.map +1 -0
- package/dist/agent/proyecto.js +107 -0
- package/dist/agent/proyecto.js.map +1 -0
- package/dist/agent/puente.js +142 -0
- package/dist/agent/puente.js.map +1 -0
- package/dist/agent/resumenDeTool.js +48 -0
- package/dist/agent/resumenDeTool.js.map +1 -0
- package/dist/agent/skills.js +65 -0
- package/dist/agent/skills.js.map +1 -0
- package/dist/agent/textoDeTool.js +113 -0
- package/dist/agent/textoDeTool.js.map +1 -0
- package/dist/agent/turnoReal.js +173 -0
- package/dist/agent/turnoReal.js.map +1 -0
- package/dist/agent/verificador.js +65 -0
- package/dist/agent/verificador.js.map +1 -0
- package/dist/agent/xoneAgent.js +106 -0
- package/dist/agent/xoneAgent.js.map +1 -0
- package/dist/bin.js +9 -0
- package/dist/bin.js.map +1 -0
- package/dist/cli/aprobar.js +82 -0
- package/dist/cli/aprobar.js.map +1 -0
- package/dist/cli/config.js +133 -0
- package/dist/cli/config.js.map +1 -0
- package/dist/cli/consola.js +346 -0
- package/dist/cli/consola.js.map +1 -0
- package/dist/cli/describe.js +53 -0
- package/dist/cli/describe.js.map +1 -0
- package/dist/cli/doctor.js +45 -0
- package/dist/cli/doctor.js.map +1 -0
- package/dist/cli/main.js +520 -0
- package/dist/cli/main.js.map +1 -0
- package/dist/cli/markdown.js +90 -0
- package/dist/cli/markdown.js.map +1 -0
- package/dist/cli/run.js +164 -0
- package/dist/cli/run.js.map +1 -0
- package/dist/cli/spinner.js +89 -0
- package/dist/cli/spinner.js.map +1 -0
- package/dist/cli/stdio.js +193 -0
- package/dist/cli/stdio.js.map +1 -0
- package/dist/cli/tema.js +40 -0
- package/dist/cli/tema.js.map +1 -0
- package/dist/cli/verify.js +62 -0
- package/dist/cli/verify.js.map +1 -0
- package/dist/core/bitacora.js +30 -0
- package/dist/core/bitacora.js.map +1 -0
- package/dist/core/config.js +236 -0
- package/dist/core/config.js.map +1 -0
- package/dist/core/contextos.js +56 -0
- package/dist/core/contextos.js.map +1 -0
- package/dist/core/deps.js +51 -0
- package/dist/core/deps.js.map +1 -0
- package/dist/core/diff.js +79 -0
- package/dist/core/diff.js.map +1 -0
- package/dist/core/esqueleto.js +366 -0
- package/dist/core/esqueleto.js.map +1 -0
- package/dist/core/events.js +2 -0
- package/dist/core/events.js.map +1 -0
- package/dist/core/modelos.js +85 -0
- package/dist/core/modelos.js.map +1 -0
- package/dist/core/notify.js +106 -0
- package/dist/core/notify.js.map +1 -0
- package/dist/core/ports.js +114 -0
- package/dist/core/ports.js.map +1 -0
- package/dist/core/turno.js +127 -0
- package/dist/core/turno.js.map +1 -0
- package/dist/vendor/hitl.js +103 -0
- package/dist/vendor/hitl.js.map +1 -0
- package/dist/vendor/skillLoaders/catalog.js +74 -0
- package/dist/vendor/skillLoaders/catalog.js.map +1 -0
- package/dist/vendor/tokenTracking.js +58 -0
- package/dist/vendor/tokenTracking.js.map +1 -0
- package/package.json +41 -0
- package/skills/xone-debugging/SKILL.md +52 -0
- package/skills/xone-debugging/references/faq.md +561 -0
- package/skills/xone-debugging/references/troubleshooting-y-glosario.md +429 -0
- package/skills/xone-development/SKILL.md +105 -0
- package/skills/xone-development/references/anti-patrones.md +94 -0
- package/skills/xone-development/references/css/atributos-por-categoria.md +509 -0
- package/skills/xone-development/references/css/buenas-practicas-y-parser.md +419 -0
- package/skills/xone-development/references/css/dinamicos-cascada-y-componentes.md +550 -0
- package/skills/xone-development/references/css/patrones-material-y-temas.md +1140 -0
- package/skills/xone-development/references/css/propiedades-y-herencia.md +885 -0
- package/skills/xone-development/references/css/selectores-unidades-colores.md +840 -0
- package/skills/xone-development/references/datos/appdata-referencia-ampliada.md +457 -0
- package/skills/xone-development/references/datos/appdata.md +611 -0
- package/skills/xone-development/references/datos/http-sqlmanager-y-crypto.md +720 -0
- package/skills/xone-development/references/datos/http.md +366 -0
- package/skills/xone-development/references/datos/oauth2-y-replica.md +156 -0
- package/skills/xone-development/references/device/biometria-imagedrawing-y-otros.md +544 -0
- package/skills/xone-development/references/device/objetos-de-dispositivo.md +583 -0
- package/skills/xone-development/references/device/systemsettings-referencia-ampliada.md +396 -0
- package/skills/xone-development/references/device/systemsettings-y-permisos.md +342 -0
- package/skills/xone-development/references/fundamentos/conceptos-clave.md +695 -0
- package/skills/xone-development/references/fundamentos/configuracion-app-xml-ini-mappings.md +668 -0
- package/skills/xone-development/references/fundamentos/errores-comunes.md +312 -0
- package/skills/xone-development/references/fundamentos/navegacion-convenciones-y-primer-proyecto.md +576 -0
- package/skills/xone-development/references/fundamentos/plataforma-y-anatomia-de-proyecto.md +387 -0
- package/skills/xone-development/references/indice-completo.md +77 -0
- package/skills/xone-development/references/javascript/coleccion-error-y-usuario.md +260 -0
- package/skills/xone-development/references/javascript/debugging-y-best-practices.md +245 -0
- package/skills/xone-development/references/javascript/metodos-de-los-controles.md +556 -0
- package/skills/xone-development/references/javascript/metodos-nativos-de-la-vista.md +114 -0
- package/skills/xone-development/references/javascript/motor-js-y-contexto-de-ejecucion.md +319 -0
- package/skills/xone-development/references/javascript/objeto-ai-llm-en-dispositivo.md +358 -0
- package/skills/xone-development/references/javascript/objetos-creables-a-m.md +527 -0
- package/skills/xone-development/references/javascript/objetos-creables-n-z.md +385 -0
- package/skills/xone-development/references/javascript/patrones-criticos-seguridad-y-rendimiento.md +595 -0
- package/skills/xone-development/references/javascript/patrones-de-navegacion-datos-y-codigo.md +751 -0
- package/skills/xone-development/references/javascript/patrones-de-ui-voz-integracion-y-seguridad.md +701 -0
- package/skills/xone-development/references/javascript/plantillas-y-funciones-utilitarias.md +647 -0
- package/skills/xone-development/references/javascript/self-y-dataobject.md +449 -0
- package/skills/xone-development/references/javascript/singletons-globales.md +361 -0
- package/skills/xone-development/references/javascript/ui-catalogo-de-metodos.md +464 -0
- package/skills/xone-development/references/javascript/ui-gps-camara-y-multimedia.md +506 -0
- package/skills/xone-development/references/javascript/ui-navegacion-mensajes-y-vista.md +422 -0
- package/skills/xone-development/references/resumen-js-datos-dispositivo.md +80 -0
- package/skills/xone-development/references/resumen-xml-y-css.md +74 -0
- package/skills/xone-development/references/tipos-de-prop.md +28 -0
- package/skills/xone-development/references/xml-ui/asfilter-visibilidad-eventos-y-macros.md +576 -0
- package/skills/xone-development/references/xml-ui/atributos-coll-group-frame.md +235 -0
- package/skills/xone-development/references/xml-ui/atributos-method-macro-script-event-app.md +244 -0
- package/skills/xone-development/references/xml-ui/atributos-prop.md +439 -0
- package/skills/xone-development/references/xml-ui/contents-y-macros.md +392 -0
- package/skills/xone-development/references/xml-ui/errores-comunes-xml.md +245 -0
- package/skills/xone-development/references/xml-ui/estructura-y-nodo-coll.md +326 -0
- package/skills/xone-development/references/xml-ui/eventos-ciclo-de-vida-e-interaccion.md +704 -0
- package/skills/xone-development/references/xml-ui/eventos-sistema-login-y-personalizados.md +944 -0
- package/skills/xone-development/references/xml-ui/layouts-herencia-y-buenas-practicas.md +618 -0
- package/skills/xone-development/references/xml-ui/mapas.md +685 -0
- package/skills/xone-development/references/xml-ui/mappings-y-colecciones-separadas.md +148 -0
- package/skills/xone-development/references/xml-ui/nodos-group-y-frame.md +666 -0
- package/skills/xone-development/references/xml-ui/patrones-de-pantalla.md +643 -0
- package/skills/xone-development/references/xml-ui/prop-atributos-y-condiciones.md +298 -0
- package/skills/xone-development/references/xml-ui/prop-tipos-basicos.md +493 -0
- package/skills/xone-development/references/xml-ui/prop-tipos-combos-y-controles.md +664 -0
- package/skills/xone-development/references/xml-ui/prop-tipos-listas-y-mapas.md +412 -0
- package/skills/xone-plan-builder/SKILL.md +183 -0
- package/skills/xone-plan-builder/agents/openai.yaml +5 -0
- package/skills/xone-plan-builder/references/TASKS-FORMAT.md +132 -0
- package/skills/xone-project-generator/SKILL.md +111 -0
- package/skills/xone-project-generator/references/canonical-sizes.md +292 -0
- package/skills/xone-project-generator/references/colecciones-base.md +16 -0
- package/skills/xone-project-generator/references/convenciones-de-nombres.md +16 -0
- package/skills/xone-project-generator/references/ejemplos-por-sector-y-prohibiciones.md +140 -0
- package/skills/xone-project-generator/references/fase-3-estilos-css.md +597 -0
- package/skills/xone-project-generator/references/fase-6-colecciones.md +762 -0
- package/skills/xone-project-generator/references/fase-7-asfilter-e-integraciones.md +142 -0
- package/skills/xone-project-generator/references/fase-7-entidades-y-estructura-de-pantalla.md +494 -0
- package/skills/xone-project-generator/references/fase-7-plantilla-consola.md +477 -0
- package/skills/xone-project-generator/references/fase-7-plantillas-de-pantalla.md +302 -0
- package/skills/xone-project-generator/references/fase-7-viewmodes-graficos-y-listas.md +681 -0
- package/skills/xone-project-generator/references/fase-7-viewmodes-mapa-y-calendario.md +479 -0
- package/skills/xone-project-generator/references/fases-0-2-analisis-y-modelo-de-datos.md +350 -0
- package/skills/xone-project-generator/references/fases-10-12-readmes-y-validacion.md +194 -0
- package/skills/xone-project-generator/references/fases-4-5-estructura-y-configuracion.md +587 -0
- package/skills/xone-project-generator/references/fases-8-9-eventos-y-javascript.md +861 -0
- package/skills/xone-project-generator/references/flujo-de-generacion.md +67 -0
- package/skills/xone-project-generator/references/plantillas-estandar.md +98 -0
- package/skills/xone-project-generator/references/tamanos-canonicos.md +49 -0
- package/skills/xone-review/SKILL.md +189 -0
- package/skills/xone-spec-builder/SKILL.md +235 -0
- package/skills/xone-spec-builder/agents/openai.yaml +5 -0
- package/skills/xone-spec-builder/references/ADR-FORMAT.md +55 -0
- package/skills/xone-spec-builder/references/CONTEXT-FORMAT.md +42 -0
- package/skills/xone-spec-builder/references/PLAN-FORMAT.md +120 -0
package/skills/xone-development/references/javascript/patrones-de-ui-voz-integracion-y-seguridad.md
ADDED
|
@@ -0,0 +1,701 @@
|
|
|
1
|
+
# XOne — Patrones de UI, voz, integración y seguridad
|
|
2
|
+
|
|
3
|
+
> Fuente: `xone/v2/xone-help-docs/topics/05-events-patterns-faq.md` §11–§13. Referencia de la skill; el índice está en [../../SKILL.md](../../SKILL.md).
|
|
4
|
+
|
|
5
|
+
Contenido: §11 patrones de UI · §11A control por voz (TTS + STT) · §12 patrones de integración · §13 patrones de seguridad
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## 11. Patrones de UI
|
|
10
|
+
|
|
11
|
+
### 11.1 Formulario con validación
|
|
12
|
+
|
|
13
|
+
```javascript
|
|
14
|
+
function validarFormulario() {
|
|
15
|
+
if (!self.MAP_NOMBRE || self.MAP_NOMBRE.length == 0) {
|
|
16
|
+
ui.showToast("El nombre es obligatorio");
|
|
17
|
+
return false;
|
|
18
|
+
}
|
|
19
|
+
if (!self.MAP_EMAIL || self.MAP_EMAIL.length == 0) {
|
|
20
|
+
ui.showToast("El email es obligatorio");
|
|
21
|
+
return false;
|
|
22
|
+
}
|
|
23
|
+
return true;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
function guardarFormulario() {
|
|
27
|
+
if (!validarFormulario()) return;
|
|
28
|
+
self.save();
|
|
29
|
+
ui.showToast("Guardado correctamente");
|
|
30
|
+
ui.getView(self).exit();
|
|
31
|
+
}
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
### 11.2 Tarjeta de información (Card)
|
|
35
|
+
|
|
36
|
+
```xml
|
|
37
|
+
<frame name="frmCard" width="96%" height="auto" lmargin="2%"
|
|
38
|
+
tmargin="10p" bgcolor="#FFFFFF" border-corner-radius="12"
|
|
39
|
+
framebox="true" forecolor="#E0E0E0" border-width="1">
|
|
40
|
+
<frame name="frmCardHeader" width="100%" height="48p" bgcolor="#F5F5F5">
|
|
41
|
+
<prop name="lblCardTitulo" type="L" visible="7"
|
|
42
|
+
width="100%" height="48p" lmargin="16p"
|
|
43
|
+
fontsize="16" fontbold="true" forecolor="#212121"
|
|
44
|
+
title="Título de la Tarjeta"/>
|
|
45
|
+
</frame>
|
|
46
|
+
<prop name="lblCardContenido" type="L" visible="7"
|
|
47
|
+
width="96%" lmargin="2%" tmargin="12p" bmargin="12p"
|
|
48
|
+
forecolor="#757575" fontsize="14" label-wrap="true"
|
|
49
|
+
title="Contenido de la tarjeta con descripción detallada"/>
|
|
50
|
+
</frame>
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
### 11.3 Dashboard con estadisticas
|
|
54
|
+
|
|
55
|
+
```xml
|
|
56
|
+
<frame name="frmDashboard" width="100%" scroll="true">
|
|
57
|
+
<!-- Fila de estadisticas -->
|
|
58
|
+
<frame name="frmStats" width="96%" lmargin="2%" tmargin="10p">
|
|
59
|
+
<frame name="frmStat1" width="31%" height="100p"
|
|
60
|
+
bgcolor="#E3F2FD" border-corner-radius="8">
|
|
61
|
+
<prop name="lblStatNum1" type="L" visible="7"
|
|
62
|
+
width="100%" align="center" tmargin="15p"
|
|
63
|
+
fontsize="24" fontbold="true" forecolor="#1565C0"
|
|
64
|
+
title="##FLD_MAP_TOTAL_PENDIENTES##"/>
|
|
65
|
+
<prop name="lblStatLabel1" type="L" visible="7"
|
|
66
|
+
width="100%" align="center"
|
|
67
|
+
fontsize="12" forecolor="#1565C0"
|
|
68
|
+
title="Pendientes"/>
|
|
69
|
+
</frame>
|
|
70
|
+
<frame name="frmStat2" width="31%" height="100p"
|
|
71
|
+
lmargin="3%" newline="false"
|
|
72
|
+
bgcolor="#E8F5E9" border-corner-radius="8">
|
|
73
|
+
<prop name="lblStatNum2" type="L" visible="7"
|
|
74
|
+
width="100%" align="center" tmargin="15p"
|
|
75
|
+
fontsize="24" fontbold="true" forecolor="#2E7D32"
|
|
76
|
+
title="##FLD_MAP_TOTAL_COMPLETADOS##"/>
|
|
77
|
+
<prop name="lblStatLabel2" type="L" visible="7"
|
|
78
|
+
width="100%" align="center"
|
|
79
|
+
fontsize="12" forecolor="#2E7D32"
|
|
80
|
+
title="Completados"/>
|
|
81
|
+
</frame>
|
|
82
|
+
</frame>
|
|
83
|
+
</frame>
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
### 11.4 Item de lista con icono + textos
|
|
87
|
+
|
|
88
|
+
```xml
|
|
89
|
+
<!-- Dentro de la coleccion del content (visible="4" para modo lista) -->
|
|
90
|
+
<group name="General" id="1">
|
|
91
|
+
<prop name="IMAGEN" type="IMG" width="115p" height="118p"
|
|
92
|
+
visible="4" tmargin="2p" lmargin="0"/>
|
|
93
|
+
<frame name="frm1" newline="false" width="600p" lmargin="5p" height="120p">
|
|
94
|
+
<prop name="MAP_NOMBRE_GRID" type="T" class="classgrid"/>
|
|
95
|
+
<prop name="MAP_DIRECCION_GRID" class="classgrid" type="T"
|
|
96
|
+
text-forecolor="#666666" textfont-size="5"
|
|
97
|
+
lines="2" fixed-lines="true"/>
|
|
98
|
+
</frame>
|
|
99
|
+
</group>
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
### 11.5 Botón flotante (FAB)
|
|
103
|
+
|
|
104
|
+
```xml
|
|
105
|
+
<frame name="floatadd1" top="920p" left="510p"
|
|
106
|
+
width="290p" height="90p" floating="true">
|
|
107
|
+
<prop name="BTADD1" type="B" visible="1" labelwidth="0"
|
|
108
|
+
method="ExecuteNode(nuevo)" width="75p"
|
|
109
|
+
img="add.png" imgsel="add_click.png"/>
|
|
110
|
+
</frame>
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
### 11.6 Modal/Overlay
|
|
114
|
+
|
|
115
|
+
```xml
|
|
116
|
+
<frame name="frmnuevochat"
|
|
117
|
+
animation-in-delay="250" animation-out-delay="250"
|
|
118
|
+
animation-in="##RIGHT_IN##" animation-out="##LEFT_OUT##"
|
|
119
|
+
disablevisible="MAP_VERFLOTANTE=0"
|
|
120
|
+
bgcolor="#ffffff" modal="true" floating="true"
|
|
121
|
+
top="0" left="0" width="100%" height="100%">
|
|
122
|
+
<!-- Contenido del modal -->
|
|
123
|
+
<prop name="MAP_BUSCAR_USUARIO"
|
|
124
|
+
ontextchanged="javascript:AccionesChatEspecial('textoU', e);"
|
|
125
|
+
type="T" visible="1"/>
|
|
126
|
+
<prop name="@nUsuarios" contents="nUsuarios" viewmode="recyclerview"
|
|
127
|
+
type="Z" visible="1" width="100%" height="1061p"/>
|
|
128
|
+
<contents name="nUsuarios" src="UsuariosChat"/>
|
|
129
|
+
</frame>
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
### 11.7 Barra de busqueda
|
|
133
|
+
|
|
134
|
+
```xml
|
|
135
|
+
<frame name="frmBuscador" width="98%" lmargin="1%" height="150p"
|
|
136
|
+
tmargin="1%" framebox="true">
|
|
137
|
+
<prop name="MAP_BUSCAR_TEXT"
|
|
138
|
+
ontextchanged="javascript:FiltraMarcados(e);"
|
|
139
|
+
labelwidth="0" text-border="true" type="T"
|
|
140
|
+
lpadding="10p" rpadding="10p"
|
|
141
|
+
width="98%" tmargin="10p" lmargin="1%" height="60p"
|
|
142
|
+
tooltip="Texto a buscar"/>
|
|
143
|
+
</frame>
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
### 11.8 Badge de notificación
|
|
147
|
+
|
|
148
|
+
```xml
|
|
149
|
+
<!-- Usar un L con fondo circular como badge -->
|
|
150
|
+
<frame name="frmBadge" floating="true" top="10p" left="80p"
|
|
151
|
+
width="24p" height="24p" disablevisible="MAP_NOTIF_COUNT=0">
|
|
152
|
+
<prop name="lblBadge" type="L" visible="7"
|
|
153
|
+
width="24p" height="24p" align="center"
|
|
154
|
+
bgcolor="#FF0000" forecolor="#FFFFFF" fontsize="10"
|
|
155
|
+
border-corner-radius="12"
|
|
156
|
+
title="##FLD_MAP_NOTIF_COUNT##"/>
|
|
157
|
+
</frame>
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
### 11.9 Slider de imágenes
|
|
161
|
+
|
|
162
|
+
```xml
|
|
163
|
+
<prop name="ContentsDatosSlide" viewmode="slideview"
|
|
164
|
+
autoslide-delay="5" type="Z"
|
|
165
|
+
width="100%" lmargin="0" height="1160p"
|
|
166
|
+
contents="ContentsDatosSlide"
|
|
167
|
+
onchange="refresh255" forceonchange="true"/>
|
|
168
|
+
<contents name="ContentsDatosSlide" src="ContentsDatosSlide"/>
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
### 11.10 Vista de chat (burbujas)
|
|
172
|
+
|
|
173
|
+
**Ejemplo real** (del proyecto EspecialChat):
|
|
174
|
+
|
|
175
|
+
```xml
|
|
176
|
+
<frame name="frmChatear" tmargin="0" width="100%" height="937p"
|
|
177
|
+
imgbk="##FLD_MAP_FOTO_FONDO##">
|
|
178
|
+
<frame name="frmContent" tmargin="0" width="100%" height="937p">
|
|
179
|
+
<prop name="Chatear" bgcolor="#00000000" contents="Chatear"
|
|
180
|
+
edit-inrow="true" type="Z" width="100%" height="100%"/>
|
|
181
|
+
<contents name="Chatear" src="Chatear"
|
|
182
|
+
filter="IDCHAT=##FLD_MAP_CHATSEL##"/>
|
|
183
|
+
</frame>
|
|
184
|
+
</frame>
|
|
185
|
+
|
|
186
|
+
<!-- Barra inferior con input y botones -->
|
|
187
|
+
<frame name="frmMenuW" bgcolor="#ffffff" align="center"
|
|
188
|
+
width="100%" height="100p">
|
|
189
|
+
<prop name="MAP_ADDOTHER" img="icon_more.png" type="B"
|
|
190
|
+
width="100p" height="100p"/>
|
|
191
|
+
<prop name="MAP_TITLE"
|
|
192
|
+
onfocuschanged="javascript:AccionesChatEspecial('foco', e);"
|
|
193
|
+
ontextchanged="javascript:AccionesChatEspecial('textoChange', e);"
|
|
194
|
+
type="T" visible="1" newline="false"
|
|
195
|
+
width="424p" height="80p" tmargin="10p"/>
|
|
196
|
+
<prop name="MAP_ADDTEXT"
|
|
197
|
+
method="executenode(AccionesChatEspecial('enviar'))"
|
|
198
|
+
img="icon_send.png" type="B" width="100p" height="100p"
|
|
199
|
+
newline="false" disablevisible="MAP_SHOWADDTEXT=0"/>
|
|
200
|
+
</frame>
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
---
|
|
204
|
+
|
|
205
|
+
## 11A. Patron Control por Voz (TTS + STT)
|
|
206
|
+
|
|
207
|
+
XOne soporta síntesis de voz (Text-to-Speech) y reconocimiento de voz (Speech-to-Text) nativamente a traves del objeto global `ui`, combinando dos métodos:
|
|
208
|
+
|
|
209
|
+
- `ui.speak({...})` — el dispositivo "habla" un texto.
|
|
210
|
+
- `ui.recognizeSpeech({...})` — el dispositivo "escucha" y te entrega el texto reconocido.
|
|
211
|
+
|
|
212
|
+
El patron más potente es **encadenar ambos**: hablar primero (pregunta al usuario) y, cuando termine la síntesis, arrancar la escucha (respuesta del usuario). Así se evita que el reconocedor capte la propia voz sintetizada.
|
|
213
|
+
|
|
214
|
+
### 11A.1 Anatomia de los parámetros
|
|
215
|
+
|
|
216
|
+
**`ui.speak(params)`**
|
|
217
|
+
|
|
218
|
+
| Parámetro | Descripción |
|
|
219
|
+
| --- | --- |
|
|
220
|
+
| `language` | Idioma: `"es"`, `"en"`, ... |
|
|
221
|
+
| `text` | Texto que se va a pronunciar. |
|
|
222
|
+
| `speechRate` | Ritmo de habla en milisegundos. |
|
|
223
|
+
| `onCompleted` | Callback `function()` al terminar de hablar. |
|
|
224
|
+
|
|
225
|
+
**`ui.recognizeSpeech(params)`**
|
|
226
|
+
|
|
227
|
+
| Parámetro | Descripción |
|
|
228
|
+
| --- | --- |
|
|
229
|
+
| `language` | Idioma del reconocedor. |
|
|
230
|
+
| `timeoutAfterSilence` | Milisegundos de silencio antes de cerrar la escucha. |
|
|
231
|
+
| `characterLimit` | *(Opcional)* Número máximo de caracteres a reconocer. |
|
|
232
|
+
| `onRecognize` | `function(sText)` con el texto reconocido. |
|
|
233
|
+
| `onError` | `function(nErrorCode, sError)` si hubo error. |
|
|
234
|
+
| `onPartialResults` | *(Opcional)* `function(extras)` con resultados parciales. |
|
|
235
|
+
| `onEndOfSpeech` | *(Opcional)* `function()` al terminar la locucion del usuario. |
|
|
236
|
+
|
|
237
|
+
### 11A.2 Patron "preguntar y escuchar" (flujo completo)
|
|
238
|
+
|
|
239
|
+
```xml
|
|
240
|
+
<!-- Botón que lanza la interacción por voz -->
|
|
241
|
+
<prop name="MAP_BT_VOZ" type="B" visible="1" title="Preguntar por voz"
|
|
242
|
+
onclick="doSpeakYRecoger('es', '¿Que opción quieres?', self, null);" />
|
|
243
|
+
|
|
244
|
+
<!-- Icono del micrófono (cambia según el estado) -->
|
|
245
|
+
<prop name="MAP_IMGLISTENING" type="IMG" visible="1" width="64p" height="64p"/>
|
|
246
|
+
```
|
|
247
|
+
|
|
248
|
+
```javascript
|
|
249
|
+
// Funcion 1: habla la pregunta y encadena la escucha cuando termina el TTS
|
|
250
|
+
function doSpeakYRecoger(sLanguage, strText, objSource, objAR) {
|
|
251
|
+
ui.speak({
|
|
252
|
+
language : sLanguage,
|
|
253
|
+
text : strText,
|
|
254
|
+
speechRate : 120,
|
|
255
|
+
onCompleted: function() {
|
|
256
|
+
// Cuando el dispositivo ha terminado de hablar, arrancamos el reconocedor.
|
|
257
|
+
// Así el microfono no capta la propia voz sintetizada.
|
|
258
|
+
objSource.MAP_IMGLISTENING = "microRojo.png"; // indicar "escuchando"
|
|
259
|
+
ui.refresh("MAP_IMGLISTENING");
|
|
260
|
+
doRecognize(sLanguage, objSource, objAR);
|
|
261
|
+
}
|
|
262
|
+
});
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
// Funcion 2: escucha, procesa, actualiza UI
|
|
266
|
+
function doRecognize(sLanguage, objSource, objAR) {
|
|
267
|
+
ui.recognizeSpeech({
|
|
268
|
+
language: sLanguage,
|
|
269
|
+
timeoutAfterSilence: 10000,
|
|
270
|
+
|
|
271
|
+
onRecognize: function(sText) {
|
|
272
|
+
sText = (sText || "").toUpperCase();
|
|
273
|
+
|
|
274
|
+
// Caso A: comparar contra opciones predefinidas
|
|
275
|
+
if (objSource.MAP_INITAR == 1 && objAR != null) {
|
|
276
|
+
let idx = 100;
|
|
277
|
+
if (objAR.MAP_TITLE0.toUpperCase() == sText) idx = 0;
|
|
278
|
+
else if (objAR.MAP_TITLE1.toUpperCase() == sText) idx = 1;
|
|
279
|
+
else if (objAR.MAP_TITLE2.toUpperCase() == sText) idx = 2;
|
|
280
|
+
// ...aquí harias algo con idx...
|
|
281
|
+
} else {
|
|
282
|
+
// Caso B: volcar el texto reconocido en una propiedad
|
|
283
|
+
objSource.MAP_TEXT = sText;
|
|
284
|
+
ui.refreshValue("MAP_TEXT");
|
|
285
|
+
}
|
|
286
|
+
},
|
|
287
|
+
|
|
288
|
+
onError: function(nErrorCode, sError) {
|
|
289
|
+
objSource.MAP_IMGLISTENING = "microGris.png"; // restaurar icono
|
|
290
|
+
ui.refresh("MAP_IMGLISTENING");
|
|
291
|
+
// ui.msgBox("Error " + nErrorCode + ": " + sError, "Voz", 0);
|
|
292
|
+
},
|
|
293
|
+
|
|
294
|
+
onEndOfSpeech: function() {
|
|
295
|
+
// Restaurar icono al terminar la locucion (aunque el reconocedor
|
|
296
|
+
// aún este procesando). onRecognize se llama después.
|
|
297
|
+
objSource.MAP_IMGLISTENING = "microGris.png";
|
|
298
|
+
ui.refresh("MAP_IMGLISTENING");
|
|
299
|
+
}
|
|
300
|
+
});
|
|
301
|
+
}
|
|
302
|
+
```
|
|
303
|
+
|
|
304
|
+
### 11A.3 Solo escuchar (sin TTS previo)
|
|
305
|
+
|
|
306
|
+
Si solo quieres dictado, se puede usar `recognizeSpeech` directamente, sin `speak`:
|
|
307
|
+
|
|
308
|
+
```javascript
|
|
309
|
+
function dictarNota() {
|
|
310
|
+
ui.recognizeSpeech({
|
|
311
|
+
language: "es",
|
|
312
|
+
timeoutAfterSilence: 10000,
|
|
313
|
+
onRecognize: function(sText) {
|
|
314
|
+
self.MAP_NOTA = sText;
|
|
315
|
+
ui.refreshValue("MAP_NOTA");
|
|
316
|
+
},
|
|
317
|
+
onError: function(nErrorCode, sError) {
|
|
318
|
+
ui.showToast("Error de voz: " + sError);
|
|
319
|
+
}
|
|
320
|
+
});
|
|
321
|
+
}
|
|
322
|
+
```
|
|
323
|
+
|
|
324
|
+
### 11A.4 Buenas prácticas
|
|
325
|
+
|
|
326
|
+
- **Siempre** lanzar `recognizeSpeech` desde `onCompleted` de `speak`, nunca antes — evita que el reconocedor oiga la síntesis.
|
|
327
|
+
- Gestionar el icono del microfono con una propiedad tipo `MAP_IMGLISTENING` y refrescarla en cada transición de estado (hablando / escuchando / inactivo).
|
|
328
|
+
- Para comparar lo dictado con opciones predefinidas, normalizar siempre con `.toUpperCase()` (o `.toLowerCase()`) antes de comparar.
|
|
329
|
+
- `timeoutAfterSilence` en ms — valores típicos 5000-10000. Más bajo corta frases largas; más alto introduce latencia.
|
|
330
|
+
- El `onError` **siempre** debe restaurar el estado visual del microfono.
|
|
331
|
+
|
|
332
|
+
### 11A.5 Referencias
|
|
333
|
+
|
|
334
|
+
- Doc del wiki: `2.-desarrollo-app/2.5.-controles-by-xone/control_por_voz/start.md`
|
|
335
|
+
- Referencia de los métodos (objetos complementarios): `ui.speak` y `ui.recognizeSpeech` en la documentación del objeto global `ui`.
|
|
336
|
+
|
|
337
|
+
---
|
|
338
|
+
|
|
339
|
+
## 12. Patrones de Integración
|
|
340
|
+
|
|
341
|
+
### 12.1 Conexión con API REST
|
|
342
|
+
|
|
343
|
+
```javascript
|
|
344
|
+
function consultarAPI(endpoint) {
|
|
345
|
+
var miObjeto = self; // Guardar contexto
|
|
346
|
+
ui.showWaitDialog("Consultando...");
|
|
347
|
+
|
|
348
|
+
var request = {
|
|
349
|
+
parameters: {
|
|
350
|
+
connectTimeout: 120000,
|
|
351
|
+
readTimeout: 120000
|
|
352
|
+
},
|
|
353
|
+
headers: {
|
|
354
|
+
"Authorization": "Bearer " + appData.getGlobalMacro("##TOKEN##"),
|
|
355
|
+
"Accept": "application/json"
|
|
356
|
+
}
|
|
357
|
+
};
|
|
358
|
+
|
|
359
|
+
$http.get("https://api.ejemplo.com/" + endpoint, request,
|
|
360
|
+
function(sData, headers, nHttpStatusCode) {
|
|
361
|
+
var json = JSON.parse(sData);
|
|
362
|
+
miObjeto.MAP_RESULTADO = json.resultado;
|
|
363
|
+
ui.refresh("MAP_RESULTADO");
|
|
364
|
+
ui.hideWaitDialog();
|
|
365
|
+
},
|
|
366
|
+
function(nError, sErrorDesc) {
|
|
367
|
+
ui.showToast("Error " + nError + ": " + sErrorDesc);
|
|
368
|
+
ui.hideWaitDialog();
|
|
369
|
+
}
|
|
370
|
+
);
|
|
371
|
+
}
|
|
372
|
+
```
|
|
373
|
+
|
|
374
|
+
> **Referencia cruzada:** Para la API HTTP completa (`$http`), consultar el tópico 03 sobre la API JavaScript.
|
|
375
|
+
|
|
376
|
+
### 12.2 GPS y seguimiento en tiempo real
|
|
377
|
+
|
|
378
|
+
**Iniciar GPS (ejemplo real de EspecialMapa):**
|
|
379
|
+
|
|
380
|
+
```javascript
|
|
381
|
+
var ok = ui.msgBox("Desea acceder al Geoposicionamiento?", "Aviso", 4);
|
|
382
|
+
if (ok == 6) {
|
|
383
|
+
appData.getCurrentEnterprise().setVariable("MIUBICACION", 1);
|
|
384
|
+
}
|
|
385
|
+
if (appData.getCurrentEnterprise().getVariable("MIUBICACION") == 1) {
|
|
386
|
+
ui.startGps();
|
|
387
|
+
PosicionamientoGPS();
|
|
388
|
+
}
|
|
389
|
+
```
|
|
390
|
+
|
|
391
|
+
**GPS con callback:**
|
|
392
|
+
|
|
393
|
+
```javascript
|
|
394
|
+
ui.startGps({
|
|
395
|
+
nodeName: "callbackGPS",
|
|
396
|
+
timeBetweenUpdates: 10000,
|
|
397
|
+
minimumMetersDistanceRange: 10,
|
|
398
|
+
foreground: true,
|
|
399
|
+
title: "Mi App GPS",
|
|
400
|
+
text: "Rastreando ubicación..."
|
|
401
|
+
});
|
|
402
|
+
```
|
|
403
|
+
|
|
404
|
+
### 12.3 Camara: captura de fotos
|
|
405
|
+
|
|
406
|
+
```xml
|
|
407
|
+
<prop name="MAP_FOTO" type="PH" img-width="48p" img-height="48p"
|
|
408
|
+
height="40%" title="Foto" lmargin="2%"/>
|
|
409
|
+
```
|
|
410
|
+
|
|
411
|
+
La captura se lanza automáticamente al tocar el control `PH`. Para foto de solo lectura:
|
|
412
|
+
|
|
413
|
+
```xml
|
|
414
|
+
<prop name="MAP_FOTOVER" type="PH" locked="true"
|
|
415
|
+
height="40%" title="foto" lmargin="2%"/>
|
|
416
|
+
```
|
|
417
|
+
|
|
418
|
+
### 12.4 Escaneo QR/Barcode
|
|
419
|
+
|
|
420
|
+
```javascript
|
|
421
|
+
ui.scanBarCode({
|
|
422
|
+
onScanned: function(sCode) {
|
|
423
|
+
self.MAP_CODIGO_QR = sCode;
|
|
424
|
+
ui.refresh("MAP_CODIGO_QR");
|
|
425
|
+
ui.showToast("Código: " + sCode);
|
|
426
|
+
},
|
|
427
|
+
onCancelled: function() {
|
|
428
|
+
ui.showToast("Escaneo cancelado");
|
|
429
|
+
}
|
|
430
|
+
});
|
|
431
|
+
```
|
|
432
|
+
|
|
433
|
+
### 12.5 Firma digital
|
|
434
|
+
|
|
435
|
+
```xml
|
|
436
|
+
<prop name="MAP_SIGNATURE" img-width="48p" img-height="48p"
|
|
437
|
+
type="DR" readonly="false" height="40%" title="Firma"
|
|
438
|
+
stroke-width="##FLD_MAP_TAMANO_TRAZO##"
|
|
439
|
+
stroke-color="##FLD_MAP_COLOR_TRAZO##"
|
|
440
|
+
bgcolor="##FLD_MAP_COLOR_FONDO##"
|
|
441
|
+
apply-format-to-file="true"
|
|
442
|
+
onchange="refresh(MAP_SIGNATURE)"/>
|
|
443
|
+
```
|
|
444
|
+
|
|
445
|
+
### 12.6 Calendario con eventos
|
|
446
|
+
|
|
447
|
+
**Ejemplo real** (del proyecto EspecialCalendario):
|
|
448
|
+
|
|
449
|
+
```xml
|
|
450
|
+
<frame name="calendario" width="100%" height="350p">
|
|
451
|
+
<prop name="Calendario" type="Z" calendar-viewmode="week"
|
|
452
|
+
contents="calendario" width="100%" height="100%"
|
|
453
|
+
viewmode="calendarview"
|
|
454
|
+
onchange="refresh" postonchange="refresh"/>
|
|
455
|
+
<contents name="calendario" src="ContentdatosCalendario"/>
|
|
456
|
+
</frame>
|
|
457
|
+
|
|
458
|
+
<!-- Lista de eventos del mes -->
|
|
459
|
+
<frame name="calendario2" width="100%" height="360p">
|
|
460
|
+
<prop name="Calendariodatos" type="Z" contents="Calendariodatos"
|
|
461
|
+
width="100%" height="300p"/>
|
|
462
|
+
<contents name="Calendariodatos" src="ContentdatosCalendariolista"
|
|
463
|
+
filter="strftime('%m',##FLD_MAP_FECHA##)=strftime('%m',FECHA)
|
|
464
|
+
and strftime('%Y',##FLD_MAP_FECHA##)=strftime('%Y',FECHA)"/>
|
|
465
|
+
</frame>
|
|
466
|
+
```
|
|
467
|
+
|
|
468
|
+
### 12.7 Gráficos (pie, bar, line)
|
|
469
|
+
|
|
470
|
+
**Ejemplo real** (del proyecto EspecialGraficos):
|
|
471
|
+
|
|
472
|
+
```xml
|
|
473
|
+
<!-- Gráfico de barras -->
|
|
474
|
+
<prop name="@GraficosBarrasDatos" classid="XOneCharts"
|
|
475
|
+
viewmode="barchart" type="Z" contents="GraficosBarrasDatos"
|
|
476
|
+
width="692p" height="500p"/>
|
|
477
|
+
<contents name="GraficosBarrasDatos" src="ContentGraficosBarrasDatos"/>
|
|
478
|
+
|
|
479
|
+
<!-- Gráfico circular -->
|
|
480
|
+
<prop name="@GraficosPastelDatos" classid="XOneCharts"
|
|
481
|
+
viewmode="piechart" type="Z" contents="GraficosPastelDatos"
|
|
482
|
+
width="692p" height="500p"/>
|
|
483
|
+
|
|
484
|
+
<!-- Gráfico de línea de tiempo -->
|
|
485
|
+
<prop name="@GraficosLineasTiempoDatos" classid="XOneCharts"
|
|
486
|
+
viewmode="timeserieschart" type="Z"
|
|
487
|
+
contents="GraficosLineasTiempoDatos"
|
|
488
|
+
width="692p" height="500p"/>
|
|
489
|
+
|
|
490
|
+
<!-- Gráfico de lineas -->
|
|
491
|
+
<prop name="@GraficosLineasDatos" classid="XOneCharts"
|
|
492
|
+
viewmode="linechart" type="Z" contents="GraficosLineasDatos"
|
|
493
|
+
width="692p" height="500p"/>
|
|
494
|
+
<contents name="GraficosLineasDatos" src="ContentGraficosLineasDatos"
|
|
495
|
+
sort="CATEGORIA,VALOR1,VALOR2,VALOR3"/>
|
|
496
|
+
|
|
497
|
+
<!-- Gráfico XY -->
|
|
498
|
+
<prop name="@GraficosLineasXYDatos" classid="XOneCharts"
|
|
499
|
+
viewmode="xylinechart" type="Z" contents="GraficosLineasXYDatos"
|
|
500
|
+
width="692p" height="500p"/>
|
|
501
|
+
```
|
|
502
|
+
|
|
503
|
+
**ViewModes de gráficos disponibles:**
|
|
504
|
+
|
|
505
|
+
| viewmode | Descripción |
|
|
506
|
+
|----------|-------------|
|
|
507
|
+
| `barchart` | Gráfico de barras |
|
|
508
|
+
| `3dbarchart` | Gráfico de barras 3D |
|
|
509
|
+
| `piechart` | Gráfico circular tipo 1 |
|
|
510
|
+
| `piechart2` | Gráfico circular tipo 2 |
|
|
511
|
+
| `linechart` | Gráfico de lineas |
|
|
512
|
+
| `timeserieschart` | Gráfico de series temporales |
|
|
513
|
+
| `xylinechart` | Gráfico de lineas XY |
|
|
514
|
+
|
|
515
|
+
### 12.8 Mapas con marcadores
|
|
516
|
+
|
|
517
|
+
**Ejemplo real** (del proyecto EspecialMapa):
|
|
518
|
+
|
|
519
|
+
```xml
|
|
520
|
+
<prop name="MAP_mapa" width="100%" title="Mapa" height="80%"
|
|
521
|
+
type="Z" visible="1" viewmode="mapview"
|
|
522
|
+
mapview-embedded="true" contents="mapaDatos"/>
|
|
523
|
+
<contents name="mapaDatos" src="ContentmapaDatos"/>
|
|
524
|
+
|
|
525
|
+
<prop name="boton0it" type="B" title="showStreetView"
|
|
526
|
+
onclick="javascript:showStreetView('MAP_mapa');"/>
|
|
527
|
+
<prop name="boton1it" type="B" title="showMap"
|
|
528
|
+
onclick="javascript:showMap('MAP_mapa');" newline="false"/>
|
|
529
|
+
```
|
|
530
|
+
|
|
531
|
+
### 12.9 Notificaciones push
|
|
532
|
+
|
|
533
|
+
**Crear notificación local (ejemplo real de EspecialNotificaciones):**
|
|
534
|
+
|
|
535
|
+
```javascript
|
|
536
|
+
ui.showNotification(1, "Titulo", "Esto es una notificación",
|
|
537
|
+
"Aviso de recepcion de datos");
|
|
538
|
+
|
|
539
|
+
// Eliminar notificación
|
|
540
|
+
ui.dismissNotification("1");
|
|
541
|
+
```
|
|
542
|
+
|
|
543
|
+
**Notificación avanzada con botones:**
|
|
544
|
+
|
|
545
|
+
```javascript
|
|
546
|
+
ui.showNotification({
|
|
547
|
+
id: 5000,
|
|
548
|
+
title: "Nueva tarea asignada",
|
|
549
|
+
text: "Tiene una nueva tarea pendiente",
|
|
550
|
+
icon: "app_icon1",
|
|
551
|
+
backgroundColor: "#1976D2",
|
|
552
|
+
sound: "notification.wav",
|
|
553
|
+
cancelable: true,
|
|
554
|
+
dataObject: self,
|
|
555
|
+
nodeName: "callbackNotificacion",
|
|
556
|
+
parameters: '{ "tareaId": "123" }',
|
|
557
|
+
buttons: [{
|
|
558
|
+
id: 5001,
|
|
559
|
+
title: "Responder",
|
|
560
|
+
directReply: true,
|
|
561
|
+
directReplyLabel: "Escriba su respuesta...",
|
|
562
|
+
dataObject: self,
|
|
563
|
+
nodeName: "respuestaCallback"
|
|
564
|
+
}]
|
|
565
|
+
});
|
|
566
|
+
```
|
|
567
|
+
|
|
568
|
+
### 12.10 Sincronización con servidor
|
|
569
|
+
|
|
570
|
+
La sincronización se configura en el nodo `maintenance` de `Empresas`:
|
|
571
|
+
|
|
572
|
+
```xml
|
|
573
|
+
<maintenance>
|
|
574
|
+
<action name="Replica" type="replica"
|
|
575
|
+
frecuency="400" period="X" synchronize="true"/>
|
|
576
|
+
<action name="SincronizarDatos" type="runscript"
|
|
577
|
+
period="S" frecuency="600" auto="true" show="false">
|
|
578
|
+
<script language="javascript">
|
|
579
|
+
sincronizarConServidor();
|
|
580
|
+
</script>
|
|
581
|
+
</action>
|
|
582
|
+
</maintenance>
|
|
583
|
+
```
|
|
584
|
+
|
|
585
|
+
---
|
|
586
|
+
|
|
587
|
+
## 13. Patrones de Seguridad
|
|
588
|
+
|
|
589
|
+
### 13.1 Login seguro
|
|
590
|
+
|
|
591
|
+
```javascript
|
|
592
|
+
function realizarLogin() {
|
|
593
|
+
if (!validarRequerido(self.MAP_EMAIL, "Email")) return;
|
|
594
|
+
if (!validarRequerido(self.MAP_PASSWORD, "Contraseña")) return;
|
|
595
|
+
|
|
596
|
+
ui.showWaitDialog("Iniciando sesion...");
|
|
597
|
+
|
|
598
|
+
var collUsuarios = appData.getCollection("Usuarios");
|
|
599
|
+
var usuario = collUsuarios.findObject(
|
|
600
|
+
"LOGIN = '" + self.MAP_EMAIL + "'"
|
|
601
|
+
);
|
|
602
|
+
|
|
603
|
+
ui.hideWaitDialog();
|
|
604
|
+
|
|
605
|
+
if (usuario) {
|
|
606
|
+
// Verificar contraseña (idealmente hasheada)
|
|
607
|
+
if (usuario.PWD == self.MAP_PASSWORD) {
|
|
608
|
+
appData.setGlobalMacro("##USERID##", usuario.ID);
|
|
609
|
+
appData.setGlobalMacro("##USERNAME##", usuario.NOMBRE);
|
|
610
|
+
ui.showToast("Bienvenido, " + usuario.NOMBRE);
|
|
611
|
+
ui.openEditView("MenuPrincipal");
|
|
612
|
+
} else {
|
|
613
|
+
ui.showToast("Credenciales incorrectas");
|
|
614
|
+
}
|
|
615
|
+
} else {
|
|
616
|
+
ui.showToast("Usuario no encontrado");
|
|
617
|
+
}
|
|
618
|
+
}
|
|
619
|
+
```
|
|
620
|
+
|
|
621
|
+
### 13.2 Validación de entrada
|
|
622
|
+
|
|
623
|
+
```javascript
|
|
624
|
+
function validarRequerido(valor, nombre) {
|
|
625
|
+
if (!valor || valor.toString().length == 0) {
|
|
626
|
+
ui.showToast("El campo " + nombre + " es obligatorio");
|
|
627
|
+
return false;
|
|
628
|
+
}
|
|
629
|
+
return true;
|
|
630
|
+
}
|
|
631
|
+
|
|
632
|
+
function validarEmail(email) {
|
|
633
|
+
if (!email) return false;
|
|
634
|
+
return email.indexOf("@") > 0 && email.indexOf(".") > 0;
|
|
635
|
+
}
|
|
636
|
+
|
|
637
|
+
function validarNumeroPositivo(valor, nombre) {
|
|
638
|
+
if (isNaN(valor) || valor <= 0) {
|
|
639
|
+
ui.showToast(nombre + " debe ser un número positivo");
|
|
640
|
+
return false;
|
|
641
|
+
}
|
|
642
|
+
return true;
|
|
643
|
+
}
|
|
644
|
+
```
|
|
645
|
+
|
|
646
|
+
### 13.3 SQL parameterizado
|
|
647
|
+
|
|
648
|
+
Para evitar inyeccion SQL al construir filtros:
|
|
649
|
+
|
|
650
|
+
```javascript
|
|
651
|
+
// INCORRECTO - vulnerable a inyeccion SQL
|
|
652
|
+
coll.setFilter("NOMBRE = '" + self.MAP_BUSCAR + "'");
|
|
653
|
+
|
|
654
|
+
// MEJOR - sanitizar la entrada
|
|
655
|
+
var busqueda = self.MAP_BUSCAR.toString().replace(/'/g, "''");
|
|
656
|
+
coll.setFilter("NOMBRE LIKE '%" + busqueda + "%'");
|
|
657
|
+
```
|
|
658
|
+
|
|
659
|
+
### 13.4 Encriptación de datos sensibles
|
|
660
|
+
|
|
661
|
+
```javascript
|
|
662
|
+
// Hashear con SHA-256
|
|
663
|
+
var hash = crypto.sha256("texto a hashear");
|
|
664
|
+
|
|
665
|
+
// Cifrar con AES
|
|
666
|
+
var cifrado = crypto.encrypt("texto", "clave_secreta", "AES");
|
|
667
|
+
|
|
668
|
+
// Descifrar
|
|
669
|
+
var original = crypto.decrypt(cifrado, "clave_secreta", "AES");
|
|
670
|
+
|
|
671
|
+
// Base64
|
|
672
|
+
var encoded = crypto.encodeBase64("texto");
|
|
673
|
+
var decoded = crypto.decodeBase64(encoded);
|
|
674
|
+
```
|
|
675
|
+
|
|
676
|
+
### 13.5 Timeout de sesión
|
|
677
|
+
|
|
678
|
+
```javascript
|
|
679
|
+
// Configurar timeout usando maintenance
|
|
680
|
+
// En Empresas (mappings.xne):
|
|
681
|
+
// <maintenance>
|
|
682
|
+
// <action name="VerificarSesion" type="runscript"
|
|
683
|
+
// period="S" frecuency="300" auto="true" show="false">
|
|
684
|
+
// <script language="javascript">
|
|
685
|
+
// verificarTimeoutSesion();
|
|
686
|
+
// </script>
|
|
687
|
+
// </action>
|
|
688
|
+
// </maintenance>
|
|
689
|
+
|
|
690
|
+
function verificarTimeoutSesion() {
|
|
691
|
+
var ultimaActividad = appData.getGlobalMacro("##ULTIMA_ACTIVIDAD##");
|
|
692
|
+
var ahora = new Date().getTime();
|
|
693
|
+
var diferencia = ahora - parseInt(ultimaActividad);
|
|
694
|
+
// 30 minutos = 1800000 ms
|
|
695
|
+
if (diferencia > 1800000) {
|
|
696
|
+
ui.showToast("Sesion expirada");
|
|
697
|
+
appData.logout();
|
|
698
|
+
}
|
|
699
|
+
}
|
|
700
|
+
```
|
|
701
|
+
|