summary refs log tree commit diff
path: root/resources/app/node_modules/loglevel/test
diff options
context:
space:
mode:
Diffstat (limited to 'resources/app/node_modules/loglevel/test')
-rw-r--r--resources/app/node_modules/loglevel/test/.jshintrc34
-rw-r--r--resources/app/node_modules/loglevel/test/console-fallback-test.js98
-rw-r--r--resources/app/node_modules/loglevel/test/cookie-test.js122
-rw-r--r--resources/app/node_modules/loglevel/test/default-level-test.js60
-rw-r--r--resources/app/node_modules/loglevel/test/get-current-level-test.js48
-rw-r--r--resources/app/node_modules/loglevel/test/global-integration-with-new-context.js29
-rw-r--r--resources/app/node_modules/loglevel/test/global-integration.js25
-rw-r--r--resources/app/node_modules/loglevel/test/integration-smoke-test.js71
-rw-r--r--resources/app/node_modules/loglevel/test/level-setting-test.js281
-rw-r--r--resources/app/node_modules/loglevel/test/local-storage-test.js201
-rw-r--r--resources/app/node_modules/loglevel/test/manual-test.html8
-rw-r--r--resources/app/node_modules/loglevel/test/method-factory-test.js42
-rw-r--r--resources/app/node_modules/loglevel/test/multiple-logger-test.js139
-rw-r--r--resources/app/node_modules/loglevel/test/node-integration.js27
-rw-r--r--resources/app/node_modules/loglevel/test/test-context-using-apply.js6
-rw-r--r--resources/app/node_modules/loglevel/test/test-helpers.js168
-rw-r--r--resources/app/node_modules/loglevel/test/test-qunit.html19
-rw-r--r--resources/app/node_modules/loglevel/test/test-qunit.js51
-rw-r--r--resources/app/node_modules/loglevel/test/vendor/json2.js486
19 files changed, 1915 insertions, 0 deletions
diff --git a/resources/app/node_modules/loglevel/test/.jshintrc b/resources/app/node_modules/loglevel/test/.jshintrc
new file mode 100644
index 0000000..cfbbfc7
--- /dev/null
+++ b/resources/app/node_modules/loglevel/test/.jshintrc
@@ -0,0 +1,34 @@
+{
+  "curly": true,
+  "globalstrict": true,
+  "eqeqeq": true,
+  "immed": true,
+  "latedef": true,
+  "newcap": true,
+  "noarg": true,
+  "sub": true,
+  "undef": true,
+  "boss": true,
+  "eqnull": true,
+  "es3": true,
+  "globals": {
+    "window": true,
+    "console": true,
+    "define": false,
+    "require": false,
+    "exports": false,
+    "_": false,
+    "afterEach": false,
+    "beforeEach": false,
+    "confirm": false,
+    "context": false,
+    "describe": false,
+    "xdescribe": false,
+    "expect": false,
+    "it": false,
+    "jasmine": false,
+    "waitsFor": false,
+    "runs": false,
+    "Symbol": false
+  }
+}
diff --git a/resources/app/node_modules/loglevel/test/console-fallback-test.js b/resources/app/node_modules/loglevel/test/console-fallback-test.js
new file mode 100644
index 0000000..58fda9d
--- /dev/null
+++ b/resources/app/node_modules/loglevel/test/console-fallback-test.js
@@ -0,0 +1,98 @@
+"use strict";

+

+function consoleLogIsCalledBy(log, methodName) {

+    it(methodName + " calls console.log", function() {

+        log.setLevel(log.levels.TRACE);

+        log[methodName]("Log message for call to " + methodName);

+        expect(console.log.calls.length).toEqual(1);

+    });

+}

+

+function mockConsole() {

+    return {"log" : jasmine.createSpy("console.log")};

+}

+

+define(['../lib/loglevel'], function(log) {

+    var originalConsole = window.console;

+

+    describe("Fallback functionality:", function() {

+        describe("with no console present", function() {

+            beforeEach(function() {

+                window.console = undefined;

+            });

+

+            afterEach(function() {

+                window.console = originalConsole;

+            });

+

+            it("silent method calls are allowed", function() {

+                var result = log.setLevel(log.levels.SILENT);

+                log.trace("hello");

+

+                expect(result).toBeUndefined();

+            });

+

+            it("setting an active level gently returns an error string", function() {

+                var result = log.setLevel(log.levels.TRACE);

+                expect(result).toEqual("No console available for logging");

+            });

+

+            it("active method calls are allowed, once the active setLevel fails", function() {

+                log.setLevel(log.levels.TRACE);

+                log.trace("hello");

+            });

+

+            describe("if a console later appears", function () {

+                it("logging is re-enabled and works correctly when next used", function () {

+                    log.setLevel(log.levels.WARN);

+

+                    window.console = mockConsole();

+                    log.error("error");

+

+                    expect(window.console.log).toHaveBeenCalled();

+                });

+

+                it("logging is re-enabled but does nothing when used at a blocked level", function () {

+                    log.setLevel(log.levels.WARN);

+

+                    window.console = mockConsole();

+                    log.trace("trace");

+

+                    expect(window.console.log).not.toHaveBeenCalled();

+                });

+

+                it("changing level works correctly from that point", function () {

+                    window.console = mockConsole();

+                    var result = log.setLevel(log.levels.WARN);

+

+                    expect(result).toBeUndefined();

+                });

+            });

+        });

+

+        describe("with a console that only supports console.log", function() {

+            beforeEach(function() {

+                window.console = mockConsole();

+            });

+

+            afterEach(function() {

+                window.console = originalConsole;

+            });

+

+            it("log can be set to silent", function() {

+                log.setLevel(log.levels.SILENT);

+            });

+

+            it("log can be set to an active level", function() {

+                log.setLevel(log.levels.ERROR);

+            });

+

+            consoleLogIsCalledBy(log, "trace");

+            consoleLogIsCalledBy(log, "debug");

+            consoleLogIsCalledBy(log, "info");

+            consoleLogIsCalledBy(log, "warn");

+            consoleLogIsCalledBy(log, "trace");

+        });

+    });

+});

+

diff --git a/resources/app/node_modules/loglevel/test/cookie-test.js b/resources/app/node_modules/loglevel/test/cookie-test.js
new file mode 100644
index 0000000..ebe2f8f
--- /dev/null
+++ b/resources/app/node_modules/loglevel/test/cookie-test.js
@@ -0,0 +1,122 @@
+"use strict";

+

+define(['test/test-helpers'], function(testHelpers) {

+    var describeIf = testHelpers.describeIf;

+    var it = testHelpers.itWithFreshLog;

+

+    var originalConsole = window.console;

+    var originalDocument = window.document;

+

+    describeIf(testHelpers.isCookieStorageAvailable() && !testHelpers.isLocalStorageAvailable(),

+               "Cookie-only persistence tests:", function() {

+

+        beforeEach(function() {

+            window.console = {"log" : jasmine.createSpy("console.log")};

+            this.addMatchers({

+                "toBeAtLevel" : testHelpers.toBeAtLevel,

+                "toBeTheStoredLevel" : testHelpers.toBeTheLevelStoredByCookie

+            });

+        });

+

+        afterEach(function() {

+            window.console = originalConsole;

+        });

+

+        describe("If no level is saved", function() {

+            beforeEach(function() {

+                testHelpers.clearStoredLevels();

+            });

+

+            it("log level is set to warn by default", function(log) {

+                expect(log).toBeAtLevel("warn");

+            });

+

+            it("warn is persisted as the current level", function(log) {

+                expect("warn").toBeTheStoredLevel();

+            });

+

+            it("log can be set to info level", function(log) {

+                log.setLevel("info");

+                expect(log).toBeAtLevel("info");

+            });

+

+            it("log.setLevel() sets a cookie with the given level", function(log) {

+                log.setLevel("debug");

+                expect("debug").toBeTheStoredLevel();

+            });

+        });

+

+        describe("If info level is saved", function() {

+            beforeEach(function() {

+                testHelpers.setStoredLevel("info");

+            });

+

+            it("info is the default log level", function(log) {

+                expect(log).toBeAtLevel("info");

+            });

+

+            it("log can be changed to warn level", function(log) {

+                log.setLevel("warn");

+                expect(log).toBeAtLevel("warn");

+            });

+

+            it("log.setLevel() overwrites the saved level", function(log) {

+                log.setLevel("error");

+

+                expect("error").toBeTheStoredLevel();

+                expect("info").not.toBeTheStoredLevel();

+            });

+        });

+

+        describe("If the level is saved with other data", function() {

+            beforeEach(function() {

+                window.document.cookie = "qwe=asd";

+                window.document.cookie = "loglevel=ERROR";

+                window.document.cookie = "msg=hello world";

+            });

+

+            it("error is the default log level", function(log) {

+                expect(log).toBeAtLevel("error");

+            });

+

+            it("log can be changed to silent level", function(log) {

+                log.setLevel("silent");

+                expect(log).toBeAtLevel("silent");

+            });

+

+            it("log.setLevel() overrides the saved level only", function(log) {

+                log.setLevel("debug");

+

+                expect('debug').toBeTheStoredLevel();

+                expect(window.document.cookie).toContain("msg=hello world");

+            });

+        });

+

+        describe("If the level cookie is set incorrectly", function() {

+            beforeEach(function() {

+                testHelpers.setCookieStoredLevel('gibberish');

+            });

+

+            it("warn is the default log level", function(log) {

+                expect(log).toBeAtLevel("warn");

+            });

+

+            it("warn is persisted as the current level, overriding the invalid cookie", function(log) {

+                expect("warn").toBeTheStoredLevel();

+            });

+

+            it("log can be changed to info level", function(log) {

+                log.setLevel("info");

+                expect(log).toBeAtLevel("info");

+            });

+

+            it("log.setLevel() overrides the saved level with the new level", function(log) {

+                expect('debug').not.toBeTheStoredLevel();

+

+                log.setLevel("debug");

+

+                expect('debug').toBeTheStoredLevel();

+            });

+        });

+    });

+});

