| 1 | "use strict"; |
| 2 | |
| 3 | function Timer(id) { |
| 4 | var precision = 100; // milliseconds |
| 5 | var defaultTime = (15 * 60 + 0) * 1000; // milliseconds |
| 6 | var time = defaultTime; |
| 7 | var intervalID; |
| 8 | this.isTicking = false; |
| 9 | var outputTime = function() { |
| 10 | document.getElementById(id).innerHTML = timeToTimeStr(time); |
| 11 | var flagid = "flag-" + id.split('-')[1]; |
| 12 | var flagelem = document.getElementById(flagid); |
| 13 | flagelem.style.visibility = time <= 0 ? "visible" : "hidden"; |
| 14 | }; |
| 15 | |
| 16 | var startTickingUnixTime = null; |
| 17 | var remainingTimeAtTickingStart = null; |
| 18 | |
| 19 | var thisTimer = this; // workaround to use this in setInterval |
| 20 | this.tick = function() { |
| 21 | var currentPassedTime = new Date() - startTickingUnixTime; |
| 22 | time = remainingTimeAtTickingStart - currentPassedTime; |
| 23 | if(time <= 0) { |
| 24 | time = 0; |
| 25 | // time's up |
| 26 | clearInterval(intervalID); |
| 27 | thisTimer.isTicking = false; |
| 28 | } |
| 29 | outputTime(); |
| 30 | }; |
| 31 | this.start = function() { |
| 32 | startTickingUnixTime = new Date(); |
| 33 | remainingTimeAtTickingStart = time; |
| 34 | intervalID = setInterval(this.tick, precision); |
| 35 | this.isTicking = true; |
| 36 | }; |
| 37 | this.stop = function() { |
| 38 | clearInterval(intervalID); |
| 39 | this.isTicking = false; |
| 40 | }; |
| 41 | this.setTime = function(t) { |
| 42 | time = t; |
| 43 | defaultTime = t; |
| 44 | outputTime(); |
| 45 | }; |
| 46 | this.getTime = function() { |
| 47 | return time; |
| 48 | }; |
| 49 | this.reset = function() { |
| 50 | if(this.isTicking) { |
| 51 | this.stop(); |
| 52 | } |
| 53 | this.setTime(defaultTime); |
| 54 | }; |
| 55 | this.getDefaultTime = function() { |
| 56 | return defaultTime; |
| 57 | }; |
| 58 | } |
| 59 | |
| 60 | var currentTimers = new (function() { |
| 61 | this.leftTimer = new Timer("timer-left"); |
| 62 | this.rightTimer = new Timer("timer-right"); |
| 63 | this.active = this.leftTimer; |
| 64 | this.passive = this.rightTimer; |
| 65 | this.isPaused = true; |
| 66 | this.swap = function() { |
| 67 | var tmp = this.passive; |
| 68 | this.passive = this.active; |
| 69 | this.active = tmp; |
| 70 | var icon = document.getElementById("play"); |
| 71 | icon.innerHTML = (icon.innerHTML == "◀" ? "▶" : "◀"); |
| 72 | }; |
| 73 | this.pause = function() { |
| 74 | this.active.stop(); |
| 75 | this.isPaused = true; |
| 76 | document.getElementById("pause").style.visibility = "visible"; |
| 77 | }; |
| 78 | this.resume = function() { |
| 79 | this.active.start(); |
| 80 | this.isPaused = false; |
| 81 | document.getElementById("pause").style.visibility = "hidden"; |
| 82 | }; |
| 83 | })(); |
| 84 | |
| 85 | function timeStrToTime(timeStr) { |
| 86 | var minute = parseInt(timeStr.substr(0, 2)); |
| 87 | var second = parseInt(timeStr.substr(3, 2)); |
| 88 | return (minute * 60 + second) * 1000; |
| 89 | } |
| 90 | |
| 91 | function timeToTimeStr(time) { |
| 92 | var minute = Math.floor(time / 1000 / 60); |
| 93 | var second = Math.floor(time / 1000) % 60; |
| 94 | if(minute < 10) { |
| 95 | minute = '0' + minute.toString(); |
| 96 | } |
| 97 | if(second < 10) { |
| 98 | second = '0' + second.toString(); |
| 99 | } |
| 100 | return minute + ':' + second; |
| 101 | } |
| 102 | |
| 103 | document.onkeydown = function(e) { |
| 104 | if(e.key) { |
| 105 | switch(e.key) { |
| 106 | case ' ': return toggle(); |
| 107 | case 'S': |
| 108 | case 's': return setTime(); |
| 109 | case 'R': |
| 110 | case 'r': return reset(); |
| 111 | case 'p': |
| 112 | case 'P': return pauseResume(); |
| 113 | default: return; |
| 114 | } |
| 115 | } else if(e.keyCode) { |
| 116 | switch(e.keyCode) { |
| 117 | case 32: return toggle(); // space |
| 118 | case 83: return setTime(); // S |
| 119 | case 82: return reset(); // R |
| 120 | case 80: return pauseResume(); // P |
| 121 | default: return; |
| 122 | } |
| 123 | } else { |
| 124 | alert("Browser not supported"); |
| 125 | } |
| 126 | }; |
| 127 | |
| 128 | function toggle() { |
| 129 | if(!currentTimers.isPaused) { |
| 130 | currentTimers.active.stop(); |
| 131 | currentTimers.passive.start(); |
| 132 | } |
| 133 | currentTimers.swap(); |
| 134 | var lbt = document.getElementById('left-button-top'); |
| 135 | var leftIsLong = lbt.getAttribute('class') == 'button-long'; |
| 136 | var lbt_d = lbt.getAttribute('d').split(' '); |
| 137 | var lbb = document.getElementById('left-button-body'); |
| 138 | var rbt = document.getElementById('right-button-top'); |
| 139 | var rbt_d = rbt.getAttribute('d').split(' '); |
| 140 | var rbb = document.getElementById('right-button-body'); |
| 141 | |
| 142 | var y_diff = 50 * (leftIsLong ? 1 : -1); |
| 143 | var classes = ['button-short', 'button-long']; |
| 144 | |
| 145 | lbt_d[2] = (parseInt(lbt_d[2]) + y_diff).toString(); |
| 146 | lbt_d[10] = (parseInt(lbt_d[10]) + y_diff).toString(); |
| 147 | lbb.setAttribute('height', |
| 148 | (parseInt(lbb.getAttribute('height')) - y_diff).toString()); |
| 149 | lbb.setAttribute('y', (parseInt(lbb.getAttribute('y')) + y_diff).toString()); |
| 150 | rbt_d[2] = (parseInt(rbt_d[2]) - y_diff).toString(); |
| 151 | rbt_d[10] = (parseInt(rbt_d[10]) - y_diff).toString(); |
| 152 | rbb.setAttribute('height', |
| 153 | (parseInt(rbb.getAttribute('height')) + y_diff).toString()); |
| 154 | rbb.setAttribute('y', (parseInt(rbb.getAttribute('y')) - y_diff).toString()); |
| 155 | lbt.setAttribute('class', classes[(leftIsLong + 1) % 2]); |
| 156 | lbb.setAttribute('class', classes[(leftIsLong + 1) % 2]); |
| 157 | rbt.setAttribute('class', classes[(leftIsLong + 1) % 2]); |
| 158 | rbb.setAttribute('class', classes[(leftIsLong + 1) % 2]); |
| 159 | lbt.setAttribute('d', lbt_d.join(' ')); |
| 160 | rbt.setAttribute('d', rbt_d.join(' ')); |
| 161 | |
| 162 | lbt.setAttribute('onclick', leftIsLong ? '' : 'toggle()'); |
| 163 | lbb.setAttribute('onclick', leftIsLong ? '' : 'toggle()'); |
| 164 | lbt.setAttribute('cursor', leftIsLong ? 'default' : 'pointer'); |
| 165 | lbb.setAttribute('cursor', leftIsLong ? 'default' : 'pointer'); |
| 166 | rbt.setAttribute('onclick', leftIsLong ? 'toggle()' : ''); |
| 167 | rbb.setAttribute('onclick', leftIsLong ? 'toggle()' : ''); |
| 168 | rbt.setAttribute('cursor', leftIsLong ? 'pointer' : 'default'); |
| 169 | rbb.setAttribute('cursor', leftIsLong ? 'pointer' : 'default'); |
| 170 | } |
| 171 | |
| 172 | function pauseResume() { |
| 173 | if(currentTimers.isPaused) { |
| 174 | currentTimers.resume(); |
| 175 | } else { |
| 176 | currentTimers.pause(); |
| 177 | } |
| 178 | } |
| 179 | |
| 180 | function setTime() { |
| 181 | var leftstart, rightstart; |
| 182 | var def = timeToTimeStr(currentTimers.leftTimer.getDefaultTime()); |
| 183 | var regex = /[0-9][0-9]:[0-5][0-9]/; |
| 184 | |
| 185 | leftstart = prompt("Time for LEFT player in MM:SS", def); |
| 186 | if(leftstart === null) return; // Cancel |
| 187 | while(!leftstart.match(regex)) { |
| 188 | leftstart = prompt("Invalid value\nTime for LEFT player in MM:SS", def); |
| 189 | if(leftstart === null) return; // Cancel |
| 190 | } |
| 191 | def = leftstart; |
| 192 | rightstart = prompt("Time for RIGHT player in MM:SS", def); |
| 193 | if(rightstart === null) return; // Cancel |
| 194 | while(!rightstart.match(regex)) { |
| 195 | rightstart = prompt("Invalid value\nTime for RIGHT player in MM:SS", def); |
| 196 | if(rightstart === null) return; // Cancel |
| 197 | } |
| 198 | currentTimers.leftTimer.setTime(timeStrToTime(leftstart)); |
| 199 | currentTimers.rightTimer.setTime(timeStrToTime(rightstart)); |
| 200 | } |
| 201 | |
| 202 | function reset() { |
| 203 | currentTimers.pause(); |
| 204 | currentTimers.active.reset(); |
| 205 | currentTimers.passive.reset(); |
| 206 | } |
| 207 | |
| 208 | function init() { |
| 209 | insertcss(); |
| 210 | setTimeout(fitscreen, 2000); // hack to allow DOM to be redrawn with new css |
| 211 | } |
| 212 | |
| 213 | function fitscreen() { |
| 214 | /* |
| 215 | * Scale body so as to fit the screen |
| 216 | */ |
| 217 | var heightRatio = window.innerHeight / document.body.scrollHeight; |
| 218 | var widthRatio = window.innerWidth / document.body.scrollWidth; |
| 219 | var scaleRatio = Math.min(heightRatio, widthRatio, 1); |
| 220 | document.body.style.transform = 'scale(' + scaleRatio + ')'; |
| 221 | } |
| 222 | |
| 223 | function insertcss() { |
| 224 | /* fix positions for browsers that don't support flex */ |
| 225 | var cssfile = document.getElementById("style"); |
| 226 | if(!('align-items' in document.body.style)) { |
| 227 | cssfile.setAttribute("href", "noflex.css"); |
| 228 | } |
| 229 | } |