diff --git a/README.md b/README.md index a7fc7367..f4f582b0 100755 --- a/README.md +++ b/README.md @@ -37,6 +37,7 @@ See CONTRIBUTING.md. Possible breaking changes +* Many backends moved to their own repositories #314 * Updated Leaflet to latest version 0.4.4 #220 * Added marker clustering in map view to handle a large number of markers * Dataset.restore method removed (not used internally except from Multiview.restore) diff --git a/_includes/recline-deps.html b/_includes/recline-deps.html index dbb4a23e..6055809a 100644 --- a/_includes/recline-deps.html +++ b/_includes/recline-deps.html @@ -53,7 +53,7 @@ - + diff --git a/dist/recline.js b/dist/recline.js index a163a421..41168e0c 100644 --- a/dist/recline.js +++ b/dist/recline.js @@ -383,174 +383,6 @@ this.recline.Backend.DataProxy = this.recline.Backend.DataProxy || {}; }(this.recline.Backend.DataProxy)); this.recline = this.recline || {}; this.recline.Backend = this.recline.Backend || {}; -this.recline.Backend.GDocs = this.recline.Backend.GDocs || {}; - -(function(my) { - "use strict"; - my.__type__ = 'gdocs'; - - // use either jQuery or Underscore Deferred depending on what is available - var Deferred = (typeof jQuery !== "undefined" && jQuery.Deferred) || _.Deferred; - - // ## Google spreadsheet backend - // - // Fetch data from a Google Docs spreadsheet. - // - // Dataset must have a url attribute pointing to the Gdocs or its JSON feed e.g. - //
-  // var dataset = new recline.Model.Dataset({
-  //     url: 'https://docs.google.com/spreadsheet/ccc?key=0Aon3JiuouxLUdGlQVDJnbjZRSU1tUUJWOUZXRG53VkE#gid=0'
-  //   },
-  //   'gdocs'
-  // );
-  //
-  // var dataset = new recline.Model.Dataset({
-  //     url: 'https://spreadsheets.google.com/feeds/list/0Aon3JiuouxLUdDQwZE1JdV94cUd6NWtuZ0IyWTBjLWc/od6/public/values?alt=json'
-  //   },
-  //   'gdocs'
-  // );
-  // 
- // - // @return object with two attributes - // - // * fields: array of Field objects - // * records: array of objects for each row - my.fetch = function(dataset) { - var dfd = new Deferred(); - var urls = my.getGDocsAPIUrls(dataset.url); - - // TODO cover it with tests - // get the spreadsheet title - (function () { - var titleDfd = new Deferred(); - - jQuery.getJSON(urls.spreadsheet, function (d) { - titleDfd.resolve({ - spreadsheetTitle: d.feed.title.$t - }); - }); - - return titleDfd.promise(); - }()).then(function (response) { - - // get the actual worksheet data - jQuery.getJSON(urls.worksheet, function(d) { - var result = my.parseData(d); - var fields = _.map(result.fields, function(fieldId) { - return {id: fieldId}; - }); - - dfd.resolve({ - metadata: { - title: response.spreadsheetTitle +" :: "+ result.worksheetTitle, - spreadsheetTitle: response.spreadsheetTitle, - worksheetTitle : result.worksheetTitle - }, - records : result.records, - fields : fields, - useMemoryStore: true - }); - }); - }); - - return dfd.promise(); - }; - - // ## parseData - // - // Parse data from Google Docs API into a reasonable form - // - // :options: (optional) optional argument dictionary: - // columnsToUse: list of columns to use (specified by field names) - // colTypes: dictionary (with column names as keys) specifying types (e.g. range, percent for use in conversion). - // :return: tabular data object (hash with keys: field and data). - // - // Issues: seems google docs return columns in rows in random order and not even sure whether consistent across rows. - my.parseData = function(gdocsSpreadsheet, options) { - var options = options || {}; - var colTypes = options.colTypes || {}; - var results = { - fields : [], - records: [] - }; - var entries = gdocsSpreadsheet.feed.entry || []; - var key; - var colName; - // percentage values (e.g. 23.3%) - var rep = /^([\d\.\-]+)\%$/; - - for(key in entries[0]) { - // it's barely possible it has inherited keys starting with 'gsx$' - if(/^gsx/.test(key)) { - colName = key.substr(4); - results.fields.push(colName); - } - } - - // converts non numberical values that should be numerical (22.3%[string] -> 0.223[float]) - results.records = _.map(entries, function(entry) { - var row = {}; - - _.each(results.fields, function(col) { - var _keyname = 'gsx$' + col; - var value = entry[_keyname].$t; - var num; - - // TODO cover this part of code with test - // TODO use the regexp only once - // if labelled as % and value contains %, convert - if(colTypes[col] === 'percent' && rep.test(value)) { - num = rep.exec(value)[1]; - value = parseFloat(num) / 100; - } - - row[col] = value; - }); - - return row; - }); - - results.worksheetTitle = gdocsSpreadsheet.feed.title.$t; - return results; - }; - - // Convenience function to get GDocs JSON API Url from standard URL - my.getGDocsAPIUrls = function(url) { - // https://docs.google.com/spreadsheet/ccc?key=XXXX#gid=YYY - var regex = /.*spreadsheet\/ccc?.*key=([^#?&+]+)[^#]*(#gid=([\d]+).*)?/; - var matches = url.match(regex); - var key; - var worksheet; - var urls; - - if(!!matches) { - key = matches[1]; - // the gid in url is 0-based and feed url is 1-based - worksheet = parseInt(matches[3], 10) + 1; - if (isNaN(worksheet)) { - worksheet = 1; - } - urls = { - worksheet : 'https://spreadsheets.google.com/feeds/list/'+ key +'/'+ worksheet +'/public/values?alt=json', - spreadsheet: 'https://spreadsheets.google.com/feeds/worksheets/'+ key +'/public/basic?alt=json' - }; - } - else { - // we assume that it's one of the feeds urls - key = url.split('/')[5]; - // by default then, take first worksheet - worksheet = 1; - urls = { - worksheet : 'https://spreadsheets.google.com/feeds/list/'+ key +'/'+ worksheet +'/public/values?alt=json', - spreadsheet: 'https://spreadsheets.google.com/feeds/worksheets/'+ key +'/public/basic?alt=json' - }; - } - - return urls; - }; -}(this.recline.Backend.GDocs)); -this.recline = this.recline || {}; -this.recline.Backend = this.recline.Backend || {}; this.recline.Backend.Memory = this.recline.Backend.Memory || {}; (function(my) { diff --git a/docs/tutorial-backends.markdown b/docs/tutorial-backends.markdown index 032452ba..1b27fb0a 100644 --- a/docs/tutorial-backends.markdown +++ b/docs/tutorial-backends.markdown @@ -76,8 +76,8 @@ much more limited if you are just using a Backend. Specifically: - - + + @@ -99,6 +99,8 @@ a bespoke chooser and a Kartograph (svg-only) map. {% highlight javascript %} +// include the Recline backend for Google Docs + {% include example-backends-gdocs.js %} {% endhighlight %} @@ -106,6 +108,8 @@ a bespoke chooser and a Kartograph (svg-only) map.
 
