Better templating support for JavaScript

"John P. Rouillard" <[email protected]> Wed, 15 Mar 2023 18:28:51 -0400
Newsgroups gmane.comp.bug-tracking.roundup.devel
Message-ID <[email protected]>
Hello all:

I have been working on trackers that need more javascript than a
classic tracker. Currently javascript is used via onclick or other onX
handlers on elements (buttons, inputs) in the generated HTML. If you
are using a content security policy, using onX inline javascript is a
bad idea.  Instead you want a javascript function to run that adds
event listeners to the elements. This will result in more required
javascript.

I would like to totally remove javascript from the Python code and
move it to external file(s). These files could be loaded via:

   <script src="@@file/...">

tag or be included inline like base_javascript().  Inserting the
javascript into the HTML will make the HTML work even if loading a
script fails (network issues ...).

When base_javascript() is called, it inserts values from Roundup
(self._client.client_nonce, self.base) into the returned
javascript/html. (Note that the client_nonce must never be exposed
anywhere except in the HTML processed by the browser.)  Some
javascript I have written needs replaced values as well.

The tokens (except for security nonces) could be specified using a
json formatted variable created by a templating extension. But some
way to embed an external javascript file into the returned HTML is
still required.

My thought is to provide a templating method:

  inline_javascript("filename", values={}, options={})
 
This would:

  1 read the contents of filename (relative to the tracker home
    unless it starts with /) into a string. (Optional: if started
    with @@file, it's relative to the HTML directory.)

  2 use string interpolation with only named values '%(name)s' on the
    string. (Optional: scan and  autofix '%' not followed by a '('
    if there is a TypeError when interpolating.)

  3 have a predefined set of values that can be updated by passing new
    values using the values={} parameter. At the very least:

      * client_nonce
      * base

    would be defined. If values=None is supplied, interpolation is
    not done.

  4 return the (optionally) interpolated string

This should allow the file to:

   * be externally stored,

   * substitute Roundup variable values,

   * be inlined in the returned HTML

The options dict is for future expansion. E.G. the file could be
minimized on the fly or something. (Note it would probably be better
to use a .js.min and inline that to remove the processing overhead.)

I chose string interpolation because '%(' is a unique marker. In
javascript '%' is used for remainder operations. It is not heavily
used IMO. If a parenthesized expression is needed, '% (' can be used
as a workaround (although minimizing the file might be an issue).

I decided not to use the string.format method because the specifiers
"{...}" can be used in javascript and I am concerned this will cause
syntax errors. (That will be difficult to auto-correct.)

I am not sure if I could write a custom Formatter class to replace the
'{' and '}' but that's more involved/fragile/requires testing. For
output that is to be consumed by humans (e.g. to provide a template
for email messages) I think format makes sense. But broken formatting
here breaks the application.

Similarly template strings require replacing $ with $$. Since a lot of
javascript uses $ as a variable/function name (e.g. jQuery) template
strings are a nonstarter as well.

Quips, comments, evasions, questions, or answers?

Have a great week.
--
				-- rouilj
John Rouillard
===========================================================================
My employers don't acknowledge my existence much less my opinions.