Hi, Jira 🚀

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

  1. // ==UserScript==
  2. // @name Hi, Jira 🚀
  3. // @namespace https://xianghongai.github.io/
  4. // @version 1.2.2
  5. // @description 作者在 Jira v8.13.15 编写和应用,其它版本可能需要调整源码。另外,需要调整域名匹配规则。
  6. // @author Nicholas Hsiang
  7. // @icon https://www.feature.com/favicon.ico
  8. // @match *://jira.feature-inc.cn/*
  9. // @grant unsafeWindow
  10. // @license MIT
  11. // ==/UserScript==
  12.  
  13. (function () {
  14. 'use strict';
  15.  
  16. /**
  17. * 🚀 可编辑字段禁止点击文本编辑,只能通过点击编辑按钮图标操作
  18. */
  19.  
  20. function editableField(event) {
  21. const targetEle = event.target;
  22.  
  23. // i. 点击的是“编辑”按钮图标
  24. const isEdit = hasClass(targetEle, 'aui-iconfont-edit');
  25.  
  26. // ii. 点击的是图片、链接
  27. // img
  28. // a
  29. const inWhitelist = ['img', 'a'].includes(targetEle.tagName.toLowerCase());
  30.  
  31. if (inWhitelist || isEdit) {
  32. return true;
  33. }
  34.  
  35. // 1. 父层为可编辑字段 (非 Description)
  36. const editableFieldParent = getParents(
  37. targetEle,
  38. '.editable-field:not(#description-val)'
  39. );
  40. const isEditableFieldParent =
  41. editableFieldParent && hasClass(editableFieldParent, 'inactive');
  42.  
  43. // 2. 当前层为可编辑字段 (非 Description)
  44. const isEditableField =
  45. hasClass(targetEle, 'editable-field') && hasClass(targetEle, 'inactive');
  46.  
  47. // 3. Description 字段
  48. const isDescriptionField = getParents(targetEle, '.user-content-block');
  49.  
  50. if (isEditableFieldParent || isEditableField || isDescriptionField) {
  51. event.preventDefault();
  52. event.stopPropagation();
  53.  
  54. return false;
  55. }
  56. }
  57.  
  58. /**
  59. * 🚀 快捷功能
  60. */
  61.  
  62. function keyboardShortcut(event) {
  63. // 在 macOS 上,Option (ALT) 键有特殊功能,它用于输入特殊字符和符号。
  64. // 按下 Option+T 时,macOS 可能将其解释为一个特殊字符输入,而不是单纯的修饰键+字母组合,
  65. // 这就导致 JavaScript 事件系统接收到的不是标准的按键事件,而是 "Unidentified"。
  66. // event.code 表示物理按键的位置,与键盘布局无关。
  67.  
  68. // 按 ALT+L 添加 Link
  69. if (
  70. event.altKey &&
  71. ((event.key === 'Unidentified' && event.code === 'KeyL') ||
  72. event.key.toLowerCase() === 'l')
  73. ) {
  74. document.querySelector('#link-issue')?.click();
  75. }
  76. }
  77.  
  78. /**
  79. * 🚀 DOM 事件
  80. */
  81.  
  82. function click(event) {
  83. editableField(event);
  84. }
  85.  
  86. document.addEventListener('click', click, true);
  87.  
  88. function keydown(event) {
  89. keyboardShortcut(event);
  90. }
  91.  
  92. document.addEventListener('keydown', keydown, true);
  93.  
  94. // #region COMMON
  95. function hasClass(el, className) {
  96. if (el.classList) {
  97. return el.classList.contains(className);
  98. }
  99. return !!el.className.match(new RegExp('(\\s|^)' + className + '(\\s|$)'));
  100. }
  101.  
  102. function getParents(elem, selector) {
  103. for (; elem && elem !== document; elem = elem.parentNode) {
  104. if (elem.matches(selector)) return elem;
  105. }
  106. return null;
  107. }
  108. // #endregion
  109. })();