Iridium

YouTube with more freedom

当前为 2018-03-31 提交的版本,查看 最新版本

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