Giter Club home page Giter Club logo

tabletop's Introduction

Tabletop.js (gives spreadsheets legs)

Tabletop.js takes a Google Spreadsheet and makes it easily accessible through JavaScript. With zero dependencies! If you've ever wanted to get JSON from a Google Spreadsheet without jumping through a thousand hoops, welcome home.

Tabletop.js easily integrates Google Spreadsheets with templating systems and anything else that is hip and cool. It will also help you make new friends and play jazz piano.

Build Status

But wait, don't use Tabletop!!!!

Tabletop was created in about 1995, back when we had to remember to disable call waiting when dialing into Compuserve. Now that it's 2020, Google is shutting down the infrastructure that Tabletop relies on.

Big surprise, right? But luckily, it's pretty easy to switch to a more modern way of doing things!

Instead of Tabletop, we're going to use Papa Parse. You still need to share your spreadsheet and all of that (see below), but the changes are actually pretty tiny! I've added a file where you can see a full example of doing it, but the quick version is:

Old Tabletop style

function init() {
  Tabletop.init( { key: 'https://docs.google.com/spreadsheets/d/0AmYzu_s7QHsmdDNZUzRlYldnWTZCLXdrMXlYQzVxSFE/pubhtml',
                    callback: function(data, tabletop) { 
                      console.log(data)
                    },
                    simpleSheet: true } )
}
window.addEventListener('DOMContentLoaded', init)

Updated version

function init() {
          Papa.parse('https://docs.google.com/spreadsheets/d/e/2PACX-1vRB4E_6RnpLP1wWMjqcwsUvotNATB8Np3OntlXb7066ULcAHI9oqqRhucltFifPTYNd7DRNRE56oTdt/pub?output=csv', {
          download: true,
          header: true,
          complete: function(results) {
            var data = results.data
            console.log(data)
          }
        })
window.addEventListener('DOMContentLoaded', init)

One important note is this won't work with Publish to Web if you have an organizational account. Like if I use my @columbia.edu account Google pretends I'm a terrible criminal and won't give me access via Papa Parse.

This also won't work if you're opening up an html file on your computer like a Normal Person, it requires you to either put it on the internet or run a local server. There are worse things, I guess.

But hey, as of September 2020 it's this way or the highway!

Google broke some MORE stuff, so you just plan might not be able to do this any more. If it's a PRIVATE project, you can get an API key and jump through a few hoops. But beware that this exposes your API key to anyone visiting the site, which allows people to do whatever they want with your data and run up bills on your end, so you probably don't want this on something facing the world.

But let's pretend Tabletop isn't leaving

Like how easy?

Step One: make a Google Spreadsheet and "Publish to Web." You'll need to pick CSV to be forwards-compatible for when Google shuts down the old way of doing things.

Step Two: Write a page that invokes Tabletop with the published URL Google gives you.

function init() {
  Tabletop.init( { key: 'https://docs.google.com/spreadsheets/d/0AmYzu_s7QHsmdDNZUzRlYldnWTZCLXdrMXlYQzVxSFE/pubhtml',
                    callback: function(data, tabletop) { 
                      console.log(data)
                    },
                    simpleSheet: true } )
}
window.addEventListener('DOMContentLoaded', init)

Step Two, modern-er version: We've moved to the future (aka like a decade ago) by supporting promises.

function init() {
  Tabletop.init( {
    key: 'https://docs.google.com/spreadsheets/d/0AmYzu_s7QHsmdDNZUzRlYldnWTZCLXdrMXlYQzVxSFE/pubhtml',
    simpleSheet: true }
  ).then(function(data, tabletop) { 
    console.log(data)
  })
}
window.addEventListener('DOMContentLoaded', init)

Step Three: Enjoy your data!

[ { name: "Carrot", category: "Vegetable", healthiness: "Adequate" }, 
  { name: "Pork Shoulder", category: "Meat", healthiness: "Questionable" }, 
  { name: "Bubblegum", category: "Candy", healthiness: "Super High"} ]

Yes, it's that easy.

NOTE: If your share URL has a /d/e in it, try refreshing the page to see if it goes away. If it doesn't, try this.

Getting Started

1) Publishing your Google Sheet

The first step is to get your data out into a form Tabletop can digest

Make a Google Spreadsheet. Give it some column headers, give it some content.

Name            Category   Healthiness
Carrot          Vegetable  Adequate
Pork Shoulder   Meat       Questionable
Bubblegum       Candy      Super High

