diff --git a/bower.json b/bower.json index a6ab232..42adcac 100644 --- a/bower.json +++ b/bower.json @@ -1,6 +1,6 @@ { "name": "vanilla-datatables", - "version": "1.0.3", + "version": "1.0.4", "ignore": [ ".gitattributes", "README.md" diff --git a/package.json b/package.json index 5733e5c..225df7c 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "vanilla-datatables", - "version": "1.0.3", + "version": "1.0.4", "description": "A lightweight, dependency-free javascript HTML table plugin.", "main": "vanilla-dataTables.min.js", "scripts": { diff --git a/vanilla-dataTables.js b/vanilla-dataTables.js index 8e4a5f4..57e5058 100644 --- a/vanilla-dataTables.js +++ b/vanilla-dataTables.js @@ -4,10 +4,10 @@ * Copyright (c) 2015-2017 Karl Saunders (http://mobiuswebdesign.co.uk) * Licensed under MIT (http://www.opensource.org/licenses/mit-license.php) * - * Version: 1.0.3 + * Version: 1.0.4 * */ -(function (root, factory) { +(function(root, factory) { var plugin = 'DataTable'; if (typeof define === 'function' && define.amd) { @@ -17,7 +17,7 @@ } else { root[plugin] = factory(plugin); } -}(this, function (plugin) { +}(this, function(plugin) { 'use strict'; /* PRIVATE VARS */ @@ -32,7 +32,7 @@ each: function(a, b, c) { if ("[object Object]" === Object.prototype.toString.call(a)) { for (var d in a) { - if ( Object.prototype.hasOwnProperty.call(a, d) ) { + if (Object.prototype.hasOwnProperty.call(a, d)) { b.call(c, d, a[d], a); } } @@ -67,21 +67,34 @@ }, addClass: function(a, b) { if (!util.hasClass(a, b)) { - if (a.classList) { a.classList.add(b); } else { a.className = a.className.trim() + " " + b; } } + if (a.classList) { + a.classList.add(b); + } else { + a.className = a.className.trim() + " " + b; + } + } }, removeClass: function(a, b) { - if (util.hasClass(a, b)) { if (a.classList) { a.classList.remove(b); } else { a.className = a.className.replace(new RegExp("(^|\\s)" + b.split(" ").join("|") + "(\\s|$)", "gi"), " "); }} + if (util.hasClass(a, b)) { + if (a.classList) { + a.classList.remove(b); + } else { + a.className = a.className.replace(new RegExp("(^|\\s)" + b.split(" ").join("|") + "(\\s|$)", "gi"), " "); + } + } }, append: function(p, e) { return p && e && p.appendChild(e); }, listen: function(e, type, callback, scope) { - e.addEventListener(type, function(e) { callback.call(scope||this, e); }, false); + e.addEventListener(type, function(e) { + callback.call(scope || this, e); + }, false); }, getBoundingRect: function(el) { var win = window; - var doc = document; - var body = doc.body; + var doc = document; + var body = doc.body; var rect = el.getBoundingClientRect(); var offsetX = win.pageXOffset !== undefined ? win.pageXOffset : (doc.documentElement || body.parentNode || body).scrollLeft; var offsetY = win.pageYOffset !== undefined ? win.pageYOffset : (doc.documentElement || body.parentNode || body).scrollTop; @@ -89,10 +102,10 @@ return { bottom: rect.bottom + offsetY, height: rect.height, - left : rect.left + offsetX, - right : rect.right + offsetX, - top : rect.top + offsetY, - width : rect.width + left: rect.left + offsetX, + right: rect.right + offsetX, + top: rect.top + offsetY, + width: rect.width }; }, preventDefault: function(e) { @@ -101,43 +114,71 @@ return e.preventDefault(); } }, - includes: function(a,b) { + includes: function(a, b) { return a.indexOf(b) > -1; }, - button: function(c, p, t){ - return util.createElement('li', { class: c, html: ''+t+''}); + button: function(c, p, t) { + return util.createElement('li', { + class: c, + html: '' + t + '' + }); }, flush: function(el, ie) { - if (ie) { while (el.hasChildNodes()) { el.removeChild(el.firstChild); } } else { el.innerHTML = ''; } + if (ie) { + while (el.hasChildNodes()) { + el.removeChild(el.firstChild); + } + } else { + el.innerHTML = ''; + } } }; var build = function() { var _ = this; - if ( _.options.data ) { + if (_.options.data) { dataToTable.call(_); } _.tbody = _.table.tBodies[0]; - var _wrapper = util.createElement('div', { class: 'dataTable-wrapper'}); - _.container = util.createElement('div', { class: 'dataTable-container'}); - _.label = util.createElement('div', { class: 'dataTable-info' }); + var _wrapper = util.createElement('div', { + class: 'dataTable-wrapper' + }); + _.container = util.createElement('div', { + class: 'dataTable-container' + }); + _.label = util.createElement('div', { + class: 'dataTable-info' + }); - var top = util.createElement('div', { class: 'dataTable-top' }); - var bottom = util.createElement('div', { class: 'dataTable-bottom' }); + var top = util.createElement('div', { + class: 'dataTable-top' + }); + var bottom = util.createElement('div', { + class: 'dataTable-bottom' + }); // Per Page Select if (_.options.perPageSelect) { // Build the selector - var wrap = util.createElement('div', { class: 'dataTable-dropdown' }); - var label = util.createElement('label', { class: 'dataTable-selectWrapper' }); - var _ppSelector = util.createElement('select', { class: 'dataTable-selector'}); + var wrap = util.createElement('div', { + class: 'dataTable-dropdown' + }); + var label = util.createElement('label', { + class: 'dataTable-selectWrapper' + }); + var _ppSelector = util.createElement('select', { + class: 'dataTable-selector' + }); util.each(_.options.perPageSelect, function(i, val) { - util.append(_ppSelector, util.createElement('option', { value: val, html: val })); + util.append(_ppSelector, util.createElement('option', { + value: val, + html: val + })); }); _ppSelector.value = _.options.perPage; @@ -152,7 +193,7 @@ _.options.perPage = parseInt(this.value, 10); _.update(); - if ( _.options.fixedHeight ) { + if (_.options.fixedHeight) { fixHeight.call(_); } @@ -163,8 +204,14 @@ // Searchable if (_.options.searchable) { - var form = util.createElement('div', { class: 'dataTable-search' }); - _.input = util.createElement('input', { type: 'text', class: 'dataTable-input', placeholder: 'Search...'}); + var form = util.createElement('div', { + class: 'dataTable-search' + }); + _.input = util.createElement('input', { + type: 'text', + class: 'dataTable-input', + placeholder: 'Search...' + }); util.append(form, _.input); util.append(top, form); @@ -178,7 +225,11 @@ var cols = _.table.tHead.rows[0].cells; util.each(cols, function(i, head) { - var link = util.createElement('a', { href: '#', class: 'dataTable-sorter', html: head.innerHTML }); + var link = util.createElement('a', { + href: '#', + class: 'dataTable-sorter', + html: head.innerHTML + }); head.idx = i; head.innerHTML = ''; util.append(head, link); @@ -210,7 +261,9 @@ util.append(_wrapper, bottom); // Paginator - var w = util.createElement('div', { class: 'dataTable-pagination' }); + var w = util.createElement('div', { + class: 'dataTable-pagination' + }); _.paginator = util.createElement('ul'); util.append(w, _.paginator); util.append(bottom, w); @@ -247,13 +300,51 @@ /* Fixed column widths */ if (_.options.fixedColumns) { - var cells = _.table.tHead.rows[0].cells; + var cells, h = false; - util.each(cells, function(i, cell) { - var rect = util.getBoundingRect(cell); - var w = (rect.width / _.rect.width) * 100; - cell.style.width = w + '%'; - }); + // If we have a headings we need only set the widths on them + // otherwise we need a temp header and the widths need applying to all cells + if (_.table.tHead) { + cells = _.table.tHead.rows[0].cells; + + util.each(cells, function(i, cell) { + var rect = util.getBoundingRect(cell); + var w = (rect.width / _.rect.width) * 100; + cell.style.width = w + '%'; + }); + } else { + + cells = []; + + // Make temperary headings + h = util.createElement("thead"); + var r = util.createElement("tr"); + var c = _.table.tBodies[0].rows[0].cells; + util.each(c, function(i, row) { + var th = util.createElement("th"); + r.appendChild(th); + cells.push(th); + }); + + h.appendChild(r); + _.table.insertBefore(h, _.table.tBodies[0]); + + var widths = []; + util.each(cells, function(i, cell) { + var rect = util.getBoundingRect(cell); + var w = (rect.width / _.rect.width) * 100; + widths.push(w); + }); + + util.each(this.rows, function(idx, row) { + util.each(row.cells, function(i, cell) { + cell.style.width = widths[i] + "%"; + }); + }); + + // Discard the temp header + _.table.removeChild(h); + } util.addClass(_.table, 'fixed-columns'); } @@ -261,7 +352,8 @@ }; var paginate = function() { - var _ = this, perPage = _.options.perPage, + var _ = this, + perPage = _.options.perPage, rows = !!_.searching ? _.searchData : _.rows; _.pages = rows.map(function(tr, i) { @@ -276,16 +368,17 @@ var render = function() { var _ = this; - if ( _.totalPages ) { + if (_.totalPages) { - if ( _.currentPage > _.totalPages ) { + if (_.currentPage > _.totalPages) { _.currentPage = 1; } // Use a fragment to limit touching the DOM - var _index = _.currentPage - 1, frag = util.createFragment(); + var _index = _.currentPage - 1, + frag = util.createFragment(); - util.each(_.pages[_index], function (i, v) { + util.each(_.pages[_index], function(i, v) { util.append(frag, v); }); @@ -305,13 +398,16 @@ } // Update the info - var current = 0, f = 0, t = 0, items; + var current = 0, + f = 0, + t = 0, + items; - if ( _.totalPages ) { - current = _.currentPage-1; + if (_.totalPages) { + current = _.currentPage - 1; f = current * _.options.perPage; t = f + _.pages[current].length; - f = f+1; + f = f + 1; items = !!_.searching ? _.searchData.length : _.rows.length; } @@ -329,23 +425,27 @@ if (_.totalPages <= 1) return; - var c = 'pager', frag = util.createFragment(), + var c = 'pager', + frag = util.createFragment(), prev = _.onFirstPage ? 1 : _.currentPage - 1, next = _.onlastPage ? _.totalPages : _.currentPage + 1; // first button if (_.options.firstLast) { - util.append(frag, util.button(c, 1, _.options.firstText)); } + util.append(frag, util.button(c, 1, _.options.firstText)); + } // prev button if (_.options.nextPrev) { - util.append(frag, util.button(c, prev, _.options.prevText)); } + util.append(frag, util.button(c, prev, _.options.prevText)); + } var pager = _.links; // truncate the links if (_.options.truncatePager) { - pager = _truncate(_.links, _.currentPage, _.pages.length, _.options.pagerDelta); } + pager = _truncate(_.links, _.currentPage, _.pages.length, _.options.pagerDelta); + } // active page link util.addClass(_.links[_.currentPage - 1], 'active'); @@ -360,11 +460,13 @@ // next button if (_.options.nextPrev) { - util.append(frag, util.button(c, next, _.options.nextText)); } + util.append(frag, util.button(c, next, _.options.nextText)); + } // first button if (_.options.firstLast) { - util.append(frag, util.button(c, _.totalPages, _.options.lastText)); } + util.append(frag, util.button(c, _.totalPages, _.options.lastText)); + } // append the fragment util.append(_.paginator, frag); @@ -372,23 +474,27 @@ var sortItems = function(a, b) { var c, d; - if ( 1 === b ) { - c = 0; d = a.length; - } else { - if ( b === -1 ) { - c = a.length - 1; d = -1; - } - } + if (1 === b) { + c = 0; + d = a.length; + } else { + if (b === -1) { + c = a.length - 1; + d = -1; + } + } for (var e = !0; e;) { e = !1; - for (var f = c; f != d; f += b) { + for (var f = c; f != d; f += b) { if (a[f + b] && a[f].value > a[f + b].value) { var g = a[f], h = a[f + b], i = g; - a[f] = h; a[f + b] = i; e = !0; + a[f] = h; + a[f + b] = i; + e = !0; } - } + } } return a; }; @@ -406,11 +512,16 @@ g = b + d, h = [], i = []; - if ( b < 4 - d + e ) {g = 3 + e; } else if ( b > c - (3 - d + e)) { f = c - (2 + e); } + if (b < 4 - d + e) { + g = 3 + e; + } else if (b > c - (3 - d + e)) { + f = c - (2 + e); + } for (var k = 1; k <= c; k++) { if (1 == k || k == c || k >= f && k <= g) { var l = a[k - 1]; - util.removeClass(l, "active"); h.push(l); + util.removeClass(l, "active"); + h.push(l); } } util.each(h, function(b, c) { @@ -419,11 +530,15 @@ var e = j.children[0].getAttribute("data-page"); if (d - e == 2) i.push(a[e]); else if (d - e != 1) { - var f = util.createElement("li", { class: "ellipsis", html: '…' }); + var f = util.createElement("li", { + class: "ellipsis", + html: '…' + }); i.push(f); } } - i.push(c); j = c; + i.push(c); + j = c; }); return i; @@ -435,10 +550,10 @@ thead = false, tbody = false; - if ( data.headings ) { + if (data.headings) { thead = util.createElement('thead'); var tr = util.createElement('tr'); - util.each(data.headings, function(i,col) { + util.each(data.headings, function(i, col) { var td = util.createElement('th', { html: col }); @@ -448,11 +563,11 @@ thead.appendChild(tr); } - if ( data.rows ) { + if (data.rows) { tbody = util.createElement('tbody'); util.each(data.rows, function(i, rows) { - if ( data.headings ) { - if ( data.headings.length !== rows.length ) { + if (data.headings) { + if (data.headings.length !== rows.length) { throw new Error("The number of rows do not match the number of headings."); } } @@ -467,15 +582,15 @@ }); } - if ( thead ) { - if ( this.table.tHead !== null ) { + if (thead) { + if (this.table.tHead !== null) { this.table.removeChild(this.table.tHead); } util.append(this.table, thead); } - if ( tbody ) { - if ( this.table.tBodies.length ) { + if (tbody) { + if (this.table.tBodies.length) { this.table.removeChild(this.table.tBodies[0]); } util.append(this.table, tbody); @@ -485,31 +600,31 @@ // Emitter var Emitter = function() {}; Emitter.prototype = { - on: function(event, fct){ + on: function(event, fct) { this._events = this._events || {}; - this._events[event] = this._events[event] || []; + this._events[event] = this._events[event] || []; this._events[event].push(fct); }, - off: function(event, fct){ + off: function(event, fct) { this._events = this._events || {}; - if( event in this._events === false ) return; + if (event in this._events === false) return; this._events[event].splice(this._events[event].indexOf(fct), 1); }, - emit: function(event /* , args... */){ + emit: function(event /* , args... */ ) { this._events = this._events || {}; - if( event in this._events === false ) return; - for(var i = 0; i < this._events[event].length; i++){ + if (event in this._events === false) return; + for (var i = 0; i < this._events[event].length; i++) { this._events[event][i].apply(this, Array.prototype.slice.call(arguments, 1)); } } }; Emitter.mixin = function(obj) { - var props = ['on', 'off', 'emit']; - for(var i = 0; i < props.length; i ++){ - if( typeof obj === 'function' ){ - obj.prototype[props[i]] = Emitter.prototype[props[i]]; - }else{ + var props = ['on', 'off', 'emit']; + for (var i = 0; i < props.length; i++) { + if (typeof obj === 'function') { + obj.prototype[props[i]] = Emitter.prototype[props[i]]; + } else { obj[props[i]] = Emitter.prototype[props[i]]; } } @@ -520,7 +635,7 @@ // DATATABLE // //////////////// - function Plugin(table, options) { + function DataTable(table, options) { var _ = this; @@ -546,33 +661,33 @@ // Checks if (!table) { - throw new Error("The plugin requires a table as the first parameter"); } + throw new Error("The plugin requires a table as the first parameter"); + } if (typeof table === "string") { var selector = table; table = document.querySelector(table); - if ( !table ) { - throw new Error("The element '"+selector+"' can not be found."); + if (!table) { + throw new Error("The element '" + selector + "' can not be found."); } } - if (table.tagName.toLowerCase() != "table") { - throw new Error("The selected element is not a table."); } + if (table.tagName.toLowerCase() !== "table") { + throw new Error("The selected element is not a table."); + } - if (table.tHead === null && _.options.sortable) { - if ( _.options.data ) { - if ( !_.options.data.headings ) { - throw new Error("You seem to be using the data option, but you've not defined any headings."); + if (table.tHead === null ) { + if ( _.options.sortable ) { + if ( !_.options.data || (_.options.data && !_.options.data.headings) ) { + _.options.sortable = false; } - } else { - throw new Error("The sortable option requires table headings."); } } if (!table.tBodies.length) { - if ( _.options.data ) { - if ( !_.options.data.rows ) { + if (_.options.data) { + if (!_.options.data.rows) { throw new Error("You seem to be using the data option, but you've not defined any rows."); } } else { @@ -581,8 +696,8 @@ } if (table.tBodies.length && !table.tBodies[0].rows.length) { - if ( _.options.data ) { - if ( !_.options.data.rows ) { + if (_.options.data) { + if (!_.options.data.rows) { throw new Error("You seem to be using the data option, but you've not defined any rows."); } } else { @@ -605,7 +720,7 @@ }, 10); } - Plugin.prototype.update = function() { + DataTable.prototype.update = function() { var _ = this; paginate.call(_); @@ -614,7 +729,7 @@ _.links = []; var i = _.pages.length; - while(i--) { + while (i--) { var num = i + 1; _.links[i] = util.button((i === 0) ? 'active' : '', num, num); } @@ -624,7 +739,7 @@ _.emit('datatable.update'); }; - Plugin.prototype.search = function(query) { + DataTable.prototype.search = function(query) { var _ = this; query = query.toLowerCase(); @@ -633,7 +748,7 @@ _.searching = true; _.searchData = []; - if ( !query.length ) { + if (!query.length) { _.searching = false; _.update(); _.emit("datatable.search", query, _.searchData); @@ -645,13 +760,14 @@ util.each(_.rows, function(idx, row) { var inArray = util.includes(_.searchData, row); // Cheat and get the row's textContent instead of searching each cell :P - if ( util.includes(row.textContent.toLowerCase(), query) && !inArray) { + if (util.includes(row.textContent.toLowerCase(), query) && !inArray) { _.searchData.push(row); } }); - if ( !_.searchData.length ) { - _.setMessage('No rows found.'); } + if (!_.searchData.length) { + _.setMessage('No rows found.'); + } _.update(); @@ -659,18 +775,21 @@ }; /* Change the page. */ - Plugin.prototype.page = function(page) { + DataTable.prototype.page = function(page) { var _ = this; // We don't want to load the current page again. - if ( page == _.currentPage ) { - return; } + if (page == _.currentPage) { + return; + } - if ( !isNaN(page)) { - _.currentPage = parseInt(page, 10); } + if (!isNaN(page)) { + _.currentPage = parseInt(page, 10); + } - if ( page > _.pages.length || page < 0 ) { - return; } + if (page > _.pages.length || page < 0) { + return; + } render.call(_); renderPager.call(_); @@ -679,7 +798,8 @@ }; /* Perform the sorting */ - Plugin.prototype.sortColumn = function(column, direction) { + /* TODO: Allow method to work in the absence of headings */ + DataTable.prototype.sortColumn = function(column, direction) { var _ = this; var dir; var rows = !!_.searching ? _.searchData : _.rows; @@ -695,9 +815,15 @@ var num = content.replace(/(\$|\,|\s)/g, ""); if (parseFloat(num) == num) { - numeric[n++] = { value: Number(num), row: tr }; + numeric[n++] = { + value: Number(num), + row: tr + }; } else { - alpha[a++] = { value: content, row: tr }; + alpha[a++] = { + value: content, + row: tr + }; } }); @@ -705,11 +831,15 @@ /* Sort according to direction (ascending or descending) */ var top, btm; if (util.hasClass(th, "asc") || direction == "asc") { - top = sortItems(alpha, -1); btm = sortItems(numeric, -1); dir = 'descending'; + top = sortItems(alpha, -1); + btm = sortItems(numeric, -1); + dir = 'descending'; util.removeClass(th, 'asc'); util.addClass(th, 'desc'); } else { - top = sortItems(numeric, 1); btm = sortItems(alpha, 1); dir = 'ascending'; + top = sortItems(numeric, 1); + btm = sortItems(alpha, 1); + dir = 'ascending'; util.removeClass(th, 'desc'); util.addClass(th, 'asc'); } @@ -725,7 +855,7 @@ /* Reorder the table */ rows = top.concat(btm); - if ( !!_.searching ) { + if (!!_.searching) { _.searchData = []; util.each(rows, function(i, v) { @@ -743,12 +873,12 @@ _.emit('datatable.sort', column, dir); }; - Plugin.prototype.addRows = function(data) { - if ( Object.prototype.toString.call(data) !== '[object Object]') { + DataTable.prototype.addRows = function(data) { + if (Object.prototype.toString.call(data) !== '[object Object]') { throw new Error("Function addRows: The method requires an object."); } - if ( !data.rows ) { + if (!data.rows) { throw new Error("Function addRows: Your object is missing the 'rows' property."); } @@ -756,7 +886,9 @@ util.each(data.rows, function(i, row) { var tr = util.createElement("tr"); util.each(row, function(a, val) { - var td = util.createElement("td", { html: val }); + var td = util.createElement("td", { + html: val + }); tr.appendChild(td); }); that.rows.push(tr); @@ -765,7 +897,7 @@ this.update(); }; - Plugin.prototype.refresh = function() { + DataTable.prototype.refresh = function() { this.input.value = ''; this.searching = false; this.update(); @@ -773,25 +905,27 @@ this.emit("datatable.refresh"); }; - Plugin.prototype.clear = function(html) { - if ( this.tbody ) { + DataTable.prototype.clear = function(html) { + if (this.tbody) { util.flush(this.tbody, this.isIE); } var parent = this.tbody; - if ( !this.tbody ) { + if (!this.tbody) { parent = this.table; } - if ( html ) { + if (html) { util.append(parent, html); - } + } }; - Plugin.prototype.setMessage = function(message) { - var colspan = this.table.tHead.rows[0].cells.length; - this.clear(util.createElement('tr', { html: '