X-Git-Url: https://gitweb.michael.orlitzky.com/?a=blobdiff_plain;f=index.html.in;h=345c432b5d591b325f2aa5c39000a19392e44fe7;hb=fe64fd48133a3d77a824615ab1534971825189e9;hp=0bec5e720a4efcf39df02f38e87054e7866d37c1;hpb=238dc11dae659a7bd7207cc606f60e7801fd5409;p=charm-bypass.git diff --git a/index.html.in b/index.html.in index 0bec5e7..345c432 100644 --- a/index.html.in +++ b/index.html.in @@ -12,77 +12,78 @@ * Reset styles for the html and body elements, the only two HTML * elements we use. */ html, body { - margin: 0; - padding: 0; - border: 0; - font-weight: normal; - font-style: inherit; - font-size: 100%; - line-height: 1.5; - font-family: inherit; - text-align: inherit; - text-decoration: none; - vertical-align: baseline; - background: transparent; + margin: 0; + padding: 0; + border: 0; + font-weight: normal; + font-style: inherit; + font-size: 100%; + line-height: 1.5; + font-family: inherit; + text-align: inherit; + text-decoration: none; + vertical-align: baseline; + background: transparent; } html, body { - /* To create our "window" onto the scene, we're going to slide the + /* To create our "window" onto the scene, we're going to slide the * SVG off the left-hand side of the screen, and we don't want - * scroll bars to appear. */ - overflow: hidden; + * scroll bars to appear. */ + overflow: hidden; } svg { - /* Set the height to 100% of the screen, which we'll keep; and the - * initial position to (0,0), which we're going to change - * every time the window is resized, because the exact amount - * that we have to slide the SVG to the left to get the ticket - * into the center will change. */ - position: fixed; - top: 0; - left: 0; - height: 100%; + /* Set the height to 100% of the screen, which we'll keep; and the + * initial position to (0,0), which we're going to change + * every time the window is resized, because the exact amount + * that we have to slide the SVG to the left to get the ticket + * into the center will change. */ + position: fixed; + top: 0; + left: 0; + height: 100%; } /* The blinking fade in/out animation for the ticket date and time */ @keyframes blink { - 25% { - opacity: 0.5; - } - 50% { - opacity: 0; - } - 75% { - opacity: 0.5; - } + 25% { + opacity: 0.5; + } + 50% { + opacity: 0; + } + 75% { + opacity: 0.5; + } } #tickettime, #ticketdate { - /* 300 two-second blinks is ten minutes */ - animation: blink 2s linear 300; + /* 300 two-second blinks is ten minutes */ + animation: blink 2s linear 300; } /* Define, load, and specify the custom font we use for the ticket * date, time, and service name. */ @font-face { - font-family: "CharmBypass Regular"; - src: - url("data:font/woff2;base64,@CBPREGULAR@") format("woff2") + font-family: "CharmBypass Regular"; + src: + url("data:font/woff2;base64,@CBPREGULAR@") format("woff2") } - #servicename, #tickettime, #ticketdate { - font-family: "CharmBypass Regular"; + #servicename, #tickettime, #ticketdate, #codetext { + font-family: "CharmBypass Regular", sans-serif; } @font-face { - font-family: "CharmBypass Bold"; - src: - url("data:font/woff2;base64,@CBPBOLD@") format("woff2") + font-family: "CharmBypass Bold"; + src: + url("data:font/woff2;base64,@CBPBOLD@") format("woff2") } - #serviceletter { - font-family: "CharmBypass Bold"; + #serviceid { + font-family: "CharmBypass Bold", sans-serif; + font-weight: bold; } /************************/ @@ -91,130 +92,130 @@ /* Bus */ @keyframes busroll { - from { - transform: translateX(0%); - } - to { - transform: translateX(100%); - } + from { + transform: translateX(0%); + } + to { + transform: translateX(-100%); + } } #bus { - animation: busroll 23s linear infinite; + animation: busroll 23s linear infinite; } /* Tram */ @keyframes tramroll { - from { - transform: translateX(0%); - } - to { - transform: translateX(100%); - } + from { + transform: translateX(0%); + } + to { + transform: translateX(100%); + } } #tram { - animation: tramroll 17s linear infinite; + animation: tramroll 17s linear infinite; } /* Tram */ @keyframes trainroll { - from { - transform: translateX(0%); - } - to { - transform: translateX(100%); - } + from { + transform: translateX(0%); + } + to { + transform: translateX(100%); + } } #train { - animation: trainroll 11s linear infinite; + animation: trainroll 11s linear infinite; } /* Clouds */ @keyframes cloudsfloat { - from { - transform: translateX(0%); - } - to { - transform: translateX(-50%); - } + from { + transform: translateX(0%); + } + to { + transform: translateX(-50%); + } } #clouds { - animation: cloudsfloat 40s linear infinite; + animation: cloudsfloat 40s linear infinite; } @keyframes cloudscopyfloat { - from { - transform: translateX(0%); - } - to { - transform: translateX(-50%); - } + from { + transform: translateX(0%); + } + to { + transform: translateX(-50%); + } } #cloudscopy { - animation: cloudscopyfloat 40s linear infinite; + animation: cloudscopyfloat 40s linear infinite; } /* Trees */ @keyframes treespass { - from { - transform: translateX(0%); - } - to { - transform: translateX(-50%); - } + from { + transform: translateX(0%); + } + to { + transform: translateX(-50%); + } } #trees { /* The trees move a little faster than the clouds */ - animation: treespass 30s linear infinite; + animation: treespass 30s linear infinite; } @keyframes treescopypass { - from { - transform: translateX(0%); - } - to { - transform: translateX(-50%); - } + from { + transform: translateX(0%); + } + to { + transform: translateX(-50%); + } } #treescopy { /* The trees move a little faster than the clouds */ - animation: treescopypass 30s linear infinite; + animation: treescopypass 30s linear infinite; } /* City skyline */ @keyframes cityscroll { - from { - transform: translateX(0%); - } - to { - transform: translateX(-50%); - } + from { + transform: translateX(0%); + } + to { + transform: translateX(-50%); + } } #city { /* The city moves faster than the clouds but slower * than the trees */ - animation: cityscroll 35s linear infinite; + animation: cityscroll 35s linear infinite; } @keyframes citycopyscroll { - from { - transform: translateX(0%); - } - to { - transform: translateX(-50%); - } + from { + transform: translateX(0%); + } + to { + transform: translateX(-50%); + } } #citycopy { @@ -235,95 +236,144 @@ /***********************************************/ function center_ticket() { - /* We're relying on the SVG being the full height of the - * viewport already, and on the aspect ratio being - * preserved. First, find the center of the ticket. */ - const r = document.getElementById("ticket").getBoundingClientRect(); - const c = r.left + (r.width / 2); - - /* That's the center-line of the ticket. We want to move it to - * the center-line of the viewport. */ - const vc = document.documentElement.clientWidth / 2; - - /* This is how much we need to translate the SVG */ - const delta = vc - c; - - /* But before we can set the absolute left-coordinate of the - * SVG, we need to know where it is now. Note: without the - * "px" this doesn't default to pixels like CSS does. */ - const svg = document.querySelector("svg"); - svg.style.left = (svg.getBoundingClientRect().left + delta) + "px"; + /* We're relying on the SVG being the full height of the + * viewport already, and on the aspect ratio being + * preserved. First, find the center of the ticket. */ + const r = document.getElementById("ticket").getBoundingClientRect(); + const c = r.left + (r.width / 2); + + /* That's the center-line of the ticket. We want to move it to + * the center-line of the viewport. */ + const vc = document.documentElement.clientWidth / 2; + + /* This is how much we need to translate the SVG */ + const hdelta = vc - c; + + /* But before we can set the absolute left-coordinate of the + * SVG, we need to know where it is now. Note: without the + * "px" this doesn't default to pixels like CSS does. */ + const svg = document.querySelector("svg"); + svg.style.left = (svg.getBoundingClientRect().left + hdelta) + "px"; + } + + + /******************************/ + /* Set the service identifier */ + /******************************/ + function set_service_id() { + const sid = document.getElementById("serviceid"); + + /* Get the "serviceid" from the querystring if it's there */ + let params = new URLSearchParams(document.location.search); + if (params.get("serviceid")) { + sid.textContent = params.get("serviceid"); + } + + /* Otherwise, leave it at "F" */ + } + + /****************************************/ + /* Set and reposition the security code */ + /****************************************/ + + function set_code() { + const ct = document.getElementById("codetext"); + + /* Get the "code" from the querystring if it's there */ + let params = new URLSearchParams(document.location.search); + if (params.get("code")) { + ct.textContent = params.get("code"); + } + else { + /* Otherwise, use a random code */ + const bucket = ["0","1","2","3","4","5","6","7","8","9", + "A","B","C","D","E","F","G","H","I","J", + "K","L","M","N","O","P","Q","R","S","T", + "U","V","W","X","Y","Z"]; + + /* Two random ints between 0 and 35 */ + const i1 = Math.floor(Math.random() * 36); + const i2 = Math.floor(Math.random() * 36); + const d1 = bucket[i1]; + const d2 = bucket[i2]; + ct.textContent = d1 + d2; + } + } + + + function center_code() { + /* Center the security code inside its red box */ + const ct = document.getElementById("codetext"); + const bg = document.getElementById("codebg"); + + /* First, find the center of the red box */ + const r1 = bg.getBoundingClientRect(); + const c1 = r1.left + (r1.width / 2); + + /* Now the center of the code text */ + const r2 = ct.getBoundingClientRect(); + const c2 = r2.left + (r2.width / 2); + + /* What do we add to c2 to make it equal to c1? */ + const hdelta = c1 - c2; + + /* We've measured everything so far in "client rect" + * coordinates, because that's the only available measurement + * we have for the width of the element after futzing + * with its contents. But when we reposition that + * element, it will be by adjusting its "x" attribute, and + * that attribute uses a different coordinate system than the + * client rect does. Specifically, "x" refers to an offset + * within the SVG's coordinate system, and the client rect + * coordinates are pixels on-screen. To convert between the + * two, we can take the "width" attribute of the background + * element and compare it to the width of the background + * element's client rect. Since the size of the background is + * fixed, this should give us a multiplier that turns client recr + * distances (what we have) into SVG distances (what we want) */ + const client_to_svg = parseFloat(bg.getAttribute("width"))/r1.width; + + /* Convert hdelta from client rect to SVG coordinates */ + const svg_hdelta = hdelta * client_to_svg; + + /* Since this element has an "x" attribute it's easier for + * us to shift that than it is to mess with the "left" style. */ + ct.setAttribute("x", parseFloat(ct.getAttribute("x")) + svg_hdelta); } - /* Re-center it when the window is resized */ - window.addEventListener("resize", center_ticket); - - /* Center it once when the page has loaded, too */ - window.addEventListener("load", center_ticket); - + /*****************************************/ + /* Next, set up the ticket date and time */ + /*****************************************/ - /*************************/ - /* Set the security code */ - /*************************/ + function set_ticket_expiry() { + /* There are two parameters, time and date, that we store in one + * underlying "date" variable. Default both to an hour and a + * half from now. This is what the CharmPass app does for + * one-way tickets. */ + const date = new Date(); - /* All elements produced by inkscape contain a single - * that itself contains the actual text. */ - const ct = document.getElementById("codetext"); + /* Add an hour and a half. We use the low-level get/setTime to + * change the number of milliseconds since the epoch that this + * date represents. Obviously correct, and avoids all suspicious + * corner cases (well, for a few more decades). */ + date.setTime(date.getTime() + (90*60*1000)); - /* Get the "code" from the querystring if it's there */ - let params = new URLSearchParams(document.location.search); - if (params.get("code")) { - ct.firstChild.textContent = params.get("code"); - } - else { - /* Otherwise, use a random code */ - const bucket = ["0","1","2","3","4","5","6","7","8","9", - "A","B","C","D","E","F","G","H","I","J", - "K","L","M","N","O","P","Q","R","S","T", - "U","V","W","X","Y","Z"]; + tt = document.getElementById("tickettime"); + tt.textContent = date.toLocaleTimeString(); - /* Two random ints between 0 and 35 */ - const i1 = Math.floor(Math.random() * 36); - const i2 = Math.floor(Math.random() * 36); - const d1 = bucket[i1]; - const d2 = bucket[i2]; - ct.firstChild.textContent = d1 + d2; + const td = document.getElementById("ticketdate"); + const dateopts = { + day: "2-digit", + month: "2-digit", + year: "2-digit" + }; + td.textContent = date.toLocaleDateString("en-US", dateopts); } - /*****************************************/ - /* Next, set up the ticket date and time */ - /*****************************************/ - /* There are two parameters, time and date, that we store in one - * underlying "date" variable. Default both to an hour and a - * half from now. This is what the CharmPass app does for - * one-way tickets. - */ - const date = new Date(); - - /* Add an hour and a half. We use the low-level get/setTime to - * change the number of milliseconds since the epoch that this - * date represents. Obviously correct, and avoids all suspicious - * corner cases (well, for a few more decades). */ - date.setTime(date.getTime() + (90*60*1000)); - - /* All elements produced by inkscape contain a single - * that itself contains the actual text. */ - tt = document.getElementById("tickettime"); - tt.firstChild.textContent = date.toLocaleTimeString(); - - const td = document.getElementById("ticketdate"); - const dateopts = { - day: "2-digit", - month: "2-digit", - year: "2-digit" - }; - td.firstChild.textContent = date.toLocaleDateString("en-US", dateopts); - - - /*************************************************************/ - /* Finally, add the onclick handler for the night/day switch */ - /*************************************************************/ + /*********************************************************/ + /* Finally, the onclick handler for the night/day switch */ + /*********************************************************/ /* We always start in "day" mode */ is_day = true; @@ -333,20 +383,45 @@ } function set_night() { - sky.style.fill = "#143b66"; + sky.style.fill = "#143b66"; } function swap_colors() { - if (is_day) { - set_night(); - is_day = false; - } - else { - set_day(); - is_day = true; - } + if (is_day) { + set_night(); + is_day = false; + } + else { + set_day(); + is_day = true; + } } + + /*****************************************************/ + /* Add event handlers for all of the functions above */ + /*****************************************************/ + + /* Center the ticket once when the page has loaded */ + window.addEventListener("load", center_ticket); + + /* Re-center the ticket when the window is resized */ + window.addEventListener("resize", center_ticket); + + /* Set the service identifier when the page has loaded */ + window.addEventListener("load", set_service_id); + + /* Set the security code text when the page has loaded */ + window.addEventListener("load", set_code); + + /* Center the security code text when the page has loaded; in + * particular, after we set the code. */ + window.addEventListener("load", center_code); + + /* Set the ticket expiration date/time upon page load */ + window.addEventListener("load", set_ticket_expiry); + + /* Swap colors when the screen is tapped */ document.body.addEventListener("click", swap_colors);