Iridium

YouTube with more freedom

  1. // ==UserScript==
  2. // @version 0.3.0a
  3. // @name Iridium
  4. // @namespace https://github.com/ParticleCore
  5. // @description YouTube with more freedom
  6. // @compatible firefox
  7. // @compatible opera
  8. // @icon https://raw.githubusercontent.com/ParticleCore/Iridium/gh-pages/images/i-icon.png
  9. // @match *://www.youtube.com/*
  10. // @exclude *://www.youtube.com/tv*
  11. // @exclude *://www.youtube.com/embed/*
  12. // @exclude *://www.youtube.com/live_chat*
  13. // @run-at document-start
  14. // @homepageURL https://github.com/ParticleCore/Iridium
  15. // @supportURL https://github.com/ParticleCore/Iridium/wiki
  16. // @contributionURL https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=UMVQJJFG4BFHW
  17. // @grant GM_getValue
  18. // @grant GM_setValue
  19. // @noframes
  20. // ==/UserScript==
  21. (function () {
  22. "use strict";
  23.  
  24. var iridium = {
  25.  
  26. inject: function (is_userscript) {
  27.  
  28. var i18n;
  29. var modules;
  30. var iridium_api;
  31. var user_settings;
  32. var default_language;
  33.  
  34. default_language = {
  35. language: "English (US)",
  36. section_titles: {
  37. about: "Information and useful links",
  38. general: "General settings",
  39. video: "Video settings",
  40. settings: "Iridium settings"
  41. },
  42. sub_section_titles: {
  43. channel: "Channel",
  44. blacklist: "Blacklist",
  45. general: "General",
  46. language: "Language",
  47. layout: "Layout",
  48. player: "Player",
  49. settings: "Settings",
  50. thumbnails: "Thumbnails"
  51. },
  52. iridium_api: {
  53. settings_button: "Iridium settings",
  54. feature_link: "Find out what this does"
  55. }
  56. };
  57.  
  58. modules = [
  59. {
  60. options: {
  61. default_logo_page: {
  62. id: "default_logo_page",
  63. section: "general",
  64. sub_section: "general",
  65. type: "dropdown",
  66. value: "home",
  67. i18n: {
  68. label: "Default YouTube logo page:",
  69. options: [
  70. "Home",
  71. "Trending",
  72. "Subscriptions"
  73. ]
  74. },
  75. options: [
  76. "home",
  77. "trending",
  78. "subscriptions"
  79. ]
  80. },
  81. default_channel_tab: {
  82. id: "default_channel_tab",
  83. section: "general",
  84. sub_section: "general",
  85. type: "dropdown",
  86. value: "home",
  87. i18n: {
  88. label: "Default channel tab:",
  89. options: [
  90. "Home",
  91. "Videos",
  92. "Playlists",
  93. "Channels",
  94. "Discussion",
  95. "About"
  96. ]
  97. },
  98. options: [
  99. "home",
  100. "videos",
  101. "playlists",
  102. "channels",
  103. "discussion",
  104. "about"
  105. ]
  106. }
  107. },
  108. setDestination: function (event) {
  109.  
  110. var url;
  111. var data;
  112.  
  113. if ((data = event.target.data) && (url = data.webNavigationEndpointData && data.webNavigationEndpointData.url)) {
  114.  
  115. if (user_settings.default_channel_tab !== "home" && url.match(/^\/(?:channel|user)\/(?:[^\/])+$/)) {
  116.  
  117. data.webNavigationEndpointData.url += "/" + user_settings.default_channel_tab;
  118. event.target.href = data.webNavigationEndpointData.url;
  119.  
  120. }
  121.  
  122. if (user_settings.default_logo_page !== "home" && url === "/" && event.target.tagName === "A" && event.target.id === "logo") {
  123.  
  124. data.browseEndpoint.browseId = "FE" + user_settings.default_logo_page;
  125. data.webNavigationEndpointData.url += "feed/" + user_settings.default_logo_page;
  126. event.target.href = data.webNavigationEndpointData.url;
  127.  
  128. }
  129.  
  130. }
  131.  
  132. },
  133. ini: function () {
  134.  
  135. if (iridium_api.initializeOption.call(this)) {
  136.  
  137. return;
  138.  
  139. }
  140.  
  141. window.addEventListener("mouseup", this.setDestination.bind(this), true);
  142.  
  143. }
  144. },
  145. {
  146. options: {
  147. square_avatars: {
  148. id: "square_avatars",
  149. section: "general",
  150. sub_section: "layout",
  151. type: "checkbox",
  152. value: true,
  153. i18n: {
  154. label: "Make user images squared"
  155. }
  156. }
  157. },
  158. ini: function () {
  159.  
  160. if (iridium_api.initializeOption.call(this)) {
  161.  
  162. return;
  163.  
  164. }
  165.  
  166. if (user_settings.square_avatars) {
  167.  
  168. document.documentElement.classList.add("iri-square-avatars");
  169.  
  170. } else {
  171.  
  172. document.documentElement.classList.remove("iri-square-avatars");
  173.  
  174. }
  175.  
  176. }
  177. },
  178. {
  179. options: {
  180. thumbnail_preview: {
  181. id: "thumbnail_preview",
  182. section: "general",
  183. sub_section: "thumbnails",
  184. type: "checkbox",
  185. value: false,
  186. i18n: {
  187. label: "Preview videos by hovering the thumbnails"
  188. }
  189. },
  190. thumbnail_preview_mute: {
  191. id: "thumbnail_preview_mute",
  192. section: "general",
  193. sub_section: "thumbnails",
  194. type: "checkbox",
  195. value: false,
  196. i18n: {
  197. label: "Shift key toggles audio on video preview"
  198. }
  199. }
  200. },
  201. togglePreviewMute: function (event) {
  202.  
  203. var player_api;
  204.  
  205. if (user_settings.thumbnail_preview_mute && event.which === 16 && (player_api = document.getElementById("iri-preview-player"))) {
  206.  
  207. player_api.handleGlobalKeyDown(77, false);
  208.  
  209. }
  210.  
  211. },
  212. setPreviewArgs: function (args) {
  213.  
  214. args.autoplay = 1;
  215. args.controls = "0";
  216. args.enablecastapi = "0";
  217. args.iv_load_policy = "3";
  218. args.modestbranding = "1";
  219. args.mute = "1";
  220. args.player_wide = "0";
  221. args.rel = "0";
  222. args.showinfo = "0";
  223. args.vq = "small";
  224.  
  225. args.ad3_module = null;
  226. args.baseUrl = null;
  227. args.eventid = null; // excludes from watch history
  228. args.iv_endscreen_url = null;
  229. args.ppv_remarketing_url = null;
  230. args.probe_url = null;
  231. args.remarketing_url = null;
  232. args.videostats_playback_base_url = null;
  233.  
  234. },
  235. iniPreview: function (context, event) {
  236.  
  237. var i;
  238. var args;
  239. var temp;
  240. var config;
  241. var data_list;
  242. var player_api;
  243.  
  244. args = {};
  245. data_list = event.target.responseText.split("&");
  246.  
  247. for (i = 0; i < data_list.length; i++) {
  248.  
  249. temp = data_list[i].split("=");
  250. args[temp[0]] = window.decodeURIComponent(temp[1]);
  251.  
  252. }
  253.  
  254. context.setPreviewArgs(args);
  255.  
  256. config = JSON.parse(JSON.stringify(window.yt.config_.FILLER_DATA.player));
  257. config.args = args;
  258. config.attrs.id = "iri-preview-player";
  259.  
  260. window.yt.player.Application.create("iri-video-preview", config);
  261.  
  262. if ((player_api = document.getElementById("iri-preview-player"))) {
  263.  
  264. player_api.setVolume(50);
  265.  
  266. }
  267.  
  268. },
  269. getPreviewArgs: function (video_id) {
  270.  
  271. var sts;
  272. var xhr;
  273. var params;
  274. var context;
  275.  
  276. context = this;
  277. sts = window.yt.config_.FILLER_DATA.player.sts;
  278. params =
  279. "video_id=" + video_id + "&" +
  280. "sts=" + sts + "&" +
  281. "ps=gaming" + "&" +
  282. "el=detailpage" + "&" +
  283. "c=WEB_GAMING" + "&" +
  284. "cplayer=UNIPLAYER" + "&" +
  285. "mute=true" + "&" +
  286. "authuser=0";
  287.  
  288. xhr = new XMLHttpRequest();
  289. xhr.addEventListener("load", function (event) {
  290.  
  291. context.iniPreview(context, event);
  292.  
  293. });
  294. xhr.open("GET", "/get_video_info?" + params, true);
  295. xhr.send();
  296.  
  297. return xhr;
  298.  
  299. },
  300. endPreviewContainer: function (event, container, listener, xhr, timer, context, video_container, clicked) {
  301.  
  302. if (clicked || !container.parentNode.contains(event.toElement || event.relatedTarget)) {
  303.  
  304. document.removeEventListener("keydown", context.togglePreviewMute, false);
  305.  
  306. container.parentNode.removeEventListener("click", listener, false);
  307. container.parentNode.removeEventListener("mouseleave", listener, false);
  308.  
  309. if (timer) {
  310.  
  311. window.clearInterval(timer);
  312.  
  313. }
  314.  
  315. if ((video_container = document.getElementById("iri-video-preview"))) {
  316.  
  317. if (xhr) {
  318.  
  319. xhr.abort();
  320.  
  321. }
  322.  
  323. if (video_container.firstChild) {
  324.  
  325. video_container.firstChild.destroy();
  326.  
  327. }
  328. }
  329.  
  330. if (clicked && video_container) {
  331.  
  332. // video_container.remove();
  333.  
  334. }
  335. }
  336.  
  337. },
  338. iniPreviewContainer: function (event) {
  339.  
  340. var xhr;
  341. var timer;
  342. var context;
  343. var video_id;
  344. var container;
  345. var video_container;
  346.  
  347. if (user_settings.thumbnail_preview) {
  348.  
  349. container = event.target;
  350. video_id = container.dataHost && container.dataHost.data && container.dataHost.data.videoId;
  351.  
  352. if (container.tagName === "YT-IMG-SHADOW" && video_id && !container.querySelector("#iri-preview-player")) {
  353.  
  354. context = this;
  355.  
  356. if (!(video_container = document.getElementById("iri-video-preview"))) {
  357.  
  358. video_container = document.createElement("iri-video-preview");
  359. video_container.id = "iri-video-preview";
  360. video_container.className = "ytp-small-mode";
  361.  
  362. }
  363.  
  364. if (video_container.parentNode !== container) {
  365.  
  366. container.appendChild(video_container);
  367.  
  368. }
  369.  
  370. if (!window.yt || !window.yt.player || !window.yt.player.Application || !window.yt.player.Application.create) {
  371.  
  372. timer = window.setInterval(function () {
  373.  
  374. if (window.yt && window.yt.player && window.yt.player.Application && window.yt.player.Application.create) {
  375.  
  376. window.clearInterval(timer);
  377. xhr = context.getPreviewArgs(video_id);
  378.  
  379. }
  380.  
  381. });
  382.  
  383. } else {
  384.  
  385. xhr = this.getPreviewArgs(video_id);
  386.  
  387. }
  388.  
  389. document.addEventListener("keydown", this.togglePreviewMute, false);
  390.  
  391. container.parentNode.addEventListener("click", function listener(event) {
  392.  
  393. context.endPreviewContainer(event, container, listener, xhr, timer, context, video_container, true);
  394.  
  395. }, false);
  396.  
  397. container.parentNode.addEventListener("mouseleave", function listener(event) {
  398.  
  399. context.endPreviewContainer(event, container, listener, xhr, timer, context);
  400.  
  401. }, false);
  402.  
  403. }
  404.  
  405. }
  406.  
  407. },
  408. ini: function () {
  409.  
  410. if (iridium_api.initializeOption.call(this)) {
  411.  
  412. return;
  413.  
  414. }
  415.  
  416. document.addEventListener("mouseenter", this.iniPreviewContainer.bind(this), true);
  417.  
  418. }
  419. },
  420. {
  421. options: {
  422. enable_blacklist: {
  423. id: "enable_blacklist",
  424. section: "general",
  425. sub_section: "blacklist",
  426. type: "checkbox",
  427. value: "home",
  428. i18n: {
  429. label: "Enable blacklist"
  430. }
  431. },
  432. blacklist_settings: {
  433. id: "blacklist_settings",
  434. section: "general",
  435. sub_section: "blacklist",
  436. type: "custom",
  437. value: {},
  438. i18n: {
  439. button_add_title: "Block",
  440. button_edit: "Edit",
  441. button_import: "Import",
  442. button_export: "Export",
  443. button_reset: "Reset",
  444. button_save: "Save",
  445. button_close: "Close",
  446. button_remove: "Remove from blacklist",
  447. placeholder: "Paste your new blacklist here",
  448. confirm_reset: "You are about to reset your blacklist. It is advised to backup your current blacklist before continuing.\n\nDo you wish to contiue?\n\n",
  449. reset_success: "Blacklist has been reset.\n\nChanges will be applied after a page refresh.\n\n",
  450. confirm_import: "You are about to override your current blacklist. It is advised to backup your current blacklist before continuing.\n\nDo you wish to contiue?\n\n",
  451. import_success: "Your blacklist has been imported with success.\n\nChanges will be applied after a page refresh.\n\n",
  452. import_error: "Your blacklist could not be imported because it appears to be invalid.\n\n"
  453. },
  454. custom: function () {
  455.  
  456. var element;
  457. var element_list;
  458.  
  459. element_list = [];
  460.  
  461. element = document.createElement("button");
  462. element.textContent = i18n.blacklist_settings.button_edit;
  463. element.className = "setting iri-settings-button";
  464. element.addEventListener("click", this.textEditor.bind(this, "edit"));
  465.  
  466. element_list.push(element);
  467.  
  468. element = document.createElement("button");
  469. element.textContent = i18n.blacklist_settings.button_import;
  470. element.className = "setting iri-settings-button";
  471. element.addEventListener("click", this.textEditor.bind(this, "import"));
  472.  
  473. element_list.push(element);
  474.  
  475. element = document.createElement("button");
  476. element.textContent = i18n.blacklist_settings.button_export;
  477. element.className = "setting iri-settings-button";
  478. element.addEventListener("click", this.textEditor.bind(this, "export"));
  479.  
  480. element_list.push(element);
  481.  
  482. element = document.createElement("button");
  483. element.textContent = i18n.blacklist_settings.button_reset;
  484. element.className = "setting iri-settings-button danger";
  485. element.addEventListener("click", this.resetBlacklist.bind(this));
  486.  
  487. element_list.push(element);
  488.  
  489. return element_list;
  490.  
  491. },
  492. resetBlacklist: function () {
  493.  
  494. if (window.confirm(i18n.blacklist_settings.confirm_reset)) {
  495.  
  496. user_settings.blacklist_settings = [];
  497.  
  498. iridium_api.initializeSettings();
  499. iridium_api.saveSettings();
  500.  
  501. window.alert(i18n.blacklist_settings.reset_success);
  502.  
  503. }
  504.  
  505. },
  506. importBlacklist: function () {
  507.  
  508. var editor;
  509. var textarea;
  510.  
  511. if ((textarea = document.getElementById("iridium-textarea")) && window.confirm(i18n.blacklist_settings.confirm_import)) {
  512.  
  513. try {
  514.  
  515. user_settings.blacklist_settings = JSON.parse(textarea.value);
  516.  
  517. iridium_api.saveSettings();
  518.  
  519. window.alert(i18n.blacklist_settings.import_success);
  520.  
  521. if ((editor = document.getElementById("iridium-text-editor"))) {
  522.  
  523. editor.remove();
  524.  
  525. }
  526.  
  527. } catch (error) {
  528.  
  529. window.alert(i18n.blacklist_settings.import_error + error.name + ": " + error.message);
  530.  
  531. }
  532.  
  533. }
  534.  
  535. },
  536. closeEditor: function (editor) {
  537.  
  538. editor.remove();
  539.  
  540. },
  541. textEditor: function (type, event) {
  542.  
  543. var i;
  544. var obj;
  545. var temp;
  546. var editor;
  547. var button;
  548. var channel;
  549. var textarea;
  550. var temp_list;
  551. var blocked_list;
  552. var close_button;
  553. var channel_link;
  554. var buttons_section;
  555.  
  556. if (!(editor = document.getElementById("iridium-text-editor"))) {
  557.  
  558. editor = document.createElement("div");
  559. editor.id = "iridium-text-editor";
  560.  
  561. document.body.appendChild(editor);
  562.  
  563. } else {
  564.  
  565. editor.textContent = "";
  566.  
  567. }
  568.  
  569. buttons_section = document.createElement("div");
  570. buttons_section.id = "buttons-section";
  571.  
  572. editor.appendChild(buttons_section);
  573.  
  574. if (type === "import" || type === "export") {
  575.  
  576. textarea = document.createElement("textarea");
  577. textarea.id = "iridium-textarea";
  578. textarea.setAttribute("spellcheck", "false");
  579.  
  580. if (type === "import") {
  581.  
  582. textarea.setAttribute("placeholder", i18n.blacklist_settings.placeholder);
  583.  
  584. button = document.createElement("button");
  585. button.textContent = i18n.blacklist_settings.button_save;
  586. button.className = "iri-settings-button";
  587. button.addEventListener("click", this.importBlacklist.bind(this));
  588.  
  589. buttons_section.appendChild(button);
  590.  
  591. } else {
  592.  
  593. textarea.value = JSON.stringify(user_settings.blacklist_settings, null, 4);
  594.  
  595. }
  596.  
  597. editor.appendChild(textarea);
  598.  
  599. } else if (type === "edit") {
  600.  
  601. blocked_list = document.createElement("div");
  602. blocked_list.id = "iridium-blacklist";
  603.  
  604. temp = Object.keys(user_settings.blacklist_settings);
  605. temp_list = [];
  606.  
  607. for (i = 0; i < temp.length; i++) {
  608.  
  609. obj = {};
  610. obj[temp[i]] = user_settings.blacklist_settings[temp[i]];
  611.  
  612. temp_list.push([
  613. temp[i],
  614. user_settings.blacklist_settings[temp[i]]
  615. ]);
  616.  
  617. }
  618.  
  619. temp_list = temp_list.sort(function (previous, next) {
  620. return previous[1].localeCompare(next[1]);
  621. });
  622.  
  623. for (i = 0; i < temp_list.length; i++) {
  624.  
  625. channel = document.createElement("template");
  626. channel.innerHTML =
  627. "<div class='iri-blacklist-channel'>" +
  628. " <button class='close' title='" + i18n.blacklist_settings.button_remove + "'>" +
  629. " <svg viewBox='0 0 10 10' height='10' width='10'>" +
  630. " <polygon points='10 1.4 8.6 0 5 3.6 1.4 0 0 1.4 3.6 5 0 8.6 1.4 10 5 6.4 8.6 10 10 8.6 6.4 5'/>" +
  631. " </svg>" +
  632. " </button><a target='_blank'></a>" +
  633. "</div>";
  634. channel = channel.content;
  635. channel.firstChild.data = true;
  636.  
  637. channel_link = channel.querySelector("a");
  638. channel_link.href = "/channel/" + temp_list[i][0];
  639. channel_link.textContent = temp_list[i][1];
  640.  
  641. close_button = channel.querySelector(".close");
  642. close_button.container = channel.firstChild;
  643. close_button.ucid = temp_list[i][0];
  644. close_button.addEventListener("click", function (event) {
  645.  
  646. event.target.container.remove();
  647. delete user_settings.blacklist_settings[event.target.ucid];
  648.  
  649. iridium_api.saveSettings();
  650.  
  651. });
  652.  
  653. blocked_list.appendChild(channel);
  654.  
  655. }
  656.  
  657. editor.appendChild(blocked_list);
  658.  
  659. }
  660.  
  661. button = document.createElement("button");
  662. button.textContent = i18n.blacklist_settings.button_close;
  663. button.className = "iri-settings-button";
  664. button.addEventListener("click", this.closeEditor.bind(this, editor));
  665.  
  666. buttons_section.appendChild(button);
  667.  
  668. }
  669. }
  670. },
  671. tag_list: [
  672. "YTD-COMPACT-LINK-RENDERER",
  673. "YTD-COMPACT-PLAYLIST-RENDERER",
  674. "YTD-COMPACT-PROMOTED-VIDEO-RENDERER",
  675. "YTD-COMPACT-RADIO-RENDERER",
  676. "YTD-COMPACT-VIDEO-RENDERER",
  677. "YTD-GRID-CHANNEL-RENDERER",
  678. "YTD-GRID-MOVIE-PLAYLIST-RENDERER",
  679. "YTD-GRID-MOVIE-RENDERER",
  680. "YTD-GRID-PLAYLIST-RENDERER",
  681. "YTD-GRID-RADIO-RENDERER",
  682. "YTD-GRID-RENDERER",
  683. "YTD-GRID-SHOW-RENDERER",
  684. "YTD-GRID-VIDEO-RENDERER",
  685. "YTD-CHANNEL-RENDERER",
  686. "YTD-MOVIE-RENDERER",
  687. "YTD-PLAYLIST-RENDERER",
  688. "YTD-RADIO-RENDERER",
  689. "YTD-SHOW-RENDERER",
  690. "YTD-VIDEO-RENDERER"
  691. ],
  692. allowedBlacklistPage: function () {
  693.  
  694. return /^\/($|feed\/(?!subscriptions)|watch|results|shared)/.test(window.location.pathname);
  695.  
  696. },
  697. hasContainers: function () {
  698.  
  699. return window.location.pathname.match(/^\/(?:(?:|results)$|feed\/)/);
  700.  
  701. },
  702. getObjectByKey: function (obj, keys, match, list, pos) {
  703.  
  704. var i;
  705. var results;
  706. var property;
  707.  
  708. results = [];
  709.  
  710. for (property in obj) {
  711.  
  712. if (obj.hasOwnProperty(property) && obj[property] !== null) {
  713.  
  714. if (keys.indexOf(property) > -1 && (!match || typeof obj[property] !== "object" && match(obj[property]))) {
  715.  
  716. results.push({
  717. target: obj,
  718. property: property,
  719. list: list,
  720. pos: pos
  721. });
  722.  
  723. } else if (obj[property].constructor === Object) {
  724.  
  725. results = results.concat(this.getObjectByKey(obj[property], keys, match, list, pos));
  726.  
  727. } else if (obj[property].constructor === Array) {
  728.  
  729. for (i = 0; i < obj[property].length; i++) {
  730.  
  731. results = results.concat(this.getObjectByKey(obj[property][i], keys, match, obj[property], i));
  732.  
  733. }
  734.  
  735. }
  736.  
  737. }
  738.  
  739. }
  740.  
  741. return results;
  742.  
  743. },
  744. clearList: function (obj) {
  745.  
  746. var i;
  747. var ids;
  748. var videos;
  749. var shelves;
  750. var sections;
  751. var shelf_tag;
  752. var video_tag;
  753. var section_tag;
  754.  
  755. section_tag = [
  756. "itemSectionRenderer",
  757. "showingResultsForRenderer",
  758. "includingResultsForRenderer",
  759. ];
  760. shelf_tag = [
  761. "shelfRenderer",
  762. "compactAutoplayRenderer"
  763. ];
  764. video_tag = [
  765. "playlistRenderer",
  766. "channelRenderer",
  767. "radioRenderer",
  768. "showRenderer",
  769. "videoRenderer",
  770. "gridChannelRenderer",
  771. "gridMoviePlaylistRenderer",
  772. "gridMovieRenderer",
  773. "gridPlaylistRenderer",
  774. "gridRadioRenderer",
  775. "gridShowRenderer",
  776. "gridVideoRenderer",
  777. "compactVideoRenderer",
  778. "compactPlaylistRenderer",
  779. "compactPromotedVideoRenderer",
  780. "playlistPanelVideoRenderer"
  781. ];
  782.  
  783. videos = this.getObjectByKey(obj, video_tag);
  784.  
  785. for (i = 0; i < videos.length; i++) {
  786.  
  787. ids = this.getObjectByKey(videos[i].target, ["browseId"], function (string) {
  788. return string.indexOf("UC") === 0;
  789. });
  790.  
  791. if (ids[0] && user_settings.blacklist_settings[ids[0].target["browseId"]]) {
  792.  
  793. videos[i].list.splice(videos[i].list.indexOf(videos[i].target), 1);
  794.  
  795. }
  796.  
  797. }
  798.  
  799. shelves = this.getObjectByKey(obj, shelf_tag);
  800.  
  801. for (i = 0; i < shelves.length; i++) {
  802.  
  803. videos = this.getObjectByKey(shelves[i].target, video_tag);
  804.  
  805. if (videos.length === 0) {
  806.  
  807. shelves[i].list.splice(shelves[i].list.indexOf(shelves[i].target), 1);
  808.  
  809. }
  810.  
  811. }
  812.  
  813. if (this.hasContainers()) {
  814.  
  815. sections = this.getObjectByKey(obj, section_tag);
  816.  
  817. for (i = 0; i < sections.length; i++) {
  818.  
  819. if (sections[i].target[sections[i].property].contents.length === 0) {
  820.  
  821. sections[i].list.splice(sections[i].list.indexOf(sections[i].target), 1);
  822.  
  823. }
  824.  
  825. }
  826.  
  827. }
  828.  
  829. },
  830. checkParse: function (original) {
  831.  
  832. var context = this;
  833.  
  834. return function (text, reviver) {
  835.  
  836. var temp = original.apply(this, arguments);
  837.  
  838. if (context.allowedBlacklistPage()) {
  839.  
  840. context.clearList(temp);
  841.  
  842. }
  843.  
  844. return temp;
  845.  
  846. };
  847.  
  848. },
  849. getEmptyContainers: function () {
  850.  
  851. var i;
  852. var temp;
  853. var shelf;
  854. var container;
  855. var container_nodes;
  856.  
  857. container_nodes = "#contents ytd-item-section-renderer, #contents ytd-shelf-renderer";
  858. container = document.querySelectorAll(container_nodes);
  859.  
  860. for (i = 0; i < container.length; i++) {
  861.  
  862. shelf = container[i].querySelector("yt-horizontal-list-renderer");
  863.  
  864. if (shelf && (shelf.hasAttribute("at-start") || shelf.hasAttribute("at-end"))) {
  865.  
  866. shelf.fillRemainingListItems();
  867.  
  868. }
  869.  
  870. temp = container[i].querySelector(this.tag_list.join(","));
  871.  
  872. if (!temp) {
  873.  
  874. container[i].remove();
  875.  
  876. }
  877.  
  878. }
  879.  
  880. window.dispatchEvent(new Event("resize"));
  881.  
  882. },
  883. getContainers: function () {
  884.  
  885. var i;
  886. var ucid;
  887. var container;
  888. var container_nodes;
  889.  
  890. container_nodes = "#contents ytd-item-section-renderer, #contents ytd-shelf-renderer";
  891. container = document.querySelectorAll(container_nodes);
  892.  
  893. for (i = 0; i < container.length; i++) {
  894.  
  895. ucid = this.getObjectByKey(container[i].data, ["browseId"], function (string) {
  896. return string.indexOf("UC") === 0;
  897. });
  898.  
  899. if (ucid[0] && ucid.length === 1 && ucid[0].target.browseId) {
  900.  
  901. if (user_settings.blacklist_settings[ucid]) {
  902.  
  903. container[i].remove();
  904.  
  905. }
  906.  
  907. }
  908.  
  909. }
  910.  
  911. },
  912. getVideos: function () {
  913.  
  914. var i;
  915. var temp;
  916. var ucid;
  917. var child;
  918. var parent;
  919. var videos;
  920. var remove;
  921. var up_next;
  922.  
  923. remove = [];
  924. up_next = document.querySelector("ytd-compact-autoplay-renderer");
  925. videos = document.querySelectorAll(this.tag_list.join(","));
  926.  
  927. for (i = 0; i < videos.length; i++) {
  928.  
  929. if (videos[i].data) {
  930.  
  931. temp = videos[i];
  932.  
  933. }
  934.  
  935. if (temp && temp.data) {
  936.  
  937. ucid = this.getObjectByKey(temp.data, ["browseId"], function (string) {
  938. return string.indexOf("UC") === 0;
  939. });
  940.  
  941. if (ucid[0] && ucid[0].target.browseId) {
  942.  
  943. ucid = ucid[0].target.browseId;
  944.  
  945. }
  946.  
  947. }
  948.  
  949. if (ucid) {
  950.  
  951. if (user_settings.blacklist_settings[ucid]) {
  952.  
  953. if (up_next && up_next.contains(videos[i])) {
  954.  
  955. if (up_next.tagName === "YTD-COMPACT-AUTOPLAY-RENDERER") {
  956.  
  957. up_next.remove();
  958.  
  959. } else {
  960.  
  961. up_next.parentNode.remove();
  962. up_next = document.querySelector(".watch-sidebar-separation-line");
  963.  
  964. if (up_next) {
  965.  
  966. up_next.remove();
  967.  
  968. }
  969.  
  970. }
  971.  
  972. } else {
  973.  
  974. remove.push(videos[i]);
  975.  
  976. }
  977.  
  978. }
  979.  
  980. }
  981.  
  982. }
  983.  
  984. if (remove.length) {
  985.  
  986. for (i = 0; i < remove.length; i++) {
  987.  
  988. child = remove[i];
  989.  
  990. while (child) {
  991.  
  992. parent = child.parentNode;
  993.  
  994. if (parent.childElementCount > 1 || parent.id === "contents" || parent.id === "items") {
  995.  
  996. child.remove();
  997. break;
  998.  
  999. }
  1000.  
  1001. child = parent;
  1002.  
  1003. }
  1004.  
  1005. }
  1006.  
  1007. if (this.hasContainers()) {
  1008.  
  1009. // ignore.containers = [];
  1010.  
  1011. } else {
  1012.  
  1013. window.dispatchEvent(new Event("resize"));
  1014. }
  1015.  
  1016. }
  1017.  
  1018. },
  1019. modImportNode: function (original) {
  1020.  
  1021. var blacklist_button;
  1022.  
  1023. blacklist_button = document.createElement("div");
  1024. blacklist_button.className = "iri-add-to-blacklist";
  1025. blacklist_button.innerHTML =
  1026. "<svg viewBox='0 0 24 24' height='16' width='16'>" +
  1027. " <polygon points='24 2.1 21.9 0 12 9.9 2.1 0 0 2.1 9.9 12 0 21.9 2.1 24 12 14.1 21.9 24 24 21.9 14.1 12'/>" +
  1028. "</svg>" +
  1029. "<div class='iri-tooltip'>" + i18n.blacklist_settings.button_add_title + "</div>";
  1030.  
  1031. return function (externalNode, deep) {
  1032.  
  1033. var node;
  1034. var container;
  1035.  
  1036. node = externalNode.firstElementChild;
  1037.  
  1038. if (node && (node.id === "thumbnail" || node.id === "img")) {
  1039.  
  1040. container = node.id === "img" ? node.parentNode : node;
  1041.  
  1042. if (!container.querySelector(".iri-add-to-blacklist")) {
  1043.  
  1044. container.appendChild(blacklist_button.cloneNode(true));
  1045.  
  1046. }
  1047. }
  1048.  
  1049. return original.apply(this, arguments);
  1050.  
  1051. };
  1052.  
  1053. },
  1054. applyBlacklist: function () {
  1055.  
  1056. var hasContainers;
  1057.  
  1058. if (!this.allowedBlacklistPage()) {
  1059.  
  1060. return;
  1061.  
  1062. }
  1063.  
  1064. hasContainers = this.hasContainers();
  1065.  
  1066. if (hasContainers) {
  1067.  
  1068. this.getContainers();
  1069.  
  1070. }
  1071.  
  1072. this.getVideos();
  1073.  
  1074. if (hasContainers) {
  1075.  
  1076. this.getEmptyContainers();
  1077.  
  1078. }
  1079.  
  1080. },
  1081. addToBlacklist: function (event) {
  1082.  
  1083. var ucid;
  1084. var brand;
  1085. var parent;
  1086.  
  1087. if (user_settings.enable_blacklist && event.target.className === "iri-add-to-blacklist") {
  1088.  
  1089. event.preventDefault();
  1090. event.stopPropagation();
  1091.  
  1092. parent = event.target.parentNode;
  1093.  
  1094. while (parent) {
  1095.  
  1096. if (this.tag_list.indexOf(parent.tagName) > -1) {
  1097.  
  1098. if (parent.data) {
  1099.  
  1100. ucid = this.getObjectByKey(parent.data, ["browseId"], function (string) {
  1101. return string.indexOf("UC") === 0;
  1102. });
  1103.  
  1104. if (ucid[0] && ucid[0].target.browseId) {
  1105.  
  1106. brand = ucid[0].list[0].text;
  1107.  
  1108. ucid = ucid[0].target.browseId;
  1109.  
  1110. }
  1111.  
  1112. }
  1113.  
  1114. break;
  1115.  
  1116. }
  1117.  
  1118. parent = parent.parentNode;
  1119.  
  1120. }
  1121.  
  1122. if (ucid && brand) {
  1123.  
  1124. user_settings.blacklist_settings[ucid] = brand;
  1125.  
  1126. iridium_api.saveSettings();
  1127.  
  1128. this.applyBlacklist();
  1129.  
  1130. }
  1131.  
  1132. return false;
  1133.  
  1134. }
  1135.  
  1136. },
  1137. iniBlacklist: function () {
  1138.  
  1139. if (this.allowedBlacklistPage()) {
  1140.  
  1141. document.documentElement.classList.add("iri-blacklist-allowed");
  1142.  
  1143. } else {
  1144.  
  1145. document.documentElement.classList.remove("iri-blacklist-allowed");
  1146.  
  1147. }
  1148.  
  1149. },
  1150. ini: function () {
  1151.  
  1152. var context;
  1153.  
  1154. if (iridium_api.initializeOption.call(this)) {
  1155.  
  1156. return;
  1157.  
  1158. }
  1159.  
  1160. if (user_settings.enable_blacklist) {
  1161.  
  1162. context = this;
  1163.  
  1164. JSON.parse = this.checkParse(JSON.parse);
  1165. HTMLDocument.prototype.importNode = this.modImportNode(HTMLDocument.prototype.importNode);
  1166.  
  1167. document.addEventListener("readystatechange", this.iniBlacklist.bind(this), false);
  1168. document.addEventListener("yt-page-data-fetched", this.iniBlacklist.bind(this), false);
  1169. document.addEventListener("click", this.addToBlacklist.bind(this), true);
  1170.  
  1171. Object.defineProperty(Object.prototype, "ytInitialData", {
  1172. set: function (data) {
  1173. this._ytInitialData = data;
  1174. },
  1175. get: function () {
  1176.  
  1177. if (context.allowedBlacklistPage()) {
  1178.  
  1179. context.clearList(this._ytInitialData);
  1180.  
  1181. }
  1182.  
  1183. return this._ytInitialData;
  1184.  
  1185. }
  1186. });
  1187.  
  1188. }
  1189.  
  1190. }
  1191. },
  1192. {
  1193. options: {
  1194. channel_video_count: {
  1195. id: "channel_video_count",
  1196. section: "video",
  1197. sub_section: "general",
  1198. type: "checkbox",
  1199. value: true,
  1200. i18n: {
  1201. label: "Display uploaded videos number"
  1202. }
  1203. },
  1204. channel_video_time: {
  1205. id: "channel_video_time",
  1206. section: "video",
  1207. sub_section: "general",
  1208. type: "checkbox",
  1209. value: true,
  1210. i18n: {
  1211. label: "Display how long the video was uploaded"
  1212. }
  1213. }
  1214. },
  1215. removeVideoCount: function (listener) {
  1216.  
  1217. var xhr;
  1218. var video_count;
  1219. var video_count_dot;
  1220.  
  1221. delete this.addVideoCount.fetching;
  1222.  
  1223. document.removeEventListener("yt-navigate-finish", listener, false);
  1224.  
  1225. xhr = this.removeVideoCount.xhr;
  1226.  
  1227. if (xhr && xhr.abort) {
  1228.  
  1229. xhr.abort();
  1230.  
  1231. delete this.removeVideoCount.xhr;
  1232.  
  1233. }
  1234.  
  1235. if ((video_count_dot = document.querySelector("span.iri-video-count"))) {
  1236.  
  1237. video_count_dot.remove();
  1238.  
  1239. }
  1240.  
  1241. if ((video_count = document.getElementById("iri-video-count"))) {
  1242.  
  1243. video_count.remove();
  1244.  
  1245. }
  1246.  
  1247. },
  1248. addVideoCount: function (channel_url, event) {
  1249.  
  1250. var count_match;
  1251. var video_count;
  1252. var video_count_dot;
  1253. var owner_container;
  1254.  
  1255. delete this.addVideoCount.fetching;
  1256.  
  1257. count_match = event.target.response.match(/"(?:stats|briefStats)":\[{"runs":\[{"text":"([\w\W ]+?")}]}/);
  1258.  
  1259. if (count_match && (count_match = count_match[1].replace("\"", "")) && (owner_container = document.getElementById("owner-container"))) {
  1260.  
  1261. video_count_dot = document.createElement("span");
  1262. video_count_dot.textContent = " · ";
  1263. video_count_dot.className = "iri-video-count";
  1264.  
  1265. video_count = document.createElement("a");
  1266. video_count.id = "iri-video-count";
  1267. video_count.textContent = count_match;
  1268. video_count.className = "yt-simple-endpoint iri-video-count";
  1269. video_count.setAttribute("href", channel_url + "/videos");
  1270. video_count.data = {
  1271. webNavigationEndpointData: {
  1272. url: channel_url + "/videos"
  1273. }
  1274. };
  1275.  
  1276. owner_container.appendChild(video_count_dot);
  1277. owner_container.appendChild(video_count);
  1278.  
  1279. owner_container.channel_url = channel_url;
  1280. owner_container.video_count = count_match;
  1281.  
  1282. }
  1283.  
  1284. },
  1285. removeVideoTime: function (listener) {
  1286.  
  1287. var xhr;
  1288. var time_container;
  1289.  
  1290. delete this.addVideoTime.fetching;
  1291.  
  1292. document.removeEventListener("yt-navigate-finish", listener, false);
  1293.  
  1294. xhr = this.removeVideoTime.xhr;
  1295.  
  1296. if (xhr && xhr.abort) {
  1297.  
  1298. xhr.abort();
  1299.  
  1300. delete this.removeVideoTime.xhr;
  1301.  
  1302. }
  1303.  
  1304. if ((time_container = document.getElementById("iri-video-time"))) {
  1305.  
  1306. time_container.remove();
  1307.  
  1308. }
  1309.  
  1310. },
  1311. addVideoTime: function (published_date, event) {
  1312.  
  1313. var time_match;
  1314. var time_container;
  1315.  
  1316. delete this.addVideoTime.fetching;
  1317.  
  1318. time_match = event.target.response.match(/"publishedTimeText":{"simpleText":"([\w\W ]+?")}/);
  1319.  
  1320. if (time_match && (time_match = time_match[1].replace("\"", ""))) {
  1321.  
  1322. time_container = document.createElement("span");
  1323. time_container.id = "iri-video-time";
  1324. time_container.textContent = " · " + time_match;
  1325.  
  1326. published_date.appendChild(time_container);
  1327.  
  1328. }
  1329.  
  1330. },
  1331. loadStart: function () {
  1332.  
  1333. var xhr;
  1334. var context;
  1335. var video_id;
  1336. var channel_id;
  1337. var channel_url;
  1338. var upload_info;
  1339. var watch_page_active;
  1340.  
  1341. watch_page_active = document.querySelector("ytd-watch:not([hidden])");
  1342.  
  1343. if (watch_page_active && (channel_url = document.querySelector("#owner-name a"))) {
  1344.  
  1345. channel_url = channel_url.getAttribute("href");
  1346. channel_id = channel_url.match(/UC([a-z0-9-_]{22})/i);
  1347.  
  1348. if (channel_id && (channel_id = channel_id[1])) {
  1349.  
  1350. if (user_settings.channel_video_count && !this.addVideoCount.fetching && document.getElementById("owner-container") && !document.getElementById("iri-video-count") && (channel_url = document.querySelector("#owner-name a"))) {
  1351.  
  1352. if (this.removeVideoCount.xhr) {
  1353.  
  1354. this.removeVideoCount.xhr.abort();
  1355.  
  1356. }
  1357.  
  1358. this.addVideoCount.fetching = true;
  1359. channel_url = channel_url.getAttribute("href");
  1360.  
  1361. xhr = new XMLHttpRequest();
  1362. xhr.addEventListener("load", this.addVideoCount.bind(this, channel_url));
  1363. xhr.open("GET", "/playlist?list=UU" + channel_id, true);
  1364. xhr.send();
  1365.  
  1366. this.removeVideoCount.xhr = xhr;
  1367.  
  1368. context = this;
  1369.  
  1370. document.addEventListener("yt-navigate-finish", function listener() {
  1371.  
  1372. context.removeVideoCount(listener);
  1373.  
  1374. }, false);
  1375.  
  1376. }
  1377.  
  1378. if (user_settings.channel_video_time && !this.addVideoTime.fetching && (upload_info = document.querySelector("#upload-info .date")) && upload_info.textContent.indexOf("·") === -1) {
  1379.  
  1380. if ((video_id = window.location.href.match(/v=([\w-]+)/)) && (video_id = video_id[1])) {
  1381.  
  1382. if (this.removeVideoTime.xhr) {
  1383.  
  1384. this.removeVideoTime.xhr.abort();
  1385.  
  1386. }
  1387.  
  1388. this.addVideoTime.fetching = true;
  1389.  
  1390. xhr = new XMLHttpRequest();
  1391. xhr.addEventListener("load", this.addVideoTime.bind(this, upload_info));
  1392. xhr.open("GET", "/channel/UC" + channel_id + "/search?query=%22com%2Fwatch%3Fv%3D" + video_id + "%22", true);
  1393. xhr.send();
  1394.  
  1395. this.removeVideoTime.xhr = xhr;
  1396.  
  1397. context = this;
  1398.  
  1399. document.addEventListener("yt-navigate-finish", function listener() {
  1400.  
  1401. context.removeVideoTime(listener);
  1402.  
  1403. }, false);
  1404.  
  1405. }
  1406.  
  1407. }
  1408.  
  1409. }
  1410.  
  1411. }
  1412.  
  1413. },
  1414. ini: function () {
  1415.  
  1416. if (iridium_api.initializeOption.call(this)) {
  1417.  
  1418. return;
  1419.  
  1420. }
  1421.  
  1422. window.addEventListener("yt-page-data-updated", this.loadStart.bind(this), true);
  1423.  
  1424. }
  1425. },
  1426. {
  1427. options: {
  1428. player_quality: {
  1429. id: "player_quality",
  1430. section: "video",
  1431. sub_section: "player",
  1432. type: "dropdown",
  1433. value: "auto",
  1434. i18n: {
  1435. label: "Default video quality:",
  1436. options: [
  1437. "Auto",
  1438. "4320p (8k)",
  1439. "2880p (5k)",
  1440. "2160p (4k)",
  1441. "1440p",
  1442. "1080p",
  1443. "720p",
  1444. "480p",
  1445. "360p",
  1446. "240p",
  1447. "144p"
  1448. ]
  1449. },
  1450. options: [
  1451. "auto",
  1452. "highres",
  1453. "hd2880",
  1454. "hd2160",
  1455. "hd1440",
  1456. "hd1080",
  1457. "hd720",
  1458. "large",
  1459. "medium",
  1460. "small",
  1461. "tiny"
  1462. ]
  1463. },
  1464. player_auto_play: {
  1465. id: "player_auto_play",
  1466. section: "video",
  1467. sub_section: "player",
  1468. type: "checkbox",
  1469. value: false,
  1470. i18n: {
  1471. label: "Play videos automatically"
  1472. }
  1473. },
  1474. channel_trailer_auto_play: {
  1475. id: "channel_trailer_auto_play",
  1476. section: "video",
  1477. sub_section: "channel",
  1478. type: "checkbox",
  1479. value: false,
  1480. i18n: {
  1481. label: "Play channel trailers automatically"
  1482. }
  1483. },
  1484. player_annotations: {
  1485. id: "player_annotations",
  1486. section: "video",
  1487. sub_section: "player",
  1488. type: "checkbox",
  1489. value: false,
  1490. i18n: {
  1491. label: "Allow annotations on videos"
  1492. }
  1493. },
  1494. player_subtitles: {
  1495. id: "player_subtitles",
  1496. section: "video",
  1497. sub_section: "player",
  1498. type: "checkbox",
  1499. value: false,
  1500. i18n: {
  1501. label: "Allow subtitles on videos"
  1502. }
  1503. },
  1504. player_loudness: {
  1505. id: "player_loudness",
  1506. section: "video",
  1507. sub_section: "player",
  1508. type: "checkbox",
  1509. value: false,
  1510. i18n: {
  1511. label: "Allow loudness normalisation"
  1512. }
  1513. },
  1514. player_ads: {
  1515. id: "player_ads",
  1516. section: "video",
  1517. sub_section: "player",
  1518. type: "checkbox",
  1519. value: false,
  1520. i18n: {
  1521. label: "Allow ads on videos"
  1522. }
  1523. },
  1524. subscribed_channel_player_ads: {
  1525. id: "subscribed_channel_player_ads",
  1526. section: "video",
  1527. sub_section: "player",
  1528. type: "checkbox",
  1529. value: false,
  1530. i18n: {
  1531. label: "Allow ads only on videos of subscribed channels"
  1532. }
  1533. },
  1534. player_hfr: {
  1535. id: "player_hfr",
  1536. section: "video",
  1537. sub_section: "player",
  1538. type: "checkbox",
  1539. value: true,
  1540. i18n: {
  1541. label: "Allow HFR (60fps) streams"
  1542. }
  1543. },
  1544. player_memorize_size: {
  1545. id: "player_memorize_size",
  1546. section: "video",
  1547. sub_section: "player",
  1548. type: "checkbox",
  1549. value: true,
  1550. i18n: {
  1551. label: "Memorize player size"
  1552. }
  1553. },
  1554. player_memorize_volume: {
  1555. id: "player_memorize_volume",
  1556. section: "video",
  1557. sub_section: "player",
  1558. type: "checkbox",
  1559. value: true,
  1560. i18n: {
  1561. label: "Memorize player volume"
  1562. }
  1563. }
  1564. },
  1565. modArgs: function (args) {
  1566.  
  1567. var i;
  1568. var fps;
  1569. var list;
  1570. var key_type;
  1571.  
  1572. if (user_settings.subscribed_channel_player_ads ? args.subscribed !== "1" : !user_settings.player_ads) {
  1573.  
  1574. delete args.ad3_module;
  1575.  
  1576. }
  1577.  
  1578. if (!user_settings.player_annotations) {
  1579.  
  1580. args.iv_load_policy = "3";
  1581.  
  1582. }
  1583.  
  1584. if (user_settings.player_memorize_size) {
  1585.  
  1586. args.player_wide = user_settings.theaterMode ? "1" : "0";
  1587.  
  1588. }
  1589.  
  1590. if (!user_settings.player_loudness) {
  1591.  
  1592. args.loudness = null;
  1593. args.relative_loudness = null;
  1594.  
  1595. delete args.loudness;
  1596. delete args.relative_loudness;
  1597.  
  1598. }
  1599.  
  1600. if (!user_settings.player_subtitles) {
  1601.  
  1602. window.localStorage.setItem("yt-html5-player-modules::subtitlesModuleData::module-enabled", "false");
  1603.  
  1604. if (args.caption_audio_tracks) {
  1605.  
  1606. args.caption_audio_tracks = args.caption_audio_tracks.split(/&d=[0-9]|d=[0-9]&/).join("");
  1607.  
  1608. }
  1609.  
  1610. }
  1611.  
  1612. if (!user_settings.player_hfr && args.adaptive_fmts) {
  1613.  
  1614. key_type = args.adaptive_fmts.indexOf(",") > -1 ? "," : "%2C";
  1615. list = args.adaptive_fmts.split(key_type);
  1616.  
  1617. for (i = 0; i < list.length; i++) {
  1618.  
  1619. fps = list[i].split(/fps(?:=|%3D)([0-9]{2})/);
  1620. fps = fps && fps[1];
  1621.  
  1622. if (fps > 30) {
  1623.  
  1624. list.splice(i--, 1);
  1625.  
  1626. }
  1627.  
  1628. }
  1629.  
  1630. args.adaptive_fmts = list.join(key_type);
  1631.  
  1632. }
  1633.  
  1634. },
  1635. modVideoByPlayerVars: function (original) {
  1636.  
  1637. var context = this;
  1638.  
  1639. return function (args) {
  1640.  
  1641. var temp;
  1642. var current_config;
  1643.  
  1644.  
  1645. if (!this.getUpdatedConfigurationData) {
  1646.  
  1647. return original.apply(this, arguments);
  1648.  
  1649. }
  1650.  
  1651. current_config = this.getUpdatedConfigurationData();
  1652.  
  1653. if (current_config && current_config.args && (current_config.args.eventid === args.eventid || current_config.args.loaderUrl === args.loaderUrl) && !document.querySelector(".ended-mode")) {
  1654.  
  1655. return;
  1656.  
  1657. }
  1658.  
  1659. context.modArgs(args);
  1660.  
  1661. temp = original.apply(this, arguments);
  1662.  
  1663. if (user_settings.player_quality !== "auto") {
  1664.  
  1665. this.setPlaybackQuality(user_settings.player_quality);
  1666.  
  1667. }
  1668.  
  1669. return temp;
  1670.  
  1671. };
  1672.  
  1673. },
  1674. modPlayerLoad: function (original) {
  1675.  
  1676. var context = this;
  1677.  
  1678. return function (text, reviver) {
  1679.  
  1680. var temp;
  1681. var player;
  1682.  
  1683. context.modArgs(this.config.args);
  1684.  
  1685. temp = original.apply(this, arguments);
  1686.  
  1687. if (user_settings.player_quality !== "auto" && (player = document.getElementById("movie_player"))) {
  1688.  
  1689. player.setPlaybackQuality(user_settings.player_quality);
  1690.  
  1691. }
  1692.  
  1693. return temp;
  1694.  
  1695. };
  1696.  
  1697. },
  1698. modJSONParse: function (original) {
  1699.  
  1700. var context = this;
  1701.  
  1702. return function (text, reviver) {
  1703.  
  1704. var temp = original.apply(this, arguments);
  1705.  
  1706. if (temp && temp.player && temp.player.args) {
  1707.  
  1708. context.modArgs(temp.player.args);
  1709.  
  1710. }
  1711.  
  1712. return temp;
  1713.  
  1714. };
  1715.  
  1716. },
  1717. patchXHR: function (event) {
  1718.  
  1719. var i;
  1720. var temp;
  1721. var temp_list;
  1722. var key_value;
  1723. var player_api;
  1724.  
  1725. if (event.target.readyState === 4 && event.target.responseText.match(/eventid=/)) {
  1726.  
  1727. temp_list = {};
  1728. temp = event.target.responseText.split("&");
  1729.  
  1730. for (i = 0; i < temp.length; i++) {
  1731.  
  1732. key_value = temp[i].split("=");
  1733. temp_list[key_value[0]] = key_value[1] || "";
  1734.  
  1735. }
  1736.  
  1737. this.modArgs(temp_list);
  1738.  
  1739. Object.defineProperty(event.target, "responseText", {writable: true});
  1740.  
  1741. event.target.responseText = "";
  1742. temp = Object.keys(temp_list);
  1743.  
  1744. for (i = 0; i < temp.length; i++) {
  1745.  
  1746. event.target.responseText += temp[i] + "=" + temp_list[temp[i]];
  1747.  
  1748. if (i + 1 < temp.length) {
  1749.  
  1750. event.target.responseText += "&";
  1751.  
  1752. }
  1753.  
  1754. }
  1755.  
  1756. if (user_settings.player_quality !== "auto" && (player_api = document.getElementById("movie_player"))) {
  1757.  
  1758. player_api.setPlaybackQuality(user_settings.player_quality);
  1759.  
  1760. }
  1761.  
  1762. }
  1763.  
  1764. },
  1765. modOpen: function (original) {
  1766.  
  1767. var context = this;
  1768.  
  1769. return function (method, url) {
  1770.  
  1771. if (url.match("get_video_info")) {
  1772.  
  1773. this.addEventListener("readystatechange", context.patchXHR.bind(context));
  1774.  
  1775. }
  1776.  
  1777. return original.apply(this, arguments);
  1778.  
  1779. };
  1780.  
  1781. },
  1782. modParseFromString: function (original) {
  1783.  
  1784. return function () {
  1785.  
  1786. var i;
  1787. var fps;
  1788. var result;
  1789. var streams;
  1790.  
  1791. if (!user_settings.player_hfr) {
  1792.  
  1793. result = original.apply(this, arguments);
  1794. streams = result.getElementsByTagName("Representation");
  1795. i = streams.length;
  1796.  
  1797. while (i--) {
  1798.  
  1799. fps = streams[i].getAttribute("frameRate");
  1800.  
  1801. if (fps > 30) {
  1802.  
  1803. streams[i].remove();
  1804.  
  1805. }
  1806.  
  1807. }
  1808.  
  1809. return result;
  1810.  
  1811. }
  1812.  
  1813. return original.apply(this, arguments);
  1814.  
  1815. };
  1816.  
  1817. },
  1818. handleCustoms: function (event) {
  1819.  
  1820. if (typeof event === "object") {
  1821.  
  1822. user_settings.userVolume = event.volume;
  1823.  
  1824. } else {
  1825.  
  1826. user_settings.theaterMode = event;
  1827.  
  1828. }
  1829.  
  1830. iridium_api.saveSettings();
  1831.  
  1832. },
  1833. playerReady: function (api) {
  1834.  
  1835. var watch_page_api;
  1836.  
  1837. if (api) {
  1838.  
  1839. if (user_settings.player_memorize_size) {
  1840.  
  1841. api.addEventListener("SIZE_CLICKED", this.handleCustoms);
  1842.  
  1843. }
  1844.  
  1845. if (user_settings.player_memorize_volume) {
  1846.  
  1847. api.setVolume(user_settings.userVolume);
  1848. api.addEventListener("onVolumeChange", this.handleCustoms);
  1849.  
  1850. }
  1851.  
  1852. if (user_settings.player_memorize_size && (watch_page_api = document.querySelector("ytd-watch"))) {
  1853.  
  1854. watch_page_api.playerApiReady_(api);
  1855. watch_page_api.theaterModeChanged_(user_settings.theaterMode);
  1856.  
  1857. }
  1858.  
  1859. }
  1860.  
  1861. },
  1862. shareApi: function (original) {
  1863.  
  1864. var context = this;
  1865.  
  1866. return function (api) {
  1867.  
  1868. context.playerReady(api);
  1869.  
  1870. if (original) {
  1871.  
  1872. return original.apply(this, arguments);
  1873.  
  1874. }
  1875.  
  1876. };
  1877. },
  1878. isChannel: function () {
  1879.  
  1880. return /^\/(user|channel)\//.test(window.location.pathname);
  1881.  
  1882. },
  1883. ini: function () {
  1884.  
  1885. var context;
  1886.  
  1887. if (iridium_api.initializeOption.call(this)) {
  1888.  
  1889. return;
  1890.  
  1891. }
  1892.  
  1893. context = this;
  1894.  
  1895. JSON.parse = this.modJSONParse(JSON.parse);
  1896. XMLHttpRequest.prototype.open = this.modOpen(XMLHttpRequest.prototype.open);
  1897. DOMParser.prototype.parseFromString = this.modParseFromString(DOMParser.prototype.parseFromString);
  1898. window.onYouTubePlayerReady = this.shareApi(window.onYouTubePlayerReady);
  1899.  
  1900. Object.defineProperties(Object.prototype, {
  1901. cueVideoByPlayerVars: {
  1902. set: function (data) {
  1903. this._cueVideoByPlayerVars = data;
  1904. },
  1905. get: function () {
  1906. return context.modVideoByPlayerVars(this._cueVideoByPlayerVars);
  1907. }
  1908. },
  1909. loadVideoByPlayerVars: {
  1910. set: function (data) {
  1911. this._loadVideoByPlayerVars = data;
  1912. },
  1913. get: function () {
  1914.  
  1915. if (context.isChannel() ? !user_settings.channel_trailer_auto_play : !user_settings.player_auto_play) {
  1916.  
  1917. return this.cueVideoByPlayerVars;
  1918.  
  1919. }
  1920.  
  1921. return context.modVideoByPlayerVars(this._loadVideoByPlayerVars);
  1922.  
  1923. }
  1924. },
  1925. TIMING_AFT_KEYS: {
  1926. set: function (data) {
  1927. this._TIMING_AFT_KEYS = data;
  1928. },
  1929. get: function () {
  1930.  
  1931. var key;
  1932.  
  1933. if (context.isChannel() ? !user_settings.channel_trailer_auto_play : !user_settings.player_auto_play) {
  1934.  
  1935. if (window.ytcsi && window.ytcsi.data_ && window.ytcsi.data_.tick) {
  1936.  
  1937. for (key in window.ytcsi.data_.tick) {
  1938.  
  1939. if (window.ytcsi.data_.tick.hasOwnProperty(key)) {
  1940.  
  1941. return [key];
  1942.  
  1943. }
  1944.  
  1945. }
  1946.  
  1947. } else {
  1948.  
  1949. return ["srt"];
  1950.  
  1951. }
  1952.  
  1953. }
  1954.  
  1955. return this._TIMING_AFT_KEYS;
  1956.  
  1957. }
  1958. },
  1959. loaded: {
  1960. set: function (data) {
  1961. this._loaded = data;
  1962. },
  1963. get: function () {
  1964.  
  1965. if (this.args && (context.isChannel() ? !user_settings.channel_trailer_auto_play : !user_settings.player_auto_play)) {
  1966.  
  1967. return false;
  1968.  
  1969. }
  1970.  
  1971. return this._loaded;
  1972.  
  1973. }
  1974.  
  1975. },
  1976. load: {
  1977. set: function (data) {
  1978. this._load = data;
  1979. },
  1980. get: function () {
  1981.  
  1982. var temp = this._load && this._load.toString();
  1983.  
  1984. if (temp && temp.match("Application.create")) {
  1985.  
  1986. return context.modPlayerLoad(this._load);
  1987.  
  1988. }
  1989.  
  1990. return this._load;
  1991.  
  1992. }
  1993.  
  1994. },
  1995. autoplay: {
  1996. set: function (data) {
  1997. this._autoplay = data;
  1998. },
  1999. get: function () {
  2000.  
  2001. if (this.ucid && this._autoplay === "1" && (context.isChannel() ? !user_settings.channel_trailer_auto_play : !user_settings.player_auto_play)) {
  2002.  
  2003. return "0";
  2004.  
  2005. }
  2006.  
  2007. return this._autoplay;
  2008.  
  2009. }
  2010.  
  2011. },
  2012. fflags: {
  2013. set: function (data) {
  2014. this._fflags = data;
  2015. },
  2016. get: function () {
  2017.  
  2018. if (this.ucid && (!this.autoplay || this.autoplay === "1") && (context.isChannel() ? !user_settings.channel_trailer_auto_play : !user_settings.player_auto_play)) {
  2019.  
  2020. if (this._fflags && this._fflags.replace) {
  2021.  
  2022. return this._fflags
  2023. .replace(
  2024. "legacy_autoplay_flag=true",
  2025. "legacy_autoplay_flag=false"
  2026. ).replace(
  2027. "disable_new_pause_state3=true",
  2028. "disable_new_pause_state3=false"
  2029. );
  2030.  
  2031. }
  2032.  
  2033. }
  2034.  
  2035. return this._fflags;
  2036.  
  2037. }
  2038.  
  2039. }
  2040. });
  2041.  
  2042. }
  2043. },
  2044. {
  2045. options: {
  2046. shortcuts_always_active: {
  2047. id: "shortcuts_always_active",
  2048. section: "video",
  2049. sub_section: "player",
  2050. type: "checkbox",
  2051. value: true,
  2052. i18n: {
  2053. label: "Player shortcuts always active"
  2054. }
  2055. }
  2056. },
  2057. alwaysActive: function (event) {
  2058.  
  2059. var i;
  2060. var api;
  2061. var list;
  2062. var clear;
  2063. var length;
  2064. var event_clone;
  2065.  
  2066. if (user_settings.shortcuts_always_active && (api = document.getElementById("movie_player"))) {
  2067.  
  2068. clear = window.location.pathname === "/watch" && api && api !== event.target && !api.contains(event.target);
  2069.  
  2070. clear = clear && !event.ctrlKey && !event.shiftKey && !event.altKey && !event.metaKey && !event.target.isContentEditable;
  2071.  
  2072. clear = clear && (event.which > 47 && event.which < 58 || event.which > 95 && event.which < 106 || [27, 32, 35, 36, 37, 38, 39, 40, 66, 67, 79, 87, 187, 189].indexOf(event.which) > -1);
  2073.  
  2074. if (clear && ["EMBED", "INPUT", "OBJECT", "TEXTAREA", "IFRAME"].indexOf(document.activeElement.tagName) === -1) {
  2075.  
  2076. event_clone = new Event("keydown");
  2077. list = Object.keys(Object.getPrototypeOf(event));
  2078. length = list.length;
  2079.  
  2080. for (i = 0; i < length; i++) {
  2081.  
  2082. event_clone[list[i]] = event[list[i]];
  2083.  
  2084. }
  2085.  
  2086. event.preventDefault();
  2087. api.dispatchEvent(event_clone);
  2088.  
  2089. }
  2090.  
  2091. }
  2092.  
  2093. },
  2094. ini: function () {
  2095.  
  2096. if (iridium_api.initializeOption.call(this)) {
  2097.  
  2098. return;
  2099.  
  2100. }
  2101.  
  2102. document.addEventListener("keydown", this.alwaysActive.bind(this));
  2103.  
  2104. }
  2105. },
  2106. {
  2107. options: {
  2108. player_volume_wheel: {
  2109. id: "player_volume_wheel",
  2110. section: "video",
  2111. sub_section: "player",
  2112. type: "checkbox",
  2113. value: false,
  2114. i18n: {
  2115. label: "Change volume using the mouse wheel"
  2116. }
  2117. }
  2118. },
  2119. changeVolume: function (event) {
  2120.  
  2121. var api;
  2122. var player;
  2123. var direction;
  2124. var timestamp;
  2125. var can_scroll;
  2126. var new_volume;
  2127. var player_state;
  2128. var chrome_bottom;
  2129. var invideo_drawer;
  2130. var player_settings;
  2131. var fullscreen_playlist;
  2132.  
  2133. api = document.getElementById("movie_player");
  2134. player = document.querySelector("video");
  2135. invideo_drawer = document.querySelector(".iv-drawer");
  2136. player_settings = document.querySelector(".ytp-settings-menu");
  2137. fullscreen_playlist = document.querySelector(".ytp-playlist-menu");
  2138. can_scroll = (!fullscreen_playlist || !fullscreen_playlist.contains(event.target)) && (!invideo_drawer || !invideo_drawer.contains(event.target)) && (!player_settings || !player_settings.contains(event.target));
  2139.  
  2140. if (can_scroll && player && api && api.contains(event.target)) {
  2141.  
  2142. player_state = api.getPlayerState();
  2143.  
  2144. if (player_state > 0 && player_state < 5) {
  2145.  
  2146. event.preventDefault();
  2147. chrome_bottom = document.querySelector(".ytp-chrome-bottom");
  2148.  
  2149. if (chrome_bottom) {
  2150.  
  2151. if (!chrome_bottom.classList.contains("ytp-volume-slider-active")) {
  2152.  
  2153. chrome_bottom.classList.add("ytp-volume-slider-active");
  2154.  
  2155. }
  2156.  
  2157. if (chrome_bottom.timer) {
  2158.  
  2159. window.clearTimeout(chrome_bottom.timer);
  2160.  
  2161. }
  2162.  
  2163. api.dispatchEvent(new Event("mousemove"));
  2164.  
  2165. chrome_bottom.timer = window.setTimeout(function () {
  2166.  
  2167. if (chrome_bottom && chrome_bottom.classList.contains("ytp-volume-slider-active")) {
  2168.  
  2169. chrome_bottom.classList.remove("ytp-volume-slider-active");
  2170. delete chrome_bottom.timer;
  2171.  
  2172. }
  2173.  
  2174. }, 4000);
  2175.  
  2176. }
  2177.  
  2178. direction = event.deltaY || event.wheelDeltaY;
  2179. new_volume = api.getVolume() - (Math.sign(direction) * 5);
  2180.  
  2181. if (new_volume < 0) {
  2182.  
  2183. new_volume = 0;
  2184.  
  2185. } else if (new_volume > 100) {
  2186.  
  2187. new_volume = 100;
  2188.  
  2189. }
  2190.  
  2191. api.setVolume(new_volume);
  2192.  
  2193. timestamp = Date.now();
  2194.  
  2195. window.localStorage.setItem(
  2196. "yt-player-volume",
  2197. JSON.stringify({
  2198. data: JSON.stringify({
  2199. volume: new_volume,
  2200. muted: false
  2201. }),
  2202. creation: timestamp,
  2203. expiration: timestamp + 2592E6
  2204. })
  2205. );
  2206.  
  2207. return false;
  2208.  
  2209. }
  2210.  
  2211. }
  2212.  
  2213. },
  2214. ini: function () {
  2215.  
  2216. if (iridium_api.initializeOption.call(this)) {
  2217.  
  2218. return;
  2219.  
  2220. }
  2221.  
  2222. if (user_settings.player_volume_wheel) {
  2223.  
  2224. document.addEventListener("wheel", this.changeVolume.bind(this));
  2225.  
  2226. }
  2227.  
  2228. }
  2229. },
  2230. {
  2231. options: {
  2232. player_always_visible: {
  2233. id: "player_always_visible",
  2234. section: "video",
  2235. sub_section: "player",
  2236. type: "checkbox",
  2237. value: true,
  2238. i18n: {
  2239. label: "Video stays always visible while scrolling"
  2240. }
  2241. },
  2242. player_always_playing: {
  2243. id: "player_always_playing",
  2244. section: "video",
  2245. sub_section: "player",
  2246. type: "checkbox",
  2247. value: true,
  2248. i18n: {
  2249. label: "Video keeps playing when changing pages",
  2250. button_restore: "Restore",
  2251. button_close: "Close"
  2252. }
  2253. }
  2254. },
  2255. endMiniPlayer: function (class_name) {
  2256.  
  2257. var player_api;
  2258. var is_in_theater_mode;
  2259.  
  2260. document.documentElement.classList.remove(class_name);
  2261.  
  2262. if ((player_api = document.getElementById("movie_player"))) {
  2263.  
  2264. is_in_theater_mode = document.querySelector("ytd-watch[theater]");
  2265.  
  2266. player_api.setSizeStyle(true, is_in_theater_mode);
  2267.  
  2268. }
  2269.  
  2270. },
  2271. iniMiniPlayer: function (class_name) {
  2272.  
  2273. var player_api;
  2274.  
  2275. document.documentElement.classList.add(class_name);
  2276.  
  2277. if ((player_api = document.getElementById("movie_player"))) {
  2278.  
  2279. player_api.setSizeStyle(false, true);
  2280.  
  2281. this.iniMiniPlayerControls(player_api);
  2282.  
  2283. }
  2284.  
  2285. },
  2286. iniAlwaysVisible: function (event) {
  2287.  
  2288. var player;
  2289. var player_bounds;
  2290. var is_out_of_sight;
  2291. var player_container;
  2292. var is_already_floating;
  2293.  
  2294. if (user_settings.player_always_visible) {
  2295.  
  2296. is_already_floating = document.documentElement.classList.contains("iri-always-visible");
  2297.  
  2298. if (event.detail && event.detail.pageType !== "watch" && is_already_floating) {
  2299.  
  2300. this.endMiniPlayer("iri-always-visible");
  2301.  
  2302. } else if (window.location.pathname === "/watch") {
  2303.  
  2304. if ((player_container = document.getElementById("player-container")) && (player_bounds = player_container.getBoundingClientRect())) {
  2305.  
  2306. is_out_of_sight = player_bounds.bottom < ((player_bounds.height / 2) + 50);
  2307.  
  2308. if (is_out_of_sight && !is_already_floating) {
  2309.  
  2310. this.iniMiniPlayer("iri-always-visible");
  2311.  
  2312. } else if (!is_out_of_sight && is_already_floating) {
  2313.  
  2314. this.endMiniPlayer("iri-always-visible");
  2315.  
  2316. }
  2317.  
  2318. }
  2319.  
  2320. }
  2321.  
  2322. }
  2323.  
  2324. },
  2325. iniAlwaysPlaying: function (event) {
  2326.  
  2327. if (user_settings.player_always_playing) {
  2328.  
  2329. if (event.detail && event.detail.pageType === "watch") {
  2330.  
  2331. this.endMiniPlayer("iri-always-playing");
  2332.  
  2333. } else if (!document.querySelector(".ended-mode")) {
  2334.  
  2335. this.iniMiniPlayer("iri-always-playing");
  2336.  
  2337. }
  2338.  
  2339. }
  2340.  
  2341. },
  2342. restorePlayer: function () {
  2343.  
  2344. var player_api;
  2345. var current_data;
  2346. var original_url;
  2347. var history_state;
  2348. var watch_page_api;
  2349. var page_manager_api;
  2350.  
  2351. if ((watch_page_api = document.querySelector("ytd-watch"))) {
  2352.  
  2353. if ((player_api = document.getElementById("movie_player"))) {
  2354.  
  2355. current_data = player_api.getUpdatedConfigurationData();
  2356. original_url = current_data.args.loaderUrl.replace(window.location.origin, "");
  2357. document.title = current_data.args.title + " - YouTube";
  2358.  
  2359. history_state = {
  2360. endpoint: {
  2361. clickTrackingParams: "",
  2362. watchEndpoint: {
  2363. videoId: current_data.args.video_id
  2364. },
  2365. webNavigationEndpointData: {
  2366. url: original_url,
  2367. webPageType: "WATCH"
  2368. }
  2369. },
  2370. entryTime: window.performance.now(),
  2371. savedComponentState: null
  2372. };
  2373.  
  2374. window.history.pushState(history_state, document.title, original_url);
  2375.  
  2376. }
  2377.  
  2378. this.endMiniPlayer("iri-always-playing");
  2379.  
  2380. if ((page_manager_api = document.querySelector("ytd-page-manager"))) {
  2381.  
  2382. page_manager_api.setActivePage_(watch_page_api);
  2383.  
  2384. }
  2385.  
  2386. watch_page_api.initComments_();
  2387.  
  2388. document.dispatchEvent(new Event("yt-page-data-fetched"));
  2389. document.dispatchEvent(new Event("yt-page-data-updated"));
  2390.  
  2391. }
  2392.  
  2393. },
  2394. closePlayer: function () {
  2395.  
  2396. var player_api;
  2397.  
  2398. this.endMiniPlayer("iri-always-playing");
  2399.  
  2400. if ((player_api = document.getElementById("movie_player"))) {
  2401.  
  2402. player_api.stopVideo(true);
  2403.  
  2404. }
  2405.  
  2406. },
  2407. iniMiniPlayerControls: function (player_api) {
  2408.  
  2409. var restore_page;
  2410. var close_mini_player;
  2411. var mini_player_controls;
  2412.  
  2413. if (!(mini_player_controls = document.getElementById("iri-mini-player-controls")) && player_api) {
  2414.  
  2415. mini_player_controls = document.createElement("div");
  2416. mini_player_controls.id = "iri-mini-player-controls";
  2417.  
  2418. restore_page = document.createElement("div");
  2419. restore_page.id = "iri-mini-player-restore";
  2420. restore_page.className = "iri-mini-player-control iri-mini-player-left-control";
  2421. restore_page.innerHTML =
  2422. "<svg height='24' width='24' fill='#FFF'>" +
  2423. " <use xlink:href='#iri-svg-restore' class='iri-svg-shadow'/>" +
  2424. " <path id='iri-svg-restore' d='M21 4H1v16h22V4h-2zm0 14H3v-6h10V6h8v12z'/>" +
  2425. "</svg>" +
  2426. "<div class='iri-mini-player-tooltip'>" + i18n.player_always_playing.button_restore + "</div>";
  2427. restore_page.addEventListener("click", this.restorePlayer.bind(this), false);
  2428.  
  2429. close_mini_player = document.createElement("div");
  2430. close_mini_player.id = "iri-mini-player-close";
  2431. close_mini_player.className = "iri-mini-player-control iri-mini-player-right-control";
  2432. close_mini_player.innerHTML =
  2433. "<svg height='24' width='24' fill='#FFF'>" +
  2434. " <use xlink:href='#iri-svg-close' class='iri-svg-shadow'/>" +
  2435. " <path id='iri-svg-close' d='M19 6.41L17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12z'/>" +
  2436. "</svg>" +
  2437. "<div class='iri-mini-player-tooltip'>" + i18n.player_always_playing.button_close + "</div>";
  2438. close_mini_player.addEventListener("click", this.closePlayer.bind(this), false);
  2439.  
  2440. mini_player_controls.appendChild(restore_page);
  2441. mini_player_controls.appendChild(close_mini_player);
  2442.  
  2443. player_api.appendChild(mini_player_controls);
  2444.  
  2445. }
  2446.  
  2447. },
  2448. modStopVideo: function (original) {
  2449.  
  2450. return function (bypass) {
  2451.  
  2452. if (user_settings.player_always_playing && !bypass) {
  2453.  
  2454. return;
  2455.  
  2456. }
  2457.  
  2458. return original.apply(this, arguments);
  2459.  
  2460. };
  2461.  
  2462. },
  2463. ini: function () {
  2464.  
  2465. var context;
  2466.  
  2467. if (iridium_api.initializeOption.call(this)) {
  2468.  
  2469. return;
  2470.  
  2471. }
  2472.  
  2473. window.addEventListener("scroll", this.iniAlwaysVisible.bind(this), false);
  2474. window.addEventListener("yt-navigate-start", this.iniAlwaysVisible.bind(this), false);
  2475. window.addEventListener("yt-navigate-finish", this.iniAlwaysVisible.bind(this), false);
  2476. window.addEventListener("yt-navigate-start", this.iniAlwaysPlaying.bind(this), false);
  2477. window.addEventListener("yt-navigate-finish", this.iniAlwaysPlaying.bind(this), false);
  2478.  
  2479. context = this;
  2480.  
  2481. Object.defineProperty(Object.prototype, "stopVideo", {
  2482. set: function (data) {
  2483. this._stopVideo = data;
  2484. },
  2485. get: function () {
  2486. return context.modStopVideo(this._stopVideo);
  2487. }
  2488. })
  2489.  
  2490. }
  2491. },
  2492. {
  2493. options: {
  2494. iridium_dark_mode: {
  2495. id: "iridium_dark_mode",
  2496. section: "settings",
  2497. sub_section: "settings",
  2498. type: "checkbox",
  2499. value: false,
  2500. i18n: {
  2501. label: "Use dark theme"
  2502. },
  2503. callback: function () {
  2504.  
  2505. if (user_settings.iridium_dark_mode) {
  2506.  
  2507. document.documentElement.classList.add("iri-dark-mode-settings");
  2508.  
  2509. } else {
  2510.  
  2511. document.documentElement.classList.remove("iri-dark-mode-settings");
  2512.  
  2513. }
  2514.  
  2515. }
  2516. }
  2517. }
  2518. },
  2519. {
  2520. options: {
  2521. iridium_user_settings: {
  2522. id: "iridium_user_settings",
  2523. section: "settings",
  2524. sub_section: "settings",
  2525. type: "custom",
  2526. i18n: {
  2527. button_save: "Save",
  2528. button_close: "Close",
  2529. button_export: "Export",
  2530. button_import: "Import",
  2531. button_reset: "Reset",
  2532. placeholder: "Paste your new settings here",
  2533. confirm_reset: "You are about to reset your settings. It is advised to backup your current settings before continuing.\n\nDo you wish to contiue?\n\n",
  2534. reset_success: "Settings have been reset.\n\nChanges will be applied after a page refresh.\n\n",
  2535. confirm_import: "You are about to override your current settings. It is advised to backup your current settings before continuing.\n\nDo you wish to contiue?\n\n",
  2536. import_success: "Your settings have been imported with success.\n\nChanges will be applied after a page refresh.\n\n",
  2537. import_error: "Your settings could not be imported because they appear to be invalid.\n\n"
  2538. },
  2539. custom: function () {
  2540.  
  2541. var element;
  2542. var element_list;
  2543.  
  2544. element_list = [];
  2545.  
  2546. element = document.createElement("button");
  2547. element.textContent = i18n.iridium_user_settings.button_export;
  2548. element.className = "setting iri-settings-button";
  2549. element.addEventListener("click", this.textEditor.bind(this, "export"));
  2550.  
  2551. element_list.push(element);
  2552.  
  2553. element = document.createElement("button");
  2554. element.textContent = i18n.iridium_user_settings.button_import;
  2555. element.className = "setting iri-settings-button";
  2556. element.addEventListener("click", this.textEditor.bind(this, "import"));
  2557.  
  2558. element_list.push(element);
  2559.  
  2560. element = document.createElement("button");
  2561. element.textContent = i18n.iridium_user_settings.button_reset;
  2562. element.className = "setting iri-settings-button danger";
  2563. element.addEventListener("click", this.resetSettings.bind(this));
  2564.  
  2565. element_list.push(element);
  2566.  
  2567. return element_list;
  2568.  
  2569. },
  2570. resetSettings: function () {
  2571.  
  2572. if (window.confirm(i18n.iridium_user_settings.confirm_reset)) {
  2573.  
  2574. user_settings = null;
  2575.  
  2576. iridium_api.initializeSettings();
  2577. iridium_api.saveSettings();
  2578.  
  2579. window.alert(i18n.iridium_user_settings.reset_success);
  2580.  
  2581. }
  2582.  
  2583. },
  2584. importSettings: function () {
  2585.  
  2586. var editor;
  2587. var textarea;
  2588.  
  2589. if ((textarea = document.getElementById("iridium-textarea")) && window.confirm(i18n.iridium_user_settings.confirm_import)) {
  2590.  
  2591. try {
  2592.  
  2593. user_settings = JSON.parse(textarea.value);
  2594.  
  2595. iridium_api.saveSettings();
  2596.  
  2597. window.alert(i18n.iridium_user_settings.import_success);
  2598.  
  2599. if ((editor = document.getElementById("iridium-text-editor"))) {
  2600.  
  2601. editor.remove();
  2602.  
  2603. }
  2604.  
  2605. } catch (error) {
  2606.  
  2607. window.alert(i18n.iridium_user_settings.import_error + error.name + ": " + error.message);
  2608.  
  2609. }
  2610.  
  2611. }
  2612.  
  2613. },
  2614. closeEditor: function (editor) {
  2615.  
  2616. editor.remove();
  2617.  
  2618. },
  2619. textEditor: function (type, event) {
  2620.  
  2621. var editor;
  2622. var button;
  2623. var textarea;
  2624. var buttons_section;
  2625.  
  2626. if (!(editor = document.getElementById("iridium-text-editor"))) {
  2627.  
  2628. editor = document.createElement("div");
  2629. editor.id = "iridium-text-editor";
  2630.  
  2631. document.body.appendChild(editor);
  2632.  
  2633. } else {
  2634.  
  2635. editor.textContent = "";
  2636.  
  2637. }
  2638.  
  2639. buttons_section = document.createElement("div");
  2640. buttons_section.id = "buttons-section";
  2641. textarea = document.createElement("textarea");
  2642. textarea.id = "iridium-textarea";
  2643. textarea.setAttribute("spellcheck", "false");
  2644.  
  2645. if (type === "import") {
  2646.  
  2647. textarea.setAttribute("placeholder", i18n.iridium_user_settings.placeholder);
  2648.  
  2649. button = document.createElement("button");
  2650. button.textContent = i18n.iridium_user_settings.button_save;
  2651. button.className = "iri-settings-button";
  2652. button.addEventListener("click", this.importSettings.bind(this));
  2653.  
  2654. buttons_section.appendChild(button);
  2655.  
  2656. }
  2657.  
  2658. button = document.createElement("button");
  2659. button.textContent = i18n.iridium_user_settings.button_close;
  2660. button.className = "iri-settings-button";
  2661. button.addEventListener("click", this.closeEditor.bind(this, editor));
  2662.  
  2663. buttons_section.appendChild(button);
  2664.  
  2665. if (type === "export") {
  2666.  
  2667. textarea.value = JSON.stringify(user_settings, null, 4);
  2668.  
  2669. }
  2670.  
  2671. editor.appendChild(buttons_section);
  2672. editor.appendChild(textarea);
  2673.  
  2674. }
  2675. },
  2676. iridium_language: {
  2677. id: "iridium_language",
  2678. section: "settings",
  2679. sub_section: "language",
  2680. type: "custom",
  2681. i18n: {
  2682. button_save: "Save",
  2683. button_close: "Close",
  2684. confirm_save: "You are about to replace your extension language settings.\n\nDo you wish to continue?\n\n",
  2685. save_success: "New language saved successfully.\n\nChanges will be applied after a page refresh.\n\n",
  2686. save_error: "The new language could not be saved because it appears to be invalid.\n\n"
  2687. },
  2688. custom: function () {
  2689.  
  2690. var element;
  2691. var element_list;
  2692.  
  2693. element_list = [];
  2694.  
  2695. element = document.createElement("button");
  2696. element.textContent = i18n.language;
  2697. element.className = "setting iri-settings-button";
  2698. element.addEventListener("click", this.textEditor.bind(this));
  2699.  
  2700. element_list.push(element);
  2701.  
  2702. return element_list;
  2703.  
  2704. },
  2705. closeEditor: function (editor) {
  2706.  
  2707. editor.remove();
  2708.  
  2709. },
  2710. saveLanguage: function (textarea) {
  2711.  
  2712. var editor;
  2713.  
  2714. if ((textarea = document.getElementById("iridium-textarea")) && window.confirm(i18n.iridium_language.confirm_save)) {
  2715.  
  2716. try {
  2717.  
  2718. user_settings.custom_language = JSON.parse(textarea.value);
  2719.  
  2720. iridium_api.setCustomLanguage(user_settings.custom_language);
  2721. iridium_api.saveSettings();
  2722.  
  2723. window.alert(i18n.iridium_language.save_success);
  2724.  
  2725. if ((editor = document.getElementById("iridium-text-editor"))) {
  2726.  
  2727. editor.remove();
  2728.  
  2729. }
  2730.  
  2731. } catch (error) {
  2732.  
  2733. window.alert(i18n.iridium_language.save_error + error.name + ": " + error.message);
  2734.  
  2735. }
  2736. }
  2737.  
  2738. },
  2739. textEditor: function (event) {
  2740.  
  2741. var editor;
  2742. var button;
  2743. var textarea;
  2744. var buttons_section;
  2745.  
  2746. if (!(editor = document.getElementById("iridium-text-editor"))) {
  2747.  
  2748. editor = document.createElement("div");
  2749. editor.id = "iridium-text-editor";
  2750.  
  2751. document.body.appendChild(editor);
  2752.  
  2753. } else {
  2754.  
  2755. editor.textContent = "";
  2756.  
  2757. }
  2758.  
  2759. buttons_section = document.createElement("div");
  2760. buttons_section.id = "buttons-section";
  2761.  
  2762. button = document.createElement("button");
  2763. button.textContent = i18n.iridium_language.button_save;
  2764. button.className = "iri-settings-button";
  2765. button.addEventListener("click", this.saveLanguage.bind(this));
  2766.  
  2767. buttons_section.appendChild(button);
  2768.  
  2769. button = document.createElement("button");
  2770. button.textContent = i18n.iridium_language.button_close;
  2771. button.className = "iri-settings-button";
  2772. button.addEventListener("click", this.closeEditor.bind(this, editor));
  2773.  
  2774. buttons_section.appendChild(button);
  2775.  
  2776. textarea = document.createElement("textarea");
  2777. textarea.id = "iridium-textarea";
  2778. textarea.value = JSON.stringify(i18n, null, 4);
  2779. textarea.setAttribute("spellcheck", "false");
  2780.  
  2781. editor.appendChild(buttons_section);
  2782. editor.appendChild(textarea);
  2783.  
  2784. }
  2785. }
  2786. },
  2787. ini: function () {
  2788.  
  2789. if (iridium_api.initializeOption.call(this)) {
  2790.  
  2791. return;
  2792.  
  2793. }
  2794.  
  2795. }
  2796. },
  2797. {
  2798. options: {
  2799. about: {
  2800. id: "about",
  2801. section: "about",
  2802. type: "custom"
  2803. }
  2804. }
  2805. }
  2806. ];
  2807.  
  2808. iridium_api = {
  2809.  
  2810. setCustomLanguage: function (custom_language) {
  2811.  
  2812. var i;
  2813. var j;
  2814. var key;
  2815. var sub_key;
  2816.  
  2817. key = Object.keys(custom_language);
  2818.  
  2819. for (i = 0; i < key.length; i++) {
  2820.  
  2821. sub_key = Object.keys(custom_language[key[i]]);
  2822.  
  2823. if (!(key[i] in i18n)) {
  2824.  
  2825. i18n[key[i]] = custom_language[key[i]];
  2826.  
  2827. } else {
  2828.  
  2829. for (j = 0; j < sub_key.length; j++) {
  2830.  
  2831. i18n[key[i]][sub_key[j]] = custom_language[key[i]][sub_key[j]];
  2832.  
  2833. }
  2834.  
  2835. }
  2836.  
  2837. }
  2838.  
  2839. },
  2840. fillSettingsContainer: function (options_list) {
  2841.  
  2842. var i;
  2843. var j;
  2844. var temp;
  2845. var input;
  2846. var label;
  2847. var select;
  2848. var header;
  2849. var option;
  2850. var options;
  2851. var section;
  2852. var setting;
  2853. var help_link;
  2854. var sub_section;
  2855.  
  2856. if (!(section = document.getElementById("settings_sub_section"))) {
  2857.  
  2858. return;
  2859.  
  2860. }
  2861.  
  2862. section.textContent = "";
  2863.  
  2864. if ((header = document.getElementById("settings_section_header"))) {
  2865.  
  2866. header.textContent = i18n.section_titles[options_list[0].section];
  2867.  
  2868. }
  2869.  
  2870. for (i = 0; i < options_list.length; i++) {
  2871.  
  2872. option = options_list[i];
  2873.  
  2874. if (!(sub_section = document.getElementById(i18n.sub_section_titles[option.sub_section]))) {
  2875.  
  2876. sub_section = document.createElement("div");
  2877. sub_section.id = i18n.sub_section_titles[option.sub_section];
  2878.  
  2879. header = document.createElement("h3");
  2880. header.textContent = i18n.sub_section_titles[option.sub_section];
  2881.  
  2882. sub_section.appendChild(header);
  2883. section.appendChild(sub_section);
  2884.  
  2885. }
  2886.  
  2887. setting = document.createElement("div");
  2888. setting.className = "settings_setting";
  2889.  
  2890. switch (option.type) {
  2891.  
  2892. case "checkbox":
  2893.  
  2894. input = document.createElement("input");
  2895. input.className = "setting";
  2896. input.id = option.id;
  2897. input.type = option.type;
  2898. input.checked = user_settings[option.id];
  2899.  
  2900. label = document.createElement("label");
  2901. label.textContent = i18n[option.id].label;
  2902. label.className = "setting";
  2903. label.setAttribute("for", option.id);
  2904.  
  2905. setting.appendChild(input);
  2906. setting.appendChild(label);
  2907.  
  2908. if (option.callback) {
  2909.  
  2910. input.callback = option.callback;
  2911.  
  2912. }
  2913.  
  2914. break;
  2915.  
  2916. case "dropdown":
  2917.  
  2918. label = document.createElement("label");
  2919. label.textContent = i18n[option.id].label;
  2920. label.className = "setting";
  2921. label.setAttribute("for", option.id);
  2922.  
  2923. select = document.createElement("select");
  2924. select.id = option.id;
  2925. select.className = "iri-settings-button";
  2926.  
  2927. for (j = 0; j < option.options.length; j++) {
  2928.  
  2929. options = document.createElement("option");
  2930. options.value = option.options[j];
  2931. options.textContent = i18n[option.id].options[j];
  2932.  
  2933. if (user_settings[option.id] === options.value) {
  2934.  
  2935. options.setAttribute("selected", "true");
  2936.  
  2937. }
  2938.  
  2939. select.appendChild(options);
  2940. }
  2941.  
  2942. setting.appendChild(label);
  2943. setting.appendChild(select);
  2944.  
  2945. break;
  2946.  
  2947. case "custom":
  2948.  
  2949. if (option.custom) {
  2950.  
  2951. temp = option.custom();
  2952.  
  2953. for (j = 0; j < temp.length; j++) {
  2954.  
  2955. setting.appendChild(temp[j]);
  2956.  
  2957. }
  2958.  
  2959. }
  2960.  
  2961. break;
  2962.  
  2963. }
  2964.  
  2965. if (option.type !== "custom") {
  2966.  
  2967. help_link = document.createElement("a");
  2968. help_link.textContent = "?";
  2969. help_link.href = "https://github.com/ParticleCore/Iridium/wiki/Features#" + option.id;
  2970. help_link.setAttribute("title", i18n.iridium_api.feature_link);
  2971. help_link.className = "feature-link";
  2972. help_link.setAttribute("target", "features");
  2973.  
  2974. setting.appendChild(help_link);
  2975.  
  2976. }
  2977.  
  2978. sub_section.appendChild(setting);
  2979.  
  2980. }
  2981.  
  2982. },
  2983. loadSelectedSection: function () {
  2984.  
  2985. var name;
  2986. var option;
  2987. var active_id;
  2988. var options_list;
  2989. var active_sidebar;
  2990.  
  2991. if (!(active_sidebar = document.querySelector(".sidebar_section.active_sidebar"))) {
  2992.  
  2993. return;
  2994.  
  2995. }
  2996.  
  2997. active_id = active_sidebar.dataset.section;
  2998. options_list = [];
  2999.  
  3000. for (i = 0; i < modules.length; i++) {
  3001.  
  3002. if (modules[i].options) {
  3003.  
  3004. for (name in modules[i].options) {
  3005.  
  3006. if (modules[i].options.hasOwnProperty(name)) {
  3007.  
  3008. option = modules[i].options[name];
  3009.  
  3010. if (option.section === active_id) {
  3011.  
  3012. options_list.push(option);
  3013.  
  3014. }
  3015.  
  3016. }
  3017.  
  3018. }
  3019.  
  3020. }
  3021.  
  3022. }
  3023.  
  3024. iridium_api.fillSettingsContainer(options_list);
  3025.  
  3026. },
  3027. updateSidebarSelection: function (event) {
  3028.  
  3029. var next;
  3030. var current;
  3031. var sidebar_current;
  3032.  
  3033. if (event.target.dataset.section) {
  3034.  
  3035. current = document.querySelector(".active_sidebar");
  3036. next = document.getElementById("sidebar_" + event.target.dataset.section);
  3037.  
  3038. if (next !== current) {
  3039.  
  3040. if ((sidebar_current = document.querySelector(".active_sidebar"))) {
  3041.  
  3042. sidebar_current.classList.remove("active_sidebar");
  3043.  
  3044. }
  3045.  
  3046. event.target.classList.add("active_sidebar");
  3047.  
  3048. iridium_api.loadSelectedSection();
  3049.  
  3050. }
  3051.  
  3052. }
  3053.  
  3054. },
  3055. settingsBuilder: function (option) {
  3056.  
  3057. var header;
  3058. var divider;
  3059. var section;
  3060. var sub_section;
  3061. var sidebar_section;
  3062. var settings_sidebar;
  3063. var settings_container;
  3064.  
  3065. if (!(settings_sidebar = document.getElementById("iridium_settings_sidebar"))) {
  3066.  
  3067. settings_sidebar = document.createElement("div");
  3068. settings_sidebar.id = "iridium_settings_sidebar";
  3069.  
  3070. document.body.appendChild(settings_sidebar);
  3071.  
  3072. }
  3073.  
  3074. if (!(sidebar_section = document.getElementById("sidebar_" + option.section))) {
  3075.  
  3076. sidebar_section = document.createElement("div");
  3077. sidebar_section.id = "sidebar_" + option.section;
  3078. sidebar_section.textContent = option.section;
  3079. sidebar_section.dataset.section = option.section;
  3080.  
  3081. sidebar_section.className = "sidebar_section";
  3082. settings_sidebar.appendChild(sidebar_section);
  3083.  
  3084. }
  3085.  
  3086. if (!(settings_container = document.getElementById("iridium_settings_container"))) {
  3087.  
  3088. settings_container = document.createElement("div");
  3089. settings_container.id = "iridium_settings_container";
  3090.  
  3091. if (!(section = document.getElementById("settings_section"))) {
  3092.  
  3093. header = document.createElement("h2");
  3094. header.id = "settings_section_header";
  3095.  
  3096. divider = document.createElement("div");
  3097. divider.className = "settings_divider";
  3098.  
  3099. section = document.createElement("div");
  3100. section.id = "settings_section";
  3101.  
  3102. section.addEventListener("change", iridium_api.autoSaveSettings, true);
  3103. section.appendChild(header);
  3104. section.appendChild(divider);
  3105.  
  3106. settings_container.appendChild(section);
  3107.  
  3108. }
  3109.  
  3110. if (!(sub_section = document.getElementById("settings_sub_section"))) {
  3111.  
  3112. sub_section = document.createElement("div");
  3113. sub_section.id = "settings_sub_section";
  3114.  
  3115. section.appendChild(sub_section);
  3116.  
  3117. }
  3118.  
  3119. document.body.appendChild(settings_container);
  3120.  
  3121. }
  3122.  
  3123. if (!document.querySelector(".active_sidebar")) {
  3124.  
  3125. sidebar_section.classList.add("active_sidebar");
  3126.  
  3127. }
  3128.  
  3129. },
  3130. loadSettingsMenu: function () {
  3131.  
  3132. var i;
  3133. var name;
  3134. var title;
  3135. var option;
  3136.  
  3137. if (document.head) {
  3138.  
  3139. document.head.textContent = "";
  3140.  
  3141. } else {
  3142.  
  3143. document.documentElement.appendChild(document.createElement("head"));
  3144.  
  3145. }
  3146.  
  3147. if (document.body) {
  3148.  
  3149. document.body.textContent = "";
  3150.  
  3151. } else {
  3152.  
  3153. document.documentElement.appendChild(document.createElement("body"));
  3154.  
  3155. }
  3156.  
  3157. if (!(title = document.querySelector("title"))) {
  3158.  
  3159. title = document.createElement("title");
  3160.  
  3161. document.head.appendChild(title);
  3162.  
  3163. }
  3164.  
  3165. title.textContent = i18n.iridium_api.settings_button;
  3166. document.body.id = "iridium_settings";
  3167. document.body.style.display = "none";
  3168.  
  3169. for (i = 0; i < modules.length; i++) {
  3170.  
  3171. if (modules[i].options) {
  3172.  
  3173. for (name in modules[i].options) {
  3174.  
  3175. if (modules[i].options.hasOwnProperty(name)) {
  3176.  
  3177. option = modules[i].options[name];
  3178. iridium_api.settingsBuilder(option);
  3179.  
  3180. }
  3181.  
  3182. }
  3183.  
  3184. }
  3185.  
  3186. }
  3187.  
  3188. document.addEventListener("click", iridium_api.updateSidebarSelection);
  3189.  
  3190. iridium_api.loadSelectedSection();
  3191.  
  3192. },
  3193. autoSaveSettings: function (event) {
  3194.  
  3195. switch (event.target.type) {
  3196.  
  3197. case "checkbox":
  3198.  
  3199. user_settings[event.target.id] = event.target.checked;
  3200.  
  3201. break;
  3202.  
  3203. case "select-one":
  3204.  
  3205. user_settings[event.target.id] = event.target.value;
  3206.  
  3207. break;
  3208.  
  3209. }
  3210.  
  3211. if (event.target.callback) {
  3212.  
  3213. event.target.callback();
  3214.  
  3215. }
  3216.  
  3217. iridium_api.saveSettings();
  3218.  
  3219. },
  3220. saveSettings: function () {
  3221.  
  3222. document.documentElement.dataset.iridium_save_settings = JSON.stringify(user_settings);
  3223.  
  3224. },
  3225. initializeSettings: function () {
  3226.  
  3227. var i;
  3228. var option;
  3229. var options;
  3230.  
  3231. user_settings = JSON.parse(document.documentElement.dataset.iridium_user_settings || "{}");
  3232.  
  3233. if (document.documentElement.dataset.iridium_user_settings) {
  3234.  
  3235. document.documentElement.removeAttribute("data-iridium_user_settings");
  3236.  
  3237. }
  3238.  
  3239. i18n = default_language;
  3240.  
  3241. if (user_settings.custom_language) {
  3242.  
  3243. iridium_api.setCustomLanguage(user_settings.custom_language);
  3244.  
  3245. }
  3246.  
  3247. for (i = 0; i < modules.length; i++) {
  3248.  
  3249. for (options in modules[i].options) {
  3250.  
  3251. if (modules[i].options.hasOwnProperty(options)) {
  3252.  
  3253. option = modules[i].options[options];
  3254.  
  3255. if (!(option.id in user_settings) && "value" in option) {
  3256.  
  3257. user_settings[option.id] = option.value;
  3258.  
  3259. }
  3260.  
  3261. if (option.i18n) {
  3262.  
  3263. i18n[option.id] = option.i18n;
  3264.  
  3265. }
  3266.  
  3267. }
  3268.  
  3269. }
  3270.  
  3271. }
  3272.  
  3273. },
  3274. initializeSettingsButton: function () {
  3275.  
  3276. var buttons;
  3277. var iridium_settings_button;
  3278.  
  3279. buttons = document.querySelector("#end #buttons");
  3280.  
  3281. if (buttons && !(iridium_settings_button = document.getElementById("iridium_settings_button"))) {
  3282.  
  3283. iridium_settings_button = document.createElement("a");
  3284. iridium_settings_button.id = "iridium_settings_button";
  3285. iridium_settings_button.href = "/iridium-settings";
  3286. iridium_settings_button.target = "_blank";
  3287. iridium_settings_button.innerHTML =
  3288. "<svg viewBox='0 0 24 24' style='height:24px;'>" +
  3289. " <radialGradient id='iri-gradient' gradientUnits='userSpaceOnUse' cx='6' cy='22' r='18.5'>" +
  3290. " <stop class='iri-start-gradient' offset='0'/>" +
  3291. " <stop class='iri-stop-gradient' offset='1'/>" +
  3292. " </radialGradient>" +
  3293. " <polygon points='24,11.8 6,1.6 6,22'/>" +
  3294. " <path d='M6 1.6V22l18-10.2L6 1.6z M9 6.8l9 5.1L9 17V6.8z'/>" +
  3295. "</svg>" +
  3296. "<div class='iri-tooltip' style='opacity: 0'>" + i18n.iridium_api.settings_button + "</div>";
  3297. buttons.parentNode.insertBefore(iridium_settings_button, buttons);
  3298.  
  3299. document.documentElement.removeEventListener("load", iridium_api.initializeSettingsButton, true);
  3300.  
  3301. }
  3302.  
  3303. },
  3304. initializeModules: function () {
  3305.  
  3306. var i;
  3307. var timestamp;
  3308.  
  3309. for (i = 0; i < modules.length; i++) {
  3310.  
  3311. if (modules[i].ini) {
  3312.  
  3313. modules[i].ini();
  3314.  
  3315. }
  3316.  
  3317. }
  3318.  
  3319. if (user_settings.player_quality !== "auto") {
  3320.  
  3321. timestamp = Date.now();
  3322.  
  3323. window.localStorage.setItem(
  3324. "yt-player-quality",
  3325. JSON.stringify({
  3326. data: user_settings.player_quality,
  3327. creation: timestamp,
  3328. expiration: timestamp + 2592E6
  3329. })
  3330. );
  3331.  
  3332. }
  3333.  
  3334. },
  3335. initializeOption: function () {
  3336.  
  3337. var key;
  3338.  
  3339. if (this.started) {
  3340.  
  3341. return true;
  3342.  
  3343. }
  3344.  
  3345. this.started = true;
  3346.  
  3347. for (key in this.options) {
  3348.  
  3349. if (this.options.hasOwnProperty(key)) {
  3350.  
  3351. if (!(key in user_settings) && this.options[key].value) {
  3352.  
  3353. user_settings[key] = this.options[key].value;
  3354.  
  3355. }
  3356.  
  3357. }
  3358.  
  3359. }
  3360.  
  3361. return false;
  3362.  
  3363. },
  3364. ini: function () {
  3365.  
  3366. iridium_api.initializeSettings();
  3367.  
  3368. if (window.location.pathname === "/iridium-settings") {
  3369.  
  3370. iridium_api.loadSettingsMenu();
  3371.  
  3372. if (user_settings.iridium_dark_mode) {
  3373.  
  3374. document.documentElement.classList.add("iri-dark-mode-settings");
  3375.  
  3376. }
  3377.  
  3378. } else {
  3379.  
  3380. iridium_api.initializeModules();
  3381.  
  3382. }
  3383.  
  3384. document.documentElement.addEventListener("load", iridium_api.initializeSettingsButton, true);
  3385.  
  3386. }
  3387.  
  3388. };
  3389.  
  3390. iridium_api.ini();
  3391.  
  3392. },
  3393. contentScriptMessages: function () {
  3394.  
  3395. var key1;
  3396. var key2;
  3397. var gate;
  3398. var sets;
  3399. var locs;
  3400. var observer;
  3401.  
  3402. key1 = "iridium_save_settings";
  3403. key2 = "getlocale";
  3404. gate = document.documentElement;
  3405. sets = JSON.parse(gate.dataset[key1] || null);
  3406. locs = gate.dataset[key2] || null;
  3407.  
  3408. if (!gate.contentscript) {
  3409.  
  3410. gate.contentscript = true;
  3411. observer = new MutationObserver(iridium.contentScriptMessages);
  3412.  
  3413. return observer.observe(gate, {
  3414. attributes: true,
  3415. attributeFilter: ["data-" + key1, "data-" + key2]
  3416. });
  3417.  
  3418. }
  3419.  
  3420. if (sets) {
  3421.  
  3422. if (iridium.is_userscript) {
  3423.  
  3424. iridium.GM_setValue(iridium.id, JSON.stringify(sets));
  3425.  
  3426. } else {
  3427.  
  3428. chrome.storage.local.set({iridiumSettings: sets});
  3429.  
  3430. }
  3431.  
  3432. document.documentElement.removeAttribute("data-iridium_save_settings");
  3433.  
  3434. } else if (locs) {
  3435.  
  3436. document.documentElement.dataset.setlocale = chrome.i18n.getMessage(locs);
  3437.  
  3438. }
  3439.  
  3440. },
  3441. filterChromeKeys: function (keys) {
  3442.  
  3443. if (keys[iridium.id] && keys[iridium.id].new_value) {
  3444.  
  3445. document.documentElement.dataset.iridium_load_settings = JSON.stringify(
  3446. (keys[iridium.id].new_value && keys[iridium.id].new_value[iridium.id]) || keys[iridium.id].new_value || {}
  3447. );
  3448.  
  3449. }
  3450.  
  3451. },
  3452. main: function (event) {
  3453.  
  3454. var holder;
  3455.  
  3456. if (!event && iridium.is_userscript) {
  3457.  
  3458. event = JSON.parse(iridium.GM_getValue(iridium.id, "{}"));
  3459.  
  3460. }
  3461.  
  3462. if (event) {
  3463.  
  3464. event = JSON.stringify(event[iridium.id] || event);
  3465. document.documentElement.dataset.iridium_user_settings = event;
  3466.  
  3467. if (iridium.is_userscript) {
  3468.  
  3469. holder = document.createElement("link");
  3470. holder.rel = "stylesheet";
  3471. holder.type = "text/css";
  3472. holder.href = "https://particlecore.github.io/Iridium/css/Iridium.css?v=0.3.0a";
  3473. document.documentElement.appendChild(holder);
  3474.  
  3475. }
  3476.  
  3477. holder = document.createElement("script");
  3478. holder.textContent = "(" + iridium.inject + "(" + iridium.is_userscript + "))";
  3479. document.documentElement.appendChild(holder);
  3480. holder.remove();
  3481.  
  3482. if (!iridium.is_userscript) {
  3483.  
  3484. chrome.storage.onChanged.addListener(iridium.filterChromeKeys);
  3485.  
  3486. }
  3487.  
  3488. }
  3489.  
  3490. },
  3491. ini: function () {
  3492.  
  3493. if (window.location.pathname === "/iridium-settings") {
  3494.  
  3495. window.stop();
  3496.  
  3497. }
  3498.  
  3499. iridium.id = "iridiumSettings";
  3500. iridium.is_userscript = typeof GM_info === "object";
  3501.  
  3502. if (iridium.is_userscript) {
  3503.  
  3504. iridium.GM_getValue = GM_getValue;
  3505. iridium.GM_setValue = GM_setValue;
  3506. iridium.main();
  3507.  
  3508. } else {
  3509.  
  3510. chrome.storage.local.get(iridium.id, iridium.main);
  3511.  
  3512. }
  3513.  
  3514. iridium.contentScriptMessages();
  3515.  
  3516. }
  3517.  
  3518. };
  3519.  
  3520. iridium.ini();
  3521.  
  3522. }());