1 2/** 3 * The Tag File Selector Wizard 4 * 5 * This code is used in the toolbar entry of the doxycode plugin for quickly selecting available 6 * tag files from the tag file configuration for a code snippet. 7 * 8 * It opens a floating jQuery dialog that is inspired from the linkwiz floating dialog by Andreas Gohr and Pierre Spring 9 * (see {@link https://github.com/dokuwiki/dokuwiki/blob/master/lib/scripts/linkwiz.js}). 10 * The user can filter all available tag file entries with a search string and select all 11 * tag files to be used via checkboxes for each entry. 12 * 13 * On load the tagselector tries to find a doxygen syntax near the cursor in the editor and loads 14 * the already used tag file names into the dialog. Tag files can quickly be added by clicking on 15 * a tag file entry. 16 * 17 * 18 * @author Lukas Probsthain <lukas.probsthain@gmail.com> 19 */ 20var doxycode_tagselector = { 21 22 /** 23 * The main div that holds the tagselector for rendering 24 * @type {jQuery} 25 */ 26 $container: null, 27 28 /** 29 * The search text input for filtering the available tag files 30 * @type {jQuery} 31 */ 32 $search: null, 33 34 /** 35 * DIV for rendering the tag name list as a table 36 * @type {DOM/Element} 37 */ 38 result: null, 39 timer: null, 40 textArea: null, 41 selected: -1, // the element in the result that is currently marked with hotkeys 42 /** Selection of the doxycode syntax in the edit textArea @type {selection_class} */ 43 doxycodeSelected: null, // <doxycode ...> syntax that was near the cursor in the edit textArea 44 selection: null, // the old text selection in the edit textArea 45 tagNames: [], 46 47 /** 48 * Initialize the tag file selector by creating the needed HTML 49 * and attaching the eventhandlers 50 */ 51 init: function($editor){ 52 // position relative to the text area 53 var pos = $editor.position(); 54 55 if(doxycode_tagselector.$container) { 56 // if we already have a container ready do nothing here 57 return; 58 } 59 60 // create HTML Structure 61 doxycode_tagselector.$container = jQuery(document.createElement('div')) 62 .dialog({ 63 autoOpen: false, 64 draggable: true, 65 title: LANG.plugins.doxycode.tag_selector_title, 66 resizable: false, 67 buttons: [ 68 { 69 text: LANG.plugins.doxycode.tag_selector_btn_insert, 70 click: function() { 71 doxycode_tagselector.insertTagNames(); 72 doxycode_tagselector.hide(); 73 } 74 },{ 75 text: LANG.plugins.doxycode.tag_selector_btn_update, 76 click: function() { 77 doxycode_tagselector.updateTagNames(); 78 } 79 } 80 ] 81 }) 82 .html( 83 '<div>'+LANG.plugins.doxycode.tag_selector_search+' <input type="text" class="edit" id="doxycode__tagselector_search" autocomplete="off" /></div>'+ 84 '<div id="doxycode__tagselector_result"></div>' 85 ) 86 .parent() 87 .attr('id','doxycode__tagselector') 88 .css({ 89 'position': 'absolute', 90 'top': (pos.top+20)+'px', 91 'left': (pos.left+80)+'px' 92 }) 93 .hide() 94 .appendTo('.dokuwiki:first'); 95 96 doxycode_tagselector.textArea = $editor[0]; 97 doxycode_tagselector.result = jQuery('#doxycode__tagselector_result')[0]; 98 99 // scrollview correction on arrow up/down gets easier 100 jQuery(doxycode_tagselector.result).css('position', 'relative'); 101 102 doxycode_tagselector.$search = jQuery('#doxycode__tagselector_search'); 103 104 // attach event handlers 105 jQuery('#doxycode__tagselector .ui-dialog-titlebar-close').on('click', doxycode_tagselector.hide); 106 doxycode_tagselector.$search.keydown(doxycode_tagselector.onEntry); 107 jQuery(doxycode_tagselector.result).on('click', 'a', doxycode_tagselector.onResultClick); 108 }, 109 110 /** 111 * handle all keyup events in the search field 112 */ 113 onEntry: function(e){ 114 if(e.keyCode == 37 || e.keyCode == 39){ //left/right 115 return true; //ignore 116 } 117 if(e.keyCode == 27){ //Escape 118 doxycode_tagselector.hide(); 119 e.preventDefault(); 120 e.stopPropagation(); 121 return false; 122 } 123 if(e.keyCode == 38){ //Up 124 doxycode_tagselector.select(doxycode_tagselector.selected -1); 125 e.preventDefault(); 126 e.stopPropagation(); 127 return false; 128 } 129 if(e.keyCode == 40){ //Down 130 doxycode_tagselector.select(doxycode_tagselector.selected +1); 131 e.preventDefault(); 132 e.stopPropagation(); 133 return false; 134 } 135 if(e.keyCode == 32){ //Space 136 // Find the currently selected row based on your selection logic 137 var $selectedRow = doxycode_tagselector.$getResult(doxycode_tagselector.selected); 138 139 // Find the checkbox within that row 140 var $checkbox = $selectedRow.find('td:first-child input[type="checkbox"]'); 141 142 // Toggle the checkbox state 143 $checkbox.prop('checked', !$checkbox.prop('checked')); 144 145 e.preventDefault(); 146 e.stopPropagation(); 147 return false; 148 } 149 if(e.keyCode == 13){ //Enter 150 151 // trigger the insertion of the tagfilelist 152 doxycode_tagselector.insertTagNames(); 153 154 // close the tag selector 155 doxycode_tagselector.hide(); 156 157 e.preventDefault(); 158 e.stopPropagation(); 159 return false; 160 } 161 doxycode_tagselector.filterRows(); 162 }, 163 164 /** 165 * Get the selected tag name list from the result table. 166 * 167 * @returns {Array} List of selected tag names 168 */ 169 getTagList: function() { 170 // get the table body 171 var $tbody = jQuery('#doxycode__tagselector_table tbody'); 172 173 /** 174 * Array to hold the tag names 175 * @type {Array} 176 */ 177 var tagNames = []; 178 179 // Iterate over each row 180 $tbody.find('tr').each(function() { 181 var $row = jQuery(this); 182 var $checkbox = $row.find('td:first-child input[type="checkbox"]'); 183 184 // Check if the checkbox is checked 185 if ($checkbox.is(':checked')) { 186 // Get the text from the second column and add it to the tagNames array 187 var name = $row.find('td:nth-child(2)').text(); 188 tagNames.push(name); 189 } 190 }); 191 192 return tagNames; 193 }, 194 195 /** 196 * Get jQuery object of the entry in the result table by index 197 * 198 * @param {num} int index of the entry in the tbody 199 * @returns {jQuery} Row matching the index 200 */ 201 $getResult: function(num) { 202 return jQuery(doxycode_tagselector.$container).find('#doxycode__tagselector_table tbody tr:visible').eq(num); 203 }, 204 205 /** 206 * Select the given entry 207 * 208 * @param {num} int index of the entry in the tbody 209 */ 210 select: function(num){ 211 if(num < 0){ 212 doxycode_tagselector.deselect(); 213 return; 214 } 215 216 // get the current item 217 var $obj = doxycode_tagselector.$getResult(num); 218 if ($obj.length === 0) { 219 return; 220 } 221 222 // remove class from item 223 doxycode_tagselector.deselect(); 224 225 $obj.addClass('selected'); 226 227 // make sure the item is viewable in the scroll view 228 229 //getting child position within the parent 230 var childPos = $obj.position().top; 231 //getting difference between the childs top and parents viewable area 232 var yDiff = childPos + $obj.outerHeight() - jQuery(doxycode_tagselector.result).innerHeight(); 233 234 if (childPos < 0) { 235 //if childPos is above viewable area (that's why it goes negative) 236 jQuery(doxycode_tagselector.result)[0].scrollTop += childPos; 237 } else if(yDiff > 0) { 238 // if difference between childs top and parents viewable area is 239 // greater than the height of a childDiv 240 jQuery(doxycode_tagselector.result)[0].scrollTop += yDiff; 241 } 242 243 doxycode_tagselector.selected = num; 244 }, 245 246 /** 247 * Deselect the entry in the result table 248 */ 249 deselect: function(){ 250 if(doxycode_tagselector.selected > -1){ 251 doxycode_tagselector.$getResult(doxycode_tagselector.selected).removeClass('selected'); 252 } 253 doxycode_tagselector.selected = -1; 254 }, 255 256 /** 257 * Handle clicks in the result set an dispatch them to resultClick() 258 * 259 * @param {Event} e 260 */ 261 onResultClick: function(e){ 262 if(!jQuery(this).is('a')) { 263 return; 264 } 265 266 e.stopPropagation(); 267 e.preventDefault(); 268 269 doxycode_tagselector.resultClick(this); 270 return false; 271 }, 272 273 /** 274 * Handles the "click" on a given result anchor 275 * 276 * Enable the clicked tag name and insert new syntax into edit textArea 277 * 278 * @param {DOM/Element} a The link element this event was triggered for. 279 */ 280 resultClick: function(a){ 281 // enable the checkbox of this item 282 var $row = jQuery(a).closest('tr'); 283 var $checkbox = $row.find('td:first-child input[type="checkbox"]'); 284 $checkbox.prop('checked', true); 285 286 // trigger the insertion of the tagfilelist 287 doxycode_tagselector.insertTagNames(); 288 289 // close the tag selector 290 doxycode_tagselector.hide(); 291 }, 292 293 /** 294 * Start the timer for filtering the tag file list by the search string. 295 * 296 * If a timer was already running we restart the timer. 297 */ 298 filterRows: function() { 299 if(doxycode_tagselector.timer !== null){ 300 window.clearTimeout(doxycode_tagselector.timer); 301 doxycode_tagselector.timer = null; 302 } 303 304 doxycode_tagselector.timer = window.setTimeout(doxycode_tagselector.filterRowsExec,350); 305 }, 306 307 /** 308 * Filter the tag file list by the search string. 309 */ 310 filterRowsExec: function(){ 311 // Convert search text to lower case for case-insensitive comparison 312 var searchText = jQuery(doxycode_tagselector.$search).val().toLowerCase(); 313 314 var $tbody = jQuery(doxycode_tagselector.result).find('#doxycode__tagselector_table tbody'); 315 316 var $selectedRow = null; 317 318 // get currently selected row so we can update the selected index 319 if(doxycode_tagselector.selected >= 0) { 320 $selectedRow = doxycode_tagselector.$getResult(doxycode_tagselector.selected); 321 } 322 323 // get all rows 324 var $rows = $tbody.find('tr'); 325 326 // TODO: maybe also match the description? 327 328 // show all matching rows 329 $rows.filter(function() { 330 var name = jQuery(this).find('td').eq(1).text().toLowerCase(); 331 return searchText === '' || name.includes(searchText); 332 }).show(); 333 334 // hide all not matching rows 335 $rows.not(function() { 336 var name = jQuery(this).find('td').eq(1).text().toLowerCase(); 337 return searchText === '' || name.includes(searchText); 338 }).hide(); 339 340 if($selectedRow != null) { 341 // update index of the currently selected item inside the list of visible files 342 doxycode_tagselector.selected = $tbody.find('tr:visible').index($selectedRow); 343 344 if(doxycode_tagselector.selected < 0) { 345 // if row can't be selected anymore remove selected class 346 $selectedRow.removeClass('selected'); 347 } 348 } 349 350 }, 351 352 /** 353 * Executes the AJAX call for loading the tag configuration from the server 354 */ 355 updateTagNames: function(){ 356 var $res = jQuery(doxycode_tagselector.result); 357 358 // show the loading animation 359 $loading_animation = jQuery('<img src="'+DOKU_BASE+'lib/images/throbber.gif" alt="" width="16" height="16" />'); 360 361 $res.prepend($loading_animation); 362 363 // request the tag file configuration from the server 364 jQuery.post( 365 DOKU_BASE + 'lib/exe/ajax.php', 366 { 367 call: 'plugin_doxycode_get_tag_files' 368 }, 369 function(response) { 370 // update the local tag name list 371 doxycode_tagselector.renderTagNameList(response); 372 }, 373 'json' 374 ).fail(function(jqXHR, textStatus, errorThrown) { 375 console.error("AJAX error:", textStatus, errorThrown); 376 }); 377 }, 378 379 /** 380 * Insert or update a table with the current tag file configuration. 381 * 382 * @param {array} response Tag file configuration from the server. 383 */ 384 renderTagNameList: function(response) { 385 var $res = jQuery(doxycode_tagselector.result); 386 387 // remove the loading animation 388 $res.find('img').remove(); 389 390 var $table = jQuery('#doxycode__tagselector_table'); // Reference to the table 391 392 if ($table.length === 0) { 393 // If the table doesn't exist, create it 394 $table = jQuery('<table id="doxycode__tagselector_table"></table>'); 395 396 var $thead = jQuery('<thead></thead>'); 397 $table.append($thead); 398 399 var $row = jQuery('<tr></tr>'); 400 $thead.append($row); 401 402 $row.append(jQuery('<th></th>')); 403 $row.append(jQuery('<th></th>').text(LANG.plugins.doxycode.tag_selector_name)); 404 $row.append(jQuery('<th></th>').text(LANG.plugins.doxycode.tag_selector_description)); 405 406 $res.append($table); // Append the table to a container 407 } 408 409 var $tbody; 410 if ($table.find('tbody').length === 0) { 411 // If tbody doesn't exist, create it 412 $tbody = jQuery('<tbody></tbody>'); 413 $table.append($tbody); 414 } else { 415 $tbody = $table.find('tbody'); 416 } 417 418 var existingRows = $tbody.find('tr').get(); // Get existing rows as an array 419 420 for (const [key, value] of Object.entries(response)) { 421 var $matchingRow = jQuery(existingRows).filter(function() { 422 return jQuery(this).find('td:nth-child(2)').text() === key; 423 }); 424 425 if ($matchingRow.length > 0) { 426 // Row exists, update it 427 $matchingRow.find('td:nth-child(3)').text(value['description']); 428 429 // Remove the row from existingRows array as it's already processed 430 existingRows = existingRows.filter(row => row !== $matchingRow[0]); 431 } else { 432 // Row doesn't exist, create and insert it at the correct position 433 var $row = doxycode_tagselector.createRow(key, value); 434 doxycode_tagselector.insertRowInOrder($tbody, $row, Object.keys(response), key); 435 } 436 } 437 438 // Remove any remaining rows that weren't in the response 439 jQuery(existingRows).remove(); 440 441 doxycode_tagselector.filterRowsExec(); 442 443 // move focus back to search input 444 doxycode_tagselector.$search.focus(); 445 }, 446 447 /** 448 * Create a row for the table with the tag file names. 449 * 450 * The row consists of: 451 * - a checkbox for selecting the tag name for insertion 452 * - the tag name itself 453 * - a short description from the tag file configuration 454 * 455 * @param {String} key Tag File name 456 * @param {Array.<{description: String}>} value Configuration of the tag file 457 * @returns 458 */ 459 createRow: function(key, value) { 460 var $row = jQuery('<tr></tr>').data('name', key); 461 var $checkbox = jQuery('<input>', { type: 'checkbox', value: 0 }); 462 if (doxycode_tagselector.tagNames.includes(key)) { 463 $checkbox.prop("checked", true); 464 } 465 $row.append(jQuery('<td></td>').append($checkbox)); 466 var $link = jQuery('<a>').text(key); 467 $row.append(jQuery('<td>').append($link)); 468 $row.append(jQuery('<td></td>').text(value['description'])); 469 return $row; 470 }, 471 472 /** 473 * Insert a new row in the result table with the correct position based on the order of a tag name array 474 * 475 * @param {jQuery} $tbody Table body that displays the tag names 476 * @param {jQuery} $newRow Entry row generated with createRow() 477 * @param {Array<String>} keys Tag names that should be displayed in the given order 478 * @param {String} currentKey Tag name of the entry 479 */ 480 insertRowInOrder: function($tbody, $newRow, keys, currentKey) { 481 var inserted = false; 482 $tbody.find('tr').each(function() { 483 var rowKey = jQuery(this).find('td:nth-child(2)').text(); 484 var index = keys.indexOf(rowKey); 485 if (index > keys.indexOf(currentKey)) { 486 jQuery(this).before($newRow); 487 inserted = true; 488 return false; // break the .each loop 489 } 490 }); 491 if (!inserted) { 492 // Append to the end if not inserted in the middle 493 $tbody.append($newRow); 494 } 495 }, 496 497 /** 498 * Insert the tagfiles attribute with the list of checked tag names from the tag file selector. 499 * 500 * If a doxycode syntax was detected when the tag file selector was shown, it tries to update 501 * the existing doxycode syntax. If the doxycode syntax didn't include the tagfiles 502 * attribute, it inserts it before the filename (if there was one). 503 * 504 * Otherwise it creates a new doxycode syntax at the position 505 * 506 * @property {selection_class} doxycode_tagselector.doxycodeSelected used for replacing the existing doxycode syntax 507 * @property {selection_class} doxycode_tagselector.selection used for inserting a new doxycode syntax 508 */ 509 insertTagNames: function() { 510 var tagFileNames = doxycode_tagselector.getTagList(); 511 512 var tagFilesString = 'tagfiles="' + tagFileNames.join(' ') + '"'; 513 var tagfilesRegex = /tagfiles=".*?"/; 514 515 if(doxycode_tagselector.doxycodeSelected == null) { 516 // we have to insert a new doxycode syntax into the editor textArea 517 // use the start position from selector 518 var doxycode_string = '<doxycode>\n</doxycode>'; 519 520 // insert the string into the editor textArea 521 pasteText(doxycode_tagselector.selection,doxycode_string,{}); 522 523 // update doxycode selection, so that it matches the selection of the new text 524 doxycode_tagselector.doxycodeSelected = doxycode_tagselector.selection; 525 } 526 527 // update or insert tagfiles attribute! 528 529 var doxycodeText = doxycode_tagselector.doxycodeSelected.getText(); 530 var updatedDoxycode; 531 532 if (tagfilesRegex.test(doxycode_tagselector.doxycodeSelected.getText())) { 533 // Update the tagfiles attribute 534 updatedDoxycode = doxycodeText.replace(tagfilesRegex, tagFilesString); 535 } else { 536 // Add the tagfiles attribute, considering self-closing tags 537 updatedDoxycode = doxycodeText.replace(/<doxycode(.*?)(\/?>)/, function(match, attributes, closingTag) { 538 // Place tagFilesString before the filename and the closing tag 539 var filenameMatch = attributes.match(/ ([^ ]+)(\/?>)$/); 540 var filename = filenameMatch ? filenameMatch[1] : ''; 541 var updatedAttributes = filename ? attributes.replace(filename, '').trim() : attributes.trim(); 542 return '<doxycode ' + updatedAttributes + ' ' + tagFilesString + (filename ? ' ' + filename : '') + closingTag; 543 }); 544 } 545 546 // insert the string into the editor textArea 547 pasteText(doxycode_tagselector.doxycodeSelected,updatedDoxycode,{}); 548 549 return; 550 551 }, 552 /** 553 * Find the nearest doxycode syntax near the cursor in the edit textArea 554 * 555 * The current selection in the edit textArea might be: 556 * 557 * - at the start or inside of a '<doxycode ...>...<\doxycode>' or '<doxycode ...\>' block: 558 * 559 * -> just mark from '<' to '>' or '\>' 560 * 561 * - inside the code content 562 * 563 * -> check if there is a '<doxycode ...>' block that is not closed before the start 564 * 565 * -> then check if there is a '<\doxycode>' block after the end 566 * 567 * @property {selection_class} doxycode_tagselector.selection used for searching doxycode syntax near the original cursor position 568 * @returns {void} 569 */ 570 findNearestDoxycode: function() { 571 572 doxycode_tagselector.doxycodeSelected = new selection_class(); 573 574 // extract the text from the edit textArea 575 var text = doxycode_tagselector.textArea.value; 576 577 // Extract the line where the selection starts 578 var selectionStartLine = text.substring(0, doxycode_tagselector.selection.start).lastIndexOf('\n') + 1; 579 var selectionEndLine = text.indexOf('\n', doxycode_tagselector.selection.start); 580 if (selectionEndLine === -1) selectionEndLine = text.length; 581 582 var line = text.substring(selectionStartLine, selectionEndLine); 583 584 // Regex to match <doxycode ...> or <doxycode ...\> 585 var regex = /<doxycode(.*?)>/; 586 587 // detect if cursors starts at doxycode syntax 588 var match = regex.exec(line); 589 if (match) { 590 // copy over the object from the original selection 591 doxycode_tagselector.doxycodeSelected.obj = doxycode_tagselector.selection.obj; 592 593 // Update the selection to cover the entire <doxycode> tag 594 doxycode_tagselector.doxycodeSelected.start = selectionStartLine + match.index; 595 doxycode_tagselector.doxycodeSelected.end = selectionStartLine + match.index + match[0].length; 596 return; 597 } 598 599 // search for doxycode block before the current selection 600 601 // Extract the entire text before and after the selection 602 var textBeforeCursor = text.substring(0, doxycode_tagselector.selection.start); 603 var textAfterCursor = text.substring(doxycode_tagselector.selection.start); 604 605 // Regex to match the opening and closing of doxycode blocks, and self-closing tag 606 var openingTagRegex = /<doxycode(.*?)>/g; 607 var closingTagRegex = /<\/doxycode>|<doxycode.*?\/>/g; 608 609 // Find the last opening tag and first closing tag before the cursor 610 var lastOpeningTagIndex = -1, firstClosingTagIndex = -1; 611 var lastOpeningTagIndexLength = -1; 612 var match; 613 614 // Find the nearest opening tag before the cursor 615 while ((match = openingTagRegex.exec(textBeforeCursor)) !== null) { 616 lastOpeningTagIndex = match.index; 617 lastOpeningTagIndexLength = match[0].length; 618 } 619 620 // Check for closing tags before the cursor 621 while ((match = closingTagRegex.exec(textBeforeCursor)) !== null) { 622 if (match.index >= lastOpeningTagIndex) { 623 // Found a closing tag after or at the last opening tag 624 // ignore opening tag 625 lastOpeningTagIndex = -1; 626 } 627 } 628 629 // Find the nearest closing tag after the last opening tag 630 if (lastOpeningTagIndex !== -1) { 631 while ((match = closingTagRegex.exec(textAfterCursor)) !== null) { 632 firstClosingTagIndex = match.index + doxycode_tagselector.selection.start; 633 if (firstClosingTagIndex > lastOpeningTagIndex) { 634 break; 635 } 636 } 637 } 638 639 // Determine if the cursor is inside an open doxycode block 640 if (lastOpeningTagIndex !== -1 && (firstClosingTagIndex === -1 || firstClosingTagIndex > doxycode_tagselector.selection.start)) { 641 // Cursor is inside an open doxycode block 642 643 // copy over the object from the original selection 644 doxycode_tagselector.doxycodeSelected.obj = doxycode_tagselector.selection.obj; 645 646 // Update the selection to cover the entire <doxycode> tag 647 doxycode_tagselector.doxycodeSelected.start = lastOpeningTagIndex; 648 doxycode_tagselector.doxycodeSelected.end = lastOpeningTagIndex + lastOpeningTagIndexLength; 649 return; 650 } 651 652 doxycode_tagselector.doxycodeSelected = null; 653 654 return; 655 }, 656 657 /** 658 * Extract the tag file list from the selected '<doxycode tagfiles="">' syntax. 659 * 660 * @returns {void} 661 */ 662 getTagNamesFromSyntax: function() { 663 // clear the current list of tagNames 664 doxycode_tagselector.tagNames = []; 665 666 if(doxycode_tagselector.doxycodeSelected == null) { 667 return; 668 } 669 670 // extract the doxycode text from the selection 671 var doxycode_syntax = doxycode_tagselector.doxycodeSelected.getText(); 672 673 // get the tagfiles from it 674 var regex = /tagfiles="(.*?)"/; 675 676 var match = regex.exec(doxycode_syntax); 677 if (match) { 678 doxycode_tagselector.tagNames = match[1].split(" "); 679 return; 680 } 681 }, 682 683 clearResults: function() { 684 // get the table body 685 var $tbody = jQuery('#doxycode__tagselector_table tbody'); 686 687 // clear contents 688 $tbody.empty(); 689 }, 690 691 /** 692 * Show the tag selector 693 */ 694 show: function(){ 695 // prepare the update from the current selection 696 doxycode_tagselector.selection = DWgetSelection(doxycode_tagselector.textArea); 697 // we'll scan for the current <doxycode> block containing a 'tagfiles' argument 698 doxycode_tagselector.findNearestDoxycode(); 699 doxycode_tagselector.getTagNamesFromSyntax(); 700 701 // show the tag selector 702 doxycode_tagselector.$container.show(); 703 doxycode_tagselector.$search.focus(); 704 705 // get the current list of tagnames from the server 706 doxycode_tagselector.updateTagNames(); 707 708 // Move the cursor to the end of the input 709 var temp = doxycode_tagselector.$search.val(); 710 doxycode_tagselector.$search.val(''); 711 doxycode_tagselector.$search.val(temp); 712 }, 713 714 /** 715 * Hide the tag selector 716 */ 717 hide: function(){ 718 doxycode_tagselector.deselect(); 719 720 // clear any results from the last time 721 doxycode_tagselector.clearResults(); 722 723 doxycode_tagselector.$container.hide(); 724 // put the focus back to the editor 725 doxycode_tagselector.textArea.focus(); 726 }, 727 728 /** 729 * Toggle the tag selector 730 */ 731 toggle: function(){ 732 if(doxycode_tagselector.$container.css('display') == 'none'){ 733 doxycode_tagselector.show(); 734 }else{ 735 doxycode_tagselector.hide(); 736 } 737 } 738}; 739