Last active
May 16, 2016 22:11
-
-
Save Federicopvs/dd6a365c4145c79c274bc52dc789ab30 to your computer and use it in GitHub Desktop.
D3 + leaflet + GForm
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!DOCTYPE HTML> | |
<html> | |
<head> | |
<script src="https://d3js.org/d3.v3.min.js" charset="utf-8"></script> | |
<link rel="stylesheet" type="text/css" href="style.css"> | |
<!--Leaflet--> | |
<link rel="stylesheet" href="http://cdn.leafletjs.com/leaflet/v0.7.7/leaflet.css" /> | |
<script src="http://cdn.leafletjs.com/leaflet/v0.7.7/leaflet.js"></script> | |
<!--Tabletop--> | |
<script type="text/javascript" src="tabletop.js"></script> | |
<!--Google Maps JavaScript API--> | |
<script src="https://maps.googleapis.com/maps/api/js?key=[YOUR_API_KEY]&language=es®ion=CO"></script> | |
</head> | |
<body> | |
<div id="map"></div> | |
<div id="tooltip"> | |
<p><span id="headline">CLICK TO START</span></p> | |
<p><span id="location"></span></p> | |
<p><span id="time"></span></p> | |
</div> | |
<script type="text/javascript"> | |
//------Leaflet------ | |
var map = L.map('map').setView([4.711245, -74.069368], 11); | |
L.tileLayer('https://api.mapbox.com/styles/v1/federicopvs/cinweuph60029b4npbawfzlc7/tiles/{z}/{x}/{y}?access_token=pk.eyJ1IjoiZmVkZXJpY29wdnMiLCJhIjoiY2lsZ3JqdXgxMDA0MHdnbTRzYjJkaWplZiJ9.P_8XuqQUogZRfBqwXnXF8g', { | |
attribution: 'Map data © <a href="http://openstreetmap.org">OpenStreetMap</a> contributors, <a href="http://creativecommons.org/licenses/by-sa/2.0/">CC-BY-SA</a>, Imagery © <a href="http://mapbox.com">Mapbox</a>', | |
maxZoom: 18, | |
minZoom: 11, | |
id: 'your.mapbox.project.id', | |
accessToken: 'your.mapbox.public.access.token' | |
}).addTo(map); | |
//------D3------ | |
var dataset; | |
/// Initialize the SVG layer | |
map._initPathRoot() | |
// We pick up the SVG from the map object | |
var svg = d3.select("#map").select("svg"), | |
g = svg.append("g"); | |
function drawCircles (d) { | |
circle = g.selectAll("circle") | |
.data(dataset) | |
.enter() | |
.append("circle") | |
.style("stroke", "black") | |
.style("fill", fillColor) | |
.style("fill-opacity", 0.7) | |
.attr("r", 20) | |
.attr("cx", -20) | |
.attr("cy", -20); | |
} | |
function fillColor (d) { | |
if (d.color=="red") {return "red";} | |
else if (d.color=="blue") {return "blue";} | |
else if (d.color=="yellow") {return "yellow";} | |
} | |
function update () { | |
circle.attr("transform", function (d) { | |
return "translate("+ | |
map.latLngToLayerPoint(d.LatLng).x +","+ | |
map.latLngToLayerPoint(d.LatLng).y +")"; | |
}) | |
} | |
function interactivity () { | |
circle.on("mouseover", function () { | |
d3.select(this) | |
.transition() | |
.duration(500) | |
.style("fill-opacity", 1) | |
.attr("r", 25) | |
.ease("elastic") | |
}) | |
circle.on("mouseout", function () { | |
d3.select(this) | |
.transition() | |
.duration(500) | |
.style("fill-opacity", 0.7) | |
.attr("r", 20) | |
.ease("elastic") | |
}) | |
circle.on("click", function (d) { | |
d3.select(this) | |
d3.select("#tooltip") | |
.select("#headline") | |
.text(d.displayName) | |
d3.select("#tooltip") | |
.select("#location") | |
.text(d.formattedAddress) | |
d3.select("#tooltip") | |
.select("#time") | |
.text(d.Timestamp) | |
}) | |
d3.select("#headline").on("click", function () { | |
update(); | |
d3.select("#headline").text('Click on a circle to visualize more information.') | |
}) | |
} | |
//------Google Maps JavaScript API------ | |
var geocoder = new google.maps.Geocoder(); | |
function getCoordinates (d, index, fullAddress) { | |
var coordinates; | |
var formattedAddress; | |
geocoder.geocode({'address': fullAddress}, function (results, status) { | |
dataset[index].formattedAddress = results[0].formatted_address; | |
coords_obj = results[0].geometry.bounds; | |
dataset[index].LatLng = new L.LatLng(coords_obj.H.H,coords_obj.j.H); | |
}) | |
} | |
// Concatenate location info | |
function findLocation () { | |
dataset.forEach(function (d, index) { | |
d.fullAddress = d.street+" "+d.number+", "+d.city+", Colombia"; | |
var fullAddress = d.fullAddress; | |
getCoordinates(d, index, fullAddress); | |
}) | |
} | |
//------Tabletop------ | |
window.onload = function() { init() }; | |
var public_spreadsheet_url = 'https://docs.google.com/spreadsheets/d/1N6FXNLNQyJUhI27-j6uhuVyEgRKm3UOrGsDfY9V_T5w/pubhtml'; | |
function init() { | |
Tabletop.init( { key: public_spreadsheet_url, | |
callback: showInfo, | |
simpleSheet: true } ) | |
} | |
function showInfo(data, tabletop) { | |
console.log(data); | |
dataset=data; | |
findLocation(); | |
drawCircles(); | |
map.on("viewreset", update); | |
interactivity(); | |
} | |
</script> | |
</body> | |
</html> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
body { | |
background-color: rgb(229,245,249); | |
} | |
#map { | |
height: 600px; | |
width: 800px; | |
background-color: rgb(229,245,249); | |
position: relative; | |
margin-top: 2%; | |
} | |
#tooltip { | |
height: 50%; | |
width: 200px; | |
background-color: rgb(153,216,201); | |
position: absolute; | |
left: 850px; | |
top: 0%; | |
margin-top: 2%; | |
font-family: sans-serif; | |
font-size: 1em; | |
text-align: center; | |
padding: 1em 1em 1em 1em; | |
} | |
#headline { | |
font-size: 1.2em; | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
(function() { | |
"use strict"; | |
var inNodeJS = false; | |
if (typeof process !== 'undefined' && !process.browser) { | |
inNodeJS = true; | |
var request = require('request'.trim()); //prevents browserify from bundling the module | |
} | |
var supportsCORS = false; | |
var inLegacyIE = false; | |
try { | |
var testXHR = new XMLHttpRequest(); | |
if (typeof testXHR.withCredentials !== 'undefined') { | |
supportsCORS = true; | |
} else { | |
if ("XDomainRequest" in window) { | |
supportsCORS = true; | |
inLegacyIE = true; | |
} | |
} | |
} catch (e) { } | |
// Create a simple indexOf function for support | |
// of older browsers. Uses native indexOf if | |
// available. Code similar to underscores. | |
// By making a separate function, instead of adding | |
// to the prototype, we will not break bad for loops | |
// in older browsers | |
var indexOfProto = Array.prototype.indexOf; | |
var ttIndexOf = function(array, item) { | |
var i = 0, l = array.length; | |
if (indexOfProto && array.indexOf === indexOfProto) return array.indexOf(item); | |
for (; i < l; i++) if (array[i] === item) return i; | |
return -1; | |
}; | |
/* | |
Initialize with Tabletop.init( { key: '0AjAPaAU9MeLFdHUxTlJiVVRYNGRJQnRmSnQwTlpoUXc' } ) | |
OR! | |
Initialize with Tabletop.init( { key: 'https://docs.google.com/spreadsheet/pub?hl=en_US&hl=en_US&key=0AjAPaAU9MeLFdHUxTlJiVVRYNGRJQnRmSnQwTlpoUXc&output=html&widget=true' } ) | |
OR! | |
Initialize with Tabletop.init('0AjAPaAU9MeLFdHUxTlJiVVRYNGRJQnRmSnQwTlpoUXc') | |
*/ | |
var Tabletop = function(options) { | |
// Make sure Tabletop is being used as a constructor no matter what. | |
if(!this || !(this instanceof Tabletop)) { | |
return new Tabletop(options); | |
} | |
if(typeof(options) === 'string') { | |
options = { key : options }; | |
} | |
this.callback = options.callback; | |
this.wanted = options.wanted || []; | |
this.key = options.key; | |
this.simpleSheet = !!options.simpleSheet; | |
this.parseNumbers = !!options.parseNumbers; | |
this.wait = !!options.wait; | |
this.reverse = !!options.reverse; | |
this.postProcess = options.postProcess; | |
this.debug = !!options.debug; | |
this.query = options.query || ''; | |
this.orderby = options.orderby; | |
this.endpoint = options.endpoint || "https://spreadsheets.google.com"; | |
this.singleton = !!options.singleton; | |
this.simple_url = !!options.simple_url; | |
this.callbackContext = options.callbackContext; | |
// Default to on, unless there's a proxy, in which case it's default off | |
this.prettyColumnNames = typeof(options.prettyColumnNames) == 'undefined' ? !options.proxy : options.prettyColumnNames | |
if(typeof(options.proxy) !== 'undefined') { | |
// Remove trailing slash, it will break the app | |
this.endpoint = options.proxy.replace(/\/$/,''); | |
this.simple_url = true; | |
this.singleton = true; | |
// Let's only use CORS (straight JSON request) when | |
// fetching straight from Google | |
supportsCORS = false; | |
} | |
this.parameterize = options.parameterize || false; | |
if(this.singleton) { | |
if(typeof(Tabletop.singleton) !== 'undefined') { | |
this.log("WARNING! Tabletop singleton already defined"); | |
} | |
Tabletop.singleton = this; | |
} | |
/* Be friendly about what you accept */ | |
if(/key=/.test(this.key)) { | |
this.log("You passed an old Google Docs url as the key! Attempting to parse."); | |
this.key = this.key.match("key=(.*?)(&|#|$)")[1]; | |
} | |
if(/pubhtml/.test(this.key)) { | |
this.log("You passed a new Google Spreadsheets url as the key! Attempting to parse."); | |
this.key = this.key.match("d\\/(.*?)\\/pubhtml")[1]; | |
} | |
if(!this.key) { | |
this.log("You need to pass Tabletop a key!"); | |
return; | |
} | |
this.log("Initializing with key " + this.key); | |
this.models = {}; | |
this.model_names = []; | |
this.base_json_path = "/feeds/worksheets/" + this.key + "/public/basic?alt="; | |
if (inNodeJS || supportsCORS) { | |
this.base_json_path += 'json'; | |
} else { | |
this.base_json_path += 'json-in-script'; | |
} | |
if(!this.wait) { | |
this.fetch(); | |
} | |
}; | |
// A global storage for callbacks. | |
Tabletop.callbacks = {}; | |
// Backwards compatibility. | |
Tabletop.init = function(options) { | |
return new Tabletop(options); | |
}; | |
Tabletop.sheets = function() { | |
this.log("Times have changed! You'll want to use var tabletop = Tabletop.init(...); tabletop.sheets(...); instead of Tabletop.sheets(...)"); | |
}; | |
Tabletop.prototype = { | |
fetch: function(callback) { | |
if(typeof(callback) !== "undefined") { | |
this.callback = callback; | |
} | |
this.requestData(this.base_json_path, this.loadSheets); | |
}, | |
/* | |
This will call the environment appropriate request method. | |
In browser it will use JSON-P, in node it will use request() | |
*/ | |
requestData: function(path, callback) { | |
if (inNodeJS) { | |
this.serverSideFetch(path, callback); | |
} else { | |
//CORS only works in IE8/9 across the same protocol | |
//You must have your server on HTTPS to talk to Google, or it'll fall back on injection | |
var protocol = this.endpoint.split("//").shift() || "http"; | |
if (supportsCORS && (!inLegacyIE || protocol === location.protocol)) { | |
this.xhrFetch(path, callback); | |
} else { | |
this.injectScript(path, callback); | |
} | |
} | |
}, | |
/* | |
Use Cross-Origin XMLHttpRequest to get the data in browsers that support it. | |
*/ | |
xhrFetch: function(path, callback) { | |
//support IE8's separate cross-domain object | |
var xhr = inLegacyIE ? new XDomainRequest() : new XMLHttpRequest(); | |
xhr.open("GET", this.endpoint + path); | |
var self = this; | |
xhr.onload = function() { | |
try { | |
var json = JSON.parse(xhr.responseText); | |
} catch (e) { | |
console.error(e); | |
} | |
callback.call(self, json); | |
}; | |
xhr.send(); | |
}, | |
/* | |
Insert the URL into the page as a script tag. Once it's loaded the spreadsheet data | |
it triggers the callback. This helps you avoid cross-domain errors | |
http://code.google.com/apis/gdata/samples/spreadsheet_sample.html | |
Let's be plain-Jane and not use jQuery or anything. | |
*/ | |
injectScript: function(path, callback) { | |
var script = document.createElement('script'); | |
var callbackName; | |
if(this.singleton) { | |
if(callback === this.loadSheets) { | |
callbackName = 'Tabletop.singleton.loadSheets'; | |
} else if (callback === this.loadSheet) { | |
callbackName = 'Tabletop.singleton.loadSheet'; | |
} | |
} else { | |
var self = this; | |
callbackName = 'tt' + (+new Date()) + (Math.floor(Math.random()*100000)); | |
// Create a temp callback which will get removed once it has executed, | |
// this allows multiple instances of Tabletop to coexist. | |
Tabletop.callbacks[ callbackName ] = function () { | |
var args = Array.prototype.slice.call( arguments, 0 ); | |
callback.apply(self, args); | |
script.parentNode.removeChild(script); | |
delete Tabletop.callbacks[callbackName]; | |
}; | |
callbackName = 'Tabletop.callbacks.' + callbackName; | |
} | |
var url = path + "&callback=" + callbackName; | |
if(this.simple_url) { | |
// We've gone down a rabbit hole of passing injectScript the path, so let's | |
// just pull the sheet_id out of the path like the least efficient worker bees | |
if(path.indexOf("/list/") !== -1) { | |
script.src = this.endpoint + "/" + this.key + "-" + path.split("/")[4]; | |
} else { | |
script.src = this.endpoint + "/" + this.key; | |
} | |
} else { | |
script.src = this.endpoint + url; | |
} | |
if (this.parameterize) { | |
script.src = this.parameterize + encodeURIComponent(script.src); | |
} | |
document.getElementsByTagName('script')[0].parentNode.appendChild(script); | |
}, | |
/* | |
This will only run if tabletop is being run in node.js | |
*/ | |
serverSideFetch: function(path, callback) { | |
var self = this | |
request({url: this.endpoint + path, json: true}, function(err, resp, body) { | |
if (err) { | |
return console.error(err); | |
} | |
callback.call(self, body); | |
}); | |
}, | |
/* | |
Is this a sheet you want to pull? | |
If { wanted: ["Sheet1"] } has been specified, only Sheet1 is imported | |
Pulls all sheets if none are specified | |
*/ | |
isWanted: function(sheetName) { | |
if(this.wanted.length === 0) { | |
return true; | |
} else { | |
return (ttIndexOf(this.wanted, sheetName) !== -1); | |
} | |
}, | |
/* | |
What gets send to the callback | |
if simpleSheet === true, then don't return an array of Tabletop.this.models, | |
only return the first one's elements | |
*/ | |
data: function() { | |
// If the instance is being queried before the data's been fetched | |
// then return undefined. | |
if(this.model_names.length === 0) { | |
return undefined; | |
} | |
if(this.simpleSheet) { | |
if(this.model_names.length > 1 && this.debug) { | |
this.log("WARNING You have more than one sheet but are using simple sheet mode! Don't blame me when something goes wrong."); | |
} | |
return this.models[ this.model_names[0] ].all(); | |
} else { | |
return this.models; | |
} | |
}, | |
/* | |
Add another sheet to the wanted list | |
*/ | |
addWanted: function(sheet) { | |
if(ttIndexOf(this.wanted, sheet) === -1) { | |
this.wanted.push(sheet); | |
} | |
}, | |
/* | |
Load all worksheets of the spreadsheet, turning each into a Tabletop Model. | |
Need to use injectScript because the worksheet view that you're working from | |
doesn't actually include the data. The list-based feed (/feeds/list/key..) does, though. | |
Calls back to loadSheet in order to get the real work done. | |
Used as a callback for the worksheet-based JSON | |
*/ | |
loadSheets: function(data) { | |
var i, ilen; | |
var toLoad = []; | |
this.googleSheetName = data.feed.title.$t; | |
this.foundSheetNames = []; | |
for(i = 0, ilen = data.feed.entry.length; i < ilen ; i++) { | |
this.foundSheetNames.push(data.feed.entry[i].title.$t); | |
// Only pull in desired sheets to reduce loading | |
if( this.isWanted(data.feed.entry[i].content.$t) ) { | |
var linkIdx = data.feed.entry[i].link.length-1; | |
var sheet_id = data.feed.entry[i].link[linkIdx].href.split('/').pop(); | |
var json_path = "/feeds/list/" + this.key + "/" + sheet_id + "/public/values?alt=" | |
if (inNodeJS || supportsCORS) { | |
json_path += 'json'; | |
} else { | |
json_path += 'json-in-script'; | |
} | |
if(this.query) { | |
json_path += "&sq=" + this.query; | |
} | |
if(this.orderby) { | |
json_path += "&orderby=column:" + this.orderby.toLowerCase(); | |
} | |
if(this.reverse) { | |
json_path += "&reverse=true"; | |
} | |
toLoad.push(json_path); | |
} | |
} | |
this.sheetsToLoad = toLoad.length; | |
for(i = 0, ilen = toLoad.length; i < ilen; i++) { | |
this.requestData(toLoad[i], this.loadSheet); | |
} | |
}, | |
/* | |
Access layer for the this.models | |
.sheets() gets you all of the sheets | |
.sheets('Sheet1') gets you the sheet named Sheet1 | |
*/ | |
sheets: function(sheetName) { | |
if(typeof sheetName === "undefined") { | |
return this.models; | |
} else { | |
if(typeof(this.models[ sheetName ]) === "undefined") { | |
// alert( "Can't find " + sheetName ); | |
return; | |
} else { | |
return this.models[ sheetName ]; | |
} | |
} | |
}, | |
sheetReady: function(model) { | |
this.models[ model.name ] = model; | |
if(ttIndexOf(this.model_names, model.name) === -1) { | |
this.model_names.push(model.name); | |
} | |
this.sheetsToLoad--; | |
if(this.sheetsToLoad === 0) | |
this.doCallback(); | |
}, | |
/* | |
Parse a single list-based worksheet, turning it into a Tabletop Model | |
Used as a callback for the list-based JSON | |
*/ | |
loadSheet: function(data) { | |
var that = this; | |
var model = new Tabletop.Model( { data: data, | |
parseNumbers: this.parseNumbers, | |
postProcess: this.postProcess, | |
tabletop: this, | |
prettyColumnNames: this.prettyColumnNames, | |
onReady: function() { | |
that.sheetReady(this); | |
} } ); | |
}, | |
/* | |
Execute the callback upon loading! Rely on this.data() because you might | |
only request certain pieces of data (i.e. simpleSheet mode) | |
Tests this.sheetsToLoad just in case a race condition happens to show up | |
*/ | |
doCallback: function() { | |
if(this.sheetsToLoad === 0) { | |
this.callback.apply(this.callbackContext || this, [this.data(), this]); | |
} | |
}, | |
log: function(msg) { | |
if(this.debug) { | |
if(typeof console !== "undefined" && typeof console.log !== "undefined") { | |
Function.prototype.apply.apply(console.log, [console, arguments]); | |
} | |
} | |
} | |
}; | |
/* | |
Tabletop.Model stores the attribute names and parses the worksheet data | |
to turn it into something worthwhile | |
Options should be in the format { data: XXX }, with XXX being the list-based worksheet | |
*/ | |
Tabletop.Model = function(options) { | |
var i, j, ilen, jlen; | |
this.column_names = []; | |
this.name = options.data.feed.title.$t; | |
this.tabletop = options.tabletop; | |
this.elements = []; | |
this.onReady = options.onReady; | |
this.raw = options.data; // A copy of the sheet's raw data, for accessing minutiae | |
if(typeof(options.data.feed.entry) === 'undefined') { | |
options.tabletop.log("Missing data for " + this.name + ", make sure you didn't forget column headers"); | |
this.original_columns = []; | |
this.elements = []; | |
this.onReady.call(this); | |
return; | |
} | |
for(var key in options.data.feed.entry[0]){ | |
if(/^gsx/.test(key)) | |
this.column_names.push( key.replace("gsx$","") ); | |
} | |
this.original_columns = this.column_names; | |
for(i = 0, ilen = options.data.feed.entry.length ; i < ilen; i++) { | |
var source = options.data.feed.entry[i]; | |
var element = {}; | |
for(var j = 0, jlen = this.column_names.length; j < jlen ; j++) { | |
var cell = source[ "gsx$" + this.column_names[j] ]; | |
if (typeof(cell) !== 'undefined') { | |
if(options.parseNumbers && cell.$t !== '' && !isNaN(cell.$t)) | |
element[ this.column_names[j] ] = +cell.$t; | |
else | |
element[ this.column_names[j] ] = cell.$t; | |
} else { | |
element[ this.column_names[j] ] = ''; | |
} | |
} | |
if(element.rowNumber === undefined) | |
element.rowNumber = i + 1; | |
if( options.postProcess ) | |
options.postProcess(element); | |
this.elements.push(element); | |
} | |
if(options.prettyColumnNames) | |
this.fetchPrettyColumns(); | |
else | |
this.onReady.call(this); | |
}; | |
Tabletop.Model.prototype = { | |
/* | |
Returns all of the elements (rows) of the worksheet as objects | |
*/ | |
all: function() { | |
return this.elements; | |
}, | |
fetchPrettyColumns: function() { | |
if(!this.raw.feed.link[3]) | |
return this.ready(); | |
var cellurl = this.raw.feed.link[3].href.replace('/feeds/list/', '/feeds/cells/').replace('https://spreadsheets.google.com', ''); | |
var that = this; | |
this.tabletop.requestData(cellurl, function(data) { | |
that.loadPrettyColumns(data) | |
}); | |
}, | |
ready: function() { | |
this.onReady.call(this); | |
}, | |
/* | |
* Store column names as an object | |
* with keys of Google-formatted "columnName" | |
* and values of human-readable "Column name" | |
*/ | |
loadPrettyColumns: function(data) { | |
var pretty_columns = {}; | |
var column_names = this.column_names; | |
var i = 0; | |
var l = column_names.length; | |
for (; i < l; i++) { | |
if (typeof data.feed.entry[i].content.$t !== 'undefined') { | |
pretty_columns[column_names[i]] = data.feed.entry[i].content.$t; | |
} else { | |
pretty_columns[column_names[i]] = column_names[i]; | |
} | |
} | |
this.pretty_columns = pretty_columns; | |
this.prettifyElements(); | |
this.ready(); | |
}, | |
/* | |
* Go through each row, substitutiting | |
* Google-formatted "columnName" | |
* with human-readable "Column name" | |
*/ | |
prettifyElements: function() { | |
var pretty_elements = [], | |
ordered_pretty_names = [], | |
i, j, ilen, jlen; | |
var ordered_pretty_names; | |
for(j = 0, jlen = this.column_names.length; j < jlen ; j++) { | |
ordered_pretty_names.push(this.pretty_columns[this.column_names[j]]); | |
} | |
for(i = 0, ilen = this.elements.length; i < ilen; i++) { | |
var new_element = {}; | |
for(j = 0, jlen = this.column_names.length; j < jlen ; j++) { | |
var new_column_name = this.pretty_columns[this.column_names[j]]; | |
new_element[new_column_name] = this.elements[i][this.column_names[j]]; | |
} | |
pretty_elements.push(new_element); | |
} | |
this.elements = pretty_elements; | |
this.column_names = ordered_pretty_names; | |
}, | |
/* | |
Return the elements as an array of arrays, instead of an array of objects | |
*/ | |
toArray: function() { | |
var array = [], | |
i, j, ilen, jlen; | |
for(i = 0, ilen = this.elements.length; i < ilen; i++) { | |
var row = []; | |
for(j = 0, jlen = this.column_names.length; j < jlen ; j++) { | |
row.push( this.elements[i][ this.column_names[j] ] ); | |
} | |
array.push(row); | |
} | |
return array; | |
} | |
}; | |
if(typeof module !== "undefined" && module.exports) { //don't just use inNodeJS, we may be in Browserify | |
module.exports = Tabletop; | |
} else if (typeof define === 'function' && define.amd) { | |
define(function () { | |
return Tabletop; | |
}); | |
} else { | |
window.Tabletop = Tabletop; | |
} | |
})(); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment