Commit 525c13d8940ad0cb94ee66117553fe47ef6c4191

Authored by Luigi Serra
1 parent ac237fdd

google component tests

bower_components/google-sheets/.bower.json 0 → 100644
  1 +{
  2 + "name": "google-sheets",
  3 + "version": "1.0.5",
  4 + "homepage": "https://googlewebcomponents.github.io/google-sheets",
  5 + "description": "Web components to interact with Google Sheets",
  6 + "main": "google-sheets.html",
  7 + "authors": [
  8 + "Eric Bidelman <ebidel@gmail.com>"
  9 + ],
  10 + "license": "Apache2",
  11 + "ignore": [
  12 + "/.*",
  13 + "/test/"
  14 + ],
  15 + "keywords": [
  16 + "web-component",
  17 + "web-components",
  18 + "polymer",
  19 + "spreadsheets",
  20 + "google"
  21 + ],
  22 + "dependencies": {
  23 + "polymer": "Polymer/polymer#^1.1.2",
  24 + "google-apis": "GoogleWebComponents/google-apis#^1.1.0",
  25 + "google-signin": "GoogleWebComponents/google-signin#^1.0.3",
  26 + "iron-ajax": "PolymerElements/iron-ajax#^1.0.0"
  27 + },
  28 + "devDependencies": {
  29 + "iron-component-page": "PolymerElements/iron-component-page#^1.0.0",
  30 + "google-map": "GoogleWebComponents/google-map#^1.1.0",
  31 + "iron-flex-layout": "PolymerElements/iron-flex-layout#^1.0.0",
  32 + "web-component-tester": "*"
  33 + },
  34 + "_release": "1.0.5",
  35 + "_resolution": {
  36 + "type": "version",
  37 + "tag": "1.0.5",
  38 + "commit": "1e87081a4abfdd31cf7cbf4ac970c7f9873c447d"
  39 + },
  40 + "_source": "git://github.com/GoogleWebComponents/google-sheets.git",
  41 + "_target": "~1.0.5",
  42 + "_originalSource": "GoogleWebComponents/google-sheets",
  43 + "_direct": true
  44 +}
0 \ No newline at end of file 45 \ No newline at end of file
bower_components/google-sheets/LICENSE 0 → 100644
  1 +Copyright 2014 Google Inc
  2 +
  3 +Licensed under the Apache License, Version 2.0 (the "License");
  4 +you may not use this file except in compliance with the License.
  5 +You may obtain a copy of the License at
  6 +
  7 + https://www.apache.org/licenses/LICENSE-2.0
  8 +
  9 +Unless required by applicable law or agreed to in writing, software
  10 +distributed under the License is distributed on an "AS IS" BASIS,
  11 +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12 +See the License for the specific language governing permissions and
  13 +limitations under the License.
