MDL-56010 libraries: remove jshint changes and update comment block
[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/releases
27 // Copy loglevel.js into lib/amd/src/ in Moodle folder.
29 /*
30  * loglevel - https://github.com/pimterry/loglevel
31  *
32  * Copyright (c) 2013 Tim Perry
33  * Licensed under the MIT license.
34  */
35 (function (root, definition) {
36     "use strict";
37     if (typeof define === 'function' && define.amd) {
38         define(definition);
39     } else if (typeof module === 'object' && module.exports) {
40         module.exports = definition();
41     } else {
42         root.log = definition();
43     }
44 }(this, function () {
45     "use strict";
47     // Slightly dubious tricks to cut down minimized file size
48     var noop = function() {};
49     var undefinedType = "undefined";
51     // Build the best logging method possible for this env
52     // Wherever possible we want to bind, not wrap, to preserve stack traces
53     function realMethod(methodName) {
54         if (typeof console === undefinedType) {
55             return false; // No method possible, for now - fixed later by enableLoggingWhenConsoleArrives
56         } else if (console[methodName] !== undefined) {
57             return bindMethod(console, methodName);
58         } else if (console.log !== undefined) {
59             return bindMethod(console, 'log');
60         } else {
61             return noop;
62         }
63     }
65     // Cross-browser bind equivalent that works at least back to IE6
66     function bindMethod(obj, methodName) {
67         var method = obj[methodName];
68         if (typeof method.bind === 'function') {
69             return method.bind(obj);
70         } else {
71             try {
72                 return Function.prototype.bind.call(method, obj);
73             } catch (e) {
74                 // Missing bind shim or IE8 + Modernizr, fallback to wrapping
75                 return function() {
76                     return Function.prototype.apply.apply(method, [obj, arguments]);
77                 };
78             }
79         }
80     }
82     // These private functions always need `this` to be set properly
84     // In old IE versions, the console isn't present until you first open it.
85     // We build realMethod() replacements here that regenerate logging methods
86     function enableLoggingWhenConsoleArrives(methodName, level, loggerName) {
87         return function () {
88             if (typeof console !== undefinedType) {
89                 replaceLoggingMethods.call(this, level, loggerName);
90                 this[methodName].apply(this, arguments);
91             }
92         };
93     }
95     function replaceLoggingMethods(level, loggerName) {
96         /*jshint validthis:true */
97         for (var i = 0; i < logMethods.length; i++) {
98             var methodName = logMethods[i];
99             this[methodName] = (i < level) ?
100                 noop :
101                 this.methodFactory(methodName, level, loggerName);
102         }
103     }
105     // By default, we use closely bound real methods wherever possible, and
106     // otherwise we wait for a console to appear, and then try again.
107     function defaultMethodFactory(methodName, level, loggerName) {
108         /*jshint validthis:true */
109         return realMethod(methodName) ||
110             enableLoggingWhenConsoleArrives.apply(this, arguments);
111     }
113     var logMethods = [
114         "trace",
115         "debug",
116         "info",
117         "warn",
118         "error"
119     ];
121     function Logger(name, defaultLevel, factory) {
122         var self = this;
123         var currentLevel;
124         var storageKey = "loglevel";
125         if (name) {
126             storageKey += ":" + name;
127         }
129         function persistLevelIfPossible(levelNum) {
130             var levelName = (logMethods[levelNum] || 'silent').toUpperCase();
132             // Use localStorage if available
133             try {
134                 window.localStorage[storageKey] = levelName;
135                 return;
136             } catch (ignore) {}
138             // Use session cookie as fallback
139             try {
140                 window.document.cookie =
141                     encodeURIComponent(storageKey) + "=" + levelName + ";";
142             } catch (ignore) {}
143         }
145         function getPersistedLevel() {
146             var storedLevel;
148             try {
149                 storedLevel = window.localStorage[storageKey];
150             } catch (ignore) {}
152             // Fallback to cookies if local storage gives us nothing
153             if (typeof storedLevel === undefinedType) {
154                 try {
155                     var cookie = window.document.cookie;
156                     var location = cookie.indexOf(
157                         encodeURIComponent(storageKey) + "=");
158                     if (location) {
159                         storedLevel = /^([^;]+)/.exec(cookie.slice(location))[1];
160                     }
161                 } catch (ignore) {}
162             }
164             // If the stored level is not valid, treat it as if nothing was stored.
165             if (self.levels[storedLevel] === undefined) {
166                 storedLevel = undefined;
167             }
169             return storedLevel;
170         }
172         /*
173          *
174          * Public logger API - see https://github.com/pimterry/loglevel for details
175          *
176          */
178         self.levels = { "TRACE": 0, "DEBUG": 1, "INFO": 2, "WARN": 3,
179             "ERROR": 4, "SILENT": 5};
181         self.methodFactory = factory || defaultMethodFactory;
183         self.getLevel = function () {
184             return currentLevel;
185         };
187         self.setLevel = function (level, persist) {
188             if (typeof level === "string" && self.levels[level.toUpperCase()] !== undefined) {
189                 level = self.levels[level.toUpperCase()];
190             }
191             if (typeof level === "number" && level >= 0 && level <= self.levels.SILENT) {
192                 currentLevel = level;
193                 if (persist !== false) {  // defaults to true
194                     persistLevelIfPossible(level);
195                 }
196                 replaceLoggingMethods.call(self, level, name);
197                 if (typeof console === undefinedType && level < self.levels.SILENT) {
198                     return "No console available for logging";
199                 }
200             } else {
201                 throw "log.setLevel() called with invalid level: " + level;
202             }
203         };
205         self.setDefaultLevel = function (level) {
206             if (!getPersistedLevel()) {
207                 self.setLevel(level, false);
208             }
209         };
211         self.enableAll = function(persist) {
212             self.setLevel(self.levels.TRACE, persist);
213         };
215         self.disableAll = function(persist) {
216             self.setLevel(self.levels.SILENT, persist);
217         };
219         // Initialize with the right level
220         var initialLevel = getPersistedLevel();
221         if (initialLevel == null) {
222             initialLevel = defaultLevel == null ? "WARN" : defaultLevel;
223         }
224         self.setLevel(initialLevel, false);
225     }
227     /*
228      *
229      * Top-level API
230      *
231      */
233     var defaultLogger = new Logger();
235     var _loggersByName = {};
236     defaultLogger.getLogger = function getLogger(name) {
237         if (typeof name !== "string" || name === "") {
238             throw new TypeError("You must supply a name when creating a logger.");
239         }
241         var logger = _loggersByName[name];
242         if (!logger) {
243             logger = _loggersByName[name] = new Logger(
244                 name, defaultLogger.getLevel(), defaultLogger.methodFactory);
245         }
246         return logger;
247     };
249     // Grab the current global log variable in case of overwrite
250     var _log = (typeof window !== undefinedType) ? window.log : undefined;
251     defaultLogger.noConflict = function() {
252         if (typeof window !== undefinedType &&
253             window.log === defaultLogger) {
254             window.log = _log;
255         }
257         return defaultLogger;
258     };
260     return defaultLogger;
261 }));