Equivalent of "GoTo" in Google Apps Script (equivalent VBA-GAS ) - javascript

When writing my VBA macros I often used "GoTo" so as to jump to a previous part of the macro without leaving the Sub. Now that I’m converting all my macros to Google Apps Script I’m trying to find the equivalent for “GoTo”.
Sub MySub()
Dim sheetname1 As String
Dim sheetname2 As String
On Error GoTo Err
sheetname1 = ActiveSheet.Name
Sheets.Add After:=Sheets(Sheets.Count)
ActiveSheet.Name = "passwords"
sheetname2 = ActiveSheet.Name
GoTo aftererr
Err:
MsgBox Error(Err)
Exit Sub
aftererr:
This is just one instance of my use of GoTo. However I need it for my new scripts in many other ways; not just for redirecting errors. For example:
function MyFunction() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sh = ss.getActiveSheet();
if(criteraA == criteraB){
sh.offset(1,0).activate();
var i=i + 1;
//?? GoTo ??
}else{
var i=0;
sh.getRange(row, column)(1,sr.offset(0,1).getColumn()).activate();
}

You don't need GoTo, most people would argue that it is terrible programming practice to use it even when it is present. Using other control structures will do the job.
if() {
} else if() {
} else {
}
for(;;) {
continue;
break;
}
while() {
}
do {
} while();
switch() {
case:
default:
}
// for errors
throw "Error string"
try {
} catch(error) {
}
You'll have to shuffle your logic around a bit, but it will result is better more maintainable code.

Related

Javascript - replace loop with native js code problem for Google Tag Manager

