| OLD | NEW |
| (Empty) | |
| 1 <link rel="import" href="../polymer/polymer.html"> |
| 2 <link rel="import" href="../iron-ajax/iron-ajax.html"> |
| 3 <link rel="import" href="../google-signin/google-signin-aware.html"> |
| 4 |
| 5 <!-- |
| 6 Element for interacting with Google Sheets. |
| 7 |
| 8 `<google-sheets>` pulls cell data from the Google Sheet specified by `key`. |
| 9 A spreadsheet's key can be found in the URL when viewing it in google docs (e.g.
`docs.google.com/spreadsheet/ccc?key=<KEY>#gid=12345`). |
| 10 |
| 11 Optionally, pass the `tab-id` attribute to specify a particular worksheet tab in
the spreadsheet. For example, the first tab would be `tab-id="1"`. If `tab` is
updated at a later time, the underlying data is also updated. **API calls are ca
ched** as to not make extraneous calls. |
| 12 |
| 13 See [developers.google.com/google-apps/spreadsheets](https://developers.google.c
om/google-apps/spreadsheets) for full Spreadsheets API documentation. |
| 14 |
| 15 #### Example |
| 16 |
| 17 <google-sheets key="..." tab-id="1" client-id="..."></google-sheets> |
| 18 |
| 19 <script> |
| 20 var sheet = document.querySelector('google-sheets'); |
| 21 |
| 22 sheet.addEventListener('google-sheet-data', function(e) { |
| 23 // this.spreadsheets - list of the user's spreadsheets |
| 24 // this.tab - information on the tab that was fetched |
| 25 // this.rows - cell row information for the tab that was fetched |
| 26 }); |
| 27 |
| 28 sheet.addEventListener('error', function(e) { |
| 29 // e.detail.response |
| 30 }); |
| 31 </script> |
| 32 |
| 33 <b>Example</b> - `published` is a perf optimization and hints that the spreadshe
et has been published (public): |
| 34 |
| 35 <google-sheets key="0Anye-JMjUkZZdDBkMVluMEhZMmFGeHpYdDJJV1FBRWc" published>
</google-sheets> |
| 36 |
| 37 <b>Example</b> - leaving off the `key` returns as list of the user's spreadsheet
s. |
| 38 |
| 39 <google-sheets client-id="..."></google-sheets> |
| 40 |
| 41 <b>Example</b> - show a list of Map markers, using data-binding features inside
Polymer: |
| 42 |
| 43 <google-sheets |
| 44 key="0Anye-JMjUkZZdDBkMVluMEhZMmFGeHpYdDJJV1FBRWc" tab-id="1" rows="{{rows
}}" |
| 45 client-id="..."> |
| 46 </google-sheets> |
| 47 <google-map> |
| 48 <template is="dom-bind"> |
| 49 <google-map-marker latitude="{{gsx$lat.$t}}" longitude="{{gsx$lng.$t}}"> |
| 50 </template> |
| 51 </google-map> |
| 52 |
| 53 @demo |
| 54 --> |
| 55 |
| 56 |
| 57 <dom-module id="google-sheets"> |
| 58 <template> |
| 59 <template if="{{!published}}"> |
| 60 <google-signin-aware client-id="{{clientId}}" |
| 61 scopes="https://spreadsheets.google.com/feeds" |
| 62 on-google-signin-aware-success="_onSignInSuccess" |
| 63 on-google-signin-aware-signed-out="_onSignInFail"></google-
signin-aware> |
| 64 </template> |
| 65 |
| 66 <iron-ajax id="publicajax" params='{"alt": "json"}' handle-as="json" |
| 67 on-response="_onCellRows"></iron-ajax> |
| 68 <iron-ajax id="listsheetsajax" params='{"alt": "json"}' handle-as="json" |
| 69 on-response="_onSpreadsheetList"></iron-ajax> |
| 70 <iron-ajax id="worksheetajax" params='{"alt": "json"}' handle-as="json" |
| 71 on-response="_onWorksheet"></iron-ajax> |
| 72 <iron-ajax id="cellrowsajax" params='{"alt": "json"}' handle-as="json" |
| 73 on-response="_onCellRows"></iron-ajax> |
| 74 |
| 75 </template> |
| 76 </dom-module> |
| 77 |
| 78 <script> |
| 79 (function() { |
| 80 var SCOPE_ = 'https://spreadsheets.google.com/feeds'; |
| 81 |
| 82 // Minimal cache for worksheet row data. Shared across instances so subsequent |
| 83 // accesses are fast and API calls only happen once. |
| 84 var rowDataCache_ = {}; |
| 85 |
| 86 function generateCacheKey_() { |
| 87 return this.worksheetId_ + '_'+ this.tabId; |
| 88 } |
| 89 |
| 90 function getLink_(rel, links) { |
| 91 for (var i = 0, link; link = links[i]; ++i) { |
| 92 if (link.rel === rel) { |
| 93 return link; |
| 94 } |
| 95 } |
| 96 return null; |
| 97 } |
| 98 |
| 99 // Conversion of Worksheet Ids to GIDs and vice versa |
| 100 // od4 > 2 |
| 101 function wid_to_gid_(wid) { |
| 102 return parseInt(String(wid), 36) ^ 31578; |
| 103 } |
| 104 // 2 > 0d4 |
| 105 function gid_to_wid_(gid) { |
| 106 // (gid xor 31578) encoded in base 36 |
| 107 return parseInt((gid ^ 31578)).toString(36); |
| 108 } |
| 109 |
| 110 window.GoogleSheets = Polymer({ |
| 111 |
| 112 is: 'google-sheets', |
| 113 |
| 114 /** |
| 115 Fired when the spreadsheet's cell information is available. |
| 116 |
| 117 @event google-sheet-data |
| 118 @param {Object} detail |
| 119 @param {Object} detail.data The data returned by the Spreadsheet API. |
| 120 @param {string} detail.type The type of data that was fetched. One of 'spreadshe
ets', 'tab', 'rows' to correspond to the feed type. |
| 121 */ |
| 122 properties: { |
| 123 /** |
| 124 * A Google Developers client ID. Obtain from [console.developers.google.c
om](https://console.developers.google.com). Required for accessing a private spr
eadsheet. Optional if accessing a public spreadsheet. |
| 125 */ |
| 126 clientId: { |
| 127 type: String, |
| 128 value: '', |
| 129 observer: '_configUpdate' |
| 130 }, |
| 131 |
| 132 /** |
| 133 * The key of the spreadsheet. This can be found in the URL when viewing |
| 134 * the document is Google Docs (e.g. `docs.google.com/spreadsheet/ccc?key=
<KEY>`). |
| 135 * |
| 136 * Leaving off this attribute still returns a list of the users spreadshee
ts in the `spreadsheets` property. |
| 137 */ |
| 138 key: { |
| 139 type: String, |
| 140 value: '', |
| 141 observer: '_keyChanged' |
| 142 }, |
| 143 |
| 144 /** |
| 145 * Tab within a spreadsheet. For example, the first tab in a spreadsheet |
| 146 * would be `tab-id="1"`. |
| 147 */ |
| 148 tabId: { |
| 149 type: Number, |
| 150 value: 1, |
| 151 observer: '_configUpdate' |
| 152 }, |
| 153 |
| 154 /** |
| 155 * A hint that the spreadsheet is published publicly in Google Docs. Used
as a performance optimization. |
| 156 * Make sure the sheet is also publicly viewable by anyone in the Share se
ttings. |
| 157 * |
| 158 * @attribute published |
| 159 * @type boolean |
| 160 * @default false |
| 161 */ |
| 162 published: { |
| 163 type: Boolean, |
| 164 value: false, |
| 165 observer: '_configUpdate' |
| 166 }, |
| 167 |
| 168 _worksheetId: { |
| 169 type: String, |
| 170 value: null, |
| 171 readOnly: true |
| 172 }, |
| 173 |
| 174 /** |
| 175 * The fetched sheet corresponding to the `key` attribute. |
| 176 */ |
| 177 sheet: { |
| 178 type: Object, |
| 179 value: function() { return {}; }, |
| 180 readOnly: true, |
| 181 notify: true, |
| 182 observer: '_sheetChanged' |
| 183 }, |
| 184 |
| 185 /** |
| 186 * Meta data about the particular tab that was retrieved for the spreadshe
et. |
| 187 */ |
| 188 tab: { |
| 189 type: Object, |
| 190 value: function() { return {}; }, |
| 191 readOnly: true, |
| 192 notify: true, |
| 193 observer: '_tabChanged' |
| 194 }, |
| 195 |
| 196 /** |
| 197 * If a spreadsheet `key` is specified, returns a list of cell row data. |
| 198 */ |
| 199 rows: { |
| 200 type: Array, |
| 201 value: function() { return []; }, |
| 202 readOnly: true, |
| 203 notify: true |
| 204 }, |
| 205 |
| 206 /** |
| 207 * List of the user's spreadsheets. Shared across instances. |
| 208 */ |
| 209 spreadsheets: { |
| 210 type: Array, |
| 211 readOnly: true, |
| 212 value: function() { return []; } |
| 213 }, |
| 214 |
| 215 /** |
| 216 * The URL to open this spreadsheet in Google Sheets. |
| 217 */ |
| 218 openInGoogleDocsUrl: { |
| 219 type: String, |
| 220 computed: '_computeGoogleDocsUrl(key)', |
| 221 notify: true |
| 222 } |
| 223 }, |
| 224 |
| 225 _computeGoogleDocsUrl: function(key) { |
| 226 var url = 'https://docs.google.com/spreadsheet/'; |
| 227 if (key) { |
| 228 url += 'ccc?key=' + key; |
| 229 } |
| 230 return url; |
| 231 }, |
| 232 |
| 233 _configUpdate: function(key, published, tabId, clientId) { |
| 234 this._tabIdChanged(); |
| 235 }, |
| 236 |
| 237 _keyChanged: function(newValue, oldValue) { |
| 238 // TODO(ericbidelman): need to better handle updates to the key attribute. |
| 239 // Below doesn't account for private feeds. |
| 240 if (this.published) { |
| 241 var url = SCOPE_ + '/list/' + this.key + '/' + |
| 242 this.tabId + '/public/values'; |
| 243 this.$.publicajax.url = url; |
| 244 this.$.publicajax.generateRequest(); |
| 245 } |
| 246 }, |
| 247 |
| 248 _tabIdChanged: function(newValue, oldValue) { |
| 249 if (this.worksheetId_) { |
| 250 this._getCellRows(); |
| 251 } else if (this.published) { |
| 252 this._keyChanged(); |
| 253 } |
| 254 }, |
| 255 |
| 256 _sheetChanged: function(newValue, oldValue) { |
| 257 if (!this.sheet.title) { |
| 258 return; |
| 259 } |
| 260 |
| 261 // Make metadata easily accessible on sheet object. |
| 262 var authors = this.sheet.author && this.sheet.author.map(function(a) { |
| 263 return {email: a.email.$t, name: a.name.$t}; |
| 264 }); |
| 265 |
| 266 this.set('sheet.title', this.sheet.title.$t); |
| 267 this.set('sheet.updated', new Date(this.sheet.updated.$t)); |
| 268 this.set('sheet.authors', authors); |
| 269 |
| 270 //this.worksheetId_ = this.sheet.id.$t.split('/').slice(-1)[0]; |
| 271 this._setWorksheetId_(this.sheet.id.$t.split('/').slice(-1)[0]); |
| 272 this._getWorksheet(); |
| 273 }, |
| 274 |
| 275 _tabChanged: function(newValue, oldValue) { |
| 276 if (!this.tab.title) { |
| 277 return; |
| 278 } |
| 279 |
| 280 var authors = this.tab.authors = this.tab.author && this.tab.author.map(fu
nction(a) { |
| 281 return {email: a.email.$t, name: a.name.$t}; |
| 282 }); |
| 283 |
| 284 this.set('tab.title', this.tab.title.$t); |
| 285 this.set('tab.updated', new Date(this.tab.updated.$t)); |
| 286 this.set('tab.authors', authors); |
| 287 |
| 288 this.fire('google-sheet-data', { |
| 289 type: 'tab', |
| 290 data: this.tab |
| 291 }); |
| 292 }, |
| 293 |
| 294 _onSignInSuccess: function(e, detail) { |
| 295 var oauthToken = gapi.auth.getToken(); |
| 296 |
| 297 var headers = { |
| 298 'Authorization': 'Bearer ' + oauthToken.access_token |
| 299 }; |
| 300 |
| 301 this.$.listsheetsajax.headers = headers; |
| 302 this.$.worksheetajax.headers = headers; |
| 303 this.$.cellrowsajax.headers = headers; |
| 304 |
| 305 // TODO(ericbidelman): don't make this call if this.spreadsheets is |
| 306 // already populated from another instance. |
| 307 this._listSpreadsheets(); |
| 308 }, |
| 309 |
| 310 _onSignInFail: function(e, detail) { |
| 311 // TODO(ericbidelman): handle this in some way. |
| 312 console.log(e, e.type); |
| 313 }, |
| 314 |
| 315 _listSpreadsheets: function() { |
| 316 var url = SCOPE_ + '/spreadsheets/private/full'; |
| 317 this.$.listsheetsajax.url = url; |
| 318 this.$.listsheetsajax.generateRequest(); |
| 319 }, |
| 320 |
| 321 _onSpreadsheetList: function(e) { |
| 322 e.stopPropagation(); |
| 323 |
| 324 var feed = e.target.lastResponse.feed; |
| 325 |
| 326 this.spreadsheets = feed.entry; |
| 327 |
| 328 this.fire('google-sheet-data', { |
| 329 type: 'spreadsheets', |
| 330 data: this.spreadsheets |
| 331 }); |
| 332 |
| 333 // Fetch worksheet feed if key was given and worksheet exists. |
| 334 if (this.key) { |
| 335 for (var i = 0, entry; entry = feed.entry[i]; ++i) { |
| 336 var altLink = getLink_('alternate', entry.link); |
| 337 if (altLink && altLink.href.indexOf(this.key) != -1) { |
| 338 this.sheet = entry; |
| 339 break; |
| 340 } |
| 341 } |
| 342 } |
| 343 }, |
| 344 |
| 345 _getWorksheet: function() { |
| 346 if (!this.worksheetId_) { |
| 347 throw new Error('workesheetId was not given.'); |
| 348 } |
| 349 |
| 350 var url = SCOPE_ + '/worksheets/' + this.worksheetId_ + |
| 351 '/private/full/' + this.tabId; |
| 352 this.$.worksheetajax.url = url; |
| 353 this.$.worksheetajax.generateRequest(); |
| 354 }, |
| 355 |
| 356 _onWorksheet: function(e) { |
| 357 e.stopPropagation(); |
| 358 |
| 359 // this.tab = e.target.lastResponse.entry; |
| 360 this._setTab(e.target.lastResponse.entry); |
| 361 this._getCellRows(); |
| 362 }, |
| 363 |
| 364 _getCellRows: function() { |
| 365 // Use cached data if available. |
| 366 var key = generateCacheKey_.call(this); |
| 367 if (key in rowDataCache_) { |
| 368 this._onCellRows(null, null, rowDataCache_[key]); |
| 369 |
| 370 return; |
| 371 } |
| 372 |
| 373 var url = SCOPE_ + '/list/' + |
| 374 this.worksheetId_ + '/' + this.tabId + |
| 375 '/private/full'; |
| 376 this.$.cellrowsajax.url = url; |
| 377 this.$.cellrowsajax.generateRequest(); |
| 378 }, |
| 379 |
| 380 _onCellRows: function(e) { |
| 381 e.stopPropagation(); |
| 382 |
| 383 var feed = e.target.lastResponse.feed; |
| 384 |
| 385 // Cache data if key doesn't exist. |
| 386 var key = generateCacheKey_.call(this); |
| 387 if (!(key in rowDataCache_)) { |
| 388 rowDataCache_[key] = {response: {feed: feed}}; |
| 389 } |
| 390 |
| 391 // this.rows = feed.entry; |
| 392 this._setRows(feed.entry); |
| 393 var authors = feed.author && feed.author.map(function(a) { |
| 394 return {email: a.email.$t, name: a.name.$t}; |
| 395 }); |
| 396 this.set('rows.authors', authors); |
| 397 |
| 398 if (this.published) { |
| 399 // this.tab = feed; |
| 400 this._setTab(feed); |
| 401 } |
| 402 |
| 403 this.fire('google-sheet-data', { |
| 404 type: 'rows', |
| 405 data: this.rows |
| 406 }); |
| 407 } |
| 408 |
| 409 }); |
| 410 |
| 411 })(); |
| 412 </script> |
| OLD | NEW |