Welcome to mirror list, hosted at ThFree Co, Russian Federation.

microhistory.js « js - github.com/phpmyadmin/phpmyadmin.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
blob: 60bb20714704ee53e3d148113b6f18f60bae646f (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
/* vim: set expandtab sw=4 ts=4 sts=4: */
/**
 * An implementation of a client-side page cache.
 * This object also uses the cache to provide a simple microhistory,
 * that is the ability to use the back and forward buttons in the browser
 */
PMA_MicroHistory = {
    /**
     * @var int The maximum number of pages to keep in the cache
     */
    MAX: 6,
    /**
     * @var object A hash used to prime the cache with data about the initially
     *             loaded page. This is set in the footer, and then loaded
     *             by a double-queued event further down this file.
     */
    primer: {},
    /**
     * @var array Stores the content of the cached pages
     */
    pages: [],
    /**
     * @var int The index of the currently loaded page
     *          This is used to know at which point in the history we are
     */
    current: 0,
    /**
     * Saves a new page in the cache
     *
     * @param string hash    The hash part of the url that is being loaded
     * @param array  scripts A list of scripts that is required for the page
     * @param string menu    A hash that links to a menu stored
     *                       in a dedicated menu cache
     * @param array  params  A list of parameters used by PMA_commonParams()
     * @param string rel     A relationship to the current page:
     *                       'samepage': Forces the response to be treated as
     *                                   the same page as the current one
     *                       'newpage':  Forces the response to be treated as
     *                                   a new page
     *                       undefined:  Default behaviour, 'samepage' if the
     *                                   selflinks of the two pages are the same.
     *                                   'newpage' otherwise
     *
     * @return void
     */
    add: function (hash, scripts, menu, params, rel) {
        if (this.pages.length > PMA_MicroHistory.MAX) {
            // Trim the cache, to the maximum number of allowed entries
            // This way we will have a cached menu for every page
            for (var i = 0; i < this.pages.length - this.MAX; i++) {
                delete this.pages[i];
            }
        }
        while (this.current < this.pages.length) {
            // trim the cache if we went back in the history
            // and are now going forward again
            this.pages.pop();
        }
        if (rel === 'newpage' ||
            (
                typeof rel === 'undefined' && (
                    typeof this.pages[this.current - 1] === 'undefined' ||
                    this.pages[this.current - 1].hash !== hash
                )
            )
        ) {
            this.pages.push({
                hash: hash,
                content: $('#page_content').html(),
                scripts: scripts,
                selflink: $('#selflink').html(),
                menu: menu,
                params: params
            });
            PMA_SetUrlHash(this.current, hash);
            this.current++;
        }
    },
    /**
     * Restores a page from the cache. This is called when the hash
     * part of the url changes and it's structure appears to be valid
     *
     * @param string index Which page from the history to load
     *
     * @return void
     */
    navigate: function (index) {
        if (typeof this.pages[index] === 'undefined' ||
            typeof this.pages[index].content === 'undefined' ||
            typeof this.pages[index].menu === 'undefined' ||
            ! PMA_MicroHistory.menus.get(this.pages[index].menu)
        ) {
            PMA_ajaxShowMessage(
                '<div class="error">' + PMA_messages.strInvalidPage + '</div>',
                false
            );
        } else {
            AJAX.active = true;
            var record = this.pages[index];
            AJAX.scriptHandler.reset(function () {
                $('#page_content').html(record.content);
                $('#selflink').html(record.selflink);
                PMA_MicroHistory.menus.replace(PMA_MicroHistory.menus.get(record.menu));
                PMA_commonParams.setAll(record.params);
                AJAX.scriptHandler.load(record.scripts);
                PMA_MicroHistory.current = ++index;
            });
        }
    },
    /**
     * Resaves the content of the current page in the cache.
     * Necessary in order not to show the user some outdated version of the page
     *
     * @return void
     */
    update: function () {
        var page = this.pages[this.current - 1];
        if (page) {
            page.content = $('#page_content').html();
        }
    },
    /**
     * @var object Dedicated menu cache
     */
    menus: {
        /**
         * Returns the number of items in an associative array
         *
         * @return int
         */
        size: function (obj) {
            var size = 0, key;
            for (key in obj) {
                if (obj.hasOwnProperty(key)) {
                    size++;
                }
            }
            return size;
        },
        /**
         * @var hash Stores the content of the cached menus
         */
        data: {},
        /**
         * Saves a new menu in the cache
         *
         * @param string hash    The hash (trimmed md5) of the menu to be saved
         * @param string content The HTML code of the menu to be saved
         *
         * @return void
         */
        add: function (hash, content) {
            if (this.size(this.data) > PMA_MicroHistory.MAX) {
                // when the cache grows, we remove the oldest entry
                var oldest, key, init = 0;
                for (var i in this.data) {
                    if (this.data[i]) {
                        if (! init || this.data[i].timestamp.getTime() < oldest.getTime()) {
                            oldest = this.data[i].timestamp;
                            key = i;
                            init = 1;
                        }
                    }
                }
                delete this.data[key];
            }
            this.data[hash] = {
                content: content,
                timestamp: new Date()
            };
        },
        /**
         * Retrieves a menu given its hash
         *
         * @param string hash The hash of the menu to be retrieved
         *
         * @return string
         */
        get: function (hash) {
            if (this.data[hash]) {
                return this.data[hash].content;
            } else {
                // This should never happen as long as the number of stored menus
                // is larger or equal to the number of pages in the page cache
                return '';
            }
        },
        /**
         * Prepares part of the parameter string used during page requests,
         * this is necessary to tell the server which menus we have in the cache
         *
         * @return string
         */
        getRequestParam: function () {
            var param = '';
            var menuHashes = [];
            for (var i in this.data) {
                menuHashes.push(i);
            }
            var menuHashesParam = menuHashes.join('-');
            if (menuHashesParam) {
                param = '&menuHashes=' + menuHashesParam;
            }
            return param;
        },
        /**
         * Replaces the menu with new content
         *
         * @return void
         */
        replace: function (content) {
            $('#floating_menubar').html(content)
                // Remove duplicate wrapper
                // TODO: don't send it in the response
                .children().first().remove();
            $('#topmenu').menuResizer(PMA_mainMenuResizerCallback);
        }
    }
};

/**
 * URL hash management module.
 * Allows direct bookmarking and microhistory.
 */
PMA_SetUrlHash = (function (jQuery, window) {
    "use strict";
    /**
     * Indictaes whether we have already completed
     * the initialisation of the hash
     *
     * @access private
     */
    var ready = false;
    /**
     * Stores a hash that needed to be set when we were not ready
     *
     * @access private
     */
    var savedHash = "";
    /**
     * Flag to indicate if the change of hash was triggered
     * by a user pressing the back/forward button or if
     * the change was triggered internally
     *
     * @access private
     */
    var userChange = true;

    // Fix favicon disappearing in Firefox when setting location.hash
    function resetFavicon() {
        if (navigator.userAgent.indexOf('Firefox') > -1) {
            // Move the link tags for the favicon to the bottom
            // of the head element to force a reload of the favicon
            $('head > link[href=favicon\\.ico]').appendTo('head');
        }
    }

    /**
     * Sets the hash part of the URL
     *
     * @access public
     */
    function setUrlHash(index, hash) {
        /*
         * Known problem:
         * Setting hash leads to reload in webkit:
         * http://www.quirksmode.org/bugreports/archives/2005/05/Safari_13_visual_anomaly_with_windowlocationhref.html
         *
         * so we expect that users are not running an ancient Safari version
         */

        userChange = false;
        if (ready) {
            window.location.hash = "PMAURL-" + index + ":" + hash;
            resetFavicon();
        } else {
            savedHash = "PMAURL-" + index + ":" + hash;
        }
    }
    /**
     * Start initialisation
     */
    if (window.location.hash.substring(0, 8) == '#PMAURL-') {
        // We have a valid hash, let's redirect the user
        // to the page that it's pointing to
        var colon_position = window.location.hash.indexOf(':');
        var questionmark_position = window.location.hash.indexOf('?');
        if (colon_position != -1 && questionmark_position != -1 && colon_position < questionmark_position) {
            var hash_url = window.location.hash.substring(colon_position + 1, questionmark_position);
            if (PMA_gotoWhitelist.indexOf(hash_url) != -1) {
                window.location = window.location.hash.substring(
                    colon_position + 1
                );
            }
        }
    } else {
        // We don't have a valid hash, so we'll set it up
        // when the page finishes loading
        jQuery(function () {
            /* Check if we should set URL */
            if (savedHash !== "") {
                window.location.hash = savedHash;
                savedHash = "";
                resetFavicon();
            }
            // Indicate that we're done initialising
            ready = true;
        });
    }
    /**
     * Register an event handler for when the url hash changes
     */
    jQuery(function () {
        jQuery(window).hashchange(function () {
            if (userChange === false) {
                // Ignore internally triggered hash changes
                userChange = true;
            } else if (/^#PMAURL-\d+:/.test(window.location.hash)) {
                // Change page if the hash changed was triggered by a user action
                var index = window.location.hash.substring(
                    8, window.location.hash.indexOf(':')
                );
                PMA_MicroHistory.navigate(index);
            }
        });
    });
    /**
     * Publicly exposes a reference to the otherwise private setUrlHash function
     */
    return setUrlHash;
})(jQuery, window);