/* * CPLog.j * Foundation * * Created by Thomas Robinson. * Copyright 2008, 280 North, Inc. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ exports.CPLogDisable = false; var CPLogDefaultTitle = "Cappuccino"; var CPLogLevels = ["fatal", "error", "warn", "info", "debug", "trace"]; var CPLogDefaultLevel = CPLogLevels[3]; var _CPLogLevelsInverted = {}; for (var i = 0; i < CPLogLevels.length; i++) _CPLogLevelsInverted[CPLogLevels[i]] = i; var _CPLogRegistrations = {}; // Register Functions: // Register a logger for all levels, or up to an optional max level exports.CPLogRegister = function(aProvider, aMaxLevel) { exports.CPLogRegisterRange(aProvider, CPLogLevels[0], aMaxLevel || CPLogLevels[CPLogLevels.length-1]); } // Register a logger for a range of levels exports.CPLogRegisterRange = function(aProvider, aMinLevel, aMaxLevel) { var min = _CPLogLevelsInverted[aMinLevel]; var max = _CPLogLevelsInverted[aMaxLevel]; if (min !== undefined && max !== undefined) for (var i = 0; i <= max; i++) CPLogRegisterSingle(aProvider, CPLogLevels[i]); } // Register a logger for a single level exports.CPLogRegisterSingle = function(aProvider, aLevel) { if (!_CPLogRegistrations[aLevel]) _CPLogRegistrations[aLevel] = []; // prevent duplicate registrations for (var i = 0; i < _CPLogRegistrations[aLevel].length; i++) if (_CPLogRegistrations[aLevel][i] === aProvider) return; _CPLogRegistrations[aLevel].push(aProvider); } exports.CPLogUnregister = function(aProvider) { for (var aLevel in _CPLogRegistrations) for (var i = 0; i < _CPLogRegistrations[aLevel].length; i++) if (_CPLogRegistrations[aLevel][i] === aProvider) _CPLogRegistrations[aLevel].splice(i--, 1); // decrement since we're removing an element } // Main CPLog, which dispatches to individual loggers function _CPLogDispatch(parameters, aLevel, aTitle) { if (aTitle == undefined) aTitle = CPLogDefaultTitle; if (aLevel == undefined) aLevel = CPLogDefaultLevel; // use sprintf if param 0 is a string and there is more than one param. otherwise just convert param 0 to a string var message = (typeof parameters[0] == "string" && parameters.length > 1) ? sprintf.apply(null, parameters) : String(parameters[0]); if (_CPLogRegistrations[aLevel]) for (var i = 0; i < _CPLogRegistrations[aLevel].length; i++) _CPLogRegistrations[aLevel][i](message, aLevel, aTitle); } // Setup CPLog() and CPLog.xxx() aliases var CPLog = exports.CPLog = function() { _CPLogDispatch(arguments); } for (var i = 0; i < CPLogLevels.length; i++) CPLog[CPLogLevels[i]] = (function(level) { return function() { _CPLogDispatch(arguments, level); }; })(CPLogLevels[i]); // Helpers functions: var _CPFormatLogMessage = function(aString, aLevel, aTitle) { var now = new Date(); aLevel = ( aLevel == null ? '' : ' [' + aLevel + ']' ); if (typeof sprintf == "function") return sprintf("%4d-%02d-%02d %02d:%02d:%02d.%03d %s%s: %s", now.getFullYear(), now.getMonth(), now.getDate(), now.getHours(), now.getMinutes(), now.getSeconds(), now.getMilliseconds(), aTitle, aLevel, aString); else return now + " " + aTitle + aLevel + ": " + aString; } // Loggers: var ANSI_ESC = String.fromCharCode(0x1B); var ANSI_CSI = ANSI_ESC + '['; var ANSI_TEXT_PROP = 'm'; var ANSI_RESET = '0'; var ANSI_BOLD = '1'; var ANSI_FAINT = '2'; // unsupported? var ANSI_NORMAL = '22'; var ANSI_ITALIC = '3'; // unsupported? var ANSI_UNDER = '4'; var ANSI_UNDER_DBL = '21'; // unsupported? var ANSI_UNDER_OFF = '24'; var ANSI_BLINK = '5'; var ANSI_BLINK_FAST = '6'; // unsupported? var ANSI_BLINK_OFF = '25'; var ANSI_REVERSE = '7'; var ANSI_POSITIVE = '27'; var ANSI_CONCEAL = '8'; var ANSI_REVEAL = '28'; var ANSI_FG = '3'; var ANSI_BG = '4'; var ANSI_FG_INTENSE = '9'; var ANSI_BG_INTENSE = '10'; var ANSI_BLACK = '0'; var ANSI_RED = '1'; var ANSI_GREEN = '2'; var ANSI_YELLOW = '3'; var ANSI_BLUE = '4'; var ANSI_MAGENTA = '5'; var ANSI_CYAN = '6'; var ANSI_WHITE = '7'; var colorCodeMap = { "black" : ANSI_BLACK, "red" : ANSI_RED, "green" : ANSI_GREEN, "yellow" : ANSI_YELLOW, "blue" : ANSI_BLUE, "magenta" : ANSI_MAGENTA, "cyan" : ANSI_CYAN, "white" : ANSI_WHITE } function ANSIControlCode(code, parameters) { if (parameters == undefined) parameters = ""; else if (typeof(parameters) == 'object' && (parameters instanceof Array)) parameters = parameters.join(';'); return ANSI_CSI + String(parameters) + String(code); } // simple text helpers: function ANSITextApplyProperties(string, properties) { return ANSIControlCode(ANSI_TEXT_PROP, properties) + String(string) + ANSIControlCode(ANSI_TEXT_PROP); } exports.ANSITextColorize = function(string, color) { if (colorCodeMap[color] == undefined) return string; return ANSITextApplyProperties(string, ANSI_FG + colorCodeMap[color]); } // CPLogPrint uses the print() functions present in many non-browser command line JavaScript interpreters var levelColorMap = { "fatal": "red", "error": "red", "warn" : "yellow", "info" : "green", "debug": "cyan", "trace": "blue" } // CPLogConsole uses the built in "console" object exports.CPLogConsole = function(aString, aLevel, aTitle) { if (typeof console != "undefined") { var message = _CPFormatLogMessage(aString, aLevel, aTitle); var logger = { "fatal": "error", "error": "error", "warn": "warn", "info": "info", "debug": "debug", "trace": "debug" }[aLevel]; if (logger && console[logger]) console[logger](message); else if (console.log) console.log(message); } } #if COMMONJS exports.CPLogPrint = function(aString, aLevel, aTitle) { if (typeof print != "undefined") { if (aLevel == "fatal" || aLevel == "error" || aLevel == "warn") var message = exports.ANSITextColorize(_CPFormatLogMessage(aString, aLevel, aTitle), levelColorMap[aLevel]); else var message = _CPFormatLogMessage(aString, exports.ANSITextColorize(aLevel, levelColorMap[aLevel]), aTitle); require("system").stderr.print(message); } } #else // CPLogAlert uses basic browser alert() functions exports.CPLogAlert = function(aString, aLevel, aTitle) { if (typeof alert != "undefined" && !exports.CPLogDisable) { var message = _CPFormatLogMessage(aString, aLevel, aTitle); exports.CPLogDisable = !confirm(message + "\n\n(Click cancel to stop log alerts)"); } } // CPLogPopup uses a slick popup window in the browser: var CPLogWindow = null; exports.CPLogPopup = function(aString, aLevel, aTitle) { try { if (exports.CPLogDisable || window.open == undefined) return; if (!CPLogWindow || !CPLogWindow.document) { CPLogWindow = window.open("", "_blank", "width=600,height=400,status=no,resizable=yes,scrollbars=yes"); if (!CPLogWindow) { exports.CPLogDisable = !confirm(aString + "\n\n(Disable pop-up blocking for CPLog window; Click cancel to stop log alerts)"); return; } _CPLogInitPopup(CPLogWindow); } var logDiv = CPLogWindow.document.createElement("div"); logDiv.setAttribute("class", aLevel || "fatal"); var message = _CPFormatLogMessage(aString, null, aTitle); logDiv.appendChild(CPLogWindow.document.createTextNode(message)); CPLogWindow.log.appendChild(logDiv); if (CPLogWindow.focusEnabled.checked) CPLogWindow.focus(); if (CPLogWindow.blockEnabled.checked) CPLogWindow.blockEnabled.checked = CPLogWindow.confirm(message+"\nContinue blocking?"); if (CPLogWindow.scrollEnabled.checked) CPLogWindow.scrollToBottom(); } catch(e) { // TODO: some error handling/reporting } } function _CPLogInitPopup(logWindow) { var doc = logWindow.document; // HACK so that head is available below: doc.writeln("