From 5a15a551a3edbc1cdb331904d2864a6d15dfdfd9 Mon Sep 17 00:00:00 2001
From: Neil Gershenfeld <gersh@cba.mit.edu>
Date: Sat, 31 Mar 2018 11:25:11 -0400
Subject: [PATCH] program load working?

---
 files.html                  |   4 +-
 js/mods.js                  | 343 ++++++++++++------------------------
 js/modules.js               |   6 +-
 js/{load.js => programs.js} |  34 ++--
 make                        |   2 +-
 modules/index.html          | 142 ---------------
 programs/index.html         |  54 ------
 programs/index.js           |  43 +++++
 8 files changed, 168 insertions(+), 460 deletions(-)
 rename js/{load.js => programs.js} (64%)
 delete mode 100644 modules/index.html
 delete mode 100644 programs/index.html
 create mode 100644 programs/index.js

diff --git a/files.html b/files.html
index a3476e3..c2f750b 100644
--- a/files.html
+++ b/files.html
@@ -17,10 +17,10 @@
 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href='./js/deviceserver.js'>deviceserver.js</a><br>
 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href='./js/echoserver.js'>echoserver.js</a><br>
 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href='./js/files.js'>files.js</a><br>
-&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href='./js/load.js'>load.js</a><br>
 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href='./js/mods.js'>mods.js</a><br>
 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href='./js/modules.js'>modules.js</a><br>
 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href='./js/printserver.js'>printserver.js</a><br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href='./js/programs.js'>programs.js</a><br>
 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href='./js/serialserver.js'>serialserver.js</a><br>
 <i>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;three.js</i><br>
 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href='./js/three.js/three.min.js'>three.min.js</a><br>
@@ -98,7 +98,6 @@
 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href='./modules/image/threshold'>threshold</a><br>
 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href='./modules/image/vector%20mask'>vector mask</a><br>
 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href='./modules/image/vectorize'>vectorize</a><br>
-&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href='./modules/index.html'>index.html</a><br>
 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href='./modules/index.js'>index.js</a><br>
 <i>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;input</i><br>
 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href='./modules/input/video'>video</a><br>
@@ -175,7 +174,6 @@
 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href='./programs/image/motion%20detect'>motion detect</a><br>
 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href='./programs/image/palette%20mask%20raster'>palette mask raster</a><br>
 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href='./programs/image/palette%20mask%20vector'>palette mask vector</a><br>
-&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href='./programs/index.html'>index.html</a><br>
 <i>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;iterate</i><br>
 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href='./programs/iterate/z%20theta%20scan'>z theta scan</a><br>
 <i>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;machines</i><br>
