Don't open new tabs for MS2 sample site examples

: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 V16 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←1 #.Files.ReadText #.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
          ⍺←''
          '',⍺,'🔗'
      }

      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
          '',(⊃⍵),''
      }

      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)⍣⍺⊢⍵)⍵)
      }

      FilterTable←{
          dt←⍺ New _.DataTable ⍵
          dt.Width←'100%'
          dt.Options.autoWidth←_true
          dt.Options.mark←_true                ⍝ highlight search-terms
          sink←'lengthMenu'dt.Set(10 25 50 ¯1)(10 25 50 'All')  ⍝ also allow "All"
          dt.Options.columns←⎕NS¨4⍴⊂⍬          ⍝ options per column
          dt.Options.columns[4].visible←_false ⍝ hide column4
          dt.Options.dom←'l<"#fltcnt_',(1⊃⍺),'.fltcnt">ftipr'    ⍝ insert div with id "fltcnt_{id}" into table header
          sink←'yadcf.Filters[1].(column_number select_type filter_container_id)'dt.Set 3 'chosen'('fltcnt_',1⊃⍺)
          sink←'yadcf.Filters[1].(filter_type select_type_options filter_default_label)'dt.Set'multi_select'(⊂'{width:"30em"}')'Filter by library'
          dt
      }

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

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

    ∇ t←Elements;tabs;r;sp;fs;ig;colors;rgbs;button;p;cbs;t2;g
      t←'#div'New _.div'This div is the main container of this tab. ' ⍝ #div
      p←'#p't.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't.Add _.Button'Button' ⍝ #Button
      sp←'#StackPanel't.Add _.StackPanel ⍝ #StackPanel
      sp.Add'StackPanel'
      '#EditField'sp.Add _.EditField'EditField' 'EditField' ⍝ #EditField
      '#Select'sp.Add _.Select'Red' 'Green' 'Blue' ⍝ #Select
      '#Input1't.Add _.Input'number' 426 ' Number Input: ' ⍝ #Input1
      t.Add _.br
      fs←'#Fieldset1't.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't.Add _.Fieldset'RadioButtonGroup' ⍝ #Fieldset2
      '#RadioButtonGroup'fs.Add _.RadioButtonGroup('Selected' 'Unselected')1 'right' ⍝ #RadioButtonGroup
      '#h5't.Add _.h5'Slider' ⍝ #h52
      '#Input2't.Add _.Input'range' 1 ⍝ #Input2
      '#List't.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't.Add _.Table('Color' 'RGB'⍪colors,⍪rgbs)'' 1 ⍝ #Table
      t.Add _.style ScriptFollows
      ⍝ #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, #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;
      ⍝ }
      'ShowElementInfo'∘AddHandler¨'#div' '#p' '#span' '#Button' '#EditField' '#StackPanel' '#Select' '#Fieldset1' '#Fieldset2' '.CheckBox' '#RadioButtonGroup' '#Input1' '#h5' '#Input2' '#progress' '#List' '#Table'
      t←'Common elements't
    ∇

    ∇ r←ShowElementInfo;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←⊃_selector CodeFrom ⎕NR'Elements'
      info,←'
