L. Carpeta « js »

Versión para imprimir.

A. js / configura.js

1import {
2 registraServiceWorkerSiEsSoportado
3} from "../lib/js/registraServiceWorkerSiEsSoportado.js"
4
5registraServiceWorkerSiEsSoportado("sw.js")

B. Carpeta « js / bd »

Versión para imprimir.

1. js / bd / Bd.js

1export const NOMBRE_DEL_ALMACEN_PASATIEMPO = "Pasatiempo"
2export const NOMBRE_DEL_INDICE_NOMBRE = "nombre"
3const BD_NOMBRE = "sincronizacion"
4const BD_VERSION = 1
5
6/** @type { Promise<IDBDatabase> } */
7export const Bd = new Promise((resolve, reject) => {
8
9 /* Se solicita abrir la base de datos, indicando nombre y
10 * número de versión. */
11 const solicitud = indexedDB.open(BD_NOMBRE, BD_VERSION)
12
13 // Si se presenta un error, rechaza la promesa.
14 solicitud.onerror = () => reject(solicitud.error)
15
16 // Si se abre con éxito, devuelve una conexión a la base de datos.
17 solicitud.onsuccess = () => resolve(solicitud.result)
18
19 // Si es necesario, se inicia una transacción para cambio de versión.
20 solicitud.onupgradeneeded = () => {
21
22 const bd = solicitud.result
23
24 // Como hay cambio de versión, borra el almacén si es que existe.
25 if (bd.objectStoreNames.contains(NOMBRE_DEL_ALMACEN_PASATIEMPO)) {
26 bd.deleteObjectStore(NOMBRE_DEL_ALMACEN_PASATIEMPO)
27 }
28
29 // Crea el almacén "Pasatiempo" con el campo llave "uuid".
30 const almacenPasatiempo =
31 bd.createObjectStore(NOMBRE_DEL_ALMACEN_PASATIEMPO, { keyPath: "uuid" })
32
33 // Crea un índice ordenado por el campo "nombre" que no acepta duplicados.
34 almacenPasatiempo.createIndex(NOMBRE_DEL_INDICE_NOMBRE, "nombre")
35 }
36
37})

2. js / bd / pasatiempoAgrega.js

1import { bdEjecuta } from "../../lib/js/bdEjecuta.js"
2import { Pasatiempo } from "../modelo/Pasatiempo.js"
3import { Bd, NOMBRE_DEL_ALMACEN_PASATIEMPO } from "./Bd.js"
4
5let secuencia = 0
6
7/**
8 * @param { Pasatiempo } modelo
9 */
10export async function pasatiempoAgrega(modelo) {
11 modelo.validaNuevo()
12 modelo.modificacion = Date.now()
13 modelo.eliminado = false
14
15 // Genera uuid único en internet.
16 modelo.uuid = Date.now().toString() + Math.random() + secuencia
17 secuencia++
18
19 return bdEjecuta(Bd, [NOMBRE_DEL_ALMACEN_PASATIEMPO],
20 transaccion => {
21 const almacenPasatiempo =
22 transaccion.objectStore(NOMBRE_DEL_ALMACEN_PASATIEMPO)
23 almacenPasatiempo.add(modelo)
24 })
25}
26
27// Permite que los eventos de html usen la función.
28window["pasatiempoAgrega"] = pasatiempoAgrega

3. js / bd / pasatiempoBusca.js

1import { bdConsulta } from "../../lib/js/bdConsulta.js"
2import { recuperaPasatiempo } from "../modelo/recuperaPasatiempo.js"
3import { Pasatiempo } from "../modelo/Pasatiempo.js"
4import { Bd, NOMBRE_DEL_ALMACEN_PASATIEMPO } from "./Bd.js"
5
6/**
7 * @param { string | null } uuid
8 */
9export async function pasatiempoBusca(uuid) {
10
11 if (uuid === null)
12 throw new Error("Falta el uuid")
13
14 return bdConsulta(Bd, [NOMBRE_DEL_ALMACEN_PASATIEMPO],
15 /**
16 * @param { IDBTransaction } transaccion
17 * @param { (resultado: Pasatiempo|undefined) => any } resolve
18 */
19 (transaccion, resolve) => {
20
21 /* Pide el primer registro de almacén pasatiempo que tenga como
22 * llave primaria el valor del parámetro uuid. */
23 const consulta =
24 transaccion.objectStore(NOMBRE_DEL_ALMACEN_PASATIEMPO).get(uuid)
25
26 /* onsuccess se invoca solo una vez, devolviendo el registro
27 * solicitado. */
28 consulta.onsuccess = () => {
29
30 /* Se recupera el registro solicitado usando
31 * consulta.result
32 * Si el registro no se encuentra se recupera undefined. */
33 const objeto = consulta.result
34
35 if (objeto !== undefined) {
36 const modelo = recuperaPasatiempo(objeto)
37 if (!modelo.eliminado) {
38 resolve(modelo)
39 }
40 }
41
42 resolve(undefined)
43
44 }
45
46 })
47
48}
49
50// Permite que los eventos de html usen la función.
51window["pasatiempoBusca"] = pasatiempoBusca