Now go up to the File menu and pick Publish to the web. Fiddle with the options, then click Start publishing. A URL will appear, something like https://docs.google.com/spreadsheets/d/e/2PACX-1vQ2qq5UByYNkhsujdrWlDXtpSUhh7ovl0Ak6pyY3sWZqEaWS2lJ0iuqcag8iDLsoTuZ4XTiaEBtbbi0/pubhtml .

IGNORE THIS URL! You used to be able to use it, you can't anymore (you still need to do this step, though).

Now that you've published your sheet, you now need to share it, too.

  1. Click the Share link in the upper right-hand corner
  2. Click the very pale Advanced button
  3. Change... access to "On - Anyone with a link"
  4. Make sure Access: Anyone says Can view, since you don't want strangers editing your data
  5. Click Save

Copy the Link to Share. Your URL should look something like https://docs.google.com/spreadsheets/d/1Io6W5XitNvifEXER9ECTsbHhAjXsQLq6VEz7kSPDPiQ/edit?usp=sharing. It should not have a /d/e in it.

2) Setting up Tabletop

Now you're going to feed your spreadsheet into Tabletop

Include the Tabletop JavaScript file in your HTML, then try the following, substituting your URL for publicSpreadsheetUrl

<script src='https://cdnjs.cloudflare.com/ajax/libs/tabletop.js/1.5.1/tabletop.min.js'></script>
<script type='text/javascript'>    
  var publicSpreadsheetUrl = 'https://docs.google.com/spreadsheets/d/1sbyMINQHPsJctjAtMW0lCfLrcpMqoGMOJj6AN-sNQrc/pubhtml';

  function init() {
    Tabletop.init( { key: publicSpreadsheetUrl,
                     callback: showInfo,
                     simpleSheet: true } )
  }

  function showInfo(data, tabletop) {
    alert('Successfully processed!')
    console.log(data);
  }

  window.addEventListener('DOMContentLoaded', init)
</script>

After Tabletop reads your Sheet, it hops to the showInfo function with your data. Open up your console and check out the data it retrieved. All of those rows were turned right into objects! See how easy that was?

3) Honestly, that's it.

Check out the reference and the examples, but basically you're set. The only thing to think about right now is if you want to deal with multiple sheets you can get rid of simpleSheet: true (more on that later).

You might also be interested in the publishing/republishing/publish-as-it-changes aspects of Google Spreadsheets, but you'll need to google that for any specifics.

A note on node

To use this in a node environment:

npm install tabletop -save

Copy and paste this in your index.js file:

 const Tabletop = require('tabletop');

 var publicSpreadsheetUrl = 'URL OF SPREADSHEET AS YOU FIND IN THE BROWSER ADDRESS BAR';

function init() {
    Tabletop.init( { key: publicSpreadsheetUrl,
                     callback: showInfo,
                     simpleSheet: false } )
  }

 function showInfo(data, tabletop) {
  // do something with the data
  console.log(JSON.stringify(data, null, 2));
}

//initialise and kickstart the whole thing.
init()

Reference

Tabletop initialization

The simplest Tabletop initialization works like this:

var tabletop = Tabletop.init({ 
  key: '1sbyMINQHPsJctjAtMW0lCfLrcpMqoGMOJj6AN-sNQrc', 
  callback: showInfo 
})

You pass in either key as the actual spreadsheet key, or just the full published-spreadsheet URL.

showInfo is a function elsewhere in your code that gets called with your data.

Depending on how recently you've published your spreadsheet, your key comes from different places. Either the spreadsheet's URL in the address bar, the Publish URL, or the Share URL. Read this

Tabletop initialization options

key

key is the key of the published spreadsheet or the URL of the published spreadsheet.

callback

callback is the callback for when the data has been successfully pulled. It will be passed an object containing the models found in the spreadsheet (worksheets => models), and the tabletop instance. Each of these models contains the rows on that worksheet (see Tabletop.Model). If simpleSheet is turned on it simply receives an array of rows of the first worksheet.

simpleSheet

simpleSheet can be true or false (default false). It assumes you have one table and you don't care what it's called, so it sends the callback an array of rows instead of a list of models. Peek at the examples for more info.

parseNumbers

parseNumbers can be true or false (default false). If true, Tabletop will automatically parse any numbers for you so they don't run around as strings.

error

error is the callback for when something goes wrong. I'm uncertain how well it works in the browser in all situations, but in our Modern World I'm pretty sure it can be relied on.

orderby

orderby asks Google to sort the results by a column. You'll need to strip spaces and lowercase your column names, i.e. {order: 'firstname'} for a column called First Name. You'll want to use this when you only have a single sheet, though, otherwise it will try to sort by the same column on every single sheet.

