|
2 | 2 | layout: home
|
3 | 3 | ---
|
4 | 4 |
|
5 |
| -<script src="{{ 'pako/dist/pako.js' | node_module_url | relative_url }}"></script> |
6 | 5 | <script src="{{ 'js-base64/base64.js' | node_module_url | relative_url }}"></script>
|
| 6 | +<script src="{{ 'pako/dist/pako.min.js' | node_module_url | relative_url }}"></script> |
| 7 | +<script src="{{ 'showdown/dist/showdown.min.js' | node_module_url | relative_url }}"></script> |
| 8 | + |
| 9 | +{%- assign markdown_preview_id = 'markdown-preview' -%} |
| 10 | +{%- capture toggle_markdown_preview_id -%}toggle-{{ markdown_preview_id }}{%- endcapture -%} |
7 | 11 |
|
8 | 12 | {%- assign textarea_id = 'textarea' -%}
|
9 | 13 | {%- capture textarea_placeholder -%}
|
|
25 | 29 |
|
26 | 30 | {%- endcapture -%}
|
27 | 31 |
|
28 |
| -<textarea id="{{ textarea_id }}" placeholder="{{ textarea_placeholder }}" autofocus></textarea> |
| 32 | +<div class="toolbar"> |
| 33 | + <button id="{{ toggle_markdown_preview_id }}">Toggle Markdown preview</button> |
| 34 | +</div> |
| 35 | + |
| 36 | +<div class="content"> |
| 37 | + <textarea id="{{ textarea_id }}" placeholder="{{ textarea_placeholder }}" autofocus></textarea> |
| 38 | + <div id="{{ markdown_preview_id }}" class="markdown-preview"></div> |
| 39 | +</div> |
29 | 40 |
|
30 | 41 | <script>
|
31 | 42 | function main() {
|
|
37 | 48 | }
|
38 | 49 |
|
39 | 50 | function run() {
|
| 51 | + const toggleMarkdownPreview = document.getElementById('{{ toggle_markdown_preview_id }}'); |
40 | 52 | const textarea = document.getElementById('{{ textarea_id }}');
|
41 |
| - loadValue(textarea); |
| 53 | + const markdownPreview = document.getElementById('{{ markdown_preview_id }}'); |
| 54 | + |
| 55 | + loadMarkdownPreviewVisibility(markdownPreview); |
| 56 | + addClickListener(toggleMarkdownPreview); |
| 57 | + |
| 58 | + loadValue(textarea, markdownPreview); |
42 | 59 | addValueListener(textarea);
|
43 | 60 | addFocusListener(textarea);
|
44 |
| - addHashListener(textarea); |
| 61 | + addHashListener(textarea, markdownPreview); |
| 62 | + } |
| 63 | + |
| 64 | + function addClickListener(toggleMarkdownPreview) { |
| 65 | + toggleMarkdownPreview.addEventListener('click', () => { |
| 66 | + const searchParams = new URLSearchParams(window.location.search); |
| 67 | + if (isMarkdownPreviewEnabled()) { |
| 68 | + searchParams.delete('{{ markdown_preview_id }}'); |
| 69 | + } else { |
| 70 | + searchParams.set('{{ markdown_preview_id }}', 'true'); |
| 71 | + } |
| 72 | + document.location.search = searchParams.toString(); |
| 73 | + }, false); |
| 74 | + } |
| 75 | + |
| 76 | + function loadMarkdownPreviewVisibility(markdownPreview) { |
| 77 | + if (isMarkdownPreviewEnabled()) { |
| 78 | + markdownPreview.style.display = 'block'; |
| 79 | + } else { |
| 80 | + markdownPreview.style.display = 'none'; |
| 81 | + } |
| 82 | + } |
| 83 | + |
| 84 | + function isMarkdownPreviewEnabled() { |
| 85 | + const searchParams = new URLSearchParams(window.location.search); |
| 86 | + return searchParams.has('{{ markdown_preview_id }}'); |
45 | 87 | }
|
46 | 88 |
|
47 |
| - function loadValue(textarea) { |
| 89 | + function loadValue(textarea, markdownPreview) { |
48 | 90 | const value = retrieveValue();
|
49 | 91 | const oldValue = textarea.value;
|
50 | 92 | textarea.value = value;
|
51 |
| - updateTitle(value); |
| 93 | + updateMarkdownPreviewIfNeeded(value, markdownPreview); |
| 94 | + updateTitle(value, markdownPreview.textContent); |
52 | 95 | if (oldValue === '') {
|
53 | 96 | textarea.selectionStart = value.length;
|
54 | 97 | }
|
|
66 | 109 |
|
67 | 110 | function addValueListener(textarea) {
|
68 | 111 | textarea.addEventListener('input', debounce((event) => {
|
69 |
| - const value = event.target.value; |
70 |
| - onValueUpdate(value); |
| 112 | + storeValue(event.target.value); |
71 | 113 | }, 300), false);
|
72 | 114 | }
|
73 | 115 |
|
74 | 116 | function addFocusListener(textarea) {
|
75 | 117 | textarea.addEventListener('blur', () => {
|
76 |
| - const value = textarea.value; |
77 |
| - onValueUpdate(value); |
| 118 | + storeValue(textarea.value); |
78 | 119 | }, false);
|
79 | 120 |
|
80 | 121 | window.addEventListener('blur', () => {
|
81 |
| - const value = textarea.value; |
82 |
| - onValueUpdate(value); |
| 122 | + storeValue(textarea.value); |
83 | 123 | }, false);
|
84 | 124 | }
|
85 | 125 |
|
86 |
| - function addHashListener(textarea) { |
| 126 | + function addHashListener(textarea, markdownPreview) { |
87 | 127 | window.addEventListener('hashchange', () => {
|
88 |
| - loadValue(textarea); |
| 128 | + loadValue(textarea, markdownPreview); |
89 | 129 | });
|
90 | 130 | }
|
91 | 131 |
|
|
95 | 135 | return deserialize(hash.substring(1));
|
96 | 136 | }
|
97 | 137 |
|
98 |
| - function onValueUpdate(value) { |
99 |
| - storeValue(value); |
100 |
| - updateTitle(value); |
101 |
| - } |
102 |
| - |
103 | 138 | function storeValue(value) {
|
104 | 139 | window.location.hash = '#' + serialize(value);
|
105 | 140 | }
|
106 | 141 |
|
107 |
| - function updateTitle(value) { |
108 |
| - document.title = '{{ site.tagline }}'; |
| 142 | + function updateTitle(value, markdownValue) { |
| 143 | + const finalValue = markdownValue || value; |
| 144 | + |
| 145 | + const titleParts = []; |
| 146 | + const customParts = []; |
| 147 | + |
| 148 | + const rawTitle = finalValue.split('\n', 1)[0]; |
| 149 | + if (rawTitle !== '') { |
| 150 | + const truncatedTitle = truncate(rawTitle.trim(), 30); |
| 151 | + customParts.push(truncatedTitle); |
| 152 | + } |
| 153 | + |
| 154 | + if (isMarkdownPreviewEnabled()) { |
| 155 | + customParts.push('Markdown'); |
| 156 | + } |
| 157 | + |
| 158 | + if (customParts.length > 0) { |
| 159 | + titleParts.push(customParts.join(' - ')); |
| 160 | + } |
| 161 | + titleParts.push('{{ site.tagline }}'); |
109 | 162 |
|
110 |
| - const rawTitle = value.split('\n', 1)[0]; |
111 |
| - if (rawTitle === '') { return; } |
112 |
| - const truncatedTitle = truncate(rawTitle.trim(), 30); |
113 |
| - document.title = truncatedTitle + ' | ' + document.title; |
| 163 | + document.title = titleParts.join(' | '); |
| 164 | + } |
| 165 | + |
| 166 | + function updateMarkdownPreviewIfNeeded(value, markdownPreview) { |
| 167 | + if (!isMarkdownPreviewEnabled()) { return; } |
| 168 | + |
| 169 | + const converter = new showdown.Converter({ |
| 170 | + omitExtraWLInCodeBlocks: true, |
| 171 | + noHeaderId: true, |
| 172 | + parseImgDimensions: true, |
| 173 | + simplifiedAutoLink: true, |
| 174 | + literalMidWordUnderscores: true, |
| 175 | + strikethrough: true, |
| 176 | + tables: true, |
| 177 | + tasklists: true, |
| 178 | + smoothLivePreview: true, |
| 179 | + simpleLineBreaks: true, |
| 180 | + openLinksInNewWindow: true, |
| 181 | + emoji: true, |
| 182 | + splitAdjacentBlockquotes: true |
| 183 | + }); |
| 184 | + const html = converter.makeHtml(value); |
| 185 | + markdownPreview.innerHTML = html; |
114 | 186 | }
|
115 | 187 |
|
116 | 188 | function truncate(string, limit) {
|
|
0 commit comments