navigation.js 37 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084
  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. * Executed on page load
  9. */
  10. $(function() {
  11. if (! $('#pma_navigation').length) {
  12. // Don't bother running any code if the navigation is not even on the page
  13. return;
  14. }
  15. // Do not let the page reload on submitting the fast filter
  16. $(document).on('submit', '.fast_filter', function(event) {
  17. event.preventDefault();
  18. });
  19. // Fire up the resize handlers
  20. new ResizeHandler();
  21. /**
  22. * opens/closes (hides/shows) tree elements
  23. * loads data via ajax
  24. */
  25. $('#pma_navigation_tree a.expander').live('click', function(event) {
  26. event.preventDefault();
  27. event.stopImmediatePropagation();
  28. var $icon = $(this).find('img');
  29. if ($icon.is('.ic_b_plus')) {
  30. expandTreeNode($(this));
  31. } else {
  32. collapseTreeNode($(this));
  33. }
  34. });
  35. /**
  36. * Register event handler for click on the reload
  37. * navigation icon at the top of the panel
  38. */
  39. $('#pma_navigation_reload').live('click', function (event) {
  40. event.preventDefault();
  41. $('#pma_navigation .throbber')
  42. .first()
  43. .css('visibility', 'visible');
  44. PMA_reloadNavigation();
  45. });
  46. /**
  47. * Bind all "fast filter" events
  48. */
  49. $('#pma_navigation_tree li.fast_filter span')
  50. .live('click', PMA_fastFilter.events.clear);
  51. $('#pma_navigation_tree li.fast_filter input.searchClause')
  52. .live('focus', PMA_fastFilter.events.focus)
  53. .live('blur', PMA_fastFilter.events.blur)
  54. .live('keyup', PMA_fastFilter.events.keyup);
  55. /**
  56. * Ajax handler for pagination
  57. */
  58. $('#pma_navigation_tree div.pageselector a.ajax').live('click', function (event) {
  59. event.preventDefault();
  60. PMA_navigationTreePagination($(this));
  61. });
  62. /**
  63. * Node highlighting
  64. */
  65. $('#pma_navigation_tree.highlight li:not(.fast_filter)').live(
  66. 'mouseover',
  67. function () {
  68. if ($('li:visible', this).length == 0) {
  69. $(this).addClass('activePointer');
  70. }
  71. }
  72. );
  73. $('#pma_navigation_tree.highlight li:not(.fast_filter)').live(
  74. 'mouseout',
  75. function () {
  76. $(this).removeClass('activePointer');
  77. }
  78. );
  79. /**
  80. * Jump to recent table
  81. */
  82. $('#recentTable').live('change', function() {
  83. if (this.value != '') {
  84. var arr = jQuery.parseJSON(this.value);
  85. var $form = $(this).closest('form');
  86. $form.find('input[name=db]').val(arr['db']);
  87. $form.find('input[name=table]').val(arr['table']);
  88. $form.submit();
  89. }
  90. });
  91. /** Create a Routine, Trigger or Event */
  92. $('li.new_procedure a.ajax, li.new_function a.ajax').live('click', function (event) {
  93. event.preventDefault();
  94. var dialog = new RTE.object('routine');
  95. dialog.editorDialog(1, $(this))
  96. });
  97. $('li.new_trigger a.ajax').live('click', function (event) {
  98. event.preventDefault();
  99. var dialog = new RTE.object('trigger');
  100. dialog.editorDialog(1, $(this))
  101. });
  102. $('li.new_event a.ajax').live('click', function (event) {
  103. event.preventDefault();
  104. var dialog = new RTE.object('event');
  105. dialog.editorDialog(1, $(this))
  106. });
  107. /** Edit Routines, Triggers and Events */
  108. $('li.procedure > a.ajax, li.function > a.ajax').live('click', function (event) {
  109. event.preventDefault();
  110. var dialog = new RTE.object('routine');
  111. dialog.editorDialog(0, $(this))
  112. });
  113. $('li.trigger > a.ajax').live('click', function (event) {
  114. event.preventDefault();
  115. var dialog = new RTE.object('trigger');
  116. dialog.editorDialog(0, $(this))
  117. });
  118. $('li.event > a.ajax').live('click', function (event) {
  119. event.preventDefault();
  120. var dialog = new RTE.object('event');
  121. dialog.editorDialog(0, $(this))
  122. });
  123. /** Export Routines, Triggers and Events */
  124. $('li.procedure a.ajax img, li.function a.ajax img, li.trigger a.ajax img, li.event a.ajax img').live('click', function (event) {
  125. event.preventDefault();
  126. var dialog = new RTE.object();
  127. dialog.exportDialog($(this).parent())
  128. });
  129. /** New index */
  130. $('li.new_index a.ajax').live('click', function (event) {
  131. event.preventDefault();
  132. var url = $(this).attr('href').substr(
  133. $(this).attr('href').indexOf('?') + 1
  134. ) + '&ajax_request=true';
  135. var title = PMA_messages['strAddIndex'];
  136. indexEditorDialog(url, title);
  137. });
  138. /** Edit index */
  139. $('li.index a.ajax').live('click', function (event) {
  140. event.preventDefault();
  141. var url = $(this).attr('href').substr(
  142. $(this).attr('href').indexOf('?') + 1
  143. ) + '&ajax_request=true';
  144. var title = PMA_messages['strEditIndex'];
  145. indexEditorDialog(url, title);
  146. });
  147. /** New view */
  148. $('li.new_view a.ajax').live('click', function (event) {
  149. event.preventDefault();
  150. PMA_createViewDialog($(this));
  151. });
  152. PMA_showCurrentNavigation();
  153. });
  154. /**
  155. * Expands a node in navigation tree.
  156. *
  157. * @param $expandElem expander
  158. * @param callback callback function
  159. *
  160. * @returns void
  161. */
  162. function expandTreeNode($expandElem, callback)
  163. {
  164. var $children = $expandElem.closest('li').children('div.list_container');
  165. var $icon = $expandElem.find('img');
  166. if ($expandElem.hasClass('loaded')) {
  167. if ($icon.is('.ic_b_plus')) {
  168. $icon.removeClass('ic_b_plus').addClass('ic_b_minus');
  169. $children.show('fast');
  170. }
  171. if (callback && typeof callback == 'function') {
  172. callback.call();
  173. }
  174. } else {
  175. var $throbber = $('#pma_navigation .throbber')
  176. .first()
  177. .clone()
  178. .css('visibility', 'visible')
  179. .click(false);
  180. $icon.hide();
  181. $throbber.insertBefore($icon);
  182. loadChildNodes($expandElem, function(data) {
  183. if (data.success === true) {
  184. var $destination = $expandElem.closest('li');
  185. $icon.removeClass('ic_b_plus').addClass('ic_b_minus');
  186. $destination
  187. .children('div.list_container')
  188. .show('fast');
  189. if ($destination.find('ul > li').length == 1) {
  190. $destination.find('ul > li')
  191. .find('a.expander.container')
  192. .click();
  193. }
  194. if (callback && typeof callback == 'function') {
  195. callback.call();
  196. }
  197. } else {
  198. PMA_ajaxShowMessage(data.error, false);
  199. }
  200. $icon.show();
  201. $throbber.remove();
  202. });
  203. }
  204. $expandElem.blur();
  205. }
  206. /**
  207. * Auto-scrolls the newly chosen database
  208. *
  209. * @param object $element The element to set to view
  210. * @param object $container The container srollable element
  211. *
  212. */
  213. function scrollToView($element, $container) {
  214. var elementOffset = $element.offset(),
  215. containerOffset = $container.offset();
  216. if(elementOffset != undefined && containerOffset != undefined) {
  217. var pushToOffset = elementOffset.top - containerOffset.top + $container.scrollTop();
  218. $('#pma_navigation_tree_content').stop().animate({
  219. scrollTop: pushToOffset
  220. });
  221. }
  222. }
  223. /**
  224. * Collapses a node in navigation tree.
  225. *
  226. * @param $expandElem expander
  227. *
  228. * @returns void
  229. */
  230. function collapseTreeNode($expandElem) {
  231. var $children = $expandElem.closest('li').children('div.list_container');
  232. var $icon = $expandElem.find('img');
  233. if ($expandElem.hasClass('loaded')) {
  234. if ($icon.is('.ic_b_minus')) {
  235. $icon.removeClass('ic_b_minus').addClass('ic_b_plus');
  236. $children.hide('fast');
  237. }
  238. }
  239. $expandElem.blur();
  240. }
  241. /**
  242. * Loads child items of a node and executes a given callback
  243. *
  244. * @param $expandElem expander
  245. * @param callback callback function
  246. *
  247. * @returns void
  248. */
  249. function loadChildNodes($expandElem, callback) {
  250. var $destination = $expandElem.closest('li');
  251. var searchClause = PMA_fastFilter.getSearchClause();
  252. var searchClause2 = PMA_fastFilter.getSearchClause2($expandElem);
  253. var params = {
  254. aPath: $expandElem.find('span.aPath').text(),
  255. vPath: $expandElem.find('span.vPath').text(),
  256. pos: $expandElem.find('span.pos').text(),
  257. pos2_name: $expandElem.find('span.pos2_name').text(),
  258. pos2_value: $expandElem.find('span.pos2_value').text(),
  259. searchClause: searchClause,
  260. searchClause2: searchClause2
  261. };
  262. var url = $('#pma_navigation').find('a.navigation_url').attr('href');
  263. $.get(url, params, function (data) {
  264. if (data.success === true) {
  265. $expandElem.addClass('loaded');
  266. $destination.find('div.list_container').remove(); // FIXME: Hack, there shouldn't be a list container there
  267. $destination.append(data.message);
  268. if (callback && typeof callback == 'function') {
  269. callback(data);
  270. }
  271. }
  272. });
  273. }
  274. /**
  275. * Expand the navigation and highlight the current database or table/view
  276. *
  277. * @returns void
  278. */
  279. function PMA_showCurrentNavigation()
  280. {
  281. var db = PMA_commonParams.get('db');
  282. var table = PMA_commonParams.get('table');
  283. $('#pma_navigation_tree')
  284. .find('li.selected')
  285. .removeClass('selected');
  286. if (db && table) { // if we are at the table/view level
  287. // open the database in the tree
  288. var $dbItem = highlightLoadedItem(
  289. $('#pma_navigation_tree > div'), db, 'database', false, false
  290. );
  291. if ($dbItem) {
  292. // open the table in the tree and select it
  293. var $expander = $dbItem.children('div:first').children('a.expander');
  294. // if not loaded or loaded but collapsed
  295. if (! $expander.hasClass('loaded')
  296. || $expander.find('img').is('.ic_b_plus')
  297. ) {
  298. expandTreeNode($expander, function() {
  299. loadAndHighlightTableOrView($dbItem, table);
  300. });
  301. } else {
  302. loadAndHighlightTableOrView($dbItem, table);
  303. }
  304. }
  305. } else if (db) { // if we are at the database level
  306. // open in the tree and select the database
  307. highlightLoadedItem(
  308. $('#pma_navigation_tree > div'), db, 'database', true, true
  309. );
  310. }
  311. function highlightLoadedItem($container, name, clazz, doSelect, doOpen) {
  312. var ret = false;
  313. $container.children('ul').children('li').each(function() {
  314. var $li = $(this);
  315. // this is a navigation group, recurse
  316. if ($li.is('.navGroup')) {
  317. var $container = $li.children('div.list_container');
  318. var $childRet = highlightLoadedItem(
  319. $container, name, clazz, doSelect, doOpen
  320. );
  321. if ($childRet) {
  322. ret = $childRet;
  323. return false;
  324. }
  325. } else { // this is a real navigation item
  326. // name and class matches
  327. if ($li.is('.' + clazz) && $li.children('a').text() == name) {
  328. if (doSelect) {
  329. $li.addClass('selected');
  330. if (! doOpen) { // if the node will be opened no point scrolling now
  331. scrollToView($li, $('#pma_navigation_tree_content'));
  332. }
  333. }
  334. if (doOpen) {
  335. var $expander = $li.find('div:first').children('a.expander');
  336. if ($expander.length > 0) {
  337. expandTreeNode($expander, function() {
  338. scrollToView($li, $('#pma_navigation_tree_content'));
  339. });
  340. }
  341. }
  342. // taverse up and expand and parent navigation groups
  343. $li.parents('.navGroup').each(function() {
  344. $cont = $(this).children('div.list_container');
  345. if (! $cont.is(':visible')) {
  346. $(this)
  347. .children('div:first')
  348. .children('a.expander')
  349. .click();
  350. }
  351. });
  352. ret = $li;
  353. return false;
  354. }
  355. }
  356. });
  357. return ret;
  358. }
  359. function loadAndHighlightTableOrView($dbItem, table) {
  360. var $container = $dbItem.children('div.list_container');
  361. var $tableContainer = $container
  362. .children('ul')
  363. .children('li.tableContainer');
  364. var $viewContainer = $container
  365. .children('ul')
  366. .children('li.viewContainer');
  367. if ($tableContainer.length > 0) {
  368. var $expander = $tableContainer
  369. .children('div:first')
  370. .children('a.expander');
  371. if (! $expander.hasClass('loaded') ) {
  372. loadChildNodes($expander, function(data) {
  373. highlightTableOrView($tableContainer, $viewContainer, table);
  374. });
  375. } else {
  376. highlightTableOrView($tableContainer, $viewContainer, table);
  377. }
  378. } else if ($viewContainer.length > 0) {
  379. highlightView($viewContainer, table);
  380. } else {
  381. // no containers, highlight the item
  382. highlightLoadedItem($container, table, 'table', true, false);
  383. }
  384. }
  385. function highlightTableOrView($tableContainer, $viewContainer, table)
  386. {
  387. if (isItemInContainer($tableContainer, table, 'table')) {
  388. var $expander = $tableContainer
  389. .children('div:first')
  390. .children('a.expander');
  391. if ($expander.find('img').is('.ic_b_plus')) {
  392. expandTreeNode($expander);
  393. }
  394. highlightLoadedItem(
  395. $tableContainer.children('div.list_container'),
  396. table, 'table', true, false
  397. );
  398. } else if ($viewContainer.length > 0) {
  399. highlightView($viewContainer, table);
  400. }
  401. }
  402. function isItemInContainer($container, name, clazz)
  403. {
  404. $items = $container.find('li.' + clazz);
  405. var found = false;
  406. $items.each(function() {
  407. if ($(this).children('a').text() == name) {
  408. found = true;
  409. return false;
  410. }
  411. });
  412. return found;
  413. }
  414. function highlightView($viewContainer, view) {
  415. var $expander = $viewContainer
  416. .children('div:first')
  417. .children('a.expander');
  418. if (! $expander.hasClass('loaded')
  419. || $expander.find('img').is('.ic_b_plus')
  420. ) {
  421. expandTreeNode($expander, function() {
  422. highlightLoadedItem(
  423. $viewContainer.children('div.list_container'),
  424. view, 'view', true, false
  425. );
  426. });
  427. } else {
  428. highlightLoadedItem(
  429. $viewContainer.children('div.list_container'),
  430. view, 'view', true, false
  431. );
  432. }
  433. }
  434. }
  435. /**
  436. * Reloads the whole navigation tree while preserving its state
  437. *
  438. * @param function the callback function
  439. * @return void
  440. */
  441. function PMA_reloadNavigation(callback) {
  442. var params = {
  443. reload: true,
  444. pos: $('#pma_navigation_tree').find('a.expander:first > span.pos').text()
  445. };
  446. // Traverse the navigation tree backwards to generate all the actual
  447. // and virtual paths, as well as the positions in the pagination at
  448. // various levels, if necessary.
  449. var count = 0;
  450. $('#pma_navigation_tree').find('a.expander:visible').each(function () {
  451. if ($(this).find('img').is('.ic_b_minus')
  452. && $(this).closest('li').find('div.list_container .ic_b_minus').length == 0
  453. ) {
  454. params['n' + count + '_aPath'] = $(this).find('span.aPath').text();
  455. params['n' + count + '_vPath'] = $(this).find('span.vPath').text();
  456. var pos2_name = $(this).find('span.pos2_name').text();
  457. if (! pos2_name) {
  458. pos2_name = $(this)
  459. .parent()
  460. .parent()
  461. .find('span.pos2_name:last')
  462. .text();
  463. }
  464. var pos2_value = $(this).find('span.pos2_value').text();
  465. if (! pos2_value) {
  466. pos2_value = $(this)
  467. .parent()
  468. .parent()
  469. .find('span.pos2_value:last')
  470. .text();
  471. }
  472. params['n' + count + '_pos2_name'] = pos2_name;
  473. params['n' + count + '_pos2_value'] = pos2_value;
  474. params['n' + count + '_pos3_name'] = $(this).find('span.pos3_name').text();
  475. params['n' + count + '_pos3_value'] = $(this).find('span.pos3_value').text();
  476. count++;
  477. }
  478. });
  479. var url = $('#pma_navigation').find('a.navigation_url').attr('href');
  480. $.post(url, params, function (data) {
  481. // Hide throbber if it's visible
  482. $('#pma_navigation .throbber')
  483. .first()
  484. .css('visibility', 'hidden');
  485. if (data.success) {
  486. $('#pma_navigation_tree').html(data.message).children('div').show();
  487. PMA_showCurrentNavigation();
  488. // Fire the callback, if any
  489. if (typeof callback === 'function') {
  490. callback.call();
  491. }
  492. } else {
  493. PMA_ajaxShowMessage(data.error);
  494. }
  495. });
  496. }
  497. /**
  498. * Handles any requests to change the page in a branch of a tree
  499. *
  500. * This can be called from link click or select change event handlers
  501. *
  502. * @param object $this A jQuery object that points to the element that
  503. * initiated the action of changing the page
  504. *
  505. * @return void
  506. */
  507. function PMA_navigationTreePagination($this)
  508. {
  509. var $msgbox = PMA_ajaxShowMessage();
  510. var isDbSelector = $this.closest('div.pageselector').is('.dbselector');
  511. if ($this[0].tagName == 'A') {
  512. var url = $this.attr('href');
  513. var params = 'ajax_request=true';
  514. } else { // tagName == 'SELECT'
  515. var url = 'navigation.php';
  516. var params = $this.closest("form").serialize() + '&ajax_request=true';
  517. }
  518. var searchClause = PMA_fastFilter.getSearchClause();
  519. if (searchClause) {
  520. params += '&searchClause=' + encodeURIComponent(searchClause);
  521. }
  522. if (isDbSelector) {
  523. params += '&full=true';
  524. } else {
  525. var searchClause2 = PMA_fastFilter.getSearchClause2($this);
  526. if (searchClause2) {
  527. params += '&searchClause2=' + encodeURIComponent(searchClause2);
  528. }
  529. }
  530. $.post(url, params, function (data) {
  531. PMA_ajaxRemoveMessage($msgbox);
  532. if (data.success) {
  533. if (isDbSelector) {
  534. var val = PMA_fastFilter.getSearchClause();
  535. $('#pma_navigation_tree')
  536. .html(data.message)
  537. .children('div')
  538. .show();
  539. if (val) {
  540. $('#pma_navigation_tree')
  541. .find('li.fast_filter input.searchClause')
  542. .val(val);
  543. }
  544. } else {
  545. var $parent = $this.closest('div.list_container').parent();
  546. var val = PMA_fastFilter.getSearchClause2($this);
  547. $this.closest('div.list_container').html(
  548. $(data.message).children().show()
  549. );
  550. if (val) {
  551. $parent.find('li.fast_filter input.searchClause').val(val);
  552. }
  553. $parent.find('span.pos2_value:first').text(
  554. $parent.find('span.pos2_value:last').text()
  555. );
  556. $parent.find('span.pos3_value:first').text(
  557. $parent.find('span.pos3_value:last').text()
  558. );
  559. }
  560. } else {
  561. PMA_ajaxShowMessage(data.error);
  562. }
  563. });
  564. }
  565. /**
  566. * @var ResizeHandler Custom object that manages the resizing of the navigation
  567. *
  568. * XXX: Must only be ever instanciated once
  569. * XXX: Inside event handlers the 'this' object is accessed as 'event.data.resize_handler'
  570. */
  571. var ResizeHandler = function () {
  572. /**
  573. * Whether the user has initiated a resize operation
  574. */
  575. this.active = false;
  576. /**
  577. * @var int panel_width Used by the collapser to know where to go
  578. * back to when uncollapsing the panel
  579. */
  580. this.panel_width = 0;
  581. /**
  582. * @var string left Used to provide support for RTL languages
  583. */
  584. this.left = $('html').attr('dir') == 'ltr' ? 'left' : 'right';
  585. /**
  586. * Adjusts the width of the navigation panel to the specified value
  587. *
  588. * @param int pos Navigation width in pixels
  589. *
  590. * @return void
  591. */
  592. this.setWidth = function (pos) {
  593. var $resizer = $('#pma_navigation_resizer');
  594. var resizer_width = $resizer.width();
  595. var $collapser = $('#pma_navigation_collapser');
  596. $('#pma_navigation').width(pos);
  597. $('body').css('margin-' + this.left, pos + 'px');
  598. $("#floating_menubar")
  599. .css('margin-' + this.left, (pos + resizer_width) + 'px');
  600. $resizer.css(this.left, pos + 'px');
  601. if (pos === 0) {
  602. $collapser
  603. .css(this.left, pos + resizer_width)
  604. .html(this.getSymbol(pos))
  605. .prop('title', PMA_messages['strShowPanel']);
  606. } else {
  607. $collapser
  608. .css(this.left, pos)
  609. .html(this.getSymbol(pos))
  610. .prop('title', PMA_messages['strHidePanel']);
  611. }
  612. setTimeout(function (){
  613. $(window).trigger('resize');
  614. }, 4);
  615. };
  616. /**
  617. * Returns the horizontal position of the mouse,
  618. * relative to the outer side of the navigation panel
  619. *
  620. * @param int pos Navigation width in pixels
  621. *
  622. * @return void
  623. */
  624. this.getPos = function (event) {
  625. var pos = event.pageX;
  626. var windowWidth = $(window).width();
  627. if (this.left != 'left') {
  628. pos = windowWidth - event.pageX;
  629. }
  630. if (pos < 0) {
  631. pos = 0;
  632. } else if (pos + 100 >= windowWidth) {
  633. pos = windowWidth - 100;
  634. } else {
  635. this.panel_width = 0;
  636. }
  637. return pos;
  638. };
  639. /**
  640. * Returns the HTML code for the arrow symbol used in the collapser
  641. *
  642. * @param int width The width of the panel
  643. *
  644. * @return string
  645. */
  646. this.getSymbol = function (width) {
  647. if (this.left == 'left') {
  648. if (width == 0) {
  649. return '&rarr;';
  650. } else {
  651. return '&larr;';
  652. }
  653. } else {
  654. if (width == 0) {
  655. return '&larr;';
  656. } else {
  657. return '&rarr;';
  658. }
  659. }
  660. };
  661. /**
  662. * Event handler for initiating a resize of the panel
  663. *
  664. * @param object e Event data (contains a reference to resizeHandler)
  665. *
  666. * @return void
  667. */
  668. this.mousedown = function (event) {
  669. event.preventDefault();
  670. event.data.resize_handler.active = true;
  671. $('body').css('cursor', 'col-resize');
  672. };
  673. /**
  674. * Event handler for terminating a resize of the panel
  675. *
  676. * @param object e Event data (contains a reference to resizeHandler)
  677. *
  678. * @return void
  679. */
  680. this.mouseup = function (event) {
  681. if (event.data.resize_handler.active) {
  682. event.data.resize_handler.active = false;
  683. $('body').css('cursor', '');
  684. $.cookie('pma_navi_width', event.data.resize_handler.getPos(event));
  685. $('#topmenu').menuResizer('resize');
  686. }
  687. };
  688. /**
  689. * Event handler for updating the panel during a resize operation
  690. *
  691. * @param object e Event data (contains a reference to resizeHandler)
  692. *
  693. * @return void
  694. */
  695. this.mousemove = function (event) {
  696. if (event.data && event.data.resize_handler && event.data.resize_handler.active) {
  697. event.preventDefault();
  698. var pos = event.data.resize_handler.getPos(event);
  699. event.data.resize_handler.setWidth(pos);
  700. }
  701. };
  702. /**
  703. * Event handler for collapsing the panel
  704. *
  705. * @param object e Event data (contains a reference to resizeHandler)
  706. *
  707. * @return void
  708. */
  709. this.collapse = function (event) {
  710. event.preventDefault();
  711. event.data.active = false;
  712. var panel_width = event.data.resize_handler.panel_width;
  713. var width = $('#pma_navigation').width();
  714. if (width === 0 && panel_width === 0) {
  715. panel_width = 240;
  716. }
  717. event.data.resize_handler.setWidth(panel_width);
  718. event.data.resize_handler.panel_width = width;
  719. };
  720. /**
  721. * Even thandler for resizing the navigation tree height on window resize
  722. *
  723. * @return void
  724. */
  725. this.treeResize = function (event) {
  726. var $nav = $("#pma_navigation"),
  727. $nav_tree = $("#pma_navigation_tree"),
  728. $nav_header = $("#pma_navigation_header"),
  729. $nav_tree_content = $("#pma_navigation_tree_content");
  730. $nav_tree.height($nav.height() - $nav_header.height());
  731. $nav_tree_content.height($nav_tree.height() - $nav_tree_content.position().top);
  732. };
  733. /* Initialisation section begins here */
  734. if ($.cookie('pma_navi_width')) {
  735. // If we have a cookie, set the width of the panel to its value
  736. var pos = Math.abs(parseInt($.cookie('pma_navi_width'), 10) || 0);
  737. this.setWidth(pos);
  738. $('#topmenu').menuResizer('resize');
  739. }
  740. // Register the events for the resizer and the collapser
  741. $('#pma_navigation_resizer')
  742. .live('mousedown', {'resize_handler':this}, this.mousedown);
  743. $(document)
  744. .bind('mouseup', {'resize_handler':this}, this.mouseup)
  745. .bind('mousemove', {'resize_handler':this}, $.throttle(this.mousemove, 4));
  746. var $collapser = $('#pma_navigation_collapser');
  747. $collapser.live('click', {'resize_handler':this}, this.collapse);
  748. // Add the correct arrow symbol to the collapser
  749. $collapser.html(this.getSymbol($('#pma_navigation').width()));
  750. // Fix navigation tree height
  751. $(window).on('resize', this.treeResize);
  752. // need to call this now and then, browser might decide
  753. // to show/hide horizontal scrollbars depending on page content width
  754. setInterval(this.treeResize, 2000);
  755. }; // End of ResizeHandler
  756. /**
  757. * @var object PMA_fastFilter Handles the functionality that allows filtering
  758. * of the items in a branch of the navigation tree
  759. */
  760. var PMA_fastFilter = {
  761. /**
  762. * Construct for the asynchronous fast filter functionality
  763. *
  764. * @param object $this A jQuery object pointing to the list container
  765. * which is the nearest parent of the fast filter
  766. * @param string searchClause The query string for the filter
  767. *
  768. * @return new PMA_fastFilter.filter object
  769. */
  770. filter: function ($this, searchClause) {
  771. /**
  772. * @var object $this A jQuery object pointing to the list container
  773. * which is the nearest parent of the fast filter
  774. */
  775. this.$this = $this;
  776. /**
  777. * @var bool searchClause The query string for the filter
  778. */
  779. this.searchClause = searchClause;
  780. /**
  781. * @var object $clone A clone of the original contents
  782. * of the navigation branch before
  783. * the fast filter was applied
  784. */
  785. this.$clone = $this.clone();
  786. /**
  787. * @var bool swapped Whether the user clicked on the "N other results" link
  788. */
  789. this.swapped = false;
  790. /**
  791. * @var object xhr A reference to the ajax request that is currently running
  792. */
  793. this.xhr = null;
  794. /**
  795. * @var int timeout Used to delay the request for asynchronous search
  796. */
  797. this.timeout = null;
  798. var $filterInput = $this.find('li.fast_filter input.searchClause');
  799. if ( $filterInput.length != 0
  800. && $filterInput.val() != ''
  801. && $filterInput.val() != $filterInput[0].defaultValue
  802. ) {
  803. this.request();
  804. }
  805. },
  806. /**
  807. * Gets the query string from the database fast filter form
  808. *
  809. * @return string
  810. */
  811. getSearchClause: function () {
  812. var retval = '';
  813. var $input = $('#pma_navigation_tree')
  814. .find('li.fast_filter.db_fast_filter input.searchClause');
  815. if ($input.length && $input.val() != $input[0].defaultValue) {
  816. retval = $input.val();
  817. }
  818. return retval;
  819. },
  820. /**
  821. * Gets the query string from a second level item's fast filter form
  822. * The retrieval is done by trasversing the navigation tree backwards
  823. *
  824. * @return string
  825. */
  826. getSearchClause2: function ($this) {
  827. var $filterContainer = $this.closest('div.list_container');
  828. var $filterInput = $([]);
  829. while (1) {
  830. if ($filterContainer.find('li.fast_filter:not(.db_fast_filter) input.searchClause').length != 0) {
  831. $filterInput = $filterContainer.find('li.fast_filter:not(.db_fast_filter) input.searchClause');
  832. break;
  833. } else if (! $filterContainer.is('div.list_container')) {
  834. break;
  835. }
  836. $filterContainer = $filterContainer
  837. .parent()
  838. .closest('div.list_container');
  839. }
  840. var searchClause2 = '';
  841. if ($filterInput.length != 0
  842. && $filterInput.first().val() != $filterInput[0].defaultValue
  843. ) {
  844. searchClause2 = $filterInput.val();
  845. }
  846. return searchClause2;
  847. },
  848. /**
  849. * @var hash events A list of functions that are bound to DOM events
  850. * at the top of this file
  851. */
  852. events: {
  853. focus: function (event) {
  854. var $obj = $(this).closest('div.list_container');
  855. if (! $obj.data('fastFilter')) {
  856. $obj.data(
  857. 'fastFilter',
  858. new PMA_fastFilter.filter($obj, $(this).val())
  859. );
  860. }
  861. if ($(this).val() == this.defaultValue) {
  862. $(this).val('');
  863. } else {
  864. $(this).select();
  865. }
  866. },
  867. blur: function (event) {
  868. if ($(this).val() == '') {
  869. $(this).val(this.defaultValue);
  870. }
  871. var $obj = $(this).closest('div.list_container');
  872. if ($(this).val() == this.defaultValue && $obj.data('fastFilter')) {
  873. $obj.data('fastFilter').restore();
  874. }
  875. },
  876. keyup: function (event) {
  877. var $obj = $(this).closest('div.list_container');
  878. var str = '';
  879. if ($(this).val() != this.defaultValue && $(this).val() != '') {
  880. $obj.find('div.pageselector').hide();
  881. str = $(this).val().toLowerCase();
  882. }
  883. $obj.find('li > a').not('.container').each(function () {
  884. if ($(this).text().toLowerCase().indexOf(str) != -1) {
  885. $(this).parent().show().removeClass('hidden');
  886. } else {
  887. $(this).parent().hide().addClass('hidden');
  888. }
  889. });
  890. var container_filter = function ($curr, str) {
  891. $curr.children('ul').children('li.navGroup').each(function() {
  892. var $group = $(this);
  893. $group.children('div.list_container').each(function() {
  894. container_filter($(this)); // recursive
  895. });
  896. $group.show().removeClass('hidden');
  897. if ($group.children('div.list_container').children('ul')
  898. .children('li').not('.hidden').length === 0) {
  899. $group.hide().addClass('hidden');
  900. }
  901. });
  902. };
  903. if ($(this).closest('li.fast_filter').is('.db_fast_filter')) {
  904. container_filter($('#pma_navigation_tree_content'), str);
  905. } else {
  906. container_filter($obj, str);
  907. }
  908. if ($(this).val() != this.defaultValue && $(this).val() != '') {
  909. if (! $obj.data('fastFilter')) {
  910. $obj.data(
  911. 'fastFilter',
  912. new PMA_fastFilter.filter($obj, $(this).val())
  913. );
  914. } else {
  915. $obj.data('fastFilter').update($(this).val());
  916. }
  917. } else if ($obj.data('fastFilter')) {
  918. $obj.data('fastFilter').restore(true);
  919. }
  920. },
  921. clear: function (event) {
  922. event.stopPropagation();
  923. // Clear the input and apply the fast filter with empty input
  924. var filter = $(this).closest('div.list_container').data('fastFilter');
  925. if (filter) {
  926. filter.restore();
  927. }
  928. var value = $(this).prev()[0].defaultValue;
  929. $(this).prev().val(value).trigger('keyup');
  930. }
  931. }
  932. };
  933. /**
  934. * Handles a change in the search clause
  935. *
  936. * @param string searchClause The query string for the filter
  937. *
  938. * @return void
  939. */
  940. PMA_fastFilter.filter.prototype.update = function (searchClause)
  941. {
  942. if (this.searchClause != searchClause) {
  943. this.searchClause = searchClause;
  944. this.$this.find('.moreResults').remove();
  945. this.request();
  946. }
  947. };
  948. /**
  949. * After a delay of 250mS, initiates a request to retrieve search results
  950. * Multiple calls to this function will always abort the previous request
  951. *
  952. * @return void
  953. */
  954. PMA_fastFilter.filter.prototype.request = function ()
  955. {
  956. var self = this;
  957. clearTimeout(self.timeout);
  958. if (self.$this.find('li.fast_filter').find('img.throbber').length == 0) {
  959. self.$this.find('li.fast_filter').append(
  960. $('<div class="throbber"></div>').append(
  961. $('#pma_navigation_content')
  962. .find('img.throbber')
  963. .clone()
  964. .css('visibility', 'visible')
  965. )
  966. );
  967. }
  968. self.timeout = setTimeout(function () {
  969. if (self.xhr) {
  970. self.xhr.abort();
  971. }
  972. var url = $('#pma_navigation').find('a.navigation_url').attr('href');
  973. var results = self.$this.find('li:not(.hidden):not(.fast_filter):not(.navGroup)').not('[class^=new]').length;
  974. var params = self.$this.find('> ul > li > form.fast_filter').first().serialize() + "&results=" + results;
  975. if (self.$this.find('> ul > li > form.fast_filter:first input[name=searchClause]').length == 0) {
  976. var $input = $('#pma_navigation_tree').find('li.fast_filter.db_fast_filter input.searchClause');
  977. if ($input.length && $input.val() != $input[0].defaultValue) {
  978. params += '&searchClause=' + encodeURIComponent($input.val());
  979. }
  980. }
  981. self.xhr = $.ajax({
  982. url: url,
  983. type: 'post',
  984. dataType: 'json',
  985. data: params,
  986. complete: function (jqXHR) {
  987. var data = $.parseJSON(jqXHR.responseText);
  988. self.$this.find('li.fast_filter').find('div.throbber').remove();
  989. if (data && data.results) {
  990. var $listItem = $('<li />', {'class':'moreResults'})
  991. .appendTo(self.$this.find('li.fast_filter'));
  992. var $link = $('<a />', {href:'#'})
  993. .text(data.results)
  994. .appendTo($listItem)
  995. .click(function (event) {
  996. event.preventDefault();
  997. self.swap.apply(self, [data.message]);
  998. });
  999. }
  1000. }
  1001. });
  1002. }, 250);
  1003. };
  1004. /**
  1005. * Replaces the contents of the navigation branch with the search results
  1006. *
  1007. * @param string list The search results
  1008. *
  1009. * @return void
  1010. */
  1011. PMA_fastFilter.filter.prototype.swap = function (list)
  1012. {
  1013. this.swapped = true;
  1014. this.$this
  1015. .html($(list).html())
  1016. .children()
  1017. .show()
  1018. .end()
  1019. .find('li.fast_filter input.searchClause')
  1020. .val(this.searchClause);
  1021. this.$this.data('fastFilter', this);
  1022. };
  1023. /**
  1024. * Restores the navigation to the original state after the fast filter is cleared
  1025. *
  1026. * @param bool focus Whether to also focus the input box of the fast filter
  1027. *
  1028. * @return void
  1029. */
  1030. PMA_fastFilter.filter.prototype.restore = function (focus)
  1031. {
  1032. if (this.swapped) {
  1033. this.swapped = false;
  1034. this.$this.html(this.$clone.html()).children().show();
  1035. this.$this.data('fastFilter', this);
  1036. if (focus) {
  1037. this.$this.find('li.fast_filter input.searchClause').focus();
  1038. }
  1039. }
  1040. this.searchClause = '';
  1041. this.$this.find('.moreResults').remove();
  1042. this.$this.find('div.pageselector').show();
  1043. this.$this.find('div.throbber').remove();
  1044. };