Advertisement
Guest User

timerUI revision 2023-07-14

a guest
Jul 14th, 2023
166
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
JavaScript 31.16 KB | Source Code | 0 0
  1. // == Dependencies ==
  2.  
  3. var mediaType; // for compatibility
  4. var playerExists = false; // is set to true below if player exists
  5. var checkMediaType_enabled = true; // for debugging purposes
  6. var media_element = {}; // declaring as object
  7. var tmp="",count=0; // initiating temporary variables used inside functions and loops
  8.  
  9. function checkMediaType() {
  10.     // checks whether it is a video or an audio tag
  11.     if ( checkMediaType_enabled ) {
  12.             var mediaTypeBeforeCheck = mediaType;
  13.             if (document.getElementsByTagName("video")[0]) {
  14.                 playerExists = true; mediaType = "video";
  15.             }
  16.             if (document.getElementsByTagName("audio")[0]) {
  17.                 playerExists = true; mediaType = "audio";
  18.             }
  19.             var mediaTypeAfterCheck = mediaType;
  20.                if (mediaTypeBeforeCheck != mediaTypeAfterCheck)
  21.                   // Only show media type in console if it has changed.
  22.                   console.log("Detected media type: " + mediaType);
  23.         media_element = document.getElementsByTagName(mediaType)[0];
  24.         // Set back to false if no player is found after using customMediaElement.
  25.         media_element ? playerExists=true : playerExists=false;
  26.     }
  27. }
  28.  
  29. function customMediaElement(custom_media_element) {
  30.     checkMediaType_enabled = false;
  31.     if (custom_media_element) {
  32.         playerExists = true;
  33.         media_element = custom_media_element;
  34.         console.log("customMediaElement: Custom media element set. Reset using checkMediaType_enabled=true.");
  35.     } else { console.error("customMediaElement: No such media element found."); }
  36. }
  37. var customTitleElement;
  38.  
  39. function checkFileExtension(ext) {
  40.     if (typeof(ext) == "string") {
  41.         ext = ext.toLowerCase(); // case-insensitive
  42.         // string
  43.         if (document.location.href.search(new RegExp(ext+"$", "i")) > -1) return true; else return false;
  44.     } else if (typeof(ext) == "object") {
  45.         // array – check against multiple strings
  46.         for (var count=0; count < ext.length; count++) {
  47.             if (document.location.href.search(new RegExp(ext[count]+"$", "i")) > -1) return true;
  48.             if (count == ext.length-1) return false; // if no matches after going through them all
  49.         }
  50.     }
  51. }
  52.  
  53. function isDomain(domain) {
  54.     // Using .search() instead of .includes() to improve browser compatibility.
  55.     if (window.location.hostname.search(domain) >= 0) return true; else return false;
  56. }
  57.  
  58. // symbols
  59. var media_symbol = {};
  60.     media_symbol.play = "▶&#xFE0E;&thinsp;"; // thin space for alignment
  61.     media_symbol.pause="&#10074;&thinsp;&#10074;"; // instead of "⏸" due to Edge browser putting an immutable blue box around it.
  62.     media_symbol.stop="■";
  63.  
  64. // mousedown status
  65. var mousedown_status;
  66. window.addEventListener("mousedown", function(){mousedown_status=true; } );
  67. window.addEventListener("mouseup", function(){mousedown_status=false; } );
  68.  
  69. function appendChildWithID(tagName,tagID,parent_element) {
  70.     // default parent element to document.body if unspecified
  71.     if (parent_element === undefined) parent_element = document.body;
  72.     parent_element.appendChild(document.createElement(tagName)); // add div
  73.     parent_element.lastElementChild.id=tagID; // give it ID
  74. }
  75.  
  76. function addStyle(new_style,parent_element) {
  77.     if (parent_element === undefined) parent_element = document.body;
  78.     parent_element.appendChild(document.createElement("style")); // add style
  79.     parent_element.lastElementChild.innerHTML = new_style;
  80. }
  81.  
  82. // time variables
  83. var media_time = {};
  84.  
  85. // HH:MM:SS timer
  86. function HMStimer_core(seconds) {
  87.  
  88.         // hours
  89.         media_time.HH = Math.floor( seconds/3600 );
  90.         // leading zero
  91.         if ( seconds < 36000 ) media_time.HH = "0" + media_time.HH;
  92.  
  93.         // minutes
  94.         media_time.MM = Math.floor( seconds/60%60 );
  95.         // leading zero
  96.         if ( seconds%3600 < 600 ) media_time.MM = "0" + media_time.MM;
  97.  
  98.         // seconds
  99.         media_time.SS = Math.floor( seconds%60 );
  100.         // leading zero
  101.         if ( seconds%60 < 10 ) media_time.SS = "0" + media_time.SS;
  102.  
  103.     return media_time.HH+":"+media_time.MM+":"+media_time.SS;
  104. }
  105.  
  106. function HMStimer(seconds) {
  107.     if (seconds >= 0) return HMStimer_core(seconds); // zero or positive
  108.     if (seconds < 0) // negative
  109.         {
  110.          seconds = seconds * (-1);
  111.          return "-"+HMStimer_core(seconds);
  112.         }
  113.     if (seconds == undefined || isNaN(seconds) ) return "–&thinsp;–:–&thinsp;–:–&thinsp;–";
  114.  
  115. }
  116.  
  117. // MM:SS timer
  118. function MStimer_core(seconds) {
  119.  
  120.         // minutes
  121.         media_time.MM = Math.floor( seconds/60 );
  122.         // leading zero
  123.         if ( seconds%3600 < 600 ) media_time.MM = "0" + media_time.MM;
  124.  
  125.         // seconds
  126.         media_time.SS = Math.floor( seconds%60 );
  127.         // leading zero
  128.         if ( seconds%60 < 10 ) media_time.SS = "0" + media_time.SS;
  129.  
  130.     return media_time.MM+":"+media_time.SS;
  131. }
  132.  
  133. function MStimer(seconds) {
  134.     if (seconds >= 0) return MStimer_core(seconds); // zero or positive
  135.     if (seconds < 0) // negative
  136.         {
  137.          seconds = seconds * (-1);
  138.          return "-"+MStimer_core(seconds);
  139.         }
  140.     if (seconds == undefined || isNaN(seconds) ) return "–&thinsp;–:–&thinsp;–";
  141.  
  142. }
  143.  
  144.  
  145. // implements togglePlay(); – deprecated due to compatibility issues on YouTube (broken site) and Dailymotion ("not a function" error through iframe'd player).
  146. /*
  147. Object.prototype.togglePlay = function togglePlay() {
  148.   return this.paused ? this.play() : this.pause();
  149. };
  150. */
  151.  
  152. // new function without object prototype for compatibility
  153. function togglePlay(media_element) {
  154.     if (media_element) { // validate media element first to avoid errors
  155.         media_element.paused ? media_element.play() : media_element.pause();
  156.     }
  157. }
  158.  
  159. // media file extension list
  160. var mediafileext = {
  161.     "video":[".mp4", ".mpg", ".mpeg", ".mts", ".mt2s", ".m4v", ".ts", ".ogv", ".wmv", ".3gp", ".3gpp", ".webm"],
  162.     "audio":[".mp3", ".wma", ".wav", ".ogg", ".opus", ".flac", ".oga", ".wma", ".aac", ".amr", ".alac", ".m4a"]
  163. };
  164.  
  165. // "replaceAll()" polyfill for pre-2020 browsers
  166.     // based on: https://stackoverflow.com/a/1144788
  167.        
  168. function escapeRegExp(string) {
  169.     return string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); // $& means the whole matched string
  170. }
  171.  
  172.     // renamed function to prevent interference with "replaceAll()" on browsers since 2020
  173. function replaceAll_polyfill(str, find, replacement) {
  174.     return str.replace(new RegExp(escapeRegExp(find), 'g'), replacement);
  175. }
  176.  
  177. // == Main code ==
  178. if (! timerUI) var timerUI = new Object({}); // create parent object if none exists
  179.  
  180.     // default system variables
  181. timerUI.debug_mode = false;
  182. timerUI.override_check = false;
  183.  
  184. timerUI.on = true;
  185. timerUI.buffer_on = true;
  186. timerUI.multiBuffer = true; // multiple buffer segments
  187. timerUI.div = {}; // unset yet, declaring to prevent reference errors
  188. timerUI.interval = {};
  189. timerUI.show_remaining = 0; // 0: show elapsed time. 1: show remaining time. 2: show elapsed and total.
  190. timerUI.update_during_seek = true; // update timer while dragging seek bar
  191. timerUI.color = "rgb(49,136,255)"; // #38F – using RGB for compatibility.
  192. timerUI.default_color = "rgb(49,136,255)"; // memorize default color
  193. timerUI.gradient = "rgba(0,0,0,0.9)"; // using RGBA instead of hexadecimal for compatibility.
  194. 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";
  195. timerUI.width_breakpoint = 768; // pixels
  196.  
  197.     // console notifications and warnings (possibly to be expanded)
  198. timerUI.msg = {
  199.     "notimer": "timerUI: No timer found; checking for media element again. Please try again.",
  200.     "nomedia": "timerUI: no media element found on page. Stopping."
  201. };
  202.  
  203.     // text containers (no const for compatibility)
  204. var timer_linefeed = "<span class=timer_linefeed><br /></span>";
  205. var timer_slash = " <span class=timer_slash>/</span> ";
  206.  
  207.     // functions
  208. timerUI.toggle = {};
  209. timerUI.toggle.main = function() {
  210.     // show and hide
  211.     if (timerUI.div) {
  212.         timerUI.update();
  213.         if (timerUI.on) {  
  214.             timerUI.div.style.display = "none";
  215.             console.log("timerUI off");
  216.         }
  217.         if (! timerUI.on ) {  
  218.             timerUI.div.style.display = "block";
  219.             console.log("timerUI on");
  220.         }
  221.         if (timerUI.on) { timerUI.on = false; return false; } else { timerUI.on = true; return false; }
  222.     } else {
  223.         console.warn(timerUI.msg.notimer);
  224.         timeUI();
  225.     }
  226. };
  227.  
  228. timerUI.toggle.buffer = function() {
  229.     if (timerUI.div) {
  230.         timerUI.update(); timerUI.updateBufferBar(true);
  231.         if (timerUI.buffer_on) {  
  232.             timerUI.buffer_bar.style.display = "none";
  233.             console.log("timerUI buffer bar off");
  234.         }
  235.         if (! timerUI.buffer_on ) {  
  236.             timerUI.buffer_bar.style.display = "block";
  237.             console.log("timerUI buffer bar on");
  238.         }
  239.         if (timerUI.buffer_on) {
  240.             timerUI.buffer_on = false; return false;
  241.         } else {
  242.             timerUI.buffer_on = true; return true;
  243.         }
  244.     } else {
  245.         console.warn(timerUI.msg.notimer);
  246.         timeUI();
  247.     }
  248. };
  249.  
  250. timerUI.toggle.title = function() {
  251.     if (timerUI.div) {
  252.         timerUI.update(); timerUI.updateBufferBar(true);
  253.         if (timerUI.title_on) {  
  254.             timerUI.title.style.display = "none";
  255.             console.log("timerUI title off");
  256.         }
  257.         if (! timerUI.title_on ) {  
  258.             timerUI.title.style.display = "block";
  259.             console.log("timerUI title on");
  260.         }
  261.         if (timerUI.title_on) {
  262.             timerUI.title_on = false; return false;
  263.         } else {
  264.             timerUI.title_on = true; return true;
  265.         }
  266.     } else {
  267.         console.warn(timerUI.msg.notimer);
  268.         timeUI();
  269.     }
  270. };
  271.  
  272. timerUI.getTitle = function() {
  273.     if (! timerUI.domainRules_checked) /* only check domain rules once */ {
  274.         timerUI.domainRules();
  275.         timerUI.domainRules_checked = true;
  276.     }
  277.     if (customTitleElement) timerUI.newTitle = customTitleElement.innerHTML;
  278.     else { // skipping this whole part if no custom title is specified
  279.         timerUI.newTitle = document.title;
  280.         // replace underscores with spaces
  281.         timerUI.newTitle = replaceAll_polyfill(timerUI.newTitle, "_"," ");
  282.         timerUI.titleDomainRules();
  283.     }
  284.     if (media_element) {
  285.         timerUI.updateFileIcon();
  286.         return timerUI.file_icon+"&nbsp;"+timerUI.newTitle;
  287.     } else {
  288.         return "TimerUI – designed for home cinemas";
  289.     }
  290. };
  291.  
  292. timerUI.guessMediaType = function() {
  293.     if (isDomain("youtube.com") || isDomain("dailymotion.com") ) return "video";
  294.     if (document.location.pathname.search(/^\/video\//) > -1) return "video";
  295.     if (! media_element.videoWidth) return "audio"; // Detects files that only contain audio, even if they have a video file extension.
  296.     if (media_element.videoWidth > 0) return "video";
  297.     if (checkFileExtension(mediafileext.video) ) return "video";
  298.     if (checkFileExtension(mediafileext.audio) ) return "audio";
  299.     return "unknown"; // if nothing detected
  300. };
  301.  
  302. timerUI.updateFileIcon = function() {
  303.     timerUI.file_icon = timerUI.guessMediaType();
  304.     switch(timerUI.file_icon) {
  305.         case "video": timerUI.file_icon = "🎞️"; break;
  306.         case "audio": timerUI.file_icon = "♫"; break;
  307.         case "unknown": timerUI.file_icon = "📄"; break;
  308.     }
  309. };
  310.  
  311.  
  312. timerUI.adaptTitleWidth = function() {
  313.     if (media_element.duration > 3600 && timerUI.show_remaining == 2 && window.innerWidth > timerUI.width_breakpoint)
  314.         timerUI.title.style.maxWidth = "calc(100% - 670px)";
  315.     else
  316.         timerUI.title.style.maxWidth = "calc(100% - 400px)";
  317. };
  318.  
  319. window.addEventListener('resize', function() {
  320.     if (window.innerWidth < timerUI.width_breakpoint) timerUI.title.removeAttribute("style");
  321. } );
  322.  
  323. timerUI.update = function() {
  324.     if (media_element) {
  325.         timerUI.bar.style.width=media_element.currentTime / media_element.duration * 100 + "%";
  326.  
  327.         // buffer bar update formerly located here; removed from the scope of this function
  328.  
  329.         switch(timerUI.show_remaining) {
  330.             // 0: "HH:MM:SS"  1: "-HH:MM:SS"  2: "MM:SS / MM:SS" or "HH:MM:SS / HH:MM:SS"
  331.             case 0: timerUI.time.innerHTML=HMStimer(media_element.currentTime); break;
  332.             case 1: timerUI.time.innerHTML=HMStimer(media_element.currentTime-media_element.duration); break;
  333.             case 2:
  334.                 if (media_element.duration < 3600 || isNaN(media_element.duration) ) {
  335.                 // show hours if duration exceeds one hour
  336.                     timerUI.time.innerHTML=
  337.                         MStimer(media_element.currentTime)
  338.                         + timer_linefeed
  339.                         + timer_slash
  340.                         + MStimer(media_element.duration);
  341.                 } else {
  342.                     timerUI.time.innerHTML=
  343.                         HMStimer(media_element.currentTime)
  344.                         + timer_linefeed
  345.                         + timer_slash
  346.                         + HMStimer(media_element.duration);
  347.                 }
  348.                 break;
  349.         }
  350.  
  351.         if (media_element.paused) {
  352.             timerUI.status.innerHTML=media_symbol.pause;
  353.         } else {
  354.             timerUI.status.innerHTML=media_symbol.play;
  355.         }
  356.     } else { timerUI.stop(); console.warn(timerUI.msg.nomedia); }
  357. };
  358. timerUI.updateTitle = function() {
  359.     if (timerUI.title) timerUI.title.innerHTML = timerUI.getTitle();
  360.     };
  361.  
  362. // update title on URL change
  363. addEventListener('popstate', timerUI.updateTitle);
  364.  
  365. // update title fallback
  366. timerUI.interval.updateTitle = setInterval(timerUI.updateTitle, 2000);
  367.  
  368.  
  369. // buffer bar
  370. timerUI.updateBufferBar = function(override_paused) {
  371.     if (media_element && timerUI.buffer_on && (!media_element.paused || override_paused) ) {
  372.     timerUI.multiBuffer ? timerUI.update_multi_buffer() : timerUI.single_segment_buffer();
  373.   }
  374. };
  375.  
  376. // single-segment buffer bar
  377. timerUI.single_segment_buffer = function() {
  378.     if (timerUI.buffer_bar.innerHTML!="") { timerUI.buffer_bar.style=""; timerUI.buffer_bar.innerHTML=""; } // reset after switching from multi-segment buffer bar
  379.     // find out first buffer segment after current playback position
  380.     media_element.buffered.length > 0 ? timerUI.buffer_segment=media_element.buffered.length-1 : timerUI.buffer_segment=0;
  381.     // media_element.buffered.length is zero until player is initialized
  382.     // prevent timerUI.buffer_segment from going negative, as it would cause a DOMException error
  383.     if  ( timerUI.buffer_segment > 0) {
  384.         while (media_element.buffered.end(timerUI.buffer_segment-1) > media_element.currentTime && timerUI.buffer_segment > 1 ) {
  385.             timerUI.update_single_buffer();
  386.             timerUI.buffer_segment-- ;
  387.         }
  388.     }
  389. };
  390.  
  391. timerUI.update_single_buffer = function() {
  392.     if (media_element.buffered.length > 0) {
  393.     // prevent "DOMException: Index or size is negative or greater than the allowed amount"
  394.         timerUI.buffer_bar.style.width=media_element.buffered.end(timerUI.buffer_segment) / media_element.duration * 100 + "%";
  395.     } else { timerUI.buffer_bar.style.width="0%"; }
  396. };
  397.  
  398. // multi-segment buffer bar – highlight all buffered parts
  399. timerUI.update_multi_buffer = function() {
  400.     if (timerUI.buffer_bar.style.length < 1) {
  401.         timerUI.buffer_bar.style.width="100%";
  402.         timerUI.buffer_bar.style.backgroundColor="rgba(0,0,0,0)";
  403.     }
  404.     if (media_element.buffered.length > 0) {
  405.         timerUI.generate_buffer_segments();
  406.     } else { timerUI.buffer_bar.style.width="0%"; }
  407. };
  408.  
  409. timerUI.generate_buffer_segments = function() {
  410.     timerUI.buffer_bar.innerHTML=""; // reset to re-generate segments
  411.     for (count=0; count < media_element.buffered.length; count++) {
  412.         timerUI.append_buffer_segment(
  413.             timerUI.get_buffer_range(count).start_pos,
  414.             timerUI.get_buffer_range(count).end_pos
  415.         );
  416.     }
  417.     timerUI.select_segments = timerUI.buffer_bar.getElementsByClassName("timerUI_buffer_segment");
  418.     timerUI.segment_count = timerUI.select_segments.length;
  419. };
  420.  
  421. timerUI.append_buffer_segment = function(start_pos,end_pos) {
  422.     timerUI.buffer_bar.appendChild(document.createElement("div") );
  423.     timerUI.buffer_bar.lastElementChild.classList.add("timerUI_buffer_segment");
  424.     timerUI.buffer_bar.lastElementChild.style="left:"+start_pos+"%;width:"+(end_pos-start_pos)+"%;background-color:"+timerUI.color+";";
  425. };
  426.  
  427. timerUI.get_buffer_range = function(segment_number) {
  428.     return {
  429.     start_pos: media_element.buffered.start(segment_number) / media_element.duration * 100,
  430.     end_pos: media_element.buffered.end(segment_number) / media_element.duration * 100
  431.     }; // object with start and end percentages
  432. };
  433.  
  434. timerUI.set_buffer_segment = function(segment_number,start_pos,end_pos) {
  435.     var selection=timerUI.buffer_bar.getElementsByClassName("timerUI_buffer_segment");
  436.     selection[segment_number].style.left = start_pos / media_element.duration * 100 + "%";
  437.     selection[segment_number].style.width = (end_pos-start_pos) / media_element.duration * 100 + "%";
  438. };
  439.  
  440.  
  441.  
  442. // colors
  443. timerUI.setColor = function(newColor) {
  444.     if (! timerUI.bar) {
  445.         /* prevent running function before timerUI is properly loaded into the DOM to prevent exception */
  446.         if (timerUI.debug_mode) console.debug("timerUI: setColor can not run before timerUI is properly loaded.");
  447.         return false;
  448.     }
  449.     timerUI.previous_color = timerUI.color; // memorize previous setting
  450.     newColor == "default" ? timerUI.color="rgb(49,136,255)" /* #38F */ : timerUI.color = newColor;
  451.  
  452.     timerUI.bar.style.backgroundColor=timerUI.color;
  453.     timerUI.buffer_bar.style.backgroundColor=timerUI.color;
  454.     timerUI.bar.style.boxShadow="0 0 30px 0 "+timerUI.color;
  455.     // (deprecated due to new buffer bar) timerUI.bar_placeholder.style.backgroundColor=timerUI.color;
  456.     timerUI.time.style.color=timerUI.color;
  457.     timerUI.status.style.color=timerUI.color;
  458.     timerUI.title.style.color=timerUI.color;
  459.  
  460.     // colour all buffer segments
  461.     for (var count=0; count < timerUI.buffer_bar.childNodes.length; count++) {
  462.         timerUI.buffer_bar.childNodes[count].style.backgroundColor=timerUI.color;
  463.     }
  464.     if (timerUI.debug_mode) console.debug("timerUI: color changed from "+timerUI.previous_color+" to "+timerUI.color);
  465. };
  466.  
  467. timerUI.setColor("default"); // prevent mixed colours if the code is run multiple times
  468.  
  469. timerUI.setGradient = function(newGradient) {
  470.     newGradient == "default" ? timerUI.gradient="rgba(0,0,0,0.9)" : timerUI.gradient = newGradient;
  471.     timerUI.gradient_placeholder.style.backgroundImage="linear-gradient(to top,"+timerUI.gradient+", rgba(0,0,0,0) )";
  472. };
  473.  
  474. timerUI.setFont = function(newFont) {
  475.     timerUI.time.style.fontFamily=newFont;
  476.     timerUI.title.style.fontFamily=newFont;
  477. };
  478.  
  479.  
  480. // light mode
  481.  
  482. timerUI.light_mode = false; // default
  483.  
  484. timerUI.light_mode_on = function() {
  485.     timerUI.light_mode = true;
  486.     timerUI.color_before_light_mode = timerUI.color;
  487.     // improves visibility:
  488.     if (timerUI.setColor) /* exists*/ {
  489.         timerUI.setColor("lightblue");
  490.     } else { return false; /* error */ }
  491.     if (timerUI.gradient_placeholder) /* exists*/ {
  492.         timerUI.gradient_placeholder.style.backgroundColor="rgba(0, 0, 0, 0.5)";
  493.     } else { return false; /* error */ }
  494. };
  495.  
  496. timerUI.light_mode_off = function() {
  497.     timerUI.light_mode = false;
  498.     if (timerUI.setColor) /* exists*/ {
  499.         timerUI.setColor("lightblue");
  500.     } else { return false; /* error */ }
  501.     if (timerUI.gradient_placeholder) /* exists*/ {
  502.         timerUI.gradient_placeholder.style.backgroundColor=""; // fall back to default
  503.     } else { return false; /* error */ }
  504. };
  505.  
  506. timerUI.toggle.light_mode = function() {
  507.     if ( ! timerUI.light_mode ) /* if light mode is deactivated */ {
  508.         timerUI.light_mode_on();
  509.         return true;
  510.     } else {
  511.         timerUI.light_mode_off();
  512.         return false;
  513.     }
  514. };
  515.  
  516. // "stop" icon
  517.  
  518. timerUI.stop = function() {
  519.     timerUI.status.innerHTML=media_symbol.stop;
  520.     timerUI.bar.style.width=0;
  521.     timerUI.buffer_bar.style.width=0;
  522.     // appearance of stopped timer consistent with the show_remaining setting
  523.     if (timerUI.show_remaining == 2) {
  524.         timerUI.time.innerHTML=MStimer(undefined)+" / "+MStimer(undefined);
  525.     } else {
  526.         timerUI.time.innerHTML=HMStimer(undefined);
  527.     }
  528. };
  529.  
  530. // Additional checks to ensure the player is detected
  531. window.onclick = function() { checkMediaType();timeUI(); };
  532. window.addEventListener("keydown", function() { checkMediaType();timeUI(); } );
  533.  
  534.  
  535. function timeUI() {
  536. // slightly different name to prevent naming collision with timerUI object
  537.  
  538.     checkMediaType();
  539.     // add timerUI if it does not already exist
  540.     if ( ( ! document.getElementById("timerUI") ) && playerExists || timerUI.override_check ) {
  541.  
  542.     // Adding elements
  543.  
  544.         // parent element
  545.         appendChildWithID("div","timerUI");
  546.         timerUI.div = document.getElementById("timerUI");
  547.  
  548.         // button styling
  549.         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);
  550.             // to suppress button background and border on earlier browser versions
  551.         timerUI.div.lastElementChild.classList.add("timerUI_buttons"); // label to improve visibility in page inspector
  552.  
  553.         // background gradient
  554.         appendChildWithID("div","timerUI_bottom_gradient",timerUI.div );
  555.         timerUI.gradient_placeholder = document.getElementById("timerUI_bottom_gradient");
  556.         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);
  557.  
  558.         // play/pause symbol
  559.         appendChildWithID("button","timerUI_playback_status",timerUI.div );
  560.         timerUI.status = document.getElementById("timerUI_playback_status");
  561.         timerUI.status.innerHTML="■";
  562.         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);
  563.  
  564.         // progress bar
  565.         appendChildWithID("div","timerUI_progress_bar",timerUI.div );
  566.         timerUI.bar = document.getElementById("timerUI_progress_bar");
  567.         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);
  568.  
  569.         // buffer bar
  570.         appendChildWithID("div","timerUI_buffer_bar",timerUI.div );
  571.         timerUI.buffer_bar = document.getElementById("timerUI_buffer_bar");
  572.         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);
  573.  
  574.  
  575.         // timer
  576.         appendChildWithID("button","timerUI_media_timer",timerUI.div );
  577.         timerUI.time = document.getElementById("timerUI_media_timer");
  578.         timerUI.time.innerHTML="00:00:00";
  579.         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);
  580.  
  581.  
  582.  
  583.         // progress bar placeholder – put last to be at the top in stacking context
  584.         appendChildWithID("div","timerUI_progress_placeholder",timerUI.div );
  585.         timerUI.bar_placeholder = document.getElementById("timerUI_progress_placeholder");
  586.         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);
  587.  
  588.         // responsive - at bottom to be able to override CSS properties without !important flag.
  589.         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);
  590.         timerUI.div.lastElementChild.classList.add("timerUI_responsive");
  591.  
  592.         // media title
  593.         appendChildWithID("div","timerUI_media_title",timerUI.div );
  594.         timerUI.title = document.getElementById("timerUI_media_title");
  595.         timerUI.title.innerHTML = timerUI.getTitle();
  596.         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);
  597.  
  598.         timerUI.domainRules(); // load domain rules after initializing timer elements
  599.  
  600.     // update timer during playback every fifteenth of a second and while mouse is dragging progress bar
  601.     timerUI.interval.update = setInterval(
  602.         function() { if (
  603.                 ( media_element /* exists? */ && timerUI.update_during_seek && mousedown_status )
  604.              || ( media_element && timerUI.on && ! media_element.paused )
  605.     ) timerUI.update(); }, 1000/15
  606.     );
  607.  
  608.     // Longer interval for buffer bar to minimize CPU usage
  609.     timerUI.interval.buffer = setInterval(timerUI.updateBufferBar, 1000);
  610.  
  611.  
  612.     // play and pause toggle
  613.     timerUI.status.onclick = function() { togglePlay(media_element); timerUI.update(); timerUI.updateBufferBar(true); };
  614.         // former code with object prototype caused compatibility issues on various sites: media_element.togglePlay();
  615.  
  616.     // toggle between elapsed, remaining, and elapsed/total time
  617.     timerUI.time.onclick = function() {
  618.         switch(timerUI.show_remaining) {
  619.             case 0: timerUI.show_remaining = 1; break;
  620.             case 1: timerUI.show_remaining = 2; timerUI.adaptTitleWidth(); break;
  621.             case 2: timerUI.show_remaining = 0; timerUI.adaptTitleWidth(); break;
  622.         }
  623.         timerUI.update(); timerUI.updateBufferBar(true);
  624.     };
  625.  
  626.     // clickable progress bar (experimental) - "clickPos" has no "timerUI." notation because it is inside the main function.
  627.     timerUI.clickSeekBar = function(m){
  628.         if (media_element) {
  629.             var clickPos = m.clientX / timerUI.bar_placeholder.offsetWidth;
  630.             // go to beginning if clicked in first percentile
  631.             if (clickPos < 0.01 ) media_element.currentTime = 0;
  632.                 else media_element.currentTime = media_element.duration * clickPos;
  633.             timerUI.update(); timerUI.updateBufferBar(true);
  634.         }
  635.     };
  636.     /*  function dragSeekBar(m){ // currently unused
  637.         m = m || window.event;
  638.         var clickPos = m.pageX / timerUI.bar_placeholder.offsetWidth;
  639.         var dragSeekBarInterval = setInterval( function() {
  640.             media_element.currentTime = media_element.duration * clickPos;
  641.             timerUI.update();
  642.             if (! mousedown_status) { clearInterval(dragSeekBarInterval); return; }
  643.         }, 200);
  644.     } */
  645.     timerUI.bar_placeholder.addEventListener("mousedown", timerUI.clickSeekBar );
  646.     // (incomplete) timerUI.bar_placeholder.addEventListener("mousemove", dragSeekBar );
  647.     // (obsolete) timerUI.bar_placeholder.addEventListener("mouseup", clickSeekBar );
  648.  
  649.     timerUI.update();
  650.  
  651.     // == Patches ==
  652.  
  653.         // prevent missing out on pausing from inside a site's existing player
  654.         window.addEventListener("mouseup", function() { setTimeout(timerUI.update, 200); } );
  655.         window.addEventListener("keyup", function() { setTimeout(timerUI.update, 200); } );
  656.  
  657.         // prevent detaching from player on sites with playlists such as Internet Archive
  658.         timerUI.interval.checkMedia = setInterval( checkMediaType,1000 );
  659.  
  660.         // prevent indicating "▶" after playback finished
  661.         timerUI.interval.checkPaused = setInterval( function() {
  662.                 if ( media_element /* exists? */ && media_element.paused && ! timerUI.pause_checked) {
  663.                     timerUI.update(); timerUI.pause_checked = true;
  664.                     if (timerUI.debug_mode) console.debug("timerUI: checking paused status: "+media_element.paused);
  665.                 } else if ( media_element && ! media_element.paused ) { timerUI.pause_checked = false; }
  666.                 // to avoid redundant checks while paused
  667.         },1000 );
  668.  
  669.     } else {
  670.     // warn in console that no player exists; prevent repetition
  671.         if (! playerExists && ! timerUI.noMediaWarned) {
  672.                 console.warn(timerUI.msg.nomedia); timerUI.noMediaWarned = true;
  673.         }
  674.     }
  675. }
  676.  
  677. // == Custom domain rules ==
  678. timerUI.domainRules = function() {
  679.     if (isDomain("dailymotion.com") && document.location.pathname.search(/^\/embed\//) < 0 ) {
  680.         // Dailymotion watch page, excluding embed page.
  681.         customMediaElement(
  682.             document.getElementById("player-body").contentWindow.document.getElementsByTagName("video")[0]
  683.         );
  684.         customTitleElement = document.getElementById("media-title"); // for unlisted videos (Dailymotion only displays the video title in the HTML page title for public videos)
  685.     }
  686.  
  687.     // activate light mode on wikis and Dailymotion due to bright backgrounds
  688.     if ( isDomain("wiki")
  689.         || isDomain("dailymotion.com")
  690.         || isDomain("ghostarchive.org")
  691.         || is_archive_library() ) {
  692.         timerUI.light_mode_on();
  693.     }
  694.  
  695.     // media embedded on Wayback Machine
  696.     if ( is_WaybackEmbed() ) {
  697.         tmp = document.getElementsByTagName("iframe")[0]; // iframe in temporary variable to deduplicate code
  698.         customMediaElement(
  699.             tmp.contentWindow.document.getElementsByTagName("video")[0]
  700.         );
  701.         // dark background for improved video visibility
  702.         tmp.contentWindow.document.body.style.backgroundColor="#222";
  703.         }
  704. };
  705.  
  706. timerUI.titleDomainRules = function() {
  707. // custom domain rules for title
  708.     if ( isDomain("youtube.com") || isDomain("dailymotion.com")
  709.         || isDomain("wikimedia.org") || isDomain("wikipedia.org")
  710.         || isDomain("wikiversity.org") || isDomain("wikibooks.org")
  711.         || isDomain("mediawiki.org")
  712.         ) {
  713.         // negative lookahead regular expression – trim after last dash
  714.         // Match both normal dash "-" and ndash "–", since the German-language wikis use the latter.
  715.         timerUI.newTitle = decodeURI(timerUI.newTitle.substring(0,timerUI.newTitle.search(/(-|–)(?:.(?!(-|–)))+$/)-1 ) );
  716.     }
  717.     // remove "File:" prefix on wikis and
  718.     if ( isDomain("wiki") ) {
  719.         if (document.title.search(/^File:/)  == 0 ) timerUI.newTitle = timerUI.newTitle.substring(5);
  720.         if (document.title.search(/^Datei:/) == 0 ) timerUI.newTitle = timerUI.newTitle.substring(6);
  721.     }
  722.  
  723.     // Internet Archive library only, not Wayback Machine
  724.     if ( is_archive_library() ) {
  725.         // get media title from page title before first colon
  726.         timerUI.newTitle = (document.location.href+"").substring((document.location.href+" ").search(/\/(?:.(?!\/))+\/?$/)+1 );
  727.         // after last slash (additional space prevents full URL from being matched)
  728.         timerUI.archive_org_title = document.title.substring(0,document.title.search(/:(?:.(?!:))+(?:.(?!:))+/)-1 );
  729.         if (timerUI.archive_org_title.length > 0) /* only append " - " if a title exists. */ {
  730.             /* only append " - " if a title exists. */
  731.             if (timerUI.newTitle != "") { timerUI.newTitle += " - "; }
  732.             timerUI.newTitle += timerUI.archive_org_title;
  733.         }
  734.         // trim after second-last colon, -1 to remove space at end
  735.  
  736.         timerUI.newTitle=decodeURI(timerUI.newTitle); // prevent spaces from turning into "%20".
  737.     }
  738.     if ( is_WaybackEmbed() ) {
  739.         // 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.
  740.         timerUI.newTitle = tmp.contentWindow.document.title;
  741.     }
  742. };
  743.  
  744. function is_archive_library() {
  745.     // function to check if the current page is an Archive.org library page and not the Wayback Machine
  746.     return (isDomain(/^(www.)?archive.org/) && ! isDomain("web.archive.org") );
  747.     // automatically returns true if the condition is met and false otherwise
  748. }
  749. function is_WaybackEmbed() {
  750.     // checks if the current page is media embedded on the Wayback Machine, for code deduplication.
  751.     if ( isDomain("web.archive.org") || isDomain("wayback.archive.org") && document.title=="Wayback Machine" && document.getElementsByTagName("iframe")[0] ) {
  752.         // separate check for ID of iframe to avoid reference error
  753.         if (document.getElementsByTagName("iframe")[0].id=="playback") {
  754.             return true;
  755.         }
  756.     } else {
  757.         return false;
  758.     }
  759. }
  760.  
  761.  
  762. // == Master function ==
  763. timeUI();
  764.  
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement