mini clock

simple clock for full screen mode

As of 2016-06-19. See the latest version.

// ==UserScript==
// @name        mini clock
// @namespace   gnblizz
// @description simple clock for full screen mode
// @version     2.01
// @compatible  firefox
// @include     *
// @grant       GM_setValue
// @grant       GM_getValue
// @grant       GM_registerMenuCommand
// @icon        data:image/gif;base64,R0lGODlhEAAQAKECABEREe7u7v///////yH5BAEKAAIALAAAAAAQABAAAAIplI+py30Bo5wB2IvzrXDvaoFcCIBeeXaeSY4tibqxSWt2RuWRw/e+UQAAOw==
// ==/UserScript==
"use strict";

/* This user script inserts the system time in the upper right corner of the browser window. This is especially useful 
 * in full screen mode, when the task bar clock is hidden.
 * To change the position, select "clock at bottom" from the context menu of the time display.
 * To set the minimum width for the clock to appear, resize the browser window to that width and select "mini clock set min width" from the "user script commands" sub menu of the greasemonkey menu.
 */

//while true; do date +%R | osd_cat --pos=top --offset=40 --align=right --colour=green --outline=1 --font=-etl-fixed-medium-r-normal-*-40-*-*-*-*-*-*-* ; done

var doc = document, isTop = window.self==window.top,
  div, spn, wrapper,
  ref = 0, fontSize,
  hTimer = 0, hTimer2 = 0, hTimer3 = 0,
  datePattern = (function(){
    switch(navigator.language.slice(0,2)){
    case 'de': return '%A, %e. %B %Y'; // e.g: 'Donnerstag, 16. Juli 2015'
    case 'en': return /-US/i.test(navigator.language) ? '%A%n%B %e, %Y' : '%A%n%e %B %Y';
    }
    return '%A, %x';
  }());
  
function getValue(name, dflt) { return (typeof(GM_getValue)=='function') ? GM_getValue(name, dflt) : dflt; }
function setValue(name, value) { if(typeof(GM_setValue)=='function') GM_setValue(name, value); }
function addStyle(style) { var o = doc.createElement("STYLE"); o.innerHTML = style; doc.getElementsByTagName('HEAD')[0].appendChild(o); return o; }

function miniClockSetWidth() {
  GM_setValue('miniClockMinWidth', window.outerWidth);
  alert('New treshold width is '+window.outerWidth+' pixel.');
}

function niceTime(d) { return d.toLocaleFormat('%R'); }
function niceDate(d) { return d.toLocaleFormat(datePattern); }

if(isTop) {
  if(typeof(GM_registerMenuCommand)=='function')
    GM_registerMenuCommand('mini clock set min width', miniClockSetWidth, 'w');
  init();
}

// full screen api

//requires about:config full-screen-api.allow-trusted-requests-only=false
function SetFullScreenMode(v) {
  (v.requestFullscreen||v.mozRequestFullScreen||v.webkitRequestFullscreen||v.webkitEnterFullscreen).call(v);
  //console.info('To allow full screen mode, you may want to set full-screen-api.allow-trusted-requests-only to false in about:config.');
}

function EndFullScreenMode() {
  if(!isFullScreen())
    return false;
  (doc.exitFullscreen||doc.mozCancelFullScreen||doc.webkitExitFullscreen).call(doc);
  return true;
}

function FullScreenElement() {
  return doc.fullscreenElement||doc.mozFullScreenElement||doc.webkitFullscreenElement;
}

function isFullScreen() {
  return !!FullScreenElement();
}

function OnFullScreenChange(fn) {
  doc.addEventListener("fullscreenchange", fn);
  doc.addEventListener("mozfullscreenchange", fn);
  doc.addEventListener("webkitfullscreenchange", fn);
}

//

function ForceEndFullscreen() {
  var fse = FullScreenElement();
  if(fse) {
    console.log('forced end fullscreen');
    var ns = fse.nextSibling, pn = fse.parentNode;
    pn.removeChild(fse);
    pn.insertBefore(fse, ns);
  }
}

