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.
Files changed (187) hide show
  1. package/README.md +167 -0
  2. package/dist/agent/authEnDisco.js +56 -0
  3. package/dist/agent/authEnDisco.js.map +1 -0
  4. package/dist/agent/configEnDisco.js +109 -0
  5. package/dist/agent/configEnDisco.js.map +1 -0
  6. package/dist/agent/crearProyecto.js +34 -0
  7. package/dist/agent/crearProyecto.js.map +1 -0
  8. package/dist/agent/entorno.js +124 -0
  9. package/dist/agent/entorno.js.map +1 -0
  10. package/dist/agent/guionizado.js +53 -0
  11. package/dist/agent/guionizado.js.map +1 -0
  12. package/dist/agent/instantanea.js +129 -0
  13. package/dist/agent/instantanea.js.map +1 -0
  14. package/dist/agent/interrupts.js +83 -0
  15. package/dist/agent/interrupts.js.map +1 -0
  16. package/dist/agent/mensajes.js +24 -0
  17. package/dist/agent/mensajes.js.map +1 -0
  18. package/dist/agent/modelos.js +42 -0
  19. package/dist/agent/modelos.js.map +1 -0
  20. package/dist/agent/normalizar.js +20 -0
  21. package/dist/agent/normalizar.js.map +1 -0
  22. package/dist/agent/perfiles.js +103 -0
  23. package/dist/agent/perfiles.js.map +1 -0
  24. package/dist/agent/proyecto.js +107 -0
  25. package/dist/agent/proyecto.js.map +1 -0
  26. package/dist/agent/puente.js +142 -0
  27. package/dist/agent/puente.js.map +1 -0
  28. package/dist/agent/resumenDeTool.js +48 -0
  29. package/dist/agent/resumenDeTool.js.map +1 -0
  30. package/dist/agent/skills.js +65 -0
  31. package/dist/agent/skills.js.map +1 -0
  32. package/dist/agent/textoDeTool.js +113 -0
  33. package/dist/agent/textoDeTool.js.map +1 -0
  34. package/dist/agent/turnoReal.js +173 -0
  35. package/dist/agent/turnoReal.js.map +1 -0
  36. package/dist/agent/verificador.js +65 -0
  37. package/dist/agent/verificador.js.map +1 -0
  38. package/dist/agent/xoneAgent.js +106 -0
  39. package/dist/agent/xoneAgent.js.map +1 -0
  40. package/dist/bin.js +9 -0
  41. package/dist/bin.js.map +1 -0
  42. package/dist/cli/aprobar.js +82 -0
  43. package/dist/cli/aprobar.js.map +1 -0
  44. package/dist/cli/config.js +133 -0
  45. package/dist/cli/config.js.map +1 -0
  46. package/dist/cli/consola.js +346 -0
  47. package/dist/cli/consola.js.map +1 -0
  48. package/dist/cli/describe.js +53 -0
  49. package/dist/cli/describe.js.map +1 -0
  50. package/dist/cli/doctor.js +45 -0
  51. package/dist/cli/doctor.js.map +1 -0
  52. package/dist/cli/main.js +520 -0
  53. package/dist/cli/main.js.map +1 -0
  54. package/dist/cli/markdown.js +90 -0
  55. package/dist/cli/markdown.js.map +1 -0
  56. package/dist/cli/run.js +164 -0
  57. package/dist/cli/run.js.map +1 -0
  58. package/dist/cli/spinner.js +89 -0
  59. package/dist/cli/spinner.js.map +1 -0
  60. package/dist/cli/stdio.js +193 -0
  61. package/dist/cli/stdio.js.map +1 -0
  62. package/dist/cli/tema.js +40 -0
  63. package/dist/cli/tema.js.map +1 -0
  64. package/dist/cli/verify.js +62 -0
  65. package/dist/cli/verify.js.map +1 -0
  66. package/dist/core/bitacora.js +30 -0
  67. package/dist/core/bitacora.js.map +1 -0
  68. package/dist/core/config.js +236 -0
  69. package/dist/core/config.js.map +1 -0
  70. package/dist/core/contextos.js +56 -0
  71. package/dist/core/contextos.js.map +1 -0
  72. package/dist/core/deps.js +51 -0
  73. package/dist/core/deps.js.map +1 -0
  74. package/dist/core/diff.js +79 -0
  75. package/dist/core/diff.js.map +1 -0
  76. package/dist/core/esqueleto.js +366 -0
  77. package/dist/core/esqueleto.js.map +1 -0
  78. package/dist/core/events.js +2 -0
  79. package/dist/core/events.js.map +1 -0
  80. package/dist/core/modelos.js +85 -0
  81. package/dist/core/modelos.js.map +1 -0
  82. package/dist/core/notify.js +106 -0
  83. package/dist/core/notify.js.map +1 -0
  84. package/dist/core/ports.js +114 -0
  85. package/dist/core/ports.js.map +1 -0
  86. package/dist/core/turno.js +127 -0
  87. package/dist/core/turno.js.map +1 -0
  88. package/dist/vendor/hitl.js +103 -0
  89. package/dist/vendor/hitl.js.map +1 -0
  90. package/dist/vendor/skillLoaders/catalog.js +74 -0
  91. package/dist/vendor/skillLoaders/catalog.js.map +1 -0
  92. package/dist/vendor/tokenTracking.js +58 -0
  93. package/dist/vendor/tokenTracking.js.map +1 -0
  94. package/package.json +41 -0
  95. package/skills/xone-debugging/SKILL.md +52 -0
  96. package/skills/xone-debugging/references/faq.md +561 -0
  97. package/skills/xone-debugging/references/troubleshooting-y-glosario.md +429 -0
  98. package/skills/xone-development/SKILL.md +105 -0
  99. package/skills/xone-development/references/anti-patrones.md +94 -0
  100. package/skills/xone-development/references/css/atributos-por-categoria.md +509 -0
  101. package/skills/xone-development/references/css/buenas-practicas-y-parser.md +419 -0
  102. package/skills/xone-development/references/css/dinamicos-cascada-y-componentes.md +550 -0
  103. package/skills/xone-development/references/css/patrones-material-y-temas.md +1140 -0
  104. package/skills/xone-development/references/css/propiedades-y-herencia.md +885 -0
  105. package/skills/xone-development/references/css/selectores-unidades-colores.md +840 -0
  106. package/skills/xone-development/references/datos/appdata-referencia-ampliada.md +457 -0
  107. package/skills/xone-development/references/datos/appdata.md +611 -0
  108. package/skills/xone-development/references/datos/http-sqlmanager-y-crypto.md +720 -0
  109. package/skills/xone-development/references/datos/http.md +366 -0
  110. package/skills/xone-development/references/datos/oauth2-y-replica.md +156 -0
  111. package/skills/xone-development/references/device/biometria-imagedrawing-y-otros.md +544 -0
  112. package/skills/xone-development/references/device/objetos-de-dispositivo.md +583 -0
  113. package/skills/xone-development/references/device/systemsettings-referencia-ampliada.md +396 -0
  114. package/skills/xone-development/references/device/systemsettings-y-permisos.md +342 -0
  115. package/skills/xone-development/references/fundamentos/conceptos-clave.md +695 -0
  116. package/skills/xone-development/references/fundamentos/configuracion-app-xml-ini-mappings.md +668 -0
  117. package/skills/xone-development/references/fundamentos/errores-comunes.md +312 -0
  118. package/skills/xone-development/references/fundamentos/navegacion-convenciones-y-primer-proyecto.md +576 -0
  119. package/skills/xone-development/references/fundamentos/plataforma-y-anatomia-de-proyecto.md +387 -0
  120. package/skills/xone-development/references/indice-completo.md +77 -0
  121. package/skills/xone-development/references/javascript/coleccion-error-y-usuario.md +260 -0
  122. package/skills/xone-development/references/javascript/debugging-y-best-practices.md +245 -0
  123. package/skills/xone-development/references/javascript/metodos-de-los-controles.md +556 -0
  124. package/skills/xone-development/references/javascript/metodos-nativos-de-la-vista.md +114 -0
  125. package/skills/xone-development/references/javascript/motor-js-y-contexto-de-ejecucion.md +319 -0
  126. package/skills/xone-development/references/javascript/objeto-ai-llm-en-dispositivo.md +358 -0
  127. package/skills/xone-development/references/javascript/objetos-creables-a-m.md +527 -0
  128. package/skills/xone-development/references/javascript/objetos-creables-n-z.md +385 -0
  129. package/skills/xone-development/references/javascript/patrones-criticos-seguridad-y-rendimiento.md +595 -0
  130. package/skills/xone-development/references/javascript/patrones-de-navegacion-datos-y-codigo.md +751 -0
  131. package/skills/xone-development/references/javascript/patrones-de-ui-voz-integracion-y-seguridad.md +701 -0
  132. package/skills/xone-development/references/javascript/plantillas-y-funciones-utilitarias.md +647 -0
  133. package/skills/xone-development/references/javascript/self-y-dataobject.md +449 -0
  134. package/skills/xone-development/references/javascript/singletons-globales.md +361 -0
  135. package/skills/xone-development/references/javascript/ui-catalogo-de-metodos.md +464 -0
  136. package/skills/xone-development/references/javascript/ui-gps-camara-y-multimedia.md +506 -0
  137. package/skills/xone-development/references/javascript/ui-navegacion-mensajes-y-vista.md +422 -0
  138. package/skills/xone-development/references/resumen-js-datos-dispositivo.md +80 -0
  139. package/skills/xone-development/references/resumen-xml-y-css.md +74 -0
  140. package/skills/xone-development/references/tipos-de-prop.md +28 -0
  141. package/skills/xone-development/references/xml-ui/asfilter-visibilidad-eventos-y-macros.md +576 -0
  142. package/skills/xone-development/references/xml-ui/atributos-coll-group-frame.md +235 -0
  143. package/skills/xone-development/references/xml-ui/atributos-method-macro-script-event-app.md +244 -0
  144. package/skills/xone-development/references/xml-ui/atributos-prop.md +439 -0
  145. package/skills/xone-development/references/xml-ui/contents-y-macros.md +392 -0
  146. package/skills/xone-development/references/xml-ui/errores-comunes-xml.md +245 -0
  147. package/skills/xone-development/references/xml-ui/estructura-y-nodo-coll.md +326 -0
  148. package/skills/xone-development/references/xml-ui/eventos-ciclo-de-vida-e-interaccion.md +704 -0
  149. package/skills/xone-development/references/xml-ui/eventos-sistema-login-y-personalizados.md +944 -0
  150. package/skills/xone-development/references/xml-ui/layouts-herencia-y-buenas-practicas.md +618 -0
  151. package/skills/xone-development/references/xml-ui/mapas.md +685 -0
  152. package/skills/xone-development/references/xml-ui/mappings-y-colecciones-separadas.md +148 -0
  153. package/skills/xone-development/references/xml-ui/nodos-group-y-frame.md +666 -0
  154. package/skills/xone-development/references/xml-ui/patrones-de-pantalla.md +643 -0
  155. package/skills/xone-development/references/xml-ui/prop-atributos-y-condiciones.md +298 -0
  156. package/skills/xone-development/references/xml-ui/prop-tipos-basicos.md +493 -0
  157. package/skills/xone-development/references/xml-ui/prop-tipos-combos-y-controles.md +664 -0
  158. package/skills/xone-development/references/xml-ui/prop-tipos-listas-y-mapas.md +412 -0
  159. package/skills/xone-plan-builder/SKILL.md +183 -0
  160. package/skills/xone-plan-builder/agents/openai.yaml +5 -0
  161. package/skills/xone-plan-builder/references/TASKS-FORMAT.md +132 -0
  162. package/skills/xone-project-generator/SKILL.md +111 -0
  163. package/skills/xone-project-generator/references/canonical-sizes.md +292 -0
  164. package/skills/xone-project-generator/references/colecciones-base.md +16 -0
  165. package/skills/xone-project-generator/references/convenciones-de-nombres.md +16 -0
  166. package/skills/xone-project-generator/references/ejemplos-por-sector-y-prohibiciones.md +140 -0
  167. package/skills/xone-project-generator/references/fase-3-estilos-css.md +597 -0
  168. package/skills/xone-project-generator/references/fase-6-colecciones.md +762 -0
  169. package/skills/xone-project-generator/references/fase-7-asfilter-e-integraciones.md +142 -0
  170. package/skills/xone-project-generator/references/fase-7-entidades-y-estructura-de-pantalla.md +494 -0
  171. package/skills/xone-project-generator/references/fase-7-plantilla-consola.md +477 -0
  172. package/skills/xone-project-generator/references/fase-7-plantillas-de-pantalla.md +302 -0
  173. package/skills/xone-project-generator/references/fase-7-viewmodes-graficos-y-listas.md +681 -0
  174. package/skills/xone-project-generator/references/fase-7-viewmodes-mapa-y-calendario.md +479 -0
  175. package/skills/xone-project-generator/references/fases-0-2-analisis-y-modelo-de-datos.md +350 -0
  176. package/skills/xone-project-generator/references/fases-10-12-readmes-y-validacion.md +194 -0
  177. package/skills/xone-project-generator/references/fases-4-5-estructura-y-configuracion.md +587 -0
  178. package/skills/xone-project-generator/references/fases-8-9-eventos-y-javascript.md +861 -0
  179. package/skills/xone-project-generator/references/flujo-de-generacion.md +67 -0
  180. package/skills/xone-project-generator/references/plantillas-estandar.md +98 -0
  181. package/skills/xone-project-generator/references/tamanos-canonicos.md +49 -0
  182. package/skills/xone-review/SKILL.md +189 -0
  183. package/skills/xone-spec-builder/SKILL.md +235 -0
  184. package/skills/xone-spec-builder/agents/openai.yaml +5 -0
  185. package/skills/xone-spec-builder/references/ADR-FORMAT.md +55 -0
  186. package/skills/xone-spec-builder/references/CONTEXT-FORMAT.md +42 -0
  187. package/skills/xone-spec-builder/references/PLAN-FORMAT.md +120 -0