diff --git a/js/mods.js b/js/mods.js
index c28fb28..08c7def 100644
--- a/js/mods.js
+++ b/js/mods.js
@@ -58,8 +58,8 @@ document.addEventListener('contextmenu',function(evt){
       }
    var div = document.createElement('div')
    make_menu(div)
-   add_menu(div,'programs',programs)
    add_menu(div,'modules',modules)
+   add_menu(div,'programs',programs)
    add_menu(div,'edit',edit)
    add_menu(div,'options',options)
    document.body.appendChild(div)
@@ -88,70 +88,94 @@ document.addEventListener('contextmenu',function(evt){
       textdiv.addEventListener('touchstart',click)
       div.appendChild(textdiv)
       }
-   function programs(evt) {
+   //
+   // modules menu
+   //
+   function modules(evt) {
       evt.preventDefault()
       document.body.removeChild(evt.target.parentNode)
       var div = document.createElement('div')
       make_menu(div)
       //
-      // open server program
+      // open server module
       //
-      add_menu(div,'open server program',function(evt){
+      add_menu(div,'open server module',function(evt){
+         function module_label(label) {
+            var div = document.createElement('div')
+            var i = document.createElement('i')
+            i.appendChild(document.createTextNode(label))
+            div.appendChild(i)
+            div.appendChild(document.createElement('br'))
+            menu.appendChild(div)
+            }
+         function module_menu(label,module) {
+            var div = document.createElement('div')
+            div.appendChild(
+               document.createTextNode('\u00A0\u00A0\u00A0'+label))
+            div.addEventListener('mouseover',function(evt){
+               evt.target.style.fontWeight = 'bold'})
+            div.addEventListener('mouseout',function(evt){
+               evt.target.style.fontWeight = 'normal'})
+            div.addEventListener('mousedown',function(evt){
+               document.body.removeChild(evt.target.parentNode)
+               mods.globals.menu = null
+               mod_message_handler(module,
+               evt.clientY+document.body.scrollTop,
+               evt.clientX+document.body.scrollLeft)})
+            div.appendChild(document.createElement('br'))
+            menu.appendChild(div)
+            }
          document.body.removeChild(evt.target.parentNode)
-         window.callback = function(msg) {
-            if (location.port == 80)
-               var uri = encodeURI('http://'+location.hostname
-                  +'?program='+msg)
-            else
-               var uri = encodeURI('http://'+location.hostname+':'
-                  +location.port+'?program='+msg)
-            set_prompt('<a href='+uri+'>program link</a>')
-            prog_message_handler(msg)
+         var menu = document.createElement('div')
+         make_menu(menu)
+         document.body.appendChild(menu)
+         menu.style.width = mods.ui.canvas
+         menu.style.height = mods.ui.canvas
+         menu.style.overflow = 'auto'
+         var req = new XMLHttpRequest()
+         req.responseType = 'text'
+         req.onreadystatechange = function() {
+            if (req.readyState == XMLHttpRequest.DONE) {
+               var str = req.response
+               eval(str)
+               }
             }
-         var win = window.open('programs/index.html')
+         req.open('GET','modules/index.js'+'?rnd='+Math.random())
+         req.send()
          })
       //
-      // open local program
+      // open local module
       //
-      add_menu(div,'open local program',function(evt){
+      add_menu(div,'open local module',function(evt){
          document.body.removeChild(evt.target.parentNode)
-         var file = document.getElementById('prog_input')
+         mods.globals.menu = null
+         var file = document.getElementById('mod_input')
          file.value = null
          file.click()
          })
       //
-      // open remote program
+      // open remote module
       //
-      add_menu(div,'open remote program',function(evt){
+      add_menu(div,'open remote module',function(evt){
          document.body.removeChild(evt.target.parentNode)
+         mods.globals.menu = null
          alert('remotes not yet implemented')
          })
-      //
-      // save local program
-      //
-      add_menu(div,'save local program',function(evt){
-         document.body.removeChild(evt.target.parentNode)
-         save_program()
-         })
-      //
-      // save local page
-      //
-      add_menu(div,'save local page',function(evt){
-         document.body.removeChild(evt.target.parentNode)
-         save_page()
-         })
       document.body.appendChild(div)
       }
-   function modules(evt) {
+   //
+   // programs menu
+   //
+   function programs(evt) {
       evt.preventDefault()
       document.body.removeChild(evt.target.parentNode)
       var div = document.createElement('div')
       make_menu(div)
       //
-      // open server module
+      // open server program
       //
-      add_menu(div,'open server module',function(evt){
-         function module_label(label) {
+      add_menu(div,'open server program',function(evt){
+         function program_label(label) {
             var div = document.createElement('div')
             var i = document.createElement('i')
             i.appendChild(document.createTextNode(label))
@@ -159,7 +183,7 @@ document.addEventListener('contextmenu',function(evt){
             div.appendChild(document.createElement('br'))
             menu.appendChild(div)
             }
-         function module_menu(label,module) {
+         function program_menu(label,program) {
             var div = document.createElement('div')
             div.appendChild(
                document.createTextNode('\u00A0\u00A0\u00A0'+label))
@@ -168,10 +192,17 @@ document.addEventListener('contextmenu',function(evt){
             div.addEventListener('mouseout',function(evt){
                evt.target.style.fontWeight = 'normal'})
             div.addEventListener('mousedown',function(evt){
+               if (location.port == 80)
+                  var uri = 'http://'+location.hostname
+                     +'?program='+program
+               else
+                  var uri = 'http://'+location.hostname+':'
+                     +location.port+'?program='+program
+               set_prompt('<a href='+uri+'>program link</a>')
+               prog_message_handler(program)
                document.body.removeChild(evt.target.parentNode)
-               mod_message_handler(module,
-               evt.clientY+document.body.scrollTop,
-               evt.clientX+document.body.scrollLeft)})
+               mods.globals.menu = null
+               })
             div.appendChild(document.createElement('br'))
             menu.appendChild(div)
             }
@@ -190,35 +221,61 @@ document.addEventListener('contextmenu',function(evt){
                eval(str)
                }
             }
-         req.open('GET','modules/index.js'+'?rnd='+Math.random())
+         req.open('GET','programs/index.js'+'?rnd='+Math.random())
          req.send()
          })
       //
-      // open local module
+      // open local program
       //
-      add_menu(div,'open local module',function(evt){
+      add_menu(div,'open local program',function(evt){
          document.body.removeChild(evt.target.parentNode)
-         var file = document.getElementById('mod_input')
+         mods.globals.menu = null
+         var file = document.getElementById('prog_input')
          file.value = null
          file.click()
          })
       //
-      // open remote module
+      // open remote program
       //
-      add_menu(div,'open remote module',function(evt){
+      add_menu(div,'open remote program',function(evt){
          document.body.removeChild(evt.target.parentNode)
-         alert('remotes not yet implemented')
+         mods.globals.menu = null
+         set_prompt('remotes not yet implemented')
+         })
+      //
+      // save local program
+      //
+      add_menu(div,'save local program',function(evt){
+         document.body.removeChild(evt.target.parentNode)
+         mods.globals.menu = null
+         save_program()
+         })
+      //
+      // save local page
+      //
+      add_menu(div,'save local page',function(evt){
+         document.body.removeChild(evt.target.parentNode)
+         mods.globals.menu = null
+         save_page()
          })
       document.body.appendChild(div)
       }
+   //
+   // edit menu
+   //
    function edit(evt) {
       evt.preventDefault()
       document.body.removeChild(evt.target.parentNode)
+      mods.globals.menu = null
       set_prompt('editing not yet implemented')
       }
+   //
+   // options menu
+   //
    function options(evt) {
       evt.preventDefault()
       document.body.removeChild(evt.target.parentNode)
+      mods.globals.menu = null
       var div = document.createElement('div')
       make_menu(div)
       //
@@ -226,6 +283,7 @@ document.addEventListener('contextmenu',function(evt){
       //
       add_menu(div,'list files',function(evt){
          document.body.removeChild(evt.target.parentNode)
+         mods.globals.menu = null
          var win = window.open('files.html')
          })
       document.body.appendChild(div)
@@ -234,199 +292,16 @@ document.addEventListener('contextmenu',function(evt){
       //
       add_menu(div,'save files',function(evt){
          document.body.removeChild(evt.target.parentNode)
+         mods.globals.menu = null
          var win = window.open('https://gitlab.cba.mit.edu/pub/mods')
          })
       document.body.appendChild(div)
       }
    })
 //
-// programs menu
-//
-document.body.appendChild(document.createTextNode(' '))
-var sel = document.createElement('select')
-   sel.style.padding = mods.ui.padding
-   sel.addEventListener(('change'),function(evt){ // click?
-      switch (evt.target.value) {
-         case 'open server program':
-            window.callback = function(msg) {
-               if (location.port == 80)
-                  var uri = encodeURI('http://'+location.hostname
-                     +'?program='+msg)
-               else
-                  var uri = encodeURI('http://'+location.hostname+':'
-                     +location.port+'?program='+msg)
-               set_prompt('<a href='+uri+'>program link</a>')
-               prog_message_handler(msg)
-               }
-            var win = window.open('programs/index.html')
-            break
-         case 'open local program':
-            var file = document.getElementById('prog_input')
-            file.value = null
-            file.click()
-            break
-         case 'open remote program':
-            alert('remotes not yet implemented')
-            break
-         case 'save local program':
-            save_program()
-            break
-         case 'save local page':
-            save_page()
-            break
-         }
-      evt.target.value = 'programs'
-      })
-   var opt = document.createElement('option')
-      opt.text = 'programs'
-      opt.value = opt.text
-      sel.add(opt)
-   var opt = document.createElement('option')
-      opt.text = 'open server program'
-      opt.value = opt.text
-      sel.add(opt)
-      optest(opt,'programs/index.html')
-   var opt = document.createElement('option')
-      opt.text = 'open local program'
-      opt.value = opt.text
-      sel.add(opt)
-   var opt = document.createElement('option')
-      opt.text = 'open remote program'
-      opt.value = opt.text
-      sel.add(opt)
-   var opt = document.createElement('option')
-      opt.text = 'save local program'
-      opt.value = opt.text
-      sel.add(opt)
-   var opt = document.createElement('option')
-      opt.text = 'save local page'
-      opt.value = opt.text
-      sel.add(opt)
-   document.body.appendChild(sel)
-mods.ui.header = 2*sel.clientHeight
-//
-// modules menu
-//
-document.body.appendChild(document.createTextNode(' '))
-var sel = document.createElement('select')
-   sel.style.padding = mods.ui.padding
-   sel.addEventListener(('change'),function(evt){ // click?
-      switch (evt.target.value) {
-         case 'add server module':
-            window.callback = function(msg) {
-               mod_message_handler(msg,
-                  1.5*mods.ui.header,3*mods.ui.header)
-               }
-            var win = window.open('modules/index.html')
-            break
-         case 'add local module':
-            var file = document.getElementById('mod_input')
-            file.value = null
-            file.click()
-            break
-         case 'add remote module':
-            alert('remotes not yet implemented')
-            break
-         }
-      evt.target.value = 'modules'
-      })
-   var opt = document.createElement('option')
-      opt.text = 'modules'
-      opt.value = 'modules'
-      sel.add(opt)
-   var opt = document.createElement('option')
-      opt.text = 'add server module'
-      opt.value = opt.text
-      sel.add(opt)
-      optest(opt,'modules/index.html')
-   var opt = document.createElement('option')
-      opt.text = 'add local module'
-      opt.value = opt.text
-      sel.add(opt)
-   var opt = document.createElement('option')
-      opt.text = 'add remote module'
-      opt.value = opt.text
-      sel.add(opt)
-   document.body.appendChild(sel)
-//
-// edit menu
-//
-document.body.appendChild(document.createTextNode(' '))
-document.body.appendChild(document.createTextNode(' '))
-var sel = document.createElement('select')
-   sel.style.padding = mods.ui.padding
-   sel.addEventListener(('change'),function(evt){ // click?
-      evt.target.value = 'edit'
-      })
-   var opt = document.createElement('option')
-      opt.text = 'edit'
-      opt.value = opt.text
-      sel.add(opt)
-   var opt = document.createElement('option')
-      opt.text = 'cut'
-      opt.value = opt.text
-      opt.disabled = true
-      sel.add(opt)
-   var opt = document.createElement('option')
-      opt.text = 'copy'
-      opt.value = opt.text
-      opt.disabled = true
-      sel.add(opt)
-   var opt = document.createElement('option')
-      opt.text = 'paste'
-      opt.value = opt.text
-      opt.disabled = true
-      sel.add(opt)
-   var opt = document.createElement('option')
-      opt.text = 'nest'
-      opt.value = opt.text
-      opt.disabled = true
-      sel.add(opt)
-   document.body.appendChild(sel)
-//
-// options menu
-//
-document.body.appendChild(document.createTextNode(' '))
-var sel = document.createElement('select')
-   sel.style.padding = mods.ui.padding
-   sel.addEventListener(('change'),function(evt){ // click
-      switch (evt.target.value) {
-         case 'list all files':
-            var win = window.open('files.html')
-            break
-         case 'save all files':
-            var win = window.open('https://gitlab.cba.mit.edu/pub/mods')
-            break
-         }
-      evt.target.value = 'options'
-      })
-   var opt = document.createElement('option')
-      opt.text = 'options'
-      opt.value = 'options'
-      sel.add(opt)
-   var opt = document.createElement('option')
-      opt.text = 'preferences'
-      opt.value = opt.text
-      opt.disabled = true
-      sel.add(opt)
-   var opt = document.createElement('option')
-      opt.text = 'list all files'
-      opt.value = opt.text
-      sel.add(opt)
-      optest(opt,'files.html')
-   var opt = document.createElement('option')
-      opt.text = 'save all files'
-      opt.value = opt.text
-      sel.add(opt)
-   var opt = document.createElement('option')
-      opt.text = 'about'
-      opt.value = opt.text
-      opt.disabled = true
-      sel.add(opt)
-   document.body.appendChild(sel)
-//
 // prompt
 //
+mods.ui.header = 50
 document.body.appendChild(document.createTextNode(' '))
 var span = document.createElement('span')
    span.setAttribute('id','logo')
@@ -516,7 +391,7 @@ function logo(size) {
    logo.appendChild(new_circ)
    return logo
    }
-set_prompt('right click for menu')
+set_prompt('right click/long press for menu')
 //
 // SVG canvas for drawing
 //
diff --git a/js/modules.js b/js/modules.js
index 987e84a..9e29272 100644
--- a/js/modules.js
+++ b/js/modules.js
@@ -1,9 +1,9 @@
 //
-// load.js
-//    node js/load.js subdir
+// modules.js
+//    node js/modules.js subdir
 //
 // Neil Gershenfeld 
-// (c) Massachusetts Institute of Technology 2016
+// (c) Massachusetts Institute of Technology 2018
 // 
 // This work may be reproduced, modified, distributed, performed, and 
 // displayed for any purpose, but must acknowledge the mods
diff --git a/js/load.js b/js/programs.js
similarity index 64%
rename from js/load.js
rename to js/programs.js
index b46732f..f553d96 100644
--- a/js/load.js
+++ b/js/programs.js
@@ -1,9 +1,9 @@
 //
-// load.js
-//    node js/load.js subdir
+// programs.js
+//    node js/programs.js subdir
 //
 // Neil Gershenfeld 
-// (c) Massachusetts Institute of Technology 2016
+// (c) Massachusetts Institute of Technology 2018
 // 
 // This work may be reproduced, modified, distributed, performed, and 
 // displayed for any purpose, but must acknowledge the mods
@@ -17,18 +17,8 @@ var fs = require('fs')
 var subdir = process.argv[2]
 var root = './'+subdir
 
-str = '<html>\n\
-   <head><meta charset="utf-8"></head>\n\
-   <body>\n\
-   <body link="black" alink="black" vlink="black">\n\
-   <b>file to open?</b><br><br>\n\
-   <script>\n\
-   function handler(uri) {\n\
-      window.opener.callback(uri)\n\
-      window.close()\n\
-      }\n\
-   </script>\n\
-   '
+var str = ''
+
 list_files(root)
 console.log(str)
 
@@ -47,14 +37,12 @@ function list_files(path) {
          if (match == null)
             var prefix = ''
          else {
-            var prefix = Array(match.length).join('&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;')
+            var prefix = Array(match.length).join('\u00A0\u00A0\u00A0')
             }
-         str += prefix
-         str += '<a href="javascript:handler('
-         str += "'"
+         str += "program_menu('"
+         str += prefix+file+"','"
          str += encodeURI(url)
-         str += "'"
-         str += ')">'+file+'</a><br>\n'
+         str += "')\n"
          }
       else if (stats.isDirectory() == true) {
          if (relpath == '')
@@ -65,9 +53,9 @@ function list_files(path) {
          if (match == null)
             var prefix = ''
          else {
-            var prefix = Array(match.length).join('&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;')
+            var prefix = Array(match.length).join('\u00A0\u00A0\u00A0')
             }
-         str += '<i>'+prefix+file+'</i><br>\n'
+         str += "program_label('"+prefix+file+"')\n"
          list_files(path+'/'+file)
          }
       else
diff --git a/make b/make
index 6e234b8..8cd9bef 100755
--- a/make
+++ b/make
@@ -1,4 +1,4 @@
 #!/bin/bash
 node js/files.js > files.html
-node js/load.js programs > programs/index.html
+node js/programs.js programs > programs/index.js
 node js/modules.js modules > modules/index.js
diff --git a/modules/index.html b/modules/index.html
deleted file mode 100644
index 86f7e9b..0000000
--- a/modules/index.html
+++ /dev/null
@@ -1,142 +0,0 @@
-   <i>apa</i><br>
-&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a onclick=set_prompt(123)>click</a><br>
-&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="javascript:window.handler('modules/apa/node')">node</a><br>
-<i>character</i><br>
-&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="javascript:handler('modules/character/convert')">convert</a><br>
-&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="javascript:handler('modules/character/in%20out')">in out</a><br>
-&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="javascript:handler('modules/character/parse')">parse</a><br>
-&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="javascript:handler('modules/character/variable')">variable</a><br>
-<i>connect</i><br>
-<i>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;svg</i><br>
-&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="javascript:handler('modules/connect/svg/ExtractFaces')">ExtractFaces</a><br>
-&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="javascript:handler('modules/connect/svg/PCB')">PCB</a><br>
-&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="javascript:handler('modules/connect/svg/SelectFace')">SelectFace</a><br>
-<i>control</i><br>
-&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="javascript:handler('modules/control/slider')">slider</a><br>
-<i>convert</i><br>
-<i>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;rgba</i><br>
-&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="javascript:handler('modules/convert/rgba/jpg')">jpg</a><br>
-&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="javascript:handler('modules/convert/rgba/png')">png</a><br>
-<i>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;svg</i><br>
-&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="javascript:handler('modules/convert/svg/array')">array</a><br>
-&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="javascript:handler('modules/convert/svg/image')">image</a><br>
-<i>event</i><br>
-&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="javascript:handler('modules/event/delay')">delay</a><br>
-&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="javascript:handler('modules/event/generate')">generate</a><br>
-&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="javascript:handler('modules/event/pause')">pause</a><br>
-<i>file</i><br>
-&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="javascript:handler('modules/file/save')">save</a><br>
-<i>frep</i><br>
-<i>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;operators</i><br>
-&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="javascript:handler('modules/frep/operators/add')">add</a><br>
-&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="javascript:handler('modules/frep/operators/intersect')">intersect</a><br>
-&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="javascript:handler('modules/frep/operators/morph')">morph</a><br>
-&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="javascript:handler('modules/frep/operators/subtract')">subtract</a><br>
-<i>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;shapes</i><br>
-<i>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2D</i><br>
-&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="javascript:handler('modules/frep/shapes/2D/circle')">circle</a><br>
-&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="javascript:handler('modules/frep/shapes/2D/function')">function</a><br>
-&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="javascript:handler('modules/frep/shapes/2D/rectangle%20corners')">rectangle corners</a><br>
-&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="javascript:handler('modules/frep/shapes/2D/right%20triangle')">right triangle</a><br>
-<i>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;3D</i><br>
-&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="javascript:handler('modules/frep/shapes/3D/cone')">cone</a><br>
-&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="javascript:handler('modules/frep/shapes/3D/cube%20corners')">cube corners</a><br>
-&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="javascript:handler('modules/frep/shapes/3D/cylinder')">cylinder</a><br>
-&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="javascript:handler('modules/frep/shapes/3D/function')">function</a><br>
-&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="javascript:handler('modules/frep/shapes/3D/sphere')">sphere</a><br>
-<i>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;transforms</i><br>
-&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="javascript:handler('modules/frep/transforms/extrude')">extrude</a><br>
-&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="javascript:handler('modules/frep/transforms/reflect')">reflect</a><br>
-&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="javascript:handler('modules/frep/transforms/repeat')">repeat</a><br>
-<i>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;view</i><br>
-&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="javascript:handler('modules/frep/view/render%20(CPU)')">render (CPU)</a><br>
-&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="javascript:handler('modules/frep/view/render%20(GPU)')">render (GPU)</a><br>
-&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="javascript:handler('modules/frep/view/slice')">slice</a><br>
-<i>graph</i><br>
-&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="javascript:handler('modules/graph/bar')">bar</a><br>
-<i>image</i><br>
-&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="javascript:handler('modules/image/boundingbox')">boundingbox</a><br>
-&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="javascript:handler('modules/image/color%20separation')">color separation</a><br>
-&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="javascript:handler('modules/image/distance%20transform')">distance transform</a><br>
-&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="javascript:handler('modules/image/dogbone')">dogbone</a><br>
-&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="javascript:handler('modules/image/edge%20detect')">edge detect</a><br>
-&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="javascript:handler('modules/image/function')">function</a><br>
-&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="javascript:handler('modules/image/motion%20detect')">motion detect</a><br>
-&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="javascript:handler('modules/image/offset')">offset</a><br>
-&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="javascript:handler('modules/image/orient%20edges')">orient edges</a><br>
-&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="javascript:handler('modules/image/palette')">palette</a><br>
-&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="javascript:handler('modules/image/raster%20mask')">raster mask</a><br>
-&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="javascript:handler('modules/image/size')">size</a><br>
-&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="javascript:handler('modules/image/threshold')">threshold</a><br>
-&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="javascript:handler('modules/image/vector%20mask')">vector mask</a><br>
-&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="javascript:handler('modules/image/vectorize')">vectorize</a><br>
-<i>input</i><br>
-&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="javascript:handler('modules/input/video')">video</a><br>
-<i>iterate</i><br>
-&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="javascript:handler('modules/iterate/step')">step</a><br>
-&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="javascript:handler('modules/iterate/step%20and%20delay')">step and delay</a><br>
-<i>library</i><br>
-&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="javascript:handler('modules/library/globals')">globals</a><br>
-&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="javascript:handler('modules/library/library')">library</a><br>
-&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="javascript:handler('modules/library/three.js')">three.js</a><br>
-<i>math</i><br>
-&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="javascript:handler('modules/math/benchmark')">benchmark</a><br>
-&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="javascript:handler('modules/math/dyadic')">dyadic</a><br>
-&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="javascript:handler('modules/math/glsl_benchmark')">glsl_benchmark</a><br>
-&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="javascript:handler('modules/math/parallel')">parallel</a><br>
-&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="javascript:handler('modules/math/scalar')">scalar</a><br>
-<i>mesh</i><br>
-&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="javascript:handler('modules/mesh/slice')">slice</a><br>
-<i>module</i><br>
-&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="javascript:handler('modules/module/create')">create</a><br>
-&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="javascript:handler('modules/module/delete')">delete</a><br>
-&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="javascript:handler('modules/module/move')">move</a><br>
-<i>object</i><br>
-&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="javascript:handler('modules/object/construct')">construct</a><br>
-&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="javascript:handler('modules/object/download')">download</a><br>
-&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="javascript:handler('modules/object/set')">set</a><br>
-&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="javascript:handler('modules/object/string')">string</a><br>
-&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="javascript:handler('modules/object/view')">view</a><br>
-<i>processes</i><br>
-<i>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;cut</i><br>
-&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="javascript:handler('modules/processes/cut/raster')">raster</a><br>
-<i>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;mill</i><br>
-<i>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;raster</i><br>
-&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="javascript:handler('modules/processes/mill/raster/2D')">2D</a><br>
-<i>read</i><br>
-&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="javascript:handler('modules/read/png')">png</a><br>
-&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="javascript:handler('modules/read/stl')">stl</a><br>
-&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="javascript:handler('modules/read/svg')">svg</a><br>
-&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="javascript:handler('modules/read/text')">text</a><br>
-<i>socket</i><br>
-<i>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;server</i><br>
-&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="javascript:handler('modules/socket/server/device')">device</a><br>
-&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="javascript:handler('modules/socket/server/echo')">echo</a><br>
-&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="javascript:handler('modules/socket/server/print')">print</a><br>
-&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="javascript:handler('modules/socket/server/serial')">serial</a><br>
-&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="javascript:handler('modules/socket/server/udp')">udp</a><br>
-&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="javascript:handler('modules/socket/websocket')">websocket</a><br>
-<i>string</i><br>
-&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="javascript:handler('modules/string/format')">format</a><br>
-&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="javascript:handler('modules/string/variables')">variables</a><br>
-<i>toolpath</i><br>
-<i>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;formats</i><br>
-&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="javascript:handler('modules/toolpath/formats/dxf')">dxf</a><br>
-&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="javascript:handler('modules/toolpath/formats/g-code')">g-code</a><br>
-<i>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;machines</i><br>
-<i>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Roland</i><br>
-<i>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;laser cutter</i><br>
-&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="javascript:handler('modules/toolpath/machines/Roland/laser%20cutter/Epilog')">Epilog</a><br>
-<i>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;milling</i><br>
-&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="javascript:handler('modules/toolpath/machines/Roland/milling/MDX-20')">MDX-20</a><br>
-&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="javascript:handler('modules/toolpath/machines/Roland/milling/SRM-20')">SRM-20</a><br>
-<i>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;vinyl cutter</i><br>
-&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="javascript:handler('modules/toolpath/machines/Roland/vinyl%20cutter/GX-24')">GX-24</a><br>
-&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="javascript:handler('modules/toolpath/machines/ShopBot')">ShopBot</a><br>
-<i>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;laser cutter</i><br>
-&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="javascript:handler('modules/toolpath/machines/laser%20cutter/Epilog')">Epilog</a><br>
-&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="javascript:handler('modules/toolpath/view')">view</a><br>
-<i>ui</i><br>
-&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="javascript:handler('modules/ui/button')">button</a><br>
-&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="javascript:handler('modules/ui/text')">text</a><br>
-
diff --git a/programs/index.html b/programs/index.html
deleted file mode 100644
index 6c17e1c..0000000
--- a/programs/index.html
+++ /dev/null
@@ -1,54 +0,0 @@
-<html>
-   <head><meta charset="utf-8"></head>
-   <body>
-   <body link="black" alink="black" vlink="black">
-   <b>file to open?</b><br><br>
-   <script>
-   function handler(uri) {
-      window.opener.callback(uri)
-      window.close()
-      }
-   </script>
-   <i>image</i><br>
-&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="javascript:handler('programs/image/function')">function</a><br>
-&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="javascript:handler('programs/image/motion%20detect')">motion detect</a><br>
-&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="javascript:handler('programs/image/palette%20mask%20raster')">palette mask raster</a><br>
-&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="javascript:handler('programs/image/palette%20mask%20vector')">palette mask vector</a><br>
-<i>iterate</i><br>
-&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="javascript:handler('programs/iterate/z%20theta%20scan')">z theta scan</a><br>
-<i>machines</i><br>
-<i>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Epilog</i><br>
-&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="javascript:handler('programs/machines/Epilog/cut%20png')">cut png</a><br>
-&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="javascript:handler('programs/machines/Epilog/cut%20svg')">cut svg</a><br>
-&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="javascript:handler('programs/machines/Epilog/cut%20svg%20connect')">cut svg connect</a><br>
-<i>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;G-code</i><br>
-&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="javascript:handler('programs/machines/G-code/mill%202D%20png')">mill 2D png</a><br>
-<i>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Roland</i><br>
-<i>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;mill</i><br>
-<i>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;MDX-20</i><br>
-&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="javascript:handler('programs/machines/Roland/mill/MDX-20/PCB')">PCB</a><br>
-<i>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;SRM-20</i><br>
-&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="javascript:handler('programs/machines/Roland/mill/SRM-20/PCB')">PCB</a><br>
-&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="javascript:handler('programs/machines/Roland/mill/SRM-20/PCB%20svg%20connect')">PCB svg connect</a><br>
-<i>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;vinyl cutter</i><br>
-<i>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;GX-24</i><br>
-&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="javascript:handler('programs/machines/Roland/vinyl%20cutter/GX-24/cut%20png')">cut png</a><br>
-&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="javascript:handler('programs/machines/Roland/vinyl%20cutter/GX-24/cut%20svg')">cut svg</a><br>
-&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="javascript:handler('programs/machines/Roland/vinyl%20cutter/GX-24/cut%20svg%20connect')">cut svg connect</a><br>
-<i>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ShopBot</i><br>
-&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="javascript:handler('programs/machines/ShopBot/mill%202D%20png')">mill 2D png</a><br>
-&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="javascript:handler('programs/machines/ShopBot/mill%202D%20svg')">mill 2D svg</a><br>
-&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="javascript:handler('programs/machines/ShopBot/mill%202D%20svg%20connect')">mill 2D svg connect</a><br>
-<i>math</i><br>
-&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="javascript:handler('programs/math/benchmark')">benchmark</a><br>
-&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="javascript:handler('programs/math/expressions')">expressions</a><br>
-<i>processes</i><br>
-<i>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;cut</i><br>
-<i>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;raster</i><br>
-&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="javascript:handler('programs/processes/cut/raster/video')">video</a><br>
-<i>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;mill</i><br>
-<i>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;raster</i><br>
-&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="javascript:handler('programs/processes/mill/raster/2D')">2D</a><br>
-<i>variable</i><br>
-&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="javascript:handler('programs/variable/text%20variables')">text variables</a><br>
-
diff --git a/programs/index.js b/programs/index.js
new file mode 100644
index 0000000..a233b4d
--- /dev/null
+++ b/programs/index.js
@@ -0,0 +1,43 @@
+program_label('image')
+program_menu('   function','programs/image/function')
+program_menu('   motion detect','programs/image/motion%20detect')
+program_menu('   palette mask raster','programs/image/palette%20mask%20raster')
+program_menu('   palette mask vector','programs/image/palette%20mask%20vector')
+program_label('iterate')
+program_menu('   z theta scan','programs/iterate/z%20theta%20scan')
+program_label('machines')
+program_label('   Epilog')
+program_menu('      cut png','programs/machines/Epilog/cut%20png')
+program_menu('      cut svg','programs/machines/Epilog/cut%20svg')
+program_menu('      cut svg connect','programs/machines/Epilog/cut%20svg%20connect')
+program_label('   G-code')
+program_menu('      mill 2D png','programs/machines/G-code/mill%202D%20png')
+program_label('   Roland')
+program_label('      mill')
+program_label('         MDX-20')
+program_menu('            PCB','programs/machines/Roland/mill/MDX-20/PCB')
+program_label('         SRM-20')
+program_menu('            PCB','programs/machines/Roland/mill/SRM-20/PCB')
+program_menu('            PCB svg connect','programs/machines/Roland/mill/SRM-20/PCB%20svg%20connect')
+program_label('      vinyl cutter')
+program_label('         GX-24')
+program_menu('            cut png','programs/machines/Roland/vinyl%20cutter/GX-24/cut%20png')
+program_menu('            cut svg','programs/machines/Roland/vinyl%20cutter/GX-24/cut%20svg')
+program_menu('            cut svg connect','programs/machines/Roland/vinyl%20cutter/GX-24/cut%20svg%20connect')
+program_label('   ShopBot')
+program_menu('      mill 2D png','programs/machines/ShopBot/mill%202D%20png')
+program_menu('      mill 2D svg','programs/machines/ShopBot/mill%202D%20svg')
+program_menu('      mill 2D svg connect','programs/machines/ShopBot/mill%202D%20svg%20connect')
+program_label('math')
+program_menu('   benchmark','programs/math/benchmark')
+program_menu('   expressions','programs/math/expressions')
+program_label('processes')
+program_label('   cut')
+program_label('      raster')
+program_menu('         video','programs/processes/cut/raster/video')
+program_label('   mill')
+program_label('      raster')
+program_menu('         2D','programs/processes/mill/raster/2D')
+program_label('variable')
+program_menu('   text variables','programs/variable/text%20variables')
+
-- 
GitLab