4. js / bd / pasatiempoConsultaNoEliminados.js

1import { bdConsulta } from "../../lib/js/bdConsulta.js"
2import {
3 Bd,
4 NOMBRE_DEL_ALMACEN_PASATIEMPO, NOMBRE_DEL_INDICE_NOMBRE
5} from "./Bd.js"
6
7export async function pasatiempoConsultaNoEliminados() {
8
9 return bdConsulta(Bd, [NOMBRE_DEL_ALMACEN_PASATIEMPO],
10 /**
11 * @param { IDBTransaction } transaccion
12 * @param { (resultado: any[]) => any } resolve
13 */
14 (transaccion, resolve) => {
15
16 const resultado = []
17
18 const almacenPasatiempo =
19 transaccion.objectStore(NOMBRE_DEL_ALMACEN_PASATIEMPO)
20
21 // Usa el índice "nombre" para reculeprar los datos ordenadors.
22 const indiceNombre = almacenPasatiempo.index(NOMBRE_DEL_INDICE_NOMBRE)
23
24 /* Pide un cursor para recorrer cada uno de los registristros
25 * que devuelve la consulta. */
26 const consulta = indiceNombre.openCursor()
27
28 /* onsuccess se invoca por cada uno de los registros de la
29 * consulta y una vez cuando se acaban dichos registros. */
30 consulta.onsuccess = () => {
31
32 /* El cursor que corresponde al registro se recupera usando
33 * consulta.result */
34 const cursor = consulta.result
35
36 if (cursor === null) {
37
38 /* Si el cursor es igual a null, ya no hay más registros que
39 * procesar; por lo mismo, se devuelve el resultado con los
40 * pasatiempos recuperados, usando
41 * resolve(resultado). */
42 resolve(resultado)
43
44 } else {
45
46 /* Si el cursor es diferente a null, si hay más registros.
47 * El registro que sigue se obtiene con
48 * cursor.value */
49 const modelo = cursor.value
50 if (!modelo.eliminado) {
51 resultado.push(modelo)
52 }
53
54 /* Busca el siguiente registro de la consulta, que se obtiene
55 * la siguiente vez que se invoque la función
56 * onsuccess. */
57 cursor.continue()
58
59 }
60
61 }
62
63 })
64
65}
66
67// Permite que los eventos de html usen la función.
68window["pasatiempoConsultaNoEliminados"] = pasatiempoConsultaNoEliminados

5. js / bd / pasatiempoConsultaTodos.js

1import { bdConsulta } from "../../lib/js/bdConsulta.js"
2import { NOMBRE_DEL_ALMACEN_PASATIEMPO, Bd } from "./Bd.js"
3
4/**
5 * Lista todos los objetos, incluyendo los que tienen
6 * borrado lógico.
7 */
8export async function pasatiempoConsultaTodos() {
9
10 return bdConsulta(Bd, [NOMBRE_DEL_ALMACEN_PASATIEMPO],
11 /**
12 * @param { IDBTransaction } transaccion
13 * @param { (result: any[]) => any } resolve
14 */
15 (transaccion, resolve) => {
16
17 const resultado = []
18
19 /* Pide un cursor para recorrer cada uno de los
20 * registristros que devuelve la consulta. */
21 const consulta =
22 transaccion.objectStore(NOMBRE_DEL_ALMACEN_PASATIEMPO).openCursor()
23
24 /* onsuccess se invoca por cada uno de los registros de la
25 * consulta y una vez cuando se acaban dichos registros. */
26 consulta.onsuccess = () => {
27
28 /* El cursor que corresponde al registro se recupera usando
29 * consulta.result */
30 const cursor = consulta.result
31
32 if (cursor === null) {
33
34 /* Si el cursor es igual a null, ya no hay más registros que
35 * procesar; por lo mismo, se devuelve el resultado con los
36 * pasatiempos recuperados, usando
37 * resolve(resultado). */
38 resolve(resultado)
39
40 } else {
41
42 /* Si el cursor es diferente a null, si hay más registros.
43 * El registro que sigue se obtiene con
44 * cursor.value*/
45 resultado.push(cursor.value)
46
47 /* Busca el siguiente registro de la consulta, que se obtiene
48 * la siguiente vez que se invoque la función
49 * onsuccess. */
50 cursor.continue()
51
52 }
53
54 }
55
56 })
57
58}

6. js / bd / pasatiempoElimina.js

1import { bdEjecuta } from "../../lib/js/bdEjecuta.js"
2import { NOMBRE_DEL_ALMACEN_PASATIEMPO, Bd } from "./Bd.js"
3import { pasatiempoBusca } from "./pasatiempoBusca.js"
4
5/**
6 * @param { string } uuid
7 */
8export async function pasatiempoElimina(uuid) {
9 const modelo = await pasatiempoBusca(uuid)
10 if (modelo !== undefined) {
11 modelo.modificacion = Date.now()
12 modelo.eliminado = true
13 return bdEjecuta(Bd, [NOMBRE_DEL_ALMACEN_PASATIEMPO],
14 transaccion => {
15 const almacenPasatiempo =
16 transaccion.objectStore(NOMBRE_DEL_ALMACEN_PASATIEMPO)
17 almacenPasatiempo.put(modelo)
18 })
19 }
20}
21
22// Permite que los eventos de html usen la función.
23window["pasatiempoElimina"] = pasatiempoElimina

