1/** 2 * general autocomplete support 3 * 4 * require jQuery, jquery.textcomplete 5 */ 6function contact_search(term, callback, backend_url, type, extra_channels, spinelement) { 7 if(spinelement) { 8 $(spinelement).show(); 9 } 10 11 var postdata = { 12 start:0, 13 count:100, 14 search:term, 15 type:type, 16 }; 17 18 if(typeof extra_channels !== 'undefined' && extra_channels) 19 postdata['extra_channels[]'] = extra_channels; 20 21 $.ajax({ 22 type:'POST', 23 url: backend_url, 24 data: postdata, 25 dataType: 'json', 26 success: function(data) { 27 var items = data.items.slice(0); 28 items.unshift({taggable:false, text: term, replace: term}); 29 callback(items); 30 $(spinelement).hide(); 31 }, 32 }).fail(function () {callback([]); }); // Callback must be invoked even if something went wrong. 33} 34 35function contact_format(item) { 36 // Show contact information if not explicitly told to show something else 37 if(typeof item.text === 'undefined') { 38 var desc = ((item.label) ? item.nick + ' ' + item.label : item.nick); 39 if(typeof desc === 'undefined') desc = ''; 40 if(desc) desc = ' ('+desc+')'; 41 return "<div class='dropdown-item dropdown-notification lh-sm text-truncate' title='{4}'><img class='menu-img-2' src='{1}' loading='lazy'><strong>{2}</strong><br><small class='text-body-secondary'>{4}</small></div>".format(item.taggable, item.photo, item.name, desc, typeof(item.link) !== 'undefined' ? item.link : desc.replace('(','').replace(')','')); 42 } 43 else 44 return ""; 45} 46 47function smiley_format(item) { 48 return "<div class='dropdown-item'><img class='emoji' src='" + item.filepath + "'> " + item.shortname.replaceAll(':', '') + "</div>"; 49} 50 51function bbco_format(item) { 52 return "<div class='dropdown-item'>" + item + "</div>"; 53} 54 55function tag_format(item) { 56 return "<div class='dropdown-item'>" + '#' + item.text + "</div>"; 57} 58 59function editor_replace(item) { 60 if(typeof item.replace !== 'undefined') { 61 return '$1$2' + item.replace; 62 } 63 64 // $2 ensures that prefix (@,@!) is preserved 65 66 return '$1$2{' + item.link + '} '; 67} 68 69function basic_replace(item) { 70 if(typeof item.replace !== 'undefined') 71 return '$1'+item.replace; 72 73 return '$1'+item.name+' '; 74} 75 76function trim_replace(item) { 77 if(typeof item.replace !== 'undefined') 78 return '$1'+item.replace; 79 80 return '$1'+item.name; 81} 82 83function getWord(text, caretPos) { 84 var index = text.indexOf(caretPos); 85 var postText = text.substring(caretPos, caretPos+13); 86 if (postText.indexOf('[/list]') > 0 || postText.indexOf('[/checklist]') > 0 || postText.indexOf('[/ul]') > 0 || postText.indexOf('[/ol]') > 0 || postText.indexOf('[/dl]') > 0) { 87 return postText; 88 } 89} 90 91function getCaretPosition(ctrl) { 92 var CaretPos = 0; // IE Support 93 if (document.selection) { 94 ctrl.focus(); 95 var Sel = document.selection.createRange(); 96 Sel.moveStart('character', -ctrl.value.length); 97 CaretPos = Sel.text.length; 98 } 99 // Firefox support 100 else if (ctrl.selectionStart || ctrl.selectionStart == '0') 101 CaretPos = ctrl.selectionStart; 102 return (CaretPos); 103} 104 105function setCaretPosition(ctrl, pos){ 106 if(ctrl.setSelectionRange) { 107 ctrl.focus(); 108 ctrl.setSelectionRange(pos,pos); 109 } 110 else if (ctrl.createTextRange) { 111 var range = ctrl.createTextRange(); 112 range.collapse(true); 113 range.moveEnd('character', pos); 114 range.moveStart('character', pos); 115 range.select(); 116 } 117} 118 119function listNewLineAutocomplete(id) { 120 var text = document.getElementById(id); 121 var caretPos = getCaretPosition(text) 122 var word = getWord(text.value, caretPos); 123 124 if (word != null) { 125 var textBefore = text.value.substring(0, caretPos); 126 var textAfter = text.value.substring(caretPos, text.length); 127 var textInsert = (word.indexOf('[/dl]') > 0) ? '\r\n[*=] ' : (word.indexOf('[/checklist]') > 0) ? '\r\n[] ' : '\r\n[*] '; 128 var caretPositionDiff = (word.indexOf('[/dl]') > 0) ? 3 : 1; 129 130 $('#' + id).val(textBefore + textInsert + textAfter); 131 setCaretPosition(text, caretPos + (textInsert.length - caretPositionDiff)); 132 return true; 133 } 134 else { 135 return false; 136 } 137} 138 139function string2bb(element) { 140 if(element == 'bold') return 'b'; 141 else if(element == 'italic') return 'i'; 142 else if(element == 'underline') return 'u'; 143 else if(element == 'overline') return 'o'; 144 else if(element == 'strike') return 's'; 145 else if(element == 'superscript') return 'sup'; 146 else if(element == 'subscript') return 'sub'; 147 else if(element == 'highlight') return 'hl'; 148 else return element; 149} 150
151/** 152 * jQuery plugin 'editor_autocomplete' 153 */ 154(function( $ ) { 155 $.fn.editor_autocomplete = function(backend_url, extra_channels) { 156 157 if(! this.length) 158 return; 159 160 if (typeof extra_channels === 'undefined') extra_channels = false; 161 162 // Autocomplete contacts 163 contacts = { 164 match: /(^|\s)(@\!)([^ \n]{3,})$/, 165 index: 3, 166 cache: true, 167 search: function(term, callback) { contact_search(term, callback, backend_url, 'm', extra_channels, spinelement=false); }, 168 replace: editor_replace, 169 template: contact_format 170 }; 171 172 channels = { 173 match: /(^(?=[^\!]{2})|\s)(@)([^ \n]{3,})$/, 174 index: 3, 175 cache: true, 176 search: function(term, callback) { contact_search(term, callback, backend_url, 'c', extra_channels, spinelement=false); }, 177 replace: editor_replace, 178 template: contact_format 179 }; 180 181 // Autocomplete hashtags 182 tags = { 183 match: /(^|\s)(\#)([^ \n]{2,})$/, 184 index: 3, 185 cache: true, 186 search: function(term, callback) { $.getJSON('/hashtags/' + '$f=&t=' + term).done(function(data) { callback($.map(data, function(entry) { return entry.text.toLowerCase().indexOf(term.toLowerCase()) === 0 ? entry : null; })); }); }, 187 replace: function(item) { return "$1$2" + item.text + ' '; }, 188 context: function(text) { return text.toLowerCase(); }, 189 template: tag_format 190 }; 191 192 smilies = { 193 match: /(^|\s)(:[a-z0-9_:]{2,})$/, 194 index: 2, 195 cache: true, 196 search: function(term, callback) { $.getJSON('/smilies/json').done(function(data) { callback($.map(data, function(entry) { return entry.shortname.indexOf(term.substr(1)) !== -1 ? entry : null; })); }); }, 197 replace: function(item) { return "$1" + item.shortname + ' '; }, 198 context: function(text) { return text.toLowerCase(); }, 199 template: smiley_format 200 }; 201 //this.attr('autocomplete','off'); 202 203 var Textarea = Textcomplete.editors.Textarea; 204 205 $(this).each(function() { 206 var editor = new Textarea(this); 207 var textcomplete = new Textcomplete(editor, { 208 dropdown: { 209 maxCount: 100 210 } 211 }); 212 // it seems important that contacts are before channels here. Otherwise we run into regex issues. 213 textcomplete.register([contacts,channels,smilies,tags]); 214 }); 215 }; 216})( jQuery ); 217 218/** 219 * jQuery plugin 'search_autocomplete' 220 */ 221(function( $ ) { 222 $.fn.search_autocomplete = function(backend_url) { 223 224 if(! this.length) 225 return; 226 227 // Autocomplete contacts 228 contacts = { 229 match: /(^@)([^\n]{2,})$/, 230 index: 2, 231 cache: true, 232 search: function(term, callback) { contact_search(term, callback, backend_url, 'x', [], spinelement='#nav-search-spinner'); }, 233 replace: basic_replace, 234 template: contact_format, 235 }; 236 237 // Autocomplete hashtags 238 tags = { 239 match: /(^\#)([^ \n]{2,})$/, 240 index: 2, 241 cache: true, 242 search: function(term, callback) { $.getJSON('/hashtags/' + '$f=&t=' + term).done(function(data) { callback($.map(data, function(entry) { return entry.text.toLowerCase().indexOf(term.toLowerCase()) === 0 ? entry : null; })); }); }, 243 replace: function(item) { return "$1" + item.text + ' '; }, 244 context: function(text) { return text.toLowerCase(); }, 245 template: tag_format 246 }; 247 248 //this.attr('autocomplete', 'off'); 249 250 var textcomplete; 251 var Textarea = Textcomplete.editors.Textarea; 252 253 $(this).each(function() { 254 var editor = new Textarea(this); 255 textcomplete = new Textcomplete(editor, { 256 dropdown: { 257 maxCount: 100 258 } 259 }); 260 textcomplete.register([contacts,tags]); 261 }); 262 263 textcomplete.on('selected', function() { this.editor.el.form.submit(); }); 264 265 }; 266})( jQuery ); 267 268(function( $ ) { 269 $.fn.contact_autocomplete = function(backend_url, typ, autosubmit, onselect) { 270 271 if(! this.length) 272 return; 273 274 if(typeof typ === 'undefined') typ = ''; 275 if(typeof autosubmit === 'undefined') autosubmit = false; 276 277 // Autocomplete contacts 278 contacts = { 279 match: /(^)([^\n]{2,})$/, 280 index: 2, 281 cache: true, 282 search: function(term, callback) { contact_search(term, callback, backend_url, typ,[], spinelement=false); }, 283 replace: basic_replace, 284 template: contact_format, 285 }; 286 287 //this.attr('autocomplete','off'); 288 289 var textcomplete; 290 var Textarea = Textcomplete.editors.Textarea; 291 292 $(this).each(function() { 293 var editor = new Textarea(this); 294 textcomplete = new Textcomplete(editor, { 295 dropdown: { 296 maxCount: 100 297 298 } 299 }); 300 textcomplete.register([contacts]); 301 }); 302 303 if(autosubmit) 304 textcomplete.on('selected', function() { this.editor.el.form.submit(); }); 305 306 if(typeof onselect !== 'undefined') 307 textcomplete.on('select', function() { var item = this.dropdown.getActiveItem(); onselect(item.searchResult.data);}); 308 309 }; 310})( jQuery ); 311 312 313(function( $ ) { 314 $.fn.name_autocomplete = function(backend_url, typ, autosubmit, onselect) { 315 316 if(! this.length) 317 return; 318 319 if(typeof typ === 'undefined') typ = ''; 320 if(typeof autosubmit === 'undefined') autosubmit = false;
321 322 // Autocomplete contacts 323 names = { 324 match: /(^)([^\n]{2,})$/, 325 index: 2, 326 cache: true, 327 search: function(term, callback) { contact_search(term, callback, backend_url, typ,[], spinelement=false); }, 328 replace: trim_replace, 329 template: contact_format, 330 }; 331 332 //this.attr('autocomplete','off'); 333 334 let textcomplete; 335 let Textarea = Textcomplete.editors.Textarea; 336 337 $(this).each(function() { 338 let editor = new Textarea(this); 339 textcomplete = new Textcomplete(editor, { 340 dropdown: { 341 maxCount: 100 342 } 343 }); 344 textcomplete.register([names]); 345 }); 346 347 348 if(autosubmit) 349 textcomplete.on('selected', function() { this.editor.el.form.submit(); }); 350 351 if(typeof onselect !== 'undefined') 352 textcomplete.on('select', function() { let item = this.dropdown.getActiveItem(); onselect(item.searchResult.data); }); 353 354 }; 355})( jQuery ); 356 357(function( $ ) { 358 $.fn.bbco_autocomplete = function(type) { 359 360 if(! this.length) 361 return; 362 363 if(type=='bbcode') { 364 var open_close_elements = ['bold', 'italic', 'underline', 'overline', 'strike', 'superscript', 'subscript', 'quote', 'code', 'open', 'spoiler', 'map', 'nobb', 'list', 'checklist', 'question', 'answer', 'ul', 'ol', 'dl', 'li', 'table', 'tr', 'th', 'td', 'center', 'color', 'font', 'size', 'zrl', 'zmg', 'rpost', 'qr', 'observer', 'observer.language','embed', 'mark', 'url', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6']; 365 var open_elements = ['observer.baseurl', 'observer.address', 'observer.photo', 'observer.name', 'observer.webname', 'observer.url', '*', 'hr' ]; 366 367 var elements = open_close_elements.concat(open_elements); 368 } 369 370 if(type=='comanche') { 371 var open_close_elements = ['region', 'layout', 'template', 'theme', 'widget', 'block', 'menu', 'var', 'css', 'js', 'authored', 'comment', 'webpage']; 372 var open_elements = []; 373 374 var elements = open_close_elements.concat(open_elements); 375 } 376 377 if(type=='comanche-block') { 378 var open_close_elements = ['menu', 'var']; 379 var open_elements = []; 380 381 var elements = open_close_elements.concat(open_elements); 382 } 383 384 bbco = { 385 match: /\[(\w*\**)$/, 386 search: function (term, callback) { 387 callback($.map(elements, function (element) { 388 return element.indexOf(term) === 0 ? element : null; 389 })); 390 }, 391 index: 1, 392 replace: function (element) { 393 element = string2bb(element); 394 if(open_elements.indexOf(element) < 0) { 395 if(element === 'list' || element === 'ol' || element === 'ul') { 396 return ['\[' + element + '\]' + '\n\[*\] ', '\n\[/' + element + '\]']; 397 } else if(element === 'checklist') { 398 return ['\[' + element + '\]' + '\n\[\] ', '\n\[/' + element + '\]']; 399 } else if (element === 'dl') { 400 return ['\[' + element + '\]' + '\n\[*=Item name\] ', '\n\[/' + element + '\]']; 401 } else if(element === 'table') { 402 return ['\[' + element + '\]' + '\n\[tr\]', '\[/tr\]\n\[/' + element + '\]']; 403 } else if(element === 'observer') { 404 return ['\[' + element + '=1\]', '\[/observer\]']; 405 } else if(element === 'observer.language') { 406 return ['\[' + element + '=en\]', '\[/observer\]']; 407 } 408 else { 409 return ['\[' + element + '\]', '\[/' + element + '\]']; 410 } 411 } 412 else { 413 return '\[' + element + '\] '; 414 } 415 }, 416 template: bbco_format 417 }; 418 419 //this.attr('autocomplete','off'); 420 421 var Textarea = Textcomplete.editors.Textarea; 422 423 $(this).each(function() { 424 var editor = new Textarea(this); 425 var textcomplete = new Textcomplete(editor); 426 textcomplete.register([bbco]); 427 }); 428 429 this.keypress(function(e){ 430 if (e.keyCode == 13) { 431 var x = listNewLineAutocomplete(this.id); 432 if(x) { 433 e.stopImmediatePropagation(); 434 e.preventDefault(); 435 } 436 } 437 }); 438 }; 439})( jQuery ); 440
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.