1/* 2# Mantis - a php based bugtracking system 3 4# Copyright 2000 - 2002 Kenzaburo Ito - [email protected] 5# Copyright 2002 MantisBT Team - [email protected] 6 7# Mantis is free software: you can redistribute it and/or modify 8# it under the terms of the GNU General Public License as published by 9# the Free Software Foundation, either version 2 of the License, or 10# (at your option) any later version. 11# 12# Mantis is distributed in the hope that it will be useful, 13# but WITHOUT ANY WARRANTY; without even the implied warranty of 14# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15# GNU General Public License for more details. 16# 17# You should have received a copy of the GNU General Public License 18# along with Mantis. If not, see <http://www.gnu.org/licenses/>. 19 */ 20 21/** 22 * MantisBT config options. 23 * Initialized in javascript_config.php 24 * @property {string} config.collapse_settings_cookie 25 * @property {string} config.cookie_path 26 * @property {string} config.cookie_domain 27 * @property {string} config.cookie_samesite 28 */ 29/* global config */ 30 31// global code to determine how to set visibility 32var a = navigator.userAgent.indexOf("MSIE"); 33var style_display; 34 35if (a!= -1) { 36 style_display = 'block'; 37} else { 38 style_display = 'table-row'; 39} 40style_display = 'block'; 41 42$(document).ready( function() { 43 $('.collapse-open').show(); 44 $('.collapse-closed').hide(); 45 $('.collapse-link').click( function(event) { 46 event.preventDefault(); 47 var id = $(this).attr('id'); 48 var t_pos = id.indexOf('_closed_link' ); 49 if( t_pos == -1 ) { 50 t_pos = id.indexOf('_open_link' ); 51 } 52 var t_div = id.substring(0, t_pos ); 53 ToggleDiv( t_div ); 54 }); 55 56 /** 57 * Manage the navbar project menu initializacion and events 58 * for focus and key presses. 59 */ 60 61 /* initialize the list */ 62 var projects_list_options = { 63 valueNames: [ 'project-link' ] 64 }; 65 var listprojects = new List('projects-list', projects_list_options); 66 if( listprojects.items.length <= 10 ) { 67 $('#projects-list .projects-searchbox').hide(); 68 } 69 70 /** 71 * Events to manage focus when displaying the dropdown. 72 * - Focus on the active item to position the scrollable list on that item. 73 * - If there is no items to show, focus on the search box. 74 */ 75 $(document).on('shown.bs.dropdown', '#dropdown_projects_menu', function() { 76 var li_active = $(this).find('.dropdown-menu li.active a'); 77 if( li_active.length ) { 78 li_active.focus(); 79 } else { 80 $('#projects-list .search').focus(); 81 } 82 }); 83 84 /** 85 * When pressing a key in the search box, targeted at navigating the list, 86 * switch focus to the list. 87 * When pressing Escape, close the dropdown. 88 */ 89 $('#projects-list .search').keydown( function(event){ 90 switch (event.key) { 91 case 'ArrowDown': 92 case 'ArrowUp': 93 case 'Down': 94 case 'Up': 95 case 'PageDown': 96 case 'PageUp': 97 var list = $('#projects-list .list'); 98 if( list.find('li.active').length ) { 99 list.find('li.active a').focus(); 100 } else { 101 list.find('li a').first().focus(); 102 } 103 break; 104 case 'Escape': 105 $('#dropdown_projects_menu').removeClass('open'); 106 } 107 }); 108 109 /** 110 * When pressing a key in the list, which is not targeted at navigating the list, 111 * for example, typing a string, toggle focus to the search box. 112 */ 113 $('#projects-list .list').keydown( function(event){ 114 switch (event.key) { 115 case 'Enter': 116 case 'ArrowDown': 117 case 'ArrowUp': 118 case 'Down': 119 case 'Up': 120 case 'PageDown': 121 case 'PageUp': 122 case 'Home': 123 case 'End': 124 return; 125 } 126 $('#projects-list .search').focus(); 127 }); 128 129 $('.widget-box').on('shown.ace.widget', function(event) { 130 SetCookie(config.collapse_settings_cookie, $(this).attr('id'), false); 131 }).on('hidden.ace.widget', function(event) { 132 SetCookie(config.collapse_settings_cookie, $(this).attr('id'), true); 133 }).each( function() { 134 // Restore widget collapse state from cookies 135 var t_collapse = GetCookieId(config.collapse_settings_cookie, $(this).attr('id')); 136 if( t_collapse != -1 && $(this).find('> .widget-header a[data-action=collapse]').length ) { 137 t_collapse = ( t_collapse == '1' ); 138 if( t_collapse != $(this).hasClass('collapsed') ) { 139 140 var t_icon = $(this).find('> .widget-header a .ace-icon'); 141 var t_icon_1 = t_icon.data('icon-hide'); 142 if ( !t_icon_1 ) t_icon_1 = 'fa-chevron-up'; 143 var t_icon_2 = t_icon.data('icon-show'); 144 if ( !t_icon_2 ) t_icon_2 = 'fa-chevron-down'; 145 146 if ( t_collapse ) { 147 $(this).addClass('collapsed'); 148 t_icon.removeClass(t_icon_1).addClass(t_icon_2); 149 } else { 150 $(this).removeClass('collapsed'); 151 t_icon.removeClass(t_icon_2).addClass(t_icon_1); 152 } 153 } 154 } 155 }); 156 157 $('#sidebar-btn.sidebar-toggle').on('click', function (event) { 158 var t_sidebar = $(this).closest('.sidebar');
159 SetCookie(config.collapse_settings_cookie, t_sidebar.attr('id'), !t_sidebar.hasClass("menu-min")); 160 }).each( function() { 161 // Restore sidebar collapse state from cookies 162 var t_sidebar = $(this).closest('.sidebar'); 163 var t_collapse = GetCookieId(config.collapse_settings_cookie, t_sidebar.attr('id')); 164 if( t_collapse != -1 ) { 165 t_collapse = ( t_collapse == '1' ); 166 if( t_collapse != t_sidebar.hasClass('menu-min') ) { 167 168 var t_icon = $(this).find('> .ace-icon'); 169 var t_icon_1 = t_icon.data('icon1'); 170 if( !t_icon_1 ) t_icon_1 = 'fa-angle-double-left'; 171 var t_icon_2 = t_icon.data('icon2'); 172 if( !t_icon_2 ) t_icon_2 = 'fa-angle-double-right'; 173 174 if ( t_collapse ) { 175 t_sidebar.addClass('menu-min'); 176 t_icon.removeClass(t_icon_1).addClass(t_icon_2); 177 } else { 178 t_sidebar.removeClass('menu-min'); 179 t_icon.removeClass(t_icon_2).addClass(t_icon_1); 180 } 181 } 182 } 183 }); 184 185 $('input[type=text].typeahead').each(function() { 186 var $this = $(this); 187 $(this).typeahead({ 188 minLength: 1, 189 highlight: true 190 }, { 191 source: function (query, undefined, callback) { 192 var params = {}; 193 params['field'] = $this[0].id; 194 params['prefix'] = query; 195 $.getJSON('api/rest/index.php/internal/autocomplete', params, function (data) { 196 var results = []; 197 $.each(data, function (i, value) { 198 results.push(value); 199 }); 200 callback(results); 201 }); 202 } 203 }); 204 }); 205 206 $('a.dynamic-filter-expander').click(function(event) { 207 event.preventDefault(); 208 var fieldID = $(this).attr('id'); 209 var filter_id = $(this).data('filter_id'); 210 var filter_tmp_id = $(this).data('filter'); 211 var targetID = fieldID + '_target'; 212 var viewType = $('#filters_form_open input[name=view_type]').val(); 213 $('#' + targetID).html('<span class="dynamic-filter-loading">' + translations['loading'] + "</span>"); 214 var params = 'view_type=' + viewType + '&filter_target=' + fieldID; 215 if( undefined !== filter_id ) { 216 params += '&filter_id=' + filter_id; 217 } 218 if( undefined !== filter_tmp_id ) { 219 params += '&filter=' + filter_tmp_id; 220 } 221 $.ajax({ 222 url: 'return_dynamic_filters.php', 223 data: params, 224 cache: false, 225 context: $('#' + targetID), 226 success: function(html) { 227 $(this).html(html); 228 }, 229 error: function(obj,status,error) { 230 $(this).html('<span class="error-msg">' + status + ': ' + error + '</span>'); 231 } 232 }); 233 }); 234 235 $('input.autofocus:first, select.autofocus:first, textarea.autofocus:first').focus(); 236 237 var checkAllSelectors = ''; 238 $(':checkbox.check_all').each(function() { 239 var baseFieldName = $(this).attr('name').replace(/_all$/, ''); 240 if (checkAllSelectors.length > 0) { 241 checkAllSelectors += ', '; 242 } 243 checkAllSelectors += ':checkbox[name="' + baseFieldName + '[]"]'; 244 }); 245 if (checkAllSelectors.length > 0) { 246 $(checkAllSelectors).click(function() { 247 var fieldName = $(this).attr('name').replace(/\[\]/g, ''); 248 var checkedCount = $(this).closest('form').find(':checkbox[name="' + fieldName + '[]"]:checked').length; 249 var totalCount = $(this).closest('form').find(':checkbox[name="' + fieldName + '[]"]').length; 250 var allSelected = checkedCount == totalCount; 251 $(this).closest('form').find(':checkbox[name=' + fieldName + '_all]').prop('checked', allSelected); 252 }); 253 $(':checkbox.check_all').click(function() { 254 var baseFieldName = $(this).attr('name').replace(/_all$/, ''); 255 $(this).closest('form').find(':checkbox[name="' + baseFieldName + '[]"]').prop('checked', $(this).is(':checked')); 256 }); 257 } 258 259 /** 260 * Prepare a table where the checkboxes range selection has been applied 261 * Save row and column index in each cell for easier iteration. 262 * This assumes no rowspan or colspan is used in the area where the ckecboxes are rendered. 263 */ 264 $('table.checkbox-range-selection').each(function(){ 265 $(this).find('tr').each( function(row_index){ 266 $(this).children('th, td').each(function(col_index){ 267 $(this).data('col_index', col_index); 268 $(this).data('row_index', row_index); 269 }); 270 }); 271 }); 272 273 /** 274 * Enable range selection for checkboxes, inside a container having class "checkbox-range-selection" 275 * Assumes the bootstrap/ace styled checkboxes: 276 * <label> 277 * <input type="checkbox" class="ace"> 278 * <span class="lbl"></span> 279 * </label> 280 */ 281 $('.checkbox-range-selection').on('click', 'label', function (e) {
282 if( $(this).children('input:checkbox').length == 0 ) { 283 return; 284 } 285 var jcontainer = $(this).closest('.checkbox-range-selection'); 286 var last_clicked = jcontainer.data('checkbox-range-last-clicked'); 287 if (!last_clicked) { 288 last_clicked = this; 289 } 290 if (e.shiftKey) { 291 // Because shift-click is triggered in a label/span, some browsers 292 // will activate a text selection. Remove text selection. 293 window.getSelection().removeAllRanges(); 294 var cb_label_list = jcontainer.find('label').has('input:checkbox'); 295 // The actual input hasn't been changed yet, so we want to set the 296 // opposite value for all the checkboxes 297 var clicked_current_st = $(this).find('input:checkbox').first().prop('checked'); 298 // The currently clicked one is also modified, becasue shift-click is not 299 // recognised correctly by the framework. See #25215 300 if( jcontainer.is('table') ) { 301 // Special case for a table container: 302 // we traverse the table cells for a rectangular area 303 var cell_1 = $(this).closest('th, td'); 304 var row_1 = cell_1.data('row_index'); 305 var col_1 = cell_1.data('col_index'); 306 var cell_2 = $(last_clicked).closest('th, td'); 307 var row_2 = cell_2.data('row_index'); 308 var col_2 = cell_2.data('col_index'); 309 var row_start = Math.min(row_1, row_2); 310 var row_end = Math.max(row_1, row_2); 311 var col_start = Math.min(col_1, col_2); 312 var col_end = Math.max(col_1, col_2); 313 for (i = 0 ; i <= cb_label_list.length ; i++) { 314 var it_td = $(cb_label_list[i]).closest('th, td'); 315 var it_row = it_td.data('row_index'); 316 var it_col = it_td.data('col_index'); 317 if( row_start <= it_row && it_row <= row_end 318 && col_start <= it_col && it_col <= col_end ) { 319 $(cb_label_list[i]).find('input:checkbox').prop('checked', !clicked_current_st); 320 } 321 } 322 } else { 323 // General case: we traverse the items by their relative index 324 var start = cb_label_list.index(this); 325 var end = cb_label_list.index(last_clicked); 326 var index_start = Math.min(start, end); 327 var index_end = Math.max(start, end); 328 for (i = index_start ; i <= index_end ; i++) { 329 $(cb_label_list[i]).find('input:checkbox').prop('checked', !clicked_current_st); 330 } 331 } 332 } 333 jcontainer.data('checkbox-range-last-clicked', this); 334 }); 335 336 var stopwatch = { 337 timerID: 0, 338 startTime: null, 339 zeroTime: moment('0', 's'), 340 tick: function() { 341 var elapsedDiff = moment().diff(this.startTime), 342 elapsedTime = this.zeroTime.clone().add(elapsedDiff); 343 344 $('input[type=text].stopwatch_time').val(elapsedTime.format('HH:mm:ss')); 345 }, 346 reset: function() { 347 this.stop(); 348 $('input[type=text].stopwatch_time').val(''); 349 }, 350 start: function() { 351 var self = this, 352 timeFormat = '', 353 stoppedTime = $('input[type=text].stopwatch_time').val(); 354 355 this.stop(); 356 357 if (stoppedTime) { 358 switch (stoppedTime.split(':').length) { 359 case 1: 360 timeFormat = 'ss'; 361 break; 362 363 case 2: 364 timeFormat = 'mm:ss'; 365 break; 366 367 default: 368 timeFormat = 'HH:mm:ss'; 369 } 370 371 this.startTime = moment().add(this.zeroTime.clone().diff(moment(stoppedTime, timeFormat))); 372 } else { 373 this.startTime = moment(); 374 } 375 376 this.timerID = window.setInterval(function() { 377 self.tick(); 378 }, 1000); 379 380 $('input[type=button].stopwatch_toggle').val(translations['time_tracking_stopwatch_stop']); 381 }, 382 stop: function() { 383 if (this.timerID) { 384 window.clearInterval(this.timerID); 385 this.timerID = 0; 386 } 387 388 $('input[type=button].stopwatch_toggle').val(translations['time_tracking_stopwatch_start']); 389 } 390 }; 391 392 $('input[type=button].stopwatch_toggle').click(function() { 393 if (!stopwatch.timerID) { 394 stopwatch.start(); 395 } else { 396 stopwatch.stop(); 397 } 398 }); 399 400 $('input[type=button].stopwatch_reset').click(function() { 401 stopwatch.reset(); 402 }); 403 404 $('input[type=text].datetimepicker').each(function(index, element) { 405 $(this).datetimepicker({ 406 locale: $(this).data('picker-locale'), 407 format: $(this).data('picker-format'), 408 useCurrent: false, 409 icons: { 410 time: 'fa fa-clock-o', 411 date: 'fa fa-calendar', 412 up: 'fa fa-chevron-up', 413 down: 'fa fa-chevron-down', 414 previous: 'fa fa-chevron-left', 415 next: 'fa fa-chevron-right', 416 today: 'fa fa-arrows ',
417 clear: 'fa fa-trash', 418 close: 'fa fa-times' 419 } 420 }).next().on(ace.click_event, function() { 421 $(this).prev().focus(); 422 }); 423 }); 424 425 $( 'form .dropzone' ).each(function(){ 426 var classPrefix = 'dropzone'; 427 var autoUpload = $(this).hasClass('auto-dropzone'); 428 var zoneObj = enableDropzone( classPrefix, autoUpload ); 429 if( zoneObj ) { 430 /* Attach image paste handler to report-bug & add-note forms */ 431 $( '#bugnoteadd, #report_bug_form' ).bind( 'paste', function( event ) { 432 var items = ( event.clipboardData || event.originalEvent.clipboardData ).items; 433 for( index in items ) { 434 var item = items[index]; 435 if( item.kind === 'file' ) { 436 zoneObj.addFile( item.getAsFile() ) 437 } 438 } 439 }); 440 } 441 }); 442 443 $('.bug-jump').find('[name=bug_id]').focus( function() { 444 var bug_label = $('.bug-jump-form').find('[name=bug_label]').val(); 445 if( $(this).val() == bug_label ) { 446 $(this).val(''); 447 $(this).removeClass('field-default'); 448 } 449 }); 450 $('.bug-jump').find('[name=bug_id]').blur( function() { 451 var bug_label = $('.bug-jump-form').find('[name=bug_label]').val(); 452 if( $(this).val() == '' ) { 453 $(this).val(bug_label); 454 $(this).addClass('field-default'); 455 } 456 }); 457 $('[name=source_query_id]').change( function() { 458 $(this.form).submit(); 459 }); 460 461 /* Project selector: auto-switch on select */ 462 $('#form-set-project-id').change( function() { 463 $('#form-set-project').submit(); 464 }); 465 $('#project-selector').children('.button').hide(); 466 467 setBugLabel(); 468 469 /* Handle standard filter date fields */ 470 $(document).on('change', '.js_switch_date_inputs_trigger', function() { 471 $(this).closest('table') 472 .find('select') 473 .prop('disabled', !$(this).prop('checked')); 474 }); 475 476 /* Handle custom field of date type */ 477 $(document).on('change', 'select[name^=custom_field_][name$=_control]', function() { 478 var table = $(this).closest('table'); 479 switch(this.value) { 480 case '2': // between 481 $(table).find("select[name*=_start_year]").prop('disabled', false); 482 $(table).find("select[name*=_start_month]").prop('disabled', false); 483 $(table).find("select[name*=_start_day]").prop('disabled', false); 484 $(table).find("select[name*=_end_year]").prop('disabled', false); 485 $(table).find("select[name*=_end_month]").prop('disabled', false); 486 $(table).find("select[name*=_end_day]").prop('disabled', false); 487 break; 488 489 case '3': // on or before 490 case '4': // before 491 case '5': // on 492 case '6': // after 493 case '7': // on or after 494 $(table).find("select[name*=_start_year]").prop('disabled', false); 495 $(table).find("select[name*=_start_month]").prop('disabled', false); 496 $(table).find("select[name*=_start_day]").prop('disabled', false); 497 $(table).find("select[name*=_end_year]").prop('disabled', true); 498 $(table).find("select[name*=_end_month]").prop('disabled', true); 499 $(table).find("select[name*=_end_day]").prop('disabled', true); 500 break; 501 502 case '0': // any 503 case '1': // none 504 default: 505 $(table).find("select[name*=_start_year]").prop('disabled', true); 506 $(table).find("select[name*=_start_month]").prop('disabled', true); 507 $(table).find("select[name*=_start_day]").prop('disabled', true); 508 $(table).find("select[name*=_end_year]").prop('disabled', true); 509 $(table).find("select[name*=_end_month]").prop('disabled', true); 510 $(table).find("select[name*=_end_day]").prop('disabled', true); 511 break; 512 } 513 }); 514 515 /* For Period.php bundled with the core MantisGraph plugin */ 516 $('#dates > input[type=image].datetime').hide(); 517 $('#period_menu > select#interval').change(function() { 518 if ($(this).val() == 10) { 519 $('#dates > input[type=text].datetime').prop('disabled', false); 520 $('#dates > input[type=image].datetime').show(); 521 } else { 522 $('#dates > input[type=text].datetime').prop('disabled', true); 523 $('#dates > input[type=image].datetime').hide(); 524 } 525 }); 526 527 $(document).on('change', '#tag_select', function() { 528 var tagSeparator = $('#tag_separator').val(); 529 var currentTagString = $('#tag_string').val(); 530 var newTagOptionID = $(this).val(); 531 var newTag = $('#tag_select option[value=' + newTagOptionID + ']').text(); 532 var tagArray = currentTagString.split(tagSeparator); 533 if (tagArray.indexOf(newTag) == -1) { 534 if (currentTagString.length > 0) { 535 $('#tag_string').val(currentTagString + tagSeparator + newTag); 536 } else { 537 $('#tag_string').val(newTag); 538 } 539 } 540 $(this).val(0); 541 }); 542 543 $('a.click-url').bind("click", function() { 544 $(this).attr("href", $(this).data("url")); 545 }); 546 547 $('input[name=private].ace').bind("click", function() { 548 if ($(this).is(":checked")){ 549 $('textarea[name=bugnote_text]').addClass("bugnote-private"); 550 } else { 551 $('textarea[name=bugnote_text]').removeClass("bugnote-private"); 552 } 553 }); 554 555 /** 556 * Manage visiblity on hover trigger objects 557 */ 558 if( $('.visible-on-hover-toggle').length ) { 559 $('.visible-on-hover-toggle').hover( 560 function(e){ // handlerIn 561 $(e.currentTarget).find('.visible-on-hover').removeClass('invisible'); 562 }, 563 function(e){ // handlerOut 564 $(e.currentTarget).find('.visible-on-hover').addClass('invisible'); 565 } 566 ); 567 $('.visible-on-hover').addClass('invisible'); 568 } 569 570 /** 571 * Enhance tables with sortable columns using list.js 572 */ 573 $('.table-responsive.sortable').each(function(){ 574 var jtable = $(this).find('table').first(); 575 var ths = jtable.find('thead > tr > th'); 576 if( !ths.length ) { 577 // exit if there is no headers 578 return; 579 } 580 var th_count = ths.length 581 582 var trs = jtable.find('tbody > tr'); 583 if( trs.length > 1000 ) { 584 // don't run on big tables to avoid perfomance issues in client side 585 return; 586 } 587 588 var options_valuenames = []; 589 var exclude_index = []; 590 ths.each(function(index){ 591 if( $(this).hasClass('no-sort') ) { 592 // if the column says no sorting, save this index for later checks and skip 593 exclude_index.push(index); 594 return; 595 } 596 // wrap the contents into a crafted div
597 var new_div = $('<div />').addClass('sort') 598 .attr('data-sort','sortkey_'+index) 599 .attr('role','button') 600 .html($(this).html()); 601 $(this).html(new_div); 602 603 options_valuenames.push( { name:'sortkey_'+index, attr:'data-sortval' } ); 604 }); 605 trs.each(function(){ 606 var tds = $(this).children('td'); 607 if( tds.length != th_count ) { 608 // exit if different number of cells than headers, possibly colspan, etc 609 return; 610 } 611 tds.each(function(index){ 612 if( exclude_index.indexOf(index) >= 0 ) { 613 // if this column was marked as no-sorting, skip. 614 return; 615 } 616 $(this).addClass( 'sortkey_'+index ).attr( 'data-sortval', $(this).text() ); 617 }); 618 }); 619 jtable.find('tbody').addClass('list'); 620 621 var listoptions = { valueNames: options_valuenames }; 622 var listobject = new List( this, listoptions ); 623 $(this).data('listobject',listobject).data('listoptions',listoptions).addClass('listjs-table'); 624 }); 625 626 /** 627 * Change status color box's color when a different status is selected. 628 * To achieve that we need to store the current value in a data attribute, 629 * to compute the class name to remove in the change event. 630 */ 631 var statusColor = $('#status'); 632 // Store current value 633 statusColor.data('prev', statusColor.val()); 634 statusColor.change(function () { 635 function getColorClassName (statusCode) { 636 return 'status-' + statusCode + '-fg'; 637 } 638 639 var me = $(this); 640 me.siblings('i') 641 .removeClass(getColorClassName(me.data('prev'))) 642 .addClass(getColorClassName(me.val())); 643 me.data('prev', me.val()); 644 }); 645 646 /** 647 * Adjust footer height based on its content 648 */ 649 var bottom = $('.navbar-fixed-bottom'); 650 var bottom_placeholder = $('<div>', { 651 'class': 'col-xs-12', 652 'width': '100%' 653 }).insertBefore(bottom); 654 var footer = $('.footer'); 655 var footer_content = $('.footer-content'); 656 $(window).on('resize.footer', function() { 657 bottom_placeholder.css('height', parseInt(bottom.height()) + 'px'); 658 footer.css('padding-top', parseInt(footer_content.height()) + 'px'); 659 }).triggerHandler('resize.footer'); 660}); 661 662function setBugLabel() { 663 var bug_label = $('.bug-jump-form').find('[name=bug_label]').val(); 664 var field = $('.bug-jump').find('[name=bug_id]'); 665 if( field.val() == '' ) { 666 field.val(bug_label); 667 field.addClass('field-default'); 668 } 669} 670 671/* 672 * String manipulation 673 */ 674function Trim( p_string ) { 675 if (typeof p_string != "string") { 676 return p_string; 677 } 678 679 var t_string = p_string; 680 var t_ch = ''; 681 682 // Trim beginning spaces 683 684 t_ch = t_string.substring( 0, 1 ); 685 while ( t_ch == " " ) { 686 t_string = t_string.substring( 1, t_string.length ); 687 t_ch = t_string.substring( 0, 1 ); 688 } 689 690 // Trim trailing spaces 691 692 t_ch = t_string.substring( t_string.length-1, t_string.length ); 693 while ( t_ch == " " ) { 694 t_string = t_string.substring( 0, t_string.length-1 ); 695 t_ch = t_string.substring( t_string.length-1, t_string.length ); 696 } 697 698 return t_string; 699} 700 701/* 702 * Cookie functions 703 */ 704function GetCookie( p_cookie ) { 705 var t_cookies = document.cookie.split(';'); 706 for( var i in t_cookies ) {
707 var t_cookie = t_cookies[i].split('='); 708 if ( Trim( t_cookie[0] ) == p_cookie ) { 709 return t_cookie[1].split('|'); 710 } 711 } 712 713 return -1; 714} 715 716function SetCookie( p_cookie, p_id, p_value ) { 717 var t_expires = new Date(); 718 t_expires.setTime(t_expires.getTime() + (365 * 24 * 60 * 60 * 1000)); 719 720 var t_new_cookies = new Array(); 721 var t_cookies = GetCookie(p_cookie); 722 if( t_cookies != -1 ) { 723 for( var i in t_cookies ) { 724 if ( t_cookies[i].indexOf(p_id + ':') != 0 ) { 725 t_new_cookies.push(t_cookies[i]); 726 } 727 } 728 } 729 t_new_cookies.push(p_id + ':' + (p_value ? '1' : '0')); 730 731 document.cookie = p_cookie + "=" + t_new_cookies.join('|') + 732 "; expires=" + t_expires.toUTCString() + 733 ( config.cookie_domain ? "; domain=" + config.cookie_domain : '' ) + 734 "; path=" + config.cookie_path + 735 "; samesite=" + config.cookie_samesite; 736} 737 738function GetCookieId( p_cookie, p_id ) { 739 var t_cookies = GetCookie(p_cookie); 740 if( t_cookies != -1 ) { 741 for( var i in t_cookies ) { 742 if( t_cookies[i].indexOf(p_id + ':') == 0 ) { 743 return t_cookies[i].at(p_id.length + 1); 744 } 745 } 746 } 747 748 return -1; 749} 750 751function ToggleDiv( p_div ) { 752 var t_open_div = '#' + p_div + "_open"; 753 var t_closed_div = '#' + p_div + "_closed"; 754 var t_open_display = $(t_open_div).css('display'); 755 $(t_open_div).toggle(); 756 757 if( $(t_closed_div).length ) { 758 $(t_closed_div).toggle(); 759 } 760 761 SetCookie( config.collapse_settings_cookie, p_div, ( t_open_display == "none" ) ); 762} 763 764function setDisplay(idTag, state) 765{ 766 if(!document.getElementById(idTag)) alert('SetDisplay(): id '+idTag+' is empty'); 767 // change display visibility 768 if ( state != 0 ) { 769 document.getElementById(idTag).style.display = style_display; 770 } else { 771 document.getElementById(idTag).style.display = 'none'; 772 } 773} 774 775function toggleDisplay(idTag) 776{ 777 setDisplay( idTag, (document.getElementById(idTag).style.display == 'none')?1:0 ); 778} 779 780// Dropzone handler 781Dropzone.autoDiscover = false; 782function enableDropzone( classPrefix, autoUpload ) { 783 var zone_class = '.' + classPrefix; 784 var zone = $( zone_class ); 785 var form = zone.closest('form'); 786 var max_filesize_bytes = zone.data('max-filesize-bytes'); 787 var max_filesize_mb = Math.ceil( max_filesize_bytes / ( 1024*1024) ); 788 var max_filename_length = zone.data( 'max-filename-length' ); 789 var options = { 790 forceFallback: zone.data('force-fallback'), 791 paramName: "ufile", 792 autoProcessQueue: autoUpload, 793 clickable: zone_class, 794 previewsContainer: '#' + classPrefix + '-previews-box', 795 uploadMultiple: true, 796 parallelUploads: 100, 797 maxFilesize: max_filesize_mb, 798 timeout: 0, 799 addRemoveLinks: false, 800 acceptedFiles: zone.data('accepted-files'), 801 thumbnailWidth: 150, 802 thumbnailMethod: 'contain', 803 dictDefaultMessage: zone.data('default-message'), 804 dictFallbackMessage: zone.data('fallback-message'), 805 dictFallbackText: zone.data('fallback-text'), 806 dictFileTooBig: zone.data('file-too-big'), 807 dictInvalidFileType: zone.data('invalid-file-type'), 808 dictResponseError: zone.data('response-error'), 809 dictCancelUpload: zone.data('cancel-upload'), 810 dictCancelUploadConfirmation: zone.data('cancel-upload-confirmation'), 811 dictRemoveFile: zone.data('remove-file'), 812 dictRemoveFileConfirmation: zone.data('remove-file-confirmation'), 813 dictMaxFilesExceeded: zone.data('max-files-exceeded'), 814 815 init: function () { 816 var dropzone = this; 817 var form = $( this.options.clickable ).closest('form'); 818 form.on('submit', function (e) { 819 if( dropzone.getQueuedFiles().length ) { 820 e.preventDefault(); 821 e.stopPropagation(); 822 dropzone.processQueue(); 823 } 824 }); 825 this.on( "successmultiple", function( files, response ) { 826 document.open(); 827 document.write( response ); 828 document.close(); 829 }); 830 this.on( "complete", function( file ) { 831 // Set progress bar as inactive 832 let progressbar = file.previewElement.querySelector('.progress'); 833 progressbar.classList.remove('active'); 834 }); 835 /** 836 * 'addedfiles' is undocumented but works similar to 'addedfile' 837 * It's triggered once after a multiple file addition, and receives 838 * an array with the added files. 839 */ 840 this.on("addedfiles", function (files) { 841 var bullet = '-\u00A0'; 842 var error_file_too_big = ''; 843 var error_filename_too_long = ''; 844 for (var i = 0; i < files.length; i++) { 845 if( files[i].size > max_filesize_bytes ) { 846 var size_mb = files[i].size / ( 1024*1024 ); 847 var dec = size_mb < 0.01 ? 3 : 2; 848 error_file_too_big += bullet + '"' + files[i].name + '" (' + size_mb.toFixed(dec) + ' MiB)\n'; 849 this.removeFile( files[i] ); 850 } else if( files[i].name.length > 250 ) { 851 error_filename_too_long += bullet + '"' + files[i].name + '" (' + files[i].name.length + ')\n'; 852 // this.removeFile( files[i] ); 853 } 854 } 855 856 var text = ''; 857 var error_message = ''; 858 if( error_file_too_big ) { 859 var max_mb = max_filesize_bytes / ( 1024*1024 ); 860 var max_mb_dec = max_mb < 0.01 ? 3 : 2; 861 text = zone.data( 'dropzone_multiple_files_too_big' ) + "\n"; 862 text = text.replace( '{{files}}', '\n' + error_file_too_big ); 863 text = text.replace( '{{maxFilesize}}', max_mb.toFixed(max_mb_dec) ); 864 error_message += text; 865 } 866 867 if( error_filename_too_long ) { 868 text = zone.data( 'dropzone_multiple_filenames_too_long' ) + "\n"; 869 text = text.replace( '{{maxFilenameLength}}', max_filename_length ); 870 text = text.replace( '{{files}}', '\n' + error_filename_too_long ); 871 error_message += text; 872 } 873 if( error_message ) { 874 alert(error_message); 875 } 876 }); 877 }, 878 fallback: function() { 879 if( $( "." + classPrefix ).length ) { 880 $( "." + classPrefix ).hide(); 881 } 882 } 883 }; 884 var preview_template = document.getElementById('dropzone-preview-template'); 885 if( preview_template ) { 886 options.previewTemplate = preview_template.innerHTML; 887 } 888 889 var zone_object = null; 890 try { 891 zone_object = new Dropzone( form[0], options ); 892 } catch (e) { 893 alert( zone.data('dropzone-not-supported') ); 894 } 895 896 return zone_object; 897}
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.