зеркало из https://github.com/mozilla/pluotsorbet.git
339 строки
9.7 KiB
JavaScript
339 строки
9.7 KiB
JavaScript
/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
|
/* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */
|
|
|
|
'use strict';
|
|
|
|
(function() {
|
|
|
|
var windowConsole = window.console;
|
|
|
|
var LOG_LEVELS = {
|
|
trace: 0,
|
|
log: 1,
|
|
info: 2,
|
|
warn: 3,
|
|
error: 4,
|
|
silent: 5,
|
|
};
|
|
|
|
/**
|
|
* The console(s) to which messages should be output. A comma-separated list
|
|
* of one or more of these consoles:
|
|
* web: the browser's Web Console (default)
|
|
* native: the native console (via the *dump* function)
|
|
* terminal: a faster canvas based console if Shumway.js is included.
|
|
*/
|
|
var ENABLED_CONSOLE_TYPES = (config.logConsole || "page").split(",");
|
|
var minLogLevel = LOG_LEVELS[config.logLevel || (config.release ? "error" : "log")];
|
|
|
|
|
|
//================================================================
|
|
|
|
|
|
var startTime = performance.now();
|
|
|
|
/**
|
|
* Every log entry serializes itself into a LogItem, so that it can
|
|
* subsequently be piped to various consoles.
|
|
*/
|
|
function LogItem(levelName, args) {
|
|
if (levelName === "trace") {
|
|
// If logging a trace, save the stack (minus uninteresting parts):
|
|
this.stack = new Error().stack.split('\n').filter(function(line) {
|
|
return line.indexOf("console.js") !== -1;
|
|
}).join('\n');
|
|
}
|
|
|
|
this.levelName = levelName;
|
|
this.ctx = typeof $ !== "undefined" && $ ? $.ctx : null;
|
|
this.logLevel = LOG_LEVELS[levelName];
|
|
this.args = args;
|
|
this.time = performance.now() - startTime;
|
|
}
|
|
|
|
function padRight(str, c, n) {
|
|
var length = str.length;
|
|
if (!c || length >= n) {
|
|
return str;
|
|
}
|
|
var max = (n - length) / c.length;
|
|
for (var i = 0; i < max; i++) {
|
|
str += c;
|
|
}
|
|
return str;
|
|
}
|
|
|
|
LogItem.prototype = {
|
|
get messagePrefix() {
|
|
var s = typeof J2ME !== "undefined" ? J2ME.Context.currentContextPrefix() : "";
|
|
if (false) {
|
|
s = this.time.toFixed(2) + " " + s;
|
|
}
|
|
return padRight(s.toString(), " ", 8) + " | ";
|
|
},
|
|
|
|
get message() {
|
|
if (this._message === undefined) {
|
|
this._message = this.messagePrefix + this.args.join(" ") + " ";
|
|
}
|
|
return this._message;
|
|
},
|
|
|
|
get searchPredicate() {
|
|
if (this._searchPredicate === undefined) {
|
|
this._searchPredicate = this.message.toLowerCase();
|
|
}
|
|
return this._searchPredicate;
|
|
},
|
|
|
|
/**
|
|
* Return this log item as an HTML node suitable for insertion
|
|
* into the page console, caching the node for performance when
|
|
* doing live filtering.
|
|
*/
|
|
toHtmlElement: function() {
|
|
if (this._cachedElement === undefined) {
|
|
var div = document.createElement("div");
|
|
div.classList.add("log-item");
|
|
div.classList.add("log-item-" + this.levelName);
|
|
div.textContent = this.message + "\n";
|
|
this._cachedElement = div;
|
|
}
|
|
return this._cachedElement;
|
|
},
|
|
|
|
matchesCurrentFilters: function() {
|
|
return (this.logLevel >= minLogLevel &&
|
|
(CONSOLES.page.currentFilterText === "" ||
|
|
this.searchPredicate.indexOf(CONSOLES.page.currentFilterText) !== -1));
|
|
}
|
|
};
|
|
|
|
|
|
//================================================================
|
|
// Console Implementations
|
|
/**
|
|
* In-page console, providing dynamic filtering and colored output.
|
|
* Renders to the document's "console" element.
|
|
*/
|
|
function PageConsole(selector) {
|
|
this.el = document.querySelector(selector);
|
|
this.items = [];
|
|
this.shouldAutoScroll = true;
|
|
this.currentFilterText = "";
|
|
window.addEventListener(
|
|
'console-filters-changed', this.onFiltersChanged.bind(this));
|
|
window.addEventListener(
|
|
'console-clear', this.onClear.bind(this));
|
|
}
|
|
|
|
PageConsole.prototype = {
|
|
push: function(item) {
|
|
this.items.push(item);
|
|
if (item.matchesCurrentFilters(item)) {
|
|
var wasAtBottom = this.isScrolledToBottom();
|
|
this.el.appendChild(item.toHtmlElement());
|
|
if (this.shouldAutoScroll && wasAtBottom) {
|
|
this.el.scrollTop = this.el.scrollHeight;
|
|
}
|
|
}
|
|
},
|
|
|
|
isScrolledToBottom: function() {
|
|
var fudgeFactor = 10; // Match the intent, not the pixel-perfect value
|
|
return this.el.scrollTop + this.el.clientHeight > this.el.scrollHeight - fudgeFactor;
|
|
},
|
|
|
|
onFiltersChanged: function() {
|
|
var fragment = document.createDocumentFragment();
|
|
this.items.forEach(function(item) {
|
|
if (item.matchesCurrentFilters()) {
|
|
fragment.appendChild(item.toHtmlElement());
|
|
}
|
|
}, this);
|
|
this.el.innerHTML = "";
|
|
this.el.appendChild(fragment);
|
|
},
|
|
|
|
onClear: function() {
|
|
this.items = [];
|
|
this.el.innerHTML = "";
|
|
}
|
|
|
|
};
|
|
|
|
/**
|
|
* WebConsole: The standard console.log() and friends.
|
|
*/
|
|
function WebConsole() {
|
|
}
|
|
|
|
WebConsole.prototype = {
|
|
push: function(item) {
|
|
if (item.matchesCurrentFilters()) {
|
|
if (consoleBuffer.length) {
|
|
// Preserve order w/r/t console.print().
|
|
flushConsoleBuffer();
|
|
}
|
|
windowConsole[item.levelName].apply(windowConsole, [item.message]);
|
|
}
|
|
},
|
|
};
|
|
|
|
/**
|
|
* NativeConsole: Throws logs at Gecko's dump().
|
|
*/
|
|
function NativeConsole() {
|
|
}
|
|
|
|
NativeConsole.prototype = {
|
|
push: function(item) {
|
|
if (item.matchesCurrentFilters()) {
|
|
dump(item.message + "\n");
|
|
}
|
|
}
|
|
};
|
|
|
|
/**
|
|
* RawConsoleForTests: Spits text directly into a textarea, for
|
|
* simpler CasperJS-style output testing.
|
|
*/
|
|
function RawConsoleForTests(selector) {
|
|
this.el = document.querySelector(selector);
|
|
}
|
|
|
|
RawConsoleForTests.prototype = {
|
|
push: function(item) {
|
|
if (item.matchesCurrentFilters()) {
|
|
this.el.textContent += item.levelName[0].toUpperCase() + ' ' + item.args.join(" ") + '\n';
|
|
}
|
|
}
|
|
};
|
|
|
|
function TerminalConsole(selector) {
|
|
this.buffer = new Terminal.Buffer();
|
|
this.view = new Terminal.View(new Terminal.Screen(document.querySelector(selector), 10), this.buffer);
|
|
this.count = 0;
|
|
window.addEventListener(
|
|
'console-clear', this.onClear.bind(this));
|
|
window.addEventListener(
|
|
'console-save', this.onSave.bind(this));
|
|
}
|
|
|
|
var contextColors = ["#111111", "#222222", "#333333", "#444444", "#555555", "#666666"];
|
|
|
|
|
|
function toRGB565(r, g, b) {
|
|
return ((r / 256 * 32) & 0x1F) << 11 |
|
|
((g / 256 * 64) & 0x3F) << 5 |
|
|
((b / 256 * 32) & 0x1F) << 0;
|
|
}
|
|
|
|
//trace: 0,
|
|
//log: 1,
|
|
//info: 2,
|
|
//warn: 3,
|
|
//error: 4,
|
|
//silent: 5,
|
|
|
|
var colors = [
|
|
toRGB565(0xFF, 0xFF, 0xFF),
|
|
toRGB565(0xFF, 0xFF, 0xFF),
|
|
toRGB565(0xFF, 0xFF, 0xFF),
|
|
toRGB565(0xFF, 0xFF, 0),
|
|
toRGB565(0xFF, 0, 0),
|
|
toRGB565(0, 0, 0),
|
|
];
|
|
|
|
var lastTime = 0;
|
|
TerminalConsole.prototype = {
|
|
push: function(item) {
|
|
if (item.matchesCurrentFilters()) {
|
|
this.buffer.color = colors[item.logLevel];
|
|
var thisTime = performance.now();
|
|
var prefix = (thisTime - lastTime).toFixed(2) + " : ";
|
|
prefix = "";
|
|
lastTime = thisTime;
|
|
this.buffer.writeString(prefix.padLeft(" ", 4) + item.logLevel + " " + item.message);
|
|
this.buffer.writeLine();
|
|
this.view.scrollToBottom();
|
|
}
|
|
},
|
|
onClear: function() {
|
|
this.buffer.clear();
|
|
this.view.scrollToBottom();
|
|
},
|
|
onSave: function() {
|
|
var string = this.buffer.toString();
|
|
var b = this.buffer;
|
|
var l = [];
|
|
for (var i = 0; i < b.h; i++) {
|
|
l.push(b.getLine(i));
|
|
}
|
|
var blob = new Blob([l.join("\n")], {type:'text/plain'});
|
|
saveAs(blob, "console-" + Date.now() + ".txt");
|
|
// window.open(URL.createObjectURL(blob));
|
|
}
|
|
}
|
|
|
|
var CONSOLES = {
|
|
web: new WebConsole(),
|
|
page: new PageConsole('#consoleContainer'),
|
|
native: new NativeConsole(),
|
|
raw: new RawConsoleForTests("#raw-console"),
|
|
terminal: typeof Terminal === "undefined" ? new WebConsole() : new TerminalConsole("#consoleContainer")
|
|
};
|
|
|
|
// If we're only printing to the web console, then use the original console
|
|
// object, so that file/line number references show up correctly in it.
|
|
if (ENABLED_CONSOLE_TYPES.length === 1 && ENABLED_CONSOLE_TYPES[0] === "web") {
|
|
return;
|
|
}
|
|
|
|
|
|
//================================================================
|
|
// Filtering & Runtime Page Console Options
|
|
|
|
document.querySelector('#console-clear').addEventListener('click', function() {
|
|
window.dispatchEvent(new CustomEvent('console-clear'));
|
|
});
|
|
|
|
document.querySelector('#console-save').addEventListener('click', function() {
|
|
window.dispatchEvent(new CustomEvent('console-save'));
|
|
});
|
|
|
|
var logLevelSelect = document.querySelector('#loglevel');
|
|
var consoleFilterTextInput = document.querySelector('#console-filter-input');
|
|
|
|
function updateFilters() {
|
|
minLogLevel = logLevelSelect.value;
|
|
CONSOLES.page.currentFilterText = consoleFilterTextInput.value.toLowerCase();
|
|
window.dispatchEvent(new CustomEvent('console-filters-changed'));
|
|
}
|
|
|
|
logLevelSelect.value = minLogLevel;
|
|
logLevelSelect.addEventListener('change', updateFilters);
|
|
|
|
consoleFilterTextInput.value = "";
|
|
consoleFilterTextInput.addEventListener('input', updateFilters);
|
|
|
|
//----------------------------------------------------------------
|
|
|
|
|
|
var logAtLevel = function(levelName) {
|
|
var item = new LogItem(levelName, Array.prototype.slice.call(arguments, 1));
|
|
ENABLED_CONSOLE_TYPES.forEach(function(consoleType) {
|
|
CONSOLES[consoleType].push(item);
|
|
});
|
|
};
|
|
|
|
window.console = Object.create(windowConsole, {
|
|
trace: { value: logAtLevel.bind(null, "trace") },
|
|
log: { value: logAtLevel.bind(null, "log") },
|
|
info: { value: logAtLevel.bind(null, "info") },
|
|
warn: { value: logAtLevel.bind(null, "warn") },
|
|
error: { value: logAtLevel.bind(null, "error") },
|
|
});
|
|
|
|
})();
|