Skip to content

Instantly share code, notes, and snippets.

@BarakChamo
Last active August 29, 2015 14:13
Show Gist options
  • Save BarakChamo/c63611ab880f56aff53f to your computer and use it in GitHub Desktop.
Save BarakChamo/c63611ab880f56aff53f to your computer and use it in GitHub Desktop.
HotTable.js
/* Space out content a bit */
body {
padding-top: 20px;
padding-bottom: 20px;
}
/* Everything but the jumbotron gets side spacing for mobile first views */
.header,
.marketing,
.footer {
padding-right: 15px;
padding-left: 15px;
}
/* Custom page header */
.header {
border-bottom: 1px solid #e5e5e5;
}
/* Make the masthead heading the same height as the navigation */
.header h3 {
padding-bottom: 19px;
margin-top: 0;
margin-bottom: 0;
line-height: 40px;
}
/* Custom page footer */
.footer {
padding-top: 19px;
color: #777;
border-top: 1px solid #e5e5e5;
}
/* Customize container */
@media (min-width: 768px) {
.container {
max-width: 730px;
}
}
.container-narrow > hr {
margin: 30px 0;
}
/* Main marketing message and sign up button */
.jumbotron {
text-align: center;
border-bottom: 1px solid #e5e5e5;
}
.jumbotron .btn {
padding: 14px 24px;
font-size: 21px;
}
/* Supporting marketing content */
.marketing {
margin: 40px 0;
}
.marketing p + h4 {
margin-top: 28px;
}
/* Responsive: Portrait tablets and up */
@media screen and (min-width: 768px) {
/* Remove the padding we set earlier */
.header,
.marketing,
.footer {
padding-right: 0;
padding-left: 0;
}
/* Space out the masthead */
.header {
margin-bottom: 30px;
}
/* Remove the bottom border on the jumbotron for visual effect */
.jumbotron {
border-bottom: 0;
}
}
table {
background-color: white;
}
/**
*
* HotTable.js v0.0.1
*
* A heat-table JS plugin by Barak Chamo, it's very nice.
*
*/
(function (root, factory) {
root.hotTable = factory(root);
})(window || this, function (root) {
'use strict';
var exports = {}, // Object for public APIs
settings, // Plugin settings
elm, // Root element
table, // Base element for the table
rendered, // Mark initial rendering passed
// Default settings
defaults = {
// Table data set
data: null,
// Formatter for cell values
formatter: function(val, rowIndex, colIndex){return val;},
// Set first row as a header row
rowHeaders: true,
// Set first column as a header column
colHeaders: true,
// classes to be applied to the table's root element
className: '',
// Color for 'hot', or max, values
hot: '3B6427',
// Color for 'cold', or min, values
cold: 'DAE289',
// Callback before rendering and re-rendering
callbackBefore: function () {},
// Callback after rendering and re-rendering
callbackAfter: function () {}
};
//
// Helpers
//
/**
* forEach() pollyfill based on _.forEach of Underscore.js
* @param {Array|Object} Collection of items for iteration
* @param {Function} Callback function for each iteration
* @param {any} context for the iterator function (this)
*/
function forEach(collection, callback, context) {
context = context || null;
if (Object.prototype.toString.call(collection) === '[object Object]') {
for (var prop in collection) {
if (Object.prototype.hasOwnProperty.call(collection, prop)) {
callback.call(context, collection[prop], prop, collection);
}
}
} else {
for (var i = 0, len = collection.length; i < len; i++) {
callback.call(context, collection[i], i, collection);
}
}
}
/**
* Merge destination with source
* @param {Object} destination object
* @param {Object} source object
*/
function extend(destination, source) {
for (var property in source) {
if (source[property] && source[property].constructor && source[property].constructor === Object) {
destination[property] = destination[property] || {};
extend(destination[property], source[property]);
} else {
destination[property] = source[property];
}
}
return destination;
}
//
// Private Methods
//
/**
* Get the extent (min and max) of a 2d collection
* @param {Object} 2d collection
*/
function getExtent( data ) {
if (!data || data.length && !data[0]) return (console.log('No Data Provided'));
var extent = [
data[defaults.rowHeaders ? 1 : 0][defaults.colHeaders ? 1 : 0],
data[defaults.rowHeaders ? 1 : 0][defaults.colHeaders ? 1 : 0]
];
// Compare each value with the current table extent
forEach(data, function(row, rowIndex){
if (!rowIndex && defaults.rowHeaders) return;
forEach(row, function(cell, colIndex){
if (!colIndex && defaults.colHeaders) return;
if (cell < extent[0]) {
extent[0] = cell;
} else if(cell > extent[1]){
extent[1] = cell;
}
});
});
return extent;
}
/**
* Get a hue on a gradient by percentage
* @param {float} stop percentage
*/
function getGradient( stop ) {
var from = defaults.cold,
to = defaults.hot,
r = Math.ceil(parseInt(from.substring(0,2), 16) * stop + parseInt(to.substring(0,2), 16) * (1-stop)),
g = Math.ceil(parseInt(from.substring(2,4), 16) * stop + parseInt(to.substring(2,4), 16) * (1-stop)),
b = Math.ceil(parseInt(from.substring(4,6), 16) * stop + parseInt(to.substring(4,6), 16) * (1-stop));
return(getHex(r) + getHex(g) + getHex(b));
}
/**
* Get a hex color definition from a RGB
* @param {int} red
* @param {int} green
* @param {int} blue
*/
function getHex( colorValue ) {
colorValue = colorValue.toString(16);
return (colorValue.length == 1) ? '0' + colorValue : colorValue;
}
/**
* Get a hex color definition from a RGB
* @param {int} red
* @param {int} green
* @param {int} blue
*/
function getPosition( min, max,val ) {
return (val - min) / (max - min);
}
/**
* Generate the heat table
* @param {array} the table's data extent
*/
function getTable() {
var table = document.createElement('TABLE'),
extent = getExtent(defaults.data),
tsection;
// Set table classes
table.className = defaults.className;
// Each row
forEach(data, function(row, rowIndex){
if(rowIndex === 0) tsection = table.appendChild(document.createElement('THEAD'));
if(rowIndex === 1) tsection = table.appendChild(document.createElement('TBODY'));
// Append a row to the table
var tr = tsection.appendChild(document.createElement('TR'));
// Each cell in row
forEach(row, function(cell, cellIndex){
var elm, format;
// Check if column or row header
if ((!rowIndex && defaults.rowHeaders) || (!cellIndex && defaults.colHeaders)){
format = true;
elm = tr.appendChild(document.createElement('TH'));
} else {
elm = tr.appendChild(document.createElement('TD'));
// Set element color
elm.style.backgroundColor = '#' + getGradient(getPosition(extent[0], extent[1], cell));
}
// Set element text, check if formatting is needed
elm.innerText = format ? defaults.formatter(cell) : cell;
});
});
return table;
}
//
// Public Methods
//
/**
* Destroy the current initialization.
*/
exports.destroy = function () {
// @todo Undo init...
};
/**
* Initialize Plugin
* @param {Object} DOMElement or selector string
* @param {Object} custom options
*/
exports.init = function ( element, options ) {
// Check if provided a selector or an element
elm = typeof element === 'string' ? document.querySelector(element) : element;
// Extend default options with custom ones
if (typeof options === 'object') extend(defaults, options);
// Render the table
exports.render();
};
/**
* set and re-set options
* @param {Object} key to be set
* @param {Object} value to be applies to key
* @param {Object} trigger re-render and callbacks to fire
*/
exports.set = function ( key, value, silent ) {
defaults[key] = value;
if(!silent) exports.render();
};
/**
* get options
* @param {Object} key to get the value of
*/
exports.get = function ( key ) {
return defaults[key];
};
/**
* render the heat table based on existing configuration
*/
exports.render = function () {
var newTable = getTable();
// Append or replace the new table;
table ? elm.replaceChild(newTable, table) : elm.appendChild(newTable);
// Keep an assignment to the new table
table = newTable;
};
//
// exports
//
return exports;
});
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Hot Table Demo</title>
<!-- Bootstrap core CSS -->
<link href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.1/css/bootstrap.min.css" rel="stylesheet">
<!-- Custom styles for this template -->
<link href="styles/demo.css" rel="stylesheet">
</head>
<body>
<!-- Container -->
<div class="container">
<!-- Header -->
<div class="header">
<h3 class="text-muted">HotTable.js</h3>
</div>
<!-- Hot Table -->
<div class="jumbotron" id="heattable"></div>
<footer class="footer">
<p>Barak Chamo 2015</p>
</footer>
</div> <!-- /container -->
<script src="scripts/hotTable.js"></script>
<script>
var data = [
['#', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I'],
['May', 90, 81, 75, 99, 90, 82, 101, 95, 85 ],
['June', 98, 102, 82, 92, 84, 101, 78, 89, 100 ],
['July', 72, 81, 93, 88, 102, 70, 90, 100, 101 ],
['August', 80, 91, 80, 104, 90, 75, 84, 81, 90 ]
];
hotTable.init('#heattable', {
data: data,
className: 'table table-bordered',
rowHeaders: true,
colHeaders: true,
formatter: function(val, rowIndex, colIndex){
return val;
}
});
</script>
</body>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment