These files got autogenerated but then I modified them a lot.  At the moment APP=Tx
So if autogeneration blows them away I saved my modified versions as *.[1,2..]

Tx.php		     Specifies IS structure for a Tx web app.
isTx.js		     Got the addCreate() function to make a nice form as a bordered table.
		     Could use a better submit button, like from the Login page.
index.php	     Put in the humbeep.css stylesheet link. Find the login one and use that, silly.
isTx.db		     Created the tv.Tx table, and the tx_user,

IDEAS:

Security model is to have a privs entry in users' tvuserdb table
entry.  This is checked against the logged-in user's request and only
empty sql is executed if they lack permission, all on the server PHP
side.  JS on the client can do anything, but the server will do no-ops
unless the logged-in user has the necessary permission.  The 7
permissions are SCRUD+NONE+ALL: priv.{SEARCH, CREATE, READ, UPDATE,
DELETE, NONE, ALL, ADMIN}. ("ALL" means only SCRUD privs, ADMIN adds
root user rights).

The IS model builds a single-page app with one primary URL
(index.php), with SCRUD operations:

   *defined* at levels of client HTML, client APP JS, client generic
    JS, server PHP, and server DB, and

   *orchestrated* through some UI-defining HTML forms and onsubmits, etc.
   exemplified in isindex.example.php but that is UGLY, so I use my
   own UI-defining forms &c (BaseThing in my Parts/Things architecture)
   and just call the IS API to push/pull data from the client side.

A basic example IS form has id,name,action,onsubmit parameters and a submit
button, along with selects and inputs with type, value, and defaults.

	(An onsubmit is generally supposed to validate the form data
	and return false if bad; in our case it should validate too
	but mainly does web requests and results handling, a.k.a. AJAX
	XHR.  Type and value are the field data type and value, and
	come from the APP.php Information Structure.)

A slightly more vanilla HTML FORM's 'action' is just a URL to go to,
where the form is packaged up by the browser into an HTTP request with
method=(GET|POST).  In the IS world, we do this in the so-called AJAX
style via the onsubmit="return is_APP_OP()" which prepares a url, json
message body, and callback results handling code, defined in
./isAPP.js:is_APP_OP(), and executed in isapi.js:isOP() using
XmlHttpRequest.  The callback passed into that asynchronous-return
process eventually is called, grabs the return data, puts it into
where it knows it goes.  This approach can bypass triggering a full
action=$url page reload.

In particular, IS does full page reloads only during the initiation of
these operations: START (no op), CREATE (SC), SEARCH (SS), and
READ(SR) (These two-letter op codes begin with 'S' for Start the op,
then S|C|R|E|D for Search,Create,Read,Edit,Delete.)  These ops hit
index.php[?op=$op]) and for these the reload with op creates a whole
page as a screen to work on, where the screen lets the user specify
the record contents (CREATE), the desired records (SEARCH), or the
record number (READ), using an appropriate form.  Then post-submit
form-handling initiated from those CREATE, READ, SEARCH pages uses the
AJAX approach to complete the operation: control flows through the
FORM onSubmit is_APP_OP() call to carry out the specified op and
display the new relevant page parts.

So much for SCR, now UD: In the cases of UPDATE/EDIT and DELETE, the
operation needs a record id first, so those are only made accessible
from the SEARCH and READ results displays, which are the places where
the user can specify a particular row.

DELETE is initiated as index.php?op=SD&id=$id via an A HREF link in
the Edit or Search results display, labeled Delete.  Thus flow control
begins in the client's FORM, but bypasses client-side JS.

       (Client-side JS *would* be an onSubmit call to
       isAPP.js:is_APP_delete() which *would* then call
       isapi.js:isDelete() and *would* then send XHR DELETE message to
       isTx.php to do the SQL and return the DELETE's result
       (success/failure/rows-affected/etc.), displaying that in a
       popup alert(), and then redoing the previous search from the
       result of which the delete was initiated.  But we bypass 
       that JS and AJAX work in case of DELETE and EDIT.)

Instead we use action=$uri which calls index.php itself for a
full-page reload, to detect op=SD and call the SQL for a delete,
showing the delete result message in the resulting page.  User can
then proceed to from the top level to redo available SCRUD ops.

