/*!
 * 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)
 * Licensed under the MIT license
 *
 * Original author: @hnldesign
 * Further changes, comments: @hnldesign
 * Copyright (c) 2014-2016 HN Leussink
 * Dual licensed under the MIT and GPL licenses.
 *
 * Info: http://www.hnldesign.nl/work/code/javascript-mobile-console/
 * Demo: http://code.hnldesign.nl/demo/hnl.MobileConsole.html
 */
var console = window.console;

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 = {
      overrideAutorun: false,
      version : '1.2.6',
      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', 'groupCollapsed', 'groupEnd', 'table', 'assert', 'time', 'timeEnd', 'clear'],
      hideButtons : ['group', 'groupCollapsed', '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 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 (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
            // this event loop finishes so all handlers will still execute
            // in order and no new ones will be added to the readyList
            // while we are processing the list
            readyList[i].fn.call(window, readyList[i].ctx);
          }
          // allow any closures held by these functions to free
          readyList = [];
        }
      }

      function readyStateChange() {
        if (document.readyState === 'complete') {
          ready();
        }
      }

      // This is the one public interface
      // docReady(fn, context);
      // the context argument is optional - if present, it will be passed
      // as an argument to the callback
      baseObj[funcName] = function (callback, context) {
        // if ready has already fired, then just schedule the callback
        // to fire asynchronously, but right away
        if (readyFired) {
          setTimeout(function () {callback(context); }, 1);
          return;
        }
        // add the function and context to the list
        readyList.push({fn: callback, ctx: context});
        // if document already ready to go, schedule the ready function to run
        if (document.readyState === 'complete') {
          setTimeout(ready, 1);
        } else if (!readyEventHandlersInstalled) {
          // otherwise if we don't have event handlers installed, install them
          if (document.addEventListener) {
            // first choice is DOMContentLoaded event
            document.addEventListener('DOMContentLoaded', ready, false);
            // backup is window load event
            window.addEventListener('load', ready, false);
          } else {
            // must be IE
            document.attachEvent('onreadystatechange', readyStateChange);
            window.attachEvent('onload', ready);
          }
          readyEventHandlersInstalled = true;
        }
      };
    }('docReady', window));
  }

  // elements
  var elements = {
    lines: [],
    acItems: [],
    base: createElem('div', 'base', {
      boxSizing: 'border-box',
      position: 'fixed',
      resize: 'none',
      fontSize: '12px',
      lineHeight: '14px',
      bottom: 0,
      top: 'auto',
      right: 0,
      width: '100%',
      zIndex: 10000,
      padding: 0,
      paddingBottom: isMobile() ? '35px' : '25px',
      margin: 0,
      border: '0 none',
      borderTop: '1px solid #808080',
      backgroundColor: '#ffffff'
    }),
    topbar : createElem('div', 'topbar', {
      boxSizing: 'border-box',
      position: 'absolute',
      height: '28px',
      left: 0,
      right: 0,
      display: 'block',
      padding: '0 2px',
      overflow: 'hidden',
      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', {
      boxSizing: 'border-box',
      border: '0 none',
      fontFamily: 'Consolas, monaco, monospace',
      position: 'relative',
      display: 'block',
      height: getMaxHeight() + 'px',
      overflow: 'auto',
      webkitOverflowScrolling: 'touch',
      '-webkit-transition': options.animParams,
      '-moz-transition': options.animParams,
      '-o-transition': options.animParams,
      'transition': options.animParams
    }),
    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',
      tableLayout: 'auto',
      maxWidth: '100%',
      color: '#333333'
    }),
    tr : createElem('tr', 'table_row', {
      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'
    }),
    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',
      fontFamily: '"Helvetica Neue",Helvetica,Arial,sans-serif',
      fontWeight: 'normal',
      textTransform: 'capitalize',
      fontSize: '12px',
      lineHeight: '26px',
      height: '26px',
      padding: '0 8px',
      margin: 0,
      textAlign: 'center',
      marginRight: '5px',
      border: '0 none',
      backgroundColor: 'transparent',
      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',
      fontSize: '16px',
      fontFamily: 'Consolas, monaco, monospace',
      fontWeight: 'bold',
      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',
      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' : '28px',
      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',
      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==">'
  };

  //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 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);
          }
        }
      };
    }()),
    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('/').filter(Boolean).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' : '0px';
        setCSS(elements.scrollcontainer, {
          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()
        );
      };
    }());

  // --==** sub functions start here **==--

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

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

      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 {
            elements.buttons[methods[i]].attachEvent('onclick', toggleLogType);
          }
        }
        if (options.hideButtons.indexOf(methods[i]) !== -1) {
          setCSS(elements.buttons[methods[i]], { display: 'none' });
        }
      }
      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);
      }
    }
    //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;
  }

  //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'
      );
    }
    function objectToString(object) {
      var simpleObject = {}, prop, classname = getClass(object);
      if (!isElement(object)) {
        for (prop in object) {
          if (!object.hasOwnProperty(prop) || (typeof (object[prop]) === 'object') || (typeof (object[prop]) === 'function')) {
            continue;
          }
          simpleObject[prop] = object[prop];
        }
        return '<em>' + classname + ' ' + JSON.stringify(simpleObject) + '</em>'; // returns cleaned up JSON
      }
      return htmlToString(object.outerHTML);
    }
    function urlFromString(string) {
      string =  String(string);
      //searches for url in string, returns url as string
      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 {
        match = string.match(uriPattern)[0];
        return match;
      } catch (e) {
        return '';
      }
    }
    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 !== ''; });

        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,
        txt,
        thisLine,
        lineAndColumn,
        caller,
        separator = options.browserinfo.ffox ? '@' : '()';

      //stop if no source trace can be determined
      if (!traceToProcess) { return; }

      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();
        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 (url[url.length - 1] === '/') {
          txt = '(index)';
        } else {
          txt = url.split('\\').pop().split('/').filter(Boolean).pop() || caller;
        }
        callStack.push({
          caller: caller,
          url:    url ? url.split(':')[0] + ':' + url.split(':')[1] : caller,
          linkText: txt + lineAndColumn[0],
          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 + '"';
        }
      }
      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' });
        }
      }
    }
    function isRepeat(message, method) {
      return (history.output.prevMsg === message && history.output.prevMethod === method) && (typeof message !== 'object') && (method !== 'trace') && (method !== 'group') && (method !== 'groupCollapsed') && (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;

        //if message emtpy, show empty message-message
        if (message === '') { message = messages.empty; }

        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;

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

          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 === 'table' || typeof arguments[1].newMessage === 'object') {
                detailTable = objectToTable(elements.stackTraceTable.cloneNode(false), arguments[1].newMessage);
                msgContainer.innerHTML = elements.arrowRight + '&nbsp;' + message;
              } else if (method === 'trace') {
                message = 'console.trace()';
                msgContainer.innerHTML = arrows + message;
              } else {
                msgContainer.innerHTML = arrows + message;
              }
              stackTable = traceToTable(elements.stackTraceTable.cloneNode(false), stackTrace);
              break;
            case 'group':
            case 'groupCollapsed':
            case 'groupEnd':
              if (method !== 'groupEnd') {
                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; }
          leftContainer.appendChild(msgContainer);

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

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

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


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

      //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;
      }
      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 {
        elements.autocomplete.attachEvent('onclick', clickAutoComplete);
      }
      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;
  }

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

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

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

}());