1/** 2 * This object handles ajax requests for pages. It also 3 * handles the reloading of the main menu and scripts. 4 * 5 * @test-module AJAX 6 */ 7var AJAX = { 8 /** 9 * @var {boolean} active Whether we are busy 10 */ 11 active: false, 12 13 /** 14 * @var {object} source The object whose event initialized the request 15 */ 16 source: null, 17 18 /** 19 * @var {object} xhr A reference to the ajax request that is currently running 20 */ 21 xhr: null, 22 23 /** 24 * @var {object} lockedTargets, list of locked targets 25 */ 26 lockedTargets: {}, 27 // eslint-disable-next-line valid-jsdoc 28 29 /** 30 * @var {Function} callback Callback to execute after a successful request 31 * Used by CommonActions from common.js 32 */ 33 callback: function () {}, 34 35 /** 36 * @var {boolean} debug Makes noise in your Firebug console 37 */ 38 debug: false, 39 40 /** 41 * @var {object} $msgbox A reference to a jQuery object that links to a message 42 * box that is generated by Functions.ajaxShowMessage() 43 */ 44 $msgbox: null, 45 46 /** 47 * Given the filename of a script, returns a hash to be 48 * used to refer to all the events registered for the file 49 * 50 * @param {string} key key The filename for which to get the event name 51 * 52 * @return {number} 53 */ 54 hash: function (key) { 55 var newKey = key; 56 /* https://burtleburtle.net/bob/hash/doobs.html#one */ 57 58 newKey += ''; 59 var len = newKey.length; 60 var hash = 0; 61 var i = 0; 62 63 for (; i < len; ++i) { 64 hash += newKey.charCodeAt(i); 65 hash += hash << 10; 66 hash ^= hash >> 6; 67 } 68 69 hash += hash << 3; 70 hash ^= hash >> 11; 71 hash += hash << 15; 72 return Math.abs(hash); 73 }, 74 75 /** 76 * Registers an onload event for a file 77 * 78 * @param {string} file The filename for which to register the event 79 * @param {Function} func The function to execute when the page is ready 80 * 81 * @return {self} For chaining 82 */ 83 registerOnload: function (file, func) { 84 var eventName = 'onload_' + AJAX.hash(file); 85 $(document).on(eventName, func); 86 87 if (this.debug) { 88 // eslint-disable-next-line no-console 89 console.log( // no need to translate 90 'Registered event ' + eventName + ' for file ' + file); 91 } 92 93 return this; 94 }, 95 96 /** 97 * Registers a teardown event for a file. This is useful to execute functions 98 * that unbind events for page elements that are about to be removed. 99 * 100 * @param {string} file The filename for which to register the event 101 * @param {Function} func The function to execute when 102 * the page is about to be torn down 103 * 104 * @return {self} For chaining 105 */ 106 registerTeardown: function (file, func) { 107 var eventName = 'teardown_' + AJAX.hash(file); 108 $(document).on(eventName, func); 109 110 if (this.debug) { 111 // eslint-disable-next-line no-console 112 console.log( // no need to translate 113 'Registered event ' + eventName + ' for file ' + file); 114 } 115 116 return this; 117 }, 118 119 /** 120 * Called when a page has finished loading, once for every 121 * file that registered to the onload event of that file. 122 * 123 * @param {string} file The filename for which to fire the event 124 * 125 * @return {void} 126 */ 127 fireOnload: function (file) { 128 var eventName = 'onload_' + AJAX.hash(file); 129 $(document).trigger(eventName); 130 131 if (this.debug) { 132 // eslint-disable-next-line no-console 133 console.log( // no need to translate 134 'Fired event ' + eventName + ' for file ' + file); 135 } 136 }, 137 138 /** 139 * Called just before a page is torn down, once for every 140 * file that registered to the teardown event of that file. 141 * 142 * @param {string} file The filename for which to fire the event 143 * 144 * @return {void} 145 */ 146 fireTeardown: function (file) { 147 var eventName = 'teardown_' + AJAX.hash(file); 148 $(document).triggerHandler(eventName); 149 150 if (this.debug) { 151 // eslint-disable-next-line no-console 152 console.log( // no need to translate 153 'Fired event ' + eventName + ' for file ' + file); 154 } 155 }, 156 157 /** 158 * function to handle lock page mechanism 159 * 160 * @param event the event object 161 * 162 * @return {void} 163 */ 164 lockPageHandler: function (event) { 165 // don't consider checkbox event 166 if (typeof event.target !== 'undefined') { 167 if (event.target.type === 'checkbox') { 168 return; 169 } 170 } 171 172 var newHash = null; 173 var oldHash = null; 174 var lockId; // CodeMirror lock 175 176 if (event.data.value === 3) { 177 newHash = event.data.content; 178 oldHash = true; 179 lockId = 'cm'; 180 } else { 181 // Don't lock on enter. 182 if (0 === event.charCode) { 183 return; 184 } 185 186 lockId = $(this).data('lock-id'); 187 188 if (typeof lockId === 'undefined') { 189 return; 190 } 191 /* 192 * @todo Fix Code mirror does not give correct full value (query) 193 * in textarea, it returns only the change in content. 194 */ 195 196 197 if (event.data.value === 1) { 198 newHash = AJAX.hash($(this).val()); 199 } else { 200 newHash = AJAX.hash($(this).is(':checked')); 201 } 202 203 oldHash = $(this).data('val-hash'); 204 } // Set lock if old value !== new value 205 // otherwise release lock 206 207 208 if (oldHash !== newHash) { 209 AJAX.lockedTargets[lockId] = true; 210 } else { 211 delete AJAX.lockedTargets[lockId]; 212 }
212 // Show lock icon if locked targets is not empty. 213 // otherwise remove lock icon 214 215 216 if (!jQuery.isEmptyObject(AJAX.lockedTargets)) { 217 $('#lock_page_icon').html(Functions.getImage('s_lock', Messages.strLockToolTip).toString()); 218 } else { 219 $('#lock_page_icon').html(''); 220 } 221 }, 222 223 /** 224 * resets the lock 225 * 226 * @return {void} 227 */ 228 resetLock: function () { 229 AJAX.lockedTargets = {}; 230 $('#lock_page_icon').html(''); 231 }, 232 handleMenu: { 233 replace: function (content) { 234 $('#floating_menubar').html(content) // Remove duplicate wrapper 235 // TODO: don't send it in the response 236 .children().first().remove(); 237 $('#topmenu').menuResizer(Functions.mainMenuResizerCallback); 238 } 239 }, 240 241 /** 242 * Event handler for clicks on links and form submissions 243 * 244 * @param {KeyboardEvent} event Event data 245 * 246 * @return {boolean | void} 247 */ 248 requestHandler: function (event) { 249 // In some cases we don't want to handle the request here and either 250 // leave the browser deal with it natively (e.g: file download) 251 // or leave an existing ajax event handler present elsewhere deal with it 252 var href = $(this).attr('href'); 253 254 if (typeof event !== 'undefined' && (event.shiftKey || event.ctrlKey || event.metaKey)) { 255 return true; 256 } else if ($(this).attr('target')) { 257 return true; 258 } else if ($(this).hasClass('ajax') || $(this).hasClass('disableAjax')) { 259 // reset the lockedTargets object, as specified AJAX operation has finished 260 AJAX.resetLock(); 261 return true; 262 } else if (href && href.match(/^#/)) { 263 return true; 264 } else if (href && href.match(/^mailto/)) { 265 return true; 266 } else if ($(this).hasClass('ui-datepicker-next') || $(this).hasClass('ui-datepicker-prev')) { 267 return true; 268 } 269 270 if (typeof event !== 'undefined') { 271 event.preventDefault(); 272 event.stopImmediatePropagation(); 273 } // triggers a confirm dialog if: 274 // the user has performed some operations on loaded page 275 // the user clicks on some link, (won't trigger for buttons) 276 // the click event is not triggered by script 277 278 279 if (typeof event !== 'undefined' && event.type === 'click' && event.isTrigger !== true && !jQuery.isEmptyObject(AJAX.lockedTargets) && confirm(Messages.strConfirmNavigation) === false) { 280 return false; 281 } 282 283 AJAX.resetLock(); 284 var isLink = !!href || false; 285 var previousLinkAborted = false; 286 287 if (AJAX.active === true) { 288 // Cancel the old request if abortable, when the user requests 289 // something else. Otherwise silently bail out, as there is already 290 // a request well in progress. 291 if (AJAX.xhr) { 292 // In case of a link request, attempt aborting 293 AJAX.xhr.abort(); 294 295 if (AJAX.xhr.status === 0 && AJAX.xhr.statusText === 'abort') { 296 // If aborted 297 AJAX.$msgbox = Functions.ajaxShowMessage(Messages.strAbortedRequest); 298 AJAX.active = false; 299 AJAX.xhr = null; 300 previousLinkAborted = true; 301 } else { 302 // If can't abort 303 return false; 304 } 305 } else { 306 // In case submitting a form, don't attempt aborting 307 return false; 308 } 309 } 310 311 AJAX.source = $(this); 312 $('html, body').animate({ 313 scrollTop: 0 314 }, 'fast'); 315 var url = isLink ? href : $(this).attr('action'); 316 var argsep = CommonParams.get('arg_separator'); 317 var params = 'ajax_request=true' + argsep + 'ajax_page_request=true'; 318 var dataPost = AJAX.source.getPostData(); 319 320 if (!isLink) { 321 params += argsep + $(this).serialize(); 322 } else if (dataPost) { 323 params += argsep + dataPost; 324 isLink = false; 325 } 326 327 if (AJAX.debug) { 328 // eslint-disable-next-line no-console 329 console.log('Loading: ' + url); // no need to translate 330 } 331 332 if (isLink) { 333 AJAX.active = true; 334 AJAX.$msgbox = Functions.ajaxShowMessage(); // Save reference for the new link request 335 336 AJAX.xhr = $.get(url, params, AJAX.responseHandler); 337 var state = { 338 url: href 339 }; 340 341 if (previousLinkAborted) {
342 // hack: there is already an aborted entry on stack 343 // so just modify the aborted one 344 history.replaceState(state, null, href); 345 } else { 346 history.pushState(state, null, href); 347 } 348 } else { 349 /** 350 * Manually fire the onsubmit event for the form, if any. 351 * The event was saved in the jQuery data object by an onload 352 * handler defined below. Workaround for bug #3583316 353 */ 354 var onsubmit = $(this).data('onsubmit'); // Submit the request if there is no onsubmit handler 355 // or if it returns a value that evaluates to true 356 357 if (typeof onsubmit !== 'function' || onsubmit.apply(this, [event])) { 358 AJAX.active = true; 359 AJAX.$msgbox = Functions.ajaxShowMessage(); 360 361 if ($(this).attr('id') === 'login_form') { 362 $.post(url, params, AJAX.loginResponseHandler); 363 } else { 364 $.post(url, params, AJAX.responseHandler); 365 } 366 } 367 } 368 }, 369 370 /** 371 * Response handler to handle login request from login modal after session expiration 372 * 373 * To refer to self use 'AJAX', instead of 'this' as this function 374 * is called in the jQuery context. 375 * 376 * @param {object} data Event data 377 * 378 * @return {void} 379 */ 380 loginResponseHandler: function (data) { 381 if (typeof data === 'undefined' || data === null) { 382 return; 383 } 384 385 Functions.ajaxRemoveMessage(AJAX.$msgbox); 386 CommonParams.set('token', data.new_token); 387 AJAX.scriptHandler.load([]); 388 389 if (data.displayMessage) { 390 $('#page_content').prepend(data.displayMessage); 391 Functions.highlightSql($('#page_content')); 392 } 393 394 $('#pma_errors').remove(); 395 var msg = ''; 396 397 if (data.errSubmitMsg) { 398 msg = data.errSubmitMsg; 399 } 400 401 if (data.errors) { 402 $('<div></div>', { 403 id: 'pma_errors', 404 class: 'clearfloat d-print-none' 405 }).insertAfter('#selflink').append(data.errors); // bind for php error reporting forms (bottom) 406 407 $('#pma_ignore_errors_bottom').on('click', function (e) { 408 e.preventDefault(); 409 Functions.ignorePhpErrors(); 410 }); 411 $('#pma_ignore_all_errors_bottom').on('click', function (e) { 412 e.preventDefault(); 413 Functions.ignorePhpErrors(false); 414 }); // In case of 'sendErrorReport'='always' 415 // submit the hidden error reporting form. 416 417 if (data.sendErrorAlways === '1' && data.stopErrorReportLoop !== '1') { 418 $('#pma_report_errors_form').trigger('submit'); 419 Functions.ajaxShowMessage(Messages.phpErrorsBeingSubmitted, false); 420 $('html, body').animate({ 421 scrollTop: $(document).height() 422 }, 'slow'); 423 } else if (data.promptPhpErrors) { 424 // otherwise just prompt user if it is set so. 425 msg = msg + Messages.phpErrorsFound; // scroll to bottom where all the errors are displayed. 426 427 $('html, body').animate({ 428 scrollTop: $(document).height() 429 }, 'slow'); 430 } 431 } 432 433 Functions.ajaxShowMessage(msg, false); // bind for php error reporting forms (popup) 434 435 $('#pma_ignore_errors_popup').on('click', function () { 436 Functions.ignorePhpErrors(); 437 }); 438 $('#pma_ignore_all_errors_popup').on('click', function () { 439 Functions.ignorePhpErrors(false); 440 }); 441 442 if (typeof data.success !== 'undefined' && data.success) { 443 // reload page if user trying to login has changed 444 if (CommonParams.get('user') !== data.params.user) { 445 window.location = 'index.php'; 446 Functions.ajaxShowMessage(Messages.strLoading, false); 447 AJAX.active = false; 448 AJAX.xhr = null; 449 return; 450 } // remove the login modal if the login is successful otherwise show error. 451 452 453 if (typeof data.logged_in !== 'undefined' && data.logged_in === 1) { 454 if ($('#modalOverlay').length) { 455 $('#modalOverlay').remove(); 456 } 457 458 $('fieldset.disabled_for_expiration').removeAttr('disabled').removeClass('disabled_for_expiration'); 459 AJAX.fireTeardown('functions.js'); 460 AJAX.fireOnload('functions.js'); 461 } 462 463 if (typeof data.new_token !== 'undefined') { 464 $('input[name=token]').val(data.new_token); 465 } 466 } else if (typeof data.logged_in !== 'undefined' && data.logged_in === 0) { 467 $('#modalOverlay').replaceWith(data.error); 468 } else { 469 Functions.ajaxShowMessage(data.error, false); 470 AJAX.active = false;
471 AJAX.xhr = null; 472 Functions.handleRedirectAndReload(data); 473 474 if (data.fieldWithError) { 475 $(':input.error').removeClass('error'); 476 $('#' + data.fieldWithError).addClass('error'); 477 } 478 } 479 }, 480 481 /** 482 * Called after the request that was initiated by this.requestHandler() 483 * has completed successfully or with a caught error. For completely 484 * failed requests or requests with uncaught errors, see the .ajaxError 485 * handler at the bottom of this file. 486 * 487 * To refer to self use 'AJAX', instead of 'this' as this function 488 * is called in the jQuery context. 489 * 490 * @param {object} data Event data 491 * 492 * @return {void} 493 */ 494 responseHandler: function (data) { 495 if (typeof data === 'undefined' || data === null) { 496 return; 497 } 498 499 if (typeof data.success !== 'undefined' && data.success) { 500 $('html, body').animate({ 501 scrollTop: 0 502 }, 'fast'); 503 Functions.ajaxRemoveMessage(AJAX.$msgbox); 504 505 if (data.redirect) { 506 Functions.ajaxShowMessage(data.redirect, false); 507 AJAX.active = false; 508 AJAX.xhr = null; 509 return; 510 } 511 512 AJAX.scriptHandler.reset(function () { 513 if (data.reloadNavigation) { 514 Navigation.reload(); 515 } 516 517 if (data.title) { 518 $('title').replaceWith(data.title); 519 } 520 521 if (data.menu) { 522 var state = { 523 url: data.selflink, 524 menu: data.menu 525 }; 526 history.replaceState(state, null); 527 AJAX.handleMenu.replace(data.menu); 528 } 529 530 if (data.disableNaviSettings) { 531 Navigation.disableSettings(); 532 } else { 533 Navigation.ensureSettings(data.selflink); 534 } // Remove all containers that may have 535 // been added outside of #page_content 536 537 538 $('body').children().not('#pma_navigation').not('#floating_menubar').not('#page_nav_icons').not('#page_content').not('#selflink').not('#pma_header').not('#pma_footer').not('#pma_demo').not('#pma_console_container').not('#prefs_autoload').remove(); // Replace #page_content with new content 539 540 if (data.message && data.message.length > 0) { 541 $('#page_content').replaceWith('<div id=\'page_content\'>' + data.message + '</div>'); 542 Functions.highlightSql($('#page_content')); 543 Functions.checkNumberOfFields(); 544 } 545 546 if (data.selflink) { 547 var source = data.selflink.split('?')[0]; // Check for faulty links 548 549 var $selflinkReplace = { 550 'index.php?route=/import': 'index.php?route=/table/sql', 551 'index.php?route=/table/chart': 'index.php?route=/sql', 552 'index.php?route=/table/gis-visualization': 'index.php?route=/sql' 553 }; 554 555 if ($selflinkReplace[source]) { 556 var replacement = $selflinkReplace[source]; 557 data.selflink = data.selflink.replace(source, replacement); 558 } 559 560 $('#selflink').find('> a').attr('href', data.selflink); 561 } 562 563 if (data.params) { 564 CommonParams.setAll(data.params); 565 } 566 567 if (data.scripts) { 568 AJAX.scriptHandler.load(data.scripts); 569 } 570 571 if (data.displayMessage) { 572 $('#page_content').prepend(data.displayMessage); 573 Functions.highlightSql($('#page_content')); 574 } 575 576 $('#pma_errors').remove(); 577 var msg = ''; 578 579 if (data.errSubmitMsg) { 580 msg = data.errSubmitMsg; 581 } 582 583 if (data.errors) { 584 $('<div></div>', { 585 id: 'pma_errors', 586 class: 'clearfloat d-print-none' 587 }).insertAfter('#selflink').append(data.errors); // bind for php error reporting forms (bottom) 588 589 $('#pma_ignore_errors_bottom').on('click', function (e) { 590 e.preventDefault(); 591 Functions.ignorePhpErrors(); 592 }); 593 $('#pma_ignore_all_errors_bottom').on('click', function (e) { 594 e.preventDefault(); 595 Functions.ignorePhpErrors(false); 596 }); // In case of 'sendErrorReport'='always' 597 // submit the hidden error reporting form. 598 599 if (data.sendErrorAlways === '1' && data.stopErrorReportLoop !== '1') { 600 $('#pma_report_errors_form').trigger('submit'); 601 Functions.ajaxShowMessage(Messages.phpErrorsBeingSubmitted, false); 602 $('html, body').animate({ 603 scrollTop: $(document).height() 604 }, 'slow'); 605 } else if (data.promptPhpErrors) { 606 // otherwise just prompt user if it is set so. 607 msg = msg + Messages.phpErrorsFound; // scroll to bottom where all the errors are displayed. 608 609 $('html, body').animate({ 610 scrollTop: $(document).height() 611 }, 'slow'); 612 } 613 } 614 615 Functions.ajaxShowMessage(msg, false); // bind for php error reporting forms (popup) 616 617 $('#pma_ignore_errors_popup').on('click', function () { 618 Functions.ignorePhpErrors(); 619 }); 620 $('#pma_ignore_all_errors_popup').on('click', function () { 621 Functions.ignorePhpErrors(false); 622 }); 623 624 if (typeof AJAX.callback === 'function') { 625 AJAX.callback.call(); 626 } 627 628 AJAX.callback = function () {}; 629 }); 630 } else { 631 Functions.ajaxShowMessage(data.error, false); 632 Functions.ajaxRemoveMessage(AJAX.$msgbox); 633 var $ajaxError = $('<div></div>'); 634 $ajaxError.attr({ 635 'id': 'ajaxError' 636 }); 637 $('#page_content').append($ajaxError); 638 $ajaxError.html(data.error); 639 $('html, body').animate({ 640 scrollTop: $(document).height() 641 }, 200); 642 AJAX.active = false;
643 AJAX.xhr = null; 644 Functions.handleRedirectAndReload(data); 645 646 if (data.fieldWithError) { 647 $(':input.error').removeClass('error'); 648 $('#' + data.fieldWithError).addClass('error'); 649 } 650 } 651 }, 652 653 /** 654 * This object is in charge of downloading scripts, 655 * keeping track of what's downloaded and firing 656 * the onload event for them when the page is ready. 657 */ 658 scriptHandler: { 659 /** 660 * @var {string[]} scripts The list of files already downloaded 661 */ 662 scripts: [], 663 664 /** 665 * @var {string} scriptsVersion version of phpMyAdmin from which the 666 * scripts have been loaded 667 */ 668 scriptsVersion: null, 669 670 /** 671 * @var {string[]} scriptsToBeLoaded The list of files that 672 * need to be downloaded 673 */ 674 scriptsToBeLoaded: [], 675 676 /** 677 * @var {string[]} scriptsToBeFired The list of files for which 678 * to fire the onload and unload events 679 */ 680 scriptsToBeFired: [], 681 scriptsCompleted: false, 682 683 /** 684 * Records that a file has been downloaded 685 * 686 * @param {string} file The filename 687 * @param {string} fire Whether this file will be registering 688 * onload/teardown events 689 * 690 * @return {self} For chaining 691 */ 692 add: function (file, fire) { 693 this.scripts.push(file); 694 695 if (fire) { 696 // Record whether to fire any events for the file 697 // This is necessary to correctly tear down the initial page 698 this.scriptsToBeFired.push(file); 699 } 700 701 return this; 702 }, 703 704 /** 705 * Download a list of js files in one request 706 * 707 * @param {string[]} files An array of filenames and flags 708 * @param {Function} callback 709 * 710 * @return {void} 711 */ 712 load: function (files, callback) { 713 var self = this; 714 var i; // Clear loaded scripts if they are from another version of phpMyAdmin. 715 // Depends on common params being set before loading scripts in responseHandler 716 717 if (self.scriptsVersion === null) { 718 self.scriptsVersion = CommonParams.get('version'); 719 } else if (self.scriptsVersion !== CommonParams.get('version')) { 720 self.scripts = []; 721 self.scriptsVersion = CommonParams.get('version'); 722 } 723 724 self.scriptsCompleted = false; 725 self.scriptsToBeFired = []; // We need to first complete list of files to load 726 // as next loop will directly fire requests to load them 727 // and that triggers removal of them from 728 // self.scriptsToBeLoaded 729 730 for (i in files) { 731 self.scriptsToBeLoaded.push(files[i].name); 732 733 if (files[i].fire) { 734 self.scriptsToBeFired.push(files[i].name); 735 } 736 } 737 738 for (i in files) { 739 var script = files[i].name; // Only for scripts that we don't already have 740 741 if ($.inArray(script, self.scripts) === -1) { 742 this.add(script); 743 this.appendScript(script, callback); 744 } else { 745 self.done(script, callback); 746 } 747 } // Trigger callback if there is nothing else to load 748 749 750 self.done(null, callback); 751 }, 752 753 /** 754 * Called whenever all files are loaded 755 * 756 * @param {string} script 757 * @param {Function?} callback 758 * 759 * @return {void} 760 */ 761 done: function (script, callback) { 762 if ($.inArray(script, this.scriptsToBeFired)) { 763 AJAX.fireOnload(script); 764 } 765 766 if ($.inArray(script, this.scriptsToBeLoaded)) { 767 this.scriptsToBeLoaded.splice($.inArray(script, this.scriptsToBeLoaded), 1); 768 } 769 770 if (script === null) { 771 this.scriptsCompleted = true; 772 } 773 /* We need to wait for last signal (with null) or last script load */ 774 775 776 AJAX.active = this.scriptsToBeLoaded.length > 0 || !this.scriptsCompleted; 777 /* Run callback on last script */ 778 779 if (!AJAX.active && typeof callback === 'function') { 780 callback(); 781 } 782 }, 783 784 /** 785 * Appends a script element to the head to load the scripts 786 * 787 * @param {string} name 788 * @param {Function} callback 789 * 790 * @return {void} 791 */ 792 appendScript: function (name, callback) { 793 var head = document.head || document.getElementsByTagName('head')[0]; 794 var script = document.createElement('script'); 795 var self = this; 796 script.type = 'text/javascript'; 797 var file = name.indexOf('vendor/') !== -1 ? name : 'dist/' + name; 798 script.src = 'js/' + file + '?' + 'v=' + encodeURIComponent(CommonParams.get('version')); 799 script.async = false;
800 801 script.onload = function () { 802 self.done(name, callback); 803 }; 804 805 head.appendChild(script); 806 }, 807 808 /** 809 * Fires all the teardown event handlers for the current page 810 * and rebinds all forms and links to the request handler 811 * 812 * @param {Function} callback The callback to call after resetting 813 * 814 * @return {void} 815 */ 816 reset: function (callback) { 817 for (var i in this.scriptsToBeFired) { 818 AJAX.fireTeardown(this.scriptsToBeFired[i]); 819 } 820 821 this.scriptsToBeFired = []; 822 /** 823 * Re-attach a generic event handler to clicks 824 * on pages and submissions of forms 825 */ 826 827 $(document).off('click', 'a').on('click', 'a', AJAX.requestHandler); 828 $(document).off('submit', 'form').on('submit', 'form', AJAX.requestHandler); 829 callback(); 830 } 831 } 832}; 833/** 834 * Here we register a function that will remove the onsubmit event from all 835 * forms that will be handled by the generic page loader. We then save this 836 * event handler in the "jQuery data", so that we can fire it up later in 837 * AJAX.requestHandler(). 838 * 839 * See bug #3583316 840 */ 841 842AJAX.registerOnload('functions.js', function () { 843 // Registering the onload event for functions.js 844 // ensures that it will be fired for all pages 845 $('form').not('.ajax').not('.disableAjax').each(function () { 846 if ($(this).attr('onsubmit')) { 847 $(this).data('onsubmit', this.onsubmit).attr('onsubmit', ''); 848 } 849 }); 850 var $pageContent = $('#page_content'); 851 /** 852 * Workaround for passing submit button name,value on ajax form submit 853 * by appending hidden element with submit button name and value. 854 */ 855 856 $pageContent.on('click', 'form input[type=submit]', function () { 857 var buttonName = $(this).attr('name'); 858 859 if (typeof buttonName === 'undefined') { 860 return; 861 } 862 863 $(this).closest('form').append($('<input>', { 864 'type': 'hidden', 865 'name': buttonName, 866 'value': $(this).val() 867 })); 868 }); 869 /** 870 * Attach event listener to events when user modify visible 871 * Input,Textarea and select fields to make changes in forms 872 */ 873 874 $pageContent.on('keyup change', 'form.lock-page textarea, ' + 'form.lock-page input[type="text"], ' + 'form.lock-page input[type="number"], ' + 'form.lock-page select', { 875 value: 1 876 }, AJAX.lockPageHandler); 877 $pageContent.on('change', 'form.lock-page input[type="checkbox"], ' + 'form.lock-page input[type="radio"]', { 878 value: 2 879 }, AJAX.lockPageHandler); 880 /** 881 * Reset lock when lock-page form reset event is fired 882 * Note: reset does not bubble in all browser so attach to 883 * form directly. 884 */ 885 886 $('form.lock-page').on('reset', function () { 887 AJAX.resetLock(); 888 }); 889}); 890/** 891 * Page load event handler 892 */ 893 894$(function () { 895 var menuContent = $('<div></div>').append($('#server-breadcrumb').clone()).append($('#topmenucontainer').clone()).html(); // set initial state reload 896 897 var initState = 'state' in window.history && window.history.state !== null; 898 var initURL = $('#selflink').find('> a').attr('href') || location.href; 899 var state = { 900 url: initURL, 901 menu: menuContent 902 }; 903 history.replaceState(state, null); 904 $(window).on('popstate', function (event) { 905 var initPop = !initState && location.href === initURL; 906 initState = true; // check if popstate fired on first page itself 907 908 if (initPop) { 909 return; 910 } 911 912 var state = event.originalEvent.state; 913 914 if (state && state.menu) { 915 AJAX.$msgbox = Functions.ajaxShowMessage(); 916 var params = 'ajax_request=true' + CommonParams.get('arg_separator') + 'ajax_page_request=true'; 917 var url = state.url || location.href; 918 $.get(url, params, AJAX.responseHandler); // TODO: Check if sometimes menu is not retrieved from server, 919 // Not sure but it seems menu was missing only for printview which 920 // been removed lately, so if it's right some dead menu checks/fallbacks 921 // may need to be removed from this file and Header.php 922 // AJAX.handleMenu.replace(event.originalEvent.state.menu); 923 } 924 }); 925}); 926/** 927 * Attach a generic event handler to clicks 928 * on pages and submissions of forms 929 */ 930 931$(document).on('click', 'a', AJAX.requestHandler); 932$(document).on('submit', 'form', AJAX.requestHandler); 933/** 934 * Gracefully handle fatal server errors 935 * (e.g: 500 - Internal server error) 936 */ 937 938$(document).on('ajaxError', function (event, request) { 939 if (AJAX.debug) { 940 // eslint-disable-next-line no-console 941 console.log('AJAX error: status=' + request.status + ', text=' + request.statusText); 942 } // Don't handle aborted requests 943 944 945 if (request.status !== 0 || request.statusText !== 'abort') { 946 var details = ''; 947 var state = request.state(); 948 949 if (request.status !== 0) { 950 details += '<div>' + Functions.escapeHtml(Functions.sprintf(Messages.strErrorCode, request.status)) + '</div>'; 951 } 952 953 details += '<div>' + Functions.escapeHtml(Functions.sprintf(Messages.strErrorText, request.statusText + ' (' + state
953+ ')')) + '</div>'; 954 955 if (state === 'rejected' || state === 'timeout') { 956 details += '<div>' + Functions.escapeHtml(Messages.strErrorConnection) + '</div>'; 957 } 958 959 Functions.ajaxShowMessage('<div class="alert alert-danger" role="alert">' + Messages.strErrorProcessingRequest + details + '</div>', false); 960 AJAX.active = false; 961 AJAX.xhr = null; 962 } 963});
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.