dataTables.responsive.js 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651
  1. /**
  2. * File: datatables.responsive.js
  3. * Version: 0.2.0
  4. * Author: Seen Sai Yang
  5. * Info: https://github.com/Comanche/datatables-responsive
  6. *
  7. * Copyright 2013 Seen Sai Yang, all rights reserved.
  8. *
  9. * This source file is free software, under either the GPL v2 license or a
  10. * BSD style license.
  11. *
  12. * This source file is distributed in the hope that it will be useful, but
  13. * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
  14. * or FITNESS FOR A PARTICULAR PURPOSE. See the license files for details.
  15. *
  16. * You should have received a copy of the GNU General Public License and the
  17. * BSD license along with this program. These licenses are also available at:
  18. * https://raw.github.com/Comanche/datatables-responsive/master/license-gpl2.txt
  19. * https://raw.github.com/Comanche/datatables-responsive/master/license-bsd.txt
  20. */
  21. 'use strict';
  22. /**
  23. * Constructor for responsive datables helper.
  24. *
  25. * This helper class makes datatables responsive to the window size.
  26. *
  27. * The parameter, breakpoints, is an object for each breakpoint key/value pair
  28. * with the following format: { breakpoint_name: pixel_width_at_breakpoint }.
  29. *
  30. * An example is as follows:
  31. *
  32. * {
  33. * tablet: 1024,
  34. * phone: 480
  35. * }
  36. *
  37. * These breakpoint name may be used as possible values for the data-hide
  38. * attribute. The data-hide attribute is optional and may be defined for each
  39. * th element in the table header.
  40. *
  41. * The parameter, options, is an object of options supported by the responsive
  42. * helper. The following options are supported:
  43. *
  44. * {
  45. * hideEmptyColumnsInRowDetail - Boolean, default: false.
  46. * clickOn - icon|cell|row, default: icon
  47. * }
  48. *
  49. * @param {Object|string} tableSelector jQuery wrapped set or selector for
  50. * datatables container element.
  51. * @param {Object} breakpoints Object defining the responsive
  52. * breakpoint for datatables.
  53. * @param {Object} options Object of options.
  54. */
  55. function ResponsiveDatatablesHelper(tableSelector, breakpoints, options) {
  56. if (typeof tableSelector === 'string') {
  57. this.tableElement = $(tableSelector);
  58. } else {
  59. this.tableElement = tableSelector;
  60. }
  61. // Get data table API.
  62. this.api = this.tableElement.dataTable().api();
  63. // State of column indexes and which are shown or hidden.
  64. this.columnIndexes = [];
  65. this.columnsShownIndexes = [];
  66. this.columnsHiddenIndexes = [];
  67. this.currentBreakpoint = '';
  68. this.lastBreakpoint = '';
  69. this.lastColumnsHiddenIndexes = [];
  70. // Save state
  71. var fileName = window.location.pathname.split("/").pop();
  72. var context = this.api.settings().context[0];
  73. this.tableId = context.sTableId;
  74. this.saveState = context.oInit.bStateSave;
  75. this.cookieName = 'DataTablesResponsiveHelper_' + this.tableId + (fileName ? '_' + fileName : '');
  76. this.lastStateExists = false;
  77. // Index of the th in the header tr that stores where the attribute
  78. // data-class="expand"
  79. // is defined.
  80. this.expandColumn = undefined;
  81. // Stores original breakpoint defitions
  82. this.origBreakpointsDefs = undefined;
  83. // Stores the break points defined in the table header.
  84. // Each th in the header tr may contain an optional attribute like
  85. // data-hide="phone,tablet"
  86. // These attributes and the breakpoints object will be used to create this
  87. // object.
  88. this.breakpoints = {
  89. /**
  90. * We will be generating data in the following format:
  91. * phone : {
  92. * lowerLimit : undefined,
  93. * upperLimit : 320,
  94. * columnsToHide: []
  95. * },
  96. * tablet: {
  97. * lowerLimit : 320,
  98. * upperLimit : 724,
  99. * columnsToHide: []
  100. * }
  101. */
  102. };
  103. // Store default options
  104. this.options = {
  105. hideEmptyColumnsInRowDetail: false,
  106. clickOn: 'icon'
  107. };
  108. // Expand icon template
  109. this.expandIconTemplate = '<span class="responsiveExpander"></span>';
  110. // Row template
  111. this.rowTemplate = '<tr class="row-detail"><td><ul><!--column item--></ul></td></tr>';
  112. this.rowLiTemplate = '<li><span class="columnTitle"><!--column title--></span>: <span class="columnValue"><!--column value--></span></li>';
  113. // Responsive behavior on/off flag
  114. this.disabled = true;
  115. // Skip next windows width change flag
  116. this.skipNextWindowsWidthChange = false;
  117. // Initialize settings
  118. this.init(breakpoints, options);
  119. }
  120. /**
  121. * Responsive datatables helper init function.
  122. * Builds breakpoint limits for columns and begins to listen to window resize
  123. * event.
  124. *
  125. * See constructor for the breakpoints parameter.
  126. *
  127. * @param {Object} breakpoints
  128. * @param {Object} options
  129. */
  130. ResponsiveDatatablesHelper.prototype.init = function (breakpoints, options) {
  131. this.origBreakpointsDefs = breakpoints;
  132. this.initBreakpoints();
  133. // Enable responsive behavior.
  134. this.disable(false);
  135. // Extend options
  136. $.extend(this.options, options);
  137. };
  138. ResponsiveDatatablesHelper.prototype.initBreakpoints = function () {
  139. // Get last state if it exists
  140. if (this.saveState) {
  141. this.getState();
  142. }
  143. if (!this.lastStateExists) {
  144. /** Generate breakpoints in the format we need. ***********************/
  145. // First, we need to create a sorted array of the breakpoints given.
  146. var breakpointsSorted = [];
  147. for (var prop in this.origBreakpointsDefs) {
  148. breakpointsSorted.push({
  149. name: prop,
  150. upperLimit: this.origBreakpointsDefs[prop],
  151. columnsToHide: []
  152. });
  153. }
  154. breakpointsSorted.sort(function (a, b) {
  155. return a.upperLimit - b.upperLimit;
  156. });
  157. // Set lower and upper limits for each breakpoint.
  158. var lowerLimit = 0;
  159. for (var i = 0; i < breakpointsSorted.length; i++) {
  160. breakpointsSorted[i].lowerLimit = lowerLimit;
  161. lowerLimit = breakpointsSorted[i].upperLimit;
  162. }
  163. // Add the default breakpoint which shows all (has no upper limit).
  164. breakpointsSorted.push({
  165. name : 'always',
  166. lowerLimit : lowerLimit,
  167. upperLimit : Infinity,
  168. columnsToHide: []
  169. });
  170. // Copy the sorted breakpoint array into the breakpoints object using the
  171. // name as the key.
  172. this.breakpoints = {};
  173. var i, l;
  174. for (i = 0, l = breakpointsSorted.length; i < l; i++) {
  175. this.breakpoints[breakpointsSorted[i].name] = breakpointsSorted[i];
  176. }
  177. /** Create range of visible columns and their indexes *****************/
  178. // We need the range of all visible column indexes to calculate the
  179. // columns to show:
  180. // Columns to show = all visible columns - columns to hide
  181. var columns = this.api.columns().header();
  182. var visibleColumnsHeadersTds = [];
  183. for (i = 0, l = columns.length; i < l; i++) {
  184. if (this.api.columns(i).visible()) {
  185. this.columnIndexes.push(i);
  186. visibleColumnsHeadersTds.push(columns[i]);
  187. }
  188. }
  189. /** Sort columns into breakpoints respectively ************************/
  190. // Read column headers' attributes and get needed info
  191. for (var index = 0; index < visibleColumnsHeadersTds.length; index++) {
  192. // Get the column with the attribute data-class="expand" so we know
  193. // where to display the expand icon.
  194. var col = visibleColumnsHeadersTds[index];
  195. if ($(col).attr('data-class') === 'expand') {
  196. this.expandColumn = this.columnIndexes[index];
  197. }
  198. // The data-hide attribute has the breakpoints that this column
  199. // is associated with.
  200. // If it's defined, get the data-hide attribute and sort this
  201. // column into the appropriate breakpoint's columnsToHide array.
  202. var dataHide = $(col).attr('data-hide');
  203. if (dataHide !== undefined) {
  204. var splitBreakingPoints = dataHide.split(/,\s*/);
  205. for (var i = 0; i < splitBreakingPoints.length; i++) {
  206. var bp = splitBreakingPoints[i];
  207. if (bp === 'always') {
  208. // A column with an 'always' breakpoint is always hidden.
  209. // Loop through all breakpoints and add it to each except the
  210. // default breakpoint.
  211. for (var prop in this.breakpoints) {
  212. if (this.breakpoints[prop].name !== 'default') {
  213. this.breakpoints[prop].columnsToHide.push(this.columnIndexes[index]);
  214. }
  215. }
  216. } else if (this.breakpoints[bp] !== undefined) {
  217. // Translate visible column index to internal column index.
  218. this.breakpoints[bp].columnsToHide.push(this.columnIndexes[index]);
  219. }
  220. }
  221. }
  222. }
  223. }
  224. };
  225. /**
  226. * Sets or removes window resize handler.
  227. *
  228. * @param {Boolean} bindFlag
  229. */
  230. ResponsiveDatatablesHelper.prototype.setWindowsResizeHandler = function(bindFlag) {
  231. if (bindFlag === undefined) {
  232. bindFlag = true;
  233. }
  234. if (bindFlag) {
  235. var that = this;
  236. $(window).bind("resize", function () {
  237. that.respond();
  238. });
  239. } else {
  240. $(window).unbind("resize");
  241. }
  242. };
  243. /**
  244. * Respond window size change. This helps make datatables responsive.
  245. */
  246. ResponsiveDatatablesHelper.prototype.respond = function () {
  247. if (this.disabled) {
  248. return;
  249. }
  250. var that = this;
  251. // Get new windows width
  252. var newWindowWidth = $(window).width();
  253. // Loop through breakpoints to see which columns need to be shown/hidden.
  254. var newColumnsToHide = [];
  255. for (var prop in this.breakpoints) {
  256. var element = this.breakpoints[prop];
  257. if ((!element.lowerLimit || newWindowWidth > element.lowerLimit) && (!element.upperLimit || newWindowWidth <= element.upperLimit)) {
  258. this.currentBreakpoint = element.name;
  259. newColumnsToHide = element.columnsToHide;
  260. }
  261. }
  262. // Find out if a column show/hide should happen.
  263. // Skip column show/hide if this window width change follows immediately
  264. // after a previous column show/hide. This will help prevent a loop.
  265. var columnShowHide = false;
  266. if (!this.skipNextWindowsWidthChange) {
  267. // Check difference in length
  268. if (this.lastBreakpoint.length === 0 && newColumnsToHide.length) {
  269. // No previous breakpoint and new breakpoint
  270. columnShowHide = true;
  271. } else if (this.lastBreakpoint != this.currentBreakpoint) {
  272. // Different breakpoints
  273. columnShowHide = true;
  274. } else if (this.columnsHiddenIndexes.length !== newColumnsToHide.length) {
  275. // Difference in number of hidden columns
  276. columnShowHide = true;
  277. } else {
  278. // Possible same number of columns but check for difference in columns
  279. var d1 = this.difference(this.columnsHiddenIndexes, newColumnsToHide).length;
  280. var d2 = this.difference(newColumnsToHide, this.columnsHiddenIndexes).length;
  281. columnShowHide = d1 + d2 > 0;
  282. }
  283. }
  284. if (columnShowHide) {
  285. // Showing/hiding a column at breakpoint may cause a windows width
  286. // change. Let's flag to skip the column show/hide that may be
  287. // caused by the next windows width change.
  288. this.skipNextWindowsWidthChange = true;
  289. this.columnsHiddenIndexes = newColumnsToHide;
  290. this.columnsShownIndexes = this.difference(this.columnIndexes, this.columnsHiddenIndexes);
  291. this.showHideColumns();
  292. this.lastBreakpoint = this.currentBreakpoint;
  293. this.setState();
  294. this.skipNextWindowsWidthChange = false;
  295. }
  296. // We don't skip this part.
  297. // If one or more columns have been hidden, add the has-columns-hidden class to table.
  298. // This class will show what state the table is in.
  299. if (this.columnsHiddenIndexes.length) {
  300. this.tableElement.addClass('has-columns-hidden');
  301. // Show details for each row that is tagged with the class .detail-show.
  302. $('tr.detail-show', this.tableElement).each(function (index, element) {
  303. var tr = $(element);
  304. if (tr.next('.row-detail').length === 0) {
  305. ResponsiveDatatablesHelper.prototype.showRowDetail(that, tr);
  306. }
  307. });
  308. } else {
  309. this.tableElement.removeClass('has-columns-hidden');
  310. $('tr.row-detail').each(function (event) {
  311. ResponsiveDatatablesHelper.prototype.hideRowDetail(that, $(this).prev());
  312. });
  313. }
  314. };
  315. /**
  316. * Show/hide datatables columns.
  317. */
  318. ResponsiveDatatablesHelper.prototype.showHideColumns = function () {
  319. // Calculate the columns to show
  320. // Show columns that may have been previously hidden.
  321. for (var i = 0, l = this.columnsShownIndexes.length; i < l; i++) {
  322. this.api.column(this.columnsShownIndexes[i]).visible(true);
  323. }
  324. // Hide columns that may have been previously shown.
  325. for (var i = 0, l = this.columnsHiddenIndexes.length; i < l; i++) {
  326. this.api.column(this.columnsHiddenIndexes[i]).visible(false);
  327. }
  328. // Rebuild details to reflect shown/hidden column changes.
  329. var that = this;
  330. $('tr.row-detail').each(function () {
  331. ResponsiveDatatablesHelper.prototype.hideRowDetail(that, $(this).prev());
  332. });
  333. if (this.tableElement.hasClass('has-columns-hidden')) {
  334. $('tr.detail-show', this.tableElement).each(function (index, element) {
  335. ResponsiveDatatablesHelper.prototype.showRowDetail(that, $(element));
  336. });
  337. }
  338. };
  339. /**
  340. * Create the expand icon on the column with the data-class="expand" attribute
  341. * defined for it's header.
  342. *
  343. * @param {Object} tr table row object
  344. */
  345. ResponsiveDatatablesHelper.prototype.createExpandIcon = function (tr) {
  346. if (this.disabled) {
  347. return;
  348. }
  349. // Get the td for tr with the same index as the th in the header tr
  350. // that has the data-class="expand" attribute defined.
  351. var tds = $('td', tr);
  352. // Loop through tds and create an expand icon on the td that has a column
  353. // index equal to the expand column given.
  354. for (var i = 0, l = tds.length; i < l; i++) {
  355. var td = tds[i];
  356. var tdIndex = this.api.cell(td).index().column;
  357. td = $(td);
  358. if (tdIndex === this.expandColumn) {
  359. // Create expand icon if there isn't one already.
  360. if ($('span.responsiveExpander', td).length == 0) {
  361. td.prepend(this.expandIconTemplate);
  362. // Respond to click event on expander icon.
  363. switch (this.options.clickOn) {
  364. case 'cell':
  365. td.on('click', {responsiveDatatablesHelperInstance: this}, this.showRowDetailEventHandler);
  366. break;
  367. case 'row':
  368. $(tr).on('click', {responsiveDatatablesHelperInstance: this}, this.showRowDetailEventHandler);
  369. break;
  370. default:
  371. td.on('click', 'span.responsiveExpander', {responsiveDatatablesHelperInstance: this}, this.showRowDetailEventHandler);
  372. break;
  373. }
  374. }
  375. break;
  376. }
  377. }
  378. };
  379. /**
  380. * Show row detail event handler.
  381. *
  382. * This handler is used to handle the click event of the expand icon defined in
  383. * the table row data element.
  384. *
  385. * @param {Object} event jQuery event object
  386. */
  387. ResponsiveDatatablesHelper.prototype.showRowDetailEventHandler = function (event) {
  388. var responsiveDatatablesHelperInstance = event.data.responsiveDatatablesHelperInstance;
  389. if (responsiveDatatablesHelperInstance.disabled) {
  390. return;
  391. }
  392. var td = $(this);
  393. // Nothing to do if there are no columns hidden.
  394. if (!td.closest('table').hasClass('has-columns-hidden')) {
  395. return;
  396. }
  397. // Get the parent tr of which this td belongs to.
  398. var tr = td.closest('tr');
  399. // Show/hide row details
  400. if (tr.hasClass('detail-show')) {
  401. ResponsiveDatatablesHelper.prototype.hideRowDetail(responsiveDatatablesHelperInstance, tr);
  402. } else {
  403. ResponsiveDatatablesHelper.prototype.showRowDetail(responsiveDatatablesHelperInstance, tr);
  404. }
  405. tr.toggleClass('detail-show');
  406. // Prevent click event from bubbling up to higher-level DOM elements.
  407. event.stopPropagation();
  408. };
  409. /**
  410. * Show row details.
  411. *
  412. * @param {ResponsiveDatatablesHelper} responsiveDatatablesHelperInstance instance of ResponsiveDatatablesHelper
  413. * @param {Object} tr jQuery wrapped set
  414. */
  415. ResponsiveDatatablesHelper.prototype.showRowDetail = function (responsiveDatatablesHelperInstance, tr) {
  416. // Get column because we need their titles.
  417. var api = responsiveDatatablesHelperInstance.api;
  418. var columns = api.columns().header();
  419. // Create the new tr.
  420. var newTr = $(responsiveDatatablesHelperInstance.rowTemplate);
  421. // Get the ul that we'll insert li's into.
  422. var ul = $('ul', newTr);
  423. // Loop through hidden columns and create an li for each of them.
  424. for (var i = 0; i < responsiveDatatablesHelperInstance.columnsHiddenIndexes.length; i++) {
  425. var index = responsiveDatatablesHelperInstance.columnsHiddenIndexes[i];
  426. // Get row td
  427. var rowIndex = api.row(tr).index();
  428. var td = api.cell(rowIndex, index).node();
  429. // Don't create li if contents are empty (depends on hideEmptyColumnsInRowDetail option).
  430. if (!responsiveDatatablesHelperInstance.options.hideEmptyColumnsInRowDetail || td.innerHTML.trim().length) {
  431. var li = $(responsiveDatatablesHelperInstance.rowLiTemplate);
  432. $('.columnTitle', li).html(columns[index].innerHTML);
  433. var contents = $(td).contents();
  434. var clonedContents = contents.clone();
  435. // Select elements' selectedIndex are not cloned. Do it manually.
  436. for (var n = 0, m = contents.length; n < m; n++) {
  437. var node = contents[n];
  438. if (node.nodeType === Node.ELEMENT_NODE && node.tagName === 'SELECT') {
  439. clonedContents[n].selectedIndex = node.selectedIndex
  440. }
  441. }
  442. // Set the column contents and save the original td source.
  443. $('.columnValue', li).append(clonedContents).data('originalTdSource', td);
  444. // Copy index to data attribute, so we'll know where to put the value when the tr.row-detail is removed.
  445. li.attr('data-column', index);
  446. // Copy td class to new li.
  447. var tdClass = $(td).attr('class');
  448. if (tdClass !== 'undefined' && tdClass !== false && tdClass !== '') {
  449. li.addClass(tdClass)
  450. }
  451. ul.append(li);
  452. }
  453. }
  454. // Create tr colspan attribute.
  455. var colspan = responsiveDatatablesHelperInstance.columnIndexes.length - responsiveDatatablesHelperInstance.columnsHiddenIndexes.length;
  456. newTr.find('> td').attr('colspan', colspan);
  457. // Append the new tr after the current tr.
  458. tr.after(newTr);
  459. };
  460. /**
  461. * Hide row details.
  462. *
  463. * @param {ResponsiveDatatablesHelper} responsiveDatatablesHelperInstance instance of ResponsiveDatatablesHelper
  464. * @param {Object} tr jQuery wrapped set
  465. */
  466. ResponsiveDatatablesHelper.prototype.hideRowDetail = function (responsiveDatatablesHelperInstance, tr) {
  467. // If the value of an input has changed while in row detail, we need to copy its state back
  468. // to the DataTables object so that value will persist when the tr.row-detail is removed.
  469. tr.next('.row-detail').find('li').each(function () {
  470. var columnValueContainer = $(this).find('span.columnValue');
  471. var tdContents = columnValueContainer.contents();
  472. var td = columnValueContainer.data('originalTdSource');
  473. $(td).empty().append(tdContents);
  474. });
  475. tr.next('.row-detail').remove();
  476. };
  477. /**
  478. * Enable/disable responsive behavior and restores changes made.
  479. *
  480. * @param {Boolean} disable, default is true
  481. */
  482. ResponsiveDatatablesHelper.prototype.disable = function (disable) {
  483. this.disabled = (disable === undefined) || disable;
  484. if (this.disabled) {
  485. // Remove windows resize handler.
  486. this.setWindowsResizeHandler(false);
  487. // Remove all trs that have row details.
  488. $('tbody tr.row-detail', this.tableElement).remove();
  489. // Remove all trs that are marked to have row details shown.
  490. $('tbody tr', this.tableElement).removeClass('detail-show');
  491. // Remove all expander icons.
  492. $('tbody tr span.responsiveExpander', this.tableElement).remove();
  493. this.columnsHiddenIndexes = [];
  494. this.columnsShownIndexes = this.columnIndexes;
  495. this.showHideColumns();
  496. this.tableElement.removeClass('has-columns-hidden');
  497. this.tableElement.off('click', 'span.responsiveExpander', this.showRowDetailEventHandler);
  498. } else {
  499. // Add windows resize handler.
  500. this.setWindowsResizeHandler();
  501. }
  502. };
  503. /**
  504. * Get state from cookie.
  505. */
  506. ResponsiveDatatablesHelper.prototype.getState = function () {
  507. try {
  508. var value = JSON.parse(decodeURIComponent(this.getCookie(this.cookieName)));
  509. if (value) {
  510. this.columnIndexes = value.columnIndexes;
  511. this.breakpoints = value.breakpoints;
  512. this.expandColumn = value.expandColumn;
  513. this.lastBreakpoint = value.lastBreakpoint;
  514. this.lastStateExists = true;
  515. }
  516. } catch (e) {
  517. }
  518. };
  519. /**
  520. * Saves state to cookie.
  521. */
  522. ResponsiveDatatablesHelper.prototype.setState = function () {
  523. var d1 = this.difference(this.lastColumnsHiddenIndexes, this.columnsHiddenIndexes).length;
  524. var d2 = this.difference(this.columnsHiddenIndexes, this.lastColumnsHiddenIndexes).length;
  525. if (d1 + d2 > 0) {
  526. var value = encodeURIComponent(JSON.stringify({
  527. columnIndexes: this.columnIndexes,
  528. columnsHiddenIndexes: this.columnsHiddenIndexes,
  529. breakpoints: this.breakpoints,
  530. expandColumn: this.expandColumn,
  531. lastBreakpoint: this.lastBreakpoint
  532. }));
  533. this.setCookie(this.cookieName, value, 2 * 60 * 60 * 1000);
  534. this.lastColumnsHiddenIndexes = this.columnsHiddenIndexes.slice(0);
  535. }
  536. };
  537. /**
  538. * Get cookie.
  539. */
  540. ResponsiveDatatablesHelper.prototype.getCookie = function (cname) {
  541. var name = cname + "=";
  542. var ca = document.cookie.split(';');
  543. for (var i = 0; i < ca.length; i++) {
  544. var c = ca[i].trim();
  545. if (c.indexOf(name) == 0) return c.substring(name.length, c.length);
  546. }
  547. return "";
  548. };
  549. /**
  550. * Set cookie.
  551. */
  552. ResponsiveDatatablesHelper.prototype.setCookie = function (cname, cvalue, cexp) {
  553. var d = new Date();
  554. d.setTime(d.getTime() + cexp);
  555. var expires = "expires=" + d.toGMTString();
  556. document.cookie = cname + "=" + cvalue + "; " + expires;
  557. };
  558. /**
  559. * Get Difference.
  560. */
  561. ResponsiveDatatablesHelper.prototype.difference = function (a, b) {
  562. var arr = [], i, hash = {};
  563. for (i = b.length - 1; i >= 0; i--) {
  564. hash[b[i]] = true;
  565. }
  566. for (i = a.length - 1; i >= 0; i--) {
  567. if (hash[a[i]] !== true) {
  568. arr.push(a[i]);
  569. }
  570. }
  571. return arr;
  572. };