reverse

reverse reverses the order if set to true.

postProcess

postProcess is a function that processes each row after it has been created. Use this to rename columns, compute attributes, etc.

For example:

 postProcess: function(element) {
   // Combine first and last name into a new column
   element["full_name"] = element["first_name"] + " " + element["last_name"];
   
   // Convert string date into Date date
   element["timestamp"] = Date.parse(element["displaydate"]);
 } 

wanted

wanted is an array of sheets you'd like to pull. If you have 20 sheets in a public spreadsheet you might as well only pull what you need to access. See the example in simple/multiple.html. Defaults to all.

endpoint

endpoint is the protocol and domain you'd like to query for your spreadsheet. Defaults to https://spreadsheets.google.com.

singleton

singleton assigned the instantiated Tabletop object to Tabletop.singleton, implemented to simplify caching and proxying of requests. Defaults to false.

simple_url

simpleUrl, if true, changes all requests to KEY and KEY-SHEET_ID. Defaults to false.

proxy

proxy allows you to easily use spreadsheets not located on Google Spreadsheet servers. Setting proxy: "http://www.proxy.com" is equivalent to setting { simple_url: true, singleton: true, endpoint: "http://www.proxy.com" }. Flatware might provide better documentation.

wait

wait prevents tabletop from pulling the Google spreadsheet until you're ready. Used in the backbone.js example.

query

query sends a structured query along with the spreadsheet request, so you can ask for rows with age > 55 and the like. Right now it's passed with every request, though, so if you're using multiple tables you'll end up in Problem City. It should work great with simpleSheet situations, though. Doesn't want to work at the moment.

debug

debug returns on debug mode, which gives you plenty of messaging about what's going on under the hood.

authkey

authkey is the authorization key for private sheet support.

parameterize

parameterize changes the src of all injected scripts. Instead of src, src is URI encoded and appended to parameterize, e.g. set it to http://example.herokuapp.com/?url=. Mostly for gs-proxy.

callbackContext

callbackContext sets the this for your callback. It's the tabletop object by default.

prettyColumnNames

prettyColumnNames can be true or false (default to true, unless proxy is enabled†). Since Google doesn't pass us exactly the same column names as in the header ('$ Processed' becomes 'processed'), it takes an extra request to correct them. If you don't want the extra request, you'll want to set it to false

† prettyColumnNames doesn't work with Flatware, is why we disable it with a proxy by default

Tabletop object attributes and methods

Once you're in the callback, you get the data and a tabletop object. That object is capable of all sorts of fun things.

.sheets()

.sheets() are the Tabletop.Models that were populated, one per worksheet. You access a sheet by its name.

.sheets(name) is how you access a specific sheet. Say I have a worksheet called Cats I Know, I'll access it via tabletop.sheets("Cats I Know")

.modelNames

.modelNames are the names of the models [read: sheets] that Tabletop knows about. The sheet names do not reflect their ordering in the original spreadsheet.

.foundSheetNames

.foundSheetNames are the names of the sheets [read: models] that Tabletop knows about. Their order reflects the sheets' order in the original spreadsheet.

.data()

.data() returns the rows of the first model if you're in simpleSheet mode. It's the same as .sheets() otherwise. This is just a little sugar.

.fetch()

.fetch() manually initializes a data request to the Google Sheet.

.addWanted(name)

.addWanted(name) adds a sheet to the list that are updated with .fetch

Tabletop.Model attributes and methods

Tabletop refers to sheets as Models, which have a few extra abilities compared to the sheets-as-plain-objects.

.name

.name is the name of the worksheet it came from (the tab at the bottom of the spreadsheet)

.columnNames

.columnNames gives you the names of the columns in that table

.originalColumns

.originalColumns gives you the names of the columns that Google sends on the first pass (numbers stripped, lowercase, etc)

.prettyColumns

.prettyColumns gives you the mapping between the column headers in the spreadsheet and the and the columnNames. Disabled by passing prettyColumnNames: false when initializing Tabletop.

.all()

.all() returns an array of the rows of the table, in the style of [ { name: "Tom", age: 5}, { name: "Liz", age: 12 } ]

.toArray()

.toArray() returns the rows of the table with numbered indices instead of named ones [ [ "Tom", 5] , [ "Liz", 12 ] ]

So what the hell do I do with this?

Imagine it's a read-only, JavaScript CMS that you can edit through Google Docs. It's like Christmas up in here.

