// ========================================================================== // SC.Logger // ========================================================================== /** If {@link SC.Logger.format} is true, this delimiter will be put between arguments. @property {String} */ SC.LOGGER_LOG_DELIMITER = ", "; /** If {@link SC.Logger.error} falls back onto {@link SC.Logger.log}, this will be prepended to the output. @property {String} */ SC.LOGGER_LOG_ERROR = "ERROR: "; /** If {@link SC.Logger.info} falls back onto {@link SC.Logger.log}, this will be prepended to the output. @property {String} */ SC.LOGGER_LOG_INFO = "INFO: "; /** If {@link SC.Logger.warn} falls back onto {@link SC.Logger.log}, this will be prepended to the output. @property {String} */ SC.LOGGER_LOG_WARN = "WARNING: "; /** If {@link SC.Logger.debug} falls back onto {@link SC.Logger.log}, this will be prepended to the output. @property {String} */ SC.LOGGER_LOG_DEBUG = "DEBUG: "; /** @class Object to allow for safe logging actions, such as using the browser console. The FireFox plugin Firebug was used as a function reference. Please see {@link Firebug Logging Reference} for further information. @author Colin Campbell @author Benedikt Böhm @extends SC.Object @since Sproutcore 1.0 @see Firebug Logging Reference */ SC.Logger = SC.Object.create({ // .......................................................... // PROPERTIES // /** Whether or not to enable debug logging. @property: {Boolean} */ debugEnabled: NO, /** Computed property that checks for the existence of the reporter object. @property {Boolean} */ exists: function() { return typeof(this.get('reporter')) !== 'undefined' && this.get('reporter') != null; }.property('reporter').cacheable(), /** If console.log does not exist, SC.Logger will use window.alert instead. This property is only used inside {@link SC.Logger.log}. If fallBackOnLog is false and you call a different function, an alert will not be opened. @property {Boolean} */ fallBackOnAlert: NO, /** If some function, such as console.dir, does not exist, SC.Logger will try console.log if this is true. @property {Boolean} */ fallBackOnLog: YES, /** Whether or not to format multiple arguments together or let the browser deal with that. @property {Boolean} */ format: YES, /** The reporter is the object which implements the actual logging functions. @default The browser's console @property {Object} */ reporter: console, // .......................................................... // METHODS // /** Log output to the console, but only if it exists. @param {String|Array|Function|Object} @returns {Boolean} true if reporter.log exists, false otherwise */ log: function() { var reporter = this.get('reporter'); // log through the reporter if (this.get('exists') && typeof(reporter.log) === "function") { if (this.get('format')) { reporter.log(this._argumentsToString.apply(this, arguments)); } else { reporter.log.apply(reporter, arguments); } return true; } // log through alert else if (this.fallBackOnAlert) { var s = this.get('format') ? this._argumentsToString.apply(this, arguments) : arguments; // include support for overriding the alert through the reporter // if it has come this far, it's likely this will fail if (this.get('exists') && typeof(reporter.alert) === "function") { reporter.alert(s); } else { alert(s); } return true; } return false; }, /** Log a debug message to the console. Logs the response using {@link SC.Logger.log} if reporter.debug does not exist and {@link SC.Logger.fallBackOnLog} is true. @param {String|Array|Function|Object} @returns {Boolean} true if logged to reporter, false if not */ debug: function() { var reporter = this.get('reporter'); if (this.get('debugEnabled') !== YES) { return false; } if (this.get('exists') && (typeof reporter.debug === "function")) { reporter.debug.apply(reporter, arguments); return true; } else if (this.fallBackOnLog) { var a = this._argumentsToArray(arguments); if (typeof(a.unshift) === "function") a.unshift(SC.LOGGER_LOG_DEBUG); return this.log.apply(this, a); } return false; }, /** Prints the properties of an object. Logs the object using {@link SC.Logger.log} if the reporter.dir function does not exist and {@link SC.Logger.fallBackOnLog} is true. @param {Object} @returns {Boolean} true if logged to console, false if not */ dir: function() { var reporter = this.get('reporter'); if (this.get('exists') && typeof(reporter.dir) === "function") { // Firebug's console.dir doesn't support multiple objects here // but maybe custom reporters will reporter.dir.apply(reporter, arguments); return true; } return (this.fallBackOnLog) ? this.log.apply(this, arguments) : false; }, /** Prints an XML outline for any HTML or XML object. Logs the object using {@link SC.Logger.log} if reporter.dirxml function does not exist and {@lnk SC.Logger.fallBackOnLog} is true. @param {Object} @returns {Boolean} true if logged to reporter, false if not */ dirxml: function() { var reporter = this.get('reporter'); if (this.get('exists') && typeof(reporter.dirxml) === "function") { // Firebug's console.dirxml doesn't support multiple objects here // but maybe custom reporters will reporter.dirxml.apply(reporter, arguments); return true; } return (this.fallBackOnLog) ? this.log.apply(this, arguments) : false; }, /** Log an error to the console Logs the error using {@link SC.Logger.log} if reporter.error does not exist and {@link SC.Logger.fallBackOnLog} is true. @param {String|Array|Function|Object} @returns {Boolean} true if logged to reporter, false if not */ error: function() { var reporter = this.get('reporter'); if (this.get('exists') && typeof(reporter.error) === "function") { reporter.error.apply(reporter, arguments); return true; } else if (this.fallBackOnLog) { var a = this._argumentsToArray(arguments); if (typeof(a.unshift) === "function") a.unshift(SC.LOGGER_LOG_ERROR); return this.log.apply(this, a); } return false; }, /** Every log after this call until {@link SC.Logger.groupEnd} is called will be indented for readability. You can create as many levels as you want. @param {String} [title] An optional title to display above the group @returns {Boolean} true if reporter.group exists, false otherwise */ group: function(s) { var reporter = this.get('reporter'); if (this.get('exists') && typeof(reporter.group) === "function") { reporter.group(s); return true; } return false; }, /** Ends a group declared with {@link SC.Logger.group}. @returns {Boolean} true if the reporter.groupEnd exists, false otherwise @see SC.Logger.group */ groupEnd: function() { var reporter = this.get('reporter'); if (this.get('exists') && typeof(reporter.groupEnd) === "function") { reporter.groupEnd(); return true; } return false; }, /** Log an information response to the reporter. Logs the response using {@link SC.Logger.log} if reporter.info does not exist and {@link SC.Logger.fallBackOnLog} is true. @param {String|Array|Function|Object} @returns {Boolean} true if logged to reporter, false if not */ info: function() { var reporter = this.get('reporter'); if (this.get('exists') && typeof(reporter.info) === "function") { reporter.info.apply(reporter, arguments); return true; } else if (this.fallBackOnLog) { var a = this._argumentsToArray(arguments); if (typeof(a.unshift) === "function") a.unshift(SC.LOGGER_LOG_INFO); return this.log.apply(this, a); } return false; }, /** Begins the JavaScript profiler, if it exists. Call {@link SC.Logger.profileEnd} to end the profiling process and receive a report. @returns {Boolean} true if reporter.profile exists, false otherwise */ profile: function() { var reporter = this.get('reporter'); if (this.get('exists') && typeof(reporter.profile) === "function") { reporter.profile(); return true; } return false; }, /** Ends the JavaScript profiler, if it exists. @returns {Boolean} true if reporter.profileEnd exists, false otherwise @see SC.Logger.profile */ profileEnd: function() { var reporter = this.get('reporter'); if (this.get('exists') && typeof(reporter.profileEnd) === "function") { reporter.profileEnd(); return true; } return false; }, /** Measure the time between when this function is called and {@link SC.Logger.timeEnd} is called. @param {String} name The name of the profile to begin @returns {Boolean} true if reporter.time exists, false otherwise @see SC.Logger.timeEnd */ time: function(name) { var reporter = this.get('reporter'); if (this.get('exists') && typeof(reporter.time) === "function") { reporter.time(name); return true; } return false; }, /** Ends the profile specified. @param {String} name The name of the profile to end @returns {Boolean} true if reporter.timeEnd exists, false otherwise @see SC.Logger.time */ timeEnd: function(name) { var reporter = this.get('reporter'); if (this.get('exists') && typeof(reporter.timeEnd) === "function") { reporter.timeEnd(name); return true; } return false; }, /** Prints a stack-trace. @returns {Boolean} true if reporter.trace exists, false otherwise */ trace: function() { var reporter = this.get('reporter'); if (this.get('exists') && typeof(reporter.trace) === "function") { reporter.trace(); return true; } return false; }, /** Log a warning to the console. Logs the warning using {@link SC.Logger.log} if reporter.warning does not exist and {@link SC.Logger.fallBackOnLog} is true. @param {String|Array|Function|Object} @returns {Boolean} true if logged to reporter, false if not */ warn: function() { var reporter = this.get('reporter'); if (this.get('exists') && typeof(reporter.warn) === "function") { reporter.warn.apply(reporter, arguments); return true; } else if (this.fallBackOnLog) { var a = this._argumentsToArray(arguments); if (typeof(a.unshift) === "function") a.unshift(SC.LOGGER_LOG_WARN); return this.log.apply(this, a); } return false; }, // .......................................................... // INTERNAL SUPPORT // /** @private The arguments function property doesn't support Array#unshift. This helper copies the elements of arguments to a blank array. @param {Array} arguments The arguments property of a function @returns {Array} An array containing the elements of arguments parameter */ _argumentsToArray: function(arguments) { if (!arguments) return []; var a = []; for (var i = 0; i < arguments.length; i++) { a[i] = arguments[i]; } return a; }, /** @private Formats the arguments array of a function by creating a string with SC.LOGGER_LOG_DELIMITER between the elements. @returns {String} A string of formatted arguments */ _argumentsToString: function() { var s = ""; for (var i = 0; i