List of samples now include note about each sample's main control

:Class index : MiPageSample

    :SECTION GLOBALS ⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝

    :Field TYPES←'Simple' 'Advanced' 'Dyalog' ⍝ Types of samples (if not App)
    :Field GROUPS ⍝ Names of groups of elements
    :Field REFS        ⍝ ... their refs
    :Field APPS        ⍝ List of all apps
    :Field SAMPLES     ⍝ List of all samples (well, groups and controls really)
    :Field INFOLONG    ⍝ Long desc of each control
    :Field INFOSHORT   ⍝ Short (1-3 words) desc of each control
    :Field NAMESLONG   ⍝ Elements that have long info
    :Field NAMESSHORT  ⍝ Elements that have short info
    :Field SEARCH←''   ⍝ Last-search cache
    :Field CONTROLS←'' ⍝ All control names
    :Field APPDESCS    ⍝ Sample Apps' Description::
    :Field APPCTRLS    ⍝ Sample Apps' Control:: (i.e. the title)
    :Field CORE        ⍝ Core subset of controls

    :ENDSECTION

    :SECTION UTILITIES ⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝

    Q←'"'∘,,∘'"'  ⍝ Surround with quotes, a.k.a. APL dragon

    D←{(×≢⍵)/' – ',⍵} ⍝ Prepend a dash if non-empty

    P←{1⌽(×≢#.HTMLInput.dtlb ⍵)/') (',⍵} ⍝ Surround with parens if non-empty

    Words←{(1↓¨(' '∘=⊂⊢)' ',⍵)~⊂''} ⍝ Split at spaces

    SpaceToDir←{'Examples/',1↓⍵} ⍝ Convert Space name to Folder name

    Frame←{'.framed' ('src=',Q⍵,'?NoWrapper=1') New _.iframe}

    NewWinA←{('target' '_blank')('href=',Q⊃⌽⍵) New _.a (⊃⍵)} ⍝ New link that opens in a new window/tab

    NewDiv←{⍵ New _.div} ⍝ Make a div

    Horz←{⍺←⊢ ⋄ r⊣(r←⍺ New _.StackPanel ⍵).Horizontal←1} ⍝ Horizontal StackPanel

    Type←{P(1⍳⍨∨/¨TYPES⍷¨⊂⍺)⊃(TYPES,¨⊂' sample for ',⍵),⊂'Sample app'} ⍝ Simple/Advanced/App?

    Dread←{0::,⊂'[file read failed]' ⋄ #.UnicodeFile.ReadNestedText #.Boot.AppRoot,⍵,'.dyalog'} ⍝ Read dyalog file

    Dlist←{0::⍬ ⋄ ¯7↓¨6⊃#.Files.DirX #.Boot.AppRoot,⍵,'/*.dyalog'} ⍝ List dyalog files

    NodeID←{⍺←⊢ ⋄ ('#node',⊃⍵)∘,¨⍕¨⍺+⍳⍴⊃⌽⍵} ⍝ Generate node ids (⍵='L' items) optional (⍺=offset)

    In←{∨/¨⊃⍷¨/{⎕SE.Dyalog.Utils.lcase¨eis ⍵}¨⍺ ⍵} ⍝ Case-insensitive find

    Names←{1↓¨⍵↑¨⍨¯1+⍵⍳¨':'} ⍝ Extract section names from set of several 'name:: description'

    AddShortInfo←{⍵,¨P¨(4+⍴¨⍵)↓¨(INFOSHORT,⊂'')[NAMESSHORT⍳⍵]} ⍝ ⍵ (ShortDesc)

    AddLongInfo←{(New¨_.strong,¨⍵),¨D¨{⍵,'[no info available]'/⍨0∊⍴⍵}¨(4+⍴¨⍵)↓¨(INFOLONG,⊂'')[NAMESLONG⍳⍵]} ⍝ ⍵ - LongDesc

      Section←{ ⍝ extract section ⍺:: from code ⍵
          regex←'^\s*⍝\s*',⍺,':(:.*?)$((\R^⍝(?!\s*\w+::).*?$)*)'
          opts←('Mode' 'M')('DotAll' 1)
          res←(regex ⎕S'\1\2'⍠opts)⍵
          (1↓⊃res)~'⍝'
      }

      JSsafe←{ ⍝ Make text JS safe by escaping problematic chars
          fr←'''' '"' '\\' '\n' '\r',⎕UCS 9 8 12               ⍝ ' " \ NL CR TB BS FF
          to←'\\''' '\\"' '\\\\' '\\n' '\\r' '\\t' '\\b' '\\f' ⍝ problematic in regex too :-)
          Q(fr ⎕R to⍠'Mode' 'D')⍵ ⍝ Replace problematic chars and sourround with quotes
      }

    ∇ js←APLtoJS vtv;act;data
      :Access public shared
     ⍝ Convert vectors of pseudo JS (generated by Replace, Execute, et al.) to proper JS

      js←''
      :For act data :In eis vtv
          data←JSsafe⊃⌽eis data ⍝ the last or only one
          :Select ⊃eis act      ⍝ the first or only one
          :Case 'assign'
              js,←(⊃⌽act),' = ',data,';' ⍝ variable = "data"
          :Case 'execute'
              js,←'eval(',data,');' ⍝ eval("data")
          :Case 'replace'
              js,←'$("',(⊃⌽act),'").html(',data,');' ⍝ $("#myid").html("data")
          :Else
              js,←'$("',(⊃⌽act),'").',(⊃act),'(',data,');' ⍝ $("#myid").append("data")
          :EndSelect
      :EndFor
    ∇

    :ENDSECTION

    :SECTION MAIN ⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝

    ∇ Compose;left;mid;right;sp;style
      :Access public

     ⍝ Initialize globals
      SAMPLES←0 3⍴⍬
      GROUPS←'Dyalog Controls' 'Base HTML' 'SyncFusion' 'JQueryUI'
      REFS←_DC _html _SF _JQ
      INFOSHORT←'⍝',¨Dread'Examples/Data/infoShort'
      INFOLONG←'⍝',¨Dread'Examples/Data/infoLong'
      NAMESSHORT←Names INFOSHORT
      NAMESLONG←Names INFOLONG

      '#title'Add _.title'MS3: About' ⍝ After the : will be updated

      Add _.StyleSheet'/Styles/homepage.css'
      style←''
      style,←'#leftBar {background-color:inherit;margin-right:6px;} '
      style,←'li:hover {background-color:highlight;font-wight:normal;}'
      style,←'.menu {padding-left,padding-right:0;} '
      style,←'.cat {font-size:12pt;cursor:pointer;}'
      style,←'.cat:hover {font-size:12pt;background-color:highlight;}'
      style,←'.submenu {max-height:75px;overflow-y:scroll;background-color:white;border:2px groove threedface;} '
      style,←'.menuitem {margin-bottom,margin-left:0px;padding-left:2px;cursor:pointer;} '
      style,←'.menuitem:hover {background-color:highlight;} '
      style,←'.framed {width:730px;max-height:600px;min-height:400px;border:2px inset;overflow-y:auto;background-color:white;} '
      style,←'.listitem {margin:0px;padding:4px;cursor:pointer;} '
      style,←'.listitem:hover {background-color:highlight;} '
      style,←'.samplesource {overflow-x:auto;width:730;background-color:#e5e5cc;border:2px inset;} '
      Add _.style style

      (left mid)←NewDiv¨'#leftBar' '#midBar' ⍝ Create panes
      sp←Add'mainSP'Horz left mid

      sp.Items[1].style←⊂'width: 200px; max-height: 450px;'
      sp.Items[2].style←⊂'margin: 5px;'
      sp.style←'height: 450px;background-color:inherit;'

      PopulateLeft left
      PopulateMid mid
    ∇

    ∇ PopulateLeft thediv;class;depths;group;items;names;ref;samples;vp;search;style;menu;i;fs;ac;text;stuff;html;SF;treeall;treecore

      stuff←''
      tree←0 3⍴0 '' ''

         ⍝ SEARCH FIELD ⍝⍝⍝
      (stuff,←New _.EditField'str').On'change' 'OnSearch'('str' 'val')
      (stuff,←'#case' '.menu'New _.button'Search').On'click' 'OnSearch'

      thediv.Add Horz stuff
      thediv.Add _.hr

      ⍝ SAMPLE APPS ⍝⍝⍝
      APPS←(Dlist'Examples/Apps')~⊂'index'
      APPDESCS←(⊂'Description')Section¨Dread¨'Examples/Apps/'∘,¨APPS
      APPCTRLS←(⊂'Control')Section¨Dread¨'Examples/Apps/'∘,¨APPS
      ('.cat'thediv.Add _.p'Sample Apps').On'click' 'OnAppHeader'
      APPS{tree⍪←1 ⍵('nodeA',⍺)}¨APPCTRLS
      (thediv.Add _.ejTreeView tree).On'nodeSelect' 'OnApp'('node' 'eval' 'argument.id')

      thediv.Add _.hr

      ('.cat'thediv.Add _.p'Controls').On'click' 'OnCtrlHeader'
      CORE←⎕SE.SALT.Load #.Boot.AppRoot,'Examples/Data/core -noname'
      treecore←1 3⍴1 'Core' 'node0C'
      treeall←1 3⍴1 'All' 'node0A'

      :For group class :InEach GROUPS(3↓¨⍕¨REFS) ⍝ Remove leading #._
          names←⎕THIS.⎕SE.SALT.Load #.Boot.AppRoot,'Examples/Data/tree',class,' -noname'
          CONTROLS,←⊂names[;2]/⍨names[;1]=⌈/names[;1]
          ⍎class,'←names'

          :If ∨/names[;2]∊CORE
              treecore⍪←2(group,P'_',class)('node__',class)
              treecore⍪←3,0 1↓names⌿⍨names[;2]∊CORE
          :EndIf

          treeall⍪←2(group,P'_',class)('node_',class)
          treeall⍪←⍎class

      :EndFor

      ('#tree'thediv.Add _.ejTreeView,⊂treecore⍪treeall).On'nodeSelect' 'OnTree'('node' 'eval' 'argument.id')
    ∇

    ∇ PopulateMid mid;url;code;frame;mya

     ⍝ Read framed pages
      url←'Examples/Apps/About'
      code←Dread url
     ⍝ Create and fill placeholder for title header
      mya←('#SampleTitle'mid.Add _.h2).Add NewWinA('Control'Section code)url
     ⍝ Create and fill placeholder for description line
      '#SampleDesc'mid.Add _.p('Description'Section code)
     ⍝ Create and fill placeholder for embedded page
      frame←mid.Add NewDiv'#SampleFrame'
      frame.Add Frame url
      '#SampleSource' '.samplesource'mid.Add _.div,⊂'x-small;border:none'#.HTMLInput.APLToHTMLColour code
    ∇

    :ENDSECTION

    :SECTION CALLBACKS ⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝

    ∇ r←OnTree;node;descs;out;items;spacectrl;control;spacedir;files;file;i;url;code;ctrlsec;desc;item;title;currctrls;core
      :Access Public
      node←4↓(1+'tree'≡_what)⊃_what(Get'node') ⍝ get list-item or node
      :If '0'∊node ⍝ 'All'
          r←''
      :ElseIf '_'=⊃node ⍝ Class or core subset thereof
          core←'_'=2⊃node
          node↓⍨←core ⍝ Remove additional _ from '__html' if core-only
          i←REFS⍳⍎node
          currctrls←(CORE∘∩⍣core)i⊃CONTROLS ⍝ Filter if core-only
          out←NewDiv'.framed'
          descs←AddLongInfo currctrls
          items←(currctrls,⍨¨⊂'list',1⌽node)out.Add¨_.p,¨descs ⍝ make IDs like 'DC_Button'
          items.Set⊂'.listitem'
          items.On⊂'click' 'OnTree'
          r←GenJS node('Members of ',node,P i⊃GROUPS)(' controls',⍨⍕⍴currctrls)out''
      :ElseIf 2≤+/'_'=node ⍝ Category
          r←''
      :Else ⍝ Control
          spacectrl←'_',('_'⎕R'.')node
          control←node↓⍨node⍳'_'
          spacedir←'Examples/',(¯1+node⍳'_')↑node
          files←Dlist spacedir
          out←NewDiv'.framed'
          CURRFILES←''
          CURRDESCS←''
          i←0
          :For file :In files
              url←spacedir,'/',file
              code←Dread url
              ctrlsec←Words'Control'Section code
              :If (⊂spacectrl)∊ctrlsec ⍝ Extract space-separated words
                  i+←1
                  CURRFILES,←⊂url
                  desc←⊂'Description'Section code
                  desc←∊desc,file Type⊃ctrlsec
                  CURRDESCS,←⊂desc
                  item←('#nodeS',⍕i)'.listitem'out.Add _.p desc
                  item.On'click' 'OnSample'
              :EndIf
          :EndFor
          :If ~×i
              '.listitem'out.Add _.p,⊂'[no samples using ',spacectrl,']'
          :EndIf
          title←1↓control Section INFOSHORT
          title←'Samples using ',spacectrl,P title
          desc←control Section INFOLONG
          r←GenJS spacectrl title desc out''
      :EndIf
    ∇

    ∇ r←OnAppHeader;descs;out;items
      :Access Public
     ⍝ Gets called upon clicking the "Sample Apps" header
      descs←APPCTRLS,¨' – '∘,¨APPDESCS
      out←NewDiv'.framed'
      items←APPS{('nodeB',⍺)out.Add _.p ⍵}¨descs
      items.Set⊂'.listitem'
      items.On⊂'click' 'OnApp'
      r←GenJS'Apps' 'Small applications to showcase MiServer 3.0 functionality'(' apps',⍨≢APPS)out''
    ∇

    ∇ r←OnCtrlHeader;descs;out;items
      :Access Public
     ⍝ Gets called upon clicking the "Sample Apps" header
      r←GenJS'Controls' 'Something about controls' 'Controls' 'list of all controls with long descs' ''
    ∇

    ∇ r←OnApp
      :Access Public
     ⍝ Gets called upon selection in Sample Apps tree
      r←GenJS Update'Examples/Apps/',5↓Get'node'
    ∇

    ∇ r←OnSample
      :Access Public
      r←GenJS Update CURRFILES⊃⍨⍎5↓_what ⍝ id="nodeS23"
    ∇

    ∇ r←OnSearch;time;str;terms;out;i;dir;files;file;url;code;ctrlsec;desc;item
      :Access Public
      time←0.001×3⊃⎕AI ⍝ prepare search statistics
      :If ×≢str←Get'str'  ⍝ get search string
      ⍝⍝⍝ Controls
          terms←INFOSHORT/⍨str In INFOSHORT
          terms,←terms~⍨INFOLONG/⍨str In INFOLONG
          terms←'.',¨∪Names terms
      ⍝⍝⍝ Samples
          out←NewDiv'.framed'
          CURRFILES←''
          CURRDESCS←''
          i←0
          :For dir :In 'Apps' 'html' 'HTMLplus' 'DC' 'JQ' 'SF'
              files←Dlist'Examples/',dir
              :For file :In files~⊂'index'
                  url←'Examples/',dir,'/',file
                  code←Dread url
                  ctrlsec←'Control'Section code
                  desc←⊂'Description'Section code
                  :If ∨/str In desc
                  :OrIf ∨/∊(terms,⊂str)In¨⊂ctrlsec ⍝ always case sensitive
                      i+←1
                      CURRFILES,←⊂url
                      desc←∊desc,file Type⊃Words ctrlsec
                      CURRDESCS,←⊂desc
                      item←('#nodeS',⍕i)'.listitem'out.Add _.p desc
                      item.On'click' 'OnSample'
                  :EndIf
              :EndFor
          :EndFor
          desc←New _.span'Showing results for '
          desc.Add _.em str
          desc.Add(×≢terms)/D'including relevant element','s'/⍨1≠≢terms
          desc.Add _.em(⍕1↓¨terms)
          SEARCH←(Q str)''desc out''
      :EndIf
      time-⍨←0.001×3⊃⎕AI
      (2⊃SEARCH)←(⍕≢CURRFILES),' results (',time,' seconds)'
      r←GenJS SEARCH

    ∇

    ∇ (page title desc iframe code)←Update url;ctrlsec
     ⍝ Create new placeholder values
      page←'MS3: Sample'
      iframe←Frame url
      code←Dread url
      ctrlsec←'Control'Section code
      desc←'Description'Section code
      title←NewWinA ctrlsec url

    ∇

    ∇ r←GenJS(page title desc iframe code)
     ⍝ Generate JavaScript for filling placeholders
      r←'#title'Replace'MS3: ',page
      r,←'#SampleTitle'Replace title
      r,←'#SampleDesc'Replace desc
      r,←'#SampleFrame'Replace iframe
      r,←'#SampleSource'Replace(×≢code)/'x-small;border:none'#.HTMLInput.APLToHTMLColour code
    ∇

    :ENDSECTION

:EndClass

:Class index : MiPageSample

    :SECTION GLOBALS ⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝

    :Field TYPES←'Simple' 'Advanced' 'Dyalog' ⍝ Types of samples (if not App)
    :Field GROUPS ⍝ Names of groups of elements
    :Field REFS        ⍝ ... their refs
    :Field APPS        ⍝ List of all apps
    :Field SAMPLES     ⍝ List of all samples (well, groups and controls really)
    :Field INFOLONG    ⍝ Long desc of each control
    :Field INFOSHORT   ⍝ Short (1-3 words) desc of each control
    :Field NAMESLONG   ⍝ Elements that have long info
    :Field NAMESSHORT  ⍝ Elements that have short info
    :Field SEARCH←''   ⍝ Last-search cache
    :Field CONTROLS←'' ⍝ All control names
    :Field APPDESCS    ⍝ Sample Apps' Description::
    :Field APPCTRLS    ⍝ Sample Apps' Control:: (i.e. the title)
    :Field CORE        ⍝ Core subset of controls

    :ENDSECTION

    :SECTION UTILITIES ⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝

    Q←'"'∘,,∘'"'  ⍝ Surround with quotes, a.k.a. APL dragon

    D←{(×≢⍵)/' – ',⍵} ⍝ Prepend a dash if non-empty

    P←{1⌽(×≢#.HTMLInput.dtlb ⍵)/') (',⍵} ⍝ Surround with parens if non-empty

    Words←{(1↓¨(' '∘=⊂⊢)' ',⍵)~⊂''} ⍝ Split at spaces

    SpaceToDir←{'Examples/',1↓⍵} ⍝ Convert Space name to Folder name

    Frame←{'.framed' ('src=',Q⍵,'?NoWrapper=1') New _.iframe}

    NewWinA←{('target' '_blank')('href=',Q⊃⌽⍵) New _.a (⊃⍵)} ⍝ New link that opens in a new window/tab

    NewDiv←{⍵ New _.div} ⍝ Make a div

    Horz←{⍺←⊢ ⋄ r⊣(r←⍺ New _.StackPanel ⍵).Horizontal←1} ⍝ Horizontal StackPanel

    Type←{P(1⍳⍨∨/¨TYPES⍷¨⊂⍵)⊃TYPES,⊂'App'} ⍝ Simple/Advanced/App?

    Dread←{0::,⊂'[file read failed]' ⋄ #.UnicodeFile.ReadNestedText #.Boot.AppRoot,⍵,'.dyalog'} ⍝ Read dyalog file

    Dlist←{0::⍬ ⋄ ¯7↓¨6⊃#.Files.DirX #.Boot.AppRoot,⍵,'/*.dyalog'} ⍝ List dyalog files

    NodeID←{⍺←⊢ ⋄ ('#node',⊃⍵)∘,¨⍕¨⍺+⍳⍴⊃⌽⍵} ⍝ Generate node ids (⍵='L' items) optional (⍺=offset)

    In←{∨/¨⊃⍷¨/{⎕SE.Dyalog.Utils.lcase¨eis ⍵}¨⍺ ⍵} ⍝ Case-insensitive find

    Names←{1↓¨⍵↑¨⍨¯1+⍵⍳¨':'} ⍝ Extract section names from set of several 'name:: description'

    AddShortInfo←{⍵,¨P¨(4+⍴¨⍵)↓¨(INFOSHORT,⊂'')[NAMESSHORT⍳⍵]} ⍝ ⍵ (ShortDesc)
                    ⍝(⍵∊NAMESSHORT)/¨
    AddLongInfo←{(New¨_.strong,¨⍵),¨D¨{⍵,'[no info available]'/⍨0∊⍴⍵}¨(4+⍴¨⍵)↓¨(INFOLONG,⊂'')[NAMESLONG⍳⍵]} ⍝ ⍵ - LongDesc

      Section←{ ⍝ extract section ⍺:: from code ⍵
          regex←'^\s*⍝\s*',⍺,':(:.*?)$((\R^⍝(?!\s*\w+::).*?$)*)'
          opts←('Mode' 'M')('DotAll' 1)
          res←(regex ⎕S'\1\2'⍠opts)⍵
          (1↓⊃res)~'⍝'
      }

      JSsafe←{ ⍝ Make text JS safe by escaping problematic chars
          fr←'''' '"' '\\' '\n' '\r',⎕UCS 9 8 12               ⍝ ' " \ NL CR TB BS FF
          to←'\\''' '\\"' '\\\\' '\\n' '\\r' '\\t' '\\b' '\\f' ⍝ problematic in regex too :-)
          Q(fr ⎕R to⍠'Mode' 'D')⍵ ⍝ Replace problematic chars and sourround with quotes
      }

    ∇ js←APLtoJS vtv;act;data
      :Access public shared
     ⍝ Convert vectors of pseudo JS (generated by Replace, Execute, et al.) to proper JS

      js←''
      :For act data :In eis vtv
          data←JSsafe⊃⌽eis data ⍝ the last or only one
          :Select ⊃eis act      ⍝ the first or only one
          :Case 'assign'
              js,←(⊃⌽act),' = ',data,';' ⍝ variable = "data"
          :Case 'execute'
              js,←'eval(',data,');' ⍝ eval("data")
          :Case 'replace'
              js,←'$("',(⊃⌽act),'").html(',data,');' ⍝ $("#myid").html("data")
          :Else
              js,←'$("',(⊃⌽act),'").',(⊃act),'(',data,');' ⍝ $("#myid").append("data")
          :EndSelect
      :EndFor
    ∇

    :ENDSECTION

    :SECTION MAIN ⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝

    ∇ Compose;left;mid;right;sp;style
      :Access public

     ⍝ Initialize globals
      SAMPLES←0 3⍴⍬
      GROUPS←'Dyalog Controls' 'Base HTML' 'SyncFusion' 'JQueryUI'
      REFS←_DC _html _SF _JQ
      INFOSHORT←'⍝',¨Dread'Examples/Data/infoShort'
      INFOLONG←'⍝',¨Dread'Examples/Data/infoLong'
      NAMESSHORT←Names INFOSHORT
      NAMESLONG←Names INFOLONG

      '#title'Add _.title'MS3: About' ⍝ After the : will be updated

      Add _.StyleSheet'/Styles/homepage.css'
      style←''
      style,←'#leftBar {background-color:inherit;margin-right:6px;} '
      style,←'li:hover {background-color:highlight;font-wight:normal;}'
      style,←'.menu {padding-left,padding-right:0;} '
      style,←'.cat {font-size:12pt;cursor:pointer;}'
      style,←'.cat:hover {font-size:12pt;background-color:highlight;}'
      style,←'.submenu {max-height:75px;overflow-y:scroll;background-color:white;border:2px groove threedface;} '
      style,←'.menuitem {margin-bottom,margin-left:0px;padding-left:2px;cursor:pointer;} '
      style,←'.menuitem:hover {background-color:highlight;} '
      style,←'.framed {width:730px;max-height:600px;min-height:400px;border:2px inset;overflow-y:auto;background-color:white;} '
      style,←'.listitem {margin:0px;padding:4px;cursor:pointer;} '
      style,←'.listitem:hover {background-color:highlight;} '
      style,←'.samplesource {overflow-x:auto;width:730;background-color:#e5e5cc;border:2px inset;} '
      Add _.style style

      (left mid)←NewDiv¨'#leftBar' '#midBar' ⍝ Create panes
      sp←Add'mainSP'Horz left mid

      sp.Items[1].style←⊂'width: 200px; max-height: 450px;'
      sp.Items[2].style←⊂'margin: 5px;'
      sp.style←'height: 450px;background-color:inherit;'

      PopulateLeft left
      PopulateMid mid
    ∇

    ∇ PopulateLeft thediv;class;depths;group;items;names;ref;samples;vp;search;style;menu;i;fs;ac;text;stuff;html;SF;treeall;treecore

      stuff←''
      tree←0 3⍴0 '' ''

         ⍝ SEARCH FIELD ⍝⍝⍝
      (stuff,←New _.EditField'str').On'change' 'OnSearch'('str' 'val')
      (stuff,←'#case' '.menu'New _.button'Search').On'click' 'OnSearch'

      thediv.Add Horz stuff
      thediv.Add _.hr

      ⍝ SAMPLE APPS ⍝⍝⍝
      APPS←(Dlist'Examples/Apps')~⊂'index'
      APPDESCS←(⊂'Description')Section¨Dread¨'Examples/Apps/'∘,¨APPS
      APPCTRLS←(⊂'Control')Section¨Dread¨'Examples/Apps/'∘,¨APPS
      ('.cat'thediv.Add _.p'Sample Apps').On'click' 'OnAppHeader'
      APPS{tree⍪←1 ⍵('nodeA',⍺)}¨APPCTRLS
      (thediv.Add _.ejTreeView tree).On'nodeSelect' 'OnApp'('node' 'eval' 'argument.id')

      thediv.Add _.hr

      ('.cat'thediv.Add _.p'Controls').On'click' 'OnCtrlHeader'
      CORE←⎕SE.SALT.Load #.Boot.AppRoot,'Examples/Data/core -noname'
      treecore←1 3⍴1 'Core' 'node0C'
      treeall←1 3⍴1 'All' 'node0A'

      :For group class :InEach GROUPS(3↓¨⍕¨REFS) ⍝ Remove leading #._
          names←⎕THIS.⎕SE.SALT.Load #.Boot.AppRoot,'Examples/Data/tree',class,' -noname'
          CONTROLS,←⊂names[;2]/⍨names[;1]=⌈/names[;1]
          ⍎class,'←names'

          :If ∨/names[;2]∊CORE
              treecore⍪←2(group,P'_',class)('node__',class)
              treecore⍪←3,0 1↓names⌿⍨names[;2]∊CORE
          :EndIf

          treeall⍪←2(group,P'_',class)('node_',class)
          treeall⍪←⍎class

      :EndFor

      ('#tree'thediv.Add _.ejTreeView,⊂treecore⍪treeall).On'nodeSelect' 'OnTree'('node' 'eval' 'argument.id')
    ∇

    ∇ PopulateMid mid;url;code;frame;mya

     ⍝ Read framed pages
      url←'Examples/Apps/About'
      code←Dread url
     ⍝ Create and fill placeholder for title header
      mya←('#SampleTitle'mid.Add _.h2).Add NewWinA('Control'Section code)url
     ⍝ Create and fill placeholder for description line
      '#SampleDesc'mid.Add _.p('Description'Section code)
     ⍝ Create and fill placeholder for embedded page
      frame←mid.Add NewDiv'#SampleFrame'
      frame.Add Frame url
      '#SampleSource' '.samplesource'mid.Add _.div,⊂'x-small;border:none'#.HTMLInput.APLToHTMLColour code
    ∇

    :ENDSECTION

    :SECTION CALLBACKS ⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝

    ∇ r←OnTree;node;descs;out;items;spacectrl;control;spacedir;files;file;i;url;code;ctrlsec;desc;item;title;currctrls;core
      :Access Public
      node←4↓(1+'tree'≡_what)⊃_what(Get'node') ⍝ get list-item or node
      :If '0'∊node ⍝ 'All'
          r←''
      :ElseIf '_'=⊃node ⍝ Class or core subset thereof
          core←'_'=2⊃node
          node↓⍨←core ⍝ Remove additional _ from '__html' if core-only
          i←REFS⍳⍎node
          currctrls←(CORE∘∩⍣core)i⊃CONTROLS ⍝ Filter if core-only
          out←NewDiv'.framed'
          descs←AddLongInfo currctrls
          items←(currctrls,⍨¨⊂'list',1⌽node)out.Add¨_.p,¨descs ⍝ make IDs like 'DC_Button'
          items.Set⊂'.listitem'
          items.On⊂'click' 'OnTree'
          r←GenJS node('Members of ',node,P i⊃GROUPS)(' controls',⍨⍕⍴currctrls)out''
      :ElseIf 2≤+/'_'=node ⍝ Category
          r←''
      :Else ⍝ Control
          spacectrl←'_',('_'⎕R'.')node
          control←node↓⍨node⍳'_'
          spacedir←'Examples/',(¯1+node⍳'_')↑node
          files←Dlist spacedir
          out←NewDiv'.framed'
          CURRFILES←''
          CURRDESCS←''
          i←0
          :For file :In files
              url←spacedir,'/',file
              code←Dread url
              ctrlsec←'Control'Section code
              :If (⊂spacectrl)∊Words ctrlsec ⍝ Extract space-separated words
                  i+←1
                  CURRFILES,←⊂url
                  desc←⊂'Description'Section code
                  desc←∊desc,Type file
                  CURRDESCS,←⊂desc
                  item←('#nodeS',⍕i)'.listitem'out.Add _.p desc
                  item.On'click' 'OnSample'
              :EndIf
          :EndFor
          :If ~×i
              '.listitem'out.Add _.p,⊂'[no samples using ',spacectrl,']'
          :EndIf
          title←1↓control Section INFOSHORT
          title←'Samples using ',spacectrl,P title
          desc←control Section INFOLONG
          r←GenJS spacectrl title desc out''
      :EndIf
    ∇

    ∇ r←OnAppHeader;descs;out;items
      :Access Public
     ⍝ Gets called upon clicking the "Sample Apps" header
      descs←APPCTRLS,¨' – '∘,¨APPDESCS
      out←NewDiv'.framed'
      items←APPS{('nodeB',⍺)out.Add _.p ⍵}¨descs
      items.Set⊂'.listitem'
      items.On⊂'click' 'OnApp'
      r←GenJS'Apps' 'Small applications to showcase MiServer 3.0 functionality'(' apps',⍨≢APPS)out''
    ∇

    ∇ r←OnCtrlHeader;descs;out;items
      :Access Public
     ⍝ Gets called upon clicking the "Sample Apps" header
      r←GenJS'Controls' 'Something about controls' 'Controls' 'list of all controls with long descs' ''
    ∇

    ∇ r←OnApp
      :Access Public
     ⍝ Gets called upon selection in Sample Apps tree
      r←GenJS Update'Examples/Apps/',5↓Get'node'
    ∇

    ∇ r←OnSample
      :Access Public
      r←GenJS Update CURRFILES⊃⍨⍎5↓_what ⍝ id="nodeS23"
    ∇

    ∇ r←OnSearch;time;str;terms;out;i;dir;files;file;url;code;ctrlsec;desc;item
      :Access Public
      time←0.001×3⊃⎕AI ⍝ prepare search statistics
      :If ×≢str←Get'str'  ⍝ get search string
      ⍝⍝⍝ Controls
          terms←INFOSHORT/⍨str In INFOSHORT
          terms,←terms~⍨INFOLONG/⍨str In INFOLONG
          terms←'.',¨∪Names terms
      ⍝⍝⍝ Samples
          out←NewDiv'.framed'
          CURRFILES←''
          CURRDESCS←''
          i←0
          :For dir :In 'Apps' 'html' 'HTMLplus' 'DC' 'JQ' 'SF'
              files←Dlist'Examples/',dir
              :For file :In files~⊂'index'
                  url←'Examples/',dir,'/',file
                  code←Dread url
                  ctrlsec←'Control'Section code
                  desc←⊂'Description'Section code
                  :If ∨/str In desc
                  :OrIf ∨/∊(terms,⊂str)In¨⊂ctrlsec ⍝ always case sensitive
                      i+←1
                      CURRFILES,←⊂url
                      desc←∊desc,Type file
                      CURRDESCS,←⊂desc
                      item←('#nodeS',⍕i)'.listitem'out.Add _.p desc
                      item.On'click' 'OnSample'
                  :EndIf
              :EndFor
          :EndFor
          desc←New _.span'Showing results for '
          desc.Add _.em str
          desc.Add(×≢terms)/D'including relevant element','s'/⍨1≠≢terms
          desc.Add _.em(⍕1↓¨terms)
          SEARCH←(Q str)''desc out''
      :EndIf
      time-⍨←0.001×3⊃⎕AI
      (2⊃SEARCH)←(⍕≢CURRFILES),' results (',time,' seconds)'
      r←GenJS SEARCH

    ∇

    ∇ (page title desc iframe code)←Update url;ctrlsec
     ⍝ Create new placeholder values
      page←'MS3: Sample'
      iframe←Frame url
      code←Dread url
      ctrlsec←'Control'Section code
      desc←'Description'Section code
      title←NewWinA ctrlsec url

    ∇

    ∇ r←GenJS(page title desc iframe code)
     ⍝ Generate JavaScript for filling placeholders
      r←'#title'Replace'MS3: ',page
      r,←'#SampleTitle'Replace title
      r,←'#SampleDesc'Replace desc
      r,←'#SampleFrame'Replace iframe
      r,←'#SampleSource'Replace(×≢code)/'x-small;border:none'#.HTMLInput.APLToHTMLColour code
    ∇

    :ENDSECTION

:EndClass


Update links to Syncfusion samples from MiPageSample - they moved again...

:Class MiPageSample : #.MiPage
   ⍝ This is a template that "wraps" the page content by
   ⍝ - adding a header and footer
   ⍝ - adding a handler that will toggle the display of the web page and its APL source code

    ∇ {r}←Wrap;lang;server;controls;sp;c
      :Access Public

      :If 0∊⍴Get'nowrapper'
          server←_Request.Server

          controls←∪Walk Body.Content

        ⍝ we use Syncfusion (which uses jQuery) to set up the controls to do cool stuff
          Use'Syncfusion' ⍝ this is a resource defined in Config/Resources.xml

        ⍝ set the title display in the browser to the name of the application defined in Config/Server.xml
          Add _.title server.Config.Name

        ⍝ add a link to our CSS stylesheet
          _CssReset←'/Styles/cssreset.css'
          _CssOverride←'/Styles/sampleStyles.css'

        ⍝ set a meta tag to make it explicitly UTF-8
          (Add _.meta).Set'http-equiv="content-type" content="text/html;charset=UTF-8"'

        ⍝ wrap the content of the <body> element in a div
          Body.Push _.div'id="contentblock"'

        ⍝ add a hidden division to the body containing the APL source code
          (Add _.div(#.HTMLInput.APLToHTMLColour ⎕SRC⊃⊃⎕CLASS ⎕THIS)).Set'id="codeblock"' 'style="display: none;"'

          :If ~0∊⍴controls
              c←Body.Content
              Body.Content←''
              (sp←Body.Add _.StackPanel(⊂FormatControls controls)c).Horizontal←1

          :EndIf

        ⍝ wrap the content of the <body> element in a div
          Body.Push _.div'class="bodyblock"'

        ⍝ add the footer to the bottom of the page
          Add #.Files.GetText server.Config.Root,'Styles\footer.txt'

        ⍝ add the header to the top of the page and wrap the body in a div with id="wrapper"
          Body.Push #.Files.GetText server.Config.Root,'Styles\banner.txt'

          Body.Push _.div'id="wrapper"'

        ⍝ add a JQuery event handler to toggle the web page/APL source code
          Add _.Script'$(function(){$("#bannerimage").on("click", function(evt){$("#contentblock,#codeblock,.widgethelp").toggle(400,"swing");});});'

        ⍝ set the language for the page
          lang←server.Config.Lang ⍝ use the language specified in Server.xml
          Set'lang="',lang,'" xml:lang="',lang,'" xmlns="http://www.w3.org/1999/xhtml"'
      :Else
          Head.Add _.StyleSheet'/Styles/sampleStyles.css'
          Body.class←'bodyblock'
      :EndIf
     ⍝ call the base class Wrap function

      r←⎕BASE.Wrap
    ∇

    ∇ r←Walk content;e
      r←⍬
      :For e :In content
          :If (#.HtmlElement.isClass e)∨#.HtmlElement.isInstance e
              r←r,⊃⎕CLASS e
              :Trap 6
                  r,←Walk e.Content
              :EndTrap
          :EndIf
      :EndFor
    ∇

    ∇ r←FormatControls controls;ctrls;ns;desc;field;n;ctrl;i;c;ref;l;u;nss;item;cn
      ctrls←⊃{⍺ ⍵}#.Utils.∆key/↓[1]0 1↓↑{⎕ML←3 ⋄ {⍵⊂⍨⍵≠'.'}⍕⍵}¨controls
      field←{0::'' ⋄ ⍺⍎⍵}
      nss←'_DC' '_SF' '_JQ' '_html'
      desc←'Dyalog Controls' 'Syncfusion Widgets' 'jQuery Widgets' 'Native HTML5 Elements'
      (r←⎕NEW _.div).class←'widgethelp'
      r.Add'This Page Contains<hr/>'
      :Trap 0
          :For (ns ctrl i) :In ctrls{↓(⍺,⍵)[⍋⍵;]}nss⍳{(⊃⍣(¯1+≡⍵))⍵}¨ctrls[;1]
              (r.Add _.span(i⊃desc)).class←'widgetNs'
              u←r.Add _.ul
              n←#.⍎ns←⊃ns
              :For c :In ctrl
                  ref←n.⍎c
                  item←⍬
                  :Select ns
                  :Case '_DC'
                      item←u.Add _.li(New _.a(c(('href=/Documentation/DyalogAPIs/WidgetDoc?namespace=',ns,'&widget=',c)'target=_blank')))
                  :Case '_SF'
                      item←u.Add _.li(New _.a c(('href'('http://js.syncfusion.com/demos/web/default.htm#!/azure/',#.Strings.lc(2×'ej'≡2↑c)↓c))('target' '_blank')))
                  :Case '_JQ'
                      item←u.Add _.li(New _.a c(('href'('http://jqueryui.com/',(2×'jq'≡2↑c)↓c))('target' '_blank')))
                  :Case '_html'
                      cn←c
                      :If ~0∊⍴('^h[1-6]$'⎕S{⍵.Match})c
                          cn←'hn'
                      :EndIf
                      item←u.Add _.li(New _.a c(('href'('http://www.w3schools.com/tags/tag_',cn,'.asp'))('target' '_blank')))
                  :EndSelect
                  :If 0∊⍴item
                      u.Add _.li c
                  :EndIf
              :EndFor
          :EndFor
      :EndTrap
    ∇

:EndClass

:Class MiPageSample : #.MiPage
   ⍝ This is a template that "wraps" the page content by
   ⍝ - adding a header and footer
   ⍝ - adding a handler that will toggle the display of the web page and its APL source code

    ∇ {r}←Wrap;lang;server;controls;sp;c
      :Access Public

      :If 0∊⍴Get'nowrapper'
          server←_Request.Server

          controls←∪Walk Body.Content

        ⍝ we use Syncfusion (which uses jQuery) to set up the controls to do cool stuff
          Use'Syncfusion' ⍝ this is a resource defined in Config/Resources.xml

        ⍝ set the title display in the browser to the name of the application defined in Config/Server.xml
          Add _.title server.Config.Name

        ⍝ add a link to our CSS stylesheet
          _CssReset←'/Styles/cssreset.css'
          _CssOverride←'/Styles/sampleStyles.css'

        ⍝ set a meta tag to make it explicitly UTF-8
          (Add _.meta).Set'http-equiv="content-type" content="text/html;charset=UTF-8"'

        ⍝ wrap the content of the <body> element in a div
          Body.Push _.div'id="contentblock"'

        ⍝ add a hidden division to the body containing the APL source code
          (Add _.div(#.HTMLInput.APLToHTMLColour ⎕SRC⊃⊃⎕CLASS ⎕THIS)).Set'id="codeblock"' 'style="display: none;"'

          :If ~0∊⍴controls
              c←Body.Content
              Body.Content←''
              (sp←Body.Add _.StackPanel(⊂FormatControls controls)c).Horizontal←1

          :EndIf

        ⍝ wrap the content of the <body> element in a div
          Body.Push _.div'class="bodyblock"'

        ⍝ add the footer to the bottom of the page
          Add #.Files.GetText server.Config.Root,'Styles\footer.txt'

        ⍝ add the header to the top of the page and wrap the body in a div with id="wrapper"
          Body.Push #.Files.GetText server.Config.Root,'Styles\banner.txt'

          Body.Push _.div'id="wrapper"'

        ⍝ add a JQuery event handler to toggle the web page/APL source code
          Add _.Script'$(function(){$("#bannerimage").on("click", function(evt){$("#contentblock,#codeblock,.widgethelp").toggle(400,"swing");});});'

        ⍝ set the language for the page
          lang←server.Config.Lang ⍝ use the language specified in Server.xml
          Set'lang="',lang,'" xml:lang="',lang,'" xmlns="http://www.w3.org/1999/xhtml"'
      :Else
          Head.Add _.StyleSheet'/Styles/sampleStyles.css'
          Body.class←'bodyblock'
      :EndIf
     ⍝ call the base class Wrap function

      r←⎕BASE.Wrap
    ∇

    ∇ r←Walk content;e
      r←⍬
      :For e :In content
          :If (#.HtmlElement.isClass e)∨#.HtmlElement.isInstance e
              r←r,⊃⎕CLASS e
              :Trap 6
                  r,←Walk e.Content
              :EndTrap
          :EndIf
      :EndFor
    ∇

    ∇ r←FormatControls controls;ctrls;ns;desc;field;n;ctrl;i;c;ref;l;u;nss;item;cn
      ctrls←⊃{⍺ ⍵}#.Utils.∆key/↓[1]0 1↓↑{⎕ML←3 ⋄ {⍵⊂⍨⍵≠'.'}⍕⍵}¨controls
      field←{0::'' ⋄ ⍺⍎⍵}
      nss←'_DC' '_SF' '_JQ' '_html'
      desc←'Dyalog Controls' 'Syncfusion Widgets' 'jQuery Widgets' 'Native HTML5 Elements'
      (r←⎕NEW _.div).class←'widgethelp'
      r.Add'This Page Contains<hr/>'
      :Trap 0
          :For (ns ctrl i) :In ctrls{↓(⍺,⍵)[⍋⍵;]}nss⍳{(⊃⍣(¯1+≡⍵))⍵}¨ctrls[;1]
              (r.Add _.span(i⊃desc)).class←'widgetNs'
              u←r.Add _.ul
              n←#.⍎ns←⊃ns
              :For c :In ctrl
                  ref←n.⍎c
                  item←⍬
                  :Select ns
                  :Case '_DC'
                      item←u.Add _.li(New _.a(c(('href=/Documentation/DyalogAPIs/WidgetDoc?namespace=',ns,'&widget=',c)'target=_blank')))
                  :Case '_SF'
                      item←u.Add _.li(New _.a c(('href'('http://js.syncfusion.com/demos/web/#!/azure/',(2×'ej'≡2↑c)↓c))('target' '_blank')))
                  :Case '_JQ'
                      item←u.Add _.li(New _.a c(('href'('http://jqueryui.com/',(2×'jq'≡2↑c)↓c))('target' '_blank')))
                  :Case '_html'
                      cn←c
                      :If ~0∊⍴('^h[1-6]$'⎕S{⍵.Match})c
                          cn←'hn'
                      :EndIf
                      item←u.Add _.li(New _.a c(('href'('http://www.w3schools.com/tags/tag_',cn,'.asp'))('target' '_blank')))
                  :EndSelect
                  :If 0∊⍴item
                      u.Add _.li c
                  :EndIf
              :EndFor
          :EndFor
      :EndTrap
    ∇

:EndClass


Delete MiPageSample.dyalog.LOCAL.15104.dyalog

:Class MiPageSample : #.EAWC
⍝ This is a template that "wraps" the page content by
⍝ - adding a header and footer
⍝ - adding a handler that will toggle the display of the web page and its APL source code

    ∇ Wrap;lang;server;controls
      :Access Public

      server←_Request.Server

      controls←∪Walk Body.Content

    ⍝ we use Syncfusion (which uses jQuery) to set up the controls to do cool stuff
      Use'Syncfusion' ⍝ this is a resource defined in Config/Resources.xml

    ⍝ set the title display in the browser to the name of the application defined in Config/Server.xml
      Add title server.Config.Name

    ⍝ add a link to our CSS stylesheet
      (Add link).SetAttr(('href' '/Styles/style.css')('rel' 'stylesheet')('type' 'text/css'))

    ⍝ set a meta tag to make it explicitly UTF-8
      (Add meta).SetAttr'http-equiv="content-type" content="text/html;charset=UTF-8"'

    ⍝ wrap the content of the <body> element in a div
      Body.Push div'id="contentblock"'

    ⍝ add a hidden division to the body containing the APL source code
      (Add div(#.HTMLInput.APLToHTML ⎕SRC⊃⊃⎕CLASS ⎕THIS)).SetAttr'id="codeblock"' 'style="display: none;"'

    ⍝ add a JQuery event handler to toggle the web page/APL source code
      Add _HTML.Script'$(function(){$("#bannerimage").on("click", function(evt){$("#contentblock,#codeblock").toggle();});});'

    ⍝ add the footer to the bottom of the page
      Add #.Files.GetText server.Config.Root,'Styles\footer.txt'

    ⍝ add the header to the top of the page and wrap the body in a div with id="wrapper"
      Body.Push #.Files.GetText server.Config.Root,'Styles\banner.txt'

      Body.Push div'id="wrapper"'

    ⍝ set the language for the page
      lang←server.Config.Lang ⍝ use the language specified in Server.xml
      Head.SetAttr'lang="',lang,'" xml:lang="',lang,'" xmlns="http://www.w3.org/1999/xhtml"'

    ⍝ call the base class Wrap function
      ⎕BASE.Wrap
    ∇

    ∇ r←Walk content;e
      r←⍬
      :For e :In content
          :If (#.HtmlElement.isClass e)∨#.HtmlElement.isInstance e
              r←r,⊃⎕CLASS e
              :Trap 6
                  r,←Walk e.Content
              :EndTrap
          :EndIf
      :EndFor
    ∇

:EndClass


jqButton: added internal documentation

jqButton: added internal documentation

:Class jqButton : #._JQ._jqUIWidget
⍝ Description:: jQueryUI Sortable widget/interaction
⍝ Constructor:: [text [type]]
⍝ text - the text/content to appear on the button
⍝ type - one of 'submit' (default), 'reset', or 'button'
⍝ Public Fields::
⍝ Text - the text/content to appear on the button
⍝ Type - one of 'submit' (default), 'reset', or 'button'

  :field public shared readonly DocBase←'http://api.jqueryui.com/button/'
    :field public shared readonly ApiLevel←3
    :field public shared readonly DocDyalog←'/Documentation/DyalogAPIs/jQuery/jqButtonHelp.html'

    :field public Text←''       ⍝ text on the button
    :field public Type←''       ⍝ valid types are 'button' 'submit' 'reset' 'checkbox' 'radio' 'anchor' ''

    ∇ Make0
      :Access public
      JQueryFn←'button'
      :Implements constructor
      InternalEvents←,⊂'create'
    ∇

    ∇ Make1 args;i
      :Access public
      JQueryFn←'button'
      args←eis args
      (Text Type)←2↑args,(⍴args)↓'' ''
      :Implements constructor
      InternalEvents←,⊂'create'
    ∇

    ∇ r←Render;type
      :Access public
      :Select #.Strings.lc Type
      :CaseList 'button' 'submit' 'reset' 'checkbox' 'radio'
          ContainerTag←'input'
          Container.type←Type
      :Case 'anchor'
          ContainerTag←,'a'
          Container.Add(⊂Text)
      :Else
          ContainerTag←'button'
          Container.Add(⊂Text)
      :EndSelect
      r←⎕BASE.Render
      :If 'input'≡ContainerTag
          :If ~0∊⍴Text
              r,←(⎕NEW #._html.label(Text('for'Container.id))).Render
          :EndIf
      :EndIf
    ∇
:EndClass

:Class jqButton : #._JQ._jqUIWidget
    :field public shared readonly DocBase←'http://api.jqueryui.com/button/'
    :field public shared readonly ApiLevel←3
    :field public shared readonly DocDyalog←'/Documentation/DyalogAPIs/jQuery/jqButtonHelp.html'

    :field public Text←''       ⍝ text on the button
    :field public Type←''       ⍝ valid types are 'button' 'submit' 'reset' 'checkbox' 'radio' 'anchor' ''

    ∇ Make0
      :Access public
      JQueryFn←'button'
      :Implements constructor
      InternalEvents←,⊂'create'
    ∇

    ∇ Make1 args;i
      :Access public
      JQueryFn←'button'
      args←eis args
      (Text Type)←2↑args,(⍴args)↓'' ''
      :Implements constructor
      InternalEvents←,⊂'create'
    ∇

    ∇ r←Render;type
      :Access public
      :Select #.Strings.lc Type
      :CaseList 'button' 'submit' 'reset' 'checkbox' 'radio'
          ContainerTag←'input'
          Container.type←Type
      :Case 'anchor'
          ContainerTag←,'a'
          Container.Add(⊂Text)
      :Else
          ContainerTag←'button'
          Container.Add(⊂Text)
      :EndSelect
      r←⎕BASE.Render
      :If 'input'≡ContainerTag
          :If ~0∊⍴Text
              r,←(⎕NEW #._html.label(Text('for'Container.id))).Render
          :EndIf
      :EndIf
    ∇
:EndClass


less verbose comments in lb-gen.dyalog

less verbose comments in lb-gen.dyalog

#!/bin/bash
(echo ∇M;tail -n+3 $0;echo -e '∇\nM\n⎕off')|dyalog -script;exit $?

⍝This script generates JS constants with information about the language bar. Usage:
⍝ #fetch the latest .xml
⍝ svn cat http://svn.dyalog.bramley/svn/dyalog/trunk/apl/svn/tools/languagebar/out/lbar_unicode.xml >lb.xml
⍝ #generate the .js
⍝ ./lb-gen.dyalog >../src/lb.js

⍝mentioned in http://wiki.dyalog.bramley/index.php/New_Glyphs

⎕io←⎕ct←0 ⋄ ⎕pw←32767 ⋄ json←7160⌶ ⋄ 'base64'⎕cy'dfns'
esc←(,¨'&<>')⎕r'\&' '\<' '\>'
trim←('\n+\Z'⎕r''⍠'Mode' 'D')(' +$'⎕r'') ⍝rm trailing spaces from each line and rm empty lines at the end of ⍵

⍝parse xml and extract tags
x←⎕xml'UTF-8'⎕ucs 83 ¯1⎕map'lb.xml'
tag val←↓⍉x[;1 2]
chr←⎕ucs⍎¨val/⍨tag≡¨⊂'chr'
text desc←↓(⊂'UTF-8')⎕ucs¨base64¨↑(⊂val)/⍨¨↓'text' 'desc'∘.≡tag

m←chr≠⎕ucs 0 ⍝grouping mask
cl←('' ' class=first' ' class=last' ' class=''first last''')[(1,2</m)+2×1,⍨2>/m] ⍝css classes
h←⊃,¨/(⊂'<b')cl(⊂'>')(esc¨chr)(⊂'</b>')
((~m)/h)←' '

⍝output
'//generated code, do not edit'
'D.lb={//language bar information'
'html:',(json∊h),','
'tips:{'
¯2↓⊃,/⊃,¨/(json¨,¨m/chr)(⊂':[')(json¨m/desc)','(json¨trim¨m/text)(⊂'],',⎕ucs 10)
'}'
'}'
⎕off

#!/bin/bash
(echo '∇M';tail -n+3 "$0";echo -e '∇\nM\n⎕off')|dyalog -script;exit $?

⍝This script takes an lbar_unicode.xml as input and
⍝generates JavaScript constants with information about the language bar.

⍝Usage from bash:
⍝ #fetch the latest .xml
⍝ svn cat http://svn.dyalog.bramley/svn/dyalog/trunk/apl/svn/tools/languagebar/out/lbar_unicode.xml >lb.xml
⍝ #generate the .js
⍝ ./lb-gen.dyalog >../src/lb.js

⍝mentioned in http://wiki.dyalog.bramley/index.php/New_Glyphs

⎕io←⎕ct←0 ⋄ ⎕pw←32767 ⋄ json←7160⌶ ⋄ 'base64'⎕cy'dfns'
esc←(,¨'&<>')⎕r'\&' '\<' '\>'
trim←('\n+\Z'⎕r''⍠'Mode' 'D')(' +$'⎕r'') ⍝rm trailing spaces from each line and rm empty lines at the end of ⍵

⍝parse xml and extract tags
x←⎕xml'UTF-8'⎕ucs 83 ¯1⎕map'lb.xml'
tag val←↓⍉x[;1 2]
chr←⎕ucs⍎¨val/⍨tag≡¨⊂'chr'
text desc←↓(⊂'UTF-8')⎕ucs¨base64¨↑(⊂val)/⍨¨↓'text' 'desc'∘.≡tag

m←chr≠⎕ucs 0 ⍝grouping mask
cl←('' ' class=first' ' class=last' ' class=''first last''')[(1,2</m)+2×1,⍨2>/m] ⍝css classes
h←⊃,¨/(⊂'<b')cl(⊂'>')(esc¨chr)(⊂'</b>')
((~m)/h)←' '

⍝output
'//generated code, do not edit'
'D.lb={//language bar information'
'html:',(json∊h),','
'tips:{'
¯2↓⊃,/⊃,¨/(json¨,¨m/chr)(⊂':[')(json¨m/desc)','(json¨trim¨m/text)(⊂'],',⎕ucs 10)
'}'
'}'
⎕off


Mea Culpa - MiPage.Get fix

Mea Culpa - MiPage.Get fix

:Class MiPage : #.HtmlPage

    ⍝∇:require =\HtmlPage.dyalog
    ⍝∇:require =\JSON.dyalog

    :Field Public _PageName←'' ⍝ Page file name
    :Field Public _PageDate←'' ⍝ Page saved date
    :field Public _Request     ⍝ HTTPRequest
    :field Public _Scripts←''
    :field Public _Styles←''
    :field Public _CssReset←''    ⍝ location of CSS Reset file (if any)
    :field Public _CssOverride←'' ⍝ location of CSS Override file (if any)
    :field Public _Serialized←1   ⍝ serialized forms to return in _PageData
    :field Public _event          ⍝ set by APLJAX callback - event that was triggered
    :field Public _what           ⍝ set by APLJAX callback - name or id of the triggering element
    :field Public _value          ⍝ set by APLJAX callback - value of the triggering element
    :field Public _selector       ⍝ set by APLJAX callback - CSS/jQuery selector for the element that triggered the event
    :field Public _callback       ⍝ set by APLJAX callback - name of the callback function
    :field Public _target         ⍝ set by APLJAX callback - id of the target element
    :field Public _currentTarget  ⍝ set by APLJAX callback - id of the currentTarget element
    :field Public _PageData       ⍝ namespace containing any data passed via forms or URL
    :field Public _AjaxResponse←''
    :field Public _DebugCallbacks←0
    :field Public _TimedOut←0
    :field Public _Sessioned←1    ⍝ by default save page instance in session
    :field Public _cache←''       ⍝ cached content if page is marked cacheable
    :field Public OnLoad←''       ⍝ page equivalent to ⎕LX
    :field Public Cacheable←0     ⍝ is the page cacheable?
    :field Public Charset←'UTF-8' ⍝ charset for page

    _used←'' ⍝ track what's been Use'd

    ∇ Make
      :Access public
      :Implements constructor :Base
      MakeCommon
    ∇

    ∇ Make1 req
      :Access public
      _Request←req
      :Implements constructor :base
      MakeCommon
    ∇

    ∇ MakeCommon
      _PageData←⎕NS''
    ∇

    ∇ {r}←Render;b;styles;_head;_body;_styles;_scripts;t;i
      :Access public
   ⍝  Capture the current Head and Body content so that we can reset it after rendering
   ⍝  This is so we can re-render and still get the same result

      (_head _body)←(Head Body).Content
      (_styles _scripts)←_Styles _Scripts

      :If ''≢OnLoad
          {0:: ⋄ Use'JQuery'}''
      :EndIf

      b←RenderBody

      :If ~0∊⍴_Scripts
          {(Insert _html.script).Set('src'⍵)}¨⌽∪_Scripts
      :EndIf

      styles←∪_Styles
      styles←styles,⍨{0∊⍴⍵:⍵ ⋄ ⊂⍵}_CssReset
      styles←styles,{0∊⍴⍵:⍵ ⋄ ⊂⍵}_CssOverride

      :If ~0∊⍴styles
          {Insert #._DC.StyleSheet ⍵}¨⌽∪styles
      :EndIf

      t←''
      :If ~0∊⍴Handlers
          t←,∊Handlers.Render
      :EndIf
      :If ~0∊⍴OnLoad
          t,←(⎕NEW #._html.script('$(function(){',OnLoad,'});')).Render
      :EndIf
      :If ~0∊⍴t
          :If '</body>'≡¯7↑b
              i←¯7
          :Else
              i←-6+⊃⍸'>ydob/<'⍷⌽b
          :EndIf
          b←(i↓b),t,i↑b
      :EndIf

      :If ''≢OnLoad

      :EndIf

      :If 0∊⍴⊃Attrs[⊂'lang'] ⍝ set the language for the page if not already set
          {0:: ⋄ {Set'lang="',⍵,'" xml:lang="',⍵,'" xmlns="http://www.w3.org/1999/xhtml"'}_Request.Server.Config.Lang}''
      :EndIf

      :If ~0∊⍴Charset
          Insert _html.meta''('charset="',Charset,'"')
      :EndIf

      r←RenderPage b

      :If 0≠⎕NC⊂'_Request.Response'
          _Request.Response.HTML←r
      :EndIf

      (Head Body).Content←_head _body
      _Styles←_styles
      _Scripts←_scripts
      _used←''
    ∇

    ∇ {r}←Wrap
      :Access public
      r←Render
      :If Cacheable ⋄ _cache←r ⋄ :EndIf
    ∇

    ∇ Use resources;n;ind;t;x;server
      :Access public
      resources←eis resources
      :For x :In resources
          :If ~(⊂x)∊_used
              :Select ⊃x
              :Case '⍎' ⍝ script
                  _Scripts,←⊂1↓x
              :Case '⍕' ⍝ style sheet
                  _Styles,←⊂1↓x
              :Else
                  :If 0≠⎕NC'_Request.Server'
                      server←_Request.Server
                  :ElseIf 0≠⎕NC'#.Boot.ms'
                      server←#.Boot.ms
                  :EndIf
                  :If 0≠server.⎕NC⊂'Config.Resources'
                  :AndIf ~0∊n←1↑⍴server.Config.Resources
                      :If n≥ind←server.Config.Resources[;1]⍳⊂x
                          :If ~0∊⍴t←{(~0∘∊∘⍴¨⍵)/⍵}(⊂ind 2)⊃server.Config.Resources
                              _Scripts,⍨←t
                          :EndIf
                          :If ~0∊⍴t←{(~0∘∊∘⍴¨⍵)/⍵}(⊂ind 3)⊃server.Config.Resources
                              _Styles,⍨←t
                          :EndIf
                      :Else
                          1 server.Log _PageName,' references unknown resource: ',x
                      :EndIf
                  :EndIf
              :EndSelect
              _used,←⊂x
          :EndIf
      :EndFor
    ∇

    ∇ r←{proto}Get names;noproto;char
    ⍝ names is a single name, a vector of vectors of names, or a space-delimited list of names
    ⍝ {proto} is the optional prototypical value if a name is not found
      :Access public
      :If noproto←0=⎕NC'proto' ⋄ proto←'' ⋄ :EndIf ⍝ default prototype is empty
      names←eis names
      names←,⍕names
      names←#.Strings.deb names
      :If ' '∊names
          names←' '(≠⊆⊢)names
          r←({⍬∘⍴⍣(1=≢⍵)⊢⍵}eis proto)Get¨names
      :ElseIf ~(_PageData.⎕NC names)∊2 9 ⍝ by this time, names is a single name
          r←,proto
      :Else
          r←_PageData⍎names
          :If 2≤≡r
              :If 1=⍴,r
                  r←⊃r
              :EndIf
          :EndIf
⍝↓↓↓ BPB - we don't want to gratuitously convert - e.g. 'null' 'true' '0002010'
⍝          :If noproto≥char←isChar proto
⍝          :AndIf isString r
⍝              r←#.JSON.toAPL r
⍝          :EndIf
          :If noproto⍱isChar proto
              :If 1≠2|⎕DR r
                  r←,proto
              :EndIf
          :EndIf
      :EndIf
    ∇

    ∇ r←GetNames str
      :Access public
      →0⍴⍨0∊⍴r←_PageData.⎕NL-2 9
      →0⍴⍨0∊⍴str
      r←r/⍨r #.Strings.beginsWith¨⊂str
    ∇

    ∇ r←{proto}GetRaw names
      :Access public
      proto←{6::⍵ ⋄ proto}''
      names←eis names
      names←,⍕names
      names←#.Strings.deb names
      :If ' '∊names
          names←{⎕ML←3 ⋄ ⍵⊂⍨⍵≠' '}names
          r←proto∘Get¨names
      :ElseIf 2≠_PageData.⎕NC names
          r←,proto
      :Else
          r←_PageData⍎names
          :If 2=≡r
              :If 1=⍴,r
                  r←⊃r
              :EndIf
          :EndIf
          :If ~isChar proto
              r←{0::⍵ ⋄ 0∊⍴⍵:⍬ ⋄ w←⍵ ⋄ ((w='-')/w)←'¯' ⋄ ⊃(//)⎕VFI w}r
          :EndIf
      :EndIf
    ∇

    ∇ r←{proto}SessionGet names
      :Access public
      proto←{6::⍵ ⋄ proto}''
      names←,⍕names
      names←#.Strings.deb names
      :If ' '∊names
          names←{⎕ML←3 ⋄ ⍵⊂⍨⍵≠' '}names
          r←proto∘SessionGet¨names
      :ElseIf 0=_Request.⎕NC⊂'Session'
      :OrIf 0=_Request.Session.⎕NC names
          r←proto
      :Else
          r←_Request.Session⍎names
          :If 1<|≡r ⋄ r←∊r ⋄ :EndIf
          :If ~0 2∊⍨10|⎕DR proto  ⍝ if the prototype is numeric
          :AndIf 0 2∊⍨10|⎕DR r    ⍝ and the element is character
              r←{0∊⍴⍵:⍬ ⋄ w←⍵ ⋄ ((w='-')/w)←'¯' ⋄ ⊃(//)⎕VFI w}r
          :EndIf
      :EndIf
    ∇

    ∇ _Close session ⍝ Called when the session ends
    ⍝ this method is specific to #.SimpleSessions and is called when a session is terminated
      :Access Public Overridable
    ∇

    :section APLJax  ⍝ used for building APLJAX responses
    ∇ _resetAjax
      :Access public
      _AjaxResponse←''
    ∇

    ∇ r←_id
      :Access public
      ⍝ as there seems to be a problem with at least some Syncfusion widgets
      ⍝ being able to provide the id (or name) of the triggering element, we try to obtain it from _what or _selector
      r←''
      :Trap 0
          :If 0∊⍴r←_what
          :AndIf '#'=⊃_selector
              r←1↓_selector
          :EndIf
      :EndTrap
    ∇

    ∇ r←renderContent content;c
      :Access public shared
      r←''
      content←eis content
      :Trap 0
          :While ~0∊⍴content
              :Select ≡c←⊃content
              :Case 0
                  :If isClass c
                      :Select ⊃⍴content
                      :Case 1
                          r,←(⎕NEW c).Render
                      :Case 2
                          r,←(⎕NEW c(2⊃content)).Render
                      :Else
                          r,←(⎕NEW c(1↓content)).Render
                      :EndSelect
                  :ElseIf isInstance c
                      r,←c.Render
                  :Else
                      r,←(⎕NEW #.HtmlElement(''content)).Render
                  :EndIf
                  content←''
              :Case 1
                  :If isClass⊃c
                      r,←(⎕NEW(⊃c)(1↓c)).Render
                  :ElseIf isInstance⊃c
                      ∘∘∘ ⍝ should not happen! (I think)
                  :Else
                      r,←(⎕NEW #.HtmlElement(''c)).Render
                  :EndIf
                  content←1↓content
              :Else
                  r,←renderContent c
                  content←1↓content
              :EndSelect
          :EndWhile
      :Else
          :If (⎕EN≥100)∧(⎕EN≤999) ⍝ HTTP status rather than an APL error
          :AndIf 0≠⎕NC⊂'_Request.Response' ⍝ we're actually running a server, not rendering in session
              _Request.Fail ⎕EN
          :Else ⍝ APL errors and in-session errors are not trapped:
              ⎕SIGNAL ⎕DMX.(⊂{⍵(⍎⍵)}¨(⎕NL-⍳9)~'InternalLocation' 'DM' 'OSError') ⍝ re-signal
          :EndIf
      :EndTrap
    ∇

    ∇ r←selector Replace content
      :Access public shared
      r←⊂('replace'selector)('data'(renderContent content))
    ∇
    ∇ r←selector Append content
      :Access public shared
      r←⊂('append'selector)('data'(renderContent content))
    ∇
    ∇ r←selector Prepend content
      :Access public shared
      r←⊂('prepend'selector)('data'(renderContent content))
    ∇
    ∇ r←Execute content
      :Access public shared
      r←⊂('execute'(renderContent content))
    ∇

    ∇ r←name Assign data
      :Access public shared
      r←⊂('assign'name)('data'data)
    ∇

    :endsection

    :section Position

    ∇ ref Position args;inds;mask;parameters;my;at;of;collision;within;q
      :Access public
      ⍝ ref  - a reference to an instance of anything based on HtmlElement
      ⍝ args - position information per jQueryUI's Position widget http://api.jqueryui.com/position/
      ⍝        can be in any of the following forms
      ⍝      1) positional (my at of collision within)  N.B. we don't use the "using" parameter
      ⍝         example:  myDiv Position 'left top' 'right bottom' '#otherElement'
      ⍝                   positions myDiv's top left corner at the bottom right corner of the element with id "otherElement"
      ⍝      2) paired
      ⍝                   myDiv Position 'my' 'left top' 'at' 'right bottom' 'of' '#otherElement'
      ⍝                   myDiv Position ('my' 'left top') ('at' 'right bottom') ('of' '#otherElement')
      ⍝                   myDiv Position 3 2⍴'my' 'left top' 'at' 'right bottom' 'of' '#otherElement'
      ⍝ Note: positional arguments are in form horizontal (left center right) vertical (top center bottom)

      parameters←'my' 'at' 'of' 'collision' 'within'
      q←{1⌽'''''',{⍵/⍨1+''''=⍵}⍕⍵}
      :If isInstance ref
          :If 2=⍴⍴args ⍝ matrix
              args←,args
          :ElseIf 3=≡args
              args←⊃,/args
          :EndIf
          args←eis args
          inds←parameters⍳args
          :If ∨/mask←inds≤⍴parameters
              :If mask≡(2×+/mask)⍴1 0
                  parameters←mask/args
                  args←(1⌽mask)/args
              :EndIf
          :Else
              parameters←(⍴args)↑parameters
          :EndIf
          mask←⍬∘≢¨args
          args←mask/args
          parameters←mask/parameters
          parameters(ref{⍺⍺⍎'Position.',⍺,'←',q ⍵})¨args
          ref.Uses,←⊂'JQueryUI'
          SetUse
      :EndIf
    ∇
    :endsection

    :section Event Handling Support
    ∇ r←isPost
      :Access public
      r←{0::0 ⋄ _Request.isPost}⍬
    ∇

    ∇ r←isAPLJax
      :Access public
      r←{0::0 ⋄ _Request.isAPLJAX}⍬
    ∇

    ∇ r←sel Css args ⍝ JQuery css cover
      :Access public
      r←(sel #._JSS.JQuery'css')args
    ∇

    ∇ r←sel Val args ⍝ JQuery val cover
      :Access public
      r←(sel #._JSS.JQuery'val')args
    ∇

    ∇ r←sel Prop args ⍝ JQuery prop cover
      :Access public
      r←(sel #._JSS.JQuery'prop')args
    ∇

    ∇ r←sel Attr args ⍝ JQuery attr cover
      :Access public
      r←(sel #._JSS.JQuery'attr')args
    ∇

    ∇ r←sel RemoveAttr args ⍝ JQuery removeAttr cover
      :Access public
      r←(sel #._JSS.JQuery'removeAttr')args
    ∇

    ∇ r←sel Html args ⍝ JQuery html cover
      :Access public
      r←(sel #._JSS.JQuery'html')args
    ∇

    ∇ r←sel Show args ⍝ JQuery show cover
      :Access public
      r←(sel #._JSS.JQuery'show')args
    ∇

    ∇ r←sel Hide args ⍝ JQuery hide cover
      :Access public
      r←(sel #._JSS.JQuery'hide')args
    ∇

    ∇ r←sel Toggle args ⍝ JQuery toggle cover
      :Access public
      r←(sel #._JSS.JQuery'toggle')args
    ∇

    ∇ r←Submit sel ⍝ JQuery submit cover
      :Access public
      r←(sel #._JSS.JQuery'submit')''
    ∇

    :endsection

:EndClass

:Class MiPage : #.HtmlPage

    ⍝∇:require =\HtmlPage.dyalog
    ⍝∇:require =\JSON.dyalog

    :Field Public _PageName←'' ⍝ Page file name
    :Field Public _PageDate←'' ⍝ Page saved date
    :field Public _Request     ⍝ HTTPRequest
    :field Public _Scripts←''
    :field Public _Styles←''
    :field Public _CssReset←''    ⍝ location of CSS Reset file (if any)
    :field Public _CssOverride←'' ⍝ location of CSS Override file (if any)
    :field Public _Serialized←1   ⍝ serialized forms to return in _PageData
    :field Public _event          ⍝ set by APLJAX callback - event that was triggered
    :field Public _what           ⍝ set by APLJAX callback - name or id of the triggering element
    :field Public _value          ⍝ set by APLJAX callback - value of the triggering element
    :field Public _selector       ⍝ set by APLJAX callback - CSS/jQuery selector for the element that triggered the event
    :field Public _callback       ⍝ set by APLJAX callback - name of the callback function
    :field Public _target         ⍝ set by APLJAX callback - id of the target element
    :field Public _currentTarget  ⍝ set by APLJAX callback - id of the currentTarget element
    :field Public _PageData       ⍝ namespace containing any data passed via forms or URL
    :field Public _AjaxResponse←''
    :field Public _DebugCallbacks←0
    :field Public _TimedOut←0
    :field Public _Sessioned←1    ⍝ by default save page instance in session
    :field Public _cache←''       ⍝ cached content if page is marked cacheable
    :field Public OnLoad←''       ⍝ page equivalent to ⎕LX
    :field Public Cacheable←0     ⍝ is the page cacheable?
    :field Public Charset←'UTF-8' ⍝ charset for page

    _used←'' ⍝ track what's been Use'd

    ∇ Make
      :Access public
      :Implements constructor :Base
      MakeCommon
    ∇

    ∇ Make1 req
      :Access public
      _Request←req
      :Implements constructor :base
      MakeCommon
    ∇

    ∇ MakeCommon
      _PageData←⎕NS''
    ∇

    ∇ {r}←Render;b;styles;_head;_body;_styles;_scripts;t;i
      :Access public
   ⍝  Capture the current Head and Body content so that we can reset it after rendering
   ⍝  This is so we can re-render and still get the same result

      (_head _body)←(Head Body).Content
      (_styles _scripts)←_Styles _Scripts

      :If ''≢OnLoad
          {0:: ⋄ Use'JQuery'}''
      :EndIf

      b←RenderBody

      :If ~0∊⍴_Scripts
          {(Insert _html.script).Set('src'⍵)}¨⌽∪_Scripts
      :EndIf

      styles←∪_Styles
      styles←styles,⍨{0∊⍴⍵:⍵ ⋄ ⊂⍵}_CssReset
      styles←styles,{0∊⍴⍵:⍵ ⋄ ⊂⍵}_CssOverride

      :If ~0∊⍴styles
          {Insert #._DC.StyleSheet ⍵}¨⌽∪styles
      :EndIf

      t←''
      :If ~0∊⍴Handlers
          t←,∊Handlers.Render
      :EndIf
      :If ~0∊⍴OnLoad
          t,←(⎕NEW #._html.script('$(function(){',OnLoad,'});')).Render
      :EndIf
      :If ~0∊⍴t
          :If '</body>'≡¯7↑b
              i←¯7
          :Else
              i←-6+⊃⍸'>ydob/<'⍷⌽b
          :EndIf
          b←(i↓b),t,i↑b
      :EndIf

      :If ''≢OnLoad

      :EndIf

      :If 0∊⍴⊃Attrs[⊂'lang'] ⍝ set the language for the page if not already set
          {0:: ⋄ {Set'lang="',⍵,'" xml:lang="',⍵,'" xmlns="http://www.w3.org/1999/xhtml"'}_Request.Server.Config.Lang}''
      :EndIf

      :If ~0∊⍴Charset
          Insert _html.meta''('charset="',Charset,'"')
      :EndIf

      r←RenderPage b

      :If 0≠⎕NC⊂'_Request.Response'
          _Request.Response.HTML←r
      :EndIf

      (Head Body).Content←_head _body
      _Styles←_styles
      _Scripts←_scripts
      _used←''
    ∇

    ∇ {r}←Wrap
      :Access public
      r←Render
      :If Cacheable ⋄ _cache←r ⋄ :EndIf
    ∇

    ∇ Use resources;n;ind;t;x;server
      :Access public
      resources←eis resources
      :For x :In resources
          :If ~(⊂x)∊_used
              :Select ⊃x
              :Case '⍎' ⍝ script
                  _Scripts,←⊂1↓x
              :Case '⍕' ⍝ style sheet
                  _Styles,←⊂1↓x
              :Else
                  :If 0≠⎕NC'_Request.Server'
                      server←_Request.Server
                  :ElseIf 0≠⎕NC'#.Boot.ms'
                      server←#.Boot.ms
                  :EndIf
                  :If 0≠server.⎕NC⊂'Config.Resources'
                  :AndIf ~0∊n←1↑⍴server.Config.Resources
                      :If n≥ind←server.Config.Resources[;1]⍳⊂x
                          :If ~0∊⍴t←{(~0∘∊∘⍴¨⍵)/⍵}(⊂ind 2)⊃server.Config.Resources
                              _Scripts,⍨←t
                          :EndIf
                          :If ~0∊⍴t←{(~0∘∊∘⍴¨⍵)/⍵}(⊂ind 3)⊃server.Config.Resources
                              _Styles,⍨←t
                          :EndIf
                      :Else
                          1 server.Log _PageName,' references unknown resource: ',x
                      :EndIf
                  :EndIf
              :EndSelect
              _used,←⊂x
          :EndIf
      :EndFor
    ∇

    ∇ r←{proto}Get names;noproto;char
    ⍝ names is a single name, a vector of vectors of names, or a space-delimited list of names
    ⍝ {proto} is the optional prototypical value if a name is not found
      :Access public
      :If noproto←0=⎕NC'proto' ⋄ proto←'' ⋄ :EndIf ⍝ default prototype is empty
      names←eis names
      names←,⍕names
      names←#.Strings.deb names
      :If ' '∊names
          names←' '(≠⊆⊢)names
          r←({⍬∘⍴⍣(1=≢⍵)⊢⍵}eis proto)Get¨names
      :ElseIf ~(_PageData.⎕NC names)∊2 9 ⍝ by this time, names is a single name
          r←,proto
      :Else
          r←_PageData⍎names
          :If 2≤≡r
              :If 1=⍴,r
                  r←⊃r
              :EndIf
          :EndIf
⍝↓↓↓ BPB - we don't want to gratuitously convert - e.g. 'null' 'true' '0002010'
⍝          :If noproto≥char←isChar proto
⍝          :AndIf isString r
⍝              r←#.JSON.toAPL r
⍝          :EndIf
          :If noproto⍱char
              :If 1≠2|⎕DR r
                  r←,proto
              :EndIf
          :EndIf
      :EndIf
    ∇

    ∇ r←GetNames str
      :Access public
      →0⍴⍨0∊⍴r←_PageData.⎕NL-2 9
      →0⍴⍨0∊⍴str
      r←r/⍨r #.Strings.beginsWith¨⊂str
    ∇

    ∇ r←{proto}GetRaw names
      :Access public
      proto←{6::⍵ ⋄ proto}''
      names←eis names
      names←,⍕names
      names←#.Strings.deb names
      :If ' '∊names
          names←{⎕ML←3 ⋄ ⍵⊂⍨⍵≠' '}names
          r←proto∘Get¨names
      :ElseIf 2≠_PageData.⎕NC names
          r←,proto
      :Else
          r←_PageData⍎names
          :If 2=≡r
              :If 1=⍴,r
                  r←⊃r
              :EndIf
          :EndIf
          :If ~isChar proto
              r←{0::⍵ ⋄ 0∊⍴⍵:⍬ ⋄ w←⍵ ⋄ ((w='-')/w)←'¯' ⋄ ⊃(//)⎕VFI w}r
          :EndIf
      :EndIf
    ∇

    ∇ r←{proto}SessionGet names
      :Access public
      proto←{6::⍵ ⋄ proto}''
      names←,⍕names
      names←#.Strings.deb names
      :If ' '∊names
          names←{⎕ML←3 ⋄ ⍵⊂⍨⍵≠' '}names
          r←proto∘SessionGet¨names
      :ElseIf 0=_Request.⎕NC⊂'Session'
      :OrIf 0=_Request.Session.⎕NC names
          r←proto
      :Else
          r←_Request.Session⍎names
          :If 1<|≡r ⋄ r←∊r ⋄ :EndIf
          :If ~0 2∊⍨10|⎕DR proto  ⍝ if the prototype is numeric
          :AndIf 0 2∊⍨10|⎕DR r    ⍝ and the element is character
              r←{0∊⍴⍵:⍬ ⋄ w←⍵ ⋄ ((w='-')/w)←'¯' ⋄ ⊃(//)⎕VFI w}r
          :EndIf
      :EndIf
    ∇

    ∇ _Close session ⍝ Called when the session ends
    ⍝ this method is specific to #.SimpleSessions and is called when a session is terminated
      :Access Public Overridable
    ∇

    :section APLJax  ⍝ used for building APLJAX responses
    ∇ _resetAjax
      :Access public
      _AjaxResponse←''
    ∇

    ∇ r←_id
      :Access public
      ⍝ as there seems to be a problem with at least some Syncfusion widgets
      ⍝ being able to provide the id (or name) of the triggering element, we try to obtain it from _what or _selector
      r←''
      :Trap 0
          :If 0∊⍴r←_what
          :AndIf '#'=⊃_selector
              r←1↓_selector
          :EndIf
      :EndTrap
    ∇

    ∇ r←renderContent content;c
      :Access public shared
      r←''
      content←eis content
      :Trap 0
          :While ~0∊⍴content
              :Select ≡c←⊃content
              :Case 0
                  :If isClass c
                      :Select ⊃⍴content
                      :Case 1
                          r,←(⎕NEW c).Render
                      :Case 2
                          r,←(⎕NEW c(2⊃content)).Render
                      :Else
                          r,←(⎕NEW c(1↓content)).Render
                      :EndSelect
                  :ElseIf isInstance c
                      r,←c.Render
                  :Else
                      r,←(⎕NEW #.HtmlElement(''content)).Render
                  :EndIf
                  content←''
              :Case 1
                  :If isClass⊃c
                      r,←(⎕NEW(⊃c)(1↓c)).Render
                  :ElseIf isInstance⊃c
                      ∘∘∘ ⍝ should not happen! (I think)
                  :Else
                      r,←(⎕NEW #.HtmlElement(''c)).Render
                  :EndIf
                  content←1↓content
              :Else
                  r,←renderContent c
                  content←1↓content
              :EndSelect
          :EndWhile
      :Else
          :If (⎕EN≥100)∧(⎕EN≤999) ⍝ HTTP status rather than an APL error
          :AndIf 0≠⎕NC⊂'_Request.Response' ⍝ we're actually running a server, not rendering in session
              _Request.Fail ⎕EN
          :Else ⍝ APL errors and in-session errors are not trapped:
              ⎕SIGNAL ⎕DMX.(⊂{⍵(⍎⍵)}¨(⎕NL-⍳9)~'InternalLocation' 'DM' 'OSError') ⍝ re-signal
          :EndIf
      :EndTrap
    ∇

    ∇ r←selector Replace content
      :Access public shared
      r←⊂('replace'selector)('data'(renderContent content))
    ∇
    ∇ r←selector Append content
      :Access public shared
      r←⊂('append'selector)('data'(renderContent content))
    ∇
    ∇ r←selector Prepend content
      :Access public shared
      r←⊂('prepend'selector)('data'(renderContent content))
    ∇
    ∇ r←Execute content
      :Access public shared
      r←⊂('execute'(renderContent content))
    ∇

    ∇ r←name Assign data
      :Access public shared
      r←⊂('assign'name)('data'data)
    ∇

    :endsection

    :section Position

    ∇ ref Position args;inds;mask;parameters;my;at;of;collision;within;q
      :Access public
      ⍝ ref  - a reference to an instance of anything based on HtmlElement
      ⍝ args - position information per jQueryUI's Position widget http://api.jqueryui.com/position/
      ⍝        can be in any of the following forms
      ⍝      1) positional (my at of collision within)  N.B. we don't use the "using" parameter
      ⍝         example:  myDiv Position 'left top' 'right bottom' '#otherElement'
      ⍝                   positions myDiv's top left corner at the bottom right corner of the element with id "otherElement"
      ⍝      2) paired
      ⍝                   myDiv Position 'my' 'left top' 'at' 'right bottom' 'of' '#otherElement'
      ⍝                   myDiv Position ('my' 'left top') ('at' 'right bottom') ('of' '#otherElement')
      ⍝                   myDiv Position 3 2⍴'my' 'left top' 'at' 'right bottom' 'of' '#otherElement'
      ⍝ Note: positional arguments are in form horizontal (left center right) vertical (top center bottom)

      parameters←'my' 'at' 'of' 'collision' 'within'
      q←{1⌽'''''',{⍵/⍨1+''''=⍵}⍕⍵}
      :If isInstance ref
          :If 2=⍴⍴args ⍝ matrix
              args←,args
          :ElseIf 3=≡args
              args←⊃,/args
          :EndIf
          args←eis args
          inds←parameters⍳args
          :If ∨/mask←inds≤⍴parameters
              :If mask≡(2×+/mask)⍴1 0
                  parameters←mask/args
                  args←(1⌽mask)/args
              :EndIf
          :Else
              parameters←(⍴args)↑parameters
          :EndIf
          mask←⍬∘≢¨args
          args←mask/args
          parameters←mask/parameters
          parameters(ref{⍺⍺⍎'Position.',⍺,'←',q ⍵})¨args
          ref.Uses,←⊂'JQueryUI'
          SetUse
      :EndIf
    ∇
    :endsection

    :section Event Handling Support
    ∇ r←isPost
      :Access public
      r←{0::0 ⋄ _Request.isPost}⍬
    ∇

    ∇ r←isAPLJax
      :Access public
      r←{0::0 ⋄ _Request.isAPLJAX}⍬
    ∇

    ∇ r←sel Css args ⍝ JQuery css cover
      :Access public
      r←(sel #._JSS.JQuery'css')args
    ∇

    ∇ r←sel Val args ⍝ JQuery val cover
      :Access public
      r←(sel #._JSS.JQuery'val')args
    ∇

    ∇ r←sel Prop args ⍝ JQuery prop cover
      :Access public
      r←(sel #._JSS.JQuery'prop')args
    ∇

    ∇ r←sel Attr args ⍝ JQuery attr cover
      :Access public
      r←(sel #._JSS.JQuery'attr')args
    ∇

    ∇ r←sel RemoveAttr args ⍝ JQuery removeAttr cover
      :Access public
      r←(sel #._JSS.JQuery'removeAttr')args
    ∇

    ∇ r←sel Html args ⍝ JQuery html cover
      :Access public
      r←(sel #._JSS.JQuery'html')args
    ∇

    ∇ r←sel Show args ⍝ JQuery show cover
      :Access public
      r←(sel #._JSS.JQuery'show')args
    ∇

    ∇ r←sel Hide args ⍝ JQuery hide cover
      :Access public
      r←(sel #._JSS.JQuery'hide')args
    ∇

    ∇ r←sel Toggle args ⍝ JQuery toggle cover
      :Access public
      r←(sel #._JSS.JQuery'toggle')args
    ∇

    ∇ r←Submit sel ⍝ JQuery submit cover
      :Access public
      r←(sel #._JSS.JQuery'submit')''
    ∇

    :endsection

:EndClass


SeleniumTests.Test now allows 2 as left arg to stop at beginning of each test.

:Namespace SeleniumTests

    ∇ x←eis x
⍝ Enclose if simple
      :If (≡x)∊0 1 ⋄ x←,⊂,x ⋄ :EndIf
    ∇

    ∇ r←lopFirst url
    ⍝ remove first segment of URL
      r←{⍵/⍨(1+'/'=1↑⍵)≤+\'/'=⍵}url
    ∇

    ∇ r←stop Run1Test page;name;ref;Test
     ⍝ eg MS3Test '/QA/DC/InputGridSimple'

      Selenium.GoTo SITE,lopFirst page ⍝ Drop the "QA"
      :If 'Test'≡name←⎕SE.SALT.Load AppRoot,page
          :Trap stop×9999
              'Test'⎕STOP⍨1/⍨2=stop ⍝ stop on line 1 if stop=2
              :If stop⌊0≠⍴r←Test ⍬
                  ⎕←'test for ',page,' failed:' ⋄ ⎕←r ⋄ ⎕←'Rerun:' ⋄ '      Test ⍬'
                  ∘∘∘
              :EndIf
          :Else
              r←'Trapped error: ',,⍕⎕DMX.EN
          :EndTrap
      :Else ⋄ r←AppRoot,page,' does not define a function called Test'
      :EndIf
    ∇

    ∇ r←FindAllFiles root;folders;ext
      :If 0=⎕NC'Config.DefaultExtension' ⋄ ext←'.dyalog'
      :Else ⋄ ext←Config.DefaultExtension
      :EndIf
      root,←'/'/⍨~'/\'∊⍨¯1↑root ⍝ append trailing / if missing
      r←root∘,¨(('*',ext)#.Files.List root)[;1]
      :If 0≠⍴folders←{(('.'≠⊃¨⍵[;1])∧⍵[;4])/⍵[;1]}#.Files.List root
          r←r,⊃,/FindAllFiles¨root∘,¨folders
      :EndIf
    ∇

    ∇ r←stop_port Test site;count;ctl;examples;f;fail;nodot;start;t;time;z;i;START;COUNT;FAIL;Config;selpath;files;n;ext;filter;⎕PATH;keynames;maxlen;⎕USING;stopOnError;stop
      ⍝ stop: 0 (default) ignore but report errors; 1 stop on error; 2 stop before every test

      stop←⊃stop_port
      r←''
      (site filter)←2↑(eis site),'' ''
      :If 0=⍴AppRoot←#.Load site
          ⎕←'Test abandoned' ⋄ →0
      :EndIf

      selpath←({∊'/',⍨¨¯1↓'/\'#.Utils.penclose ⍵}#.Boot.MSRoot),'Selenium/'
      :If 0=⎕NC'Selenium'
          :Trap 0 ⋄ ⎕SE.SALT.Load selpath,'/Selenium'
          :Else
              ⎕←'Selenium library not found at: ',selpath
              →0⍴⍨0∊⍴selpath←{⍞↓⍨⍴⍞←⍵,': '}'Selenium path'
              :Trap 0 ⋄ ⎕SE.SALT.Load selpath,'/Selenium'
              :Else ⋄ ⎕←'Unable to load Selenium' ⋄ →0
              :EndTrap
          :EndTrap
      :EndIf
      ⎕PATH,←' Selenium'
      Selenium.DLLPATH←selpath

      Config←#.Boot.ConfigureServer AppRoot
      ext←Config.DefaultExtension
      Config.DefaultExtension←'.dyalog' ⍝ We are searching for code
      n←⍴files←(⍴AppRoot)↓¨¯7↓¨FindAllFiles AppRoot,'QA'
      ⍝ // Add code to compare this to the mipages found in the whole app
      :If 0≠⍴filter
          files←(filter ⎕S'%')files
          ⎕←'Selected: ',(⍕⍴files),' of ',(⍕n),' tests.'
      :EndIf
      n←⍴files
      SITE←'http://127.0.0.1:',⍕⊃1↓stop_port,Config.Port

⍝⍝ Un-comment to play music while testing:
⍝      :If site filter≡'MS3' ''
⍝          ⎕CMD('"\Program Files (x86)\Windows Media Player\wmplayer.exe" "',AppRoot,'\Examples\Data\tellintro.mp3"')''
⍝      :EndIf

      Selenium.InitBrowser''
      Selenium.BROWSER.Manage.Window.Maximize

     ⍝ Localize non-alphanumeric key names for easy access
      keynames←⍕#.SeleniumTests.Selenium.Keys.⎕NL ¯2
      ⍎keynames,'←∊#.SeleniumTests.Selenium.Keys.(',keynames,')'

      START←⎕AI[3] ⋄ COUNT←0 ⋄ FAIL←0
      maxlen←¯2+⌈/⊃∘⍴¨files

      :For i :In ⍳n
          COUNT+←1
          :If 0=⍴t←stop Run1Test{⍵⊣⍞←(⎕UCS 13),maxlen↑lopFirst ⍵}z←i⊃files
              ⍞←'*** PASSED ***'
          :Else
              FAIL+←1
              r,←⊂z
              ⍞←'*** FAILED *** #',(⍕i),' of ',(⍕n),': ',z,': ',t
          :EndIf
      :EndFor

      ⎕←'Total of ',(⍕COUNT),' samples tested in ',(∊(⍕¨24 60⊤⌊0.5+(⎕AI[3]-START)÷1000),¨'ms'),': ',(⍕FAIL),' failed.'

      Selenium.BROWSER.Quit
    ∇

:EndNamespace

:Namespace SeleniumTests

    ∇ x←eis x
⍝ Enclose if simple
      :If (≡x)∊0 1 ⋄ x←,⊂,x ⋄ :EndIf
    ∇

    ∇ r←lopFirst url
    ⍝ remove first segment of URL
      r←{⍵/⍨(1+'/'=1↑⍵)≤+\'/'=⍵}url
    ∇

    ∇ r←Run1Test page;name;ref;Test
     ⍝ eg MS3Test '/QA/DC/InputGridSimple'

      Selenium.GoTo SITE,lopFirst page ⍝ Drop the "QA"
      :If 'Test'≡name←⎕SE.SALT.Load AppRoot,page
          :Trap stopOnError×9999
              :If stopOnError∧0≠⍴r←Test ⍬
                  ⎕←'test for ',page,' failed:' ⋄ ⎕←r ⋄ ⎕←'Rerun:' ⋄ '      Test ⍬'
                  ∘∘∘
              :EndIf
          :Else
              r←'Trapped error: ',,⍕⎕DMX.EN
          :EndTrap
      :Else ⋄ r←AppRoot,page,' does not define a function called Test'
      :EndIf
    ∇

    ∇ r←FindAllFiles root;folders;ext
      :If 0=⎕NC'Config.DefaultExtension' ⋄ ext←'.dyalog'
      :Else ⋄ ext←Config.DefaultExtension
      :EndIf
      root,←'/'/⍨~'/\'∊⍨¯1↑root ⍝ append trailing / if missing
      r←root∘,¨(('*',ext)#.Files.List root)[;1]
      :If 0≠⍴folders←{(('.'≠⊃¨⍵[;1])∧⍵[;4])/⍵[;1]}#.Files.List root
          r←r,⊃,/FindAllFiles¨root∘,¨folders
      :EndIf
    ∇

    ∇ r←stopOnError_port Test site;count;ctl;examples;f;fail;nodot;start;t;time;z;i;START;COUNT;FAIL;Config;selpath;files;n;ext;filter;⎕PATH;keynames;maxlen;⎕USING;stopOnError
      stopOnError←⊃stopOnError_port
      r←''
      (site filter)←2↑(eis site),'' ''
      :If 0=⍴AppRoot←#.Load site
          ⎕←'Test abandoned' ⋄ →0
      :EndIf

      selpath←({∊'/',⍨¨¯1↓'/\'#.Utils.penclose ⍵}#.Boot.MSRoot),'Selenium/'
      :If 0=⎕NC'Selenium'
          :Trap 0 ⋄ ⎕SE.SALT.Load selpath,'/Selenium'
          :Else
              ⎕←'Selenium library not found at: ',selpath
              →0⍴⍨0∊⍴selpath←{⍞↓⍨⍴⍞←⍵,': '}'Selenium path'
              :Trap 0 ⋄ ⎕SE.SALT.Load selpath,'/Selenium'
              :Else ⋄ ⎕←'Unable to load Selenium' ⋄ →0
              :EndTrap
          :EndTrap
      :EndIf
      ⎕PATH,←' Selenium'
      Selenium.DLLPATH←selpath

      Config←#.Boot.ConfigureServer AppRoot
      ext←Config.DefaultExtension
      Config.DefaultExtension←'.dyalog' ⍝ We are searching for code
      n←⍴files←(⍴AppRoot)↓¨¯7↓¨FindAllFiles AppRoot,'QA'
      ⍝ // Add code to compare this to the mipages found in the whole app
      :If 0≠⍴filter
          files←(filter ⎕S'%')files
          ⎕←'Selected: ',(⍕⍴files),' of ',(⍕n),' tests.'
      :EndIf
      n←⍴files
      SITE←'http://127.0.0.1:',⍕⊃1↓stopOnError_port,Config.Port

⍝⍝ Un-comment to play music while testing:
⍝      :If site filter≡'MS3' ''
⍝          ⎕CMD('"\Program Files (x86)\Windows Media Player\wmplayer.exe" "',AppRoot,'\Examples\Data\tellintro.mp3"')''
⍝      :EndIf

      Selenium.InitBrowser''
      Selenium.BROWSER.Manage.Window.Maximize

     ⍝ Localize non-alphanumeric key names for easy access
      keynames←⍕#.SeleniumTests.Selenium.Keys.⎕NL ¯2
      ⍎keynames,'←∊#.SeleniumTests.Selenium.Keys.(',keynames,')'

      START←⎕AI[3] ⋄ COUNT←0 ⋄ FAIL←0
      maxlen←¯2+⌈/⊃∘⍴¨files

      :For i :In ⍳n
          COUNT+←1
          :If 0=⍴t←Run1Test{⍵⊣⍞←(⎕UCS 13),maxlen↑lopFirst ⍵}z←i⊃files
              ⍞←'*** PASSED ***'
          :Else
              FAIL+←1
              r,←⊂z
              ⍞←'*** FAILED *** #',(⍕i),' of ',(⍕n),': ',z,': ',t
          :EndIf
      :EndFor

      ⎕←'Total of ',(⍕COUNT),' samples tested in ',(∊(⍕¨24 60⊤⌊0.5+(⎕AI[3]-START)÷1000),¨'ms'),': ',(⍕FAIL),' failed.'

      Selenium.BROWSER.Quit
    ∇

:EndNamespace