· 8 years ago · Jun 14, 2018, 02:54 PM
1(function webpackUniversalModuleDefinition(root, factory) {
2 if(typeof exports === 'object' && typeof module === 'object')
3 module.exports = factory();
4 else if(typeof define === 'function' && define.amd)
5 define([], factory);
6 else if(typeof exports === 'object')
7 exports["jwt"] = factory();
8 else
9 root["jwt"] = factory();
10})(typeof self !== 'undefined' ? self : this, function() {
11return /******/ (function(modules) { // webpackBootstrap
12/******/ // The module cache
13/******/ var installedModules = {};
14/******/
15/******/ // The require function
16/******/ function __webpack_require__(moduleId) {
17/******/
18/******/ // Check if module is in cache
19/******/ if(installedModules[moduleId]) {
20/******/ return installedModules[moduleId].exports;
21/******/ }
22/******/ // Create a new module (and put it into the cache)
23/******/ var module = installedModules[moduleId] = {
24/******/ i: moduleId,
25/******/ l: false,
26/******/ exports: {}
27/******/ };
28/******/
29/******/ // Execute the module function
30/******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
31/******/
32/******/ // Flag the module as loaded
33/******/ module.l = true;
34/******/
35/******/ // Return the exports of the module
36/******/ return module.exports;
37/******/ }
38/******/
39/******/
40/******/ // expose the modules object (__webpack_modules__)
41/******/ __webpack_require__.m = modules;
42/******/
43/******/ // expose the module cache
44/******/ __webpack_require__.c = installedModules;
45/******/
46/******/ // define getter function for harmony exports
47/******/ __webpack_require__.d = function(exports, name, getter) {
48/******/ if(!__webpack_require__.o(exports, name)) {
49/******/ Object.defineProperty(exports, name, {
50/******/ configurable: false,
51/******/ enumerable: true,
52/******/ get: getter
53/******/ });
54/******/ }
55/******/ };
56/******/
57/******/ // getDefaultExport function for compatibility with non-harmony modules
58/******/ __webpack_require__.n = function(module) {
59/******/ var getter = module && module.__esModule ?
60/******/ function getDefault() { return module['default']; } :
61/******/ function getModuleExports() { return module; };
62/******/ __webpack_require__.d(getter, 'a', getter);
63/******/ return getter;
64/******/ };
65/******/
66/******/ // Object.prototype.hasOwnProperty.call
67/******/ __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };
68/******/
69/******/ // __webpack_public_path__
70/******/ __webpack_require__.p = "";
71/******/
72/******/ // Load entry module and return exports
73/******/ return __webpack_require__(__webpack_require__.s = 0);
74/******/ })
75/************************************************************************/
76/******/ ([
77/* 0 */
78/***/ (function(module, exports, __webpack_require__) {
79
80"use strict";
81
82Object.defineProperty(exports, "__esModule", { value: true });
83var jwt_1 = __webpack_require__(1);
84exports.default = jwt_1.default;
85
86
87/***/ }),
88/* 1 */
89/***/ (function(module, exports, __webpack_require__) {
90
91"use strict";
92
93var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
94 return new (P || (P = Promise))(function (resolve, reject) {
95 function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
96 function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
97 function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); }
98 step((generator = generator.apply(thisArg, _arguments || [])).next());
99 });
100};
101var __generator = (this && this.__generator) || function (thisArg, body) {
102 var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
103 return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
104 function verb(n) { return function (v) { return step([n, v]); }; }
105 function step(op) {
106 if (f) throw new TypeError("Generator is already executing.");
107 while (_) try {
108 if (f = 1, y && (t = y[op[0] & 2 ? "return" : op[0] ? "throw" : "next"]) && !(t = t.call(y, op[1])).done) return t;
109 if (y = 0, t) op = [0, t.value];
110 switch (op[0]) {
111 case 0: case 1: t = op; break;
112 case 4: _.label++; return { value: op[1], done: false };
113 case 5: _.label++; y = op[1]; op = [0]; continue;
114 case 7: op = _.ops.pop(); _.trys.pop(); continue;
115 default:
116 if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
117 if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
118 if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
119 if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
120 if (t[2]) _.ops.pop();
121 _.trys.pop(); continue;
122 }
123 op = body.call(thisArg, _);
124 } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
125 if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
126 }
127};
128Object.defineProperty(exports, "__esModule", { value: true });
129var Keycloak = __webpack_require__(2);
130var cacheUtils_1 = __webpack_require__(4);
131// Use Polyfill for BroadcastChannel if not supported natively by browser
132if (!('BroadcastChannel' in window)) {
133 log("[jwt.js] Using polyfill for BroadcastChannel");
134 (function (global) {
135 var channels = [];
136 function BroadcastChannel(channel) {
137 var $this = this;
138 channel = String(channel);
139 var id = '$BroadcastChannel$' + channel + '$';
140 channels[id] = channels[id] || [];
141 channels[id].push(this);
142 this._name = channel;
143 this._id = id;
144 this._closed = false;
145 this._mc = new global.MessageChannel();
146 this._mc.port1.start();
147 this._mc.port2.start();
148 global.addEventListener('storage', function (e) {
149 if (e.storageArea !== global.localStorage)
150 return;
151 if (e.newValue === null)
152 return;
153 if (e.key.substring(0, id.length) !== id)
154 return;
155 var data = JSON.parse(e.newValue);
156 $this._mc.port2.postMessage(data);
157 });
158 }
159 BroadcastChannel.prototype = {
160 // BroadcastChannel API
161 get name() { return this._name; },
162 postMessage: function (message) {
163 var $this = this;
164 if (this._closed) {
165 var e = new Error();
166 e.name = 'InvalidStateError';
167 throw e;
168 }
169 var value = JSON.stringify(message);
170 // Broadcast to other contexts via storage events...
171 var key = this._id + String(Date.now()) + '$' + String(Math.random());
172 global.localStorage.setItem(key, value);
173 setTimeout(function () { global.localStorage.removeItem(key); }, 500);
174 // Broadcast to current context via ports
175 channels[this._id].forEach(function (bc) {
176 if (bc === $this)
177 return;
178 bc._mc.port2.postMessage(JSON.parse(value));
179 });
180 },
181 close: function () {
182 if (this._closed)
183 return;
184 this._closed = true;
185 this._mc.port1.close();
186 this._mc.port2.close();
187 var index = channels[this._id].indexOf(this);
188 channels[this._id].splice(index, 1);
189 },
190 // EventTarget API
191 get onmessage() { return this._mc.port1.onmessage; },
192 set onmessage(value) { this._mc.port1.onmessage = value; },
193 addEventListener: function (type, listener /*, useCapture*/) {
194 return this._mc.port1.addEventListener.apply(this._mc.port1, arguments);
195 },
196 removeEventListener: function (type, listener /*, useCapture*/) {
197 return this._mc.port1.removeEventListener.apply(this._mc.port1, arguments);
198 },
199 dispatchEvent: function (event) {
200 return this._mc.port1.dispatchEvent.apply(this._mc.port1, arguments);
201 }
202 };
203 global.BroadcastChannel = global.BroadcastChannel || BroadcastChannel;
204 }(self));
205}
206/*
207 * Copyright 2016 Red Hat, Inc. and/or its affiliates
208 * and other contributors as indicated by the @author tags.
209 *
210 * Licensed under the Apache License, Version 2.0 (the "License");
211 * you may not use this file except in compliance with the License.
212 * You may obtain a copy of the License at
213 *
214 * http://www.apache.org/licenses/LICENSE-2.0
215 *
216 * Unless required by applicable law or agreed to in writing, software
217 * distributed under the License is distributed on an "AS IS" BASIS,
218 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
219 * See the License for the specific language governing permissions and
220 * limitations under the License.
221 */
222/*global JSON, define, console, document, window, chrometwo_require*/
223/*jslint browser: true*/
224var private_functions = {
225 /**
226 * Store things in local- or sessionStorage. Because *Storage only
227 * accepts string values, the store will automatically serialize
228 * objects into JSON strings when you store them (set), and deserialize
229 * them back into objects when you retrieve them (get).
230 *
231 * @param {string} type Either "local" or "session", depending on
232 * whether you want localStorage or sessionStorage.
233 * @return {object} An object-friendly interface to localStorage or
234 * sessionStorage.
235 */
236 make_store: function (type) {
237 var store;
238 try {
239 // if DOM Storage is disabled in Chrome, merely referencing
240 // window.localStorage or window.sessionStorage will throw a
241 // DOMException.
242 store = window[type + 'Storage'];
243 // if DOM Storage is disabled in other browsers, it may not
244 // throw an error, but we should still throw one for them.
245 if (!store)
246 throw new Error('DOM Storage is disabled');
247 }
248 catch (e) {
249 // this means DOM storage is disabled in the users' browser, so
250 // we'll create an in-memory object that simulates the DOM
251 // Storage API.
252 store = {
253 getItem: function mem_store_get_item(key) {
254 return store[key];
255 },
256 setItem: function mem_store_set_item(key, value) {
257 return (store[key] = value);
258 },
259 removeItem: function mem_store_remove_item(key) {
260 return delete store[key];
261 }
262 };
263 }
264 // The get and set here are used exclusively for getting and setting the token and refreshToken which are strings.
265 return {
266 get: function get(key) {
267 var value = store.getItem(key);
268 return value && JSON.parse(value);
269 },
270 set: function set(key, val) {
271 if (typeof val !== 'undefined') {
272 return store.setItem(key, JSON.stringify(val));
273 }
274 },
275 remove: function remove(key) {
276 return store.removeItem(key);
277 }
278 };
279 }
280};
281var lib = {
282 /**
283 * A simple function to get the value of a given cookie
284 * @param {string} cookieName The cookie name/key
285 * @returns {string} The string value of the cookie, "" if there was no cookie
286 */
287 getCookieValue: function (cookieName) {
288 var start, end;
289 if (document.cookie.length > 0) {
290 start = document.cookie.indexOf(cookieName + '=');
291 if (start !== -1 && (start === 0 || (document.cookie.charAt(start - 1) === ' '))) {
292 start += cookieName.length + 1;
293 end = document.cookie.indexOf(';', start);
294 if (end === -1) {
295 end = document.cookie.length;
296 }
297 return decodeURI(document.cookie.substring(start, end));
298 }
299 }
300 return '';
301 },
302 setCookie: function (name, value, expires, path, domain, secure) {
303 // set time, it's in milliseconds
304 var today = new Date();
305 today.setTime(today.getTime());
306 /*
307 if the expires variable is set, make the correct
308 expires time, the current script below will set
309 it for x number of days, to make it for hours,
310 delete * 24, for minutes, delete * 60 * 24
311 */
312 if (expires) {
313 expires = expires * 1000 * 60 * 60;
314 }
315 var expires_date = new Date(today.getTime() + (expires));
316 document.cookie = name + '=' + encodeURI(value) +
317 ((expires) ? ';expires=' + expires_date.toUTCString() : '') +
318 ((path) ? ';path=' + path : '') +
319 ((domain) ? ';domain=' + domain : '') +
320 ((secure) ? ';secure' : '');
321 },
322 removeCookie: function removeCookie(cookie_name) {
323 var cookie_date = new Date(); // current date & time
324 cookie_date.setTime(cookie_date.getTime() - 1);
325 document.cookie = cookie_name += '=; expires=' + cookie_date.toUTCString();
326 },
327 log: function (message) {
328 if (typeof console !== 'undefined') {
329 console.log(message);
330 }
331 },
332 store: {
333 local: private_functions.make_store('local'),
334 session: private_functions.make_store('session')
335 }
336};
337var DEFAULT_KEYCLOAK_OPTIONS = {
338 realm: 'redhat-external',
339 // realm: 'short-session',
340 clientId: 'changeme'
341};
342var JWT_REDHAT_IDENTIFIER = 'jwt_redhat';
343var TOKEN_SURFIX = "_" + JWT_REDHAT_IDENTIFIER + "_token";
344var REFRESH_TOKEN_NAME_SURFIX = "_" + JWT_REDHAT_IDENTIFIER + "_refresh_token";
345var FAIL_COUNT_NAME_SURFIX = "_" + JWT_REDHAT_IDENTIFIER + "_refresh_fail_count";
346var INTERNAL_ROLE = 'redhat:employees';
347var TOKEN_NAME = "" + DEFAULT_KEYCLOAK_OPTIONS.clientId + TOKEN_SURFIX;
348var INITIAL_JWT_OPTIONS = undefined;
349var COOKIE_TOKEN_NAME = TOKEN_NAME;
350var REFRESH_TOKEN_NAME = "" + DEFAULT_KEYCLOAK_OPTIONS.clientId + REFRESH_TOKEN_NAME_SURFIX;
351var FAIL_COUNT_NAME = "" + DEFAULT_KEYCLOAK_OPTIONS.clientId + FAIL_COUNT_NAME_SURFIX;
352var TOKEN_EXP_TTE = 58; // Seconds to check forward if the token will expire
353var REFRESH_INTERVAL = 1 * TOKEN_EXP_TTE * 1000; // ms. check token for upcoming expiration every this many milliseconds
354var REFRESH_TTE = 90; // seconds. refresh only token if it would expire this many seconds from now
355var FAIL_COUNT_THRESHOLD = 5; // how many times in a row token refresh can fail before we give up trying
356var userInfo; // To be used to set the user context in Raven
357var disablePolling = false;
358var initialUserToken = null;
359var broadcastChannel = null;
360// This is explicitly to track when the first successfull updateToken happens.
361var timeSkew = null;
362var DEFAULT_KEYCLOAK_INIT_OPTIONS = {
363 responseMode: 'query',
364 flow: 'standard',
365 token: null,
366 refreshToken: null
367};
368var origin = location.hostname;
369// const originWithPort = location.hostname + (location.port ? ':' + location.port : '');
370var token = null;
371var refreshToken = null;
372var state = {
373 initialized: false,
374 keycloak: null
375};
376var events = {
377 init: [],
378 token: [],
379 tokenMismatch: [],
380 jwtTokenUpdateFailed: [],
381 refreshError: [],
382 refreshSuccess: [],
383 logout: [],
384 tokenExpired: [],
385 initError: []
386};
387document.cookie = COOKIE_TOKEN_NAME + '=;expires=Thu, 01 Jan 1970 00:00:00 GMT; domain=.redhat.com; path=/; secure;';
388/**
389 * Log session-related messages to the console, in pre-prod environments.
390 */
391function log(message) {
392 var args = arguments;
393 try {
394 cacheUtils_1.CacheUtils.get('debug-logging').then(function (debugLoggingCache) {
395 if (debugLoggingCache && debugLoggingCache.value === true) {
396 console.log.apply(console, args);
397 }
398 });
399 }
400 catch (e) { }
401}
402// Keep track of the setInterval for the refresh token so we can cancel it and restart it if need be
403var refreshIntervalId;
404/**
405 * Kicks off all the session-related things again.
406 *
407 * @memberof module:jwt
408 * @private
409 */
410function reinit() {
411 log('[jwt.js] Re-initializing jwt');
412 if (!INITIAL_JWT_OPTIONS) {
413 return;
414 }
415 resetKeyCount(FAIL_COUNT_NAME);
416 init(INITIAL_JWT_OPTIONS);
417}
418/**
419 * Kicks off all the session-related things.
420 *
421 * @memberof module:jwt
422 * @private
423 */
424function init(jwtOptions) {
425 log('[jwt.js] initializing');
426 INITIAL_JWT_OPTIONS = jwtOptions;
427 var options = jwtOptions.keycloakOptions ? Object.assign({}, DEFAULT_KEYCLOAK_OPTIONS, jwtOptions.keycloakOptions) : DEFAULT_KEYCLOAK_OPTIONS;
428 options.url = !options.url ? ssoUrl(options.internalAuth) : options.url;
429 disablePolling = jwtOptions.disablePolling;
430 initialUserToken = null;
431 // Token names are now namespaced by clientId, thus moving the token_name evaluation
432 // and token initialization into the init function where we get the actual clientId
433 // We don't need to change COOKIE_TOKEN_NAME as its domain specific and will not
434 // conflict with other applications.
435 TOKEN_NAME = "" + options.clientId + TOKEN_SURFIX;
436 COOKIE_TOKEN_NAME = TOKEN_NAME;
437 REFRESH_TOKEN_NAME = "" + options.clientId + REFRESH_TOKEN_NAME_SURFIX;
438 FAIL_COUNT_NAME = "" + options.clientId + FAIL_COUNT_NAME_SURFIX;
439 token = lib.store.local.get(TOKEN_NAME) || lib.getCookieValue(COOKIE_TOKEN_NAME);
440 refreshToken = lib.store.local.get(REFRESH_TOKEN_NAME);
441 if (token && token !== 'undefined') {
442 DEFAULT_KEYCLOAK_INIT_OPTIONS.token = token;
443 }
444 if (refreshToken) {
445 DEFAULT_KEYCLOAK_INIT_OPTIONS.refreshToken = refreshToken;
446 }
447 // for multi tab communication
448 if (!jwtOptions.disableBroadcastMessage) {
449 if (!broadcastChannel) {
450 broadcastChannel = new BroadcastChannel("jwt_" + options.realm);
451 }
452 broadcastChannel.onmessage = function (e) {
453 log("[jwt.js] BroadcastChannel, Received event : " + e.data.type);
454 if (this && e && e.data && e.data.type === 'Initialized' && !this.isAuthenticated() && e.data.authenticated) {
455 if (options.clientId === e.data.clientId) {
456 this.reinit();
457 }
458 else {
459 if (jwtOptions.reLoginIframeEnabled && jwtOptions.reLoginIframe) {
460 var iframeMessage = { value: null, message: 'reinit' };
461 jwtOptions.reLoginIframe.contentWindow.postMessage(JSON.stringify(iframeMessage), '*');
462 }
463 }
464 }
465 }.bind(this);
466 }
467 state.keycloak = Keycloak(options);
468 // wire up our handlers to keycloak's events
469 state.keycloak.onAuthSuccess = onAuthSuccessCallback;
470 state.keycloak.onAuthError = onAuthError;
471 state.keycloak.onAuthRefreshSuccess = onAuthRefreshSuccessCallback;
472 state.keycloak.onAuthRefreshError = onAuthRefreshErrorCallback;
473 state.keycloak.onAuthLogout = onAuthLogoutCallback;
474 state.keycloak.onTokenExpired = onTokenExpiredCallback;
475 return state.keycloak
476 .init(jwtOptions.keycloakInitOptions ? Object.assign({}, DEFAULT_KEYCLOAK_INIT_OPTIONS, jwtOptions.keycloakInitOptions) : DEFAULT_KEYCLOAK_INIT_OPTIONS)
477 .success(keycloakInitSuccess)
478 .error(keycloakInitError);
479}
480/**
481 * Keycloak init success handler.
482 * @memberof module:jwt
483 * @param {Boolean} authenticated whether the user is authenticated or not
484 * @private
485 */
486function keycloakInitSuccess(authenticated) {
487 log('[jwt.js] initialized (authenticated: ' + authenticated + ')');
488 if (authenticated) {
489 setToken(state.keycloak.token);
490 setRefreshToken(state.keycloak.refreshToken);
491 initialUserToken = getToken();
492 resetKeyCount(FAIL_COUNT_NAME).then(function () {
493 startRefreshLoop();
494 }).catch(function () {
495 log('[jwt.js] unable to reset the fail count');
496 startRefreshLoop();
497 });
498 if (!INITIAL_JWT_OPTIONS.disableBroadcastMessage && broadcastChannel) {
499 broadcastChannel.postMessage({
500 type: 'Initialized',
501 clientId: INITIAL_JWT_OPTIONS.keycloakOptions.clientId,
502 authenticated: authenticated
503 });
504 }
505 // initialize re-login iframe only after the application has initialized
506 if (INITIAL_JWT_OPTIONS.reLoginIframeEnabled && INITIAL_JWT_OPTIONS.reLoginIframe) {
507 var iframeJwtOptions = Object.assign({}, INITIAL_JWT_OPTIONS);
508 iframeJwtOptions.reLoginIframeEnabled = false;
509 iframeJwtOptions.reLoginIframe = null;
510 // no need to broadcast messages from iframe
511 iframeJwtOptions.disableBroadcastMessage = true;
512 var iframeMessage = { value: iframeJwtOptions, message: 'init' };
513 INITIAL_JWT_OPTIONS.reLoginIframe.contentWindow.postMessage(JSON.stringify(iframeMessage), '*');
514 }
515 }
516 keycloakInitHandler();
517}
518/**
519 * Call any init event handlers that have are registered.
520 *
521 * @memberof module:jwt
522 * @private
523 */
524function handleInitEvents() {
525 if (events.init.length > 0) {
526 events.init.forEach(function (event) {
527 if (typeof event === 'function') {
528 event(Jwt);
529 }
530 });
531 }
532}
533/**
534 * Call refresh error events
535 *
536 * @memberof module:jwt
537 * @private
538 */
539function handleRefreshErrorEvents() {
540 if (events.refreshError.length > 0) {
541 events.refreshError.forEach(function (event) {
542 if (typeof event === 'function') {
543 event(Jwt);
544 }
545 });
546 }
547}
548/**
549 * Call refresh success events
550 *
551 * @memberof module:jwt
552 * @private
553 */
554function handleRefreshSuccessEvents() {
555 if (events.refreshSuccess.length > 0) {
556 events.refreshSuccess.forEach(function (event) {
557 if (typeof event === 'function') {
558 event(Jwt);
559 }
560 });
561 }
562}
563/**
564 * Call logout events
565 *
566 * @memberof module:jwt
567 * @private
568 */
569function handleLogoutEvents() {
570 if (events.logout.length > 0) {
571 events.logout.forEach(function (event) {
572 if (typeof event === 'function') {
573 event(Jwt);
574 }
575 });
576 }
577}
578/**
579 * Call init error events
580 *
581 * @memberof module:jwt
582 * @private
583 */
584function handleInitErrorEvents() {
585 if (events.initError.length > 0) {
586 events.initError.forEach(function (event) {
587 if (typeof event === 'function') {
588 event(Jwt);
589 }
590 });
591 }
592}
593/**
594 * Call Token expired events
595 *
596 * @memberof module:jwt
597 * @private
598 */
599function handleTokenExpiredEvents() {
600 if (events.tokenExpired.length > 0) {
601 events.tokenExpired.forEach(function (event) {
602 if (typeof event === 'function') {
603 event(Jwt);
604 }
605 });
606 }
607}
608/**
609 * Call any token event handlers that have are registered. One time call then removed.
610 *
611 * @memberof module:jwt
612 * @private
613 */
614function handleTokenEvents() {
615 while (events.token.length) {
616 var event_1 = events.token.shift();
617 if (typeof event_1 === 'function') {
618 event_1(Jwt);
619 }
620 }
621}
622/**
623 * Call any token mismatch event handlers that have are registered. One time call then removed.
624 *
625 * @memberof module:jwt
626 * @private
627 */
628function handleTokenMismatchEvents() {
629 while (events.tokenMismatch.length) {
630 var event_2 = events.tokenMismatch.shift();
631 if (typeof event_2 === 'function') {
632 event_2(Jwt);
633 }
634 }
635}
636/**
637 * Call any token mismatch event handlers that have are registered. One time call then removed.
638 *
639 * @memberof module:jwt
640 * @private
641 */
642function handleJwtTokenUpdateFailedEvents() {
643 while (events.jwtTokenUpdateFailed.length) {
644 var event_3 = events.jwtTokenUpdateFailed.shift();
645 if (typeof event_3 === 'function') {
646 event_3(Jwt);
647 }
648 }
649}
650/**
651 * Register a function to be called when jwt.js has initialized. Runs
652 * immediately if already initialized. When called, the function will be
653 * passed a reference to the jwt.js API.
654 *
655 * @memberof module:jwt
656 */
657function onInit(func) {
658 log('[jwt.js] registering init handler');
659 if (state.initialized) {
660 log("[jwt.js] running event handler: onInit");
661 func(Jwt);
662 }
663 else {
664 events.init.push(func);
665 }
666}
667/**
668 * Register a function to be called when jwt.js has initialized and
669 * the first token update has successful run
670 * @memberof module:jwt
671 */
672function onInitialUpdateToken(func) {
673 log("[jwt.js] registering the onInitialUpdateToken handler");
674 // We know the setToken has happened at least once when the timeLocal is properly set
675 if (state.initialized && state.keycloak.timeSkew !== null) {
676 log("[jwt.js] running event handler: onInitialUpdateToken");
677 func(Jwt);
678 }
679 else {
680 events.token.push(func);
681 }
682}
683/**
684 * Register a function to be called when the tokens mismatch. This is a hard
685 * error caused when mixing sso envs/tokens and requires a logout/log back in
686 * @memberof module:jwt
687 */
688function onTokenMismatch(func) {
689 log("[jwt.js] registering the onTokenMismatch handler");
690 if (state.initialized) {
691 log("[jwt.js] running event handler: onTokenMismatch");
692 func(Jwt);
693 }
694 else {
695 events.tokenMismatch.push(func);
696 }
697}
698/**
699 * Register a function to be called when the tokens mismatch. This is a hard
700 * error caused when mixing sso envs/tokens and requires a logout/log back in
701 * @memberof module:jwt
702 */
703function onJwtTokenUpdateFailed(func) {
704 log("[jwt.js] registering the onJwtTokenUpdateFailed handler");
705 if (state.initialized) {
706 log("[jwt.js] running event handler: onJwtTokenUpdateFailed");
707 func(Jwt);
708 }
709 else {
710 events.jwtTokenUpdateFailed.push(func);
711 }
712}
713/**
714 * Enable debug logging
715 * @memberof module:jwt
716 */
717function enableDebugLogging() {
718 return __awaiter(this, void 0, void 0, function () {
719 var booleanCache, newBooleanCache;
720 return __generator(this, function (_a) {
721 switch (_a.label) {
722 case 0: return [4 /*yield*/, cacheUtils_1.CacheUtils.get('debug-logging')];
723 case 1:
724 booleanCache = _a.sent();
725 if (!(booleanCache && booleanCache.value === true)) return [3 /*break*/, 2];
726 log("[jwt.js] Debug logging already enabled");
727 return [3 /*break*/, 4];
728 case 2:
729 newBooleanCache = { value: true };
730 return [4 /*yield*/, cacheUtils_1.CacheUtils.set('debug-logging', newBooleanCache)];
731 case 3:
732 _a.sent();
733 log("[jwt.js] Enabled debug logging");
734 _a.label = 4;
735 case 4: return [2 /*return*/];
736 }
737 });
738 });
739}
740/**
741 * Disable debug logging
742 * @memberof module:jwt
743 */
744function disableDebugLogging() {
745 log("[jwt.js] Disabling debug logging");
746 var newBooleanCache = { value: false };
747 cacheUtils_1.CacheUtils.set('debug-logging', newBooleanCache);
748}
749/**
750 * Keycloak init error handler.
751 * @memberof module:jwt
752 * @private
753 */
754function keycloakInitError() {
755 log('[jwt.js] init error');
756 keycloakInitErrorHandler();
757 removeToken();
758 removeRefreshToken();
759 cancelRefreshLoop(); // Cancel update token refresh loop
760}
761/**
762 * Does some things after keycloak initializes, whether or not
763 * initialization was successful.
764 *
765 * @memberof module:jwt
766 * @private
767 */
768function keycloakInitHandler() {
769 state.initialized = true;
770 handleInitEvents();
771}
772/**
773 * Call events after keycloak auth refresh error.
774 *
775 * @memberof module:jwt
776 * @private
777 */
778function keycloakRefreshErrorHandler() {
779 handleRefreshErrorEvents();
780}
781/**
782 * Call events after keycloak auth refresh success.
783 *
784 * @memberof module:jwt
785 * @private
786 */
787function keycloakRefreshSuccessHandler() {
788 handleRefreshSuccessEvents();
789}
790/**
791 * Call events after keycloak auth logout.
792 *
793 * @memberof module:jwt
794 * @private
795 */
796function keycloakLogoutHandler() {
797 handleLogoutEvents();
798}
799/**
800 * Call events after keycloak init error.
801 *
802 * @memberof module:jwt
803 * @private
804 */
805function keycloakInitErrorHandler() {
806 state.initialized = false;
807 handleInitErrorEvents();
808}
809/**
810 * Call events after keycloak token expired
811 *
812 * @memberof module:jwt
813 * @private
814 */
815function keycloakTokenExpiredHandler() {
816 handleTokenExpiredEvents();
817}
818/**
819 * Creates a URL to the SSO service based on an old IDP URL.
820 *
821 * @memberof module:jwt
822 * @returns {String} a URL to the SSO service
823 * @private
824 */
825function ssoUrl(isInternal) {
826 var subDomain = isInternal === true ? 'auth' : 'sso'; // defaults to sso
827 switch (location.hostname) {
828 // Valid PROD URLs
829 case 'access.redhat.com':
830 case 'prod.foo.redhat.com':
831 case 'rhn.redhat.com':
832 case 'hardware.redhat.com':
833 case 'unified.gsslab.rdu2.redhat.com':
834 log('[jwt.js] ENV: prod');
835 return "https://" + subDomain + ".redhat.com/auth";
836 // Valid STAGE URLs
837 case 'access.stage.redhat.com':
838 case 'accessstage.usersys.redhat.com':
839 case 'stage.foo.redhat.com':
840 log('[jwt.js] ENV: stage');
841 return "https://" + subDomain + ".stage.redhat.com/auth";
842 // Valid QA URLs
843 case 'access.qa.redhat.com':
844 case 'qa.foo.redhat.com':
845 case 'accessqa.usersys.redhat.com':
846 case 'unified-qa.gsslab.pnq2.redhat.com':
847 log('[jwt.js] ENV: qa');
848 return "https://" + subDomain + ".qa.redhat.com/auth";
849 case 'ui.foo.redhat.com':
850 log('[jwt.js] ENV: qa / dev');
851 return "https://" + subDomain + ".dev1.redhat.com/auth";
852 case 'fte.foo.redhat.com':
853 log('[jwt.js] ENV: fte');
854 return "https://" + subDomain + ".dev.redhat.com/auth";
855 // Valid CI URLs
856 case 'access.devgssci.devlab.phx1.redhat.com':
857 case 'accessci.usersys.redhat.com':
858 case 'ci.foo.redhat.com':
859 default:
860 log('[jwt.js] ENV: ci');
861 return "https://" + subDomain + ".dev2.redhat.com/auth";
862 }
863}
864/**
865 * A handler for when authentication is successfully established.
866 *
867 * @memberof module:jwt
868 * @private
869 */
870function onAuthSuccessCallback() {
871 log('[jwt.js] onAuthSuccessCallback');
872}
873function onAuthError() {
874 removeToken();
875 removeRefreshToken();
876 cancelRefreshLoop(); // Cancel update token refresh loop
877 log('[jwt.js] onAuthError');
878}
879function onAuthRefreshSuccessCallback() {
880 log('[jwt.js] onAuthRefreshSuccess');
881 keycloakRefreshSuccessHandler();
882}
883function onAuthRefreshErrorCallback() {
884 log('[jwt.js] onAuthRefreshError');
885 keycloakRefreshErrorHandler();
886}
887function onAuthLogoutCallback() {
888 log('[jwt.js] onAuthLogout');
889 keycloakLogoutHandler();
890 // skip redirect if user is logs out from other tabs.
891 logout({ skipRedirect: true });
892}
893/**
894 * Register a function to be called when keycloak has failed to refresh the session. Runs
895 * immediately if already initialized. When called, the function will be
896 * passed a reference to the jwt.js API.
897 *
898 * @memberof module:jwt
899 */
900function onAuthRefreshError(func) {
901 log('[jwt.js] registering auth refresh error handler');
902 events.refreshError.push(func);
903}
904/**
905 * Register a function to be called when keycloak has successfully to refresh the session.
906 *
907 * @memberof module:jwt
908 */
909function onAuthRefreshSuccess(func) {
910 log('[jwt.js] registering auth refresh success handler');
911 events.refreshSuccess.push(func);
912}
913/**
914 * Register a function to be called when keycloak has logged out.
915 *
916 * @memberof module:jwt
917 */
918function onAuthLogout(func) {
919 log('[jwt.js] registering auth logout handler');
920 events.logout.push(func);
921}
922/**
923 * Register a function to be called when keycloak init fails.
924 *
925 * @memberof module:jwt
926 */
927function onInitError(func) {
928 log('[jwt.js] registering init error handler');
929 events.initError.push(func);
930}
931function onTokenExpiredCallback() {
932 log('[jwt.js] onTokenExpired');
933 keycloakTokenExpiredHandler();
934}
935/**
936 * Register a function to be called when keycloak as expired the token. Runs
937 * immediately if already initialized. When called, the function will be
938 * passed a reference to the jwt.js API.
939 *
940 * @memberof module:jwt
941 */
942function onTokenExpired(func) {
943 log('[jwt.js] registering token expired handler');
944 events.tokenExpired.push(func);
945}
946/**
947 * Checks if the token is expired
948 *
949 * @memberof module:jwt
950 * @private
951 */
952function isTokenExpired(tte) {
953 if (tte === void 0) { tte = REFRESH_TTE; }
954 return state.keycloak.isTokenExpired(tte) === true;
955}
956/**
957 * Refreshes the access token. Recursively can be called with an iteration count
958 * where the function will retry x number of times.
959 *
960 * @memberof module:jwt
961 * @private
962 */
963function updateToken(force) {
964 if (force === void 0) { force = false; }
965 return __awaiter(this, void 0, void 0, function () {
966 var isFailCountPassed;
967 return __generator(this, function (_a) {
968 switch (_a.label) {
969 case 0: return [4 /*yield*/, failCountPassed(FAIL_COUNT_NAME, FAIL_COUNT_THRESHOLD)];
970 case 1:
971 isFailCountPassed = _a.sent();
972 return [2 /*return*/, new Promise(function (resolve, reject) {
973 try {
974 if (isFailCountPassed && force !== true) {
975 var msg = "Not updating token because updating failed more than " + FAIL_COUNT_THRESHOLD + " times in a row";
976 log("[jwt.js] " + msg);
977 reject(msg);
978 }
979 else {
980 log('[jwt.js] running updateToken');
981 state.keycloak
982 .updateToken(force === true ? -1 : REFRESH_TTE)
983 .success(function (refreshed) {
984 updateTokenSuccess(refreshed);
985 resolve(refreshed);
986 })
987 // ITokenUpdateFailure
988 .error(function (e) {
989 updateTokenFailure(e);
990 reject(e);
991 });
992 }
993 }
994 catch (e) {
995 reject(e);
996 }
997 })];
998 }
999 });
1000 });
1001}
1002/**
1003 * Start the {@link module:jwt.refreshLoop refreshLoop}, which
1004 * periodically updates the authentication token. This should only ever
1005 * be called manually if manually first cancelling the refresh loop
1006 *
1007 * @memberof module:jwt
1008 * @private
1009 */
1010function startRefreshLoop() {
1011 refreshLoop();
1012 if (disablePolling === true) {
1013 log('[jwt.js] Not starting the refresh loop interval as disablePolling is true.');
1014 }
1015 else {
1016 if (!refreshIntervalId) {
1017 log('[jwt.js] Starting refresh loop.');
1018 refreshIntervalId = setInterval(refreshLoop, REFRESH_INTERVAL);
1019 }
1020 else {
1021 log('[jwt.js] Cannot start refresh loop as it is already started.');
1022 }
1023 }
1024}
1025/**
1026 * Cancel the {@link module:jwt.refreshLoop refreshLoop}
1027 * @memberof module:jwt
1028 * @private
1029 */
1030function cancelRefreshLoop(shouldStopTokenUpdates) {
1031 if (refreshIntervalId) {
1032 clearInterval(refreshIntervalId);
1033 log('[jwt.js] token refresh interval cancelled');
1034 }
1035}
1036/**
1037 * This is run periodically by {@link module:jwt.startRefreshLoop
1038 * startRefreshLoop}.
1039 *
1040 * @memberof module:jwt
1041 * @private
1042 */
1043function refreshLoop() {
1044 return updateToken().then(function (refreshed) {
1045 log('[jwt.js] The refresh loop ' + ['did not refresh', 'refreshed'][~~refreshed] + ' the token');
1046 return refreshed;
1047 }).catch(function (e) {
1048 log("[jwt.js] The refresh loop failed to update the token due to: " + e);
1049 if (e && e.message && e.message.indexOf('not match') !== -1) {
1050 handleTokenMismatchEvents();
1051 }
1052 return false;
1053 });
1054}
1055/**
1056 * Handler run when a token is successfully updated.
1057 *
1058 * @memberof module:jwt
1059 * @private
1060 */
1061function updateTokenSuccess(refreshed) {
1062 log('[jwt.js] updateTokenSuccess, token was ' + ['not ', ''][~~refreshed] + 'refreshed');
1063 if (refreshed) {
1064 resetKeyCount(FAIL_COUNT_NAME); // token update worked, so reset number of consecutive failures
1065 }
1066 setToken(state.keycloak.token);
1067 setRefreshToken(state.keycloak.refreshToken);
1068 if (timeSkew === null && state.keycloak.timeSkew != null) {
1069 timeSkew = state.keycloak.timeSkew;
1070 handleTokenEvents();
1071 }
1072 try {
1073 if ((refreshed && !userInfo) || (refreshed && userInfo && (userInfo.username !== getUserInfo().username))) {
1074 setRavenUserContext();
1075 }
1076 }
1077 catch (e) {
1078 log("[jwt.js] Could not set Raven user context due to: " + e.message);
1079 }
1080}
1081/**
1082 * Handler run when a token update fails.
1083 *
1084 * @memberof module:jwt
1085 * @private
1086 */
1087function updateTokenFailure(e) {
1088 log('[jwt.js] updateTokenFailure');
1089 var userLoginTime = undefined;
1090 if (initialUserToken) {
1091 userLoginTime = (+new Date() - initialUserToken.auth_time * 1000) / 1000 / 60 / 60;
1092 }
1093 failCountPassed(FAIL_COUNT_NAME, 4).then(function (isFailCountPassed) {
1094 if (isFailCountPassed) {
1095 sendToSentry(new Error("[jwt.js] Update token failure: after " + FAIL_COUNT_THRESHOLD + " attempts within " + userLoginTime + " hours of logging in"), e);
1096 }
1097 incKeyCount(FAIL_COUNT_NAME);
1098 });
1099 handleJwtTokenUpdateFailedEvents();
1100}
1101/**
1102 * Save the refresh token value in a semi-persistent place (sessionStorage).
1103 *
1104 * @memberof module:jwt
1105 * @private
1106 */
1107function setRefreshToken(refresh_token) {
1108 log('[jwt.js] setting refresh token');
1109 lib.store.local.set(REFRESH_TOKEN_NAME, refresh_token);
1110}
1111/**
1112 * Remove the token value from its a semi-persistent place.
1113 *
1114 * @memberof module:jwt
1115 * @private
1116 */
1117function removeRefreshToken() {
1118 log('[jwt.js] removing refresh token');
1119 lib.store.local.remove(REFRESH_TOKEN_NAME);
1120}
1121/**
1122 * Save the token value in a semi-persistent place (cookie).
1123 *
1124 * @memberof module:jwt
1125 * @private
1126 */
1127function setToken(token) {
1128 // make sure token is defined
1129 if (token) {
1130 // save the token in localStorage AND in a cookie. the cookie
1131 // exists so it'll be sent along with AJAX requests. the
1132 // localStorage value exists so the token can be refreshed even if
1133 // it's been expired for a long time.
1134 log('[jwt.js] setting access token');
1135 lib.store.local.set(TOKEN_NAME, token);
1136 document.cookie = COOKIE_TOKEN_NAME + '=' + token + ';path=/;max-age=' + 15 * 60 + ';domain=.' + origin + ';secure;';
1137 }
1138}
1139/**
1140 * Remove the token value from its a semi-persistent place.
1141 *
1142 * @memberof module:jwt
1143 * @private
1144 */
1145function removeToken() {
1146 log('[jwt.js] removing access token');
1147 lib.store.local.remove(TOKEN_NAME);
1148 document.cookie = COOKIE_TOKEN_NAME + '=;expires=Thu, 01 Jan 1970 00:00:00 GMT; domain=.' + origin + '; path=/;secure;';
1149}
1150// init
1151// login
1152// createLoginUrl
1153// logout
1154// createLogoutUrl
1155// register
1156// createRegisterUrl
1157// createAccountUrl
1158// accountManagement
1159// hasRealmRole
1160// hasResourceRole
1161// loadUserProfile
1162// loadUserInfo
1163// isTokenExpired
1164// updateToken
1165// clearToken
1166// callback_id
1167// authenticated
1168// responseMode
1169// responseType
1170// flow
1171// authServerUrl
1172// realm
1173// clientId
1174// clientSecret
1175/**
1176 * Get an object containing the parsed JSON Web Token. Contains user and session metadata.
1177 *
1178 * @memberof module:jwt
1179 * @return {Object} the parsed JSON Web Token
1180 */
1181function getToken() {
1182 // any here as actual RH tokens have more information than this, which we will customize with IToken above
1183 return state.keycloak.tokenParsed;
1184}
1185/**
1186 * Get the token value stored in the lib. This method should always be used to get the token value
1187 * when constructing ajax calls in apps depending on jwt.js. This ensures that the token is being
1188 * fetched from localStorage which is cross tab vs. on the keycloak instance which is per tab.
1189 *
1190 * If this method falls back to the getCookieValue, which I believe is per tab, then it still may succumb
1191 * to token expired errors to due to stale
1192 *
1193 * Note that the token is technically kept in sync across tabs, but this is the safest function to access
1194 * the latest token with
1195 *
1196 * @memberof module:jwt
1197 * @return {Object} the parsed JSON Web Token
1198 */
1199function getStoredTokenValue() {
1200 return lib.store.local.get(TOKEN_NAME) || lib.getCookieValue(COOKIE_TOKEN_NAME);
1201}
1202/* Get a string containing the unparsed, base64-encoded JSON Web Token.
1203*
1204* @memberof module:jwt
1205* @return {Object} the parsed JSON Web Token
1206*/
1207function getEncodedToken() {
1208 return state.keycloak.token;
1209}
1210/**
1211 * Get the user info from the JSON Web Token. Contains user information
1212 * similar to what the old userStatus REST service returned.
1213 *
1214 * @memberof module:jwt
1215 * @return {Object} the user information
1216 */
1217function getUserInfo() {
1218 // the properties to return
1219 var token = getToken();
1220 return token ? {
1221 user_id: token.user_id,
1222 id: token.user_id,
1223 username: token.username,
1224 account_id: token.account_id,
1225 account_number: token.account_number,
1226 email: token.email,
1227 firstName: token.firstName,
1228 lastName: token.lastName,
1229 lang: token.lang,
1230 region: token.region,
1231 login: token.username,
1232 internal: isInternal()
1233 } : null;
1234}
1235/**
1236 * Is the user authenticated?
1237 *
1238 * @memberof module:jwt
1239 * @returns {Boolean} true if the user is authenticated, false otherwise
1240 */
1241function isAuthenticated() {
1242 return state.keycloak.authenticated;
1243}
1244/**
1245 * Is the user is a Red Hat employee?
1246 *
1247 * @memberof module:jwt
1248 * @returns {Boolean} true if the user is a Red Hat employee, otherwise false
1249 */
1250function isInternal() {
1251 return state.keycloak.hasRealmRole(INTERNAL_ROLE);
1252}
1253/**
1254 * Returns true if the user has all the given role(s). You may provide any
1255 * number of roles.
1256 *
1257 * @param {...String} roles All the roles you wish to test for. See
1258 * examples.
1259 * @returns {Boolean} whether the user is a member of ALL given roles
1260 * @example session.hasRole('portal_manage_cases');
1261 * session.hasRole('role1', 'role2', 'role3');
1262 * @memberof module:jwt
1263 */
1264function hasRole() {
1265 var roles = [];
1266 for (var _i = 0; _i < arguments.length; _i++) {
1267 roles[_i] = arguments[_i];
1268 }
1269 if (!roles)
1270 return false;
1271 for (var i = 0; i < roles.length; ++i) {
1272 if (!state.keycloak.hasRealmRole(roles[i])) {
1273 return false;
1274 }
1275 }
1276 return true;
1277}
1278/**
1279 * Get the URL to the registration page.
1280 * @return {String} the URL to the registration page
1281 * @memberof module:jwt
1282 */
1283function getRegisterUrl() {
1284 return state.keycloak.createRegisterUrl();
1285}
1286/**
1287 * Get the URL to the login page.
1288 * @return {String} the URL to the login page
1289 * @memberof module:jwt
1290 */
1291function getLoginUrl(options) {
1292 if (options === void 0) { options = {}; }
1293 var redirectUri = options.redirectUri || location.href;
1294 options.redirectUri = redirectUri;
1295 return state.keycloak.createLoginUrl(options);
1296}
1297/**
1298 * Get the URL to the logout page.
1299 * @return {String} the URL to the logout page
1300 * @memberof module:jwt
1301 */
1302function getLogoutUrl() {
1303 return state.keycloak.createLogoutUrl();
1304}
1305/**
1306 * Get the URL to the account management page.
1307 * @return {String} the URL to the account management page
1308 * @memberof module:jwt
1309 */
1310function getAccountUrl() {
1311 return state.keycloak.createAccountUrl();
1312}
1313/**
1314 * "Decorator" enforcing that jwt.js be initialized before the wrapped
1315 * function will be run.
1316 *
1317 * @memberof module:jwt
1318 * @private
1319 * @param {Function} func a function which shouldn't be run before jwt.js is
1320 * initialized.
1321 * @return {Function}
1322 */
1323function initialized(func) {
1324 return function () {
1325 if (state.initialized) {
1326 return func.apply({}, arguments);
1327 }
1328 else {
1329 console.warn('[jwt.js] couldn\'t call function, session not initialized');
1330 return;
1331 }
1332 };
1333}
1334/**
1335 * Logs the user in. An unauthenticated user will be sent to the
1336 * credentials form and then back to the current page. An authenticated
1337 * user will be sent to the Keycloak server but bounced back to the current
1338 * page right away.
1339 *
1340 * @memberof module:jwt
1341 * @param {Object} options See [options](https://keycloak.gitbooks.io/securing-client-applications-guide/content/v/2.2/topics/oidc/javascript-adapter.html#_login_options) for valid options.
1342 */
1343function login(options) {
1344 if (options === void 0) { options = {}; }
1345 var redirectUri = options.redirectUri || location.href;
1346 options.redirectUri = redirectUri;
1347 return state.keycloak.login(options);
1348}
1349/**
1350 * Navigate to the logout page, end session, then navigate back.
1351 * @memberof module:jwt
1352 */
1353function logout(options) {
1354 if (options === void 0) { options = {}; }
1355 removeToken();
1356 removeRefreshToken();
1357 resetKeyCount(FAIL_COUNT_NAME);
1358 cancelRefreshLoop(); // Cancel update token refresh loop
1359 if (!options.skipRedirect) {
1360 state.keycloak.logout(options);
1361 }
1362}
1363/**
1364 * Navigate to the account registration page.
1365 * @memberof module:jwt
1366 */
1367function register(options) {
1368 state.keycloak.register(options);
1369}
1370/**
1371 * Send current user context to Raven (JS error logging library).
1372 * @memberof module:jwt
1373 * @private
1374 */
1375function setRavenUserContext() {
1376 // once the user info service has returned, use its data to add user
1377 // context to RavenJS, for inclusion in Sentry error reports.
1378 userInfo = getUserInfo();
1379 if (typeof window.Raven !== 'undefined' && typeof window.Raven.setUserContext === 'function') {
1380 log('[jwt.js] sent user context to Raven');
1381 Raven.setUserContext(userInfo);
1382 }
1383}
1384/**
1385 * When the token expires
1386 * @memberof module:jwt
1387 * @private
1388 */
1389function expiresIn() {
1390 try {
1391 return state.keycloak.tokenParsed['exp'] - Math.ceil(new Date().getTime() / 1000) + state.keycloak.timeSkew;
1392 }
1393 catch (e) {
1394 return null;
1395 }
1396}
1397/**
1398 * Send current user context to Raven (JS error logging library).
1399 * @memberof module:jwt
1400 * @private
1401 */
1402function sendToSentry(error, extra) {
1403 // once the user info service has returned, use its data to add user
1404 // context to RavenJS, for inclusion in Sentry error reports.
1405 userInfo = getUserInfo();
1406 if (typeof window.Raven !== 'undefined' && typeof window.Raven.captureException === 'function') {
1407 Raven.setTagsContext({
1408 is_authenticated: isAuthenticated(),
1409 is_token_expired: state.keycloak.authenticated ? state.keycloak.isTokenExpired(0) : null,
1410 token_expires_in: expiresIn(),
1411 // TODO -- if ever upgrading keycloak to upstream see https://github.com/keycloak/keycloak/pull/5008 to ensure this error message stays inline
1412 state_changed: extra && extra.message && extra.message.toLowerCase().indexOf('Cookie sessionId and keycloak sessionId do not match') !== -1
1413 });
1414 Raven.captureException(error, { extra: extra });
1415 }
1416}
1417/**
1418 * Get the count of the $key.
1419 * @return {Number} Get the count of the $key.
1420 * @memberof module:jwt
1421 */
1422function getCountForKey(key) {
1423 try {
1424 return cacheUtils_1.CacheUtils.get(key).then(function (countCache) {
1425 return countCache.value;
1426 }).catch(function (e) {
1427 return 0;
1428 });
1429 }
1430 catch (e) {
1431 return Promise.resolve(0);
1432 }
1433}
1434/**
1435 * Return whether or not the consecutive failure count has been exceeded.
1436 * @memberof module:jwt
1437 * @return {Boolean} has the consecutive failure count been exceeded
1438 */
1439function failCountPassed(key, threshold) {
1440 return getCountForKey(key).then(function (count) {
1441 return count > threshold;
1442 });
1443}
1444/**
1445 * Increment the value of the $key.
1446 * @return {Number} Increment the value of the $key and return new key count.
1447 */
1448function incKeyCount(key) {
1449 return getCountForKey(key).then(function (keyCount) {
1450 var newKeyCount = keyCount + 1;
1451 var newFailCountCache = {
1452 value: newKeyCount
1453 };
1454 cacheUtils_1.CacheUtils.set(key, newFailCountCache);
1455 return newKeyCount;
1456 });
1457}
1458/**
1459 * Reset the value of $key to zero.
1460 */
1461function resetKeyCount(key) {
1462 var newSentryLogCountCache = {
1463 value: 0
1464 };
1465 return cacheUtils_1.CacheUtils.set(key, newSentryLogCountCache);
1466}
1467var Jwt = {
1468 login: initialized(login),
1469 logout: initialized(logout),
1470 register: initialized(register),
1471 hasRole: initialized(hasRole),
1472 isInternal: initialized(isInternal),
1473 isAuthenticated: initialized(isAuthenticated),
1474 getRegisterUrl: initialized(getRegisterUrl),
1475 getLoginUrl: initialized(getLoginUrl),
1476 getLogoutUrl: initialized(getLogoutUrl),
1477 getAccountUrl: initialized(getAccountUrl),
1478 getToken: initialized(getToken),
1479 getStoredTokenValue: initialized(getStoredTokenValue),
1480 getEncodedToken: initialized(getEncodedToken),
1481 getUserInfo: initialized(getUserInfo),
1482 updateToken: initialized(updateToken),
1483 cancelRefreshLoop: initialized(cancelRefreshLoop),
1484 startRefreshLoop: initialized(startRefreshLoop),
1485 isTokenExpired: initialized(isTokenExpired),
1486 onInit: onInit,
1487 onInitError: onInitError,
1488 onAuthRefreshError: onAuthRefreshError,
1489 onAuthRefreshSuccess: onAuthRefreshSuccess,
1490 onAuthLogout: onAuthLogout,
1491 onTokenExpired: onTokenExpired,
1492 onInitialUpdateToken: onInitialUpdateToken,
1493 onTokenMismatch: onTokenMismatch,
1494 onJwtTokenUpdateFailed: onJwtTokenUpdateFailed,
1495 onAuthError: onAuthError,
1496 enableDebugLogging: enableDebugLogging,
1497 disableDebugLogging: disableDebugLogging,
1498 init: init,
1499 reinit: reinit,
1500 _state: state,
1501 getCountForKey: getCountForKey,
1502 failCountPassed: failCountPassed,
1503 expiresIn: expiresIn
1504};
1505exports.default = Jwt;
1506
1507
1508/***/ }),
1509/* 2 */
1510/***/ (function(module, exports, __webpack_require__) {
1511
1512/* WEBPACK VAR INJECTION */(function(module) {var __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_DEFINE_RESULT__;/*
1513 * Copyright 2016 Red Hat, Inc. and/or its affiliates
1514 * and other contributors as indicated by the @author tags.
1515 *
1516 * Licensed under the Apache License, Version 2.0 (the "License");
1517 * you may not use this file except in compliance with the License.
1518 * You may obtain a copy of the License at
1519 *
1520 * http://www.apache.org/licenses/LICENSE-2.0
1521 *
1522 * Unless required by applicable law or agreed to in writing, software
1523 * distributed under the License is distributed on an "AS IS" BASIS,
1524 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1525 * See the License for the specific language governing permissions and
1526 * limitations under the License.
1527 */
1528
1529(function( window, undefined ) {
1530
1531 var Keycloak = function (config) {
1532 if (!(this instanceof Keycloak)) {
1533 return new Keycloak(config);
1534 }
1535
1536 var kc = this;
1537 var adapter;
1538 var refreshQueue = [];
1539 var callbackStorage;
1540
1541 var loginIframe = {
1542 enable: true,
1543 callbackList: [],
1544 interval: 5
1545 };
1546
1547 var scripts = document.getElementsByTagName('script');
1548 for (var i = 0; i < scripts.length; i++) {
1549 if ((scripts[i].src.indexOf('keycloak.js') !== -1 || scripts[i].src.indexOf('keycloak.min.js') !== -1) && scripts[i].src.indexOf('version=') !== -1) {
1550 kc.iframeVersion = scripts[i].src.substring(scripts[i].src.indexOf('version=') + 8).split('&')[0];
1551 }
1552 }
1553
1554 var useNonce = true;
1555
1556 kc.init = function (initOptions) {
1557 kc.authenticated = false;
1558
1559 callbackStorage = createCallbackStorage();
1560
1561 if (initOptions && initOptions.adapter === 'cordova') {
1562 adapter = loadAdapter('cordova');
1563 } else if (initOptions && initOptions.adapter === 'default') {
1564 adapter = loadAdapter();
1565 } else {
1566 if (window.Cordova || window.cordova) {
1567 adapter = loadAdapter('cordova');
1568 } else {
1569 adapter = loadAdapter();
1570 }
1571 }
1572
1573 if (initOptions) {
1574 if (typeof initOptions.useNonce !== 'undefined') {
1575 useNonce = initOptions.useNonce;
1576 }
1577
1578 if (typeof initOptions.checkLoginIframe !== 'undefined') {
1579 loginIframe.enable = initOptions.checkLoginIframe;
1580 }
1581
1582 if (initOptions.checkLoginIframeInterval) {
1583 loginIframe.interval = initOptions.checkLoginIframeInterval;
1584 }
1585
1586 if (initOptions.onLoad === 'login-required') {
1587 kc.loginRequired = true;
1588 }
1589
1590 if (initOptions.responseMode) {
1591 if (initOptions.responseMode === 'query' || initOptions.responseMode === 'fragment') {
1592 kc.responseMode = initOptions.responseMode;
1593 } else {
1594 throw 'Invalid value for responseMode';
1595 }
1596 }
1597
1598 if (initOptions.flow) {
1599 switch (initOptions.flow) {
1600 case 'standard':
1601 kc.responseType = 'code';
1602 break;
1603 case 'implicit':
1604 kc.responseType = 'id_token token';
1605 break;
1606 case 'hybrid':
1607 kc.responseType = 'code id_token token';
1608 break;
1609 default:
1610 throw 'Invalid value for flow';
1611 }
1612 kc.flow = initOptions.flow;
1613 }
1614
1615 if (initOptions.timeSkew != null) {
1616 kc.timeSkew = initOptions.timeSkew;
1617 }
1618 }
1619
1620 if (!kc.responseMode) {
1621 kc.responseMode = 'fragment';
1622 }
1623 if (!kc.responseType) {
1624 kc.responseType = 'code';
1625 kc.flow = 'standard';
1626 }
1627
1628 var promise = createPromise();
1629
1630 var initPromise = createPromise();
1631 initPromise.promise.success(function() {
1632 kc.onReady && kc.onReady(kc.authenticated);
1633 promise.setSuccess(kc.authenticated);
1634 }).error(function(errorData) {
1635 promise.setError(errorData);
1636 });
1637
1638 var configPromise = loadConfig(config);
1639
1640 function onLoad() {
1641 var doLogin = function(prompt) {
1642 if (!prompt) {
1643 options.prompt = 'none';
1644 }
1645 kc.login(options).success(function () {
1646 initPromise.setSuccess();
1647 }).error(function () {
1648 initPromise.setError();
1649 });
1650 }
1651
1652 var options = {};
1653 switch (initOptions.onLoad) {
1654 case 'check-sso':
1655 if (loginIframe.enable) {
1656 setupCheckLoginIframe().success(function() {
1657 checkLoginIframe().success(function () {
1658 doLogin(false);
1659 }).error(function () {
1660 initPromise.setSuccess();
1661 });
1662 });
1663 } else {
1664 doLogin(false);
1665 }
1666 break;
1667 case 'login-required':
1668 doLogin(true);
1669 break;
1670 default:
1671 throw 'Invalid value for onLoad';
1672 }
1673 }
1674
1675 function processInit() {
1676 var callback = parseCallback(window.location.href);
1677
1678 if (callback) {
1679 window.history.replaceState({}, null, callback.newUrl);
1680 }
1681
1682 if (callback && callback.valid) {
1683 return setupCheckLoginIframe().success(function() {
1684 processCallback(callback, initPromise);
1685 }).error(function (e) {
1686 initPromise.setError();
1687 });
1688 } else if (initOptions) {
1689 if (initOptions.token && initOptions.refreshToken) {
1690 setToken(initOptions.token, initOptions.refreshToken, initOptions.idToken);
1691
1692 if (loginIframe.enable) {
1693 setupCheckLoginIframe().success(function() {
1694 checkLoginIframe().success(function () {
1695 kc.onAuthSuccess && kc.onAuthSuccess();
1696 initPromise.setSuccess();
1697 }).error(function () {
1698 setToken(null, null, null);
1699 initPromise.setSuccess();
1700 });
1701 });
1702 } else {
1703 kc.updateToken(-1).success(function() {
1704 kc.onAuthSuccess && kc.onAuthSuccess();
1705 initPromise.setSuccess();
1706 }).error(function() {
1707 kc.onAuthError && kc.onAuthError();
1708 if (initOptions.onLoad) {
1709 onLoad();
1710 } else {
1711 initPromise.setError();
1712 }
1713 });
1714 }
1715 } else if (initOptions.onLoad) {
1716 onLoad();
1717 } else {
1718 initPromise.setSuccess();
1719 }
1720 } else {
1721 initPromise.setSuccess();
1722 }
1723 }
1724
1725 configPromise.success(processInit);
1726 configPromise.error(function() {
1727 promise.setError();
1728 });
1729
1730 return promise.promise;
1731 }
1732
1733 kc.login = function (options) {
1734 return adapter.login(options);
1735 }
1736
1737 kc.createLoginUrl = function(options) {
1738 var state = createUUID();
1739 var nonce = createUUID();
1740
1741 var redirectUri = adapter.redirectUri(options);
1742
1743 var callbackState = {
1744 state: state,
1745 nonce: nonce,
1746 redirectUri: encodeURIComponent(redirectUri)
1747 }
1748
1749 if (options && options.prompt) {
1750 callbackState.prompt = options.prompt;
1751 }
1752
1753 callbackStorage.add(callbackState);
1754
1755 var baseUrl;
1756 if (options && options.action == 'register') {
1757 baseUrl = kc.endpoints.register();
1758 } else {
1759 baseUrl = kc.endpoints.authorize();
1760 }
1761
1762 var scope = (options && options.scope) ? "openid " + options.scope : "openid";
1763
1764 var url = baseUrl
1765 + '?client_id=' + encodeURIComponent(kc.clientId)
1766 + '&redirect_uri=' + encodeURIComponent(redirectUri)
1767 + '&state=' + encodeURIComponent(state)
1768 + '&response_mode=' + encodeURIComponent(kc.responseMode)
1769 + '&response_type=' + encodeURIComponent(kc.responseType)
1770 + '&scope=' + encodeURIComponent(scope);
1771 if (useNonce) {
1772 url = url + '&nonce=' + encodeURIComponent(nonce);
1773 }
1774
1775 if (options && options.prompt) {
1776 url += '&prompt=' + encodeURIComponent(options.prompt);
1777 }
1778
1779 if (options && options.maxAge) {
1780 url += '&max_age=' + encodeURIComponent(options.maxAge);
1781 }
1782
1783 if (options && options.loginHint) {
1784 url += '&login_hint=' + encodeURIComponent(options.loginHint);
1785 }
1786
1787 if (options && options.idpHint) {
1788 url += '&kc_idp_hint=' + encodeURIComponent(options.idpHint);
1789 }
1790
1791 if (options && options.locale) {
1792 url += '&ui_locales=' + encodeURIComponent(options.locale);
1793 }
1794
1795 return url;
1796 }
1797
1798 kc.logout = function(options) {
1799 return adapter.logout(options);
1800 }
1801
1802 kc.createLogoutUrl = function(options) {
1803 var url = kc.endpoints.logout()
1804 + '?redirect_uri=' + encodeURIComponent(adapter.redirectUri(options, false));
1805
1806 return url;
1807 }
1808
1809 kc.register = function (options) {
1810 return adapter.register(options);
1811 }
1812
1813 kc.createRegisterUrl = function(options) {
1814 if (!options) {
1815 options = {};
1816 }
1817 options.action = 'register';
1818 return kc.createLoginUrl(options);
1819 }
1820
1821 kc.createAccountUrl = function(options) {
1822 var realm = getRealmUrl();
1823 var url = undefined;
1824 if (typeof realm !== 'undefined') {
1825 url = realm
1826 + '/account'
1827 + '?referrer=' + encodeURIComponent(kc.clientId)
1828 + '&referrer_uri=' + encodeURIComponent(adapter.redirectUri(options));
1829 }
1830 return url;
1831 }
1832
1833 kc.accountManagement = function() {
1834 return adapter.accountManagement();
1835 }
1836
1837 kc.hasRealmRole = function (role) {
1838 var access = kc.realmAccess;
1839 return !!access && access.roles.indexOf(role) >= 0;
1840 }
1841
1842 kc.hasResourceRole = function(role, resource) {
1843 if (!kc.resourceAccess) {
1844 return false;
1845 }
1846
1847 var access = kc.resourceAccess[resource || kc.clientId];
1848 return !!access && access.roles.indexOf(role) >= 0;
1849 }
1850
1851 kc.loadUserProfile = function() {
1852 var url = getRealmUrl() + '/account';
1853 var req = new XMLHttpRequest();
1854 req.open('GET', url, true);
1855 req.setRequestHeader('Accept', 'application/json');
1856 req.setRequestHeader('Authorization', 'bearer ' + kc.token);
1857
1858 var promise = createPromise();
1859
1860 req.onreadystatechange = function () {
1861 if (req.readyState == 4) {
1862 if (req.status == 200) {
1863 kc.profile = JSON.parse(req.responseText);
1864 promise.setSuccess(kc.profile);
1865 } else {
1866 promise.setError();
1867 }
1868 }
1869 }
1870
1871 req.send();
1872
1873 return promise.promise;
1874 }
1875
1876 kc.loadUserInfo = function() {
1877 var url = kc.endpoints.userinfo();
1878 var req = new XMLHttpRequest();
1879 req.open('GET', url, true);
1880 req.setRequestHeader('Accept', 'application/json');
1881 req.setRequestHeader('Authorization', 'bearer ' + kc.token);
1882
1883 var promise = createPromise();
1884
1885 req.onreadystatechange = function () {
1886 if (req.readyState == 4) {
1887 if (req.status == 200) {
1888 kc.userInfo = JSON.parse(req.responseText);
1889 promise.setSuccess(kc.userInfo);
1890 } else {
1891 promise.setError();
1892 }
1893 }
1894 }
1895
1896 req.send();
1897
1898 return promise.promise;
1899 }
1900
1901 kc.isTokenExpired = function(minValidity) {
1902 if (!kc.tokenParsed || (!kc.refreshToken && kc.flow != 'implicit' )) {
1903 throw 'Not authenticated';
1904 }
1905
1906 if (kc.timeSkew == null) {
1907 console.info('[KEYCLOAK] Unable to determine if token is expired as timeskew is not set');
1908 return true;
1909 }
1910
1911 var expiresIn = kc.tokenParsed['exp'] - Math.ceil(new Date().getTime() / 1000) + kc.timeSkew;
1912 if (minValidity) {
1913 expiresIn -= minValidity;
1914 }
1915 return expiresIn < 0;
1916 }
1917
1918 kc.updateToken = function(minValidity) {
1919 var promise = createPromise();
1920
1921 if (!kc.refreshToken) {
1922 promise.setError();
1923 return promise.promise;
1924 }
1925
1926 minValidity = minValidity || 5;
1927
1928 var exec = function() {
1929 var refreshToken = false;
1930 if (minValidity == -1) {
1931 refreshToken = true;
1932 console.info('[KEYCLOAK] Refreshing token: forced refresh');
1933 } else if (!kc.tokenParsed || kc.isTokenExpired(minValidity)) {
1934 refreshToken = true;
1935 console.info('[KEYCLOAK] Refreshing token: token expired');
1936 }
1937
1938 if (!refreshToken) {
1939 promise.setSuccess(false);
1940 } else {
1941 var params = 'grant_type=refresh_token&' + 'refresh_token=' + kc.refreshToken;
1942 var url = kc.endpoints.token();
1943
1944 refreshQueue.push(promise);
1945
1946 if (refreshQueue.length == 1) {
1947 var req = new XMLHttpRequest();
1948 req.open('POST', url, true);
1949 req.setRequestHeader('Content-type', 'application/x-www-form-urlencoded');
1950 req.withCredentials = true;
1951
1952 if (kc.clientId && kc.clientSecret) {
1953 req.setRequestHeader('Authorization', 'Basic ' + btoa(kc.clientId + ':' + kc.clientSecret));
1954 } else {
1955 params += '&client_id=' + encodeURIComponent(kc.clientId);
1956 }
1957
1958 var timeLocal = new Date().getTime();
1959
1960 req.onreadystatechange = function () {
1961 if (req.readyState == 4) {
1962 if (req.status == 200) {
1963 console.info('[KEYCLOAK] Token refreshed');
1964
1965 timeLocal = (timeLocal + new Date().getTime()) / 2;
1966
1967 var tokenResponse = JSON.parse(req.responseText);
1968
1969 setToken(tokenResponse['access_token'], tokenResponse['refresh_token'], tokenResponse['id_token'], timeLocal);
1970
1971 kc.onAuthRefreshSuccess && kc.onAuthRefreshSuccess();
1972 for (var p = refreshQueue.pop(); p != null; p = refreshQueue.pop()) {
1973 p.setSuccess(true);
1974 }
1975 } else {
1976 console.warn('[KEYCLOAK] Failed to refresh token');
1977
1978 kc.onAuthRefreshError && kc.onAuthRefreshError();
1979 for (var p = refreshQueue.pop(); p != null; p = refreshQueue.pop()) {
1980 p.setError(true);
1981 }
1982 }
1983 }
1984 };
1985
1986 req.send(params);
1987 }
1988 }
1989 }
1990
1991 if (loginIframe.enable) {
1992 var iframePromise = checkLoginIframe();
1993 iframePromise.success(function() {
1994 exec();
1995 }).error(function(e) {
1996 promise.setError(e);
1997 });
1998 } else {
1999 exec();
2000 }
2001
2002 return promise.promise;
2003 }
2004
2005 kc.clearToken = function() {
2006 if (kc.token) {
2007 setToken(null, null, null);
2008 kc.onAuthLogout && kc.onAuthLogout();
2009 if (kc.loginRequired) {
2010 kc.login();
2011 }
2012 }
2013 }
2014
2015 function getRealmUrl() {
2016 if (typeof kc.authServerUrl !== 'undefined') {
2017 if (kc.authServerUrl.charAt(kc.authServerUrl.length - 1) == '/') {
2018 return kc.authServerUrl + 'realms/' + encodeURIComponent(kc.realm);
2019 } else {
2020 return kc.authServerUrl + '/realms/' + encodeURIComponent(kc.realm);
2021 }
2022 } else {
2023 return undefined;
2024 }
2025 }
2026
2027 function getOrigin() {
2028 if (!window.location.origin) {
2029 return window.location.protocol + "//" + window.location.hostname + (window.location.port ? ':' + window.location.port: '');
2030 } else {
2031 return window.location.origin;
2032 }
2033 }
2034
2035 function processCallback(oauth, promise) {
2036 var code = oauth.code;
2037 var error = oauth.error;
2038 var prompt = oauth.prompt;
2039
2040 var timeLocal = new Date().getTime();
2041
2042 if (error) {
2043 if (prompt != 'none') {
2044 var errorData = { error: error, error_description: oauth.error_description };
2045 kc.onAuthError && kc.onAuthError(errorData);
2046 promise && promise.setError(errorData);
2047 } else {
2048 promise && promise.setSuccess();
2049 }
2050 return;
2051 } else if ((kc.flow != 'standard') && (oauth.access_token || oauth.id_token)) {
2052 authSuccess(oauth.access_token, null, oauth.id_token, true);
2053 }
2054
2055 if ((kc.flow != 'implicit') && code) {
2056 var params = 'code=' + code + '&grant_type=authorization_code';
2057 var url = kc.endpoints.token();
2058
2059 var req = new XMLHttpRequest();
2060 req.open('POST', url, true);
2061 req.setRequestHeader('Content-type', 'application/x-www-form-urlencoded');
2062
2063 if (kc.clientId && kc.clientSecret) {
2064 req.setRequestHeader('Authorization', 'Basic ' + btoa(kc.clientId + ':' + kc.clientSecret));
2065 } else {
2066 params += '&client_id=' + encodeURIComponent(kc.clientId);
2067 }
2068
2069 params += '&redirect_uri=' + oauth.redirectUri;
2070
2071 req.withCredentials = true;
2072
2073 req.onreadystatechange = function() {
2074 if (req.readyState == 4) {
2075 if (req.status == 200) {
2076
2077 var tokenResponse = JSON.parse(req.responseText);
2078 authSuccess(tokenResponse['access_token'], tokenResponse['refresh_token'], tokenResponse['id_token'], kc.flow === 'standard');
2079 } else {
2080 kc.onAuthError && kc.onAuthError();
2081 promise && promise.setError();
2082 }
2083 }
2084 };
2085
2086 req.send(params);
2087 }
2088
2089 function authSuccess(accessToken, refreshToken, idToken, fulfillPromise) {
2090 timeLocal = (timeLocal + new Date().getTime()) / 2;
2091
2092 setToken(accessToken, refreshToken, idToken, timeLocal);
2093
2094 if (useNonce && ((kc.tokenParsed && kc.tokenParsed.nonce != oauth.storedNonce) ||
2095 (kc.refreshTokenParsed && kc.refreshTokenParsed.nonce != oauth.storedNonce) ||
2096 (kc.idTokenParsed && kc.idTokenParsed.nonce != oauth.storedNonce))) {
2097
2098 console.info('[KEYCLOAK] Invalid nonce, clearing token');
2099 kc.clearToken();
2100 promise && promise.setError();
2101 } else {
2102 if (fulfillPromise) {
2103 kc.onAuthSuccess && kc.onAuthSuccess();
2104 promise && promise.setSuccess();
2105 }
2106 }
2107 }
2108
2109 }
2110
2111 function loadConfig(url) {
2112 var promise = createPromise();
2113 var configUrl;
2114
2115 if (!config) {
2116 configUrl = 'keycloak.json';
2117 } else if (typeof config === 'string') {
2118 configUrl = config;
2119 }
2120
2121 function setupOidcEndoints(oidcConfiguration) {
2122 if (! oidcConfiguration) {
2123 kc.endpoints = {
2124 authorize: function() {
2125 return getRealmUrl() + '/protocol/openid-connect/auth';
2126 },
2127 token: function() {
2128 return getRealmUrl() + '/protocol/openid-connect/token';
2129 },
2130 logout: function() {
2131 return getRealmUrl() + '/protocol/openid-connect/logout';
2132 },
2133 checkSessionIframe: function() {
2134 var src = getRealmUrl() + '/protocol/openid-connect/login-status-iframe.html';
2135 if (kc.iframeVersion) {
2136 src = src + '?version=' + kc.iframeVersion;
2137 }
2138 return src;
2139 },
2140 register: function() {
2141 return getRealmUrl() + '/protocol/openid-connect/registrations';
2142 },
2143 userinfo: function() {
2144 return getRealmUrl() + '/protocol/openid-connect/userinfo';
2145 }
2146 };
2147 } else {
2148 kc.endpoints = {
2149 authorize: function() {
2150 return oidcConfiguration.authorization_endpoint;
2151 },
2152 token: function() {
2153 return oidcConfiguration.token_endpoint;
2154 },
2155 logout: function() {
2156 if (!oidcConfiguration.end_session_endpoint) {
2157 throw "Not supported by the OIDC server";
2158 }
2159 return oidcConfiguration.end_session_endpoint;
2160 },
2161 checkSessionIframe: function() {
2162 if (!oidcConfiguration.check_session_iframe) {
2163 throw "Not supported by the OIDC server";
2164 }
2165 return oidcConfiguration.check_session_iframe;
2166 },
2167 register: function() {
2168 throw 'Redirection to "Register user" page not supported in standard OIDC mode';
2169 },
2170 userinfo: function() {
2171 if (!oidcConfiguration.userinfo_endpoint) {
2172 throw "Not supported by the OIDC server";
2173 }
2174 return oidcConfiguration.userinfo_endpoint;
2175 }
2176 }
2177 }
2178 }
2179
2180 if (configUrl) {
2181 var req = new XMLHttpRequest();
2182 req.open('GET', configUrl, true);
2183 req.setRequestHeader('Accept', 'application/json');
2184
2185 req.onreadystatechange = function () {
2186 if (req.readyState == 4) {
2187 if (req.status == 200 || fileLoaded(req)) {
2188 var config = JSON.parse(req.responseText);
2189
2190 kc.authServerUrl = config['auth-server-url'];
2191 kc.realm = config['realm'];
2192 kc.clientId = config['resource'];
2193 kc.clientSecret = (config['credentials'] || {})['secret'];
2194 setupOidcEndoints(null);
2195 promise.setSuccess();
2196 } else {
2197 promise.setError();
2198 }
2199 }
2200 };
2201
2202 req.send();
2203 } else {
2204 if (!config.clientId) {
2205 throw 'clientId missing';
2206 }
2207
2208 kc.clientId = config.clientId;
2209 kc.clientSecret = (config.credentials || {}).secret;
2210
2211 var oidcProvider = config['oidcProvider'];
2212 if (!oidcProvider) {
2213 if (!config['url']) {
2214 var scripts = document.getElementsByTagName('script');
2215 for (var i = 0; i < scripts.length; i++) {
2216 if (scripts[i].src.match(/.*keycloak\.js/)) {
2217 config.url = scripts[i].src.substr(0, scripts[i].src.indexOf('/js/keycloak.js'));
2218 break;
2219 }
2220 }
2221 }
2222 if (!config.realm) {
2223 throw 'realm missing';
2224 }
2225
2226 kc.authServerUrl = config.url;
2227 kc.realm = config.realm;
2228 setupOidcEndoints(null);
2229 promise.setSuccess();
2230 } else {
2231 if (typeof oidcProvider === 'string') {
2232 var oidcProviderConfigUrl;
2233 if (oidcProvider.charAt(oidcProvider.length - 1) == '/') {
2234 oidcProviderConfigUrl = oidcProvider + '.well-known/openid-configuration';
2235 } else {
2236 oidcProviderConfigUrl = oidcProvider + '/.well-known/openid-configuration';
2237 }
2238 var req = new XMLHttpRequest();
2239 req.open('GET', oidcProviderConfigUrl, true);
2240 req.setRequestHeader('Accept', 'application/json');
2241
2242 req.onreadystatechange = function () {
2243 if (req.readyState == 4) {
2244 if (req.status == 200 || fileLoaded(req)) {
2245 var oidcProviderConfig = JSON.parse(req.responseText);
2246 setupOidcEndoints(oidcProviderConfig);
2247 promise.setSuccess();
2248 } else {
2249 promise.setError();
2250 }
2251 }
2252 };
2253
2254 req.send();
2255 } else {
2256 setupOidcEndoints(oidcProvider);
2257 promise.setSuccess();
2258 }
2259 }
2260 }
2261
2262 return promise.promise;
2263 }
2264
2265 function fileLoaded(xhr) {
2266 return xhr.status == 0 && xhr.responseText && xhr.responseURL.startsWith('file:');
2267 }
2268
2269 function setToken(token, refreshToken, idToken, timeLocal) {
2270 if (kc.tokenTimeoutHandle) {
2271 clearTimeout(kc.tokenTimeoutHandle);
2272 kc.tokenTimeoutHandle = null;
2273 }
2274
2275 if (refreshToken) {
2276 kc.refreshToken = refreshToken;
2277 kc.refreshTokenParsed = decodeToken(refreshToken);
2278 } else {
2279 delete kc.refreshToken;
2280 delete kc.refreshTokenParsed;
2281 }
2282
2283 if (idToken) {
2284 kc.idToken = idToken;
2285 kc.idTokenParsed = decodeToken(idToken);
2286 } else {
2287 delete kc.idToken;
2288 delete kc.idTokenParsed;
2289 }
2290
2291 if (token) {
2292 kc.token = token;
2293 kc.tokenParsed = decodeToken(token);
2294 kc.sessionId = kc.tokenParsed.session_state;
2295 kc.authenticated = true;
2296 kc.subject = kc.tokenParsed.sub;
2297 kc.realmAccess = kc.tokenParsed.realm_access;
2298 kc.resourceAccess = kc.tokenParsed.resource_access;
2299
2300 if (timeLocal) {
2301 kc.timeSkew = Math.floor(timeLocal / 1000) - kc.tokenParsed.iat;
2302 }
2303
2304 if (kc.timeSkew != null) {
2305 console.info('[KEYCLOAK] Estimated time difference between browser and server is ' + kc.timeSkew + ' seconds');
2306
2307 if (kc.onTokenExpired) {
2308 var expiresIn = (kc.tokenParsed['exp'] - (new Date().getTime() / 1000) + kc.timeSkew) * 1000;
2309 console.info('[KEYCLOAK] Token expires in ' + Math.round(expiresIn / 1000) + ' s');
2310 if (expiresIn <= 0) {
2311 kc.onTokenExpired();
2312 } else {
2313 kc.tokenTimeoutHandle = setTimeout(kc.onTokenExpired, expiresIn);
2314 }
2315 }
2316 }
2317 } else {
2318 delete kc.token;
2319 delete kc.tokenParsed;
2320 delete kc.subject;
2321 delete kc.realmAccess;
2322 delete kc.resourceAccess;
2323
2324 kc.authenticated = false;
2325 }
2326 }
2327
2328 function decodeToken(str) {
2329 str = str.split('.')[1];
2330
2331 str = str.replace('/-/g', '+');
2332 str = str.replace('/_/g', '/');
2333 switch (str.length % 4)
2334 {
2335 case 0:
2336 break;
2337 case 2:
2338 str += '==';
2339 break;
2340 case 3:
2341 str += '=';
2342 break;
2343 default:
2344 throw 'Invalid token';
2345 }
2346
2347 str = (str + '===').slice(0, str.length + (str.length % 4));
2348 str = str.replace(/-/g, '+').replace(/_/g, '/');
2349
2350 str = decodeURIComponent(escape(atob(str)));
2351
2352 str = JSON.parse(str);
2353 return str;
2354 }
2355
2356 function createUUID() {
2357 var s = [];
2358 var hexDigits = '0123456789abcdef';
2359 for (var i = 0; i < 36; i++) {
2360 s[i] = hexDigits.substr(Math.floor(Math.random() * 0x10), 1);
2361 }
2362 s[14] = '4';
2363 s[19] = hexDigits.substr((s[19] & 0x3) | 0x8, 1);
2364 s[8] = s[13] = s[18] = s[23] = '-';
2365 var uuid = s.join('');
2366 return uuid;
2367 }
2368
2369 kc.callback_id = 0;
2370
2371 function createCallbackId() {
2372 var id = '<id: ' + (kc.callback_id++) + (Math.random()) + '>';
2373 return id;
2374
2375 }
2376
2377 function parseCallback(url) {
2378 var oauth = parseCallbackUrl(url);
2379 if (!oauth) {
2380 return;
2381 }
2382
2383 var oauthState = callbackStorage.get(oauth.state);
2384
2385 if (oauthState) {
2386 oauth.valid = true;
2387 oauth.redirectUri = oauthState.redirectUri;
2388 oauth.storedNonce = oauthState.nonce;
2389 oauth.prompt = oauthState.prompt;
2390 }
2391
2392 return oauth;
2393 }
2394
2395 function parseCallbackUrl(url) {
2396 var supportedParams;
2397 switch (kc.flow) {
2398 case 'standard':
2399 supportedParams = ['code', 'state', 'session_state'];
2400 break;
2401 case 'implicit':
2402 supportedParams = ['access_token', 'id_token', 'state', 'session_state'];
2403 break;
2404 case 'hybrid':
2405 supportedParams = ['access_token', 'id_token', 'code', 'state', 'session_state'];
2406 break;
2407 }
2408
2409 supportedParams.push('error');
2410 supportedParams.push('error_description');
2411 supportedParams.push('error_uri');
2412
2413 var queryIndex = url.indexOf('?');
2414 var fragmentIndex = url.indexOf('#');
2415
2416 var newUrl;
2417 var parsed;
2418
2419 if (kc.responseMode === 'query' && queryIndex !== -1) {
2420 newUrl = url.substring(0, queryIndex);
2421 parsed = parseCallbackParams(url.substring(queryIndex + 1, fragmentIndex !== -1 ? fragmentIndex : url.length), supportedParams);
2422 if (parsed.paramsString !== '') {
2423 newUrl += '?' + parsed.paramsString;
2424 }
2425 if (fragmentIndex !== -1) {
2426 newUrl += url.substring(fragmentIndex);
2427 }
2428 } else if (kc.responseMode === 'fragment' && fragmentIndex !== -1) {
2429 newUrl = url.substring(0, fragmentIndex);
2430 parsed = parseCallbackParams(url.substring(fragmentIndex + 1), supportedParams);
2431 if (parsed.paramsString !== '') {
2432 newUrl += '#' + parsed.paramsString;
2433 }
2434 }
2435
2436 if (parsed && parsed.oauthParams) {
2437 if (kc.flow === 'standard' || kc.flow === 'hybrid') {
2438 if ((parsed.oauthParams.code || parsed.oauthParams.error) && parsed.oauthParams.state) {
2439 parsed.oauthParams.newUrl = newUrl;
2440 return parsed.oauthParams;
2441 }
2442 } else if (kc.flow === 'implicit') {
2443 if ((parsed.oauthParams.access_token || parsed.oauthParams.error) && parsed.oauthParams.state) {
2444 parsed.oauthParams.newUrl = newUrl;
2445 return parsed.oauthParams;
2446 }
2447 }
2448 }
2449 }
2450
2451 function parseCallbackParams(paramsString, supportedParams) {
2452 var p = paramsString.split('&');
2453 var result = {
2454 paramsString: '',
2455 oauthParams: {}
2456 }
2457 for (var i = 0; i < p.length; i++) {
2458 var t = p[i].split('=');
2459 if (supportedParams.indexOf(t[0]) !== -1) {
2460 result.oauthParams[t[0]] = t[1];
2461 } else {
2462 if (result.paramsString !== '') {
2463 result.paramsString += '&';
2464 }
2465 result.paramsString += p[i];
2466 }
2467 }
2468 return result;
2469 }
2470
2471 function createPromise() {
2472 if (typeof Promise === "function") {
2473 return createNativePromise();
2474 } else {
2475 return createLegacyPromise();
2476 }
2477 }
2478
2479 function createNativePromise() {
2480 // Need to create a native Promise which also preserves the
2481 // interface of the custom promise type previously used by the API
2482 var p = {
2483 setSuccess: function(result) {
2484 p.success = true;
2485 p.resolve(result);
2486 },
2487
2488 setError: function(result) {
2489 p.success = false;
2490 p.reject(result);
2491 }
2492 };
2493 p.promise = new Promise(function(resolve, reject) {
2494 p.resolve = resolve;
2495 p.reject = reject;
2496 });
2497 p.promise.success = function(callback) {
2498 p.promise.then(callback);
2499 return p.promise;
2500 }
2501 p.promise.error = function(callback) {
2502 p.promise.catch(callback);
2503 return p.promise;
2504 }
2505 return p;
2506 }
2507
2508 function createLegacyPromise() {
2509 var p = {
2510 setSuccess: function(result) {
2511 p.success = true;
2512 p.result = result;
2513 if (p.successCallback) {
2514 p.successCallback(result);
2515 }
2516 },
2517
2518 setError: function(result) {
2519 p.error = true;
2520 p.result = result;
2521 if (p.errorCallback) {
2522 p.errorCallback(result);
2523 }
2524 },
2525
2526 promise: {
2527 success: function(callback) {
2528 if (p.success) {
2529 callback(p.result);
2530 } else if (!p.error) {
2531 p.successCallback = callback;
2532 }
2533 return p.promise;
2534 },
2535 error: function(callback) {
2536 if (p.error) {
2537 callback(p.result);
2538 } else if (!p.success) {
2539 p.errorCallback = callback;
2540 }
2541 return p.promise;
2542 }
2543 }
2544 }
2545 return p;
2546 }
2547
2548 function setupCheckLoginIframe() {
2549 var promise = createPromise();
2550
2551 if (!loginIframe.enable) {
2552 promise.setSuccess();
2553 return promise.promise;
2554 }
2555
2556 if (loginIframe.iframe) {
2557 promise.setSuccess();
2558 return promise.promise;
2559 }
2560
2561 var iframe = document.createElement('iframe');
2562 loginIframe.iframe = iframe;
2563
2564 iframe.onload = function() {
2565 var authUrl = kc.endpoints.authorize();
2566 if (authUrl.charAt(0) === '/') {
2567 loginIframe.iframeOrigin = getOrigin();
2568 } else {
2569 loginIframe.iframeOrigin = authUrl.substring(0, authUrl.indexOf('/', 8));
2570 }
2571 promise.setSuccess();
2572
2573 setTimeout(check, loginIframe.interval * 1000);
2574 }
2575
2576 var src = kc.endpoints.checkSessionIframe();
2577 iframe.setAttribute('src', src );
2578 iframe.setAttribute('title', 'keycloak-session-iframe' );
2579 iframe.style.display = 'none';
2580 document.body.appendChild(iframe);
2581
2582 var messageCallback = function(event) {
2583 if ((event.origin !== loginIframe.iframeOrigin) || (loginIframe.iframe.contentWindow !== event.source)) {
2584 return;
2585 }
2586
2587 if (!(event.data == 'unchanged' || event.data == 'changed' || event.data == 'error')) {
2588 return;
2589 }
2590
2591
2592 if (event.data != 'unchanged') {
2593 kc.clearToken();
2594 }
2595
2596 var callbacks = loginIframe.callbackList.splice(0, loginIframe.callbackList.length);
2597
2598 for (var i = callbacks.length - 1; i >= 0; --i) {
2599 var promise = callbacks[i];
2600 if (event.data == 'unchanged') {
2601 promise.setSuccess();
2602 } else {
2603 promise.setError(new Error('Cookie sessionId and keycloak sessionId do not match. Please logout and log back in.'));
2604 }
2605 }
2606 };
2607
2608 window.addEventListener('message', messageCallback, false);
2609
2610 var check = function() {
2611 checkLoginIframe();
2612 if (kc.token) {
2613 setTimeout(check, loginIframe.interval * 1000);
2614 }
2615 };
2616
2617 return promise.promise;
2618 }
2619
2620 function checkLoginIframe() {
2621 var promise = createPromise();
2622
2623 if (loginIframe.iframe && loginIframe.iframeOrigin ) {
2624 var msg = kc.clientId + ' ' + kc.sessionId;
2625 loginIframe.callbackList.push(promise);
2626 var origin = loginIframe.iframeOrigin;
2627 if (loginIframe.callbackList.length == 1) {
2628 loginIframe.iframe.contentWindow.postMessage(msg, origin);
2629 }
2630 } else {
2631 promise.setSuccess();
2632 }
2633
2634 return promise.promise;
2635 }
2636
2637 function loadAdapter(type) {
2638 if (!type || type == 'default') {
2639 return {
2640 login: function(options) {
2641 window.location.href = kc.createLoginUrl(options);
2642 return createPromise().promise;
2643 },
2644
2645 logout: function(options) {
2646 window.location.href = kc.createLogoutUrl(options);
2647 return createPromise().promise;
2648 },
2649
2650 register: function(options) {
2651 window.location.href = kc.createRegisterUrl(options);
2652 return createPromise().promise;
2653 },
2654
2655 accountManagement : function() {
2656 var accountUrl = kc.createAccountUrl();
2657 if (typeof accountUrl !== 'undefined') {
2658 window.location.href = accountUrl;
2659 } else {
2660 throw "Not supported by the OIDC server";
2661 }
2662 return createPromise().promise;
2663 },
2664
2665 redirectUri: function(options, encodeHash) {
2666 if (arguments.length == 1) {
2667 encodeHash = true;
2668 }
2669
2670 if (options && options.redirectUri) {
2671 return options.redirectUri;
2672 } else if (kc.redirectUri) {
2673 return kc.redirectUri;
2674 } else {
2675 return location.href;
2676 }
2677 }
2678 };
2679 }
2680
2681 if (type == 'cordova') {
2682 loginIframe.enable = false;
2683 var cordovaOpenWindowWrapper = function(loginUrl, target, options) {
2684 if (window.cordova && window.cordova.InAppBrowser) {
2685 // Use inappbrowser for IOS and Android if available
2686 return window.cordova.InAppBrowser.open(loginUrl, target, options);
2687 } else {
2688 return window.open(loginUrl, target, options);
2689 }
2690 };
2691 return {
2692 login: function(options) {
2693 var promise = createPromise();
2694
2695 var o = 'location=no';
2696 if (options && options.prompt == 'none') {
2697 o += ',hidden=yes';
2698 }
2699
2700 var loginUrl = kc.createLoginUrl(options);
2701 var ref = cordovaOpenWindowWrapper(loginUrl, '_blank', o);
2702 var completed = false;
2703
2704 ref.addEventListener('loadstart', function(event) {
2705 if (event.url.indexOf('http://localhost') == 0) {
2706 var callback = parseCallback(event.url);
2707 processCallback(callback, promise);
2708 ref.close();
2709 completed = true;
2710 }
2711 });
2712
2713 ref.addEventListener('loaderror', function(event) {
2714 if (!completed) {
2715 if (event.url.indexOf('http://localhost') == 0) {
2716 var callback = parseCallback(event.url);
2717 processCallback(callback, promise);
2718 ref.close();
2719 completed = true;
2720 } else {
2721 promise.setError();
2722 ref.close();
2723 }
2724 }
2725 });
2726
2727 return promise.promise;
2728 },
2729
2730 logout: function(options) {
2731 var promise = createPromise();
2732
2733 var logoutUrl = kc.createLogoutUrl(options);
2734 var ref = cordovaOpenWindowWrapper(logoutUrl, '_blank', 'location=no,hidden=yes');
2735
2736 var error;
2737
2738 ref.addEventListener('loadstart', function(event) {
2739 if (event.url.indexOf('http://localhost') == 0) {
2740 ref.close();
2741 }
2742 });
2743
2744 ref.addEventListener('loaderror', function(event) {
2745 if (event.url.indexOf('http://localhost') == 0) {
2746 ref.close();
2747 } else {
2748 error = true;
2749 ref.close();
2750 }
2751 });
2752
2753 ref.addEventListener('exit', function(event) {
2754 if (error) {
2755 promise.setError();
2756 } else {
2757 kc.clearToken();
2758 promise.setSuccess();
2759 }
2760 });
2761
2762 return promise.promise;
2763 },
2764
2765 register : function() {
2766 var registerUrl = kc.createRegisterUrl();
2767 var ref = cordovaOpenWindowWrapper(registerUrl, '_blank', 'location=no');
2768 ref.addEventListener('loadstart', function(event) {
2769 if (event.url.indexOf('http://localhost') == 0) {
2770 ref.close();
2771 }
2772 });
2773 },
2774
2775 accountManagement : function() {
2776 var accountUrl = kc.createAccountUrl();
2777 if (typeof accountUrl !== 'undefined') {
2778 var ref = cordovaOpenWindowWrapper(accountUrl, '_blank', 'location=no');
2779 ref.addEventListener('loadstart', function(event) {
2780 if (event.url.indexOf('http://localhost') == 0) {
2781 ref.close();
2782 }
2783 });
2784 } else {
2785 throw "Not supported by the OIDC server";
2786 }
2787 },
2788
2789 redirectUri: function(options) {
2790 return 'http://localhost';
2791 }
2792 }
2793 }
2794
2795 throw 'invalid adapter type: ' + type;
2796 }
2797
2798 var LocalStorage = function() {
2799 if (!(this instanceof LocalStorage)) {
2800 return new LocalStorage();
2801 }
2802
2803 localStorage.setItem('kc-test', 'test');
2804 localStorage.removeItem('kc-test');
2805
2806 var cs = this;
2807
2808 function clearExpired() {
2809 var time = new Date().getTime();
2810 for (var i = 0; i < localStorage.length; i++) {
2811 var key = localStorage.key(i);
2812 if (key && key.indexOf('kc-callback-') == 0) {
2813 var value = localStorage.getItem(key);
2814 if (value) {
2815 try {
2816 var expires = JSON.parse(value).expires;
2817 if (!expires || expires < time) {
2818 localStorage.removeItem(key);
2819 }
2820 } catch (err) {
2821 localStorage.removeItem(key);
2822 }
2823 }
2824 }
2825 }
2826 }
2827
2828 cs.get = function(state) {
2829 if (!state) {
2830 return;
2831 }
2832
2833 var key = 'kc-callback-' + state;
2834 var value = localStorage.getItem(key);
2835 if (value) {
2836 localStorage.removeItem(key);
2837 value = JSON.parse(value);
2838 }
2839
2840 clearExpired();
2841 return value;
2842 };
2843
2844 cs.add = function(state) {
2845 clearExpired();
2846
2847 var key = 'kc-callback-' + state.state;
2848 state.expires = new Date().getTime() + (60 * 60 * 1000);
2849 localStorage.setItem(key, JSON.stringify(state));
2850 };
2851 };
2852
2853 var CookieStorage = function() {
2854 if (!(this instanceof CookieStorage)) {
2855 return new CookieStorage();
2856 }
2857
2858 var cs = this;
2859
2860 cs.get = function(state) {
2861 if (!state) {
2862 return;
2863 }
2864
2865 var value = getCookie('kc-callback-' + state);
2866 setCookie('kc-callback-' + state, '', cookieExpiration(-100));
2867 if (value) {
2868 return JSON.parse(value);
2869 }
2870 };
2871
2872 cs.add = function(state) {
2873 setCookie('kc-callback-' + state.state, JSON.stringify(state), cookieExpiration(60));
2874 };
2875
2876 cs.removeItem = function(key) {
2877 setCookie(key, '', cookieExpiration(-100));
2878 };
2879
2880 var cookieExpiration = function (minutes) {
2881 var exp = new Date();
2882 exp.setTime(exp.getTime() + (minutes*60*1000));
2883 return exp;
2884 };
2885
2886 var getCookie = function (key) {
2887 var name = key + '=';
2888 var ca = document.cookie.split(';');
2889 for (var i = 0; i < ca.length; i++) {
2890 var c = ca[i];
2891 while (c.charAt(0) == ' ') {
2892 c = c.substring(1);
2893 }
2894 if (c.indexOf(name) == 0) {
2895 return c.substring(name.length, c.length);
2896 }
2897 }
2898 return '';
2899 };
2900
2901 var setCookie = function (key, value, expirationDate) {
2902 var cookie = key + '=' + value + '; '
2903 + 'expires=' + expirationDate.toUTCString() + '; ';
2904 document.cookie = cookie;
2905 }
2906 };
2907
2908 function createCallbackStorage() {
2909 try {
2910 return new LocalStorage();
2911 } catch (err) {
2912 }
2913
2914 return new CookieStorage();
2915 }
2916 }
2917
2918 if ( typeof module === "object" && module && typeof module.exports === "object" ) {
2919 module.exports = Keycloak;
2920 } else {
2921 window.Keycloak = Keycloak;
2922
2923 if ( true ) {
2924 !(__WEBPACK_AMD_DEFINE_ARRAY__ = [], __WEBPACK_AMD_DEFINE_RESULT__ = (function () { return Keycloak; }).apply(exports, __WEBPACK_AMD_DEFINE_ARRAY__),
2925 __WEBPACK_AMD_DEFINE_RESULT__ !== undefined && (module.exports = __WEBPACK_AMD_DEFINE_RESULT__));
2926 }
2927 }
2928})( window );
2929/* WEBPACK VAR INJECTION */}.call(exports, __webpack_require__(3)(module)))
2930
2931/***/ }),
2932/* 3 */
2933/***/ (function(module, exports) {
2934
2935module.exports = function(module) {
2936 if(!module.webpackPolyfill) {
2937 module.deprecate = function() {};
2938 module.paths = [];
2939 // module.parent = undefined by default
2940 if(!module.children) module.children = [];
2941 Object.defineProperty(module, "loaded", {
2942 enumerable: true,
2943 get: function() {
2944 return module.l;
2945 }
2946 });
2947 Object.defineProperty(module, "id", {
2948 enumerable: true,
2949 get: function() {
2950 return module.i;
2951 }
2952 });
2953 module.webpackPolyfill = 1;
2954 }
2955 return module;
2956};
2957
2958
2959/***/ }),
2960/* 4 */
2961/***/ (function(module, exports, __webpack_require__) {
2962
2963"use strict";
2964
2965Object.defineProperty(exports, "__esModule", { value: true });
2966var localForage = __webpack_require__(5);
2967// const MINUTES_1 = 60 * 1000;
2968// const HOURS_1 = 60 * MINUTES_1;
2969// const DAYS_1 = 24 * HOURS_1;
2970// const MONTHS_1 = 30 * DAYS_1;
2971exports.CACHE_STORAGE_NAME = 'jwt-redhat-lf';
2972localForage.config({
2973 name: exports.CACHE_STORAGE_NAME,
2974 driver: [localForage.LOCALSTORAGE]
2975});
2976var CacheUtils = /** @class */ (function () {
2977 function CacheUtils() {
2978 }
2979 CacheUtils.set = function (key, obj, options) {
2980 if (!obj.lastModifiedDate) {
2981 obj.lastModifiedDate = (new Date()).toISOString();
2982 }
2983 try {
2984 return localForage.setItem(key, obj);
2985 }
2986 catch (e) {
2987 console.warn("Unable to set " + key + " due to: " + e.message);
2988 }
2989 };
2990 CacheUtils.get = function (key) {
2991 try {
2992 return localForage.getItem(key);
2993 }
2994 catch (e) {
2995 console.warn("Unable to get " + key + " due to: " + e.message);
2996 }
2997 };
2998 CacheUtils.delete = function (key) {
2999 try {
3000 return localForage.removeItem(key);
3001 }
3002 catch (e) {
3003 console.warn("Unable to delete " + key + " due to: " + e.message);
3004 }
3005 };
3006 CacheUtils.clear = function () {
3007 try {
3008 return localForage.clear();
3009 }
3010 catch (e) {
3011 console.warn("Unable to clear all cache: " + e.message);
3012 }
3013 };
3014 CacheUtils.keys = function (text) {
3015 try {
3016 if (text) {
3017 return localForage.keys().then(function (keys) {
3018 if (keys) {
3019 return keys.filter(function (key) { return key.indexOf(text) !== -1; });
3020 }
3021 else {
3022 return [];
3023 }
3024 });
3025 }
3026 else {
3027 return localForage.keys();
3028 }
3029 }
3030 catch (e) {
3031 console.warn("Unable to get keys containing " + text + " due to: " + e.message);
3032 }
3033 };
3034 CacheUtils.setInSessionStorage = function (key, value) {
3035 try {
3036 sessionStorage.setItem(key, value);
3037 }
3038 catch (e) {
3039 console.warn("Could not set key: " + key + ", value: " + value + " in sessionStorage");
3040 }
3041 };
3042 CacheUtils.getInSessionStorage = function (key) {
3043 try {
3044 return sessionStorage.getItem(key);
3045 }
3046 catch (e) {
3047 console.warn("Could not get key: " + key + " in sessionStorage");
3048 }
3049 };
3050 return CacheUtils;
3051}());
3052exports.CacheUtils = CacheUtils;
3053
3054
3055/***/ }),
3056/* 5 */
3057/***/ (function(module, exports) {
3058
3059/*!
3060 localForage -- Offline Storage, Improved
3061 Version 1.7.1
3062 https://localforage.github.io/localForage
3063 (c) 2013-2017 Mozilla, Apache License 2.0
3064*/
3065(function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.localforage = f()}})(function(){var define,module,exports;return (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw (f.code="MODULE_NOT_FOUND", f)}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(_dereq_,module,exports){
3066(function (global){
3067'use strict';
3068var Mutation = global.MutationObserver || global.WebKitMutationObserver;
3069
3070var scheduleDrain;
3071
3072{
3073 if (Mutation) {
3074 var called = 0;
3075 var observer = new Mutation(nextTick);
3076 var element = global.document.createTextNode('');
3077 observer.observe(element, {
3078 characterData: true
3079 });
3080 scheduleDrain = function () {
3081 element.data = (called = ++called % 2);
3082 };
3083 } else if (!global.setImmediate && typeof global.MessageChannel !== 'undefined') {
3084 var channel = new global.MessageChannel();
3085 channel.port1.onmessage = nextTick;
3086 scheduleDrain = function () {
3087 channel.port2.postMessage(0);
3088 };
3089 } else if ('document' in global && 'onreadystatechange' in global.document.createElement('script')) {
3090 scheduleDrain = function () {
3091
3092 // Create a <script> element; its readystatechange event will be fired asynchronously once it is inserted
3093 // into the document. Do so, thus queuing up the task. Remember to clean up once it's been called.
3094 var scriptEl = global.document.createElement('script');
3095 scriptEl.onreadystatechange = function () {
3096 nextTick();
3097
3098 scriptEl.onreadystatechange = null;
3099 scriptEl.parentNode.removeChild(scriptEl);
3100 scriptEl = null;
3101 };
3102 global.document.documentElement.appendChild(scriptEl);
3103 };
3104 } else {
3105 scheduleDrain = function () {
3106 setTimeout(nextTick, 0);
3107 };
3108 }
3109}
3110
3111var draining;
3112var queue = [];
3113//named nextTick for less confusing stack traces
3114function nextTick() {
3115 draining = true;
3116 var i, oldQueue;
3117 var len = queue.length;
3118 while (len) {
3119 oldQueue = queue;
3120 queue = [];
3121 i = -1;
3122 while (++i < len) {
3123 oldQueue[i]();
3124 }
3125 len = queue.length;
3126 }
3127 draining = false;
3128}
3129
3130module.exports = immediate;
3131function immediate(task) {
3132 if (queue.push(task) === 1 && !draining) {
3133 scheduleDrain();
3134 }
3135}
3136
3137}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})
3138},{}],2:[function(_dereq_,module,exports){
3139'use strict';
3140var immediate = _dereq_(1);
3141
3142/* istanbul ignore next */
3143function INTERNAL() {}
3144
3145var handlers = {};
3146
3147var REJECTED = ['REJECTED'];
3148var FULFILLED = ['FULFILLED'];
3149var PENDING = ['PENDING'];
3150
3151module.exports = Promise;
3152
3153function Promise(resolver) {
3154 if (typeof resolver !== 'function') {
3155 throw new TypeError('resolver must be a function');
3156 }
3157 this.state = PENDING;
3158 this.queue = [];
3159 this.outcome = void 0;
3160 if (resolver !== INTERNAL) {
3161 safelyResolveThenable(this, resolver);
3162 }
3163}
3164
3165Promise.prototype["catch"] = function (onRejected) {
3166 return this.then(null, onRejected);
3167};
3168Promise.prototype.then = function (onFulfilled, onRejected) {
3169 if (typeof onFulfilled !== 'function' && this.state === FULFILLED ||
3170 typeof onRejected !== 'function' && this.state === REJECTED) {
3171 return this;
3172 }
3173 var promise = new this.constructor(INTERNAL);
3174 if (this.state !== PENDING) {
3175 var resolver = this.state === FULFILLED ? onFulfilled : onRejected;
3176 unwrap(promise, resolver, this.outcome);
3177 } else {
3178 this.queue.push(new QueueItem(promise, onFulfilled, onRejected));
3179 }
3180
3181 return promise;
3182};
3183function QueueItem(promise, onFulfilled, onRejected) {
3184 this.promise = promise;
3185 if (typeof onFulfilled === 'function') {
3186 this.onFulfilled = onFulfilled;
3187 this.callFulfilled = this.otherCallFulfilled;
3188 }
3189 if (typeof onRejected === 'function') {
3190 this.onRejected = onRejected;
3191 this.callRejected = this.otherCallRejected;
3192 }
3193}
3194QueueItem.prototype.callFulfilled = function (value) {
3195 handlers.resolve(this.promise, value);
3196};
3197QueueItem.prototype.otherCallFulfilled = function (value) {
3198 unwrap(this.promise, this.onFulfilled, value);
3199};
3200QueueItem.prototype.callRejected = function (value) {
3201 handlers.reject(this.promise, value);
3202};
3203QueueItem.prototype.otherCallRejected = function (value) {
3204 unwrap(this.promise, this.onRejected, value);
3205};
3206
3207function unwrap(promise, func, value) {
3208 immediate(function () {
3209 var returnValue;
3210 try {
3211 returnValue = func(value);
3212 } catch (e) {
3213 return handlers.reject(promise, e);
3214 }
3215 if (returnValue === promise) {
3216 handlers.reject(promise, new TypeError('Cannot resolve promise with itself'));
3217 } else {
3218 handlers.resolve(promise, returnValue);
3219 }
3220 });
3221}
3222
3223handlers.resolve = function (self, value) {
3224 var result = tryCatch(getThen, value);
3225 if (result.status === 'error') {
3226 return handlers.reject(self, result.value);
3227 }
3228 var thenable = result.value;
3229
3230 if (thenable) {
3231 safelyResolveThenable(self, thenable);
3232 } else {
3233 self.state = FULFILLED;
3234 self.outcome = value;
3235 var i = -1;
3236 var len = self.queue.length;
3237 while (++i < len) {
3238 self.queue[i].callFulfilled(value);
3239 }
3240 }
3241 return self;
3242};
3243handlers.reject = function (self, error) {
3244 self.state = REJECTED;
3245 self.outcome = error;
3246 var i = -1;
3247 var len = self.queue.length;
3248 while (++i < len) {
3249 self.queue[i].callRejected(error);
3250 }
3251 return self;
3252};
3253
3254function getThen(obj) {
3255 // Make sure we only access the accessor once as required by the spec
3256 var then = obj && obj.then;
3257 if (obj && (typeof obj === 'object' || typeof obj === 'function') && typeof then === 'function') {
3258 return function appyThen() {
3259 then.apply(obj, arguments);
3260 };
3261 }
3262}
3263
3264function safelyResolveThenable(self, thenable) {
3265 // Either fulfill, reject or reject with error
3266 var called = false;
3267 function onError(value) {
3268 if (called) {
3269 return;
3270 }
3271 called = true;
3272 handlers.reject(self, value);
3273 }
3274
3275 function onSuccess(value) {
3276 if (called) {
3277 return;
3278 }
3279 called = true;
3280 handlers.resolve(self, value);
3281 }
3282
3283 function tryToUnwrap() {
3284 thenable(onSuccess, onError);
3285 }
3286
3287 var result = tryCatch(tryToUnwrap);
3288 if (result.status === 'error') {
3289 onError(result.value);
3290 }
3291}
3292
3293function tryCatch(func, value) {
3294 var out = {};
3295 try {
3296 out.value = func(value);
3297 out.status = 'success';
3298 } catch (e) {
3299 out.status = 'error';
3300 out.value = e;
3301 }
3302 return out;
3303}
3304
3305Promise.resolve = resolve;
3306function resolve(value) {
3307 if (value instanceof this) {
3308 return value;
3309 }
3310 return handlers.resolve(new this(INTERNAL), value);
3311}
3312
3313Promise.reject = reject;
3314function reject(reason) {
3315 var promise = new this(INTERNAL);
3316 return handlers.reject(promise, reason);
3317}
3318
3319Promise.all = all;
3320function all(iterable) {
3321 var self = this;
3322 if (Object.prototype.toString.call(iterable) !== '[object Array]') {
3323 return this.reject(new TypeError('must be an array'));
3324 }
3325
3326 var len = iterable.length;
3327 var called = false;
3328 if (!len) {
3329 return this.resolve([]);
3330 }
3331
3332 var values = new Array(len);
3333 var resolved = 0;
3334 var i = -1;
3335 var promise = new this(INTERNAL);
3336
3337 while (++i < len) {
3338 allResolver(iterable[i], i);
3339 }
3340 return promise;
3341 function allResolver(value, i) {
3342 self.resolve(value).then(resolveFromAll, function (error) {
3343 if (!called) {
3344 called = true;
3345 handlers.reject(promise, error);
3346 }
3347 });
3348 function resolveFromAll(outValue) {
3349 values[i] = outValue;
3350 if (++resolved === len && !called) {
3351 called = true;
3352 handlers.resolve(promise, values);
3353 }
3354 }
3355 }
3356}
3357
3358Promise.race = race;
3359function race(iterable) {
3360 var self = this;
3361 if (Object.prototype.toString.call(iterable) !== '[object Array]') {
3362 return this.reject(new TypeError('must be an array'));
3363 }
3364
3365 var len = iterable.length;
3366 var called = false;
3367 if (!len) {
3368 return this.resolve([]);
3369 }
3370
3371 var i = -1;
3372 var promise = new this(INTERNAL);
3373
3374 while (++i < len) {
3375 resolver(iterable[i]);
3376 }
3377 return promise;
3378 function resolver(value) {
3379 self.resolve(value).then(function (response) {
3380 if (!called) {
3381 called = true;
3382 handlers.resolve(promise, response);
3383 }
3384 }, function (error) {
3385 if (!called) {
3386 called = true;
3387 handlers.reject(promise, error);
3388 }
3389 });
3390 }
3391}
3392
3393},{"1":1}],3:[function(_dereq_,module,exports){
3394(function (global){
3395'use strict';
3396if (typeof global.Promise !== 'function') {
3397 global.Promise = _dereq_(2);
3398}
3399
3400}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})
3401},{"2":2}],4:[function(_dereq_,module,exports){
3402'use strict';
3403
3404var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; };
3405
3406function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
3407
3408function getIDB() {
3409 /* global indexedDB,webkitIndexedDB,mozIndexedDB,OIndexedDB,msIndexedDB */
3410 try {
3411 if (typeof indexedDB !== 'undefined') {
3412 return indexedDB;
3413 }
3414 if (typeof webkitIndexedDB !== 'undefined') {
3415 return webkitIndexedDB;
3416 }
3417 if (typeof mozIndexedDB !== 'undefined') {
3418 return mozIndexedDB;
3419 }
3420 if (typeof OIndexedDB !== 'undefined') {
3421 return OIndexedDB;
3422 }
3423 if (typeof msIndexedDB !== 'undefined') {
3424 return msIndexedDB;
3425 }
3426 } catch (e) {
3427 return;
3428 }
3429}
3430
3431var idb = getIDB();
3432
3433function isIndexedDBValid() {
3434 try {
3435 // Initialize IndexedDB; fall back to vendor-prefixed versions
3436 // if needed.
3437 if (!idb) {
3438 return false;
3439 }
3440 // We mimic PouchDB here;
3441 //
3442 // We test for openDatabase because IE Mobile identifies itself
3443 // as Safari. Oh the lulz...
3444 var isSafari = typeof openDatabase !== 'undefined' && /(Safari|iPhone|iPad|iPod)/.test(navigator.userAgent) && !/Chrome/.test(navigator.userAgent) && !/BlackBerry/.test(navigator.platform);
3445
3446 var hasFetch = typeof fetch === 'function' && fetch.toString().indexOf('[native code') !== -1;
3447
3448 // Safari <10.1 does not meet our requirements for IDB support (#5572)
3449 // since Safari 10.1 shipped with fetch, we can use that to detect it
3450 return (!isSafari || hasFetch) && typeof indexedDB !== 'undefined' &&
3451 // some outdated implementations of IDB that appear on Samsung
3452 // and HTC Android devices <4.4 are missing IDBKeyRange
3453 // See: https://github.com/mozilla/localForage/issues/128
3454 // See: https://github.com/mozilla/localForage/issues/272
3455 typeof IDBKeyRange !== 'undefined';
3456 } catch (e) {
3457 return false;
3458 }
3459}
3460
3461// Abstracts constructing a Blob object, so it also works in older
3462// browsers that don't support the native Blob constructor. (i.e.
3463// old QtWebKit versions, at least).
3464// Abstracts constructing a Blob object, so it also works in older
3465// browsers that don't support the native Blob constructor. (i.e.
3466// old QtWebKit versions, at least).
3467function createBlob(parts, properties) {
3468 /* global BlobBuilder,MSBlobBuilder,MozBlobBuilder,WebKitBlobBuilder */
3469 parts = parts || [];
3470 properties = properties || {};
3471 try {
3472 return new Blob(parts, properties);
3473 } catch (e) {
3474 if (e.name !== 'TypeError') {
3475 throw e;
3476 }
3477 var Builder = typeof BlobBuilder !== 'undefined' ? BlobBuilder : typeof MSBlobBuilder !== 'undefined' ? MSBlobBuilder : typeof MozBlobBuilder !== 'undefined' ? MozBlobBuilder : WebKitBlobBuilder;
3478 var builder = new Builder();
3479 for (var i = 0; i < parts.length; i += 1) {
3480 builder.append(parts[i]);
3481 }
3482 return builder.getBlob(properties.type);
3483 }
3484}
3485
3486// This is CommonJS because lie is an external dependency, so Rollup
3487// can just ignore it.
3488if (typeof Promise === 'undefined') {
3489 // In the "nopromises" build this will just throw if you don't have
3490 // a global promise object, but it would throw anyway later.
3491 _dereq_(3);
3492}
3493var Promise$1 = Promise;
3494
3495function executeCallback(promise, callback) {
3496 if (callback) {
3497 promise.then(function (result) {
3498 callback(null, result);
3499 }, function (error) {
3500 callback(error);
3501 });
3502 }
3503}
3504
3505function executeTwoCallbacks(promise, callback, errorCallback) {
3506 if (typeof callback === 'function') {
3507 promise.then(callback);
3508 }
3509
3510 if (typeof errorCallback === 'function') {
3511 promise["catch"](errorCallback);
3512 }
3513}
3514
3515function normalizeKey(key) {
3516 // Cast the key to a string, as that's all we can set as a key.
3517 if (typeof key !== 'string') {
3518 console.warn(key + ' used as a key, but it is not a string.');
3519 key = String(key);
3520 }
3521
3522 return key;
3523}
3524
3525function getCallback() {
3526 if (arguments.length && typeof arguments[arguments.length - 1] === 'function') {
3527 return arguments[arguments.length - 1];
3528 }
3529}
3530
3531// Some code originally from async_storage.js in
3532// [Gaia](https://github.com/mozilla-b2g/gaia).
3533
3534var DETECT_BLOB_SUPPORT_STORE = 'local-forage-detect-blob-support';
3535var supportsBlobs = void 0;
3536var dbContexts = {};
3537var toString = Object.prototype.toString;
3538
3539// Transaction Modes
3540var READ_ONLY = 'readonly';
3541var READ_WRITE = 'readwrite';
3542
3543// Transform a binary string to an array buffer, because otherwise
3544// weird stuff happens when you try to work with the binary string directly.
3545// It is known.
3546// From http://stackoverflow.com/questions/14967647/ (continues on next line)
3547// encode-decode-image-with-base64-breaks-image (2013-04-21)
3548function _binStringToArrayBuffer(bin) {
3549 var length = bin.length;
3550 var buf = new ArrayBuffer(length);
3551 var arr = new Uint8Array(buf);
3552 for (var i = 0; i < length; i++) {
3553 arr[i] = bin.charCodeAt(i);
3554 }
3555 return buf;
3556}
3557
3558//
3559// Blobs are not supported in all versions of IndexedDB, notably
3560// Chrome <37 and Android <5. In those versions, storing a blob will throw.
3561//
3562// Various other blob bugs exist in Chrome v37-42 (inclusive).
3563// Detecting them is expensive and confusing to users, and Chrome 37-42
3564// is at very low usage worldwide, so we do a hacky userAgent check instead.
3565//
3566// content-type bug: https://code.google.com/p/chromium/issues/detail?id=408120
3567// 404 bug: https://code.google.com/p/chromium/issues/detail?id=447916
3568// FileReader bug: https://code.google.com/p/chromium/issues/detail?id=447836
3569//
3570// Code borrowed from PouchDB. See:
3571// https://github.com/pouchdb/pouchdb/blob/master/packages/node_modules/pouchdb-adapter-idb/src/blobSupport.js
3572//
3573function _checkBlobSupportWithoutCaching(idb) {
3574 return new Promise$1(function (resolve) {
3575 var txn = idb.transaction(DETECT_BLOB_SUPPORT_STORE, READ_WRITE);
3576 var blob = createBlob(['']);
3577 txn.objectStore(DETECT_BLOB_SUPPORT_STORE).put(blob, 'key');
3578
3579 txn.onabort = function (e) {
3580 // If the transaction aborts now its due to not being able to
3581 // write to the database, likely due to the disk being full
3582 e.preventDefault();
3583 e.stopPropagation();
3584 resolve(false);
3585 };
3586
3587 txn.oncomplete = function () {
3588 var matchedChrome = navigator.userAgent.match(/Chrome\/(\d+)/);
3589 var matchedEdge = navigator.userAgent.match(/Edge\//);
3590 // MS Edge pretends to be Chrome 42:
3591 // https://msdn.microsoft.com/en-us/library/hh869301%28v=vs.85%29.aspx
3592 resolve(matchedEdge || !matchedChrome || parseInt(matchedChrome[1], 10) >= 43);
3593 };
3594 })["catch"](function () {
3595 return false; // error, so assume unsupported
3596 });
3597}
3598
3599function _checkBlobSupport(idb) {
3600 if (typeof supportsBlobs === 'boolean') {
3601 return Promise$1.resolve(supportsBlobs);
3602 }
3603 return _checkBlobSupportWithoutCaching(idb).then(function (value) {
3604 supportsBlobs = value;
3605 return supportsBlobs;
3606 });
3607}
3608
3609function _deferReadiness(dbInfo) {
3610 var dbContext = dbContexts[dbInfo.name];
3611
3612 // Create a deferred object representing the current database operation.
3613 var deferredOperation = {};
3614
3615 deferredOperation.promise = new Promise$1(function (resolve, reject) {
3616 deferredOperation.resolve = resolve;
3617 deferredOperation.reject = reject;
3618 });
3619
3620 // Enqueue the deferred operation.
3621 dbContext.deferredOperations.push(deferredOperation);
3622
3623 // Chain its promise to the database readiness.
3624 if (!dbContext.dbReady) {
3625 dbContext.dbReady = deferredOperation.promise;
3626 } else {
3627 dbContext.dbReady = dbContext.dbReady.then(function () {
3628 return deferredOperation.promise;
3629 });
3630 }
3631}
3632
3633function _advanceReadiness(dbInfo) {
3634 var dbContext = dbContexts[dbInfo.name];
3635
3636 // Dequeue a deferred operation.
3637 var deferredOperation = dbContext.deferredOperations.pop();
3638
3639 // Resolve its promise (which is part of the database readiness
3640 // chain of promises).
3641 if (deferredOperation) {
3642 deferredOperation.resolve();
3643 return deferredOperation.promise;
3644 }
3645}
3646
3647function _rejectReadiness(dbInfo, err) {
3648 var dbContext = dbContexts[dbInfo.name];
3649
3650 // Dequeue a deferred operation.
3651 var deferredOperation = dbContext.deferredOperations.pop();
3652
3653 // Reject its promise (which is part of the database readiness
3654 // chain of promises).
3655 if (deferredOperation) {
3656 deferredOperation.reject(err);
3657 return deferredOperation.promise;
3658 }
3659}
3660
3661function _getConnection(dbInfo, upgradeNeeded) {
3662 return new Promise$1(function (resolve, reject) {
3663 dbContexts[dbInfo.name] = dbContexts[dbInfo.name] || createDbContext();
3664
3665 if (dbInfo.db) {
3666 if (upgradeNeeded) {
3667 _deferReadiness(dbInfo);
3668 dbInfo.db.close();
3669 } else {
3670 return resolve(dbInfo.db);
3671 }
3672 }
3673
3674 var dbArgs = [dbInfo.name];
3675
3676 if (upgradeNeeded) {
3677 dbArgs.push(dbInfo.version);
3678 }
3679
3680 var openreq = idb.open.apply(idb, dbArgs);
3681
3682 if (upgradeNeeded) {
3683 openreq.onupgradeneeded = function (e) {
3684 var db = openreq.result;
3685 try {
3686 db.createObjectStore(dbInfo.storeName);
3687 if (e.oldVersion <= 1) {
3688 // Added when support for blob shims was added
3689 db.createObjectStore(DETECT_BLOB_SUPPORT_STORE);
3690 }
3691 } catch (ex) {
3692 if (ex.name === 'ConstraintError') {
3693 console.warn('The database "' + dbInfo.name + '"' + ' has been upgraded from version ' + e.oldVersion + ' to version ' + e.newVersion + ', but the storage "' + dbInfo.storeName + '" already exists.');
3694 } else {
3695 throw ex;
3696 }
3697 }
3698 };
3699 }
3700
3701 openreq.onerror = function (e) {
3702 e.preventDefault();
3703 reject(openreq.error);
3704 };
3705
3706 openreq.onsuccess = function () {
3707 resolve(openreq.result);
3708 _advanceReadiness(dbInfo);
3709 };
3710 });
3711}
3712
3713function _getOriginalConnection(dbInfo) {
3714 return _getConnection(dbInfo, false);
3715}
3716
3717function _getUpgradedConnection(dbInfo) {
3718 return _getConnection(dbInfo, true);
3719}
3720
3721function _isUpgradeNeeded(dbInfo, defaultVersion) {
3722 if (!dbInfo.db) {
3723 return true;
3724 }
3725
3726 var isNewStore = !dbInfo.db.objectStoreNames.contains(dbInfo.storeName);
3727 var isDowngrade = dbInfo.version < dbInfo.db.version;
3728 var isUpgrade = dbInfo.version > dbInfo.db.version;
3729
3730 if (isDowngrade) {
3731 // If the version is not the default one
3732 // then warn for impossible downgrade.
3733 if (dbInfo.version !== defaultVersion) {
3734 console.warn('The database "' + dbInfo.name + '"' + " can't be downgraded from version " + dbInfo.db.version + ' to version ' + dbInfo.version + '.');
3735 }
3736 // Align the versions to prevent errors.
3737 dbInfo.version = dbInfo.db.version;
3738 }
3739
3740 if (isUpgrade || isNewStore) {
3741 // If the store is new then increment the version (if needed).
3742 // This will trigger an "upgradeneeded" event which is required
3743 // for creating a store.
3744 if (isNewStore) {
3745 var incVersion = dbInfo.db.version + 1;
3746 if (incVersion > dbInfo.version) {
3747 dbInfo.version = incVersion;
3748 }
3749 }
3750
3751 return true;
3752 }
3753
3754 return false;
3755}
3756
3757// encode a blob for indexeddb engines that don't support blobs
3758function _encodeBlob(blob) {
3759 return new Promise$1(function (resolve, reject) {
3760 var reader = new FileReader();
3761 reader.onerror = reject;
3762 reader.onloadend = function (e) {
3763 var base64 = btoa(e.target.result || '');
3764 resolve({
3765 __local_forage_encoded_blob: true,
3766 data: base64,
3767 type: blob.type
3768 });
3769 };
3770 reader.readAsBinaryString(blob);
3771 });
3772}
3773
3774// decode an encoded blob
3775function _decodeBlob(encodedBlob) {
3776 var arrayBuff = _binStringToArrayBuffer(atob(encodedBlob.data));
3777 return createBlob([arrayBuff], { type: encodedBlob.type });
3778}
3779
3780// is this one of our fancy encoded blobs?
3781function _isEncodedBlob(value) {
3782 return value && value.__local_forage_encoded_blob;
3783}
3784
3785// Specialize the default `ready()` function by making it dependent
3786// on the current database operations. Thus, the driver will be actually
3787// ready when it's been initialized (default) *and* there are no pending
3788// operations on the database (initiated by some other instances).
3789function _fullyReady(callback) {
3790 var self = this;
3791
3792 var promise = self._initReady().then(function () {
3793 var dbContext = dbContexts[self._dbInfo.name];
3794
3795 if (dbContext && dbContext.dbReady) {
3796 return dbContext.dbReady;
3797 }
3798 });
3799
3800 executeTwoCallbacks(promise, callback, callback);
3801 return promise;
3802}
3803
3804// Try to establish a new db connection to replace the
3805// current one which is broken (i.e. experiencing
3806// InvalidStateError while creating a transaction).
3807function _tryReconnect(dbInfo) {
3808 _deferReadiness(dbInfo);
3809
3810 var dbContext = dbContexts[dbInfo.name];
3811 var forages = dbContext.forages;
3812
3813 for (var i = 0; i < forages.length; i++) {
3814 var forage = forages[i];
3815 if (forage._dbInfo.db) {
3816 forage._dbInfo.db.close();
3817 forage._dbInfo.db = null;
3818 }
3819 }
3820 dbInfo.db = null;
3821
3822 return _getOriginalConnection(dbInfo).then(function (db) {
3823 dbInfo.db = db;
3824 if (_isUpgradeNeeded(dbInfo)) {
3825 // Reopen the database for upgrading.
3826 return _getUpgradedConnection(dbInfo);
3827 }
3828 return db;
3829 }).then(function (db) {
3830 // store the latest db reference
3831 // in case the db was upgraded
3832 dbInfo.db = dbContext.db = db;
3833 for (var i = 0; i < forages.length; i++) {
3834 forages[i]._dbInfo.db = db;
3835 }
3836 })["catch"](function (err) {
3837 _rejectReadiness(dbInfo, err);
3838 throw err;
3839 });
3840}
3841
3842// FF doesn't like Promises (micro-tasks) and IDDB store operations,
3843// so we have to do it with callbacks
3844function createTransaction(dbInfo, mode, callback, retries) {
3845 if (retries === undefined) {
3846 retries = 1;
3847 }
3848
3849 try {
3850 var tx = dbInfo.db.transaction(dbInfo.storeName, mode);
3851 callback(null, tx);
3852 } catch (err) {
3853 if (retries > 0 && (!dbInfo.db || err.name === 'InvalidStateError' || err.name === 'NotFoundError')) {
3854 return Promise$1.resolve().then(function () {
3855 if (!dbInfo.db || err.name === 'NotFoundError' && !dbInfo.db.objectStoreNames.contains(dbInfo.storeName) && dbInfo.version <= dbInfo.db.version) {
3856 // increase the db version, to create the new ObjectStore
3857 if (dbInfo.db) {
3858 dbInfo.version = dbInfo.db.version + 1;
3859 }
3860 // Reopen the database for upgrading.
3861 return _getUpgradedConnection(dbInfo);
3862 }
3863 }).then(function () {
3864 return _tryReconnect(dbInfo).then(function () {
3865 createTransaction(dbInfo, mode, callback, retries - 1);
3866 });
3867 })["catch"](callback);
3868 }
3869
3870 callback(err);
3871 }
3872}
3873
3874function createDbContext() {
3875 return {
3876 // Running localForages sharing a database.
3877 forages: [],
3878 // Shared database.
3879 db: null,
3880 // Database readiness (promise).
3881 dbReady: null,
3882 // Deferred operations on the database.
3883 deferredOperations: []
3884 };
3885}
3886
3887// Open the IndexedDB database (automatically creates one if one didn't
3888// previously exist), using any options set in the config.
3889function _initStorage(options) {
3890 var self = this;
3891 var dbInfo = {
3892 db: null
3893 };
3894
3895 if (options) {
3896 for (var i in options) {
3897 dbInfo[i] = options[i];
3898 }
3899 }
3900
3901 // Get the current context of the database;
3902 var dbContext = dbContexts[dbInfo.name];
3903
3904 // ...or create a new context.
3905 if (!dbContext) {
3906 dbContext = createDbContext();
3907 // Register the new context in the global container.
3908 dbContexts[dbInfo.name] = dbContext;
3909 }
3910
3911 // Register itself as a running localForage in the current context.
3912 dbContext.forages.push(self);
3913
3914 // Replace the default `ready()` function with the specialized one.
3915 if (!self._initReady) {
3916 self._initReady = self.ready;
3917 self.ready = _fullyReady;
3918 }
3919
3920 // Create an array of initialization states of the related localForages.
3921 var initPromises = [];
3922
3923 function ignoreErrors() {
3924 // Don't handle errors here,
3925 // just makes sure related localForages aren't pending.
3926 return Promise$1.resolve();
3927 }
3928
3929 for (var j = 0; j < dbContext.forages.length; j++) {
3930 var forage = dbContext.forages[j];
3931 if (forage !== self) {
3932 // Don't wait for itself...
3933 initPromises.push(forage._initReady()["catch"](ignoreErrors));
3934 }
3935 }
3936
3937 // Take a snapshot of the related localForages.
3938 var forages = dbContext.forages.slice(0);
3939
3940 // Initialize the connection process only when
3941 // all the related localForages aren't pending.
3942 return Promise$1.all(initPromises).then(function () {
3943 dbInfo.db = dbContext.db;
3944 // Get the connection or open a new one without upgrade.
3945 return _getOriginalConnection(dbInfo);
3946 }).then(function (db) {
3947 dbInfo.db = db;
3948 if (_isUpgradeNeeded(dbInfo, self._defaultConfig.version)) {
3949 // Reopen the database for upgrading.
3950 return _getUpgradedConnection(dbInfo);
3951 }
3952 return db;
3953 }).then(function (db) {
3954 dbInfo.db = dbContext.db = db;
3955 self._dbInfo = dbInfo;
3956 // Share the final connection amongst related localForages.
3957 for (var k = 0; k < forages.length; k++) {
3958 var forage = forages[k];
3959 if (forage !== self) {
3960 // Self is already up-to-date.
3961 forage._dbInfo.db = dbInfo.db;
3962 forage._dbInfo.version = dbInfo.version;
3963 }
3964 }
3965 });
3966}
3967
3968function getItem(key, callback) {
3969 var self = this;
3970
3971 key = normalizeKey(key);
3972
3973 var promise = new Promise$1(function (resolve, reject) {
3974 self.ready().then(function () {
3975 createTransaction(self._dbInfo, READ_ONLY, function (err, transaction) {
3976 if (err) {
3977 return reject(err);
3978 }
3979
3980 try {
3981 var store = transaction.objectStore(self._dbInfo.storeName);
3982 var req = store.get(key);
3983
3984 req.onsuccess = function () {
3985 var value = req.result;
3986 if (value === undefined) {
3987 value = null;
3988 }
3989 if (_isEncodedBlob(value)) {
3990 value = _decodeBlob(value);
3991 }
3992 resolve(value);
3993 };
3994
3995 req.onerror = function () {
3996 reject(req.error);
3997 };
3998 } catch (e) {
3999 reject(e);
4000 }
4001 });
4002 })["catch"](reject);
4003 });
4004
4005 executeCallback(promise, callback);
4006 return promise;
4007}
4008
4009// Iterate over all items stored in database.
4010function iterate(iterator, callback) {
4011 var self = this;
4012
4013 var promise = new Promise$1(function (resolve, reject) {
4014 self.ready().then(function () {
4015 createTransaction(self._dbInfo, READ_ONLY, function (err, transaction) {
4016 if (err) {
4017 return reject(err);
4018 }
4019
4020 try {
4021 var store = transaction.objectStore(self._dbInfo.storeName);
4022 var req = store.openCursor();
4023 var iterationNumber = 1;
4024
4025 req.onsuccess = function () {
4026 var cursor = req.result;
4027
4028 if (cursor) {
4029 var value = cursor.value;
4030 if (_isEncodedBlob(value)) {
4031 value = _decodeBlob(value);
4032 }
4033 var result = iterator(value, cursor.key, iterationNumber++);
4034
4035 // when the iterator callback retuns any
4036 // (non-`undefined`) value, then we stop
4037 // the iteration immediately
4038 if (result !== void 0) {
4039 resolve(result);
4040 } else {
4041 cursor["continue"]();
4042 }
4043 } else {
4044 resolve();
4045 }
4046 };
4047
4048 req.onerror = function () {
4049 reject(req.error);
4050 };
4051 } catch (e) {
4052 reject(e);
4053 }
4054 });
4055 })["catch"](reject);
4056 });
4057
4058 executeCallback(promise, callback);
4059
4060 return promise;
4061}
4062
4063function setItem(key, value, callback) {
4064 var self = this;
4065
4066 key = normalizeKey(key);
4067
4068 var promise = new Promise$1(function (resolve, reject) {
4069 var dbInfo;
4070 self.ready().then(function () {
4071 dbInfo = self._dbInfo;
4072 if (toString.call(value) === '[object Blob]') {
4073 return _checkBlobSupport(dbInfo.db).then(function (blobSupport) {
4074 if (blobSupport) {
4075 return value;
4076 }
4077 return _encodeBlob(value);
4078 });
4079 }
4080 return value;
4081 }).then(function (value) {
4082 createTransaction(self._dbInfo, READ_WRITE, function (err, transaction) {
4083 if (err) {
4084 return reject(err);
4085 }
4086
4087 try {
4088 var store = transaction.objectStore(self._dbInfo.storeName);
4089
4090 // The reason we don't _save_ null is because IE 10 does
4091 // not support saving the `null` type in IndexedDB. How
4092 // ironic, given the bug below!
4093 // See: https://github.com/mozilla/localForage/issues/161
4094 if (value === null) {
4095 value = undefined;
4096 }
4097
4098 var req = store.put(value, key);
4099
4100 transaction.oncomplete = function () {
4101 // Cast to undefined so the value passed to
4102 // callback/promise is the same as what one would get out
4103 // of `getItem()` later. This leads to some weirdness
4104 // (setItem('foo', undefined) will return `null`), but
4105 // it's not my fault localStorage is our baseline and that
4106 // it's weird.
4107 if (value === undefined) {
4108 value = null;
4109 }
4110
4111 resolve(value);
4112 };
4113 transaction.onabort = transaction.onerror = function () {
4114 var err = req.error ? req.error : req.transaction.error;
4115 reject(err);
4116 };
4117 } catch (e) {
4118 reject(e);
4119 }
4120 });
4121 })["catch"](reject);
4122 });
4123
4124 executeCallback(promise, callback);
4125 return promise;
4126}
4127
4128function removeItem(key, callback) {
4129 var self = this;
4130
4131 key = normalizeKey(key);
4132
4133 var promise = new Promise$1(function (resolve, reject) {
4134 self.ready().then(function () {
4135 createTransaction(self._dbInfo, READ_WRITE, function (err, transaction) {
4136 if (err) {
4137 return reject(err);
4138 }
4139
4140 try {
4141 var store = transaction.objectStore(self._dbInfo.storeName);
4142 // We use a Grunt task to make this safe for IE and some
4143 // versions of Android (including those used by Cordova).
4144 // Normally IE won't like `.delete()` and will insist on
4145 // using `['delete']()`, but we have a build step that
4146 // fixes this for us now.
4147 var req = store["delete"](key);
4148 transaction.oncomplete = function () {
4149 resolve();
4150 };
4151
4152 transaction.onerror = function () {
4153 reject(req.error);
4154 };
4155
4156 // The request will be also be aborted if we've exceeded our storage
4157 // space.
4158 transaction.onabort = function () {
4159 var err = req.error ? req.error : req.transaction.error;
4160 reject(err);
4161 };
4162 } catch (e) {
4163 reject(e);
4164 }
4165 });
4166 })["catch"](reject);
4167 });
4168
4169 executeCallback(promise, callback);
4170 return promise;
4171}
4172
4173function clear(callback) {
4174 var self = this;
4175
4176 var promise = new Promise$1(function (resolve, reject) {
4177 self.ready().then(function () {
4178 createTransaction(self._dbInfo, READ_WRITE, function (err, transaction) {
4179 if (err) {
4180 return reject(err);
4181 }
4182
4183 try {
4184 var store = transaction.objectStore(self._dbInfo.storeName);
4185 var req = store.clear();
4186
4187 transaction.oncomplete = function () {
4188 resolve();
4189 };
4190
4191 transaction.onabort = transaction.onerror = function () {
4192 var err = req.error ? req.error : req.transaction.error;
4193 reject(err);
4194 };
4195 } catch (e) {
4196 reject(e);
4197 }
4198 });
4199 })["catch"](reject);
4200 });
4201
4202 executeCallback(promise, callback);
4203 return promise;
4204}
4205
4206function length(callback) {
4207 var self = this;
4208
4209 var promise = new Promise$1(function (resolve, reject) {
4210 self.ready().then(function () {
4211 createTransaction(self._dbInfo, READ_ONLY, function (err, transaction) {
4212 if (err) {
4213 return reject(err);
4214 }
4215
4216 try {
4217 var store = transaction.objectStore(self._dbInfo.storeName);
4218 var req = store.count();
4219
4220 req.onsuccess = function () {
4221 resolve(req.result);
4222 };
4223
4224 req.onerror = function () {
4225 reject(req.error);
4226 };
4227 } catch (e) {
4228 reject(e);
4229 }
4230 });
4231 })["catch"](reject);
4232 });
4233
4234 executeCallback(promise, callback);
4235 return promise;
4236}
4237
4238function key(n, callback) {
4239 var self = this;
4240
4241 var promise = new Promise$1(function (resolve, reject) {
4242 if (n < 0) {
4243 resolve(null);
4244
4245 return;
4246 }
4247
4248 self.ready().then(function () {
4249 createTransaction(self._dbInfo, READ_ONLY, function (err, transaction) {
4250 if (err) {
4251 return reject(err);
4252 }
4253
4254 try {
4255 var store = transaction.objectStore(self._dbInfo.storeName);
4256 var advanced = false;
4257 var req = store.openCursor();
4258
4259 req.onsuccess = function () {
4260 var cursor = req.result;
4261 if (!cursor) {
4262 // this means there weren't enough keys
4263 resolve(null);
4264
4265 return;
4266 }
4267
4268 if (n === 0) {
4269 // We have the first key, return it if that's what they
4270 // wanted.
4271 resolve(cursor.key);
4272 } else {
4273 if (!advanced) {
4274 // Otherwise, ask the cursor to skip ahead n
4275 // records.
4276 advanced = true;
4277 cursor.advance(n);
4278 } else {
4279 // When we get here, we've got the nth key.
4280 resolve(cursor.key);
4281 }
4282 }
4283 };
4284
4285 req.onerror = function () {
4286 reject(req.error);
4287 };
4288 } catch (e) {
4289 reject(e);
4290 }
4291 });
4292 })["catch"](reject);
4293 });
4294
4295 executeCallback(promise, callback);
4296 return promise;
4297}
4298
4299function keys(callback) {
4300 var self = this;
4301
4302 var promise = new Promise$1(function (resolve, reject) {
4303 self.ready().then(function () {
4304 createTransaction(self._dbInfo, READ_ONLY, function (err, transaction) {
4305 if (err) {
4306 return reject(err);
4307 }
4308
4309 try {
4310 var store = transaction.objectStore(self._dbInfo.storeName);
4311 var req = store.openCursor();
4312 var keys = [];
4313
4314 req.onsuccess = function () {
4315 var cursor = req.result;
4316
4317 if (!cursor) {
4318 resolve(keys);
4319 return;
4320 }
4321
4322 keys.push(cursor.key);
4323 cursor["continue"]();
4324 };
4325
4326 req.onerror = function () {
4327 reject(req.error);
4328 };
4329 } catch (e) {
4330 reject(e);
4331 }
4332 });
4333 })["catch"](reject);
4334 });
4335
4336 executeCallback(promise, callback);
4337 return promise;
4338}
4339
4340function dropInstance(options, callback) {
4341 callback = getCallback.apply(this, arguments);
4342
4343 var currentConfig = this.config();
4344 options = typeof options !== 'function' && options || {};
4345 if (!options.name) {
4346 options.name = options.name || currentConfig.name;
4347 options.storeName = options.storeName || currentConfig.storeName;
4348 }
4349
4350 var self = this;
4351 var promise;
4352 if (!options.name) {
4353 promise = Promise$1.reject('Invalid arguments');
4354 } else {
4355 var isCurrentDb = options.name === currentConfig.name && self._dbInfo.db;
4356
4357 var dbPromise = isCurrentDb ? Promise$1.resolve(self._dbInfo.db) : _getOriginalConnection(options).then(function (db) {
4358 var dbContext = dbContexts[options.name];
4359 var forages = dbContext.forages;
4360 dbContext.db = db;
4361 for (var i = 0; i < forages.length; i++) {
4362 forages[i]._dbInfo.db = db;
4363 }
4364 return db;
4365 });
4366
4367 if (!options.storeName) {
4368 promise = dbPromise.then(function (db) {
4369 _deferReadiness(options);
4370
4371 var dbContext = dbContexts[options.name];
4372 var forages = dbContext.forages;
4373
4374 db.close();
4375 for (var i = 0; i < forages.length; i++) {
4376 var forage = forages[i];
4377 forage._dbInfo.db = null;
4378 }
4379
4380 var dropDBPromise = new Promise$1(function (resolve, reject) {
4381 var req = idb.deleteDatabase(options.name);
4382
4383 req.onerror = req.onblocked = function (err) {
4384 var db = req.result;
4385 if (db) {
4386 db.close();
4387 }
4388 reject(err);
4389 };
4390
4391 req.onsuccess = function () {
4392 var db = req.result;
4393 if (db) {
4394 db.close();
4395 }
4396 resolve(db);
4397 };
4398 });
4399
4400 return dropDBPromise.then(function (db) {
4401 dbContext.db = db;
4402 for (var i = 0; i < forages.length; i++) {
4403 var _forage = forages[i];
4404 _advanceReadiness(_forage._dbInfo);
4405 }
4406 })["catch"](function (err) {
4407 (_rejectReadiness(options, err) || Promise$1.resolve())["catch"](function () {});
4408 throw err;
4409 });
4410 });
4411 } else {
4412 promise = dbPromise.then(function (db) {
4413 if (!db.objectStoreNames.contains(options.storeName)) {
4414 return;
4415 }
4416
4417 var newVersion = db.version + 1;
4418
4419 _deferReadiness(options);
4420
4421 var dbContext = dbContexts[options.name];
4422 var forages = dbContext.forages;
4423
4424 db.close();
4425 for (var i = 0; i < forages.length; i++) {
4426 var forage = forages[i];
4427 forage._dbInfo.db = null;
4428 forage._dbInfo.version = newVersion;
4429 }
4430
4431 var dropObjectPromise = new Promise$1(function (resolve, reject) {
4432 var req = idb.open(options.name, newVersion);
4433
4434 req.onerror = function (err) {
4435 var db = req.result;
4436 db.close();
4437 reject(err);
4438 };
4439
4440 req.onupgradeneeded = function () {
4441 var db = req.result;
4442 db.deleteObjectStore(options.storeName);
4443 };
4444
4445 req.onsuccess = function () {
4446 var db = req.result;
4447 db.close();
4448 resolve(db);
4449 };
4450 });
4451
4452 return dropObjectPromise.then(function (db) {
4453 dbContext.db = db;
4454 for (var j = 0; j < forages.length; j++) {
4455 var _forage2 = forages[j];
4456 _forage2._dbInfo.db = db;
4457 _advanceReadiness(_forage2._dbInfo);
4458 }
4459 })["catch"](function (err) {
4460 (_rejectReadiness(options, err) || Promise$1.resolve())["catch"](function () {});
4461 throw err;
4462 });
4463 });
4464 }
4465 }
4466
4467 executeCallback(promise, callback);
4468 return promise;
4469}
4470
4471var asyncStorage = {
4472 _driver: 'asyncStorage',
4473 _initStorage: _initStorage,
4474 _support: isIndexedDBValid(),
4475 iterate: iterate,
4476 getItem: getItem,
4477 setItem: setItem,
4478 removeItem: removeItem,
4479 clear: clear,
4480 length: length,
4481 key: key,
4482 keys: keys,
4483 dropInstance: dropInstance
4484};
4485
4486function isWebSQLValid() {
4487 return typeof openDatabase === 'function';
4488}
4489
4490// Sadly, the best way to save binary data in WebSQL/localStorage is serializing
4491// it to Base64, so this is how we store it to prevent very strange errors with less
4492// verbose ways of binary <-> string data storage.
4493var BASE_CHARS = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/';
4494
4495var BLOB_TYPE_PREFIX = '~~local_forage_type~';
4496var BLOB_TYPE_PREFIX_REGEX = /^~~local_forage_type~([^~]+)~/;
4497
4498var SERIALIZED_MARKER = '__lfsc__:';
4499var SERIALIZED_MARKER_LENGTH = SERIALIZED_MARKER.length;
4500
4501// OMG the serializations!
4502var TYPE_ARRAYBUFFER = 'arbf';
4503var TYPE_BLOB = 'blob';
4504var TYPE_INT8ARRAY = 'si08';
4505var TYPE_UINT8ARRAY = 'ui08';
4506var TYPE_UINT8CLAMPEDARRAY = 'uic8';
4507var TYPE_INT16ARRAY = 'si16';
4508var TYPE_INT32ARRAY = 'si32';
4509var TYPE_UINT16ARRAY = 'ur16';
4510var TYPE_UINT32ARRAY = 'ui32';
4511var TYPE_FLOAT32ARRAY = 'fl32';
4512var TYPE_FLOAT64ARRAY = 'fl64';
4513var TYPE_SERIALIZED_MARKER_LENGTH = SERIALIZED_MARKER_LENGTH + TYPE_ARRAYBUFFER.length;
4514
4515var toString$1 = Object.prototype.toString;
4516
4517function stringToBuffer(serializedString) {
4518 // Fill the string into a ArrayBuffer.
4519 var bufferLength = serializedString.length * 0.75;
4520 var len = serializedString.length;
4521 var i;
4522 var p = 0;
4523 var encoded1, encoded2, encoded3, encoded4;
4524
4525 if (serializedString[serializedString.length - 1] === '=') {
4526 bufferLength--;
4527 if (serializedString[serializedString.length - 2] === '=') {
4528 bufferLength--;
4529 }
4530 }
4531
4532 var buffer = new ArrayBuffer(bufferLength);
4533 var bytes = new Uint8Array(buffer);
4534
4535 for (i = 0; i < len; i += 4) {
4536 encoded1 = BASE_CHARS.indexOf(serializedString[i]);
4537 encoded2 = BASE_CHARS.indexOf(serializedString[i + 1]);
4538 encoded3 = BASE_CHARS.indexOf(serializedString[i + 2]);
4539 encoded4 = BASE_CHARS.indexOf(serializedString[i + 3]);
4540
4541 /*jslint bitwise: true */
4542 bytes[p++] = encoded1 << 2 | encoded2 >> 4;
4543 bytes[p++] = (encoded2 & 15) << 4 | encoded3 >> 2;
4544 bytes[p++] = (encoded3 & 3) << 6 | encoded4 & 63;
4545 }
4546 return buffer;
4547}
4548
4549// Converts a buffer to a string to store, serialized, in the backend
4550// storage library.
4551function bufferToString(buffer) {
4552 // base64-arraybuffer
4553 var bytes = new Uint8Array(buffer);
4554 var base64String = '';
4555 var i;
4556
4557 for (i = 0; i < bytes.length; i += 3) {
4558 /*jslint bitwise: true */
4559 base64String += BASE_CHARS[bytes[i] >> 2];
4560 base64String += BASE_CHARS[(bytes[i] & 3) << 4 | bytes[i + 1] >> 4];
4561 base64String += BASE_CHARS[(bytes[i + 1] & 15) << 2 | bytes[i + 2] >> 6];
4562 base64String += BASE_CHARS[bytes[i + 2] & 63];
4563 }
4564
4565 if (bytes.length % 3 === 2) {
4566 base64String = base64String.substring(0, base64String.length - 1) + '=';
4567 } else if (bytes.length % 3 === 1) {
4568 base64String = base64String.substring(0, base64String.length - 2) + '==';
4569 }
4570
4571 return base64String;
4572}
4573
4574// Serialize a value, afterwards executing a callback (which usually
4575// instructs the `setItem()` callback/promise to be executed). This is how
4576// we store binary data with localStorage.
4577function serialize(value, callback) {
4578 var valueType = '';
4579 if (value) {
4580 valueType = toString$1.call(value);
4581 }
4582
4583 // Cannot use `value instanceof ArrayBuffer` or such here, as these
4584 // checks fail when running the tests using casper.js...
4585 //
4586 // TODO: See why those tests fail and use a better solution.
4587 if (value && (valueType === '[object ArrayBuffer]' || value.buffer && toString$1.call(value.buffer) === '[object ArrayBuffer]')) {
4588 // Convert binary arrays to a string and prefix the string with
4589 // a special marker.
4590 var buffer;
4591 var marker = SERIALIZED_MARKER;
4592
4593 if (value instanceof ArrayBuffer) {
4594 buffer = value;
4595 marker += TYPE_ARRAYBUFFER;
4596 } else {
4597 buffer = value.buffer;
4598
4599 if (valueType === '[object Int8Array]') {
4600 marker += TYPE_INT8ARRAY;
4601 } else if (valueType === '[object Uint8Array]') {
4602 marker += TYPE_UINT8ARRAY;
4603 } else if (valueType === '[object Uint8ClampedArray]') {
4604 marker += TYPE_UINT8CLAMPEDARRAY;
4605 } else if (valueType === '[object Int16Array]') {
4606 marker += TYPE_INT16ARRAY;
4607 } else if (valueType === '[object Uint16Array]') {
4608 marker += TYPE_UINT16ARRAY;
4609 } else if (valueType === '[object Int32Array]') {
4610 marker += TYPE_INT32ARRAY;
4611 } else if (valueType === '[object Uint32Array]') {
4612 marker += TYPE_UINT32ARRAY;
4613 } else if (valueType === '[object Float32Array]') {
4614 marker += TYPE_FLOAT32ARRAY;
4615 } else if (valueType === '[object Float64Array]') {
4616 marker += TYPE_FLOAT64ARRAY;
4617 } else {
4618 callback(new Error('Failed to get type for BinaryArray'));
4619 }
4620 }
4621
4622 callback(marker + bufferToString(buffer));
4623 } else if (valueType === '[object Blob]') {
4624 // Conver the blob to a binaryArray and then to a string.
4625 var fileReader = new FileReader();
4626
4627 fileReader.onload = function () {
4628 // Backwards-compatible prefix for the blob type.
4629 var str = BLOB_TYPE_PREFIX + value.type + '~' + bufferToString(this.result);
4630
4631 callback(SERIALIZED_MARKER + TYPE_BLOB + str);
4632 };
4633
4634 fileReader.readAsArrayBuffer(value);
4635 } else {
4636 try {
4637 callback(JSON.stringify(value));
4638 } catch (e) {
4639 console.error("Couldn't convert value into a JSON string: ", value);
4640
4641 callback(null, e);
4642 }
4643 }
4644}
4645
4646// Deserialize data we've inserted into a value column/field. We place
4647// special markers into our strings to mark them as encoded; this isn't
4648// as nice as a meta field, but it's the only sane thing we can do whilst
4649// keeping localStorage support intact.
4650//
4651// Oftentimes this will just deserialize JSON content, but if we have a
4652// special marker (SERIALIZED_MARKER, defined above), we will extract
4653// some kind of arraybuffer/binary data/typed array out of the string.
4654function deserialize(value) {
4655 // If we haven't marked this string as being specially serialized (i.e.
4656 // something other than serialized JSON), we can just return it and be
4657 // done with it.
4658 if (value.substring(0, SERIALIZED_MARKER_LENGTH) !== SERIALIZED_MARKER) {
4659 return JSON.parse(value);
4660 }
4661
4662 // The following code deals with deserializing some kind of Blob or
4663 // TypedArray. First we separate out the type of data we're dealing
4664 // with from the data itself.
4665 var serializedString = value.substring(TYPE_SERIALIZED_MARKER_LENGTH);
4666 var type = value.substring(SERIALIZED_MARKER_LENGTH, TYPE_SERIALIZED_MARKER_LENGTH);
4667
4668 var blobType;
4669 // Backwards-compatible blob type serialization strategy.
4670 // DBs created with older versions of localForage will simply not have the blob type.
4671 if (type === TYPE_BLOB && BLOB_TYPE_PREFIX_REGEX.test(serializedString)) {
4672 var matcher = serializedString.match(BLOB_TYPE_PREFIX_REGEX);
4673 blobType = matcher[1];
4674 serializedString = serializedString.substring(matcher[0].length);
4675 }
4676 var buffer = stringToBuffer(serializedString);
4677
4678 // Return the right type based on the code/type set during
4679 // serialization.
4680 switch (type) {
4681 case TYPE_ARRAYBUFFER:
4682 return buffer;
4683 case TYPE_BLOB:
4684 return createBlob([buffer], { type: blobType });
4685 case TYPE_INT8ARRAY:
4686 return new Int8Array(buffer);
4687 case TYPE_UINT8ARRAY:
4688 return new Uint8Array(buffer);
4689 case TYPE_UINT8CLAMPEDARRAY:
4690 return new Uint8ClampedArray(buffer);
4691 case TYPE_INT16ARRAY:
4692 return new Int16Array(buffer);
4693 case TYPE_UINT16ARRAY:
4694 return new Uint16Array(buffer);
4695 case TYPE_INT32ARRAY:
4696 return new Int32Array(buffer);
4697 case TYPE_UINT32ARRAY:
4698 return new Uint32Array(buffer);
4699 case TYPE_FLOAT32ARRAY:
4700 return new Float32Array(buffer);
4701 case TYPE_FLOAT64ARRAY:
4702 return new Float64Array(buffer);
4703 default:
4704 throw new Error('Unkown type: ' + type);
4705 }
4706}
4707
4708var localforageSerializer = {
4709 serialize: serialize,
4710 deserialize: deserialize,
4711 stringToBuffer: stringToBuffer,
4712 bufferToString: bufferToString
4713};
4714
4715/*
4716 * Includes code from:
4717 *
4718 * base64-arraybuffer
4719 * https://github.com/niklasvh/base64-arraybuffer
4720 *
4721 * Copyright (c) 2012 Niklas von Hertzen
4722 * Licensed under the MIT license.
4723 */
4724
4725function createDbTable(t, dbInfo, callback, errorCallback) {
4726 t.executeSql('CREATE TABLE IF NOT EXISTS ' + dbInfo.storeName + ' ' + '(id INTEGER PRIMARY KEY, key unique, value)', [], callback, errorCallback);
4727}
4728
4729// Open the WebSQL database (automatically creates one if one didn't
4730// previously exist), using any options set in the config.
4731function _initStorage$1(options) {
4732 var self = this;
4733 var dbInfo = {
4734 db: null
4735 };
4736
4737 if (options) {
4738 for (var i in options) {
4739 dbInfo[i] = typeof options[i] !== 'string' ? options[i].toString() : options[i];
4740 }
4741 }
4742
4743 var dbInfoPromise = new Promise$1(function (resolve, reject) {
4744 // Open the database; the openDatabase API will automatically
4745 // create it for us if it doesn't exist.
4746 try {
4747 dbInfo.db = openDatabase(dbInfo.name, String(dbInfo.version), dbInfo.description, dbInfo.size);
4748 } catch (e) {
4749 return reject(e);
4750 }
4751
4752 // Create our key/value table if it doesn't exist.
4753 dbInfo.db.transaction(function (t) {
4754 createDbTable(t, dbInfo, function () {
4755 self._dbInfo = dbInfo;
4756 resolve();
4757 }, function (t, error) {
4758 reject(error);
4759 });
4760 }, reject);
4761 });
4762
4763 dbInfo.serializer = localforageSerializer;
4764 return dbInfoPromise;
4765}
4766
4767function tryExecuteSql(t, dbInfo, sqlStatement, args, callback, errorCallback) {
4768 t.executeSql(sqlStatement, args, callback, function (t, error) {
4769 if (error.code === error.SYNTAX_ERR) {
4770 t.executeSql('SELECT name FROM sqlite_master ' + "WHERE type='table' AND name = ?", [name], function (t, results) {
4771 if (!results.rows.length) {
4772 // if the table is missing (was deleted)
4773 // re-create it table and retry
4774 createDbTable(t, dbInfo, function () {
4775 t.executeSql(sqlStatement, args, callback, errorCallback);
4776 }, errorCallback);
4777 } else {
4778 errorCallback(t, error);
4779 }
4780 }, errorCallback);
4781 } else {
4782 errorCallback(t, error);
4783 }
4784 }, errorCallback);
4785}
4786
4787function getItem$1(key, callback) {
4788 var self = this;
4789
4790 key = normalizeKey(key);
4791
4792 var promise = new Promise$1(function (resolve, reject) {
4793 self.ready().then(function () {
4794 var dbInfo = self._dbInfo;
4795 dbInfo.db.transaction(function (t) {
4796 tryExecuteSql(t, dbInfo, 'SELECT * FROM ' + dbInfo.storeName + ' WHERE key = ? LIMIT 1', [key], function (t, results) {
4797 var result = results.rows.length ? results.rows.item(0).value : null;
4798
4799 // Check to see if this is serialized content we need to
4800 // unpack.
4801 if (result) {
4802 result = dbInfo.serializer.deserialize(result);
4803 }
4804
4805 resolve(result);
4806 }, function (t, error) {
4807 reject(error);
4808 });
4809 });
4810 })["catch"](reject);
4811 });
4812
4813 executeCallback(promise, callback);
4814 return promise;
4815}
4816
4817function iterate$1(iterator, callback) {
4818 var self = this;
4819
4820 var promise = new Promise$1(function (resolve, reject) {
4821 self.ready().then(function () {
4822 var dbInfo = self._dbInfo;
4823
4824 dbInfo.db.transaction(function (t) {
4825 tryExecuteSql(t, dbInfo, 'SELECT * FROM ' + dbInfo.storeName, [], function (t, results) {
4826 var rows = results.rows;
4827 var length = rows.length;
4828
4829 for (var i = 0; i < length; i++) {
4830 var item = rows.item(i);
4831 var result = item.value;
4832
4833 // Check to see if this is serialized content
4834 // we need to unpack.
4835 if (result) {
4836 result = dbInfo.serializer.deserialize(result);
4837 }
4838
4839 result = iterator(result, item.key, i + 1);
4840
4841 // void(0) prevents problems with redefinition
4842 // of `undefined`.
4843 if (result !== void 0) {
4844 resolve(result);
4845 return;
4846 }
4847 }
4848
4849 resolve();
4850 }, function (t, error) {
4851 reject(error);
4852 });
4853 });
4854 })["catch"](reject);
4855 });
4856
4857 executeCallback(promise, callback);
4858 return promise;
4859}
4860
4861function _setItem(key, value, callback, retriesLeft) {
4862 var self = this;
4863
4864 key = normalizeKey(key);
4865
4866 var promise = new Promise$1(function (resolve, reject) {
4867 self.ready().then(function () {
4868 // The localStorage API doesn't return undefined values in an
4869 // "expected" way, so undefined is always cast to null in all
4870 // drivers. See: https://github.com/mozilla/localForage/pull/42
4871 if (value === undefined) {
4872 value = null;
4873 }
4874
4875 // Save the original value to pass to the callback.
4876 var originalValue = value;
4877
4878 var dbInfo = self._dbInfo;
4879 dbInfo.serializer.serialize(value, function (value, error) {
4880 if (error) {
4881 reject(error);
4882 } else {
4883 dbInfo.db.transaction(function (t) {
4884 tryExecuteSql(t, dbInfo, 'INSERT OR REPLACE INTO ' + dbInfo.storeName + ' ' + '(key, value) VALUES (?, ?)', [key, value], function () {
4885 resolve(originalValue);
4886 }, function (t, error) {
4887 reject(error);
4888 });
4889 }, function (sqlError) {
4890 // The transaction failed; check
4891 // to see if it's a quota error.
4892 if (sqlError.code === sqlError.QUOTA_ERR) {
4893 // We reject the callback outright for now, but
4894 // it's worth trying to re-run the transaction.
4895 // Even if the user accepts the prompt to use
4896 // more storage on Safari, this error will
4897 // be called.
4898 //
4899 // Try to re-run the transaction.
4900 if (retriesLeft > 0) {
4901 resolve(_setItem.apply(self, [key, originalValue, callback, retriesLeft - 1]));
4902 return;
4903 }
4904 reject(sqlError);
4905 }
4906 });
4907 }
4908 });
4909 })["catch"](reject);
4910 });
4911
4912 executeCallback(promise, callback);
4913 return promise;
4914}
4915
4916function setItem$1(key, value, callback) {
4917 return _setItem.apply(this, [key, value, callback, 1]);
4918}
4919
4920function removeItem$1(key, callback) {
4921 var self = this;
4922
4923 key = normalizeKey(key);
4924
4925 var promise = new Promise$1(function (resolve, reject) {
4926 self.ready().then(function () {
4927 var dbInfo = self._dbInfo;
4928 dbInfo.db.transaction(function (t) {
4929 tryExecuteSql(t, dbInfo, 'DELETE FROM ' + dbInfo.storeName + ' WHERE key = ?', [key], function () {
4930 resolve();
4931 }, function (t, error) {
4932 reject(error);
4933 });
4934 });
4935 })["catch"](reject);
4936 });
4937
4938 executeCallback(promise, callback);
4939 return promise;
4940}
4941
4942// Deletes every item in the table.
4943// TODO: Find out if this resets the AUTO_INCREMENT number.
4944function clear$1(callback) {
4945 var self = this;
4946
4947 var promise = new Promise$1(function (resolve, reject) {
4948 self.ready().then(function () {
4949 var dbInfo = self._dbInfo;
4950 dbInfo.db.transaction(function (t) {
4951 tryExecuteSql(t, dbInfo, 'DELETE FROM ' + dbInfo.storeName, [], function () {
4952 resolve();
4953 }, function (t, error) {
4954 reject(error);
4955 });
4956 });
4957 })["catch"](reject);
4958 });
4959
4960 executeCallback(promise, callback);
4961 return promise;
4962}
4963
4964// Does a simple `COUNT(key)` to get the number of items stored in
4965// localForage.
4966function length$1(callback) {
4967 var self = this;
4968
4969 var promise = new Promise$1(function (resolve, reject) {
4970 self.ready().then(function () {
4971 var dbInfo = self._dbInfo;
4972 dbInfo.db.transaction(function (t) {
4973 // Ahhh, SQL makes this one soooooo easy.
4974 tryExecuteSql(t, dbInfo, 'SELECT COUNT(key) as c FROM ' + dbInfo.storeName, [], function (t, results) {
4975 var result = results.rows.item(0).c;
4976 resolve(result);
4977 }, function (t, error) {
4978 reject(error);
4979 });
4980 });
4981 })["catch"](reject);
4982 });
4983
4984 executeCallback(promise, callback);
4985 return promise;
4986}
4987
4988// Return the key located at key index X; essentially gets the key from a
4989// `WHERE id = ?`. This is the most efficient way I can think to implement
4990// this rarely-used (in my experience) part of the API, but it can seem
4991// inconsistent, because we do `INSERT OR REPLACE INTO` on `setItem()`, so
4992// the ID of each key will change every time it's updated. Perhaps a stored
4993// procedure for the `setItem()` SQL would solve this problem?
4994// TODO: Don't change ID on `setItem()`.
4995function key$1(n, callback) {
4996 var self = this;
4997
4998 var promise = new Promise$1(function (resolve, reject) {
4999 self.ready().then(function () {
5000 var dbInfo = self._dbInfo;
5001 dbInfo.db.transaction(function (t) {
5002 tryExecuteSql(t, dbInfo, 'SELECT key FROM ' + dbInfo.storeName + ' WHERE id = ? LIMIT 1', [n + 1], function (t, results) {
5003 var result = results.rows.length ? results.rows.item(0).key : null;
5004 resolve(result);
5005 }, function (t, error) {
5006 reject(error);
5007 });
5008 });
5009 })["catch"](reject);
5010 });
5011
5012 executeCallback(promise, callback);
5013 return promise;
5014}
5015
5016function keys$1(callback) {
5017 var self = this;
5018
5019 var promise = new Promise$1(function (resolve, reject) {
5020 self.ready().then(function () {
5021 var dbInfo = self._dbInfo;
5022 dbInfo.db.transaction(function (t) {
5023 tryExecuteSql(t, dbInfo, 'SELECT key FROM ' + dbInfo.storeName, [], function (t, results) {
5024 var keys = [];
5025
5026 for (var i = 0; i < results.rows.length; i++) {
5027 keys.push(results.rows.item(i).key);
5028 }
5029
5030 resolve(keys);
5031 }, function (t, error) {
5032 reject(error);
5033 });
5034 });
5035 })["catch"](reject);
5036 });
5037
5038 executeCallback(promise, callback);
5039 return promise;
5040}
5041
5042// https://www.w3.org/TR/webdatabase/#databases
5043// > There is no way to enumerate or delete the databases available for an origin from this API.
5044function getAllStoreNames(db) {
5045 return new Promise$1(function (resolve, reject) {
5046 db.transaction(function (t) {
5047 t.executeSql('SELECT name FROM sqlite_master ' + "WHERE type='table' AND name <> '__WebKitDatabaseInfoTable__'", [], function (t, results) {
5048 var storeNames = [];
5049
5050 for (var i = 0; i < results.rows.length; i++) {
5051 storeNames.push(results.rows.item(i).name);
5052 }
5053
5054 resolve({
5055 db: db,
5056 storeNames: storeNames
5057 });
5058 }, function (t, error) {
5059 reject(error);
5060 });
5061 }, function (sqlError) {
5062 reject(sqlError);
5063 });
5064 });
5065}
5066
5067function dropInstance$1(options, callback) {
5068 callback = getCallback.apply(this, arguments);
5069
5070 var currentConfig = this.config();
5071 options = typeof options !== 'function' && options || {};
5072 if (!options.name) {
5073 options.name = options.name || currentConfig.name;
5074 options.storeName = options.storeName || currentConfig.storeName;
5075 }
5076
5077 var self = this;
5078 var promise;
5079 if (!options.name) {
5080 promise = Promise$1.reject('Invalid arguments');
5081 } else {
5082 promise = new Promise$1(function (resolve) {
5083 var db;
5084 if (options.name === currentConfig.name) {
5085 // use the db reference of the current instance
5086 db = self._dbInfo.db;
5087 } else {
5088 db = openDatabase(options.name, '', '', 0);
5089 }
5090
5091 if (!options.storeName) {
5092 // drop all database tables
5093 resolve(getAllStoreNames(db));
5094 } else {
5095 resolve({
5096 db: db,
5097 storeNames: [options.storeName]
5098 });
5099 }
5100 }).then(function (operationInfo) {
5101 return new Promise$1(function (resolve, reject) {
5102 operationInfo.db.transaction(function (t) {
5103 function dropTable(storeName) {
5104 return new Promise$1(function (resolve, reject) {
5105 t.executeSql('DROP TABLE IF EXISTS ' + storeName, [], function () {
5106 resolve();
5107 }, function (t, error) {
5108 reject(error);
5109 });
5110 });
5111 }
5112
5113 var operations = [];
5114 for (var i = 0, len = operationInfo.storeNames.length; i < len; i++) {
5115 operations.push(dropTable(operationInfo.storeNames[i]));
5116 }
5117
5118 Promise$1.all(operations).then(function () {
5119 resolve();
5120 })["catch"](function (e) {
5121 reject(e);
5122 });
5123 }, function (sqlError) {
5124 reject(sqlError);
5125 });
5126 });
5127 });
5128 }
5129
5130 executeCallback(promise, callback);
5131 return promise;
5132}
5133
5134var webSQLStorage = {
5135 _driver: 'webSQLStorage',
5136 _initStorage: _initStorage$1,
5137 _support: isWebSQLValid(),
5138 iterate: iterate$1,
5139 getItem: getItem$1,
5140 setItem: setItem$1,
5141 removeItem: removeItem$1,
5142 clear: clear$1,
5143 length: length$1,
5144 key: key$1,
5145 keys: keys$1,
5146 dropInstance: dropInstance$1
5147};
5148
5149function isLocalStorageValid() {
5150 try {
5151 return typeof localStorage !== 'undefined' && 'setItem' in localStorage &&
5152 // in IE8 typeof localStorage.setItem === 'object'
5153 !!localStorage.setItem;
5154 } catch (e) {
5155 return false;
5156 }
5157}
5158
5159function _getKeyPrefix(options, defaultConfig) {
5160 var keyPrefix = options.name + '/';
5161
5162 if (options.storeName !== defaultConfig.storeName) {
5163 keyPrefix += options.storeName + '/';
5164 }
5165 return keyPrefix;
5166}
5167
5168// Check if localStorage throws when saving an item
5169function checkIfLocalStorageThrows() {
5170 var localStorageTestKey = '_localforage_support_test';
5171
5172 try {
5173 localStorage.setItem(localStorageTestKey, true);
5174 localStorage.removeItem(localStorageTestKey);
5175
5176 return false;
5177 } catch (e) {
5178 return true;
5179 }
5180}
5181
5182// Check if localStorage is usable and allows to save an item
5183// This method checks if localStorage is usable in Safari Private Browsing
5184// mode, or in any other case where the available quota for localStorage
5185// is 0 and there wasn't any saved items yet.
5186function _isLocalStorageUsable() {
5187 return !checkIfLocalStorageThrows() || localStorage.length > 0;
5188}
5189
5190// Config the localStorage backend, using options set in the config.
5191function _initStorage$2(options) {
5192 var self = this;
5193 var dbInfo = {};
5194 if (options) {
5195 for (var i in options) {
5196 dbInfo[i] = options[i];
5197 }
5198 }
5199
5200 dbInfo.keyPrefix = _getKeyPrefix(options, self._defaultConfig);
5201
5202 if (!_isLocalStorageUsable()) {
5203 return Promise$1.reject();
5204 }
5205
5206 self._dbInfo = dbInfo;
5207 dbInfo.serializer = localforageSerializer;
5208
5209 return Promise$1.resolve();
5210}
5211
5212// Remove all keys from the datastore, effectively destroying all data in
5213// the app's key/value store!
5214function clear$2(callback) {
5215 var self = this;
5216 var promise = self.ready().then(function () {
5217 var keyPrefix = self._dbInfo.keyPrefix;
5218
5219 for (var i = localStorage.length - 1; i >= 0; i--) {
5220 var key = localStorage.key(i);
5221
5222 if (key.indexOf(keyPrefix) === 0) {
5223 localStorage.removeItem(key);
5224 }
5225 }
5226 });
5227
5228 executeCallback(promise, callback);
5229 return promise;
5230}
5231
5232// Retrieve an item from the store. Unlike the original async_storage
5233// library in Gaia, we don't modify return values at all. If a key's value
5234// is `undefined`, we pass that value to the callback function.
5235function getItem$2(key, callback) {
5236 var self = this;
5237
5238 key = normalizeKey(key);
5239
5240 var promise = self.ready().then(function () {
5241 var dbInfo = self._dbInfo;
5242 var result = localStorage.getItem(dbInfo.keyPrefix + key);
5243
5244 // If a result was found, parse it from the serialized
5245 // string into a JS object. If result isn't truthy, the key
5246 // is likely undefined and we'll pass it straight to the
5247 // callback.
5248 if (result) {
5249 result = dbInfo.serializer.deserialize(result);
5250 }
5251
5252 return result;
5253 });
5254
5255 executeCallback(promise, callback);
5256 return promise;
5257}
5258
5259// Iterate over all items in the store.
5260function iterate$2(iterator, callback) {
5261 var self = this;
5262
5263 var promise = self.ready().then(function () {
5264 var dbInfo = self._dbInfo;
5265 var keyPrefix = dbInfo.keyPrefix;
5266 var keyPrefixLength = keyPrefix.length;
5267 var length = localStorage.length;
5268
5269 // We use a dedicated iterator instead of the `i` variable below
5270 // so other keys we fetch in localStorage aren't counted in
5271 // the `iterationNumber` argument passed to the `iterate()`
5272 // callback.
5273 //
5274 // See: github.com/mozilla/localForage/pull/435#discussion_r38061530
5275 var iterationNumber = 1;
5276
5277 for (var i = 0; i < length; i++) {
5278 var key = localStorage.key(i);
5279 if (key.indexOf(keyPrefix) !== 0) {
5280 continue;
5281 }
5282 var value = localStorage.getItem(key);
5283
5284 // If a result was found, parse it from the serialized
5285 // string into a JS object. If result isn't truthy, the
5286 // key is likely undefined and we'll pass it straight
5287 // to the iterator.
5288 if (value) {
5289 value = dbInfo.serializer.deserialize(value);
5290 }
5291
5292 value = iterator(value, key.substring(keyPrefixLength), iterationNumber++);
5293
5294 if (value !== void 0) {
5295 return value;
5296 }
5297 }
5298 });
5299
5300 executeCallback(promise, callback);
5301 return promise;
5302}
5303
5304// Same as localStorage's key() method, except takes a callback.
5305function key$2(n, callback) {
5306 var self = this;
5307 var promise = self.ready().then(function () {
5308 var dbInfo = self._dbInfo;
5309 var result;
5310 try {
5311 result = localStorage.key(n);
5312 } catch (error) {
5313 result = null;
5314 }
5315
5316 // Remove the prefix from the key, if a key is found.
5317 if (result) {
5318 result = result.substring(dbInfo.keyPrefix.length);
5319 }
5320
5321 return result;
5322 });
5323
5324 executeCallback(promise, callback);
5325 return promise;
5326}
5327
5328function keys$2(callback) {
5329 var self = this;
5330 var promise = self.ready().then(function () {
5331 var dbInfo = self._dbInfo;
5332 var length = localStorage.length;
5333 var keys = [];
5334
5335 for (var i = 0; i < length; i++) {
5336 var itemKey = localStorage.key(i);
5337 if (itemKey.indexOf(dbInfo.keyPrefix) === 0) {
5338 keys.push(itemKey.substring(dbInfo.keyPrefix.length));
5339 }
5340 }
5341
5342 return keys;
5343 });
5344
5345 executeCallback(promise, callback);
5346 return promise;
5347}
5348
5349// Supply the number of keys in the datastore to the callback function.
5350function length$2(callback) {
5351 var self = this;
5352 var promise = self.keys().then(function (keys) {
5353 return keys.length;
5354 });
5355
5356 executeCallback(promise, callback);
5357 return promise;
5358}
5359
5360// Remove an item from the store, nice and simple.
5361function removeItem$2(key, callback) {
5362 var self = this;
5363
5364 key = normalizeKey(key);
5365
5366 var promise = self.ready().then(function () {
5367 var dbInfo = self._dbInfo;
5368 localStorage.removeItem(dbInfo.keyPrefix + key);
5369 });
5370
5371 executeCallback(promise, callback);
5372 return promise;
5373}
5374
5375// Set a key's value and run an optional callback once the value is set.
5376// Unlike Gaia's implementation, the callback function is passed the value,
5377// in case you want to operate on that value only after you're sure it
5378// saved, or something like that.
5379function setItem$2(key, value, callback) {
5380 var self = this;
5381
5382 key = normalizeKey(key);
5383
5384 var promise = self.ready().then(function () {
5385 // Convert undefined values to null.
5386 // https://github.com/mozilla/localForage/pull/42
5387 if (value === undefined) {
5388 value = null;
5389 }
5390
5391 // Save the original value to pass to the callback.
5392 var originalValue = value;
5393
5394 return new Promise$1(function (resolve, reject) {
5395 var dbInfo = self._dbInfo;
5396 dbInfo.serializer.serialize(value, function (value, error) {
5397 if (error) {
5398 reject(error);
5399 } else {
5400 try {
5401 localStorage.setItem(dbInfo.keyPrefix + key, value);
5402 resolve(originalValue);
5403 } catch (e) {
5404 // localStorage capacity exceeded.
5405 // TODO: Make this a specific error/event.
5406 if (e.name === 'QuotaExceededError' || e.name === 'NS_ERROR_DOM_QUOTA_REACHED') {
5407 reject(e);
5408 }
5409 reject(e);
5410 }
5411 }
5412 });
5413 });
5414 });
5415
5416 executeCallback(promise, callback);
5417 return promise;
5418}
5419
5420function dropInstance$2(options, callback) {
5421 callback = getCallback.apply(this, arguments);
5422
5423 options = typeof options !== 'function' && options || {};
5424 if (!options.name) {
5425 var currentConfig = this.config();
5426 options.name = options.name || currentConfig.name;
5427 options.storeName = options.storeName || currentConfig.storeName;
5428 }
5429
5430 var self = this;
5431 var promise;
5432 if (!options.name) {
5433 promise = Promise$1.reject('Invalid arguments');
5434 } else {
5435 promise = new Promise$1(function (resolve) {
5436 if (!options.storeName) {
5437 resolve(options.name + '/');
5438 } else {
5439 resolve(_getKeyPrefix(options, self._defaultConfig));
5440 }
5441 }).then(function (keyPrefix) {
5442 for (var i = localStorage.length - 1; i >= 0; i--) {
5443 var key = localStorage.key(i);
5444
5445 if (key.indexOf(keyPrefix) === 0) {
5446 localStorage.removeItem(key);
5447 }
5448 }
5449 });
5450 }
5451
5452 executeCallback(promise, callback);
5453 return promise;
5454}
5455
5456var localStorageWrapper = {
5457 _driver: 'localStorageWrapper',
5458 _initStorage: _initStorage$2,
5459 _support: isLocalStorageValid(),
5460 iterate: iterate$2,
5461 getItem: getItem$2,
5462 setItem: setItem$2,
5463 removeItem: removeItem$2,
5464 clear: clear$2,
5465 length: length$2,
5466 key: key$2,
5467 keys: keys$2,
5468 dropInstance: dropInstance$2
5469};
5470
5471var sameValue = function sameValue(x, y) {
5472 return x === y || typeof x === 'number' && typeof y === 'number' && isNaN(x) && isNaN(y);
5473};
5474
5475var includes = function includes(array, searchElement) {
5476 var len = array.length;
5477 var i = 0;
5478 while (i < len) {
5479 if (sameValue(array[i], searchElement)) {
5480 return true;
5481 }
5482 i++;
5483 }
5484
5485 return false;
5486};
5487
5488var isArray = Array.isArray || function (arg) {
5489 return Object.prototype.toString.call(arg) === '[object Array]';
5490};
5491
5492// Drivers are stored here when `defineDriver()` is called.
5493// They are shared across all instances of localForage.
5494var DefinedDrivers = {};
5495
5496var DriverSupport = {};
5497
5498var DefaultDrivers = {
5499 INDEXEDDB: asyncStorage,
5500 WEBSQL: webSQLStorage,
5501 LOCALSTORAGE: localStorageWrapper
5502};
5503
5504var DefaultDriverOrder = [DefaultDrivers.INDEXEDDB._driver, DefaultDrivers.WEBSQL._driver, DefaultDrivers.LOCALSTORAGE._driver];
5505
5506var OptionalDriverMethods = ['dropInstance'];
5507
5508var LibraryMethods = ['clear', 'getItem', 'iterate', 'key', 'keys', 'length', 'removeItem', 'setItem'].concat(OptionalDriverMethods);
5509
5510var DefaultConfig = {
5511 description: '',
5512 driver: DefaultDriverOrder.slice(),
5513 name: 'localforage',
5514 // Default DB size is _JUST UNDER_ 5MB, as it's the highest size
5515 // we can use without a prompt.
5516 size: 4980736,
5517 storeName: 'keyvaluepairs',
5518 version: 1.0
5519};
5520
5521function callWhenReady(localForageInstance, libraryMethod) {
5522 localForageInstance[libraryMethod] = function () {
5523 var _args = arguments;
5524 return localForageInstance.ready().then(function () {
5525 return localForageInstance[libraryMethod].apply(localForageInstance, _args);
5526 });
5527 };
5528}
5529
5530function extend() {
5531 for (var i = 1; i < arguments.length; i++) {
5532 var arg = arguments[i];
5533
5534 if (arg) {
5535 for (var _key in arg) {
5536 if (arg.hasOwnProperty(_key)) {
5537 if (isArray(arg[_key])) {
5538 arguments[0][_key] = arg[_key].slice();
5539 } else {
5540 arguments[0][_key] = arg[_key];
5541 }
5542 }
5543 }
5544 }
5545 }
5546
5547 return arguments[0];
5548}
5549
5550var LocalForage = function () {
5551 function LocalForage(options) {
5552 _classCallCheck(this, LocalForage);
5553
5554 for (var driverTypeKey in DefaultDrivers) {
5555 if (DefaultDrivers.hasOwnProperty(driverTypeKey)) {
5556 var driver = DefaultDrivers[driverTypeKey];
5557 var driverName = driver._driver;
5558 this[driverTypeKey] = driverName;
5559
5560 if (!DefinedDrivers[driverName]) {
5561 // we don't need to wait for the promise,
5562 // since the default drivers can be defined
5563 // in a blocking manner
5564 this.defineDriver(driver);
5565 }
5566 }
5567 }
5568
5569 this._defaultConfig = extend({}, DefaultConfig);
5570 this._config = extend({}, this._defaultConfig, options);
5571 this._driverSet = null;
5572 this._initDriver = null;
5573 this._ready = false;
5574 this._dbInfo = null;
5575
5576 this._wrapLibraryMethodsWithReady();
5577 this.setDriver(this._config.driver)["catch"](function () {});
5578 }
5579
5580 // Set any config values for localForage; can be called anytime before
5581 // the first API call (e.g. `getItem`, `setItem`).
5582 // We loop through options so we don't overwrite existing config
5583 // values.
5584
5585
5586 LocalForage.prototype.config = function config(options) {
5587 // If the options argument is an object, we use it to set values.
5588 // Otherwise, we return either a specified config value or all
5589 // config values.
5590 if ((typeof options === 'undefined' ? 'undefined' : _typeof(options)) === 'object') {
5591 // If localforage is ready and fully initialized, we can't set
5592 // any new configuration values. Instead, we return an error.
5593 if (this._ready) {
5594 return new Error("Can't call config() after localforage " + 'has been used.');
5595 }
5596
5597 for (var i in options) {
5598 if (i === 'storeName') {
5599 options[i] = options[i].replace(/\W/g, '_');
5600 }
5601
5602 if (i === 'version' && typeof options[i] !== 'number') {
5603 return new Error('Database version must be a number.');
5604 }
5605
5606 this._config[i] = options[i];
5607 }
5608
5609 // after all config options are set and
5610 // the driver option is used, try setting it
5611 if ('driver' in options && options.driver) {
5612 return this.setDriver(this._config.driver);
5613 }
5614
5615 return true;
5616 } else if (typeof options === 'string') {
5617 return this._config[options];
5618 } else {
5619 return this._config;
5620 }
5621 };
5622
5623 // Used to define a custom driver, shared across all instances of
5624 // localForage.
5625
5626
5627 LocalForage.prototype.defineDriver = function defineDriver(driverObject, callback, errorCallback) {
5628 var promise = new Promise$1(function (resolve, reject) {
5629 try {
5630 var driverName = driverObject._driver;
5631 var complianceError = new Error('Custom driver not compliant; see ' + 'https://mozilla.github.io/localForage/#definedriver');
5632
5633 // A driver name should be defined and not overlap with the
5634 // library-defined, default drivers.
5635 if (!driverObject._driver) {
5636 reject(complianceError);
5637 return;
5638 }
5639
5640 var driverMethods = LibraryMethods.concat('_initStorage');
5641 for (var i = 0, len = driverMethods.length; i < len; i++) {
5642 var driverMethodName = driverMethods[i];
5643
5644 // when the property is there,
5645 // it should be a method even when optional
5646 var isRequired = !includes(OptionalDriverMethods, driverMethodName);
5647 if ((isRequired || driverObject[driverMethodName]) && typeof driverObject[driverMethodName] !== 'function') {
5648 reject(complianceError);
5649 return;
5650 }
5651 }
5652
5653 var configureMissingMethods = function configureMissingMethods() {
5654 var methodNotImplementedFactory = function methodNotImplementedFactory(methodName) {
5655 return function () {
5656 var error = new Error('Method ' + methodName + ' is not implemented by the current driver');
5657 var promise = Promise$1.reject(error);
5658 executeCallback(promise, arguments[arguments.length - 1]);
5659 return promise;
5660 };
5661 };
5662
5663 for (var _i = 0, _len = OptionalDriverMethods.length; _i < _len; _i++) {
5664 var optionalDriverMethod = OptionalDriverMethods[_i];
5665 if (!driverObject[optionalDriverMethod]) {
5666 driverObject[optionalDriverMethod] = methodNotImplementedFactory(optionalDriverMethod);
5667 }
5668 }
5669 };
5670
5671 configureMissingMethods();
5672
5673 var setDriverSupport = function setDriverSupport(support) {
5674 if (DefinedDrivers[driverName]) {
5675 console.info('Redefining LocalForage driver: ' + driverName);
5676 }
5677 DefinedDrivers[driverName] = driverObject;
5678 DriverSupport[driverName] = support;
5679 // don't use a then, so that we can define
5680 // drivers that have simple _support methods
5681 // in a blocking manner
5682 resolve();
5683 };
5684
5685 if ('_support' in driverObject) {
5686 if (driverObject._support && typeof driverObject._support === 'function') {
5687 driverObject._support().then(setDriverSupport, reject);
5688 } else {
5689 setDriverSupport(!!driverObject._support);
5690 }
5691 } else {
5692 setDriverSupport(true);
5693 }
5694 } catch (e) {
5695 reject(e);
5696 }
5697 });
5698
5699 executeTwoCallbacks(promise, callback, errorCallback);
5700 return promise;
5701 };
5702
5703 LocalForage.prototype.driver = function driver() {
5704 return this._driver || null;
5705 };
5706
5707 LocalForage.prototype.getDriver = function getDriver(driverName, callback, errorCallback) {
5708 var getDriverPromise = DefinedDrivers[driverName] ? Promise$1.resolve(DefinedDrivers[driverName]) : Promise$1.reject(new Error('Driver not found.'));
5709
5710 executeTwoCallbacks(getDriverPromise, callback, errorCallback);
5711 return getDriverPromise;
5712 };
5713
5714 LocalForage.prototype.getSerializer = function getSerializer(callback) {
5715 var serializerPromise = Promise$1.resolve(localforageSerializer);
5716 executeTwoCallbacks(serializerPromise, callback);
5717 return serializerPromise;
5718 };
5719
5720 LocalForage.prototype.ready = function ready(callback) {
5721 var self = this;
5722
5723 var promise = self._driverSet.then(function () {
5724 if (self._ready === null) {
5725 self._ready = self._initDriver();
5726 }
5727
5728 return self._ready;
5729 });
5730
5731 executeTwoCallbacks(promise, callback, callback);
5732 return promise;
5733 };
5734
5735 LocalForage.prototype.setDriver = function setDriver(drivers, callback, errorCallback) {
5736 var self = this;
5737
5738 if (!isArray(drivers)) {
5739 drivers = [drivers];
5740 }
5741
5742 var supportedDrivers = this._getSupportedDrivers(drivers);
5743
5744 function setDriverToConfig() {
5745 self._config.driver = self.driver();
5746 }
5747
5748 function extendSelfWithDriver(driver) {
5749 self._extend(driver);
5750 setDriverToConfig();
5751
5752 self._ready = self._initStorage(self._config);
5753 return self._ready;
5754 }
5755
5756 function initDriver(supportedDrivers) {
5757 return function () {
5758 var currentDriverIndex = 0;
5759
5760 function driverPromiseLoop() {
5761 while (currentDriverIndex < supportedDrivers.length) {
5762 var driverName = supportedDrivers[currentDriverIndex];
5763 currentDriverIndex++;
5764
5765 self._dbInfo = null;
5766 self._ready = null;
5767
5768 return self.getDriver(driverName).then(extendSelfWithDriver)["catch"](driverPromiseLoop);
5769 }
5770
5771 setDriverToConfig();
5772 var error = new Error('No available storage method found.');
5773 self._driverSet = Promise$1.reject(error);
5774 return self._driverSet;
5775 }
5776
5777 return driverPromiseLoop();
5778 };
5779 }
5780
5781 // There might be a driver initialization in progress
5782 // so wait for it to finish in order to avoid a possible
5783 // race condition to set _dbInfo
5784 var oldDriverSetDone = this._driverSet !== null ? this._driverSet["catch"](function () {
5785 return Promise$1.resolve();
5786 }) : Promise$1.resolve();
5787
5788 this._driverSet = oldDriverSetDone.then(function () {
5789 var driverName = supportedDrivers[0];
5790 self._dbInfo = null;
5791 self._ready = null;
5792
5793 return self.getDriver(driverName).then(function (driver) {
5794 self._driver = driver._driver;
5795 setDriverToConfig();
5796 self._wrapLibraryMethodsWithReady();
5797 self._initDriver = initDriver(supportedDrivers);
5798 });
5799 })["catch"](function () {
5800 setDriverToConfig();
5801 var error = new Error('No available storage method found.');
5802 self._driverSet = Promise$1.reject(error);
5803 return self._driverSet;
5804 });
5805
5806 executeTwoCallbacks(this._driverSet, callback, errorCallback);
5807 return this._driverSet;
5808 };
5809
5810 LocalForage.prototype.supports = function supports(driverName) {
5811 return !!DriverSupport[driverName];
5812 };
5813
5814 LocalForage.prototype._extend = function _extend(libraryMethodsAndProperties) {
5815 extend(this, libraryMethodsAndProperties);
5816 };
5817
5818 LocalForage.prototype._getSupportedDrivers = function _getSupportedDrivers(drivers) {
5819 var supportedDrivers = [];
5820 for (var i = 0, len = drivers.length; i < len; i++) {
5821 var driverName = drivers[i];
5822 if (this.supports(driverName)) {
5823 supportedDrivers.push(driverName);
5824 }
5825 }
5826 return supportedDrivers;
5827 };
5828
5829 LocalForage.prototype._wrapLibraryMethodsWithReady = function _wrapLibraryMethodsWithReady() {
5830 // Add a stub for each driver API method that delays the call to the
5831 // corresponding driver method until localForage is ready. These stubs
5832 // will be replaced by the driver methods as soon as the driver is
5833 // loaded, so there is no performance impact.
5834 for (var i = 0, len = LibraryMethods.length; i < len; i++) {
5835 callWhenReady(this, LibraryMethods[i]);
5836 }
5837 };
5838
5839 LocalForage.prototype.createInstance = function createInstance(options) {
5840 return new LocalForage(options);
5841 };
5842
5843 return LocalForage;
5844}();
5845
5846// The actual localForage object that we expose as a module or via a
5847// global. It's extended by pulling in one of our other libraries.
5848
5849
5850var localforage_js = new LocalForage();
5851
5852module.exports = localforage_js;
5853
5854},{"3":3}]},{},[4])(4)
5855});
5856
5857
5858/***/ })
5859/******/ ]);
5860});