text: 
  /*
   * Copyright 2016 Red Hat, Inc. and/or its affiliates
   * and other contributors as indicated by the @author tags.
   *
   * 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
   *
   * http://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.
   */
  
  /* eslint-disable indent */
  (function (window, undefined) {
      var Keycloak = function (config) {
          if (!(this instanceof Keycloak)) {
              return new Keycloak(config)
          }
  
          var kc = this
          var adapter
          var refreshQueue = []
          var callbackStorage
  
          var loginIframe = {
              enable: true,
              callbackList: [],
              interval: 5
          }
  
          var scripts = document.getElementsByTagName('script')
          for (var i = 0; i < scripts.length; i++) {
              if ((scripts[i].src.indexOf('keycloak.js') !== -1 || scripts[i].src.indexOf('keycloak.min.js') !== -1) && scripts[i].src.indexOf('version=') !== -1) {
                  kc.iframeVersion = scripts[i].src.substring(scripts[i].src.indexOf('version=') + 8).split('&')[0]
              }
          }
  
          var useNonce = true
  
          kc.init = function (initOptions) {
              kc.authenticated = false
  
              callbackStorage = createCallbackStorage()
              var adapters = ['default', 'cordova', 'cordova-native']
  
              if (initOptions && adapters.indexOf(initOptions.adapter) > -1) {
                  adapter = loadAdapter(initOptions.adapter)
              } else if (initOptions && typeof initOptions.adapter === 'object') {
                  adapter = initOptions.adapter
              } else {
                  if (window.Cordova || window.cordova) {
                      adapter = loadAdapter('cordova')
                  } else {
                      adapter = loadAdapter()
                  }
              }
  
              if (initOptions) {
                  if (typeof initOptions.useNonce !== 'undefined') {
                      useNonce = initOptions.useNonce
                  }
  
                  if (typeof initOptions.checkLoginIframe !== 'undefined') {
                      loginIframe.enable = initOptions.checkLoginIframe
                  }
  
                  if (initOptions.checkLoginIframeInterval) {
                      loginIframe.interval = initOptions.checkLoginIframeInterval
                  }
  
                  if (initOptions.promiseType === 'native') {
                      kc.useNativePromise = typeof Promise === 'function'
                  } else {
                      kc.useNativePromise = false
                  }
  
                  if (initOptions.onLoad === 'login-required') {
                      kc.loginRequired = true
                  }
  
                  if (initOptions.responseMode) {
                      if (initOptions.responseMode === 'query' || initOptions.responseMode === 'fragment') {
                          kc.responseMode = initOptions.responseMode
                      } else {
                          throw 'Invalid value for responseMode'
                      }
                  }
  
                  if (initOptions.flow) {
                      switch (initOptions.flow) {
                          case 'standard':
                              kc.responseType = 'code'
                              break
                          case 'implicit':
                              kc.responseType = 'id_token token'
                              break
                          case 'hybrid':
                              kc.responseType = 'code id_token token'
                              break
                          default:
                              throw 'Invalid value for flow'
                      }
                      kc.flow = initOptions.flow
                  }
  
                  if (initOptions.timeSkew != null) {
                      kc.timeSkew = initOptions.timeSkew
                  }
  
                  if (initOptions.redirectUri) {
                      kc.redirectUri = initOptions.redirectUri
                  }
              }
  
              if (!kc.responseMode) {
                  kc.responseMode = 'fragment'
              }
              if (!kc.responseType) {
                  kc.responseType = 'code'
                  kc.flow = 'standard'
              }
  
              var promise = createPromise(false)
  
              var initPromise = createPromise(true)
              initPromise.promise.success(function () {
                  kc.onReady && kc.onReady(kc.authenticated)
                  promise.setSuccess(kc.authenticated)
              }).error(function (errorData) {
                  promise.setError(errorData)
              })
  
              var configPromise = loadConfig(config)
  
              function onLoad () {
                  var doLogin = function (prompt) {
                      if (!prompt) {
                          options.prompt = 'none'
                      }
                      kc.login(options).success(function () {
                          initPromise.setSuccess()
                      }).error(function () {
                          initPromise.setError()
                      })
                  }
  
                  var options = {}
                  switch (initOptions.onLoad) {
                      case 'check-sso':
                          if (loginIframe.enable) {
                              setupCheckLoginIframe().success(function () {
                                  checkLoginIframe().success(function (unchanged) {
                                      if (!unchanged) {
                                          doLogin(false)
                                      } else {
                                          initPromise.setSuccess()
                                      }
                                  }).error(function () {
                                      initPromise.setError()
                                  })
                              })
                          } else {
                              doLogin(false)
                          }
                          break
                      case 'login-required':
                          doLogin(true)
                          break
                      default:
                          throw 'Invalid value for onLoad'
                  }
              }
  
              function processInit () {
                  var callback = parseCallback(window.location.href)
  
                  if (callback) {
                      window.history.replaceState(window.history.state, null, callback.newUrl)
                  }
  
                  if (callback && callback.valid) {
                      return setupCheckLoginIframe().success(function () {
                          processCallback(callback, initPromise)
                      }).error(function (e) {
                          initPromise.setError()
                      })
                  } else if (initOptions) {
                      if (initOptions.token && initOptions.refreshToken) {
                          setToken(initOptions.token, initOptions.refreshToken, initOptions.idToken)
  
                          if (loginIframe.enable) {
                              setupCheckLoginIframe().success(function () {
                                  checkLoginIframe().success(function (unchanged) {
                                      if (unchanged) {
                                          kc.onAuthSuccess && kc.onAuthSuccess()
                                          initPromise.setSuccess()
                                          scheduleCheckIframe()
                                      } else {
                                          initPromise.setSuccess()
                                      }
                                  }).error(function () {
                                      initPromise.setError()
                                  })
                              })
                          } else {
                              kc.updateToken(-1).success(function () {
                                  kc.onAuthSuccess && kc.onAuthSuccess()
                                  initPromise.setSuccess()
                              }).error(function () {
                                  kc.onAuthError && kc.onAuthError()
                                  if (initOptions.onLoad) {
                                      onLoad()
                                  } else {
                                      initPromise.setError()
                                  }
                              })
                          }
                      } else if (initOptions.onLoad) {
                          onLoad()
                      } else {
                          initPromise.setSuccess()
                      }
                  } else {
                      initPromise.setSuccess()
                  }
              }
  
              configPromise.success(processInit)
              configPromise.error(function () {
                  promise.setError()
              })
  
              return promise.promise
          }
  
          kc.login = function (options) {
              return adapter.login(options)
          }
  
          kc.createLoginUrl = function (options) {
              var state = createUUID()
              var nonce = createUUID()
  
              var redirectUri = adapter.redirectUri(options)
  
              var callbackState = {
                  state: state,
                  nonce: nonce,
                  redirectUri: encodeURIComponent(redirectUri)
              }
  
              if (options && options.prompt) {
                  callbackState.prompt = options.prompt
              }
  
              callbackStorage.add(callbackState)
  
              var baseUrl
              if (options && options.action == 'register') {
                  baseUrl = kc.endpoints.register()
              } else {
                  baseUrl = kc.endpoints.authorize()
              }
  
              var scope
              if (options && options.scope) {
                  if (options.scope.indexOf('openid') != -1) {
                      scope = options.scope
                  } else {
                      scope = 'openid ' + options.scope
                  }
              } else {
                  scope = 'openid'
              }
  
              var url = baseUrl +
                  '?client_id=' + encodeURIComponent(kc.clientId) +
                  '&redirect_uri=' + encodeURIComponent(redirectUri) +
                  '&state=' + encodeURIComponent(state) +
                  '&response_mode=' + encodeURIComponent(kc.responseMode) +
                  '&response_type=' + encodeURIComponent(kc.responseType) +
                  '&scope=' + encodeURIComponent(scope)
              if (useNonce) {
                  url = url + '&nonce=' + encodeURIComponent(nonce)
              }
  
              if (options && options.prompt) {
                  url += '&prompt=' + encodeURIComponent(options.prompt)
              }
  
              if (options && options.maxAge) {
                  url += '&max_age=' + encodeURIComponent(options.maxAge)
              }
  
              if (options && options.loginHint) {
                  url += '&login_hint=' + encodeURIComponent(options.loginHint)
              }
  
              if (options && options.idpHint) {
                  url += '&kc_idp_hint=' + encodeURIComponent(options.idpHint)
              }
  
              if (options && options.locale) {
                  url += '&ui_locales=' + encodeURIComponent(options.locale)
              }
  
              if (options && options.kcLocale) {
                  url += '&kc_locale=' + encodeURIComponent(options.kcLocale)
              }
  
              return url
          }
  
          kc.logout = function (options) {
              return adapter.logout(options)
          }
  
          kc.createLogoutUrl = function (options) {
              var url = kc.endpoints.logout() +
                  '?redirect_uri=' + encodeURIComponent(adapter.redirectUri(options, false))
  
              return url
          }
  
          kc.register = function (options) {
              return adapter.register(options)
          }
  
          kc.createRegisterUrl = function (options) {
              if (!options) {
                  options = {}
              }
              options.action = 'register'
              return kc.createLoginUrl(options)
          }
  
          kc.createAccountUrl = function (options) {
              var realm = getRealmUrl()
              var url = undefined
              if (typeof realm !== 'undefined') {
                  url = realm +
                      '/account' +
                      '?referrer=' + encodeURIComponent(kc.clientId) +
                      '&referrer_uri=' + encodeURIComponent(adapter.redirectUri(options))
              }
              return url
          }
  
          kc.accountManagement = function () {
              return adapter.accountManagement()
          }
  
          kc.hasRealmRole = function (role) {
              var access = kc.realmAccess
              return !!access && access.roles.indexOf(role) >= 0
          }
  
          kc.hasResourceRole = function (role, resource) {
              if (!kc.resourceAccess) {
                  return false
              }
  
              var access = kc.resourceAccess[resource || kc.clientId]
              return !!access && access.roles.indexOf(role) >= 0
          }
  
          kc.loadUserProfile = function () {
              var url = getRealmUrl() + '/account'
              var req = new XMLHttpRequest()
              req.open('GET', url, true)
              req.setRequestHeader('Accept', 'application/json')
              req.setRequestHeader('Authorization', 'bearer ' + kc.token)
  
              var promise = createPromise(false)
  
              req.onreadystatechange = function () {
                  if (req.readyState == 4) {
                      if (req.status == 200) {
                          kc.profile = JSON.parse(req.responseText)
                          promise.setSuccess(kc.profile)
                      } else {
                          promise.setError()
                      }
                  }
              }
  
              req.send()
  
              return promise.promise
          }
  
          kc.loadUserInfo = function () {
              var url = kc.endpoints.userinfo()
              var req = new XMLHttpRequest()
              req.open('GET', url, true)
              req.setRequestHeader('Accept', 'application/json')
              req.setRequestHeader('Authorization', 'bearer ' + kc.token)
  
              var promise = createPromise(false)
  
              req.onreadystatechange = function () {
                  if (req.readyState == 4) {
                      if (req.status == 200) {
                          kc.userInfo = JSON.parse(req.responseText)
                          promise.setSuccess(kc.userInfo)
                      } else {
                          promise.setError()
                      }
                  }
              }
  
              req.send()
  
              return promise.promise
          }
  
          kc.isTokenExpired = function (minValidity) {
              if (!kc.tokenParsed || (!kc.refreshToken && kc.flow != 'implicit')) {
                  throw 'Not authenticated'
              }
  
              if (kc.timeSkew == null) {
                  console.info('[KEYCLOAK] Unable to determine if token is expired as timeskew is not set')
                  return true
              }
  
              var expiresIn = kc.tokenParsed.exp - Math.ceil(new Date().getTime() / 1000) + kc.timeSkew
              if (minValidity) {
                  expiresIn -= minValidity
              }
              return expiresIn < 0
          }
  
          kc.updateToken = function (minValidity) {
              var promise = createPromise(false)
  
              if (!kc.refreshToken) {
                  promise.setError()
                  return promise.promise
              }
  
              minValidity = minValidity || 5
  
              var exec = function () {
                  var refreshToken = false
                  if (minValidity == -1) {
                      refreshToken = true
                      console.info('[KEYCLOAK] Refreshing token: forced refresh')
                  } else if (!kc.tokenParsed || kc.isTokenExpired(minValidity)) {
                      refreshToken = true
                      console.info('[KEYCLOAK] Refreshing token: token expired')
                  }
  
                  if (!refreshToken) {
                      promise.setSuccess(false)
                  } else {
                      var params = 'grant_type=refresh_token&' + 'refresh_token=' + kc.refreshToken
                      var url = kc.endpoints.token()
  
                      refreshQueue.push(promise)
  
                      if (refreshQueue.length == 1) {
                          var req = new XMLHttpRequest()
                          req.open('POST', url, true)
                          req.setRequestHeader('Content-type', 'application/x-www-form-urlencoded')
                          req.withCredentials = true
  
                          if (kc.clientId && kc.clientSecret) {
                              req.setRequestHeader('Authorization', 'Basic ' + btoa(kc.clientId + ':' + kc.clientSecret))
                          } else {
                              params += '&client_id=' + encodeURIComponent(kc.clientId)
                          }
  
                          var timeLocal = new Date().getTime()
  
                          req.onreadystatechange = function () {
                              if (req.readyState == 4) {
                                  if (req.status == 200) {
                                      console.info('[KEYCLOAK] Token refreshed')
  
                                      timeLocal = (timeLocal + new Date().getTime()) / 2
  
                                      var tokenResponse = JSON.parse(req.responseText)
  
                                      setToken(tokenResponse.access_token, tokenResponse.refresh_token, tokenResponse.id_token, timeLocal)
  
                                      kc.onAuthRefreshSuccess && kc.onAuthRefreshSuccess()
                                      for (var p = refreshQueue.pop(); p != null; p = refreshQueue.pop()) {
                                          p.setSuccess(true)
                                      }
                                  } else {
                                      console.warn('[KEYCLOAK] Failed to refresh token')
  
                                      if (req.status == 400) {
                                          kc.clearToken()
                                      }
  
                                      kc.onAuthRefreshError && kc.onAuthRefreshError()
                                      for (var p = refreshQueue.pop(); p != null; p = refreshQueue.pop()) {
                                          p.setError(true)
                                      }
                                  }
                              }
                          }
  
                          req.send(params)
                      }
                  }
              }
  
              if (loginIframe.enable) {
                  var iframePromise = checkLoginIframe()
                  iframePromise.success(function () {
                      exec()
                  }).error(function () {
                      promise.setError()
                  })
              } else {
                  exec()
              }
  
              return promise.promise
          }
  
          kc.clearToken = function () {
              if (kc.token) {
                  setToken(null, null, null)
                  kc.onAuthLogout && kc.onAuthLogout()
                  if (kc.loginRequired) {
                      kc.login()
                  }
              }
          }
  
          function getRealmUrl () {
              if (typeof kc.authServerUrl !== 'undefined') {
                  if (kc.authServerUrl.charAt(kc.authServerUrl.length - 1) == '/') {
                      return kc.authServerUrl + 'realms/' + encodeURIComponent(kc.realm)
                  } else {
                      return kc.authServerUrl + '/realms/' + encodeURIComponent(kc.realm)
                  }
              } else {
                  return undefined
              }
          }
  
          function getOrigin () {
              if (!window.location.origin) {
                  return window.location.protocol + '//' + window.location.hostname + (window.location.port ? ':' + window.location.port : '')
              } else {
                  return window.location.origin
              }
          }
  
          function processCallback (oauth, promise) {
              var code = oauth.code
              var error = oauth.error
              var prompt = oauth.prompt
  
              var timeLocal = new Date().getTime()
  
              if (error) {
                  if (prompt != 'none') {
                      var errorData = { error: error, error_description: oauth.error_description }
                      kc.onAuthError && kc.onAuthError(errorData)
                      promise && promise.setError(errorData)
                  } else {
                      promise && promise.setSuccess()
                  }
                  return
              } else if ((kc.flow != 'standard') && (oauth.access_token || oauth.id_token)) {
                  authSuccess(oauth.access_token, null, oauth.id_token, true)
              }
  
              if ((kc.flow != 'implicit') && code) {
                  var params = 'code=' + code + '&grant_type=authorization_code'
                  var url = kc.endpoints.token()
  
                  var req = new XMLHttpRequest()
                  req.open('POST', url, true)
                  req.setRequestHeader('Content-type', 'application/x-www-form-urlencoded')
  
                  if (kc.clientId && kc.clientSecret) {
                      req.setRequestHeader('Authorization', 'Basic ' + btoa(kc.clientId + ':' + kc.clientSecret))
                  } else {
                      params += '&client_id=' + encodeURIComponent(kc.clientId)
                  }
  
                  params += '&redirect_uri=' + oauth.redirectUri
  
                  req.withCredentials = true
  
                  req.onreadystatechange = function () {
                      if (req.readyState == 4) {
                          if (req.status == 200) {
                              var tokenResponse = JSON.parse(req.responseText)
                              authSuccess(tokenResponse.access_token, tokenResponse.refresh_token, tokenResponse.id_token, kc.flow === 'standard')
                              scheduleCheckIframe()
                          } else {
                              kc.onAuthError && kc.onAuthError()
                              promise && promise.setError()
                          }
                      }
                  }
  
                  req.send(params)
              }
  
              function authSuccess (accessToken, refreshToken, idToken, fulfillPromise) {
                  timeLocal = (timeLocal + new Date().getTime()) / 2
  
                  setToken(accessToken, refreshToken, idToken, timeLocal)
  
                  if (useNonce && ((kc.tokenParsed && kc.tokenParsed.nonce != oauth.storedNonce) ||
                      (kc.refreshTokenParsed && kc.refreshTokenParsed.nonce != oauth.storedNonce) ||
                      (kc.idTokenParsed && kc.idTokenParsed.nonce != oauth.storedNonce))) {
                      console.info('[KEYCLOAK] Invalid nonce, clearing token')
                      kc.clearToken()
                      promise && promise.setError()
                  } else {
                      if (fulfillPromise) {
                          kc.onAuthSuccess && kc.onAuthSuccess()
                          promise && promise.setSuccess()
                      }
                  }
              }
          }
  
          function loadConfig (url) {
              var promise = createPromise(true)
              var configUrl
  
              if (!config) {
                  // configUrl = 'keycloak.json';
                  configUrl = 'static/keycloak/keycloak.json'
              } else if (typeof config === 'string') {
                  configUrl = config
              }
  
              function setupOidcEndoints (oidcConfiguration) {
                  if (!oidcConfiguration) {
                      kc.endpoints = {
                          authorize: function () {
                              return getRealmUrl() + '/protocol/openid-connect/auth'
                          },
                          token: function () {
                              return getRealmUrl() + '/protocol/openid-connect/token'
                          },
                          logout: function () {
                              return getRealmUrl() + '/protocol/openid-connect/logout'
                          },
                          checkSessionIframe: function () {
                              var src = getRealmUrl() + '/protocol/openid-connect/login-status-iframe.html'
                              if (kc.iframeVersion) {
                                  src = src + '?version=' + kc.iframeVersion
                              }
                              return src
                          },
                          register: function () {
                              return getRealmUrl() + '/protocol/openid-connect/registrations'
                          },
                          userinfo: function () {
                              return getRealmUrl() + '/protocol/openid-connect/userinfo'
                          }
                      }
                  } else {
                      kc.endpoints = {
                          authorize: function () {
                              return oidcConfiguration.authorization_endpoint
                          },
                          token: function () {
                              return oidcConfiguration.token_endpoint
                          },
                          logout: function () {
                              if (!oidcConfiguration.end_session_endpoint) {
                                  throw 'Not supported by the OIDC server'
                              }
                              return oidcConfiguration.end_session_endpoint
                          },
                          checkSessionIframe: function () {
                              if (!oidcConfiguration.check_session_iframe) {
                                  throw 'Not supported by the OIDC server'
                              }
                              return oidcConfiguration.check_session_iframe
                          },
                          register: function () {
                              throw 'Redirection to "Register user" page not supported in standard OIDC mode'
                          },
                          userinfo: function () {
                              if (!oidcConfiguration.userinfo_endpoint) {
                                  throw 'Not supported by the OIDC server'
                              }
                              return oidcConfiguration.userinfo_endpoint
                          }
                      }
                  }
              }
  
              if (configUrl) {
                  var req = new XMLHttpRequest()
                  req.open('GET', configUrl, true)
                  req.setRequestHeader('Accept', 'application/json')
  
                  req.onreadystatechange = function () {
                      if (req.readyState == 4) {
                          if (req.status == 200 || fileLoaded(req)) {
                              var config = JSON.parse(req.responseText)
  
                              kc.authServerUrl = config['auth-server-url']
                              kc.realm = config.realm
                              kc.clientId = config.resource
                              kc.clientSecret = (config.credentials || {}).secret
                              setupOidcEndoints(null)
                              promise.setSuccess()
                          } else {
                              promise.setError()
                          }
                      }
                  }
  
                  req.send()
              } else {
                  if (!config.clientId) {
                      throw 'clientId missing'
                  }
  
                  kc.clientId = config.clientId
                  kc.clientSecret = (config.credentials || {}).secret
  
                  var oidcProvider = config.oidcProvider
                  if (!oidcProvider) {
                      if (!config.url) {
                          var scripts = document.getElementsByTagName('script')
                          for (var i = 0; i < scripts.length; i++) {
                              if (scripts[i].src.match(/.*keycloak\.js/)) {
                                  config.url = scripts[i].src.substr(0, scripts[i].src.indexOf('/js/keycloak.js'))
                                  break
                              }
                          }
                      }
                      if (!config.realm) {
                          throw 'realm missing'
                      }
  
                      kc.authServerUrl = config.url
                      kc.realm = config.realm
                      setupOidcEndoints(null)
                      promise.setSuccess()
                  } else {
                      if (typeof oidcProvider === 'string') {
                          var oidcProviderConfigUrl
                          if (oidcProvider.charAt(oidcProvider.length - 1) == '/') {
                              oidcProviderConfigUrl = oidcProvider + '.well-known/openid-configuration'
                          } else {
                              oidcProviderConfigUrl = oidcProvider + '/.well-known/openid-configuration'
                          }
                          var req = new XMLHttpRequest()
                          req.open('GET', oidcProviderConfigUrl, true)
                          req.setRequestHeader('Accept', 'application/json')
  
                          req.onreadystatechange = function () {
                              if (req.readyState == 4) {
                                  if (req.status == 200 || fileLoaded(req)) {
                                      var oidcProviderConfig = JSON.parse(req.responseText)
                                      setupOidcEndoints(oidcProviderConfig)
                                      promise.setSuccess()
                                  } else {
                                      promise.setError()
                                  }
                              }
                          }
  
                          req.send()
                      } else {
                          setupOidcEndoints(oidcProvider)
                          promise.setSuccess()
                      }
                  }
              }
  
              return promise.promise
          }
  
          function fileLoaded (xhr) {
              return xhr.status == 0 && xhr.responseText && xhr.responseURL.startsWith('file:')
          }
  
          function setToken (token, refreshToken, idToken, timeLocal) {
              if (kc.tokenTimeoutHandle) {
                  clearTimeout(kc.tokenTimeoutHandle)
                  kc.tokenTimeoutHandle = null
              }
  
              if (refreshToken) {
                  kc.refreshToken = refreshToken
                  kc.refreshTokenParsed = decodeToken(refreshToken)
              } else {
                  delete kc.refreshToken
                  delete kc.refreshTokenParsed
              }
  
              if (idToken) {
                  kc.idToken = idToken
                  kc.idTokenParsed = decodeToken(idToken)
              } else {
                  delete kc.idToken
                  delete kc.idTokenParsed
              }
  
              if (token) {
                  kc.token = token
                  kc.tokenParsed = decodeToken(token)
                  kc.sessionId = kc.tokenParsed.session_state
                  kc.authenticated = true
                  kc.subject = kc.tokenParsed.sub
                  kc.realmAccess = kc.tokenParsed.realm_access
                  kc.resourceAccess = kc.tokenParsed.resource_access
  
                  if (timeLocal) {
                      kc.timeSkew = Math.floor(timeLocal / 1000) - kc.tokenParsed.iat
                  }
  
                  if (kc.timeSkew != null) {
                      console.info('[KEYCLOAK] Estimated time difference between browser and server is ' + kc.timeSkew + ' seconds')
  
                      if (kc.onTokenExpired) {
                          var expiresIn = (kc.tokenParsed.exp - (new Date().getTime() / 1000) + kc.timeSkew) * 1000
                          console.info('[KEYCLOAK] Token expires in ' + Math.round(expiresIn / 1000) + ' s')
                          if (expiresIn <= 0) {
                              kc.onTokenExpired()
                          } else {
                              kc.tokenTimeoutHandle = setTimeout(kc.onTokenExpired, expiresIn)
                          }
                      }
                  }
              } else {
                  delete kc.token
                  delete kc.tokenParsed
                  delete kc.subject
                  delete kc.realmAccess
                  delete kc.resourceAccess
  
                  kc.authenticated = false
              }
          }
  
          function decodeToken (str) {
              str = str.split('.')[1]
  
              str = str.replace('/-/g', '+')
              str = str.replace('/_/g', '/')
              switch (str.length % 4) {
                  case 0:
                      break
                  case 2:
                      str += '=='
                      break
                  case 3:
                      str += '='
                      break
                  default:
                      throw 'Invalid token'
              }
  
              str = (str + '===').slice(0, str.length + (str.length % 4))
              str = str.replace(/-/g, '+').replace(/_/g, '/')
  
              str = decodeURIComponent(escape(atob(str)))
  
              str = JSON.parse(str)
              return str
          }
  
          function createUUID () {
              var s = []
              var hexDigits = '0123456789abcdef'
              for (var i = 0; i < 36; i++) {
                  s[i] = hexDigits.substr(Math.floor(Math.random() * 0x10), 1)
              }
              s[14] = '4'
              s[19] = hexDigits.substr((s[19] & 0x3) | 0x8, 1)
              s[8] = s[13] = s[18] = s[23] = '-'
              var uuid = s.join('')
              return uuid
          }
  
          kc.callback_id = 0
  
          function createCallbackId () {
              var id = '<id: ' + (kc.callback_id++) + (Math.random()) + '>'
              return id
          }
  
          function parseCallback (url) {
              var oauth = parseCallbackUrl(url)
              if (!oauth) {
                  return
              }
  
              var oauthState = callbackStorage.get(oauth.state)
  
              if (oauthState) {
                  oauth.valid = true
                  oauth.redirectUri = oauthState.redirectUri
                  oauth.storedNonce = oauthState.nonce
                  oauth.prompt = oauthState.prompt
              }
  
              return oauth
          }
  
          function parseCallbackUrl (url) {
              var supportedParams
              switch (kc.flow) {
                  case 'standard':
                      supportedParams = ['code', 'state', 'session_state']
                      break
                  case 'implicit':
                      supportedParams = ['access_token', 'token_type', 'id_token', 'state', 'session_state', 'expires_in']
                      break
                  case 'hybrid':
                      supportedParams = ['access_token', 'id_token', 'code', 'state', 'session_state']
                      break
              }
  
              supportedParams.push('error')
              supportedParams.push('error_description')
              supportedParams.push('error_uri')
  
              var queryIndex = url.indexOf('?')
              var fragmentIndex = url.indexOf('#')
  
              var newUrl
              var parsed
  
              if (kc.responseMode === 'query' && queryIndex !== -1) {
                  newUrl = url.substring(0, queryIndex)
                  parsed = parseCallbackParams(url.substring(queryIndex + 1, fragmentIndex !== -1 ? fragmentIndex : url.length), supportedParams)
                  if (parsed.paramsString !== '') {
                      newUrl += '?' + parsed.paramsString
                  }
                  if (fragmentIndex !== -1) {
                      newUrl += url.substring(fragmentIndex)
                  }
              } else if (kc.responseMode === 'fragment' && fragmentIndex !== -1) {
                  newUrl = url.substring(0, fragmentIndex)
                  parsed = parseCallbackParams(url.substring(fragmentIndex + 1), supportedParams)
                  if (parsed.paramsString !== '') {
                      newUrl += '#' + parsed.paramsString
                  }
              }
  
              if (parsed && parsed.oauthParams) {
                  if (kc.flow === 'standard' || kc.flow === 'hybrid') {
                      if ((parsed.oauthParams.code || parsed.oauthParams.error) && parsed.oauthParams.state) {
                          parsed.oauthParams.newUrl = newUrl
                          return parsed.oauthParams
                      }
                  } else if (kc.flow === 'implicit') {
                      if ((parsed.oauthParams.access_token || parsed.oauthParams.error) && parsed.oauthParams.state) {
                          parsed.oauthParams.newUrl = newUrl
                          return parsed.oauthParams
                      }
                  }
              }
          }
  
          function parseCallbackParams (paramsString, supportedParams) {
              var p = paramsString.split('&')
              var result = {
                  paramsString: '',
                  oauthParams: {}
              }
              for (var i = 0; i < p.length; i++) {
                  var t = p[i].split('=')
                  if (supportedParams.indexOf(t[0]) !== -1) {
                      result.oauthParams[t[0]] = t[1]
                  } else {
                      if (result.paramsString !== '') {
                          result.paramsString += '&'
                      }
                      result.paramsString += p[i]
                  }
              }
              return result
          }
  
          function createPromise (internal) {
              if (!internal && kc.useNativePromise) {
                  return createNativePromise()
              } else {
                  return createLegacyPromise()
              }
          }
  
          function createNativePromise () {
              // Need to create a native Promise which also preserves the
              // interface of the custom promise type previously used by the API
              var p = {
                  setSuccess: function (result) {
                      p.resolve(result)
                  },
  
                  setError: function (result) {
                      p.reject(result)
                  }
              }
              p.promise = new Promise(function (resolve, reject) {
                  p.resolve = resolve
                  p.reject = reject
              })
              return p
          }
  
          function createLegacyPromise () {
              var p = {
                  setSuccess: function (result) {
                      p.success = true
                      p.result = result
                      if (p.successCallback) {
                          p.successCallback(result)
                      }
                  },
  
                  setError: function (result) {
                      p.error = true
                      p.result = result
                      if (p.errorCallback) {
                          p.errorCallback(result)
                      }
                  },
  
                  promise: {
                      success: function (callback) {
                          if (p.success) {
                              callback(p.result)
                          } else if (!p.error) {
                              p.successCallback = callback
                          }
                          return p.promise
                      },
                      error: function (callback) {
                          if (p.error) {
                              callback(p.result)
                          } else if (!p.success) {
                              p.errorCallback = callback
                          }
                          return p.promise
                      }
                  }
              }
              return p
          }
  
          function setupCheckLoginIframe () {
              var promise = createPromise(true)
  
              if (!loginIframe.enable) {
                  promise.setSuccess()
                  return promise.promise
              }
  
              if (loginIframe.iframe) {
                  promise.setSuccess()
                  return promise.promise
              }
  
              var iframe = document.createElement('iframe')
              loginIframe.iframe = iframe
  
              iframe.onload = function () {
                  var authUrl = kc.endpoints.authorize()
                  if (authUrl.charAt(0) === '/') {
                      loginIframe.iframeOrigin = getOrigin()
                  } else {
                      loginIframe.iframeOrigin = authUrl.substring(0, authUrl.indexOf('/', 8))
                  }
                  promise.setSuccess()
              }
  
              var src = kc.endpoints.checkSessionIframe()
              iframe.setAttribute('src', src)
              iframe.setAttribute('title', 'keycloak-session-iframe')
              iframe.style.display = 'none'
              document.body.appendChild(iframe)
  
              var messageCallback = function (event) {
                  if ((event.origin !== loginIframe.iframeOrigin) || (loginIframe.iframe.contentWindow !== event.source)) {
                      return
                  }
  
                  if (!(event.data == 'unchanged' || event.data == 'changed' || event.data == 'error')) {
                      return
                  }
  
                  if (event.data != 'unchanged') {
                      kc.clearToken()
                  }
  
                  var callbacks = loginIframe.callbackList.splice(0, loginIframe.callbackList.length)
  
                  for (var i = callbacks.length - 1; i >= 0; --i) {
                      var promise = callbacks[i]
                      if (event.data == 'error') {
                          promise.setError()
                      } else {
                          promise.setSuccess(event.data == 'unchanged')
                      }
                  }
              }
  
              window.addEventListener('message', messageCallback, false)
  
              return promise.promise
          }
  
          function scheduleCheckIframe () {
              if (loginIframe.enable) {
                  if (kc.token) {
                      setTimeout(function () {
                          checkLoginIframe().success(function (unchanged) {
                              if (unchanged) {
                                  scheduleCheckIframe()
                              }
                          })
                      }, loginIframe.interval * 1000)
                  }
              }
          }
  
          function checkLoginIframe () {
              var promise = createPromise(true)
  
              if (loginIframe.iframe && loginIframe.iframeOrigin) {
                  var msg = kc.clientId + ' ' + (kc.sessionId ? kc.sessionId : '')
                  loginIframe.callbackList.push(promise)
                  var origin = loginIframe.iframeOrigin
                  if (loginIframe.callbackList.length == 1) {
                      loginIframe.iframe.contentWindow.postMessage(msg, origin)
                  }
              } else {
                  promise.setSuccess()
              }
  
              return promise.promise
          }
  
          function loadAdapter (type) {
              if (!type || type == 'default') {
                  return {
                      login: function (options) {
                          window.location.replace(kc.createLoginUrl(options))
                          return createPromise().promise
                      },
  
                      logout: function (options) {
                          window.location.replace(kc.createLogoutUrl(options))
                          return createPromise().promise
                      },
  
                      register: function (options) {
                          window.location.replace(kc.createRegisterUrl(options))
                          return createPromise().promise
                      },
  
                      accountManagement: function () {
                          var accountUrl = kc.createAccountUrl()
                          if (typeof accountUrl !== 'undefined') {
                              window.location.href = accountUrl
                          } else {
                              throw 'Not supported by the OIDC server'
                          }
                          return createPromise(false).promise
                      },
  
                      redirectUri: function (options, encodeHash) {
                          if (arguments.length == 1) {
                              encodeHash = true
                          }
  
                          if (options && options.redirectUri) {
                              return options.redirectUri
                          } else if (kc.redirectUri) {
                              return kc.redirectUri
                          } else {
                              return location.href
                          }
                      }
                  }
              }
  
              if (type == 'cordova') {
                  loginIframe.enable = false
                  var cordovaOpenWindowWrapper = function (loginUrl, target, options) {
                      if (window.cordova && window.cordova.InAppBrowser) {
                          // Use inappbrowser for IOS and Android if available
                          return window.cordova.InAppBrowser.open(loginUrl, target, options)
                      } else {
                          return window.open(loginUrl, target, options)
                      }
                  }
  
                  var shallowCloneCordovaOptions = function (userOptions) {
                      if (userOptions && userOptions.cordovaOptions) {
                          return Object.keys(userOptions.cordovaOptions).reduce(function (options, optionName) {
                              options[optionName] = userOptions.cordovaOptions[optionName]
                              return options
                          }, {})
                      } else {
                          return {}
                      }
                  }
  
                  var formatCordovaOptions = function (cordovaOptions) {
                      return Object.keys(cordovaOptions).reduce(function (options, optionName) {
                          options.push(optionName + '=' + cordovaOptions[optionName])
                          return options
                      }, []).join(',')
                  }
  
                  var createCordovaOptions = function (userOptions) {
                      var cordovaOptions = shallowCloneCordovaOptions(userOptions)
                      cordovaOptions.location = 'no'
                      if (userOptions && userOptions.prompt == 'none') {
                          cordovaOptions.hidden = 'yes'
                      }
                      return formatCordovaOptions(cordovaOptions)
                  }
  
                  return {
                      login: function (options) {
                          var promise = createPromise(false)
  
                          var cordovaOptions = createCordovaOptions(options)
                          var loginUrl = kc.createLoginUrl(options)
                          var ref = cordovaOpenWindowWrapper(loginUrl, '_blank', cordovaOptions)
                          var completed = false
  
                          var closed = false
                          var closeBrowser = function () {
                              closed = true
                              ref.close()
                          }
  
                          ref.addEventListener('loadstart', function (event) {
                              if (event.url.indexOf('http://localhost') == 0) {
                                  var callback = parseCallback(event.url)
                                  processCallback(callback, promise)
                                  closeBrowser()
                                  completed = true
                              }
                          })
  
                          ref.addEventListener('loaderror', function (event) {
                              if (!completed) {
                                  if (event.url.indexOf('http://localhost') == 0) {
                                      var callback = parseCallback(event.url)
                                      processCallback(callback, promise)
                                      closeBrowser()
                                      completed = true
                                  } else {
                                      promise.setError()
                                      closeBrowser()
                                  }
                              }
                          })
  
                          ref.addEventListener('exit', function (event) {
                              if (!closed) {
                                  promise.setError({
                                      reason: 'closed_by_user'
                                  })
                              }
                          })
  
                          return promise.promise
                      },
  
                      logout: function (options) {
                          var promise = createPromise(false)
  
                          var logoutUrl = kc.createLogoutUrl(options)
                          var ref = cordovaOpenWindowWrapper(logoutUrl, '_blank', 'location=no,hidden=yes')
  
                          var error
  
                          ref.addEventListener('loadstart', function (event) {
                              if (event.url.indexOf('http://localhost') == 0) {
                                  ref.close()
                              }
                          })
  
                          ref.addEventListener('loaderror', function (event) {
                              if (event.url.indexOf('http://localhost') == 0) {
                                  ref.close()
                              } else {
                                  error = true
                                  ref.close()
                              }
                          })
  
                          ref.addEventListener('exit', function (event) {
                              if (error) {
                                  promise.setError()
                              } else {
                                  kc.clearToken()
                                  promise.setSuccess()
                              }
                          })
  
                          return promise.promise
                      },
  
                      register: function () {
                          var registerUrl = kc.createRegisterUrl()
                          var cordovaOptions = createCordovaOptions(options)
                          var ref = cordovaOpenWindowWrapper(registerUrl, '_blank', cordovaOptions)
                          ref.addEventListener('loadstart', function (event) {
                              if (event.url.indexOf('http://localhost') == 0) {
                                  ref.close()
                              }
                          })
                      },
  
                      accountManagement: function () {
                          var accountUrl = kc.createAccountUrl()
                          if (typeof accountUrl !== 'undefined') {
                              var ref = cordovaOpenWindowWrapper(accountUrl, '_blank', 'location=no')
                              ref.addEventListener('loadstart', function (event) {
                                  if (event.url.indexOf('http://localhost') == 0) {
                                      ref.close()
                                  }
                              })
                          } else {
                              throw 'Not supported by the OIDC server'
                          }
                      },
  
                      redirectUri: function (options) {
                          return 'http://localhost'
                      }
                  }
              }
  
              if (type == 'cordova-native') {
                  loginIframe.enable = false
  
                  return {
                      login: function (options) {
                          var promise = createPromise(false)
                          var loginUrl = kc.createLoginUrl(options)
  
                          universalLinks.subscribe('keycloak', function (event) {
                              universalLinks.unsubscribe('keycloak')
                              window.cordova.plugins.browsertab.close()
                              var oauth = parseCallback(event.url)
                              processCallback(oauth, promise)
                          })
  
                          window.cordova.plugins.browsertab.openUrl(loginUrl)
                          return promise.promise
                      },
  
                      logout: function (options) {
                          var promise = createPromise(false)
                          var logoutUrl = kc.createLogoutUrl(options)
  
                          universalLinks.subscribe('keycloak', function (event) {
                              universalLinks.unsubscribe('keycloak')
                              window.cordova.plugins.browsertab.close()
                              kc.clearToken()
                              promise.setSuccess()
                          })
  
                          window.cordova.plugins.browsertab.openUrl(logoutUrl)
                          return promise.promise
                      },
  
                      register: function (options) {
                          var promise = createPromise(false)
                          var registerUrl = kc.createRegisterUrl(options)
                          universalLinks.subscribe('keycloak', function (event) {
                              universalLinks.unsubscribe('keycloak')
                              window.cordova.plugins.browsertab.close()
                              var oauth = parseCallback(event.url)
                              processCallback(oauth, promise)
                          })
                          window.cordova.plugins.browsertab.openUrl(registerUrl)
                          return promise.promise
                      },
  
                      accountManagement: function () {
                          var accountUrl = kc.createAccountUrl()
                          if (typeof accountUrl !== 'undefined') {
                              window.cordova.plugins.browsertab.openUrl(accountUrl)
                          } else {
                              throw 'Not supported by the OIDC server'
                          }
                      },
  
                      redirectUri: function (options) {
                          if (options && options.redirectUri) {
                              return options.redirectUri
                          } else if (kc.redirectUri) {
                              return kc.redirectUri
                          } else {
                              return 'http://localhost'
                          }
                      }
                  }
              }
  
              throw 'invalid adapter type: ' + type
          }
  
          var LocalStorage = function () {
              if (!(this instanceof LocalStorage)) {
                  return new LocalStorage()
              }
  
              localStorage.setItem('kc-test', 'test')
              localStorage.removeItem('kc-test')
  
              var cs = this
  
              function clearExpired () {
                  var time = new Date().getTime()
                  for (var i = 0; i < localStorage.length; i++) {
                      var key = localStorage.key(i)
                      if (key && key.indexOf('kc-callback-') == 0) {
                          var value = localStorage.getItem(key)
                          if (value) {
                              try {
                                  var expires = JSON.parse(value).expires
                                  if (!expires || expires < time) {
                                      localStorage.removeItem(key)
                                  }
                              } catch (err) {
                                  localStorage.removeItem(key)
                              }
                          }
                      }
                  }
              }
  
              cs.get = function (state) {
                  if (!state) {
                      return
                  }
  
                  var key = 'kc-callback-' + state
                  var value = localStorage.getItem(key)
                  if (value) {
                      localStorage.removeItem(key)
                      value = JSON.parse(value)
                  }
  
                  clearExpired()
                  return value
              }
  
              cs.add = function (state) {
                  clearExpired()
  
                  var key = 'kc-callback-' + state.state
                  state.expires = new Date().getTime() + (60 * 60 * 1000)
                  localStorage.setItem(key, JSON.stringify(state))
              }
          }
  
          var CookieStorage = function () {
              if (!(this instanceof CookieStorage)) {
                  return new CookieStorage()
              }
  
              var cs = this
  
              cs.get = function (state) {
                  if (!state) {
                      return
                  }
  
                  var value = getCookie('kc-callback-' + state)
                  setCookie('kc-callback-' + state, '', cookieExpiration(-100))
                  if (value) {
                      return JSON.parse(value)
                  }
              }
  
              cs.add = function (state) {
                  setCookie('kc-callback-' + state.state, JSON.stringify(state), cookieExpiration(60))
              }
  
              cs.removeItem = function (key) {
                  setCookie(key, '', cookieExpiration(-100))
              }
  
              var cookieExpiration = function (minutes) {
                  var exp = new Date()
                  exp.setTime(exp.getTime() + (minutes * 60 * 1000))
                  return exp
              }
  
              var getCookie = function (key) {
                  var name = key + '='
                  var ca = document.cookie.split(';')
                  for (var i = 0; i < ca.length; i++) {
                      var c = ca[i]
                      while (c.charAt(0) == ' ') {
                          c = c.substring(1)
                      }
                      if (c.indexOf(name) == 0) {
                          return c.substring(name.length, c.length)
                      }
                  }
                  return ''
              }
  
              var setCookie = function (key, value, expirationDate) {
                  var cookie = key + '=' + value + '; ' +
                      'expires=' + expirationDate.toUTCString() + '; '
                  document.cookie = cookie
              }
          }
  
          function createCallbackStorage () {
              try {
                  return new LocalStorage()
              } catch (err) {
              }
  
              return new CookieStorage()
          }
      }
  
      if (typeof module === 'object' && module && typeof module.exports === 'object') {
          module.exports = Keycloak
      } else {
          window.Keycloak = Keycloak
  
          if (typeof define === 'function' && define.amd) {
              define('keycloak', [], function () { return Keycloak })
          }
      }
  })(window)
  