module_filter_tab.js 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559
  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.checkPlain(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', this);
  75. name = Drupal.checkPlain($.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', table).hide();
  199. $('td.checkbox', table).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. $switch.toggleClass('off');
  208. }
  209. else {
  210. $checkbox.click().change();
  211. $switch.toggleClass('off');
  212. }
  213. }
  214. });
  215. });
  216. }
  217. var $tabs = $('#module-filter-tabs');
  218. function getParentTopOffset($obj, offset) {
  219. var $parent = $obj.offsetParent();
  220. if ($obj[0] != $parent[0]) {
  221. offset += $parent.position().top;
  222. return getParentTopOffset($parent, offset);
  223. }
  224. return offset;
  225. }
  226. var tabsTopOffset = null;
  227. function getParentsTopOffset() {
  228. if (tabsTopOffset === null) {
  229. tabsTopOffset = getParentTopOffset($tabs.parent(), 0);
  230. }
  231. return tabsTopOffset;
  232. }
  233. function viewportTop() {
  234. var top = $(window).scrollTop();
  235. return top;
  236. }
  237. function viewportBottom() {
  238. var top = $(window).scrollTop();
  239. var bottom = top + $(window).height();
  240. bottom -= $('#page-actions').height();
  241. return bottom;
  242. }
  243. function fixToTop(top) {
  244. if ($tabs.hasClass('bottom-fixed')) {
  245. $tabs.css({
  246. 'position': 'absolute',
  247. 'top': $tabs.position().top - getParentsTopOffset(),
  248. 'bottom': 'auto'
  249. });
  250. $tabs.removeClass('bottom-fixed');
  251. }
  252. if (($tabs.css('position') == 'absolute' && $tabs.offset().top - top >= 0) || ($tabs.css('position') != 'absolute' && $tabs.offset().top - top <= 0)) {
  253. $tabs.addClass('top-fixed');
  254. $tabs.attr('style', '');
  255. }
  256. }
  257. function fixToBottom(bottom) {
  258. if ($tabs.hasClass('top-fixed')) {
  259. $tabs.css({
  260. 'position': 'absolute',
  261. 'top': $tabs.position().top - getParentsTopOffset(),
  262. 'bottom': 'auto'
  263. });
  264. $tabs.removeClass('top-fixed');
  265. }
  266. if ($tabs.offset().top + $tabs.height() - bottom <= 0) {
  267. $tabs.addClass('bottom-fixed');
  268. var style = '';
  269. var pageActionsHeight = $('#page-actions').height();
  270. if (pageActionsHeight > 0) {
  271. style = 'bottom: ' + pageActionsHeight + 'px';
  272. }
  273. else if (Drupal.settings.moduleFilter.dynamicPosition) {
  274. // style = 'bottom: ' + $('#module-filter-submit', $tabs).height() + 'px';
  275. }
  276. $tabs.attr('style', style);
  277. }
  278. }
  279. var lastTop = 0;
  280. $(window).scroll(function() {
  281. var top = viewportTop();
  282. var bottom = viewportBottom();
  283. if ($modules.offset().top >= top) {
  284. $tabs.removeClass('top-fixed').attr('style', '');
  285. }
  286. else {
  287. if (top > lastTop) { // Downward scroll.
  288. if ($tabs.height() > bottom - top) {
  289. fixToBottom(bottom);
  290. }
  291. else {
  292. fixToTop(top);
  293. }
  294. }
  295. else { // Upward scroll.
  296. fixToTop(top);
  297. }
  298. }
  299. lastTop = top;
  300. });
  301. moduleFilter.adjustHeight();
  302. });
  303. }
  304. }
  305. };
  306. Drupal.ModuleFilter.Tab = function(element, id) {
  307. var self = this;
  308. this.id = id;
  309. this.hash = id.substring(0, id.length - 4);
  310. this.element = element;
  311. $('a', this.element).click(function() {
  312. if (!Drupal.settings.moduleFilter.useURLFragment) {
  313. var hash = (!self.element.hasClass('selected')) ? self.hash : 'all';
  314. Drupal.ModuleFilter.selectTab(hash);
  315. return false;
  316. }
  317. if (self.element.hasClass('selected')) {
  318. // Clear the active tab.
  319. window.location.hash = 'all';
  320. return false;
  321. }
  322. });
  323. $('tr.' + this.id, $('#system-modules')).hover(
  324. function() {
  325. self.element.addClass('suggest');
  326. },
  327. function() {
  328. self.element.removeClass('suggest');
  329. }
  330. );
  331. };
  332. Drupal.ModuleFilter.selectTab = function(hash) {
  333. if (!hash || Drupal.ModuleFilter.tabs[hash + '-tab'] == undefined || Drupal.settings.moduleFilter.enabledCounts[hash].total == 0) {
  334. if (Drupal.settings.moduleFilter.rememberActiveTab) {
  335. var activeTab = Drupal.ModuleFilter.getState('activeTab');
  336. if (activeTab && Drupal.ModuleFilter.tabs[activeTab + '-tab'] != undefined) {
  337. hash = activeTab;
  338. }
  339. }
  340. if (!hash) {
  341. hash = 'all';
  342. }
  343. }
  344. if (Drupal.ModuleFilter.activeTab != undefined) {
  345. Drupal.ModuleFilter.activeTab.element.removeClass('selected');
  346. }
  347. Drupal.ModuleFilter.activeTab = Drupal.ModuleFilter.tabs[hash + '-tab'];
  348. Drupal.ModuleFilter.activeTab.element.addClass('selected');
  349. var moduleFilter = $('input[name="module_filter[name]"]').data('moduleFilter');
  350. var filter = moduleFilter.applyFilter();
  351. if (!Drupal.ModuleFilter.modulesTop) {
  352. Drupal.ModuleFilter.modulesTop = $('#module-filter-modules').offset().top;
  353. }
  354. else {
  355. // Calculate header offset; this is important in case the site is using
  356. // admin_menu module which has fixed positioning and is on top of everything
  357. // else.
  358. var headerOffset = Drupal.settings.tableHeaderOffset ? eval(Drupal.settings.tableHeaderOffset + '()') : 0;
  359. // Scroll back to top of #module-filter-modules.
  360. $('html, body').animate({
  361. scrollTop: Drupal.ModuleFilter.modulesTop - headerOffset
  362. }, 500);
  363. // $('html, body').scrollTop(Drupal.ModuleFilter.modulesTop);
  364. }
  365. Drupal.ModuleFilter.setState('activeTab', hash);
  366. };
  367. Drupal.ModuleFilter.eventHandlerOperateByURLFragment = function(event) {
  368. var hash = $.param.fragment();
  369. Drupal.ModuleFilter.selectTab(hash);
  370. };
  371. Drupal.ModuleFilter.countSummary = function(id) {
  372. return Drupal.t('@enabled of @total', { '@enabled': Drupal.settings.moduleFilter.enabledCounts[id].enabled, '@total': Drupal.settings.moduleFilter.enabledCounts[id].total });
  373. };
  374. Drupal.ModuleFilter.Tab.prototype.updateEnabling = function(name, remove) {
  375. this.enabling = this.enabling || {};
  376. if (!remove) {
  377. this.enabling[name] = name;
  378. }
  379. else {
  380. delete this.enabling[name];
  381. }
  382. };
  383. Drupal.ModuleFilter.Tab.prototype.updateDisabling = function(name, remove) {
  384. this.disabling = this.disabling || {};
  385. if (!remove) {
  386. this.disabling[name] = name;
  387. }
  388. else {
  389. delete this.disabling[name];
  390. }
  391. };
  392. Drupal.ModuleFilter.Tab.prototype.updateVisualAid = function() {
  393. var visualAid = '';
  394. var enabling = new Array();
  395. var disabling = new Array();
  396. if (this.enabling != undefined) {
  397. for (var i in this.enabling) {
  398. enabling.push(this.enabling[i]);
  399. }
  400. if (enabling.length > 0) {
  401. enabling.sort();
  402. visualAid += '<span class="enabling">+' + enabling.join('</span>, <span class="enabling">') + '</span>';
  403. }
  404. }
  405. if (this.disabling != undefined) {
  406. for (var i in this.disabling) {
  407. disabling.push(this.disabling[i]);
  408. }
  409. if (disabling.length > 0) {
  410. disabling.sort();
  411. if (enabling.length > 0) {
  412. visualAid += '<br />';
  413. }
  414. visualAid += '<span class="disabling">-' + disabling.join('</span>, <span class="disabling">') + '</span>';
  415. }
  416. }
  417. if (this.visualAid == undefined) {
  418. $('a span.summary', this.element).append('<span class="visual-aid"></span>');
  419. this.visualAid = $('span.visual-aid', this.element);
  420. }
  421. this.visualAid.empty().append(visualAid);
  422. };
  423. Drupal.ModuleFilter.getTabID = function($row) {
  424. var id = $row.data('moduleFilterTabID');
  425. if (!id) {
  426. // Find the tab ID.
  427. var classes = $row.attr('class').split(' ');
  428. for (var i in classes) {
  429. if (Drupal.ModuleFilter.tabs[classes[i]] != undefined) {
  430. id = classes[i];
  431. break;
  432. }
  433. }
  434. $row.data('moduleFilterTabID', id);
  435. }
  436. return id;
  437. };
  438. Drupal.ModuleFilter.updateVisualAid = function(type, $row) {
  439. var id = Drupal.ModuleFilter.getTabID($row);
  440. if (!id) {
  441. return false;
  442. }
  443. var tab = Drupal.ModuleFilter.tabs[id];
  444. var name = Drupal.checkPlain($('td:nth(1) strong', $row).text());
  445. switch (type) {
  446. case 'enable':
  447. if (Drupal.ModuleFilter.disabling[id + name] != undefined) {
  448. delete Drupal.ModuleFilter.disabling[id + name];
  449. tab.updateDisabling(name, true);
  450. $row.removeClass('disabling');
  451. }
  452. else {
  453. Drupal.ModuleFilter.enabling[id + name] = name;
  454. tab.updateEnabling(name);
  455. $row.addClass('enabling');
  456. }
  457. break;
  458. case 'disable':
  459. if (Drupal.ModuleFilter.enabling[id + name] != undefined) {
  460. delete Drupal.ModuleFilter.enabling[id + name];
  461. tab.updateEnabling(name, true);
  462. $row.removeClass('enabling');
  463. }
  464. else {
  465. Drupal.ModuleFilter.disabling[id + name] = name;
  466. tab.updateDisabling(name);
  467. $row.addClass('disabling');
  468. }
  469. break;
  470. }
  471. tab.updateVisualAid();
  472. };
  473. Drupal.ModuleFilter.Filter.prototype.adjustHeight = function() {
  474. // Hack for adjusting the height of the modules section.
  475. var minHeight = $('#module-filter-tabs ul').height() + 10;
  476. minHeight += $('#module-filter-tabs #module-filter-submit').height();
  477. $('#module-filter-modules').css('min-height', minHeight);
  478. this.element.trigger('moduleFilter:adjustHeight');
  479. }
  480. })(jQuery);