diff --git a/resources/app/node_modules/loglevel/test/default-level-test.js b/resources/app/node_modules/loglevel/test/default-level-test.js
new file mode 100644
index 0000000..6e3e3c7
--- /dev/null
+++ b/resources/app/node_modules/loglevel/test/default-level-test.js
@@ -0,0 +1,60 @@
+"use strict";
+
+define(['test/test-helpers'], function(testHelpers) {
+    var describeIf = testHelpers.describeIf;
+    var it = testHelpers.itWithFreshLog;
+
+    var originalConsole = window.console;
+
+    describe("Setting default log level tests:", function() {
+
+        beforeEach(function() {
+            window.console = {"log" : jasmine.createSpy("console.log")};
+            this.addMatchers({
+                "toBeAtLevel" : testHelpers.toBeAtLevel,
+                "toBeTheStoredLevel" : testHelpers.toBeTheLevelStoredByLocalStorage
+            });
+
+            testHelpers.clearStoredLevels();
+        });
+
+        afterEach(function() {
+            window.console = originalConsole;
+        });
+
+        describe("If no level is saved", function() {
+            it("new level is always set", function(log) {
+                log.setDefaultLevel("trace");
+                expect(log).toBeAtLevel("trace");
+            });
+
+            it("level is not persisted", function(log) {
+                log.setDefaultLevel("debug");
+                expect("debug").not.toBeTheStoredLevel();
+            });
+        });
+        
+        describe("If a level is saved", function () {
+            beforeEach(function () {
+                testHelpers.setStoredLevel("trace");
+            });
+            
+            it("saved level is not modified", function (log) {
+                log.setDefaultLevel("debug");
+                expect(log).toBeAtLevel("trace");
+            });
+        });
+
+        describe("If the level is stored incorrectly", function() {
+            beforeEach(function() {
+                testHelpers.setLocalStorageStoredLevel("gibberish");
+            });
+
+            it("new level is set", function(log) {
+                log.setDefaultLevel("debug");
+                expect(log).toBeAtLevel("debug");
+                expect("debug").not.toBeTheStoredLevel();
+            });
+        });
+    });
+});
diff --git a/resources/app/node_modules/loglevel/test/get-current-level-test.js b/resources/app/node_modules/loglevel/test/get-current-level-test.js
new file mode 100644
index 0000000..01902ae
--- /dev/null
+++ b/resources/app/node_modules/loglevel/test/get-current-level-test.js
@@ -0,0 +1,48 @@
+"use strict";
+
+define(['test/test-helpers'], function(testHelpers) {
+    var describeIf = testHelpers.describeIf;
+    var it = testHelpers.itWithFreshLog;
+
+    var originalConsole = window.console;
+
+    describe("Setting default log level tests:", function() {
+
+        beforeEach(function() {
+            window.console = {"log" : jasmine.createSpy("console.log")};
+        });
+
+        afterEach(function() {
+            window.console = originalConsole;
+        });
+
+        describe("If no level is saved", function() {
+            it("current level is the default level", function(log) {
+                log.setDefaultLevel("trace");
+                expect(log.getLevel()).toBe(log.levels.TRACE);
+            });
+        });
+
+        describe("If a level is saved", function () {
+            beforeEach(function () {
+                testHelpers.setStoredLevel("trace");
+            });
+
+            it("current level is the level which has been saved", function (log) {
+                log.setDefaultLevel("debug");
+                expect(log.getLevel()).toBe(log.levels.TRACE);
+            });
+        });
+
+        describe("If the level is stored incorrectly", function() {
+            beforeEach(function() {
+                testHelpers.setLocalStorageStoredLevel("gibberish");
+            });
+
+            it("current level is the default level", function(log) {
+                log.setDefaultLevel("debug");
+                expect(log.getLevel()).toBe(log.levels.DEBUG);
+            });
+        });
+    });
+});
diff --git a/resources/app/node_modules/loglevel/test/global-integration-with-new-context.js b/resources/app/node_modules/loglevel/test/global-integration-with-new-context.js
new file mode 100644
index 0000000..b7324e5
--- /dev/null
+++ b/resources/app/node_modules/loglevel/test/global-integration-with-new-context.js
@@ -0,0 +1,29 @@
+/* global MyCustomLogger, log */
+"use strict";
+
+describe("loglevel from a global <script> tag with a custom context", function () {
+    it("is available globally", function () {
+        expect(MyCustomLogger).not.toBeUndefined();
+    });
+
+    it("doesn't have log defined globally", function () {
+        expect(window.log).not.toBeDefined();
+    });
+
+    it("allows setting the logging level", function () {
+        MyCustomLogger.setLevel(MyCustomLogger.levels.TRACE);
+        MyCustomLogger.setLevel(MyCustomLogger.levels.DEBUG);
+        MyCustomLogger.setLevel(MyCustomLogger.levels.INFO);
+        MyCustomLogger.setLevel(MyCustomLogger.levels.WARN);
+        MyCustomLogger.setLevel(MyCustomLogger.levels.ERROR);
+    });
+
+    it("successfully logs", function () {
+        window.console = { "log": jasmine.createSpy("log") };
+
+        MyCustomLogger.setLevel(MyCustomLogger.levels.INFO);
+        MyCustomLogger.info("test message");
+
+        expect(console.log).toHaveBeenCalledWith("test message");
+    });
+});
diff --git a/resources/app/node_modules/loglevel/test/global-integration.js b/resources/app/node_modules/loglevel/test/global-integration.js
new file mode 100644
index 0000000..149474c
--- /dev/null
+++ b/resources/app/node_modules/loglevel/test/global-integration.js
@@ -0,0 +1,25 @@
+/* global log */
+"use strict";
+
+describe("loglevel from a global <script> tag", function () {
+    it("is available globally", function () {
+        expect(log).not.toBeUndefined();
+    });
+
+    it("allows setting the logging level", function () {
+        log.setLevel(log.levels.TRACE);
+        log.setLevel(log.levels.DEBUG);
+        log.setLevel(log.levels.INFO);
+        log.setLevel(log.levels.WARN);
+        log.setLevel(log.levels.ERROR);
+    });
+
+    it("successfully logs", function () {
+        window.console = { "log": jasmine.createSpy("log") };
+
+        log.setLevel(log.levels.INFO);
+        log.info("test message");
+
+        expect(console.log).toHaveBeenCalledWith("test message");
+    });
+});
\ No newline at end of file
diff --git a/resources/app/node_modules/loglevel/test/integration-smoke-test.js b/resources/app/node_modules/loglevel/test/integration-smoke-test.js
new file mode 100644
index 0000000..7c7850e
--- /dev/null
+++ b/resources/app/node_modules/loglevel/test/integration-smoke-test.js
@@ -0,0 +1,71 @@
+"use strict";
+
+define(['../lib/loglevel', 'test/test-helpers'], function(log, testHelpers) {
+    var describeIf = testHelpers.describeIf;
+    var itIf = testHelpers.itIf;
+
+    describe("Integration smoke tests:", function() {
+        describe("log methods", function() {
+            it("can all be disabled", function() {
+                log.setLevel(log.levels.SILENT);
+                log.trace("trace");
+                log.debug("debug");
+                log.log("log");
+                log.info("info");
+                log.warn("warn");
+                log.error("error");
+            });
+        });
+
+        describeIf(typeof console !== "undefined", "log methods", function() {
+            it("can all be called", function() {
+                if (typeof console !== "undefined") {
+                    log.setLevel(log.levels.TRACE);
+                }
+
+                log.trace("trace");
+                log.debug("debug");
+                log.log("log");
+                log.info("info");
+                log.warn("warn");
+                log.error("error");
+            });
+        });
+
+        describeIf(typeof console !== "undefined", "log levels", function() {
+            beforeEach(function() {
+                this.addMatchers({
+                    "toBeTheStoredLevel" : testHelpers.toBeTheStoredLevel
+                });
+            });
+
+            it("are all settable", function() {
+                log.setLevel(log.levels.TRACE);
+                log.setLevel(log.levels.DEBUG);
+                log.setLevel(log.levels.INFO);
+                log.setLevel(log.levels.WARN);
+                log.setLevel(log.levels.ERROR);
+            });
+
+            itIf(testHelpers.isAnyLevelStoragePossible(), "are persisted", function() {
+                log.setLevel(log.levels.TRACE);
+                expect('trace').toBeTheStoredLevel();
+
+                log.setLevel(log.levels.DEBUG);
+                expect('debug').toBeTheStoredLevel();
+
+                log.setLevel(log.levels.INFO);
+                expect('info').toBeTheStoredLevel();
+
+                log.setLevel(log.levels.WARN);
+                expect('warn').toBeTheStoredLevel();
+
+                log.setLevel(log.levels.ERROR);
+                expect('error').toBeTheStoredLevel();
+
+                log.setLevel(log.levels.SILENT);
+                expect('silent').toBeTheStoredLevel();
+            });
+        });
+    });
+});
diff --git a/resources/app/node_modules/loglevel/test/level-setting-test.js b/resources/app/node_modules/loglevel/test/level-setting-test.js
new file mode 100644
index 0000000..f5d6d13
--- /dev/null
+++ b/resources/app/node_modules/loglevel/test/level-setting-test.js
@@ -0,0 +1,281 @@
+"use strict";
+
+var logMethods = [
+    "trace",
+    "debug",
+    "info",
+    "warn",
+    "error"
+];
+
+function getConsoleMethod(logMethodName) {
+    if (logMethodName === 'debug') {
+        return console.log;
+    } else {
+        return console[logMethodName];
+    }
+}
+
+define(['../lib/loglevel'], function(log) {
+    var originalConsole = window.console;
+
+    describe("Basic log levels changing tests:", function() {
+        beforeEach(function() {
+            window.console = {};
+
+            for (var ii = 0; ii < logMethods.length; ii++) {
+                window.console[logMethods[ii]] = jasmine.createSpy(logMethods[ii]);
+            }
+
+            window.console.log = jasmine.createSpy('log');
+        });
+
+        afterEach(function() {
+            window.console = originalConsole;
+        });
+
+        describe("log.enableAll()", function() {
+            it("enables all log methods", function() {
+                log.enableAll(false);
+
+                for (var ii = 0; ii < logMethods.length; ii++) {
+                    var method = logMethods[ii];
+                    log[method]("a log message");
+
+                    expect(getConsoleMethod(method)).toHaveBeenCalled();
+                }
+            });
+        });
+
+        describe("log.disableAll()", function() {
+            it("disables all log methods", function() {
+                log.disableAll(false);
+
+                for (var ii = 0; ii < logMethods.length; ii++) {
+                    var method = logMethods[ii];
+                    log[method]("a log message");
+
+                    expect(getConsoleMethod(method)).not.toHaveBeenCalled();
+                }
+            });
+        });
+
+        describe("log.setLevel() throws errors if given", function() {
+            it("no level argument", function() {
+                expect(function() {
+                    log.setLevel();
+                }).toThrow("log.setLevel() called with invalid level: undefined");
+            });
+
+            it("a null level argument", function() {
+                expect(function() {
+                    log.setLevel(null);
+                }).toThrow("log.setLevel() called with invalid level: null");
+            });
+
+            it("an undefined level argument", function() {
+                expect(function() {
+                    log.setLevel(undefined);
+                }).toThrow("log.setLevel() called with invalid level: undefined");
+            });
+
+            it("an invalid log level index", function() {
+                expect(function() {
+                    log.setLevel(-1);
+                }).toThrow("log.setLevel() called with invalid level: -1");
+            });
+
+            it("an invalid log level name", function() {
+                expect(function() {
+                    log.setLevel("InvalidLevelName");
+                }).toThrow("log.setLevel() called with invalid level: InvalidLevelName");
+            });
+        });
+
+        describe("setting log level by name", function() {
+            function itCanSetLogLevelTo(level) {
+                it("can set log level to " + level, function() {
+                    log.setLevel(level, false);
+
+                    log[level]("log message");
+                    expect(getConsoleMethod(level)).toHaveBeenCalled();
+                });
+            }
+
+            itCanSetLogLevelTo("trace");
+            itCanSetLogLevelTo("debug");
+            itCanSetLogLevelTo("info");
+            itCanSetLogLevelTo("warn");
+            itCanSetLogLevelTo("error");
+        });
+
+        describe("log level settings", function() {
+            describe("log.trace", function() {
+                it("is enabled at trace level", function() {
+                    log.setLevel(log.levels.TRACE);
+
+                    log.trace("a log message");
+                    expect(console.trace).toHaveBeenCalled();
+                });
+
+                it("is disabled at debug level", function() {
+                    log.setLevel(log.levels.DEBUG);
+
+                    log.trace("a log message");
+                    expect(console.trace).not.toHaveBeenCalled();
+                });
+
+                it("is disabled at silent level", function() {
+                    log.setLevel(log.levels.SILENT);
+
+                    log.trace("a log message");
+                    expect(console.trace).not.toHaveBeenCalled();
+                });
+            });
+
+            describe("log.debug", function() {
+                it("is enabled at trace level", function() {
+                    log.setLevel(log.levels.TRACE);
+
+                    log.debug("a log message");
+                    expect(console.log).toHaveBeenCalled();
+                });
+
+                it("is enabled at debug level", function() {
+                    log.setLevel(log.levels.DEBUG);
+
+                    log.debug("a log message");
+                    expect(console.log).toHaveBeenCalled();
+                });
+
+                it("is disabled at info level", function() {
+                    log.setLevel(log.levels.INFO);
+
+                    log.debug("a log message");
+                    expect(console.log).not.toHaveBeenCalled();
+                });
+
+                it("is disabled at silent level", function() {
+                    log.setLevel(log.levels.SILENT);
+
+                    log.debug("a log message");
+                    expect(console.log).not.toHaveBeenCalled();
+                });
+            });
+
+            describe("log.log", function() {
+                it("is enabled at trace level", function() {
+                    log.setLevel(log.levels.TRACE);
+
+                    log.log("a log message");
+                    expect(console.log).toHaveBeenCalled();
+                });
+
+                it("is enabled at debug level", function() {
+                    log.setLevel(log.levels.DEBUG);
+
+                    log.log("a log message");
+                    expect(console.log).toHaveBeenCalled();
+                });
+
+                it("is disabled at info level", function() {
+                    log.setLevel(log.levels.INFO);
+
+                    log.log("a log message");
+                    expect(console.log).not.toHaveBeenCalled();
+                });
+
+                it("is disabled at silent level", function() {
+                    log.setLevel(log.levels.SILENT);
+
+                    log.log("a log message");
+                    expect(console.log).not.toHaveBeenCalled();
+                });
+            });
+
+            describe("log.info", function() {
+                it("is enabled at debug level", function() {
+                    log.setLevel(log.levels.DEBUG);
+
+                    log.info("a log message");
+                    expect(console.info).toHaveBeenCalled();
+                });
+
+                it("is enabled at info level", function() {
+                    log.setLevel(log.levels.INFO);
+
+                    log.info("a log message");
+                    expect(console.info).toHaveBeenCalled();
+                });
+
+                it("is disabled at warn level", function() {
+                    log.setLevel(log.levels.WARN);
+
+                    log.info("a log message");
+                    expect(console.info).not.toHaveBeenCalled();
+                });
+
+                it("is disabled at silent level", function() {
+                    log.setLevel(log.levels.SILENT);
+
+                    log.info("a log message");
+                    expect(console.info).not.toHaveBeenCalled();
+                });
+            });
+
+            describe("log.warn", function() {
+                it("is enabled at info level", function() {
+                    log.setLevel(log.levels.INFO);
+
+                    log.warn("a log message");
+                    expect(console.warn).toHaveBeenCalled();
+                });
+
+                it("is enabled at warn level", function() {
+                    log.setLevel(log.levels.WARN);
+
+                    log.warn("a log message");
+                    expect(console.warn).toHaveBeenCalled();
+                });
+
+                it("is disabled at error level", function() {
+                    log.setLevel(log.levels.ERROR);
+
+                    log.warn("a log message");
+                    expect(console.warn).not.toHaveBeenCalled();
+                });
+
+                it("is disabled at silent level", function() {
+                    log.setLevel(log.levels.SILENT);
+
+                    log.warn("a log message");
+                    expect(console.warn).not.toHaveBeenCalled();
+                });
+            });
+
+            describe("log.error", function() {
+                it("is enabled at warn level", function() {
+                    log.setLevel(log.levels.WARN);
+
+                    log.error("a log message");
+                    expect(console.error).toHaveBeenCalled();
+                });
+
+                it("is enabled at error level", function() {
+                    log.setLevel(log.levels.ERROR);
+
+                    log.error("a log message");
+                    expect(console.error).toHaveBeenCalled();
+                });
+
+                it("is disabled at silent level", function() {
+                    log.setLevel(log.levels.SILENT);
+
+                    log.error("a log message");
+                    expect(console.error).not.toHaveBeenCalled();
+                });
+            });
+        });
+    });
+});
+
diff --git a/resources/app/node_modules/loglevel/test/local-storage-test.js b/resources/app/node_modules/loglevel/test/local-storage-test.js
new file mode 100644
index 0000000..2f843df
--- /dev/null
+++ b/resources/app/node_modules/loglevel/test/local-storage-test.js
@@ -0,0 +1,201 @@
+"use strict";
+
+define(['test/test-helpers'], function(testHelpers) {
+    var describeIf = testHelpers.describeIf;
+    var it = testHelpers.itWithFreshLog;
+
+    var originalConsole = window.console;
+
+    describeIf(testHelpers.isLocalStorageAvailable(), "Local storage persistence tests:", function() {
+
+        beforeEach(function() {
+            window.console = {"log" : jasmine.createSpy("console.log")};
+            this.addMatchers({
+                "toBeAtLevel" : testHelpers.toBeAtLevel,
+                "toBeTheStoredLevel" : testHelpers.toBeTheLevelStoredByLocalStorage,
+                "toBeTheLevelStoredByLocalStorage": testHelpers.toBeTheLevelStoredByLocalStorage,
+                "toBeTheLevelStoredByCookie": testHelpers.toBeTheLevelStoredByCookie
+            });
+
+            testHelpers.clearStoredLevels();
+        });
+
+        afterEach(function() {
+            window.console = originalConsole;
+        });
+
+        describe("If no level is saved", function() {
+            it("log level is set to warn by default", function(log) {
+                expect(log).toBeAtLevel("warn");
+            });
+
+            it("warn is not persisted as the current level", function(log) {
+                expect("warn").not.toBeTheStoredLevel();
+            });
+
+            it("log can be set to info level", function(log) {
+                log.setLevel("info");
+                expect(log).toBeAtLevel("info");
+            });
+
+            it("log.setLevel() sets a cookie with the given level", function(log) {
+                log.setLevel("debug");
+                expect("debug").toBeTheStoredLevel();
+            });
+
+            it("log.setLevel() does not set a cookie if `persist` argument is false", function(log) {
+                log.setLevel("debug", false);
+                expect("debug").not.toBeTheStoredLevel();
+            });
+        });
+        
+        describe("If trace level is saved", function () {
+            beforeEach(function () {
+                testHelpers.setStoredLevel("trace");
+            });
+            
+            it("trace is the default log level", function (log) {
+                expect(log).toBeAtLevel("trace");
+            });
+        });
+
+        describe("If debug level is saved", function () {
+            beforeEach(function () {
+                testHelpers.setStoredLevel("debug");
+            });
+
+            it("debug is the default log level", function (log) {
+                expect(log).toBeAtLevel("debug");
+            });
+        });
+        
+        describe("If info level is saved", function() {
+            beforeEach(function() {
+                testHelpers.setStoredLevel("info");
+            });
+
+            it("info is the default log level", function(log) {
+                expect(log).toBeAtLevel("info");
+            });
+
+            it("log can be changed to warn level", function(log) {
+                log.setLevel("warn");
+                expect(log).toBeAtLevel("warn");
+            });
+
+            it("log.setLevel() overwrites the saved level", function(log) {
+                log.setLevel("error");
+
+                expect("error").toBeTheStoredLevel();
+                expect("info").not.toBeTheStoredLevel();
+            });
+
+            it("log.setLevel() does not overwrite the saved level if `persist` argument is false", function(log) {
+                log.setLevel("error", false);
+
+                expect("info").toBeTheStoredLevel();
+                expect("error").not.toBeTheStoredLevel();
+            });
+        });
+
+        describe("If warn level is saved", function () {
+            beforeEach(function () {
+                testHelpers.setStoredLevel("warn");
+            });
+
+            it("warn is the default log level", function (log) {
+                expect(log).toBeAtLevel("warn");
+            });
+        });
+
+        describe("If error level is saved", function () {
+            beforeEach(function () {
+                testHelpers.setStoredLevel("error");
+            });
+
+            it("error is the default log level", function (log) {
+                expect(log).toBeAtLevel("error");
+            });
+        });
+
+
+        describe("If the level is saved with other data", function() {
+            beforeEach(function() {
+                window.localStorage['qwe'] = "asd";
+                window.localStorage['loglevel'] = "ERROR";
+                window.localStorage['msg'] = "hello world";
+            });
+
+            it("error is the default log level", function(log) {
+                expect(log).toBeAtLevel("error");
+            });
+
+            it("log can be changed to silent level", function(log) {
+                log.setLevel("silent");
+                expect(log).toBeAtLevel("silent");
+            });
+
+            it("log.setLevel() overrides the saved level only", function(log) {
+                log.setLevel("debug");
+
+                expect('debug').toBeTheStoredLevel();
+                expect(window.localStorage['msg']).toBe("hello world");
+            });
+        });
+
+        describe("If the level is stored incorrectly", function() {
+            beforeEach(function() {
+                testHelpers.setLocalStorageStoredLevel('gibberish');
+            });
+
+            it("warn is the default log level", function(log) {
+                expect(log).toBeAtLevel("warn");
+            });
+
+            it("warn is not persisted as the current level", function(log) {
+                expect("warn").not.toBeTheStoredLevel();
+            });
+
+            it("log can be changed to info level", function(log) {
+                log.setLevel("info");
+                expect(log).toBeAtLevel("info");
+            });
+
+            it("log.setLevel() overrides the saved level with the new level", function(log) {
+                expect('debug').not.toBeTheStoredLevel();
+
+                log.setLevel("debug");
+
+                expect('debug').toBeTheStoredLevel();
+            });
+        });
+
+        describeIf(testHelpers.isCookieStorageAvailable() && testHelpers.isLocalStorageAvailable(),
+                   "if localStorage and cookies are both available", function () {
+
+            it("the level stored in cookies is ignored if a local storage level is set", function () {
+                testHelpers.setCookieStoredLevel("info");
+                testHelpers.setLocalStorageStoredLevel("debug");
+
+                testHelpers.withFreshLog(function (log) {
+                    expect(log).toBeAtLevel("debug");
+                });
+            });
+
+            it("the level stored in cookies is used if no local storage level is set", function () {
+                testHelpers.setCookieStoredLevel("info");
+                window.localStorage.clear();
+
+                testHelpers.withFreshLog(function (log) {
+                    expect(log).toBeAtLevel("info");
+                });
+            });
+
+            it("the local storage level is set and the cookie level is not", function (log) {
+                log.setLevel("error");
+                expect("error").toBeTheLevelStoredByLocalStorage();
+                expect("error").not.toBeTheLevelStoredByCookie();
+            });
+        });
+    });
+});
diff --git a/resources/app/node_modules/loglevel/test/manual-test.html b/resources/app/node_modules/loglevel/test/manual-test.html
new file mode 100644
index 0000000..9b24a65
--- /dev/null
+++ b/resources/app/node_modules/loglevel/test/manual-test.html
@@ -0,0 +1,8 @@
+<html>