You can see examples of working with different systems in, yes, /examples/.

Tabletop and any templating system (Handlebars etc)

Super easy. Just feed the models to your template and you're all set.

Tabletop and Backbone.js

I've put together a Backbone.tabletopSync driver for Backbone collections and models. It's read-only, but you can't really complain if you're powering your Backbone app through Google Spreadsheets.

Source is, of course, in /src, and you can check it out in action in examples/backbone/

Tabletop and AngularJS

Ændrew Rininsland (@aendrew) at The Times and Sunday Times has created a module that makes using Tabletop with AngularJS extremely easy. It also includes a loader for angular-translate that gives Tabletop the ability to provide i18n translation strings.

Please see times/angular-tabletop for more details.

Caching/Proxying Google Spreadsheets

Yeah, Google Spreadsheets can sometimes be slow or sometimes be overwhelmed or maybe one day Google will just up and disappear on us. So Tabletop.js now supports fetching your data from elsewhere, using options like endpoint and proxy.

proxy is the fun one, in that it rewrites your requests to be simpler-looking and plays nicely with the app & example I put together.

Using Flatware

If you don't mind running around with Heroku and AWS, Flatware is an app I built that uploads the spreadsheet JSON response to S3.

Using table-service

table-service hosts it on your own server using a python script, and auto-updates thanks to a tiny script you add to your spreadsheet.

Using gs-proxy

gs-proxy is another option that also uses Heroku. You'll set parameterize to something like http://example.herokuapp.com/?url= and off you go!

Using other caching

You can point proxy at anything you'd like as long as it has KEY and KEY-SHEET_ID files sitting in a directory. Feel free to host it on your own server! You can use /caching/local.rb if you want a pretty easy solution for generating the flat files.

Notes

Strange behavior

Empty tables are trouble. We can't get column names from them (c'mon, Google!), so don't be too confused when a table with 0 rows is coming back with an empty .column_names or your code starts throwing weird errors when processing the results.

Empty rows are trouble. If you have a row that's completely empty, Google doesn't return any rows after the empty row. As a result, you need to make sure every line in your spreadsheet has data in it.

Weird-named columns are trouble. A column named % might cause your spreadsheet to stop processing.

Unnamed columns are trouble. A column without a name will get in the way of your data successfully coming through.

If you are having trouble

Turn on debugging by passing debug: true when you initialize Tabletop. Check out the console, I try to keep my error messages chatty and informative. Or just email me at [email protected], I'm happy to help!

Tabletop.js in the wild

The more examples the better, right? Feel free to fork or contact me if you have a good example of something you've done.

A contextual video player with popcorn.js by @maboa

The WNYC mayoral tracker uses Tabletop along with Backbone.js

A Facebook-esque timeline from Balance Media (with a git repo)

Mapsheet creates super easy, customizable maps.

Other Options

If you aren't really feeling Tabletop, you should give Dataset a shot. It's "a JavaScript client-side data transformation and management library," which means it does a hell of a lot more than our dear Tabletop.

Credits

Jonathan Soma, who would rather be cooking than coding. Inspired by the relentless demands of John Keefe of WNYC.

Thanks to Scott Seaward for implementing multi-instance Tabletop.

Alan Palazzolo hooked the world up with gs-proxy and added support for it into Tabletop via parameterize

Max Ogden was kind enough to lend Tabletop nodejs support as part of an Open News code sprint

tabletop's People

Contributors

allishultes avatar astik avatar clkao avatar ctbarna avatar dannguyen avatar futuraprime avatar ghing avatar ilyankou avatar jsoma avatar jsvine avatar kirkman avatar kkirsche avatar laurent-le-graverend avatar martinburch avatar max-mapper avatar rakannimer avatar robertfischer avatar seiyria avatar shakhal avatar shurane avatar smarques avatar thenano avatar thomaswilburn avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

tabletop's Issues

Eliminate one of the requests?

Hey, fantastic job on this first of all!
I notice you're making two requests to get the data - one to feeds/worksheets to get the worksheet ID and then one to feeds/list to get the actual data.

My question is whether it'd be possible to eliminate the first request (to get the worksheet ID) if you're working with a spreadsheet that only has one worksheet - it seems the ID is always od6 by default. Have you considered this?

Handling errors

Would be awesome to get a callback to handle error responses from Google. Today I was getting 324 (ERR_EMPTY_RESPONSE: Server closed the connection without sending any data) and 429 (unknown).

I <3 Tabletop

tabletop enters infinite loop of requests

I am using tabletop with backbone.

