@yz-social/civildefense.io 4.4.1

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 (77) hide show
  1. package/README.md +68 -0
  2. package/announce.js +24 -0
  3. package/docs/YZ-Brief.pdf +0 -0
  4. package/index.js +2 -0
  5. package/movie/camera.jpg +0 -0
  6. package/movie/movie.js +151 -0
  7. package/movie/script.js +272 -0
  8. package/movie/test.js +2 -0
  9. package/nginx/nginx.conf +83 -0
  10. package/nginx/yz.social +81 -0
  11. package/package.json +34 -0
  12. package/public/.well-known/appspecific/com.chrome.devtools.json +1 -0
  13. package/public/about/CivilDefense.mp4 +0 -0
  14. package/public/about/In case of Nazis, use CivilDefense.io.png +0 -0
  15. package/public/about/broadcast.png +0 -0
  16. package/public/about/civil-defense.png +0 -0
  17. package/public/about/conversation-and-image-high.png +0 -0
  18. package/public/about/conversation-and-image.png +0 -0
  19. package/public/about/en.html +157 -0
  20. package/public/about/es.html +142 -0
  21. package/public/about/flood-fire-ice-high.png +0 -0
  22. package/public/about/flood-fire-ice.png +0 -0
  23. package/public/about/hero-high.png +0 -0
  24. package/public/about/hero.png +0 -0
  25. package/public/about/index.html +8 -0
  26. package/public/about/nazis.png +0 -0
  27. package/public/about/script.js +69 -0
  28. package/public/about/streaming-radio-high.png +0 -0
  29. package/public/about/streaming-radio.png +0 -0
  30. package/public/about/style.css +306 -0
  31. package/public/control-safe-rectangle.html +40 -0
  32. package/public/favicon.ico +0 -0
  33. package/public/images/Achtung.png +0 -0
  34. package/public/images/YZ Owl.png +0 -0
  35. package/public/images/civil-defense-122.png +0 -0
  36. package/public/images/civil-defense-192.png +0 -0
  37. package/public/images/civil-defense-240.png +0 -0
  38. package/public/images/civil-defense-512.png +0 -0
  39. package/public/images/civil-defense.png +0 -0
  40. package/public/images/hero-small.png +0 -0
  41. package/public/images/qr-scan.svg +2 -0
  42. package/public/images/qr.png +0 -0
  43. package/public/images/qr.svg +155 -0
  44. package/public/images/recenter.svg +5 -0
  45. package/public/images/share.png +0 -0
  46. package/public/images/share.svg +2 -0
  47. package/public/index.html +170 -0
  48. package/public/javascripts/agent.js +324 -0
  49. package/public/javascripts/display.js +25 -0
  50. package/public/javascripts/hashtags.js +205 -0
  51. package/public/javascripts/main.js +394 -0
  52. package/public/javascripts/map.js +725 -0
  53. package/public/javascripts/p2pWebNetwork.js +245 -0
  54. package/public/javascripts/s2.js +77 -0
  55. package/public/javascripts/scripting.js +112 -0
  56. package/public/javascripts/service-manager.js +149 -0
  57. package/public/javascripts/translations.js +139 -0
  58. package/public/javascripts/versions.js +23 -0
  59. package/public/manifest.json +25 -0
  60. package/public/owl.ico +0 -0
  61. package/public/platformer.html +52 -0
  62. package/public/robots.txt +6 -0
  63. package/public/service-worker.js +218 -0
  64. package/public/stylesheets/style.css +442 -0
  65. package/routes/index.js +123 -0
  66. package/server/app.js +116 -0
  67. package/server/bridge.js +397 -0
  68. package/server/dirname.js +15 -0
  69. package/server/getLocation.js +18 -0
  70. package/server/identity.js +111 -0
  71. package/server/location.json +12 -0
  72. package/spec/axonSpec.gratuitousNameChangeForSignal +246 -0
  73. package/spec/axonSpec.js +280 -0
  74. package/spec/axonSpec.jsRemoveThePartAfterJS +252 -0
  75. package/spec/civildefenseSpec.js +61 -0
  76. package/spec/pubsubSpec.js +128 -0
  77. package/spec/support/jasmine.mjs +14 -0