Put this in the Compose function
',P code
      r←'#info'Replace info
      r,←ShowPdf
    ∇

    ∇ t←Formatting
      t←'#margrad'New _.div'This div has inner padding and gradient backround' ⍝ #margrad
      '#boldin't.Add _.p'Bold indented text which has to be extremely long in order to show that only the first line is being indented.' ⍝ #boldin
      '#huge't.Add _.span'Huge text with outer margin' ⍝ #huge
      '#expund't.Add _.span'Expanded and underlined' ⍝ #expund
      '#bluebox't.Add _.p'Blue box with shadow' ⍝ #bluebox
      '#centshad't.Add _.p'Centered text with shadow' ⍝ #centshad
      '#whimg't.Add _.p'White text on image background' ⍝ #whimg
      '#border't.Add _.p'Surrounded by borders and top-right corner is round' ⍝ #border
      '#img' 'src="Examples/Data/4lines.png"' 'alt="Image"' 'title="Four lines high"'t.Add _.img ⍝ #img
      '#narrow't.Add _.p'Inline paragraph with limited width and single line height.' ⍝ #narrow
      '#mono't.Add _.span'Monospace, ' ⍝ #mono
      '#sans't.Add _.span'Sans-Serif, ' ⍝ #sans
      '#serif't.Add _.span'and Serif.' ⍝ #serif
      t.Add _.style ScriptFollows
      ⍝ #margrad {padding: 2em; background: linear-gradient(White, SkyBlue);}
      ⍝ #boldin {font-weight: bold; text-indent: 2em;}
      ⍝ #huge {font-size: x-large; margin: 2em;}
      ⍝ #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;}
      ⍝
      ⍝ /**/#boldin:hover, #huge:hover, #expund:hover, #centshad:hover, #serif:hover, #sans:hover, #mono:hover, #narrow:hover {
      ⍝     box-shadow: 0 0 5px 1px orange,0 0 5px orange inset;
      ⍝ }
      ⍝ /**/#margrad:hover {box-shadow: 0 0 5px 2px Orange inset;}
      ⍝ /**/#whimg:hover {box-shadow: 0 0 5px 2px Orange;}
      ⍝ /**/#border:hover {box-shadow: 0 0 5px 2px Orange, 0 0 5px 2px Orange inset;}
      ⍝ /**/#img:hover {box-shadow: 0 0 5px 3px Orange,0 0 5px 3px Orange;}
      ⍝ /**/#bluebox:hover {box-shadow:0 0 5px 1px Orange, 0 0 5px 1px Orange inset, 0.25em 0.5em 1em Gray;}
      'ShowInfo'∘AddHandler¨'#margrad' '#boldin' '#huge' '#expund' '#bluebox' '#centshad' '#whimg' '#serif' '#sans' '#mono' '#border' '#img' '#narrow'
      t←'Formatting't
    ∇

    ∇ t←Positioning
      Add _.style ScriptFollows
      ⍝ #parent {position: relative; height: 32em; margin: 1em;}
      ⍝ /**/#parent, #parent div {margin-top: 1em; margin-bottom: 1em; padding: 1ex;}
      ⍝ /**/#parent div {width: 50%; border: 1px solid black; }
      ⍝ #default {/* nothing to see here */}
      ⍝ #textcenter {text-align: center;}
      ⍝ #textright {text-align: right;}
      ⍝ #boxright {margin-left: auto; margin-right: 0;}
      ⍝ #boxcenter {margin-left: auto; margin-right: auto;}
      ⍝ #textbottom {position: relative; height: 4em;}
      ⍝ #textbottom span {position: absolute; bottom: 1ex;}
      ⍝ #boxbottom {position: absolute; bottom: 0;}
      ⍝
      ⍝ /**/#parent:hover, #parent div:hover {
      ⍝     box-shadow: 0 0 5px 1px orange,0 0 5px orange inset;
      ⍝ }
      t←'#parent'New _.div'Parent element with "relative" position so we can align vertically' ⍝ #parent
      '#default't.Add _.div'Default position and text alignment' ⍝ #default
      '#textcenter't.Add _.div'Centered text' ⍝ #textcenter
      '#textright't.Add _.div'Right-aligned text' ⍝ #textright
      '#boxcenter't.Add _.div'Centered element' ⍝ #boxcenter
      '#boxright't.Add _.div'Right-aligned element' ⍝ #boxright
      '#textbottom't.Add _.div,New _.span'Bottom-aligned text' ⍝ #textbottom
      '#boxbottom't.Add _.div'Bottom-aligned element' ⍝ #boxbottom
      'ShowInfo'∘AddHandler¨'#parent' '#default' '#textcenter' '#textright' '#boxcenter' '#boxright' '#textbottom' '#boxbottom'
      t←'Positioning't
    ∇

    ∇ t←Embedding;c
      Add _.style ScriptFollows
      ⍝ #embed>* {margin: 1ex;}
      ⍝ #quack {filter: hue-rotate(-205deg); padding: 2em;}
      ⍝ #quack:hover {filter:  hue-rotate(0deg);}
      ⍝ #bear {padding: 0.2em; border: 1px solid silver; margin: 1ex;}
      ⍝ #duck {height: 12em; transform: rotate(-15deg);}
      ⍝ #poly {height: 240px; width: 200px}
      ⍝ #poly polygon {fill: orange; stroke: purple; stroke-width: 1ex;}
      ⍝ #pdf {height: 240px; width:400px}
      ⍝
      ⍝ /*#bear:hover, #quack:hover, #duck:hover, #poly:hover, #embed>div>div:hover*/ #embed *:hover{
      ⍝     box-shadow: 0 0 5px 1px orange,0 0 5px orange inset;
      ⍝ }
      t←'#embed'New _.div
      t.Add _.br
      '#bear' 'src=Examples/Data/bear.mp4' 'autoplay=' 'loop=' 'muted='t.Add _.video'No video support!' ⍝ #bear
      '#quack' 'controls='t.Add _.Audio'Examples/Data/quack.mp3' ⍝ #quack
      '#duck' 'src=Examples/Data/duck.png' 'alt="Silly Duck"'t.Add _.img'No img support!' ⍝ #duck
      '#poly't.Add _.svg,'points=20,20 50,120 120,190 180,80'New _.polygon ⍝ #poly
      c←'style=display:inline-block;position:relative;'t.Add _.div
      '#pdf' 'style=position:absolute;height:100%;width:100%;top:0;' 'onclick="$(this).hide();"'c.Add _.div
      '#pdf' 'data=Examples/Data/licence.pdf'c.Add _.object ⍝ #pdf
      'ShowInfo'∘AddHandler¨'#bear' '#duck' '#quack' '#poly' '#pdf'
      t←'Embedding't
    ∇

    ∇ r←ShowPdf
      r←(_selector≢'#pdf')/Execute'$("#pdf").show();'
    ∇

    ∇ t←Interaction;cb;m
      Add _.style ScriptFollows
      ⍝ #ui>* {margin: 1ex;}
      ⍝ #adjust {width: 8em;}
      ⍝ #adjust:hover {text-align: justify;}
      ⍝ #adjust:active {text-align: right;}
      ⍝ #callback {width: 8em;}
      ⍝ #move {height: 6em; width: 12em; border: 1px solid black;}
      ⍝ #move div {height: 3em; width: 6em; cursor: move; background: silver;}
      ⍝ /**/ #ui>*:hover {box-shadow: 0 0 5px 1px orange,0 0 5px orange inset;}
      t←'#ui'New _.div
      '#adjust't.Add _.Button'Styling for reaction to hover and click' ⍝ #adjust
      cb←'#callback't.Add _.Button('Update with callback click ',#.Dates.(TSFmt ⎕TS)) ⍝ #callback
      cb.On'click' 'CallbackFn' ⍝ #callback
      '#move't.Add _.div,New _.div'Movable (drag me!)' ⍝ #move
      m←Add _.jqDraggable'#move div' ⍝ #move
      m.Set'containment' 'parent' ⍝ #move

      'ShowInfo'∘AddHandler¨'#adjust' '#callback' '#move'
      t←'Interaction't
    ∇

    ∇ r←CallbackFn ⍝ #callback
      :Access Public ⍝ #callback
      r←'#callback'Replace'Update with callback click ',#.Dates.(TSFmt ⎕TS) ⍝ #callback
    ∇ ⍝ #callback

    ∇ t←Windows;c;p;d
      Add _.style ScriptFollows
      ⍝ #win {margin: 1em; padding: 1ex;}
      ⍝ #win button {margin: 1em;}
      ⍝ #win #color {display: inline-block;}
      ⍝ #win #color:hover {box-shadow: 0 0 5px 1px orange,0 0 5px orange inset;}
      ⍝ #css {width: 6em; padding: 1ex; background: silver; border-radius: 0.5ex; box-shadow: 0.25em 0.5em 2em grey;}
      ⍝ #dialog {display: none;}
      ⍝ /**/ #css:hover {width: 6em; background: silver; box-shadow: 0 0 5px 1px orange,0 0 5px orange inset,0.25em 0.5em 2em grey;}
      ⍝ /**/ #win>button:hover {box-shadow: 0 0 5px 1px orange,0 0 5px orange inset;}
      t←'#win'New _.div
      t.Add _.p'Choose a color to get a matching notification:'
      d←'#color't.Add _.div
      d.On'mousedown' 'ShowInfo'
      c←d.Add _.ejColorPicker'#ffff00' ⍝ #color
      c.On'close' 'ColorFn'('chosenColor' 'model' 'value') ⍝ #color
      '#css't.Add _.div'CSS to give floating impression' ⍝ #css

      '#dialog't.Add _.div'Content of dialog box widget' ⍝ #dialog
      d←t.Add _.ejDialog'#dialog' 'Syncfusion Dialog' ⍝ #dialog
      'showOnInit'd.Set _false ⍝ #dialog
      'onclick="$(''#dialog'').ejDialog(''open'')"'t.Add _.Button'Open dialog' ⍝ #dialog
      Add _.Handler'#win button' 'click' 'DialogFn'

      'ShowInfo'AddHandler'#css'
      t←'Windows & Popups't
    ∇

    ∇ r←ColorFn;chosen;all;options ⍝ #color
      :Access public ⍝ #color
      chosen←0 #.Utils.hex⍕↓3 2⍴1↓Get'chosenColor' ⍝ #color
      all←(0 0 0)(255 0 0)(0 255 0)(0 0 255)(319 319 0)(255 255 255) ⍝ #color
      (options←⎕NS ⍬).color←'black' 'red' 'green' 'blue' 'yellow' 'black'⊃⍨(⊢⍳⌊/)+/¨2*⍨all-⊂chosen ⍝ #color
      r←Execute options _.jBox.Notice('You chose ',Get'chosenColor') ⍝ #color
    ∇ ⍝ #color

    ∇ r←ShowDialog
      :Access public
      _selector←'#dialog'
      r←ShowInfo
    ∇

    ∇ r←ShowInfo;info;css;code;fns
      :Access Public
      (code css)←_selector CodeFrom ⎕SRC⍎NoExt⊃⎕XSI ⍝ this ns
      code←SplitFns code
      info←'
Put this in the Compose function
',P Break⊃code
      fns←1↓code
      :If ×≢css
          info,←'
Put Add _.style''…'' in Compose or add  this to the stylesheet
'
          info,←P Break css
      :EndIf
      :If ×≢fns
          info,←'
Add ','this funtion' 'these functions'⊃⍨2⌊≢fns
          info,←' to the MiPage
',P Break¨fns
      :EndIf
      r←'#info'Replace info
      r,←ShowPdf
    ∇

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

    Break←{4↓∊'
'∘,¨⍵}

      CodeFrom←{
          (css lines)←↓⍉↑('⍝ ',⍺)∘Prep¨⍵/⍨('⍝ ',⍺)∘(∨/⍷)¨⍵
          (lines/⍨~css)(css/lines)
      }

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

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

    SplitFns←{⍵⊂⍨1,2</≠\'∇'=⊃¨⍵}

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

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

    ∇ r←NSS;types;names
      :Access public
      :If 0=C.⎕NC'NSS'
          C.NSS←1↓¨1↓'_'#.⎕NL ¯9
          names types←0 1 ⎕NINFO⍠1⊢#.Boot.AppRoot,'Examples/*'
          C.NSS∩←LastSeg¨names/⍨1=types
      :EndIf
      r←C.NSS
    ∇

    ∇ r←GROUPS
      :Access public
      :If 0=C.⎕NC'GROUPS'
          C.GROUPS←{#.Strings.deb(⍳∘'⍝'↓⊢)⊃⎕SRC #⍎⍵}¨'_',¨NSS
      :EndIf
      r←C.GROUPS
    ∇

    ∇ 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←1 #.Files.ReadText #.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)↑¨↓⍒⍤ 1⊢scores  ⍝ cache all rankings

              C.(descr ctor notes relevant reldocs 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.cats←GROUPS[NSS⍳3↓¨⍕¨('#._'∘⍎¨C.controls).##]

              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]

              ⍝ These cause an empty result if looked-up is not found
              C.(filedescr demos rankings descr ctor notes notes 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


Accordion now honours user's id instead of always having id="myAccordian"

:Namespace JQUI
    (⎕IO ⎕ML)←1
⍝ == JQUI - JQueryUI
⍝ For more information:
⍝ * JQuery - http://jquery.com
⍝ * JQUery UI - http://jqueryui.com

    CRLF←⎕UCS 13 10

⍝ --- Utility functions ---

    ine←{0∊⍴⍺:'' ⋄ ⍵}  ⍝ if not empty
    eis←{(,∘⊂)⍣((326∊⎕DR ⍵)<2>|≡⍵),⍵} ⍝ Enclose if simple
    enlist←{⎕ML←1 ⋄ ∊⍵} ⍝ APL2 style enlist
    quote←{'"'∊⍵:⍵ ⋄ '"',⍵,'"'}
    isSingleNum←{(,1)≡⊃⎕VFI ⍵}
    firstAfter←{pos str←⍵ ⋄ pos+1⍳⍨⍺⍷pos↓str} ⍝ return position of first occurrence after a position
    empty←{0∊⍴⍵}
    SignalError←{}∘{0≠1↑⍵: ⍺ ⎕SIGNAL ⍵ ⋄ ⍵}
    makeID←{'#'=1↑⍵:⍵ ⋄ '#',⍵}

    ∇ r←class insertClass attrs;m;beg;end;q;f
     ⍝ insert class into a list of attributes, if there's already a class= attribute append class to it
      :If ~∨/m←'class='⍷attrs ⋄ r←'class="',class,'"',(0∊⍴attrs)↓' ',attrs ⋄ :Return ⋄ :EndIf
      q←(6+beg←m⍳1)⊃attrs,' '
      :If q∊'''"' ⋄ end←¯1+q firstAfter(6+beg)attrs ⋄ r←(end↑attrs),' ',class,end↓attrs
      :Else
          f←~(⍴attrs)≥end←¯1+' 'firstAfter(5+beg)attrs
          r←attrs
          r[beg+5]←⊂'="'
          r←enlist((end-f)↑r),(f↓' ',class,'"'),end↓r
      :EndIf
    ∇

⍝ --- Effects ---

    ∇ r←Show pars;selector;effect;options;duration;complete
    ⍝ pars[1] selector to act upon
    ⍝     [2] the effect to use (see http://api.jqueryui.com/category/effects/ for list of effects)
    ⍝     [3] any options for the effect (JSON formatted - 'direction : "up"')
    ⍝     [4] duration in ms or string ("slow", "fast", etc)
      r←'show'jqEffect pars
    ∇

    ∇ r←Hide pars;selector;effect;options;duration;complete
      r←'hide'jqEffect pars
    ∇

    ∇ r←Toggle pars;selector;effect;options;duration;complete
      r←'toggle'jqEffect pars
    ∇

    ∇ r←Effect pars;complete;duration;options;effect;fn;selector
      r←'effect'jqEffect pars
    ∇

    ∇ r←fn jqEffect pars;selector;effect;options;duration;complete
      pars←eis pars
      (selector effect options duration complete)←5↑pars,(⍴pars)↓5⍴⊂''
      options←options ine',{',({'{}'≡2↑¯1⌽⍵:1↓¯1↓⍵ ⋄ ⍵}options),'}'
      duration←duration ine',',{((quote⍣(~isSingleNum ⍵))⍵)}duration
      complete←complete ine',function(){',fn,'}'
      r←'$("',selector,'").',fn,'("',effect,'"',options,duration,complete,');'
    ∇

⍝ --- Widget functions ---

    ∇ r←{req}Accordion pars;id;hdrs;content;jqpars;oname;chain;myid
    ⍝ req - HTTPRequest object
    ⍝ id - the id for the accordion
    ⍝ jqpars - Accordion parameters
    ⍝ hdrs - n-element array of header names for each accordion folder
    ⍝ content - n-element array of content for each accordion folder
    ⍝ updates req.Response.head and returns html
      pars←eis pars
      id hdrs content jqpars chain oname←6↑pars,(⍴pars)↓'' '' '' '' '' ''
      :If ⎕NULL≡myid←id ⍝ AB: from #.HtmlElement.SetId
          :If ''≡myid←⊃Attrs[⊂'id']
              id←myid←GenId
          :Else
              id←myid
          :EndIf
      :EndIf
      :If 9=⎕NC'req' ⋄ req.Use'JQueryUI' ⋄ :EndIf
      hdrs←#.OldHTML.h3¨('href' '#')∘#.OldHTML.a¨hdrs
      content←#.OldHTML.div¨content
      r←'id'id #.OldHTML.div,hdrs,[1.1]content
      r,←#.JQ.JQueryfn'accordion'(makeID id)jqpars chain oname
    ∇

    ∇ r←{req}Autocomplete pars;id;jqpars;oname;chain;strings
    ⍝ req - HTTPRequest object
    ⍝ id - the selector(s) (generally ids) for items to be dragged
    ⍝ jqpars - Draggable parameters (if supplied, will override the default)
    ⍝ updates req.Response.head and returns html
      pars←eis pars
      id strings jqpars chain oname←5↑pars,(⍴pars)↓'myAutocomplete'(0⍴⊂'')'' '' ''
      :If 9=⎕NC'req' ⋄ req.Use'JQueryUI' ⋄ :EndIf
      jqpars,←(0∊⍴jqpars)↓',source: ',#.JSON.fromArray strings
      r←#.JQ.JQueryfn'autocomplete'(makeID id)jqpars chain oname
    ∇

    ∇ r←{req}Button pars;id;jqpars;caption;buttonpars;oname;chain
      :If 9=⎕NC'req' ⋄ req.Use'JQueryUI' ⋄ :EndIf
      pars←eis pars
      id caption buttonpars jqpars chain oname←6↑pars,(⍴pars)↓'' '' '' '' '' ''
      r←(('id'id),{0∊⍴⍵:⍵ ⋄ eis ⍵}buttonpars)#.OldHTML.button caption
      r,←#.JQ.JQueryfn'button'(makeID id)jqpars chain oname
    ∇

    ∇ r←{req}DatePicker pars;id;editpars;jqpars;oname;chain
    ⍝ req - HTTPRequest object
    ⍝ pars - id editpars jqpars
    ⍝ id - the id for the datepicker
    ⍝ editpars - parameters for the edit field (see HTMLInput.Edit)
    ⍝ jqpars - datepicker parameters
    ⍝ updates req.Response.head and returns html
      :If 9=⎕NC'req' ⋄ req.Use'JQueryUI' ⋄ :EndIf
      pars←eis pars
      id editpars jqpars chain oname←5↑pars,(⍴pars)↓''⍬'' '' ''
      :If 0∊⍴id ⋄ id←'myDatePicker' ⋄ :EndIf
      r←id #.HTMLInput.Edit editpars
      r,←#.JQ.JQueryfn'datepicker'(makeID id)jqpars chain oname
    ∇

    ∇ r←{req}Dialog pars;id;title;innerhtml;jqpars;chain;oname
    ⍝ req - HTTPRequest object
    ⍝ id - the id for the dialog
    ⍝ jqpars - Dialog parameters
    ⍝ title - title for the dialog
    ⍝ innerhtml - body for the dialog
    ⍝ updates req.Response.head and returns html
      pars←eis pars
      id title innerhtml jqpars chain oname←6↑pars,(⍴pars)↓'' '' '' '' '' ''
      :If 9=⎕NC'req' ⋄ req.Use'JQueryUI' ⋄ :EndIf
      r←''
      r←((title∘ine¨'title'title),(id∘ine¨'id'id))#.OldHTML.div innerhtml
      r,←#.JQ.JQueryfn'dialog'(makeID id)jqpars chain oname
    ∇

    ∇ r←{req}Draggable pars;id;jqpars;oname;chain
    ⍝ req - HTTPRequest object
    ⍝ id - the selector(s) (generally ids) for items to be dragged
    ⍝ jqpars - Draggable parameters (if supplied, will override the default)
    ⍝ updates req.Response.head and returns html
      pars←eis pars
      id jqpars chain oname←4↑pars,(⍴pars)↓'' '' '' ''
      'Draggable requires the element id to be specified'⎕SIGNAL(0∊⍴id)/6
      :If 9=⎕NC'req' ⋄ req.Use'JQueryUI' ⋄ :EndIf
      r←#.JQ.JQueryfn'draggable'(makeID id)jqpars chain oname
    ∇

    ∇ r←page Droppable pars;jqpars;accept;id;update;page;dtype;success;oname;chain
    ⍝ req - HTTPRequest object
    ⍝ id - the selector(s) for the droppable elements
    ⍝ accept - the selectors(s) for what can be dropped on them
    ⍝ update - the selector(s) for the element whose html to update if the server returns data (empty if no data expected)
    ⍝ jqpars - Droppable parameters (if supplied, will override the default)
    ⍝ updates req.Response.head and returns html
    ⍝
    ⍝ NB: the default behavior expects id attributes on both the dragger and droppee

      :Select ⊃⎕NC'page'
      :Case 9 ⋄ page.Use'JQueryUI' ⋄ page←page.Page ⍝ page is the request object
      :EndSelect
      page←quote page

      pars←eis pars
      id accept update jqpars chain oname←6↑pars,(⍴pars)↓'' '' '' '' '' ''

      'id and accept cannot be empty'SignalError 900×∨/empty¨id accept

      :If empty jqpars
          :If 0∊⍴update ⍝ if no response element specified
              dtype←'"json"'
              success←'success: function(obj){$.each(obj,function(i,d){if(typeof(d.replace)!=="undefined"){$(d.replace).html(d.data);}else if(typeof(d.append)!=="undefined"){$(d.append).append(d.data);}else if(typeof(d.prepend)!=="undefined"){$(d.prepend).prepend(d.data);}else if(typeof(d.execute)!=="undefined"){eval(d.execute);}});}'
          :Else
              dtype←'"html"'
              success←'success: function(d){$(',(quote update),').empty().html(d);}'
          :EndIf
          jqpars←'accept: "',accept,'", activeClass: "ui-state-default", hoverClass: "ui-state-hover", '
          jqpars,←'drop: function(evt, ui){$.ajax({url:',page,', cache:false, type:"POST", dataType:',dtype,', data:{event:"drop",drag:ui.draggable.attr("id"),receiver:$(this).attr("id")},',success,'});}'
      :EndIf
      r←#.JQ.JQueryfn'droppable'(makeID id)jqpars chain oname
    ∇

    ∇ r←{req}Resizable pars;id;jqpars;oname;chain
    ⍝ req - HTTPRequest object
    ⍝ id - the selector(s) (generally ids) for items to be dragged
    ⍝ jqpars - Draggable parameters (if supplied, will override the default)
    ⍝ updates req.Response.head and returns html
      pars←eis pars
      id jqpars chain oname←4↑pars,(⍴pars)↓'' '' '' ''
      'Resizable requires the element id to be specified'⎕SIGNAL(0∊⍴id)/6
      :If 9=⎕NC'req' ⋄ req.Use'JQueryUI' ⋄ :EndIf
      :If 0∊⍴jqpars ⋄ jqpars←'appendTo: "body", helper: "clone"' ⋄ :EndIf
      r←#.JQ.JQueryfn'resizable'(makeID id)jqpars chain oname
    ∇

    ∇ r←{req}Selectable pars;id;jqpars;oname;chain
    ⍝ req - HTTPRequest object
    ⍝ id - the selector(s) (generally ids) for items to be dragged
    ⍝ jqpars - Draggable parameters (if supplied, will override the default)
    ⍝ updates req.Response.head and returns html
      pars←eis pars
      id jqpars chain oname←4↑pars,(⍴pars)↓'' '' '' ''
      'Resizable requires the element id to be specified'⎕SIGNAL(0∊⍴id)/6
      :If 9=⎕NC'req' ⋄ req.Use'JQueryUI' ⋄ :EndIf
      :If 0∊⍴jqpars ⋄ jqpars←'appendTo: "body", helper: "clone"' ⋄ :EndIf
      r←#.JQ.JQueryfn'resizable'(makeID id)jqpars chain oname
    ∇

    ∇ r←{req}Sortable pars;ids;lists;styles;usehd;jqpars;chain;liststyle;itemstyle;style;js;callback;lids;cbcode;cbids;i;update;oname
      pars←eis pars
      usehd ids lists styles jqpars chain callback update oname←9↑pars,(⍴pars)↓0 '' '' '' '' '' 0 '' ''
      :If 9=⎕NC'req'
          req.Use'JQueryUI'
          :If 0∊⍴styles ⋄ liststyle←itemstyle←''
          :Else ⋄ liststyle itemstyle←styles
          :EndIf
          liststyle←liststyle,⍨'list-style-type' 'none' 'margin' 0 'padding' 0 'float' 'left' 'margin-right' '10px' 'background' '#eee' 'padding' '5px' 'width' '143px'
          itemstyle←itemstyle,⍨'margin' '5px' 'padding' '5px' 'font-size' '1.2em' 'width' '120px'
          ids←eis ids
          style←⊂('#'∘,¨ids)#.HTMLInput.MakeStyle liststyle
          style,←⊂('#'∘,¨ids,¨⊂' li')#.HTMLInput.MakeStyle itemstyle
          style req.Style''
      :EndIf

      lists←eis¨lists
      :If callback
          lids←(⊂¨ids),¨¨'_'∘,∘⍕¨¨⍳∘⍴¨lists ⍝ list IDs
          lids←'li class="ui-state-default" id="'∘,∘(,∘'"')¨¨lids
          lists←lids #.HTMLInput.Enclose¨¨lists
      :Else
          lists←'li class="ui-state-default"'∘#.HTMLInput.Enclose¨¨lists
      :EndIf
      :If usehd
          lists←{w←⍵ ⋄ f←'ui-state-disabled'insertClass 1⊃w ⋄ w[1]←⊂f ⋄ w}¨lists
      :EndIf
      lists←(1∘⌽¨'"ul id="'∘,¨ids)#.HTMLInput.Enclose¨lists
      jqpars←2↓enlist', '∘,¨(⊂'')~⍨((1<⍴lists)/'connectWith: "ul"')(usehd/'items: "li:not(.ui-state-disabled)"')jqpars
      :If callback
          js←{'{',2↓(enlist ⍵),'}'}{', ',⍵,': $("#',⍵,'").sortable("serialize")'}¨ids
          cbcode←', update: function(){$.post("',req.Page,'", ',js
          :If ~empty update
              cbcode,←', function(d){$("',update,'").empty().html(d)}'
          :EndIf
          jqpars,←(2×empty jqpars)↓cbcode,')}'
      :EndIf
      chain←'.disableSelection()',chain
      js←#.JQ.JQueryfn'sortable'(makeID¨ids)jqpars chain oname
      r←(enlist CRLF∘,¨lists),CRLF,js,'div style="clear: both;"'#.HTMLInput.Enclose''
    ∇

    ∇ r←{req}Tabs pars;id;tabnames;content;jqpars;uris;tabids;hrefs;oname;chain
    ⍝ req - HTTPRequest object
    ⍝ id - the id for the tabs
    ⍝ jqpars - Tabs parameters
    ⍝ tabnames - n-element vector of charvec of names to appear on the tabs
    ⍝ content - n-element vector of charvecs with the HTML content for each tab
    ⍝ updates req.Response.head and returns html
      :If 9=⎕NC'req' ⋄ req.Use'JQueryUI' ⋄ :EndIf
      pars←eis pars
      id tabnames content jqpars chain oname←6↑pars,(⍴pars)↓'' '' '' '' '' ''
      :If 0∊⍴id ⋄ id←'myTabs' ⋄ :EndIf
      tabnames←eis tabnames
      :If 1<|≡id
          tabids←1↓id
          id←1⊃id
      :Else
          tabids←id∘,¨'-'∘,¨⍕¨⍳⍴tabnames
      :EndIf
      content←eis content
      uris←#.Files.LikelyURL¨content ⍝ identify likely URIs
      hrefs←'#',¨tabids
      (uris/hrefs)←uris/content
      hrefs←'a href='∘,¨#.HTMLInput.quote¨hrefs
      r←'ul'#.HTMLInput.Enclose enlist'li'∘#.HTMLInput.Enclose¨hrefs #.HTMLInput.Enclose¨tabnames
      :If ~∧/uris
          r,←enlist('div id="'∘,¨(,∘'"')¨(~uris)/tabids)#.HTMLInput.Enclose¨(~uris)/content
      :EndIf
      r←('div id="',id,'"')#.HTMLInput.Enclose r
      r,←#.JQ.JQueryfn'tabs'(makeID id)jqpars chain oname
    ∇

    ∇ r←{req}Slider pars;id;jqpars;cur;max;min;exec;oname;chain
    ⍝ req - HTTPRequest object
    ⍝ id
    ⍝ min - minimum value
    ⍝ max - maximum value
    ⍝ cur - current value
    ⍝ exec - script to execute when value changes
    ⍝ jqpars - Draggable parameters (if supplied, will override the default)
    ⍝ updates req.Response.head and returns html
      pars←eis pars
      id min max cur exec jqpars chain oname←8↑pars,(⍴pars)↓'' 1 10 1 '' '' '' ''
      :If empty id ⋄ 'id cannot be empty'⎕SIGNAL 11 ⋄ :EndIf ⍝ id is required
      :If 9=⎕NC'req' ⋄ req.Use'JQueryUI' ⋄ :EndIf
      jqpars,←(0∊⍴jqpars)↓',min: ',(⍕min),', max: ',(⍕max),', value: ',(⍕cur),(~0∊exec)/', slide: function(evt,ui){',exec,(';'=¯1↑exec)↓';}'
      r←#.JQ.JQueryfn'slider'(makeID id)jqpars chain oname
    ∇

:EndNamespace

:Namespace JQUI
    (⎕IO ⎕ML)←1
⍝ == JQUI - JQueryUI
⍝ For more information:
⍝ * JQuery - http://jquery.com
⍝ * JQUery UI - http://jqueryui.com

    CRLF←⎕UCS 13 10

⍝ --- Utility functions ---

    ine←{0∊⍴⍺:'' ⋄ ⍵}  ⍝ if not empty
    eis←{(,∘⊂)⍣((326∊⎕DR ⍵)<2>|≡⍵),⍵} ⍝ Enclose if simple
    enlist←{⎕ML←1 ⋄ ∊⍵} ⍝ APL2 style enlist
    quote←{'"'∊⍵:⍵ ⋄ '"',⍵,'"'}
    isSingleNum←{(,1)≡⊃⎕VFI ⍵}
    firstAfter←{pos str←⍵ ⋄ pos+1⍳⍨⍺⍷pos↓str} ⍝ return position of first occurrence after a position
    empty←{0∊⍴⍵}
    SignalError←{}∘{0≠1↑⍵: ⍺ ⎕SIGNAL ⍵ ⋄ ⍵}
    makeID←{'#'=1↑⍵:⍵ ⋄ '#',⍵}

    ∇ r←class insertClass attrs;m;beg;end;q;f
     ⍝ insert class into a list of attributes, if there's already a class= attribute append class to it
      :If ~∨/m←'class='⍷attrs ⋄ r←'class="',class,'"',(0∊⍴attrs)↓' ',attrs ⋄ :Return ⋄ :EndIf
      q←(6+beg←m⍳1)⊃attrs,' '
      :If q∊'''"' ⋄ end←¯1+q firstAfter(6+beg)attrs ⋄ r←(end↑attrs),' ',class,end↓attrs
      :Else
          f←~(⍴attrs)≥end←¯1+' 'firstAfter(5+beg)attrs
          r←attrs
          r[beg+5]←⊂'="'
          r←enlist((end-f)↑r),(f↓' ',class,'"'),end↓r
      :EndIf
    ∇

⍝ --- Effects ---

    ∇ r←Show pars;selector;effect;options;duration;complete
    ⍝ pars[1] selector to act upon
    ⍝     [2] the effect to use (see http://api.jqueryui.com/category/effects/ for list of effects)
    ⍝     [3] any options for the effect (JSON formatted - 'direction : "up"')
    ⍝     [4] duration in ms or string ("slow", "fast", etc)
      r←'show'jqEffect pars
    ∇

    ∇ r←Hide pars;selector;effect;options;duration;complete
      r←'hide'jqEffect pars
    ∇

    ∇ r←Toggle pars;selector;effect;options;duration;complete
      r←'toggle'jqEffect pars
    ∇

    ∇ r←Effect pars;complete;duration;options;effect;fn;selector
      r←'effect'jqEffect pars
    ∇

    ∇ r←fn jqEffect pars;selector;effect;options;duration;complete
      pars←eis pars
      (selector effect options duration complete)←5↑pars,(⍴pars)↓5⍴⊂''
      options←options ine',{',({'{}'≡2↑¯1⌽⍵:1↓¯1↓⍵ ⋄ ⍵}options),'}'
      duration←duration ine',',{((quote⍣(~isSingleNum ⍵))⍵)}duration
      complete←complete ine',function(){',fn,'}'
      r←'$("',selector,'").',fn,'("',effect,'"',options,duration,complete,');'
    ∇

⍝ --- Widget functions ---

    ∇ r←{req}Accordion pars;id;hdrs;content;jqpars;oname;chain
    ⍝ req - HTTPRequest object
    ⍝ id - the id for the accordion
    ⍝ jqpars - Accordion parameters
    ⍝ hdrs - n-element array of header names for each accordion folder
    ⍝ content - n-element array of content for each accordion folder
    ⍝ updates req.Response.head and returns html
      pars←eis pars
      id hdrs content jqpars chain oname←6↑pars,(⍴pars)↓'' '' '' '' '' ''
      :If 0∊⍴id ⋄ id←'myAccordian' ⋄ :EndIf
      :If 9=⎕NC'req' ⋄ req.Use'JQueryUI' ⋄ :EndIf
      hdrs←#.OldHTML.h3¨('href' '#')∘#.OldHTML.a¨hdrs
      content←#.OldHTML.div¨content
      r←'id'id #.OldHTML.div,hdrs,[1.1]content
      r,←#.JQ.JQueryfn'accordion'(makeID id)jqpars chain oname
    ∇

    ∇ r←{req}Autocomplete pars;id;jqpars;oname;chain;strings
    ⍝ req - HTTPRequest object
    ⍝ id - the selector(s) (generally ids) for items to be dragged
    ⍝ jqpars - Draggable parameters (if supplied, will override the default)
    ⍝ updates req.Response.head and returns html
      pars←eis pars
      id strings jqpars chain oname←5↑pars,(⍴pars)↓'myAutocomplete'(0⍴⊂'')'' '' ''
      :If 9=⎕NC'req' ⋄ req.Use'JQueryUI' ⋄ :EndIf
      jqpars,←(0∊⍴jqpars)↓',source: ',#.JSON.fromArray strings
      r←#.JQ.JQueryfn'autocomplete'(makeID id)jqpars chain oname
    ∇

    ∇ r←{req}Button pars;id;jqpars;caption;buttonpars;oname;chain
      :If 9=⎕NC'req' ⋄ req.Use'JQueryUI' ⋄ :EndIf
      pars←eis pars
      id caption buttonpars jqpars chain oname←6↑pars,(⍴pars)↓'' '' '' '' '' ''
      r←(('id'id),{0∊⍴⍵:⍵ ⋄ eis ⍵}buttonpars)#.OldHTML.button caption
      r,←#.JQ.JQueryfn'button'(makeID id)jqpars chain oname
    ∇

    ∇ r←{req}DatePicker pars;id;editpars;jqpars;oname;chain
    ⍝ req - HTTPRequest object
    ⍝ pars - id editpars jqpars
    ⍝ id - the id for the datepicker
    ⍝ editpars - parameters for the edit field (see HTMLInput.Edit)
    ⍝ jqpars - datepicker parameters
    ⍝ updates req.Response.head and returns html
      :If 9=⎕NC'req' ⋄ req.Use'JQueryUI' ⋄ :EndIf
      pars←eis pars
      id editpars jqpars chain oname←5↑pars,(⍴pars)↓''⍬'' '' ''
      :If 0∊⍴id ⋄ id←'myDatePicker' ⋄ :EndIf
      r←id #.HTMLInput.Edit editpars
      r,←#.JQ.JQueryfn'datepicker'(makeID id)jqpars chain oname
    ∇

    ∇ r←{req}Dialog pars;id;title;innerhtml;jqpars;chain;oname
    ⍝ req - HTTPRequest object
    ⍝ id - the id for the dialog
    ⍝ jqpars - Dialog parameters
    ⍝ title - title for the dialog
    ⍝ innerhtml - body for the dialog
    ⍝ updates req.Response.head and returns html
      pars←eis pars
      id title innerhtml jqpars chain oname←6↑pars,(⍴pars)↓'' '' '' '' '' ''
      :If 9=⎕NC'req' ⋄ req.Use'JQueryUI' ⋄ :EndIf
      r←''
      r←((title∘ine¨'title'title),(id∘ine¨'id'id))#.OldHTML.div innerhtml
      r,←#.JQ.JQueryfn'dialog'(makeID id)jqpars chain oname
    ∇

    ∇ r←{req}Draggable pars;id;jqpars;oname;chain
    ⍝ req - HTTPRequest object
    ⍝ id - the selector(s) (generally ids) for items to be dragged
    ⍝ jqpars - Draggable parameters (if supplied, will override the default)
    ⍝ updates req.Response.head and returns html
      pars←eis pars
      id jqpars chain oname←4↑pars,(⍴pars)↓'' '' '' ''
      'Draggable requires the element id to be specified'⎕SIGNAL(0∊⍴id)/6
      :If 9=⎕NC'req' ⋄ req.Use'JQueryUI' ⋄ :EndIf
      r←#.JQ.JQueryfn'draggable'(makeID id)jqpars chain oname
    ∇

    ∇ r←page Droppable pars;jqpars;accept;id;update;page;dtype;success;oname;chain
    ⍝ req - HTTPRequest object
    ⍝ id - the selector(s) for the droppable elements
    ⍝ accept - the selectors(s) for what can be dropped on them
    ⍝ update - the selector(s) for the element whose html to update if the server returns data (empty if no data expected)
    ⍝ jqpars - Droppable parameters (if supplied, will override the default)
    ⍝ updates req.Response.head and returns html
    ⍝
    ⍝ NB: the default behavior expects id attributes on both the dragger and droppee

      :Select ⊃⎕NC'page'
      :Case 9 ⋄ page.Use'JQueryUI' ⋄ page←page.Page ⍝ page is the request object
      :EndSelect
      page←quote page

      pars←eis pars
      id accept update jqpars chain oname←6↑pars,(⍴pars)↓'' '' '' '' '' ''

      'id and accept cannot be empty'SignalError 900×∨/empty¨id accept

      :If empty jqpars
          :If 0∊⍴update ⍝ if no response element specified
              dtype←'"json"'
              success←'success: function(obj){$.each(obj,function(i,d){if(typeof(d.replace)!=="undefined"){$(d.replace).html(d.data);}else if(typeof(d.append)!=="undefined"){$(d.append).append(d.data);}else if(typeof(d.prepend)!=="undefined"){$(d.prepend).prepend(d.data);}else if(typeof(d.execute)!=="undefined"){eval(d.execute);}});}'
          :Else
              dtype←'"html"'
              success←'success: function(d){$(',(quote update),').empty().html(d);}'
          :EndIf
          jqpars←'accept: "',accept,'", activeClass: "ui-state-default", hoverClass: "ui-state-hover", '
          jqpars,←'drop: function(evt, ui){$.ajax({url:',page,', cache:false, type:"POST", dataType:',dtype,', data:{event:"drop",drag:ui.draggable.attr("id"),receiver:$(this).attr("id")},',success,'});}'
      :EndIf
      r←#.JQ.JQueryfn'droppable'(makeID id)jqpars chain oname
    ∇

    ∇ r←{req}Resizable pars;id;jqpars;oname;chain
    ⍝ req - HTTPRequest object
    ⍝ id - the selector(s) (generally ids) for items to be dragged
    ⍝ jqpars - Draggable parameters (if supplied, will override the default)
    ⍝ updates req.Response.head and returns html
      pars←eis pars
      id jqpars chain oname←4↑pars,(⍴pars)↓'' '' '' ''
      'Resizable requires the element id to be specified'⎕SIGNAL(0∊⍴id)/6
      :If 9=⎕NC'req' ⋄ req.Use'JQueryUI' ⋄ :EndIf
      :If 0∊⍴jqpars ⋄ jqpars←'appendTo: "body", helper: "clone"' ⋄ :EndIf
      r←#.JQ.JQueryfn'resizable'(makeID id)jqpars chain oname
    ∇

    ∇ r←{req}Selectable pars;id;jqpars;oname;chain
    ⍝ req - HTTPRequest object
    ⍝ id - the selector(s) (generally ids) for items to be dragged
    ⍝ jqpars - Draggable parameters (if supplied, will override the default)
    ⍝ updates req.Response.head and returns html
      pars←eis pars
      id jqpars chain oname←4↑pars,(⍴pars)↓'' '' '' ''
      'Resizable requires the element id to be specified'⎕SIGNAL(0∊⍴id)/6
      :If 9=⎕NC'req' ⋄ req.Use'JQueryUI' ⋄ :EndIf
      :If 0∊⍴jqpars ⋄ jqpars←'appendTo: "body", helper: "clone"' ⋄ :EndIf
      r←#.JQ.JQueryfn'resizable'(makeID id)jqpars chain oname
    ∇

    ∇ r←{req}Sortable pars;ids;lists;styles;usehd;jqpars;chain;liststyle;itemstyle;style;js;callback;lids;cbcode;cbids;i;update;oname
      pars←eis pars
      usehd ids lists styles jqpars chain callback update oname←9↑pars,(⍴pars)↓0 '' '' '' '' '' 0 '' ''
      :If 9=⎕NC'req'
          req.Use'JQueryUI'
          :If 0∊⍴styles ⋄ liststyle←itemstyle←''
          :Else ⋄ liststyle itemstyle←styles
          :EndIf
          liststyle←liststyle,⍨'list-style-type' 'none' 'margin' 0 'padding' 0 'float' 'left' 'margin-right' '10px' 'background' '#eee' 'padding' '5px' 'width' '143px'
          itemstyle←itemstyle,⍨'margin' '5px' 'padding' '5px' 'font-size' '1.2em' 'width' '120px'
          ids←eis ids
          style←⊂('#'∘,¨ids)#.HTMLInput.MakeStyle liststyle
          style,←⊂('#'∘,¨ids,¨⊂' li')#.HTMLInput.MakeStyle itemstyle
          style req.Style''
      :EndIf

      lists←eis¨lists
      :If callback
          lids←(⊂¨ids),¨¨'_'∘,∘⍕¨¨⍳∘⍴¨lists ⍝ list IDs
          lids←'li class="ui-state-default" id="'∘,∘(,∘'"')¨¨lids
          lists←lids #.HTMLInput.Enclose¨¨lists
      :Else
          lists←'li class="ui-state-default"'∘#.HTMLInput.Enclose¨¨lists
      :EndIf
      :If usehd
          lists←{w←⍵ ⋄ f←'ui-state-disabled'insertClass 1⊃w ⋄ w[1]←⊂f ⋄ w}¨lists
      :EndIf
      lists←(1∘⌽¨'"ul id="'∘,¨ids)#.HTMLInput.Enclose¨lists
      jqpars←2↓enlist', '∘,¨(⊂'')~⍨((1<⍴lists)/'connectWith: "ul"')(usehd/'items: "li:not(.ui-state-disabled)"')jqpars
      :If callback
          js←{'{',2↓(enlist ⍵),'}'}{', ',⍵,': $("#',⍵,'").sortable("serialize")'}¨ids
          cbcode←', update: function(){$.post("',req.Page,'", ',js
          :If ~empty update
              cbcode,←', function(d){$("',update,'").empty().html(d)}'
          :EndIf
          jqpars,←(2×empty jqpars)↓cbcode,')}'
      :EndIf
      chain←'.disableSelection()',chain
      js←#.JQ.JQueryfn'sortable'(makeID¨ids)jqpars chain oname
      r←(enlist CRLF∘,¨lists),CRLF,js,'div style="clear: both;"'#.HTMLInput.Enclose''
    ∇

    ∇ r←{req}Tabs pars;id;tabnames;content;jqpars;uris;tabids;hrefs;oname;chain
    ⍝ req - HTTPRequest object
    ⍝ id - the id for the tabs
    ⍝ jqpars - Tabs parameters
    ⍝ tabnames - n-element vector of charvec of names to appear on the tabs
    ⍝ content - n-element vector of charvecs with the HTML content for each tab
    ⍝ updates req.Response.head and returns html
      :If 9=⎕NC'req' ⋄ req.Use'JQueryUI' ⋄ :EndIf
      pars←eis pars
      id tabnames content jqpars chain oname←6↑pars,(⍴pars)↓'' '' '' '' '' ''
      :If 0∊⍴id ⋄ id←'myTabs' ⋄ :EndIf
      tabnames←eis tabnames
      :If 1<|≡id
          tabids←1↓id
          id←1⊃id
      :Else
          tabids←id∘,¨'-'∘,¨⍕¨⍳⍴tabnames
      :EndIf
      content←eis content
      uris←#.Files.LikelyURL¨content ⍝ identify likely URIs
      hrefs←'#',¨tabids
      (uris/hrefs)←uris/content
      hrefs←'a href='∘,¨#.HTMLInput.quote¨hrefs
      r←'ul'#.HTMLInput.Enclose enlist'li'∘#.HTMLInput.Enclose¨hrefs #.HTMLInput.Enclose¨tabnames
      :If ~∧/uris
          r,←enlist('div id="'∘,¨(,∘'"')¨(~uris)/tabids)#.HTMLInput.Enclose¨(~uris)/content
      :EndIf
      r←('div id="',id,'"')#.HTMLInput.Enclose r
      r,←#.JQ.JQueryfn'tabs'(makeID id)jqpars chain oname
    ∇

    ∇ r←{req}Slider pars;id;jqpars;cur;max;min;exec;oname;chain
    ⍝ req - HTTPRequest object
    ⍝ id
    ⍝ min - minimum value
    ⍝ max - maximum value
    ⍝ cur - current value
    ⍝ exec - script to execute when value changes
    ⍝ jqpars - Draggable parameters (if supplied, will override the default)
    ⍝ updates req.Response.head and returns html
      pars←eis pars
      id min max cur exec jqpars chain oname←8↑pars,(⍴pars)↓'' 1 10 1 '' '' '' ''
      :If empty id ⋄ 'id cannot be empty'⎕SIGNAL 11 ⋄ :EndIf ⍝ id is required
      :If 9=⎕NC'req' ⋄ req.Use'JQueryUI' ⋄ :EndIf
      jqpars,←(0∊⍴jqpars)↓',min: ',(⍕min),', max: ',(⍕max),', value: ',(⍕cur),(~0∊exec)/', slide: function(evt,ui){',exec,(';'=¯1↑exec)↓';}'
      r←#.JQ.JQueryfn'slider'(makeID id)jqpars chain oname
    ∇

:EndNamespace


Took a closer look at _HTML.Table, found the internal comments to be incorrect, and fixed a bug in generation of RowIds.

:Class Table : #._html.table
⍝ Description: Improved html table
⍝ Constructor: {Data} {CellAttr} {HeaderRows} {HeaderAttr} {MakeCellIds} {MakeRowIds}

⍝ Public Fields:
⍝ Data        - matrix of data to display in the table
⍝ CellAttr    - Cell Attributes
⍝ HeaderRows  - # of header rows
⍝ HeaderAttr  - Header attributes
⍝ MakeCellIds - 1 to generate IDs
⍝ MakeRowIds  - 1 to generate Row IDs

    :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
      :If 1<⍴⍴data ⋄ data←,⊂data ⋄ :EndIf
      (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←{z⊣(z←⎕NEW #._html.th).Add ⍵}¨hdrrows↑data
          :If cellids
              head.id←{∊'rc',¨⍕¨⍵}¨⍳⍴head
          :EndIf
          :If ~0∊⍴tha
              head.Set¨(⍴head)⍴⊂¨eis tha
          :EndIf
      :EndIf
      :If 0<(⊃⍴data)-hdrrows
          body←{z⊣(z←⎕NEW #._html.td).Add ⍵}¨hdrrows↓data
          :If cellids
              body.id←{∊'rc',¨⍕¨⍵}¨hdrrows↓⍳⍴data
          :EndIf
          :If ~0∊⍴tda
              body.Set¨(⍴body)⍴⊂¨eis tda
          :EndIf
      :EndIf
      :If rows>0
          (table←⎕NEW¨rows⍴#._html.tr).Add↓head⍪body
          :If rowids
              table.id←{'row',⍕⍵}⍳rows
          :EndIf
      :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

:class Table : #._html.table
⍝ Description: Improved html table
⍝ Constructor: {Data} {CellAttr} {HeaderRows} {HeaderAttr} {MakeCellIds} {MakeRowIds}
⍝ data - matrix of data to display in the table
⍝ cellAttr - the id/name for the button
⍝ headerRows - the id/name for the button
⍝ headerAttr - the id/name for the button
⍝ makeCellIds - the text to appear on the button
⍝ makeRowIds - one of 'submit' (default), 'reset', or 'button'
⍝ Public Fields:
⍝ Text - the text to appear on the button
    :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
      :If 1<⍴⍴data ⋄ data←,⊂data ⋄ :EndIf
      (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←{z⊣(z←⎕NEW #._html.th).Add ⍵}¨hdrrows↑data
          :If cellids
              head.id←{∊'rc',¨⍕¨⍵}¨⍳⍴head
          :EndIf
          :If ~0∊⍴tha
              head.Set¨(⍴head)⍴⊂¨eis tha
          :EndIf
      :EndIf
      :If 0<(⊃⍴data)-hdrrows
          body←{z⊣(z←⎕NEW #._html.td).Add ⍵}¨hdrrows↓data
          :If cellids
              body.id←{∊'rc',¨⍕¨⍵}¨hdrrows↓⍳⍴data
          :EndIf
          :If ~0∊⍴tda
              body.Set¨(⍴body)⍴⊂¨eis tda
          :EndIf
      :EndIf
      :If rows>0
          (table←⎕NEW¨rows⍴#._html.tr).Add↓head⍪body
          :If rowids
              table.id←{'row',⍕⍵}⍳rows
          :EndIf
      :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


Set default character set to UTF-8

Set default character set to UTF-8

:Class MiPage : #.HtmlPage

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

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

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

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

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

    ∇ MakeCommon
      _PageData←⎕NS''
    ∇

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

      (_head _body)←(Head Body).Content
      (_styles _scripts)←_Styles _Scripts
      host←{6::⍵ ⋄ '//',_Request.Host}''

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

      b←RenderBody

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

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

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

      :If ~0∊⍴Handlers
          b,←∊Handlers.Render
      :EndIf

      :If ''≢OnLoad
          b,←(⎕NEW #._html.script('$(function(){',OnLoad,'});')).Render
      :EndIf

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

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

      r←RenderPage b

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

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

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

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

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

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

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

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

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

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

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

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

    ∇ r←renderContent content;c
      :Access public shared
      r←''
      content←eis content
      :While ~0∊⍴content
          :Select ≡c←⊃content
          :Case 0
              :If isClass c
                  :Select ⊃⍴content
                  :Case 1
                      r,←(⎕NEW c).Render
                  :Case 2
                      r,←(⎕NEW c(2⊃content)).Render
                  :Else
                      r,←(⎕NEW c(1↓content)).Render
                  :EndSelect
              :ElseIf isInstance c
                  r,←c.Render
              :Else
                  r,←(⎕NEW #.HtmlElement(''content)).Render
              :EndIf
              content←''
          :Case 1
              :If isClass⊃c
                  r,←(⎕NEW(⊃c)(1↓c)).Render
              :ElseIf isInstance⊃c
                  ∘∘∘ ⍝ should not happen! (I think)
              :Else
                  r,←(⎕NEW #.HtmlElement(''c)).Render
              :EndIf
              content←1↓content
          :Else
              r,←renderContent c
              content←1↓content
          :EndSelect
      :EndWhile
    ∇

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

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

    :endsection

    :section Position

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    :endsection

:EndClass

:Class MiPage : #.HtmlPage

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

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

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

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

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

    ∇ MakeCommon
      _PageData←⎕NS''
    ∇

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

      (_head _body)←(Head Body).Content
      (_styles _scripts)←_Styles _Scripts
      host←{6::⍵ ⋄ '//',_Request.Host}''
      :If ''≢OnLoad
          {0:: ⋄ Use'JQuery'}''
      :EndIf
      b←RenderBody
      :If ~0∊⍴_Scripts
          {(Insert _html.script).Set('src=',⍵)}¨host∘AddHost¨⌽∪_Scripts
      :EndIf
      styles←∪_Styles
      styles←styles,⍨{0∊⍴⍵:⍵ ⋄ ⊂⍵}_CssReset
      styles←styles,{0∊⍴⍵:⍵ ⋄ ⊂⍵}_CssOverride
      :If ~0∊⍴styles
          {Insert #._DC.StyleSheet ⍵}¨host∘AddHost¨⌽∪styles
      :EndIf
      :If ~0∊⍴Handlers
          b,←∊Handlers.Render
      :EndIf
      :If ''≢OnLoad
          b,←(⎕NEW #._html.script('$(function(){',OnLoad,'});')).Render
      :EndIf
      :If 0∊⍴⊃Attrs[⊂'lang'] ⍝ set the language for the page if not already set
          {0:: ⋄ {Set'lang="',⍵,'" xml:lang="',⍵,'" xmlns="http://www.w3.org/1999/xhtml"'}_Request.Server.Config.Lang}''
      :EndIf
      r←RenderPage b
      :If 0≠⎕NC⊂'_Request.Response'
          _Request.Response.HTML←r
      :EndIf
      (Head Body).Content←_head _body
      _Styles←_styles
      _Scripts←_scripts
      _used←''
    ∇

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

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

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

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

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

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

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

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

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

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

    ∇ r←renderContent content;c
      :Access public shared
      r←''
      content←eis content
      :While ~0∊⍴content
          :Select ≡c←⊃content
          :Case 0
              :If isClass c
                  :Select ⊃⍴content
                  :Case 1
                      r,←(⎕NEW c).Render
                  :Case 2
                      r,←(⎕NEW c(2⊃content)).Render
                  :Else
                      r,←(⎕NEW c(1↓content)).Render
                  :EndSelect
              :ElseIf isInstance c
                  r,←c.Render
              :Else
                  r,←(⎕NEW #.HtmlElement(''content)).Render
              :EndIf
              content←''
          :Case 1
              :If isClass⊃c
                  r,←(⎕NEW(⊃c)(1↓c)).Render
              :ElseIf isInstance⊃c
                  ∘∘∘ ⍝ should not happen! (I think)
              :Else
                  r,←(⎕NEW #.HtmlElement(''c)).Render
              :EndIf
              content←1↓content
          :Else
              r,←renderContent c
              content←1↓content
          :EndSelect
      :EndWhile
    ∇

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

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

    :endsection

    :section Position

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    :endsection

:EndClass