remove Tabletop dependency (#2)
* reformat and remove Tabletop dependency * add an eslint file * remove Tabletop from linter file
This commit is contained in:
parent
6d7fa5b675
commit
90e327b548
31
.eslintrc
Normal file
31
.eslintrc
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
{
|
||||||
|
"rules": {
|
||||||
|
"indent": [
|
||||||
|
2,
|
||||||
|
4
|
||||||
|
],
|
||||||
|
"quotes": [
|
||||||
|
2,
|
||||||
|
"single"
|
||||||
|
],
|
||||||
|
"linebreak-style": [
|
||||||
|
2,
|
||||||
|
"unix"
|
||||||
|
],
|
||||||
|
"semi": [
|
||||||
|
2,
|
||||||
|
"always"
|
||||||
|
],
|
||||||
|
"strict": [
|
||||||
|
2,
|
||||||
|
"never"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"env": {
|
||||||
|
"browser": true
|
||||||
|
},
|
||||||
|
"globals": {
|
||||||
|
"Sheetsee": true,
|
||||||
|
"$": true
|
||||||
|
}
|
||||||
|
}
|
160
index.html
160
index.html
@ -4,9 +4,7 @@
|
|||||||
|
|
||||||
<meta charset='utf-8'>
|
<meta charset='utf-8'>
|
||||||
<script type="text/javascript" src='js/jquery.js '></script>
|
<script type="text/javascript" src='js/jquery.js '></script>
|
||||||
<script type="text/javascript" src='js/tabletop1.3.4.js'></script>
|
|
||||||
<script type="text/javascript" src='js/sheetsee.js'></script>
|
<script type="text/javascript" src='js/sheetsee.js'></script>
|
||||||
<script type="text/javascript" src='js/leaflet.markercluster.js'></script>
|
|
||||||
<link rel="shortcut icon" href="favicon.png"/>
|
<link rel="shortcut icon" href="favicon.png"/>
|
||||||
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
|
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
|
||||||
@ -95,123 +93,105 @@
|
|||||||
{{/rows}}
|
{{/rows}}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<script type="text/javascript">
|
<script type="text/javascript">
|
||||||
document.addEventListener('DOMContentLoaded', function() {
|
function showInfo(gData) {
|
||||||
var gData
|
var tableData = gData.slice(0).sort(
|
||||||
var URL = "0AnhMurQVK5pqdHU0UF9QTFRRNTQwR3hQTFdOV3laRGc"
|
function(a, b){
|
||||||
Tabletop.init( { key: URL, callback: showInfo, simpleSheet: true } )
|
var aname = a.name.toLowerCase();
|
||||||
})
|
var bname = b.name.toLowerCase();
|
||||||
|
return aname === bname ? 0 : +(aname > bname) || -1;
|
||||||
// so long, so messy
|
|
||||||
function showInfo(gData) {
|
|
||||||
tableData = gData.slice(0).sort(
|
|
||||||
function(a, b){ return a.name == b.name ? 0 : +(a.name > b.name) || -1; }
|
|
||||||
);
|
|
||||||
tableOptions = {
|
|
||||||
"data": tableData,
|
|
||||||
"tableDiv": "#hackSpotsTable",
|
|
||||||
"filterDiv": "#tableFilter"
|
|
||||||
}
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
var tableOptions = {
|
||||||
|
'data': tableData,
|
||||||
|
'tableDiv': '#hackSpotsTable',
|
||||||
|
'filterDiv': '#tableFilter'
|
||||||
|
};
|
||||||
|
|
||||||
// make the table, and the search bar
|
// make the table, and the search bar
|
||||||
Sheetsee.makeTable(tableOptions)
|
Sheetsee.makeTable(tableOptions);
|
||||||
Sheetsee.initiateTableFilter(tableOptions)
|
Sheetsee.initiateTableFilter(tableOptions);
|
||||||
|
|
||||||
// create geoJSON with coordinates and other
|
// create geoJSON with coordinates and other
|
||||||
// useful bits from the original data
|
// useful bits from the original data
|
||||||
var optionsJSON = ["name", "address", "city", "rowNumber"]
|
var optionsJSON = ['name', 'address', 'city', 'rowNumber'];
|
||||||
var geoJSON = Sheetsee.createGeoJSON(gData, optionsJSON)
|
var geoJSON = Sheetsee.createGeoJSON(gData, optionsJSON);
|
||||||
|
|
||||||
// create map, tilelayer (map background), markers and popups
|
// create map, tilelayer (map background), markers and popups
|
||||||
var map = Sheetsee.loadMap("map")
|
var map = Sheetsee.loadMap('map');
|
||||||
Sheetsee.addTileLayer(map, 'jllord.n7aml2bc')
|
Sheetsee.addTileLayer(map, 'jllord.n7aml2bc');
|
||||||
var markerLayer = Sheetsee.addMarkerLayer(geoJSON, map, "<h2>{{ name }}</h2>")
|
var markerLayer = Sheetsee.addMarkerLayer(geoJSON, map, '<h2>{{ name }}</h2>');
|
||||||
|
|
||||||
// find the latest spot with the most important
|
var theLatestSpot = gData[gData.length - 1];
|
||||||
// info filled in (to prevent map breaking if
|
|
||||||
// someone is currently editing spreadsheet)
|
|
||||||
var theLatestSpot = findLatestCompleteSpot(gData)
|
|
||||||
var latestSpot = Sheetsee.ich.latestSpot({
|
var latestSpot = Sheetsee.ich.latestSpot({
|
||||||
rows: theLatestSpot
|
rows: theLatestSpot
|
||||||
})
|
});
|
||||||
|
|
||||||
// set it and pan to it
|
// set it and pan to it
|
||||||
$('#latestSpot').html(latestSpot)
|
$('#latestSpot').html(latestSpot);
|
||||||
map.setView([theLatestSpot.lat, theLatestSpot.long], 14)
|
map.setView([theLatestSpot.lat, theLatestSpot.long], 14);
|
||||||
|
|
||||||
// when someone clicks on a row, highlight it and
|
// when someone clicks on a row, highlight it and re-center the map
|
||||||
// re-center the map
|
$('.spotRow').live('click', function() {
|
||||||
// TODO show popup, change marker color
|
$('.spotRow').removeClass('selectedRow');
|
||||||
$('.spotRow').live("click", function(event) {
|
var rowNumber = $(this).closest('tr').attr('id');
|
||||||
$('.spotRow').removeClass("selectedRow")
|
$('#' + rowNumber).addClass('selectedRow');
|
||||||
var rowNumber = $(this).closest("tr").attr("id")
|
var dataElement = Sheetsee.getMatches(gData, rowNumber, 'rowNumber');
|
||||||
$('#' + rowNumber).addClass("selectedRow")
|
var selectedSpot = Sheetsee.ich.selectedSpot({ rows: dataElement });
|
||||||
var dataElement = Sheetsee.getMatches(gData, rowNumber, "rowNumber")
|
$('#latestSpot').css('display', 'none');
|
||||||
var selectedSpot = Sheetsee.ich.selectedSpot({
|
$('#selectedSpot').html(selectedSpot).css('display', 'inline');
|
||||||
rows: dataElement
|
var selectedCoords = [dataElement[0].lat, dataElement[0].long];
|
||||||
})
|
map.setView(selectedCoords, 14);
|
||||||
$('#latestSpot').css("display", "none")
|
});
|
||||||
$('#selectedSpot').html(selectedSpot).css("display", "inline")
|
|
||||||
var selectedCoords = [dataElement[0].lat, dataElement[0].long]
|
|
||||||
map.setView(selectedCoords, 14)
|
|
||||||
})
|
|
||||||
|
|
||||||
// so that the first map and info that loads
|
|
||||||
// is complete and doesn't show rows that are
|
|
||||||
// actively being edited by folk
|
|
||||||
function findLatestCompleteSpot(data) {
|
|
||||||
var latestCompleteSpot = []
|
|
||||||
var startWithLatestRow = data.reverse()
|
|
||||||
startWithLatestRow.forEach(function(row){
|
|
||||||
if (!row.lat || !row.long || !row.name || !row.address || !row.city || !row.state ) return
|
|
||||||
else latestCompleteSpot.push(row)
|
|
||||||
})
|
|
||||||
return latestCompleteSpot[0]
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add click listener to the markerLayer
|
// Add click listener to the markerLayer
|
||||||
markerLayer.on('click', function(e) {
|
markerLayer.on('click', function(e) {
|
||||||
// clear any selected rows
|
// clear any selected rows
|
||||||
$('.spotRow').removeClass("selectedRow")
|
$('.spotRow').removeClass('selectedRow');
|
||||||
// get row number of selected marker
|
// get row number of selected marker
|
||||||
var rowNumber = e.layer.feature.opts.rowNumber
|
var rowNumber = e.layer.feature.opts.rowNumber;
|
||||||
// find that row in the table and make consider it selected
|
// find that row in the table and make consider it selected
|
||||||
$('#' + rowNumber).addClass("selectedRow")
|
$('#' + rowNumber).addClass('selectedRow');
|
||||||
// using row number, get the data for the selected spot
|
// using row number, get the data for the selected spot
|
||||||
var dataElement = Sheetsee.getMatches(gData, rowNumber.toString(), "rowNumber")
|
var dataElement = Sheetsee.getMatches(gData, rowNumber.toString(), 'rowNumber');
|
||||||
// take those details and re-write the selected spot section
|
// take those details and re-write the selected spot section
|
||||||
var selectedSpot = Sheetsee.ich.selectedSpot({
|
var selectedSpot = Sheetsee.ich.selectedSpot({ rows: dataElement });
|
||||||
rows: dataElement
|
|
||||||
})
|
|
||||||
// center the map on the selected element
|
// center the map on the selected element
|
||||||
map.panTo([dataElement[0].lat, dataElement[0].long])
|
map.panTo([dataElement[0].lat, dataElement[0].long]);
|
||||||
// update the spot listing
|
// update the spot listing
|
||||||
$('#latestSpot').css("display", "none")
|
$('#latestSpot').css('display', 'none');
|
||||||
$('#selectedSpot').html(selectedSpot).css("display", "inline")
|
$('#selectedSpot').html(selectedSpot).css('display', 'inline');
|
||||||
})
|
});
|
||||||
|
|
||||||
// reset the map, zoom out, and recenter on 0,0
|
// reset the map, zoom out, and recenter on 0,0
|
||||||
$('.resetMap').click(function() {
|
$('.resetMap').click(function() {
|
||||||
$('.spotRow').removeClass("selectedRow")
|
$('.spotRow').removeClass('selectedRow');
|
||||||
$('#latestSpot').css("display", "inline")
|
$('#latestSpot').css('display', 'inline');
|
||||||
$('#selectedSpot').css("display", "none")
|
$('#selectedSpot').css('display', 'none');
|
||||||
map.setView([0,0], 1)
|
map.setView([0, 0], 1);
|
||||||
})
|
});
|
||||||
|
|
||||||
// find total number of spots added
|
// find total number of spots added
|
||||||
var theNumberofSpots = Sheetsee.ich.theNumberofSpots({
|
$('#theNumberofSpots').html(Sheetsee.ich.theNumberofSpots({ numberOfSpots: gData.length }));
|
||||||
numberOfSpots: gData.length
|
|
||||||
})
|
|
||||||
$('#theNumberofSpots').html(theNumberofSpots)
|
|
||||||
|
|
||||||
if(window.location.hash) {
|
if (window.location.hash) {
|
||||||
$('#tableFilter').val(window.location.hash.substring(1)).keyup()
|
$('#tableFilter').val(window.location.hash.substring(1)).keyup();
|
||||||
$('.spotRow').first().click()
|
$('.spotRow').first().click();
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
$(document).on('keyup', '#tableFilter', function() {
|
document.addEventListener('DOMContentLoaded', function() {
|
||||||
window.location.hash = $(this).val()
|
$.getJSON('./stores.json', function(data) {
|
||||||
$('.spotRow').first().click()
|
$.each(data, function(key, value) { value.rowNumber = key; });
|
||||||
})
|
showInfo(data);
|
||||||
</script>
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
$(document).on('keyup', '#tableFilter', function() {
|
||||||
|
window.location.hash = $(this).val();
|
||||||
|
$('.spotRow').first().click();
|
||||||
|
});
|
||||||
|
</script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
File diff suppressed because one or more lines are too long
@ -1,474 +0,0 @@
|
|||||||
(function(global) {
|
|
||||||
"use strict";
|
|
||||||
|
|
||||||
var inNodeJS = false;
|
|
||||||
if (typeof module !== 'undefined' && module.exports) {
|
|
||||||
inNodeJS = true;
|
|
||||||
var request = require('request');
|
|
||||||
}
|
|
||||||
|
|
||||||
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;
|
|
||||||
|
|
||||||
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.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 ];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
/*
|
|
||||||
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 model = new Tabletop.Model( { data: data,
|
|
||||||
parseNumbers: this.parseNumbers,
|
|
||||||
postProcess: this.postProcess,
|
|
||||||
tabletop: this } );
|
|
||||||
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();
|
|
||||||
},
|
|
||||||
|
|
||||||
/*
|
|
||||||
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.elements = [];
|
|
||||||
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.elements = [];
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
for(var key in options.data.feed.entry[0]){
|
|
||||||
if(/^gsx/.test(key))
|
|
||||||
this.column_names.push( key.replace("gsx$","") );
|
|
||||||
}
|
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
Tabletop.Model.prototype = {
|
|
||||||
/*
|
|
||||||
Returns all of the elements (rows) of the worksheet as objects
|
|
||||||
*/
|
|
||||||
all: function() {
|
|
||||||
return this.elements;
|
|
||||||
},
|
|
||||||
|
|
||||||
/*
|
|
||||||
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(inNodeJS) {
|
|
||||||
module.exports = Tabletop;
|
|
||||||
} else {
|
|
||||||
global.Tabletop = Tabletop;
|
|
||||||
}
|
|
||||||
|
|
||||||
})(this);
|
|
1187
stores.json
Normal file
1187
stores.json
Normal file
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue
Block a user