Skip to content

Instantly share code, notes, and snippets.

@iamso
Forked from c-kick/hnl.mobileConsole.js
Created March 2, 2017 16:22
Show Gist options
  • Select an option

  • Save iamso/24f81f9d0bf63d39c42a85983ef20b19 to your computer and use it in GitHub Desktop.

Select an option

Save iamso/24f81f9d0bf63d39c42a85983ef20b19 to your computer and use it in GitHub Desktop.

Revisions

  1. @c-kick c-kick revised this gist Oct 26, 2016. 1 changed file with 6 additions and 6 deletions.
    12 changes: 6 additions & 6 deletions hnl.mobileConsole.js
    Original file line number Diff line number Diff line change
    @@ -1,5 +1,5 @@
    /*!
    * hnl.mobileConsole - javascript mobile console - v1.2.5 - 21/10/2016
    * hnl.mobileConsole - javascript mobile console - v1.2.6 - 26/10/2016
    * Adds html console to webpage. Especially useful for debugging JS on mobile devices.
    * Supports 'log', 'trace', 'info', 'warn', 'error', 'group', 'groupEnd', 'table', 'assert', 'clear'
    * Inspired by code by jakub fiala (https://gist.github.com/jakubfiala/8fe3461ab6508f46003d)
    @@ -67,7 +67,7 @@ var mobileConsole = (function () {
    //options and other variable containers
    var options = {
    overrideAutorun: false,
    version : '1.2.5',
    version : '1.2.6',
    baseClass : 'mobileConsole_',
    animParams: 'all 200ms ease',
    browserinfo: {
    @@ -949,12 +949,12 @@ var mobileConsole = (function () {
    case 'dir':
    case 'table':
    //left side
    if (method === 'trace') {
    message = 'console.trace()';
    msgContainer.innerHTML = arrows + message;
    } else if (method === 'table') {
    if (method === 'table' || typeof arguments[1].newMessage === 'object') {
    detailTable = objectToTable(elements.stackTraceTable.cloneNode(false), arguments[1].newMessage);
    msgContainer.innerHTML = elements.arrowRight + ' ' + message;
    } else if (method === 'trace') {
    message = 'console.trace()';
    msgContainer.innerHTML = arrows + message;
    } else {
    msgContainer.innerHTML = arrows + message;
    }
  2. @c-kick c-kick revised this gist Oct 26, 2016. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion hnl.mobileConsole.js
    Original file line number Diff line number Diff line change
    @@ -1012,7 +1012,7 @@ var mobileConsole = (function () {

    //populate right side
    if (stackTrace && stackTrace[stackTrace.length - 1] !== undefined) {
    rightContainer.appendChild(setCSS(getLink(stackTrace[stackTrace.length - 1].url, stackTrace[stackTrace.length - 1].linkText), {color: '#808080'}));
    rightContainer.appendChild(setCSS(getLink(stackTrace[0].url, stackTrace[0].linkText), {color: '#808080'}));
    }

    //add to line
  3. @c-kick c-kick revised this gist Oct 25, 2016. 1 changed file with 6 additions and 6 deletions.
    12 changes: 6 additions & 6 deletions hnl.mobileConsole.js
    Original file line number Diff line number Diff line change
    @@ -1,5 +1,5 @@
    /*!
    * hnl.mobileConsole - javascript mobile console - v1.2.4 - 21/10/2016
    * hnl.mobileConsole - javascript mobile console - v1.2.5 - 21/10/2016
    * Adds html console to webpage. Especially useful for debugging JS on mobile devices.
    * Supports 'log', 'trace', 'info', 'warn', 'error', 'group', 'groupEnd', 'table', 'assert', 'clear'
    * Inspired by code by jakub fiala (https://gist.github.com/jakubfiala/8fe3461ab6508f46003d)
    @@ -67,7 +67,7 @@ var mobileConsole = (function () {
    //options and other variable containers
    var options = {
    overrideAutorun: false,
    version : '1.2.4',
    version : '1.2.5',
    baseClass : 'mobileConsole_',
    animParams: 'all 200ms ease',
    browserinfo: {
    @@ -78,8 +78,8 @@ var mobileConsole = (function () {
    evtLstn: typeof window.addEventListener === 'function',
    isCrap: document.querySelectorAll === undefined
    },
    methods : ['log', 'trace', 'info', 'warn', 'error', 'group', 'groupEnd', 'table', 'assert', 'time', 'timeEnd', 'clear'],
    hideButtons : ['group', 'groupEnd', 'table', 'assert', 'time', 'timeEnd'],
    methods : ['log', 'trace', 'info', 'warn', 'error', 'group', 'groupCollapsed', 'groupEnd', 'table', 'assert', 'time', 'timeEnd', 'clear'],
    hideButtons : ['group', 'groupCollapsed', 'groupEnd', 'table', 'assert', 'time', 'timeEnd'],
    ratio: 0.4,
    paddingLeft: 0,
    groupDepth: 0
    @@ -882,7 +882,7 @@ var mobileConsole = (function () {
    }
    }
    function isRepeat(message, method) {
    return (history.output.prevMsg === message && history.output.prevMethod === method) && (typeof message !== 'object') && (method !== 'trace') && (method !== 'group') && (method !== 'groupEnd');
    return (history.output.prevMsg === message && history.output.prevMethod === method) && (typeof message !== 'object') && (method !== 'trace') && (method !== 'group') && (method !== 'groupCollapsed') && (method !== 'groupEnd');
    }
    function newConsole() {
    try {
    @@ -963,7 +963,7 @@ var mobileConsole = (function () {
    case 'group':
    case 'groupCollapsed':
    case 'groupEnd':
    if (method === 'group') {
    if (method !== 'groupEnd') {
    options.groupDepth = options.groupDepth + 1;
    msgContainer.innerHTML = '<strong>' + message + '</strong>';
    msgContainer.setAttribute('toggles', 'group_' + options.groupDepth);
  4. @c-kick c-kick revised this gist Oct 21, 2016. 1 changed file with 0 additions and 1 deletion.
    1 change: 0 additions & 1 deletion hnl.mobileConsole.js
    Original file line number Diff line number Diff line change
    @@ -1011,7 +1011,6 @@ var mobileConsole = (function () {
    }

    //populate right side
    originalConsole.log(stackTrace[stackTrace.length - 1].linkText);
    if (stackTrace && stackTrace[stackTrace.length - 1] !== undefined) {
    rightContainer.appendChild(setCSS(getLink(stackTrace[stackTrace.length - 1].url, stackTrace[stackTrace.length - 1].linkText), {color: '#808080'}));
    }
  5. @c-kick c-kick revised this gist Oct 21, 2016. 1 changed file with 2 additions and 1 deletion.
    3 changes: 2 additions & 1 deletion hnl.mobileConsole.js
    Original file line number Diff line number Diff line change
    @@ -798,7 +798,7 @@ var mobileConsole = (function () {
    lineAndColumn = thisLine.match(/(?::)(\d+)(?::)(\d+)/);
    url = urlFromString(thisLine).replace(lineAndColumn[0], '').split('#')[0] || '';
    caller = htmlToString(thisLine.replace(urlFromString(thisLine), '').replace(separator, '').replace('at ', '').trim());
    if (caller === '' || caller === lineAndColumn[0]) { caller = '(anonymous)'; }
    if (caller === '' || caller === lineAndColumn[0]) { continue; }
    if (url[url.length - 1] === '/') {
    txt = '(index)';
    } else {
    @@ -1011,6 +1011,7 @@ var mobileConsole = (function () {
    }

    //populate right side
    originalConsole.log(stackTrace[stackTrace.length - 1].linkText);
    if (stackTrace && stackTrace[stackTrace.length - 1] !== undefined) {
    rightContainer.appendChild(setCSS(getLink(stackTrace[stackTrace.length - 1].url, stackTrace[stackTrace.length - 1].linkText), {color: '#808080'}));
    }
  6. @c-kick c-kick revised this gist Oct 21, 2016. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion hnl.mobileConsole.js
    Original file line number Diff line number Diff line change
    @@ -798,7 +798,7 @@ var mobileConsole = (function () {
    lineAndColumn = thisLine.match(/(?::)(\d+)(?::)(\d+)/);
    url = urlFromString(thisLine).replace(lineAndColumn[0], '').split('#')[0] || '';
    caller = htmlToString(thisLine.replace(urlFromString(thisLine), '').replace(separator, '').replace('at ', '').trim());
    if (caller === '' || caller === lineAndColumn[0]) { caller = '(anonymous)' }
    if (caller === '' || caller === lineAndColumn[0]) { caller = '(anonymous)'; }
    if (url[url.length - 1] === '/') {
    txt = '(index)';
    } else {
  7. @c-kick c-kick revised this gist Oct 21, 2016. 1 changed file with 2 additions and 2 deletions.
    4 changes: 2 additions & 2 deletions hnl.mobileConsole.js
    Original file line number Diff line number Diff line change
    @@ -66,8 +66,8 @@ var mobileConsole = (function () {

    //options and other variable containers
    var options = {
    overrideAutorun: true,
    version : '1.2.1',
    overrideAutorun: false,
    version : '1.2.4',
    baseClass : 'mobileConsole_',
    animParams: 'all 200ms ease',
    browserinfo: {
  8. @c-kick c-kick revised this gist Oct 21, 2016. 1 changed file with 1 addition and 2 deletions.
    3 changes: 1 addition & 2 deletions hnl.mobileConsole.js
    Original file line number Diff line number Diff line change
    @@ -1,5 +1,5 @@
    /*!
    * hnl.mobileConsole - javascript mobile console - v1.2.3 - 21/10/2016
    * hnl.mobileConsole - javascript mobile console - v1.2.4 - 21/10/2016
    * Adds html console to webpage. Especially useful for debugging JS on mobile devices.
    * Supports 'log', 'trace', 'info', 'warn', 'error', 'group', 'groupEnd', 'table', 'assert', 'clear'
    * Inspired by code by jakub fiala (https://gist.github.com/jakubfiala/8fe3461ab6508f46003d)
    @@ -1011,7 +1011,6 @@ var mobileConsole = (function () {
    }

    //populate right side
    originalConsole.warn(stackTrace);
    if (stackTrace && stackTrace[stackTrace.length - 1] !== undefined) {
    rightContainer.appendChild(setCSS(getLink(stackTrace[stackTrace.length - 1].url, stackTrace[stackTrace.length - 1].linkText), {color: '#808080'}));
    }
  9. @c-kick c-kick revised this gist Oct 21, 2016. 1 changed file with 4 additions and 3 deletions.
    7 changes: 4 additions & 3 deletions hnl.mobileConsole.js
    Original file line number Diff line number Diff line change
    @@ -798,7 +798,7 @@ var mobileConsole = (function () {
    lineAndColumn = thisLine.match(/(?::)(\d+)(?::)(\d+)/);
    url = urlFromString(thisLine).replace(lineAndColumn[0], '').split('#')[0] || '';
    caller = htmlToString(thisLine.replace(urlFromString(thisLine), '').replace(separator, '').replace('at ', '').trim());
    if (caller === '' || caller === lineAndColumn[0]) { continue; }
    if (caller === '' || caller === lineAndColumn[0]) { caller = '(anonymous)' }
    if (url[url.length - 1] === '/') {
    txt = '(index)';
    } else {
    @@ -1011,8 +1011,9 @@ var mobileConsole = (function () {
    }

    //populate right side
    if (stackTrace && stackTrace[0] !== undefined) {
    rightContainer.appendChild(setCSS(getLink(stackTrace[0].url, stackTrace[0].linkText), {color: '#808080'}));
    originalConsole.warn(stackTrace);
    if (stackTrace && stackTrace[stackTrace.length - 1] !== undefined) {
    rightContainer.appendChild(setCSS(getLink(stackTrace[stackTrace.length - 1].url, stackTrace[stackTrace.length - 1].linkText), {color: '#808080'}));
    }

    //add to line
  10. @c-kick c-kick revised this gist Oct 21, 2016. No changes.
  11. @c-kick c-kick revised this gist Oct 21, 2016. 1 changed file with 246 additions and 225 deletions.
    471 changes: 246 additions & 225 deletions hnl.mobileConsole.js
    Original file line number Diff line number Diff line change
    @@ -1,5 +1,5 @@
    /*!
    * hnl.mobileConsole - javascript mobile console - v1.2.2 - 17/10/2016
    * hnl.mobileConsole - javascript mobile console - v1.2.3 - 21/10/2016
    * Adds html console to webpage. Especially useful for debugging JS on mobile devices.
    * Supports 'log', 'trace', 'info', 'warn', 'error', 'group', 'groupEnd', 'table', 'assert', 'clear'
    * Inspired by code by jakub fiala (https://gist.github.com/jakubfiala/8fe3461ab6508f46003d)
    @@ -66,27 +66,28 @@ var mobileConsole = (function () {

    //options and other variable containers
    var options = {
    version : '1.2.1',
    baseClass : 'mobileConsole_',
    animParams: 'all 200ms ease',
    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',
    isCrap: document.querySelectorAll === undefined
    overrideAutorun: true,
    version : '1.2.1',
    baseClass : 'mobileConsole_',
    animParams: 'all 200ms ease',
    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',
    isCrap: document.querySelectorAll === undefined
    },
    methods : ['log', 'trace', 'info', 'warn', 'error', 'group', 'groupEnd', 'table', 'assert', 'time', 'timeEnd', 'clear'],
    hideButtons : ['group', 'groupEnd', 'table', 'assert', 'time', 'timeEnd'],
    ratio: 0.4,
    paddingLeft: 0,
    groupDepth: 0
    },
    methods : ['log', 'trace', 'info', 'warn', 'error', 'group', 'groupEnd', 'table', 'assert', 'time', 'timeEnd', 'clear'],
    hideButtons : ['group', 'groupEnd', 'table', 'assert', 'time', 'timeEnd'],
    ratio: 0.4,
    paddingLeft: 0,
    groupDepth: 0
    },
    messages = {
    clear : 'Console was cleared',
    empty: '(Empty string)'
    },
    clear : 'Console was cleared',
    empty: '(Empty string)'
    },
    status = {
    initialized: false,
    acActive : false,
    @@ -107,7 +108,7 @@ var mobileConsole = (function () {
    acHovered: false
    }
    },
    //'backup' original console for reference & internal debugging
    //'backup' original console for reference & internal debugging
    originalConsole = {
    log: (typeof console.log === 'function') ? console.log.bind(console) : null,
    info: (typeof console.info === 'function') ? console.info.bind(console) : null,
    @@ -119,7 +120,7 @@ var mobileConsole = (function () {
    trace: (typeof console.trace === 'function') ? console.trace.bind(console) : null,
    clear: (typeof console.clear === 'function') ? console.clear.bind(console) : null
    },
    // reference variables
    // reference variables
    mobileConsole, consoleElement, commandLine;

    if(options.browserinfo.isCrap) {
    @@ -256,6 +257,7 @@ var mobileConsole = (function () {
    lines: [],
    acItems: [],
    base: createElem('div', 'base', {
    boxSizing: 'border-box',
    position: 'fixed',
    resize: 'none',
    fontSize: '12px',
    @@ -264,21 +266,18 @@ var mobileConsole = (function () {
    top: 'auto',
    right: 0,
    width: '100%',
    height: getMaxHeight() + 'px',
    zIndex: 10000,
    padding: 0,
    paddingBottom: isMobile() ? '35px' : '25px',
    margin: 0,
    border: '0 none',
    borderTop: '1px solid #808080',
    backgroundColor: '#ffffff',
    '-webkit-transition': options.animParams,
    '-moz-transition': options.animParams,
    '-o-transition': options.animParams,
    'transition': options.animParams
    backgroundColor: '#ffffff'
    }),
    topbar : createElem('div', 'topbar', {
    boxSizing: 'border-box',
    position: 'absolute',
    height: '28px',
    left: 0,
    right: 0,
    display: 'block',
    @@ -294,13 +293,18 @@ var mobileConsole = (function () {
    overflowX: 'auto'
    }),
    scrollcontainer : createElem('div', 'scroller', {
    boxSizing: 'border-box',
    border: '0 none',
    fontFamily: 'Consolas, monaco, monospace',
    position: 'relative',
    display: 'block',
    height: '100%',
    height: getMaxHeight() + 'px',
    overflow: 'auto',
    webkitOverflowScrolling: 'touch'
    webkitOverflowScrolling: 'touch',
    '-webkit-transition': options.animParams,
    '-moz-transition': options.animParams,
    '-o-transition': options.animParams,
    'transition': options.animParams
    }),
    table : createElem('table', 'table', {
    border: '0 none',
    @@ -367,50 +371,68 @@ var mobileConsole = (function () {
    fontWeight: 'normal',
    textTransform: 'capitalize',
    fontSize: '12px',
    padding: isMobile() ? '8px 10px' : '5px 8px',
    lineHeight: '26px',
    height: '26px',
    padding: '0 8px',
    margin: 0,
    textAlign: 'center',
    marginRight: '5px',
    border: '0 none',
    backgroundColor: 'transparent',
    color: 'inherit'
    color: 'inherit',
    cursor: 'pointer'
    }),
    buttons : {
    },
    input : createElem('div', 'input', {
    boxSizing: 'border-box',
    height: isMobile() ? '35px' : '29px',
    fontFamily: 'Consolas, monaco, monospace',
    position: 'absolute',
    bottom: 0,
    left: 0,
    right: 0,
    margin: 0,
    border: '0 none',
    borderTop: '1px solid #EEEEEE'
    }),
    gt : createElem('DIV', 'gt', {
    position: 'absolute',
    bottom: 0,
    width: '25px',
    lineHeight: isMobile() ? '34px' : '28px',
    height: isMobile() ? '34px' : '28px',
    textAlign: 'center',
    padding: isMobile() ? '10px 8px' : '6px 8px',
    fontSize: '16px',
    fontFamily: 'Consolas, monaco, monospace',
    fontWeight: 'bold',
    color: '#3577B1'
    color: '#3577B1',
    zIndex: 2
    }),
    consoleinput : createElem('input', 'consoleinput', {
    boxSizing: 'border-box',
    position: 'absolute',
    bottom: 0,
    width : '100%',
    fontSize: isMobile() ? '16px' : 'inherit', //prevents ios safari's zoom on focus
    fontFamily: 'Consolas, monaco, monospace',
    paddingLeft: '25px',
    margin: 0,
    height: isMobile() ? '35px' : '25px',
    border: '0 none',
    backgroundColor: '#FFFFFF',
    color: '#000000'
    outline: 'none',
    outlineWidth: 0,
    boxShadow: 'none',
    '-moz-appearance': 'none',
    '-webkit-appearance': 'none',
    backgroundColor: 'transparent',
    color: '#000000',
    zIndex: 1
    }),
    autocomplete : createElem('div', 'autocomplete', {
    display: 'none',
    position: 'absolute',
    bottom: isMobile() ? '35px' : '25px',
    bottom: isMobile() ? '35px' : '28px',
    left: 0,
    boxShadow: '1px 2px 5px rgba(0,0,0,0.1)',
    color: '#000000',
    @@ -420,7 +442,6 @@ var mobileConsole = (function () {
    autocompleteItem : createElem('a', 'autocompleteitem', {
    display: 'block',
    textDecoration: 'none',
    height: '25px',
    fontSize: isMobile() ? '16px' : 'inherit',
    padding: '5px 8px',
    wordWrap: 'break-word',
    @@ -434,117 +455,117 @@ var mobileConsole = (function () {
    //shared functions

    var setLineStyle = (function () {
    var lineStyles = function (style) {
    switch (style) {
    case 'log':
    return {
    text : {
    borderBottom: '1px solid #DDDDDD',
    color: '#000000'
    },
    dot : {
    color: '#FFFFFF',
    backgroundColor: '#8097bd'
    }
    };
    case 'info':
    return {
    text : {
    borderBottom: '1px solid #DDDDDD',
    color: '#1f3dc4'
    },
    dot : {
    color: '#FFFFFF',
    backgroundColor: '#367AB4'
    }
    };
    case 'warn':
    return {
    text : {
    borderBottom: '1px solid #DDDDDD',
    color: '#CE8724',
    backgroundColor : '#fff6e0'
    },
    dot : {
    color: '#FFFFFF',
    backgroundColor: '#e8a400'
    }
    };
    case 'error':
    case 'table':
    return {
    text : {
    borderBottom: '1px solid #DDDDDD',
    color: '#FF0000',
    backgroundColor : '#ffe5e5'
    },
    dot : {
    color: '#FFFFFF',
    backgroundColor: '#FF0000'
    }
    };
    case 'assert':
    return {
    text : {
    borderBottom: '1px solid #DDDDDD',
    color: '#FF0000',
    backgroundColor : '#ffe5e5'
    },
    dot : {
    color: '#FFFFFF',
    backgroundColor: '#FF0000'
    }
    };
    case 'trace':
    return {
    text : {
    borderBottom: '1px solid #DDDDDD',
    color: '#000000'
    },
    dot : {
    //will not happen
    }
    };
    case 'time':
    case 'timeEnd':
    return {
    text : {
    borderBottom: '1px solid #DDDDDD',
    color: '#0000ff'
    },
    dot : {
    color: '#FFFFFF',
    backgroundColor: '#0000ff'
    }
    };
    default:
    return {
    text : {
    borderBottom: '1px solid #DDDDDD',
    color: '#000000'
    },
    dot : {
    color: '#FFFFFF',
    backgroundColor: '#8097bd'
    }
    };
    }
    var lineStyles = function (style) {
    switch (style) {
    case 'log':
    return {
    text : {
    borderBottom: '1px solid #DDDDDD',
    color: '#000000'
    },
    dot : {
    color: '#FFFFFF',
    backgroundColor: '#8097bd'
    }
    };
    case 'info':
    return {
    text : {
    borderBottom: '1px solid #DDDDDD',
    color: '#1f3dc4'
    },
    dot : {
    color: '#FFFFFF',
    backgroundColor: '#367AB4'
    }
    };
    case 'warn':
    return {
    text : {
    borderBottom: '1px solid #DDDDDD',
    color: '#CE8724',
    backgroundColor : '#fff6e0'
    },
    dot : {
    color: '#FFFFFF',
    backgroundColor: '#e8a400'
    }
    };
    case 'error':
    case 'table':
    return {
    text : {
    borderBottom: '1px solid #DDDDDD',
    color: '#FF0000',
    backgroundColor : '#ffe5e5'
    },
    dot : {
    color: '#FFFFFF',
    backgroundColor: '#FF0000'
    }
    };
    case 'assert':
    return {
    text : {
    borderBottom: '1px solid #DDDDDD',
    color: '#FF0000',
    backgroundColor : '#ffe5e5'
    },
    dot : {
    color: '#FFFFFF',
    backgroundColor: '#FF0000'
    }
    };
    case 'trace':
    return {
    text : {
    borderBottom: '1px solid #DDDDDD',
    color: '#000000'
    },
    dot : {
    //will not happen
    }
    };
    case 'time':
    case 'timeEnd':
    return {
    text : {
    borderBottom: '1px solid #DDDDDD',
    color: '#0000ff'
    },
    dot : {
    color: '#FFFFFF',
    backgroundColor: '#0000ff'
    }
    };
    default:
    return {
    text : {
    borderBottom: '1px solid #DDDDDD',
    color: '#000000'
    },
    dot : {
    color: '#FFFFFF',
    backgroundColor: '#8097bd'
    }
    };
    }

    };
    var color, dot;
    };
    var color, dot;

    return function (element, type, msg) {
    if (status.initialized) {
    color = (msg === 'undefined' || msg === htmlToString(messages.empty)) ? {color: '#808080'} : ((msg === htmlToString(messages.clear)) ? {color: '#808080', fontStyle: 'italic'} : (lineStyles(type) !== undefined ? lineStyles(type).text : lineStyles.log.text));
    dot = lineStyles(type) !== undefined ? lineStyles(type).dot : lineStyles.log.dot;
    setCSS(element, color);
    //has dot?
    if (element.childNodes[0].childNodes[0].className.indexOf('dot') !== -1) {
    setCSS(element.childNodes[0].childNodes[0], lineStyles(type).dot);
    return function (element, type, msg) {
    if (status.initialized) {
    color = (msg === 'undefined' || msg === htmlToString(messages.empty)) ? {color: '#808080'} : ((msg === htmlToString(messages.clear)) ? {color: '#808080', fontStyle: 'italic'} : (lineStyles(type) !== undefined ? lineStyles(type).text : lineStyles.log.text));
    dot = lineStyles(type) !== undefined ? lineStyles(type).dot : lineStyles.log.dot;
    setCSS(element, color);
    //has dot?
    if (element.childNodes[0].childNodes[0].className.indexOf('dot') !== -1) {
    setCSS(element.childNodes[0].childNodes[0], lineStyles(type).dot);
    }
    }
    }
    };
    }()),
    };
    }()),
    getLink = function (href, textString) {
    var HTMLurl = elements.link.cloneNode(false);
    if (href) {
    @@ -557,8 +578,8 @@ var mobileConsole = (function () {
    toggleHeight = function () {
    if (status.initialized) {
    var existingPadding = parseInt(document.body.style.paddingBottom, 10) - Math.abs(elements.base.offsetHeight + elements.topbar.offsetHeight);
    var newHeight = (elements.base.minimized) ? getMaxHeight() + 'px' : (elements.input.clientHeight + 2) + 'px';
    setCSS(elements.base, {
    var newHeight = (elements.base.minimized) ? getMaxHeight() + 'px' : '0px';
    setCSS(elements.scrollcontainer, {
    height: newHeight
    });
    setCSS(document.body, {
    @@ -575,8 +596,8 @@ var mobileConsole = (function () {
    return function () {
    console.info(
    '--==## Mobile Console ' + (status.initialized ? 'active' : 'inactive') + ' ##==--' + '\n' +
    '--===============================--' + '\n' +
    'MobileConsole v' + options.version + ', running on ' + navigator.userAgent.toLowerCase()
    '--===============================--' + '\n' +
    'MobileConsole v' + options.version + ', running on ' + navigator.userAgent.toLowerCase()
    );
    };
    }());
    @@ -696,7 +717,7 @@ var mobileConsole = (function () {
    function isElement(o) {
    return (
    typeof HTMLElement === 'object' ? o instanceof HTMLElement : //DOM2
    o && typeof o === 'object' && o !== null && o.nodeType === 1 && typeof o.nodeName === 'string'
    o && typeof o === 'object' && o !== null && o.nodeType === 1 && typeof o.nodeName === 'string'
    );
    }
    function objectToString(object) {
    @@ -720,7 +741,7 @@ var mobileConsole = (function () {
    match = string.match(uriPattern)[0];
    return match;
    } catch (e) {
    return undefined;
    return '';
    }
    }
    function filterOut(array, match) {
    @@ -905,79 +926,79 @@ var mobileConsole = (function () {
    arrows = stackTrace ? elements.arrowRight + '&nbsp;' : '';

    switch (method) {
    case 'assert':
    if (message[0] === false) {
    msgContainer.innerHTML = arrows + 'Assertion failed: ' + message[1];
    }
    stackTable = traceToTable(elements.stackTraceTable.cloneNode(false), stackTrace);
    method = 'error'; //groups it under 'error' and is thus toggleable in view
    break;
    case 'log':
    case 'debug':
    case 'info':
    case 'warn':
    if (typeof arguments[1].newMessage === 'object') {
    detailTable = objectToTable(elements.stackTraceTable.cloneNode(false), arguments[1].newMessage);
    msgContainer.innerHTML = elements.arrowRight + '&nbsp;' + message;
    } else {
    msgContainer.innerHTML = message;
    }
    break;
    case 'error':
    case 'trace':
    case 'dir':
    case 'table':
    //left side
    if (method === 'trace') {
    message = 'console.trace()';
    msgContainer.innerHTML = arrows + message;
    } else if (method === 'table') {
    detailTable = objectToTable(elements.stackTraceTable.cloneNode(false), arguments[1].newMessage);
    msgContainer.innerHTML = elements.arrowRight + '&nbsp;' + message;
    } else {
    msgContainer.innerHTML = arrows + message;
    }
    stackTable = traceToTable(elements.stackTraceTable.cloneNode(false), stackTrace);
    break;
    case 'group':
    case 'groupCollapsed':
    case 'groupEnd':
    if (method === 'group') {
    options.groupDepth = options.groupDepth + 1;
    msgContainer.innerHTML = '<strong>' + message + '</strong>';
    msgContainer.setAttribute('toggles', 'group_' + options.groupDepth);
    } else {
    options.groupDepth = valBetween(options.groupDepth - 1, 0, 99);
    history.output.prevMsg = '';
    }
    if (options.groupDepth > 0) {
    options.paddingLeft = (options.groupDepth * 23) + 'px';
    } else {
    options.paddingLeft = 0;
    }
    break;
    case 'time':
    case 'timeEnd':
    var timerName = arguments[1].newMessage || 'default', now, passed;
    if (method === 'time') {
    status.timers[timerName] = Date.now();
    if (typeof arguments[1].original === 'function') {
    arguments[1].original.apply(console, arguments[1].originalArguments); //make sure we still call the original console.time to start the browser's console timer
    case 'assert':
    if (message[0] === false) {
    msgContainer.innerHTML = arrows + 'Assertion failed: ' + message[1];
    }
    return;
    }
    now = Date.now();
    if (!status.timers[timerName]) {
    console.warn('Timer "' + timerName + '" does not exist.');
    return;
    }
    passed = now - (status.timers[timerName] || 0);
    message = timerName + ': ' + passed + 'ms';
    msgContainer.innerHTML = message;
    delete status.timers[timerName];
    break;
    default:
    msgContainer.innerHTML = message;
    stackTable = traceToTable(elements.stackTraceTable.cloneNode(false), stackTrace);
    method = 'error'; //groups it under 'error' and is thus toggleable in view
    break;
    case 'log':
    case 'debug':
    case 'info':
    case 'warn':
    if (typeof arguments[1].newMessage === 'object') {
    detailTable = objectToTable(elements.stackTraceTable.cloneNode(false), arguments[1].newMessage);
    msgContainer.innerHTML = elements.arrowRight + '&nbsp;' + message;
    } else {
    msgContainer.innerHTML = message;
    }
    break;
    case 'error':
    case 'trace':
    case 'dir':
    case 'table':
    //left side
    if (method === 'trace') {
    message = 'console.trace()';
    msgContainer.innerHTML = arrows + message;
    } else if (method === 'table') {
    detailTable = objectToTable(elements.stackTraceTable.cloneNode(false), arguments[1].newMessage);
    msgContainer.innerHTML = elements.arrowRight + '&nbsp;' + message;
    } else {
    msgContainer.innerHTML = arrows + message;
    }
    stackTable = traceToTable(elements.stackTraceTable.cloneNode(false), stackTrace);
    break;
    case 'group':
    case 'groupCollapsed':
    case 'groupEnd':
    if (method === 'group') {
    options.groupDepth = options.groupDepth + 1;
    msgContainer.innerHTML = '<strong>' + message + '</strong>';
    msgContainer.setAttribute('toggles', 'group_' + options.groupDepth);
    } else {
    options.groupDepth = valBetween(options.groupDepth - 1, 0, 99);
    history.output.prevMsg = '';
    }
    if (options.groupDepth > 0) {
    options.paddingLeft = (options.groupDepth * 23) + 'px';
    } else {
    options.paddingLeft = 0;
    }
    break;
    case 'time':
    case 'timeEnd':
    var timerName = arguments[1].newMessage || 'default', now, passed;
    if (method === 'time') {
    status.timers[timerName] = Date.now();
    if (typeof arguments[1].original === 'function') {
    arguments[1].original.apply(console, arguments[1].originalArguments); //make sure we still call the original console.time to start the browser's console timer
    }
    return;
    }
    now = Date.now();
    if (!status.timers[timerName]) {
    console.warn('Timer "' + timerName + '" does not exist.');
    return;
    }
    passed = now - (status.timers[timerName] || 0);
    message = timerName + ': ' + passed + 'ms';
    msgContainer.innerHTML = message;
    delete status.timers[timerName];
    break;
    default:
    msgContainer.innerHTML = message;
    }

    if (!msgContainer.innerHTML) { return; }
    @@ -1288,7 +1309,7 @@ var mobileConsole = (function () {
    }

    //autorun if mobile
    if (isMobile()) {
    if (isMobile() || options.overrideAutorun) {
    init();
    }

  12. @c-kick c-kick revised this gist Oct 19, 2016. 1 changed file with 3 additions and 3 deletions.
    6 changes: 3 additions & 3 deletions hnl.mobileConsole.js
    Original file line number Diff line number Diff line change
    @@ -765,7 +765,7 @@ var mobileConsole = (function () {
    thisLine,
    lineAndColumn,
    caller,
    separator = options.browserinfo.ffox || options.browserinfo.safari ? '@' : '()';
    separator = options.browserinfo.ffox ? '@' : '()';

    //stop if no source trace can be determined
    if (!traceToProcess) { return; }
    @@ -776,8 +776,8 @@ var mobileConsole = (function () {
    thisLine = lines[i].trim();
    lineAndColumn = thisLine.match(/(?::)(\d+)(?::)(\d+)/);
    url = urlFromString(thisLine).replace(lineAndColumn[0], '').split('#')[0] || '';
    caller = htmlToString(thisLine.replace(url, '').replace(separator, '').replace('at ', '').trim());
    if (caller === '') { continue; }
    caller = htmlToString(thisLine.replace(urlFromString(thisLine), '').replace(separator, '').replace('at ', '').trim());
    if (caller === '' || caller === lineAndColumn[0]) { continue; }
    if (url[url.length - 1] === '/') {
    txt = '(index)';
    } else {
  13. @c-kick c-kick revised this gist Oct 19, 2016. 1 changed file with 5 additions and 5 deletions.
    10 changes: 5 additions & 5 deletions hnl.mobileConsole.js
    Original file line number Diff line number Diff line change
    @@ -774,19 +774,19 @@ var mobileConsole = (function () {
    i = lines.length;
    while (i--) {
    thisLine = lines[i].trim();
    url = urlFromString(thisLine) || '';
    lineAndColumn = thisLine.match(/(?::)(\d+)(?::)(\d+)/);
    url = urlFromString(thisLine).replace(lineAndColumn[0], '').split('#')[0] || '';
    caller = htmlToString(thisLine.replace(url, '').replace(separator, '').replace('at ', '').trim());
    if (caller === '') { continue; }
    if (url.split('/:')[1] !== undefined) {
    txt = '(index):' + url.split('/:')[1];
    if (url[url.length - 1] === '/') {
    txt = '(index)';
    } else {
    txt = url.split('\\').pop().split('/').filter(Boolean).pop() || caller;
    }
    lineAndColumn = thisLine.match(/(?::)(\d+)(?::)(\d+)/);
    callStack.push({
    caller: caller,
    url: url ? url.split(':')[0] + ':' + url.split(':')[1] : caller,
    linkText: txt,
    linkText: txt + lineAndColumn[0],
    line: lineAndColumn[1],
    col: lineAndColumn[2],
    originalLine: thisLine
  14. @c-kick c-kick revised this gist Oct 18, 2016. 1 changed file with 38 additions and 11 deletions.
    49 changes: 38 additions & 11 deletions hnl.mobileConsole.js
    Original file line number Diff line number Diff line change
    @@ -1,5 +1,5 @@
    /*!
    * hnl.mobileConsole - javascript mobile console - v1.2.1 - 17/10/2016
    * hnl.mobileConsole - javascript mobile console - v1.2.2 - 17/10/2016
    * Adds html console to webpage. Especially useful for debugging JS on mobile devices.
    * Supports 'log', 'trace', 'info', 'warn', 'error', 'group', 'groupEnd', 'table', 'assert', 'clear'
    * Inspired by code by jakub fiala (https://gist.github.com/jakubfiala/8fe3461ab6508f46003d)
    @@ -269,6 +269,7 @@ var mobileConsole = (function () {
    padding: 0,
    paddingBottom: isMobile() ? '35px' : '25px',
    margin: 0,
    border: '0 none',
    borderTop: '1px solid #808080',
    backgroundColor: '#ffffff',
    '-webkit-transition': options.animParams,
    @@ -286,12 +287,14 @@ var mobileConsole = (function () {
    webkitOverflowScrolling: 'touch',
    color: '#444444',
    backgroundColor: '#f3f3f3',
    border: '0 none',
    borderTop: '1px solid #a3a3a3',
    borderBottom: '1px solid #a3a3a3',
    whiteSpace: 'nowrap',
    overflowX: 'auto'
    }),
    scrollcontainer : createElem('div', 'scroller', {
    border: '0 none',
    fontFamily: 'Consolas, monaco, monospace',
    position: 'relative',
    display: 'block',
    @@ -300,12 +303,16 @@ var mobileConsole = (function () {
    webkitOverflowScrolling: 'touch'
    }),
    table : createElem('table', 'table', {
    border: '0 none',
    margin: 0,
    position: 'relative',
    tableLayout: 'auto',
    width: '100%',
    borderCollapse: 'collapse'
    }),
    stackTraceTable : createElem('table', 'stackTraceTable', {
    border: '0 none',
    margin: 0,
    display: 'none',
    marginLeft: '10px',
    marginTop: isMobile() ? '8px' : '4px',
    @@ -317,23 +324,27 @@ var mobileConsole = (function () {
    verticalAlign: 'top'
    }),
    td : createElem('td', 'table_row', {
    border: '0 none',
    padding: '2px 4px',
    verticalAlign: 'top'
    }),
    msgContainer : createElem('span', 'msgContainer', {
    border: '0 none',
    margin: 0,
    display: 'inline',
    overflow: 'hidden'
    }),
    tdLeft : createElem('td', 'table_row_data', {
    border: '0 none',
    textAlign: 'left',
    padding: isMobile() ? '8px 12px' : '4px 8px'
    }),
    tdRight : createElem('td', 'table_row_data', {
    border: '0 none',
    textAlign: 'left',
    padding: isMobile() ? '8px 12px' : '4px 8px',
    whiteSpace: 'nowrap',
    overflow: 'hidden',
    direction: 'rtl'
    overflow: 'hidden'
    }),
    link : createElem('a', 'link', {
    color: '#1155cc',
    @@ -352,20 +363,27 @@ var mobileConsole = (function () {
    }),
    button : createElem('button', 'button', {
    display: 'inline-block',
    fontFamily: '"Helvetica Neue",Helvetica,Arial,sans-serif',
    fontWeight: 'normal',
    textTransform: 'capitalize',
    fontSize: '12px',
    padding: isMobile() ? '8px 10px' : '5px 8px',
    margin: 0,
    textAlign: 'center',
    marginRight: '5px',
    border: '0 none',
    backgroundColor: 'transparent'
    backgroundColor: 'transparent',
    color: 'inherit'
    }),
    buttons : {},
    buttons : {
    },
    input : createElem('div', 'input', {
    fontFamily: 'Consolas, monaco, monospace',
    position: 'absolute',
    bottom: 0,
    left: 0,
    right: 0,
    margin: 0,
    borderTop: '1px solid #EEEEEE'
    }),
    gt : createElem('DIV', 'gt', {
    @@ -375,14 +393,15 @@ var mobileConsole = (function () {
    textAlign: 'center',
    padding: isMobile() ? '10px 8px' : '6px 8px',
    fontSize: '16px',
    fontFamily: 'monospace',
    fontFamily: 'Consolas, monaco, monospace',
    fontWeight: 'bold',
    color: '#3577B1'
    }),
    consoleinput : createElem('input', 'consoleinput', {
    width : '100%',
    fontSize: isMobile() ? '16px' : 'inherit', //prevents ios safari's zoom on focus
    paddingLeft: '25px',
    margin: 0,
    height: isMobile() ? '35px' : '25px',
    border: '0 none',
    backgroundColor: '#FFFFFF',
    @@ -532,7 +551,7 @@ var mobileConsole = (function () {
    HTMLurl.setAttribute('href', href);
    HTMLurl.setAttribute('target', '_blank');
    }
    HTMLurl.innerHTML = textString || href.split('\\').pop().split('/').pop();
    HTMLurl.innerHTML = textString || href.split('\\').pop().split('/').filter(Boolean).pop();
    return HTMLurl;
    },
    toggleHeight = function () {
    @@ -694,10 +713,12 @@ var mobileConsole = (function () {
    return htmlToString(object.outerHTML);
    }
    function urlFromString(string) {
    string = String(string);
    //searches for url in string, returns url as string
    var uriPattern = /\b((?:[a-z][\w-]+:(?:\/{1,3}|[a-z0-9%])|www\d{0,3}[.]|[a-z0-9.\-]+[.][a-z]{2,4}\/)(?:[^\s()<>]+|\(([^\s()<>]+|(\([^\s()<>]+\)))*\))+(?:\(([^\s()<>]+|(\([^\s()<>]+\)))*\)|[^\s`!()\[\]{};:'".,<>?«»]))/ig;
    var match, uriPattern = /\b((?:[a-z][\w-]+:(?:\/{1,3}|[a-z0-9%])|www\d{0,3}[.]|[a-z0-9.\-]+[.][a-z]{2,4}\/)(?:[^\s()<>]+|\(([^\s()<>]+|(\([^\s()<>]+\)))*\))+(?:\(([^\s()<>]+|(\([^\s()<>]+\)))*\)|[^\s`!()\[\]{};:'".,<>?«»]))/ig;
    try {
    return String(string).match(uriPattern)[0];
    match = string.match(uriPattern)[0];
    return match;
    } catch (e) {
    return undefined;
    }
    @@ -740,6 +761,7 @@ var mobileConsole = (function () {
    i,
    lines,
    url,
    txt,
    thisLine,
    lineAndColumn,
    caller,
    @@ -755,11 +777,16 @@ var mobileConsole = (function () {
    url = urlFromString(thisLine) || '';
    caller = htmlToString(thisLine.replace(url, '').replace(separator, '').replace('at ', '').trim());
    if (caller === '') { continue; }
    if (url.split('/:')[1] !== undefined) {
    txt = '(index):' + url.split('/:')[1];
    } else {
    txt = url.split('\\').pop().split('/').filter(Boolean).pop() || caller;
    }
    lineAndColumn = thisLine.match(/(?::)(\d+)(?::)(\d+)/);
    callStack.push({
    caller: caller,
    url: url ? url.split(':')[0] + ':' + url.split(':')[1] : caller,
    linkText: url ? url.split('\\').pop().split('/').pop() : caller,
    linkText: txt,
    line: lineAndColumn[1],
    col: lineAndColumn[2],
    originalLine: thisLine
    @@ -963,7 +990,7 @@ var mobileConsole = (function () {
    }

    //populate right side
    if (stackTrace) {
    if (stackTrace && stackTrace[0] !== undefined) {
    rightContainer.appendChild(setCSS(getLink(stackTrace[0].url, stackTrace[0].linkText), {color: '#808080'}));
    }

  15. @c-kick c-kick revised this gist Oct 17, 2016. 1 changed file with 1141 additions and 732 deletions.
    1,873 changes: 1,141 additions & 732 deletions hnl.mobileConsole.js
    Original file line number Diff line number Diff line change
    @@ -1,8 +1,8 @@
    /*!
    * hnl.mobileConsole - javascript mobile console - v0.5.1 - 30/9/2016
    * hnl.mobileConsole - javascript mobile console - v1.2.1 - 17/10/2016
    * Adds html console to webpage. Especially useful for debugging JS on mobile devices.
    * Supports .log, .info, .warn, .error and .trace
    * Based on code by jakub fiala (https://gist.github.com/jakubfiala/8fe3461ab6508f46003d)
    * Supports 'log', 'trace', 'info', 'warn', 'error', 'group', 'groupEnd', 'table', 'assert', 'clear'
    * Inspired by code by jakub fiala (https://gist.github.com/jakubfiala/8fe3461ab6508f46003d)
    * Licensed under the MIT license
    *
    * Original author: @hnldesign
    @@ -13,31 +13,191 @@
    * Info: http://www.hnldesign.nl/work/code/javascript-mobile-console/
    * Demo: http://code.hnldesign.nl/demo/hnl.MobileConsole.html
    */
    var console = window.console;

    //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
    */
    var mobileConsole = (function () {
    'use strict';

    //stop if there is no console in this browser
    if (!console) {
    alert('mobileConsole not supported on this browser');
    return;
    }
    //polyfills
    if (!Date.now) {
    Date.now = function now() {
    return new Date().getTime();
    };
    }
    if (!Array.prototype.filter) {
    Array.prototype.filter = function(fun/*, thisArg*/) {
    'use strict';

    if (this === void 0 || this === null) {
    throw new TypeError();
    }

    var t = Object(this);
    var len = t.length >>> 0;
    if (typeof fun !== 'function') {
    throw new TypeError();
    }

    var res = [];
    var thisArg = arguments.length >= 2 ? arguments[1] : void 0;
    for (var i = 0; i < len; i++) {
    if (i in t) {
    var val = t[i];

    // NOTE: Technically this should Object.defineProperty at
    // the next index, as push can be affected by
    // properties on Object.prototype and Array.prototype.
    // But that method's new, and collisions should be
    // rare, so use the more-compatible alternative.
    if (fun.call(thisArg, val, i, t)) {
    res.push(val);
    }
    }
    }

    return res;
    };
    }

    //options and other variable containers
    var options = {
    version : '1.2.1',
    baseClass : 'mobileConsole_',
    animParams: 'all 200ms ease',
    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',
    isCrap: document.querySelectorAll === undefined
    },
    methods : ['log', 'trace', 'info', 'warn', 'error', 'group', 'groupEnd', 'table', 'assert', 'time', 'timeEnd', 'clear'],
    hideButtons : ['group', 'groupEnd', 'table', 'assert', 'time', 'timeEnd'],
    ratio: 0.4,
    paddingLeft: 0,
    groupDepth: 0
    },
    messages = {
    clear : 'Console was cleared',
    empty: '(Empty string)'
    },
    status = {
    initialized: false,
    acActive : false,
    acHovered : false,
    acInput : '',
    timers : {}
    },
    history = {
    output : {
    prevMsg : '',
    prevMethod : '',
    counter : 0
    },
    input : {
    commands : window.sessionStorage ? (sessionStorage.getItem('mobileConsoleCommandHistory') ? JSON.parse(sessionStorage.getItem('mobileConsoleCommandHistory')) : []) : [],
    commandIdx: window.sessionStorage ? (sessionStorage.getItem('mobileConsoleCommandHistory') ? JSON.parse(sessionStorage.getItem('mobileConsoleCommandHistory')).length : 0) : 0,
    acIdx: 0,
    acHovered: false
    }
    },
    //'backup' original console for reference & internal debugging
    originalConsole = {
    log: (typeof console.log === 'function') ? console.log.bind(console) : null,
    info: (typeof console.info === 'function') ? console.info.bind(console) : null,
    dir: (typeof console.dir === 'function') ? console.dir.bind(console) : null,
    group: (typeof console.group === 'function') ? console.group.bind(console) : null,
    groupEnd: (typeof console.groupEnd === 'function') ? console.groupEnd.bind(console) : null,
    warn: (typeof console.warn === 'function') ? console.warn.bind(console) : null,
    error: (typeof console.error === 'function') ? console.error.bind(console) : null,
    trace: (typeof console.trace === 'function') ? console.trace.bind(console) : null,
    clear: (typeof console.clear === 'function') ? console.clear.bind(console) : null
    },
    // reference variables
    mobileConsole, consoleElement, commandLine;

    if(options.browserinfo.isCrap) {
    console.error(
    '--==## Error: Browser not supported by Mobile Console ##==--' + '\n' +
    'MobileConsole v' + options.version + ', running on ' + navigator.userAgent.toLowerCase()
    );
    return false;
    }

    //helpers for all sub functions
    function isMobile() {
    var check = false;
    (function (a) {
    if (/(android|bb\d+|meego).+mobile|avantgo|bada\/|blackberry|blazer|compal|elaine|fennec|hiptop|iemobile|ip(hone|od)|iris|kindle|lge |maemo|midp|mmp|mobile.+firefox|netfront|opera m(ob|in)i|palm( os)?|phone|p(ixi|re)\/|plucker|pocket|psp|series(4|6)0|symbian|treo|up\.(browser|link)|vodafone|wap|windows ce|xda|xiino/i.test(a) || /1207|6310|6590|3gso|4thp|50[1-6]i|770s|802s|a wa|abac|ac(er|oo|s\-)|ai(ko|rn)|al(av|ca|co)|amoi|an(ex|ny|yw)|aptu|ar(ch|go)|as(te|us)|attw|au(di|\-m|r |s )|avan|be(ck|ll|nq)|bi(lb|rd)|bl(ac|az)|br(e|v)w|bumb|bw\-(n|u)|c55\/|capi|ccwa|cdm\-|cell|chtm|cldc|cmd\-|co(mp|nd)|craw|da(it|ll|ng)|dbte|dc\-s|devi|dica|dmob|do(c|p)o|ds(12|\-d)|el(49|ai)|em(l2|ul)|er(ic|k0)|esl8|ez([4-7]0|os|wa|ze)|fetc|fly(\-|_)|g1 u|g560|gene|gf\-5|g\-mo|go(\.w|od)|gr(ad|un)|haie|hcit|hd\-(m|p|t)|hei\-|hi(pt|ta)|hp( i|ip)|hs\-c|ht(c(\-| |_|a|g|p|s|t)|tp)|hu(aw|tc)|i\-(20|go|ma)|i230|iac( |\-|\/)|ibro|idea|ig01|ikom|im1k|inno|ipaq|iris|ja(t|v)a|jbro|jemu|jigs|kddi|keji|kgt( |\/)|klon|kpt |kwc\-|kyo(c|k)|le(no|xi)|lg( g|\/(k|l|u)|50|54|\-[a-w])|libw|lynx|m1\-w|m3ga|m50\/|ma(te|ui|xo)|mc(01|21|ca)|m\-cr|me(rc|ri)|mi(o8|oa|ts)|mmef|mo(01|02|bi|de|do|t(\-| |o|v)|zz)|mt(50|p1|v )|mwbp|mywa|n10[0-2]|n20[2-3]|n30(0|2)|n50(0|2|5)|n7(0(0|1)|10)|ne((c|m)\-|on|tf|wf|wg|wt)|nok(6|i)|nzph|o2im|op(ti|wv)|oran|owg1|p800|pan(a|d|t)|pdxg|pg(13|\-([1-8]|c))|phil|pire|pl(ay|uc)|pn\-2|po(ck|rt|se)|prox|psio|pt\-g|qa\-a|qc(07|12|21|32|60|\-[2-7]|i\-)|qtek|r380|r600|raks|rim9|ro(ve|zo)|s55\/|sa(ge|ma|mm|ms|ny|va)|sc(01|h\-|oo|p\-)|sdk\/|se(c(\-|0|1)|47|mc|nd|ri)|sgh\-|shar|sie(\-|m)|sk\-0|sl(45|id)|sm(al|ar|b3|it|t5)|so(ft|ny)|sp(01|h\-|v\-|v )|sy(01|mb)|t2(18|50)|t6(00|10|18)|ta(gt|lk)|tcl\-|tdg\-|tel(i|m)|tim\-|t\-mo|to(pl|sh)|ts(70|m\-|m3|m5)|tx\-9|up(\.b|g1|si)|utst|v400|v750|veri|vi(rg|te)|vk(40|5[0-3]|\-v)|vm40|voda|vulc|vx(52|53|60|61|70|80|81|83|85|98)|w3c(\-| )|webc|whit|wi(g |nc|nw)|wmlb|wonu|x700|yas\-|your|zeto|zte\-/i.test(a.substr(0, 4))) {
    check = true;
    }
    }(navigator.userAgent || navigator.vendor || window.opera));
    return check;
    }
    function setCSS(el, css) {
    var i;
    for (i in css) {
    if (css.hasOwnProperty(i)) {
    el.style[i] = css[i];
    }
    }
    return el;
    }
    function htmlToString(html) {
    return String(html).replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/"/g, '&quot;').replace(/ /g, '\u00a0').replace(/(?:\r\n|\r|\n)/g, '<br />').trim();
    }
    function createElem(type, className, css) {
    if (!type || typeof setCSS !== 'function') { return; }
    var element = setCSS(document.createElement(type), css);
    if (className) { element.className = options.baseClass + className; }
    return setCSS(element, css);
    }
    function storeCommand(command) {
    if (history) {
    history.input.commands.push(encodeURI(command.trim()));
    history.input.commandIdx = history.input.commands.length;
    if (window.sessionStorage) { sessionStorage.setItem('mobileConsoleCommandHistory', JSON.stringify(history.input.commands)); }
    }
    }
    function valBetween(val, min, max) {
    return (Math.min(max, Math.max(min, val)));
    }
    function getMaxHeight() {
    return valBetween(Math.floor((window.innerHeight || document.documentElement.clientHeight) * options.ratio), 55, 300);
    }
    function getClass(item) {
    var returnVal = '';
    if (item && item.constructor) {
    returnVal = item.constructor.name;
    } else {
    returnVal = Object.prototype.toString.call(item);
    }
    return String(returnVal);
    }

    // DocReady - Fires supplied function when document is ready
    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;
    var i, len, readyList = [], readyFired = false, 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++) {
    for (i = 0, len = readyList.length; i < len; i = i + 1) {
    // 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
    @@ -65,12 +225,11 @@ if (window.console !== 'undefined') {
    // if ready has already fired, then just schedule the callback
    // to fire asynchronously, but right away
    if (readyFired) {
    setTimeout(function () {callback(context);}, 1);
    setTimeout(function () {callback(context); }, 1);
    return;
    } else {
    // add the function and context to the list
    readyList.push({fn: callback, ctx: context});
    }
    // 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);
    @@ -89,780 +248,1030 @@ if (window.console !== 'undefined') {
    readyEventHandlersInstalled = true;
    }
    };
    })('docReady', window);
    }('docReady', window));
    }

    var MobileConsole = {
    Version: '0.5',
    consoleDiv: undefined,
    console_old : {
    log : console.log.bind(console),
    info : console.info.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: {
    lines : []
    },
    built: false,
    commandHistory: window.sessionStorage ? (sessionStorage.getItem('ConsoleCommandHistory') ? JSON.parse(sessionStorage.getItem('ConsoleCommandHistory')) : []) : [],
    historyIndex: window.sessionStorage ? (sessionStorage.getItem('ConsoleCommandHistory') ? JSON.parse(sessionStorage.getItem('ConsoleCommandHistory')).length - 1 : 0) : 0,
    // elements
    var elements = {
    lines: [],
    acItems: [],
    base: createElem('div', 'base', {
    position: 'fixed',
    resize: 'none',
    fontSize: '12px',
    lineHeight: '14px',
    bottom: 0,
    top: 'auto',
    right: 0,
    width: '100%',
    height: getMaxHeight() + 'px',
    zIndex: 10000,
    padding: 0,
    paddingBottom: isMobile() ? '35px' : '25px',
    margin: 0,
    borderTop: '1px solid #808080',
    backgroundColor: '#ffffff',
    '-webkit-transition': options.animParams,
    '-moz-transition': options.animParams,
    '-o-transition': options.animParams,
    'transition': options.animParams
    }),
    topbar : createElem('div', 'topbar', {
    position: 'absolute',
    left: 0,
    right: 0,
    display: 'block',
    padding: '0 2px',
    overflow: 'hidden',
    webkitOverflowScrolling: 'touch',
    color: '#444444',
    backgroundColor: '#f3f3f3',
    borderTop: '1px solid #a3a3a3',
    borderBottom: '1px solid #a3a3a3',
    whiteSpace: 'nowrap',
    overflowX: 'auto'
    }),
    scrollcontainer : createElem('div', 'scroller', {
    fontFamily: 'Consolas, monaco, monospace',
    position: 'relative',
    display: 'block',
    height: '100%',
    overflow: 'auto',
    webkitOverflowScrolling: 'touch'
    }),
    table : createElem('table', 'table', {
    position: 'relative',
    tableLayout: 'auto',
    width: '100%',
    borderCollapse: 'collapse'
    }),
    stackTraceTable : createElem('table', 'stackTraceTable', {
    display: 'none',
    marginLeft: '10px',
    marginTop: isMobile() ? '8px' : '4px',
    tableLayout: 'auto',
    maxWidth: '100%',
    color: '#333333'
    }),
    tr : createElem('tr', 'table_row', {
    verticalAlign: 'top'
    }),
    td : createElem('td', 'table_row', {
    padding: '2px 4px',
    verticalAlign: 'top'
    }),
    msgContainer : createElem('span', 'msgContainer', {
    display: 'inline',
    overflow: 'hidden'
    }),
    tdLeft : createElem('td', 'table_row_data', {
    textAlign: 'left',
    padding: isMobile() ? '8px 12px' : '4px 8px'
    }),
    tdRight : createElem('td', 'table_row_data', {
    textAlign: 'left',
    padding: isMobile() ? '8px 12px' : '4px 8px',
    whiteSpace: 'nowrap',
    overflow: 'hidden',
    direction: 'rtl'
    }),
    link : createElem('a', 'link', {
    color: '#1155cc',
    textDecoration: 'underline'
    }),
    dot : createElem('div', 'table_row_data_dot', {
    display: 'inline',
    borderRadius: '50%',
    fontSize: '80%',
    fontWeight: 'bold',
    padding: '2px 5px',
    textAlign: 'center',
    marginRight: '5px',
    backgroundColor: '#333333',
    color: '#ffffff'
    }),
    button : createElem('button', 'button', {
    display: 'inline-block',
    fontSize: '12px',
    padding: isMobile() ? '8px 10px' : '5px 8px',
    textAlign: 'center',
    marginRight: '5px',
    border: '0 none',
    backgroundColor: 'transparent'
    }),
    buttons : {},
    input : createElem('div', 'input', {
    fontFamily: 'Consolas, monaco, monospace',
    position: 'absolute',
    bottom: 0,
    left: 0,
    right: 0,
    borderTop: '1px solid #EEEEEE'
    }),
    gt : createElem('DIV', 'gt', {
    position: 'absolute',
    bottom: 0,
    width: '25px',
    textAlign: 'center',
    padding: isMobile() ? '10px 8px' : '6px 8px',
    fontSize: '16px',
    fontFamily: 'monospace',
    fontWeight: 'bold',
    color: '#3577B1'
    }),
    consoleinput : createElem('input', 'consoleinput', {
    width : '100%',
    fontSize: isMobile() ? '16px' : 'inherit', //prevents ios safari's zoom on focus
    paddingLeft: '25px',
    height: isMobile() ? '35px' : '25px',
    border: '0 none',
    backgroundColor: '#FFFFFF',
    color: '#000000'
    }),
    autocomplete : createElem('div', 'autocomplete', {
    display: 'none',
    position: 'absolute',
    bottom: isMobile() ? '35px' : '25px',
    left: 0,
    boxShadow: '1px 2px 5px rgba(0,0,0,0.1)',
    color: '#000000',
    backgroundColor: '#FFFFFF',
    border: '1px solid #b5b5b5'
    }),
    autocompleteItem : createElem('a', 'autocompleteitem', {
    display: 'block',
    textDecoration: 'none',
    height: '25px',
    fontSize: isMobile() ? '16px' : 'inherit',
    padding: '5px 8px',
    wordWrap: 'break-word',
    whiteSpace: 'nowrap'
    }),
    arrowUp: '<img width="10" height="10" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAcAAAAHCAMAAADzjKfhAAAACVBMVEUAAAD///93d3eMZ/YKAAAAAnRSTlMAAHaTzTgAAAAdSURBVHgBY2CEAAYgYAJiEMXEBKHADCYIgKmD0QAFdAA2OHJXEwAAAABJRU5ErkJggg==">',
    arrowDown: '<img width="10" height="10" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAcAAAAHCAYAAADEUlfTAAAAG0lEQVR42mNgwAfKy8v/48I4FeA0AacVDFQBAP9wJkE/KhUMAAAAAElFTkSuQmCC">',
    arrowRight: '<img width="10" height="10" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAcAAAAHCAYAAADEUlfTAAAAJUlEQVR42mNgAILy8vL/DLgASBKnApgkVgXIkhgKiNKJ005s4gDLbCZBiSxfygAAAABJRU5ErkJggg==">'
    };

    //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) ? '127px' : '262px',
    opacity: (elem.toggled) ? '0.8' : 1
    });
    this.innerHTML = (elem.toggled) ? '&plus;' : '&minus;';
    elem.toggled = !elem.toggled;
    return elem;
    };
    //shared functions

    /**
    * Scrolls the console to the bottom
    * @returns {toggleScroll}
    */
    this.toggleScroll = function (e) {
    var elem = mc.elems.scrollContainer;
    elem.scrollTop = elem.scrollHeight;
    return elem;
    };
    var setLineStyle = (function () {
    var lineStyles = function (style) {
    switch (style) {
    case 'log':
    return {
    text : {
    borderBottom: '1px solid #DDDDDD',
    color: '#000000'
    },
    dot : {
    color: '#FFFFFF',
    backgroundColor: '#8097bd'
    }
    };
    case 'info':
    return {
    text : {
    borderBottom: '1px solid #DDDDDD',
    color: '#1f3dc4'
    },
    dot : {
    color: '#FFFFFF',
    backgroundColor: '#367AB4'
    }
    };
    case 'warn':
    return {
    text : {
    borderBottom: '1px solid #DDDDDD',
    color: '#CE8724',
    backgroundColor : '#fff6e0'
    },
    dot : {
    color: '#FFFFFF',
    backgroundColor: '#e8a400'
    }
    };
    case 'error':
    case 'table':
    return {
    text : {
    borderBottom: '1px solid #DDDDDD',
    color: '#FF0000',
    backgroundColor : '#ffe5e5'
    },
    dot : {
    color: '#FFFFFF',
    backgroundColor: '#FF0000'
    }
    };
    case 'assert':
    return {
    text : {
    borderBottom: '1px solid #DDDDDD',
    color: '#FF0000',
    backgroundColor : '#ffe5e5'
    },
    dot : {
    color: '#FFFFFF',
    backgroundColor: '#FF0000'
    }
    };
    case 'trace':
    return {
    text : {
    borderBottom: '1px solid #DDDDDD',
    color: '#000000'
    },
    dot : {
    //will not happen
    }
    };
    case 'time':
    case 'timeEnd':
    return {
    text : {
    borderBottom: '1px solid #DDDDDD',
    color: '#0000ff'
    },
    dot : {
    color: '#FFFFFF',
    backgroundColor: '#0000ff'
    }
    };
    default:
    return {
    text : {
    borderBottom: '1px solid #DDDDDD',
    color: '#000000'
    },
    dot : {
    color: '#FFFFFF',
    backgroundColor: '#8097bd'
    }
    };
    }

    /**
    * Clear the console
    * @returns {toggleScroll}
    */
    this.empty = function (e) {
    console.clear();
    mc.elems.consoleBodyTable.innerHTML = '';
    var methods = ['log', 'trace', 'info', 'warn', 'error'];
    for (var i = 0; i < methods.length; i++) {
    mc.elems.lines[methods[i]] = [];
    }
    };
    };
    var color, dot;

    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++;
    return function (element, type, msg) {
    if (status.initialized) {
    color = (msg === 'undefined' || msg === htmlToString(messages.empty)) ? {color: '#808080'} : ((msg === htmlToString(messages.clear)) ? {color: '#808080', fontStyle: 'italic'} : (lineStyles(type) !== undefined ? lineStyles(type).text : lineStyles.log.text));
    dot = lineStyles(type) !== undefined ? lineStyles(type).dot : lineStyles.log.dot;
    setCSS(element, color);
    //has dot?
    if (element.childNodes[0].childNodes[0].className.indexOf('dot') !== -1) {
    setCSS(element.childNodes[0].childNodes[0], lineStyles(type).dot);
    }
    mc.elems.scrollContainer.scrollTop = mc.elems.scrollContainer.scrollHeight;
    return button;
    }
    };
    }()),
    getLink = function (href, textString) {
    var HTMLurl = elements.link.cloneNode(false);
    if (href) {
    HTMLurl.setAttribute('href', href);
    HTMLurl.setAttribute('target', '_blank');
    }
    HTMLurl.innerHTML = textString || href.split('\\').pop().split('/').pop();
    return HTMLurl;
    },
    toggleHeight = function () {
    if (status.initialized) {
    var existingPadding = parseInt(document.body.style.paddingBottom, 10) - Math.abs(elements.base.offsetHeight + elements.topbar.offsetHeight);
    var newHeight = (elements.base.minimized) ? getMaxHeight() + 'px' : (elements.input.clientHeight + 2) + 'px';
    setCSS(elements.base, {
    height: newHeight
    });
    setCSS(document.body, {
    paddingBottom: existingPadding + Math.abs(parseInt(newHeight, 10) + elements.topbar.offsetHeight) + 'px'
    });
    elements.buttons.toggler.innerHTML = (elements.base.minimized) ? elements.arrowDown : elements.arrowUp;
    elements.buttons.toggler.setAttribute('title', (elements.base.minimized) ? 'Minimize console' : 'Maximize console');
    elements.base.minimized = !elements.base.minimized;
    return elements.base.minimized;
    }
    return 'Not built!';
    },
    about = (function () {
    return function () {
    console.info(
    '--==## Mobile Console ' + (status.initialized ? 'active' : 'inactive') + ' ##==--' + '\n' +
    '--===============================--' + '\n' +
    'MobileConsole v' + options.version + ', running on ' + navigator.userAgent.toLowerCase()
    );
    };
    }());

    /**
    * 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 = '#000000';
    if (mc.Options.Style === 'white') {
    if (method === 'error') { color = '#FF0000'; }
    if (method === 'info' || typeof message === 'boolean') { color = '#1f3dc4'; }
    if (method === 'warn') { color = '#CE8724'; }
    if (message === 'undefined') { color = '#808080'; }
    } else {
    color = '#EEEEEE';
    if (method === 'info' || typeof message === 'boolean') { color = '#a0eaff'; }
    if (method === 'warn') { color = '#CE8724'; }
    if (method === 'error') { color = '#ff4848'; }
    if (message === 'undefined') { color = '#afafaf'; }
    }
    // --==** sub functions start here **==--

    //object requires some more handling
    if (typeof message === 'object') {
    mc.console_old.log(message);
    message = mc.objectStringify(message);
    } else {
    message = mc.htmlStringify(message);
    }
    //initializes the console HTML element
    function initConsoleElement() {
    //reference
    var ref;
    //core
    function toggleScroll() {
    elements.scrollcontainer.scrollTop = elements.scrollcontainer.scrollHeight;
    elements.scrollcontainer.scrollLeft = 0;
    }
    function assemble() {
    var i = options.methods.length, key;

    //add buttons
    while (i--) {
    elements.buttons[options.methods[i]] = elements.button.cloneNode(false);
    elements.buttons[options.methods[i]].innerHTML = options.methods[i].charAt(0).toUpperCase() + options.methods[i].slice(1);
    elements.buttons[options.methods[i]].setAttribute('title', (options.methods[i] !== 'clear') ? 'Toggle the display of ' + options.methods[i] + ' messages' : 'Clear the console');
    }
    //add min/maximize button
    elements.buttons.toggler = elements.button.cloneNode(false);
    elements.buttons.toggler.innerHTML = elements.arrowDown;
    elements.buttons.toggler.setAttribute('title', 'Minimize console');

    if ((this.prevMsg !== message || this.prevMethod !== method) || 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 if (stackTrace.objTrace !== undefined) {
    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(')'));
    }
    if (url !== undefined) {
    cleanUrl = url.split(':')[0] + ':' + url.split(':')[1];
    } else {
    cleanUrl = '';
    }
    }
    }
    //assemble everything
    for (key in elements.buttons) {
    if (elements.buttons.hasOwnProperty(key)) {
    elements.topbar.insertBefore(elements.buttons[key], elements.topbar.firstChild);
    }
    }
    elements.scrollcontainer.appendChild(elements.table);

    //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();
    elements.base.appendChild(elements.topbar);
    elements.base.appendChild(elements.scrollcontainer);

    status.initialized = true;
    return elements.base;
    }
    function attach(console) {
    document.body.appendChild(console);
    setCSS(elements.topbar, {
    top: -Math.abs(elements.topbar.offsetHeight) + 'px'
    });
    var existingPadding = isNaN(parseInt(document.body.style.paddingBottom, 10)) ? 0 : parseInt(document.body.style.paddingBottom, 10);
    setCSS(document.body, {
    paddingBottom: existingPadding + Math.abs(console.offsetHeight + elements.topbar.offsetHeight) + 'px'
    });
    elements.scrollcontainer.scrollTop = elements.scrollcontainer.scrollHeight;

    return elements.base;
    }
    function toggleLogType() {
    //togglelogtype is a click handler; 'this' is the button that was clicked
    var button = this;
    var logType = button.innerHTML.toLowerCase();
    var elems = elements.lines[logType], i = elems.length;
    button.toggled = (button.toggled === undefined) ? true : !button.toggled;
    setCSS(button, { opacity: (button.toggled) ? '0.5' : '' });
    while (i--) {
    setCSS(elems[i], { display: (button.toggled) ? 'none' : '' });
    }
    toggleScroll();
    button.blur();
    return button;
    }
    function setBinds() {
    var methods = options.methods, i = methods.length;
    while (i--) {
    if (methods[i] !== 'clear') {
    if (options.browserinfo.evtLstn) {
    elements.buttons[methods[i]].addEventListener('click', toggleLogType, false);
    } else {
    // not logging. Why?
    elements.buttons[methods[i]].attachEvent('onclick', toggleLogType);
    }
    }
    /*
    console.info('method: ' + method);
    console.info('message: ' + message);
    console.info('prevMethod: ' + this.prevMethod);
    console.info('prevMsg: ' + this.prevMsg);
    */
    };

    //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.info.addEventListener('click', function () { th.toggleLogtype('info', 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.info.attachEvent('onclick', function () { th.toggleLogtype('info', 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', 'info', 'warn', 'error', 'trace'];
    for (var i = 0; i < methods.length; i++) {
    mc.elems.lines[methods[i]] = [];
    mc.interceptConsole(methods[i]);
    if (options.hideButtons.indexOf(methods[i]) !== -1) {
    setCSS(elements.buttons[methods[i]], { display: 'none' });
    }
    }
    },

    /**
    * 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];
    if (options.browserinfo.evtLstn) {
    elements.buttons.toggler.addEventListener('click', toggleHeight, false);
    elements.buttons.clear.addEventListener('click', console.clear, false);
    } else {
    elements.buttons.toggler.attachEvent('onclick', toggleHeight);
    elements.buttons.clear.attachEvent('onclick', console.clear);
    }
    return el;
    },

    /**
    * check if is DOM node element. Taken from: http://stackoverflow.com/questions/384286/javascript-isdom-how-do-you-check-if-a-javascript-object-is-a-dom-object
    * @param o
    * @returns {boolean}
    */
    isNode: function (o) {
    return (
    typeof Node === 'object' ? o instanceof Node :
    o && typeof o === 'object' && typeof o.nodeType === 'number' && typeof o.nodeName==='string'
    );
    },
    }
    //init
    function init() {
    var element = assemble();
    docReady(function () {
    setBinds();
    attach(element);
    });
    //expose Public methods and variables
    return {
    toggleHeight : toggleHeight,
    toggleScroll : toggleScroll
    };
    }
    if (!ref) {
    ref = init();
    }
    return ref;
    }

    /**
    * check if is element. Taken from: http://stackoverflow.com/questions/384286/javascript-isdom-how-do-you-check-if-a-javascript-object-is-a-dom-object
    * @param o
    * @returns {boolean}
    */
    isElement: function (o) {
    //initializes the new console logger
    function initConsole() {
    //reference
    var ref;
    //sub helpers
    function isElement(o) {
    return (
    typeof HTMLElement === 'object' ? o instanceof HTMLElement : //DOM2
    o && typeof o === 'object' && o !== null && o.nodeType === 1 && typeof o.nodeName==='string'
    o && typeof o === 'object' && o !== null && o.nodeType === 1 && typeof o.nodeName === 'string'
    );
    },

    /**
    * Simple stringer for html
    * @param str
    * @returns {string}
    */
    htmlStringify: function (str) {
    return String(str).replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/"/g, '&quot;');
    },

    /**
    * Simple object stringer
    * @param object
    * @returns {*|string}
    */
    objectStringify: function (object) {
    'use strict';
    var simpleObject = {}, prop;
    if (!this.isElement(object)) {
    }
    function objectToString(object) {
    var simpleObject = {}, prop, classname = getClass(object);
    if (!isElement(object)) {
    for (prop in object) {
    if (!object.hasOwnProperty(prop)) {
    continue;
    }
    if (typeof(object[prop]) === 'object') {
    continue;
    }
    if (typeof(object[prop]) === 'function') {
    if (!object.hasOwnProperty(prop) || (typeof (object[prop]) === 'object') || (typeof (object[prop]) === 'function')) {
    continue;
    }
    simpleObject[prop] = object[prop];
    }
    return '<em>Object ' + JSON.stringify(simpleObject) + '</em>'; // returns cleaned up JSON
    return '<em>' + classname + ' ' + JSON.stringify(simpleObject) + '</em>'; // returns cleaned up JSON
    }
    return this.htmlStringify(object.outerHTML);
    //
    },

    /**
    * 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>');
    },
    return htmlToString(object.outerHTML);
    }
    function urlFromString(string) {
    //searches for url in string, returns url as string
    var uriPattern = /\b((?:[a-z][\w-]+:(?:\/{1,3}|[a-z0-9%])|www\d{0,3}[.]|[a-z0-9.\-]+[.][a-z]{2,4}\/)(?:[^\s()<>]+|\(([^\s()<>]+|(\([^\s()<>]+\)))*\))+(?:\(([^\s()<>]+|(\([^\s()<>]+\)))*\)|[^\s`!()\[\]{};:'".,<>?«»]))/ig;
    try {
    return String(string).match(uriPattern)[0];
    } catch (e) {
    return undefined;
    }
    }
    function filterOut(array, match) {
    return array.filter(function(item){
    return typeof item === 'string' && item.indexOf(match) === -1;
    });
    }
    function preFilterTrace(array) {
    var newArray = array.split('\n').filter(Boolean), //filter cleans out empty values
    isCommandLine = false, stealthThese, i;
    if (newArray[0].indexOf('http') === -1) { newArray.shift(); } //remove first line if contains no 'http' (Chrome starts with 'Error', Firefox doesn't..)
    if (newArray[0].indexOf('console.') !== -1 || newArray[0].indexOf('console[method]') !== -1) { newArray.shift(); }
    if (newArray.length > 0) {
    isCommandLine = newArray[newArray.length - 1].indexOf('keydown') !== -1;
    newArray = newArray.filter(function(item){ return item !== ''; });

    /**
    * 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);
    },
    if (isCommandLine) {
    stealthThese = ['submitCommand', 'eval', 'setBinds', 'interceptConsole', 'newConsole'];
    newArray.pop(); //remove last index, as it is the keydown event.
    i = stealthThese.length;
    while(i--) {
    newArray = filterOut(newArray, stealthThese[i]);
    }
    }
    }
    if (isCommandLine || newArray.length === 0) {
    newArray.push('(anonymous function) console:1:1');
    }
    return newArray;
    }
    //core
    function formatStackTrace(trace, origtrace) {
    var callStack = [];
    //original stack is hidden inside trace object, if specified
    var stackTraceOrig = (trace !== undefined && trace[4] !== undefined) ? trace[4].stack : undefined;
    //if the first line contains this, skip it. Meant for browsers that begin the stack with the error message itself (already captured before formatStackTrace)
    var traceToProcess = (origtrace && origtrace !== '') ? origtrace : stackTraceOrig,
    i,
    lines,
    url,
    thisLine,
    lineAndColumn,
    caller,
    separator = options.browserinfo.ffox || options.browserinfo.safari ? '@' : '()';

    /**
    * 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;
    },
    //stop if no source trace can be determined
    if (!traceToProcess) { return; }

    //Console constructor
    buildConsole: function () {
    'use strict';
    var th = this;
    var white = (th.Options.Style === 'white'), top = (th.Options.Position === 'top'), windowed = (th.Options.Type === 'window');
    th.elems.consoleDiv = th.createElem('DIV', 'hnlMobileConsole', {
    position: 'fixed',
    resize: 'none',
    fontSize: '12px',
    lineHeight: '14px',
    bottom: top ? 'auto' : 0,
    top: top ? (windowed ? '32px' : 0) : 'auto',
    right: windowed ? '70px' : 0,
    backgroundColor: white ? '#ffffff' : '#333333',
    width: windowed ? '70%' : '100%',
    height: '127px',
    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', {
    position: 'relative',
    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'),
    wordBreak: 'break-all'
    });
    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',
    width: th.mobileCheck() ? '35%' : '50%'
    });
    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 ? '#444444' : '#FFFFFF',
    backgroundColor: white ? '#f3f3f3' : '#222222',
    borderTop: '1px solid ' + (white ? '#a3a3a3' : '#111111'),
    borderBottom: '1px solid ' + (white ? '#a3a3a3' : '#111111'),
    webkitOverflowScrolling: 'touch'
    });
    th.elems.scrollContainer = th.createElem('DIV', 'hnlMobileConsole_scrolltcontainer', {
    fontFamily: 'Consolas, monaco, monospace',
    position: 'relative',
    display: 'block',
    height: '100%',
    overflow: 'hidden',
    overflowY: 'auto',
    webkitOverflowScrolling: 'touch',
    paddingBottom: '25px'
    });
    th.elems.button = th.createElem('BUTTON', 'hnlMobileConsole_button', {
    display: 'inline-block',
    fontSize: '12px',
    padding: '5px 8px',
    textAlign: 'center',
    marginRight: '5px',
    border: '0 none',
    backgroundColor: 'transparent'
    });
    th.elems.inputDiv = th.createElem('DIV', 'hnlMobileConsole_inputDiv', {
    fontFamily: 'Consolas, monaco, monospace',
    position: 'absolute',
    bottom: 0,
    left: 0,
    right: th.mobileCheck() ? 0 : '17px',
    height: '25px',
    borderTop: '1px solid ' + (white ? '#EEEEEE' : '#777777')
    });
    th.elems.inputIndicator = th.createElem('DIV', 'hnlMobileConsole_inputIndicator', {
    position: 'absolute',
    bottom: 0,
    width: '25px',
    textAlign: 'center',
    padding: '6px 8px',
    color: white ? '#3577B1' : '#70bcff',
    fontSize: '16px',
    fontFamily: 'monospace',
    fontWeight: 'bold'
    });
    th.elems.consoleInput = th.createElem('INPUT', 'hnlMobileConsole_input', {
    width : '100%',
    fontSize: '16px', //prevents ios safari's zoom on focus
    backgroundColor: white ? '#FFFFFF' : '#222222',
    color: white ? '#000000' : '#EEEEEE',
    paddingLeft: '25px',
    border: '0 none'
    });
    th.elems.consoleInput.setAttribute('type', 'text');
    th.elems.consoleInput.setAttribute('autocapitalize', 'off');
    th.elems.consoleInput.setAttribute('autocorrect', 'off');

    //set up buttons object
    th.elems.buttons = [];

    th.elems.buttons.toggler = th.elems.button.cloneNode(false);
    th.elems.buttons.toggler.innerHTML = '&plus;';
    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.info = th.elems.button.cloneNode(false);
    th.elems.buttons.info.innerHTML = 'Info';
    th.elems.topBar.appendChild(th.elems.buttons.info);

    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);

    th.elems.inputDiv.appendChild(th.elems.inputIndicator);
    th.elems.inputDiv.appendChild(th.elems.consoleInput);
    th.elems.inputIndicator.innerHTML = '&gt;';

    //construct elements
    th.elems.consoleDiv.appendChild(th.elems.topBar);
    th.elems.scrollContainer.appendChild(th.elems.consoleBodyTable);
    th.elems.consoleDiv.appendChild(th.elems.scrollContainer);
    th.elems.consoleDiv.appendChild(th.elems.inputDiv);

    // Console emulation
    document.onkeypress = function (e) {
    if ((e.key === 'Enter' || e.keyCode === 13) && e.target === th.elems.consoleInput) {
    var command = th.elems.consoleInput.value;
    if (command !== '') {
    th.commandHistory.push(encodeURI(command));
    th.historyIndex = th.commandHistory.length;
    if (window.sessionStorage) { sessionStorage.setItem('ConsoleCommandHistory', JSON.stringify(th.commandHistory)); }
    var result;
    try {
    result = eval.call(window, command);
    console.log(result);
    if (result === undefined || result === 'undefined' || result === '') {
    var stackTraceOrig, stackTrace;
    try { throw new Error(); } catch(e) { stackTraceOrig = e.stack; }
    if (stackTrace === undefined && stackTraceOrig) {
    stackTrace = th.stackTraceFormat(stackTraceOrig);
    }
    th.consoleDiv.prototype.theConsole('log', (result !== '' ? 'undefined' : ' '), stackTrace, stackTraceOrig);
    }
    } catch(e) {
    console.error(e.message);
    } finally {
    th.elems.consoleInput.value = '';
    }
    }
    lines = preFilterTrace(traceToProcess); //pre filters all lines by filtering out all mobileConsole's own methods so mobileConsole runs Stealth and unobtrusive
    i = lines.length;
    while (i--) {
    thisLine = lines[i].trim();
    url = urlFromString(thisLine) || '';
    caller = htmlToString(thisLine.replace(url, '').replace(separator, '').replace('at ', '').trim());
    if (caller === '') { continue; }
    lineAndColumn = thisLine.match(/(?::)(\d+)(?::)(\d+)/);
    callStack.push({
    caller: caller,
    url: url ? url.split(':')[0] + ':' + url.split(':')[1] : caller,
    linkText: url ? url.split('\\').pop().split('/').pop() : caller,
    line: lineAndColumn[1],
    col: lineAndColumn[2],
    originalLine: thisLine
    });
    }
    return callStack;
    }
    function traceToTable(table, trace) {
    var i, tdLeft, tdRight, tr;
    if (trace === undefined) {
    return;
    }
    trace.reverse(); //reverse order of trace, as it is in a browser's console
    i = trace.length;
    while (i--) {
    tdLeft = elements.td.cloneNode(false);
    tdRight = elements.td.cloneNode(false);
    tr = elements.tr.cloneNode(false);
    tdLeft.innerHTML = trace[i].caller;
    tdRight.innerHTML = '&nbsp;@&nbsp;';
    tdRight.appendChild(getLink((trace[i].url || ''), trace[i].linkText));
    tr.appendChild(tdLeft);
    tr.appendChild(tdRight);
    table.insertBefore(tr, table.firstChild);
    }
    return table;
    }
    function colorizeData(key, value) {
    var valueColor = '#3c53da', keyColor = '#ae33b7', classname = getClass(value);
    if (value && classname.indexOf('HTML') !== -1) {
    value = htmlToString(value.outerHTML);
    valueColor = '#ad8200';
    } else if (key === 'innerHTML' || key === 'outerHTML') {
    value = htmlToString(value);
    valueColor = '#ad8200';
    }
    if (value === null) {
    valueColor = '#808080';
    }
    if (typeof value === 'string') {
    valueColor = '#c54300';
    //HARD limit, for speed/mem issues with consecutive logging of large strings
    if (value.length > 400) {
    value = '"' + String(value).substring(0, 400) + '" [...] <br/><span style="color:#FF0000;text-decoration: underline;">Note: string was truncated to 400 chars</span>';
    } else {
    value = '"' + value + '"';
    }
    };
    document.onkeydown = function (e) {
    var idx = th.historyIndex, hist = th.commandHistory;
    if ((e.keyCode === 38 || e.keyCode === 40) && e.target === th.elems.consoleInput) {
    e.preventDefault();
    if (e.keyCode === 40) {
    idx = idx + 1;
    } else if (e.keyCode === 38) {
    idx = idx - 1;
    }
    if (idx > hist.length) {
    idx = hist.length;
    } else if (idx < 0) {
    idx = 0;
    }
    th.elems.consoleInput.value = hist[idx] === undefined ? '' : decodeURI(hist[idx]);
    th.historyIndex = idx;
    }
    return '<span style="color:' + keyColor + ';">' + key + ':</span> <span style="color:' + valueColor + ';">' + value + '</span>';
    }
    function objectToTable(table, object) {
    var i;
    for (i in object) {
    var tdLeft = elements.td.cloneNode(false), tr = elements.tr.cloneNode(false);
    tdLeft.innerHTML = colorizeData(i, object[i]);
    tr.appendChild(tdLeft);
    table.appendChild(tr);
    }
    return table;
    }
    function toggleDetails() {
    //toggleDetails is a click handler; 'this' is the button that was clicked
    var button = this, i, hidden;
    if (button.getAttribute('toggles') === 'table') {
    var tables = button.parentElement.getElementsByTagName('table');
    i = tables.length;
    while (i--) {
    hidden = (tables[i].currentStyle ? tables[i].currentStyle.display : window.getComputedStyle(tables[i], null).display) === 'none';
    button.innerHTML = button.innerHTML.replace((hidden ? elements.arrowRight : elements.arrowDown), (hidden ? elements.arrowDown : elements.arrowRight));
    setCSS(tables[i], { display: hidden ? 'table' : 'none' });
    }
    };
    // handle focus
    th.elems.consoleInput.onfocus = function () {
    th.focusConsole('in', true);
    };
    th.elems.consoleDiv.onmouseover = function () { th.focusConsole('in'); };
    th.elems.consoleDiv.onmouseout = function () { th.focusConsole('out'); };
    th.elems.consoleInput.onblur = function () { th.focusConsole('out', false); };
    return th.elems.consoleDiv;
    },
    }
    }
    function isRepeat(message, method) {
    return (history.output.prevMsg === message && history.output.prevMethod === method) && (typeof message !== 'object') && (method !== 'trace') && (method !== 'group') && (method !== 'groupEnd');
    }
    function newConsole() {
    try {
    //get arguments, set vars
    var method = arguments[0], className,
    message = (arguments[1].newMessage !== undefined) ? arguments[1].newMessage : undefined,
    stackTrace = (arguments[1].newStackTrace !== undefined) ? arguments[1].newStackTrace : undefined;

    /**
    * Handle focus
    */
    focusConsole: function (dir, hasFocus) {
    'use strict';
    var th = this;
    th.elems.consoleDiv.hasFocus = (hasFocus !== undefined) ? hasFocus : th.elems.consoleDiv.hasFocus;
    th.setCSS(th.elems.consoleDiv, {
    opacity: (th.elems.consoleDiv.hasFocus || th.elems.consoleDiv.toggled || dir === 'in') ? 1 : '0.8'
    });
    },
    //if message emtpy, show empty message-message
    if (message === '') { message = messages.empty; }

    /**
    * See if mobile device (dodgy, but covers 99%)
    * Source: http://stackoverflow.com/questions/11381673/detecting-a-mobile-browser/11381730#11381730
    */
    mobileCheck : function() {
    var check = false;
    (function(a){if(/(android|bb\d+|meego).+mobile|avantgo|bada\/|blackberry|blazer|compal|elaine|fennec|hiptop|iemobile|ip(hone|od)|iris|kindle|lge |maemo|midp|mmp|mobile.+firefox|netfront|opera m(ob|in)i|palm( os)?|phone|p(ixi|re)\/|plucker|pocket|psp|series(4|6)0|symbian|treo|up\.(browser|link)|vodafone|wap|windows ce|xda|xiino/i.test(a)||/1207|6310|6590|3gso|4thp|50[1-6]i|770s|802s|a wa|abac|ac(er|oo|s\-)|ai(ko|rn)|al(av|ca|co)|amoi|an(ex|ny|yw)|aptu|ar(ch|go)|as(te|us)|attw|au(di|\-m|r |s )|avan|be(ck|ll|nq)|bi(lb|rd)|bl(ac|az)|br(e|v)w|bumb|bw\-(n|u)|c55\/|capi|ccwa|cdm\-|cell|chtm|cldc|cmd\-|co(mp|nd)|craw|da(it|ll|ng)|dbte|dc\-s|devi|dica|dmob|do(c|p)o|ds(12|\-d)|el(49|ai)|em(l2|ul)|er(ic|k0)|esl8|ez([4-7]0|os|wa|ze)|fetc|fly(\-|_)|g1 u|g560|gene|gf\-5|g\-mo|go(\.w|od)|gr(ad|un)|haie|hcit|hd\-(m|p|t)|hei\-|hi(pt|ta)|hp( i|ip)|hs\-c|ht(c(\-| |_|a|g|p|s|t)|tp)|hu(aw|tc)|i\-(20|go|ma)|i230|iac( |\-|\/)|ibro|idea|ig01|ikom|im1k|inno|ipaq|iris|ja(t|v)a|jbro|jemu|jigs|kddi|keji|kgt( |\/)|klon|kpt |kwc\-|kyo(c|k)|le(no|xi)|lg( g|\/(k|l|u)|50|54|\-[a-w])|libw|lynx|m1\-w|m3ga|m50\/|ma(te|ui|xo)|mc(01|21|ca)|m\-cr|me(rc|ri)|mi(o8|oa|ts)|mmef|mo(01|02|bi|de|do|t(\-| |o|v)|zz)|mt(50|p1|v )|mwbp|mywa|n10[0-2]|n20[2-3]|n30(0|2)|n50(0|2|5)|n7(0(0|1)|10)|ne((c|m)\-|on|tf|wf|wg|wt)|nok(6|i)|nzph|o2im|op(ti|wv)|oran|owg1|p800|pan(a|d|t)|pdxg|pg(13|\-([1-8]|c))|phil|pire|pl(ay|uc)|pn\-2|po(ck|rt|se)|prox|psio|pt\-g|qa\-a|qc(07|12|21|32|60|\-[2-7]|i\-)|qtek|r380|r600|raks|rim9|ro(ve|zo)|s55\/|sa(ge|ma|mm|ms|ny|va)|sc(01|h\-|oo|p\-)|sdk\/|se(c(\-|0|1)|47|mc|nd|ri)|sgh\-|shar|sie(\-|m)|sk\-0|sl(45|id)|sm(al|ar|b3|it|t5)|so(ft|ny)|sp(01|h\-|v\-|v )|sy(01|mb)|t2(18|50)|t6(00|10|18)|ta(gt|lk)|tcl\-|tdg\-|tel(i|m)|tim\-|t\-mo|to(pl|sh)|ts(70|m\-|m3|m5)|tx\-9|up(\.b|g1|si)|utst|v400|v750|veri|vi(rg|te)|vk(40|5[0-3]|\-v)|vm40|voda|vulc|vx(52|53|60|61|70|80|81|83|85|98)|w3c(\-| )|webc|whit|wi(g |nc|nw)|wmlb|wonu|x700|yas\-|your|zeto|zte\-/i.test(a.substr(0,4))) check = true;})(navigator.userAgent||navigator.vendor||window.opera);
    return check;
    },
    if (isRepeat(message, method) && method.indexOf('time') === -1) {
    // up the counter and add the dot
    history.output.counter = history.output.counter + 1;
    elements.table.lastChild.countDot = elements.table.lastChild.countDot || elements.dot.cloneNode(false);
    elements.table.lastChild.firstChild.insertBefore(elements.table.lastChild.countDot, elements.table.lastChild.firstChild.firstChild).innerHTML = history.output.counter;
    setLineStyle(elements.table.lastChild, method, message);
    } else {
    history.output.prevMsg = message;
    history.output.prevMethod = method;
    history.output.counter = 1;

    /**
    * 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;
    },
    //an object requires some more handling
    if (typeof message === 'object' && method !== 'assert' && method !== 'timeEnd') {
    className = getClass(message);
    if (className.indexOf('HTML') !== -1 && className !== 'HTMLDocument') {
    message = htmlToString(message.outerHTML.match(/<(.*?)>/g)[0] + '...' + message.outerHTML.match(/<(.*?)>/g).pop()); //gets first and last tag, adds '...' in middle. e.g. <div>...</div>
    } else {
    message = objectToString(message);
    }
    } else if (method !== 'assert' && method.indexOf('time') === -1) {
    message = htmlToString(message);
    }

    /**
    * 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, '&lt;')
    .replace(/>/gm, '&gt;');
    //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;
    var detailTable,
    stackTable,
    msgContainer = elements.msgContainer.cloneNode(false),
    lineContainer = elements.tr.cloneNode(false),
    leftContainer = elements.tdLeft.cloneNode(true),
    rightContainer = elements.tdRight.cloneNode(false),
    arrows = stackTrace ? elements.arrowRight + '&nbsp;' : '';

    switch (method) {
    case 'assert':
    if (message[0] === false) {
    msgContainer.innerHTML = arrows + 'Assertion failed: ' + message[1];
    }
    stackTable = traceToTable(elements.stackTraceTable.cloneNode(false), stackTrace);
    method = 'error'; //groups it under 'error' and is thus toggleable in view
    break;
    case 'log':
    case 'debug':
    case 'info':
    case 'warn':
    if (typeof arguments[1].newMessage === 'object') {
    detailTable = objectToTable(elements.stackTraceTable.cloneNode(false), arguments[1].newMessage);
    msgContainer.innerHTML = elements.arrowRight + '&nbsp;' + message;
    } else {
    msgContainer.innerHTML = message;
    }
    break;
    case 'error':
    case 'trace':
    case 'dir':
    case 'table':
    //left side
    if (method === 'trace') {
    message = 'console.trace()';
    msgContainer.innerHTML = arrows + message;
    } else if (method === 'table') {
    detailTable = objectToTable(elements.stackTraceTable.cloneNode(false), arguments[1].newMessage);
    msgContainer.innerHTML = elements.arrowRight + '&nbsp;' + message;
    } else {
    msgContainer.innerHTML = arrows + message;
    }
    stackTable = traceToTable(elements.stackTraceTable.cloneNode(false), stackTrace);
    break;
    case 'group':
    case 'groupCollapsed':
    case 'groupEnd':
    if (method === 'group') {
    options.groupDepth = options.groupDepth + 1;
    msgContainer.innerHTML = '<strong>' + message + '</strong>';
    msgContainer.setAttribute('toggles', 'group_' + options.groupDepth);
    } else {
    options.groupDepth = valBetween(options.groupDepth - 1, 0, 99);
    history.output.prevMsg = '';
    }
    if (options.groupDepth > 0) {
    options.paddingLeft = (options.groupDepth * 23) + 'px';
    } else {
    options.paddingLeft = 0;
    }
    break;
    case 'time':
    case 'timeEnd':
    var timerName = arguments[1].newMessage || 'default', now, passed;
    if (method === 'time') {
    status.timers[timerName] = Date.now();
    if (typeof arguments[1].original === 'function') {
    arguments[1].original.apply(console, arguments[1].originalArguments); //make sure we still call the original console.time to start the browser's console timer
    }
    return;
    }
    now = Date.now();
    if (!status.timers[timerName]) {
    console.warn('Timer "' + timerName + '" does not exist.');
    return;
    }
    passed = now - (status.timers[timerName] || 0);
    message = timerName + ': ' + passed + 'ms';
    msgContainer.innerHTML = message;
    delete status.timers[timerName];
    break;
    default:
    msgContainer.innerHTML = message;
    }
    }

    //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, '&commat;');
    if (!msgContainer.innerHTML) { return; }
    leftContainer.appendChild(msgContainer);

    if (detailTable || stackTable) {
    setCSS(msgContainer, {cursor : 'pointer'});
    leftContainer.appendChild(detailTable || stackTable);
    msgContainer.setAttribute('toggles', 'table');
    }

    //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);
    //populate right side
    if (stackTrace) {
    rightContainer.appendChild(setCSS(getLink(stackTrace[0].url, stackTrace[0].linkText), {color: '#808080'}));
    }

    break;
    //add to line
    lineContainer.appendChild(leftContainer);
    lineContainer.appendChild(rightContainer);

    //set colors
    setCSS(lineContainer, { display: (elements.buttons[method].toggled ? 'none' : '') });
    setLineStyle(lineContainer, method, message);

    //set binds
    if (options.browserinfo.evtLstn) {
    msgContainer.addEventListener('click', toggleDetails, false);
    } else {
    msgContainer.attachEvent('onclick', toggleDetails);
    }

    //store the lines in the object corresponding to the method used
    elements.lines[method].push(lineContainer);

    //handle grouping (group and groupEnd
    if (options.paddingLeft !== 0) {
    setCSS(leftContainer, {paddingLeft: options.paddingLeft});
    setCSS(msgContainer, {borderLeft: '1px solid #808080', paddingLeft: '5px'});
    }

    //add the line to the table
    elements.table.appendChild(lineContainer);
    }
    //scroll
    consoleElement.toggleScroll();
    //==========================================================
    //make sure we still call the original method, if applicable (not window.onerror)
    if (typeof arguments[1].original === 'function') {
    arguments[1].original.apply(console, arguments[1].originalArguments);
    }
    } catch (e) {
    //not logging. why? throw error
    if (isMobile()) { alert(e); }
    originalConsole.error('mobileConsole generated an error logging this event!');
    originalConsole.error(arguments);
    originalConsole.error(e);
    //try to re-log it as an error
    newConsole('error', e);
    }

    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(' ');
    }
    function interceptConsole(method) {
    var original = console[method], i, stackTraceOrig;
    console[method] = function () {
    var args = Array.prototype.slice.call(arguments);
    args.original = original;
    args.originalArguments = arguments;
    args.newMessage = (method === 'assert') ? [args[0], args[1]] : args[0];
    //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);
    try { throw new Error(); } catch (e) { stackTraceOrig = e.stack; }
    args.newStackTrace = formatStackTrace(args.newStackTrace, stackTraceOrig);
    if (method === 'clear') {
    elements.table.innerHTML = '';
    history.output.prevMethod = '';
    i = options.methods.length;
    while (i--) {
    elements.lines[options.methods[i]] = [];
    }
    options.groupDepth = 0;
    options.paddingLeft = 0;
    console.log(messages.clear);
    originalConsole.clear();
    return;
    }
    //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);
    newConsole(method, args);
    };
    }
    //init
    function init() {
    //Intercept all original console methods including trace. Register the event type as a line type.
    var i = options.methods.length;
    while (i--) {
    elements.lines[options.methods[i]] = [];
    interceptConsole(options.methods[i]);
    }
    //Bind to window.onerror
    window.onerror = function() {
    var args = Array.prototype.slice.call(arguments);
    args.newMessage = args[0];
    args.newStackTrace = formatStackTrace(arguments);
    newConsole('error', args);
    };
    },

    init: function (autorun) {
    'use strict';
    var th = this;
    //don't go when we have already built it
    if (th.consoleDiv !== undefined || th.built) {
    th.console_old.warn('MobileConsole already built');
    //expose Public methods and variables
    return {
    //nothing yet to expose
    };
    }
    //return
    if (!ref) {
    ref = init();
    }
    return ref;
    }

    //initialize the console commandline
    function initCommandLine() {
    //reference
    var ref;
    //sub helpers
    function getFromArrayById(id) {
    var pos = elements.acItems.map(function(x) {return x.id; }).indexOf(id);
    return {
    position: pos,
    element: (pos !== -1) ? elements.acItems[pos] : undefined
    };
    }
    function findInArray(array, match) {
    return array.filter(function(item, index, self){
    return (typeof item === 'string' && item.indexOf(match) > -1) && (index === self.indexOf(item));
    });
    }
    //core
    function assemble() {
    elements.consoleinput.setAttribute('type', 'text');
    elements.consoleinput.setAttribute('autocapitalize', 'off');
    elements.consoleinput.setAttribute('autocorrect', 'off');
    elements.autocompleteItem.setAttribute('href', '#');
    elements.gt.innerHTML = '&gt;';
    elements.input.appendChild(elements.gt);
    elements.input.appendChild(elements.consoleinput);
    elements.input.appendChild(elements.autocomplete);
    elements.base.appendChild(elements.input);

    return elements.base;
    }
    function submitCommand(command) {
    if (command !== '') {
    storeCommand(command);
    var result;
    try {
    result = eval.call(window, command.trim());
    console.log.call(window, result);
    } catch(e) {
    console.error(e.message);
    } finally {
    elements.consoleinput.value = '';
    }
    }
    }
    function hoverAutoComplete(e) {
    if (e === undefined) { return; }
    //unset any already hovered elements
    var hovered = getFromArrayById('hover').element, target = e.target, over;
    if (hovered !== undefined) {
    setCSS(hovered, {
    color: '',
    backgroundColor: 'rgba(0, 0, 0, 0)'
    }).id = '';
    }
    if (e.type === 'mouseover') {
    status.acHovered = true;
    over = true;
    } else {
    over = false;
    }
    setCSS(target, {
    color: over ? '#FFFFFF' : '',
    backgroundColor: over ? 'rgba(66, 139, 202, 1)' : 'rgba(0, 0, 0, 0)'
    }).id = over ? 'hover' : '';
    }
    function toggleAutoComplete(show) {
    var hidden = (elements.autocomplete.currentStyle ? elements.autocomplete.currentStyle.display : window.getComputedStyle(elements.autocomplete, null).display) === 'none';
    show = (show === undefined) ? hidden : show;
    setCSS(elements.autocomplete, {display: (show) ? 'inherit' : 'none'});
    status.acActive = show;
    if (!show) { status.acHovered = false; }
    }
    function clickAutoComplete(e) {
    e.preventDefault();
    elements.consoleinput.value = e.target.innerHTML;
    elements.consoleinput.focus();
    toggleAutoComplete();
    }
    function autoComplete(command) {
    if (command.length < 1) {
    toggleAutoComplete(false);
    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(function () {
    th.attachConsole();
    });
    var searchString = encodeURI(command), matches, match, row, i, maxAmount = isMobile() ? 3 : 5;
    elements.autocomplete.innerHTML = '';
    elements.acItems = [];
    matches = findInArray(history.input.commands, searchString);
    matches = matches.slice(Math.max(matches.length - maxAmount, 0));
    i = matches.length;
    while (i--) {
    match = decodeURI(matches[i]);
    row = elements.autocompleteItem.cloneNode(false);
    row.innerHTML = match;
    row.onmouseover = hoverAutoComplete;
    elements.autocomplete.insertBefore(row, elements.autocomplete.firstChild);
    elements.acItems.unshift(row);
    }
    toggleAutoComplete(matches.length > 0);
    }
    function setBinds() {
    if (options.browserinfo.evtLstn) {
    elements.autocomplete.addEventListener('click', clickAutoComplete, false);
    } else {
    //if no autorun: attach the built console
    th.attachConsole();
    elements.autocomplete.attachEvent('onclick', clickAutoComplete);
    }
    console.log('MobileConsole initialized. v' + MobileConsole.Version);
    console.log('Running on ' + navigator.userAgent.toLowerCase());

    //return the built console
    return th.consoleDiv;
    document.onkeydown = function (e) {
    if (e.target === elements.consoleinput) {
    if ((e.key === 'Enter' || e.keyCode === 13)) { //enter
    e.preventDefault();
    if(!status.acHovered) {
    submitCommand(elements.consoleinput.value);
    } else {
    elements.consoleinput.value = getFromArrayById('hover').element.innerHTML;
    elements.consoleinput.focus();
    }
    toggleAutoComplete(false);
    status.acInput = '';
    } else if ((e.keyCode === 38 || e.keyCode === 40)) { //up and down arrows for history browsing
    e.preventDefault();
    var up = (e.keyCode === 40);
    if(status.acActive) {
    //autocomplete window is opened
    //get id of currently hovered element
    var hovered = getFromArrayById('hover').position;
    var counter = (hovered === -1) ? elements.acItems.length : hovered;
    //hover new (in- or decreased number) one
    counter = valBetween((counter += (up) ? 1 : -1), 0, elements.acItems.length - 1);
    hoverAutoComplete({target : elements.acItems[counter], type : 'mouseover'});
    } else {
    //autocompete window not opened
    var hist = history.input.commands;
    history.input.commandIdx += (up) ? 1 : -1;
    history.input.commandIdx = valBetween(history.input.commandIdx, 0, hist.length);
    elements.consoleinput.value = hist[history.input.commandIdx] === undefined ? '' : decodeURI(hist[history.input.commandIdx]);
    }
    }
    }
    if (e.keyCode === 27 && status.acActive) {
    toggleAutoComplete(false);
    }
    };
    document.onkeyup = function (e) {
    if (e.target === elements.consoleinput && status.acInput !== elements.consoleinput.value && (e.keyCode !== 38 && e.keyCode !== 40 && e.keyCode !== 27 && e.key !== 'Enter' && e.keyCode !== 13)) {
    status.acInput = elements.consoleinput.value.trim();
    autoComplete(elements.consoleinput.value);
    }
    };
    }
    };
    //init
    function init() {
    var element = assemble();
    setBinds();
    //expose Public methods and variables
    return {
    //nothing yet to expose
    };
    }
    //return
    if (!ref) {
    ref = init();
    }
    return ref;
    }

    if (MobileConsole.Options.AutoRun) {
    MobileConsole.init(true);
    } else {
    //run on mobile devices, if no autorun is explicitly defined
    if (MobileConsole.mobileCheck()) {
    MobileConsole.init(true);
    } else {
    console.warn('MobileConsole loaded but not displaying as AutoRun is disabled (and this is not a mobile device). To initialize, run MobileConsole.init();');
    function init() {
    if (!status.initialized) {
    status.initialized = true;
    //populate references
    if (!mobileConsole) {
    //taps into native console and adds new functionality
    mobileConsole = initConsole();
    }
    if (!consoleElement && mobileConsole) {
    //creates the new HTML console element and attaches it to document
    consoleElement = initConsoleElement();
    }
    if (!commandLine && consoleElement && mobileConsole) {
    //creates an HTML commandline and attaches it to existing console element
    commandLine = initCommandLine();
    }
    //log a 'welcome' message
    console.info( '--==## Mobile Console v' + options.version + ' ' + (status.initialized ? 'active' : 'inactive' ) + ' ##==--' );
    } else if (options.browserinfo.isCrap) {
    console.error(
    '--==## Error: Browser not supported by Mobile Console ##==--' + '\n' +
    '--===============================--' + '\n' +
    'MobileConsole v' + options.version + ', running on ' + navigator.userAgent.toLowerCase()
    );
    }
    }
    } 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() + ').');
    }

    //autorun if mobile
    if (isMobile()) {
    init();
    }

    //expose the mobileConsole
    return {
    init : init,
    about: about,
    toggle : toggleHeight,
    status : status,
    options : options
    };

    }());
  16. @c-kick c-kick revised this gist Sep 30, 2016. 1 changed file with 5 additions and 6 deletions.
    11 changes: 5 additions & 6 deletions hnl.mobileConsole.js
    Original file line number Diff line number Diff line change
    @@ -530,7 +530,8 @@ if (window.console !== 'undefined') {
    borderBottom: '1px solid ' + (white ? '#EEEEEE' : '#777777'),
    whiteSpace: 'nowrap',
    overflow: 'hidden',
    direction: 'rtl'
    direction: 'rtl',
    width: th.mobileCheck() ? '35%' : '50%'
    });
    th.elems.link = th.createElem('A', 'hnlMobileConsole_link', {
    color: (white ? '#0000EE' : '#AAC1D2')
    @@ -582,29 +583,27 @@ if (window.console !== 'undefined') {
    position: 'absolute',
    bottom: 0,
    left: 0,
    right: '17px',
    right: th.mobileCheck() ? 0 : '17px',
    height: '25px',
    borderTop: '1px solid ' + (white ? '#EEEEEE' : '#777777')
    });
    th.elems.inputIndicator = th.createElem('DIV', 'hnlMobileConsole_inputIndicator', {
    position: 'absolute',
    bottom: 0,
    width: '25px',
    height: '25px',
    textAlign: 'center',
    lineHeight: '25px',
    padding: '6px 8px',
    color: white ? '#3577B1' : '#70bcff',
    fontSize: '16px',
    fontFamily: 'monospace',
    fontWeight: 'bold'
    });
    th.elems.consoleInput = th.createElem('INPUT', 'hnlMobileConsole_input', {
    width : '100%',
    fontSize: '12px',
    fontSize: '16px', //prevents ios safari's zoom on focus
    backgroundColor: white ? '#FFFFFF' : '#222222',
    color: white ? '#000000' : '#EEEEEE',
    paddingLeft: '25px',
    lineHeight: '25px',
    border: '0 none'
    });
    th.elems.consoleInput.setAttribute('type', 'text');
  17. @c-kick c-kick revised this gist Sep 30, 2016. 1 changed file with 14 additions and 13 deletions.
    27 changes: 14 additions & 13 deletions hnl.mobileConsole.js
    Original file line number Diff line number Diff line change
    @@ -493,7 +493,7 @@ if (window.console !== 'undefined') {
    position: 'fixed',
    resize: 'none',
    fontSize: '12px',
    lineHeight: '12px',
    lineHeight: '14px',
    bottom: top ? 'auto' : 0,
    top: top ? (windowed ? '32px' : 0) : 'auto',
    right: windowed ? '70px' : 0,
    @@ -552,9 +552,10 @@ if (window.console !== 'undefined') {
    display: 'block',
    padding: '0 2px',
    overflow: 'hidden',
    color: white ? '#333333' : '#FFFFFF',
    backgroundColor: white ? '#DDDDDD' : '#222222',
    borderBottom: '1px solid ' + (white ? '#AAA' : '#111111'),
    color: white ? '#444444' : '#FFFFFF',
    backgroundColor: white ? '#f3f3f3' : '#222222',
    borderTop: '1px solid ' + (white ? '#a3a3a3' : '#111111'),
    borderBottom: '1px solid ' + (white ? '#a3a3a3' : '#111111'),
    webkitOverflowScrolling: 'touch'
    });
    th.elems.scrollContainer = th.createElem('DIV', 'hnlMobileConsole_scrolltcontainer', {
    @@ -563,18 +564,18 @@ if (window.console !== 'undefined') {
    display: 'block',
    height: '100%',
    overflow: 'hidden',
    overflowY: 'scroll',
    overflowY: 'auto',
    webkitOverflowScrolling: 'touch',
    paddingBottom: '25px'
    });
    th.elems.button = th.createElem('BUTTON', 'hnlMobileConsole_button', {
    display: 'inline-block',
    fontSize: '14px',
    padding: '6px 8px',
    fontSize: '12px',
    padding: '5px 8px',
    textAlign: 'center',
    marginRight: '5px',
    border: '0 none',
    backgroundColor: white ? '#DDDDDD' : '#111111'
    backgroundColor: 'transparent'
    });
    th.elems.inputDiv = th.createElem('DIV', 'hnlMobileConsole_inputDiv', {
    fontFamily: 'Consolas, monaco, monospace',
    @@ -592,7 +593,7 @@ if (window.console !== 'undefined') {
    height: '25px',
    textAlign: 'center',
    lineHeight: '25px',
    color: '#3577B1',
    color: white ? '#3577B1' : '#70bcff',
    fontSize: '16px',
    fontFamily: 'monospace',
    fontWeight: 'bold'
    @@ -681,7 +682,7 @@ if (window.console !== 'undefined') {
    };
    document.onkeydown = function (e) {
    var idx = th.historyIndex, hist = th.commandHistory;
    if (e.keyCode === 38 || e.keyCode === 40) {
    if ((e.keyCode === 38 || e.keyCode === 40) && e.target === th.elems.consoleInput) {
    e.preventDefault();
    if (e.keyCode === 40) {
    idx = idx + 1;
    @@ -710,12 +711,12 @@ if (window.console !== 'undefined') {
    /**
    * Handle focus
    */
    focusConsole: function (dir, toggle) {
    focusConsole: function (dir, hasFocus) {
    'use strict';
    var th = this;
    if (toggle !== undefined) { th.elems.consoleDiv.toggled = toggle; }
    th.elems.consoleDiv.hasFocus = (hasFocus !== undefined) ? hasFocus : th.elems.consoleDiv.hasFocus;
    th.setCSS(th.elems.consoleDiv, {
    opacity: (dir === 'in') ? 1 : ((th.elems.consoleDiv.toggled) ? 1 : '0.8')
    opacity: (th.elems.consoleDiv.hasFocus || th.elems.consoleDiv.toggled || dir === 'in') ? 1 : '0.8'
    });
    },

  18. @c-kick c-kick revised this gist Sep 30, 2016. 1 changed file with 6 additions and 2 deletions.
    8 changes: 6 additions & 2 deletions hnl.mobileConsole.js
    Original file line number Diff line number Diff line change
    @@ -107,7 +107,7 @@ if (window.console !== 'undefined') {
    Position: 'bottom', //or 'top' NOT IMPLMENTED
    Type: 'docked', //or 'window' NOT IMPLMENTED,
    AnimParams: 'all 200ms ease',
    Style: 'white' //or 'black',
    Style: 'white' //or 'black'
    },
    BrowserInfo: {
    browserChrome: /chrome/.test(navigator.userAgent.toLowerCase()),
    @@ -203,7 +203,10 @@ if (window.console !== 'undefined') {
    if (message === 'undefined') { color = '#808080'; }
    } else {
    color = '#EEEEEE';
    if (method === 'info' || typeof message === 'boolean') { color = '#a0eaff'; }
    if (method === 'warn') { color = '#CE8724'; }
    if (method === 'error') { color = '#ff4848'; }
    if (message === 'undefined') { color = '#afafaf'; }
    }

    //object requires some more handling
    @@ -597,7 +600,8 @@ if (window.console !== 'undefined') {
    th.elems.consoleInput = th.createElem('INPUT', 'hnlMobileConsole_input', {
    width : '100%',
    fontSize: '12px',
    color: '#000000',
    backgroundColor: white ? '#FFFFFF' : '#222222',
    color: white ? '#000000' : '#EEEEEE',
    paddingLeft: '25px',
    lineHeight: '25px',
    border: '0 none'
  19. @c-kick c-kick revised this gist Sep 30, 2016. 1 changed file with 123 additions and 33 deletions.
    156 changes: 123 additions & 33 deletions hnl.mobileConsole.js
    Original file line number Diff line number Diff line change
    @@ -1,5 +1,5 @@
    /*!
    * hnl.mobileConsole - javascript mobile console - v0.5.0 - 29/9/2016
    * hnl.mobileConsole - javascript mobile console - v0.5.1 - 30/9/2016
    * Adds html console to webpage. Especially useful for debugging JS on mobile devices.
    * Supports .log, .info, .warn, .error and .trace
    * Based on code by jakub fiala (https://gist.github.com/jakubfiala/8fe3461ab6508f46003d)
    @@ -107,7 +107,7 @@ if (window.console !== 'undefined') {
    Position: 'bottom', //or 'top' NOT IMPLMENTED
    Type: 'docked', //or 'window' NOT IMPLMENTED,
    AnimParams: 'all 200ms ease',
    Style: 'white' //or 'black'
    Style: 'white' //or 'black',
    },
    BrowserInfo: {
    browserChrome: /chrome/.test(navigator.userAgent.toLowerCase()),
    @@ -122,8 +122,8 @@ if (window.console !== 'undefined') {
    lines : []
    },
    built: false,
    commandHistory: [],
    historyIndex: 0,
    commandHistory: window.sessionStorage ? (sessionStorage.getItem('ConsoleCommandHistory') ? JSON.parse(sessionStorage.getItem('ConsoleCommandHistory')) : []) : [],
    historyIndex: window.sessionStorage ? (sessionStorage.getItem('ConsoleCommandHistory') ? JSON.parse(sessionStorage.getItem('ConsoleCommandHistory')).length - 1 : 0) : 0,

    //constructor
    MobileConsole: function () {
    @@ -198,7 +198,7 @@ if (window.console !== 'undefined') {
    var color = '#000000';
    if (mc.Options.Style === 'white') {
    if (method === 'error') { color = '#FF0000'; }
    if (method === 'info') { color = '#1f3dc4'; }
    if (method === 'info' || typeof message === 'boolean') { color = '#1f3dc4'; }
    if (method === 'warn') { color = '#CE8724'; }
    if (message === 'undefined') { color = '#808080'; }
    } else {
    @@ -208,9 +208,13 @@ if (window.console !== 'undefined') {

    //object requires some more handling
    if (typeof message === 'object') {
    message = 'Object ' + mc.objectStringify(message);
    mc.console_old.log(message);
    message = mc.objectStringify(message);
    } else {
    message = mc.htmlStringify(message);
    }


    if ((this.prevMsg !== message || this.prevMethod !== method) || method === 'trace') {
    //message is not a repeat of the previous
    var cleanUrl, url;
    @@ -230,7 +234,11 @@ if (window.console !== 'undefined') {
    } else {
    url = origin.substring(origin.indexOf('(') + 1, origin.indexOf(')'));
    }
    cleanUrl = url.split(':')[0] + ':' + url.split(':')[1];
    if (url !== undefined) {
    cleanUrl = url.split(':')[0] + ':' + url.split(':')[1];
    } else {
    cleanUrl = '';
    }
    }
    }

    @@ -366,22 +374,64 @@ if (window.console !== 'undefined') {
    return el;
    },

    /**
    * check if is DOM node element. Taken from: http://stackoverflow.com/questions/384286/javascript-isdom-how-do-you-check-if-a-javascript-object-is-a-dom-object
    * @param o
    * @returns {boolean}
    */
    isNode: function (o) {
    return (
    typeof Node === 'object' ? o instanceof Node :
    o && typeof o === 'object' && typeof o.nodeType === 'number' && typeof o.nodeName==='string'
    );
    },

    /**
    * check if is element. Taken from: http://stackoverflow.com/questions/384286/javascript-isdom-how-do-you-check-if-a-javascript-object-is-a-dom-object
    * @param o
    * @returns {boolean}
    */
    isElement: function (o) {
    return (
    typeof HTMLElement === 'object' ? o instanceof HTMLElement : //DOM2
    o && typeof o === 'object' && o !== null && o.nodeType === 1 && typeof o.nodeName==='string'
    );
    },

    /**
    * Simple stringer for html
    * @param str
    * @returns {string}
    */
    htmlStringify: function (str) {
    return String(str).replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/"/g, '&quot;');
    },

    /**
    * Simple object stringer
    * @param object
    * @returns {*|string}
    */
    objectStringify: function (object) {
    'use strict';
    var simpleObject = {}, prop;
    for (prop in object) {
    if (!object.hasOwnProperty(prop)) {
    continue;
    }
    if (typeof(object[prop]) === 'object') {
    continue;
    }
    if (typeof(object[prop]) === 'function') {
    continue;
    if (!this.isElement(object)) {
    for (prop in object) {
    if (!object.hasOwnProperty(prop)) {
    continue;
    }
    if (typeof(object[prop]) === 'object') {
    continue;
    }
    if (typeof(object[prop]) === 'function') {
    continue;
    }
    simpleObject[prop] = object[prop];
    }
    simpleObject[prop] = object[prop];
    return '<em>Object ' + JSON.stringify(simpleObject) + '</em>'; // returns cleaned up JSON
    }
    return JSON.stringify(simpleObject); // returns cleaned up JSON
    return this.htmlStringify(object.outerHTML);
    //
    },

    /**
    @@ -435,17 +485,17 @@ if (window.console !== 'undefined') {
    buildConsole: function () {
    'use strict';
    var th = this;
    var white = (th.Options.Style === 'white'), top = (th.Options.Position === 'top'), window = (th.Options.Type === 'window');
    var white = (th.Options.Style === 'white'), top = (th.Options.Position === 'top'), windowed = (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,
    top: top ? (windowed ? '32px' : 0) : 'auto',
    right: windowed ? '70px' : 0,
    backgroundColor: white ? '#ffffff' : '#333333',
    width: window ? '70%' : '100%',
    width: windowed ? '70%' : '100%',
    height: '127px',
    zIndex: 10000,
    padding: 0,
    @@ -468,7 +518,8 @@ if (window.console !== 'undefined') {
    th.elems.tdLeft = th.createElem('TD', 'hnlMobileConsole_table_row_data', {
    textAlign: 'left',
    padding: '2px 4px',
    borderBottom: '1px solid ' + (white ? '#EEEEEE' : '#777777')
    borderBottom: '1px solid ' + (white ? '#EEEEEE' : '#777777'),
    wordBreak: 'break-all'
    });
    th.elems.tdRight = th.createElem('TD', 'hnlMobileConsole_table_row_data', {
    textAlign: 'left',
    @@ -545,10 +596,13 @@ if (window.console !== 'undefined') {
    });
    th.elems.consoleInput = th.createElem('INPUT', 'hnlMobileConsole_input', {
    width : '100%',
    fontSize: '12px',
    color: '#000000',
    paddingLeft: '25px',
    lineHeight: '25px',
    border: '0 none'
    });
    th.elems.consoleInput.setAttribute('type', 'text');
    th.elems.consoleInput.setAttribute('autocapitalize', 'off');
    th.elems.consoleInput.setAttribute('autocorrect', 'off');

    @@ -598,8 +652,9 @@ if (window.console !== 'undefined') {
    if ((e.key === 'Enter' || e.keyCode === 13) && e.target === th.elems.consoleInput) {
    var command = th.elems.consoleInput.value;
    if (command !== '') {
    MobileConsole.commandHistory.push(command);
    MobileConsole.historyIndex = MobileConsole.commandHistory.length;
    th.commandHistory.push(encodeURI(command));
    th.historyIndex = th.commandHistory.length;
    if (window.sessionStorage) { sessionStorage.setItem('ConsoleCommandHistory', JSON.stringify(th.commandHistory)); }
    var result;
    try {
    result = eval.call(window, command);
    @@ -608,9 +663,9 @@ if (window.console !== 'undefined') {
    var stackTraceOrig, stackTrace;
    try { throw new Error(); } catch(e) { stackTraceOrig = e.stack; }
    if (stackTrace === undefined && stackTraceOrig) {
    stackTrace = MobileConsole.stackTraceFormat(stackTraceOrig);
    stackTrace = th.stackTraceFormat(stackTraceOrig);
    }
    MobileConsole.consoleDiv.prototype.theConsole('log', (result !== '' ? 'undefined' : ' '), stackTrace, stackTraceOrig);
    th.consoleDiv.prototype.theConsole('log', (result !== '' ? 'undefined' : ' '), stackTrace, stackTraceOrig);
    }
    } catch(e) {
    console.error(e.message);
    @@ -621,7 +676,7 @@ if (window.console !== 'undefined') {
    }
    };
    document.onkeydown = function (e) {
    var idx = MobileConsole.historyIndex, hist = MobileConsole.commandHistory;
    var idx = th.historyIndex, hist = th.commandHistory;
    if (e.keyCode === 38 || e.keyCode === 40) {
    e.preventDefault();
    if (e.keyCode === 40) {
    @@ -634,14 +689,42 @@ if (window.console !== 'undefined') {
    } else if (idx < 0) {
    idx = 0;
    }
    th.elems.consoleInput.value = hist[idx] === undefined ? '' : hist[idx];
    MobileConsole.historyIndex = idx;
    th.elems.consoleInput.value = hist[idx] === undefined ? '' : decodeURI(hist[idx]);
    th.historyIndex = idx;
    }
    };

    // handle focus
    th.elems.consoleInput.onfocus = function () {
    th.focusConsole('in', true);
    };
    th.elems.consoleDiv.onmouseover = function () { th.focusConsole('in'); };
    th.elems.consoleDiv.onmouseout = function () { th.focusConsole('out'); };
    th.elems.consoleInput.onblur = function () { th.focusConsole('out', false); };
    return th.elems.consoleDiv;
    },

    /**
    * Handle focus
    */
    focusConsole: function (dir, toggle) {
    'use strict';
    var th = this;
    if (toggle !== undefined) { th.elems.consoleDiv.toggled = toggle; }
    th.setCSS(th.elems.consoleDiv, {
    opacity: (dir === 'in') ? 1 : ((th.elems.consoleDiv.toggled) ? 1 : '0.8')
    });
    },

    /**
    * See if mobile device (dodgy, but covers 99%)
    * Source: http://stackoverflow.com/questions/11381673/detecting-a-mobile-browser/11381730#11381730
    */
    mobileCheck : function() {
    var check = false;
    (function(a){if(/(android|bb\d+|meego).+mobile|avantgo|bada\/|blackberry|blazer|compal|elaine|fennec|hiptop|iemobile|ip(hone|od)|iris|kindle|lge |maemo|midp|mmp|mobile.+firefox|netfront|opera m(ob|in)i|palm( os)?|phone|p(ixi|re)\/|plucker|pocket|psp|series(4|6)0|symbian|treo|up\.(browser|link)|vodafone|wap|windows ce|xda|xiino/i.test(a)||/1207|6310|6590|3gso|4thp|50[1-6]i|770s|802s|a wa|abac|ac(er|oo|s\-)|ai(ko|rn)|al(av|ca|co)|amoi|an(ex|ny|yw)|aptu|ar(ch|go)|as(te|us)|attw|au(di|\-m|r |s )|avan|be(ck|ll|nq)|bi(lb|rd)|bl(ac|az)|br(e|v)w|bumb|bw\-(n|u)|c55\/|capi|ccwa|cdm\-|cell|chtm|cldc|cmd\-|co(mp|nd)|craw|da(it|ll|ng)|dbte|dc\-s|devi|dica|dmob|do(c|p)o|ds(12|\-d)|el(49|ai)|em(l2|ul)|er(ic|k0)|esl8|ez([4-7]0|os|wa|ze)|fetc|fly(\-|_)|g1 u|g560|gene|gf\-5|g\-mo|go(\.w|od)|gr(ad|un)|haie|hcit|hd\-(m|p|t)|hei\-|hi(pt|ta)|hp( i|ip)|hs\-c|ht(c(\-| |_|a|g|p|s|t)|tp)|hu(aw|tc)|i\-(20|go|ma)|i230|iac( |\-|\/)|ibro|idea|ig01|ikom|im1k|inno|ipaq|iris|ja(t|v)a|jbro|jemu|jigs|kddi|keji|kgt( |\/)|klon|kpt |kwc\-|kyo(c|k)|le(no|xi)|lg( g|\/(k|l|u)|50|54|\-[a-w])|libw|lynx|m1\-w|m3ga|m50\/|ma(te|ui|xo)|mc(01|21|ca)|m\-cr|me(rc|ri)|mi(o8|oa|ts)|mmef|mo(01|02|bi|de|do|t(\-| |o|v)|zz)|mt(50|p1|v )|mwbp|mywa|n10[0-2]|n20[2-3]|n30(0|2)|n50(0|2|5)|n7(0(0|1)|10)|ne((c|m)\-|on|tf|wf|wg|wt)|nok(6|i)|nzph|o2im|op(ti|wv)|oran|owg1|p800|pan(a|d|t)|pdxg|pg(13|\-([1-8]|c))|phil|pire|pl(ay|uc)|pn\-2|po(ck|rt|se)|prox|psio|pt\-g|qa\-a|qc(07|12|21|32|60|\-[2-7]|i\-)|qtek|r380|r600|raks|rim9|ro(ve|zo)|s55\/|sa(ge|ma|mm|ms|ny|va)|sc(01|h\-|oo|p\-)|sdk\/|se(c(\-|0|1)|47|mc|nd|ri)|sgh\-|shar|sie(\-|m)|sk\-0|sl(45|id)|sm(al|ar|b3|it|t5)|so(ft|ny)|sp(01|h\-|v\-|v )|sy(01|mb)|t2(18|50)|t6(00|10|18)|ta(gt|lk)|tcl\-|tdg\-|tel(i|m)|tim\-|t\-mo|to(pl|sh)|ts(70|m\-|m3|m5)|tx\-9|up(\.b|g1|si)|utst|v400|v750|veri|vi(rg|te)|vk(40|5[0-3]|\-v)|vm40|voda|vulc|vx(52|53|60|61|70|80|81|83|85|98)|w3c(\-| )|webc|whit|wi(g |nc|nw)|wmlb|wonu|x700|yas\-|your|zeto|zte\-/i.test(a.substr(0,4))) check = true;})(navigator.userAgent||navigator.vendor||window.opera);
    return check;
    },

    /**
    * Attaches console to the document body
    */
    @@ -750,7 +833,9 @@ if (window.console !== 'undefined') {
    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);
    docReady(function () {
    th.attachConsole();
    });
    } else {
    //if no autorun: attach the built console
    th.attachConsole();
    @@ -766,7 +851,12 @@ if (window.console !== 'undefined') {
    if (MobileConsole.Options.AutoRun) {
    MobileConsole.init(true);
    } else {
    console.warn('MobileConsole loaded but not displaying, AutoRun disabled. To initialize, run MobileConsole.init();');
    //run on mobile devices, if no autorun is explicitly defined
    if (MobileConsole.mobileCheck()) {
    MobileConsole.init(true);
    } else {
    console.warn('MobileConsole loaded but not displaying as AutoRun is disabled (and this is not a mobile device). To initialize, run MobileConsole.init();');
    }
    }
    } else {
    //there is no console. Throw error.
  20. @c-kick c-kick revised this gist Sep 30, 2016. 1 changed file with 118 additions and 13 deletions.
    131 changes: 118 additions & 13 deletions hnl.mobileConsole.js
    Original file line number Diff line number Diff line change
    @@ -1,5 +1,5 @@
    /*!
    * hnl.mobileConsole - javascript mobile console - v0.4.5 - 28/9/2016
    * hnl.mobileConsole - javascript mobile console - v0.5.0 - 29/9/2016
    * Adds html console to webpage. Especially useful for debugging JS on mobile devices.
    * Supports .log, .info, .warn, .error and .trace
    * Based on code by jakub fiala (https://gist.github.com/jakubfiala/8fe3461ab6508f46003d)
    @@ -14,7 +14,6 @@
    * 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;
    @@ -94,7 +93,7 @@ if (window.console !== 'undefined') {
    }

    var MobileConsole = {
    Version: '0.4',
    Version: '0.5',
    consoleDiv: undefined,
    console_old : {
    log : console.log.bind(console),
    @@ -119,7 +118,12 @@ if (window.console !== 'undefined') {
    supportsElsByCln: document.getElementsByClassName !== undefined,
    isCrap: document.querySelectorAll === undefined
    },
    elems: {},
    elems: {
    lines : []
    },
    built: false,
    commandHistory: [],
    historyIndex: 0,

    //constructor
    MobileConsole: function () {
    @@ -134,7 +138,7 @@ if (window.console !== 'undefined') {
    this.toggle = function () {
    var elem = mc.elems.consoleDiv;
    mc.setCSS(elem, {
    height: (elem.toggled) ? '102px' : '262px',
    height: (elem.toggled) ? '127px' : '262px',
    opacity: (elem.toggled) ? '0.8' : 1
    });
    this.innerHTML = (elem.toggled) ? '&plus;' : '&minus;';
    @@ -196,14 +200,15 @@ if (window.console !== 'undefined') {
    if (method === 'error') { color = '#FF0000'; }
    if (method === 'info') { color = '#1f3dc4'; }
    if (method === 'warn') { color = '#CE8724'; }
    if (message === 'undefined') { color = '#808080'; }
    } else {
    color = '#EEEEEE';
    if (method === 'error') { color = '#ff4848'; }
    }

    //object requires some more handling
    if (typeof message === 'object') {
    message = 'Object ' + JSON.stringify(message);
    message = 'Object ' + mc.objectStringify(message);
    }

    if ((this.prevMsg !== message || this.prevMethod !== method) || method === 'trace') {
    @@ -361,6 +366,24 @@ if (window.console !== 'undefined') {
    return el;
    },

    objectStringify: function (object) {
    'use strict';
    var simpleObject = {}, prop;
    for (prop in object) {
    if (!object.hasOwnProperty(prop)) {
    continue;
    }
    if (typeof(object[prop]) === 'object') {
    continue;
    }
    if (typeof(object[prop]) === 'function') {
    continue;
    }
    simpleObject[prop] = object[prop];
    }
    return JSON.stringify(simpleObject); // returns cleaned up JSON
    },

    /**
    * Linkify passed string content
    * @returns {XML|string}
    @@ -423,7 +446,7 @@ if (window.console !== 'undefined') {
    right: window ? '70px' : 0,
    backgroundColor: white ? '#ffffff' : '#333333',
    width: window ? '70%' : '100%',
    height: '102px',
    height: '127px',
    zIndex: 10000,
    padding: 0,
    margin: 0,
    @@ -435,7 +458,7 @@ if (window.console !== 'undefined') {
    'transition': th.Options.AnimParams
    });
    th.elems.consoleBodyTable = th.createElem('DIV', 'hnlMobileConsole_table', {
    fontFamily: 'monospace',
    position: 'relative',
    maxWidth: '100%',
    display: 'table',
    tableLayout: 'fixed',
    @@ -481,12 +504,14 @@ if (window.console !== 'undefined') {
    webkitOverflowScrolling: 'touch'
    });
    th.elems.scrollContainer = th.createElem('DIV', 'hnlMobileConsole_scrolltcontainer', {
    fontFamily: 'Consolas, monaco, monospace',
    position: 'relative',
    display: 'block',
    height: '100%',
    overflow: 'hidden',
    overflowY: 'scroll',
    webkitOverflowScrolling: 'touch'
    webkitOverflowScrolling: 'touch',
    paddingBottom: '25px'
    });
    th.elems.button = th.createElem('BUTTON', 'hnlMobileConsole_button', {
    display: 'inline-block',
    @@ -497,9 +522,36 @@ if (window.console !== 'undefined') {
    border: '0 none',
    backgroundColor: white ? '#DDDDDD' : '#111111'
    });
    th.elems.inputDiv = th.createElem('DIV', 'hnlMobileConsole_inputDiv', {
    fontFamily: 'Consolas, monaco, monospace',
    position: 'absolute',
    bottom: 0,
    left: 0,
    right: '17px',
    height: '25px',
    borderTop: '1px solid ' + (white ? '#EEEEEE' : '#777777')
    });
    th.elems.inputIndicator = th.createElem('DIV', 'hnlMobileConsole_inputIndicator', {
    position: 'absolute',
    bottom: 0,
    width: '25px',
    height: '25px',
    textAlign: 'center',
    lineHeight: '25px',
    color: '#3577B1',
    fontSize: '16px',
    fontFamily: 'monospace',
    fontWeight: 'bold'
    });
    th.elems.consoleInput = th.createElem('INPUT', 'hnlMobileConsole_input', {
    width : '100%',
    paddingLeft: '25px',
    lineHeight: '25px',
    border: '0 none'
    });
    th.elems.consoleInput.setAttribute('autocapitalize', 'off');
    th.elems.consoleInput.setAttribute('autocorrect', 'off');

    //set up lines object
    th.elems.lines = [];
    //set up buttons object
    th.elems.buttons = [];

    @@ -531,11 +583,61 @@ if (window.console !== 'undefined') {
    th.elems.buttons.clear.innerHTML = 'Clear';
    th.elems.topBar.appendChild(th.elems.buttons.clear);

    th.elems.inputDiv.appendChild(th.elems.inputIndicator);
    th.elems.inputDiv.appendChild(th.elems.consoleInput);
    th.elems.inputIndicator.innerHTML = '&gt;';

    //construct elements
    th.elems.consoleDiv.appendChild(th.elems.topBar);
    th.elems.scrollContainer.appendChild(th.elems.consoleBodyTable);
    th.elems.consoleDiv.appendChild(th.elems.scrollContainer);

    th.elems.consoleDiv.appendChild(th.elems.inputDiv);

    // Console emulation
    document.onkeypress = function (e) {
    if ((e.key === 'Enter' || e.keyCode === 13) && e.target === th.elems.consoleInput) {
    var command = th.elems.consoleInput.value;
    if (command !== '') {
    MobileConsole.commandHistory.push(command);
    MobileConsole.historyIndex = MobileConsole.commandHistory.length;
    var result;
    try {
    result = eval.call(window, command);
    console.log(result);
    if (result === undefined || result === 'undefined' || result === '') {
    var stackTraceOrig, stackTrace;
    try { throw new Error(); } catch(e) { stackTraceOrig = e.stack; }
    if (stackTrace === undefined && stackTraceOrig) {
    stackTrace = MobileConsole.stackTraceFormat(stackTraceOrig);
    }
    MobileConsole.consoleDiv.prototype.theConsole('log', (result !== '' ? 'undefined' : ' '), stackTrace, stackTraceOrig);
    }
    } catch(e) {
    console.error(e.message);
    } finally {
    th.elems.consoleInput.value = '';
    }
    }
    }
    };
    document.onkeydown = function (e) {
    var idx = MobileConsole.historyIndex, hist = MobileConsole.commandHistory;
    if (e.keyCode === 38 || e.keyCode === 40) {
    e.preventDefault();
    if (e.keyCode === 40) {
    idx = idx + 1;
    } else if (e.keyCode === 38) {
    idx = idx - 1;
    }
    if (idx > hist.length) {
    idx = hist.length;
    } else if (idx < 0) {
    idx = 0;
    }
    th.elems.consoleInput.value = hist[idx] === undefined ? '' : hist[idx];
    MobileConsole.historyIndex = idx;
    }
    };

    return th.elems.consoleDiv;
    },
    @@ -638,7 +740,10 @@ if (window.console !== 'undefined') {
    'use strict';
    var th = this;
    //don't go when we have already built it
    if (th.consoleDiv !== undefined) { return; }
    if (th.consoleDiv !== undefined || th.built) {
    th.console_old.warn('MobileConsole already built');
    return;
    }
    //build the console DIV
    th.consoleDiv = th.buildConsole();
    //attach prototype to the console (and take over console events)
  21. @c-kick c-kick revised this gist Sep 28, 2016. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion hnl.mobileConsole.js
    Original file line number Diff line number Diff line change
    @@ -1,7 +1,7 @@
    /*!
    * hnl.mobileConsole - javascript mobile console - v0.4.5 - 28/9/2016
    * Adds html console to webpage. Especially useful for debugging JS on mobile devices.
    * Supports .log, .warn, .error and .trace
    * Supports .log, .info, .warn, .error and .trace
    * Based on code by jakub fiala (https://gist.github.com/jakubfiala/8fe3461ab6508f46003d)
    * Licensed under the MIT license
    *
  22. @c-kick c-kick revised this gist Sep 28, 2016. 1 changed file with 8 additions and 1 deletion.
    9 changes: 8 additions & 1 deletion hnl.mobileConsole.js
    Original file line number Diff line number Diff line change
    @@ -1,5 +1,5 @@
    /*!
    * hnl.mobileConsole - javascript mobile console - v0.4.4 - 5/9/2016
    * hnl.mobileConsole - javascript mobile console - v0.4.5 - 28/9/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)
    @@ -10,6 +10,7 @@
    * Copyright (c) 2014-2016 HN Leussink
    * Dual licensed under the MIT and GPL licenses.
    *
    * Info: http://www.hnldesign.nl/work/code/javascript-mobile-console/
    * Demo: http://code.hnldesign.nl/demo/hnl.MobileConsole.html
    */

    @@ -199,6 +200,12 @@ if (window.console !== 'undefined') {
    color = '#EEEEEE';
    if (method === 'error') { color = '#ff4848'; }
    }

    //object requires some more handling
    if (typeof message === 'object') {
    message = 'Object ' + JSON.stringify(message);
    }

    if ((this.prevMsg !== message || this.prevMethod !== method) || method === 'trace') {
    //message is not a repeat of the previous
    var cleanUrl, url;
  23. @c-kick c-kick revised this gist Sep 5, 2016. 1 changed file with 27 additions and 6 deletions.
    33 changes: 27 additions & 6 deletions hnl.mobileConsole.js
    Original file line number Diff line number Diff line change
    @@ -1,5 +1,5 @@
    /*!
    * hnl.mobileConsole - javascript mobile console - v0.4.3 - 18/5/2016
    * hnl.mobileConsole - javascript mobile console - v0.4.4 - 5/9/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)
    @@ -97,6 +97,7 @@ if (window.console !== 'undefined') {
    consoleDiv: undefined,
    console_old : {
    log : console.log.bind(console),
    info : console.info.bind(console),
    warn : console.warn.bind(console),
    error : console.error.bind(console),
    trace : console.trace.bind(console)
    @@ -157,7 +158,7 @@ if (window.console !== 'undefined') {
    this.empty = function (e) {
    console.clear();
    mc.elems.consoleBodyTable.innerHTML = '';
    var methods = ['log', 'warn', 'error', 'trace'];
    var methods = ['log', 'trace', 'info', 'warn', 'error'];
    for (var i = 0; i < methods.length; i++) {
    mc.elems.lines[methods[i]] = [];
    }
    @@ -189,8 +190,16 @@ if (window.console !== 'undefined') {
    */
    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') {
    var color = '#000000';
    if (mc.Options.Style === 'white') {
    if (method === 'error') { color = '#FF0000'; }
    if (method === 'info') { color = '#1f3dc4'; }
    if (method === 'warn') { color = '#CE8724'; }
    } else {
    color = '#EEEEEE';
    if (method === 'error') { color = '#ff4848'; }
    }
    if ((this.prevMsg !== message || this.prevMethod !== method) || method === 'trace') {
    //message is not a repeat of the previous
    var cleanUrl, url;
    if(stackTrace !== undefined) {
    @@ -269,9 +278,15 @@ if (window.console !== 'undefined') {
    }
    mc.elems.consoleBodyTable.lastChild.countDot.innerHTML = this.c.toString();
    } else {
    console.error('not logging. Why?');
    // not logging. Why?
    }
    }
    /*
    console.info('method: ' + method);
    console.info('message: ' + message);
    console.info('prevMethod: ' + this.prevMethod);
    console.info('prevMsg: ' + this.prevMsg);
    */
    };

    //setup binds
    @@ -290,13 +305,15 @@ if (window.console !== 'undefined') {
    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.info.addEventListener('click', function () { th.toggleLogtype('info', 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.info.attachEvent('onclick', function () { th.toggleLogtype('info', 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(); });
    @@ -315,7 +332,7 @@ if (window.console !== 'undefined') {
    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'];
    var methods = ['log', 'info', 'warn', 'error', 'trace'];
    for (var i = 0; i < methods.length; i++) {
    mc.elems.lines[methods[i]] = [];
    mc.interceptConsole(methods[i]);
    @@ -487,6 +504,10 @@ if (window.console !== 'undefined') {
    th.elems.buttons.log.innerHTML = 'Log';
    th.elems.topBar.appendChild(th.elems.buttons.log);

    th.elems.buttons.info = th.elems.button.cloneNode(false);
    th.elems.buttons.info.innerHTML = 'Info';
    th.elems.topBar.appendChild(th.elems.buttons.info);

    th.elems.buttons.warn = th.elems.button.cloneNode(false);
    th.elems.buttons.warn.innerHTML = 'Warn';
    th.elems.topBar.appendChild(th.elems.buttons.warn);
  24. @c-kick c-kick revised this gist May 19, 2016. 1 changed file with 2 additions and 2 deletions.
    4 changes: 2 additions & 2 deletions hnl.mobileConsole.js
    Original file line number Diff line number Diff line change
    @@ -1,5 +1,5 @@
    /*!
    * hnl.mobileConsole - javascript mobile console - v0.4.2 - 18/5/2016
    * hnl.mobileConsole - javascript mobile console - v0.4.3 - 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)
    @@ -198,7 +198,7 @@ if (window.console !== 'undefined') {
    //this is window.onerror
    url = stackTrace.url + ':' + stackTrace.line + ':' + stackTrace.column;
    cleanUrl = stackTrace.url;
    } else {
    } else if (stackTrace.objTrace !== undefined) {
    var origin = (stackTrace.objTrace[1] === 'global') ? stackTrace.objTrace[0] : stackTrace.objTrace[1];
    if (mc.BrowserInfo.safari || mc.BrowserInfo.ffox) {
    if (/@/i.test(origin)) {
  25. @c-kick c-kick revised this gist May 18, 2016. 1 changed file with 2 additions and 2 deletions.
    4 changes: 2 additions & 2 deletions hnl.mobileConsole.js
    Original file line number Diff line number Diff line change
    @@ -15,8 +15,8 @@


    //check if the browser has a console. If not (older Firefox without Firebug): stop.
    var console = window.console;
    if (console) {
    if (window.console !== 'undefined') {
    var console = window.console;
    /**
    * DocReady - Replacement for jQuery's $(document).ready() event
    */
  26. @c-kick c-kick revised this gist May 18, 2016. 1 changed file with 586 additions and 571 deletions.
    1,157 changes: 586 additions & 571 deletions hnl.mobileConsole.js
    Original file line number Diff line number Diff line change
    @@ -1,5 +1,5 @@
    /*!
    * hnl.mobileConsole - javascript mobile console - v0.4 - 17/5/2016
    * 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)
    @@ -13,614 +13,629 @@
    * Demo: http://code.hnldesign.nl/demo/hnl.MobileConsole.html
    */

    /**
    * 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);

    //check if the browser has a console. If not (older Firefox without Firebug): stop.
    var console = window.console;
    if (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 = [];
    }
    // allow any closures held by these functions to free
    readyList = [];
    }
    }

    function readyStateChange() {
    if (document.readyState === 'complete') {
    ready();
    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);
    // 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 {
    // must be IE
    document.attachEvent('onreadystatechange', readyStateChange);
    window.attachEvent('onload', ready);
    // add the function and context to the list
    readyList.push({fn: callback, ctx: context});
    }
    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'
    Type: 'docked', //or 'window',
    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()),
    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;
    // 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);
    }

    /**
    * 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) ? '&plus;' : '&minus;';
    elem.toggled = !elem.toggled;
    return elem;
    };
    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) ? '&plus;' : '&minus;';
    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;
    };
    /**
    * 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]] = [];
    }
    };
    /**
    * 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' : '' });
    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++;
    }
    x++;
    }
    mc.elems.scrollContainer.scrollTop = mc.elems.scrollContainer.scrollHeight;
    return button;
    };
    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.url && stackTrace.line && stackTrace.column) {
    //this is window.onerror
    url = stackTrace.url + ':' + stackTrace.line;
    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];
    /**
    * 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 {
    url = origin;
    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];
    }
    } 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 !== '') {
    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') {
    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
    //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' : '')
    });
    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.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?');
    }
    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);
    }
    };

    //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
    //check if the browser has a console. If not: stop.
    var console = window.console;
    if (!console) { return; }
    //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
    //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);
    };
    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]);
    }
    },
    //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;
    },
    /**
    * 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>');
    },
    /**
    * 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);
    },
    /**
    * 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%' : '',
    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 = '&plus;';
    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;
    },
    /**
    * 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'
    });

    /**
    * 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'
    });
    MobileConsole.elems.scrollContainer.scrollTop = MobileConsole.elems.scrollContainer.scrollHeight;
    },
    //set up lines object
    th.elems.lines = [];
    //set up buttons object
    th.elems.buttons = [];

    /**
    * Formats a stacktrace to human readable HTML
    * @param stackTrace
    * @returns {{}}
    */
    stackTraceFormat: function (stackTrace) {
    'use strict';
    var th = this;
    var returnObj = {};
    if (th.BrowserInfo.browserChrome) {
    //we first format the string a bit
    stackTrace = stackTrace.replace("Error", "")
    .replace(/\r?\n|\r/g, '')
    .replace(' ', '')
    .replace(/</gm, '&lt;')
    .replace(/>/gm, '&gt;');
    //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;
    th.elems.buttons.toggler = th.elems.button.cloneNode(false);
    th.elems.buttons.toggler.innerHTML = '&plus;';
    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, '&lt;')
    .replace(/>/gm, '&gt;');
    //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;
    }
    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, '&commat;');

    //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;
    //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, '&commat;');

    //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 = new Error().stack;
    if (stackTrace === undefined) {
    //if no stacktrace defined (window.onerror defines one), use the generated one
    stackTrace = MobileConsole.stackTraceFormat(stackTraceOrig);
    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();
    }
    //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;
    }
    //return the built console
    return th.consoleDiv;
    }
    };
    };

    if (MobileConsole.Options.AutoRun) {
    MobileConsole.init(true);
    if (MobileConsole.Options.AutoRun) {
    MobileConsole.init(true);
    } else {
    console.warn('MobileConsole loaded but not displaying, AutoRun disabled. To initialize, run MobileConsole.init();');
    }
    } else {
    console.warn('MobileConsole initialized but not displaying, AutoRun disabled. To enable, run MobileConsole.init();');
    //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() + ').');
    }
  27. @c-kick c-kick revised this gist May 18, 2016. 1 changed file with 65 additions and 62 deletions.
    127 changes: 65 additions & 62 deletions hnl.mobileConsole.js
    Original file line number Diff line number Diff line change
    @@ -10,80 +10,83 @@
    * Copyright (c) 2014-2016 HN Leussink
    * Dual licensed under the MIT and GPL licenses.
    *
    * Demo: http://code.hnldesign.nl/demo/hnl.MobileConsole.html
    */

    /**
    * DocReady - Replacement for jQuery's $(document).ready() event
    */
    (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);
    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 = [];
    }
    // allow any closures held by these functions to free
    readyList = [];
    }
    }

    function readyStateChange() {
    if (document.readyState === 'complete') {
    ready();
    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);
    // 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 {
    // must be IE
    document.attachEvent('onreadystatechange', readyStateChange);
    window.attachEvent('onload', ready);
    // add the function and context to the list
    readyList.push({fn: callback, ctx: context});
    }
    readyEventHandlersInstalled = true;
    }
    };
    })('docReady', window);
    // 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',
  28. @c-kick c-kick revised this gist May 17, 2016. 1 changed file with 16 additions and 16 deletions.
    32 changes: 16 additions & 16 deletions hnl.mobileConsole.js
    Original file line number Diff line number Diff line change
    @@ -181,7 +181,7 @@ var MobileConsole = {
    */
    this.theConsole = function (method, message, stackTrace, stackTraceOrig) {
    if (message !== '' && message !== undefined) {
    var color = (method === 'error') ? ((mc.Options.Style === 'white') ? '#FF0000' : '#ff4848') : ((method === 'warn') ? '#bc7a1e' : ((mc.Options.Style === 'white') ? '#000000' : '#EEEEEE'));
    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;
    @@ -226,7 +226,7 @@ var MobileConsole = {

    //put message in container
    var lineContainer = mc.elems.tr.cloneNode(false);
    mc.setCSS(lineContainer, {backgroundColor: (method ==='warn') ? '#FFF6E0' : (method === 'error' ? '#ffe5e5' : '')});
    if (mc.Options.Style === 'white') { mc.setCSS(lineContainer, {backgroundColor: (method ==='warn') ? '#FFF6E0' : (method === 'error' ? '#ffe5e5' : '')}); }
    lineContainer.appendChild(messageContainer);
    lineContainer.appendChild(linkContainer);

    @@ -279,17 +279,17 @@ var MobileConsole = {
    //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); }, false);
    mc.elems.buttons.warn.addEventListener('click', function () { th.toggleLogtype('warn', this); }, false);
    mc.elems.buttons.error.addEventListener('click', function () { th.toggleLogtype('error', this); }, false);
    mc.elems.buttons.trace.addEventListener('click', function () { th.toggleLogtype('trace', this); }, 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); });
    mc.elems.buttons.warn.attachEvent('onclick', function () { th.toggleLogtype('warn', this); });
    mc.elems.buttons.error.attachEvent('onclick', function () { th.toggleLogtype('error', this); });
    mc.elems.buttons.trace.attachEvent('onclick', function () { th.toggleLogtype('trace', this); });
    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);
    }

    @@ -385,7 +385,6 @@ var MobileConsole = {
    th.elems.consoleDiv = th.createElem('DIV', 'hnlMobileConsole', {
    position: 'fixed',
    resize: 'none',
    fontFamily: 'monospace',
    fontSize: '12px',
    lineHeight: '12px',
    bottom: top ? 'auto' : 0,
    @@ -405,6 +404,7 @@ var MobileConsole = {
    'transition': th.Options.AnimParams
    });
    th.elems.consoleBodyTable = th.createElem('DIV', 'hnlMobileConsole_table', {
    fontFamily: 'monospace',
    maxWidth: '100%',
    display: 'table',
    tableLayout: 'fixed',
    @@ -444,8 +444,9 @@ var MobileConsole = {
    display: 'block',
    padding: '0 2px',
    overflow: 'hidden',
    backgroundColor: '#DDDDDD',
    borderBottom: '1px solid #AAAAAA',
    color: white ? '#333333' : '#FFFFFF',
    backgroundColor: white ? '#DDDDDD' : '#222222',
    borderBottom: '1px solid ' + (white ? '#AAA' : '#111111'),
    webkitOverflowScrolling: 'touch'
    });
    th.elems.scrollContainer = th.createElem('DIV', 'hnlMobileConsole_scrolltcontainer', {
    @@ -459,12 +460,11 @@ var MobileConsole = {
    th.elems.button = th.createElem('BUTTON', 'hnlMobileConsole_button', {
    display: 'inline-block',
    fontSize: '14px',
    fontWeight: white ? '' : 'bold',
    padding: '4px 8px',
    padding: '6px 8px',
    textAlign: 'center',
    marginRight: '5px',
    border: '0 none',
    backgroundColor: '#BBBBBB'
    backgroundColor: white ? '#DDDDDD' : '#111111'
    });

    //set up lines object
  29. @c-kick c-kick revised this gist May 17, 2016. 1 changed file with 136 additions and 116 deletions.
    252 changes: 136 additions & 116 deletions hnl.mobileConsole.js
    Original file line number Diff line number Diff line change
    @@ -103,13 +103,13 @@ var MobileConsole = {
    },
    BrowserInfo: {
    browserChrome: /chrome/.test(navigator.userAgent.toLowerCase()),
    browserfirefox: /firefox/.test(navigator.userAgent.toLowerCase()) && !/chrome/.test(navigator.userAgent.toLowerCase()),
    browserSafari: /safari/.test(navigator.userAgent.toLowerCase()) && !/chrome/.test(navigator.userAgent.toLowerCase()),
    supportsEvtLstnr: typeof window.addEventListener === 'function',
    ffox: /firefox/.test(navigator.userAgent.toLowerCase()) && !/chrome/.test(navigator.userAgent.toLowerCase()),
    safari: /safari/.test(navigator.userAgent.toLowerCase()) && !/chrome/.test(navigator.userAgent.toLowerCase()),
    evtLstn: typeof window.addEventListener === 'function',
    supportsElsByCln: document.getElementsByClassName !== undefined,
    isCrap: document.querySelectorAll === undefined
    },
    Elements: {},
    elems: {},

    //constructor
    MobileConsole: function () {
    @@ -122,9 +122,9 @@ var MobileConsole = {
    * @returns {toggle}
    */
    this.toggle = function () {
    var elem = mc.Elements.consoleDiv;
    var elem = mc.elems.consoleDiv;
    mc.setCSS(elem, {
    height: (elem.toggled) ? '85px' : '255px',
    height: (elem.toggled) ? '102px' : '262px',
    opacity: (elem.toggled) ? '0.8' : 1
    });
    this.innerHTML = (elem.toggled) ? '&plus;' : '&minus;';
    @@ -137,7 +137,7 @@ var MobileConsole = {
    * @returns {toggleScroll}
    */
    this.toggleScroll = function (e) {
    var elem = mc.Elements.scrollContainer;
    var elem = mc.elems.scrollContainer;
    elem.scrollTop = elem.scrollHeight;
    return elem;
    };
    @@ -148,10 +148,10 @@ var MobileConsole = {
    */
    this.empty = function (e) {
    console.clear();
    mc.Elements.consoleBodyTable.innerHTML = '';
    mc.elems.consoleBodyTable.innerHTML = '';
    var methods = ['log', 'warn', 'error', 'trace'];
    for (var i = 0; i < methods.length; i++) {
    mc.Elements.lines[methods[i]] = [];
    mc.elems.lines[methods[i]] = [];
    }
    };

    @@ -160,18 +160,25 @@ var MobileConsole = {
    mc.setCSS(button, {
    opacity: (button.toggled) ? '0.5' : ''
    });
    var elems = mc.Elements.lines[method];
    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.Elements.scrollContainer.scrollTop = mc.Elements.scrollContainer.scrollHeight;
    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') ? '#bc7a1e' : ((mc.Options.Style === 'white') ? '#000000' : '#EEEEEE'));
    @@ -184,32 +191,32 @@ var MobileConsole = {
    cleanUrl = stackTrace.url;
    } else {
    var origin = (stackTrace.objTrace[1] === 'global') ? stackTrace.objTrace[0] : stackTrace.objTrace[1];

    if (mc.BrowserInfo.browserSafari || mc.BrowserInfo.browserfirefox) {
    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(")"));
    url = origin.substring(origin.indexOf('(') + 1, origin.indexOf(')'));
    }
    cleanUrl = url.split(':')[0] + ':' + url.split(':')[1];
    }

    //construct link to caller
    if (url !== 'anonymous function') {
    var newLink = mc.Elements.link.cloneNode(false);
    var linkContainer = mc.elems.tdRight.cloneNode(false);
    if (url !== 'anonymous function' && url !== undefined && url !== '') {
    var newLink = mc.elems.link.cloneNode(false);
    newLink.innerHTML = url;
    newLink.href = cleanUrl;
    newLink.setAttribute('target', '_blank');

    //put link in container
    var linkContainer = mc.Elements.tdRight.cloneNode(false);
    linkContainer.appendChild(newLink);
    } else {
    linkContainer.innerHTML = (String(url) === '' ? '(Undefined)' : String(url));
    }
    //construct message
    var messageContainer = mc.Elements.tdLeft.cloneNode(false);
    var messageContainer = mc.elems.tdLeft.cloneNode(false);
    if (method === 'trace') {
    messageContainer.innerHTML = stackTrace.htmlTrace;
    } else {
    @@ -218,20 +225,18 @@ var MobileConsole = {
    mc.setCSS(messageContainer, {color: color});

    //put message in container
    var lineContainer = mc.Elements.tr.cloneNode(false);
    var lineContainer = mc.elems.tr.cloneNode(false);
    mc.setCSS(lineContainer, {backgroundColor: (method ==='warn') ? '#FFF6E0' : (method === 'error' ? '#ffe5e5' : '')});
    lineContainer.appendChild(messageContainer);
    if (url !== 'anonymous function') {
    lineContainer.appendChild(linkContainer);
    }
    lineContainer.appendChild(linkContainer);

    //store the lines in the object corresponding to the method used
    mc.Elements.lines[method].push(lineContainer);
    mc.elems.lines[method].push(lineContainer);
    //add it to the constructed console
    mc.setCSS(lineContainer, {
    display: (mc.Elements.buttons[method].toggled ? 'none' : '')
    display: (mc.elems.buttons[method].toggled ? 'none' : '')
    });
    mc.Elements.consoleBodyTable.appendChild(lineContainer);
    mc.elems.consoleBodyTable.appendChild(lineContainer);
    mc.consoleDiv.prototype.toggleScroll();

    this.prevMsg = message;
    @@ -241,59 +246,58 @@ var MobileConsole = {
    } 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.Elements.consoleBodyTable.lastChild.countDot === undefined) {
    var countDot = mc.setCSS(mc.Elements.dot.cloneNode(false), {
    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.Elements.consoleBodyTable.lastChild.firstChild.innerHTML = '';
    mc.Elements.consoleBodyTable.lastChild.firstChild.appendChild(countDot);
    mc.Elements.consoleBodyTable.lastChild.firstChild.appendChild(span);
    mc.Elements.consoleBodyTable.lastChild.countDot = countDot;
    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.Elements.consoleBodyTable.lastChild.countDot.innerHTML = this.c.toString();
    mc.elems.consoleBodyTable.lastChild.countDot.innerHTML = this.c.toString();
    } else {
    console.error('not logging. Why?');
    }
    }
    };

    //setup binds
    if (mc.BrowserInfo.supportsEvtLstnr) {
    mc.Elements.consoleDiv.addEventListener('transitionend', th.toggleScroll, false);
    mc.Elements.consoleDiv.addEventListener('webkitTransitionEnd', th.toggleScroll, false);
    mc.Elements.consoleDiv.addEventListener('oTransitionEnd', th.toggleScroll, false);
    mc.Elements.consoleDiv.addEventListener('MSTransitionEnd', th.toggleScroll, false);
    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.Elements.consoleDiv.attachEvent('transitionend', th.toggleScroll);
    mc.Elements.consoleDiv.attachEvent('webkitTransitionEnd', th.toggleScroll);
    mc.Elements.consoleDiv.attachEvent('oTransitionEnd', th.toggleScroll);
    mc.Elements.consoleDiv.attachEvent('MSTransitionEnd', th.toggleScroll);
    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.supportsEvtLstnr) {
    mc.Elements.buttons.toggler.addEventListener('click', th.toggle, false);
    mc.Elements.buttons.log.addEventListener('click', function () { th.toggleLogtype('log', this); }, false);
    mc.Elements.buttons.warn.addEventListener('click', function () { th.toggleLogtype('warn', this); }, false);
    mc.Elements.buttons.error.addEventListener('click', function () { th.toggleLogtype('error', this); }, false);
    mc.Elements.buttons.trace.addEventListener('click', function () { th.toggleLogtype('trace', this); }, false);
    mc.Elements.buttons.clear.addEventListener('click', th.empty, false);
    if (mc.BrowserInfo.evtLstn) {
    mc.elems.buttons.toggler.addEventListener('click', th.toggle, false);
    mc.elems.buttons.log.addEventListener('click', function () { th.toggleLogtype('log', this); }, false);
    mc.elems.buttons.warn.addEventListener('click', function () { th.toggleLogtype('warn', this); }, false);
    mc.elems.buttons.error.addEventListener('click', function () { th.toggleLogtype('error', this); }, false);
    mc.elems.buttons.trace.addEventListener('click', function () { th.toggleLogtype('trace', this); }, false);
    mc.elems.buttons.clear.addEventListener('click', th.empty, false);
    } else {
    mc.Elements.buttons.toggler.attachEvent('onclick', th.toggle);
    mc.Elements.buttons.log.attachEvent('onclick', function () { th.toggleLogtype('log', this); });
    mc.Elements.buttons.warn.attachEvent('onclick', function () { th.toggleLogtype('warn', this); });
    mc.Elements.buttons.error.attachEvent('onclick', function () { th.toggleLogtype('error', this); });
    mc.Elements.buttons.trace.attachEvent('onclick', function () { th.toggleLogtype('trace', this); });
    mc.Elements.buttons.clear.attachEvent('onclick', th.empty);
    mc.elems.buttons.toggler.attachEvent('onclick', th.toggle);
    mc.elems.buttons.log.attachEvent('onclick', function () { th.toggleLogtype('log', this); });
    mc.elems.buttons.warn.attachEvent('onclick', function () { th.toggleLogtype('warn', this); });
    mc.elems.buttons.error.attachEvent('onclick', function () { th.toggleLogtype('error', this); });
    mc.elems.buttons.trace.attachEvent('onclick', function () { th.toggleLogtype('trace', this); });
    mc.elems.buttons.clear.attachEvent('onclick', th.empty);
    }

    //logic starts here
    //check if the browser has a console. If not: stop.
    var console = window.console;
    if (!console) {
    return;
    }
    //Intercept all original console methods including trace, and bind to window.onerror
    if (!console) { return; }
    //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 = {
    @@ -303,9 +307,10 @@ var MobileConsole = {
    };
    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.Elements.lines[methods[i]] = [];
    mc.elems.lines[methods[i]] = [];
    mc.interceptConsole(methods[i]);
    }
    },
    @@ -343,14 +348,26 @@ var MobileConsole = {
    .replace(emailAddressPattern, '<span class="link" data-href="mailto:$&">$&</span>');
    },

    //taken from http://stackoverflow.com/a/21350614
    //modified to get rid of the unused add variable
    /**
    * 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
    /**
    * 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;
    @@ -365,7 +382,7 @@ var MobileConsole = {
    'use strict';
    var th = this;
    var white = (th.Options.Style === 'white'), top = (th.Options.Position === 'top'), window = (th.Options.Type === 'window');
    th.Elements.consoleDiv = th.createElem('DIV', 'hnlMobileConsole', {
    th.elems.consoleDiv = th.createElem('DIV', 'hnlMobileConsole', {
    position: 'fixed',
    resize: 'none',
    fontFamily: 'monospace',
    @@ -376,7 +393,7 @@ var MobileConsole = {
    right: window ? '70px' : 0,
    backgroundColor: white ? '#ffffff' : '#333333',
    width: window ? '70%' : '',
    height: '85px',
    height: '102px',
    zIndex: 10000,
    padding: 0,
    margin: 0,
    @@ -387,30 +404,30 @@ var MobileConsole = {
    '-o-transition': th.Options.AnimParams,
    'transition': th.Options.AnimParams
    });
    th.Elements.consoleBodyTable = th.createElem('DIV', 'hnlMobileConsole_table', {
    th.elems.consoleBodyTable = th.createElem('DIV', 'hnlMobileConsole_table', {
    maxWidth: '100%',
    display: 'table',
    tableLayout: 'fixed',
    width: '100%'
    });
    th.Elements.tr = th.createElem('TR', 'hnlMobileConsole_table_row', {});
    th.Elements.tdLeft = th.createElem('TD', 'hnlMobileConsole_table_row_data', {
    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.Elements.tdRight = th.createElem('TD', 'hnlMobileConsole_table_row_data', {
    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.Elements.link = th.createElem('A', 'hnlMobileConsole_link', {
    th.elems.link = th.createElem('A', 'hnlMobileConsole_link', {
    color: (white ? '#0000EE' : '#AAC1D2')
    });
    th.Elements.dot = th.createElem('DIV', 'hnlMobileConsole_table_row_data_dot', {
    th.elems.dot = th.createElem('DIV', 'hnlMobileConsole_table_row_data_dot', {
    display: 'inline-block',
    borderRadius: '5px',
    color: white ? '#ffffff' : '#333333',
    @@ -420,7 +437,7 @@ var MobileConsole = {
    textAlign: 'center',
    marginRight: '5px'
    });
    th.Elements.topBar = th.createElem('DIV', 'hnlMobileConsole_topbar', {
    th.elems.topBar = th.createElem('DIV', 'hnlMobileConsole_topbar', {
    position: 'absolute',
    left: 0,
    right: 0,
    @@ -431,15 +448,15 @@ var MobileConsole = {
    borderBottom: '1px solid #AAAAAA',
    webkitOverflowScrolling: 'touch'
    });
    th.Elements.scrollContainer = th.createElem('DIV', 'hnlMobileConsole_scrolltcontainer', {
    th.elems.scrollContainer = th.createElem('DIV', 'hnlMobileConsole_scrolltcontainer', {
    position: 'relative',
    display: 'block',
    height: '100%',
    overflow: 'hidden',
    overflowY: 'scroll',
    webkitOverflowScrolling: 'touch'
    });
    th.Elements.button = th.createElem('BUTTON', 'hnlMobileConsole_button', {
    th.elems.button = th.createElem('BUTTON', 'hnlMobileConsole_button', {
    display: 'inline-block',
    fontSize: '14px',
    fontWeight: white ? '' : 'bold',
    @@ -451,51 +468,60 @@ var MobileConsole = {
    });

    //set up lines object
    th.Elements.lines = [];
    th.Elements.buttons = [];
    th.elems.lines = [];
    //set up buttons object
    th.elems.buttons = [];

    th.Elements.buttons.toggler = th.Elements.button.cloneNode(false);
    th.Elements.buttons.toggler.innerHTML = '&plus;';
    th.Elements.topBar.appendChild(th.Elements.buttons.toggler);
    th.elems.buttons.toggler = th.elems.button.cloneNode(false);
    th.elems.buttons.toggler.innerHTML = '&plus;';
    th.elems.topBar.appendChild(th.elems.buttons.toggler);

    th.Elements.buttons.log = th.Elements.button.cloneNode(false);
    th.Elements.buttons.log.innerHTML = 'Log';
    th.Elements.topBar.appendChild(th.Elements.buttons.log);
    th.elems.buttons.log = th.elems.button.cloneNode(false);
    th.elems.buttons.log.innerHTML = 'Log';
    th.elems.topBar.appendChild(th.elems.buttons.log);

    th.Elements.buttons.warn = th.Elements.button.cloneNode(false);
    th.Elements.buttons.warn.innerHTML = 'Warn';
    th.Elements.topBar.appendChild(th.Elements.buttons.warn);
    th.elems.buttons.warn = th.elems.button.cloneNode(false);
    th.elems.buttons.warn.innerHTML = 'Warn';
    th.elems.topBar.appendChild(th.elems.buttons.warn);

    th.Elements.buttons.error = th.Elements.button.cloneNode(false);
    th.Elements.buttons.error.innerHTML = 'Error';
    th.Elements.topBar.appendChild(th.Elements.buttons.error);
    th.elems.buttons.error = th.elems.button.cloneNode(false);
    th.elems.buttons.error.innerHTML = 'Error';
    th.elems.topBar.appendChild(th.elems.buttons.error);

    th.Elements.buttons.trace = th.Elements.button.cloneNode(false);
    th.Elements.buttons.trace.innerHTML = 'Trace';
    th.Elements.topBar.appendChild(th.Elements.buttons.trace);
    th.elems.buttons.trace = th.elems.button.cloneNode(false);
    th.elems.buttons.trace.innerHTML = 'Trace';
    th.elems.topBar.appendChild(th.elems.buttons.trace);

    th.Elements.buttons.clear = th.Elements.button.cloneNode(false);
    th.Elements.buttons.clear.innerHTML = 'Clear';
    th.Elements.topBar.appendChild(th.Elements.buttons.clear);
    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.Elements.consoleDiv.appendChild(th.Elements.topBar);
    th.Elements.scrollContainer.appendChild(th.Elements.consoleBodyTable);
    th.Elements.consoleDiv.appendChild(th.Elements.scrollContainer);
    th.elems.consoleDiv.appendChild(th.elems.topBar);
    th.elems.scrollContainer.appendChild(th.elems.consoleBodyTable);
    th.elems.consoleDiv.appendChild(th.elems.scrollContainer);


    return th.Elements.consoleDiv;
    return th.elems.consoleDiv;
    },

    /**
    * Attaches console to the document body
    */
    attachConsole: function () {
    'use strict';
    document.body.appendChild(MobileConsole.Elements.consoleDiv);
    MobileConsole.setCSS(MobileConsole.Elements.topBar, {
    top: -Math.abs(MobileConsole.Elements.topBar.offsetHeight) + 'px'
    document.body.appendChild(MobileConsole.elems.consoleDiv);
    MobileConsole.setCSS(MobileConsole.elems.topBar, {
    top: -Math.abs(MobileConsole.elems.topBar.offsetHeight) + 'px'
    });
    MobileConsole.Elements.scrollContainer.scrollTop = MobileConsole.Elements.scrollContainer.scrollHeight;
    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;
    @@ -527,7 +553,7 @@ var MobileConsole = {
    returnObj.objTrace = stackTrace.split(/at /gm);
    stackTrace = th.linkify(stackTrace.replace(/at /gm, '</span><span>'));
    }
    else if (th.BrowserInfo.browserSafari || th.BrowserInfo.browserfirefox) {
    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);
    @@ -553,20 +579,16 @@ var MobileConsole = {
    interceptConsole: function (method) {
    var original = console[method];
    console[method] = function (message, stackTrace) {
    //var message = Array.prototype.slice.apply(arguments).join(' ');
    if (message === undefined) {
    message = msg;
    }
    // alt:
    // var message = Array.prototype.slice.apply(arguments).join(' ');
    //create an Error and get its stack trace and format it
    var stackTraceOrig = new Error().stack;
    if (stackTrace === undefined) {
    //if no stacktrace defined (window.onerror defines one), use the generated one
    stackTrace = MobileConsole.stackTraceFormat(stackTraceOrig);
    }

    //Handle console logging
    //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);
    @@ -577,18 +599,16 @@ var MobileConsole = {
    'use strict';
    var th = this;
    //don't go when we have already built it
    if (th.consoleDiv !== undefined) {
    return;
    }
    if (th.consoleDiv !== undefined) { return; }
    //build the console DIV
    th.consoleDiv = th.buildConsole();
    //attach prototype to the console
    //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) {
    if (autorun && typeof docReady === 'function') {
    docReady(th.attachConsole);
    } else {
    //if no autorun
    //if no autorun: attach the built console
    th.attachConsole();
    }
    //return the built console
  30. @c-kick c-kick revised this gist May 17, 2016. 1 changed file with 129 additions and 120 deletions.
    249 changes: 129 additions & 120 deletions hnl.mobileConsole.js
    Original file line number Diff line number Diff line change
    @@ -15,7 +15,7 @@
    /**
    * DocReady - Replacement for jQuery's $(document).ready() event
    */
    (function(funcName, baseObj) {
    (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
    @@ -46,7 +46,7 @@
    }

    function readyStateChange() {
    if ( document.readyState === 'complete' ) {
    if (document.readyState === 'complete') {
    ready();
    }
    }
    @@ -55,11 +55,11 @@
    // 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) {
    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);
    setTimeout(function () {callback(context);}, 1);
    return;
    } else {
    // add the function and context to the list
    @@ -82,7 +82,7 @@
    }
    readyEventHandlersInstalled = true;
    }
    }
    };
    })('docReady', window);

    var MobileConsole = {
    @@ -115,14 +115,15 @@ var MobileConsole = {
    MobileConsole: function () {
    'use strict';
    var th = this;
    var mc = MobileConsole;

    /**
    * Toggles the console height
    * @returns {toggle}
    */
    this.toggle = function () {
    var elem = MobileConsole.Elements.consoleDiv;
    MobileConsole.setCSS(elem, {
    var elem = mc.Elements.consoleDiv;
    mc.setCSS(elem, {
    height: (elem.toggled) ? '85px' : '255px',
    opacity: (elem.toggled) ? '0.8' : 1
    });
    @@ -136,7 +137,7 @@ var MobileConsole = {
    * @returns {toggleScroll}
    */
    this.toggleScroll = function (e) {
    var elem = MobileConsole.Elements.scrollContainer;
    var elem = mc.Elements.scrollContainer;
    elem.scrollTop = elem.scrollHeight;
    return elem;
    };
    @@ -147,33 +148,33 @@ var MobileConsole = {
    */
    this.empty = function (e) {
    console.clear();
    MobileConsole.Elements.consoleBodyTable.innerHTML = '';
    mc.Elements.consoleBodyTable.innerHTML = '';
    var methods = ['log', 'warn', 'error', 'trace'];
    for (var i = 0; i < methods.length; i++) {
    MobileConsole.Elements.lines[methods[i]] = [];
    mc.Elements.lines[methods[i]] = [];
    }
    };

    this.toggleLogtype = function (method, button) {
    button.toggled = !button.toggled;
    MobileConsole.setCSS(button, {
    mc.setCSS(button, {
    opacity: (button.toggled) ? '0.5' : ''
    });
    var elems = MobileConsole.Elements.lines[method];
    var elems = mc.Elements.lines[method];
    var key, x = 0;
    for (key in elems) {
    if (elems.hasOwnProperty(key)) {
    MobileConsole.setCSS(elems[x], { display: (button.toggled) ? 'none' : '' });
    mc.setCSS(elems[x], { display: (button.toggled) ? 'none' : '' });
    }
    x++;
    }
    MobileConsole.Elements.scrollContainer.scrollTop = MobileConsole.Elements.scrollContainer.scrollHeight;
    mc.Elements.scrollContainer.scrollTop = mc.Elements.scrollContainer.scrollHeight;
    return button;
    };

    this.theConsole = function (method, message, stackTrace, stackTraceOrig) {
    if (message !== '' && message !== undefined) {
    var color = (method === 'error') ? ((MobileConsole.Options.Style === 'white') ? '#FF0000' : '#ff4848') : ((method === 'warn') ? '#bc7a1e' : ((MobileConsole.Options.Style === 'white') ? '#000000' : '#EEEEEE'));
    var color = (method === 'error') ? ((mc.Options.Style === 'white') ? '#FF0000' : '#ff4848') : ((method === 'warn') ? '#bc7a1e' : ((mc.Options.Style === 'white') ? '#000000' : '#EEEEEE'));
    if (this.prevMsg !== message || method === 'trace') {
    //message is not a repeat of the previous
    var cleanUrl, url;
    @@ -184,7 +185,7 @@ var MobileConsole = {
    } else {
    var origin = (stackTrace.objTrace[1] === 'global') ? stackTrace.objTrace[0] : stackTrace.objTrace[1];

    if (MobileConsole.BrowserInfo.browserSafari || MobileConsole.BrowserInfo.browserfirefox) {
    if (mc.BrowserInfo.browserSafari || mc.BrowserInfo.browserfirefox) {
    if (/@/i.test(origin)) {
    url = origin.split('@')[1];
    } else {
    @@ -198,40 +199,40 @@ var MobileConsole = {

    //construct link to caller
    if (url !== 'anonymous function') {
    var newLink = MobileConsole.Elements.link.cloneNode(false);
    var newLink = mc.Elements.link.cloneNode(false);
    newLink.innerHTML = url;
    newLink.href = cleanUrl;
    newLink.setAttribute('target', '_blank');

    //put link in container
    var linkContainer = MobileConsole.Elements.tdRight.cloneNode(false);
    var linkContainer = mc.Elements.tdRight.cloneNode(false);
    linkContainer.appendChild(newLink);
    }
    //construct message
    var messageContainer = MobileConsole.Elements.tdLeft.cloneNode(false);
    var messageContainer = mc.Elements.tdLeft.cloneNode(false);
    if (method === 'trace') {
    messageContainer.innerHTML = stackTrace.htmlTrace;
    } else {
    messageContainer.innerHTML = message;
    }
    MobileConsole.setCSS(messageContainer, {color: color});
    mc.setCSS(messageContainer, {color: color});

    //put message in container
    var lineContainer = MobileConsole.Elements.tr.cloneNode(false);
    MobileConsole.setCSS(lineContainer, {backgroundColor: (method ==='warn') ? '#FFF6E0' : (method === 'error' ? '#ffe5e5' : '')});
    var lineContainer = mc.Elements.tr.cloneNode(false);
    mc.setCSS(lineContainer, {backgroundColor: (method ==='warn') ? '#FFF6E0' : (method === 'error' ? '#ffe5e5' : '')});
    lineContainer.appendChild(messageContainer);
    if (url !== 'anonymous function') {
    lineContainer.appendChild(linkContainer);
    }

    //store the lines in the object corresponding to the method used
    MobileConsole.Elements.lines[method].push(lineContainer);
    mc.Elements.lines[method].push(lineContainer);
    //add it to the constructed console
    MobileConsole.setCSS(lineContainer, {
    display: (MobileConsole.Elements.buttons[method].toggled ? 'none' : '')
    mc.setCSS(lineContainer, {
    display: (mc.Elements.buttons[method].toggled ? 'none' : '')
    });
    MobileConsole.Elements.consoleBodyTable.appendChild(lineContainer);
    MobileConsole.consoleDiv.prototype.toggleScroll();
    mc.Elements.consoleBodyTable.appendChild(lineContainer);
    mc.consoleDiv.prototype.toggleScroll();

    this.prevMsg = message;
    this.prevMethod = method;
    @@ -240,51 +241,51 @@ var MobileConsole = {
    } 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 (MobileConsole.Elements.consoleBodyTable.lastChild.countDot === undefined) {
    var countDot = MobileConsole.setCSS(MobileConsole.Elements.dot.cloneNode(false), {
    if (mc.Elements.consoleBodyTable.lastChild.countDot === undefined) {
    var countDot = mc.setCSS(mc.Elements.dot.cloneNode(false), {
    backgroundColor: color
    });
    var span = document.createElement('span');
    span.innerHTML = this.prevMsg;
    MobileConsole.Elements.consoleBodyTable.lastChild.firstChild.innerHTML = '';
    MobileConsole.Elements.consoleBodyTable.lastChild.firstChild.appendChild(countDot);
    MobileConsole.Elements.consoleBodyTable.lastChild.firstChild.appendChild(span);
    MobileConsole.Elements.consoleBodyTable.lastChild.countDot = countDot;
    mc.Elements.consoleBodyTable.lastChild.firstChild.innerHTML = '';
    mc.Elements.consoleBodyTable.lastChild.firstChild.appendChild(countDot);
    mc.Elements.consoleBodyTable.lastChild.firstChild.appendChild(span);
    mc.Elements.consoleBodyTable.lastChild.countDot = countDot;
    }
    MobileConsole.Elements.consoleBodyTable.lastChild.countDot.innerHTML = this.c.toString();
    mc.Elements.consoleBodyTable.lastChild.countDot.innerHTML = this.c.toString();
    } else {
    console.error('not logging. Why?');
    }
    }
    };

    //setup binds
    if (MobileConsole.BrowserInfo.supportsEvtLstnr) {
    MobileConsole.Elements.consoleDiv.addEventListener('transitionend', th.toggleScroll, false);
    MobileConsole.Elements.consoleDiv.addEventListener('webkitTransitionEnd', th.toggleScroll, false);
    MobileConsole.Elements.consoleDiv.addEventListener('oTransitionEnd', th.toggleScroll, false);
    MobileConsole.Elements.consoleDiv.addEventListener('MSTransitionEnd', th.toggleScroll, false);
    if (mc.BrowserInfo.supportsEvtLstnr) {
    mc.Elements.consoleDiv.addEventListener('transitionend', th.toggleScroll, false);
    mc.Elements.consoleDiv.addEventListener('webkitTransitionEnd', th.toggleScroll, false);
    mc.Elements.consoleDiv.addEventListener('oTransitionEnd', th.toggleScroll, false);
    mc.Elements.consoleDiv.addEventListener('MSTransitionEnd', th.toggleScroll, false);
    } else {
    MobileConsole.Elements.consoleDiv.attachEvent('transitionend', th.toggleScroll);
    MobileConsole.Elements.consoleDiv.attachEvent('webkitTransitionEnd', th.toggleScroll);
    MobileConsole.Elements.consoleDiv.attachEvent('oTransitionEnd', th.toggleScroll);
    MobileConsole.Elements.consoleDiv.attachEvent('MSTransitionEnd', th.toggleScroll);
    mc.Elements.consoleDiv.attachEvent('transitionend', th.toggleScroll);
    mc.Elements.consoleDiv.attachEvent('webkitTransitionEnd', th.toggleScroll);
    mc.Elements.consoleDiv.attachEvent('oTransitionEnd', th.toggleScroll);
    mc.Elements.consoleDiv.attachEvent('MSTransitionEnd', th.toggleScroll);
    }
    //button binds
    if (MobileConsole.BrowserInfo.supportsEvtLstnr) {
    MobileConsole.Elements.buttons.toggler.addEventListener('click', th.toggle, false);
    MobileConsole.Elements.buttons.log.addEventListener('click', function () { th.toggleLogtype('log', this); }, false);
    MobileConsole.Elements.buttons.warn.addEventListener('click', function () { th.toggleLogtype('warn', this); }, false);
    MobileConsole.Elements.buttons.error.addEventListener('click', function () { th.toggleLogtype('error', this); }, false);
    MobileConsole.Elements.buttons.trace.addEventListener('click', function () { th.toggleLogtype('trace', this); }, false);
    MobileConsole.Elements.buttons.clear.addEventListener('click', th.empty, false);
    if (mc.BrowserInfo.supportsEvtLstnr) {
    mc.Elements.buttons.toggler.addEventListener('click', th.toggle, false);
    mc.Elements.buttons.log.addEventListener('click', function () { th.toggleLogtype('log', this); }, false);
    mc.Elements.buttons.warn.addEventListener('click', function () { th.toggleLogtype('warn', this); }, false);
    mc.Elements.buttons.error.addEventListener('click', function () { th.toggleLogtype('error', this); }, false);
    mc.Elements.buttons.trace.addEventListener('click', function () { th.toggleLogtype('trace', this); }, false);
    mc.Elements.buttons.clear.addEventListener('click', th.empty, false);
    } else {
    MobileConsole.Elements.buttons.toggler.attachEvent('onclick', th.toggle);
    MobileConsole.Elements.buttons.log.attachEvent('onclick', function () { th.toggleLogtype('log', this); });
    MobileConsole.Elements.buttons.warn.attachEvent('onclick', function () { th.toggleLogtype('warn', this); });
    MobileConsole.Elements.buttons.error.attachEvent('onclick', function () { th.toggleLogtype('error', this); });
    MobileConsole.Elements.buttons.trace.attachEvent('onclick', function () { th.toggleLogtype('trace', this); });
    MobileConsole.Elements.buttons.clear.attachEvent('onclick', th.empty);
    mc.Elements.buttons.toggler.attachEvent('onclick', th.toggle);
    mc.Elements.buttons.log.attachEvent('onclick', function () { th.toggleLogtype('log', this); });
    mc.Elements.buttons.warn.attachEvent('onclick', function () { th.toggleLogtype('warn', this); });
    mc.Elements.buttons.error.attachEvent('onclick', function () { th.toggleLogtype('error', this); });
    mc.Elements.buttons.trace.attachEvent('onclick', function () { th.toggleLogtype('trace', this); });
    mc.Elements.buttons.clear.attachEvent('onclick', th.empty);
    }

    //logic starts here
    @@ -304,8 +305,8 @@ var MobileConsole = {
    };
    var methods = ['log', 'warn', 'error', 'trace'];
    for (var i = 0; i < methods.length; i++) {
    MobileConsole.Elements.lines[methods[i]] = [];
    MobileConsole.interceptConsole(methods[i]);
    mc.Elements.lines[methods[i]] = [];
    mc.interceptConsole(methods[i]);
    }
    },

    @@ -352,71 +353,74 @@ var MobileConsole = {
    //Element builder
    createElem: function (type, className, css) {
    'use strict';
    var th = this;
    var element = document.createElement(type);
    MobileConsole.setCSS(element, css);
    th.setCSS(element, css);
    element.className = className;
    return element;
    },

    //Console constructor
    buildConsole: function () {
    'use strict';
    MobileConsole.Elements.consoleDiv = MobileConsole.createElem('DIV', 'hnlMobileConsole', {
    var th = this;
    var white = (th.Options.Style === 'white'), top = (th.Options.Position === 'top'), window = (th.Options.Type === 'window');
    th.Elements.consoleDiv = th.createElem('DIV', 'hnlMobileConsole', {
    position: 'fixed',
    resize: 'none',
    fontFamily: 'monospace',
    fontSize: '12px',
    lineHeight: '12px',
    bottom: (MobileConsole.Options.Position === 'top') ? 'auto' : 0,
    top: (MobileConsole.Options.Position === 'top') ? ((MobileConsole.Options.Type === 'window') ? '32px' : 0) : 'auto',
    right: (MobileConsole.Options.Type === 'window') ? '70px' : 0,
    backgroundColor: (MobileConsole.Options.Style === 'white') ? '#ffffff' : '#333333',
    width: (MobileConsole.Options.Type === 'window') ? '70%' : '',
    bottom: top ? 'auto' : 0,
    top: top ? (window ? '32px' : 0) : 'auto',
    right: window ? '70px' : 0,
    backgroundColor: white ? '#ffffff' : '#333333',
    width: window ? '70%' : '',
    height: '85px',
    zIndex: 10000,
    padding: 0,
    margin: 0,
    opacity: '0.8',
    borderTop: '1px solid #808080',
    '-webkit-transition': MobileConsole.Options.AnimParams,
    '-moz-transition': MobileConsole.Options.AnimParams,
    '-o-transition': MobileConsole.Options.AnimParams,
    'transition': MobileConsole.Options.AnimParams
    '-webkit-transition': th.Options.AnimParams,
    '-moz-transition': th.Options.AnimParams,
    '-o-transition': th.Options.AnimParams,
    'transition': th.Options.AnimParams
    });
    MobileConsole.Elements.consoleBodyTable = MobileConsole.createElem('DIV', 'hnlMobileConsole_table', {
    th.Elements.consoleBodyTable = th.createElem('DIV', 'hnlMobileConsole_table', {
    maxWidth: '100%',
    display: 'table',
    tableLayout: 'fixed',
    width: '100%'
    });
    MobileConsole.Elements.tr = MobileConsole.createElem('TR', 'hnlMobileConsole_table_row', {});
    MobileConsole.Elements.tdLeft = MobileConsole.createElem('TD', 'hnlMobileConsole_table_row_data', {
    th.Elements.tr = th.createElem('TR', 'hnlMobileConsole_table_row', {});
    th.Elements.tdLeft = th.createElem('TD', 'hnlMobileConsole_table_row_data', {
    textAlign: 'left',
    padding: '2px 4px',
    borderBottom: '1px solid ' + ((this.Options.Style === 'white') ? '#EEEEEE' : '#777777')
    borderBottom: '1px solid ' + (white ? '#EEEEEE' : '#777777')
    });
    MobileConsole.Elements.tdRight = MobileConsole.createElem('TD', 'hnlMobileConsole_table_row_data', {
    th.Elements.tdRight = th.createElem('TD', 'hnlMobileConsole_table_row_data', {
    textAlign: 'left',
    padding: '2px 4px',
    borderBottom: '1px solid ' + ((this.Options.Style === 'white') ? '#EEEEEE' : '#777777'),
    borderBottom: '1px solid ' + (white ? '#EEEEEE' : '#777777'),
    whiteSpace: 'nowrap',
    overflow: 'hidden',
    direction: 'rtl'
    });
    MobileConsole.Elements.link = MobileConsole.createElem('A', 'hnlMobileConsole_link', {
    color: ((this.Options.Style === 'white') ? '#0000EE' : '#AAC1D2')
    th.Elements.link = th.createElem('A', 'hnlMobileConsole_link', {
    color: (white ? '#0000EE' : '#AAC1D2')
    });
    MobileConsole.Elements.dot = MobileConsole.createElem('DIV', 'hnlMobileConsole_table_row_data_dot', {
    th.Elements.dot = th.createElem('DIV', 'hnlMobileConsole_table_row_data_dot', {
    display: 'inline-block',
    borderRadius: '5px',
    color: (this.Options.Style === 'white') ? '#ffffff' : '#333333',
    fontWeight: (this.Options.Style === 'white') ? '' : 'bold',
    color: white ? '#ffffff' : '#333333',
    fontWeight: white ? '' : 'bold',
    minWidth: '12px',
    padding: '0 1px',
    textAlign: 'center',
    marginRight: '5px'
    });
    MobileConsole.Elements.topBar = MobileConsole.createElem('DIV', 'hnlMobileConsole_topbar', {
    th.Elements.topBar = th.createElem('DIV', 'hnlMobileConsole_topbar', {
    position: 'absolute',
    left: 0,
    right: 0,
    @@ -427,18 +431,18 @@ var MobileConsole = {
    borderBottom: '1px solid #AAAAAA',
    webkitOverflowScrolling: 'touch'
    });
    MobileConsole.Elements.scrollContainer = MobileConsole.createElem('DIV', 'hnlMobileConsole_scrolltcontainer', {
    th.Elements.scrollContainer = th.createElem('DIV', 'hnlMobileConsole_scrolltcontainer', {
    position: 'relative',
    display: 'block',
    height: '100%',
    overflow: 'hidden',
    overflowY: 'scroll',
    webkitOverflowScrolling: 'touch'
    });
    MobileConsole.Elements.button = MobileConsole.createElem('BUTTON', 'hnlMobileConsole_button', {
    th.Elements.button = th.createElem('BUTTON', 'hnlMobileConsole_button', {
    display: 'inline-block',
    fontSize: '14px',
    fontWeight: (this.Options.Style === 'white') ? '' : 'bold',
    fontWeight: white ? '' : 'bold',
    padding: '4px 8px',
    textAlign: 'center',
    marginRight: '5px',
    @@ -447,43 +451,44 @@ var MobileConsole = {
    });

    //set up lines object
    MobileConsole.Elements.lines = [];
    MobileConsole.Elements.buttons = [];
    th.Elements.lines = [];
    th.Elements.buttons = [];

    MobileConsole.Elements.buttons.toggler = MobileConsole.Elements.button.cloneNode(false);
    MobileConsole.Elements.buttons.toggler.innerHTML = '&plus;';
    MobileConsole.Elements.topBar.appendChild(MobileConsole.Elements.buttons.toggler);
    th.Elements.buttons.toggler = th.Elements.button.cloneNode(false);
    th.Elements.buttons.toggler.innerHTML = '&plus;';
    th.Elements.topBar.appendChild(th.Elements.buttons.toggler);

    MobileConsole.Elements.buttons.log = MobileConsole.Elements.button.cloneNode(false);
    MobileConsole.Elements.buttons.log.innerHTML = 'Log';
    MobileConsole.Elements.topBar.appendChild(MobileConsole.Elements.buttons.log);
    th.Elements.buttons.log = th.Elements.button.cloneNode(false);
    th.Elements.buttons.log.innerHTML = 'Log';
    th.Elements.topBar.appendChild(th.Elements.buttons.log);

    MobileConsole.Elements.buttons.warn = MobileConsole.Elements.button.cloneNode(false);
    MobileConsole.Elements.buttons.warn.innerHTML = 'Warn';
    MobileConsole.Elements.topBar.appendChild(MobileConsole.Elements.buttons.warn);
    th.Elements.buttons.warn = th.Elements.button.cloneNode(false);
    th.Elements.buttons.warn.innerHTML = 'Warn';
    th.Elements.topBar.appendChild(th.Elements.buttons.warn);

    MobileConsole.Elements.buttons.error = MobileConsole.Elements.button.cloneNode(false);
    MobileConsole.Elements.buttons.error.innerHTML = 'Error';
    MobileConsole.Elements.topBar.appendChild(MobileConsole.Elements.buttons.error);
    th.Elements.buttons.error = th.Elements.button.cloneNode(false);
    th.Elements.buttons.error.innerHTML = 'Error';
    th.Elements.topBar.appendChild(th.Elements.buttons.error);

    MobileConsole.Elements.buttons.trace = MobileConsole.Elements.button.cloneNode(false);
    MobileConsole.Elements.buttons.trace.innerHTML = 'Trace';
    MobileConsole.Elements.topBar.appendChild(MobileConsole.Elements.buttons.trace);
    th.Elements.buttons.trace = th.Elements.button.cloneNode(false);
    th.Elements.buttons.trace.innerHTML = 'Trace';
    th.Elements.topBar.appendChild(th.Elements.buttons.trace);

    MobileConsole.Elements.buttons.clear = MobileConsole.Elements.button.cloneNode(false);
    MobileConsole.Elements.buttons.clear.innerHTML = 'Clear';
    MobileConsole.Elements.topBar.appendChild(MobileConsole.Elements.buttons.clear);
    th.Elements.buttons.clear = th.Elements.button.cloneNode(false);
    th.Elements.buttons.clear.innerHTML = 'Clear';
    th.Elements.topBar.appendChild(th.Elements.buttons.clear);

    //construct elements
    MobileConsole.Elements.consoleDiv.appendChild(MobileConsole.Elements.topBar);
    MobileConsole.Elements.scrollContainer.appendChild(MobileConsole.Elements.consoleBodyTable);
    MobileConsole.Elements.consoleDiv.appendChild(MobileConsole.Elements.scrollContainer);
    th.Elements.consoleDiv.appendChild(th.Elements.topBar);
    th.Elements.scrollContainer.appendChild(th.Elements.consoleBodyTable);
    th.Elements.consoleDiv.appendChild(th.Elements.scrollContainer);


    return MobileConsole.Elements.consoleDiv;
    return th.Elements.consoleDiv;
    },

    attachConsole: function () {
    'use strict';
    document.body.appendChild(MobileConsole.Elements.consoleDiv);
    MobileConsole.setCSS(MobileConsole.Elements.topBar, {
    top: -Math.abs(MobileConsole.Elements.topBar.offsetHeight) + 'px'
    @@ -492,8 +497,10 @@ var MobileConsole = {
    },

    stackTraceFormat: function (stackTrace) {
    'use strict';
    var th = this;
    var returnObj = {};
    if (MobileConsole.BrowserInfo.browserChrome) {
    if (th.BrowserInfo.browserChrome) {
    //we first format the string a bit
    stackTrace = stackTrace.replace("Error", "")
    .replace(/\r?\n|\r/g, '')
    @@ -508,7 +515,7 @@ var MobileConsole = {
    if (stackTrace[j] == 'a' && stackTrace[j + 1] == 't' && stackTrace[j + 2] == ' ') {
    var endIndex = j;
    //found beginning and end of this part, remove it
    stackTrace = MobileConsole.strSplice(stackTrace, i, j);
    stackTrace = th.strSplice(stackTrace, i, j);
    break;
    }
    }
    @@ -518,20 +525,20 @@ var MobileConsole = {

    //then replace all 'at's with list elements, and convert to link spans
    returnObj.objTrace = stackTrace.split(/at /gm);
    stackTrace = MobileConsole.linkify(stackTrace.replace(/at /gm, '</span><span>'));
    stackTrace = th.linkify(stackTrace.replace(/at /gm, '</span><span>'));
    }
    else if (MobileConsole.BrowserInfo.browserSafari || MobileConsole.BrowserInfo.browserfirefox) {
    else if (th.BrowserInfo.browserSafari || th.BrowserInfo.browserfirefox) {
    //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 = MobileConsole.linkify('<li>' + stackTrace.replace(/\s/gm, '</li><li>')).replace(/\@/gm, '&commat;');
    stackTrace = th.linkify('<li>' + stackTrace.replace(/\s/gm, '</li><li>')).replace(/\@/gm, '&commat;');

    //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 = MobileConsole.strSplice(stackTrace, 0, index);
    stackTrace = th.strSplice(stackTrace, 0, index);

    break;
    }
    @@ -567,23 +574,25 @@ var MobileConsole = {
    },

    init: function (autorun) {
    'use strict';
    var th = this;
    //don't go when we have already built it
    if (MobileConsole.consoleDiv !== undefined) {
    if (th.consoleDiv !== undefined) {
    return;
    }
    //build the console DIV
    MobileConsole.consoleDiv = MobileConsole.buildConsole();
    th.consoleDiv = th.buildConsole();
    //attach prototype to the console
    MobileConsole.consoleDiv.prototype = new MobileConsole.MobileConsole();
    th.consoleDiv.prototype = new th.MobileConsole();
    //wait for load if autorun is enabled and attach console to body
    if (autorun) {
    docReady(MobileConsole.attachConsole);
    docReady(th.attachConsole);
    } else {
    //if no autorun
    MobileConsole.attachConsole();
    th.attachConsole();
    }
    //return the built console
    return MobileConsole.consoleDiv;
    return th.consoleDiv;
    }
    };