1 /* 
  2  * Timemap.js Copyright 2010 Nick Rabinowitz.
  3  * Licensed under the MIT License (see LICENSE.txt)
  4  */
  5 
  6 /**
  7  * @fileOverview
  8  * Google Spreadsheet Loader
  9  *
 10  * @author Nick Rabinowitz (www.nickrabinowitz.com)
 11  */
 12 
 13 // for JSLint
 14 /*global TimeMap, TimeMapItem */
 15  
 16 /**
 17  * @class
 18  * Google Spreadsheet loader.
 19  *
 20  * <p>This is a loader for data from Google Spreadsheets. The constructor takes an optional map
 21  * to indicate which columns contain which data elements; the default column
 22  * names (case-insensitive) are: title, description, start, end, lat, lon</p>
 23  * 
 24  * <p>See <a href="http://code.google.com/apis/spreadsheets/docs/2.0/reference.html#gsx_reference">http://code.google.com/apis/spreadsheets/docs/2.0/reference.html#gsx_reference</a>
 25  * for details on how spreadsheet column ids are derived. Note that date fields 
 26  * must be in yyyy-mm-dd format - you may need to set the cell format as "plain text" 
 27  * in the spreadsheet (Google's automatic date formatting won't work).</p>
 28  *
 29  * <p>The loader takes either a full URL, minus the JSONP callback function, or 
 30  * just the spreadsheet key. Note that the spreadsheet must be published.</p>
 31  *
 32  * @augments TimeMap.loaders.jsonp
 33  * @requires param.js
 34  * @requires loaders/json.js
 35  *
 36  * @example
 37 TimeMap.init({
 38     datasets: [
 39         {
 40             title: "Google Spreadsheet by key",
 41             type: "gss",
 42             options: {
 43                 key: "pjUcDAp-oNIOjmx3LCxT4XA" // Spreadsheet key
 44             }
 45         },
 46         {
 47             title: "Google Spreadsheet by url",
 48             type: "gss",
 49             options: {
 50                 url: "http://spreadsheets.google.com/feeds/list/pjUcDAp-oNIOjmx3LCxT4XA/1/public/values?alt=json-in-script&callback="
 51             }
 52         }
 53     ],
 54     // etc...
 55 });
 56  * @see <a href="../../examples/google_spreadsheet.html">Google Spreadsheet Example</a>
 57  * @see <a href="../../examples/google_spreadsheet_columns.html">Google Spreadsheet Example, Arbitrary Columns</a>
 58  *
 59  * @param {Object} options          All options for the loader:
 60  * @param {String} options.key                      Key of spreadsheet to load, or
 61  * @param {String} options.url                      Full JSONP url of spreadsheet to load
 62  * @param {Object} [options.paramMap]               Map of paramName:columnName pairs for core parameters, 
 63  *                                                  if using non-standard column names; see keys in 
 64 *                                                   {@link TimeMap.loaders.base#params} for the standard param names
 65  * @param {String[]} [options.extraColumns]         Array of additional columns to load; all named columns will be 
 66  *                                                  loaded into the item.opts object.
 67  * @param {mixed} [options[...]]    Other options (see {@link TimeMap.loaders.jsonp})
 68  */
 69 TimeMap.loaders.gss = function(options) {
 70     var loader = new TimeMap.loaders.jsonp(options),
 71         params = loader.params, paramName, x,
 72         setParamField = TimeMap.loaders.gss.setParamField,
 73         paramMap = options.paramMap || {},
 74         extraColumns = options.extraColumns || [];
 75     
 76     // use key if no URL was supplied
 77     if (!loader.url) {
 78         loader.url = "http://spreadsheets.google.com/feeds/list/" + 
 79             options.key + "/1/public/values?alt=json-in-script&callback=";
 80     }
 81     
 82     // Set up additional columns
 83     for (x=0; x < extraColumns.length; x++) {
 84         paramName = extraColumns[x];
 85         params[paramName] = new TimeMap.params.OptionParam(paramName);
 86     }
 87     
 88     // Set up parameters to work with Google Spreadsheets
 89     for (paramName in params) {
 90         if (params.hasOwnProperty(paramName)) {
 91             fieldName = paramMap[paramName] || paramName;
 92             setParamField(params[paramName], fieldName);
 93         }
 94     }
 95     
 96     /**
 97      * Preload function for spreadsheet data
 98      * @name TimeMap.loaders.gss#preload
 99      * @function
100      * @parameter {Object} data     Data to preload
101      * @return {Array} data         Array of item data
102      */
103     loader.preload = function(data) {
104         return data.feed.entry;
105     };
106     
107     /**
108      * Transform function for spreadsheet data
109      * @name TimeMap.loaders.gss#transform
110      * @function
111      * @parameter {Object} data     Data to transform
112      * @return {Object} data        Transformed data for one item
113      */
114     loader.transform = function(data) {
115         var item = {}, params = loader.params, paramName,
116             transform = options.transformFunction;
117         // run through parameters, loading each
118         for (paramName in params) {
119             if (params.hasOwnProperty(paramName)) {
120                 params[paramName].setConfigGSS(item, data);
121             }
122         }
123         // hook for further transformation
124         if (transform) {
125             item = transform(item);
126         }
127         return item;
128     };
129 
130     return loader;
131 };
132 
133 /**
134  * Set a parameter to get its value from a given Google Spreadsheet field.
135  *
136  * @param {TimeMap.Param} param     Param object
137  * @param {String} fieldName        Name of the field
138  */
139 TimeMap.loaders.gss.setParamField = function(param, fieldName) {
140     // internal function: Get the value of a Google Spreadsheet field
141     var getField = function(data, fieldName) {
142         // get element, converting field name to GSS format
143         var el = data['gsx$' + fieldName.toLowerCase().replace(" ", "")];
144         if (el) {
145             return el.$t;
146         }
147         return null;
148     };
149     // set the method on the parameter
150     param.setConfigGSS = function(config, data) {
151         this.setConfig(config, getField(data, fieldName));
152     };
153 };
154