module_filter_tab.js 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562
  1. (function ($) {
  2. Drupal.ModuleFilter.tabs = {};
  3. Drupal.ModuleFilter.enabling = {};
  4. Drupal.ModuleFilter.disabling = {};
  5. Drupal.ModuleFilter.jQueryIsNewer = function() {
  6. if (Drupal.ModuleFilter.jQueryNewer == undefined) {
  7. var v1parts = $.fn.jquery.split('.');
  8. var v2parts = new Array('1', '4', '4');
  9. for (var i = 0; i < v1parts.length; ++i) {
  10. if (v2parts.length == i) {
  11. Drupal.ModuleFilter.jQueryNewer = true;
  12. return Drupal.ModuleFilter.jQueryNewer;
  13. }
  14. if (v1parts[i] == v2parts[i]) {
  15. continue;
  16. }
  17. else if (v1parts[i] > v2parts[i]) {
  18. Drupal.ModuleFilter.jQueryNewer = true;
  19. return Drupal.ModuleFilter.jQueryNewer;
  20. }
  21. else {
  22. Drupal.ModuleFilter.jQueryNewer = false;
  23. return Drupal.ModuleFilter.jQueryNewer;
  24. }
  25. }
  26. if (v1parts.length != v2parts.length) {
  27. Drupal.ModuleFilter.jQueryNewer = false;
  28. return Drupal.ModuleFilter.jQueryNewer;
  29. }
  30. Drupal.ModuleFilter.jQueryNewer = false;
  31. }
  32. return Drupal.ModuleFilter.jQueryNewer;
  33. };
  34. Drupal.behaviors.moduleFilterTabs = {
  35. attach: function(context) {
  36. if (Drupal.settings.moduleFilter.tabs) {
  37. $('#module-filter-wrapper table:not(.sticky-header)', context).once('module-filter-tabs', function() {
  38. var $modules = $('#module-filter-modules');
  39. var moduleFilter = $('input[name="module_filter[name]"]').data('moduleFilter');
  40. var table = $(this);
  41. $('thead', table).show();
  42. // Remove package header rows.
  43. $('tr.admin-package-header', table).remove();
  44. var $tabsWrapper = $('<div id="module-filter-tabs"></div>');
  45. // Build tabs from package title rows.
  46. var tabs = '<ul>';
  47. for (var i in Drupal.settings.moduleFilter.packageIDs) {
  48. var id = Drupal.settings.moduleFilter.packageIDs[i];
  49. var name = id;
  50. var tabClass = 'project-tab';
  51. var title = null;
  52. var summary = (Drupal.settings.moduleFilter.countEnabled) ? '<span class="count">' + Drupal.ModuleFilter.countSummary(id) + '</span>' : '';
  53. switch (id) {
  54. case 'all':
  55. name = Drupal.t('All');
  56. break;
  57. case 'new':
  58. name = Drupal.t('New');
  59. title = Drupal.t('Modules installed within the last week.');
  60. if (Drupal.settings.moduleFilter.enabledCounts['new'].total == 0) {
  61. tabClass += ' disabled';
  62. summary += '<span>' + Drupal.t('No modules added within the last week.') + '</span>';
  63. }
  64. break;
  65. case 'recent':
  66. name = Drupal.t('Recent');
  67. title = Drupal.t('Modules enabled/disabled within the last week.');
  68. if (Drupal.settings.moduleFilter.enabledCounts['recent'].total == 0) {
  69. tabClass += ' disabled';
  70. summary += '<span>' + Drupal.t('No modules were enabled or disabled within the last week.') + '</span>';
  71. }
  72. break;
  73. default:
  74. var $row = $('#' + id + '-package');
  75. name = $.trim($row.text());
  76. $row.remove();
  77. break;
  78. }
  79. tabs += '<li id="' + id + '-tab" class="' + tabClass + '"><a href="#' + id + '" class="overlay-exclude"' + (title ? ' title="' + title + '"' : '') + '><strong>' + name + '</strong><span class="summary">' + summary + '</span></a></li>';
  80. }
  81. tabs += '</ul>';
  82. $tabsWrapper.append(tabs);
  83. $modules.before($tabsWrapper);
  84. // Index tabs.
  85. $('#module-filter-tabs li').each(function() {
  86. var $tab = $(this);
  87. var id = $tab.attr('id');
  88. Drupal.ModuleFilter.tabs[id] = new Drupal.ModuleFilter.Tab($tab, id);
  89. });
  90. $('tbody td.checkbox input', $modules).change(function() {
  91. var $checkbox = $(this);
  92. var key = $checkbox.parents('tr').data('indexKey');
  93. moduleFilter.index[key].status = $checkbox.is(':checked');
  94. if (Drupal.settings.moduleFilter.visualAid) {
  95. var type = ($checkbox.is(':checked')) ? 'enable' : 'disable';
  96. Drupal.ModuleFilter.updateVisualAid(type, $checkbox.parents('tr'));
  97. }
  98. });
  99. // Sort rows.
  100. var rows = $('tbody tr.module', table).get();
  101. rows.sort(function(a, b) {
  102. var compA = $('td:nth(1)', a).text().toLowerCase();
  103. var compB = $('td:nth(1)', b).text().toLowerCase();
  104. return (compA < compB) ? -1 : (compA > compB) ? 1 : 0;
  105. });
  106. $.each(rows, function(idx, itm) { table.append(itm); });
  107. // Re-stripe rows.
  108. $('tr.module', table)
  109. .removeClass('odd even')
  110. .filter(':odd').addClass('even').end()
  111. .filter(':even').addClass('odd');
  112. moduleFilter.adjustHeight();
  113. moduleFilter.element.bind('moduleFilter:start', function() {
  114. moduleFilter.tabResults = {
  115. 'all-tab': { items: {}, count: 0 },
  116. 'recent-tab': { items: {}, count: 0 },
  117. 'new-tab': { items: {}, count: 0 }
  118. };
  119. // Empty result info from tabs.
  120. for (var i in Drupal.ModuleFilter.tabs) {
  121. if (Drupal.ModuleFilter.tabs[i].resultInfo != undefined) {
  122. Drupal.ModuleFilter.tabs[i].resultInfo.empty();
  123. }
  124. }
  125. });
  126. moduleFilter.element.bind('moduleFilter:finish', function(e, data) {
  127. $.each(moduleFilter.index, function(key, item) {
  128. if (!item.element.hasClass('js-hide')) {
  129. var id = Drupal.ModuleFilter.getTabID(item.element);
  130. if (moduleFilter.tabResults[id] == undefined) {
  131. moduleFilter.tabResults[id] = { items: {}, count: 0 };
  132. }
  133. if (moduleFilter.tabResults[id].items[item.key] == undefined) {
  134. // All tab
  135. moduleFilter.tabResults['all-tab'].count++;
  136. // Recent tab
  137. if (item.element.hasClass('recent-module')) {
  138. moduleFilter.tabResults['recent-tab'].count++;
  139. }
  140. // New tab
  141. if (item.element.hasClass('new-module')) {
  142. moduleFilter.tabResults['new-tab'].count++;
  143. }
  144. moduleFilter.tabResults[id].items[item.key] = item;
  145. moduleFilter.tabResults[id].count++;
  146. }
  147. if (Drupal.ModuleFilter.activeTab != undefined && Drupal.ModuleFilter.activeTab.id != 'all-tab') {
  148. if ((Drupal.ModuleFilter.activeTab.id == 'recent-tab' && !item.element.hasClass('recent-module')) || (Drupal.ModuleFilter.activeTab.id == 'new-tab' && !item.element.hasClass('new-module')) || (Drupal.ModuleFilter.activeTab.id != 'recent-tab' && Drupal.ModuleFilter.activeTab.id != 'new-tab' && id != Drupal.ModuleFilter.activeTab.id)) {
  149. // The item is not in the active tab, so hide it.
  150. item.element.addClass('js-hide');
  151. }
  152. }
  153. }
  154. });
  155. if (Drupal.settings.moduleFilter.visualAid) {
  156. if (moduleFilter.text) {
  157. // Add result info to tabs.
  158. for (var id in moduleFilter.tabResults) {
  159. var tab = Drupal.ModuleFilter.tabs[id];
  160. if (tab.resultInfo == undefined) {
  161. var resultInfo = '<span class="result-info"></span>'
  162. $('a', tab.element).prepend(resultInfo);
  163. tab.resultInfo = $('span.result-info', tab.element);
  164. }
  165. tab.resultInfo.append(moduleFilter.tabResults[id].count);
  166. }
  167. if (Drupal.settings.moduleFilter.hideEmptyTabs) {
  168. for (var id in Drupal.ModuleFilter.tabs) {
  169. if (moduleFilter.tabResults[id] != undefined) {
  170. Drupal.ModuleFilter.tabs[id].element.show();
  171. }
  172. else if (Drupal.ModuleFilter.activeTab == undefined || Drupal.ModuleFilter.activeTab.id != id) {
  173. Drupal.ModuleFilter.tabs[id].element.hide();
  174. }
  175. }
  176. }
  177. }
  178. else {
  179. // Make sure all tabs are visible.
  180. if (Drupal.settings.moduleFilter.hideEmptyTabs) {
  181. $('#module-filter-tabs li').show();
  182. }
  183. }
  184. }
  185. if ((Drupal.ModuleFilter.activeTab != undefined && (moduleFilter.tabResults[Drupal.ModuleFilter.activeTab.id] == undefined || moduleFilter.tabResults[Drupal.ModuleFilter.activeTab.id].count <= 0))) {
  186. // The current tab contains no results.
  187. moduleFilter.results = 0;
  188. }
  189. moduleFilter.adjustHeight();
  190. });
  191. if (Drupal.settings.moduleFilter.useURLFragment) {
  192. $(window).bind('hashchange.module-filter', $.proxy(Drupal.ModuleFilter, 'eventHandlerOperateByURLFragment')).triggerHandler('hashchange.module-filter');
  193. }
  194. else {
  195. Drupal.ModuleFilter.selectTab();
  196. }
  197. if (Drupal.settings.moduleFilter.useSwitch) {
  198. $('td.checkbox div.form-item').hide();
  199. $('td.checkbox').each(function(i) {
  200. var $cell = $(this);
  201. var $checkbox = $(':checkbox', $cell);
  202. var $switch = $('.toggle-enable', $cell);
  203. $switch.removeClass('js-hide').click(function() {
  204. if (!$(this).hasClass('disabled')) {
  205. if (Drupal.ModuleFilter.jQueryIsNewer()) {
  206. $checkbox.click();
  207. }
  208. else {
  209. $checkbox.click().change();
  210. }
  211. }
  212. });
  213. $checkbox.click(function() {
  214. if (!$switch.hasClass('disabled')) {
  215. $switch.toggleClass('off');
  216. }
  217. });
  218. });
  219. }
  220. var $tabs = $('#module-filter-tabs');
  221. function getParentTopOffset($obj, offset) {
  222. var $parent = $obj.offsetParent();
  223. if ($obj[0] != $parent[0]) {
  224. offset += $parent.position().top;
  225. return getParentTopOffset($parent, offset);
  226. }
  227. return offset;
  228. }
  229. var tabsTopOffset = null;
  230. function getParentsTopOffset() {
  231. if (tabsTopOffset === null) {
  232. tabsTopOffset = getParentTopOffset($tabs.parent(), 0);
  233. }
  234. return tabsTopOffset;
  235. }
  236. function viewportTop() {
  237. var top = $(window).scrollTop();
  238. return top;
  239. }
  240. function viewportBottom() {
  241. var top = $(window).scrollTop();
  242. var bottom = top + $(window).height();
  243. bottom -= $('#page-actions').height();
  244. return bottom;
  245. }
  246. function fixToTop(top) {
  247. if ($tabs.hasClass('bottom-fixed')) {
  248. $tabs.css({
  249. 'position': 'absolute',
  250. 'top': $tabs.position().top - getParentsTopOffset(),
  251. 'bottom': 'auto'
  252. });
  253. $tabs.removeClass('bottom-fixed');
  254. }
  255. if (($tabs.css('position') == 'absolute' && $tabs.offset().top - top >= 0) || ($tabs.css('position') != 'absolute' && $tabs.offset().top - top <= 0)) {
  256. $tabs.addClass('top-fixed');
  257. $tabs.attr('style', '');
  258. }
  259. }
  260. function fixToBottom(bottom) {
  261. if ($tabs.hasClass('top-fixed')) {
  262. $tabs.css({
  263. 'position': 'absolute',
  264. 'top': $tabs.position().top - getParentsTopOffset(),
  265. 'bottom': 'auto'
  266. });
  267. $tabs.removeClass('top-fixed');
  268. }
  269. if ($tabs.offset().top + $tabs.height() - bottom <= 0) {
  270. $tabs.addClass('bottom-fixed');
  271. var style = '';
  272. var pageActionsHeight = $('#page-actions').height();
  273. if (pageActionsHeight > 0) {
  274. style = 'bottom: ' + pageActionsHeight + 'px';
  275. }
  276. else if (Drupal.settings.moduleFilter.dynamicPosition) {
  277. // style = 'bottom: ' + $('#module-filter-submit', $tabs).height() + 'px';
  278. }
  279. $tabs.attr('style', style);
  280. }
  281. }
  282. var lastTop = 0;
  283. $(window).scroll(function() {
  284. var top = viewportTop();
  285. var bottom = viewportBottom();
  286. if ($modules.offset().top >= top) {
  287. $tabs.removeClass('top-fixed').attr('style', '');
  288. }
  289. else {
  290. if (top > lastTop) { // Downward scroll.
  291. if ($tabs.height() > bottom - top) {
  292. fixToBottom(bottom);
  293. }
  294. else {
  295. fixToTop(top);
  296. }
  297. }
  298. else { // Upward scroll.
  299. fixToTop(top);
  300. }
  301. }
  302. lastTop = top;
  303. });
  304. moduleFilter.adjustHeight();
  305. });
  306. }
  307. }
  308. };
  309. Drupal.ModuleFilter.Tab = function(element, id) {
  310. var self = this;
  311. this.id = id;
  312. this.hash = id.substring(0, id.length - 4);
  313. this.element = element;
  314. $('a', this.element).click(function() {
  315. if (!Drupal.settings.moduleFilter.useURLFragment) {
  316. var hash = (!self.element.hasClass('selected')) ? self.hash : 'all';
  317. Drupal.ModuleFilter.selectTab(hash);
  318. return false;
  319. }
  320. if (self.element.hasClass('selected')) {
  321. // Clear the active tab.
  322. window.location.hash = 'all';
  323. return false;
  324. }
  325. });
  326. $('tr.' + this.id, $('#system-modules')).hover(
  327. function() {
  328. self.element.addClass('suggest');
  329. },
  330. function() {
  331. self.element.removeClass('suggest');
  332. }
  333. );
  334. };
  335. Drupal.ModuleFilter.selectTab = function(hash) {
  336. if (!hash || Drupal.ModuleFilter.tabs[hash + '-tab'] == undefined || Drupal.settings.moduleFilter.enabledCounts[hash].total == 0) {
  337. if (Drupal.settings.moduleFilter.rememberActiveTab) {
  338. var activeTab = Drupal.ModuleFilter.getState('activeTab');
  339. if (activeTab && Drupal.ModuleFilter.tabs[activeTab + '-tab'] != undefined) {
  340. hash = activeTab;
  341. }
  342. }
  343. if (!hash) {
  344. hash = 'all';
  345. }
  346. }
  347. if (Drupal.ModuleFilter.activeTab != undefined) {
  348. Drupal.ModuleFilter.activeTab.element.removeClass('selected');
  349. }
  350. Drupal.ModuleFilter.activeTab = Drupal.ModuleFilter.tabs[hash + '-tab'];
  351. Drupal.ModuleFilter.activeTab.element.addClass('selected');
  352. var moduleFilter = $('input[name="module_filter[name]"]').data('moduleFilter');
  353. var filter = moduleFilter.applyFilter();
  354. if (!Drupal.ModuleFilter.modulesTop) {
  355. Drupal.ModuleFilter.modulesTop = $('#module-filter-modules').offset().top;
  356. }
  357. else {
  358. // Calculate header offset; this is important in case the site is using
  359. // admin_menu module which has fixed positioning and is on top of everything
  360. // else.
  361. var headerOffset = Drupal.settings.tableHeaderOffset ? eval(Drupal.settings.tableHeaderOffset + '()') : 0;
  362. // Scroll back to top of #module-filter-modules.
  363. $('html, body').animate({
  364. scrollTop: Drupal.ModuleFilter.modulesTop - headerOffset
  365. }, 500);
  366. // $('html, body').scrollTop(Drupal.ModuleFilter.modulesTop);
  367. }
  368. Drupal.ModuleFilter.setState('activeTab', hash);
  369. };
  370. Drupal.ModuleFilter.eventHandlerOperateByURLFragment = function(event) {
  371. var hash = $.param.fragment();
  372. Drupal.ModuleFilter.selectTab(hash);
  373. };
  374. Drupal.ModuleFilter.countSummary = function(id) {
  375. return Drupal.t('@enabled of @total', { '@enabled': Drupal.settings.moduleFilter.enabledCounts[id].enabled, '@total': Drupal.settings.moduleFilter.enabledCounts[id].total });
  376. };
  377. Drupal.ModuleFilter.Tab.prototype.updateEnabling = function(name, remove) {
  378. this.enabling = this.enabling || {};
  379. if (!remove) {
  380. this.enabling[name] = name;
  381. }
  382. else {
  383. delete this.enabling[name];
  384. }
  385. };
  386. Drupal.ModuleFilter.Tab.prototype.updateDisabling = function(name, remove) {
  387. this.disabling = this.disabling || {};
  388. if (!remove) {
  389. this.disabling[name] = name;
  390. }
  391. else {
  392. delete this.disabling[name];
  393. }
  394. };
  395. Drupal.ModuleFilter.Tab.prototype.updateVisualAid = function() {
  396. var visualAid = '';
  397. var enabling = new Array();
  398. var disabling = new Array();
  399. if (this.enabling != undefined) {
  400. for (var i in this.enabling) {
  401. enabling.push(this.enabling[i]);
  402. }
  403. if (enabling.length > 0) {
  404. enabling.sort();
  405. visualAid += '<span class="enabling">+' + enabling.join('</span>, <span class="enabling">') + '</span>';
  406. }
  407. }
  408. if (this.disabling != undefined) {
  409. for (var i in this.disabling) {
  410. disabling.push(this.disabling[i]);
  411. }
  412. if (disabling.length > 0) {
  413. disabling.sort();
  414. if (enabling.length > 0) {
  415. visualAid += '<br />';
  416. }
  417. visualAid += '<span class="disabling">-' + disabling.join('</span>, <span class="disabling">') + '</span>';
  418. }
  419. }
  420. if (this.visualAid == undefined) {
  421. $('a span.summary', this.element).append('<span class="visual-aid"></span>');
  422. this.visualAid = $('span.visual-aid', this.element);
  423. }
  424. this.visualAid.empty().append(visualAid);
  425. };
  426. Drupal.ModuleFilter.getTabID = function($row) {
  427. var id = $row.data('moduleFilterTabID');
  428. if (!id) {
  429. // Find the tab ID.
  430. var classes = $row.attr('class').split(' ');
  431. for (var i in classes) {
  432. if (Drupal.ModuleFilter.tabs[classes[i]] != undefined) {
  433. id = classes[i];
  434. break;
  435. }
  436. }
  437. $row.data('moduleFilterTabID', id);
  438. }
  439. return id;
  440. };
  441. Drupal.ModuleFilter.updateVisualAid = function(type, $row) {
  442. var id = Drupal.ModuleFilter.getTabID($row);
  443. if (!id) {
  444. return false;
  445. }
  446. var tab = Drupal.ModuleFilter.tabs[id];
  447. var name = $('td:nth(1) strong', $row).text();
  448. switch (type) {
  449. case 'enable':
  450. if (Drupal.ModuleFilter.disabling[id + name] != undefined) {
  451. delete Drupal.ModuleFilter.disabling[id + name];
  452. tab.updateDisabling(name, true);
  453. $row.removeClass('disabling');
  454. }
  455. else {
  456. Drupal.ModuleFilter.enabling[id + name] = name;
  457. tab.updateEnabling(name);
  458. $row.addClass('enabling');
  459. }
  460. break;
  461. case 'disable':
  462. if (Drupal.ModuleFilter.enabling[id + name] != undefined) {
  463. delete Drupal.ModuleFilter.enabling[id + name];
  464. tab.updateEnabling(name, true);
  465. $row.removeClass('enabling');
  466. }
  467. else {
  468. Drupal.ModuleFilter.disabling[id + name] = name;
  469. tab.updateDisabling(name);
  470. $row.addClass('disabling');
  471. }
  472. break;
  473. }
  474. tab.updateVisualAid();
  475. };
  476. Drupal.ModuleFilter.Filter.prototype.adjustHeight = function() {
  477. // Hack for adjusting the height of the modules section.
  478. var minHeight = $('#module-filter-tabs ul').height() + 10;
  479. minHeight += $('#module-filter-tabs #module-filter-submit').height();
  480. $('#module-filter-modules').css('min-height', minHeight);
  481. this.element.trigger('moduleFilter:adjustHeight');
  482. }
  483. })(jQuery);