bitbucket-global-search

bitbucket全局搜索

// ==UserScript==
// @name         bitbucket-global-search
// @version      0.0.8
// @author       alan
// @include      https://code.fineres.com/*
// @noframes
// @description  bitbucket全局搜索
// @namespace bitbucket-global-search
// @license MIT
// @grant        GM_xmlhttpRequest
// @grant        GM_addStyle
// @grant        GM_getResourceText
// @resource css https://code.fineres.com/download/resources/com.atlassian.bitbucket.server.bitbucket-frontend:split_dashboard/dashboard.bundle.css
// ==/UserScript==
(() => {
  "use strict";
  var __webpack_exports__ = {};
  ;
  function getLastReps() {
    return new Promise((resolve, reject) => {
      GM_xmlhttpRequest({
        method: "GET",
        url: "https://code.fineres.com/rest/api/latest/profile/recent/repos?avatarSize=64&limit=10",
        headers: {
          Accept: "application/json, text/javascript, */*; q=0.01",
          "Accept-Language": "zh,zh-CN;q=0.9,en-US;q=0.8,en;q=0.7",
          "Cache-Control": "no-cache",
          Connection: "keep-alive",
          "Content-Type": "application/json",
          "X-Requested-With": "XMLHttpRequest",
          cookie: document.cookie
        },
        data: ``,
        onload(response) {
          const result = JSON.parse(response.responseText);
          resolve(result);
        },
        onerror(response) {
          console.error("\u8BF7\u6C42\u5931\u8D25:");
          console.error(response);
          reject(response);
        }
      });
    });
  }
  function getAllPorjects() {
    return new Promise((resolve, reject) => {
      GM_xmlhttpRequest({
        method: "GET",
        url: "https://code.fineres.com/rest/categories/latest/projects?start=0&limit=5000&project=",
        headers: {
          Accept: "application/json, text/javascript, */*; q=0.01",
          "Accept-Language": "zh,zh-CN;q=0.9,en-US;q=0.8,en;q=0.7",
          "Cache-Control": "no-cache",
          Connection: "keep-alive",
          "Content-Type": "application/json",
          "X-Requested-With": "XMLHttpRequest",
          cookie: document.cookie
        },
        data: ``,
        onload(response) {
          const result = JSON.parse(response.responseText);
          resolve(result);
        },
        onerror(response) {
          console.error("\u8BF7\u6C42\u5931\u8D25:");
          console.error(response);
          reject(response);
        }
      });
    });
  }
  function getReposByProject(project) {
    return new Promise((resolve, reject) => {
      GM_xmlhttpRequest({
        method: "GET",
        url: `https://code.fineres.com/rest/api/latest/projects/${project}/repos?avatarSize=64&limit=5000`,
        headers: {
          Accept: "application/json, text/javascript, */*; q=0.01",
          "Accept-Language": "zh,zh-CN;q=0.9,en-US;q=0.8,en;q=0.7",
          "Cache-Control": "no-cache",
          Connection: "keep-alive",
          "Content-Type": "application/json",
          "X-Requested-With": "XMLHttpRequest",
          cookie: document.cookie
        },
        data: ``,
        onload(response) {
          const result = JSON.parse(response.responseText);
          resolve(result);
        },
        onerror(response) {
          console.error("\u8BF7\u6C42\u5931\u8D25:");
          console.error(response);
          reject(response);
        }
      });
    });
  }
  ;
  var __async = (__this, __arguments, generator) => {
    return new Promise((resolve, reject) => {
      var fulfilled = (value) => {
        try {
          step(generator.next(value));
        } catch (e) {
          reject(e);
        }
      };
      var rejected = (value) => {
        try {
          step(generator.throw(value));
        } catch (e) {
          reject(e);
        }
      };
      var step = (x) => x.done ? resolve(x.value) : Promise.resolve(x.value).then(fulfilled, rejected);
      step((generator = generator.apply(__this, __arguments)).next());
    });
  };
  let cacheResult = [];
  function appendEntryButton() {
    const nav = document.querySelector(".aui-nav");
    const a = document.createElement("a");
    a.innerText = "\u5168\u5C40\u641C\u7D22";
    a.style.cursor = "pointer";
    const li = document.createElement("li");
    li.appendChild(a);
    nav == null ? void 0 : nav.appendChild(li);
    return a;
  }
  function init() {
    appendDialog();
  }
  function appendDialog() {
    const dialog = document.createElement("div");
    dialog.innerHTML = `
    <div class="aui-dialog2 aui-dialog2-large aui-dialog2-current aui-layer" id="aui-dialog2-1" role="dialog" aria-labelledby="aui-dialog2-1-heading" aria-hidden="true">
      <div class="aui-dialog2-header">
        <h2 class="aui-dialog2-header-main" id="aui-dialog2-1-heading">\u5168\u5C40\u641C\u7D22</h2>
        <a class="aui-dialog2-header-close">
          <span class="aui-icon aui-icon-small aui-iconfont-close-dialog">Close</span>
        </a>
      </div>
      <div class="aui-dialog2-content" style="height: 500px;">
        <div class="aui-field-group">
          <div class="aui-field">
            <form class="aui" onsubmit="return false">
                <input placeholder="\u8BF7\u8F93\u5165\u5173\u952E\u5B57" id="search" class="text" type="search" name="search">
                <input type="button" value="\u5168\u5C40\u641C\u7D22" id="search-button" class="aui-button"></input>
            </form>
          </div>
        </div>
        <div class="search-result-repositories">
          <ol id="search-result-repositories" class="dashboard-repositories-list"></ol>
        </div>
      </div>
      <div class="aui-dialog2-footer">
        <div id="search-progress" style="display: inline-flex;align-items: center;height: 100%;">
        </div>
        <div class="aui-dialog2-footer-actions">
          <button class="aui-button" id="close-btn">\u5173\u95ED</button>
        </div>
      </div>
    </div>
  `;
    document.body.appendChild(dialog);
    AJS.$("#close-btn").click(() => {
      AJS.dialog2("#aui-dialog2-1").hide();
    });
    AJS.$(".aui-iconfont-close-dialog").click(() => {
      AJS.dialog2("#aui-dialog2-1").hide();
    });
    return dialog;
  }
  function renderSearchResult(items) {
    const repositories = document.querySelector("#search-result-repositories");
    if (repositories) {
      repositories;
      repositories.innerHTML = `${items.map((item) => `<li><a href="/projects/${item.project.key}/repos/${item.name}/browse" aria-label="${item.project.name} / ${item.name}"><div class="project-avatar"><div style="display: inline-block; position: relative; outline: 0px;"><span class="css-50fm9s"><span class="css-1gv8fjs" role="img" aria-label="" style="background-image: url('${item.project.avatarUrl}');height: 40px;background-size: contain;"></span></span></div></div><div class="repository-details"><div class="repository-name">${item.name}</div><div class="project-name">${item.project.name}</div></div></a></li>`).join("")}`;
    }
  }
  function renderLoading() {
    const repositories = document.querySelector("#search-result-repositories");
    if (repositories) {
      repositories.innerHTML = `<div class="loading"><div class="loading-spinner" style="height: 100px;width: 100%;display: flex;justify-content: center;align-items: center;"><aui-spinner></aui-spinner></div></div>`;
    }
  }
  function renderEmpty() {
    const repositories = document.querySelector("#search-result-repositories");
    if (repositories) {
      repositories.innerHTML = `<div style="height: 100px;width: 100%;display: flex;justify-content: center;align-items: center;" class="empty">\u6682\u65E0\u6570\u636E</div>`;
    }
  }
  function filterReps(keyWord, render) {
    return __async(this, null, function* () {
      let searchResult = cacheResult;
      if (searchResult.length === 0) {
        const result2 = yield getAllPorjects();
        const projects = result2.result;
        for (let index = 0; index < projects.length; index++) {
          const project = projects[index];
          AJS.$("#search-progress").text(`\u6B63\u5728\u641C\u7D22${project.projectName}\u7684\u4ED3\u5E93`);
          const repo = yield getReposByProject(project.projectKey);
          searchResult = [...searchResult, ...repo.values];
        }
        cacheResult = searchResult;
      }
      const result = searchResult.filter((v) => {
        return v.name.toLowerCase().includes(keyWord.toLowerCase());
      });
      if (result.length > 0) {
        render(result);
      } else {
        renderEmpty();
      }
    });
  }
  function debounce(fn, wait) {
    let timeout = null;
    return function() {
      if (timeout !== null)
        clearTimeout(timeout);
      timeout = window.setTimeout(fn, wait);
    };
  }
  function main() {
    return __async(this, null, function* () {
      appendStyle();
      cacheResult = JSON.parse(localStorage.getItem("bitbucket-search-result") || "[]");
      init();
      appendEntryButton().addEventListener("click", () => __async(this, null, function* () {
        var _a;
        const search = document.getElementById("search");
        search.value = "";
        search.focus();
        const inputAction = debounce(() => {
          if (cacheResult.length === 0)
            return;
          const { value } = search;
          if (value) {
            filterReps(value, renderSearchResult);
          } else {
            renderEmpty();
          }
        }, 500);
        search.addEventListener("input", inputAction);
        AJS.dialog2("#aui-dialog2-1").show();
        (_a = document.getElementById("search")) == null ? void 0 : _a.addEventListener("keydown", (e) => {
          if (e.keyCode === 13) {
            AJS.$("#search-button").click();
          }
        });
        AJS.$("#search-button").click(() => __async(this, null, function* () {
          cacheResult = [];
          const { values } = yield getLastReps();
          const searchText = search.value;
          renderLoading();
          if (!searchText) {
            renderSearchResult(values);
          }
          yield filterReps(searchText, renderSearchResult);
          AJS.$("#search-progress").text("");
          localStorage.setItem("bitbucket-search-result", JSON.stringify(cacheResult));
        }));
      }));
    });
  }
  function appendStyle() {
    GM_addStyle(GM_getResourceText("css"));
    GM_addStyle(`
    .css-50fm9s {
      height: 40px;
      width: 40px;
      -webkit-box-align: stretch;
      align-items: stretch;
      background-color: rgb(255, 255, 255);
      border-radius: 3px;
      box-sizing: content-box;
      cursor: inherit;
      display: flex;
      flex-direction: column;
      -webkit-box-pack: center;
      justify-content: center;
      outline: none;
      overflow: hidden;
      position: static;
      transform: translateZ(0px);
      transition: transform 200ms ease 0s, opacity 200ms ease 0s;
      box-shadow: rgb(255, 255, 255) 0px 0px 0px 2px;
      border: none;
      margin: 2px;
      padding: 0px;
      font-size: inherit;
      font-family: inherit;
    }
  `);
  }
  setTimeout(() => {
    main();
  }, 500);
})();