User:Elominius/media timer.js

// == Dependencies ==

var mediaType; // for compatibility var playerExists = false; // is set to true below if player exists var checkMediaType_enabled = true; // for debugging purposes var media_element = {}; // declaring as object var tmp="",count=0; // initiating temporary variables used inside functions and loops

function checkMediaType { // checks whether it is a video or an audio tag if ( checkMediaType_enabled ) { var mediaTypeBeforeCheck = mediaType; if (document.getElementsByTagName("video")[0]) { playerExists = true; mediaType = "video"; }			if (document.getElementsByTagName("audio")[0]) { playerExists = true; mediaType = "audio"; }			var 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 false if no player is found after using customMediaElement. media_element ? playerExists=true : playerExists=false; } }

function customMediaElement(custom_media_element) { checkMediaType_enabled = false; if (custom_media_element) { playerExists = true; media_element = custom_media_element; console.log("customMediaElement: Custom media element set. Reset using checkMediaType_enabled=true."); } 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.href.search(new RegExp(ext+"$", "i")) > -1) return true; else return false; } else if (typeof(ext) == "object") { // array – check against multiple strings for (var count=0; count < ext.length; count++) { 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 }	} }

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 var media_symbol = {}; media_symbol.play = "▶&#xFE0E;&thinsp;"; // thin space for alignment media_symbol.pause="&#10074;&thinsp;&#10074;"; // instead of "⏸" due to Edge browser putting an immutable blue box around it. media_symbol.stop="■";

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

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

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

// time variables var media_time = {};

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

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

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

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

return media_time.HH+":"+media_time.MM+":"+media_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 media_time.MM = Math.floor( seconds/60 ); // leading zero if ( seconds%3600 < 600 ) media_time.MM = "0" + media_time.MM;

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

return media_time.MM+":"+media_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) { if (media_element) { // validate media element first to avoid errors media_element.paused ? media_element.play : media_element.pause; } }

// media file extension list var mediafileext = { "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"] };

// "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 == if (! timerUI) var timerUI = new Object({}); // create parent object if none exists

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

timerUI.on = true; timerUI.buffer_on = true; timerUI.multiBuffer = true; // multiple buffer segments 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.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.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

// 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 = " "; var timer_slash = " / ";

// functions timerUI.toggle = {}; timerUI.toggle.main = function { // show and hide if (timerUI.div) { timerUI.update; if (timerUI.on) { timerUI.div.style.display = "none"; console.log("timerUI off"); }		if (! timerUI.on ) { timerUI.div.style.display = "block"; console.log("timerUI on"); }		if (timerUI.on) { timerUI.on = false; return false; } else { timerUI.on = true; return false; } } else { console.warn(timerUI.msg.notimer); timeUI; } };

timerUI.toggle.buffer = function { if (timerUI.div) { timerUI.update; timerUI.updateBufferBar(true); 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"); }		if (timerUI.buffer_on) { timerUI.buffer_on = false; return false; } else { timerUI.buffer_on = true; return true; }	} else { console.warn(timerUI.msg.notimer); timeUI; } };

timerUI.toggle.title = function { if (timerUI.div) { timerUI.update; timerUI.updateBufferBar(true); 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"); }		if (timerUI.title_on) { timerUI.title_on = false; return false; } else { timerUI.title_on = true; return true; }	} else { console.warn(timerUI.msg.notimer); timeUI; } };

timerUI.getTitle = function { if (! timerUI.domainRules_checked) /* only check domain rules once */ { timerUI.domainRules; timerUI.domainRules_checked = true; }	if (customTitleElement) timerUI.newTitle = customTitleElement.innerHTML; else { // skipping this whole part if no custom title is specified timerUI.newTitle = document.title; // replace underscores with spaces timerUI.newTitle = replaceAll_polyfill(timerUI.newTitle, "_"," "); timerUI.titleDomainRules; }	if (media_element) { timerUI.updateFileIcon; return timerUI.file_icon+" "+timerUI.newTitle; } else { return "TimerUI – designed for home cinemas"; } };

timerUI.guessMediaType = function { if (isDomain("youtube.com") || isDomain("dailymotion.com") ) 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 > 0) return "video"; if (checkFileExtension(mediafileext.video) ) return "video"; if (checkFileExtension(mediafileext.audio) ) return "audio"; return "unknown"; // if nothing detected };

timerUI.updateFileIcon = function { timerUI.file_icon = timerUI.guessMediaType; switch(timerUI.file_icon) { case "video": timerUI.file_icon = "🎞️"; break; case "audio": timerUI.file_icon = "♫"; break; case "unknown": timerUI.file_icon = "📄"; 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 { if (media_element) { timerUI.bar.style.width=media_element.currentTime / media_element.duration * 100 + "%";

// buffer bar update formerly located here; removed from the scope of this function

switch(timerUI.show_remaining) { // 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 1: timerUI.time.innerHTML=HMStimer(media_element.currentTime-media_element.duration); break; case 2: if (media_element.duration < 3600 || isNaN(media_element.duration) ) { // show hours if duration exceeds one hour timerUI.time.innerHTML= MStimer(media_element.currentTime) + timer_linefeed + timer_slash + MStimer(media_element.duration); } else { timerUI.time.innerHTML= HMStimer(media_element.currentTime) + timer_linefeed + timer_slash + HMStimer(media_element.duration); }				break; }

if (media_element.paused) { timerUI.status.innerHTML=media_symbol.pause; } else { timerUI.status.innerHTML=media_symbol.play; }	} 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);

// 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 is zero until player is initialized // prevent timerUI.buffer_segment from going negative, as it would cause a DOMException error if ( timerUI.buffer_segment > 0) { while (media_element.buffered.end(timerUI.buffer_segment-1) > media_element.currentTime && timerUI.buffer_segment > 1 ) { timerUI.update_single_buffer; 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).start_pos,			timerUI.get_buffer_range(count).end_pos		); }	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) { 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)+"%;background-color:"+timerUI.color+";"; };

timerUI.get_buffer_range = function(segment_number) { return { start_pos: media_element.buffered.start(segment_number) / media_element.duration * 100, end_pos: media_element.buffered.end(segment_number) / media_element.duration * 100 }; // object 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) { 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;

timerUI.bar.style.backgroundColor=timerUI.color; timerUI.buffer_bar.style.backgroundColor=timerUI.color; timerUI.bar.style.boxShadow="0 0 30px 0 "+timerUI.color; // (deprecated due to new buffer bar) timerUI.bar_placeholder.style.backgroundColor=timerUI.color; timerUI.time.style.color=timerUI.color; timerUI.status.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) { 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.setFont = function(newFont) { timerUI.time.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.status.innerHTML=media_symbol.stop; timerUI.bar.style.width=0; timerUI.buffer_bar.style.width=0; // 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 window.onclick = function { checkMediaType;timeUI; }; window.addEventListener("keydown", function { checkMediaType;timeUI; } );

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

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

// Adding elements

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

// button styling 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 timerUI.div.lastElementChild.classList.add("timerUI_buttons"); // label to improve visibility in page inspector

// background gradient appendChildWithID("div","timerUI_bottom_gradient",timerUI.div ); 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);

// 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 appendChildWithID("div","timerUI_progress_bar",timerUI.div ); 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%; left:0; bottom:0; }", timerUI.div);

// buffer bar appendChildWithID("div","timerUI_buffer_bar",timerUI.div ); 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);

// timer appendChildWithID("button","timerUI_media_timer",timerUI.div ); timerUI.time = document.getElementById("timerUI_media_timer"); 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);

// progress bar placeholder – put last to be at the top in stacking context appendChildWithID("div","timerUI_progress_placeholder",timerUI.div ); 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%; left:0; bottom:0; opacity:0.2; }", timerUI.div);

// 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); 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

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

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

// play and pause toggle 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;

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

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

timerUI.update;

// == Patches ==

// prevent missing out on pausing from inside a site's existing player window.addEventListener("mouseup", 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 timerUI.interval.checkMedia = setInterval( checkMediaType,1000 );

// prevent indicating "▶" after playback finished timerUI.interval.checkPaused = setInterval( function {				if ( media_element /* exists? */ && media_element.paused && ! timerUI.pause_checked) {					timerUI.update; timerUI.pause_checked = true;					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 );

} else { // warn in console that no player exists; prevent repetition if (! playerExists && ! timerUI.noMediaWarned) { console.warn(timerUI.msg.nomedia); timerUI.noMediaWarned = true; }	} }

// == Custom domain rules == timerUI.domainRules = function { if (isDomain("dailymotion.com") && document.location.pathname.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 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") 		|| is_archive_library ) { timerUI.light_mode_on; }

// 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 { // 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 and 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); }

// Internet Archive library only, not Wayback Machine if ( is_archive_library ) { // get media title from page title before first colon timerUI.newTitle = (document.location.href+"").substring((document.location.href+" ").search(/\/(?:.(?!\/))+\/?$/)+1 ); // after last slash (additional space prevents full URL from being matched) timerUI.archive_org_title = 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

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_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 { // 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 == timeUI;