bootstrap-datatable.js 35 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222
  1. /*!
  2. * Bootstrap Data Table Plugin v1.5.5
  3. *
  4. * Author: Jeff Dupont
  5. * ==========================================================
  6. * Copyright 2012
  7. *
  8. * Licensed under the Apache License, Version 2.0 (the "License");
  9. * you may not use this file except in compliance with the License.
  10. * You may obtain a copy of the License at
  11. *
  12. * http://www.apache.org/licenses/LICENSE-2.0
  13. *
  14. * Unless required by applicable law or agreed to in writing, software
  15. * distributed under the License is distributed on an "AS IS" BASIS,
  16. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  17. * See the License for the specific language governing permissions and
  18. * limitations under the License.
  19. * ==========================================================
  20. */
  21. ;(function( $ ){
  22. /* DATATABLE CLASS DEFINITION
  23. * ========================== */
  24. var DataTable = function ( element, options ) {
  25. this.$element = $(element);
  26. this.options = options;
  27. this.enabled = true;
  28. this.columns = [];
  29. this.rows = [];
  30. this.buttons = [];
  31. // this needs to be handled better
  32. this.localStorageId = "datatable_" + (options.id || options.url.replace(/\W/ig, '_'));
  33. // set the defaults for the column options array
  34. for(var column in this.options.columns) {
  35. // check sortable
  36. if(typeof this.options.columns[column].sortable === undefined)
  37. this.options.columns[column].sortable = true;
  38. }
  39. this.$default = this.$element.children().length ?
  40. this.$element.children() :
  41. $("<div></div>")
  42. .addClass("alert alert-error")
  43. .html("No Results Found");
  44. this.$element.addClass("clearfix");
  45. // clear out the localStorage for this entry
  46. if(localStorage) {
  47. localStorage[this.localStorageId] = 'false';
  48. }
  49. if(this.options.tablePreRender && typeof this.options.tablePreRender === 'function')
  50. this.options.tablePreRender.call(this)
  51. // initialize the toolbar
  52. //_initToolbar.call(this)
  53. if(this.options.autoLoad === true) this.render();
  54. };
  55. DataTable.prototype = {
  56. constructor: DataTable
  57. , render: function () {
  58. var o = this.options
  59. , $e = this.$element;
  60. // show loading
  61. this.loading( true );
  62. // reset the columns and rows
  63. this.columns = [];
  64. this.rows = [];
  65. this.buttons = [];
  66. this.$wrapper = undefined;
  67. this.$table = undefined;
  68. this.$header = undefined;
  69. this.$body = undefined;
  70. this.$footer = undefined;
  71. this.$pagination = undefined;
  72. if(this.$toolbar) this.$toolbar.remove();
  73. // top
  74. this.$top_details = $("<div></div>")
  75. .attr("id", "dt-top-details");
  76. // bottom
  77. this.$bottom_details = $("<div></div>")
  78. .attr("id", "dt-bottom-details");
  79. // localize the object
  80. var that = this;
  81. // pull in the data from the ajax call
  82. if(o.url !== "") {
  83. $.ajax({
  84. url: o.url
  85. , type: "POST"
  86. , dataType: "json"
  87. , data: $.extend({}, o.post, {
  88. currentPage: o.currentPage
  89. , perPage: o.perPage
  90. , sort: o.sort
  91. , filter: o.filter
  92. })
  93. , success: function( res ) {
  94. that.resultset = res;
  95. if(!res || res === undefined || !res.data || res.data.length == 0) {
  96. showError.call(that);
  97. return;
  98. }
  99. // clear out the current elements in the container
  100. $e.empty();
  101. // set the sort and filter configuration
  102. o.sort = res.sort;
  103. o.filter = res.filter;
  104. o.totalRows = res.totalRows;
  105. // set the current page if we're forcing it from the server
  106. if(res.currentPage) o.currentPage = parseInt(res.currentPage);
  107. // retrieve the saved columns
  108. _retrieveColumns.call(that, localStorage[that.localStorageId])
  109. // append the table
  110. $e.append(that.table());
  111. // append the detail boxes
  112. $e.prepend(that.$top_details);
  113. $e.append(that.$bottom_details);
  114. // render the rest of the table
  115. if(o.showHeader) that.header();
  116. if(o.showFooter) that.footer();
  117. // fill in the table body
  118. that.body();
  119. // render the pagination
  120. if(o.showTopPagination && that.pagination())
  121. that.$top_details.append(that.pagination().clone(true));
  122. if(o.showPagination && that.pagination())
  123. that.$bottom_details.append(that.pagination().clone(true));
  124. // update the details for the results
  125. that.details();
  126. // initialize the toolbar
  127. //_initToolbar.call(that);
  128. // nearly complete... let the user apply any final adjustments
  129. if(o.tableCallback && typeof o.tableCallback === 'function')
  130. o.tableCallback.call(that);
  131. that.loading( false );
  132. }
  133. , error: function( e ) {
  134. if(o.debug) console.log(e);
  135. showError.call(that);
  136. that.loading( false );
  137. }
  138. });
  139. }
  140. }
  141. , loading: function ( show ) {
  142. var $e = this.$element;
  143. if(!this.$loading) {
  144. this.$loading = $("<div></div>")
  145. .css({
  146. position: 'absolute'
  147. , top: parseInt($e.position().top) + 5
  148. , left: parseInt($e.position().left) + parseInt($e.css("marginLeft")) + Math.floor($e.width() / 4)
  149. , width: Math.floor($e.width() / 2) + "px"
  150. })
  151. .append(
  152. $("<div></div>")
  153. .addClass("progress progress-striped active")
  154. .append(
  155. $("<div></div>")
  156. .addClass("bar")
  157. )
  158. )
  159. .appendTo(document.body)
  160. }
  161. if(show) {
  162. $e.css({ opacity: 0.2 });
  163. }
  164. else {
  165. $e.css({ opacity: 1 });
  166. this.$loading.remove();
  167. this.$loading = undefined;
  168. }
  169. }
  170. , toolbar: function () {
  171. var o = this.options
  172. , $e = this.$element
  173. , that = this;
  174. this.$toolbar = $("<div></div>")
  175. .addClass("dt-toolbar btn-toolbar pull-right");
  176. this.$button_group = $("<div></div>")
  177. .addClass("btn-group")
  178. .appendTo(this.$toolbar);
  179. // add all the custom buttons
  180. for(var i = 0; i < o.buttons.length; i++) {
  181. this.buttons.push(o.buttons[i]);
  182. }
  183. // attach all buttons to the toolbar
  184. $.each(this.buttons, function() {
  185. that.$button_group.append(this);
  186. });
  187. // attach the toolbar to the section header
  188. if(o.sectionHeader) {
  189. this.$section_header = $(o.sectionHeader);
  190. this.$section_header.append(this.$toolbar);
  191. }
  192. else if(o.title !== '' && !this.$section_header) {
  193. this.$section_header = $("<h2></h2>")
  194. .text(o.title)
  195. .append(this.$toolbar);
  196. $e.before(this.$section_header);
  197. }
  198. else {
  199. if(!this.$toolbar_container) {
  200. this.$toolbar_container = $("<div></div>")
  201. .addClass('dt-toolbar-container clearfix')
  202. }
  203. $e.prepend(
  204. this.$toolbar_container
  205. .append(this.$toolbar)
  206. );
  207. }
  208. return this.$toolbar;
  209. }
  210. , details: function () {
  211. var o = this.options
  212. , res = this.resultset
  213. , start = 0
  214. , end = 0
  215. , that = this;
  216. start = (o.currentPage * o.perPage) - o.perPage + 1
  217. if(start < 1) start = 1;
  218. end = (o.currentPage * o.perPage);
  219. if(end > o.totalRows) end = o.totalRows;
  220. // $('<div class="pull-left"><p>Showing ' + start + ' to ' + end + ' of ' + o.totalRows + ' rows</p></div>')
  221. // .prependTo(this.$bottom_details);
  222. }
  223. , table: function () {
  224. var $e = this.$element
  225. , o = this.options;
  226. if(!this.$table_wrapper) {
  227. this.$wrapper = $("<div></div>")
  228. .addClass("dt-table-wrapper");
  229. }
  230. if (!this.$table) {
  231. this.$table = $('<table></table>')
  232. .addClass(o.class);
  233. }
  234. this.$wrapper.append(this.$table);
  235. return this.$wrapper;
  236. }
  237. , header: function () {
  238. var o = this.options
  239. , res = this.resultset;
  240. if(!this.$header) {
  241. this.$header = $('<thead></thead>');
  242. var row = $('<tr></tr>');
  243. // loop through the columns
  244. for(var column in o.columns) {
  245. var $cell = this.column(column)
  246. , colprop = $cell.data("column_properties");
  247. // attach the sort click event
  248. if(colprop.sortable && !colprop.custom)
  249. $cell.click(this, this.sort)
  250. .css({'cursor':'pointer'});
  251. if(o.sort)
  252. for(var i = 0; i < o.sort.length; i++) {
  253. if(o.sort[i][0] == colprop.field) {
  254. if(o.sort[i][1] == "asc") {
  255. $cell.append($(o.ascending));
  256. colprop.sortOrder = "asc";
  257. }
  258. else if(o.sort[i][1] == "desc") {
  259. $cell.append($(o.descending));
  260. colprop.sortOrder = "desc";
  261. }
  262. }
  263. }
  264. row.append($cell);
  265. this.$header.append(row);
  266. this.columns.push($cell);
  267. }
  268. // any final user adjustments to the header
  269. if(o.headerCallback && typeof o.headerCallback === 'function')
  270. o.headerCallback.call(this);
  271. this.$table
  272. .append(this.$header);
  273. }
  274. return this.$header;
  275. }
  276. , footer: function () {
  277. var o = this.options
  278. , res = this.resultset
  279. if(!this.$footer) {
  280. this.$footer = $('<tfoot></tfoot>');
  281. // loop through the columns
  282. for(column in o.columns) {
  283. var $cell = $('<td></td>')
  284. $cell
  285. .data("cell_properties", o.columns[column])
  286. .addClass(o.columns[column].classname)
  287. this.$footer.append($cell);
  288. }
  289. // any final user adjustments to the footer
  290. if(o.footerCallback && typeof o.footerCallback === 'function')
  291. o.footerCallback.call(this, this.resultset.footer)
  292. this.$table
  293. .append(this.$footer);
  294. }
  295. return this.$footer;
  296. }
  297. , body: function () {
  298. var res = this.resultset
  299. , o = this.options;
  300. if(!this.$body) {
  301. this.$body = $('<tbody></tbody>');
  302. // loop through the results
  303. for(var i = 0; i < res.data.length; i++) {
  304. var row = this.row(res.data[i]);
  305. this.$body.append(row);
  306. this.rows.push(row);
  307. }
  308. if(o.showFilterRow) this.$body.prepend(this.filter());
  309. this.$table
  310. .append(this.$body);
  311. }
  312. return this.$body;
  313. }
  314. , filter: function () {
  315. var $row = $("<tr></tr>")
  316. , o = this.options
  317. , that = this;
  318. $row.addClass("dt-filter-row");
  319. // loop through the columns
  320. for(var column in o.columns) {
  321. var $cell = $("<td></td>")
  322. .addClass(o.columns[column].classname);
  323. if(o.columns[column].hidden) $cell.hide();
  324. if(o.columns[column].filter && o.columns[column].field) {
  325. $cell
  326. .append(
  327. $("<input/>")
  328. .attr("name", "filter_" + o.columns[column].field)
  329. .data("filter", o.columns[column].field)
  330. .val(o.filter[o.columns[column].field] || "")
  331. // .change(this, this.runFilter)
  332. .change(function(e){
  333. runFilter.call(this, that);
  334. })
  335. );
  336. }
  337. $row.append($cell);
  338. }
  339. return $row;
  340. }
  341. , row: function ( rowdata ) {
  342. var $row = $("<tr></tr>")
  343. , o = this.options;
  344. // loop through the columns
  345. for(var column in o.columns) {
  346. var cell = this.cell( rowdata, column );
  347. $row.append(cell);
  348. }
  349. // callback for postprocessing on the row
  350. if(o.rowCallback && typeof o.rowCallback === "function")
  351. $row = o.rowCallback( $row, rowdata );
  352. return $row;
  353. }
  354. , cell: function ( data, column ) {
  355. var celldata = data[this.options.columns[column].field] || this.options.columns[column].custom
  356. , $cell = $('<td></td>')
  357. , o = this.options;
  358. // preprocess on the cell data for a column
  359. if(o.columns[column].callback && typeof o.columns[column].callback === "function")
  360. celldata = o.columns[column].callback.call( $cell, data, o.columns[column] )
  361. $cell
  362. .data("cell_properties", o.columns[column])
  363. .addClass(o.columns[column].classname)
  364. .append(celldata || "&nbsp;")
  365. if(o.columns[column].css) $cell.css(o.columns[column].css);
  366. if(o.columns[column].hidden) $cell.hide();
  367. return $cell;
  368. }
  369. , column: function ( column ) {
  370. var $cell = $('<th></th>')
  371. , o = this.options
  372. , classname = "dt-column_" + column + Math.floor((Math.random()*1000)+1);
  373. o.columns[column].classname = classname;
  374. $cell
  375. .data("column_properties", o.columns[column])
  376. .addClass(classname)
  377. .text(o.columns[column].title);
  378. if(o.columns[column].css) $cell.css(o.columns[column].css);
  379. if(o.columns[column].hidden) $cell.hide();
  380. return $cell;
  381. }
  382. , sort: function ( e ) {
  383. var colprop = $(this).data("column_properties")
  384. , that = e.data
  385. , o = e.data.options
  386. , found = false;
  387. colprop.sortOrder = colprop.sortOrder ? (colprop.sortOrder == "asc" ? "desc" : "") : "asc";
  388. if(o.allowMultipleSort) {
  389. // does the sort already exist?
  390. for(var i = 0; i < o.sort.length; i++) {
  391. if(o.sort[i][0] == colprop.field) {
  392. o.sort[i][1] = colprop.sortOrder;
  393. if(colprop.sortOrder === "") o.sort.splice(i,1);
  394. found = true;
  395. }
  396. }
  397. if(!found) o.sort.push([colprop.field, colprop.sortOrder]);
  398. }
  399. else {
  400. // clear out any current sorts
  401. o.sort = [];
  402. o.sort.push([colprop.field, colprop.sortOrder]);
  403. }
  404. if(o.debug) console.log(o.sort);
  405. that.render();
  406. }
  407. , pagination: function () {
  408. var $e = this.$element
  409. , that = this
  410. , o = this.options
  411. , res = this.resultset;
  412. // no paging needed
  413. if(o.perPage >= res.totalRows) return;
  414. if(!this.$pagination) {
  415. this.$pagination = $("<div></div>").addClass("pull-right");
  416. // how many pages?
  417. o.pageCount = Math.ceil(res.totalRows / o.perPage);
  418. // setup the pager container and the quick page buttons
  419. var $pager = $("<ul></ul>").addClass("pagination")
  420. , $first = $("<li></li>").append(
  421. $("<a></a>")
  422. .attr("href", "#")
  423. .data("page", 1)
  424. .html("&laquo;")
  425. .click(function() {
  426. o.currentPage = 1
  427. that.render();
  428. return false;
  429. })
  430. )
  431. , $previous = $("<li></li>").append(
  432. $("<a></a>")
  433. .attr("href", "#")
  434. .data("page", o.currentPage - 1)
  435. .html("&lt;")
  436. .click(function() {
  437. o.currentPage -= 1
  438. o.currentPage = o.currentPage >= 1 ? o.currentPage : 1
  439. that.render();
  440. return false;
  441. })
  442. )
  443. , $next = $("<li></li>").append(
  444. $("<a></a>")
  445. .attr("href", "#")
  446. .data("page", o.currentPage + 1)
  447. .html("&gt;")
  448. .click(function() {
  449. o.currentPage += 1
  450. o.currentPage = o.currentPage <= o.pageCount? o.currentPage : o.pageCount
  451. that.render();
  452. return false;
  453. })
  454. )
  455. , $last = $("<li></li>").append(
  456. $("<a></a>")
  457. .attr("href", "#")
  458. .data("page", o.pageCount)
  459. .html("&raquo;")
  460. .click(function() {
  461. o.currentPage = o.pageCount
  462. that.render();
  463. return false;
  464. })
  465. );
  466. var totalPages = o.pagePadding * 2
  467. , start
  468. , end;
  469. if(totalPages >= o.pageCount) {
  470. start = 1;
  471. end = o.pageCount;
  472. }
  473. else {
  474. start = o.currentPage - o.pagePadding;
  475. if(start <= 0) start = 1;
  476. end = start + totalPages;
  477. if(end > o.pageCount) {
  478. end = o.pageCount;
  479. start = end - totalPages;
  480. }
  481. }
  482. // append the pagination links
  483. for(var i = start; i <= end; i++) {
  484. var $link = $("<li></li>")
  485. .append(
  486. $("<a></a>")
  487. .attr("href", "#")
  488. .data("page", i)
  489. .text(i)
  490. .click(function() {
  491. o.currentPage = $(this).data('page')
  492. that.render();
  493. return false;
  494. })
  495. );
  496. if(i == o.currentPage) $link.addClass("active");
  497. $pager.append($link);
  498. }
  499. // append quick jump buttons
  500. if(o.currentPage == 1) {
  501. $first.addClass("disabled");
  502. $previous.addClass("disabled");
  503. }
  504. if(o.currentPage == o.pageCount) {
  505. $next.addClass("disabled");
  506. $last.addClass("disabled");
  507. }
  508. $pager.prepend($first, $previous);
  509. $pager.append($next, $last);
  510. this.$pagination.append($pager);
  511. }
  512. return this.$pagination;
  513. }
  514. , remove: function() {
  515. var $e = this.$element
  516. if(this.$section_header) this.$section_header.remove();
  517. $e.data("datatable", null);
  518. $e.empty();
  519. }
  520. };
  521. /* DATATABLE PRIVATE METHODS
  522. * ========================= */
  523. function _initToolbar() {
  524. var o = this.options;
  525. // create the perpage dropdown
  526. _initPerPage.call(this);
  527. // handle filter options
  528. if(o.filterModal) _initFilterModal.call(this);
  529. // handle the column management
  530. if(o.toggleColumns) _initColumnModal.call(this);
  531. // handle the overflow option
  532. if(o.allowOverflow) _initOverflowToggle.call(this);
  533. // initialize the table info
  534. if(o.allowTableinfo) _initTableInfo.call(this);
  535. // create the buttons and section functions
  536. this.toolbar();
  537. }
  538. function _initColumnModal() {
  539. var o = this.options
  540. , $e = this.$element
  541. , $top_details = this.$top_details
  542. , $toggle = $("<a></a>");
  543. // localize the object
  544. var that = this;
  545. if(!this.$column_modal) {
  546. var randId = Math.floor((Math.random()*100)+1);
  547. this.$column_modal = $('<div></div>')
  548. .attr("id", "dt-column-modal_" + randId)
  549. .attr("tabindex","-1")
  550. .attr("role","dialog")
  551. .attr("aria-labelledby", "dt-column-modal-label_" + randId)
  552. .attr("aria-hidden","true")
  553. .addClass("modal fade")
  554. .hide();
  555. // render the modal header
  556. this.$column_modalheader = $("<div></div>")
  557. .addClass("modal-header")
  558. .append(
  559. $("<button></button>")
  560. .addClass("close")
  561. .data("dismiss", "modal")
  562. .attr("aria-hidden","true")
  563. .html('&times;')
  564. .click(function(){
  565. that.$column_modal.modal('hide');
  566. })
  567. )
  568. .append(
  569. $("<h3></h3>")
  570. .addClass("modal-title")
  571. .attr("id","dt-column-modal-label_" + randId)
  572. .text("Toggle Columns")
  573. );
  574. // render the modal footer
  575. this.$column_modalfooter = $("<div></div>")
  576. .addClass("modal-footer")
  577. .append(
  578. // show the check 'all / none' columns
  579. $('<div class="pull-left"></div>')
  580. .append(
  581. $('<div class="btn-group"></div>')
  582. .append(
  583. $('<button></button>')
  584. .addClass("btn btn-info")
  585. .append(
  586. $("<span></span>")
  587. .addClass("glyphicon glyphicon-check")
  588. .text("All")
  589. )
  590. .click(function () {
  591. $(this).closest(".modal").find('button.on-off').each(function () {
  592. if($(this).data('column-hidden')){
  593. $(this).click();
  594. }
  595. })
  596. return false;
  597. }),
  598. $('<button></button>')
  599. .addClass("btn btn-warning")
  600. .append(
  601. $("<span></span>")
  602. .addClass("glyphicon glyphicon-unchecked")
  603. .text("None")
  604. )
  605. .click(function () {
  606. $(this).closest(".modal").find('button.on-off').each(function () {
  607. if(!$(this).data('column-hidden')){
  608. $(this).click();
  609. }
  610. })
  611. return false;
  612. })
  613. )
  614. )
  615. , o.allowSaveColumns ? $("<button></button>")
  616. .addClass("btn btn-primary")
  617. .text("Save")
  618. .click(function() {
  619. _saveColumns.call(that)
  620. return false;
  621. }) : ""
  622. , $("<button></button>")
  623. .addClass("btn btn-default")
  624. .data('dismiss', 'modal')
  625. .append(
  626. $("<span></span>")
  627. )
  628. .text("Close")
  629. .click(function() {
  630. that.$column_modal.modal('hide')
  631. return false;
  632. })
  633. );
  634. // render the modal body
  635. this.$column_modalbody = $("<div></div>")
  636. .addClass("modal-body");
  637. this.$column_modaldialog = $("<div></div>")
  638. .addClass("modal-dialog")
  639. .append(
  640. $("<div></div>")
  641. .addClass("modal-content")
  642. .append(
  643. this.$column_modalheader
  644. ,this.$column_modalbody
  645. ,this.$column_modalfooter
  646. )
  647. );
  648. // render and add the modal to the container
  649. this.$column_modal
  650. .append(
  651. this.$column_modaldialog
  652. ).appendTo(document.body);
  653. }
  654. // render the display modal button
  655. $toggle
  656. .addClass("btn")
  657. .data("toggle", "modal")
  658. .data("content", "Choose which columns you would like to display.")
  659. .data("target","#" + this.$column_modal.attr("id"))
  660. .attr("href", "#" + this.$column_modal.attr("id"))
  661. .append(
  662. $("<span></span>")
  663. .addClass("glyphicon glyphicon-cog")
  664. )
  665. .click(function(e) {
  666. that.$column_modal
  667. .on('show.bs.modal', function () {
  668. if(o.debug) console.log(that);
  669. _updateColumnModalBody.call(that, that.$column_modalbody);
  670. })
  671. .modal();
  672. return false;
  673. })
  674. .popover({
  675. "trigger": 'hover',
  676. "placement":'top'
  677. });
  678. this.buttons.unshift($toggle);
  679. if(o.debug) console.log($toggle);
  680. return this.$column_modal;
  681. }
  682. function _initFilterModal() {
  683. var o = this.options
  684. , $e = this.$element
  685. , $toggle = $("<a></a>");
  686. // render the display modal button
  687. $toggle
  688. .addClass("btn")
  689. .data("toggle", "modal")
  690. .attr("href", "#")
  691. .data("content", "Open the filter dialog.")
  692. .extend(
  693. $("<span></span>")
  694. .addClass("glyphicon glyphicon-filter")
  695. )
  696. .click(function() {
  697. if($(o.filterModal).hasClass("modal"))
  698. $(o.filterModal)
  699. .modal();
  700. else if($(o.filterModal).is(":visible"))
  701. $(o.filterModal)
  702. .hide();
  703. else
  704. $(o.filterModal)
  705. .show();
  706. return false;
  707. })
  708. .popover({
  709. "trigger": 'hover',
  710. "placement":'top'
  711. });
  712. this.buttons.unshift($toggle);
  713. }
  714. function _initPerPage() {
  715. var o = this.options
  716. , $e = this.$element
  717. , that = this;
  718. // per page options and current filter/sorting
  719. var $perpage_select = $("<a></a>")
  720. .addClass("btn dropdown-toggle")
  721. .data("content", "Change the number of rows per page.")
  722. .attr("data-toggle", "dropdown")
  723. .html(o.perPage + "&nbsp;")
  724. .css({ fontWeight: 'normal' })
  725. .append(
  726. $("<span></span>")
  727. .addClass("caret")
  728. )
  729. .popover({
  730. "trigger": 'hover',
  731. "placement":'top'
  732. });
  733. this.buttons.push($perpage_select);
  734. var $perpage_values = $("<ul></ul>")
  735. .addClass("dropdown-menu")
  736. .css({ fontSize: 'initial', fontWeight: 'normal' })
  737. .append(
  738. $('<li data-value="10"><a href="#">10</a></li>')
  739. .click(function() { _updatePerPage.call(this, that); return false; })
  740. , $('<li data-value="20"><a href="#">20</a></li>')
  741. .click(function() { _updatePerPage.call(this, that); return false; })
  742. , $('<li data-value="50"><a href="#">50</a></li>')
  743. .click(function() { _updatePerPage.call(this, that); return false; })
  744. , $('<li data-value="100"><a href="#">100</a></li>')
  745. .click(function() { _updatePerPage.call(this, that); return false; })
  746. , $('<li data-value="150"><a href="#">200</a></li>')
  747. .click(function() { _updatePerPage.call(this, that); return false; })
  748. );
  749. this.buttons.push($perpage_values);
  750. }
  751. function _initTableInfo() {
  752. var o = this.options
  753. , $e = this.$element
  754. , $info = $("<a></a>");
  755. // render the display modal button
  756. $info
  757. .addClass("btn")
  758. .attr("href", "#")
  759. .append(
  760. $("<span></span>")
  761. .addClass("glyphicon glyphicon-info-sign")
  762. )
  763. .click(function() {
  764. return false;
  765. });
  766. var $page_sort = []
  767. , $page_filter = [];
  768. // sort
  769. $.each(o.sort, function(i, v){
  770. if(!v.length) return;
  771. var heading;
  772. for(var column in o.columns) {
  773. if(o.columns[column].field == v[0]) heading = o.columns[column].title;
  774. }
  775. $page_sort.push( heading + " " + v[1].toUpperCase() );
  776. });
  777. // filter
  778. $.each(o.filter, function(k, v) {
  779. var heading;
  780. for(var column in o.columns) {
  781. if(o.columns[column].field == k) heading = o.columns[column].title;
  782. }
  783. $page_filter.push( (heading || k) + " = '" + v + "'" );
  784. });
  785. $($info)
  786. .data("content",
  787. $('<dl></dl>').append(
  788. $page_sort.length > 0 ? '<dt><i class="icon-th-list"></i> Sort:</dt><dd>' + $page_sort.join(", ") + '</dd>' : ''
  789. ,
  790. $page_filter.length > 0 ? '<dt><i class="icon-filter"></i> Filter:</dt><dd>' + $page_filter.join(", ") + '</dd>' : ''
  791. ))
  792. .popover({
  793. placement: "bottom"
  794. });
  795. this.buttons.unshift($info);
  796. }
  797. function _initOverflowToggle() {
  798. var o = this.options
  799. , $wrapper = this.$wrapper
  800. , $overflow = $("<a></a>");
  801. // create the button
  802. $overflow
  803. .addClass("btn")
  804. .attr("href", "#")
  805. .attr("title", "Toggle the size of the table to fit the data or to fit the screen.")
  806. .append(
  807. $("<span></span>")
  808. .addClass("glyphicon glyphicon-resize-full")
  809. )
  810. .click(function() {
  811. if($wrapper) _toggleOverflow.call(this, $wrapper);
  812. return false;
  813. });
  814. if(!this.resultset || !this.resultset.data || this.resultset.data.length == 0)
  815. $overflow
  816. .addClass("disabled")
  817. this.buttons.push($overflow);
  818. }
  819. function _toggleOverflow(el) {
  820. if(el.css('overflow') == 'scroll') {
  821. $(this).children("span.glyphicon")
  822. .attr("class", "glyphicon glyphicon-resize-full");
  823. el.css({
  824. overflow: 'visible'
  825. , width: 'auto'
  826. });
  827. }
  828. else {
  829. $(this).children("span.glyphicon")
  830. .attr("class", "glyphicon glyphicon-resize-small");
  831. el.css({
  832. overflow: 'scroll'
  833. , width: el.width()
  834. });
  835. }
  836. }
  837. function _updatePerPage(that) {
  838. var o = that.options;
  839. // update the perpage value
  840. o.perPage = $(this).data("value");
  841. // the offset
  842. var offset = o.currentPage * o.perPage;
  843. while(offset > o.totalRows) {
  844. o.currentPage--;
  845. offset = o.currentPage * o.perPage;
  846. }
  847. if(o.currentPage < 1) o.currentPage = 1;
  848. if($(this).popover) $(this).popover('hide');
  849. // update the table
  850. that.render();
  851. return false;
  852. }
  853. function showError() {
  854. var o = this.options
  855. , $e = this.$element;
  856. $e.empty();
  857. // initialize the toolbar
  858. _initToolbar.call(this);
  859. // nearly complete... let the user apply any final adjustments
  860. if(o.tableCallback && typeof o.tableCallback === 'function')
  861. o.tableCallback.call(this);
  862. this.loading( false );
  863. if(this.$default) $e.append(this.$default);
  864. }
  865. function runFilter(that) {
  866. var o = that.options;
  867. o.filter[$(this).data("filter")] = $(this).val();
  868. if(o.debug) console.log(o.filter);
  869. that.render();
  870. }
  871. function _updateColumnModalBody(body) {
  872. var o = this.options
  873. , $container = $("<form></form>").addClass("form-inline")
  874. , that = this;
  875. // loop through the columns
  876. for(var column in o.columns) {
  877. if(o.columns[column].title === "") continue;
  878. var $item = $('<div></div>')
  879. .addClass('form-group')
  880. .append(
  881. $("<label></label>")
  882. .addClass("control-label")
  883. .append(
  884. o.columns[column].title,
  885. $("<button></button>")
  886. .addClass("on-off btn " + (o.columns[column].hidden ? 'btn-info' : 'btn-warning'))
  887. .data("column", column)
  888. .data("column-hidden",o.columns[column].hidden)
  889. .text(o.columns[column].hidden ? "ON" : "OFF")
  890. .click(function () {
  891. _toggleColumn.call(this, that);
  892. return false;
  893. })
  894. )
  895. );
  896. $container.append($item);
  897. }
  898. body.empty();
  899. body.append(
  900. $container
  901. );
  902. }
  903. function _toggleColumn(that) {
  904. var o = that.options
  905. , column = $(this).data("column")
  906. , $column = $("." + o.columns[column].classname);
  907. if($column.is(":visible")) {
  908. $column.hide();
  909. o.columns[column].hidden = true;
  910. $(this).removeClass("btn-warning").addClass("btn-info").text("ON").data("column-hidden",true);
  911. }
  912. else {
  913. $column.show();
  914. o.columns[column].hidden = false;
  915. $(this).removeClass("btn-info").addClass("btn-warning").text("OFF").data("column-hidden",false);
  916. }
  917. return false;
  918. }
  919. function _saveColumns() {
  920. var o = this.options
  921. , columns = [];
  922. // save the columns to the localstorage
  923. if(localStorage) {
  924. localStorage[this.localStorageId] = JSON.stringify(o.columns);
  925. }
  926. $.ajax({
  927. url: o.url
  928. , type: "POST"
  929. , dataType: "json"
  930. , data: $.extend({}, o.post, {
  931. action: "save-columns"
  932. , columns: JSON.stringify(o.columns)
  933. , sort: JSON.stringify(o.sort)
  934. , filter: JSON.stringify(o.filter)
  935. })
  936. , success: function( res ) {
  937. if(o.debug) console.log("columns saved");
  938. }
  939. });
  940. this.$column_modal.modal("hide");
  941. }
  942. function _retrieveColumns(data) {
  943. var o = this.options
  944. , columns = data ? JSON.parse(data) : []
  945. , res = this.resultset;
  946. // if the server doesn't pass the column property back
  947. if(!res.columns) res.columns = [];
  948. for(column in o.columns) {
  949. o.columns[column] = $.extend({}, o.columns[column], res.columns[column], columns[column]);
  950. }
  951. }
  952. /* DATATABLE PLUGIN DEFINITION
  953. * =========================== */
  954. $.fn.datatable = function ( options ) {
  955. $.fn.datatable.init.call(this, options, DataTable, 'datatable');
  956. return this;
  957. };
  958. $.fn.datatable.init = function ( options, Constructor, name ) {
  959. var datatable;
  960. if (options === true) {
  961. return this.data(name);
  962. } else if (typeof options == 'string') {
  963. datatable = this.data(name);
  964. if (datatable) {
  965. datatable[options]();
  966. }
  967. return this;
  968. }
  969. options = $.extend({}, $.fn[name].defaults, options);
  970. function get ( el ) {
  971. var datatable = $.data(el, name);
  972. if (!datatable) {
  973. datatable = new Constructor(el, $.fn.datatable.elementOptions(el, options));
  974. $.data(el, name, datatable);
  975. }
  976. return datatable;
  977. }
  978. this.each(function() {
  979. get(this);
  980. });
  981. return this;
  982. };
  983. $.fn.datatable.DataTable = DataTable;
  984. $.fn.datatable.elementOptions = function ( el, options ) {
  985. return $.metadata ? $.extend({}, options, $(el).metadata()) : options;
  986. };
  987. $.fn.datatable.defaults = {
  988. debug: true,
  989. id: undefined,
  990. title: 'Data Table Results',
  991. class: 'table table-striped table-bordered table-hover',
  992. perPage: 10,
  993. pagePadding: 2,
  994. sort: [],
  995. filter: {},
  996. post: {},
  997. buttons: [],
  998. sectionHeader: undefined,
  999. totalRows: 0,
  1000. currentPage: 1,
  1001. showPagination: true,
  1002. showTopPagination: false,
  1003. showHeader: true,
  1004. showFooter: false,
  1005. showFilterRow: false,
  1006. filterModal: undefined,
  1007. allowExport: false,
  1008. allowOverflow: true,
  1009. allowMultipleSort: false,
  1010. allowSaveColumns: true,
  1011. toggleColumns: true,
  1012. url: '',
  1013. columns: [],
  1014. ascending: $("<span></span>").addClass("glyphicon glyphicon-chevron-up"),
  1015. descending: $("<span></span>").addClass("glyphicon glyphicon-chevron-down"),
  1016. rowCallback: undefined,
  1017. tableCallback: undefined,
  1018. headerCallback: undefined,
  1019. footerCallback: undefined,
  1020. tablePreRender: undefined,
  1021. checkbox:undefined,
  1022. autoLoad: true
  1023. };
  1024. })(window.jQuery);