+ + diff --git a/src/backend.gdocs.js b/src/backend.gdocs.js deleted file mode 100644 index 16976884..00000000 --- a/src/backend.gdocs.js +++ /dev/null @@ -1,168 +0,0 @@ -this.recline = this.recline || {}; -this.recline.Backend = this.recline.Backend || {}; -this.recline.Backend.GDocs = this.recline.Backend.GDocs || {}; - -(function(my) { - "use strict"; - my.__type__ = 'gdocs'; - - // use either jQuery or Underscore Deferred depending on what is available - var Deferred = (typeof jQuery !== "undefined" && jQuery.Deferred) || _.Deferred; - - // ## Google spreadsheet backend - // - // Fetch data from a Google Docs spreadsheet. - // - // Dataset must have a url attribute pointing to the Gdocs or its JSON feed e.g. - //
-  // var dataset = new recline.Model.Dataset({
-  //     url: 'https://docs.google.com/spreadsheet/ccc?key=0Aon3JiuouxLUdGlQVDJnbjZRSU1tUUJWOUZXRG53VkE#gid=0'
-  //   },
-  //   'gdocs'
-  // );
-  //
-  // var dataset = new recline.Model.Dataset({
-  //     url: 'https://spreadsheets.google.com/feeds/list/0Aon3JiuouxLUdDQwZE1JdV94cUd6NWtuZ0IyWTBjLWc/od6/public/values?alt=json'
-  //   },
-  //   'gdocs'
-  // );
-  // 
- // - // @return object with two attributes - // - // * fields: array of Field objects - // * records: array of objects for each row - my.fetch = function(dataset) { - var dfd = new Deferred(); - var urls = my.getGDocsAPIUrls(dataset.url); - - // TODO cover it with tests - // get the spreadsheet title - (function () { - var titleDfd = new Deferred(); - - jQuery.getJSON(urls.spreadsheet, function (d) { - titleDfd.resolve({ - spreadsheetTitle: d.feed.title.$t - }); - }); - - return titleDfd.promise(); - }()).then(function (response) { - - // get the actual worksheet data - jQuery.getJSON(urls.worksheet, function(d) { - var result = my.parseData(d); - var fields = _.map(result.fields, function(fieldId) { - return {id: fieldId}; - }); - - dfd.resolve({ - metadata: { - title: response.spreadsheetTitle +" :: "+ result.worksheetTitle, - spreadsheetTitle: response.spreadsheetTitle, - worksheetTitle : result.worksheetTitle - }, - records : result.records, - fields : fields, - useMemoryStore: true - }); - }); - }); - - return dfd.promise(); - }; - - // ## parseData - // - // Parse data from Google Docs API into a reasonable form - // - // :options: (optional) optional argument dictionary: - // columnsToUse: list of columns to use (specified by field names) - // colTypes: dictionary (with column names as keys) specifying types (e.g. range, percent for use in conversion). - // :return: tabular data object (hash with keys: field and data). - // - // Issues: seems google docs return columns in rows in random order and not even sure whether consistent across rows. - my.parseData = function(gdocsSpreadsheet, options) { - var options = options || {}; - var colTypes = options.colTypes || {}; - var results = { - fields : [], - records: [] - }; - var entries = gdocsSpreadsheet.feed.entry || []; - var key; - var colName; - // percentage values (e.g. 23.3%) - var rep = /^([\d\.\-]+)\%$/; - - for(key in entries[0]) { - // it's barely possible it has inherited keys starting with 'gsx$' - if(/^gsx/.test(key)) { - colName = key.substr(4); - results.fields.push(colName); - } - } - - // converts non numberical values that should be numerical (22.3%[string] -> 0.223[float]) - results.records = _.map(entries, function(entry) { - var row = {}; - - _.each(results.fields, function(col) { - var _keyname = 'gsx$' + col; - var value = entry[_keyname].$t; - var num; - - // TODO cover this part of code with test - // TODO use the regexp only once - // if labelled as % and value contains %, convert - if(colTypes[col] === 'percent' && rep.test(value)) { - num = rep.exec(value)[1]; - value = parseFloat(num) / 100; - } - - row[col] = value; - }); - - return row; - }); - - results.worksheetTitle = gdocsSpreadsheet.feed.title.$t; - return results; - }; - - // Convenience function to get GDocs JSON API Url from standard URL - my.getGDocsAPIUrls = function(url) { - // https://docs.google.com/spreadsheet/ccc?key=XXXX#gid=YYY - var regex = /.*spreadsheet\/ccc?.*key=([^#?&+]+)[^#]*(#gid=([\d]+).*)?/; - var matches = url.match(regex); - var key; - var worksheet; - var urls; - - if(!!matches) { - key = matches[1]; - // the gid in url is 0-based and feed url is 1-based - worksheet = parseInt(matches[3], 10) + 1; - if (isNaN(worksheet)) { - worksheet = 1; - } - urls = { - worksheet : 'https://spreadsheets.google.com/feeds/list/'+ key +'/'+ worksheet +'/public/values?alt=json', - spreadsheet: 'https://spreadsheets.google.com/feeds/worksheets/'+ key +'/public/basic?alt=json' - }; - } - else { - // we assume that it's one of the feeds urls - key = url.split('/')[5]; - // by default then, take first worksheet - worksheet = 1; - urls = { - worksheet : 'https://spreadsheets.google.com/feeds/list/'+ key +'/'+ worksheet +'/public/values?alt=json', - spreadsheet: 'https://spreadsheets.google.com/feeds/worksheets/'+ key +'/public/basic?alt=json' - }; - } - - return urls; - }; -}(this.recline.Backend.GDocs)); diff --git a/test/backend.gdocs.test.js b/test/backend.gdocs.test.js deleted file mode 100644 index 0e860b9c..00000000 --- a/test/backend.gdocs.test.js +++ /dev/null @@ -1,320 +0,0 @@ -(function ($) { -module("Backend GDocs"); - -var sampleGDocsSpreadsheetMetadata = { - feed: { - category: [ - { - term: "http://schemas.google.com/spreadsheets/2006#worksheet", - scheme: "http://schemas.google.com/spreadsheets/2006" - } - ], - updated: { - $t: "2010-07-13T09:57:28.408Z" - }, - xmlns: "http://www.w3.org/2005/Atom", - title: { - $t: "javascript-test", - type: "text" - }, - author: [ - { - name: { - $t: "okfn.rufus.pollock" - }, - email: { - $t: "okfn.rufus.pollock@gmail.com" - } - } - ], - openSearch$startIndex: { - $t: "1" - }, - xmlns$gs: "http://schemas.google.com/spreadsheets/2006", - xmlns$openSearch: "http://a9.com/-/spec/opensearchrss/1.0/", - entry: [ - { - category: [ - { - term: "http://schemas.google.com/spreadsheets/2006#worksheet", - scheme: "http://schemas.google.com/spreadsheets/2006" - } - ], - updated: { - $t: "2010-07-13T09:57:28.408Z" - }, - title: { - $t: "Sheet1", - type: "text" - }, - content: { - $t: "Sheet1", - type: "text" - }, - link: [ - { - href: "https://spreadsheets.google.com/feeds/list/0Aon3JiuouxLUdDQwZE1JdV94cUd6NWtuZ0IyWTBjLWc/od6/public/basic", - type: "application/atom+xml", - rel: "http://schemas.google.com/spreadsheets/2006#listfeed" - }, - { - href: "https://spreadsheets.google.com/feeds/cells/0Aon3JiuouxLUdDQwZE1JdV94cUd6NWtuZ0IyWTBjLWc/od6/public/basic", - type: "application/atom+xml", - rel: "http://schemas.google.com/spreadsheets/2006#cellsfeed" - }, - { - href: "https://spreadsheets.google.com/tq?key=0Aon3JiuouxLUdDQwZE1JdV94cUd6NWtuZ0IyWTBjLWc&sheet=od6&pub=1", - type: "application/atom+xml", - rel: "http://schemas.google.com/visualization/2008#visualizationApi" - }, - { - href: "https://spreadsheets.google.com/feeds/worksheets/0Aon3JiuouxLUdDQwZE1JdV94cUd6NWtuZ0IyWTBjLWc/public/basic/od6", - type: "application/atom+xml", - rel: "self" - } - ], - id: { - $t: "https://spreadsheets.google.com/feeds/worksheets/0Aon3JiuouxLUdDQwZE1JdV94cUd6NWtuZ0IyWTBjLWc/public/basic/od6" - } - } - ], - link: [ - { - href: "https://spreadsheets.google.com/pub?key=0Aon3JiuouxLUdDQwZE1JdV94cUd6NWtuZ0IyWTBjLWc", - type: "text/html", - rel: "alternate" - }, - { - href: "https://spreadsheets.google.com/feeds/worksheets/0Aon3JiuouxLUdDQwZE1JdV94cUd6NWtuZ0IyWTBjLWc/public/basic", - type: "application/atom+xml", - rel: "http://schemas.google.com/g/2005#feed" - }, - { - href: "https://spreadsheets.google.com/feeds/worksheets/0Aon3JiuouxLUdDQwZE1JdV94cUd6NWtuZ0IyWTBjLWc/public/basic?alt=json", - type: "application/atom+xml", - rel: "self" - } - ], - openSearch$totalResults: { - $t: "1" - }, - id: { - $t: "https://spreadsheets.google.com/feeds/worksheets/0Aon3JiuouxLUdDQwZE1JdV94cUd6NWtuZ0IyWTBjLWc/public/basic" - } - }, - version: "1.0", - encoding: "UTF-8" -} - -var sampleGDocsSpreadsheetData = { - feed: { - category: [ - { - term: "http://schemas.google.com/spreadsheets/2006#list", - scheme: "http://schemas.google.com/spreadsheets/2006" - } - ], - updated: { - $t: "2010-07-12T18:32:16.200Z" - }, - xmlns: "http://www.w3.org/2005/Atom", - xmlns$gsx: "http://schemas.google.com/spreadsheets/2006/extended", - title: { - $t: "Sheet1", - type: "text" - }, - author: [ - { - name: { - $t: "okfn.rufus.pollock" - }, - email: { - $t: "okfn.rufus.pollock@gmail.com" - } - } - ], - openSearch$startIndex: { - $t: "1" - }, - link: [ - { - href: "http://spreadsheets.google.com/pub?key=0Aon3JiuouxLUdDQwZE1JdV94cUd6NWtuZ0IyWTBjLWc", - type: "text/html", - rel: "alternate" - }, - { - href: "http://spreadsheets.google.com/feeds/list/0Aon3JiuouxLUdDQwZE1JdV94cUd6NWtuZ0IyWTBjLWc/od6/public/values", - type: "application/atom+xml", - rel: "http://schemas.google.com/g/2005#feed" - }, - { - href: "http://spreadsheets.google.com/feeds/list/0Aon3JiuouxLUdDQwZE1JdV94cUd6NWtuZ0IyWTBjLWc/od6/public/values?alt=json-in-script", - type: "application/atom+xml", - rel: "self" - } - ], - xmlns$openSearch: "http://a9.com/-/spec/opensearchrss/1.0/", - entry: [ - { - category: [ - { - term: "http://schemas.google.com/spreadsheets/2006#list", - scheme: "http://schemas.google.com/spreadsheets/2006" - } - ], - updated: { - $t: "2010-07-12T18:32:16.200Z" - }, - 'gsx$column-2': { - $t: "1" - }, - 'gsx$column-1': { - $t: "A" - }, - title: { - $t: "A", - type: "text" - }, - content: { - $t: "column-2: 1", - type: "text" - }, - link: [ - { - href: "http://spreadsheets.google.com/feeds/list/0Aon3JiuouxLUdDQwZE1JdV94cUd6NWtuZ0IyWTBjLWc/od6/public/values/cokwr", - type: "application/atom+xml", - rel: "self" - } - ], - id: { - $t: "http://spreadsheets.google.com/feeds/list/0Aon3JiuouxLUdDQwZE1JdV94cUd6NWtuZ0IyWTBjLWc/od6/public/values/cokwr" - } - }, - { - category: [ - { - term: "http://schemas.google.com/spreadsheets/2006#list", - scheme: "http://schemas.google.com/spreadsheets/2006" - } - ], - updated: { - $t: "2010-07-12T18:32:16.200Z" - }, - 'gsx$column-2': { - $t: "2" - }, - 'gsx$column-1': { - $t: "b" - }, - title: { - $t: "b", - type: "text" - }, - content: { - $t: "column-2: 2", - type: "text" - }, - link: [ - { - href: "http://spreadsheets.google.com/feeds/list/0Aon3JiuouxLUdDQwZE1JdV94cUd6NWtuZ0IyWTBjLWc/od6/public/values/cpzh4", - type: "application/atom+xml", - rel: "self" - } - ], - id: { - $t: "http://spreadsheets.google.com/feeds/list/0Aon3JiuouxLUdDQwZE1JdV94cUd6NWtuZ0IyWTBjLWc/od6/public/values/cpzh4" - } - }, - { - category: [ - { - term: "http://schemas.google.com/spreadsheets/2006#list", - scheme: "http://schemas.google.com/spreadsheets/2006" - } - ], - updated: { - $t: "2010-07-12T18:32:16.200Z" - }, - 'gsx$column-2': { - $t: "3" - }, - 'gsx$column-1': { - $t: "c" - }, - title: { - $t: "c", - type: "text" - }, - content: { - $t: "column-2: 3", - type: "text" - }, - link: [ - { - href: "http://spreadsheets.google.com/feeds/list/0Aon3JiuouxLUdDQwZE1JdV94cUd6NWtuZ0IyWTBjLWc/od6/public/values/cre1l", - type: "application/atom+xml", - rel: "self" - } - ], - id: { - $t: "http://spreadsheets.google.com/feeds/list/0Aon3JiuouxLUdDQwZE1JdV94cUd6NWtuZ0IyWTBjLWc/od6/public/values/cre1l" - } - } - ], - openSearch$totalResults: { - $t: "3" - }, - id: { - $t: "http://spreadsheets.google.com/feeds/list/0Aon3JiuouxLUdDQwZE1JdV94cUd6NWtuZ0IyWTBjLWc/od6/public/values" - } - }, - version: "1.0", - encoding: "UTF-8" -} - -test("GDocs Backend", function() { - var dataset = new recline.Model.Dataset({ - url: 'https://spreadsheets.google.com/feeds/list/0Aon3JiuouxLUdDQwZE1JdV94cUd6NWtuZ0IyWTBjLWc/od6/public/values?alt=json', - backend: 'gdocs' - }); - - var stub = sinon.stub($, 'getJSON', function(options, cb) { - var spreadsheetUrl = 'spreadsheets.google.com/feeds/worksheets/'; - var worksheetUrl = 'spreadsheets.google.com/feeds/list/'; - - if (options.indexOf(spreadsheetUrl) !== -1) { - cb(sampleGDocsSpreadsheetMetadata) - } - else if(options.indexOf(worksheetUrl) !== -1) { - cb(sampleGDocsSpreadsheetData) - } - }); - - dataset.fetch().then(function() { - var docList = dataset.records; - deepEqual(['column-2', 'column-1'], _.pluck(dataset.fields.toJSON(), 'id')); - equal(3, docList.length); - equal("A", docList.models[0].get('column-1')); - equal('javascript-test :: Sheet1', dataset.get('title')); - }); - $.getJSON.restore(); -}); - -test("GDocs Backend.getUrl", function() { - var key = 'Abc_dajkdkjdafkj'; - var gid = 0; - var worksheet = 1; - var url = 'https://docs.google.com/spreadsheet/ccc?key=' + key + '#gid=' + gid - var out = recline.Backend.GDocs.getGDocsAPIUrls(url); - var exp1 = 'https://spreadsheets.google.com/feeds/list/' + key + '/' + worksheet + '/public/values?alt=json' - var exp2 = 'https://spreadsheets.google.com/feeds/worksheets/' + key + '/public/basic?alt=json' - equal(exp1, out.worksheet); - equal(exp2, out.spreadsheet); - - var url = 'https://docs.google.com/spreadsheet/ccc?key=' + key; - var out = recline.Backend.GDocs.getGDocsAPIUrls(url); - equal(out.worksheet, exp1); -}); - -})(this.jQuery); - diff --git a/test/index.html b/test/index.html index 1c103cac..eb0cf8d8 100644 --- a/test/index.html +++ b/test/index.html @@ -40,13 +40,11 @@ - -