OnFullScreenChange(function(event) {
 try {
  //console.log('event fullscreenchange with'+(doc.mozFullScreen ? '' : 'out')+' FullScreen and FullScreenElement='+(doc.mozFullScreenElement ? doc.mozFullScreenElement.tagName+(doc.mozFullScreenElement.id ? '#'+doc.mozFullScreenElement.id : '') : ''));
  if(!ref++)
    init();
  ref--;
 } catch(e) { console.log(e); }
});

function OnTimer() {
  var dt = new Date(), t = niceTime(dt);
  if(spn.textContent != t) {
    spn.textContent = t;
    if(!div.title || t.slice(-1)=='0')
      div.title = niceDate(dt);
  }
}

function OnMouseMove() {
  clearTimeout(hTimer2);
  hTimer2 = setTimeout(OnTimer2, 3333);
  if(wrapper.style.cursor) {
    wrapper.style.cursor = '';
    doc.getElementById('mcwrfsb').style.visibility='';
  }
}

function OnTimer2() {
  hTimer2 = 0;
  wrapper.style.cursor = 'none';
  doc.getElementById('mcwrfsb').style.visibility = 'hidden';
}

function OnMouseEnter() {
  if(hTimer3)
    clearTimeout(hTimer3);
  hTimer3 = setTimeout(function() {
   try {
    hTimer3 = 0;
    div.style.visibility = 'hidden';
   }catch(e){ console.log(e);}
  }, 2222);
  doc.addEventListener("mousemove", OnMouseMove2);
}

function OnMouseMove2(event) {
 try {
  if(event.clientX+20 < div.offsetLeft || (event.clientY > 80 && event.clientY+80 < window.innerHeight))
    CancelAutoHide(event);
 }catch(e){ console.log(e); }
}

function CancelAutoHide(event){
  if(hTimer3) {
    clearTimeout(hTimer3);
    hTimer3 = 0;
  } else div.style.visibility = '';
  doc.removeEventListener("mousemove", OnMouseMove2);
}


function shouldShow() {
  if(isTop) {
    var minWidth = getValue('miniClockMinWidth', 999);
    if(!minWidth || minWidth >= window.screen.availWidth || window.outerWidth >= minWidth)
      return true;
  } else if(isFullScreen())
    return true;
  return false;
}

function OnReSize() {
  try {
    //console.log('ReSize '+window.outerWidth+'x'+window.outerHeight);
    clearInterval(hTimer);
    if(shouldShow()) {
      OnTimer();
      div.style.display = 'block';
      hTimer = setInterval(OnTimer, 1900);
    } else {
      div.style.display = 'none';
      hTimer = 0;
    }
    if(wrapper)
      OnMouseMove();
  } catch(e) { console.log(e); }
}

function InsertClock(here) {
  div.parentNode.removeChild(div);
  here.appendChild(div);
}

function AtBottomClicked() {
  if(div.style.top == 'unset') {
    div.style.top = '';
    div.style.bottom = '';
    setValue('atbottom', false);
  } else {
    div.style.top = 'unset';
    div.style.bottom = '0px';
    setValue('atbottom', true);
  }
  div.blur();
}

function FontChange(event) {
  var frm = doc.createElement('form');
  frm.innerHTML = '<input id="us_MiniClockFontSel" type="number" value="'+fontSize+'" min="3" max="200" maxlength="3" size="3" style="text-align:right;width:84px;"><button type="submit"> OK </button>';
  frm.style = 'position:fixed!important;top:55px;right:10px;color:#222;background-color:#181818;border:1px solid gray;padding:10px 17px;font:18pt normal sans-serif;z-index:2147483647;';
  var inp = frm.firstChild, fse = FullScreenElement() || doc.body;
  inp.onchange = function(event){
    div.style.fontSize = inp.value + 'pt';
    div.style.visibility = '';
  }
  frm.lastChild.onclick = function(event){
    setValue('fontsize', fontSize = inp.value);
    fse.removeChild(frm);
  };
  fse.appendChild(frm);
  inp.focus();
}