Apologies in advance for my ignorance. I've searched the site, but haven't had any luck.
Rather than manually enter each hostname via GA's Admin Interface, utilize the following JS function in GTM to defines the list of exclusions (var referrals), create/compare incoming {{Referrer}} (.exec, .test methods), and then null the {{Referrer}} if it's on the list, or lets it pass unmodified to GA if no match is found:
function()
{
var referrals = [
'domain_one.com',
'domain_two.com',
'domain_three.refer.com',
'store.domain_three.refer.com'
];
var hname = new RegExp('https?://([^/:]+)').exec({{Referrer}});
if (hname) {
for (var i = referrals.length; i--;) {
if (new RegExp(referrals[i] + '$').test(hname[1])) {
return null;
}
}
}
return {{Referrer}};
}
I sent the code to a developer for feedback, and he suggested replacing the for loop with with this (a direct replacement for the loop):
if (referrals.find(function(referral) { return hname[1].includes(referral); })) { return null; } else { return {{ Referrer }};
I attempted to do so like this:
function()
{
var referrals = [
'domain_one.com',
'domain_two.com',
'domain_three.refer.com',
'store.domain_three.refer.com'
];
var hname = new RegExp('https?://([^/:]+)').exec({{ Referrer }});
if (hname) {
if (referrals.find(function(referral) { return hname[1].includes(referral); })) { return null; } else { return {{ Referrer }};
}
When attempting to publish this in GTM, I'm getting both parsing errors as well as unreferenced variable errors for {{Referrer}}.
If anyone has some feedback, I'd be super super grateful.
Uh... your developer doesn't know GTM's syntax. So now for this to work, it's either the developer needs to know GTM's variable syntax or for you to know JS, so I suggest you to use your old code. It's better to use the code you understand than the code you won't be able to maintain.
If you still wanna use it, try removing spaces from the variable reference.
And you forgot to close the else claus: else { return {{ Referrer }};} And one more time to close the external if... else { return {{ Referrer }};}}
And now it looks like a mess, so here, try this:
function() {
var referrals = [
'domain_one.com',
'domain_two.com',
'domain_three.refer.com',
'store.domain_three.refer.com'
];
if (/https?:\/\/([^\/:]+)/i.test({{Referrer}})) {
if (referrals.find(function (referral) { return hname[1].includes(referral); })) { return null; } else { return {{Referrer}}; }
}
}

Checkout querySelector

I am relatively new to Javascript. I am trying to get the Price Value on our Checkout page.
document.querySelector returns different values if the checkout is made via Prepayment and Paypal.
Here is my code:
function() {
try {
var Prepayment = document.querySelector(".order-number>strong").innerText.trim();
var Paypal = document.querySelector(".checkout-success>p>span").innerText.trim();
if (document.querySelector(".order-number>strong") != null) {
return capturedText;
} else {
return Paypal;
}
} catch(e) {
return "";
}
}
Upon using the Tag manager, the Tag is giving me "" value.
Can someone help me how do i execute the queries with the QuerySelector within the If / Else environment, so does the function cannot block the entire script if my element which I am looking was not found.

OR operator in extendscript

I'm creating some scripts voor InDesign to speed up the process.
I have created a script where a certain line, I think, should work but InDesign disagrees.
It fails on ("Geen"||"None"); in the following
app.changeGrepPreferences.appliedCharacterStyle = myDoc.characterStyles.item("[Geen]"||"[None]");
I expect it to change to a characterStyle [Geen] or [None]. Depending on what is available in the predefined character styles.
What am I doing wrong? This seems kinda basic.
Unfortunately is not that easy. If you use doc.characterStyles.item('foo') it still will give you an [object CharacterStyle]. Even tough it does not exsist.
var doc = app.activeDocument;
$.writeln(doc.characterStyles.item('foo'));
// writes [object CharacterStyle] into the console
What you can do is use a try{}catch(error){} block and ask for the name property of that object. In that case InDesign will throw an error that you can catch. Then you can fall back to the default character style [None]
var doc = app.activeDocument;
try{
$.writeln(doc.characterStyles.item('foo').name);
}catch(e) {
$.writeln(e);
$.writeln(doc.characterStyles.item('[None]').name);
}
Edit: As mentioned by mdomino. You can use the isValid property.
var doc = app.activeDocument;
if(doc.characterStyles.item('foo').isValid === true) {
$.writeln('doc.characterStyles.item(\'foo\') exists');
} else {
$.writeln('use doc.characterStyles.item(\'[None]\') because ');
var defaultStyle = doc.characterStyles.item('[None]');
$.writeln(defaultStyle.name + ' is ' + defaultStyle.isValid);
}

Improvement to my horrible switch statement for regex matching

I'm trying to get away from using horrible switch cases in node.js. I am looking for a more efficient way of testing an input against various regex cases. Dependent on the case that is matched I either fire an event or I do some transformation of the input before running another function.
To save having a really long block of code I have cut down my function to the skeleton below so it shows a focus on the switch.
I've taken a look at the possibility of using .map to return a true false but I'm unsure how best to implement that also.
Any advise or suggestions on the best way to do this?
function ParseLogMessages(message, config, callback){
var _this = this;
try {
//Define regex in order to match strings based on case
_this.to_group = new RegExp("^\\[\\d{2}:\\d{2}\\]\\s+\\w+\\s+tg+\\s\\>{3}");
_this.from_group=new RegExp("^\\[\\d\\d:\\d\\d\\]\\s\\w+\\s\\w+\\s\\>{3}");
_this.to_person = new RegExp("^\\[\\d{2}:\\d{2}\\]\\s[a-zA-Z0-9 \\- _]+\\s\\<{3}.+");
_this.from_person = new RegExp("^\\[\\d{2}:\\d{2}\\]\\s\\w+\\s\\>{3}");
_this.contact = new RegExp("(User #+\\d+:)");
_this.contact = new RegExp("(User #+\\d+:)");
//Test message against each to find type
switch (true){
//Message sent to a group chat
case _this.to_group.test(_this.payload.raw):
break;
//Message from a group chat
case _this.from_group.test(_this.payload.raw):
break;
//Message sent to a person from the bot
case _this.to_person.test(_this.payload.raw):
break;
//Message sent from a person to the bot
case _this.from_person.test(_this.payload.raw):
break;
//Contact shared
case _this.contact.test(_this.payload.raw):
break;
default:
break;
}
callback(null,"Logfile message parsed ok!");
} catch(err) {
log.error(err);
return callback(err,null);
}
}
You can create an array of regex/function pairs and loop through the array:
_this.tests = [
{ regex: new RegExp("^\\[\\d{2}:\\d{2}\\]\\s+\\w+\\s+tg+\\s\\>{3}"), // to_group
action: ... // action for to_group
},
{ regex : new RegExp("^\\[\\d\\d:\\d\\d\\]\\s\\w+\\s\\w+\\s\\>{3}"), // from_group
action: ... // action for from_group
},
// etc.
];
Then you can loop through the array, testing, and breaking when the test works:
for (i=0; i<tests.length; ++i) {
if (tests[i].regex.test(_this.payload.raw) {
tests[i].action();
break;
}
}
You can put the objects in an array and call the test function until one returns true:
var o = [
_this.to_group,
_this.from_group,
_this.to_person,
_this.from_person,
_this.contact
];
for (var i in o) {
if (o[i].test(_this.payload.raw)) {
// got a match
break;
}
}
What you want is to convert that into an associative array and match with a loop. Untested code that should work:
let patterns = {
"^\\[\\d{2}:\\d{2}\\]\\s+\\w+\\s+tg+\\s\\>{3}": funcToGroup /* code for this case, preferably a [reference to a] function object without the parens */,
"^\\[\\d\\d:\\d\\d\\]\\s\\w+\\s\\w+\\s\\>{3}": function () {
// An inline anonymous function is also fine
},
"^\\[\\d{2}:\\d{2}\\]\\s[a-zA-Z0-9 \\- _]+\\s\\<{3}.+": funcToPerson,
"^\\[\\d{2}:\\d{2}\\]\\s\\w+\\s\\>{3}": funcFromPerson,
"(User #+\\d+:)": funcContactShared
};
for (let pat in _this.patterns) {
if (new RegExp(pat).test(_this.payload.raw)) {
_this.patterns[pat](); // Actually execute the relevant case
}
}
That should handle all the code within the try block.

throwing a debug from chrome extension content script

Short version
Trying to write a debug command that returns the call stack, minus the current position. I thought I'd use:
try {
throw new Error(options["msg"])
} catch (e) {
e.stack.shift;
throw (e);
}
but I don't know how to do it exactly. apparently I can't just e.stack.shift like that. Also that always makes it an Uncaught Error — but these should just be debug messages.
Long version
I decided I needed a debug library for my content scripts. Here it is:
debug.js
var debugKeys = {
"level": ["off", "event", "function", "timeouts"],
"detail": ["minimal", "detailed"]
};
var debugState = { "level": "off", "detail": "minimal" };
function debug(options) {
if ("level" in options) {
if (verifyDebugValue("level", options["level"]) == false)
return
}
if ("detail" in options) {
if (verifyDebugValue("detail", options["detail"]) == false)
return
}
console.log(options["msg"]);
}
function verifyDebugValue(lval, rval){
var state = 10; // sufficiently high
for (k in debugKeys[lval]) {
if (debugKeys[lval][k] == rval) {
return true;
}
if (debugKeys[lval][k] == debugState[lval]) { // rval was greater than debug key
return false;
}
}
}
When you using it, you can change the debugState in the code to suit your needs. it is still a work in progress but it works just fine.
To use it from another content script, just load it in the manifest like:
manifest.json
"content_scripts": [
{
"js": ["debug.js", "foobar.js"],
}
],
and then call it like:
debug({"level": "timeouts", "msg": "foobar.js waitOnElement() timeout"});
which generates:
foobar.js waitOnElement() timeout debug.js:17
And there is my problem. At the moment, it is using the console log and so all the debug statements come from the same debug.js line. I'd rather return the calling context. I imagine I need something like:
try {
throw new Error(options["msg"])
} catch (e) {
e.stack.shift;
throw (e);
}
but I don't know how to do it exactly. apparently I can't just e.stack.shift like that. Also that always makes it an Uncaught Error — but these should just be debug messages.
You can't avoid mentioning the line in your debug.js, because either using throw (...) or console.log/error(...) your debug.js will be issuing the command.
What you can do, is have some try-catch blocks in your code, then in the catch block pass the error object to your debug function, which will handle it according to its debugState.
In any case, it is not quite clear how you are using your debug library (and why you need to remove the last call from the stack-trace, but you could try something like this:
Split the stack-trace (which is actually a multiline string) into lines.
Isolate the first line (corresponding to the last call) that is not part of the error's message.
Put together a new stack-trace, with the removed line.
E.g.:
function removeLastFromStack(stack, errMsg) {
var firstLines = 'Error: ' + errMsg + '\n';
var restOfStack = stack
.substring(firstLines.length) // <-- skip the error's message
.split('\n') // <-- split into lines
.slice(1) // <-- "slice out" the first line
.join('\n'); // <-- put the rest back together
return firstLines + restOfStack;
}
function myDebug(err) {
/* Based on my `debugState` I should decide what to do with this error.
* E.g. I could ignore it, or print the message only,
* or print the full stack-trace, or alert the user, or whatever */
var oldStack = err.stack;
var newStack = removeLastFromStack(oldStack, err.message);
console.log(newStack);
//or: console.error(newStack);
}
/* Somewhere in your code */
function someFuncThatMayThrowAnErr(errMsg) {
throw new Error(errMsg);
}
try {
someFuncThatMayThrowAnErr('test');
} catch (err) {
myDebug(err);
}
...but I still don't see how removing the last call from the trace would be helpful

Categories