@@ -0,0 +1,751 @@
1
+ # XOne — Patrones de navegación, datos y código
2
+
3
+ > Fuente: `xone/v2/xone-help-docs/topics/05-events-patterns-faq.md` §9–§10A. Referencia de la skill; el índice está en [../../SKILL.md](../../SKILL.md).
4
+
5
+ Contenido: §9 patrones de navegación · §10 patrones de datos · §10A patrones críticos de código
6
+
7
+ ---
8
+
9
+ ## 9. Patrones de Navegación
10
+
11
+ ### 9.1 Pantalla de Login
12
+
13
+ Plantilla mínima de coll de login (el framework la muestra automáticamente al arrancar la app cuando no hay sesión activa; tras un `appData.logout()` también vuelve a esta pantalla).
14
+
15
+ ```xml
16
+ <coll name="Login" title="Iniciar Sesion"
17
+ notab="true" show-toolbar="false">
18
+
19
+ <create>
20
+ <script>
21
+ self.MAP_EMAIL = "";
22
+ self.MAP_PASSWORD = "";
23
+ </script>
24
+ </create>
25
+
26
+ <group name="grpPrincipal" id="1" class="groupNoTab">
27
+ <frame name="frmFormulario" width="100%" bgcolor="#FFFFFF">
28
+ <prop name="MAP_EMAIL" type="T" visible="7"
29
+ width="90%" height="56p" align="center"
30
+ hint="tu@email.com"/>
31
+ <prop name="MAP_PASSWORD" type="X" visible="7"
32
+ width="90%" height="56p" align="center"
33
+ hint="Tu contraseña"/>
34
+ <prop name="btnLogin" type="B" visible="7"
35
+ width="90%" height="56p" align="center" tmargin="30p"
36
+ title="Iniciar Sesion"
37
+ onclick="realizarLogin();" />
38
+ </frame>
39
+ </group>
40
+
41
+ <onback>
42
+ <script>
43
+ cerrarPantalla();
44
+ </script>
45
+ </onback>
46
+ </coll>
47
+ ```
48
+
49
+ ### 9.2 Lista -> Detalle (maestro-detalle)
50
+
51
+ Patron para navegar de una lista a los detalles de un registro seleccionado.
52
+
53
+ **Coleccion lista con selecteditem:**
54
+
55
+ ```xml
56
+ <coll name="ListaProductos" sql="SELECT * FROM ##PREF##Productos"
57
+ loadall="true">
58
+ <group name="General" id="1">
59
+ <prop name="MAP_NOMBRE" type="T" visible="4"/>
60
+ <prop name="MAP_PRECIO" type="N2" visible="4"/>
61
+ </group>
62
+
63
+ <selecteditem refresh="false" show-wait-dialog="false">
64
+ <action name="runscript">
65
+ <script language="javascript">
66
+ ui.openEditView(self);
67
+ </script>
68
+ </action>
69
+ </selecteditem>
70
+ </coll>
71
+ ```
72
+
73
+ **Content embebido en la pantalla padre:**
74
+
75
+ ```xml
76
+ <prop name="@listaProductos" type="Z" contents="listaProductos"
77
+ width="100%" height="70%" viewmode="recyclerview"/>
78
+ <contents name="listaProductos" src="ListaProductos"/>
79
+ ```
80
+
81
+ ### 9.3 Menu con tarjetas
82
+
83
+ Patron de menu principal con tarjetas de acceso rápido.
84
+
85
+ ```xml
86
+ <coll name="MenuPrincipal" special="true" notab="true">
87
+ <group name="grpPrincipal" id="1" class="groupNoTab">
88
+ <frame name="frmHeader" width="100%" height="120p" bgcolor="#1565C0">
89
+ <prop name="lblTitulo" type="L" visible="7"
90
+ width="100%" height="40p" align="center"
91
+ forecolor="#FFFFFF" fontsize="20" title="Menu Principal"/>
92
+ </frame>
93
+
94
+ <frame name="frmTarjetas" width="100%" scroll="true" tmargin="10p">
95
+ <!-- Tarjeta 1 -->
96
+ <frame name="frmTarjeta1" width="47%" height="150p"
97
+ lmargin="2%" tmargin="10p" bgcolor="#FFFFFF"
98
+ border-corner-radius="12" framebox="true"
99
+ onclick="javascript:abrirModulo('Inventario');">
100
+ <prop name="imgTarjeta1" type="IMG" visible="7"
101
+ width="48p" height="48p" align="center" tmargin="20p"
102
+ src="./icons/ic_inventory.png"/>
103
+ <prop name="lblTarjeta1" type="L" visible="7"
104
+ width="100%" height="30p" align="center" tmargin="10p"
105
+ fontsize="14" title="Inventario"/>
106
+ </frame>
107
+
108
+ <!-- Tarjeta 2 -->
109
+ <frame name="frmTarjeta2" width="47%" height="150p"
110
+ lmargin="2%" tmargin="10p" newline="false"
111
+ bgcolor="#FFFFFF" border-corner-radius="12" framebox="true"
112
+ onclick="javascript:abrirModulo('Pedidos');">
113
+ <prop name="imgTarjeta2" type="IMG" visible="7"
114
+ width="48p" height="48p" align="center" tmargin="20p"
115
+ src="./icons/ic_orders.png"/>
116
+ <prop name="lblTarjeta2" type="L" visible="7"
117
+ width="100%" height="30p" align="center" tmargin="10p"
118
+ fontsize="14" title="Pedidos"/>
119
+ </frame>
120
+ </frame>
121
+ </group>
122
+ </coll>
123
+ ```
124
+
125
+ ### 9.4 Navegación con pestanas (group-swipe)
126
+
127
+ ```xml
128
+ <coll name="PantallaConTabs" special="true" group-swipe="true">
129
+ <group name="HEADER" id="10" class="groupfixed_header">
130
+ <frame name="frmtitulo" class="frmsuperior">
131
+ <prop name="MENU" type="L" title="MI PANTALLA"/>
132
+ </frame>
133
+ </group>
134
+
135
+ <group name="Tab1" id="1" onfocus="ExecuteNode(onfocusgrupo(1))">
136
+ <!-- Contenido pestana 1 -->
137
+ </group>
138
+
139
+ <group name="Tab2" id="2" onfocus="ExecuteNode(onfocusgrupo(2))">
140
+ <!-- Contenido pestana 2 -->
141
+ </group>
142
+
143
+ <group name="Tab3" id="3" onfocus="ExecuteNode(onfocusgrupo(3))">
144
+ <!-- Contenido pestana 3 -->
145
+ </group>
146
+
147
+ <onfocusgrupo show-wait-dialog="false">
148
+ <action name="runscript">
149
+ <param name="index"/>
150
+ <script language="javascript">
151
+ self.MAP_GROUP = index;
152
+ </script>
153
+ </action>
154
+ </onfocusgrupo>
155
+
156
+ <before-edit>
157
+ <action name="runscript">
158
+ <script language="javascript">
159
+ self.MAP_GROUP = 1;
160
+ self.MAP_TOTAL_PAGES = 3;
161
+ </script>
162
+ </action>
163
+ </before-edit>
164
+ </coll>
165
+ ```
166
+
167
+ ### 9.5 Drawer lateral
168
+
169
+ ```xml
170
+ <coll name="PantallaConDrawer" notab="true"
171
+ ondraweropened="onDrawerOpened(e);"
172
+ ondrawerclosed="onDrawerClosed(e);">
173
+
174
+ <group name="Drawer" id="999"
175
+ width="60%" drawer-orientation="left" bgcolor="#FFFFFF">
176
+ <frame name="header_drawer" width="100%" height="25%"
177
+ bgcolor="#2B3E51" align="bottom">
178
+ <prop name="lblUsuario" type="L" visible="7"
179
+ forecolor="#FFFFFF" title="Nombre Usuario"/>
180
+ </frame>
181
+ <prop name="btnOpcion1" type="B" visible="7"
182
+ width="100%" height="60p" title="Inventario"
183
+ onclick="javascript:abrirModulo('Inventario');"/>
184
+ <prop name="btnOpcion2" type="B" visible="7"
185
+ width="100%" height="60p" title="Pedidos"
186
+ onclick="javascript:abrirModulo('Pedidos');"/>
187
+ </group>
188
+
189
+ <group name="Contenido" id="1">
190
+ <!-- Contenido principal -->
191
+ </group>
192
+ </coll>
193
+ ```
194
+
195
+ ### 9.6 Volver atrás con confirmacion
196
+
197
+ ```xml
198
+ <onback show-wait-dialog="false">
199
+ <action name="runscript">
200
+ <script language="javascript">
201
+ var ok = ui.msgBox("Desea salir sin guardar?", "Confirmar", 4);
202
+ if (ok == 6) {
203
+ ui.getView(self).exit();
204
+ }
205
+ </script>
206
+ </action>
207
+ </onback>
208
+ ```
209
+
210
+ ---
211
+
212
+ ## 10. Patrones de Datos
213
+
214
+ ### 10.1 CRUD completo (crear, leer, actualizar, eliminar)
215
+
216
+ **Crear:**
217
+
218
+ ```javascript
219
+ var coll = appData.getCollection("Productos");
220
+ var obj = new Productos({
221
+ NOMBRE: "Producto nuevo",
222
+ PRECIO: 29.99,
223
+ ACTIVO: 1,
224
+ FECHA_ALTA: new Date()
225
+ });
226
+ coll.addItem(obj);
227
+ obj.save();
228
+ ui.openEditView(obj); // Abrir en edición
229
+ ```
230
+
231
+ **Leer (lista con selecteditem + openEditView):**
232
+
233
+ ```xml
234
+ <selecteditem refresh="false" show-wait-dialog="false">
235
+ <action name="runscript">
236
+ <script language="javascript">
237
+ ui.openEditView(self);
238
+ </script>
239
+ </action>
240
+ </selecteditem>
241
+ ```
242
+
243
+ **Actualizar (formulario con save):**
244
+
245
+ ```javascript
246
+ // Dentro de un evento custom "guardar"
247
+ self.NOMBRE = self.MAP_NOMBRE;
248
+ self.PRECIO = self.MAP_PRECIO;
249
+ self.FECHA_MOD = new Date();
250
+ self.save();
251
+ ui.showToast("Guardado correctamente");
252
+ ui.getView(self).exit();
253
+ ```
254
+
255
+ **Eliminar (confirmar + deleteItem):**
256
+
257
+ ```xml
258
+ <eliminar show-wait-dialog="false" refresh="true">
259
+ <action name="runscript">
260
+ <script language="javascript">
261
+ var ok = ui.msgBox("Desea eliminar el registro seleccionado?",
262
+ "Aviso", 4);
263
+ if (ok === 6) {
264
+ var CollCal = appData.getCollection("ContentdatosCalendario");
265
+ CollCal.deleteItem(self.MAP_IDTAREASELECTED.toString());
266
+ self.MAP_IDTAREASELECTED = 0;
267
+ CollCal = null;
268
+ ui.showToast("Elemento borrado correctamente.");
269
+ } else {
270
+ ui.showToast("Se ha cancelado la acción.");
271
+ }
272
+ </script>
273
+ </action>
274
+ </eliminar>
275
+ ```
276
+
277
+ ### 10.2 Maestro-detalle con contents
278
+
279
+ **XML del maestro con content embebido:**
280
+
281
+ ```xml
282
+ <frame name="c1" width="98%" height="78%"
283
+ framebox="true" border-corner-radius="10" lmargin="1%">
284
+ <prop name="MAP_content1" height="96%" type="Z"
285
+ contents="content1" forceonchange="true" bgcolor="#FFFFFF"
286
+ onchange="refresh(@content1)"/>
287
+ <contents name="content1" src="ContentDatos"/>
288
+ </frame>
289
+ ```
290
+
291
+ **Agregar item al content (ejemplo real de EspecialContents):**
292
+
293
+ ```xml
294
+ <nuevo show-wait-dialog="false" refresh="false">
295
+ <action name="runscript">
296
+ <script language="javascript">
297
+ var coll = self.getContents("content1");
298
+ var obj = coll.createObject();
299
+ obj.setValue("ID", MAP_COUNTER_EXT);
300
+ obj.NOMBRE = "Hola " + MAP_COUNTER_EXT.toString();
301
+ obj.DIRECCION = "dirección " + MAP_COUNTER_EXT.toString();
302
+ obj.IMAGEN = "campanon.jpg";
303
+ MAP_COUNTER_EXT = MAP_COUNTER_EXT + 1;
304
+ let view = ui.getView(self);
305
+ view.MAP_content1.addItem(obj);
306
+ </script>
307
+ </action>
308
+ </nuevo>
309
+ ```
310
+
311
+ ### 10.3 Filtrado dinámico de listas
312
+
313
+ **Ejemplo real** (del proyecto EspecialContents):
314
+
315
+ ```xml
316
+ <buscar show-wait-dialog="false" refresh="false">
317
+ <action name="runscript">
318
+ <param name="param"/>
319
+ <script language="javascript">
320
+ if (param == "1") {
321
+ if (self.MAP_FILTRO.length == 0) {
322
+ self.getContents("content4").setFilter("");
323
+ } else {
324
+ self.getContents("content4").setFilter(
325
+ "NOMBRE like '%" + self.MAP_FILTRO.toString()
326
+ + "%' OR DIRECCION like '%"
327
+ + self.MAP_FILTRO.toString() + "%'"
328
+ );
329
+ }
330
+ }
331
+ self.getContents("content4").unlock();
332
+ self.getContents("content4").loadAll();
333
+ self.getContents("content4").lock();
334
+ ui.getView(self).refresh("@content4");
335
+ </script>
336
+ </action>
337
+ </buscar>
338
+ ```
339
+
340
+ **Filtro en XML con macros de campo:**
341
+
342
+ ```xml
343
+ <contents name="ContentDatosFiltroMultiseleccion"
344
+ src="ContentDatosFiltroMultiseleccion"
345
+ filter="((t1.MARCADO=1 AND 1=##FLD_MAP_BUSCAR_MARCADOS##)
346
+ OR (t1.MARCADO=0 AND 1=##FLD_MAP_BUSCAR_NOMARCADOS##))
347
+ AND (ifnull(t1.NOMBRE,'') LIKE ##FLD_MAP_BUSCAR_TEXT##
348
+ OR ifnull(t1.DIRECCION,'') LIKE ##FLD_MAP_BUSCAR_TEXT##)"/>
349
+ ```
350
+
351
+ ### 10.4 Busqueda en tiempo real con ontextchanged
352
+
353
+ ```xml
354
+ <prop name="MAP_BUSCAR_TEXT"
355
+ ontextchanged="javascript:FiltraMarcados(e);"
356
+ labelwidth="0" text-border="true" type="T"
357
+ width="98%" height="60p" tooltip="Texto a buscar"/>
358
+ ```
359
+
360
+ ```javascript
361
+ function FiltraMarcados(e) {
362
+ self.MAP_BUSCAR_TEXT = e.newText;
363
+ self.executeNode("applyfilter");
364
+ }
365
+ ```
366
+
367
+ ```xml
368
+ <applyfilter>
369
+ <action name="runscript">
370
+ <script language="javascript">
371
+ self.getContents("ContentDatosFiltroMultiseleccion").clear();
372
+ self.getContents("ContentDatosFiltroMultiseleccion").loadAll();
373
+ ui.refresh("@ContentDatosFiltroMultiseleccion");
374
+ </script>
375
+ </action>
376
+ </applyfilter>
377
+ ```
378
+
379
+ ### 10.5 Ordenamiento ASC/DESC
380
+
381
+ ```javascript
382
+ if (self.MAP_ORDEN == "ASC") {
383
+ self.MAP_ORDEN = "DESC";
384
+ self.MAP_BTORDEN = "sortZA.png";
385
+ self.MAP_BTORDENCLICK = "sortZA_click.png";
386
+ } else {
387
+ self.MAP_ORDEN = "ASC";
388
+ self.MAP_BTORDEN = "sortAZ.png";
389
+ self.MAP_BTORDENCLICK = "sortAZ_click.png";
390
+ }
391
+ self.getContents("content4").sort = "NOMBRE " + self.MAP_ORDEN.toString();
392
+ self.getContents("content4").unlock();
393
+ self.getContents("content4").loadAll();
394
+ self.getContents("content4").lock();
395
+ ui.getView(self).refresh("@content4,BTORDENAR");
396
+ ```
397
+
398
+ ### 10.6 Multiseleccion en lista
399
+
400
+ **XML con checkbox en cada fila:**
401
+
402
+ ```xml
403
+ <prop name="MAP_BUSCAR_MARCADOS" title="Done" labelwidth="5"
404
+ type="NC" width="49%" height="60p"/>
405
+ <prop name="MAP_BUSCAR_NOMARCADOS" title="Not done" labelwidth="8"
406
+ type="NC" width="49%" height="60p" newline="false"/>
407
+
408
+ <prop name="MAP_DONE_PENDIENTES_BT" title="Marcar seleccionados"
409
+ method="Executenode(checkAll(1))" type="B" width="48%"/>
410
+ <prop name="MAP_NOTDONE_PENDIENTES_BT" title="Desmarcar seleccionados"
411
+ method="Executenode(checkAll(0))" type="B" width="48%" newline="false"/>
412
+ ```
413
+
414
+ ### 10.7 Paginación
415
+
416
+ XOne carga datos de forma paginada por defecto. Para controlar la paginación:
417
+
418
+ ```xml
419
+ <!-- Desactivar paginacion (cargar todo) -->
420
+ <coll name="MiColeccion" page-limit-off="1" loadall="true">
421
+
422
+ <!-- O usar loadAll desde script -->
423
+ ```
424
+
425
+ ```javascript
426
+ var coll = self.getContents("miContent");
427
+ coll.clear();
428
+ coll.loadAll();
429
+ ```
430
+
431
+ ---
432
+
433
+ ## 10A. Patrones Críticos de Código
434
+
435
+ ### 10A.1 Patron lock/unlock para escritura en colecciones
436
+
437
+ **Regla fundamental:** Siempre usar `unlock()` antes de modificar y `lock()` en un bloque `finally` para garantizar que la coleccion se bloquea incluso si hay error.
438
+
439
+ ```javascript
440
+ function agregarItem(nombreColeccion, datos) {
441
+ var coll = appData.getCollection(nombreColeccion);
442
+ try {
443
+ coll.unlock();
444
+ var newObj = coll.createObject();
445
+ for (var key in datos) {
446
+ if (datos.hasOwnProperty(key)) {
447
+ newObj[key] = datos[key];
448
+ }
449
+ }
450
+ coll.addItem(newObj);
451
+ newObj.save();
452
+ return true;
453
+ } catch(error) {
454
+ ui.showToast("Error: " + error.message);
455
+ return false;
456
+ } finally {
457
+ coll.lock();
458
+ }
459
+ }
460
+ ```
461
+
462
+ **Para contents embebidos:**
463
+
464
+ ```javascript
465
+ var content = self.getContents("content4");
466
+ try {
467
+ content.unlock();
468
+ content.loadAll();
469
+ // ... modificar datos ...
470
+ } finally {
471
+ content.lock();
472
+ }
473
+ ui.getView(self).refresh("@content4");
474
+ ```
475
+
476
+ ### 10A.2 Patron startBrowse/endBrowse para iteracion
477
+
478
+ **Regla:** `startBrowse()`/`endBrowse()` solo son necesarios cuando vas a **iterar** la coleccion (recorrer registros con `moveNext()`/`movePrevious()`). Abren un cursor en BD que hay que cerrar siempre en un `finally`.
479
+
480
+ **Cómo se itera correctamente:**
481
+
482
+ - `startBrowse()` ya deja el cursor en el **primer** item — no hace falta `moveFirst()` después.
483
+ - `getCurrentItem()` devuelve el `dataobject` de la fila actual.
484
+ - `moveNext()` devuelve `boolean`: `true` si pudo avanzar, `false` cuando ya no hay más filas. **No existe `coll.eof()`** en el JS API.
485
+ - **El objeto devuelto por `getCurrentItem()` es efímero**: el mismo objeto se reutiliza en el siguiente `moveNext()`, su contenido se reemplaza. No guardes la referencia en una variable para usarla más tarde; lee lo que necesites en la iteración o haz una copia (p.ej. con `JSON.stringify`/`JSON.parse` o copiando campo a campo en un objeto plano).
486
+ - `coll.MAP_XXX` **no** lee del item actual: las colecciones no exponen campos del objeto; siempre acceder vía `coll.getCurrentItem().MAP_XXX`.
487
+
488
+ ```javascript
489
+ function listarUsuariosActivos() {
490
+ var coll = appData.getCollection("Usuarios");
491
+ var lista = [];
492
+ coll.startBrowse();
493
+ try {
494
+ var obj = coll.getCurrentItem();
495
+ while (obj != null) {
496
+ // Copia el campo (no guardes obj — se reutiliza en el siguiente moveNext)
497
+ lista.push(obj.MAP_LOGIN);
498
+ if (!coll.moveNext()) break;
499
+ obj = coll.getCurrentItem();
500
+ }
501
+ } finally {
502
+ coll.endBrowse();
503
+ }
504
+ return lista;
505
+ }
506
+ ```
507
+
508
+ **No requieren `startBrowse`:**
509
+
510
+ - `findObject(criteria)` y `findAllObjects(criteria)` — ejecutan su propia SQL independiente del cursor.
511
+ - `get(index)` / `getItem(index)` / `getItem(field, value)` — acceso directo sin cursor.
512
+ - `loadAll()` seguido de `getCount()` + `get(i)` — `loadAll` carga la lista en memoria y se accede por índice.
513
+
514
+ ```javascript
515
+ // CORRECTO: findObject NO necesita startBrowse/endBrowse
516
+ function obtenerUsuario(userId) {
517
+ var coll = appData.getCollection("Usuarios");
518
+ var escapado = cstr(userId).replace(/'/g, "''");
519
+ return coll.findObject("LOGIN='" + escapado + "'");
520
+ }
521
+ ```
522
+
523
+ ### 10A.3 Patron filtro con restauracion
524
+
525
+ Siempre guardar y restaurar el filtro original en un bloque `finally`:
526
+
527
+ ```javascript
528
+ function procesarDatosFiltrados(coll, filtro) {
529
+ var filtroOriginal = coll.getFilter();
530
+ try {
531
+ coll.setFilter(filtro);
532
+ coll.loadAll();
533
+ var count = coll.count();
534
+ // ... procesar datos filtrados ...
535
+ return count;
536
+ } finally {
537
+ coll.setFilter(filtroOriginal);
538
+ }
539
+ }
540
+ ```
541
+
542
+ ### 10A.4 Patron GPS: iniciar, leer, detener
543
+
544
+ ```javascript
545
+ function obtenerPosicionGPS() {
546
+ ui.startGps();
547
+
548
+ var lat = ui.getGpsLatitude();
549
+ var lng = ui.getGpsLongitude();
550
+
551
+ if (lat != 0 && lng != 0) {
552
+ self.MAP_LATITUD = lat;
553
+ self.MAP_LONGITUD = lng;
554
+ ui.refresh("MAP_LATITUD,MAP_LONGITUD");
555
+ }
556
+ }
557
+ ```
558
+
559
+ **GPS con callback para tracking continuo:**
560
+
561
+ ```javascript
562
+ function iniciarTracking() {
563
+ var jsParams = {
564
+ nodeName: "callbackgps",
565
+ timeBetweenUpdates: 10000,
566
+ minimumMetersDistanceRange: 10,
567
+ foreground: true,
568
+ title: "Mi App GPS",
569
+ text: "Rastreando ubicación..."
570
+ };
571
+ ui.startGps(jsParams);
572
+ }
573
+
574
+ function detenerTracking() {
575
+ ui.stopGps();
576
+ }
577
+ ```
578
+
579
+ **GPS con variables de empresa para compartir coordenadas:**
580
+
581
+ ```javascript
582
+ function GetPosGPS(tipo, auxcollobj) {
583
+ var latitud = 0, longitud = 0;
584
+ ui.startGps();
585
+
586
+ var collGPS = appData.getCollection("ContentConectarGPS");
587
+ collGPS.startBrowse();
588
+ var x = collGPS.getCurrentItem();
589
+ if (typeof x !== "undefined" && x !== null) {
590
+ if (x.STATUS == 1 && x.HGPS.length > 0) {
591
+ if (x.LATITUD !== "") latitud = parseFloat(x.LATITUD);
592
+ if (x.LONGITUD.length > 0) longitud = parseFloat(x.LONGITUD);
593
+ }
594
+ }
595
+
596
+ // Guardar en variable global
597
+ appData.getCurrentEnterprise().setVariable("LATITUD", latitud);
598
+ appData.getCurrentEnterprise().setVariable("LONGITUD", longitud);
599
+ }
600
+ ```
601
+
602
+ ### 10A.5 Patron de chat/mensajeria (resumen)
603
+
604
+ El chat en XOne se implementa con 4 colecciones: pantalla principal, lista de conversaciones, usuarios y mensajes. Patron clave:
605
+
606
+ ```javascript
607
+ function enviarMensaje(obj, tipo) {
608
+ if (obj.MAP_MENSAJE.length > 0 || tipo > 0) {
609
+ var contentChatear = obj.getContents("ContentChatear");
610
+ try {
611
+ contentChatear.lock();
612
+ var msgObj = contentChatear.createObject();
613
+ contentChatear.addItem(msgObj);
614
+ msgObj.IDCHAT = obj.MAP_IDCHAT;
615
+ msgObj.USUARIO = appData.getGlobalMacro("##MACRO##");
616
+ msgObj.TIPO = tipo;
617
+ msgObj.FECHA = new Date();
618
+ if (tipo === 0) {
619
+ msgObj.MENSAJE = obj.MAP_MENSAJE;
620
+ obj.MAP_MENSAJE = "";
621
+ }
622
+ msgObj.save();
623
+ } finally {
624
+ contentChatear.unlock();
625
+ }
626
+ ui.refresh("ContentChatear", "MAP_MENSAJE");
627
+ }
628
+ }
629
+ ```
630
+
631
+ **Atributo clave para chat:** `start-from-bottom="true"` en el prop del content para que los mensajes se muestren desde abajo (estilo WhatsApp).
632
+
633
+ ### 10A.6 Patron de dialogo modal (coleccion como modal)
634
+
635
+ Abrir una coleccion como dialogo modal usando `ui.openEditView()`:
636
+
637
+ ```javascript
638
+ function abrirDialogoModal() {
639
+ var coll = appData.getCollection("MiDialogo");
640
+ var obj = new MiDialogo({ MAP_TITULO: "Titulo del dialogo", MAP_MENSAJE: "Contenido" });
641
+ coll.addItem(obj);
642
+ ui.openEditView(obj);
643
+ }
644
+ ```
645
+
646
+ **Alternativa con frame flotante y modal:**
647
+
648
+ ```xml
649
+ <frame name="frmModal"
650
+ animation-in-delay="250" animation-out-delay="250"
651
+ animation-in="##RIGHT_IN##" animation-out="##LEFT_OUT##"
652
+ disablevisible="MAP_VERMODAL=0"
653
+ bgcolor="#ffffff" modal="true" floating="true"
654
+ top="0" left="0" width="100%" height="100%">
655
+ <!-- Contenido del modal -->
656
+ <prop name="btnCerrar" type="B" title="Cerrar"
657
+ onclick="self.MAP_VERMODAL=0; ui.refresh('frmModal');"/>
658
+ </frame>
659
+ ```
660
+
661
+ ### 10A.7 Patron custom msgbox (coleccion como msgbox)
662
+
663
+ Usar una coleccion como cuadro de dialogo personalizado en lugar del `ui.msgBox()` nativo:
664
+
665
+ ```javascript
666
+ function userMsgBox(title, msg, type) {
667
+ var collMsgBox = appData.getCollection("EspecialMsgbox").createClone();
668
+ var objMsgBox = collMsgBox.createObject();
669
+ collMsgBox.addItem(objMsgBox);
670
+ objMsgBox.MAP_TITULO = title;
671
+ objMsgBox.MAP_MENSAJE = msg;
672
+ objMsgBox.MAP_TIPO = type;
673
+ var nResult = ui.msgBox(objMsgBox);
674
+ return nResult;
675
+ }
676
+ ```
677
+
678
+ **La coleccion del msgbox usa atributos especiales:**
679
+ - `cancelable="false"` - No se puede cerrar pulsando fuera
680
+ - `cancelable-outside="false"` - No se puede cerrar tocando fuera
681
+ - `button-option="10"` en el botón OK, `button-option="1"` en SI, `button-option="2"` en NO
682
+ - `height="-2"` para altura automática basada en contenido
683
+
684
+ ### 10A.8 Patron wizard (formulario multi-paso con groups)
685
+
686
+ Usar `group-swipe="true"` y grupos numerados para crear un asistente paso a paso:
687
+
688
+ ```xml
689
+ <coll name="FormularioWizard" special="true" group-swipe="true">
690
+ <group name="HEADER" id="10" class="groupfixed_header">
691
+ <frame name="frmtitulo" class="frmsuperior">
692
+ <prop name="MENU" type="L" title="Formulario"/>
693
+ <prop name="MAP_LAST" type="B" img="atras.png"
694
+ method="ExecuteNode(ir(-1))"
695
+ disablevisible="MAP_GROUP=1"/>
696
+ <prop name="MAP_NEXT" type="B" img="siguiente.png"
697
+ method="ExecuteNode(ir(1))"
698
+ disablevisible="MAP_GROUP=MAP_TOTAL_PAGES"
699
+ newline="false"/>
700
+ </frame>
701
+ </group>
702
+
703
+ <group name="Paso1" id="1" onfocus="ExecuteNode(onfocusgrupo(1))">
704
+ <!-- Datos personales -->
705
+ </group>
706
+ <group name="Paso2" id="2" onfocus="ExecuteNode(onfocusgrupo(2))">
707
+ <!-- Dirección -->
708
+ </group>
709
+ <group name="Paso3" id="3" onfocus="ExecuteNode(onfocusgrupo(3))">
710
+ <!-- Confirmación -->
711
+ </group>
712
+
713
+ <before-edit>
714
+ <action name="runscript">
715
+ <script language="javascript">
716
+ self.MAP_GROUP = 1;
717
+ self.MAP_TOTAL_PAGES = 3;
718
+ </script>
719
+ </action>
720
+ </before-edit>
721
+
722
+ <onfocusgrupo show-wait-dialog="false">
723
+ <action name="runscript">
724
+ <param name="index"/>
725
+ <script language="javascript">
726
+ self.MAP_GROUP = index;
727
+ </script>
728
+ </action>
729
+ </onfocusgrupo>
730
+
731
+ <ir show-wait-dialog="false">
732
+ <action name="runscript">
733
+ <param name="direccion"/>
734
+ <script language="javascript">
735
+ let nuevo = parseInt(self.MAP_GROUP) + parseInt(direccion);
736
+ if (nuevo >= 1 && nuevo <= self.MAP_TOTAL_PAGES) {
737
+ ui.showGroup(nuevo);
738
+ }
739
+ </script>
740
+ </action>
741
+ </ir>
742
+ </coll>
743
+ ```
744
+
745
+ **Elementos clave del patron wizard:**
746
+ - `group-swipe="true"` habilita deslizar entre pasos
747
+ - `MAP_GROUP` rastrea el paso actual
748
+ - `MAP_TOTAL_PAGES` almacena el número total de pasos
749
+ - `disablevisible="MAP_GROUP=1"` oculta el botón "Atrás" en el primer paso
750
+ - `disablevisible="MAP_GROUP=MAP_TOTAL_PAGES"` oculta "Siguiente" en el último paso
751
+