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 } |
