~ubuntu-branches/ubuntu/trusty/phpmyadmin/trusty

« back to all changes in this revision

Viewing changes to js/ajax.js

  • Committer: Package Import Robot
  • Author(s): Thijs Kinkhorst
  • Date: 2013-08-04 13:24:37 UTC
  • mfrom: (1.2.44)
  • Revision ID: package-import@ubuntu.com-20130804132437-jznw8efwy4hr1nms
Tags: 4:4.0.5-1
* New upstream release.
  - Fixes security issue PMASA-2013-10.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
var AJAX={active:false,source:null,_callback:function(){},_debug:false,$msgbox:null,hash:function(c){c+="";var a=c.length,d=0,b=0;for(;b<a;++b){d+=c.charCodeAt(b);d+=(d<<10);d^=(d>>6)}d+=(d<<3);d^=(d>>11);d+=(d<<15);return Math.abs(d)},registerOnload:function(b,c){var a="onload_"+AJAX.hash(b);$(document).bind(a,c);this._debug&&console.log("Registered event "+a+" for file "+b);return this},registerTeardown:function(b,c){var a="teardown_"+AJAX.hash(b);$(document).bind(a,c);this._debug&&console.log("Registered event "+a+" for file "+b);return this},fireOnload:function(b){var a="onload_"+AJAX.hash(b);$(document).trigger(a);this._debug&&console.log("Fired event "+a+" for file "+b)},fireTeardown:function(b){var a="teardown_"+AJAX.hash(b);$(document).triggerHandler(a);this._debug&&console.log("Fired event "+a+" for file "+b)},requestHandler:function(d){var b=$(this).attr("href");if(d.shiftKey||d.ctrlKey){return true}else{if($(this).attr("target")){return true}else{if($(this).hasClass("ajax")||$(this).hasClass("disableAjax")){return true}else{if(b&&b.match(/^#/)){return true}else{if(b&&b.match(/^mailto/)){return true}else{if($(this).hasClass("ui-datepicker-next")||$(this).hasClass("ui-datepicker-prev")){return true}}}}}}if(typeof d!="undefined"){d.preventDefault();d.stopImmediatePropagation()}if(AJAX.active==true){return false}AJAX.source=$(this);$("html, body").animate({scrollTop:0},"fast");var e=!!b||false;var c=e?b:$(this).attr("action");var f="ajax_request=true&ajax_page_request=true";if(!e){f+="&"+$(this).serialize()}f+=AJAX.cache.menus.getRequestParam();AJAX._debug&&console.log("Loading: "+c);if(e){AJAX.active=true;AJAX.$msgbox=PMA_ajaxShowMessage();$.get(c,f,AJAX.responseHandler)}else{var a=$(this).data("onsubmit");if(typeof a!=="function"||a.apply(this,[d])){AJAX.active=true;AJAX.$msgbox=PMA_ajaxShowMessage();$.post(c,f,AJAX.responseHandler)}}},responseHandler:function(a){if(a.success){$table_clone=false;PMA_ajaxRemoveMessage(AJAX.$msgbox);if(a._redirect){PMA_ajaxShowMessage(a._redirect,false);AJAX.active=false;return}AJAX.scriptHandler.reset(function(){if(a._reloadNavigation){PMA_reloadNavigation()}if(a._reloadQuerywindow){var b=a._reloadQuerywindow;PMA_querywindow.reload(b.db,b.table,b.sql_query)}if(a._focusQuerywindow){PMA_querywindow.focus(a._focusQuerywindow)}if(a._title){$("title").replaceWith(a._title)}if(a._menu){AJAX.cache.menus.replace(a._menu);AJAX.cache.menus.add(a._menuHash,a._menu)}else{if(a._menuHash){AJAX.cache.menus.replace(AJAX.cache.menus.get(a._menuHash))}}$("body").children().not("#pma_navigation").not("#floating_menubar").not("#goto_pagetop").not("#page_content").not("#selflink").not("#session_debug").remove();if(a.message&&a.message.length>0){$("#page_content").replaceWith("<div id='page_content'>"+a.message+"</div>")}if(a._selflink){$("#selflink > a").attr("href",a._selflink)}if(a._scripts){AJAX.scriptHandler.load(a._scripts)}if(a._selflink&&a._scripts&&a._menuHash&&a._params){AJAX.cache.add(a._selflink,a._scripts,a._menuHash,a._params,AJAX.source.attr("rel"))}if(a._params){PMA_commonParams.setAll(a._params)}if(a._displayMessage){$("#page_content").prepend(a._displayMessage)}$("#pma_errors").remove();if(a._errors){$("<div/>",{id:"pma_errors"}).insertAfter("#selflink").append(a._errors)}if(typeof AJAX._callback==="function"){AJAX._callback.call()}AJAX._callback=function(){}})}else{PMA_ajaxShowMessage(a.error,false);AJAX.active=false}},scriptHandler:{_scripts:[],_scriptsToBeLoaded:[],_scriptsToBeFired:[],add:function(a,b){this._scripts.push(a);if(b){this._scriptsToBeFired.push(a)}return this},load:function(g){var b=this;b._scriptsToBeLoaded=[];b._scriptsToBeFired=[];for(var d in g){b._scriptsToBeLoaded.push(g[d].name);if(g[d].fire){b._scriptsToBeFired.push(g[d].name)}}var f=[];var e=false;for(var c in b._scriptsToBeLoaded){var a=b._scriptsToBeLoaded[c];if($.inArray(a,b._scripts)==-1){e=true;this.add(a);f.push("scripts[]="+a)}}if(e){$.ajax({url:"js/get_scripts.js.php?"+f.join("&"),cache:true,success:function(){b.done()},dataType:"script"})}else{b.done()}},done:function(){for(var a in this._scriptsToBeFired){AJAX.fireOnload(this._scriptsToBeFired[a])}AJAX.active=false},reset:function(b){for(var a in this._scriptsToBeFired){AJAX.fireTeardown(this._scriptsToBeFired[a])}this._scriptsToBeFired=[];$("a").die("click").live("click",AJAX.requestHandler);$("form").die("submit").live("submit",AJAX.requestHandler);AJAX.cache.update();b()}}};AJAX.registerOnload("functions.js",function(){$("form").not(".ajax").not(".disableAjax").each(function(){if($(this).attr("onsubmit")){$(this).data("onsubmit",this.onsubmit).attr("onsubmit","")}})});AJAX.cache={MAX:6,primer:{},pages:[],current:0,add:function(d,b,f,e,a){if(this.pages.length>AJAX.cache.MAX){for(var c=0;c<this.pages.length-this.MAX;c++){delete this.pages[c]}}while(this.current<this.pages.length){this.pages.pop()}if(a==="newpage"||(typeof a==="undefined"&&(typeof this.pages[this.current-1]==="undefined"||this.pages[this.current-1].hash!==d))){this.pages.push({hash:d,content:$("#page_content").html(),scripts:b,selflink:$("#selflink").html(),menu:f,params:e});AJAX.setUrlHash(this.current,d);this.current++}},navigate:function(b){if(typeof this.pages[b]==="undefined"){PMA_ajaxShowMessage('<div class="error">'+PMA_messages.strInvalidPage+"</div>",false)}else{AJAX.active=true;var a=this.pages[b];AJAX.scriptHandler.reset(function(){$("#page_content").html(a.content);$("#selflink").html(a.selflink);AJAX.cache.menus.replace(AJAX.cache.menus.get(a.menu));PMA_commonParams.setAll(a.params);AJAX.scriptHandler.load(a.scripts);AJAX.cache.current=++b})}},update:function(){var a=this.pages[this.current-1];if(a){a.content=$("#page_content").html()}},menus:{size:function(c){var b=0,a;for(a in c){if(c.hasOwnProperty(a)){b++}}return b},data:{},add:function(e,d){if(this.size(this.data)>AJAX.cache.MAX){var a,c,f=0;for(var b in this.data){if(this.data[b]){if(!f||this.data[b].timestamp.getTime()<a.getTime()){a=this.data[b].timestamp;c=b;f=1}}}delete this.data[c]}this.data[e]={content:d,timestamp:new Date()}},get:function(a){if(this.data[a]){return this.data[a].content}else{return""}},getRequestParam:function(){var d="";var a=[];for(var b in this.data){a.push(b)}var c=a.join("-");if(c){d="&menuHashes="+c}return d},replace:function(a){$("#floating_menubar").html(a).children().first().remove();$("#topmenu").menuResizer(PMA_mainMenuResizerCallback)}}};AJAX.setUrlHash=(function(f,d){var c=false;var g="";var a=true;function e(){if(f.browser.mozilla){$("head > link[href=favicon\\.ico]").appendTo("head")}}function b(h,i){a=false;if(c){d.location.hash="PMAURL-"+h+":"+i;e()}else{g="PMAURL-"+h+":"+i}}if(d.location.hash.substring(0,8)=="#PMAURL-"){d.location=d.location.hash.substring(d.location.hash.indexOf(":")+1)}else{f(function(){if(g!=""){d.location.hash=g;g="";e()}c=true})}f(function(){f(d).hashchange(function(){if(a===false){a=true}else{if(/^#PMAURL-\d+:/.test(d.location.hash)){var h=d.location.hash.substring(8,d.location.hash.indexOf(":"));AJAX.cache.navigate(h)}}})});return b})(jQuery,window);$(function(){if(AJAX.cache.primer.url){AJAX.cache.menus.add(AJAX.cache.primer.menuHash,$("<div></div>").append("<div></div>").append($("#serverinfo").clone()).append($("#topmenucontainer").clone()).html())}$(function(){if(AJAX.cache.primer.url){AJAX.cache.add(AJAX.cache.primer.url,AJAX.cache.primer.scripts,AJAX.cache.primer.menuHash)}})});$("a").live("click",AJAX.requestHandler);$("form").live("submit",AJAX.requestHandler);$(document).ajaxError(function(d,c,b){if(c.status!==0){var e=$.sprintf(PMA_messages.strErrorCode,c.status);var a=$.sprintf(PMA_messages.strErrorText,c.statusText);PMA_ajaxShowMessage('<div class="error">'+PMA_messages.strErrorProcessingRequest+"<div>"+e+"</div><div>"+a+"</div></div>",false);AJAX.active=false}});
 
 
b'\\ No newline at end of file'
 
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 function Callback to execute after a successful request
 
17
     *               Used by PMA_commonFunctions from common.js
 
18
     */
 
19
    _callback: function () {},
 
20
    /**
 
21
     * @var bool _debug Makes noise in your Firebug console
 
22
     */
 
23
    _debug: false,
 
24
    /**
 
25
     * @var object $msgbox A reference to a jQuery object that links to a message
 
26
     *                     box that is generated by PMA_ajaxShowMessage()
 
27
     */
 
28
    $msgbox: null,
 
29
    /**
 
30
     * Given the filename of a script, returns a hash to be
 
31
     * used to refer to all the events registered for the file
 
32
     *
 
33
     * @param string key The filename for which to get the event name
 
34
     *
 
35
     * @return int
 
36
     */
 
37
    hash: function (key){
 
38
        /* http://burtleburtle.net/bob/hash/doobs.html#one */
 
39
        key += "";
 
40
        var len = key.length, hash=0, i=0;
 
41
        for (; i<len; ++i) {
 
42
            hash += key.charCodeAt(i);
 
43
            hash += (hash << 10);
 
44
            hash ^= (hash >> 6);
 
45
        }
 
46
        hash += (hash << 3);
 
47
        hash ^= (hash >> 11);
 
48
        hash += (hash << 15);
 
49
        return Math.abs(hash);
 
50
    },
 
51
    /**
 
52
     * Registers an onload event for a file
 
53
     *
 
54
     * @param string   file The filename for which to register the event
 
55
     * @param function func The function to execute when the page is ready
 
56
     *
 
57
     * @return self For chaining
 
58
     */
 
59
    registerOnload: function (file, func) {
 
60
        var eventName = 'onload_' + AJAX.hash(file);
 
61
        $(document).bind(eventName, func);
 
62
        this._debug && console.log(
 
63
            // no need to translate
 
64
            "Registered event " + eventName + " for file " + file
 
65
        );
 
66
        return this;
 
67
    },
 
68
    /**
 
69
     * Registers a teardown event for a file. This is useful to execute functions
 
70
     * that unbind events for page elements that are about to be removed.
 
71
     *
 
72
     * @param string   file The filename for which to register the event
 
73
     * @param function func The function to execute when
 
74
     *                      the page is about to be torn down
 
75
     *
 
76
     * @return self For chaining
 
77
     */
 
78
    registerTeardown: function (file, func) {
 
79
        var eventName = 'teardown_' + AJAX.hash(file);
 
80
        $(document).bind(eventName, func);
 
81
        this._debug && console.log(
 
82
            // no need to translate
 
83
            "Registered event " + eventName + " for file " + file
 
84
        );
 
85
        return this;
 
86
    },
 
87
    /**
 
88
     * Called when a page has finished loading, once for every
 
89
     * file that registered to the onload event of that file.
 
90
     *
 
91
     * @param string file The filename for which to fire the event
 
92
     *
 
93
     * @return void
 
94
     */
 
95
    fireOnload: function (file) {
 
96
        var eventName = 'onload_' + AJAX.hash(file);
 
97
        $(document).trigger(eventName);
 
98
        this._debug && console.log(
 
99
            // no need to translate
 
100
            "Fired event " + eventName + " for file " + file
 
101
        );
 
102
    },
 
103
    /**
 
104
     * Called just before a page is torn down, once for every
 
105
     * file that registered to the teardown event of that file.
 
106
     *
 
107
     * @param string file The filename for which to fire the event
 
108
     *
 
109
     * @return void
 
110
     */
 
111
    fireTeardown: function (file) {
 
112
        var eventName = 'teardown_' + AJAX.hash(file);
 
113
        $(document).triggerHandler(eventName);
 
114
        this._debug && console.log(
 
115
            // no need to translate
 
116
            "Fired event " + eventName + " for file " + file
 
117
        );
 
118
    },
 
119
    /**
 
120
     * Event handler for clicks on links and form submissions
 
121
     *
 
122
     * @param object e Event data
 
123
     *
 
124
     * @return void
 
125
     */
 
126
    requestHandler: function (event) {
 
127
        // In some cases we don't want to handle the request here and either
 
128
        // leave the browser deal with it natively (e.g: file download)
 
129
        // or leave an existing ajax event handler present elsewhere deal with it
 
130
        var href = $(this).attr('href');
 
131
        if (event.shiftKey || event.ctrlKey) {
 
132
            return true;
 
133
        } else if ($(this).attr('target')) {
 
134
            return true;
 
135
        } else if ($(this).hasClass('ajax') || $(this).hasClass('disableAjax')) {
 
136
            return true;
 
137
        } else if (href && href.match(/^#/)) {
 
138
            return true;
 
139
        } else if (href && href.match(/^mailto/)) {
 
140
            return true;
 
141
        } else if ($(this).hasClass('ui-datepicker-next') || 
 
142
                   $(this).hasClass('ui-datepicker-prev')
 
143
                  ) {
 
144
            return true;
 
145
        }
 
146
 
 
147
        if (typeof event != 'undefined') {
 
148
            event.preventDefault();
 
149
            event.stopImmediatePropagation();
 
150
        }
 
151
        if (AJAX.active == true) {
 
152
            // Silently bail out, there is already a request in progress.
 
153
            // TODO: save a reference to the request and cancel the old request
 
154
            // when the user requests something else. Something like this is
 
155
            // already implemented in the PMA_fastFilter object in navigation.js
 
156
            return false;
 
157
        }
 
158
 
 
159
        AJAX.source = $(this);
 
160
 
 
161
        $('html, body').animate({scrollTop: 0}, 'fast');
 
162
 
 
163
        var isLink = !! href || false;
 
164
        var url = isLink ? href : $(this).attr('action');
 
165
        var params = 'ajax_request=true&ajax_page_request=true';
 
166
        if (! isLink) {
 
167
            params += '&' + $(this).serialize();
 
168
        }
 
169
        // Add a list of menu hashes that we have in the cache to the request
 
170
        params += AJAX.cache.menus.getRequestParam();
 
171
 
 
172
        AJAX._debug && console.log("Loading: " + url); // no need to translate
 
173
 
 
174
        if (isLink) {
 
175
            AJAX.active = true;
 
176
            AJAX.$msgbox = PMA_ajaxShowMessage();
 
177
            $.get(url, params, AJAX.responseHandler);
 
178
        } else {
 
179
            /**
 
180
             * Manually fire the onsubmit event for the form, if any.
 
181
             * The event was saved in the jQuery data object by an onload
 
182
             * handler defined below. Workaround for bug #3583316
 
183
             */
 
184
            var onsubmit = $(this).data('onsubmit');
 
185
            // Submit the request if there is no onsubmit handler
 
186
            // or if it returns a value that evaluates to true
 
187
            if (typeof onsubmit !== 'function' || onsubmit.apply(this, [event])) {
 
188
                AJAX.active = true;
 
189
                AJAX.$msgbox = PMA_ajaxShowMessage();
 
190
                $.post(url, params, AJAX.responseHandler);
 
191
            }
 
192
        }
 
193
    },
 
194
    /**
 
195
     * Called after the request that was initiated by this.requestHandler()
 
196
     * has completed successfully or with a caught error. For completely
 
197
     * failed requests or requests with uncaught errors, see the .ajaxError
 
198
     * handler at the bottom of this file.
 
199
     *
 
200
     * To refer to self use 'AJAX', instead of 'this' as this function
 
201
     * is called in the jQuery context.
 
202
     *
 
203
     * @param object e Event data
 
204
     *
 
205
     * @return void
 
206
     */
 
207
    responseHandler: function (data) {
 
208
        if (data.success) {
 
209
            $table_clone = false;
 
210
            PMA_ajaxRemoveMessage(AJAX.$msgbox);
 
211
 
 
212
            if (data._redirect) {
 
213
                PMA_ajaxShowMessage(data._redirect, false);
 
214
                AJAX.active = false;
 
215
                return;
 
216
            }
 
217
 
 
218
            AJAX.scriptHandler.reset(function () {
 
219
                if (data._reloadNavigation) {
 
220
                    PMA_reloadNavigation();
 
221
                }
 
222
                if (data._reloadQuerywindow) {
 
223
                    var params = data._reloadQuerywindow;
 
224
                    PMA_querywindow.reload(
 
225
                        params.db,
 
226
                        params.table,
 
227
                        params.sql_query
 
228
                    );
 
229
                }
 
230
                if (data._focusQuerywindow) {
 
231
                    PMA_querywindow.focus(
 
232
                        data._focusQuerywindow
 
233
                    );
 
234
                }
 
235
                if (data._title) {
 
236
                    $('title').replaceWith(data._title);
 
237
                }
 
238
                if (data._menu) {
 
239
                    AJAX.cache.menus.replace(data._menu);
 
240
                    AJAX.cache.menus.add(data._menuHash, data._menu);
 
241
                } else if (data._menuHash) {
 
242
                    AJAX.cache.menus.replace(AJAX.cache.menus.get(data._menuHash));
 
243
                }
 
244
 
 
245
                // Remove all containers that may have
 
246
                // been added outside of #page_content
 
247
                $('body').children()
 
248
                    .not('#pma_navigation')
 
249
                    .not('#floating_menubar')
 
250
                    .not('#goto_pagetop')
 
251
                    .not('#page_content')
 
252
                    .not('#selflink')
 
253
                    .not('#session_debug')
 
254
                    .remove();
 
255
                // Replace #page_content with new content
 
256
                if (data.message && data.message.length > 0) {
 
257
                    $('#page_content').replaceWith(
 
258
                        "<div id='page_content'>" + data.message + "</div>"
 
259
                    );
 
260
                }
 
261
 
 
262
                if (data._selflink) {
 
263
                    $('#selflink > a').attr('href', data._selflink);
 
264
                }
 
265
                if (data._scripts) {
 
266
                    AJAX.scriptHandler.load(data._scripts);
 
267
                }
 
268
                if (data._selflink && data._scripts && data._menuHash && data._params) {
 
269
                    AJAX.cache.add(
 
270
                        data._selflink,
 
271
                        data._scripts,
 
272
                        data._menuHash,
 
273
                        data._params,
 
274
                        AJAX.source.attr('rel')
 
275
                    );
 
276
                }
 
277
                if (data._params) {
 
278
                    PMA_commonParams.setAll(data._params);
 
279
                }
 
280
                if (data._displayMessage) {
 
281
                    $('#page_content').prepend(data._displayMessage);
 
282
                }
 
283
 
 
284
                $('#pma_errors').remove();
 
285
                if (data._errors) {
 
286
                    $('<div/>', {id:'pma_errors'})
 
287
                        .insertAfter('#selflink')
 
288
                        .append(data._errors);
 
289
                }
 
290
 
 
291
                if (typeof AJAX._callback === 'function') {
 
292
                    AJAX._callback.call();
 
293
                }
 
294
                AJAX._callback = function () {};
 
295
            });
 
296
        } else {
 
297
            PMA_ajaxShowMessage(data.error, false);
 
298
            AJAX.active = false;
 
299
        }
 
300
    },
 
301
    /**
 
302
     * This object is in charge of downloading scripts,
 
303
     * keeping track of what's downloaded and firing
 
304
     * the onload event for them when the page is ready.
 
305
     */
 
306
    scriptHandler: {
 
307
        /**
 
308
         * @var array _scripts The list of files already downloaded
 
309
         */
 
310
        _scripts: [],
 
311
        /**
 
312
         * @var array _scriptsToBeLoaded The list of files that
 
313
         *                               need to be downloaded
 
314
         */
 
315
        _scriptsToBeLoaded: [],
 
316
        /**
 
317
         * @var array _scriptsToBeFired The list of files for which
 
318
         *                              to fire the onload event
 
319
         */
 
320
        _scriptsToBeFired: [],
 
321
        /**
 
322
         * Records that a file has been downloaded
 
323
         *
 
324
         * @param string file The filename
 
325
         * @param string fire Whether this file will be registering
 
326
         *                    onload/teardown events
 
327
         *
 
328
         * @return self For chaining
 
329
         */
 
330
        add: function (file, fire) {
 
331
            this._scripts.push(file);
 
332
            if (fire) {
 
333
                // Record whether to fire any events for the file
 
334
                // This is necessary to correctly tear down the initial page
 
335
                this._scriptsToBeFired.push(file);
 
336
            }
 
337
            return this;
 
338
        },
 
339
        /**
 
340
         * Download a list of js files in one request
 
341
         *
 
342
         * @param array files An array of filenames and flags
 
343
         *
 
344
         * @return void
 
345
         */
 
346
        load: function (files) {
 
347
            var self = this;
 
348
            self._scriptsToBeLoaded = [];
 
349
            self._scriptsToBeFired = [];
 
350
            for (var i in files) {
 
351
                self._scriptsToBeLoaded.push(files[i].name);
 
352
                if (files[i].fire) {
 
353
                    self._scriptsToBeFired.push(files[i].name);
 
354
                }
 
355
            }
 
356
            // Generate a request string
 
357
            var request = [];
 
358
            var needRequest = false;
 
359
            for (var index in self._scriptsToBeLoaded) {
 
360
                var script = self._scriptsToBeLoaded[index];
 
361
                // Only for scripts that we don't already have
 
362
                if ($.inArray(script, self._scripts) == -1) {
 
363
                    needRequest = true;
 
364
                    this.add(script);
 
365
                    request.push("scripts[]=" + script);
 
366
                }
 
367
            }
 
368
            // Download the composite js file, if necessary
 
369
            if (needRequest) {
 
370
                $.ajax({
 
371
                    url: "js/get_scripts.js.php?" + request.join("&"),
 
372
                    cache: true,
 
373
                    success: function () {
 
374
                        self.done();
 
375
                    },
 
376
                    dataType: "script"
 
377
                });
 
378
            } else {
 
379
                self.done();
 
380
            }
 
381
        },
 
382
        /**
 
383
         * Called whenever all files are loaded
 
384
         *
 
385
         * @return void
 
386
         */
 
387
        done: function () {
 
388
            for (var i in this._scriptsToBeFired) {
 
389
                AJAX.fireOnload(this._scriptsToBeFired[i]);
 
390
            }
 
391
            AJAX.active = false;
 
392
        },
 
393
        /**
 
394
         * Fires all the teardown event handlers for the current page
 
395
         * and rebinds all forms and links to the request handler
 
396
         *
 
397
         * @param function callback The callback to call after resetting
 
398
         *
 
399
         * @return void
 
400
         */
 
401
        reset: function (callback) {
 
402
            for (var i in this._scriptsToBeFired) {
 
403
                AJAX.fireTeardown(this._scriptsToBeFired[i]);
 
404
            }
 
405
            this._scriptsToBeFired = [];
 
406
            /**
 
407
             * Re-attach a generic event handler to clicks
 
408
             * on pages and submissions of forms
 
409
             */
 
410
            $('a').die('click').live('click', AJAX.requestHandler);
 
411
            $('form').die('submit').live('submit', AJAX.requestHandler);
 
412
            AJAX.cache.update();
 
413
            callback();
 
414
        }
 
415
    }
 
416
};
 
417
 
 
418
/**
 
419
 * Here we register a function that will remove the onsubmit event from all
 
420
 * forms that will be handled by the generic page loader. We then save this
 
421
 * event handler in the "jQuery data", so that we can fire it up later in
 
422
 * AJAX.requestHandler().
 
423
 *
 
424
 * See bug #3583316
 
425
 */
 
426
AJAX.registerOnload('functions.js', function () {
 
427
    // Registering the onload event for functions.js
 
428
    // ensures that it will be fired for all pages
 
429
    $('form').not('.ajax').not('.disableAjax').each(function () {
 
430
        if ($(this).attr('onsubmit')) {
 
431
            $(this).data('onsubmit', this.onsubmit).attr('onsubmit', '');
 
432
        }
 
433
    });
 
434
});
 
435
 
 
436
/**
 
437
 * An implementation of a client-side page cache.
 
438
 * This object also uses the cache to provide a simple microhistory,
 
439
 * that is the ability to use the back and forward buttons in the browser
 
440
 */
 
441
AJAX.cache = {
 
442
    /**
 
443
     * @var int The maximum number of pages to keep in the cache
 
444
     */
 
445
    MAX: 6,
 
446
    /**
 
447
     * @var object A hash used to prime the cache with data about the initially
 
448
     *             loaded page. This is set in the footer, and then loaded
 
449
     *             by a double-queued event further down this file.
 
450
     */
 
451
    primer: {},
 
452
    /**
 
453
     * @var array Stores the content of the cached pages
 
454
     */
 
455
    pages: [],
 
456
    /**
 
457
     * @var int The index of the currently loaded page
 
458
     *          This is used to know at which point in the history we are
 
459
     */
 
460
    current: 0,
 
461
    /**
 
462
     * Saves a new page in the cache
 
463
     *
 
464
     * @param string hash    The hash part of the url that is being loaded
 
465
     * @param array  scripts A list of scripts that is requured for the page
 
466
     * @param string menu    A hash that links to a menu stored
 
467
     *                       in a dedicated menu cache
 
468
     * @param array  params  A list of parameters used by PMA_commonParams()
 
469
     * @param string rel     A relationship to the current page:
 
470
     *                       'samepage': Forces the response to be treated as
 
471
     *                                   the same page as the current one
 
472
     *                       'newpage':  Forces the response to be treated as
 
473
     *                                   a new page
 
474
     *                       undefined:  Default behaviour, 'samepage' if the
 
475
     *                                   selflinks of the two pages are the same.
 
476
     *                                   'newpage' otherwise
 
477
     *
 
478
     * @return void
 
479
     */
 
480
    add: function (hash, scripts, menu, params, rel) {
 
481
        if (this.pages.length > AJAX.cache.MAX) {
 
482
            // Trim the cache, to the maximum number of allowed entries
 
483
            // This way we will have a cached menu for every page
 
484
            for (var i=0; i<this.pages.length-this.MAX; i++) {
 
485
                delete this.pages[i];
 
486
            }
 
487
        }
 
488
        while (this.current < this.pages.length) {
 
489
            // trim the cache if we went back in the history
 
490
            // and are now going forward again
 
491
            this.pages.pop();
 
492
        }
 
493
        if (rel === 'newpage' ||
 
494
            (
 
495
                typeof rel === 'undefined' && (
 
496
                    typeof this.pages[this.current - 1] === 'undefined'
 
497
                    ||
 
498
                    this.pages[this.current - 1].hash !== hash
 
499
                )
 
500
            )
 
501
        ) {
 
502
            this.pages.push({
 
503
                hash: hash,
 
504
                content: $('#page_content').html(),
 
505
                scripts: scripts,
 
506
                selflink: $('#selflink').html(),
 
507
                menu: menu,
 
508
                params: params
 
509
            });
 
510
            AJAX.setUrlHash(this.current, hash);
 
511
            this.current++;
 
512
        }
 
513
    },
 
514
    /**
 
515
     * Restores a page from the cache. This is called when the hash
 
516
     * part of the url changes and it's structure appears to be valid
 
517
     *
 
518
     * @param string index Which page from the history to load
 
519
     *
 
520
     * @return void
 
521
     */
 
522
    navigate: function (index) {
 
523
        if (typeof this.pages[index] === 'undefined') {
 
524
            PMA_ajaxShowMessage(
 
525
                '<div class="error">' + PMA_messages['strInvalidPage'] + '</div>',
 
526
                false
 
527
            );
 
528
        } else {
 
529
            AJAX.active = true;
 
530
            var record = this.pages[index];
 
531
            AJAX.scriptHandler.reset(function () {
 
532
                $('#page_content').html(record.content);
 
533
                $('#selflink').html(record.selflink);
 
534
                AJAX.cache.menus.replace(AJAX.cache.menus.get(record.menu));
 
535
                PMA_commonParams.setAll(record.params);
 
536
                AJAX.scriptHandler.load(record.scripts);
 
537
                AJAX.cache.current = ++index;
 
538
            });
 
539
        }
 
540
    },
 
541
    /**
 
542
     * Resaves the content of the current page in the cache.
 
543
     * Necessary in order not to show the user some outdated version of the page
 
544
     *
 
545
     * @return void
 
546
     */
 
547
    update: function () {
 
548
        var page = this.pages[this.current - 1];
 
549
        if (page) {
 
550
            page.content = $('#page_content').html();
 
551
        }
 
552
    },
 
553
    /**
 
554
     * @var object Dedicated menu cache
 
555
     */
 
556
    menus: {
 
557
        /**
 
558
         * Returns the number of items in an associative array
 
559
         *
 
560
         * @return int
 
561
         */
 
562
        size: function(obj) {
 
563
            var size = 0, key;
 
564
            for (key in obj) {
 
565
                if (obj.hasOwnProperty(key)) {
 
566
                    size++;
 
567
                }
 
568
            }
 
569
            return size;
 
570
        },
 
571
        /**
 
572
         * @var hash Stores the content of the cached menus
 
573
         */
 
574
        data: {},
 
575
        /**
 
576
         * Saves a new menu in the cache
 
577
         *
 
578
         * @param string hash    The hash (trimmed md5) of the menu to be saved
 
579
         * @param string content The HTML code of the menu to be saved
 
580
         *
 
581
         * @return void
 
582
         */
 
583
        add: function (hash, content) {
 
584
            if (this.size(this.data) > AJAX.cache.MAX) {
 
585
                // when the cache grows, we remove the oldest entry
 
586
                var oldest, key, init = 0;
 
587
                for (var i in this.data) {
 
588
                    if (this.data[i]) {
 
589
                        if (! init || this.data[i].timestamp.getTime() < oldest.getTime()) {
 
590
                            oldest = this.data[i].timestamp;
 
591
                            key = i;
 
592
                            init = 1;
 
593
                        }
 
594
                    }
 
595
                }
 
596
                delete this.data[key];
 
597
            }
 
598
            this.data[hash] = {
 
599
                content: content,
 
600
                timestamp: new Date()
 
601
            };
 
602
        },
 
603
        /**
 
604
         * Retrieves a menu given its hash
 
605
         *
 
606
         * @param string hash The hash of the menu to be retrieved
 
607
         *
 
608
         * @return string
 
609
         */
 
610
        get: function (hash) {
 
611
            if (this.data[hash]) {
 
612
                return this.data[hash].content;
 
613
            } else {
 
614
                // This should never happen as long as the number of stored menus
 
615
                // is larger or equal to the number of pages in the page cache
 
616
                return '';
 
617
            }
 
618
        },
 
619
        /**
 
620
         * Prepares part of the parameter string used during page requests,
 
621
         * this is necessary to tell the server which menus we have in the cache
 
622
         *
 
623
         * @return string
 
624
         */
 
625
        getRequestParam: function () {
 
626
            var param = '';
 
627
            var menuHashes = [];
 
628
            for (var i in this.data) {
 
629
                menuHashes.push(i);
 
630
            }
 
631
            var menuHashesParam = menuHashes.join('-');
 
632
            if (menuHashesParam) {
 
633
                param = '&menuHashes=' + menuHashesParam;
 
634
            }
 
635
            return param;
 
636
        },
 
637
        /**
 
638
         * Replaces the menu with new content
 
639
         *
 
640
         * @return void
 
641
         */
 
642
        replace: function (content) {
 
643
            $('#floating_menubar').html(content)
 
644
                // Remove duplicate wrapper
 
645
                // TODO: don't send it in the response
 
646
                .children().first().remove();
 
647
            $('#topmenu').menuResizer(PMA_mainMenuResizerCallback);
 
648
        }
 
649
    }
 
650
};
 
651
 
 
652
/**
 
653
 * URL hash management module.
 
654
 * Allows direct bookmarking and microhistory.
 
655
 */
 
656
AJAX.setUrlHash = (function (jQuery, window) {
 
657
    "use strict";
 
658
    /**
 
659
     * Indictaes whether we have already completed
 
660
     * the initialisation of the hash
 
661
     *
 
662
     * @access private
 
663
     */
 
664
    var ready = false;
 
665
    /**
 
666
     * Stores a hash that needed to be set when we were not ready
 
667
     *
 
668
     * @access private
 
669
     */
 
670
    var savedHash = "";
 
671
    /**
 
672
     * Flag to indicate if the change of hash was triggered
 
673
     * by a user pressing the back/forward button or if
 
674
     * the change was triggered internally
 
675
     *
 
676
     * @access private
 
677
     */
 
678
    var userChange = true;
 
679
 
 
680
    // Fix favicon disappearing in Firefox when setting location.hash
 
681
    function resetFavicon() {
 
682
        if (jQuery.browser.mozilla) {
 
683
            // Move the link tags for the favicon to the bottom
 
684
            // of the head element to force a reload of the favicon
 
685
            $('head > link[href=favicon\\.ico]').appendTo('head');
 
686
        }
 
687
    }
 
688
 
 
689
    /**
 
690
     * Sets the hash part of the URL
 
691
     *
 
692
     * @access public
 
693
     */
 
694
    function setUrlHash(index, hash) {
 
695
        /*
 
696
         * Known problem:
 
697
         * Setting hash leads to reload in webkit:
 
698
         * http://www.quirksmode.org/bugreports/archives/2005/05/Safari_13_visual_anomaly_with_windowlocationhref.html
 
699
         *
 
700
         * so we expect that users are not running an ancient Safari version
 
701
         */
 
702
 
 
703
        userChange = false;
 
704
        if (ready) {
 
705
            window.location.hash = "PMAURL-" + index + ":" + hash;
 
706
            resetFavicon();
 
707
        } else {
 
708
            savedHash = "PMAURL-" + index + ":" + hash;
 
709
        }
 
710
    }
 
711
    /**
 
712
     * Start initialisation
 
713
     */
 
714
    if (window.location.hash.substring(0, 8) == '#PMAURL-') {
 
715
        // We have a valid hash, let's redirect the user
 
716
        // to the page that it's pointing to
 
717
        window.location = window.location.hash.substring(
 
718
            window.location.hash.indexOf(':') + 1
 
719
        );
 
720
    } else {
 
721
        // We don't have a valid hash, so we'll set it up
 
722
        // when the page finishes loading
 
723
        jQuery(function(){
 
724
            /* Check if we should set URL */
 
725
            if (savedHash != "") {
 
726
                window.location.hash = savedHash;
 
727
                savedHash = "";
 
728
                resetFavicon();
 
729
            }
 
730
            // Indicate that we're done initialising
 
731
            ready = true;
 
732
        });
 
733
    }
 
734
    /**
 
735
     * Register an event handler for when the url hash changes
 
736
     */
 
737
    jQuery(function(){
 
738
        jQuery(window).hashchange(function () {
 
739
            if (userChange === false) {
 
740
                // Ignore internally triggered hash changes
 
741
                userChange = true;
 
742
            } else if (/^#PMAURL-\d+:/.test(window.location.hash)) {
 
743
                // Change page if the hash changed was triggered by a user action
 
744
                var index = window.location.hash.substring(
 
745
                    8, window.location.hash.indexOf(':')
 
746
                );
 
747
                AJAX.cache.navigate(index);
 
748
            }
 
749
        });
 
750
    });
 
751
    /**
 
752
     * Publicly exposes a reference to the otherwise private setUrlHash function
 
753
     */
 
754
    return setUrlHash;
 
755
})(jQuery, window);
 
756
 
 
757
/**
 
758
 * Page load event handler
 
759
 */
 
760
$(function () {
 
761
    // Add the menu from the initial page into the cache
 
762
    // The cache primer is set by the footer class
 
763
    if (AJAX.cache.primer.url) {
 
764
        AJAX.cache.menus.add(
 
765
            AJAX.cache.primer.menuHash,
 
766
            $('<div></div>')
 
767
                .append('<div></div>')
 
768
                .append($('#serverinfo').clone())
 
769
                .append($('#topmenucontainer').clone())
 
770
                .html()
 
771
        );
 
772
    }
 
773
    $(function () {
 
774
        // Queue up this event twice to make sure that we get a copy
 
775
        // of the page after all other onload events have been fired
 
776
        if (AJAX.cache.primer.url) {
 
777
            AJAX.cache.add(
 
778
                AJAX.cache.primer.url,
 
779
                AJAX.cache.primer.scripts,
 
780
                AJAX.cache.primer.menuHash
 
781
            );
 
782
        }
 
783
    });
 
784
});
 
785
 
 
786
/**
 
787
 * Attach a generic event handler to clicks
 
788
 * on pages and submissions of forms
 
789
 */
 
790
$('a').live('click', AJAX.requestHandler);
 
791
$('form').live('submit', AJAX.requestHandler);
 
792
 
 
793
/**
 
794
 * Gracefully handle fatal server errors
 
795
 * (e.g: 500 - Internal server error)
 
796
 */
 
797
$(document).ajaxError(function(event, request, settings){
 
798
    if (request.status !== 0) { // Don't handle aborted requests
 
799
        var errorCode = $.sprintf(PMA_messages['strErrorCode'], request.status);
 
800
        var errorText = $.sprintf(PMA_messages['strErrorText'], request.statusText);
 
801
        PMA_ajaxShowMessage(
 
802
            '<div class="error">'
 
803
            + PMA_messages['strErrorProcessingRequest']
 
804
            + '<div>' + errorCode + '</div>'
 
805
            + '<div>' + errorText + '</div>'
 
806
            + '</div>',
 
807
            false
 
808
        );
 
809
        AJAX.active = false;
 
810
    }
 
811
});