EDIT is initiated with the same index.php but op=SE (Start Edit) (with
row id) from the client's search results display.  The browser calls
index.php?op=SE&id=$id as a GET with a full page reload but requesting
a single-row display into the same form as is used in CREATE, however
it is not just editable but also pre-populated with the row's existing
values as taken from the DB, and offering a Submit button labelled
SAVE CHANGES.  Both the Edit and the Create forms are built by
index.php:BuildRecordForm($op,$id), which implements both
BuildEditForm() and BuildCreateForm() in full-page reloads.  The
form's onSubmit calls isAPP.js:is_APP_Update() to AJAX XHR an Update
of the changes via isapi.js:isUpdate(), which sends XHR UPDATE to
isTx.php which does the SQL and returns the UPDATE's result
(success/failure/rows-affected/etc.)  to be displayed in a popup
myonload:alert().  For now, you can continue on the main menu, but it
would be nice in future to re-display the Edit display in case of need
for another change of the same row.

Thus we mix page reloads in the search result EDIT link (edit
initiation) with AJAX calls inside the EDIT form (edit execution).

Full-page displays are constructed in index.php;
AJAX-returned data are displayed in the onload callback functions
defined in isAPP.js.  Again, AJAX calls are made on the client side 
by isAPP.js through isapi.js, and handled on the server side mostly by
isAPP.php (which also checks the security privileges of the logged-in
requester before doing the operation on the DB).

The thought is with this general structure in place, an IS app
developer can modify the various page displays and SCRUD op results
displays, based on how they want to show stuff, and what they want
next to get or display or navigate to.  But the working SCRUD
operations are there at the levels of isapi.js, isTx.js, isTx.php and
index.php with some kind of generically useable user interface.

To follow the general dataflow for the different SCRUD ops use:
   	 grep -h HOWTO * | sort -n
	 tail -f isphp.err
	 tail -f /var/log/php/php_errors.log


Rather reiterating the above, I also wrote the following while
struggling to understand the flow:

     index.php->BuildReadForm()->isTx.js:is_Tx_read()->isapi.js:isRead()
     does AJAX call onto the server.  sets XHR up and sends it into
     the ether.  If the XHR gets a return result invisible to us,
     check the onload function, which is called asynchronously upon
     eventual receipt of the result.

     Server-Side: isAPP.php recieves the AJAX call request, unpacks
     it, checks security privileges for the logged-in user, builds an
     SQL query, queries mysql with it, receives the result,
     re-packages the result as appropriate (as JSON?), and returns it
     via php header() and XHR send functions.  Unseen, this processes
     through to the inputs of the AJAX requester/caller
     (is_Tx_Search():myonload()) the myonload() function was pushed by
     isTx.js::is_Tx_Search() down into isapi.js::isSearch() and thence
     into the XHR process; returning, myonload() gets its data, and
     puts that into the HTML document.
     
     One might think the above is wrong since onSubmit=\"return fn()\"
     was originally intended for a client-side form data validator
     function.  Whereas action=url routes the form data (in URL if
     GET, in body if POST) to url to do the form's client side work,
     returning a page to display.  But onSubmit=return is_Tx_OP()
     instead an AJAX request function.  Combined with action=\"\",
     onSubmit=\"return fn()\" lets fn() validate but then also at the
     end call JS to do AJAX.

DONE:
   Fixed: put them under links in a nav bar so
     you don't have to stare at them when you don't want them.
   Fixed: Create Tx returns a 404, so something is wrong.
     Figure it out big bboy.  Use your Developer Tools.
     Or see if isTx.php barfs always or only now.
     isTx.php barfed trying to open  /tmp/isphp.err in error_log.  ok create erroutf and use that.
     isTx.php barfed on getallheaders() and apache_request_headers() maybe because in CLI and there is no apache.
     but isTx.php shows nothing when your browser looks for it.
   Document the flow from client form to server DB back to client DOM. (grep -h HOWTO * | sort -n)
   Create fails to alert as expected upon SUBMIT, lacking id and name in the form.  Fixed.
   View document source fails to show onSubmit parameter, a function was defined twice. Fixed.
   Read won't work until after Create works because you need something to read.
      So get Create to work first.  Create works.
   Read couldn't seem to iterate through e.entries() to find id,
      that's because read form inputs lacked id & name.
   Read uses modified gotPut() and ObjectPut() to create a table with header and rows from result.
   Search construst SELECT from form, fetch it as JSON array of objects,
      parse into a table for viewing and selecting Edit or Delete.
   Fixed, Search fails, with several bugs.
   Added Delete & Edit links to the search results table.
   Implemented index.php?op=SD&id=$id (SD=Start Delete)
   Implemented index.php?op=SE&id=$id (SD=Start Edit) in index.php, and isTx.js...
   Show the row with a delete link in thesearch results;
      the link hits index.php?op=SD&id=$id putting delete results into the Data Area.
   Bypass isTx.js:addOP() functions along with index.php:isOPDiv.
   Carry out the SCRUD Ops successfully.
   A search-result table with [DELETE] [EDIT=read+editable] buttons
   A create form with editable texts, selectable enums, etc., and a [SAVE=Create] button
   A read-result display, update/editable, deleteable via links
   Fixed: login.php fails to forward to aim="is/Tx" after login.  Don't bindParam :privs $privs.
   Implemented privileges in /var/www/shared/lib.php
   Bootstrap fonts all tiny.  Fixed by humbeep.css: body { .. font-size: 2.0vw; }
   Clean up and minimize the screen displays from useless crap and structure.  Hide inside if debugs.
   Implemented index.php?op=SE&id=$id to show the row in dataaarea, allowing INPUTs editing and SAVE CHANGES.
   index.php: permitted(priv::READ|priv::DELETE)... checks if they have user permission to delete/edit on the DB
   return results processing of the Read operation shows the read results (the whole row, at present) 
   SCRUD ops with handlers on the client and server are looking reasonable.
   Tried prettifying with bootstrappy classes from login.php, but not pretty.  Generalized and commented it out.
   Validated that the particular user has permission to DELETE, EDIT, etc.
      At what level? Using login.php and lib.php: wherever DB is called, thus index.php and isTx.php.
   diff -y templates/predecessorFN Tx/derivedFN & emacs templates/predecessorFN
   Next abstract this all down to essence, putting generative PHP
   source herefore into ops/is/template so they will show up in future
   apps more easily.
     Do: Tx.php, isapi.js, isTx.js, index.php, isTx.php, isTx.db
     Did Tx.php, isapi.js  isTx.js, index.php, isTx.php, isTx.db