+<head>

+    <title>Standalone manual test bed for loglevel</title>

+</head>

+<body>

+<script src="../lib/loglevel.js"></script>

+</body>

+</html>
\ No newline at end of file
diff --git a/resources/app/node_modules/loglevel/test/method-factory-test.js b/resources/app/node_modules/loglevel/test/method-factory-test.js
new file mode 100644
index 0000000..aa80fc6
--- /dev/null
+++ b/resources/app/node_modules/loglevel/test/method-factory-test.js
@@ -0,0 +1,42 @@
+"use strict";
+
+define(['test/test-helpers'], function(testHelpers) {
+    var it = testHelpers.itWithFreshLog;
+
+    describe("Setting the methodFactory tests:", function() {
+
+        it("methodFactory should be called once for each loggable level", function(log) {
+            log.methodFactory = jasmine.createSpy("methodFactory");
+
+            log.setLevel("trace");
+            expect(log.methodFactory.calls.length).toEqual(5);
+            expect(log.methodFactory.argsForCall[0]).toEqual(["trace", 0, undefined]);
+            expect(log.methodFactory.argsForCall[1]).toEqual(["debug", 0, undefined]);
+            expect(log.methodFactory.argsForCall[2]).toEqual(["info",  0, undefined]);
+            expect(log.methodFactory.argsForCall[3]).toEqual(["warn",  0, undefined]);
+            expect(log.methodFactory.argsForCall[4]).toEqual(["error", 0, undefined]);
+
+            log.setLevel("error");
+            expect(log.methodFactory.calls.length).toEqual(6);
+            expect(log.methodFactory.argsForCall[5]).toEqual(["error", 4, undefined]);
+        });
+
+        it("functions returned by methodFactory should be used as logging functions", function(log) {
+            var logFunction = function() {};
+            log.methodFactory = function() { return logFunction; };
+            log.setLevel("error");
+
+            expect(log.warn).not.toEqual(logFunction);
+            expect(log.error).toEqual(logFunction);
+        });
+
+        it("the third argument should be logger's name", function(log) {
+            var logger = log.getLogger("newLogger");
+            logger.methodFactory = jasmine.createSpy("methodFactory");
+
+            logger.setLevel("error");
+            expect(logger.methodFactory.argsForCall[0]).toEqual(["error", 4, "newLogger"]);
+        });
+
+    });
+});
diff --git a/resources/app/node_modules/loglevel/test/multiple-logger-test.js b/resources/app/node_modules/loglevel/test/multiple-logger-test.js
new file mode 100644
index 0000000..ba132e6
--- /dev/null
+++ b/resources/app/node_modules/loglevel/test/multiple-logger-test.js
@@ -0,0 +1,139 @@
+"use strict";
+
+define(['test/test-helpers'], function(testHelpers) {
+    var describeIf = testHelpers.describeIf;
+    var it = testHelpers.itWithFreshLog;
+
+    var originalConsole = window.console;
+
+    describe("Multiple logger instances tests:", function() {
+
+        describe("log.getLogger()", function() {
+            it("returns a new logger that is not the default one", function(log) {
+                var newLogger = log.getLogger("newLogger");
+                expect(newLogger).not.toEqual(log);
+                expect(newLogger.trace).toBeDefined();
+                expect(newLogger.debug).toBeDefined();
+                expect(newLogger.info).toBeDefined();
+                expect(newLogger.warn).toBeDefined();
+                expect(newLogger.error).toBeDefined();
+                expect(newLogger.setLevel).toBeDefined();
+                expect(newLogger.setDefaultLevel).toBeDefined();
+                expect(newLogger.enableAll).toBeDefined();
+                expect(newLogger.disableAll).toBeDefined();
+                expect(newLogger.methodFactory).toBeDefined();
+            });
+
+            it("returns loggers without `getLogger()` and `noConflict()`", function(log) {
+                var newLogger = log.getLogger("newLogger");
+                expect(newLogger.getLogger).toBeUndefined();
+                expect(newLogger.noConflict).toBeUndefined();
+            });
+
+            it("returns the same instance when called repeatedly with the same name", function(log) {
+                var logger1 = log.getLogger("newLogger");
+                var logger2 = log.getLogger("newLogger");
+
+                expect(logger1).toEqual(logger2);
+            });
+
+            it("should throw if called with no name", function(log) {
+                expect(function() {
+                  log.getLogger();
+                }).toThrow();
+            });
+
+            it("should throw if called with empty string for name", function(log) {
+                expect(function() {
+                  log.getLogger("");
+                }).toThrow();
+            });
+
+            it("should throw if called with a non-string name", function(log) {
+                expect(function() { log.getLogger(true); }).toThrow();
+                expect(function() { log.getLogger({}); }).toThrow();
+                expect(function() { log.getLogger([]); }).toThrow();
+                expect(function() { log.getLogger(10); }).toThrow();
+                expect(function() { log.getLogger(function(){}); }).toThrow();
+                expect(function() { log.getLogger(null); }).toThrow();
+                expect(function() { log.getLogger(undefined); }).toThrow();
+                if (window.Symbol) {
+                    expect(function() { log.getLogger(Symbol()); }).toThrow();
+                }
+            });
+        });
+
+        describe("inheritance", function() {
+            beforeEach(function() {
+                window.console = {"log" : jasmine.createSpy("console.log")};
+                this.addMatchers({
+                    "toBeAtLevel" : testHelpers.toBeAtLevel
+                });
+                testHelpers.clearStoredLevels();
+            });
+
+            afterEach(function() {
+                window.console = originalConsole;
+            });
+
+            it("loggers are created with the same level as the default logger", function(log) {
+              log.setLevel("ERROR");
+              var newLogger = log.getLogger("newLogger");
+              expect(newLogger).toBeAtLevel("error");
+            });
+
+            it("if a logger's level is persisted, it uses that level rather than the default logger's level", function(log) {
+                testHelpers.setStoredLevel("error", "newLogger");
+                log.setLevel("TRACE");
+                var newLogger = log.getLogger("newLogger");
+                expect(newLogger).toBeAtLevel("error");
+            });
+
+            it("other loggers do not change when the default logger's level is changed", function(log) {
+                log.setLevel("TRACE");
+                var newLogger = log.getLogger("newLogger");
+                log.setLevel("ERROR");
+                expect(newLogger).toBeAtLevel("TRACE");
+                expect(log.getLogger("newLogger")).toBeAtLevel("TRACE");
+            });
+
+            it("loggers are created with the same methodFactory as the default logger", function(log) {
+                log.methodFactory = function(methodName, level) {
+                  return function() {};
+                };
+
+                var newLogger = log.getLogger("newLogger");
+                expect(newLogger.methodFactory).toEqual(log.methodFactory);
+            });
+
+            it("loggers have independent method factories", function(log) {
+                var log1 = log.getLogger('logger1');
+                var log2 = log.getLogger('logger2');
+
+                var log1Spy = jasmine.createSpy('log1spy');
+                log1.methodFactory = function(methodName, level) {
+                    return log1Spy;
+                };
+                log1.setLevel(log1.getLevel());
+
+                var log2Spy = jasmine.createSpy('log2spy');
+                log2.methodFactory = function(methodName, level) {
+                    return log2Spy;
+                };
+                log2.setLevel(log2.getLevel());
+
+                log1.error('test1');
+                log2.error('test2');
+
+                expect(log1Spy).toHaveBeenCalledWith("test1");
+                expect(log2Spy).toHaveBeenCalledWith("test2");
+            });
+
+            it("new loggers correctly inherit a logging level of `0`", function(log) {
+              log.setLevel(0);
+              var newLogger = log.getLogger("newLogger");
+              expect(newLogger).toBeAtLevel("trace");
+            });
+        });
+    });
+});
diff --git a/resources/app/node_modules/loglevel/test/node-integration.js b/resources/app/node_modules/loglevel/test/node-integration.js
new file mode 100644
index 0000000..f1d1d1f
--- /dev/null
+++ b/resources/app/node_modules/loglevel/test/node-integration.js
@@ -0,0 +1,27 @@
+"use strict";