package/package.json ADDED
@@ -0,0 +1,34 @@
1
+ {
2
+ "name": "@yz-social/civildefense.io",
3
+ "description": "Browser app to safely share live sightings on a map.",
4
+ "version": "4.4.1",
5
+ "keywords": [
6
+ "map",
7
+ "browser",
8
+ "alert"
9
+ ],
10
+ "scripts": {
11
+ "start": "npm stop; node ./server/app.js",
12
+ "stop": "pkill yz.social"
13
+ },
14
+ "type": "module",
15
+ "exports": {
16
+ ".": "./index.js"
17
+ },
18
+ "dependencies": {
19
+ "@axona/protocol": "github:axona-net/axona-protocol#semver:^4.18.2",
20
+ "cors": "^2.8.6",
21
+ "express": "^4.22.1",
22
+ "leaflet": "^1.9.4",
23
+ "minidenticons": "^4.2.1",
24
+ "morgan": "^1.10.1",
25
+ "node-datachannel": "^0.32.3",
26
+ "pica": "^9.0.1",
27
+ "s2js": "^1.44.0",
28
+ "uuid": "^14.0.0",
29
+ "yargs": "^18.0.0"
30
+ },
31
+ "overrides": {
32
+ "log4js": "^6.9.1"
33
+ }
34
+ }
Binary file
Binary file
Binary file
@@ -0,0 +1,157 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>The Civil Defense App</title>
7
+ <link rel="preconnect" href="https://fonts.googleapis.com">
8
+ <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
9
+ <link href="https://fonts.googleapis.com/css2?family=Barlow+Condensed:wght@400;600;700;800&family=Inter:wght@400;500;600&display=swap" rel="stylesheet">
10
+ <link rel="stylesheet" href="style.css" />
11
+ <script>var ariaSlideLabel = 'Slide ';</script>
12
+ </head>
13
+ <body>
14
+
15
+ <!-- ── HERO ──────────────────────────────────────────────────── -->
16
+ <header class="hero">
17
+ <div class="hero-inner">
18
+ <img class="hero-logo" src="civil-defense.png" alt="Civil Defense Logo">
19
+ <h1>The Civil Defense <span>App</span></h1>
20
+ <!-- <div class="hero-divider"></div> -->
21
+ <!-- <p class="hero-tagline">Realtime crowdmapping that is resistant to tracking, censorship, and takedown</p> -->
22
+ </div>
23
+ <div class="language-selector">
24
+ <label for="language">Language:</label>
25
+ <select id="language" name="language" onchange="location = this.value;">
26
+ <option value="en.html" selected>English</option>
27
+ <option value="es.html">Español</option>
28
+ </select>
29
+ </div>
30
+ </header>
31
+
32
+ <!-- ── CAROUSEL ──────────────────────────────────────────────── -->
33
+ <section class="carousel-section" aria-label="App screenshots">
34
+
35
+ <div class="carousel-wrap">
36
+ <div class="carousel-track-outer">
37
+ <div class="carousel-track" id="carouselTrack">
38
+
39
+ <div class="carousel-slide">
40
+ <img src="broadcast.png" alt="A mobile app showing alerts">
41
+ <div class="carousel-caption">Realtime crowdmapping that is resistant to tracking, censorship, and takedown.</div>
42
+ </div>
43
+
44
+ <div class="carousel-slide">
45
+ <img src="flood-fire-ice.png" alt="Map view showing alert markers scattered across a neighborhood">
46
+ <div class="carousel-caption">Like Twitter, you create the topics.<br>But you only get alerts based on your current map.<br>Like Snapchat, alerts fade away after 24 hours.</div>
47
+ </div>
48
+
49
+ <div class="carousel-slide">
50
+ <img src="conversation-and-image.png" alt="Map with an open alert popup showing a photo and comment thread">
51
+ <div class="carousel-caption">Click a marker to read and add comments and attachments.</div>
52
+ </div>
53
+
54
+ <div class="carousel-slide">
55
+ <img src="streaming-radio.png" alt="North America map with playable streaming radio station markers">
56
+ <div class="carousel-caption">People control the topics, and they do not have to be about safety. Here are playable streaming radio stations by category.</div>
57
+ </div>
58
+
59
+ <div class="carousel-slide">
60
+ <video controls src="CivilDefense.mp4"></video>
61
+ <div class="carousel-caption">A 30 second AI vibe video overview.</div>
62
+ </div>
63
+
64
+ <!-- <div class="carousel-slide"> -->
65
+ <!-- <img src="hero.png" alt="One man with shoping bags in Tiananmen Square, facing an enormous column of tanks."> -->
66
+ <!-- <div class="carousel-caption">Bear witness.</div> -->
67
+ <!-- </div> -->
68
+
69
+ <!-- <div class="carousel-slide"> -->
70
+ <!-- <img src="nazis.png" alt="WWII-style poster exhorting the public to use Civil Defense in case of Nazi attack."> -->
71
+ <!-- </div> -->
72
+
73
+ </div>
74
+ </div>
75
+
76
+ <button class="carousel-btn prev" id="prevBtn" aria-label="Previous slide">&#8249;</button>
77
+ <button class="carousel-btn next" id="nextBtn" aria-label="Next slide">&#8250;</button>
78
+
79
+ <div class="carousel-dots" id="carouselDots" role="tablist" aria-label="Carousel position"></div>
80
+ </div>
81
+ </section>
82
+
83
+ <!-- ── CONTENT ───────────────────────────────────────────────── -->
84
+ <main class="content-wrap">
85
+
86
+ <h1>What You Do With It</h1>
87
+
88
+ <ul>
89
+ <li>Warn others of danger by tapping the map.</li>
90
+ <li>Add comments, photos, video, or other attachments.</li>
91
+ <li>Get notified of new alerts near you or on your current map.</li>
92
+ <li>Share to social media, text, or email.</li>
93
+ <li>Discuss an alert by secure video conference. (Coming this Fall.)</li>
94
+ <!-- <li>Alerts disappear after 24 hours—this is not an archive or long-term storage system. (This Winter, users will be able to download tamper-evident reports for use in other systems.)</li> -->
95
+ </ul>
96
+
97
+ <a href="/?lang=en" class="launch-btn" target="civildefense">
98
+ <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5"><polygon points="5 3 19 12 5 21 5 3"></polygon></svg>
99
+ Try it!
100
+ </a>
101
+
102
+ <!-- <hr class="rule"> -->
103
+
104
+ <h1>Broadcast Safely</h1>
105
+
106
+ <h2>Resist Tracking</h2>
107
+ <ul>
108
+ <li>You cannot be identified as a person because there is no login, sign-up, or accounts. No phone number or email is used. There is no app store logging downloads. Users can choose to let others recognize them over multiple interactions — without divulging their actual identity.</li>
109
+ <li>It is extremely difficult to identify your IP address or network patterns, because alerts are distributed phone-to-phone over a peer-to-peer network.</li>
110
+ <li>Notifications are sent directly through the foreground app on your phone, without going through Apple or Google's messaging service. (Backgrounded app notifications available this Winter.)</li>
111
+ <li>Each alert disappears everywhere on the network after 24 hours. There are no collected records of messages, comments, or requested topics, and this is not an archive or long-term storage system.</li>
112
+ </ul>
113
+
114
+ <h2>Resist Censorship</h2>
115
+ <ul>
116
+ <li>No user can be censored because there are no accounts. No one can steal your account information because there isn't any.</li>
117
+ <li>No content can be censored, there is no central organization who controls the network or has power to alter content.</li>
118
+ </ul>
119
+
120
+ <h2>Resist Takedown</h2>
121
+ <ul>
122
+ <li>No app store can block Civil Defense — the app is just a web page.</li>
123
+ <li>Taking down web servers or domain names cannot block access for those who already have it, because the app caches everything it needs on first use. The <a href="https://github.com/yz-social/civildefense.io" target="yz.other" rel="noopener">open-source code</a> is available so anyone can host an alternative mirror.</li>
124
+ <li>There is no back-end server or data center to take down. All alert data is entirely peer-to-peer. All mirrors share this network: a post on <a href="https://civildefense.io" target="civildefense">civildefense.io</a> appears on the mirror at <a href="https://ki1r0y.com" target="ki1r0y" rel="noopener">ki1r0y.com</a> and vice versa.</li>
125
+ <li>Blocking network traffic is extremely difficult, because the low-level data is identical to video conferencing traffic.</li>
126
+ <li>Civil Defense is highly resistant to Denial of Service attacks. However, it does require a working Internet, and does not by itself offer any protection against a complete Internet shutdown.</li>
127
+ </ul>
128
+ <!-- <hr class="rule"> -->
129
+
130
+ <h1>The Bigger Picture</h1>
131
+ <p>The Civil Defense software was created by a group of experienced system developers, running on the <a href="https://github.com/axona-net/axona-protocol" target="yz.other" rel="noopener">Axona network</a>. Both are free and open source.</p>
132
+ <p>Axona is a modern descendant of the old <a href="https://en.wikipedia.org/wiki/Peer-to-peer" target="yz.other" rel="noopener">peer-to-peer networks</a> such as <a href="https://en.wikipedia.org/wiki/Napster">Napster</a>. It has a unique self-healing routing mechanism that uses neuron learning techniques to find the best way to distribute alerts. It is analogous to <a href="https://en.wikipedia.org/wiki/Tor_(network)" target="yz.other" rel="noopener">TOR</a> (The Onion Router), but designed from the ground up to run in mobile and desktop browsers.</p>
133
+ <p>We are also developing a comprehensive quantum-ready cryptography system that will add private-groups for Civil Defense and any software on the network. We believe there are many applications where developers, users, and the public all benefit from open software and secure, decentralized networks — without app stores, servers, or data centers. Beyond Civil Defense, we are also developing large-scale <a href="https://en.wikipedia.org/wiki/Distributed_computing" target="yz.other" rel="noopener">multiuser collaboration</a> and <a href="https://poverty-action.org/inclusive-instant-payment-systems-evidence-based-approach-design-impact" target="yz.other" rel="noopener">Inclusive Instant Payment Systems</a>.</p>
134
+
135
+ </main>
136
+
137
+ <div class="centered">
138
+ <img src="nazis.png" alt="WWII-style poster exhorting the public to use Civil Defense in case of Nazi attack.">
139
+ </div>
140
+
141
+ <!-- ── FOOTER ─────────────────────────────────────────────────── -->
142
+ <footer>
143
+ <p><a href="/?lang=en" target="civildefense">civildefense.io</a> &mdash; Safe and free crowdmapping for vulnerable populations.<br>
144
+ We gratefully use open software from:
145
+ <a href="https://leafletjs.com/" target="yz.other" rel="noopener">Leaflet</a>,
146
+ <a href="https://www.openstreetmap.org/" target="yz.other" rel="noopener">OpenStreetMap</a>,
147
+ <a href="http://s2geometry.io" target="yz.other" rel="noopener">S2Geometry</a>,
148
+ <a href="https://github.com/kozakdenys/qr-code-styling" target="yz.other" rel="noopener">qr-code-styling</a>,
149
+ <a href="https://expressjs.com/" target="yz.other" rel="noopener">Express.js</a>,
150
+ <a href="https://nodejs.org/" target="yz.other" rel="noopener">Node.js</a></p>
151
+ </footer>
152
+
153
+ <!-- ── CAROUSEL SCRIPT ───────────────────────────────────────── -->
154
+ <script src="script.js"></script>
155
+
156
+ </body>
157
+ </html>
@@ -0,0 +1,142 @@
1
+ <!DOCTYPE html>
2
+ <html lang="es">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>La Aplicación de Defensa Civil</title>
7
+ <link rel="preconnect" href="https://fonts.googleapis.com">
8
+ <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
9
+ <link href="https://fonts.googleapis.com/css2?family=Barlow+Condensed:wght@400;600;700;800&family=Inter:wght@400;500;600&display=swap" rel="stylesheet">
10
+ <link rel="stylesheet" href="style.css" />
11
+ <script>var ariaSlideLabel = 'Diapositiva ';</script>
12
+ </head>
13
+ <body>
14
+
15
+ <!-- ── HERO ──────────────────────────────────────────────────── -->
16
+ <header class="hero">
17
+ <div class="hero-inner">
18
+ <img class="hero-logo" src="civil-defense.png" alt="Logo de Defensa Civil">
19
+ <h1>La Aplicación de <span>Defensa Civil</span></h1>
20
+ </div>
21
+ <div class="language-selector">
22
+ <label for="language">Idioma:</label>
23
+ <select id="language" name="language" onchange="location = this.value;">
24
+ <option value="en.html">English</option>
25
+ <option value="es.html" selected>Español</option>
26
+ </select>
27
+ </div>
28
+ </header>
29
+
30
+ <!-- ── CAROUSEL ──────────────────────────────────────────────── -->
31
+ <section class="carousel-section" aria-label="Capturas de pantalla de la aplicación">
32
+
33
+ <div class="carousel-wrap">
34
+ <div class="carousel-track-outer">
35
+ <div class="carousel-track" id="carouselTrack">
36
+
37
+ <div class="carousel-slide">
38
+ <img src="broadcast.png" alt="Una aplicación móvil mostrando alertas">
39
+ <div class="carousel-caption">Mapeo colaborativo en tiempo real resistente al rastreo, la censura y el bloqueo.</div>
40
+ </div>
41
+
42
+ <div class="carousel-slide">
43
+ <img src="flood-fire-ice.png" alt="Vista del mapa con marcadores de alerta dispersos por un vecindario">
44
+ <div class="carousel-caption">Como en Twitter, tú creas los temas.<br>Pero solo recibes alertas según tu mapa actual.<br>Como en Snapchat, las alertas desaparecen después de 24 horas.</div>
45
+ </div>
46
+
47
+ <div class="carousel-slide">
48
+ <img src="conversation-and-image.png" alt="Mapa con una ventana emergente de alerta abierta mostrando una foto y comentarios">
49
+ <div class="carousel-caption">Haz clic en un marcador para leer y añadir comentarios y archivos adjuntos.</div>
50
+ </div>
51
+
52
+ <div class="carousel-slide">
53
+ <img src="streaming-radio.png" alt="Mapa de Norteamérica con marcadores de estaciones de radio en streaming reproducibles">
54
+ <div class="carousel-caption">Las personas controlan los temas, y no tienen que ser sobre seguridad. Aquí hay estaciones de radio en streaming reproducibles por categoría.</div>
55
+ </div>
56
+
57
+ <div class="carousel-slide">
58
+ <video controls src="CivilDefense.mp4"></video>
59
+ <div class="carousel-caption">Un video de presentación de 30 segundos generado con IA. (En Inglés.)</div>
60
+ </div>
61
+
62
+ </div>
63
+ </div>
64
+
65
+ <button class="carousel-btn prev" id="prevBtn" aria-label="Diapositiva anterior">&#8249;</button>
66
+ <button class="carousel-btn next" id="nextBtn" aria-label="Diapositiva siguiente">&#8250;</button>
67
+
68
+ <div class="carousel-dots" id="carouselDots" role="tablist" aria-label="Posición del carrusel"></div>
69
+ </div>
70
+ </section>
71
+
72
+ <!-- ── CONTENT ───────────────────────────────────────────────── -->
73
+ <main class="content-wrap">
74
+
75
+ <h1>Qué Puedes Hacer Con Ella</h1>
76
+
77
+ <ul>
78
+ <li>Advertir a otros sobre peligros tocando el mapa.</li>
79
+ <li>Añadir comentarios, fotos, videos u otros archivos adjuntos.</li>
80
+ <li>Recibir notificaciones de nuevas alertas cerca de ti o en tu mapa actual.</li>
81
+ <li>Compartir en redes sociales, por mensaje de texto o correo electrónico.</li>
82
+ <li>Discutir una alerta mediante videoconferencia segura. (Disponible este otoño.)</li>
83
+ </ul>
84
+
85
+ <a href="/?lang=es" class="launch-btn" target="civildefense">
86
+ <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5"><polygon points="5 3 19 12 5 21 5 3"></polygon></svg>
87
+ ¡Pruébala!
88
+ </a>
89
+
90
+ <h1>Transmite con Seguridad</h1>
91
+
92
+ <h2>Resiste el Rastreo</h2>
93
+ <ul>
94
+ <li>No puedes ser identificado como persona porque no hay inicio de sesión, registro ni cuentas. No se utiliza número de teléfono ni correo electrónico. No hay tienda de aplicaciones que registre las descargas. Los usuarios pueden optar por permitir que otros los reconozcan en múltiples interacciones, sin revelar su identidad real.</li>
95
+ <li>Es extremadamente difícil identificar tu dirección IP o los patrones de red, porque las alertas se distribuyen de teléfono a teléfono a través de una red entre pares.</li>
96
+ <li>Las notificaciones se envían directamente a través de la aplicación en primer plano en tu teléfono, sin pasar por el servicio de mensajería de Apple o Google. (Las notificaciones con la aplicación en segundo plano estarán disponibles este invierno.)</li>
97
+ <li>Cada alerta desaparece de toda la red después de 24 horas. No se recopilan registros de mensajes, comentarios ni temas solicitados, y este no es un sistema de archivo ni de almacenamiento a largo plazo.</li>
98
+ </ul>
99
+
100
+ <h2>Resiste la Censura</h2>
101
+ <ul>
102
+ <li>Ningún usuario puede ser censurado porque no hay cuentas. Nadie puede robar la información de tu cuenta porque no existe.</li>
103
+ <li>Ningún contenido puede ser censurado; no hay una organización central que controle la red o tenga poder para modificar el contenido.</li>
104
+ </ul>
105
+
106
+ <h2>Resiste el Bloqueo</h2>
107
+ <ul>
108
+ <li>Ninguna tienda de aplicaciones puede bloquear Defensa Civil: la aplicación es simplemente una página web.</li>
109
+ <li>Bloquear servidores web o nombres de dominio no puede impedir el acceso a quienes ya la tienen, porque la aplicación guarda en caché todo lo que necesita en el primer uso. El <a href="https://github.com/yz-social/civildefense.io" target="yz.other" rel="noopener">código fuente abierto</a> está disponible para que cualquiera pueda alojar un espejo alternativo.</li>
110
+ <li>No hay servidor de back-end ni centro de datos que bloquear. Todos los datos de alertas son completamente entre pares. Todos los espejos comparten esta red: una publicación en <a href="https://civildefense.io" target="civildefense">civildefense.io</a> aparece en el espejo de <a href="https://ki1r0y.com" target="ki1r0y" rel="noopener">ki1r0y.com</a> y viceversa.</li>
111
+ <li>Bloquear el tráfico de red es extremadamente difícil porque los datos de bajo nivel son idénticos al tráfico de videoconferencia.</li>
112
+ <li>Defensa Civil es muy resistente a los ataques de denegación de servicio. Sin embargo, requiere una conexión a Internet funcional y, por sí sola, no ofrece protección contra un corte completo de Internet.</li>
113
+ </ul>
114
+
115
+ <h1>El Panorama General</h1>
116
+ <p>El software de Defensa Civil fue creado por un grupo de desarrolladores de sistemas experimentados, que se ejecuta en el <a href="https://github.com/axona-net/axona-protocol" target="yz.other" rel="noopener">red Axona</a>. Ambos son gratuitos y de código abierto.</p>
117
+ <p>Axona es un descendiente moderno de las antiguas <a href="https://en.wikipedia.org/wiki/Peer-to-peer" target="yz.other" rel="noopener">redes entre pares</a> como <a href="https://en.wikipedia.org/wiki/Napster">Napster</a>. Tiene un mecanismo único de enrutamiento autocurativo que utiliza técnicas de aprendizaje neuronal para encontrar la mejor manera de distribuir alertas. Es análogo a <a href="https://en.wikipedia.org/wiki/Tor_(network)" target="yz.other" rel="noopener">TOR</a> (The Onion Router), pero diseñado desde cero para funcionar en navegadores móviles y de escritorio.</p>
118
+ <p>También estamos desarrollando un sistema integral de criptografía preparado para la era cuántica que añadirá grupos privados para Defensa Civil y cualquier software en la red. Creemos que hay muchas aplicaciones en las que los desarrolladores, los usuarios y el público en general se benefician del software abierto y las redes seguras y descentralizadas, sin tiendas de aplicaciones, servidores ni centros de datos. Más allá de Defensa Civil, también estamos desarrollando <a href="https://en.wikipedia.org/wiki/Distributed_computing" target="yz.other" rel="noopener">colaboración multiusuario a gran escala</a> y <a href="https://poverty-action.org/inclusive-instant-payment-systems-evidence-based-approach-design-impact" target="yz.other" rel="noopener">Sistemas de Pago Instantáneo Inclusivos</a>.</p>
119
+
120
+ </main>
121
+
122
+ <div class="centered">
123
+ <img src="nazis.png" alt="Póster de estilo de la Segunda Guerra Mundial que exhorta al público a usar Defensa Civil en caso de ataque nazi.">
124
+ </div>
125
+
126
+ <!-- ── FOOTER ─────────────────────────────────────────────────── -->
127
+ <footer>
128
+ <p><a href="/?lang=es" target="civildefense">civildefense.io</a> &mdash; Mapeo colaborativo seguro y gratuito para poblaciones vulnerables.<br>
129
+ Usamos con gratitud software abierto de:
130
+ <a href="https://leafletjs.com/" target="yz.other" rel="noopener">Leaflet</a>,
131
+ <a href="https://www.openstreetmap.org/" target="yz.other" rel="noopener">OpenStreetMap</a>,
132
+ <a href="http://s2geometry.io" target="yz.other" rel="noopener">S2Geometry</a>,
133
+ <a href="https://github.com/kozakdenys/qr-code-styling" target="yz.other" rel="noopener">qr-code-styling</a>,
134
+ <a href="https://expressjs.com/" target="yz.other" rel="noopener">Express.js</a>,
135
+ <a href="https://nodejs.org/" target="yz.other" rel="noopener">Node.js</a></p>
136
+ </footer>
137
+
138
+ <!-- ── CAROUSEL SCRIPT ───────────────────────────────────────── -->
139
+ <script src="script.js"></script>
140
+
141
+ </body>
142
+ </html>
Binary file
Binary file
Binary file
@@ -0,0 +1,8 @@
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <script>
5
+ location = `${navigator.language.split('-')[0].toLowerCase()}.html`;
6
+ </script>
7
+ </head>
8
+ </html>
Binary file
@@ -0,0 +1,69 @@
1
+ const track = document.getElementById('carouselTrack');
2
+ const slides = track.querySelectorAll('.carousel-slide');
3
+ const dotsWrap = document.getElementById('carouselDots');
4
+ const prevBtn = document.getElementById('prevBtn');
5
+ const nextBtn = document.getElementById('nextBtn');
6
+ let current = 0;
7
+ let autoTimer;
8
+
9
+ // Build dots
10
+ slides.forEach((_, i) => {
11
+ const btn = document.createElement('button');
12
+ btn.className = 'dot' + (i === 0 ? ' active' : '');
13
+ btn.setAttribute('role', 'tab');
14
+ btn.setAttribute('aria-label', ariaSlideLabel + (i + 1));
15
+ btn.addEventListener('click', () => goTo(i));
16
+ dotsWrap.appendChild(btn);
17
+ });
18
+
19
+ function goTo(idx) {
20
+ current = (idx + slides.length) % slides.length;
21
+ track.style.transform = `translateX(-${current * 100}%)`;
22
+ dotsWrap.querySelectorAll('.dot').forEach((d, i) => {
23
+ d.classList.toggle('active', i === current);
24
+ });
25
+ resetAuto();
26
+ }
27
+
28
+ prevBtn.addEventListener('click', () => goTo(current - 1));
29
+ nextBtn.addEventListener('click', () => goTo(current + 1));
30
+
31
+ // Touch / swipe
32
+ let startX = null;
33
+ track.addEventListener('touchstart', e => { startX = e.touches[0].clientX; }, { passive: true });
34
+ track.addEventListener('touchend', e => {
35
+ if (startX === null) return;
36
+ const dx = e.changedTouches[0].clientX - startX;
37
+ if (Math.abs(dx) > 40) goTo(dx < 0 ? current + 1 : current - 1);
38
+ startX = null;
39
+ }, { passive: true });
40
+
41
+ // Pause on hover/focus
42
+ const wrap = document.querySelector('.carousel-track-outer');
43
+ wrap.addEventListener('mouseenter', stopAuto);
44
+ wrap.addEventListener('mouseleave', resetAuto);
45
+
46
+ // Auto-advance every few seconds
47
+ function advance() {
48
+ goTo(current + 1);
49
+ }
50
+ function resetAuto() {
51
+ if (navigator.userAgentData?.mobile || navigator.maxTouchPoints > 0 || window.matchMedia("(max-width: 768px)").matches) return; // Do not auto-advance on mobile.
52
+ const video = document.querySelector('video');
53
+ if (video.currentTime > 0 && !video.paused && !video.ended && video.readyState > 2) return;
54
+ if (wrap.matches(':hover')) return;
55
+ clearInterval(autoTimer);
56
+ autoTimer = setInterval(advance, 10e3);
57
+ }
58
+ function stopAuto() {
59
+ clearInterval(autoTimer);
60
+ }
61
+ resetAuto();
62
+ document.querySelectorAll('video').forEach(video => {
63
+ video.addEventListener('play', stopAuto);
64
+ video.addEventListener('ended', () => {
65
+ advance();
66
+ resetAuto();
67
+ });
68
+ });
69
+
Binary file