---
project
  tofgui
version
  1.0.0
unit
  struct
  system_target
info
  This project provides a function `tofgui`, that starts a Graphical User Interfact in the browser that supports the management and editing of [TofJs](htttp://tofjs.org) projects.

  # Usage

  Start a [NodeJs](http://nodejs.org) REPL, load the required projects (see below) and then call `tofgui(port)`, e.g.

      > tofgui(55555)
      Server started listening on port `55555` ...
      `tofgui` is running now. Point your Firefox browser to `localhost:55555/?home` to get started.
      undefined
      > ...

  By ending the REPL session (e.g. with `Ctrl+C`, `Ctrl+C`) you also terminate the GUI.

  Alternatively, you could also use the module

      > require ('./tofgui.tofm.js') . tofgui (55555);

  # Requirements

  There are a couple of other projects that are required to run `tofgui`, namely:
  `type`, `value`, `html`, `augment`, `global`, `system`, `utils`, `flatcode`, `session`, `unit`, `HttpTools`, `tofgui`.

---
---
unit
  value
name
  tofgui
type
  $act (HttpServer.$HttpPortNumber)
value
  function (port) {
    var httpServer = HttpServer.fileAndDocProducerServer (port) (TofGui.docProducer);
    httpServer.start();
    console.log ( '`tofgui` is running now. Point your Firefox browser to `localhost:%d/?home` to get started.', port );
  }
info
  `tofgui(port)` creates a HTTP server that immediately starts to listen to the `$HttpPortNumber` value `port`.

  For example,

  Start a [NodeJs](http://nodejs.org) REPL, load the required projects (see below) and then call `tofgui(port)`, e.g.

      > tofgui(55555)
      Server started listening on port `55555` ...
      `tofgui` is running now. Point your Firefox browser to `localhost:55555/?home` to get started.
      undefined
      > ...

  By ending the REPL session (e.g. with `Ctrl+C`, `Ctrl+C`) you also terminate the GUI.
comment
  Recall, that the two lines

          var httpServer = HttpServer.fileAndDocProducerServer (port) (TofGui.docProducer);
          httpServer.start();

  first create an instance of the `HttpServer` class (see the `HttpTools` project) and then starts it, so that the browser can point to `http://localhost:port` to use this GUI.

  Recall (see the info in the `HttpTools` project), that the `httpServer` created with `HttpServer.fileAndDocProducerServer(port)(dp)` works as a *file server* as well as a *document producer server*, depending on the URL requested by the browser:

  * If the URL query part (in fact, the combined query and data part from a GET as well as a POST request) of the URL is empty, the file from the URL path is returned.
      For example,

          http://localhost:port/one/two/three.html

      displays `/one/two/three.html` in the browser window.

  * If the URL query part is not empty, say if it is `?key1=value1&key2=value2&key3=value3`, the query part is converted into a record `{key1: value1, key2: value2, key3: value3}` and the HTML document produced by `dp({key1: value1, key2: value2, key3: value3})` is shown in the browser window.

  The second parameter `dp` is a **document producer**, i.e. a function that takes a record `r` and returns an HTML document `dp(r)`.
  In our case of `tofgui`, the document producer is the [`TofGui.docProducer`](#TofGui-docProducer).
---
unit
  struct
name
  TofGui
---
unit
  value
name
  TofGui.docProducer
type
  $prod ([$Rec, $HtmlDoc])
value
  function (rec) {
    if ('home' in rec) {
     return homePage();
    } else {
      if ('do' in rec) {
        switch (rec.do) {
          case 'list':
            return listProjects ();
          case 'edit':
            if ('project' in rec) { return editProject (rec.project); }
            else { return errorPage ("Cannot edit: no `project` was submitted!"); }
          case 'compile':
            if ('project' in rec) {
              if ('flatcode' in rec) {
                return compileProject (rec.project) (rec.flatcode);
              } else {
                return errorPage ("Cannot compile `" + rec.project + "`, because no `flatcode` was submitted.");
              }
            } else {
              return errorPage ("Cannot compile: no `project` was submitted!");
            }
          case 'open':
            return openProject (rec);
          case 'delete':
            if ('project' in rec) {
              if ('confirmed' in rec) { return deleteProject ([rec.project, rec.confirmed]); }
              else                    { return deleteProject (rec.project); }
            } else {
              return errorPage ("<code>do=delete</code> is called, but there is no <code>project</code> specified.");
            }
          case 'create':
            return createProject (rec);
          case 'move':
            return moveProject (rec);
          default:
            return errorPage ("Unknown `do` value `" + rec.do + "`!");
        }
      } else {
        return errorPage ("Either a `home` or `do` value must be specified!");
      }
    }
  }
info
  `TofGui.docProducer(rec)` returns a HTML document, depending on the parameters provided in the record `rec`.
  In fact, this function works more like a router for a couple of other functions, namely:

      Parameter      HTML document is created by
      -------------  ---------------------------------------------------------------------------------------
      `home=*`       `homePage()`
      `do=edit`      `editProject(project)` or `editProject([project, log])`
      `do=compile`   `compileProject(project)`
      `do=list`      `listProjects ()`
      `do=open`      `openProject(rec)` where `rec` is the requested record
      `do=delete`    `deleteProject(project)` or `deleteProject([project,confirmed])`
      `do=create`    `createProject (rec)` where `rec` is the requested record
      `do=move`      `moveProject(rec)` where `rec` is the requested record

  Note, that it is possible to move a project. But you cannot delete a project, at least not with this GUI.

  If `rec` does not neither have a `home` or `do` property, or if the `do` property does not have one of the defined values (`'edit'`, `'compile'`, etc.), an error is thrown.
---
---


---
// Singular page generators ////////////////////////////////////////////////////////////////////////////
---
---
unit
  doc
name
  TofGui
header
  The Single Page Generators
---
---
unit
  value
name
  TofGui.homePage
type
  $prod0 ($HtmlDoc)
value
  function () {
    var html
      = '<p><a href="http://tofjs.org" title="`tofjs.org` home page">' + Logo.chiLambdaCanvas + '</a></p>\n'
      + '<h1> The GUI (Graphical User Interface) for editing and managing <a href="http://tofjs.org">TofJs</a> projects </h1>\n'
      + '<div style="margin:10px; padding:10px; border:solid 3pt orange; font-size:x-large">\n'
      + '<p><a href="?home">home</a> of the TofJs GUI (= this page) </p>\n'
      + '<p><a href="?do=list">list</a> all projects </p>\n'
      + '<p><a href="?do=open">open</a> an existing project </p>\n'
      + '<p><a href="?do=create">create</a> a new project </p>\n'
      + '</div>\n'
      + '<p>Server root: <a href="/"><code>' + process.cwd() + '</code></a></p>\n'
      ;
    return Html.htmlDoc
      ( { title : "Tof.js GUI -- home page"
        , css   : defaultCss } )
      ( html ) ;
  }
info
  `TofGui.homePage()` returns a HTML document of the following layout

      +----------------------------------------------------------------------------------------------------------------+
      | +---------+                                                                                                    |
      | | chi-    |                                                                                                    |
      | | lambda- |                                                                                                    |
      | | logo    |                                                                                                    |
      | +---------+                                                                                                    |
      |                                                                                                                |
      | The GUI (Graphical User Interface) for editing and managing TofJs projects                                     |
      |                                                                                                                |
      | +------------------------------------------------------------------------------------------------------------+ |
      | |                                                                                                            | |
      | |  home of the TofJs GUI (= this page)                                                                       | |
      | |                                                                                                            | |
      | |  list all projects                                                                                         | |
      | |                                                                                                            | |
      | |  open an existing project                                                                                  | |
      | |                                                                                                            | |
      | |  create a new project                                                                                      | |
      | |                                                                                                            | |
      | +------------------------------------------------------------------------------------------------------------+ |
      | Server root: `/path/to/server/root`                                                                            |
      +----------------------------------------------------------------------------------------------------------------+


  `TofGui.homePage()` is the HTML page displayed when the `tofgui` server is running and the browser is pointed to

      http://localhost:port/?home

  From there, you can choose the first steps and choices when managing your [TofJs](http://tofjs.org) projects.

---
unit
  value
name
  TofGui.errorPage
type
  $prod ([$Html, $HtmlDoc])
value
  function (html) {
    return Html.htmlDoc
      ( { title : 'Error'
        , css   : defaultCss } )
      ( '<p><a href="/?home">home</a></p>\n'
      + '<h1 style="color:red">Error</h1>\n'
      + html );
  }
info
  .....
comment
  The `href` value in the `<a>` tag is not yet specified!!!
---
unit
  value
name
  TofGui.listProjects
type
  $prod0 ($HtmlDoc)
value
  function () {
    var dir = process.cwd();
    var branchL = findProjects (dir);
    var body = '<p><a href="?home">home</a></p>\n';
    if (branchL.length > 0) {
      body += '<h1> List of all projects found below <code>' + dir + '</code></h1>\n';
      body += '<table>\n';
      body += '<tr><th> Project </th><th> Project files </th>\n';
      for (var i = 0; i < branchL.length; i++) {
        var pi = branchL[i];
        body += '<tr>'
             +  '<th style="font-size:large"><a href="?do=edit&project=' + pi + '">' + pi + '</a></th>\n'
             +  '<td style="font-size:xx-small">' + projectFileList(pi) + '</td>\n'
             +  '<td>'
             +    '<a href="?do=move&sourceProject=' + pi + '"> move or copy </a> <br>'
             +    '<a href="?do=delete&project=' + pi + '"> delete </a>'
             +  '</td>\n'
             +  '</tr>\n';
      }
      body += '</table>\n';
    } else {
      body += "<h1>No tof.js projects found below <code>" + dir + "</code>.</h1>\n";
    }
    body += '<p><a href="?home">home</a></p>\n';
    var htmlDoc = Html.htmlDoc ({css: defaultCss}) (body);
    return htmlDoc;
  }
info
  .....
---
unit
  value
name
  TofGui.openProject
type
  prod ([$rec($null($String)), $HtmlDoc])
value
  function (rec) {

    var projectDir = rec.projectDir || '.';

    var projectL, // e.g. `['foo/pi1', 'foo/pi2', 'bar/bla/bla/proj123']`
        subDirL;  // e.g. `['foo', 'bar']`

    projectL = TofGui.findProjects (projectDir);

    try {
      subDirL = System.listDir (projectDir);
      subDirL = subDirL.filter ( function (id) { return System.isDirectory (System.Path.join ([projectDir, id])); } );
    } catch (e) {
      return errorPage ( '<p>Something went wrong in <code>TofGui.openProject(' + rec + ')<code></p>'
                       + '<pre>' + e + '</pre>' );
    }

    var html = '<p><a href="?home">home</a></p>\n';
    html += '<h1>Navigate</h1>\n'
         +  '<p>'
         +  clickablePathList ({"do":"open", "projectDir":projectDir}, 'projectDir')
         +  '</p>\n';
    html += '<ul>\n';
    for (var i = 0; i < subDirL.length; i++) {
      var subDir = System.Path.join ([projectDir, subDirL[i]]);
      var href = queryString ([{"do": "open", "projectDir": subDir}]);
      html += '<li>'
           +  '<a href="' + href + '">' + subDirL[i] + '</a>'
           +  '</li>\n';
    }
    html += '</ul>\n';
    html += '<h1>Available projects under <code>' + projectDir + '</code></h1>\n';
    html += '<ul>\n';
    for (var i = 0; i < projectL.length; i++) {
      var project = Unit.ProjectName.join ([projectDir, projectL[i]]);
      var href = queryString ([{"do": "edit", "project": project}]);
      html += '<li>'
           +  '<a href="' + href + '">' + projectL[i] + '</a>'
           +  '</li>\n';
    }
    html += '</ul>\n';

    return Html.htmlDoc
      ( { css: defaultCss
        , title: 'open project' } )
      ( html );
  }
info
  `TofGui.openProject(rec)` with lists the subdirectories and all projects that can be found under the `rec.projectDir` directory. It does so recursively until a project has been chosen.
---
unit
  value
name
  TofGui.deleteProject
type
  prod ([$singlePlus ([Unit.$ProjectName, $String]), $Boolean])
value
  function (args) {

    // reconstruct the arguments; `args` is either the project name `pi` or `[pi,confirmed]` with string `confirmed`
    var pi        = (typeof args === 'string' ? args  : args[0]);
    var confirmed = (typeof args === 'string' ? false : args[1]);

    // delete the project and create the HTML text
    var html = '<p><a href="?home">home</a></p>\n'
             + '<p><a href="?do=list">list</a></p>\n';
    if (confirmed) {
      html += '<h1>Deleting <code>' + pi + '</code> ... </h1>\n';
      try {
        var n = 0;
        for (var i = 0; i < projectFileExtensions.length; i++) {
          var ext = projectFileExtensions [i];
          if (System.exists (pi + ext)) {
            n++;
            System.removeFile (pi + ext);
            html += '<p><code>' + pi + ext + '</code> is deleted </p>';
          }
        }
        html += '<p><code>' + pi + '</code> is deleted and ' + n + ' files were removed. </p>';
      } catch (e) {
        return errorPage ( "<p>Something went wrong in deleting project <code>" + pi + "</code><p>\n"
                         + "<pre>" + e + "</pre>" );
      }
    } else {
      html += '<h1>Are you sure you want to delete the <code>' + pi + '</code> project?<h1>\n'
           +  '<form method="GET">\n'
           +  '<input type="hidden" name="do" value="delete">\n'
           +  '<input type="hidden" name="project" value="' + pi + '">\n'
           +  '<input type="hidden" name="confirmed" value="yes">\n'
           +  '<input type="submit" value="Yes, delete!">\n'
           +  '</form>\n';
    }

    // return the document
    return Html.htmlDoc
      ( { css: defaultCss
        , title: 'delete `' + pi + '`' } )
      ( html );
  }
info
  .....
---
unit
  value
name
  TofGui.createProject
type
  $prod ([$Rec, $HtmlDoc])
value
  function __createProject (rec) {

    // normalize the input `rec`
    rec.button     = rec.button     || 'none';   // value of one of the four <form> buttons; or `'none'`
    rec.projectDir = rec.projectDir || '';
    rec.projectId  = rec.projectId  || '';
    rec.subDir     = rec.subDir     || '';
    rec.newSubDir  = rec.newSubDir  || '';

    switch (rec.button) {
      case 'none':
        return makeHtml (rec);
      case 'change directory':
        rec.button = 'none';
        if (rec.subDir !== '') {
          rec.projectDir = Unit.ProjectName.join ([rec.projectDir, rec.subDir]);
          rec.subDir = '';
        }
        return __createProject (rec);
      case 'create new subdirectory':
        rec.newSubDir = Str.trim (rec.newSubDir);
        if (rec.newSubDir === '') {
          return errorPage ("You did not specify a new subdirectory.");
        } else {
          var newProjectDir = Unit.ProjectName.join ([rec.projectDir, rec.newSubDir]);
          if (System.exists (newProjectDir)) {
            return errorPage ("Could not create <code>" + newProjectDir + "</code>, it already exists.");
          } else {
            try {
              rec.button = 'none';
              System.mkdir (newProjectDir);
              rec.projectDir = newProjectDir;
              rec.newSubDir = '';
              return __createProject (rec);
            } catch (e) {
              return errorPage ( "<p>Could not create a new subdirectory.</p>\n"
                               + "<pre>" + e + "</pre>" );
            }
          }
        }
      case 'define project identifier':
        rec.projectId = Str.trim (rec.projectId);
        var c = Unit.$ProjectId.complain (rec.projectId);
        if (c) {
          return errorPage ( "<p><code>" + rec.projectId + "</code> is not a proper project identifier:</p>\n"
                           + "<pre>" + c + "</pre>" );
        } else {
          rec.button = 'none';
          return makeHtml (rec);
        }
      case 'create new project':   // try to create the new project and finally return the `editProject(project)` page ...
        try {
          var project = Unit.ProjectName.join ([rec.projectDir, rec.projectId]);
          var flatCodeFile = project + '.tofu.flatcode';
          if (System.exists (flatCodeFile)) {
            return errorPage ( 'Cannot create `' + project + '` as a new project. <br>\n'
                             + '<a href="' + flatCodeFile + '">' + flatCodeFile + '</a> already exists.' );
          } else {
            System.writeFileContent (flatCodeFile) (flatCodeTemplate (rec.projectId));
          }
          return editProject (project);
        } catch (e) {
          return errorPage ("Could not create the new project: " + e);
        }
      default:
        return errorPage ( "<p>Fatal and unpredicted error: `TofGui.createProject(rec)` "
                         + "was called with an undefined `rec.button` value `" + rec.button + "`.</p>" );
    }

    // `makeHtml(rec)`
    // produces the  HTML document. It assumes a normalized `rec`, otherwise an error is thrown.
    function makeHtml (rec) {

      // determine `project`, which is of type `$null($ProjectName)`
      var project;
      if (Unit.$ProjectId.chi (rec.projectId)) {
        if (rec.projectDir === '' || rec.projectDir === '/' || rec.projectDir === '.') {
          project = rec.projectId;
        } else {
          project = Unit.ProjectName.join ([rec.projectDir, rec.projectId]);
        }
      } else {
        project = null;
      }

      // determine `subDirL`, the list of all subdirectory names of `rec.projectDir`
      try {
        var subDirL = System.listDir (rec.projectDir || '.');
        subDirL = subDirL.filter ( function (id) { return System.isDirectory (System.Path.join ([rec.projectDir, id])); } );
      } catch (e) {
        return errorPage ( "<p>Could not determine the subdirectories of <code>" + rec.projectDir + "</code></p>\n"
                         + "<pre>" + e + "</pre>\n" );
      }

      // create the HTML `<table>` and `<form>`
      var html = '<p><a href="?home">home</a></p>\n';
      html += '<form method="GET">\n'
           +  '<input type="hidden" name="do" value="create">\n'
           +  '<input type="hidden" name="projectDir" value="' + rec.projectDir + '">\n'
           +  '<table style="border: ridge 5pt">\n';
      html += '<tr><td> Navigate: </td></tr>\n';
      if (subDirL.length > 0) {
        html += '<tr>\n';
        html += '<td rowspan="3"></td>\n';
        html += '<td rowspan="3">' + clickablePathList (rec, 'projectDir') + '</td>\n';
        html += '<td> <select name="subDir" size="1">';
        html += '<option value="" selected> </option>';
        for (var i = 0; i < subDirL.length; i++) {
          html += '<option value="' + subDirL[i] + '">' + subDirL[i] + '</option>';
        }
        html += '</select> </td>\n';
        html += '<td><input type="submit" name="button" value="change directory"></td>\n';
        html += '</tr>\n';
        html += '<tr>';
        html += '<td><input type="text" name="newSubDir"></td>\n';
        html += '<td><input type="submit" name="button" value="create new subdirectory"></td>\n';
        html += '</tr>\n';
        html += '<tr>'
        html += '<td><input type="text" name="projectId" value="' + rec.projectId + '"></td>\n';
        html += '<td><input type="submit" name="button" value="define project identifier"></td>\n';
        html += '</tr>\n';
      } else { // there are not subdirectories, so we exclude the "change directory" selection and button row
        html += '<tr>\n';
        html += '<td rowspan="2"></td>';
        html += '<td rowspan="2">\n'
             +  clickablePathList (rec, 'projectDir')
             +  '</td>\n';
        html += '<td><input type="text" name="newSubDir"></td>\n';
        html += '<td><input type="submit" name="button" value="create new subdirectory"></td>\n';
        html += '</tr>\n';
        html += '<tr>'
        html += '<td><input type="text" name="projectId" value="' + rec.projectId + '"></td>\n';
        html += '<td><input type="submit" name="button" value="define project identifier"></td>\n';
        html += '</tr>\n';
      }
      if (project !== null) {
        html += '<tr><td> New project: </td></tr>\n'
             +  '<tr><th colspan="4" style="font-size:xx-large;font-weight:bold;">' + project + '</th></tr>\n'
             +  '<tr><th colspan="4"><input type="submit" name="button" value="create new project"></th></tr>\n';
      }
      html += '</table>\n'
           +  '</form>\n';

      // finish and return the document
      return Html.htmlDoc
        ( { title : 'Create new project'
          , css   : defaultCss } )
        ( html );

    }

  }
info
  `TofGui.createProject(rec)` generates a HTML page working as a GUI for the creation of a new project. This HTML page is repeated on every request until the "create new project" button is pressed. Then the output of `createProject(rec)` returns `editProject(project)`.

  During the input dialog, the structure of the page content is very much like so:

      <table style="border: 5pt ridge grey">
      <tr>
        <td> Navigate: </td>
      </tr>
      <tr>
        <td rowspan="3"></td>
        <td rowspan="3">
          <a href="">/home/myself/foo/bar</a> /
          <a href="">one</a> /
          <a href="">two</a> /
          <a href="">three</a> /
          <a href="">four</a> /
        </td>
        <td>
          <select name="subDir" size="1">
            <option value="" selected> </option>
            <option value="uno"> uno </option>
            <option value="dos"> dos </option>
            <option value="tres"> tres </option>
            <option value="cuatro"> cuatro </option>
          </select>
        </td>
        <td>
          <input type="submit" value="change directory">
        </td>
      </tr>
      <tr>
        <td>
          <input type="text" width="20">
        </td>
        <td>
          <input type="submit" value="create new subdirectory">
        </td>
      </tr>
      <tr>
        <td>
          <input type="text" width="20" value="veryNewProject">
        </td>
        <td>
          <input type="submit" value="define project identifier">
        </td>
      </tr>
      <tr>
        <td> New project: </td>
      </tr>
      <tr>
        <th colspan="4" style="font-size: xx-large; font-weight:bold"> one/two/three/four/veryNewProject </th>
      </tr>
      <tr>
        <th colspan="4">
          <input type="submit" value="create new project">
        </th>
      </tr>
      </table>

  The parameter record for each request generated by that given formula is this

      key / item name            value                                                                                                                                 default value
      ------------------------   ------------------------------------------------------------------------------------------------------------------------------------  --------------
      `button`                   values: `'change directory'`, `'create new subdirectory'`, `'define project identifier'`, `'create new project'` and `'none'`         `'none'`
      `projectDir`               `one/two/three/four` the project directory                                                                                            `''`
      `projectId`                is a `$ProjectId`, e.g. `veryNewProject`                                                                                              `''`
      `subDir`                   value from the `<select>` field, all the subdirectories under `/home/myself/foo/bar/one/two/three/four`                               `''`
      `newSubDir`                value from the text input field                                                                                                       `''`

  Furthermore, there are other derived parameters:

      key                        value
      ------------------------   --------------------------------------------------------------------------------------------------------------
      `project`                  which is `projectDir/projectId`, in this case `one/two/three/four/veryNewProject`

  ...................

---
---
unit
  value
name
  TofGui.moveProject
type
  $prod ([$Rec, $HtmlDoc])
value
  function __moveProject (rec) {

    // normalize the input `rec`
    if (! ('sourceProject' in rec)) {
      return errorPage ( "<p><code>do=move</code> is called, but there is no <code>sourceProject</code> specified.</p>" );
    }
    rec.button    = rec.button    || 'none';
    rec.targetDir = (('targetDir' in rec) ? rec.targetDir : Unit.ProjectName.projectDir (rec.sourceProject));
    rec.targetId  = (('targetId'  in rec) ? rec.targetId  : Unit.ProjectName.projectId  (rec.sourceProject));
    rec.subDir    = rec.subDir    || '';
    rec.newSubDir = rec.newSubDir || '';

    switch (rec.button) {

      case 'none':
        return makeHtml (rec);

      case 'change directory':
        rec.button = 'none';
        if (rec.subDir !== '') {
          rec.targetDir = Unit.ProjectName.join ([rec.targetDir, rec.subDir]);
          rec.subDir = '';
        }
        return __moveProject (rec);

      case 'create new subdirectory':
        rec.newSubDir = Str.trim (rec.newSubDir);
        if (rec.newSubDir === '') {
          return errorPage ("You did not specify a new subdirectory.");
        } else {
          var newTargetDir = Unit.ProjectName.join ([rec.targetDir, rec.newSubDir]);
          if (System.exists (newTargetDir)) {
            return errorPage ("Could not create <code" + newTargetDir + "</code>, it already exists.");
          } else {
            try {
              rec.button = 'none';
              System.mkdir (newTargetDir);
              rec.targetDir = newTargetDir;
              rec.newSubDir = '';
              return __moveProject (rec);
            } catch (e) {
              return errorPage ( "<p>Could not create a new subdirectory.</p>\n"
                               + "<pre>" + c + "</pre>" );
            }
          }
        }
        break;

      case 'define target identifier':
        rec.targetId = Str.trim (rec.targetId);
        var c = Unit.$ProjectId.complain (rec.targetId);
        if (c) {
          return errorPage ( "<p><code>" + rec.targetId + "</code> is not a proper project identifier:</p>\n"
                           + "<pre>" + e + "</pre>" );
        } else {
          rec.button = 'none';
          return makeHtml (rec);
        }
        break;

      case 'move the project':
      case 'copy the project':
        var moveOrCopy = ( rec.button === 'move the project'
                         ? System.moveFile
                         : System.copyFile );
        try {
          var targetProject = Unit.ProjectName.join ([rec.targetDir, rec.targetId]);
          var n = 0;
          for (var i = 0; i < projectFileExtensions.length; i++) {
            var ext = projectFileExtensions[i];
            var source = rec.sourceProject + ext;
            var target = targetProject + ext;
            if (System.fileExists (source)) {
              n++;
              moveOrCopy (source) (target);
            }
          }
          var html = '<p><a href="?home">home</a></p>\n'
                   + '<p><a href="?do=list">list</a></p>\n';
          if (rec.button === 'move the project') {
            html += '<p>'
                 +  '<code>' + rec.sourceProject + '</code> has been moved to '
                 +  '<code><a href="?do=edit&project=' + targetProject + '">' + targetProject + '</a>. '
                 +  'Alltogeter, ' + n + ' file(s) have been moved.'
                 +  '</p>\n';
          } else {
            html += '<p>'
                 +  '<code><a href="?do=edit&project=' + rec.sourceProject + '">' + rec.sourceProject + '</a></code> '
                 +  'has been copied to '
                 +  '<code><a href="?do=edit&project=' + targetProject + '">' + targetProject + '</a></code>. '
                 +  'Alltogeter, ' + n + ' file(s) have been copied.'
                 +  '</p>\n';
          }
          return Html.htmlDoc
            ( { title : 'Project is moved/copied'
              , css : defaultCss } )
            ( html );
        } catch (e) {
          return errorPage ( "<p>Could not move or copy the project:<p>\n"
                           + "<pre>" + e + "</pre>" );
        }
        break;

      default:
        return errorPage ( "<p>Fatal and unpredicted error: `TofGui.moveProject(rec)` "
                         + "was called with an undefined `rec.button` value `" + rec.button + "`.</p>" );
    }

    // `makeHtml(rec)`
    // produces the HTML document. It assumes a  normalized `rec`, otherwise an error is thrown.
    function makeHtml (rec) {

      try {
        var subDirL = System.listDir (rec.targetDir || '.');
        subDirL = subDirL.filter ( function (id) { return System.isDirectory (System.Path.join ([rec.targetDir, id])); } );
      } catch (e) {
        return errorPage ( "<p>Could not determine thh subdirectories of <code>" + rec.targetDir + "</code></p>\n"
                         + "<pre>" + e + "</pre>" );
      }

      var targetProject = Unit.ProjectName.join ([rec.targetDir, rec.targetId]);

      var html = '<p><a href="?home">home</a></p>\n';
      html += '<form method="GET">\n'
           +  '<input type="hidden" name="do" value="move">\n'
           +  '<input type="hidden" name="sourceProject" value="' + rec.sourceProject + '">\n'
           +  '<input type="hidden" name="targetDir" value="' + rec.targetDir + '">\n'
           +  '<table style="border: ridge 5pt">\n';
      html += '<tr><td> Source: </td></tr>\n'
           +  '<tr><th colspan="4" style="font-size:xx-large;font-weight:bold;">' + rec.sourceProject + '</th></tr>\n'
      html += '<tr><td> Navigate: </td></tr>\n';
      if (subDirL.length > 0) {
        html += '<tr>\n';
        html += '<td rowspan="3"></td>\n';
        html += '<td rowspan="3">\n'
             +  clickablePathList (rec, 'targetDir')
             + '</td>\n';
        html += '<td> <select name="subDir" size="1">';
        html += '<option value="" selected> </option>';
        for (var i = 0; i < subDirL.length; i++) {
          html += '<option value="' + subDirL[i] + '">' + subDirL[i] + '</option>';
        }
        html += '</select> </td>\n';
        html += '<td><input type="submit" name="button" value="change directory"></td>\n';
        html += '</tr>\n';
        html += '<tr>';
        html += '<td><input type="text" name="newSubDir"></td>\n';
        html += '<td><input type="submit" name="button" value="create new subdirectory"></td>\n';
        html += '</tr>\n';
        html += '<tr>'
        html += '<td><input type="text" name="targetId" value="' + rec.targetId + '"></td>\n';
        html += '<td><input type="submit" name="button" value="define target identifier"></td>\n';
        html += '</tr>\n';
      } else { // there are not subdirectories, so we exclude the "change directory" selection and button row
        html += '<tr>\n';
        html += '<td rowspan="2"></td>';
        html += '<td rowspan="2">\n'
             +  clickablePathList (rec, 'targetDir')
             +  '</td>\n';
        html += '<td><input type="text" name="newSubDir"></td>\n';
        html += '<td><input type="submit" name="button" value="create new subdirectory"></td>\n';
        html += '</tr>\n';
        html += '<tr>'
        html += '<td><input type="text" name="targetId" value="' + rec.targetId + '"></td>\n';
        html += '<td><input type="submit" name="button" value="define target identifier"></td>\n';
        html += '</tr>\n';
      }
      if (targetProject !== null && targetProject !== rec.sourceProject) {
        html += '<tr><td> Target: </td></tr>\n'
             +  '<tr><th colspan="4" style="font-size:xx-large;font-weight:bold;">' + targetProject + '</th></tr>\n'
             +  '<tr><th colspan="4">'
             +     '<input type="submit" name="button" value="move the project">'
             +     '<input type="submit" name="button" value="copy the project">'
             +  '</th></tr>\n' ;
      }
      html += '</table>\n'
           +  '</form>\n';

      // finish and return the document
      return Html.htmlDoc
        ( { title : 'Move or copy a project'
        , css   : defaultCss } )
        ( html );
    }

  }
info
  .....

  `act=null` means not yet defined, `act='move'` means that the source project is (re)moved, `act='copy'` means that the source project remains.


  During the input dialog, the structure of the page content is very much like so:

      <table style="border: 5pt ridge grey">
      <tr>
        <td> Source: </td>
      </tr>
      <tr>
        <th colspan="4" style="font-size: xx-large; font-weight:bold"> one/two/foo/bar/someExistingProject </th>
      </tr>
      <tr>
        <td> Navigate: </td>
      </tr>
      <tr>
        <td rowspan="3"></td>
        <td rowspan="3">
          <a href="">/home/myself/foo/bar</a> /
          <a href="">one</a> /
          <a href="">two</a> /
          <a href="">three</a> /
          <a href="">four</a> /
        </td>
        <td>
          <select name="subDir" size="1">
            <option value="" selected> </option>
            <option value="uno"> uno </option>
            <option value="dos"> dos </option>
            <option value="tres"> tres </option>
            <option value="cuatro"> cuatro </option>
          </select>
        </td>
        <td>
          <input type="submit" value="change directory">
        </td>
      </tr>
      <tr>
        <td>
          <input type="text" width="20">
        </td>
        <td>
          <input type="submit" value="create new subdirectory">
        </td>
      </tr>
      <tr>
        <td>
          <input type="text" width="20" value="veryNewProject">
        </td>
        <td>
          <input type="submit" value="define target identifier">
        </td>
      </tr>
      <tr>
        <td> Target: </td>
      </tr>
      <tr>
        <th colspan="4" style="font-size: xx-large; font-weight:bold"> one/two/three/four/veryNewProject </th>
      </tr>
      <tr>
        <th colspan="4">
          <input type="submit" value="move the project"
                 title="`move` takes all files of the source and moves them into the new target">
          <input type="submit" value="copy the project"
                 title="`copy` duplicates the files of the source into the new target">
        </th>
      </tr>
      </table>

  The parameter record for each request generated by that given formula is this

      key / item name            value                                                                                                                                     default value
      ------------------------   ----------------------------------------------------------------------------------------------------------------------------------------  ---------------------------------
      `sourceProject`            a `$ProjectName`, e.g.`one/two/foo/bar/someExistingProject`                                                                               no default (error if not present)
      `button`                   `'change directory'`, `'create new subdirectory'`, `'define target identifier'`, `'move the project'`, `'copy the project'` and `'none'`  `'none'`
      `targetDir`                `one/two/three/four` the project directory                                                                                                `''`
      `targetId`                 is a `$ProjectId`, e.g. `veryNewProject`                                                                                                  `''`
      `subDir`                   value from the `<select>` field, all the subdirectories under `/home/myself/foo/bar/one/two/three/four`                                   `''`
      `newSubDir`                value from the text input field                                                                                                           `''`

  Furthermore, there are other parameters:

      key                        value
      ------------------------   --------------------------------------------------------------------------------------------------------------
      `targetProject`            which is `projectDir/projectId`, in this case `one/two/three/four/veryNewProject`

  ...................

---
unit
  value
name
  TofGui.compileProject
type
  $prod ([Unit.$ProjectName, $FlatCode, $HtmlDoc])
value
  function (pi) {
    return function (flatcode) {
      var flatCodeFile = pi + '.tofu.flatcode';
      var logFile      = pi + '.toflog.html';
      try {
        System.writeFileContent (flatCodeFile) (flatcode);
      } catch (e) {
        return errorPage ( "Something went wrong with saving the Flat Code to `" + flatCodeFile
                         + "`:\n<pre>" + e + "</pre>\n" );
      }
      var log;
      try {
        log = Unit.compile (pi);
      } catch (e) {
        return errorPage ( "Error compiling the `" + pi + "` project:\n"
                         + "<pre>" + e + "</pre>\n"
                         + "<h1>The compilation log (<a href=\"" + logFile + "\">" + logFile + "</a>)</h2>\n"
                         + "<pre>" + log + "</pre>\n" )
      }
      return editProject ([pi, log]);
    }
  }
info
  .....
---
unit
  value
name
  TofGui.editProject
type
  $prod ([$singlePlus ([Unit.$ProjectName, $String]), $HtmlDoc])
value
  function editProject (args) {

    // reconstruct the arguments; `args` is either the project name `pi` or `[pi, log]`
    var pi  = (typeof args === 'string' ? args : args[0]);
    var log = (typeof args === 'string' ? "" : args[1]);

    try {

      // The files involved
      var flatCodeFile              = pi + '.tofu.flatcode';
      var tofuFile                  = pi + '.tofu.json';
      var logFile                   = pi + '.toflog.html';
      var docFile                   = pi + '.tofd.html';
      var moduleFile                = pi + '.tofm.js';
      var globalScriptFile          = pi + '.tofg.js';
      var optimizedModuleFile       = pi + '.tofm.min.js';
      var optimizedGlobalScriptFile = pi + '.tofg.min.js';

      // Read the `flatCode` from the `flatCodeFile`
      var flatCode = System.readFileContent (flatCodeFile);

      // CSS settings
      var textAreaStyle
        = 'background-color:beige; padding: 5px' ;
      var boxStyle
        = 'width:600px; height:600px;'
        + ' margin:5px;'
        + ' resize:both; overflow:scroll; float:left;'
        + ' border:ridge;'
        + ' background-color: #EEEEEE;' ;
      var menuLinkStyle
        = 'font-size: large;';

      // 1. box
      var textAreaBox
        = '<form method="POST"'
        +      ' action="?do=compile&project=' + pi + '"'
        +      ' encoding="application/x-www-form-urlencoded"'
        +      ' style="position: relative; left:0; top:0;' + boxStyle + '"' + '>\n'
        + '<textarea name="flatcode" style="position:absolute; width:100%; height:100%; margin-right:20px;' + textAreaStyle + '">'
        + flatCode
        + '</textarea>\n'
        + '<input type="submit" name="submit" value="save and compile" style="position:absolute; bottom:10px; right:15px;">\n'
        + '</form>\n';

      // 2. box
      var compilerLogBox
        = '<div style="' + boxStyle + '">'
        + ( log === ''
          ? '<div style="text-align:center;font-style:italic">... not compiled, yet...</div>\n'
          : '<pre style="padding:5px">' + Html.entityify (log)+ '</pre>\n' )
        + '</div>\n' ;

      // 3. box
      var menuBox
        = '<div style="' + boxStyle + '">\n'
        + '<h3><code>' + pi + '</code> project files</h3>\n'
        + '<div style="' + menuLinkStyle + '">'
        +    projectFileList (pi)
        + '</div>\n'
        + '<h3>GUI menu</h3>\n'
        + '<ul style="' + menuLinkStyle + '">\n'
        + '<li><a href="?home">home</a></li>\n'
        + '<li><a href="?do=list">list</a> all projects</li>\n'
        + '<li><a href="?do=move&sourceProject=' + pi + '">move or copy</a> project `' + pi + '`</li>\n'
        + '<li><a href="?do=delete&project=' + pi + '">delete</a> project `' + pi + '`</li>\n'
        + '</ul>\n'
        + '<h3>Links</h3>\n'
        + '<ul style="' + menuLinkStyle + '">\n'
        + '<li><a href="http://tofjs.org">tofjs.org</a> home page</li>\n'
        + '</ul>\n'
        + '</div>\n' ;

      // The final HTML document
      return Html.htmlDoc
        ( { title : "`" + pi + "` project in Tof.js"
          , css   : defaultCss } )
        ( textAreaBox
        + compilerLogBox
        + menuBox );

    } catch (e) {
      return errorPage (e);
    }
  }
info
  Syntax

      TofGui.editProject (pi)
      TofGui.editProject ([pi, log])

  .....
---
---


---
// Auxiliary functions ////////////////////////////////////////////////////////////////////////////
---
---
unit
  doc
name
  TofGui
header
  Auxiliary functions
---
---
unit
  value
name
  TofGui.findProjects
type
  $prod ([System.$Path, $list(Unit.$ProjectName)])
value
  function (rootDir) {
    function __findProjects (branch) {
      var localPath = System.Path.join ([rootDir, branch]);
      if (System.exists (localPath)) {
        if (System.isDirectory (localPath)) {
          var branchL = [];
          var nameL = System.listDir (localPath);
          for (var i = 0; i < nameL.length; i++) {
            var subBranch = System.Path.join ([branch, nameL[i]]);
            branchL = branchL.concat (__findProjects (subBranch));
          }
          return branchL;
        } else {
          if (System.isFile (localPath) && Str.endsWith (localPath) ('.tofu.flatcode')) {
            var pi = branch.substr (0, branch.length - 14);   // note, that the length of '.tofu.flatcode' is 14
            return [pi];
          } else {
            return [];
          }
        }
      } else {
        return [];
      }
    }
    return __findProjects ('.');
  }
info
  `TofGui.findProjects(dir)` returns a list of all branches `[b1,...,bN]` so that each of `dir/b1`, ...., `dir/bN` is a project, i.e. each of `dir/b1.tofu.flatcode`, ..., `dir/bN.tofu.flatcode` is an existing file.

  An example could be this

      > TofGui.findProjects('/home/myname/myrepo')
      [ 'project1', 'subrepo/project23', 'subrepo/project34' ]

  which means that the following are paths to existing files

      /home/myname/myrepo/project1.tofu.flatcode
      /home/myname/myrepo/subrepo/project23.tofu.flatcode
      /home/myname/myrepo/subrepo/project34.tofu.flatcode

---
unit
  value
name
  TofGui.projectFileList
type
  $prod ([Unit.$ProjectName, $Html])
value
  function (pi) {

    var dir = System.Path.dirname (pi);
    var id = System.Path.basename (pi);

    var flatCodeFile              = id + '.tofu.flatcode';
    var tofuFile                  = id + '.tofu.json';
    var moduleFile                = id + '.tofm.js';
    var globalScriptFile          = id + '.tofg.js';
    var optimizedModuleFile       = id + '.tofm.min.js';
    var optimizedGlobalScriptFile = id + '.tofg.min.js';
    var docFile                   = id + '.tofd.html';
    var logFile                   = id + '.toflog.html';

    function entry (file, title) {
      var path = System.Path.join ([dir, file])
               . replace ('\\', '/');            // the `path` must be web/UNIX style, not Windows
      if (System.fileExists (path)) {
        return ( '  <li><a href="' + path + '">' + file + '</a>, '
               + System.fileSize (path) + ' Bytes, '
               + ' the ' + title
               + ' </li>\n' );
      } else {
        return ( '  <li><code>' + file + '</code>,'
               + ' the ' + title
               + ' </li>\n' );
      }
    }

    var html = '<ul style="margin:0;padding:0 0 0 5;">\n'
             + entry (flatCodeFile, "Flat Code File")
             + entry (tofuFile, "Tofu File")
             + entry (moduleFile, "Module File")
             + entry (globalScriptFile, "Global Script File")
             + entry (optimizedModuleFile, "Optimized Module File")
             + entry (optimizedGlobalScriptFile, "Optimized Global Script File")
             + entry (docFile, "Documentation File")
             + entry (logFile, "Compilation Log File")
             + '</ul>\n';
    return html;
  }
info
  `TofGui.projectFileList(pi)` returns an unordered list, i.e. a `<ul>` list, with all the files involved in mentioned project.

  For example,

      > display (TofGui.projectFileList ('tofus/simpleSample'))
      ╭───────────────────────────────────────────────────────────────────────────────────────────────────────────╮
      │<ul style="margin:0;padding:0 0 0 5;">↵                                                                    │
      │  <li><a href="tofus/simpleSample.tofu.flatcode">simpleSample.tofu.flatcode</a> the Flat Code File </li>↵  │
      │  <li><a href="tofus/simpleSample.tofu.json">simpleSample.tofu.json</a> the Tofu File </li>↵               │
      │  <li><a href="tofus/simpleSample.tofm.js">simpleSample.tofm.js</a> the Module File </li>↵                 │
      │  <li><a href="tofus/simpleSample.tofg.js">simpleSample.tofg.js</a> the Global Script File </li>↵          │
      ...
      ...
      │  <li><a href="tofus/simpleSample.tofd.html">simpleSample.tofd.html</a> the Documentation File </li>↵      │
      │  <li><a href="tofus/simpleSample.toflog.html">simpleSample.toflog.html</a> the Compilation Log File </li>↵│
      │</ul>↵                                                                                                     │
      │                                                                                                           │
      ╰───────────────────────────────────────────────────────────────────────────────────────────────────────────╯
      undefined
---
unit
  value
name
  TofGui.queryString
type
  $lambda ([$list($rec($null($String))), $Html])
value
  function queryString (recL) {
    return '?' + Uri.record2queryString (Rec.updateList (recL));
  }
info
  `TofGui.queryString([r_1,...,r_n])` first combines the string records `r_1,...,r_n` into a single record `r`, by means of
  `Rec.updateList([r1,...,r_n])`, converts this into a query string, by means of `Uri.record2queryString(r)`, and finally
  adds a question mark `?` at the front.

  For example,

      > TofGui.queryString ([{one:'123', two:'234', three:'345'}])
     '?one=123&two=234&three=345'

      > TofGui.queryString ([{one:'123'}, {two:'234'}, {one:'321', three:'345'}])
      '?one=321&two=234&three=345'

---
unit
  value
name
  TofGui.clickablePathList
type
  $prod ([$args ([$rec($null($String)), $Id]), $Html])
value
  function (rec, dirKey) {
    var dirValue = rec[dirKey] || '';
    var steps = Unit.ProjectName.split (dirValue);   // e.g. `['one', 'two', 'three', 'four']`
    var serverRootDirectory = process.cwd();         // e.g. `/home/myself/myprojects/` or `C:\\User\\myself\\myprojects\\`
    var dir = '';
    var newRec = {}; newRec[dirKey] = dir;
    var href = queryString ([rec, newRec]);
    var html = '<a href="' + href + '">' + serverRootDirectory + '</a>\n';
    for (var i = 0; i < steps.length; i++) {
      dir = Unit.ProjectName.join ([dir, steps[i]]);
      newRec = {}; newRec[dirKey] = dir;
      href = queryString ([rec, newRec]);
      html += '/' + '<a href="' + href + '">' + steps[i] + '</a>\n';
    }
    return html;
  }
info
  `linkedPathList (rec, dirKey)`, where `rec` is a `$rec($null($String))` value and `dirKey` is the key for the (project) directory in `rec`.
  It should have a `projectDir` key with a value like say `one/two/three/four`, otherwise this value is set to `''`.

  For example,

      > TofGui.clickablePathList ({projectDir: 'one/two/three/four', greeting: 'Hello!'}, 'projectDir')
      ╭────────────────────────────────────────────────────────────────────────────╮
      │<a href="?projectDir=&greeting=Hello!">/home/buc/Dropbox/buc/tofjs4</a>↵    │
      │/<a href="?projectDir=%2Fone&greeting=Hello!">one</a>↵                      │
      │/<a href="?projectDir=%2Fone%2Ftwo&greeting=Hello!">two</a>↵                │
      │/<a href="?projectDir=%2Fone%2Ftwo%2Fthree&greeting=Hello!">three</a>↵      │
      │/<a href="?projectDir=%2Fone%2Ftwo%2Fthree%2Ffour&greeting=Hello!">four</a>↵│
      │                                                                            │
      ╰────────────────────────────────────────────────────────────────────────────╯
---
unit
  value
name
  TofGui.projectFileExtensions
type
  $list ($String)
value
  ['.tofu.flatcode', '.tofu.json', '.tofg.js', '.tofm.js', '.tofd.html', '.toflog.html' ]
info

      `projectFileExtensions`        File
      -------------------------      ------------------------------------------
      `.tofu.flatcode`               Flat Code Tofu file
      `.tofu.json`                   JSON Tofu file
      `.tofg.js`                     Global Script file
      `.tofm.js`                     Module file
      `.tofd.html`                   Document file
      `.toflog.html`                 Compilation log file

---
unit
  value
name
  TofGui.defaultCss
value
  '  body { background-color: ivory; padding: 20px; }' + '\n' +
  '  td, th { border:solid 1pt; padding:3px; background-color:beige }'
info
  `TofGui.defaultCss` is the style sheet used for all Tof GUI pages.
---
unit
  value
name
  TofGui.flatCodeTemplate
type
  $lambda ([$Id, $FlatCode])
value
  function (projectId) {
    return ( '---'
    + '\n' + 'project'
    + '\n' + '  ' + projectId
    + '\n' + 'version'
    + '\n' + '  0.0.0'
    + '\n' + 'unit'
    + '\n' + '  value'
    + '\n' + 'info'
    + '\n' + '  This is a new project.'
    + '\n' + '  Here we provide some user information in [Markdown](http://en.wikipedia.org/wiki/Markdown) syntax.'
    + '\n' + 'comment'
    + '\n' + '  A **comment** is some information for developers, hidden from the ordinary user.'
    + '\n' + '---'
    + '\n' + '---'
    + '\n' );
  }
info
  `TofGui.flatCodeTemplate('newProjectId')` returns this Flat Code:

      .....

comment
  New version:

        ---
      project
        dummy1
      version
        0.0.0
      unit
        value
      info
        This is a new project.
        Here we provide some user information in [Markdown](http://en.wikipedia.org/wiki/Markdown) syntax.
      comment
        A **comment** is some information for developers, hidden from the ordinary user.
      ---

      /***
      // Template for proper units:
      ---
      unit
        ... value(anything/struct/construct/type/function)/proto/param  [global/local] [constant/variable] ...
      name
        ...name...
      type
        ...type...
      value
        ...value...
      info
        ... documentation for this unit ...
      comment
        ... information for developers ...
      ---
      ***/
---



---
// Logo ///////////////////////////////////////////////////////////////////////////////////////////
---
unit
  struct
name
  TofGui.Logo
---
unit
  value
name
  TofGui.Logo.chiLambdaCanvas
type
  $Html
value
  '<canvas id="chiLambdaCanvas" width=200 height=200 style="border: 1pt solid orange"></canvas>\n' +
  '<' + 'script' + '>' + '\n' +
  '  var context = document.getElementById("chiLambdaCanvas").getContext("2d");\n' +
  '  var rg = context.createRadialGradient (80,80,15,100,100,90);\n' +
  '  rg.addColorStop (0, "yellow");\n' +
  '  rg.addColorStop (1, "red");\n' +
  '  context.fillStyle = rg;\n' +
  '  context.font = "180px monospace";\n' +
  '  context.shadowOffsetX = 5;\n' +
  '  context.shadowOffsetY = 5;\n' +
  '  context.shadowBlur = 3;\n' +
  '  context.shadowColor = "green"; \n' +
  '  context.lineWidth = 4.0;\n' +
  '  context.fillText ("\u03C7", 0, 145);\n' +
  '  context.fillText ("\u03BB", 75, 145);\n' +
  '<' + '/' + 'script' + '>' + '\n'
info
  Contains the HTML code for the following `<canvas>` element and according script.
---
---
unit
  value
name
  TofGui.Logo.tofJsCanvas
type
  $Html
value
  '<canvas id="tofJsCanvas" width=400 height=120 style="border: 1pt solid orange"></canvas>\n' +
  '<' + 'script' + '>' + '\n' +
  '  var context = document.getElementById("tofJsCanvas").getContext("2d");\n' +
  '  var rg = context.createRadialGradient (80,80,15,100,100,90);\n' +
  '  rg.addColorStop (0, "yellow");\n' +
  '  rg.addColorStop (1, "red");\n' +
  '  context.fillStyle = rg;\n' +
  '  context.font = "110px monospace";\n' +
  '  context.shadowOffsetX = 5;\n' +
  '  context.shadowOffsetY = 5;\n' +
  '  context.shadowBlur = 4;\n' +
  '  context.shadowColor = "green"; \n' +
  '  context.lineWidth = 4.0;\n' +
  '  context.fillText ("tof.js", 0, 87);\n' +
  '<' + '/' + 'script' + '>' + '\n'
info
  Contains the HTML code for the following `<canvas>` element and according script.
---
---
