ajax.js 28 KB

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