bower_components/google-sheets/README.md 0 → 100755
  1 +google-sheets
  2 +================
  3 +
  4 +See the [component page](https://googlewebcomponents.github.io/google-sheets) for more information.
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>&lt;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 &rarr;">
  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>&lt;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 &rarr;">
  68 + "<span>{{tab.title}}</span>" tab
  69 + </a>
  70 + </h3>
  71 + <h5>updated: <span>{{tab.updated}}</span>, by: <template is="dom-repeat" items="{{rows.authors}}">{{item.name}}</template></h5>
  72 + </heading>
  73 + <ul>
  74 + <template is="dom-repeat" items="[[rows]]">
  75 + <li>Name: <span>{{item.gsx$name.$t}}</span> ( lat: <span>{{item.gsx$lat.$t}}</span>, lng: <span>{{item.gsx$lng.$t}}</span> )</li>
  76 + </template>
  77 + </ul>
  78 +
  79 + </section>
  80 +
  81 + <section style="padding: 0;">
  82 +
  83 + <google-map disable-default-ui fit-to-markers>
  84 + <template is="dom-repeat" items="[[rows]]">
  85 + <google-map-marker latitude="{{item.gsx$lat.$t}}" longitude="{{item.gsx$lng.$t}}"></google-map-marker>
  86 + </template>
  87 + </google-map>
  88 +
  89 + <button on-click="useTab" data-tabid="1">View tab 1 data</button>
  90 + <button on-click="useTab" data-tabid="2">View tab 2 data</button>
  91 +
  92 + </section>
  93 +
  94 +</template>
  95 +</main>
  96 +
  97 +<script>
  98 +var template = document.querySelector('#container');
  99 +
  100 +template.useTab = function(e, detail, sender) {
  101 + document.querySelector('#sheet').tabId = Number(e.currentTarget.dataset.tabid);
  102 +};
  103 +</script>
  104 +</body>
  105 +</html>
bower_components/google-sheets/google-sheets.html 0 → 100755
  1 +<!-- Copyright (c) 2015 Google Inc. All rights reserved. -->
  2 +
  3 +<link rel="import" href="../polymer/polymer.html">
  4 +<link rel="import" href="../iron-ajax/iron-ajax.html">
  5 +<link rel="import" href="../google-signin/google-signin-aware.html">
  6 +
  7 +<!--
  8 +Element for interacting with Google Sheets.
  9 +
  10 +`<google-sheets>` pulls cell data from the Google Sheet specified by `key`.
  11 +A spreadsheet's key can be found in the URL when viewing it in google docs (e.g. `docs.google.com/spreadsheet/ccc?key=<KEY>#gid=12345`).
  12 +
  13 +Optionally, pass the `tab-id` attribute to specify a particular worksheet tab in the spreadsheet. For example, the first tab would be `tab-id="1"`. If `tab` is updated at a later time, the underlying data is also updated. **API calls are cached** as to not make extraneous calls.
  14 +
  15 +See [developers.google.com/google-apps/spreadsheets](https://developers.google.com/google-apps/spreadsheets) for full Spreadsheets API documentation.
  16 +
  17 +#### Example
  18 +
  19 + <google-sheets key="..." tab-id="1" client-id="..."></google-sheets>
  20 +
  21 + <script>
  22 + var sheet = document.querySelector('google-sheets');
  23 +
  24 + sheet.addEventListener('google-sheet-data', function(e) {
  25 + // this.spreadsheets - list of the user's spreadsheets
  26 + // this.tab - information on the tab that was fetched
  27 + // this.rows - cell row information for the tab that was fetched
  28 + });
  29 +
  30 + sheet.addEventListener('error', function(e) {
  31 + // e.detail.response
  32 + });
  33 + </script>
  34 +
  35 +<b>Example</b> - `published` is a perf optimization and hints that the spreadsheet has been published (public):
  36 +
  37 + <google-sheets key="0Anye-JMjUkZZdDBkMVluMEhZMmFGeHpYdDJJV1FBRWc" published></google-sheets>
  38 +
  39 +<b>Example</b> - leaving off the `key` returns as list of the user's spreadsheets.
  40 +
  41 + <google-sheets client-id="..."></google-sheets>
  42 +
  43 +<b>Example</b> - show a list of Map markers, using data-binding features inside Polymer:
  44 +
  45 + <template is="dom-bind">
  46 + <google-sheets
  47 + key="0Anye-JMjUkZZdDBkMVluMEhZMmFGeHpYdDJJV1FBRWc" tab-id="1" rows="{{rows}}"
  48 + client-id="...">
  49 + </google-sheets>
  50 + <google-map>
  51 + <google-map-marker latitude="{{gsx$lat.$t}}" longitude="{{gsx$lng.$t}}">
  52 + </google-map>
  53 + </template>
  54 +
  55 +<b>Example</b> - list a user's private spreadsheets. Authenticate with google-signin button.
  56 +
  57 + <google-signin
  58 + client-id="1054047045356-j8pgqgls9vdef3rl09hapoicumbte0bo.apps.googleusercontent.com"
  59 + scopes="https://spreadsheets.google.com/feeds">
  60 + </google-signin>
  61 +
  62 + <template is="dom-bind">
  63 + <google-sheets client-id="1054047045356-j8pgqgls9vdef3rl09hapoicumbte0bo.apps.googleusercontent.com"
  64 + key="1QMGizivw3UJ3-R9BFK7sfrXE0RL87dygk2C0RcuKoDY" tab-id="1"
  65 + spreadsheets="{{spreadsheets}}"></google-sheets>
  66 + <template is="dom-repeat" items="[[spreadsheets]]">
  67 + <p>{{item.title.$t}}</p>
  68 + </template>
  69 + </template>
  70 +
  71 +@demo
  72 +-->
  73 +
  74 +<dom-module id="google-sheets">
  75 + <template>
  76 + <template is="dom-if" if="{{!published}}">
  77 + <google-signin-aware client-id="{{clientId}}"
  78 + scopes="https://spreadsheets.google.com/feeds"
  79 + on-google-signin-aware-success="_onSignInSuccess"
  80 + on-google-signin-aware-signed-out="_onSignInFail"></google-signin-aware>
  81 + </template>
  82 +
  83 + <iron-ajax id="publicajax" params='{"alt": "json"}' handle-as="json"
  84 + on-response="_onCellRows"></iron-ajax>
  85 + <iron-ajax id="listsheetsajax" params='{"alt": "json"}' handle-as="json"
  86 + on-response="_onSpreadsheetList"></iron-ajax>
  87 + <iron-ajax id="worksheetajax" params='{"alt": "json"}' handle-as="json"
  88 + on-response="_onWorksheet"></iron-ajax>
  89 + <iron-ajax id="cellrowsajax" params='{"alt": "json"}' handle-as="json"
  90 + on-response="_onCellRows"></iron-ajax>
  91 +
  92 + </template>
  93 +</dom-module>
  94 +
  95 +<script>
  96 +(function() {
  97 + var SCOPE_ = 'https://spreadsheets.google.com/feeds';
  98 +
  99 + // Minimal cache for worksheet row data. Shared across instances so subsequent
  100 + // accesses are fast and API calls only happen once.
  101 + var rowDataCache_ = {};
  102 +
  103 + function generateCacheKey_() {
  104 + return this._worksheetId + '_'+ this.tabId;
  105 + }
  106 +
  107 + function getLink_(rel, links) {
  108 + for (var i = 0, link; link = links[i]; ++i) {
  109 + if (link.rel === rel) {
  110 + return link;
  111 + }
  112 + }
  113 + return null;
  114 + }
  115 +
  116 + // Conversion of Worksheet Ids to GIDs and vice versa
  117 + // od4 > 2
  118 + function wid_to_gid_(wid) {
  119 + return parseInt(String(wid), 36) ^ 31578;
  120 + }
  121 + // 2 > 0d4
  122 + function gid_to_wid_(gid) {
  123 + // (gid xor 31578) encoded in base 36
  124 + return parseInt((gid ^ 31578)).toString(36);
  125 + }
  126 +
  127 + window.GoogleSheets = Polymer({
  128 +
  129 + is: 'google-sheets',
  130 +
  131 + /**
  132 + * Fired when the spreadsheet's cell information is available.
  133 + *
  134 + * @event google-sheet-data
  135 + * @param {Object} detail
  136 + * @param {Object} detail.data The data returned by the Spreadsheet API.
  137 + * @param {string} detail.type The type of data that was fetched.
  138 + * One of 'spreadsheets', 'tab', 'rows' * to correspond to the feed type.
  139 + */
  140 +
  141 + hostAttributes: {
  142 + hidden: true
  143 + },
  144 +
  145 + properties: {
  146 + /**
  147 + * A Google Developers client ID. Obtain from [console.developers.google.com](https://console.developers.google.com). Required for accessing a private spreadsheet. Optional if accessing a public spreadsheet.
  148 + */
  149 + clientId: {
  150 + type: String,
  151 + value: '',
  152 + observer: '_configUpdate'
  153 + },
  154 +
  155 + /**
  156 + * The key of the spreadsheet. This can be found in the URL when viewing
  157 + * the document is Google Docs (e.g. `docs.google.com/spreadsheet/ccc?key=<KEY>`).
  158 + *
  159 + * Leaving off this attribute still returns a list of the users spreadsheets in the `spreadsheets` property.
  160 + */
  161 + key: {
  162 + type: String,
  163 + value: '',
  164 + observer: '_keyChanged'
  165 + },
  166 +
  167 + /**
  168 + * Tab within a spreadsheet. For example, the first tab in a spreadsheet
  169 + * would be `tab-id="1"`.
  170 + */
  171 + tabId: {
  172 + type: Number,
  173 + value: 1,
  174 + observer: '_configUpdate'
  175 + },
  176 +
  177 + /**
  178 + * A hint that the spreadsheet is published publicly in Google Docs. Used as a performance optimization.
  179 + * Make sure the sheet is also publicly viewable by anyone in the Share settings.
  180 + *
  181 + * @attribute published
  182 + * @type boolean
  183 + * @default false
  184 + */
  185 + published: {
  186 + type: Boolean,
  187 + value: false,
  188 + observer: '_configUpdate'
  189 + },
  190 +
  191 + /**
  192 + * The fetched sheet corresponding to the `key` attribute.
  193 + */
  194 + sheet: {
  195 + type: Object,
  196 + value: function() { return {}; },
  197 + readOnly: true,
  198 + notify: true,
  199 + observer: '_sheetChanged'
  200 + },
  201 +
  202 + /**
  203 + * Meta data about the particular tab that was retrieved for the spreadsheet.
  204 + */
  205 + tab: {
  206 + type: Object,
  207 + value: function() { return {}; },
  208 + readOnly: true,
  209 + notify: true,
  210 + observer: '_tabChanged'
  211 + },
  212 +
  213 + /**
  214 + * If a spreadsheet `key` is specified, returns a list of cell row data.
  215 + */
  216 + rows: {
  217 + type: Array,
  218 + value: function() { return []; },
  219 + readOnly: true,
  220 + notify: true
  221 + },
  222 +
  223 + /**
  224 + * List of the user's spreadsheets. Shared across instances.
  225 + */
  226 + spreadsheets: {
  227 + type: Array,
  228 + readOnly: true,
  229 + notify: true,
  230 + value: function() { return []; }
  231 + },
  232 +
  233 + /**
  234 + * The URL to open this spreadsheet in Google Sheets.
  235 + */
  236 + openInGoogleDocsUrl: {
  237 + type: String,
  238 + computed: '_computeGoogleDocsUrl(key)',
  239 + notify: true
  240 + }
  241 + },
  242 +
  243 + _worksheetId: null,
  244 +
  245 + _computeGoogleDocsUrl: function(key) {
  246 + var url = 'https://docs.google.com/spreadsheet/';
  247 + if (key) {
  248 + url += 'ccc?key=' + key;
  249 + }
  250 + return url;
  251 + },
  252 +
  253 + _configUpdate: function(key, published, tabId, clientId) {
  254 + this._tabIdChanged();
  255 + },
  256 +
  257 + _keyChanged: function(newValue, oldValue) {
  258 + // TODO(ericbidelman): need to better handle updates to the key attribute.
  259 + // Below doesn't account for private feeds.
  260 + if (this.published) {
  261 + var url = SCOPE_ + '/list/' + this.key + '/' +
  262 + this.tabId + '/public/values';
  263 + this.$.publicajax.url = url;
  264 + this.$.publicajax.generateRequest();
  265 + }
  266 + },
  267 +
  268 + _tabIdChanged: function(newValue, oldValue) {
  269 + if (this._worksheetId) {
  270 + this._getCellRows();
  271 + } else if (this.published) {
  272 + this._keyChanged();
  273 + }
  274 + },
  275 +
  276 + _sheetChanged: function(newValue, oldValue) {
  277 + if (!this.sheet.title) {
  278 + return;
  279 + }
  280 +
  281 + // Make metadata easily accessible on sheet object.
  282 + var authors = this.sheet.author && this.sheet.author.map(function(a) {
  283 + return {email: a.email.$t, name: a.name.$t};
  284 + });
  285 +
  286 + this.set('sheet.title', this.sheet.title.$t);
  287 + this.set('sheet.updated', new Date(this.sheet.updated.$t));
  288 + this.set('sheet.authors', authors);
  289 +
  290 + this._worksheetId = this.sheet.id.$t.split('/').slice(-1)[0];
  291 + this._getWorksheet();
  292 + },
  293 +
  294 + _tabChanged: function(newValue, oldValue) {
  295 + if (!this.tab.title) {
  296 + return;
  297 + }
  298 +
  299 + var authors = this.tab.authors = this.tab.author && this.tab.author.map(function(a) {
  300 + return {email: a.email.$t, name: a.name.$t};
  301 + });
  302 +
  303 + this.set('tab.title', this.tab.title.$t);
  304 + this.set('tab.updated', new Date(this.tab.updated.$t));
  305 + this.set('tab.authors', authors);
  306 +
  307 + this.fire('google-sheet-data', {
  308 + type: 'tab',
  309 + data: this.tab
  310 + });
  311 + },
  312 +
  313 + _onSignInSuccess: function(e, detail) {
  314 + var oauthToken = gapi.auth2.getAuthInstance().currentUser.get().getAuthResponse();
  315 +
  316 + var headers = {
  317 + 'Authorization': 'Bearer ' + oauthToken.access_token
  318 + };
  319 +
  320 + this.$.listsheetsajax.headers = headers;
  321 + this.$.worksheetajax.headers = headers;
  322 + this.$.cellrowsajax.headers = headers;
  323 +
  324 + // TODO(ericbidelman): don't make this call if this.spreadsheets is
  325 + // already populated from another instance.
  326 + this._listSpreadsheets();
  327 + },
  328 +
  329 + _onSignInFail: function(e, detail) {
  330 + // TODO(ericbidelman): handle this in some way.
  331 + console.log(e, e.type);
  332 + },
  333 +
  334 + _listSpreadsheets: function() {
  335 + var url = SCOPE_ + '/spreadsheets/private/full';
  336 + this.$.listsheetsajax.url = url;
  337 + this.$.listsheetsajax.generateRequest();
  338 + },
  339 +
  340 + _onSpreadsheetList: function(e) {
  341 + e.stopPropagation();
  342 +
  343 + var feed = e.target.lastResponse.feed;
  344 +
  345 + this._setSpreadsheets(feed.entry);
  346 +
  347 + this.fire('google-sheet-data', {
  348 + type: 'spreadsheets',
  349 + data: this.spreadsheets
  350 + });
  351 +
  352 + // Fetch worksheet feed if key was given and worksheet exists.
  353 + if (this.key) {
  354 + for (var i = 0, entry; entry = feed.entry[i]; ++i) {
  355 + var altLink = getLink_('alternate', entry.link);
  356 + if (altLink && altLink.href.indexOf(this.key) != -1) {
  357 + this._setSheet(entry);
  358 + break;
  359 + }
  360 + }
  361 + }
  362 + },
  363 +
  364 + _getWorksheet: function() {
  365 + if (!this._worksheetId) {
  366 + throw new Error('workesheetId was not given.');
  367 + }
  368 +
  369 + var url = SCOPE_ + '/worksheets/' + this._worksheetId +
  370 + '/private/full/' + this.tabId;
  371 + this.$.worksheetajax.url = url;
  372 + this.$.worksheetajax.generateRequest();
  373 + },
  374 +
  375 + _onWorksheet: function(e) {
  376 + e.stopPropagation();
  377 +
  378 + // this.tab = e.target.lastResponse.entry;
  379 + this._setTab(e.target.lastResponse.entry);
  380 + this._getCellRows();
  381 + },
  382 +
  383 + _getCellRows: function() {
  384 + // Use cached data if available.
  385 + var key = generateCacheKey_.call(this);
  386 + if (key in rowDataCache_) {
  387 + this._onCellRows(null, null, rowDataCache_[key]);
  388 +
  389 + return;
  390 + }
  391 +
  392 + var url = SCOPE_ + '/list/' +
  393 + this._worksheetId + '/' + this.tabId +
  394 + '/private/full';
  395 + this.$.cellrowsajax.url = url;
  396 + this.$.cellrowsajax.generateRequest();
  397 + },
  398 +
  399 + _onCellRows: function(e) {
  400 + e.stopPropagation();
  401 +
  402 + var feed = e.target.lastResponse.feed;
  403 +
  404 + // Cache data if key doesn't exist.
  405 + var key = generateCacheKey_.call(this);
  406 + if (!(key in rowDataCache_)) {
  407 + rowDataCache_[key] = {response: {feed: feed}};
  408 + }
  409 +
  410 + // this.rows = feed.entry;
  411 + this._setRows(feed.entry);
  412 + var authors = feed.author && feed.author.map(function(a) {
  413 + return {email: a.email.$t, name: a.name.$t};
  414 + });
  415 + this.set('rows.authors', authors);
  416 +
  417 + if (this.published) {
  418 + // this.tab = feed;
  419 + this._setTab(feed);
  420 + }
  421 +
  422 + this.fire('google-sheet-data', {
  423 + type: 'rows',
  424 + data: this.rows
  425 + });
  426 + }
  427 +
  428 + });
  429 +
  430 +})();
  431 +</script>
bower_components/google-sheets/index.html 0 → 100755
  1 +<!doctype html>
  2 +<!-- Copyright (c) 2015 Google Inc. All rights reserved. -->
  3 +<html>
  4 +<head>
  5 +
  6 + <script src="../webcomponentsjs/webcomponents-lite.js"></script>
  7 + <link rel="import" href="../iron-component-page/iron-component-page.html">
  8 +
  9 +</head>
  10 +<body>
  11 +
  12 + <iron-component-page></iron-component-page>
  13 +
  14 +</body>
  15 +</html>
bower_components/google-sheets/tests/google-sheet.html 0 → 100644
  1 +<!doctype html>
  2 +<!-- Copyright (c) 2015 Google Inc. All rights reserved. -->
  3 +<html>
  4 +<head>
  5 + <meta charset="utf-8">
  6 + <title>google-sheet tests</title>
  7 + <meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
  8 +
  9 + <script src="../../../platform/platform.js"></script>
  10 + <link rel="import" href="../../../polymer-test-tools/tools.html">
  11 + <script src="../../../polymer-test-tools/htmltest.js"></script>
  12 +
  13 + <link rel="import" href="../google-sheets.html">
  14 +</head>
  15 +<body>
  16 +
  17 +<google-sheets id="sheet1"></google-sheets>
  18 +
  19 +<script>
  20 +document.addEventListener('polymer-ready', function() {
  21 +
  22 + (function() {
  23 + var sheet = document.querySelector('#sheet1');
  24 + var root = sheet.shadowRoot;
  25 +
  26 + // Check defaults.
  27 + assert.lengthOf(sheet.spreadsheets, 0,
  28 + '.spreadsheets length should default to 0');
  29 + assert.lengthOf(sheet.rows, 0, '.rows length should default to 0');
  30 + assert.isObject(sheet.sheet, '.sheet should default to {}');
  31 + assert.isObject(sheet.tab, '.tab should default to {}');
  32 +
  33 + assert.equal(sheet.key, '', ".key default is not ''");
  34 + assert.equal(sheet.gid, 0, '.gid default is not 0');
  35 + assert.isFalse(sheet.published, '.published does not default to false');
  36 + assert.isNotNull(root.querySelector('google-signin-aware'),
  37 + 'google-signin-aware should be created for non-public sheet');
  38 +
  39 + done();
  40 +
  41 + })();
  42 +
  43 +});
  44 +</script>
  45 +</body>
  46 +</html>
bower_components/google-sheets/tests/index.html 0 → 100644
  1 +<!doctype html>
  2 +<!-- Copyright (c) 2015 Google Inc. All rights reserved. -->
  3 +<html>
  4 + <head>
  5 + <meta charset="utf-8">
  6 + <title>Runs all tests</title>
  7 + <meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
  8 + <script src="../../platform/platform.js"></script>
  9 + <link rel="import" href="tests.html">
  10 + </head>
  11 + <body>
  12 + <div id="mocha"></div>
  13 + </body>
  14 +</html>
bower_components/google-sheets/tests/private.html 0 → 100644
  1 +<!doctype html>
  2 +<!-- Copyright (c) 2015 Google Inc. All rights reserved. -->
  3 +<html>
  4 +<head>
  5 + <meta charset="utf-8">
  6 + <title>google-sheet tests</title>
  7 + <meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
  8 +
  9 + <script src="../../../platform/platform.js"></script>
  10 + <link rel="import" href="../../../polymer-test-tools/tools.html">
  11 + <script src="../../../polymer-test-tools/htmltest.js"></script>
  12 +
  13 + <link rel="import" href="../google-sheets.html">
  14 +</head>
  15 +<body>
  16 +
  17 +<div>
  18 + <p>Tests live private spreadsheet data.</p>
  19 + <p>This test needs to be run manually, standalone in order to fetch an OAuth token.</p>
  20 +
  21 + <b>NOTES:</b><br>
  22 + <li>run manually off of localhost:3000/google-sheets/tests/private.html. Can also run from port 8080.</li>
  23 + <li>use the account: webcomponents.test@gmail.com</li>
  24 +</div>
  25 +
  26 +<google-sheets id="sheet" gid="1"
  27 + clientId="750497606405-1hq66meqmr4dp09dn54j9ggv85vbv0gp.apps.googleusercontent.com"
  28 + key="0AjqzxJ5RoRrWdGh2S0RRYURXTlNHdm9pQlFuM1ZwZWc"></google-sheets>
  29 +
  30 +<script>
  31 +document.addEventListener('polymer-ready', function() {
  32 +
  33 + (function() {
  34 + var sheet = document.querySelector('#sheet');
  35 + var root = sheet.shadowRoot;
  36 +
  37 + // Test set attributes.
  38 + assert.equal(sheet.key,
  39 + '0AjqzxJ5RoRrWdGh2S0RRYURXTlNHdm9pQlFuM1ZwZWc', ".key was not updated");
  40 + assert.equal(sheet.gid, 1, '.gid was not updated');
  41 + assert.equal(sheet.clientId,
  42 + '750497606405-1hq66meqmr4dp09dn54j9ggv85vbv0gp.apps.googleusercontent.com', ".clientId was not set");
  43 +
  44 + sheet.addEventListener('google-sheet-data', function(e) {
  45 +
  46 + switch (e.detail.type) {
  47 + case 'spreadsheets':
  48 + assert.isTrue(this.spreadsheets.length > 0,
  49 + '.spreadsheets should be populated for private feeds.');
  50 + break;
  51 + case 'tab':
  52 + assert.equal(this.tab.title, 'SECONDTAB', '.tab.title is incorrect');
  53 + break;
  54 + case 'rows':
  55 + assert.lengthOf(this.rows.authors, 1, '.rows.authors array');
  56 +
  57 + var name = this.rows.authors[0].name;
  58 + var email = this.rows.authors[0].email
  59 +
  60 + assert.equal(email, 'webcomponents.test@gmail.com', 'author email not set correctly');
  61 + assert.equal(name, 'webcomponents.test', 'author name not set correctly');
  62 +
  63 + assert.equal(this.rows[0].title.$t, 'FIRST NAME', '"name" column was incorrect');
  64 + assert.equal(this.rows[1].gsx$state.$t, 'OR', '"state" column was incorrect');
  65 +
  66 + break;
  67 + default:
  68 + // Noop
  69 + }
  70 +
  71 + done();
  72 +
  73 + });
  74 +
  75 + })();
  76 +
  77 +});
  78 +</script>
  79 +</body>
  80 +</html>
  81 +
bower_components/google-sheets/tests/published.html 0 → 100644
  1 +<!doctype html>
  2 +<!-- Copyright (c) 2015 Google Inc. All rights reserved. -->
  3 +<html>
  4 +<head>
  5 + <meta charset="utf-8">
  6 + <title>google-sheet tests</title>
  7 + <meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
  8 +
  9 + <script src="../../../platform/platform.js"></script>
  10 + <link rel="import" href="../../../polymer-test-tools/tools.html">
  11 + <script src="../../../polymer-test-tools/htmltest.js"></script>
  12 +
  13 + <link rel="import" href="../google-sheets.html">
  14 +</head>
  15 +<body>
  16 +
  17 +<google-sheets id="sheet" key="0Anye-JMjUkZZdDBkMVluMEhZMmFGeHpYdDJJV1FBRWc"
  18 + published></google-sheets>
  19 +
  20 +<script>
  21 +document.addEventListener('polymer-ready', function() {
  22 +
  23 + var sheet = document.querySelector('#sheet');
  24 + var root = sheet.shadowRoot;
  25 +
  26 + assert.isTrue(sheet.published);
  27 + assert.isNull(root.querySelector('google-signin-aware'),
  28 + 'google-signin-aware should not be created for a published sheet');
  29 +
  30 + sheet.addEventListener('google-sheet-data', function(e) {
  31 +
  32 + if (e.detail.type === 'tab') {
  33 + assert.equal(this.tab.title, 'Locations',
  34 + 'Published spreadsheet title is not correct.');
  35 + assert.isNotNull(this.tab.updated, '.tab.updated was not set');
  36 + assert.isTrue(this.tab.authors.length > 0, '.tab.authors was 0');
  37 + } else if (e.detail.type === 'rows') {
  38 + assert.lengthOf(this.spreadsheets, 0,
  39 + '.spreadsheets length should be 0 since spreadsheet key was given');
  40 + assert.isTrue(this.rows.length > 0, '.rows was not populated');
  41 + }
  42 +
  43 + assert.equal(this.$.cellrowsajax.url, '',
  44 + '#cellrowsajax should not be invoked for a public spreadsheet');
  45 +
  46 + done();
  47 + });
  48 +});
  49 +</script>
  50 +</body>
  51 +</html>
bower_components/google-sheets/tests/tests.html 0 → 100644
  1 +<!-- Copyright (c) 2015 Google Inc. All rights reserved. -->
  2 +
  3 +<link rel="import" href="../../polymer-test-tools/tools.html">
  4 +<script src="../../polymer-test-tools/mocha-htmltest.js"></script>
  5 +
  6 +<script>
  7 +mocha.setup({ui: 'tdd', slow: 1000, timeout: 5000, htmlbase: ''});
  8 +
  9 +htmlSuite('google-sheet', function() {
  10 + htmlTest('google-sheet.html');
  11 + htmlTest('published.html');
  12 + htmlTest('private.html');
  13 +});
  14 +
  15 +mocha.run();
  16 +</script>
bower_components/google-signin/.bower.json 0 → 100644
  1 +{
  2 + "name": "google-signin",
  3 + "version": "1.2.0",
  4 + "description": "Web components to authenticate with Google services",
  5 + "homepage": "https://googlewebcomponents.github.io/google-signin",
  6 + "main": "google-signin.html",
  7 + "authors": [
  8 + "Addy Osmani",
  9 + "Randy Merrill"
  10 + ],
  11 + "license": "Apache-2",
  12 + "ignore": [
  13 + "/.*",
  14 + "/test/"
  15 + ],
  16 + "keywords": [
  17 + "web-component",
  18 + "web-components",
  19 + "polymer",
  20 + "sign-in",
  21 + "google",
  22 + "authentication"
  23 + ],
  24 + "dependencies": {
  25 + "polymer": "Polymer/polymer#^1.0.0",
  26 + "font-roboto": "PolymerElements/font-roboto#^1.0.0",
  27 + "iron-icon": "PolymerElements/iron-icon#^1.0.0",
  28 + "iron-icons": "PolymerElements/iron-icons#^1.0.0",
  29 + "iron-flex-layout": "PolymerElements/iron-flex-layout#^1.0.0",
  30 + "paper-ripple": "PolymerElements/paper-ripple#^1.0.0",
  31 + "paper-material": "PolymerElements/paper-material#^1.0.0",
  32 + "google-apis": "GoogleWebComponents/google-apis#^1.0.0"
  33 + },
  34 + "devDependencies": {
  35 + "iron-component-page": "PolymerElements/iron-component-page#^1.0.2"
  36 + },
  37 + "_release": "1.2.0",
  38 + "_resolution": {
  39 + "type": "version",
  40 + "tag": "v1.2.0",
  41 + "commit": "a1414b51b39e0a7dfe7031eb73a7733e2216579d"
  42 + },
  43 + "_source": "git://github.com/GoogleWebComponents/google-signin.git",
  44 + "_target": "^1.0.3",
  45 + "_originalSource": "GoogleWebComponents/google-signin"
  46 +}
0 \ No newline at end of file 47 \ No newline at end of file
bower_components/google-signin/LICENSE 0 → 100644
  1 +Copyright 2014 Google Inc
  2 +
  3 +Licensed under the Apache License, Version 2.0 (the "License");
  4 +you may not use this file except in compliance with the License.
  5 +You may obtain a copy of the License at
  6 +
  7 + https://www.apache.org/licenses/LICENSE-2.0
  8 +
  9 +Unless required by applicable law or agreed to in writing, software
  10 +distributed under the License is distributed on an "AS IS" BASIS,
  11 +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12 +See the License for the specific language governing permissions and
  13 +limitations under the License.
bower_components/google-signin/README.md 0 → 100644
  1 +google-signin
  2 +================
  3 +
  4 +See the [component page](https://googlewebcomponents.github.io/google-signin) for more information.
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>&lt;google-signin&gt;</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>&lt;google-signin-aware&gt;</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>&lt;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>"&gt;</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 +&lt;google-signin&gt; is used to authenticate with Google, allowing you to interact
  121 +with other Google APIs such as Drive and Google+.
  122 +
  123 +<img style="max-width:100%;" src="https://cloud.githubusercontent.com/assets/107076/6791176/5c868822-d16a-11e4-918c-ec9b84a2db45.png"/>
  124 +
  125 +If you do not need to show the button, use companion `<google-signin-aware>` element to declare scopes, check authentication state.
  126 +
  127 +#### Examples
  128 +
  129 + <google-signin client-id="..." scopes="https://www.googleapis.com/auth/drive"></google-signin>
  130 +
  131 + <google-signin label-signin="Sign-in" client-id="..." scopes="https://www.googleapis.com/auth/drive"></google-signin>
  132 +
  133 + <google-signin theme="dark" width="iconOnly" client-id="..." scopes="https://www.googleapis.com/auth/drive"></google-signin>
  134 +
  135 +
  136 +#### Notes
  137 +
  138 +The attribute `clientId` is provided in your Google Developers Console
  139 +(https://console.developers.google.com).
  140 +
  141 +The `scopes` attribute allows you to specify which scope permissions are required
  142 +(e.g do you want to allow interaction with the Google Drive API). Many APIs also
  143 +need to be enabled in the Google Developers Console before you can use them.
  144 +
  145 +The `requestVisibleActions` attribute is necessary if you want to write app
  146 +activities (https://developers.google.com/+/web/app-activities/) on behalf of
  147 +the user. Please note that this attribute is only valid in combination with the
  148 +plus.login scope (https://www.googleapis.com/auth/plus.login).
  149 +
  150 +The `offline` attribute allows you to get an auth code which your server can
  151 +redeem for an offline access token
  152 +(https://developers.google.com/identity/sign-in/web/server-side-flow).
  153 +You can also set `offline-always-prompt` instead of `offline` to ensure that your app
  154 +will re-prompt the user for offline access and generate a working `refresh_token`
  155 +even if they have already granted offline access to your app in the past.
  156 +
  157 +Use label properties to customize prompts.
  158 +
  159 +The button can be styled in using the `height`, `width`, and `theme` attributes.
  160 +These attributes help you follow the Google+ Sign-In button branding guidelines
  161 +(https://developers.google.com/+/branding-guidelines).
  162 +
  163 +The `google-signin-success` event is triggered when a user successfully authenticates
  164 +and `google-signed-out` is triggered when user signs out.
  165 +You can also use `isAuthorized` attribute to observe user's authentication state.
  166 +
  167 +Additional events, such as `google-signout-attempted` are
  168 +triggered when the user attempts to sign-out and successfully signs out.
  169 +
  170 +When requesting offline access, the `google-signin-offline-success` event is
  171 +triggered when the user successfully consents with offline support.
  172 +
  173 +The `google-signin-necessary` event is fired when scopes requested via
  174 +google-signin-aware elements require additional user permissions.
  175 +
  176 +#### Testing
  177 +
  178 +By default, the demo accompanying this element is setup to work on localhost with
  179 +port 8080. That said, you *should* update the `clientId` to your own one for
  180 +any apps you're building. See the Google Developers Console
  181 +(https://console.developers.google.com) for more info.
  182 +
  183 +@demo
  184 +*/
  185 +
  186 + Polymer({
  187 +
  188 + is: 'google-signin',
  189 +
  190 + /**
  191 + * Fired when user is signed in.
  192 + * You can use auth2 api to retrieve current user: `gapi.auth2.getAuthInstance().currentUser.get();`
  193 + * @event google-signin-success
  194 + */
  195 +
  196 + /**
  197 + * Fired when the user is signed-out.
  198 + * @event google-signed-out
  199 + */
  200 +
  201 + /**
  202 + * Fired if user requires additional authorization
  203 + * @event google-signin-necessary
  204 + */
  205 +
  206 + /**
  207 + * Fired when signed in, and scope has been authorized
  208 + * @param {Object} result Authorization result.
  209 + * @event google-signin-aware-success
  210 + */
  211 +
  212 + /**
  213 + * Fired when an offline authorization is successful.
  214 + * @event google-signin-offline-success
  215 + * @param {Object} detail
  216 + * @param {string} detail.code The one-time authorization code from Google.
  217 + * Your application can exchange this for an `access_token` and `refresh_token`
  218 + */
  219 +
  220 + /**
  221 + * Fired when this scope is not authorized
  222 + * @event google-signin-aware-signed-out
  223 + */
  224 + properties: {
  225 + /**
  226 + * App package name for android over-the-air installs.
  227 + * See the relevant [docs](https://developers.google.com/+/web/signin/android-app-installs)
  228 + */
  229 + appPackageName: {
  230 + type: String,
  231 + value: ''
  232 + },
  233 +
  234 + /**
  235 + * The brand being used for logo and styling.
  236 + *
  237 + * @default 'google'
  238 + */
  239 + brand: {
  240 + type: String,
  241 + value: ''
  242 + },
  243 +
  244 + /** @private */
  245 + _brand: {
  246 + type: String,
  247 + computed: '_computeBrand(brand, hasPlusScopes)'
  248 + },
  249 +
  250 + /**
  251 + * a Google Developers clientId reference
  252 + */
  253 + clientId: {
  254 + type: String,
  255 + value: ''
  256 + },
  257 +
  258 + /**
  259 + * The cookie policy defines what URIs have access to the session cookie
  260 + * remembering the user's sign-in state.
  261 + * See the relevant [docs](https://developers.google.com/+/web/signin/reference#determining_a_value_for_cookie_policy) for more information.
  262 + *
  263 + * @default 'single_host_origin'
  264 + */
  265 + cookiePolicy: {
  266 + type: String,
  267 + value: ''
  268 + },
  269 +
  270 + /**
  271 + * The height to use for the button.
  272 + *
  273 + * Available options: short, standard, tall.
  274 + *
  275 + * @type {HeightValue}
  276 + */
  277 + height: {
  278 + type: String,
  279 + value: 'standard'
  280 + },
  281 +
  282 + /**
  283 + * By default the ripple expands to fill the button. Set this to true to
  284 + * constrain the ripple to a circle within the button.
  285 + */
  286 + fill: {
  287 + type: Boolean,
  288 + value: true
  289 + },
  290 +
  291 + /**
  292 + * An optional label for the button for additional permissions.
  293 + */
  294 + labelAdditional: {
  295 + type: String,
  296 + value: 'Additional permissions required'
  297 + },
  298 +
  299 + /**
  300 + * An optional label for the sign-in button.
  301 + */
  302 + labelSignin: {
  303 + type: String,
  304 + value: ''
  305 + },
  306 +
  307 + _labelSignin: {
  308 + type: String,
  309 + computed: '_computeSigninLabel(labelSignin, width, _brand)'
  310 + },
  311 +
  312 + /**
  313 + * An optional label for the sign-out button.
  314 + */
  315 + labelSignout: {
  316 + type: String,
  317 + value: 'Sign out'
  318 + },
  319 +
  320 + /**
  321 + * If true, the button will be styled with a shadow.
  322 + */
  323 + raised: {
  324 + type: Boolean,
  325 + value: false
  326 + },
  327 +
  328 + /**
  329 + * The app activity types you want to write on behalf of the user
  330 + * (e.g http://schemas.google.com/AddActivity)
  331 + */
  332 + requestVisibleActions: {
  333 + type: String,
  334 + value: ''
  335 + },
  336 +
  337 + /**
  338 + * The Google Apps domain to which users must belong to sign in.
  339 + * See the relevant [docs](https://developers.google.com/identity/sign-in/web/reference) for more information.
  340 + */
  341 + hostedDomain: {
  342 + type: String,
  343 + value: ''
  344 + },
  345 +
  346 + /**
  347 + * Allows for offline `access_token` retrieval during the signin process.
  348 + */
  349 + offline: {
  350 + type: Boolean,
  351 + value: false
  352 + },
  353 +
  354 + /**
  355 + * Forces a re-prompt, even if the user has already granted offline
  356 + * access to your application in the past. You only need one of
  357 + * `offline` and `offlineAlwaysPrompt`.
  358 + */
  359 + offlineAlwaysPrompt: {
  360 + type: Boolean,
  361 + value: false
  362 + },
  363 +
  364 + /**
  365 + * The scopes to provide access to (e.g https://www.googleapis.com/auth/drive)
  366 + * and should be space-delimited.
  367 + */
  368 + scopes: {
  369 + type: String,
  370 + value: ''
  371 + },
  372 +
  373 + /**
  374 + * The theme to use for the button.
  375 + *
  376 + * Available options: light, dark.
  377 + *
  378 + * @attribute theme
  379 + * @type {ThemeValue}
  380 + * @default 'dark'
  381 + */
  382 + theme: {
  383 + type: String,
  384 + value: 'dark'
  385 + },
  386 +
  387 + /**
  388 + * The width to use for the button.
  389 + *
  390 + * Available options: iconOnly, standard, wide.
  391 + *
  392 + * @type {WidthValue}
  393 + */
  394 + width: {
  395 + type: String,
  396 + value: 'standard'
  397 + },
  398 +
  399 + _brandIcon: {
  400 + type: String,
  401 + computed: '_computeIcon(_brand)'
  402 + },
  403 +
  404 + /**
  405 + * True if *any* element has google+ scopes
  406 + */
  407 + hasPlusScopes: {
  408 + type: Boolean,
  409 + notify: true,
  410 + value: false
  411 + },
  412 +
  413 + /**
  414 + * True if additional authorization required globally
  415 + */
  416 + needAdditionalAuth: {
  417 + type: Boolean,
  418 + value: false
  419 + },
  420 +
  421 + /**
  422 + * Is user signed in?
  423 + */
  424 + signedIn: {
  425 + type: Boolean,
  426 + notify: true,
  427 + value: false,
  428 + observer: '_observeSignedIn'
  429 + },
  430 +
  431 + /**
  432 + * True if authorizations for *this* element have been granted
  433 + */
  434 + isAuthorized: {
  435 + type: Boolean,
  436 + notify: true,
  437 + value: false
  438 + }
  439 +
  440 + },
  441 +
  442 + _computeButtonClass: function(height, width, theme, signedIn, brand, needAdditionalAuth) {
  443 + return "height-" + height + " width-" + width + " theme-" + theme + " signedIn-" + signedIn + " brand-" + brand + " additionalAuth-" + needAdditionalAuth;
  444 + },
  445 +
  446 + _computeIcon: function(brand) {
  447 + return "google:" + brand;
  448 + },
  449 +
  450 + /* Button state computed */
  451 + _computeButtonIsSignIn: function(signedIn, additionalAuth) {
  452 + return !signedIn;
  453 + },
  454 +
  455 + _computeButtonIsSignOut: function(signedIn, additionalAuth) {
  456 + return signedIn && !additionalAuth;
  457 + },
  458 +
  459 + _computeButtonIsSignOutAddl: function(signedIn, additionalAuth) {
  460 + return signedIn && additionalAuth;
  461 + },
  462 +
  463 + _computeBrand: function(attrBrand, hasPlusScopes) {
  464 + var newBrand;
  465 + if (attrBrand) {
  466 + newBrand = attrBrand;
  467 + } else if (hasPlusScopes) {
  468 + newBrand = BrandValue.PLUS;
  469 + } else {
  470 + newBrand = BrandValue.GOOGLE;
  471 + };
  472 + return newBrand;
  473 + },
  474 +
  475 + _observeSignedIn: function(newVal, oldVal) {
  476 + if (newVal) {
  477 + if (this.needAdditionalAuth)
  478 + this.fire('google-signin-necessary');
  479 + this.fire('google-signin-success');
  480 + }
  481 + else
  482 + this.fire('google-signed-out');
  483 + },
  484 +
  485 + /**
  486 + * Determines the proper label based on the attributes.
  487 + */
  488 + _computeSigninLabel: function(labelSignin, width, _brand) {
  489 + if (labelSignin) {
  490 + return labelSignin;
  491 + } else {
  492 + switch(width) {
  493 +
  494 + case WidthValue.WIDE:
  495 + return (_brand == BrandValue.PLUS) ?
  496 + LabelValue.WIDE_PLUS : LabelValue.WIDE;
  497 +
  498 + case WidthValue.STANDARD:
  499 + return LabelValue.STANDARD;
  500 +
  501 + case WidthValue.ICON_ONLY:
  502 + return '';
  503 +
  504 + default:
  505 + console.warn("bad width value: ", width);
  506 + return LabelValue.STANDARD;
  507 + }
  508 + }
  509 + },
  510 +
  511 + /** Sign in user. Opens the authorization dialog for signing in.
  512 + * The dialog will be blocked by a popup blocker unless called inside click handler.
  513 + */
  514 + signIn: function () {
  515 + this.$.aware.signIn();
  516 + },
  517 +
  518 + _signInKeyPress: function (e) {
  519 + if (e.which == 13 || e.keyCode == 13 || e.which == 32 || e.keyCode == 32) {
  520 + e.preventDefault();
  521 + this.signIn();
  522 + }
  523 + },
  524 +
  525 + /** Sign out the user */
  526 + signOut: function () {
  527 + this.fire('google-signout-attempted');
  528 + this.$.aware.signOut();
  529 + },
  530 +
  531 + _signOutKeyPress: function (e) {
  532 + if (e.which == 13 || e.keyCode == 13 || e.which == 32 || e.keyCode == 32) {
  533 + e.preventDefault();
  534 + this.signOut();
  535 + }
  536 + }
  537 + });
  538 + }());
  539 +</script>
bower_components/google-signin/index.html 0 → 100644
  1 +<!doctype html>
  2 +<!-- Copyright (c) 2014 Google Inc. All rights reserved. -->
  3 +<html>
  4 +<head>
  5 +
  6 + <script src="../webcomponentsjs/webcomponents-lite.js"></script>
  7 + <link rel="import" href="../iron-component-page/iron-component-page.html">
  8 +
  9 +</head>
  10 +<body>
  11 +
  12 + <iron-component-page></iron-component-page>
  13 +
  14 +</body>
  15 +</html>
datalets/test-google-components/test.html 0 → 100644
  1 +<!DOCTYPE html>
  2 +<html lang="en">
  3 +<head>
  4 + <meta charset="UTF-8">
  5 + <title>Google spreadsheet and document test page</title>
  6 +
  7 + <script type="text/javascript" src="../../bower_components/webcomponentsjs/webcomponents-lite.min.js"></script>
  8 + <script type="text/javascript" src="../shared_js/jquery-1.11.2.min.js"></script>
  9 +
  10 + <link rel="import" href="../../bower_components/google-sheets/google-sheets.html">
  11 + <link rel="import" href="../../bower_components/iron-flex-layout/iron-flex-layout.html">
  12 + <link rel="import" href="../../bower_components/iron-icons/iron-icons.html">
  13 + <link rel="import" href="../../bower_components/paper-fab/paper-fab.html">
  14 + <link rel="import" href="../linechart-datalet/linechart-datalet.html">
  15 +
  16 + <style>
  17 + #sheet{
  18 + height: 400px;
  19 + width: 40%;
  20 + margin: 40px;
  21 + }
  22 +
  23 + #showVisualization{
  24 + background-color: #2196F3;
  25 + color: #ffffff;
  26 + margin-left: 50px;
  27 + }
  28 +
  29 +
  30 + #datalet_placeholder{
  31 + height: 400px;
  32 + width: 40%;
  33 + margin: 40px;/
  34 + }
  35 +
  36 + #doc{
  37 + height: 400px;
  38 + width: 100%;
  39 + }
  40 + </style>
  41 +
  42 + <script>
  43 +
  44 + getData = function()
  45 + {
  46 + var sheet = document.querySelector('google-sheets');
  47 +
  48 + sheet.addEventListener('google-sheet-data', function(e)
  49 + {
  50 + var data = [];
  51 + //get the keys
  52 + for(var key in this.rows[0]) {
  53 + if (key.match(/gsx\$*/)) {
  54 + data.push({name: key.replace("gsx$", ""), data: []});
  55 + }
  56 + }
  57 + //fill the structure with values
  58 + for(var row in this.rows){
  59 + var i = 0;
  60 + for(var key in this.rows[row]){
  61 + if(key.match(/gsx\$*/)){
  62 + data[i++].data.push(this.rows[row][key].$t.replace("'",""));
  63 + }
  64 + }
  65 +
  66 + }
  67 +
  68 + $("#datalet_placeholder").html('<linechart-datalet ' +
  69 + 'data-url="https://docs.google.com/spreadsheets/d/1EYJ88pOsbc0GkUTTdPhuReUjHewk1fhis9VZBXes_Ao" ' +
  70 + //'fields=\'[]\'' +
  71 + 'title="Google spreadsheet realtime data visualization"' +
  72 + 'data=\'' + JSON.stringify(data) + '\'>' +
  73 + '</linechart-datalet>');
  74 +
  75 + $("#spreadsheet_url").html('<a href="' + this.openInGoogleDocsUrl + '" target="_blank">' + this.openInGoogleDocsUrl + '</a>');
  76 +
  77 + });
  78 +
  79 + sheet.addEventListener('error', function(e) {
  80 + // e.detail.response
  81 + });
  82 +
  83 + }
  84 +
  85 + refresh = function(){
  86 + $("#sheet-component").html('<google-sheets key="1EYJ88pOsbc0GkUTTdPhuReUjHewk1fhis9VZBXes_Ao" published></google-sheets>');
  87 + getData();
  88 + }
  89 +
  90 + $( document ).ready(function() {
  91 + getData();
  92 + });
  93 +
  94 + </script>
  95 +
  96 +</head>
  97 +<body is="dom-bind">
  98 + <div class="layout vertical">
  99 + <div class="layout vertical">
  100 + <div class="layout horizontal" style="width: 100%;">
  101 + <h1>Google Spreadsheet realtime visualization with Datalet</h1>
  102 + <paper-fab id="showVisualization" icon="refresh" onClick="refresh()"></paper-fab>
  103 + </div>
  104 + <div class="layout horizontal" style="width: 100%;">
  105 + <h3 id="spreadsheet_url"></h3>
  106 + </div>
  107 + <div class="layout horizontal">
  108 + <iframe id="sheet" src="https://docs.google.com/spreadsheets/d/1EYJ88pOsbc0GkUTTdPhuReUjHewk1fhis9VZBXes_Ao/edit?usp=sharing?embedded=true"></iframe>
  109 + <div id="datalet_placeholder"></div>
  110 + </div>
  111 + </div>
  112 +
  113 + <div class="layout vertical">
  114 + <h1 style="width: 100%;margin-top: 100px;">Google Docs realtime visualization with Datalet</h1>
  115 + <iframe id="doc" src="https://docs.google.com/document/d/1IIfmqTGBpTkEMHPnuxJeoOJI3u-8wQu9dkf49X2T6SE/edit?usp=sharing?embedded=true"></iframe>
  116 + </div>
  117 + </div>
  118 +
  119 + <div id="sheet-component">
  120 + <google-sheets key="1EYJ88pOsbc0GkUTTdPhuReUjHewk1fhis9VZBXes_Ao" published></google-sheets>
  121 + </div>
  122 +
  123 +</body>
  124 +</html>
0 \ No newline at end of file 125 \ No newline at end of file