+

+describe("loglevel included via node", function () {

+    it("is included successfully", function () {

+        expect(require('../lib/loglevel')).not.toBeUndefined();

+    });

+

+    it("allows setting the logging level", function () {

+        var log = require('../lib/loglevel');

+

+        log.setLevel(log.levels.TRACE);

+        log.setLevel(log.levels.DEBUG);

+        log.setLevel(log.levels.INFO);

+        log.setLevel(log.levels.WARN);

+        log.setLevel(log.levels.ERROR);

+    });

+

+    it("successfully logs", function () {

+        var log = require('../lib/loglevel');

+        console.info = jasmine.createSpy("info");

+

+        log.setLevel(log.levels.INFO);

+        log.info("test message");

+

+        expect(console.info).toHaveBeenCalledWith("test message");

+    });

+});
\ No newline at end of file
diff --git a/resources/app/node_modules/loglevel/test/test-context-using-apply.js b/resources/app/node_modules/loglevel/test/test-context-using-apply.js
new file mode 100644
index 0000000..4e57669
--- /dev/null
+++ b/resources/app/node_modules/loglevel/test/test-context-using-apply.js
@@ -0,0 +1,6 @@
+"use strict";
+/* jshint node:true */
+var MyCustomLogger = (function() {
+    // @include ../lib/loglevel.js
+    return this.log;
+}).apply({});
diff --git a/resources/app/node_modules/loglevel/test/test-helpers.js b/resources/app/node_modules/loglevel/test/test-helpers.js
new file mode 100644
index 0000000..12cc4e5
--- /dev/null
+++ b/resources/app/node_modules/loglevel/test/test-helpers.js
@@ -0,0 +1,168 @@
+"use strict";
+
+if (typeof window === "undefined") {
+    window = {};
+}
+
+var logMethods = [
+    "trace",
+    "debug",
+    "info",
+    "warn",
+    "error"
+];
+
+define(function () {
+    function getStorageKey(loggerName) {
+        var key = "loglevel";
+        if (loggerName) {
+            key += ":" + loggerName;
+        }
+        return key;
+    }
+
+    var self = {};
+
+    // Jasmine matcher to check the log level of a log object
+    self.toBeAtLevel = function toBeAtLevel(level) {
+        var log = this.actual;
+        var expectedWorkingCalls = log.levels.SILENT - log.levels[level.toUpperCase()];
+        var realLogMethod = window.console.log;
+        var priorCalls = realLogMethod.calls.length;
+
+        for (var ii = 0; ii < logMethods.length; ii++) {
+            var methodName = logMethods[ii];
+            log[methodName](methodName);
+        }
+
+        expect(realLogMethod.calls.length - priorCalls).toEqual(expectedWorkingCalls);
+        return true;
+    };
+
+    self.isCookieStorageAvailable = function isCookieStorageAvailable() {
+        if (window && window.document && window.document.cookie) {
+            // We need to check not just that the cookie objects are available, but that they work, because
+            // if we run from file:// URLs they appear present but are non-functional
+            window.document.cookie = "test=hi;";
+
+            var result = window.document.cookie.indexOf('test=hi') !== -1;
+            window.document.cookie = "test=; expires=Thu, 01 Jan 1970 00:00:01 GMT;";
+
+            return result;
+        } else {
+            return false;
+        }
+    };
+
+    self.isLocalStorageAvailable = function isLocalStorageAvailable() {
+        try {
+            return !!window.localStorage;
+        } catch (e){
+            return false;
+        }
+    };
+
+    self.isAnyLevelStoragePossible = function isAnyLevelStoragePossible() {
+        return self.isCookieStorageAvailable() || self.isLocalStorageAvailable();
+    };
+
+    self.toBeTheLevelStoredByCookie = function toBeTheLevelStoredByCookie(name) {
+        var level = this.actual.toUpperCase();
+        var storageKey = encodeURIComponent(getStorageKey(name));
+
+        if (window.document.cookie.indexOf(storageKey + "=" + level) !== -1) {
+            return true;
+        } else {
+            return false;
+        }
+    };
+
+    self.toBeTheLevelStoredByLocalStorage = function toBeTheLevelStoredByLocalStorage(name) {
+        var level = this.actual.toUpperCase();
+
+        if (window.localStorage[getStorageKey(name)] === level) {
+            return true;
+        }
+
+        return false;
+    };
+
+    // Jasmine matcher to check whether a given string was saved by loglevel
+    self.toBeTheStoredLevel = function toBeTheStoredLevel(name) {
+        return self.toBeTheLevelStoredByLocalStorage.call(this, name) ||
+               self.toBeTheLevelStoredByCookie.call(this, name);
+    };
+
+    self.setCookieStoredLevel = function setCookieStoredLevel(level, name) {
+        window.document.cookie =
+            encodeURIComponent(getStorageKey(name)) + "=" +
+            level.toUpperCase() + ";";
+    };
+
+    self.setLocalStorageStoredLevel = function setLocalStorageStoredLevel(level, name) {
+        window.localStorage[getStorageKey(name)] = level.toUpperCase();
+    };
+
+    self.setStoredLevel = function setStoredLevel(level, name) {
+        if (self.isCookieStorageAvailable()) {
+            self.setCookieStoredLevel(level, name);
+        }
+        if (self.isLocalStorageAvailable()) {
+            self.setLocalStorageStoredLevel(level, name);
+        }
+    };
+
+    self.clearStoredLevels = function clearStoredLevels() {
+        if (self.isLocalStorageAvailable()) {
+            window.localStorage.clear();
+        }
+        if (self.isCookieStorageAvailable()) {
+            var storedKeys = window.document.cookie.match(/(?:^|;\s)(loglevel(\:\w+)?)(?=\=)/g);
+            if (storedKeys) {
+                for (var i = 0; i < storedKeys.length; i++) {
+                    window.document.cookie = storedKeys[i] + "=; expires=Thu, 01 Jan 1970 00:00:01 GMT;";
+                }
+            }
+        }
+    };
+
+    self.describeIf = function describeIf(condition, name, test) {
+        if (condition) {
+            jasmine.getEnv().describe(name, test);
+        }
+    };
+
+    self.itIf = function itIf(condition, name, test) {
+        if (condition) {
+            jasmine.getEnv().it(name, test);
+        }
+    };
+
+    // Forcibly reloads loglevel, and asynchronously hands the resulting log back to the given callback
+    // via Jasmine async magic
+    self.withFreshLog = function withFreshLog(toRun) {
+        require.undef("lib/loglevel");
+
+        var freshLog;
+
+        waitsFor(function() {
+            require(['lib/loglevel'], function(log) {
+                freshLog = log;
+            });
+            return typeof freshLog !== "undefined";
+        });
+
+        runs(function() {
+            toRun(freshLog);
+        });
+    };
+
+    // Wraps Jasmine's it(name, test) call to reload the loglevel dependency for the given test
+    self.itWithFreshLog = function itWithFreshLog(name, test) {
+        jasmine.getEnv().it(name, function() {
+            self.withFreshLog(test);
+        });
+    };
+
+    return self;
+});
diff --git a/resources/app/node_modules/loglevel/test/test-qunit.html b/resources/app/node_modules/loglevel/test/test-qunit.html
new file mode 100644
index 0000000..d2b8c5d
--- /dev/null
+++ b/resources/app/node_modules/loglevel/test/test-qunit.html
@@ -0,0 +1,19 @@
+<!DOCTYPE html>

