Going home; wasn't welcome there
authoradrianiainlam <adrianiainlam@gmail.com>
Tue, 2 Feb 2016 01:09:34 +0000 (01:09 +0000)
committeradrianiainlam <adrianiainlam@gmail.com>
Tue, 2 Feb 2016 01:09:34 +0000 (01:09 +0000)
index.html [new file with mode: 0644]
noflex.css [new file with mode: 0644]
script.js [new file with mode: 0644]
style.css [new file with mode: 0644]

diff --git a/index.html b/index.html
new file mode 100644 (file)
index 0000000..6785a3a
--- /dev/null
@@ -0,0 +1,54 @@
+<!DOCTYPE html>
+<html>
+  <head>
+    <meta charset="utf-8">
+    <title>JSChessClock</title>
+    <script src="script.js"></script>
+    <!--<link rel="stylesheet" type="text/css" href="style.css" />-->
+  </head>
+  <body onload="init()" onresize="fitscreen()">
+    <h1>JSChessClock</h1>
+    
+    <div class="clock-container">
+      <svg version="1.1" width="650" height="500"
+           xmlns="http://www.w3.org/2000/svg">
+        <path id="left-button-top" class="button-long" fill="#c0c0c0" 
+              d="M 210 70 A 140 140 0 0 0 30 70 Z" stroke="#c0c0c0"
+              onclick="toggle()" cursor="pointer" />
+        <rect id="left-button-body" class="button-long" x="60" y="70"
+              width="120" height="80" stroke="#c0c0c0" fill="#c0c0c0"
+              onclick="toggle()" cursor="pointer" />
+        <path id="right-button-top" class="button-short" stroke="#c0c0c0"
+              d="M 620 120 A 140 140 0 0 0 440 120 Z" fill="#c0c0c0" />
+        <rect id="right-button-body" class="button-short" x="470" y="120"
+              width="120" height="30" stroke="#c0c0c0" fill="#c0c0c0" />
+        <rect x="0" y="150" width="650" height="350" rx="25" ry="25"
+              fill="#bc5454" />
+        <rect x="40" y="245" width="235" height="90" rx="10" ry="10"
+              fill="white" />
+        <rect x="375" y="245" width="235" height="90" rx="10" ry="10"
+              fill="white" />
+        <line x1="0" y1="420" x2="650" y2="420" stroke="black"
+              stroke-width="1" />
+      </svg>
+      <div class="flags">
+        <div id="flag-left">&#x2690;</div>
+        <div id="flag-right">&#x2691;</div>
+      </div>
+      <div class="timers">
+        <div class="timer" id="timer-left">15:00</div>
+        <div class="indicators">
+          <div id="pause">❚❚</div>
+          <div id="play">◀</div>
+        </div>
+        <div class="timer" id="timer-right">15:00</div>
+      </div>
+      <div class="commands">
+        <div class="cmd" onclick="setTime();">[S]et time</div>
+        <div class="cmd" onclick="reset();">[R]eset to previous</div>
+        <div class="cmd" onclick="pauseResume();">[P]ause / resume</div>
+        <div class="cmd" onclick="toggle();">[space] Toggle</div>
+      </div>
+    </div>
+  </body>
+</html>
diff --git a/noflex.css b/noflex.css
new file mode 100644 (file)
index 0000000..b57e0ea
--- /dev/null
@@ -0,0 +1,97 @@
+body {
+  transform-origin: 0 0;
+  overflow: hidden;
+  text-align: center;
+}
+
+.clock-container {
+  position: relative;
+  width: 650px;
+  margin-left: auto;
+  margin-right: auto;
+}
+
+.flags > div {
+  display: inline-block;
+  visibility: hidden;
+  animation: blinker 1s linear infinite;
+  font-size: 200%;
+  font-family: "DejaVu Sans",FreeSerif,Code2000,"BabelStone Han","Lucida Sans";
+}
+
+#flag-left {
+  position: absolute;
+  top: 35%;
+  z-index: 9999;
+  left: 5%;
+}
+#flag-right {
+  position: absolute;
+  top: 35%;
+  z-index: 9999;
+  right: 5%;
+}
+
+@keyframes blinker {
+  50% { opacity: 0; }
+}
+
+.timers {
+  clear: both;
+  width: 530px; /* 570, -40 for aesthetic reason */
+  position: absolute;
+  top: 50%;
+  z-index: 9999;
+  left: 0;
+  right: 0;
+  margin-left: auto;
+  margin-right: auto;
+}
+
+.timer {
+  font-size: 400%;
+  display: inline-block;
+}
+
+#timer-left {
+  float: left;
+}
+#timer-right {
+  float: right;
+}
+
+.indicators {
+  font-size: 167%;
+  display: inline-block;
+  position: absolute;
+  left: 0;
+  right: 0;
+  margin-left: auto;
+  margin-right: auto;
+}
+
+h1 {
+  text-align: center;
+}
+
+.commands {
+  clear: both;
+  margin: 0 auto;
+  width: 650px;
+  position: absolute;
+  left: 0;
+  right: 0;
+  margin-left: auto;
+  margin-right:auto;
+  bottom: 5%;
+  z-index: 9999;
+}
+
+.cmd {
+  display: inline-block;
+  cursor: pointer;
+  background-color: #FFF;
+  padding: 5px;
+  border: 3px solid #800000;
+  border-radius: 5px;
+}
diff --git a/script.js b/script.js
new file mode 100644 (file)
index 0000000..3e524f4
--- /dev/null
+++ b/script.js
@@ -0,0 +1,226 @@
+"use strict";
+
+function Timer(id) {
+  var precision = 100; // milliseconds
+  var defaultTime = (15 * 60 + 0) * 1000; // milliseconds
+  var time = defaultTime;
+  var intervalID;
+  this.isTicking = false;
+  var outputTime = function() {
+    document.getElementById(id).innerHTML = timeToTimeStr(time);
+    var flagid = "flag-" + id.split('-')[1];
+    var flagelem = document.getElementById(flagid);
+    flagelem.style.visibility = time <= 0 ? "visible" : "hidden";
+  };
+  var thisTimer = this; // workaround to use this in setInterval
+  this.tick = function() {
+    time -= (precision <= time ? precision : time);
+    if(time <= 0) {
+      // time's up
+      clearInterval(intervalID);
+      thisTimer.isTicking = false;
+    }
+    outputTime();
+  };
+  this.start = function() {
+    intervalID = setInterval(this.tick, precision);
+    this.isTicking = true;
+  };
+  this.stop = function() {
+    clearInterval(intervalID);
+    this.isTicking = false;
+  };
+  this.setTime = function(t) {
+    time = t;
+    defaultTime = t;
+    outputTime();
+  };
+  this.getTime = function() {
+    return time;
+  };
+  this.reset = function() {
+    if(this.isTicking) {
+      this.stop();
+    }
+    this.setTime(defaultTime);
+  };
+  this.getDefaultTime = function() {
+    return defaultTime;
+  };
+}
+
+var currentTimers = new (function() {
+  this.leftTimer  = new Timer("timer-left");
+  this.rightTimer = new Timer("timer-right");
+  this.active     = this.leftTimer;
+  this.passive    = this.rightTimer;
+  this.isPaused   = true;
+  this.swap = function() {
+    var tmp        = this.passive;
+    this.passive   = this.active;
+    this.active    = tmp;
+    var icon       = document.getElementById("play");
+    icon.innerHTML = (icon.innerHTML == "◀" ? "▶" : "◀");
+  };
+  this.pause = function() {
+    this.active.stop();
+    this.isPaused = true;
+    document.getElementById("pause").style.visibility = "visible";
+  };
+  this.resume = function() {
+    this.active.start();
+    this.isPaused = false;
+    document.getElementById("pause").style.visibility = "hidden";
+  };
+})();
+
+function timeStrToTime(timeStr) {
+  var minute = parseInt(timeStr.substr(0, 2));
+  var second = parseInt(timeStr.substr(3, 2));
+  return (minute * 60 + second) * 1000;
+}
+
+function timeToTimeStr(time) {
+  var minute = Math.floor(time / 1000 / 60);
+  var second = Math.floor(time / 1000) % 60;
+  if(minute < 10) {
+    minute = '0' + minute.toString();
+  }
+  if(second < 10) {
+    second = '0' + second.toString();
+  }
+  return minute + ':' + second;
+}
+
+document.onkeydown = function(e) {
+  if(e.key) {
+    switch(e.key) {
+      case ' ': return toggle();
+      case 'S':
+      case 's': return setTime();
+      case 'R':
+      case 'r': return reset();
+      case 'p':
+      case 'P': return pauseResume();
+      default:  return;
+    }
+  } else if(e.keyCode) {
+    switch(e.keyCode) {
+      case 32: return toggle();      // space
+      case 83: return setTime();     // S
+      case 82: return reset();       // R
+      case 80: return pauseResume(); // P
+      default: return;
+    }
+  } else {
+    alert("Browser not supported");
+  }
+};
+
+function toggle() {
+  if(!currentTimers.isPaused) {
+    currentTimers.active.stop();
+    currentTimers.passive.start();
+  }
+  currentTimers.swap();
+  var lbt = document.getElementById('left-button-top');
+  var leftIsLong = lbt.getAttribute('class') == 'button-long';
+  var lbt_d = lbt.getAttribute('d').split(' ');
+  var lbb = document.getElementById('left-button-body');
+  var rbt = document.getElementById('right-button-top');
+  var rbt_d = rbt.getAttribute('d').split(' ');
+  var rbb = document.getElementById('right-button-body');
+  
+  var y_diff = 50 * (leftIsLong ? 1 : -1);
+  var classes = ['button-short', 'button-long'];
+
+  lbt_d[2] = (parseInt(lbt_d[2]) + y_diff).toString();
+  lbt_d[10] = (parseInt(lbt_d[10]) + y_diff).toString();
+  lbb.setAttribute('height',
+      (parseInt(lbb.getAttribute('height')) - y_diff).toString());
+  lbb.setAttribute('y', (parseInt(lbb.getAttribute('y')) + y_diff).toString());
+  rbt_d[2] = (parseInt(rbt_d[2]) - y_diff).toString();
+  rbt_d[10] = (parseInt(rbt_d[10]) - y_diff).toString();
+  rbb.setAttribute('height',
+      (parseInt(rbb.getAttribute('height')) + y_diff).toString());
+  rbb.setAttribute('y', (parseInt(rbb.getAttribute('y')) - y_diff).toString());
+  lbt.setAttribute('class', classes[(leftIsLong + 1) % 2]);
+  lbb.setAttribute('class', classes[(leftIsLong + 1) % 2]);
+  rbt.setAttribute('class', classes[(leftIsLong + 1) % 2]);
+  rbb.setAttribute('class', classes[(leftIsLong + 1) % 2]);
+  lbt.setAttribute('d', lbt_d.join(' '));
+  rbt.setAttribute('d', rbt_d.join(' '));
+  
+  lbt.setAttribute('onclick', leftIsLong ? '' : 'toggle()');
+  lbb.setAttribute('onclick', leftIsLong ? '' : 'toggle()');
+  lbt.setAttribute('cursor', leftIsLong ? 'default' : 'pointer');
+  lbb.setAttribute('cursor', leftIsLong ? 'default' : 'pointer');
+  rbt.setAttribute('onclick', leftIsLong ? 'toggle()' : '');
+  rbb.setAttribute('onclick', leftIsLong ? 'toggle()' : '');
+  rbt.setAttribute('cursor', leftIsLong ? 'pointer' : 'default');
+  rbb.setAttribute('cursor', leftIsLong ? 'pointer' : 'default');
+}
+
+function pauseResume() {
+  if(currentTimers.isPaused) {
+    currentTimers.resume();
+  } else {
+    currentTimers.pause();
+  }
+}
+
+function setTime() {
+  var leftstart, rightstart;
+  var def = timeToTimeStr(currentTimers.leftTimer.getDefaultTime());
+  var regex = /[0-9][0-9]:[0-5][0-9]/;
+  
+  leftstart = prompt("Time for LEFT player in MM:SS", def);
+  if(leftstart === null) return; // Cancel
+  while(!leftstart.match(regex)) {
+    leftstart = prompt("Invalid value\nTime for LEFT player in MM:SS", def);
+    if(leftstart === null) return; // Cancel
+  }
+  def = leftstart;
+  rightstart = prompt("Time for RIGHT player in MM:SS", def);
+  if(rightstart === null) return; // Cancel
+  while(!rightstart.match(regex)) {
+    rightstart = prompt("Invalid value\nTime for RIGHT player in MM:SS", def);
+    if(rightstart === null) return; // Cancel
+  }
+  currentTimers.leftTimer.setTime(timeStrToTime(leftstart));
+  currentTimers.rightTimer.setTime(timeStrToTime(rightstart));
+}
+
+function reset() {
+  currentTimers.pause();
+  currentTimers.active.reset();
+  currentTimers.passive.reset();
+}
+
+function init() {
+  insertcss();
+  setTimeout(fitscreen, 5); // hack to allow DOM to be redrawn with new css
+}
+
+function fitscreen() {
+  /*
+   * Scale body so as to fit the screen
+   */
+  var heightRatio =  window.innerHeight / document.body.scrollHeight;
+  var widthRatio = window.innerWidth / document.body.scrollWidth;
+  var scaleRatio = Math.min(heightRatio, widthRatio);
+  document.body.style.transform = 'scale(' + scaleRatio + ')';
+}
+
+function insertcss() {
+  /* fix positions for browsers that don't support flex */
+  var cssfile = document.createElement("link");
+  cssfile.setAttribute("rel", "stylesheet");
+  cssfile.setAttribute("type", "text/css");
+  if('align-items' in document.body.style) {
+    cssfile.setAttribute("href", "style.css");
+  } else {
+    cssfile.setAttribute("href", "noflex.css");
+  }
+  document.head.appendChild(cssfile);
+}
diff --git a/style.css b/style.css
new file mode 100644 (file)
index 0000000..5a8c6e6
--- /dev/null
+++ b/style.css
@@ -0,0 +1,85 @@
+body {
+    transform-origin: 0 0;
+    overflow: hidden;
+    text-align: center;
+}
+
+.clock-container {
+    position: relative;
+}
+
+.flags {
+  width: 570px;
+  display: flex;
+  justify-content: space-between;
+  position: absolute;
+  top: 40%;
+  z-index: 9999;
+  left: 0;
+  right: 0;
+  margin-left: auto;
+  margin-right: auto;
+}
+
+.flags > div {
+  visibility: hidden;
+  animation: blinker 1s linear infinite;
+  font-size: 25pt;
+  font-family: "DejaVu Sans",FreeSerif,Code2000,"BabelStone Han","Lucida Sans";
+}
+
+@keyframes blinker {
+  50% { opacity: 0; }
+}
+
+.timers {
+  width: 650px;
+  display: flex;
+  justify-content: center;
+  align-items: center;
+  /*margin-bottom: 96pt;*/
+  position: absolute;
+  top: 50%;
+  z-index: 9999;
+  left: 0;
+  right: 0;
+  margin-left: auto;
+  margin-right: auto;
+}
+
+.timer {
+  margin-left: 48pt;
+  margin-right: 48pt;
+  font-size: 48pt;
+}
+
+.indicators {
+  width: 20pt;
+  font-size: 20pt;
+}
+
+h1 {
+  text-align: center;
+}
+
+.commands {
+  margin: 0 auto;
+  width: 650px;
+  display: flex;
+  justify-content: space-around;
+  position: absolute;
+  left: 0;
+  right: 0;
+  margin-left: auto;
+  margin-right:auto;
+  bottom: 5%;
+  z-index: 9999;
+}
+
+.cmd {
+  cursor: pointer;
+  background-color: #FFF;
+  padding: 5px;
+  border: 3px solid #800000;
+  border-radius: 5px;
+}