Updated MiPageSample to format SF docn liks better

: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
          Insert _.StyleSheet'/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'id="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"'

      :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
      ctrls←⊃{⍺ ⍵}⌸/↓[1]0 1↓↑{⎕ML←3 ⋄ {⍵⊂⍨⍵≠'.'}⍕⍵}¨controls
      field←{0::'' ⋄ ⍺⍎⍵}
      ns←'_html' '_DC' '_JQ' '_SF'
      desc←'Native HTML5 Elements' 'Dyalog Controls' 'jQuery Widgets' 'Syncfusion Widgets'
      (r←⎕NEW _.div).class←'widgethelp'
      r.Add'This Page Contains<hr>'
      :For n ctrl i :In ctrls{↓(⍺,⍵)[⍋⍵;]}ns⍳{(⊃⍣(¯1+≡⍵))⍵}¨ctrls[;1]
          (r.Add _.span(i⊃desc)).class←'widgetNs'
          u←r.Add _.ul
          :For c :In ctrl
              ref←(#.⍎⊃n).⍎c
              :If ''≢l←ref field'MSDoc'
              :AndIf #.Files.Exists #.Boot.MSRoot,l
                  u.Add _.li(_.a c(('href'('..',l))('target' '_blank')))
              :ElseIf ''≢l←ref field'BaseDoc'
              :AndIf #.Files.Exists #.Boot.MSRoot,l
                  u.Add _.li(_.a c(('href'('..',l))('target' '_blank')))
              :Else
                  :If 'ej'≡2↑c←#.Strings.lc c
                      u.Add _.li(New _.a c(('href'('http://js.syncfusion.com/demos/web/#!/azure/',2↓c⊣,'/defaultfunctionalities'))('target' '_blank')))
                  :Else
                      u.Add _.li c
                  :EndIf
              :EndIf
          :EndFor
      :EndFor
    ∇

: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
          Insert _.StyleSheet'/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'id="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"'

      :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
      ctrls←⊃{⍺ ⍵}⌸/↓[1]0 1↓↑{⎕ML←3 ⋄ {⍵⊂⍨⍵≠'.'}⍕⍵}¨controls
      field←{0::'' ⋄ ⍺⍎⍵}
      ns←'_html' '_DC' '_JQ' '_SF'
      desc←'Native HTML5 Elements' 'Dyalog Controls' 'jQuery Widgets' 'Syncfusion Widgets'
      (r←⎕NEW _.div).class←'widgethelp'
      r.Add'This Page Contains<hr>'
      :For n ctrl i :In ctrls{↓(⍺,⍵)[⍋⍵;]}ns⍳{(⊃⍣(¯1+≡⍵))⍵}¨ctrls[;1]
          (r.Add _.span(i⊃desc)).class←'widgetNs'
          u←r.Add _.ul
          :For c :In ctrl
              ref←(#.⍎⊃n).⍎c
              :If ''≢l←ref field'MSDoc'
              :AndIf #.Files.Exists #.Boot.MSRoot,l
                  u.Add _.li(_.a c(('href'('..',l))('target' '_blank')))
              :ElseIf ''≢l←ref field'BaseDoc'
              :AndIf #.Files.Exists #.Boot.MSRoot,l
                  u.Add _.li(_.a c(('href'('..',l))('target' '_blank')))
              :Else
                  :If 'ej'≡2↑c←#.Strings.lc c
                      u.Add _.li(_.a c(('href'('http://js.syncfusion.com/demos/web/#!/azure/',2↓c⊣,'/defaultfunctionalities'))('target' '_blank')))
                  :Else
                      u.Add _.li c
                  :EndIf
              :EndIf
          :EndFor
      :EndFor
    ∇

:EndClass


Documented ReplaceOptions-Method.

Documented ReplaceOptions-Method.

:class Select : #._html.select
⍝ Description:: Dyalog Enhanced HTML select
⍝ Constructor:: [options [[selected] [[disabled] [prompt]]]]
⍝ options   - vector of options or 2 column matrix of displayed[;1] and returned[;2] values
⍝ selected  - Boolean or integer array indicating pre-selected options(s)
⍝ disabled  - Boolean or integer array indicating disabled options(s)
⍝ prompt    - first item to display (has no value) (default '[Select]')
⍝
⍝ Public Fields::
⍝ Options   - vector of options or 2 column matrix of displayed[;1] and returned[;2] values
⍝ Selected  - Boolean or integer array indicating pre-selected options(s)
⍝ Disabled  - Boolean or integer array indicating disabled options(s)
⍝ Prompt      - first item to display (has no value) (default '[Select]')
⍝
⍝ Public Methods::
⍝ ReplaceOptions options [[selected] [disabled]]
⍝ Arguments as discussed above
⍝
⍝ Examples::
⍝ Select  ('Choice 1' 'Choice 2' 'Choice 3')
⍝ Select  (3 2⍴'One' 'c1' 'Two' 'c2' 'Three' 'c3')
⍝ Select ((3 2⍴'One' 'c1' 'Two' 'c2' 'Three' 'c3') 2) ⍝ second item is selected
⍝ Select ((3 2⍴'One' 'c1' 'Two' 'c2' 'Three' 'c3') (0 1 0)) ⍝ second item is selected
⍝ Select ((3 2⍴'One' 'c1' 'Two' 'c2' 'Three' 'c3') 2 3 'Pick One') ⍝ second item is selected, third item is disabled
⍝ Select ((3 2⍴'One' 'c1' 'Two' 'c2' 'Three' 'c3') (2 3) 1) ⍝ second and third items are selected, first item is disabled

    :field public Options←0 2⍴⊂''     ⍝ vector or matrix [;1] display, [;2] value
    :field public Selected←⍬          ⍝ either Boolean integer vector indicating
    :field public Prompt←'[Select]'   ⍝ character vector "Prompt" choice - the first choice on the list and does not have a value
    :field public Disabled←⍬          ⍝ either Boolean integer vector indicating

    ∇ make
      :Access public
      :Implements constructor
    ∇

    ∇ make1 args;attr
      :Access public
      :Implements constructor
      args←eis args
      :If (|≡args)∊0 1 2
      :AndIf ~0∊⍴⊃args
          args←,⊂args
      :EndIf
      (Options Selected Disabled Prompt)←args defaultArgs Options Selected Disabled Prompt
    ∇

    ∇ r←Render;opts;sel
      :Access public

      Content←opts←''
      SetInputName
      Add BuildOptions(Options Selected Disabled)

      r←⎕BASE.Render
    ∇

    ∇ r←BuildOptions(opts sel dis);v
      r←''
      :If ~0∊⍴opts
          opts←eis opts
          :If 1=⍴⍴opts
              opts←opts,⍪opts
          :EndIf
      :EndIf

      v←⍳⍬⍴⍴opts
      (sel dis)←v∘∊∘{∧/⍵∊0 1:⍵/⍳⍴⍵ ⋄ ⍵}∘,¨sel dis

      :If 1<+/sel                         ⍝ if we have multiple items selected
      :AndIf 0∊⍴⊃Attrs[⊂'multiple']       ⍝ and the multiple attribute is not set
          Attrs[⊂'multiple']←⊂'multiple'  ⍝ then set it
      :EndIf

      :If ~0∊⍴Prompt
      :AndIf 0∊⍴⊃Attrs[⊂'multiple']  ⍝ prompt makes no sense if multiple selections are allowed
          r,←('disabled="disabled" selected="selected"'New #._html.option Prompt).Render
      :EndIf

      r,←FormatOptions(opts sel dis)
    ∇

    ∇ r←FormatOptions(opts sel dis);o;s;d
      :Access Public Shared
      r←''
      :If 1=⍴⍴opts ⋄ opts←opts,⍪opts ⋄ :EndIf
      :For o s d :InEach (↓opts)sel dis
          r,←'<option',({⍵ ine' value="',(HtmlSafeText ⍵),'"'}2⊃o),(s/' selected="selected"'),(d/' disabled="disabled"'),'>',(1⊃o),'</option>'
      :EndFor
    ∇

    ∇ r←{selector}ReplaceOptions args;sel;opts;dis
⍝     Replaces select elements options - used by callback functions
⍝ Ex: r←Execute ReplaceOptions ('New Option 1' 'New Option 2') 1
⍝     arg = options [[selected] [disabled]]
      :Access public
      :If 0=⎕NC'selector' ⋄ selector←'#',id ⋄ :EndIf
      args←eis args
      :If (|≡args)∊0 1 2
      :AndIf ~0∊⍴⊃args
          args←,⊂args
      :EndIf
      (opts sel dis)←args defaultArgs Options ⍬ ⍬
      r←selector #.JQ.Replace BuildOptions(opts sel dis)
    ∇
:endclass

:class Select : #._html.select
⍝ Description:: Dyalog Enhanced HTML select
⍝ Constructor:: [options [[selected] [[disabled] [prompt]]]]
⍝ options   - vector of options or 2 column matrix of displayed[;1] and returned[;2] values
⍝ selected  - Boolean or integer array indicating pre-selected options(s)
⍝ disabled  - Boolean or integer array indicating disabled options(s)
⍝ prompt    - first item to display (has no value) (default '[Select]')
⍝
⍝ Public Fields::
⍝ Options   - vector of options or 2 column matrix of displayed[;1] and returned[;2] values
⍝ Selected  - Boolean or integer array indicating pre-selected options(s)
⍝ Disabled  - Boolean or integer array indicating disabled options(s)
⍝ Prompt      - first item to display (has no value) (default '[Select]')
⍝
⍝ Examples::
⍝ Select  ('Choice 1' 'Choice 2' 'Choice 3')
⍝ Select  (3 2⍴'One' 'c1' 'Two' 'c2' 'Three' 'c3')
⍝ Select ((3 2⍴'One' 'c1' 'Two' 'c2' 'Three' 'c3') 2) ⍝ second item is selected
⍝ Select ((3 2⍴'One' 'c1' 'Two' 'c2' 'Three' 'c3') (0 1 0)) ⍝ second item is selected
⍝ Select ((3 2⍴'One' 'c1' 'Two' 'c2' 'Three' 'c3') 2 3 'Pick One') ⍝ second item is selected, third item is disabled
⍝ Select ((3 2⍴'One' 'c1' 'Two' 'c2' 'Three' 'c3') (2 3) 1) ⍝ second and third items are selected, first item is disabled

    :field public Options←0 2⍴⊂''     ⍝ vector or matrix [;1] display, [;2] value
    :field public Selected←⍬          ⍝ either Boolean integer vector indicating
    :field public Prompt←'[Select]'   ⍝ character vector "Prompt" choice - the first choice on the list and does not have a value
    :field public Disabled←⍬          ⍝ either Boolean integer vector indicating

    ∇ make
      :Access public
      :Implements constructor
    ∇

    ∇ make1 args;attr
      :Access public
      :Implements constructor
      args←eis args
      :If (|≡args)∊0 1 2
      :AndIf ~0∊⍴⊃args
          args←,⊂args
      :EndIf
      (Options Selected Disabled Prompt)←args defaultArgs Options Selected Disabled Prompt
    ∇

    ∇ r←Render;opts;sel
      :Access public

      Content←opts←''
      SetInputName
      Add BuildOptions(Options Selected Disabled)

      r←⎕BASE.Render
    ∇

    ∇ r←BuildOptions(opts sel dis);v
      r←''
      :If ~0∊⍴opts
          opts←eis opts
          :If 1=⍴⍴opts
              opts←opts,⍪opts
          :EndIf
      :EndIf

      v←⍳⍬⍴⍴opts
      (sel dis)←v∘∊∘{∧/⍵∊0 1:⍵/⍳⍴⍵ ⋄ ⍵}∘,¨sel dis

      :If 1<+/sel                         ⍝ if we have multiple items selected
      :AndIf 0∊⍴⊃Attrs[⊂'multiple']       ⍝ and the multiple attribute is not set
          Attrs[⊂'multiple']←⊂'multiple'  ⍝ then set it
      :EndIf

      :If ~0∊⍴Prompt
      :AndIf 0∊⍴⊃Attrs[⊂'multiple']  ⍝ prompt makes no sense if multiple selections are allowed
          r,←('disabled="disabled" selected="selected"'New #._html.option Prompt).Render
      :EndIf

      r,←FormatOptions(opts sel dis)
    ∇

    ∇ r←FormatOptions(opts sel dis);o;s;d
      :Access Public Shared
      r←''
      :If 1=⍴⍴opts ⋄ opts←opts,⍪opts ⋄ :EndIf
      :For o s d :InEach (↓opts)sel dis
          r,←'<option',({⍵ ine' value="',(HtmlSafeText ⍵),'"'}2⊃o),(s/' selected="selected"'),(d/' disabled="disabled"'),'>',(1⊃o),'</option>'
      :EndFor
    ∇

    ∇ r←{selector}ReplaceOptions args;sel;opts;dis
⍝     Replaces select elements options - used by callback functions
⍝ Ex: r←Execute ReplaceOptions ('New Option 1' 'New Option 2') 1
⍝     arg = options [[selected] [disabled]]
      :Access public
      :If 0=⎕NC'selector' ⋄ selector←'#',id ⋄ :EndIf
      args←eis args
      :If (|≡args)∊0 1 2
      :AndIf ~0∊⍴⊃args
          args←,⊂args
      :EndIf
      (opts sel dis)←args defaultArgs Options ⍬ ⍬
      r←selector #.JQ.Replace BuildOptions(opts sel dis)
    ∇
:endclass


index: click element and get proper list of examples

:Class index : MiPageSample

    ∇ GROUPS←GROUPS
      GROUPS←'Base HTML' 'Wrapped HTML' 'Dyalog Controls' 'JQueryUI' 'SyncFusion'
    ∇

    ∇ REFS←REFS
      REFS←_html _HTML _DC _JQ _SF
    ∇
      Section←{
          regex←'^\s*⍝\s*',⍺,':(:.*?)$((\R^⍝(?!\s*\w+::).*?$)*)'
          opts←('Mode' 'M')('DotAll' 1)
          res←(regex ⎕S'\1\2'⍠opts)⍵
          (1↓⊃res)~'⍝'
      }

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

      Use'ejTab' ⍝ May get added by callbacks

      newdiv←{⍵ New _.div}                                  ⍝ Make a div
      textspan←{'.textspan'New _.span ⍵}                    ⍝ And a span
      horz←{⍺←⊣ ⋄ r⊣(r←⍺ New _.StackPanel ⍵).Horizontal←1}  ⍝ Horizontal StackPanel

      mytit←'#title'Add _.title'MS3: About'

      Add _.StyleSheet'/Styles/homepage.css'

      ⍝(left mid right)←newdiv¨'leftBar' 'midBar' 'rightBar'
      (left mid)←newdiv¨'leftBar' 'midBar'

      sp←'mainSP'horz left mid⍝ right
      sp.Items[1⊣3].style←⊂'width: 200px; max-height: 450px;'
      sp.Items[2].style←⊂'margin: 5px;'
      sp.style←'height: 450px; width: 100%;'

      vp←Add _.StackPanel sp
      vp.style←'width:100%'

      PopulateLeft left
      PopulateMid mid
     ⍝ PopulateRight right
    ∇

    ∇ PopulateLeft thediv;class;depths;group;items;names;ref;samples;tv;vp
     ⍝ Populate the Left Bar

     ⍝ SEARCH FIELD ⍝⍝⍝

      (thediv.Add _.EditField'str').On'onSearch' ''('str' 'val')
      thediv.Add _.hr

     ⍝ SAMPLE APPS ⍝⍝⍝
      Apps←{0::⍬ ⋄ ¯7↓¨6⊃#.Files.DirX #.Boot.AppRoot,⍵}'/Examples/Apps/*.dyalog'
      thediv.Add textspan'Sample Apps'
      tv←thediv.Add _.ejTreeView(1,[1.5]Apps)
      tv.style←'max-height: 250px'
      tv.On'nodeSelect' 'onSelectApp'('node' 'eval' 'argument.id')

      thediv.Add _.hr

     ⍝ CONTROLS ⍝⍝⍝
      items←0 3⍴⍬
      ⍝:For (group ref) :In ('Base HTML'_html)('Wrapped HTML'_HTML)('Dyalog Controls'_DC)('JQueryUI'_JQ)('SyncFusion'_SF)
      :For (group ref) :InEach GROUPS REFS
          names←{({#.HtmlElement=⊃⊃⌽⎕CLASS ⍵}¨⍵)/⍵}ref.(⍎¨⎕NL ¯9.4)
          class←2↓⍕ref               ⍝ Remove leading #.
          names←(3+⍴class)↓¨⍕¨names  ⍝
          samples←class FindSamples names
          names←(⊂group),⊃,/(⊂¨names),¨samples
          depths←1,∊2,⍪(⍴¨samples)⍴¨3
          items⍪←depths,(⍪names),(⊂class)
      :EndFor
      thediv.Add textspan'Controls'
      tv←thediv.Add _.ejTreeView(0 ¯1↓Samples←items)
      tv.style←'max-height: 300px'
      tv.On'nodeSelect' 'onSelectSample'('node' 'eval' 'argument.id')

    ∇

    ∇ r←space FindSamples names;folder;i;samples;suffix
      space,←(space≡'_HTML')/'plus' ⍝ _HTML is in the HTMLplus folder
      folder←#.Boot.AppRoot,'Examples/',(1↓space),'/'
      samples←{0::⍬ ⋄ ¯7↓¨6⊃#.Files.DirX ⍵}folder,'*.dyalog'
      r←(⍴names)⍴⊂''

      :For suffix :In 'Simple' 'Advanced'
          i←(((-⍴suffix)↑¨samples)∊⊂suffix)/⍳⍴samples
          i←{(⍵≤⍴names)/⍵}names⍳(-⍴suffix)↓¨samples[i]
          r[i]←r[i],¨⊂⊂suffix
      :EndFor
    ∇

⍝    ∇ PopulateRight thediv;pages;ul
⍝     ⍝ Populate the Right Bar
⍝
⍝      thediv.Add textspan'Documentation'
⍝      pages←1 2⍴'SyncFusion APIs' 'http://helpjs.syncfusion.com/js/api/ejaccordion'
⍝      pages⍪←'SyncFusion Demos' 'http://js.syncfusion.com/demos/web/'
⍝      pages⍪←'JQueryUI' 'https://jqueryui.com/'
⍝      pages⍪←2 2⍴'HTML' 'http://www.w3schools.com/html/' 'CSS' 'http://www.w3schools.com/css/'
⍝      pages[;2]←↓pages[;,2],⊂'target=_blank"'
⍝      ul←thediv.Add _.Ul pages
⍝      ul.style←'font-size: 10px;'
⍝
⍝      thediv.Add _.br
⍝
⍝      thediv.Add textspan'Resources'
⍝      pages←1 2⍴'MiServer Forum' 'http://www.dyalog.com/forum/viewforum.php?f=34'
⍝      pages⍪←'Blog' 'http://www.dyalog.com/blog/category/miserver/'
⍝      pages⍪←'GitHub Repo' 'https://github.com/Dyalog/MiServer'
⍝      pages⍪←'dyalog.com' 'http://www.dyalog.com'
⍝      pages[;2]←↓pages[;,2],⊂'target=_blank"'
⍝      ul←thediv.Add _.Ul pages
⍝      ul.style←'font-size: 10px;'
⍝    ∇

    ∇ PopulateMid mid;url;code;frame;mya

      url←'/Examples/Apps/about.dyalog'
      code←{0::,⊂'[file read failed]' ⋄ #.UnicodeFile.ReadNestedText ⍵}#.Boot.AppRoot,url

      mya←('#SampleTitle'mid.Add _.h2).Add _.a('Title'Section code)
      'target' 'href'mya.Set'_blank'url

      '#SampleDesc'mid.Add _.p('Description'Section code)

      frame←'#SampleFrame'mid.Add _.div
      ('src=',url,'?NoWrapper=1')'width="800"' 'height="400"'frame.Add _.iframe

      '#SampleSource'mid.Add _.div(#.HTMLInput.APLToHTMLColour code)

      :If 0 ⍝ If we can make a call upon page load and set node
          _PageData.node←1
          middiv.On'load' 'onSelectApp'
      :EndIf
    ∇

    ∇ r←LinkButton(label image link);a;d;size

      d←newdiv'.linkbutton'
      size←'height: 32px; width: 32px;'
      'src' 'style'(d.Add _.img).Set image size
      d.Add textspan label
      r←New _.A d link
    ∇

    ∇ r←onSelectSample;code;control;depth;folder;html;i;iframe;node;p;page;sample;samples;section;simple;source;sp;space;t;tab;text;titles;url;file;link;title;spacedir;spctrl;files;dir;pathfile;ctrlsec;pagea;outfile;tie;outdiv;mya
      :Access Public
     ⍝ When a sample is selected, call this
      node←⊃2⊃⎕VFI{((+\⍵='_')⍳2)↓⍵}⊃_PageData.node

      (depth sample)←Samples[node;1 2]
      (control space section)←3↑,Samples[1⌈1+node-(⌽node↑Samples[;1])⍳2 1;2 3]
      :If depth=1 ⍝ Clicked on group
          (sample control)←'index' ''
      :EndIf

      spacedir←1↓space,(space≡'_HTML')/'plus' ⍝ _HTML is in the HTMLplus folder

      :If depth=2  ⍝ Element
          spctrl←space,'.',control
          r←'#title'Replace'MS3: ',spctrl
          r,←'#SampleTitle'Replace spctrl
          r,←'#SampleDesc'Replace'The below samples demonstrate usage of ',spctrl,'. Click one to fetch.'

          dir←_Request.Server.Config.Root,'Examples/',spacedir
          files←'*Simple.dyalog'#.Files.List dir
          files⍪←'*Advanced.dyalog'#.Files.List dir
          ⍝r,←'#SampleFrame'Replace''
          outdiv←New _.div
          :For file :In files[;1]
              pathfile←dir,'/',file
              source←⎕SE.SALT.Load pathfile,' -source'
              ctrlsec←' ','Control'Section source
              :If (⊂spctrl)∊1↓¨(' '∘=⊂⊢)ctrlsec ⍝ Split Space-delimited list
                  mya←(outdiv.Add _.p).Add _.A(('Description'Section source)('/Examples/',spacedir,'/',file,'?NoWrapper=1'))
                  ⍝ mya.On 'click' [something here] '#myid' Replace 'newdata'
                  ⍝pagea←New _.a,⊂'Description'Section source
                  ⍝'href'pagea.Set'Examples/',spacedir,'/',file
                  ⍝r,←'#SampleFrame'Append pagea
              :EndIf
          :EndFor
          outfile←'/Examples/Data/indexoutput.html'
         ⍝ Empty, i.e.delete if exists, then (re-)create
          tie←¯1 ⎕NCREATE⍨{22::⍵ ⋄ ⍵⊣⍵ ⎕NERASE ⍵ ⎕NTIE 0}_Request.Server.Config.Root,1↓outfile
          outdiv.Render ⎕NAPPEND tie
          ⎕NUNTIE tie
          iframe←'src' 'width' 'height'(New _.iframe).Set outfile 800 400
          r,←'#SampleFrame'Replace iframe
          r,←'#SampleSource'Replace''

      :Else ⍝ depth=3 leaf, i.e. Actual Sample
          folder←#.Boot.AppRoot
          file←control,sample
          r←'#title'Replace'MS3: ',file
          url←'Examples/',spacedir,'/',file,'.dyalog'
          code←{0::,⊂'[file read failed]' ⋄ #.UnicodeFile.ReadNestedText ⍵}folder,url
          title←'Control'Section code
          link←'target' 'href'(New _.a title).Set'_blank'url
          r,←'#SampleTitle'Replace link
          r,←'#SampleDesc'Replace'Description'Section code
          iframe←'src' 'width' 'height'(New _.iframe).Set('/',url,'?NoWrapper=1')800 400
          r,←'#SampleFrame'Replace iframe
          r,←'#SampleSource'Replace #.HTMLInput.APLToHTMLColour code
      :EndIf
    ∇

    ∇ r←onSelectApp;code;iframe;node;sp;url;app;title;link
      :Access Public
     ⍝ When an app is selected, call this
      node←⊃2⊃⎕VFI{((+\⍵='_')⍳2)↓⍵}⊃_PageData.node
      app←node⊃Apps
      url←'Examples/Apps/',app,'.dyalog'
      code←{0::,⊂'[file read failed]' ⋄ #.UnicodeFile.ReadNestedText ⍵}#.Boot.AppRoot,url
      title←'Title'Section code
      r←'#title'Replace'MS3: ',title
      link←'target' 'href'(New _.a title).Set'_blank'url
      r,←'#SampleTitle'Replace link
      r,←'#SampleDesc'Replace'Description'Section code
      iframe←'src' 'width' 'height'(New _.iframe).Set('/',url,'?NoWrapper=1')800 400
      r,←'#SampleFrame'Replace iframe
      r,←'#SampleSource'Replace #.HTMLInput.APLToHTMLColour code

    ∇

    ∇ r←OnSearch
      :Access Public
      r←''
    ∇
:EndClass

:Class index : MiPageSample

    ∇ GROUPS←GROUPS
      GROUPS←'Base HTML' 'Wrapped HTML' 'Dyalog Controls' 'JQueryUI' 'SyncFusion'
    ∇

    ∇ REFS←REFS
      REFS←_html _HTML _DC _JQ _SF
    ∇
      Section←{
          regex←'^\s*⍝\s*',⍺,':(:.*?)$((\R^⍝(?!\s*\w+::).*?$)*)'
          opts←('Mode' 'M')('DotAll' 1)
          res←(regex ⎕S'\1\2'⍠opts)⍵
          (1↓⊃res)~'⍝'
      }

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

      Use'ejTab' ⍝ May get added by callbacks

      newdiv←{⍵ New _.div}                                  ⍝ Make a div
      textspan←{'.textspan'New _.span ⍵}                    ⍝ And a span
      horz←{⍺←⊣ ⋄ r⊣(r←⍺ New _.StackPanel ⍵).Horizontal←1}  ⍝ Horizontal StackPanel

      mytit←'#title'Add _.title'MS3: About'

      Add _.StyleSheet'/Styles/homepage.css'

      ⍝(left mid right)←newdiv¨'leftBar' 'midBar' 'rightBar'
      (left mid)←newdiv¨'leftBar' 'midBar'

      sp←'mainSP'horz left mid⍝ right
      sp.Items[1⊣3].style←⊂'width: 200px; max-height: 450px;'
      sp.Items[2].style←⊂'margin: 5px;'
      sp.style←'height: 450px; width: 100%;'

      vp←Add _.StackPanel sp
      vp.style←'width:100%'

      PopulateLeft left
      PopulateMid mid
     ⍝ PopulateRight right
    ∇

    ∇ PopulateLeft thediv;class;depths;group;items;names;ref;samples;tv;vp
     ⍝ Populate the Left Bar

     ⍝ SEARCH FIELD ⍝⍝⍝

      (thediv.Add _.EditField'str').On'onSearch' ''('str' 'val')
      thediv.Add _.hr

     ⍝ SAMPLE APPS ⍝⍝⍝
      Apps←{0::⍬ ⋄ ¯7↓¨6⊃#.Files.DirX #.Boot.AppRoot,⍵}'/Examples/Apps/*.dyalog'
      thediv.Add textspan'Sample Apps'
      tv←thediv.Add _.ejTreeView(1,[1.5]Apps)
      tv.style←'max-height: 250px'
      tv.On'nodeSelect' 'onSelectApp'('node' 'eval' 'argument.id')

      thediv.Add _.hr

     ⍝ CONTROLS ⍝⍝⍝
      items←0 3⍴⍬
      ⍝:For (group ref) :In ('Base HTML'_html)('Wrapped HTML'_HTML)('Dyalog Controls'_DC)('JQueryUI'_JQ)('SyncFusion'_SF)
      :For (group ref) :InEach GROUPS REFS
          names←{({#.HtmlElement=⊃⊃⌽⎕CLASS ⍵}¨⍵)/⍵}ref.(⍎¨⎕NL ¯9.4)
          class←2↓⍕ref               ⍝ Remove leading #.
          names←(3+⍴class)↓¨⍕¨names  ⍝
          samples←class FindSamples names
          names←(⊂group),⊃,/(⊂¨names),¨samples
          depths←1,∊2,⍪(⍴¨samples)⍴¨3
          items⍪←depths,(⍪names),(⊂class)
      :EndFor
      thediv.Add textspan'Controls'
      tv←thediv.Add _.ejTreeView(0 ¯1↓Samples←items)
      tv.style←'max-height: 300px'
      tv.On'nodeSelect' 'onSelectSample'('node' 'eval' 'argument.id')

    ∇

    ∇ r←space FindSamples names;folder;i;samples;suffix
      space,←(space≡'_HTML')/'plus' ⍝ _HTML is in the HTMLplus folder
      folder←#.Boot.AppRoot,'Examples/',(1↓space),'/'
      samples←{0::⍬ ⋄ ¯7↓¨6⊃#.Files.DirX ⍵}folder,'*.dyalog'
      r←(⍴names)⍴⊂''

      :For suffix :In 'Simple' 'Advanced'
          i←(((-⍴suffix)↑¨samples)∊⊂suffix)/⍳⍴samples
          i←{(⍵≤⍴names)/⍵}names⍳(-⍴suffix)↓¨samples[i]
          r[i]←r[i],¨⊂⊂suffix
      :EndFor
    ∇

⍝    ∇ PopulateRight thediv;pages;ul
⍝     ⍝ Populate the Right Bar
⍝
⍝      thediv.Add textspan'Documentation'
⍝      pages←1 2⍴'SyncFusion APIs' 'http://helpjs.syncfusion.com/js/api/ejaccordion'
⍝      pages⍪←'SyncFusion Demos' 'http://js.syncfusion.com/demos/web/'
⍝      pages⍪←'JQueryUI' 'https://jqueryui.com/'
⍝      pages⍪←2 2⍴'HTML' 'http://www.w3schools.com/html/' 'CSS' 'http://www.w3schools.com/css/'
⍝      pages[;2]←↓pages[;,2],⊂'target=_blank"'
⍝      ul←thediv.Add _.Ul pages
⍝      ul.style←'font-size: 10px;'
⍝
⍝      thediv.Add _.br
⍝
⍝      thediv.Add textspan'Resources'
⍝      pages←1 2⍴'MiServer Forum' 'http://www.dyalog.com/forum/viewforum.php?f=34'
⍝      pages⍪←'Blog' 'http://www.dyalog.com/blog/category/miserver/'
⍝      pages⍪←'GitHub Repo' 'https://github.com/Dyalog/MiServer'
⍝      pages⍪←'dyalog.com' 'http://www.dyalog.com'
⍝      pages[;2]←↓pages[;,2],⊂'target=_blank"'
⍝      ul←thediv.Add _.Ul pages
⍝      ul.style←'font-size: 10px;'
⍝    ∇

    ∇ PopulateMid mid;url;code;frame;mya

      url←'/Examples/Apps/about.dyalog'
      code←{0::,⊂'[file read failed]' ⋄ #.UnicodeFile.ReadNestedText ⍵}#.Boot.AppRoot,url

      mya←('#SampleTitle'mid.Add _.h2).Add _.a('Title'Section code)
      'target' 'href'mya.Set'_blank'url

      '#SampleDesc'mid.Add _.p('Description'Section code)

      frame←'#SampleFrame'mid.Add _.div
      ('src=',url,'?NoWrapper=1')'width="800"' 'height="400"'frame.Add _.iframe

      '#SampleSource'mid.Add _.div(#.HTMLInput.APLToHTMLColour code)

      :If 0 ⍝ If we can make a call upon page load and set node
          _PageData.node←1
          middiv.On'load' 'onSelectApp'
      :EndIf
    ∇

    ∇ r←LinkButton(label image link);a;d;size

      d←newdiv'.linkbutton'
      size←'height: 32px; width: 32px;'
      'src' 'style'(d.Add _.img).Set image size
      d.Add textspan label
      r←New _.A d link
    ∇

    ∇ r←onSelectSample;code;control;depth;folder;html;i;iframe;node;p;page;sample;samples;section;simple;source;sp;space;t;tab;text;titles;url;file;link;title;spacedir;spctrl;files;dir;pathfile;ctrlsec;pagea
      :Access Public
     ⍝ When a sample is selected, call this
      node←⊃2⊃⎕VFI{((+\⍵='_')⍳2)↓⍵}⊃_PageData.node

      (depth sample)←Samples[node;1 2]
      (control space section)←3↑,Samples[1⌈1+node-(⌽node↑Samples[;1])⍳2 1;2 3]
      :If depth=1 ⍝ Clicked on group
          (sample control)←'index' ''
      :EndIf

      :If 0

          examples←_Request.Server.Config.Root,'Examples/'
          dirs←2↓#.Files.Dir examples,'*.' ⍝ Delete . and ..

          currtab←Add _.table

          files←#.Files.Dir examples,dir,'/*Simple.dyalog'
          files,←#.Files.Dir examples,dir,'/*Advanced.dyalog'

          :For file :In files
              currtr←currtab.Add _.tr
              currtd←currtr.Add _.td
              dirfile←dir,'/',file
              pathfile←examples,dirfile
              source←⎕SE.SALT.Load pathfile,' -source'
              (control desc)←'Control' 'Description'Section¨⊂source
              ('href'dirfile)currtd.Add _.a control
              currtr.Add _.td,⊂('\R'⎕R'<br>'⍠'Mode' 'D')desc
          :EndFor

      :EndIf

      spacedir←1↓space,(space≡'_HTML')/'plus' ⍝ _HTML is in the HTMLplus folder

      :If depth=2  ⍝ Element
          spctrl←space,'.',control
          r←'#title'Replace'MS3: ',spctrl
          r,←'#SampleTitle'Replace spctrl
          r,←'#SampleDesc'Replace'The below samples demonstrate usage of ',spctrl,'. Click one to fetch.'

          dir←_Request.Server.Config.Root,'Examples/',spacedir
          files←'*Simple.dyalog'#.Files.List dir
          files⍪←'*Advanced.dyalog'#.Files.List dir
          r,←'#SampleFrame'Replace''
         ⍝ res←New _.frameset
          :For file :In files[;1]
              pathfile←dir,'/',file
              source←⎕SE.SALT.Load pathfile,' -source'
              ctrlsec←' ','Control'Section source
              :If (⊂spctrl)∊1↓¨(' '∘=⊂⊢)ctrlsec ⍝ Split Space-delimited list
                  pagea←New _.a,⊂'Description'Section source
                  'href'pagea.Set'Examples/',spacedir,'/',file
                  r,←'#SampleFrame'Append pagea
              :EndIf
          :EndFor
          r,←'#SampleSource'Replace''

      :Else ⍝ depth=3 leaf, i.e. Actual Sample
          folder←#.Boot.AppRoot
          file←control,sample
          r←'#title'Replace'MS3: ',file
          url←'Examples/',spacedir,'/',file,'.dyalog'
          code←{0::,⊂'[file read failed]' ⋄ #.UnicodeFile.ReadNestedText ⍵}folder,url
          title←'Control'Section code
          link←'target' 'href'(New _.a title).Set'_blank'url
          r,←'#SampleTitle'Replace link
          r,←'#SampleDesc'Replace'Description'Section code
          iframe←'src' 'width' 'height'(New _.iframe).Set('/',url,'?NoWrapper=1')800 400
          r,←'#SampleFrame'Replace iframe
          r,←'#SampleSource'Replace #.HTMLInput.APLToHTMLColour code
      :EndIf
    ∇

    ∇ r←onSelectApp;code;iframe;node;sp;url;app;title;link
      :Access Public
     ⍝ When an app is selected, call this
      node←⊃2⊃⎕VFI{((+\⍵='_')⍳2)↓⍵}⊃_PageData.node
      app←node⊃Apps
      url←'Examples/Apps/',app,'.dyalog'
      code←{0::,⊂'[file read failed]' ⋄ #.UnicodeFile.ReadNestedText ⍵}#.Boot.AppRoot,url
      title←'Title'Section code
      r←'#title'Replace'MS3: ',title
      link←'target' 'href'(New _.a title).Set'_blank'url
      r,←'#SampleTitle'Replace link
      r,←'#SampleDesc'Replace'Description'Section code
      iframe←'src' 'width' 'height'(New _.iframe).Set('/',url,'?NoWrapper=1')800 400
      r,←'#SampleFrame'Replace iframe
      r,←'#SampleSource'Replace #.HTMLInput.APLToHTMLColour code

    ∇

    ∇ r←OnSearch
      :Access Public
      r←''
    ∇
:EndClass


MiPage: Updated to stricter control structure syntax.

: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 _cache←''       ⍝ cached content if page is marked cacheable
    :field Public OnLoad←''       ⍝ page equivalent to ⎕LX
    :field Public Charset←'UTF-8' ⍝ default charset
    :field Public Cacheable←0     ⍝ is the page cacheable?

    _used←'' ⍝ keep track of what's been used

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

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

    ∇ MakeCommon
      _PageData←⎕NS''
    ∇

    ∇ {r}←Render;b;styles
      :Access public
      Head.Insert #._html.meta''('charset=',⍕Charset)
      :If ''≢OnLoad
          Use'JQuery'
      :EndIf
      b←RenderBody
      styles←∪_Styles
      styles←styles,⍨{0∊⍴⍵:⍵ ⋄ ⊂⍵}_CssReset
      styles←styles,{0∊⍴⍵:⍵ ⋄ ⊂⍵}_CssOverride
      :If ~0∊⍴styles
          {Insert #._DC.StyleSheet ⍵}¨⌽∪styles
      :EndIf
      :If ~0∊⍴_Scripts
          {(Insert _html.script).Set('src=',⍵)}¨⌽∪_Scripts
      :EndIf
      :If ~0∊⍴Handlers
          b,←∊Handlers.Render
      :EndIf
      :If ''≢OnLoad
          b,←(⎕NEW #._html.script('$(function(){',OnLoad,'});')).Render
      :EndIf
      r←RenderPage b
      :If 0≠⎕NC⊂'_Request.Response'
          _Request.Response.HTML←r
      :EndIf
    ∇

    ∇ {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
      :Access public
      :If noproto←0=⎕NC'proto' ⋄ proto←'' ⋄ :EndIf
      names←eis names
      names←,⍕names
      names←#.Strings.deb names
      :If ' '∊names
          names←{⎕ML←3 ⋄ ⍵⊂⍨⍵≠' '}names
          r←proto∘Get¨names
      :ElseIf ~(_PageData.⎕NC names)∊2 9
          r←,proto
      :Else
          r←_PageData⍎names
          :If 2≤≡r
              :If 1=⍴,r
                  r←⊃r
              :EndIf
          :EndIf
          :If noproto≥char←0=2|⎕DR 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.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
      :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
      :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
    ∇

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

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

    :endsection

    :section Position
    ∇ ref Position args;inds;mask;parameters;my;at;of;collision;within;q
      :Access public shared
      ⍝ 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
          parameters(ref{⍺⍺⍎'Position.',⍺,'←',q ⍵})¨args
          ref.Uses,←⊂'JQueryUI'
          ref.Use
      :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 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
    ∇

    :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 _cache←''       ⍝ cached content if page is marked cacheable
    :field Public OnLoad←''       ⍝ page equivalent to ⎕LX
    :field Public Charset←'UTF-8' ⍝ default charset
    :field Public Cacheable←0     ⍝ is the page cacheable?

    _used←'' ⍝ keep track of what's been used

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

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

    ∇ MakeCommon
      _PageData←⎕NS''
    ∇

    ∇ {r}←Render;b;styles
      :Access public
      Head.Insert #._html.meta''('charset=',⍕Charset)
      :If ''≢OnLoad
          Use'JQuery'
      :EndIf
      b←RenderBody
      styles←∪_Styles
      styles←styles,⍨{0∊⍴⍵:⍵ ⋄ ⊂⍵}_CssReset
      styles←styles,{0∊⍴⍵:⍵ ⋄ ⊂⍵}_CssOverride
      :If ~0∊⍴styles
          {Insert #._DC.StyleSheet ⍵}¨⌽∪styles
      :EndIf
      :If ~0∊⍴_Scripts
          {(Insert _html.script).Set('src=',⍵)}¨⌽∪_Scripts
      :EndIf
      :If ~0∊⍴Handlers
          b,←∊Handlers.Render
      :EndIf
      :If ''≢OnLoad
          b,←(⎕NEW #._html.script('$(function(){',OnLoad,'});')).Render
      :EndIf
      r←RenderPage b
      :If 0≠⎕NC⊂'_Request.Response'
          _Request.Response.HTML←r
      :EndIf
    ∇

    ∇ {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
      :Access public
      :If noproto←0=⎕NC'proto' ⋄ proto←'' ⋄ :EndIf
      names←eis names
      names←,⍕names
      names←#.Strings.deb names
      :If ' '∊names
          names←{⎕ML←3 ⋄ ⍵⊂⍨⍵≠' '}names
          r←proto∘Get¨names
      :ElseIf ~(_PageData.⎕NC names)∊2 9
          r←,proto
      :Else
          r←_PageData⍎names
          :If 2≤≡r
              :If 1=⍴,r
                  r←⊃r
              :EndIf
          :EndIf
          :If noproto≥char←0=2|⎕DR 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.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
      :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
      :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
    ∇

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

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

    :endsection

    :section Position
    ∇ ref Position args;inds;mask;parameters;my;at;of;collision;within;q
      :Access public shared
      ⍝ 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
          parameters(ref{⍺⍺⍎'Position.',⍺,'←',q ⍵})¨args
          ref.Uses,←⊂'JQueryUI'
          ref.Use
      :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 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
    ∇

    :endsection

:EndClass


Flush list of used resources after page render

: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 _cache←''       ⍝ cached content if page is marked cacheable
    :field Public OnLoad←''       ⍝ page equivalent to ⎕LX
    :field Public Cacheable←0     ⍝ is the page cacheable?

    _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;host
      :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
      host←{6::⍵ ⋄ '//',_Request.Host}''
      :If ''≢OnLoad
          {0:: ⋄ Use'JQuery'}''
      :EndIf
      b←RenderBody
      :If ~0∊⍴_Scripts
          {(Insert _html.script).Set('src=',⍵)}¨host∘AddHost¨⌽∪_Scripts
      :EndIf
      styles←∪_Styles
      styles←styles,⍨{0∊⍴⍵:⍵ ⋄ ⊂⍵}_CssReset
      styles←styles,{0∊⍴⍵:⍵ ⋄ ⊂⍵}_CssOverride
      :If ~0∊⍴styles
          {Insert #._DC.StyleSheet ⍵}¨host∘AddHost¨⌽∪styles
      :EndIf
      :If ~0∊⍴Handlers
          b,←∊Handlers.Render
      :EndIf
      :If ''≢OnLoad
          b,←(⎕NEW #._html.script('$(function(){',OnLoad,'});')).Render
      :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
      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
      :Access public
      :If noproto←0=⎕NC'proto' ⋄ proto←'' ⋄ :EndIf
      names←eis names
      names←,⍕names
      names←#.Strings.deb names
      :If ' '∊names
          names←{⎕ML←3 ⋄ ⍵⊂⍨⍵≠' '}names
          r←proto∘Get¨names
      :ElseIf ~(_PageData.⎕NC names)∊2 9
          r←,proto
      :Else
          r←_PageData⍎names
          :If 2≤≡r
              :If 1=⍴,r
                  r←⊃r
              :EndIf
          :EndIf
          :If noproto≥char←0=2|⎕DR 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.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
    ∇

    ∇ url←{host}AddHost url
      ⍝ adds the request host name to the URL if it's not already there
      :Access public shared
      :If 0=⎕NC'host' ⋄ host←{6::⍵ ⋄ _Request.Host}'' ⋄ :EndIf
      :If ''≢host
      :AndIf ~∨/((⊃⍷#.Strings.nocase)∘url)¨'//' 'http:' 'https:'
          url,⍨←host
      :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
      :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
    ∇

    ∇ 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 _cache←''       ⍝ cached content if page is marked cacheable
    :field Public OnLoad←''       ⍝ page equivalent to ⎕LX
    :field Public Cacheable←0     ⍝ is the page cacheable?

    _used←'' ⍝ keep track of what's been used

    ∇ 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;host
      :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
      host←{6::⍵ ⋄ '//',_Request.Host}''
      :If ''≢OnLoad
          {0:: ⋄ Use'JQuery'}''
      :EndIf
      b←RenderBody
      :If ~0∊⍴_Scripts
          {(Insert _html.script).Set('src=',⍵)}¨host∘AddHost¨⌽∪_Scripts
      :EndIf
      styles←∪_Styles
      styles←styles,⍨{0∊⍴⍵:⍵ ⋄ ⊂⍵}_CssReset
      styles←styles,{0∊⍴⍵:⍵ ⋄ ⊂⍵}_CssOverride
      :If ~0∊⍴styles
          {Insert #._DC.StyleSheet ⍵}¨host∘AddHost¨⌽∪styles
      :EndIf
      :If ~0∊⍴Handlers
          b,←∊Handlers.Render
      :EndIf
      :If ''≢OnLoad
          b,←(⎕NEW #._html.script('$(function(){',OnLoad,'});')).Render
      :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
      r←RenderPage b
      :If 0≠⎕NC⊂'_Request.Response'
          _Request.Response.HTML←r
      :EndIf
      (Head Body).Content←_head _body
      _Styles←_styles
      _Scripts←_scripts
    ∇

    ∇ {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
      :Access public
      :If noproto←0=⎕NC'proto' ⋄ proto←'' ⋄ :EndIf
      names←eis names
      names←,⍕names
      names←#.Strings.deb names
      :If ' '∊names
          names←{⎕ML←3 ⋄ ⍵⊂⍨⍵≠' '}names
          r←proto∘Get¨names
      :ElseIf ~(_PageData.⎕NC names)∊2 9
          r←,proto
      :Else
          r←_PageData⍎names
          :If 2≤≡r
              :If 1=⍴,r
                  r←⊃r
              :EndIf
          :EndIf
          :If noproto≥char←0=2|⎕DR 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.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
    ∇

    ∇ url←{host}AddHost url
      ⍝ adds the request host name to the URL if it's not already there
      :Access public shared
      :If 0=⎕NC'host' ⋄ host←{6::⍵ ⋄ _Request.Host}'' ⋄ :EndIf
      :If ''≢host
      :AndIf ~∨/((⊃⍷#.Strings.nocase)∘url)¨'//' 'http:' 'https:'
          url,⍨←host
      :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
      :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
    ∇

    ∇ 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


Restore RenderPage

Restore RenderPage

:class HtmlPage : #.HtmlElement

    ⍝∇:require =\HtmlElement.dyalog

    :field public Head
    :field public Body
    :field public Scripts
    :field public StylesLinks

    ∇ make
      :Access public
      :Implements constructor
      _init
      Tag←'html'
    ∇

    ∇ r←RenderBody;scr;mask
    ⍝ Called by MiPage because rendering can add scripts to the head element
      :Access public
      :If ~0∊⍴scr←∪Scripts
      :AndIf ∨/mask←{~0∊⍴⍵}¨scr.Content
          Body.Add¨mask/scr
      :EndIf
      r←Body.Render
    ∇

    ∇ r←RenderPage b;scr;mask;content
      :Access public
      content←Content
      :If ~0∊⍴scr←∪Scripts
      :AndIf ∨/mask←{0∊⍴⍵}¨scr.Content
          Head.Add¨mask/scr
      :EndIf
      :If ~0∊⍴StylesLinks
          Head.Add¨StylesLinks
      :EndIf
      Content←(Head.Render),b
      r←'<!DOCTYPE html>',∊⎕BASE.Render
      Content←content
    ∇

    ∇ r←Render;s;b;mask;scr;sty;content
      :Access public
      content←Content
      :If ~0∊⍴scr←∪Scripts
      :AndIf ∨/mask←{~0∊⍴⍵}¨scr.Content
          Body.Add¨mask/scr
      :EndIf
      b←Body.Render
      :If ~0∊⍴scr←∪Scripts
      :AndIf ∨/mask←{0∊⍴⍵}¨scr.Content
          Head.Add¨mask/scr
      :EndIf
      :If ~0∊⍴sty←∪⌽StylesLinks
          Head.Add¨StylesLinks
      :EndIf
      Content←(Head.Render),b
      r←'<!DOCTYPE html>',∊⎕BASE.Render
      Content←content
    ∇

    ∇ _init
      :Access public
      Body←⎕NEW #.HtmlElement'body'
      Head←⎕NEW #.HtmlElement'head'
      (Body Head)._PageRef←⎕THIS
      Scripts←''
      StylesLinks←''
      Handlers←''
      Content←Head,Body
    ∇

    ∇ {r}←{attr}Add what;c
      :Access public
      :If 0=⎕NC'attr' ⋄ attr←'' ⋄ :EndIf
      :If isClass⊃what
          :If #._html.script∊c←∊⎕CLASS⊃what
              r←Scripts,←{(⎕NEW(⊃⍵)((⊃⍣(2=⊃⍴⍵))1↓⍵))}what
          :ElseIf #._JQ.Handler∊c
              r←Body.Handlers,←{(⎕NEW(⊃⍵)((⊃⍣(2=⊃⍴⍵))1↓⍵))}what
              :If 0∊⍴r.Selector ⋄ r.Selector←'html' ⋄ :EndIf ⍝ if no selector specified, use page level
          :ElseIf ⊃∨/c∊¨⊂#._html.(style link)
              r←StylesLinks,←{(⎕NEW(⊃⍵)((⊃⍣(2=⊃⍴⍵))1↓⍵))}what
          :ElseIf ⊃∨/c∊¨⊂#._html.(title meta noscript base) ⍝ elements that belong exclusively or primarily in the <head> element
              r←attr Head.Add what
          :Else
              r←attr Body.Add what
          :EndIf
          r._PageRef←⎕THIS
      :Else
          r←attr Body.Add what
      :EndIf
    ∇

    ∇ {r}←{attr}Insert what;c
      :Access public
      :If 0=⎕NC'attr' ⋄ attr←'' ⋄ :EndIf
      :If isClass⊃what
          :If #._html.script∊c←∊⎕CLASS⊃what
              r←Scripts,⍨←{(⎕NEW(⊃⍵)((⊃⍣(2=⊃⍴⍵))1↓⍵))}what
          :ElseIf #._JQ.Handler∊c
              r←Body.Handlers,⍨←{(⎕NEW(⊃⍵)((⊃⍣(2=⊃⍴⍵))1↓⍵))}what
              :If 0∊⍴r.Selector ⋄ r.Selector←'html' ⋄ :EndIf ⍝ if no selector specified, use page level
          :ElseIf ⊃∨/c∊¨⊂#._html.(style link)
              r←StylesLinks,⍨←{(⎕NEW(⊃⍵)((⊃⍣(2=⊃⍴⍵))1↓⍵))}what
          :ElseIf ⊃∨/c∊¨⊂#._html.(title meta noscript base) ⍝ elements that belong exclusively or primarily in the <head> element
              r←attr Head.Insert what
          :Else
              r←attr Body.Insert what
          :EndIf
          r._PageRef←⎕THIS
      :Else
          r←attr Body.Insert what
      :EndIf
    ∇

    ∇ {r}←On args
      :Access public
      r←Body.On args
      r.Selector←'⍎document'
    ∇

    ∇ {r}←{attr}New what
      :Access public
      :If 0=⎕NC'attr' ⋄ attr←'' ⋄ :EndIf
      r←attr ⎕BASE.New what
      r._PageRef←⎕THIS
    ∇

    dtlb←{⍵{((∨\⍵)∧⌽∨\⌽⍵)/⍺}' '≠⍵}

    ∇ r←Style style
      :Access public
      r←{(⎕NEW #._html.link).Set(('href'⍵)('rel' 'stylesheet')('type' 'text/css'))}style
    ∇

:endclass

:class HtmlPage : #.HtmlElement

    ⍝∇:require =\HtmlElement.dyalog

    :field public Head
    :field public Body
    :field public Scripts
    :field public StylesLinks

    ∇ make
      :Access public
      :Implements constructor
      _init
      Tag←'html'
    ∇

    ∇ r←RenderBody;scr;mask
    ⍝ Called by MiPage because rendering can add scripts to the head element
      :Access public
      :If ~0∊⍴scr←∪Scripts
      :AndIf ∨/mask←{~0∊⍴⍵}¨scr.Content
          Body.Add¨mask/scr
      :EndIf
      r←Body.Render
    ∇

⍝    ∇ r←RenderPage b;scr;mask
⍝      :Access public
⍝      :If ~0∊⍴scr←∪Scripts
⍝      :AndIf ∨/mask←{0∊⍴⍵}¨scr.Content
⍝          Head.Add¨mask/scr
⍝      :EndIf
⍝      :If ~0∊⍴StylesLinks
⍝          Head.Add¨StylesLinks
⍝      :EndIf
⍝      Content←(Head.Render),b
⍝      r←'<!DOCTYPE html>',∊⎕BASE.Render
⍝    ∇

    ∇ r←Render;s;b;mask;scr;sty;content
      :Access public
      content←Content
      :If ~0∊⍴scr←∪Scripts
      :AndIf ∨/mask←{~0∊⍴⍵}¨scr.Content
          Body.Add¨mask/scr
      :EndIf
      b←Body.Render
      :If ~0∊⍴scr←∪Scripts
      :AndIf ∨/mask←{0∊⍴⍵}¨scr.Content
          Head.Add¨mask/scr
      :EndIf
      :If ~0∊⍴sty←∪⌽StylesLinks
          Head.Add¨StylesLinks
      :EndIf
      Content←(Head.Render),b
      r←'<!DOCTYPE html>',∊⎕BASE.Render
      Content←content
    ∇

    ∇ _init
      :Access public
      Body←⎕NEW #.HtmlElement'body'
      Head←⎕NEW #.HtmlElement'head'
      (Body Head)._PageRef←⎕THIS
      Scripts←''
      StylesLinks←''
      Handlers←''
      Content←Head,Body
    ∇

    ∇ {r}←{attr}Add what;c
      :Access public
      :If 0=⎕NC'attr' ⋄ attr←'' ⋄ :EndIf
      :If isClass⊃what
          :If #._html.script∊c←∊⎕CLASS⊃what
              r←Scripts,←{(⎕NEW(⊃⍵)((⊃⍣(2=⊃⍴⍵))1↓⍵))}what
          :ElseIf #._JQ.Handler∊c
              r←Body.Handlers,←{(⎕NEW(⊃⍵)((⊃⍣(2=⊃⍴⍵))1↓⍵))}what
              :If 0∊⍴r.Selector ⋄ r.Selector←'html' ⋄ :EndIf ⍝ if no selector specified, use page level
          :ElseIf ⊃∨/c∊¨⊂#._html.(style link)
              r←StylesLinks,←{(⎕NEW(⊃⍵)((⊃⍣(2=⊃⍴⍵))1↓⍵))}what
          :ElseIf ⊃∨/c∊¨⊂#._html.(title meta noscript base) ⍝ elements that belong exclusively or primarily in the <head> element
              r←attr Head.Add what
          :Else
              r←attr Body.Add what
          :EndIf
          r._PageRef←⎕THIS
      :Else
          r←attr Body.Add what
      :EndIf
    ∇

    ∇ {r}←{attr}Insert what;c
      :Access public
      :If 0=⎕NC'attr' ⋄ attr←'' ⋄ :EndIf
      :If isClass⊃what
          :If #._html.script∊c←∊⎕CLASS⊃what
              r←Scripts,⍨←{(⎕NEW(⊃⍵)((⊃⍣(2=⊃⍴⍵))1↓⍵))}what
          :ElseIf #._JQ.Handler∊c
              r←Body.Handlers,⍨←{(⎕NEW(⊃⍵)((⊃⍣(2=⊃⍴⍵))1↓⍵))}what
              :If 0∊⍴r.Selector ⋄ r.Selector←'html' ⋄ :EndIf ⍝ if no selector specified, use page level
          :ElseIf ⊃∨/c∊¨⊂#._html.(style link)
              r←StylesLinks,⍨←{(⎕NEW(⊃⍵)((⊃⍣(2=⊃⍴⍵))1↓⍵))}what
          :ElseIf ⊃∨/c∊¨⊂#._html.(title meta noscript base) ⍝ elements that belong exclusively or primarily in the <head> element
              r←attr Head.Insert what
          :Else
              r←attr Body.Insert what
          :EndIf
          r._PageRef←⎕THIS
      :Else
          r←attr Body.Insert what
      :EndIf
    ∇

    ∇ {r}←On args
      :Access public
      r←Body.On args
      r.Selector←'⍎document'
    ∇

    ∇ {r}←{attr}New what
      :Access public
      :If 0=⎕NC'attr' ⋄ attr←'' ⋄ :EndIf
      r←attr ⎕BASE.New what
      r._PageRef←⎕THIS
    ∇

    dtlb←{⍵{((∨\⍵)∧⌽∨\⌽⍵)/⍺}' '≠⍵}

    ∇ r←Style style
      :Access public
      r←{(⎕NEW #._html.link).Set(('href'⍵)('rel' 'stylesheet')('type' 'text/css'))}style
    ∇

:endclass