From Code to Community: Sponsoring The Perl and Raku Conference 2025 Learn more

//; use Qgoda::JavaScript::Filter('Qgoda::JavaScript::console');
/*
* Copyright (C) 2016-2018 Guido Flohr <guido.flohr@cantanea.com>,
* all rights reserved.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/* Polyfill for a minimal console. Less minimal than the original
* Duktape console, and it allows tieing the standard streams.
*
* Apart from that, the code is a mess and should be replaced with something
* reasonable.
*/
(function () {
// Pre-fill escapes.
const escapes = [];
for (var i = 0; i < 32; ++i) {
var hex = i.toString(16);
if (i < 0x10)
escapes[i] = '\\u000' + hex;
else
escapes[i] = '\\u00' + hex;
escapes[0x8] = '\\b';
escapes[0x9] = '\\t';
escapes[0xa] = '\\n';
escapes[0xc] = '\\f';
escapes[0xd] = '\\r';
}
Object.defineProperty(this.console, 'log', {
value: function () {
__perl__.modules.console.log(format(arguments));
}, writable: true, enumerable: false, configurable: true
});
Object.defineProperty(this.console, 'warn', {
value: function () {
__perl__.modules.console.warn(format(arguments));
}, writable: true, enumerable: false, configurable: true
});
Object.defineProperty(this.console, 'error', {
value: function () {
__perl__.modules.console.error(format(arguments));
}, writable: true, enumerable: false, configurable: true
});
function format(args) {
const output = [];
var i = 0;
if (args.length > 1 && 'string' === typeof args[0]
&& args[0].match(/%[sjdif%]/)) {
// Format string.
++i;
var fmt = args[0];
var interpolated = fmt.replace(/%(.)/g, function(match, p1) {
if (i >= args.length) return '%' + p1;
switch(p1) {
case 'd':
return Number(args[i++]);
case 's':
return String(args[i++]);
case 'f':
return parseFloat(args[i++]);
case 'i':
return ;
case 'j':
var retval;
try {
retval = JSON.stringify(args[i++]);
} catch(e) {
retval = args[--i];
}
return retval;
case 'o':
case 'O':
// Marginal support for Node.JS's %o and %O.
return inspect(args[i++]);
case '%':
return '%';
default:
return '%' + p1;
}
});
output.push(interpolated);
}
for (; i < args.length; ++i) {
output.push(inspect(args[i]));
}
return output.join(' ');
}
function stringify(obj) {
return obj.replace(/[\u0000-\u001f\\']/g, function(match) {
if (match === "'")
return "\\'";
else if (match === '\\')
return '\\\\';
else
return escapes[match.charCodeAt(0)];
});
}
function objectType(obj) {
if (obj === null) {
return 'null';
} else if ('[object Array]' === toString.call(obj)) {
return 'array';
} else {
return 'object';
}
}
/*
* Similar to util.inspect from NodeJS but all options are ignored.
* Circular references should be handled correctly but the function
* recurses infinitely (FIXME! This should probably be changed) and
* it always prints all array elements.
*/
function inspect(root) {
var seen = [];
var depth = 0;
const inspectRecursive = function(tokens, obj) {
// FIXME! How does Node.JS actually count the depth?
++depth;
var type = typeof obj;
if ('object' === type) type = objectType(obj);
if ('array' === type) {
if (seen.indexOf(obj) !== -1) {
tokens.push('[Circular]');
return;
}
seen.push(obj);
tokens.push('[');
for (var i = 0; i < obj.length; ++i) {
if (i !== 0) tokens.push(', ');
const item = obj[i];
Array.prototype.push.apply(tokens,
inspectRecursive(tokens, item));
}
tokens.push(']');
} else if ('object' === type) {
if (seen.indexOf(obj) !== -1) {
tokens.push('[Circular]');
return;
}
seen.push(obj);
tokens.push('{');
var count = 0;
for (var prop in obj) {
if (!obj.hasOwnProperty(prop)) continue;
if (count++) tokens.push(', ');
if (prop.match(/^[_a-zA-Z][_a-zA-Z0-9]*$/)) {
tokens.push(prop + ': ');
} else {
tokens.push("'" + stringify(prop) + "': ");
}
Array.prototype.push.apply(tokens,
inspectRecursive(tokens,
obj[prop]))
}
tokens.push('}');
} else if ('string' === type) {
if (depth !== 1) {
tokens.push("'" + stringify(obj) + "'");
} else {
tokens.push(obj);
}
} else if ('function' === type) {
tokens.push('[Function]');
} else if ('null' === type) {
tokens.push('null');
} else if ('undefined' === type) {
tokens.push('undefined');
} else if ('number' === type) {
tokens.push(obj);
} else {
tokens.push(obj);
}
}
const output = [];
inspectRecursive(output, root);
return output.join('');
}
})();