123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287 |
- /* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this file,
- * You can obtain one at http://mozilla.org/MPL/2.0/. */
- define(["util"], function (util) {
- var console = window.console || {log: function () {}};
- var Console = util.Class({
- constructor: function () {
- this.messages = [];
- this.level = this.levels.log;
- },
- messageLimit: 100,
- levels: {
- debug: 1,
- // FIXME: I'm considering *not* wrapping console.log, and strictly keeping
- // it as a debugging tool; also line numbers would be preserved
- log: 2,
- info: 3,
- notify: 4,
- warn: 5,
- error: 6,
- fatal: 7
- },
- // Gets set below:
- maxLevel: 0,
- consoleLevels: [
- [],
- console.debug || [],
- console.log || [],
- console.info || [],
- console.notify || [],
- console.warn || [],
- console.error || [],
- console.fatal || []
- ],
- levelNames: {},
- setLevel: function (l) {
- var number;
- if (typeof l == "string") {
- number = this.levels[l];
- if (number === undefined) {
- throw new Error("Tried to set Console level to unknown level string: " + l);
- }
- l = number;
- }
- if (typeof l == "function") {
- number = this.consoleLevels.indexOf(l);
- if (number == -1) {
- throw new Error("Tried to set Console level based on unknown console function: " + l);
- }
- l = number;
- }
- if (typeof l == "number") {
- if (l < 0) {
- throw new Error("Console level must be 0 or larger: " + l);
- } else if (l > this.maxLevel) {
- throw new Error("Console level must be " + this.maxLevel + " or smaller: " + l);
- }
- }
- this.level = l;
- },
- write: function (level) {
- try {
- this.messages.push([
- Date.now(),
- level,
- this._stringify(Array.prototype.slice.call(arguments, 1))
- ]);
- } catch (e) {
- console.warn("Error stringifying argument:", e);
- }
- if (level != "suppress" && this.level <= level) {
- var method = console[this.levelNames[level]];
- if (! method) {
- method = console.log;
- }
- method.apply(console, Array.prototype.slice.call(arguments, 1));
- }
- },
- suppressedWrite: function () {
- this.write.apply(this, ["suppress"].concat(Array.prototype.slice.call(arguments)));
- },
- trace: function (level) {
- level = level || 'log';
- if (console.trace) {
- level = "suppressedWrite";
- }
- try {
- throw new Error();
- } catch (e) {
- // FIXME: trim this frame
- var stack = e.stack;
- stack = stack.replace(/^[^\n]*\n/, "");
- this[level](stack);
- }
- if (console.trace) {
- console.trace();
- }
- },
- _browserInfo: function () {
- // FIXME: add TogetherJS version and
- return [
- "TogetherJS base URL: " + TogetherJS.baseUrl,
- "User Agent: " + navigator.userAgent,
- "Page loaded: " + this._formatDate(TogetherJS.pageLoaded),
- "Age: " + this._formatMinutes(Date.now() - TogetherJS.pageLoaded) + " minutes",
- // FIXME: make this right:
- //"Window: height: " + window.screen.height + " width: " + window.screen.width
- "URL: " + location.href,
- "------+------+----------------------------------------------"
- ];
- },
- _stringify: function (args) {
- var s = "";
- for (var i=0; i<args.length; i++) {
- if (s) {
- s += " ";
- }
- s += this._stringifyItem(args[i]);
- }
- return s;
- },
- _stringifyItem: function (item) {
- if (typeof item == "string") {
- if (item === "") {
- return '""';
- }
- return item;
- }
- if (typeof item == "object" && item.repr) {
- try {
- return item.repr();
- } catch (e) {
- console.warn("Error getting object repr:", item, e);
- }
- }
- if (item !== null && typeof item == "object") {
- // FIXME: this can drop lots of kinds of values, like a function or undefined
- item = JSON.stringify(item);
- }
- return item.toString();
- },
- _formatDate: function (timestamp) {
- return (new Date(timestamp)).toISOString();
- },
- _formatTime: function (timestamp) {
- return ((timestamp - TogetherJS.pageLoaded) / 1000).toFixed(2);
- },
- _formatMinutes: function (milliseconds) {
- var m = Math.floor(milliseconds / 1000 / 60);
- var remaining = milliseconds - (m * 1000 * 60);
- if (m > 10) {
- // Over 10 minutes, just ignore the seconds
- return m;
- }
- var seconds = Math.floor(remaining / 1000) + "";
- m += ":";
- seconds = lpad(seconds, 2, "0");
- m += seconds;
- if (m == "0:00") {
- m += ((remaining / 1000).toFixed(3) + "").substr(1);
- }
- return m;
- },
- _formatLevel: function (l) {
- if (l === "suppress") {
- return "";
- }
- return this.levelNames[l];
- },
- toString: function () {
- try {
- var lines = this._browserInfo();
- this.messages.forEach(function (m) {
- lines.push(lpad(this._formatTime(m[0]), 6) + " " + rpad(this._formatLevel(m[1]), 6) + " " + lpadLines(m[2], 14));
- }, this);
- return lines.join("\n");
- } catch (e) {
- // toString errors can otherwise be swallowed:
- console.warn("Error running console.toString():", e);
- throw e;
- }
- },
- submit: function (options) {
- // FIXME: friendpaste is broken for this
- // (and other pastebin sites aren't really Browser-accessible)
- return util.Deferred(function (def) {
- options = options || {};
- var site = options.site || TogetherJS.config.get("pasteSite") || "https://www.friendpaste.com/";
- var req = new XMLHttpRequest();
- req.open("POST", site);
- req.setRequestHeader("Content-Type", "application/json");
- req.send(JSON.stringify({
- "title": options.title || "TogetherJS log file",
- "snippet": this.toString(),
- "language": "text"
- }));
- req.onreadystatechange = function () {
- if (req.readyState === 4) {
- var data = JSON.parse(req.responseText);
- }
- };
- });
- }
- });
- function rpad(s, len, pad) {
- s = s + "";
- pad = pad || " ";
- while (s.length < len) {
- s += pad;
- }
- return s;
- }
- function lpad(s, len, pad) {
- s = s + "";
- pad = pad || " ";
- while (s.length < len) {
- s = pad + s;
- }
- return s;
- }
- function lpadLines(s, len, pad) {
- var i;
- s = s + "";
- if (s.indexOf("\n") == -1) {
- return s;
- }
- pad = pad || " ";
- var fullPad = "";
- for (i=0; i<len; i++) {
- fullPad += pad;
- }
- s = s.split(/\n/g);
- for (i=1; i<s.length; i++) {
- s[i] = fullPad + s[i];
- }
- return s.join("\n");
- }
- // This is a factory that creates `Console.prototype.debug`, `.error` etc:
- function logFunction(name, level) {
- return function () {
- this.write.apply(this, [level].concat(Array.prototype.slice.call(arguments)));
- };
- }
- util.forEachAttr(Console.prototype.levels, function (value, name) {
- Console.prototype[name] = logFunction(name, value);
- Console.prototype.maxLevel = Math.max(Console.prototype.maxLevel, value);
- });
- util.forEachAttr(Console.prototype.levels, function (value, name) {
- Console.prototype.levelNames[value] = name;
- });
- var appConsole = Console();
- appConsole.ConsoleClass = Console;
- return appConsole;
- });
|