import {Settings, getSettings, createElementFromString} from '../utilities'; const markdownSnippets: MarkdownSnippet[] = [ { dropdown: false, index: -1, markdown: '[$]()', name: 'Link' }, { dropdown: false, index: -1, markdown: '```\n$\n```', name: 'Code' }, { dropdown: false, index: -1, markdown: '~~$~~', name: 'Strikethrough' }, { dropdown: false, index: -1, markdown: '
\nClick to expand spoiler.\n\n$\n
', name: 'Spoilerbox' }, { dropdown: true, index: -1, markdown: '**$**', name: 'Bold' }, { dropdown: true, index: -1, markdown: '\n\n---\n\n$', name: 'Horizontal Divider' }, { dropdown: true, index: -1, markdown: '`$`', name: 'Inline Code' }, { dropdown: true, index: -1, markdown: '*$*', name: 'Italic' }, { dropdown: true, index: -1, markdown: '1. $', name: 'Ordered List' }, { dropdown: true, index: -1, markdown: '$', name: 'Small' }, { dropdown: true, index: -1, markdown: '* $', name: 'Unordered List' } ]; (async (): Promise => { const settings: Settings = await getSettings(); if (!settings.features.markdownToolbar) { return; } calculateSnippetIndexes(); // Create an observer that will add toolbars whenever something changes. const observer: MutationObserver = new window.MutationObserver((): void => { observer.disconnect(); addToolbarToTextareas(); startObserver(); }); function startObserver(): void { observer.observe(document, { childList: true, subtree: true }); } // Run once when the page loads. addToolbarToTextareas(); startObserver(); })(); interface MarkdownSnippet { dropdown: boolean; index: number; markdown: string; name: string; } function addToolbarToTextareas(): void { // Grab all Markdown forms that don't have already have a toolbar (see below). const markdownForms: NodeListOf = document.querySelectorAll( '.form-markdown:not(.trx-toolbar)' ); if (markdownForms.length === 0) { return; } for (const form of markdownForms) { // Add `trx-toolbar` to indicate this Markdown form already has the toolbar. form.classList.add('trx-toolbar'); const tabMenu: HTMLMenuElement = form.querySelector( '.tab-markdown-mode' ) as HTMLMenuElement; const textarea: HTMLTextAreaElement = form.querySelector( 'textarea[name="markdown"]' ) as HTMLTextAreaElement; const markdownSelect: HTMLSelectElement = createElementFromString( '' ); for (const snippet of markdownSnippets) { // If the snippet should go in the dropdown, add the `` ); markdownSelect.insertAdjacentElement('beforeend', snippetOption); continue; } // Otherwise, add it the tab menu as a tab item. const tabItem: HTMLLIElement = createElementFromString( `
  • ` ); tabItem.addEventListener('click', (event: MouseEvent): void => insertSnippet(snippet, textarea, event) ); tabMenu.insertAdjacentElement('beforeend', tabItem); } // When the dropdown value changes, add the snippet. markdownSelect.addEventListener('change', (): void => { const snippet: MarkdownSnippet | undefined = markdownSnippets.find( val => val.name === markdownSelect.value ); if (typeof snippet === 'undefined') { return; } insertSnippet(snippet, textarea); // Reset the dropdown index so it always displays "More..." and so it's // possible to select the same snippet multiple times. markdownSelect.selectedIndex = 0; }); // Insert the dropdown after the tab menu. tabMenu.insertAdjacentElement('afterend', markdownSelect); } } function insertSnippet( snippet: MarkdownSnippet, textarea: HTMLTextAreaElement, event?: MouseEvent ): void { // If insertSnippet is called from a button it will pass through event. // So preventDefault() that when it's defined. if (typeof event !== 'undefined') { event.preventDefault(); } // Since you have to press a button or go into a dropdown to click on a // snippet, the textarea won't be focused anymore. So focus it again. textarea.focus(); const currentSelectionStart: number = textarea.selectionStart; const currentSelectionEnd: number = textarea.selectionEnd; let {markdown} = snippet; let snippetIndex: number = snippet.index; // If text has been selected, change the markdown so it includes what's // been selected. if (currentSelectionStart !== currentSelectionEnd) { markdown = snippet.markdown.slice(0, snippetIndex) + textarea.value.slice(currentSelectionStart, currentSelectionEnd) + snippet.markdown.slice(snippetIndex); // A special behavior for the Link snippet so it places the cursor in the // URL part of a Markdown link instead of the text part: "[](here)". if (snippet.name === 'Link') { snippetIndex += 2; } } textarea.value = textarea.value.slice(0, currentSelectionStart) + markdown + textarea.value.slice(currentSelectionEnd); textarea.selectionEnd = currentSelectionEnd + snippetIndex; } // This function gets called at the beginning of the script to set the snippet // indexes where the dollar sign is and to remove the dollar sign from it. // This could be manually done but I figure it's easier to write snippets and // have a placeholder for where the cursor is intended to go than to count // where the index is manually and figure it out yourself. function calculateSnippetIndexes(): void { for (const snippet of markdownSnippets) { const insertIndex: number = snippet.markdown.indexOf('$'); const newMarkdown: string = snippet.markdown.replace('$', ''); snippet.index = insertIndex; snippet.markdown = newMarkdown; } }