!function(e){"object"==typeof exports?module.exports=e():"function"==typeof define&&define.amd?define(e):"undefined"!=typeof window?window.log=e():"undefined"!=typeof global?global.log=e():"undefined"!=typeof self&&(self.log=e())}(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);throw new Error("Cannot find module '"+o+"'")}var f=n[o]={exports:{}};t[o][0].call(f.exports,function(e){var n=t[o][1][e];return s(n?n:e)},f,f.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(require, module, exports) {
		var MemStore = function() {
			this.events = [];
		};

		MemStore.prototype.append = function(event) {
			this.events.push(event);
		};

		MemStore.prototype.clear = function() {
			this.events = [];
		};

		MemStore.prototype.getEvents = function() {
			return this.events;
		};

		module.exports = MemStore;
	}, {}],
	2: [function(require, module, exports) {

		function initialize() {
			saveIndex({first: 0, next: 0});
		}

		function saveIndex(index) {
			try {
				localStorage.log = JSON.stringify(index);
			} catch (ex) {}
		}

		function loadIndex() {
			try {
				return JSON.parse(localStorage.log);
			} catch (ex) {
				return {first: 0, next: 0};
			}
		}

		function foreachKey(cb, startId) {
			var index = loadIndex();
			startId = startId || index.first;

			while (startId < index.next) {
				cb('log' + startId++);
			}
		}

		var LocalStore = function(opts) {
			opts = opts || {};
			this.maxSize = opts.maxSize || 500;

			if (typeof localStorage.log === 'undefined') {
				initialize();
			}
		};

		LocalStore.prototype.clear = function() {
			foreachKey(function(key) {
				delete localStorage[key];
			});
			initialize();
		};

		LocalStore.prototype.purgeOldEvents = function() {
			var index = loadIndex();
			var toPurge = index.first + Math.round(this.maxSize / 3);

			while (index.first < toPurge) {
				delete localStorage['log' + index.first++];
			}
			saveIndex(index);
		};

		LocalStore.prototype.getNext = function() {
			return loadIndex().next;
		};

		LocalStore.prototype.getEvents = function(startId) {
			var events = [];
			foreachKey(function(key) {
				try {
					events.push(JSON.parse(localStorage[key]));
				} catch (ex) {}
			}, startId);
			return events;
		};

		LocalStore.prototype.append = function(event) {
			var index = loadIndex();
			var currentSize = index.next - index.first;
			if (currentSize >= this.maxSize) {
				this.purgeOldEvents();
				index = loadIndex();
			}

			var key = 'log' + index.next;
			try {
				try {
					localStorage[key] = JSON.stringify(event);
				} catch (ex) {
					this.purgeOldEvents();
					index = loadIndex();

					key = 'log' + index.next;
					localStorage[key] = JSON.stringify(event);
				}
				++index.next;
				saveIndex(index);
			} catch (ex2) {
				console.warn("WARNING: OUT OF LOCALSTORAGE!");
			}
		};

		LocalStore.prototype.upgrade = function() {
			if (!localStorage['plog-index']) {
				return;
			}
			try {
				var index = JSON.parse(localStorage['plog-index']);
				delete localStorage['plog-index'];
				localStorage.log = JSON.stringify({
					first: index.firstId,
					next: index.nextId
				});

				for (var i = index.firstId; i < index.nextId; ++i) {
					var event = JSON.parse(localStorage['plog-event-' + i]);
					delete localStorage['plog-event-' + i];
					localStorage['log' + i] = JSON.stringify([event.level, event.date, event.message]);
				}
			} catch (ex) {}
		};

		module.exports = LocalStore;
	}, {}],
	3: [function(require, module, exports) {
		var RelayStore = function() {};

		RelayStore.prototype.append = function(event) {
			event[1] = event[1].getTime();
			window.postMessage({method: 'log', event: event}, location.origin);
		};

		RelayStore.prototype.clear = function() {}

		RelayStore.prototype.getEvents = function() {
			return [];
		};

		module.exports = RelayStore;
	}, {}],
	4: [function(require, module, exports) {
		var DBStore = function(opts) {
			opts = opts || {};
			opts.size = opts.size || 80;
			this.db = null;
			function createDB(opts) {
				const that = this;
				try {
					const req = indexedDB.open("plog", 1);
					req.onerror = function(ev) {
						opts._retry = (opts._retry || 0) + 1;
						//console.error("LOG RETRY #"+opts._retry);
						if (opts._retry < 100) {
							setTimeout(createDB.bind(that, opts), 50);
						} else {
							console.warn("WARNING: COULDN'T CREATE DATABASE FOR LOGGING! " + ev);
							localStorage.statSessionLogErr = Number(localStorage.statSessionLogErr || 0) + 1;
							localStorage.statTotalLogErr = Number(localStorage.statTotalLogErr || 0) + 1;
						}
					}
					req.onsuccess = function(ev) {
						that.db = ev.target.result;
					}
					req.onupgradeneeded = function(ev) {
						const db = ev.target.result;
						const store = db.createObjectStore("plog", {keyPath: "id", autoIncrement: true});
						store.createIndex("d", "d", {unique: false});
					}
				} catch(err) {
					console.warn("WARNING: COULDN'T CREATE DATABASE FOR LOGGING! " + err.message)
					localStorage.statSessionLogErr = Number(localStorage.statSessionLogErr || 0) + 1;
					localStorage.statTotalLogErr = Number(localStorage.statTotalLogErr || 0) + 1;
				}
			}
			createDB.call(this, opts);
		};

		DBStore.prototype.upgrade = function() {
			if (!localStorage.log) return;
			if (!this.db) return;
			try {
				const tx = this.db.transaction("plog", "readwrite");
				const store = tx.objectStore("plog");
				var req = null;
				var index = JSON.parse(localStorage.log);
				delete localStorage.log;

				for (var i = index.first; i < index.next; ++i) {
					var event = JSON.parse(localStorage['log' + i]);
					delete localStorage['log' + i];

					req = store.add({l: event[0], d: new Date(event[1]).getTime(), m: event[2]});
				}
				req && (req.onsuccess = function(ev) {
					localStorage.plogNext = ev.target.result + 1;
				})
			} catch (ex) {}
		};

		DBStore.prototype.append = function(event) {
			if (!this.db) return;
			const that = this;
			try {
				const tx = this.db.transaction("plog", "readwrite");
				tx.onerror = function(ev) {
					event._retry = (event._retry || 0) + 1;
					//console.error("LOG RETRY #"+event._retry);
					if (event._retry < 100) {
						setTimeout(DBStore.prototype.append.bind(that, event), 50);
					} else {
						console.error("Error writing log entry to DB: " + ev)
						localStorage.statSessionLogErr = Number(localStorage.statSessionLogErr || 0) + 1
						localStorage.statTotalLogErr = Number(localStorage.statTotalLogErr || 0) + 1
					}
				}
				const req = tx.objectStore("plog").add({l: event[0], d: event[1].getTime ? event[1].getTime() : event[1], m: event[2]});
				req.onsuccess = function(ev) {
					localStorage.plogNext = ev.target.result + 1;
				}
			} catch (ex) {}
		};

		DBStore.prototype.clear = function() {
			if (!this.db) return;
			try {
				const store = this.db.transaction("plog", "readwrite").objectStore("plog");
				const req = store.clear();
				req.onsuccess = function(ev) {
					localStorage.plogNext = 0;
				}
			} catch (ex) {}
		};

		DBStore.prototype.purgeBefore = function(date) {
			if (!this.db) return;
			try {
				const store = this.db.transaction("plog", "readwrite").objectStore("plog");
				const req = store.index("d").getKey(IDBKeyRange.lowerBound(date.getTime ? date.getTime() : date));
				req.onsuccess = function(ev) {
					const id = ev.target.result;
					id && store.delete(IDBKeyRange.upperBound(id, true));
				}
			} catch (ex) {}
		};

		DBStore.prototype.getNext = function() {
			return localStorage.plogNext || 1;
		};

		DBStore.prototype.getEvents = function() {
			return [];
		};

		DBStore.prototype.getEventsCB = function(cb, startId, limit, doSort) {
			if (!this.db) return;
			try {
				const tx = this.db.transaction("plog", "readonly");
				const req = tx.objectStore("plog").getAll(startId ? IDBKeyRange.lowerBound(startId) : undefined, limit);
				req.onsuccess = function(ev) {
					cb(ev.target.result);
				}
			} catch (ex) {}
		};

		module.exports = DBStore;
	}, {}],
	5: [function(require, module, exports) {
		var log = {};

		log.store = {
			LocalStore: require('./LocalStore'),
			MemStore: require('./MemStore'),
			RelayStore: require('./RelayStore'),
			DBStore: require('./DBStore'),
		};

		log.level = {
			DEBUG: 0,
			INFO: 1,
			WARN: 2,
			ERROR: 3,
			FATAL: 4,
			NOTICE: 5,
			SUCCESS: 6
		};
		log.levelName = [];

		var currentStore, currentLevel;

		log.reset = function() {
			const isBG = chrome.extension && chrome.extension.getBackgroundPage() === window
			currentStore = isBG ? new log.store.DBStore() : new log.store.RelayStore();
			currentLevel = isBG ? log.level.INFO : log.level.DEBUG;
		};

		log.reset();

		log.useStore = function(store) {
			currentStore = store;
		};

		log.getStore = function() {
			return currentStore;
		};

		log.upgrade = function() {
			if (currentStore && currentStore.upgrade) {
				currentStore.upgrade();
			}
		};

		log.getLevel = function() {
			return currentLevel;
		};

		log.setLevel = function(level) {
			currentLevel = level;
		};

		function write(level, msg) {
			if (level < currentLevel)
				return;
			var date = new Date();
			currentStore.append([level, date, msg]);

			console.log((date.toISOString().replace('T', ' ').slice(0, -1)) + ': ' + log.levelName[level] + ': ' + msg);
		}

		for (var levelName in log.level) {
			log.levelName.push(levelName);
			const level = log.level[levelName];
			log[levelName.toLowerCase()] = function(msg) {
				write(level, msg);
			};
		}

		module.exports = log;
	}, {
		"./MemStore": 1,
		"./LocalStore": 2,
		"./RelayStore": 3,
		"./DBStore": 4
	}]}, {}, [5])(5)
});
