├── .editorconfig ├── .gitignore ├── .travis.yml ├── src ├── index.js ├── config.js ├── date.js ├── table.js ├── rows.js ├── style.css ├── helpers.js ├── columns.js └── datatable.js ├── docs ├── demo.css ├── 3-ajax │ ├── index.html │ └── ajax-example.txt ├── index.html ├── 5-column-manipulation │ ├── datatable.column.json │ ├── index.html │ └── datatable.json ├── index.js ├── 4-render-column-cell │ └── index.html ├── 2-filters │ └── index.html ├── umd.html ├── 7-init-destroy-import-export │ ├── utils.js │ └── index.html ├── 1-simple │ └── index.html └── 6-datetime-sorting │ └── index.html ├── rollup.docs.config.js ├── rollup.config.js ├── package.json ├── README.md ├── LICENSE └── .eslintrc.js /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | indent_style = space 5 | indent_size = 4 -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | dist/ 2 | node_modules/ 3 | tests/qunit.css 4 | package-lock.json 5 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | "10" 4 | before_install: 5 | npm update -g npm 6 | 7 | sudo: false 8 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * 3 | * Vanilla-DataTables 4 | * Copyright (c) 2015-2017 Karl Saunders (http://mobius.ovh) 5 | * Licensed under MIT (http://www.opensource.org/licenses/mit-license.php) 6 | * 7 | * Version: 1.6.14 8 | * 9 | */ 10 | 11 | export {DataTable} from "./datatable" 12 | -------------------------------------------------------------------------------- /docs/demo.css: -------------------------------------------------------------------------------- 1 | * { margin: 0; padding: 0; box-sizing: border-box; font-family: Roboto, Arial, Helvetica, sans-serif; } 2 | html, body { overflow-x: hidden; overflow-y: auto; } 3 | body { margin: 5vw; } 4 | 5 | h1 { margin: 1rem 0 2rem; } 6 | button { background: blue; color: white; border: none; border-radius: 4px; padding: .25rem .5rem; margin: 0 0 0 .25rem; } -------------------------------------------------------------------------------- /docs/3-ajax/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | AJAX Example 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 |

AJAX Example

16 |
17 | 18 | 19 | 20 | 21 | 32 | 33 | 34 | -------------------------------------------------------------------------------- /src/config.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Default configuration 3 | * @typ {Object} 4 | */ 5 | export const defaultConfig = { 6 | sortable: true, 7 | searchable: true, 8 | 9 | // Pagination 10 | paging: true, 11 | perPage: 10, 12 | perPageSelect: [5, 10, 15, 20, 25], 13 | nextPrev: true, 14 | firstLast: false, 15 | prevText: "‹", 16 | nextText: "›", 17 | firstText: "«", 18 | lastText: "»", 19 | ellipsisText: "…", 20 | ascText: "▴", 21 | descText: "▾", 22 | truncatePager: true, 23 | pagerDelta: 2, 24 | 25 | scrollY: "", 26 | 27 | fixedColumns: true, 28 | fixedHeight: false, 29 | 30 | header: true, 31 | footer: false, 32 | 33 | // Customise the display text 34 | labels: { 35 | placeholder: "Search...", // The search input placeholder 36 | perPage: "{select} entries per page", // per-page dropdown label 37 | noRows: "No entries found", // Message shown when there are no search results 38 | info: "Showing {start} to {end} of {rows} entries" // 39 | }, 40 | 41 | // Customise the layout 42 | layout: { 43 | top: "{select}{search}", 44 | bottom: "{info}{pager}" 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /docs/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Demo 7 | 8 | 13 | 14 | 15 |

Demos

16 | 26 | 27 | 31 | 41 | 42 | 43 | -------------------------------------------------------------------------------- /src/date.js: -------------------------------------------------------------------------------- 1 | import dayjs from "dayjs" 2 | import customParseFormat from "dayjs/plugin/customParseFormat" 3 | 4 | dayjs.extend(customParseFormat) 5 | 6 | /** 7 | * Use dayjs to parse cell contents for sorting 8 | * @param {String} content The datetime string to parse 9 | * @param {String} format The format for dayjs to use 10 | * @return {String|Boolean} Datatime string or false 11 | */ 12 | export const parseDate = (content, format) => { 13 | let date = false 14 | 15 | // Converting to YYYYMMDD ensures we can accurately sort the column numerically 16 | 17 | if (format) { 18 | switch (format) { 19 | case "ISO_8601": 20 | // ISO8601 is already lexiographically sorted, so we can just sort it as a string. 21 | date = content 22 | break 23 | case "RFC_2822": 24 | date = dayjs(content, "ddd, MM MMM YYYY HH:mm:ss ZZ").format("YYYYMMDD") 25 | break 26 | case "MYSQL": 27 | date = dayjs(content, "YYYY-MM-DD hh:mm:ss").format("YYYYMMDD") 28 | break 29 | case "UNIX": 30 | date = dayjs(content).unix() 31 | break 32 | // User defined format using the data-format attribute or columns[n].format option 33 | default: 34 | date = dayjs(content, format).format("YYYYMMDD") 35 | break 36 | } 37 | } 38 | 39 | return date 40 | } 41 | -------------------------------------------------------------------------------- /rollup.docs.config.js: -------------------------------------------------------------------------------- 1 | import commonjs from 'rollup-plugin-commonjs' 2 | import globals from 'rollup-plugin-node-globals' 3 | import builtins from 'rollup-plugin-node-builtins' 4 | import resolve from 'rollup-plugin-node-resolve' 5 | import babel from 'rollup-plugin-babel' 6 | import {terser} from 'rollup-plugin-terser' 7 | 8 | export default [ 9 | { 10 | input: 'docs/index.js', 11 | plugins: [ 12 | resolve({browser: true}), 13 | commonjs(), 14 | builtins(), 15 | globals(), 16 | babel({ 17 | plugins: [ 18 | '@babel/plugin-syntax-dynamic-import' 19 | ] 20 | }), 21 | terser() 22 | ], 23 | // ES module version, for modern browsers 24 | output: { 25 | dir: "docs/dist/module", 26 | format: "es", 27 | sourcemap: true 28 | } 29 | }, 30 | { 31 | input: 'docs/index.js', 32 | plugins: [ 33 | resolve({browser: true}), 34 | commonjs(), 35 | builtins(), 36 | globals(), 37 | babel({ 38 | plugins: [ 39 | '@babel/plugin-syntax-dynamic-import' 40 | ] 41 | }), 42 | terser() 43 | ], 44 | // SystemJS version, for older browsers 45 | output: { 46 | dir: "docs/dist/nomodule", 47 | format: "system", 48 | sourcemap: true 49 | } 50 | } 51 | ] 52 | -------------------------------------------------------------------------------- /docs/5-column-manipulation/datatable.column.json: -------------------------------------------------------------------------------- 1 | { 2 | "heading": "Completion", 3 | "data": [ 4 | "37%", 5 | "97%", 6 | "63%", 7 | "30%", 8 | "17%", 9 | "57%", 10 | "93%", 11 | "100%", 12 | "44%", 13 | "33%", 14 | "77%", 15 | "49%", 16 | "9%", 17 | "24%", 18 | "10%", 19 | "14%", 20 | "58%", 21 | "58%", 22 | "34%", 23 | "18%", 24 | "2%", 25 | "93%", 26 | "94%", 27 | "55%", 28 | "81%", 29 | "48%", 30 | "76%", 31 | "62%", 32 | "67%", 33 | "9%", 34 | "13%", 35 | "13%", 36 | "56%", 37 | "28%", 38 | "15%", 39 | "30%", 40 | "51%", 41 | "85%", 42 | "92%", 43 | "50%", 44 | "92%", 45 | "17%", 46 | "96%", 47 | "26%", 48 | "71%", 49 | "82%", 50 | "87%", 51 | "94%", 52 | "8%", 53 | "21%", 54 | "52%", 55 | "72%", 56 | "6%", 57 | "58%", 58 | "70%", 59 | "59%", 60 | "88%", 61 | "79%", 62 | "36%", 63 | "71%", 64 | "89%", 65 | "98%", 66 | "88%", 67 | "8%", 68 | "22%", 69 | "16%", 70 | "41%", 71 | "16%", 72 | "23%", 73 | "65%", 74 | "61%", 75 | "24%", 76 | "90%", 77 | "37%", 78 | "41%", 79 | "2%", 80 | "72%", 81 | "23%", 82 | "94%", 83 | "5%", 84 | "50%", 85 | "2%", 86 | "74%", 87 | "53%", 88 | "51%", 89 | "48%", 90 | "25%", 91 | "93%", 92 | "65%", 93 | "86%", 94 | "72%", 95 | "20%", 96 | "41%", 97 | "47%", 98 | "24%", 99 | "68%", 100 | "4%", 101 | "16%", 102 | "39%", 103 | "31%" 104 | ] 105 | } -------------------------------------------------------------------------------- /src/table.js: -------------------------------------------------------------------------------- 1 | import {createElement} from "./helpers" 2 | 3 | /** 4 | * Parse data to HTML table 5 | */ 6 | export const dataToTable = function (data) { 7 | let thead = false 8 | let tbody = false 9 | 10 | data = data || this.options.data 11 | 12 | if (data.headings) { 13 | thead = createElement("thead") 14 | const tr = createElement("tr") 15 | data.headings.forEach(col => { 16 | const td = createElement("th", { 17 | html: col 18 | }) 19 | tr.appendChild(td) 20 | }) 21 | 22 | thead.appendChild(tr) 23 | } 24 | 25 | if (data.data && data.data.length) { 26 | tbody = createElement("tbody") 27 | data.data.forEach(rows => { 28 | if (data.headings) { 29 | if (data.headings.length !== rows.length) { 30 | throw new Error( 31 | "The number of rows do not match the number of headings." 32 | ) 33 | } 34 | } 35 | const tr = createElement("tr") 36 | rows.forEach(value => { 37 | const td = createElement("td", { 38 | html: value 39 | }) 40 | tr.appendChild(td) 41 | }) 42 | tbody.appendChild(tr) 43 | }) 44 | } 45 | 46 | if (thead) { 47 | if (this.table.tHead !== null) { 48 | this.table.removeChild(this.table.tHead) 49 | } 50 | this.table.appendChild(thead) 51 | } 52 | 53 | if (tbody) { 54 | if (this.table.tBodies.length) { 55 | this.table.removeChild(this.table.tBodies[0]) 56 | } 57 | this.table.appendChild(tbody) 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /rollup.config.js: -------------------------------------------------------------------------------- 1 | import commonjs from 'rollup-plugin-commonjs' 2 | import globals from 'rollup-plugin-node-globals' 3 | import builtins from 'rollup-plugin-node-builtins' 4 | import resolve from 'rollup-plugin-node-resolve' 5 | import babel from 'rollup-plugin-babel' 6 | import {terser} from 'rollup-plugin-terser' 7 | 8 | export default [ 9 | { 10 | input: 'src/index.js', 11 | plugins: [ 12 | resolve({browser: true}), 13 | commonjs(), 14 | builtins(), 15 | globals(), 16 | babel({ 17 | plugins: [ 18 | '@babel/plugin-syntax-dynamic-import' 19 | ] 20 | }), 21 | terser() 22 | ], 23 | output: // ES module version, for modern browsers 24 | { 25 | dir: "dist/module", 26 | format: "es", 27 | sourcemap: true 28 | } 29 | }, 30 | { 31 | input: 'src/index.js', 32 | plugins: [ 33 | resolve({browser: true}), 34 | commonjs(), 35 | builtins(), 36 | globals(), 37 | babel({ 38 | plugins: [ 39 | '@babel/plugin-syntax-dynamic-import' 40 | ] 41 | }), 42 | terser() 43 | ], 44 | output: // SystemJS version, for older browsers 45 | { 46 | dir: "dist/nomodule", 47 | format: "system", 48 | sourcemap: true 49 | }, 50 | }, 51 | { 52 | input: 'src/index.js', 53 | plugins: [ 54 | resolve({browser: true}), 55 | commonjs(), 56 | builtins(), 57 | globals(), 58 | babel({ 59 | plugins: [ 60 | '@babel/plugin-syntax-dynamic-import' 61 | ] 62 | }), 63 | terser() 64 | ], 65 | output: // CJS version 66 | { 67 | dir: "dist", 68 | format: "cjs", 69 | sourcemap: true 70 | } 71 | } 72 | ] 73 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "simple-datatables-classic", 3 | "version": "2.1.14", 4 | "description": "A lightweight, dependency-free JavaScript HTML table plugin.", 5 | "main": "dist/index.js", 6 | "jsdelivr": "dist/umd/simple-datatables.js", 7 | "unpkg": "dist/umd/simple-datatables.js", 8 | "module": "src/index.js", 9 | "scripts": { 10 | "test": "npm run lint", 11 | "lint": "eslint src/", 12 | "compile": "npm run compile_js && npm run compile_js_umd && npm run compile_css && npm run compile_docs", 13 | "compile_js": "rollup -c", 14 | "compile_js_umd": "browserify dist/index.js --standalone simpleDatatables -o dist/umd/simple-datatables.js", 15 | "compile_css": "cp src/style.css dist/style.css", 16 | "compile_docs": "npm run compile_docs_js && cp node_modules/systemjs/dist/s.min.js docs/dist/nomodule/ && cp src/style.css docs/dist/style.css", 17 | "compile_docs_js": "rollup -c rollup.docs.config.js", 18 | "postcompile_docs": "cp -r dist/umd docs/dist", 19 | "prepare": "npm run compile" 20 | }, 21 | "repository": { 22 | "type": "git", 23 | "url": "git+https://github.com/fiduswriter/Simple-DataTables-classic.git" 24 | }, 25 | "keywords": [ 26 | "DataTable", 27 | "DataTables", 28 | "table", 29 | "html table", 30 | "filter", 31 | "sort" 32 | ], 33 | "author": "Johannes Wilm", 34 | "license": "LGPL-3.0", 35 | "bugs": { 36 | "url": "https://github.com/fiduswriter/Simple-DataTables-classic/issues" 37 | }, 38 | "homepage": "https://github.com/fiduswriter/Simple-DataTables-classic#readme", 39 | "devDependencies": { 40 | "@babel/core": "^7.10.2", 41 | "@babel/plugin-syntax-dynamic-import": "^7.8.3", 42 | "babel-eslint": "^10.1.0", 43 | "browserify": "^16.5.1", 44 | "eslint": "^7.2.0", 45 | "rollup": "^2.15.0", 46 | "rollup-plugin-babel": "^4.4.0", 47 | "rollup-plugin-commonjs": "^10.1.0", 48 | "rollup-plugin-node-builtins": "^2.1.2", 49 | "rollup-plugin-node-globals": "^1.4.0", 50 | "rollup-plugin-node-resolve": "^5.2.0", 51 | "rollup-plugin-terser": "^6.1.0", 52 | "systemjs": "^6.3.2", 53 | "updates": "^10.2.13" 54 | }, 55 | "dependencies": { 56 | "dayjs": "^1.8.28" 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /src/rows.js: -------------------------------------------------------------------------------- 1 | import {createElement} from "./helpers" 2 | /** 3 | * Rows API 4 | * @param {Object} instance DataTable instance 5 | * @param {Array} rows 6 | */ 7 | export class Rows { 8 | constructor(dt, rows) { 9 | this.dt = dt 10 | this.rows = rows 11 | 12 | return this 13 | } 14 | 15 | /** 16 | * Build a new row 17 | * @param {Array} row 18 | * @return {HTMLElement} 19 | */ 20 | build(row) { 21 | const tr = createElement("tr") 22 | 23 | let headings = this.dt.headings 24 | 25 | if (!headings.length) { 26 | headings = row.map(() => "") 27 | } 28 | 29 | headings.forEach((h, i) => { 30 | const td = createElement("td") 31 | 32 | // Fixes #29 33 | if (!row[i] || !row[i].length) { 34 | row[i] = "" 35 | } 36 | 37 | td.innerHTML = row[i] 38 | 39 | td.data = row[i] 40 | 41 | tr.appendChild(td) 42 | }) 43 | 44 | return tr 45 | } 46 | 47 | render(row) { 48 | return row 49 | } 50 | 51 | /** 52 | * Add new row 53 | * @param {Array} select 54 | */ 55 | add(data) { 56 | if (Array.isArray(data)) { 57 | const dt = this.dt 58 | // Check for multiple rows 59 | if (Array.isArray(data[0])) { 60 | data.forEach(row => { 61 | dt.data.push(this.build(row)) 62 | }) 63 | } else { 64 | dt.data.push(this.build(data)) 65 | } 66 | 67 | // We may have added data to an empty table 68 | if ( dt.data.length ) { 69 | dt.hasRows = true 70 | } 71 | 72 | 73 | this.update() 74 | 75 | dt.columns().rebuild() 76 | } 77 | 78 | } 79 | 80 | /** 81 | * Remove row(s) 82 | * @param {Array|Number} select 83 | * @return {Void} 84 | */ 85 | remove(select) { 86 | const dt = this.dt 87 | 88 | if (Array.isArray(select)) { 89 | // Remove in reverse otherwise the indexes will be incorrect 90 | select.sort((a, b) => b - a) 91 | 92 | select.forEach(row => { 93 | dt.data.splice(row, 1) 94 | }) 95 | } else if (select == 'all') { 96 | dt.data = []; 97 | } else { 98 | dt.data.splice(select, 1) 99 | } 100 | 101 | // We may have emptied the table 102 | if ( !dt.data.length ) { 103 | dt.hasRows = false 104 | } 105 | 106 | this.update() 107 | dt.columns().rebuild() 108 | } 109 | 110 | /** 111 | * Update row indexes 112 | * @return {Void} 113 | */ 114 | update() { 115 | this.dt.data.forEach((row, i) => { 116 | row.dataIndex = i 117 | }) 118 | } 119 | } 120 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Simple-DataTables-classic 2 | 3 | A lightweight, extendable, dependency-free javascript HTML table plugin **that also works in older browsers**. Similar to jQuery DataTables, but without the jQuery dependency. See [Simple-DataTables](https://github.com/fiduswriter/Simple-DataTables) for a version that uses up-to-date technology and only targets current browsers. 4 | 5 | Based on [Vanilla-DataTables](https://github.com/Mobius1/Vanilla-DataTables), but written in ES2018. 6 | 7 | See the demos [here](https://fiduswriter.github.io/Simple-DataTables-classic/). 8 | 9 | # CDN 10 | 11 | To use the CDN version of Simple-DataTables use either [https://cdn.jsdelivr.net/npm/simple-datatables-classic@latest](https://cdn.jsdelivr.net/npm/simple-datatables-classic@latest) or [https://unpkg.com/simple-datatables-classic](https://unpkg.com/simple-datatables-classic). You also need to add the CSS styling, so the elements you'll add to html head element can for example be these: 12 | 13 | ```html 14 | 15 | 16 | ``` 17 | 18 | 19 | 20 | ### License 21 | 22 | LGPL 23 | 24 | ### Features 25 | 26 | * Sortable columns 27 | * Pagination 28 | * Searchable 29 | * Customisable layout 30 | * Customisable labels 31 | * Customise column rendering 32 | * Load data via AJAX requests 33 | * Export to common formats like `csv`, `txt` `json`, and `sql` 34 | * Import `csv` and `json` data 35 | * Control column visibility 36 | * Reorder or swap columns 37 | * dayjs integration for sorting columns with datetime strings 38 | * Extentable with custom plugins [See the Simple-DataTables-classic wiki](https://github.com/fiduswriter/Simple-DataTables-classic/wiki/Plugins) 39 | 40 | 41 | [Simple-DataTables-classic Documentation](https://github.com/fiduswriter/Simple-DataTables-classic/wiki) 42 | 43 | 44 | --- 45 | 46 | ### Install 47 | 48 | ## npm 49 | ``` 50 | npm install simple-datatables-classic --save 51 | ``` 52 | 53 | --- 54 | 55 | ### Quick Start 56 | 57 | Then just initialise the plugin by import DataTable and either passing a reference to the table or a CSS3 selector string as the first parameter: 58 | 59 | ```javascript 60 | import {DataTable} from "simple-datatables-classic" 61 | 62 | const myTable = document.querySelector("#myTable"); 63 | const dataTable = new DataTable(myTable); 64 | 65 | // or 66 | 67 | const dataTable = new DataTable("#myTable"); 68 | 69 | ``` 70 | 71 | You can also pass the options object as the second paramater: 72 | 73 | ```javascript 74 | import {DataTable} from "simple-datatables-classic" 75 | 76 | const dataTable = new DataTable("#myTable", { 77 | searchable: false, 78 | fixedHeight: true, 79 | ... 80 | }) 81 | ``` 82 | 83 | If using the CDN: 84 | 85 | ```javascript 86 | const dataTable = new simpleDatatables.DataTable("#myTable", { 87 | searchable: false, 88 | fixedHeight: true, 89 | ... 90 | }) 91 | ``` 92 | -------------------------------------------------------------------------------- /src/style.css: -------------------------------------------------------------------------------- 1 | .dataTable-wrapper.no-header .dataTable-container { 2 | border-top: 1px solid #d9d9d9; 3 | } 4 | 5 | .dataTable-wrapper.no-footer .dataTable-container { 6 | border-bottom: 1px solid #d9d9d9; 7 | } 8 | 9 | .dataTable-top, 10 | .dataTable-bottom { 11 | padding: 8px 10px; 12 | } 13 | 14 | .dataTable-top > nav:first-child, 15 | .dataTable-top > div:first-child, 16 | .dataTable-bottom > nav:first-child, 17 | .dataTable-bottom > div:first-child { 18 | float: left; 19 | } 20 | 21 | .dataTable-top > nav:last-child, 22 | .dataTable-top > div:last-child, 23 | .dataTable-bottom > nav:last-child, 24 | .dataTable-bottom > div:last-child { 25 | float: right; 26 | } 27 | 28 | .dataTable-selector { 29 | padding: 6px; 30 | } 31 | 32 | .dataTable-input { 33 | padding: 6px 12px; 34 | } 35 | 36 | .dataTable-info { 37 | margin: 7px 0; 38 | } 39 | 40 | /* PAGER */ 41 | .dataTable-pagination ul { 42 | margin: 0; 43 | padding-left: 0; 44 | } 45 | 46 | .dataTable-pagination li { 47 | list-style: none; 48 | float: left; 49 | } 50 | 51 | .dataTable-pagination a { 52 | border: 1px solid transparent; 53 | float: left; 54 | margin-left: 2px; 55 | padding: 6px 12px; 56 | position: relative; 57 | text-decoration: none; 58 | color: #333; 59 | } 60 | 61 | .dataTable-pagination a:hover { 62 | background-color: #d9d9d9; 63 | } 64 | 65 | .dataTable-pagination .active a, 66 | .dataTable-pagination .active a:focus, 67 | .dataTable-pagination .active a:hover { 68 | background-color: #d9d9d9; 69 | cursor: default; 70 | } 71 | 72 | .dataTable-pagination .ellipsis a, 73 | .dataTable-pagination .disabled a, 74 | .dataTable-pagination .disabled a:focus, 75 | .dataTable-pagination .disabled a:hover { 76 | cursor: not-allowed; 77 | } 78 | 79 | .dataTable-pagination .disabled a, 80 | .dataTable-pagination .disabled a:focus, 81 | .dataTable-pagination .disabled a:hover { 82 | cursor: not-allowed; 83 | opacity: 0.4; 84 | } 85 | 86 | .dataTable-pagination .pager a { 87 | font-weight: bold; 88 | } 89 | 90 | /* TABLE */ 91 | .dataTable-table { 92 | max-width: 100%; 93 | width: 100%; 94 | border-spacing: 0; 95 | border-collapse: separate; 96 | } 97 | 98 | .dataTable-table > tbody > tr > td, 99 | .dataTable-table > tbody > tr > th, 100 | .dataTable-table > tfoot > tr > td, 101 | .dataTable-table > tfoot > tr > th, 102 | .dataTable-table > thead > tr > td, 103 | .dataTable-table > thead > tr > th { 104 | vertical-align: top; 105 | padding: 8px 10px; 106 | } 107 | 108 | .dataTable-table > thead > tr > th { 109 | vertical-align: bottom; 110 | text-align: left; 111 | border-bottom: 1px solid #d9d9d9; 112 | } 113 | 114 | .dataTable-table > tfoot > tr > th { 115 | vertical-align: bottom; 116 | text-align: left; 117 | border-top: 1px solid #d9d9d9; 118 | } 119 | 120 | .dataTable-table th { 121 | vertical-align: bottom; 122 | text-align: left; 123 | } 124 | 125 | .dataTable-table th a { 126 | text-decoration: none; 127 | color: inherit; 128 | } 129 | 130 | .dataTable-sorter { 131 | display: inline-block; 132 | height: 100%; 133 | position: relative; 134 | width: 100%; 135 | } 136 | 137 | .dataTable-sorter::before, 138 | .dataTable-sorter::after { 139 | content: ""; 140 | height: 0; 141 | width: 0; 142 | position: absolute; 143 | right: 4px; 144 | border-left: 4px solid transparent; 145 | border-right: 4px solid transparent; 146 | opacity: 0.2; 147 | } 148 | 149 | .dataTable-sorter::before { 150 | border-top: 4px solid #000; 151 | bottom: 0px; 152 | } 153 | 154 | .dataTable-sorter::after { 155 | border-bottom: 4px solid #000; 156 | border-top: 4px solid transparent; 157 | top: 0px; 158 | } 159 | 160 | .asc .dataTable-sorter::after, 161 | .desc .dataTable-sorter::before { 162 | opacity: 0.6; 163 | } 164 | 165 | .dataTables-empty { 166 | text-align: center; 167 | } 168 | 169 | .dataTable-top::after, .dataTable-bottom::after { 170 | clear: both; 171 | content: " "; 172 | display: table; 173 | } 174 | -------------------------------------------------------------------------------- /src/helpers.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Check is item is object 3 | * @return {Boolean} 4 | */ 5 | export const isObject = val => Object.prototype.toString.call(val) === "[object Object]" 6 | 7 | /** 8 | * Check for valid JSON string 9 | * @param {String} str 10 | * @return {Boolean|Array|Object} 11 | */ 12 | export const isJson = str => { 13 | let t = !1 14 | try { 15 | t = JSON.parse(str) 16 | } catch (e) { 17 | return !1 18 | } 19 | return !(null === t || (!Array.isArray(t) && !isObject(t))) && t 20 | } 21 | 22 | /** 23 | * Create DOM element node 24 | * @param {String} nodeName nodeName 25 | * @param {Object} attrs properties and attributes 26 | * @return {Object} 27 | */ 28 | export const createElement = (nodeName, attrs) => { 29 | const dom = document.createElement(nodeName) 30 | if (attrs && "object" == typeof attrs) { 31 | for (const attr in attrs) { 32 | if ("html" === attr) { 33 | dom.innerHTML = attrs[attr] 34 | } else { 35 | dom.setAttribute(attr, attrs[attr]) 36 | } 37 | } 38 | } 39 | return dom 40 | } 41 | 42 | export const flush = el => { 43 | if (el instanceof NodeList) { 44 | el.forEach(e => flush(e)) 45 | } else if (el instanceof Object) { 46 | // IE fix - recognises NodeList as Object 47 | if (Object.prototype.hasOwnProperty.call(el, 'length')) { 48 | el.forEach(e => flush(e)); 49 | } else { 50 | el.innerHTML = ""; 51 | } 52 | } else { 53 | el.innerHTML = "" 54 | } 55 | } 56 | 57 | /** 58 | * Create button helper 59 | * @param {String} class 60 | * @param {Number} page 61 | * @param {String} text 62 | * @return {Object} 63 | */ 64 | export const button = (className, page, text) => createElement( 65 | "li", 66 | { 67 | class: className, 68 | html: `${text}` 69 | } 70 | ) 71 | 72 | /** 73 | * Bubble sort algorithm 74 | */ 75 | export const sortItems = (a, b) => { 76 | let c 77 | let d 78 | if (1 === b) { 79 | c = 0 80 | d = a.length 81 | } else { 82 | if (b === -1) { 83 | c = a.length - 1 84 | d = -1 85 | } 86 | } 87 | for (let e = !0; e;) { 88 | e = !1 89 | for (let f = c; f != d; f += b) { 90 | if (a[f + b] && a[f].value > a[f + b].value) { 91 | const g = a[f] 92 | const h = a[f + b] 93 | const i = g 94 | a[f] = h 95 | a[f + b] = i 96 | e = !0 97 | } 98 | } 99 | } 100 | return a 101 | } 102 | 103 | /** 104 | * Pager truncation algorithm 105 | */ 106 | export const truncate = (a, b, c, d, ellipsis) => { 107 | d = d || 2 108 | let j 109 | const e = 2 * d 110 | let f = b - d 111 | let g = b + d 112 | const h = [] 113 | const i = [] 114 | if (b < 4 - d + e) { 115 | g = 3 + e 116 | } else if (b > c - (3 - d + e)) { 117 | f = c - (2 + e) 118 | } 119 | for (let k = 1; k <= c; k++) { 120 | if (1 == k || k == c || (k >= f && k <= g)) { 121 | const l = a[k - 1] 122 | l.classList.remove("active") 123 | h.push(l) 124 | } 125 | } 126 | h.forEach(c => { 127 | const d = c.children[0].getAttribute("data-page") 128 | if (j) { 129 | const e = j.children[0].getAttribute("data-page") 130 | if (d - e == 2) i.push(a[e]) 131 | else if (d - e != 1) { 132 | const f = createElement("li", { 133 | class: "ellipsis", 134 | html: `${ellipsis}` 135 | }) 136 | i.push(f) 137 | } 138 | } 139 | i.push(c) 140 | j = c 141 | }) 142 | 143 | return i 144 | } 145 | -------------------------------------------------------------------------------- /docs/5-column-manipulation/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Column Manipulation 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 |

Column Manipulation

19 |
20 | 21 | 22 | 23 | 24 | 123 | 124 | 125 | -------------------------------------------------------------------------------- /docs/index.js: -------------------------------------------------------------------------------- 1 | import { 2 | DataTable 3 | } from "../src" 4 | 5 | const t = document.createElement('table') 6 | const data = { 7 | "headings": [ 8 | "Name", 9 | "Job", 10 | "Company", 11 | "Ext.", 12 | "Start Date", 13 | "Email", 14 | "Phone No." 15 | ], 16 | "data": [ 17 | [ 18 | "Hedwig F. Nguyen", 19 | "Manager", 20 | "Arcu Vel Foundation", 21 | "9875", 22 | "March 27 2017", 23 | "nunc.ullamcorper@metusvitae.com", 24 | "070 8206 9605" 25 | ], 26 | [ 27 | "Genevieve U. Watts", 28 | "Manager", 29 | "Eget Incorporated", 30 | "9557", 31 | "July 18 2017", 32 | "Nullam.vitae@egestas.edu", 33 | "0800 025698" 34 | ], 35 | [ 36 | "Kyra S. Baldwin", 37 | "Manager", 38 | "Lorem Vitae Limited", 39 | "3854", 40 | "April 14 2016", 41 | "in@elita.org", 42 | "0800 237 8846" 43 | ], 44 | [ 45 | "Stephen V. Hill", 46 | "Manager", 47 | "Eget Mollis Institute", 48 | "8820", 49 | "March 3 2016", 50 | "eu@vel.com", 51 | "0800 682 4591" 52 | ], 53 | [ 54 | "Vielka Q. Chapman", 55 | "Manager", 56 | "Velit Pellentesque Ultricies Institute", 57 | "2307", 58 | "June 25 2017", 59 | "orci.Donec.nibh@mauriserateget.edu", 60 | "0800 181 5795" 61 | ], 62 | [ 63 | "Ocean W. Curtis", 64 | "Manager", 65 | "EU Ltd", 66 | "6868", 67 | "August 24 2017", 68 | "cursus.et@cursus.edu", 69 | "(016977) 9585" 70 | ], 71 | [ 72 | "Kato F. Tucker", 73 | "Manager", 74 | "Vel Lectus Limited", 75 | "4713", 76 | "November 6 2017", 77 | "Duis@Lorem.edu", 78 | "070 0981 8503" 79 | ], 80 | [ 81 | "Robin J. Wise", 82 | "Manager", 83 | "iCurabitur Dictum PC", 84 | "3285", 85 | "February 9 2017", 86 | "blandit@montesnascetur.edu", 87 | "0800 259158" 88 | ], 89 | [ 90 | "Uriel H. Guerrero", 91 | "Assistant", 92 | "Mauris Inc.", 93 | "2294", 94 | "February 11 2018", 95 | "vitae@Innecorci.net", 96 | "0500 948772" 97 | ], 98 | [ 99 | "Yasir W. Benson", 100 | "Assistant", 101 | "At Incorporated", 102 | "3897", 103 | "January 13 2017", 104 | "ornare.elit.elit@atortor.edu", 105 | "0391 916 3600" 106 | ], 107 | [ 108 | "Shafira U. French", 109 | "Assistant", 110 | "Nisi Magna Incorporated", 111 | "5116", 112 | "July 23 2016", 113 | "metus.In.nec@bibendum.ca", 114 | "(018013) 26699" 115 | ], 116 | [ 117 | "Casey E. Hood", 118 | "Assistant", 119 | "Lorem Vitae Odio Consulting", 120 | "7079", 121 | "May 5 2017", 122 | "justo.Praesent@sitamet.ca", 123 | "0800 570796" 124 | ], 125 | [ 126 | "Caleb X. Finch", 127 | "Assistant", 128 | "Elit Associates", 129 | "3629", 130 | "September 19 2016", 131 | "condimentum@eleifend.com", 132 | "056 1551 7431" 133 | ] 134 | ] 135 | } 136 | 137 | document.body.appendChild(t) 138 | 139 | const log = [] 140 | let testName 141 | window.dt = new DataTable(t, { 142 | data, 143 | filters: {"Job": ["Assistant", "Manager"]}, 144 | columns: [ 145 | { 146 | select: 4, 147 | type: "date", 148 | format: "MMMM D, YYYY" 149 | } 150 | ] 151 | }) 152 | -------------------------------------------------------------------------------- /docs/4-render-column-cell/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Render Column Cells 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 31 | 32 | 33 |

Render Column Cells

34 |
35 | 36 | 37 | 38 | 39 | 40 | 125 | 126 | 127 | -------------------------------------------------------------------------------- /docs/3-ajax/ajax-example.txt: -------------------------------------------------------------------------------- 1 | Name,Ext.,City,Start Date 2 | Hammett Gordon,8101,Wah,1998/06/09 3 | Kyra Moses,3796,Quenast,1998/07/07 4 | Kelly Cameron,4836,Fontaine-Valmont,1999/02/07 5 | Theodore Duran,8971,Dhanbad,1999/04/07 6 | Armand Suarez,6583,Funtua,1999/06/11 7 | Jerry Beach,6801,Gattatico,1999/07/07 8 | Garrett Rocha,3938,Gavorrano,2000/06/08 9 | Daniel Baldwin,6095,Moircy,2000/11/01 10 | Francesca Brock,1337,Oban,2000/12/06 11 | Danielle Oconnor,9464,Neuwied,2001/05/10 12 | Vivien Dominguez,5653,Bargagli,2001/09/01 13 | Leigh Ruiz,5112,Lac Ste. Anne,2001/09/02 14 | Mira Rocha,4393,Port Harcourt,2002/04/10 15 | Emerald Warner,6205,Chiavari,2002/08/04 16 | Oliver Huber,1265,Hannche,2002/11/01 17 | Karen Whitley,4357,Sluis,2003/02/05 18 | Stewart Stephenson,5350,Villa Faraldi,2003/05/07 19 | Colin Burch,7457,Anamur,2004/02/01 20 | Unity Pugh,9958,Curicó,2005/02/11 21 | Kylie Bishop,3147,Norman,2005/09/08 22 | Elliott Snyder,3925,Enines,2006/03/08 23 | Zachery Morgan,6730,Collines-de-l'Outaouais,2006/04/09 24 | Mark Brock,3310,Veenendaal,2006/08/09 25 | Blossom Dickerson,5018,Kempten,2006/11/09 26 | Alisa Horn,9853,Ucluelet,2007/01/11 27 | Roth Cherry,4006,Flin Flon,2008/02/09 28 | Harding Thompson,2824,Abeokuta,2008/06/08 29 | Lani Lawrence,8501,Turnhout,2008/07/05 30 | Lareina Jones,8642,East Linton,2009/07/08 31 | Evangeline Beasley,3820,Caplan,2009/12/03 32 | Tad Munoz,2902,Saint-Nazaire,2010/09/05 33 | Hilda Whitley,3514,New Sarepta,2011/05/07 34 | Walker Nixon,6901,Metz,2011/12/11 35 | Grace Bishop,8340,Rodez,2012/02/10 36 | Zelenia Roman,7516,Redwater,2012/03/03 37 | Kato Carr,4842,Faridabad,2012/11/05 38 | Daryl Ayers,8276,Barchi,2012/12/11 39 | Lois Talley,9393,Dorchester,2014/05/01 40 | Iola Shaw,6447,Albany,2014/05/03 41 | Shad Hudson,5944,Salamanca,2014/10/12 42 | Sylvia Peters,6829,Arrah,2015/03/02 43 | Macaulay Pruitt,4457,Fraser-Fort George,2015/03/08 44 | Sydney Meyer,4576,Neubrandenburg,2015/06/02 45 | Scarlett Hurst,1019,Brampton,2015/07/01 46 | Carissa Lara,3241,Sherborne,2015/07/12 47 | Dalton Jennings,5416,Dudzele,2015/09/02 48 | Vielka Olsen,3745,Vrasene,2016/08/01 49 | Gretchen Rogers,5393,Moxhe,1998/26/10 50 | Phelan Kane,9519,Germersheim,1999/16/04 51 | Ursula Reynolds,7544,Southampton,1999/16/12 52 | Lois Vargas,6825,Cumberland,1999/25/04 53 | Brennan Brooks,9011,Olmué,2000/18/04 54 | Dale Rush,5050,Chicoutimi,2000/27/03 55 | Haviva Hernandez,8136,Suwałki,2000/30/01 56 | Paki Santos,4424,Cache Creek,2001/18/11 57 | Hermione Dickson,2785,Woodstock,2001/22/03 58 | Kiona Lowery,5952,Inuvik,2002/17/12 59 | Nathan Espinoza,5956,Strathcona County,2002/25/01 60 | Yoko Freeman,4077,Lidköping,2002/27/12 61 | Riley Nunez,5533,Sart-Eustache,2003/26/02 62 | Allegra Shepherd,2576,Meeuwen-Gruitrode,2004/19/04 63 | Hilda Nelson,6307,Posina,2004/23/05 64 | Fallon Reyes,3178,Monceau-sur-Sambre,2005/15/02 65 | Kane Anthony,8075,LaSalle,2006/21/05 66 | Calista Maynard,3315,Pozzuolo del Friuli,2006/23/03 67 | Meghan Cunningham,8604,Söke,2007/16/02 68 | Imelda Cole,4564,Haasdonk,2007/16/11 69 | Malachi Mejia,7133,Vorst,2007/25/04 70 | James Scott,3008,Meux,2007/30/05 71 | Elaine Bishop,9160,Petrópolis,2008/23/12 72 | Nina Rush,7567,Bo‘lhe,2008/27/01 73 | Desiree Ferguson,9054,Gojra,2009/15/02 74 | Willow Gilliam,3497,Amqui,2009/29/11 75 | Joshua Weiss,2289,Saint-L�onard,2010/15/01 76 | Ross Hodges,1862,Trazegnies,2010/19/09 77 | Athena Aguirre,5741,Romeral,2010/24/03 78 | Chaim Waller,4240,North Shore,2010/25/07 79 | Drew Phillips,2931,Goes,2011/18/10 80 | Maia Pate,6682,Louvain-la-Neuve,2011/23/04 81 | Quentin Salas,1339,Los Andes,2011/26/01 82 | Wyatt Riley,5694,Cavaion Veronese,2012/19/02 83 | Porter Nicholson,4539,Bismil,2012/22/10 84 | Palmer Parker,2000,Stade,2012/24/07 85 | Hop Bass,1024,Westerlo,2012/25/09 86 | Cathleen Kramer,3380,Crowsnest Pass,2012/27/07 87 | Shellie Murphy,3845,Marlborough,2013/13/11 88 | Kalia Diaz,9184,Ichalkaranji,2013/26/06 89 | Ursa Davenport,7629,New Plymouth,2013/27/06 90 | Caleb Livingston,3094,Fatehpur,2014/13/02 91 | Dominic Carver,3476,Pointe-aux-Trembles,2014/14/03 92 | Adrienne Winters,4425,Laguna Blanca,2014/15/09 93 | Derek Kerr,1724,Gualdo Cattaneo,2014/21/01 94 | Wyatt Mccarthy,3547,Patan,2014/23/06 95 | Castor Pugh,9488,Neath,2014/23/12 96 | Deirdre Bridges,1579,Eberswalde-Finow,2014/26/08 97 | Pearl Carlson,6231,Cobourg,2014/31/08 98 | Kasper Craig,5515,Firenze,2015/26/04 99 | Russell Haynes,8916,Frascati,2015/28/04 100 | Berk Johnston,4532,Vergnies,2016/23/02 101 | Cairo Rice,6273,Ostra Vetere,2016/27/02 -------------------------------------------------------------------------------- /docs/2-filters/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Filters 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 |

Filters

19 | 20 | 21 | 22 | 23 | 170 | 171 | 172 | -------------------------------------------------------------------------------- /docs/umd.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Demo 7 | 8 | 9 | 10 | 11 |

Simple-DataTables-classic (UMD Mode)

12 | 13 | 156 | 157 | 158 | -------------------------------------------------------------------------------- /docs/7-init-destroy-import-export/utils.js: -------------------------------------------------------------------------------- 1 | var util = { 2 | extend: function(src, props) { 3 | props = props || {}; 4 | var p; 5 | for (p in src) { 6 | if (!props.hasOwnProperty(p)) { 7 | props[p] = src[p]; 8 | } 9 | } 10 | return props; 11 | }, 12 | each: function(a, b, c) { 13 | if ("[object Object]" === Object.prototype.toString.call(a)) { 14 | for (var d in a) { 15 | if (Object.prototype.hasOwnProperty.call(a, d)) { 16 | b.call(c, d, a[d], a); 17 | } 18 | } 19 | } else { 20 | for (var e = 0, f = a.length; e < f; e++) { 21 | b.call(c, e, a[e], a); 22 | } 23 | } 24 | }, 25 | createElement: function(a, b) { 26 | var c = document, 27 | d = c.createElement(a); 28 | if (b && "object" == typeof b) { 29 | var e; 30 | for (e in b) 31 | if ("html" === e) d.innerHTML = b[e]; 32 | else if ("text" === e) { 33 | var f = c.createTextNode(b[e]); 34 | d.appendChild(f); 35 | } else d.setAttribute(e, b[e]); 36 | } 37 | return d; 38 | }, 39 | css: function(el, prop, val) { 40 | var style = el && el.style, 41 | isObj = "[object Object]" === Object.prototype.toString.call(prop); 42 | 43 | if (style) { 44 | if (val === void 0 && !isObj) { 45 | val = window.getComputedStyle(el, ''); 46 | return prop === void 0 ? val : val[prop]; 47 | } else { 48 | if (isObj) { 49 | util.each(prop, function(p, v) { 50 | if (!(p in style)) { p = '-webkit-' + p; } 51 | style[p] = v + (typeof v === 'string' ? '' : p === "opacity" ? "" : "px"); 52 | }); 53 | } else { 54 | if (!(prop in style)) { prop = '-webkit-' + prop; } 55 | style[prop] = val + (typeof val === 'string' ? '' : prop === "opacity" ? "" : "px"); 56 | } 57 | } 58 | } 59 | }, 60 | hasClass: function(a, b) { 61 | return a.classList ? a.classList.contains(b) : !!a.className && !!a.className.match(new RegExp("(\\s|^)" + b + "(\\s|$)")); 62 | }, 63 | addClass: function(a, b) { 64 | if (!util.hasClass(a, b)) { 65 | if (a.classList) { 66 | a.classList.add(b); 67 | } else { 68 | a.className = a.className.trim() + " " + b; 69 | } 70 | } 71 | }, 72 | removeClass: function(a, b) { 73 | if (util.hasClass(a, b)) { 74 | if (a.classList) { 75 | a.classList.remove(b); 76 | } else { 77 | a.className = a.className.replace(new RegExp("(^|\\s)" + b.split(" ").join("|") + "(\\s|$)", "gi"), " "); 78 | } 79 | } 80 | }, 81 | replace: function(n, o) { 82 | return o.parentNode.replaceChild(n, o); 83 | }, 84 | closest: function(el, fn) { 85 | return el && el !== document.body && (fn(el) ? el : util.closest(el.parentNode, fn)); 86 | }, 87 | delegate: function(parent, child, evt, callback, scope) { 88 | // Allow selector strings 89 | if (typeof parent === "string") { 90 | parent = document.querySelector(parent); 91 | } 92 | 93 | var func = function(e) { 94 | scope = scope || this; 95 | 96 | var t = e.target; 97 | 98 | // Climb the DOM tree from the current target until we find the matching selector 99 | for (; t && t !== document; t = t.parentNode) { 100 | if (t.matches(child)) { 101 | e.delegateTarget = t; 102 | callback.call(scope, e); 103 | } 104 | } 105 | }; 106 | 107 | // Attach the event listener to the parent 108 | parent.addEventListener(evt, func, false); 109 | }, 110 | on: function(e, type, callback, scope) { 111 | e.addEventListener(type, function(e) { 112 | scope = scope || this; 113 | callback.call(scope, e); 114 | }, false); 115 | }, 116 | off: function(e, type, callback) { 117 | e.removeEventListener(type, callback); 118 | }, 119 | isNumber: function(n) { 120 | return !isNaN(parseFloat(n)) && isFinite(n); 121 | }, 122 | isObject: function(a) { 123 | return "[object Object]" === Object.prototype.toString.call(a); 124 | }, 125 | isArray: function(a) { 126 | return "[object Array]" === Object.prototype.toString.call(a); 127 | }, 128 | isInt: function(val) { 129 | return !isNaN(val) && (function(x) { 130 | return (x || 0) === x; 131 | })(parseFloat(val)); 132 | }, 133 | debounce: function(a, b, c) { 134 | var d; 135 | return function() { 136 | var e = this, 137 | f = arguments, 138 | g = function() { 139 | d = null; 140 | if (!c) a.apply(e, f); 141 | }, 142 | h = c && !d; 143 | clearTimeout(d); 144 | d = setTimeout(g, b); 145 | if (h) { 146 | a.apply(e, f); 147 | } 148 | }; 149 | }, 150 | getBoundingRect: function(el, margins) { 151 | var win = window; 152 | var doc = document; 153 | var body = doc.body; 154 | var rect = el.getBoundingClientRect(); 155 | var offsetX = win.pageXOffset !== undefined ? win.pageXOffset : (doc.documentElement || body.parentNode || body).scrollLeft; 156 | var offsetY = win.pageYOffset !== undefined ? win.pageYOffset : (doc.documentElement || body.parentNode || body).scrollTop; 157 | 158 | var mt = 0, ml = 0; 159 | 160 | if (margins) { 161 | mt = parseInt(util.css(el, 'margin-top'), 10); 162 | ml = parseInt(util.css(el, 'margin-left'), 10); 163 | } 164 | 165 | return { 166 | bottom: rect.bottom + offsetY, 167 | height: rect.height, 168 | left: rect.left + offsetX - ml, 169 | right: rect.right + offsetX, 170 | top: rect.top + offsetY - mt, 171 | width: rect.width 172 | }; 173 | }, 174 | includes: function(a, b) { 175 | return a.indexOf(b) > -1; 176 | }, 177 | after: function(a, b) { 178 | a.nextSibling ? a.parentNode.insertBefore(b, a.nextSibling) : a.parentNode.appendChild(b) 179 | }, 180 | parents: function(el, selector) { 181 | var elements = []; 182 | var ishaveselector = selector !== undefined; 183 | 184 | while ((el = el.parentElement) !== null) { 185 | if (el.nodeType !== Node.ELEMENT_NODE) { 186 | continue; 187 | } 188 | 189 | if (!ishaveselector || el.matches(selector)) { 190 | elements.push(el); 191 | } 192 | } 193 | 194 | return elements; 195 | }, 196 | match: function(a, b) { 197 | var c = b.charAt(0); 198 | return !("." !== c || !_hasClass(a, b.substr(1))) || ("#" === c && a.id === b.substr(1) || (!("[" !== c || !a.hasAttribute(b.substr(1, b.length - 1))) || a.tagName.toLowerCase() === b)) 199 | }, 200 | sortNumber: function(a,b) { 201 | return a - b; 202 | } 203 | }; -------------------------------------------------------------------------------- /docs/7-init-destroy-import-export/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Init/ Destroy - Import/ Export 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 30 | 31 | 32 |

Init/ Destroy - Import/ Export

33 |
34 |
35 |
36 |
37 |
38 |
39 | 40 | 41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 |
SelectCustomerVersionPlanned upgradeDB-InfoWebsite info
Test one
CCode: 33123
7.1.526.11.2018Server: s1
DB: db1
Websitename: Red
Node: 10
Test two
CCode: 12353
7.1.5.226.11.2019Server: s1
DB: db2
Websitename: Green
Node: 6
Test three
CCode: 33522
7.1.601.01.2020Server: s2
DB: db1
Websitename: Orange
Node: 5
Test four
CCode: 25512
7.1.726.03.2021Server: s1
DB: db3
Websitename: Blue
Node: 10
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 | Column Visibility 106 |
107 |
108 |
109 |
110 |
111 |
112 |
113 |
114 |
115 |
116 |
117 | Import / Export Data 118 |
119 |
120 |
121 | 122 |
123 |
124 | 125 | 126 | 127 | 128 | 129 |
130 |
131 | NOTE: The csv column delimiters and line delimiters are set to "," and "\n" respectively. 132 |
133 |
134 |
135 |
136 |
137 |
138 |
139 | 140 | 141 | 142 | 143 | 144 | 145 | 268 | 269 | 270 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | GNU LESSER GENERAL PUBLIC LICENSE 2 | Version 3, 29 June 2007 3 | 4 | Copyright (C) 2007 Free Software Foundation, Inc. 5 | Everyone is permitted to copy and distribute verbatim copies 6 | of this license document, but changing it is not allowed. 7 | 8 | 9 | This version of the GNU Lesser General Public License incorporates 10 | the terms and conditions of version 3 of the GNU General Public 11 | License, supplemented by the additional permissions listed below. 12 | 13 | 0. Additional Definitions. 14 | 15 | As used herein, "this License" refers to version 3 of the GNU Lesser 16 | General Public License, and the "GNU GPL" refers to version 3 of the GNU 17 | General Public License. 18 | 19 | "The Library" refers to a covered work governed by this License, 20 | other than an Application or a Combined Work as defined below. 21 | 22 | An "Application" is any work that makes use of an interface provided 23 | by the Library, but which is not otherwise based on the Library. 24 | Defining a subclass of a class defined by the Library is deemed a mode 25 | of using an interface provided by the Library. 26 | 27 | A "Combined Work" is a work produced by combining or linking an 28 | Application with the Library. The particular version of the Library 29 | with which the Combined Work was made is also called the "Linked 30 | Version". 31 | 32 | The "Minimal Corresponding Source" for a Combined Work means the 33 | Corresponding Source for the Combined Work, excluding any source code 34 | for portions of the Combined Work that, considered in isolation, are 35 | based on the Application, and not on the Linked Version. 36 | 37 | The "Corresponding Application Code" for a Combined Work means the 38 | object code and/or source code for the Application, including any data 39 | and utility programs needed for reproducing the Combined Work from the 40 | Application, but excluding the System Libraries of the Combined Work. 41 | 42 | 1. Exception to Section 3 of the GNU GPL. 43 | 44 | You may convey a covered work under sections 3 and 4 of this License 45 | without being bound by section 3 of the GNU GPL. 46 | 47 | 2. Conveying Modified Versions. 48 | 49 | If you modify a copy of the Library, and, in your modifications, a 50 | facility refers to a function or data to be supplied by an Application 51 | that uses the facility (other than as an argument passed when the 52 | facility is invoked), then you may convey a copy of the modified 53 | version: 54 | 55 | a) under this License, provided that you make a good faith effort to 56 | ensure that, in the event an Application does not supply the 57 | function or data, the facility still operates, and performs 58 | whatever part of its purpose remains meaningful, or 59 | 60 | b) under the GNU GPL, with none of the additional permissions of 61 | this License applicable to that copy. 62 | 63 | 3. Object Code Incorporating Material from Library Header Files. 64 | 65 | The object code form of an Application may incorporate material from 66 | a header file that is part of the Library. You may convey such object 67 | code under terms of your choice, provided that, if the incorporated 68 | material is not limited to numerical parameters, data structure 69 | layouts and accessors, or small macros, inline functions and templates 70 | (ten or fewer lines in length), you do both of the following: 71 | 72 | a) Give prominent notice with each copy of the object code that the 73 | Library is used in it and that the Library and its use are 74 | covered by this License. 75 | 76 | b) Accompany the object code with a copy of the GNU GPL and this license 77 | document. 78 | 79 | 4. Combined Works. 80 | 81 | You may convey a Combined Work under terms of your choice that, 82 | taken together, effectively do not restrict modification of the 83 | portions of the Library contained in the Combined Work and reverse 84 | engineering for debugging such modifications, if you also do each of 85 | the following: 86 | 87 | a) Give prominent notice with each copy of the Combined Work that 88 | the Library is used in it and that the Library and its use are 89 | covered by this License. 90 | 91 | b) Accompany the Combined Work with a copy of the GNU GPL and this license 92 | document. 93 | 94 | c) For a Combined Work that displays copyright notices during 95 | execution, include the copyright notice for the Library among 96 | these notices, as well as a reference directing the user to the 97 | copies of the GNU GPL and this license document. 98 | 99 | d) Do one of the following: 100 | 101 | 0) Convey the Minimal Corresponding Source under the terms of this 102 | License, and the Corresponding Application Code in a form 103 | suitable for, and under terms that permit, the user to 104 | recombine or relink the Application with a modified version of 105 | the Linked Version to produce a modified Combined Work, in the 106 | manner specified by section 6 of the GNU GPL for conveying 107 | Corresponding Source. 108 | 109 | 1) Use a suitable shared library mechanism for linking with the 110 | Library. A suitable mechanism is one that (a) uses at run time 111 | a copy of the Library already present on the user's computer 112 | system, and (b) will operate properly with a modified version 113 | of the Library that is interface-compatible with the Linked 114 | Version. 115 | 116 | e) Provide Installation Information, but only if you would otherwise 117 | be required to provide such information under section 6 of the 118 | GNU GPL, and only to the extent that such information is 119 | necessary to install and execute a modified version of the 120 | Combined Work produced by recombining or relinking the 121 | Application with a modified version of the Linked Version. (If 122 | you use option 4d0, the Installation Information must accompany 123 | the Minimal Corresponding Source and Corresponding Application 124 | Code. If you use option 4d1, you must provide the Installation 125 | Information in the manner specified by section 6 of the GNU GPL 126 | for conveying Corresponding Source.) 127 | 128 | 5. Combined Libraries. 129 | 130 | You may place library facilities that are a work based on the 131 | Library side by side in a single library together with other library 132 | facilities that are not Applications and are not covered by this 133 | License, and convey such a combined library under terms of your 134 | choice, if you do both of the following: 135 | 136 | a) Accompany the combined library with a copy of the same work based 137 | on the Library, uncombined with any other library facilities, 138 | conveyed under the terms of this License. 139 | 140 | b) Give prominent notice with the combined library that part of it 141 | is a work based on the Library, and explaining where to find the 142 | accompanying uncombined form of the same work. 143 | 144 | 6. Revised Versions of the GNU Lesser General Public License. 145 | 146 | The Free Software Foundation may publish revised and/or new versions 147 | of the GNU Lesser General Public License from time to time. Such new 148 | versions will be similar in spirit to the present version, but may 149 | differ in detail to address new problems or concerns. 150 | 151 | Each version is given a distinguishing version number. If the 152 | Library as you received it specifies that a certain numbered version 153 | of the GNU Lesser General Public License "or any later version" 154 | applies to it, you have the option of following the terms and 155 | conditions either of that published version or of any later version 156 | published by the Free Software Foundation. If the Library as you 157 | received it does not specify a version number of the GNU Lesser 158 | General Public License, you may choose any version of the GNU Lesser 159 | General Public License ever published by the Free Software Foundation. 160 | 161 | If the Library as you received it specifies that a proxy can decide 162 | whether future versions of the GNU Lesser General Public License shall 163 | apply, that proxy's public statement of acceptance of any version is 164 | permanent authorization for you to choose that version for the 165 | Library. 166 | 167 | ----- 168 | 169 | This software incorporates work covered by the following copyright and 170 | permission notice: 171 | 172 | The MIT License (MIT) 173 | 174 | Copyright (c) 2016-present, Karl Saunders (mobius1[at]gmx[dot]com). 175 | 176 | Permission is hereby granted, free of charge, to any person obtaining a copy 177 | of this software and associated documentation files (the "Software"), to deal 178 | in the Software without restriction, including without limitation the rights 179 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 180 | copies of the Software, and to permit persons to whom the Software is 181 | furnished to do so, subject to the following conditions: 182 | 183 | The above copyright notice and this permission notice shall be included in all 184 | copies or substantial portions of the Software. 185 | 186 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 187 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 188 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 189 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 190 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 191 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 192 | SOFTWARE. 193 | -------------------------------------------------------------------------------- /.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | "env": { 3 | "browser": true, 4 | "es6": true 5 | }, 6 | "extends": "eslint:recommended", 7 | "parser": "babel-eslint", 8 | "parserOptions": { 9 | "ecmaVersion": 2018, 10 | "sourceType": "module", 11 | "allowImportExportEverywhere": true 12 | }, 13 | "rules": { 14 | "accessor-pairs": "error", 15 | "array-bracket-newline": "error", 16 | "array-bracket-spacing": [ 17 | "error", 18 | "never" 19 | ], 20 | "array-callback-return": "error", 21 | "array-element-newline": "off", 22 | "arrow-body-style": "error", 23 | "arrow-parens": [ 24 | "error", 25 | "as-needed" 26 | ], 27 | "arrow-spacing": [ 28 | "error", 29 | { 30 | "after": true, 31 | "before": true 32 | } 33 | ], 34 | "block-scoped-var": "error", 35 | "block-spacing": "error", 36 | "brace-style": [ 37 | "error", 38 | "1tbs" 39 | ], 40 | "callback-return": "error", 41 | "camelcase": "error", 42 | "capitalized-comments": "off", 43 | "class-methods-use-this": "off", 44 | "comma-dangle": "error", 45 | "comma-spacing": [ 46 | "error", 47 | { 48 | "after": true, 49 | "before": false 50 | } 51 | ], 52 | "comma-style": [ 53 | "error", 54 | "last" 55 | ], 56 | "complexity": "off", 57 | "computed-property-spacing": [ 58 | "error", 59 | "never" 60 | ], 61 | "consistent-return": "off", 62 | "consistent-this": "error", 63 | "curly": "off", 64 | "default-case": "off", 65 | "dot-location": [ 66 | "error", 67 | "property" 68 | ], 69 | "dot-notation": [ 70 | "error", 71 | { 72 | "allowKeywords": true 73 | } 74 | ], 75 | "eol-last": "error", 76 | "eqeqeq": "off", 77 | "func-call-spacing": "error", 78 | "func-name-matching": "error", 79 | "func-names": [ 80 | "error", 81 | "never" 82 | ], 83 | "func-style": [ 84 | "error", 85 | "expression" 86 | ], 87 | "function-paren-newline": "off", 88 | "generator-star-spacing": "error", 89 | "global-require": "error", 90 | "guard-for-in": "error", 91 | "handle-callback-err": "error", 92 | "id-blacklist": "error", 93 | "id-length": "off", 94 | "id-match": "error", 95 | "implicit-arrow-linebreak": [ 96 | "error", 97 | "beside" 98 | ], 99 | "indent": "error", 100 | "indent-legacy": "error", 101 | "init-declarations": "off", 102 | "jsx-quotes": "error", 103 | "key-spacing": "error", 104 | "keyword-spacing": [ 105 | "error", 106 | { 107 | "after": true, 108 | "before": true 109 | } 110 | ], 111 | "line-comment-position": "off", 112 | "linebreak-style": [ 113 | "error", 114 | "unix" 115 | ], 116 | "lines-around-comment": "off", 117 | "lines-around-directive": "error", 118 | "lines-between-class-members": [ 119 | "error", 120 | "always" 121 | ], 122 | "max-classes-per-file": "error", 123 | "max-depth": "off", 124 | "max-len": "off", 125 | "max-lines": "off", 126 | "max-lines-per-function": "off", 127 | "max-nested-callbacks": "error", 128 | "max-params": "off", 129 | "max-statements": "off", 130 | "max-statements-per-line": "error", 131 | "multiline-comment-style": "off", 132 | "multiline-ternary": [ 133 | "error", 134 | "always-multiline" 135 | ], 136 | "new-cap": "error", 137 | "new-parens": "error", 138 | "newline-after-var": "off", 139 | "newline-before-return": "off", 140 | "newline-per-chained-call": "error", 141 | "no-alert": "error", 142 | "no-array-constructor": "error", 143 | "no-async-promise-executor": "error", 144 | "no-await-in-loop": "error", 145 | "no-bitwise": "error", 146 | "no-buffer-constructor": "error", 147 | "no-caller": "error", 148 | "no-catch-shadow": "error", 149 | "no-confusing-arrow": "off", 150 | "no-continue": "error", 151 | "no-div-regex": "error", 152 | "no-duplicate-imports": "error", 153 | "no-else-return": "error", 154 | "no-empty-function": "error", 155 | "no-eq-null": "error", 156 | "no-eval": "error", 157 | "no-extend-native": "error", 158 | "no-extra-bind": "error", 159 | "no-extra-label": "error", 160 | "no-extra-parens": "off", 161 | "no-floating-decimal": "error", 162 | "no-implicit-globals": "error", 163 | "no-implied-eval": "error", 164 | "no-inline-comments": "off", 165 | "no-invalid-this": "off", 166 | "no-iterator": "error", 167 | "no-label-var": "error", 168 | "no-labels": "error", 169 | "no-lone-blocks": "error", 170 | "no-lonely-if": "off", 171 | "no-loop-func": "error", 172 | "no-magic-numbers": "off", 173 | "no-misleading-character-class": "error", 174 | "no-mixed-operators": [ 175 | "error", 176 | { 177 | "allowSamePrecedence": true 178 | } 179 | ], 180 | "no-mixed-requires": "error", 181 | "no-multi-assign": "off", 182 | "no-multi-spaces": "error", 183 | "no-multi-str": "error", 184 | "no-multiple-empty-lines": "error", 185 | "no-native-reassign": "error", 186 | "no-negated-condition": "off", 187 | "no-negated-in-lhs": "error", 188 | "no-nested-ternary": "error", 189 | "no-new": "error", 190 | "no-new-func": "error", 191 | "no-new-object": "error", 192 | "no-new-require": "error", 193 | "no-new-wrappers": "error", 194 | "no-octal-escape": "error", 195 | "no-param-reassign": "off", 196 | "no-path-concat": "error", 197 | "no-plusplus": "off", 198 | "no-process-env": "error", 199 | "no-process-exit": "error", 200 | "no-proto": "error", 201 | "no-prototype-builtins": "off", 202 | "no-restricted-globals": "error", 203 | "no-restricted-imports": "error", 204 | "no-restricted-modules": "error", 205 | "no-restricted-properties": "error", 206 | "no-restricted-syntax": "error", 207 | "no-return-assign": "error", 208 | "no-return-await": "error", 209 | "no-script-url": "error", 210 | "no-self-compare": "error", 211 | "no-sequences": "error", 212 | "no-shadow": "off", 213 | "no-shadow-restricted-names": "error", 214 | "no-spaced-func": "error", 215 | "no-sync": "error", 216 | "no-tabs": "error", 217 | "no-template-curly-in-string": "error", 218 | "no-ternary": "off", 219 | "no-throw-literal": "error", 220 | "no-trailing-spaces": "error", 221 | "no-undef-init": "error", 222 | "no-undefined": "off", 223 | "no-underscore-dangle": "error", 224 | "no-unmodified-loop-condition": "error", 225 | "no-unneeded-ternary": "error", 226 | "no-unused-expressions": "error", 227 | "no-use-before-define": "error", 228 | "no-useless-call": "error", 229 | "no-useless-computed-key": "error", 230 | "no-useless-concat": "error", 231 | "no-useless-constructor": "error", 232 | "no-useless-rename": "error", 233 | "no-useless-return": "error", 234 | "no-var": "error", 235 | "no-void": "error", 236 | "no-warning-comments": "error", 237 | "no-whitespace-before-property": "error", 238 | "no-with": "error", 239 | "nonblock-statement-body-position": [ 240 | "error", 241 | "any" 242 | ], 243 | "object-curly-newline": "error", 244 | "object-curly-spacing": [ 245 | "error", 246 | "never" 247 | ], 248 | "object-property-newline": "error", 249 | "object-shorthand": "error", 250 | "one-var": "off", 251 | "one-var-declaration-per-line": "error", 252 | "operator-assignment": "off", 253 | "operator-linebreak": [ 254 | "error", 255 | "after" 256 | ], 257 | "padded-blocks": "off", 258 | "padding-line-between-statements": "error", 259 | "prefer-arrow-callback": "error", 260 | "prefer-const": "error", 261 | "prefer-destructuring": "off", 262 | "prefer-numeric-literals": "error", 263 | "prefer-object-spread": "error", 264 | "prefer-promise-reject-errors": "error", 265 | "prefer-reflect": "off", 266 | "prefer-rest-params": "off", 267 | "prefer-spread": "error", 268 | "prefer-template": "error", 269 | "quote-props": "off", 270 | "quotes": "off", 271 | "radix": [ 272 | "error", 273 | "always" 274 | ], 275 | "require-atomic-updates": "error", 276 | "require-await": "error", 277 | "require-jsdoc": "error", 278 | "require-unicode-regexp": "off", 279 | "rest-spread-spacing": "error", 280 | "semi": "off", 281 | "semi-spacing": [ 282 | "error", 283 | { 284 | "after": true, 285 | "before": false 286 | } 287 | ], 288 | "semi-style": [ 289 | "error", 290 | "last" 291 | ], 292 | "sort-imports": "off", 293 | "sort-keys": "off", 294 | "sort-vars": "error", 295 | "space-before-blocks": "error", 296 | "space-before-function-paren": "off", 297 | "space-in-parens": "off", 298 | "space-infix-ops": "off", 299 | "space-unary-ops": "error", 300 | "spaced-comment": "off", 301 | "strict": "error", 302 | "switch-colon-spacing": "error", 303 | "symbol-description": "error", 304 | "template-curly-spacing": [ 305 | "error", 306 | "never" 307 | ], 308 | "template-tag-spacing": "error", 309 | "unicode-bom": [ 310 | "error", 311 | "never" 312 | ], 313 | "valid-jsdoc": "off", 314 | "vars-on-top": "error", 315 | "wrap-iife": "error", 316 | "wrap-regex": "off", 317 | "yield-star-spacing": "error", 318 | "yoda": "off" 319 | } 320 | }; 321 | -------------------------------------------------------------------------------- /docs/1-simple/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Simple 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 |

