GitHub Issue Comments

A userscript that toggles issues/pull request comments & messages

当前为 2020-02-16 提交的版本,查看 最新版本

  1. // ==UserScript==
  2. // @name GitHub Issue Comments
  3. // @version 1.4.1
  4. // @description A userscript that toggles issues/pull request comments & messages
  5. // @license MIT
  6. // @author Rob Garrison
  7. // @namespace https://github.com/Mottie
  8. // @include https://github.com/*
  9. // @run-at document-idle
  10. // @grant GM_addStyle
  11. // @grant GM_getValue
  12. // @grant GM_setValue
  13. // @require https://greasyfork.org/scripts/28721-mutations/code/mutations.js?version=666427
  14. // @icon https://github.githubassets.com/pinned-octocat.svg
  15. // ==/UserScript==
  16. (() => {
  17. "use strict";
  18.  
  19. GM_addStyle(`
  20. .ghic-button { float:right; }
  21. .ghic-button .btn:hover div.select-menu-modal-holder { display:block; top:auto; bottom:25px; right:0; }
  22. .ghic-right { position:absolute; right:10px; top:9px; }
  23. .ghic-button .select-menu-header, .ghic-participants { cursor:default; display:block; }
  24. .ghic-participants { border-top:1px solid #484848; padding:15px; }
  25. .ghic-avatar { display:inline-block; float:left; margin: 0 2px 2px 0; cursor:pointer; position:relative; }
  26. .ghic-avatar:last-child { margin-bottom:5px; }
  27. .ghic-avatar.comments-hidden svg { display:block; position:absolute; top:-2px; left:-2px; z-index:1; }
  28. .ghic-avatar.comments-hidden img { opacity:0.5; }
  29. .ghic-button .dropdown-item { font-weight:normal; position:relative; }
  30. .ghic-button .dropdown-item span { font-weight:normal; opacity:.5; }
  31. .ghic-button .dropdown-item.ghic-has-content span { opacity:1; }
  32. .ghic-button .dropdown-item.ghic-checked span { font-weight:bold; }
  33. .ghic-button .dropdown-item.ghic-checked svg,
  34. .ghic-button .dropdown-item:not(.ghic-checked) .ghic-count { display:inline-block; }
  35. .ghic-button .dropdown-item:not(.ghic-checked) { text-decoration:line-through; }
  36. .ghic-button .ghic-count { margin-left:5px; }
  37. .ghic-button .select-menu-modal { margin:0; }
  38. .ghic-button .ghic-participants { margin-bottom:20px; }
  39. /* for testing: ".ghic-hidden { opacity: 0.3; } */
  40. body .ghic-hidden { display:none !important; }
  41. .ghic-hidden-participant, body .ghic-avatar svg, .dropdown-item.ghic-checked .ghic-count,
  42. .ghic-hide-reactions .TimelineItem .comment-reactions,
  43. .select-menu-header.ghic-active + .select-menu-list .dropdown-item:not(.ghic-has-content) { display:none; }
  44. .ghic-menu-wrapper input[type=checkbox] { height:0; width:0; visibility:hidden; position:absolute; }
  45. .ghic-menu-wrapper .ghic-toggle { cursor:pointer; text-indent:-9999px; width:20px; height:10px;
  46. background:grey; display:block; border-radius:10px; position:relative; }
  47. .ghic-menu-wrapper .ghic-toggle:after { content:''; position:absolute; top:0; left:1px; width:9px;
  48. height:9px; background:#fff; border-radius:9px; transition:.3s; }
  49. .ghic-menu-wrapper input:checked + .ghic-toggle { background:#070; }
  50. .ghic-menu-wrapper input:checked + .ghic-toggle:after { top:0; left:calc(100% - 1px);
  51. transform:translateX(-100%); }
  52. .ghic-menu-wrapper .ghic-toggle:active:after { width:13px; }
  53. .TimelineItem.ghic-highlight .comment { border-color:#800 !important; }
  54. `);
  55.  
  56. const regex = /(svg|path)/i;
  57. // ZenHub addon active (include ZenHub Enterprise)
  58. const hasZenHub = $(".zhio, .zhe") ? true : false;
  59.  
  60. const exceptions = [
  61. "ghsr-sort-block" // sort reactions block (github-sort-reactions.user.js)
  62. ];
  63.  
  64. const settings = {
  65. // example: https://github.com/Mottie/Keyboard/issues/448
  66. title: {
  67. isHidden: false,
  68. name: "ghic-title",
  69. selector: ".TimelineItem-badge .octicon-pencil",
  70. containsText: "changed the title",
  71. label: "Title Changes"
  72. },
  73. labels: {
  74. isHidden: false,
  75. name: "ghic-labels",
  76. selector: ".TimelineItem-badge .octicon-tag",
  77. containsText: "label",
  78. label: "Label Changes"
  79. },
  80. state: {
  81. isHidden: false,
  82. name: "ghic-state",
  83. selector: `.TimelineItem-badge .octicon-primitive-dot,
  84. .TimelineItem-badge .octicon-circle-slash`,
  85. label: "State Changes (close/reopen)"
  86. },
  87.  
  88. // example: https://github.com/jquery/jquery/issues/2986
  89. milestone: {
  90. isHidden: false,
  91. name: "ghic-milestone",
  92. selector: ".TimelineItem-badge .octicon-milestone",
  93. label: "Milestone Changes"
  94. },
  95. refs: {
  96. isHidden: false,
  97. name: "ghic-refs",
  98. selector: ".TimelineItem-badge .octicon-bookmark",
  99. containsText: "referenced",
  100. label: "References"
  101. },
  102. mentioned: {
  103. isHidden: false,
  104. name: "ghic-mentions",
  105. selector: ".TimelineItem-badge .octicon-bookmark",
  106. containsText: "mentioned",
  107. label: "Mentioned"
  108. },
  109. assigned: {
  110. isHidden: false,
  111. name: "ghic-assigned",
  112. selector: ".TimelineItem-badge .octicon-person",
  113. label: "Assignment Changes"
  114. },
  115.  
  116. // Pull Requests
  117. commits: {
  118. isHidden: false,
  119. name: "ghic-commits",
  120. selector: ".discussion-commits",
  121. label: "Commits"
  122. },
  123. reviews: {
  124. isHidden: false,
  125. name: "ghic-reviews",
  126. selector: ".discussion-item-review, .discussion-item-review_requested",
  127. label: "Reviews (All)"
  128. },
  129. outdated: {
  130. isHidden: false,
  131. name: "ghic-outdated",
  132. selector: ".discussion-item-review",
  133. contains: ".outdated-comment-label",
  134. label: "Reviews (Outdated)"
  135. },
  136. // example: https://github.com/jquery/jquery/pull/3014
  137. diffOld: {
  138. isHidden: false,
  139. name: "ghic-diffOld",
  140. selector: ".outdated-diff-comment-container",
  141. label: "Diff (outdated) Comments"
  142. },
  143. diffNew: {
  144. isHidden: false,
  145. name: "ghic-diffNew",
  146. selector: "[id^=diff-for-comment-]:not(.outdated-diff-comment-container)",
  147. label: "Diff (current) Comments"
  148. },
  149. // example: https://github.com/jquery/jquery/pull/2949
  150. merged: {
  151. isHidden: false,
  152. name: "ghic-merged",
  153. selector: ".discussion-item-merged",
  154. label: "Merged"
  155. },
  156. integrate: {
  157. isHidden: false,
  158. name: "ghic-integrate",
  159. selector: ".discussion-item-integrations-callout",
  160. label: "Integrations"
  161. },
  162. resolved: {
  163. isHidden: false,
  164. name: "ghic-resolved",
  165. selector: ".js-minimizable-comment-group .minimized-comment:not(.d-none)",
  166. label: "Resolved"
  167. },
  168.  
  169. // similar comments
  170. similar: {
  171. isHidden: false,
  172. name: "ghic-similar",
  173. selector: `.js-discussion > .Details-element.details-reset:not([open]),
  174. #js-progressive-timeline-item-container > .Details-element.details-reset:not([open])`,
  175. label: "Similar comments"
  176. },
  177.  
  178. // extras (special treatment - no selector)
  179. plus1: {
  180. isHidden: false,
  181. name: "ghic-plus1",
  182. label: "+1 Comments"
  183. },
  184. reactions: {
  185. isHidden: false,
  186. name: "ghic-reactions",
  187. label: "Reactions"
  188. },
  189. projects: {
  190. isHidden: false,
  191. name: "ghic-projects",
  192. selector: `.discussion-item-added_to_project,
  193. .discussion-item-moved_columns_in_project,
  194. .discussion-item-removed_from_project`,
  195. label: "Project Changes"
  196. },
  197. // page with lots of users to hide:
  198. // https://github.com/isaacs/github/issues/215
  199.  
  200. // ZenHub pipeline change
  201. pipeline: {
  202. isHidden: false,
  203. name: "ghic-pipeline",
  204. selector: ".discussion-item.zh-discussion-item",
  205. label: "ZenHub Pipeline Changes"
  206. }
  207. };
  208.  
  209. const iconHidden = `<svg class="octicon" xmlns="http://www.w3.org/2000/svg" width="30" height="30" viewBox="0 0 9 9"><path fill="#777" d="M7.07 4.5c0-.47-.12-.9-.35-1.3L3.2 6.7c.4.25.84.37 1.3.37.35 0 .68-.07 1-.2.32-.14.6-.32.82-.55.23-.23.4-.5.55-.82.13-.32.2-.65.2-1zM2.3 5.8l3.5-3.52c-.4-.23-.83-.35-1.3-.35-.35 0-.68.07-1 .2-.3.14-.6.32-.82.55-.23.23-.4.5-.55.82-.13.32-.2.65-.2 1 0 .47.12.9.36 1.3zm6.06-1.3c0 .7-.17 1.34-.52 1.94-.34.6-.8 1.05-1.4 1.4-.6.34-1.24.52-1.94.52s-1.34-.18-1.94-.52c-.6-.35-1.05-.8-1.4-1.4C.82 5.84.64 5.2.64 4.5s.18-1.35.52-1.94.8-1.06 1.4-1.4S3.8.64 4.5.64s1.35.17 1.94.52 1.06.8 1.4 1.4c.35.6.52 1.24.52 1.94z"/></svg>`;
  210. const plus1Icon = `<img src="https://github.githubassets.com/images/icons/emoji/unicode/1f44d.png" class="emoji" title=":+1:" alt=":+1:" height="20" width="20" align="absmiddle">`;
  211.  
  212. function addMenu() {
  213. if ($("#discussion_bucket") && !$(".ghic-button")) {
  214. // update "isHidden" values
  215. getSettings();
  216. let name, isHidden, isChecked,
  217. list = "",
  218. keys = Object.keys(settings),
  219. onlyActive = GM_getValue("onlyActive", false),
  220. header = $(".discussion-sidebar-item:last-child"),
  221. menu = document.createElement("div");
  222.  
  223. for (name of keys) {
  224. if (!(name === "pipeline" && !hasZenHub)) {
  225. isHidden = settings[name].isHidden;
  226. isChecked = isHidden ? "" : "ghic-checked";
  227. list += `<label class="dropdown-item ${isChecked} ${settings[name].name}" data-ghic="${name}">
  228. <span>${settings[name].label} <span class="ghic-count"> </span></span>
  229. <span class="ghic-right">
  230. <input type="checkbox"${isHidden ? "" : " checked"}>
  231. <span class="ghic-toggle"></span>
  232. </span>
  233. </label>`;
  234. }
  235. }
  236.  
  237. menu.className = "ghic-button";
  238. menu.innerHTML = `
  239. <span class="btn btn-sm" role="button" tabindex="0" aria-haspopup="true">
  240. <span class="tooltipped tooltipped-w" aria-label="Toggle issue comments">
  241. <svg class="octicon octicon-comment-discussion" height="16" width="16" role="img" viewBox="0 0 16 16">
  242. <path d="M15 2H6c-0.55 0-1 0.45-1 1v2H1c-0.55 0-1 0.45-1 1v6c0 0.55 0.45 1 1 1h1v3l3-3h4c0.55 0 1-0.45 1-1V10h1l3 3V10h1c0.55 0 1-0.45 1-1V3c0-0.55-0.45-1-1-1zM9 12H4.5l-1.5 1.5v-1.5H1V6h4v3c0 0.55 0.45 1 1 1h3v2z m6-3H13v1.5l-1.5-1.5H6V3h9v6z"></path>
  243. </svg>
  244. </span>
  245. <div class="select-menu-modal-holder ghic-menu-wrapper">
  246. <div class="select-menu-modal" aria-hidden="true">
  247. <div class="select-menu-header ${onlyActive ? "ghic-active" : ""}" tabindex="-1">
  248. <span class="select-menu-title">Toggle items</span>
  249. <label class="ghic-right tooltipped tooltipped-w" aria-label="Only show active items">
  250. <input id="ghic-only-active" type="checkbox" ${onlyActive ? "checked" : ""}>
  251. <span class="ghic-toggle"></span>
  252. </label>
  253. </div>
  254. <div class="select-menu-list ghic-menu" role="menu">
  255. ${list}
  256. <div class="ghic-participants">
  257. <p><strong>Hide Comments from</strong></p>
  258. <div class="ghic-list"></div>
  259. </div>
  260. </div>
  261. </div>
  262. </div>
  263. </span>
  264. `;
  265. if (hasZenHub) {
  266. header.insertBefore(menu, header.childNodes[0]);
  267. } else {
  268. header.appendChild(menu);
  269. }
  270. addAvatars();
  271. }
  272. update();
  273. }
  274.  
  275. function addAvatars() {
  276. let indx = 0,
  277. str = "",
  278. list = $(".ghic-list"),
  279. unique = $$("span.ghic-avatar", list).map(el => el.getAttribute("aria-label")),
  280. // get all avatars
  281. avatars = $$(".TimelineItem-avatar img"),
  282. len = avatars.length - 1, // last avatar is the new comment with the current user
  283. updateAvatars = () => {
  284. list.innerHTML += str;
  285. str = "";
  286. },
  287.  
  288. loop = () => {
  289. let el, name,
  290. max = 0;
  291. while (max < 50 && indx <= len) {
  292. if (indx > len) {
  293. return updateAvatars();
  294. }
  295. el = avatars[indx];
  296. name = (el.getAttribute("alt") || "").replace("@", "");
  297. if (!unique.includes(name)) {
  298. str += `<span class="ghic-avatar tooltipped tooltipped-n" aria-label="${name}">
  299. ${iconHidden}
  300. <img class="ghic-avatar avatar" width="24" height="24" src="${el.src}"/>
  301. </span>`;
  302. unique[unique.length] = name;
  303. max++;
  304. }
  305. indx++;
  306. }
  307. updateAvatars();
  308. if (indx < len) {
  309. setTimeout(() => {
  310. loop();
  311. }, 200);
  312. }
  313. };
  314. loop();
  315. }
  316.  
  317. function getSettings() {
  318. let name,
  319. keys = Object.keys(settings);
  320. for (name of keys) {
  321. settings[name].isHidden = GM_getValue(settings[name].name, false);
  322. }
  323. }
  324.  
  325. function saveSettings() {
  326. let name,
  327. keys = Object.keys(settings);
  328. for (name of keys) {
  329. GM_setValue(settings[name].name, settings[name].isHidden);
  330. }
  331. }
  332.  
  333. function getInputValues() {
  334. let name, item,
  335. keys = Object.keys(settings),
  336. menu = $(".ghic-menu");
  337. for (name of keys) {
  338. if (!(name === "pipeline" && !hasZenHub)) {
  339. item = $("." + settings[name].name, menu).closest(".dropdown-item");
  340. if (item) {
  341. settings[name].isHidden = !$("input", item).checked;
  342. toggleClass(item, "ghic-checked", !settings[name].isHidden);
  343. }
  344. }
  345. }
  346. }
  347.  
  348. function hideStuff(name, init) {
  349. const obj = settings[name],
  350. isHidden = obj.isHidden;
  351. let count, results,
  352. item = $(".ghic-menu .dropdown-item." + obj.name);
  353. if (name === "plus1") {
  354. hidePlus1(init, item);
  355. } else if (item && name === "reactions") {
  356. toggleClass($("body"), "ghic-hide-reactions", isHidden);
  357. toggleClass(item, "ghic-has-content", $$(".has-reactions").length > 0);
  358. // make first comment reactions visible
  359. item = $(".TimelineItem .comment-reactions");
  360. if (item && item.classList.contains("has-reactions")) {
  361. item.style.display = "block";
  362. }
  363. } else if (item && obj.selector) {
  364. results = $$(obj.selector).map(el =>
  365. el.closest(".TimelineItem, .js-timeline-item, .Details-element")
  366. );
  367. if (obj.containsText) {
  368. results = results.filter(el => {
  369. return el.textContent.includes(obj.containsText);
  370. });
  371. }
  372. toggleClass(item, "ghic-checked", !isHidden);
  373. if (isHidden) {
  374. count = addClass(results, "ghic-hidden");
  375. $(".ghic-count", item).textContent = count ? "(" + count + " hidden)" : " ";
  376. } else if (!init) {
  377. // no need to remove classes on initialization
  378. removeClass(results, "ghic-hidden");
  379. }
  380. toggleClass(item, "ghic-has-content", results.length);
  381. }
  382. }
  383.  
  384. function hidePlus1(init, item) {
  385. let max,
  386. indx = 0,
  387. count = 0,
  388. total = 0,
  389. // keep a list of post authors to prevent duplicate +1 counts
  390. authors = [],
  391. // used https://github.com/isaacs/github/issues/215 for matches here...
  392. // matches "+1!!!!", "++1", "+!", "+99!!!", "-1", "+ 100", "thumbs up"; ":+1:^21425235"
  393. // ignoring -1's... add unicode for thumbs up; it gets replaced with an image in Windows
  394. regexPlus = /([?!*,.:^[\]()\'\"+-\d]|bump|thumbs|up|\ud83d\udc4d)/gi,
  395. // other comments to hide - they are still counted towards the +1 counter (for now?)
  396. // seen "^^^" to bump posts; "bump plleeaaassee"; "eta?"; "pretty please"
  397. // "need this"; "right now"; "still nothing?"; "super helpful"; "for gods sake"
  398. regexHide = new RegExp("(" + [
  399. "@\\w+",
  400. "\\b(it|is|a|so|the|and|no|on|oh|do|this|any|very|much|here|just|my|me|too|want|yet|image)\\b",
  401. "pretty",
  402. "pl+e+a+s+e+",
  403. "plz",
  404. "totally",
  405. "y+e+s+",
  406. "eta",
  407. "fix",
  408. "right",
  409. "now",
  410. "hope(ful)?",
  411. "still",
  412. "wait(ed|ing)?",
  413. "nothing",
  414. "really",
  415. "add(ed|ing)?",
  416. "need(ed|ing)?",
  417. "updat(es|ed|ing)?",
  418. "(months|years)\\slater",
  419. "back",
  420. "features?",
  421. "infinity", // +Infinity
  422. "useful",
  423. "super",
  424. "helpful",
  425. "thanks",
  426. "for\\sgod'?s\\ssake",
  427. "c['emon]+" // c'mon, com'on, comeon
  428. ].join("|") + ")", "gi"),
  429. // image title ":{anything}:", etc.
  430. regexEmoji = /(:.*:)|[\u{1f300}-\u{1f64f}\u{1f680}-\u{1f6ff}\u{1f900}-\u{1f9ff}]/gu,
  431. regexWhitespace = /\s+/g,
  432.  
  433. comments = $$(".js-discussion .TimelineItem").filter(comment => {
  434. const classes = comment.className.split(" ");
  435. return !exceptions.some(ex => classes.includes(ex));
  436. }),
  437. len = comments.length,
  438.  
  439. loop = () => {
  440. let wrapper, el, tmp, txt, img, hasLink, dupe;
  441. max = 0;
  442. while (max < 20 && indx < len) {
  443. if (indx >= len) {
  444. if (init) {
  445. item.classList.toggle("ghic-has-content", count > 0);
  446. }
  447. return;
  448. }
  449. wrapper = comments[indx];
  450. // save author list to prevent repeat +1s
  451. el = $(".timeline-comment-header .author", wrapper);
  452. txt = (el ? el.textContent || "" : "").toLowerCase();
  453. dupe = true;
  454. if (txt && authors.indexOf(txt) < 0) {
  455. authors[authors.length] = txt;
  456. dupe = false;
  457. }
  458. el = $(".comment-body", wrapper);
  459. if (el) {
  460. // ignore quoted messages, but get all fragments
  461. tmp = $$(".email-fragment", el);
  462. // some posts only contain a link to related issues; these should not be counted as a +1
  463. // see https://github.com/isaacs/github/issues/618#issuecomment-200869630
  464. hasLink = $$(tmp.length ? ".email-fragment .issue-link" : ".issue-link", el).length;
  465. if (tmp.length) {
  466. // ignore quoted messages
  467. txt = getAllText(tmp);
  468. } else {
  469. txt = (el ? el.textContent || "" : "").trim();
  470. }
  471. if (!txt) {
  472. img = $("img", el);
  473. if (img) {
  474. txt = img.getAttribute("title") || img.getAttribute("alt");
  475. }
  476. }
  477. // remove fluff
  478. txt = (txt || "")
  479. .replace(regexEmoji, "")
  480. .replace(regexHide, "")
  481. .replace(regexPlus, "")
  482. .replace(regexWhitespace, " ")
  483. .trim();
  484. if (txt === "" || (txt.length <= 4 && !hasLink)) {
  485. if (init && !settings.plus1.isHidden) {
  486. // +1 Comments has-content
  487. item.classList.toggle("ghic-has-content", true);
  488. return;
  489. }
  490. if (settings.plus1.isHidden) {
  491. wrapper.classList.add("ghic-hidden", "ghic-highlight");
  492. total++;
  493. // one +1 per author
  494. if (!dupe) {
  495. count++;
  496. }
  497. } else if (!init) {
  498. wrapper.classList.remove("ghic-hidden");
  499. }
  500. max++;
  501. }
  502. }
  503. indx++;
  504. }
  505. if (indx < len) {
  506. setTimeout(() => {
  507. window.requestAnimationFrame(loop);
  508. }, 200);
  509. } else {
  510. if (init) {
  511. item.classList.toggle("ghic-has-content", count > 0);
  512. }
  513. $(".ghic-menu .ghic-plus1 .ghic-count").textContent = total
  514. ? "(" + total + " hidden)"
  515. : " ";
  516. addCountToReaction(count);
  517. }
  518. };
  519. loop();
  520. }
  521.  
  522. function getAllText(el) {
  523. let txt = "",
  524. indx = el.length;
  525. // text order doesn't matter
  526. while (indx--) {
  527. txt += el[indx].textContent.trim();
  528. }
  529. return txt;
  530. }
  531.  
  532. function addCountToReaction(count) {
  533. if (!count) {
  534. count = ($(".ghic-menu .ghic-plus1 .ghic-count").textContent || "")
  535. .replace(/[()]/g, "")
  536. .trim();
  537. }
  538. let comment = $(".timeline-comment"),
  539. tmp = $(
  540. ".has-reactions button[value='THUMBS_UP react'], .has-reactions button[value='THUMBS_UP unreact']",
  541. comment
  542. ),
  543. el = $(".ghic-count", comment);
  544. if (el) {
  545. // the count may have been appended to the comment & now
  546. // there is a reaction, so remove any "ghic-count" elements
  547. el.parentNode.removeChild(el);
  548. }
  549. if (count) {
  550. if (tmp) {
  551. el = document.createElement("span");
  552. el.className = "ghic-count";
  553. el.textContent = count ? " + " + count + " (from hidden comments)" : "";
  554. tmp.appendChild(el);
  555. } else {
  556. el = document.createElement("p");
  557. el.className = "ghic-count";
  558. el.innerHTML = "<hr>" + plus1Icon + " " + count + " (from hidden comments)";
  559. $(".comment-body", comment).appendChild(el);
  560. }
  561. }
  562. }
  563.  
  564. function hideParticipant(el) {
  565. if (el) {
  566. el.classList.toggle("comments-hidden");
  567. let name = el.getAttribute("aria-label"),
  568. results = $$(
  569. ".TimelineItem, .commit-comment, .discussion-item"
  570. ).filter(el => {
  571. const author = $(".js-discussion .author", el);
  572. return author ? name === author.textContent.trim() : false;
  573. });
  574. // use a different participant class name to hide timeline events
  575. // or unselecting all users will show everything
  576. if (el.classList.contains("comments-hidden")) {
  577. addClass(results, "ghic-hidden-participant");
  578. } else {
  579. removeClass(results, "ghic-hidden-participant");
  580. }
  581. results = [];
  582. }
  583. }
  584.  
  585. function update() {
  586. if ($("#discussion_bucket") && $(".ghic-button")) {
  587. let keys = Object.keys(settings),
  588. indx = keys.length;
  589. while (indx--) {
  590. // true flag for init - no need to remove classes
  591. hideStuff(keys[indx], true);
  592. }
  593. addAvatars();
  594. }
  595. }
  596.  
  597. function checkItem(event) {
  598. if (document.getElementById("discussion_bucket")) {
  599. let name,
  600. target = event.target,
  601. wrap = target && target.closest(".dropdown-item, .ghic-participants");
  602. if (target && wrap) {
  603. if (target.nodeName === "INPUT") {
  604. getInputValues();
  605. saveSettings();
  606. name = wrap.dataset.ghic;
  607. if (name) {
  608. hideStuff(name);
  609. }
  610. } else if (target.classList.contains("ghic-avatar")) {
  611. // make sure we're targeting the span wrapping the image
  612. hideParticipant(target.nodeName === "IMG" ? target.parentNode : target);
  613. } else if (regex.test(target.nodeName)) {
  614. // clicking on the SVG may target the svg or path inside
  615. hideParticipant(target.closest(".ghic-avatar"));
  616. }
  617. } else if (target.id === "ghic-only-active") {
  618. target.closest(".select-menu-header").classList.toggle("ghic-active", target.checked);
  619. GM_setValue("onlyActive", target.checked);
  620. }
  621. // Make button show if it is active
  622. target = $(".ghic-button .btn");
  623. if (target) {
  624. const active = $$(".ghic-hidden, .ghic-hidden-participant").length > 0;
  625. target.classList.toggle("btn-outline", active);
  626. }
  627. }
  628. }
  629.  
  630. function $(selector, el) {
  631. return (el || document).querySelector(selector);
  632. }
  633.  
  634. function $$(selector, el) {
  635. return [...(el || document).querySelectorAll(selector)];
  636. }
  637.  
  638. function addClass(els, name) {
  639. let indx,
  640. len = els.length;
  641. for (indx = 0; indx < len; indx++) {
  642. els[indx] && els[indx].classList.add(name);
  643. }
  644. return len;
  645. }
  646.  
  647. function removeClass(els, name) {
  648. let indx,
  649. len = els.length;
  650. for (indx = 0; indx < len; indx++) {
  651. els[indx] && els[indx].classList.remove(name);
  652. }
  653. }
  654.  
  655. function toggleClass(els, name, flag) {
  656. els = Array.isArray(els) ? els : [els];
  657. const undef = typeof flag === "undefined";
  658. let el,
  659. indx = els.length;
  660. while (indx--) {
  661. el = els[indx];
  662. if (el) {
  663. if (undef) {
  664. flag = !el.classList.contains(name);
  665. }
  666. if (flag) {
  667. el.classList.add(name);
  668. } else {
  669. el.classList.remove(name);
  670. }
  671. }
  672. }
  673. }
  674.  
  675. function init() {
  676. getSettings();
  677. addMenu();
  678. $("body").addEventListener("click", checkItem);
  679. update();
  680. }
  681.  
  682. // update list when content changes
  683. document.addEventListener("ghmo:container", addMenu);
  684. document.addEventListener("ghmo:comments", update);
  685. init();
  686.  
  687. })();