[Extension] Add App Timer extension 42/157242/20
authorjaekuk, lee <juku1999@samsung.com>
Tue, 24 Oct 2017 02:35:58 +0000 (11:35 +0900)
committerHye Kyoung Hwang <cookie@samsung.com>
Tue, 31 Oct 2017 01:03:29 +0000 (01:03 +0000)
This extension sets the timer for App.

version 1.0.1

Change-Id: Ibacc85fce3bbe78534ab99a4012967ddc47e72a6
Signed-off-by: jaekuk, lee <juku1999@samsung.com>
wrt/wrt_support/extensions_repo/app_timer/background.js [new file with mode: 0755]
wrt/wrt_support/extensions_repo/app_timer/contentscript.js [new file with mode: 0755]
wrt/wrt_support/extensions_repo/app_timer/icon.png [new file with mode: 0755]
wrt/wrt_support/extensions_repo/app_timer/images/exit.png [new file with mode: 0755]
wrt/wrt_support/extensions_repo/app_timer/lib/clock.js [new file with mode: 0755]
wrt/wrt_support/extensions_repo/app_timer/manifest.json [new file with mode: 0755]
wrt/wrt_support/extensions_repo/app_timer/style.css [new file with mode: 0755]

diff --git a/wrt/wrt_support/extensions_repo/app_timer/background.js b/wrt/wrt_support/extensions_repo/app_timer/background.js
new file mode 100755 (executable)
index 0000000..8b08974
--- /dev/null
@@ -0,0 +1,102 @@
+var js = document.createElement("script");
+js.type = "text/javascript";
+js.src = "lib/clock.js";
+document.body.appendChild(js);
+
+var TimerState = {
+  time: {
+    hours: 0,
+    minutes: 1,
+    seconds: 0
+  },
+  displayTime: {
+    hours: '00',
+    minutes: '01',
+    seconds: '00',
+  },
+  defaultTime: {
+    hours: 0,
+    minutes: 1,
+    seconds: 0
+  },
+  defaultDisplayTime: {
+       hours: '00',
+       minutes: '01',
+       seconds: '00',
+  }
+};
+
+var thisclock;
+
+chrome.runtime.onConnect.addListener(function(port) {
+
+  (function () {
+    thisclock = new Clock({
+    second: function (clock) {
+      update(clock);
+    },
+    finish: function (clock) {
+      port.postMessage({background: "timer-finish"});
+    }
+  });
+})();
+
+
+  function init() {
+    port.postMessage({background: "timer-init", value:TimerState});
+  }
+
+  function reset() {
+    TimerState.time = TimerState.defaultTime;
+    update(TimerState.time);
+  }
+
+  function setTime(time) {
+    if (time.hours != TimerState.time.hours ||
+      time.minutes != TimerState.time.minutes ||
+      time.seconds != TimerState.time.seconds) {
+        TimerState.defaultTime = time;
+         TimerState.defaultDisplayTime = _getDisplayableTime(time);
+      }
+      TimerState.time = time;
+      TimerState.displayTime = _getDisplayableTime(time);
+  }
+
+  function _getDisplayableTime(time) {
+    var dh = (time.hours < 10 ? '0' : '') + String(time.hours),
+      dm = (time.minutes < 10 ? '0' : '') + String(time.minutes),
+      ds = (time.seconds < 10 ? '0' : '') + String(time.seconds)
+    return {hours: dh, minutes: dm, seconds: ds};
+  }
+
+  function start() {   
+    thisclock.start(TimerState.time.hours, TimerState.time.minutes, TimerState.time.seconds);
+  }
+
+  function stop() {
+    thisclock.stop();
+  }
+
+  function update(time) {
+    setTime(time);
+    port.postMessage({background: "timer-update", value:TimerState});
+  }
+
+  port.onMessage.addListener(function(msg) {
+    if (msg.contentscript == "timer-init") {
+      init();
+    }
+    else if (msg.contentscript == "timer-start") {
+      start();
+    }
+    else if (msg.contentscript == "timer-stop") {
+      stop();
+    }
+    else if (msg.contentscript == "timer-reset") {
+      reset();
+    }
+    else if (msg.contentscript == "timer-setTime") {
+      setTime(msg.value);
+    }
+  });
+});
diff --git a/wrt/wrt_support/extensions_repo/app_timer/contentscript.js b/wrt/wrt_support/extensions_repo/app_timer/contentscript.js
new file mode 100755 (executable)
index 0000000..f8a5040
--- /dev/null
@@ -0,0 +1,267 @@
+const EXIT_HTML = '\
+<div id="exit-popup"> \
+\
+<center><div id="exit-popup-container"> \
+\
+<div id = "exit-title"><h1> Exit App? </h1></div> \
+<div class = "line"> </div> \
+<img src = "../../extensions/app_timer/images/exit.png"></img> \
+<div class = "line"> </div> \
+<div class="exit-button-group"> \
+<button id="yes-button" class="exit-button">Yes</button> \
+<button id="no-button"class="exit-button">No</button> \
+</div> \
+\
+</div></center> \
+\
+</div>';
+//"
+
+const SET_HTML = '\
+<div id="main-popup"> \
+\
+<center><div id="popup-container"> \
+\
+<div id = "title"><h1> App Timer </h1></div> \
+\
+<div class = "line"> </div> \
+\
+<label class = "switch"> \
+<input type = "checkbox" class = "switch_input" id = "sme_switch" checked> \
+<span class = "switch_label" data-on = "On" data-off = "Off"></span> \
+<span class = "switch_handle"><div class = "switch_pix"></div></span> \
+</label> \
+\
+<div class = "line"> </div> \
+\
+<div class = "action"> \
+\
+<div class="timer-group"> \
+<span id="hours-digit" class="digit editable" >00</span> \
+<span class="colon">:</span> \
+<span id="minutes-digit" class="digit editable">00</span> \
+<span class="colon">:</span> \
+<span id="seconds-digit" class="digit editable">00</span> \
+</div> \
+\
+<div class = "line"> </div> \
+\
+<div class="btn-group"> \
+<button id="start-button" class="button">start</button> \
+<button id="reset-button" class="button">reset</button> \
+</div> \
+</div> \
+\
+</div></center> \
+\
+</div>';
+//"
+
+var timerStart = false;
+var defaultDisplayTime = {hours: 0, minutes: 1, seconds: 0};
+var f1Key = false;
+var port = chrome.runtime.connect({name: "contentscript"});
+
+port.onMessage.addListener(function(msg) {
+  console.log('from background : ' + msg.background);
+  if (msg.background == "timer-init") {
+    setTime(msg.value);
+  }
+  else if (msg.background == "timer-update") {
+    console.log('value, '+msg.value.displayTime.hours+' : '+msg.value.displayTime.minutes+' : '+msg.value.displayTime.seconds);
+    setTime(msg.value);
+  }
+  else if (msg.background == "timer-finish") {
+    exitPopup();
+  }
+});
+
+document.body.insertAdjacentHTML('beforeend', SET_HTML);
+initialize(); 
+
+function exitPopup() {
+  hideModal(); 
+  document.body.insertAdjacentHTML('beforeend', EXIT_HTML);
+
+  document.getElementById('yes-button').onclick= function(event) {
+    console.log('exitPopup: yes');
+    reset();
+    stop();
+    var launcher = require('electron').remote.getCurrentWindow();
+    launcher.close();
+  };
+
+  document.getElementById('no-button').onclick= function(event) {
+    console.log('exitPopup: no');
+    document.body.removeChild(document.getElementById('exit-popup'));
+    document.getElementById('hours-digit').innerHTML = defaultDisplayTime.hours;
+    document.getElementById('minutes-digit').innerHTML = defaultDisplayTime.minutes;
+    document.getElementById('seconds-digit').innerHTML = defaultDisplayTime.seconds;
+    reset();
+    start();
+  };    
+}
+
+window.onkeydown = function(event) {
+  if (event.keyCode != 112)  // F1 key
+    return;
+  if (f1Key) {
+    hideModal();
+    f1Key = false ;    
+  } else {
+    showModal();
+    f1Key = true ;     
+  }
+}
+
+function initialize() {
+  port.postMessage({contentscript: "timer-init"});
+  if (!timerStart) {
+    pauseMode();
+  } else {
+    runningMode();
+  }
+  bindAll();
+}
+
+function showModal() {
+  document.getElementById('main-popup').style.display = 'block';
+}
+
+function hideModal() {
+  document.getElementById('main-popup').style.display = 'none';
+}
+
+function bindAll() {
+  document.getElementById('sme_switch').addEventListener("change", function(event){
+    if(event.srcElement.checked){
+      document.querySelector(".action").style.display = "block";
+    } else{
+      reset();
+      stop();
+      document.querySelector(".action").style.display = "none";
+    };
+  });
+
+  document.getElementById('start-button').onclick= function(event) {
+    if (timerStart == false) {
+      start();
+    } else {
+      stop();
+    }
+  };
+
+  document.getElementById('reset-button').onclick= function(event) {
+    if (!timerStart) {
+      reset();
+    }
+  };
+
+  document.getElementById('hours-digit').onclick= function(event) {
+    event.stopPropagation();
+    if (timerStart)
+      return;
+    document.getElementById('hours-digit').setAttribute("contenteditable", true);
+    document.getElementById('hours-digit').addEventListener('mouseout', hoursMouseoutHandler );
+  };
+
+function hoursMouseoutHandler(e){
+    document.getElementById('hours-digit').removeEventListener('mouseout', hoursMouseoutHandler);
+    var value = document.getElementById('hours-digit').innerHTML;
+    if (value.length == 0 || Number(value) == 0) {
+      document.getElementById('hours-digit').innerHTML = '00';
+    } else if (!/^[0-9]{1,2}$/.test(value)) {
+      value = value.replace(/[^0-9]/,'').substr(0,2);
+      document.getElementById('hours-digit').innerHTML = value;
+    } else {
+      value = ((Number(value) < 10) ? '0' : '') + Number(value);
+      document.getElementById('hours-digit').innerHTML = value;
+    }
+  }
+
+  document.getElementById('minutes-digit').onclick= function(event) {
+    event.stopPropagation();
+    if (timerStart)
+      return;
+    document.getElementById('minutes-digit').setAttribute("contenteditable", true);
+    document.getElementById('minutes-digit').addEventListener('mouseout', minutesMouseoutHandler );
+  };
+
+  function minutesMouseoutHandler(e){
+    document.getElementById('minutes-digit').removeEventListener('mouseout', minutesMouseoutHandler);
+    var value = document.getElementById('minutes-digit').innerHTML;
+    if (value.length == 0 || Number(value) == 0) {
+      document.getElementById('minutes-digit').innerHTML = '00';
+    } else if (!/^[0-9]{1,2}$/.test(value)) {
+      value = value.replace(/[^0-9]/,'').substr(0,2);
+      document.getElementById('minutes-digit').innerHTML = value;
+    } else {
+      value = ((Number(value) < 10) ? '0' : '') + Number(value);
+      document.getElementById('minutes-digit').innerHTML = value;
+    }
+  }
+
+  document.getElementById('seconds-digit').onclick= function(event) {
+    event.stopPropagation();
+    if (timerStart)
+      return;
+    document.getElementById('seconds-digit').setAttribute("contenteditable", true);
+    document.getElementById('seconds-digit').addEventListener('mouseout', secondsMouseoutHandler );
+  };
+  
+  function secondsMouseoutHandler(e){
+    document.getElementById('seconds-digit').removeEventListener('mouseout', secondsMouseoutHandler);
+     var value = document.getElementById('seconds-digit').innerHTML;
+    if (value.length == 0 || Number(value) == 0) {
+      document.getElementById('seconds-digit').innerHTML = '00';
+    } else if (!/^[0-9]{1,2}$/.test(value)) {
+      value = value.replace(/[^0-9]/,'').substr(0,2);
+      document.getElementById('seconds-digit').innerHTML = value;
+    } else {
+      value = ((Number(value) < 10) ? '0' : '') + Number(value);
+      document.getElementById('seconds-digit').innerHTML = value;
+    }
+  }
+}
+
+function start() {
+  timerStart = true;
+  var time = {hours: 0, minutes: 0, seconds: 0};
+  time.hours   = Number(document.getElementById('hours-digit').innerHTML);
+  time.minutes = Number(document.getElementById('minutes-digit').innerHTML);
+  time.seconds = Number(document.getElementById('seconds-digit').innerHTML);
+  console.log('start, '+time.hours+' : '+time.minutes+' : '+time.seconds); 
+  if (time.hours + time.minutes + time.seconds > 0) {
+    runningMode();
+    port.postMessage({contentscript: "timer-setTime", value:time});
+    port.postMessage({contentscript: "timer-start"});
+  }
+}
+
+function reset() {
+  port.postMessage({contentscript: "timer-reset"});
+}
+
+function stop() {
+  timerStart = false;
+  pauseMode();
+  port.postMessage({contentscript: "timer-stop"});
+}
+
+function setTime(time) {
+  console.log('setTime, '+time.displayTime.hours+' : '+time.displayTime.minutes+' : '+time.displayTime.seconds); 
+  document.getElementById('hours-digit').innerHTML = time.displayTime.hours;
+  document.getElementById('minutes-digit').innerHTML = time.displayTime.minutes;
+  document.getElementById('seconds-digit').innerHTML = time.displayTime.seconds;
+  defaultDisplayTime.hours = time.defaultDisplayTime.hours;
+  defaultDisplayTime.minutes = time.defaultDisplayTime.minutes;
+  defaultDisplayTime.seconds = time.defaultDisplayTime.seconds;
+}
+
+function runningMode() {
+  document.getElementById('start-button').innerHTML = 'stop';
+}
+
+function pauseMode() {
+  document.getElementById('start-button').innerHTML = 'start';
+}
diff --git a/wrt/wrt_support/extensions_repo/app_timer/icon.png b/wrt/wrt_support/extensions_repo/app_timer/icon.png
new file mode 100755 (executable)
index 0000000..5515365
Binary files /dev/null and b/wrt/wrt_support/extensions_repo/app_timer/icon.png differ
diff --git a/wrt/wrt_support/extensions_repo/app_timer/images/exit.png b/wrt/wrt_support/extensions_repo/app_timer/images/exit.png
new file mode 100755 (executable)
index 0000000..6500ffd
Binary files /dev/null and b/wrt/wrt_support/extensions_repo/app_timer/images/exit.png differ
diff --git a/wrt/wrt_support/extensions_repo/app_timer/lib/clock.js b/wrt/wrt_support/extensions_repo/app_timer/lib/clock.js
new file mode 100755 (executable)
index 0000000..533642b
--- /dev/null
@@ -0,0 +1,187 @@
+// a simple countdown clock.
+
+(function (window, undefined) {
+'use strict';
+
+var TIMER_FINISHED = 0,
+    TIMER_RUNNING  = 1,
+    TIMER_PAUSED   = 2,
+    ticks_per_second = 5,
+    cb,
+    callbacks = ['tick', 'second', 'finish'],
+    debug = function (message) {
+      if (window.console !== undefined && window.console.log !== undefined && window.DEBUG) {
+        window.console.log.apply(window.console, arguments);
+      }
+    },
+    error = function (message) {
+      if (window.console !== undefined && window.console.error !== undefined) {
+        window.console.error.apply(window.console, arguments);
+      }
+    };
+
+/***
+ * Constructor.
+ *
+ * Takes callbacks object.
+ *
+ * callbacks can be:
+ *   tick: function (clock) {}
+ *     called on every tick. up to 5 times per second if tab is in the
+ *     foreground, probably zero to one times per second if tab is
+ *     backgrounded.
+ *
+ *   second: function (clock) {}
+ *     called at most once per second, but time between calls may be greater if
+ *     tab is backgrounded or browser gets lazy or busy.
+ *
+ *   finish: function (clock) {}
+ *     called once when the clock finishes counting down.
+ */
+var Clock = function (callbacks) {
+  this.callbacks = (callbacks || {});
+  this.status    = TIMER_PAUSED;
+};
+
+/***
+ * private:
+ *
+ * step forwards through time, one tick at a ... time
+ */
+Clock.prototype.next_tick = function () {
+  if (!(this.status === TIMER_FINISHED || this.status === TIMER_PAUSED)) {
+    var self = this;
+
+    this.timeout = window.setTimeout(function () { self.next_tick() }, 1000 / ticks_per_second);
+    this.call_tick();
+    this.countdown();
+  }
+}
+
+/***
+ * private:
+ *
+ * populate zero padded strings version of time
+ */
+Clock.prototype.zero_pad = function () {
+  this.zero_padded = (this.zero_padded || {});
+  this.zero_padded.hours   = (this.hours < 10 ? '0' : '')   + String(this.hours);
+  this.zero_padded.minutes = (this.minutes < 10 ? '0' : '') + String(this.minutes);
+  this.zero_padded.seconds = (this.seconds < 10 ? '0' : '') + String(this.seconds);
+
+  // dupe property for camel-casers
+  this.zeroPadded = this.zero_padded;
+
+  return this.zero_padded;
+}
+
+/**
+ * private:
+ *
+ * generate the callback calling functions with appropriate error handling.
+ */
+for (var i=0; i < 3; i++) {
+  cb = callbacks[i];
+  Clock.prototype['call_' + cb] = (function (callbackType) {
+    return function () {
+      try {
+        if (this.callbacks[callbackType] !== undefined)
+          this.callbacks[callbackType](this);
+      } catch (ex) {
+        // don't crash because of lame callbacks
+        error('[clock.js] Failed ' + callbackType + ' callback!', ex.message)
+      }
+    };
+  })(cb);
+}
+
+/***
+ * private:
+ *
+ * run every tick, check to see whether a second has passed
+ */
+Clock.prototype.countdown = function () {
+  var whole_minutes, elapsed_seconds = Math.floor((new Date().getTime() - this.start_time) / 1000);
+
+  // how much time has actually passed
+  this.remaining_seconds = this.requested_seconds - elapsed_seconds;
+
+  // separate remaining_seconds into time components
+  this.seconds  = this.remaining_seconds % 60;
+  whole_minutes = Math.floor(this.remaining_seconds / 60);
+  this.minutes  = whole_minutes % 60;
+  this.hours    = Math.floor(whole_minutes / 60);
+
+  this.zero_pad();
+
+  // at least a second has passed, trigger callbacks
+  if (this.seconds != this.previous.seconds || this.minutes != this.previous.minutes || this.hours != this.previous.hours) {
+    if (this.hours <= 0 && this.minutes <= 0 && this.seconds <= 0) {
+      this.call_second();
+      this.call_finish();
+      this.stop();
+    } else {
+      // call second callback
+      this.call_second();
+    }
+
+    this.previous = {
+      hours: this.hours,
+      minutes: this.minutes,
+      seconds: this.seconds
+    }
+  }
+}
+
+/***
+ * Start the clock.
+ */
+Clock.prototype.start = function (hours, minutes, seconds) {
+  this.hours   = hours || 0;
+  this.minutes = minutes || 0;
+  this.seconds = seconds || 0;
+
+  debug(
+    "[clock.js] starting clock at " +
+    this.hours + ":" +
+    this.minutes + ":" +
+    this.seconds
+  );
+
+  // make sure we can check when the clock has rolled over
+  this.previous = {h: 0, m: 0, s: 0};
+
+  // total countown seconds
+  this.remaining_seconds = this.hours * 3600 + this.minutes * 60 + this.seconds;
+  this.requested_seconds = this.remaining_seconds;
+
+  // clock is now in a prepared-but-paused state, we just have to unpause
+  this.unpause();
+}
+
+Clock.prototype.unpause = function () {
+  this.running = true;
+  this.status = TIMER_RUNNING;
+  this.start_time = new Date().getTime();
+
+  // set requested_seconds to remaining so that unpausing doesn't reset the clock
+  this.requested_seconds = this.remaining_seconds;
+
+  this.next_tick();
+}
+
+/***
+ * Stop the clock.
+ */
+Clock.prototype.stop = function () {
+  this.status  = TIMER_PAUSED;
+  this.running = false;
+}
+
+// alias `stop` to `pause`
+Clock.prototype.pause = Clock.prototype.stop;
+
+window.Clock = Clock;
+
+})(window);
+
diff --git a/wrt/wrt_support/extensions_repo/app_timer/manifest.json b/wrt/wrt_support/extensions_repo/app_timer/manifest.json
new file mode 100755 (executable)
index 0000000..eb5907b
--- /dev/null
@@ -0,0 +1,15 @@
+{
+  "name": "app_timer",
+  "version": "1.0.1",
+  "manifest_version": 1,
+  "background": {
+    "scripts": ["background.js"]
+  },
+  "content_scripts": [
+    {
+      "matches": ["<all_urls>"],
+      "js": ["contentscript.js"],
+      "css": ["style.css"]
+    }
+  ]
+}
diff --git a/wrt/wrt_support/extensions_repo/app_timer/style.css b/wrt/wrt_support/extensions_repo/app_timer/style.css
new file mode 100755 (executable)
index 0000000..d1fb9bc
--- /dev/null
@@ -0,0 +1,240 @@
+/*----------------------------------------------------*/
+#main-popup {
+  display: none; /* Hidden by default */
+  position: fixed;
+  z-index: 1; /* Sit on top */
+  left: 0;
+  top: 0;
+  width: 100%; /* Full width */
+  height: 100%; /* Full height */
+  overflow: auto; /* Enable scroll if needed */
+  background-color: rgb(0,0,0); /* Fallback color */
+  background-color: rgba(0,0,0,0.4); /* Black w/ opacity */
+}
+#popup-container {
+  margin: auto;
+  margin-top: 20%;
+  border: 1px solid #888;
+  height: auto;
+  width: 350px;
+  background-color: rgb(255,255,255); 
+}
+#title {
+  line-height: 24px;
+  font-weight: bold;
+  text-shadow:0 0 10px white;
+  position: relative;
+  margin-top: 5%;
+}      
+.line  {
+  width:90%;
+  height: 1%;
+  border-top:  1px solid rgba(175, 154, 110, 0.51);
+  padding: 0 0 5px;
+  position: relative;
+  margin-top: 5%;
+}
+/*----------------------------------------------------*/
+.switch {
+  position: relative;
+  display: inline-block;
+  vertical-align: top;
+  width: 85px;
+  height: 30px;
+  padding: 3px;
+  background-color: white;
+  border-radius: 18px;
+  box-shadow: inset 0 -1px white, inset 0 1px 1px rgba(0, 0, 0, 0.05);
+  cursor: pointer;
+  background-image: -webkit-linear-gradient(top, #eeeeee, white 25px);
+  background-image: -moz-linear-gradient(top, #eeeeee, white 25px);
+  background-image: -o-linear-gradient(top, #eeeeee, white 25px);
+  background-image: linear-gradient(to bottom, #eeeeee, white 25px);
+}
+.switch .switch_input {
+  position: absolute;
+  top: 0;
+  left: 0;
+  opacity: 0;
+}
+.switch .switch_label {
+  position: relative;
+  display: block;
+  height: inherit;
+  font-size: 15px;
+  text-transform: uppercase;
+  background: #FF5D1F;
+  border-radius: inherit;
+  box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.12), inset 0 0 2px rgba(0, 0, 0, 0.15);
+  -webkit-transition: 0.15s ease-out;
+  -moz-transition: 0.15s ease-out;
+  -o-transition: 0.15s ease-out;
+  transition: 0.15s ease-out;
+  -moz-transition-property: opacity background;
+  -o-transition-property: opacity background;
+}
+.switch .switch_label:before, .switch_label:after {
+  position: absolute;
+  top: 50%;
+  margin-top: -.5em;
+  line-height: 1;
+  -webkit-transition: inherit;
+  -moz-transition: inherit;
+  -o-transition: inherit;
+  transition: inherit;
+}
+.switch .switch_label:before {
+  content: attr(data-off);
+  right: 11px;
+  color: white;
+  text-shadow: 0 1px rgba(255, 255, 255, 0.5);
+}
+.switch .switch_label:after {
+  content: attr(data-on);
+  left: 11px;
+  color: white;
+  text-shadow: 0 1px rgba(0, 0, 0, 0.2);
+  opacity: 0;
+}
+.switch .switch_input:checked ~ .switch_label {
+  background: #47a8d8;
+  box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.15), inset 0 0 3px rgba(0, 0, 0, 0.2);
+}
+.switch .switch_input:checked ~ .switch_label:before {
+  opacity: 0;
+}
+.switch .switch_input:checked ~ .switch_label:after {
+  opacity: 1;
+}
+.switch .switch_handle {
+  position: absolute;
+  top: 5px;
+  left: 4px;
+  width: 35px;
+  height: 26px;
+  background: white;
+  border-radius: 10px;
+  box-shadow: 1px 1px 5px rgba(0, 0, 0, 0.2);
+  background-image: -webkit-linear-gradient(top, white 40%, #f0f0f0);
+  background-image: -moz-linear-gradient(top, white 40%, #f0f0f0);
+  background-image: -o-linear-gradient(top, white 40%, #f0f0f0);
+  background-image: linear-gradient(to bottom, white 40%, #f0f0f0);
+  -webkit-transition: left 0.15s ease-out;
+  -moz-transition: left 0.15s ease-out;
+  -o-transition: left 0.15s ease-out;
+  transition: left 0.15s ease-out;
+}
+.switch .switch_handle .switch_pix{
+  content: '';
+  position: absolute;
+  top: 50%;
+  left: 50%;
+  margin: -6px 0 0 -6px;
+  width: 12px;
+  height: 12px;
+  background: #f9f9f9;
+  border-radius: 6px;
+  box-shadow:  0px 0px 15px #D5D5D5;
+  background-color: #FF5D1F;
+}
+.switch .switch_input:checked ~ .switch_handle {
+  left: 51px;
+  box-shadow: -1px 1px 5px rgba(0, 0, 0, 0.2);
+}
+.switch .switch_input:checked ~ .switch_handle .switch_pix{
+  content: '';
+  position: absolute;
+  top: 50%;
+  left: 50%;
+  margin: -6px 0 0 -6px;
+  width: 12px;
+  height: 12px;
+  background: #f9f9f9;
+  border-radius: 6px;
+  box-shadow:  0px 0px 15px #D5D5D5;
+  background-color: #4fb845;
+}
+.switch > .switch_input:checked ~ .switch_label {
+  background: #4fb845;
+}
+/*----------------------------------------------------*/
+.action {
+  display: block;
+  position: relative;
+}
+/*----------------------------------------------------*/
+.timer-group {
+  margin-top: 5%;
+}
+.editable {
+  display: inline-block;
+  font-size: 80px;
+  overflow: hidden;
+  white-space: nowrap;
+  cursor: pointer;
+}
+.editable:focus {
+  outline: none;
+}
+.colon {
+  display: inline-block;
+  font-size: 70px;
+  vertical-align: top;
+}
+.digit {
+  vertical-align: middle;
+}
+/*----------------------------------------------------*/
+.btn-group {
+  margin-top: 25%;
+  margin-bottom: 10%;
+}
+.button {
+  width: 100px;
+  height: 50px;
+  font-size: 30px;
+  margin-left: 5%;
+  margin-right: 5%;
+  cursor: pointer;
+}
+/*----------------------------------------------------*/
+/*----------------------------------------------------*/
+#exit-popup {
+  position: fixed;
+  z-index: 1; /* Sit on top */
+  left: 0;
+  top: 0;
+  width: 100%; /* Full width */
+  height: 100%; /* Full height */
+  overflow: auto; /* Enable scroll if needed */
+  background-color: rgb(0,0,0); /* Fallback color */
+  background-color: rgba(0,0,0,0.4); /* Black w/ opacity */
+}
+#exit-popup-container {
+  margin: auto;
+  margin-top: 20%;
+  border: 1px solid #888;
+  height: auto;
+  width: 350px;
+  background-color: rgb(255,255,255); 
+}
+#exit-title {
+  line-height: 24px;
+  font-weight: bold;
+  text-shadow:0 0 10px white;
+  position: relative;
+  margin-top: 5%;
+}
+/*----------------------------------------------------*/
+.exit-button-group {
+  margin-top: 25%;
+  margin-bottom: 10%;
+}
+.exit-button {
+  width: 100px;
+  height: 50px;
+  font-size: 30px;
+  margin-left: 5%;
+  margin-right: 5%;
+  cursor: pointer;
+}
\ No newline at end of file