Iridium

YouTube with more freedom

目前為 2018-07-17 提交的版本,檢視 最新版本

  1. // ==UserScript==
  2. // @version 0.1.8
  3. // @name Iridium
  4. // @namespace https://github.com/ParticleCore
  5. // @description YouTube with more freedom
  6. // @compatible firefox
  7. // @compatible chrome
  8. // @icon https://raw.githubusercontent.com/ParticleCore/Iridium/gh-pages/images/i-icon.png
  9. // @match *://www.youtube.com/*
  10. // @run-at document-start
  11. // @homepageURL https://github.com/ParticleCore/Iridium
  12. // @supportURL https://github.com/ParticleCore/Iridium/wiki
  13. // @contributionURL https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=UMVQJJFG4BFHW&lc=US
  14. // @grant GM.getValue
  15. // @grant GM.setValue
  16. // @grant GM_getValue
  17. // @grant GM_setValue
  18. // @noframes
  19. // ==/UserScript==
  20. (function () {
  21. "use strict";
  22.  
  23. var iridium = {
  24.  
  25. inject: function (is_user_script) {
  26.  
  27. var i18n;
  28. var modules;
  29. var iridium_api;
  30. var user_settings;
  31. var default_language;
  32. var send_settings_to_page;
  33. var receive_settings_from_page;
  34.  
  35. default_language = {
  36. language: "English (US)",
  37. section_list: {
  38. donate: "donate",
  39. general: "general",
  40. video: "video",
  41. settings: "settings"
  42. },
  43. section_titles: {
  44. donate: "Support Iridium",
  45. general: "General settings",
  46. video: "Video settings",
  47. settings: "Iridium settings"
  48. },
  49. sub_section_titles: {
  50. channel: "Channel",
  51. blacklist: "Blacklist",
  52. general: "General",
  53. language: "Language",
  54. layout: "Layout",
  55. paypal: "Paypal",
  56. patreon: "Patreon",
  57. playlist: "Playlist",
  58. player: "Player",
  59. settings: "Settings",
  60. thumbnails: "Thumbnails"
  61. },
  62. iridium_api: {
  63. settings_button: "Iridium",
  64. feature_link: "Find out what this does"
  65. },
  66. welcome_box: {
  67. thank_you: "Thank you for installing Iridium!",
  68. settings_instruction: "You can open your settings by clicking the triangle located on the top right corner of this page.",
  69. features_instruction: "For a complete list of features visit the following link:",
  70. features_link: "Feature list",
  71. donate_instruction: "If you wish to help the development of this extension you can use the following options:",
  72. paypal_one_time: "One time donation",
  73. paypal_any_amount: "Any amount",
  74. paypal_monthly: "Monthly donation",
  75. patreon_support: "Support with Patreon",
  76. button_close: "close"
  77. }
  78. };
  79.  
  80. modules = [
  81. {
  82. options: {
  83. default_logo_page: {
  84. id: "default_logo_page",
  85. section: "general",
  86. sub_section: "general",
  87. type: "dropdown",
  88. value: "home",
  89. i18n: {
  90. label: "Default YouTube logo page:",
  91. options: [
  92. "Home",
  93. "Trending",
  94. "Subscriptions"
  95. ]
  96. },
  97. options: [
  98. "home",
  99. "trending",
  100. "subscriptions"
  101. ]
  102. },
  103. default_channel_tab: {
  104. id: "default_channel_tab",
  105. section: "general",
  106. sub_section: "general",
  107. type: "dropdown",
  108. value: "home",
  109. i18n: {
  110. label: "Default channel tab:",
  111. options: [
  112. "Home",
  113. "Videos",
  114. "Playlists",
  115. "Channels",
  116. "Discussion",
  117. "About"
  118. ]
  119. },
  120. options: [
  121. "home",
  122. "videos",
  123. "playlists",
  124. "channels",
  125. "discussion",
  126. "about"
  127. ]
  128. }
  129. },
  130. setDestination: function (event) {
  131.  
  132. var url;
  133. var data;
  134. var parent;
  135. var target;
  136.  
  137. target = event.target;
  138.  
  139. if (!(data = target.data)) {
  140.  
  141. parent = target.parentNode;
  142.  
  143. while (parent) {
  144.  
  145. if (parent.data) {
  146. target = parent;
  147. data = target.data;
  148. break;
  149. }
  150.  
  151. parent = parent.parentNode;
  152.  
  153. }
  154.  
  155. }
  156.  
  157. if (data && (url = iridium_api.getSingleObjectByKey(data, ["webCommandMetadata"])) && (url = url.url)) {
  158.  
  159. if (user_settings.default_channel_tab !== "home" && url.match(/^\/(?:channel|user)\/(?:[^\/])+$/)) {
  160. if (data.commandMetadata && data.commandMetadata.webCommandMetadata) {
  161.  
  162. data.commandMetadata.webCommandMetadata.url += "/" + user_settings.default_channel_tab;
  163.  
  164. if (target.href) {
  165. target.href = data.commandMetadata.webCommandMetadata.url;
  166. }
  167.  
  168. }
  169. }
  170.  
  171. if (user_settings.default_logo_page !== "home" && url === "/" && target.tagName === "A" && target.id === "logo") {
  172.  
  173. if (data.browseEndpoint) {
  174. data.browseEndpoint.browseId = "FE" + user_settings.default_logo_page;
  175. }
  176.  
  177. if (data.commandMetadata && data.commandMetadata.webCommandMetadata) {
  178.  
  179. data.commandMetadata.webCommandMetadata.url += "feed/" + user_settings.default_logo_page;
  180. target.href = data.commandMetadata.webCommandMetadata.url;
  181.  
  182. }
  183.  
  184. }
  185.  
  186. }
  187.  
  188. },
  189. ini: function () {
  190.  
  191. if (iridium_api.initializeOption.call(this)) {
  192. return;
  193. }
  194.  
  195. window.addEventListener("mouseup", this.setDestination.bind(this), true);
  196.  
  197. }
  198. },
  199. {
  200. options: {
  201. square_avatars: {
  202. id: "square_avatars",
  203. section: "general",
  204. sub_section: "layout",
  205. type: "checkbox",
  206. value: true,
  207. i18n: {
  208. label: "Make user images squared"
  209. }
  210. }
  211. },
  212. ini: function () {
  213.  
  214. if (iridium_api.initializeOption.call(this)) {
  215. return;
  216. }
  217.  
  218. if (user_settings.square_avatars) {
  219. document.documentElement.classList.add("iri-square-avatars");
  220. } else {
  221. document.documentElement.classList.remove("iri-square-avatars");
  222. }
  223.  
  224. }
  225. },
  226. {
  227. options: {
  228. improved_logo: {
  229. id: "improved_logo",
  230. section: "general",
  231. sub_section: "layout",
  232. type: "checkbox",
  233. value: true,
  234. i18n: {
  235. label: "Improve the YouTube logo"
  236. }
  237. }
  238. },
  239. ini: function () {
  240.  
  241. if (iridium_api.initializeOption.call(this)) {
  242. return;
  243. }
  244.  
  245. if (user_settings.improved_logo) {
  246. document.documentElement.classList.add("iri-improved-logo");
  247. } else {
  248. document.documentElement.classList.remove("iri-improved-logo");
  249. }
  250.  
  251. }
  252. },
  253. {
  254. options: {
  255. thumbnail_preview: {
  256. id: "thumbnail_preview",
  257. section: "general",
  258. sub_section: "thumbnails",
  259. type: "checkbox",
  260. value: false,
  261. i18n: {
  262. label: "Play videos by hovering the thumbnails"
  263. }
  264. },
  265. thumbnail_preview_mute: {
  266. id: "thumbnail_preview_mute",
  267. section: "general",
  268. sub_section: "thumbnails",
  269. type: "checkbox",
  270. value: false,
  271. i18n: {
  272. label: "Shift key toggles audio on video thumbnail playback"
  273. }
  274. }
  275. },
  276. togglePreviewMute: function (event) {
  277.  
  278. var player_api;
  279.  
  280. if (!user_settings.thumbnail_preview_mute) {
  281. return;
  282. }
  283.  
  284. if (event.which === 16 && (player_api = document.getElementById("iri-preview-player"))) {
  285. player_api.handleGlobalKeyDown(77, false);
  286. }
  287.  
  288. },
  289. setPreviewArgs: function (args) {
  290.  
  291. args.autoplay = 1;
  292. args.controls = "0";
  293. args.enablecastapi = "0";
  294. args.iv_load_policy = "3";
  295. args.modestbranding = "1";
  296. args.mute = "1";
  297. args.player_wide = "0";
  298. args.rel = "0";
  299. args.showinfo = "0";
  300. args.vq = "small";
  301.  
  302. args.ad3_module = null;
  303. args.baseUrl = null;
  304. args.eventid = null;
  305. args.iv_endscreen_url = null;
  306. args.ppv_remarketing_url = null;
  307. args.probe_url = null;
  308. args.remarketing_url = null;
  309. args.videostats_playback_base_url = null;
  310.  
  311. },
  312. iniPreview: function (context, event) {
  313.  
  314. var i;
  315. var args;
  316. var temp;
  317. var config;
  318. var data_list;
  319. var player_api;
  320.  
  321. args = {};
  322. data_list = event.target.responseText.split("&");
  323.  
  324. for (i = 0; i < data_list.length; i++) {
  325.  
  326. temp = data_list[i].split("=");
  327. args[temp[0]] = window.decodeURIComponent(temp[1]);
  328.  
  329. }
  330.  
  331. context.setPreviewArgs(args);
  332.  
  333. config = JSON.parse(JSON.stringify(window.yt.config_.FILLER_DATA.player));
  334. config.args = args;
  335. config.attrs.id = "iri-preview-player";
  336.  
  337. window.yt.player.Application.create("iri-video-preview", config);
  338.  
  339. if ((player_api = document.getElementById("iri-preview-player"))) {
  340.  
  341. if (player_api.setVolume) {
  342. player_api.setVolume(50);
  343. }
  344.  
  345. if (player_api.setSizeStyle) {
  346. player_api.setSizeStyle(false, true);
  347. }
  348.  
  349. }
  350.  
  351. if (document.documentElement.classList.contains("iri-always-visible")) {
  352. if ((player_api = document.getElementById("movie_player"))) {
  353. if (player_api.setSizeStyle) {
  354. player_api.setSizeStyle(false, true);
  355. }
  356. }
  357. }
  358.  
  359. },
  360. getPreviewArgs: function (video_id) {
  361.  
  362. var sts;
  363. var xhr;
  364. var params;
  365. var context;
  366.  
  367. context = this;
  368. sts = window.yt.config_.FILLER_DATA.player.sts;
  369. params =
  370. "video_id=" + video_id + "&" +
  371. "sts=" + sts + "&" +
  372. "ps=gaming" + "&" +
  373. "el=detailpage" + "&" +
  374. "c=WEB_GAMING" + "&" +
  375. "cplayer=UNIPLAYER" + "&" +
  376. "mute=true" + "&" +
  377. "authuser=0";
  378.  
  379. xhr = new XMLHttpRequest();
  380. xhr.addEventListener("load", function (event) {
  381. context.iniPreview(context, event);
  382. });
  383. xhr.open("GET", "/get_video_info?" + params, true);
  384. xhr.send();
  385.  
  386. return xhr;
  387.  
  388. },
  389. endPreviewContainer: function (event, container, listener, xhr, timer, context, video_container, clicked) {
  390.  
  391. if (clicked || !container.parentNode.contains(event.toElement || event.relatedTarget)) {
  392.  
  393. document.removeEventListener("keydown", context.togglePreviewMute, false);
  394.  
  395. container.parentNode.removeEventListener("click", listener, false);
  396. container.parentNode.removeEventListener("mouseleave", listener, false);
  397.  
  398. if (timer) {
  399. window.clearInterval(timer);
  400. }
  401.  
  402. if ((video_container = document.getElementById("iri-video-preview"))) {
  403.  
  404. if (xhr) {
  405. xhr.abort();
  406. }
  407.  
  408. if (iridium_api.checkIfExists("firstChild.destroy", video_container)) {
  409. video_container.firstChild.destroy();
  410. }
  411. }
  412. }
  413.  
  414. },
  415. iniPreviewContainer: function (event) {
  416.  
  417. var xhr;
  418. var timer;
  419. var context;
  420. var video_id;
  421. var container;
  422. var video_container;
  423. var moving_thumbnail;
  424. var player_container;
  425. var player_manager_api;
  426.  
  427. moving_thumbnail = event.target.querySelector("#mouseover-overlay");
  428.  
  429. if (!user_settings.thumbnail_preview) {
  430.  
  431. if (moving_thumbnail) {
  432. moving_thumbnail.removeAttribute("style");
  433. }
  434.  
  435. return;
  436.  
  437. }
  438.  
  439. container = event.target;
  440.  
  441. if (container.tagName === "YTD-THUMBNAIL" && (container = event.target.querySelector("yt-img-shadow"))) {
  442. if ((video_id = iridium_api.checkIfExists("dataHost.data.videoId", container)) && !container.querySelector("#iri-preview-player")) {
  443.  
  444. context = this;
  445.  
  446. if (moving_thumbnail) {
  447. moving_thumbnail.setAttribute("style", "display:none");
  448. }
  449.  
  450. if (!(video_container = document.getElementById("iri-video-preview"))) {
  451.  
  452. video_container = document.createElement("iri-video-preview");
  453. video_container.id = "iri-video-preview";
  454. video_container.className = "ytp-small-mode";
  455.  
  456. }
  457.  
  458. if (video_container.parentNode !== container) {
  459. container.appendChild(video_container);
  460. }
  461.  
  462. if (iridium_api.checkIfExists("yt.player.Application.create")) {
  463. xhr = this.getPreviewArgs(video_id);
  464. } else {
  465.  
  466. if ((player_manager_api = document.querySelector("yt-player-manager"))) {
  467. if ((player_container = document.getElementById("player-container"))) {
  468. if (iridium_api.checkIfExists("yt.config_.FILLER_DATA.player")) {
  469. player_manager_api["acquireApi"](player_container, window.yt.config_.FILLER_DATA.player);
  470. }
  471. }
  472. }
  473.  
  474. timer = window.setInterval(function () {
  475. if (iridium_api.checkIfExists("yt.player.Application.create")) {
  476.  
  477. window.clearInterval(timer);
  478. xhr = context.getPreviewArgs(video_id);
  479.  
  480. }
  481. });
  482.  
  483. }
  484.  
  485. document.addEventListener("keydown", this.togglePreviewMute, false);
  486.  
  487. container.parentNode.addEventListener("click", function listener(event) {
  488. context.endPreviewContainer(event, container, listener, xhr, timer, context, video_container, true);
  489. }, false);
  490.  
  491. container.parentNode.addEventListener("mouseleave", function listener(event) {
  492. context.endPreviewContainer(event, container, listener, xhr, timer, context);
  493. }, false);
  494.  
  495. }
  496. }
  497.  
  498. },
  499. ini: function () {
  500.  
  501. if (iridium_api.initializeOption.call(this)) {
  502. return;
  503. }
  504.  
  505. document.addEventListener("mouseenter", this.iniPreviewContainer.bind(this), true);
  506.  
  507. }
  508. },
  509. {
  510. options: {
  511. popup_player: {
  512. id: "popup_player",
  513. section: "general",
  514. sub_section: "thumbnails",
  515. type: "checkbox",
  516. value: true,
  517. i18n: {
  518. label: "Enable player pop-up",
  519. button_add_title: "Pop-up"
  520. }
  521. }
  522. },
  523. popUpPlayerMinWidth: 533,
  524. modImportNode: function (original) {
  525.  
  526. var pop_out_button;
  527.  
  528. pop_out_button = document.createElement("template");
  529. pop_out_button.innerHTML =
  530. "<div class='iri-pop-up-player' style='opacity:0'>" +
  531. " <svg viewBox='0 0 24 24' height='16' width='16'>" +
  532. " <path d='M6 0v6H0v18h18v-6h6V0H6z M15 21H3V9h3v9h9V21z M21 15h-3h-3H9V9V6V3h12V15z'/>" +
  533. " </svg>" +
  534. " <div class='iri-tooltip' data-locale='text|button_add_title'></div>" +
  535. "</div>";
  536. pop_out_button = pop_out_button.content;
  537. iridium_api.applyText(pop_out_button, i18n.popup_player);
  538.  
  539. return function (externalNode, deep) {
  540.  
  541. var node;
  542. var container;
  543.  
  544. if (!user_settings.popup_player) {
  545. return original.apply(this, arguments);
  546. }
  547.  
  548. node = externalNode.firstElementChild;
  549.  
  550. if (node) {
  551. if ((node.id === "thumbnail" || node.id === "img")) {
  552.  
  553. container = node.id === "img" ? node.parentNode : node;
  554.  
  555. if (!container.querySelector(".iri-pop-up-player")) {
  556. container.appendChild(pop_out_button.cloneNode(true));
  557. }
  558.  
  559. }
  560. }
  561.  
  562. return original.apply(this, arguments);
  563.  
  564. };
  565.  
  566. },
  567. resumePlayback: function () {
  568.  
  569. var temp;
  570. var player_api;
  571.  
  572. if (window.location.pathname === "/watch") {
  573. if ((player_api = document.getElementById("movie_player"))) {
  574. if ((temp = this.document.querySelector("video"))) {
  575.  
  576. if (!isNaN(temp.duration) && temp.currentTime < temp.duration) {
  577.  
  578. temp = temp.currentTime;
  579. player_api.seekTo(temp);
  580.  
  581. }
  582.  
  583. }
  584. }
  585. }
  586.  
  587. },
  588. popUpPlayer: function (event, url) {
  589.  
  590. var top;
  591. var left;
  592. var video;
  593. var width;
  594. var height;
  595. var pop_up;
  596. var pop_up_url;
  597. var player_api;
  598. var current_config;
  599.  
  600. width = user_settings.popup_player_size || this.popUpPlayerMinWidth;
  601. height = Math.round(width / (16 / 9));
  602. left = event.screenX - (width / 2);
  603. top = event.screenY - 15;
  604. video = document.querySelector("video");
  605. pop_up_url = url || window.location.href.split(/&t=[0-9]+|#t=[0-9]+|&time=[0-9]+/).join("");
  606.  
  607. if (!url && video && video.currentTime && video.currentTime < video.duration) {
  608. if ((player_api = document.getElementById("movie_player"))) {
  609.  
  610. current_config = player_api.getUpdatedConfigurationData();
  611. pop_up_url = pop_up_url + "#t=" + video.currentTime;
  612. current_config.args.start = video.currentTime;
  613. current_config.args.cue_player = true;
  614.  
  615. player_api.cueVideoByPlayerVars(current_config.args);
  616.  
  617. }
  618. }
  619.  
  620. pop_up = window.open(pop_up_url, "popUpPlayer", "width=" + width + ",height=" + height + ",left=" + left + ",top=" + top);
  621.  
  622. if (!url) {
  623. pop_up.addEventListener("beforeunload", this.resumePlayback.bind(pop_up), false);
  624. }
  625.  
  626. pop_up.focus();
  627.  
  628. },
  629. startPopUpPlayer: function (event) {
  630.  
  631. var url;
  632. var parent;
  633.  
  634. if (!user_settings.popup_player) {
  635. return;
  636. }
  637.  
  638. if (event.type === "message") {
  639. if (event.data.id === user_settings.broadcast_id && event.data.action === "ini-pop-up-player") {
  640.  
  641. event.screenX = event.data.screenX;
  642. event.screenY = event.data.screenY;
  643.  
  644. this.popUpPlayer(event);
  645.  
  646. }
  647. } else if (event.target.className === "iri-pop-up-player") {
  648.  
  649. event.preventDefault();
  650. event.stopPropagation();
  651.  
  652. parent = event.target.parentNode;
  653.  
  654. while (parent) {
  655.  
  656. if (parent.data) {
  657.  
  658. if ("commandMetadata" in parent.data) {
  659. if ((url = iridium_api.getSingleObjectByKey(parent.data["commandMetadata"], ["url"]))) {
  660. this.popUpPlayer(event, url);
  661. }
  662. }
  663.  
  664. break;
  665.  
  666. }
  667.  
  668. parent = parent.parentNode;
  669.  
  670. }
  671.  
  672. return false;
  673.  
  674. }
  675.  
  676. },
  677. saveNewSize: function () {
  678.  
  679. this.popUpPlayerResizeTimer = null;
  680.  
  681. if (window.innerWidth > this.popUpPlayerMinWidth) {
  682. user_settings.popup_player_size = window.innerWidth;
  683. } else {
  684. user_settings.popup_player_size = this.popUpPlayerMinWidth;
  685. }
  686.  
  687. iridium_api.saveSettings("popup_player_size");
  688.  
  689. },
  690. popUpPlayerResize: function (event) {
  691.  
  692. if (this.popUpPlayerResizeTimer) {
  693. window.clearTimeout(this.popUpPlayerResizeTimer);
  694. }
  695.  
  696. this.popUpPlayerResizeTimer = window.setTimeout(this.saveNewSize.bind(this), 1000);
  697.  
  698. },
  699. updatePlayerStyle: function (api) {
  700. api.setSizeStyle(true, true);
  701. },
  702. playerReady: function (api) {
  703. if (api) {
  704. api.addEventListener("onStateChange", this.updatePlayerStyle.bind(this, api));
  705. }
  706. },
  707. shareApi: function (original) {
  708.  
  709. var context = this;
  710.  
  711. return function (api) {
  712.  
  713. context.playerReady(api);
  714.  
  715. if (original) {
  716. return original.apply(this, arguments);
  717. }
  718.  
  719. };
  720. },
  721. ini: function () {
  722.  
  723. if (iridium_api.initializeOption.call(this)) {
  724. return;
  725. }
  726.  
  727. if (iridium_api.isPopUpPlayer) {
  728.  
  729. window.onYouTubePlayerReady = this.shareApi(window.onYouTubePlayerReady);
  730.  
  731. window.addEventListener("resize", this.popUpPlayerResize.bind(this), false);
  732. document.documentElement.classList.add("iri-pop-up-player-window");
  733.  
  734. } else {
  735.  
  736. document.addEventListener("click", this.startPopUpPlayer.bind(this), true);
  737. window.addEventListener("message", this.startPopUpPlayer.bind(this), false);
  738.  
  739. HTMLDocument.prototype.importNode = this.modImportNode(HTMLDocument.prototype.importNode);
  740.  
  741. }
  742.  
  743. }
  744. },
  745. {
  746. options: {
  747. enable_blacklist: {
  748. id: "enable_blacklist",
  749. section: "general",
  750. sub_section: "blacklist",
  751. type: "checkbox",
  752. value: "true",
  753. i18n: {
  754. label: "Enable blacklist"
  755. }
  756. },
  757. blacklist_settings: {
  758. id: "blacklist_settings",
  759. section: "general",
  760. sub_section: "blacklist",
  761. type: "custom",
  762. value: {},
  763. i18n: {
  764. button_add_title: "Block",
  765. button_edit: "Edit",
  766. button_import: "Import",
  767. button_export: "Export",
  768. button_reset: "Reset",
  769. button_save: "Save",
  770. button_close: "Close",
  771. button_remove: "Remove from blacklist",
  772. placeholder: "Paste your new blacklist here",
  773. confirm_reset: "You are about to reset your blacklist. It is advised to backup your current blacklist before continuing.\n\nDo you wish to continue?\n\n",
  774. reset_success: "Blacklist has been reset.\n\nChanges will be applied after a page refresh.\n\n",
  775. 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 continue?\n\n",
  776. import_success: "Your blacklist has been imported with success.\n\nChanges will be applied after a page refresh.\n\n",
  777. import_error: "Your blacklist could not be imported because it appears to be invalid.\n\n"
  778. },
  779. custom: function () {
  780.  
  781. var element;
  782. var element_list;
  783.  
  784. element_list = [];
  785.  
  786. element = document.createElement("button");
  787. element.textContent = i18n.blacklist_settings.button_edit;
  788. element.className = "setting iri-settings-button";
  789. element.addEventListener("click", this.textEditor.bind(this, "edit"), false);
  790.  
  791. element_list.push(element);
  792.  
  793. element = document.createElement("button");
  794. element.textContent = i18n.blacklist_settings.button_import;
  795. element.className = "setting iri-settings-button";
  796. element.addEventListener("click", this.textEditor.bind(this, "import"), false);
  797.  
  798. element_list.push(element);
  799.  
  800. element = document.createElement("button");
  801. element.textContent = i18n.blacklist_settings.button_export;
  802. element.className = "setting iri-settings-button";
  803. element.addEventListener("click", this.textEditor.bind(this, "export"), false);
  804.  
  805. element_list.push(element);
  806.  
  807. element = document.createElement("button");
  808. element.textContent = i18n.blacklist_settings.button_reset;
  809. element.className = "setting iri-settings-button danger";
  810. element.addEventListener("click", this.resetBlacklist.bind(this), false);
  811.  
  812. element_list.push(element);
  813.  
  814. return element_list;
  815.  
  816. },
  817. resetBlacklist: function () {
  818. if (window.confirm(i18n.blacklist_settings.confirm_reset)) {
  819.  
  820. user_settings.blacklist_settings = {};
  821.  
  822. iridium_api.initializeSettings();
  823. iridium_api.saveSettings("blacklist_settings");
  824.  
  825. window.alert(i18n.blacklist_settings.reset_success);
  826.  
  827. }
  828. },
  829. importBlacklist: function () {
  830.  
  831. var editor;
  832. var textarea;
  833.  
  834. if ((textarea = document.getElementById("iridium-textarea")) && window.confirm(i18n.blacklist_settings.confirm_import)) {
  835.  
  836. try {
  837.  
  838. user_settings.blacklist_settings = JSON.parse(textarea.value);
  839.  
  840. iridium_api.saveSettings("blacklist_settings");
  841.  
  842. window.alert(i18n.blacklist_settings.import_success);
  843.  
  844. if ((editor = document.getElementById("iridium-text-editor"))) {
  845.  
  846. editor.remove();
  847.  
  848. }
  849.  
  850. } catch (error) {
  851.  
  852. window.alert(i18n.blacklist_settings.import_error + error.name + ": " + error.message);
  853.  
  854. }
  855.  
  856. }
  857.  
  858. },
  859. closeEditor: function (editor) {
  860.  
  861. editor.remove();
  862.  
  863. },
  864. textEditor: function (type, event) {
  865.  
  866. var i;
  867. var obj;
  868. var temp;
  869. var editor;
  870. var button;
  871. var channel;
  872. var textarea;
  873. var temp_list;
  874. var blocked_list;
  875. var close_button;
  876. var channel_link;
  877. var buttons_section;
  878.  
  879. if (!(editor = document.getElementById("iridium-text-editor"))) {
  880.  
  881. editor = document.createElement("div");
  882. editor.id = "iridium-text-editor";
  883.  
  884. document.body.appendChild(editor);
  885.  
  886. } else {
  887. editor.textContent = "";
  888. }
  889.  
  890. buttons_section = document.createElement("div");
  891. buttons_section.id = "buttons-section";
  892.  
  893. editor.appendChild(buttons_section);
  894.  
  895. if (type === "import" || type === "export") {
  896.  
  897. textarea = document.createElement("textarea");
  898. textarea.id = "iridium-textarea";
  899. textarea.setAttribute("spellcheck", "false");
  900.  
  901. if (type === "import") {
  902.  
  903. textarea.setAttribute("placeholder", i18n.blacklist_settings.placeholder);
  904.  
  905. button = document.createElement("button");
  906. button.textContent = i18n.blacklist_settings.button_save;
  907. button.className = "iri-settings-button";
  908. button.addEventListener("click", this.importBlacklist.bind(this));
  909.  
  910. buttons_section.appendChild(button);
  911.  
  912. } else {
  913. textarea.value = JSON.stringify(user_settings.blacklist_settings, null, 4);
  914. }
  915.  
  916. editor.appendChild(textarea);
  917.  
  918. } else if (type === "edit") {
  919.  
  920. blocked_list = document.createElement("div");
  921. blocked_list.id = "iridium-blacklist";
  922.  
  923. temp = Object.keys(user_settings.blacklist_settings);
  924. temp_list = [];
  925.  
  926. for (i = 0; i < temp.length; i++) {
  927.  
  928. obj = {};
  929. obj[temp[i]] = user_settings.blacklist_settings[temp[i]];
  930.  
  931. temp_list.push([
  932. temp[i],
  933. user_settings.blacklist_settings[temp[i]]
  934. ]);
  935.  
  936. }
  937.  
  938. temp_list = temp_list.sort(function (previous, next) {
  939. return previous[1].localeCompare(next[1]);
  940. });
  941.  
  942. for (i = 0; i < temp_list.length; i++) {
  943.  
  944. channel = document.createElement("template");
  945. channel.innerHTML =
  946. "<div class='iri-blacklist-channel'>" +
  947. " <button class='close' data-locale='title|button_remove'>" +
  948. " <svg viewBox='0 0 10 10' height='10' width='10'>" +
  949. " <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'/>" +
  950. " </svg>" +
  951. " </button><a target='_blank'></a>" +
  952. "</div>";
  953. channel = channel.content;
  954. iridium_api.applyText(channel, i18n.blacklist_settings);
  955. channel.firstChild.data = true;
  956.  
  957. channel_link = channel.querySelector("a");
  958. channel_link.href = "/channel/" + temp_list[i][0];
  959. channel_link.textContent = temp_list[i][1];
  960.  
  961. close_button = channel.querySelector(".close");
  962. close_button.container = channel.firstChild;
  963. close_button.ucid = temp_list[i][0];
  964. close_button.addEventListener("click", function (event) {
  965.  
  966. event.target.container.remove();
  967. delete user_settings.blacklist_settings[event.target.ucid];
  968.  
  969. iridium_api.saveSettings("blacklist_settings");
  970.  
  971. }, false);
  972.  
  973. blocked_list.appendChild(channel);
  974.  
  975. }
  976.  
  977. editor.appendChild(blocked_list);
  978.  
  979. }
  980.  
  981. button = document.createElement("button");
  982. button.textContent = i18n.blacklist_settings.button_close;
  983. button.className = "iri-settings-button";
  984. button.addEventListener("click", this.closeEditor.bind(this, editor), false);
  985.  
  986. buttons_section.appendChild(button);
  987.  
  988. }
  989. }
  990. },
  991. tag_list: [
  992. "YTD-COMPACT-LINK-RENDERER",
  993. "YTD-COMPACT-PLAYLIST-RENDERER",
  994. "YTD-COMPACT-PROMOTED-VIDEO-RENDERER",
  995. "YTD-COMPACT-RADIO-RENDERER",
  996. "YTD-COMPACT-VIDEO-RENDERER",
  997. "YTD-GRID-CHANNEL-RENDERER",
  998. "YTD-GRID-MOVIE-PLAYLIST-RENDERER",
  999. "YTD-GRID-MOVIE-RENDERER",
  1000. "YTD-GRID-PLAYLIST-RENDERER",
  1001. "YTD-GRID-RADIO-RENDERER",
  1002. "YTD-GRID-RENDERER",
  1003. "YTD-GRID-SHOW-RENDERER",
  1004. "YTD-GRID-VIDEO-RENDERER",
  1005. "YTD-CHANNEL-RENDERER",
  1006. "YTD-MOVIE-RENDERER",
  1007. "YTD-PLAYLIST-RENDERER",
  1008. "YTD-RADIO-RENDERER",
  1009. "YTD-SHOW-RENDERER",
  1010. "YTD-VIDEO-RENDERER"
  1011. ],
  1012. allowedBlacklistPage: function () {
  1013. return /(\/live$)|^\/($|feed\/(?!subscriptions)|watch|results|shared)/.test(window.location.pathname);
  1014. },
  1015. hasContainers: function () {
  1016. return window.location.pathname.match(/^\/(?:(?:|results)$|feed\/)/);
  1017. },
  1018. clearList: function (obj) {
  1019.  
  1020. var i;
  1021. var ids;
  1022. var videos;
  1023. var shelves;
  1024. var sections;
  1025. var shelf_tag;
  1026. var video_tag;
  1027. var section_tag;
  1028.  
  1029. section_tag = [
  1030. "itemSectionRenderer",
  1031. "showingResultsForRenderer",
  1032. "includingResultsForRenderer"
  1033. ];
  1034. shelf_tag = [
  1035. "shelfRenderer",
  1036. "compactAutoplayRenderer"
  1037. ];
  1038. video_tag = [
  1039. "playlistRenderer",
  1040. "channelRenderer",
  1041. "radioRenderer",
  1042. "showRenderer",
  1043. "videoRenderer",
  1044. "gridChannelRenderer",
  1045. "gridMoviePlaylistRenderer",
  1046. "gridMovieRenderer",
  1047. "gridPlaylistRenderer",
  1048. "gridRadioRenderer",
  1049. "gridShowRenderer",
  1050. "gridVideoRenderer",
  1051. "compactVideoRenderer",
  1052. "compactPlaylistRenderer",
  1053. "compactPromotedVideoRenderer",
  1054. "playlistPanelVideoRenderer"
  1055. ];
  1056.  
  1057. videos = iridium_api.getObjectByKey(obj, video_tag);
  1058.  
  1059. for (i = 0; i < videos.length; i++) {
  1060.  
  1061. ids = iridium_api.getObjectByKey(videos[i].target, ["browseId"], function (string) {
  1062. return string.indexOf("UC") === 0;
  1063. });
  1064.  
  1065. if (ids[0] && user_settings.blacklist_settings[ids[0].target["browseId"]]) {
  1066. videos[i].list.splice(videos[i].list.indexOf(videos[i].target), 1);
  1067. }
  1068.  
  1069. }
  1070.  
  1071. shelves = iridium_api.getObjectByKey(obj, shelf_tag);
  1072.  
  1073. for (i = 0; i < shelves.length; i++) {
  1074.  
  1075. videos = iridium_api.getObjectByKey(shelves[i].target, video_tag);
  1076.  
  1077. if (videos.length === 0) {
  1078. shelves[i].list.splice(shelves[i].list.indexOf(shelves[i].target), 1);
  1079. }
  1080.  
  1081. }
  1082.  
  1083. if (this.hasContainers()) {
  1084.  
  1085. sections = iridium_api.getObjectByKey(obj, section_tag);
  1086.  
  1087. for (i = 0; i < sections.length; i++) {
  1088. if (sections[i].target[sections[i].property].contents.length === 0) {
  1089. sections[i].list.splice(sections[i].list.indexOf(sections[i].target), 1);
  1090. }
  1091. }
  1092.  
  1093. }
  1094.  
  1095. },
  1096. modOnDone: function (original) {
  1097.  
  1098. var context = this;
  1099.  
  1100. return function (data) {
  1101.  
  1102. context.clearList(data);
  1103.  
  1104. return original.apply(this, arguments);
  1105.  
  1106. };
  1107.  
  1108. },
  1109. getEmptyContainers: function () {
  1110.  
  1111. var i;
  1112. var temp;
  1113. var shelf;
  1114. var container;
  1115. var container_nodes;
  1116.  
  1117. container_nodes = "#contents ytd-item-section-renderer, #contents ytd-shelf-renderer";
  1118. container = document.querySelectorAll(container_nodes);
  1119.  
  1120. for (i = 0; i < container.length; i++) {
  1121.  
  1122. shelf = container[i].querySelector("yt-horizontal-list-renderer");
  1123.  
  1124. if (shelf && (shelf.hasAttribute("at-start") || shelf.hasAttribute("at-end"))) {
  1125. shelf.fillRemainingListItems();
  1126. }
  1127.  
  1128. temp = container[i].querySelector(this.tag_list.join(","));
  1129.  
  1130. if (!temp) {
  1131. container[i].remove();
  1132. }
  1133.  
  1134. }
  1135.  
  1136. window.dispatchEvent(new Event("resize"));
  1137.  
  1138. },
  1139. getContainers: function () {
  1140.  
  1141. var i;
  1142. var ucid;
  1143. var container;
  1144. var container_nodes;
  1145.  
  1146. container_nodes = "#contents ytd-item-section-renderer, #contents ytd-shelf-renderer";
  1147. container = document.querySelectorAll(container_nodes);
  1148.  
  1149. for (i = 0; i < container.length; i++) {
  1150.  
  1151. ucid = iridium_api.getObjectByKey(container[i].data, ["browseId"], function (string) {
  1152. return string.indexOf("UC") === 0;
  1153. });
  1154.  
  1155. if (ucid[0] && ucid.length === 1 && ucid[0].target.browseId) {
  1156. if (user_settings.blacklist_settings[ucid]) {
  1157. container[i].remove();
  1158. }
  1159. }
  1160.  
  1161. }
  1162.  
  1163. },
  1164. getVideos: function () {
  1165.  
  1166. var i;
  1167. var temp;
  1168. var ucid;
  1169. var child;
  1170. var parent;
  1171. var videos;
  1172. var remove;
  1173. var up_next;
  1174.  
  1175. remove = [];
  1176. up_next = document.querySelector("ytd-compact-autoplay-renderer");
  1177. videos = document.querySelectorAll(this.tag_list.join(","));
  1178.  
  1179. for (i = 0; i < videos.length; i++) {
  1180.  
  1181. if (videos[i].data) {
  1182. temp = videos[i];
  1183. }
  1184.  
  1185. if (temp && temp.data) {
  1186.  
  1187. ucid = iridium_api.getObjectByKey(temp.data, ["browseId"], function (string) {
  1188. return string.indexOf("UC") === 0;
  1189. });
  1190.  
  1191. if (ucid[0] && ucid[0].target.browseId) {
  1192. ucid = ucid[0].target.browseId;
  1193. }
  1194.  
  1195. }
  1196.  
  1197. if (ucid) {
  1198. if (user_settings.blacklist_settings[ucid]) {
  1199.  
  1200. if (up_next && up_next.contains(videos[i])) {
  1201.  
  1202. if (up_next.tagName === "YTD-COMPACT-AUTOPLAY-RENDERER") {
  1203. up_next.remove();
  1204. } else {
  1205.  
  1206. up_next.parentNode.remove();
  1207. up_next = document.querySelector(".watch-sidebar-separation-line");
  1208.  
  1209. if (up_next) {
  1210. up_next.remove();
  1211. }
  1212.  
  1213. }
  1214.  
  1215. } else {
  1216. remove.push(videos[i]);
  1217. }
  1218.  
  1219. }
  1220. }
  1221.  
  1222. }
  1223.  
  1224. if (remove.length) {
  1225.  
  1226. for (i = 0; i < remove.length; i++) {
  1227.  
  1228. child = remove[i];
  1229.  
  1230. while (child) {
  1231.  
  1232. parent = child.parentNode;
  1233.  
  1234. if (parent.childElementCount > 1 || parent.id === "contents" || parent.id === "items") {
  1235.  
  1236. child.remove();
  1237. break;
  1238.  
  1239. }
  1240.  
  1241. child = parent;
  1242.  
  1243. }
  1244.  
  1245. }
  1246.  
  1247. if (!this.hasContainers()) {
  1248. window.dispatchEvent(new Event("resize"));
  1249. }
  1250.  
  1251. }
  1252.  
  1253. },
  1254. modImportNode: function (original) {
  1255.  
  1256. var blacklist_button;
  1257.  
  1258. blacklist_button = document.createElement("template");
  1259. blacklist_button.innerHTML =
  1260. "<div class='iri-add-to-blacklist' style='opacity:0'>" +
  1261. " <svg viewBox='0 0 24 24' height='16' width='16'>" +
  1262. " <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'/>" +
  1263. " </svg>" +
  1264. " <div class='iri-tooltip' data-locale='text|button_add_title'></div>" +
  1265. "</div>";
  1266. blacklist_button = blacklist_button.content;
  1267. iridium_api.applyText(blacklist_button, i18n.blacklist_settings);
  1268.  
  1269. return function (externalNode, deep) {
  1270.  
  1271. var node;
  1272. var container;
  1273.  
  1274. if (!user_settings.enable_blacklist) {
  1275. return original.apply(this, arguments);
  1276. }
  1277.  
  1278. node = externalNode.firstElementChild;
  1279.  
  1280. if (node) {
  1281. if ((node.id === "thumbnail" || node.id === "img")) {
  1282.  
  1283. container = node.id === "img" ? node.parentNode : node;
  1284.  
  1285. if (!container.querySelector(".iri-add-to-blacklist")) {
  1286. container.appendChild(blacklist_button.cloneNode(true));
  1287. }
  1288. }
  1289. }
  1290.  
  1291. return original.apply(this, arguments);
  1292.  
  1293. };
  1294.  
  1295. },
  1296. applyBlacklist: function () {
  1297.  
  1298. var hasContainers;
  1299.  
  1300. if (!this.allowedBlacklistPage()) {
  1301. return;
  1302. }
  1303.  
  1304. hasContainers = this.hasContainers();
  1305.  
  1306. if (hasContainers) {
  1307. this.getContainers();
  1308. }
  1309.  
  1310. this.getVideos();
  1311.  
  1312. if (hasContainers) {
  1313. this.getEmptyContainers();
  1314. }
  1315.  
  1316. },
  1317. addToBlacklist: function (event) {
  1318.  
  1319. var i;
  1320. var ucid;
  1321. var brand;
  1322. var parent;
  1323.  
  1324. if (!user_settings.enable_blacklist) {
  1325. return;
  1326. }
  1327.  
  1328. if (event.target.className === "iri-add-to-blacklist") {
  1329.  
  1330. event.preventDefault();
  1331. event.stopPropagation();
  1332.  
  1333. parent = event.target.parentNode;
  1334.  
  1335. while (parent) {
  1336.  
  1337. if (this.tag_list.indexOf(parent.tagName) > -1) {
  1338.  
  1339. if (parent.data) {
  1340.  
  1341. ucid = iridium_api.getObjectByKey(parent.data, ["browseId"], function (string) {
  1342. return string.indexOf("UC") === 0;
  1343. });
  1344.  
  1345. for (i = 0; i < ucid.length; i++) {
  1346. if (ucid[i] && ucid[i].target && ucid[i].target.browseId) {
  1347. if (ucid[i].list && ucid[i].list[0] && ucid[i].list[0].text) {
  1348.  
  1349. brand = ucid[i].list[0].text;
  1350. ucid = ucid[i].target.browseId;
  1351.  
  1352. break;
  1353.  
  1354. }
  1355. }
  1356. }
  1357.  
  1358. }
  1359.  
  1360. break;
  1361.  
  1362. }
  1363.  
  1364. parent = parent.parentNode;
  1365.  
  1366. }
  1367.  
  1368. if (ucid && brand) {
  1369.  
  1370. if (user_settings.blacklist_settings.constructor.name !== "Object") {
  1371. user_settings.blacklist_settings = {};
  1372. }
  1373.  
  1374. user_settings.blacklist_settings[ucid] = brand;
  1375.  
  1376. iridium_api.saveSettings("blacklist_settings");
  1377.  
  1378. this.applyBlacklist();
  1379.  
  1380. }
  1381.  
  1382. return false;
  1383.  
  1384. }
  1385.  
  1386. },
  1387. iniBlacklist: function () {
  1388.  
  1389. if (user_settings.enable_blacklist && this.allowedBlacklistPage()) {
  1390. document.documentElement.classList.add("iri-blacklist-allowed");
  1391. } else {
  1392. document.documentElement.classList.remove("iri-blacklist-allowed");
  1393. }
  1394.  
  1395. },
  1396. ini: function () {
  1397.  
  1398. var context;
  1399. var iniBlacklistListener;
  1400.  
  1401. if (iridium_api.initializeOption.call(this)) {
  1402. return;
  1403. }
  1404.  
  1405. iniBlacklistListener = this.iniBlacklist.bind(this);
  1406.  
  1407. document.addEventListener("readystatechange", iniBlacklistListener, false);
  1408. document.addEventListener("yt-page-data-fetched", iniBlacklistListener, false);
  1409. document.addEventListener("click", this.addToBlacklist.bind(this), true);
  1410.  
  1411. HTMLDocument.prototype.importNode = this.modImportNode(HTMLDocument.prototype.importNode);
  1412.  
  1413. context = this;
  1414.  
  1415. Object.defineProperties(Object.prototype, {
  1416. ytInitialData: {
  1417. set: function (data) {
  1418. this._ytInitialData = data;
  1419. },
  1420. get: function () {
  1421.  
  1422. if (user_settings.enable_blacklist && context.allowedBlacklistPage()) {
  1423. context.clearList(this._ytInitialData);
  1424. }
  1425.  
  1426. return this._ytInitialData;
  1427.  
  1428. }
  1429. },
  1430. onDone: {
  1431. set: function (data) {
  1432. this._onDone = data;
  1433. },
  1434. get: function () {
  1435.  
  1436. if (user_settings.enable_blacklist && context.allowedBlacklistPage()) {
  1437. return context.modOnDone(this._onDone);
  1438. }
  1439.  
  1440. return this._onDone;
  1441.  
  1442. }
  1443. }
  1444. });
  1445.  
  1446. }
  1447. },
  1448. {
  1449. options: {
  1450. channel_video_count: {
  1451. id: "channel_video_count",
  1452. section: "video",
  1453. sub_section: "general",
  1454. type: "checkbox",
  1455. value: true,
  1456. i18n: {
  1457. label: "Display uploaded videos number"
  1458. }
  1459. },
  1460. channel_video_time: {
  1461. id: "channel_video_time",
  1462. section: "video",
  1463. sub_section: "general",
  1464. type: "checkbox",
  1465. value: true,
  1466. i18n: {
  1467. label: "Display how long the video was uploaded"
  1468. }
  1469. }
  1470. },
  1471. removeVideoCount: function (listener) {
  1472.  
  1473. var xhr;
  1474. var video_count;
  1475. var video_count_dot;
  1476.  
  1477. delete this.addVideoCount.fetching;
  1478.  
  1479. document.removeEventListener("yt-navigate-finish", listener, false);
  1480.  
  1481. xhr = this.removeVideoCount.xhr;
  1482.  
  1483. if (xhr && xhr.abort) {
  1484.  
  1485. xhr.abort();
  1486.  
  1487. delete this.removeVideoCount.xhr;
  1488.  
  1489. }
  1490.  
  1491. if ((video_count_dot = document.querySelector("span.iri-video-count"))) {
  1492. video_count_dot.remove();
  1493. }
  1494.  
  1495. if ((video_count = document.getElementById("iri-video-count"))) {
  1496. video_count.remove();
  1497. }
  1498.  
  1499. },
  1500. addVideoCount: function (channel_url, event) {
  1501.  
  1502. var i;
  1503. var page_data;
  1504. var count_match;
  1505. var script_list;
  1506. var video_count;
  1507. var playlist_data;
  1508. var video_count_dot;
  1509. var owner_container;
  1510.  
  1511. delete this.addVideoCount.fetching;
  1512.  
  1513. script_list = event.target ? event.target.response.querySelectorAll("script") : [];
  1514.  
  1515. for (i = 0; i < script_list.length; i++) {
  1516. if ((page_data = script_list[i].textContent.match(/window\["ytInitialData"] = ({[\w\W]+});/))) {
  1517. if ((page_data = JSON.parse(page_data[1], null, true))) {
  1518.  
  1519. playlist_data = iridium_api.getObjectByKey(page_data.sidebar, ["playlistSidebarPrimaryInfoRenderer"]);
  1520.  
  1521. for (i = 0; i < playlist_data.length; i++) {
  1522. if (iridium_api.checkIfExists("target.playlistSidebarPrimaryInfoRenderer.stats", playlist_data[i])) {
  1523.  
  1524. count_match = iridium_api.getObjectByKey(playlist_data[i].target.playlistSidebarPrimaryInfoRenderer.stats, ["text", "simpleText"]);
  1525.  
  1526. if (count_match.length > 0 && (owner_container = document.getElementById("owner-container"))) {
  1527.  
  1528. count_match = count_match[0].target.text || count_match[0].target.simpleText;
  1529.  
  1530. video_count_dot = document.createElement("span");
  1531. video_count_dot.textContent = " · ";
  1532. video_count_dot.className = "iri-video-count";
  1533.  
  1534. video_count = document.createElement("a");
  1535. video_count.id = "iri-video-count";
  1536. video_count.textContent = count_match;
  1537. video_count.className = "yt-simple-endpoint iri-video-count";
  1538. video_count.setAttribute("href", channel_url + "/videos");
  1539. video_count.data = {
  1540. commandMetadata: {
  1541. webCommandMetadata: {
  1542. url: channel_url + "/videos"
  1543. }
  1544. },
  1545. urlEndpoint: {
  1546. url: channel_url + "/videos"
  1547. }
  1548. };
  1549.  
  1550. owner_container.appendChild(video_count_dot);
  1551. owner_container.appendChild(video_count);
  1552.  
  1553. owner_container.channel_url = channel_url;
  1554. owner_container.video_count = count_match;
  1555.  
  1556. }
  1557.  
  1558. break;
  1559.  
  1560. }
  1561. }
  1562.  
  1563. break;
  1564.  
  1565. }
  1566. }
  1567. }
  1568.  
  1569. },
  1570. removeVideoTime: function (listener) {
  1571.  
  1572. var xhr;
  1573. var time_container;
  1574.  
  1575. delete this.addVideoTime.fetching;
  1576.  
  1577. document.removeEventListener("yt-navigate-finish", listener, false);
  1578.  
  1579. xhr = this.removeVideoTime.xhr;
  1580.  
  1581. if (xhr && xhr.abort) {
  1582.  
  1583. xhr.abort();
  1584.  
  1585. delete this.removeVideoTime.xhr;
  1586.  
  1587. }
  1588.  
  1589. if ((time_container = document.getElementById("iri-video-time"))) {
  1590. time_container.remove();
  1591. }
  1592.  
  1593. },
  1594. addVideoTime: function (published_date, event) {
  1595.  
  1596. var i;
  1597. var page_data;
  1598. var video_data;
  1599. var script_list;
  1600. var time_container;
  1601.  
  1602. delete this.addVideoTime.fetching;
  1603.  
  1604. script_list = event.target.response.querySelectorAll("script");
  1605.  
  1606. for (i = 0; i < script_list.length; i++) {
  1607. if ((page_data = script_list[i].textContent.match(/window\["ytInitialData"] = ({[\w\W]+});/))) {
  1608. if ((page_data = JSON.parse(page_data[1], null, true))) {
  1609.  
  1610. video_data = iridium_api.getObjectByKey(page_data.contents, ["videoId"], function (video_id, obj) {
  1611.  
  1612. var current_video_id;
  1613.  
  1614. if (obj && obj.publishedTimeText) {
  1615. if ((current_video_id = window.location.href.match(iridium_api.videoIdPattern))) {
  1616. if ((current_video_id = current_video_id[1])) {
  1617. return video_id === current_video_id;
  1618. }
  1619. }
  1620. }
  1621.  
  1622. });
  1623.  
  1624. for (i = 0; i < video_data.length; i++) {
  1625. if (video_data[i].target.publishedTimeText && video_data[i].target.publishedTimeText.simpleText) {
  1626.  
  1627. time_container = document.createElement("span");
  1628. time_container.id = "iri-video-time";
  1629. time_container.textContent = " · " + video_data[i].target.publishedTimeText.simpleText;
  1630.  
  1631. published_date.appendChild(time_container);
  1632.  
  1633. break;
  1634.  
  1635. }
  1636. }
  1637.  
  1638. break;
  1639.  
  1640. }
  1641. }
  1642. }
  1643.  
  1644. },
  1645. loadStart: function () {
  1646.  
  1647. var xhr;
  1648. var context;
  1649. var video_id;
  1650. var channel_id;
  1651. var channel_url;
  1652. var upload_info;
  1653. var watch_page_active;
  1654.  
  1655. watch_page_active = document.querySelector("ytd-watch:not([hidden]), ytd-watch-flexy:not([hidden])");
  1656.  
  1657. if (watch_page_active) {
  1658.  
  1659. if ((channel_url = document.querySelector("#owner-name a"))) {
  1660.  
  1661. channel_url = channel_url.getAttribute("href").split(/\/videos/)[0];
  1662. channel_id = channel_url.match(/UC([a-z0-9-_]{22})/i);
  1663.  
  1664. } else if ((channel_id = iridium_api.getSingleObjectByKey(window.ytplayer, "ucid"))) {
  1665.  
  1666. channel_url = "/channel/" + channel_id;
  1667. channel_id = channel_url.match(/UC([a-z0-9-_]{22})/i);
  1668.  
  1669. } else {
  1670.  
  1671. return;
  1672.  
  1673. }
  1674.  
  1675. if (channel_id && (channel_id = channel_id[1])) {
  1676. if (user_settings.channel_video_count) {
  1677. if (!this.addVideoCount.fetching) {
  1678. if (document.getElementById("owner-container")) {
  1679. if (!document.getElementById("iri-video-count")) {
  1680.  
  1681. if (this.removeVideoCount.xhr) {
  1682. this.removeVideoCount.xhr.abort();
  1683. }
  1684.  
  1685. this.addVideoCount.fetching = true;
  1686.  
  1687. xhr = new XMLHttpRequest();
  1688. xhr.addEventListener("load", this.addVideoCount.bind(this, channel_url));
  1689. xhr.open("GET", "/playlist?list=UU" + channel_id, true);
  1690. xhr.responseType = "document";
  1691. xhr.send();
  1692.  
  1693. this.removeVideoCount.xhr = xhr;
  1694.  
  1695. context = this;
  1696.  
  1697. document.addEventListener("yt-navigate-finish", function listener() {
  1698. context.removeVideoCount(listener);
  1699. }, false);
  1700.  
  1701. }
  1702. }
  1703. }
  1704. }
  1705.  
  1706. if (user_settings.channel_video_time) {
  1707. if (!this.addVideoTime.fetching) {
  1708. if ((upload_info = document.querySelector("#upload-info .date"))) {
  1709. if (upload_info.textContent.indexOf("·") === -1) {
  1710. if ((video_id = window.location.href.match(iridium_api.videoIdPattern)) && (video_id = video_id[1])) {
  1711.  
  1712. if (this.removeVideoTime.xhr) {
  1713. this.removeVideoTime.xhr.abort();
  1714. }
  1715.  
  1716. this.addVideoTime.fetching = true;
  1717.  
  1718. xhr = new XMLHttpRequest();
  1719. xhr.addEventListener("load", this.addVideoTime.bind(this, upload_info));
  1720. xhr.open("GET", "/channel/UC" + channel_id + "/search?query=%22" + video_id + "%22", true);
  1721. xhr.responseType = "document";
  1722. xhr.send();
  1723.  
  1724. this.removeVideoTime.xhr = xhr;
  1725.  
  1726. context = this;
  1727.  
  1728. document.addEventListener("yt-navigate-finish", function listener() {
  1729. context.removeVideoTime(listener);
  1730. }, false);
  1731.  
  1732. }
  1733. }
  1734. }
  1735. }
  1736. }
  1737. }
  1738.  
  1739. }
  1740.  
  1741. },
  1742. ini: function () {
  1743.  
  1744. if (iridium_api.initializeOption.call(this)) {
  1745. return;
  1746. }
  1747.  
  1748. window.addEventListener("yt-page-data-updated", this.loadStart.bind(this), true);
  1749.  
  1750. }
  1751. },
  1752. {
  1753. options: {
  1754. playlist_reverse_control: {
  1755. id: "playlist_reverse_control",
  1756. section: "video",
  1757. sub_section: "playlist",
  1758. type: "checkbox",
  1759. value: true,
  1760. i18n: {
  1761. label: "Enable reverse playlist control",
  1762. button_label: "Reverse playlist",
  1763. toggle_on: "Reverse is on",
  1764. toggle_off: "Reverse is off"
  1765. }
  1766. }
  1767. },
  1768. reversePlaylist: function () {
  1769.  
  1770. var i;
  1771. var autoplay;
  1772. var playlist;
  1773. var ytd_watch;
  1774. var yt_navigation_manager;
  1775. var twoColumnWatchNextResults;
  1776.  
  1777. if ((ytd_watch = document.querySelector("ytd-watch, ytd-watch-flexy"))) {
  1778. if (ytd_watch.data) {
  1779. if ((twoColumnWatchNextResults = iridium_api.getSingleObjectByKey(ytd_watch.data, ["twoColumnWatchNextResults"]))) {
  1780. if ("playlist" in twoColumnWatchNextResults && "playlist" in (playlist = twoColumnWatchNextResults["playlist"])) {
  1781. if ("contents" in (playlist = playlist["playlist"])) {
  1782.  
  1783. playlist["contents"].reverse();
  1784.  
  1785. if ("currentIndex" in playlist) {
  1786. playlist["currentIndex"] = playlist["totalVideos"] - playlist["currentIndex"] - 1;
  1787. }
  1788.  
  1789. if ("localCurrentIndex" in playlist) {
  1790. playlist["localCurrentIndex"] = playlist["contents"].length - playlist["localCurrentIndex"] - 1;
  1791. }
  1792.  
  1793. if ("autoplay" in twoColumnWatchNextResults && "autoplay" in (autoplay = twoColumnWatchNextResults["autoplay"])) {
  1794. if ("sets" in (autoplay = autoplay["autoplay"])) {
  1795. for (i = 0; i < autoplay["sets"].length; i++) {
  1796. if (autoplay["sets"][i]["previousButtonVideo"] && autoplay["sets"][i]["nextButtonVideo"]) {
  1797.  
  1798. autoplay["sets"][i]["autoplayVideo"] = autoplay["sets"][i]["previousButtonVideo"];
  1799. autoplay["sets"][i]["previousButtonVideo"] = autoplay["sets"][i]["nextButtonVideo"];
  1800. autoplay["sets"][i]["nextButtonVideo"] = autoplay["sets"][i]["autoplayVideo"];
  1801.  
  1802. }
  1803. }
  1804. }
  1805. }
  1806.  
  1807. if ("updatePageData_" in ytd_watch) {
  1808. ytd_watch["updatePageData_"](JSON.parse(JSON.stringify(ytd_watch.data)));
  1809. }
  1810.  
  1811. // timeout temporary workaround for playlist buttons ui not updating after first video changes
  1812. window.setTimeout(function () {
  1813. if ((yt_navigation_manager = document.querySelector("yt-navigation-manager"))) {
  1814. if ("updatePlayerComponents_" in yt_navigation_manager) {
  1815. yt_navigation_manager["updatePlayerComponents_"](null, autoplay, null, playlist);
  1816. }
  1817. }
  1818. }, 500);
  1819.  
  1820. }
  1821. }
  1822. }
  1823. }
  1824. }
  1825.  
  1826. },
  1827. reverseButtonToggled: function (event) {
  1828. if (event.target.data.isReverseButton) {
  1829.  
  1830. user_settings.playlist_reverse = event.target.data.isToggled;
  1831. iridium_api.saveSettings("playlist_reverse");
  1832. this.reversePlaylist();
  1833.  
  1834. }
  1835. },
  1836. setReverseButtonData: function () {
  1837.  
  1838. var defaultLabel;
  1839. var toggledLabel;
  1840. var notificationActionRenderer;
  1841.  
  1842. this["defaultIcon"].iconType = "REVERSE";
  1843. this["accessibility"].label = i18n.playlist_reverse_control.button_label;
  1844. this.defaultTooltip = i18n.playlist_reverse_control.button_label;
  1845. this.toggledTooltip = i18n.playlist_reverse_control.button_label;
  1846. this.isToggled = user_settings.playlist_reverse;
  1847. this.isReverseButton = true;
  1848.  
  1849. if ((defaultLabel = iridium_api.getObjectByKey(this["defaultServiceEndpoint"], ["text"]))) {
  1850. if (defaultLabel.length) {
  1851. defaultLabel[0].target.text = i18n.playlist_reverse_control.toggle_on;
  1852. }
  1853. }
  1854.  
  1855. if ((toggledLabel = iridium_api.getObjectByKey(this["toggledServiceEndpoint"], ["text"]))) {
  1856. if (toggledLabel.length) {
  1857. toggledLabel[0].target.text = i18n.playlist_reverse_control.toggle_off;
  1858. }
  1859. }
  1860.  
  1861. if ((defaultLabel = iridium_api.getObjectByKey(this["defaultServiceEndpoint"], ["simpleText"]))) {
  1862. if (defaultLabel.length) {
  1863. defaultLabel[0].target.simpleText = i18n.playlist_reverse_control.toggle_on;
  1864. }
  1865. }
  1866.  
  1867. if ((toggledLabel = iridium_api.getObjectByKey(this["toggledServiceEndpoint"], ["simpleText"]))) {
  1868. if (toggledLabel.length) {
  1869. toggledLabel[0].target.simpleText = i18n.playlist_reverse_control.toggle_off;
  1870. }
  1871. }
  1872.  
  1873. },
  1874. buildReverseButton: function (data) {
  1875.  
  1876. var i;
  1877. var reversePlaylistButton;
  1878.  
  1879. for (i = 0; i < data.length; i++) {
  1880. if ("toggleButtonRenderer" in data[i] && data[i]["toggleButtonRenderer"].isReverseButton) {
  1881. return;
  1882. }
  1883. }
  1884.  
  1885. reversePlaylistButton = JSON.parse(JSON.stringify(data[0]));
  1886.  
  1887. data.push(reversePlaylistButton);
  1888. this.setReverseButtonData.apply(reversePlaylistButton["toggleButtonRenderer"]);
  1889.  
  1890. if (!this.reverseButtonToggledListener) {
  1891.  
  1892. this.reverseButtonToggledListener = this.reverseButtonToggled.bind(this);
  1893.  
  1894. window.addEventListener("yt-toggle-button", this.reverseButtonToggledListener, false);
  1895.  
  1896. }
  1897.  
  1898. if (user_settings.playlist_reverse) {
  1899. this.reversePlaylist();
  1900. }
  1901.  
  1902. },
  1903. createReverseButton: function () {
  1904.  
  1905. var path;
  1906.  
  1907. this.id = "reverse";
  1908.  
  1909. if ((path = this.querySelector("path"))) {
  1910. path.setAttribute("d", "M6 21l-4-4h3V5h2v12h3L6 21z M19 7v12h-2V7h-3l4-4l4 4H19z");
  1911. }
  1912.  
  1913. },
  1914. modSetMenuData: function (original) {
  1915.  
  1916. var context = this;
  1917.  
  1918. return function (data) {
  1919.  
  1920. var playlistButtons;
  1921. var topLevelButtons;
  1922.  
  1923. if (!data || !data["playlistButtons"]) {
  1924. return original.apply(this, arguments);
  1925. }
  1926.  
  1927. if ((playlistButtons = iridium_api.getSingleObjectByKey(data, ["playlistButtons"]))) {
  1928. if ((topLevelButtons = iridium_api.getSingleObjectByKey(playlistButtons, ["topLevelButtons"]))) {
  1929. context.buildReverseButton(topLevelButtons);
  1930. }
  1931. }
  1932.  
  1933. return original.apply(this, arguments);
  1934.  
  1935. };
  1936.  
  1937. },
  1938. interceptYtIcons: function () {
  1939.  
  1940. var loopIcon;
  1941. var reverseIcon;
  1942.  
  1943. if ((loopIcon = document.querySelector("g#loop"))) {
  1944.  
  1945. reverseIcon = loopIcon.cloneNode(true);
  1946.  
  1947. this.createReverseButton.apply(reverseIcon);
  1948. loopIcon.parentNode.appendChild(reverseIcon);
  1949.  
  1950. document.removeEventListener("readystatechange", this.interceptListener, true);
  1951.  
  1952. }
  1953.  
  1954. },
  1955. interceptImport: function (data) {
  1956.  
  1957. var iconSet;
  1958. var reverseIcon;
  1959.  
  1960. if (data.target.tagName === "LINK" && data.target.rel === "import" && data.target.getAttribute("name")) {
  1961. if ((reverseIcon = data.target.import.querySelector("#loop"))) {
  1962.  
  1963. iconSet = reverseIcon.parentElement;
  1964. reverseIcon = reverseIcon.cloneNode(true);
  1965.  
  1966. this.createReverseButton.apply(reverseIcon);
  1967. iconSet.appendChild(reverseIcon);
  1968.  
  1969. document.documentElement.removeEventListener("load", this.interceptListener, true);
  1970.  
  1971. this.interceptListener = null;
  1972.  
  1973. }
  1974. }
  1975.  
  1976. },
  1977. ini: function () {
  1978.  
  1979. var context;
  1980.  
  1981. if (iridium_api.initializeOption.call(this)) {
  1982. return;
  1983. }
  1984.  
  1985. if ("import" in document.createElement("link")) {
  1986.  
  1987. this.interceptListener = this.interceptImport.bind(this);
  1988.  
  1989. document.documentElement.addEventListener("load", this.interceptListener, true);
  1990.  
  1991. } else {
  1992.  
  1993. this.interceptListener = this.interceptYtIcons.bind(this);
  1994.  
  1995. document.addEventListener("readystatechange", this.interceptListener, true);
  1996. this.interceptYtIcons();
  1997.  
  1998. }
  1999.  
  2000. context = this;
  2001.  
  2002. Object.defineProperties(Object.prototype, {
  2003. setMenuData_: {
  2004. set: function (data) {
  2005. this._setMenuData_ = data;
  2006. },
  2007. get: function () {
  2008.  
  2009. if (this._setMenuData_) {
  2010. return context.modSetMenuData(this._setMenuData_);
  2011. }
  2012.  
  2013. return this._setMenuData_;
  2014. }
  2015. }
  2016. });
  2017.  
  2018. }
  2019. },
  2020. {
  2021. options: {
  2022. player_quality: {
  2023. id: "player_quality",
  2024. section: "video",
  2025. sub_section: "player",
  2026. type: "dropdown",
  2027. value: "auto",
  2028. i18n: {
  2029. label: "Default video quality:",
  2030. options: [
  2031. "4320p (8k)",
  2032. "2880p (5k)",
  2033. "2160p (4k)",
  2034. "1440p",
  2035. "1080p",
  2036. "720p",
  2037. "480p",
  2038. "360p",
  2039. "240p",
  2040. "144p",
  2041. "Auto"
  2042. ]
  2043. },
  2044. options: [
  2045. "highres",
  2046. "hd2880",
  2047. "hd2160",
  2048. "hd1440",
  2049. "hd1080",
  2050. "hd720",
  2051. "large",
  2052. "medium",
  2053. "small",
  2054. "tiny",
  2055. "auto"
  2056. ]
  2057. },
  2058. player_auto_play: {
  2059. id: "player_auto_play",
  2060. section: "video",
  2061. sub_section: "player",
  2062. type: "checkbox",
  2063. value: false,
  2064. i18n: {
  2065. label: "Play videos automatically"
  2066. }
  2067. },
  2068. channel_trailer_auto_play: {
  2069. id: "channel_trailer_auto_play",
  2070. section: "video",
  2071. sub_section: "channel",
  2072. type: "checkbox",
  2073. value: false,
  2074. i18n: {
  2075. label: "Play channel trailers automatically"
  2076. }
  2077. },
  2078. player_annotations: {
  2079. id: "player_annotations",
  2080. section: "video",
  2081. sub_section: "player",
  2082. type: "checkbox",
  2083. value: false,
  2084. i18n: {
  2085. label: "Allow annotations on videos"
  2086. }
  2087. },
  2088. player_subtitles: {
  2089. id: "player_subtitles",
  2090. section: "video",
  2091. sub_section: "player",
  2092. type: "checkbox",
  2093. value: false,
  2094. i18n: {
  2095. label: "Allow subtitles on videos"
  2096. }
  2097. },
  2098. player_loudness: {
  2099. id: "player_loudness",
  2100. section: "video",
  2101. sub_section: "player",
  2102. type: "checkbox",
  2103. value: false,
  2104. i18n: {
  2105. label: "Allow loudness normalization"
  2106. }
  2107. },
  2108. player_ads: {
  2109. id: "player_ads",
  2110. section: "video",
  2111. sub_section: "player",
  2112. type: "checkbox",
  2113. value: false,
  2114. i18n: {
  2115. label: "Allow ads on videos"
  2116. }
  2117. },
  2118. subscribed_channel_player_ads: {
  2119. id: "subscribed_channel_player_ads",
  2120. section: "video",
  2121. sub_section: "player",
  2122. type: "checkbox",
  2123. value: false,
  2124. i18n: {
  2125. label: "Allow ads only on videos of subscribed channels"
  2126. }
  2127. },
  2128. player_hfr: {
  2129. id: "player_hfr",
  2130. section: "video",
  2131. sub_section: "player",
  2132. type: "checkbox",
  2133. value: true,
  2134. i18n: {
  2135. label: "Allow HFR (60fps) streams"
  2136. }
  2137. },
  2138. player_memorize_size: {
  2139. id: "player_memorize_size",
  2140. section: "video",
  2141. sub_section: "player",
  2142. type: "checkbox",
  2143. value: true,
  2144. i18n: {
  2145. label: "Memorize player size"
  2146. }
  2147. },
  2148. player_memorize_volume: {
  2149. id: "player_memorize_volume",
  2150. section: "video",
  2151. sub_section: "player",
  2152. type: "checkbox",
  2153. value: true,
  2154. i18n: {
  2155. label: "Memorize player volume"
  2156. }
  2157. },
  2158. player_max_res_thumbnail: {
  2159. id: "player_max_res_thumbnail",
  2160. section: "video",
  2161. sub_section: "player",
  2162. type: "checkbox",
  2163. value: true,
  2164. i18n: {
  2165. label: "Force high quality player thumbnail"
  2166. }
  2167. }
  2168. },
  2169. modArgs: function (args) {
  2170.  
  2171. var i;
  2172. var fps;
  2173. var list;
  2174. var key_type;
  2175. var player_response;
  2176. var thumbnail_image;
  2177.  
  2178. if (args.controls !== "0") {
  2179.  
  2180. if (!iridium_api.isPopUpPlayer && this.isChannel() ? !user_settings.channel_trailer_auto_play : !user_settings.player_auto_play) {
  2181.  
  2182. args.autoplay = "0";
  2183. args.suppress_autoplay_on_watch = true;
  2184. args.fflags = args.fflags.replace(/html5_new_autoplay_redux=true/g, "html5_new_autoplay_redux=false");
  2185.  
  2186. }
  2187.  
  2188. }
  2189.  
  2190. if (user_settings.player_max_res_thumbnail) {
  2191. if (args.eventid && args.thumbnail_url) {
  2192.  
  2193. args.iurlmaxres = args.thumbnail_url.replace(/\/[^\/]+$/, "/maxresdefault.jpg");
  2194.  
  2195. thumbnail_image = new Image();
  2196.  
  2197. thumbnail_image.addEventListener("load", this.checkHighQualityThumbnail.bind(this, args.iurlmaxres), false);
  2198.  
  2199. thumbnail_image.src = args.iurlmaxres;
  2200. thumbnail_image = null;
  2201.  
  2202. }
  2203. }
  2204.  
  2205. if (user_settings.subscribed_channel_player_ads ? args.subscribed !== "1" : !user_settings.player_ads) {
  2206.  
  2207. delete args.ad3_module;
  2208.  
  2209. if (args.player_response) {
  2210.  
  2211. player_response = JSON.parse(args.player_response);
  2212.  
  2213. if (player_response && player_response.adPlacements) {
  2214.  
  2215. delete player_response.adPlacements;
  2216. args.player_response = JSON.stringify(player_response);
  2217.  
  2218. }
  2219.  
  2220. }
  2221.  
  2222. }
  2223.  
  2224. if (!user_settings.player_annotations) {
  2225. args.iv_load_policy = "3";
  2226. }
  2227.  
  2228. if (!user_settings.player_loudness) {
  2229.  
  2230. args.loudness = null;
  2231. args.relative_loudness = null;
  2232.  
  2233. delete args.loudness;
  2234. delete args.relative_loudness;
  2235.  
  2236. }
  2237.  
  2238. if (!user_settings.player_subtitles) {
  2239.  
  2240. iridium_api.setStorage(
  2241. "yt-html5-player-modules::subtitlesModuleData::module-enabled",
  2242. "false"
  2243. );
  2244.  
  2245. if (args.caption_audio_tracks) {
  2246. args.caption_audio_tracks = args.caption_audio_tracks.split(/&d=[0-9]|d=[0-9]&/).join("");
  2247. }
  2248.  
  2249. }
  2250.  
  2251. if (!user_settings.player_hfr && args.adaptive_fmts) {
  2252.  
  2253. key_type = args.adaptive_fmts.indexOf(",") > -1 ? "," : "%2C";
  2254. list = args.adaptive_fmts.split(key_type);
  2255.  
  2256. for (i = 0; i < list.length; i++) {
  2257.  
  2258. fps = list[i].split(/fps(?:=|%3D)([0-9]{2})/);
  2259. fps = fps && fps[1];
  2260.  
  2261. if (fps > 30) {
  2262. list.splice(i--, 1);
  2263. }
  2264.  
  2265. }
  2266.  
  2267. args.adaptive_fmts = list.join(key_type);
  2268.  
  2269. }
  2270.  
  2271. if (iridium_api.isPopUpPlayer) {
  2272. args.el = "embedded";
  2273. }
  2274.  
  2275. },
  2276. modVideoByPlayerVars: function (original) {
  2277.  
  2278. var context = this;
  2279.  
  2280. return function (args) {
  2281.  
  2282. var temp;
  2283. var current_config;
  2284. var current_video_id;
  2285.  
  2286. if (!this.getUpdatedConfigurationData || !args.eventid || iridium_api.isPopUpPlayer) {
  2287. return original.apply(this, arguments);
  2288. }
  2289.  
  2290. current_config = this.getUpdatedConfigurationData();
  2291.  
  2292. if (current_config && current_config.args && !args.cue_player) {
  2293.  
  2294. context.updatePlayerLayout = !!current_config.args.list !== !!args.list;
  2295.  
  2296. if ((current_config.args.eventid === args.eventid || current_config.args.loaderUrl === args.loaderUrl)) {
  2297. if (!document.querySelector(".ended-mode,.unstarted-mode") && (current_video_id = window.location.href.match(iridium_api.videoIdPattern))) {
  2298. if (current_video_id[1] === current_config.args.video_id) {
  2299. return function () {
  2300. };
  2301. }
  2302. }
  2303. }
  2304.  
  2305. }
  2306.  
  2307. context.modArgs(args);
  2308.  
  2309. temp = original.apply(this, arguments);
  2310.  
  2311. if (user_settings.player_quality !== "auto") {
  2312. context.markedForQuality = true;
  2313. }
  2314.  
  2315. context.setPlayerResize();
  2316.  
  2317. return temp;
  2318.  
  2319. };
  2320.  
  2321. },
  2322. modPlayerLoad: function (original) {
  2323.  
  2324. var context = this;
  2325.  
  2326. return function (api_name, config) {
  2327.  
  2328. var temp;
  2329.  
  2330. context.modArgs(config.args);
  2331.  
  2332. temp = original.apply(this, arguments);
  2333.  
  2334. if (user_settings.player_quality !== "auto") {
  2335. context.markedForQuality = true;
  2336. }
  2337.  
  2338. context.setPlayerResize();
  2339.  
  2340. return temp;
  2341.  
  2342. };
  2343.  
  2344. },
  2345. modJSONParse: function (original) {
  2346.  
  2347. var context = this;
  2348.  
  2349. return function (text, reviver, bypass) {
  2350.  
  2351. var temp = original.apply(this, arguments);
  2352.  
  2353. if (!bypass && temp && temp.player && temp.player.args) {
  2354. context.modArgs(temp.player.args);
  2355. }
  2356.  
  2357. return temp;
  2358.  
  2359. };
  2360.  
  2361. },
  2362. modOpen: function (original) {
  2363.  
  2364. var context = this;
  2365.  
  2366. return function (method, url) {
  2367.  
  2368. if (url.match("get_video_info") && !url.match("el=adunit") && !url.match("ps=gaming")) {
  2369. this.addEventListener("readystatechange", context.patchXHR.bind(context));
  2370. }
  2371.  
  2372. return original.apply(this, arguments);
  2373.  
  2374. };
  2375.  
  2376. },
  2377. modParseFromString: function (original) {
  2378.  
  2379. return function () {
  2380.  
  2381. var i;
  2382. var fps;
  2383. var result;
  2384. var streams;
  2385.  
  2386. if (!user_settings.player_hfr) {
  2387.  
  2388. result = original.apply(this, arguments);
  2389. streams = result.getElementsByTagName("Representation");
  2390. i = streams.length;
  2391.  
  2392. while (i--) {
  2393.  
  2394. fps = streams[i].getAttribute("frameRate");
  2395.  
  2396. if (fps > 30) {
  2397. streams[i].remove();
  2398. }
  2399.  
  2400. }
  2401.  
  2402. return result;
  2403.  
  2404. }
  2405.  
  2406. return original.apply(this, arguments);
  2407.  
  2408. };
  2409.  
  2410. },
  2411. setPlayerResize: function () {
  2412.  
  2413. var watch_page_api;
  2414.  
  2415. if (user_settings.player_memorize_size && window.location.pathname === "/watch" && (watch_page_api = document.querySelector("ytd-watch, ytd-watch-flexy"))) {
  2416. try {
  2417. watch_page_api["theaterModeChanged_"](user_settings.theaterMode);
  2418. } catch (ignore) {
  2419. }
  2420. }
  2421.  
  2422. },
  2423. setQuality: function (player_api, quality) {
  2424.  
  2425. var position;
  2426. var max_position;
  2427. var available_qualities;
  2428.  
  2429. if (player_api.getAvailableQualityLevels && (available_qualities = player_api.getAvailableQualityLevels())) {
  2430.  
  2431. if (available_qualities.length === 0) {
  2432. return;
  2433. }
  2434.  
  2435. this.markedForQuality = false;
  2436.  
  2437. if (available_qualities.indexOf(quality) > -1) {
  2438.  
  2439. player_api.setPlaybackQuality(quality);
  2440. player_api.setPlaybackQualityRange(quality);
  2441.  
  2442. } else {
  2443.  
  2444. if ((position = this.options.player_quality.options.indexOf(quality)) > -1) {
  2445.  
  2446. max_position = this.options.player_quality.options.indexOf(available_qualities[0]);
  2447.  
  2448. if (position <= max_position) {
  2449.  
  2450. player_api.setPlaybackQuality(available_qualities[0]);
  2451. player_api.setPlaybackQualityRange(available_qualities[0]);
  2452.  
  2453. } else {
  2454.  
  2455. player_api.setPlaybackQuality(available_qualities[available_qualities.length - 2]);
  2456. player_api.setPlaybackQualityRange(available_qualities[available_qualities.length - 2]);
  2457.  
  2458. }
  2459.  
  2460. }
  2461.  
  2462. }
  2463.  
  2464. }
  2465.  
  2466. },
  2467. checkHighQualityThumbnail: function (thumbnail_url, event) {
  2468.  
  2469. var style_element;
  2470. var thumbnail_container;
  2471.  
  2472. if (event.target.width < 121 && (thumbnail_container = document.querySelector(".ytp-cued-thumbnail-overlay-image"))) {
  2473.  
  2474. if (!(style_element = document.getElementById("style-thumbnail"))) {
  2475.  
  2476. style_element = document.createElement("style");
  2477. style_element.id = "style-thumbnail";
  2478.  
  2479. thumbnail_container.parentNode.insertBefore(style_element, thumbnail_container);
  2480.  
  2481. }
  2482.  
  2483. style_element.textContent =
  2484. ".ytp-cued-thumbnail-overlay-image {" +
  2485. " background-image:url('" + thumbnail_url.replace("maxresdefault", "mqdefault") + "') !important;" +
  2486. "}";
  2487.  
  2488. } else if ((style_element = document.getElementById("style-thumbnail"))) {
  2489. style_element.textContent = "";
  2490. }
  2491.  
  2492. },
  2493. patchXHR: function (event) {
  2494.  
  2495. var i;
  2496. var temp;
  2497. var temp_list;
  2498. var key_value;
  2499.  
  2500. if (event.target.readyState === 4 && event.target.responseText.match(/eventid=/)) {
  2501.  
  2502. temp_list = {};
  2503. temp = event.target.responseText.split("&");
  2504.  
  2505. for (i = 0; i < temp.length; i++) {
  2506.  
  2507. key_value = temp[i].split("=");
  2508. key_value[1] = key_value[1] === undefined ? "" : key_value[1].replace(/\+/g, "%20");
  2509. temp_list[key_value[0]] = window.decodeURIComponent(key_value[1]);
  2510.  
  2511. }
  2512.  
  2513. this.modArgs(temp_list);
  2514.  
  2515. Object.defineProperty(event.target, "responseText", {writable: true});
  2516.  
  2517. event.target.responseText = "";
  2518. temp = Object.keys(temp_list);
  2519.  
  2520. for (i = 0; i < temp.length; i++) {
  2521.  
  2522. event.target.responseText += temp[i] + "=" + window.encodeURIComponent(temp_list[temp[i]]).replace(/%20/g, "+");
  2523.  
  2524. if (i + 1 < temp.length) {
  2525. event.target.responseText += "&";
  2526. }
  2527.  
  2528. }
  2529.  
  2530. if (user_settings.player_quality !== "auto") {
  2531. this.markedForQuality = true;
  2532. }
  2533.  
  2534. this.setPlayerResize();
  2535.  
  2536. }
  2537.  
  2538. },
  2539. interceptHooks: function (event) {
  2540.  
  2541. if (iridium_api.checkIfExists("yt.player.Application.create")) {
  2542.  
  2543. window.yt.player.Application.create = this.modPlayerLoad(window.yt.player.Application.create);
  2544.  
  2545. document.documentElement.removeEventListener("load", this.fileLoadListener, true);
  2546.  
  2547. this.fileLoadListener = null;
  2548.  
  2549. }
  2550.  
  2551. },
  2552. handleCustoms: function (event) {
  2553.  
  2554. if (typeof event === "object") {
  2555. if (user_settings.player_memorize_volume && user_settings.userVolume !== event.volume) {
  2556.  
  2557. user_settings.userVolume = event.volume;
  2558.  
  2559. iridium_api.saveSettings("userVolume");
  2560.  
  2561. }
  2562. } else {
  2563. if (user_settings.player_memorize_size && user_settings.theaterMode !== event) {
  2564.  
  2565. user_settings.theaterMode = event;
  2566.  
  2567. iridium_api.saveSettings("theaterMode");
  2568.  
  2569. }
  2570. }
  2571.  
  2572. },
  2573. onStateChange: function (event) {
  2574.  
  2575. var player;
  2576.  
  2577. if (this.markedForQuality && (event === 1 || event === 3) && user_settings.player_quality !== "auto" && (player = document.getElementById("movie_player"))) {
  2578.  
  2579. this.setQuality(player, user_settings.player_quality);
  2580.  
  2581. }
  2582.  
  2583. document.documentElement.classList.remove("video_unstarted", "video_active", "video_ended", "video_playing", "video_paused", "video_buffering", "video_cued");
  2584.  
  2585. switch (event) {
  2586.  
  2587. case -1:
  2588. document.documentElement.classList.add("video_unstarted");
  2589. break;
  2590. case 0:
  2591. document.documentElement.classList.add("video_ended");
  2592. break;
  2593. case 1:
  2594. document.documentElement.classList.add("video_active", "video_playing");
  2595. break;
  2596. case 2:
  2597. document.documentElement.classList.add("video_active", "video_paused");
  2598. break;
  2599. case 3:
  2600. document.documentElement.classList.add("video_active", "video_buffering");
  2601. break;
  2602. case 5:
  2603. document.documentElement.classList.add("video_cued");
  2604. break;
  2605.  
  2606. }
  2607.  
  2608. if (user_settings.fullBrowser) {
  2609. window.dispatchEvent(new Event("resize"));
  2610. }
  2611.  
  2612. },
  2613. playerReady: function (api) {
  2614.  
  2615. var timestamp;
  2616. var handleCustomsListener;
  2617.  
  2618. if (api) {
  2619.  
  2620. handleCustomsListener = this.handleCustoms.bind(this);
  2621.  
  2622. api.addEventListener("SIZE_CLICKED", handleCustomsListener);
  2623. api.addEventListener("onVolumeChange", handleCustomsListener);
  2624. api.addEventListener("onStateChange", this.onStateChange.bind(this));
  2625.  
  2626. if (user_settings.player_memorize_volume) {
  2627.  
  2628. api.setVolume(user_settings.userVolume);
  2629.  
  2630. timestamp = Date.now();
  2631.  
  2632. iridium_api.setStorage(
  2633. "yt-player-volume",
  2634. JSON.stringify({
  2635. data: JSON.stringify({
  2636. volume: user_settings.userVolume,
  2637. muted: false
  2638. }),
  2639. creation: timestamp,
  2640. expiration: timestamp + 2592E6
  2641. })
  2642. );
  2643.  
  2644. }
  2645.  
  2646. }
  2647.  
  2648. },
  2649. shareApi: function (original) {
  2650.  
  2651. var context = this;
  2652.  
  2653. return function (api) {
  2654.  
  2655. context.playerReady(api);
  2656.  
  2657. var ytSignalInstance;
  2658.  
  2659. ytSignalInstance = window["ytSignalsInstance"];
  2660.  
  2661. if (!ytSignalInstance) {
  2662. if ((ytSignalInstance = window["ytSignals"])) {
  2663. ytSignalInstance = ytSignalInstance["getInstance"] && ytSignalInstance["getInstance"]();
  2664. }
  2665. }
  2666.  
  2667. if (ytSignalInstance && ytSignalInstance["processSignal"]) {
  2668. ytSignalInstance["processSignal"]("eocs");
  2669. // ytSignalInstance["processSignal"]("fwtr"); // first warm transition requested
  2670. }
  2671.  
  2672. if (original) {
  2673. return original.apply(this, arguments);
  2674. }
  2675.  
  2676. };
  2677. },
  2678. isChannel: function () {
  2679. return /^\/(user|channel)\//.test(window.location.pathname);
  2680. },
  2681. loadStart: function (event) {
  2682.  
  2683. var is_watch;
  2684. var is_playlist;
  2685. var yt_player_manager;
  2686.  
  2687. if (event) {
  2688.  
  2689. is_watch = window.location.pathname === "/watch";
  2690. is_playlist = !!window.location.search.match(/list=[A-Z]{2}/);
  2691.  
  2692. switch (event.type) {
  2693.  
  2694. case "popstate":
  2695. case "yt-navigate-start":
  2696.  
  2697. if (!user_settings.player_auto_play) {
  2698. if (is_watch && this.previous_url !== window.location.href && (is_watch !== this.was_watch || is_playlist !== this.was_playlist)) {
  2699. if ((yt_player_manager = document.querySelector("yt-player-manager")) && yt_player_manager["playerContainer_"]) {
  2700. yt_player_manager["playerContainer_"] = undefined;
  2701. }
  2702. }
  2703. }
  2704.  
  2705. break;
  2706.  
  2707. }
  2708.  
  2709. this.previous_url = is_watch ? window.location.href : this.previous_url;
  2710. this.was_watch = is_watch;
  2711. this.was_playlist = is_playlist;
  2712.  
  2713. }
  2714.  
  2715. },
  2716. ini: function () {
  2717.  
  2718. var context;
  2719. var timestamp;
  2720.  
  2721. if (iridium_api.initializeOption.call(this)) {
  2722. return;
  2723. }
  2724.  
  2725. if (user_settings.player_quality !== "auto") {
  2726.  
  2727. timestamp = Date.now();
  2728.  
  2729. iridium_api.setStorage(
  2730. "yt-player-quality",
  2731. JSON.stringify({
  2732. data: user_settings.player_quality,
  2733. creation: timestamp,
  2734. expiration: timestamp + 2592E6
  2735. })
  2736. );
  2737.  
  2738. }
  2739.  
  2740. if (this.loadStartListener) {
  2741.  
  2742. window.removeEventListener("yt-page-data-updated", this.loadStartListener, true);
  2743. window.removeEventListener("yt-navigate-start", this.loadStartListener, false);
  2744. window.removeEventListener("yt-navigate-finish", this.loadStartListener, false);
  2745. window.removeEventListener("popstate", this.loadStartListener, true);
  2746.  
  2747. }
  2748.  
  2749. if (this.setPlayerResizeListener) {
  2750. window.removeEventListener("yt-navigate-finish", this.setPlayerResizeListener, false);
  2751. }
  2752.  
  2753. if (this.fileLoadListener) {
  2754. document.documentElement.removeEventListener("load", this.fileLoadListener, true);
  2755. }
  2756.  
  2757. this.loadStartListener = this.loadStart.bind(this);
  2758. this.setPlayerResizeListener = this.setPlayerResize.bind(this);
  2759. this.fileLoadListener = this.interceptHooks.bind(this);
  2760.  
  2761. window.addEventListener("yt-page-data-updated", this.loadStartListener, true);
  2762. window.addEventListener("yt-navigate-start", this.loadStartListener, false);
  2763. window.addEventListener("yt-navigate-finish", this.loadStartListener, false);
  2764. window.addEventListener("popstate", this.loadStartListener, true);
  2765. window.addEventListener("yt-navigate-finish", this.setPlayerResizeListener, false);
  2766.  
  2767. document.documentElement.addEventListener("load", this.fileLoadListener, true);
  2768.  
  2769. window.onYouTubePlayerReady = this.shareApi(window.onYouTubePlayerReady);
  2770. JSON.parse = this.modJSONParse(JSON.parse);
  2771. XMLHttpRequest.prototype.open = this.modOpen(XMLHttpRequest.prototype.open);
  2772. DOMParser.prototype.parseFromString = this.modParseFromString(DOMParser.prototype.parseFromString);
  2773.  
  2774. this.interceptHooks();
  2775.  
  2776. context = this;
  2777.  
  2778. Object.defineProperties(Object.prototype, {
  2779. cueVideoByPlayerVars: {
  2780. set: function (data) {
  2781. this._cueVideoByPlayerVars = data;
  2782. },
  2783. get: function () {
  2784. return context.modVideoByPlayerVars(this._cueVideoByPlayerVars);
  2785. }
  2786. },
  2787. loadVideoByPlayerVars: {
  2788. set: function (data) {
  2789. this._loadVideoByPlayerVars = data;
  2790. },
  2791. get: function () {
  2792.  
  2793. if (context.isChannel() ? !user_settings.channel_trailer_auto_play : !user_settings.player_auto_play) {
  2794. return this.cueVideoByPlayerVars;
  2795. }
  2796.  
  2797. return context.modVideoByPlayerVars(this._loadVideoByPlayerVars);
  2798.  
  2799. }
  2800. },
  2801. playVideo: {
  2802. set: function (data) {
  2803. this._playVideo = data;
  2804. },
  2805. get: function () {
  2806.  
  2807. if (context.isChannel() ? !user_settings.channel_trailer_auto_play : !user_settings.player_auto_play) {
  2808. if (!document.querySelector(".ad-showing,.ad-interrupting")) {
  2809. return function () {
  2810. };
  2811. }
  2812. }
  2813.  
  2814. return this._playVideo;
  2815.  
  2816. }
  2817. },
  2818. experiments: {
  2819. set: function (data) {
  2820. this._experiments = data;
  2821. },
  2822. get: function experimentsGetter() {
  2823.  
  2824. var i;
  2825. var matching;
  2826. var keys_list;
  2827. var function_string;
  2828.  
  2829. keys_list = Object.keys(this);
  2830.  
  2831. for (i = 0; i < keys_list.length; i++) {
  2832. if (this[keys_list[i]] && this[keys_list[i]].eventid) {
  2833.  
  2834. if (context.isChannel() ? !user_settings.channel_trailer_auto_play : !user_settings.player_auto_play) {
  2835.  
  2836. function_string = experimentsGetter["caller"].toString();
  2837. matching = function_string.match(/this\.([a-z0-9$_]{1,3})=[^;]+\.autoplay/i);
  2838.  
  2839. if (matching && matching[1]) {
  2840. this[matching[1]] = false;
  2841. }
  2842.  
  2843. }
  2844.  
  2845. break;
  2846.  
  2847. }
  2848. }
  2849.  
  2850. return this._experiments;
  2851.  
  2852. }
  2853. }
  2854. });
  2855.  
  2856. }
  2857. },
  2858. {
  2859. options: {
  2860. player_quick_controls: {
  2861. id: "player_quick_controls",
  2862. section: "video",
  2863. sub_section: "general",
  2864. type: "checkbox",
  2865. value: true,
  2866. i18n: {
  2867. label: "Enable quick controls",
  2868. button_auto_play: "Autoplay",
  2869. button_pop_up_player: "Pop-up Video",
  2870. button_full_browser: "Full Browser",
  2871. button_screen_shot: "Screen Shot",
  2872. button_thumbnails: "Thumbnails",
  2873. thumbnails_title: "Click to download\nRight click for options",
  2874. screen_shot_title: "Right click for options",
  2875. full_browser_info: "Click here or\npress \"Esc\" to exit"
  2876. }
  2877. }
  2878. },
  2879. exitFullBrowser: function (event) {
  2880.  
  2881. var i;
  2882. var ytd_watch;
  2883. var video_player;
  2884.  
  2885. if (!user_settings.fullBrowser || (event.type === "click" || event.keyCode === 27 || event.key === "Escape")) {
  2886.  
  2887. window.removeEventListener("keydown", this.exitFullBrowserlistener, false);
  2888. this.exitFullBrowserlistener = null;
  2889. document.documentElement.classList.remove("iri-full-browser");
  2890. user_settings.fullBrowser = false;
  2891. iridium_api.saveSettings("fullBrowser");
  2892. window.dispatchEvent(new Event("resize"));
  2893.  
  2894. if (this.ironMediaQueryList) {
  2895.  
  2896. if ((ytd_watch = document.querySelector("ytd-watch, ytd-watch-flexy"))) {
  2897. for (i = 0; i < this.ironMediaQueryList.childElementCount; i++) {
  2898. ytd_watch.appendChild(this.ironMediaQueryList.firstElementChild);
  2899. }
  2900. }
  2901.  
  2902. this.ironMediaQueryList = null;
  2903.  
  2904. }
  2905.  
  2906. if ((video_player = document.getElementById("movie_player"))) {
  2907. if (!document.querySelector("[theater]")) {
  2908. video_player.setSizeStyle(true, false);
  2909. }
  2910. }
  2911.  
  2912. this.quickControlsState();
  2913.  
  2914. }
  2915.  
  2916. },
  2917. quickControlAutoPlay: function () {
  2918.  
  2919. user_settings.player_auto_play = !user_settings.player_auto_play;
  2920. iridium_api.saveSettings("player_auto_play");
  2921. this.quickControlsState();
  2922.  
  2923. },
  2924. closeThumbnails: function (event) {
  2925.  
  2926. var thumbnail_gallery;
  2927.  
  2928. if (event.target.tagName !== "IMG" && (thumbnail_gallery = document.getElementById("iri-thumbnail-gallery"))) {
  2929. thumbnail_gallery.remove();
  2930. }
  2931.  
  2932. },
  2933. closeScreenShot: function (event) {
  2934.  
  2935. var link;
  2936. var screen_shot_container;
  2937.  
  2938. if (event.target.tagName !== "CANVAS" && (screen_shot_container = document.getElementById("iri-screen-shot-container"))) {
  2939.  
  2940. if ((link = screen_shot_container.querySelector("a"))) {
  2941. URL.revokeObjectURL(link.href);
  2942. }
  2943.  
  2944. screen_shot_container.remove();
  2945.  
  2946. }
  2947.  
  2948. },
  2949. quickControlThumbnail: function () {
  2950.  
  2951. var i;
  2952. var video_id;
  2953. var thumbnail_list;
  2954. var thumbnail_base;
  2955. var thumbnail_size;
  2956. var thumbnail_gallery;
  2957. var thumbnail_size_list;
  2958.  
  2959. if ((video_id = window.location.href.match(iridium_api.videoIdPattern))) {
  2960.  
  2961. thumbnail_base = window.location.protocol + "//i.ytimg.com/vi/";
  2962.  
  2963. thumbnail_size_list = {
  2964. iurl: "default.jpg",
  2965. iurlmq: "mqdefault.jpg",
  2966. iurlhq: "hqdefault.jpg",
  2967. iurlsd: "sddefault.jpg",
  2968. iurlhq720: "hq720.jpg",
  2969. iurlmaxres: "maxresdefault.jpg"
  2970. };
  2971.  
  2972. thumbnail_gallery = document.createElement("template");
  2973. thumbnail_gallery.innerHTML =
  2974. "<div id='iri-thumbnail-gallery'>" +
  2975. " <div id='iri-thumbnail-gallery-first-row'>" +
  2976. " <div class='iri-thumbnail-labels'>" +
  2977. " <div>MAXRES</div>" +
  2978. " <div>HQ720</div>" +
  2979. " <div>SD</div>" +
  2980. " </div>" +
  2981. " <a target='_blank' download>" +
  2982. " <img data-thumbnail-type='iurlmaxres' />" +
  2983. " </a>" +
  2984. " <a target='_blank' download>" +
  2985. " <img data-thumbnail-type='iurlhq720' />" +
  2986. " </a>" +
  2987. " <a target='_blank' download>" +
  2988. " <img data-thumbnail-type='iurlsd' />" +
  2989. " </a>" +
  2990. " </div>" +
  2991. " <div id='iri-thumbnail-gallery-second-row'>" +
  2992. " <div class='iri-thumbnail-labels'>" +
  2993. " <div>HQ</div>" +
  2994. " <div>MQ</div>" +
  2995. " <div>DEFAULT</div>" +
  2996. " </div>" +
  2997. " <a target='_blank' download>" +
  2998. " <img data-thumbnail-type='iurlhq' />" +
  2999. " </a>" +
  3000. " <a target='_blank' download>" +
  3001. " <img data-thumbnail-type='iurlmq' />" +
  3002. " </a>" +
  3003. " <a target='_blank' download>" +
  3004. " <img data-thumbnail-type='iurl' />" +
  3005. " </a>" +
  3006. " </div>" +
  3007. "</div>";
  3008. thumbnail_gallery = thumbnail_gallery.content;
  3009.  
  3010. thumbnail_list = thumbnail_gallery.querySelectorAll("[data-thumbnail-type]");
  3011.  
  3012. for (i = 0; i < thumbnail_list.length; i++) {
  3013.  
  3014. thumbnail_size = thumbnail_list[i].dataset.thumbnailType;
  3015.  
  3016. if ((thumbnail_size = thumbnail_size_list[thumbnail_size])) {
  3017.  
  3018. thumbnail_list[i].src = thumbnail_base + video_id[1] + "/" + thumbnail_size;
  3019. thumbnail_list[i].parentNode.href = thumbnail_list[i].src;
  3020. thumbnail_list[i].parentNode.title = i18n.player_quick_controls.thumbnails_title;
  3021.  
  3022. }
  3023.  
  3024. }
  3025.  
  3026. thumbnail_gallery.firstChild.addEventListener("click", this.closeThumbnails.bind(this), false);
  3027.  
  3028. document.documentElement.appendChild(thumbnail_gallery);
  3029.  
  3030. }
  3031.  
  3032. },
  3033. quickControlFullBrowser: function (event) {
  3034.  
  3035. var i;
  3036. var ytd_watch;
  3037. var video_player;
  3038. var media_query_list;
  3039. var full_browser_info;
  3040.  
  3041. if (this.exitFullBrowserlistener) {
  3042.  
  3043. window.removeEventListener("keydown", this.exitFullBrowserlistener, false);
  3044. this.exitFullBrowserlistener = null;
  3045.  
  3046. }
  3047.  
  3048. if (event) {
  3049.  
  3050. user_settings.fullBrowser = !user_settings.fullBrowser;
  3051. iridium_api.saveSettings("fullBrowser");
  3052.  
  3053. }
  3054.  
  3055. if (user_settings.fullBrowser) {
  3056.  
  3057. document.documentElement.classList.add("iri-full-browser");
  3058. document.documentElement.scrollTop = 0;
  3059.  
  3060. if (!this.ironMediaQueryList) {
  3061.  
  3062. this.ironMediaQueryList = document.createDocumentFragment();
  3063. media_query_list = document.querySelectorAll("ytd-watch iron-media-query, ytd-watch-flexy iron-media-query");
  3064.  
  3065. for (i = 0; i < media_query_list.length; i++) {
  3066. this.ironMediaQueryList.appendChild(media_query_list[i]);
  3067. }
  3068.  
  3069. }
  3070.  
  3071. if ((video_player = document.getElementById("movie_player"))) {
  3072.  
  3073. this.exitFullBrowserlistener = this.exitFullBrowser.bind(this);
  3074. window.addEventListener("keydown", this.exitFullBrowserlistener, false);
  3075. window.dispatchEvent(new Event("resize"));
  3076.  
  3077. if (!document.getElementById("iri-full-browser-info")) {
  3078.  
  3079. full_browser_info = document.createElement("template");
  3080. full_browser_info.innerHTML =
  3081. "<div id='iri-full-browser-info'>" +
  3082. " <div id='iri-full-browser-info-message' data-locale='text|full_browser_info'></div>" +
  3083. "</div>";
  3084. full_browser_info = full_browser_info.content;
  3085.  
  3086. iridium_api.applyText(full_browser_info, i18n.player_quick_controls);
  3087. video_player.insertBefore(full_browser_info.firstChild, video_player.firstChild);
  3088. video_player.querySelector("#iri-full-browser-info-message").addEventListener("click", this.exitFullBrowserlistener, false);
  3089.  
  3090. }
  3091.  
  3092. if (!document.querySelector("[theater]")) {
  3093. video_player.setSizeStyle(true, true);
  3094. }
  3095.  
  3096. }
  3097.  
  3098. } else if (!user_settings.fullBrowser && document.documentElement.classList.contains("iri-full-browser")) {
  3099.  
  3100. document.documentElement.classList.remove("iri-full-browser");
  3101. window.dispatchEvent(new Event("resize"));
  3102.  
  3103. if (this.ironMediaQueryList) {
  3104.  
  3105. if ((ytd_watch = document.querySelector("ytd-watch, ytd-watch-flexy"))) {
  3106. for (i = 0; i < this.ironMediaQueryList.childElementCount; i++) {
  3107. ytd_watch.appendChild(this.ironMediaQueryList.firstElementChild);
  3108. }
  3109. }
  3110.  
  3111. this.ironMediaQueryList = null;
  3112.  
  3113. }
  3114.  
  3115. if ((video_player = document.getElementById("movie_player"))) {
  3116. if (!document.querySelector("[theater]")) {
  3117. video_player.setSizeStyle(true, false);
  3118. }
  3119. }
  3120.  
  3121. }
  3122.  
  3123. if (event) {
  3124. this.quickControlsState();
  3125. }
  3126.  
  3127. },
  3128. quickControlScreenShot: function () {
  3129.  
  3130. var canvas;
  3131. var aspect_ratio;
  3132. var video;
  3133. var canvas_height;
  3134. var canvas_width;
  3135. var canvas_context;
  3136. var screen_shot_container;
  3137.  
  3138. if ((video = document.querySelector("video")) && video.src) {
  3139.  
  3140. if (!(screen_shot_container = document.getElementById("iri-screen-shot-container"))) {
  3141.  
  3142. screen_shot_container = document.createElement("template");
  3143. screen_shot_container.innerHTML =
  3144. "<div id='iri-screen-shot-container'>" +
  3145. " <a target='_blank' download data-locale='title|screen_shot_title'>" +
  3146. " <canvas></canvas>" +
  3147. " </a>" +
  3148. "</div>";
  3149. screen_shot_container = screen_shot_container.content;
  3150.  
  3151. }
  3152.  
  3153. iridium_api.applyText(screen_shot_container, i18n.player_quick_controls);
  3154.  
  3155. canvas = screen_shot_container.querySelector("canvas");
  3156. canvas_context = canvas.getContext("2d");
  3157. aspect_ratio = video.videoWidth / video.videoHeight;
  3158. canvas_width = video.videoWidth;
  3159. canvas_height = parseInt(canvas_width / aspect_ratio, 10);
  3160. canvas.width = canvas_width;
  3161. canvas.height = canvas_height;
  3162. canvas_context.drawImage(video, 0, 0, canvas_width, canvas_height);
  3163.  
  3164. canvas.toBlob(function (blob) {
  3165.  
  3166. canvas.parentNode.href = URL.createObjectURL(blob);
  3167.  
  3168. });
  3169.  
  3170. screen_shot_container.firstChild.addEventListener("click", this.closeScreenShot.bind(this), false);
  3171.  
  3172. document.documentElement.appendChild(screen_shot_container);
  3173.  
  3174. }
  3175.  
  3176. },
  3177. quickControlPopUpPlayer: function (event) {
  3178.  
  3179. window.postMessage({
  3180. id: user_settings.broadcast_id,
  3181. action: "ini-pop-up-player",
  3182. screenX: event.screenX,
  3183. screenY: event.screenY
  3184. }, "*");
  3185.  
  3186. },
  3187. quickControls: function (event) {
  3188.  
  3189. switch (event.target.id) {
  3190.  
  3191. case "iri-quick-control-auto-play":
  3192. this.quickControlAutoPlay();
  3193. break;
  3194.  
  3195. case "iri-quick-control-thumbnail":
  3196. this.quickControlThumbnail();
  3197. break;
  3198.  
  3199. case "iri-quick-control-full-browser":
  3200. this.quickControlFullBrowser(event);
  3201. break;
  3202.  
  3203. case "iri-quick-control-screen-shot":
  3204. this.quickControlScreenShot(event);
  3205. break;
  3206.  
  3207. case "iri-quick-control-pop-up-player":
  3208. this.quickControlPopUpPlayer(event);
  3209. break;
  3210.  
  3211. }
  3212.  
  3213. },
  3214. quickControlsState: function () {
  3215.  
  3216. var button;
  3217.  
  3218. if ((button = document.getElementById("iri-quick-control-auto-play"))) {
  3219.  
  3220. if (user_settings.player_auto_play) {
  3221. button.setAttribute("enabled", "true");
  3222. } else {
  3223. button.removeAttribute("enabled");
  3224. }
  3225.  
  3226. }
  3227.  
  3228. if ((button = document.getElementById("iri-quick-control-full-browser"))) {
  3229.  
  3230. if (user_settings.fullBrowser) {
  3231.  
  3232. button.setAttribute("enabled", "true");
  3233. this.quickControlFullBrowser();
  3234.  
  3235. } else {
  3236.  
  3237. button.removeAttribute("enabled");
  3238. this.quickControlFullBrowser();
  3239.  
  3240. }
  3241.  
  3242. }
  3243.  
  3244. },
  3245. loadStart: function (event) {
  3246.  
  3247. var controls;
  3248. var meta_section;
  3249.  
  3250. if (this.quickControlsListener) {
  3251.  
  3252. document.removeEventListener("click", this.quickControlsListener, false);
  3253. this.quickControlsListener = null;
  3254.  
  3255. }
  3256.  
  3257. controls = document.querySelector("#iri-quick-controls");
  3258.  
  3259. if (user_settings.player_quick_controls && document.querySelector("ytd-watch:not([hidden]), ytd-watch-flexy:not([hidden])") && (meta_section = document.querySelector("#menu-container"))) {
  3260.  
  3261. if (!controls) {
  3262.  
  3263. controls = document.createElement("template");
  3264. controls.innerHTML =
  3265. "<div id='iri-quick-controls' class='closed-mode'>" +
  3266. " <div id='iri-quick-controls-container'>" +
  3267. " <button id='iri-quick-control-auto-play'>" +
  3268. " <svg viewBox='0 0 20 20' height='20' width='20'>" +
  3269. " <polygon points='3 2 16.9 10 3 18'/>" +
  3270. " </svg>" +
  3271. " <div class='iri-quick-controls-tooltip' data-locale='text|button_auto_play'></div>" +
  3272. " </button>" +
  3273. " <button id='iri-quick-control-full-browser'>" +
  3274. " <svg viewBox='0 0 20 20' height='20' width='20'>" +
  3275. " <path d='M0 4v12h20V4H0z M12 12H2V6h10V12'/>" +
  3276. " </svg>" +
  3277. " <div class='iri-quick-controls-tooltip' data-locale='text|button_full_browser'></div>" +
  3278. " </button>" +
  3279. " <button id='iri-quick-control-screen-shot'>" +
  3280. " <svg viewBox='0 0 20 20' height='20' width='20'>" +
  3281. " <circle cx='13' cy='10' r='3.5'/>" +
  3282. " <path d='M0 4v12h20V4H0z M7 7H1V5h6V7z M13 15c-2.8 0-5-2.2-5-5s2.2-5 5-5s5 2.2 5 5S15.8 15 13 15z'/>" +
  3283. " </svg>" +
  3284. " <div class='iri-quick-controls-tooltip' data-locale='text|button_screen_shot'></div>" +
  3285. " </button>" +
  3286. " <button id='iri-quick-control-thumbnail'>" +
  3287. " <svg viewBox='0 0 20 20' height='20' width='20'>" +
  3288. " <circle cx='8' cy='7.2' r='2'/>" +
  3289. " <path d='M0 2v16h20V2H0z M18 16H2V4h16V16z'/>" +
  3290. " <polygon points='17 10.9 14 7.9 9 12.9 6 9.9 3 12.9 3 15 17 15'/>" +
  3291. " </svg>" +
  3292. " <div class='iri-quick-controls-tooltip' data-locale='text|button_thumbnails'></div>" +
  3293. " </button>" +
  3294. " <button id='iri-quick-control-pop-up-player'>" +
  3295. " <svg viewBox='0 0 20 20' height='20' width='20'>" +
  3296. " <path d='M18 2H6v4H2v12h12v-4h4V2z M12 16H4V8h2v6h6V16z M16 12h-2h-2H8V8V6V4h8V12z'/>" +
  3297. " </svg>" +
  3298. " <div class='iri-quick-controls-tooltip' data-locale='text|button_pop_up_player'></div>" +
  3299. " </button>" +
  3300. " </div>" +
  3301. "</div>";
  3302. controls = controls.content;
  3303.  
  3304. iridium_api.applyText(controls, i18n.player_quick_controls);
  3305. meta_section.parentNode.insertBefore(controls, meta_section);
  3306.  
  3307. }
  3308.  
  3309. this.quickControlsState();
  3310.  
  3311. this.quickControlsListener = this.quickControls.bind(this);
  3312.  
  3313. document.addEventListener("click", this.quickControlsListener, false);
  3314.  
  3315. } else if (controls && !user_settings.player_quick_controls) {
  3316. controls.remove();
  3317. }
  3318.  
  3319. },
  3320. onSettingsUpdated: function () {
  3321. this.quickControlsState();
  3322. },
  3323. ini: function () {
  3324.  
  3325. if (iridium_api.initializeOption.call(this)) {
  3326. return;
  3327. }
  3328.  
  3329. if (this.loadStartListener) {
  3330.  
  3331. window.removeEventListener("yt-page-data-updated", this.loadStartListener, true);
  3332. window.removeEventListener("yt-navigate-start", this.loadStartListener, false);
  3333. window.removeEventListener("yt-navigate-finish", this.loadStartListener, false);
  3334. window.removeEventListener("popstate", this.loadStartListener, true);
  3335.  
  3336. }
  3337.  
  3338. this.loadStartListener = this.loadStart.bind(this);
  3339.  
  3340. window.addEventListener("yt-page-data-updated", this.loadStartListener, true);
  3341. window.addEventListener("yt-navigate-start", this.loadStartListener, false);
  3342. window.addEventListener("yt-navigate-finish", this.loadStartListener, false);
  3343. window.addEventListener("popstate", this.loadStartListener, true);
  3344.  
  3345. }
  3346. },
  3347. {
  3348. options: {
  3349. comments_visibility: {
  3350. id: "comments_visibility",
  3351. section: "video",
  3352. sub_section: "general",
  3353. type: "dropdown",
  3354. value: 1,
  3355. i18n: {
  3356. label: "Comment section:",
  3357. button_show_comments: "Show comments",
  3358. options: [
  3359. "Show",
  3360. "Hide",
  3361. "Remove"
  3362. ]
  3363. },
  3364. options: [
  3365. 0,
  3366. 1,
  3367. 2
  3368. ]
  3369. }
  3370. },
  3371. modOnShow: function (original) {
  3372.  
  3373. return function (bypass) {
  3374.  
  3375. var comments_loaded;
  3376. var comment_contents;
  3377.  
  3378. if (window.location.pathname !== "/watch") {
  3379. return original.apply(this, arguments);
  3380. }
  3381.  
  3382. if (user_settings.comments_visibility > 1) {
  3383. return function () {
  3384. };
  3385. }
  3386.  
  3387. comments_loaded = (comment_contents = document.querySelector("ytd-comments #contents")) && !!comment_contents.firstElementChild;
  3388.  
  3389. if (bypass || comments_loaded || user_settings.comments_visibility < 1) {
  3390. return original.apply(this, arguments);
  3391. }
  3392.  
  3393. return function () {
  3394. };
  3395.  
  3396. };
  3397.  
  3398. },
  3399. iniLoadComments: function (event) {
  3400.  
  3401. var comment_section = document.querySelector("ytd-comments yt-next-continuation");
  3402.  
  3403. event.target.remove();
  3404.  
  3405. if (comment_section) {
  3406. comment_section.onShow(true);
  3407. }
  3408.  
  3409. },
  3410. iniLoadCommentsButton: function () {
  3411.  
  3412. var button;
  3413. var comment_section;
  3414.  
  3415. button = document.getElementById("iri-show-comments");
  3416.  
  3417. if (!button && (comment_section = document.querySelector("ytd-comments"))) {
  3418.  
  3419. button = document.createElement("div");
  3420. button.id = "iri-show-comments";
  3421. button.textContent = i18n.comments_visibility.button_show_comments;
  3422. button.addEventListener("click", this.iniLoadComments.bind(this), false);
  3423.  
  3424. comment_section.insertBefore(button, comment_section.firstChild);
  3425.  
  3426. }
  3427.  
  3428. },
  3429. iniLoadStartListener: function () {
  3430.  
  3431. var button;
  3432. var comment_section;
  3433. var comment_contents;
  3434.  
  3435. if (user_settings.comments_visibility > 0) {
  3436.  
  3437. if (!((comment_contents = document.querySelector("ytd-comments #contents")) && comment_contents.firstElementChild)) {
  3438. if ((comment_section = document.querySelector("ytd-comments yt-next-continuation"))) {
  3439.  
  3440. if (comment_section.onShow) {
  3441. comment_section.onShow = this.modOnShow(comment_section.onShow);
  3442. }
  3443.  
  3444. if (user_settings.comments_visibility < 2) {
  3445. this.iniLoadCommentsButton();
  3446. }
  3447. }
  3448.  
  3449. } else if (comment_contents.firstElementChild && (button = document.getElementById("iri-show-comments"))) {
  3450. button.remove();
  3451. }
  3452.  
  3453. }
  3454.  
  3455. },
  3456. ini: function () {
  3457.  
  3458. if (iridium_api.initializeOption.call(this)) {
  3459. return;
  3460. }
  3461.  
  3462. window.addEventListener("yt-visibility-refresh", this.iniLoadStartListener.bind(this), true);
  3463.  
  3464. }
  3465. },
  3466. {
  3467. options: {
  3468. shortcuts_always_active: {
  3469. id: "shortcuts_always_active",
  3470. section: "video",
  3471. sub_section: "player",
  3472. type: "checkbox",
  3473. value: true,
  3474. i18n: {
  3475. label: "Player shortcuts always active"
  3476. }
  3477. }
  3478. },
  3479. alwaysActive: function (event) {
  3480.  
  3481. var i;
  3482. var api;
  3483. var list;
  3484. var clear;
  3485. var length;
  3486. var event_clone;
  3487.  
  3488. if (!user_settings.shortcuts_always_active) {
  3489. return;
  3490. }
  3491.  
  3492. if ((api = document.getElementById("movie_player"))) {
  3493.  
  3494. clear = window.location.pathname === "/watch" && api && api !== event.target && !api.contains(event.target);
  3495.  
  3496. clear = clear && !event.ctrlKey && !event.shiftKey && !event.altKey && !event.metaKey && !event.target.isContentEditable;
  3497.  
  3498. 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);
  3499.  
  3500. if (clear && ["EMBED", "INPUT", "OBJECT", "TEXTAREA", "IFRAME"].indexOf(document.activeElement.tagName) === -1) {
  3501.  
  3502. event_clone = new Event("keydown");
  3503. list = Object.keys(Object.getPrototypeOf(event));
  3504. length = list.length;
  3505.  
  3506. for (i = 0; i < length; i++) {
  3507. event_clone[list[i]] = event[list[i]];
  3508. }
  3509.  
  3510. event.preventDefault();
  3511. api.dispatchEvent(event_clone);
  3512.  
  3513. }
  3514.  
  3515. }
  3516.  
  3517. },
  3518. ini: function () {
  3519.  
  3520. if (iridium_api.initializeOption.call(this)) {
  3521. return;
  3522. }
  3523.  
  3524. document.addEventListener("keydown", this.alwaysActive.bind(this), false);
  3525.  
  3526. }
  3527. },
  3528. {
  3529. options: {
  3530. player_volume_wheel: {
  3531. id: "player_volume_wheel",
  3532. section: "video",
  3533. sub_section: "player",
  3534. type: "checkbox",
  3535. value: false,
  3536. i18n: {
  3537. label: "Change volume using the mouse wheel"
  3538. }
  3539. }
  3540. },
  3541. changeVolume: function (event) {
  3542.  
  3543. var api;
  3544. var player;
  3545. var direction;
  3546. var timestamp;
  3547. var can_scroll;
  3548. var new_volume;
  3549. var player_state;
  3550. var chrome_bottom;
  3551. var invideo_drawer;
  3552. var player_settings;
  3553. var fullscreen_playlist;
  3554.  
  3555. if (!user_settings.player_volume_wheel) {
  3556. return;
  3557. }
  3558.  
  3559. api = document.getElementById("movie_player");
  3560. player = document.querySelector("video");
  3561. invideo_drawer = document.querySelector(".iv-drawer");
  3562. player_settings = document.querySelector(".ytp-settings-menu");
  3563. fullscreen_playlist = document.querySelector(".ytp-playlist-menu");
  3564. can_scroll = (!fullscreen_playlist || !fullscreen_playlist.contains(event.target)) && (!invideo_drawer || !invideo_drawer.contains(event.target)) && (!player_settings || !player_settings.contains(event.target));
  3565.  
  3566. if (can_scroll && player && api && api.contains(event.target)) {
  3567.  
  3568. player_state = api.getPlayerState();
  3569.  
  3570. if (player_state > 0 && player_state < 5) {
  3571.  
  3572. event.preventDefault();
  3573. chrome_bottom = document.querySelector(".ytp-chrome-bottom");
  3574.  
  3575. if (chrome_bottom) {
  3576.  
  3577. if (!chrome_bottom.classList.contains("ytp-volume-slider-active")) {
  3578. chrome_bottom.classList.add("ytp-volume-slider-active");
  3579. }
  3580.  
  3581. if (chrome_bottom.timer) {
  3582. window.clearTimeout(chrome_bottom.timer);
  3583. }
  3584.  
  3585. api.dispatchEvent(new Event("mousemove"));
  3586.  
  3587. chrome_bottom.timer = window.setTimeout(function () {
  3588. if (chrome_bottom && chrome_bottom.classList.contains("ytp-volume-slider-active")) {
  3589.  
  3590. chrome_bottom.classList.remove("ytp-volume-slider-active");
  3591. delete chrome_bottom.timer;
  3592.  
  3593. }
  3594. }, 4000);
  3595.  
  3596. }
  3597.  
  3598. direction = event.deltaY || event.wheelDeltaY;
  3599. new_volume = api.getVolume() - (Math.sign(direction) * 5);
  3600.  
  3601. if (new_volume < 0) {
  3602. new_volume = 0;
  3603. } else if (new_volume > 100) {
  3604. new_volume = 100;
  3605. }
  3606.  
  3607. api.setVolume(new_volume);
  3608.  
  3609. timestamp = Date.now();
  3610.  
  3611. iridium_api.setStorage(
  3612. "yt-player-volume",
  3613. JSON.stringify({
  3614. data: JSON.stringify({
  3615. volume: new_volume,
  3616. muted: false
  3617. }),
  3618. creation: timestamp,
  3619. expiration: timestamp + 2592E6
  3620. })
  3621. );
  3622.  
  3623. return false;
  3624.  
  3625. }
  3626.  
  3627. }
  3628.  
  3629. },
  3630. ini: function () {
  3631.  
  3632. if (iridium_api.initializeOption.call(this)) {
  3633. return;
  3634. }
  3635.  
  3636. document.addEventListener("wheel", this.changeVolume.bind(this));
  3637.  
  3638. }
  3639. },
  3640. {
  3641. options: {
  3642. player_always_visible: {
  3643. id: "player_always_visible",
  3644. section: "video",
  3645. sub_section: "player",
  3646. type: "checkbox",
  3647. value: true,
  3648. i18n: {
  3649. label: "Video stays always visible while scrolling"
  3650. }
  3651. },
  3652. player_always_playing: {
  3653. id: "player_always_playing",
  3654. section: "video",
  3655. sub_section: "player",
  3656. type: "checkbox",
  3657. value: true,
  3658. i18n: {
  3659. label: "Video keeps playing when changing pages",
  3660. button_restore: "Restore",
  3661. button_close: "Close"
  3662. }
  3663. }
  3664. },
  3665. move_data: {
  3666. is_mini: false,
  3667. mouse_offset: {X: 0, Y: 0},
  3668. player_position: {X: 0, Y: 0, snapRight: true, snapBottom: true},
  3669. player_dimension: {height: 0, width: 0}
  3670. },
  3671. updatePlayerPosition: function (is_moving) {
  3672.  
  3673. var style;
  3674. var masthead;
  3675. var video_player;
  3676. var player_margin;
  3677. var masthead_offset;
  3678.  
  3679. is_moving = is_moving === true;
  3680. player_margin = 10;
  3681. masthead_offset = player_margin;
  3682.  
  3683. if (!this.move_data.is_mini || document.webkitIsFullScreen || window.fullScreen) {
  3684. return;
  3685. }
  3686.  
  3687. if ((masthead = document.getElementById("masthead"))) {
  3688. masthead_offset += masthead.offsetHeight;
  3689. }
  3690.  
  3691. this.move_data.player_position.snapRight = false;
  3692. this.move_data.player_position.snapBottom = false;
  3693.  
  3694. if (is_moving || !user_settings.miniPlayer.position.snapRight) {
  3695.  
  3696. if (this.move_data.player_position.X < player_margin) {
  3697. this.move_data.player_position.X = player_margin;
  3698. } else if (this.move_data.player_position.X + this.move_data.player_dimension.width > document.documentElement.clientWidth - player_margin) {
  3699. this.move_data.player_position.snapRight = true;
  3700. }
  3701.  
  3702. }
  3703.  
  3704.  
  3705. if (is_moving || !user_settings.miniPlayer.position.snapBottom) {
  3706.  
  3707. if (this.move_data.player_position.Y < masthead_offset) {
  3708. this.move_data.player_position.Y = masthead_offset;
  3709. } else if (this.move_data.player_position.Y + this.move_data.player_dimension.height > document.documentElement.clientHeight - player_margin) {
  3710. this.move_data.player_position.snapBottom = true;
  3711. }
  3712.  
  3713. }
  3714.  
  3715. if ((video_player = document.getElementById("movie_player"))) {
  3716.  
  3717. style = "";
  3718.  
  3719. if (!is_moving && user_settings.miniPlayer.position.snapRight || this.move_data.player_position.snapRight) {
  3720. style += "right:" + player_margin + "px;";
  3721. } else {
  3722. style += "left:" + this.move_data.player_position.X + "px;";
  3723. }
  3724.  
  3725. if (!is_moving && user_settings.miniPlayer.position.snapBottom || this.move_data.player_position.snapBottom) {
  3726. style += "bottom:" + player_margin + "px;";
  3727. } else {
  3728. style += "top:" + this.move_data.player_position.Y + "px;";
  3729. }
  3730.  
  3731. video_player.setAttribute("style", style);
  3732.  
  3733. }
  3734.  
  3735. },
  3736. iniMoveData: function (clientX, clientY) {
  3737.  
  3738. var video_rects;
  3739. var video_player;
  3740.  
  3741. if ((video_player = document.getElementById("movie_player"))) {
  3742.  
  3743. video_rects = video_player.getBoundingClientRect();
  3744.  
  3745. this.move_data.player_dimension.height = video_rects.height;
  3746. this.move_data.player_dimension.width = video_rects.width;
  3747. this.move_data.player_position.X = video_rects.left;
  3748. this.move_data.player_position.Y = video_rects.top;
  3749. this.move_data.mouse_offset.X = clientX - video_rects.left;
  3750. this.move_data.mouse_offset.Y = clientY - video_rects.top;
  3751.  
  3752. }
  3753.  
  3754. },
  3755. movePlayer: function (event) {
  3756.  
  3757. if (event.type === "mousemove") {
  3758.  
  3759. document.documentElement.classList.add("iri-mini-player-moving");
  3760.  
  3761. this.move_data.player_position.X = event.clientX - this.move_data.mouse_offset.X;
  3762. this.move_data.player_position.Y = event.clientY - this.move_data.mouse_offset.Y;
  3763.  
  3764. this.hasMoved = true;
  3765. this.updatePlayerPosition(true);
  3766.  
  3767. } else if (event.type === "click" || event.type === "mouseup" || event.type === "mousedown") {
  3768.  
  3769. if (this.mouseListener) {
  3770.  
  3771. window.removeEventListener("click", this.mouseListener, true);
  3772. window.removeEventListener("mouseup", this.mouseListener, true);
  3773. window.removeEventListener("mousemove", this.mouseListener, true);
  3774.  
  3775. this.mouseListener = null;
  3776.  
  3777. }
  3778.  
  3779. switch (event.type) {
  3780. case "mousedown":
  3781.  
  3782. this.iniMoveData(event.clientX, event.clientY);
  3783.  
  3784. this.mouseListener = this.movePlayer.bind(this);
  3785.  
  3786. window.addEventListener("click", this.mouseListener, true);
  3787. window.addEventListener("mouseup", this.mouseListener, true);
  3788. window.addEventListener("mousemove", this.mouseListener, true);
  3789.  
  3790. break;
  3791. case "mouseup":
  3792. case "click":
  3793.  
  3794. document.documentElement.classList.remove("iri-mini-player-moving");
  3795.  
  3796. user_settings.miniPlayer = {
  3797. position: {
  3798. X: this.move_data.player_position.X,
  3799. Y: this.move_data.player_position.Y,
  3800. snapRight: this.move_data.player_position.snapRight,
  3801. snapBottom: this.move_data.player_position.snapBottom
  3802. },
  3803. size: 352
  3804. };
  3805.  
  3806. break;
  3807. }
  3808.  
  3809. if (this.hasMoved) {
  3810.  
  3811. iridium_api.saveSettings("miniPlayer");
  3812. this.hasMoved = false;
  3813.  
  3814. }
  3815.  
  3816. }
  3817.  
  3818. event.preventDefault();
  3819. event.stopPropagation();
  3820. return false;
  3821.  
  3822. },
  3823. restorePlayer: function () {
  3824.  
  3825. var i;
  3826. var keys;
  3827. var history;
  3828. var ytd_app;
  3829. var player_api;
  3830. var current_data;
  3831. var history_list;
  3832. var history_state;
  3833. var yt_history_manager;
  3834.  
  3835. if ((player_api = document.getElementById("movie_player"))) {
  3836. if ((yt_history_manager = document.querySelector("yt-history-manager"))) {
  3837.  
  3838. history_list = [];
  3839.  
  3840. if (yt_history_manager.USE_HISTORY_SNAPSHOT_CACHE_) {
  3841. if (iridium_api.checkIfExists("historySnapshotCache_.timeToDataCache_", yt_history_manager)) {
  3842. yt_history_manager.historySnapshotCache_.timeToDataCache_.forEach(function (value, key) {
  3843. history_list[key] = value;
  3844. });
  3845. }
  3846. } else if (yt_history_manager.historyEntryTimeToDataMap_) {
  3847. history_list = yt_history_manager.historyEntryTimeToDataMap_;
  3848. }
  3849.  
  3850. keys = Object.keys(history_list);
  3851. current_data = player_api.getUpdatedConfigurationData();
  3852.  
  3853. for (i = 0; i < keys.length; i++) {
  3854. if ((history = history_list[keys[i]].rootData)) {
  3855. if (current_data.args.eventid === history.csn) {
  3856.  
  3857. history.response.currentVideoEndpoint.urlEndpoint = {
  3858. url: iridium_api.getSingleObjectByKey(history.response.currentVideoEndpoint, ["url"])
  3859. };
  3860.  
  3861. history_state = {
  3862. endpoint: history.response.currentVideoEndpoint,
  3863. entryTime: +keys[i],
  3864. savedComponentState: null
  3865. };
  3866.  
  3867. window.history.pushState(history_state, current_data.args.title, history.url);
  3868.  
  3869. yt_history_manager.onPopState_({state: history_state});
  3870.  
  3871. if ((ytd_app = document.querySelector("ytd-app"))) {
  3872. ytd_app.setPageTitle(current_data.args.title);
  3873. }
  3874.  
  3875. break;
  3876.  
  3877. }
  3878. }
  3879. }
  3880.  
  3881. }
  3882. }
  3883.  
  3884. this.endMiniPlayer("iri-always-playing");
  3885.  
  3886. },
  3887. closePlayer: function () {
  3888.  
  3889. var player_api;
  3890. var current_config;
  3891.  
  3892. this.endMiniPlayer("iri-always-playing");
  3893.  
  3894. if ((player_api = document.getElementById("movie_player"))) {
  3895. if (iridium_api.checkIfExists("yt.config_.FILLER_DATA.player.args")) {
  3896. if ((current_config = player_api.getUpdatedConfigurationData())) {
  3897. player_api.cueVideoByPlayerVars(current_config.args);
  3898. }
  3899. }
  3900. }
  3901.  
  3902. },
  3903. setMiniPlayerSize: function (player_api, event) {
  3904.  
  3905. if (event) {
  3906. if ("fullscreen" in event) {
  3907.  
  3908. if (event.fullscreen) {
  3909. player_api.removeAttribute("style");
  3910. } else {
  3911. this.updatePlayerPosition();
  3912. }
  3913.  
  3914. }
  3915. }
  3916.  
  3917. player_api.setSizeStyle(false, true);
  3918.  
  3919. },
  3920. endMiniPlayer: function (class_name) {
  3921.  
  3922. var player_api;
  3923. var is_in_theater_mode;
  3924.  
  3925. this.move_data.is_mini = false;
  3926.  
  3927. document.documentElement.classList.remove(class_name);
  3928.  
  3929. if (!iridium_api.isPopUpPlayer && (player_api = document.getElementById("movie_player"))) {
  3930.  
  3931. is_in_theater_mode = document.querySelector("ytd-watch[theater], ytd-watch-flexy[theater]");
  3932.  
  3933. if (!document.querySelector(".iri-always-visible,.iri-always-playing")) {
  3934. player_api.removeAttribute("style");
  3935. }
  3936.  
  3937. player_api.setSizeStyle(true, is_in_theater_mode);
  3938.  
  3939. if (this.setMiniPlayerSizeListener) {
  3940.  
  3941. player_api.removeEventListener("onFullscreenChange", this.setMiniPlayerSizeListener, false);
  3942. this.setMiniPlayerSizeListener = null;
  3943.  
  3944. }
  3945.  
  3946. if (this.setMiniPlayerSizeResizeListener) {
  3947.  
  3948. window.removeEventListener("resize", this.setMiniPlayerSizeResizeListener, false);
  3949. this.setMiniPlayerSizeResizeListener = null;
  3950.  
  3951. }
  3952.  
  3953. }
  3954.  
  3955. },
  3956. iniMiniPlayer: function (class_name) {
  3957.  
  3958. var player_api;
  3959.  
  3960. this.move_data.is_mini = true;
  3961.  
  3962. document.documentElement.classList.add(class_name);
  3963.  
  3964. if ((player_api = document.getElementById("movie_player"))) {
  3965.  
  3966. if (this.setMiniPlayerSizeListener) {
  3967.  
  3968. player_api.removeEventListener("onFullscreenChange", this.setMiniPlayerSizeListener, false);
  3969. delete this.setMiniPlayerSizeListener;
  3970.  
  3971. }
  3972.  
  3973. this.iniMiniPlayerControls(player_api);
  3974. this.setMiniPlayerSize(player_api);
  3975.  
  3976. this.iniMoveData(0, 0);
  3977.  
  3978. this.move_data.player_position.X = user_settings.miniPlayer.position.X;
  3979. this.move_data.player_position.Y = user_settings.miniPlayer.position.Y;
  3980.  
  3981. this.updatePlayerPosition();
  3982.  
  3983. this.setMiniPlayerSizeListener = this.setMiniPlayerSize.bind(this, player_api);
  3984. player_api.addEventListener("onFullscreenChange", this.setMiniPlayerSizeListener);
  3985.  
  3986. this.setMiniPlayerSizeResizeListener = this.updatePlayerPosition.bind(this);
  3987. window.addEventListener("resize", this.setMiniPlayerSizeResizeListener, false);
  3988.  
  3989. }
  3990.  
  3991. },
  3992. iniAlwaysVisible: function (event) {
  3993.  
  3994. var player_bounds;
  3995. var is_out_of_sight;
  3996. var player_container;
  3997. var is_already_floating;
  3998.  
  3999. if (!user_settings.player_always_visible || document.querySelector("ytd-miniplayer-bar-renderer")) {
  4000. return;
  4001. }
  4002.  
  4003. is_already_floating = document.documentElement.classList.contains("iri-always-visible");
  4004.  
  4005. if (event.detail && event.detail.pageType !== "watch" && is_already_floating) {
  4006. this.endMiniPlayer("iri-always-visible");
  4007. } else if (window.location.pathname === "/watch") {
  4008. if ((player_container = document.querySelector("#player #player-container")) && (player_bounds = player_container.getBoundingClientRect())) {
  4009.  
  4010. is_out_of_sight = player_bounds.bottom < ((player_bounds.height / 2) + 50);
  4011.  
  4012. if (is_out_of_sight && !is_already_floating) {
  4013. this.iniMiniPlayer("iri-always-visible");
  4014. } else if (!is_out_of_sight && is_already_floating) {
  4015. this.endMiniPlayer("iri-always-visible");
  4016. }
  4017.  
  4018. }
  4019. }
  4020.  
  4021. },
  4022. iniAlwaysPlaying: function (event) {
  4023.  
  4024. if (!user_settings.player_always_playing) {
  4025.  
  4026. this.endMiniPlayer("iri-always-playing");
  4027.  
  4028. return;
  4029.  
  4030. }
  4031.  
  4032. if (event.detail && event.detail.pageType === "watch") {
  4033. this.endMiniPlayer("iri-always-playing");
  4034. } else if (document.querySelector(".playing-mode")) {
  4035. this.iniMiniPlayer("iri-always-playing");
  4036. }
  4037.  
  4038. },
  4039. setPageCsn: function (event) {
  4040.  
  4041. var player_api;
  4042. var current_data;
  4043. var page_manager;
  4044.  
  4045. if (!user_settings.player_always_playing) {
  4046.  
  4047. return;
  4048.  
  4049. }
  4050.  
  4051. if (!document.documentElement.classList.contains("iri-always-playing")) {
  4052. if (document.querySelector(".playing-mode")) {
  4053. if ((player_api = document.getElementById("movie_player"))) {
  4054. if ((page_manager = document.querySelector("ytd-page-manager"))) {
  4055. if (page_manager.data && page_manager.data.csn) {
  4056. if ((current_data = player_api.getUpdatedConfigurationData())) {
  4057.  
  4058. console.log("csn saved", page_manager.data.csn);
  4059. current_data.args.eventid = page_manager.data.csn;
  4060. player_api.updateVideoData(current_data.args, true);
  4061.  
  4062. }
  4063. }
  4064. }
  4065. }
  4066. }
  4067. }
  4068.  
  4069. },
  4070. iniMiniPlayerControls: function (player_api) {
  4071.  
  4072. var move_area;
  4073. var restore_page;
  4074. var close_mini_player;
  4075. var mini_player_controls;
  4076.  
  4077. if (!(mini_player_controls = document.getElementById("iri-mini-player-controls")) && player_api) {
  4078.  
  4079. mini_player_controls = document.createElement("div");
  4080. mini_player_controls.id = "iri-mini-player-controls";
  4081.  
  4082. move_area = document.createElement("div");
  4083. move_area.id = "iri-mini-player-move";
  4084. move_area.addEventListener("mousedown", this.movePlayer.bind(this), true);
  4085.  
  4086. restore_page = document.createElement("template");
  4087. restore_page.innerHTML =
  4088. "<div id='iri-mini-player-restore' class='iri-mini-player-control iri-mini-player-left-control'>" +
  4089. " <svg height='24' width='24' fill='#FFF'>" +
  4090. " <use xlink:href='#iri-svg-restore' class='iri-svg-shadow'/>" +
  4091. " <path id='iri-svg-restore' d='M21 4H1v16h22V4h-2zm0 14H3v-6h10V6h8v12z'/>" +
  4092. " </svg>" +
  4093. " <div class='iri-mini-player-tooltip' data-locale='text|button_restore'></div>" +
  4094. "</div>";
  4095. restore_page = restore_page.content;
  4096. iridium_api.applyText(restore_page, i18n.player_always_playing);
  4097. restore_page.firstChild.addEventListener("click", this.restorePlayer.bind(this), false);
  4098.  
  4099. close_mini_player = document.createElement("template");
  4100. close_mini_player.innerHTML =
  4101. "<div id='iri-mini-player-close' class='iri-mini-player-control iri-mini-player-right-control'>" +
  4102. " <svg height='24' width='24' fill='#FFF'>" +
  4103. " <use xlink:href='#iri-svg-close' class='iri-svg-shadow'/>" +
  4104. " <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'/>" +
  4105. " </svg>" +
  4106. " <div class='iri-mini-player-tooltip' data-locale='text|button_close'></div>" +
  4107. "</div>";
  4108. close_mini_player = close_mini_player.content;
  4109. iridium_api.applyText(close_mini_player, i18n.player_always_playing);
  4110. close_mini_player.firstChild.addEventListener("click", this.closePlayer.bind(this), false);
  4111.  
  4112. mini_player_controls.appendChild(move_area);
  4113. mini_player_controls.appendChild(restore_page);
  4114. mini_player_controls.appendChild(close_mini_player);
  4115.  
  4116. player_api.appendChild(mini_player_controls);
  4117.  
  4118. }
  4119.  
  4120. },
  4121. modStopVideo: function (original) {
  4122. return function () {
  4123.  
  4124. if (user_settings.player_always_playing || user_settings.player_auto_play && window.location.pathname === "/watch") {
  4125. return;
  4126. }
  4127.  
  4128. return original.apply(this, arguments);
  4129.  
  4130. };
  4131. },
  4132. onSettingsUpdated: function () {
  4133.  
  4134. if (user_settings.miniPlayer) {
  4135.  
  4136. this.move_data.player_position.X = user_settings.miniPlayer.position.X;
  4137. this.move_data.player_position.Y = user_settings.miniPlayer.position.Y;
  4138.  
  4139. }
  4140.  
  4141. this.updatePlayerPosition();
  4142.  
  4143. },
  4144. ini: function () {
  4145.  
  4146. var context;
  4147. var always_playing_listener;
  4148. var always_visible_listener;
  4149.  
  4150. if (iridium_api.initializeOption.call(this)) {
  4151. return;
  4152. }
  4153.  
  4154. always_playing_listener = this.iniAlwaysPlaying.bind(this);
  4155. always_visible_listener = this.iniAlwaysVisible.bind(this);
  4156.  
  4157. window.addEventListener("scroll", always_visible_listener, false);
  4158. window.addEventListener("popstate", always_playing_listener, true);
  4159. window.addEventListener("yt-navigate-finish", always_visible_listener, false);
  4160. window.addEventListener("yt-navigate-finish", always_playing_listener, false);
  4161. window.addEventListener("yt-navigate-start", this.setPageCsn.bind(this), false);
  4162.  
  4163. context = this;
  4164.  
  4165. Object.defineProperty(Object.prototype, "stopVideo", {
  4166. set: function (data) {
  4167. this._stopVideo = data;
  4168. },
  4169. get: function () {
  4170. return context.modStopVideo(this._stopVideo);
  4171. }
  4172. });
  4173.  
  4174.  
  4175. }
  4176. },
  4177. {
  4178. options: {
  4179. player_hide_end_screen: {
  4180. id: "player_hide_end_screen",
  4181. section: "video",
  4182. sub_section: "player",
  4183. type: "checkbox",
  4184. value: false,
  4185. i18n: {
  4186. label: "Hide end screen cards on mouse hover"
  4187. }
  4188. }
  4189. },
  4190. toggleHideCards: function () {
  4191.  
  4192. if (user_settings.player_hide_end_screen) {
  4193. document.documentElement.classList.add("iri-hide-end-screen-cards");
  4194. } else {
  4195. document.documentElement.classList.remove("iri-hide-end-screen-cards");
  4196. }
  4197.  
  4198. },
  4199. onSettingsUpdated: function () {
  4200. this.toggleHideCards();
  4201. },
  4202. ini: function () {
  4203.  
  4204. if (iridium_api.initializeOption.call(this)) {
  4205. return;
  4206. }
  4207.  
  4208. this.toggleHideCards();
  4209.  
  4210. }
  4211. },
  4212. {
  4213. options: {
  4214. iridium_dark_mode: {
  4215. id: "iridium_dark_mode",
  4216. section: "settings",
  4217. sub_section: "settings",
  4218. type: "checkbox",
  4219. value: false,
  4220. i18n: {
  4221. label: "Use dark theme"
  4222. },
  4223. callback: function () {
  4224.  
  4225. if (user_settings.iridium_dark_mode) {
  4226. document.documentElement.classList.add("iri-dark-mode-settings");
  4227. } else {
  4228. document.documentElement.classList.remove("iri-dark-mode-settings");
  4229. }
  4230.  
  4231. }
  4232. }
  4233. }
  4234. },
  4235. {
  4236. options: {
  4237. iridium_user_settings: {
  4238. id: "iridium_user_settings",
  4239. section: "settings",
  4240. sub_section: "settings",
  4241. type: "custom",
  4242. i18n: {
  4243. button_save: "Save",
  4244. button_close: "Close",
  4245. button_export: "Export",
  4246. button_import: "Import",
  4247. button_reset: "Reset",
  4248. placeholder: "Paste your new settings here",
  4249. confirm_reset: "You are about to reset your settings. It is advised to backup your current settings before continuing.\n\nDo you wish to continue?\n\n",
  4250. reset_success: "Settings have been reset.\n\nChanges will be applied after a page refresh.\n\n",
  4251. 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 continue?\n\n",
  4252. import_success: "Your settings have been imported with success.\n\nChanges will be applied after a page refresh.\n\n",
  4253. import_error: "Your settings could not be imported because they appear to be invalid.\n\n"
  4254. },
  4255. custom: function () {
  4256.  
  4257. var element;
  4258. var element_list;
  4259.  
  4260. element_list = [];
  4261.  
  4262. element = document.createElement("button");
  4263. element.textContent = i18n.iridium_user_settings.button_import;
  4264. element.className = "setting iri-settings-button";
  4265. element.addEventListener("click", this.textEditor.bind(this, "import"), false);
  4266.  
  4267. element_list.push(element);
  4268.  
  4269. element = document.createElement("button");
  4270. element.textContent = i18n.iridium_user_settings.button_export;
  4271. element.className = "setting iri-settings-button";
  4272. element.addEventListener("click", this.textEditor.bind(this, "export"), false);
  4273.  
  4274. element_list.push(element);
  4275.  
  4276. element = document.createElement("button");
  4277. element.textContent = i18n.iridium_user_settings.button_reset;
  4278. element.className = "setting iri-settings-button danger";
  4279. element.addEventListener("click", this.resetSettings.bind(this), false);
  4280.  
  4281. element_list.push(element);
  4282.  
  4283. return element_list;
  4284.  
  4285. },
  4286. resetSettings: function () {
  4287.  
  4288. if (window.confirm(i18n.iridium_user_settings.confirm_reset)) {
  4289.  
  4290. user_settings = null;
  4291. iridium_api.initializeSettings();
  4292. iridium_api.saveSettings();
  4293.  
  4294. window.alert(i18n.iridium_user_settings.reset_success);
  4295.  
  4296. }
  4297.  
  4298. },
  4299. importSettings: function () {
  4300.  
  4301. var editor;
  4302. var textarea;
  4303.  
  4304. if ((textarea = document.getElementById("iridium-textarea")) && window.confirm(i18n.iridium_user_settings.confirm_import)) {
  4305. try {
  4306.  
  4307. user_settings = JSON.parse(textarea.value);
  4308.  
  4309. iridium_api.saveSettings();
  4310.  
  4311. window.alert(i18n.iridium_user_settings.import_success);
  4312.  
  4313. if ((editor = document.getElementById("iridium-text-editor"))) {
  4314. editor.remove();
  4315. }
  4316.  
  4317. } catch (error) {
  4318. window.alert(i18n.iridium_user_settings.import_error + error.name + ": " + error.message);
  4319. }
  4320. }
  4321.  
  4322. },
  4323. closeEditor: function (editor) {
  4324.  
  4325. editor.remove();
  4326.  
  4327. },
  4328. textEditor: function (type, event) {
  4329.  
  4330. var editor;
  4331. var button;
  4332. var textarea;
  4333. var buttons_section;
  4334.  
  4335. if (!(editor = document.getElementById("iridium-text-editor"))) {
  4336.  
  4337. editor = document.createElement("div");
  4338. editor.id = "iridium-text-editor";
  4339.  
  4340. document.body.appendChild(editor);
  4341.  
  4342. } else {
  4343. editor.textContent = "";
  4344. }
  4345.  
  4346. buttons_section = document.createElement("div");
  4347. buttons_section.id = "buttons-section";
  4348. textarea = document.createElement("textarea");
  4349. textarea.id = "iridium-textarea";
  4350. textarea.setAttribute("spellcheck", "false");
  4351.  
  4352. if (type === "import") {
  4353.  
  4354. textarea.setAttribute("placeholder", i18n.iridium_user_settings.placeholder);
  4355.  
  4356. button = document.createElement("button");
  4357. button.textContent = i18n.iridium_user_settings.button_save;
  4358. button.className = "iri-settings-button";
  4359. button.addEventListener("click", this.importSettings.bind(this), false);
  4360.  
  4361. buttons_section.appendChild(button);
  4362.  
  4363. }
  4364.  
  4365. button = document.createElement("button");
  4366. button.textContent = i18n.iridium_user_settings.button_close;
  4367. button.className = "iri-settings-button";
  4368. button.addEventListener("click", this.closeEditor.bind(this, editor), false);
  4369.  
  4370. buttons_section.appendChild(button);
  4371.  
  4372. if (type === "export") {
  4373. textarea.value = JSON.stringify(user_settings, null, 4);
  4374. }
  4375.  
  4376. editor.appendChild(buttons_section);
  4377. editor.appendChild(textarea);
  4378.  
  4379. }
  4380. },
  4381. iridium_custom_language: {
  4382. id: "iridium_custom_language",
  4383. section: "settings",
  4384. sub_section: "language",
  4385. type: "checkbox",
  4386. value: false,
  4387. i18n: {
  4388. label: "Use modified locale"
  4389. }
  4390. },
  4391. iridium_language: {
  4392. id: "iridium_language",
  4393. section: "settings",
  4394. sub_section: "language",
  4395. type: "custom",
  4396. i18n: {
  4397. button_save: "Save",
  4398. button_close: "Close",
  4399. confirm_save: "You are about to replace your extension language settings.\n\nDo you wish to continue?\n\n",
  4400. save_success: "New language saved successfully.\n\nChanges will be applied after a page refresh.\n\n",
  4401. save_error: "The new language could not be saved because it appears to be invalid.\n\n"
  4402. },
  4403. custom: function () {
  4404.  
  4405. var element;
  4406. var element_list;
  4407.  
  4408. element_list = [];
  4409.  
  4410. element = document.createElement("button");
  4411. element.textContent = i18n.language;
  4412. element.className = "setting iri-settings-button";
  4413. element.addEventListener("click", this.textEditor.bind(this), false);
  4414.  
  4415. element_list.push(element);
  4416.  
  4417. return element_list;
  4418.  
  4419. },
  4420. closeEditor: function (editor) {
  4421.  
  4422. editor.remove();
  4423.  
  4424. },
  4425. saveLanguage: function (textarea) {
  4426.  
  4427. var editor;
  4428.  
  4429. if ((textarea = document.getElementById("iridium-textarea")) && window.confirm(i18n.iridium_language.confirm_save)) {
  4430. try {
  4431.  
  4432. user_settings.custom_language = JSON.parse(textarea.value);
  4433.  
  4434. iridium_api.setCustomLanguage(user_settings.custom_language);
  4435. iridium_api.saveSettings("custom_language");
  4436.  
  4437. window.alert(i18n.iridium_language.save_success);
  4438.  
  4439. if ((editor = document.getElementById("iridium-text-editor"))) {
  4440. editor.remove();
  4441. }
  4442.  
  4443. } catch (error) {
  4444. window.alert(i18n.iridium_language.save_error + error.name + ": " + error.message);
  4445. }
  4446. }
  4447.  
  4448. },
  4449. textEditor: function (event) {
  4450.  
  4451. var editor;
  4452. var button;
  4453. var textarea;
  4454. var buttons_section;
  4455.  
  4456. if (!(editor = document.getElementById("iridium-text-editor"))) {
  4457.  
  4458. editor = document.createElement("div");
  4459. editor.id = "iridium-text-editor";
  4460.  
  4461. document.body.appendChild(editor);
  4462.  
  4463. } else {
  4464. editor.textContent = "";
  4465. }
  4466.  
  4467. buttons_section = document.createElement("div");
  4468. buttons_section.id = "buttons-section";
  4469.  
  4470. button = document.createElement("button");
  4471. button.textContent = i18n.iridium_language.button_save;
  4472. button.className = "iri-settings-button";
  4473. button.addEventListener("click", this.saveLanguage.bind(this), false);
  4474.  
  4475. buttons_section.appendChild(button);
  4476.  
  4477. button = document.createElement("button");
  4478. button.textContent = i18n.iridium_language.button_close;
  4479. button.className = "iri-settings-button";
  4480. button.addEventListener("click", this.closeEditor.bind(this, editor), false);
  4481.  
  4482. buttons_section.appendChild(button);
  4483.  
  4484. textarea = document.createElement("textarea");
  4485. textarea.id = "iridium-textarea";
  4486. textarea.value = JSON.stringify(i18n, null, 4);
  4487. textarea.setAttribute("spellcheck", "false");
  4488.  
  4489. editor.appendChild(buttons_section);
  4490. editor.appendChild(textarea);
  4491.  
  4492. }
  4493. }
  4494. },
  4495. google_api_locale: "",
  4496. fetchingLocale: false,
  4497. locale_base_url: "https://api.github.com/repos/ParticleCore/Iridium/contents/i18n/",
  4498. saveLanguage: function (data) {
  4499.  
  4500. var locale_updated;
  4501.  
  4502. this.fetchingLocale = false;
  4503.  
  4504. window.removeEventListener(send_settings_to_page, this.saveLanguageListener, false);
  4505.  
  4506. if (data.detail.locale !== "") {
  4507.  
  4508. try {
  4509.  
  4510. user_settings.i18n_locale = JSON.parse(data.detail.locale);
  4511. user_settings.i18n_locale.code = this.google_api_locale;
  4512. locale_updated = true;
  4513.  
  4514. } catch (ignore) {
  4515. }
  4516.  
  4517. user_settings.iridium_language_data.next_check = new Date().getTime() + 6048E5;
  4518.  
  4519. if (locale_updated) {
  4520.  
  4521. iridium_api.saveSettings("i18n_locale");
  4522. iridium_api.saveSettings("iridium_language_data");
  4523.  
  4524. }
  4525.  
  4526. }
  4527.  
  4528. },
  4529. getLanguage: function (data) {
  4530.  
  4531. var locale_updated;
  4532.  
  4533. this.fetchingLocale = false;
  4534.  
  4535. if (data.target.readyState === 4) {
  4536. if (data.target.status === 200) {
  4537.  
  4538. try {
  4539.  
  4540. user_settings.i18n_locale = JSON.parse(data.target.response);
  4541. user_settings.i18n_locale.code = this.google_api_locale;
  4542. locale_updated = true;
  4543.  
  4544. } catch (ignore) {
  4545. }
  4546.  
  4547. user_settings.iridium_language_data.last_modified = new Date(data.target.getResponseHeader("Last-Modified")).getTime();
  4548. }
  4549. }
  4550.  
  4551. user_settings.iridium_language_data.next_check = new Date().getTime() + 6048E5;
  4552.  
  4553. if (locale_updated) {
  4554.  
  4555. iridium_api.saveSettings("i18n_locale");
  4556. iridium_api.saveSettings("iridium_language_data");
  4557.  
  4558. }
  4559.  
  4560. },
  4561. getLocale: function () {
  4562.  
  4563. this.fetchingLocale = true;
  4564.  
  4565. iridium_api.localXMLHttpRequest(
  4566. "GET",
  4567. this.getLanguage.bind(this),
  4568. this.locale_base_url + this.google_api_locale + ".json",
  4569. ["Accept", "application/vnd.github.raw"]
  4570. );
  4571.  
  4572. },
  4573. checkModified: function (data) {
  4574.  
  4575. this.fetchingLocale = false;
  4576.  
  4577. if (data.target.readyState === 4) {
  4578. if (data.target.status === 200) {
  4579. this.getLocale();
  4580. }
  4581. }
  4582.  
  4583. },
  4584. checkLocale: function () {
  4585.  
  4586. var current_time;
  4587.  
  4588. if ((this.google_api_locale = iridium_api.checkIfExists("yt.config_.GAPI_LOCALE"))) {
  4589.  
  4590. if (this.google_api_locale !== "en_US") {
  4591.  
  4592. if (!this.fetchingLocale) {
  4593.  
  4594. this.fetchingLocale = true;
  4595.  
  4596. if (!user_settings.iridium_language_data) {
  4597.  
  4598. current_time = new Date().getTime();
  4599.  
  4600. user_settings.iridium_language_data = {
  4601. last_modified: current_time,
  4602. next_check: current_time + 6048E5
  4603. };
  4604.  
  4605. iridium_api.saveSettings("iridium_language_data");
  4606.  
  4607. }
  4608.  
  4609. if (is_user_script) {
  4610.  
  4611. if (!user_settings.i18n_locale || user_settings.i18n_locale.code !== this.google_api_locale) {
  4612. this.getLocale();
  4613. } else if (current_time || user_settings.iridium_language_data.next_check < new Date().getTime()) {
  4614.  
  4615. iridium_api.localXMLHttpRequest(
  4616. "HEAD",
  4617. this.checkModified.bind(this),
  4618. this.locale_base_url + this.google_api_locale + ".json",
  4619. [
  4620. "If-Modified-Since",
  4621. new Date(user_settings.iridium_language_data.last_modified).toUTCString()
  4622. ]
  4623. );
  4624.  
  4625. }
  4626.  
  4627. } else if (
  4628. !user_settings.i18n_locale ||
  4629. user_settings.i18n_locale.code !== this.google_api_locale ||
  4630. user_settings.iridium_language_data.next_check < new Date().getTime()
  4631. ) {
  4632.  
  4633. this.saveLanguageListener = this.saveLanguage.bind(this);
  4634.  
  4635. window.addEventListener(send_settings_to_page, this.saveLanguageListener, false);
  4636.  
  4637. window.dispatchEvent(new CustomEvent(receive_settings_from_page, {
  4638. detail: {
  4639. locale: this.google_api_locale
  4640. }
  4641. }));
  4642.  
  4643. }
  4644.  
  4645. }
  4646.  
  4647. } else if (user_settings.i18n_locale || user_settings.iridium_language_data) {
  4648.  
  4649. delete user_settings.i18n_locale;
  4650. delete user_settings.iridium_language_data;
  4651.  
  4652. iridium_api.deleteSetting("i18n_locale");
  4653. iridium_api.deleteSetting("iridium_language_data");
  4654. iridium_api.initializeSettings();
  4655.  
  4656. }
  4657.  
  4658. }
  4659.  
  4660. },
  4661. ini: function () {
  4662.  
  4663. if (iridium_api.initializeOption.call(this)) {
  4664. return;
  4665. }
  4666.  
  4667. if (!user_settings.iridium_custom_language) {
  4668. document.addEventListener("readystatechange", this.checkLocale.bind(this), false);
  4669. }
  4670.  
  4671. }
  4672. },
  4673. {
  4674. options: {
  4675. donate_paypal: {
  4676. id: "donate_paypal",
  4677. section: "donate",
  4678. sub_section: "paypal",
  4679. type: "custom",
  4680. i18n: {
  4681. one_time: "One time donation",
  4682. any_amount: "Any amount",
  4683. monthly: "Monthly donation",
  4684. one_euro: "1€",
  4685. three_euro: "3€",
  4686. five_euro: "5€",
  4687. ten_euro: "10€"
  4688. },
  4689. custom: function () {
  4690.  
  4691. var element;
  4692. var element_list;
  4693.  
  4694. element_list = [];
  4695.  
  4696. element = document.createElement("textnode");
  4697. element.textContent = i18n.donate_paypal.one_time;
  4698. element.className = "setting";
  4699.  
  4700. element_list.push(element);
  4701.  
  4702. element = document.createElement("a");
  4703. element.href = "https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=UMVQJJFG4BFHW&lc=US";
  4704. element.target = "_blank";
  4705. element.textContent = i18n.donate_paypal.any_amount;
  4706. element.className = "setting iri-settings-button";
  4707.  
  4708. element_list.push(element);
  4709.  
  4710. element = document.createElement("br");
  4711.  
  4712. element_list.push(element);
  4713.  
  4714. element = document.createElement("textnode");
  4715. element.textContent = i18n.donate_paypal.monthly;
  4716. element.className = "setting";
  4717.  
  4718. element_list.push(element);
  4719.  
  4720. element = document.createElement("a");
  4721. element.href = "https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=7VPKXJ49XFAPC&lc=US";
  4722. element.target = "_blank";
  4723. element.textContent = i18n.donate_paypal.one_euro;
  4724. element.className = "setting iri-settings-button";
  4725.  
  4726. element_list.push(element);
  4727.  
  4728. element = document.createElement("a");
  4729. element.href = "https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=2G4G9HLVKSR5C&lc=US";
  4730. element.target = "_blank";
  4731. element.textContent = i18n.donate_paypal.three_euro;
  4732. element.className = "setting iri-settings-button";
  4733.  
  4734. element_list.push(element);
  4735.  
  4736. element = document.createElement("a");
  4737. element.href = "https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=3KGWY5QQFFYCS&lc=US";
  4738. element.target = "_blank";
  4739. element.textContent = i18n.donate_paypal.five_euro;
  4740. element.className = "setting iri-settings-button";
  4741.  
  4742. element_list.push(element);
  4743.  
  4744. element = document.createElement("a");
  4745. element.href = "https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=U5RPAT2VUEM2N&lc=US";
  4746. element.target = "_blank";
  4747. element.textContent = i18n.donate_paypal.ten_euro;
  4748. element.className = "setting iri-settings-button";
  4749.  
  4750. element_list.push(element);
  4751.  
  4752. return element_list;
  4753.  
  4754. }
  4755. }
  4756. }
  4757. },
  4758. {
  4759. options: {
  4760. donate_patreon: {
  4761. id: "donate_patreon",
  4762. section: "donate",
  4763. sub_section: "patreon",
  4764. type: "custom",
  4765. i18n: {
  4766. patreon: "Support with Patreon"
  4767. },
  4768. custom: function () {
  4769.  
  4770. var element;
  4771. var element_list;
  4772.  
  4773. element_list = [];
  4774.  
  4775. element = document.createElement("a");
  4776. element.href = "https://www.patreon.com/particle";
  4777. element.target = "_blank";
  4778. element.textContent = i18n.donate_patreon.patreon;
  4779. element.className = "setting iri-settings-button";
  4780.  
  4781. element_list.push(element);
  4782.  
  4783. return element_list;
  4784.  
  4785. }
  4786. }
  4787. }
  4788. }
  4789. ];
  4790.  
  4791. iridium_api = {
  4792. videoIdPattern: /v=([\w-_]+)/,
  4793. isSettingsPage: window.location.pathname === "/iridium-settings",
  4794. isPopUpPlayer: window.name === "popUpPlayer",
  4795. localXMLHttpRequest: function (method, call, url, head) {
  4796.  
  4797. var request;
  4798.  
  4799. request = new XMLHttpRequest();
  4800. request.addEventListener("load", call);
  4801. request.open(method, url, true);
  4802.  
  4803. if (head && head !== "doc") {
  4804. request.setRequestHeader(head[0], head[1]);
  4805. } else {
  4806. request.responseType = "document";
  4807. }
  4808.  
  4809. request.send();
  4810.  
  4811. },
  4812. closeWelcomeBox: function (event) {
  4813.  
  4814. var welcome_box;
  4815.  
  4816. if (event.target.tagName === "BUTTON" || event.target.id === "iri-welcome-box") {
  4817. if ((welcome_box = document.getElementById("iri-welcome-box"))) {
  4818.  
  4819. welcome_box.remove();
  4820. user_settings.welcome_closed = true;
  4821. iridium_api.saveSettings("welcome_closed");
  4822.  
  4823. }
  4824. }
  4825.  
  4826. },
  4827. showWelcomeBox: function () {
  4828.  
  4829. var welcome_box;
  4830.  
  4831. if (!user_settings.welcome_closed && !this.isSettingsPage) {
  4832.  
  4833. if (!document.getElementById("iri-welcome-box")) {
  4834.  
  4835. welcome_box = document.createElement("template");
  4836. welcome_box.innerHTML =
  4837. "<div id='iri-welcome-box' style='display:none;'>" +
  4838. " <div id='iri-welcome-box-content'>" +
  4839. " <div style='text-align:center;' data-locale='text|thank_you'></div>" +
  4840. " <svg id='iri-welcome-icon' viewBox='0 0 24 24' style='height:48px;'>" +
  4841. " <radialGradient id='iri-welcome-gradient' gradientUnits='userSpaceOnUse' cx='6' cy='22' r='18.5'>" +
  4842. " <stop class='iri-start-gradient' offset='0'/>" +
  4843. " <stop class='iri-stop-gradient' offset='1'/>" +
  4844. " </radialGradient>" +
  4845. " <polygon points='21 12 3,1.8 3 22.2''/>" +
  4846. " <path d='M3 1.8v20.4L21 12L3 1.8z M6 7l9 5.1l-9 5.1V7z''/>" +
  4847. " </svg>" +
  4848. " <div data-locale='text|settings_instruction'></div>" +
  4849. " <hr style='opacity:0;'/>" +
  4850. " <div style='display:inline;' data-locale='text|features_instruction'></div>" +
  4851. " <a href='https://github.com/ParticleCore/Iridium/wiki/Features' target='_blank' data-locale='text|features_link'></a>" +
  4852. " <hr style='opacity:0;'/>" +
  4853. " <div data-locale='text|donate_instruction'></div>" +
  4854. " <hr style='opacity:0;'/>" +
  4855. " <h3 style='font-weight:500;'>PayPal</h3>" +
  4856. " <div style='display:inline;' data-locale='text|paypal_one_time'></div>" +
  4857. " <a href='https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=UMVQJJFG4BFHW&lc=US' target='_blank' class='iri-button' data-locale='text|paypal_any_amount'></a>" +
  4858. " <hr style='opacity:0;'/>" +
  4859. " <div style='display:inline;' data-locale='text|paypal_monthly'></div>" +
  4860. " <a href='https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=7VPKXJ49XFAPC&lc=US' target='_blank' class='iri-button'>1€</a>" +
  4861. " <a href='https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=2G4G9HLVKSR5C&lc=US' target='_blank' class='iri-button'>3€</a>" +
  4862. " <a href='https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=3KGWY5QQFFYCS&lc=US' target='_blank' class='iri-button'>5€</a>" +
  4863. " <a href='https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=U5RPAT2VUEM2N&lc=US' target='_blank' class='iri-button'>10€</a>" +
  4864. " <hr style='opacity:0;'/>" +
  4865. " <h3 style='font-weight:500;'>Patreon</h3>" +
  4866. " <a href='https://www.patreon.com/particle' target='_blank' class='iri-button' data-locale='text|patreon_support'></a>" +
  4867. " <div style='text-align:right;white-space: normal'>" +
  4868. " <button style='padding:5px 10px;' data-locale='text|button_close'></button>" +
  4869. " </div>" +
  4870. " </div>" +
  4871. "</div>";
  4872. welcome_box = welcome_box.content;
  4873. iridium_api.applyText(welcome_box, i18n.welcome_box);
  4874.  
  4875. document.documentElement.addEventListener("click", this.closeWelcomeBox.bind(this), false);
  4876. document.documentElement.appendChild(welcome_box);
  4877.  
  4878. }
  4879.  
  4880. } else if ((welcome_box = document.getElementById("iri-welcome-box"))) {
  4881. welcome_box.remove();
  4882.  
  4883. }
  4884.  
  4885. },
  4886. setStorage: function (id, value) {
  4887. try {
  4888. window.localStorage.setItem(id, value);
  4889. } catch (ignore) {
  4890. }
  4891. },
  4892. checkIfExists: function (path, host) {
  4893.  
  4894. var i;
  4895. var path_list;
  4896.  
  4897. host = host || window;
  4898. path_list = path.split(".");
  4899.  
  4900. for (i = 0; i < path_list.length; i++) {
  4901. if (!(host = host[path_list[i]])) {
  4902. return null;
  4903. }
  4904. }
  4905.  
  4906. return host;
  4907.  
  4908. },
  4909. applyText: function (html, text_list) {
  4910.  
  4911. var i;
  4912. var j;
  4913. var locale;
  4914. var locale_text;
  4915. var node_list;
  4916. var locale_list;
  4917.  
  4918. node_list = html.querySelectorAll("[data-locale]");
  4919.  
  4920. for (i = 0; i < node_list.length; i++) {
  4921.  
  4922. locale_list = node_list[i].dataset.locale.split("&");
  4923.  
  4924. for (j = 0; j < locale_list.length; j++) {
  4925.  
  4926. locale = locale_list[j].split("|");
  4927. locale_text = text_list[locale[1]];
  4928.  
  4929. switch (locale[0]) {
  4930. case "title":
  4931. node_list[i].setAttribute("title", locale_text);
  4932. break;
  4933. case "text":
  4934. node_list[i].appendChild(document.createTextNode(locale_text));
  4935. break;
  4936. case "tooltip":
  4937. node_list[i].tooltipText = locale_text;
  4938. break;
  4939. }
  4940.  
  4941. }
  4942.  
  4943. }
  4944.  
  4945. },
  4946. getSingleObjectByKey: function (obj, keys, match) {
  4947.  
  4948. var i;
  4949. var hasKey;
  4950. var result;
  4951. var property;
  4952.  
  4953. for (property in obj) {
  4954.  
  4955. if (obj.hasOwnProperty(property) && obj[property] !== null) {
  4956.  
  4957. hasKey = keys.constructor.name === "String" ? keys === property : keys.indexOf(property) > -1;
  4958.  
  4959. if (hasKey && (!match || obj[property].constructor.name !== "Object" && match(obj[property], obj))) {
  4960. return obj[property];
  4961. } else if (obj[property].constructor.name === "Object") {
  4962. if ((result = this.getSingleObjectByKey(obj[property], keys, match))) {
  4963. return result;
  4964. }
  4965. } else if (obj[property].constructor.name === "Array") {
  4966. for (i = 0; i < obj[property].length; i++) {
  4967. if ((result = this.getSingleObjectByKey(obj[property][i], keys, match))) {
  4968. return result;
  4969. }
  4970. }
  4971. }
  4972.  
  4973. }
  4974.  
  4975. }
  4976.  
  4977. },
  4978. getObjectByKey: function (obj, keys, match, list, pos) {
  4979.  
  4980. var i;
  4981. var hasKey;
  4982. var results;
  4983. var property;
  4984.  
  4985. results = [];
  4986.  
  4987. for (property in obj) {
  4988. if (obj.hasOwnProperty(property) && obj[property] !== null) {
  4989.  
  4990. hasKey = keys.constructor.name === "String" ? keys === property : keys.indexOf(property) > -1;
  4991.  
  4992. if (hasKey && (!match || typeof obj[property] !== "object" && match(obj[property], obj))) {
  4993.  
  4994. results.push({
  4995. target: obj,
  4996. property: property,
  4997. list: list,
  4998. pos: pos
  4999. });
  5000.  
  5001. } else if (obj[property]) {
  5002.  
  5003. if (obj[property].constructor === Object) {
  5004. results = results.concat(this.getObjectByKey(obj[property], keys, match, list, pos));
  5005. } else if (obj[property].constructor === Array) {
  5006. for (i = 0; i < obj[property].length; i++) {
  5007. results = results.concat(this.getObjectByKey(obj[property][i], keys, match, obj[property], i));
  5008. }
  5009. }
  5010.  
  5011. }
  5012.  
  5013. }
  5014. }
  5015.  
  5016. return results;
  5017.  
  5018. },
  5019. setCustomLanguage: function (custom_language) {
  5020.  
  5021. var i;
  5022. var j;
  5023. var key;
  5024. var parsed;
  5025. var sub_key;
  5026.  
  5027. try {
  5028.  
  5029. i18n = JSON.stringify(custom_language);
  5030.  
  5031. if (i18n !== "{}") {
  5032.  
  5033. i18n = JSON.parse(i18n);
  5034. parsed = true;
  5035.  
  5036. }
  5037.  
  5038. } catch (error) {
  5039. }
  5040.  
  5041. if (!parsed) {
  5042.  
  5043. i18n = default_language;
  5044. return;
  5045.  
  5046. }
  5047.  
  5048. key = Object.keys(default_language);
  5049.  
  5050. for (i = 0; i < key.length; i++) {
  5051.  
  5052. if (!(key[i] in i18n)) {
  5053. i18n[key[i]] = default_language[key[i]];
  5054. } else if (default_language[key[i]].constructor.name === "Object") {
  5055.  
  5056. sub_key = Object.keys(default_language[key[i]]);
  5057.  
  5058. for (j = 0; j < sub_key.length; j++) {
  5059. if (i18n[key[i]].constructor.name === "Object") {
  5060. if (!(sub_key[j] in i18n[key[i]])) {
  5061. i18n[key[i]][sub_key[j]] = default_language[key[i]][sub_key[j]];
  5062. }
  5063. }
  5064. }
  5065.  
  5066. }
  5067.  
  5068. }
  5069.  
  5070. },
  5071. fillSettingsContainer: function (options_list) {
  5072.  
  5073. var i;
  5074. var j;
  5075. var temp;
  5076. var input;
  5077. var label;
  5078. var select;
  5079. var header;
  5080. var option;
  5081. var options;
  5082. var section;
  5083. var setting;
  5084. var help_link;
  5085. var sub_section;
  5086.  
  5087. if (!(section = document.getElementById("settings_sub_section"))) {
  5088. return;
  5089. }
  5090.  
  5091. section.textContent = "";
  5092.  
  5093. if ((header = document.getElementById("settings_section_header"))) {
  5094. header.textContent = i18n.section_titles[options_list[0].section];
  5095. }
  5096.  
  5097. for (i = 0; i < options_list.length; i++) {
  5098.  
  5099. option = options_list[i];
  5100.  
  5101. if (!(sub_section = document.getElementById(i18n.sub_section_titles[option.sub_section]))) {
  5102.  
  5103. sub_section = document.createElement("div");
  5104. sub_section.id = i18n.sub_section_titles[option.sub_section];
  5105.  
  5106. header = document.createElement("h3");
  5107. header.textContent = i18n.sub_section_titles[option.sub_section];
  5108.  
  5109. sub_section.appendChild(header);
  5110. section.appendChild(sub_section);
  5111.  
  5112. }
  5113.  
  5114. setting = document.createElement("div");
  5115. setting.className = "settings_setting";
  5116.  
  5117. switch (option.type) {
  5118.  
  5119. case "checkbox":
  5120.  
  5121. input = document.createElement("input");
  5122. input.className = "setting";
  5123. input.id = option.id;
  5124. input.type = option.type;
  5125. input.checked = user_settings[option.id];
  5126.  
  5127. label = document.createElement("label");
  5128. label.textContent = i18n[option.id].label;
  5129. label.className = "setting";
  5130. label.setAttribute("for", option.id);
  5131.  
  5132. setting.appendChild(input);
  5133. setting.appendChild(label);
  5134.  
  5135. if (option.callback) {
  5136. input.callback = option.callback;
  5137. }
  5138.  
  5139. break;
  5140.  
  5141. case "dropdown":
  5142.  
  5143. label = document.createElement("label");
  5144. label.textContent = i18n[option.id].label;
  5145. label.className = "setting";
  5146. label.setAttribute("for", option.id);
  5147.  
  5148. select = document.createElement("select");
  5149. select.id = option.id;
  5150. select.className = "iri-settings-button";
  5151.  
  5152. for (j = 0; j < option.options.length; j++) {
  5153.  
  5154. options = document.createElement("option");
  5155. options.value = option.options[j];
  5156. options.textContent = i18n[option.id].options[j];
  5157.  
  5158. if (user_settings[option.id] === option.options[j]) {
  5159. options.setAttribute("selected", "true");
  5160. }
  5161.  
  5162. select.appendChild(options);
  5163. }
  5164.  
  5165. setting.appendChild(label);
  5166. setting.appendChild(select);
  5167.  
  5168. break;
  5169.  
  5170. case "custom":
  5171.  
  5172. if (option.custom) {
  5173.  
  5174. temp = option.custom();
  5175.  
  5176. for (j = 0; j < temp.length; j++) {
  5177. setting.appendChild(temp[j]);
  5178. }
  5179.  
  5180. }
  5181.  
  5182. break;
  5183.  
  5184. }
  5185.  
  5186. if (option.type !== "custom") {
  5187.  
  5188. help_link = document.createElement("a");
  5189. help_link.textContent = "?";
  5190. help_link.href = "https://github.com/ParticleCore/Iridium/wiki/Features#" + option.id;
  5191. help_link.setAttribute("title", i18n.iridium_api.feature_link);
  5192. help_link.className = "feature-link";
  5193. help_link.setAttribute("target", "features");
  5194.  
  5195. setting.appendChild(help_link);
  5196.  
  5197. }
  5198.  
  5199. sub_section.appendChild(setting);
  5200.  
  5201. }
  5202.  
  5203. },
  5204. loadSelectedSection: function () {
  5205.  
  5206. var i;
  5207. var name;
  5208. var option;
  5209. var active_id;
  5210. var options_list;
  5211. var active_sidebar;
  5212.  
  5213. if (!(active_sidebar = document.querySelector(".sidebar_section.active_sidebar"))) {
  5214. return;
  5215. }
  5216.  
  5217. active_id = active_sidebar.dataset.section;
  5218. options_list = [];
  5219.  
  5220. for (i = 0; i < modules.length; i++) {
  5221. if (modules[i].options) {
  5222. for (name in modules[i].options) {
  5223. if (modules[i].options.hasOwnProperty(name)) {
  5224.  
  5225. option = modules[i].options[name];
  5226.  
  5227. if (option.section === active_id) {
  5228. options_list.push(option);
  5229. }
  5230.  
  5231. }
  5232. }
  5233. }
  5234. }
  5235.  
  5236. iridium_api.fillSettingsContainer(options_list);
  5237.  
  5238. },
  5239. updateSidebarSelection: function (event) {
  5240.  
  5241. var next;
  5242. var current;
  5243. var sidebar_current;
  5244.  
  5245. if (event.target.dataset.section) {
  5246.  
  5247. current = document.querySelector(".active_sidebar");
  5248. next = document.getElementById("sidebar_" + event.target.dataset.section);
  5249.  
  5250. if (next !== current) {
  5251.  
  5252. if ((sidebar_current = document.querySelector(".active_sidebar"))) {
  5253. sidebar_current.classList.remove("active_sidebar");
  5254. }
  5255.  
  5256. event.target.classList.add("active_sidebar");
  5257.  
  5258. iridium_api.loadSelectedSection();
  5259.  
  5260. }
  5261.  
  5262. }
  5263.  
  5264. },
  5265. settingsBuilder: function (option) {
  5266.  
  5267. var header;
  5268. var divider;
  5269. var section;
  5270. var sub_section;
  5271. var sidebar_section;
  5272. var settings_sidebar;
  5273. var settings_container;
  5274.  
  5275. if (!(settings_sidebar = document.getElementById("iridium_settings_sidebar"))) {
  5276.  
  5277. settings_sidebar = document.createElement("div");
  5278. settings_sidebar.id = "iridium_settings_sidebar";
  5279.  
  5280. document.body.appendChild(settings_sidebar);
  5281.  
  5282. }
  5283.  
  5284. if (!(sidebar_section = document.getElementById("sidebar_" + option.section))) {
  5285.  
  5286. sidebar_section = document.createElement("div");
  5287. sidebar_section.id = "sidebar_" + option.section;
  5288. sidebar_section.textContent = i18n.section_list[option.section];
  5289. sidebar_section.dataset.section = option.section;
  5290.  
  5291. sidebar_section.className = "sidebar_section";
  5292. settings_sidebar.appendChild(sidebar_section);
  5293.  
  5294. }
  5295.  
  5296. if (!(settings_container = document.getElementById("iridium_settings_container"))) {
  5297.  
  5298. settings_container = document.createElement("div");
  5299. settings_container.id = "iridium_settings_container";
  5300.  
  5301. if (!(section = document.getElementById("settings_section"))) {
  5302.  
  5303. header = document.createElement("h2");
  5304. header.id = "settings_section_header";
  5305.  
  5306. divider = document.createElement("div");
  5307. divider.className = "settings_divider";
  5308.  
  5309. section = document.createElement("div");
  5310. section.id = "settings_section";
  5311.  
  5312. section.addEventListener("change", iridium_api.autoSaveSettings, true);
  5313. section.appendChild(header);
  5314. section.appendChild(divider);
  5315.  
  5316. settings_container.appendChild(section);
  5317.  
  5318. }
  5319.  
  5320. if (!(sub_section = document.getElementById("settings_sub_section"))) {
  5321.  
  5322. sub_section = document.createElement("div");
  5323. sub_section.id = "settings_sub_section";
  5324.  
  5325. section.appendChild(sub_section);
  5326.  
  5327. }
  5328.  
  5329. document.body.appendChild(settings_container);
  5330.  
  5331. }
  5332.  
  5333. if (!document.querySelector(".active_sidebar")) {
  5334. sidebar_section.classList.add("active_sidebar");
  5335. }
  5336.  
  5337. },
  5338. loadSettingsMenu: function (is_refresh) {
  5339.  
  5340. var i;
  5341. var name;
  5342. var title;
  5343. var option;
  5344. var new_section;
  5345. var current_section;
  5346.  
  5347. if (is_refresh && (current_section = document.querySelector(".sidebar_section.active_sidebar"))) {
  5348. current_section = current_section.id;
  5349. }
  5350.  
  5351. if (document.head) {
  5352. document.head.textContent = "";
  5353. } else {
  5354. document.documentElement.appendChild(document.createElement("head"));
  5355. }
  5356.  
  5357. if (document.body) {
  5358. document.body.textContent = "";
  5359. } else {
  5360. document.documentElement.appendChild(document.createElement("body"));
  5361. }
  5362.  
  5363. if (!(title = document.querySelector("title"))) {
  5364.  
  5365. title = document.createElement("title");
  5366.  
  5367. document.head.appendChild(title);
  5368.  
  5369. }
  5370.  
  5371. title.textContent = i18n.iridium_api.settings_button;
  5372. document.body.id = "iridium_settings";
  5373. document.body.style.display = "none";
  5374.  
  5375. for (i = 0; i < modules.length; i++) {
  5376. if (modules[i].options) {
  5377. for (name in modules[i].options) {
  5378. if (modules[i].options.hasOwnProperty(name)) {
  5379.  
  5380. option = modules[i].options[name];
  5381. iridium_api.settingsBuilder(option);
  5382.  
  5383. }
  5384. }
  5385. }
  5386. }
  5387.  
  5388. document.removeEventListener("click", iridium_api.updateSidebarSelection, false);
  5389. document.addEventListener("click", iridium_api.updateSidebarSelection, false);
  5390.  
  5391. if (is_refresh) {
  5392.  
  5393. if ((new_section = document.querySelector(".sidebar_section.active_sidebar"))) {
  5394. new_section.classList.remove("active_sidebar");
  5395. }
  5396.  
  5397. if ((current_section = document.getElementById(current_section))) {
  5398. current_section.classList.add("active_sidebar");
  5399. }
  5400.  
  5401. }
  5402.  
  5403. iridium_api.loadSelectedSection();
  5404.  
  5405. },
  5406. autoSaveSettings: function (event) {
  5407.  
  5408. switch (event.target.type) {
  5409.  
  5410. case "checkbox":
  5411.  
  5412. user_settings[event.target.id] = event.target.checked;
  5413.  
  5414. break;
  5415.  
  5416. case "select-one":
  5417.  
  5418. user_settings[event.target.id] = event.target.value;
  5419.  
  5420. break;
  5421.  
  5422. }
  5423.  
  5424. if (event.target.callback) {
  5425. event.target.callback();
  5426. }
  5427.  
  5428. iridium_api.saveSettings();
  5429.  
  5430. },
  5431. deleteSetting: function (setting) {
  5432.  
  5433. window.dispatchEvent(new CustomEvent(receive_settings_from_page, {
  5434. detail: {
  5435. settings: setting,
  5436. delete: true
  5437. }
  5438. }));
  5439.  
  5440. },
  5441. saveSettings: function (single_setting) {
  5442.  
  5443. var settings;
  5444.  
  5445. if (single_setting in user_settings) {
  5446. settings = user_settings[single_setting];
  5447. } else {
  5448. settings = user_settings;
  5449. }
  5450.  
  5451. window.dispatchEvent(new CustomEvent(receive_settings_from_page, {
  5452. detail: {
  5453. settings: settings,
  5454. single_setting: single_setting
  5455. }
  5456. }));
  5457.  
  5458. },
  5459. initializeSettings: function (new_settings) {
  5460.  
  5461. var i;
  5462. var j;
  5463. var option;
  5464. var options;
  5465. var i18n_entry;
  5466. var loaded_settings;
  5467. var iridium_settings;
  5468.  
  5469. if ((iridium_settings = document.getElementById("iridium-settings"))) {
  5470.  
  5471. loaded_settings = JSON.parse(iridium_settings.textContent || "null");
  5472. receive_settings_from_page = iridium_settings.getAttribute("settings-beacon-from");
  5473. send_settings_to_page = iridium_settings.getAttribute("settings-beacon-to");
  5474.  
  5475. iridium_settings.remove();
  5476.  
  5477. }
  5478.  
  5479. user_settings = new_settings || loaded_settings || user_settings || {};
  5480. i18n = default_language;
  5481.  
  5482. if (user_settings.iridium_custom_language) {
  5483. if (user_settings.custom_language) {
  5484. iridium_api.setCustomLanguage(user_settings.custom_language);
  5485. }
  5486. } else if (user_settings.i18n_locale) {
  5487. iridium_api.setCustomLanguage(user_settings.i18n_locale);
  5488. }
  5489.  
  5490. for (i = 0; i < modules.length; i++) {
  5491.  
  5492. for (options in modules[i].options) {
  5493.  
  5494. if (modules[i].options.hasOwnProperty(options)) {
  5495.  
  5496. option = modules[i].options[options];
  5497.  
  5498. if (!(option.id in user_settings) && "value" in option) {
  5499. user_settings[option.id] = option.value;
  5500. }
  5501.  
  5502. if (option.i18n) {
  5503. if (!(option.id in i18n)) {
  5504. i18n[option.id] = option.i18n;
  5505. } else if (option.i18n.constructor.name === "Object") {
  5506.  
  5507. i18n_entry = Object.keys(option.i18n);
  5508.  
  5509. for (j = 0; j < i18n_entry.length; j++) {
  5510. if (i18n[option.id].constructor.name === "Object") {
  5511. if (!(i18n_entry[j] in i18n[option.id])) {
  5512. i18n[option.id][i18n_entry[j]] = option.i18n[i18n_entry[j]];
  5513. }
  5514. }
  5515. }
  5516.  
  5517. }
  5518. }
  5519.  
  5520. }
  5521.  
  5522. }
  5523.  
  5524. }
  5525.  
  5526. this.showWelcomeBox();
  5527.  
  5528. },
  5529. initializeSettingsButton: function () {
  5530.  
  5531. var buttons;
  5532. var iridium_settings_button;
  5533.  
  5534. buttons = document.querySelector("#end #buttons");
  5535.  
  5536. if (buttons && !(iridium_settings_button = document.getElementById("iridium_settings_button"))) {
  5537.  
  5538. iridium_settings_button = document.createElement("template");
  5539. iridium_settings_button.innerHTML =
  5540. "<a id='iridium_settings_button' href='/iridium-settings' target='iridium-settings'>" +
  5541. " <svg viewBox='0 0 24 24' style='height:24px;'>" +
  5542. " <radialGradient id='iri-gradient' gradientUnits='userSpaceOnUse' cx='6' cy='22' r='18.5'>" +
  5543. " <stop class='iri-start-gradient' offset='0'/>" +
  5544. " <stop class='iri-stop-gradient' offset='1'/>" +
  5545. " </radialGradient>" +
  5546. " <polygon points='21 12 3,1.8 3 22.2'/>" +
  5547. " <path d='M3 1.8v20.4L21 12L3 1.8z M6 7l9 5.1l-9 5.1V7z'/>" +
  5548. " </svg>" +
  5549. " <div class='iri-tooltip' data-locale='text|settings_button' style='opacity: 0'></div>" +
  5550. "</a>";
  5551. iridium_settings_button = iridium_settings_button.content;
  5552. iridium_api.applyText(iridium_settings_button, i18n.iridium_api);
  5553. buttons.parentNode.insertBefore(iridium_settings_button, buttons);
  5554.  
  5555. document.documentElement.removeEventListener("load", iridium_api.initializeSettingsButton, true);
  5556.  
  5557. }
  5558.  
  5559. },
  5560. initializeModulesUpdate: function () {
  5561.  
  5562. var i;
  5563.  
  5564. for (i = 0; i < modules.length; i++) {
  5565. if (modules[i].onSettingsUpdated) {
  5566. modules[i].onSettingsUpdated();
  5567. }
  5568. }
  5569.  
  5570. },
  5571. initializeModules: function () {
  5572.  
  5573. var i;
  5574.  
  5575. for (i = 0; i < modules.length; i++) {
  5576. if (modules[i].ini) {
  5577. modules[i].ini();
  5578. }
  5579. }
  5580.  
  5581. },
  5582. initializeOption: function () {
  5583.  
  5584. var key;
  5585.  
  5586. if (this.started) {
  5587. return true;
  5588. }
  5589.  
  5590. this.started = true;
  5591.  
  5592. for (key in this.options) {
  5593. if (this.options.hasOwnProperty(key)) {
  5594. if (!(key in user_settings) && this.options[key].value) {
  5595. user_settings[key] = this.options[key].value;
  5596. }
  5597. }
  5598. }
  5599.  
  5600. return false;
  5601.  
  5602. },
  5603. initializeBroadcast: function (event) {
  5604. if (event.data && event.data.broadcast_id === this.broadcast_channel.name) {
  5605.  
  5606. this.initializeSettings(event.data);
  5607. this.initializeModulesUpdate();
  5608.  
  5609. }
  5610. },
  5611. ini: function () {
  5612.  
  5613. this.initializeSettings();
  5614.  
  5615. this.broadcast_channel = new BroadcastChannel(user_settings.broadcast_id);
  5616. this.broadcast_channel.addEventListener("message", this.initializeBroadcast.bind(this));
  5617.  
  5618. document.documentElement.addEventListener("load", this.initializeSettingsButton, true);
  5619.  
  5620. if (this.isSettingsPage) {
  5621.  
  5622. this.loadSettingsMenu();
  5623.  
  5624. if (user_settings.iridium_dark_mode) {
  5625. document.documentElement.classList.add("iri-dark-mode-settings");
  5626. }
  5627.  
  5628. } else {
  5629. this.initializeModules();
  5630. }
  5631.  
  5632. }
  5633. };
  5634.  
  5635. iridium_api.ini();
  5636.  
  5637. },
  5638. isAllowedPage: function () {
  5639.  
  5640. var current_page;
  5641. var disallowed_pages;
  5642.  
  5643. if ((current_page = window.location.pathname.match(/\/[a-z-]+/))) {
  5644. current_page = current_page[0];
  5645. } else {
  5646. current_page = window.location.pathname;
  5647. }
  5648.  
  5649. disallowed_pages = [
  5650. "/tv",
  5651. "/embed",
  5652. "/live_chat",
  5653. "/account",
  5654. "/account_notifications",
  5655. "/create_channel",
  5656. "/dashboard",
  5657. "/upload",
  5658. "/webcam"
  5659. ];
  5660.  
  5661. return disallowed_pages.indexOf(current_page) < 0;
  5662.  
  5663. },
  5664. generateUUID: function () {
  5665. return ([1e7] + -1e3 + -4e3 + -8e3 + -1e11).replace(/[018]/g, function (point) {
  5666. return (point ^ window.crypto.getRandomValues(new Uint8Array(1))[0] & 15 >> point / 4).toString(16);
  5667. });
  5668. },
  5669. saveSettings: function () {
  5670.  
  5671. if (this.is_user_script) {
  5672. this.GM.setValue(this.id, JSON.stringify(this.user_settings));
  5673. } else {
  5674. chrome.storage.local.set({iridiumSettings: this.user_settings});
  5675. }
  5676.  
  5677. },
  5678. updateSettingsOnOpenWindows: function () {
  5679. this.broadcast_channel.postMessage(this.user_settings);
  5680. },
  5681. settingsUpdatedFromOtherWindow: function (event) {
  5682. if (event.data && event.data.broadcast_id === this.broadcast_channel.name) {
  5683.  
  5684. this.user_settings = event.data;
  5685.  
  5686. this.saveSettings();
  5687.  
  5688. }
  5689. },
  5690. contentScriptMessages: function (custom_event) {
  5691.  
  5692. var key;
  5693. var locale_request;
  5694. var updated_settings;
  5695.  
  5696. if ((updated_settings = custom_event.detail.settings) !== undefined) {
  5697.  
  5698. if (custom_event.detail.single_setting) {
  5699. this.user_settings[custom_event.detail.single_setting] = custom_event.detail.settings;
  5700. } else if (custom_event.detail.delete) {
  5701. if (custom_event.detail.settings in this.user_settings) {
  5702. delete this.user_settings[custom_event.detail.settings];
  5703. }
  5704. } else if (this.is_settings_page && typeof updated_settings === "object") {
  5705.  
  5706. this.user_settings = {};
  5707.  
  5708. for (key in updated_settings) {
  5709. if (updated_settings.hasOwnProperty(key)) {
  5710.  
  5711. this.user_settings = updated_settings;
  5712.  
  5713. break;
  5714.  
  5715. }
  5716. }
  5717.  
  5718. }
  5719.  
  5720. this.saveSettings();
  5721. this.updateSettingsOnOpenWindows();
  5722.  
  5723. } else if ((locale_request = custom_event.detail.locale)) {
  5724. window.dispatchEvent(new CustomEvent(this.send_settings_to_page, {
  5725. detail: {
  5726. locale: chrome.i18n.getMessage(locale_request)
  5727. }
  5728. }));
  5729. }
  5730.  
  5731. },
  5732. initializeScript: function (event) {
  5733.  
  5734. var holder;
  5735.  
  5736. this.user_settings = event[this.id] || event;
  5737.  
  5738. if (!this.user_settings.broadcast_id) {
  5739.  
  5740. this.user_settings.broadcast_id = this.generateUUID();
  5741.  
  5742. this.saveSettings();
  5743.  
  5744. }
  5745.  
  5746. this.broadcast_channel = new BroadcastChannel(this.user_settings.broadcast_id);
  5747. this.broadcast_channel.addEventListener("message", this.settingsUpdatedFromOtherWindow.bind(this));
  5748.  
  5749. event = JSON.stringify(this.user_settings);
  5750.  
  5751. if (this.is_user_script) {
  5752.  
  5753. holder = document.createElement("link");
  5754. holder.rel = "stylesheet";
  5755. holder.type = "text/css";
  5756. holder.href = "https://particlecore.github.io/Iridium/css/Iridium.css?v=" + this.GM.info.script.version;
  5757. document.documentElement.appendChild(holder);
  5758.  
  5759. }
  5760.  
  5761. holder = document.createElement("iridium-settings");
  5762.  
  5763. holder.id = "iridium-settings";
  5764. holder.textContent = event;
  5765.  
  5766. holder.setAttribute("style", "display: none");
  5767. holder.setAttribute("settings-beacon-from", this.receive_settings_from_page);
  5768. holder.setAttribute("settings-beacon-to", this.send_settings_to_page);
  5769.  
  5770. document.documentElement.appendChild(holder);
  5771.  
  5772. holder = document.createElement("script");
  5773.  
  5774. holder.textContent = "(" + this.inject + "(" + this.is_user_script.toString() + "))";
  5775.  
  5776. document.documentElement.appendChild(holder);
  5777.  
  5778. holder.remove();
  5779. this.inject = null;
  5780. delete this.inject;
  5781.  
  5782. },
  5783. main: function (event) {
  5784.  
  5785. var now;
  5786. var context;
  5787.  
  5788. now = Date.now();
  5789.  
  5790. this.receive_settings_from_page = now + this.generateUUID();
  5791. this.send_settings_to_page = now + 1 + this.generateUUID();
  5792.  
  5793. window.addEventListener(this.receive_settings_from_page, this.contentScriptMessages.bind(this), false);
  5794.  
  5795. if (!event) {
  5796. if (this.is_user_script) {
  5797.  
  5798. context = this;
  5799.  
  5800. // javascript promises are horrible
  5801.  
  5802. this.GM.getValue(this.id, "{}").then(function (value) {
  5803.  
  5804. event = JSON.parse(value);
  5805. context.initializeScript(event);
  5806.  
  5807. }
  5808. );
  5809.  
  5810. }
  5811. } else {
  5812. this.initializeScript(event);
  5813. }
  5814.  
  5815. },
  5816. ini: function () {
  5817.  
  5818. if (this.isAllowedPage()) {
  5819.  
  5820. this.is_settings_page = window.location.pathname === "/iridium-settings";
  5821.  
  5822. this.id = "iridiumSettings";
  5823.  
  5824. if (typeof GM === "object" || typeof GM_info === "object") {
  5825.  
  5826. this.is_user_script = true;
  5827.  
  5828. // GreaseMonkey 4 polly fill
  5829. // https://arantius.com/misc/greasemonkey/imports/greasemonkey4-polyfill.js
  5830.  
  5831. if (typeof GM === "undefined") {
  5832.  
  5833. this.GM = {
  5834. setValue: GM_setValue,
  5835. info: GM_info,
  5836. getValue: function () {
  5837. return new Promise((resolve, reject) => {
  5838. try {
  5839. resolve(GM_getValue.apply(this, arguments));
  5840. } catch (e) {
  5841. reject(e);
  5842. }
  5843. });
  5844.  
  5845. }
  5846. };
  5847.  
  5848. } else {
  5849. this.GM = GM;
  5850. }
  5851.  
  5852. this.main();
  5853.  
  5854. } else {
  5855.  
  5856. this.is_user_script = false;
  5857.  
  5858. chrome.storage.local.get(this.id, this.main.bind(this));
  5859.  
  5860. }
  5861.  
  5862. }
  5863.  
  5864. }
  5865.  
  5866. };
  5867.  
  5868. iridium.ini();
  5869.  
  5870. }());