Mercurial Hosting > freedit
annotate src/bbcode/bbcode.js @ 61:389e5d8e5f8a default tip
minor
author | Franklin Schmidt <fschmidt@gmail.com> |
---|---|
date | Tue, 06 Dec 2022 13:37:25 -0700 |
parents | 8b5b1bce7d6b |
children |
rev | line source |
---|---|
45 | 1 // https://fonts.google.com/icons |
2 | |
51 | 3 function ajax(url,postData,context) { |
4 let request = new XMLHttpRequest(); | |
5 let method = postData ? 'POST' : 'GET'; | |
6 request.open( method, url ); | |
7 if( postData ) | |
8 request.setRequestHeader( 'Content-Type', 'application/x-www-form-urlencoded' ); | |
9 request.onload = function() { | |
10 if( request.status !== 200 ) { | |
11 window.console && console.log( 'ajax failed: ' + request.status ); | |
12 if( request.responseText ) | |
13 document.write('<pre>'+request.responseText+'</pre>'); | |
14 return; | |
15 } | |
16 eval( request.responseText ); | |
17 }; | |
18 request.send(postData); | |
19 } | |
20 | |
48 | 21 function upload(input,callback) { |
44 | 22 let file = input.files[0]; |
23 input.value = null; | |
24 let request = new XMLHttpRequest(); | |
25 let url = 'https://upload.uploadcare.com/base/'; | |
26 request.open( 'POST', url ); | |
27 request.onload = function() { | |
28 if( request.status !== 200 ) { | |
29 let err = 'ajax failed: ' + request.status; | |
30 if( request.responseText ) { | |
31 err += '\n' + request.responseText; | |
32 document.write('<pre>'+request.responseText+'</pre>'); | |
33 } | |
34 console.log(err); | |
35 ajax( '/error_log.js', 'err='+encodeURIComponent(err) ); | |
36 return; | |
37 } | |
38 let response = JSON.parse(request.responseText); | |
39 let filename = file.name; | |
40 let url = 'https://ucarecdn.com/' + response.file + '/' + filename; | |
41 callback(input,url,filename); | |
42 }; | |
43 let formData = new FormData(); | |
44 formData.append( 'UPLOADCARE_PUB_KEY', 'fe3d30f3088a50941d45' ); | |
45 formData.append( 'file', file ); | |
46 request.send(formData); | |
47 } | |
48 | |
49 function bbcodeCreate(div,options) { | |
46 | 50 if( typeof(div) === 'string' ) { |
44 | 51 div = document.querySelector(div); |
46 | 52 if(!div) throw 'selector not found'; |
53 } | |
54 options = options || {}; | |
60 | 55 options.buttons = options.buttons || {}; |
44 | 56 let content = options.content || ''; |
57 let html = `\ | |
50 | 58 <div bbcode_editor> |
60 | 59 <div buttons=top> |
48 | 60 <button type=button bold title="Bold"><img src="/bbcode/icons/format_bold.svg"></button> |
61 <button type=button italic title="Italic"><img src="/bbcode/icons/format_italic.svg"></button> | |
62 <button type=button underline title="Underline"><img src="/bbcode/icons/format_underlined.svg"></button> | |
63 <button type=button strikethrough title="Strikethrough"><img src="/bbcode/icons/format_strikethrough.svg"></button> | |
64 <button type=button sub title="Subscript"><img src="/bbcode/icons/subscript.svg"></button> | |
65 <button type=button sup title="Superscript"><img src="/bbcode/icons/superscript.svg"></button> | |
66 <button type=button ul title="Bullet list"><img src="/bbcode/icons/format_list_bulleted.svg"></button> | |
67 <button type=button ol title="Numbered list"><img src="/bbcode/icons/format_list_numbered.svg"></button> | |
49 | 68 <button type=button code_block title="Code block"><img src="/bbcode/icons/code_blocks.svg"></button> |
69 <button type=button code_inline title="Inline code"><img src="/bbcode/icons/code.svg"></button> | |
60 | 70 <button type=button convert_urls><input type=checkbox name=convert_urls checked> Convert URLs</button> |
71 </div> | |
72 <textarea name=bbcode>${content}</textarea> | |
73 <div preview from_bbcode></div> | |
74 <div buttons=bottom> | |
75 <button type=button more><input type=checkbox> BBCode menu</button> | |
48 | 76 <input type=file> |
60 | 77 <button type=button upload>Upload file</button> |
78 <button type=button preview><input type=checkbox> Preview</button> | |
44 | 79 ` ; |
60 | 80 for( let key of Object.keys(options.buttons) ) { |
44 | 81 html += `\ |
60 | 82 <button type=button key="${key}">${key}</button> |
44 | 83 ` ; |
84 } | |
85 html += `\ | |
45 | 86 </div> |
44 | 87 </div> |
88 ` ; | |
89 div.innerHTML = html; | |
48 | 90 |
51 | 91 let enabledInPreview = []; |
52 | 92 let convertUrls = true; |
51 | 93 |
60 | 94 for( let entry of Object.entries(options.buttons) ) { |
95 console.log(entry); | |
96 let button = div.querySelector('button[key="'+entry[0]+'"]'); | |
97 let fn = entry[1]; | |
52 | 98 button.onclick = function(event) { |
99 event.convertUrls = convertUrls; | |
60 | 100 fn(event); |
52 | 101 }; |
51 | 102 enabledInPreview.push(button); |
103 } | |
48 | 104 |
105 let textarea = div.querySelector('textarea'); | |
106 function fixTextarea() { | |
107 let height = textarea.scrollHeight; | |
108 if( height > textarea.clientHeight ) { | |
109 textarea.style.height = (height+2) + "px"; | |
110 } | |
111 }; | |
112 textarea.oninput = fixTextarea; | |
113 fixTextarea(); | |
114 | |
60 | 115 function checkboxButtonClick(event) { |
116 event.target.querySelector('input').click(); | |
117 }; | |
52 | 118 |
60 | 119 let topButtons = div.querySelector('[buttons="top"]'); |
120 topButtons.style.display = 'none'; | |
48 | 121 let moreButton = div.querySelector('button[more]'); |
60 | 122 moreButton.onclick = checkboxButtonClick; |
123 let moreCheckbox = moreButton.querySelector('input'); | |
124 moreCheckbox.onclick = function() { | |
125 if( moreCheckbox.checked ) { | |
126 topButtons.style.display = 'flex'; | |
52 | 127 } else { |
60 | 128 topButtons.style.display = 'none'; |
48 | 129 } |
60 | 130 event.stopPropagation(); |
48 | 131 }; |
51 | 132 |
133 let divPreview = div.querySelector('div[preview]'); | |
52 | 134 function contextPreview(bbcode,html) { |
135 textarea.value = bbcode; | |
51 | 136 divPreview.innerHTML = html; |
137 } | |
138 let previewButton = div.querySelector('button[preview]'); | |
60 | 139 previewButton.onclick = checkboxButtonClick; |
140 let previewCheckbox = previewButton.querySelector('input'); | |
141 previewCheckbox.onclick = function() { | |
52 | 142 let buttons = previewButton.parentNode.querySelectorAll('button'); |
60 | 143 if( previewCheckbox.checked ) { |
52 | 144 for( let b of buttons ) { |
145 if( !enabledInPreview.includes(b) ) | |
146 b.disabled = true; | |
147 } | |
148 textarea.style.display = 'none'; | |
149 divPreview.style.display = 'block'; | |
60 | 150 topButtons.style.display = 'none'; |
53
cac477dd1f82
convert image and video urls
Franklin Schmidt <fschmidt@gmail.com>
parents:
52
diff
changeset
|
151 let postData = 'text=' + encodeURIComponent(textarea.value) + '&convert_urls=' + convertUrls; |
cac477dd1f82
convert image and video urls
Franklin Schmidt <fschmidt@gmail.com>
parents:
52
diff
changeset
|
152 ajax( '/bbcode/preview.js', postData, {preview: contextPreview} ); |
52 | 153 } else { |
51 | 154 for( let b of buttons ) { |
155 b.disabled = false; | |
156 } | |
157 textarea.style.display = 'initial'; | |
158 divPreview.style.display = 'none'; | |
60 | 159 if( moreCheckbox.checked ) |
160 topButtons.style.display = 'flex'; | |
51 | 161 textarea.focus(); |
162 } | |
60 | 163 event.stopPropagation(); |
51 | 164 } |
165 enabledInPreview.push(previewButton); | |
48 | 166 |
52 | 167 let convertUrlsButton = div.querySelector('button[convert_urls]'); |
60 | 168 convertUrlsButton.onclick = checkboxButtonClick; |
169 let convertUrlsCheckbox = convertUrlsButton.querySelector('input'); | |
170 convertUrlsCheckbox.onclick = function(event) { | |
171 convertUrls = convertUrlsCheckbox.checked; | |
172 event.stopPropagation(); | |
173 }; | |
52 | 174 |
48 | 175 function add(tag,openTag,closeTag) { |
176 let button = div.querySelector('button['+tag+']'); | |
177 button.onclick = function() { | |
178 let start = textarea.selectionStart; | |
179 let end = textarea.selectionEnd; | |
180 textarea.setRangeText(closeTag,end,end); | |
181 textarea.setRangeText(openTag,start,start); | |
182 let len = openTag.length; | |
183 textarea.setSelectionRange(start+len,end+len); | |
184 fixTextarea(); | |
185 textarea.focus(); | |
186 }; | |
187 } | |
188 add('bold','[b]','[/b]'); | |
189 add('italic','[i]','[/i]'); | |
190 add('underline','[u]','[/u]'); | |
191 add('strikethrough','[s]','[/s]'); | |
192 add('sub','[sub]','[/sub]'); | |
193 add('sup','[sup]','[/sup]'); | |
194 add('ul','[list]\n[item]','[/item]\n[/list]'); | |
195 add('ol','[list=1]\n[item]','[/item]\n[/list]'); | |
49 | 196 add('code_block','[code]','[/code]'); |
197 add('code_inline','[code=inline]','[/code]'); | |
48 | 198 |
199 let fileInput = div.querySelector('input[type="file"]'); | |
200 div.querySelector('button[upload]').onclick = function(){ fileInput.click(); }; | |
201 function uploaded(input,url,filename) { | |
202 textarea.setRangeText(url,textarea.selectionStart,textarea.selectionEnd,'select'); | |
203 textarea.focus(); | |
204 } | |
205 fileInput.onchange = function() { upload(fileInput,uploaded); }; | |
44 | 206 } |