Bugzilla@Mozdev – Bug 25676
Turing complete templates [patch] - Script Blocks in Template
Last modified: 2019-10-22 07:36:12
You need to log in before you can comment on or make changes to this bug.
The addon is called SmartTemplate, but does it have smart templates? Let us think about it. What is smartness? Reacting on unexpected situations, thinking in original ways, creating new stuff never seen before... Can a template react on anything and create something original? No! It basically just copies something from somewhere. Even always to the same places in the mail. So despite its name, it is actually not smart at all! It is a stupid template -- This patch changes that and maximizes the smartness by evaluating arbitrary javascript expressions between %%..%%-markers in the templates. Thereby the usual variables can be accessed. E.g.: Hi %% from.contains("local.company.domain") ? from("firstname") : from("name") %%, %% ["Bye", "Best"][Math.floor(Math.random()*2)] %%,
(In reply to comment #0) > > This patch changes that and maximizes the smartness by evaluating arbitrary > javascript expressions between %%..%%-markers in the templates. Thereby the > usual variables can be accessed. > > E.g.: > > Hi %% from.contains("local.company.domain") ? from("firstname") : from("name") > %%, > So where is the patch? Are you saying you want to contribute some code? Also, keep in mind that most users won't be familar with C trinary operators such as A ? B : C; > %% ["Bye", "Best"][Math.floor(Math.random()*2)] %%, well, same to you, but if you suggest something we need better defined requirements.
By the way I am open to contributors of code. But let's first define what makes most sense to the users of this Addon. One thing we do not necessarily want it for it to be abused by people who want to turn Thunderbird into a mass-mailing solution. Also the fields are mainly taken from the mail header and there is very little "smartness" involved in that, the main engine is one terrible mess of regexp replacements (you can tell that I am not the original author, I only inherited this Addon). So there is no proper parsing going on, no trees are built and the whole thing is a terrible unstructured mess. I only took over the Addon as a favor, but I honestly do not have enough spare time to completely refactor it. However I made the fields integrate with Stationery and there is even parsing / replacement possible in Signatures, so I tried to make it more flexible, without doing a whole rewrite. There was also a lot of hard coded <br> counting badness in it which I removed and made more resilient(*), but I am still quite amazed that it actually works at all. As a User I am not overly impressed with it and I see it as a good addition to Stationery; it would be "smarter" if it could insert names etc. after adding them to the Address-bar in Composer, so writing a patch to support this would probably be a better use of your energy: feel free to raise a bug on that, but be a little more concrete that time. (*) usually users thank for this with complaints about changed functionality, as they do not know what unwholesome mess they are working with...
Created an attachment (id=7759) [details] patch
>So where is the patch? This bug tracker is also not smart, and discards attachments without description :( >Also, keep in mind that most users won't be familar with C trinary operators such as But it is the javascript syntax. There is no choice
I merged in the patch and checked it in at https://code.google.com/p/smart-template-4/source/list I will test that functionality tomorrow. Hopefully this is not going to create some problems during the review as even evalInSandbox can be problematic during security vetting, but if it works as advertised I am willing to take a chance on this. PS: if you want to do some good, find a way of eliminating that terrible code in smartTemplate-overlay.js:1187 ff. it is an absolute eyesore and bet it can be done much more elegantly without adding all that terrible temporary markup to each line... (It is from the original author)
>PS: if you want to do some good, find a way of eliminating that terrible code in >smartTemplate-overlay.js:1187 ff. Three possible ways: 1) Use indexOf with startindex to find the next line break / [[-marker and then check the replacement offset (fourth parameter of replaceReservedWords) to check if the offset is before or after the line break. And update the indices when the inserted text changes the length. 2) Use split with capturing parenthesis to split it into \n,[[,$ tokens and then process every token, and join the result back together 3) Do not use regex replace, but your own parser / regex search to find the next \n,[[,%,... and then concatenate the pieces together
There are a couple of improvements that are necessary with the demarkation of Javascript blocks. %% is not a good idea for 2 reasons 1. could be added accidentally, too unspecific 2. we shouldn't have the same token for start & end of a JS bracket 3. it is also not specific as to "when" / "how often" the JS should be executed. 4. It probably also keep track of whether it has already been executed At the moment we usually execute the %variable% replacements on the composer specific NotifyComposeBodyReady event of the gMsgCompose State Listener. It is conceivable that a future version would have script that can execute on other events, such as NotifyComposeFieldsReady, ComposeProcessDone, SaveInFolderDone etc. (I am hoping for a dedicated "Send" event as this would be most useful for a final update of Address-specific headers) We should probably address 1 & 2 before thinking of release I would propose something like this: %script% %scriptEnd% (At a later stage we could add optional parameters that specify an event type, such as) %script(ComposeBodyReady)% %script(ComposeProcessDone)% %script(BeforeSend)% etc.
>%script% >%scriptEnd% That's cumbersome. I thought of the javascript insertions as nothing special, just as a more flexible template variable. Evaluated, when all the other variables are inserted Perhaps go the other way, and just use single % .. % as markers? Then insert the replaced variables, if the text between the markers is a valid variable, and evaluate it as javascript otherwise...
(In reply to comment #8) > >%script% > >%scriptEnd% > > That's cumbersome. > > I thought of the javascript insertions as nothing special, just as a more > flexible template variable. Evaluated, when all the other variables are > inserted > > Perhaps go the other way, and just use single % .. % as markers? no that's even worse as it can very well lead to problems as % is also a valid character within a HTML document. I am afraid I have to insist on at least 2 things: a)start and finish delimiter for Javascript block must be distinct b)a single character (or anything that isn't unique enough so that it requires "recognization from context") is not acceptable. it is simply too fragile. with my suggestion I kept in sync with the existing naming conventions %variable%. Of course, if you intend to write a lot of JS code in your templates are worried about typing too much (or creating somewhat cramped / noisy markup), I would settle for this pair: %{% ...insert JS code here... %}% This is just about as terse as I am willing to make it - would this be acceptable? I also have a follow question on usage of smartTemplate variables _from within_ these JS code blocks. Within this context are you removing the need of wrapping them with % so that %varName% can (must) be written varName? I am not saying I am against that (although there is the danger of being overwritten by JS variables) just curious, and I haven't taken the time to read your code in great detail yet.
(In reply to comment #9) > I also have a follow question on usage of smartTemplate variables _from within_ > these JS code blocks. Within this context are you removing the need of wrapping > them with % so that %varName% can (must) be written varName? I am not saying I > am against that (although there is the danger of being overwritten by JS > variables) just curious, and I haven't taken the time to read your code in > great detail yet. > Just reading the patch from line 211 ff. I see you actually add functions specifically. I think we need some documentation on what you adding. Would it be fair to say +227 is a list of JavaScript functions that you import? I vote we rename the new function "scriptlet block" to highlight that only a limited / selected number of functions are supported. Did you test whether replace supports (expects) regexp functions. I dread the moment my users expect me to support advanced JavaScript from within smartTemplate4 - I think we need a few good links for "how-to" pages. By the way seeing the toLocaleUpperCase() function reminds me that %name% needs a "capitalize" mode... I was thinking of adding that as a global option but it might become a scriptlet function as well...
>Of course, if you intend to write a lot of JS code in your templates are worried about typing too much (or creating somewhat cramped / noisy markup), I am going to use only js code, so it can smartly choose a random text every time and people do not notice that it is a template... >I would settle for this pair: >%{% ...insert JS code here... %}% That would be okay There is also the problem that it only evaluates a single expression, so anything complicated would have to be written as %{% (function(){ ... return ...})() %}% >Within this context are you removing the need of wrapping them with % so that %varName% can (must) be written varName? Yes >Would it be fair to say +227 is a list of JavaScript functions that you import? No These functions are only used to let varName behave like a string (so e.g. varName.substr exists), although varName is a function. (btw, I could not figure out how to give varName a length property) It is a clever hack, so if you have a variable with parameter like %from(firstname)%, you can write from in the javascript, and get the same as %from%, and you can write from("firstname") to get the same as %from(firstname)%. (it is even possible to add a variable firstname = "firstname", then you can just write from(firstname) ) In the javascript, all variables of the rw2h map can be used directly, and in addition there is a function variable("..."), which returns the same as %...%
(In reply to comment #11) > I am going to use only js code, so it can smartly choose a random text every > time and people do not notice that it is a template... Hope you don't use it to create a SpamBot :) If I read this correctly do you write the complete email as a series of expressions? Surely you only need the flexibility of scriptlets when you are using fields? Do you also intend to include HTML markup within these blocks? A "rainbow colored text" script would be great... > >Would it be fair to say +227 is a list of JavaScript functions that you import? > > No > > These functions are only used to let varName behave like a string (so e.g. > varName.substr exists), although varName is a function. (btw, I could not > figure out how to give varName a length property) > > It is a clever hack, so if you have a variable with parameter like > %from(firstname)%, you can write from in the javascript, and get the same as > %from%, and you can write from("firstname") to get the same as > %from(firstname)%. (it is even possible to add a variable firstname = > "firstname", then you can just write from(firstname) ) ok, so let me rephrase that - you overload a selection of string methods onto the variables - the evaluation result is then passed back and replaced with the complete scriptlet containing the expression. can we also have a constant string as starting point, such as 'ABCDE'.toLowerCase(); or would we have to create a smartTemplate function %string('ABCD')% for this? > > In the javascript, all variables of the rw2h map can be used directly, and in > addition there is a function variable("..."), which returns the same as %...% > ok, which seems to be the reverse of what I asked above (string literal).
> the evaluation result is then passed back and replaced with the complete scriptlet containing the expression. What do you mean? The expression is evaluated to a normal string >If I read this correctly do you write the complete email as a series of expressions? Surely you only need the flexibility of scriptlets when you are using fields? I use it to choose a random greeting, closing; choosing different texts depending if I reply to a mail in German/English from a friend or a professional contact. Currently my final template looks like this: %% (function(){ var message = messageRaw(1000); function score(a){ var res = 0; for (var i=0;i<a.length;i++) res += message.count(" "+a[i]+" "); return res; } var langDE = score(["der", "die", "du", "Hallo", "hallo", "und"]); if (from.toString().contains(".de")) langDE += 1; var langEN = score(["the", "a", "you", "Hello", "Hello", "and"]); var lang = langDE > langEN ? "de" : "en"; formal = false; var greeting; var finalize = function(s){return s;} if (lang == "de") { formal = message.contains("Sehr geehrte") || message.contains("Mit freundlichen Gr") || (score(["Sie", "Ihr", "Ihnen", "Ihrem", "Ihrer"]) - message.count(".Sie") - message.count(". Sie") > score(["du", "Du", "dir", "dein", "Dir", "Dein", "deinem"])); greeting = ["Hallo"]; if (!formal) greeting = greeting.concat(["Hi", "Hallo", "Hallo"]); else greeting = greeting.concat(["Sehr geehrte"]); closing=["Viele Grüße", "Viele Grüße", "Viele Grüße"]; if (!formal) closing = closing.concat(["Gruß", "Schöne Grüße", "Grüße", "Liebe Grüße"]); else closing = closing.concat(["Mit freundlichen Grüßen"]); finalize = function (s) { if (formal || s.endsWith("e")) { var fullname = from("name"); var female, addgenderspec = formal; if (fullname.contains("Fr.")) { female = true; addgenderspec = false; } else if (fullname.contains("Hr.")) { female = false; addgenderspec = false; } else { if (fullname.contains("Dr.")) addgenderspec = false; var firstname = from("firstname"); female = (firstname.endsWith("a") || firstname.endsWith("ah") || (firstname.endsWith("an") && !firstname.endsWith("ian"))); } if (!female && s.endsWith("e")) s = s + "r"; if (addgenderspec) s = s + " " + (female ? "Frau" : "Herr"); } return s; } } else if (lang == "en") { greeting = ["Hi"]; closing = ["Bye", "Best", "Cheers"]; } return finalize(choose(greeting)) + " " + (formal ? from("name") : from("firstname")); })()%%, %cursor% %% choose(closing) %%, Benito %% formal ? "van der Zander" : "" %%
> Do you also intend to include HTML markup within these blocks? I do not do that, but it should be possible. (which is why the result of the script expression is inserted after everything has been escaped)
(In reply to comment #13) > > the evaluation result is then passed back and replaced with the > complete scriptlet containing the expression. > > What do you mean? > > The expression is evaluated to a normal string I meant the opposite of what I wrote :-) the evaluated string replaces the whole block %{% (function(){return 'schöner Tach heute'; }); %}% !! => schöner Tach heute !! Did I get it right this time? :) > > I use it to choose a random greeting, closing; choosing different texts > depending if I reply to a mail in German/English from a friend or a > professional contact. > > Currently my final template looks like this: > > %% (function(){ > var message = messageRaw(1000); > function score(a){ > var res = 0; > for (var i=0;i<a.length;i++) res += message.count(" "+a[i]+" "); > return res; > } > var langDE = score(["der", "die", "du", "Hallo", "hallo", "und"]); > if (from.toString().contains(".de")) langDE += 1; > var langEN = score(["the", "a", "you", "Hello", "Hello", "and"]); > var lang = langDE > langEN ? "de" : "en"; > > formal = false; > var greeting; > var finalize = function(s){return s;} > if (lang == "de") { > formal = message.contains("Sehr geehrte") || message.contains("Mit > freundlichen Gr") > || (score(["Sie", "Ihr", "Ihnen", "Ihrem", "Ihrer"]) - > message.count(".Sie") - message.count(". Sie") > score(["du", "Du", "dir", > "dein", "Dir", "Dein", "deinem"])); > > greeting = ["Hallo"]; > if (!formal) greeting = greeting.concat(["Hi", "Hallo", "Hallo"]); > else greeting = greeting.concat(["Sehr geehrte"]); > closing=["Viele Grüße", "Viele Grüße", "Viele Grüße"]; > if (!formal) closing = closing.concat(["Gruß", "Schöne Grüße", > "Grüße", "Liebe Grüße"]); > else closing = closing.concat(["Mit freundlichen Grüßen"]); > finalize = function (s) { > if (formal || s.endsWith("e")) { > var fullname = from("name"); > var female, addgenderspec = formal; > if (fullname.contains("Fr.")) { female = true; addgenderspec = false; } > else if (fullname.contains("Hr.")) { female = false; addgenderspec = > false; } > else { > if (fullname.contains("Dr.")) addgenderspec = false; > var firstname = from("firstname"); > female = (firstname.endsWith("a") || firstname.endsWith("ah") || > (firstname.endsWith("an") && !firstname.endsWith("ian"))); > } > if (!female && s.endsWith("e")) s = s + "r"; > if (addgenderspec) s = s + " " + (female ? "Frau" : "Herr"); > } > return s; > } > } else if (lang == "en") { > greeting = ["Hi"]; > closing = ["Bye", "Best", "Cheers"]; > } > > return finalize(choose(greeting)) + " " + (formal ? from("name") : > from("firstname")); > })()%%, > > %cursor% > > %% choose(closing) %%, > Benito %% formal ? "van der Zander" : "" %% funky. unfortunately, I have more questions. can you please explain: what is "closing", "formal" etc. an adhoc declared variable? In what scope are these variables declared (I hope they stay sandboxed and do not pollute chrome context)? From your (I assume working) example I see that the three parts of the script seem to work within the same context, so that's a good thing. I assume one could even potentially add DOM manipulation and inject css given a powerful set of scripts, it is all a little scary as well. Can you send me an example email generated with this, I am interested in the end-result and whether the scripts are completely "consumed" (replaced with text that they generate after running once) or whether they stay within the email text (which was my original assumption). Would it behoove us to declare "use strict" within this scriptlet, or is that impossible?
>Did I get it right this time? :) yes >what is "closing", "formal" etc. an adhoc declared variable? >In what scope are these variables declared Yes. Not sure where they are, probably they become properties of the window object of the sandbox >Would it behoove us to declare "use strict" within this scriptlet, or is that impossible? It might already be in strict mode (got some errors related to that, when I wrote it lazily)
2 questions (I believe the patch is incomplete) 1. where do you integrate (call) replaceJavascript (I see it is recursive but you have to call it somewhere, possibly within replaceReservedWords)? 2. to implement %{% %}% syntax I modified line 1183 to msg = msg.replace(/%\{%((.|\n)*?)%\}%/gm, replaceJavascript); I guess you need a similar change to the regex at the (missing?) integration point.
>1. where do you integrate (call) replaceJavascript (I see it is recursive but you have to call it somewhere, possibly within replaceReservedWords)? It is called by the msg.replace in line 1183 Then it takes two steps %% 1+2+3 %% becomes %internal-javascript-ref(0)% which is replaced, when all the normal variables are replaced >2. to implement %{% %}% syntax I modified line 1183 to >msg = msg.replace(/%\{%((.|\n)*?)%\}%/gm, replaceJavascript); That should work
(In reply to comment #18) > >1. where do you integrate (call) replaceJavascript (I see it is recursive but > you have to call it somewhere, possibly within replaceReservedWords)? > > It is called by the msg.replace in line 1183 > Darn. My Syntax highlighting program (notepad++) got confused and missed one of the closing } in line 1155. So it looked like replaceReservedWords was called in recursion. Gonna pull this apart into 2 lines. :-)
Well, you should try to get a *smart* editor
(In reply to comment #20) > Well, you should try to get a *smart* editor > Benito, could you write a short "how to use this feature" section so I can include it in the version notes? What I have is too vague: "We are allowing certain (string) Javascript functions in concatenation to our %variable% as long as they are in a script block %{% %}% . Local variables can be defined within these blocks, only 1 expression line is allowed per block, hence best to wrap all code in (function() { ..code.. })()" Also at least one simple example with a local variable that can be used between 2 code blocks would be a great help. And do we need an inclusive list of allowed JavaScript functions?
"We are allowing certain (string) Javascript functions in concatenation to our %variable% as long as they are in a script block %{% %}% . Local variables can be defined within these blocks, only 1 expression line is allowed per block, hence best to wrap all code in (function() { ..code.. })()" We have added %{% expression %}% as syntax to include a single, arbitrary javascript expression in a template. All usual javascript expressions and functions can be used, and the usual SmartTemplate variables are available as functions and variables. The window object is shared across all %{%%}% blocks, so multiple blocks can use the same variables. To use multiple statements in an expression these statements can be wrapped in a single function call: %{% (function() { ..code.. })() %}% ? >Also at least one simple example with a local variable that can be used between 2 code blocks would be a great help. %{% (function() { switch (from.toString()) { case "rob@example.org": greeting="Hi Rob!"; closing="Bye"; break; case "bob@example.org": greeting="Hi Bob!"; closing="Bye"; break; default: greeting="Hi"; closing="Regards"; break; } })() %}% %{% greeting %}% %{% closing %}% > And do we need an inclusive list of allowed JavaScript functions? Not really, that depends too much on the functions thunderbird makes available
(In reply to comment #18) > >1. where do you integrate (call) replaceJavascript (I see it is recursive but > you have to call it somewhere, possibly within replaceReservedWords)? > > It is called by the msg.replace in line 1183 > > > Then it takes two steps > > %% 1+2+3 %% becomes %internal-javascript-ref(0)% which is replaced, when all > the normal variables are replaced > > > >2. to implement %{% %}% syntax I modified line 1183 to > >msg = msg.replace(/%\{%((.|\n)*?)%\}%/gm, replaceJavascript); > > That should work > Thanks for that. I am stil having trouble testing this, when I use your example I am getting: ==================== ERR: TypeError: (intermediate value)(...) is undefined Hi ... Regards ====================== ALso I am not sure whether I like the way the syntax does not require smartTemplate variables to be wrapped in %% (e.g. from is a reserved word so this might lead to problems). Is there an issue with strict mode?
(In reply to comment #23) > (In reply to comment #18) > > Thanks for that. I am stil having trouble testing this, when I use your example > I am getting: > ==================== > ERR: TypeError: (intermediate value)(...) is undefined Hi > > ... > > Regards > ====================== > > ALso I am not sure whether I like the way the syntax does not require > smartTemplate variables to be wrapped in %% (e.g. from is a reserved word so > this might lead to problems). Is there an issue with strict mode? > Ignore comment #23 for now... extra } found. I am working on an additional example at the moment.
(In reply to comment #24) > (In reply to comment #23) > > (In reply to comment #18) > > > > > Thanks for that. I am stil having trouble testing this, when I use your example > > I am getting: > > ==================== > > ERR: TypeError: (intermediate value)(...) is undefined Hi > > > > ... > > > > Regards > > ====================== > > > > ALso I am not sure whether I like the way the syntax does not require > > smartTemplate variables to be wrapped in %% (e.g. from is a reserved word so > > this might lead to problems). Is there an issue with strict mode? > > > Ignore comment #23 for now... extra } found. I am working on an additional > example at the moment. > Tried to use the script block for recording the time and injecting a onClose event but the problem is of course all code runs in sandbox and cannot access any objects from outside its own scope (e.g. window). The only way I could do this if I exposed a %fileStream% variable. (which I won't)
>ALso I am not sure whether I like the way the syntax does not require smartTemplate variables to be wrapped in %% (e.g. from is a reserved word so this might lead to problems). Is there an issue with strict mode? Is it? It is not listed there: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Reserved_Words There is also the variable("from") function >Tried to use the script block for recording the time and injecting a onClose event but the problem is of course all code runs in sandbox and cannot access any objects from outside its own scope (e.g. window). The only way I could do this if I exposed a %fileStream% variable. (which I won't) ?
I had a user request of somebody who wants to use a template mail in order to automate sending replies based on a paypal notification. The pattern he has in his email is "Shoulder Tatoo NNN", where NNN is a 3 digit number. He basically is using a template for each Tatoo to reply individually, the only difference in the template appears to that number (NNN). Is it possible to use your patch to craft a regex to determine this number so I can re-inject it into the template - this way the user can use one template only? If so, can you give me an idea how to code this?
Something like this could work: %{% /Shoulder Tatoo ([0-9]+)/.exec(messageRaw(1000))[1] %}%
(In reply to comment #22) > > > >Also at least one simple example with a local variable that can be used between > 2 code blocks would be a great help. > > %{% (function() { > switch (from.toString()) { > case "rob@example.org": greeting="Hi Rob!"; closing="Bye"; break; > case "bob@example.org": greeting="Hi Bob!"; closing="Bye"; break; > default: greeting="Hi"; closing="Regards"; break; > } > })() %}% > %{% greeting %}% > > %{% closing %}% I have tested this but it has a few drawbacks. from is replaced by the script with %from()% which is then replaced with the name not the email address. If we could include the from(mail) from(mail,name) etc. syntax and replace it this would be better. as a workaround, for the moment we can use frommail. (It returns the mail address within < >). Also the function must return a string otherwise an error is inserted. So the revised version for the moment would be: %{% (function() { switch (frommail.toString()) { case "<rob@example.org>": greeting="Hallo Rob"; closing="later!"; break; case "<bob@example.org>": greeting="Hi Bob"; closing="Ciao"; break; default: greeting="Hi " + from; closing="Regards"; break; } return ""; })() %}% %{% greeting %}%<br> ...<br> %{% closing %}%<br>
Improved version of a language determining script %{% (function() { test = frommail.toString(); dotPos = test.lastIndexOf('.'); ending = test.substring(dotPos).slice(0,3); switch (ending) { case ".de": greeting="Hallo " + fromname; closing="Auf Wiedersehen"; break; case ".co": case ".uk": greeting="Dear " + fromname; closing="kind regards"; break; default: greeting="Hi " + frommail; closing="Regards"; break; } return ""; })() %}% One of the problems with the ending is that it includes the > (>) entity and for some reasons ".de>" turns out to be 11 characters long, not quite sure why as I didn't debug it. So I chose to be lazy and truncate after 3 characters instead (which is not nice for .com addresses). But it kind of works so you can build your own more sophisticated versions from there.
>. If we could include the from(mail) from(mail,name) etc. syntax and replace it this would be better. as a workaround, for the moment we can use frommail. (It returns the mail address within < >). The syntax is supposed to work as from("mail") in the patch >One of the problems with the ending is that it includes the > (>) entity and for some reasons ".de>" It is probably better to use regular expressions, like: ... test = frommail.toString(); if (/[.]de>?$/.test(test)) { /* de ... */ } else if (/([.]uk|[.]com)>?$/.test(test)) { /* en ... */ } ...
(In reply to comment #31) > >. If we could include the from(mail) from(mail,name) etc. syntax and replace it this would be better. as a workaround, for the moment we can use frommail. (It > returns the mail address within < >). > > The syntax is supposed to work as from("mail") in the patch > > I tried these variants test = from("mail").toString(); test = from(mail).toString(); test = from(mail); But they always throws: ERR: ReferenceError: mail is not defined if I try to force the variable using this syntax test = %from(mail)%; I get this: ERR: SyntaxError: expected expression, got '%' I was wondering whether this is because of smartTempalte-overlay.js line 1643 if (typeof arg === "undefined") return "%"+aname + "()%"; //do not allow name() remote debugging it is a little hit & miss.
>test = from("mail").toString(); >test = from(mail).toString(); >test = from(mail); >But they always throws: >ERR: ReferenceError: mail is not defined You sure? For 2 and 3 that is supposed to happen, but I do not see how it can happen in 1. mail is not even a reference there >if (typeof arg === "undefined") return "%"+aname + "()%"; //do not allow name() That is there to prevent people from using from() with parenthesis and nothing in them
(In reply to comment #33) > >test = from("mail").toString(); > >test = from(mail).toString(); > >test = from(mail); > >But they always throws: > >ERR: ReferenceError: mail is not defined > > You sure? Yes, but I was able to make it work since with single quotes: test = from('mail') I understand that this will be executed within the Script Block like %from(mail)% I wonder two things a) are the double quotes removed by something in the sandbox? b) does the mechanism support multiple arguments? Such as: %from(firstname,lastname,bracketMail(;))% -> from('firstname','lastname','bracketMail[;]') (not tested) > >if (typeof arg === "undefined") return "%"+aname + "()%"; //do not allow name() > > That is there to prevent people from using from() with parenthesis and nothing > in them if there is no argument, should it not be simply "%" + aname + "%" ? We don't generally support empty parentheses () in our variables. I know the syntax is horrible, but it is how it is.
>I wonder two things >a) are the double quotes removed by something in the sandbox? No idea. It works on my computer with double and single quotes. >b) does the mechanism support multiple arguments? Such as: It would probably be from('firstname,lastname,bracketMail(;)') Afair, in the normal, non-javascript mode, something like %foo(bar,123)% is split to a pair of "foo" and "bar,123", and then a "foo" function is called with argument "(bar,123)". In the javascript, you call the "foo" function directly and can choose any argument (except for the surrounding parentheses that are always added). >if there is no argument, should it not be simply "%" + aname + "%" ? We don't generally support empty parentheses () in our variables. I know the syntax is horrible, but it is how it is. That part is not evaluated further. If you write mail(), it will insert %mail()% as string in the final mail. So %mail()% and %{%mail()%}% becomes the same nonsense.
It seems the Cu.nukeSandbox() function fails in Thunderbird 68 so I have a bugfix which is going to land in v2.4.2 - so by then, scripting in ST⁴ will be back. I also heard that "new Functino()" will eventually be deprecated, not sure from which version onwards this will be an issue.