Iridium

YouTube with more freedom

目前為 2018-06-09 提交的版本,檢視 最新版本

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