]> gitweb.michael.orlitzky.com - charm-bypass.git/blob - index.html.in
index.html.in: support random and user-specified security codes
[charm-bypass.git] / index.html.in
1 <!doctype html>
2 <html lang="en-US">
3 <head>
4 <meta name="viewport" content="width=device-width, initial-scale=1" />
5
6 <title>
7 CharmBypass.
8 </title>
9
10 <style>
11 /*
12 * Reset styles for the html and body elements, the only two HTML
13 * elements we use. */
14 html, body {
15 margin: 0;
16 padding: 0;
17 border: 0;
18 font-weight: normal;
19 font-style: inherit;
20 font-size: 100%;
21 line-height: 1.5;
22 font-family: inherit;
23 text-align: inherit;
24 text-decoration: none;
25 vertical-align: baseline;
26 background: transparent;
27 }
28
29 html, body {
30 /* To create our "window" onto the scene, we're going to slide the
31 * SVG off the left-hand side of the screen, and we don't want
32 * scroll bars to appear. */
33 overflow: hidden;
34 }
35
36 svg {
37 /* Set the height to 100% of the screen, which we'll keep; and the
38 * initial position to (0,0), which we're going to change
39 * every time the window is resized, because the exact amount
40 * that we have to slide the SVG to the left to get the ticket
41 * into the center will change. */
42 position: fixed;
43 top: 0;
44 left: 0;
45 height: 100%;
46 }
47
48 /* The blinking fade in/out animation for the ticket date and time */
49 @keyframes blink {
50 25% {
51 opacity: 0.5;
52 }
53 50% {
54 opacity: 0;
55 }
56 75% {
57 opacity: 0.5;
58 }
59 }
60
61 #tickettime, #ticketdate {
62 /* 300 two-second blinks is ten minutes */
63 animation: blink 2s linear 300;
64 }
65
66 /* Define, load, and specify the custom font we use for the ticket
67 * date, time, and service name. */
68 @font-face {
69 font-family: "CharmBypass Regular";
70 src:
71 url("data:font/woff2;base64,@CBPREGULAR@") format("woff2")
72 }
73
74 #servicename, #tickettime, #ticketdate {
75 font-family: "CharmBypass Regular";
76 }
77
78 @font-face {
79 font-family: "CharmBypass Bold";
80 src:
81 url("data:font/woff2;base64,@CBPBOLD@") format("woff2")
82 }
83
84 #serviceletter {
85 font-family: "CharmBypass Bold";
86 }
87
88 /************************/
89 /* Scrolling animations */
90 /************************/
91
92 /* Bus */
93 @keyframes busroll {
94 from {
95 transform: translateX(0%);
96 }
97 to {
98 transform: translateX(100%);
99 }
100 }
101
102 #bus {
103 animation: busroll 23s linear infinite;
104 }
105
106
107 /* Tram */
108 @keyframes tramroll {
109 from {
110 transform: translateX(0%);
111 }
112 to {
113 transform: translateX(100%);
114 }
115 }
116
117 #tram {
118 animation: tramroll 17s linear infinite;
119 }
120
121
122 /* Tram */
123 @keyframes trainroll {
124 from {
125 transform: translateX(0%);
126 }
127 to {
128 transform: translateX(100%);
129 }
130 }
131
132 #train {
133 animation: trainroll 11s linear infinite;
134 }
135
136
137 /* Clouds */
138 @keyframes cloudsfloat {
139 from {
140 transform: translateX(0%);
141 }
142 to {
143 transform: translateX(-50%);
144 }
145 }
146
147 #clouds {
148 animation: cloudsfloat 40s linear infinite;
149 }
150
151 @keyframes cloudscopyfloat {
152 from {
153 transform: translateX(0%);
154 }
155 to {
156 transform: translateX(-50%);
157 }
158 }
159
160 #cloudscopy {
161 animation: cloudscopyfloat 40s linear infinite;
162 }
163
164
165 /* Trees */
166 @keyframes treespass {
167 from {
168 transform: translateX(0%);
169 }
170 to {
171 transform: translateX(-50%);
172 }
173 }
174
175 #trees {
176 /* The trees move a little faster than the clouds */
177 animation: treespass 30s linear infinite;
178 }
179
180 @keyframes treescopypass {
181 from {
182 transform: translateX(0%);
183 }
184 to {
185 transform: translateX(-50%);
186 }
187 }
188
189 #treescopy {
190 /* The trees move a little faster than the clouds */
191 animation: treescopypass 30s linear infinite;
192 }
193
194
195 /* City skyline */
196 @keyframes cityscroll {
197 from {
198 transform: translateX(0%);
199 }
200 to {
201 transform: translateX(-50%);
202 }
203 }
204
205 #city {
206 /* The city moves faster than the clouds but slower
207 * than the trees */
208 animation: cityscroll 35s linear infinite;
209 }
210
211 @keyframes citycopyscroll {
212 from {
213 transform: translateX(0%);
214 }
215 to {
216 transform: translateX(-50%);
217 }
218 }
219
220 #citycopy {
221 /* The city moves faster than the clouds but slower
222 * than the trees */
223 animation: citycopyscroll 35s linear infinite;
224 }
225 </style>
226 </head>
227
228 <body>
229 @SVGDATA@
230
231 <script>
232
233 /***********************************************/
234 /* First, center the ticket within the browser */
235 /***********************************************/
236
237 function center_ticket() {
238 /* We're relying on the SVG being the full height of the
239 * viewport already, and on the aspect ratio being
240 * preserved. First, find the center of the ticket. */
241 const r = document.getElementById("ticket").getBoundingClientRect();
242 const c = r.left + (r.width / 2);
243
244 /* That's the center-line of the ticket. We want to move it to
245 * the center-line of the viewport. */
246 const vc = document.documentElement.clientWidth / 2;
247
248 /* This is how much we need to translate the SVG */
249 const delta = vc - c;
250
251 /* But before we can set the absolute left-coordinate of the
252 * SVG, we need to know where it is now. Note: without the
253 * "px" this doesn't default to pixels like CSS does. */
254 const svg = document.querySelector("svg");
255 svg.style.left = (svg.getBoundingClientRect().left + delta) + "px";
256 }
257
258 /* Re-center it when the window is resized */
259 window.addEventListener("resize", center_ticket);
260
261 /* Center it once when the page has loaded, too */
262 window.addEventListener("load", center_ticket);
263
264
265 /*************************/
266 /* Set the security code */
267 /*************************/
268
269 /* All <text> elements produced by inkscape contain a single <tspan>
270 * that itself contains the actual text. */
271 const ct = document.getElementById("codetext");
272
273 /* Get the "code" from the querystring if it's there */
274 let params = new URLSearchParams(document.location.search);
275 if (params.get("code")) {
276 ct.firstChild.textContent = params.get("code");
277 }
278 else {
279 /* Otherwise, use a random code */
280 const bucket = ["0","1","2","3","4","5","6","7","8","9",
281 "A","B","C","D","E","F","G","H","I","J",
282 "K","L","M","N","O","P","Q","R","S","T",
283 "U","V","W","X","Y","Z"];
284
285 /* Two random ints between 0 and 35 */
286 const i1 = Math.floor(Math.random() * 36);
287 const i2 = Math.floor(Math.random() * 36);
288 const d1 = bucket[i1];
289 const d2 = bucket[i2];
290 ct.firstChild.textContent = d1 + d2;
291 }
292
293 /*****************************************/
294 /* Next, set up the ticket date and time */
295 /*****************************************/
296
297 /* There are two parameters, time and date, that we store in one
298 * underlying "date" variable. Default both to an hour and a
299 * half from now. This is what the CharmPass app does for
300 * one-way tickets.
301 */
302 const date = new Date();
303
304 /* Add an hour and a half. We use the low-level get/setTime to
305 * change the number of milliseconds since the epoch that this
306 * date represents. Obviously correct, and avoids all suspicious
307 * corner cases (well, for a few more decades). */
308 date.setTime(date.getTime() + (90*60*1000));
309
310 /* All <text> elements produced by inkscape contain a single <tspan>
311 * that itself contains the actual text. */
312 tt = document.getElementById("tickettime");
313 tt.firstChild.textContent = date.toLocaleTimeString();
314
315 const td = document.getElementById("ticketdate");
316 const dateopts = {
317 day: "2-digit",
318 month: "2-digit",
319 year: "2-digit"
320 };
321 td.firstChild.textContent = date.toLocaleDateString("en-US", dateopts);
322
323
324 /*************************************************************/
325 /* Finally, add the onclick handler for the night/day switch */
326 /*************************************************************/
327
328 /* We always start in "day" mode */
329 is_day = true;
330
331 function set_day() {
332 sky.style.fill = "#efb02f";
333 }
334
335 function set_night() {
336 sky.style.fill = "#143b66";
337 }
338
339 function swap_colors() {
340 if (is_day) {
341 set_night();
342 is_day = false;
343 }
344 else {
345 set_day();
346 is_day = true;
347 }
348 }
349
350 document.body.addEventListener("click", swap_colors);
351
352 </script>
353 </body>
354 </html>