/* * CPLog.js * Objective-J * * Created by Thomas Robinson. * Copyright 2008-2010, 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 */ GLOBAL(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 GLOBAL(CPLogRegister) = function(aProvider, aMaxLevel, aFormatter) { CPLogRegisterRange(aProvider, CPLogLevels[0], aMaxLevel || CPLogLevels[CPLogLevels.length-1], aFormatter); } // Register a logger for a range of levels GLOBAL(CPLogRegisterRange) = function(aProvider, aMinLevel, aMaxLevel, aFormatter) { var min = _CPLogLevelsInverted[aMinLevel]; var max = _CPLogLevelsInverted[aMaxLevel]; if (min !== undefined && max !== undefined && min <= max) for (var i = min; i <= max; i++) CPLogRegisterSingle(aProvider, CPLogLevels[i], aFormatter); } // Register a logger for a single level GLOBAL(CPLogRegisterSingle) = function(aProvider, aLevel, aFormatter) { if (!_CPLogRegistrations[aLevel]) _CPLogRegistrations[aLevel] = []; // prevent duplicate registrations, but change formatter for (var i = 0; i < _CPLogRegistrations[aLevel].length; i++) if (_CPLogRegistrations[aLevel][i][0] === aProvider) { _CPLogRegistrations[aLevel][i][1] = aFormatter; return; } _CPLogRegistrations[aLevel].push([aProvider, aFormatter]); } GLOBAL(CPLogUnregister) = function(aProvider) { for (var aLevel in _CPLogRegistrations) for (var i = 0; i < _CPLogRegistrations[aLevel].length; i++) if (_CPLogRegistrations[aLevel][i][0] === 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) ? exports.sprintf.apply(null, parameters) : String(parameters[0]); if (_CPLogRegistrations[aLevel]) for (var i = 0; i < _CPLogRegistrations[aLevel].length; i++) { var logger = _CPLogRegistrations[aLevel][i]; logger[0](message, aLevel, aTitle, logger[1]); } } // Setup CPLog() and CPLog.xxx() aliases GLOBAL(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 ? '' : ' [' + CPLogColorize(aLevel, aLevel) + ']' ); if (typeof exports.sprintf == "function") return exports.sprintf("%4d-%02d-%02d %02d:%02d:%02d.%03d %s%s: %s", now.getFullYear(), now.getMonth() + 1, now.getDate(), now.getHours(), now.getMinutes(), now.getSeconds(), now.getMilliseconds(), aTitle, aLevel, aString); else return now + " " + aTitle + aLevel + ": " + aString; } // Loggers: // CPLogConsole uses the built in "console" object GLOBAL(CPLogConsole) = function(aString, aLevel, aTitle, aFormatter) { if (typeof console != "undefined") { var message = (aFormatter || _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 var levelColorMap = { "fatal": "red", "error": "red", "warn" : "yellow", "info" : "green", "debug": "cyan", "trace": "blue" } try { var SYSTEM = require("system"); var FILE = require("file"); if (SYSTEM.args[0]) CPLogDefaultTitle = FILE.basename(SYSTEM.args[0]); } catch (e) { } var stream; GLOBAL(CPLogColorize) = function(aString, aLevel) { // Try to determine if a colorizing stanza is already open, they can't be nested if (/^.*\x00\w+\([^\x00]*$/.test(aString)) return aString; else return "\0" + (levelColorMap[aLevel] || "info") + "(" + aString + "\0)"; } GLOBAL(CPLogPrint) = function(aString, aLevel, aTitle, aFormatter) { if (stream === undefined) { try { stream = require("narwhal/term").stream; } catch (e) { stream = null; } } var formatter = aFormatter || _CPFormatLogMessage; if (stream) { if (aLevel == "fatal" || aLevel == "error" || aLevel == "warn") stream.print(CPLogColorize(formatter(aString, aLevel, aTitle), aLevel)); else stream.print(formatter(aString, aLevel, aTitle)); } else if (typeof print != "undefined") { print(formatter(aString, aLevel, aTitle)) } } #else // A stub to allow the same formatter to be used for both stream and browser output GLOBAL(CPLogColorize) = function(aString, aLevel) { return aString; } // CPLogAlert uses basic browser alert() functions GLOBAL(CPLogAlert) = function(aString, aLevel, aTitle, aFormatter) { if (typeof alert != "undefined" && !CPLogDisable) { var message = (aFormatter || _CPFormatLogMessage)(aString, aLevel, aTitle); CPLogDisable = !confirm(message + "\n\n(Click cancel to stop log alerts)"); } } // CPLogPopup uses a slick popup window in the browser: var CPLogWindow = null; GLOBAL(CPLogPopup) = function(aString, aLevel, aTitle, aFormatter) { try { if (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) { 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 = (aFormatter || _CPFormatLogMessage)(aString, aFormatter ? aLevel : 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 } } var CPLogPopupStyle =''; function _CPLogInitPopup(logWindow) { var doc = logWindow.document; // HACK so that head is available below: doc.writeln("