navigation.js 59 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678
  1. /* vim: set expandtab sw=4 ts=4 sts=4: */
  2. /**
  3. * function used in or for navigation panel
  4. *
  5. * @package phpMyAdmin-Navigation
  6. */
  7. /**
  8. * updates the tree state in sessionStorage
  9. *
  10. * @returns void
  11. */
  12. function navTreeStateUpdate () {
  13. // update if session storage is supported
  14. if (isStorageSupported('sessionStorage')) {
  15. var storage = window.sessionStorage;
  16. // try catch necessary here to detect whether
  17. // content to be stored exceeds storage capacity
  18. try {
  19. storage.setItem('navTreePaths', JSON.stringify(traverseNavigationForPaths()));
  20. storage.setItem('server', PMA_commonParams.get('server'));
  21. storage.setItem('token', PMA_commonParams.get('token'));
  22. } catch (error) {
  23. // storage capacity exceeded & old navigation tree
  24. // state is no more valid, so remove it
  25. storage.removeItem('navTreePaths');
  26. storage.removeItem('server');
  27. storage.removeItem('token');
  28. }
  29. }
  30. }
  31. /**
  32. * updates the filter state in sessionStorage
  33. *
  34. * @returns void
  35. */
  36. function navFilterStateUpdate (filterName, filterValue) {
  37. if (isStorageSupported('sessionStorage')) {
  38. var storage = window.sessionStorage;
  39. try {
  40. var currentFilter = $.extend({}, JSON.parse(storage.getItem('navTreeSearchFilters')));
  41. var filter = {};
  42. filter[filterName] = filterValue;
  43. currentFilter = $.extend(currentFilter, filter);
  44. storage.setItem('navTreeSearchFilters', JSON.stringify(currentFilter));
  45. } catch (error) {
  46. storage.removeItem('navTreeSearchFilters');
  47. }
  48. }
  49. }
  50. /**
  51. * restores the filter state on navigation reload
  52. *
  53. * @returns void
  54. */
  55. function navFilterStateRestore () {
  56. if (isStorageSupported('sessionStorage')
  57. && typeof window.sessionStorage.navTreeSearchFilters !== 'undefined'
  58. ) {
  59. var searchClauses = JSON.parse(window.sessionStorage.navTreeSearchFilters);
  60. if (Object.keys(searchClauses).length < 1) {
  61. return;
  62. }
  63. // restore database filter if present and not empty
  64. if (searchClauses.hasOwnProperty('dbFilter')
  65. && searchClauses.dbFilter.length
  66. ) {
  67. $obj = $('#pma_navigation_tree');
  68. if (! $obj.data('fastFilter')) {
  69. $obj.data(
  70. 'fastFilter',
  71. new PMA_fastFilter.filter($obj, '')
  72. );
  73. }
  74. $obj.find('li.fast_filter.db_fast_filter input.searchClause')
  75. .val(searchClauses.dbFilter)
  76. .trigger('keyup');
  77. }
  78. // find all table filters present in the tree
  79. $tableFilters = $('#pma_navigation_tree li.database')
  80. .children('div.list_container')
  81. .find('li.fast_filter input.searchClause');
  82. // restore table filters
  83. $tableFilters.each(function () {
  84. $obj = $(this).closest('div.list_container');
  85. // aPath associated with this filter
  86. var filterName = $(this).siblings('input[name=aPath]').val();
  87. // if this table's filter has a state stored in storage
  88. if (searchClauses.hasOwnProperty(filterName)
  89. && searchClauses[filterName].length
  90. ) {
  91. // clear state if item is not visible,
  92. // happens when table filter becomes invisible
  93. // as db filter has already been applied
  94. if (! $obj.is(':visible')) {
  95. navFilterStateUpdate(filterName, '');
  96. return true;
  97. }
  98. if (! $obj.data('fastFilter')) {
  99. $obj.data(
  100. 'fastFilter',
  101. new PMA_fastFilter.filter($obj, '')
  102. );
  103. }
  104. $(this).val(searchClauses[filterName])
  105. .trigger('keyup');
  106. }
  107. });
  108. }
  109. }
  110. /**
  111. * Loads child items of a node and executes a given callback
  112. *
  113. * @param isNode
  114. * @param $expandElem expander
  115. * @param callback callback function
  116. *
  117. * @returns void
  118. */
  119. function loadChildNodes (isNode, $expandElem, callback) {
  120. var $destination = null;
  121. var params = null;
  122. if (isNode) {
  123. if (!$expandElem.hasClass('expander')) {
  124. return;
  125. }
  126. $destination = $expandElem.closest('li');
  127. params = {
  128. aPath: $expandElem.find('span.aPath').text(),
  129. vPath: $expandElem.find('span.vPath').text(),
  130. pos: $expandElem.find('span.pos').text(),
  131. pos2_name: $expandElem.find('span.pos2_name').text(),
  132. pos2_value: $expandElem.find('span.pos2_value').text(),
  133. searchClause: '',
  134. searchClause2: ''
  135. };
  136. if ($expandElem.closest('ul').hasClass('search_results')) {
  137. params.searchClause = PMA_fastFilter.getSearchClause();
  138. params.searchClause2 = PMA_fastFilter.getSearchClause2($expandElem);
  139. }
  140. } else {
  141. $destination = $('#pma_navigation_tree_content');
  142. params = {
  143. aPath: $expandElem.attr('aPath'),
  144. vPath: $expandElem.attr('vPath'),
  145. pos: $expandElem.attr('pos'),
  146. pos2_name: '',
  147. pos2_value: '',
  148. searchClause: '',
  149. searchClause2: ''
  150. };
  151. }
  152. var url = $('#pma_navigation').find('a.navigation_url').attr('href');
  153. $.post(url, params, function (data) {
  154. if (typeof data !== 'undefined' && data.success === true) {
  155. $destination.find('div.list_container').remove(); // FIXME: Hack, there shouldn't be a list container there
  156. if (isNode) {
  157. $destination.append(data.message);
  158. $expandElem.addClass('loaded');
  159. } else {
  160. $destination.html(data.message);
  161. $destination.children()
  162. .first()
  163. .css({
  164. border: '0px',
  165. margin: '0em',
  166. padding : '0em'
  167. })
  168. .slideDown('slow');
  169. }
  170. if (data._errors) {
  171. var $errors = $(data._errors);
  172. if ($errors.children().length > 0) {
  173. $('#pma_errors').replaceWith(data._errors);
  174. }
  175. }
  176. if (callback && typeof callback === 'function') {
  177. callback(data);
  178. }
  179. } else if (data.redirect_flag === '1') {
  180. if (window.location.href.indexOf('?') === -1) {
  181. window.location.href += '?session_expired=1';
  182. } else {
  183. window.location.href += PMA_commonParams.get('arg_separator') + 'session_expired=1';
  184. }
  185. window.location.reload();
  186. } else {
  187. var $throbber = $expandElem.find('img.throbber');
  188. $throbber.hide();
  189. var $icon = $expandElem.find('img.ic_b_plus');
  190. $icon.show();
  191. PMA_ajaxShowMessage(data.error, false);
  192. }
  193. });
  194. }
  195. /**
  196. * Collapses a node in navigation tree.
  197. *
  198. * @param $expandElem expander
  199. *
  200. * @returns void
  201. */
  202. function collapseTreeNode ($expandElem) {
  203. var $children = $expandElem.closest('li').children('div.list_container');
  204. var $icon = $expandElem.find('img');
  205. if ($expandElem.hasClass('loaded')) {
  206. if ($icon.is('.ic_b_minus')) {
  207. $icon.removeClass('ic_b_minus').addClass('ic_b_plus');
  208. $children.slideUp('fast');
  209. }
  210. }
  211. $expandElem.blur();
  212. $children.promise().done(navTreeStateUpdate);
  213. }
  214. /**
  215. * Traverse the navigation tree backwards to generate all the actual
  216. * and virtual paths, as well as the positions in the pagination at
  217. * various levels, if necessary.
  218. *
  219. * @return Object
  220. */
  221. function traverseNavigationForPaths () {
  222. var params = {
  223. pos: $('#pma_navigation_tree').find('div.dbselector select').val()
  224. };
  225. if ($('#navi_db_select').length) {
  226. return params;
  227. }
  228. var count = 0;
  229. $('#pma_navigation_tree').find('a.expander:visible').each(function () {
  230. if ($(this).find('img').is('.ic_b_minus') &&
  231. $(this).closest('li').find('div.list_container .ic_b_minus').length === 0
  232. ) {
  233. params['n' + count + '_aPath'] = $(this).find('span.aPath').text();
  234. params['n' + count + '_vPath'] = $(this).find('span.vPath').text();
  235. var pos2_name = $(this).find('span.pos2_name').text();
  236. if (! pos2_name) {
  237. pos2_name = $(this)
  238. .parent()
  239. .parent()
  240. .find('span.pos2_name:last')
  241. .text();
  242. }
  243. var pos2_value = $(this).find('span.pos2_value').text();
  244. if (! pos2_value) {
  245. pos2_value = $(this)
  246. .parent()
  247. .parent()
  248. .find('span.pos2_value:last')
  249. .text();
  250. }
  251. params['n' + count + '_pos2_name'] = pos2_name;
  252. params['n' + count + '_pos2_value'] = pos2_value;
  253. params['n' + count + '_pos3_name'] = $(this).find('span.pos3_name').text();
  254. params['n' + count + '_pos3_value'] = $(this).find('span.pos3_value').text();
  255. count++;
  256. }
  257. });
  258. return params;
  259. }
  260. /**
  261. * Executed on page load
  262. */
  263. $(function () {
  264. if (! $('#pma_navigation').length) {
  265. // Don't bother running any code if the navigation is not even on the page
  266. return;
  267. }
  268. // Do not let the page reload on submitting the fast filter
  269. $(document).on('submit', '.fast_filter', function (event) {
  270. event.preventDefault();
  271. });
  272. // Fire up the resize handlers
  273. new ResizeHandler();
  274. /**
  275. * opens/closes (hides/shows) tree elements
  276. * loads data via ajax
  277. */
  278. $(document).on('click', '#pma_navigation_tree a.expander', function (event) {
  279. event.preventDefault();
  280. event.stopImmediatePropagation();
  281. var $icon = $(this).find('img');
  282. if ($icon.is('.ic_b_plus')) {
  283. expandTreeNode($(this));
  284. } else {
  285. collapseTreeNode($(this));
  286. }
  287. });
  288. /**
  289. * Register event handler for click on the reload
  290. * navigation icon at the top of the panel
  291. */
  292. $(document).on('click', '#pma_navigation_reload', function (event) {
  293. event.preventDefault();
  294. // Find the loading symbol and show it
  295. var $icon_throbber_src = $('#pma_navigation').find('.throbber');
  296. $icon_throbber_src.show();
  297. // TODO Why is a loading symbol both hidden, and invisible?
  298. $icon_throbber_src.css('visibility', '');
  299. // Callback to be used to hide the loading symbol when done reloading
  300. function hideNav () {
  301. $icon_throbber_src.hide();
  302. }
  303. // Reload the navigation
  304. PMA_reloadNavigation(hideNav);
  305. });
  306. $(document).on('change', '#navi_db_select', function (event) {
  307. if (! $(this).val()) {
  308. PMA_commonParams.set('db', '');
  309. PMA_reloadNavigation();
  310. }
  311. $(this).closest('form').trigger('submit');
  312. });
  313. /**
  314. * Register event handler for click on the collapse all
  315. * navigation icon at the top of the navigation tree
  316. */
  317. $(document).on('click', '#pma_navigation_collapse', function (event) {
  318. event.preventDefault();
  319. $('#pma_navigation_tree').find('a.expander').each(function () {
  320. var $icon = $(this).find('img');
  321. if ($icon.is('.ic_b_minus')) {
  322. $(this).click();
  323. }
  324. });
  325. });
  326. /**
  327. * Register event handler to toggle
  328. * the 'link with main panel' icon on mouseenter.
  329. */
  330. $(document).on('mouseenter', '#pma_navigation_sync', function (event) {
  331. event.preventDefault();
  332. var synced = $('#pma_navigation_tree').hasClass('synced');
  333. var $img = $('#pma_navigation_sync').children('img');
  334. if (synced) {
  335. $img.removeClass('ic_s_link').addClass('ic_s_unlink');
  336. } else {
  337. $img.removeClass('ic_s_unlink').addClass('ic_s_link');
  338. }
  339. });
  340. /**
  341. * Register event handler to toggle
  342. * the 'link with main panel' icon on mouseout.
  343. */
  344. $(document).on('mouseout', '#pma_navigation_sync', function (event) {
  345. event.preventDefault();
  346. var synced = $('#pma_navigation_tree').hasClass('synced');
  347. var $img = $('#pma_navigation_sync').children('img');
  348. if (synced) {
  349. $img.removeClass('ic_s_unlink').addClass('ic_s_link');
  350. } else {
  351. $img.removeClass('ic_s_link').addClass('ic_s_unlink');
  352. }
  353. });
  354. /**
  355. * Register event handler to toggle
  356. * the linking with main panel behavior
  357. */
  358. $(document).on('click', '#pma_navigation_sync', function (event) {
  359. event.preventDefault();
  360. var synced = $('#pma_navigation_tree').hasClass('synced');
  361. var $img = $('#pma_navigation_sync').children('img');
  362. if (synced) {
  363. $img
  364. .removeClass('ic_s_unlink')
  365. .addClass('ic_s_link')
  366. .attr('alt', PMA_messages.linkWithMain)
  367. .attr('title', PMA_messages.linkWithMain);
  368. $('#pma_navigation_tree')
  369. .removeClass('synced')
  370. .find('li.selected')
  371. .removeClass('selected');
  372. } else {
  373. $img
  374. .removeClass('ic_s_link')
  375. .addClass('ic_s_unlink')
  376. .attr('alt', PMA_messages.unlinkWithMain)
  377. .attr('title', PMA_messages.unlinkWithMain);
  378. $('#pma_navigation_tree').addClass('synced');
  379. PMA_showCurrentNavigation();
  380. }
  381. });
  382. /**
  383. * Bind all "fast filter" events
  384. */
  385. $(document).on('click', '#pma_navigation_tree li.fast_filter span', PMA_fastFilter.events.clear);
  386. $(document).on('focus', '#pma_navigation_tree li.fast_filter input.searchClause', PMA_fastFilter.events.focus);
  387. $(document).on('blur', '#pma_navigation_tree li.fast_filter input.searchClause', PMA_fastFilter.events.blur);
  388. $(document).on('keyup', '#pma_navigation_tree li.fast_filter input.searchClause', PMA_fastFilter.events.keyup);
  389. /**
  390. * Ajax handler for pagination
  391. */
  392. $(document).on('click', '#pma_navigation_tree div.pageselector a.ajax', function (event) {
  393. event.preventDefault();
  394. PMA_navigationTreePagination($(this));
  395. });
  396. /**
  397. * Node highlighting
  398. */
  399. $(document).on(
  400. 'mouseover',
  401. '#pma_navigation_tree.highlight li:not(.fast_filter)',
  402. function () {
  403. if ($('li:visible', this).length === 0) {
  404. $(this).addClass('activePointer');
  405. }
  406. }
  407. );
  408. $(document).on(
  409. 'mouseout',
  410. '#pma_navigation_tree.highlight li:not(.fast_filter)',
  411. function () {
  412. $(this).removeClass('activePointer');
  413. }
  414. );
  415. /** Create a Routine, Trigger or Event */
  416. $(document).on('click', 'li.new_procedure a.ajax, li.new_function a.ajax', function (event) {
  417. event.preventDefault();
  418. var dialog = new RTE.object('routine');
  419. dialog.editorDialog(1, $(this));
  420. });
  421. $(document).on('click', 'li.new_trigger a.ajax', function (event) {
  422. event.preventDefault();
  423. var dialog = new RTE.object('trigger');
  424. dialog.editorDialog(1, $(this));
  425. });
  426. $(document).on('click', 'li.new_event a.ajax', function (event) {
  427. event.preventDefault();
  428. var dialog = new RTE.object('event');
  429. dialog.editorDialog(1, $(this));
  430. });
  431. /** Edit Routines, Triggers or Events */
  432. $(document).on('click', 'li.procedure > a.ajax, li.function > a.ajax', function (event) {
  433. event.preventDefault();
  434. var dialog = new RTE.object('routine');
  435. dialog.editorDialog(0, $(this));
  436. });
  437. $(document).on('click', 'li.trigger > a.ajax', function (event) {
  438. event.preventDefault();
  439. var dialog = new RTE.object('trigger');
  440. dialog.editorDialog(0, $(this));
  441. });
  442. $(document).on('click', 'li.event > a.ajax', function (event) {
  443. event.preventDefault();
  444. var dialog = new RTE.object('event');
  445. dialog.editorDialog(0, $(this));
  446. });
  447. /** Execute Routines */
  448. $(document).on('click', 'li.procedure div a.ajax img,' +
  449. ' li.function div a.ajax img', function (event) {
  450. event.preventDefault();
  451. var dialog = new RTE.object('routine');
  452. dialog.executeDialog($(this).parent());
  453. });
  454. /** Export Triggers and Events */
  455. $(document).on('click', 'li.trigger div:eq(1) a.ajax img,' +
  456. ' li.event div:eq(1) a.ajax img', function (event) {
  457. event.preventDefault();
  458. var dialog = new RTE.object();
  459. dialog.exportDialog($(this).parent());
  460. });
  461. /** New index */
  462. $(document).on('click', '#pma_navigation_tree li.new_index a.ajax', function (event) {
  463. event.preventDefault();
  464. var url = $(this).attr('href').substr(
  465. $(this).attr('href').indexOf('?') + 1
  466. ) + PMA_commonParams.get('arg_separator') + 'ajax_request=true';
  467. var title = PMA_messages.strAddIndex;
  468. indexEditorDialog(url, title);
  469. });
  470. /** Edit index */
  471. $(document).on('click', 'li.index a.ajax', function (event) {
  472. event.preventDefault();
  473. var url = $(this).attr('href').substr(
  474. $(this).attr('href').indexOf('?') + 1
  475. ) + PMA_commonParams.get('arg_separator') + 'ajax_request=true';
  476. var title = PMA_messages.strEditIndex;
  477. indexEditorDialog(url, title);
  478. });
  479. /** New view */
  480. $(document).on('click', 'li.new_view a.ajax', function (event) {
  481. event.preventDefault();
  482. PMA_createViewDialog($(this));
  483. });
  484. /** Hide navigation tree item */
  485. $(document).on('click', 'a.hideNavItem.ajax', function (event) {
  486. event.preventDefault();
  487. var argSep = PMA_commonParams.get('arg_separator');
  488. var params = $(this).getPostData();
  489. params += argSep + 'ajax_request=true' + argSep + 'server=' + PMA_commonParams.get('server');
  490. $.ajax({
  491. type: 'POST',
  492. data: params,
  493. url: $(this).attr('href'),
  494. success: function (data) {
  495. if (typeof data !== 'undefined' && data.success === true) {
  496. PMA_reloadNavigation();
  497. } else {
  498. PMA_ajaxShowMessage(data.error);
  499. }
  500. }
  501. });
  502. });
  503. /** Display a dialog to choose hidden navigation items to show */
  504. $(document).on('click', 'a.showUnhide.ajax', function (event) {
  505. event.preventDefault();
  506. var $msg = PMA_ajaxShowMessage();
  507. var argSep = PMA_commonParams.get('arg_separator');
  508. var params = $(this).getPostData();
  509. params += argSep + 'ajax_request=true';
  510. $.post($(this).attr('href'), params, function (data) {
  511. if (typeof data !== 'undefined' && data.success === true) {
  512. PMA_ajaxRemoveMessage($msg);
  513. var buttonOptions = {};
  514. buttonOptions[PMA_messages.strClose] = function () {
  515. $(this).dialog('close');
  516. };
  517. $('<div/>')
  518. .attr('id', 'unhideNavItemDialog')
  519. .append(data.message)
  520. .dialog({
  521. width: 400,
  522. minWidth: 200,
  523. modal: true,
  524. buttons: buttonOptions,
  525. title: PMA_messages.strUnhideNavItem,
  526. close: function () {
  527. $(this).remove();
  528. }
  529. });
  530. } else {
  531. PMA_ajaxShowMessage(data.error);
  532. }
  533. });
  534. });
  535. /** Show a hidden navigation tree item */
  536. $(document).on('click', 'a.unhideNavItem.ajax', function (event) {
  537. event.preventDefault();
  538. var $tr = $(this).parents('tr');
  539. var $msg = PMA_ajaxShowMessage();
  540. var argSep = PMA_commonParams.get('arg_separator');
  541. var params = $(this).getPostData();
  542. params += argSep + 'ajax_request=true' + argSep + 'server=' + PMA_commonParams.get('server');
  543. $.ajax({
  544. type: 'POST',
  545. data: params,
  546. url: $(this).attr('href'),
  547. success: function (data) {
  548. PMA_ajaxRemoveMessage($msg);
  549. if (typeof data !== 'undefined' && data.success === true) {
  550. $tr.remove();
  551. PMA_reloadNavigation();
  552. } else {
  553. PMA_ajaxShowMessage(data.error);
  554. }
  555. }
  556. });
  557. });
  558. // Add/Remove favorite table using Ajax.
  559. $(document).on('click', '.favorite_table_anchor', function (event) {
  560. event.preventDefault();
  561. $self = $(this);
  562. var anchor_id = $self.attr('id');
  563. if ($self.data('favtargetn') !== null) {
  564. if ($('a[data-favtargets="' + $self.data('favtargetn') + '"]').length > 0) {
  565. $('a[data-favtargets="' + $self.data('favtargetn') + '"]').trigger('click');
  566. return;
  567. }
  568. }
  569. $.ajax({
  570. url: $self.attr('href'),
  571. cache: false,
  572. type: 'POST',
  573. data: {
  574. favorite_tables: (isStorageSupported('localStorage') && typeof window.localStorage.favorite_tables !== 'undefined')
  575. ? window.localStorage.favorite_tables
  576. : '',
  577. server: PMA_commonParams.get('server'),
  578. },
  579. success: function (data) {
  580. if (data.changes) {
  581. $('#pma_favorite_list').html(data.list);
  582. $('#' + anchor_id).parent().html(data.anchor);
  583. PMA_tooltip(
  584. $('#' + anchor_id),
  585. 'a',
  586. $('#' + anchor_id).attr('title')
  587. );
  588. // Update localStorage.
  589. if (isStorageSupported('localStorage')) {
  590. window.localStorage.favorite_tables = data.favorite_tables;
  591. }
  592. } else {
  593. PMA_ajaxShowMessage(data.message);
  594. }
  595. }
  596. });
  597. });
  598. // Check if session storage is supported
  599. if (isStorageSupported('sessionStorage')) {
  600. var storage = window.sessionStorage;
  601. // remove tree from storage if Navi_panel config form is submitted
  602. $(document).on('submit', 'form.config-form', function (event) {
  603. storage.removeItem('navTreePaths');
  604. });
  605. // Initialize if no previous state is defined
  606. if ($('#pma_navigation_tree_content').length &&
  607. typeof storage.navTreePaths === 'undefined'
  608. ) {
  609. PMA_reloadNavigation();
  610. } else if (PMA_commonParams.get('server') === storage.server &&
  611. PMA_commonParams.get('token') === storage.token
  612. ) {
  613. // Reload the tree to the state before page refresh
  614. PMA_reloadNavigation(navFilterStateRestore, JSON.parse(storage.navTreePaths));
  615. } else {
  616. // If the user is different
  617. navTreeStateUpdate();
  618. }
  619. }
  620. });
  621. /**
  622. * Expands a node in navigation tree.
  623. *
  624. * @param $expandElem expander
  625. * @param callback callback function
  626. *
  627. * @returns void
  628. */
  629. function expandTreeNode ($expandElem, callback) {
  630. var $children = $expandElem.closest('li').children('div.list_container');
  631. var $icon = $expandElem.find('img');
  632. if ($expandElem.hasClass('loaded')) {
  633. if ($icon.is('.ic_b_plus')) {
  634. $icon.removeClass('ic_b_plus').addClass('ic_b_minus');
  635. $children.slideDown('fast');
  636. }
  637. if (callback && typeof callback === 'function') {
  638. callback.call();
  639. }
  640. $children.promise().done(navTreeStateUpdate);
  641. } else {
  642. var $throbber = $('#pma_navigation').find('.throbber')
  643. .first()
  644. .clone()
  645. .css({ visibility: 'visible', display: 'block' })
  646. .click(false);
  647. $icon.hide();
  648. $throbber.insertBefore($icon);
  649. loadChildNodes(true, $expandElem, function (data) {
  650. if (typeof data !== 'undefined' && data.success === true) {
  651. var $destination = $expandElem.closest('li');
  652. $icon.removeClass('ic_b_plus').addClass('ic_b_minus');
  653. $children = $destination.children('div.list_container');
  654. $children.slideDown('fast');
  655. if ($destination.find('ul > li').length === 1) {
  656. $destination.find('ul > li')
  657. .find('a.expander.container')
  658. .click();
  659. }
  660. if (callback && typeof callback === 'function') {
  661. callback.call();
  662. }
  663. PMA_showFullName($destination);
  664. } else {
  665. PMA_ajaxShowMessage(data.error, false);
  666. }
  667. $icon.show();
  668. $throbber.remove();
  669. $children.promise().done(navTreeStateUpdate);
  670. });
  671. }
  672. $expandElem.blur();
  673. }
  674. /**
  675. * Auto-scrolls the newly chosen database
  676. *
  677. * @param object $element The element to set to view
  678. * @param boolean $forceToTop Whether to force scroll to top
  679. *
  680. */
  681. function scrollToView ($element, $forceToTop) {
  682. navFilterStateRestore();
  683. var $container = $('#pma_navigation_tree_content');
  684. var elemTop = $element.offset().top - $container.offset().top;
  685. var textHeight = 20;
  686. var scrollPadding = 20; // extra padding from top of bottom when scrolling to view
  687. if (elemTop < 0 || $forceToTop) {
  688. $container.stop().animate({
  689. scrollTop: elemTop + $container.scrollTop() - scrollPadding
  690. });
  691. } else if (elemTop + textHeight > $container.height()) {
  692. $container.stop().animate({
  693. scrollTop: elemTop + textHeight - $container.height() + $container.scrollTop() + scrollPadding
  694. });
  695. }
  696. }
  697. /**
  698. * Expand the navigation and highlight the current database or table/view
  699. *
  700. * @returns void
  701. */
  702. function PMA_showCurrentNavigation () {
  703. var db = PMA_commonParams.get('db');
  704. var table = PMA_commonParams.get('table');
  705. $('#pma_navigation_tree')
  706. .find('li.selected')
  707. .removeClass('selected');
  708. if (db) {
  709. var $dbItem = findLoadedItem(
  710. $('#pma_navigation_tree').find('> div'), db, 'database', !table
  711. );
  712. if ($('#navi_db_select').length &&
  713. $('option:selected', $('#navi_db_select')).length
  714. ) {
  715. if (! PMA_selectCurrentDb()) {
  716. return;
  717. }
  718. // If loaded database in navigation is not same as current one
  719. if ($('#pma_navigation_tree_content').find('span.loaded_db:first').text()
  720. !== $('#navi_db_select').val()
  721. ) {
  722. loadChildNodes(false, $('option:selected', $('#navi_db_select')), function (data) {
  723. handleTableOrDb(table, $('#pma_navigation_tree_content'));
  724. var $children = $('#pma_navigation_tree_content').children('div.list_container');
  725. $children.promise().done(navTreeStateUpdate);
  726. });
  727. } else {
  728. handleTableOrDb(table, $('#pma_navigation_tree_content'));
  729. }
  730. } else if ($dbItem) {
  731. var $expander = $dbItem.children('div:first').children('a.expander');
  732. // if not loaded or loaded but collapsed
  733. if (! $expander.hasClass('loaded') ||
  734. $expander.find('img').is('.ic_b_plus')
  735. ) {
  736. expandTreeNode($expander, function () {
  737. handleTableOrDb(table, $dbItem);
  738. });
  739. } else {
  740. handleTableOrDb(table, $dbItem);
  741. }
  742. }
  743. } else if ($('#navi_db_select').length && $('#navi_db_select').val()) {
  744. $('#navi_db_select').val('').hide().trigger('change');
  745. }
  746. PMA_showFullName($('#pma_navigation_tree'));
  747. function handleTableOrDb (table, $dbItem) {
  748. if (table) {
  749. loadAndHighlightTableOrView($dbItem, table);
  750. } else {
  751. var $container = $dbItem.children('div.list_container');
  752. var $tableContainer = $container.children('ul').children('li.tableContainer');
  753. if ($tableContainer.length > 0) {
  754. var $expander = $tableContainer.children('div:first').children('a.expander');
  755. $tableContainer.addClass('selected');
  756. expandTreeNode($expander, function () {
  757. scrollToView($dbItem, true);
  758. });
  759. } else {
  760. scrollToView($dbItem, true);
  761. }
  762. }
  763. }
  764. function findLoadedItem ($container, name, clazz, doSelect) {
  765. var ret = false;
  766. $container.children('ul').children('li').each(function () {
  767. var $li = $(this);
  768. // this is a navigation group, recurse
  769. if ($li.is('.navGroup')) {
  770. var $container = $li.children('div.list_container');
  771. var $childRet = findLoadedItem(
  772. $container, name, clazz, doSelect
  773. );
  774. if ($childRet) {
  775. ret = $childRet;
  776. return false;
  777. }
  778. } else { // this is a real navigation item
  779. // name and class matches
  780. if (((clazz && $li.is('.' + clazz)) || ! clazz) &&
  781. $li.children('a').text() === name) {
  782. if (doSelect) {
  783. $li.addClass('selected');
  784. }
  785. // taverse up and expand and parent navigation groups
  786. $li.parents('.navGroup').each(function () {
  787. var $cont = $(this).children('div.list_container');
  788. if (! $cont.is(':visible')) {
  789. $(this)
  790. .children('div:first')
  791. .children('a.expander')
  792. .click();
  793. }
  794. });
  795. ret = $li;
  796. return false;
  797. }
  798. }
  799. });
  800. return ret;
  801. }
  802. function loadAndHighlightTableOrView ($dbItem, itemName) {
  803. var $container = $dbItem.children('div.list_container');
  804. var $expander;
  805. var $whichItem = isItemInContainer($container, itemName, 'li.table, li.view');
  806. // If item already there in some container
  807. if ($whichItem) {
  808. // get the relevant container while may also be a subcontainer
  809. var $relatedContainer = $whichItem.closest('li.subContainer').length
  810. ? $whichItem.closest('li.subContainer')
  811. : $dbItem;
  812. $whichItem = findLoadedItem(
  813. $relatedContainer.children('div.list_container'),
  814. itemName, null, true
  815. );
  816. // Show directly
  817. showTableOrView($whichItem, $relatedContainer.children('div:first').children('a.expander'));
  818. // else if item not there, try loading once
  819. } else {
  820. var $sub_containers = $dbItem.find('.subContainer');
  821. // If there are subContainers i.e. tableContainer or viewContainer
  822. if ($sub_containers.length > 0) {
  823. var $containers = [];
  824. $sub_containers.each(function (index) {
  825. $containers[index] = $(this);
  826. $expander = $containers[index]
  827. .children('div:first')
  828. .children('a.expander');
  829. if (! $expander.hasClass('loaded')) {
  830. loadAndShowTableOrView($expander, $containers[index], itemName);
  831. }
  832. });
  833. // else if no subContainers
  834. } else {
  835. $expander = $dbItem
  836. .children('div:first')
  837. .children('a.expander');
  838. if (! $expander.hasClass('loaded')) {
  839. loadAndShowTableOrView($expander, $dbItem, itemName);
  840. }
  841. }
  842. }
  843. }
  844. function loadAndShowTableOrView ($expander, $relatedContainer, itemName) {
  845. loadChildNodes(true, $expander, function (data) {
  846. var $whichItem = findLoadedItem(
  847. $relatedContainer.children('div.list_container'),
  848. itemName, null, true
  849. );
  850. if ($whichItem) {
  851. showTableOrView($whichItem, $expander);
  852. }
  853. });
  854. }
  855. function showTableOrView ($whichItem, $expander) {
  856. expandTreeNode($expander, function (data) {
  857. if ($whichItem) {
  858. scrollToView($whichItem, false);
  859. }
  860. });
  861. }
  862. function isItemInContainer ($container, name, clazz) {
  863. var $whichItem = null;
  864. $items = $container.find(clazz);
  865. var found = false;
  866. $items.each(function () {
  867. if ($(this).children('a').text() === name) {
  868. $whichItem = $(this);
  869. return false;
  870. }
  871. });
  872. return $whichItem;
  873. }
  874. }
  875. /**
  876. * Disable navigation panel settings
  877. *
  878. * @return void
  879. */
  880. function PMA_disableNaviSettings () {
  881. $('#pma_navigation_settings_icon').addClass('hide');
  882. $('#pma_navigation_settings').remove();
  883. }
  884. /**
  885. * Ensure that navigation panel settings is properly setup.
  886. * If not, set it up
  887. *
  888. * @return void
  889. */
  890. function PMA_ensureNaviSettings (selflink) {
  891. $('#pma_navigation_settings_icon').removeClass('hide');
  892. if (!$('#pma_navigation_settings').length) {
  893. var params = {
  894. getNaviSettings: true,
  895. server: PMA_commonParams.get('server'),
  896. };
  897. var url = $('#pma_navigation').find('a.navigation_url').attr('href');
  898. $.post(url, params, function (data) {
  899. if (typeof data !== 'undefined' && data.success) {
  900. $('#pma_navi_settings_container').html(data.message);
  901. setupRestoreField();
  902. setupValidation();
  903. setupConfigTabs();
  904. $('#pma_navigation_settings').find('form').attr('action', selflink);
  905. } else {
  906. PMA_ajaxShowMessage(data.error);
  907. }
  908. });
  909. } else {
  910. $('#pma_navigation_settings').find('form').attr('action', selflink);
  911. }
  912. }
  913. /**
  914. * Reloads the whole navigation tree while preserving its state
  915. *
  916. * @param function the callback function
  917. * @param Object stored navigation paths
  918. *
  919. * @return void
  920. */
  921. function PMA_reloadNavigation (callback, paths) {
  922. var params = {
  923. reload: true,
  924. no_debug: true,
  925. server: PMA_commonParams.get('server'),
  926. };
  927. paths = paths || traverseNavigationForPaths();
  928. $.extend(params, paths);
  929. if ($('#navi_db_select').length) {
  930. params.db = PMA_commonParams.get('db');
  931. requestNaviReload(params);
  932. return;
  933. }
  934. requestNaviReload(params);
  935. function requestNaviReload (params) {
  936. var url = $('#pma_navigation').find('a.navigation_url').attr('href');
  937. $.post(url, params, function (data) {
  938. if (typeof data !== 'undefined' && data.success) {
  939. $('#pma_navigation_tree').html(data.message).children('div').show();
  940. if ($('#pma_navigation_tree').hasClass('synced')) {
  941. PMA_selectCurrentDb();
  942. PMA_showCurrentNavigation();
  943. }
  944. // Fire the callback, if any
  945. if (typeof callback === 'function') {
  946. callback.call();
  947. }
  948. navTreeStateUpdate();
  949. } else {
  950. PMA_ajaxShowMessage(data.error);
  951. }
  952. });
  953. }
  954. }
  955. function PMA_selectCurrentDb () {
  956. var $naviDbSelect = $('#navi_db_select');
  957. if (!$naviDbSelect.length) {
  958. return false;
  959. }
  960. if (PMA_commonParams.get('db')) { // db selected
  961. $naviDbSelect.show();
  962. }
  963. $naviDbSelect.val(PMA_commonParams.get('db'));
  964. return $naviDbSelect.val() === PMA_commonParams.get('db');
  965. }
  966. /**
  967. * Handles any requests to change the page in a branch of a tree
  968. *
  969. * This can be called from link click or select change event handlers
  970. *
  971. * @param object $this A jQuery object that points to the element that
  972. * initiated the action of changing the page
  973. *
  974. * @return void
  975. */
  976. function PMA_navigationTreePagination ($this) {
  977. var $msgbox = PMA_ajaxShowMessage();
  978. var isDbSelector = $this.closest('div.pageselector').is('.dbselector');
  979. var url = 'navigation.php';
  980. var params = 'ajax_request=true';
  981. if ($this[0].tagName === 'A') {
  982. params += PMA_commonParams.get('arg_separator') + $this.getPostData();
  983. } else { // tagName === 'SELECT'
  984. params += PMA_commonParams.get('arg_separator') + $this.closest('form').serialize();
  985. }
  986. var searchClause = PMA_fastFilter.getSearchClause();
  987. if (searchClause) {
  988. params += PMA_commonParams.get('arg_separator') + 'searchClause=' + encodeURIComponent(searchClause);
  989. }
  990. if (isDbSelector) {
  991. params += PMA_commonParams.get('arg_separator') + 'full=true';
  992. } else {
  993. var searchClause2 = PMA_fastFilter.getSearchClause2($this);
  994. if (searchClause2) {
  995. params += PMA_commonParams.get('arg_separator') + 'searchClause2=' + encodeURIComponent(searchClause2);
  996. }
  997. }
  998. $.post(url, params, function (data) {
  999. if (typeof data !== 'undefined' && data.success) {
  1000. PMA_ajaxRemoveMessage($msgbox);
  1001. if (isDbSelector) {
  1002. var val = PMA_fastFilter.getSearchClause();
  1003. $('#pma_navigation_tree')
  1004. .html(data.message)
  1005. .children('div')
  1006. .show();
  1007. if (val) {
  1008. $('#pma_navigation_tree')
  1009. .find('li.fast_filter input.searchClause')
  1010. .val(val);
  1011. }
  1012. } else {
  1013. var $parent = $this.closest('div.list_container').parent();
  1014. var val = PMA_fastFilter.getSearchClause2($this);
  1015. $this.closest('div.list_container').html(
  1016. $(data.message).children().show()
  1017. );
  1018. if (val) {
  1019. $parent.find('li.fast_filter input.searchClause').val(val);
  1020. }
  1021. $parent.find('span.pos2_value:first').text(
  1022. $parent.find('span.pos2_value:last').text()
  1023. );
  1024. $parent.find('span.pos3_value:first').text(
  1025. $parent.find('span.pos3_value:last').text()
  1026. );
  1027. }
  1028. } else {
  1029. PMA_ajaxShowMessage(data.error);
  1030. PMA_handleRedirectAndReload(data);
  1031. }
  1032. navTreeStateUpdate();
  1033. });
  1034. }
  1035. /**
  1036. * @var ResizeHandler Custom object that manages the resizing of the navigation
  1037. *
  1038. * XXX: Must only be ever instanciated once
  1039. * XXX: Inside event handlers the 'this' object is accessed as 'event.data.resize_handler'
  1040. */
  1041. var ResizeHandler = function () {
  1042. /**
  1043. * @var int panel_width Used by the collapser to know where to go
  1044. * back to when uncollapsing the panel
  1045. */
  1046. this.panel_width = 0;
  1047. /**
  1048. * @var string left Used to provide support for RTL languages
  1049. */
  1050. this.left = $('html').attr('dir') === 'ltr' ? 'left' : 'right';
  1051. /**
  1052. * Adjusts the width of the navigation panel to the specified value
  1053. *
  1054. * @param int pos Navigation width in pixels
  1055. *
  1056. * @return void
  1057. */
  1058. this.setWidth = function (pos) {
  1059. if (typeof pos !== 'number') {
  1060. pos = 240;
  1061. }
  1062. var $resizer = $('#pma_navigation_resizer');
  1063. var resizer_width = $resizer.width();
  1064. var $collapser = $('#pma_navigation_collapser');
  1065. var windowWidth = $(window).width();
  1066. $('#pma_navigation').width(pos);
  1067. $('body').css('margin-' + this.left, pos + 'px');
  1068. // Issue #15127 : Adding fixed positioning to menubar
  1069. // Issue #15570 : Panels on homescreen go underneath of floating menubar
  1070. $('#floating_menubar')
  1071. .css('margin-' + this.left, $('#pma_navigation').width() + $('#pma_navigation_resizer').width())
  1072. .css(this.left, 0)
  1073. .css({
  1074. 'position': 'fixed',
  1075. 'top': 0,
  1076. 'width': '100%',
  1077. 'z-index': 99
  1078. })
  1079. .append($('#serverinfo'))
  1080. .append($('#topmenucontainer'));
  1081. // Allow the DOM to render, then adjust the padding on the body
  1082. setTimeout(function () {
  1083. $('body').css(
  1084. 'padding-top',
  1085. $('#floating_menubar').outerHeight(true)
  1086. );
  1087. }, 2);
  1088. $('#pma_console')
  1089. .css('margin-' + this.left, (pos + resizer_width) + 'px');
  1090. $resizer.css(this.left, pos + 'px');
  1091. if (pos === 0) {
  1092. $collapser
  1093. .css(this.left, pos + resizer_width)
  1094. .html(this.getSymbol(pos))
  1095. .prop('title', PMA_messages.strShowPanel);
  1096. } else if (windowWidth > 768) {
  1097. $collapser
  1098. .css(this.left, pos)
  1099. .html(this.getSymbol(pos))
  1100. .prop('title', PMA_messages.strHidePanel);
  1101. $('#pma_navigation_resizer').css({ 'width': '3px' });
  1102. } else {
  1103. $collapser
  1104. .css(this.left, windowWidth - 22)
  1105. .html(this.getSymbol(100))
  1106. .prop('title', PMA_messages.strHidePanel);
  1107. $('#pma_navigation').width(windowWidth);
  1108. $('body').css('margin-' + this.left, '0px');
  1109. $('#pma_navigation_resizer').css({ 'width': '0px' });
  1110. }
  1111. setTimeout(function () {
  1112. $(window).trigger('resize');
  1113. }, 4);
  1114. };
  1115. /**
  1116. * Returns the horizontal position of the mouse,
  1117. * relative to the outer side of the navigation panel
  1118. *
  1119. * @param int pos Navigation width in pixels
  1120. *
  1121. * @return void
  1122. */
  1123. this.getPos = function (event) {
  1124. var pos = event.pageX;
  1125. var windowWidth = $(window).width();
  1126. var windowScroll = $(window).scrollLeft();
  1127. pos = pos - windowScroll;
  1128. if (this.left !== 'left') {
  1129. pos = windowWidth - event.pageX;
  1130. }
  1131. if (pos < 0) {
  1132. pos = 0;
  1133. } else if (pos + 100 >= windowWidth) {
  1134. pos = windowWidth - 100;
  1135. } else {
  1136. this.panel_width = 0;
  1137. }
  1138. return pos;
  1139. };
  1140. /**
  1141. * Returns the HTML code for the arrow symbol used in the collapser
  1142. *
  1143. * @param int width The width of the panel
  1144. *
  1145. * @return string
  1146. */
  1147. this.getSymbol = function (width) {
  1148. if (this.left === 'left') {
  1149. if (width === 0) {
  1150. return '&rarr;';
  1151. } else {
  1152. return '&larr;';
  1153. }
  1154. } else {
  1155. if (width === 0) {
  1156. return '&larr;';
  1157. } else {
  1158. return '&rarr;';
  1159. }
  1160. }
  1161. };
  1162. /**
  1163. * Event handler for initiating a resize of the panel
  1164. *
  1165. * @param object e Event data (contains a reference to resizeHandler)
  1166. *
  1167. * @return void
  1168. */
  1169. this.mousedown = function (event) {
  1170. event.preventDefault();
  1171. $(document)
  1172. .on('mousemove', { 'resize_handler': event.data.resize_handler },
  1173. $.throttle(event.data.resize_handler.mousemove, 4))
  1174. .on('mouseup', { 'resize_handler': event.data.resize_handler },
  1175. event.data.resize_handler.mouseup);
  1176. $('body').css('cursor', 'col-resize');
  1177. };
  1178. /**
  1179. * Event handler for terminating a resize of the panel
  1180. *
  1181. * @param object e Event data (contains a reference to resizeHandler)
  1182. *
  1183. * @return void
  1184. */
  1185. this.mouseup = function (event) {
  1186. $('body').css('cursor', '');
  1187. configSet('NavigationWidth', event.data.resize_handler.getPos(event));
  1188. $('#topmenu').menuResizer('resize');
  1189. $(document)
  1190. .off('mousemove')
  1191. .off('mouseup');
  1192. };
  1193. /**
  1194. * Event handler for updating the panel during a resize operation
  1195. *
  1196. * @param object e Event data (contains a reference to resizeHandler)
  1197. *
  1198. * @return void
  1199. */
  1200. this.mousemove = function (event) {
  1201. event.preventDefault();
  1202. var pos = event.data.resize_handler.getPos(event);
  1203. event.data.resize_handler.setWidth(pos);
  1204. if ($('.sticky_columns').length !== 0) {
  1205. handleAllStickyColumns();
  1206. }
  1207. };
  1208. /**
  1209. * Event handler for collapsing the panel
  1210. *
  1211. * @param object e Event data (contains a reference to resizeHandler)
  1212. *
  1213. * @return void
  1214. */
  1215. this.collapse = function (event) {
  1216. event.preventDefault();
  1217. var panel_width = event.data.resize_handler.panel_width;
  1218. var width = $('#pma_navigation').width();
  1219. if (width === 0 && panel_width === 0) {
  1220. panel_width = 240;
  1221. }
  1222. configSet('NavigationWidth', panel_width);
  1223. event.data.resize_handler.setWidth(panel_width);
  1224. event.data.resize_handler.panel_width = width;
  1225. };
  1226. /**
  1227. * Event handler for resizing the navigation tree height on window resize
  1228. *
  1229. * @return void
  1230. */
  1231. this.treeResize = function (event) {
  1232. var $nav = $('#pma_navigation');
  1233. var $nav_tree = $('#pma_navigation_tree');
  1234. var $nav_header = $('#pma_navigation_header');
  1235. var $nav_tree_content = $('#pma_navigation_tree_content');
  1236. $nav_tree.height($nav.height() - $nav_header.height());
  1237. if ($nav_tree_content.length > 0) {
  1238. $nav_tree_content.height($nav_tree.height() - $nav_tree_content.position().top);
  1239. } else {
  1240. // TODO: in fast filter search response there is no #pma_navigation_tree_content, needs to be added in php
  1241. $nav_tree.css({
  1242. 'overflow-y': 'auto'
  1243. });
  1244. }
  1245. // Set content bottom space beacuse of console
  1246. $('body').css('margin-bottom', $('#pma_console').height() + 'px');
  1247. };
  1248. // Hide the pma_navigation initially when loaded on mobile
  1249. if ($(window).width() < 768) {
  1250. this.setWidth(0);
  1251. } else {
  1252. this.setWidth(configGet('NavigationWidth', false));
  1253. $('#topmenu').menuResizer('resize');
  1254. }
  1255. // Register the events for the resizer and the collapser
  1256. $(document).on('mousedown', '#pma_navigation_resizer', { 'resize_handler': this }, this.mousedown);
  1257. $(document).on('click', '#pma_navigation_collapser', { 'resize_handler': this }, this.collapse);
  1258. // Add the correct arrow symbol to the collapser
  1259. $('#pma_navigation_collapser').html(this.getSymbol($('#pma_navigation').width()));
  1260. // Fix navigation tree height
  1261. $(window).on('resize', this.treeResize);
  1262. // need to call this now and then, browser might decide
  1263. // to show/hide horizontal scrollbars depending on page content width
  1264. setInterval(this.treeResize, 2000);
  1265. this.treeResize();
  1266. }; // End of ResizeHandler
  1267. /**
  1268. * @var object PMA_fastFilter Handles the functionality that allows filtering
  1269. * of the items in a branch of the navigation tree
  1270. */
  1271. var PMA_fastFilter = {
  1272. /**
  1273. * Construct for the asynchronous fast filter functionality
  1274. *
  1275. * @param object $this A jQuery object pointing to the list container
  1276. * which is the nearest parent of the fast filter
  1277. * @param string searchClause The query string for the filter
  1278. *
  1279. * @return new PMA_fastFilter.filter object
  1280. */
  1281. filter: function ($this, searchClause) {
  1282. /**
  1283. * @var object $this A jQuery object pointing to the list container
  1284. * which is the nearest parent of the fast filter
  1285. */
  1286. this.$this = $this;
  1287. /**
  1288. * @var bool searchClause The query string for the filter
  1289. */
  1290. this.searchClause = searchClause;
  1291. /**
  1292. * @var object $clone A clone of the original contents
  1293. * of the navigation branch before
  1294. * the fast filter was applied
  1295. */
  1296. this.$clone = $this.clone();
  1297. /**
  1298. * @var object xhr A reference to the ajax request that is currently running
  1299. */
  1300. this.xhr = null;
  1301. /**
  1302. * @var int timeout Used to delay the request for asynchronous search
  1303. */
  1304. this.timeout = null;
  1305. var $filterInput = $this.find('li.fast_filter input.searchClause');
  1306. if ($filterInput.length !== 0 &&
  1307. $filterInput.val() !== '' &&
  1308. $filterInput.val() !== $filterInput[0].defaultValue
  1309. ) {
  1310. this.request();
  1311. }
  1312. },
  1313. /**
  1314. * Gets the query string from the database fast filter form
  1315. *
  1316. * @return string
  1317. */
  1318. getSearchClause: function () {
  1319. var retval = '';
  1320. var $input = $('#pma_navigation_tree')
  1321. .find('li.fast_filter.db_fast_filter input.searchClause');
  1322. if ($input.length && $input.val() !== $input[0].defaultValue) {
  1323. retval = $input.val();
  1324. }
  1325. return retval;
  1326. },
  1327. /**
  1328. * Gets the query string from a second level item's fast filter form
  1329. * The retrieval is done by trasversing the navigation tree backwards
  1330. *
  1331. * @return string
  1332. */
  1333. getSearchClause2: function ($this) {
  1334. var $filterContainer = $this.closest('div.list_container');
  1335. var $filterInput = $([]);
  1336. if ($filterContainer
  1337. .find('li.fast_filter:not(.db_fast_filter) input.searchClause')
  1338. .length !== 0) {
  1339. $filterInput = $filterContainer
  1340. .find('li.fast_filter:not(.db_fast_filter) input.searchClause');
  1341. }
  1342. var searchClause2 = '';
  1343. if ($filterInput.length !== 0 &&
  1344. $filterInput.first().val() !== $filterInput[0].defaultValue
  1345. ) {
  1346. searchClause2 = $filterInput.val();
  1347. }
  1348. return searchClause2;
  1349. },
  1350. /**
  1351. * @var hash events A list of functions that are bound to DOM events
  1352. * at the top of this file
  1353. */
  1354. events: {
  1355. focus: function (event) {
  1356. var $obj = $(this).closest('div.list_container');
  1357. if (! $obj.data('fastFilter')) {
  1358. $obj.data(
  1359. 'fastFilter',
  1360. new PMA_fastFilter.filter($obj, $(this).val())
  1361. );
  1362. }
  1363. if ($(this).val() === this.defaultValue) {
  1364. $(this).val('');
  1365. } else {
  1366. $(this).select();
  1367. }
  1368. },
  1369. blur: function (event) {
  1370. if ($(this).val() === '') {
  1371. $(this).val(this.defaultValue);
  1372. }
  1373. var $obj = $(this).closest('div.list_container');
  1374. if ($(this).val() === this.defaultValue && $obj.data('fastFilter')) {
  1375. $obj.data('fastFilter').restore();
  1376. }
  1377. },
  1378. keyup: function (event) {
  1379. var $obj = $(this).closest('div.list_container');
  1380. var str = '';
  1381. if ($(this).val() !== this.defaultValue && $(this).val() !== '') {
  1382. $obj.find('div.pageselector').hide();
  1383. str = $(this).val();
  1384. }
  1385. /**
  1386. * FIXME at the server level a value match is done while on
  1387. * the client side it is a regex match. These two should be aligned
  1388. */
  1389. // regex used for filtering.
  1390. var regex;
  1391. try {
  1392. regex = new RegExp(str, 'i');
  1393. } catch (err) {
  1394. return;
  1395. }
  1396. // this is the div that houses the items to be filtered by this filter.
  1397. var outerContainer;
  1398. if ($(this).closest('li.fast_filter').is('.db_fast_filter')) {
  1399. outerContainer = $('#pma_navigation_tree_content');
  1400. } else {
  1401. outerContainer = $obj;
  1402. }
  1403. // filters items that are directly under the div as well as grouped in
  1404. // groups. Does not filter child items (i.e. a database search does
  1405. // not filter tables)
  1406. var item_filter = function ($curr) {
  1407. $curr.children('ul').children('li.navGroup').each(function () {
  1408. $(this).children('div.list_container').each(function () {
  1409. item_filter($(this)); // recursive
  1410. });
  1411. });
  1412. $curr.children('ul').children('li').children('a').not('.container').each(function () {
  1413. if (regex.test($(this).text())) {
  1414. $(this).parent().show().removeClass('hidden');
  1415. } else {
  1416. $(this).parent().hide().addClass('hidden');
  1417. }
  1418. });
  1419. };
  1420. item_filter(outerContainer);
  1421. // hides containers that does not have any visible children
  1422. var container_filter = function ($curr) {
  1423. $curr.children('ul').children('li.navGroup').each(function () {
  1424. var $group = $(this);
  1425. $group.children('div.list_container').each(function () {
  1426. container_filter($(this)); // recursive
  1427. });
  1428. $group.show().removeClass('hidden');
  1429. if ($group.children('div.list_container').children('ul')
  1430. .children('li').not('.hidden').length === 0) {
  1431. $group.hide().addClass('hidden');
  1432. }
  1433. });
  1434. };
  1435. container_filter(outerContainer);
  1436. if ($(this).val() !== this.defaultValue && $(this).val() !== '') {
  1437. if (! $obj.data('fastFilter')) {
  1438. $obj.data(
  1439. 'fastFilter',
  1440. new PMA_fastFilter.filter($obj, $(this).val())
  1441. );
  1442. } else {
  1443. if (event.keyCode === 13) {
  1444. $obj.data('fastFilter').update($(this).val());
  1445. }
  1446. }
  1447. } else if ($obj.data('fastFilter')) {
  1448. $obj.data('fastFilter').restore(true);
  1449. }
  1450. // update filter state
  1451. var filterName;
  1452. if ($(this).attr('name') === 'searchClause2') {
  1453. filterName = $(this).siblings('input[name=aPath]').val();
  1454. } else {
  1455. filterName = 'dbFilter';
  1456. }
  1457. navFilterStateUpdate(filterName, $(this).val());
  1458. },
  1459. clear: function (event) {
  1460. event.stopPropagation();
  1461. // Clear the input and apply the fast filter with empty input
  1462. var filter = $(this).closest('div.list_container').data('fastFilter');
  1463. if (filter) {
  1464. filter.restore();
  1465. }
  1466. var value = $(this).prev()[0].defaultValue;
  1467. $(this).prev().val(value).trigger('keyup');
  1468. }
  1469. }
  1470. };
  1471. /**
  1472. * Handles a change in the search clause
  1473. *
  1474. * @param string searchClause The query string for the filter
  1475. *
  1476. * @return void
  1477. */
  1478. PMA_fastFilter.filter.prototype.update = function (searchClause) {
  1479. if (this.searchClause !== searchClause) {
  1480. this.searchClause = searchClause;
  1481. this.request();
  1482. }
  1483. };
  1484. /**
  1485. * After a delay of 250mS, initiates a request to retrieve search results
  1486. * Multiple calls to this function will always abort the previous request
  1487. *
  1488. * @return void
  1489. */
  1490. PMA_fastFilter.filter.prototype.request = function () {
  1491. var self = this;
  1492. if (self.$this.find('li.fast_filter').find('img.throbber').length === 0) {
  1493. self.$this.find('li.fast_filter').append(
  1494. $('<div class="throbber"></div>').append(
  1495. $('#pma_navigation_content')
  1496. .find('img.throbber')
  1497. .clone()
  1498. .css({ visibility: 'visible', display: 'block' })
  1499. )
  1500. );
  1501. }
  1502. if (self.xhr) {
  1503. self.xhr.abort();
  1504. }
  1505. var url = $('#pma_navigation').find('a.navigation_url').attr('href');
  1506. var params = self.$this.find('> ul > li > form.fast_filter').first().serialize();
  1507. if (self.$this.find('> ul > li > form.fast_filter:first input[name=searchClause]').length === 0) {
  1508. var $input = $('#pma_navigation_tree').find('li.fast_filter.db_fast_filter input.searchClause');
  1509. if ($input.length && $input.val() !== $input[0].defaultValue) {
  1510. params += PMA_commonParams.get('arg_separator') + 'searchClause=' + encodeURIComponent($input.val());
  1511. }
  1512. }
  1513. self.xhr = $.ajax({
  1514. url: url,
  1515. type: 'post',
  1516. dataType: 'json',
  1517. data: params,
  1518. complete: function (jqXHR, status) {
  1519. if (status !== 'abort') {
  1520. var data = JSON.parse(jqXHR.responseText);
  1521. self.$this.find('li.fast_filter').find('div.throbber').remove();
  1522. if (data && data.results) {
  1523. self.swap.apply(self, [data.message]);
  1524. }
  1525. }
  1526. }
  1527. });
  1528. };
  1529. /**
  1530. * Replaces the contents of the navigation branch with the search results
  1531. *
  1532. * @param string list The search results
  1533. *
  1534. * @return void
  1535. */
  1536. PMA_fastFilter.filter.prototype.swap = function (list) {
  1537. this.$this
  1538. .html($(list).html())
  1539. .children()
  1540. .show()
  1541. .end()
  1542. .find('li.fast_filter input.searchClause')
  1543. .val(this.searchClause);
  1544. this.$this.data('fastFilter', this);
  1545. };
  1546. /**
  1547. * Restores the navigation to the original state after the fast filter is cleared
  1548. *
  1549. * @param bool focus Whether to also focus the input box of the fast filter
  1550. *
  1551. * @return void
  1552. */
  1553. PMA_fastFilter.filter.prototype.restore = function (focus) {
  1554. if (this.$this.children('ul').first().hasClass('search_results')) {
  1555. this.$this.html(this.$clone.html()).children().show();
  1556. this.$this.data('fastFilter', this);
  1557. if (focus) {
  1558. this.$this.find('li.fast_filter input.searchClause').focus();
  1559. }
  1560. }
  1561. this.searchClause = '';
  1562. this.$this.find('div.pageselector').show();
  1563. this.$this.find('div.throbber').remove();
  1564. };
  1565. /**
  1566. * Show full name when cursor hover and name not shown completely
  1567. *
  1568. * @param object $containerELem Container element
  1569. *
  1570. * @return void
  1571. */
  1572. function PMA_showFullName ($containerELem) {
  1573. $containerELem.find('.hover_show_full').mouseenter(function () {
  1574. /** mouseenter */
  1575. var $this = $(this);
  1576. var thisOffset = $this.offset();
  1577. if ($this.text() === '') {
  1578. return;
  1579. }
  1580. var $parent = $this.parent();
  1581. if (($parent.offset().left + $parent.outerWidth())
  1582. < (thisOffset.left + $this.outerWidth())) {
  1583. var $fullNameLayer = $('#full_name_layer');
  1584. if ($fullNameLayer.length === 0) {
  1585. $('body').append('<div id="full_name_layer" class="hide"></div>');
  1586. $('#full_name_layer').mouseleave(function () {
  1587. /** mouseleave */
  1588. $(this).addClass('hide')
  1589. .removeClass('hovering');
  1590. }).mouseenter(function () {
  1591. /** mouseenter */
  1592. $(this).addClass('hovering');
  1593. });
  1594. $fullNameLayer = $('#full_name_layer');
  1595. }
  1596. $fullNameLayer.removeClass('hide');
  1597. $fullNameLayer.css({ left: thisOffset.left, top: thisOffset.top });
  1598. $fullNameLayer.html($this.clone());
  1599. setTimeout(function () {
  1600. if (! $fullNameLayer.hasClass('hovering')) {
  1601. $fullNameLayer.trigger('mouseleave');
  1602. }
  1603. }, 200);
  1604. }
  1605. });
  1606. }