MDL-60285 lib: Update to Moodle changes to loglevel.
[moodle.git] / lib / amd / src / loglevel.js
1 // The MIT License
2 //
3 // Copyright (c) 2013 Tim Perry
4 //
5 // Permission is hereby granted, free of charge, to any person obtaining
6 // a copy of this software and associated documentation files (the
7 // "Software"), to deal in the Software without restriction, including
8 // without limitation the rights to use, copy, modify, merge, publish,
9 // distribute, sublicense, and/or sell copies of the Software, and to
10 // permit persons to whom the Software is furnished to do so, subject to
11 // the following conditions:
12 //
13 // The above copyright notice and this permission notice shall be
14 // included in all copies or substantial portions of the Software.
15 //
16 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
19 // IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
20 // CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
21 // TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
22 // SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
23 //
25 // Description of import into Moodle:
26 // Download from https://github.com/pimterry/loglevel/dist
27 // Copy loglevel.js into lib/amd/src/ in Moodle folder.
28 // Add the license as a comment to the file and these instructions.
30 /*
31  * loglevel - https://github.com/pimterry/loglevel
32  *
33  * Copyright (c) 2013 Tim Perry
34  * Licensed under the MIT license.
35  */
36 /*! loglevel - v1.5.1 - https://github.com/pimterry/loglevel - (c) 2017 Tim Perry - licensed MIT */
37 (function (root, definition) {
38     "use strict";
39     if (typeof define === 'function' && define.amd) {
40         define(definition);
41     } else if (typeof module === 'object' && module.exports) {
42         module.exports = definition();
43     } else {
44         root.log = definition();
45     }
46 }(this, function () {
47     "use strict";
49     // Slightly dubious tricks to cut down minimized file size
50     var noop = function() {};
51     var undefinedType = "undefined";
53     var logMethods = [
54         "trace",
55         "debug",
56         "info",
57         "warn",
58         "error"
59     ];
61     // Cross-browser bind equivalent that works at least back to IE6
62     function bindMethod(obj, methodName) {
63         var method = obj[methodName];
64         if (typeof method.bind === 'function') {
65             return method.bind(obj);
66         } else {
67             try {
68                 return Function.prototype.bind.call(method, obj);
69             } catch (e) {
70                 // Missing bind shim or IE8 + Modernizr, fallback to wrapping
71                 return function() {
72                     return Function.prototype.apply.apply(method, [obj, arguments]);
73                 };
74             }
75         }
76     }
78     // Build the best logging method possible for this env
79     // Wherever possible we want to bind, not wrap, to preserve stack traces
80     function realMethod(methodName) {
81         if (methodName === 'debug') {
82             methodName = 'log';
83         }
85         if (typeof console === undefinedType) {
86             return false; // No method possible, for now - fixed later by enableLoggingWhenConsoleArrives
87         } else if (console[methodName] !== undefined) {
88             return bindMethod(console, methodName);
89         } else if (console.log !== undefined) {
90             return bindMethod(console, 'log');
91         } else {
92             return noop;
93         }
94     }
96     // These private functions always need `this` to be set properly
98     function replaceLoggingMethods(level, loggerName) {
99         /*jshint validthis:true */
100         for (var i = 0; i < logMethods.length; i++) {
101             var methodName = logMethods[i];
102             this[methodName] = (i < level) ?
103                 noop :
104                 this.methodFactory(methodName, level, loggerName);
105         }
107         // Define log.log as an alias for log.debug
108         this.log = this.debug;
109     }
111     // In old IE versions, the console isn't present until you first open it.
112     // We build realMethod() replacements here that regenerate logging methods
113     function enableLoggingWhenConsoleArrives(methodName, level, loggerName) {
114         return function () {
115             if (typeof console !== undefinedType) {
116                 replaceLoggingMethods.call(this, level, loggerName);
117                 this[methodName].apply(this, arguments);
118             }
119         };
120     }
122     // By default, we use closely bound real methods wherever possible, and
123     // otherwise we wait for a console to appear, and then try again.
124     function defaultMethodFactory(methodName, level, loggerName) {
125         /*jshint validthis:true */
126         return realMethod(methodName) ||
127                enableLoggingWhenConsoleArrives.apply(this, arguments);
128     }
130     function Logger(name, defaultLevel, factory) {
131       var self = this;
132       var currentLevel;
133       var storageKey = "loglevel";
134       if (name) {
135         storageKey += ":" + name;
136       }
138       function persistLevelIfPossible(levelNum) {
139           var levelName = (logMethods[levelNum] || 'silent').toUpperCase();
141           if (typeof window === undefinedType) return;
143           // Use localStorage if available
144           try {
145               window.localStorage[storageKey] = levelName;
146               return;
147           } catch (ignore) {}
149           // Use session cookie as fallback
150           try {
151               window.document.cookie =
152                 encodeURIComponent(storageKey) + "=" + levelName + ";";
153           } catch (ignore) {}
154       }
156       function getPersistedLevel() {
157           var storedLevel;
159           if (typeof window === undefinedType) return;
161           try {
162               storedLevel = window.localStorage[storageKey];
163           } catch (ignore) {}
165           // Fallback to cookies if local storage gives us nothing
166           if (typeof storedLevel === undefinedType) {
167               try {
168                   var cookie = window.document.cookie;
169                   var location = cookie.indexOf(
170                       encodeURIComponent(storageKey) + "=");
171                   if (location !== -1) {
172                       storedLevel = /^([^;]+)/.exec(cookie.slice(location))[1];
173                   }
174               } catch (ignore) {}
175           }
177           // If the stored level is not valid, treat it as if nothing was stored.
178           if (self.levels[storedLevel] === undefined) {
179               storedLevel = undefined;
180           }
182           return storedLevel;
183       }
185       /*
186        *
187        * Public logger API - see https://github.com/pimterry/loglevel for details
188        *
189        */
191       self.levels = { "TRACE": 0, "DEBUG": 1, "INFO": 2, "WARN": 3,
192           "ERROR": 4, "SILENT": 5};
194       self.methodFactory = factory || defaultMethodFactory;
196       self.getLevel = function () {
197           return currentLevel;
198       };
200       self.setLevel = function (level, persist) {
201           if (typeof level === "string" && self.levels[level.toUpperCase()] !== undefined) {
202               level = self.levels[level.toUpperCase()];
203           }
204           if (typeof level === "number" && level >= 0 && level <= self.levels.SILENT) {
205               currentLevel = level;
206               if (persist !== false) {  // defaults to true
207                   persistLevelIfPossible(level);
208               }
209               replaceLoggingMethods.call(self, level, name);
210               if (typeof console === undefinedType && level < self.levels.SILENT) {
211                   return "No console available for logging";
212               }
213           } else {
214               throw "log.setLevel() called with invalid level: " + level;
215           }
216       };
218       self.setDefaultLevel = function (level) {
219           if (!getPersistedLevel()) {
220               self.setLevel(level, false);
221           }
222       };
224       self.enableAll = function(persist) {
225           self.setLevel(self.levels.TRACE, persist);
226       };
228       self.disableAll = function(persist) {
229           self.setLevel(self.levels.SILENT, persist);
230       };
232       // Initialize with the right level
233       var initialLevel = getPersistedLevel();
234       if (initialLevel == null) {
235           initialLevel = defaultLevel == null ? "WARN" : defaultLevel;
236       }
237       self.setLevel(initialLevel, false);
238     }
240     /*
241      *
242      * Top-level API
243      *
244      */
246     var defaultLogger = new Logger();
248     var _loggersByName = {};
249     defaultLogger.getLogger = function getLogger(name) {
250         if (typeof name !== "string" || name === "") {
251           throw new TypeError("You must supply a name when creating a logger.");
252         }
254         var logger = _loggersByName[name];
255         if (!logger) {
256           logger = _loggersByName[name] = new Logger(
257             name, defaultLogger.getLevel(), defaultLogger.methodFactory);
258         }
259         return logger;
260     };
262     // Grab the current global log variable in case of overwrite
263     var _log = (typeof window !== undefinedType) ? window.log : undefined;
264     defaultLogger.noConflict = function() {
265         if (typeof window !== undefinedType &&
266                window.log === defaultLogger) {
267             window.log = _log;
268         }
270         return defaultLogger;
271     };
273     return defaultLogger;
274 }));