-
-
Save iamso/24f81f9d0bf63d39c42a85983ef20b19 to your computer and use it in GitHub Desktop.
hnl.mobileConsole.js - extends JavaScript's console to display a visual console inside the webpage. Very usefull for debugging JS on mobile devices with no real console. Info and demo: http://www.hnldesign.nl/work/code/mobileconsole-javascript-console-for-mobile-devices/
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| /*! | |
| * hnl.mobileConsole - javascript mobile console - v0.4.2 - 18/5/2016 | |
| * Adds html console to webpage. Especially useful for debugging JS on mobile devices. | |
| * Supports .log, .warn, .error and .trace | |
| * Based on code by jakub fiala (https://gist.github.com/jakubfiala/8fe3461ab6508f46003d) | |
| * Licensed under the MIT license | |
| * | |
| * Original author: @hnldesign | |
| * Further changes, comments: @hnldesign | |
| * Copyright (c) 2014-2016 HN Leussink | |
| * Dual licensed under the MIT and GPL licenses. | |
| * | |
| * Demo: http://code.hnldesign.nl/demo/hnl.MobileConsole.html | |
| */ | |
| //check if the browser has a console. If not (older Firefox without Firebug): stop. | |
| if (window.console !== 'undefined') { | |
| var console = window.console; | |
| /** | |
| * DocReady - Replacement for jQuery's $(document).ready() event | |
| */ | |
| if (typeof 'docReady' !== 'function') { | |
| (function (funcName, baseObj) { | |
| // The public function name defaults to window.docReady | |
| // but you can pass in your own object and own function name and those will be used | |
| // if you want to put them in a different namespace | |
| funcName = funcName || 'docReady'; | |
| baseObj = baseObj || window; | |
| var readyList = []; | |
| var readyFired = false; | |
| var readyEventHandlersInstalled = false; | |
| // call this when the document is ready | |
| // this function protects itself against being called more than once | |
| function ready() { | |
| if (!readyFired) { | |
| // this must be set to true before we start calling callbacks | |
| readyFired = true; | |
| for (var i = 0; i < readyList.length; i++) { | |
| // if a callback here happens to add new ready handlers, | |
| // the docReady() function will see that it already fired | |
| // and will schedule the callback to run right after | |
| // this event loop finishes so all handlers will still execute | |
| // in order and no new ones will be added to the readyList | |
| // while we are processing the list | |
| readyList[i].fn.call(window, readyList[i].ctx); | |
| } | |
| // allow any closures held by these functions to free | |
| readyList = []; | |
| } | |
| } | |
| function readyStateChange() { | |
| if (document.readyState === 'complete') { | |
| ready(); | |
| } | |
| } | |
| // This is the one public interface | |
| // docReady(fn, context); | |
| // the context argument is optional - if present, it will be passed | |
| // as an argument to the callback | |
| baseObj[funcName] = function (callback, context) { | |
| // if ready has already fired, then just schedule the callback | |
| // to fire asynchronously, but right away | |
| if (readyFired) { | |
| setTimeout(function () {callback(context);}, 1); | |
| return; | |
| } else { | |
| // add the function and context to the list | |
| readyList.push({fn: callback, ctx: context}); | |
| } | |
| // if document already ready to go, schedule the ready function to run | |
| if (document.readyState === 'complete') { | |
| setTimeout(ready, 1); | |
| } else if (!readyEventHandlersInstalled) { | |
| // otherwise if we don't have event handlers installed, install them | |
| if (document.addEventListener) { | |
| // first choice is DOMContentLoaded event | |
| document.addEventListener('DOMContentLoaded', ready, false); | |
| // backup is window load event | |
| window.addEventListener('load', ready, false); | |
| } else { | |
| // must be IE | |
| document.attachEvent('onreadystatechange', readyStateChange); | |
| window.attachEvent('onload', ready); | |
| } | |
| readyEventHandlersInstalled = true; | |
| } | |
| }; | |
| })('docReady', window); | |
| } | |
| var MobileConsole = { | |
| Version: '0.4', | |
| consoleDiv: undefined, | |
| console_old : { | |
| log : console.log.bind(console), | |
| warn : console.warn.bind(console), | |
| error : console.error.bind(console), | |
| trace : console.trace.bind(console) | |
| }, | |
| Options: { | |
| AutoRun: window.location.search.indexOf('debug') !== -1, | |
| Position: 'bottom', //or 'top' NOT IMPLMENTED | |
| Type: 'docked', //or 'window' NOT IMPLMENTED, | |
| AnimParams: 'all 200ms ease', | |
| Style: 'white' //or 'black' | |
| }, | |
| BrowserInfo: { | |
| browserChrome: /chrome/.test(navigator.userAgent.toLowerCase()), | |
| ffox: /firefox/.test(navigator.userAgent.toLowerCase()) && !/chrome/.test(navigator.userAgent.toLowerCase()), | |
| safari: /safari/.test(navigator.userAgent.toLowerCase()) && !/chrome/.test(navigator.userAgent.toLowerCase()), | |
| trident: /trident/.test(navigator.userAgent.toLowerCase()), | |
| evtLstn: typeof window.addEventListener === 'function', | |
| supportsElsByCln: document.getElementsByClassName !== undefined, | |
| isCrap: document.querySelectorAll === undefined | |
| }, | |
| elems: {}, | |
| //constructor | |
| MobileConsole: function () { | |
| 'use strict'; | |
| var th = this; | |
| var mc = MobileConsole; | |
| /** | |
| * Toggles the console height | |
| * @returns {toggle} | |
| */ | |
| this.toggle = function () { | |
| var elem = mc.elems.consoleDiv; | |
| mc.setCSS(elem, { | |
| height: (elem.toggled) ? '102px' : '262px', | |
| opacity: (elem.toggled) ? '0.8' : 1 | |
| }); | |
| this.innerHTML = (elem.toggled) ? '+' : '−'; | |
| elem.toggled = !elem.toggled; | |
| return elem; | |
| }; | |
| /** | |
| * Scrolls the console to the bottom | |
| * @returns {toggleScroll} | |
| */ | |
| this.toggleScroll = function (e) { | |
| var elem = mc.elems.scrollContainer; | |
| elem.scrollTop = elem.scrollHeight; | |
| return elem; | |
| }; | |
| /** | |
| * Clear the console | |
| * @returns {toggleScroll} | |
| */ | |
| this.empty = function (e) { | |
| console.clear(); | |
| mc.elems.consoleBodyTable.innerHTML = ''; | |
| var methods = ['log', 'warn', 'error', 'trace']; | |
| for (var i = 0; i < methods.length; i++) { | |
| mc.elems.lines[methods[i]] = []; | |
| } | |
| }; | |
| this.toggleLogtype = function (method, button) { | |
| button.toggled = !button.toggled; | |
| mc.setCSS(button, { | |
| opacity: (button.toggled) ? '0.5' : '' | |
| }); | |
| var elems = mc.elems.lines[method]; | |
| var key, x = 0; | |
| for (key in elems) { | |
| if (elems.hasOwnProperty(key)) { | |
| mc.setCSS(elems[x], { display: (button.toggled) ? 'none' : '' }); | |
| } | |
| x++; | |
| } | |
| mc.elems.scrollContainer.scrollTop = mc.elems.scrollContainer.scrollHeight; | |
| return button; | |
| }; | |
| /** | |
| * theConsole - The beating heart. This is the 'new console' that takes and renders everything thrown at it. | |
| * @param method | |
| * @param message | |
| * @param stackTrace | |
| * @param stackTraceOrig | |
| */ | |
| this.theConsole = function (method, message, stackTrace, stackTraceOrig) { | |
| if (message !== '' && message !== undefined) { | |
| var color = (method === 'error') ? ((mc.Options.Style === 'white') ? '#FF0000' : '#ff4848') : ((method === 'warn') ? '#CE8724' : ((mc.Options.Style === 'white') ? '#000000' : '#EEEEEE')); | |
| if (this.prevMsg !== message || method === 'trace') { | |
| //message is not a repeat of the previous | |
| var cleanUrl, url; | |
| if(stackTrace !== undefined) { | |
| if (stackTrace.url !== undefined || stackTrace.line !== undefined || stackTrace.column !== undefined) { | |
| //this is window.onerror | |
| url = stackTrace.url + ':' + stackTrace.line + ':' + stackTrace.column; | |
| cleanUrl = stackTrace.url; | |
| } else { | |
| var origin = (stackTrace.objTrace[1] === 'global') ? stackTrace.objTrace[0] : stackTrace.objTrace[1]; | |
| if (mc.BrowserInfo.safari || mc.BrowserInfo.ffox) { | |
| if (/@/i.test(origin)) { | |
| url = origin.split('@')[1]; | |
| } else { | |
| url = origin; | |
| } | |
| } else { | |
| url = origin.substring(origin.indexOf('(') + 1, origin.indexOf(')')); | |
| } | |
| cleanUrl = url.split(':')[0] + ':' + url.split(':')[1]; | |
| } | |
| } | |
| //construct link to caller | |
| var linkContainer = mc.elems.tdRight.cloneNode(false); | |
| if (url !== 'anonymous function' && url !== undefined && url !== '' && /http/i.test(cleanUrl)) { | |
| var newLink = mc.elems.link.cloneNode(false); | |
| newLink.innerHTML = url; | |
| newLink.href = cleanUrl; | |
| newLink.setAttribute('target', '_blank'); | |
| //put link in container | |
| linkContainer.appendChild(newLink); | |
| } else { | |
| linkContainer.innerHTML = (String(url) === '' ? '(Undefined)' : String(url)); | |
| } | |
| //construct message | |
| var messageContainer = mc.elems.tdLeft.cloneNode(false); | |
| if (method === 'trace' && stackTrace !== undefined) { | |
| messageContainer.innerHTML = stackTrace.htmlTrace; | |
| } else { | |
| messageContainer.innerHTML = message; | |
| } | |
| mc.setCSS(messageContainer, {color: color}); | |
| //put message in container | |
| var lineContainer = mc.elems.tr.cloneNode(false); | |
| if (mc.Options.Style === 'white') { mc.setCSS(lineContainer, {backgroundColor: (method ==='warn') ? '#FFF6E0' : (method === 'error' ? '#ffe5e5' : '')}); } | |
| lineContainer.appendChild(messageContainer); | |
| lineContainer.appendChild(linkContainer); | |
| //store the lines in the object corresponding to the method used | |
| mc.elems.lines[method].push(lineContainer); | |
| //add it to the constructed console | |
| mc.setCSS(lineContainer, { | |
| display: (mc.elems.buttons[method].toggled ? 'none' : '') | |
| }); | |
| mc.elems.consoleBodyTable.appendChild(lineContainer); | |
| mc.consoleDiv.prototype.toggleScroll(); | |
| this.prevMsg = message; | |
| this.prevMethod = method; | |
| this.c = 1; | |
| } else if (this.prevMethod === method) { | |
| //message is a repeat of the previous, AND the method is the same. Add a count-dot / update the count-dot | |
| this.c = this.c + 1; | |
| if (mc.elems.consoleBodyTable.lastChild.countDot === undefined) { | |
| var countDot = mc.setCSS(mc.elems.dot.cloneNode(false), { | |
| backgroundColor: color | |
| }); | |
| var span = document.createElement('span'); | |
| span.innerHTML = this.prevMsg; | |
| mc.elems.consoleBodyTable.lastChild.firstChild.innerHTML = ''; | |
| mc.elems.consoleBodyTable.lastChild.firstChild.appendChild(countDot); | |
| mc.elems.consoleBodyTable.lastChild.firstChild.appendChild(span); | |
| mc.elems.consoleBodyTable.lastChild.countDot = countDot; | |
| } | |
| mc.elems.consoleBodyTable.lastChild.countDot.innerHTML = this.c.toString(); | |
| } else { | |
| console.error('not logging. Why?'); | |
| } | |
| } | |
| }; | |
| //setup binds | |
| if (mc.BrowserInfo.evtLstn) { | |
| mc.elems.consoleDiv.addEventListener('transitionend', th.toggleScroll, false); | |
| mc.elems.consoleDiv.addEventListener('webkitTransitionEnd', th.toggleScroll, false); | |
| mc.elems.consoleDiv.addEventListener('oTransitionEnd', th.toggleScroll, false); | |
| mc.elems.consoleDiv.addEventListener('MSTransitionEnd', th.toggleScroll, false); | |
| } else { | |
| mc.elems.consoleDiv.attachEvent('transitionend', th.toggleScroll); | |
| mc.elems.consoleDiv.attachEvent('webkitTransitionEnd', th.toggleScroll); | |
| mc.elems.consoleDiv.attachEvent('oTransitionEnd', th.toggleScroll); | |
| mc.elems.consoleDiv.attachEvent('MSTransitionEnd', th.toggleScroll); | |
| } | |
| //button binds | |
| if (mc.BrowserInfo.evtLstn) { | |
| mc.elems.buttons.toggler.addEventListener('click', th.toggle, false); | |
| mc.elems.buttons.log.addEventListener('click', function () { th.toggleLogtype('log', this); this.blur(); }, false); | |
| mc.elems.buttons.warn.addEventListener('click', function () { th.toggleLogtype('warn', this); this.blur(); }, false); | |
| mc.elems.buttons.error.addEventListener('click', function () { th.toggleLogtype('error', this); this.blur(); }, false); | |
| mc.elems.buttons.trace.addEventListener('click', function () { th.toggleLogtype('trace', this); this.blur(); }, false); | |
| mc.elems.buttons.clear.addEventListener('click', th.empty, false); | |
| } else { | |
| mc.elems.buttons.toggler.attachEvent('onclick', th.toggle); | |
| mc.elems.buttons.log.attachEvent('onclick', function () { th.toggleLogtype('log', this); this.blur(); }); | |
| mc.elems.buttons.warn.attachEvent('onclick', function () { th.toggleLogtype('warn', this); this.blur(); }); | |
| mc.elems.buttons.error.attachEvent('onclick', function () { th.toggleLogtype('error', this); this.blur(); }); | |
| mc.elems.buttons.trace.attachEvent('onclick', function () { th.toggleLogtype('trace', this); this.blur(); }); | |
| mc.elems.buttons.clear.attachEvent('onclick', th.empty); | |
| } | |
| //logic starts here | |
| //bind to window.onerror and make it trigger a console.error | |
| window.onerror = function(message, url, lineNumber, column) { | |
| //save error and send to server for example. | |
| var stackTrace = { | |
| url : url, | |
| line : lineNumber, | |
| column : column | |
| }; | |
| console.error(message, stackTrace); | |
| }; | |
| //Intercept all original console methods including trace. Register the event type as a line type. | |
| var methods = ['log', 'warn', 'error', 'trace']; | |
| for (var i = 0; i < methods.length; i++) { | |
| mc.elems.lines[methods[i]] = []; | |
| mc.interceptConsole(methods[i]); | |
| } | |
| }, | |
| /** | |
| * set CSS for passed element | |
| * @param el | |
| * @param css | |
| * @returns {*} | |
| */ | |
| setCSS: function (el, css) { | |
| 'use strict'; | |
| var i; | |
| for (i in css) { | |
| el.style[i] = css[i]; | |
| } | |
| return el; | |
| }, | |
| /** | |
| * Linkify passed string content | |
| * @returns {XML|string} | |
| */ | |
| linkify : function (str) { | |
| 'use strict'; | |
| // http://, https://, ftp:// | |
| var urlPattern = /\b(?:https?|ftp):\/\/[a-z0-9-+&@#\/%?=~_|!:,.;]*[a-z0-9-+&@#\/%=~_|]/gim; | |
| // www. sans http:// or https:// | |
| var pseudoUrlPattern = /(^|[^\/])(www\.[\S]+(\b|$))/gim; | |
| // Email addresses | |
| var emailAddressPattern = /[\w.]+@[a-zA-Z_-]+?(?:\.[a-zA-Z]{2,6})+/gim; | |
| return str | |
| .replace(urlPattern, '<span class="link" data-href="$&">$&</span>') | |
| .replace(pseudoUrlPattern, '$1<span class="link" data-href="http://$2">$2</span>') | |
| .replace(emailAddressPattern, '<span class="link" data-href="mailto:$&">$&</span>'); | |
| }, | |
| /** | |
| * strSplice - a JS-version of PHP's string_splice | |
| * taken from http://stackoverflow.com/a/21350614 | |
| * @param str | |
| * @param index | |
| * @param endIndex | |
| * @returns {*} | |
| */ | |
| strSplice : function(str, index, endIndex) { | |
| 'use strict'; | |
| return str.slice(0, index) + str.slice(endIndex); | |
| }, | |
| /** | |
| * Element builder. Returns element of type, with className and css applied | |
| * @param type | |
| * @param className | |
| * @param css | |
| * @returns {HTMLElement} | |
| */ | |
| createElem: function (type, className, css) { | |
| 'use strict'; | |
| var th = this; | |
| var element = document.createElement(type); | |
| th.setCSS(element, css); | |
| element.className = className; | |
| return element; | |
| }, | |
| //Console constructor | |
| buildConsole: function () { | |
| 'use strict'; | |
| var th = this; | |
| var white = (th.Options.Style === 'white'), top = (th.Options.Position === 'top'), window = (th.Options.Type === 'window'); | |
| th.elems.consoleDiv = th.createElem('DIV', 'hnlMobileConsole', { | |
| position: 'fixed', | |
| resize: 'none', | |
| fontSize: '12px', | |
| lineHeight: '12px', | |
| bottom: top ? 'auto' : 0, | |
| top: top ? (window ? '32px' : 0) : 'auto', | |
| right: window ? '70px' : 0, | |
| backgroundColor: white ? '#ffffff' : '#333333', | |
| width: window ? '70%' : '100%', | |
| height: '102px', | |
| zIndex: 10000, | |
| padding: 0, | |
| margin: 0, | |
| opacity: '0.8', | |
| borderTop: '1px solid #808080', | |
| '-webkit-transition': th.Options.AnimParams, | |
| '-moz-transition': th.Options.AnimParams, | |
| '-o-transition': th.Options.AnimParams, | |
| 'transition': th.Options.AnimParams | |
| }); | |
| th.elems.consoleBodyTable = th.createElem('DIV', 'hnlMobileConsole_table', { | |
| fontFamily: 'monospace', | |
| maxWidth: '100%', | |
| display: 'table', | |
| tableLayout: 'fixed', | |
| width: '100%' | |
| }); | |
| th.elems.tr = th.createElem('TR', 'hnlMobileConsole_table_row', {}); | |
| th.elems.tdLeft = th.createElem('TD', 'hnlMobileConsole_table_row_data', { | |
| textAlign: 'left', | |
| padding: '2px 4px', | |
| borderBottom: '1px solid ' + (white ? '#EEEEEE' : '#777777') | |
| }); | |
| th.elems.tdRight = th.createElem('TD', 'hnlMobileConsole_table_row_data', { | |
| textAlign: 'left', | |
| padding: '2px 4px', | |
| borderBottom: '1px solid ' + (white ? '#EEEEEE' : '#777777'), | |
| whiteSpace: 'nowrap', | |
| overflow: 'hidden', | |
| direction: 'rtl' | |
| }); | |
| th.elems.link = th.createElem('A', 'hnlMobileConsole_link', { | |
| color: (white ? '#0000EE' : '#AAC1D2') | |
| }); | |
| th.elems.dot = th.createElem('DIV', 'hnlMobileConsole_table_row_data_dot', { | |
| display: 'inline-block', | |
| borderRadius: '5px', | |
| color: white ? '#ffffff' : '#333333', | |
| fontWeight: white ? '' : 'bold', | |
| minWidth: '12px', | |
| padding: '0 1px', | |
| textAlign: 'center', | |
| marginRight: '5px' | |
| }); | |
| th.elems.topBar = th.createElem('DIV', 'hnlMobileConsole_topbar', { | |
| position: 'absolute', | |
| left: 0, | |
| right: 0, | |
| display: 'block', | |
| padding: '0 2px', | |
| overflow: 'hidden', | |
| color: white ? '#333333' : '#FFFFFF', | |
| backgroundColor: white ? '#DDDDDD' : '#222222', | |
| borderBottom: '1px solid ' + (white ? '#AAA' : '#111111'), | |
| webkitOverflowScrolling: 'touch' | |
| }); | |
| th.elems.scrollContainer = th.createElem('DIV', 'hnlMobileConsole_scrolltcontainer', { | |
| position: 'relative', | |
| display: 'block', | |
| height: '100%', | |
| overflow: 'hidden', | |
| overflowY: 'scroll', | |
| webkitOverflowScrolling: 'touch' | |
| }); | |
| th.elems.button = th.createElem('BUTTON', 'hnlMobileConsole_button', { | |
| display: 'inline-block', | |
| fontSize: '14px', | |
| padding: '6px 8px', | |
| textAlign: 'center', | |
| marginRight: '5px', | |
| border: '0 none', | |
| backgroundColor: white ? '#DDDDDD' : '#111111' | |
| }); | |
| //set up lines object | |
| th.elems.lines = []; | |
| //set up buttons object | |
| th.elems.buttons = []; | |
| th.elems.buttons.toggler = th.elems.button.cloneNode(false); | |
| th.elems.buttons.toggler.innerHTML = '+'; | |
| th.elems.topBar.appendChild(th.elems.buttons.toggler); | |
| th.elems.buttons.log = th.elems.button.cloneNode(false); | |
| th.elems.buttons.log.innerHTML = 'Log'; | |
| th.elems.topBar.appendChild(th.elems.buttons.log); | |
| th.elems.buttons.warn = th.elems.button.cloneNode(false); | |
| th.elems.buttons.warn.innerHTML = 'Warn'; | |
| th.elems.topBar.appendChild(th.elems.buttons.warn); | |
| th.elems.buttons.error = th.elems.button.cloneNode(false); | |
| th.elems.buttons.error.innerHTML = 'Error'; | |
| th.elems.topBar.appendChild(th.elems.buttons.error); | |
| th.elems.buttons.trace = th.elems.button.cloneNode(false); | |
| th.elems.buttons.trace.innerHTML = 'Trace'; | |
| th.elems.topBar.appendChild(th.elems.buttons.trace); | |
| th.elems.buttons.clear = th.elems.button.cloneNode(false); | |
| th.elems.buttons.clear.innerHTML = 'Clear'; | |
| th.elems.topBar.appendChild(th.elems.buttons.clear); | |
| //construct elements | |
| th.elems.consoleDiv.appendChild(th.elems.topBar); | |
| th.elems.scrollContainer.appendChild(th.elems.consoleBodyTable); | |
| th.elems.consoleDiv.appendChild(th.elems.scrollContainer); | |
| return th.elems.consoleDiv; | |
| }, | |
| /** | |
| * Attaches console to the document body | |
| */ | |
| attachConsole: function () { | |
| 'use strict'; | |
| document.body.appendChild(MobileConsole.elems.consoleDiv); | |
| MobileConsole.setCSS(MobileConsole.elems.topBar, { | |
| top: -Math.abs(MobileConsole.elems.topBar.offsetHeight) + 'px' | |
| }); | |
| var existingPadding = isNaN(parseInt(document.body.style.paddingBottom, 10)) ? 0 : parseInt(document.body.style.paddingBottom, 10); | |
| MobileConsole.setCSS(document.body, { | |
| paddingBottom: existingPadding + Math.abs(MobileConsole.elems.consoleDiv.offsetHeight + MobileConsole.elems.topBar.offsetHeight) + 'px' | |
| }); | |
| MobileConsole.elems.scrollContainer.scrollTop = MobileConsole.elems.scrollContainer.scrollHeight; | |
| }, | |
| /** | |
| * Formats a stacktrace to human readable HTML | |
| * @param stackTrace | |
| * @returns {{}} | |
| */ | |
| stackTraceFormat: function (stackTrace) { | |
| 'use strict'; | |
| var th = this; | |
| var returnObj = {}; | |
| if (th.BrowserInfo.browserChrome || th.BrowserInfo.trident) { | |
| //we first format the string a bit | |
| stackTrace = stackTrace.replace("Error", "") | |
| .replace(/\r?\n|\r/g, '') | |
| .replace(' ', '') | |
| .replace(/</gm, '<') | |
| .replace(/>/gm, '>'); | |
| //then look for the first part of the trace (which is this method, we don't want that) | |
| for (var i = 0; i < stackTrace.length; i++) { | |
| if (stackTrace[i] == 'a' && stackTrace[i + 1] == 't' && stackTrace[i + 2] == ' ') { | |
| var startIndex = i; | |
| for (var j = startIndex + 1; j < stackTrace.length; j++) { | |
| if (stackTrace[j] == 'a' && stackTrace[j + 1] == 't' && stackTrace[j + 2] == ' ') { | |
| var endIndex = j; | |
| //found beginning and end of this part, remove it | |
| stackTrace = th.strSplice(stackTrace, i, j); | |
| break; | |
| } | |
| } | |
| break; | |
| } | |
| } | |
| //then replace all 'at's with list elements, and convert to link spans | |
| returnObj.objTrace = stackTrace.split(/at /gm); | |
| stackTrace = th.linkify(stackTrace.replace(/at /gm, '</span><span>')); | |
| } | |
| else if (th.BrowserInfo.safari || th.BrowserInfo.ffox) { | |
| //this seems to kind of work for both | |
| //turn spaces into list elmt boundaries, linkify, and replace at signs with html entities, just for the lulz | |
| returnObj.objTrace = stackTrace.split(/\s/gm); | |
| stackTrace = th.linkify('<li>' + stackTrace.replace(/\s/gm, '</li><li>')).replace(/\@/gm, '@'); | |
| //again, look for the first part of the trace (which is this method, we don't want that) | |
| for (var i = 0; i < stackTrace.length; i++) { | |
| if (stackTrace[i] == '<' && stackTrace[i + 1] == '/' && stackTrace[i + 2] == 'l') { | |
| var index = i; | |
| //found end of this part, remove it | |
| stackTrace = th.strSplice(stackTrace, 0, index); | |
| break; | |
| } | |
| } | |
| } | |
| returnObj.htmlTrace = stackTrace; | |
| return returnObj; | |
| }, | |
| //this is where everything happens | |
| interceptConsole: function (method) { | |
| var original = console[method]; | |
| console[method] = function (message, stackTrace) { | |
| // alt: var message = Array.prototype.slice.apply(arguments).join(' '); | |
| //create an Error and get its stack trace and format it | |
| var stackTraceOrig; | |
| try { throw new Error(); } catch(e) { stackTraceOrig = e.stack; } | |
| if (stackTrace === undefined && stackTraceOrig) { | |
| //if no stacktrace defined (window.onerror defines one), use the generated one | |
| stackTrace = MobileConsole.stackTraceFormat(stackTraceOrig); | |
| } | |
| //Handle the new console logging | |
| MobileConsole.consoleDiv.prototype.theConsole(method, message, stackTrace, stackTraceOrig); | |
| //========================================================== | |
| //make sure we still call the original method | |
| original.call(console, message); | |
| }; | |
| }, | |
| init: function (autorun) { | |
| 'use strict'; | |
| var th = this; | |
| //don't go when we have already built it | |
| if (th.consoleDiv !== undefined) { return; } | |
| //build the console DIV | |
| th.consoleDiv = th.buildConsole(); | |
| //attach prototype to the console (and take over console events) | |
| th.consoleDiv.prototype = new th.MobileConsole(); | |
| //wait for load if autorun is enabled and attach console to body | |
| if (autorun && typeof docReady === 'function') { | |
| docReady(th.attachConsole); | |
| } else { | |
| //if no autorun: attach the built console | |
| th.attachConsole(); | |
| } | |
| console.log('MobileConsole initialized. v' + MobileConsole.Version); | |
| console.log('Running on ' + navigator.userAgent.toLowerCase()); | |
| //return the built console | |
| return th.consoleDiv; | |
| } | |
| }; | |
| if (MobileConsole.Options.AutoRun) { | |
| MobileConsole.init(true); | |
| } else { | |
| console.warn('MobileConsole loaded but not displaying, AutoRun disabled. To initialize, run MobileConsole.init();'); | |
| } | |
| } else { | |
| //there is no console. Throw error. | |
| throw Error('No window.console available, MobileConsole can not run on this ' + ((document.querySelectorAll === undefined) ? '(old)' : '') + ' browser (' + navigator.userAgent.toLowerCase() + ').'); | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment