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