Textarea Typograf

Replaces hyphens, quotation marks, uncanonic smiles and "yo" in some russian words.

当前为 2020-11-15 提交的版本,查看 最新版本

  1. // ==UserScript==
  2. // @name Textarea Typograf
  3. // @namespace https://github.com/glebkema/tampermonkey-textarea-typograf
  4. // @description Replaces hyphens, quotation marks, uncanonic smiles and "yo" in some russian words.
  5. // @author glebkema
  6. // @copyright 2020, glebkema (https://github.com/glebkema)
  7. // @license MIT
  8. // @version 0.4.16
  9. // @match http://*/*
  10. // @match https://*/*
  11. // @grant none
  12. // @run-at context-menu
  13. // ==/UserScript==
  14.  
  15. // ==OpenUserJS==
  16. // @author glebkema
  17. // ==/OpenUserJS==
  18.  
  19. 'use strict';
  20.  
  21. const MODE_EXCEPTIONS = 'exceptions';
  22. const MODE_EXTRA_PREFIXES = 'extraPrefixes';
  23. const MODE_NO_CAPITAL_LETTER = 'noCapitalLetter';
  24. const MODE_NO_PREFIXES = 'noPrefixes';
  25. const MODE_NO_SUFFIXES = 'noSuffixes';
  26. const MODE_STANDARD = 'standard';
  27.  
  28. class Typograf {
  29. run(element) {
  30. if (element && 'textarea' === element.tagName.toLowerCase() && element.value) {
  31. const start = element.selectionStart;
  32. const end = element.selectionEnd;
  33. if (start === end) {
  34. element.value = this.improve(element.value);
  35. } else {
  36. const selected = element.value.substring(start, end);
  37. const theLength = element.value.length;
  38. element.value = element.value.substring(0, start)
  39. + this.improve(selected) + element.value.substring(end, theLength);
  40. }
  41. } else {
  42. // console.info('Start editing a non-empty textarea before calling the script');
  43. }
  44. }
  45.  
  46. improve(text) {
  47. if (text) {
  48. text = this.improveDash(text);
  49. text = this.improveQuotes(text);
  50. text = this.improveSmile(text);
  51. text = this.improveYo(text);
  52. }
  53. return text;
  54. }
  55.  
  56. improveDash(text) {
  57. text = text.replace(/ - /g, ' — ');
  58. return text;
  59. }
  60.  
  61. improveQuotes(text) {
  62. text = text.replace(/(?<=^|[(\s])"/g, '«');
  63. text = text.replace(/"(?=$|[.,;:!?)\s])/g, '»');
  64. return text;
  65. }
  66.  
  67. improveSmile(text) {
  68. text = text.replace(/([:;])[—oо]?([D)(|])/g, '$1-$2');
  69. return text;
  70. }
  71.  
  72. improveYo(text) {
  73. // verbs - list of the cores (with a capital letter and yo)
  74. text = this.improveYoVerb(text, MODE_EXCEPTIONS,
  75. 'Льё,Мнё,Рвё');
  76. text = this.improveYoVerb(text, MODE_EXTRA_PREFIXES,
  77. 'Вернё,Даё,Орё,Плывё,Поё,Стаё');
  78. text = this.improveYoVerb(text, MODE_NO_CAPITAL_LETTER,
  79. 'Йдё,Ймё');
  80. text = this.improveYoVerb(text, MODE_NO_PREFIXES,
  81. 'Идё,Начнё,Обернё,Придё,Улыбнё');
  82. text = this.improveYoVerb(text, MODE_NO_SUFFIXES,
  83. 'Шёл');
  84. text = this.improveYoVerb(text, MODE_STANDARD,
  85. 'Бьё,Врё,Вьё,Жмё,Жрё,Несё,Прё,Пьё,Ткнё,Трё,Чтё,Шлё,Шьё');
  86.  
  87. // verbs - fix the exceptions
  88. text = this.replaceException(text, 'Расстаёт', '(?![а-дж-я])');
  89. text = this.replaceException(text, 'Шлём');
  90.  
  91. // list of the words (with a capital letter and yo)
  92. text = this.improveYoWord(text, null,
  93. 'Её,Ещё,Моё,Неё,Своё,Твоё');
  94. text = this.improveYoWord(text, null,
  95. 'Вдвоём,Втроём,Объём,Остриём,Приём,Причём,Огнём,Своём,Твоём');
  96. text = this.improveYoWord(text, null,
  97. 'Василёк,Мотылёк,Огонёк,Пенёк,Ручеёк');
  98. text = this.improveYoWord(text, null,
  99. 'Затёк,Натёк,Потёк');
  100. text = this.improveYoWord(text, null,
  101. 'Грёза,Грёзы,Слёзы');
  102.  
  103. return text;
  104. }
  105.  
  106. improveYoVerb(text, mode, list) {
  107. return this.iterator(text, mode, list, this.replaceYoVerb.bind(this));
  108. }
  109.  
  110. improveYoWord(text, mode, list) {
  111. return this.iterator(text, mode, list, this.replaceYoWord.bind(this));
  112. }
  113.  
  114. iterator(text, mode, list, callback) {
  115. if ('string' === typeof list) {
  116. list = list.split(',');
  117. }
  118. for (let i = 0; i < list.length; i++) {
  119. const replace = list[i].trim();
  120. if (replace) {
  121. const find = this.removeAllYo(replace);
  122. text = callback(text, mode, find, replace);
  123. }
  124. }
  125. return text;
  126. }
  127.  
  128. removeAllYo(text) {
  129. return text.replace(/ё/g, 'е').replace(/Ё/g, 'Е');
  130. }
  131.  
  132. replaceException(text, exception, lookAhead = '') {
  133. const replace = this.removeAllYo(exception);
  134. let regex = new RegExp(exception + lookAhead, 'g');
  135. text = text.replace(regex, replace);
  136. regex = new RegExp('(?<![А-Яa-я])' + exception.toLowerCase() + lookAhead, 'g');
  137. text = text.replace(regex, replace.toLowerCase());
  138. return text;
  139. }
  140.  
  141. replaceYo(text, find, replace,
  142. lookBack = '(?<![б-джзй-нп-тф-я])', // +аеиоу
  143. lookAhead = '(?=[мтш])'
  144. ) {
  145. let regex;
  146. let findLowerCase = find.toLowerCase();
  147. // NB: \b doesn't work for russian words
  148. // 1) starts with a capital letter = just a begining of the word
  149. if (find !== findLowerCase) {
  150. regex = new RegExp(find + lookAhead, 'g');
  151. text = text.replace(regex, replace);
  152. }
  153. // 2) in lowercase = with a prefix ahead or without it
  154. regex = new RegExp(lookBack + findLowerCase + lookAhead, 'gi');
  155. text = text.replace(regex, replace.toLowerCase());
  156. return text;
  157. }
  158.  
  159. replaceYoVerb(text, mode, find, replace) {
  160. if (MODE_EXCEPTIONS === mode) {
  161. return this.replaceYo(text, find, replace,
  162. '(?<![б-джзй-нп-тф-я]|ко|фе)', // +аеиоу -"корвет" -"фельетон"
  163. '(?=[мтш])(?!мо)'); // -"мнемо"
  164. }
  165. if (MODE_EXTRA_PREFIXES === mode) {
  166. let lookBack = '(?<![гжк-нпрф-я])'; // +аеиоу +бвдзст
  167. if ('Даё' === replace) {
  168. lookBack = '(?<![гжк-нпрф-ъь-я])'; // +ы
  169. } else if ('Стаё' === replace) {
  170. lookBack = '(?<![гжк-нпрф-я]|ра)'; // -"вы/за/от/подрастает"
  171. }
  172. return this.replaceYo(text, find, replace, lookBack);
  173. }
  174. if (MODE_NO_CAPITAL_LETTER === mode) {
  175. return this.replaceYo(text, find.toLowerCase(), replace);
  176. }
  177. if (MODE_NO_PREFIXES === mode) {
  178. return this.replaceYo(text, find, replace,
  179. '(?<![А-Яа-яЁё])');
  180. }
  181. if (MODE_NO_SUFFIXES === mode) {
  182. return this.replaceYo(text, find, replace,
  183. '(?<![б-джзй-нп-тф-я])', // +аеиоу
  184. '(?![а-яё])');
  185. }
  186. // MODE_STANDARD
  187. return this.replaceYo(text, find, replace);
  188. }
  189.  
  190. replaceYoWord(text, mode, find, replace) {
  191. return this.replaceYo(text, find, replace,
  192. '(?<![А-Яа-яЁё])',
  193. '(?![а-яё])');
  194. }
  195. }
  196.  
  197. // if it's a browser, not a test
  198. if ('undefined' !== typeof document) {
  199. let typograf = new Typograf();
  200. typograf.run(document.activeElement);
  201. }
  202.  
  203. // if it's a test by Node.js
  204. if (module) {
  205. module.exports = {
  206. Typograf: Typograf,
  207. };
  208. } else {
  209. var module; // hack for Tampermonkey's eslint
  210. }