datatable: major reword, add lazyscroll mode - jscancer - Javascript crap (rela… | |
git clone git://git.codemadness.org/jscancer | |
Log | |
Files | |
Refs | |
README | |
LICENSE | |
--- | |
commit 44c57648b8d5f8ecc939bec21affe6fb7c1fec62 | |
parent 2967ba6942ad55041437a01f8603cc43e5e91c0a | |
Author: Hiltjo Posthuma <[email protected]> | |
Date: Sat, 28 May 2016 20:44:49 +0200 | |
datatable: major reword, add lazyscroll mode | |
however this is now broken and needs more work. | |
Diffstat: | |
M datatable/README | 22 +++++++++++----------- | |
M datatable/TODO | 29 ++++++++++++++++++++++++++++- | |
A datatable/datatable.css | 99 +++++++++++++++++++++++++++++… | |
A datatable/datatable.js | 287 +++++++++++++++++++++++++++++… | |
M datatable/example-ajax.html | 1 + | |
M datatable/example.html | 16 ++++------------ | |
D datatable/style.css | 45 -----------------------------… | |
D datatable/tbl.js | 183 -----------------------------… | |
8 files changed, 430 insertions(+), 252 deletions(-) | |
--- | |
diff --git a/datatable/README b/datatable/README | |
@@ -1,15 +1,15 @@ | |
jsdatatable | |
=========== | |
-small Javascript datatable script. | |
+small datatable script. | |
FEATURES | |
-------- | |
- Small: | |
- - Filesize: +- 5.5KB, +- 3.5KB minified, < 1337 bytes GZIP'd. | |
- - Lines: +- 185, not much code, so hopefully easy to understand. | |
+ - Filesize: +- 9KB. | |
+ - Lines: +- 285, not much code, so hopefully easy to understand. | |
- No dependencies on other libraries like jQuery. | |
- Sorting on columns, multi-column support with shift-click. | |
- Filtering values: case-insensitively, tokenized (separated by space). | |
@@ -18,9 +18,13 @@ FEATURES | |
responsive for big datasets. | |
- Permissive ISC license, see LICENSE file, feel free to contact me for | |
questions or other terms. | |
-- No support for legacy browsers, officially supported are: | |
+- "Lazy scroll" mode: | |
+ - fixed column headers and renders only visible rows, this allows you to | |
+ "lazily" render millions of rows. | |
+- Officially supported browsers are: | |
- Firefox and Firefox ESR. | |
- Chrome and most recent webkit-based browsers. | |
+ - IE8+ (use compat.js). | |
USAGE | |
@@ -49,7 +53,7 @@ minimal code needed for a working datatable: | |
<tr><td>b</td></tr> | |
</tbody> | |
</table> | |
-<script type="text/javascript" src="tbl.js"></script> | |
+<script type="text/javascript" src="datatable.js"></script> | |
<script type="text/javascript">var datatables = datatable_autoload();</script> | |
</body> | |
</html> | |
@@ -118,8 +122,8 @@ date/datetimes: | |
use Zulu times, like: "2016-01-01T01:02:03Z" or other | |
time strings that are parsable as the data-value attribute. | |
icons: | |
- generally use data-value attribute with integer, set data-parse | |
- column to "int". | |
+ generally use data-value attribute with integer as weight value to sort | |
+ on, set data-parse column to "int". | |
DYNAMICALLY UPDATE DATA | |
@@ -135,10 +139,6 @@ A date, integer, float or other values must be able to par… | |
the parse function returns NaN, null or undefined etc. the sorting behaviour | |
is also undefined. It is recommended to always set a zero value for each type. | |
-Rendering tables is generally slow in all browsers (2000+ rows) due to | |
-recalculating styles and slow HTML/XML parsing. Please contact me if you know | |
-a solution for this! | |
- | |
Author | |
------ | |
diff --git a/datatable/TODO b/datatable/TODO | |
@@ -1,4 +1,31 @@ | |
-- when filtering data, then use sort, then remove the filter the data is unsor… | |
+TODO: | |
+ | |
+- AJAX example: | |
+ - allow simple load from AJAX, make example-ajax.html. | |
+ - ajax example use createTextNode (much faster than setting textContent). | |
+- in lazyload render: | |
+ - scrolling is broken at the end? | |
+ ? use documentfragment to insert rows, should not matter much performa… | |
+ - use childNodes, not children? NOTE: may contain textNode? | |
+- lazyload: broken on Firefox ESR 45.1.1 | |
+- lazyload: render function: set height each time only when nrows changed, it … | |
+- lazyload: render function: nrows can be < 0 if data.length = 0 ? might not b… | |
+x lazyload: clicking on column doesn't toggle class. | |
+? lazyload: onclick handler: reuse code. | |
+x lazyload: data-sortable="false" is ignored on column. | |
+x filter: on Chrome/IE clearing input (oninput event?) reset the filtering. | |
+- increase IE dependency to 9? | |
+? fix margin-right offset for scrollbar when table does not fit on screen (win… | |
+ - calculate margin-right in javascript (in case of weird-sized scrollbars). | |
+- keep lazyload code modular (don't interweave with datatables code), should b… | |
+ to remove. | |
+? in IE atleast: table can be sorted before the table is fully parsed which re… | |
+ in weirdly sorted data, not sure. | |
+x IE: Array.from not supported. | |
+ | |
+- datatable_data_parse: use childNodes? (faster). | |
+ | |
+- bug: when filtering data, then use sort, then remove the filter the data is … | |
- optimize table rendering, row creation etc somehow. | |
- update classlist separately (sort-asc, sort-desc, sort-disabled), prevent … | |
diff --git a/datatable/datatable.css b/datatable/datatable.css | |
@@ -0,0 +1,99 @@ | |
+table.datatable { | |
+ table-layout: fixed; | |
+ border-collapse: collapse; | |
+} | |
+ | |
+table.datatable tr td, | |
+table.datatable tr th { | |
+ border: 1px solid #aaa; | |
+ padding: 0 16px 0 3px; | |
+ /* TODO: add line-height: 25px; */ | |
+} | |
+ | |
+table.datatable thead td, | |
+table.datatable thead th { | |
+ background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAA… | |
+ background-position: right center; | |
+ background-repeat: no-repeat; | |
+ cursor: pointer; | |
+} | |
+ | |
+table.datatable thead tr td:hover, | |
+table.datatable thead tr th:hover { | |
+ background-color: #ccc; | |
+} | |
+ | |
+table.datatable thead .sort-disabled { | |
+ background: none; | |
+ cursor: default; | |
+} | |
+ | |
+table.datatable thead .sort-asc { | |
+ background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAA… | |
+} | |
+ | |
+table.datatable thead .sort-desc { | |
+ background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAA… | |
+} | |
+ | |
+table.datatable tbody tr:nth-child(2n+1) td { | |
+ background-color: #f9f9f9; | |
+} | |
+ | |
+table.datatable tbody tr:hover td { | |
+ background-color: #d6f0ff; | |
+} | |
+ | |
+/* datatable lazy scroll styles below */ | |
+.datatable-lazyscroll-container { | |
+ overflow: hidden; | |
+ position: relative; | |
+} | |
+ | |
+.datatable-lazyscroll-container table { | |
+ table-layout: fixed; | |
+ border-collapse: collapse; | |
+ width: 1px; | |
+ height: 1px; | |
+} | |
+ | |
+.datatable-lazyscroll-container th { | |
+ width: 200px; | |
+} | |
+ | |
+.datatable-lazyscroll-headers { | |
+ overflow: hidden; | |
+ position: relative; | |
+ margin-right: 17px; /* fix scrollbar */ | |
+} | |
+ | |
+.datatable-lazyscroll-headers th { | |
+ height: 25px !important; | |
+ text-align: left; | |
+} | |
+ | |
+.datatable-lazyscroll-body td { | |
+ overflow: hidden !important; | |
+ line-height: 25px !important; | |
+ margin: 0 !important; | |
+ padding-top: 0 !important; | |
+ padding-bottom: 0 !important; | |
+} | |
+ | |
+.datatable-lazyscroll-body { | |
+ overflow-y: scroll; | |
+ overflow-x: auto; | |
+ position: relative; | |
+} | |
+ | |
+.datatable-lazyscroll-container { | |
+ height: 608px; | |
+} | |
+ | |
+.datatable-lazyscroll-body { | |
+ height: 580px; | |
+} | |
+ | |
+.datatable-lazyscroll-headers th { | |
+ border-bottom: 0 !important; | |
+} | |
diff --git a/datatable/datatable.js b/datatable/datatable.js | |
@@ -0,0 +1,286 @@ | |
+var datatable_parse_date = Date.parse, | |
+ datatable_parse_float = parseFloat, | |
+ datatable_parse_int = parseInt, | |
+ datatable_parse_string = String; | |
+ | |
+function datatable_sort_default(x, y) { | |
+ return x > y ? 1 : (x == y ? 0 : -1); | |
+} | |
+ | |
+function datatable_init(el) { | |
+ var thead = el.getElementsByTagName("thead"); | |
+ if (!thead.length) | |
+ return null; | |
+ var tbody = el.getElementsByTagName("tbody"); | |
+ if (!tbody.length) | |
+ return null; | |
+ var ths = thead[0].children[0].children; | |
+ if (!ths.length) | |
+ return null; | |
+ var cols = []; | |
+ for (var i = 0; i < ths.length; i++) | |
+ cols.push({ | |
+ filterable: ["1", "true"].indexOf(ths[i].getAttribute(… | |
+ parsefn: window["datatable_parse_" + (ths[i].getAtt… | |
+ sortfn: window["datatable_sort_" + (ths[i].getAttr… | |
+ sortable: ["1", "true"].indexOf(ths[i].getAttribute(… | |
+ }); | |
+ var d = { | |
+ table: el, | |
+ thead: thead[0], | |
+ ths: ths, | |
+ tbody: tbody[0], | |
+ cols: cols, | |
+ sort: [], /* sort options: [colidx, order (ASC = 0, DESC = 1)… | |
+ lazyscroll: ["1", "true"].indexOf(el.getAttribute("data-lazysc… | |
+ }; | |
+ d.data_raw = d.data = datatable_data_parse(d); | |
+ | |
+ if (d.lazyscroll) { | |
+ var bodytable = document.createElement("table"); | |
+ bodytable.className = el.className; | |
+ bodytable.setAttribute("cellspacing", "0"); | |
+ bodytable.setAttribute("cellpadding", "0"); | |
+ bodytable.setAttribute("border", "0"); | |
+ | |
+ var tr = document.createElement("tr"); | |
+ for (var i = 0; i < ths.length; i++) { | |
+ var th = ths[i].cloneNode(true); | |
+ th.innerHTML = ""; | |
+ tr.appendChild(th); | |
+ } | |
+ var bodythead = document.createElement("thead"); | |
+ bodythead.appendChild(tr); | |
+ | |
+ tr = document.createElement("tr"); | |
+ var newths = []; | |
+ for (var i = 0; i < ths.length; i++) | |
+ newths.push(tr.appendChild(ths[i].cloneNode(true))); | |
+ d.ths = newths; // set new columns (for sorting etc).. | |
+ var elthead = document.createElement("thead"); | |
+ elthead.appendChild(tr); | |
+ | |
+ var headerstable = document.createElement("table"); | |
+ headerstable.setAttribute("cellspacing", "0"); | |
+ headerstable.setAttribute("cellpadding", "0"); | |
+ headerstable.setAttribute("border", "0"); | |
+ headerstable.className = el.className; | |
+ headerstable.appendChild(elthead); | |
+ | |
+ var headersel = document.createElement("div"); | |
+ headersel.className = "datatable-lazyscroll-headers"; | |
+ headersel.appendChild(headerstable); | |
+ | |
+ bodytable.appendChild(bodythead); | |
+ | |
+ var bodyel = document.createElement("div"); | |
+ bodyel.className = "datatable-lazyscroll-body"; | |
+ bodyel.appendChild(bodytable); | |
+ | |
+ var containerel = document.createElement("div"); | |
+ containerel.className = "datatable-lazyscroll-container"; | |
+ containerel.appendChild(headersel); | |
+ containerel.appendChild(bodyel); | |
+ | |
+ var bodytbody = bodytable.appendChild(document.createElement("… | |
+ var startfiller = bodytbody.appendChild(document.createElement… | |
+ var endfiller = bodytbody.appendChild(document.createElement("… | |
+ | |
+ el.parentNode.insertBefore(containerel, el); | |
+ | |
+ var rowheight = 25; | |
+ d.display = function(d, data) { | |
+ var nrows = data.length; | |
+ | |
+ bodytable.style.height = (nrows * rowheight) + "px"; | |
+ | |
+ var start = parseInt(bodyel.scrollTop / rowheight), | |
+ end = Math.min(parseInt((bodyel.scrollTop + bodyel… | |
+ | |
+ startfiller.style.height = (start * rowheight) + "px"; | |
+ endfiller.style.height = ((nrows - end - 1) * rowheigh… | |
+ | |
+ // remove nodes but keep first startfiller and endfill… | |
+ for (var c = bodytbody.childNodes; c.length > 2; ) | |
+ bodytbody.removeChild(startfiller.nextSibling); | |
+ | |
+ for (var i = start, prev = startfiller, p = bodytbody;… | |
+ prev = p.insertBefore(d.data[i].tr, prev.nextS… | |
+ }; | |
+ | |
+ var curscrollleft, curscrolltop, verticalscrolltimer; | |
+ bodyel.addEventListener("scroll", function() { | |
+ // handle left / right scroll | |
+ var scrolleft = bodyel.scrollLeft; | |
+ if (curscrollleft !== scrolleft) | |
+ headersel.scrollLeft = curscrollleft = scrolle… | |
+ // handle up/down scroll | |
+ var scrolltop = bodyel.scrollTop; | |
+ if (curscrolltop !== scrolltop) { | |
+ clearTimeout(verticalscrolltimer); | |
+ verticalscrolltimer = setTimeout(function() { | |
+ datatable_display(d, d.data); | |
+ }, 32); | |
+ curscrolltop = scrolltop; | |
+ } | |
+ }); | |
+ datatable_display(d, d.data); | |
+ } else { | |
+ d.display = function(d, data) { | |
+ var tbody = document.createElement("tbody"); | |
+ for (var i = 0; i < data.length; i++) | |
+ tbody.appendChild(data[i].tr); | |
+ d.table.replaceChild(tbody, d.tbody); | |
+ d.tbody = tbody; | |
+ }; | |
+ } | |
+ /* setup click event handlers for sorting. */ | |
+ for (var i = 0; i < d.ths.length; i++) | |
+ d.cols[i].sortable && d.ths[i].addEventListener("click", funct… | |
+ return function(e) { | |
+ /* shift-click for multi-select modifier. */ | |
+ datatable_sort_column_toggle(d, idx, e.shiftKe… | |
+ d.data = datatable_sort(d, d.data); | |
+ datatable_display(d, d.data); | |
+ }; | |
+ }(i), false); | |
+ return d; | |
+} | |
+ | |
+function datatable_display(d, data) { | |
+ return d.display(d, data); | |
+} | |
+ | |
+function datatable_sort_column_get(d, idx) { | |
+ for (var i = 0; i < d.sort.length; i++) | |
+ if (d.sort[i][0] == idx) | |
+ return i; | |
+ return -1; | |
+} | |
+ | |
+function datatable_sort_column_set(d, idx, order, multi) { | |
+ var c = datatable_sort_column_get(d, idx); | |
+ if (multi) | |
+ if (c != -1) | |
+ d.sort[c][1] = order; | |
+ else | |
+ d.sort.push([ idx, order ]); | |
+ else | |
+ d.sort = [ [idx, order] ]; | |
+ | |
+ for (var i = 0; i < d.ths.length; i++) { | |
+ var c = " " + d.ths[i].className + " "; | |
+ d.ths[i].className = c.replace(/ sort-(asc|desc) /g, " ").repl… | |
+ } | |
+ for (var i = 0; i < d.sort.length; i++) | |
+ d.ths[d.sort[i][0]].className += d.sort[i][1] ? " sort-desc" :… | |
+} | |
+ | |
+/* toggle sort or use default order: ASC. */ | |
+function datatable_sort_column_toggle(d, idx, multi) { | |
+ var c = datatable_sort_column_get(d, idx); | |
+ datatable_sort_column_set(d, idx, c == -1 || d.sort[c][1] ? 0 : 1, mul… | |
+} | |
+ | |
+function datatable_data_parse(d) { | |
+ var data = [], trs = d.tbody.children; | |
+ /* NOTE: assumes each tr has only "<td>" childnodes. */ | |
+ for (var i = 0; i < trs.length; i++) { | |
+ var values = [], fv = []; | |
+ for (var j = 0, trc = trs[i].children; j < trc.length; j++) { | |
+ var td = trc[j], v = td.getAttribute("data-value"); | |
+ /* prefer data-value attribute, else use cell contents, | |
+ * also set preprocess values to filter on cell content | |
+ * and data-value (lower-case string). */ | |
+ var s = (td.textContent || td.innerText).toLowerCase(); | |
+ if (typeof(v) != "undefined" && v !== null) { | |
+ fv.push([ v.toLowerCase(), s ]); | |
+ values.push(d.cols[j].parsefn(v)); | |
+ } else { | |
+ fv.push([ s ]); | |
+ values.push(d.cols[j].parsefn(s)); | |
+ } | |
+ } | |
+ data.push({ | |
+ filtervalues: fv, | |
+ tr: trs[i], | |
+ values: values | |
+ }); | |
+ } | |
+ return data; | |
+} | |
+ | |
+function datatable_sort(d, data) { | |
+ /* setup sort functions once (in order for multi-select). */ | |
+ var sortfns = d.sort.map(function(s) { | |
+ return (function(c, o, fn) { | |
+ if (o) | |
+ return function(xvals, yvals) { | |
+ return -fn(xvals[c], yvals[c]); | |
+ }; | |
+ else | |
+ return function(xvals, yvals) { | |
+ return fn(xvals[c], yvals[c]); | |
+ }; | |
+ })(s[0], s[1], d.cols[s[0]].sortfn); | |
+ }); | |
+ | |
+ return data.sort(function(x, y) { | |
+ for (var i = 0, r; i < sortfns.length; i++) | |
+ if ((r = sortfns[i](x.values, y.values)) != 0) | |
+ return r; | |
+ return r; | |
+ }); | |
+} | |
+ | |
+function datatable_filter(d, data, s) { | |
+ var ret = [], tok = s.toLowerCase().split(" "); | |
+ for (var i = 0; i < data.length; i++) { | |
+ var fc = 0; | |
+ for (var k = 0; k < tok.length && fc < tok.length; k++) { | |
+ var f = false; | |
+ for (var j = 0; j < data[i].values.length && fc < tok.… | |
+ for (var l = 0; l < data[i].filtervalues[j].le… | |
+ d.cols[j].filterable; l++) | |
+ if (data[i].filtervalues[j][l].indexOf… | |
+ f = true; | |
+ if (f) | |
+ fc++; | |
+ } | |
+ /* all tokens (separated by space) must match. */ | |
+ if (fc == tok.length) | |
+ ret.push(data[i]); | |
+ } | |
+ return ret; | |
+} | |
+ | |
+function datatable_filter_delayed(d, fn, e) { | |
+ clearTimeout(d.filter_timer); | |
+ d.filter_timer = setTimeout(function() { | |
+ fn(e); | |
+ }, 150); /* filter delay in ms. */ | |
+} | |
+ | |
+function datatable_autoload() { | |
+ // convert to Array (not changed in-place, mandatory). | |
+ var ds = [], dl = [], els = document.getElementsByClassName && documen… | |
+ for (var i = 0; i < els.length; i++) | |
+ dl.push(els[i]); | |
+ for (var i = 0, d; i < dl.length; i++) { | |
+ if ((d = datatable_init(dl[i])) === null) | |
+ continue; | |
+ var input = dl[i].parentNode.getElementsByClassName("filter-te… | |
+ /* delayed filtering. */ | |
+ for (var j = 0; j < input.length; j++) | |
+ input[j].addEventListener("input", (function(d, el) { | |
+ return function(e) { | |
+ datatable_filter_delayed(d, function()… | |
+ d.data = datatable_filter(d, d… | |
+ datatable_display(d, d.data); | |
+ }, e); | |
+ }; | |
+ })(d, input[j]), false); | |
+ ds.push(d); | |
+ } | |
+ return ds; | |
+} | |
+\ No newline at end of file | |
diff --git a/datatable/example-ajax.html b/datatable/example-ajax.html | |
@@ -34,6 +34,7 @@ | |
</tbody> | |
</table> | |
+<!--[if lte IE 8]><script type="text/javascript" src="../compat.js"></script><… | |
<script type="text/javascript" src="tbl.js"></script> | |
<script type="text/javascript"> | |
diff --git a/datatable/example.html b/datatable/example.html | |
@@ -3,22 +3,13 @@ | |
<head> | |
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> | |
<title>jsdatatable</title> | |
- <link rel="stylesheet" type="text/css" href="style.css" /> | |
- <style type="text/css"> | |
- table { | |
- table-layout: fixed; | |
- width: 800px; | |
- } | |
- table.datatable tr td input { | |
- width: 100%; | |
- } | |
- </style> | |
+ <link rel="stylesheet" type="text/css" href="datatable.css" /> | |
</head> | |
<body> | |
<input type="search" class="filter-text" value="" placeholder="Search..." auto… | |
-<table class="datatable"> | |
+<table class="datatable" data-lazyscroll="1" cellpadding="0" cellspacing="0" b… | |
<thead> | |
<tr> | |
<th data-parse="int">#</th> | |
@@ -69,7 +60,8 @@ | |
</tbody> | |
</table> | |
-<script type="text/javascript" src="tbl.js"></script> | |
+<!--[if lte IE 8]><script type="text/javascript" src="../compat.js"></script><… | |
+<script type="text/javascript" src="datatable.js"></script> | |
<script type="text/javascript">var datatables = datatable_autoload();</script> | |
</body> | |
diff --git a/datatable/style.css b/datatable/style.css | |
@@ -1,45 +0,0 @@ | |
-table.datatable { | |
- border-collapse: collapse; | |
-} | |
- | |
-table.datatable tr td, | |
-table.datatable tr th { | |
- border: 1px solid #aaa; | |
- padding: 3px; | |
-} | |
- | |
-table.datatable thead td, | |
-table.datatable thead th { | |
- background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAA… | |
- background-position: right center; | |
- background-repeat: no-repeat; | |
- cursor: pointer; | |
- padding-right: 16px; | |
-} | |
- | |
-table.datatable thead tr td:hover, | |
-table.datatable thead tr th:hover { | |
- background-color: #ccc; | |
-} | |
- | |
-table.datatable thead .sort-disabled { | |
- background: none; | |
- cursor: default; | |
- padding-right: inherit; | |
-} | |
- | |
-table.datatable thead .sort-asc { | |
- background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAA… | |
-} | |
- | |
-table.datatable thead .sort-desc { | |
- background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAA… | |
-} | |
- | |
-table.datatable tbody tr:nth-child(2n+1) td { | |
- background-color: #f9f9f9; | |
-} | |
- | |
-table.datatable tbody tr:hover td { | |
- background-color: #d6f0ff; | |
-} | |
diff --git a/datatable/tbl.js b/datatable/tbl.js | |
@@ -1,183 +0,0 @@ | |
-var datatable_parse_date = Date.parse, | |
- datatable_parse_float = parseFloat, | |
- datatable_parse_int = parseInt, | |
- datatable_parse_string = String; | |
- | |
-function datatable_sort_default(x, y) { | |
- return x > y ? 1 : (x == y ? 0 : -1); | |
-} | |
- | |
-function datatable_init(el) { | |
- var thead = el.getElementsByTagName("thead"); | |
- if (!thead.length) | |
- return null; | |
- var tbody = el.getElementsByTagName("tbody"); | |
- if (!tbody.length) | |
- return null; | |
- var ths = thead[0].children[0].children; | |
- if (!ths.length) | |
- return null; | |
- var cols = []; | |
- for (var i = 0; i < ths.length; i++) | |
- cols.push({ | |
- filterable: ["1", "true"].indexOf(ths[i].getAttribute(… | |
- parsefn: window["datatable_parse_" + (ths[i].getAtt… | |
- sortfn: window["datatable_sort_" + (ths[i].getAttr… | |
- sortable: ["1", "true"].indexOf(ths[i].getAttribute(… | |
- }); | |
- var d = { | |
- table: el, | |
- thead: thead[0], | |
- ths: ths, | |
- tbody: tbody[0], | |
- cols: cols, | |
- sort: [] /* sort options: [colidx, order (ASC = 0, DESC = 1)]… | |
- }; | |
- d.data_raw = d.data = datatable_data_parse(d); | |
- /* setup click event handlers for sorting. */ | |
- for (var i = 0; i < ths.length; i++) | |
- d.cols[i].sortable && (ths[i].onclick = function(idx) { | |
- return function(e) { | |
- /* shift-click for multi-select modifier. */ | |
- datatable_sort_column_toggle(d, idx, e.shiftKe… | |
- d.data = datatable_sort(d, d.data); | |
- datatable_display(d, d.data); | |
- }; | |
- }(i)); | |
- return d; | |
-} | |
- | |
-function datatable_sort_column_get(d, idx) { | |
- for (var i = 0; i < d.sort.length; i++) | |
- if (d.sort[i][0] == idx) | |
- return i; | |
- return -1; | |
-} | |
- | |
-function datatable_sort_column_set(d, idx, order, multi) { | |
- var c = datatable_sort_column_get(d, idx); | |
- if (multi) | |
- if (c != -1) | |
- d.sort[c][1] = order; | |
- else | |
- d.sort.push([ idx, order ]); | |
- else | |
- d.sort = [ [idx, order] ]; | |
- for (var i = 0; i < d.ths.length; i++) | |
- d.ths[i].classList.remove("sort-asc"), | |
- d.ths[i].classList.remove("sort-desc"); | |
- for (var i = 0; i < d.sort.length; i++) | |
- d.ths[d.sort[i][0]].classList.add(d.sort[i][1] ? "sort-desc" :… | |
-} | |
- | |
-/* toggle sort or use default order: ASC. */ | |
-function datatable_sort_column_toggle(d, idx, multi) { | |
- var c = datatable_sort_column_get(d, idx); | |
- datatable_sort_column_set(d, idx, c == -1 || d.sort[c][1] ? 0 : 1, mul… | |
-} | |
- | |
-function datatable_data_parse(d) { | |
- var data = [], trs = d.tbody.children; | |
- /* NOTE: assumes each tr has only "<td>" childnodes. */ | |
- for (var i = 0; i < trs.length; i++) { | |
- var values = [], fv = []; | |
- for (var j = 0, trc = trs[i].children; j < trc.length; j++) { | |
- var td = trc[j], v = td.getAttribute("data-value"); | |
- /* prefer data-value attribute, else use cell contents, | |
- * also set preprocess values to filter on cell content | |
- * and data-value (lower-case string). */ | |
- if (v !== null) { | |
- fv.push([ v.toLowerCase(), td.textContent.toLo… | |
- values.push(d.cols[j].parsefn(v)); | |
- } else { | |
- fv.push([ td.textContent.toLowerCase() ]); | |
- values.push(d.cols[j].parsefn(td.textContent)); | |
- } | |
- } | |
- data.push({ | |
- filtervalues: fv, | |
- tr: trs[i], | |
- values: values | |
- }); | |
- } | |
- return data; | |
-} | |
- | |
-function datatable_sort(d, data) { | |
- /* setup sort functions once (in order for multi-select). */ | |
- var sortfns = d.sort.map(function(s) { | |
- return (function(c, o, fn) { | |
- if (o) | |
- return function(xvals, yvals) { | |
- return -fn(xvals[c], yvals[c]); | |
- }; | |
- else | |
- return function(xvals, yvals) { | |
- return fn(xvals[c], yvals[c]); | |
- }; | |
- })(s[0], s[1], d.cols[s[0]].sortfn); | |
- }); | |
- return data.sort(function(x, y) { | |
- for (var i = 0, r; i < sortfns.length; i++) | |
- if ((r = sortfns[i](x.values, y.values)) != 0) | |
- return r; | |
- return r; | |
- }); | |
-} | |
- | |
-function datatable_display(d, data) { | |
- var tbody = document.createElement("tbody"); | |
- for (var i = 0; i < data.length; i++) | |
- tbody.appendChild(data[i].tr); | |
- d.table.replaceChild(tbody, d.tbody); | |
- d.tbody = tbody; | |
-} | |
- | |
-function datatable_filter(d, data, s) { | |
- var ret = [], tok = s.toLowerCase().split(" "); | |
- for (var i = 0; i < data.length; i++) { | |
- var fc = 0; | |
- for (var k = 0; k < tok.length && fc < tok.length; k++) { | |
- var f = false; | |
- for (var j = 0; j < data[i].values.length && fc < tok.… | |
- for (var l = 0; l < data[i].filtervalues[j].le… | |
- d.cols[j].filterable; l++) | |
- if (data[i].filtervalues[j][l].indexOf… | |
- f = true; | |
- if (f) | |
- fc++; | |
- } | |
- /* all tokens (separated by space) must match. */ | |
- if (fc == tok.length) | |
- ret.push(data[i]); | |
- } | |
- return ret; | |
-} | |
- | |
-function datatable_filter_delayed(d, fn, e) { | |
- clearTimeout(d.filter_timer); | |
- d.filter_timer = setTimeout(function() { | |
- fn(e); | |
- }, 150); /* filter delay in ms. */ | |
-} | |
- | |
-function datatable_autoload() { | |
- var dl = document.getElementsByClassName("datatable"), ds = []; | |
- for (var i = 0, d; i < dl.length; i++) { | |
- if ((d = datatable_init(dl[i])) === null) | |
- continue; | |
- var input = dl[i].parentNode.getElementsByClassName("filter-te… | |
- /* delayed filtering. */ | |
- for (var j = 0; j < input.length; j++) | |
- input[j].onkeyup = (function(d, el) { | |
- return function(ev) { | |
- datatable_filter_delayed(d, function()… | |
- d.data = datatable_filter(d, d… | |
- datatable_display(d, d.data); | |
- }, ev); | |
- }; | |
- })(d, input[j]); | |
- ds.push(d); | |
- } | |
- return ds; | |
-} |