Translated using Weblate (Turkish)
[phpmyadmin.git] / js / microhistory.js
blobc36d821342be40b5c71e9c887e15e7b473e6bbe2
1 /* vim: set expandtab sw=4 ts=4 sts=4: */
3 /* global GotoWhitelist */ // js/whitelist.php
5 /**
6  * An implementation of a client-side page cache.
7  * This object also uses the cache to provide a simple microhistory,
8  * that is the ability to use the back and forward buttons in the browser
9  */
10 var MicroHistory = {
11     /**
12      * @var int The maximum number of pages to keep in the cache
13      */
14     MAX: 6,
15     /**
16      * @var object A hash used to prime the cache with data about the initially
17      *             loaded page. This is set in the footer, and then loaded
18      *             by a double-queued event further down this file.
19      */
20     primer: {},
21     /**
22      * @var array Stores the content of the cached pages
23      */
24     pages: [],
25     /**
26      * @var int The index of the currently loaded page
27      *          This is used to know at which point in the history we are
28      */
29     current: 0,
30     /**
31      * Saves a new page in the cache
32      *
33      * @param string hash    The hash part of the url that is being loaded
34      * @param array  scripts A list of scripts that is required for the page
35      * @param string menu    A hash that links to a menu stored
36      *                       in a dedicated menu cache
37      * @param array  params  A list of parameters used by CommonParams()
38      * @param string rel     A relationship to the current page:
39      *                       'samepage': Forces the response to be treated as
40      *                                   the same page as the current one
41      *                       'newpage':  Forces the response to be treated as
42      *                                   a new page
43      *                       undefined:  Default behaviour, 'samepage' if the
44      *                                   selflinks of the two pages are the same.
45      *                                   'newpage' otherwise
46      *
47      * @return void
48      */
49     add: function (hash, scripts, menu, params, rel) {
50         if (this.pages.length > MicroHistory.MAX) {
51             // Trim the cache, to the maximum number of allowed entries
52             // This way we will have a cached menu for every page
53             for (var i = 0; i < this.pages.length - this.MAX; i++) {
54                 delete this.pages[i];
55             }
56         }
57         while (this.current < this.pages.length) {
58             // trim the cache if we went back in the history
59             // and are now going forward again
60             this.pages.pop();
61         }
62         if (rel === 'newpage' ||
63             (
64                 typeof rel === 'undefined' && (
65                     typeof this.pages[this.current - 1] === 'undefined' ||
66                     this.pages[this.current - 1].hash !== hash
67                 )
68             )
69         ) {
70             this.pages.push({
71                 hash: hash,
72                 content: $('#page_content').html(),
73                 scripts: scripts,
74                 selflink: $('#selflink').html(),
75                 menu: menu,
76                 params: params
77             });
78             MicroHistory.setUrlHash(this.current, hash);
79             this.current++;
80         }
81     },
82     /**
83      * Restores a page from the cache. This is called when the hash
84      * part of the url changes and it's structure appears to be valid
85      *
86      * @param string index Which page from the history to load
87      *
88      * @return void
89      */
90     navigate: function (index) {
91         var localIndex = index;
92         if (typeof this.pages[localIndex] === 'undefined' ||
93             typeof this.pages[localIndex].content === 'undefined' ||
94             typeof this.pages[localIndex].menu === 'undefined' ||
95             ! MicroHistory.menus.get(this.pages[localIndex].menu)
96         ) {
97             Functions.ajaxShowMessage(
98                 '<div class="error">' + Messages.strInvalidPage + '</div>',
99                 false
100             );
101         } else {
102             AJAX.active = true;
103             var record = this.pages[localIndex];
104             AJAX.scriptHandler.reset(function () {
105                 $('#page_content').html(record.content);
106                 $('#selflink').html(record.selflink);
107                 MicroHistory.menus.replace(MicroHistory.menus.get(record.menu));
108                 CommonParams.setAll(record.params);
109                 AJAX.scriptHandler.load(record.scripts);
110                 MicroHistory.current = ++localIndex;
111             });
112         }
113     },
114     /**
115      * Resaves the content of the current page in the cache.
116      * Necessary in order not to show the user some outdated version of the page
117      *
118      * @return void
119      */
120     update: function () {
121         var page = this.pages[this.current - 1];
122         if (page) {
123             page.content = $('#page_content').html();
124         }
125     },
126     /**
127      * @var object Dedicated menu cache
128      */
129     menus: {
130         /**
131          * Returns the number of items in an associative array
132          *
133          * @return int
134          */
135         size: function (obj) {
136             var size = 0;
137             var key;
138             for (key in obj) {
139                 if (obj.hasOwnProperty(key)) {
140                     size++;
141                 }
142             }
143             return size;
144         },
145         /**
146          * @var hash Stores the content of the cached menus
147          */
148         data: {},
149         /**
150          * Saves a new menu in the cache
151          *
152          * @param string hash    The hash (trimmed md5) of the menu to be saved
153          * @param string content The HTML code of the menu to be saved
154          *
155          * @return void
156          */
157         add: function (hash, content) {
158             if (this.size(this.data) > MicroHistory.MAX) {
159                 // when the cache grows, we remove the oldest entry
160                 var oldest;
161                 var key;
162                 var init = 0;
163                 for (var i in this.data) {
164                     if (this.data[i]) {
165                         if (! init || this.data[i].timestamp.getTime() < oldest.getTime()) {
166                             oldest = this.data[i].timestamp;
167                             key = i;
168                             init = 1;
169                         }
170                     }
171                 }
172                 delete this.data[key];
173             }
174             this.data[hash] = {
175                 content: content,
176                 timestamp: new Date()
177             };
178         },
179         /**
180          * Retrieves a menu given its hash
181          *
182          * @param string hash The hash of the menu to be retrieved
183          *
184          * @return string
185          */
186         get: function (hash) {
187             if (this.data[hash]) {
188                 return this.data[hash].content;
189             } else {
190                 // This should never happen as long as the number of stored menus
191                 // is larger or equal to the number of pages in the page cache
192                 return '';
193             }
194         },
195         /**
196          * Prepares part of the parameter string used during page requests,
197          * this is necessary to tell the server which menus we have in the cache
198          *
199          * @return string
200          */
201         getRequestParam: function () {
202             var param = '';
203             var menuHashes = [];
204             for (var i in this.data) {
205                 menuHashes.push(i);
206             }
207             var menuHashesParam = menuHashes.join('-');
208             if (menuHashesParam) {
209                 param = CommonParams.get('arg_separator') + 'menuHashes=' + menuHashesParam;
210             }
211             return param;
212         },
213         /**
214          * Replaces the menu with new content
215          *
216          * @return void
217          */
218         replace: function (content) {
219             $('#floating_menubar').html(content)
220                 // Remove duplicate wrapper
221                 // TODO: don't send it in the response
222                 .children().first().remove();
223             $('#topmenu').menuResizer(Functions.mainMenuResizerCallback);
224         }
225     }
229  * URL hash management module.
230  * Allows direct bookmarking and microhistory.
231  */
232 MicroHistory.setUrlHash = (function (jQuery, window) {
233     'use strict';
234     /**
235      * Indictaes whether we have already completed
236      * the initialisation of the hash
237      *
238      * @access private
239      */
240     var ready = false;
241     /**
242      * Stores a hash that needed to be set when we were not ready
243      *
244      * @access private
245      */
246     var savedHash = '';
247     /**
248      * Flag to indicate if the change of hash was triggered
249      * by a user pressing the back/forward button or if
250      * the change was triggered internally
251      *
252      * @access private
253      */
254     var userChange = true;
256     // Fix favicon disappearing in Firefox when setting location.hash
257     function resetFavicon () {
258         if (navigator.userAgent.indexOf('Firefox') > -1) {
259             // Move the link tags for the favicon to the bottom
260             // of the head element to force a reload of the favicon
261             $('head > link[href="favicon\\.ico"]').appendTo('head');
262         }
263     }
265     /**
266      * Sets the hash part of the URL
267      *
268      * @access public
269      */
270     function setUrlHash (index, hash) {
271         /*
272          * Known problem:
273          * Setting hash leads to reload in webkit:
274          * http://www.quirksmode.org/bugreports/archives/2005/05/Safari_13_visual_anomaly_with_windowlocationhref.html
275          *
276          * so we expect that users are not running an ancient Safari version
277          */
279         userChange = false;
280         if (ready) {
281             window.location.hash = 'PMAURL-' + index + ':' + hash;
282             resetFavicon();
283         } else {
284             savedHash = 'PMAURL-' + index + ':' + hash;
285         }
286     }
287     /**
288      * Start initialisation
289      */
290     var urlHash = window.location.hash;
291     if (urlHash.substring(0, 8) === '#PMAURL-') {
292         // We have a valid hash, let's redirect the user
293         // to the page that it's pointing to
294         var colonPosition = urlHash.indexOf(':');
295         var questionMarkPosition = urlHash.indexOf('?');
296         if (colonPosition !== -1 && questionMarkPosition !== -1 && colonPosition < questionMarkPosition) {
297             var hashUrl = urlHash.substring(colonPosition + 1, questionMarkPosition);
298             if (GotoWhitelist.indexOf(hashUrl) !== -1) {
299                 window.location = urlHash.substring(
300                     colonPosition + 1
301                 );
302             }
303         }
304     } else {
305         // We don't have a valid hash, so we'll set it up
306         // when the page finishes loading
307         jQuery(function () {
308             /* Check if we should set URL */
309             if (savedHash !== '') {
310                 window.location.hash = savedHash;
311                 savedHash = '';
312                 resetFavicon();
313             }
314             // Indicate that we're done initialising
315             ready = true;
316         });
317     }
318     /**
319      * Register an event handler for when the url hash changes
320      */
321     jQuery(function () {
322         jQuery(window).hashchange(function () {
323             if (userChange === false) {
324                 // Ignore internally triggered hash changes
325                 userChange = true;
326             } else if (/^#PMAURL-\d+:/.test(window.location.hash)) {
327                 // Change page if the hash changed was triggered by a user action
328                 var index = window.location.hash.substring(
329                     8, window.location.hash.indexOf(':')
330                 );
331                 MicroHistory.navigate(index);
332             }
333         });
334     });
335     /**
336      * Publicly exposes a reference to the otherwise private setUrlHash function
337      */
338     return setUrlHash;
339 }(jQuery, window));