mirror of
https://github.com/lxsang/antd-web-apps
synced 2024-11-20 10:18:21 +01:00
1325 lines
51 KiB
JavaScript
1325 lines
51 KiB
JavaScript
/*!
|
|
* 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, '&').replace(/</g, '<').replace(/>/g, '>').replace(/"/g, '"').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 = ' @ ';
|
|
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 + ' ' : '';
|
|
|
|
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 + ' ' + 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 + ' ' + 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 = '>';
|
|
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
|
|
};
|
|
|
|
}()); |