+<html>

+<head>

+	<meta charset="utf-8">

+	<title>QUnit Integration Test</title>

+	<link rel="stylesheet" href="../node_modules/qunitjs/qunit/qunit.css">

+</head>

+<body>

+	<script src="../lib/loglevel.js" loglevel-name="logging"></script>

+	<script>

+		var logging = log.noConflict();

+	</script>

+        <!-- Pretend the users code is included here -->

+	<div id="qunit"></div>

+	<div id="qunit-fixture"></div>

+	<script src="../node_modules/qunitjs/qunit/qunit.js"></script>

+	<script src="test-qunit.js"></script>

+</body>

+</html>

diff --git a/resources/app/node_modules/loglevel/test/test-qunit.js b/resources/app/node_modules/loglevel/test/test-qunit.js
new file mode 100644
index 0000000..b0f7670
--- /dev/null
+++ b/resources/app/node_modules/loglevel/test/test-qunit.js
@@ -0,0 +1,51 @@
+"use strict";
+
+/*global document*/
+var fixture = document.getElementById("qunit-fixture");
+
+/*global QUnit*/
+QUnit.module('loglevel', {
+    setup: function() {
+    },
+    teardown: function() {
+    }
+});
+
+/*global test*/
+test('basic test', function() {
+    /*global ok*/
+    /*global logging*/
+    /*global log*/
+
+    // Check that noConflict restored the original log
+    ok(typeof log === "function", "log is a function");
+    ok(log === QUnit.log, "log is Qunit.log");
+
+    // Check that noConflict setup logging
+    ok(typeof logging !== "undefined", "logging is defined");
+    ok(typeof logging === "object", "logging is an object");
+    ok(typeof logging.trace === "function", "trace is a function");
+    ok(typeof logging.debug === "function", "debug is a function");
+    ok(typeof logging.info === "function", "info is a function");
+    ok(typeof logging.warn === "function", "warn is a function");
+    ok(typeof logging.error === "function", "error is a function");
+    ok(typeof logging.setLevel === "function", "setLevel is a function");
+    ok(typeof logging.setDefaultLevel === "function", "setDefaultLevel is a function");
+    ok(typeof logging.enableAll === "function", "enableAll is a function");
+    ok(typeof logging.disableAll === "function", "disableAll is a function");
+    ok(typeof logging.getLogger === "function", "getLogger is a function");
+
+    // Use the API to make sure it doesn't blantantly fail with exceptions
+    logging.trace("a trace message");
+    logging.debug("a debug message");
+    logging.info("an info message");
+    logging.warn("a warn message");
+    logging.error("an error message");
+
+    var newLogger = logging.getLogger("newLogger");
+    newLogger.trace("a trace message");
+    newLogger.debug("a debug message");
+    newLogger.info("an info message");
+    newLogger.warn("a warn message");
+    newLogger.error("an error message");
+});
diff --git a/resources/app/node_modules/loglevel/test/vendor/json2.js b/resources/app/node_modules/loglevel/test/vendor/json2.js
new file mode 100644
index 0000000..f7eb646
--- /dev/null
+++ b/resources/app/node_modules/loglevel/test/vendor/json2.js
@@ -0,0 +1,486 @@
+/*

+    json2.js

+    2012-10-08

+

+    Public Domain.

+

+    NO WARRANTY EXPRESSED OR IMPLIED. USE AT YOUR OWN RISK.

+

+    See http://www.JSON.org/js.html

+

+

+    This code should be minified before deployment.

+    See http://javascript.crockford.com/jsmin.html

+

+    USE YOUR OWN COPY. IT IS EXTREMELY UNWISE TO LOAD CODE FROM SERVERS YOU DO

+    NOT CONTROL.

+

+

+    This file creates a global JSON object containing two methods: stringify

+    and parse.

+

+        JSON.stringify(value, replacer, space)

+            value       any JavaScript value, usually an object or array.

+

+            replacer    an optional parameter that determines how object

+                        values are stringified for objects. It can be a

+                        function or an array of strings.

+

+            space       an optional parameter that specifies the indentation

+                        of nested structures. If it is omitted, the text will

+                        be packed without extra whitespace. If it is a number,

+                        it will specify the number of spaces to indent at each

+                        level. If it is a string (such as '\t' or '&nbsp;'),

+                        it contains the characters used to indent at each level.

+

+            This method produces a JSON text from a JavaScript value.

+

+            When an object value is found, if the object contains a toJSON

+            method, its toJSON method will be called and the result will be

+            stringified. A toJSON method does not serialize: it returns the

+            value represented by the name/value pair that should be serialized,

+            or undefined if nothing should be serialized. The toJSON method

+            will be passed the key associated with the value, and this will be

+            bound to the value

+

+            For example, this would serialize Dates as ISO strings.

+

+                Date.prototype.toJSON = function (key) {

+                    function f(n) {

+                        // Format integers to have at least two digits.

+                        return n < 10 ? '0' + n : n;

+                    }

+

+                    return this.getUTCFullYear()   + '-' +

+                         f(this.getUTCMonth() + 1) + '-' +

+                         f(this.getUTCDate())      + 'T' +

+                         f(this.getUTCHours())     + ':' +

+                         f(this.getUTCMinutes())   + ':' +

+                         f(this.getUTCSeconds())   + 'Z';

+                };

+

+            You can provide an optional replacer method. It will be passed the

+            key and value of each member, with this bound to the containing

+            object. The value that is returned from your method will be

+            serialized. If your method returns undefined, then the member will

+            be excluded from the serialization.

+

+            If the replacer parameter is an array of strings, then it will be

+            used to select the members to be serialized. It filters the results

+            such that only members with keys listed in the replacer array are

+            stringified.

+

+            Values that do not have JSON representations, such as undefined or

+            functions, will not be serialized. Such values in objects will be

+            dropped; in arrays they will be replaced with null. You can use

+            a replacer function to replace those with JSON values.

+            JSON.stringify(undefined) returns undefined.

+

+            The optional space parameter produces a stringification of the

+            value that is filled with line breaks and indentation to make it

+            easier to read.

+

+            If the space parameter is a non-empty string, then that string will

+            be used for indentation. If the space parameter is a number, then

+            the indentation will be that many spaces.

+

+            Example:

+

+            text = JSON.stringify(['e', {pluribus: 'unum'}]);

+            // text is '["e",{"pluribus":"unum"}]'

+

+

+            text = JSON.stringify(['e', {pluribus: 'unum'}], null, '\t');

+            // text is '[\n\t"e",\n\t{\n\t\t"pluribus": "unum"\n\t}\n]'

+

+            text = JSON.stringify([new Date()], function (key, value) {

+                return this[key] instanceof Date ?

+                    'Date(' + this[key] + ')' : value;

+            });

+            // text is '["Date(---current time---)"]'

+

+

+        JSON.parse(text, reviver)

+            This method parses a JSON text to produce an object or array.

+            It can throw a SyntaxError exception.

+

+            The optional reviver parameter is a function that can filter and

+            transform the results. It receives each of the keys and values,

+            and its return value is used instead of the original value.

+            If it returns what it received, then the structure is not modified.

+            If it returns undefined then the member is deleted.

+

+            Example:

+

+            // Parse the text. Values that look like ISO date strings will

+            // be converted to Date objects.

+

+            myData = JSON.parse(text, function (key, value) {

+                var a;

+                if (typeof value === 'string') {

+                    a =

+/^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2}(?:\.\d*)?)Z$/.exec(value);

+                    if (a) {

+                        return new Date(Date.UTC(+a[1], +a[2] - 1, +a[3], +a[4],

+                            +a[5], +a[6]));

+                    }

+                }

+                return value;

+            });

+

+            myData = JSON.parse('["Date(09/09/2001)"]', function (key, value) {

+                var d;

+                if (typeof value === 'string' &&

+                        value.slice(0, 5) === 'Date(' &&

+                        value.slice(-1) === ')') {

+                    d = new Date(value.slice(5, -1));

+                    if (d) {

+                        return d;

+                    }

+                }

+                return value;

+            });

+

+

+    This is a reference implementation. You are free to copy, modify, or

+    redistribute.

+*/

