rwd-table.min.js 29 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812
  1. /*!
  2. * Responsive Tables v5.3.2 (http://gergeo.se/RWD-Table-Patterns)
  3. * This is an awesome solution for responsive tables with complex data.
  4. * Authors: Nadan Gergeo <nadan@blimp.se> (www.blimp.se), Lucas Wiener <lucas@blimp.se> & "Maggie Wachs (www.filamentgroup.com)"
  5. * Licensed under MIT (https://github.com/nadangergeo/RWD-Table-Patterns/blob/master/LICENSE-MIT)
  6. */
  7. (function ($) {
  8. 'use strict';
  9. // RESPONSIVE TABLE CLASS DEFINITION
  10. // ==========================
  11. var ResponsiveTable = function(element, options) {
  12. // console.time('init');
  13. var that = this;
  14. this.options = options;
  15. this.$tableWrapper = null; //defined later in wrapTable
  16. this.$tableScrollWrapper = $(element); //defined later in wrapTable
  17. this.$table = $(element).find('.table');
  18. if (this.$table.length > 1) {
  19. this.$table = this.$table.eq(1);
  20. }
  21. if(this.$table.length !== 1) {
  22. throw new Error('Exactly one table is expected in a .table-responsive div.');
  23. }
  24. //apply pattern option as data-attribute, in case it was set via js
  25. this.$tableScrollWrapper.attr('data-pattern', this.options.pattern);
  26. //if the table doesn't have a unique id, give it one.
  27. //The id will be a random hexadecimal value, prefixed with id.
  28. //Used for triggers with displayAll button.
  29. this.id = this.$table.prop('id') || this.$tableScrollWrapper.prop('id') || 'id' + Math.random().toString(16).slice(2);
  30. this.$tableClone = null; //defined farther down
  31. this.$stickyTableHeader = null; //defined farther down
  32. //good to have - for easy access
  33. this.$thead = this.$table.find('thead');
  34. this.$hdrCells = this.$thead.find("tr").first().find('th');
  35. this.$bodyRows = this.$table.find('tbody, tfoot').find('tr');
  36. //toolbar and buttons
  37. this.$btnToolbar = null; //defined farther down
  38. this.$dropdownGroup = null; //defined farther down
  39. this.$dropdownBtn = null; //defined farther down
  40. this.$dropdownContainer = null; //defined farther down
  41. this.$displayAllBtn = null; //defined farther down
  42. this.$focusGroup = null; //defined farther down
  43. this.$focusBtn = null; //defined farther down
  44. //misc
  45. this.displayAllTrigger = 'display-all-' + this.id + '.responsive-table';
  46. this.idPrefix = this.id + '-col-';
  47. this.headerColIndices = {};
  48. this.headerRowIndices = {};
  49. // Setup table
  50. // -------------------------
  51. //wrap table
  52. this.wrapTable();
  53. //create toolbar with buttons
  54. this.createButtonToolbar();
  55. //Build header indices mapping (for colspans in header)
  56. this.buildHeaderCellIndices();
  57. // Setup cells
  58. // -------------------------
  59. //setup header
  60. this.setupTableHeader();
  61. //setup standard cells
  62. this.setupBodyRows();
  63. //create sticky table head
  64. if(this.options.stickyTableHeader){
  65. this.createStickyTableHeader();
  66. }
  67. // hide toggle button if the list is empty
  68. if(this.$dropdownContainer.is(':empty')){
  69. this.$dropdownGroup.hide();
  70. }
  71. // Event binding
  72. // -------------------------
  73. // on orientchange, resize and displayAllBtn-click
  74. $(window).bind('orientationchange resize ' + this.displayAllTrigger, function(){
  75. //update the inputs' checked status
  76. that.$dropdownContainer.find('input').trigger('updateCheck');
  77. //update colspan and visibility of spanning cells
  78. $.proxy(that.updateSpanningCells(), that);
  79. }).trigger('resize');
  80. // console.timeEnd('init');
  81. };
  82. ResponsiveTable.DEFAULTS = {
  83. pattern: 'priority-columns',
  84. stickyTableHeader: true,
  85. fixedNavbar: '.navbar-fixed-top', // Is there a fixed navbar? The stickyTableHeader needs to know about it!
  86. addDisplayAllBtn: false, // should it have a display-all button?
  87. addFocusBtn: true, // should it have a focus button?
  88. addDisplayBtn: true, // should it have a focus button?
  89. focusBtnIcon: 'glyphicon glyphicon-screenshot',
  90. mainContainer: window,
  91. i18n: {
  92. focus : 'Focus',
  93. display : 'Display',
  94. displayAll: 'Display all'
  95. }
  96. };
  97. // Wrap table
  98. ResponsiveTable.prototype.wrapTable = function() {
  99. this.$tableScrollWrapper.wrap('<div class="table-wrapper"/>');
  100. this.$tableWrapper = this.$tableScrollWrapper.parent();
  101. };
  102. // Create toolbar with buttons
  103. ResponsiveTable.prototype.createButtonToolbar = function() {
  104. var that = this;
  105. if ($('.btn-rwd-table').length) {
  106. $('.btn-rwd-table').remove();
  107. }
  108. this.$btnToolbar = $('[data-responsive-table-toolbar="' + this.id + '"]').addClass('btn-toolbar');
  109. if(this.$btnToolbar.length === 0) {
  110. this.$btnToolbar = $('<div class="btn-toolbar btn-rwd-table" />');
  111. }
  112. this.$dropdownGroup = $('<div class="btn-group dropdown-btn-group pull-right" />');
  113. if (this.options.addDisplayBtn) {
  114. this.$dropdownBtn = $('<button type="button" class="btn btn-default dropdown-toggle" data-toggle="dropdown">' + this.options.i18n.display + ' <span class="caret"></span></button>');
  115. }
  116. this.$dropdownContainer = $('<ul class="dropdown-menu"/>');
  117. // Focus btn
  118. if(this.options.addFocusBtn) {
  119. // Create focus btn group
  120. this.$focusGroup = $('<div class="btn-group focus-btn-group" />');
  121. // Create focus btn
  122. this.$focusBtn = $('<button type="button" class="btn btn-default">' + this.options.i18n.focus + '</button>');
  123. if(this.options.focusBtnIcon) {
  124. this.$focusBtn.prepend('<span class="' + this.options.focusBtnIcon + '"></span> ');
  125. }
  126. // Add btn to group
  127. this.$focusGroup.append(this.$focusBtn);
  128. // Add focus btn to toolbar
  129. this.$btnToolbar.append(this.$focusGroup);
  130. // bind click on focus btn
  131. this.$focusBtn.click(function(){
  132. $.proxy(that.activateFocus(), that);
  133. });
  134. // bind click on rows
  135. this.$bodyRows.click(function(){
  136. $.proxy(that.focusOnRow($(this)), that);
  137. });
  138. }
  139. // Display-all btn
  140. if(this.options.addDisplayAllBtn) {
  141. // Create display-all btn
  142. this.$displayAllBtn = $('<button type="button" class="btn btn-default">' + this.options.i18n.displayAll + '</button>');
  143. // Add display-all btn to dropdown-btn-group
  144. this.$dropdownGroup.append(this.$displayAllBtn);
  145. if (this.$table.hasClass('display-all')) {
  146. // add 'btn-primary' class to btn to indicate that display all is activated
  147. this.$displayAllBtn.addClass('btn-primary');
  148. }
  149. // bind click on display-all btn
  150. this.$displayAllBtn.click(function(){
  151. $.proxy(that.displayAll(null, true), that);
  152. });
  153. }
  154. //add dropdown btn and menu to dropdown-btn-group
  155. this.$dropdownGroup.append(this.$dropdownBtn).append(this.$dropdownContainer);
  156. //add dropdown group to toolbar
  157. this.$btnToolbar.append(this.$dropdownGroup);
  158. // add toolbar above table
  159. this.$tableScrollWrapper.before(this.$btnToolbar);
  160. };
  161. ResponsiveTable.prototype.clearAllFocus = function() {
  162. this.$bodyRows.removeClass('unfocused');
  163. this.$bodyRows.removeClass('focused');
  164. };
  165. ResponsiveTable.prototype.activateFocus = function() {
  166. // clear all
  167. this.clearAllFocus();
  168. if(this.$focusBtn){
  169. this.$focusBtn.toggleClass('btn-primary');
  170. }
  171. this.$table.toggleClass('focus-on');
  172. };
  173. ResponsiveTable.prototype.focusOnRow = function(row) {
  174. // only if activated (.i.e the table has the class focus-on)
  175. if(this.$table.hasClass('focus-on')) {
  176. var alreadyFocused = $(row).hasClass('focused');
  177. // clear all
  178. this.clearAllFocus();
  179. if(!alreadyFocused) {
  180. this.$bodyRows.addClass('unfocused');
  181. $(row).addClass('focused');
  182. }
  183. }
  184. };
  185. /**
  186. * @param activate Forces the displayAll to be active or not. If anything else than bool, it will not force the state so it will toggle as normal.
  187. * @param trigger Bool to indicate if the displayAllTrigger should be triggered.
  188. */
  189. ResponsiveTable.prototype.displayAll = function(activate, trigger) {
  190. if(this.$displayAllBtn){
  191. // add 'btn-primary' class to btn to indicate that display all is activated
  192. this.$displayAllBtn.toggleClass('btn-primary', activate);
  193. }
  194. this.$table.toggleClass('display-all', activate);
  195. if(this.$tableClone){
  196. this.$tableClone.toggleClass('display-all', activate);
  197. }
  198. if(trigger) {
  199. $(window).trigger(this.displayAllTrigger);
  200. }
  201. };
  202. ResponsiveTable.prototype.preserveDisplayAll = function() {
  203. var displayProp = 'table-cell';
  204. if($('html').hasClass('lt-ie9')){
  205. displayProp = 'inline';
  206. }
  207. $(this.$table).find('th, td').css('display', displayProp);
  208. if(this.$tableClone){
  209. $(this.$tableClone).find('th, td').css('display', displayProp);
  210. }
  211. };
  212. ResponsiveTable.prototype.createStickyTableHeader = function() {
  213. var that = this;
  214. //clone table head
  215. that.$tableClone = that.$table.clone();
  216. //replace ids
  217. that.$tableClone.prop('id', this.id + '-clone');
  218. that.$tableClone.find('[id]').each(function() {
  219. $(this).prop('id', $(this).prop('id') + '-clone');
  220. });
  221. // wrap table clone (this is our "sticky table header" now)
  222. that.$tableClone.wrap('<div class="sticky-table-header"/>');
  223. that.$stickyTableHeader = that.$tableClone.parent();
  224. // give the sticky table header same height as original
  225. that.$stickyTableHeader.css('height', that.$thead.height() + 2);
  226. //insert sticky table header
  227. that.$table.before(that.$stickyTableHeader);
  228. // bind scroll on mainContainer with updateStickyTableHeader
  229. $(this.options.mainContainer).bind('scroll', function(){
  230. $.proxy(that.updateStickyTableHeader(), that);
  231. });
  232. // bind resize on window with updateStickyTableHeader
  233. $(window).bind('resize', function(e){
  234. $.proxy(that.updateStickyTableHeader(), that);
  235. });
  236. $(that.$tableScrollWrapper).bind('scroll', function(){
  237. $.proxy(that.updateStickyTableHeader(), that);
  238. });
  239. // determine what solution to use for rendereing sticky table head (aboslute/fixed).
  240. that.useFixedSolution = !isIOS() || (getIOSVersion() >= 8);
  241. //add class for rendering solution
  242. if(that.useFixedSolution) {
  243. that.$tableScrollWrapper.addClass('fixed-solution');
  244. } else {
  245. that.$tableScrollWrapper.addClass('absolute-solution');
  246. }
  247. };
  248. // Help function for sticky table header
  249. ResponsiveTable.prototype.updateStickyTableHeader = function() {
  250. var that = this,
  251. top = 0,
  252. offsetTop = that.$table.offset().top,
  253. scrollTop = $(this.options.mainContainer).scrollTop() -1, //-1 to accomodate for top border
  254. maxTop = that.$table.height() - that.$stickyTableHeader.height(),
  255. rubberBandOffset = (scrollTop + $(this.options.mainContainer).height()) - $(document).height(),
  256. navbarHeight = 0;
  257. //Is there a fixed navbar?
  258. if($(that.options.fixedNavbar).length) {
  259. var $navbar = $(that.options.fixedNavbar).first();
  260. navbarHeight = $navbar.height();
  261. scrollTop = scrollTop + navbarHeight;
  262. }
  263. var shouldBeVisible;
  264. if(this.options.mainContainer === window) {
  265. shouldBeVisible = (scrollTop > offsetTop) && (scrollTop < offsetTop + that.$table.height());
  266. } else {
  267. shouldBeVisible = (offsetTop <= 0) && (-offsetTop < that.$table.height());
  268. }
  269. // console.log('offsetTop:' + offsetTop);
  270. // console.log('scrollTop:' + scrollTop);
  271. // console.log('tableHeight:' + that.$table.height());
  272. // console.log('shouldBeVisible:' + shouldBeVisible);
  273. if(that.useFixedSolution) { //fixed solution
  274. that.$stickyTableHeader.scrollLeft(that.$tableScrollWrapper.scrollLeft());
  275. // Calculate top property value (-1 to accomodate for top border)
  276. top = navbarHeight - 1;
  277. // When the user is about to scroll past the table, move sticky table head up
  278. if(this.options.mainContainer === window && ((scrollTop - offsetTop) > maxTop)){
  279. top -= ((scrollTop - offsetTop) - maxTop);
  280. that.$stickyTableHeader.addClass('border-radius-fix');
  281. } else if(this.options.mainContainer !== window && ((- offsetTop) > maxTop)){
  282. top -= ((- offsetTop) - maxTop);
  283. that.$stickyTableHeader.addClass('border-radius-fix');
  284. } else {
  285. that.$stickyTableHeader.removeClass('border-radius-fix');
  286. }
  287. if (shouldBeVisible) {
  288. //show sticky table header and update top and width.
  289. that.$stickyTableHeader.css({ 'visibility': 'visible', 'top': top + 'px', 'width': that.$tableScrollWrapper.innerWidth() + 'px'});
  290. //no more stuff to do - return!
  291. return;
  292. } else {
  293. //hide sticky table header and reset width
  294. that.$stickyTableHeader.css({'visibility': 'hidden', 'width': 'auto' });
  295. }
  296. } else { // alternate method
  297. //animation duration
  298. var animationDuration = 400;
  299. // Calculate top property value (-1 to accomodate for top border)
  300. if(this.options.mainContainer === window) {
  301. top = scrollTop - offsetTop - 1;
  302. } else {
  303. top = -offsetTop - 1;
  304. // console.log('top:' + top);
  305. }
  306. // Make sure the sticky table header doesn't slide up/down too far.
  307. if(top < 0) {
  308. top = 0;
  309. } else if (top > maxTop) {
  310. top = maxTop;
  311. }
  312. // Accomandate for rubber band effect
  313. if(this.options.mainContainer === window) {
  314. if(rubberBandOffset > 0) {
  315. top = top - rubberBandOffset;
  316. }
  317. }
  318. if (shouldBeVisible) {
  319. //show sticky table header (animate repositioning)
  320. that.$stickyTableHeader.css({ 'visibility': 'visible' });
  321. that.$stickyTableHeader.animate({ 'top': top + 'px' }, animationDuration);
  322. // hide original table head
  323. that.$thead.css({ 'visibility': 'hidden' });
  324. } else {
  325. that.$stickyTableHeader.animate({ 'top': '0' }, animationDuration, function(){
  326. // show original table head
  327. that.$thead.css({ 'visibility': 'visible' });
  328. // hide sticky table head
  329. that.$stickyTableHeader.css({ 'visibility': 'hidden' });
  330. });
  331. }
  332. }
  333. };
  334. // Setup header cells
  335. ResponsiveTable.prototype.setupTableHeader = function() {
  336. var that = this;
  337. // for each header column
  338. that.$hdrCells.each(function(i){
  339. var $th = $(this),
  340. id = $th.prop('id'),
  341. thText = $th.text();
  342. // assign an id to each header, if none is in the markup
  343. if (!id) {
  344. id = that.idPrefix + i;
  345. $th.prop('id', id);
  346. }
  347. if(thText === ''){
  348. thText = $th.attr('data-col-name');
  349. }
  350. // create the hide/show toggle for the current column
  351. if ( $th.is('[data-priority]') && $th.data('priority') !== -1 ) {
  352. var $toggle = $('<li class="checkbox-row"><input type="checkbox" name="toggle-'+id+'" id="toggle-'+id+'" value="'+id+'" /> <label for="toggle-'+id+'">'+ thText +'</label></li>');
  353. var $checkbox = $toggle.find('input');
  354. that.$dropdownContainer.append($toggle);
  355. $toggle.click(function(){
  356. // console.log("cliiiick!");
  357. $checkbox.prop('checked', !$checkbox.prop('checked'));
  358. $checkbox.trigger('change');
  359. });
  360. //Freakin' IE fix
  361. if ($('html').hasClass('lt-ie9')) {
  362. $checkbox.click(function() {
  363. $(this).trigger('change');
  364. });
  365. }
  366. $toggle.find('label').click(function(event){
  367. event.stopPropagation();
  368. });
  369. $toggle.find('input')
  370. .click(function(event){
  371. event.stopPropagation();
  372. })
  373. .change(function(){ // bind change event on checkbox
  374. var $checkbox = $(this),
  375. val = $checkbox.val(),
  376. //all cells under the column, including the header and its clone
  377. $cells = that.$tableWrapper.find('#' + val + ', #' + val + '-clone, [data-columns~='+ val +']');
  378. //if display-all is on - save state and carry on
  379. if(that.$table.hasClass('display-all')){
  380. //save state
  381. $.proxy(that.preserveDisplayAll(), that);
  382. //remove display all class
  383. that.$table.removeClass('display-all');
  384. if(that.$tableClone){
  385. that.$tableClone.removeClass('display-all');
  386. }
  387. //switch off button
  388. that.$displayAllBtn.removeClass('btn-primary');
  389. }
  390. // loop through the cells
  391. $cells.each(function(){
  392. var $cell = $(this);
  393. // is the checkbox checked now?
  394. if ($checkbox.is(':checked')) {
  395. // if the cell was already visible, it means its original colspan was >1
  396. // so let's increment the colspan
  397. // This should not be done for th's in thead.
  398. if(!$cell.closest("thead").length && $cell.css('display') !== 'none'){
  399. // make sure new colspan value does not exceed original colspan value
  400. var newColSpan = Math.min(parseInt($cell.prop('colSpan')) + 1, $cell.attr('data-org-colspan'));
  401. // update colspan
  402. $cell.prop('colSpan', newColSpan);
  403. }
  404. // show cell
  405. $cell.show();
  406. }
  407. // checkbox has been unchecked
  408. else {
  409. // decrement colSpan if it's not 1 (because colSpan should not be 0)
  410. // This should not be done for th's in thead.
  411. if(!$cell.closest("thead").length && parseInt($cell.prop('colSpan'))>1){
  412. $cell.prop('colSpan', parseInt($cell.prop('colSpan')) - 1);
  413. }
  414. // otherwise, hide the cell
  415. else {
  416. $cell.hide();
  417. }
  418. }
  419. });
  420. })
  421. .bind('updateCheck', function(){
  422. if ( $th.css('display') !== 'none') {
  423. $(this).prop('checked', true);
  424. }
  425. else {
  426. $(this).prop('checked', false);
  427. }
  428. });
  429. } // end if
  430. }); // end hdrCells loop
  431. if(!$.isEmptyObject(this.headerRowIndices)) {
  432. that.setupRow(this.$thead.find("tr:eq(1)"), this.headerRowIndices);
  433. }
  434. };
  435. // Setup body rows
  436. // assign matching "data-columns" attributes to the associated cells "(cells with colspan>1 has multiple columns).
  437. ResponsiveTable.prototype.setupBodyRows = function() {
  438. var that = this;
  439. // for each body rows
  440. that.$bodyRows.each(function(){
  441. that.setupRow($(this), that.headerColIndices);
  442. });
  443. };
  444. ResponsiveTable.prototype.setupRow = function($row, indices) {
  445. var that = this;
  446. //check if it's already set up
  447. if($row.data('setup')){
  448. // don't do anything
  449. return;
  450. } else {
  451. $row.data('setup', true);
  452. }
  453. var idStart = 0;
  454. // for each cell
  455. $row.find('th, td').each(function(){
  456. var $cell = $(this);
  457. var columnsAttr = '';
  458. var colSpan = $cell.prop('colSpan');
  459. $cell.attr('data-org-colspan', colSpan);
  460. // if colSpan is more than 1
  461. if(colSpan > 1) {
  462. //give it the class 'spn-cell';
  463. $cell.addClass('spn-cell');
  464. }
  465. // loop through columns that the cell spans over
  466. for (var k = idStart; k < (idStart + colSpan); k++) {
  467. // add column id
  468. columnsAttr = columnsAttr + ' ' + that.idPrefix + indices[k];
  469. // get column header
  470. var $colHdr = that.$table.find('#' + that.idPrefix + indices[k]);
  471. // copy data-priority attribute from column header
  472. var dataPriority = $colHdr.attr('data-priority');
  473. if (dataPriority) { $cell.attr('data-priority', dataPriority); }
  474. }
  475. //remove whitespace in begining of string.
  476. columnsAttr = columnsAttr.substring(1);
  477. //set attribute to cell
  478. $cell.attr('data-columns', columnsAttr);
  479. //increment idStart with the current cells colSpan.
  480. idStart = idStart + colSpan;
  481. });
  482. };
  483. ResponsiveTable.prototype.buildHeaderCellIndices = function() {
  484. var that = this;
  485. var rowspansBeforeIndex = {};
  486. this.headerColIndices = {};
  487. this.headerRowIndices = {};
  488. var colPadding = 0;
  489. var rowPadding = 0;
  490. this.$thead.find("tr").first().find('th').each(function(i){
  491. var $th = $(this);
  492. var colSpan = $th.prop('colSpan');
  493. var rowSpan = $th.prop("rowSpan");
  494. for(var index = 0; index < colSpan; index++) {
  495. that.headerColIndices[colPadding + i + index] = i;
  496. if(colPadding + i + index >= 0) {
  497. rowspansBeforeIndex[colPadding + i + index - rowPadding] = rowPadding;
  498. }
  499. }
  500. if(rowSpan > 1) {
  501. rowPadding++;
  502. }
  503. colPadding += colSpan - 1;
  504. });
  505. if(this.$thead.find("tr").length > 2) {
  506. throw new Error("This plugin doesnt support more than two rows in thead.");
  507. }
  508. if(this.$thead.find("tr").length === 2) {
  509. var $row = $(this.$thead.find("tr")[1]);
  510. $row.find("th").each(function(cellIndex) {
  511. that.headerRowIndices[cellIndex] = that.headerColIndices[rowspansBeforeIndex[cellIndex] + cellIndex];
  512. });
  513. }
  514. }
  515. // Run this after the content in tbody has changed
  516. ResponsiveTable.prototype.update = function() {
  517. this.$bodyRows = this.$table.find('tbody, tfoot').find('tr');
  518. this.setupBodyRows();
  519. // Remove old tbody clone from Tableclone
  520. this.$tableClone.find('tbody, tfoot').remove();
  521. // Make new clone of tbody
  522. var $tbodyClone = this.$table.find('tbody, tfoot').clone();
  523. //replace ids
  524. $tbodyClone.find('[id]').each(function() {
  525. $(this).prop('id', $(this).prop('id') + '-clone');
  526. });
  527. // Append new clone to tableClone
  528. $tbodyClone.appendTo(this.$tableClone);
  529. // Make sure columns visibility is in sync,
  530. // by triggering a (non-changing) change event on all checkboxes
  531. this.$dropdownContainer.find('input').trigger('change');
  532. // ¯\(°_o)/¯ I dunno if this is needed
  533. // this.updateSpanningCells();
  534. };
  535. // Update colspan and visibility of spanning cells
  536. ResponsiveTable.prototype.updateSpanningCells = function() {
  537. var that = this;
  538. // iterate through cells with class 'spn-cell'
  539. that.$table.find('.spn-cell').each( function(){
  540. var $cell = $(this);
  541. var columnsAttr = $cell.attr('data-columns').split(' ');
  542. var colSpan = columnsAttr.length;
  543. var numOfHidden = 0;
  544. for (var i = 0; i < colSpan; i++) {
  545. if($('#' + columnsAttr[i]).css('display')==='none'){
  546. numOfHidden++;
  547. }
  548. }
  549. // if one of the columns that the cell belongs to is visible then show the cell
  550. if(numOfHidden !== colSpan){
  551. $cell.show();
  552. } else {
  553. $cell.hide(); //just in case
  554. }
  555. // console.log('numOfHidden: ' + numOfHidden);
  556. // console.log("new colSpan:" +Math.max((colSpan - numOfHidden),1));
  557. //update colSpan to match number of visible columns that i belongs to
  558. $cell.prop('colSpan',Math.max((colSpan - numOfHidden),1));
  559. });
  560. };
  561. // RESPONSIVE TABLE PLUGIN DEFINITION
  562. // ===========================
  563. var old = $.fn.responsiveTable;
  564. $.fn.responsiveTable = function (option) {
  565. return this.each(function () {
  566. var $this = $(this);
  567. var data = $this.data('responsiveTable');
  568. var options = $.extend({}, ResponsiveTable.DEFAULTS, $this.data(), typeof option === 'object' && option);
  569. if(options.pattern === '') {
  570. return;
  571. }
  572. if (!data) {
  573. $this.data('responsiveTable', (data = new ResponsiveTable(this, options)));
  574. }
  575. if (typeof option === 'string') {
  576. data[option]();
  577. }
  578. });
  579. };
  580. $.fn.responsiveTable.Constructor = ResponsiveTable;
  581. // RESPONSIVE TABLE NO CONFLICT
  582. // =====================
  583. $.fn.responsiveTable.noConflict = function () {
  584. $.fn.responsiveTable = old;
  585. return this;
  586. };
  587. // RESPONSIVE TABLE DATA-API
  588. // ==================
  589. $(document).on('ready.responsive-table.data-api', function () {
  590. $('.table-responsive[data-pattern]').each(function () {
  591. var $tableScrollWrapper = $(this);
  592. $tableScrollWrapper.responsiveTable($tableScrollWrapper.data());
  593. });
  594. });
  595. // DROPDOWN
  596. // ==========================
  597. // Prevent dropdown from closing when toggling checkbox
  598. $(document).on('click.dropdown.data-api', '.dropdown-menu .checkbox-row', function (e) {
  599. e.stopPropagation();
  600. });
  601. // FEATURE DETECTION (instead of Modernizr)
  602. // ==========================
  603. // media queries
  604. function mediaQueriesSupported() {
  605. return (typeof window.matchMedia !== 'undefined' || typeof window.msMatchMedia !== 'undefined' || typeof window.styleMedia !== 'undefined');
  606. }
  607. // touch
  608. function hasTouch() {
  609. return 'ontouchstart' in window;
  610. }
  611. // Checks if current browser is on IOS.
  612. function isIOS() {
  613. return !!(navigator.userAgent.match(/iPhone/i) || navigator.userAgent.match(/iPad/i) || navigator.userAgent.match(/iPod/i));
  614. }
  615. // Gets iOS version number. If the user is not on iOS, the function returns 0.
  616. function getIOSVersion() {
  617. if(isIOS()){
  618. var iphone_version = parseFloat(('' + (/CPU.*OS ([0-9_]{1,5})|(CPU like).*AppleWebKit.*Mobile/i.exec(navigator.userAgent) || [0,''])[1]).replace('undefined', '3_2').replace('_', '.').replace('_', ''));
  619. return iphone_version;
  620. } else {
  621. return 0;
  622. }
  623. }
  624. $(document).ready(function() {
  625. // Change `no-js` to `js`
  626. $('html').removeClass('no-js').addClass('js');
  627. // Add mq/no-mq class to html
  628. if(mediaQueriesSupported()) {
  629. $('html').addClass('mq');
  630. } else {
  631. $('html').addClass('no-mq');
  632. }
  633. // Add touch/no-touch class to html
  634. if(hasTouch()) {
  635. $('html').addClass('touch');
  636. } else {
  637. $('html').addClass('no-touch');
  638. }
  639. });
  640. })(jQuery);