Commit 436d3f5aaaea9ccb1d05f67eb7351bfae200586a
Merge branch 'master' of http://service.routetopa.eu:7480/WebCompDev/COMPONENTS
Showing
25 changed files
with
2842 additions
and
3 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 | 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 | 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/graph-datalet/graph-datalet.html
... | ... | @@ -246,12 +246,12 @@ Example: |
246 | 246 | return "fill:" + t.color + "; stroke:white" |
247 | 247 | }); |
248 | 248 | |
249 | + d3.selectAll(".drag").call(force.drag), this.svg.selectAll("g.node").call(this.text); | |
250 | + | |
249 | 251 | d3.selectAll('circle').attr("style", function(t){ |
250 | - return "fill :" + (t.image ? 'url(#image_' + t.id +')' : t.color) | |
252 | + return (t != undefined) ? "fill :" + (t.image ? 'url(#image_' + t.id +')' : t.color) : ""; | |
251 | 253 | }); |
252 | 254 | |
253 | - d3.selectAll(".drag").call(force.drag), this.svg.selectAll("g.node").call(this.text); | |
254 | - | |
255 | 255 | }, |
256 | 256 | |
257 | 257 | zoom: function() { | ... | ... |
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 | 125 | \ No newline at end of file | ... | ... |