config_service: Added google-signin-aware.html for Polymer 2.0.

This is the original file that was pulled from Scarygami's 2.0-preview branch
of the Google Web Component google-signin-aware. This CL is being uploaded
so that my changes to this file can be viewed in another CL.

The link to this particular file can be found here:
https://github.com/Scarygami/google-signin/blob/599547e042ae4e5dec21051953754eec93fe26ca/google-signin-aware.html

BUG=730832

Review-Url: https://codereview.chromium.org/2963963003
diff --git a/appengine/config_service/ui/common/third-party/google-signin-aware.html b/appengine/config_service/ui/common/third-party/google-signin-aware.html
new file mode 100644
index 0000000..e6e731b
--- /dev/null
+++ b/appengine/config_service/ui/common/third-party/google-signin-aware.html
@@ -0,0 +1,824 @@
+<!--
+Copyright 2014 Google Inc
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    https://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+-->
+
+<link rel="import" href="../polymer/polymer.html">
+<link rel="import" href="../google-apis/google-js-api.html">
+
+<script>
+  (function() {
+
+    /**
+     * Enum of attributes to be passed through to the login API call.
+     * @readonly
+     * @enum {string}
+     */
+    var ProxyLoginAttributes = {
+      'appPackageName': 'apppackagename',
+      'clientId': 'clientid',
+      'cookiePolicy': 'cookiepolicy',
+      'hostedDomain': 'hostedDomain',
+      'openidPrompt': 'prompt',
+      'requestVisibleActions': 'requestvisibleactions'
+    };
+
+    /**
+     * AuthEngine does all interactions with gapi.auth2
+     *
+     * It is tightly coupled with <google-signin-aware> element
+     * The elements configure AuthEngine.
+     * AuthEngine propagates all authentication events to all google-signin-aware elements
+     *
+     * API used: https://developers.google.com/identity/sign-in/web/reference
+     *
+     */
+    var AuthEngine = {
+
+      /**
+       * oauth2 argument, set by google-signin-aware
+       */
+      _clientId: null,
+
+      get clientId() {
+        return this._clientId;
+      },
+
+      set clientId(val) {
+        if (this._clientId && val && val != this._clientId) {
+          throw new Error('clientId cannot change. Values do not match. New: ' + val + ' Old:' + this._clientId);
+        }
+        if (val && val != this._clientId) {
+          this._clientId = val;
+          this.initAuth2();
+        }
+      },
+
+      /**
+       * oauth2 argument, set by google-signin-aware
+       */
+      _cookiePolicy: 'single_host_origin',
+
+      get cookiePolicy() {
+        return this._cookiePolicy;
+      },
+
+      set cookiePolicy(val) {
+        if (val) {
+          this._cookiePolicy = val;
+        }
+      },
+
+      /**
+       * oauth2 argument, set by google-signin-aware
+       */
+      _appPackageName: '',
+
+      get appPackageName() {
+        return this._appPackageName;
+      },
+
+      set appPackageName(val) {
+        if (this._appPackageName && val && val != this._appPackageName) {
+          throw new Error('appPackageName cannot change. Values do not match. New: ' + val + ' Old: ' + this._appPackageName);
+        }
+        if (val) {
+          this._appPackageName = val;
+        }
+      },
+
+     /**
+       * oauth2 argument, set by google-signin-aware
+       */
+      _requestVisibleActions: '',
+
+      get requestVisibleactions() {
+        return this._requestVisibleActions;
+      },
+
+      set requestVisibleactions(val) {
+        if (this._requestVisibleActions && val && val != this._requestVisibleActions) {
+          throw new Error('requestVisibleactions cannot change. Values do not match. New: ' + val + ' Old: ' + this._requestVisibleActions);
+        }
+        if (val)
+          this._requestVisibleActions = val;
+      },
+
+     /**
+       * oauth2 argument, set by google-signin-aware
+       */
+      _hostedDomain: '',
+
+      get hostedDomain() {
+        return this._hostedDomain;
+      },
+
+      set hostedDomain(val) {
+        if (this._hostedDomain && val && val != this._hostedDomain) {
+          throw new Error('hostedDomain cannot change. Values do not match. New: ' + val + ' Old: ' + this._hostedDomain);
+        }
+        if (val)
+          this._hostedDomain = val;
+      },
+
+     /**
+       * oauth2 argument, set by google-signin-aware
+       */
+      _openidPrompt: '',
+
+      get openidPrompt() {
+        return this._openidPrompt;
+      },
+
+      set openidPrompt(val) {
+        if (typeof val !== 'string') {
+          throw new Error(
+              'openidPrompt must be a string. Received ' + typeof val);
+        }
+        if (val) {
+          var values = val.split(' ');
+          values = values.map(function(v) {
+            return v.trim();
+          });
+          values = values.filter(function(v) {
+            return v;
+          });
+          var validValues = {none: 0, login: 0, consent: 0, select_account: 0};
+          values.forEach(function(v) {
+            if (v == 'none' && values.length > 1) {
+              throw new Error(
+                  'none cannot be combined with other openidPrompt values');
+            }
+            if (!(v in validValues)) {
+              throw new Error(
+                  'invalid openidPrompt value ' + v +
+                  '. Valid values: ' + Object.keys(validValues).join(', '));
+            }
+          });
+        }
+        this._openidPrompt = val;
+      },
+
+      /** Is offline access currently enabled in the google-signin-aware element? */
+      _offline: false,
+
+      get offline() {
+        return this._offline;
+      },
+
+      set offline(val) {
+        this._offline = val;
+        this.updateAdditionalAuth();
+      },
+
+      /** Should we force a re-prompt for offline access? */
+      _offlineAlwaysPrompt: false,
+
+      get offlineAlwaysPrompt() {
+        return this._offlineAlwaysPrompt;
+      },
+
+      set offlineAlwaysPrompt(val) {
+        this._offlineAlwaysPrompt = val;
+        this.updateAdditionalAuth();
+      },
+
+      /** Have we already gotten offline access from Google during this session? */
+      offlineGranted: false,
+
+      /** <google-js-api> */
+      _apiLoader: null,
+
+      /** an array of wanted scopes. oauth2 argument */
+      _requestedScopeArray: [],
+
+      /** _requestedScopeArray as string */
+      get requestedScopes() {
+        return this._requestedScopeArray.join(' ');
+      },
+
+      /** Is auth library initalized? */
+      _initialized: false,
+
+      /** Is user signed in? */
+      _signedIn: false,
+
+      /** Currently granted scopes */
+      _grantedScopeArray: [],
+
+      /** True if additional authorization is required */
+      _needAdditionalAuth: true,
+
+      /** True if have google+ scopes */
+      _hasPlusScopes: false,
+
+      /**
+       * array of <google-signin-aware>
+       * state changes are broadcast to them
+       */
+      signinAwares: [],
+
+      init: function() {
+        this._apiLoader = document.createElement('google-js-api');
+        this._apiLoader.addEventListener('js-api-load', this.loadAuth2.bind(this));
+        if (Polymer.Element) {
+          document.body.appendChild(this._apiLoader);
+        }
+      },
+
+      loadAuth2: function() {
+        gapi.load('auth2', this.initAuth2.bind(this));
+      },
+
+      initAuth2: function() {
+        if (!('gapi' in window) || !('auth2' in window.gapi) || !this.clientId) {
+          return;
+        }
+        var auth = gapi.auth2.init({
+          'client_id': this.clientId,
+          'cookie_policy': this.cookiePolicy,
+          'scope': this.requestedScopes,
+          'hosted_domain': this.hostedDomain
+        });
+
+        auth['currentUser'].listen(this.handleUserUpdate.bind(this));
+
+        auth.then(
+          function onFulfilled() {
+          // Let the current user listener trigger the changes.
+          },
+          function onRejected(error) {
+            console.error(error);
+          }
+        );
+      },
+
+      handleUserUpdate: function(newPrimaryUser) {
+        // update and broadcast currentUser
+        var isSignedIn = newPrimaryUser.isSignedIn();
+        if (isSignedIn != this._signedIn) {
+          this._signedIn = isSignedIn;
+          for (var i=0; i<this.signinAwares.length; i++) {
+            this.signinAwares[i]._setSignedIn(isSignedIn);
+          }
+        }
+        // update and broadcast initialized property the first time the isSignedIn property is set.
+        if(!this._initialized) {
+            for (var i=0; i<this.signinAwares.length; i++) {
+                this.signinAwares[i]._setInitialized(true);
+            }
+            this._initialized = true;
+        }
+
+
+        // update granted scopes
+        this._grantedScopeArray = this.strToScopeArray(
+          newPrimaryUser.getGrantedScopes());
+        // console.log(this._grantedScopeArray);
+        this.updateAdditionalAuth();
+
+        var response = newPrimaryUser.getAuthResponse();
+        for (var i=0; i<this.signinAwares.length; i++) {
+          this.signinAwares[i]._updateScopeStatus(response);
+        }
+      },
+
+      setOfflineCode: function(code) {
+        for (var i=0; i<this.signinAwares.length; i++) {
+          this.signinAwares[i]._updateOfflineCode(code);
+        }
+      },
+
+      /** convert scope string to scope array */
+      strToScopeArray: function(str) {
+        if (!str) {
+          return [];
+        }
+        // remove extra spaces, then split
+        var scopes = str.replace(/\ +/g, ' ').trim().split(' ');
+        for (var i=0; i<scopes.length; i++) {
+          scopes[i] = scopes[i].toLowerCase();
+           // Handle scopes that will be deprecated but are still returned with their old value
+          if (scopes[i] === 'https://www.googleapis.com/auth/userinfo.profile') {
+            scopes[i] = 'profile';
+          }
+          if (scopes[i] === 'https://www.googleapis.com/auth/userinfo.email') {
+            scopes[i] = 'email';
+          }
+        }
+        // return with duplicates filtered out
+        return scopes.filter( function(value, index, self) {
+          return self.indexOf(value) === index;
+        });
+      },
+
+      /** true if scopes have google+ scopes */
+      isPlusScope: function(scope) {
+        return (scope.indexOf('/auth/games') > -1)
+            || (scope.indexOf('auth/plus.') > -1 && scope.indexOf('auth/plus.me') < 0);
+      },
+
+      /** true if scopes have been granted */
+      hasGrantedScopes: function(scopeStr) {
+        var scopes = this.strToScopeArray(scopeStr);
+        for (var i=0; i< scopes.length; i++) {
+          if (this._grantedScopeArray.indexOf(scopes[i]) === -1)
+            return false;
+        }
+        return true;
+      },
+
+      /** request additional scopes */
+      requestScopes: function(newScopeStr) {
+        var newScopes = this.strToScopeArray(newScopeStr);
+        var scopesUpdated = false;
+        for (var i=0; i<newScopes.length; i++) {
+          if (this._requestedScopeArray.indexOf(newScopes[i]) === -1) {
+            this._requestedScopeArray.push(newScopes[i]);
+            scopesUpdated = true;
+          }
+        }
+        if (scopesUpdated) {
+          this.updateAdditionalAuth();
+          this.updatePlusScopes();
+        }
+      },
+
+      /** update status of _needAdditionalAuth */
+      updateAdditionalAuth: function() {
+        var needMoreAuth = false;
+        if ((this.offlineAlwaysPrompt || this.offline ) && !this.offlineGranted) {
+          needMoreAuth = true;
+        } else {
+          for (var i=0; i<this._requestedScopeArray.length; i++) {
+            if (this._grantedScopeArray.indexOf(this._requestedScopeArray[i]) === -1) {
+              needMoreAuth = true;
+              break;
+            }
+          }
+        }
+        if (this._needAdditionalAuth != needMoreAuth) {
+          this._needAdditionalAuth = needMoreAuth;
+          // broadcast new value
+          for (var i=0; i<this.signinAwares.length; i++) {
+            this.signinAwares[i]._setNeedAdditionalAuth(needMoreAuth);
+          }
+        }
+      },
+
+      updatePlusScopes: function() {
+        var hasPlusScopes = false;
+        for (var i = 0; i < this._requestedScopeArray.length; i++) {
+          if (this.isPlusScope(this._requestedScopeArray[i])) {
+            hasPlusScopes = true;
+            break;
+          }
+        }
+        if (this._hasPlusScopes != hasPlusScopes) {
+          this._hasPlusScopes = hasPlusScopes;
+          for (var i=0; i<this.signinAwares.length; i++) {
+            this.signinAwares[i]._setHasPlusScopes(hasPlusScopes);
+          }
+        }
+      },
+      /**
+       * attached <google-signin-aware>
+       * @param {!GoogleSigninAwareElement} aware element to add
+       */
+      attachSigninAware: function(aware) {
+        if (this.signinAwares.indexOf(aware) == -1) {
+          this.signinAwares.push(aware);
+          // Initialize aware properties
+          aware._setNeedAdditionalAuth(this._needAdditionalAuth);
+          aware._setInitialized(this._initialized);
+          aware._setSignedIn(this._signedIn);
+          aware._setHasPlusScopes(this._hasPlusScopes);
+        } else {
+          console.warn('signinAware attached more than once', aware);
+        }
+      },
+
+      detachSigninAware: function(aware) {
+        var index = this.signinAwares.indexOf(aware);
+        if (index != -1) {
+          this.signinAwares.splice(index, 1);
+        } else {
+          console.warn('Trying to detach unattached signin-aware');
+        }
+      },
+
+      /** returns scopes not granted */
+      getMissingScopes: function() {
+        return this._requestedScopeArray.filter( function(scope) {
+          return this._grantedScopeArray.indexOf(scope) === -1;
+        }.bind(this)).join(' ');
+      },
+
+      assertAuthInitialized: function() {
+        if (!this.clientId) {
+          throw new Error("AuthEngine not initialized. clientId has not been configured.");
+        }
+        if (!('gapi' in window)) {
+          throw new Error("AuthEngine not initialized. gapi has not loaded.");
+        }
+        if (!('auth2' in window.gapi)) {
+          throw new Error("AuthEngine not initialized. auth2 not loaded.");
+        }
+      },
+
+      /** pops up sign-in dialog */
+      signIn: function() {
+        this.assertAuthInitialized();
+        var params = {
+          'scope': this.getMissingScopes()
+        };
+
+        // Proxy specific attributes through to the signIn options.
+        Object.keys(ProxyLoginAttributes).forEach(function(key) {
+          if (this[key] && this[key] !== '') {
+            params[ProxyLoginAttributes[key]] = this[key];
+          }
+        }, this);
+
+        var promise;
+        var user = gapi.auth2.getAuthInstance()['currentUser'].get();
+        if (!(this.offline || this.offlineAlwaysPrompt)) {
+          if (user.getGrantedScopes()) {
+            // additional auth, skip multiple account dialog
+            promise = user.grant(params);
+          } else {
+            // initial signin
+            promise = gapi.auth2.getAuthInstance().signIn(params);
+          }
+        } else {
+          params.redirect_uri = 'postmessage';
+          if (this.offlineAlwaysPrompt) {
+            params.approval_prompt = 'force';
+          }
+
+          // Despite being documented at https://goo.gl/tiO0Bk
+          // It doesn't seem like user.grantOfflineAccess() actually exists in
+          // the current version of the Google Sign-In JS client we're using
+          // through GoogleWebComponents. So in the offline case, we will not
+          // distinguish between a first auth and an additional one.
+          promise = gapi.auth2.getAuthInstance().grantOfflineAccess(params);
+        }
+        promise.then(
+          function onFulfilled(response) {
+            // If login was offline, response contains one string "code"
+            // Otherwise it contains the user object already
+            var newUser;
+            if (response.code) {
+              AuthEngine.offlineGranted = true;
+              newUser = gapi.auth2.getAuthInstance()['currentUser'].get();
+              AuthEngine.setOfflineCode(response.code);
+            } else {
+              newUser = response;
+            }
+
+            var authResponse = newUser.getAuthResponse();
+            // Let the current user listener trigger the changes.
+          },
+          function onRejected(error) {
+            // Access denied is not an error, user hit cancel
+            if ("Access denied." !== error.reason) {
+              this.signinAwares.forEach(function(awareInstance) {
+                awareInstance.errorNotify(error);
+              });
+            }
+          }.bind(this)
+        );
+      },
+
+      /** signs user out */
+      signOut: function() {
+        this.assertAuthInitialized();
+        gapi.auth2.getAuthInstance().signOut().then(
+          function onFulfilled() {
+          // Let the current user listener trigger the changes.
+          },
+          function onRejected(error) {
+            console.error(error);
+          }
+        );
+      }
+    };
+
+    AuthEngine.init();
+
+/**
+`google-signin-aware` is used to enable authentication in custom elements by
+interacting with a google-signin element that needs to be present somewhere
+on the page.
+
+The `scopes` attribute allows you to specify which scope permissions are required
+(e.g do you want to allow interaction with the Google Drive API).
+
+The `google-signin-aware-success` event is triggered when a user successfully
+authenticates. If either `offline` or `offlineAlwaysPrompt` is set to true, successful
+authentication will also trigger the `google-signin-offline-success`event.
+The `google-signin-aware-signed-out` event is triggered when a user explicitly
+signs out via the google-signin element.
+
+You can bind to `isAuthorized` property to monitor authorization state.
+##### Example
+
+    <google-signin-aware scopes="https://www.googleapis.com/auth/drive"></google-signin-aware>
+
+
+##### Example with offline
+    <template id="awareness" is="dom-bind">
+      <google-signin-aware
+          scopes="https://www.googleapis.com/auth/drive"
+          offline
+          on-google-signin-aware-success="handleSignin"
+          on-google-signin-offline-success="handleOffline"></google-signin-aware>
+    <\/template>
+    <script>
+      var aware = document.querySelector('#awareness');
+      aware.handleSignin = function(response) {
+        var user = gapi.auth2.getAuthInstance()['currentUser'].get();
+        console.log('User name: ' + user.getBasicProfile().getName());
+      };
+      aware.handleOffline = function(response) {
+        console.log('Offline code received: ' + response.detail.code);
+        // Here you would POST response.detail.code to your webserver, which can
+        // exchange the authorization code for an access token. More info at:
+        // https://developers.google.com/identity/protocols/OAuth2WebServer
+      };
+    <\/script>
+*/
+    Polymer({
+
+      is: 'google-signin-aware',
+
+      /**
+       * Fired when this scope has been authorized
+       * @param {Object} result Authorization result.
+       * @event google-signin-aware-success
+       */
+
+      /**
+       * Fired when an offline authorization is successful.
+       * @param {{code: string}} detail -
+       *     code: The one-time authorization code from Google.
+       *         Your application can exchange this for an `access_token` and `refresh_token`
+       * @event google-signin-offline-success
+       */
+
+      /**
+       * Fired when this scope is not authorized
+       * @event google-signin-aware-signed-out
+       */
+
+      /**
+       * Fired when there is an error during the signin flow.
+       * @param {Object} detail The error object returned from the OAuth 2 flow.
+       * @event google-signin-aware-error
+       */
+
+      /**
+       * This block is needed so the previous @param is not assigned to the next property.
+       */
+
+      properties: {
+        /**
+         * App package name for android over-the-air installs.
+         * See the relevant [docs](https://developers.google.com/+/web/signin/android-app-installs)
+         */
+        appPackageName: {
+          type: String,
+          observer: '_appPackageNameChanged'
+        },
+
+        /**
+         * a Google Developers clientId reference
+         */
+        clientId: {
+          type: String,
+          observer: '_clientIdChanged'
+        },
+
+        /**
+         * The cookie policy defines what URIs have access to the session cookie
+         * remembering the user's sign-in state.
+         * See the relevant [docs](https://developers.google.com/+/web/signin/reference#determining_a_value_for_cookie_policy) for more information.
+         * @default 'single_host_origin'
+         */
+        cookiePolicy: {
+          type: String,
+          observer: '_cookiePolicyChanged'
+        },
+
+        /**
+         * The app activity types you want to write on behalf of the user
+         * (e.g http://schemas.google.com/AddActivity)
+         *
+         */
+        requestVisibleActions: {
+          type: String,
+          observer: '_requestVisibleActionsChanged'
+        },
+
+        /**
+         * The Google Apps domain to which users must belong to sign in.
+         * See the relevant [docs](https://developers.google.com/identity/sign-in/web/reference) for more information.
+         */
+        hostedDomain: {
+          type: String,
+          observer: '_hostedDomainChanged'
+        },
+
+       /**
+         * Allows for offline `access_token` retrieval during the signin process.
+         * See also `offlineAlwaysPrompt`. You only need to set one of the two; if both
+         * are set, the behavior of `offlineAlwaysPrompt` will override `offline`.
+         */
+        offline: {
+          type: Boolean,
+          value: false,
+          observer: '_offlineChanged'
+        },
+
+        /**
+          * Works the same as `offline` with the addition that it will always
+          * force a re-prompt to the user, guaranteeing that you will get a
+          * refresh_token even if the user has already granted offline access to
+          * this application. You only need to set one of `offline` or
+          * `offlineAlwaysPrompt`, not both.
+          */
+        offlineAlwaysPrompt: {
+          type: Boolean,
+          value: false,
+          observer: '_offlineAlwaysPromptChanged'
+        },
+
+       /**
+         * The scopes to provide access to (e.g https://www.googleapis.com/auth/drive)
+         * and should be space-delimited.
+         */
+        scopes: {
+          type: String,
+          value: 'profile',
+          observer: '_scopesChanged'
+        },
+
+        /**
+         * Space-delimited, case-sensitive list of strings that
+         * specifies whether the the user is prompted for reauthentication
+         * and/or consent. The defined values are:
+         *   none: do not display authentication or consent pages.
+         *     This value is mutually exclusive with the rest.
+         *   login: always prompt the user for reauthentication.
+         *   consent: always show consent screen.
+         *   select_account: always show account selection page.
+         *     This enables a user who has multiple accounts to select amongst
+         *     the multiple accounts that they might have current sessions for.
+         * For more information, see "prompt" parameter description in
+         * https://openid.net/specs/openid-connect-basic-1_0.html#RequestParameters
+         */
+        openidPrompt: {
+          type: String,
+          value: '',
+          observer: '_openidPromptChanged'
+        },
+
+        /**
+         * True when the auth library has been initialized, and signedIn property value is set from the first api response.
+         */
+        initialized: {
+            type: Boolean,
+            notify: true,
+            readOnly: true
+        },
+
+        /**
+         * True if user is signed in
+         */
+        signedIn: {
+          type: Boolean,
+          notify: true,
+          readOnly: true
+        },
+
+        /**
+         * True if authorizations for *this* element have been granted
+         */
+        isAuthorized: {
+          type: Boolean,
+          notify: true,
+          readOnly: true,
+          value: false
+        },
+
+        /**
+         * True if additional authorizations for *any* element are required
+         */
+        needAdditionalAuth: {
+          type: Boolean,
+          notify: true,
+          readOnly: true
+        },
+
+        /**
+         * True if *any* element has google+ scopes
+         */
+        hasPlusScopes: {
+          type: Boolean,
+          value: false,
+          notify: true,
+          readOnly: true
+        }
+      },
+
+      attached: function() {
+        AuthEngine.attachSigninAware(this);
+      },
+
+      detached: function() {
+        AuthEngine.detachSigninAware(this);
+      },
+
+      /** pops up the authorization dialog */
+      signIn: function() {
+        AuthEngine.signIn();
+      },
+
+      /** signs user out */
+      signOut: function() {
+        AuthEngine.signOut();
+      },
+
+      errorNotify: function(error) {
+        this.fire('google-signin-aware-error', error);
+      },
+
+      _appPackageNameChanged: function(newName, oldName) {
+        AuthEngine.appPackageName = newName;
+      },
+
+      _clientIdChanged: function(newId, oldId) {
+        AuthEngine.clientId = newId;
+      },
+
+      _cookiePolicyChanged: function(newPolicy, oldPolicy) {
+        AuthEngine.cookiePolicy = newPolicy;
+      },
+
+      _requestVisibleActionsChanged: function(newVal, oldVal) {
+        AuthEngine.requestVisibleActions = newVal;
+      },
+
+      _hostedDomainChanged: function(newVal, oldVal) {
+        AuthEngine.hostedDomain = newVal;
+      },
+
+      _offlineChanged: function(newVal, oldVal) {
+        AuthEngine.offline = newVal;
+      },
+
+      _offlineAlwaysPromptChanged: function(newVal, oldVal) {
+        AuthEngine.offlineAlwaysPrompt = newVal;
+      },
+
+      _scopesChanged: function(newVal, oldVal) {
+        AuthEngine.requestScopes(newVal);
+        this._updateScopeStatus(undefined);
+      },
+
+      _openidPromptChanged: function(newVal, oldVal) {
+        AuthEngine.openidPrompt = newVal;
+      },
+
+      _updateScopeStatus: function(user) {
+        var newAuthorized = this.signedIn && AuthEngine.hasGrantedScopes(this.scopes);
+        if (newAuthorized !== this.isAuthorized) {
+          this._setIsAuthorized(newAuthorized);
+          if (newAuthorized) {
+            this.fire('google-signin-aware-success', user);
+          }
+          else {
+            this.fire('google-signin-aware-signed-out', user);
+          }
+        }
+      },
+
+      _updateOfflineCode: function(code) {
+        if (code) {
+          this.fire('google-signin-offline-success', {code: code});
+        }
+      }
+    });
+  })();
+</script>