Greasy Fork is available in English.

Markdown toolbar for GreasyFork

Select Markdown format by default, add help links, add toolbar formatting buttons for markdown

Verze ze dne 08. 03. 2021. Zobrazit nejnovější verzi.

  1. // ==UserScript==
  2. // @name Markdown toolbar for GreasyFork
  3. // @name:ru Markdown-тулбар для GreasyFork
  4. // @name:zh-CN GreasyFork markdown
  5. // @namespace darkred
  6. // @version 2.0.0
  7. // @description Select Markdown format by default, add help links, add toolbar formatting buttons for markdown
  8. // @description:ru Включает формат Markdown по умолчанию, добавляет справочные ссылки по форматам, добавляет панель кнопок форматирования markdown
  9. // @description:zh-CN 在论坛默认使用 Markdown 格式,添加格式帮助链接及 Markdown 工具栏
  10. // @author wOxxOm, darkred
  11. // @contributor JixunMoe
  12. // @license MIT
  13. // @icon https://raw.githubusercontent.com/dcurtis/markdown-mark/master/png/66x40-solid.png
  14. // @include https://greatest.deepsurf.us/*discussions/*
  15. // @include https://greatest.deepsurf.us/*scripts/*/versions/new*
  16. // @include https://greatest.deepsurf.us/*script_versions/new*
  17. // @include https://greatest.deepsurf.us/*/conversations/*
  18. // @include https://greatest.deepsurf.us/en/users/edit
  19. // @grant GM_addStyle
  20. // @run-at document-start
  21. // @supportURL https://github.com/darkred/Userscripts/issues
  22. // ==/UserScript==
  23.  
  24. /* jshint lastsemic:true, multistr:true, laxbreak:true, -W030, -W041, -W084 */
  25.  
  26.  
  27. // Previous inclure rules - kept for reference
  28. // @include https://forum.userstyles.org/discussion/*
  29. // @include https://forum.userstyles.org/post/discussion*
  30. // @include https://forum.userstyles.org/messages/*
  31. // @include https://forum.userstyles.org/messages/add*
  32. // @include https://forum.userstyles.org/*/editdiscussion/*
  33.  
  34.  
  35.  
  36. var inForum = location.href.indexOf('/discussions') > 0;
  37.  
  38. window.addEventListener('DOMContentLoaded', function(e) {
  39. if (inForum){
  40. addFeatures(document.querySelector('.label-note > input').parentNode.appendChild(document.createElement('br')));
  41. }
  42. else {
  43.  
  44. if (nn = document.querySelectorAll('input[value="markdown"]'))
  45. for (var n, i=0; (i<nn.length) && (n=nn[i]); i++) {
  46. if (location.href.indexOf('/script_versions/')) {
  47. n.click();
  48. }
  49. n.click(); // posting a new script
  50. addFeatures(n.parentNode.appendChild(document.createElement('br')));
  51. }
  52.  
  53. // addFeatures(document.querySelector('.form-control'));
  54. }
  55.  
  56. new MutationObserver(function(mutations) {
  57. for (var i=0, ml=mutations.length, m; (i<ml) && (m=mutations[i]); i++) {
  58. for (var j=0, nodes=m.addedNodes, nl=nodes.length, n; (j<nl) && (n=nodes[j]); j++) {
  59. if (n.nodeType == 1) // Node.ELEMENT_NODE 1 An Element node like <p> or <div>.
  60. if (inForum) {
  61. if ((n.localName == 'label' && n.querySelector('input[value="Markdown"], input[value="Html"], input[value="markdown"], input[value="html"]'))
  62. || (n = n.querySelector('input[value="Markdown"], input[value="markdown"]') || n.querySelector('input[value="Html"], input[value="html"]')))
  63. return addFeatures(n.closest('label'));
  64. } else {
  65. if (((n.localName == 'input') && (n.value.toLowerCase() == 'Markdown'))
  66. || (n = n.querySelector('input[value="Markdown"], input[value="markdown"]'))) {
  67. if (location.href.indexOf('/script_versions/'))
  68. n.click();
  69. return addFeatures(n.parentNode.appendChild(document.createElement('br')));
  70. }
  71. }
  72. }
  73. }
  74. }).observe(document, {subtree:true, childList:true});
  75. });
  76.  
  77.  
  78.  
  79.  
  80.  
  81. function addFeatures(n) {
  82.  
  83. if (!n){
  84. return;
  85. }
  86.  
  87. /*
  88. if (!inForum) {
  89. */
  90. var form = n.closest('form');
  91. // span.current > a.write-tab
  92. // var form = n.querySelector('form');
  93. if (form.action.indexOf('/edit') < 0) {
  94. n.click();
  95. }
  96.  
  97. n.parentNode.textAreaNode = form.querySelector('textarea.TextBox, textarea.previewable');
  98. // add formatting help tooltips (the '(?)' )
  99. n.previousElementSibling.insertAdjacentHTML('beforeend',
  100. ' (<a href="/help/allowed-markup" target="_blank" title="'+
  101. '* (name, title), a (href), abbr, b, blockquote (cite), br, center, cite, code, dd, del, dfn, div, dl, dt, em, '+
  102. 'h1, h2, h3, h4, h5, h6, hr, i, ins, img (alt, height, src (https), width), kbd, li, mark, ol, p, pre, q (cite), '+
  103. 'rp, rt, ruby, s, samp, small, span, strike, strong, tt, table, tbody, tfoot, thead, td, th, tr, sub, sup, '+
  104. 'time (datetime, pubdate), u, ul, var">?</a>)');
  105. n.insertAdjacentHTML('beforeend',
  106. ' (<a href="http://www.darkcoding.net/software/markdown-quick-reference/" target="_blank">?</a>)');
  107. // if (location.href.indexOf('/forum/messages/') > -1)
  108. if (location.href.indexOf('/conversations/') > -1)
  109. GM_addStyle('#ConversationForm label { display:inline-block; margin-right:2ex }\
  110. #ConversationForm .TextBox { margin-top:0 }');
  111.  
  112.  
  113. /*
  114.  
  115. } else { // if not in forum
  116. */
  117. for (var wrapper=n; wrapper = wrapper.parentNode; )
  118. // if (t = wrapper.querySelector('textarea[id*="additional-info"]')) {
  119. if (t = wrapper.querySelector('textarea[id*="additional-info"], textarea[id*="conversation_messages_attributes_0_content"], textarea[id*="discussion_comments_attributes_0_text"], textarea[id*="comment_text"], textarea[id*="user_profile"]')) {
  120. n.parentNode.textAreaNode = t;
  121. break;
  122. }
  123. GM_addStyle('\
  124. .Button {\
  125. display: inline-block;\
  126. cursor: pointer;\
  127. margin: 0px;\
  128. font-size: 12px;\
  129. line-height: 1;\
  130. font-weight: bold;\
  131. padding: 4px 6px;\
  132. background: -moz-linear-gradient(center bottom , #CCC 0%, #FAFAFA 100%) repeat scroll 0% 0% #F8F8F8;\
  133. border: 1px solid #999;\
  134. border-radius: 2px;\
  135. white-space: nowrap;\
  136. text-shadow: 0px 1px 0px #FFF;\
  137. box-shadow: 0px 1px 0px #FFF inset, 0px -1px 2px #BBB inset;\
  138. color: #333;}'
  139. );
  140.  
  141. // }
  142.  
  143.  
  144.  
  145.  
  146. // add buttons
  147. // console.log(n);
  148. // debugger
  149. btnMake(n, '<b>'+__('B')+'</b>', __('Bold'), '**');
  150. btnMake(n, '<i>'+__('I')+'</i>', __('Italic'), '*');
  151. btnMake(n, '<u>'+__('U')+'</u>', __('Underline'), '<u>','</u>');
  152. btnMake(n, '<s>'+__('S')+'</s>', __('Strikethrough'), '<s>','</s>');
  153. btnMake(n, '&lt;br&gt;', __('Force line break'), '<br>','', true);
  154. btnMake(n, '---', __('Horizontal line'), '\n\n---\n\n', '', true);
  155. btnMake(n, __('URL'), __('Add URL to selected text'),
  156. function(e) {
  157. try {edWrapInTag('[', ']('+prompt(__('URL')+':')+')', edInit(e.target))}
  158. catch(ex) {}
  159. });
  160. btnMake(n, __('Image (https)'), __('Convert selected https://url to inline image'), '!['+__('image')+'](', ')');
  161. if (inForum)
  162. btnMake(n, __('Table'), __('Insert table template'), __('\n| head1 | head2 |\n|-------|-------|\n| cell1 | cell2 |\n| cell3 | cell4 |\n'), '', true);
  163. btnMake(n, __('Code'), __('Apply CODE markdown to selected text'),
  164. function(e){
  165. var ed = edInit(e.target);
  166. if (ed.sel.indexOf('\n') < 0)
  167. edWrapInTag('`', '`', ed);
  168. else
  169. edWrapInTag(((ed.sel1==0) || (ed.text.charAt(ed.sel1-1) == '\n') ? '' : '\n') + '```' + (ed.sel.charAt(0) == '\n' ? '' : '\n'),
  170. (ed.sel.substr(-1) == '\n' ? '' : '\n') + '```' + (ed.text.substr(ed.sel2,1) == '\n' ? '' : '\n'),
  171. ed);
  172. });
  173.  
  174.  
  175.  
  176.  
  177. var previewTab = document.querySelector('span > a.preview-tab > span');
  178. if (previewTab){
  179. previewTab.onclick = function(){
  180. document.querySelectorAll('.Button').forEach(element => element.style.display = 'none');
  181. };
  182. }
  183.  
  184. var writeTab = document.querySelector('span > a.write-tab > span');
  185. if (writeTab){
  186. writeTab.onclick = function(){
  187. document.querySelectorAll('.Button').forEach(element => element.style.display = 'inline-block');
  188. };
  189. }
  190.  
  191.  
  192.  
  193.  
  194. }
  195.  
  196.  
  197.  
  198.  
  199. function btnMake(afterNode, label, title, tag1_or_cb, tag2, noWrap) {
  200. var a = document.createElement('a');
  201. a.className = 'Button';
  202. a.innerHTML = label;
  203. a.title = title;
  204. // if (inForum)
  205. // a.style.setProperty('float','right');
  206. a.addEventListener('click',
  207. typeof(tag1_or_cb) == 'function' ? tag1_or_cb : // if
  208. noWrap ? function(e){edInsertText(tag1_or_cb, edInit(e.target))} : // else if
  209. function(e){edWrapInTag(tag1_or_cb, tag2, edInit(e.target))} // else
  210. );
  211. var nparent = afterNode.parentNode;
  212. console.log(nparent)
  213. // a.textAreaNode = nparent.textAreaNode ;
  214. a.textAreaNode = nparent.parentNode.querySelector('textArea');
  215. // console.log(a.textAreaNode)
  216. // inForum ? nparent.insertBefore(a, nparent.firstElementChild) : nparent.appendChild(a);
  217. // nparent.insertBefore(a, nparent.firstElementChild)
  218. nparent.appendChild(a);
  219. }
  220.  
  221.  
  222.  
  223.  
  224. function edInit(btn) {
  225. var ed = {node: btn.textAreaNode || btn.parentNode.textAreaNode};
  226. ed.sel1 = ed.node.selectionStart;
  227. ed.sel2 = ed.node.selectionEnd,
  228. ed.text = ed.node.value;
  229. ed.sel = ed.text.substring(ed.sel1, ed.sel2);
  230. return ed;
  231. }
  232.  
  233. function edWrapInTag(tag1, tag2, ed) {
  234. ed.node.value = ed.text.substr(0, ed.sel1) + tag1 + ed.sel + (tag2?tag2:tag1) + ed.text.substr(ed.sel2);
  235. ed.node.setSelectionRange(ed.sel1 + tag1.length, ed.sel1 + tag1.length + ed.sel.length);
  236. ed.node.focus();
  237. }
  238.  
  239. function edInsertText(text, ed) {
  240. ed.node.value = ed.text.substr(0, ed.sel2) + text + ed.text.substr(ed.sel2);
  241. ed.node.setSelectionRange(ed.sel2 + text.length, ed.sel2 + text.length);
  242. ed.node.focus();
  243. }
  244.  
  245. var __ = (function (l, langs) {
  246. var lang = langs[l] || langs[l.replace(/-.+/, '')];
  247. return lang ? function (text) { return lang[text] || text; }
  248. : function (text) { return text }; // No matching language, fallback to english
  249. })(location.pathname.match(/^\/(.+?)\//)[1], {
  250. // Can be full name, or just the beginning part.
  251. 'zh-CN': {
  252. 'Bold': '粗体',
  253. 'Italic': '斜体',
  254. 'Underline': '下划线',
  255. 'Strikethrough': '删除线',
  256. 'Force line break': '强制换行',
  257. 'Horizontal line': '水平分割线',
  258. 'URL': '链接',
  259. 'Add URL to selected text': '为所选文字添加链接',
  260. 'Image (https)': '图片 (https)',
  261. 'Convert selected https://url to inline image': '将所选地址转换为行内图片',
  262. 'image': '图片描述', // Default image alt value
  263. 'Table': '表格',
  264. 'Insert table template': '插入表格模板',
  265. 'Code': '代码',
  266. 'Apply CODE markdown to selected text': '将选中代码围起来',
  267.  
  268. '\n| head1 | head2 |\n|-------|-------|\n| cell1 | cell2 |\n| cell3 | cell4 |\n':
  269. '\n| 表头 1 | 表头 2 |\n|-------|-------|\n| 表格 1 | 表格 2 |\n| 表格 3 | 表格 4 |\n'
  270. },
  271. 'ru': {
  272. 'B': 'Ж',
  273. 'I': 'К',
  274. 'U': 'Ч',
  275. 'S': 'П',
  276. 'Bold': 'Жирный',
  277. 'Italic': 'Курсив',
  278. 'Underline': 'Подчеркнутый',
  279. 'Strikethrough': 'Перечеркнутый',
  280. 'Force line break': 'Новая строка',
  281. 'Horizontal line': 'Горизонтальная линия',
  282. 'URL': 'ссылка',
  283. 'Add URL to selected text': 'Добавить ссылку к выделенному тексту',
  284. 'Image (https)': 'Картинка (https)',
  285. 'Convert selected https://url to inline image': 'Преобразовать выделенный https:// адрес в картинку',
  286. 'image': 'картинка', // Default image alt value
  287. 'Table': 'Таблица',
  288. 'Insert table template': 'Вставить шаблон таблицы',
  289. 'Code': 'Код',
  290. 'Apply CODE markdown to selected text': 'Пометить выделенный фрагмент как программный код',
  291.  
  292. '\n| head1 | head2 |\n|-------|-------|\n| cell1 | cell2 |\n| cell3 | cell4 |\n':
  293. '\n| заголовок1 | заголовок2 |\n|-------|-------|\n| ячейка1 | ячейка2 |\n| ячейка3 | ячейка4 |\n'
  294. },
  295. 'fr': {
  296. 'B': 'G',
  297. 'I': 'I',
  298. 'U': 'S',
  299. 'S': 'B',
  300. 'Bold': 'Gras',
  301. 'Italic': 'Italique',
  302. 'Underline': 'Souligné',
  303. 'Strikethrough': 'Barré',
  304. 'Force line break': 'Forcer le saut de ligne',
  305. 'Horizontal line': 'Ligne horizontale',
  306. 'URL': 'URL',
  307. 'Add URL to selected text': 'Ajouter URL au texte sélectionné',
  308. 'Image (https)': 'Image (https)',
  309. 'Convert selected https://url to inline image': 'Convertir https://url sélectionnés en images',
  310. 'image': 'image', // Default image alt value
  311. 'Table': 'Tableau',
  312. 'Insert table template': 'Insérer un modèle de table',
  313. 'Code': 'Code',
  314. 'Apply CODE markdown to selected text': 'Appliquer CODE markdown au texte sélectionné',
  315.  
  316. '\n| head1 | head2 |\n|-------|-------|\n| cell1 | cell2 |\n| cell3 | cell4 |\n':
  317. '\n| En-tête 1 | En-tête 2 |\n|-------|-------|\n| cellule 1 | cellule 2 |\n| cellule 3 | cellule 4 |\n'
  318. }
  319. });