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
|
@@ -0,0 +1,647 @@
|
|
|
1
|
+
# XOne JavaScript — Plantillas de código y funciones utilitarias
|
|
2
|
+
|
|
3
|
+
> Fuente: `xone/v2/xone-help-docs/topics/03e-js-patrones-buenas-practicas.md` §12–§13. Referencia de la skill; el índice está en [../../SKILL.md](../../SKILL.md).
|
|
4
|
+
|
|
5
|
+
Contenido: §12 plantillas completas: CRUD, filtrado, maestro-detalle, GPS, fotos, chat, QR, login personalizado · §13 funciones utilitarias recomendadas para functions.js
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## 12. Patrones Comunes con Ejemplos
|
|
10
|
+
|
|
11
|
+
### 12.1 CRUD Completo
|
|
12
|
+
|
|
13
|
+
```javascript
|
|
14
|
+
// === CREAR ===
|
|
15
|
+
function crearProducto() {
|
|
16
|
+
if (!validarRequerido(self.MAP_NOMBRE, "Nombre")) return;
|
|
17
|
+
if (!validarRequerido(self.MAP_PRECIO, "Precio")) return;
|
|
18
|
+
|
|
19
|
+
let coll = appData.getCollection("Productos");
|
|
20
|
+
coll.unlock();
|
|
21
|
+
try {
|
|
22
|
+
let obj = new Productos({
|
|
23
|
+
MAP_NOMBRE: cstr(self.MAP_NOMBRE),
|
|
24
|
+
MAP_PRECIO: cnum(self.MAP_PRECIO),
|
|
25
|
+
MAP_ACTIVO: 1,
|
|
26
|
+
MAP_FECHA_ALTA: new Date()
|
|
27
|
+
});
|
|
28
|
+
coll.addItem(obj);
|
|
29
|
+
obj.save();
|
|
30
|
+
ui.showToast("Producto creado");
|
|
31
|
+
cerrarPantalla();
|
|
32
|
+
} catch(ex) {
|
|
33
|
+
ui.showToast("Error: " + ex);
|
|
34
|
+
} finally {
|
|
35
|
+
coll.lock();
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// === LEER ===
|
|
40
|
+
function buscarProducto(codigo) {
|
|
41
|
+
let coll = appData.getCollection("Productos");
|
|
42
|
+
let escapado = cstr(codigo).replace(/'/g, "''");
|
|
43
|
+
return coll.findObject("MAP_CODIGO = '" + escapado + "'");
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function listarProductosActivos() {
|
|
47
|
+
let coll = appData.getCollection("Productos");
|
|
48
|
+
coll.setFilter("MAP_ACTIVO = 1");
|
|
49
|
+
coll.clear();
|
|
50
|
+
coll.loadAll();
|
|
51
|
+
coll.doSort("MAP_NOMBRE ASC");
|
|
52
|
+
|
|
53
|
+
let items = [];
|
|
54
|
+
for (let i = 0; i < coll.getCount(); i++) {
|
|
55
|
+
items.push(coll.get(i));
|
|
56
|
+
}
|
|
57
|
+
return items;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// === ACTUALIZAR ===
|
|
61
|
+
function actualizarProducto(producto) {
|
|
62
|
+
if (!producto) return;
|
|
63
|
+
producto.MAP_FECHA_MOD = new Date();
|
|
64
|
+
producto.save();
|
|
65
|
+
ui.showToast("Producto actualizado");
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// === ELIMINAR ===
|
|
69
|
+
function eliminarProducto(producto) {
|
|
70
|
+
if (!producto) return;
|
|
71
|
+
if (!confirmar("Eliminar " + producto.MAP_NOMBRE + "?", "Eliminar")) return;
|
|
72
|
+
|
|
73
|
+
let coll = appData.getCollection("Productos");
|
|
74
|
+
for (let i = 0; i < coll.getCount(); i++) {
|
|
75
|
+
if (coll.get(i).ID == producto.ID) {
|
|
76
|
+
coll.deleteItem(i);
|
|
77
|
+
break;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
ui.showToast("Producto eliminado");
|
|
81
|
+
}
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
### 12.2 Filtrado Dinámico
|
|
85
|
+
|
|
86
|
+
```javascript
|
|
87
|
+
function buscarProductos(criterio, precioMin, precioMax, activo) {
|
|
88
|
+
let filtros = [];
|
|
89
|
+
|
|
90
|
+
if (!isEmpty(criterio)) {
|
|
91
|
+
let escapado = cstr(criterio).replace(/'/g, "''");
|
|
92
|
+
filtros.push("MAP_NOMBRE LIKE '%" + escapado + "%'");
|
|
93
|
+
}
|
|
94
|
+
if (!isEmpty(precioMin)) filtros.push("MAP_PRECIO >= " + cnum(precioMin));
|
|
95
|
+
if (!isEmpty(precioMax)) filtros.push("MAP_PRECIO <= " + cnum(precioMax));
|
|
96
|
+
if (!isEmpty(activo)) filtros.push("MAP_ACTIVO = " + cnum(activo));
|
|
97
|
+
|
|
98
|
+
let coll = appData.getCollection("Productos");
|
|
99
|
+
coll.setFilter(filtros.length > 0 ? filtros.join(" AND ") : "");
|
|
100
|
+
coll.clear();
|
|
101
|
+
coll.loadAll();
|
|
102
|
+
coll.doSort("MAP_NOMBRE ASC");
|
|
103
|
+
ui.refresh("MAP_LISTA_PRODUCTOS");
|
|
104
|
+
}
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
### 12.3 Navegación Maestro-Detalle
|
|
108
|
+
|
|
109
|
+
```javascript
|
|
110
|
+
function onItemSeleccionado() {
|
|
111
|
+
let collDetalle = appData.getCollection("DetalleProducto");
|
|
112
|
+
let obj = new DetalleProducto({
|
|
113
|
+
MAP_NOMBRE: self.MAP_NOMBRE,
|
|
114
|
+
MAP_PRECIO: self.MAP_PRECIO,
|
|
115
|
+
MAP_ID_PRODUCTO: self.ID
|
|
116
|
+
});
|
|
117
|
+
collDetalle.addItem(obj);
|
|
118
|
+
|
|
119
|
+
ui.openEditView(obj);
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
function cargarDetalle() {
|
|
123
|
+
let idProducto = self.MAP_ID_PRODUCTO;
|
|
124
|
+
if (isEmpty(idProducto)) return;
|
|
125
|
+
|
|
126
|
+
let coll = appData.getCollection("Productos");
|
|
127
|
+
let producto = coll.findObject("ID = " + idProducto);
|
|
128
|
+
if (producto) {
|
|
129
|
+
let lineas = producto.getContents("@LineasProducto");
|
|
130
|
+
lineas.unlock();
|
|
131
|
+
lineas.clear();
|
|
132
|
+
lineas.loadAll();
|
|
133
|
+
lineas.lock();
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
### 12.4 Manejo de GPS en Tiempo Real
|
|
139
|
+
|
|
140
|
+
```javascript
|
|
141
|
+
var rastreoActivo = false;
|
|
142
|
+
|
|
143
|
+
function iniciarRastreo() {
|
|
144
|
+
let estadoGPS = ui.checkGpsStatus();
|
|
145
|
+
if (estadoGPS == 0 || estadoGPS == 3) {
|
|
146
|
+
ui.askUserForGpsPermission({
|
|
147
|
+
onEnabled: function() { activarGPS(); },
|
|
148
|
+
onDenied : function() { ui.showToast("Se necesita GPS"); }
|
|
149
|
+
});
|
|
150
|
+
} else {
|
|
151
|
+
activarGPS();
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
function activarGPS() {
|
|
156
|
+
ui.startGps({
|
|
157
|
+
nodeName : "onPosicionActualizada",
|
|
158
|
+
timeBetweenUpdates : 5000,
|
|
159
|
+
minimumMetersDistanceRange: 10,
|
|
160
|
+
foreground : true,
|
|
161
|
+
title : "Rastreo GPS",
|
|
162
|
+
text : "Registrando ubicación..."
|
|
163
|
+
});
|
|
164
|
+
rastreoActivo = true;
|
|
165
|
+
ui.showToast("Rastreo GPS iniciado");
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
function detenerRastreo() {
|
|
169
|
+
ui.stopGps();
|
|
170
|
+
rastreoActivo = false;
|
|
171
|
+
ui.showToast("Rastreo GPS detenido");
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
// Callback GPS (nodo "onPosicionActualizada" en XML)
|
|
175
|
+
function onPosicionRecibida() {
|
|
176
|
+
let collGps = appData.getCollection("GPSColl");
|
|
177
|
+
collGps.startBrowse();
|
|
178
|
+
try {
|
|
179
|
+
let gps = collGps.getCurrentItem();
|
|
180
|
+
if (!gps || gps.STATUS != 1) return;
|
|
181
|
+
|
|
182
|
+
self.MAP_LATITUD = gps.LATITUD;
|
|
183
|
+
self.MAP_LONGITUD = gps.LONGITUD;
|
|
184
|
+
self.MAP_VELOCIDAD = gps.VELOCIDAD;
|
|
185
|
+
ui.refresh("MAP_LATITUD,MAP_LONGITUD,MAP_VELOCIDAD");
|
|
186
|
+
|
|
187
|
+
guardarPosicion(gps.LATITUD, gps.LONGITUD, gps.PRECISION);
|
|
188
|
+
|
|
189
|
+
let mapControl = getControl("MAP_MAPA");
|
|
190
|
+
if (mapControl) {
|
|
191
|
+
mapControl.zoomTo(gps.LATITUD, gps.LONGITUD, 15);
|
|
192
|
+
}
|
|
193
|
+
} finally {
|
|
194
|
+
collGps.endBrowse();
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
function guardarPosicion(lat, lng, precision) {
|
|
199
|
+
let coll = appData.getCollection("Posiciones");
|
|
200
|
+
coll.unlock();
|
|
201
|
+
try {
|
|
202
|
+
let obj = new Posiciones({
|
|
203
|
+
MAP_LATITUD: lat,
|
|
204
|
+
MAP_LONGITUD: lng,
|
|
205
|
+
MAP_PRECISION: precision,
|
|
206
|
+
MAP_TIMESTAMP: new Date()
|
|
207
|
+
});
|
|
208
|
+
coll.addItem(obj);
|
|
209
|
+
obj.save();
|
|
210
|
+
} finally {
|
|
211
|
+
coll.lock();
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
```
|
|
215
|
+
|
|
216
|
+
### 12.5 Tomar y Mostrar Fotos
|
|
217
|
+
|
|
218
|
+
```javascript
|
|
219
|
+
// Foto con prop tipo PH (se activa automaticamente al pulsar)
|
|
220
|
+
// En <onchange> de MAP_FOTO:
|
|
221
|
+
function onFotoCapturada() {
|
|
222
|
+
let foto = self.MAP_FOTO;
|
|
223
|
+
if (isEmpty(foto)) {
|
|
224
|
+
ui.showToast("Captura cancelada");
|
|
225
|
+
return;
|
|
226
|
+
}
|
|
227
|
+
self.MAP_FECHA_FOTO = new Date();
|
|
228
|
+
self.save();
|
|
229
|
+
ui.showToast("Foto capturada");
|
|
230
|
+
ui.refresh("MAP_FOTO");
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
// Foto con camara tipo VD (control manual)
|
|
234
|
+
function tomarFotoConCamara() {
|
|
235
|
+
let control = getControl("MAP_CAMERA");
|
|
236
|
+
if (!control) return;
|
|
237
|
+
|
|
238
|
+
control.takePicture({
|
|
239
|
+
filename : "foto_" + Date.now() + ".jpg",
|
|
240
|
+
saveToGallery: false,
|
|
241
|
+
width : 640,
|
|
242
|
+
height : 480,
|
|
243
|
+
onFinished : function(sFileName) {
|
|
244
|
+
if (sFileName) {
|
|
245
|
+
self.MAP_FOTO = sFileName;
|
|
246
|
+
self.save();
|
|
247
|
+
ui.refresh("MAP_FOTO");
|
|
248
|
+
ui.showToast("Foto guardada");
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
});
|
|
252
|
+
}
|
|
253
|
+
```
|
|
254
|
+
|
|
255
|
+
### 12.6 Sistema de Chat
|
|
256
|
+
|
|
257
|
+
```javascript
|
|
258
|
+
// Inicializar chat
|
|
259
|
+
function inicializarChat() {
|
|
260
|
+
self.MAP_GRUPOSEL = 1;
|
|
261
|
+
self.MAP_VERFLOTANTE = 0;
|
|
262
|
+
self.MAP_RECORDON = 0;
|
|
263
|
+
self.MAP_USERLOGIN = appData.getGlobalMacro("##USERNAME##");
|
|
264
|
+
self.getContents("Chat").setMacro("##MACRO##", self.MAP_USERLOGIN);
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
// Crear un chat entre dos usuarios
|
|
268
|
+
function createChat(userFrom, userTo) {
|
|
269
|
+
let coll = self.getContents("Chat");
|
|
270
|
+
coll.unlock();
|
|
271
|
+
let obj = coll.findObject(
|
|
272
|
+
"(USUARIO='" + userFrom + "' AND USUARIO2='" + userTo + "') OR " +
|
|
273
|
+
"(USUARIO='" + userTo + "' AND USUARIO2='" + userFrom + "')"
|
|
274
|
+
);
|
|
275
|
+
if (obj == null) {
|
|
276
|
+
obj = coll.createObject();
|
|
277
|
+
obj.USUARIO = userFrom;
|
|
278
|
+
obj.USUARIO2 = userTo;
|
|
279
|
+
obj.FECHA = new Date();
|
|
280
|
+
obj.save();
|
|
281
|
+
}
|
|
282
|
+
let index = obj.getObjectIndex();
|
|
283
|
+
coll.lock();
|
|
284
|
+
return index;
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
// Enviar mensaje
|
|
288
|
+
function sendMessage(colMensajes, obj, titleField, isFromUser) {
|
|
289
|
+
if (obj[titleField].length == 0) return;
|
|
290
|
+
|
|
291
|
+
let msg = new MensajesReader();
|
|
292
|
+
|
|
293
|
+
if (isFromUser) {
|
|
294
|
+
msg.USUARIOTO = self.MAP_CCUSUARIO;
|
|
295
|
+
msg.USUARIOFROM = appData.getGlobalMacro("##USERNAME##");
|
|
296
|
+
} else {
|
|
297
|
+
msg.USUARIOTO = appData.getGlobalMacro("##USERNAME##");
|
|
298
|
+
msg.USUARIOFROM = self.MAP_CCUSUARIO;
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
msg.FECHA = new Date();
|
|
302
|
+
msg.MENSAJE = self[titleField];
|
|
303
|
+
msg.TIPO = self.MAP_TIPO;
|
|
304
|
+
msg.IDCHAT = self.MAP_IDCHATSEL;
|
|
305
|
+
msg.save();
|
|
306
|
+
|
|
307
|
+
self[titleField] = "";
|
|
308
|
+
ui.refresh("MensajesUsuarios," + titleField);
|
|
309
|
+
}
|
|
310
|
+
```
|
|
311
|
+
|
|
312
|
+
### 12.7 Escaneo QR/Barcode
|
|
313
|
+
|
|
314
|
+
```javascript
|
|
315
|
+
function iniciarEscaneoQR() {
|
|
316
|
+
let control = getControl("MAP_CAMERA");
|
|
317
|
+
if (!control) {
|
|
318
|
+
ui.showToast("Control de camara no encontrado");
|
|
319
|
+
return;
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
control.setOnCodeScanned(function(evento) {
|
|
323
|
+
self.MAP_CODIGO_ESCANEADO = evento.data;
|
|
324
|
+
self.MAP_TIPO_CODIGO = evento.type;
|
|
325
|
+
ui.refresh("MAP_CODIGO_ESCANEADO,MAP_TIPO_CODIGO");
|
|
326
|
+
|
|
327
|
+
let nResult = ui.msgBox(
|
|
328
|
+
"Código: " + evento.data + "\nTipo: " + evento.type,
|
|
329
|
+
"Lectura correcta?", 4
|
|
330
|
+
);
|
|
331
|
+
return (nResult == 6);
|
|
332
|
+
});
|
|
333
|
+
}
|
|
334
|
+
```
|
|
335
|
+
|
|
336
|
+
### 12.8 Descarga de Archivos del Servidor
|
|
337
|
+
|
|
338
|
+
```javascript
|
|
339
|
+
function descargarDocumento(url, nombre) {
|
|
340
|
+
ui.showWaitDialog("Descargando " + nombre + "...");
|
|
341
|
+
|
|
342
|
+
let miObjeto = self;
|
|
343
|
+
let request = {
|
|
344
|
+
headers: { "Authorization": "Bearer " + obtenerToken() },
|
|
345
|
+
parameters: { connectTimeout: 30000, readTimeout: 60000 }
|
|
346
|
+
};
|
|
347
|
+
|
|
348
|
+
$http.download(url, request,
|
|
349
|
+
function(sPath, headers, nStatus) {
|
|
350
|
+
ui.hideWaitDialog();
|
|
351
|
+
miObjeto.MAP_ARCHIVO = sPath;
|
|
352
|
+
miObjeto.save();
|
|
353
|
+
ui.refresh("MAP_ARCHIVO");
|
|
354
|
+
|
|
355
|
+
let nResult = ui.msgBox("Abrir archivo?", "Descarga completa", 4);
|
|
356
|
+
if (nResult == 6) {
|
|
357
|
+
ui.openFile(sPath);
|
|
358
|
+
}
|
|
359
|
+
},
|
|
360
|
+
function(nError, sMessage) {
|
|
361
|
+
ui.hideWaitDialog();
|
|
362
|
+
ui.showToast("Error: " + sMessage);
|
|
363
|
+
}
|
|
364
|
+
);
|
|
365
|
+
}
|
|
366
|
+
```
|
|
367
|
+
|
|
368
|
+
### 12.9 Sincronización con Servidor
|
|
369
|
+
|
|
370
|
+
```javascript
|
|
371
|
+
function sincronizarPendientes(nombreColl, endpoint) {
|
|
372
|
+
let coll = appData.getCollection(nombreColl);
|
|
373
|
+
coll.setFilter("MAP_SINCRONIZADO = 0");
|
|
374
|
+
coll.clear();
|
|
375
|
+
coll.loadAll();
|
|
376
|
+
|
|
377
|
+
let count = coll.getCount();
|
|
378
|
+
if (count == 0) {
|
|
379
|
+
ui.showToast("No hay registros pendientes");
|
|
380
|
+
return;
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
ui.showWaitDialog("Sincronizando " + count + " registros...");
|
|
384
|
+
|
|
385
|
+
let pendientes = [];
|
|
386
|
+
for (let i = 0; i < count; i++) {
|
|
387
|
+
pendientes.push(coll.get(i).toJson());
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
let miColl = coll;
|
|
391
|
+
let request = {
|
|
392
|
+
headers: {
|
|
393
|
+
"Content-Type": "application/json",
|
|
394
|
+
"Authorization": "Bearer " + obtenerToken()
|
|
395
|
+
},
|
|
396
|
+
parameters: { connectTimeout: 30000, readTimeout: 120000 },
|
|
397
|
+
data: { registros: pendientes }
|
|
398
|
+
};
|
|
399
|
+
|
|
400
|
+
$http.post(endpoint, request,
|
|
401
|
+
function(sData) {
|
|
402
|
+
for (let i = 0; i < miColl.getCount(); i++) {
|
|
403
|
+
let obj = miColl.get(i);
|
|
404
|
+
obj.MAP_SINCRONIZADO = 1;
|
|
405
|
+
obj.MAP_FECHA_SYNC = new Date();
|
|
406
|
+
obj.save();
|
|
407
|
+
}
|
|
408
|
+
ui.hideWaitDialog();
|
|
409
|
+
ui.showToast(count + " registros sincronizados");
|
|
410
|
+
},
|
|
411
|
+
function(nError, sDesc) {
|
|
412
|
+
ui.hideWaitDialog();
|
|
413
|
+
ui.showToast("Error sync: " + sDesc);
|
|
414
|
+
}
|
|
415
|
+
);
|
|
416
|
+
}
|
|
417
|
+
```
|
|
418
|
+
|
|
419
|
+
### 12.10 Login Personalizado
|
|
420
|
+
|
|
421
|
+
```javascript
|
|
422
|
+
function doLogin() {
|
|
423
|
+
let usuario = cstr(self.MAP_LOGIN).trim();
|
|
424
|
+
let password = cstr(self.MAP_PASSWORD).trim();
|
|
425
|
+
|
|
426
|
+
// Solo validamos el usuario: en XOne puede haber cuentas sin contraseña
|
|
427
|
+
// (invitado, kiosco) y, si la contraseña es incorrecta o falta cuando hace
|
|
428
|
+
// falta, el backend la rechaza vía onLoginFailed (no la validamos en cliente).
|
|
429
|
+
if (isEmpty(usuario)) { ui.showToast("Introduzca el usuario"); return; }
|
|
430
|
+
|
|
431
|
+
appData.login({
|
|
432
|
+
userName : usuario,
|
|
433
|
+
password : password,
|
|
434
|
+
entryPoint : "MenuPrincipal",
|
|
435
|
+
onLoginSuccessful : function() {
|
|
436
|
+
appData.setGlobalMacro("##USERNAME##", usuario);
|
|
437
|
+
ui.showToast("Bienvenido, " + usuario);
|
|
438
|
+
},
|
|
439
|
+
onLoginFailed : function() {
|
|
440
|
+
ui.showToast("Usuario o contraseña incorrectos");
|
|
441
|
+
self.MAP_PASSWORD = "";
|
|
442
|
+
ui.refresh("MAP_PASSWORD");
|
|
443
|
+
}
|
|
444
|
+
});
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
function doLogout() {
|
|
448
|
+
if (confirmar("Cerrar sesion?", "Confirmar")) {
|
|
449
|
+
appData.setGlobalMacro("##USERNAME##", "");
|
|
450
|
+
appData.setGlobalMacro("##SESSION_TOKEN##", "");
|
|
451
|
+
appData.logout();
|
|
452
|
+
}
|
|
453
|
+
}
|
|
454
|
+
```
|
|
455
|
+
|
|
456
|
+
---
|
|
457
|
+
|
|
458
|
+
## 13. Funciones Utilitarias Recomendadas
|
|
459
|
+
|
|
460
|
+
Coleccion de funciones helper que todo proyecto XOne deberia incluir en `functions.js`.
|
|
461
|
+
|
|
462
|
+
```javascript
|
|
463
|
+
/**
|
|
464
|
+
* Funciones Utilitarias para XOne
|
|
465
|
+
* Incluir en functions.js de todo proyecto
|
|
466
|
+
*/
|
|
467
|
+
|
|
468
|
+
// ============================================
|
|
469
|
+
// CONVERSIONES Y VERIFICACIONES
|
|
470
|
+
// ============================================
|
|
471
|
+
|
|
472
|
+
function isEmpty(val) {
|
|
473
|
+
return val === undefined || val === null || val === "";
|
|
474
|
+
}
|
|
475
|
+
|
|
476
|
+
function cstr(val) {
|
|
477
|
+
if (val === undefined || val === null) return "";
|
|
478
|
+
return val.toString();
|
|
479
|
+
}
|
|
480
|
+
|
|
481
|
+
function cnum(val) {
|
|
482
|
+
if (val === undefined || val === null || val === "") return 0;
|
|
483
|
+
let num = parseFloat(val);
|
|
484
|
+
return isNaN(num) ? 0 : num;
|
|
485
|
+
}
|
|
486
|
+
|
|
487
|
+
function isNothing(obj) {
|
|
488
|
+
return obj === null || obj === undefined || obj == "undefined";
|
|
489
|
+
}
|
|
490
|
+
|
|
491
|
+
// ============================================
|
|
492
|
+
// ACCESO A CONTROLES
|
|
493
|
+
// ============================================
|
|
494
|
+
|
|
495
|
+
// getControl(name, [dataObject]) es NATIVA del motor — NO declararla aquí.
|
|
496
|
+
// Si un proyecto ya tiene su propio "function getControl(...)" como helper
|
|
497
|
+
// legacy, lo respetamos: la declaración del script sombrea a la nativa en
|
|
498
|
+
// su scope local sin tocar la del global.
|
|
499
|
+
|
|
500
|
+
// ============================================
|
|
501
|
+
// NAVEGACION
|
|
502
|
+
// ============================================
|
|
503
|
+
|
|
504
|
+
function mostrarGrupo(nGroup, sAnimIn, sAnimOut) {
|
|
505
|
+
sAnimIn = sAnimIn || "##ALPHA_IN##";
|
|
506
|
+
sAnimOut = sAnimOut || "##ALPHA_OUT##";
|
|
507
|
+
ui.showGroup(nGroup, sAnimIn, 200, sAnimOut, 200);
|
|
508
|
+
}
|
|
509
|
+
|
|
510
|
+
function cerrarPantalla() {
|
|
511
|
+
let window = ui.getView(self);
|
|
512
|
+
if (window) window.exit();
|
|
513
|
+
}
|
|
514
|
+
|
|
515
|
+
// ============================================
|
|
516
|
+
// MENSAJES Y DIALOGOS
|
|
517
|
+
// ============================================
|
|
518
|
+
|
|
519
|
+
function confirmar(mensaje, titulo) {
|
|
520
|
+
titulo = titulo || "Confirmar";
|
|
521
|
+
let nResult = ui.msgBox(mensaje, titulo, 4);
|
|
522
|
+
return nResult == 6;
|
|
523
|
+
}
|
|
524
|
+
|
|
525
|
+
function mostrarToast(mensaje) {
|
|
526
|
+
ui.showToast(mensaje);
|
|
527
|
+
}
|
|
528
|
+
|
|
529
|
+
function mostrarToastExito(mensaje) {
|
|
530
|
+
ui.showToast({
|
|
531
|
+
text: mensaje, color: "#4CAF50",
|
|
532
|
+
textColor: "#FFFFFF", duration: "short"
|
|
533
|
+
});
|
|
534
|
+
}
|
|
535
|
+
|
|
536
|
+
function mostrarToastError(mensaje) {
|
|
537
|
+
ui.showToast({
|
|
538
|
+
text: mensaje, color: "#F44336",
|
|
539
|
+
textColor: "#FFFFFF", duration: "long"
|
|
540
|
+
});
|
|
541
|
+
}
|
|
542
|
+
|
|
543
|
+
function mostrarCargando(mensaje) {
|
|
544
|
+
ui.showWaitDialog(mensaje || "Cargando...");
|
|
545
|
+
}
|
|
546
|
+
|
|
547
|
+
function ocultarCargando() {
|
|
548
|
+
ui.hideWaitDialog();
|
|
549
|
+
}
|
|
550
|
+
|
|
551
|
+
// ============================================
|
|
552
|
+
// COLECCIONES Y DATOS
|
|
553
|
+
// ============================================
|
|
554
|
+
|
|
555
|
+
function obtenerColeccion(nombreColl) {
|
|
556
|
+
return appData.getCollection(nombreColl);
|
|
557
|
+
}
|
|
558
|
+
|
|
559
|
+
function crearObjeto(nombreColl) {
|
|
560
|
+
let coll = appData.getCollection(nombreColl);
|
|
561
|
+
let obj = coll.createObject();
|
|
562
|
+
coll.addItem(obj);
|
|
563
|
+
return obj;
|
|
564
|
+
}
|
|
565
|
+
|
|
566
|
+
function buscarObjeto(nombreColl, campo, valor) {
|
|
567
|
+
let escapado = cstr(valor).replace(/'/g, "''");
|
|
568
|
+
let coll = appData.getCollection(nombreColl);
|
|
569
|
+
return coll.findObject(campo + "='" + escapado + "'");
|
|
570
|
+
}
|
|
571
|
+
|
|
572
|
+
// ============================================
|
|
573
|
+
// VALIDACIONES
|
|
574
|
+
// ============================================
|
|
575
|
+
|
|
576
|
+
function validarRequerido(valor, nombreCampo) {
|
|
577
|
+
if (isEmpty(valor)) {
|
|
578
|
+
mostrarToastError("El campo " + nombreCampo + " es obligatorio");
|
|
579
|
+
return false;
|
|
580
|
+
}
|
|
581
|
+
return true;
|
|
582
|
+
}
|
|
583
|
+
|
|
584
|
+
function validarEmail(email) {
|
|
585
|
+
if (isEmpty(email)) return false;
|
|
586
|
+
return email.indexOf("@") > 0 && email.indexOf(".") > 0;
|
|
587
|
+
}
|
|
588
|
+
|
|
589
|
+
function sanearEntrada(valor, maxLength) {
|
|
590
|
+
if (isEmpty(valor)) return "";
|
|
591
|
+
let sValor = cstr(valor).trim();
|
|
592
|
+
if (maxLength && sValor.length > maxLength) {
|
|
593
|
+
sValor = sValor.substring(0, maxLength);
|
|
594
|
+
}
|
|
595
|
+
return sValor;
|
|
596
|
+
}
|
|
597
|
+
|
|
598
|
+
// ============================================
|
|
599
|
+
// FECHAS Y TIEMPO
|
|
600
|
+
// ============================================
|
|
601
|
+
|
|
602
|
+
function obtenerFechaActual() {
|
|
603
|
+
let f = new Date();
|
|
604
|
+
return ("0" + f.getDate()).slice(-2) + "/" +
|
|
605
|
+
("0" + (f.getMonth() + 1)).slice(-2) + "/" +
|
|
606
|
+
f.getFullYear();
|
|
607
|
+
}
|
|
608
|
+
|
|
609
|
+
function obtenerHoraActual() {
|
|
610
|
+
let f = new Date();
|
|
611
|
+
return ("0" + f.getHours()).slice(-2) + ":" +
|
|
612
|
+
("0" + f.getMinutes()).slice(-2);
|
|
613
|
+
}
|
|
614
|
+
|
|
615
|
+
function obtenerAhora() {
|
|
616
|
+
return new Date();
|
|
617
|
+
}
|
|
618
|
+
|
|
619
|
+
// ============================================
|
|
620
|
+
// CONVERSION BINARIA (NFC, Bluetooth, etc.)
|
|
621
|
+
// ============================================
|
|
622
|
+
|
|
623
|
+
function toUint8Array(str) {
|
|
624
|
+
let arr = new Uint8Array(str.length);
|
|
625
|
+
for (let i = 0; i < str.length; i++) arr[i] = str.charCodeAt(i);
|
|
626
|
+
return arr;
|
|
627
|
+
}
|
|
628
|
+
|
|
629
|
+
function toStringFromUint8Array(arr) {
|
|
630
|
+
let str = "";
|
|
631
|
+
for (let i = 0; i < arr.length; i++) str += String.fromCharCode(arr[i]);
|
|
632
|
+
return str;
|
|
633
|
+
}
|
|
634
|
+
|
|
635
|
+
// ============================================
|
|
636
|
+
// GUID
|
|
637
|
+
// ============================================
|
|
638
|
+
|
|
639
|
+
function generarGUID() {
|
|
640
|
+
return 'xxxxxxxxxxxx4xxxyxxxxxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
|
|
641
|
+
var r = Math.random() * 16 | 0;
|
|
642
|
+
var v = c == 'x' ? r : (r & 0x3 | 0x8);
|
|
643
|
+
return v.toString(16);
|
|
644
|
+
});
|
|
645
|
+
}
|
|
646
|
+
```
|
|
647
|
+
|