My google spread sheet structure:

"Sections" sheet contains a list of gallery names, which is a reference to other sheets by the name.

var GalleriesCollection = Backbone.Collection.extend({
        model: GalleryModel,

        tabletop: {
            instance: GoogleSpreadsheet,
            sheet: 'sections'
        },

        sync: Backbone.tabletopSync
    });
var GalleryModel = Backbone.Model.extend({
        initialize: function(){
            this.slides = new SlidesCollection();

            this.slides.tabletop.sheet = this.get('name');

            this.slides.fetch({
                success: function( sections ){

                    console.log(sections);

                }
            });
var SlidesCollection = Backbone.Collection.extend({
        model: SlideModel,

        tabletop: {
            instance: GoogleSpreadsheet
        },

        sync: Backbone.tabletopSync
    });

I make an instance of the collection and call fetch:

var sections = new GalleriesCollection();

sections.fetch({
                success: function( sections ){
                   // console.log(sections);


                }
            });

The instance of the collection logs a proper response, but then tabletop.js:159 goes into an infinite loop of calling

https://spreadsheets.google.com/feeds/list/0AnHTNwS00YBMdElNcTI2TlpwNDF4QkdJRVNqeGNidlE/od4/public/values?alt=json-in-script&sq=&callback=Tabletop.callbacks.tt136503242367234341

This is line 159 from tabletop.js:

document.getElementsByTagName('script')[0].parentNode.appendChild(script);

This infinite loop results in Google blocking me and me having to do a captcha to get back in.

Any ideas what I'm doing wrong?

Select coulmns

Is there a way to only return certain columns?
For example I am interested in just one column in a spreadsheet with 10, so it kinda seems like a waste to select that one column out.
Thanks for the help.

Column header not parsed as string

I have column headers like this "0-14", "15-20", "20-25" and so on...

How can I make Tabletop read this data as a sting cause now it ignores it considering it to be illegal column name.

Guess that issue is caused by column name starting with number.

Many thanks in advance.

Retrieve un-sanitized column names

Hey

Great script!

I have been tasked with creating a table on a website from Google Spreadsheet, so need column names for table headings.

Is it possible to retrieve the column names, without lowercasing and spaces removed?

Thanks.

New spreadsheets in non-updated GDrive no longer work

First reported by @mattparrilla on Twitter https://twitter.com/mattparrilla/status/437645997884198912

it appears that Google Spreadsheet updates have made it so that newly created sheets will not work with Tabletop, even if you have not updated your Google Drive account.

I've provided an illustrative example below, using a URL that works for an old doc's key, and putting a new doc's key into it:

Live: http://daguar.github.io/tabletop-broken-example/
Code: https://github.com/daguar/tabletop-broken-example/blob/gh-pages/index.html

Can others please try this and confirm/disconfirm?

If it's true, we should add a note to the README (and probably sulk.)

if one sheet is empty the whole transaction is aborted without an error message

I have a sheet in a multisheet for errors/ambiguities, which starts with headers, but empty, and gets filled in as errors are found.

Expected behaviour: return the sheets, the empty sheet being empty.

Actual behaviour: data gets downloaded, but callback is not executed at all, so tabletop stops working. When I added a dummy row to the empty table it started working.

I think there's a bug around https://github.com/jsoma/tabletop/blob/master/src/tabletop.js#L422 where it spots an empty feed and logs a warning. Should it be returning this instead of nothing?

I can provide tests (though I don't see any here?) or set something up in jsfiddle if it helps.

Unclear instructions?

The README seems rather content in stating that this "just works like so:". I cloned the repo, rsynced it to my server, and added an index.html file to the tabletop root directory, which contains:

<!DOCTYPE html>
<html>
<body>
<script type="text/javascript">
  window.onload = function() { init() };

  var public_spreadsheet_url = 'https://docs.google.com/spreadsheet/pub?hl=en_US&hl=en_US&key=0AmYzu_s7QHsmdDNZUzRlYldnWTZCLXdrMXlYQzVxSFE&output=html';

  function init() {
    Tabletop.init( { key: public_spreadsheet_url,
                     callback: showInfo,
                     simpleSheet: true } )
  }

  function showInfo(data, tabletop) {
    alert("Successfully processed!")
    console.log(data);
  }
</script>
</body>
</html>

This shows me a blank page.
What am I missing?

Releasing a 1.3.5?

Hello and thank you for Tabletop, it's been super useful to me.

Do you think it would be okay to do a bump release to 1.3.5 to fix the #49 issue? It does break with a lot of new sheets because of the new IDs.

'Tabletop is undefined' error in Internet Explorer

Hey, loving tabletop.js. Makes deploying small web apps much easier.

Question, I'm running into an error with Internet Explorer when I initialize Tabeltop.

The breakpoint is here:

window.onload = function() { init() }

var public_spreadsheet_url = 'https://docs.google.com/spreadsheet/pub?key=0AmXUp_k1PfvcdGJqbm1TTElEUW16YXJXZWZsakFXaEE&output=html'

  function init() {
    Tabletop.init( { key: public_spreadsheet_url,
                     callback: showInfo,
                     wanted: ["Sheet1"],
                     debug: true })
  }

The JS debugger in IE stops at Tabletop.init. I'm using code from the standard.html file as a guide. Any help on this would be grand. Thanks!

Node detection breaks Karma runner

I'm using Tabletop.js to feed data into AngularJS, and all my tests are failing because Karma runs as a Node app (Thus triggering the conditional at line 5), and then fails when loading tabletop.js in the browser (Which can't use require()).

I've temporarily circumvented this by && typeof module.exports.browsers === undefined to the conditional, so that it ignores it if module.exports has a browsers object. NodeJS really isn't my strong suit, so there's probably a better way of doing this...

Empty Response

I copied the simple example shown in the examples directory. It works fine with the included public_spreadsheet_url, but as soon as I replace that with the URL of my spreadsheet, https://docs.google.com/spreadsheet/pub?key=0Alz1sw-5qPXHdGRyWTNsZmNsSG9MVjdlN2dmSUtDV0E&output=html, the alert says that 0 rows have been processed, and the console.log shows an empty object.

I double checked that the spreadsheet is public, and I tested the spreadsheet URL in an incognito window too just to be sure.

I've been scouring this project's docs on GitHub looking for an answer, but I've got nothing. Any ideas?

Browserify support

Nowadays, Tabletop works with NodeJS using the request module. But when using browserify, request became a big package (and consequently Tabletop too) and the resulting bundle is a ~1MB file (which is a bit huge for a client side file).

backbone tabletop question

I'm loading dynamically the sheets in the collection. When I make a fetch dont work.
My solution:
App.Collections.Mapa = Backbone.Collection.extend({

initialize:function(){
    this.tabletop();
    this.sync();
},

model:App.Models.mapa,

tabletop: function()
{
    return this.tabletop = {
    instance:ff(),
    sheet:App.sheet

    }
},

sync:function(){
    this.sync = Backbone.tabletopSync;
    return this.sync; 
}

});

Not really an issue but a comment

Just wanted to say Tabletop.js works great with jQuery Brackets! The area I'm struggling with is figuring out how to send edited data, made using jQuery Brackets on a website, to the original Google Spreadsheet.

The API to edit jQuery Bracket data can be viewed here. Any ideas on how to achieve this would be greatly appreciated.

Thank you for your time and keep up the great work!

XHR cross origin failure when initializing tabletop with a query

initialization such as:

Tabletop.init({
    key: "ABCD",
    query: "select B, C",
    simpleSheet: true,
    callback: callback
});

fails with the following error:

XMLHttpRequest cannot load https://spreadsheets.google.com/feeds/list/ABCD/oq7ao2t/public/values?alt=json&sq=select%20B,%20C. No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'https://4afe0e27c254af279a8d9135ef7dbe31d78cf487.googledrive.com' is therefore not allowed access. The response had HTTP status code 400.

blank lines in the spreadsheet break table top

Hey,

thanks for the fantastic library!
I have a following problem: each time I have a blank line in my spreadsheet, all the data is "cut" after that --- I only see data before the blank line. Is it the tabletop bug? Or is it how Google structures it's ATOM's feed?

How to retrieve sheet order

Scratched my head a couple of hours finding how to retrieve the sheets in the order they are ordered in the spreadsheet.

I could finally achieve it by using tableTop.foundSheetNames, but shouldn't it be documented since the values of tableTop.model_names seem to come in a random order (maybe in the order of asynchronous retrieval)?

Cheers,

Old Google Spreadsheets

This error:

You passed a new Google Spreadsheets url as the key! This won't work yet, you'll need to change back to old Sheets.

How to I change back to the old sheets?

Ember.js Support

Does tabletop supports ember.js with ember-data ?

i've been using tabletop for simple static stuff for while now and i'm trying to get it to work with ember. so far no luck, if it is possible could you put up a demo page for that like the backbone one please ?

Retrieved "Loading..." when using IMPORTRANGE in spreadsheet

I have multiple sheets, and the mail one obtains data from the rest using IMPORTRANGE. The values I get with tabletop for the cells where IMPORTRANGE is used are "Loading..." since the spreadsheet takes a while to calculate all data. ¿Is there a way to make tabletop wait until the spreadsheet is fully loaded?

Spreadsheets endpoint returns error with empty sq query parameter

Using Tabletop like this:

var spreadsheetId = 'some-spreadhseet-key'; 
Tabletop.init({
  key: spreadsheetId,
  callback: function(data, tabletop) {
    console.log('here');
    console.log(data);
  },
  simpleSheet: true
});

results in Tabletop.loadSheets building a URL like https://spreadsheets.google.com/feeds/list/some-spreadsheet-key/od6/public/values?sq=&alt=json

This returns a HTTP 400 error with a message of "Invalid query parameter value for sq.". If I get rid of the sq parameter in the query string, it returns the expected data.

Only adding the sq parameter to the URL if this.query is truthy seems to fix the issue for me, i.e.:

      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 sheet_id = data.feed.entry[i].link[3].href.substr( data.feed.entry[i].link[3].href.length - 3, 3);
          //var json_path = "/feeds/list/" + this.key + "/" + sheet_id + "/public/values?sq=" + this.query + '&alt='
          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.orderby) {
            json_path += "&orderby=column:" + this.orderby.toLowerCase();
          }
          if(this.reverse) {
            json_path += "&reverse=true";
          }
          if(this.query) {
            json_path += "&sq=" + this.query;
          }
          toLoad.push(json_path);
        }
      }

