This is my Franklin.jl-powered website. I have several mathematical simulation pages and while they do differ in some ways, a lot of their code is more or less shared and I am trying to reduce the redundancy between them. This is the original HTML code of my Chen attractor article:
<!DOCTYPE html>
<html>
<head>
<title>Chen equations solver</title>
<!--We need a few external JS libraries to provide LaTeX and plot rendering. -->
{{ insert head_solver.html }}
<script src="/libs/common/generateTableXYZ.js"></script>
</head>
<body>
<h1>Chen equations solver</h1>
<div>
This webpage uses the <a href='/RKF45/' link='_blank'>Runge-Kutta-Fehlberg fourth-order method with fifth-order error checking (RKF45)</a> to approximate the solution to <a href='https://en.wikipedia.org/wiki/Multiscroll_attractor' link='_blank'>Chen equations</a>:
\[
\begin{aligned}
\dfrac{dx}{dt} &= a (y-x) \\
\dfrac{dy}{dt} &= x(c-a-z) + cy \\
\dfrac{dz}{dt} &= xy - b z.
\end{aligned}
\]
</div>
<!--A form for users to enter in all the parameters of the problem-->
<form name="requiredData">
<table>
<tr>
<th>Parameter</th>
<th>Value</th>
<th>Explanation</th>
</tr>
<tr>
<td><label for="a">\(a\):</label></td>
<td><input type="Number"
id="a"
name="a"
value="40"></td>
<td>Problem parameter.</td>
</tr>
<tr>
<td><label for="b">\(b\):</label></td>
<td><input type="Number"
id="b"
name="b"
value="3"></td>
<td>Problem parameter.</td>
</tr>
<tr>
<td><label for="c">\(c\):</label></td>
<td><input type="Number"
id="c"
name="c"
value="28"></td>
<td>Problem parameter.</td>
</tr>
<tr>
<td><label for="t0">\(t_0\):</label></td>
<td><input type="Number"
id="t0"
name="t0"
value="0"></td>
<td>Starting time for the simulation in seconds (s).</td>
</tr>
<tr>
<td><label for="tf">\(t_f\):</label></td>
<td><input type="Number"
id="tf"
name="tf"
value="120"></td>
<td>End time for the simulation in seconds.</td>
</tr>
<tr>
<td><label for="x0">\(x_0\):</label></td>
<td><input type="Number"
id="x0"
name="x0"
value="-0.1"></td>
<td>Value of \(x\) at \(t_0\).</td>
</tr>
<tr>
<td><label for="y0">\(y_0\):</label></td>
<td><input type="Number"
id="y0"
name="y0"
value="0.5"></td>
<td>Value of \(y\) at \(t_0\).</td>
</tr>
<tr>
<td><label for="z0">\(z_0\):</label></td>
<td><input type="Number"
id="z0"
name="z0"
value="-0.6"></td>
<td>Value of \(z\) at \(t_0\).</td>
</tr>
{{ insert attractor_data.html }}
</table>
</form>
<!--Buttons-->
{{ insert attractor_button_table.html }}
<!--Where the table and plot goes-->
{{ insert attractor_output.html }}
{{ insert page_foot_general.html }}
</body>
</html>
I’ve been asking ChatGPT for tips on how to achieve my goal of modularizing this code. I know ChatGPT is far from perfect on this, but I don’t want to pester this discourse too much with my questions and for many questions, I do find ChatGPT sufficient with a bit of trial and error.
I asked, for instance, how do I include HTML code with some parameters. ChatGPT told me that I should use {{ insert:file.html param1=... param2=... }}
. I tried this and it failed. The only option was to define parameters in a Markdown file in my Franklin site. So I copy-pasted this code into a Markdown file between ~~~
blocks. This sort of worked, it produced a functioning webpage, but it also introduced some styling bugs (e.g. my table for parameter inputs was originally right aligned and with CSS styling I’ve only managed to get it centre aligned (when I want left alignment)). But one commonality between these webpages is that they all include parameters, but the precise parameters do differ a little between the simulation pages. So I was hoping I could define a vector of strings called “params” in my Markdown file and have:
<tr>
<td><label for="{{ params.el}}">\({{ params.el}}\):</label></td>
<td><input type="Number"
id="{{ params.el}}"
name="{{ params.el}}"
value="{{ el }}"></td>
<td>{{ el_explanation }}</td>
</tr>
created in my HTML file based on element of params (which I’ve called params.el
). The {{ el }}
would be from where I add @def el = "VALUE"
in my Markdown file. For example, say I had @def params = ["a", "b", "c"]
then I would like this added to my Markdown file during rendering:
<tr>
<td><label for="a">\(a\):</label></td>
<td><input type="Number"
id="a"
name="a"
value="{{ a }}"></td>
<td>{{ a_explanation }}</td>
</tr>
<tr>
<td><label for="b">\(b\):</label></td>
<td><input type="Number"
id="b"
name="b"
value="{{ b }}"></td>
<td>{{ b_explanation }}</td>
</tr>
<tr>
<td><label for="c">\(c\):</label></td>
<td><input type="Number"
id="c"
name="c"
value="{{ c }}"></td>
<td>{{ c_explanation }}</td>
</tr>
. The values of a
, b
, c
, a_explanation
, b_explanation
and c_explanation
would be defined in my Markdown file, of course. I tried achieving this by adding this to utils.jl
function render_form(params::Vector{String})
inputs = join(["<tr><td><label for=\"$p\">\\($p\\):</label></td>\n<td><input name=\"$p\" id=\"$p\" name=\"$p\" value=\"{{$p}}\"/></td>\n<td>{{ $p_explanation }} </td>\n</tr>" for p in params], "\n")
return "<form>\n$inputs\n</form>"
end
which was based on what ChatGPT suggested. And, of course, I tried to replace the a, b and c parameter input lines of my HTML file with {{ render_form(params) }}
with those variable definitions previously mentioned. But it caused serve() of Franklin.jl to crash with this error
⋮ shutting down LiveServer… ✓
ERROR: Franklin.PageVariableError("An error (of type 'UndefVarError') occurred when trying to evaluate 'utils.jl' in a page variable assignment.")
Stacktrace:
[1] serve(fw::LiveServer.SimpleWatcher; host::String, port::Int64, dir::String, debug::Bool, verbose::Bool, coreloopfun::Franklin.var"#261#264"{…}, preprocess_request::typeof(identity), inject_browser_reload_script::Bool, launch_browser::Bool, allow_cors::Bool)
@ LiveServer ~/.julia/packages/LiveServer/7eI98/src/server.jl:681
[2] serve (repeats 2 times)
@ ~/.julia/packages/LiveServer/7eI98/src/server.jl:604 [inlined]
[3]
@ Franklin ~/.julia/packages/Franklin/X68Y3/src/manager/franklin.jl:140
[4] serve()
@ Franklin ~/.julia/packages/Franklin/X68Y3/src/manager/franklin.jl:49
[5] top-level scope
@ REPL[1]:1
caused by: Franklin.PageVariableError("An error (of type 'UndefVarError') occurred when trying to evaluate 'utils.jl' in a page variable assignment.")
Stacktrace:
[1] set_vars!(vars::OrderedCollections.LittleDict{…}, assignments::Vector{…}; isglobal::Bool)
@ Franklin ~/.julia/packages/Franklin/X68Y3/src/utils/vars.jl:480
[2] set_vars!
@ ~/.julia/packages/Franklin/X68Y3/src/utils/vars.jl:462 [inlined]
[3] process_mddefs(blocks::Vector{Franklin.OCBlock}, isconfig::Bool, pagevar::Bool, isinternal::Bool)
@ Franklin ~/.julia/packages/Franklin/X68Y3/src/converter/markdown/mddefs.jl:66
[4] convert_md(mds::String, pre_lxdefs::Vector{…}; isrecursive::Bool, isinternal::Bool, isconfig::Bool, has_mddefs::Bool, pagevar::Bool, nostripp::Bool)
@ Franklin ~/.julia/packages/Franklin/X68Y3/src/converter/markdown/md.jl:129
[5] convert_md (repeats 2 times)
@ ~/.julia/packages/Franklin/X68Y3/src/converter/markdown/md.jl:23 [inlined]
[6] process_config()
@ Franklin ~/.julia/packages/Franklin/X68Y3/src/manager/file_utils.jl:15
[7] fd_fullpass(watched_files::@NamedTuple{…}, join_to_prepath::String)
@ Franklin ~/.julia/packages/Franklin/X68Y3/src/manager/franklin.jl:227
[8] fd_fullpass
@ ~/.julia/packages/Franklin/X68Y3/src/manager/franklin.jl:210 [inlined]
[9] fd_loop(cycle_counter::Int64, ::LiveServer.SimpleWatcher, watched_files::@NamedTuple{…})
@ Franklin ~/.julia/packages/Franklin/X68Y3/src/manager/franklin.jl:362
[10] #261
@ ~/.julia/packages/Franklin/X68Y3/src/manager/franklin.jl:136 [inlined]
[11] serve(fw::LiveServer.SimpleWatcher; host::String, port::Int64, dir::String, debug::Bool, verbose::Bool, coreloopfun::Franklin.var"#261#264"{…}, preprocess_request::typeof(identity), inject_browser_reload_script::Bool, launch_browser::Bool, allow_cors::Bool)
@ LiveServer ~/.julia/packages/LiveServer/7eI98/src/server.jl:671
[12] serve (repeats 2 times)
@ ~/.julia/packages/LiveServer/7eI98/src/server.jl:604 [inlined]
[13]
@ Franklin ~/.julia/packages/Franklin/X68Y3/src/manager/franklin.jl:140
[14] serve()
@ Franklin ~/.julia/packages/Franklin/X68Y3/src/manager/franklin.jl:49
[15] top-level scope
@ REPL[1]:1
caused by: UndefVarError: `utils` not defined in `Franklin`
Suggestion: check for spelling errors or missing imports.
Stacktrace:
[1] top-level scope
@ :0
[2] eval
@ ./boot.jl:430 [inlined]
[3] eval
@ ~/.julia/packages/Franklin/X68Y3/src/Franklin.jl:1 [inlined]
[4] set_vars!(vars::OrderedCollections.LittleDict{…}, assignments::Vector{…}; isglobal::Bool)
@ Franklin ~/.julia/packages/Franklin/X68Y3/src/utils/vars.jl:478
[5] set_vars!
@ ~/.julia/packages/Franklin/X68Y3/src/utils/vars.jl:462 [inlined]
[6] process_mddefs(blocks::Vector{Franklin.OCBlock}, isconfig::Bool, pagevar::Bool, isinternal::Bool)
@ Franklin ~/.julia/packages/Franklin/X68Y3/src/converter/markdown/mddefs.jl:66
[7] convert_md(mds::String, pre_lxdefs::Vector{…}; isrecursive::Bool, isinternal::Bool, isconfig::Bool, has_mddefs::Bool, pagevar::Bool, nostripp::Bool)
@ Franklin ~/.julia/packages/Franklin/X68Y3/src/converter/markdown/md.jl:129
[8] convert_md (repeats 2 times)
@ ~/.julia/packages/Franklin/X68Y3/src/converter/markdown/md.jl:23 [inlined]
[9] process_config()
@ Franklin ~/.julia/packages/Franklin/X68Y3/src/manager/file_utils.jl:15
[10] fd_fullpass(watched_files::@NamedTuple{…}, join_to_prepath::String)
@ Franklin ~/.julia/packages/Franklin/X68Y3/src/manager/franklin.jl:227
[11] fd_fullpass
@ ~/.julia/packages/Franklin/X68Y3/src/manager/franklin.jl:210 [inlined]
[12] fd_loop(cycle_counter::Int64, ::LiveServer.SimpleWatcher, watched_files::@NamedTuple{…})
@ Franklin ~/.julia/packages/Franklin/X68Y3/src/manager/franklin.jl:362
[13] #261
@ ~/.julia/packages/Franklin/X68Y3/src/manager/franklin.jl:136 [inlined]
[14] serve(fw::LiveServer.SimpleWatcher; host::String, port::Int64, dir::String, debug::Bool, verbose::Bool, coreloopfun::Franklin.var"#261#264"{…}, preprocess_request::typeof(identity), inject_browser_reload_script::Bool, launch_browser::Bool, allow_cors::Bool)
@ LiveServer ~/.julia/packages/LiveServer/7eI98/src/server.jl:671
[15] serve (repeats 2 times)
@ ~/.julia/packages/LiveServer/7eI98/src/server.jl:604 [inlined]
[16]
@ Franklin ~/.julia/packages/Franklin/X68Y3/src/manager/franklin.jl:140
[17] serve()
@ Franklin ~/.julia/packages/Franklin/X68Y3/src/manager/franklin.jl:49
[18] top-level scope
@ REPL[1]:1
Some type information was truncated. Use `show(err)` to see complete types.
And its suggestions for fixing this haven’t really been helpful. So I am hoping someone here may have a solution for this issue. If it helps my config.md does specify @def utils = utils.jl
which was also suggested by ChatGPT, as before that my changes to utils.jl had no effect.
Any ideas how to fix this?