Source: core/logger.js

/**
 * @module montage/core/logger
 * @requires montage/core/core
 */
var console = require('./extras/console').console;
var Montage = require("./core").Montage;

function getFunctionName(montageObject) {
    var aCaller = getFunctionName.caller.caller,
        aFunctionName;
    aFunctionName = aCaller.name;
    if (aFunctionName === "") {
        aFunctionName = "anonymous";
    }
    return aFunctionName;
}

function toTimeString(date) {
    if (date.getHours) {
        var hours = date.getHours(),
        mins = date.getMinutes(),
        secs = date.getSeconds();
        return (hours.length === 1 ? "0" + hours : hours) + ":" + (mins.length === 1 ? "0" + mins : mins) + ":" + (secs.length === 1 ? "0" + secs : secs) + "." + date.getMilliseconds();
    }
}

function consoleLog() {
    console.log(arguments);
}

var loggers = exports.loggers = {};

var SOLARIZED_COLORS = {
    "base03":  "#002b36",
    "base02":  "#073642",
    "base01":  "#586e75",
    "base00":  "#657b83",
    "base0":   "#839496",
    "base1":   "#93a1a1",
    "base2":   "#eee8d5",
    "base3":   "#fdf6e3",
    "yellow":  "#b58900",
    "orange":  "#cb4b16",
    "red":     "#dc322f",
    "magenta": "#d33682",
    "violet":  "#6c71c4",
    "blue":    "#268bd2",
    "cyan":    "#2aa198",
    "green":   "#859900"
};

function addColorProperty (logger) {
    var _color = function (cssString) {
            this._color = cssString;
            return this;
        },
        _getColor = function (name) {
            return function () {
                return logger.color(SOLARIZED_COLORS[name]);
            };
        };
    for (var name in SOLARIZED_COLORS) {
        if (SOLARIZED_COLORS.hasOwnProperty(name)) {
            _color[name] = _getColor(name);
        }
    }
    logger.color = _color;
}

/**
 * @class Logger
 * @extends Montage
 */
var Logger = exports.Logger = Montage.specialize(/** @lends Logger# */ {

    constructor: {
        value: function Logger() {

            // Set Function.noop on construcot 
            // to avoid issue with Object.create
            this.debug = Function.noop; 
            this.error = Function.noop;

            addColorProperty(this);
        }
    },

    /**
     * @function
     * @param {string} name The name of the logger.
     * @param {State} [dontStoreState=false] If true, don't store the isDebug state of the logger in localStorage .
     * @returns itself
     */
    init: {
        value: function (name, onStateChange, dontStoreState) {
            var storedState;
            this.name = name;
            this._onStateChange = onStateChange;
            this._storeState = !dontStoreState;
            if (this._storeState && typeof localStorage !== "undefined") {
                storedState = localStorage.getItem("_montage_logger_" + name);
                if (storedState) {
                    this.isDebug = storedState === "true";
                }
            }
            if (onStateChange) {
                this._onStateChange(storedState === "true");
            }
            this.isError = true;
            return this;
        }
    },

    /**
     * @type {string}
     * @default {string} null
     */
    name: {
        value: null
    },

    /**
     * @private
     * @type {Array}
     * @default {Array} []
     */
    _buffer: {
        value: null
    },

    /**
     * @private
     * @type {Array}
     * @default {Array} []
     */
    buffer: {
        get: function() {
            return this._buffer || (this._buffer = []);
        }
    },

    /**
     * @type {Property}
     * @default {boolean} false
     */
    buffered: {
        value: false
    },

    /**
     * Log all the contents the logger's buffer.
     */
    flush: {
        value: function () {
            var buffer = this.buffer,
                args,
                i;
            for (i = 0; (args = buffer[i]); i++) {
                this._formattedLog(args);
            }
            this.buffer.length = 0;
        }
    },

    /**
     * @type {boolean}
     */
    isDebug: {
        get: function () {
            return this.debug !== Function.noop;
        },
        set: function (value) {
            if (value) {
                this.debug = this._consoleLogMontage;
            } else {
                this.debug = Function.noop;
            }
        }

    },

    /**
     * @type {boolean}
     */
    isError: {
        get: function () {
            return this.error !== Function.noop;
        },
        set: function (value) {
            if (value) {
                this.error = this._consoleLogMontage;
            } else {
                this.error = Function.noop;
            }
        }
    },

    _consoleLogMontage: {
        value: function () {
            var firstArgument = arguments[0],
                //jshint -W106
                metadata = firstArgument._montage_metadata,
                //jshint +W106
                now = new Date();
            //[].unshift.call(arguments, toTimeString(now));
            // if the first argument is a Montage object, we replace it with the class and method's function name.
            if (metadata) {
                Array.prototype.shift.call(arguments);
                Array.prototype.unshift.call(arguments, metadata.objectName + "." + getFunctionName(firstArgument) + "()");
                if (this.buffered) {
                    this.buffer.push(arguments);
                } else {
                    this._formattedLog(arguments);
                }
            } else {
                if (this.buffered) {
                    this.buffer.push(arguments);
                } else {
                    this._formattedLog(arguments);
                }
            }
        }
    },

    _formattedLog: {
        value: function (args) {
            var firstArgument = args[0];
            if(exports.colors.isDebug && typeof firstArgument === "string") {
                Array.prototype.splice.call(args, 0, 1, "%c"+firstArgument, this._logCss);
            }
            console.log.apply(console, args);
        }
    },

    __logCss: {
        value: null
    },

    _logCss: {
        get: function () {
            if(this.__logCss === null) {
                this.__logCss = "";
                if (this._color) {
                    this.__logCss += "color:" + this._color + ";";
                } else {
                    this.__logCss += "color:black;";
                }
            }
            return this.__logCss;
        }
    },

    /**
     * @function Logger#debug
     * @param {Function|String} object If the first argument is a function the logger with print its name
     * @param {string} [...]
     */
    debug: {
        value: null
    },

    /**
     * @function Logger#error
     * @param {Function|String} object If the first argument is a function the logger with print its name
     * @param {string} [...]
     */
    error: {
        value: null
    },

    /**
     * @function Logger#toTimeString
     * @description Prints the current time in format HH:MM:SS.000
     */
    toTimeString: {
        value: toTimeString
    },

    _storeState: {
        value: null
    },

    _onStateChange: {
        value: null
    }
});

