Skip to content

Instantly share code, notes, and snippets.

@HT-7
Last active August 12, 2023 18:44
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
Star You must be signed in to star a gist
Embed
What would you like to do?

Revisions

  1. HT-7 revised this gist Aug 12, 2023. 1 changed file with 26 additions and 17 deletions.
    43 changes: 26 additions & 17 deletions timerUI.js
    @@ -1,5 +1,3 @@
    // TimerUI – for home cinemas

    // == Dependencies == // == Dependencies ==


    var mediaType; // for compatibility var mediaType; // for compatibility
    @@ -557,6 +555,12 @@ function timeUI() {
    timerUI.gradient_placeholder = document.getElementById("timerUI_bottom_gradient"); timerUI.gradient_placeholder = document.getElementById("timerUI_bottom_gradient");
    addStyle("#timerUI #timerUI_bottom_gradient { display:block; position:fixed; background-image:linear-gradient(to top,"+timerUI.gradient+", rgba(0,0,0,0) ); opacity:1; height:80pt; width:100%; left:0; bottom:0; pointer-events:none; }", timerUI.div); addStyle("#timerUI #timerUI_bottom_gradient { display:block; position:fixed; background-image:linear-gradient(to top,"+timerUI.gradient+", rgba(0,0,0,0) ); opacity:1; height:80pt; width:100%; left:0; bottom:0; pointer-events:none; }", timerUI.div);


    // play/pause symbol
    appendChildWithID("button","timerUI_playback_status",timerUI.div );
    timerUI.status = document.getElementById("timerUI_playback_status");
    timerUI.status.innerHTML="■";
    addStyle("#timerUI #timerUI_playback_status { display:block; position:fixed; cursor:pointer; color:"+timerUI.color+"; font-size:24pt; line-height:40pt; bottom:30pt; right:3pt; font-family:none; }", timerUI.div);

    // progress bar // progress bar
    appendChildWithID("div","timerUI_progress_bar",timerUI.div ); appendChildWithID("div","timerUI_progress_bar",timerUI.div );
    timerUI.bar = document.getElementById("timerUI_progress_bar"); timerUI.bar = document.getElementById("timerUI_progress_bar");
    @@ -567,23 +571,14 @@ function timeUI() {
    timerUI.buffer_bar = document.getElementById("timerUI_buffer_bar"); timerUI.buffer_bar = document.getElementById("timerUI_buffer_bar");
    addStyle("#timerUI #timerUI_buffer_bar, #timerUI .timerUI_buffer_segment { display:block; position:fixed; background-color:"+timerUI.color+"; height:8pt; width:75%; left:0; bottom:0; opacity:0.4; } #timerUI .timerUI_buffer_segment { opacity:1; }", timerUI.div); addStyle("#timerUI #timerUI_buffer_bar, #timerUI .timerUI_buffer_segment { display:block; position:fixed; background-color:"+timerUI.color+"; height:8pt; width:75%; left:0; bottom:0; opacity:0.4; } #timerUI .timerUI_buffer_segment { opacity:1; }", timerUI.div);


    // media title
    appendChildWithID("div","timerUI_media_title",timerUI.div );
    timerUI.title = document.getElementById("timerUI_media_title");
    timerUI.title.innerHTML = timerUI.getTitle();
    addStyle("#timerUI #timerUI_media_title { position:fixed; text-shadow: 0 0 5px black; display:inline; display:-webkit-box; bottom:15pt; left:2pt; color:"+timerUI.color+"; font-family:"+timerUI.font_pack+"; font-size:20pt; width:60%; max-width:calc(100% - 500px); text-overflow: ellipsis; overflow: hidden; -webkit-box-orient: vertical; -webkit-line-clamp: 2; vertical-align: bottom;", timerUI.div);


    // timer // timer
    appendChildWithID("button","timerUI_media_timer",timerUI.div ); appendChildWithID("button","timerUI_media_timer",timerUI.div );
    timerUI.time = document.getElementById("timerUI_media_timer"); timerUI.time = document.getElementById("timerUI_media_timer");
    timerUI.time.innerHTML="00:00:00"; timerUI.time.innerHTML="00:00:00";
    addStyle("#timerUI #timerUI_media_timer { display:block; color:"+timerUI.color+"; position:fixed; cursor:pointer; font-size:50pt; text-shadow: 0 0 20px black; line-height:60pt; bottom:10pt; right:30pt; text-align:right; font-weight:400; font-family:"+timerUI.font_pack+"; } #timerUI #timerUI_media_timer .timer_linefeed { display:none; }", timerUI.div); addStyle("#timerUI #timerUI_media_timer { display:block; color:"+timerUI.color+"; position:fixed; cursor:pointer; font-size:50pt; text-shadow: 0 0 20px black; line-height:60pt; bottom:10pt; right:30pt; text-align:right; font-weight:400; font-family:"+timerUI.font_pack+"; } #timerUI #timerUI_media_timer .timer_linefeed { display:none; }", timerUI.div);


    // play/pause symbol
    appendChildWithID("button","timerUI_playback_status",timerUI.div );
    timerUI.status = document.getElementById("timerUI_playback_status");
    timerUI.status.innerHTML="■";
    addStyle("#timerUI #timerUI_playback_status { display:block; position:fixed; cursor:pointer; color:"+timerUI.color+"; font-size:24pt; line-height:40pt; bottom:30pt; right:3pt; font-family:none; }", timerUI.div);


    // progress bar placeholder – put last to be at the top in stacking context // progress bar placeholder – put last to be at the top in stacking context
    appendChildWithID("div","timerUI_progress_placeholder",timerUI.div ); appendChildWithID("div","timerUI_progress_placeholder",timerUI.div );
    @@ -594,6 +589,12 @@ function timeUI() {
    addStyle("@media (max-width:"+timerUI.width_breakpoint+"px) { #timerUI #timerUI_media_timer { font-size:30pt; line-height:24pt; bottom:15pt; } #timerUI #timerUI_playback_status { bottom:10pt; } } @media (max-width:500px) { #timerUI #timerUI_media_timer .timer_linefeed { display:inline; } #timerUI #timerUI_media_timer .timer_slash { display:none; } } @media (max-width:"+timerUI.width_breakpoint+"px) { #timerUI #timerUI_buffer_bar { font-size:10pt; -webkit-line-clamp: 3; max-width:60%;} } @media (max-width:480px) { #timerUI #timerUI_buffer_bar { display: none; } } ", timerUI.div); addStyle("@media (max-width:"+timerUI.width_breakpoint+"px) { #timerUI #timerUI_media_timer { font-size:30pt; line-height:24pt; bottom:15pt; } #timerUI #timerUI_playback_status { bottom:10pt; } } @media (max-width:500px) { #timerUI #timerUI_media_timer .timer_linefeed { display:inline; } #timerUI #timerUI_media_timer .timer_slash { display:none; } } @media (max-width:"+timerUI.width_breakpoint+"px) { #timerUI #timerUI_buffer_bar { font-size:10pt; -webkit-line-clamp: 3; max-width:60%;} } @media (max-width:480px) { #timerUI #timerUI_buffer_bar { display: none; } } ", timerUI.div);
    timerUI.div.lastElementChild.classList.add("timerUI_responsive"); timerUI.div.lastElementChild.classList.add("timerUI_responsive");


    // media title
    appendChildWithID("div","timerUI_media_title",timerUI.div );
    timerUI.title = document.getElementById("timerUI_media_title");
    timerUI.title.innerHTML = timerUI.getTitle();
    addStyle("#timerUI #timerUI_media_title { position:fixed; text-shadow: 0 0 5px black; display:inline; display:-webkit-box; bottom:15pt; left:2pt; color:"+timerUI.color+"; font-family:"+timerUI.font_pack+"; font-size:20pt; width:60%; max-width:calc(100% - 500px); text-overflow: ellipsis; overflow: hidden; -webkit-box-orient: vertical; -webkit-line-clamp: 2; vertical-align: bottom;", timerUI.div);

    timerUI.domainRules(); // load domain rules after initializing timer elements timerUI.domainRules(); // load domain rules after initializing timer elements


    // update timer during playback every fifteenth of a second and while mouse is dragging progress bar // update timer during playback every fifteenth of a second and while mouse is dragging progress bar
    @@ -632,15 +633,15 @@ function timeUI() {
    timerUI.update(); timerUI.updateBufferBar(true); timerUI.update(); timerUI.updateBufferBar(true);
    } }
    }; };
    function dragSeekBar(m){ // currently unused /* function dragSeekBar(m){ // currently unused
    m = m || window.event; m = m || window.event;
    var clickPos = m.pageX / timerUI.bar_placeholder.offsetWidth; var clickPos = m.pageX / timerUI.bar_placeholder.offsetWidth;
    var dragSeekBarInterval = setInterval( function() { var dragSeekBarInterval = setInterval( function() {
    media_element.currentTime = media_element.duration * clickPos; media_element.currentTime = media_element.duration * clickPos;
    timerUI.update(); timerUI.update();
    if (! mousedown_status) { clearInterval(dragSeekBarInterval); return; } if (! mousedown_status) { clearInterval(dragSeekBarInterval); return; }
    }, 200); }, 200);
    } } */
    timerUI.bar_placeholder.addEventListener("mousedown", timerUI.clickSeekBar ); timerUI.bar_placeholder.addEventListener("mousedown", timerUI.clickSeekBar );
    // (incomplete) timerUI.bar_placeholder.addEventListener("mousemove", dragSeekBar ); // (incomplete) timerUI.bar_placeholder.addEventListener("mousemove", dragSeekBar );
    // (obsolete) timerUI.bar_placeholder.addEventListener("mouseup", clickSeekBar ); // (obsolete) timerUI.bar_placeholder.addEventListener("mouseup", clickSeekBar );
    @@ -684,7 +685,10 @@ timerUI.domainRules = function() {
    } }


    // activate light mode on wikis and Dailymotion due to bright backgrounds // activate light mode on wikis and Dailymotion due to bright backgrounds
    if ( isDomain("wiki") || isDomain("dailymotion.com") || isDomain("ghostarchive.org") ) { if ( isDomain("wiki")
    || isDomain("dailymotion.com")
    || isDomain("ghostarchive.org")
    || is_archive_library() ) {
    timerUI.light_mode_on(); timerUI.light_mode_on();
    } }


    @@ -717,7 +721,7 @@ timerUI.titleDomainRules = function() {
    } }


    // Internet Archive library only, not Wayback Machine // Internet Archive library only, not Wayback Machine
    if ( isDomain(/^(www.)?archive.org/) && ! isDomain("web.archive.org") ) { if ( is_archive_library() ) {
    // get media title from page title before first colon // get media title from page title before first colon
    timerUI.newTitle = (document.location.href+"").substring((document.location.href+" ").search(/\/(?:.(?!\/))+\/?$/)+1 ); timerUI.newTitle = (document.location.href+"").substring((document.location.href+" ").search(/\/(?:.(?!\/))+\/?$/)+1 );
    // after last slash (additional space prevents full URL from being matched) // after last slash (additional space prevents full URL from being matched)
    @@ -737,6 +741,11 @@ timerUI.titleDomainRules = function() {
    } }
    }; };


    function is_archive_library() {
    // function to check if the current page is an Archive.org library page and not the Wayback Machine
    return (isDomain(/^(www.)?archive.org/) && ! isDomain("web.archive.org") );
    // automatically returns true if the condition is met and false otherwise
    }
    function is_WaybackEmbed() { function is_WaybackEmbed() {
    // checks if the current page is media embedded on the Wayback Machine, for code deduplication. // checks if the current page is media embedded on the Wayback Machine, for code deduplication.
    if ( isDomain("web.archive.org") || isDomain("wayback.archive.org") && document.title=="Wayback Machine" && document.getElementsByTagName("iframe")[0] ) { if ( isDomain("web.archive.org") || isDomain("wayback.archive.org") && document.title=="Wayback Machine" && document.getElementsByTagName("iframe")[0] ) {
  2. HT-7 revised this gist Jul 10, 2023. 1 changed file with 122 additions and 26 deletions.
    148 changes: 122 additions & 26 deletions timerUI.js
    @@ -58,8 +58,10 @@ function isDomain(domain) {
    } }


    // symbols // symbols
    var symbol_play = "▶︎ "; // thin space for alignment var media_symbol = {};
    var symbol_pause="❚ ❚"; // instead of "⏸" due to Edge browser putting an immutable blue box around it. media_symbol.play = "▶︎ "; // thin space for alignment
    media_symbol.pause="❚ ❚"; // instead of "⏸" due to Edge browser putting an immutable blue box around it.
    media_symbol.stop="■";


    // mousedown status // mousedown status
    var mousedown_status; var mousedown_status;
    @@ -162,6 +164,18 @@ var mediafileext = {
    "audio":[".mp3", ".wma", ".wav", ".ogg", ".opus", ".flac", ".oga", ".wma", ".aac", ".amr", ".alac", ".m4a"] "audio":[".mp3", ".wma", ".wav", ".ogg", ".opus", ".flac", ".oga", ".wma", ".aac", ".amr", ".alac", ".m4a"]
    }; };


    // "replaceAll()" polyfill for pre-2020 browsers
    // based on: https://stackoverflow.com/a/1144788

    function escapeRegExp(string) {
    return string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); // $& means the whole matched string
    }

    // renamed function to prevent interference with "replaceAll()" on browsers since 2020
    function replaceAll_polyfill(str, find, replacement) {
    return str.replace(new RegExp(escapeRegExp(find), 'g'), replacement);
    }

    // == Main code == // == Main code ==
    if (! timerUI) var timerUI = new Object({}); // create parent object if none exists if (! timerUI) var timerUI = new Object({}); // create parent object if none exists


    @@ -177,6 +191,7 @@ timerUI.interval = {};
    timerUI.show_remaining = 0; // 0: show elapsed time. 1: show remaining time. 2: show elapsed and total. timerUI.show_remaining = 0; // 0: show elapsed time. 1: show remaining time. 2: show elapsed and total.
    timerUI.update_during_seek = true; // update timer while dragging seek bar timerUI.update_during_seek = true; // update timer while dragging seek bar
    timerUI.color = "rgb(49,136,255)"; // #38F – using RGB for compatibility. timerUI.color = "rgb(49,136,255)"; // #38F – using RGB for compatibility.
    timerUI.default_color = "rgb(49,136,255)"; // memorize default color
    timerUI.gradient = "rgba(0,0,0,0.9)"; // using RGBA instead of hexadecimal for compatibility. timerUI.gradient = "rgba(0,0,0,0.9)"; // using RGBA instead of hexadecimal for compatibility.
    timerUI.font_pack = "din, futura, 'noto sans', 'open sans', ubuntu, 'segoe ui', verdana, tahoma, roboto, 'roboto light', arial, helvetica, 'trebuchet ms' ,'bitstream vera sans', sans-serif, consolas, monospace"; timerUI.font_pack = "din, futura, 'noto sans', 'open sans', ubuntu, 'segoe ui', verdana, tahoma, roboto, 'roboto light', arial, helvetica, 'trebuchet ms' ,'bitstream vera sans', sans-serif, consolas, monospace";
    timerUI.width_breakpoint = 768; // pixels timerUI.width_breakpoint = 768; // pixels
    @@ -205,7 +220,7 @@ timerUI.toggle.main = function() {
    timerUI.div.style.display = "block"; timerUI.div.style.display = "block";
    console.log("timerUI on"); console.log("timerUI on");
    } }
    timerUI.on ? timerUI.on = false : timerUI.on = true; if (timerUI.on) { timerUI.on = false; return false; } else { timerUI.on = true; return false; }
    } else { } else {
    console.warn(timerUI.msg.notimer); console.warn(timerUI.msg.notimer);
    timeUI(); timeUI();
    @@ -223,7 +238,11 @@ timerUI.toggle.buffer = function() {
    timerUI.buffer_bar.style.display = "block"; timerUI.buffer_bar.style.display = "block";
    console.log("timerUI buffer bar on"); console.log("timerUI buffer bar on");
    } }
    timerUI.buffer_on ? timerUI.buffer_on = false : timerUI.buffer_on = true; if (timerUI.buffer_on) {
    timerUI.buffer_on = false; return false;
    } else {
    timerUI.buffer_on = true; return true;
    }
    } else { } else {
    console.warn(timerUI.msg.notimer); console.warn(timerUI.msg.notimer);
    timeUI(); timeUI();
    @@ -241,7 +260,11 @@ timerUI.toggle.title = function() {
    timerUI.title.style.display = "block"; timerUI.title.style.display = "block";
    console.log("timerUI title on"); console.log("timerUI title on");
    } }
    timerUI.title_on ? timerUI.title_on = false : timerUI.title_on = true; if (timerUI.title_on) {
    timerUI.title_on = false; return false;
    } else {
    timerUI.title_on = true; return true;
    }
    } else { } else {
    console.warn(timerUI.msg.notimer); console.warn(timerUI.msg.notimer);
    timeUI(); timeUI();
    @@ -256,6 +279,8 @@ timerUI.getTitle = function() {
    if (customTitleElement) timerUI.newTitle = customTitleElement.innerHTML; if (customTitleElement) timerUI.newTitle = customTitleElement.innerHTML;
    else { // skipping this whole part if no custom title is specified else { // skipping this whole part if no custom title is specified
    timerUI.newTitle = document.title; timerUI.newTitle = document.title;
    // replace underscores with spaces
    timerUI.newTitle = replaceAll_polyfill(timerUI.newTitle, "_"," ");
    timerUI.titleDomainRules(); timerUI.titleDomainRules();
    } }
    if (media_element) { if (media_element) {
    @@ -270,6 +295,7 @@ timerUI.guessMediaType = function() {
    if (isDomain("youtube.com") || isDomain("dailymotion.com") ) return "video"; if (isDomain("youtube.com") || isDomain("dailymotion.com") ) return "video";
    if (document.location.pathname.search(/^\/video\//) > -1) return "video"; if (document.location.pathname.search(/^\/video\//) > -1) return "video";
    if (! media_element.videoWidth) return "audio"; // Detects files that only contain audio, even if they have a video file extension. if (! media_element.videoWidth) return "audio"; // Detects files that only contain audio, even if they have a video file extension.
    if (media_element.videoWidth > 0) return "video";
    if (checkFileExtension(mediafileext.video) ) return "video"; if (checkFileExtension(mediafileext.video) ) return "video";
    if (checkFileExtension(mediafileext.audio) ) return "audio"; if (checkFileExtension(mediafileext.audio) ) return "audio";
    return "unknown"; // if nothing detected return "unknown"; // if nothing detected
    @@ -324,13 +350,16 @@ timerUI.update = function() {
    break; break;
    } }


    media_element.paused == false ? if (media_element.paused) {
    timerUI.status.innerHTML=symbol_play timerUI.status.innerHTML=media_symbol.pause;
    : timerUI.status.innerHTML=symbol_pause; } else {

    timerUI.status.innerHTML=media_symbol.play;
    } else { timerUI.stop(); console.warn(timerUI.msg.nomedia); } }
    } else { timerUI.stop(); console.warn(timerUI.msg.nomedia); }
    }; };
    timerUI.updateTitle = function() { if (timerUI.title) timerUI.title.innerHTML = timerUI.getTitle(); }; timerUI.updateTitle = function() {
    if (timerUI.title) timerUI.title.innerHTML = timerUI.getTitle();
    };


    // update title on URL change // update title on URL change
    addEventListener('popstate', timerUI.updateTitle); addEventListener('popstate', timerUI.updateTitle);
    @@ -414,6 +443,12 @@ timerUI.set_buffer_segment = function(segment_number,start_pos,end_pos) {


    // colors // colors
    timerUI.setColor = function(newColor) { timerUI.setColor = function(newColor) {
    if (! timerUI.bar) {
    /* prevent running function before timerUI is properly loaded into the DOM to prevent exception */
    if (timerUI.debug_mode) console.debug("timerUI: setColor can not run before timerUI is properly loaded.");
    return false;
    }
    timerUI.previous_color = timerUI.color; // memorize previous setting
    newColor == "default" ? timerUI.color="rgb(49,136,255)" /* #38F */ : timerUI.color = newColor; newColor == "default" ? timerUI.color="rgb(49,136,255)" /* #38F */ : timerUI.color = newColor;


    timerUI.bar.style.backgroundColor=timerUI.color; timerUI.bar.style.backgroundColor=timerUI.color;
    @@ -423,8 +458,16 @@ timerUI.setColor = function(newColor) {
    timerUI.time.style.color=timerUI.color; timerUI.time.style.color=timerUI.color;
    timerUI.status.style.color=timerUI.color; timerUI.status.style.color=timerUI.color;
    timerUI.title.style.color=timerUI.color; timerUI.title.style.color=timerUI.color;

    // colour all buffer segments
    for (var count=0; count < timerUI.buffer_bar.childNodes.length; count++) {
    timerUI.buffer_bar.childNodes[count].style.backgroundColor=timerUI.color;
    }
    if (timerUI.debug_mode) console.debug("timerUI: color changed from "+timerUI.previous_color+" to "+timerUI.color);
    }; };


    timerUI.setColor("default"); // prevent mixed colours if the code is run multiple times

    timerUI.setGradient = function(newGradient) { timerUI.setGradient = function(newGradient) {
    newGradient == "default" ? timerUI.gradient="rgba(0,0,0,0.9)" : timerUI.gradient = newGradient; newGradient == "default" ? timerUI.gradient="rgba(0,0,0,0.9)" : timerUI.gradient = newGradient;
    timerUI.gradient_placeholder.style.backgroundImage="linear-gradient(to top,"+timerUI.gradient+", rgba(0,0,0,0) )"; timerUI.gradient_placeholder.style.backgroundImage="linear-gradient(to top,"+timerUI.gradient+", rgba(0,0,0,0) )";
    @@ -435,11 +478,55 @@ timerUI.setFont = function(newFont) {
    timerUI.title.style.fontFamily=newFont; timerUI.title.style.fontFamily=newFont;
    }; };



    // light mode

    timerUI.light_mode = false; // default

    timerUI.light_mode_on = function() {
    timerUI.light_mode = true;
    timerUI.color_before_light_mode = timerUI.color;
    // improves visibility:
    if (timerUI.setColor) /* exists*/ {
    timerUI.setColor("lightblue");
    } else { return false; /* error */ }
    if (timerUI.gradient_placeholder) /* exists*/ {
    timerUI.gradient_placeholder.style.backgroundColor="rgba(0, 0, 0, 0.5)";
    } else { return false; /* error */ }
    };

    timerUI.light_mode_off = function() {
    timerUI.light_mode = false;
    if (timerUI.setColor) /* exists*/ {
    timerUI.setColor("lightblue");
    } else { return false; /* error */ }
    if (timerUI.gradient_placeholder) /* exists*/ {
    timerUI.gradient_placeholder.style.backgroundColor=""; // fall back to default
    } else { return false; /* error */ }
    };

    timerUI.toggle.light_mode = function() {
    if ( ! timerUI.light_mode ) /* if light mode is deactivated */ {
    timerUI.light_mode_on();
    return true;
    } else {
    timerUI.light_mode_off();
    return false;
    }
    };

    // "stop" icon

    timerUI.stop = function() { timerUI.stop = function() {
    timerUI.status.innerHTML="■"; timerUI.status.innerHTML=media_symbol.stop;
    timerUI.bar.style.width=0; timerUI.bar.style.width=0;
    timerUI.buffer_bar.style.width=0; timerUI.buffer_bar.style.width=0;
    timerUI.time.innerHTML=HMStimer(undefined); // appearance of stopped timer consistent with the show_remaining setting
    if (timerUI.show_remaining == 2) {
    timerUI.time.innerHTML=MStimer(undefined)+" / "+MStimer(undefined);
    } else {
    timerUI.time.innerHTML=HMStimer(undefined);
    }
    }; };


    // Additional checks to ensure the player is detected // Additional checks to ensure the player is detected
    @@ -451,7 +538,6 @@ function timeUI() {
    // slightly different name to prevent naming collision with timerUI object // slightly different name to prevent naming collision with timerUI object


    checkMediaType(); checkMediaType();
    timerUI.domainRules(); // load domain rules
    // add timerUI if it does not already exist // add timerUI if it does not already exist
    if ( ( ! document.getElementById("timerUI") ) && playerExists || timerUI.override_check ) { if ( ( ! document.getElementById("timerUI") ) && playerExists || timerUI.override_check ) {


    @@ -462,30 +548,30 @@ function timeUI() {
    timerUI.div = document.getElementById("timerUI"); timerUI.div = document.getElementById("timerUI");


    // button styling // button styling
    addStyle("#timerUI button { background:none; border:none; outline:none; line-height:unset; } #timerUI button:hover { filter: brightness(1.2); } #timerUI button:active { filter: brightness(0.6); }", timerUI.div); addStyle("#timerUI button { background:none; border:none; outline:none; line-height:unset; padding:0; margin:0; } #timerUI button:hover { filter: brightness(1.2); } #timerUI button:active { filter: brightness(0.6); }", timerUI.div);
    // to suppress button background and border on earlier browser versions // to suppress button background and border on earlier browser versions
    timerUI.div.lastElementChild.classList.add("timerUI_buttons"); // label to improve visibility in page inspector timerUI.div.lastElementChild.classList.add("timerUI_buttons"); // label to improve visibility in page inspector


    // background gradient // background gradient
    appendChildWithID("div","timerUI_bottom_gradient",timerUI.div ); appendChildWithID("div","timerUI_bottom_gradient",timerUI.div );
    timerUI.gradient_placeholder = document.getElementById("timerUI_bottom_gradient"); timerUI.gradient_placeholder = document.getElementById("timerUI_bottom_gradient");
    addStyle("#timerUI #timerUI_bottom_gradient { display:block; position:fixed; background-image:linear-gradient(to top,"+timerUI.gradient+", rgba(0,0,0,0) ); opacity:1; height:80pt; width:100%; bottom:0; pointer-events:none; }", timerUI.div); addStyle("#timerUI #timerUI_bottom_gradient { display:block; position:fixed; background-image:linear-gradient(to top,"+timerUI.gradient+", rgba(0,0,0,0) ); opacity:1; height:80pt; width:100%; left:0; bottom:0; pointer-events:none; }", timerUI.div);


    // progress bar // progress bar
    appendChildWithID("div","timerUI_progress_bar",timerUI.div ); appendChildWithID("div","timerUI_progress_bar",timerUI.div );
    timerUI.bar = document.getElementById("timerUI_progress_bar"); timerUI.bar = document.getElementById("timerUI_progress_bar");
    addStyle("#timerUI #timerUI_progress_bar { display:block; position:fixed; background-color:"+timerUI.color+"; box-shadow: 0 0 30px 0px "+timerUI.color+"; height:8pt; width:50%; bottom:0; }", timerUI.div); addStyle("#timerUI #timerUI_progress_bar { display:block; position:fixed; background-color:"+timerUI.color+"; box-shadow: 0 0 30px 0px "+timerUI.color+"; height:8pt; width:50%; left:0; bottom:0; }", timerUI.div);


    // buffer bar // buffer bar
    appendChildWithID("div","timerUI_buffer_bar",timerUI.div ); appendChildWithID("div","timerUI_buffer_bar",timerUI.div );
    timerUI.buffer_bar = document.getElementById("timerUI_buffer_bar"); timerUI.buffer_bar = document.getElementById("timerUI_buffer_bar");
    addStyle("#timerUI #timerUI_buffer_bar, #timerUI .timerUI_buffer_segment { display:block; position:fixed; background-color:"+timerUI.color+"; height:8pt; width:75%; bottom:0; opacity:0.4; } #timerUI .timerUI_buffer_segment { opacity:1; }", timerUI.div); addStyle("#timerUI #timerUI_buffer_bar, #timerUI .timerUI_buffer_segment { display:block; position:fixed; background-color:"+timerUI.color+"; height:8pt; width:75%; left:0; bottom:0; opacity:0.4; } #timerUI .timerUI_buffer_segment { opacity:1; }", timerUI.div);


    // media title // media title
    appendChildWithID("div","timerUI_media_title",timerUI.div ); appendChildWithID("div","timerUI_media_title",timerUI.div );
    timerUI.title = document.getElementById("timerUI_media_title"); timerUI.title = document.getElementById("timerUI_media_title");
    timerUI.title.innerHTML = timerUI.getTitle(); timerUI.title.innerHTML = timerUI.getTitle();
    addStyle("#timerUI #timerUI_media_title { position:fixed; text-shadow: 0 0 5px black; display:inline; display:-webkit-box; bottom:15pt; left:2pt; color:"+timerUI.color+"; font-family:"+timerUI.font_pack+"; font-size:20pt; width:60%; max-width:calc(100% - 500px); text-overflow: ellipsis; overflow: hidden; -webkit-box-orient: vertical; -webkit-line-clamp: 2; vertical-align: bottom;"); addStyle("#timerUI #timerUI_media_title { position:fixed; text-shadow: 0 0 5px black; display:inline; display:-webkit-box; bottom:15pt; left:2pt; color:"+timerUI.color+"; font-family:"+timerUI.font_pack+"; font-size:20pt; width:60%; max-width:calc(100% - 500px); text-overflow: ellipsis; overflow: hidden; -webkit-box-orient: vertical; -webkit-line-clamp: 2; vertical-align: bottom;", timerUI.div);


    // timer // timer
    appendChildWithID("button","timerUI_media_timer",timerUI.div ); appendChildWithID("button","timerUI_media_timer",timerUI.div );
    @@ -502,12 +588,13 @@ function timeUI() {
    // progress bar placeholder – put last to be at the top in stacking context // progress bar placeholder – put last to be at the top in stacking context
    appendChildWithID("div","timerUI_progress_placeholder",timerUI.div ); appendChildWithID("div","timerUI_progress_placeholder",timerUI.div );
    timerUI.bar_placeholder = document.getElementById("timerUI_progress_placeholder"); timerUI.bar_placeholder = document.getElementById("timerUI_progress_placeholder");
    addStyle("#timerUI #timerUI_progress_placeholder { display:block; position:fixed; cursor:pointer; background-color:grey; height:8pt; width:100%; bottom:0; opacity:0.2; }", timerUI.div); addStyle("#timerUI #timerUI_progress_placeholder { display:block; position:fixed; cursor:pointer; background-color:grey; height:8pt; width:100%; left:0; bottom:0; opacity:0.2; }", timerUI.div);


    // responsive - at bottom to be able to override CSS properties without !important flag. // responsive - at bottom to be able to override CSS properties without !important flag.
    addStyle("@media (max-width:"+timerUI.width_breakpoint+"px) { #timerUI #timerUI_media_timer { font-size:30pt; line-height:24pt; bottom:15pt; } #timerUI #timerUI_playback_status { bottom:10pt; } } @media (max-width:500px) { #timerUI #timerUI_media_timer .timer_linefeed { display:inline; } #timerUI #timerUI_media_timer .timer_slash { display:none; } } @media (max-width:"+timerUI.width_breakpoint+"px) { #timerUI #timerUI_buffer_bar { font-size:10pt; -webkit-line-clamp: 3; max-width:60%;} } @media (max-width:480px) { #timerUI #timerUI_buffer_bar { display: none; } } ", timerUI.div); addStyle("@media (max-width:"+timerUI.width_breakpoint+"px) { #timerUI #timerUI_media_timer { font-size:30pt; line-height:24pt; bottom:15pt; } #timerUI #timerUI_playback_status { bottom:10pt; } } @media (max-width:500px) { #timerUI #timerUI_media_timer .timer_linefeed { display:inline; } #timerUI #timerUI_media_timer .timer_slash { display:none; } } @media (max-width:"+timerUI.width_breakpoint+"px) { #timerUI #timerUI_buffer_bar { font-size:10pt; -webkit-line-clamp: 3; max-width:60%;} } @media (max-width:480px) { #timerUI #timerUI_buffer_bar { display: none; } } ", timerUI.div);
    timerUI.div.lastElementChild.classList.add("timerUI_responsive"); timerUI.div.lastElementChild.classList.add("timerUI_responsive");


    timerUI.domainRules(); // load domain rules after initializing timer elements


    // update timer during playback every fifteenth of a second and while mouse is dragging progress bar // update timer during playback every fifteenth of a second and while mouse is dragging progress bar
    timerUI.interval.update = setInterval( timerUI.interval.update = setInterval(
    @@ -535,7 +622,7 @@ function timeUI() {
    timerUI.update(); timerUI.updateBufferBar(true); timerUI.update(); timerUI.updateBufferBar(true);
    }; };


    // clickable progress bar (experimental) - no "timerUI." notation because inside main function // clickable progress bar (experimental) - "clickPos" has no "timerUI." notation because it is inside the main function.
    timerUI.clickSeekBar = function(m){ timerUI.clickSeekBar = function(m){
    if (media_element) { if (media_element) {
    var clickPos = m.clientX / timerUI.bar_placeholder.offsetWidth; var clickPos = m.clientX / timerUI.bar_placeholder.offsetWidth;
    @@ -596,6 +683,11 @@ timerUI.domainRules = function() {
    customTitleElement = document.getElementById("media-title"); // for unlisted videos (Dailymotion only displays the video title in the HTML page title for public videos) customTitleElement = document.getElementById("media-title"); // for unlisted videos (Dailymotion only displays the video title in the HTML page title for public videos)
    } }


    // activate light mode on wikis and Dailymotion due to bright backgrounds
    if ( isDomain("wiki") || isDomain("dailymotion.com") || isDomain("ghostarchive.org") ) {
    timerUI.light_mode_on();
    }

    // media embedded on Wayback Machine // media embedded on Wayback Machine
    if ( is_WaybackEmbed() ) { if ( is_WaybackEmbed() ) {
    tmp = document.getElementsByTagName("iframe")[0]; // iframe in temporary variable to deduplicate code tmp = document.getElementsByTagName("iframe")[0]; // iframe in temporary variable to deduplicate code
    @@ -618,19 +710,23 @@ timerUI.titleDomainRules = function() {
    // Match both normal dash "-" and ndash "–", since the German-language wikis use the latter. // Match both normal dash "-" and ndash "–", since the German-language wikis use the latter.
    timerUI.newTitle = decodeURI(timerUI.newTitle.substring(0,timerUI.newTitle.search(/(-|)(?:.(?!(-|)))+$/)-1 ) ); timerUI.newTitle = decodeURI(timerUI.newTitle.substring(0,timerUI.newTitle.search(/(-|)(?:.(?!(-|)))+$/)-1 ) );
    } }
    // remove "File:" prefix on wikis // remove "File:" prefix on wikis and
    if ( isDomain("wiki") ) { if ( isDomain("wiki") ) {
    if (document.title.search(/^File:/) == 0 ) timerUI.newTitle = timerUI.newTitle.substring(5); if (document.title.search(/^File:/) == 0 ) timerUI.newTitle = timerUI.newTitle.substring(5);
    if (document.title.search(/^Datei:/) == 0 ) timerUI.newTitle = timerUI.newTitle.substring(6); if (document.title.search(/^Datei:/) == 0 ) timerUI.newTitle = timerUI.newTitle.substring(6);
    } }


    // Internet Archive library only, not Wayback Machine // Internet Archive library only, not Wayback Machine
    if ( isDomain("archive.org") && ! isDomain("web.archive.org") ) { if ( isDomain(/^(www.)?archive.org/) && ! isDomain("web.archive.org") ) {
    // get media title from page title before first colon // get media title from page title before first colon
    timerUI.newTitle = (document.location.href+"").substring((document.location.href+" ").search(/\/(?:.(?!\/))+\/?$/)+1 ); timerUI.newTitle = (document.location.href+"").substring((document.location.href+" ").search(/\/(?:.(?!\/))+\/?$/)+1 );
    // after last slash (additional space prevents full URL from being matched) // after last slash (additional space prevents full URL from being matched)
    if (timerUI.newTitle != "") { timerUI.newTitle += " - "; } timerUI.archive_org_title = document.title.substring(0,document.title.search(/:(?:.(?!:))+(?:.(?!:))+/)-1 );
    timerUI.newTitle += document.title.substring(0,document.title.search(/:(?:.(?!:))+(?:.(?!:))+/)-1 ); if (timerUI.archive_org_title.length > 0) /* only append " - " if a title exists. */ {
    /* only append " - " if a title exists. */
    if (timerUI.newTitle != "") { timerUI.newTitle += " - "; }
    timerUI.newTitle += timerUI.archive_org_title;
    }
    // trim after second-last colon, -1 to remove space at end // trim after second-last colon, -1 to remove space at end


    timerUI.newTitle=decodeURI(timerUI.newTitle); // prevent spaces from turning into "%20". timerUI.newTitle=decodeURI(timerUI.newTitle); // prevent spaces from turning into "%20".
    @@ -655,4 +751,4 @@ function is_WaybackEmbed() {




    // == Master function == // == Master function ==
    timeUI(); timeUI();
  3. HT-7 revised this gist Mar 14, 2023. 1 changed file with 1 addition and 3 deletions.
    4 changes: 1 addition & 3 deletions timerUI.js
    @@ -655,6 +655,4 @@ function is_WaybackEmbed() {




    // == Master function == // == Master function ==
    timeUI(); timeUI();

    }
  4. HT-7 revised this gist Mar 14, 2023. 1 changed file with 11 additions and 8 deletions.
    19 changes: 11 additions & 8 deletions timerUI.js
    @@ -158,8 +158,8 @@ function togglePlay(media_element) {


    // media file extension list // media file extension list
    var mediafileext = { var mediafileext = {
    "video":[".mp4",".mpg",".mpeg",".mts",".mt2s",".m4v",".ts",".ogv",".wmv",".3gp",".3gpp"], "video":[".mp4", ".mpg", ".mpeg", ".mts", ".mt2s", ".m4v", ".ts", ".ogv", ".wmv", ".3gp", ".3gpp", ".webm"],
    "audio":[".mp3",".wma",".wav",".ogg",".opus",".flac",".oga",".wma",".aac",".amr",".alac",".m4a"] "audio":[".mp3", ".wma", ".wav", ".ogg", ".opus", ".flac", ".oga", ".wma", ".aac", ".amr", ".alac", ".m4a"]
    }; };


    // == Main code == // == Main code ==
    @@ -260,7 +260,7 @@ timerUI.getTitle = function() {
    } }
    if (media_element) { if (media_element) {
    timerUI.updateFileIcon(); timerUI.updateFileIcon();
    return timerUI.fileIcon+"&nbsp;"+timerUI.newTitle; return timerUI.file_icon+"&nbsp;"+timerUI.newTitle;
    } else { } else {
    return "TimerUI – designed for home cinemas"; return "TimerUI – designed for home cinemas";
    } }
    @@ -269,17 +269,18 @@ timerUI.getTitle = function() {
    timerUI.guessMediaType = function() { timerUI.guessMediaType = function() {
    if (isDomain("youtube.com") || isDomain("dailymotion.com") ) return "video"; if (isDomain("youtube.com") || isDomain("dailymotion.com") ) return "video";
    if (document.location.pathname.search(/^\/video\//) > -1) return "video"; if (document.location.pathname.search(/^\/video\//) > -1) return "video";
    if (! media_element.videoWidth) return "audio"; // Detects files that only contain audio, even if they have a video file extension.
    if (checkFileExtension(mediafileext.video) ) return "video"; if (checkFileExtension(mediafileext.video) ) return "video";
    if (checkFileExtension(mediafileext.audio) ) return "audio"; if (checkFileExtension(mediafileext.audio) ) return "audio";
    return "unknown"; // if nothing detected return "unknown"; // if nothing detected
    }; };


    timerUI.updateFileIcon = function() { timerUI.updateFileIcon = function() {
    timerUI.fileIcon = timerUI.guessMediaType(); timerUI.file_icon = timerUI.guessMediaType();
    switch(timerUI.fileIcon) { switch(timerUI.file_icon) {
    case "video": timerUI.fileIcon = "🎞️"; break; case "video": timerUI.file_icon = "🎞️"; break;
    case "audio": timerUI.fileIcon = "♫"; break; case "audio": timerUI.file_icon = "♫"; break;
    case "unknown": timerUI.fileIcon = "📄"; break; case "unknown": timerUI.file_icon = "📄"; break;
    } }
    }; };


    @@ -655,3 +656,5 @@ function is_WaybackEmbed() {


    // == Master function == // == Master function ==
    timeUI(); timeUI();

    }
  5. HT-7 revised this gist Nov 5, 2022. 1 changed file with 71 additions and 31 deletions.
    102 changes: 71 additions & 31 deletions timerUI.js
    @@ -6,13 +6,18 @@ var mediaType; // for compatibility
    var playerExists = false; // is set to true below if player exists var playerExists = false; // is set to true below if player exists
    var checkMediaType_enabled = true; // for debugging purposes var checkMediaType_enabled = true; // for debugging purposes
    var media_element = {}; // declaring as object var media_element = {}; // declaring as object
    var tmp="",count=0; // initiating temporary variables used inside functions and loops


    function checkMediaType() { function checkMediaType() {
    // checks whether it is a video or an audio tag // checks whether it is a video or an audio tag
    if ( checkMediaType_enabled ) { if ( checkMediaType_enabled ) {
    var mediaTypeBeforeCheck = mediaType; var mediaTypeBeforeCheck = mediaType;
    if (document.getElementsByTagName("video")[0]) { playerExists = true; mediaType = "video"; } if (document.getElementsByTagName("video")[0]) {
    if (document.getElementsByTagName("audio")[0]) { playerExists = true; mediaType = "audio"; } playerExists = true; mediaType = "video";
    }
    if (document.getElementsByTagName("audio")[0]) {
    playerExists = true; mediaType = "audio";
    }
    var mediaTypeAfterCheck = mediaType; var mediaTypeAfterCheck = mediaType;
    if (mediaTypeBeforeCheck != mediaTypeAfterCheck) if (mediaTypeBeforeCheck != mediaTypeAfterCheck)
    // Only show media type in console if it has changed. // Only show media type in console if it has changed.
    @@ -173,7 +178,7 @@ timerUI.show_remaining = 0; // 0: show elapsed time. 1: show remaining time. 2:
    timerUI.update_during_seek = true; // update timer while dragging seek bar timerUI.update_during_seek = true; // update timer while dragging seek bar
    timerUI.color = "rgb(49,136,255)"; // #38F – using RGB for compatibility. timerUI.color = "rgb(49,136,255)"; // #38F – using RGB for compatibility.
    timerUI.gradient = "rgba(0,0,0,0.9)"; // using RGBA instead of hexadecimal for compatibility. timerUI.gradient = "rgba(0,0,0,0.9)"; // using RGBA instead of hexadecimal for compatibility.
    timerUI.font_pack = "din,futura,'noto sans',ubuntu,'segoe ui',verdana,tahoma,roboto,'roboto light',arial,helvetica,'trebuchet ms',sans-serif,consolas,monospace"; timerUI.font_pack = "din, futura, 'noto sans', 'open sans', ubuntu, 'segoe ui', verdana, tahoma, roboto, 'roboto light', arial, helvetica, 'trebuchet ms' ,'bitstream vera sans', sans-serif, consolas, monospace";
    timerUI.width_breakpoint = 768; // pixels timerUI.width_breakpoint = 768; // pixels


    // console notifications and warnings (possibly to be expanded) // console notifications and warnings (possibly to be expanded)
    @@ -244,7 +249,10 @@ timerUI.toggle.title = function() {
    }; };


    timerUI.getTitle = function() { timerUI.getTitle = function() {
    if (! timerUI.domainRules_checked) /* only check domain rules once */ {
    timerUI.domainRules(); timerUI.domainRules();
    timerUI.domainRules_checked = true;
    }
    if (customTitleElement) timerUI.newTitle = customTitleElement.innerHTML; if (customTitleElement) timerUI.newTitle = customTitleElement.innerHTML;
    else { // skipping this whole part if no custom title is specified else { // skipping this whole part if no custom title is specified
    timerUI.newTitle = document.title; timerUI.newTitle = document.title;
    @@ -373,15 +381,16 @@ timerUI.update_multi_buffer = function() {
    timerUI.generate_buffer_segments = function() { timerUI.generate_buffer_segments = function() {
    timerUI.buffer_bar.innerHTML=""; // reset to re-generate segments timerUI.buffer_bar.innerHTML=""; // reset to re-generate segments
    for (count=0; count < media_element.buffered.length; count++) { for (count=0; count < media_element.buffered.length; count++) {
    timerUI.append_buffer_segment(timerUI.get_buffer_range(count) ); timerUI.append_buffer_segment(
    timerUI.get_buffer_range(count).start_pos,
    timerUI.get_buffer_range(count).end_pos
    );
    } }
    timerUI.select_segments = timerUI.buffer_bar.getElementsByClassName("timerUI_buffer_segment"); timerUI.select_segments = timerUI.buffer_bar.getElementsByClassName("timerUI_buffer_segment");
    timerUI.segment_count = timerUI.select_segments.length; timerUI.segment_count = timerUI.select_segments.length;
    }; };


    timerUI.append_buffer_segment = function(start_pos,end_pos) { timerUI.append_buffer_segment = function(start_pos,end_pos) {
    if (!start_pos) start_pos=timerUI.get_buffer_range(segment_number).start_pos;
    if (!end_pos) end_pos=timerUI.get_buffer_range(segment_number).end_pos;
    timerUI.buffer_bar.appendChild(document.createElement("div") ); timerUI.buffer_bar.appendChild(document.createElement("div") );
    timerUI.buffer_bar.lastElementChild.classList.add("timerUI_buffer_segment"); timerUI.buffer_bar.lastElementChild.classList.add("timerUI_buffer_segment");
    timerUI.buffer_bar.lastElementChild.style="left:"+start_pos+"%;width:"+(end_pos-start_pos)+"%;background-color:"+timerUI.color+";"; timerUI.buffer_bar.lastElementChild.style="left:"+start_pos+"%;width:"+(end_pos-start_pos)+"%;background-color:"+timerUI.color+";";
    @@ -580,38 +589,69 @@ function timeUI() {
    timerUI.domainRules = function() { timerUI.domainRules = function() {
    if (isDomain("dailymotion.com") && document.location.pathname.search(/^\/embed\//) < 0 ) { if (isDomain("dailymotion.com") && document.location.pathname.search(/^\/embed\//) < 0 ) {
    // Dailymotion watch page, excluding embed page. // Dailymotion watch page, excluding embed page.
    customMediaElement(document.getElementById("player-body").contentWindow.document.getElementsByTagName("video")[0]); customMediaElement(
    customTitleElement = document.getElementById("media-title"); // for unlisted document.getElementById("player-body").contentWindow.document.getElementsByTagName("video")[0]
    );
    customTitleElement = document.getElementById("media-title"); // for unlisted videos (Dailymotion only displays the video title in the HTML page title for public videos)
    } }

    // media embedded on Wayback Machine
    if ( is_WaybackEmbed() ) {
    tmp = document.getElementsByTagName("iframe")[0]; // iframe in temporary variable to deduplicate code
    customMediaElement(
    tmp.contentWindow.document.getElementsByTagName("video")[0]
    );
    // dark background for improved video visibility
    tmp.contentWindow.document.body.style.backgroundColor="#222";
    }
    }; };


    timerUI.titleDomainRules = function() { timerUI.titleDomainRules = function() {
    // custom domain rules for title // custom domain rules for title
    if ( isDomain("youtube.com") || isDomain("dailymotion.com") if ( isDomain("youtube.com") || isDomain("dailymotion.com")
    || isDomain("wikimedia.org") || isDomain("wikipedia.org") || isDomain("wikimedia.org") || isDomain("wikipedia.org")
    || isDomain("wikiversity.org") || isDomain("wikibooks.org") || isDomain("wikiversity.org") || isDomain("wikibooks.org")
    || isDomain("mediawiki.org") || isDomain("mediawiki.org")
    ) { ) {
    // negative lookahead regular expression – trim after last dash // negative lookahead regular expression – trim after last dash
    // Match both normal dash "-" and ndash "–", since the German-language wikis use the latter. // Match both normal dash "-" and ndash "–", since the German-language wikis use the latter.
    timerUI.newTitle = decodeURI(timerUI.newTitle.substring(0,timerUI.newTitle.search(/(-|)(?:.(?!(-|)))+$/)-1 ) ); timerUI.newTitle = decodeURI(timerUI.newTitle.substring(0,timerUI.newTitle.search(/(-|)(?:.(?!(-|)))+$/)-1 ) );
    } }
    // remove "File:" prefix on wikis // remove "File:" prefix on wikis
    if ( isDomain("wiki") ) { if ( isDomain("wiki") ) {
    if (document.title.search(/^File:/) == 0 ) timerUI.newTitle = timerUI.newTitle.substring(5); if (document.title.search(/^File:/) == 0 ) timerUI.newTitle = timerUI.newTitle.substring(5);
    if (document.title.search(/^Datei:/) == 0 ) timerUI.newTitle = timerUI.newTitle.substring(6); if (document.title.search(/^Datei:/) == 0 ) timerUI.newTitle = timerUI.newTitle.substring(6);
    } }


    if ( isDomain("archive.org") && ! isDomain("web.archive.org") ) { // Internet Archive library only, not Wayback Machine
    timerUI.newTitle = (document.location.href+"").substring((document.location.href+" ").search(/\/(?:.(?!\/))+\/?$/)+1 ); if ( isDomain("archive.org") && ! isDomain("web.archive.org") ) {
    // after last slash (additional space prevents full URL from being matched) // get media title from page title before first colon
    if (timerUI.newTitle != "") { timerUI.newTitle += " - "; } timerUI.newTitle = (document.location.href+"").substring((document.location.href+" ").search(/\/(?:.(?!\/))+\/?$/)+1 );
    timerUI.newTitle += document.title.substring(0,document.title.search(/:(?:.(?!:))+(?:.(?!:))+/)-1 ); // after last slash (additional space prevents full URL from being matched)
    // trim after second-last colon, -1 to remove space at end if (timerUI.newTitle != "") { timerUI.newTitle += " - "; }
    timerUI.newTitle += document.title.substring(0,document.title.search(/:(?:.(?!:))+(?:.(?!:))+/)-1 );
    // trim after second-last colon, -1 to remove space at end


    decodeURI(timerUI.newTitle); // prevent spaces from turning into "%20". timerUI.newTitle=decodeURI(timerUI.newTitle); // prevent spaces from turning into "%20".
    } }
    if ( is_WaybackEmbed() ) {
    // Already generated by the browser inside the iframe. How convenient. Otherwise, a regular expression that matches the part after the last slash in the URL, and a decodeURI would have to be used.
    timerUI.newTitle = tmp.contentWindow.document.title;
    }
    }; };


    function is_WaybackEmbed() {
    // checks if the current page is media embedded on the Wayback Machine, for code deduplication.
    if ( isDomain("web.archive.org") || isDomain("wayback.archive.org") && document.title=="Wayback Machine" && document.getElementsByTagName("iframe")[0] ) {
    // separate check for ID of iframe to avoid reference error
    if (document.getElementsByTagName("iframe")[0].id=="playback") {
    return true;
    }
    } else {
    return false;
    }
    }


    // == Master function == // == Master function ==
    timeUI(); timeUI();
  6. HT-7 revised this gist Oct 9, 2022. 1 changed file with 32 additions and 54 deletions.
    86 changes: 32 additions & 54 deletions timerUI.js
    @@ -37,11 +37,11 @@ function checkFileExtension(ext) {
    if (typeof(ext) == "string") { if (typeof(ext) == "string") {
    ext = ext.toLowerCase(); // case-insensitive ext = ext.toLowerCase(); // case-insensitive
    // string // string
    if (document.location.toString().search(new RegExp(ext+"$", "i")) > -1) return true; else return false; if (document.location.href.search(new RegExp(ext+"$", "i")) > -1) return true; else return false;
    } else if (typeof(ext) == "object") { } else if (typeof(ext) == "object") {
    // array – check against multiple strings // array – check against multiple strings
    for (var count=0; count < ext.length; count++) { for (var count=0; count < ext.length; count++) {
    if (document.location.toString().search(new RegExp(ext[count]+"$", "i")) > -1) return true; if (document.location.href.search(new RegExp(ext[count]+"$", "i")) > -1) return true;
    if (count == ext.length-1) return false; // if no matches after going through them all if (count == ext.length-1) return false; // if no matches after going through them all
    } }
    } }
    @@ -167,10 +167,8 @@ timerUI.override_check = false;
    timerUI.on = true; timerUI.on = true;
    timerUI.buffer_on = true; timerUI.buffer_on = true;
    timerUI.multiBuffer = true; // multiple buffer segments timerUI.multiBuffer = true; // multiple buffer segments
    timerUI.left_mode = 1;
    timerUI.div = {}; // unset yet, declaring to prevent reference errors timerUI.div = {}; // unset yet, declaring to prevent reference errors
    timerUI.interval = {}; timerUI.interval = {};
    timerUI.css = {};
    timerUI.show_remaining = 0; // 0: show elapsed time. 1: show remaining time. 2: show elapsed and total. timerUI.show_remaining = 0; // 0: show elapsed time. 1: show remaining time. 2: show elapsed and total.
    timerUI.update_during_seek = true; // update timer while dragging seek bar timerUI.update_during_seek = true; // update timer while dragging seek bar
    timerUI.color = "rgb(49,136,255)"; // #38F – using RGB for compatibility. timerUI.color = "rgb(49,136,255)"; // #38F – using RGB for compatibility.
    @@ -182,7 +180,7 @@ timerUI.width_breakpoint = 768; // pixels
    timerUI.msg = { timerUI.msg = {
    "notimer": "timerUI: No timer found; checking for media element again. Please try again.", "notimer": "timerUI: No timer found; checking for media element again. Please try again.",
    "nomedia": "timerUI: no media element found on page. Stopping." "nomedia": "timerUI: no media element found on page. Stopping."
    } };


    // text containers (no const for compatibility) // text containers (no const for compatibility)
    var timer_linefeed = "<span class=timer_linefeed><br /></span>"; var timer_linefeed = "<span class=timer_linefeed><br /></span>";
    @@ -191,6 +189,7 @@ var timer_slash = " <span class=timer_slash>/</span> ";
    // functions // functions
    timerUI.toggle = {}; timerUI.toggle = {};
    timerUI.toggle.main = function() { timerUI.toggle.main = function() {
    // show and hide
    if (timerUI.div) { if (timerUI.div) {
    timerUI.update(); timerUI.update();
    if (timerUI.on) { if (timerUI.on) {
    @@ -257,15 +256,15 @@ timerUI.getTitle = function() {
    } else { } else {
    return "TimerUI – designed for home cinemas"; return "TimerUI – designed for home cinemas";
    } }
    } };


    timerUI.guessMediaType = function() { timerUI.guessMediaType = function() {
    if (isDomain("youtube.com") || isDomain("dailymotion.com") ) return "video"; if (isDomain("youtube.com") || isDomain("dailymotion.com") ) return "video";
    if (document.location.pathname.toString().search(/^\/video\//) > -1) return "video"; if (document.location.pathname.search(/^\/video\//) > -1) return "video";
    if (checkFileExtension(mediafileext.video) ) return "video"; if (checkFileExtension(mediafileext.video) ) return "video";
    if (checkFileExtension(mediafileext.audio) ) return "audio"; if (checkFileExtension(mediafileext.audio) ) return "audio";
    return "unknown"; // if nothing detected return "unknown"; // if nothing detected
    } };


    timerUI.updateFileIcon = function() { timerUI.updateFileIcon = function() {
    timerUI.fileIcon = timerUI.guessMediaType(); timerUI.fileIcon = timerUI.guessMediaType();
    @@ -274,15 +273,15 @@ timerUI.updateFileIcon = function() {
    case "audio": timerUI.fileIcon = "♫"; break; case "audio": timerUI.fileIcon = "♫"; break;
    case "unknown": timerUI.fileIcon = "📄"; break; case "unknown": timerUI.fileIcon = "📄"; break;
    } }
    } };




    timerUI.adaptTitleWidth = function() { timerUI.adaptTitleWidth = function() {
    if (media_element.duration > 3600 && timerUI.show_remaining == 2 && window.innerWidth > timerUI.width_breakpoint) if (media_element.duration > 3600 && timerUI.show_remaining == 2 && window.innerWidth > timerUI.width_breakpoint)
    timerUI.title.style.maxWidth = "calc(100% - 670px)"; timerUI.title.style.maxWidth = "calc(100% - 670px)";
    else else
    timerUI.title.style.maxWidth = "calc(100% - 400px)"; timerUI.title.style.maxWidth = "calc(100% - 400px)";
    } };


    window.addEventListener('resize', function() { window.addEventListener('resize', function() {
    if (window.innerWidth < timerUI.width_breakpoint) timerUI.title.removeAttribute("style"); if (window.innerWidth < timerUI.width_breakpoint) timerUI.title.removeAttribute("style");
    @@ -320,17 +319,9 @@ timerUI.update = function() {
    timerUI.status.innerHTML=symbol_play timerUI.status.innerHTML=symbol_play
    : timerUI.status.innerHTML=symbol_pause; : timerUI.status.innerHTML=symbol_pause;


    } else { timerUI.stop(); console.warn(timerUI.msg.nomedia) } } else { timerUI.stop(); console.warn(timerUI.msg.nomedia); }
    }; };

    timerUI.updateTitle = function() { if (timerUI.title) timerUI.title.innerHTML = timerUI.getTitle(); };
    timerUI.leftPlaybackControls

    timerUI.updateTitle = function() { if (timerUI.title) {
    switch(timerUI.left_mode) {
    case 1: timerUI.title.innerHTML = timerUI.getTitle();
    case 2: timerUI.leftPlaybackControls();
    case 3: timerUI.left_hidden();
    } }


    // update title on URL change // update title on URL change
    addEventListener('popstate', timerUI.updateTitle); addEventListener('popstate', timerUI.updateTitle);
    @@ -388,18 +379,19 @@ timerUI.generate_buffer_segments = function() {
    timerUI.segment_count = timerUI.select_segments.length; timerUI.segment_count = timerUI.select_segments.length;
    }; };


    timerUI.append_buffer_segment = function([start_pos,end_pos]) { timerUI.append_buffer_segment = function(start_pos,end_pos) {
    // array function if (!start_pos) start_pos=timerUI.get_buffer_range(segment_number).start_pos;
    timerUI.buffer_bar.appendChild(document.createElement("div")); if (!end_pos) end_pos=timerUI.get_buffer_range(segment_number).end_pos;
    timerUI.buffer_bar.appendChild(document.createElement("div") );
    timerUI.buffer_bar.lastElementChild.classList.add("timerUI_buffer_segment"); timerUI.buffer_bar.lastElementChild.classList.add("timerUI_buffer_segment");
    timerUI.buffer_bar.lastElementChild.style="left:"+start_pos+"%;width:"+(end_pos-start_pos)+"%;"; timerUI.buffer_bar.lastElementChild.style="left:"+start_pos+"%;width:"+(end_pos-start_pos)+"%;background-color:"+timerUI.color+";";
    }; };


    timerUI.get_buffer_range = function(segment_number) { timerUI.get_buffer_range = function(segment_number) {
    return [ return {
    media_element.buffered.start(segment_number) / media_element.duration * 100, start_pos: media_element.buffered.start(segment_number) / media_element.duration * 100,
    media_element.buffered.end(segment_number) / media_element.duration * 100 end_pos: media_element.buffered.end(segment_number) / media_element.duration * 100
    ]; // array with start and end percentages }; // object with start and end percentages
    }; };


    timerUI.set_buffer_segment = function(segment_number,start_pos,end_pos) { timerUI.set_buffer_segment = function(segment_number,start_pos,end_pos) {
    @@ -421,9 +413,6 @@ timerUI.setColor = function(newColor) {
    timerUI.time.style.color=timerUI.color; timerUI.time.style.color=timerUI.color;
    timerUI.status.style.color=timerUI.color; timerUI.status.style.color=timerUI.color;
    timerUI.title.style.color=timerUI.color; timerUI.title.style.color=timerUI.color;

    // additional colors
    timerUI.css.colors.innerHTML = "#timerUI .timerUI_buffer_segment { background-color:"+timerUI.color+"; }";
    }; };


    timerUI.setGradient = function(newGradient) { timerUI.setGradient = function(newGradient) {
    @@ -486,7 +475,7 @@ function timeUI() {
    appendChildWithID("div","timerUI_media_title",timerUI.div ); appendChildWithID("div","timerUI_media_title",timerUI.div );
    timerUI.title = document.getElementById("timerUI_media_title"); timerUI.title = document.getElementById("timerUI_media_title");
    timerUI.title.innerHTML = timerUI.getTitle(); timerUI.title.innerHTML = timerUI.getTitle();
    addStyle("#timerUI #timerUI_media_title { position:fixed; text-shadow: 0 0 5px black; display:inline; display:-webkit-box; bottom:15pt; left:2pt; color:"+timerUI.color+"; font-family:"+timerUI.font_pack+"; font-size:20pt; width:60%; max-width:calc(100% - 500px); text-overflow: ellipsis; overflow: hidden; -webkit-box-orient: vertical; -webkit-line-clamp: 2; vertical-align: bottom;") addStyle("#timerUI #timerUI_media_title { position:fixed; text-shadow: 0 0 5px black; display:inline; display:-webkit-box; bottom:15pt; left:2pt; color:"+timerUI.color+"; font-family:"+timerUI.font_pack+"; font-size:20pt; width:60%; max-width:calc(100% - 500px); text-overflow: ellipsis; overflow: hidden; -webkit-box-orient: vertical; -webkit-line-clamp: 2; vertical-align: bottom;");


    // timer // timer
    appendChildWithID("button","timerUI_media_timer",timerUI.div ); appendChildWithID("button","timerUI_media_timer",timerUI.div );
    @@ -506,12 +495,8 @@ function timeUI() {
    addStyle("#timerUI #timerUI_progress_placeholder { display:block; position:fixed; cursor:pointer; background-color:grey; height:8pt; width:100%; bottom:0; opacity:0.2; }", timerUI.div); addStyle("#timerUI #timerUI_progress_placeholder { display:block; position:fixed; cursor:pointer; background-color:grey; height:8pt; width:100%; bottom:0; opacity:0.2; }", timerUI.div);


    // responsive - at bottom to be able to override CSS properties without !important flag. // responsive - at bottom to be able to override CSS properties without !important flag.
    addStyle("@media (max-width:"+timerUI.width_breakpoint+"px) { #timerUI #timerUI_media_timer { font-size:30pt; line-height:24pt; bottom:15pt; } #timerUI #timerUI_playback_status { bottom:10pt; } } @media (max-width:500px) { #timerUI #timerUI_media_timer .timer_linefeed { display:inline; } #timerUI #timerUI_media_timer .timer_slash { display:none; } } @media (max-width:"+timerUI.width_breakpoint+"px) { #timerUI #timerUI_buffer_bar { font-size:10pt; -webkit-line-clamp: 3; max-width:60%; } } @media (max-width:480px) { #timerUI #timerUI_buffer_bar { display: none; } } ", timerUI.div); addStyle("@media (max-width:"+timerUI.width_breakpoint+"px) { #timerUI #timerUI_media_timer { font-size:30pt; line-height:24pt; bottom:15pt; } #timerUI #timerUI_playback_status { bottom:10pt; } } @media (max-width:500px) { #timerUI #timerUI_media_timer .timer_linefeed { display:inline; } #timerUI #timerUI_media_timer .timer_slash { display:none; } } @media (max-width:"+timerUI.width_breakpoint+"px) { #timerUI #timerUI_buffer_bar { font-size:10pt; -webkit-line-clamp: 3; max-width:60%;} } @media (max-width:480px) { #timerUI #timerUI_buffer_bar { display: none; } } ", timerUI.div);
    timerUI.div.lastElementChild.classList.add("timerUI_responsive"); timerUI.div.lastElementChild.classList.add("timerUI_responsive");
    timerUI.css.responsive=timerUI.div.lastElementChild;

    appendChildWithID("style","timerUI_colors",timerUI.div);
    timerUI.css.colors=document.getElementById("timerUI_colors");




    // update timer during playback every fifteenth of a second and while mouse is dragging progress bar // update timer during playback every fifteenth of a second and while mouse is dragging progress bar
    @@ -540,25 +525,16 @@ function timeUI() {
    timerUI.update(); timerUI.updateBufferBar(true); timerUI.update(); timerUI.updateBufferBar(true);
    }; };


    // toggle between elapsed, remaining, and elapsed/total time
    timerUI.title.onclick = function() {
    switch(timerUI.left_mode) {
    case 1: timerUI.left_mode = 2; break;
    case 2: timerUI.left_mode = 3; break;
    case 3: timerUI.left_mode = 1; break;
    }
    timerUI.update(); timerUI.updateBufferBar(true);
    };


    // clickable progress bar (experimental) - no "timerUI." notation because inside main function // clickable progress bar (experimental) - no "timerUI." notation because inside main function
    timerUI.clickSeekBar = function(m){ timerUI.clickSeekBar = function(m){
    if (media_element) { if (media_element) {
    var clickPos = m.clientX / timerUI.bar_placeholder.offsetWidth; var clickPos = m.clientX / timerUI.bar_placeholder.offsetWidth;
    media_element.currentTime = media_element.duration * clickPos; // go to beginning if clicked in first percentile
    if (clickPos < 0.01 ) media_element.currentTime = 0;
    else media_element.currentTime = media_element.duration * clickPos;
    timerUI.update(); timerUI.updateBufferBar(true); timerUI.update(); timerUI.updateBufferBar(true);
    } }
    } };
    function dragSeekBar(m){ // currently unused function dragSeekBar(m){ // currently unused
    m = m || window.event; m = m || window.event;
    var clickPos = m.pageX / timerUI.bar_placeholder.offsetWidth; var clickPos = m.pageX / timerUI.bar_placeholder.offsetWidth;
    @@ -602,7 +578,7 @@ function timeUI() {


    // == Custom domain rules == // == Custom domain rules ==
    timerUI.domainRules = function() { timerUI.domainRules = function() {
    if (isDomain("dailymotion.com") && document.location.pathname.toString().search(/^\/embed\//) < 0 ) { if (isDomain("dailymotion.com") && document.location.pathname.search(/^\/embed\//) < 0 ) {
    // Dailymotion watch page, excluding embed page. // Dailymotion watch page, excluding embed page.
    customMediaElement(document.getElementById("player-body").contentWindow.document.getElementsByTagName("video")[0]); customMediaElement(document.getElementById("player-body").contentWindow.document.getElementsByTagName("video")[0]);
    customTitleElement = document.getElementById("media-title"); // for unlisted customTitleElement = document.getElementById("media-title"); // for unlisted
    @@ -627,11 +603,13 @@ timerUI.titleDomainRules = function() {
    } }


    if ( isDomain("archive.org") && ! isDomain("web.archive.org") ) { if ( isDomain("archive.org") && ! isDomain("web.archive.org") ) {
    timerUI.newTitle = (document.location.toString()+"").substring((document.location.toString()+" ").search(/\/(?:.(?!\/))+\/?$/)+1 ); timerUI.newTitle = (document.location.href+"").substring((document.location.href+" ").search(/\/(?:.(?!\/))+\/?$/)+1 );
    // after last slash (additional space prevents full URL from being matched) // after last slash (additional space prevents full URL from being matched)
    if (timerUI.newTitle != "") { timerUI.newTitle += " - " }; if (timerUI.newTitle != "") { timerUI.newTitle += " - "; }
    timerUI.newTitle += document.title.toString().substring(0,document.title.toString().search(/:(?:.(?!:))+(?:.(?!:))+/)-1 ); timerUI.newTitle += document.title.substring(0,document.title.search(/:(?:.(?!:))+(?:.(?!:))+/)-1 );
    // trim after second-last colon, -1 to remove space at end // trim after second-last colon, -1 to remove space at end

    decodeURI(timerUI.newTitle); // prevent spaces from turning into "%20".
    } }
    }; };


  7. HT-7 revised this gist Jun 1, 2022. 1 changed file with 30 additions and 2 deletions.
    32 changes: 30 additions & 2 deletions timerUI.js
    @@ -167,8 +167,10 @@ timerUI.override_check = false;
    timerUI.on = true; timerUI.on = true;
    timerUI.buffer_on = true; timerUI.buffer_on = true;
    timerUI.multiBuffer = true; // multiple buffer segments timerUI.multiBuffer = true; // multiple buffer segments
    timerUI.left_mode = 1;
    timerUI.div = {}; // unset yet, declaring to prevent reference errors timerUI.div = {}; // unset yet, declaring to prevent reference errors
    timerUI.interval = {}; timerUI.interval = {};
    timerUI.css = {};
    timerUI.show_remaining = 0; // 0: show elapsed time. 1: show remaining time. 2: show elapsed and total. timerUI.show_remaining = 0; // 0: show elapsed time. 1: show remaining time. 2: show elapsed and total.
    timerUI.update_during_seek = true; // update timer while dragging seek bar timerUI.update_during_seek = true; // update timer while dragging seek bar
    timerUI.color = "rgb(49,136,255)"; // #38F – using RGB for compatibility. timerUI.color = "rgb(49,136,255)"; // #38F – using RGB for compatibility.
    @@ -320,7 +322,15 @@ timerUI.update = function() {


    } else { timerUI.stop(); console.warn(timerUI.msg.nomedia) } } else { timerUI.stop(); console.warn(timerUI.msg.nomedia) }
    }; };
    timerUI.updateTitle = function() { if (timerUI.title) timerUI.title.innerHTML = timerUI.getTitle(); }
    timerUI.leftPlaybackControls

    timerUI.updateTitle = function() { if (timerUI.title) {
    switch(timerUI.left_mode) {
    case 1: timerUI.title.innerHTML = timerUI.getTitle();
    case 2: timerUI.leftPlaybackControls();
    case 3: timerUI.left_hidden();
    } }


    // update title on URL change // update title on URL change
    addEventListener('popstate', timerUI.updateTitle); addEventListener('popstate', timerUI.updateTitle);
    @@ -411,6 +421,9 @@ timerUI.setColor = function(newColor) {
    timerUI.time.style.color=timerUI.color; timerUI.time.style.color=timerUI.color;
    timerUI.status.style.color=timerUI.color; timerUI.status.style.color=timerUI.color;
    timerUI.title.style.color=timerUI.color; timerUI.title.style.color=timerUI.color;

    // additional colors
    timerUI.css.colors.innerHTML = "#timerUI .timerUI_buffer_segment { background-color:"+timerUI.color+"; }";
    }; };


    timerUI.setGradient = function(newGradient) { timerUI.setGradient = function(newGradient) {
    @@ -493,8 +506,12 @@ function timeUI() {
    addStyle("#timerUI #timerUI_progress_placeholder { display:block; position:fixed; cursor:pointer; background-color:grey; height:8pt; width:100%; bottom:0; opacity:0.2; }", timerUI.div); addStyle("#timerUI #timerUI_progress_placeholder { display:block; position:fixed; cursor:pointer; background-color:grey; height:8pt; width:100%; bottom:0; opacity:0.2; }", timerUI.div);


    // responsive - at bottom to be able to override CSS properties without !important flag. // responsive - at bottom to be able to override CSS properties without !important flag.
    addStyle("@media (max-width:"+timerUI.width_breakpoint+"px) { #timerUI #timerUI_media_timer { font-size:30pt; line-height:24pt; bottom:15pt; } #timerUI #timerUI_playback_status { bottom:10pt; } } @media (max-width:500px) { #timerUI #timerUI_media_timer .timer_linefeed { display:inline; } #timerUI #timerUI_media_timer .timer_slash { display:none; } } @media (max-width:"+timerUI.width_breakpoint+"px) { #timerUI #timerUI_buffer_bar { font-size:10pt; -webkit-line-clamp: 3; max-width:60%;} } @media (max-width:480px) { #timerUI #timerUI_buffer_bar { display: none; } } ", timerUI.div); addStyle("@media (max-width:"+timerUI.width_breakpoint+"px) { #timerUI #timerUI_media_timer { font-size:30pt; line-height:24pt; bottom:15pt; } #timerUI #timerUI_playback_status { bottom:10pt; } } @media (max-width:500px) { #timerUI #timerUI_media_timer .timer_linefeed { display:inline; } #timerUI #timerUI_media_timer .timer_slash { display:none; } } @media (max-width:"+timerUI.width_breakpoint+"px) { #timerUI #timerUI_buffer_bar { font-size:10pt; -webkit-line-clamp: 3; max-width:60%; } } @media (max-width:480px) { #timerUI #timerUI_buffer_bar { display: none; } } ", timerUI.div);
    timerUI.div.lastElementChild.classList.add("timerUI_responsive"); timerUI.div.lastElementChild.classList.add("timerUI_responsive");
    timerUI.css.responsive=timerUI.div.lastElementChild;

    appendChildWithID("style","timerUI_colors",timerUI.div);
    timerUI.css.colors=document.getElementById("timerUI_colors");




    // update timer during playback every fifteenth of a second and while mouse is dragging progress bar // update timer during playback every fifteenth of a second and while mouse is dragging progress bar
    @@ -523,6 +540,17 @@ function timeUI() {
    timerUI.update(); timerUI.updateBufferBar(true); timerUI.update(); timerUI.updateBufferBar(true);
    }; };


    // toggle between elapsed, remaining, and elapsed/total time
    timerUI.title.onclick = function() {
    switch(timerUI.left_mode) {
    case 1: timerUI.left_mode = 2; break;
    case 2: timerUI.left_mode = 3; break;
    case 3: timerUI.left_mode = 1; break;
    }
    timerUI.update(); timerUI.updateBufferBar(true);
    };


    // clickable progress bar (experimental) - no "timerUI." notation because inside main function // clickable progress bar (experimental) - no "timerUI." notation because inside main function
    timerUI.clickSeekBar = function(m){ timerUI.clickSeekBar = function(m){
    if (media_element) { if (media_element) {
  8. HT-7 revised this gist Jun 1, 2022. 1 changed file with 113 additions and 46 deletions.
    159 changes: 113 additions & 46 deletions timerUI.js
    @@ -1,4 +1,4 @@
    // TimerUI // TimerUI – for home cinemas


    // == Dependencies == // == Dependencies ==


    @@ -40,9 +40,9 @@ function checkFileExtension(ext) {
    if (document.location.toString().search(new RegExp(ext+"$", "i")) > -1) return true; else return false; if (document.location.toString().search(new RegExp(ext+"$", "i")) > -1) return true; else return false;
    } else if (typeof(ext) == "object") { } else if (typeof(ext) == "object") {
    // array – check against multiple strings // array – check against multiple strings
    for (var a=0; a < ext.length; a++) { for (var count=0; count < ext.length; count++) {
    if (document.location.toString().search(new RegExp(ext[a]+"$", "i")) > -1) return true; if (document.location.toString().search(new RegExp(ext[count]+"$", "i")) > -1) return true;
    if (a == ext.length-1) return false; // if no matches after going through them all if (count == ext.length-1) return false; // if no matches after going through them all
    } }
    } }
    } }
    @@ -162,8 +162,11 @@ if (! timerUI) var timerUI = new Object({}); // create parent object if none exi


    // default system variables // default system variables
    timerUI.debug_mode = false; timerUI.debug_mode = false;
    timerUI.override_check = false;

    timerUI.on = true; timerUI.on = true;
    timerUI.buffer_on = true; timerUI.buffer_on = true;
    timerUI.multiBuffer = true; // multiple buffer segments
    timerUI.div = {}; // unset yet, declaring to prevent reference errors timerUI.div = {}; // unset yet, declaring to prevent reference errors
    timerUI.interval = {}; timerUI.interval = {};
    timerUI.show_remaining = 0; // 0: show elapsed time. 1: show remaining time. 2: show elapsed and total. timerUI.show_remaining = 0; // 0: show elapsed time. 1: show remaining time. 2: show elapsed and total.
    @@ -184,7 +187,8 @@ var timer_linefeed = "<span class=timer_linefeed><br /></span>";
    var timer_slash = " <span class=timer_slash>/</span> "; var timer_slash = " <span class=timer_slash>/</span> ";


    // functions // functions
    timerUI.toggle = function() { timerUI.toggle = {};
    timerUI.toggle.main = function() {
    if (timerUI.div) { if (timerUI.div) {
    timerUI.update(); timerUI.update();
    if (timerUI.on) { if (timerUI.on) {
    @@ -202,9 +206,9 @@ timerUI.toggle = function() {
    } }
    }; };


    timerUI.toggleBuffer = function() { timerUI.toggle.buffer = function() {
    if (timerUI.div) { if (timerUI.div) {
    timerUI.update(); timerUI.update(); timerUI.updateBufferBar(true);
    if (timerUI.buffer_on) { if (timerUI.buffer_on) {
    timerUI.buffer_bar.style.display = "none"; timerUI.buffer_bar.style.display = "none";
    console.log("timerUI buffer bar off"); console.log("timerUI buffer bar off");
    @@ -220,9 +224,9 @@ timerUI.toggleBuffer = function() {
    } }
    }; };


    timerUI.toggleTitle = function() { timerUI.toggle.title = function() {
    if (timerUI.div) { if (timerUI.div) {
    timerUI.update(); timerUI.update(); timerUI.updateBufferBar(true);
    if (timerUI.title_on) { if (timerUI.title_on) {
    timerUI.title.style.display = "none"; timerUI.title.style.display = "none";
    console.log("timerUI title off"); console.log("timerUI title off");
    @@ -245,8 +249,12 @@ timerUI.getTitle = function() {
    timerUI.newTitle = document.title; timerUI.newTitle = document.title;
    timerUI.titleDomainRules(); timerUI.titleDomainRules();
    } }
    timerUI.updateFileIcon(); if (media_element) {
    return timerUI.fileIcon+"&nbsp;"+timerUI.newTitle; timerUI.updateFileIcon();
    return timerUI.fileIcon+"&nbsp;"+timerUI.newTitle;
    } else {
    return "TimerUI – designed for home cinemas";
    }
    } }


    timerUI.guessMediaType = function() { timerUI.guessMediaType = function() {
    @@ -281,10 +289,8 @@ window.addEventListener('resize', function() {
    timerUI.update = function() { timerUI.update = function() {
    if (media_element) { if (media_element) {
    timerUI.bar.style.width=media_element.currentTime / media_element.duration * 100 + "%"; timerUI.bar.style.width=media_element.currentTime / media_element.duration * 100 + "%";
    if (media_element.buffered.length > 0) {
    // prevent "DOMException: Index or size is negative or greater than the allowed amount" // buffer bar update formerly located here; removed from the scope of this function
    timerUI.buffer_bar.style.width=media_element.buffered.end(timerUI.buffer_segment) / media_element.duration * 100 + "%";
    } else { timerUI.buffer_bar.style.width="0%"; }


    switch(timerUI.show_remaining) { switch(timerUI.show_remaining) {
    // 0: "HH:MM:SS" 1: "-HH:MM:SS" 2: "MM:SS / MM:SS" or "HH:MM:SS / HH:MM:SS" // 0: "HH:MM:SS" 1: "-HH:MM:SS" 2: "MM:SS / MM:SS" or "HH:MM:SS / HH:MM:SS"
    @@ -322,19 +328,79 @@ addEventListener('popstate', timerUI.updateTitle);
    // update title fallback // update title fallback
    timerUI.interval.updateTitle = setInterval(timerUI.updateTitle, 2000); timerUI.interval.updateTitle = setInterval(timerUI.updateTitle, 2000);


    timerUI.updateBufferSegment = function() { if (media_element) {
    // find out first buffer segment after current playback position // buffer bar
    timerUI.updateBufferBar = function(override_paused) {
    if (media_element && timerUI.buffer_on && (!media_element.paused || override_paused) ) {
    timerUI.multiBuffer ? timerUI.update_multi_buffer() : timerUI.single_segment_buffer();
    }
    };

    // single-segment buffer bar
    timerUI.single_segment_buffer = function() {
    if (timerUI.buffer_bar.innerHTML!="") { timerUI.buffer_bar.style=""; timerUI.buffer_bar.innerHTML=""; } // reset after switching from multi-segment buffer bar
    // find out first buffer segment after current playback position
    media_element.buffered.length > 0 ? timerUI.buffer_segment=media_element.buffered.length-1 : timerUI.buffer_segment=0; media_element.buffered.length > 0 ? timerUI.buffer_segment=media_element.buffered.length-1 : timerUI.buffer_segment=0;
    // media_element.buffered.length is zero until player is initialized // media_element.buffered.length is zero until player is initialized
    // prevent timerUI.buffer_segment from going negative, as it would cause a DOMException error // prevent timerUI.buffer_segment from going negative, as it would cause a DOMException error
    if ( timerUI.buffer_on && timerUI.buffer_segment > 0) { if ( timerUI.buffer_segment > 0) {
    while (media_element.buffered.end(timerUI.buffer_segment-1) > media_element.currentTime && timerUI.buffer_segment > 1 ) { while (media_element.buffered.end(timerUI.buffer_segment-1) > media_element.currentTime && timerUI.buffer_segment > 1 ) {
    timerUI.update_single_buffer();
    timerUI.buffer_segment-- ; timerUI.buffer_segment-- ;
    } }
    } }
    }
    }; };


    timerUI.update_single_buffer = function() {
    if (media_element.buffered.length > 0) {
    // prevent "DOMException: Index or size is negative or greater than the allowed amount"
    timerUI.buffer_bar.style.width=media_element.buffered.end(timerUI.buffer_segment) / media_element.duration * 100 + "%";
    } else { timerUI.buffer_bar.style.width="0%"; }
    };

    // multi-segment buffer bar – highlight all buffered parts
    timerUI.update_multi_buffer = function() {
    if (timerUI.buffer_bar.style.length < 1) {
    timerUI.buffer_bar.style.width="100%";
    timerUI.buffer_bar.style.backgroundColor="rgba(0,0,0,0)";
    }
    if (media_element.buffered.length > 0) {
    timerUI.generate_buffer_segments();
    } else { timerUI.buffer_bar.style.width="0%"; }
    };

    timerUI.generate_buffer_segments = function() {
    timerUI.buffer_bar.innerHTML=""; // reset to re-generate segments
    for (count=0; count < media_element.buffered.length; count++) {
    timerUI.append_buffer_segment(timerUI.get_buffer_range(count) );
    }
    timerUI.select_segments = timerUI.buffer_bar.getElementsByClassName("timerUI_buffer_segment");
    timerUI.segment_count = timerUI.select_segments.length;
    };

    timerUI.append_buffer_segment = function([start_pos,end_pos]) {
    // array function
    timerUI.buffer_bar.appendChild(document.createElement("div"));
    timerUI.buffer_bar.lastElementChild.classList.add("timerUI_buffer_segment");
    timerUI.buffer_bar.lastElementChild.style="left:"+start_pos+"%;width:"+(end_pos-start_pos)+"%;";
    };

    timerUI.get_buffer_range = function(segment_number) {
    return [
    media_element.buffered.start(segment_number) / media_element.duration * 100,
    media_element.buffered.end(segment_number) / media_element.duration * 100
    ]; // array with start and end percentages
    };

    timerUI.set_buffer_segment = function(segment_number,start_pos,end_pos) {
    var selection=timerUI.buffer_bar.getElementsByClassName("timerUI_buffer_segment");
    selection[segment_number].style.left = start_pos / media_element.duration * 100 + "%";
    selection[segment_number].style.width = (end_pos-start_pos) / media_element.duration * 100 + "%";
    };



    // colors
    timerUI.setColor = function(newColor) { timerUI.setColor = function(newColor) {
    newColor == "default" ? timerUI.color="rgb(49,136,255)" /* #38F */ : timerUI.color = newColor; newColor == "default" ? timerUI.color="rgb(49,136,255)" /* #38F */ : timerUI.color = newColor;


    @@ -375,7 +441,7 @@ function timeUI() {
    checkMediaType(); checkMediaType();
    timerUI.domainRules(); // load domain rules timerUI.domainRules(); // load domain rules
    // add timerUI if it does not already exist // add timerUI if it does not already exist
    if ( ( ! document.getElementById("timerUI") ) && playerExists ) { if ( ( ! document.getElementById("timerUI") ) && playerExists || timerUI.override_check ) {


    // Adding elements // Adding elements


    @@ -384,50 +450,50 @@ function timeUI() {
    timerUI.div = document.getElementById("timerUI"); timerUI.div = document.getElementById("timerUI");


    // button styling // button styling
    addStyle("#timerUI button { background:none; border:none; outline:none; line-height:unset; } #timerUI button:hover { filter: brightness(1.2); } #timerUI button:active { filter: brightness(0.6); }", timerUI.div); addStyle("#timerUI button { background:none; border:none; outline:none; line-height:unset; } #timerUI button:hover { filter: brightness(1.2); } #timerUI button:active { filter: brightness(0.6); }", timerUI.div);
    // to suppress button background and border on earlier browser versions // to suppress button background and border on earlier browser versions
    timerUI.div.lastElementChild.classList.add("timerUI_buttons"); // label to improve visibility in page inspector timerUI.div.lastElementChild.classList.add("timerUI_buttons"); // label to improve visibility in page inspector


    // background gradient // background gradient
    appendChildWithID("div","bottomGradient",timerUI.div ); appendChildWithID("div","timerUI_bottom_gradient",timerUI.div );
    timerUI.gradient_placeholder = document.getElementById("bottomGradient"); timerUI.gradient_placeholder = document.getElementById("timerUI_bottom_gradient");
    addStyle("#timerUI #bottomGradient { display:block; position:fixed; background-image:linear-gradient(to top,"+timerUI.gradient+", rgba(0,0,0,0) ); opacity:1; height:80pt; width:100%; bottom:0; pointer-events:none; }", timerUI.div); addStyle("#timerUI #timerUI_bottom_gradient { display:block; position:fixed; background-image:linear-gradient(to top,"+timerUI.gradient+", rgba(0,0,0,0) ); opacity:1; height:80pt; width:100%; bottom:0; pointer-events:none; }", timerUI.div);


    // progress bar // progress bar
    appendChildWithID("div","progBar",timerUI.div ); appendChildWithID("div","timerUI_progress_bar",timerUI.div );
    timerUI.bar = document.getElementById("progBar"); timerUI.bar = document.getElementById("timerUI_progress_bar");
    addStyle("#timerUI #progBar { display:block; position:fixed; background-color:"+timerUI.color+"; box-shadow: 0 0 30px 0px "+timerUI.color+"; height:8pt; width:50%; bottom:0; }", timerUI.div); addStyle("#timerUI #timerUI_progress_bar { display:block; position:fixed; background-color:"+timerUI.color+"; box-shadow: 0 0 30px 0px "+timerUI.color+"; height:8pt; width:50%; bottom:0; }", timerUI.div);


    // buffer bar // buffer bar
    appendChildWithID("div","bufferBar",timerUI.div ); appendChildWithID("div","timerUI_buffer_bar",timerUI.div );
    timerUI.buffer_bar = document.getElementById("bufferBar"); timerUI.buffer_bar = document.getElementById("timerUI_buffer_bar");
    addStyle("#timerUI #bufferBar { display:block; position:fixed; background-color:"+timerUI.color+"; height:8pt; width:75%; bottom:0; opacity:0.4; }", timerUI.div); addStyle("#timerUI #timerUI_buffer_bar, #timerUI .timerUI_buffer_segment { display:block; position:fixed; background-color:"+timerUI.color+"; height:8pt; width:75%; bottom:0; opacity:0.4; } #timerUI .timerUI_buffer_segment { opacity:1; }", timerUI.div);


    // media title // media title
    appendChildWithID("div","mediaTitle",timerUI.div ); appendChildWithID("div","timerUI_media_title",timerUI.div );
    timerUI.title = document.getElementById("mediaTitle"); timerUI.title = document.getElementById("timerUI_media_title");
    timerUI.title.innerHTML = timerUI.getTitle(); timerUI.title.innerHTML = timerUI.getTitle();
    addStyle("#timerUI #mediaTitle { position:fixed; text-shadow: 0 0 5px black; display:inline; display:-webkit-box; bottom:15pt; left:2pt; color:"+timerUI.color+"; font-family:"+timerUI.font_pack+"; font-size:20pt; width:60%; max-width:calc(100% - 500px); text-overflow: ellipsis; overflow: hidden; -webkit-box-orient: vertical; -webkit-line-clamp: 2; vertical-align: bottom;") addStyle("#timerUI #timerUI_media_title { position:fixed; text-shadow: 0 0 5px black; display:inline; display:-webkit-box; bottom:15pt; left:2pt; color:"+timerUI.color+"; font-family:"+timerUI.font_pack+"; font-size:20pt; width:60%; max-width:calc(100% - 500px); text-overflow: ellipsis; overflow: hidden; -webkit-box-orient: vertical; -webkit-line-clamp: 2; vertical-align: bottom;")


    // timer // timer
    appendChildWithID("button","playTimer",timerUI.div ); appendChildWithID("button","timerUI_media_timer",timerUI.div );
    timerUI.time = document.getElementById("playTimer"); timerUI.time = document.getElementById("timerUI_media_timer");
    timerUI.time.innerHTML="00:00:00"; timerUI.time.innerHTML="00:00:00";
    addStyle("#timerUI #playTimer { display:block; color:"+timerUI.color+"; position:fixed; cursor:pointer; font-size:50pt; text-shadow: 0 0 20px black; line-height:60pt; bottom:10pt; right:30pt; text-align:right; font-weight:400; font-family:"+timerUI.font_pack+"; } #playTimer .timer_linefeed { display:none; }", timerUI.div); addStyle("#timerUI #timerUI_media_timer { display:block; color:"+timerUI.color+"; position:fixed; cursor:pointer; font-size:50pt; text-shadow: 0 0 20px black; line-height:60pt; bottom:10pt; right:30pt; text-align:right; font-weight:400; font-family:"+timerUI.font_pack+"; } #timerUI #timerUI_media_timer .timer_linefeed { display:none; }", timerUI.div);


    // play/pause symbol // play/pause symbol
    appendChildWithID("button","playStatus",timerUI.div ); appendChildWithID("button","timerUI_playback_status",timerUI.div );
    timerUI.status = document.getElementById("playStatus"); timerUI.status = document.getElementById("timerUI_playback_status");
    timerUI.status.innerHTML="■"; timerUI.status.innerHTML="■";
    addStyle("#timerUI #playStatus { display:block; position:fixed; cursor:pointer; color:"+timerUI.color+"; font-size:24pt; line-height:40pt; bottom:30pt; right:3pt; font-family:none; }", timerUI.div); addStyle("#timerUI #timerUI_playback_status { display:block; position:fixed; cursor:pointer; color:"+timerUI.color+"; font-size:24pt; line-height:40pt; bottom:30pt; right:3pt; font-family:none; }", timerUI.div);


    // progress bar placeholder – put last to be at the top in stacking context // progress bar placeholder – put last to be at the top in stacking context
    appendChildWithID("div","progBarPlaceholder",timerUI.div ); appendChildWithID("div","timerUI_progress_placeholder",timerUI.div );
    timerUI.bar_placeholder = document.getElementById("progBarPlaceholder"); timerUI.bar_placeholder = document.getElementById("timerUI_progress_placeholder");
    addStyle("#timerUI #progBarPlaceholder { display:block; position:fixed; cursor:pointer; background-color:grey; height:8pt; width:100%; bottom:0; opacity:0.2; }", timerUI.div); addStyle("#timerUI #timerUI_progress_placeholder { display:block; position:fixed; cursor:pointer; background-color:grey; height:8pt; width:100%; bottom:0; opacity:0.2; }", timerUI.div);


    // responsive - at bottom to be able to override CSS properties without !important flag. // responsive - at bottom to be able to override CSS properties without !important flag.
    addStyle("@media (max-width:"+timerUI.width_breakpoint+"px) { #timerUI #playTimer { font-size:30pt; line-height:24pt; bottom:15pt; } #timerUI #playStatus { bottom:10pt; } } @media (max-width:500px) { #playTimer .timer_linefeed { display:inline; } #timerUI #playTimer .timer_slash { display:none; } } @media (max-width:"+timerUI.width_breakpoint+"px) { #timerUI #mediaTitle { font-size:10pt; -webkit-line-clamp: 3; max-width:60%;} } @media (max-width:480px) { #timerUI #mediaTitle { display: none; } } ", timerUI.div); addStyle("@media (max-width:"+timerUI.width_breakpoint+"px) { #timerUI #timerUI_media_timer { font-size:30pt; line-height:24pt; bottom:15pt; } #timerUI #timerUI_playback_status { bottom:10pt; } } @media (max-width:500px) { #timerUI #timerUI_media_timer .timer_linefeed { display:inline; } #timerUI #timerUI_media_timer .timer_slash { display:none; } } @media (max-width:"+timerUI.width_breakpoint+"px) { #timerUI #timerUI_buffer_bar { font-size:10pt; -webkit-line-clamp: 3; max-width:60%;} } @media (max-width:480px) { #timerUI #timerUI_buffer_bar { display: none; } } ", timerUI.div);
    timerUI.div.lastElementChild.classList.add("timerUI_responsive"); timerUI.div.lastElementChild.classList.add("timerUI_responsive");




    @@ -440,11 +506,11 @@ function timeUI() {
    ); );


    // Longer interval for buffer bar to minimize CPU usage // Longer interval for buffer bar to minimize CPU usage
    timerUI.interval.buffer = setInterval(timerUI.updateBufferSegment, 1000); timerUI.interval.buffer = setInterval(timerUI.updateBufferBar, 1000);




    // play and pause toggle // play and pause toggle
    timerUI.status.onclick = function() { togglePlay(media_element); timerUI.update(); }; timerUI.status.onclick = function() { togglePlay(media_element); timerUI.update(); timerUI.updateBufferBar(true); };
    // former code with object prototype caused compatibility issues on various sites: media_element.togglePlay(); // former code with object prototype caused compatibility issues on various sites: media_element.togglePlay();


    // toggle between elapsed, remaining, and elapsed/total time // toggle between elapsed, remaining, and elapsed/total time
    @@ -454,14 +520,15 @@ function timeUI() {
    case 1: timerUI.show_remaining = 2; timerUI.adaptTitleWidth(); break; case 1: timerUI.show_remaining = 2; timerUI.adaptTitleWidth(); break;
    case 2: timerUI.show_remaining = 0; timerUI.adaptTitleWidth(); break; case 2: timerUI.show_remaining = 0; timerUI.adaptTitleWidth(); break;
    } }
    timerUI.update(); timerUI.update(); timerUI.updateBufferBar(true);
    }; };


    // clickable progress bar (experimental) - no "timerUI." notation because inside main function // clickable progress bar (experimental) - no "timerUI." notation because inside main function
    timerUI.clickSeekBar = function(m){ timerUI.clickSeekBar = function(m){
    if (media_element) { if (media_element) {
    var clickPos = m.clientX / timerUI.bar_placeholder.offsetWidth; var clickPos = m.clientX / timerUI.bar_placeholder.offsetWidth;
    media_element.currentTime = media_element.duration * clickPos; media_element.currentTime = media_element.duration * clickPos;
    timerUI.update(); timerUI.updateBufferBar(true);
    } }
    } }
    function dragSeekBar(m){ // currently unused function dragSeekBar(m){ // currently unused
  9. HT-7 revised this gist May 24, 2022. 1 changed file with 223 additions and 69 deletions.
    292 changes: 223 additions & 69 deletions timerUI.js
    @@ -28,8 +28,28 @@ function customMediaElement(custom_media_element) {
    if (custom_media_element) { if (custom_media_element) {
    playerExists = true; playerExists = true;
    media_element = custom_media_element; media_element = custom_media_element;
    console.log("timerUI: Custom media element set. Reset using checkMediaType_enabled=true."); console.log("customMediaElement: Custom media element set. Reset using checkMediaType_enabled=true.");
    } else { console.error("timerUI: No such media element found."); } } else { console.error("customMediaElement: No such media element found."); }
    }
    var customTitleElement;

    function checkFileExtension(ext) {
    if (typeof(ext) == "string") {
    ext = ext.toLowerCase(); // case-insensitive
    // string
    if (document.location.toString().search(new RegExp(ext+"$", "i")) > -1) return true; else return false;
    } else if (typeof(ext) == "object") {
    // array – check against multiple strings
    for (var a=0; a < ext.length; a++) {
    if (document.location.toString().search(new RegExp(ext[a]+"$", "i")) > -1) return true;
    if (a == ext.length-1) return false; // if no matches after going through them all
    }
    }
    }

    function isDomain(domain) {
    // Using .search() instead of .includes() to improve browser compatibility.
    if (window.location.hostname.search(domain) >= 0) return true; else return false;
    } }


    // symbols // symbols
    @@ -55,29 +75,27 @@ function addStyle(new_style,parent_element) {
    } }


    // time variables // time variables
    var time_HH = 0; var media_time = {};
    var time_MM = 0;
    var time_SS = 0;


    // HH:MM:SS timer // HH:MM:SS timer
    function HMStimer_core(seconds) { function HMStimer_core(seconds) {


    // hours // hours
    time_HH = Math.floor( seconds/3600 ); media_time.HH = Math.floor( seconds/3600 );
    // leading zero // leading zero
    if ( seconds < 36000 ) time_HH = "0" + time_HH; if ( seconds < 36000 ) media_time.HH = "0" + media_time.HH;


    // minutes // minutes
    time_MM = Math.floor( seconds/60%60 ); media_time.MM = Math.floor( seconds/60%60 );
    // leading zero // leading zero
    if ( seconds%3600 < 600 ) time_MM = "0" + time_MM; if ( seconds%3600 < 600 ) media_time.MM = "0" + media_time.MM;


    // seconds // seconds
    time_SS = Math.floor( seconds%60 ); media_time.SS = Math.floor( seconds%60 );
    // leading zero // leading zero
    if ( seconds%60 < 10 ) time_SS = "0" + time_SS; if ( seconds%60 < 10 ) media_time.SS = "0" + media_time.SS;


    return time_HH+":"+time_MM+":"+time_SS; return media_time.HH+":"+media_time.MM+":"+media_time.SS;
    } }


    function HMStimer(seconds) { function HMStimer(seconds) {
    @@ -95,16 +113,16 @@ function HMStimer(seconds) {
    function MStimer_core(seconds) { function MStimer_core(seconds) {


    // minutes // minutes
    time_MM = Math.floor( seconds/60 ); media_time.MM = Math.floor( seconds/60 );
    // leading zero // leading zero
    if ( seconds%3600 < 600 ) time_MM = "0" + time_MM; if ( seconds%3600 < 600 ) media_time.MM = "0" + media_time.MM;


    // seconds // seconds
    time_SS = Math.floor( seconds%60 ); media_time.SS = Math.floor( seconds%60 );
    // leading zero // leading zero
    if ( seconds%60 < 10 ) time_SS = "0" + time_SS; if ( seconds%60 < 10 ) media_time.SS = "0" + media_time.SS;


    return time_MM+":"+time_SS; return media_time.MM+":"+media_time.SS;
    } }


    function MStimer(seconds) { function MStimer(seconds) {
    @@ -128,23 +146,44 @@ Object.prototype.togglePlay = function togglePlay() {


    // new function without object prototype for compatibility // new function without object prototype for compatibility
    function togglePlay(media_element) { function togglePlay(media_element) {
    media_element.paused ? media_element.play() : media_element.pause(); if (media_element) { // validate media element first to avoid errors
    } media_element.paused ? media_element.play() : media_element.pause();

    }
    // == Custom domain rules ==
    function isDomain(domain) {
    // Using .search() instead of .includes() to improve browser compatibility.
    if (window.location.hostname.search(domain) >= 0) return true; else return false;
    } }


    if (isDomain("dailymotion.com") && document.location.pathname.toString().search(/^\/embed\//) < 0 ) { // media file extension list
    // Dailymotion watch page, excluding embed page. var mediafileext = {
    customMediaElement(document.getElementById("player-body").contentWindow.document.getElementsByTagName("video")[0]); "video":[".mp4",".mpg",".mpeg",".mts",".mt2s",".m4v",".ts",".ogv",".wmv",".3gp",".3gpp"],
    } "audio":[".mp3",".wma",".wav",".ogg",".opus",".flac",".oga",".wma",".aac",".amr",".alac",".m4a"]
    };


    // == Main code == // == Main code ==
    if (! timerUI) var timerUI = new Object({}); // create parent object if none exists if (! timerUI) var timerUI = new Object({}); // create parent object if none exists


    // default system variables
    timerUI.debug_mode = false;
    timerUI.on = true;
    timerUI.buffer_on = true;
    timerUI.div = {}; // unset yet, declaring to prevent reference errors
    timerUI.interval = {};
    timerUI.show_remaining = 0; // 0: show elapsed time. 1: show remaining time. 2: show elapsed and total.
    timerUI.update_during_seek = true; // update timer while dragging seek bar
    timerUI.color = "rgb(49,136,255)"; // #38F – using RGB for compatibility.
    timerUI.gradient = "rgba(0,0,0,0.9)"; // using RGBA instead of hexadecimal for compatibility.
    timerUI.font_pack = "din,futura,'noto sans',ubuntu,'segoe ui',verdana,tahoma,roboto,'roboto light',arial,helvetica,'trebuchet ms',sans-serif,consolas,monospace";
    timerUI.width_breakpoint = 768; // pixels

    // console notifications and warnings (possibly to be expanded)
    timerUI.msg = {
    "notimer": "timerUI: No timer found; checking for media element again. Please try again.",
    "nomedia": "timerUI: no media element found on page. Stopping."
    }

    // text containers (no const for compatibility)
    var timer_linefeed = "<span class=timer_linefeed><br /></span>";
    var timer_slash = " <span class=timer_slash>/</span> ";

    // functions
    timerUI.toggle = function() { timerUI.toggle = function() {
    if (timerUI.div) { if (timerUI.div) {
    timerUI.update(); timerUI.update();
    @@ -158,7 +197,7 @@ timerUI.toggle = function() {
    } }
    timerUI.on ? timerUI.on = false : timerUI.on = true; timerUI.on ? timerUI.on = false : timerUI.on = true;
    } else { } else {
    console.warn("timerUI: No timer found; checking for media element again. Please try again."); console.warn(timerUI.msg.notimer);
    timeUI(); timeUI();
    } }
    }; };
    @@ -176,14 +215,71 @@ timerUI.toggleBuffer = function() {
    } }
    timerUI.buffer_on ? timerUI.buffer_on = false : timerUI.buffer_on = true; timerUI.buffer_on ? timerUI.buffer_on = false : timerUI.buffer_on = true;
    } else { } else {
    console.warn("timerUI: No timer found; checking for media element again. Please try again."); console.warn(timerUI.msg.notimer);
    timeUI(); timeUI();
    } }
    }; };


    var timer_linefeed = "<span class=timer_linefeed><br /></span>"; timerUI.toggleTitle = function() {
    var timer_slash = " <span class=timer_slash>/</span> "; if (timerUI.div) {
    timerUI.update();
    if (timerUI.title_on) {
    timerUI.title.style.display = "none";
    console.log("timerUI title off");
    }
    if (! timerUI.title_on ) {
    timerUI.title.style.display = "block";
    console.log("timerUI title on");
    }
    timerUI.title_on ? timerUI.title_on = false : timerUI.title_on = true;
    } else {
    console.warn(timerUI.msg.notimer);
    timeUI();
    }
    };

    timerUI.getTitle = function() {
    timerUI.domainRules();
    if (customTitleElement) timerUI.newTitle = customTitleElement.innerHTML;
    else { // skipping this whole part if no custom title is specified
    timerUI.newTitle = document.title;
    timerUI.titleDomainRules();
    }
    timerUI.updateFileIcon();
    return timerUI.fileIcon+"&nbsp;"+timerUI.newTitle;
    }

    timerUI.guessMediaType = function() {
    if (isDomain("youtube.com") || isDomain("dailymotion.com") ) return "video";
    if (document.location.pathname.toString().search(/^\/video\//) > -1) return "video";
    if (checkFileExtension(mediafileext.video) ) return "video";
    if (checkFileExtension(mediafileext.audio) ) return "audio";
    return "unknown"; // if nothing detected
    }

    timerUI.updateFileIcon = function() {
    timerUI.fileIcon = timerUI.guessMediaType();
    switch(timerUI.fileIcon) {
    case "video": timerUI.fileIcon = "🎞️"; break;
    case "audio": timerUI.fileIcon = "♫"; break;
    case "unknown": timerUI.fileIcon = "📄"; break;
    }
    }


    timerUI.adaptTitleWidth = function() {
    if (media_element.duration > 3600 && timerUI.show_remaining == 2 && window.innerWidth > timerUI.width_breakpoint)
    timerUI.title.style.maxWidth = "calc(100% - 670px)";
    else
    timerUI.title.style.maxWidth = "calc(100% - 400px)";
    }

    window.addEventListener('resize', function() {
    if (window.innerWidth < timerUI.width_breakpoint) timerUI.title.removeAttribute("style");
    } );

    timerUI.update = function() { timerUI.update = function() {
    if (media_element) {
    timerUI.bar.style.width=media_element.currentTime / media_element.duration * 100 + "%"; timerUI.bar.style.width=media_element.currentTime / media_element.duration * 100 + "%";
    if (media_element.buffered.length > 0) { if (media_element.buffered.length > 0) {
    // prevent "DOMException: Index or size is negative or greater than the allowed amount" // prevent "DOMException: Index or size is negative or greater than the allowed amount"
    @@ -215,19 +311,28 @@ timerUI.update = function() {
    media_element.paused == false ? media_element.paused == false ?
    timerUI.status.innerHTML=symbol_play timerUI.status.innerHTML=symbol_play
    : timerUI.status.innerHTML=symbol_pause; : timerUI.status.innerHTML=symbol_pause;

    } else { timerUI.stop(); console.warn(timerUI.msg.nomedia) }
    }; };
    timerUI.updateTitle = function() { if (timerUI.title) timerUI.title.innerHTML = timerUI.getTitle(); }

    // update title on URL change
    addEventListener('popstate', timerUI.updateTitle);


    // update title fallback
    timerUI.interval.updateTitle = setInterval(timerUI.updateTitle, 2000);


    timerUI.updateBufferSegment = function() { timerUI.updateBufferSegment = function() { if (media_element) {
    // find out first buffer segment after current playback position // find out first buffer segment after current playback position
    media_element.buffered.length > 0 ? timerUI.buffer_segment=media_element.buffered.length-1 : timerUI.buffer_segment=0; media_element.buffered.length > 0 ? timerUI.buffer_segment=media_element.buffered.length-1 : timerUI.buffer_segment=0;
    // media_element.buffered.length is zero until player is initialized // media_element.buffered.length is zero until player is initialized
    // prevent timerUI.buffer_segment from going negative, as it would cause a DOMException error // prevent timerUI.buffer_segment from going negative, as it would cause a DOMException error
    if (timerUI.buffer_on && timerUI.buffer_segment > 0) { if ( timerUI.buffer_on && timerUI.buffer_segment > 0) {
    while ( media_element.buffered.end(timerUI.buffer_segment-1) > media_element.currentTime && timerUI.buffer_segment > 0 ) { while (media_element.buffered.end(timerUI.buffer_segment-1) > media_element.currentTime && timerUI.buffer_segment > 1 ) {
    timerUI.buffer_segment-- ; timerUI.buffer_segment-- ;
    } }
    } }
    }
    }; };


    timerUI.setColor = function(newColor) { timerUI.setColor = function(newColor) {
    @@ -239,46 +344,48 @@ timerUI.setColor = function(newColor) {
    // (deprecated due to new buffer bar) timerUI.bar_placeholder.style.backgroundColor=timerUI.color; // (deprecated due to new buffer bar) timerUI.bar_placeholder.style.backgroundColor=timerUI.color;
    timerUI.time.style.color=timerUI.color; timerUI.time.style.color=timerUI.color;
    timerUI.status.style.color=timerUI.color; timerUI.status.style.color=timerUI.color;
    timerUI.title.style.color=timerUI.color;
    }; };


    timerUI.setGradient = function(newGradient) { timerUI.setGradient = function(newGradient) {
    newGradient == "default" ? timerUI.gradient="rgba(0,0,0,0.7)" : timerUI.gradient = newGradient; newGradient == "default" ? timerUI.gradient="rgba(0,0,0,0.9)" : timerUI.gradient = newGradient;
    timerUI.gradient_placeholder.style.backgroundImage="linear-gradient(to top,"+timerUI.gradient+", rgba(0,0,0,0) )"; timerUI.gradient_placeholder.style.backgroundImage="linear-gradient(to top,"+timerUI.gradient+", rgba(0,0,0,0) )";
    }; };


    timerUI.setFont = function(newFont) { timerUI.setFont = function(newFont) {
    timerUI.time.style.fontFamily=newFont; timerUI.time.style.fontFamily=newFont;
    timerUI.title.style.fontFamily=newFont;
    };

    timerUI.stop = function() {
    timerUI.status.innerHTML="■";
    timerUI.bar.style.width=0;
    timerUI.buffer_bar.style.width=0;
    timerUI.time.innerHTML=HMStimer(undefined);
    }; };


    // Additional checks to ensure the player is detected // Additional checks to ensure the player is detected
    window.onclick = function() { checkMediaType();timeUI(); }; window.onclick = function() { checkMediaType();timeUI(); };
    window.addEventListener("keydown", function() { checkMediaType();timeUI(); } ); window.addEventListener("keydown", function() { checkMediaType();timeUI(); } );


    timeUI(); // slightly different name to prevent naming collision with timerUI object


    function timeUI() { function timeUI() {
    // slightly different name to prevent naming collision with timerUI object


    checkMediaType(); checkMediaType();
    timerUI.domainRules(); // load domain rules
    // add timerUI if it does not already exist // add timerUI if it does not already exist
    if ( ( ! document.getElementById("timerUI") ) && playerExists ) { if ( ( ! document.getElementById("timerUI") ) && playerExists ) {


    // Declaring variables
    timerUI.on = true;
    timerUI.buffer_on = true;
    timerUI.div = {}; // unset yet, declaring to prevent reference errors
    timerUI.show_remaining = 0; // 0: show elapsed time. 1: show remaining time. 2: show elapsed and total.
    timerUI.update_during_seek = true; // update timer while dragging seek bar
    timerUI.color = "rgb(49,136,255)"; // #38F – using RGB for compatibility.
    timerUI.gradient = "rgba(0,0,0,0.7)"; // using RGBA instead of hexadecimal for compatibility.

    // Adding elements // Adding elements


    // parent element // parent element
    appendChildWithID("div","timerUI"); appendChildWithID("div","timerUI");
    timerUI.div = document.getElementById("timerUI"); timerUI.div = document.getElementById("timerUI");


    // button styling // button styling
    addStyle("#timerUI button { background:unset; border:unset; outline:unset; line-height:unset; }", timerUI.div); addStyle("#timerUI button { background:none; border:none; outline:none; line-height:unset; } #timerUI button:hover { filter: brightness(1.2); } #timerUI button:active { filter: brightness(0.6); }", timerUI.div);
    // to suppress button background and border on earlier browser versions
    timerUI.div.lastElementChild.classList.add("timerUI_buttons"); // label to improve visibility in page inspector timerUI.div.lastElementChild.classList.add("timerUI_buttons"); // label to improve visibility in page inspector


    // background gradient // background gradient
    @@ -296,11 +403,17 @@ function timeUI() {
    timerUI.buffer_bar = document.getElementById("bufferBar"); timerUI.buffer_bar = document.getElementById("bufferBar");
    addStyle("#timerUI #bufferBar { display:block; position:fixed; background-color:"+timerUI.color+"; height:8pt; width:75%; bottom:0; opacity:0.4; }", timerUI.div); addStyle("#timerUI #bufferBar { display:block; position:fixed; background-color:"+timerUI.color+"; height:8pt; width:75%; bottom:0; opacity:0.4; }", timerUI.div);


    // media title
    appendChildWithID("div","mediaTitle",timerUI.div );
    timerUI.title = document.getElementById("mediaTitle");
    timerUI.title.innerHTML = timerUI.getTitle();
    addStyle("#timerUI #mediaTitle { position:fixed; text-shadow: 0 0 5px black; display:inline; display:-webkit-box; bottom:15pt; left:2pt; color:"+timerUI.color+"; font-family:"+timerUI.font_pack+"; font-size:20pt; width:60%; max-width:calc(100% - 500px); text-overflow: ellipsis; overflow: hidden; -webkit-box-orient: vertical; -webkit-line-clamp: 2; vertical-align: bottom;")

    // timer // timer
    appendChildWithID("button","playTimer",timerUI.div ); appendChildWithID("button","playTimer",timerUI.div );
    timerUI.time = document.getElementById("playTimer"); timerUI.time = document.getElementById("playTimer");
    timerUI.time.innerHTML="00:00:00"; timerUI.time.innerHTML="00:00:00";
    addStyle("#timerUI #playTimer { display:block; color:"+timerUI.color+"; position:fixed; cursor:pointer; font-size:50pt; text-shadow: 0 0 20px black; line-height:60pt; bottom:10pt; right:30pt; text-align:right; font-weight:400; font-family:din,futura,'noto sans',ubuntu,'segoe ui',verdana,tahoma,roboto,'roboto light',arial,helvetica,'trebuchet ms',sans-serif,consolas,monospace; } #playTimer .timer_linefeed { display:none; }", timerUI.div); addStyle("#timerUI #playTimer { display:block; color:"+timerUI.color+"; position:fixed; cursor:pointer; font-size:50pt; text-shadow: 0 0 20px black; line-height:60pt; bottom:10pt; right:30pt; text-align:right; font-weight:400; font-family:"+timerUI.font_pack+"; } #playTimer .timer_linefeed { display:none; }", timerUI.div);


    // play/pause symbol // play/pause symbol
    appendChildWithID("button","playStatus",timerUI.div ); appendChildWithID("button","playStatus",timerUI.div );
    @@ -311,24 +424,23 @@ function timeUI() {
    // progress bar placeholder – put last to be at the top in stacking context // progress bar placeholder – put last to be at the top in stacking context
    appendChildWithID("div","progBarPlaceholder",timerUI.div ); appendChildWithID("div","progBarPlaceholder",timerUI.div );
    timerUI.bar_placeholder = document.getElementById("progBarPlaceholder"); timerUI.bar_placeholder = document.getElementById("progBarPlaceholder");
    addStyle("#progBarPlaceholder { display:block; position:fixed; cursor:pointer; background-color:grey; height:8pt; width:100%; bottom:0; opacity:0.2; }", timerUI.div); addStyle("#timerUI #progBarPlaceholder { display:block; position:fixed; cursor:pointer; background-color:grey; height:8pt; width:100%; bottom:0; opacity:0.2; }", timerUI.div);


    // responsive - at bottom to be able to override CSS properties without !important flag. // responsive - at bottom to be able to override CSS properties without !important flag.
    addStyle("@media (max-width:768px) { #timerUI #playTimer { font-size:30pt; line-height:24pt; bottom:15pt; } #timerUI #playStatus { bottom:10pt; } } @media (max-width:500px) { #playTimer .timer_linefeed { display:inline; } #timerUI #playTimer .timer_slash { display:none; } }", timerUI.div); addStyle("@media (max-width:"+timerUI.width_breakpoint+"px) { #timerUI #playTimer { font-size:30pt; line-height:24pt; bottom:15pt; } #timerUI #playStatus { bottom:10pt; } } @media (max-width:500px) { #playTimer .timer_linefeed { display:inline; } #timerUI #playTimer .timer_slash { display:none; } } @media (max-width:"+timerUI.width_breakpoint+"px) { #timerUI #mediaTitle { font-size:10pt; -webkit-line-clamp: 3; max-width:60%;} } @media (max-width:480px) { #timerUI #mediaTitle { display: none; } } ", timerUI.div);
    timerUI.div.lastElementChild.classList.add("timerUI_responsive"); timerUI.div.lastElementChild.classList.add("timerUI_responsive");




    // update timer during playback every fifteenth of a second and while mouse is dragging progress bar // update timer during playback every fifteenth of a second and while mouse is dragging progress bar
    timerUI.updateInterval = setInterval( timerUI.interval.update = setInterval(
    function() { if ( function() { if (
    ( timerUI.update_during_seek && mousedown_status ) ( media_element /* exists? */ && timerUI.update_during_seek && mousedown_status )
    || ( timerUI.on && ! media_element.paused ) || ( media_element && timerUI.on && ! media_element.paused )
    ) timerUI.update(); ) timerUI.update(); }, 1000/15
    }, 1000/15
    ); );


    // Longer interval for buffer bar to minimize CPU usage // Longer interval for buffer bar to minimize CPU usage
    timerUI.updateBufferInterval = setInterval(timerUI.updateBufferSegment, 1000); timerUI.interval.buffer = setInterval(timerUI.updateBufferSegment, 1000);




    // play and pause toggle // play and pause toggle
    @@ -339,16 +451,18 @@ function timeUI() {
    timerUI.time.onclick = function() { timerUI.time.onclick = function() {
    switch(timerUI.show_remaining) { switch(timerUI.show_remaining) {
    case 0: timerUI.show_remaining = 1; break; case 0: timerUI.show_remaining = 1; break;
    case 1: timerUI.show_remaining = 2; break; case 1: timerUI.show_remaining = 2; timerUI.adaptTitleWidth(); break;
    case 2: timerUI.show_remaining = 0; break; case 2: timerUI.show_remaining = 0; timerUI.adaptTitleWidth(); break;
    } }
    timerUI.update(); timerUI.update();
    }; };


    // clickable progress bar (experimental) - no "timerUI." notation because inside main function // clickable progress bar (experimental) - no "timerUI." notation because inside main function
    function clickSeekBar(m){ timerUI.clickSeekBar = function(m){
    var clickPos = m.clientX / timerUI.bar_placeholder.offsetWidth; if (media_element) {
    media_element.currentTime = media_element.duration * clickPos; var clickPos = m.clientX / timerUI.bar_placeholder.offsetWidth;
    media_element.currentTime = media_element.duration * clickPos;
    }
    } }
    function dragSeekBar(m){ // currently unused function dragSeekBar(m){ // currently unused
    m = m || window.event; m = m || window.event;
    @@ -359,7 +473,7 @@ function timeUI() {
    if (! mousedown_status) { clearInterval(dragSeekBarInterval); return; } if (! mousedown_status) { clearInterval(dragSeekBarInterval); return; }
    }, 200); }, 200);
    } }
    timerUI.bar_placeholder.addEventListener("mousedown", clickSeekBar ); timerUI.bar_placeholder.addEventListener("mousedown", timerUI.clickSeekBar );
    // (incomplete) timerUI.bar_placeholder.addEventListener("mousemove", dragSeekBar ); // (incomplete) timerUI.bar_placeholder.addEventListener("mousemove", dragSeekBar );
    // (obsolete) timerUI.bar_placeholder.addEventListener("mouseup", clickSeekBar ); // (obsolete) timerUI.bar_placeholder.addEventListener("mouseup", clickSeekBar );


    @@ -372,19 +486,59 @@ function timeUI() {
    window.addEventListener("keyup", function() { setTimeout(timerUI.update, 200); } ); window.addEventListener("keyup", function() { setTimeout(timerUI.update, 200); } );


    // prevent detaching from player on sites with playlists such as Internet Archive // prevent detaching from player on sites with playlists such as Internet Archive
    timerUI.checkMediaInterval = setInterval( checkMediaType,1000 ); timerUI.interval.checkMedia = setInterval( checkMediaType,1000 );


    // prevent indicating "▶" after playback finished // prevent indicating "▶" after playback finished
    timerUI.checkPausedInterval = setInterval( function() { timerUI.interval.checkPaused = setInterval( function() {
    if (media_element.paused && ! timerUI.pause_checked) { if ( media_element /* exists? */ && media_element.paused && ! timerUI.pause_checked) {
    timerUI.update(); timerUI.pause_checked = true; timerUI.update(); timerUI.pause_checked = true;
    } else { timerUI.pause_checked = false; } if (timerUI.debug_mode) console.debug("timerUI: checking paused status: "+media_element.paused);
    } else if ( media_element && ! media_element.paused ) { timerUI.pause_checked = false; }
    // to avoid redundant checks while paused
    },1000 ); },1000 );


    } else { } else {
    // warn in console that no player exists; prevent repetition // warn in console that no player exists; prevent repetition
    if (! playerExists && ! timerUI.noMediaWarned) { if (! playerExists && ! timerUI.noMediaWarned) {
    console.warn("timerUI: no media element found on page."); timerUI.noMediaWarned = true; console.warn(timerUI.msg.nomedia); timerUI.noMediaWarned = true;
    } }
    } }
    } }

    // == Custom domain rules ==
    timerUI.domainRules = function() {
    if (isDomain("dailymotion.com") && document.location.pathname.toString().search(/^\/embed\//) < 0 ) {
    // Dailymotion watch page, excluding embed page.
    customMediaElement(document.getElementById("player-body").contentWindow.document.getElementsByTagName("video")[0]);
    customTitleElement = document.getElementById("media-title"); // for unlisted
    }
    };

    timerUI.titleDomainRules = function() {
    // custom domain rules for title
    if ( isDomain("youtube.com") || isDomain("dailymotion.com")
    || isDomain("wikimedia.org") || isDomain("wikipedia.org")
    || isDomain("wikiversity.org") || isDomain("wikibooks.org")
    || isDomain("mediawiki.org")
    ) {
    // negative lookahead regular expression – trim after last dash
    // Match both normal dash "-" and ndash "–", since the German-language wikis use the latter.
    timerUI.newTitle = decodeURI(timerUI.newTitle.substring(0,timerUI.newTitle.search(/(-|)(?:.(?!(-|)))+$/)-1 ) );
    }
    // remove "File:" prefix on wikis
    if ( isDomain("wiki") ) {
    if (document.title.search(/^File:/) == 0 ) timerUI.newTitle = timerUI.newTitle.substring(5);
    if (document.title.search(/^Datei:/) == 0 ) timerUI.newTitle = timerUI.newTitle.substring(6);
    }

    if ( isDomain("archive.org") && ! isDomain("web.archive.org") ) {
    timerUI.newTitle = (document.location.toString()+"").substring((document.location.toString()+" ").search(/\/(?:.(?!\/))+\/?$/)+1 );
    // after last slash (additional space prevents full URL from being matched)
    if (timerUI.newTitle != "") { timerUI.newTitle += " - " };
    timerUI.newTitle += document.title.toString().substring(0,document.title.toString().search(/:(?:.(?!:))+(?:.(?!:))+/)-1 );
    // trim after second-last colon, -1 to remove space at end
    }
    };

    // == Master function ==
    timeUI();
  10. HT-7 revised this gist May 20, 2022. 1 changed file with 19 additions and 17 deletions.
    36 changes: 19 additions & 17 deletions timerUI.js
    @@ -33,9 +33,8 @@ function customMediaElement(custom_media_element) {
    } }


    // symbols // symbols
    var symbol_play = "▶&#xFE0E;"; var symbol_play = "▶&#xFE0E;&thinsp;"; // thin space for alignment
    var symbol_pause = "⏸&#xFE0E;"; var symbol_pause="&#10074;&thinsp;&#10074;"; // instead of "⏸" due to Edge browser putting an immutable blue box around it.
    // "&#xFE0E;" is necessary to prevent the symbols from being rendered as clip art icons bundled by vendors of browsers and devices.


    // mousedown status // mousedown status
    var mousedown_status; var mousedown_status;
    @@ -225,7 +224,7 @@ timerUI.updateBufferSegment = function() {
    // media_element.buffered.length is zero until player is initialized // media_element.buffered.length is zero until player is initialized
    // prevent timerUI.buffer_segment from going negative, as it would cause a DOMException error // prevent timerUI.buffer_segment from going negative, as it would cause a DOMException error
    if (timerUI.buffer_on && timerUI.buffer_segment > 0) { if (timerUI.buffer_on && timerUI.buffer_segment > 0) {
    while ( media_element.buffered.end(timerUI.buffer_segment-1) > media_element.currentTime ) { while ( media_element.buffered.end(timerUI.buffer_segment-1) > media_element.currentTime && timerUI.buffer_segment > 0 ) {
    timerUI.buffer_segment-- ; timerUI.buffer_segment-- ;
    } }
    } }
    @@ -279,7 +278,7 @@ function timeUI() {
    timerUI.div = document.getElementById("timerUI"); timerUI.div = document.getElementById("timerUI");


    // button styling // button styling
    addStyle("#timerUI button \{ background:unset; border:unset; outline:unset; line-height:unset; \}", timerUI.div); addStyle("#timerUI button { background:unset; border:unset; outline:unset; line-height:unset; }", timerUI.div);
    timerUI.div.lastElementChild.classList.add("timerUI_buttons"); // label to improve visibility in page inspector timerUI.div.lastElementChild.classList.add("timerUI_buttons"); // label to improve visibility in page inspector


    // background gradient // background gradient
    @@ -301,7 +300,7 @@ function timeUI() {
    appendChildWithID("button","playTimer",timerUI.div ); appendChildWithID("button","playTimer",timerUI.div );
    timerUI.time = document.getElementById("playTimer"); timerUI.time = document.getElementById("playTimer");
    timerUI.time.innerHTML="00:00:00"; timerUI.time.innerHTML="00:00:00";
    addStyle("#timerUI #playTimer { display:block; color:"+timerUI.color+"; position:fixed; cursor:pointer; font-size:50pt; line-height:60pt; bottom:10pt; right:25pt; text-align:right; font-weight:400; font-family:din,futura,'noto sans',ubuntu,'segoe ui',verdana,tahoma,roboto,'roboto light',arial,helvetica,'trebuchet ms',sans-serif,consolas,monospace; } #playTimer .timer_linefeed { display:none; }", timerUI.div); addStyle("#timerUI #playTimer { display:block; color:"+timerUI.color+"; position:fixed; cursor:pointer; font-size:50pt; text-shadow: 0 0 20px black; line-height:60pt; bottom:10pt; right:30pt; text-align:right; font-weight:400; font-family:din,futura,'noto sans',ubuntu,'segoe ui',verdana,tahoma,roboto,'roboto light',arial,helvetica,'trebuchet ms',sans-serif,consolas,monospace; } #playTimer .timer_linefeed { display:none; }", timerUI.div);


    // play/pause symbol // play/pause symbol
    appendChildWithID("button","playStatus",timerUI.div ); appendChildWithID("button","playStatus",timerUI.div );
    @@ -315,7 +314,7 @@ function timeUI() {
    addStyle("#progBarPlaceholder { display:block; position:fixed; cursor:pointer; background-color:grey; height:8pt; width:100%; bottom:0; opacity:0.2; }", timerUI.div); addStyle("#progBarPlaceholder { display:block; position:fixed; cursor:pointer; background-color:grey; height:8pt; width:100%; bottom:0; opacity:0.2; }", timerUI.div);


    // responsive - at bottom to be able to override CSS properties without !important flag. // responsive - at bottom to be able to override CSS properties without !important flag.
    addStyle("@media (max-width:768px) \{ #timerUI #playTimer \{ font-size:30pt; line-height:24pt; bottom:15pt; \} #timerUI #playStatus \{ bottom:10pt; \} \} @media (max-width:500px) \{ #playTimer .timer_linefeed { display:inline; } #timerUI #playTimer .timer_slash \{ display:none; \} \}", timerUI.div); addStyle("@media (max-width:768px) { #timerUI #playTimer { font-size:30pt; line-height:24pt; bottom:15pt; } #timerUI #playStatus { bottom:10pt; } } @media (max-width:500px) { #playTimer .timer_linefeed { display:inline; } #timerUI #playTimer .timer_slash { display:none; } }", timerUI.div);
    timerUI.div.lastElementChild.classList.add("timerUI_responsive"); timerUI.div.lastElementChild.classList.add("timerUI_responsive");




    @@ -368,21 +367,24 @@ function timeUI() {


    // == Patches == // == Patches ==


    // prevent missing out on pausing from inside a site's existing player // prevent missing out on pausing from inside a site's existing player
    window.addEventListener("mouseup", function() { setTimeout(timerUI.update, 200); } ); window.addEventListener("mouseup", function() { setTimeout(timerUI.update, 200); } );
    window.addEventListener("keyup", function() { setTimeout(timerUI.update, 200); } ); window.addEventListener("keyup", function() { setTimeout(timerUI.update, 200); } );


    // prevent detaching from player on sites with playlists such as Internet Archive // prevent detaching from player on sites with playlists such as Internet Archive
    timerUI.checkMediaInterval = setInterval( checkMediaType,1000 ); timerUI.checkMediaInterval = setInterval( checkMediaType,1000 );


    // prevent indicating "▶" after playback finished // prevent indicating "▶" after playback finished
    timerUI.checkPaused = setInterval( function() { timerUI.checkPausedInterval = setInterval( function() {
    if (media_element.paused && timerUI.status.innerHTML!=symbol_pause) timerUI.update(); if (media_element.paused && ! timerUI.pause_checked) {
    timerUI.update(); timerUI.pause_checked = true;
    } else { timerUI.pause_checked = false; }
    },1000 ); },1000 );

    } else { } else {
    // warn in console that no player exists; prevent repetition // warn in console that no player exists; prevent repetition
    if (! playerExists && ! timerUI.noMediaWarned) { if (! playerExists && ! timerUI.noMediaWarned) {
    console.warn("timerUI: no media element found on page."); timerUI.noMediaWarned = true; console.warn("timerUI: no media element found on page."); timerUI.noMediaWarned = true;
    } }
    } }
    } }
  11. HT-7 revised this gist May 20, 2022. 1 changed file with 8 additions and 3 deletions.
    11 changes: 8 additions & 3 deletions timerUI.js
    @@ -32,6 +32,11 @@ function customMediaElement(custom_media_element) {
    } else { console.error("timerUI: No such media element found."); } } else { console.error("timerUI: No such media element found."); }
    } }


    // symbols
    var symbol_play = "▶&#xFE0E;";
    var symbol_pause = "⏸&#xFE0E;";
    // "&#xFE0E;" is necessary to prevent the symbols from being rendered as clip art icons bundled by vendors of browsers and devices.

    // mousedown status // mousedown status
    var mousedown_status; var mousedown_status;
    window.addEventListener("mousedown", function(){mousedown_status=true; } ); window.addEventListener("mousedown", function(){mousedown_status=true; } );
    @@ -209,8 +214,8 @@ timerUI.update = function() {
    } }


    media_element.paused == false ? media_element.paused == false ?
    timerUI.status.innerHTML="▶" timerUI.status.innerHTML=symbol_play
    : timerUI.status.innerHTML="⏸"; : timerUI.status.innerHTML=symbol_pause;
    }; };




    @@ -372,7 +377,7 @@ function timeUI() {


    // prevent indicating "▶" after playback finished // prevent indicating "▶" after playback finished
    timerUI.checkPaused = setInterval( function() { timerUI.checkPaused = setInterval( function() {
    if (media_element.paused && timerUI.status.innerHTML!="⏸") timerUI.update(); if (media_element.paused && timerUI.status.innerHTML!=symbol_pause) timerUI.update();
    },1000 ); },1000 );
    } else { } else {
    // warn in console that no player exists; prevent repetition // warn in console that no player exists; prevent repetition
  12. HT-7 revised this gist May 20, 2022. 1 changed file with 40 additions and 4 deletions.
    44 changes: 40 additions & 4 deletions timerUI.js
    @@ -144,7 +144,7 @@ if (! timerUI) var timerUI = new Object({}); // create parent object if none exi
    timerUI.toggle = function() { timerUI.toggle = function() {
    if (timerUI.div) { if (timerUI.div) {
    timerUI.update(); timerUI.update();
    if (timerUI.on ) { if (timerUI.on) {
    timerUI.div.style.display = "none"; timerUI.div.style.display = "none";
    console.log("timerUI off"); console.log("timerUI off");
    } }
    @@ -159,14 +159,32 @@ timerUI.toggle = function() {
    } }
    }; };


    timerUI.toggleBuffer = function() {
    if (timerUI.div) {
    timerUI.update();
    if (timerUI.buffer_on) {
    timerUI.buffer_bar.style.display = "none";
    console.log("timerUI buffer bar off");
    }
    if (! timerUI.buffer_on ) {
    timerUI.buffer_bar.style.display = "block";
    console.log("timerUI buffer bar on");
    }
    timerUI.buffer_on ? timerUI.buffer_on = false : timerUI.buffer_on = true;
    } else {
    console.warn("timerUI: No timer found; checking for media element again. Please try again.");
    timeUI();
    }
    };

    var timer_linefeed = "<span class=timer_linefeed><br /></span>"; var timer_linefeed = "<span class=timer_linefeed><br /></span>";
    var timer_slash = " <span class=timer_slash>/</span> "; var timer_slash = " <span class=timer_slash>/</span> ";
    timerUI.update = function() { timerUI.update = function() {
    timerUI.bar.style.width=media_element.currentTime / media_element.duration * 100 + "%"; timerUI.bar.style.width=media_element.currentTime / media_element.duration * 100 + "%";
    if (media_element.buffered.length > 0) { if (media_element.buffered.length > 0) {
    // prevent "DOMException: Index or size is negative or greater than the allowed amount" // prevent "DOMException: Index or size is negative or greater than the allowed amount"
    timerUI.buffer_bar.style.width=media_element.buffered.end(media_element.buffered.length-1) / media_element.duration * 100 + "%"; timerUI.buffer_bar.style.width=media_element.buffered.end(timerUI.buffer_segment) / media_element.duration * 100 + "%";
    } } else { timerUI.buffer_bar.style.width="0%"; }


    switch(timerUI.show_remaining) { switch(timerUI.show_remaining) {
    // 0: "HH:MM:SS" 1: "-HH:MM:SS" 2: "MM:SS / MM:SS" or "HH:MM:SS / HH:MM:SS" // 0: "HH:MM:SS" 1: "-HH:MM:SS" 2: "MM:SS / MM:SS" or "HH:MM:SS / HH:MM:SS"
    @@ -195,6 +213,19 @@ timerUI.update = function() {
    : timerUI.status.innerHTML="⏸"; : timerUI.status.innerHTML="⏸";
    }; };



    timerUI.updateBufferSegment = function() {
    // find out first buffer segment after current playback position
    media_element.buffered.length > 0 ? timerUI.buffer_segment=media_element.buffered.length-1 : timerUI.buffer_segment=0;
    // media_element.buffered.length is zero until player is initialized
    // prevent timerUI.buffer_segment from going negative, as it would cause a DOMException error
    if (timerUI.buffer_on && timerUI.buffer_segment > 0) {
    while ( media_element.buffered.end(timerUI.buffer_segment-1) > media_element.currentTime ) {
    timerUI.buffer_segment-- ;
    }
    }
    };

    timerUI.setColor = function(newColor) { timerUI.setColor = function(newColor) {
    newColor == "default" ? timerUI.color="rgb(49,136,255)" /* #38F */ : timerUI.color = newColor; newColor == "default" ? timerUI.color="rgb(49,136,255)" /* #38F */ : timerUI.color = newColor;


    @@ -229,6 +260,7 @@ function timeUI() {


    // Declaring variables // Declaring variables
    timerUI.on = true; timerUI.on = true;
    timerUI.buffer_on = true;
    timerUI.div = {}; // unset yet, declaring to prevent reference errors timerUI.div = {}; // unset yet, declaring to prevent reference errors
    timerUI.show_remaining = 0; // 0: show elapsed time. 1: show remaining time. 2: show elapsed and total. timerUI.show_remaining = 0; // 0: show elapsed time. 1: show remaining time. 2: show elapsed and total.
    timerUI.update_during_seek = true; // update timer while dragging seek bar timerUI.update_during_seek = true; // update timer while dragging seek bar
    @@ -258,7 +290,7 @@ function timeUI() {
    // buffer bar // buffer bar
    appendChildWithID("div","bufferBar",timerUI.div ); appendChildWithID("div","bufferBar",timerUI.div );
    timerUI.buffer_bar = document.getElementById("bufferBar"); timerUI.buffer_bar = document.getElementById("bufferBar");
    addStyle("#timerUI #bufferBar { display:block; position:fixed; background-color:"+timerUI.color+"; box-shadow: 0 0 30px 0px "+timerUI.color+"; height:8pt; width:75%; bottom:0; opacity:0.4; }", timerUI.div); addStyle("#timerUI #bufferBar { display:block; position:fixed; background-color:"+timerUI.color+"; height:8pt; width:75%; bottom:0; opacity:0.4; }", timerUI.div);


    // timer // timer
    appendChildWithID("button","playTimer",timerUI.div ); appendChildWithID("button","playTimer",timerUI.div );
    @@ -291,6 +323,10 @@ function timeUI() {
    }, 1000/15 }, 1000/15
    ); );


    // Longer interval for buffer bar to minimize CPU usage
    timerUI.updateBufferInterval = setInterval(timerUI.updateBufferSegment, 1000);


    // play and pause toggle // play and pause toggle
    timerUI.status.onclick = function() { togglePlay(media_element); timerUI.update(); }; timerUI.status.onclick = function() { togglePlay(media_element); timerUI.update(); };
    // former code with object prototype caused compatibility issues on various sites: media_element.togglePlay(); // former code with object prototype caused compatibility issues on various sites: media_element.togglePlay();
  13. HT-7 revised this gist May 20, 2022. 1 changed file with 11 additions and 1 deletion.
    12 changes: 11 additions & 1 deletion timerUI.js
    @@ -163,6 +163,10 @@ var timer_linefeed = "<span class=timer_linefeed><br /></span>";
    var timer_slash = " <span class=timer_slash>/</span> "; var timer_slash = " <span class=timer_slash>/</span> ";
    timerUI.update = function() { timerUI.update = function() {
    timerUI.bar.style.width=media_element.currentTime / media_element.duration * 100 + "%"; timerUI.bar.style.width=media_element.currentTime / media_element.duration * 100 + "%";
    if (media_element.buffered.length > 0) {
    // prevent "DOMException: Index or size is negative or greater than the allowed amount"
    timerUI.buffer_bar.style.width=media_element.buffered.end(media_element.buffered.length-1) / media_element.duration * 100 + "%";
    }


    switch(timerUI.show_remaining) { switch(timerUI.show_remaining) {
    // 0: "HH:MM:SS" 1: "-HH:MM:SS" 2: "MM:SS / MM:SS" or "HH:MM:SS / HH:MM:SS" // 0: "HH:MM:SS" 1: "-HH:MM:SS" 2: "MM:SS / MM:SS" or "HH:MM:SS / HH:MM:SS"
    @@ -195,8 +199,9 @@ timerUI.setColor = function(newColor) {
    newColor == "default" ? timerUI.color="rgb(49,136,255)" /* #38F */ : timerUI.color = newColor; newColor == "default" ? timerUI.color="rgb(49,136,255)" /* #38F */ : timerUI.color = newColor;


    timerUI.bar.style.backgroundColor=timerUI.color; timerUI.bar.style.backgroundColor=timerUI.color;
    timerUI.buffer_bar.style.backgroundColor=timerUI.color;
    timerUI.bar.style.boxShadow="0 0 30px 0 "+timerUI.color; timerUI.bar.style.boxShadow="0 0 30px 0 "+timerUI.color;
    timerUI.bar_placeholder.style.backgroundColor=timerUI.color; // (deprecated due to new buffer bar) timerUI.bar_placeholder.style.backgroundColor=timerUI.color;
    timerUI.time.style.color=timerUI.color; timerUI.time.style.color=timerUI.color;
    timerUI.status.style.color=timerUI.color; timerUI.status.style.color=timerUI.color;
    }; };
    @@ -250,6 +255,11 @@ function timeUI() {
    timerUI.bar = document.getElementById("progBar"); timerUI.bar = document.getElementById("progBar");
    addStyle("#timerUI #progBar { display:block; position:fixed; background-color:"+timerUI.color+"; box-shadow: 0 0 30px 0px "+timerUI.color+"; height:8pt; width:50%; bottom:0; }", timerUI.div); addStyle("#timerUI #progBar { display:block; position:fixed; background-color:"+timerUI.color+"; box-shadow: 0 0 30px 0px "+timerUI.color+"; height:8pt; width:50%; bottom:0; }", timerUI.div);


    // buffer bar
    appendChildWithID("div","bufferBar",timerUI.div );
    timerUI.buffer_bar = document.getElementById("bufferBar");
    addStyle("#timerUI #bufferBar { display:block; position:fixed; background-color:"+timerUI.color+"; box-shadow: 0 0 30px 0px "+timerUI.color+"; height:8pt; width:75%; bottom:0; opacity:0.4; }", timerUI.div);

    // timer // timer
    appendChildWithID("button","playTimer",timerUI.div ); appendChildWithID("button","playTimer",timerUI.div );
    timerUI.time = document.getElementById("playTimer"); timerUI.time = document.getElementById("playTimer");
  14. HT-7 revised this gist May 20, 2022. 1 changed file with 46 additions and 41 deletions.
    87 changes: 46 additions & 41 deletions timerUI.js
    @@ -5,11 +5,11 @@
    var mediaType; // for compatibility var mediaType; // for compatibility
    var playerExists = false; // is set to true below if player exists var playerExists = false; // is set to true below if player exists
    var checkMediaType_enabled = true; // for debugging purposes var checkMediaType_enabled = true; // for debugging purposes
    var media_element = {}; var media_element = {}; // declaring as object


    function checkMediaType() { function checkMediaType() {
    if ( checkMediaType_enabled == true ) {
    // checks whether it is a video or an audio tag // checks whether it is a video or an audio tag
    if ( checkMediaType_enabled ) {
    var mediaTypeBeforeCheck = mediaType; var mediaTypeBeforeCheck = mediaType;
    if (document.getElementsByTagName("video")[0]) { playerExists = true; mediaType = "video"; } if (document.getElementsByTagName("video")[0]) { playerExists = true; mediaType = "video"; }
    if (document.getElementsByTagName("audio")[0]) { playerExists = true; mediaType = "audio"; } if (document.getElementsByTagName("audio")[0]) { playerExists = true; mediaType = "audio"; }
    @@ -18,7 +18,7 @@ function checkMediaType() {
    // Only show media type in console if it has changed. // Only show media type in console if it has changed.
    console.log("Detected media type: " + mediaType); console.log("Detected media type: " + mediaType);
    media_element = document.getElementsByTagName(mediaType)[0]; media_element = document.getElementsByTagName(mediaType)[0];
    // Set back to 0 if no player is found after using customMediaElement. // Set back to false if no player is found after using customMediaElement.
    media_element ? playerExists=true : playerExists=false; media_element ? playerExists=true : playerExists=false;
    } }
    } }
    @@ -28,14 +28,10 @@ function customMediaElement(custom_media_element) {
    if (custom_media_element) { if (custom_media_element) {
    playerExists = true; playerExists = true;
    media_element = custom_media_element; media_element = custom_media_element;
    console.log("Custom media element set. Reset using checkMediaType_enabled=true."); console.log("timerUI: Custom media element set. Reset using checkMediaType_enabled=true.");
    } else { console.error("No such media element found."); } } else { console.error("timerUI: No such media element found."); }
    } }


    // Additional checks to ensure the player is detected
    window.onclick = function() { checkMediaType();timeUI(); };
    window.addEventListener("keydown", function() { checkMediaType();timeUI(); } );

    // mousedown status // mousedown status
    var mousedown_status; var mousedown_status;
    window.addEventListener("mousedown", function(){mousedown_status=true; } ); window.addEventListener("mousedown", function(){mousedown_status=true; } );
    @@ -137,35 +133,35 @@ function isDomain(domain) {
    if (window.location.hostname.search(domain) >= 0) return true; else return false; if (window.location.hostname.search(domain) >= 0) return true; else return false;
    } }


    if (isDomain("dailymotion.com") && document.location.pathname.toString().search(/^\/embed\//)==(-1) ) if (isDomain("dailymotion.com") && document.location.pathname.toString().search(/^\/embed\//) < 0 ) {
    // Dailymotion watch page, excluding embed page. // Dailymotion watch page, excluding embed page.
    customMediaElement(document.getElementById("player-body").contentWindow.document.getElementsByTagName("video")[0]); customMediaElement(document.getElementById("player-body").contentWindow.document.getElementsByTagName("video")[0]);

    }


    // == Main code == // == Main code ==
    if (! timerUI) var timerUI = new Object({}); // create parent object if none exists if (! timerUI) var timerUI = new Object({}); // create parent object if none exists


    function timerToggle() { timerUI.toggle = function() {
    if (timerUI.div) { if (timerUI.div) {
    timerUpdate(); timerUI.update();
    if (timerUI.on == true) { if (timerUI.on ) {
    timerUI.div.style.display = "none"; timerUI.div.style.display = "none";
    console.log("timerUI off"); console.log("timerUI off");
    } }
    if (timerUI.on == false) { if (! timerUI.on ) {
    timerUI.div.style.display = "block"; timerUI.div.style.display = "block";
    console.log("timerUI on"); console.log("timerUI on");
    } }
    timerUI.on == false ? timerUI.on = true : timerUI.on = false; timerUI.on ? timerUI.on = false : timerUI.on = true;
    } else { } else {
    console.warn("timerUI: No timer found; checking for media element again. Please try again."); console.warn("timerUI: No timer found; checking for media element again. Please try again.");
    timeUI(); timeUI();
    } }
    } };


    var timer_linefeed = "<span class=timer_linefeed><br /></span>"; var timer_linefeed = "<span class=timer_linefeed><br /></span>";
    var timer_slash = " <span class=timer_slash>/</span> "; var timer_slash = " <span class=timer_slash>/</span> ";
    function timerUpdate() { timerUI.update = function() {
    timerUI.bar.style.width=media_element.currentTime / media_element.duration * 100 + "%"; timerUI.bar.style.width=media_element.currentTime / media_element.duration * 100 + "%";


    switch(timerUI.show_remaining) { switch(timerUI.show_remaining) {
    @@ -193,34 +189,38 @@ function timerUpdate() {
    media_element.paused == false ? media_element.paused == false ?
    timerUI.status.innerHTML="▶" timerUI.status.innerHTML="▶"
    : timerUI.status.innerHTML="⏸"; : timerUI.status.innerHTML="⏸";
    } };


    function timerColor(setColor) { timerUI.setColor = function(newColor) {
    setColor == "default" ? timerUI.color="rgb(49,136,255)" : timerUI.color = setColor; newColor == "default" ? timerUI.color="rgb(49,136,255)" /* #38F */ : timerUI.color = newColor;


    timerUI.bar.style.backgroundColor=timerUI.color; timerUI.bar.style.backgroundColor=timerUI.color;
    timerUI.bar.style.boxShadow="0 0 30px 0 "+timerUI.color; timerUI.bar.style.boxShadow="0 0 30px 0 "+timerUI.color;
    timerUI.bar_placeholder.style.backgroundColor=timerUI.color; timerUI.bar_placeholder.style.backgroundColor=timerUI.color;
    timerUI.time.style.color=timerUI.color; timerUI.time.style.color=timerUI.color;
    timerUI.status.style.color=timerUI.color; timerUI.status.style.color=timerUI.color;
    } };


    function timerGradient(setGradient) { timerUI.setGradient = function(newGradient) {
    setGradient == "default" ? timerUI.gradient="rgba(0,0,0,0.7)" : timerUI.gradient = setGradient; newGradient == "default" ? timerUI.gradient="rgba(0,0,0,0.7)" : timerUI.gradient = newGradient;
    timerUI.gradient_placeholder.style.backgroundImage="linear-gradient(to top,"+timerUI.gradient+", rgba(0,0,0,0) )"; timerUI.gradient_placeholder.style.backgroundImage="linear-gradient(to top,"+timerUI.gradient+", rgba(0,0,0,0) )";
    } };


    function timerFont(setFont) { timerUI.setFont = function(newFont) {
    timerUI.time.style.fontFamily=setFont; timerUI.time.style.fontFamily=newFont;
    } };


    timeUI(); // Additional checks to ensure the player is detected
    window.onclick = function() { checkMediaType();timeUI(); };
    window.addEventListener("keydown", function() { checkMediaType();timeUI(); } );

    timeUI(); // slightly different name to prevent naming collision with timerUI object


    function timeUI() { function timeUI() {


    checkMediaType(); checkMediaType();
    // add timerUI if it does not already exist // add timerUI if it does not already exist
    if ( ( ! document.getElementById("timerUI") ) && playerExists == true ) { if ( ( ! document.getElementById("timerUI") ) && playerExists ) {


    // Declaring variables // Declaring variables
    timerUI.on = true; timerUI.on = true;
    @@ -277,12 +277,12 @@ function timeUI() {
    function() { if ( function() { if (
    ( timerUI.update_during_seek && mousedown_status ) ( timerUI.update_during_seek && mousedown_status )
    || ( timerUI.on && ! media_element.paused ) || ( timerUI.on && ! media_element.paused )
    ) timerUpdate(); ) timerUI.update();
    }, 1000/15 }, 1000/15
    ); );


    // play and pause toggle // play and pause toggle
    timerUI.status.onclick = function() { togglePlay(media_element); timerUpdate(); }; timerUI.status.onclick = function() { togglePlay(media_element); timerUI.update(); };
    // former code with object prototype caused compatibility issues on various sites: media_element.togglePlay(); // former code with object prototype caused compatibility issues on various sites: media_element.togglePlay();


    // toggle between elapsed, remaining, and elapsed/total time // toggle between elapsed, remaining, and elapsed/total time
    @@ -292,41 +292,46 @@ function timeUI() {
    case 1: timerUI.show_remaining = 2; break; case 1: timerUI.show_remaining = 2; break;
    case 2: timerUI.show_remaining = 0; break; case 2: timerUI.show_remaining = 0; break;
    } }
    timerUpdate(); timerUI.update();
    }; };


    // clickable progress bar (experimental) // clickable progress bar (experimental) - no "timerUI." notation because inside main function
    function clickSeekBar(m){ function clickSeekBar(m){
    var clickPos = m.clientX / timerUI.bar_placeholder.offsetWidth; var clickPos = m.clientX / timerUI.bar_placeholder.offsetWidth;
    media_element.currentTime = media_element.duration * clickPos; media_element.currentTime = media_element.duration * clickPos;
    } }
    function dragSeekBar(m){ function dragSeekBar(m){ // currently unused
    m = m || window.event; m = m || window.event;
    var clickPos = m.pageX / timerUI.bar_placeholder.offsetWidth; var clickPos = m.pageX / timerUI.bar_placeholder.offsetWidth;
    var dragSeekBarInterval = setInterval( function() { var dragSeekBarInterval = setInterval( function() {
    media_element.currentTime = media_element.duration * clickPos; media_element.currentTime = media_element.duration * clickPos;
    timerUpdate(); timerUI.update();
    if (! mousedown_status) { clearInterval(dragSeekBarInterval); return; } if (! mousedown_status) { clearInterval(dragSeekBarInterval); return; }
    }, 200); }, 200);
    } }
    timerUI.bar_placeholder.addEventListener("mousedown", clickSeekBar ); timerUI.bar_placeholder.addEventListener("mousedown", clickSeekBar );
    // (incomplete) timerUI.bar_placeholder.addEventListener("mousemove", dragSeekBar ); // (incomplete) timerUI.bar_placeholder.addEventListener("mousemove", dragSeekBar );
    // (obsolete) timerUI.bar_placeholder.addEventListener("mouseup", clickSeekBar ); // (obsolete) timerUI.bar_placeholder.addEventListener("mouseup", clickSeekBar );


    timerUpdate(); timerUI.update();


    // == Patches == // == Patches ==


    // prevent missing out on pausing from inside a site's existing player // prevent missing out on pausing from inside a site's existing player
    window.addEventListener("mouseup", function() { setTimeout(timerUpdate, 200); } ); window.addEventListener("mouseup", function() { setTimeout(timerUI.update, 200); } );
    window.addEventListener("keyup", function() { setTimeout(timerUpdate, 200); } ); window.addEventListener("keyup", function() { setTimeout(timerUI.update, 200); } );


    // prevent detaching from player on sites with playlists such as Internet Archive // prevent detaching from player on sites with playlists such as Internet Archive
    timerUI.checkMediaInterval = setInterval( checkMediaType,1000 ); timerUI.checkMediaInterval = setInterval( checkMediaType,1000 );


    // prevent indicating "▶" after playback finished // prevent indicating "▶" after playback finished
    timerUI.checkPaused = setInterval( function() { timerUI.checkPaused = setInterval( function() {
    if (media_element.paused && timerUI.status.innerHTML!="⏸") timerUpdate(); if (media_element.paused && timerUI.status.innerHTML!="⏸") timerUI.update();
    },1000 ); },1000 );
    } } else {
    // warn in console that no player exists; prevent repetition
    if (! playerExists && ! timerUI.noMediaWarned) {
    console.warn("timerUI: no media element found on page."); timerUI.noMediaWarned = true;
    }
    }
    } }
  15. HT-7 revised this gist May 19, 2022. 1 changed file with 2 additions and 2 deletions.
    4 changes: 2 additions & 2 deletions timerUI.js
    @@ -173,8 +173,8 @@ function timerUpdate() {
    case 0: timerUI.time.innerHTML=HMStimer(media_element.currentTime); break; case 0: timerUI.time.innerHTML=HMStimer(media_element.currentTime); break;
    case 1: timerUI.time.innerHTML=HMStimer(media_element.currentTime-media_element.duration); break; case 1: timerUI.time.innerHTML=HMStimer(media_element.currentTime-media_element.duration); break;
    case 2: case 2:
    if (media_element.duration < 6000 || isNaN(media_element.duration) ) { if (media_element.duration < 3600 || isNaN(media_element.duration) ) {
    // show hours if duration exceeds 100 minutes // show hours if duration exceeds one hour
    timerUI.time.innerHTML= timerUI.time.innerHTML=
    MStimer(media_element.currentTime) MStimer(media_element.currentTime)
    + timer_linefeed + timer_linefeed
  16. HT-7 revised this gist May 19, 2022. 1 changed file with 6 additions and 5 deletions.
    11 changes: 6 additions & 5 deletions timerUI.js
    @@ -158,7 +158,7 @@ function timerToggle() {
    } }
    timerUI.on == false ? timerUI.on = true : timerUI.on = false; timerUI.on == false ? timerUI.on = true : timerUI.on = false;
    } else { } else {
    console.log("No timer found; checking for media element again. Please try again."); console.warn("timerUI: No timer found; checking for media element again. Please try again.");
    timeUI(); timeUI();
    } }
    } }
    @@ -173,8 +173,8 @@ function timerUpdate() {
    case 0: timerUI.time.innerHTML=HMStimer(media_element.currentTime); break; case 0: timerUI.time.innerHTML=HMStimer(media_element.currentTime); break;
    case 1: timerUI.time.innerHTML=HMStimer(media_element.currentTime-media_element.duration); break; case 1: timerUI.time.innerHTML=HMStimer(media_element.currentTime-media_element.duration); break;
    case 2: case 2:
    if (media_element.duration < 3600 || isNaN(media_element.duration) ) { if (media_element.duration < 6000 || isNaN(media_element.duration) ) {
    // show hours if duration exceeds one hour // show hours if duration exceeds 100 minutes
    timerUI.time.innerHTML= timerUI.time.innerHTML=
    MStimer(media_element.currentTime) MStimer(media_element.currentTime)
    + timer_linefeed + timer_linefeed
    @@ -199,6 +199,7 @@ function timerColor(setColor) {
    setColor == "default" ? timerUI.color="rgb(49,136,255)" : timerUI.color = setColor; setColor == "default" ? timerUI.color="rgb(49,136,255)" : timerUI.color = setColor;


    timerUI.bar.style.backgroundColor=timerUI.color; timerUI.bar.style.backgroundColor=timerUI.color;
    timerUI.bar.style.boxShadow="0 0 30px 0 "+timerUI.color;
    timerUI.bar_placeholder.style.backgroundColor=timerUI.color; timerUI.bar_placeholder.style.backgroundColor=timerUI.color;
    timerUI.time.style.color=timerUI.color; timerUI.time.style.color=timerUI.color;
    timerUI.status.style.color=timerUI.color; timerUI.status.style.color=timerUI.color;
    @@ -247,7 +248,7 @@ function timeUI() {
    // progress bar // progress bar
    appendChildWithID("div","progBar",timerUI.div ); appendChildWithID("div","progBar",timerUI.div );
    timerUI.bar = document.getElementById("progBar"); timerUI.bar = document.getElementById("progBar");
    addStyle("#timerUI #progBar { display:block; position:fixed; background-color:"+timerUI.color+"; height:8pt; width:50%; bottom:0; }", timerUI.div); addStyle("#timerUI #progBar { display:block; position:fixed; background-color:"+timerUI.color+"; box-shadow: 0 0 30px 0px "+timerUI.color+"; height:8pt; width:50%; bottom:0; }", timerUI.div);


    // timer // timer
    appendChildWithID("button","playTimer",timerUI.div ); appendChildWithID("button","playTimer",timerUI.div );
    @@ -284,7 +285,7 @@ function timeUI() {
    timerUI.status.onclick = function() { togglePlay(media_element); timerUpdate(); }; timerUI.status.onclick = function() { togglePlay(media_element); timerUpdate(); };
    // former code with object prototype caused compatibility issues on various sites: media_element.togglePlay(); // former code with object prototype caused compatibility issues on various sites: media_element.togglePlay();


    // toggle between elapsed and remaining time // toggle between elapsed, remaining, and elapsed/total time
    timerUI.time.onclick = function() { timerUI.time.onclick = function() {
    switch(timerUI.show_remaining) { switch(timerUI.show_remaining) {
    case 0: timerUI.show_remaining = 1; break; case 0: timerUI.show_remaining = 1; break;
  17. HT-7 revised this gist May 19, 2022. 1 changed file with 30 additions and 25 deletions.
    55 changes: 30 additions & 25 deletions timerUI.js
    @@ -3,31 +3,32 @@
    // == Dependencies == // == Dependencies ==


    var mediaType; // for compatibility var mediaType; // for compatibility
    var playerExists = 0; // is set to 1 below if player exists var playerExists = false; // is set to true below if player exists
    var checkMediaType_enabled = 1; // for debugging purposes var checkMediaType_enabled = true; // for debugging purposes
    var media_element = {};


    function checkMediaType() { function checkMediaType() {
    if ( checkMediaType_enabled == 1 ) { if ( checkMediaType_enabled == true ) {
    // checks whether it is a video or an audio tag // checks whether it is a video or an audio tag
    mediaTypeBeforeCheck = mediaType; var mediaTypeBeforeCheck = mediaType;
    if (document.getElementsByTagName("video")[0]) { playerExists = 1; mediaType = "video"; } if (document.getElementsByTagName("video")[0]) { playerExists = true; mediaType = "video"; }
    if (document.getElementsByTagName("audio")[0]) { playerExists = 1; mediaType = "audio"; } if (document.getElementsByTagName("audio")[0]) { playerExists = true; mediaType = "audio"; }
    mediaTypeAfterCheck = mediaType; var mediaTypeAfterCheck = mediaType;
    if (mediaTypeBeforeCheck != mediaTypeAfterCheck) if (mediaTypeBeforeCheck != mediaTypeAfterCheck)
    // Only show media type in console if it has changed. // Only show media type in console if it has changed.
    console.log("Detected media type: " + mediaType); console.log("Detected media type: " + mediaType);
    media_element = document.getElementsByTagName(mediaType)[0]; media_element = document.getElementsByTagName(mediaType)[0];
    // Set back to 0 if no player is found after using customMediaElement. // Set back to 0 if no player is found after using customMediaElement.
    media_element ? playerExists=1 : playerExists=0; media_element ? playerExists=true : playerExists=false;
    } }
    } }


    function customMediaElement(custom_media_element) { function customMediaElement(custom_media_element) {
    checkMediaType_enabled = 0; checkMediaType_enabled = false;
    if (custom_media_element) { if (custom_media_element) {
    playerExists = 1; playerExists = true;
    media_element = custom_media_element; media_element = custom_media_element;
    console.log("Custom media element set. Reset using checkMediaType_enabled=1."); console.log("Custom media element set. Reset using checkMediaType_enabled=true.");
    } else { console.error("No such media element found."); } } else { console.error("No such media element found."); }
    } }


    @@ -37,8 +38,8 @@ window.addEventListener("keydown", function() { checkMediaType();timeUI(); } );


    // mousedown status // mousedown status
    var mousedown_status; var mousedown_status;
    window.addEventListener("mousedown", function(){mousedown_status=true} ); window.addEventListener("mousedown", function(){mousedown_status=true; } );
    window.addEventListener("mouseup", function(){mousedown_status=false} ); window.addEventListener("mouseup", function(){mousedown_status=false; } );


    function appendChildWithID(tagName,tagID,parent_element) { function appendChildWithID(tagName,tagID,parent_element) {
    // default parent element to document.body if unspecified // default parent element to document.body if unspecified
    @@ -53,6 +54,10 @@ function addStyle(new_style,parent_element) {
    parent_element.lastElementChild.innerHTML = new_style; parent_element.lastElementChild.innerHTML = new_style;
    } }


    // time variables
    var time_HH = 0;
    var time_MM = 0;
    var time_SS = 0;


    // HH:MM:SS timer // HH:MM:SS timer
    function HMStimer_core(seconds) { function HMStimer_core(seconds) {
    @@ -143,15 +148,15 @@ if (! timerUI) var timerUI = new Object({}); // create parent object if none exi
    function timerToggle() { function timerToggle() {
    if (timerUI.div) { if (timerUI.div) {
    timerUpdate(); timerUpdate();
    if (timerUI.on == 1) { if (timerUI.on == true) {
    timerUI.div.style.display = "none"; timerUI.div.style.display = "none";
    console.log("timerUI off"); console.log("timerUI off");
    } }
    if (timerUI.on == 0) { if (timerUI.on == false) {
    timerUI.div.style.display = "block"; timerUI.div.style.display = "block";
    console.log("timerUI on"); console.log("timerUI on");
    } }
    timerUI.on == 0 ? timerUI.on = 1 : timerUI.on = 0; timerUI.on == false ? timerUI.on = true : timerUI.on = false;
    } else { } else {
    console.log("No timer found; checking for media element again. Please try again."); console.log("No timer found; checking for media element again. Please try again.");
    timeUI(); timeUI();
    @@ -185,7 +190,7 @@ function timerUpdate() {
    break; break;
    } }


    media_element.paused == 0 ? media_element.paused == false ?
    timerUI.status.innerHTML="▶" timerUI.status.innerHTML="▶"
    : timerUI.status.innerHTML="⏸"; : timerUI.status.innerHTML="⏸";
    } }
    @@ -214,13 +219,13 @@ function timeUI() {


    checkMediaType(); checkMediaType();
    // add timerUI if it does not already exist // add timerUI if it does not already exist
    if ( ( ! document.getElementById("timerUI") ) && playerExists == 1 ) { if ( ( ! document.getElementById("timerUI") ) && playerExists == true ) {


    // Declaring variables // Declaring variables
    timerUI.on = 1; timerUI.on = true;
    timerUI.div; // unset yet, declaring to prevent reference errors timerUI.div = {}; // unset yet, declaring to prevent reference errors
    timerUI.show_remaining = 0; // 0: show elapsed time. 1: show remaining time. 2: show elapsed and total. timerUI.show_remaining = 0; // 0: show elapsed time. 1: show remaining time. 2: show elapsed and total.
    timerUI.update_during_seek = 1; // 1: update timer while dragging seek bar timerUI.update_during_seek = true; // update timer while dragging seek bar
    timerUI.color = "rgb(49,136,255)"; // #38F – using RGB for compatibility. timerUI.color = "rgb(49,136,255)"; // #38F – using RGB for compatibility.
    timerUI.gradient = "rgba(0,0,0,0.7)"; // using RGBA instead of hexadecimal for compatibility. timerUI.gradient = "rgba(0,0,0,0.7)"; // using RGBA instead of hexadecimal for compatibility.


    @@ -231,7 +236,7 @@ function timeUI() {
    timerUI.div = document.getElementById("timerUI"); timerUI.div = document.getElementById("timerUI");


    // button styling // button styling
    addStyle("#timerUI button \{ background:unset; border:unset; outline:unset; line-height:unset; \}", timerUI.div) addStyle("#timerUI button \{ background:unset; border:unset; outline:unset; line-height:unset; \}", timerUI.div);
    timerUI.div.lastElementChild.classList.add("timerUI_buttons"); // label to improve visibility in page inspector timerUI.div.lastElementChild.classList.add("timerUI_buttons"); // label to improve visibility in page inspector


    // background gradient // background gradient
    @@ -259,10 +264,10 @@ function timeUI() {
    // progress bar placeholder – put last to be at the top in stacking context // progress bar placeholder – put last to be at the top in stacking context
    appendChildWithID("div","progBarPlaceholder",timerUI.div ); appendChildWithID("div","progBarPlaceholder",timerUI.div );
    timerUI.bar_placeholder = document.getElementById("progBarPlaceholder"); timerUI.bar_placeholder = document.getElementById("progBarPlaceholder");
    addStyle("#progBarPlaceholder { display:block; position:fixed; cursor:pointer; background-color:grey; height:8pt; width:100%; bottom:0; opacity:0.2; }", timerUI.div) addStyle("#progBarPlaceholder { display:block; position:fixed; cursor:pointer; background-color:grey; height:8pt; width:100%; bottom:0; opacity:0.2; }", timerUI.div);


    // responsive - at bottom to be able to override CSS properties without !important flag. // responsive - at bottom to be able to override CSS properties without !important flag.
    addStyle("@media (max-width:768px) \{ #timerUI #playTimer \{ font-size:30pt; line-height:24pt; bottom:15pt; \} #timerUI #playStatus \{ bottom:10pt; \} \} @media (max-width:500px) \{ #playTimer .timer_linefeed { display:inline; } #timerUI #playTimer .timer_slash \{ display:none; \} \}", timerUI.div) addStyle("@media (max-width:768px) \{ #timerUI #playTimer \{ font-size:30pt; line-height:24pt; bottom:15pt; \} #timerUI #playStatus \{ bottom:10pt; \} \} @media (max-width:500px) \{ #playTimer .timer_linefeed { display:inline; } #timerUI #playTimer .timer_slash \{ display:none; \} \}", timerUI.div);
    timerUI.div.lastElementChild.classList.add("timerUI_responsive"); timerUI.div.lastElementChild.classList.add("timerUI_responsive");




    @@ -285,7 +290,7 @@ function timeUI() {
    case 0: timerUI.show_remaining = 1; break; case 0: timerUI.show_remaining = 1; break;
    case 1: timerUI.show_remaining = 2; break; case 1: timerUI.show_remaining = 2; break;
    case 2: timerUI.show_remaining = 0; break; case 2: timerUI.show_remaining = 0; break;
    }; }
    timerUpdate(); timerUpdate();
    }; };


  18. HT-7 revised this gist May 19, 2022. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion timerUI.js
    @@ -222,7 +222,7 @@ function timeUI() {
    timerUI.show_remaining = 0; // 0: show elapsed time. 1: show remaining time. 2: show elapsed and total. timerUI.show_remaining = 0; // 0: show elapsed time. 1: show remaining time. 2: show elapsed and total.
    timerUI.update_during_seek = 1; // 1: update timer while dragging seek bar timerUI.update_during_seek = 1; // 1: update timer while dragging seek bar
    timerUI.color = "rgb(49,136,255)"; // #38F – using RGB for compatibility. timerUI.color = "rgb(49,136,255)"; // #38F – using RGB for compatibility.
    timerUI.gradient = "rgb(0,0,0,0.7)"; // #38F – using RGB for compatibility. timerUI.gradient = "rgba(0,0,0,0.7)"; // using RGBA instead of hexadecimal for compatibility.


    // Adding elements // Adding elements


  19. HT-7 revised this gist May 18, 2022. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion timerUI.js
    @@ -254,7 +254,7 @@ function timeUI() {
    appendChildWithID("button","playStatus",timerUI.div ); appendChildWithID("button","playStatus",timerUI.div );
    timerUI.status = document.getElementById("playStatus"); timerUI.status = document.getElementById("playStatus");
    timerUI.status.innerHTML="■"; timerUI.status.innerHTML="■";
    addStyle("#timerUI #playStatus { display:block; position:fixed; color:"+timerUI.color+"; font-size:24pt; line-height:40pt; bottom:30pt; right:3pt; font-family:none; }", timerUI.div); addStyle("#timerUI #playStatus { display:block; position:fixed; cursor:pointer; color:"+timerUI.color+"; font-size:24pt; line-height:40pt; bottom:30pt; right:3pt; font-family:none; }", timerUI.div);


    // progress bar placeholder – put last to be at the top in stacking context // progress bar placeholder – put last to be at the top in stacking context
    appendChildWithID("div","progBarPlaceholder",timerUI.div ); appendChildWithID("div","progBarPlaceholder",timerUI.div );
  20. HT-7 revised this gist May 18, 2022. 1 changed file with 233 additions and 192 deletions.
    425 changes: 233 additions & 192 deletions timerUI.js
    @@ -7,28 +7,28 @@ var playerExists = 0; // is set to 1 below if player exists
    var checkMediaType_enabled = 1; // for debugging purposes var checkMediaType_enabled = 1; // for debugging purposes


    function checkMediaType() { function checkMediaType() {
    if ( checkMediaType_enabled == 1 ) { if ( checkMediaType_enabled == 1 ) {
    // checks whether it is a video or an audio tag // checks whether it is a video or an audio tag
    mediaTypeBeforeCheck = mediaType; mediaTypeBeforeCheck = mediaType;
    if (document.getElementsByTagName("video")[0]) { playerExists = 1; mediaType = "video"; } if (document.getElementsByTagName("video")[0]) { playerExists = 1; mediaType = "video"; }
    if (document.getElementsByTagName("audio")[0]) { playerExists = 1; mediaType = "audio"; } if (document.getElementsByTagName("audio")[0]) { playerExists = 1; mediaType = "audio"; }
    mediaTypeAfterCheck = mediaType; mediaTypeAfterCheck = mediaType;
    if (mediaTypeBeforeCheck != mediaTypeAfterCheck) if (mediaTypeBeforeCheck != mediaTypeAfterCheck)
    // Only show media type in console if it has changed. // Only show media type in console if it has changed.
    console.log("Detected media type: " + mediaType); console.log("Detected media type: " + mediaType);
    media_element = document.getElementsByTagName(mediaType)[0]; media_element = document.getElementsByTagName(mediaType)[0];
    // Set back to 0 if no player is found after using customMediaElement. // Set back to 0 if no player is found after using customMediaElement.
    media_element ? playerExists=1 : playerExists=0; media_element ? playerExists=1 : playerExists=0;
    } }
    } }


    function customMediaElement(custom_media_element) { function customMediaElement(custom_media_element) {
    checkMediaType_enabled = 0; checkMediaType_enabled = 0;
    if (custom_media_element) { if (custom_media_element) {
    playerExists = 1; playerExists = 1;
    media_element = custom_media_element; media_element = custom_media_element;
    console.log("Custom media element set. Reset using checkMediaType_enabled=1."); console.log("Custom media element set. Reset using checkMediaType_enabled=1.");
    } else { console.error("No such media element found."); } } else { console.error("No such media element found."); }
    } }


    // Additional checks to ensure the player is detected // Additional checks to ensure the player is detected
    @@ -41,75 +41,75 @@ window.addEventListener("mousedown", function(){mousedown_status=true} );
    window.addEventListener("mouseup", function(){mousedown_status=false} ); window.addEventListener("mouseup", function(){mousedown_status=false} );


    function appendChildWithID(tagName,tagID,parent_element) { function appendChildWithID(tagName,tagID,parent_element) {
    // default parent element to document.body if unspecified // default parent element to document.body if unspecified
    if (parent_element === undefined) var parent_element = document.body; if (parent_element === undefined) parent_element = document.body;
    parent_element.appendChild(document.createElement(tagName)); // add div parent_element.appendChild(document.createElement(tagName)); // add div
    parent_element.lastElementChild.id=tagID; // give it ID parent_element.lastElementChild.id=tagID; // give it ID
    } }


    function addStyle(new_style,parent_element) { function addStyle(new_style,parent_element) {
    if (parent_element === undefined) var parent_element = document.body; if (parent_element === undefined) parent_element = document.body;
    parent_element.appendChild(document.createElement("style")); // add style parent_element.appendChild(document.createElement("style")); // add style
    parent_element.lastElementChild.innerHTML = new_style; parent_element.lastElementChild.innerHTML = new_style;
    } }




    // HH:MM:SS timer // HH:MM:SS timer
    function HMStimer_core(seconds) { function HMStimer_core(seconds) {


    // hours // hours
    time_HH = Math.floor( seconds/3600 ); time_HH = Math.floor( seconds/3600 );
    // leading zero // leading zero
    if ( seconds < 36000 ) time_HH = "0" + time_HH; if ( seconds < 36000 ) time_HH = "0" + time_HH;


    // minutes // minutes
    time_MM = Math.floor( seconds/60%60 ); time_MM = Math.floor( seconds/60%60 );
    // leading zero // leading zero
    if ( seconds%3600 < 600 ) time_MM = "0" + time_MM; if ( seconds%3600 < 600 ) time_MM = "0" + time_MM;


    // seconds // seconds
    time_SS = Math.floor( seconds%60 ); time_SS = Math.floor( seconds%60 );
    // leading zero // leading zero
    if ( seconds%60 < 10 ) time_SS = "0" + time_SS; if ( seconds%60 < 10 ) time_SS = "0" + time_SS;


    return time_HH+":"+time_MM+":"+time_SS; return time_HH+":"+time_MM+":"+time_SS;
    } }


    function HMStimer(seconds) { function HMStimer(seconds) {
    if (seconds >= 0) return HMStimer_core(seconds); // zero or positive if (seconds >= 0) return HMStimer_core(seconds); // zero or positive
    if (seconds < 0) // negative if (seconds < 0) // negative
    { {
    seconds = seconds * (-1); seconds = seconds * (-1);
    return "-"+HMStimer_core(seconds); return "-"+HMStimer_core(seconds);
    } }
    if (seconds == undefined || isNaN(seconds) ) return "–&thinsp;–:–&thinsp;–:–&thinsp;–"; if (seconds == undefined || isNaN(seconds) ) return "–&thinsp;–:–&thinsp;–:–&thinsp;–";


    } }


    // MM:SS timer // MM:SS timer
    function MStimer_core(seconds) { function MStimer_core(seconds) {


    // minutes // minutes
    time_MM = Math.floor( seconds/60 ); time_MM = Math.floor( seconds/60 );
    // leading zero // leading zero
    if ( seconds%3600 < 600 ) time_MM = "0" + time_MM; if ( seconds%3600 < 600 ) time_MM = "0" + time_MM;


    // seconds // seconds
    time_SS = Math.floor( seconds%60 ); time_SS = Math.floor( seconds%60 );
    // leading zero // leading zero
    if ( seconds%60 < 10 ) time_SS = "0" + time_SS; if ( seconds%60 < 10 ) time_SS = "0" + time_SS;


    return time_MM+":"+time_SS; return time_MM+":"+time_SS;
    } }


    function MStimer(seconds) { function MStimer(seconds) {
    if (seconds >= 0) return MStimer_core(seconds); // zero or positive if (seconds >= 0) return MStimer_core(seconds); // zero or positive
    if (seconds < 0) // negative if (seconds < 0) // negative
    { {
    seconds = seconds * (-1); seconds = seconds * (-1);
    return "-"+MStimer_core(seconds); return "-"+MStimer_core(seconds);
    } }
    if (seconds == undefined || isNaN(seconds) ) return "–&thinsp;–:–&thinsp;–"; if (seconds == undefined || isNaN(seconds) ) return "–&thinsp;–:–&thinsp;–";


    } }


    @@ -123,163 +123,204 @@ Object.prototype.togglePlay = function togglePlay() {


    // new function without object prototype for compatibility // new function without object prototype for compatibility
    function togglePlay(media_element) { function togglePlay(media_element) {
    media_element.paused ? media_element.play() : media_element.pause(); media_element.paused ? media_element.play() : media_element.pause();
    } }


    // == Custom domain rules == // == Custom domain rules ==
    function isDomain(domain) { function isDomain(domain) {
    // Using .search() instead of .includes() to improve browser compatibility. // Using .search() instead of .includes() to improve browser compatibility.
    if (window.location.hostname.search(domain) >= 0) return true; else return false; if (window.location.hostname.search(domain) >= 0) return true; else return false;
    } }


    if (isDomain("dailymotion.com") && document.location.pathname.toString().search(/^\/embed\//)==(-1) ) if (isDomain("dailymotion.com") && document.location.pathname.toString().search(/^\/embed\//)==(-1) )
    // Dailymotion watch page, excluding embed page. // Dailymotion watch page, excluding embed page.
    customMediaElement(document.getElementById("player-body").contentWindow.document.getElementsByTagName("video")[0]); customMediaElement(document.getElementById("player-body").contentWindow.document.getElementsByTagName("video")[0]);




    // == Main code == // == Main code ==

    if (! timerUI) var timerUI = new Object({}); // create parent object if none exists
    var timerUI_on = 1;
    var timerUI_id; // unset yet, declaring to prevent reference errors
    var show_remaining_time = 0; // 0: show elapsed time. 1: show remaining time. 2: show elapsed and total.
    var timer_update_during_seek = 1; // 1: update timer while dragging seek bar
    var timeUI_color = "rgb(49,136,255)"; // #38F – using RGB for compatibility.


    function timerToggle() { function timerToggle() {
    if (timerUI_id) { if (timerUI.div) {
    timerUpdate(); timerUpdate();
    if (timerUI_on == 1) { if (timerUI.on == 1) {
    timerUI_id.style.display = "none"; timerUI.div.style.display = "none";
    console.log("timerUI off"); console.log("timerUI off");
    } }
    if (timerUI_on == 0) { if (timerUI.on == 0) {
    timerUI_id.style.display = "block"; timerUI.div.style.display = "block";
    console.log("timerUI on"); console.log("timerUI on");
    } }
    timerUI_on == 0 ? timerUI_on = 1 : timerUI_on = 0; timerUI.on == 0 ? timerUI.on = 1 : timerUI.on = 0;
    } else { } else {
    console.log("No timer found; checking for media element again. Please try again.") console.log("No timer found; checking for media element again. Please try again.");
    timeUI(); timeUI();
    } }
    } }


    var timer_linefeed = "<span class=timer_linefeed><br /></span>";
    var timer_slash = " <span class=timer_slash>/</span> ";
    function timerUpdate() { function timerUpdate() {
    if (timerUI_on == 1) { timerUI.bar.style.width=media_element.currentTime / media_element.duration * 100 + "%";
    document.getElementById("progBar").style.width=media_element.currentTime / media_element.duration * 100 + "%";

    switch(timerUI.show_remaining) {
    switch(show_remaining_time) { // 0: "HH:MM:SS" 1: "-HH:MM:SS" 2: "MM:SS / MM:SS" or "HH:MM:SS / HH:MM:SS"
    // 0: "HH:MM:SS" 1: "-HH:MM:SS" 2: "MM:SS / MM:SS" or "HH:MM:SS / HH:MM:SS" case 0: timerUI.time.innerHTML=HMStimer(media_element.currentTime); break;
    case 0: document.getElementById("playTimer").innerHTML=HMStimer(media_element.currentTime); break; case 1: timerUI.time.innerHTML=HMStimer(media_element.currentTime-media_element.duration); break;
    case 1: document.getElementById("playTimer").innerHTML=HMStimer(media_element.currentTime-media_element.duration); break; case 2:
    case 2: if (media_element.duration < 3600 || isNaN(media_element.duration) ) {
    if (media_element.duration < 3600 || isNaN(media_element) ) { // show hours if duration exceeds one hour
    // show hours if duration exceeds one hour timerUI.time.innerHTML=
    document.getElementById("playTimer").innerHTML=MStimer(media_element.currentTime)+" / "+MStimer(media_element.duration); MStimer(media_element.currentTime)
    } else { + timer_linefeed
    document.getElementById("playTimer").innerHTML=HMStimer(media_element.currentTime)+" / "+HMStimer(media_element.duration); + timer_slash
    } + MStimer(media_element.duration);
    break; } else {
    } timerUI.time.innerHTML=

    HMStimer(media_element.currentTime)
    media_element.paused == 0 ? + timer_linefeed
    document.getElementById("playStatus").innerHTML="▶" + timer_slash
    : document.getElementById("playStatus").innerHTML="⏸"; + HMStimer(media_element.duration);
    } }
    break;
    }

    media_element.paused == 0 ?
    timerUI.status.innerHTML="▶"
    : timerUI.status.innerHTML="⏸";
    } }


    function timerColor(setColor) { function timerColor(setColor) {
    setColor == "default" ? timeUI_color="rgb(49,136,255)" : timeUI_color = setColor; setColor == "default" ? timerUI.color="rgb(49,136,255)" : timerUI.color = setColor;


    document.getElementById("progBar").style.backgroundColor=timeUI_color; timerUI.bar.style.backgroundColor=timerUI.color;
    document.getElementById("progBarPlaceholder").style.backgroundColor=timeUI_color; timerUI.bar_placeholder.style.backgroundColor=timerUI.color;
    document.getElementById("playTimer").style.color=timeUI_color; timerUI.time.style.color=timerUI.color;
    document.getElementById("playStatus").style.color=timeUI_color; timerUI.status.style.color=timerUI.color;
    document.getElementById("bottomGradient").style.backgroundImage="linear-gradient(to top,"+timeUI_color+", rgba(0,0,0,0) )";
    } }


    function timerFont(setFont) { function timerGradient(setGradient) {
    document.getElementById("playTimer").style.fontFamily=setFont; setGradient == "default" ? timerUI.gradient="rgba(0,0,0,0.7)" : timerUI.gradient = setGradient;
    timerUI.gradient_placeholder.style.backgroundImage="linear-gradient(to top,"+timerUI.gradient+", rgba(0,0,0,0) )";
    } }


    // patch: prevent missing out on pausing from inside a site's existing player function timerFont(setFont) {
    window.addEventListener("mouseup", function() { setTimeout(timerUpdate, 200); } ); timerUI.time.style.fontFamily=setFont;
    window.addEventListener("keyup", function() { setTimeout(timerUpdate, 200); } ); }



    timeUI(); timeUI();
    var timerUI_id = document.getElementById("timerUI");


    function timeUI() { function timeUI() {


    checkMediaType(); checkMediaType();
    // add timerUI if it does not already exist // add timerUI if it does not already exist
    if ( ( ! document.getElementById("timerUI") ) && playerExists == 1 ) { if ( ( ! document.getElementById("timerUI") ) && playerExists == 1 ) {


    // Adding elements // Declaring variables

    timerUI.on = 1;
    // parent element timerUI.div; // unset yet, declaring to prevent reference errors
    appendChildWithID("div","timerUI"); timerUI.show_remaining = 0; // 0: show elapsed time. 1: show remaining time. 2: show elapsed and total.
    var timerUI_id = document.getElementById("timerUI"); timerUI.update_during_seek = 1; // 1: update timer while dragging seek bar

    timerUI.color = "rgb(49,136,255)"; // #38F – using RGB for compatibility.
    // background gradient timerUI.gradient = "rgb(0,0,0,0.7)"; // #38F – using RGB for compatibility.
    appendChildWithID("div","bottomGradient",timerUI_id );
    addStyle("#bottomGradient { display:block; position:fixed; background-image:linear-gradient(to top, "+timeUI_color+", rgba(0,0,0,0) ); opacity:0.2; height:80pt; width:100%; bottom:0; pointer-events:none; }"); // Adding elements


    // progress bar // parent element
    appendChildWithID("div","progBar",timerUI_id ); appendChildWithID("div","timerUI");
    addStyle("#progBar { display:block; position:fixed; background-color:"+timeUI_color+"; height:8pt; width:50%; bottom:0; }"); timerUI.div = document.getElementById("timerUI");


    // timer // button styling
    appendChildWithID("div","playTimer",timerUI_id ); addStyle("#timerUI button \{ background:unset; border:unset; outline:unset; line-height:unset; \}", timerUI.div)
    document.getElementById("playTimer").innerHTML="00:00:00"; timerUI.div.lastElementChild.classList.add("timerUI_buttons"); // label to improve visibility in page inspector
    addStyle("#playTimer { display:block; color:"+timeUI_color+"; position:fixed; font-size:50pt; bottom:0; line-height:80pt; right:25pt; font-weight:400; font-family:din,futura,'noto sans',ubuntu,'segoe ui',verdana,tahoma,roboto,'roboto light',arial,helvetica,'trebuchet ms',sans-serif,consolas,monospace; }");

    // background gradient
    // play/pause symbol appendChildWithID("div","bottomGradient",timerUI.div );
    appendChildWithID("div","playStatus",timerUI_id ); timerUI.gradient_placeholder = document.getElementById("bottomGradient");
    document.getElementById("playStatus").innerHTML="■"; addStyle("#timerUI #bottomGradient { display:block; position:fixed; background-image:linear-gradient(to top,"+timerUI.gradient+", rgba(0,0,0,0) ); opacity:1; height:80pt; width:100%; bottom:0; pointer-events:none; }", timerUI.div);
    addStyle("#playStatus { display:block; color:"+timeUI_color+"; position:fixed; font-size:24pt; line-height:40pt; bottom:30pt; right:4pt; font-family:none; }");

    // progress bar
    // progress bar placeholder – put last to be at the top in stacking context appendChildWithID("div","progBar",timerUI.div );
    appendChildWithID("div","progBarPlaceholder",timerUI_id ); timerUI.bar = document.getElementById("progBar");
    addStyle("#progBarPlaceholder { display:block; position:fixed; background-color:grey; height:8pt; width:100%; bottom:0; opacity:0.2; }") addStyle("#timerUI #progBar { display:block; position:fixed; background-color:"+timerUI.color+"; height:8pt; width:50%; bottom:0; }", timerUI.div);



    // timer
    // update timer during playback every fifteenth of a second and while mouse is dragging progress bar appendChildWithID("button","playTimer",timerUI.div );
    var timerUpdateInterval = setInterval( timerUI.time = document.getElementById("playTimer");
    function() { if ( ( timer_update_during_seek && mousedown_status ) || ! media_element.paused) timerUpdate(); } timerUI.time.innerHTML="00:00:00";
    , 1000/15 addStyle("#timerUI #playTimer { display:block; color:"+timerUI.color+"; position:fixed; cursor:pointer; font-size:50pt; line-height:60pt; bottom:10pt; right:25pt; text-align:right; font-weight:400; font-family:din,futura,'noto sans',ubuntu,'segoe ui',verdana,tahoma,roboto,'roboto light',arial,helvetica,'trebuchet ms',sans-serif,consolas,monospace; } #playTimer .timer_linefeed { display:none; }", timerUI.div);
    );

    // play/pause symbol
    // patch: prevent detaching from player on sites with playlists such as Internet Archive appendChildWithID("button","playStatus",timerUI.div );
    var checkMediaInterval = setInterval( checkMediaType,1000 ); timerUI.status = document.getElementById("playStatus");

    timerUI.status.innerHTML="■";
    // patch: prevent showing "▶" after playback finished addStyle("#timerUI #playStatus { display:block; position:fixed; color:"+timerUI.color+"; font-size:24pt; line-height:40pt; bottom:30pt; right:3pt; font-family:none; }", timerUI.div);
    var checkPaused = setInterval( function() {
    if (media_element.paused && document.getElementById("playStatus").innerHTML!="⏸") timerUpdate(); // progress bar placeholder – put last to be at the top in stacking context
    },1000 ); appendChildWithID("div","progBarPlaceholder",timerUI.div );

    timerUI.bar_placeholder = document.getElementById("progBarPlaceholder");
    // play and pause toggle addStyle("#progBarPlaceholder { display:block; position:fixed; cursor:pointer; background-color:grey; height:8pt; width:100%; bottom:0; opacity:0.2; }", timerUI.div)
    document.getElementById("playStatus").onclick = function() { togglePlay(media_element); timerUpdate(); };
    // former code with object prototype caused compatibility issues on various sites: media_element.togglePlay(); // responsive - at bottom to be able to override CSS properties without !important flag.

    addStyle("@media (max-width:768px) \{ #timerUI #playTimer \{ font-size:30pt; line-height:24pt; bottom:15pt; \} #timerUI #playStatus \{ bottom:10pt; \} \} @media (max-width:500px) \{ #playTimer .timer_linefeed { display:inline; } #timerUI #playTimer .timer_slash \{ display:none; \} \}", timerUI.div)
    // toggle between elapsed and remaining time timerUI.div.lastElementChild.classList.add("timerUI_responsive");
    document.getElementById("playTimer").onclick = function() {
    switch(show_remaining_time) {
    case 0: show_remaining_time = 1; break; // update timer during playback every fifteenth of a second and while mouse is dragging progress bar
    case 1: show_remaining_time = 2; break; timerUI.updateInterval = setInterval(
    case 2: show_remaining_time = 0; break; function() { if (
    }; ( timerUI.update_during_seek && mousedown_status )
    timerUpdate(); || ( timerUI.on && ! media_element.paused )
    }; ) timerUpdate();

    }, 1000/15
    // clickable progress bar (experimental) );
    function clickSeekBar(m){
    var clickPos = m.clientX / document.getElementById("progBarPlaceholder").offsetWidth; // play and pause toggle
    media_element.currentTime = media_element.duration * clickPos; timerUI.status.onclick = function() { togglePlay(media_element); timerUpdate(); };
    } // former code with object prototype caused compatibility issues on various sites: media_element.togglePlay();
    document.getElementById("progBarPlaceholder").addEventListener("mousedown", clickSeekBar );
    document.getElementById("progBarPlaceholder").addEventListener("mouseup", clickSeekBar ); // toggle between elapsed and remaining time

    timerUI.time.onclick = function() {
    timerUpdate(); switch(timerUI.show_remaining) {
    case 0: timerUI.show_remaining = 1; break;
    case 1: timerUI.show_remaining = 2; break;
    case 2: timerUI.show_remaining = 0; break;
    };
    timerUpdate();
    };

    // clickable progress bar (experimental)
    function clickSeekBar(m){
    var clickPos = m.clientX / timerUI.bar_placeholder.offsetWidth;
    media_element.currentTime = media_element.duration * clickPos;
    }
    function dragSeekBar(m){
    m = m || window.event;
    var clickPos = m.pageX / timerUI.bar_placeholder.offsetWidth;
    var dragSeekBarInterval = setInterval( function() {
    media_element.currentTime = media_element.duration * clickPos;
    timerUpdate();
    if (! mousedown_status) { clearInterval(dragSeekBarInterval); return; }
    }, 200);
    }
    timerUI.bar_placeholder.addEventListener("mousedown", clickSeekBar );
    // (incomplete) timerUI.bar_placeholder.addEventListener("mousemove", dragSeekBar );
    // (obsolete) timerUI.bar_placeholder.addEventListener("mouseup", clickSeekBar );

    timerUpdate();

    // == Patches ==

    // prevent missing out on pausing from inside a site's existing player
    window.addEventListener("mouseup", function() { setTimeout(timerUpdate, 200); } );
    window.addEventListener("keyup", function() { setTimeout(timerUpdate, 200); } );

    // prevent detaching from player on sites with playlists such as Internet Archive
    timerUI.checkMediaInterval = setInterval( checkMediaType,1000 );

    // prevent indicating "▶" after playback finished
    timerUI.checkPaused = setInterval( function() {
    if (media_element.paused && timerUI.status.innerHTML!="⏸") timerUpdate();
    },1000 );
    } }
    } }
  21. HT-7 revised this gist May 15, 2022. 1 changed file with 48 additions and 29 deletions.
    77 changes: 48 additions & 29 deletions timerUI.js
    @@ -141,14 +141,21 @@ if (isDomain("dailymotion.com") && document.location.pathname.toString().search(


    var timerUI_on = 1; var timerUI_on = 1;
    var timerUI_id; // unset yet, declaring to prevent reference errors var timerUI_id; // unset yet, declaring to prevent reference errors
    var show_remaining_time = 0; // 0: show elapsed time. 1: show remaining time. 2: show both. var show_remaining_time = 0; // 0: show elapsed time. 1: show remaining time. 2: show elapsed and total.
    var timer_update_during_seek = 1; // 1: update timer while dragging seek bar var timer_update_during_seek = 1; // 1: update timer while dragging seek bar
    var timeUI_color = "rgb(49,136,255)"; // #38F – using RGB for compatibility. var timeUI_color = "rgb(49,136,255)"; // #38F – using RGB for compatibility.


    function timerToggle() { function timerToggle() {
    if (timerUI_id) { if (timerUI_id) {
    if (timerUI_on == 1) timerUI_id.style.display = "none"; timerUpdate();
    if (timerUI_on == 0) timerUI_id.style.display = "block"; timerUpdate(); if (timerUI_on == 1) {
    timerUI_id.style.display = "none";
    console.log("timerUI off");
    }
    if (timerUI_on == 0) {
    timerUI_id.style.display = "block";
    console.log("timerUI on");
    }
    timerUI_on == 0 ? timerUI_on = 1 : timerUI_on = 0; timerUI_on == 0 ? timerUI_on = 1 : timerUI_on = 0;
    } else { } else {
    console.log("No timer found; checking for media element again. Please try again.") console.log("No timer found; checking for media element again. Please try again.")
    @@ -181,12 +188,13 @@ function timerUpdate() {
    } }


    function timerColor(setColor) { function timerColor(setColor) {
    timeUI_color = setColor; // for debugging setColor == "default" ? timeUI_color="rgb(49,136,255)" : timeUI_color = setColor;
    document.getElementById("progBar").style.backgroundColor=setColor;
    document.getElementById("progBarPlaceholder").style.backgroundColor=setColor; document.getElementById("progBar").style.backgroundColor=timeUI_color;
    document.getElementById("playTimer").style.color=setColor; document.getElementById("progBarPlaceholder").style.backgroundColor=timeUI_color;
    document.getElementById("playStatus").style.color=setColor; document.getElementById("playTimer").style.color=timeUI_color;
    document.getElementById("bottomGradient").style.backgroundImage="linear-gradient(to top,"+setColor+", rgba(0,0,0,0) )"; document.getElementById("playStatus").style.color=timeUI_color;
    document.getElementById("bottomGradient").style.backgroundImage="linear-gradient(to top,"+timeUI_color+", rgba(0,0,0,0) )";
    } }


    function timerFont(setFont) { function timerFont(setFont) {
    @@ -207,31 +215,34 @@ function timeUI() {
    // add timerUI if it does not already exist // add timerUI if it does not already exist
    if ( ( ! document.getElementById("timerUI") ) && playerExists == 1 ) { if ( ( ! document.getElementById("timerUI") ) && playerExists == 1 ) {


    // parent element // Adding elements
    appendChildWithID("div","timerUI");
    var timerUI_id = document.getElementById("timerUI");


    // background gradient // parent element
    appendChildWithID("div","bottomGradient",timerUI_id ); appendChildWithID("div","timerUI");
    addStyle("#bottomGradient { display:block; position:fixed; background-image:linear-gradient(to top, "+timeUI_color+", rgba(0,0,0,0) ); opacity:0.2; height:80pt; width:100%; bottom:0; pointer-events:none; }"); var timerUI_id = document.getElementById("timerUI");


    // progress bar // background gradient
    appendChildWithID("div","progBar",timerUI_id ); appendChildWithID("div","bottomGradient",timerUI_id );
    addStyle("#progBar { display:block; position:fixed; background-color:"+timeUI_color+"; height:8pt; width:50%; bottom:0; }"); addStyle("#bottomGradient { display:block; position:fixed; background-image:linear-gradient(to top, "+timeUI_color+", rgba(0,0,0,0) ); opacity:0.2; height:80pt; width:100%; bottom:0; pointer-events:none; }");


    // progress bar placeholder // progress bar
    appendChildWithID("div","progBarPlaceholder",timerUI_id ); appendChildWithID("div","progBar",timerUI_id );
    addStyle("#progBarPlaceholder { display:block; position:fixed; background-color:grey; height:8pt; width:100%; bottom:0; opacity:0.2; }") addStyle("#progBar { display:block; position:fixed; background-color:"+timeUI_color+"; height:8pt; width:50%; bottom:0; }");


    // timer // timer
    appendChildWithID("div","playTimer",timerUI_id ); appendChildWithID("div","playTimer",timerUI_id );
    document.getElementById("playTimer").innerHTML="00:00:00"; document.getElementById("playTimer").innerHTML="00:00:00";
    addStyle("#playTimer { display:block; color:"+timeUI_color+"; position:fixed; font-size:50pt; bottom:0; line-height:80pt; right:25pt; font-weight:400; font-family:din,futura,'noto sans',ubuntu,'segoe ui',verdana,tahoma,roboto,'roboto light',arial,helvetica,'trebuchet ms',sans-serif,consolas,monospace; }"); addStyle("#playTimer { display:block; color:"+timeUI_color+"; position:fixed; font-size:50pt; bottom:0; line-height:80pt; right:25pt; font-weight:400; font-family:din,futura,'noto sans',ubuntu,'segoe ui',verdana,tahoma,roboto,'roboto light',arial,helvetica,'trebuchet ms',sans-serif,consolas,monospace; }");

    // play/pause symbol
    appendChildWithID("div","playStatus",timerUI_id );
    document.getElementById("playStatus").innerHTML="■";
    addStyle("#playStatus { display:block; color:"+timeUI_color+"; position:fixed; font-size:24pt; line-height:40pt; bottom:30pt; right:4pt; font-family:none; }");

    // progress bar placeholder – put last to be at the top in stacking context
    appendChildWithID("div","progBarPlaceholder",timerUI_id );
    addStyle("#progBarPlaceholder { display:block; position:fixed; background-color:grey; height:8pt; width:100%; bottom:0; opacity:0.2; }")


    // play/pause symbol
    appendChildWithID("div","playStatus",timerUI_id );
    document.getElementById("playStatus").innerHTML="■";
    addStyle("#playStatus { display:block; color:"+timeUI_color+"; position:fixed; font-size:24pt; line-height:40pt; bottom:30pt; right:4pt; font-family:none; }");


    // update timer during playback every fifteenth of a second and while mouse is dragging progress bar // update timer during playback every fifteenth of a second and while mouse is dragging progress bar
    var timerUpdateInterval = setInterval( var timerUpdateInterval = setInterval(
    @@ -261,6 +272,14 @@ function timeUI() {
    timerUpdate(); timerUpdate();
    }; };


    // clickable progress bar (experimental)
    function clickSeekBar(m){
    var clickPos = m.clientX / document.getElementById("progBarPlaceholder").offsetWidth;
    media_element.currentTime = media_element.duration * clickPos;
    }
    document.getElementById("progBarPlaceholder").addEventListener("mousedown", clickSeekBar );
    document.getElementById("progBarPlaceholder").addEventListener("mouseup", clickSeekBar );

    timerUpdate(); timerUpdate();
    } }
    } }
  22. HT-7 revised this gist May 15, 2022. 1 changed file with 53 additions and 30 deletions.
    83 changes: 53 additions & 30 deletions timerUI.js
    @@ -40,14 +40,17 @@ var mousedown_status;
    window.addEventListener("mousedown", function(){mousedown_status=true} ); window.addEventListener("mousedown", function(){mousedown_status=true} );
    window.addEventListener("mouseup", function(){mousedown_status=false} ); window.addEventListener("mouseup", function(){mousedown_status=false} );


    function appendChildWithID(tagName,tagID) { function appendChildWithID(tagName,tagID,parent_element) {
    document.body.appendChild(document.createElement(tagName)); // add div // default parent element to document.body if unspecified
    document.body.lastElementChild.id=tagID; // give it ID if (parent_element === undefined) var parent_element = document.body;
    parent_element.appendChild(document.createElement(tagName)); // add div
    parent_element.lastElementChild.id=tagID; // give it ID
    } }


    function addStyle(new_style) { function addStyle(new_style,parent_element) {
    document.body.appendChild(document.createElement("style")); // add style if (parent_element === undefined) var parent_element = document.body;
    document.body.lastElementChild.innerHTML = new_style; parent_element.appendChild(document.createElement("style")); // add style
    parent_element.lastElementChild.innerHTML = new_style;
    } }




    @@ -136,30 +139,45 @@ if (isDomain("dailymotion.com") && document.location.pathname.toString().search(


    // == Main code == // == Main code ==


    var timerUI_on = 1;
    var timerUI_id; // unset yet, declaring to prevent reference errors
    var show_remaining_time = 0; // 0: show elapsed time. 1: show remaining time. 2: show both. var show_remaining_time = 0; // 0: show elapsed time. 1: show remaining time. 2: show both.
    var timer_update_during_seek = 1; // 1: update timer while dragging seek bar var timer_update_during_seek = 1; // 1: update timer while dragging seek bar
    var timeUI_color = "rgb(49,136,255)"; // #38F – using RGB for compatibility. var timeUI_color = "rgb(49,136,255)"; // #38F – using RGB for compatibility.


    function timerUpdate() { function timerToggle() {
    document.getElementById("progBar").style.width=media_element.currentTime / media_element.duration * 100 + "%"; if (timerUI_id) {

    if (timerUI_on == 1) timerUI_id.style.display = "none";
    switch(show_remaining_time) { if (timerUI_on == 0) timerUI_id.style.display = "block"; timerUpdate();
    // 0: "HH:MM:SS" 1: "-HH:MM:SS" 2: "MM:SS / MM:SS" or "HH:MM:SS / HH:MM:SS" timerUI_on == 0 ? timerUI_on = 1 : timerUI_on = 0;
    case 0: document.getElementById("playTimer").innerHTML=HMStimer(media_element.currentTime); break; } else {
    case 1: document.getElementById("playTimer").innerHTML=HMStimer(media_element.currentTime-media_element.duration); break; console.log("No timer found; checking for media element again. Please try again.")
    case 2: timeUI();
    if (media_element.duration < 3600 || isNaN(media_element) ) {
    // show hours if duration exceeds one hour
    document.getElementById("playTimer").innerHTML=MStimer(media_element.currentTime)+" / "+MStimer(media_element.duration);
    } else {
    document.getElementById("playTimer").innerHTML=HMStimer(media_element.currentTime)+" / "+HMStimer(media_element.duration);
    }
    break;
    } }
    }


    media_element.paused == 0 ? function timerUpdate() {
    document.getElementById("playStatus").innerHTML="▶" if (timerUI_on == 1) {
    : document.getElementById("playStatus").innerHTML="⏸"; document.getElementById("progBar").style.width=media_element.currentTime / media_element.duration * 100 + "%";

    switch(show_remaining_time) {
    // 0: "HH:MM:SS" 1: "-HH:MM:SS" 2: "MM:SS / MM:SS" or "HH:MM:SS / HH:MM:SS"
    case 0: document.getElementById("playTimer").innerHTML=HMStimer(media_element.currentTime); break;
    case 1: document.getElementById("playTimer").innerHTML=HMStimer(media_element.currentTime-media_element.duration); break;
    case 2:
    if (media_element.duration < 3600 || isNaN(media_element) ) {
    // show hours if duration exceeds one hour
    document.getElementById("playTimer").innerHTML=MStimer(media_element.currentTime)+" / "+MStimer(media_element.duration);
    } else {
    document.getElementById("playTimer").innerHTML=HMStimer(media_element.currentTime)+" / "+HMStimer(media_element.duration);
    }
    break;
    }

    media_element.paused == 0 ?
    document.getElementById("playStatus").innerHTML="▶"
    : document.getElementById("playStatus").innerHTML="⏸";
    }
    } }


    function timerColor(setColor) { function timerColor(setColor) {
    @@ -181,32 +199,37 @@ window.addEventListener("keyup", function() { setTimeout(timerUpdate, 200); } );




    timeUI(); timeUI();
    var timerUI_id = document.getElementById("timerUI");


    function timeUI() { function timeUI() {


    checkMediaType(); checkMediaType();
    // add timerUI if it does not already exist // add timerUI if it does not already exist
    if ( ( ! document.getElementById("progBar") ) && playerExists == 1 ) { if ( ( ! document.getElementById("timerUI") ) && playerExists == 1 ) {

    // parent element
    appendChildWithID("div","timerUI");
    var timerUI_id = document.getElementById("timerUI");


    // background gradient // background gradient
    appendChildWithID("div","bottomGradient"); appendChildWithID("div","bottomGradient",timerUI_id );
    addStyle("#bottomGradient { display:block; position:fixed; background-image:linear-gradient(to top, "+timeUI_color+", rgba(0,0,0,0) ); opacity:0.2; height:80pt; width:100%; bottom:0; pointer-events:none; }"); addStyle("#bottomGradient { display:block; position:fixed; background-image:linear-gradient(to top, "+timeUI_color+", rgba(0,0,0,0) ); opacity:0.2; height:80pt; width:100%; bottom:0; pointer-events:none; }");


    // progress bar // progress bar
    appendChildWithID("div","progBar"); appendChildWithID("div","progBar",timerUI_id );
    addStyle("#progBar { display:block; position:fixed; background-color:"+timeUI_color+"; height:8pt; width:50%; bottom:0; }"); addStyle("#progBar { display:block; position:fixed; background-color:"+timeUI_color+"; height:8pt; width:50%; bottom:0; }");


    // progress bar placeholder // progress bar placeholder
    appendChildWithID("div","progBarPlaceholder"); appendChildWithID("div","progBarPlaceholder",timerUI_id );
    addStyle("#progBarPlaceholder { display:block; position:fixed; background-color:grey; height:8pt; width:100%; bottom:0; opacity:0.2; }") addStyle("#progBarPlaceholder { display:block; position:fixed; background-color:grey; height:8pt; width:100%; bottom:0; opacity:0.2; }")


    // timer // timer
    appendChildWithID("div","playTimer"); appendChildWithID("div","playTimer",timerUI_id );
    document.getElementById("playTimer").innerHTML="00:00:00"; document.getElementById("playTimer").innerHTML="00:00:00";
    addStyle("#playTimer { display:block; color:"+timeUI_color+"; position:fixed; font-size:50pt; bottom:0; line-height:80pt; right:25pt; font-weight:400; font-family:din,futura,'noto sans',ubuntu,'segoe ui',verdana,tahoma,roboto,'roboto light',arial,helvetica,'trebuchet ms',sans-serif,consolas,monospace; }"); addStyle("#playTimer { display:block; color:"+timeUI_color+"; position:fixed; font-size:50pt; bottom:0; line-height:80pt; right:25pt; font-weight:400; font-family:din,futura,'noto sans',ubuntu,'segoe ui',verdana,tahoma,roboto,'roboto light',arial,helvetica,'trebuchet ms',sans-serif,consolas,monospace; }");


    // play/pause symbol // play/pause symbol
    appendChildWithID("div","playStatus"); appendChildWithID("div","playStatus",timerUI_id );
    document.getElementById("playStatus").innerHTML="■"; document.getElementById("playStatus").innerHTML="■";
    addStyle("#playStatus { display:block; color:"+timeUI_color+"; position:fixed; font-size:24pt; line-height:40pt; bottom:30pt; right:4pt; font-family:none; }"); addStyle("#playStatus { display:block; color:"+timeUI_color+"; position:fixed; font-size:24pt; line-height:40pt; bottom:30pt; right:4pt; font-family:none; }");


  23. HT-7 created this gist May 14, 2022.
    243 changes: 243 additions & 0 deletions timerUI.js
    @@ -0,0 +1,243 @@
    // TimerUI

    // == Dependencies ==

    var mediaType; // for compatibility
    var playerExists = 0; // is set to 1 below if player exists
    var checkMediaType_enabled = 1; // for debugging purposes

    function checkMediaType() {
    if ( checkMediaType_enabled == 1 ) {
    // checks whether it is a video or an audio tag
    mediaTypeBeforeCheck = mediaType;
    if (document.getElementsByTagName("video")[0]) { playerExists = 1; mediaType = "video"; }
    if (document.getElementsByTagName("audio")[0]) { playerExists = 1; mediaType = "audio"; }
    mediaTypeAfterCheck = mediaType;
    if (mediaTypeBeforeCheck != mediaTypeAfterCheck)
    // Only show media type in console if it has changed.
    console.log("Detected media type: " + mediaType);
    media_element = document.getElementsByTagName(mediaType)[0];
    // Set back to 0 if no player is found after using customMediaElement.
    media_element ? playerExists=1 : playerExists=0;
    }
    }

    function customMediaElement(custom_media_element) {
    checkMediaType_enabled = 0;
    if (custom_media_element) {
    playerExists = 1;
    media_element = custom_media_element;
    console.log("Custom media element set. Reset using checkMediaType_enabled=1.");
    } else { console.error("No such media element found."); }
    }

    // Additional checks to ensure the player is detected
    window.onclick = function() { checkMediaType();timeUI(); };
    window.addEventListener("keydown", function() { checkMediaType();timeUI(); } );

    // mousedown status
    var mousedown_status;
    window.addEventListener("mousedown", function(){mousedown_status=true} );
    window.addEventListener("mouseup", function(){mousedown_status=false} );

    function appendChildWithID(tagName,tagID) {
    document.body.appendChild(document.createElement(tagName)); // add div
    document.body.lastElementChild.id=tagID; // give it ID
    }

    function addStyle(new_style) {
    document.body.appendChild(document.createElement("style")); // add style
    document.body.lastElementChild.innerHTML = new_style;
    }


    // HH:MM:SS timer
    function HMStimer_core(seconds) {

    // hours
    time_HH = Math.floor( seconds/3600 );
    // leading zero
    if ( seconds < 36000 ) time_HH = "0" + time_HH;

    // minutes
    time_MM = Math.floor( seconds/60%60 );
    // leading zero
    if ( seconds%3600 < 600 ) time_MM = "0" + time_MM;

    // seconds
    time_SS = Math.floor( seconds%60 );
    // leading zero
    if ( seconds%60 < 10 ) time_SS = "0" + time_SS;

    return time_HH+":"+time_MM+":"+time_SS;
    }

    function HMStimer(seconds) {
    if (seconds >= 0) return HMStimer_core(seconds); // zero or positive
    if (seconds < 0) // negative
    {
    seconds = seconds * (-1);
    return "-"+HMStimer_core(seconds);
    }
    if (seconds == undefined || isNaN(seconds) ) return "–&thinsp;–:–&thinsp;–:–&thinsp;–";

    }

    // MM:SS timer
    function MStimer_core(seconds) {

    // minutes
    time_MM = Math.floor( seconds/60 );
    // leading zero
    if ( seconds%3600 < 600 ) time_MM = "0" + time_MM;

    // seconds
    time_SS = Math.floor( seconds%60 );
    // leading zero
    if ( seconds%60 < 10 ) time_SS = "0" + time_SS;

    return time_MM+":"+time_SS;
    }

    function MStimer(seconds) {
    if (seconds >= 0) return MStimer_core(seconds); // zero or positive
    if (seconds < 0) // negative
    {
    seconds = seconds * (-1);
    return "-"+MStimer_core(seconds);
    }
    if (seconds == undefined || isNaN(seconds) ) return "–&thinsp;–:–&thinsp;–";

    }


    // implements togglePlay(); – deprecated due to compatibility issues on YouTube (broken site) and Dailymotion ("not a function" error through iframe'd player).
    /*
    Object.prototype.togglePlay = function togglePlay() {
    return this.paused ? this.play() : this.pause();
    };
    */

    // new function without object prototype for compatibility
    function togglePlay(media_element) {
    media_element.paused ? media_element.play() : media_element.pause();
    }

    // == Custom domain rules ==
    function isDomain(domain) {
    // Using .search() instead of .includes() to improve browser compatibility.
    if (window.location.hostname.search(domain) >= 0) return true; else return false;
    }

    if (isDomain("dailymotion.com") && document.location.pathname.toString().search(/^\/embed\//)==(-1) )
    // Dailymotion watch page, excluding embed page.
    customMediaElement(document.getElementById("player-body").contentWindow.document.getElementsByTagName("video")[0]);


    // == Main code ==

    var show_remaining_time = 0; // 0: show elapsed time. 1: show remaining time. 2: show both.
    var timer_update_during_seek = 1; // 1: update timer while dragging seek bar
    var timeUI_color = "rgb(49,136,255)"; // #38F – using RGB for compatibility.

    function timerUpdate() {
    document.getElementById("progBar").style.width=media_element.currentTime / media_element.duration * 100 + "%";

    switch(show_remaining_time) {
    // 0: "HH:MM:SS" 1: "-HH:MM:SS" 2: "MM:SS / MM:SS" or "HH:MM:SS / HH:MM:SS"
    case 0: document.getElementById("playTimer").innerHTML=HMStimer(media_element.currentTime); break;
    case 1: document.getElementById("playTimer").innerHTML=HMStimer(media_element.currentTime-media_element.duration); break;
    case 2:
    if (media_element.duration < 3600 || isNaN(media_element) ) {
    // show hours if duration exceeds one hour
    document.getElementById("playTimer").innerHTML=MStimer(media_element.currentTime)+" / "+MStimer(media_element.duration);
    } else {
    document.getElementById("playTimer").innerHTML=HMStimer(media_element.currentTime)+" / "+HMStimer(media_element.duration);
    }
    break;
    }

    media_element.paused == 0 ?
    document.getElementById("playStatus").innerHTML="▶"
    : document.getElementById("playStatus").innerHTML="⏸";
    }

    function timerColor(setColor) {
    timeUI_color = setColor; // for debugging
    document.getElementById("progBar").style.backgroundColor=setColor;
    document.getElementById("progBarPlaceholder").style.backgroundColor=setColor;
    document.getElementById("playTimer").style.color=setColor;
    document.getElementById("playStatus").style.color=setColor;
    document.getElementById("bottomGradient").style.backgroundImage="linear-gradient(to top,"+setColor+", rgba(0,0,0,0) )";
    }

    function timerFont(setFont) {
    document.getElementById("playTimer").style.fontFamily=setFont;
    }

    // patch: prevent missing out on pausing from inside a site's existing player
    window.addEventListener("mouseup", function() { setTimeout(timerUpdate, 200); } );
    window.addEventListener("keyup", function() { setTimeout(timerUpdate, 200); } );


    timeUI();

    function timeUI() {

    checkMediaType();
    // add timerUI if it does not already exist
    if ( ( ! document.getElementById("progBar") ) && playerExists == 1 ) {

    // background gradient
    appendChildWithID("div","bottomGradient");
    addStyle("#bottomGradient { display:block; position:fixed; background-image:linear-gradient(to top, "+timeUI_color+", rgba(0,0,0,0) ); opacity:0.2; height:80pt; width:100%; bottom:0; pointer-events:none; }");

    // progress bar
    appendChildWithID("div","progBar");
    addStyle("#progBar { display:block; position:fixed; background-color:"+timeUI_color+"; height:8pt; width:50%; bottom:0; }");

    // progress bar placeholder
    appendChildWithID("div","progBarPlaceholder");
    addStyle("#progBarPlaceholder { display:block; position:fixed; background-color:grey; height:8pt; width:100%; bottom:0; opacity:0.2; }")

    // timer
    appendChildWithID("div","playTimer");
    document.getElementById("playTimer").innerHTML="00:00:00";
    addStyle("#playTimer { display:block; color:"+timeUI_color+"; position:fixed; font-size:50pt; bottom:0; line-height:80pt; right:25pt; font-weight:400; font-family:din,futura,'noto sans',ubuntu,'segoe ui',verdana,tahoma,roboto,'roboto light',arial,helvetica,'trebuchet ms',sans-serif,consolas,monospace; }");

    // play/pause symbol
    appendChildWithID("div","playStatus");
    document.getElementById("playStatus").innerHTML="■";
    addStyle("#playStatus { display:block; color:"+timeUI_color+"; position:fixed; font-size:24pt; line-height:40pt; bottom:30pt; right:4pt; font-family:none; }");

    // update timer during playback every fifteenth of a second and while mouse is dragging progress bar
    var timerUpdateInterval = setInterval(
    function() { if ( ( timer_update_during_seek && mousedown_status ) || ! media_element.paused) timerUpdate(); }
    , 1000/15
    );

    // patch: prevent detaching from player on sites with playlists such as Internet Archive
    var checkMediaInterval = setInterval( checkMediaType,1000 );

    // patch: prevent showing "▶" after playback finished
    var checkPaused = setInterval( function() {
    if (media_element.paused && document.getElementById("playStatus").innerHTML!="⏸") timerUpdate();
    },1000 );

    // play and pause toggle
    document.getElementById("playStatus").onclick = function() { togglePlay(media_element); timerUpdate(); };
    // former code with object prototype caused compatibility issues on various sites: media_element.togglePlay();

    // toggle between elapsed and remaining time
    document.getElementById("playTimer").onclick = function() {
    switch(show_remaining_time) {
    case 0: show_remaining_time = 1; break;
    case 1: show_remaining_time = 2; break;
    case 2: show_remaining_time = 0; break;
    };
    timerUpdate();
    };

    timerUpdate();
    }
    }