javascript abstract console logging - javascript

I want to make a function, like this.
For example:
function Logger() {
this.log = function(msg) {
console.log(msg);
}
}
And I want to use it in functions/modules etc, and that all works fine.
But the default console in my browser normally give the fileName + lineNumber.
Now when I abstract this functionality, the fileName and lineNumber is not where I put my instance.log(). Because it will say from where the console.log is being called, not the function itself.
So my question:
How can I get the correct information from where I want to use my logger?
Or give me, please, any tips to improve this functionality.

function Logger() {
this.log = console.log.bind(console);
}
I asked about this some time ago: Create shortcut to console.log() in Chrome.

Try using backtrace function like this one :
function printStackTrace() {
var callstack = [];
var isCallstackPopulated = false;
try {
i.dont.exist += 0; //doesn't exist- that's the point
} catch (e) {
if (e.stack) { //Firefox
var lines = e.stack.split('\n');
for (var i = 0, len = lines.length; i & lt; len; i++) {
if (lines[i].match(/^\s*[A-Za-z0-9\-_\$]+\(/)) {
callstack.push(lines[i]);
}
}
//Remove call to printStackTrace()
callstack.shift();
isCallstackPopulated = true;
}
else if (window.opera & amp; & amp; e.message) { //Opera
var lines = e.message.split('\n');
for (var i = 0, len = lines.length; i & lt; len; i++) {
if (lines[i].match(/^\s*[A-Za-z0-9\-_\$]+\(/)) {
var entry = lines[i];
//Append next line also since it has the file info
if (lines[i + 1]) {
entry += ' at ' + lines[i + 1];
i++;
}
callstack.push(entry);
}
}
//Remove call to printStackTrace()
callstack.shift();
isCallstackPopulated = true;
}
}
if (!isCallstackPopulated) { //IE and Safari
var currentFunction = arguments.callee.caller;
while (currentFunction) {
var fn = currentFunction.toString();
var fname = fn.substring(fn.indexOf( & amp; quot;
function & amp; quot;) + 8, fn.indexOf('')) || 'anonymous';
callstack.push(fname);
currentFunction = currentFunction.caller;
}
}
output(callstack);
}
function output(arr) {
//Optput however you want
alert(arr.join('\n\n'));
}

Try assigning the function:
(function () {
window.log = (console && console.log
? console.log
: function () {
// Alternative log
});
})();
Later just call log('Message') in your code.

Related

How to underline charachters in InDesign with JavaScript?

I started writing this piece of code for InDesign to underline all letters except from the one with descendants, and added a dialog window to chose stroke and offset of the line.
Now I have two problems:
the program underlines all letters
the stroke and offset won't change
I'm a beginner in Javascript and it's the first time coding for InDesign. Does someone have a clue? Thank you!
// UNDERLINE ALL BUT NO DESCENDANTS
//Make certain that user interaction (display of dialogs, etc.) is turned on.
app.scriptPreferences.userInteractionLevel = UserInteractionLevels.interactWithAll;
if (app.documents.length != 0){
try {
// Run script with single undo if supported
if (parseFloat(app.version) < 6) {
main();
} else {
app.doScript(main, ScriptLanguage.JAVASCRIPT, undefined, UndoModes.ENTIRE_SCRIPT, "Expand State Abbreviations");
}
// Global error reporting
} catch ( error ) {
alert( error + " (Line " + error.line + " in file " + error.fileName + ")");
}
}else{
alert("Open a document first before running this script.");
}
///MAIN FUNCTION
function main(){
if(app.selection.length != 0){
myDisplayDialog();
}
}
//INTERFACE
function myDisplayDialog(){
//declare variables
//general
var myDoc = app.activeDocument;
var mS = myDoc.selection;
// dialog
var myDialog = app.dialogs.add({name:"Underliner"});
var myLabelWidth = 70;
with(myDialog.dialogColumns.add()){
with(borderPanels.add()){
with(dialogColumns.add()){
with(dialogRows.add()){
staticTexts.add({staticLabel:"Stroke:", minWidth:myLabelWidth});
staticTexts.add({staticLabel:"Offset:", minWidth:myLabelWidth});
}
}
with(dialogRows.add()){
staticTexts.add({staticLabel:""});
var myStroke = measurementEditboxes.add({editValue:1, editUnits:MeasurementUnits.points});
var myOffset = measurementEditboxes.add({editValue: 15, editUnits:MeasurementUnits.points});
}
}
}
var myResult = myDialog.show();
if(myResult == true){
var myStroke = myStroke.editValue;
var myOffset = myOffset.editValue;
myDialog.destroy();
underline(mS,myStroke,myOffset);
}
else{
myDialog.destroy();
alert("Invalid page range.");
}
}
//REAL FUNCTION
function underline(charList,stroke, offset){
var len = charList.length;
const doNotUnderline = ['g','j','p','q','y'];
for (var i=0; i < len; i++){
try{
var myChar = charList[i];
//console.log(typeof myText);
if (includes(myChar, doNotUnderline) == false)
{
myChar.underline = true;
myChar.underlineWeight == stroke;
myChar.underlineOffset == offset;
} else {
myChar.underline = false;
}
}catch(r){
alert(r.description);
break;
}
}
}
//function to know if char is in array
function includes(elemento,array)
{
var len = array.length;
for(var i=0; i<len ;i++)
{
if(array[i]==elemento){return true;}
}
return false;
}
Try these changes in the function underline():
//REAL FUNCTION
function underline(words,stroke, offset) { // <------ here 'words' instead of 'charList'
var charList = words[0].characters; // <------ here get 'characters' of the 'words'
var len = charList.length;
const doNotUnderline = ['g','j','p','q','y'].join(); // <------- here '.join()'
for (var i=0; i < len; i++){
try{
var myChar = charList[i];
// if (includes(myChar, doNotUnderline) == false) // <----- no need
if (doNotUnderline.indexOf(myChar.contents) < 0) // <------ 'indexOf()' instead of 'includes()'
{
myChar.underline = true;
myChar.underlineWeight = stroke; // <------- here '=' instead of '=='
myChar.underlineOffset = offset; // <------- here '=' instead of '=='
} else {
myChar.underline = false;
}
}catch(r){
alert(r.description);
break;
}
}
}
Probably there can be another improvements as well. It's need additional researches. But if you change these lines it should work to a degree.
And there is one little thing that improves user experience greatly: to keep last used values in the input fields. It can be done pretty easy, let me know it you need it.
Update
Here is the way I'm using to store and restore any preferences of my scripts.
Add somewhere at the start of your script these lines:
// get preferences
var PREFS = { stroke: 1, offset: 15 }; // set default prefs
var PREFS_FILE = File(Folder.temp + '/underline_prefs.json'); // the file with preferences
if (PREFS_FILE.exists) PREFS = $.evalFile(PREFS_FILE); // get the prefs from the file
Now you can use the global values PREFS.stroke and PREFS.offset anywhere you want. In your case they go here:
with(dialogRows.add()){
staticTexts.add({staticLabel:""});
var myStroke = measurementEditboxes.add({editValue:PREFS.stroke, editUnits:MeasurementUnits.points});
var myOffset = measurementEditboxes.add({editValue:PREFS.offset, editUnits:MeasurementUnits.points});
}
This way script will get the stroke and weight from the file underline_prefs.json that will be stored in the standard temporary folder of current user.
Final step is to save the values back into the file after the script got them from the dialog window.
I'd put this piece of code here:
if (myResult == true) {
var myStroke = myStroke.editValue;
var myOffset = myOffset.editValue;
myDialog.destroy();
underline(mS, myStroke, myOffset);
// save preferences here
PREFS.stroke = myStroke;
PREFS.offset = myOffset;
PREFS_FILE.open('w');
PREFS_FILE.write(PREFS.toSource());
PREFS_FILE.close();
} else {
myDialog.destroy();
alert("Invalid page range.");
}
Voilá. Now don't need to type the values every time they differ from default ones.

Getting javascript undefined TypeError

Please help....Tried executing the below mentioned function but web console always shows
TypeError: xml.location.forecast[j] is undefined
I was able to print the values in alert but the code is not giving output to the browser because of this error. Tried initializing j in different locations and used different increment methods.How can i get pass this TypeError
Meteogram.prototype.parseYrData = function () {
var meteogram = this,xml = this.xml,pointStart;
if (!xml) {
return this.error();
}
var j;
$.each(xml.location.forecast, function (i,forecast) {
j= Number(i)+1;
var oldto = xml.location.forecast[j]["#attributes"].iso8601;
var mettemp=parseInt(xml.location.forecast[i]["#attributes"].temperature, 10);
var from = xml.location.forecast[i]["#attributes"].iso8601;
var to = xml.location.forecast[j]["#attributes"].iso8601;
from = from.replace(/-/g, '/').replace('T', ' ');
from = Date.parse(from);
to = to.replace(/-/g, '/').replace('T', ' ');
to = Date.parse(to);
if (to > pointStart + 4 * 24 * 36e5) {
return;
}
if (i === 0) {
meteogram.resolution = to - from;
}
meteogram.temperatures.push({
x: from,
y: mettemp,
to: to,
index: i
});
if (i === 0) {
pointStart = (from + to) / 2;
}
});
this.smoothLine(this.temperatures);
this.createChart();
};
You are trying to access the element after the last one. You can check if there is the element pointed by j before proceeding:
Meteogram.prototype.parseYrData = function () {
var meteogram = this,
xml = this.xml,
pointStart;
if (!xml) {
return this.error();
}
var i = 0;
var j;
$.each(xml.location.forecast, function (i, forecast) {
j = Number(i) + 1;
if (!xml.location.forecast[j]) return;
var from = xml.location.forecast[i]["#attributes"].iso8601;
var to = xml.location.forecast[j]["#attributes"].iso8601;
});
};

Javascript For Loop interrupted by function call?

I have a for-loop that is terminating without finishing the loop. It seems to be related to whether or not a call to another function is made within the loop.
this.cycle = function() {
var list = this.getBreaches("Uncontained");
if (list.length > 0) {
for (i=0; i < list.length; i++) {
this.saveVsRupture(DC=11, i); //calls this.rupture() if save failed
}}
return 1;
};
this.saveVsRupture() calls a function that rolls a d20 and checks the result. If the save fails, it calls a method called this.rupture() that does some adjusting to this.
Problem
If the saving throw is successful, the loop continues, but if the saving throw fails, it runs the this.rupture() function and then breaks the for-loop. It should keep running the for-loop.
Why is this happening?
Additional Details
Here are the other functions...
savingThrow = function(DC=11) {
// DC = Difficulty Check on a d20
try {
if (0 <= DC) {
var roll = Math.floor((Math.random() * 20))+1; //roll d20
var msg = "(Rolled "+roll+" vs DC "+DC+")";
console.log(msg);
if (roll >= DC) { //Saved
return true;
}
else { //Failed save
return false;
}
}
}
catch(e) {
console.log("Exception in savingThrow: "+e);
};
};
this.saveVsRupture = function(DC=1, i=null) {
try {
if (!savingThrow(DC)) {
this.rupture(i);
return false;
}
return true;
}
catch(e) {
console.log(e);
}
};
this.rupture = function(i=null) {
if (i == null) {
i = range(1,this.damageList.length).sample();
}
var hole = this.damageList[i];
var dmg = range(1,this.harmonics()).sample();
hole.rupture(dmg);
msg = "*ALERT* " + hole + " expanded by " + dmg + "% Hull Integrity #"+this.hullIntegrity();
this.log(msg);
if (hole.size % 10 == 0) {
this.health -= 25;
msg = "The ship creaks ominously.";
this.log(msg);
}
return 1;
};
The correct syntax for the for-loop declares the counter variable.
for (var i=0; i < list.length; i++) {etc..
/// Causes the For-Loop to exit prematurely...
for (i=0; i < list.length; i++) {etc..
Once the "var i=0" is used, the for-loop operates as expected.
consider implementing a try-catch in your for loop, with your saveVsRupture function within the try. This implementation will catch errors in your function but allow the program to keep running.
Change the saveVsRupture function like this:
function saveVsRupture(a,b) {
try{
//your saveVsRupture code here...
}
catch(e){}
}
Your should catch problems that occurred in your code with try,catch block to prevent throw them to top level of your code (the browser in this example) .
Don't use return in for loop!
Change your code as following:
this.cycle = function() {
var list = this.getBreaches("Uncontained");
if (list.length > 0) {
for (i=0; i < list.length; i++) {
var temp = this.saveVsRupture(DC=11, i); //calls this.rupture() if save failed
console.log(temp);
}}
return 1;
};

Debugging Javascript in IE 8

I'm trying to debug an issue that I'm only having in IE8. It works fine in IE 9+, and chrome. I'm using Aspera to select a file, and am calling a custom function on a callback. the function is as follows;
function uploadPathsRecieved(pathsArray) {
var file_path_selector = '#file_path';
...
$(file_path_selector).text(''); // (*)
...
}
On the (*) line, I get an error that file_path_selector is undefined. This didn't make much sense to me, so after some playing around to get a feel for the problem, I wound up with the following code:
function uploadPathsRecieved(pathsArray) {
var x = 3;
var y = 4;
var z = x + y;
z += 2;
$('#file_path').text(''); // (*)
...
}
When I run the program with this code, I still get the error "file_path_selector is undefined" at the (*) line. I'm out of ideas on what the next steps I should take to try and hunt down this problem are.
My gut feeling tells me that there's something being cached, but if I move the (*) line around, the error follows it, and the script window reflects the changes that I make to it.
Here's the Aspera code that's calling my function:
function wrapCallbacks(callbacks) {
return wrapCallback(function() {
var args, i;
try {
args = Array.prototype.slice.call(arguments);
for ( i = 0; i < args.length; i++) {
if (isObjectAndNotNull(args[i]) && isDefined(args[i].error)) {
// error found
if (isDefined(callbacks.error)) {
callbacks.error.apply(null, args);
}
return;
}
}
// success
if (isDefined(callbacks.success)) {
callbacks.success.apply(null, args);
}
} catch (e) {
AW.utils.console.error(e.name + ": " + e.message);
AW.utils.console.trace();
}
});
}
And here's the entirety of my function, as it exists right now:
var uploadPathsRecieved = function uploadPathsRecieved(pathsArray) {
//var file_path_selector = '#file_path';
var x = 3;
var y = 4;
var z = x + y;
z += 2;
$('#file_path').text('');
var button_selector = '#select_aspera_file';
var textbox_selector = '.aspera_textbox';
/*if (uploadPathsRecieved.fileSelecting == 'cc_file') {
file_path_selector = '#cc_file_path';
button_selector = '#select_cc_file';
textbox_selector = '.cc_aspera_textbox';
} else if (uploadPathsRecieved.fileSelecting == 'preview_file') {
file_path_selector = '#preview_file_path';
button_selector = '#select_preview_file';
textbox_selector = '.preview_aspera_textbox';
}*/
App.AsperaUploadPaths = [];
if (pathsArray.length == 1) {
$(button_selector).text("Clear File");
App.AsperaUploadPaths = pathsArray;
var error_message = pathsArray[0];
$(button_selector).parent().children(textbox_selector).text(error_message).removeClass('error');
//$(file_path_selector).attr('value', pathsArray[0]);
}
else
{
var error_message = 'Please select a single file';
$(button_selector).parent().children(textbox_selector).text(error_message).addClass('error');
}
}
Solved it. file_path was an <input>, and IE 8 and below doesn't allow you to add text or html to inputs.
I fixed it by changing $(file_path_selector).text(''); to $(file_path_selector).attr('value', '');

Customized Stack Traces in Google Chrome Developer Tools?

I'm looking to customize the items that show up in the strack trace panel in the Scripts tab of Google Chrome's developers tools. Specifically, I want to filter out items in the stack trace and to add more descriptive names to some of the items on the stack trace without having to rename my objects and functions.
I found V8's Stack Trace API at http://code.google.com/p/v8/wiki/JavaScriptStackTraceApi but overriding Error.prepareStackTrace doesn't seem to have any effect.
The description on that page is definitely a little hard to follow, here's how it's done:
Error.prepareStackTrace = function(error, stack) {
return stack;
};
var someObj = {
someMethod : function () {
crash();
}
}
function bar(barArg) { someObj.someMethod(); };
function foo(fooArg) { bar("barArgString"); };
function getTrace(e) {
var stack = e.stack;
var trace = "";
for (var i = 0; i < stack.length; i++) {
var frame = stack[i],
func = frame.getFunction();
trace += "\r" + frame.getThis() + "." + frame.getFunctionName();
}
return trace;
}
try {
foo("fooArgString");
} catch (e) {
alert("trace from catch(): " + getTrace(e));
}
This will show:
trace from catch():
[object Object].someObj.someMethod
[object Window].bar
[object Window].foo
[object Window].
The last frame is global scope (no function name).
Essentially your override of prepareStackTrace() causes error.stack to become whatever you return from prepareStackTrace(). The trick is that the second argument to prepareStackTrace() is an Array of CallSite objects - the objects that support getThis(), getFunctionName() etc.
The code above overrides prepareStackTrace() so that it returns the Array of CallSite objects ("stack" parameter above), so this means when you try..catch an Error, Error.stack is going to contain the Array of CallSite objects instead of the usual stack trace in String form. Another approach would be to process the CallSite objects inside of your replacement prepareStackTrace() function and return your alternative stack trace as a String.
Note the CallSite objects are really finicky. Try to do frame.toString(), or just try to alert(frame) (implicitly this involves toString()) and it crashes and Chrome's developer tools show no error.
Here's the code that did the trick for me:
<head>
<script>
Error.prepareStackTrace = function()
{
return "MyStackObject";
}
try {
throw new Error();
} catch (e) {
console.log(e.stack);
}
</script>
</head>
The documentation has moved here:
https://github.com/v8/v8/wiki/Stack-Trace-API
Just put this at the beginning of your javascript code, it formats a nice stack trace:
Error.prepareStackTrace = function(error, stack) {
var trace = '';
var max_width = 0;
for (var i = 0; i < stack.length; i++){
var frame = stack[i];
var typeLength = 0;
typeLength = (frame.getTypeName() !== null && frame.getTypeName() !== '[object global]') ? frame.getTypeName().length : 0;
typeLength = typeLength.length > 50 ? 50 : typeLength;
functionlength = frame.getFunctionName() !== null ? frame.getFunctionName().length : '<anonymous>'.length;
functionlength = functionlength > 50 ? 50 : functionlength;
if (typeLength + functionlength > max_width)
max_width = typeLength + functionlength;
}
for (var i = 0; i < stack.length; i++) {
var frame = stack[i];
var filepath = frame.getFileName();
var typeName = '';
if (frame.getTypeName() !== null && frame.getTypeName() !== '[object global]')
typeName = frame.getTypeName().substring(0, 50) + '.';
var functionName = '<anonymous>';
if (frame.getFunctionName() !== null)
functionName = frame.getFunctionName().substring(0, 50);
var space = '';
var width = max_width - (typeName.length + functionName.length) + 2;
space = Array(width).join(' ');
var line = ' at ' + typeName + functionName + space + filepath +
' (' + frame.getLineNumber() +
':' + frame.getColumnNumber() + ')\n';
trace += line;
}
return trace;
};
Here's an example to the test the code:
function A() { B(); }
function B() { C(); }
function C() { throw new Error('asd'); }
try {
A();
} catch (e) { print(e + '\n' + e.stack); }

Categories

Resources