7. js / bd / pasatiempoModifica.js

1import { bdEjecuta } from "../../lib/js/bdEjecuta.js"
2import { Pasatiempo } from "../modelo/Pasatiempo.js"
3import { Bd, NOMBRE_DEL_ALMACEN_PASATIEMPO } from "./Bd.js"
4import { pasatiempoBusca } from "./pasatiempoBusca.js"
5
6/**
7 * @param { Pasatiempo } modelo
8 */
9export async function pasatiempoModifica(modelo) {
10 modelo.valida()
11 const anterior = await pasatiempoBusca(modelo.uuid)
12 if (anterior === undefined) {
13 return undefined
14 } else {
15 modelo.modificacion = Date.now()
16 modelo.eliminado = false
17 return bdEjecuta(Bd, [NOMBRE_DEL_ALMACEN_PASATIEMPO],
18 transaccion => {
19 const almacenPasatiempo =
20 transaccion.objectStore(NOMBRE_DEL_ALMACEN_PASATIEMPO)
21 almacenPasatiempo.put(modelo)
22 })
23 }
24}
25
26// Permite que los eventos de html usen la función.
27window["pasatiempoModifica"] = pasatiempoModifica

8. js / bd / pasatiemposReemplaza.js

1import { bdEjecuta } from "../../lib/js/bdEjecuta.js"
2import { Bd, NOMBRE_DEL_ALMACEN_PASATIEMPO } from "./Bd.js"
3
4/**
5 * Borra el contenido del almacén pasatiempo y guarda el
6 * contenido del listado.
7 * @param {any[]} datosNuevos
8 */
9export async function pasatiemposReemplaza(datosNuevos) {
10 return bdEjecuta(Bd, [NOMBRE_DEL_ALMACEN_PASATIEMPO],
11 transaccion => {
12 const almacenPasatiempo =
13 transaccion.objectStore(NOMBRE_DEL_ALMACEN_PASATIEMPO)
14 almacenPasatiempo.clear()
15 for (const objeto of datosNuevos) {
16 almacenPasatiempo.add(objeto)
17 }
18 })
19}

C. Carpeta « js / modelo »

Versión para imprimir.

1. js / modelo / Pasatiempo.js

1export class Pasatiempo {
2
3 /**
4 * @param { {
5 * uuid?: string,
6 * nombre?: string,
7 * modificacion?: number,
8 * eliminado?: boolean,
9 * } } modelo
10 */
11 constructor(modelo) {
12 this.nombre = modelo.nombre === undefined
13 ? ""
14 : modelo.nombre
15 this.uuid = modelo.uuid === undefined
16 ? ""
17 : modelo.uuid
18 this.modificacion = modelo.modificacion === undefined
19 ? NaN
20 : modelo.modificacion
21 this.eliminado = modelo.eliminado === undefined
22 ? true
23 : modelo.eliminado
24 }
25
26 validaNuevo() {
27 if (this.nombre === "")
28 throw new Error("Falta el nombre.")
29 }
30
31 valida() {
32 if (this.uuid === "")
33 throw new Error("Falta el uuid.")
34 if (this.nombre === "")
35 throw new Error("Falta el nombre.")
36 }
37
38}
39
40// Permite que los eventos de html usen la clase.
41window["Pasatiempo"] = Pasatiempo

2. js / modelo / recuperaPasatiempo.js

1import { Pasatiempo } from "./Pasatiempo.js"
2import {
3 validaPasatiempo
4} from "./validaPasatiempo.js"
5
6/** @param { any } objeto */
7export function recuperaPasatiempo(objeto) {
8 const validado = validaPasatiempo(objeto)
9 return new Pasatiempo({
10 uuid: validado.uuid,
11 nombre: validado.nombre,
12 modificacion: validado.modificacion,
13 eliminado: validado.eliminado,
14 })
15}

3. js / modelo / validaPasatiempo.js

1/**
2 * @param { any } objeto
3 * @returns { {
4 * uuid: string,
5 * nombre: string,
6 * modificacion: number,
7 * eliminado: boolean,
8 * } }
9 */
10export function validaPasatiempo(objeto) {
11
12 if (typeof objeto.uuid !== "string")
13 throw new Error("El uuid debe ser texto.")
14
15 if (typeof objeto.nombre !== "string")
16 throw new Error("El nombre debe ser texto.")
17
18 if (typeof objeto.modificacion !== "number")
19 throw new Error("El campo modificacion debe ser número.")
20
21 if (typeof objeto.eliminado !== "boolean")
22 throw new Error("El campo eliminado debe ser booleano.")
23
24 return objeto
25
26}