Note that I'm using this from Node.js, but I don't see why it makes a difference.

Caching scripts broken on Google Sheets

I'm experiencing an intermittent issue with Tabletop.js not loading the dataset from Google Sheets. In order to combat this as I have in the past, I've been trying to use one of the caching scripts to pull down the relevant files and upload them to S3.

Alas, 'tis not to be. It downloads the first file, then tries to extract the spreadsheet keys from that, then tries to load https://spreadsheets.google.com/feeds/cells/"+key+"/"+sheet_id+"/public/values?alt=json-in-script&sq=&callback=Tabletop.singleton.loadSheet. Alas, this fails with error 400.

Visiting that URL yields the following error: Invalid query parameter value for sq. Removing the &sq= query parameter from the URL, however, causes it to succeed.

Tabletop is undefined in Internet Explorer 9

I'm getting a SCRIPT 5009: Tabletop is undefined error in IE9.

The following is contained in a script tag just before the closing body tag. Any thoughts?

start();

function start()
{
var url = 'MY_URL_IS_HERE';
Tabletop.init(
{
key: url,
callback: fillBarker,
simpleSheet: true
}
);
}

Browserify example returns "Uncaught TypeError: undefined is not a function :5811"

The line that chrome complains about:

content = fs.readFileSync(file, 'ascii'),

