您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Replaces the Jira icons with the relevant issue-type icon in the quick-search-results popup.
// ==UserScript== // @namespace BF_Chaos // @name Jira Search Icons Rewriter // @description Replaces the Jira icons with the relevant issue-type icon in the quick-search-results popup. // @license GPL // @version 1 // @grant GM.xmlHttpRequest // @include https://*.atlassian.net/jira/* // @match https://*.atlassian.net/jira/* // ==/UserScript== let global_IssueIdToIconUrl = {}; let headerNode = document.body; let domObserverConfig = { subtree: true, childList: true, }; const getIssueId = (node) => { return node.getAttribute("data-id").replace(/^.*:issue\/(\d+)$/, "$1"); }; const replaceIconInNode = (node, issueType, iconUrl) => { console.log(`========= replaceIconInNode(..., ${issueType}, ${iconUrl})`, node); let iconNode = node.querySelector("a[data-testid='search-dialog-result-link'] span[role=img]"); iconNode.innerHTML = `<img src="${iconUrl}">`; }; const replaceIcon = (issueId, issueType, iconUrl) => { global_IssueIdToIconUrl[issueId] = [issueType, iconUrl]; console.log(`========= replaceIcon(${issueId}, ${issueType}, ${iconUrl})`); let node = document.querySelector(`[data-result-type='searchResults'][data-product='jira'][data-id$=':issue/${issueId}'].search-dialog-result`); if (node) { replaceIconInNode(node, issueType, iconUrl); } }; const rewriteSearchResultsIcons = (container, observer) => { let results = container.querySelectorAll("[data-result-type='searchResults'][data-product='jira'][data-id*=':issue/'].search-dialog-result"); if (results.length === 0) { return; } let jiraReqBody = { "expand": ["names"], "fields": ["issuetype"], "issueIdsOrKeys": [], "properties": [] }; let issueIdToIconUrl = {}; let issueIdToIconUrlCount = 0; results.forEach((node) => { const issueId = getIssueId(node); if (global_IssueIdToIconUrl[issueId]) { issueIdToIconUrl[issueId] = global_IssueIdToIconUrl[issueId]; issueIdToIconUrlCount += 1; } else { jiraReqBody.issueIdsOrKeys.push(getIssueId(node)); } }); console.log(`========= rewriteSearchResultsIcons, found ${issueIdToIconUrlCount} known issues, ${jiraReqBody.issueIdsOrKeys.length} unknown issues.`); if (issueIdToIconUrl.length !== 0) { console.log(`========= rewriteSearchResultsIcons, replace icons in ${issueIdToIconUrlCount} known issues`); observer.disconnect(); results.forEach((node) => { const issueId = getIssueId(node); if (issueIdToIconUrl[issueId]) { replaceIconInNode(node, issueIdToIconUrl[issueId][0], issueIdToIconUrl[issueId][1]); } }); observer.observe(headerNode, domObserverConfig); } if (jiraReqBody.issueIdsOrKeys.length !== 0) { console.log(`========= rewriteSearchResultsIcons, query icons for ${jiraReqBody.issueIdsOrKeys.length} unknown issues`); let url = `https://${window.location.host}/rest/api/3/issue/bulkfetch`; let headers = { "Accept": "application/json", "Content-Type": "application/json", "Origin": `https://${window.location.host}`, "Referer": window.location.toString(), }; console.log(url); console.log(headers); console.log(jiraReqBody); /* fetch(url, { method: "POST", headers: headers, body: JSON.stringify(jiraReqBody), }) .then((r) => { console.log(r); return r.json(); }) .then((r) => { console.log(r); if (r["errorMessages"]) { console.log(`========= Failed to query icons: ${r.errorMessages}`); } else { console.log(`========= rewriteSearchResultsIcons, replace icons for ${r.issues.length} issues`); observer.disconnect(); for (issue of r.issues) { replaceIcon(issue.id, issue.fields.issuetype.name, issue.fields.issuetype.iconUrl); } observer.observe(headerNode, domObserverConfig); } }); */ GM.xmlHttpRequest({ method: "POST", url: url, data: JSON.stringify(jiraReqBody), headers: headers, onload: function(response) { console.log(response); let r = JSON.parse(response.responseText); console.log(r); if (r["errorMessages"]) { console.log(`========= Failed to query icons: ${r.errorMessages}`); } else { console.log(`========= rewriteSearchResultsIcons, replace icons for ${r.issues.length} issues`); observer.disconnect(); for (issue of r.issues) { replaceIcon(issue.id, issue.fields.issuetype.name, issue.fields.issuetype.iconUrl); } observer.observe(headerNode, domObserverConfig); } } }); } }; if (headerNode) { console.log("========= register header observer"); const domObserverCallback = (mutationList, observer) => { const searchResultsContainer = headerNode.querySelector("[data-testid='search-dialog']") if (searchResultsContainer) { console.log("========= search results found, start rewriting"); rewriteSearchResultsIcons(searchResultsContainer, observer); console.log("========= search results found, finished rewriting"); } else { console.log("========= search results not found"); } }; const observer = new MutationObserver(domObserverCallback); observer.observe(headerNode, domObserverConfig); } else { console.log("========= Header node not found, cannot register header observer!"); }