Mercurial Hosting > freedit
annotate src/bbcode/bbcode.js @ 56:7ce54f6d93f2
add change name
author | Franklin Schmidt <fschmidt@gmail.com> |
---|---|
date | Mon, 28 Nov 2022 22:00:43 -0700 |
parents | cac477dd1f82 |
children | 8b5b1bce7d6b |
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 || {}; | |
44 | 55 let content = options.content || ''; |
56 let save = options.save; | |
57 let cancel = options.cancel; | |
58 let html = `\ | |
50 | 59 <div bbcode_editor> |
48 | 60 <textarea name=bbcode>${content}</textarea> |
51 | 61 <div preview from_bbcode></div> |
45 | 62 <div buttons> |
48 | 63 <button type=button bold title="Bold"><img src="/bbcode/icons/format_bold.svg"></button> |
64 <button type=button italic title="Italic"><img src="/bbcode/icons/format_italic.svg"></button> | |
65 <button type=button underline title="Underline"><img src="/bbcode/icons/format_underlined.svg"></button> | |
66 <button type=button strikethrough title="Strikethrough"><img src="/bbcode/icons/format_strikethrough.svg"></button> | |
67 <button type=button sub title="Subscript"><img src="/bbcode/icons/subscript.svg"></button> | |
68 <button type=button sup title="Superscript"><img src="/bbcode/icons/superscript.svg"></button> | |
69 <button type=button ul title="Bullet list"><img src="/bbcode/icons/format_list_bulleted.svg"></button> | |
70 <button type=button ol title="Numbered list"><img src="/bbcode/icons/format_list_numbered.svg"></button> | |
49 | 71 <button type=button code_block title="Code block"><img src="/bbcode/icons/code_blocks.svg"></button> |
72 <button type=button code_inline title="Inline code"><img src="/bbcode/icons/code.svg"></button> | |
52 | 73 <button type=button convert_urls checked title="Convert URLs"><img src="/bbcode/icons/media_link.svg"></button> |
74 <input type=hidden name=convert_urls value=true> | |
48 | 75 <button type=button more checked title="More..."><img src="/bbcode/icons/more_horiz.svg"></button> |
76 <input type=file> | |
77 <button type=button upload title="Upload File"><img src="/bbcode/icons/file_upload.svg"></button> | |
51 | 78 <button type=button preview title="Preview"><img src="/bbcode/icons/preview.svg"></button> |
44 | 79 ` ; |
80 if(save) { | |
81 html += `\ | |
45 | 82 <button type=button save title="Send message"><img src="/bbcode/icons/send.svg"></button> |
44 | 83 ` ; |
84 } | |
85 if(cancel) { | |
86 html += `\ | |
45 | 87 <button type=button cancel title="Cancel"><img src="/bbcode/icons/cancel.svg"></button> |
44 | 88 ` ; |
89 } | |
90 html += `\ | |
45 | 91 </div> |
44 | 92 </div> |
93 ` ; | |
94 div.innerHTML = html; | |
48 | 95 |
51 | 96 let enabledInPreview = []; |
52 | 97 let convertUrls = true; |
51 | 98 |
99 if(save) { | |
100 let button = div.querySelector('button[save]'); | |
52 | 101 button.onclick = function(event) { |
102 event.convertUrls = convertUrls; | |
103 save(event); | |
104 }; | |
51 | 105 enabledInPreview.push(button); |
106 } | |
107 if(cancel) { | |
108 let button = div.querySelector('button[cancel]'); | |
109 button.onclick = cancel; | |
110 enabledInPreview.push(button); | |
111 } | |
48 | 112 |
113 let textarea = div.querySelector('textarea'); | |
114 function fixTextarea() { | |
115 let height = textarea.scrollHeight; | |
116 if( height > textarea.clientHeight ) { | |
117 textarea.style.height = (height+2) + "px"; | |
118 } | |
119 }; | |
120 textarea.oninput = fixTextarea; | |
121 fixTextarea(); | |
122 | |
52 | 123 function toggle(button) { |
124 let checked = button.getAttribute('checked') !== null; | |
125 checked = !checked; // toggle | |
126 if( checked ) { | |
127 button.setAttribute('checked',''); | |
128 } else { | |
129 button.removeAttribute('checked'); | |
130 } | |
131 return checked; | |
132 } | |
133 | |
48 | 134 let moreButton = div.querySelector('button[more]'); |
135 function more() { | |
52 | 136 let checked = toggle(moreButton); |
137 let buttons = moreButton.parentNode.querySelectorAll('button'); | |
48 | 138 if( checked ) { |
139 for( let b of buttons ) { | |
52 | 140 if( b === moreButton ) |
141 break; | |
142 b.removeAttribute('hidden'); | |
143 } | |
144 } else { | |
145 for( let b of buttons ) { | |
146 if( b === moreButton ) | |
48 | 147 break; |
148 b.setAttribute('hidden',''); | |
149 } | |
150 } | |
151 }; | |
152 moreButton.onclick = more; | |
153 more(); | |
51 | 154 enabledInPreview.push(moreButton); |
155 | |
156 let divPreview = div.querySelector('div[preview]'); | |
52 | 157 function contextPreview(bbcode,html) { |
158 textarea.value = bbcode; | |
51 | 159 divPreview.innerHTML = html; |
160 } | |
161 let previewButton = div.querySelector('button[preview]'); | |
162 function preview() { | |
52 | 163 let checked = toggle(previewButton); |
164 let buttons = previewButton.parentNode.querySelectorAll('button'); | |
51 | 165 if( checked ) { |
52 | 166 for( let b of buttons ) { |
167 if( !enabledInPreview.includes(b) ) | |
168 b.disabled = true; | |
169 } | |
170 textarea.style.display = 'none'; | |
171 divPreview.style.display = 'block'; | |
53
cac477dd1f82
convert image and video urls
Franklin Schmidt <fschmidt@gmail.com>
parents:
52
diff
changeset
|
172 let postData = 'text=' + encodeURIComponent(textarea.value) + '&convert_urls=' + convertUrls; |
cac477dd1f82
convert image and video urls
Franklin Schmidt <fschmidt@gmail.com>
parents:
52
diff
changeset
|
173 ajax( '/bbcode/preview.js', postData, {preview: contextPreview} ); |
52 | 174 } else { |
51 | 175 for( let b of buttons ) { |
176 b.disabled = false; | |
177 } | |
178 textarea.style.display = 'initial'; | |
179 divPreview.style.display = 'none'; | |
180 textarea.focus(); | |
181 } | |
182 } | |
183 previewButton.onclick = preview; | |
184 enabledInPreview.push(previewButton); | |
48 | 185 |
52 | 186 let convertUrlsButton = div.querySelector('button[convert_urls]'); |
187 let convertUrlsInput = div.querySelector('input[name="convert_urls"]'); | |
188 function toggleConvertUrls() { | |
189 convertUrls = toggle(convertUrlsButton); | |
190 convertUrlsInput.value = convertUrls; | |
191 } | |
192 convertUrlsButton.onclick = toggleConvertUrls; | |
193 | |
48 | 194 function add(tag,openTag,closeTag) { |
195 let button = div.querySelector('button['+tag+']'); | |
196 button.onclick = function() { | |
197 let start = textarea.selectionStart; | |
198 let end = textarea.selectionEnd; | |
199 textarea.setRangeText(closeTag,end,end); | |
200 textarea.setRangeText(openTag,start,start); | |
201 let len = openTag.length; | |
202 textarea.setSelectionRange(start+len,end+len); | |
203 fixTextarea(); | |
204 textarea.focus(); | |
205 }; | |
206 } | |
207 add('bold','[b]','[/b]'); | |
208 add('italic','[i]','[/i]'); | |
209 add('underline','[u]','[/u]'); | |
210 add('strikethrough','[s]','[/s]'); | |
211 add('sub','[sub]','[/sub]'); | |
212 add('sup','[sup]','[/sup]'); | |
213 add('ul','[list]\n[item]','[/item]\n[/list]'); | |
214 add('ol','[list=1]\n[item]','[/item]\n[/list]'); | |
49 | 215 add('code_block','[code]','[/code]'); |
216 add('code_inline','[code=inline]','[/code]'); | |
48 | 217 |
218 let fileInput = div.querySelector('input[type="file"]'); | |
219 div.querySelector('button[upload]').onclick = function(){ fileInput.click(); }; | |
220 function uploaded(input,url,filename) { | |
221 textarea.setRangeText(url,textarea.selectionStart,textarea.selectionEnd,'select'); | |
222 textarea.focus(); | |
223 } | |
224 fileInput.onchange = function() { upload(fileInput,uploaded); }; | |
44 | 225 } |