Revert "Extend _HTML.Table"

Revert "Extend _HTML.Table"

This reverts commit 74fc0567c21704cfa8a0a0bdc253e7ef64a9df8f.

:Namespace _HTML

  :class Form : #._html.form

    :field public Method←'post'
    :field public Action←''
    :field public Serialize←0

    ∇ make;c
      :Access public
      :Implements constructor
      :If {6::0 ⋄ ''≢c∘←##.context ⍵}'_Request'
        Action←c._Request.Page
      :EndIf
    ∇

    ∇ make1 action
      :Access public
      :Implements constructor
      Action←action
    ∇

    ∇ r←Render
      :Access public
      Attr['action' 'method']←Action Method
      r←⎕BASE.Render
    ∇
  :endclass

  :class InputSubmit : #._html.input

    ∇ Make
      :Access public
      :Implements constructor :base
      (name value)←'submit' 'submit'
      SetAttr('type' 'submit')
    ∇

    ∇ Make1 args;n;v
      :Access public
      :Implements constructor :base
      args←eis args
      (n v)←2↑args,(⍴args)↓'submit' 'submit'
      (name value)←n v
      SetAttr('type' 'submit')
    ∇
  :endclass

  :class Input : #._html.input

    :field public Label←''

    ∇ Make1 args;l;n;t;v
      :Access public
      :Implements constructor
      args←eis args
      t n v l←4↑args,(⍴args)↓'text' '' '' ''
      (type value Label)←t v l
      :If ~0∊⍴n ⋄ name←id←n ⋄ :EndIf
    ∇

    ∇ html←Render
      :Access public
      html←''
      :If ~0∊⍴Label
        :If id≡⎕NULL ⋄ id←'gen',⍕?1000 ⋄ :EndIf
        html,←'<label for="',(⍕id),'">',Label,'</label>'
      :EndIf
      html,←⎕BASE.Render
    ∇
  :endclass

  :class InputGrid : #._html.table

    :field public Border←0
    :field public Labels←''
    :field public Inputs←''
    :field public Headings←''

    ∇ html←Render;cells;rows
      :Access public
      cells←{⎕NEW #._html.td(⍵)}¨renderIt¨Labels,[1.1]Inputs
      cells[;1].style←⊂'text-align:right'
      rows←⎕NEW¨(⊃⍴cells)⍴#._html.tr
      rows.Add↓cells
      Content←rows.Render
      SetAttr'border'(⍕Border)
      html←⎕BASE.Render
    ∇
  :endclass

  :class EditField : #._html.input

    ∇ make1 nm
      :Access public
      :Implements constructor
      name←nm
      id←name
      type←'text'
    ∇
    ∇ make2(nm val)
      :Access public
      :Implements constructor
      :If ~1=≡name←nm val    ⍝ handle 2-character names
        (name value)←nm val
      :EndIf
      id←name
      type←'text'
    ∇
    ∇ make3(nm val att)
      :Access public
      :Implements constructor
      :If ~1=≡name←nm val att    ⍝ handle 3-character names
        (name value)←nm val
        SetAttr att
      :EndIf
      id←name
      type←'text'
    ∇
  :endclass

  :class Button : #._html.button

    :field _content←''

    ∇ Make0
      :Access public
      :Implements constructor :base
      SetAttr('type' 'button')
    ∇

    ∇ Make args;n;v
      :Access public
      :Implements constructor :base
          ⍝ arguments are name value {content}
      args←eis args
      (n v _content)←3↑args,(⍴args)↓'button' 'button' ''
      :If 0∊⍴_content ⋄ _content←v ⋄ :EndIf
      (name value)←n v
      id←name
      SetAttr('type' 'button')
    ∇

    ∇ html←Render
      :Access public
      :If 0∊⍴Content
        Content←_content
      :EndIf
      html←⎕BASE.Render
    ∇

  :endclass

  :class Script : #._html.script

    :field public File←''
    :field public Code←''

    ∇ Make0
      :Access public
      :Implements constructor
      SetAttr'type="text/javascript"'
    ∇

    ∇ Make1 params
      :Access public
      :Implements constructor
      params←eis params
      Code File←2↑params,(⍴params)↓'' ''
      SetAttr'type="text/javascript"'
    ∇

    ∇ html←Render
      :Access public
      :If ~0∊⍴File
        SetAttr('src'File)
      :ElseIf ~0∊⍴Code
        Content←Code
      :EndIf
      html←⎕BASE.Render
    ∇

  :endclass

  :class Style : #._html.style

    :field public Selector←''
    :field public Styles←''

    ∇ Make1 params
      :Access public
      :Implements constructor
      params←eis params
      (Selector Styles)←2↑params,(⍴params)↓'' ''
    ∇

    ∇ html←Render
      :Access public
      Content←Selector,#.HTMLUtils.Styles Styles
      html←⎕BASE.Render
    ∇
  :endclass

  :class StyleSheet : #._html.link

    :field public href←''

    ∇ Make1 params
      :Access public
      :Implements constructor
      href←params
    ∇

    ∇ html←Render
      :Access public
      SetAttr(('href'href)('rel' 'stylesheet')('type' 'text/css'))
      html←⎕BASE.Render
    ∇
  :endclass

  :class Table : #._html.table
    :field public Data←0 0⍴⊂''
    :field public CellAttr←''
    :field public HeaderRows←0
    :field public HeaderAttr←''
    :field public MakeCellIds←0
    :field public MakeRowIds←0

    ∇ Make0
      :Access public
      :Implements constructor
    ∇

    ∇ Make1 data
      :Access public
      :Implements constructor
      Data←data
    ∇

    ∇ html←Render;data;atts;tda;tha;hdrrows;cellids;rowids;rows;x;head;body;table;thead;tbody
      :Access public
      data tda tha hdrrows cellids rowids←Data CellAttr HeaderAttr HeaderRows MakeCellIds MakeRowIds
      hdrrows←⍬⍴hdrrows
      data←((rows←×/¯1↓⍴data),¯1↑⍴data)⍴data
      head←body←(0 1×⍴data)⍴⊂''
      :If 0≠hdrrows
        head←{⎕NEW #._html.th ⍵}¨hdrrows↑data
        :If cellids
          head.id←{∊'rc',¨⍕¨⍵}¨⍳⍴head
        :EndIf
        :If ~0∊⍴tha
          head.SetAttr¨(⍴head)⍴⊂¨eis tha
        :EndIf
      :EndIf
      :If 0<(⊃⍴data)-hdrrows
        body←{⎕NEW #._html.td ⍵}¨hdrrows↓data
        :If cellids
          body.id←{∊'rc',¨⍕¨⍵}¨hdrrows↓⍳⍴data
        :EndIf
        :If ~0∊⍴tda
          body.SetAttr¨(⍴body)⍴⊂¨eis tda
        :EndIf
      :EndIf
      (table←⎕NEW¨rows⍴#._html.tr).Add↓head⍪body
      :If rowids
        table.id←{'row',⍕⍵}⍳rows
      :EndIf
      thead←tbody←''
      :If 0≠hdrrows
        (thead←⎕NEW #._html.thead).Add hdrrows↑table
      :EndIf
      :If 0<(⊃⍴data)-hdrrows
        (tbody←⎕NEW #._html.tbody).Add hdrrows↓table
      :EndIf
      Content←thead,tbody
      html←⎕BASE.Render
    ∇
  :endclass

:EndNamespace

:Namespace _HTML

  :class Form : #._html.form

    :field public Method←'post'
    :field public Action←''
    :field public Serialize←0

    ∇ make;c
      :Access public
      :Implements constructor
      :If {6::0 ⋄ ''≢c∘←##.context ⍵}'_Request'
        Action←c._Request.Page
      :EndIf
    ∇

    ∇ make1 action
      :Access public
      :Implements constructor
      Action←action
    ∇

    ∇ r←Render
      :Access public
      Attr['action' 'method']←Action Method
      r←⎕BASE.Render
    ∇
  :endclass

  :class InputSubmit : #._html.input

    ∇ Make
      :Access public
      :Implements constructor :base
      (name value)←'submit' 'submit'
      SetAttr('type' 'submit')
    ∇

    ∇ Make1 args;n;v
      :Access public
      :Implements constructor :base
      args←eis args
      (n v)←2↑args,(⍴args)↓'submit' 'submit'
      (name value)←n v
      SetAttr('type' 'submit')
    ∇
  :endclass

  :class Input : #._html.input

    :field public Label←''

    ∇ Make1 args;l;n;t;v
      :Access public
      :Implements constructor
      args←eis args
      t n v l←4↑args,(⍴args)↓'text' '' '' ''
      (type value Label)←t v l
      :If ~0∊⍴n ⋄ name←id←n ⋄ :EndIf
    ∇

    ∇ html←Render
      :Access public
      html←''
      :If ~0∊⍴Label
        :If id≡⎕NULL ⋄ id←'gen',⍕?1000 ⋄ :EndIf
        html,←'<label for="',(⍕id),'">',Label,'</label>'
      :EndIf
      html,←⎕BASE.Render
    ∇
  :endclass

  :class InputGrid : #._html.table

    :field public Border←0
    :field public Labels←''
    :field public Inputs←''
    :field public Headings←''

    ∇ html←Render;cells;rows
      :Access public
      cells←{⎕NEW #._html.td(⍵)}¨renderIt¨Labels,[1.1]Inputs
      cells[;1].style←⊂'text-align:right'
      rows←⎕NEW¨(⊃⍴cells)⍴#._html.tr
      rows.Add↓cells
      Content←rows.Render
      SetAttr'border'(⍕Border)
      html←⎕BASE.Render
    ∇
  :endclass

  :class EditField : #._html.input

    ∇ make1 nm
      :Access public
      :Implements constructor
      name←nm
      id←name
      type←'text'
    ∇
    ∇ make2(nm val)
      :Access public
      :Implements constructor
      :If ~1=≡name←nm val    ⍝ handle 2-character names
        (name value)←nm val
      :EndIf
      id←name
      type←'text'
    ∇
    ∇ make3(nm val att)
      :Access public
      :Implements constructor
      :If ~1=≡name←nm val att    ⍝ handle 3-character names
        (name value)←nm val
        SetAttr att
      :EndIf
      id←name
      type←'text'
    ∇
  :endclass

  :class Button : #._html.button

    :field _content←''

    ∇ Make0
      :Access public
      :Implements constructor :base
      SetAttr('type' 'button')
    ∇

    ∇ Make args;n;v
      :Access public
      :Implements constructor :base
          ⍝ arguments are name value {content}
      args←eis args
      (n v _content)←3↑args,(⍴args)↓'button' 'button' ''
      :If 0∊⍴_content ⋄ _content←v ⋄ :EndIf
      (name value)←n v
      id←name
      SetAttr('type' 'button')
    ∇

    ∇ html←Render
      :Access public
      :If 0∊⍴Content
        Content←_content
      :EndIf
      html←⎕BASE.Render
    ∇

  :endclass

  :class Script : #._html.script

    :field public File←''
    :field public Code←''

    ∇ Make0
      :Access public
      :Implements constructor
      SetAttr'type="text/javascript"'
    ∇

    ∇ Make1 params
      :Access public
      :Implements constructor
      params←eis params
      Code File←2↑params,(⍴params)↓'' ''
      SetAttr'type="text/javascript"'
    ∇

    ∇ html←Render
      :Access public
      :If ~0∊⍴File
        SetAttr('src'File)
      :ElseIf ~0∊⍴Code
        Content←Code
      :EndIf
      html←⎕BASE.Render
    ∇

  :endclass

  :class Style : #._html.style

    :field public Selector←''
    :field public Styles←''

    ∇ Make1 params
      :Access public
      :Implements constructor
      params←eis params
      (Selector Styles)←2↑params,(⍴params)↓'' ''
    ∇

    ∇ html←Render
      :Access public
      Content←Selector,#.HTMLUtils.Styles Styles
      html←⎕BASE.Render
    ∇
  :endclass

  :class StyleSheet : #._html.link

    :field public href←''

    ∇ Make1 params
      :Access public
      :Implements constructor
      href←params
    ∇

    ∇ html←Render
      :Access public
      SetAttr(('href'href)('rel' 'stylesheet')('type' 'text/css'))
      html←⎕BASE.Render
    ∇
  :endclass

  :class Table : #._html.table
    :field public Data←0 0⍴⊂''
    :field public CellAttr←''
    :field public HeaderRows←0
    :field public HeaderAttr←''
    :field public MakeCellIds←0
    :field public MakeRowIds←0

    ∇ Make0
      :Access public
      :Implements constructor
    ∇

    ∇ Make1 data
      :Access public
      :Implements constructor
      data←eis data
      (Data CellAttr HeaderRows HeaderAttr MakeCellIds MakeRowIds)←6↑data,(⍴data)↓Data CellAttr HeaderRows HeaderAttr MakeCellIds MakeRowIds
    ∇

    ∇ html←Render;data;atts;tda;tha;hdrrows;cellids;rowids;rows;x;head;body;table;thead;tbody
      :Access public
      data tda tha hdrrows cellids rowids←Data CellAttr HeaderAttr HeaderRows MakeCellIds MakeRowIds
      hdrrows←⍬⍴hdrrows
      data←((rows←×/¯1↓⍴data),¯1↑⍴data)⍴data
      head←body←(0 1×⍴data)⍴⊂''
      :If 0≠hdrrows
        head←{⎕NEW #._html.th ⍵}¨hdrrows↑data
        :If cellids
          head.id←{∊'rc',¨⍕¨⍵}¨⍳⍴head
        :EndIf
        :If ~0∊⍴tha
          head.SetAttr¨(⍴head)⍴⊂¨eis tha
        :EndIf
      :EndIf
      :If 0<(⊃⍴data)-hdrrows
        body←{⎕NEW #._html.td ⍵}¨hdrrows↓data
        :If cellids
          body.id←{∊'rc',¨⍕¨⍵}¨hdrrows↓⍳⍴data
        :EndIf
        :If ~0∊⍴tda
          body.SetAttr¨(⍴body)⍴⊂¨eis tda
        :EndIf
      :EndIf
      (table←⎕NEW¨rows⍴#._html.tr).Add↓head⍪body
      :If rowids
        table.id←{'row',⍕⍵}⍳rows
      :EndIf
      thead←tbody←''
      :If 0≠hdrrows
        (thead←⎕NEW #._html.thead).Add hdrrows↑table
      :EndIf
      :If 0<(⊃⍴data)-hdrrows
        (tbody←⎕NEW #._html.tbody).Add hdrrows↓table
      :EndIf
      Content←thead,tbody
      html←⎕BASE.Render
    ∇
  :endclass

:EndNamespace


DirectoryBrowser now uses icons instead of . and ./ to induce proper sorting.

:Class DirectoryBrowser : #._JS.DataTable

    :field public Folder←''
    :field public Filter←''
    :field public Down←0
    :field public Up←0

    ∇ make
      :Access public
      :Implements constructor
      makeCommon
    ∇

    ∇ make1 args
      :Access public
      :Implements constructor
      (Folder Filter Down Up)←args defaultArgs Folder Filter Down Up
      makeCommon
    ∇

    ∇ makeCommon
      :Trap 6
          Root←#.Boot.AppRoot
      :Else
          Root←#.Utils.WSRoot
      :EndTrap
    ∇

    ∇ r←Render;files;filenames;links;sizes
      :Access public
      files←{⍵⌿⍨Down≥⍵[;4]}Filter #.Files.List Root,Folder  ⍝ remove folder entrie if no drilldown
      :If Down>0∊⍴Filter                       ⍝ if filtered and drilldown...
          files⍪←{⍵⌿⍨⍵[;4]}#.Files.List Root,Folder ⍝ ...add in folders
      :EndIf
      Data←0 3⍴⊂''
      files⌿⍨←'.'≠⊃¨files[;1] ⍝ remove current and parent entries
      :If ~0∊⍴files
          files←{⍵[⍒⍵[;4];]}{⍵[⍋#.Strings.lc↑⍵[;1];]}files
          filenames←files[;1]{'&#',(⍕128196-2×⍵),'; ',⍺,⍵/'/'}¨files[;4]
          links←files[;4](Folder{'<a ',((~⍺)/'target="_blank" '),'href="',(⍺⍺,10↓⍵),'">',⍵,'</a>'})¨filenames
          sizes←#.Strings.commaFmt files[;2]
          (files[;4]/sizes)←⊂'[dir]'
          Data←links,sizes,⍪#.Dates.TSFmt¨files[;3]
      :EndIf
      :If Up>0∊⍴Folder
          Data⍪⍨←((New #._DC.A'📁 ..'({⍵↓⍨-⊥⍨'/'≠⍵}¯1↓Folder)).Render)'' ''
      :EndIf
      Data⍪⍨←'Name' 'Size' 'Timestamp'
      'autoWidth'Set _true
      'columns'Set'⍎[{"type":"html","className":"db-left"},{"type":"num-fmt","className":"db-right"},{"type":"date","className":"db-left"}]'
      AddClass'display'
      r←(⎕NEW #._DC.Style((('.db-left')('text-align' 'left'))(('.db-center')('text-align' 'center'))(('.db-right')('text-align' 'right')))).Render
      r,←⎕BASE.Render
    ∇

:EndClass

:Class DirectoryBrowser : #._JS.DataTable

    :field public Folder←''
    :field public Filter←''
    :field public Down←0
    :field public Up←0

    ∇ make
      :Access public
      :Implements constructor
      makeCommon
    ∇

    ∇ make1 args
      :Access public
      :Implements constructor
      (Folder Filter Down Up)←args defaultArgs Folder Filter Down Up
      makeCommon
    ∇

    ∇ makeCommon
      :Trap 6
          Root←#.Boot.AppRoot
      :Else
          Root←#.Utils.WSRoot
      :EndTrap
    ∇

    ∇ r←Render;files;filenames;links;sizes
      :Access public
      files←{⍵⌿⍨Down≥⍵[;4]}Filter #.Files.List Root,Folder  ⍝ remove folder entrie if no drilldown
      :If Down>0∊⍴Filter                       ⍝ if filtered and drilldown...
          files⍪←{⍵⌿⍨⍵[;4]}#.Files.List Root,Folder ⍝ ...add in folders
      :EndIf
      Data←0 3⍴⊂''
      files⌿⍨←'.'≠⊃¨files[;1] ⍝ remove current and parent entries
      :If ~0∊⍴files
          files←{⍵[⍒⍵[;4];]}{⍵[⍋#.Strings.lc↑⍵[;1];]}files
          filenames←files[;1]{(⍵/'./'),⍺,⍵/'/'}¨files[;4]
          links←files[;4](Folder{'<a ',((~⍺)/'target="_blank" '),'href="',(⍺⍺,⍵),'">',⍵,'</a>'})¨filenames
          sizes←#.Strings.commaFmt files[;2]
          (files[;4]/sizes)←⊂'[dir]'
          Data←links,sizes,⍪#.Dates.TSFmt¨files[;3]
      :EndIf
      :If Up>0∊⍴Folder
          Data⍪⍨←((New #._DC.A'..'({⍵↓⍨-⊥⍨'/'≠⍵}¯1↓Folder)).Render)'' ''
      :EndIf
      Data⍪⍨←'Name' 'Size' 'Timestamp'
      'autoWidth'Set _true
      'columns'Set'⍎[{"type":"html","className":"db-left"},{"type":"num-fmt","className":"db-right"},{"type":"date","className":"db-left"}]'
      AddClass'display'
      r←(⎕NEW #._DC.Style((('.db-left')('text-align' 'left'))(('.db-center')('text-align' 'center'))(('.db-right')('text-align' 'right')))).Render
      r,←⎕BASE.Render
    ∇

:EndClass


RadioButtonGroup - insert require: =/Table.dyalog

:class RadioButtonGroup : #._DC.Table
⍝ Description:: Dyalog Radio Button Group widget
⍝ Constructor:: [(Values | [;Labels Values]) [SelectedIndex [LabelPos [Horizontal]]]]
⍝ Values           - a vector of values for the radio buttons (Labels will be set to the same as Values)
⍝ [;Labels Values] - a matrix of [;1] labels [;2] values
⍝ SelectedIndex    - index of a pre-selected choice (⍬ is the default)
⍝ LabelPos         - label position relative to the radio button ('left' is the default)
⍝ Horizontal       - 0 if the labels/buttons are to be arranged in a vertical grid, 1 if horizontal
⍝ Public Fields::
⍝ Values           - a vector of values for the radio buttons (Labels will be set to the same as Values)
⍝ Labels           - a matrix of [;1] labels [;2] values
⍝ SelectedIndex    - index of a pre-selected choice (⍬ is the default)
⍝ LabelPos         - label position relative to the radio button ('left' is the default)
⍝ Horizontal       - 0 if the labels/buttons are to be arranged in a vertical grid, 1 if horizontal
⍝ Notes::
⍝ The underlying input elements all share the same name, and have ids of name,'_1', name,'_2', etc
⍝ The containing table has an id of name,'_container'
⍝ input cells are assigned a class of (id,'_input')

⍝∇:require =\Table.dyalog

    :field public Labels←⍬
    :field public Values←⍬
    :field public SelectedIndex←⍬
    :field public LabelPos←'left'
    :field public Horizontal←0

    ∇ make
      :Access public
      :Implements constructor
    ∇

    ∇ make1 args;data;sel;pos;horz
      :Access public
      :Implements constructor
      (data SelectedIndex LabelPos Horizontal)←args defaultArgs ⍬ SelectedIndex LabelPos Horizontal
      :If ~0∊⍴data
          :Select ⊃⍴⍴data
          :Case 1
              Labels←Values←data
          :Case 2
              (Labels Values)←↓[1]data
          :EndSelect
      :EndIf
    ∇

    ∇ r←Render;n;inps;tmpname;tmpid
      :Access public
      SetInputName
      r←''
      (Labels Values)←Labels{0∊⍴⍵:⍺ ⍺ ⋄ 0∊⍴⍺:⍵ ⍵ ⋄ ⍺ ⍵}Values
      :If ~0∊n←⍴Values
          :If (,Horizontal)≡,1 ⍝ horizontal layout
              inps←Values{⎕NEW #._DC.Input('radio'⍺ ⍵ LabelPos)}¨Labels
              inps.name←⊂name
              inps.id←(⊂name,'_'),¨⍕¨⍳n
              :If ⍬≢SelectedIndex ⋄ inps[SelectedIndex].Attrs[⊂'checked']←⊂'checked' ⋄ :EndIf
              Data←(1,⍴inps)⍴inps
          :Else ⍝ vertical layout
              inps←⎕NEW¨n⍴#._html.input
              inps.(name type)←(⊂name'radio')
              inps.id←(⊂name,'_'),¨⍕¨⍳n
              :If ⍬≢SelectedIndex ⋄ inps[SelectedIndex].Attrs[⊂'checked']←⊂'checked' ⋄ :EndIf
              Data←Labels,⍪inps
              :If LabelPos≡'left'
                  CellAttr←'style=text-align:right;' ''
              :Else
                  Data←⌽Data
              :EndIf
          :EndIf
          (tmpname tmpid)←name id
          id←name,'_container'
          name←''
          r←⎕BASE.Render
          (name id)←tmpname tmpid
      :EndIf
    ∇
:EndClass

:class RadioButtonGroup : #._DC.Table
⍝ Description:: Dyalog Radio Button Group widget
⍝ Constructor:: [(Values | [;Labels Values]) [SelectedIndex [LabelPos [Horizontal]]]]
⍝ Values           - a vector of values for the radio buttons (Labels will be set to the same as Values)
⍝ [;Labels Values] - a matrix of [;1] labels [;2] values
⍝ SelectedIndex    - index of a pre-selected choice (⍬ is the default)
⍝ LabelPos         - label position relative to the radio button ('left' is the default)
⍝ Horizontal       - 0 if the labels/buttons are to be arranged in a vertical grid, 1 if horizontal
⍝ Public Fields::
⍝ Values           - a vector of values for the radio buttons (Labels will be set to the same as Values)
⍝ Labels           - a matrix of [;1] labels [;2] values
⍝ SelectedIndex    - index of a pre-selected choice (⍬ is the default)
⍝ LabelPos         - label position relative to the radio button ('left' is the default)
⍝ Horizontal       - 0 if the labels/buttons are to be arranged in a vertical grid, 1 if horizontal
⍝ Notes::
⍝ The underlying input elements all share the same name, and have ids of name,'_1', name,'_2', etc
⍝ The containing table has an id of name,'_container'
⍝ input cells are assigned a class of (id,'_input')

    :field public Labels←⍬
    :field public Values←⍬
    :field public SelectedIndex←⍬
    :field public LabelPos←'left'
    :field public Horizontal←0

    ∇ make
      :Access public
      :Implements constructor
    ∇

    ∇ make1 args;data;sel;pos;horz
      :Access public
      :Implements constructor
      (data SelectedIndex LabelPos Horizontal)←args defaultArgs ⍬ SelectedIndex LabelPos Horizontal
      :If ~0∊⍴data
          :Select ⊃⍴⍴data
          :Case 1
              Labels←Values←data
          :Case 2
              (Labels Values)←↓[1]data
          :EndSelect
      :EndIf
    ∇

    ∇ r←Render;n;inps;tmpname;tmpid
      :Access public
      SetInputName
      r←''
      (Labels Values)←Labels{0∊⍴⍵:⍺ ⍺ ⋄ 0∊⍴⍺:⍵ ⍵ ⋄ ⍺ ⍵}Values
      :If ~0∊n←⍴Values
          :If (,Horizontal)≡,1 ⍝ horizontal layout
              inps←Values{⎕NEW #._DC.Input('radio'⍺ ⍵ LabelPos)}¨Labels
              inps.name←⊂name
              inps.id←(⊂name,'_'),¨⍕¨⍳n
              :If ⍬≢SelectedIndex ⋄ inps[SelectedIndex].Attrs[⊂'checked']←⊂'checked' ⋄ :EndIf
              Data←(1,⍴inps)⍴inps
          :Else ⍝ vertical layout
              inps←⎕NEW¨n⍴#._html.input
              inps.(name type)←(⊂name'radio')
              inps.id←(⊂name,'_'),¨⍕¨⍳n
              :If ⍬≢SelectedIndex ⋄ inps[SelectedIndex].Attrs[⊂'checked']←⊂'checked' ⋄ :EndIf
              Data←Labels,⍪inps
              :If LabelPos≡'left'
                  CellAttr←'style=text-align:right;' ''
              :Else
                  Data←⌽Data
              :EndIf
          :EndIf
          (tmpname tmpid)←name id
          id←name,'_container'
          name←''
          r←⎕BASE.Render
          (name id)←tmpname tmpid
      :EndIf
    ∇
:EndClass


RadioButtonGroup fix

RadioButtonGroup fix

:Namespace _DC ⍝ Dyalog Controls

⍝∇:require =\_HTML.dyalog

    :class Gyro : #._HTML.Script

        :include #.HTMLUtils

        :field public Page←''
        :field public Interval←250

        ∇ Make0
          :Access public
          :Implements constructor
        ∇

        ∇ Make1 arg
          :Access public
          :Implements constructor
          arg←eis arg
          Page Interval←⍕¨2↑arg,(⍴arg)↓'' 250
        ∇

        ∇ r←Render;code
          :Access public
          Use'JQuery'
⍝ create a gyro listener
⍝ page     - the page to signal the event to
⍝ interval - the number of ms to wait between AJAX requests
⍝ returns
⍝ event    - "deviceorientation"
⍝ beta     - front/back tilt
⍝ gamma    - left/right tilt
          code←ScriptFollows
⍝     if (window.DeviceOrientationEvent) {
⍝         var time = 0;
⍝         var newtime = 0;
⍝         window.addEventListener('deviceorientation', function (evt) {
⍝             newtime = new Date().getTime();
⍝             if (⍳ < (newtime - time)) {
⍝                 time = newtime;
⍝                 $.ajax({
⍝                     url: "∆",
⍝                     cache: false,
⍝                     type: "POST",
⍝                     dataType: "json",
⍝                     data: {
⍝                         event: evt.type,
⍝                         beta: Math.round(evt.beta),
⍝                         gamma: Math.round(evt.gamma)
⍝                     },
⍝                 });
⍝             }}, false);
⍝         }
          code[code⍳'⍳∆']←⍕¨Interval Page
          Code←enlist code
          r←⎕BASE.Render
        ∇
    :endclass

    :class Timer : #._HTML.Script
        :field public Id
        :field public Event←'tick'
        :field public Delay←5000
        :field public Autostart←1
        :field public Block←1
        ∇ r←Render
    ⍝ return script to implement a timed loop
    ⍝ pars - [1] id [2] event to trigger [3] delay in ms [4] autostart?
          pars←eis pars
          (id event delay autostart)←4↑pars,(⍴pars)↓'JSTimer' 'ding' 5000 1
          :If 9=⎕NC'req' ⋄ req.Use'JQuery' ⋄ :EndIf
          r←'var timer⍳; var running⍳=0; var delay⍳=',(⍕delay),';',CRLF
          r,←'function next⍳(){$("body").trigger("',event,'"); timer⍳=setTimeout("next⍳()",delay⍳);}',CRLF
          r,←'function run⍳(){if (!running⍳){running⍳=1; next⍳();}}',CRLF
          r,←'function stop⍳(){clearTimeout(timer⍳); running⍳=0;}',CRLF
          :If autostart ⋄ r,←'$(function(){run⍳();});',CRLF ⋄ :EndIf
          ((r='⍳')/r)←⊂id
          r←#.HTMLInput.JS enlist r
        ∇
    :endclass

    :Class StackPanel : #._html.table
        :Field public Items←⍬
        :Field public Horizontal←0 ⍝ orientation

        ∇ Make0
          :Access public
          :Implements constructor
        ∇

        ∇ Make1 args
          :Access public
          :Implements constructor
          Items←args
        ∇

        ∇ {r}←{attr}Add item;td
          :Access public
          attr←{6::⍵ ⋄ attr}''
          r←attr(td←⎕NEW #._html.td).Add item ⍝ #.HtmlElement.Add
          Items,←td
        ∇

        ∇ html←Render
          :Access public
          :If (,Horizontal)≡,1
              (Content←⎕NEW #._html.tr).Add Items
          :Else
              (Content←⎕NEW¨(⍴Items)⍴#._html.tr).Add Items
          :EndIf
          html←⎕BASE.Render
        ∇

    :endclass

    :class RadioButtonGroup : StackPanel
        :field public Labels←⍬
        :field public Values←⍬
        :field public SelectedIndex←⍬

        ∇ make args;data;sel
          :Access public
          :Implements constructor
          args←eis args
          data sel←2↑args,(⍴args)↓(0 2⍴⊂'')⍬
          :If ~0∊⍴data
              :Select ⊃⍴⍴data
              :Case 1
                  Labels←Values←data
              :Case 2
                  (Labels Values)←↓[1]data
              :EndSelect
          :EndIf
          SelectedIndex←sel
        ∇

        ∇ html←Render
          :Access public

        ∇
    :EndClass
:EndNamespace

:Namespace _DC ⍝ Dyalog Controls

⍝∇:require =\_HTML.dyalog

    :class Gyro : #._HTML.Script

        :include #.HTMLUtils

        :field public Page←''
        :field public Interval←250

        ∇ Make0
          :Access public
          :Implements constructor
        ∇

        ∇ Make1 arg
          :Access public
          :Implements constructor
          arg←eis arg
          Page Interval←⍕¨2↑arg,(⍴arg)↓'' 250
        ∇

        ∇ r←Render;code
          :Access public
          Use'JQuery'
⍝ create a gyro listener
⍝ page     - the page to signal the event to
⍝ interval - the number of ms to wait between AJAX requests
⍝ returns
⍝ event    - "deviceorientation"
⍝ beta     - front/back tilt
⍝ gamma    - left/right tilt
          code←ScriptFollows
⍝     if (window.DeviceOrientationEvent) {
⍝         var time = 0;
⍝         var newtime = 0;
⍝         window.addEventListener('deviceorientation', function (evt) {
⍝             newtime = new Date().getTime();
⍝             if (⍳ < (newtime - time)) {
⍝                 time = newtime;
⍝                 $.ajax({
⍝                     url: "∆",
⍝                     cache: false,
⍝                     type: "POST",
⍝                     dataType: "json",
⍝                     data: {
⍝                         event: evt.type,
⍝                         beta: Math.round(evt.beta),
⍝                         gamma: Math.round(evt.gamma)
⍝                     },
⍝                 });
⍝             }}, false);
⍝         }
          code[code⍳'⍳∆']←⍕¨Interval Page
          Code←enlist code
          r←⎕BASE.Render
        ∇
    :endclass

    :class Timer : #._HTML.Script
        :field public Id
        :field public Event←'tick'
        :field public Delay←5000
        :field public Autostart←1
        :field public Block←1
        ∇ r←Render
    ⍝ return script to implement a timed loop
    ⍝ pars - [1] id [2] event to trigger [3] delay in ms [4] autostart?
          pars←eis pars
          (id event delay autostart)←4↑pars,(⍴pars)↓'JSTimer' 'ding' 5000 1
          :If 9=⎕NC'req' ⋄ req.Use'JQuery' ⋄ :EndIf
          r←'var timer⍳; var running⍳=0; var delay⍳=',(⍕delay),';',CRLF
          r,←'function next⍳(){$("body").trigger("',event,'"); timer⍳=setTimeout("next⍳()",delay⍳);}',CRLF
          r,←'function run⍳(){if (!running⍳){running⍳=1; next⍳();}}',CRLF
          r,←'function stop⍳(){clearTimeout(timer⍳); running⍳=0;}',CRLF
          :If autostart ⋄ r,←'$(function(){run⍳();});',CRLF ⋄ :EndIf
          ((r='⍳')/r)←⊂id
          r←#.HTMLInput.JS enlist r
        ∇
    :endclass

    :class RadioButtonGroup : #._DC.StackPanel
        :field public Labels←⍬
        :field public Values←⍬
        :field public SelectedIndex←⍬

        ∇ make args;data;sel
          :Access public
          :Implements constructor
          args←eis args
          data sel←2↑args,(⍴args)↓(0 2⍴⊂'')⍬
          :If ~0∊⍴data
              :Select ⊃⍴⍴data
              :Case 1
                  Labels←Values←data
              :Case 2
                  (Labels Values)←↓[1]data
              :EndSelect
          :EndIf
          SelectedIndex←sel
        ∇

        ∇ html←Render
          :Access public

        ∇

    :EndClass

    :Class StackPanel : #._html.table
        :Field public Items←⍬
        :Field public Horizontal←0 ⍝ orientation

        ∇ Make0
          :Access public
          :Implements constructor
        ∇

        ∇ Make1 args
          :Access public
          :Implements constructor
          Items←args
        ∇

        ∇ {r}←{attr}Add item;td
          :Access public
          attr←{6::⍵ ⋄ attr}''
          r←attr(td←⎕NEW #._html.td).Add item ⍝ #.HtmlElement.Add
          Items,←td
        ∇

        ∇ html←Render
          :Access public
          :If (,Horizontal)≡,1
              (Content←⎕NEW #._html.tr).Add Items
          :Else
              (Content←⎕NEW¨(⍴Items)⍴#._html.tr).Add Items
          :EndIf
          html←⎕BASE.Render
        ∇

    :endclass

:EndNamespace


Visual tutorial: fix handling of font types.

:Namespace MS3SiteUtils ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝
⍝ ┌─────────────────────────────────────────────────────────────────────────────────────────────┐ ⍝
⍝ │ This is the collection of utility functions used by MS3Server and the index page of the     │ ⍝
⍝ │ MiServer Sample Site, MS3, accessible at miserver.dyalog.com                                │ ⍝
⍝ ├─────────────────────────────────────────────────────────────────────────────────────────────┤ ⍝
⍝ │ The code is split up in six sections:                                                       │ ⍝
⍝ │                                                                                │ ⍝
⍝ │     ∘ A_GENERAL has utilities that appropriate to APL or MiServer in general                │ ⍝
⍝ │                                                                                │ ⍝
⍝ │     ∘ B_FILES has various tools to list, read, and cache files and their content            │ ⍝
⍝ │                                                                                │ ⍝
⍝ │     ∘ C_STRINGS string and multi-string manipulation and extraction of information          │ ⍝
⍝ │                                                                                │ ⍝
⍝ │     ∘ D_ELEMENTS aids to create elements for the HTML Document Object Model                 │ ⍝
⍝ │                                                                                │ ⍝
⍝ │     ∘ E_TUTORIAL has subroutines for the Tutorial tab on the index page                     │ ⍝
⍝ │                                                                                │ ⍝
⍝ │     ∘ F_CONSTANTS niladic functions that return unchanging values and cache                 │ ⍝
⍝ │                                                                                │ ⍝
⍝ └─────────────────────────────────────────────────────────────────────────────────────────────┘ ⍝
⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝

    :SECTION A_GENERAL ⍝ UTILITY FUNCTIONS THAT ARE NOT SPECIFIC TO THE "MS3" SITE

    Ⓒ←{⊂⍣(1=≡,⍵)⊢⍵} ⍝ enclose if simple     Mnemonic for the ⊆ glyph

    FwSl←'[\\/]+'⎕R'/' ⍝ Make all slash-block into single forward-slashes

    Q←'"'∘,,∘'"' ⍝ Surround with quotes

    FromCSV←↑{'"'~¨⍨1↓¨t⊂⍨(','=t)∧~≠\'"'=t←',',⍵}¨ ⍝ Convert CSV VTV to matrix (no escaped chars)

      Section←{ ⍝ Extract section ⍺:: from code ⍵
          regex←'^\s*⍝\s*',⍺,':(:.*?)$((\R^⍝(?!\s*\w+::).*?$)*)' ⍝ find '  ⍝  LeftArg:: some text'
          opts←('Mode' 'M')('DotAll' 1)('ML' 1)                  ⍝ '^'≡linestart  EOL∊'.'  1st-only
          res←regex ⎕S'\1\2'⍠opts⊢⍵                              ⍝ return parts 1 and 2
          #.Strings.deb'⍝'~⍨1↓⊃res                               ⍝ strip spaces and lamp
      }

      Controls←{ ⍝ Extract names of controls used in sample ⍵
          (Words'Control'Section Read ⍵)~'_',¨NSS
      }

    Of←{⍵/⍨⍺⍺¨⍵} ⍝ Those elements of ⍵ that satisfy ⍺⍺ element

    :ENDSECTION ⍝ ─────────────────────────────────────────────────────────────────────────────────
⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝

    :SECTION B_FILES ⍝ FUNCTIONS THAT READ FILES

    Score←{(102-2×(50↑⊃⌽⍵)⍳⊂⍺)-∨/'Advanced'⍷⊃⍵} ⍝ Relevance score on index of control in list

      List←{ ⍝ Files in site folder
          ⍺←1↓FILEEXT                                      ⍝ default .ext
          mods←' -rec -raw -full=2 -ext=',⍺                ⍝ subdirs no-format rooted spec-ext
          list←(⎕SE.SALT.List #.Boot.AppRoot,⍵,mods)[;1 2] ⍝ begin at application root (MS3/)
          f←~d←list[;1]≡¨⊂'<DIR>'                          ⍝ find dirs
          list←FwSl list[;2]                               ⍝ normalize slashes for WWW
          (d/list),←'/'                                    ⍝ mark dirs with final slashes
          (f/list),←⊂'.',⍺                                 ⍝ add .ext to files
          list↓¨⍨¯1+1⍳⍨(FwSl ⍵)⍷⊃list                      ⍝ trim
      }

    ∇ r←Read page;i ⍝ Read a page via the #.CACHE.read (C)
      :Trap 0
          :If (≢C.read.keys)≥i←C.read.keys⍳⊂page ⍝ is it cached, and if so, save position
              r←i⊃C.read.data                     ⍝ extract
          :Else
              C.read.data,←⊂r←#.Files.GetVTV #.Boot.AppRoot,page,(~'.'∊page)/FILEEXT
              C.read.keys,←⊂page
          :EndIf
      :Else
          r←,⊂'[failed to read "',page,'"]'
      :EndTrap
    ∇

    :ENDSECTION ⍝ ─────────────────────────────────────────────────────────────────────────────────
⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝

    :SECTION C_STRINGS ⍝ MANIPULATION OF CHARACTER VECTORS AND VECTORS OF CHARACTER VECTORS

    Slash←'/'∘=⊂⊢

    Name←{1↓⊃⌽(Slash ⍵)~⊂,'/'}  ⍝ '/aaa/bbb/ccc/ddd/' →         'ddd' (only in BuildTree)

    Words←{(1↓¨(∊∘' .'⊂⊢)' ',⍵)~⊂''} ⍝ Split at spaces and dots and remove empty pieces

    NoExt←{'.'∊⍵:⍵↓⍨-'.'⍳⍨⌽⍵⋄⍵} ⍝ 'aaa.bbb.ccc'  →  'aaa.bbb'

    NoNL←'\n' '\r'⎕R' ' ''⍠'Mode' 'D' ⍝ Replace newlines with spaces

    LastSeg←{⍺←⊢ ⋄ 1↓⍺⊃⌽Slash '/',⍵} ⍝ ⍺th (default 1st) /-separated segment from the end

    Levels←{(+/¨'/'=⍵)+~'/'=(⊃⌽)¨⍵} ⍝ Number of /s adjusted for categories' final /

    Shuffle←(?⍨∘≢⊃¨⊂)⊢⊂⍨⊢≡¨1↑⊢ ⍝ Shuffle the root elements of an XML snippet

    :ENDSECTION ⍝ ─────────────────────────────────────────────────────────────────────────────────
⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝

    :SECTION D_ELEMENTS ⍝ FUNCTIONS THAT AUTOMATE THE CREATION OF DOM ELEMENTS AND HTML CODE

      Over←{
          ⊂'',⍺,'
',('/'⎕R'
'⊢⍵),''
      }

      External←{ ⍝ Icon off-site link
          ⍺←''
      ⍝    '.external' 'target=_blank' 'data-dyalog-tip="External link"'New _.A((⍺,'🔗')⍵)
          '',⍺,'🔗'
      }

      DescrEmbed←{ ⍝ Link to and iframed page
          l←('Description'Section Read ⍵),'
'
          l,←'Open this '
          l,←'simple sample' 'advanced sample' 'mini-app'⊃⍨1⍳⍨∨/¨'Simple' 'Advanced'⍷¨⊂⍵
          l,←' in a new tab'
          e←''
          l e
      }

      Link←{ ⍝ New-tab link with optional (⍺) "tooltip"
          ⍺←0
          ⍝(('data-dyalog-tip=',Q ⍺){⍺ ⍵}⍣(⍺≢0)⊢'target="_blank"')New _.A ⍵
          '',(⊃⍵),''
      }

      DocLink←{ ⍝ Link to WidgetDoc with appropriate parameters
          link←DocUrl ⍵
          ctor←'ctor'ForControl ⍵
          0=⍴ctor::Link ⍵ link ⍝ upon fail to retrieve info
          ('Constructor: ',ctor)Link ⍵ link
      }

      DocUrl←{
          '/Documentation/DyalogAPIs/WidgetDoc?namespace=_',('ns'ForControl ⍵),'&widget=',⍵
      }

      DocTreeLink←{ ⍝ Link to file in doc folder
          '/'=⊃⌽⍵:''
          ('/Documentation/',⍵)'target="_blank"'
      }

      ReadingTree←{ ⍝ Create tree of documentation links
          l←'/',¨⍵~⊂''
          (Name∘NoExt¨l)(Levels l)(DocTreeLink¨l)
      }

    ∇ r←RelDocs w;list;item ⍝ Links to related samples
      list←'relevant'ForControl w
      r←''
      :For item :In list
          r,←'
',(Link('filedescr'ForFile item)item),'

'
      :EndFor
      r↓⍨←¯5
    ∇

      FormatList←{ ⍝ List of category-title pairs
          ⍺←1↓FILEEXT
          (⍺≡1↓FILEEXT)CatAndItem¨{'/'≠⊃⌽⍵}Of ⍺ List ⍵
      }

      CatAndItem←{ ⍝ (category) (filename/description)
          cat←2 LastSeg ⍵
          cat←'General' 'Mini App'cat['Documentation' 'Applications'⍳⊂cat]
          cat(Link((NoExt LastSeg)⍣(~⍺)('Description'Section Read)⍣⍺⊢⍵)⍵)
      }

    :ENDSECTION ⍝ ─────────────────────────────────────────────────────────────────────────────────
⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝

    :SECTION E_TUTORIAL ⍝ FUNCTIONS USED BY THE index PAGE'S Tutorial TAB

    ∇ tut←Tutorial;tabs;t1;r;sp;fs;ig;colors;rgbs;button;p;cbs;t2;g
      tut←'#tut'New _.div
      tut.Add _.p'In the tabs below, you will find categorized examples of webpage content. Click any element for more information.'
      tabs←'#Tabs'tut.Add _.Tabs ⍝ #Tabs
      t1←'#div'New _.div'This div is the main container of this tab. ' ⍝ #div
      p←'#p't1.Add _.p'This is the beginning of a paragraph, ' ⍝ #p
      '#span'p.Add _.span'but this phrase is a separate piece of text inside the paragraph. ' ⍝ #span
      button←'#Button't1.Add _.Button'Button' ⍝ #Button
      sp←'#StackPanel't1.Add _.StackPanel ⍝ #StackPanel
      sp.Add'StackPanel'
      '#EditField'sp.Add _.EditField'EditField' 'EditField' ⍝ #EditField
      '#Select'sp.Add _.Select'Red' 'Green' 'Blue' ⍝ #Select
      '#Input1't1.Add _.Input'number' 426 ' Number Input: ' ⍝ #Input1
      t1.Add _.br
      fs←'#Fieldset1't1.Add _.Fieldset'CheckBoxes' ⍝ #Fieldset1
      cbs←{'.CheckBox'New _.CheckBox ⍵}¨'checked' 'unchecked' ⍝ .CheckBox
      ig←'#InputGrid'fs.Add _.InputGrid('Checked' 'Unchecked')cbs ⍝ #InputGrid
      ig.Flip←1 ⍝ #InputGrid
      fs←'#Fieldset2't1.Add _.Fieldset'RadioButtonGroup' ⍝ #Fieldset2
      '#RadioButtonGroup'fs.Add _.RadioButtonGroup('Selected' 'Unselected')1 'right' ⍝ #RadioButtonGroup
      '#h5't1.Add _.h5'Slider' ⍝ #h52
      '#Input2't1.Add _.Input'range' 1 ⍝ #Input2
      '#List't1.Add _.List('Primaries' 'Cyan' 'Magenta' 'Yellow' 'Key' 'Black')(1 2 2 2 1 2) ⍝ #List
      colors←'aqua' 'black' 'blue' 'fuchsia' 'gray' 'green' 'lime' 'maroon' ⍝ #Table
      rgbs←'#00FFFF' '#000000' '#0000FF' '#FF00FF' '#808080' '#008000' '#00FF00' '#800000' ⍝ #Table
      colors,←'navy' 'olive' 'purple' 'red' 'silver' 'teal' 'white' 'yellow' ⍝ #Table
      rgbs,←'#000080' '#808000' '#800080' '#FF0000' '#C0C0C0' '#008080' '#FFFFFF' '#FFFF00' ⍝ #Table
      '#Table't1.Add _.Table('Color' 'RGB'⍪colors,⍪rgbs)'' 1 ⍝ #Table
      'Common elements'tabs.AddSection t1 ⍝ #Tabs

      '#info'tut.Add _.div
      tut.Add _.style ScriptFollows
      ⍝ #tut {padding: 10px;}
      ⍝ #info {display: inline-block; width: calc(50% - 15px); vertical-align: top; padding: 5px;}
      ⍝ #info h3 ~ p {text-indent:-4em; padding-left: 4em; font-family: APL385 Unicode;}
      ⍝ #Tabs {width: calc(50% - 15px); display: inline-block; margin-right: 10px;}
      ⍝ #div {padding: 10px; margin: 10px;}
      ⍝ #Button {vertical-align: top; margin: 5px;}
      ⍝ #StackPanel {display: inline-block; padding: 5px;}
      ⍝ #Fieldset1 {display: inline-block;}
      ⍝ #Fieldset2 {display: inline-block;}
      ⍝ #h5 {margin: 1em 0 0.5em 0;}
      ⍝ #Input1 {width: 4em;}
      ⍝ #Input2 {width: 100%;}
      ⍝ #List {display: inline-block; margin: 1em; padding: 5px 5px 5px 25px;}
      ⍝ #Table {display: inline-block; max-height: 7em; overflow-y: scroll; margin-top: 1em;}
      ⍝
      ⍝ /**/#span:hover, #p:hover, #Tabs:hover, #div:hover, #Button:hover, #StackPanel:hover, #EditField:hover, #Select:hover, #Fieldset1:hover, #Fieldset2:hover, .CheckBox:hover, #RadioButtonGroup:hover, #Input1:hover, #Input2:hover, #h5:hover, #progress:hover, #List:hover, #Table:hover {
      ⍝     box-shadow: 0 0 5px 1px orange,0 0 5px orange inset;
      ⍝ }
      'ShowElement'∘AddHandler¨'#span' '#p' '#Tabs' '#div' '#Button' '#EditField' '#StackPanel' '#Select' '#Fieldset1' '#Fieldset2' '.CheckBox' '#RadioButtonGroup' '#Input1' '#h5' '#Input2' '#progress' '#List' '#Table'

      t2←'#formatting'New _.div'This div has margin and gradient backround' ⍝ #formatting
      'Formatting'tabs.AddSection t2 ⍝ #Tabs
      '#boldin't2.Add _.p'Bold indented text which must be long to show how only the first line is indented.' ⍝ #boldin
      '#huge't2.Add _.span'Huge text with padding' ⍝ #huge
      '#expund't2.Add _.span'Expanded and underlined' ⍝ #expund
      '#bluebox't2.Add _.p'Blue box with shadow' ⍝ #bluebox
      '#centshad't2.Add _.p'Centered text with shadow' ⍝ #centshad
      '#whimg't2.Add _.p'White text on image background' ⍝ #whimg
      '#border't2.Add _.p'Surrounded by borders and top-right corner is round' ⍝ #border
      '#img' 'src="Examples/Data/4lines.png"' 'alt="Image"' 'title="Four lines high"'t2.Add _.img ⍝ #img
      '#narrow't2.Add _.p'Inline paragraph with limited width and single line height.' ⍝ #narrow
      '#mono't2.Add _.span'Monospace, ' ⍝ #mono
      '#sans't2.Add _.span'Sans-Serif, ' ⍝ #sans
      '#serif't2.Add _.span'and Serif.' ⍝ #serif
      t2.Add _.style ScriptFollows
      ⍝ #formatting {margin: 1em; background: linear-gradient(White, SkyBlue);}
      ⍝ #boldin {font-weight: bold; text-indent: 2em;}
      ⍝ #huge {font-size: x-large; padding: 1em;}
      ⍝ #expund {text-decoration: underline; letter-spacing: 0.5ex;}
      ⍝ #bluebox {background-color: SkyBlue; box-shadow: 0.25em 0.5em 1em Gray;}
      ⍝ #centshad {text-align: center; text-shadow: 0.1em 0.3em 0.2em blue;}
      ⍝ #whimg {color: white; background-image: url(Examples/Data/pic3.png);}
      ⍝ #serif {font-family: "Times New Roman", Times, serif; font-size: 150%;}
      ⍝ #sans {font-family: Arial, Helvetica, sans-serif; font-size: 150%;}
      ⍝ #mono {font-family: "Courier New", Courier, monospace; font-size: 150%;}
      ⍝ #border {border: 0.25em solid Blue; border-top-right-radius: 1.5em;}
      ⍝ #img {height: 4em; opacity: 0.5;}
      ⍝ #narrow {width: 20%; display: inline-block; line-height: 1;}
      ⍝
      ⍝ /**/#formatting:hover, #boldin:hover, #huge:hover, #expund:hover, #centshad:hover, #whimg:hover, #serif:hover, #sans:hover, #mono:hover, #border:hover, #img:hover, #narrow:hover {
      ⍝     box-shadow: 0 0 5px 1px orange,0 0 5px orange inset;
      ⍝ }
      ⍝ /**/#bluebox:hover {box-shadow:0 0 5px 1px orange, 0 0 5px orange inset, 0.25em 0.5em 1em gray;}

      'ShowFormatting'∘AddHandler¨'#formatting' '#boldin' '#huge' '#expund' '#bluebox' '#centshad' '#whimg' '#serif' '#sans' '#mono' '#border' '#img' '#narrow'

      {⍵ tabs.AddSection New _.div}¨'Positioning' 'Embedding' 'Interaction' 'Windows & Popups'

    ∇

    ∇ {ctrl}←callbackfn AddHandler sel
      ctrl←sel~'#.12'
      Add _.Handler sel'click'callbackfn'' '' 'event.stopPropagation()'
    ∇

    ∇ r←ShowElement;code;ctrl;info
      :Access Public
      ctrl←_selector~'#.12'
      info←'
This element is called _.',ctrl,'
'
      info,←P'descr'ForControl ctrl
      info,←'
Constructor (what goes to the right of Add _.',ctrl,')
'
      info,←P Link('ctor'ForControl ctrl)(DocUrl ctrl)
      code←⊃ExtractTutorial _selector
      info,←'
Put this in the Compose function
',P code
      r←'#info'Replace info
    ∇

    ∇ r←ShowFormatting;css;info;code
      :Access Public
      (code css)←ExtractTutorial _selector
      info←'
Put this in the Compose function
',P code
      info,←'
Put Add _.style''…'' in Compose or add  this to the stylesheet
',P css
      r←'#info'Replace info
    ∇

      ExtractTutorial←{
          nr←⎕NR'Tutorial'
          (css lines)←↓⍉↑('⍝ ',⍵)∘Prep¨nr/⍨('⍝ ',⍵)∘(∨/⍷)¨nr
          (lines/⍨~css)(css/lines)
      }

      Prep←{
          c←#.Strings.dlb ⍵
          ⍺≡c↑⍨-l←≢⍺:0('\w+\.Add'⎕R'Add'⊢c↓⍨¯1-l)
          1(2↓c)
      }

    P←{∊('
',,∘'
')¨Ⓒ⍵}

    :ENDSECTION ⍝ ─────────────────────────────────────────────────────────────────────────────────
⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝

    :SECTION F_CONSTANTS ⍝ NILADIC FUNCTIONS THAT ACT LIKE :include'ABLE CONSTANTS

    ∇ r←NSS
      :Access public
      r←'DC' 'SF' 'JQ' 'JS' 'html'
    ∇

    ∇ r←GROUPS
      :Access public
      r←'Dyalog' 'Syncfusion' 'jQueryUI' 'JavaScript' 'Base HTML'
    ∇

    ∇ r←CACHE
      :Access public
      r←'#.CACHE'
    ∇

    ∇ r←FILEEXT
      :Access public
      r←#.Boot.ms.Config.DefaultExtension
    ∇

    ∇ C←C;scores;list;refs;srcs;control;i;info;ref;src;ctor;Read;Ø;demo
     ⍝ Return ref to cache (initialize cache if nonexistant)
      :Access public
      Read←#.Files.GetVTV #.Boot.AppRoot∘, ⍝ no-cache reading
      :Hold CACHE                          ⍝ prevent clashes
          :If 9≠⎕NC CACHE                  ⍝ if cache is empty:
              Ø←⊂''
              C←⍎CACHE ⎕NS ⍬       ⍝ create with shortcut
              C.read←⎕NS ⍬         ⍝ init cache for files
              C.read.(keys←data←⍬) ⍝ init keys and data

              C.controls←(#._.⎕NL ¯9)~⊂'Handler'                       ⍝ cache all controls
              C.controlsoi←C.controls∘⍳Ⓒ ⋄ C.eocontrols←∊∘C.controls Ⓒ ⍝ hash tables
              refs←#._⍎¨C.controls                                     ⍝ refs of all controls
              C.ns←3↓¨⍕¨refs.##                                        ⍝ ns of each control
              srcs←⎕SRC¨refs                                           ⍝ all sources

              C.files←⊃⍪/{List'Examples/',⍵}¨NSS,⊂'Applications' ⍝ sample filenames
              C.filesoi←C.files∘⍳Ⓒ                               ⍝ hash table
              C.filedescr←('Description'Section Read)¨C.files    ⍝ Description:: for all files
              C.demos←Controls¨C.files                           ⍝ controls demoed in each
              scores←C.controls∘.Score↓⍉↑C.files C.demos         ⍝ controls vs files
              C.rankings←(+/0<scores)↑¨↓⍒#.Utils.∆rank 1⊢scores  ⍝ cache all rankings

              C.descr←''
              C.ctor←''
              C.notes←''
              C.relevant←''
              C.reldocs←''
              C.doclinks←''
              info←FromCSV Read'IndexData/info.csv'
              :For control ref src :InEach C.controls refs srcs
                  i←info[;1]⍳⊂control
                  :If i≤≢info
                      C.descr,←info[i;2]
                      C.notes,←info[i;3]
                  :Else
                      C.descr,←⊂NoNL'Description'Section src
                      C.notes,←⊂NoNL'Notes'Section src
                  :EndIf

                  ctor←{⍵↑⍨¯1+⌊/⍵⍳⎕UCS 13 10}'Constructor'Section src
                  :If ''≡ctor
                      C.ctor,←'[content [attributes]]' '[attributes]'[1+(⎕NEW ref).NoEndTag]
                  :Else
                      C.ctor,←⊂ctor
                  :EndIf

                  :If C.eocontrols⊂control
                      C.relevant,←⊂C.files[C.rankings⊃⍨C.controlsoi⊂control]
                  :Else
                      C.relevant,←Ø
                  :EndIf

              :EndFor

              C.reldocs←(RelDocs¨C.controls),Ø
              C.doclink←(DocLink¨C.controls),Ø
              C.used←{⊂2↓∊', '∘,¨C.doclink[C.controlsoi¨⍵]}¨C.demos

              C.tasks←{Link('Description'Section Read ⍵)⍵}¨C.files

              C.type←'simple' 'advanced' 'mini-app'[1⍳⍨¨↓⍉∨/¨'Simple' 'Advanced'∘.⍷C.files]

              C.filedescr,←Ø ⍝ These cause an empty result if looked-up is not found
              C.demos,←Ø
              C.rankings,←Ø
              C.descr,←Ø
              C.ctor,←Ø
              C.notes,←Ø
              C.notes,←Ø
              C.ns,←Ø

          :Else
              C←⍎CACHE ⍝ establish shortcut
          :EndIf
      :EndHold
    ∇

      ForControl←{ ⍝ e.g. 'notes'ForControl'DataTable'
          (C.controlsoi⊂,⍵)⊃C⍎⍺
      }
      ForFile←{ ⍝ e.g. 'filedescr'ForFile'DataTable'
          (C.filesoi⊂,⍵)⊃C⍎⍺
      }

    :ENDSECTION ⍝ ─────────────────────────────────────────────────────────────────────────────────
⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝

:EndNamespace

:Namespace MS3SiteUtils ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝
⍝ ┌─────────────────────────────────────────────────────────────────────────────────────────────┐ ⍝
⍝ │ This is the collection of utility functions used by MS3Server and the index page of the     │ ⍝
⍝ │ MiServer Sample Site, MS3, accessible at miserver.dyalog.com                                │ ⍝
⍝ ├─────────────────────────────────────────────────────────────────────────────────────────────┤ ⍝
⍝ │ The code is split up in six sections:                                                       │ ⍝
⍝ │                                                                                │ ⍝
⍝ │     ∘ A_GENERAL has utilities that appropriate to APL or MiServer in general                │ ⍝
⍝ │                                                                                │ ⍝
⍝ │     ∘ B_FILES has various tools to list, read, and cache files and their content            │ ⍝
⍝ │                                                                                │ ⍝
⍝ │     ∘ C_STRINGS string and multi-string manipulation and extraction of information          │ ⍝
⍝ │                                                                                │ ⍝
⍝ │     ∘ D_ELEMENTS aids to create elements for the HTML Document Object Model                 │ ⍝
⍝ │                                                                                │ ⍝
⍝ │     ∘ E_TUTORIAL has subroutines for the Tutorial tab on the index page                     │ ⍝
⍝ │                                                                                │ ⍝
⍝ │     ∘ F_CONSTANTS niladic functions that return unchanging values and cache                 │ ⍝
⍝ │                                                                                │ ⍝
⍝ └─────────────────────────────────────────────────────────────────────────────────────────────┘ ⍝
⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝

    :SECTION A_GENERAL ⍝ UTILITY FUNCTIONS THAT ARE NOT SPECIFIC TO THE "MS3" SITE

    Ⓒ←{⊂⍣(1=≡,⍵)⊢⍵} ⍝ enclose if simple     Mnemonic for the ⊆ glyph

    FwSl←'[\\/]+'⎕R'/' ⍝ Make all slash-block into single forward-slashes

    Q←'"'∘,,∘'"' ⍝ Surround with quotes

    FromCSV←↑{'"'~¨⍨1↓¨t⊂⍨(','=t)∧~≠\'"'=t←',',⍵}¨ ⍝ Convert CSV VTV to matrix (no escaped chars)

      Section←{ ⍝ Extract section ⍺:: from code ⍵
          regex←'^\s*⍝\s*',⍺,':(:.*?)$((\R^⍝(?!\s*\w+::).*?$)*)' ⍝ find '  ⍝  LeftArg:: some text'
          opts←('Mode' 'M')('DotAll' 1)('ML' 1)                  ⍝ '^'≡linestart  EOL∊'.'  1st-only
          res←regex ⎕S'\1\2'⍠opts⊢⍵                              ⍝ return parts 1 and 2
          #.Strings.deb'⍝'~⍨1↓⊃res                               ⍝ strip spaces and lamp
      }

      Controls←{ ⍝ Extract names of controls used in sample ⍵
          (Words'Control'Section Read ⍵)~'_',¨NSS
      }

    Of←{⍵/⍨⍺⍺¨⍵} ⍝ Those elements of ⍵ that satisfy ⍺⍺ element

    :ENDSECTION ⍝ ─────────────────────────────────────────────────────────────────────────────────
⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝

    :SECTION B_FILES ⍝ FUNCTIONS THAT READ FILES

    Score←{(102-2×(50↑⊃⌽⍵)⍳⊂⍺)-∨/'Advanced'⍷⊃⍵} ⍝ Relevance score on index of control in list

      List←{ ⍝ Files in site folder
          ⍺←1↓FILEEXT                                      ⍝ default .ext
          mods←' -rec -raw -full=2 -ext=',⍺                ⍝ subdirs no-format rooted spec-ext
          list←(⎕SE.SALT.List #.Boot.AppRoot,⍵,mods)[;1 2] ⍝ begin at application root (MS3/)
          f←~d←list[;1]≡¨⊂'<DIR>'                          ⍝ find dirs
          list←FwSl list[;2]                               ⍝ normalize slashes for WWW
          (d/list),←'/'                                    ⍝ mark dirs with final slashes
          (f/list),←⊂'.',⍺                                 ⍝ add .ext to files
          list↓¨⍨¯1+1⍳⍨(FwSl ⍵)⍷⊃list                      ⍝ trim
      }

    ∇ r←Read page;i ⍝ Read a page via the #.CACHE.read (C)
      :Trap 0
          :If (≢C.read.keys)≥i←C.read.keys⍳⊂page ⍝ is it cached, and if so, save position
              r←i⊃C.read.data                     ⍝ extract
          :Else
              C.read.data,←⊂r←#.Files.GetVTV #.Boot.AppRoot,page,(~'.'∊page)/FILEEXT
              C.read.keys,←⊂page
          :EndIf
      :Else
          r←,⊂'[failed to read "',page,'"]'
      :EndTrap
    ∇

    :ENDSECTION ⍝ ─────────────────────────────────────────────────────────────────────────────────
⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝

    :SECTION C_STRINGS ⍝ MANIPULATION OF CHARACTER VECTORS AND VECTORS OF CHARACTER VECTORS

    Slash←'/'∘=⊂⊢

    Name←{1↓⊃⌽(Slash ⍵)~⊂,'/'}  ⍝ '/aaa/bbb/ccc/ddd/' →         'ddd' (only in BuildTree)

    Words←{(1↓¨(∊∘' .'⊂⊢)' ',⍵)~⊂''} ⍝ Split at spaces and dots and remove empty pieces

    NoExt←{'.'∊⍵:⍵↓⍨-'.'⍳⍨⌽⍵⋄⍵} ⍝ 'aaa.bbb.ccc'  →  'aaa.bbb'

    NoNL←'\n' '\r'⎕R' ' ''⍠'Mode' 'D' ⍝ Replace newlines with spaces

    LastSeg←{⍺←⊢ ⋄ 1↓⍺⊃⌽Slash '/',⍵} ⍝ ⍺th (default 1st) /-separated segment from the end

    Levels←{(+/¨'/'=⍵)+~'/'=(⊃⌽)¨⍵} ⍝ Number of /s adjusted for categories' final /

    Shuffle←(?⍨∘≢⊃¨⊂)⊢⊂⍨⊢≡¨1↑⊢ ⍝ Shuffle the root elements of an XML snippet

    :ENDSECTION ⍝ ─────────────────────────────────────────────────────────────────────────────────
⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝

    :SECTION D_ELEMENTS ⍝ FUNCTIONS THAT AUTOMATE THE CREATION OF DOM ELEMENTS AND HTML CODE

      Over←{
          ⊂'',⍺,'
',('/'⎕R'
'⊢⍵),''
      }

      External←{ ⍝ Icon off-site link
          ⍺←''
      ⍝    '.external' 'target=_blank' 'data-dyalog-tip="External link"'New _.A((⍺,'🔗')⍵)
          '',⍺,'🔗'
      }

      DescrEmbed←{ ⍝ Link to and iframed page
          l←('Description'Section Read ⍵),'
'
          l,←'Open this '
          l,←'simple sample' 'advanced sample' 'mini-app'⊃⍨1⍳⍨∨/¨'Simple' 'Advanced'⍷¨⊂⍵
          l,←' in a new tab'
          e←''
          l e
      }

      Link←{ ⍝ New-tab link with optional (⍺) "tooltip"
          ⍺←0
          ⍝(('data-dyalog-tip=',Q ⍺){⍺ ⍵}⍣(⍺≢0)⊢'target="_blank"')New _.A ⍵
          '',(⊃⍵),''
      }

      DocLink←{ ⍝ Link to WidgetDoc with appropriate parameters
          link←DocUrl ⍵
          ctor←'ctor'ForControl ⍵
          0=⍴ctor::Link ⍵ link ⍝ upon fail to retrieve info
          ('Constructor: ',ctor)Link ⍵ link
      }

      DocUrl←{
          '/Documentation/DyalogAPIs/WidgetDoc?namespace=_',('ns'ForControl ⍵),'&widget=',⍵
      }

      DocTreeLink←{ ⍝ Link to file in doc folder
          '/'=⊃⌽⍵:''
          ('/Documentation/',⍵)'target="_blank"'
      }

      ReadingTree←{ ⍝ Create tree of documentation links
          l←'/',¨⍵~⊂''
          (Name∘NoExt¨l)(Levels l)(DocTreeLink¨l)
      }

    ∇ r←RelDocs w;list;item ⍝ Links to related samples
      list←'relevant'ForControl w
      r←''
      :For item :In list
          r,←'
',(Link('filedescr'ForFile item)item),'

'
      :EndFor
      r↓⍨←¯5
    ∇

      FormatList←{ ⍝ List of category-title pairs
          ⍺←1↓FILEEXT
          (⍺≡1↓FILEEXT)CatAndItem¨{'/'≠⊃⌽⍵}Of ⍺ List ⍵
      }

      CatAndItem←{ ⍝ (category) (filename/description)
          cat←2 LastSeg ⍵
          cat←'General' 'Mini App'cat['Documentation' 'Applications'⍳⊂cat]
          cat(Link((NoExt LastSeg)⍣(~⍺)('Description'Section Read)⍣⍺⊢⍵)⍵)
      }

    :ENDSECTION ⍝ ─────────────────────────────────────────────────────────────────────────────────
⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝

    :SECTION E_TUTORIAL ⍝ FUNCTIONS USED BY THE index PAGE'S Tutorial TAB

    ∇ tut←Tutorial;tabs;t1;r;sp;fs;ig;colors;rgbs;button;p;cbs;t2;g
      tut←'#tut'New _.div
      tut.Add _.p'In the tabs below, you will find categorized examples of webpage content. Click any element for more information.'
      tabs←'#Tabs'tut.Add _.Tabs ⍝ #Tabs
      t1←'#div'New _.div'This div is the main container of this tab. ' ⍝ #div
      p←'#p't1.Add _.p'This is the beginning of a paragraph, ' ⍝ #p
      '#span'p.Add _.span'but this phrase is a separate piece of text inside the paragraph. ' ⍝ #span
      button←'#Button't1.Add _.Button'Button' ⍝ #Button
      sp←'#StackPanel't1.Add _.StackPanel ⍝ #StackPanel
      sp.Add'StackPanel'
      '#EditField'sp.Add _.EditField'EditField' 'EditField' ⍝ #EditField
      '#Select'sp.Add _.Select'Red' 'Green' 'Blue' ⍝ #Select
      '#Input1't1.Add _.Input'number' 426 ' Number Input: ' ⍝ #Input1
      t1.Add _.br
      fs←'#Fieldset1't1.Add _.Fieldset'CheckBoxes' ⍝ #Fieldset1
      cbs←{'.CheckBox'New _.CheckBox ⍵}¨'checked' 'unchecked' ⍝ .CheckBox
      ig←'#InputGrid'fs.Add _.InputGrid('Checked' 'Unchecked')cbs ⍝ #InputGrid
      ig.Flip←1 ⍝ #InputGrid
      fs←'#Fieldset2't1.Add _.Fieldset'RadioButtonGroup' ⍝ #Fieldset2
      '#RadioButtonGroup'fs.Add _.RadioButtonGroup('Selected' 'Unselected')1 'right' ⍝ #RadioButtonGroup
      '#h5't1.Add _.h5'Slider' ⍝ #h52
      '#Input2't1.Add _.Input'range' 1 ⍝ #Input2
      '#List't1.Add _.List('Primaries' 'Cyan' 'Magenta' 'Yellow' 'Key' 'Black')(1 2 2 2 1 2) ⍝ #List
      colors←'aqua' 'black' 'blue' 'fuchsia' 'gray' 'green' 'lime' 'maroon' ⍝ #Table
      rgbs←'#00FFFF' '#000000' '#0000FF' '#FF00FF' '#808080' '#008000' '#00FF00' '#800000' ⍝ #Table
      colors,←'navy' 'olive' 'purple' 'red' 'silver' 'teal' 'white' 'yellow' ⍝ #Table
      rgbs,←'#000080' '#808000' '#800080' '#FF0000' '#C0C0C0' '#008080' '#FFFFFF' '#FFFF00' ⍝ #Table
      '#Table't1.Add _.Table('Color' 'RGB'⍪colors,⍪rgbs)'' 1 ⍝ #Table
      'Common elements'tabs.AddSection t1 ⍝ #Tabs

      '#info'tut.Add _.div
      tut.Add _.style ScriptFollows
      ⍝ #tut {padding: 10px;}
      ⍝ #info {display: inline-block; width: calc(50% - 15px); vertical-align: top; padding: 5px;}
      ⍝ #info h3 ~ p {text-indent:-4em; padding-left: 4em; font-family: APL385 Unicode;}
      ⍝ #Tabs {width: calc(50% - 15px); display: inline-block; margin-right: 10px;}
      ⍝ #div {padding: 10px; margin: 10px;}
      ⍝ #Button {vertical-align: top; margin: 5px;}
      ⍝ #StackPanel {display: inline-block; padding: 5px;}
      ⍝ #Fieldset1 {display: inline-block;}
      ⍝ #Fieldset2 {display: inline-block;}
      ⍝ #h5 {margin: 1em 0 0.5em 0;}
      ⍝ #Input1 {width: 4em;}
      ⍝ #Input2 {width: 100%;}
      ⍝ #List {display: inline-block; margin: 1em; padding: 5px 5px 5px 25px;}
      ⍝ #Table {display: inline-block; max-height: 7em; overflow-y: scroll; margin-top: 1em;}
      ⍝
      ⍝ /**/#span:hover, #p:hover, #Tabs:hover, #div:hover, #Button:hover, #StackPanel:hover, #EditField:hover, #Select:hover, #Fieldset1:hover, #Fieldset2:hover, .CheckBox:hover, #RadioButtonGroup:hover, #Input1:hover, #Input2:hover, #h5:hover, #progress:hover, #List:hover, #Table:hover {
      ⍝     box-shadow: 0 0 5px 1px orange,0 0 5px orange inset;
      ⍝ }
      'ShowElement'∘AddHandler¨'#span' '#p' '#Tabs' '#div' '#Button' '#EditField' '#StackPanel' '#Select' '#Fieldset1' '#Fieldset2' '.CheckBox' '#RadioButtonGroup' '#Input1' '#h5' '#Input2' '#progress' '#List' '#Table'

      t2←'#formatting'New _.div'This div has margin and gradient backround' ⍝ #formatting
      'Formatting'tabs.AddSection t2 ⍝ #Tabs
      '#boldin't2.Add _.p'Bold indented text which must be long to show how only the first line is indented.' ⍝ #boldin
      '#huge't2.Add _.span'Huge text with padding' ⍝ #huge
      '#expund't2.Add _.span'Expanded and underlined' ⍝ #expund
      '#bluebox't2.Add _.p'Blue box with shadow' ⍝ #bluebox
      '#centshad't2.Add _.p'Centered text with shadow' ⍝ #centshad
      '#whimg't2.Add _.p'White text on image background' ⍝ #whimg
      '#border't2.Add _.p'Surrounded by borders and top-right corner is round' ⍝ #border
      '#img' 'src="Examples/Data/4lines.png"' 'alt="Image"' 'title="Four lines high"'t2.Add _.img ⍝ #img
      '#narrow't2.Add _.p'Inline paragraph with limited width and single line height.' ⍝ #narrow
      '#mono't2.Add _.span'Monospace, ' ⍝ #mono
      '#sans't2.Add _.span'Sans-Serif, ' ⍝ #sans
      '#serif't2.Add _.span'and Serif.' ⍝ #serif
      t2.Add _.style ScriptFollows
      ⍝ #formatting {margin: 1em; background: linear-gradient(White, SkyBlue);}
      ⍝ #boldin {font-weight: bold; text-indent: 2em;}
      ⍝ #huge {font-size: x-large; padding: 1em;}
      ⍝ #expund {text-decoration: underline; letter-spacing: 0.5ex;}
      ⍝ #bluebox {background-color: SkyBlue; box-shadow: 0.25em 0.5em 1em Gray;}
      ⍝ #centshad {text-align: center; text-shadow: 0.1em 0.3em 0.2em blue;}
      ⍝ #whimg {color: white; background-image: url(Examples/Data/pic3.png);}
      ⍝ #serif {font-family: "Times New Roman", Times, serif; font-size: 150%;}
      ⍝ #sans {font-family: Arial, Helvetica, sans-serif; font-size: 150%;}
      ⍝ #mono {font-family: "Courier New", Courier, monospace; font-size: 150%;}
      ⍝ #border {border: 0.25em solid Blue; border-top-right-radius: 1.5em;}
      ⍝ #img {height: 4em; opacity: 0.5;}
      ⍝ #narrow {width: 20%; display: inline-block; line-height: 1;}
      ⍝
      ⍝ /**/#formatting:hover, #boldin:hover, #huge:hover, #expund:hover, #centshad:hover, #whimg:hover, #serif:hover, #sans:hover, #mono:hover, #border:hover, #img:hover, #narrow:hover {
      ⍝     box-shadow: 0 0 5px 1px orange,0 0 5px orange inset;
      ⍝ }
      ⍝ /**/#bluebox:hover {box-shadow:0 0 5px 1px orange, 0 0 5px orange inset, 0.25em 0.5em 1em gray;}

      'ShowFormatting'∘AddHandler¨'#formatting' '#boldin' '#huge' '#expund' '#bluebox' '#centshad' '#whimg' 'serif' 'sans' 'mono' '#border' '#img' '#narrow'

      {⍵ tabs.AddSection New _.div}¨'Positioning' 'Embedding' 'Interaction' 'Windows & Popups'

    ∇

    ∇ {ctrl}←callbackfn AddHandler sel
      ctrl←sel~'#.12'
      Add _.Handler sel'click'callbackfn'' '' 'event.stopPropagation()'
    ∇

    ∇ r←ShowElement;code;ctrl;info
      :Access Public
      ctrl←_selector~'#.12'
      info←'
This element is called _.',ctrl,'
'
      info,←P'descr'ForControl ctrl
      info,←'
Constructor (what goes to the right of Add _.',ctrl,')
'
      info,←P Link('ctor'ForControl ctrl)(DocUrl ctrl)
      code←⊃ExtractTutorial _selector
      info,←'
Put this in the Compose function
',P code
      r←'#info'Replace info
    ∇

    ∇ r←ShowFormatting;css;info;code
      :Access Public
      (code css)←ExtractTutorial _selector
      info←'
Put this in the Compose function
',P code
      info,←'
Put Add _.style''…'' in Compose or add  this to the stylesheet
',P css
      r←'#info'Replace info
    ∇

      ExtractTutorial←{
          nr←⎕NR'Tutorial'
          (css lines)←↓⍉↑('⍝ ',⍵)∘Prep¨nr/⍨('⍝ ',⍵)∘(∨/⍷)¨nr
          (lines/⍨~css)(css/lines)
      }

      Prep←{
          c←#.Strings.dlb ⍵
          ⍺≡c↑⍨-l←≢⍺:0('\w+\.Add'⎕R'Add'⊢c↓⍨¯1-l)
          1(2↓c)
      }

    P←{∊('
',,∘'
')¨Ⓒ⍵}

    :ENDSECTION ⍝ ─────────────────────────────────────────────────────────────────────────────────
⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝

    :SECTION F_CONSTANTS ⍝ NILADIC FUNCTIONS THAT ACT LIKE :include'ABLE CONSTANTS

    ∇ r←NSS
      :Access public
      r←'DC' 'SF' 'JQ' 'JS' 'html'
    ∇

    ∇ r←GROUPS
      :Access public
      r←'Dyalog' 'Syncfusion' 'jQueryUI' 'JavaScript' 'Base HTML'
    ∇

    ∇ r←CACHE
      :Access public
      r←'#.CACHE'
    ∇

    ∇ r←FILEEXT
      :Access public
      r←#.Boot.ms.Config.DefaultExtension
    ∇

    ∇ C←C;scores;list;refs;srcs;control;i;info;ref;src;ctor;Read;Ø;demo
     ⍝ Return ref to cache (initialize cache if nonexistant)
      :Access public
      Read←#.Files.GetVTV #.Boot.AppRoot∘, ⍝ no-cache reading
      :Hold CACHE                          ⍝ prevent clashes
          :If 9≠⎕NC CACHE                  ⍝ if cache is empty:
              Ø←⊂''
              C←⍎CACHE ⎕NS ⍬       ⍝ create with shortcut
              C.read←⎕NS ⍬         ⍝ init cache for files
              C.read.(keys←data←⍬) ⍝ init keys and data

              C.controls←(#._.⎕NL ¯9)~⊂'Handler'                       ⍝ cache all controls
              C.controlsoi←C.controls∘⍳Ⓒ ⋄ C.eocontrols←∊∘C.controls Ⓒ ⍝ hash tables
              refs←#._⍎¨C.controls                                     ⍝ refs of all controls
              C.ns←3↓¨⍕¨refs.##                                        ⍝ ns of each control
              srcs←⎕SRC¨refs                                           ⍝ all sources

              C.files←⊃⍪/{List'Examples/',⍵}¨NSS,⊂'Applications' ⍝ sample filenames
              C.filesoi←C.files∘⍳Ⓒ                               ⍝ hash table
              C.filedescr←('Description'Section Read)¨C.files    ⍝ Description:: for all files
              C.demos←Controls¨C.files                           ⍝ controls demoed in each
              scores←C.controls∘.Score↓⍉↑C.files C.demos         ⍝ controls vs files
              C.rankings←(+/0<scores)↑¨↓⍒#.Utils.∆rank 1⊢scores  ⍝ cache all rankings

              C.descr←''
              C.ctor←''
              C.notes←''
              C.relevant←''
              C.reldocs←''
              C.doclinks←''
              info←FromCSV Read'IndexData/info.csv'
              :For control ref src :InEach C.controls refs srcs
                  i←info[;1]⍳⊂control
                  :If i≤≢info
                      C.descr,←info[i;2]
                      C.notes,←info[i;3]
                  :Else
                      C.descr,←⊂NoNL'Description'Section src
                      C.notes,←⊂NoNL'Notes'Section src
                  :EndIf

                  ctor←{⍵↑⍨¯1+⌊/⍵⍳⎕UCS 13 10}'Constructor'Section src
                  :If ''≡ctor
                      C.ctor,←'[content [attributes]]' '[attributes]'[1+(⎕NEW ref).NoEndTag]
                  :Else
                      C.ctor,←⊂ctor
                  :EndIf

                  :If C.eocontrols⊂control
                      C.relevant,←⊂C.files[C.rankings⊃⍨C.controlsoi⊂control]
                  :Else
                      C.relevant,←Ø
                  :EndIf

              :EndFor

              C.reldocs←(RelDocs¨C.controls),Ø
              C.doclink←(DocLink¨C.controls),Ø
              C.used←{⊂2↓∊', '∘,¨C.doclink[C.controlsoi¨⍵]}¨C.demos

              C.tasks←{Link('Description'Section Read ⍵)⍵}¨C.files

              C.type←'simple' 'advanced' 'mini-app'[1⍳⍨¨↓⍉∨/¨'Simple' 'Advanced'∘.⍷C.files]

              C.filedescr,←Ø ⍝ These cause an empty result if looked-up is not found
              C.demos,←Ø
              C.rankings,←Ø
              C.descr,←Ø
              C.ctor,←Ø
              C.notes,←Ø
              C.notes,←Ø
              C.ns,←Ø

          :Else
              C←⍎CACHE ⍝ establish shortcut
          :EndIf
      :EndHold
    ∇

      ForControl←{ ⍝ e.g. 'notes'ForControl'DataTable'
          (C.controlsoi⊂,⍵)⊃C⍎⍺
      }
      ForFile←{ ⍝ e.g. 'filedescr'ForFile'DataTable'
          (C.filesoi⊂,⍵)⊃C⍎⍺
      }

    :ENDSECTION ⍝ ─────────────────────────────────────────────────────────────────────────────────
⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝ ⍝

:EndNamespace


commented out ScriptFollows - shouldn't be needed here, can't be called from a derived class added isTrue and isFalse

:class HtmlPage : #.HtmlElement

    ⍝∇:require =\HtmlElement.dyalog

    :field public Head
    :field public Body
    :field public Scripts
    :field public Styles

    ∇ 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∊⍴Styles
          Head.Add¨Style¨∪⌽Styles
      :EndIf
      Content←(Head.Render),b
      r←'<!DOCTYPE html>',∊⎕BASE.Render
    ∇

    ∇ r←Render;s;b;mask;scr;sty
      :Access public
      :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←∪⌽Styles
          Head.Add¨Style¨sty
      :EndIf
      Content←(Head.Render),b
      r←'<!DOCTYPE html>',∊⎕BASE.Render
    ∇

    ∇ _init
      :Access public
      Body←⎕NEW #.HtmlElement'body'
      Head←⎕NEW #.HtmlElement'head'
      (Body Head)._PageRef←⎕THIS
      Scripts←''
      Styles←''
      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.Selectors ⋄ r.Selectors←'html' ⋄ :EndIf ⍝ if no selector specified, use page level
          :ElseIf ⊃∨/c∊¨⊂#._html.(title style meta link 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 ⊃∨/c∊¨⊂#._html.(title style meta link 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.Selectors←'body'
    ∇

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

    ∇ r←isClass ao
      :Access public
      r←9.4∊⎕NC⊂'ao'
    ∇

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

⍝    ∇ r←ScriptFollows
⍝      :Access public
⍝     ⍝ treat following commented lines in caller as a script, lines beginning with ⍝⍝ are stripped out
⍝      r←{⎕ML←1 ⋄ ∊{'⍝'=⊃⍵:'' ⋄ ' ',dtlb ⍵}¨1↓¨⍵/⍨∧\'⍝'=⊃¨⍵}dtlb¨(1+2⊃⎕LC)↓⎕NR 2⊃⎕SI
⍝    ∇

    ∇ r←isTrue a
      :Access public shared
      →0⍴⍨r←(,1)≡,a
      →0⍴⍨r←#.JSON.true≡a
      →0⍴⍨r←'true'≡a
    ∇

    ∇ r←isFalse a
      :Access public shared
      →0⍴⍨r←(,0)≡,a
      →0⍴⍨r←#.JSON.false≡a
      →0⍴⍨r←'false'≡a
    ∇

    ∇ 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 Styles

    ∇ 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∊⍴Styles
          Head.Add¨Style¨∪⌽Styles
      :EndIf
      Content←(Head.Render),b
      r←'<!DOCTYPE html>',∊⎕BASE.Render
    ∇

    ∇ r←Render;s;b;mask;scr;sty
      :Access public
      :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←∪⌽Styles
          Head.Add¨Style¨sty
      :EndIf
      Content←(Head.Render),b
      r←'<!DOCTYPE html>',∊⎕BASE.Render
    ∇

    ∇ _init
      :Access public
      Body←⎕NEW #.HtmlElement'body'
      Head←⎕NEW #.HtmlElement'head'
      (Body Head)._PageRef←⎕THIS
      Scripts←''
      Styles←''
      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.Selectors ⋄ r.Selectors←'html' ⋄ :EndIf ⍝ if no selector specified, use page level
          :ElseIf ⊃∨/c∊¨⊂#._html.(title style meta link 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 ⊃∨/c∊¨⊂#._html.(title style meta link 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.Selectors←'body'
    ∇

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

    ∇ r←isClass ao
      :Access public
      r←9.4∊⎕NC⊂'ao'
    ∇

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

    ∇ r←ScriptFollows
      :Access public
     ⍝ treat following commented lines in caller as a script, lines beginning with ⍝⍝ are stripped out
      r←{⎕ML←1 ⋄ ∊{'⍝'=⊃⍵:'' ⋄ ' ',dtlb ⍵}¨1↓¨⍵/⍨∧\'⍝'=⊃¨⍵}dtlb¨(1+2⊃⎕LC)↓⎕NR 2⊃⎕SI
    ∇
    ∇ r←Style style
      :Access public
      r←{(⎕NEW #._html.link).Set(('href'⍵)('rel' 'stylesheet')('type' 'text/css'))}style
    ∇

:endclass


Fix to make sure scripts/css files that the user adds to his page load after any framework scripts

: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 OnLoad←''       ⍝ page equivalent to ⎕LX
    :field Public Charset←'UTF-8' ⍝ default charset

    _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
    ∇

    ∇ 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 OnLoad←''       ⍝ page equivalent to ⎕LX
    :field Public Charset←'UTF-8' ⍝ default charset

    _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
    ∇

    ∇ 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