+

+/*jslint evil: true, regexp: true */

+

+/*members "", "\b", "\t", "\n", "\f", "\r", "\"", JSON, "\\", apply,

+    call, charCodeAt, getUTCDate, getUTCFullYear, getUTCHours,

+    getUTCMinutes, getUTCMonth, getUTCSeconds, hasOwnProperty, join,

+    lastIndex, length, parse, prototype, push, replace, slice, stringify,

+    test, toJSON, toString, valueOf

+*/

+

+

+// Create a JSON object only if one does not already exist. We create the

+// methods in a closure to avoid creating global variables.

+

+if (typeof JSON !== 'object') {

+    JSON = {};

+}

+

+(function () {

+    'use strict';

+

+    function f(n) {

+        // Format integers to have at least two digits.

+        return n < 10 ? '0' + n : n;

+    }

+

+    if (typeof Date.prototype.toJSON !== 'function') {

+

+        Date.prototype.toJSON = function (key) {

+

+            return isFinite(this.valueOf())

+                ? this.getUTCFullYear()     + '-' +

+                    f(this.getUTCMonth() + 1) + '-' +

+                    f(this.getUTCDate())      + 'T' +

+                    f(this.getUTCHours())     + ':' +

+                    f(this.getUTCMinutes())   + ':' +

+                    f(this.getUTCSeconds())   + 'Z'

+                : null;

+        };

+

+        String.prototype.toJSON      =

+            Number.prototype.toJSON  =

+            Boolean.prototype.toJSON = function (key) {

+                return this.valueOf();

+            };

+    }

+

+    var cx = /[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,

+        escapable = /[\\\"\x00-\x1f\x7f-\x9f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,

+        gap,

+        indent,

+        meta = {    // table of character substitutions

+            '\b': '\\b',

+            '\t': '\\t',

+            '\n': '\\n',

+            '\f': '\\f',

+            '\r': '\\r',

+            '"' : '\\"',

+            '\\': '\\\\'

+        },

+        rep;

+

+

+    function quote(string) {

+

+// If the string contains no control characters, no quote characters, and no

+// backslash characters, then we can safely slap some quotes around it.

+// Otherwise we must also replace the offending characters with safe escape

+// sequences.

+

+        escapable.lastIndex = 0;

+        return escapable.test(string) ? '"' + string.replace(escapable, function (a) {

+            var c = meta[a];

+            return typeof c === 'string'

+                ? c

+                : '\\u' + ('0000' + a.charCodeAt(0).toString(16)).slice(-4);

+        }) + '"' : '"' + string + '"';

+    }

+

+

+    function str(key, holder) {

+

+// Produce a string from holder[key].

+

+        var i,          // The loop counter.

+            k,          // The member key.

+            v,          // The member value.

+            length,

+            mind = gap,

+            partial,

+            value = holder[key];

+

+// If the value has a toJSON method, call it to obtain a replacement value.

+

+        if (value && typeof value === 'object' &&

+                typeof value.toJSON === 'function') {

+            value = value.toJSON(key);

+        }

+

+// If we were called with a replacer function, then call the replacer to

+// obtain a replacement value.

+

+        if (typeof rep === 'function') {

+            value = rep.call(holder, key, value);

+        }

+

+// What happens next depends on the value's type.

+

+        switch (typeof value) {

+        case 'string':

+            return quote(value);

+

+        case 'number':

+

+// JSON numbers must be finite. Encode non-finite numbers as null.

+

+            return isFinite(value) ? String(value) : 'null';

+

+        case 'boolean':

+        case 'null':

+

+// If the value is a boolean or null, convert it to a string. Note:

+// typeof null does not produce 'null'. The case is included here in

+// the remote chance that this gets fixed someday.

+

+            return String(value);

+

+// If the type is 'object', we might be dealing with an object or an array or

+// null.

+

+        case 'object':

+

+// Due to a specification blunder in ECMAScript, typeof null is 'object',

+// so watch out for that case.

+

+            if (!value) {

+                return 'null';

+            }

+

+// Make an array to hold the partial results of stringifying this object value.

+

+            gap += indent;

+            partial = [];

+

+// Is the value an array?

+

+            if (Object.prototype.toString.apply(value) === '[object Array]') {

+

+// The value is an array. Stringify every element. Use null as a placeholder

+// for non-JSON values.

+

+                length = value.length;

+                for (i = 0; i < length; i += 1) {

+                    partial[i] = str(i, value) || 'null';

+                }

+

+// Join all of the elements together, separated with commas, and wrap them in

+// brackets.

+

+                v = partial.length === 0

+                    ? '[]'

+                    : gap

+                    ? '[\n' + gap + partial.join(',\n' + gap) + '\n' + mind + ']'

+                    : '[' + partial.join(',') + ']';

+                gap = mind;

+                return v;

+            }

+

+// If the replacer is an array, use it to select the members to be stringified.

+

+            if (rep && typeof rep === 'object') {

+                length = rep.length;

+                for (i = 0; i < length; i += 1) {

+                    if (typeof rep[i] === 'string') {

+                        k = rep[i];

+                        v = str(k, value);

+                        if (v) {

+                            partial.push(quote(k) + (gap ? ': ' : ':') + v);

+                        }

+                    }

+                }

+            } else {

+

+// Otherwise, iterate through all of the keys in the object.

+

+                for (k in value) {

+                    if (Object.prototype.hasOwnProperty.call(value, k)) {

+                        v = str(k, value);

+                        if (v) {

+                            partial.push(quote(k) + (gap ? ': ' : ':') + v);

+                        }

+                    }

+                }

+            }

+

+// Join all of the member texts together, separated with commas,

+// and wrap them in braces.

+

+            v = partial.length === 0

+                ? '{}'

+                : gap

+                ? '{\n' + gap + partial.join(',\n' + gap) + '\n' + mind + '}'

+                : '{' + partial.join(',') + '}';

+            gap = mind;

+            return v;

+        }

+    }

+

+// If the JSON object does not yet have a stringify method, give it one.

+

+    if (typeof JSON.stringify !== 'function') {

+        JSON.stringify = function (value, replacer, space) {

+

+// The stringify method takes a value and an optional replacer, and an optional

+// space parameter, and returns a JSON text. The replacer can be a function

+// that can replace values, or an array of strings that will select the keys.

+// A default replacer method can be provided. Use of the space parameter can

+// produce text that is more easily readable.

+

+            var i;

+            gap = '';

+            indent = '';

+

+// If the space parameter is a number, make an indent string containing that

+// many spaces.

+

+            if (typeof space === 'number') {

+                for (i = 0; i < space; i += 1) {

+                    indent += ' ';

+                }

+

+// If the space parameter is a string, it will be used as the indent string.

+

+            } else if (typeof space === 'string') {

+                indent = space;

+            }

+

+// If there is a replacer, it must be a function or an array.

+// Otherwise, throw an error.

+

+            rep = replacer;

+            if (replacer && typeof replacer !== 'function' &&

+                    (typeof replacer !== 'object' ||

+                    typeof replacer.length !== 'number')) {

+                throw new Error('JSON.stringify');

+            }

+

+// Make a fake root object containing our value under the key of ''.

+// Return the result of stringifying the value.

+

+            return str('', {'': value});

+        };

+    }

+

+

+// If the JSON object does not yet have a parse method, give it one.

+

+    if (typeof JSON.parse !== 'function') {

+        JSON.parse = function (text, reviver) {

+

+// The parse method takes a text and an optional reviver function, and returns

+// a JavaScript value if the text is a valid JSON text.

+

+            var j;

+

+            function walk(holder, key) {

+

+// The walk method is used to recursively walk the resulting structure so

+// that modifications can be made.

+

+                var k, v, value = holder[key];

+                if (value && typeof value === 'object') {

+                    for (k in value) {

+                        if (Object.prototype.hasOwnProperty.call(value, k)) {

+                            v = walk(value, k);

+                            if (v !== undefined) {

+                                value[k] = v;

+                            } else {

+                                delete value[k];

+                            }

+                        }

+                    }

+                }

+                return reviver.call(holder, key, value);

+            }

+

+

+// Parsing happens in four stages. In the first stage, we replace certain

+// Unicode characters with escape sequences. JavaScript handles many characters

+// incorrectly, either silently deleting them, or treating them as line endings.

+

+            text = String(text);

+            cx.lastIndex = 0;

+            if (cx.test(text)) {

+                text = text.replace(cx, function (a) {

+                    return '\\u' +

+                        ('0000' + a.charCodeAt(0).toString(16)).slice(-4);

+                });

+            }

+

+// In the second stage, we run the text against regular expressions that look

+// for non-JSON patterns. We are especially concerned with '()' and 'new'

+// because they can cause invocation, and '=' because it can cause mutation.

+// But just to be safe, we want to reject all unexpected forms.

+

+// We split the second stage into 4 regexp operations in order to work around

+// crippling inefficiencies in IE's and Safari's regexp engines. First we

+// replace the JSON backslash pairs with '@' (a non-JSON character). Second, we

+// replace all simple value tokens with ']' characters. Third, we delete all

+// open brackets that follow a colon or comma or that begin the text. Finally,

+// we look to see that the remaining characters are only whitespace or ']' or

+// ',' or ':' or '{' or '}'. If that is so, then the text is safe for eval.

+

+            if (/^[\],:{}\s]*$/

+                    .test(text.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g, '@')

+                        .replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g, ']')

+                        .replace(/(?:^|:|,)(?:\s*\[)+/g, ''))) {

+

+// In the third stage we use the eval function to compile the text into a

+// JavaScript structure. The '{' operator is subject to a syntactic ambiguity

+// in JavaScript: it can begin a block or an object literal. We wrap the text

+// in parens to eliminate the ambiguity.

+

+                j = eval('(' + text + ')');

+

+// In the optional fourth stage, we recursively walk the new structure, passing

+// each name/value pair to a reviver function for possible transformation.

+

+                return typeof reviver === 'function'

+                    ? walk({'': j}, '')

+                    : j;

+            }

+

+// If the text is not JSON parseable, then a SyntaxError is thrown.

+

+            throw new SyntaxError('JSON.parse');

+        };

+    }

+}());