/**
 * @function module:montage/core/logger#logger
 */
exports.logger = function (loggerName, onStateChange, dontStoreState) {
    var logger;
    if (!(logger = loggers[loggerName])) {
        logger = new Logger().init(loggerName, onStateChange, dontStoreState);
        Montage.defineProperty(loggers, loggerName, {
            value: logger
        });
    }
    return logger;
};

exports.colors = exports.logger("colors");

var LoggerUI = Montage.specialize( /** @lends LoggerUI# */{

    init: {
        value: function () {
            if (document.nativeAddEventListener) {
                document.nativeAddEventListener("keyup", this, false);
                document.nativeAddEventListener("keydown", this, false);
            } else {
                document.addEventListener("keyup", this, false);
                document.addEventListener("keydown", this, false);
            }
            return this;
        }
    },

    inspectorElement: {
        value: null
    },

    m_dontRemove: {
        value: null
    },

    titleHeader: {
        value: null
    },

    shown: {
        value: false
    },

    isCtrl: {
        value: false
    },

    isAlt: {
        value: false
    },

    keyup: {
        value: function (event) {
            if (event.which === 17) {
                this.isCtrl = false;
            }
            if (event.which === 18) {
                this.isAlt = false;
            }
        }
    },

    keydown: {
        value: function (event) {
            if (event.which === 17) {
                this.isCtrl = true;
            }
            if (event.which === 18) {
                this.isAlt = true;
            }
            if (event.which === 76 && this.isCtrl === true && this.isAlt === true) {
                if (this.shown) {
                    this.hideInspector();
                } else {
                    this.showInspector();
                }
                return false;
            }
        }
    },

    change: {
        value: function (event) {
            var value = event.target.checked,
                name = event.target.value,
                logger = loggers[name];
            logger.isDebug = value;
            if (logger._onStateChange) {
                logger._onStateChange(value);
            }
            if (logger._storeState && typeof localStorage !== "undefined") {
                localStorage.setItem("_montage_logger_" + name, value);
            }
        }
    },

    mouseup: {
        value: function (event) {
            this.hideInspector();
        }
    },

    showInspector: {
        value: function () {
            if (! this.inspectorElement) {
                var i = 0,
                    iLogger,
                    div1,
                    h1,
                    div2,
                    label,
                    input,
                    storedValue,
                    storageKey,
                    loggerKeys,
                    style,
                    span;
                this.m_dontRemove = document.getElementsByTagName("body")[0];
                this.inspectorElement = document.createElement("div");
                this.inspectorElement.id = "_montage_logger_inspector";
                div1 = document.createElement("div");
                this.inspectorElement.appendChild(div1);
                div2 = document.createElement("div");
                div1.appendChild(div2);
                h1 = document.createElement("h1");
                h1.className = "_montage_logger_inspector-title";
                h1.textContent = "Logger Inspector";
                this.titleHeader = h1;
                div2.appendChild(h1);
                loggerKeys = Object.keys(loggers);

                for (i = 0; (iLogger = loggers[loggerKeys[i]]); i++) {
                    label = document.createElement("label");
                    input = document.createElement("input");
                    span = document.createElement("span");
                    label.className = "_montage_logger_inspector-content";
                    span.textContent = iLogger.name;
                    label.appendChild(input);
                    label.appendChild(span);
                    input.value = iLogger.name;
                    input.type = "checkbox";
                    input.checked = !!iLogger.isDebug;
                    storageKey = "_montage_logger_" + iLogger.name;
                    if (iLogger._storeState && typeof localStorage !== "undefined") {
                        storedValue = localStorage.getItem(storageKey);
                        if (!storedValue) {
                            localStorage.setItem(storageKey, iLogger.isDebug);
                        }
                    }
                    div2.appendChild(label);
                }
                style = document.createElement("style");
                //YUCK!! I wish I could use a reel!!!
                var styleTest = "#_montage_logger_inspector {";
                styleTest += "    border: 1px solid rgba(15,15,15,0.4);";
                styleTest += "    position: fixed;";
                styleTest += "    right: 25px;";
                styleTest += "    top: 25px;";
                styleTest += "    -webkit-border-radius: 5px;";
                styleTest += "    color: #dddddd;";
                styleTest += '    font: 10px "Lucida Grande","Lucida Sans", sans;';
                styleTest += "    background:-webkit-gradient(linear, left top, left bottom, from(rgba(15,15,15,0.75)), to(rgba(15,15,15,0.95)) );";
                styleTest += "    -webkit-box-shadow: 0 0 15px rgba(0,0,0,.3);";
                styleTest += "    width: 250px;";
                styleTest += "}";
                styleTest += "#_montage_logger_inspector div {";
                styleTest += "    -webkit-border-radius: 5px;";
                styleTest += "    background: -webkit-gradient(radial, 100 -60, 0, 125 -50, 125, from(rgba(255,255,255,0.00)), to(rgba(0,0,0,.2)), color-stop(1, rgba(0,0,0,.2)));";
                styleTest += "}";
                styleTest += "#_montage_logger_inspector div div {";
                styleTest += "    background: -webkit-gradient(linear, left top, left bottom, from(rgba(255,255,255,0.2)), to(rgba(0,0,0,.1)), color-stop(0.33, rgba(255,255,255,.01)), color-stop(0.33, rgba(50,50,50,1)) );";
                styleTest += "    padding: 7px 10px;";
                styleTest += "    -webkit-border-radius: 3px;";
                styleTest += "    overflow-x: hidden;";
                styleTest += "}";
                styleTest += "._montage_logger_inspector-title {";
                styleTest += "    color: rgba(255,255,255,0.9);";
                styleTest += "    font-size: 13px;";
                styleTest += "    margin: 0 0 11px 0;";
                styleTest += "    padding: 0 0 0 5px;";
                styleTest += "}";
                styleTest += "._montage_logger_inspector-content {";
                styleTest += "    padding: 0 0 0 20px;";
                styleTest += "    margin: 0;";
                styleTest += "    display: block;";
                styleTest += "}";
                style.textContent = styleTest;
                document.head.appendChild(style);
            }
            this.shown = true;

            this.m_dontRemove.appendChild(this.inspectorElement);
            this.titleHeader.nativeAddEventListener("mouseup", this, false);
            this.inspectorElement.nativeAddEventListener("change", this, false);

        }
    },

    hideInspector: {
        value: function () {
            if (document.getElementById("_montage_logger_inspector")) {
                this.shown = false;
                this.m_dontRemove.removeChild(this.inspectorElement);
                this.titleHeader.nativeRemoveEventListener("mouseup", this, false);
                this.inspectorElement.nativeRemoveEventListener("change", this, false);
            }
        }
    },

    handleEvent: {
        enumerable: false,
        value: function (event) {
            if (this[event.type]) {
                this[event.type](event);
            }
        }
    }
});

if (typeof window !== "undefined") {
    window.loggers = loggers;
    if (typeof localStorage !== "undefined") {
        new LoggerUI().init();
    }
}