In this function:

/**
 * Load an Apache2-style ".types" file
 *
 * This may be called multiple times (it's expected).  Where files declare
 * overlapping types/extensions, the last file wins.
 *
 * @param file (String) path of file to load.
 */
Mime.prototype.load = function(file) {

  this._loading = file;
  // Read file and split into lines
  var map = {},
      content = fs.readFileSync(file, 'ascii'),
      lines = content.split(/[\r\n]+/);

Fetching all sheets with backbone.tabletopSync

Is there a way to fetch all sheets with backbone.tabletopSync? Glancing at the code it seems that if the sheet option isn't set, nothing will happen:

if(typeof(tabletopOptions.sheet) == "undefined") {
    return;
  }

I am interested getting all of my sheets to JSON with one http request instead of many.

Thanks for making tabletop.

Version numbers

It would be helpful for tabletop.js to have some sort of version number system, especially when backwards-incompatibile changes are made, like this.

Return a Promise from Backbone.tabletopSync

Backbone.tabletopSync does not correctly implement the original Backbone.sync interface, which returns a Promise that is resolved when the request completes and rejected when it fails. This is a useful pattern and one Backbone developers are accustomed to using. I suspect Backbone developers interested in using Tabletop would greatly appreciate such functionality in this already-great library.

callback arguments

Is it possible to pass extra arguments to the callback function within the Tabletop.init function?

Couple of typos on the intro page.

Amazing library, clear amusing explanation. Really good.

Just a couple of points, I know the code on http://builtbybalance.com/Tabletop/ wasn't supposed to be copied and pasted "as is", but there's a couple of ; missing.
Confuses us JS newbies!

Also on the footer:,
Want to ge [get] started on your project?
Fill out our Project Questionarre[Questionnaire].

But apart from those minor details, I just really wanted to say thanks!

FireFox Errors - JSON Response is Requestiong Login on Public & Published Spreadsheet

Works fine in Chrome & Safari but When loading with FireFox data is never loaded.

According to FireBug -
"SyntaxError: syntax error"

"https://spreadsheets.google.com/feeds/worksheets/0ApLwuu1HQNO7dHJVY1dsdWZlT2lNRjZ4eXluY1BWdHc/public/basic?alt=json-in-script&callback=Tabletop.callbacks.tt137382618883958301"

Looks like it is requesting to login to google. I have this issue on a personal project as well as I check on the Demo project of http://dataforradicals.com/js/tabletop-to-datatables/

Populating array within Tabletop callback

Inside the Tabletop callback, I'm populating an array, which is created before the callback function.

When I console.log() the array outside of the Tabletop callback, on the first page load, it contains all the elements I push into the array in the callback. On subsequent page loads though, my array is empty.

Do you have any idea if this could be caused by the way Tabletop calls the callback function, or something else related to Tabletop?

Would you consider adding this to CDNJS?

Sorry, me again - last one I promise!

CDNJS (http://cdnjs.com/ ) is a popular community-curated CDN of js libraries, and handlebars is in there but your library isn't. They'll consider anything over 100 github stars. I tried adding it (https://github.com/cdnjs/cdnjs) but all the github stuff about running an npm validator is way beyond me! Having your on there (in minified JS too) would mean I could pull jquery, tabletop and handle bars all from CDNs - how's that for the ultimate low-byte flat file cms? :)

retry failed requests

Sometimes with multiple spreadsheets the request might time-out, or the proxy might fail.

Is there any way to force a retry on the failed request / handle the error?

Caching worked, not anymore

Hi there,

I have used caching via the 'proxy' parameter successfully with tabletop revision: 3531dd6

I recently updated to latest revision:
10e7808
and the caching doesn't work anymore.

I use a local proxy:

proxy: "tabletop/caching"

after the local.py script is run in this same directory.

In the working proxy version, tabletop.js successfully requests the URL:

<proxy>/<key>

Now, tabletop.js requests (and fails to load) the URL:

<proxy>/feeds/worksheets/<key>/public/basic?alt=json

This has been seen in both Firefox and Chrome. Thanks!

Herve

Trigger update events

It would be amazing to have tabletop monitor the data and trigger events when it changes.

That way you could use handlebars' automatic bind/update stuff. For awesome.

Setting custom marker icons based on column entry

I'm having some trouble figuring out how to set custom marker icons. I've got some icons defined per leaflet's documentation

var None = L.icon({
        iconUrl: 'icons/shadow.jpg',
        iconSize:     [10,10], // size of the icon
        iconAnchor:   [0, 0], // point of the icon which will correspond to marker's location
        popupAnchor:  [5, 5] // point from which the popup should open relative to the iconAnchor

And when tabletop is called one of the groups it pulls is the "faction" column

for (var num = 0; num < tabletopData.length; num ++) {
                var dataOne = tabletopData[num].name;
                var dataTwo = tabletopData[num].faction;

... And so on for all the columns and the latitude and longitude information.

So then later I try to use the dataTwo variable to change the icon on a per marker basis. In this case all cells in the entire faction column are currently filled with just the word None.

marker_location = new L.LatLng(dataLat, dataLong);
layer = new L.Marker(marker_location, {icon: dataTwo});
map.addLayer(layer);

I get an error (I'm using Chrome's javascript debugger to look at it) and the markers just don't show up. markerscript.js is the file with this code in it.

Uncaught TypeError: undefined is not a function     leaflet.js:7
o.Marker.o.Class.extend._initIcon                   leaflet.js:7
o.Marker.o.Class.extend.onAdd                       leaflet.js:7
o.Map.o.Class.extend._layerAdd                      leaflet.js:6
o.Map.o.Class.extend.addLayer                       leaflet.js:6
startUpLeafet                                       markerscript.js:54
Tabletop.doCallback                                 tabletop.js:382
Tabletop.loadSheet                                  tabletop.js:372
xhr.onload                                          tabletop.js:181

I'm sure I'm just missing something in how variables are handled though. If I replace dataTwo with the word None then all the markers load with the None icon as expected. So it's something about how the variable is formatted? I tried having the script dump the dataTwo variable to the console but I just get the word None as I was hoping.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.