TO DO:
   Test the templates again by running make Tx into a new directory and then compare the outputs,
   also test that they actually work correctly, fixing differences. Then you can publish version++.

    Form handler to ask are you sure you want to delete this entry?
        [Yes] -> isTx.js handler, send to isTx.php to actually delete it via isTx.php?op=DELETE&id=$id
        [No]  -> link through to index.php?op=READ&id=$id
	This seems like a lot of extra useless legwork and what about instead just
	do the delete call inside index.php instead of exposing so many other attackable layers.
   
     Validation functions for each column type. No semicolons equalses, mysql reserved words like SELECT, etc.
     Validate that inputs are not empty.
     Search form with validation of each entered field against its enum or text type etc., and a [SEARCH] button
     Read-choice form which knows the range to be read, like an id amongst a set of integer ranges.
  // before carrying out the operation, 
  // before displaying the possibility of the operation in a menu, 
  // Then the handler should display it with succeeding choices made
  // accessible, e.g., edit, edit/save, delete, or nothing if user lacks permission

If using Tom's BaseThing, this might help you.

The IS Interface = Just these 5 functions from isapi.js:
  - isCreate(server, json, onload)
  - isRead(server, json, onload)
  - isSearch(server, json, onload)
  - isUpdate(server, json, onload)
  - isDelete(server, json, onload)

Pattern (ExampleApp.js):
  class ExampleApp {
    constructor() {
      this.currentRecord = null;
      this.initializeBaseThing();
      this.initializeScreens();
    }

    initializeBaseThing() {
      const menus = {
        'File': [{id: 'new', label: 'New Record'}, {id: 'exit', label: 'Exit'}]
      };
      baseThingInstance.registerMenus(menus, (action) => this.handleMenu(action));
    }

    initializeScreens() {
      const screens = {
        list: {up: null, down: null, left: 'create', right: 'detail'},
        create: {up: null, down: null, left: 'list', right: null},
        detail: {up: null, down: null, left: 'list', right: 'edit'}
      };
      baseThingInstance.enableSwipes(screens, (from, to, dir) => this.handleTransition(from, to));
      baseThingInstance.registerScreen('list', {
        onEnter: () => this.loadList(),
        render: () => this.renderList()
      });
    }

    loadList() {
      isSearch('/path/isAppName.php', {}, (results) => {
        this.records = results;
        baseThingInstance.updateContent(this.renderList());
      });
    }

    renderList() {
      return `<div id="list-screen">
        ${this.records.map(r => `<div onclick="app.viewDetail(${r.id})">${r.name}</div>`).join('')}
      </div>`;
    }

    handleMenu(action) {
      if (action === 'new') baseThingInstance.transitionTo('create', 'left');
    }

    stop() {
      // BaseThing calls this on Exit
      window.location = '/';
    }
  }

  document.addEventListener('DOMContentLoaded', () => {
    window.app = new ExampleApp();
  });

  Key Pattern:
  - BaseThing provides: menus, swipes, lifecycle, modals
  - IS provides: CRUD API via isapi.js
  - App provides: screens, business logic, rendering
  - BaseThing does NOT instantiate IS - App does!
