Greasy Fork is available in English.

rangyInput@dkn

覆盖替换式插入、在文选处前后追加式插入

Ten skrypt nie powinien być instalowany bezpośrednio. Jest to biblioteka dla innych skyptów do włączenia dyrektywą meta // @require https://update.greasyfork.org/scripts/475357/1257488/rangyInput%40dkn.js

  1. (()=> {
  2. // 自执行改为 function 编辑框fn() ⥅就变为外围广谱, 使用:在需要页面⥅编辑框fn(); 然后每次使用先⥅window.编辑框插内容.init();
  3. //🕗V2 同步更新⥅库 见 https:github.com/dnknn/js/issues/63#issuecomment-1739080488
  4. window.编辑框插内容 = {}; let getSelection, setSelection;
  5. function isHostMethod(object, property) {
  6. var t = typeof object[property]; return t === "function" || (!!(t == "object" && object[property])) || t == "unknown";
  7. }
  8. function isHostProperty(object, property) {return typeof(object[property]) != "undefined";}
  9. function isHostObject(object, property) {return !!(typeof(object[property]) == "object" && object[property]);}
  10. function fail(reason) {window.console.log(`RangyInputs not supported in your browser. Reason: ${reason}`);}
  11. function adjustOffsets(el, start, end) {
  12. if (start < 0) {start += el.value.length;}
  13. if (typeof end == "undefined") {end = start;}
  14. if (end < 0) {end += el.value.length;}
  15. return { start: start, end: end };
  16. }
  17. function makeSelection(el, start, end) {
  18. return {
  19. start: start, end: end, length: end - start, text: el.value.slice(start, end)
  20. };
  21. }
  22. function getBody() {return isHostObject(document, "body") ? document.body : document.querySelector("body");}
  23.  
  24. window.编辑框插内容.init = ()=> {
  25. const testTextArea = document.createElement("textarea"); getBody().appendChild(testTextArea);
  26. if (isHostProperty(testTextArea, "selectionStart") && isHostProperty(testTextArea, "selectionEnd") ) {
  27. getSelection = el => {return makeSelection(el, el.selectionStart, el.selectionEnd);};
  28. setSelection = (el, startOffset, endOffset) => {
  29. var offsets = adjustOffsets(el, startOffset, endOffset);
  30. el.selectionStart = offsets.start; el.selectionEnd = offsets.end;
  31. };
  32. } else if (isHostMethod(testTextArea, "createTextRange") && isHostObject(document, "selection") && isHostMethod(document.selection, "createRange")
  33. ) {
  34. getSelection = el => {
  35. let normalizedValue, textInputRange, len, endRange, start = 0, end = 0;
  36. const range = document.selection.createRange();
  37. if (range && range.parentElement() == el) {
  38. len = el.value.length;
  39. normalizedValue = el.value.replace(/\r\n/g, "\n");
  40. textInputRange = el.createTextRange();
  41. textInputRange.moveToBookmark(range.getBookmark());
  42. endRange = el.createTextRange();
  43. endRange.collapse(false);
  44. if (textInputRange.compareEndPoints("StartToEnd", endRange) > -1) {
  45. start = end = len;
  46. } else {
  47. start = -textInputRange.moveStart("character", -len);
  48. start += normalizedValue.slice(0, start).split("\n").length - 1;
  49. if (textInputRange.compareEndPoints("EndToEnd", endRange) > -1) {
  50. end = len;
  51. } else {
  52. end = -textInputRange.moveEnd("character", -len);
  53. end += normalizedValue.slice(0, end).split("\n").length - 1;
  54. }
  55. }
  56. }
  57. return makeSelection(el, start, end);
  58. };
  59.  
  60. const offsetToRangeCharacterMove = function(el, offset) {return offset - (el.value.slice(0, offset).split("\r\n").length - 1);};
  61. setSelection = (el, startOffset, endOffset) => {
  62. const offsets = adjustOffsets(el, startOffset, endOffset),
  63. range = el.createTextRange(),
  64. startCharMove = offsetToRangeCharacterMove(el, offsets.start);
  65. range.collapse(true);
  66. if (offsets.start == offsets.end) {
  67. range.move("character", startCharMove);
  68. } else {
  69. range.moveEnd(
  70. "character",
  71. offsetToRangeCharacterMove(el, offsets.end)
  72. );
  73. range.moveStart("character", startCharMove);
  74. }
  75. range.select();
  76. };
  77.  
  78. } else {getBody().removeChild(testTextArea); fail("No means of finding text input caret position"); return;}
  79.  
  80. getBody().removeChild(testTextArea); // Clean up
  81.  
  82. function getValueAfterPaste(el, text) {
  83. const val = el.value, sel = getSelection(el), selStart = sel.start;
  84. return {
  85. value: val.slice(0, selStart) + text + val.slice(sel.end),
  86. index: selStart, replaced: sel.text
  87. };
  88. }
  89.  
  90. function pasteTextWithCommand(el, text) {
  91. el.focus(); const sel = getSelection(el);
  92. // Hack to work around incorrect delete command when deleting the last
  93. // word on a line
  94. setSelection(el, sel.start, sel.end);
  95. if (text === "") {document.execCommand("delete", false, null);}
  96. else {document.execCommand("insertText", false, text);}
  97.  
  98. return {replaced: sel.text, index: sel.start};
  99. }
  100.  
  101. function pasteTextWithValueChange(el, text) {
  102. el.focus(); const valueAfterPaste = getValueAfterPaste(el, text);
  103. el.value = valueAfterPaste.value; return valueAfterPaste;
  104. }
  105.  
  106. let pasteText = (el, text) => {
  107. const valueAfterPaste = getValueAfterPaste(el, text);
  108. try {
  109. const pasteInfo = pasteTextWithCommand(el, text);
  110. if (el.value == valueAfterPaste.value) {
  111. pasteText = pasteTextWithCommand;
  112. return pasteInfo;
  113. }
  114. } catch (ex) {
  115. // Do nothing and fall back to changing the value manually
  116. }
  117. pasteText = pasteTextWithValueChange;
  118. el.value = valueAfterPaste.value;
  119. return valueAfterPaste;
  120. };
  121.  
  122. function updateSelectionAfterInsert(el, startIndex, text, selBehaviour) {
  123. let endIndex = startIndex + text.length;
  124. // selBehaviour = (typeof selBehaviour=="string") ? selBehaviour.toLowerCase() : ""; //新增[数组参数]用于替换选择后的选中的自定义范围 2
  125. selBehaviour = (typeof selBehaviour=="string") ? selBehaviour.toLowerCase() : Array.isArray(selBehaviour) ? selBehaviour : "";
  126. if ((selBehaviour=="collapsetoend" || selBehaviour=="select") && /[\r\n]/.test(text)) {
  127. // Find the length of the actual text inserted, which could vary
  128. // depending on how the browser deals with line breaks
  129. const normalizedText = text.replace(/\r\n/g, "\n").replace(/\r/g, "\n");
  130. endIndex = startIndex + normalizedText.length;
  131. const firstLineBreakIndex = startIndex + normalizedText.indexOf("\n");
  132.  
  133. if (el.value.slice(firstLineBreakIndex, firstLineBreakIndex + 2) == "\r\n") {
  134. // Browser uses \r\n, so we need to account for extra \r characters
  135. endIndex += normalizedText.match(/\n/g).length;
  136. }
  137. }
  138.  
  139. switch(selBehaviour) {
  140. case "select": setSelection(el, startIndex, endIndex); break;
  141. case "collapsetostart": setSelection(el, startIndex, startIndex); break;
  142. case "collapsetoend": setSelection(el, endIndex, endIndex); break;
  143. //新增[数组参数]用于替换选择后的选中的自定义范围 3 即在👆范围基础上的二次范围
  144. // default: Array.isArray(selBehaviour)&&setSelection(el, startIndex+selBehaviour[0], endIndex+selBehaviour[1]);
  145. default:
  146. if(Array.isArray(selBehaviour)) {
  147. selBehaviour.includes(``)
  148. ? selBehaviour[0]===`` ? setSelection(el, startIndex+selBehaviour[1], startIndex+selBehaviour[2]) // [``,11,22] 相对于起点加减
  149. : setSelection(el, endIndex+selBehaviour[0], endIndex+selBehaviour[1]) // [11,22,``] 相对于终点加减
  150. : setSelection(el, startIndex+selBehaviour[0], endIndex+selBehaviour[1]); // [11,22] 相对于文选替换后的范围加减
  151. }
  152.  
  153. }
  154.  
  155. }
  156.  
  157. window.编辑框插内容.在选择处覆盖替换 = (el, 回调, 选择范围="select")=> {
  158. const sel = getSelection(el), result = 回调(sel.text), pasteInfo = pasteText(el, result);
  159. updateSelectionAfterInsert(el, pasteInfo.index, result, 选择范围); //"select"改为选择范围 新增[数组参数]用于替换选择后的选中的自定义范围 1
  160. };
  161. window.编辑框插内容.在选择处前后追加 = (el, before, after, 选择范围="select")=> {
  162. if(typeof after=="undefined") after = before;
  163. const sel = getSelection(el), pasteInfo = pasteText(el, before + sel.text + after);
  164. updateSelectionAfterInsert(el, pasteInfo.index+before.length, sel.text, 选择范围); // "select"改为选择范围
  165. };
  166. };
  167. })(); //编辑框插入内容 每次使用得先执行初始化语句 [window.编辑框插内容.init();]