Commit 525c13d8940ad0cb94ee66117553fe47ef6c4191
1 parent
ac237fdd
google component tests
Showing
24 changed files
with
2839 additions
and
0 deletions
bower_components/google-sheets/.bower.json
0 → 100644
| 1 | +{ | ||
| 2 | + "name": "google-sheets", | ||
| 3 | + "version": "1.0.5", | ||
| 4 | + "homepage": "https://googlewebcomponents.github.io/google-sheets", | ||
| 5 | + "description": "Web components to interact with Google Sheets", | ||
| 6 | + "main": "google-sheets.html", | ||
| 7 | + "authors": [ | ||
| 8 | + "Eric Bidelman <ebidel@gmail.com>" | ||
| 9 | + ], | ||
| 10 | + "license": "Apache2", | ||
| 11 | + "ignore": [ | ||
| 12 | + "/.*", | ||
| 13 | + "/test/" | ||
| 14 | + ], | ||
| 15 | + "keywords": [ | ||
| 16 | + "web-component", | ||
| 17 | + "web-components", | ||
| 18 | + "polymer", | ||
| 19 | + "spreadsheets", | ||
| 20 | + "google" | ||
| 21 | + ], | ||
| 22 | + "dependencies": { | ||
| 23 | + "polymer": "Polymer/polymer#^1.1.2", | ||
| 24 | + "google-apis": "GoogleWebComponents/google-apis#^1.1.0", | ||
| 25 | + "google-signin": "GoogleWebComponents/google-signin#^1.0.3", | ||
| 26 | + "iron-ajax": "PolymerElements/iron-ajax#^1.0.0" | ||
| 27 | + }, | ||
| 28 | + "devDependencies": { | ||
| 29 | + "iron-component-page": "PolymerElements/iron-component-page#^1.0.0", | ||
| 30 | + "google-map": "GoogleWebComponents/google-map#^1.1.0", | ||
| 31 | + "iron-flex-layout": "PolymerElements/iron-flex-layout#^1.0.0", | ||
| 32 | + "web-component-tester": "*" | ||
| 33 | + }, | ||
| 34 | + "_release": "1.0.5", | ||
| 35 | + "_resolution": { | ||
| 36 | + "type": "version", | ||
| 37 | + "tag": "1.0.5", | ||
| 38 | + "commit": "1e87081a4abfdd31cf7cbf4ac970c7f9873c447d" | ||
| 39 | + }, | ||
| 40 | + "_source": "git://github.com/GoogleWebComponents/google-sheets.git", | ||
| 41 | + "_target": "~1.0.5", | ||
| 42 | + "_originalSource": "GoogleWebComponents/google-sheets", | ||
| 43 | + "_direct": true | ||
| 44 | +} | ||
| 0 | \ No newline at end of file | 45 | \ No newline at end of file |
bower_components/google-sheets/LICENSE
0 → 100644
| 1 | +Copyright 2014 Google Inc | ||
| 2 | + | ||
| 3 | +Licensed under the Apache License, Version 2.0 (the "License"); | ||
| 4 | +you may not use this file except in compliance with the License. | ||
| 5 | +You may obtain a copy of the License at | ||
| 6 | + | ||
| 7 | + https://www.apache.org/licenses/LICENSE-2.0 | ||
| 8 | + | ||
| 9 | +Unless required by applicable law or agreed to in writing, software | ||
| 10 | +distributed under the License is distributed on an "AS IS" BASIS, | ||
| 11 | +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
| 12 | +See the License for the specific language governing permissions and | ||
| 13 | +limitations under the License. |
bower_components/google-sheets/README.md
0 → 100755
bower_components/google-sheets/bower.json
0 → 100755
| 1 | +{ | ||
| 2 | + "name": "google-sheets", | ||
| 3 | + "version": "1.0.5", | ||
| 4 | + "homepage": "https://googlewebcomponents.github.io/google-sheets", | ||
| 5 | + "description": "Web components to interact with Google Sheets", | ||
| 6 | + "main": "google-sheets.html", | ||
| 7 | + "authors": [ | ||
| 8 | + "Eric Bidelman <ebidel@gmail.com>" | ||
| 9 | + ], | ||
| 10 | + "license": "Apache2", | ||
| 11 | + "ignore": [ | ||
| 12 | + "/.*", | ||
| 13 | + "/test/" | ||
| 14 | + ], | ||
| 15 | + "keywords": [ | ||
| 16 | + "web-component", | ||
| 17 | + "web-components", | ||
| 18 | + "polymer", | ||
| 19 | + "spreadsheets", | ||
| 20 | + "google" | ||
| 21 | + ], | ||
| 22 | + "dependencies": { | ||
| 23 | + "polymer": "Polymer/polymer#^1.1.2", | ||
| 24 | + "google-apis": "GoogleWebComponents/google-apis#^1.1.0", | ||
| 25 | + "google-signin": "GoogleWebComponents/google-signin#^1.0.3", | ||
| 26 | + "iron-ajax": "PolymerElements/iron-ajax#^1.0.0" | ||
| 27 | + }, | ||
| 28 | + "devDependencies": { | ||
| 29 | + "iron-component-page": "PolymerElements/iron-component-page#^1.0.0", | ||
| 30 | + "google-map": "GoogleWebComponents/google-map#^1.1.0", | ||
| 31 | + "iron-flex-layout": "PolymerElements/iron-flex-layout#^1.0.0", | ||
| 32 | + "web-component-tester": "*" | ||
| 33 | + } | ||
| 34 | +} |
bower_components/google-sheets/demo/demo-private.html
0 → 100644
| 1 | +<!doctype html> | ||
| 2 | +<!-- Copyright (c) 2015 Google Inc. All rights reserved. --> | ||
| 3 | +<html> | ||
| 4 | +<head> | ||
| 5 | + <meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes"> | ||
| 6 | + <title>google-sheets private data demo</title> | ||
| 7 | + <script src="../../webcomponentsjs/webcomponents-lite.min.js"></script> | ||
| 8 | + <link rel="import" href="../google-sheets.html"> | ||
| 9 | + <link rel="import" href="../../google-map/google-map.html"> | ||
| 10 | + <link rel="import" href="../../google-signin/google-signin.html"> | ||
| 11 | + <style> | ||
| 12 | + * { | ||
| 13 | + box-sizing: border-box; | ||
| 14 | + } | ||
| 15 | + body { | ||
| 16 | + margin: 2em; | ||
| 17 | + font-family: 'Roboto', 'Helvetica Neue', Helvetica, Arial; | ||
| 18 | + font-weight: 300; | ||
| 19 | + background-color: #f1f1f3; | ||
| 20 | + } | ||
| 21 | + a { | ||
| 22 | + text-decoration: none; | ||
| 23 | + color: blue; | ||
| 24 | + } | ||
| 25 | + ul { | ||
| 26 | + padding-left: 0; | ||
| 27 | + } | ||
| 28 | + ul, li { | ||
| 29 | + list-style: none; | ||
| 30 | + font-size: 14px; | ||
| 31 | + } | ||
| 32 | + section { | ||
| 33 | + border-radius: 3px; | ||
| 34 | + box-shadow: 1px 1px 3px #ccc; | ||
| 35 | + padding: 1em 2em; | ||
| 36 | + background-color: white; | ||
| 37 | + width: 500px; | ||
| 38 | + min-height: 500px; | ||
| 39 | + } | ||
| 40 | + main { | ||
| 41 | + justify-content: space-around; | ||
| 42 | + margin-top: 2em; | ||
| 43 | + } | ||
| 44 | + </style> | ||
| 45 | +</head> | ||
| 46 | +<body> | ||
| 47 | + | ||
| 48 | +<google-signin | ||
| 49 | + client-id="1054047045356-j8pgqgls9vdef3rl09hapoicumbte0bo.apps.googleusercontent.com" | ||
| 50 | + scopes="https://spreadsheets.google.com/feeds"> | ||
| 51 | +</google-signin> | ||
| 52 | + | ||
| 53 | +<p>A <code><google-sheets></code> element returning data from a <b>private</b> Google Spreadsheet:</p> | ||
| 54 | +<p><b>Note:</b> update the demo source to your clientId and private spreadsheet key for the demo to work.</p> | ||
| 55 | + | ||
| 56 | +<main class="layout horizontal"> | ||
| 57 | + | ||
| 58 | +<template id="spreadsheets" is="dom-bind"> | ||
| 59 | + | ||
| 60 | +<!-- Example: private spreadsheet --> | ||
| 61 | +<google-sheets id="sheet" tab-id="1" | ||
| 62 | + client-id="1054047045356-j8pgqgls9vdef3rl09hapoicumbte0bo.apps.googleusercontent.com" | ||
| 63 | + key="1QMGizivw3UJ3-R9BFK7sfrXE0RL87dygk2C0RcuKoDY" | ||
| 64 | + open-in-google-docs-url="{{openInGoogleDocsURL}}" | ||
| 65 | + tab="{{tab}}" | ||
| 66 | + spreadsheets="{{spreadsheets}}" | ||
| 67 | + rows="{{rows}}"></google-sheets> | ||
| 68 | + | ||
| 69 | +<section> | ||
| 70 | + | ||
| 71 | + <heading> | ||
| 72 | + <h3>List of spreadsheets</h3> | ||
| 73 | + </heading> | ||
| 74 | + | ||
| 75 | + <ul> | ||
| 76 | + <template is="dom-repeat" items="[[spreadsheets]]"> | ||
| 77 | + <li>{{item.title.$t}}</li> | ||
| 78 | + </template> | ||
| 79 | + </ul> | ||
| 80 | + | ||
| 81 | +</section> | ||
| 82 | + | ||
| 83 | +<section> | ||
| 84 | + | ||
| 85 | + <heading> | ||
| 86 | + <h3>Spreadsheet rows: | ||
| 87 | + <a href="{{openInGoogleDocsURL}}" target="_blank" title="Open in Google Docs →"> | ||
| 88 | + "<span>{{tab.title}}</span>" tab | ||
| 89 | + </a> | ||
| 90 | + </h3> | ||
| 91 | + <h5>updated: <span>{{tab.updated}}</span>, by: <template is="dom-repeat" items="{{rows.authors}}"><span>{{item.name}}</span></template></h5> | ||
| 92 | + </heading> | ||
| 93 | + <ul> | ||
| 94 | + <template is="dom-repeat" items="{{rows}}"> | ||
| 95 | + <li>Name: <span>{{item.gsx$name.$t}}</span> ( lat: <span>{{item.gsx$lat.$t}}</span>, lng: <span>{{item.gsx$lng.$t}}</span> )</li> | ||
| 96 | + </template> | ||
| 97 | + </ul> | ||
| 98 | + | ||
| 99 | +</section> | ||
| 100 | + | ||
| 101 | +</template> | ||
| 102 | + | ||
| 103 | +</main> | ||
| 104 | + | ||
| 105 | +</body> | ||
| 106 | +</html> |
bower_components/google-sheets/demo/index.html
0 → 100755
| 1 | +<!doctype html> | ||
| 2 | +<!-- Copyright (c) 2015 Google Inc. All rights reserved. --> | ||
| 3 | +<html> | ||
| 4 | +<head> | ||
| 5 | + <meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes"> | ||
| 6 | + <title>google-sheets Demo</title> | ||
| 7 | + <script src="../../webcomponentsjs/webcomponents-lite.min.js"></script> | ||
| 8 | + <link rel="import" href="../../iron-flex-layout/classes/iron-flex-layout.html"> | ||
| 9 | + <link rel="import" href="../google-sheets.html"> | ||
| 10 | + <link rel="import" href="../../google-map/google-map.html"> | ||
| 11 | + <style> | ||
| 12 | + * { | ||
| 13 | + box-sizing: border-box; | ||
| 14 | + } | ||
| 15 | + body { | ||
| 16 | + margin: 2em; | ||
| 17 | + font-family: 'Roboto', 'Helvetica Neue', Helvetica, Arial; | ||
| 18 | + font-weight: 300; | ||
| 19 | + background-color: #f1f1f3; | ||
| 20 | + } | ||
| 21 | + a { | ||
| 22 | + text-decoration: none; | ||
| 23 | + color: blue; | ||
| 24 | + } | ||
| 25 | + ul { | ||
| 26 | + padding-left: 0; | ||
| 27 | + } | ||
| 28 | + ul, li { | ||
| 29 | + list-style: none; | ||
| 30 | + font-size: 14px; | ||
| 31 | + } | ||
| 32 | + section { | ||
| 33 | + border-radius: 3px; | ||
| 34 | + box-shadow: 1px 1px 3px #ccc; | ||
| 35 | + padding: 1em 2em; | ||
| 36 | + background-color: white; | ||
| 37 | + width: 500px; | ||
| 38 | + height: 500px; | ||
| 39 | + } | ||
| 40 | + google-map { | ||
| 41 | + display: block; | ||
| 42 | + height: 100%; | ||
| 43 | + width: 100%; | ||
| 44 | + } | ||
| 45 | + main { | ||
| 46 | + justify-content: space-around; | ||
| 47 | + margin-top: 2em; | ||
| 48 | + } | ||
| 49 | + </style> | ||
| 50 | +</head> | ||
| 51 | +<body> | ||
| 52 | + | ||
| 53 | +<p>A <code><google-sheets></code> element returns data from a Google Spreadsheet:</p> | ||
| 54 | + | ||
| 55 | +<main class="layout horizontal"> | ||
| 56 | + | ||
| 57 | +<template id="container" is="dom-bind"> | ||
| 58 | + | ||
| 59 | + <section> | ||
| 60 | + | ||
| 61 | + <!-- Example: published spreadsheet --> | ||
| 62 | + <google-sheets id="sheet" key="0Anye-JMjUkZZdDBkMVluMEhZMmFGeHpYdDJJV1FBRWc" tab-id="1" | ||
| 63 | + published rows="{{rows}}" tab="{{tab}}" open-in-google-docs-url="{{openInGoogleDocsUrl}}"></google-sheets> | ||
| 64 | + | ||
| 65 | + <heading> | ||
| 66 | + <h3>Spreadsheet rows: | ||
| 67 | + <a href="{{openInGoogleDocsUrl}}" target="_blank" title="Open in Google Docs →"> | ||
| 68 | + "<span>{{tab.title}}</span>" tab | ||
| 69 | + </a> | ||
| 70 | + </h3> | ||
| 71 | + <h5>updated: <span>{{tab.updated}}</span>, by: <template is="dom-repeat" items="{{rows.authors}}">{{item.name}}</template></h5> | ||
| 72 | + </heading> | ||
| 73 | + <ul> | ||
| 74 | + <template is="dom-repeat" items="[[rows]]"> | ||
| 75 | + <li>Name: <span>{{item.gsx$name.$t}}</span> ( lat: <span>{{item.gsx$lat.$t}}</span>, lng: <span>{{item.gsx$lng.$t}}</span> )</li> | ||
| 76 | + </template> | ||
| 77 | + </ul> | ||
| 78 | + | ||
| 79 | + </section> | ||
| 80 | + | ||
| 81 | + <section style="padding: 0;"> | ||
| 82 | + | ||
| 83 | + <google-map disable-default-ui fit-to-markers> | ||
| 84 | + <template is="dom-repeat" items="[[rows]]"> | ||
| 85 | + <google-map-marker latitude="{{item.gsx$lat.$t}}" longitude="{{item.gsx$lng.$t}}"></google-map-marker> | ||
| 86 | + </template> | ||
| 87 | + </google-map> | ||
| 88 | + | ||
| 89 | + <button on-click="useTab" data-tabid="1">View tab 1 data</button> | ||
| 90 | + <button on-click="useTab" data-tabid="2">View tab 2 data</button> | ||
| 91 | + | ||
| 92 | + </section> | ||
| 93 | + | ||
| 94 | +</template> | ||
| 95 | +</main> | ||
| 96 | + | ||
| 97 | +<script> | ||
| 98 | +var template = document.querySelector('#container'); | ||
| 99 | + | ||
| 100 | +template.useTab = function(e, detail, sender) { | ||
| 101 | + document.querySelector('#sheet').tabId = Number(e.currentTarget.dataset.tabid); | ||
| 102 | +}; | ||
| 103 | +</script> | ||
| 104 | +</body> | ||
| 105 | +</html> |
bower_components/google-sheets/google-sheets.html
0 → 100755
| 1 | +<!-- Copyright (c) 2015 Google Inc. All rights reserved. --> | ||
| 2 | + | ||
| 3 | +<link rel="import" href="../polymer/polymer.html"> | ||
| 4 | +<link rel="import" href="../iron-ajax/iron-ajax.html"> | ||
| 5 | +<link rel="import" href="../google-signin/google-signin-aware.html"> | ||
| 6 | + | ||
| 7 | +<!-- | ||
| 8 | +Element for interacting with Google Sheets. | ||
| 9 | + | ||
| 10 | +`<google-sheets>` pulls cell data from the Google Sheet specified by `key`. | ||
| 11 | +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`). | ||
| 12 | + | ||
| 13 | +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 cached** as to not make extraneous calls. | ||
| 14 | + | ||
| 15 | +See [developers.google.com/google-apps/spreadsheets](https://developers.google.com/google-apps/spreadsheets) for full Spreadsheets API documentation. | ||
| 16 | + | ||
| 17 | +#### Example | ||
| 18 | + | ||
| 19 | + <google-sheets key="..." tab-id="1" client-id="..."></google-sheets> | ||
| 20 | + | ||
| 21 | + <script> | ||
| 22 | + var sheet = document.querySelector('google-sheets'); | ||
| 23 | + | ||
| 24 | + sheet.addEventListener('google-sheet-data', function(e) { | ||
| 25 | + // this.spreadsheets - list of the user's spreadsheets | ||
| 26 | + // this.tab - information on the tab that was fetched | ||
| 27 | + // this.rows - cell row information for the tab that was fetched | ||
| 28 | + }); | ||
| 29 | + | ||
| 30 | + sheet.addEventListener('error', function(e) { | ||
| 31 | + // e.detail.response | ||
| 32 | + }); | ||
| 33 | + </script> | ||
| 34 | + | ||
| 35 | +<b>Example</b> - `published` is a perf optimization and hints that the spreadsheet has been published (public): | ||
| 36 | + | ||
| 37 | + <google-sheets key="0Anye-JMjUkZZdDBkMVluMEhZMmFGeHpYdDJJV1FBRWc" published></google-sheets> | ||
| 38 | + | ||
| 39 | +<b>Example</b> - leaving off the `key` returns as list of the user's spreadsheets. | ||
| 40 | + | ||
| 41 | + <google-sheets client-id="..."></google-sheets> | ||
| 42 | + | ||
| 43 | +<b>Example</b> - show a list of Map markers, using data-binding features inside Polymer: | ||
| 44 | + | ||
| 45 | + <template is="dom-bind"> | ||
| 46 | + <google-sheets | ||
| 47 | + key="0Anye-JMjUkZZdDBkMVluMEhZMmFGeHpYdDJJV1FBRWc" tab-id="1" rows="{{rows}}" | ||
| 48 | + client-id="..."> | ||
| 49 | + </google-sheets> | ||
| 50 | + <google-map> | ||
| 51 | + <google-map-marker latitude="{{gsx$lat.$t}}" longitude="{{gsx$lng.$t}}"> | ||
| 52 | + </google-map> | ||
| 53 | + </template> | ||
| 54 | + | ||
| 55 | +<b>Example</b> - list a user's private spreadsheets. Authenticate with google-signin button. | ||
| 56 | + | ||
| 57 | + <google-signin | ||
| 58 | + client-id="1054047045356-j8pgqgls9vdef3rl09hapoicumbte0bo.apps.googleusercontent.com" | ||
| 59 | + scopes="https://spreadsheets.google.com/feeds"> | ||
| 60 | + </google-signin> | ||
| 61 | + | ||
| 62 | + <template is="dom-bind"> | ||
| 63 | + <google-sheets client-id="1054047045356-j8pgqgls9vdef3rl09hapoicumbte0bo.apps.googleusercontent.com" | ||
| 64 | + key="1QMGizivw3UJ3-R9BFK7sfrXE0RL87dygk2C0RcuKoDY" tab-id="1" | ||
| 65 | + spreadsheets="{{spreadsheets}}"></google-sheets> | ||
| 66 | + <template is="dom-repeat" items="[[spreadsheets]]"> | ||
| 67 | + <p>{{item.title.$t}}</p> | ||
| 68 | + </template> | ||
| 69 | + </template> | ||
| 70 | + | ||
| 71 | +@demo | ||
| 72 | +--> | ||
| 73 | + | ||
| 74 | +<dom-module id="google-sheets"> | ||
| 75 | + <template> | ||
| 76 | + <template is="dom-if" if="{{!published}}"> | ||
| 77 | + <google-signin-aware client-id="{{clientId}}" | ||
| 78 | + scopes="https://spreadsheets.google.com/feeds" | ||
| 79 | + on-google-signin-aware-success="_onSignInSuccess" | ||
| 80 | + on-google-signin-aware-signed-out="_onSignInFail"></google-signin-aware> | ||
| 81 | + </template> | ||
| 82 | + | ||
| 83 | + <iron-ajax id="publicajax" params='{"alt": "json"}' handle-as="json" | ||
| 84 | + on-response="_onCellRows"></iron-ajax> | ||
| 85 | + <iron-ajax id="listsheetsajax" params='{"alt": "json"}' handle-as="json" | ||
| 86 | + on-response="_onSpreadsheetList"></iron-ajax> | ||
| 87 | + <iron-ajax id="worksheetajax" params='{"alt": "json"}' handle-as="json" | ||
| 88 | + on-response="_onWorksheet"></iron-ajax> | ||
| 89 | + <iron-ajax id="cellrowsajax" params='{"alt": "json"}' handle-as="json" | ||
| 90 | + on-response="_onCellRows"></iron-ajax> | ||
| 91 | + | ||
| 92 | + </template> | ||
| 93 | +</dom-module> | ||
| 94 | + | ||
| 95 | +<script> | ||
| 96 | +(function() { | ||
| 97 | + var SCOPE_ = 'https://spreadsheets.google.com/feeds'; | ||
| 98 | + | ||
| 99 | + // Minimal cache for worksheet row data. Shared across instances so subsequent | ||
| 100 | + // accesses are fast and API calls only happen once. | ||
| 101 | + var rowDataCache_ = {}; | ||
| 102 | + | ||
| 103 | + function generateCacheKey_() { | ||
| 104 | + return this._worksheetId + '_'+ this.tabId; | ||
| 105 | + } | ||
| 106 | + | ||
| 107 | + function getLink_(rel, links) { | ||
| 108 | + for (var i = 0, link; link = links[i]; ++i) { | ||
| 109 | + if (link.rel === rel) { | ||
| 110 | + return link; | ||
| 111 | + } | ||
| 112 | + } | ||
| 113 | + return null; | ||
| 114 | + } | ||
| 115 | + | ||
| 116 | + // Conversion of Worksheet Ids to GIDs and vice versa | ||
| 117 | + // od4 > 2 | ||
| 118 | + function wid_to_gid_(wid) { | ||
| 119 | + return parseInt(String(wid), 36) ^ 31578; | ||
| 120 | + } | ||
| 121 | + // 2 > 0d4 | ||
| 122 | + function gid_to_wid_(gid) { | ||
| 123 | + // (gid xor 31578) encoded in base 36 | ||
| 124 | + return parseInt((gid ^ 31578)).toString(36); | ||
| 125 | + } | ||
| 126 | + | ||
| 127 | + window.GoogleSheets = Polymer({ | ||
| 128 | + | ||
| 129 | + is: 'google-sheets', | ||
| 130 | + | ||
| 131 | + /** | ||
| 132 | + * Fired when the spreadsheet's cell information is available. | ||
| 133 | + * | ||
| 134 | + * @event google-sheet-data | ||
| 135 | + * @param {Object} detail | ||
| 136 | + * @param {Object} detail.data The data returned by the Spreadsheet API. | ||
| 137 | + * @param {string} detail.type The type of data that was fetched. | ||
| 138 | + * One of 'spreadsheets', 'tab', 'rows' * to correspond to the feed type. | ||
| 139 | + */ | ||
| 140 | + | ||
| 141 | + hostAttributes: { | ||
| 142 | + hidden: true | ||
| 143 | + }, | ||
| 144 | + | ||
| 145 | + properties: { | ||
| 146 | + /** | ||
| 147 | + * A Google Developers client ID. Obtain from [console.developers.google.com](https://console.developers.google.com). Required for accessing a private spreadsheet. Optional if accessing a public spreadsheet. | ||
| 148 | + */ | ||
| 149 | + clientId: { | ||
| 150 | + type: String, | ||
| 151 | + value: '', | ||
| 152 | + observer: '_configUpdate' | ||
| 153 | + }, | ||
| 154 | + | ||
| 155 | + /** | ||
| 156 | + * The key of the spreadsheet. This can be found in the URL when viewing | ||
| 157 | + * the document is Google Docs (e.g. `docs.google.com/spreadsheet/ccc?key=<KEY>`). | ||
| 158 | + * | ||
| 159 | + * Leaving off this attribute still returns a list of the users spreadsheets in the `spreadsheets` property. | ||
| 160 | + */ | ||
| 161 | + key: { | ||
| 162 | + type: String, | ||
| 163 | + value: '', | ||
| 164 | + observer: '_keyChanged' | ||
| 165 | + }, | ||
| 166 | + | ||
| 167 | + /** | ||
| 168 | + * Tab within a spreadsheet. For example, the first tab in a spreadsheet | ||
| 169 | + * would be `tab-id="1"`. | ||
| 170 | + */ | ||
| 171 | + tabId: { | ||
| 172 | + type: Number, | ||
| 173 | + value: 1, | ||
| 174 | + observer: '_configUpdate' | ||
| 175 | + }, | ||
| 176 | + | ||
| 177 | + /** | ||
| 178 | + * A hint that the spreadsheet is published publicly in Google Docs. Used as a performance optimization. | ||
| 179 | + * Make sure the sheet is also publicly viewable by anyone in the Share settings. | ||
| 180 | + * | ||
| 181 | + * @attribute published | ||
| 182 | + * @type boolean | ||
| 183 | + * @default false | ||
| 184 | + */ | ||
| 185 | + published: { | ||
| 186 | + type: Boolean, | ||
| 187 | + value: false, | ||
| 188 | + observer: '_configUpdate' | ||
| 189 | + }, | ||
| 190 | + | ||
| 191 | + /** | ||
| 192 | + * The fetched sheet corresponding to the `key` attribute. | ||
| 193 | + */ | ||
| 194 | + sheet: { | ||
| 195 | + type: Object, | ||
| 196 | + value: function() { return {}; }, | ||
| 197 | + readOnly: true, | ||
| 198 | + notify: true, | ||
| 199 | + observer: '_sheetChanged' | ||
| 200 | + }, | ||
| 201 | + | ||
| 202 | + /** | ||
| 203 | + * Meta data about the particular tab that was retrieved for the spreadsheet. | ||
| 204 | + */ | ||
| 205 | + tab: { | ||
| 206 | + type: Object, | ||
| 207 | + value: function() { return {}; }, | ||
| 208 | + readOnly: true, | ||
| 209 | + notify: true, | ||
| 210 | + observer: '_tabChanged' | ||
| 211 | + }, | ||
| 212 | + | ||
| 213 | + /** | ||
| 214 | + * If a spreadsheet `key` is specified, returns a list of cell row data. | ||
| 215 | + */ | ||
| 216 | + rows: { | ||
| 217 | + type: Array, | ||
| 218 | + value: function() { return []; }, | ||
| 219 | + readOnly: true, | ||
| 220 | + notify: true | ||
| 221 | + }, | ||
| 222 | + | ||
| 223 | + /** | ||
| 224 | + * List of the user's spreadsheets. Shared across instances. | ||
| 225 | + */ | ||
| 226 | + spreadsheets: { | ||
| 227 | + type: Array, | ||
| 228 | + readOnly: true, | ||
| 229 | + notify: true, | ||
| 230 | + value: function() { return []; } | ||
| 231 | + }, | ||
| 232 | + | ||
| 233 | + /** | ||
| 234 | + * The URL to open this spreadsheet in Google Sheets. | ||
| 235 | + */ | ||
| 236 | + openInGoogleDocsUrl: { | ||
| 237 | + type: String, | ||
| 238 | + computed: '_computeGoogleDocsUrl(key)', | ||
| 239 | + notify: true | ||
| 240 | + } | ||
| 241 | + }, | ||
| 242 | + | ||
| 243 | + _worksheetId: null, | ||
| 244 | + | ||
| 245 | + _computeGoogleDocsUrl: function(key) { | ||
| 246 | + var url = 'https://docs.google.com/spreadsheet/'; | ||
| 247 | + if (key) { | ||
| 248 | + url += 'ccc?key=' + key; | ||
| 249 | + } | ||
| 250 | + return url; | ||
| 251 | + }, | ||
| 252 | + | ||
| 253 | + _configUpdate: function(key, published, tabId, clientId) { | ||
| 254 | + this._tabIdChanged(); | ||
| 255 | + }, | ||
| 256 | + | ||
| 257 | + _keyChanged: function(newValue, oldValue) { | ||
| 258 | + // TODO(ericbidelman): need to better handle updates to the key attribute. | ||
| 259 | + // Below doesn't account for private feeds. | ||
| 260 | + if (this.published) { | ||
| 261 | + var url = SCOPE_ + '/list/' + this.key + '/' + | ||
| 262 | + this.tabId + '/public/values'; | ||
| 263 | + this.$.publicajax.url = url; | ||
| 264 | + this.$.publicajax.generateRequest(); | ||
| 265 | + } | ||
| 266 | + }, | ||
| 267 | + | ||
| 268 | + _tabIdChanged: function(newValue, oldValue) { | ||
| 269 | + if (this._worksheetId) { | ||
| 270 | + this._getCellRows(); | ||
| 271 | + } else if (this.published) { | ||
| 272 | + this._keyChanged(); | ||
| 273 | + } | ||
| 274 | + }, | ||
| 275 | + | ||
| 276 | + _sheetChanged: function(newValue, oldValue) { | ||
| 277 | + if (!this.sheet.title) { | ||
| 278 | + return; | ||
| 279 | + } | ||
| 280 | + | ||
| 281 | + // Make metadata easily accessible on sheet object. | ||
| 282 | + var authors = this.sheet.author && this.sheet.author.map(function(a) { | ||
| 283 | + return {email: a.email.$t, name: a.name.$t}; | ||
| 284 | + }); | ||
| 285 | + | ||
| 286 | + this.set('sheet.title', this.sheet.title.$t); | ||
| 287 | + this.set('sheet.updated', new Date(this.sheet.updated.$t)); | ||
| 288 | + this.set('sheet.authors', authors); | ||
| 289 | + | ||
| 290 | + this._worksheetId = this.sheet.id.$t.split('/').slice(-1)[0]; | ||
| 291 | + this._getWorksheet(); | ||
| 292 | + }, | ||
| 293 | + | ||
| 294 | + _tabChanged: function(newValue, oldValue) { | ||
| 295 | + if (!this.tab.title) { | ||
| 296 | + return; | ||
| 297 | + } | ||
| 298 | + | ||
| 299 | + var authors = this.tab.authors = this.tab.author && this.tab.author.map(function(a) { | ||
| 300 | + return {email: a.email.$t, name: a.name.$t}; | ||
| 301 | + }); | ||
| 302 | + | ||
| 303 | + this.set('tab.title', this.tab.title.$t); | ||
| 304 | + this.set('tab.updated', new Date(this.tab.updated.$t)); | ||
| 305 | + this.set('tab.authors', authors); | ||
| 306 | + | ||
| 307 | + this.fire('google-sheet-data', { | ||
| 308 | + type: 'tab', | ||
| 309 | + data: this.tab | ||
| 310 | + }); | ||
| 311 | + }, | ||
| 312 | + | ||
| 313 | + _onSignInSuccess: function(e, detail) { | ||
| 314 | + var oauthToken = gapi.auth2.getAuthInstance().currentUser.get().getAuthResponse(); | ||
| 315 | + | ||
| 316 | + var headers = { | ||
| 317 | + 'Authorization': 'Bearer ' + oauthToken.access_token | ||
| 318 | + }; | ||
| 319 | + | ||
| 320 | + this.$.listsheetsajax.headers = headers; | ||
| 321 | + this.$.worksheetajax.headers = headers; | ||
| 322 | + this.$.cellrowsajax.headers = headers; | ||
| 323 | + | ||
| 324 | + // TODO(ericbidelman): don't make this call if this.spreadsheets is | ||
| 325 | + // already populated from another instance. | ||
| 326 | + this._listSpreadsheets(); | ||
| 327 | + }, | ||
| 328 | + | ||
| 329 | + _onSignInFail: function(e, detail) { | ||
| 330 | + // TODO(ericbidelman): handle this in some way. | ||
| 331 | + console.log(e, e.type); | ||
| 332 | + }, | ||
| 333 | + | ||
| 334 | + _listSpreadsheets: function() { | ||
| 335 | + var url = SCOPE_ + '/spreadsheets/private/full'; | ||
| 336 | + this.$.listsheetsajax.url = url; | ||
| 337 | + this.$.listsheetsajax.generateRequest(); | ||
| 338 | + }, | ||
| 339 | + | ||
| 340 | + _onSpreadsheetList: function(e) { | ||
| 341 | + e.stopPropagation(); | ||
| 342 | + | ||
| 343 | + var feed = e.target.lastResponse.feed; | ||
| 344 | + | ||
| 345 | + this._setSpreadsheets(feed.entry); | ||
| 346 | + | ||
| 347 | + this.fire('google-sheet-data', { | ||
| 348 | + type: 'spreadsheets', | ||
| 349 | + data: this.spreadsheets | ||
| 350 | + }); | ||
| 351 | + | ||
| 352 | + // Fetch worksheet feed if key was given and worksheet exists. | ||
| 353 | + if (this.key) { | ||
| 354 | + for (var i = 0, entry; entry = feed.entry[i]; ++i) { | ||
| 355 | + var altLink = getLink_('alternate', entry.link); | ||
| 356 | + if (altLink && altLink.href.indexOf(this.key) != -1) { | ||
| 357 | + this._setSheet(entry); | ||
| 358 | + break; | ||
| 359 | + } | ||
| 360 | + } | ||
| 361 | + } | ||
| 362 | + }, | ||
| 363 | + | ||
| 364 | + _getWorksheet: function() { | ||
| 365 | + if (!this._worksheetId) { | ||
| 366 | + throw new Error('workesheetId was not given.'); | ||
| 367 | + } | ||
| 368 | + | ||
| 369 | + var url = SCOPE_ + '/worksheets/' + this._worksheetId + | ||
| 370 | + '/private/full/' + this.tabId; | ||
| 371 | + this.$.worksheetajax.url = url; | ||
| 372 | + this.$.worksheetajax.generateRequest(); | ||
| 373 | + }, | ||
| 374 | + | ||
| 375 | + _onWorksheet: function(e) { | ||
| 376 | + e.stopPropagation(); | ||
| 377 | + | ||
| 378 | + // this.tab = e.target.lastResponse.entry; | ||
| 379 | + this._setTab(e.target.lastResponse.entry); | ||
| 380 | + this._getCellRows(); | ||
| 381 | + }, | ||
| 382 | + | ||
| 383 | + _getCellRows: function() { | ||
| 384 | + // Use cached data if available. | ||
| 385 | + var key = generateCacheKey_.call(this); | ||
| 386 | + if (key in rowDataCache_) { | ||
| 387 | + this._onCellRows(null, null, rowDataCache_[key]); | ||
| 388 | + | ||
| 389 | + return; | ||
| 390 | + } | ||
| 391 | + | ||
| 392 | + var url = SCOPE_ + '/list/' + | ||
| 393 | + this._worksheetId + '/' + this.tabId + | ||
| 394 | + '/private/full'; | ||
| 395 | + this.$.cellrowsajax.url = url; | ||
| 396 | + this.$.cellrowsajax.generateRequest(); | ||
| 397 | + }, | ||
| 398 | + | ||
| 399 | + _onCellRows: function(e) { | ||
| 400 | + e.stopPropagation(); | ||
| 401 | + | ||
| 402 | + var feed = e.target.lastResponse.feed; | ||
| 403 | + | ||
| 404 | + // Cache data if key doesn't exist. | ||
| 405 | + var key = generateCacheKey_.call(this); | ||
| 406 | + if (!(key in rowDataCache_)) { | ||
| 407 | + rowDataCache_[key] = {response: {feed: feed}}; | ||
| 408 | + } | ||
| 409 | + | ||
| 410 | + // this.rows = feed.entry; | ||
| 411 | + this._setRows(feed.entry); | ||
| 412 | + var authors = feed.author && feed.author.map(function(a) { | ||
| 413 | + return {email: a.email.$t, name: a.name.$t}; | ||
| 414 | + }); | ||
| 415 | + this.set('rows.authors', authors); | ||
| 416 | + | ||
| 417 | + if (this.published) { | ||
| 418 | + // this.tab = feed; | ||
| 419 | + this._setTab(feed); | ||
| 420 | + } | ||
| 421 | + | ||
| 422 | + this.fire('google-sheet-data', { | ||
| 423 | + type: 'rows', | ||
| 424 | + data: this.rows | ||
| 425 | + }); | ||
| 426 | + } | ||
| 427 | + | ||
| 428 | + }); | ||
| 429 | + | ||
| 430 | +})(); | ||
| 431 | +</script> |
bower_components/google-sheets/index.html
0 → 100755
| 1 | +<!doctype html> | ||
| 2 | +<!-- Copyright (c) 2015 Google Inc. All rights reserved. --> | ||
| 3 | +<html> | ||
| 4 | +<head> | ||
| 5 | + | ||
| 6 | + <script src="../webcomponentsjs/webcomponents-lite.js"></script> | ||
| 7 | + <link rel="import" href="../iron-component-page/iron-component-page.html"> | ||
| 8 | + | ||
| 9 | +</head> | ||
| 10 | +<body> | ||
| 11 | + | ||
| 12 | + <iron-component-page></iron-component-page> | ||
| 13 | + | ||
| 14 | +</body> | ||
| 15 | +</html> |
bower_components/google-sheets/tests/google-sheet.html
0 → 100644
| 1 | +<!doctype html> | ||
| 2 | +<!-- Copyright (c) 2015 Google Inc. All rights reserved. --> | ||
| 3 | +<html> | ||
| 4 | +<head> | ||
| 5 | + <meta charset="utf-8"> | ||
| 6 | + <title>google-sheet tests</title> | ||
| 7 | + <meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes"> | ||
| 8 | + | ||
| 9 | + <script src="../../../platform/platform.js"></script> | ||
| 10 | + <link rel="import" href="../../../polymer-test-tools/tools.html"> | ||
| 11 | + <script src="../../../polymer-test-tools/htmltest.js"></script> | ||
| 12 | + | ||
| 13 | + <link rel="import" href="../google-sheets.html"> | ||
| 14 | +</head> | ||
| 15 | +<body> | ||
| 16 | + | ||
| 17 | +<google-sheets id="sheet1"></google-sheets> | ||
| 18 | + | ||
| 19 | +<script> | ||
| 20 | +document.addEventListener('polymer-ready', function() { | ||
| 21 | + | ||
| 22 | + (function() { | ||
| 23 | + var sheet = document.querySelector('#sheet1'); | ||
| 24 | + var root = sheet.shadowRoot; | ||
| 25 | + | ||
| 26 | + // Check defaults. | ||
| 27 | + assert.lengthOf(sheet.spreadsheets, 0, | ||
| 28 | + '.spreadsheets length should default to 0'); | ||
| 29 | + assert.lengthOf(sheet.rows, 0, '.rows length should default to 0'); | ||
| 30 | + assert.isObject(sheet.sheet, '.sheet should default to {}'); | ||
| 31 | + assert.isObject(sheet.tab, '.tab should default to {}'); | ||
| 32 | + | ||
| 33 | + assert.equal(sheet.key, '', ".key default is not ''"); | ||
| 34 | + assert.equal(sheet.gid, 0, '.gid default is not 0'); | ||
| 35 | + assert.isFalse(sheet.published, '.published does not default to false'); | ||
| 36 | + assert.isNotNull(root.querySelector('google-signin-aware'), | ||
| 37 | + 'google-signin-aware should be created for non-public sheet'); | ||
| 38 | + | ||
| 39 | + done(); | ||
| 40 | + | ||
| 41 | + })(); | ||
| 42 | + | ||
| 43 | +}); | ||
| 44 | +</script> | ||
| 45 | +</body> | ||
| 46 | +</html> |
bower_components/google-sheets/tests/index.html
0 → 100644
| 1 | +<!doctype html> | ||
| 2 | +<!-- Copyright (c) 2015 Google Inc. All rights reserved. --> | ||
| 3 | +<html> | ||
| 4 | + <head> | ||
| 5 | + <meta charset="utf-8"> | ||
| 6 | + <title>Runs all tests</title> | ||
| 7 | + <meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes"> | ||
| 8 | + <script src="../../platform/platform.js"></script> | ||
| 9 | + <link rel="import" href="tests.html"> | ||
| 10 | + </head> | ||
| 11 | + <body> | ||
| 12 | + <div id="mocha"></div> | ||
| 13 | + </body> | ||
| 14 | +</html> |
bower_components/google-sheets/tests/private.html
0 → 100644
| 1 | +<!doctype html> | ||
| 2 | +<!-- Copyright (c) 2015 Google Inc. All rights reserved. --> | ||
| 3 | +<html> | ||
| 4 | +<head> | ||
| 5 | + <meta charset="utf-8"> | ||
| 6 | + <title>google-sheet tests</title> | ||
| 7 | + <meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes"> | ||
| 8 | + | ||
| 9 | + <script src="../../../platform/platform.js"></script> | ||
| 10 | + <link rel="import" href="../../../polymer-test-tools/tools.html"> | ||
| 11 | + <script src="../../../polymer-test-tools/htmltest.js"></script> | ||
| 12 | + | ||
| 13 | + <link rel="import" href="../google-sheets.html"> | ||
| 14 | +</head> | ||
| 15 | +<body> | ||
| 16 | + | ||
| 17 | +<div> | ||
| 18 | + <p>Tests live private spreadsheet data.</p> | ||
| 19 | + <p>This test needs to be run manually, standalone in order to fetch an OAuth token.</p> | ||
| 20 | + | ||
| 21 | + <b>NOTES:</b><br> | ||
| 22 | + <li>run manually off of localhost:3000/google-sheets/tests/private.html. Can also run from port 8080.</li> | ||
| 23 | + <li>use the account: webcomponents.test@gmail.com</li> | ||
| 24 | +</div> | ||
| 25 | + | ||
| 26 | +<google-sheets id="sheet" gid="1" | ||
| 27 | + clientId="750497606405-1hq66meqmr4dp09dn54j9ggv85vbv0gp.apps.googleusercontent.com" | ||
| 28 | + key="0AjqzxJ5RoRrWdGh2S0RRYURXTlNHdm9pQlFuM1ZwZWc"></google-sheets> | ||
| 29 | + | ||
| 30 | +<script> | ||
| 31 | +document.addEventListener('polymer-ready', function() { | ||
| 32 | + | ||
| 33 | + (function() { | ||
| 34 | + var sheet = document.querySelector('#sheet'); | ||
| 35 | + var root = sheet.shadowRoot; | ||
| 36 | + | ||
| 37 | + // Test set attributes. | ||
| 38 | + assert.equal(sheet.key, | ||
| 39 | + '0AjqzxJ5RoRrWdGh2S0RRYURXTlNHdm9pQlFuM1ZwZWc', ".key was not updated"); | ||
| 40 | + assert.equal(sheet.gid, 1, '.gid was not updated'); | ||
| 41 | + assert.equal(sheet.clientId, | ||
| 42 | + '750497606405-1hq66meqmr4dp09dn54j9ggv85vbv0gp.apps.googleusercontent.com', ".clientId was not set"); | ||
| 43 | + | ||
| 44 | + sheet.addEventListener('google-sheet-data', function(e) { | ||
| 45 | + | ||
| 46 | + switch (e.detail.type) { | ||
| 47 | + case 'spreadsheets': | ||
| 48 | + assert.isTrue(this.spreadsheets.length > 0, | ||
| 49 | + '.spreadsheets should be populated for private feeds.'); | ||
| 50 | + break; | ||
| 51 | + case 'tab': | ||
| 52 | + assert.equal(this.tab.title, 'SECONDTAB', '.tab.title is incorrect'); | ||
| 53 | + break; | ||
| 54 | + case 'rows': | ||
| 55 | + assert.lengthOf(this.rows.authors, 1, '.rows.authors array'); | ||
| 56 | + | ||
| 57 | + var name = this.rows.authors[0].name; | ||
| 58 | + var email = this.rows.authors[0].email | ||
| 59 | + | ||
| 60 | + assert.equal(email, 'webcomponents.test@gmail.com', 'author email not set correctly'); | ||
| 61 | + assert.equal(name, 'webcomponents.test', 'author name not set correctly'); | ||
| 62 | + | ||
| 63 | + assert.equal(this.rows[0].title.$t, 'FIRST NAME', '"name" column was incorrect'); | ||
| 64 | + assert.equal(this.rows[1].gsx$state.$t, 'OR', '"state" column was incorrect'); | ||
| 65 | + | ||
| 66 | + break; | ||
| 67 | + default: | ||
| 68 | + // Noop | ||
| 69 | + } | ||
| 70 | + | ||
| 71 | + done(); | ||
| 72 | + | ||
| 73 | + }); | ||
| 74 | + | ||
| 75 | + })(); | ||
| 76 | + | ||
| 77 | +}); | ||
| 78 | +</script> | ||
| 79 | +</body> | ||
| 80 | +</html> | ||
| 81 | + |
bower_components/google-sheets/tests/published.html
0 → 100644
| 1 | +<!doctype html> | ||
| 2 | +<!-- Copyright (c) 2015 Google Inc. All rights reserved. --> | ||
| 3 | +<html> | ||
| 4 | +<head> | ||
| 5 | + <meta charset="utf-8"> | ||
| 6 | + <title>google-sheet tests</title> | ||
| 7 | + <meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes"> | ||
| 8 | + | ||
| 9 | + <script src="../../../platform/platform.js"></script> | ||
| 10 | + <link rel="import" href="../../../polymer-test-tools/tools.html"> | ||
| 11 | + <script src="../../../polymer-test-tools/htmltest.js"></script> | ||
| 12 | + | ||
| 13 | + <link rel="import" href="../google-sheets.html"> | ||
| 14 | +</head> | ||
| 15 | +<body> | ||
| 16 | + | ||
| 17 | +<google-sheets id="sheet" key="0Anye-JMjUkZZdDBkMVluMEhZMmFGeHpYdDJJV1FBRWc" | ||
| 18 | + published></google-sheets> | ||
| 19 | + | ||
| 20 | +<script> | ||
| 21 | +document.addEventListener('polymer-ready', function() { | ||
| 22 | + | ||
| 23 | + var sheet = document.querySelector('#sheet'); | ||
| 24 | + var root = sheet.shadowRoot; | ||
| 25 | + | ||
| 26 | + assert.isTrue(sheet.published); | ||
| 27 | + assert.isNull(root.querySelector('google-signin-aware'), | ||
| 28 | + 'google-signin-aware should not be created for a published sheet'); | ||
| 29 | + | ||
| 30 | + sheet.addEventListener('google-sheet-data', function(e) { | ||
| 31 | + | ||
| 32 | + if (e.detail.type === 'tab') { | ||
| 33 | + assert.equal(this.tab.title, 'Locations', | ||
| 34 | + 'Published spreadsheet title is not correct.'); | ||
| 35 | + assert.isNotNull(this.tab.updated, '.tab.updated was not set'); | ||
| 36 | + assert.isTrue(this.tab.authors.length > 0, '.tab.authors was 0'); | ||
| 37 | + } else if (e.detail.type === 'rows') { | ||
| 38 | + assert.lengthOf(this.spreadsheets, 0, | ||
| 39 | + '.spreadsheets length should be 0 since spreadsheet key was given'); | ||
| 40 | + assert.isTrue(this.rows.length > 0, '.rows was not populated'); | ||
| 41 | + } | ||
| 42 | + | ||
| 43 | + assert.equal(this.$.cellrowsajax.url, '', | ||
| 44 | + '#cellrowsajax should not be invoked for a public spreadsheet'); | ||
| 45 | + | ||
| 46 | + done(); | ||
| 47 | + }); | ||
| 48 | +}); | ||
| 49 | +</script> | ||
| 50 | +</body> | ||
| 51 | +</html> |
bower_components/google-sheets/tests/tests.html
0 → 100644
| 1 | +<!-- Copyright (c) 2015 Google Inc. All rights reserved. --> | ||
| 2 | + | ||
| 3 | +<link rel="import" href="../../polymer-test-tools/tools.html"> | ||
| 4 | +<script src="../../polymer-test-tools/mocha-htmltest.js"></script> | ||
| 5 | + | ||
| 6 | +<script> | ||
| 7 | +mocha.setup({ui: 'tdd', slow: 1000, timeout: 5000, htmlbase: ''}); | ||
| 8 | + | ||
| 9 | +htmlSuite('google-sheet', function() { | ||
| 10 | + htmlTest('google-sheet.html'); | ||
| 11 | + htmlTest('published.html'); | ||
| 12 | + htmlTest('private.html'); | ||
| 13 | +}); | ||
| 14 | + | ||
| 15 | +mocha.run(); | ||
| 16 | +</script> |
bower_components/google-signin/.bower.json
0 → 100644
| 1 | +{ | ||
| 2 | + "name": "google-signin", | ||
| 3 | + "version": "1.2.0", | ||
| 4 | + "description": "Web components to authenticate with Google services", | ||
| 5 | + "homepage": "https://googlewebcomponents.github.io/google-signin", | ||
| 6 | + "main": "google-signin.html", | ||
| 7 | + "authors": [ | ||
| 8 | + "Addy Osmani", | ||
| 9 | + "Randy Merrill" | ||
| 10 | + ], | ||
| 11 | + "license": "Apache-2", | ||
| 12 | + "ignore": [ | ||
| 13 | + "/.*", | ||
| 14 | + "/test/" | ||
| 15 | + ], | ||
| 16 | + "keywords": [ | ||
| 17 | + "web-component", | ||
| 18 | + "web-components", | ||
| 19 | + "polymer", | ||
| 20 | + "sign-in", | ||
| 21 | + "google", | ||
| 22 | + "authentication" | ||
| 23 | + ], | ||
| 24 | + "dependencies": { | ||
| 25 | + "polymer": "Polymer/polymer#^1.0.0", | ||
| 26 | + "font-roboto": "PolymerElements/font-roboto#^1.0.0", | ||
| 27 | + "iron-icon": "PolymerElements/iron-icon#^1.0.0", | ||
| 28 | + "iron-icons": "PolymerElements/iron-icons#^1.0.0", | ||
| 29 | + "iron-flex-layout": "PolymerElements/iron-flex-layout#^1.0.0", | ||
| 30 | + "paper-ripple": "PolymerElements/paper-ripple#^1.0.0", | ||
| 31 | + "paper-material": "PolymerElements/paper-material#^1.0.0", | ||
| 32 | + "google-apis": "GoogleWebComponents/google-apis#^1.0.0" | ||
| 33 | + }, | ||
| 34 | + "devDependencies": { | ||
| 35 | + "iron-component-page": "PolymerElements/iron-component-page#^1.0.2" | ||
| 36 | + }, | ||
| 37 | + "_release": "1.2.0", | ||
| 38 | + "_resolution": { | ||
| 39 | + "type": "version", | ||
| 40 | + "tag": "v1.2.0", | ||
| 41 | + "commit": "a1414b51b39e0a7dfe7031eb73a7733e2216579d" | ||
| 42 | + }, | ||
| 43 | + "_source": "git://github.com/GoogleWebComponents/google-signin.git", | ||
| 44 | + "_target": "^1.0.3", | ||
| 45 | + "_originalSource": "GoogleWebComponents/google-signin" | ||
| 46 | +} | ||
| 0 | \ No newline at end of file | 47 | \ No newline at end of file |
bower_components/google-signin/LICENSE
0 → 100644
| 1 | +Copyright 2014 Google Inc | ||
| 2 | + | ||
| 3 | +Licensed under the Apache License, Version 2.0 (the "License"); | ||
| 4 | +you may not use this file except in compliance with the License. | ||
| 5 | +You may obtain a copy of the License at | ||
| 6 | + | ||
| 7 | + https://www.apache.org/licenses/LICENSE-2.0 | ||
| 8 | + | ||
| 9 | +Unless required by applicable law or agreed to in writing, software | ||
| 10 | +distributed under the License is distributed on an "AS IS" BASIS, | ||
| 11 | +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
| 12 | +See the License for the specific language governing permissions and | ||
| 13 | +limitations under the License. |
bower_components/google-signin/README.md
0 → 100644
bower_components/google-signin/bower.json
0 → 100644
| 1 | +{ | ||
| 2 | + "name": "google-signin", | ||
| 3 | + "version": "1.2.0", | ||
| 4 | + "description": "Web components to authenticate with Google services", | ||
| 5 | + "homepage": "https://googlewebcomponents.github.io/google-signin", | ||
| 6 | + "main": "google-signin.html", | ||
| 7 | + "authors": [ | ||
| 8 | + "Addy Osmani", | ||
| 9 | + "Randy Merrill" | ||
| 10 | + ], | ||
| 11 | + "license": "Apache-2", | ||
| 12 | + "ignore": [ | ||
| 13 | + "/.*", | ||
| 14 | + "/test/" | ||
| 15 | + ], | ||
| 16 | + "keywords": [ | ||
| 17 | + "web-component", | ||
| 18 | + "web-components", | ||
| 19 | + "polymer", | ||
| 20 | + "sign-in", | ||
| 21 | + "google", | ||
| 22 | + "authentication" | ||
| 23 | + ], | ||
| 24 | + "dependencies": { | ||
| 25 | + "polymer": "Polymer/polymer#^1.0.0", | ||
| 26 | + "font-roboto": "PolymerElements/font-roboto#^1.0.0", | ||
| 27 | + "iron-icon": "PolymerElements/iron-icon#^1.0.0", | ||
| 28 | + "iron-icons": "PolymerElements/iron-icons#^1.0.0", | ||
| 29 | + "iron-flex-layout": "PolymerElements/iron-flex-layout#^1.0.0", | ||
| 30 | + "paper-ripple": "PolymerElements/paper-ripple#^1.0.0", | ||
| 31 | + "paper-material": "PolymerElements/paper-material#^1.0.0", | ||
| 32 | + "google-apis": "GoogleWebComponents/google-apis#^1.0.0" | ||
| 33 | + }, | ||
| 34 | + "devDependencies": { | ||
| 35 | + "iron-component-page": "PolymerElements/iron-component-page#^1.0.2" | ||
| 36 | + } | ||
| 37 | +} |
bower_components/google-signin/demo/index.html
0 → 100644
| 1 | +<!doctype html> | ||
| 2 | +<!-- Copyright (c) 2014 Google Inc. All rights reserved. --> | ||
| 3 | +<html> | ||
| 4 | +<head> | ||
| 5 | + <meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes"> | ||
| 6 | + | ||
| 7 | + <title>google-signin Demo</title> | ||
| 8 | + | ||
| 9 | + <script src="../../webcomponentsjs/webcomponents-lite.js"></script> | ||
| 10 | + <link rel="import" href="../google-signin.html"> | ||
| 11 | + <link rel="import" href="../google-signin-aware.html"> | ||
| 12 | + | ||
| 13 | + <!-- Demo only styles --> | ||
| 14 | + <style> | ||
| 15 | + body { | ||
| 16 | + font-family: 'RobotoDraft', 'Roboto', sans-serif; | ||
| 17 | + line-height:1.2; | ||
| 18 | + vertical-align:middle; | ||
| 19 | + background: rgba(204, 204, 204, 0.31); | ||
| 20 | + } | ||
| 21 | + | ||
| 22 | + | ||
| 23 | + .map { | ||
| 24 | + background: whitesmoke; | ||
| 25 | + margin: .5rem -1.5rem 0 -1.5rem; | ||
| 26 | + padding: 0.5rem; | ||
| 27 | + } | ||
| 28 | + | ||
| 29 | + h1 { | ||
| 30 | + font-size: 2rem; | ||
| 31 | + font-weight:200; | ||
| 32 | + clear: both; | ||
| 33 | + } | ||
| 34 | + | ||
| 35 | + h1 strong { | ||
| 36 | + font-weight:300; | ||
| 37 | + color:#539D00; | ||
| 38 | + } | ||
| 39 | + | ||
| 40 | + h2 { | ||
| 41 | + font-size:.9rem; | ||
| 42 | + line-height:2.5; | ||
| 43 | + color:gray; | ||
| 44 | + font-weight:400; | ||
| 45 | + clear: both; | ||
| 46 | + } | ||
| 47 | + | ||
| 48 | + .showcase { | ||
| 49 | + display: inline-block; | ||
| 50 | + margin-right: 2rem; | ||
| 51 | + float: left; | ||
| 52 | + } | ||
| 53 | + </style> | ||
| 54 | + | ||
| 55 | +</head> | ||
| 56 | + | ||
| 57 | +<body> | ||
| 58 | + <p>A <code><google-signin></code> element looks like this button:</p> | ||
| 59 | + | ||
| 60 | + <p><google-signin brand="google" client-id="1054047045356-j8pgqgls9vdef3rl09hapoicumbte0bo.apps.googleusercontent.com"></google-signin> | ||
| 61 | +or like this if plus scopes are present | ||
| 62 | + <google-signin brand="google-plus"></google-signin> | ||
| 63 | + </p> | ||
| 64 | + <p>Signin button can vary its appearance:</p> | ||
| 65 | + <p>Width: | ||
| 66 | + <google-signin brand="google" width="wide"></google-signin> | ||
| 67 | + <google-signin brand="google" width="iconOnly"></google-signin> | ||
| 68 | + Height: | ||
| 69 | + <google-signin brand="google" height="tall"></google-signin> | ||
| 70 | + <google-signin brand="google" height="standard"></google-signin> | ||
| 71 | + <google-signin brand="google" height="short"></google-signin> | ||
| 72 | + </p> | ||
| 73 | + <p> | ||
| 74 | + Theme: | ||
| 75 | + <google-signin brand="google" theme="dark"></google-signin> | ||
| 76 | + <google-signin brand="google" theme="light"></google-signin> | ||
| 77 | + <google-signin brand="google-plus" theme="light"></google-signin> | ||
| 78 | + <google-signin brand="google-plus" theme="light" raised></google-signin> | ||
| 79 | + </p> | ||
| 80 | + <!-- Demo the ability to use the google-signin-aware element. --> | ||
| 81 | + <p><code><google-signin-aware></code> is a companion element.</p> | ||
| 82 | + <p>You can use it inside your components to request additional scopes.</p> | ||
| 83 | + <p>Every signin button will request all the scopes present in the document, | ||
| 84 | + and change its appearance to match</p> | ||
| 85 | + <p>For example, here is a signin-aware scope. You can change its scopes via popup</p> | ||
| 86 | + <template id="awareness" is="dom-bind"> | ||
| 87 | + <div><code><google-signin-aware | ||
| 88 | + <div>scope= | ||
| 89 | + <select value="{{scope::change}}"> | ||
| 90 | + <option value="">None</option> | ||
| 91 | + <option value="https://www.googleapis.com/auth/analytics">Google Analytics</option> | ||
| 92 | + <option value="https://www.googleapis.com/auth/plus.login">Google Plus view circles</option> | ||
| 93 | + <option value="https://www.googleapis.com/auth/youtube">YouTube</option> | ||
| 94 | + <option value="https://www.googleapis.com/auth/calendar">Calendar</option> | ||
| 95 | + <option value="profile">Profile info</option> | ||
| 96 | + </select> | ||
| 97 | + </div> | ||
| 98 | + <div>offline=<input type="checkbox" checked="{{offline::change}}"></div> | ||
| 99 | + <div>signedIn="<span>{{signedIn}}</span>"</div> | ||
| 100 | + <div>isAuthorized="<span>{{isAuthorized}}</span>"</div> | ||
| 101 | + <div>needAdditionalAuth:"<span>{{needAdditionalAuth}}</span>"></div> | ||
| 102 | + </code></div> | ||
| 103 | + <p>Every new scope you select will be added to requested scopes.</p> | ||
| 104 | + <p>When you select a Google Plus scope, button will turn red.</p> | ||
| 105 | + <google-signin></google-signin> | ||
| 106 | + </p> | ||
| 107 | + <google-signin-aware | ||
| 108 | + scopes="{{scope}}" | ||
| 109 | + signed-in="{{signedIn}}" | ||
| 110 | + offline="{{offline}}" | ||
| 111 | + is-authorized="{{isAuthorized}}" | ||
| 112 | + need-additional-auth="{{needAdditionalAuth}}" | ||
| 113 | + on-google-signin-aware-success="handleSignIn" | ||
| 114 | + on-google-signin-offline-success="handleOffline" | ||
| 115 | + on-google-signin-aware-signed-out="handleSignOut"></google-signin-aware> | ||
| 116 | + <p>User name:<span>{{userName}}</span></p> | ||
| 117 | + <p>Testing <code>google-signin-aware</code> events: <span>{{status}}</span></p> | ||
| 118 | + <p>Testing <code>google-signin-offline</code> events: <span>{{offlineCode}}</span></p> | ||
| 119 | + <p><button on-click="disconnect">Disconnect to start over</button></p> | ||
| 120 | + </template> | ||
| 121 | + <script> | ||
| 122 | + var aware = document.querySelector('#awareness'); | ||
| 123 | + aware.status = 'Not granted'; | ||
| 124 | + aware.offlineCode = 'No offline login.'; | ||
| 125 | + aware.userName = 'N/A'; | ||
| 126 | + aware.handleSignIn = function(response) { | ||
| 127 | + this.status = 'Signin granted'; | ||
| 128 | + // console.log('[Aware] Signin Response', response); | ||
| 129 | + this.userName = gapi.auth2.getAuthInstance().currentUser.get().getBasicProfile().getName(); | ||
| 130 | + }; | ||
| 131 | + aware.handleOffline = function(response) { | ||
| 132 | + this.offlineCode = response.detail.code; | ||
| 133 | + }; | ||
| 134 | + aware.handleSignOut = function(response) { | ||
| 135 | + this.status = 'Signed out'; | ||
| 136 | + // console.log('[Aware] Signout Response', response); | ||
| 137 | + this.userName = 'N/A'; | ||
| 138 | + }; | ||
| 139 | + aware.disconnect = function() { | ||
| 140 | + var b = document.querySelector('google-signin'); | ||
| 141 | + var currentUser = gapi.auth2.getAuthInstance().currentUser.get(); | ||
| 142 | + if (currentUser) { | ||
| 143 | + currentUser.disconnect(); | ||
| 144 | + } | ||
| 145 | + gapi.auth2.getAuthInstance().signOut(); | ||
| 146 | + }; | ||
| 147 | + | ||
| 148 | + </script> | ||
| 149 | +</body> | ||
| 150 | +</html> |
bower_components/google-signin/google-icons.html
0 → 100644
| 1 | +<!-- | ||
| 2 | +Copyright (c) 2014 The Polymer Project Authors. All rights reserved. | ||
| 3 | +This code may only be used under the BSD style license found at https://polymer.github.io/LICENSE.txt | ||
| 4 | +The complete set of authors may be found at https://polymer.github.io/AUTHORS.txt | ||
| 5 | +The complete set of contributors may be found at https://polymer.github.io/CONTRIBUTORS.txt | ||
| 6 | +Code distributed by Google as part of the polymer project is also | ||
| 7 | +subject to an additional IP rights grant found at https://polymer.github.io/PATENTS.txt | ||
| 8 | +--> | ||
| 9 | + | ||
| 10 | +<link rel="import" href="../iron-icon/iron-icon.html"> | ||
| 11 | +<link rel="import" href="../iron-iconset-svg/iron-iconset-svg.html"> | ||
| 12 | +<iron-iconset-svg name="google" iconSize="24"> | ||
| 13 | +<svg><defs> | ||
| 14 | +<g id="google"><path d="M16.3,13.4l-1.1-0.8c-0.4-0.3-0.8-0.7-0.8-1.4c0-0.7,0.5-1.3,1-1.6c1.3-1,2.6-2.1,2.6-4.3c0-2.1-1.3-3.3-2-3.9h1.7L18.9,0h-6.2C8.3,0,6.1,2.8,6.1,5.8c0,2.3,1.8,4.8,5,4.8h0.8c-0.1,0.3-0.4,0.8-0.4,1.3c0,1,0.4,1.4,0.9,2c-1.4,0.1-4,0.4-5.9,1.6c-1.8,1.1-2.3,2.6-2.3,3.7c0,2.3,2.1,4.5,6.6,4.5c5.4,0,8-3,8-5.9C18.8,15.7,17.7,14.6,16.3,13.4z M8.7,4.3c0-2.2,1.3-3.2,2.7-3.2c2.6,0,4,3.5,4,5.5c0,2.6-2.1,3.1-2.9,3.1C10,9.7,8.7,6.6,8.7,4.3z M12.3,22.3c-3.3,0-5.4-1.5-5.4-3.7c0-2.2,2-2.9,2.6-3.2c1.3-0.4,3-0.5,3.3-0.5c0.3,0,0.5,0,0.7,0c2.4,1.7,3.4,2.4,3.4,4C16.9,20.8,15,22.3,12.3,22.3z"/></g> | ||
| 15 | +<g id="google-plus"><path d="M21,10V7h-2v3h-3v2h3v3h2v-3h3v-2H21z M13.3,13.4l-1.1-0.8c-0.4-0.3-0.8-0.7-0.8-1.4c0-0.7,0.5-1.3,1-1.6c1.3-1,2.6-2.1,2.6-4.3c0-2.1-1.3-3.3-2-3.9h1.7L15.9,0H9.7C5.3,0,3.1,2.8,3.1,5.8c0,2.3,1.8,4.8,5,4.8h0.8c-0.1,0.3-0.4,0.8-0.4,1.3c0,1,0.4,1.4,0.9,2c-1.4,0.1-4,0.4-5.9,1.6c-1.8,1.1-2.3,2.6-2.3,3.7c0,2.3,2.1,4.5,6.6,4.5c5.4,0,8-3,8-5.9C15.8,15.7,14.7,14.6,13.3,13.4z M5.7,4.3c0-2.2,1.3-3.2,2.7-3.2c2.6,0,4,3.5,4,5.5c0,2.6-2.1,3.1-2.9,3.1C7,9.7,5.7,6.6,5.7,4.3z M9.3,22.3c-3.3,0-5.4-1.5-5.4-3.7c0-2.2,2-2.9,2.6-3.2c1.3-0.4,3-0.5,3.3-0.5c0.3,0,0.5,0,0.7,0c2.4,1.7,3.4,2.4,3.4,4C13.9,20.8,12,22.3,9.3,22.3z"/></g> | ||
| 16 | +</defs></svg> | ||
| 17 | +</iron-iconset-svg> |
bower_components/google-signin/google-signin-aware.html
0 → 100644
| 1 | +<link rel="import" href="../polymer/polymer.html"> | ||
| 2 | +<link rel="import" href="../google-apis/google-js-api.html"> | ||
| 3 | + | ||
| 4 | +<script> | ||
| 5 | + (function() { | ||
| 6 | + | ||
| 7 | + /** | ||
| 8 | + * Enum of attributes to be passed through to the login API call. | ||
| 9 | + * @readonly | ||
| 10 | + * @enum {string} | ||
| 11 | + */ | ||
| 12 | + var ProxyLoginAttributes = { | ||
| 13 | + 'appPackageName': 'apppackagename', | ||
| 14 | + 'clientId': 'clientid', | ||
| 15 | + 'cookiePolicy': 'cookiepolicy', | ||
| 16 | + 'requestVisibleActions': 'requestvisibleactions', | ||
| 17 | + 'hostedDomain': 'hostedDomain' | ||
| 18 | + }; | ||
| 19 | + | ||
| 20 | + /** | ||
| 21 | + * AuthEngine does all interactions with gapi.auth2 | ||
| 22 | + * | ||
| 23 | + * It is tightly coupled with <google-signin-aware> element | ||
| 24 | + * The elements configure AuthEngine. | ||
| 25 | + * AuthEngine propagates all authentication events to all google-signin-aware elements | ||
| 26 | + * | ||
| 27 | + * API used: https://developers.google.com/identity/sign-in/web/reference | ||
| 28 | + * | ||
| 29 | + */ | ||
| 30 | + var AuthEngine = { | ||
| 31 | + | ||
| 32 | + /** | ||
| 33 | + * oauth2 argument, set by google-signin-aware | ||
| 34 | + */ | ||
| 35 | + _clientId: null, | ||
| 36 | + | ||
| 37 | + get clientId() { | ||
| 38 | + return this._clientId; | ||
| 39 | + }, | ||
| 40 | + | ||
| 41 | + set clientId(val) { | ||
| 42 | + if (this._clientId && val && val != this._clientId) { | ||
| 43 | + throw new Error('clientId cannot change. Values do not match. New: ' + val + ' Old:' + this._clientId); | ||
| 44 | + } | ||
| 45 | + if (val) { | ||
| 46 | + this._clientId = val; | ||
| 47 | + this.initAuth2(); | ||
| 48 | + } | ||
| 49 | + }, | ||
| 50 | + | ||
| 51 | + /** | ||
| 52 | + * oauth2 argument, set by google-signin-aware | ||
| 53 | + */ | ||
| 54 | + _cookiePolicy: 'single_host_origin', | ||
| 55 | + | ||
| 56 | + get cookiePolicy() { | ||
| 57 | + return this._cookiePolicy; | ||
| 58 | + }, | ||
| 59 | + | ||
| 60 | + set cookiePolicy(val) { | ||
| 61 | + if (val) { | ||
| 62 | + this._cookiePolicy = val; | ||
| 63 | + } | ||
| 64 | + }, | ||
| 65 | + | ||
| 66 | + /** | ||
| 67 | + * oauth2 argument, set by google-signin-aware | ||
| 68 | + */ | ||
| 69 | + _appPackageName: '', | ||
| 70 | + | ||
| 71 | + get appPackageName() { | ||
| 72 | + return this._appPackageName; | ||
| 73 | + }, | ||
| 74 | + | ||
| 75 | + set appPackageName(val) { | ||
| 76 | + if (this._appPackageName && val && val != this._appPackageName) { | ||
| 77 | + throw new Error('appPackageName cannot change. Values do not match. New: ' + val + ' Old: ' + this._appPackageName); | ||
| 78 | + } | ||
| 79 | + if (val) { | ||
| 80 | + this._appPackageName = val; | ||
| 81 | + } | ||
| 82 | + }, | ||
| 83 | + | ||
| 84 | + /** | ||
| 85 | + * oauth2 argument, set by google-signin-aware | ||
| 86 | + */ | ||
| 87 | + _requestVisibleActions: '', | ||
| 88 | + | ||
| 89 | + get requestVisibleactions() { | ||
| 90 | + return this._requestVisibleActions; | ||
| 91 | + }, | ||
| 92 | + | ||
| 93 | + set requestVisibleactions(val) { | ||
| 94 | + if (this._requestVisibleActions && val && val != this._requestVisibleActions) { | ||
| 95 | + throw new Error('requestVisibleactions cannot change. Values do not match. New: ' + val + ' Old: ' + this._requestVisibleActions); | ||
| 96 | + } | ||
| 97 | + if (val) | ||
| 98 | + this._requestVisibleActions = val; | ||
| 99 | + }, | ||
| 100 | + | ||
| 101 | + /** | ||
| 102 | + * oauth2 argument, set by google-signin-aware | ||
| 103 | + */ | ||
| 104 | + _hostedDomain: '', | ||
| 105 | + | ||
| 106 | + get hostedDomain() { | ||
| 107 | + return this._hostedDomain; | ||
| 108 | + }, | ||
| 109 | + | ||
| 110 | + set hostedDomain(val) { | ||
| 111 | + if (this._hostedDomain && val && val != this._hostedDomain) { | ||
| 112 | + throw new Error('hostedDomain cannot change. Values do not match. New: ' + val + ' Old: ' + this._hostedDomain); | ||
| 113 | + } | ||
| 114 | + if (val) | ||
| 115 | + this._hostedDomain = val; | ||
| 116 | + }, | ||
| 117 | + | ||
| 118 | + /** Is offline access currently enabled in the google-signin-aware element? */ | ||
| 119 | + _offline: false, | ||
| 120 | + | ||
| 121 | + get offline() { | ||
| 122 | + return this._offline; | ||
| 123 | + }, | ||
| 124 | + | ||
| 125 | + set offline(val) { | ||
| 126 | + this._offline = val; | ||
| 127 | + this.updateAdditionalAuth(); | ||
| 128 | + }, | ||
| 129 | + | ||
| 130 | + /** Should we force a re-prompt for offline access? */ | ||
| 131 | + _offlineAlwaysPrompt: false, | ||
| 132 | + | ||
| 133 | + get offlineAlwaysPrompt() { | ||
| 134 | + return this._offlineAlwaysPrompt; | ||
| 135 | + }, | ||
| 136 | + | ||
| 137 | + set offlineAlwaysPrompt(val) { | ||
| 138 | + this._offlineAlwaysPrompt = val; | ||
| 139 | + this.updateAdditionalAuth(); | ||
| 140 | + }, | ||
| 141 | + | ||
| 142 | + /** Have we already gotten offline access from Google during this session? */ | ||
| 143 | + offlineGranted: false, | ||
| 144 | + | ||
| 145 | + /** <google-js-api> */ | ||
| 146 | + _apiLoader: null, | ||
| 147 | + | ||
| 148 | + /** an array of wanted scopes. oauth2 argument */ | ||
| 149 | + _requestedScopeArray: [], | ||
| 150 | + | ||
| 151 | + /** _requestedScopeArray as string */ | ||
| 152 | + get requestedScopes() { | ||
| 153 | + return this._requestedScopeArray.join(' '); | ||
| 154 | + }, | ||
| 155 | + | ||
| 156 | + /** Is user signed in? */ | ||
| 157 | + _signedIn: false, | ||
| 158 | + | ||
| 159 | + /** Currently granted scopes */ | ||
| 160 | + _grantedScopeArray: [], | ||
| 161 | + | ||
| 162 | + /** True if additional authorization is required */ | ||
| 163 | + _needAdditionalAuth: true, | ||
| 164 | + | ||
| 165 | + /** True if have google+ scopes */ | ||
| 166 | + _hasPlusScopes: false, | ||
| 167 | + | ||
| 168 | + /** | ||
| 169 | + * array of <google-signin-aware> | ||
| 170 | + * state changes are broadcast to them | ||
| 171 | + */ | ||
| 172 | + signinAwares: [], | ||
| 173 | + | ||
| 174 | + init: function() { | ||
| 175 | + this._apiLoader = document.createElement('google-js-api'); | ||
| 176 | + this._apiLoader.addEventListener('js-api-load', this.loadAuth2.bind(this)); | ||
| 177 | + }, | ||
| 178 | + | ||
| 179 | + loadAuth2: function() { | ||
| 180 | + gapi.load('auth2', this.initAuth2.bind(this)); | ||
| 181 | + }, | ||
| 182 | + | ||
| 183 | + initAuth2: function() { | ||
| 184 | + if (!('gapi' in window) || !('auth2' in window.gapi) || !this.clientId) { | ||
| 185 | + return; | ||
| 186 | + } | ||
| 187 | + var auth = gapi.auth2.init({ | ||
| 188 | + 'client_id': this.clientId, | ||
| 189 | + 'cookie_policy': this.cookiePolicy, | ||
| 190 | + 'scope': this.requestedScopes, | ||
| 191 | + 'hosted_domain': this.hostedDomain | ||
| 192 | + }); | ||
| 193 | + | ||
| 194 | + auth.currentUser.listen(this.handleUserUpdate.bind(this)); | ||
| 195 | + | ||
| 196 | + auth.then( | ||
| 197 | + function success() { | ||
| 198 | + // Let the current user listener trigger the changes. | ||
| 199 | + }, | ||
| 200 | + function error(error) { | ||
| 201 | + console.error(error); | ||
| 202 | + } | ||
| 203 | + ); | ||
| 204 | + }, | ||
| 205 | + | ||
| 206 | + handleUserUpdate: function(newPrimaryUser) { | ||
| 207 | + // update and broadcast currentUser | ||
| 208 | + var isSignedIn = newPrimaryUser.isSignedIn(); | ||
| 209 | + if (isSignedIn != this._signedIn) { | ||
| 210 | + this._signedIn = isSignedIn; | ||
| 211 | + for (var i=0; i<this.signinAwares.length; i++) { | ||
| 212 | + this.signinAwares[i]._setSignedIn(isSignedIn); | ||
| 213 | + } | ||
| 214 | + } | ||
| 215 | + | ||
| 216 | + // update granted scopes | ||
| 217 | + this._grantedScopeArray = this.strToScopeArray( | ||
| 218 | + newPrimaryUser.getGrantedScopes()); | ||
| 219 | + // console.log(this._grantedScopeArray); | ||
| 220 | + this.updateAdditionalAuth(); | ||
| 221 | + | ||
| 222 | + var response = newPrimaryUser.getAuthResponse(); | ||
| 223 | + for (var i=0; i<this.signinAwares.length; i++) { | ||
| 224 | + this.signinAwares[i]._updateScopeStatus(response); | ||
| 225 | + } | ||
| 226 | + }, | ||
| 227 | + | ||
| 228 | + setOfflineCode: function(code) { | ||
| 229 | + for (var i=0; i<this.signinAwares.length; i++) { | ||
| 230 | + this.signinAwares[i]._updateOfflineCode(code); | ||
| 231 | + } | ||
| 232 | + }, | ||
| 233 | + | ||
| 234 | + /** convert scope string to scope array */ | ||
| 235 | + strToScopeArray: function(str) { | ||
| 236 | + if (!str) { | ||
| 237 | + return []; | ||
| 238 | + } | ||
| 239 | + // remove extra spaces, then split | ||
| 240 | + var scopes = str.replace(/\ +/g, ' ').trim().split(' '); | ||
| 241 | + for (var i=0; i<scopes.length; i++) { | ||
| 242 | + scopes[i] = scopes[i].toLowerCase(); | ||
| 243 | + // Handle scopes that will be deprecated but are still returned with their old value | ||
| 244 | + if (scopes[i] === 'https://www.googleapis.com/auth/userinfo.profile') { | ||
| 245 | + scopes[i] = 'profile'; | ||
| 246 | + } | ||
| 247 | + if (scopes[i] === 'https://www.googleapis.com/auth/userinfo.email') { | ||
| 248 | + scopes[i] = 'email'; | ||
| 249 | + } | ||
| 250 | + } | ||
| 251 | + // return with duplicates filtered out | ||
| 252 | + return scopes.filter( function(value, index, self) { | ||
| 253 | + return self.indexOf(value) === index; | ||
| 254 | + }); | ||
| 255 | + }, | ||
| 256 | + | ||
| 257 | + /** true if scopes have google+ scopes */ | ||
| 258 | + isPlusScope: function(scope) { | ||
| 259 | + return (scope.indexOf('/auth/games') > -1) | ||
| 260 | + || (scope.indexOf('auth/plus.') > -1 && scope.indexOf('auth/plus.me') < 0); | ||
| 261 | + }, | ||
| 262 | + | ||
| 263 | + /** true if scopes have been granted */ | ||
| 264 | + hasGrantedScopes: function(scopeStr) { | ||
| 265 | + var scopes = this.strToScopeArray(scopeStr); | ||
| 266 | + for (var i=0; i< scopes.length; i++) { | ||
| 267 | + if (this._grantedScopeArray.indexOf(scopes[i]) === -1) | ||
| 268 | + return false; | ||
| 269 | + } | ||
| 270 | + return true; | ||
| 271 | + }, | ||
| 272 | + | ||
| 273 | + /** request additional scopes */ | ||
| 274 | + requestScopes: function(newScopeStr) { | ||
| 275 | + var newScopes = this.strToScopeArray(newScopeStr); | ||
| 276 | + var scopesUpdated = false; | ||
| 277 | + for (var i=0; i<newScopes.length; i++) { | ||
| 278 | + if (this._requestedScopeArray.indexOf(newScopes[i]) === -1) { | ||
| 279 | + this._requestedScopeArray.push(newScopes[i]); | ||
| 280 | + scopesUpdated = true; | ||
| 281 | + } | ||
| 282 | + } | ||
| 283 | + if (scopesUpdated) { | ||
| 284 | + this.updateAdditionalAuth(); | ||
| 285 | + this.updatePlusScopes(); | ||
| 286 | + } | ||
| 287 | + }, | ||
| 288 | + | ||
| 289 | + /** update status of _needAdditionalAuth */ | ||
| 290 | + updateAdditionalAuth: function() { | ||
| 291 | + var needMoreAuth = false; | ||
| 292 | + if ((this.offlineAlwaysPrompt || this.offline ) && !this.offlineGranted) { | ||
| 293 | + needMoreAuth = true; | ||
| 294 | + } else { | ||
| 295 | + for (var i=0; i<this._requestedScopeArray.length; i++) { | ||
| 296 | + if (this._grantedScopeArray.indexOf(this._requestedScopeArray[i]) === -1) { | ||
| 297 | + needMoreAuth = true; | ||
| 298 | + break; | ||
| 299 | + } | ||
| 300 | + } | ||
| 301 | + } | ||
| 302 | + if (this._needAdditionalAuth != needMoreAuth) { | ||
| 303 | + this._needAdditionalAuth = needMoreAuth; | ||
| 304 | + // broadcast new value | ||
| 305 | + for (var i=0; i<this.signinAwares.length; i++) { | ||
| 306 | + this.signinAwares[i]._setNeedAdditionalAuth(needMoreAuth); | ||
| 307 | + } | ||
| 308 | + } | ||
| 309 | + }, | ||
| 310 | + | ||
| 311 | + updatePlusScopes: function() { | ||
| 312 | + var hasPlusScopes = false; | ||
| 313 | + for (var i = 0; i < this._requestedScopeArray.length; i++) { | ||
| 314 | + if (this.isPlusScope(this._requestedScopeArray[i])) { | ||
| 315 | + hasPlusScopes = true; | ||
| 316 | + break; | ||
| 317 | + } | ||
| 318 | + } | ||
| 319 | + if (this._hasPlusScopes != hasPlusScopes) { | ||
| 320 | + this._hasPlusScopes = hasPlusScopes; | ||
| 321 | + for (var i=0; i<this.signinAwares.length; i++) { | ||
| 322 | + this.signinAwares[i]._setHasPlusScopes(hasPlusScopes); | ||
| 323 | + } | ||
| 324 | + } | ||
| 325 | + }, | ||
| 326 | + /** | ||
| 327 | + * attached <google-signin-aware> | ||
| 328 | + * @param {<google-signin-aware>} aware element to add | ||
| 329 | + */ | ||
| 330 | + attachSigninAware: function(aware) { | ||
| 331 | + if (this.signinAwares.indexOf(aware) == -1) { | ||
| 332 | + this.signinAwares.push(aware); | ||
| 333 | + // Initialize aware properties | ||
| 334 | + aware._setNeedAdditionalAuth(this._needAdditionalAuth); | ||
| 335 | + aware._setSignedIn(this._signedIn); | ||
| 336 | + aware._setHasPlusScopes(this._hasPlusScopes); | ||
| 337 | + } else { | ||
| 338 | + console.warn('signinAware attached more than once', aware); | ||
| 339 | + } | ||
| 340 | + }, | ||
| 341 | + | ||
| 342 | + detachSigninAware: function(aware) { | ||
| 343 | + var index = this.signinAwares.indexOf(aware); | ||
| 344 | + if (index != -1) { | ||
| 345 | + this.signinAwares.splice(index, 1); | ||
| 346 | + } else { | ||
| 347 | + console.warn('Trying to detach unattached signin-aware'); | ||
| 348 | + } | ||
| 349 | + }, | ||
| 350 | + | ||
| 351 | + /** returns scopes not granted */ | ||
| 352 | + getMissingScopes: function() { | ||
| 353 | + return this._requestedScopeArray.filter( function(scope) { | ||
| 354 | + return this._grantedScopeArray.indexOf(scope) === -1; | ||
| 355 | + }.bind(this)).join(' '); | ||
| 356 | + }, | ||
| 357 | + | ||
| 358 | + assertAuthInitialized: function() { | ||
| 359 | + if (!this.clientId) { | ||
| 360 | + throw new Error("AuthEngine not initialized. clientId has not been configured."); | ||
| 361 | + } | ||
| 362 | + if (!('gapi' in window)) { | ||
| 363 | + throw new Error("AuthEngine not initialized. gapi has not loaded."); | ||
| 364 | + } | ||
| 365 | + if (!('auth2' in window.gapi)) { | ||
| 366 | + throw new Error("AuthEngine not initialized. auth2 not loaded."); | ||
| 367 | + } | ||
| 368 | + }, | ||
| 369 | + | ||
| 370 | + /** pops up sign-in dialog */ | ||
| 371 | + signIn: function() { | ||
| 372 | + this.assertAuthInitialized(); | ||
| 373 | + var params = { | ||
| 374 | + 'scope': this.getMissingScopes() | ||
| 375 | + }; | ||
| 376 | + | ||
| 377 | + // Proxy specific attributes through to the signIn options. | ||
| 378 | + Object.keys(ProxyLoginAttributes).forEach(function(key) { | ||
| 379 | + if (this[key] && this[key] !== '') { | ||
| 380 | + params[ProxyLoginAttributes[key]] = this[key]; | ||
| 381 | + } | ||
| 382 | + }, this); | ||
| 383 | + | ||
| 384 | + var promise; | ||
| 385 | + var user = gapi.auth2.getAuthInstance().currentUser.get(); | ||
| 386 | + if (!(this.offline || this.offlineAlwaysPrompt)) { | ||
| 387 | + if (user.getGrantedScopes()) { | ||
| 388 | + // additional auth, skip multiple account dialog | ||
| 389 | + promise = user.grant(params); | ||
| 390 | + } else { | ||
| 391 | + // initial signin | ||
| 392 | + promise = gapi.auth2.getAuthInstance().signIn(params); | ||
| 393 | + } | ||
| 394 | + } else { | ||
| 395 | + params.redirect_uri = 'postmessage'; | ||
| 396 | + if (this.offlineAlwaysPrompt) { | ||
| 397 | + params.approval_prompt = 'force'; | ||
| 398 | + } | ||
| 399 | + | ||
| 400 | + // Despite being documented at https://goo.gl/tiO0Bk | ||
| 401 | + // It doesn't seem like user.grantOfflineAccess() actually exists in | ||
| 402 | + // the current version of the Google Sign-In JS client we're using | ||
| 403 | + // through GoogleWebComponents. So in the offline case, we will not | ||
| 404 | + // distinguish between a first auth and an additional one. | ||
| 405 | + promise = gapi.auth2.getAuthInstance().grantOfflineAccess(params); | ||
| 406 | + } | ||
| 407 | + promise.then( | ||
| 408 | + function success(response) { | ||
| 409 | + // If login was offline, response contains one string "code" | ||
| 410 | + // Otherwise it contains the user object already | ||
| 411 | + var newUser; | ||
| 412 | + if (response.code) { | ||
| 413 | + AuthEngine.offlineGranted = true; | ||
| 414 | + newUser = gapi.auth2.getAuthInstance().currentUser.get(); | ||
| 415 | + AuthEngine.setOfflineCode(response.code); | ||
| 416 | + } else { | ||
| 417 | + newUser = response; | ||
| 418 | + } | ||
| 419 | + | ||
| 420 | + var authResponse = newUser.getAuthResponse(); | ||
| 421 | + // Let the current user listener trigger the changes. | ||
| 422 | + }, | ||
| 423 | + function error(error) { | ||
| 424 | + if ("Access denied." == error.reason) { | ||
| 425 | + // Access denied is not an error, user hit cancel | ||
| 426 | + return; | ||
| 427 | + } else { | ||
| 428 | + console.error(error); | ||
| 429 | + } | ||
| 430 | + } | ||
| 431 | + ); | ||
| 432 | + }, | ||
| 433 | + | ||
| 434 | + /** signs user out */ | ||
| 435 | + signOut: function() { | ||
| 436 | + this.assertAuthInitialized(); | ||
| 437 | + gapi.auth2.getAuthInstance().signOut().then( | ||
| 438 | + function success() { | ||
| 439 | + // Let the current user listener trigger the changes. | ||
| 440 | + }, | ||
| 441 | + function error(error) { | ||
| 442 | + console.error(error); | ||
| 443 | + } | ||
| 444 | + ); | ||
| 445 | + } | ||
| 446 | + }; | ||
| 447 | + | ||
| 448 | + AuthEngine.init(); | ||
| 449 | + | ||
| 450 | +/** | ||
| 451 | +`google-signin-aware` is used to enable authentication in custom elements by | ||
| 452 | +interacting with a google-signin element that needs to be present somewhere | ||
| 453 | +on the page. | ||
| 454 | + | ||
| 455 | +The `scopes` attribute allows you to specify which scope permissions are required | ||
| 456 | +(e.g do you want to allow interaction with the Google Drive API). | ||
| 457 | + | ||
| 458 | +The `google-signin-aware-success` event is triggered when a user successfully | ||
| 459 | +authenticates. If either `offline` or `offlineAlwaysPrompt` is set to true, successful | ||
| 460 | +authentication will also trigger the `google-signin-offline-success`event. | ||
| 461 | +The `google-signin-aware-signed-out` event is triggered when a user explicitly | ||
| 462 | +signs out via the google-signin element. | ||
| 463 | + | ||
| 464 | +You can bind to `isAuthorized` property to monitor authorization state. | ||
| 465 | +##### Example | ||
| 466 | + | ||
| 467 | + <google-signin-aware scopes="https://www.googleapis.com/auth/drive"></google-signin-aware> | ||
| 468 | + | ||
| 469 | + | ||
| 470 | +##### Example with offline | ||
| 471 | + <template id="awareness" is="dom-bind"> | ||
| 472 | + <google-signin-aware | ||
| 473 | + scopes="https://www.googleapis.com/auth/drive" | ||
| 474 | + offline | ||
| 475 | + on-google-signin-aware-success="handleSignin" | ||
| 476 | + on-google-signin-offline-success="handleOffline"></google-signin-aware> | ||
| 477 | + <\/template> | ||
| 478 | + <script> | ||
| 479 | + var aware = document.querySelector('#awareness'); | ||
| 480 | + aware.handleSignin = function(response) { | ||
| 481 | + var user = gapi.auth2.getAuthInstance().currentUser.get(); | ||
| 482 | + console.log('User name: ' + user.getBasicProfile().getName()); | ||
| 483 | + }; | ||
| 484 | + aware.handleOffline = function(response) { | ||
| 485 | + console.log('Offline code received: ' + response.detail.code); | ||
| 486 | + // Here you would POST response.detail.code to your webserver, which can | ||
| 487 | + // exchange the authorization code for an access token. More info at: | ||
| 488 | + // https://developers.google.com/identity/protocols/OAuth2WebServer | ||
| 489 | + }; | ||
| 490 | + <\/script> | ||
| 491 | +*/ | ||
| 492 | + Polymer({ | ||
| 493 | + | ||
| 494 | + is: 'google-signin-aware', | ||
| 495 | + | ||
| 496 | + /** | ||
| 497 | + * Fired when this scope has been authorized | ||
| 498 | + * @param {Object} result Authorization result. | ||
| 499 | + * @event google-signin-aware-success | ||
| 500 | + */ | ||
| 501 | + /** | ||
| 502 | + * Fired when an offline authorization is successful. | ||
| 503 | + * @param {Object} detail | ||
| 504 | + * @param {string} detail.code The one-time authorization code from Google. | ||
| 505 | + * Your application can exchange this for an `access_token` and `refresh_token` | ||
| 506 | + * @event google-signin-offline-success | ||
| 507 | + */ | ||
| 508 | + /** | ||
| 509 | + * Fired when this scope is not authorized | ||
| 510 | + * @event google-signin-aware-signed-out | ||
| 511 | + */ | ||
| 512 | + properties: { | ||
| 513 | + /** | ||
| 514 | + * App package name for android over-the-air installs. | ||
| 515 | + * See the relevant [docs](https://developers.google.com/+/web/signin/android-app-installs) | ||
| 516 | + */ | ||
| 517 | + appPackageName: { | ||
| 518 | + type: String, | ||
| 519 | + observer: '_appPackageNameChanged' | ||
| 520 | + }, | ||
| 521 | + /** | ||
| 522 | + * a Google Developers clientId reference | ||
| 523 | + */ | ||
| 524 | + clientId: { | ||
| 525 | + type: String, | ||
| 526 | + observer: '_clientIdChanged' | ||
| 527 | + }, | ||
| 528 | + | ||
| 529 | + /** | ||
| 530 | + * The cookie policy defines what URIs have access to the session cookie | ||
| 531 | + * remembering the user's sign-in state. | ||
| 532 | + * See the relevant [docs](https://developers.google.com/+/web/signin/reference#determining_a_value_for_cookie_policy) for more information. | ||
| 533 | + * @default 'single_host_origin' | ||
| 534 | + */ | ||
| 535 | + cookiePolicy: { | ||
| 536 | + type: String, | ||
| 537 | + observer: '_cookiePolicyChanged' | ||
| 538 | + }, | ||
| 539 | + | ||
| 540 | + /** | ||
| 541 | + * The app activity types you want to write on behalf of the user | ||
| 542 | + * (e.g http://schemas.google.com/AddActivity) | ||
| 543 | + * | ||
| 544 | + */ | ||
| 545 | + requestVisibleActions: { | ||
| 546 | + type: String, | ||
| 547 | + observer: '_requestVisibleActionsChanged' | ||
| 548 | + }, | ||
| 549 | + | ||
| 550 | + /** | ||
| 551 | + * The Google Apps domain to which users must belong to sign in. | ||
| 552 | + * See the relevant [docs](https://developers.google.com/identity/sign-in/web/reference) for more information. | ||
| 553 | + */ | ||
| 554 | + hostedDomain: { | ||
| 555 | + type: String, | ||
| 556 | + observer: '_hostedDomainChanged' | ||
| 557 | + }, | ||
| 558 | + | ||
| 559 | + /** | ||
| 560 | + * Allows for offline `access_token` retrieval during the signin process. | ||
| 561 | + * See also `offlineAlwaysPrompt`. You only need to set one of the two; if both | ||
| 562 | + * are set, the behavior of `offlineAlwaysPrompt` will override `offline`. | ||
| 563 | + */ | ||
| 564 | + offline: { | ||
| 565 | + type: Boolean, | ||
| 566 | + value: false, | ||
| 567 | + observer: '_offlineChanged' | ||
| 568 | + }, | ||
| 569 | + | ||
| 570 | + /** | ||
| 571 | + * Works the same as `offline` with the addition that it will always | ||
| 572 | + * force a re-prompt to the user, guaranteeing that you will get a | ||
| 573 | + * refresh_token even if the user has already granted offline access to | ||
| 574 | + * this application. You only need to set one of `offline` or | ||
| 575 | + * `offlineAlwaysPrompt`, not both. | ||
| 576 | + */ | ||
| 577 | + offlineAlwaysPrompt: { | ||
| 578 | + type: Boolean, | ||
| 579 | + value: false, | ||
| 580 | + observer: '_offlineAlwaysPromptChanged' | ||
| 581 | + }, | ||
| 582 | + | ||
| 583 | + /** | ||
| 584 | + * The scopes to provide access to (e.g https://www.googleapis.com/auth/drive) | ||
| 585 | + * and should be space-delimited. | ||
| 586 | + */ | ||
| 587 | + scopes: { | ||
| 588 | + type: String, | ||
| 589 | + value: 'profile', | ||
| 590 | + observer: '_scopesChanged' | ||
| 591 | + }, | ||
| 592 | + | ||
| 593 | + /** | ||
| 594 | + * True if user is signed in | ||
| 595 | + */ | ||
| 596 | + signedIn: { | ||
| 597 | + type: Boolean, | ||
| 598 | + notify: true, | ||
| 599 | + readOnly: true | ||
| 600 | + }, | ||
| 601 | + | ||
| 602 | + /** | ||
| 603 | + * True if authorizations for *this* element have been granted | ||
| 604 | + */ | ||
| 605 | + isAuthorized: { | ||
| 606 | + type: Boolean, | ||
| 607 | + notify: true, | ||
| 608 | + readOnly: true, | ||
| 609 | + value: false | ||
| 610 | + }, | ||
| 611 | + | ||
| 612 | + /** | ||
| 613 | + * True if additional authorizations for *any* element are required | ||
| 614 | + */ | ||
| 615 | + needAdditionalAuth: { | ||
| 616 | + type: Boolean, | ||
| 617 | + notify: true, | ||
| 618 | + readOnly: true | ||
| 619 | + }, | ||
| 620 | + | ||
| 621 | + /** | ||
| 622 | + * True if *any* element has google+ scopes | ||
| 623 | + */ | ||
| 624 | + hasPlusScopes: { | ||
| 625 | + type: Boolean, | ||
| 626 | + value: false, | ||
| 627 | + notify: true, | ||
| 628 | + readOnly: true | ||
| 629 | + } | ||
| 630 | + }, | ||
| 631 | + | ||
| 632 | + attached: function() { | ||
| 633 | + AuthEngine.attachSigninAware(this); | ||
| 634 | + }, | ||
| 635 | + | ||
| 636 | + detached: function() { | ||
| 637 | + AuthEngine.detachSigninAware(this); | ||
| 638 | + }, | ||
| 639 | + | ||
| 640 | + /** pops up the authorization dialog */ | ||
| 641 | + signIn: function() { | ||
| 642 | + AuthEngine.signIn(); | ||
| 643 | + }, | ||
| 644 | + | ||
| 645 | + /** signs user out */ | ||
| 646 | + signOut: function() { | ||
| 647 | + AuthEngine.signOut(); | ||
| 648 | + }, | ||
| 649 | + | ||
| 650 | + _appPackageNameChanged: function(newName, oldName) { | ||
| 651 | + AuthEngine.appPackageName = newName; | ||
| 652 | + }, | ||
| 653 | + | ||
| 654 | + _clientIdChanged: function(newId, oldId) { | ||
| 655 | + AuthEngine.clientId = newId; | ||
| 656 | + }, | ||
| 657 | + | ||
| 658 | + _cookiePolicyChanged: function(newPolicy, oldPolicy) { | ||
| 659 | + AuthEngine.cookiePolicy = newPolicy; | ||
| 660 | + }, | ||
| 661 | + | ||
| 662 | + _requestVisibleActionsChanged: function(newVal, oldVal) { | ||
| 663 | + AuthEngine.requestVisibleActions = newVal; | ||
| 664 | + }, | ||
| 665 | + | ||
| 666 | + _hostedDomainChanged: function(newVal, oldVal) { | ||
| 667 | + AuthEngine.hostedDomain = newVal; | ||
| 668 | + }, | ||
| 669 | + | ||
| 670 | + _offlineChanged: function(newVal, oldVal) { | ||
| 671 | + AuthEngine.offline = newVal; | ||
| 672 | + }, | ||
| 673 | + | ||
| 674 | + _offlineAlwaysPromptChanged: function(newVal, oldVal) { | ||
| 675 | + AuthEngine.offlineAlwaysPrompt = newVal; | ||
| 676 | + }, | ||
| 677 | + | ||
| 678 | + _scopesChanged: function(newVal, oldVal) { | ||
| 679 | + AuthEngine.requestScopes(newVal); | ||
| 680 | + this._updateScopeStatus(); | ||
| 681 | + }, | ||
| 682 | + | ||
| 683 | + _updateScopeStatus: function(user) { | ||
| 684 | + var newAuthorized = this.signedIn && AuthEngine.hasGrantedScopes(this.scopes); | ||
| 685 | + if (newAuthorized !== this.isAuthorized) { | ||
| 686 | + this._setIsAuthorized(newAuthorized); | ||
| 687 | + if (newAuthorized) { | ||
| 688 | + this.fire('google-signin-aware-success', user); | ||
| 689 | + } | ||
| 690 | + else { | ||
| 691 | + this.fire('google-signin-aware-signed-out', user); | ||
| 692 | + } | ||
| 693 | + } | ||
| 694 | + }, | ||
| 695 | + | ||
| 696 | + _updateOfflineCode: function(code) { | ||
| 697 | + if (code) { | ||
| 698 | + this.fire('google-signin-offline-success', {code: code}); | ||
| 699 | + } | ||
| 700 | + } | ||
| 701 | + }); | ||
| 702 | + })(); | ||
| 703 | +</script> |
bower_components/google-signin/google-signin.css
0 → 100644
| 1 | +:host { | ||
| 2 | + display: inline-block; | ||
| 3 | + position: relative; | ||
| 4 | + box-sizing: border-box; | ||
| 5 | + margin: 0 0.29em; | ||
| 6 | + background: transparent; | ||
| 7 | + text-align: center; | ||
| 8 | + font: inherit; | ||
| 9 | + outline: none; | ||
| 10 | + border-radius: 3px; | ||
| 11 | + -webkit-user-select: none; | ||
| 12 | + user-select: none; | ||
| 13 | + cursor: pointer; | ||
| 14 | + z-index: 0; | ||
| 15 | +} | ||
| 16 | + | ||
| 17 | +:host([disabled]) { | ||
| 18 | + cursor: auto; | ||
| 19 | + pointer-events: none; | ||
| 20 | +} | ||
| 21 | + | ||
| 22 | +:host([disabled]) #button { | ||
| 23 | + background: #eaeaea; | ||
| 24 | + color: #a8a8a8; | ||
| 25 | +} | ||
| 26 | + | ||
| 27 | +#button { | ||
| 28 | + position: relative; | ||
| 29 | + outline: none; | ||
| 30 | + font-size: 14px; | ||
| 31 | + font-weight: 400; | ||
| 32 | + font-family: 'RobotoDraft','Roboto',arial,sans-serif; | ||
| 33 | + white-space: nowrap; | ||
| 34 | + border-radius: inherit; | ||
| 35 | +} | ||
| 36 | + | ||
| 37 | +iron-icon { | ||
| 38 | + width: 22px; | ||
| 39 | + height: 22px; | ||
| 40 | + margin: 6px; | ||
| 41 | +} | ||
| 42 | + | ||
| 43 | +.icon { | ||
| 44 | + display: inline-block; | ||
| 45 | + vertical-align: middle; | ||
| 46 | +} | ||
| 47 | + | ||
| 48 | +#shadow { | ||
| 49 | + border-radius: inherit; | ||
| 50 | +} | ||
| 51 | + | ||
| 52 | +#ripple { | ||
| 53 | + pointer-events: none; | ||
| 54 | +} | ||
| 55 | + | ||
| 56 | +.button-content { | ||
| 57 | + outline: none; | ||
| 58 | +} | ||
| 59 | + | ||
| 60 | +.buttonText { | ||
| 61 | + display: inline-block; | ||
| 62 | + vertical-align: middle; | ||
| 63 | + padding-right: .8em; | ||
| 64 | +} | ||
| 65 | + | ||
| 66 | +/* | ||
| 67 | + * Dark Theme | ||
| 68 | + */ | ||
| 69 | +.theme-dark { | ||
| 70 | + background: #da4336; | ||
| 71 | + color: #ffffff; | ||
| 72 | + border: 1px solid transparent; | ||
| 73 | +} | ||
| 74 | + | ||
| 75 | +.theme-dark.signedIn-true.additionalAuth-false { | ||
| 76 | + background: #999; | ||
| 77 | + border: 1px solid #888; | ||
| 78 | +} | ||
| 79 | + | ||
| 80 | +.theme-dark.signedIn-true.additionalAuth-false:hover, | ||
| 81 | +.theme-dark.signedIn-true.additionalAuth-false:focus { | ||
| 82 | + background: #aaa; | ||
| 83 | +} | ||
| 84 | + | ||
| 85 | +:host([noink]) .theme-dark:hover, | ||
| 86 | +:host([noink]) .theme-dark:focus { | ||
| 87 | + background: #e74b37; | ||
| 88 | +} | ||
| 89 | + | ||
| 90 | +:host([noink]) .theme-dark.signedIn-true.additionalAuth-false:hover, | ||
| 91 | +:host([noink]) .theme-dark.signedIn-true.additionalAuth-false:focus { | ||
| 92 | + background: #aaa; | ||
| 93 | +} | ||
| 94 | + | ||
| 95 | +/* | ||
| 96 | + * Light Theme | ||
| 97 | + */ | ||
| 98 | +.theme-light { | ||
| 99 | + background: #fff; | ||
| 100 | + color: #737373; | ||
| 101 | + border: 1px solid #d9d9d9; | ||
| 102 | +} | ||
| 103 | + | ||
| 104 | +.theme-light.signedIn-true.additionalAuth-false { | ||
| 105 | + background: #c0c0c0; | ||
| 106 | + color: #fff; | ||
| 107 | + border: #888 1px solid; | ||
| 108 | +} | ||
| 109 | + | ||
| 110 | +.theme-light.signedIn-true.additionalAuth-false:hover, | ||
| 111 | +.theme-light.signedIn-true.additionalAuth-false:focus { | ||
| 112 | + background: #aaa; | ||
| 113 | +} | ||
| 114 | + | ||
| 115 | +:host([noink]) .theme-light .button-content:hover, | ||
| 116 | +:host([noink]) .theme-light:focus { | ||
| 117 | + border: 1px solid #c0c0c0; | ||
| 118 | +} | ||
| 119 | + | ||
| 120 | +:host([noink]) .theme-light.signedIn-true.additionalAuth-false:hover, | ||
| 121 | +:host([noink]) .theme-light.signedIn-true.additionalAuth-false:focus { | ||
| 122 | + background: #aaa; | ||
| 123 | +} | ||
| 124 | + | ||
| 125 | +/* | ||
| 126 | + * Icon Only Width | ||
| 127 | + */ | ||
| 128 | +.width-iconOnly .buttonText { | ||
| 129 | + display: none; | ||
| 130 | +} | ||
| 131 | + | ||
| 132 | +/* | ||
| 133 | + * Tall Height | ||
| 134 | + */ | ||
| 135 | +.height-tall .buttonText { | ||
| 136 | + font-size: 15px; | ||
| 137 | + font-weight: 700; | ||
| 138 | +} | ||
| 139 | + | ||
| 140 | +.height-tall iron-icon { | ||
| 141 | + width: 30px; | ||
| 142 | + height: 30px; | ||
| 143 | + margin: 8px; | ||
| 144 | +} | ||
| 145 | + | ||
| 146 | +/* | ||
| 147 | + * Short Height | ||
| 148 | + */ | ||
| 149 | +.height-short .buttonText { | ||
| 150 | + font-size: 11px; | ||
| 151 | +} | ||
| 152 | + | ||
| 153 | +.height-short iron-icon { | ||
| 154 | + width: 16px; | ||
| 155 | + height: 16px; | ||
| 156 | + margin: 3px; | ||
| 157 | +} | ||
| 158 | + | ||
| 159 | + | ||
| 160 | +/* | ||
| 161 | + * Branding | ||
| 162 | + */ | ||
| 163 | + | ||
| 164 | +/* Google Scopes */ | ||
| 165 | + | ||
| 166 | +/* Dark Theme */ | ||
| 167 | +.brand-google.theme-dark { | ||
| 168 | + background: #4184F3; | ||
| 169 | + color: #fff; | ||
| 170 | + border: 1px solid #3266d5; | ||
| 171 | +} | ||
| 172 | + | ||
| 173 | +.brand-google.theme-dark #ripple { | ||
| 174 | + color: #1b39a8; | ||
| 175 | +} | ||
| 176 | + | ||
| 177 | +:host([noink]) .brand-google.theme-dark:hover, | ||
| 178 | +:host([noink]) .brand-google.theme-dark:focus { | ||
| 179 | + background: #e74b37; | ||
| 180 | +} | ||
| 181 | + | ||
| 182 | +.brand-google.theme-light .icon { | ||
| 183 | + color: #4184F3; | ||
| 184 | +} | ||
| 185 | + | ||
| 186 | +.brand-google.theme-light.signedIn-true.additionalAuth-false .icon { | ||
| 187 | + color: #fff; | ||
| 188 | +} | ||
| 189 | + | ||
| 190 | +.brand-google.theme-light #ripple { | ||
| 191 | + color: #444; | ||
| 192 | +} | ||
| 193 | + | ||
| 194 | +:host([noink]) .brand-google.theme-light:hover, | ||
| 195 | +:host([noink]) .brand-google.theme-light:focus { | ||
| 196 | + border: 1px solid #c0c0c0; | ||
| 197 | +} | ||
| 198 | + | ||
| 199 | +.brand-google-plus.theme-dark { | ||
| 200 | + background: #da4336; | ||
| 201 | + color: #fff; | ||
| 202 | + border: 1px solid transparent; | ||
| 203 | +} | ||
| 204 | + | ||
| 205 | +.brand-google-plus.theme-dark #ripple { | ||
| 206 | + color: #c43828; | ||
| 207 | +} | ||
| 208 | + | ||
| 209 | +/* Light Theme */ | ||
| 210 | +.brand-google-plus.theme-light { | ||
| 211 | + background: #fff; | ||
| 212 | + color: #737373; | ||
| 213 | + border: 1px solid #d9d9d9; | ||
| 214 | +} | ||
| 215 | + | ||
| 216 | +.brand-google-plus.theme-light .icon { | ||
| 217 | + color: #e74b37; | ||
| 218 | +} | ||
| 219 | + | ||
| 220 | +.brand-google-plus.theme-light.signedIn-true.additionalAuth-false .icon { | ||
| 221 | + color: #fff; | ||
| 222 | +} | ||
| 223 | + | ||
| 224 | +.brand-google-plus.theme-light #ripple { | ||
| 225 | + color: #400; | ||
| 226 | +} | ||
| 227 | + | ||
| 228 | +:host([noink]) .brand-google-plus.theme-light:hover, | ||
| 229 | +:host([noink]) .brand-google-plus.theme-light:focus { | ||
| 230 | + border: 1px solid #c0c0c0; | ||
| 231 | +} |
bower_components/google-signin/google-signin.html
0 → 100644
| 1 | +<link rel="import" href="../polymer/polymer.html"> | ||
| 2 | +<link rel="import" href="google-signin-aware.html"> | ||
| 3 | +<link rel="import" href="../iron-icon/iron-icon.html"> | ||
| 4 | +<link rel="import" href="../iron-icons/iron-icons.html"> | ||
| 5 | +<link rel="import" href="../font-roboto/roboto.html"> | ||
| 6 | +<link rel="import" href="../google-apis/google-js-api.html"> | ||
| 7 | +<link rel="import" href="../paper-ripple/paper-ripple.html"> | ||
| 8 | +<link rel="import" href="../paper-material/paper-material.html"> | ||
| 9 | +<link rel="import" href="../iron-flex-layout/classes/iron-flex-layout.html"> | ||
| 10 | +<link rel="import" href="google-icons.html"> | ||
| 11 | + | ||
| 12 | +<dom-module id="google-signin"> | ||
| 13 | + <link rel="import" type="css" href="google-signin.css"> | ||
| 14 | + <template> | ||
| 15 | + <google-signin-aware id="aware" | ||
| 16 | + app-package-name="{{appPackageName}}" | ||
| 17 | + client-id="{{clientId}}" | ||
| 18 | + cookie-policy="{{cookiePolicy}}" | ||
| 19 | + request-visible-actions="{{requestVisibleActions}}" | ||
| 20 | + hosted-domain="{{hostedDomain}}" | ||
| 21 | + offline="{{offline}}" | ||
| 22 | + offline-always-prompt="{{offlineAlwaysPrompt}}" | ||
| 23 | + scopes="{{scopes}}" | ||
| 24 | + signed-in="{{signedIn}}" | ||
| 25 | + is-authorized="{{isAuthorized}}" | ||
| 26 | + need-additional-auth="{{needAdditionalAuth}}" | ||
| 27 | + has-plus-scopes="{{hasPlusScopes}}"></google-signin-aware> | ||
| 28 | + <template is="dom-if" if="{{raised}}"> | ||
| 29 | + <paper-material id="shadow" class="fit" elevation="2" animated></paper-material> | ||
| 30 | + </template> | ||
| 31 | + <div id="button" | ||
| 32 | + class$="[[_computeButtonClass(height, width, theme, signedIn, _brand, needAdditionalAuth)]]"> | ||
| 33 | + | ||
| 34 | + <paper-ripple id="ripple" class="fit"></paper-ripple> | ||
| 35 | + <!-- this div is needed to position the ripple behind text content --> | ||
| 36 | + <div relative layout horizontal center-center> | ||
| 37 | + <template is="dom-if" if="{{_computeButtonIsSignIn(signedIn, needAdditionalAuth)}}"> | ||
| 38 | + <div class="button-content signIn" tabindex="0" | ||
| 39 | + on-click="signIn" on-keydown="_signInKeyPress"> | ||
| 40 | + <span class="icon"><iron-icon icon="[[_brandIcon]]"></iron-icon></span> | ||
| 41 | + <span class="buttonText">{{_labelSignin}}</span> | ||
| 42 | + </div> | ||
| 43 | + </template> | ||
| 44 | + <template is="dom-if" if="{{_computeButtonIsSignOut(signedIn, needAdditionalAuth) }}"> | ||
| 45 | + <div class="button-content signOut" tabindex="0" | ||
| 46 | + on-click="signOut" on-keydown="_signOutKeyPress"> | ||
| 47 | + <span class="icon"><iron-icon icon="[[_brandIcon]]"></iron-icon></span> | ||
| 48 | + <span class="buttonText">{{labelSignout}}</span> | ||
| 49 | + </div> | ||
| 50 | + </template> | ||
| 51 | + <template is="dom-if" if="{{_computeButtonIsSignOutAddl(signedIn, needAdditionalAuth) }}"> | ||
| 52 | + <div class="button-content signIn" tabindex="0" | ||
| 53 | + on-click="signIn" on-keydown="_signInKeyPress"> | ||
| 54 | + <span class="icon"><iron-icon icon="[[_brandIcon]]"></iron-icon></span> | ||
| 55 | + <span class="buttonText">{{labelAdditional}}</span> | ||
| 56 | + </div> | ||
| 57 | + </template> | ||
| 58 | + </div> | ||
| 59 | + | ||
| 60 | + </div> | ||
| 61 | + </template> | ||
| 62 | +</dom-module> | ||
| 63 | +<script> | ||
| 64 | + (function() { | ||
| 65 | + | ||
| 66 | + /** | ||
| 67 | + * Enum brand values. | ||
| 68 | + * @readonly | ||
| 69 | + * @enum {string} | ||
| 70 | + */ | ||
| 71 | + var BrandValue = { | ||
| 72 | + GOOGLE: 'google', | ||
| 73 | + PLUS: 'google-plus' | ||
| 74 | + }; | ||
| 75 | + | ||
| 76 | + /** | ||
| 77 | + * Enum height values. | ||
| 78 | + * @readonly | ||
| 79 | + * @enum {string} | ||
| 80 | + */ | ||
| 81 | + var HeightValue = { | ||
| 82 | + SHORT: 'short', | ||
| 83 | + STANDARD: 'standard', | ||
| 84 | + TALL: 'tall' | ||
| 85 | + }; | ||
| 86 | + | ||
| 87 | + /** | ||
| 88 | + * Enum button label default values. | ||
| 89 | + * @readonly | ||
| 90 | + * @enum {string} | ||
| 91 | + */ | ||
| 92 | + var LabelValue = { | ||
| 93 | + STANDARD: 'Sign in', | ||
| 94 | + WIDE: 'Sign in with Google', | ||
| 95 | + WIDE_PLUS: 'Sign in with Google+' | ||
| 96 | + }; | ||
| 97 | + | ||
| 98 | + /** | ||
| 99 | + * Enum theme values. | ||
| 100 | + * @readonly | ||
| 101 | + * @enum {string} | ||
| 102 | + */ | ||
| 103 | + var ThemeValue = { | ||
| 104 | + LIGHT: 'light', | ||
| 105 | + DARK: 'dark' | ||
| 106 | + }; | ||
| 107 | + | ||
| 108 | + /** | ||
| 109 | + * Enum width values. | ||
| 110 | + * @readonly | ||
| 111 | + * @enum {string} | ||
| 112 | + */ | ||
| 113 | + var WidthValue = { | ||
| 114 | + ICON_ONLY: 'iconOnly', | ||
| 115 | + STANDARD: 'standard', | ||
| 116 | + WIDE: 'wide' | ||
| 117 | + }; | ||
| 118 | + | ||
| 119 | +/** | ||
| 120 | +<google-signin> is used to authenticate with Google, allowing you to interact | ||
| 121 | +with other Google APIs such as Drive and Google+. | ||
| 122 | + | ||
| 123 | +<img style="max-width:100%;" src="https://cloud.githubusercontent.com/assets/107076/6791176/5c868822-d16a-11e4-918c-ec9b84a2db45.png"/> | ||
| 124 | + | ||
| 125 | +If you do not need to show the button, use companion `<google-signin-aware>` element to declare scopes, check authentication state. | ||
| 126 | + | ||
| 127 | +#### Examples | ||
| 128 | + | ||
| 129 | + <google-signin client-id="..." scopes="https://www.googleapis.com/auth/drive"></google-signin> | ||
| 130 | + | ||
| 131 | + <google-signin label-signin="Sign-in" client-id="..." scopes="https://www.googleapis.com/auth/drive"></google-signin> | ||
| 132 | + | ||
| 133 | + <google-signin theme="dark" width="iconOnly" client-id="..." scopes="https://www.googleapis.com/auth/drive"></google-signin> | ||
| 134 | + | ||
| 135 | + | ||
| 136 | +#### Notes | ||
| 137 | + | ||
| 138 | +The attribute `clientId` is provided in your Google Developers Console | ||
| 139 | +(https://console.developers.google.com). | ||
| 140 | + | ||
| 141 | +The `scopes` attribute allows you to specify which scope permissions are required | ||
| 142 | +(e.g do you want to allow interaction with the Google Drive API). Many APIs also | ||
| 143 | +need to be enabled in the Google Developers Console before you can use them. | ||
| 144 | + | ||
| 145 | +The `requestVisibleActions` attribute is necessary if you want to write app | ||
| 146 | +activities (https://developers.google.com/+/web/app-activities/) on behalf of | ||
| 147 | +the user. Please note that this attribute is only valid in combination with the | ||
| 148 | +plus.login scope (https://www.googleapis.com/auth/plus.login). | ||
| 149 | + | ||
| 150 | +The `offline` attribute allows you to get an auth code which your server can | ||
| 151 | +redeem for an offline access token | ||
| 152 | +(https://developers.google.com/identity/sign-in/web/server-side-flow). | ||
| 153 | +You can also set `offline-always-prompt` instead of `offline` to ensure that your app | ||
| 154 | +will re-prompt the user for offline access and generate a working `refresh_token` | ||
| 155 | +even if they have already granted offline access to your app in the past. | ||
| 156 | + | ||
| 157 | +Use label properties to customize prompts. | ||
| 158 | + | ||
| 159 | +The button can be styled in using the `height`, `width`, and `theme` attributes. | ||
| 160 | +These attributes help you follow the Google+ Sign-In button branding guidelines | ||
| 161 | +(https://developers.google.com/+/branding-guidelines). | ||
| 162 | + | ||
| 163 | +The `google-signin-success` event is triggered when a user successfully authenticates | ||
| 164 | +and `google-signed-out` is triggered when user signs out. | ||
| 165 | +You can also use `isAuthorized` attribute to observe user's authentication state. | ||
| 166 | + | ||
| 167 | +Additional events, such as `google-signout-attempted` are | ||
| 168 | +triggered when the user attempts to sign-out and successfully signs out. | ||
| 169 | + | ||
| 170 | +When requesting offline access, the `google-signin-offline-success` event is | ||
| 171 | +triggered when the user successfully consents with offline support. | ||
| 172 | + | ||
| 173 | +The `google-signin-necessary` event is fired when scopes requested via | ||
| 174 | +google-signin-aware elements require additional user permissions. | ||
| 175 | + | ||
| 176 | +#### Testing | ||
| 177 | + | ||
| 178 | +By default, the demo accompanying this element is setup to work on localhost with | ||
| 179 | +port 8080. That said, you *should* update the `clientId` to your own one for | ||
| 180 | +any apps you're building. See the Google Developers Console | ||
| 181 | +(https://console.developers.google.com) for more info. | ||
| 182 | + | ||
| 183 | +@demo | ||
| 184 | +*/ | ||
| 185 | + | ||
| 186 | + Polymer({ | ||
| 187 | + | ||
| 188 | + is: 'google-signin', | ||
| 189 | + | ||
| 190 | + /** | ||
| 191 | + * Fired when user is signed in. | ||
| 192 | + * You can use auth2 api to retrieve current user: `gapi.auth2.getAuthInstance().currentUser.get();` | ||
| 193 | + * @event google-signin-success | ||
| 194 | + */ | ||
| 195 | + | ||
| 196 | + /** | ||
| 197 | + * Fired when the user is signed-out. | ||
| 198 | + * @event google-signed-out | ||
| 199 | + */ | ||
| 200 | + | ||
| 201 | + /** | ||
| 202 | + * Fired if user requires additional authorization | ||
| 203 | + * @event google-signin-necessary | ||
| 204 | + */ | ||
| 205 | + | ||
| 206 | + /** | ||
| 207 | + * Fired when signed in, and scope has been authorized | ||
| 208 | + * @param {Object} result Authorization result. | ||
| 209 | + * @event google-signin-aware-success | ||
| 210 | + */ | ||
| 211 | + | ||
| 212 | + /** | ||
| 213 | + * Fired when an offline authorization is successful. | ||
| 214 | + * @event google-signin-offline-success | ||
| 215 | + * @param {Object} detail | ||
| 216 | + * @param {string} detail.code The one-time authorization code from Google. | ||
| 217 | + * Your application can exchange this for an `access_token` and `refresh_token` | ||
| 218 | + */ | ||
| 219 | + | ||
| 220 | + /** | ||
| 221 | + * Fired when this scope is not authorized | ||
| 222 | + * @event google-signin-aware-signed-out | ||
| 223 | + */ | ||
| 224 | + properties: { | ||
| 225 | + /** | ||
| 226 | + * App package name for android over-the-air installs. | ||
| 227 | + * See the relevant [docs](https://developers.google.com/+/web/signin/android-app-installs) | ||
| 228 | + */ | ||
| 229 | + appPackageName: { | ||
| 230 | + type: String, | ||
| 231 | + value: '' | ||
| 232 | + }, | ||
| 233 | + | ||
| 234 | + /** | ||
| 235 | + * The brand being used for logo and styling. | ||
| 236 | + * | ||
| 237 | + * @default 'google' | ||
| 238 | + */ | ||
| 239 | + brand: { | ||
| 240 | + type: String, | ||
| 241 | + value: '' | ||
| 242 | + }, | ||
| 243 | + | ||
| 244 | + /** @private */ | ||
| 245 | + _brand: { | ||
| 246 | + type: String, | ||
| 247 | + computed: '_computeBrand(brand, hasPlusScopes)' | ||
| 248 | + }, | ||
| 249 | + | ||
| 250 | + /** | ||
| 251 | + * a Google Developers clientId reference | ||
| 252 | + */ | ||
| 253 | + clientId: { | ||
| 254 | + type: String, | ||
| 255 | + value: '' | ||
| 256 | + }, | ||
| 257 | + | ||
| 258 | + /** | ||
| 259 | + * The cookie policy defines what URIs have access to the session cookie | ||
| 260 | + * remembering the user's sign-in state. | ||
| 261 | + * See the relevant [docs](https://developers.google.com/+/web/signin/reference#determining_a_value_for_cookie_policy) for more information. | ||
| 262 | + * | ||
| 263 | + * @default 'single_host_origin' | ||
| 264 | + */ | ||
| 265 | + cookiePolicy: { | ||
| 266 | + type: String, | ||
| 267 | + value: '' | ||
| 268 | + }, | ||
| 269 | + | ||
| 270 | + /** | ||
| 271 | + * The height to use for the button. | ||
| 272 | + * | ||
| 273 | + * Available options: short, standard, tall. | ||
| 274 | + * | ||
| 275 | + * @type {HeightValue} | ||
| 276 | + */ | ||
| 277 | + height: { | ||
| 278 | + type: String, | ||
| 279 | + value: 'standard' | ||
| 280 | + }, | ||
| 281 | + | ||
| 282 | + /** | ||
| 283 | + * By default the ripple expands to fill the button. Set this to true to | ||
| 284 | + * constrain the ripple to a circle within the button. | ||
| 285 | + */ | ||
| 286 | + fill: { | ||
| 287 | + type: Boolean, | ||
| 288 | + value: true | ||
| 289 | + }, | ||
| 290 | + | ||
| 291 | + /** | ||
| 292 | + * An optional label for the button for additional permissions. | ||
| 293 | + */ | ||
| 294 | + labelAdditional: { | ||
| 295 | + type: String, | ||
| 296 | + value: 'Additional permissions required' | ||
| 297 | + }, | ||
| 298 | + | ||
| 299 | + /** | ||
| 300 | + * An optional label for the sign-in button. | ||
| 301 | + */ | ||
| 302 | + labelSignin: { | ||
| 303 | + type: String, | ||
| 304 | + value: '' | ||
| 305 | + }, | ||
| 306 | + | ||
| 307 | + _labelSignin: { | ||
| 308 | + type: String, | ||
| 309 | + computed: '_computeSigninLabel(labelSignin, width, _brand)' | ||
| 310 | + }, | ||
| 311 | + | ||
| 312 | + /** | ||
| 313 | + * An optional label for the sign-out button. | ||
| 314 | + */ | ||
| 315 | + labelSignout: { | ||
| 316 | + type: String, | ||
| 317 | + value: 'Sign out' | ||
| 318 | + }, | ||
| 319 | + | ||
| 320 | + /** | ||
| 321 | + * If true, the button will be styled with a shadow. | ||
| 322 | + */ | ||
| 323 | + raised: { | ||
| 324 | + type: Boolean, | ||
| 325 | + value: false | ||
| 326 | + }, | ||
| 327 | + | ||
| 328 | + /** | ||
| 329 | + * The app activity types you want to write on behalf of the user | ||
| 330 | + * (e.g http://schemas.google.com/AddActivity) | ||
| 331 | + */ | ||
| 332 | + requestVisibleActions: { | ||
| 333 | + type: String, | ||
| 334 | + value: '' | ||
| 335 | + }, | ||
| 336 | + | ||
| 337 | + /** | ||
| 338 | + * The Google Apps domain to which users must belong to sign in. | ||
| 339 | + * See the relevant [docs](https://developers.google.com/identity/sign-in/web/reference) for more information. | ||
| 340 | + */ | ||
| 341 | + hostedDomain: { | ||
| 342 | + type: String, | ||
| 343 | + value: '' | ||
| 344 | + }, | ||
| 345 | + | ||
| 346 | + /** | ||
| 347 | + * Allows for offline `access_token` retrieval during the signin process. | ||
| 348 | + */ | ||
| 349 | + offline: { | ||
| 350 | + type: Boolean, | ||
| 351 | + value: false | ||
| 352 | + }, | ||
| 353 | + | ||
| 354 | + /** | ||
| 355 | + * Forces a re-prompt, even if the user has already granted offline | ||
| 356 | + * access to your application in the past. You only need one of | ||
| 357 | + * `offline` and `offlineAlwaysPrompt`. | ||
| 358 | + */ | ||
| 359 | + offlineAlwaysPrompt: { | ||
| 360 | + type: Boolean, | ||
| 361 | + value: false | ||
| 362 | + }, | ||
| 363 | + | ||
| 364 | + /** | ||
| 365 | + * The scopes to provide access to (e.g https://www.googleapis.com/auth/drive) | ||
| 366 | + * and should be space-delimited. | ||
| 367 | + */ | ||
| 368 | + scopes: { | ||
| 369 | + type: String, | ||
| 370 | + value: '' | ||
| 371 | + }, | ||
| 372 | + | ||
| 373 | + /** | ||
| 374 | + * The theme to use for the button. | ||
| 375 | + * | ||
| 376 | + * Available options: light, dark. | ||
| 377 | + * | ||
| 378 | + * @attribute theme | ||
| 379 | + * @type {ThemeValue} | ||
| 380 | + * @default 'dark' | ||
| 381 | + */ | ||
| 382 | + theme: { | ||
| 383 | + type: String, | ||
| 384 | + value: 'dark' | ||
| 385 | + }, | ||
| 386 | + | ||
| 387 | + /** | ||
| 388 | + * The width to use for the button. | ||
| 389 | + * | ||
| 390 | + * Available options: iconOnly, standard, wide. | ||
| 391 | + * | ||
| 392 | + * @type {WidthValue} | ||
| 393 | + */ | ||
| 394 | + width: { | ||
| 395 | + type: String, | ||
| 396 | + value: 'standard' | ||
| 397 | + }, | ||
| 398 | + | ||
| 399 | + _brandIcon: { | ||
| 400 | + type: String, | ||
| 401 | + computed: '_computeIcon(_brand)' | ||
| 402 | + }, | ||
| 403 | + | ||
| 404 | + /** | ||
| 405 | + * True if *any* element has google+ scopes | ||
| 406 | + */ | ||
| 407 | + hasPlusScopes: { | ||
| 408 | + type: Boolean, | ||
| 409 | + notify: true, | ||
| 410 | + value: false | ||
| 411 | + }, | ||
| 412 | + | ||
| 413 | + /** | ||
| 414 | + * True if additional authorization required globally | ||
| 415 | + */ | ||
| 416 | + needAdditionalAuth: { | ||
| 417 | + type: Boolean, | ||
| 418 | + value: false | ||
| 419 | + }, | ||
| 420 | + | ||
| 421 | + /** | ||
| 422 | + * Is user signed in? | ||
| 423 | + */ | ||
| 424 | + signedIn: { | ||
| 425 | + type: Boolean, | ||
| 426 | + notify: true, | ||
| 427 | + value: false, | ||
| 428 | + observer: '_observeSignedIn' | ||
| 429 | + }, | ||
| 430 | + | ||
| 431 | + /** | ||
| 432 | + * True if authorizations for *this* element have been granted | ||
| 433 | + */ | ||
| 434 | + isAuthorized: { | ||
| 435 | + type: Boolean, | ||
| 436 | + notify: true, | ||
| 437 | + value: false | ||
| 438 | + } | ||
| 439 | + | ||
| 440 | + }, | ||
| 441 | + | ||
| 442 | + _computeButtonClass: function(height, width, theme, signedIn, brand, needAdditionalAuth) { | ||
| 443 | + return "height-" + height + " width-" + width + " theme-" + theme + " signedIn-" + signedIn + " brand-" + brand + " additionalAuth-" + needAdditionalAuth; | ||
| 444 | + }, | ||
| 445 | + | ||
| 446 | + _computeIcon: function(brand) { | ||
| 447 | + return "google:" + brand; | ||
| 448 | + }, | ||
| 449 | + | ||
| 450 | + /* Button state computed */ | ||
| 451 | + _computeButtonIsSignIn: function(signedIn, additionalAuth) { | ||
| 452 | + return !signedIn; | ||
| 453 | + }, | ||
| 454 | + | ||
| 455 | + _computeButtonIsSignOut: function(signedIn, additionalAuth) { | ||
| 456 | + return signedIn && !additionalAuth; | ||
| 457 | + }, | ||
| 458 | + | ||
| 459 | + _computeButtonIsSignOutAddl: function(signedIn, additionalAuth) { | ||
| 460 | + return signedIn && additionalAuth; | ||
| 461 | + }, | ||
| 462 | + | ||
| 463 | + _computeBrand: function(attrBrand, hasPlusScopes) { | ||
| 464 | + var newBrand; | ||
| 465 | + if (attrBrand) { | ||
| 466 | + newBrand = attrBrand; | ||
| 467 | + } else if (hasPlusScopes) { | ||
| 468 | + newBrand = BrandValue.PLUS; | ||
| 469 | + } else { | ||
| 470 | + newBrand = BrandValue.GOOGLE; | ||
| 471 | + }; | ||
| 472 | + return newBrand; | ||
| 473 | + }, | ||
| 474 | + | ||
| 475 | + _observeSignedIn: function(newVal, oldVal) { | ||
| 476 | + if (newVal) { | ||
| 477 | + if (this.needAdditionalAuth) | ||
| 478 | + this.fire('google-signin-necessary'); | ||
| 479 | + this.fire('google-signin-success'); | ||
| 480 | + } | ||
| 481 | + else | ||
| 482 | + this.fire('google-signed-out'); | ||
| 483 | + }, | ||
| 484 | + | ||
| 485 | + /** | ||
| 486 | + * Determines the proper label based on the attributes. | ||
| 487 | + */ | ||
| 488 | + _computeSigninLabel: function(labelSignin, width, _brand) { | ||
| 489 | + if (labelSignin) { | ||
| 490 | + return labelSignin; | ||
| 491 | + } else { | ||
| 492 | + switch(width) { | ||
| 493 | + | ||
| 494 | + case WidthValue.WIDE: | ||
| 495 | + return (_brand == BrandValue.PLUS) ? | ||
| 496 | + LabelValue.WIDE_PLUS : LabelValue.WIDE; | ||
| 497 | + | ||
| 498 | + case WidthValue.STANDARD: | ||
| 499 | + return LabelValue.STANDARD; | ||
| 500 | + | ||
| 501 | + case WidthValue.ICON_ONLY: | ||
| 502 | + return ''; | ||
| 503 | + | ||
| 504 | + default: | ||
| 505 | + console.warn("bad width value: ", width); | ||
| 506 | + return LabelValue.STANDARD; | ||
| 507 | + } | ||
| 508 | + } | ||
| 509 | + }, | ||
| 510 | + | ||
| 511 | + /** Sign in user. Opens the authorization dialog for signing in. | ||
| 512 | + * The dialog will be blocked by a popup blocker unless called inside click handler. | ||
| 513 | + */ | ||
| 514 | + signIn: function () { | ||
| 515 | + this.$.aware.signIn(); | ||
| 516 | + }, | ||
| 517 | + | ||
| 518 | + _signInKeyPress: function (e) { | ||
| 519 | + if (e.which == 13 || e.keyCode == 13 || e.which == 32 || e.keyCode == 32) { | ||
| 520 | + e.preventDefault(); | ||
| 521 | + this.signIn(); | ||
| 522 | + } | ||
| 523 | + }, | ||
| 524 | + | ||
| 525 | + /** Sign out the user */ | ||
| 526 | + signOut: function () { | ||
| 527 | + this.fire('google-signout-attempted'); | ||
| 528 | + this.$.aware.signOut(); | ||
| 529 | + }, | ||
| 530 | + | ||
| 531 | + _signOutKeyPress: function (e) { | ||
| 532 | + if (e.which == 13 || e.keyCode == 13 || e.which == 32 || e.keyCode == 32) { | ||
| 533 | + e.preventDefault(); | ||
| 534 | + this.signOut(); | ||
| 535 | + } | ||
| 536 | + } | ||
| 537 | + }); | ||
| 538 | + }()); | ||
| 539 | +</script> |
bower_components/google-signin/index.html
0 → 100644
| 1 | +<!doctype html> | ||
| 2 | +<!-- Copyright (c) 2014 Google Inc. All rights reserved. --> | ||
| 3 | +<html> | ||
| 4 | +<head> | ||
| 5 | + | ||
| 6 | + <script src="../webcomponentsjs/webcomponents-lite.js"></script> | ||
| 7 | + <link rel="import" href="../iron-component-page/iron-component-page.html"> | ||
| 8 | + | ||
| 9 | +</head> | ||
| 10 | +<body> | ||
| 11 | + | ||
| 12 | + <iron-component-page></iron-component-page> | ||
| 13 | + | ||
| 14 | +</body> | ||
| 15 | +</html> |
datalets/test-google-components/test.html
0 → 100644
| 1 | +<!DOCTYPE html> | ||
| 2 | +<html lang="en"> | ||
| 3 | +<head> | ||
| 4 | + <meta charset="UTF-8"> | ||
| 5 | + <title>Google spreadsheet and document test page</title> | ||
| 6 | + | ||
| 7 | + <script type="text/javascript" src="../../bower_components/webcomponentsjs/webcomponents-lite.min.js"></script> | ||
| 8 | + <script type="text/javascript" src="../shared_js/jquery-1.11.2.min.js"></script> | ||
| 9 | + | ||
| 10 | + <link rel="import" href="../../bower_components/google-sheets/google-sheets.html"> | ||
| 11 | + <link rel="import" href="../../bower_components/iron-flex-layout/iron-flex-layout.html"> | ||
| 12 | + <link rel="import" href="../../bower_components/iron-icons/iron-icons.html"> | ||
| 13 | + <link rel="import" href="../../bower_components/paper-fab/paper-fab.html"> | ||
| 14 | + <link rel="import" href="../linechart-datalet/linechart-datalet.html"> | ||
| 15 | + | ||
| 16 | + <style> | ||
| 17 | + #sheet{ | ||
| 18 | + height: 400px; | ||
| 19 | + width: 40%; | ||
| 20 | + margin: 40px; | ||
| 21 | + } | ||
| 22 | + | ||
| 23 | + #showVisualization{ | ||
| 24 | + background-color: #2196F3; | ||
| 25 | + color: #ffffff; | ||
| 26 | + margin-left: 50px; | ||
| 27 | + } | ||
| 28 | + | ||
| 29 | + | ||
| 30 | + #datalet_placeholder{ | ||
| 31 | + height: 400px; | ||
| 32 | + width: 40%; | ||
| 33 | + margin: 40px;/ | ||
| 34 | + } | ||
| 35 | + | ||
| 36 | + #doc{ | ||
| 37 | + height: 400px; | ||
| 38 | + width: 100%; | ||
| 39 | + } | ||
| 40 | + </style> | ||
| 41 | + | ||
| 42 | + <script> | ||
| 43 | + | ||
| 44 | + getData = function() | ||
| 45 | + { | ||
| 46 | + var sheet = document.querySelector('google-sheets'); | ||
| 47 | + | ||
| 48 | + sheet.addEventListener('google-sheet-data', function(e) | ||
| 49 | + { | ||
| 50 | + var data = []; | ||
| 51 | + //get the keys | ||
| 52 | + for(var key in this.rows[0]) { | ||
| 53 | + if (key.match(/gsx\$*/)) { | ||
| 54 | + data.push({name: key.replace("gsx$", ""), data: []}); | ||
| 55 | + } | ||
| 56 | + } | ||
| 57 | + //fill the structure with values | ||
| 58 | + for(var row in this.rows){ | ||
| 59 | + var i = 0; | ||
| 60 | + for(var key in this.rows[row]){ | ||
| 61 | + if(key.match(/gsx\$*/)){ | ||
| 62 | + data[i++].data.push(this.rows[row][key].$t.replace("'","")); | ||
| 63 | + } | ||
| 64 | + } | ||
| 65 | + | ||
| 66 | + } | ||
| 67 | + | ||
| 68 | + $("#datalet_placeholder").html('<linechart-datalet ' + | ||
| 69 | + 'data-url="https://docs.google.com/spreadsheets/d/1EYJ88pOsbc0GkUTTdPhuReUjHewk1fhis9VZBXes_Ao" ' + | ||
| 70 | + //'fields=\'[]\'' + | ||
| 71 | + 'title="Google spreadsheet realtime data visualization"' + | ||
| 72 | + 'data=\'' + JSON.stringify(data) + '\'>' + | ||
| 73 | + '</linechart-datalet>'); | ||
| 74 | + | ||
| 75 | + $("#spreadsheet_url").html('<a href="' + this.openInGoogleDocsUrl + '" target="_blank">' + this.openInGoogleDocsUrl + '</a>'); | ||
| 76 | + | ||
| 77 | + }); | ||
| 78 | + | ||
| 79 | + sheet.addEventListener('error', function(e) { | ||
| 80 | + // e.detail.response | ||
| 81 | + }); | ||
| 82 | + | ||
| 83 | + } | ||
| 84 | + | ||
| 85 | + refresh = function(){ | ||
| 86 | + $("#sheet-component").html('<google-sheets key="1EYJ88pOsbc0GkUTTdPhuReUjHewk1fhis9VZBXes_Ao" published></google-sheets>'); | ||
| 87 | + getData(); | ||
| 88 | + } | ||
| 89 | + | ||
| 90 | + $( document ).ready(function() { | ||
| 91 | + getData(); | ||
| 92 | + }); | ||
| 93 | + | ||
| 94 | + </script> | ||
| 95 | + | ||
| 96 | +</head> | ||
| 97 | +<body is="dom-bind"> | ||
| 98 | + <div class="layout vertical"> | ||
| 99 | + <div class="layout vertical"> | ||
| 100 | + <div class="layout horizontal" style="width: 100%;"> | ||
| 101 | + <h1>Google Spreadsheet realtime visualization with Datalet</h1> | ||
| 102 | + <paper-fab id="showVisualization" icon="refresh" onClick="refresh()"></paper-fab> | ||
| 103 | + </div> | ||
| 104 | + <div class="layout horizontal" style="width: 100%;"> | ||
| 105 | + <h3 id="spreadsheet_url"></h3> | ||
| 106 | + </div> | ||
| 107 | + <div class="layout horizontal"> | ||
| 108 | + <iframe id="sheet" src="https://docs.google.com/spreadsheets/d/1EYJ88pOsbc0GkUTTdPhuReUjHewk1fhis9VZBXes_Ao/edit?usp=sharing?embedded=true"></iframe> | ||
| 109 | + <div id="datalet_placeholder"></div> | ||
| 110 | + </div> | ||
| 111 | + </div> | ||
| 112 | + | ||
| 113 | + <div class="layout vertical"> | ||
| 114 | + <h1 style="width: 100%;margin-top: 100px;">Google Docs realtime visualization with Datalet</h1> | ||
| 115 | + <iframe id="doc" src="https://docs.google.com/document/d/1IIfmqTGBpTkEMHPnuxJeoOJI3u-8wQu9dkf49X2T6SE/edit?usp=sharing?embedded=true"></iframe> | ||
| 116 | + </div> | ||
| 117 | + </div> | ||
| 118 | + | ||
| 119 | + <div id="sheet-component"> | ||
| 120 | + <google-sheets key="1EYJ88pOsbc0GkUTTdPhuReUjHewk1fhis9VZBXes_Ao" published></google-sheets> | ||
| 121 | + </div> | ||
| 122 | + | ||
| 123 | +</body> | ||
| 124 | +</html> | ||
| 0 | \ No newline at end of file | 125 | \ No newline at end of file |