function MenuCreate(menu) {
  menu.setAttribute('type','context');
  menu.setAttribute('id','us_MiniClockMenu');
  menu.innerHTML='<menuitem label="hide mini clock"></menuitem><menuitem label="adjust clock size"></menuitem><menuitem label="clock at bottom" type="checkbox"></menuitem><menuitem label="allow clock in video tag" type="checkbox"></menuitem>';
  var item = menu.firstChild;
  item.onclick = function(event) { div.style.opacity='0'; };
  item = item.nextSibling;
  item.onclick = FontChange;
  item = item.nextSibling;
  item.onclick = AtBottomClicked;
  if(getValue('atbottom')) {
    div.style.top = 'unset';
    div.style.bottom = '0px';
    item.checked = true;
  }
  item = item.nextSibling;
  item.onclick = function() { setValue('allowAtVideo', !getValue('allowAtVideo')); };
  if(getValue('allowAtVideo')) {
    item.checked = true;
  }
  div.appendChild(menu);
  div.setAttribute('contextmenu','us_MiniClockMenu');
  div.oncontextmenu = function(event) {
    menu.lastChild.checked = getValue('allowAtVideo')==true;
    CancelAutoHide(event);
  };
}

function create() {
  div = doc.createElement('div');
  div.id = 'us_MiniClock';
  div.appendChild(spn = doc.createElement('span'));
  doc.body.appendChild(div);
  OnReSize();
  if(!doc.getElementById('us_MiniClockStyle')) {
    fontSize = getValue('fontsize', 18);
    var style = addStyle('#us_MiniClock{position:fixed!important;top:0px;right:0px;width:auto;color:#E8E8E8;background-color:#181818;border:1px solid gray;padding:1px 7px;font:'
    +fontSize+'pt normal sans-serif;z-index:2147483647;}@media print{#us_MiniClock{display:none!important;}} \
    #mcwrfsb{position:fixed;right:0px;bottom:0px;cursor:pointer;border:1px solid;height:25px;width:25px;}');
    style.id = 'us_MiniClockStyle';
    if(div.offsetLeft) {
      window.onresize = OnReSize;
      div.addEventListener("mouseenter", OnMouseEnter, false);
      var menu = MenuCreate(doc.createElement('menu'));
    } else {
      clearInterval(hTimer);
      div.parentNode.removeChild(div);
      div = null;
    }
  }
}

function init() {
  try {
    if(!div)
      create();
    var fse = FullScreenElement() || doc.body, wrp, st;
    if(fse.contains(div))
      return;
    switch(fse.tagName) {
      case 'VIDEO':
        if(getValue('allowAtVideo')) {
          st = !fse.paused;
          EndFullScreenMode();
          wrp = fse.parentNode;
          if(wrp.id != 'miniClockWrapper') {
            wrapper = wrp = doc.createElement('div');
            wrp.id = 'miniClockWrapper';
            wrp.innerHTML = '<div id="mcwrfsb">&nbsp;</div>';
            wrp.firstChild.onclick = function(e) { if(!EndFullScreenMode()) SetFullScreenMode(wrapper); };
            wrp.appendChild(fse.parentNode.replaceChild(wrp, fse));
            wrp.addEventListener("mousemove", OnMouseMove);
          }
          if(isFullScreen())// why must it be asynchroniously?
            ForceEndFullscreen();
          SetFullScreenMode(wrp);
          InsertClock(wrp);
          if(st && fse.paused) {
            //console.log('video stopped while processed');
            fse.play();
          }
          window.setTimeout( function() {
            if(!isFullScreen())
              console.log('Clock could not reactivate full screen mode. Make sure, full-screen-api.allow-trusted-requests-only is false in about:config');
          }, 500);
        } else console.log('Clock not allowed in video tags. See https://greasyfork.org/de/scripts/11402-mini-clock for details.');
      case 'IFRAME':
        break;
      default:
        InsertClock(fse);
    }
  } catch(e){ console.log(e); }
}

/*
doc.addEventListener("visibilitychange", function() {
  if(doc.visibilityState == "hidden") {
    doc.documentElement.style.display = "none";
  } else {
    doc.documentElement.style.display = "block";
  }
});
*/

//public domain by gnblizz