First Last Prev Next    No search results available
Details
: Turing complete templates [patch] - Script Blocks in Temp...
Bug#: 25676
: smarttemplate4
: General
Status: RESOLVED
Resolution: FIXED
: PC
: Linux
: unspecified
: P3
: enhancement
: ---

:
:
:
:
:
  Show dependency tree - Show dependency graph
People
Reporter: Benito van der Zander <benito@benibela.de>
Assigned To: Axel Grude <axel.grude@gmail.com>

Attachments
patch (13.80 KB, patch)
2014-01-04 16:38, Benito van der Zander
no flags Details | Diff


Note

You need to log in before you can comment on or make changes to this bug.

Related actions


Description:   Opened: 2014-01-04 15:10
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)]  %%,
------- Comment #1 From Axel Grude 2014-01-04 16:06:35 -------
(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.
------- Comment #2 From Axel Grude 2014-01-04 16:33:30 -------
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...
------- Comment #3 From Benito van der Zander 2014-01-04 16:38:02 -------
Created an attachment (id=7759) [details]
patch
------- Comment #4 From Benito van der Zander 2014-01-04 16:40:00 -------
>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
------- Comment #5 From Axel Grude 2014-01-04 18:05:39 -------
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)
------- Comment #6 From Benito van der Zander 2014-01-05 07:50:19 -------
>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
------- Comment #7 From Axel Grude 2014-01-06 04:55:14 -------
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.
------- Comment #8 From Benito van der Zander 2014-01-06 11:25:53 -------
>%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...
------- Comment #9 From Axel Grude 2014-01-06 11:51:50 -------
(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.
------- Comment #10 From Axel Grude 2014-01-06 12:03:12 -------
(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...
------- Comment #11 From Benito van der Zander 2014-01-06 12:36:58 -------

>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 %...%  
------- Comment #12 From Axel Grude 2014-01-06 12:59:37 -------
(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).
------- Comment #13 From Benito van der Zander 2014-01-06 13:23:39 -------
> 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" : "" %%
------- Comment #14 From Benito van der Zander 2014-01-06 13:25:07 -------
> 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)
------- Comment #15 From Axel Grude 2014-01-06 15:13:16 -------
(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?
------- Comment #16 From Benito van der Zander 2014-01-06 15:27:33 -------
>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)
------- Comment #17 From Axel Grude 2014-01-09 04:00:39 -------
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.
------- Comment #18 From Benito van der Zander 2014-01-09 08:22:47 -------
>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
------- Comment #19 From Axel Grude 2014-01-09 09:43:42 -------
(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.

:-)
------- Comment #20 From Benito van der Zander 2014-01-09 12:46:45 -------
Well, you should try to get a *smart* editor
------- Comment #21 From Axel Grude 2014-03-26 10:59:29 -------
(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?
------- Comment #22 From Benito van der Zander 2014-03-29 00:02:38 -------


"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
------- Comment #23 From Axel Grude 2014-04-06 16:49:22 -------
(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?
------- Comment #24 From Axel Grude 2014-04-06 16:54:34 -------
(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.
------- Comment #25 From Axel Grude 2014-04-06 17:05:06 -------
(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)
------- Comment #26 From Benito van der Zander 2014-04-06 18:46:44 -------
>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)


?
------- Comment #27 From Axel Grude 2014-07-10 11:49:55 -------
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?
------- Comment #28 From Benito van der Zander 2014-07-13 18:34:49 -------
Something like this could work:

    %{% /Shoulder Tatoo ([0-9]+)/.exec(messageRaw(1000))[1] %}%
------- Comment #29 From Axel Grude 2015-07-25 05:52:47 -------
(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>
------- Comment #30 From Axel Grude 2015-07-25 06:43:17 -------
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 > (&gt;) 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.
------- Comment #31 From Benito van der Zander 2015-07-25 08:32:34 -------
>. 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 > (&gt;) 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 ... */ } 
    ...
------- Comment #32 From Axel Grude 2015-07-25 09:33:48 -------
(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.
------- Comment #33 From Benito van der Zander 2015-07-25 13:01:08 -------
>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
------- Comment #34 From Axel Grude 2015-07-25 13:21:28 -------
(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. 
------- Comment #35 From Benito van der Zander 2015-07-26 14:07:23 -------
>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. 
------- Comment #36 From Axel Grude 2019-10-22 07:36:12 -------
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.

First Last Prev Next    No search results available