Simple

19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 |
NameExt.CityStart DateCompletion
Unity Pugh9958Curicó2005/02/1137%
Theodore Duran8971Dhanbad1999/04/0797%
Kylie Bishop3147Norman2005/09/0863%
Willow Gilliam3497Amqui2009/29/1130%
Blossom Dickerson5018Kempten2006/11/0917%
Elliott Snyder3925Enines2006/03/0857%
Castor Pugh9488Neath2014/23/1293%
Pearl Carlson6231Cobourg2014/31/08100%
Deirdre Bridges1579Eberswalde-Finow2014/26/0844%
Daniel Baldwin6095Moircy2000/11/0133%
Phelan Kane9519Germersheim1999/16/0477%
Quentin Salas1339Los Andes2011/26/0149%
Armand Suarez6583Funtua1999/06/119%
Gretchen Rogers5393Moxhe1998/26/1024%
Harding Thompson2824Abeokuta2008/06/0810%
Mira Rocha4393Port Harcourt2002/04/1014%
Drew Phillips2931Goes2011/18/1058%
Emerald Warner6205Chiavari2002/08/0458%
Colin Burch7457Anamur2004/02/0134%
Russell Haynes8916Frascati2015/28/0418%
Brennan Brooks9011Olmué2000/18/042%
Kane Anthony8075LaSalle2006/21/0593%
Scarlett Hurst1019Brampton2015/07/0194%
James Scott3008Meux2007/30/0555%
Desiree Ferguson9054Gojra2009/15/0281%
Elaine Bishop9160Petrópolis2008/23/1248%
Hilda Nelson6307Posina2004/23/0576%
Evangeline Beasley3820Caplan2009/12/0362%
Wyatt Riley5694Cavaion Veronese2012/19/0267%
Wyatt Mccarthy3547Patan2014/23/069%
Cairo Rice6273Ostra Vetere2016/27/0213%
Sylvia Peters6829Arrah2015/03/0213%
Kasper Craig5515Firenze2015/26/0456%
Leigh Ruiz5112Lac Ste. Anne2001/09/0228%
Athena Aguirre5741Romeral2010/24/0315%
Riley Nunez5533Sart-Eustache2003/26/0230%
Lois Talley9393Dorchester2014/05/0151%
Hop Bass1024Westerlo2012/25/0985%
Kalia Diaz9184Ichalkaranji2013/26/0692%
Maia Pate6682Louvain-la-Neuve2011/23/0450%
Macaulay Pruitt4457Fraser-Fort George2015/03/0892%
Danielle Oconnor9464Neuwied2001/05/1017%
Kato Carr4842Faridabad2012/11/0596%
Malachi Mejia7133Vorst2007/25/0426%
Dominic Carver3476Pointe-aux-Trembles2014/14/0371%
Paki Santos4424Cache Creek2001/18/1182%
Ross Hodges1862Trazegnies2010/19/0987%
Hilda Whitley3514New Sarepta2011/05/0794%
Roth Cherry4006Flin Flon2008/02/098%
Lareina Jones8642East Linton2009/07/0821%
Joshua Weiss2289Saint-L�onard2010/15/0152%
Kiona Lowery5952Inuvik2002/17/1272%
Nina Rush7567Bo‘lhe2008/27/016%
Palmer Parker2000Stade2012/24/0758%
Vielka Olsen3745Vrasene2016/08/0170%
Meghan Cunningham8604Söke2007/16/0259%
Iola Shaw6447Albany2014/05/0388%
Imelda Cole4564Haasdonk2007/16/1179%
Jerry Beach6801Gattatico1999/07/0736%
Garrett Rocha3938Gavorrano2000/06/0871%
Derek Kerr1724Gualdo Cattaneo2014/21/0189%
Shad Hudson5944Salamanca2014/10/1298%
Daryl Ayers8276Barchi2012/12/1188%
Caleb Livingston3094Fatehpur2014/13/028%
Sydney Meyer4576Neubrandenburg2015/06/0222%
Lani Lawrence8501Turnhout2008/07/0516%
Allegra Shepherd2576Meeuwen-Gruitrode2004/19/0441%
Fallon Reyes3178Monceau-sur-Sambre2005/15/0216%
Karen Whitley4357Sluis2003/02/0523%
Stewart Stephenson5350Villa Faraldi2003/05/0765%
Ursula Reynolds7544Southampton1999/16/1261%
Adrienne Winters4425Laguna Blanca2014/15/0924%
Francesca Brock1337Oban2000/12/0690%
Ursa Davenport7629New Plymouth2013/27/0637%
Mark Brock3310Veenendaal2006/08/0941%
Dale Rush5050Chicoutimi2000/27/032%
Shellie Murphy3845Marlborough2013/13/1172%
Porter Nicholson4539Bismil2012/22/1023%
Oliver Huber1265Hannche2002/11/0194%
Calista Maynard3315Pozzuolo del Friuli2006/23/035%
Lois Vargas6825Cumberland1999/25/0450%
Hermione Dickson2785Woodstock2001/22/032%
Dalton Jennings5416Dudzele2015/09/0274%
Cathleen Kramer3380Crowsnest Pass2012/27/0753%
Zachery Morgan6730Collines-de-l'Outaouais2006/04/0951%
Yoko Freeman4077Lidköping2002/27/1248%
Chaim Waller4240North Shore2010/25/0725%
Berk Johnston4532Vergnies2016/23/0293%
Tad Munoz2902Saint-Nazaire2010/09/0565%
Vivien Dominguez5653Bargagli2001/09/0186%
Carissa Lara3241Sherborne2015/07/1272%
Hammett Gordon8101Wah1998/06/0920%
Walker Nixon6901Metz2011/12/1141%
Nathan Espinoza5956Strathcona County2002/25/0147%
Kelly Cameron4836Fontaine-Valmont1999/02/0724%
Kyra Moses3796Quenast1998/07/0768%
Grace Bishop8340Rodez2012/02/104%
Haviva Hernandez8136Suwałki2000/30/0116%
Alisa Horn9853Ucluelet2007/01/1139%
Zelenia Roman7516Redwater2012/03/0331%
132 | 133 | 134 | 135 | 136 | 137 | 140 | 141 | 142 | -------------------------------------------------------------------------------- /docs/5-column-manipulation/datatable.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "Name": "Unity Pugh", 4 | "Ext.": "9958", 5 | "City": "Curicó", 6 | "Start Date": "2005/02/11" 7 | }, 8 | { 9 | "Name": "Theodore Duran", 10 | "Ext.": "8971", 11 | "City": "Dhanbad", 12 | "Start Date": "1999/04/07" 13 | }, 14 | { 15 | "Name": "Kylie Bishop", 16 | "Ext.": "3147", 17 | "City": "Norman", 18 | "Start Date": "2005/09/08" 19 | }, 20 | { 21 | "Name": "Willow Gilliam", 22 | "Ext.": "3497", 23 | "City": "Amqui", 24 | "Start Date": "2009/29/11" 25 | }, 26 | { 27 | "Name": "Blossom Dickerson", 28 | "Ext.": "5018", 29 | "City": "Kempten", 30 | "Start Date": "2006/11/09" 31 | }, 32 | { 33 | "Name": "Elliott Snyder", 34 | "Ext.": "3925", 35 | "City": "Enines", 36 | "Start Date": "2006/03/08" 37 | }, 38 | { 39 | "Name": "Castor Pugh", 40 | "Ext.": "9488", 41 | "City": "Neath", 42 | "Start Date": "2014/23/12" 43 | }, 44 | { 45 | "Name": "Pearl Carlson", 46 | "Ext.": "6231", 47 | "City": "Cobourg", 48 | "Start Date": "2014/31/08" 49 | }, 50 | { 51 | "Name": "Deirdre Bridges", 52 | "Ext.": "1579", 53 | "City": "Eberswalde-Finow", 54 | "Start Date": "2014/26/08" 55 | }, 56 | { 57 | "Name": "Daniel Baldwin", 58 | "Ext.": "6095", 59 | "City": "Moircy", 60 | "Start Date": "2000/11/01" 61 | }, 62 | { 63 | "Name": "Phelan Kane", 64 | "Ext.": "9519", 65 | "City": "Germersheim", 66 | "Start Date": "1999/16/04" 67 | }, 68 | { 69 | "Name": "Quentin Salas", 70 | "Ext.": "1339", 71 | "City": "Los Andes", 72 | "Start Date": "2011/26/01" 73 | }, 74 | { 75 | "Name": "Armand Suarez", 76 | "Ext.": "6583", 77 | "City": "Funtua", 78 | "Start Date": "1999/06/11" 79 | }, 80 | { 81 | "Name": "Gretchen Rogers", 82 | "Ext.": "5393", 83 | "City": "Moxhe", 84 | "Start Date": "1998/26/10" 85 | }, 86 | { 87 | "Name": "Harding Thompson", 88 | "Ext.": "2824", 89 | "City": "Abeokuta", 90 | "Start Date": "2008/06/08" 91 | }, 92 | { 93 | "Name": "Mira Rocha", 94 | "Ext.": "4393", 95 | "City": "Port Harcourt", 96 | "Start Date": "2002/04/10" 97 | }, 98 | { 99 | "Name": "Drew Phillips", 100 | "Ext.": "2931", 101 | "City": "Goes", 102 | "Start Date": "2011/18/10" 103 | }, 104 | { 105 | "Name": "Emerald Warner", 106 | "Ext.": "6205", 107 | "City": "Chiavari", 108 | "Start Date": "2002/08/04" 109 | }, 110 | { 111 | "Name": "Colin Burch", 112 | "Ext.": "7457", 113 | "City": "Anamur", 114 | "Start Date": "2004/02/01" 115 | }, 116 | { 117 | "Name": "Russell Haynes", 118 | "Ext.": "8916", 119 | "City": "Frascati", 120 | "Start Date": "2015/28/04" 121 | }, 122 | { 123 | "Name": "Brennan Brooks", 124 | "Ext.": "9011", 125 | "City": "Olmué", 126 | "Start Date": "2000/18/04" 127 | }, 128 | { 129 | "Name": "Kane Anthony", 130 | "Ext.": "8075", 131 | "City": "LaSalle", 132 | "Start Date": "2006/21/05" 133 | }, 134 | { 135 | "Name": "Scarlett Hurst", 136 | "Ext.": "1019", 137 | "City": "Brampton", 138 | "Start Date": "2015/07/01" 139 | }, 140 | { 141 | "Name": "James Scott", 142 | "Ext.": "3008", 143 | "City": "Meux", 144 | "Start Date": "2007/30/05" 145 | }, 146 | { 147 | "Name": "Desiree Ferguson", 148 | "Ext.": "9054", 149 | "City": "Gojra", 150 | "Start Date": "2009/15/02" 151 | }, 152 | { 153 | "Name": "Elaine Bishop", 154 | "Ext.": "9160", 155 | "City": "Petrópolis", 156 | "Start Date": "2008/23/12" 157 | }, 158 | { 159 | "Name": "Hilda Nelson", 160 | "Ext.": "6307", 161 | "City": "Posina", 162 | "Start Date": "2004/23/05" 163 | }, 164 | { 165 | "Name": "Evangeline Beasley", 166 | "Ext.": "3820", 167 | "City": "Caplan", 168 | "Start Date": "2009/12/03" 169 | }, 170 | { 171 | "Name": "Wyatt Riley", 172 | "Ext.": "5694", 173 | "City": "Cavaion Veronese", 174 | "Start Date": "2012/19/02" 175 | }, 176 | { 177 | "Name": "Wyatt Mccarthy", 178 | "Ext.": "3547", 179 | "City": "Patan", 180 | "Start Date": "2014/23/06" 181 | }, 182 | { 183 | "Name": "Cairo Rice", 184 | "Ext.": "6273", 185 | "City": "Ostra Vetere", 186 | "Start Date": "2016/27/02" 187 | }, 188 | { 189 | "Name": "Sylvia Peters", 190 | "Ext.": "6829", 191 | "City": "Arrah", 192 | "Start Date": "2015/03/02" 193 | }, 194 | { 195 | "Name": "Kasper Craig", 196 | "Ext.": "5515", 197 | "City": "Firenze", 198 | "Start Date": "2015/26/04" 199 | }, 200 | { 201 | "Name": "Leigh Ruiz", 202 | "Ext.": "5112", 203 | "City": "Lac Ste. Anne", 204 | "Start Date": "2001/09/02" 205 | }, 206 | { 207 | "Name": "Athena Aguirre", 208 | "Ext.": "5741", 209 | "City": "Romeral", 210 | "Start Date": "2010/24/03" 211 | }, 212 | { 213 | "Name": "Riley Nunez", 214 | "Ext.": "5533", 215 | "City": "Sart-Eustache", 216 | "Start Date": "2003/26/02" 217 | }, 218 | { 219 | "Name": "Lois Talley", 220 | "Ext.": "9393", 221 | "City": "Dorchester", 222 | "Start Date": "2014/05/01" 223 | }, 224 | { 225 | "Name": "Hop Bass", 226 | "Ext.": "1024", 227 | "City": "Westerlo", 228 | "Start Date": "2012/25/09" 229 | }, 230 | { 231 | "Name": "Kalia Diaz", 232 | "Ext.": "9184", 233 | "City": "Ichalkaranji", 234 | "Start Date": "2013/26/06" 235 | }, 236 | { 237 | "Name": "Maia Pate", 238 | "Ext.": "6682", 239 | "City": "Louvain-la-Neuve", 240 | "Start Date": "2011/23/04" 241 | }, 242 | { 243 | "Name": "Macaulay Pruitt", 244 | "Ext.": "4457", 245 | "City": "Fraser-Fort George", 246 | "Start Date": "2015/03/08" 247 | }, 248 | { 249 | "Name": "Danielle Oconnor", 250 | "Ext.": "9464", 251 | "City": "Neuwied", 252 | "Start Date": "2001/05/10" 253 | }, 254 | { 255 | "Name": "Kato Carr", 256 | "Ext.": "4842", 257 | "City": "Faridabad", 258 | "Start Date": "2012/11/05" 259 | }, 260 | { 261 | "Name": "Malachi Mejia", 262 | "Ext.": "7133", 263 | "City": "Vorst", 264 | "Start Date": "2007/25/04" 265 | }, 266 | { 267 | "Name": "Dominic Carver", 268 | "Ext.": "3476", 269 | "City": "Pointe-aux-Trembles", 270 | "Start Date": "2014/14/03" 271 | }, 272 | { 273 | "Name": "Paki Santos", 274 | "Ext.": "4424", 275 | "City": "Cache Creek", 276 | "Start Date": "2001/18/11" 277 | }, 278 | { 279 | "Name": "Ross Hodges", 280 | "Ext.": "1862", 281 | "City": "Trazegnies", 282 | "Start Date": "2010/19/09" 283 | }, 284 | { 285 | "Name": "Hilda Whitley", 286 | "Ext.": "3514", 287 | "City": "New Sarepta", 288 | "Start Date": "2011/05/07" 289 | }, 290 | { 291 | "Name": "Roth Cherry", 292 | "Ext.": "4006", 293 | "City": "Flin Flon", 294 | "Start Date": "2008/02/09" 295 | }, 296 | { 297 | "Name": "Lareina Jones", 298 | "Ext.": "8642", 299 | "City": "East Linton", 300 | "Start Date": "2009/07/08" 301 | }, 302 | { 303 | "Name": "Joshua Weiss", 304 | "Ext.": "2289", 305 | "City": "Saint-L�onard", 306 | "Start Date": "2010/15/01" 307 | }, 308 | { 309 | "Name": "Kiona Lowery", 310 | "Ext.": "5952", 311 | "City": "Inuvik", 312 | "Start Date": "2002/17/12" 313 | }, 314 | { 315 | "Name": "Nina Rush", 316 | "Ext.": "7567", 317 | "City": "Bo‘lhe", 318 | "Start Date": "2008/27/01" 319 | }, 320 | { 321 | "Name": "Palmer Parker", 322 | "Ext.": "2000", 323 | "City": "Stade", 324 | "Start Date": "2012/24/07" 325 | }, 326 | { 327 | "Name": "Vielka Olsen", 328 | "Ext.": "3745", 329 | "City": "Vrasene", 330 | "Start Date": "2016/08/01" 331 | }, 332 | { 333 | "Name": "Meghan Cunningham", 334 | "Ext.": "8604", 335 | "City": "Söke", 336 | "Start Date": "2007/16/02" 337 | }, 338 | { 339 | "Name": "Iola Shaw", 340 | "Ext.": "6447", 341 | "City": "Albany", 342 | "Start Date": "2014/05/03" 343 | }, 344 | { 345 | "Name": "Imelda Cole", 346 | "Ext.": "4564", 347 | "City": "Haasdonk", 348 | "Start Date": "2007/16/11" 349 | }, 350 | { 351 | "Name": "Jerry Beach", 352 | "Ext.": "6801", 353 | "City": "Gattatico", 354 | "Start Date": "1999/07/07" 355 | }, 356 | { 357 | "Name": "Garrett Rocha", 358 | "Ext.": "3938", 359 | "City": "Gavorrano", 360 | "Start Date": "2000/06/08" 361 | }, 362 | { 363 | "Name": "Derek Kerr", 364 | "Ext.": "1724", 365 | "City": "Gualdo Cattaneo", 366 | "Start Date": "2014/21/01" 367 | }, 368 | { 369 | "Name": "Shad Hudson", 370 | "Ext.": "5944", 371 | "City": "Salamanca", 372 | "Start Date": "2014/10/12" 373 | }, 374 | { 375 | "Name": "Daryl Ayers", 376 | "Ext.": "8276", 377 | "City": "Barchi", 378 | "Start Date": "2012/12/11" 379 | }, 380 | { 381 | "Name": "Caleb Livingston", 382 | "Ext.": "3094", 383 | "City": "Fatehpur", 384 | "Start Date": "2014/13/02" 385 | }, 386 | { 387 | "Name": "Sydney Meyer", 388 | "Ext.": "4576", 389 | "City": "Neubrandenburg", 390 | "Start Date": "2015/06/02" 391 | }, 392 | { 393 | "Name": "Lani Lawrence", 394 | "Ext.": "8501", 395 | "City": "Turnhout", 396 | "Start Date": "2008/07/05" 397 | }, 398 | { 399 | "Name": "Allegra Shepherd", 400 | "Ext.": "2576", 401 | "City": "Meeuwen-Gruitrode", 402 | "Start Date": "2004/19/04" 403 | }, 404 | { 405 | "Name": "Fallon Reyes", 406 | "Ext.": "3178", 407 | "City": "Monceau-sur-Sambre", 408 | "Start Date": "2005/15/02" 409 | }, 410 | { 411 | "Name": "Karen Whitley", 412 | "Ext.": "4357", 413 | "City": "Sluis", 414 | "Start Date": "2003/02/05" 415 | }, 416 | { 417 | "Name": "Stewart Stephenson", 418 | "Ext.": "5350", 419 | "City": "Villa Faraldi", 420 | "Start Date": "2003/05/07" 421 | }, 422 | { 423 | "Name": "Ursula Reynolds", 424 | "Ext.": "7544", 425 | "City": "Southampton", 426 | "Start Date": "1999/16/12" 427 | }, 428 | { 429 | "Name": "Adrienne Winters", 430 | "Ext.": "4425", 431 | "City": "Laguna Blanca", 432 | "Start Date": "2014/15/09" 433 | }, 434 | { 435 | "Name": "Francesca Brock", 436 | "Ext.": "1337", 437 | "City": "Oban", 438 | "Start Date": "2000/12/06" 439 | }, 440 | { 441 | "Name": "Ursa Davenport", 442 | "Ext.": "7629", 443 | "City": "New Plymouth", 444 | "Start Date": "2013/27/06" 445 | }, 446 | { 447 | "Name": "Mark Brock", 448 | "Ext.": "3310", 449 | "City": "Veenendaal", 450 | "Start Date": "2006/08/09" 451 | }, 452 | { 453 | "Name": "Dale Rush", 454 | "Ext.": "5050", 455 | "City": "Chicoutimi", 456 | "Start Date": "2000/27/03" 457 | }, 458 | { 459 | "Name": "Shellie Murphy", 460 | "Ext.": "3845", 461 | "City": "Marlborough", 462 | "Start Date": "2013/13/11" 463 | }, 464 | { 465 | "Name": "Porter Nicholson", 466 | "Ext.": "4539", 467 | "City": "Bismil", 468 | "Start Date": "2012/22/10" 469 | }, 470 | { 471 | "Name": "Oliver Huber", 472 | "Ext.": "1265", 473 | "City": "Hannche", 474 | "Start Date": "2002/11/01" 475 | }, 476 | { 477 | "Name": "Calista Maynard", 478 | "Ext.": "3315", 479 | "City": "Pozzuolo del Friuli", 480 | "Start Date": "2006/23/03" 481 | }, 482 | { 483 | "Name": "Lois Vargas", 484 | "Ext.": "6825", 485 | "City": "Cumberland", 486 | "Start Date": "1999/25/04" 487 | }, 488 | { 489 | "Name": "Hermione Dickson", 490 | "Ext.": "2785", 491 | "City": "Woodstock", 492 | "Start Date": "2001/22/03" 493 | }, 494 | { 495 | "Name": "Dalton Jennings", 496 | "Ext.": "5416", 497 | "City": "Dudzele", 498 | "Start Date": "2015/09/02" 499 | }, 500 | { 501 | "Name": "Cathleen Kramer", 502 | "Ext.": "3380", 503 | "City": "Crowsnest Pass", 504 | "Start Date": "2012/27/07" 505 | }, 506 | { 507 | "Name": "Zachery Morgan", 508 | "Ext.": "6730", 509 | "City": "Collines-de-l'Outaouais", 510 | "Start Date": "2006/04/09" 511 | }, 512 | { 513 | "Name": "Yoko Freeman", 514 | "Ext.": "4077", 515 | "City": "Lidköping", 516 | "Start Date": "2002/27/12" 517 | }, 518 | { 519 | "Name": "Chaim Waller", 520 | "Ext.": "4240", 521 | "City": "North Shore", 522 | "Start Date": "2010/25/07" 523 | }, 524 | { 525 | "Name": "Berk Johnston", 526 | "Ext.": "4532", 527 | "City": "Vergnies", 528 | "Start Date": "2016/23/02" 529 | }, 530 | { 531 | "Name": "Tad Munoz", 532 | "Ext.": "2902", 533 | "City": "Saint-Nazaire", 534 | "Start Date": "2010/09/05" 535 | }, 536 | { 537 | "Name": "Vivien Dominguez", 538 | "Ext.": "5653", 539 | "City": "Bargagli", 540 | "Start Date": "2001/09/01" 541 | }, 542 | { 543 | "Name": "Carissa Lara", 544 | "Ext.": "3241", 545 | "City": "Sherborne", 546 | "Start Date": "2015/07/12" 547 | }, 548 | { 549 | "Name": "Hammett Gordon", 550 | "Ext.": "8101", 551 | "City": "Wah", 552 | "Start Date": "1998/06/09" 553 | }, 554 | { 555 | "Name": "Walker Nixon", 556 | "Ext.": "6901", 557 | "City": "Metz", 558 | "Start Date": "2011/12/11" 559 | }, 560 | { 561 | "Name": "Nathan Espinoza", 562 | "Ext.": "5956", 563 | "City": "Strathcona County", 564 | "Start Date": "2002/25/01" 565 | }, 566 | { 567 | "Name": "Kelly Cameron", 568 | "Ext.": "4836", 569 | "City": "Fontaine-Valmont", 570 | "Start Date": "1999/02/07" 571 | }, 572 | { 573 | "Name": "Kyra Moses", 574 | "Ext.": "3796", 575 | "City": "Quenast", 576 | "Start Date": "1998/07/07" 577 | }, 578 | { 579 | "Name": "Grace Bishop", 580 | "Ext.": "8340", 581 | "City": "Rodez", 582 | "Start Date": "2012/02/10" 583 | }, 584 | { 585 | "Name": "Haviva Hernandez", 586 | "Ext.": "8136", 587 | "City": "Suwałki", 588 | "Start Date": "2000/30/01" 589 | }, 590 | { 591 | "Name": "Alisa Horn", 592 | "Ext.": "9853", 593 | "City": "Ucluelet", 594 | "Start Date": "2007/01/11" 595 | }, 596 | { 597 | "Name": "Zelenia Roman", 598 | "Ext.": "7516", 599 | "City": "Redwater", 600 | "Start Date": "2012/03/03" 601 | } 602 | ] -------------------------------------------------------------------------------- /src/columns.js: -------------------------------------------------------------------------------- 1 | import {sortItems} from "./helpers" 2 | 3 | /** 4 | * Columns API 5 | * @param {Object} instance DataTable instance 6 | * @param {Mixed} columns Column index or array of column indexes 7 | */ 8 | export class Columns { 9 | constructor(dt) { 10 | this.dt = dt 11 | return this 12 | } 13 | 14 | /** 15 | * Swap two columns 16 | * @return {Void} 17 | */ 18 | swap(columns) { 19 | if (columns.length && columns.length === 2) { 20 | const cols = [] 21 | 22 | // Get the current column indexes 23 | this.dt.headings.forEach((h, i) => { 24 | cols.push(i) 25 | }) 26 | 27 | const x = columns[0] 28 | const y = columns[1] 29 | const b = cols[y] 30 | cols[y] = cols[x] 31 | cols[x] = b 32 | 33 | this.order(cols) 34 | } 35 | } 36 | 37 | /** 38 | * Reorder the columns 39 | * @return {Array} columns Array of ordered column indexes 40 | */ 41 | order(columns) { 42 | let a 43 | let b 44 | let c 45 | let d 46 | let h 47 | let s 48 | let cell 49 | 50 | const temp = [ 51 | [], 52 | [], 53 | [], 54 | [] 55 | ] 56 | 57 | const dt = this.dt 58 | 59 | // Order the headings 60 | columns.forEach((column, x) => { 61 | h = dt.headings[column] 62 | s = h.getAttribute("data-sortable") !== "false" 63 | a = h.cloneNode(true) 64 | a.originalCellIndex = x 65 | a.sortable = s 66 | 67 | temp[0].push(a) 68 | 69 | if (!dt.hiddenColumns.includes(column)) { 70 | b = h.cloneNode(true) 71 | b.originalCellIndex = x 72 | b.sortable = s 73 | 74 | temp[1].push(b) 75 | } 76 | }) 77 | 78 | // Order the row cells 79 | dt.data.forEach((row, i) => { 80 | c = row.cloneNode(false) 81 | d = row.cloneNode(false) 82 | 83 | c.dataIndex = d.dataIndex = i 84 | 85 | if (row.searchIndex !== null && row.searchIndex !== undefined) { 86 | c.searchIndex = d.searchIndex = row.searchIndex 87 | } 88 | 89 | // Append the cell to the fragment in the correct order 90 | columns.forEach(column => { 91 | cell = row.cells[column].cloneNode(true) 92 | cell.data = row.cells[column].data 93 | c.appendChild(cell) 94 | 95 | if (!dt.hiddenColumns.includes(column)) { 96 | cell = row.cells[column].cloneNode(true) 97 | cell.data = row.cells[column].data 98 | d.appendChild(cell) 99 | } 100 | }) 101 | 102 | temp[2].push(c) 103 | temp[3].push(d) 104 | }) 105 | 106 | dt.headings = temp[0] 107 | dt.activeHeadings = temp[1] 108 | 109 | dt.data = temp[2] 110 | dt.activeRows = temp[3] 111 | 112 | // Update 113 | dt.update() 114 | } 115 | 116 | /** 117 | * Hide columns 118 | * @return {Void} 119 | */ 120 | hide(columns) { 121 | if (columns.length) { 122 | const dt = this.dt 123 | 124 | columns.forEach(column => { 125 | if (!dt.hiddenColumns.includes(column)) { 126 | dt.hiddenColumns.push(column) 127 | } 128 | }) 129 | 130 | this.rebuild() 131 | } 132 | } 133 | 134 | /** 135 | * Show columns 136 | * @return {Void} 137 | */ 138 | show(columns) { 139 | if (columns.length) { 140 | let index 141 | const dt = this.dt 142 | 143 | columns.forEach(column => { 144 | index = dt.hiddenColumns.indexOf(column) 145 | if (index > -1) { 146 | dt.hiddenColumns.splice(index, 1) 147 | } 148 | }) 149 | 150 | this.rebuild() 151 | } 152 | } 153 | 154 | /** 155 | * Check column(s) visibility 156 | * @return {Boolean} 157 | */ 158 | visible(columns) { 159 | let cols 160 | const dt = this.dt 161 | 162 | columns = columns || dt.headings.map(th => th.originalCellIndex) 163 | 164 | if (!isNaN(columns)) { 165 | cols = !dt.hiddenColumns.includes(columns) 166 | } else if (Array.isArray(columns)) { 167 | cols = [] 168 | columns.forEach(column => { 169 | cols.push(!dt.hiddenColumns.includes(column)) 170 | }) 171 | } 172 | 173 | return cols 174 | } 175 | 176 | /** 177 | * Add a new column 178 | * @param {Object} data 179 | */ 180 | add(data) { 181 | let td 182 | const th = document.createElement("th") 183 | 184 | if (!this.dt.headings.length) { 185 | this.dt.insert({ 186 | headings: [data.heading], 187 | data: data.data.map(i => [i]) 188 | }) 189 | this.rebuild() 190 | return 191 | } 192 | 193 | if (!this.dt.hiddenHeader) { 194 | if (data.heading.nodeName) { 195 | th.appendChild(data.heading) 196 | } else { 197 | th.innerHTML = data.heading 198 | } 199 | } else { 200 | th.innerHTML = "" 201 | } 202 | 203 | this.dt.headings.push(th) 204 | 205 | this.dt.data.forEach((row, i) => { 206 | if (data.data[i]) { 207 | td = document.createElement("td") 208 | 209 | if (data.data[i].nodeName) { 210 | td.appendChild(data.data[i]) 211 | } else { 212 | td.innerHTML = data.data[i] 213 | } 214 | 215 | td.data = td.innerHTML 216 | 217 | if (data.render) { 218 | td.innerHTML = data.render.call(this, td.data, td, row) 219 | } 220 | 221 | row.appendChild(td) 222 | } 223 | }) 224 | 225 | if (data.type) { 226 | th.setAttribute("data-type", data.type) 227 | } 228 | if (data.format) { 229 | th.setAttribute("data-format", data.format) 230 | } 231 | 232 | if (data.hasOwnProperty("sortable")) { 233 | th.sortable = data.sortable 234 | th.setAttribute("data-sortable", data.sortable === true ? "true" : "false") 235 | } 236 | 237 | this.rebuild() 238 | 239 | this.dt.renderHeader() 240 | } 241 | 242 | /** 243 | * Remove column(s) 244 | * @param {Array|Number} select 245 | * @return {Void} 246 | */ 247 | remove(select) { 248 | if (Array.isArray(select)) { 249 | // Remove in reverse otherwise the indexes will be incorrect 250 | select.sort((a, b) => b - a) 251 | select.forEach(column => this.remove(column)) 252 | } else { 253 | this.dt.headings.splice(select, 1) 254 | 255 | this.dt.data.forEach(row => { 256 | row.removeChild(row.cells[select]) 257 | }) 258 | } 259 | 260 | this.rebuild() 261 | } 262 | 263 | /** 264 | * Filter by column 265 | * @param {int} column - The column no. 266 | * @param {string} dir - asc or desc 267 | * @filter {array} filter - optional parameter with a list of strings 268 | * @return {void} 269 | */ 270 | filter(column, dir, init, terms) { 271 | const dt = this.dt 272 | 273 | // Creates a internal state that manages filters if there are none 274 | if ( !dt.filterState ) { 275 | dt.filterState = { 276 | originalData: dt.data 277 | } 278 | } 279 | 280 | // If that column is was not filtered yet, we need to create its state 281 | if ( !dt.filterState[column] ) { 282 | 283 | // append a filter that selects all rows, 'resetting' the filter 284 | const filters = [...terms, () => true] 285 | 286 | dt.filterState[column] = ( 287 | function() { 288 | let i = 0; 289 | return () => filters[i++ % (filters.length)] 290 | }() 291 | ) 292 | } 293 | 294 | // Apply the filter and rebuild table 295 | const rowFilter = dt.filterState[column]() // fetches next filter 296 | const filteredRows = Array.from(dt.filterState.originalData).filter(tr => { 297 | const cell = tr.cells[column] 298 | const content = cell.hasAttribute('data-content') ? cell.getAttribute('data-content') : cell.innerText 299 | 300 | // If the filter is a function, call it, if it is a string, compare it 301 | return (typeof rowFilter) === 'function' ? rowFilter(content) : content === rowFilter; 302 | }) 303 | 304 | dt.data = filteredRows 305 | this.rebuild() 306 | dt.update() 307 | if (!init) { 308 | dt.emit("datatable.sort", column, dir) 309 | } 310 | } 311 | 312 | /** 313 | * Sort by column 314 | * @param {int} column - The column no. 315 | * @param {string} dir - asc or desc 316 | * @return {void} 317 | */ 318 | sort(column, dir, init) { 319 | const dt = this.dt 320 | 321 | // Check column is present 322 | if (dt.hasHeadings && (column < 0 || column > dt.headings.length)) { 323 | return false 324 | } 325 | 326 | //If there is a filter for this column, apply it instead of sorting 327 | const filterTerms = dt.options.filters && 328 | dt.options.filters[dt.headings[column].textContent] 329 | if ( filterTerms && filterTerms.length !== 0 ) { 330 | this.filter(column, dir, init, filterTerms) 331 | return; 332 | } 333 | 334 | dt.sorting = true 335 | 336 | if (!init) { 337 | dt.emit("datatable.sorting", column, dir) 338 | } 339 | 340 | let rows = dt.data 341 | const alpha = [] 342 | const numeric = [] 343 | let a = 0 344 | let n = 0 345 | const th = dt.headings[column] 346 | 347 | const waitFor = [] 348 | 349 | // Check for date format 350 | if (th.getAttribute("data-type") === "date") { 351 | let format = false 352 | const formatted = th.hasAttribute("data-format") 353 | 354 | if (formatted) { 355 | format = th.getAttribute("data-format") 356 | } 357 | waitFor.push(import("./date").then(({parseDate}) => date => parseDate(date, format))) 358 | } 359 | 360 | Promise.all(waitFor).then(importedFunctions => { 361 | const parseFunction = importedFunctions[0] // only defined if date 362 | Array.from(rows).forEach(tr => { 363 | const cell = tr.cells[column] 364 | const content = cell.hasAttribute('data-content') ? cell.getAttribute('data-content') : cell.innerText 365 | let num 366 | if (parseFunction) { 367 | num = parseFunction(content) 368 | } else if (typeof content==="string") { 369 | num = content.replace(/(\$|,|\s|%)/g, "") 370 | } else { 371 | num = content 372 | } 373 | 374 | if (parseFloat(num) == num) { 375 | numeric[n++] = { 376 | value: Number(num), 377 | row: tr 378 | } 379 | } else { 380 | alpha[a++] = { 381 | value: typeof content==="string" ? content.toLowerCase() : content, 382 | row: tr 383 | } 384 | } 385 | }) 386 | 387 | /* Sort according to direction (ascending or descending) */ 388 | if (!dir) { 389 | if (th.classList.contains("asc")) { 390 | dir = "desc" 391 | } else { 392 | dir = "asc" 393 | } 394 | } 395 | let top 396 | let btm 397 | if (dir == "desc") { 398 | top = sortItems(alpha, -1) 399 | btm = sortItems(numeric, -1) 400 | th.classList.remove("asc") 401 | th.classList.add("desc") 402 | } else { 403 | top = sortItems(numeric, 1) 404 | btm = sortItems(alpha, 1) 405 | th.classList.remove("desc") 406 | th.classList.add("asc") 407 | } 408 | 409 | /* Clear asc/desc class names from the last sorted column's th if it isn't the same as the one that was just clicked */ 410 | if (dt.lastTh && th != dt.lastTh) { 411 | dt.lastTh.classList.remove("desc") 412 | dt.lastTh.classList.remove("asc") 413 | } 414 | 415 | dt.lastTh = th 416 | 417 | /* Reorder the table */ 418 | rows = top.concat(btm) 419 | 420 | dt.data = [] 421 | const indexes = [] 422 | 423 | rows.forEach((v, i) => { 424 | dt.data.push(v.row) 425 | 426 | if (v.row.searchIndex !== null && v.row.searchIndex !== undefined) { 427 | indexes.push(i) 428 | } 429 | }) 430 | 431 | dt.searchData = indexes 432 | 433 | this.rebuild() 434 | 435 | dt.update() 436 | 437 | if (!init) { 438 | dt.emit("datatable.sort", column, dir) 439 | } 440 | }) 441 | } 442 | 443 | /** 444 | * Rebuild the columns 445 | * @return {Void} 446 | */ 447 | rebuild() { 448 | let a 449 | let b 450 | let c 451 | let d 452 | const dt = this.dt 453 | const temp = [] 454 | 455 | dt.activeRows = [] 456 | dt.activeHeadings = [] 457 | 458 | dt.headings.forEach((th, i) => { 459 | th.originalCellIndex = i 460 | th.sortable = th.getAttribute("data-sortable") !== "false" 461 | if (!dt.hiddenColumns.includes(i)) { 462 | dt.activeHeadings.push(th) 463 | } 464 | }) 465 | 466 | // Loop over the rows and reorder the cells 467 | dt.data.forEach((row, i) => { 468 | a = row.cloneNode(false) 469 | b = row.cloneNode(false) 470 | 471 | a.dataIndex = b.dataIndex = i 472 | 473 | if (row.searchIndex !== null && row.searchIndex !== undefined) { 474 | a.searchIndex = b.searchIndex = row.searchIndex 475 | } 476 | 477 | // Append the cell to the fragment in the correct order 478 | Array.from(row.cells).forEach(cell => { 479 | c = cell.cloneNode(true) 480 | c.data = cell.data 481 | a.appendChild(c) 482 | 483 | if (!dt.hiddenColumns.includes(c.cellIndex)) { 484 | d = c.cloneNode(true) 485 | d.data = c.data 486 | b.appendChild(d) 487 | } 488 | }) 489 | 490 | // Append the fragment with the ordered cells 491 | temp.push(a) 492 | dt.activeRows.push(b) 493 | }) 494 | 495 | dt.data = temp 496 | 497 | dt.update() 498 | } 499 | } 500 | -------------------------------------------------------------------------------- /docs/6-datetime-sorting/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Datetime Sorting 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 |

Datetime Sorting

19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 | 208 | 209 | 210 | 211 | 212 | 213 | 214 | 215 | 216 | 217 | 218 | 219 | 220 | 221 | 222 | 223 | 224 | 225 | 226 | 227 | 228 | 229 | 230 | 231 | 232 | 233 | 234 | 235 | 236 | 237 | 238 | 239 | 240 | 241 | 242 | 243 | 244 | 245 | 246 | 247 | 248 | 249 | 250 | 251 | 252 | 253 | 254 | 255 | 256 | 257 | 258 | 259 | 260 | 261 | 262 | 263 | 264 | 265 | 266 | 267 | 268 | 269 | 270 | 271 | 272 | 273 | 274 | 275 | 276 | 277 | 278 | 279 | 280 | 281 | 282 | 283 | 284 | 285 | 286 | 287 | 288 | 289 | 290 | 291 | 292 | 293 | 294 | 295 | 296 | 297 | 298 | 299 | 300 | 301 | 302 | 303 | 304 | 305 | 306 | 307 | 308 | 309 | 310 | 311 | 312 | 313 | 314 | 315 | 316 | 317 | 318 | 319 | 320 | 321 | 322 | 323 | 324 | 325 | 326 | 327 | 328 | 329 | 330 | 331 | 332 | 333 | 334 | 335 | 336 | 337 | 338 | 339 | 340 | 341 | 342 | 343 | 344 | 345 | 346 | 347 | 348 | 349 | 350 | 351 | 352 | 353 | 354 | 355 | 356 | 357 | 358 | 359 | 360 | 361 | 362 | 363 | 364 | 365 | 366 | 367 | 368 | 369 | 370 | 371 | 372 | 373 | 374 | 375 | 376 | 377 | 378 | 379 | 380 | 381 | 382 | 383 | 384 | 385 | 386 | 387 | 388 | 389 | 390 | 391 | 392 | 393 | 394 | 395 | 396 | 397 | 398 | 399 | 400 | 401 | 402 | 403 | 404 | 405 | 406 | 407 | 408 | 409 | 410 | 411 | 412 | 413 | 414 | 415 | 416 | 417 | 418 | 419 | 420 | 421 | 422 | 423 | 424 | 425 | 426 | 427 | 428 | 429 | 430 | 431 | 432 | 433 | 434 | 435 | 436 | 437 | 438 | 439 | 440 | 441 | 442 | 443 | 444 | 445 | 446 | 447 | 448 | 449 | 450 | 451 | 452 | 453 | 454 | 455 | 456 | 457 | 458 | 459 | 460 | 461 | 462 | 463 | 464 | 465 | 466 | 467 | 468 | 469 | 470 | 471 | 472 | 473 | 474 | 475 | 476 | 477 | 478 | 479 | 480 | 481 | 482 | 483 | 484 | 485 | 486 | 487 | 488 | 489 | 490 | 491 | 492 | 493 | 494 | 495 | 496 | 497 | 498 | 499 | 500 | 501 | 502 | 503 | 504 | 505 | 506 | 507 | 508 | 509 | 510 | 511 | 512 | 513 | 514 | 515 | 516 | 517 | 518 | 519 | 520 | 521 | 522 | 523 | 524 | 525 | 526 | 527 | 528 | 529 | 530 | 531 | 532 | 533 | 534 | 535 | 536 | 537 | 538 | 539 | 540 | 541 | 542 | 543 | 544 | 545 | 546 | 547 | 548 | 549 | 550 | 551 | 552 | 553 | 554 | 555 | 556 | 557 | 558 | 559 | 560 | 561 | 562 | 563 | 564 | 565 | 566 | 567 | 568 | 569 | 570 | 571 | 572 | 573 | 574 | 575 | 576 | 577 | 578 | 579 | 580 | 581 | 582 | 583 | 584 | 585 | 586 | 587 | 588 | 589 | 590 | 591 | 592 | 593 | 594 | 595 | 596 | 597 | 598 | 599 | 600 | 601 | 602 | 603 | 604 | 605 | 606 | 607 | 608 | 609 | 610 | 611 | 612 | 613 | 614 | 615 | 616 | 617 | 618 | 619 | 620 | 621 | 622 | 623 | 624 | 625 | 626 | 627 | 628 | 629 | 630 | 631 | 632 | 633 | 634 | 635 | 636 | 637 | 638 | 639 | 640 | 641 | 642 | 643 | 644 | 645 | 646 | 647 | 648 | 649 | 650 | 651 | 652 | 653 | 654 | 655 | 656 | 657 | 658 | 659 | 660 | 661 | 662 | 663 | 664 | 665 | 666 | 667 | 668 | 669 | 670 | 671 | 672 | 673 | 674 | 675 | 676 | 677 | 678 | 679 | 680 | 681 | 682 | 683 | 684 | 685 | 686 | 687 | 688 | 689 | 690 | 691 | 692 | 693 | 694 | 695 | 696 | 697 | 698 | 699 | 700 | 701 | 702 | 703 | 704 | 705 | 706 | 707 | 708 | 709 | 710 | 711 | 712 | 713 | 714 | 715 | 716 | 717 | 718 | 719 | 720 | 721 | 722 | 723 | 724 | 725 | 726 | 727 | 728 | 729 | 730 | 731 | 732 | 733 | 734 | 735 | 736 | 737 | 738 | 739 | 740 | 741 | 742 | 743 | 744 | 745 | 746 | 747 | 748 | 749 | 750 | 751 | 752 | 753 | 754 | 755 | 756 | 757 | 758 | 759 | 760 | 761 | 762 | 763 | 764 | 765 | 766 | 767 | 768 | 769 | 770 | 771 | 772 | 773 | 774 | 775 | 776 | 777 | 778 | 779 | 780 | 781 | 782 | 783 | 784 | 785 | 786 | 787 | 788 | 789 | 790 | 791 | 792 | 793 | 794 | 795 | 796 | 797 | 798 | 799 | 800 | 801 | 802 | 803 | 804 | 805 | 806 | 807 | 808 | 809 | 810 | 811 | 812 | 813 | 814 | 815 | 816 | 817 | 818 | 819 | 820 | 821 | 822 | 823 | 824 | 825 | 826 | 827 | 828 | 829 | 830 | 831 | 832 | 833 | 834 | 835 | 836 | 837 | 838 | 839 | 840 | 841 | 842 | 843 | 844 | 845 | 846 | 847 | 848 | 849 | 850 | 851 | 852 | 853 | 854 | 855 | 856 | 857 | 858 | 859 | 860 | 861 | 862 | 863 | 864 | 865 | 866 | 867 | 868 | 869 | 870 | 871 | 872 | 873 | 874 | 875 | 876 | 877 | 878 | 879 | 880 | 881 | 882 | 883 | 884 | 885 | 886 | 887 | 888 | 889 | 890 | 891 | 892 | 893 | 894 | 895 | 896 | 897 | 898 | 899 | 900 | 901 | 902 | 903 | 904 | 905 | 906 | 907 | 908 | 909 | 910 | 911 | 912 | 913 | 914 | 915 | 916 | 917 | 918 | 919 | 920 | 921 | 922 | 923 | 924 | 925 | 926 | 927 | 928 | 929 | 930 | 931 | 932 | 933 |
ISO 8601MySQLRFC 2822MMM DD, YYYYDD/MM/YYYYMM/DD/YYUNIX
2013-06-27T19:10:21-07:001994-12-06 22:56:36Tue, 14 Mar 1995 07:11:46 -0800Dec 8, 200119/10/201607-23-12853102101
1980-09-12T02:50:23-07:001997-11-23 07:06:45Mon, 21 Nov 2005 04:23:35 -0800Aug 19, 199727/05/198101-13-17713671686
1994-10-27T12:11:30-07:002001-06-22 09:21:47Sat, 30 Apr 2005 03:36:38 -0700Aug 1, 198404/11/201104-25-111408349475
2015-05-20T15:38:00-07:001990-12-20 10:13:03Thu, 16 Jan 2003 16:38:36 -0800Aug 19, 198205/11/198903-06-18677441387
1982-07-05T06:31:38-07:001989-11-18 13:43:58Thu, 09 Nov 2000 10:25:09 -0800Oct 11, 200316/07/199210-29-11523469491
2008-08-11T01:01:32-07:001981-02-16 19:29:34Thu, 18 Dec 2003 07:16:19 -0800May 16, 198130/04/198905-24-871141495716
1982-11-28T18:34:32-08:002002-02-22 04:36:38Fri, 11 Mar 1994 01:16:08 -0800Apr 2, 201412/10/200805-01-90464746558
1996-07-29T13:05:44-07:001993-03-15 18:27:14Fri, 06 Jun 2003 07:53:59 -0700Jul 16, 200414/02/199502-08-96405430252
1987-03-02T20:03:45-08:001990-01-09 10:26:10Thu, 15 Sep 2005 13:12:25 -0700Nov 9, 200514/06/198311-19-131310935236
2014-04-02T01:20:31-07:001995-03-19 17:07:42Sat, 11 May 2013 04:14:36 -0700Jun 11, 200017/02/200105-03-15636464866
1984-01-18T00:39:34-08:002015-12-11 13:14:55Fri, 25 Jan 2013 21:09:50 -0800Aug 15, 201311/02/200511-29-93922286213
2012-09-04T15:14:23-07:001990-01-22 12:37:32Sat, 01 May 1999 08:20:37 -0700Jan 27, 199902/02/201012-04-06545366064
1988-03-14T11:42:05-08:001992-10-11 21:13:23Tue, 16 Sep 2008 10:32:44 -0700May 28, 201215/08/200302-21-90896785729
1983-06-01T05:28:43-07:001999-07-12 06:19:13Tue, 16 Nov 1999 21:31:30 -0800Nov 30, 200823/01/200403-17-081422578713
1980-08-06T05:20:08-07:001986-09-14 19:36:53Fri, 29 Oct 1999 22:21:35 -0700May 28, 201423/01/198303-13-041459690948
2014-02-15T19:50:40-08:001997-11-11 15:45:24Tue, 07 Jun 1988 16:20:04 -0700Oct 17, 199204/12/199310-10-081349969151
2007-02-08T13:38:13-08:002010-01-05 22:22:21Mon, 18 Sep 2017 01:18:07 -0700Apr 29, 199916/08/199406-30-84756326365
2014-02-03T17:32:42-08:001989-06-20 22:14:41Mon, 20 Apr 1981 04:11:15 -0800Jan 17, 199316/07/201509-20-15688566388
2015-03-30T05:58:40-07:002010-10-31 08:34:15Tue, 26 May 1998 19:03:54 -0700Jan 12, 201213/08/198811-27-09660566468
1992-07-03T09:37:26-07:001996-01-19 16:42:48Wed, 06 Apr 2005 16:01:04 -0700Aug 19, 199410/10/199012-03-10797131280
2004-03-01T04:34:57-08:001982-10-26 19:46:32Fri, 23 May 2008 11:31:53 -0700Apr 27, 201830/03/200009-15-131012207791
1984-01-23T01:49:39-08:002003-06-12 23:12:34Wed, 07 May 2014 13:14:43 -0700May 22, 198114/12/199405-04-88411303318
2000-02-01T08:26:03-08:002013-08-17 17:08:56Sun, 16 Feb 2014 08:49:40 -0800Sep 14, 199105/01/199809-03-001230907590
1990-04-28T11:08:20-07:002016-04-15 18:30:45Mon, 30 Jan 1995 17:37:31 -0800Nov 5, 199826/10/201508-04-99506528026
2017-05-30T18:51:39-07:002008-10-16 08:24:15Fri, 24 May 2002 16:37:40 -0700Jul 31, 200805/07/199210-13-081469041684
1997-09-23T16:45:18-07:001980-10-18 17:44:41Mon, 01 May 1995 19:59:01 -0700Nov 4, 200025/12/198907-19-12621984653
1995-04-29T23:03:40-07:001989-10-25 12:31:30Tue, 15 Jan 2002 05:54:34 -0800Jan 6, 200414/09/198812-06-051197629198
1992-09-03T16:13:30-07:002016-11-02 20:53:12Sun, 26 Oct 2008 10:23:41 -0700Feb 20, 201224/08/199710-09-90971030263
1985-06-14T07:56:01-07:002012-08-21 03:36:44Sat, 14 Jul 1990 09:55:01 -0700Jul 18, 200602/12/198607-19-10790694847
2003-07-04T23:31:49-07:002011-09-10 03:13:33Wed, 14 Apr 1999 22:22:13 -0700Mar 17, 201504/04/199702-14-17531923003
2003-04-08T17:11:53-07:001994-01-07 13:48:34Tue, 21 Nov 2000 08:27:16 -0800Dec 20, 199410/01/200409-09-12852109187
2004-10-13T08:16:30-07:002003-10-15 00:27:08Wed, 05 Nov 2008 04:29:41 -0800Apr 16, 200815/07/201106-29-89768738934
2014-09-08T02:46:50-07:002005-06-25 22:30:28Wed, 29 Aug 2007 11:36:57 -0700Jul 30, 200714/06/201712-19-82792021852
2013-07-31T20:04:20-07:002000-05-08 03:10:48Wed, 21 Aug 2002 02:44:56 -0700Aug 5, 199212/06/199001-31-09499082057
1981-06-19T06:12:48-07:001996-01-04 13:55:27Sun, 16 Apr 1995 17:10:31 -0700Jan 29, 200317/12/200909-23-01751381880
1983-03-26T22:14:09-08:002010-05-24 10:15:52Wed, 30 May 1990 04:00:23 -0700Nov 5, 200130/06/200506-27-171361673368
2007-10-27T00:12:47-07:001990-03-19 14:32:54Wed, 15 Jul 2015 19:09:29 -0700Jun 9, 200924/10/200208-06-06381211783
1986-03-01T20:03:21-08:001999-11-11 15:25:57Wed, 12 Dec 1990 08:33:58 -0800Mar 30, 200909/10/199609-30-14409927546
2008-05-23T06:42:32-07:001997-09-23 09:21:44Tue, 21 Aug 1990 22:35:33 -0700Jul 13, 200207/06/200603-07-96720880299
1988-01-08T21:13:18-08:002003-08-29 15:35:34Wed, 04 May 1983 07:15:13 -0700May 3, 200106/10/201603-22-92589612117
1985-11-28T05:00:37-08:001998-02-15 10:31:39Wed, 17 Jul 1996 14:19:42 -0700Jan 16, 200619/01/198202-16-03936614487
1998-04-16T10:01:58-07:001988-09-27 12:09:21Sun, 31 Dec 1989 04:56:52 -0800Jul 1, 200611/07/201706-29-88744280097
2001-04-26T04:45:59-07:001983-12-12 01:14:03Tue, 10 May 1994 16:46:37 -0700Feb 13, 198712/01/200208-08-931032132619
2016-07-09T08:17:55-07:001995-12-30 01:59:58Sun, 24 Oct 1982 08:21:14 -0700Jan 23, 200628/09/201403-20-12438442662
1986-04-17T16:29:47-08:001997-12-23 00:33:41Tue, 19 Dec 2006 08:37:27 -0800Apr 21, 199404/03/201810-28-921283999369
2001-05-28T14:42:06-07:001989-07-17 21:20:24Thu, 03 Oct 1996 14:01:26 -0700Jul 11, 199812/02/199607-17-14796383797
2010-05-10T21:56:37-07:001981-11-30 23:34:05Sun, 20 Jan 1991 18:26:15 -0800May 12, 198413/08/198909-27-151093571343
2013-04-15T11:28:17-07:002018-05-26 18:25:17Mon, 07 Jul 2008 21:44:42 -0700Jul 7, 199722/01/199007-25-941129728454
2011-03-21T07:49:44-07:001986-10-01 15:01:21Tue, 02 Dec 2014 09:47:38 -0800Sep 2, 201407/10/200910-17-101513973450
1985-09-18T16:06:08-07:002007-11-28 01:13:17Sat, 14 Dec 2013 02:28:33 -0800Jan 29, 200230/01/198303-30-02465440158
1998-03-06T08:34:10-08:002012-09-04 15:17:32Tue, 20 Sep 2005 00:58:46 -0700Jan 8, 201011/05/199205-31-921252904607
1980-10-08T17:46:35-07:001984-07-06 07:49:35Sun, 25 May 2008 09:57:45 -0700Oct 14, 201105/04/199602-07-941502652368
1997-09-07T09:04:35-07:001986-11-12 02:14:07Sat, 17 Apr 1993 04:28:18 -0700Nov 14, 200323/10/199211-28-83566372083
1992-10-10T21:54:26-07:001992-08-15 20:56:52Fri, 15 Nov 2013 19:02:12 -0800Apr 1, 201824/11/201104-19-15654883244
2011-12-14T11:17:22-08:002000-09-16 01:28:14Sun, 21 Nov 1999 11:32:28 -0800Sep 2, 198801/12/198902-13-09972417505
1995-12-13T19:41:05-08:002010-05-28 14:09:07Thu, 13 Nov 2014 04:04:59 -0800Jan 21, 199228/12/198902-18-90553737111
1995-05-16T17:26:28-07:002000-05-07 02:25:42Tue, 26 Oct 1999 08:25:52 -0700Jun 2, 200403/04/199204-09-99404504492
2014-02-28T00:16:31-08:002009-05-05 15:57:45Mon, 28 Apr 1986 02:59:02 -0700Jan 23, 198103/06/198304-26-94994880898
1984-11-06T19:37:35-08:002000-08-28 03:13:44Mon, 22 Mar 1982 11:49:23 -0800Dec 4, 199102/12/201411-13-94960682780
1989-03-06T03:14:21-08:002006-09-29 04:05:51Sun, 23 Oct 2005 09:06:01 -0700Jun 4, 200710/11/198410-11-811444696397
1990-06-21T22:15:34-07:002002-05-23 00:48:07Wed, 25 Dec 2013 03:52:48 -0800Feb 14, 198228/04/198602-16-131100640865
2009-02-18T20:45:17-08:002005-07-08 06:53:19Mon, 21 Jul 2008 12:19:24 -0700Mar 8, 198410/03/201408-29-041447158214
1987-09-16T02:54:29-07:001982-08-28 04:58:50Tue, 15 Dec 1992 21:20:20 -0800Sep 7, 199826/06/198407-25-841167747504
2001-09-18T03:43:28-07:002011-07-29 19:00:15Sat, 05 Nov 2005 20:41:02 -0800Mar 5, 201706/08/200001-30-10907753976
1982-10-07T08:29:34-07:002017-01-01 09:17:12Fri, 04 Dec 1998 22:59:19 -0800Nov 21, 201710/10/200111-20-021087680650
1989-11-19T07:23:39-08:002017-12-04 23:21:41Thu, 09 Apr 1987 23:05:11 -0700Dec 19, 199009/01/201711-15-891007550561
2007-01-19T10:24:33-08:002007-09-12 18:37:35Fri, 15 Jan 1988 08:01:15 -0800Dec 11, 199217/12/198105-15-841247997030
1981-03-04T02:11:30-08:002018-07-06 01:00:20Fri, 10 Sep 1982 23:29:16 -0700Jul 23, 200516/04/201708-03-82412241977
1987-04-04T03:55:06-08:002000-06-05 23:08:42Wed, 16 Mar 2011 06:28:51 -0700Sep 8, 201326/07/199507-16-901528912284
2007-07-01T17:54:15-07:001982-04-30 02:46:18Tue, 04 Jan 2000 16:39:06 -0800Jul 14, 200506/10/201602-18-111102235287
1985-10-25T00:44:19-07:002012-06-02 13:39:51Sat, 08 Feb 2003 16:25:09 -0800Oct 4, 198804/10/199912-09-931170775456
1984-05-29T14:29:13-07:002006-08-16 01:33:19Mon, 09 Apr 2001 12:57:28 -0700Nov 8, 200725/05/200709-27-041178915549
2012-06-19T02:43:23-07:002004-11-19 19:28:01Wed, 26 Jan 2011 09:25:55 -0800Jun 27, 198215/09/200212-22-15424763754
1992-08-30T02:31:43-07:002011-12-04 16:58:24Mon, 12 Apr 1999 12:08:21 -0700May 23, 199809/09/198902-09-011063333412
2017-03-03T11:09:22-08:002017-04-26 06:56:32Wed, 05 Feb 2014 18:06:44 -0800Jan 29, 199711/05/198204-22-141494069173
2009-07-06T14:51:37-07:002005-07-31 14:00:30Sun, 05 May 1985 02:19:30 -0700Sep 20, 198614/11/201510-16-82425494316
2010-06-05T02:22:52-07:001983-11-26 00:30:41Mon, 08 Feb 2010 00:13:50 -0800Aug 6, 201703/09/198207-24-831132474872
2003-12-11T01:02:09-08:002004-04-17 17:22:59Tue, 29 Jul 2008 18:35:35 -0700Sep 17, 201011/07/198303-27-92683336914
1992-09-07T19:05:17-07:002004-06-14 21:40:06Sat, 01 Feb 1986 06:43:34 -0800Oct 21, 200203/12/199201-22-09491024628
1985-04-12T06:55:47-08:001995-10-28 19:25:40Mon, 29 Sep 1986 10:29:15 -0700Aug 22, 201323/05/198703-30-101488163985
1996-02-08T00:41:25-08:001981-10-23 05:37:50Mon, 14 Nov 2005 04:01:01 -0800Jul 14, 200627/11/201106-18-00790020757
1982-11-10T01:01:25-08:002001-05-13 08:39:35Wed, 12 Mar 2003 17:07:44 -0800May 26, 198313/09/201104-22-11846212303
2016-09-15T21:43:58-07:001987-07-26 19:05:46Thu, 26 May 1983 04:02:54 -0700Mar 17, 201221/10/200208-13-04485633547
1983-08-26T03:18:00-07:002005-11-07 22:50:23Mon, 02 Aug 1999 18:13:42 -0700Oct 28, 200113/08/200907-16-851059643859
1986-05-01T01:58:05-07:001998-07-15 17:40:44Sat, 18 Sep 2010 05:07:33 -0700Oct 2, 200102/07/199312-22-011370732530
1984-10-16T12:35:13-07:002012-07-27 09:20:43Wed, 22 Oct 1980 01:17:26 -0700Dec 25, 199305/04/199902-03-061019935495
1992-08-17T07:18:25-07:001997-12-17 06:44:41Tue, 03 Apr 1990 16:40:26 -0700Mar 10, 200518/02/198102-01-11826578686
2017-12-20T15:20:50-08:002012-01-16 10:09:24Sat, 26 Aug 2000 03:38:49 -0700May 1, 201211/03/200601-02-13847762413
1997-01-17T22:35:53-08:001983-01-12 02:54:15Sat, 12 Jan 1985 15:22:12 -0800Oct 25, 200604/08/198207-03-891322895415
1988-02-17T16:49:39-08:001983-09-24 11:23:10Wed, 30 Dec 1992 09:46:28 -0800Jan 21, 198304/11/199705-13-03474793582
2013-05-29T15:54:03-07:001984-08-23 12:04:35Thu, 19 Apr 1984 13:50:07 -0800Mar 14, 200004/08/198207-25-121129290297
2005-02-04T06:43:42-08:001994-07-03 04:41:15Sat, 12 Feb 1994 23:52:24 -0800Aug 29, 198808/08/200302-04-991066277541
1994-04-27T07:34:29-07:002016-04-24 11:38:30Mon, 24 Nov 1986 11:38:00 -0800Mar 20, 201806/07/200607-27-051085900004
2009-10-17T15:44:07-07:001985-05-11 01:15:50Thu, 16 Aug 1990 01:30:02 -0700Jan 9, 198329/02/199206-10-85718192382
1990-10-23T01:21:06-07:002018-01-15 00:49:17Sun, 31 May 1981 18:43:13 -0700Jan 6, 200331/01/199611-06-881192114903
2000-02-08T17:44:10-08:001991-06-26 11:33:19Fri, 20 Feb 1987 21:27:10 -0800Oct 20, 198904/12/201308-15-84822820973
1983-10-12T23:57:34-07:001981-10-18 00:03:12Mon, 14 Feb 2005 05:32:45 -0800Jan 31, 198919/05/201604-20-06587460720
1999-12-15T17:31:49-08:002014-07-01 12:04:56Thu, 31 Mar 2011 20:16:22 -0700Sep 15, 199212/05/200604-01-17609833456
1986-12-31T10:34:22-08:002001-10-04 06:18:49Fri, 06 Apr 2018 05:11:27 -0700Jul 1, 199412/09/200607-09-011316507465
1989-12-21T20:58:50-08:002015-05-26 10:58:21Thu, 26 Mar 2015 21:13:34 -0700Oct 16, 198612/02/199809-17-841183738788
934 | 935 | 936 | 937 | 938 | 939 | 956 | 957 | 958 | -------------------------------------------------------------------------------- /src/datatable.js: -------------------------------------------------------------------------------- 1 | import {Rows} from "./rows" 2 | import {Columns} from "./columns" 3 | import {dataToTable} from "./table" 4 | import {defaultConfig} from "./config" 5 | import { 6 | isObject, 7 | isJson, 8 | createElement, 9 | flush, 10 | button, 11 | truncate 12 | } from "./helpers" 13 | 14 | 15 | export class DataTable { 16 | constructor(table, options = {}) { 17 | this.initialized = false 18 | 19 | // user options 20 | this.options = { 21 | ...defaultConfig, 22 | ...options, 23 | layout: { 24 | ...defaultConfig.layout, 25 | ...options.layout 26 | }, 27 | labels: { 28 | ...defaultConfig.labels, 29 | ...options.labels 30 | } 31 | } 32 | 33 | if (typeof table === "string") { 34 | table = document.querySelector(table) 35 | } 36 | 37 | this.initialLayout = table.innerHTML 38 | this.initialSortable = this.options.sortable 39 | 40 | // Disable manual sorting if no header is present (#4) 41 | if (!this.options.header) { 42 | this.options.sortable = false 43 | } 44 | 45 | if (table.tHead === null) { 46 | if (!this.options.data || 47 | (this.options.data && !this.options.data.headings) 48 | ) { 49 | this.options.sortable = false 50 | } 51 | } 52 | 53 | if (table.tBodies.length && !table.tBodies[0].rows.length) { 54 | if (this.options.data) { 55 | if (!this.options.data.data) { 56 | throw new Error( 57 | "You seem to be using the data option, but you've not defined any rows." 58 | ) 59 | } 60 | } 61 | } 62 | 63 | this.table = table 64 | 65 | this.init() 66 | } 67 | 68 | /** 69 | * Add custom property or method to extend DataTable 70 | * @param {String} prop - Method name or property 71 | * @param {Mixed} val - Function or property value 72 | * @return {Void} 73 | */ 74 | static extend(prop, val) { 75 | if (typeof val === "function") { 76 | DataTable.prototype[prop] = val 77 | } else { 78 | DataTable[prop] = val 79 | } 80 | } 81 | 82 | /** 83 | * Initialize the instance 84 | * @param {Object} options 85 | * @return {Void} 86 | */ 87 | init(options) { 88 | if (this.initialized || this.table.classList.contains("dataTable-table")) { 89 | return false 90 | } 91 | 92 | Object.assign(this.options, options || {}) 93 | 94 | this.currentPage = 1 95 | this.onFirstPage = true 96 | 97 | this.hiddenColumns = [] 98 | this.columnRenderers = [] 99 | this.selectedColumns = [] 100 | 101 | this.render() 102 | 103 | setTimeout(() => { 104 | this.emit("datatable.init") 105 | this.initialized = true 106 | 107 | if (this.options.plugins) { 108 | Object.entries(this.options.plugins).forEach(([plugin, options]) => { 109 | if (this[plugin] && typeof this[plugin] === "function") { 110 | this[plugin] = this[plugin](options, {createElement}) 111 | 112 | // Init plugin 113 | if (options.enabled && this[plugin].init && typeof this[plugin].init === "function") { 114 | this[plugin].init() 115 | } 116 | } 117 | }) 118 | } 119 | }, 10) 120 | } 121 | 122 | /** 123 | * Render the instance 124 | * @param {String} type 125 | * @return {Void} 126 | */ 127 | render(type) { 128 | if (type) { 129 | switch (type) { 130 | case "page": 131 | this.renderPage() 132 | break 133 | case "pager": 134 | this.renderPager() 135 | break 136 | case "header": 137 | this.renderHeader() 138 | break 139 | } 140 | 141 | return false 142 | } 143 | 144 | const options = this.options 145 | let template = "" 146 | 147 | // Convert data to HTML 148 | if (options.data) { 149 | dataToTable.call(this) 150 | } 151 | 152 | if (options.ajax) { 153 | const ajax = options.ajax 154 | const xhr = new XMLHttpRequest() 155 | 156 | const xhrProgress = e => { 157 | this.emit("datatable.ajax.progress", e, xhr) 158 | } 159 | 160 | const xhrLoad = e => { 161 | if (xhr.readyState === 4) { 162 | this.emit("datatable.ajax.loaded", e, xhr) 163 | 164 | if (xhr.status === 200) { 165 | const obj = {} 166 | obj.data = ajax.load ? ajax.load.call(this, xhr) : xhr.responseText 167 | 168 | obj.type = "json" 169 | 170 | if (ajax.content && ajax.content.type) { 171 | obj.type = ajax.content.type 172 | 173 | Object.assign(obj, ajax.content) 174 | } 175 | 176 | this.import(obj) 177 | 178 | this.setColumns(true) 179 | 180 | this.emit("datatable.ajax.success", e, xhr) 181 | } else { 182 | this.emit("datatable.ajax.error", e, xhr) 183 | } 184 | } 185 | } 186 | 187 | const xhrFailed = e => { 188 | this.emit("datatable.ajax.error", e, xhr) 189 | } 190 | 191 | const xhrCancelled = e => { 192 | this.emit("datatable.ajax.abort", e, xhr) 193 | } 194 | 195 | xhr.addEventListener("progress", xhrProgress, false) 196 | xhr.addEventListener("load", xhrLoad, false) 197 | xhr.addEventListener("error", xhrFailed, false) 198 | xhr.addEventListener("abort", xhrCancelled, false) 199 | 200 | this.emit("datatable.ajax.loading", xhr) 201 | 202 | xhr.open("GET", typeof ajax === "string" ? options.ajax : options.ajax.url) 203 | xhr.send() 204 | } 205 | 206 | // Store references 207 | this.body = this.table.tBodies[0] 208 | this.head = this.table.tHead 209 | this.foot = this.table.tFoot 210 | 211 | if (!this.body) { 212 | this.body = createElement("tbody") 213 | 214 | this.table.appendChild(this.body) 215 | } 216 | 217 | this.hasRows = this.body.rows.length > 0 218 | 219 | // Make a tHead if there isn't one (fixes #8) 220 | if (!this.head) { 221 | const h = createElement("thead") 222 | const t = createElement("tr") 223 | 224 | if (this.hasRows) { 225 | Array.from(this.body.rows[0].cells).forEach(() => { 226 | t.appendChild(createElement("th")) 227 | }) 228 | 229 | h.appendChild(t) 230 | } 231 | 232 | this.head = h 233 | 234 | this.table.insertBefore(this.head, this.body) 235 | 236 | this.hiddenHeader = !options.ajax 237 | } 238 | 239 | this.headings = [] 240 | this.hasHeadings = this.head.rows.length > 0 241 | 242 | if (this.hasHeadings) { 243 | this.header = this.head.rows[0] 244 | this.headings = [].slice.call(this.header.cells) 245 | } 246 | 247 | // Header 248 | if (!options.header) { 249 | if (this.head) { 250 | this.table.removeChild(this.table.tHead) 251 | } 252 | } 253 | 254 | // Footer 255 | if (options.footer) { 256 | if (this.head && !this.foot) { 257 | this.foot = createElement("tfoot", { 258 | html: this.head.innerHTML 259 | }) 260 | this.table.appendChild(this.foot) 261 | } 262 | } else { 263 | if (this.foot) { 264 | this.table.removeChild(this.table.tFoot) 265 | } 266 | } 267 | 268 | // Build 269 | this.wrapper = createElement("div", { 270 | class: "dataTable-wrapper dataTable-loading" 271 | }) 272 | 273 | // Template for custom layouts 274 | template += "
" 275 | template += options.layout.top 276 | template += "
" 277 | if (options.scrollY.length) { 278 | template += `
` 279 | } else { 280 | template += "
" 281 | } 282 | template += "
" 283 | template += options.layout.bottom 284 | template += "
" 285 | 286 | // Info placement 287 | template = template.replace("{info}", options.paging ? "
" : "") 288 | 289 | // Per Page Select 290 | if (options.paging && options.perPageSelect) { 291 | let wrap = "
" 294 | 295 | // Create the select 296 | const select = createElement("select", { 297 | class: "dataTable-selector" 298 | }) 299 | 300 | // Create the options 301 | options.perPageSelect.forEach(val => { 302 | const selected = val === options.perPage 303 | const option = new Option(val, val, selected, selected) 304 | select.add(option) 305 | }) 306 | 307 | // Custom label 308 | wrap = wrap.replace("{select}", select.outerHTML) 309 | 310 | // Selector placement 311 | template = template.replace("{select}", wrap) 312 | } else { 313 | template = template.replace("{select}", "") 314 | } 315 | 316 | // Searchable 317 | if (options.searchable) { 318 | const form = 319 | `` 320 | 321 | // Search input placement 322 | template = template.replace("{search}", form) 323 | } else { 324 | template = template.replace("{search}", "") 325 | } 326 | 327 | if (this.hasHeadings) { 328 | // Sortable 329 | this.render("header") 330 | } 331 | 332 | // Add table class 333 | this.table.classList.add("dataTable-table") 334 | 335 | // Paginator 336 | const paginatorWrapper = createElement("nav", { 337 | class: "dataTable-pagination" 338 | }) 339 | const paginator = createElement("ul", { 340 | class: "dataTable-pagination-list" 341 | }) 342 | paginatorWrapper.appendChild(paginator) 343 | 344 | // Pager(s) placement 345 | template = template.replace(/\{pager\}/g, paginatorWrapper.outerHTML) 346 | this.wrapper.innerHTML = template 347 | 348 | this.container = this.wrapper.querySelector(".dataTable-container") 349 | 350 | this.pagers = this.wrapper.querySelectorAll(".dataTable-pagination-list") 351 | 352 | this.label = this.wrapper.querySelector(".dataTable-info") 353 | 354 | // Insert in to DOM tree 355 | this.table.parentNode.replaceChild(this.wrapper, this.table) 356 | this.container.appendChild(this.table) 357 | 358 | // Store the table dimensions 359 | this.rect = this.table.getBoundingClientRect() 360 | 361 | // Convert rows to array for processing 362 | this.data = Array.from(this.body.rows) 363 | this.activeRows = this.data.slice() 364 | this.activeHeadings = this.headings.slice() 365 | 366 | // Update 367 | this.update() 368 | 369 | if (!options.ajax) { 370 | this.setColumns() 371 | } 372 | 373 | // Fix height 374 | this.fixHeight() 375 | 376 | // Fix columns 377 | this.fixColumns() 378 | 379 | // Class names 380 | if (!options.header) { 381 | this.wrapper.classList.add("no-header") 382 | } 383 | 384 | if (!options.footer) { 385 | this.wrapper.classList.add("no-footer") 386 | } 387 | 388 | if (options.sortable) { 389 | this.wrapper.classList.add("sortable") 390 | } 391 | 392 | if (options.searchable) { 393 | this.wrapper.classList.add("searchable") 394 | } 395 | 396 | if (options.fixedHeight) { 397 | this.wrapper.classList.add("fixed-height") 398 | } 399 | 400 | if (options.fixedColumns) { 401 | this.wrapper.classList.add("fixed-columns") 402 | } 403 | 404 | this.bindEvents() 405 | } 406 | 407 | /** 408 | * Render the page 409 | * @return {Void} 410 | */ 411 | renderPage() { 412 | if (this.hasHeadings) { 413 | flush(this.header) 414 | 415 | this.activeHeadings.forEach(th => this.header.appendChild(th)) 416 | } 417 | 418 | 419 | if (this.hasRows && this.totalPages) { 420 | if (this.currentPage > this.totalPages) { 421 | this.currentPage = 1 422 | } 423 | 424 | // Use a fragment to limit touching the DOM 425 | const index = this.currentPage - 1 426 | 427 | const frag = document.createDocumentFragment() 428 | this.pages[index].forEach(row => frag.appendChild(this.rows().render(row))) 429 | 430 | this.clear(frag) 431 | 432 | this.onFirstPage = this.currentPage === 1 433 | this.onLastPage = this.currentPage === this.lastPage 434 | } else { 435 | this.setMessage(this.options.labels.noRows) 436 | } 437 | 438 | // Update the info 439 | let current = 0 440 | 441 | let f = 0 442 | let t = 0 443 | let items 444 | 445 | if (this.totalPages) { 446 | current = this.currentPage - 1 447 | f = current * this.options.perPage 448 | t = f + this.pages[current].length 449 | f = f + 1 450 | items = this.searching ? this.searchData.length : this.data.length 451 | } 452 | 453 | if (this.label && this.options.labels.info.length) { 454 | // CUSTOM LABELS 455 | const string = this.options.labels.info 456 | .replace("{start}", f) 457 | .replace("{end}", t) 458 | .replace("{page}", this.currentPage) 459 | .replace("{pages}", this.totalPages) 460 | .replace("{rows}", items) 461 | 462 | this.label.innerHTML = items ? string : "" 463 | } 464 | 465 | if (this.currentPage == 1) { 466 | this.fixHeight() 467 | } 468 | } 469 | 470 | /** 471 | * Render the pager(s) 472 | * @return {Void} 473 | */ 474 | renderPager() { 475 | flush(this.pagers) 476 | 477 | if (this.totalPages > 1) { 478 | const c = "pager" 479 | const frag = document.createDocumentFragment() 480 | const prev = this.onFirstPage ? 1 : this.currentPage - 1 481 | const next = this.onLastPage ? this.totalPages : this.currentPage + 1 482 | 483 | // first button 484 | if (this.options.firstLast) { 485 | frag.appendChild(button(c, 1, this.options.firstText)) 486 | } 487 | 488 | // prev button 489 | if (this.options.nextPrev) { 490 | frag.appendChild(button(c, prev, this.options.prevText)) 491 | } 492 | 493 | let pager = this.links 494 | 495 | // truncate the links 496 | if (this.options.truncatePager) { 497 | pager = truncate( 498 | this.links, 499 | this.currentPage, 500 | this.pages.length, 501 | this.options.pagerDelta, 502 | this.options.ellipsisText 503 | ) 504 | } 505 | 506 | // active page link 507 | this.links[this.currentPage - 1].classList.add("active") 508 | 509 | // append the links 510 | pager.forEach(p => { 511 | p.classList.remove("active") 512 | frag.appendChild(p) 513 | }) 514 | 515 | this.links[this.currentPage - 1].classList.add("active") 516 | 517 | // next button 518 | if (this.options.nextPrev) { 519 | frag.appendChild(button(c, next, this.options.nextText)) 520 | } 521 | 522 | // first button 523 | if (this.options.firstLast) { 524 | frag.appendChild(button(c, this.totalPages, this.options.lastText)) 525 | } 526 | 527 | // We may have more than one pager 528 | this.pagers.forEach(pager => { 529 | pager.appendChild(frag.cloneNode(true)) 530 | }) 531 | } 532 | } 533 | 534 | /** 535 | * Render the header 536 | * @return {Void} 537 | */ 538 | renderHeader() { 539 | this.labels = [] 540 | 541 | if (this.headings && this.headings.length) { 542 | this.headings.forEach((th, i) => { 543 | 544 | this.labels[i] = th.textContent 545 | 546 | if (th.firstElementChild && th.firstElementChild.classList.contains("dataTable-sorter")) { 547 | th.innerHTML = th.firstElementChild.innerHTML 548 | } 549 | 550 | th.sortable = th.getAttribute("data-sortable") !== "false" 551 | 552 | th.originalCellIndex = i 553 | if (this.options.sortable && th.sortable) { 554 | const link = createElement("a", { 555 | href: "#", 556 | class: "dataTable-sorter", 557 | html: th.innerHTML 558 | }) 559 | 560 | th.innerHTML = "" 561 | th.setAttribute("data-sortable", "") 562 | th.appendChild(link) 563 | } 564 | }) 565 | } 566 | 567 | this.fixColumns() 568 | } 569 | 570 | /** 571 | * Bind event listeners 572 | * @return {[type]} [description] 573 | */ 574 | bindEvents() { 575 | const options = this.options 576 | // Per page selector 577 | if (options.perPageSelect) { 578 | const selector = this.wrapper.querySelector(".dataTable-selector") 579 | if (selector) { 580 | // Change per page 581 | selector.addEventListener("change", () => { 582 | options.perPage = parseInt(selector.value, 10) 583 | this.update() 584 | 585 | this.fixHeight() 586 | 587 | this.emit("datatable.perpage", options.perPage) 588 | }, false) 589 | } 590 | } 591 | 592 | // Search input 593 | if (options.searchable) { 594 | this.input = this.wrapper.querySelector(".dataTable-input") 595 | if (this.input) { 596 | this.input.addEventListener("keyup", () => this.search(this.input.value), false) 597 | } 598 | } 599 | 600 | // Pager(s) / sorting 601 | this.wrapper.addEventListener("click", e => { 602 | const t = e.target.closest('a') 603 | if (t && (t.nodeName.toLowerCase() === "a")) { 604 | if (t.hasAttribute("data-page")) { 605 | this.page(t.getAttribute("data-page")) 606 | e.preventDefault() 607 | } else if ( 608 | options.sortable && 609 | t.classList.contains("dataTable-sorter") && 610 | t.parentNode.getAttribute("data-sortable") != "false" 611 | ) { 612 | this.columns().sort(this.headings.indexOf(t.parentNode)) 613 | e.preventDefault() 614 | } 615 | } 616 | }, false) 617 | 618 | window.addEventListener("resize", () => { 619 | this.rect = this.container.getBoundingClientRect() 620 | this.fixColumns() 621 | }) 622 | } 623 | 624 | /** 625 | * Set up columns 626 | * @return {[type]} [description] 627 | */ 628 | setColumns(ajax) { 629 | 630 | if (!ajax) { 631 | this.data.forEach(row => { 632 | Array.from(row.cells).forEach(cell => { 633 | cell.data = cell.innerHTML 634 | }) 635 | }) 636 | } 637 | 638 | // Check for the columns option 639 | if (this.options.columns && this.headings.length) { 640 | 641 | this.options.columns.forEach(data => { 642 | 643 | // convert single column selection to array 644 | if (!Array.isArray(data.select)) { 645 | data.select = [data.select] 646 | } 647 | 648 | if (data.hasOwnProperty("render") && typeof data.render === "function") { 649 | this.selectedColumns = this.selectedColumns.concat(data.select) 650 | 651 | this.columnRenderers.push({ 652 | columns: data.select, 653 | renderer: data.render 654 | }) 655 | } 656 | 657 | // Add the data attributes to the th elements 658 | data.select.forEach(column => { 659 | const th = this.headings[column] 660 | if (data.type) { 661 | th.setAttribute("data-type", data.type) 662 | } 663 | if (data.format) { 664 | th.setAttribute("data-format", data.format) 665 | } 666 | if (data.hasOwnProperty("sortable")) { 667 | th.setAttribute("data-sortable", data.sortable) 668 | } 669 | 670 | if (data.hasOwnProperty("hidden")) { 671 | if (data.hidden !== false) { 672 | this.columns().hide([column]) 673 | } 674 | } 675 | 676 | if (data.hasOwnProperty("sort") && data.select.length === 1) { 677 | this.columns().sort(data.select[0], data.sort, true) 678 | } 679 | }) 680 | }) 681 | } 682 | 683 | if (this.hasRows) { 684 | this.data.forEach((row, i) => { 685 | row.dataIndex = i 686 | Array.from(row.cells).forEach(cell => { 687 | cell.data = cell.innerHTML 688 | }) 689 | }) 690 | 691 | if (this.selectedColumns.length) { 692 | this.data.forEach(row => { 693 | Array.from(row.cells).forEach((cell, i) => { 694 | if (this.selectedColumns.includes(i)) { 695 | this.columnRenderers.forEach(options => { 696 | if (options.columns.includes(i)) { 697 | cell.innerHTML = options.renderer.call(this, cell.data, cell, row) 698 | } 699 | }) 700 | } 701 | }) 702 | }) 703 | } 704 | 705 | this.columns().rebuild() 706 | } 707 | 708 | this.render("header") 709 | } 710 | 711 | /** 712 | * Destroy the instance 713 | * @return {void} 714 | */ 715 | destroy() { 716 | this.table.innerHTML = this.initialLayout 717 | 718 | // Remove the className 719 | this.table.classList.remove("dataTable-table") 720 | 721 | // Remove the containers 722 | this.wrapper.parentNode.replaceChild(this.table, this.wrapper) 723 | 724 | this.initialized = false 725 | } 726 | 727 | /** 728 | * Update the instance 729 | * @return {Void} 730 | */ 731 | update() { 732 | this.wrapper.classList.remove("dataTable-empty") 733 | 734 | this.paginate(this) 735 | this.render("page") 736 | 737 | this.links = [] 738 | 739 | let i = this.pages.length 740 | while (i--) { 741 | const num = i + 1 742 | this.links[i] = button(i === 0 ? "active" : "", num, num) 743 | } 744 | 745 | this.sorting = false 746 | 747 | this.render("pager") 748 | 749 | this.rows().update() 750 | 751 | this.emit("datatable.update") 752 | } 753 | 754 | /** 755 | * Sort rows into pages 756 | * @return {Number} 757 | */ 758 | paginate() { 759 | const perPage = this.options.perPage 760 | let rows = this.activeRows 761 | 762 | if (this.searching) { 763 | rows = [] 764 | 765 | this.searchData.forEach(index => rows.push(this.activeRows[index])) 766 | } 767 | 768 | if (this.options.paging) { 769 | // Check for hidden columns 770 | this.pages = rows 771 | .map((tr, i) => i % perPage === 0 ? rows.slice(i, i + perPage) : null) 772 | .filter(page => page) 773 | } else { 774 | this.pages = [rows] 775 | } 776 | 777 | this.totalPages = this.lastPage = this.pages.length 778 | 779 | return this.totalPages 780 | } 781 | 782 | /** 783 | * Fix column widths 784 | * @return {Void} 785 | */ 786 | fixColumns() { 787 | 788 | if ((this.options.scrollY.length || this.options.fixedColumns) && this.activeHeadings && this.activeHeadings.length) { 789 | let cells 790 | let hd = false 791 | this.columnWidths = [] 792 | 793 | // If we have headings we need only set the widths on them 794 | // otherwise we need a temp header and the widths need applying to all cells 795 | if (this.table.tHead) { 796 | 797 | if (this.options.scrollY.length) { 798 | hd = createElement("thead") 799 | hd.appendChild(createElement("tr")) 800 | hd.style.height = '0px' 801 | if (this.headerTable) { 802 | // move real header back into place 803 | this.table.tHead = this.headerTable.tHead 804 | } 805 | } 806 | 807 | // Reset widths 808 | this.activeHeadings.forEach(cell => { 809 | cell.style.width = "" 810 | }) 811 | 812 | this.activeHeadings.forEach((cell, i) => { 813 | const ow = cell.offsetWidth 814 | const w = ow / this.rect.width * 100 815 | cell.style.width = `${w}%` 816 | this.columnWidths[i] = ow 817 | if (this.options.scrollY.length) { 818 | const th = createElement("th") 819 | hd.firstElementChild.appendChild(th) 820 | th.style.width = `${w}%` 821 | th.style.paddingTop = "0" 822 | th.style.paddingBottom = "0" 823 | th.style.border = "0" 824 | } 825 | }) 826 | 827 | if (this.options.scrollY.length) { 828 | const container = this.table.parentElement 829 | if (!this.headerTable) { 830 | this.headerTable = createElement("table", { 831 | class: "dataTable-table" 832 | }) 833 | const headercontainer = createElement("div", { 834 | class: "dataTable-headercontainer" 835 | }) 836 | headercontainer.appendChild(this.headerTable) 837 | container.parentElement.insertBefore(headercontainer, container) 838 | } 839 | const thd = this.table.tHead 840 | this.table.replaceChild(hd, thd) 841 | this.headerTable.tHead = thd 842 | 843 | // Compensate for scrollbars. 844 | this.headerTable.parentElement.style.paddingRight = `${ 845 | this.headerTable.clientWidth - 846 | this.table.clientWidth + 847 | parseInt( 848 | this.headerTable.parentElement.style.paddingRight || 849 | '0', 850 | 10 851 | ) 852 | }px` 853 | 854 | if (container.scrollHeight > container.clientHeight) { 855 | // scrollbars on one page means scrollbars on all pages. 856 | container.style.overflowY = 'scroll' 857 | } 858 | } 859 | 860 | } else { 861 | cells = [] 862 | 863 | // Make temperary headings 864 | hd = createElement("thead") 865 | const r = createElement("tr") 866 | Array.from(this.table.tBodies[0].rows[0].cells).forEach(() => { 867 | const th = createElement("th") 868 | r.appendChild(th) 869 | cells.push(th) 870 | }) 871 | 872 | hd.appendChild(r) 873 | this.table.insertBefore(hd, this.body) 874 | 875 | const widths = [] 876 | cells.forEach((cell, i) => { 877 | const ow = cell.offsetWidth 878 | const w = ow / this.rect.width * 100 879 | widths.push(w) 880 | this.columnWidths[i] = ow 881 | }) 882 | 883 | this.data.forEach(row => { 884 | Array.from(row.cells).forEach((cell, i) => { 885 | if (this.columns(cell.cellIndex).visible()) 886 | cell.style.width = `${widths[i]}%` 887 | }) 888 | }) 889 | 890 | // Discard the temp header 891 | this.table.removeChild(hd) 892 | } 893 | } 894 | } 895 | 896 | /** 897 | * Fix the container height 898 | * @return {Void} 899 | */ 900 | fixHeight() { 901 | if (this.options.fixedHeight) { 902 | this.container.style.height = null 903 | this.rect = this.container.getBoundingClientRect() 904 | this.container.style.height = `${this.rect.height}px` 905 | } 906 | } 907 | 908 | /** 909 | * Perform a search of the data set 910 | * @param {string} query 911 | * @return {void} 912 | */ 913 | search(query) { 914 | if (!this.hasRows) return false 915 | 916 | query = query.toLowerCase() 917 | 918 | this.currentPage = 1 919 | this.searching = true 920 | this.searchData = [] 921 | 922 | if (!query.length) { 923 | this.searching = false 924 | this.update() 925 | this.emit("datatable.search", query, this.searchData) 926 | this.wrapper.classList.remove("search-results") 927 | return false 928 | } 929 | 930 | this.clear() 931 | 932 | this.data.forEach((row, idx) => { 933 | const inArray = this.searchData.includes(row) 934 | 935 | // https://github.com/Mobius1/Vanilla-DataTables/issues/12 936 | const doesQueryMatch = query.split(" ").reduce((bool, word) => { 937 | let includes = false 938 | let cell = null 939 | let content = null 940 | 941 | for (let x = 0; x < row.cells.length; x++) { 942 | cell = row.cells[x] 943 | content = cell.hasAttribute('data-content') ? cell.getAttribute('data-content') : cell.textContent 944 | 945 | if ( 946 | content.toLowerCase().includes(word) && 947 | this.columns(cell.cellIndex).visible() 948 | ) { 949 | includes = true 950 | break 951 | } 952 | } 953 | 954 | return bool && includes 955 | }, true) 956 | 957 | if (doesQueryMatch && !inArray) { 958 | row.searchIndex = idx 959 | this.searchData.push(idx) 960 | } else { 961 | row.searchIndex = null 962 | } 963 | }) 964 | 965 | this.wrapper.classList.add("search-results") 966 | 967 | if (!this.searchData.length) { 968 | this.wrapper.classList.remove("search-results") 969 | 970 | this.setMessage(this.options.labels.noRows) 971 | } else { 972 | this.update() 973 | } 974 | 975 | this.emit("datatable.search", query, this.searchData) 976 | } 977 | 978 | /** 979 | * Change page 980 | * @param {int} page 981 | * @return {void} 982 | */ 983 | page(page) { 984 | // We don't want to load the current page again. 985 | if (page == this.currentPage) { 986 | return false 987 | } 988 | 989 | if (!isNaN(page)) { 990 | this.currentPage = parseInt(page, 10) 991 | } 992 | 993 | if (page > this.pages.length || page < 0) { 994 | return false 995 | } 996 | 997 | this.render("page") 998 | this.render("pager") 999 | 1000 | this.emit("datatable.page", page) 1001 | } 1002 | 1003 | /** 1004 | * Sort by column 1005 | * @param {int} column - The column no. 1006 | * @param {string} direction - asc or desc 1007 | * @return {void} 1008 | */ 1009 | sortColumn(column, direction) { 1010 | // Use columns API until sortColumn method is removed 1011 | this.columns().sort(column, direction) 1012 | } 1013 | 1014 | /** 1015 | * Add new row data 1016 | * @param {object} data 1017 | */ 1018 | insert(data) { 1019 | let rows = [] 1020 | if (isObject(data)) { 1021 | if (data.headings) { 1022 | if (!this.hasHeadings && !this.hasRows) { 1023 | const tr = createElement("tr") 1024 | data.headings.forEach(heading => { 1025 | const th = createElement("th", { 1026 | html: heading 1027 | }) 1028 | 1029 | tr.appendChild(th) 1030 | }) 1031 | this.head.appendChild(tr) 1032 | 1033 | this.header = tr 1034 | this.headings = [].slice.call(tr.cells) 1035 | this.hasHeadings = true 1036 | 1037 | // Re-enable sorting if it was disabled due 1038 | // to missing header 1039 | this.options.sortable = this.initialSortable 1040 | 1041 | // Allow sorting on new header 1042 | this.render("header") 1043 | 1044 | // Activate newly added headings 1045 | this.activeHeadings = this.headings.slice() 1046 | } 1047 | } 1048 | 1049 | if (data.data && Array.isArray(data.data)) { 1050 | rows = data.data 1051 | } 1052 | } else if (Array.isArray(data)) { 1053 | data.forEach(row => { 1054 | const r = [] 1055 | Object.entries(row).forEach(([heading, cell]) => { 1056 | 1057 | const index = this.labels.indexOf(heading) 1058 | 1059 | if (index > -1) { 1060 | r[index] = cell 1061 | } 1062 | }) 1063 | rows.push(r) 1064 | }) 1065 | } 1066 | 1067 | if (rows.length) { 1068 | this.rows().add(rows) 1069 | 1070 | this.hasRows = true 1071 | } 1072 | 1073 | this.update() 1074 | this.setColumns() 1075 | this.fixColumns() 1076 | } 1077 | 1078 | /** 1079 | * Refresh the instance 1080 | * @return {void} 1081 | */ 1082 | refresh() { 1083 | if (this.options.searchable) { 1084 | this.input.value = "" 1085 | this.searching = false 1086 | } 1087 | this.currentPage = 1 1088 | this.onFirstPage = true 1089 | this.update() 1090 | 1091 | this.emit("datatable.refresh") 1092 | } 1093 | 1094 | /** 1095 | * Truncate the table 1096 | * @param {mixes} html - HTML string or HTMLElement 1097 | * @return {void} 1098 | */ 1099 | clear(html) { 1100 | if (this.body) { 1101 | flush(this.body) 1102 | } 1103 | 1104 | let parent = this.body 1105 | if (!this.body) { 1106 | parent = this.table 1107 | } 1108 | 1109 | if (html) { 1110 | if (typeof html === "string") { 1111 | const frag = document.createDocumentFragment() 1112 | frag.innerHTML = html 1113 | } 1114 | 1115 | parent.appendChild(html) 1116 | } 1117 | } 1118 | 1119 | /** 1120 | * Export table to various formats (csv, txt or sql) 1121 | * @param {Object} userOptions User options 1122 | * @return {Boolean} 1123 | */ 1124 | export(userOptions) { 1125 | if (!this.hasHeadings && !this.hasRows) return false 1126 | 1127 | const headers = this.activeHeadings 1128 | let rows = [] 1129 | const arr = [] 1130 | let i 1131 | let x 1132 | let str 1133 | let link 1134 | 1135 | const defaults = { 1136 | download: true, 1137 | skipColumn: [], 1138 | 1139 | // csv 1140 | lineDelimiter: "\n", 1141 | columnDelimiter: ",", 1142 | 1143 | // sql 1144 | tableName: "myTable", 1145 | 1146 | // json 1147 | replacer: null, 1148 | space: 4 1149 | } 1150 | 1151 | // Check for the options object 1152 | if (!isObject(userOptions)) { 1153 | return false 1154 | } 1155 | 1156 | const options = { 1157 | ...defaults, 1158 | ...userOptions 1159 | } 1160 | 1161 | if (options.type) { 1162 | if (options.type === "txt" || options.type === "csv") { 1163 | // Include headings 1164 | rows[0] = this.header 1165 | } 1166 | 1167 | // Selection or whole table 1168 | if (options.selection) { 1169 | // Page number 1170 | if (!isNaN(options.selection)) { 1171 | rows = rows.concat(this.pages[options.selection - 1]) 1172 | } else if (Array.isArray(options.selection)) { 1173 | // Array of page numbers 1174 | for (i = 0; i < options.selection.length; i++) { 1175 | rows = rows.concat(this.pages[options.selection[i] - 1]) 1176 | } 1177 | } 1178 | } else { 1179 | rows = rows.concat(this.activeRows) 1180 | } 1181 | 1182 | // Only proceed if we have data 1183 | if (rows.length) { 1184 | if (options.type === "txt" || options.type === "csv") { 1185 | str = "" 1186 | 1187 | for (i = 0; i < rows.length; i++) { 1188 | for (x = 0; x < rows[i].cells.length; x++) { 1189 | // Check for column skip and visibility 1190 | if ( 1191 | !options.skipColumn.includes(headers[x].originalCellIndex) && 1192 | this.columns(headers[x].originalCellIndex).visible() 1193 | ) { 1194 | let text = rows[i].cells[x].textContent 1195 | text = text.trim() 1196 | text = text.replace(/\s{2,}/g, ' ') 1197 | text = text.replace(/\n/g, ' ') 1198 | text = text.replace(/"/g, '""') 1199 | //have to manually encode "#" as encodeURI leaves it as is. 1200 | text = text.replace(/#/g, "%23") 1201 | if (text.includes(",")) 1202 | text = `"${text}"` 1203 | 1204 | 1205 | str += text + options.columnDelimiter 1206 | } 1207 | } 1208 | // Remove trailing column delimiter 1209 | str = str.trim().substring(0, str.length - 1) 1210 | 1211 | // Apply line delimiter 1212 | str += options.lineDelimiter 1213 | } 1214 | 1215 | // Remove trailing line delimiter 1216 | str = str.trim().substring(0, str.length - 1) 1217 | 1218 | if (options.download) { 1219 | str = `data:text/csv;charset=utf-8,${str}` 1220 | } 1221 | } else if (options.type === "sql") { 1222 | // Begin INSERT statement 1223 | str = `INSERT INTO \`${options.tableName}\` (` 1224 | 1225 | // Convert table headings to column names 1226 | for (i = 0; i < headers.length; i++) { 1227 | // Check for column skip and column visibility 1228 | if ( 1229 | !options.skipColumn.includes(headers[i].originalCellIndex) && 1230 | this.columns(headers[i].originalCellIndex).visible() 1231 | ) { 1232 | str += `\`${headers[i].textContent}\`,` 1233 | } 1234 | } 1235 | 1236 | // Remove trailing comma 1237 | str = str.trim().substring(0, str.length - 1) 1238 | 1239 | // Begin VALUES 1240 | str += ") VALUES " 1241 | 1242 | // Iterate rows and convert cell data to column values 1243 | for (i = 0; i < rows.length; i++) { 1244 | str += "(" 1245 | 1246 | for (x = 0; x < rows[i].cells.length; x++) { 1247 | // Check for column skip and column visibility 1248 | if ( 1249 | !options.skipColumn.includes(headers[x].originalCellIndex) && 1250 | this.columns(headers[x].originalCellIndex).visible() 1251 | ) { 1252 | str += `"${rows[i].cells[x].textContent}",` 1253 | } 1254 | } 1255 | 1256 | // Remove trailing comma 1257 | str = str.trim().substring(0, str.length - 1) 1258 | 1259 | // end VALUES 1260 | str += ")," 1261 | } 1262 | 1263 | // Remove trailing comma 1264 | str = str.trim().substring(0, str.length - 1) 1265 | 1266 | // Add trailing colon 1267 | str += ";" 1268 | 1269 | if (options.download) { 1270 | str = `data:application/sql;charset=utf-8,${str}` 1271 | } 1272 | } else if (options.type === "json") { 1273 | // Iterate rows 1274 | for (x = 0; x < rows.length; x++) { 1275 | arr[x] = arr[x] || {} 1276 | // Iterate columns 1277 | for (i = 0; i < headers.length; i++) { 1278 | // Check for column skip and column visibility 1279 | if ( 1280 | !options.skipColumn.includes(headers[i].originalCellIndex) && 1281 | this.columns(headers[i].originalCellIndex).visible() 1282 | ) { 1283 | arr[x][headers[i].textContent] = rows[x].cells[i].textContent 1284 | } 1285 | } 1286 | } 1287 | 1288 | // Convert the array of objects to JSON string 1289 | str = JSON.stringify(arr, options.replacer, options.space) 1290 | 1291 | if (options.download) { 1292 | str = `data:application/json;charset=utf-8,${str}` 1293 | } 1294 | } 1295 | 1296 | // Download 1297 | if (options.download) { 1298 | // Filename 1299 | options.filename = options.filename || "datatable_export" 1300 | options.filename += `.${options.type}` 1301 | 1302 | str = encodeURI(str) 1303 | 1304 | // Create a link to trigger the download 1305 | link = document.createElement("a") 1306 | link.href = str 1307 | link.download = options.filename 1308 | 1309 | // Append the link 1310 | document.body.appendChild(link) 1311 | 1312 | // Trigger the download 1313 | link.click() 1314 | 1315 | // Remove the link 1316 | document.body.removeChild(link) 1317 | } 1318 | 1319 | return str 1320 | } 1321 | } 1322 | 1323 | return false 1324 | } 1325 | 1326 | /** 1327 | * Import data to the table 1328 | * @param {Object} userOptions User options 1329 | * @return {Boolean} 1330 | */ 1331 | import(userOptions) { 1332 | let obj = false 1333 | const defaults = { 1334 | // csv 1335 | lineDelimiter: "\n", 1336 | columnDelimiter: "," 1337 | } 1338 | 1339 | // Check for the options object 1340 | if (!isObject(userOptions)) { 1341 | return false 1342 | } 1343 | 1344 | const options = { 1345 | ...defaults, 1346 | ...userOptions 1347 | } 1348 | 1349 | if (options.data.length || isObject(options.data)) { 1350 | // Import CSV 1351 | if (options.type === "csv") { 1352 | obj = { 1353 | data: [] 1354 | } 1355 | 1356 | // Split the string into rows 1357 | const rows = options.data.split(options.lineDelimiter) 1358 | 1359 | if (rows.length) { 1360 | 1361 | if (options.headings) { 1362 | obj.headings = rows[0].split(options.columnDelimiter) 1363 | 1364 | rows.shift() 1365 | } 1366 | 1367 | rows.forEach((row, i) => { 1368 | obj.data[i] = [] 1369 | 1370 | // Split the rows into values 1371 | const values = row.split(options.columnDelimiter) 1372 | 1373 | if (values.length) { 1374 | values.forEach(value => { 1375 | obj.data[i].push(value) 1376 | }) 1377 | } 1378 | }) 1379 | } 1380 | } else if (options.type === "json") { 1381 | const json = isJson(options.data) 1382 | 1383 | // Valid JSON string 1384 | if (json) { 1385 | obj = { 1386 | headings: [], 1387 | data: [] 1388 | } 1389 | 1390 | json.forEach((data, i) => { 1391 | obj.data[i] = [] 1392 | Object.entries(data).forEach(([column, value]) => { 1393 | if (!obj.headings.includes(column)) { 1394 | obj.headings.push(column) 1395 | } 1396 | 1397 | obj.data[i].push(value) 1398 | }) 1399 | }) 1400 | } else { 1401 | // console.warn("That's not valid JSON!") 1402 | } 1403 | } 1404 | 1405 | if (isObject(options.data)) { 1406 | obj = options.data 1407 | } 1408 | 1409 | if (obj) { 1410 | // Add the rows 1411 | this.insert(obj) 1412 | } 1413 | } 1414 | 1415 | return false 1416 | } 1417 | 1418 | /** 1419 | * Print the table 1420 | * @return {void} 1421 | */ 1422 | print() { 1423 | const headings = this.activeHeadings 1424 | const rows = this.activeRows 1425 | const table = createElement("table") 1426 | const thead = createElement("thead") 1427 | const tbody = createElement("tbody") 1428 | 1429 | const tr = createElement("tr") 1430 | headings.forEach(th => { 1431 | tr.appendChild( 1432 | createElement("th", { 1433 | html: th.textContent 1434 | }) 1435 | ) 1436 | }) 1437 | 1438 | thead.appendChild(tr) 1439 | 1440 | rows.forEach(row => { 1441 | const tr = createElement("tr") 1442 | Array.from(row.cells).forEach(cell => { 1443 | tr.appendChild( 1444 | createElement("td", { 1445 | html: cell.textContent 1446 | }) 1447 | ) 1448 | }) 1449 | tbody.appendChild(tr) 1450 | }) 1451 | 1452 | table.appendChild(thead) 1453 | table.appendChild(tbody) 1454 | 1455 | // Open new window 1456 | const w = window.open() 1457 | 1458 | // Append the table to the body 1459 | w.document.body.appendChild(table) 1460 | 1461 | // Print 1462 | w.print() 1463 | } 1464 | 1465 | /** 1466 | * Show a message in the table 1467 | * @param {string} message 1468 | */ 1469 | setMessage(message) { 1470 | let colspan = 1 1471 | 1472 | if (this.hasRows) { 1473 | colspan = this.data[0].cells.length 1474 | } else if (this.activeHeadings.length) { 1475 | colspan = this.activeHeadings.length 1476 | } 1477 | 1478 | this.wrapper.classList.add("dataTable-empty") 1479 | 1480 | if (this.label) { 1481 | this.label.innerHTML = "" 1482 | } 1483 | this.totalPages = 0 1484 | this.render("pager") 1485 | 1486 | this.clear( 1487 | createElement("tr", { 1488 | html: `${message}` 1489 | }) 1490 | ) 1491 | } 1492 | 1493 | /** 1494 | * Columns API access 1495 | * @return {Object} new Columns instance 1496 | */ 1497 | columns(columns) { 1498 | return new Columns(this, columns) 1499 | } 1500 | 1501 | /** 1502 | * Rows API access 1503 | * @return {Object} new Rows instance 1504 | */ 1505 | rows(rows) { 1506 | return new Rows(this, rows) 1507 | } 1508 | 1509 | /** 1510 | * Add custom event listener 1511 | * @param {String} event 1512 | * @param {Function} callback 1513 | * @return {Void} 1514 | */ 1515 | on(event, callback) { 1516 | this.events = this.events || {} 1517 | this.events[event] = this.events[event] || [] 1518 | this.events[event].push(callback) 1519 | } 1520 | 1521 | /** 1522 | * Remove custom event listener 1523 | * @param {String} event 1524 | * @param {Function} callback 1525 | * @return {Void} 1526 | */ 1527 | off(event, callback) { 1528 | this.events = this.events || {} 1529 | if (event in this.events === false) return 1530 | this.events[event].splice(this.events[event].indexOf(callback), 1) 1531 | } 1532 | 1533 | /** 1534 | * Fire custom event 1535 | * @param {String} event 1536 | * @return {Void} 1537 | */ 1538 | emit(event) { 1539 | this.events = this.events || {} 1540 | if (event in this.events === false) return 1541 | for (let i = 0; i < this.events[event].length; i++) { 1542 | this.events[event][i].apply(this, Array.prototype.slice.call(arguments, 1)) 1543 | } 1544 | } 1545 | } 1546 | --------------------------------------------------------------------------------