Hi, Jira 🚀

作者在 Jira v8.13.15 编写和应用,其它版本可能需要调整源码。另外,需要调整域名匹配规则。

// ==UserScript==
// @name         Hi, Jira 🚀
// @namespace    https://xianghongai.github.io/
// @version      1.2.2
// @description  作者在 Jira v8.13.15 编写和应用,其它版本可能需要调整源码。另外,需要调整域名匹配规则。
// @author       Nicholas Hsiang
// @icon         https://www.feature.com/favicon.ico
// @match        *://jira.feature-inc.cn/*
// @grant        unsafeWindow
// @license      MIT
// ==/UserScript==

(function () {
  'use strict';

  /**
   * 🚀 可编辑字段禁止点击文本编辑,只能通过点击编辑按钮图标操作
   */

  function editableField(event) {
    const targetEle = event.target;

    // i. 点击的是“编辑”按钮图标
    const isEdit = hasClass(targetEle, 'aui-iconfont-edit');

    // ii. 点击的是图片、链接
    // img
    // a
    const inWhitelist = ['img', 'a'].includes(targetEle.tagName.toLowerCase());

    if (inWhitelist || isEdit) {
      return true;
    }

    // 1. 父层为可编辑字段 (非 Description)
    const editableFieldParent = getParents(
      targetEle,
      '.editable-field:not(#description-val)'
    );
    const isEditableFieldParent =
      editableFieldParent && hasClass(editableFieldParent, 'inactive');

    // 2. 当前层为可编辑字段 (非 Description)
    const isEditableField =
      hasClass(targetEle, 'editable-field') && hasClass(targetEle, 'inactive');

    // 3. Description 字段
    const isDescriptionField = getParents(targetEle, '.user-content-block');

    if (isEditableFieldParent || isEditableField || isDescriptionField) {
      event.preventDefault();
      event.stopPropagation();

      return false;
    }
  }

  /**
   * 🚀 快捷功能
   */

  function keyboardShortcut(event) {
    // 在 macOS 上,Option (ALT) 键有特殊功能,它用于输入特殊字符和符号。
    // 按下 Option+T 时,macOS 可能将其解释为一个特殊字符输入,而不是单纯的修饰键+字母组合,
    // 这就导致 JavaScript 事件系统接收到的不是标准的按键事件,而是 "Unidentified"。
    // event.code 表示物理按键的位置,与键盘布局无关。

    // 按 ALT+L 添加 Link
    if (
      event.altKey &&
      ((event.key === 'Unidentified' && event.code === 'KeyL') ||
        event.key.toLowerCase() === 'l')
    ) {
      document.querySelector('#link-issue')?.click();
    }
  }

  /**
   * 🚀 DOM 事件
   */

  function click(event) {
    editableField(event);
  }

  document.addEventListener('click', click, true);

  function keydown(event) {
    keyboardShortcut(event);
  }

  document.addEventListener('keydown', keydown, true);

  // #region COMMON
  function hasClass(el, className) {
    if (el.classList) {
      return el.classList.contains(className);
    }
    return !!el.className.match(new RegExp('(\\s|^)' + className + '(\\s|$)'));
  }

  function getParents(elem, selector) {
    for (; elem && elem !== document; elem = elem.parentNode) {
      if (elem.matches(selector)) return elem;
    }
    return null;
  }
  // #endregion
})();