Code display's line numbers are now neither selected, nor copied as part of selecting and copying code.

:Namespace HtmlUtils

    (⎕IO ⎕ML)←1
    ⎕FX 'r←CRLF' 'r←⎕UCS 13 10' ⍝ So it will be :Included
    enlist←{⎕ML←1 ⋄ ∊⍵} ⍝ APL2 enlist
    eis←{(,∘⊂)⍣((326∊⎕DR ⍵)<2>|≡⍵),⍵} ⍝ Enclose if simple
    ine←{0∊⍴⍺:'' ⋄ ⍵} ⍝ if not empty
    ischar←{0 2∊⍨10|⎕DR⍵}
    quote←{0∊⍴⍵:'' ⋄ '"',({w←⍵⋄((w='"')/w)←⊂'\"'⋄enlist w}⍵),'"'}
    iotaz←{(⍴⍺){⍵×⍺≥⍵}⍺⍳⍵}
    innerhtml←{⊃↓/(⍵ iotaz'>')(-(⌽⍵)iotaz'<') ⍵}
    dtlb←{⍵{((∨\⍵)∧⌽∨\⌽⍵)/⍺}' '≠⍵}
   ⍝ tonum←{0∊⍴⍵:⍬ ⋄ w←⍵ ⋄ ((w='-')/w)←'¯' ⋄ ⊃(//)⎕VFI w}

    ∇ r←atts Enclose innerhtml;i
    ⍝ Put an HTML tag around some HTML
      :If 1<|≡innerhtml ⋄ innerhtml←CRLF,enlist innerhtml,¨⊂CRLF ⋄ :EndIf
      :If 0∊⍴atts
          r←innerhtml
      :Else
          i←¯1+(atts←,atts)⍳' '
          r←'<',atts,'>',innerhtml,'</',(i↑atts),'>',CRLF
      :EndIf
    ∇

    ∇ r←{nl}Tag tag
    ⍝ Make a  self-closing tag
      nl←{6::1 ⋄ nl}⍬
      r←'<',tag,' />',nl/CRLF
    ∇

      FormatAttrs←{
      ⍝ format name/value pairs as tag attributes
      ⍝  ⍵ - name/value pairs, valid forms:
      ⍝  'name="value"'
      ⍝  [n,2⍴] 'name1' 'value1' ['name2' 'value2'...]
      ⍝ ('name1' 'value1') [('name2' 'value2')]
          0∊⍴⍵:''
          {
              enlist{(×⍴⍺)/' ',⍺,(×⍴⍵)/'=',quote ⍵}/,∘⍕¨⊃⍵
          }_pifn¨,2 _box _pifn{
              1=|≡⍵:⍵
              2=|≡⍵:{1=⍴⍴⍵:(⌽2,0.5×⍴⍵)⍴⍵ ⋄ ⍵}⍵
              ↑⍵}⍵
      }

    ∇ r←tag GetAttr attr;attrs
      r←''
      :Trap 6
          →0⍴⍨0∊⍴tag←⎕XML tag
      :EndTrap
      attrs←(⊂1 4)⊃tag
      r←(attrs[;1]⍳⊂attr)⊃attrs[;2],⊂''
    ∇

      Styles←{
      ⍝ format name/value pairs as CSS style attributes
      ⍝  ⍵ - name/value pairs, valid forms:
      ⍝  'name: value'
      ⍝  [n,2⍴] 'name1' 'value1' ['name2' 'value2'...]
      ⍝ ('name1' 'value1') [('name2' 'value2')]
          ⍺←''
          0∊⍴⍵:⍺,'{}'
          (0∊⍴⍺)↓⍺,{'{',({';'=¯1↑⍵:⍵ ⋄ ⍵,';'}⍵),'}'}{
              enlist{(×⍴⍺)/⍺,(×⍴⍵)/':',⍵,';'}/,∘⍕¨⊃⍵
          }_pifn¨,2 _box _pifn{
              1=|≡⍵:⍵
              2=|≡⍵:{1=⍴⍴⍵:(⌽2,0.5×⍴⍵)⍴⍵ ⋄ ⍵}⍵
              ↑⍵}⍵
      }

    _box←{⍺←1 ⋄ (⊂⍣(⍺=|≡⍵))⍵}
    _pifn←{({⍵''}⍣(1=|≡⍵))⍵}

    ∇ r←ScriptFollows;lines;pgm;from
     ⍝ Treat following commented lines in caller as a script, lines beginning with ⍝⍝ are stripped out
      :If 0∊⍴lines←(from←⎕IO⊃⎕RSI).⎕NR pgm←2⊃⎕SI
          lines←↓from.(180⌶)pgm
      :EndIf
      r←2↓∊CRLF∘,¨{⍵/⍨'⍝'≠⊃¨⍵}{1↓¨⍵/⍨∧\'⍝'=⊃¨⍵}dtlb¨(1+2⊃⎕LC)↓lines
    ∇

    ∇ html←TextToHTML html;mask;CR
    ⍝ Add/insert
, replaces CR with
,CR
      :If ~0∊⍴html
          :If ∨/mask←html=CR←''⍴CRLF
              (mask/html)←⊂'
',CR
              html←enlist html
          :EndIf
          html,←(~∨/¯2↑mask)/'
',CRLF
      :EndIf
    ∇

    ∇ html←{fontsize}APLToHTML APL
    ⍝ returns APL code formatted for HTML
      fontsize←{6::'' ⋄ ';fontsize:',⍎⍵}'fontsize'
      :If 1<|≡APL ⋄ APL←enlist,∘CRLF¨APL ⋄ :EndIf
      :Trap 0
          html←3↓¯4↓'whitespace' 'preserve'⎕XML 1 3⍴0 'x'APL
      :Else
          html←APL
      :EndTrap
      html←('pre style="font-family:APL385 Unicode',fontsize,'"')Enclose CRLF,⍨html
    ∇

    ∇ html←APLToHTMLColor APL;types;colors;class;codes;apply;lines;head;tail;c;ent;to;style;nums
     ⍝ returns APL code formatted for HTML with syntax coloring
      to←{(¯1↓⍺),((¯1+⊃⌽⍺)+⍳1+(⊃⍵)-(⊃⌽⍺)),1↓⍵}
      :Trap 0
          colors←⍬
          colors,←⊂'i200comment'(1 26 63)
          colors,←⊂'i200char'(4 29)
          colors,←⊂'i200num'(5 30)
          colors,←⊂'i200local'(10 32 35 53)
          colors,←⊂'i200global'(7 52 55)
          colors,←⊂'i200primitive'(19 44 146 to 153 214 to 221)
          colors,←⊂'i200idiom'(23 48)
          colors,←⊂'i200control'(58 155 to 179 181 to 213 222 to 248)
          colors,←⊂'i200space'(8 9 33 34)
          colors,←⊂'i200quad'(12 to 15 37 to 40)

          html←({(+/∨\' '≠⌽⍵)↑¨↓⍵}⍣(1≥|≡APL))APL ⍝ Make VTV if matrix
          html,¨←' '
          lines←∊1↑¨⍨≢¨html
          types←0,0,⍨∊200⌶html                        ⍝ 200⌶ is color coding

          :For c ent :InEach '&<'('&' '<')
              ((c⍷∊html)/∊html)←⊂⊂ent
          :EndFor

          html←' ',' ',⍨⊃,/html
          :For class codes :In colors
              apply←1 0⍷types∊codes
              (apply/html)←(apply/html),¨⊂''
          :EndFor
          :For class codes :In colors
              apply←0 1⍷types∊codes
              (apply/html)←(apply/html),¨⊂''
          :EndFor
          head←1↓⊃html ⋄ tail←¯1↓⊃⌽html
          html←lines⊂1↓¯1↓html
          (⊃html),⍨←head ⋄ (⊃⌽html),←tail
          html←∊¨html
          nums←⍕¨¯1+⍳≢html
          style←''
          html,¨⍨←'' ⍝ Prepend line numbers
          html←style,'pre'Enclose'code'Enclose html
      :Else
          html←APLToHTML APL
      :EndTrap
    ∇

      MakeStyle←{
          ⍺←''
          0∊⍴⍵:''
          (' ',⍨¯2↓enlist(eis ⍺),¨⊂', '),Styles ⍵
      }

    ∇ r←HtmlSafeText txt;i;m;u;ucs;s
    ⍝ make text HTML "safe"
      r←,⎕FMT txt
      i←'&<>"#'⍳r
      i-←(i=1)∧1↓(i=5),0 ⍝ mark & that aren't &#
      m←i∊⍳4
      u←127<ucs←⎕UCS r
      s←' '=r
      (s/r)←⊂' '
      (m/r)←('&' '<' '>' '"')[m/i]
      (u/r)←(~∘' ')¨↓'G<&#ZZZ9;>'⎕FMT u/ucs
      r←enlist r
    ∇

:EndNamespace

:Namespace HtmlUtils

    (⎕IO ⎕ML)←1
    ⎕FX 'r←CRLF' 'r←⎕UCS 13 10' ⍝ So it will be :Included
    enlist←{⎕ML←1 ⋄ ∊⍵} ⍝ APL2 enlist
    eis←{(,∘⊂)⍣((326∊⎕DR ⍵)<2>|≡⍵),⍵} ⍝ Enclose if simple
    ine←{0∊⍴⍺:'' ⋄ ⍵} ⍝ if not empty
    ischar←{0 2∊⍨10|⎕DR⍵}
    quote←{0∊⍴⍵:'' ⋄ '"',({w←⍵⋄((w='"')/w)←⊂'\"'⋄enlist w}⍵),'"'}
    iotaz←{(⍴⍺){⍵×⍺≥⍵}⍺⍳⍵}
    innerhtml←{⊃↓/(⍵ iotaz'>')(-(⌽⍵)iotaz'<') ⍵}
    dtlb←{⍵{((∨\⍵)∧⌽∨\⌽⍵)/⍺}' '≠⍵}
   ⍝ tonum←{0∊⍴⍵:⍬ ⋄ w←⍵ ⋄ ((w='-')/w)←'¯' ⋄ ⊃(//)⎕VFI w}

    ∇ r←atts Enclose innerhtml;i
    ⍝ Put an HTML tag around some HTML
      :If 1<|≡innerhtml ⋄ innerhtml←CRLF,enlist innerhtml,¨⊂CRLF ⋄ :EndIf
      :If 0∊⍴atts
          r←innerhtml
      :Else
          i←¯1+(atts←,atts)⍳' '
          r←'<',atts,'>',innerhtml,'</',(i↑atts),'>',CRLF
      :EndIf
    ∇

    ∇ r←{nl}Tag tag
    ⍝ Make a  self-closing tag
      nl←{6::1 ⋄ nl}⍬
      r←'<',tag,' />',nl/CRLF
    ∇

      FormatAttrs←{
      ⍝ format name/value pairs as tag attributes
      ⍝  ⍵ - name/value pairs, valid forms:
      ⍝  'name="value"'
      ⍝  [n,2⍴] 'name1' 'value1' ['name2' 'value2'...]
      ⍝ ('name1' 'value1') [('name2' 'value2')]
          0∊⍴⍵:''
          {
              enlist{(×⍴⍺)/' ',⍺,(×⍴⍵)/'=',quote ⍵}/,∘⍕¨⊃⍵
          }_pifn¨,2 _box _pifn{
              1=|≡⍵:⍵
              2=|≡⍵:{1=⍴⍴⍵:(⌽2,0.5×⍴⍵)⍴⍵ ⋄ ⍵}⍵
              ↑⍵}⍵
      }

    ∇ r←tag GetAttr attr;attrs
      r←''
      :Trap 6
          →0⍴⍨0∊⍴tag←⎕XML tag
      :EndTrap
      attrs←(⊂1 4)⊃tag
      r←(attrs[;1]⍳⊂attr)⊃attrs[;2],⊂''
    ∇

      Styles←{
      ⍝ format name/value pairs as CSS style attributes
      ⍝  ⍵ - name/value pairs, valid forms:
      ⍝  'name: value'
      ⍝  [n,2⍴] 'name1' 'value1' ['name2' 'value2'...]
      ⍝ ('name1' 'value1') [('name2' 'value2')]
          ⍺←''
          0∊⍴⍵:⍺,'{}'
          (0∊⍴⍺)↓⍺,{'{',({';'=¯1↑⍵:⍵ ⋄ ⍵,';'}⍵),'}'}{
              enlist{(×⍴⍺)/⍺,(×⍴⍵)/':',⍵,';'}/,∘⍕¨⊃⍵
          }_pifn¨,2 _box _pifn{
              1=|≡⍵:⍵
              2=|≡⍵:{1=⍴⍴⍵:(⌽2,0.5×⍴⍵)⍴⍵ ⋄ ⍵}⍵
              ↑⍵}⍵
      }

    _box←{⍺←1 ⋄ (⊂⍣(⍺=|≡⍵))⍵}
    _pifn←{({⍵''}⍣(1=|≡⍵))⍵}

    ∇ r←ScriptFollows;lines;pgm;from
     ⍝ Treat following commented lines in caller as a script, lines beginning with ⍝⍝ are stripped out
      :If 0∊⍴lines←(from←⎕IO⊃⎕RSI).⎕NR pgm←2⊃⎕SI
          lines←↓from.(180⌶)pgm
      :EndIf
      r←2↓∊CRLF∘,¨{⍵/⍨'⍝'≠⊃¨⍵}{1↓¨⍵/⍨∧\'⍝'=⊃¨⍵}dtlb¨(1+2⊃⎕LC)↓lines
    ∇

    ∇ html←TextToHTML html;mask;CR
    ⍝ Add/insert
, replaces CR with
,CR
      :If ~0∊⍴html
          :If ∨/mask←html=CR←''⍴CRLF
              (mask/html)←⊂'
',CR
              html←enlist html
          :EndIf
          html,←(~∨/¯2↑mask)/'
',CRLF
      :EndIf
    ∇

    ∇ html←{fontsize}APLToHTML APL
    ⍝ returns APL code formatted for HTML
      fontsize←{6::'' ⋄ ';fontsize:',⍎⍵}'fontsize'
      :If 1<|≡APL ⋄ APL←enlist,∘CRLF¨APL ⋄ :EndIf
      :Trap 0
          html←3↓¯4↓'whitespace' 'preserve'⎕XML 1 3⍴0 'x'APL
      :Else
          html←APL
      :EndTrap
      html←('pre style="font-family:APL385 Unicode',fontsize,'"')Enclose CRLF,⍨html
    ∇

    ∇ html←APLToHTMLColor APL;types;colors;class;codes;apply;lines;head;tail;c;ent;to
     ⍝ returns APL code formatted for HTML with syntax coloring
       to←{(¯1↓⍺),((¯1+⊃⌽⍺)+⍳1+(⊃⍵)-(⊃⌽⍺)),1↓⍵}
      :Trap 0
          colors←⍬
          colors,←⊂'i200comment'(1 26 63)
          colors,←⊂'i200char'(4 29)
          colors,←⊂'i200num'(5 30)
          colors,←⊂'i200local'(10 32 35 53)
          colors,←⊂'i200global'(7 52 55)
          colors,←⊂'i200primitive'(19 44 146 to 153 214 to 221)
          colors,←⊂'i200idiom'(23 48)
          colors,←⊂'i200control'(58 155 to 179 181 to 213 222 to 248)
          colors,←⊂'i200space'(8 9 33 34)
          colors,←⊂'i200quad'(12 to 15 37 to 40)

          html←({(+/∨\' '≠⌽⍵)↑¨↓⍵}⍣(1≥|≡APL))APL ⍝ Make VTV if matrix
          html,¨←' '
          lines←∊1↑¨⍨≢¨html
          types←0,0,⍨∊200⌶html                        ⍝ 200⌶ is color coding

          :For c ent :InEach '&<'('&' '<')
              ((c⍷∊html)/∊html)←⊂⊂ent
          :EndFor

          html←' ',' ',⍨⊃,/html
          :For class codes :In colors
              apply←1 0⍷types∊codes
              (apply/html)←(apply/html),¨⊂''
          :EndFor
          :For class codes :In colors
              apply←0 1⍷types∊codes
              (apply/html)←(apply/html),¨⊂''
          :EndFor
          head←1↓⊃html ⋄ tail←¯1↓⊃⌽html
          html←lines⊂1↓¯1↓html
          (⊃html),⍨←head ⋄ (⊃⌽html),←tail
          html←∊¨html
          html,⍨¨←,∘''¨↓↑('['∘,,∘']')¨⍕¨¯1+⍳⍴html ⍝ Prepend line numbers
          html←'pre'Enclose'code'Enclose html
      :Else
          html←APLToHTML APL
      :EndTrap
    ∇

      MakeStyle←{
          ⍺←''
          0∊⍴⍵:''
          (' ',⍨¯2↓enlist(eis ⍺),¨⊂', '),Styles ⍵
      }

    ∇ r←HtmlSafeText txt;i;m;u;ucs;s
    ⍝ make text HTML "safe"
      r←,⎕FMT txt
      i←'&<>"#'⍳r
      i-←(i=1)∧1↓(i=5),0 ⍝ mark & that aren't &#
      m←i∊⍳4
      u←127<ucs←⎕UCS r
      s←' '=r
      (s/r)←⊂' '
      (m/r)←('&' '<' '>' '"')[m/i]
      (u/r)←(~∘' ')¨↓'G<&#ZZZ9;>'⎕FMT u/ucs
      r←enlist r
    ∇

:EndNamespace


Executes even with ?. All messages to STDERR. Better error messages. Handles single line code. Displays length-1 strings with ,¨ .

 output←regexes(op Run args)input;from;to;options;lines;Expand;repeat;s;r;last;fns;q;hasrepeat;postproc;combine;Quote;Nest;a;n
 output←⍬⊤⍬ ⍝ default result is empty matrix

 n←⎕UCS 10 ⍝ newline
 a←⎕UCS 39 ⍝ apostrofe
 Nest←{⊂⍣(1=≡,⍵)⊢⍵}
 Quote←{a,a,⍨⍵/⍨1+⍵=a}

 :Trap 11 22 ⍝ accept filename or actual input
     input←input ⎕NTIE 0
 :EndTrap

 :Trap 11 22 ⍝ accept filename or actual regex
     regexes←⊃⎕NGET regexes 1
 :EndTrap
 regexes←{,¨⍣(¯2=≡⍵)⊢⍵}Nest regexes

 repeat←⍎{×≢⍵:⍵ ⋄ '1'}args∩'≡',⎕D ⍝ extract repetition, if any, else 1
 hasrepeat←∨/args∊⍨'≡',⎕D
 ⍝ select options:
 options←('ResultText' 'Simple')('Greedy' 0)('IC' 1)('Mode' 'D')('Mode' 'M')('DotAll' 1)('UCP' 1)('OM' 1)/⍨1,'gidmauo'∊819⌶args~' -'
 s r←(∨/∊∘op)¨'Ss' 'Rr'
 :If s∨r
     options↓⍨←s ⍝ ResultText is not for ⎕S

     fns←+/∧\'⍵'∊¨regexes
     postproc←∊'⊢','∘{'∘,¨,∘'}'¨fns↑regexes ⍝ post-processing functions
     regexes↓⍨←fns

     last←⊃regexes↓⍨¯1+≢regexes
     :If '⍵'∊last ⍝ transformation function → many-to-function
     :OrIf 2|≢regexes ⍝ odd number of strings → many-to-one
         from←¯1↓regexes ⍝ remove transformation pattern/function
         :If ~'⍵'∊last ⍝ no ⍵ → pattern
             Expand←'⍵B' '⍵b' '⍵P' '⍵p' '⍵M' '⍵O' '⍵L' '⍵N'⎕R'⍵.Block' '⍵.BlockNum' '⍵.Pattern' '⍵.PatternNum' '⍵.Match' '⍵.Offsets' '⍵.Lengths' '⍵.Names'
         :OrIf ~≡⎕FX,⊂'to←{',(r/',⍕{'),(Expand last),(r/'}⍵'),'}' ⍝ if definition fails, revert to pattern (allows ⍵ in pattern)
             to←last
         :EndIf
     :Else
         lines←2÷⍨≢regexes
         from←regexes↓⍨-lines
         to←lines↓regexes
     :EndIf

     combine←'{⊃,/⍵↑¨⍨⌈/≢¨⍵}'
     q←⎕NS ⍬
     q.from←⍕Quote¨Nest from
     :If 0=≢q.from
         q.from←a a
     :EndIf
     :If 1∧.=≢¨from
         q.(from←'(,¨',from,')')
     :EndIf
     :If 3=⎕NC'to'
         q.to←1⌽'  ',3↓∊⎕NR'to'
     :Else
         q.to←⍕Quote¨Nest to
         :If 0=≢q.to
             q.to←a a
         :EndIf
         :If 1∧.=≢¨to
             q.(to←'(,¨',to,')')
         :EndIf
     :EndIf
     :If 3=⎕NC'repeat'
         q.repeat←'⍣',∊⎕NR'repeat'
     :ElseIf 1≢repeat
         q.repeat←'⍣',⍕repeat
     :Else
         q.repeat←''
     :EndIf
     :If ×≢options
         q.options←'⍠',∊{' (',(Quote⊃⍵),' ',({0 1∊⍨⊂⍵:⍕⍵ ⋄ Quote ⍵}⊃⌽⍵),')'}¨options
     :Else
         q.options←''
     :EndIf
     q.op←s r/'SR'
     q.postproc←2↓postproc
     q.postproc,←(hasrepeat∧s)/(''≡q.postproc)↓'∘',combine
     q.(expr←postproc,from,'⎕',op,to,options,repeat)
     :If '?'∊args
         ⍞←q.expr,n
     :EndIf

     :If 0=≢from
         from←''
     :EndIf
     :If 2=⎕NC'to'
     :AndIf 0=≢to
         to←''
     :EndIf

     :Trap 11
         :If r
             output←from ⎕R to⍠options⍣repeat⊢input
         :ElseIf s
             output←from ⎕S to⍠options⍣repeat⊢input
             output←(⍎combine)⍣hasrepeat⊢output ⍝ merge search results if we need to continue
         :EndIf
         output←(⍎postproc)output
     :Case 11 ⍝ faulty regex
         ⍞←'*** ',⎕DMX.(Message,(''≡Message)/⊃DM),' ***',n
         ⍞←'Attempted APL expression: ',q.expr,n
     :EndTrap
 :Else
     ⍞←'*** Left operand must be ''R'' or ''S'' ***'
 :EndIf

 output←regexes(op Run args)input;from;to;options;lines;trans;Expand;repeat;s;r;last;fns;Quote;q;hasrepeat;postproc;combine

 :Trap 11 22 ⍝ accept filename or actual input
     input←input ⎕NTIE 0
 :EndTrap

 :Trap 11 22 ⍝ accept filename or actual regex
     regexes←⊃⎕NGET regexes 1
 :EndTrap

 repeat←⍎{×≢⍵:⍵ ⋄ '1'}args∩'≡',⎕D ⍝ extract repetition, if any, else 1
 hasrepeat←∨/args∊⍨'≡',⎕D
 ⍝ select options:
 options←('ResultText' 'Simple')('Greedy' 0)('IC' 1)('Mode' 'D')('Mode' 'M')('DotAll' 1)('UCP' 1)('OM' 1)/⍨1,'gidmauo'∊819⌶args~' -'
 s r←(∨/∊∘op)¨'Ss' 'Rr'
 options↓⍨←s ⍝ ResultText is not for ⎕S

 fns←+/∧\'⍵'∊¨regexes
 postproc←∊'{⍵}','∘{'∘,¨,∘'}'¨fns↑regexes ⍝ post-processing functions
 regexes↓⍨←fns

 last←⊃⌽regexes
 :If 2|≢regexes ⍝ odd number of strings → many-to-one
 :OrIf '⍵'∊last ⍝ transformation function → many-to-function
     from←¯1↓regexes ⍝ remove transformation pattern/function
     :If ~'⍵'∊last ⍝ no ⍵ → pattern
         Expand←'⍵B' '⍵b' '⍵P' '⍵p' '⍵M' '⍵O' '⍵L' '⍵N'⎕R'⍵.Block' '⍵.BlockNum' '⍵.Pattern' '⍵.PatternNum' '⍵.Match' '⍵.Offsets' '⍵.Lengths' '⍵.Names'
     :OrIf ~≡⎕FX,⊂'to←{',(r/',⍕{'),(Expand last),(r/'}⍵'),'}' ⍝ if definition fails, revert to pattern (allows ⍵ in pattern)
         to←⊃⌽regexes
     :EndIf
 :Else
     lines←2÷⍨≢regexes
     from←lines↑regexes
     to←lines↓regexes
 :EndIf

 combine←'{⊃,/⍵↑¨⍨⌈/≢¨⍵}'
 q←⎕NS ⍬
 :If s r∨.∧'?'∊args
     Quote←{q,q,⍨⍵/⍨1+⍵=q←''''}
     q.from←⍕Quote¨from
     :If 3=⎕NC'to'
         q.to←1⌽'  ',3↓∊⎕NR'to'
     :Else
         q.to←⍕Quote¨to
     :EndIf
     :If 3=⎕NC'repeat'
         q.repeat←'⍣',∊⎕NR'repeat'
     :ElseIf 1≢repeat
         q.repeat←'⍣',⍕repeat
     :Else
         q.repeat←''
     :EndIf
     :If ×≢options
         q.options←'⍠',∊{' (',(Quote⊃⍵),' ',({0 1∊⍨⊂⍵:⍕⍵ ⋄ '''',⍵,''''}⊃⌽⍵),')'}¨options
     :Else
         q.options←''
     :EndIf
     q.op←s r/'SR'
     q.postproc←4↓postproc
     q.postproc,←(hasrepeat∧s)/(''≡q.postproc)↓'∘',combine
     output←q.(postproc,from,'⎕',op,to,options,repeat)
 :Else
     :Trap 6 11
         :If r
             output←from ⎕R to⍠options⍣repeat⊢input
         :ElseIf s
             output←from ⎕S to⍠options⍣repeat⊢input
             output←(⍎combine)⍣hasrepeat⊢output ⍝ merge search results if we need to continue
         :EndIf
         output←(⍎postproc)output
     :Case 6 ⍝ no output
         output←'*** Left operand must be ''R'' or ''S'' ***'
     :Case 11 ⍝ faulty regex
         output←'*** ',⎕DMX.Message,' ─ try adding ? to the Arguments to see the attempted APL expression ***'
     :EndTrap
 :EndIf


Substantial change (improvement) to _JQ.SetOption

:Namespace _JQ
    (⎕IO ⎕ML)←1

    :section Common Code
    quote←{'"'∊⍵:⍵ ⋄ '"',⍵,'"'}

    ∇ r←opt(sel Update jqfn)val;v
    ⍝ update an option for a widget
      v←{0 2∊⍨10|⎕DR ⍵:{'⍎'=⊃⍵:1↓⍵ ⋄ quote ⍵}⍵ ⋄ ,⍕⍵}val
      r←#.JQ.Execute'$("',sel,'").',jqfn,'("option","',opt,'",',v,');'
    ∇
    :endsection

    :section Base Classes
    :class _jqObject : #.HtmlElement
        :field public Selector←''      ⍝ Selector to apply the JQuery function to
        :field public JavaScript←''    ⍝ additional JavaScript to run AFTER the jQuery function, can be function chain, separate code or both
        :field public PreJavaScript←'' ⍝ additional JavaScript to run BEFORE the jQuery function
        :field public Var←''           ⍝ JavaScript variable name for created object
        :field public JQueryFn←''      ⍝ JQuery function to apply
        :field public JQPars←''        ⍝ JQuery function parameters
        :field public shared readonly _true←#.JSON.true     ⍝ same definition as in #.JSON
        :field public shared readonly _false←#.JSON.false   ⍝ same definition as in #.JSON
        :field public ScriptOptions←1 1 ⍝ determines how script will be rendered [1] wrap with <script>...</script>, [2] wrap with $(function(){...})

        ∇ Make0
          :Access public
          :Implements constructor
        ∇

        ∇ Make1 pars
          :Access public
          :Implements constructor
          pars←eis pars
          JQueryFn Selector JQPars JavaScript Var PreJavaScript←6↑pars,(⍴pars)↓'' '' '' '' '' ''
        ∇

        ∇ r←Render
          :Access public
          Use
          r←ScriptOptions #.JQ.JQueryfn JQueryFn Selector JQPars JavaScript Var PreJavaScript
        ∇

        ∇ r←isSelector str ⍝ checks if str is probably a jQuery selector
          :Access public shared
          r←∨/str∊'''"*[]:>+~()#.'
        ∇

        :section APLJax

        ∇ r←selector Replace content
          :Access public
          :If isClass content ⋄ content←(⎕NEW content).Render ⋄ :EndIf
          r←⊂('replace'selector)('data'content)
        ∇
        ∇ r←selector Append content
          :Access public
          :If isClass content ⋄ content←(⎕NEW content).Render ⋄ :EndIf
          r←⊂('append'selector)('data'content)
        ∇
        ∇ r←selector Prepend content
          :Access public
          :If isClass content ⋄ content←(⎕NEW content).Render ⋄ :EndIf
          r←⊂('prepend'selector)('data'content)
        ∇
        ∇ r←Execute content
          :Access public
          r←⊂('execute'content)
        ∇

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

    :endclass

    :class _jqWidget : _jqObject

        :field public Options←''    ⍝ options for the object to be created
        :field public ContainerType←'div' ⍝ default container type
        :field public Container
        :field public eventHandlers←''
        :field public InternalEvents←'' ⍝ list of events the widget "knows" about
        :field public Force←¯1          ⍝ force event to be treated as internal event, ¯1=check InternalEvents, 1=yes, 0=no
        :field _build←0

        handlerSyntax←'event,ui' 'event'  'ui' '$(event.target)'  ⍝ see _JQ.RenderHandlerCore for details

        ∇ r←{a}rand w;rnd
          :Access public
          rnd←{⍺←⊢ ⋄ t←16807⌶2 ⋄ r←⍺?⍵ ⋄ t←16807⌶t ⋄ r}
          :If 0=⎕NC'a' ⋄ r←rnd w
          :Else ⋄ r←a rnd w ⋄ :EndIf
        ∇

        ∇ Make
          :Access public
          Options←⎕NS''
          Container←⎕NEW #.HtmlElement
          :Implements constructor
        ∇

        ∇ r←Render;build;html;handlers;js;opts
          :Access public

          r←html←js←''
          Use

          _build∨←0∊⍴Selector ⍝ if the user explicitly specifies a selector, assume he's built the content himself

          :If _build
              Container.(id name type style class title)←Container.(id name type style class title){UNDEF≡⍵:⍺ ⋄ UNDEF≢⍺:⍺ ⋄ ⍵}¨⎕THIS.(id name type style class title)
              :If Container.id≡UNDEF
                  :If Container.name≢UNDEF
                      Container.id←Container.name
                  :Else
                      Container.id←SetId
                  :EndIf
              :EndIf
              Selector←'#',Container.id
          :EndIf

          handlers←''
          :If ~0∊⍴eventHandlers
              handlers←';',⍨∊¯1↓¨Options∘RenderHandler¨eventHandlers
          :EndIf

          opts←{0::⍬ ⋄ 1 0≥_PageRef._Request.isAPLJax}⍬
          js←opts #.JQ.JQueryfn JQueryFn Selector Options(JavaScript,handlers)Var PreJavaScript

          :If _build≥0∊⍴Container.Content
              :Select ⊃Selector
              :Case '#' ⍝ id?
                  Container.id←1↓Selector
              :Case '.' ⍝ class?
                  Container.class←1↓Selector
              :EndSelect
              :If ContainerType{⍵≡(⍴⍵)↑⍺}'input'
              :AndIf UNDEF≡Container.name
                  Container.name←('.#'∊⍨⊃Selector)↓Selector
              :EndIf
              Container.Tag←ContainerType
              html←Container.Render
          :EndIf
          r←html,js
        ∇

        ∇ {handler}←On args;event;callback;clientData;javaScript;n;i
          :Access public
          ⍝ args - event callback clientData javascript
          args←eis args
          handler←⎕NS''
          handler.(Event Callback ClientData JavaScript Hourglass)←5↑args,(⍴args)↓'' 1 '' '' 1
          :If 0∊n←⍴eventHandlers
              eventHandlers,←handler
          :ElseIf n<i←eventHandlers.Event⍳⊂handler.Event
              eventHandlers,←handler
          :Else
              eventHandlers[i]←handler
          :EndIf
        ∇

        ∇ {r}←opts RenderHandler handler
          :Access public overridable
          r←opts RenderHandlerCore(handler handlerSyntax Force)
        ∇

        ∇ {r}←opts RenderHandlerCore args;handler;widgettype;force;syntax;evt;model;page;event;callback;clientdata;javascript;useajax;data;cd;name;selector;type;what;this;hourglass;dtype;success;ajax;hg;removehg;status
          :Access public
         ⍝ unified event handling core for jQueryUI and Syncfusion widget
         ⍝ Syncfusion and jQueryUI use different models, if other jQuery-based libraries are used, this may need to be changed
         ⍝ args is [1] handler [2] widgettype [3] force
         ⍝ opts - Options namespace for the widget
         ⍝ handler - handler definition
         ⍝ widgettype - three element vector of vectors [1] syntax for calling function, [2] event object name, [3] model object name, [4] how to retrieve id
         ⍝ force - Boolean to force treatment of event as an InternalEvent

          args←eis args
          (handler widgettype force)←3↑args,(⍴args)↓(⎕NS'')('event,ui' 'event' 'ui' '$(event.target)')0
          (syntax evt model this)←widgettype

          r←page←''
          :If isInstance _PageRef
              page←_PageRef._PageName
          :EndIf

          page←quote page

          (event callback clientdata javascript hourglass)←handler.(Event Callback ClientData JavaScript Hourglass)
          useajax←(,0)≢,callback ⍝ callback=0 → don't make callback to server; =1 → use APLJax, =charvec → call ⍎charvec

          :If force=¯1
              force←(⊂event)∊InternalEvents
          :EndIf

          data←''
          data,←', _event: ',evt,'.type'
          data,←', _what:  ',this,'.attr("id")'
          data,←', _value: ',this,'.attr("value")'
          data,←', _selector: "',Selector,'"'
          data,←(isString callback)/', _callback: ',quote callback
          data←2↓data

          :Select |≡clientdata
          :CaseList 0 1  ⍝ simple vector
              clientdata←,⊂2⍴⊂clientdata ⍝ name/id are set to the same
          :Case 2
              clientdata←,⊂clientdata
          :EndSelect

          :For cd :In clientdata
              cd←eis cd
              (name selector type what)←4↑cd,(⍴cd)↓4⍴⊂''
              :If name≡'serialize'
                  (name selector type what)←4↑(⊂''),cd
              :EndIf
              :If (~0∊⍴name)∨selector≡'serialize'

                  :Select selector

                  :CaseList 'attr' 'css' 'html' 'is' 'serialize' 'val' 'eval' ⍝ no selector specified, use event.target
                      (type what)←selector type
                      selector←''

                  :Case 'string'
                      (type what)←selector(quote type)
                      selector←''

                  :Case ''
                      selector←'this' ⍝ null selector means reference the current object

                  :CaseList syntax evt model
                      :If type≡''
                          name←,'_',selector
                          type←'JSON.stringify(',selector,')'
                      :Else
                          type←selector,'.',⍕type
                      :EndIf
                      selector←''
                  :Case 'model'
                      :If type≡''
                          name←,'_',selector
                          type←'JSON.stringify(',model,')'
                      :Else
                          type←model,'.',⍕type
                      :EndIf
                      selector←''
                  :Else
                      :If ∨/mask←'model.' 'event.' 'ui.' 'argument.'{⍺≡(⍴⍺)↑⍵}¨⊂selector
                          (type what)←2↑{⎕ML←3 ⋄ ⍵⊂⍨⍵≠'.'}selector
                          selector←''
                          :If ⊣/mask
                              type←model
                          :EndIf
                      :Else
                          selector←quote selector
                      :EndIf
                  :EndSelect

                  :Select type
                  :Case 'eval'
                      type←what
                  :CaseList 'event' 'ui'
                      type←type,'.',what
                  :Case ''
                      type←'val()'
                  :Case 'string'
                      type←what
                  :Else
                      :If type≡'serialize'
                          name,←'_serialized'
                      :EndIf
                      type←type,what ine'(',(quote what),')'
                  :EndSelect
                  data,←',',name,': ',(selector ine'$(',selector,').'),type
              :EndIf
          :EndFor

          (hg removehg)←hourglass∘{⍺:'document.body.style.cursor="',⍵,'";' ⋄ ''}¨'wait' 'default'

          dtype←'"json"'
          success←'success: function(obj){APLJaxReturn(obj);document.body.style.cursor="default";',removehg,'}'
          status←'statusCode:{ 408: function(){alert("Session timed out");',removehg,'}}'
          ajax←(javascript ine javascript,';'),useajax/hg,'$.ajax({url: ',page,', cache: false, type: "POST", dataType: ',dtype,', data: {',data,'}, ',success,', ',status,'});'
          :If force
              event(opts{⍺⍺⍎⍺,'←⍵'})'function(',syntax,'){',ajax,'}'
          :Else
              r←'.on(',(quote event),', function(',syntax,'){',ajax,'});'
          :EndIf
        ∇

        ∇ {name}Set value
          :Access public
          →(0∊⍴value)⍴0
          :If 326≠⎕DR Options ⋄ Options←⎕NS'' ⋄ :EndIf
          :If 0=⎕NC'name'
              :Trap 11
                  (name value)←ParseValue value
              :Else
                  ⎕SIGNAL/⎕DMX.(EM EN)
              :EndTrap
          :EndIf
          name(Options SetOption)value
        ∇

        ∇ name(ref SetOption)value;set;parent;i;ind;newref;chunk;pos;n;now;new;chunkroot
          →(0∊⍴value)⍴0
          :If 1<|≡name ⍝ multiple names?
              value←(⊂⍣((⎕DR value)∊80 82))value
              name(ref SetOption)¨value
          :Else

              set←{⍺⍺⍎'(',⍺,')←⍵'}

              :If 0∊⍴parent←(i←-'.'⍳⍨⌽name)↓name
                  name(ref set)value ⍝ single name: assign directly (may be more than 1 name)
              :Else
                  ind←name⍳'.'
                  chunk←¯1↓ind↑name
                  (chunkroot pos)←'[]'#.Utils.penclose chunk
                  pos←2⊃⎕VFI pos
                  :Trap 3 6
                      newref←ref⍎chunk
                  :Case 3 ⍝ index error
                      n←⍴now←ref⍎chunkroot
                      new←⎕NS¨(pos-n)⍴⊂⍬
                      chunkroot(ref set)now,new
                      newref←ref⍎chunk
                  :Case 6 ⍝ value error
                      chunkroot ref.⎕NS''
                      :If ~0∊pos
                          new←⎕NS¨pos⍴⊂⍬
                          chunkroot(ref set)new
                      :EndIf
                      newref←ref⍎chunk
                  :EndTrap
                  (ind↓name)(newref SetOption)value
              :EndIf
          :EndIf
        ∇

        ∇ r←ParseValue value
          :Access shared public
          :If 2=⍴⍴value
          :AndIf 2=¯1↑⍴value
          :ElseIf 2<|≡value
          :AndIf ∧/(,2)∘≡∘⍴¨value
              value←↑value
          :ElseIf 0=2|⍴value
              value←((0.5×⍴value),2)⍴value
          :Else
              'Invalid option format'⎕SIGNAL 11
          :EndIf
          r←↓[1]value
        ∇

        ∇ r←GetOption names
          :Access public
          r←{6::'' ⋄ Options.⍎⍵}¨{⎕ML←3 ⋄ ⍵⊂⍨⍵≠' '}names
          :If 1=⍴r ⋄ r←⊃r ⋄ :EndIf ⍝ if single setting requested
        ∇

        ∇ r←name Update value;v
          :Access public
          r←name(Selector #._JQ.Update JQueryFn)value
          name Set value
        ∇
    :endclass

    :class _jqUIWidget : _jqWidget

        :field public Force←0

        handlerSyntax←⊂'event,ui' 'event'  'ui' '$(event.target)'

        ∇ make
          :Access public
          :If 0=⎕NC⊂'Uses' ⋄ Uses←'' ⋄ :EndIf
          :If 0∊⍴Uses ⋄ Uses←'JQueryUI' ⋄ :EndIf
          :Implements constructor
        ∇

        ∇ make1 pars
          :Access public
          :If 0=⎕NC⊂'Uses' ⋄ Uses←'' ⋄ :EndIf
          :If 0∊⍴Uses ⋄ Uses←'JQueryUI' ⋄ :EndIf
          :Implements constructor :base pars
        ∇

        ∇ {r}←opts RenderHandler handler
          :Access public
          r←opts ⎕BASE.RenderHandler(handler handlerSyntax Force)
        ∇

    :endclass

    :class _jqUITabbedWidget : _jqUIWidget
        :field public Titles←''
        :field public Id←''
        :field public Tabs←''
        :field public Widget

        ∇ Make0
          :Access public
          :Implements constructor
          Widget←⎕NS''
          Widget._function←{}
        ∇

        ∇ Make1 pars;t
          :Access public
          :Implements constructor
          :Select |≡pars
          :Case 2
              :If 2=⍴pars ⍝ could be title/tab or title/title
                  :If (10|⎕DR t←2⊃pars)∊0 2 ⍝ if the second element is character
                  :AndIf 1=⍴⍴t ⍝ and it's a vector
                  :AndIf 20>⍴t ⍝ and it's relatively short
                      pars←,⊂pars
                  :Else
                      pars←#.HtmlElement.eis¨pars
                  :EndIf
              :EndIf
          :Case 0 1
              pars←#.HtmlElement.eis pars
          :EndSelect
          Titles Tabs Options JavaScript Var←5↑pars,(⍴pars)↓'' '' '' '' ''
          Widget←⎕NS''
          Widget._function←{}
        ∇

        ∇ r←Render;opts
          :Access public
          'Accordion title and content lengths do not match'⎕SIGNAL((⍴Titles)≠⍴Tabs)/5
          opts←Options
          :If 2≤|≡Options ⋄ opts←#.JSON.toJQueryParameters Options ⋄ :EndIf
          r←Widget._function Id Titles(RenderTab¨Tabs)opts JavaScript Var
        ∇

        ∇ r←RenderTab tab;e;t
          :Access public
          :If 326=⎕DR tab
              r←''
              :For e :In tab
                  t←⊃e
                  :If isInstance t
                  :AndIf isHtmlElement t
                      r,←e.Render
                  :ElseIf isClass t
                  :AndIf isHtmlElement t
                      r,←(⎕NEW∘{2<⍴,⍵:(⊃⍵)(1↓⍵) ⋄ ⍵}eis e).Render
                  :Else
                      r,←e
                  :EndIf
              :EndFor
          :Else
              r←,⍕tab
          :EndIf
        ∇

    :endclass
    :endsection

⍝ --- Events ---
    :Section Events

    :Class Handler
        :Field public Selectors←''  ⍝ CSS/JQuery selectors to bind handler to
        :Field public Delegates←''  ⍝
        :Field public Events←''     ⍝ events to bind
        :Field public ClientData←'' ⍝ any additional client data to send to server
        :Field public Callback←1    ⍝ execute AJAX callback to server?  or the name of the server-side callback function
        :Field public JavaScript←'' ⍝ JavaScript to execute prior to server callback
        :Field public Page←''       ⍝ server URL to run for an AJAX callback
        :Field public jQueryWrap←1  ⍝ wrap handler in $(function(){...});
        :Field public ScriptWrap←1  ⍝ wrap handler in <script>...</script>
        :Field public Hourglass←1   ⍝ display houglass cursor during AJAX call?
        :field public Uses←'JQuery'

        ∇ Make0
          :Access public
          :Implements constructor
          Use
        ∇

        ∇ Make1 params
          :Access public
          :Implements constructor
          params←#.HtmlElement.eis params
          (Selectors Events Callback ClientData Delegates JavaScript Page)←7↑params,(⍴params)↓7⍴⊂''
          :If #.HtmlElement.isClass Page ⋄ Page←Page.Page ⋄ :EndIf ⍝ if request object passed
          Use
        ∇

        ∇ Use;c
          :Access public
          :Trap 0
              c←#.HtmlElement.context'_PageRef'
              :If ~0∊⍴c
                  (c⍎'_PageRef').Use Uses
              :EndIf
          :EndTrap
        ∇

        ∇ r←Render;sel;cd;pg;req
          :Access public
          sel←Selectors
          :If 0∊⍴sel
              sel←'html'
          :EndIf
          pg←Page
          :If 0∊⍴pg
          :AndIf ''≢req←#.HtmlElement.context'_Request'
              pg←req._Request.Page
              jQueryWrap←~req._Request.isAPLJax ⍝!!!BPB!!!
          :EndIf
          :If ''≢Delegates
              sel←Selectors Delegates
          :EndIf
          cd←ClientData
          :If (~0∊⍴cd)∧2≥|≡cd ⋄ cd←,⊂cd ⋄ :EndIf
          :If ' '=1↑0⍴Callback
          :AndIf ~0∊⍴Callback
              cd,←⊂'_callback' 'string'Callback
          :EndIf
          r←pg #.JQ.On sel Events cd''JavaScript(0≢⊃Callback)jQueryWrap ScriptWrap Hourglass
        ∇

    :endclass
    :endsection

:EndNamespace

:Namespace _JQ
    (⎕IO ⎕ML)←1

    :section Common Code
    quote←{'"'∊⍵:⍵ ⋄ '"',⍵,'"'}

    ∇ r←opt(sel Update jqfn)val;v
    ⍝ update an option for a widget
      v←{0 2∊⍨10|⎕DR ⍵:{'⍎'=⊃⍵:1↓⍵ ⋄ quote ⍵}⍵ ⋄ ,⍕⍵}val
      r←#.JQ.Execute'$("',sel,'").',jqfn,'("option","',opt,'",',v,');'
    ∇
    :endsection

    :section Base Classes
    :class _jqObject : #.HtmlElement
        :field public Selector←''      ⍝ Selector to apply the JQuery function to
        :field public JavaScript←''    ⍝ additional JavaScript to run AFTER the jQuery function, can be function chain, separate code or both
        :field public PreJavaScript←'' ⍝ additional JavaScript to run BEFORE the jQuery function
        :field public Var←''           ⍝ JavaScript variable name for created object
        :field public JQueryFn←''      ⍝ JQuery function to apply
        :field public JQPars←''        ⍝ JQuery function parameters
        :field public shared readonly _true←#.JSON.true     ⍝ same definition as in #.JSON
        :field public shared readonly _false←#.JSON.false   ⍝ same definition as in #.JSON
        :field public ScriptOptions←1 1 ⍝ determines how script will be rendered [1] wrap with <script>...</script>, [2] wrap with $(function(){...})

        ∇ Make0
          :Access public
          :Implements constructor
        ∇

        ∇ Make1 pars
          :Access public
          :Implements constructor
          pars←eis pars
          JQueryFn Selector JQPars JavaScript Var PreJavaScript←6↑pars,(⍴pars)↓'' '' '' '' '' ''
        ∇

        ∇ r←Render
          :Access public
          Use
          r←ScriptOptions #.JQ.JQueryfn JQueryFn Selector JQPars JavaScript Var PreJavaScript
        ∇

        ∇ r←isSelector str ⍝ checks if str is probably a jQuery selector
          :Access public shared
          r←∨/str∊'''"*[]:>+~()#.'
        ∇

        :section APLJax

        ∇ r←selector Replace content
          :Access public
          :If isClass content ⋄ content←(⎕NEW content).Render ⋄ :EndIf
          r←⊂('replace'selector)('data'content)
        ∇
        ∇ r←selector Append content
          :Access public
          :If isClass content ⋄ content←(⎕NEW content).Render ⋄ :EndIf
          r←⊂('append'selector)('data'content)
        ∇
        ∇ r←selector Prepend content
          :Access public
          :If isClass content ⋄ content←(⎕NEW content).Render ⋄ :EndIf
          r←⊂('prepend'selector)('data'content)
        ∇
        ∇ r←Execute content
          :Access public
          r←⊂('execute'content)
        ∇

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

    :endclass

    :class _jqWidget : _jqObject

        :field public Options←''    ⍝ options for the object to be created
        :field public ContainerType←'div' ⍝ default container type
        :field public Container
        :field public eventHandlers←''
        :field public InternalEvents←'' ⍝ list of events the widget "knows" about
        :field public Force←¯1          ⍝ force event to be treated as internal event, ¯1=check InternalEvents, 1=yes, 0=no
        :field _build←0

        handlerSyntax←'event,ui' 'event'  'ui' '$(event.target)'  ⍝ see _JQ.RenderHandlerCore for details

        ∇ r←{a}rand w;rnd
          :Access public
          rnd←{⍺←⊢ ⋄ t←16807⌶2 ⋄ r←⍺?⍵ ⋄ t←16807⌶t ⋄ r}
          :If 0=⎕NC'a' ⋄ r←rnd w
          :Else ⋄ r←a rnd w ⋄ :EndIf
        ∇

        ∇ Make
          :Access public
          Options←⎕NS''
          Container←⎕NEW #.HtmlElement
          :Implements constructor
        ∇

        ∇ r←Render;build;html;handlers;js;opts
          :Access public

          r←html←js←''
          Use

          _build∨←0∊⍴Selector ⍝ if the user explicitly specifies a selector, assume he's built the content himself

          :If _build
              Container.(id name type style class title)←Container.(id name type style class title){UNDEF≡⍵:⍺ ⋄ UNDEF≢⍺:⍺ ⋄ ⍵}¨⎕THIS.(id name type style class title)
              :If Container.id≡UNDEF
                  :If Container.name≢UNDEF
                      Container.id←Container.name
                  :Else
                      Container.id←SetId
                  :EndIf
              :EndIf
              Selector←'#',Container.id
          :EndIf

          handlers←''
          :If ~0∊⍴eventHandlers
              handlers←';',⍨∊¯1↓¨Options∘RenderHandler¨eventHandlers
          :EndIf

          opts←{0::⍬ ⋄ 1 0≥_PageRef._Request.isAPLJax}⍬
          js←opts #.JQ.JQueryfn JQueryFn Selector Options(JavaScript,handlers)Var PreJavaScript

          :If _build≥0∊⍴Container.Content
              :Select ⊃Selector
              :Case '#' ⍝ id?
                  Container.id←1↓Selector
              :Case '.' ⍝ class?
                  Container.class←1↓Selector
              :EndSelect
              :If ContainerType{⍵≡(⍴⍵)↑⍺}'input'
              :AndIf UNDEF≡Container.name
                  Container.name←('.#'∊⍨⊃Selector)↓Selector
              :EndIf
              Container.Tag←ContainerType
              html←Container.Render
          :EndIf
          r←html,js
        ∇

        ∇ {handler}←On args;event;callback;clientData;javaScript;n;i
          :Access public
          ⍝ args - event callback clientData javascript
          args←eis args
          handler←⎕NS''
          handler.(Event Callback ClientData JavaScript Hourglass)←5↑args,(⍴args)↓'' 1 '' '' 1
          :If 0∊n←⍴eventHandlers
              eventHandlers,←handler
          :ElseIf n<i←eventHandlers.Event⍳⊂handler.Event
              eventHandlers,←handler
          :Else
              eventHandlers[i]←handler
          :EndIf
        ∇

        ∇ {r}←opts RenderHandler handler
          :Access public overridable
          r←opts RenderHandlerCore(handler handlerSyntax Force)
        ∇

        ∇ {r}←opts RenderHandlerCore args;handler;widgettype;force;syntax;evt;model;page;event;callback;clientdata;javascript;useajax;data;cd;name;selector;type;what;this;hourglass;dtype;success;ajax;hg;removehg;status
          :Access public
         ⍝ unified event handling core for jQueryUI and Syncfusion widget
         ⍝ Syncfusion and jQueryUI use different models, if other jQuery-based libraries are used, this may need to be changed
         ⍝ args is [1] handler [2] widgettype [3] force
         ⍝ opts - Options namespace for the widget
         ⍝ handler - handler definition
         ⍝ widgettype - three element vector of vectors [1] syntax for calling function, [2] event object name, [3] model object name, [4] how to retrieve id
         ⍝ force - Boolean to force treatment of event as an InternalEvent

          args←eis args
          (handler widgettype force)←3↑args,(⍴args)↓(⎕NS'')('event,ui' 'event' 'ui' '$(event.target)')0
          (syntax evt model this)←widgettype

          r←page←''
          :If isInstance _PageRef
              page←_PageRef._PageName
          :EndIf

          page←quote page

          (event callback clientdata javascript hourglass)←handler.(Event Callback ClientData JavaScript Hourglass)
          useajax←(,0)≢,callback ⍝ callback=0 → don't make callback to server; =1 → use APLJax, =charvec → call ⍎charvec

          :If force=¯1
              force←(⊂event)∊InternalEvents
          :EndIf

          data←''
          data,←', _event: ',evt,'.type'
          data,←', _what:  ',this,'.attr("id")'
          data,←', _value: ',this,'.attr("value")'
          data,←', _selector: "',Selector,'"'
          data,←(isString callback)/', _callback: ',quote callback
          data←2↓data

          :Select |≡clientdata
          :CaseList 0 1  ⍝ simple vector
              clientdata←,⊂2⍴⊂clientdata ⍝ name/id are set to the same
          :Case 2
              clientdata←,⊂clientdata
          :EndSelect

          :For cd :In clientdata
              cd←eis cd
              (name selector type what)←4↑cd,(⍴cd)↓4⍴⊂''
              :If name≡'serialize'
                  (name selector type what)←4↑(⊂''),cd
              :EndIf
              :If (~0∊⍴name)∨selector≡'serialize'

                  :Select selector

                  :CaseList 'attr' 'css' 'html' 'is' 'serialize' 'val' 'eval' ⍝ no selector specified, use event.target
                      (type what)←selector type
                      selector←''

                  :Case 'string'
                      (type what)←selector(quote type)
                      selector←''

                  :Case ''
                      selector←'this' ⍝ null selector means reference the current object

                  :CaseList syntax evt model
                      :If type≡''
                          name←,'_',selector
                          type←'JSON.stringify(',selector,')'
                      :Else
                          type←selector,'.',⍕type
                      :EndIf
                      selector←''
                  :Case 'model'
                      :If type≡''
                          name←,'_',selector
                          type←'JSON.stringify(',model,')'
                      :Else
                          type←model,'.',⍕type
                      :EndIf
                      selector←''
                  :Else
                      :If ∨/mask←'model.' 'event.' 'ui.' 'argument.'{⍺≡(⍴⍺)↑⍵}¨⊂selector
                          (type what)←2↑{⎕ML←3 ⋄ ⍵⊂⍨⍵≠'.'}selector
                          selector←''
                          :If ⊣/mask
                              type←model
                          :EndIf
                      :Else
                          selector←quote selector
                      :EndIf
                  :EndSelect

                  :Select type
                  :Case 'eval'
                      type←what
                  :CaseList 'event' 'ui'
                      type←type,'.',what
                  :Case ''
                      type←'val()'
                  :Case 'string'
                      type←what
                  :Else
                      :If type≡'serialize'
                          name,←'_serialized'
                      :EndIf
                      type←type,what ine'(',(quote what),')'
                  :EndSelect
                  data,←',',name,': ',(selector ine'$(',selector,').'),type
              :EndIf
          :EndFor

          (hg removehg)←hourglass∘{⍺:'document.body.style.cursor="',⍵,'";' ⋄ ''}¨'wait' 'default'

          dtype←'"json"'
          success←'success: function(obj){APLJaxReturn(obj);document.body.style.cursor="default";',removehg,'}'
          status←'statusCode:{ 408: function(){alert("Session timed out");',removehg,'}}'
          ajax←(javascript ine javascript,';'),useajax/hg,'$.ajax({url: ',page,', cache: false, type: "POST", dataType: ',dtype,', data: {',data,'}, ',success,', ',status,'});'
          :If force
              event(opts{⍺⍺⍎⍺,'←⍵'})'function(',syntax,'){',ajax,'}'
          :Else
              r←'.on(',(quote event),', function(',syntax,'){',ajax,'});'
          :EndIf
        ∇

        ∇ {name}Set value
          :Access public
          →(0∊⍴value)⍴0
          :If 326≠⎕DR Options ⋄ Options←⎕NS'' ⋄ :EndIf
          :If 0=⎕NC'name'
              :Trap 11
                  (name value)←ParseValue value
              :Else
                  ⎕SIGNAL/⎕DMX.(EM EN)
              :EndTrap
          :EndIf
          name(Options SetOption)value
        ∇

        ∇ name(ref SetOption)value;parent;i;ns;split;set;index;lb;new;now;an;n
          →(0∊⍴value)⍴0
          :If 1<|≡name ⍝ multiple names?
              value←(⊂⍣((⎕DR value)∊80 82))value
              name(ref SetOption)¨value
          :Else

              set←{⍺⍺⍎'(',⍺,')←⍵'}

              :If 0∊⍴parent←(i←-'.'⍳⍨⌽name)↓name
                  name(ref set)value ⍝ single name: assign directly (may be more than 1 name)
              :Else
                  :If ~'['∊parent
                      parent ref.⎕NS''
                  :Else
                      split←parent⍳']'              ⍝ if present, ] will be ≤⍴parent
                      :If (⍴parent)≤split+1         ⍝ if ] is in the last 2 positions
                          an←parent↑⍨¯1+lb←parent⍳'['      ⍝ find the array name
                          index←2⊃⎕VFI lb↓(split-1)↑parent ⍝ the position wanted
                          new←⎕NS¨index⍴⊂⍬
                          :If 0=ref.⎕NC an             ⍝ if it does not exist
                              an(ref set)new           ⍝ create it as a vector
                          :EndIf
                     ⍝ If the index is beyond the current shape, extend it
                          :If index>n←⍴now←ref⍎an
                              an(ref set)now,n↓new
                          :EndIf
                      :EndIf
                      (parent↓⍨1+split)(ref⍎split↑parent).⎕NS''
                  :EndIf
                  (1↓i↑name)((ref⍎parent)set)value
              :EndIf
          :EndIf
        ∇

        ∇ r←ParseValue value
          :Access shared public
          :If 2=⍴⍴value
          :AndIf 2=¯1↑⍴value
          :ElseIf 2<|≡value
          :AndIf ∧/(,2)∘≡∘⍴¨value
              value←↑value
          :ElseIf 0=2|⍴value
              value←((0.5×⍴value),2)⍴value
          :Else
              'Invalid option format'⎕SIGNAL 11
          :EndIf
          r←↓[1]value
        ∇

        ∇ r←GetOption names
          :Access public
          r←{6::'' ⋄ Options.⍎⍵}¨{⎕ML←3 ⋄ ⍵⊂⍨⍵≠' '}names
          :If 1=⍴r ⋄ r←⊃r ⋄ :EndIf ⍝ if single setting requested
        ∇

        ∇ r←name Update value;v
          :Access public
          r←name(Selector #._JQ.Update JQueryFn)value
          name Set value
        ∇
    :endclass

    :class _jqUIWidget : _jqWidget

        :field public Force←0

        handlerSyntax←⊂'event,ui' 'event'  'ui' '$(event.target)'

        ∇ make
          :Access public
          :If 0=⎕NC⊂'Uses' ⋄ Uses←'' ⋄ :EndIf
          :If 0∊⍴Uses ⋄ Uses←'JQueryUI' ⋄ :EndIf
          :Implements constructor
        ∇

        ∇ make1 pars
          :Access public
          :If 0=⎕NC⊂'Uses' ⋄ Uses←'' ⋄ :EndIf
          :If 0∊⍴Uses ⋄ Uses←'JQueryUI' ⋄ :EndIf
          :Implements constructor :base pars
        ∇

        ∇ {r}←opts RenderHandler handler
          :Access public
          r←opts ⎕BASE.RenderHandler(handler handlerSyntax Force)
        ∇

    :endclass

    :class _jqUITabbedWidget : _jqUIWidget
        :field public Titles←''
        :field public Id←''
        :field public Tabs←''
        :field public Widget

        ∇ Make0
          :Access public
          :Implements constructor
          Widget←⎕NS''
          Widget._function←{}
        ∇

        ∇ Make1 pars;t
          :Access public
          :Implements constructor
          :Select |≡pars
          :Case 2
              :If 2=⍴pars ⍝ could be title/tab or title/title
                  :If (10|⎕DR t←2⊃pars)∊0 2 ⍝ if the second element is character
                  :AndIf 1=⍴⍴t ⍝ and it's a vector
                  :AndIf 20>⍴t ⍝ and it's relatively short
                      pars←,⊂pars
                  :Else
                      pars←#.HtmlElement.eis¨pars
                  :EndIf
              :EndIf
          :Case 0 1
              pars←#.HtmlElement.eis pars
          :EndSelect
          Titles Tabs Options JavaScript Var←5↑pars,(⍴pars)↓'' '' '' '' ''
          Widget←⎕NS''
          Widget._function←{}
        ∇

        ∇ r←Render;opts
          :Access public
          'Accordion title and content lengths do not match'⎕SIGNAL((⍴Titles)≠⍴Tabs)/5
          opts←Options
          :If 2≤|≡Options ⋄ opts←#.JSON.toJQueryParameters Options ⋄ :EndIf
          r←Widget._function Id Titles(RenderTab¨Tabs)opts JavaScript Var
        ∇

        ∇ r←RenderTab tab;e;t
          :Access public
          :If 326=⎕DR tab
              r←''
              :For e :In tab
                  t←⊃e
                  :If isInstance t
                  :AndIf isHtmlElement t
                      r,←e.Render
                  :ElseIf isClass t
                  :AndIf isHtmlElement t
                      r,←(⎕NEW∘{2<⍴,⍵:(⊃⍵)(1↓⍵) ⋄ ⍵}eis e).Render
                  :Else
                      r,←e
                  :EndIf
              :EndFor
          :Else
              r←,⍕tab
          :EndIf
        ∇

    :endclass
    :endsection

⍝ --- Events ---
    :Section Events

    :Class Handler
        :Field public Selectors←''  ⍝ CSS/JQuery selectors to bind handler to
        :Field public Delegates←''  ⍝
        :Field public Events←''     ⍝ events to bind
        :Field public ClientData←'' ⍝ any additional client data to send to server
        :Field public Callback←1    ⍝ execute AJAX callback to server?  or the name of the server-side callback function
        :Field public JavaScript←'' ⍝ JavaScript to execute prior to server callback
        :Field public Page←''       ⍝ server URL to run for an AJAX callback
        :Field public jQueryWrap←1  ⍝ wrap handler in $(function(){...});
        :Field public ScriptWrap←1  ⍝ wrap handler in <script>...</script>
        :Field public Hourglass←1   ⍝ display houglass cursor during AJAX call?
        :field public Uses←'JQuery'

        ∇ Make0
          :Access public
          :Implements constructor
          Use
        ∇

        ∇ Make1 params
          :Access public
          :Implements constructor
          params←#.HtmlElement.eis params
          (Selectors Events Callback ClientData Delegates JavaScript Page)←7↑params,(⍴params)↓7⍴⊂''
          :If #.HtmlElement.isClass Page ⋄ Page←Page.Page ⋄ :EndIf ⍝ if request object passed
          Use
        ∇

        ∇ Use;c
          :Access public
          :Trap 0
              c←#.HtmlElement.context'_PageRef'
              :If ~0∊⍴c
                  (c⍎'_PageRef').Use Uses
              :EndIf
          :EndTrap
        ∇

        ∇ r←Render;sel;cd;pg;req
          :Access public
          sel←Selectors
          :If 0∊⍴sel
              sel←'html'
          :EndIf
          pg←Page
          :If 0∊⍴pg
          :AndIf ''≢req←#.HtmlElement.context'_Request'
              pg←req._Request.Page
              jQueryWrap←~req._Request.isAPLJax ⍝!!!BPB!!!
          :EndIf
          :If ''≢Delegates
              sel←Selectors Delegates
          :EndIf
          cd←ClientData
          :If (~0∊⍴cd)∧2≥|≡cd ⋄ cd←,⊂cd ⋄ :EndIf
          :If ' '=1↑0⍴Callback
          :AndIf ~0∊⍴Callback
              cd,←⊂'_callback' 'string'Callback
          :EndIf
          r←pg #.JQ.On sel Events cd''JavaScript(0≢⊃Callback)jQueryWrap ScriptWrap Hourglass
        ∇

    :endclass
    :endsection

:EndNamespace


Remove stop in ejButtonSimple

Remove stop in ejButtonSimple

:Class ejButtonSimple : MiPageSample
⍝ Control:: _SF.ejButton
⍝ Description::  two buttons, one en/disables the other and another that displays an alert

    ∇ Compose
      :Access Public

     ⍝ The first button (b1)
      b1←'b1'Add _SF.ejButton'Toggle its state →'
      'type'b1.Set'ej.ButtonType.Button'
      b1.On'click' 'myCallback'

     ⍝ The second button (b2)
      b2←Add _SF.ejButton'Show alert'
      b2.id←'b2'
      'type' 'enabled'b2.Set'ej.ButtonType.Button'_false
      b2.On'click' 'myCallback'
    ∇

    ∇ r←myCallback
      :Access public
     ⍝ This callback simply flips b2's state when b1 is clicked
      :If 'b1'≡_what
          r←'enabled'b2.Update⊃_true _false~b2.GetOption'enabled'
      :Else
          r←Execute'alert("Hi there!")'
      :EndIf

    ∇

:EndClass

:Class ejButtonSimple : MiPageSample
⍝ Control:: _SF.ejButton
⍝ Description::  two buttons, one en/disables the other and another that displays an alert

    ∇ Compose
      :Access Public

     ⍝ The first button (b1)
      b1←'b1'Add _SF.ejButton'Toggle its state →'
      'type'b1.Set'ej.ButtonType.Button'
      b1.On'click' 'myCallback'

     ⍝ The second button (b2)
      b2←Add _SF.ejButton'Show alert'
      b2.id←'b2'
      'type' 'enabled'b2.Set'ej.ButtonType.Button'_false
      b2.On'click' 'myCallback'
    ∇

    ∇ r←myCallback
      :Access public
      ∘∘∘∘
     ⍝ This callback simply flips b2's state when b1 is clicked
      :If 'b1'≡_what
          r←'enabled'b2.Update⊃_true _false~b2.GetOption'enabled'
      :Else
          r←Execute'alert("Hi there!")'
      :EndIf

    ∇

:EndClass


one more fix for ⎕fix

one more fix for ⎕fix

 output←regexes(op Run args)input;from;to;options;lines;Expand;repeat;s;r;last;fns;q;hasrepeat;postproc;combine;Quote;Nest;a;n
 output←⍬⊤⍬ ⍝ default result is empty matrix

 n←⎕UCS 10 ⍝ newline
 a←⎕UCS 39 ⍝ apostrofe
 ⍎'Nest←{⊂⍣(1=≡,⍵)⊢⍵}'
 ⍎'Quote←{a,a,⍨⍵/⍨1+⍵=a}'

 :Trap 11 22 ⍝ accept filename or actual input
     input←input ⎕NTIE 0
 :EndTrap

 :Trap 11 22 ⍝ accept filename or actual regex
     regexes←⊃⎕NGET regexes 1
 :EndTrap
 ⍎'regexes←{,¨⍣(¯2=≡⍵)⊢⍵}Nest regexes'

 repeat←⍎{×≢⍵:⍵ ⋄ '1'}args∩'≡',⎕D ⍝ extract repetition, if any, else 1
 hasrepeat←∨/args∊⍨'≡',⎕D
 ⍝ select options:
 options←('ResultText' 'Simple')('Greedy' 0)('IC' 1)('Mode' 'D')('Mode' 'M')('DotAll' 1)('UCP' 1)('OM' 1)/⍨1,'gidmauo'∊819⌶args~' -'
 s r←(∨/∊∘op)¨'Ss' 'Rr'
 :If s∨r
     options↓⍨←s ⍝ ResultText is not for ⎕S

     fns←+/∧\'⍵'∊¨regexes
     postproc←∊'⊢','∘{'∘,¨,∘'}'¨fns↑regexes ⍝ post-processing functions
     regexes↓⍨←fns

     last←⊃regexes↓⍨¯1+≢regexes
     :If '⍵'∊last ⍝ transformation function → many-to-function
     :OrIf 2|≢regexes ⍝ odd number of strings → many-to-one
         from←¯1↓regexes ⍝ remove transformation pattern/function
         :If ~'⍵'∊last ⍝ no ⍵ → pattern
             Expand←'⍵B' '⍵b' '⍵P' '⍵p' '⍵M' '⍵O' '⍵L' '⍵N'⎕R'⍵.Block' '⍵.BlockNum' '⍵.Pattern' '⍵.PatternNum' '⍵.Match' '⍵.Offsets' '⍵.Lengths' '⍵.Names'
         :OrIf ~≡⎕FX,⊂'to←{',(r/',⍕{'),(Expand last),(r/'}⍵'),'}' ⍝ if definition fails, revert to pattern (allows ⍵ in pattern)
             to←last
         :EndIf
     :Else
         lines←2÷⍨≢regexes
         from←regexes↓⍨-lines
         to←lines↓regexes
     :EndIf

     combine←'{⊃,/⍵↑¨⍨⌈/≢¨⍵}'
     q←⎕NS ⍬
     q.from←⍕Quote¨Nest from
     :If 0=≢q.from
         q.from←a a
     :EndIf
     :If 1∧.=≢¨from
         q.(from←'(,¨',from,')')
     :EndIf
     :If 3=⎕NC'to'
         q.to←1⌽'  ',3↓∊⎕NR'to'
     :Else
         q.to←⍕Quote¨Nest to
         :If 0=≢q.to
             q.to←a a
         :EndIf
         :If 1∧.=≢¨to
             q.(to←'(,¨',to,')')
         :EndIf
     :EndIf
     :If 3=⎕NC'repeat'
         q.repeat←'⍣',∊⎕NR'repeat'
     :ElseIf 1≢repeat
         q.repeat←'⍣',⍕repeat
     :Else
         q.repeat←''
     :EndIf
     :If ×≢options
         q.options←'⍠',∊{' (',(Quote⊃⍵),' ',({0 1∊⍨⊂⍵:⍕⍵ ⋄ Quote ⍵}⊃⌽⍵),')'}¨options
     :Else
         q.options←''
     :EndIf
     q.op←s r/'SR'
     q.postproc←2↓postproc
     q.postproc,←(hasrepeat∧s)/(''≡q.postproc)↓'∘',combine
     q.(expr←postproc,from,'⎕',op,to,options,repeat)
     :If '?'∊args
         ⍞←q.expr,n
     :EndIf

     :If 0=≢from
         from←''
     :EndIf
     :If 2=⎕NC'to'
     :AndIf 0=≢to
         to←''
     :EndIf

     :Trap 11
         :If r
             output←from ⎕R to⍠options⍣repeat⊢input
         :ElseIf s
             output←from ⎕S to⍠options⍣repeat⊢input
             output←(⍎combine)⍣hasrepeat⊢output ⍝ merge search results if we need to continue
         :EndIf
         output←(⍎postproc)output
     :Case 11 ⍝ faulty regex
         ⍞←'*** ',⎕DMX.(Message,(''≡Message)/⊃DM),' ***',n
         ⍞←'Attempted APL expression: ',q.expr,n
     :EndTrap
 :Else
     ⍞←'*** Left operand must be ''R'' or ''S'' ***'
 :EndIf

 output←regexes(op Run args)input;from;to;options;lines;Expand;repeat;s;r;last;fns;q;hasrepeat;postproc;combine;Quote;Nest;a;n
 output←⍬⊤⍬ ⍝ default result is empty matrix

 n←⎕UCS 10 ⍝ newline
 a←⎕UCS 39 ⍝ apostrofe
 ⍎'Nest←{⊂⍣(1=≡,⍵)⊢⍵}'
 ⍎'Quote←{a,a,⍨⍵/⍨1+⍵=a}'

 :Trap 11 22 ⍝ accept filename or actual input
     input←input ⎕NTIE 0
 :EndTrap

 :Trap 11 22 ⍝ accept filename or actual regex
     regexes←⊃⎕NGET regexes 1
 :EndTrap
 regexes←{,¨⍣(¯2=≡⍵)⊢⍵}Nest regexes

 repeat←⍎{×≢⍵:⍵ ⋄ '1'}args∩'≡',⎕D ⍝ extract repetition, if any, else 1
 hasrepeat←∨/args∊⍨'≡',⎕D
 ⍝ select options:
 options←('ResultText' 'Simple')('Greedy' 0)('IC' 1)('Mode' 'D')('Mode' 'M')('DotAll' 1)('UCP' 1)('OM' 1)/⍨1,'gidmauo'∊819⌶args~' -'
 s r←(∨/∊∘op)¨'Ss' 'Rr'
 :If s∨r
     options↓⍨←s ⍝ ResultText is not for ⎕S

     fns←+/∧\'⍵'∊¨regexes
     postproc←∊'⊢','∘{'∘,¨,∘'}'¨fns↑regexes ⍝ post-processing functions
     regexes↓⍨←fns

     last←⊃regexes↓⍨¯1+≢regexes
     :If '⍵'∊last ⍝ transformation function → many-to-function
     :OrIf 2|≢regexes ⍝ odd number of strings → many-to-one
         from←¯1↓regexes ⍝ remove transformation pattern/function
         :If ~'⍵'∊last ⍝ no ⍵ → pattern
             Expand←'⍵B' '⍵b' '⍵P' '⍵p' '⍵M' '⍵O' '⍵L' '⍵N'⎕R'⍵.Block' '⍵.BlockNum' '⍵.Pattern' '⍵.PatternNum' '⍵.Match' '⍵.Offsets' '⍵.Lengths' '⍵.Names'
         :OrIf ~≡⎕FX,⊂'to←{',(r/',⍕{'),(Expand last),(r/'}⍵'),'}' ⍝ if definition fails, revert to pattern (allows ⍵ in pattern)
             to←last
         :EndIf
     :Else
         lines←2÷⍨≢regexes
         from←regexes↓⍨-lines
         to←lines↓regexes
     :EndIf

     combine←'{⊃,/⍵↑¨⍨⌈/≢¨⍵}'
     q←⎕NS ⍬
     q.from←⍕Quote¨Nest from
     :If 0=≢q.from
         q.from←a a
     :EndIf
     :If 1∧.=≢¨from
         q.(from←'(,¨',from,')')
     :EndIf
     :If 3=⎕NC'to'
         q.to←1⌽'  ',3↓∊⎕NR'to'
     :Else
         q.to←⍕Quote¨Nest to
         :If 0=≢q.to
             q.to←a a
         :EndIf
         :If 1∧.=≢¨to
             q.(to←'(,¨',to,')')
         :EndIf
     :EndIf
     :If 3=⎕NC'repeat'
         q.repeat←'⍣',∊⎕NR'repeat'
     :ElseIf 1≢repeat
         q.repeat←'⍣',⍕repeat
     :Else
         q.repeat←''
     :EndIf
     :If ×≢options
         q.options←'⍠',∊{' (',(Quote⊃⍵),' ',({0 1∊⍨⊂⍵:⍕⍵ ⋄ Quote ⍵}⊃⌽⍵),')'}¨options
     :Else
         q.options←''
     :EndIf
     q.op←s r/'SR'
     q.postproc←2↓postproc
     q.postproc,←(hasrepeat∧s)/(''≡q.postproc)↓'∘',combine
     q.(expr←postproc,from,'⎕',op,to,options,repeat)
     :If '?'∊args
         ⍞←q.expr,n
     :EndIf

     :If 0=≢from
         from←''
     :EndIf
     :If 2=⎕NC'to'
     :AndIf 0=≢to
         to←''
     :EndIf

     :Trap 11
         :If r
             output←from ⎕R to⍠options⍣repeat⊢input
         :ElseIf s
             output←from ⎕S to⍠options⍣repeat⊢input
             output←(⍎combine)⍣hasrepeat⊢output ⍝ merge search results if we need to continue
         :EndIf
         output←(⍎postproc)output
     :Case 11 ⍝ faulty regex
         ⍞←'*** ',⎕DMX.(Message,(''≡Message)/⊃DM),' ***',n
         ⍞←'Attempted APL expression: ',q.expr,n
     :EndTrap
 :Else
     ⍞←'*** Left operand must be ''R'' or ''S'' ***'
 :EndIf


Boot.LoadHTML utility

Boot.LoadHTML utility

:Namespace Boot

    (⎕ML ⎕IO)←1

    :section Startup/Shutdown

    ∇ Run root

      AppRoot←folderize root  ⍝ application (website) root
      AppRoot Load 1 ⍝ load essential objects
      #.Utils.SetupCompatibility
      ms←Init ConfigureServer AppRoot ⍝ read configuration and create server instance
      ConfigureDatasources ms
      ConfigureVirtual ms
      ConfigureResources ms
      ConfigureContentTypes ms
      ConfigureLogger ms

      ms.Run
    ∇

    ∇ {AppRoot}Load yes;files;f;classes;class;utils;t;core;HTML;extensions;HTMLsubdirs
      ⍝ Load required objects for MiServer
      ⍝ Note: DRC namespace is not SALTed
      ⍝ yes - 1 to perform load, 0 to clean up

      classes←''
      :If 0≠⎕NC'AppRoot'
          classes←(⎕SE.SALT.List AppRoot,'Code -raw')[;1 2] ⍝ Classes in application folder
          classes←(empty¨classes[;1])/classes[;2] ⍝ remove directory entries (have <DIR> in [;1])
      :EndIf
      utils←(⎕SE.SALT.List MSRoot,'Utils -raw')[;2]   ⍝ find utility libraries
      core←(⊂'Boot')~⍨(⎕SE.SALT.List MSRoot,'Core -raw')[;2]
      extensions←(⎕SE.SALT.List MSRoot,'Extensions -raw')[;2]
      HTML←(⎕SE.SALT.List MSRoot,'HTML -raw')[;2] ⍝ force to be loaded first
      HTMLsubdirs←HTML∩,⎕SE.SALT.List MSRoot,'HTML -folder'
      :If yes

          files←'Core/'∘,¨core
          files,←'Utils/'∘,¨utils
          files,←'Extensions/'∘,¨extensions

          :For f :In files
              disperror 1 ⎕SE.SALT.Load MSRoot,f,' -target=#' ⍝ do not reload already loaded spaces
          :EndFor

          #.SupportedHtml5Elements.Build_html_namespace

          :For f :In HTML
              disperror ⎕SE.SALT.Load MSRoot,'HTML/',f,' -target=#'
              :If (⊂f)∊HTMLsubdirs
                  disperror¨⎕SE.SALT.Load MSRoot,'HTML/',f,'/* -target=#.',f
              :EndIf
          :EndFor

⍝          :For f :In HTML∩,⎕SE.SALT.List MSRoot,'HTML -folder'
⍝              ⎕SE.SALT.Load MSRoot,'HTML/',f,'/* -target=#.',f
⍝          :EndFor

          :If 0≠⍴classes
              :For class :In classes
                  disperror ⎕SE.SALT.Load AppRoot,'Code/',class,' -target=#'
              :EndFor
          :EndIf

          'Pages'#.⎕NS'' ⍝ Container Space for loaded classes
          #.Pages.(MiPage MildPage RESTful)←#.(MiPage MildPage RESTful)
          BuildEAWC ⍝ build the Easy As ⎕WC namespace
          :If #.Files.DirExists AppRoot,'/Code/Templates/'
              disperror ⎕SE.SALT.Load AppRoot,'/Code/Templates/* -target=#.Pages'
          :EndIf

      :Else ⍝ Cleanup
          #.⎕EX¨classes
          #.⎕EX¨utils
          #.⎕EX'Pages'
          #.⎕EX¨'MiServer' 'HTTPRequest'
      :EndIf
    ∇

    ∇ LoadHTML f
    ⍝ Utility to reload an HTML content-generating namespace
      disperror ⎕SE.SALT.Load MSRoot,'HTML/',f,' -target=#'
      disperror¨⎕SE.SALT.Load MSRoot,'HTML/',f,'/* -target=#.',f
    ∇

    ∇ ms←Init Config;path;class;classes;e;res;mask
     ⍝ Create instances of MiServer, Session and Authentication Handlers

      ms←⎕NEW(#⍎Config.ClassName)Config
      path←MSRoot,'Extensions/'

      :If 0≠⍴Config.SessionHandler
          class←⎕SE.SALT.Load path,Config.SessionHandler
          ms.SessionHandler←⎕NEW class ms
      :EndIf

      :If 0≠⍴Config.Authentication
          class←⎕SE.SALT.Load path,Config.Authentication
          ms.Authentication←⎕NEW class ms
      :EndIf

      :If 0≠⍴Config.SupportedEncodings
          {}⎕SE.SALT.Load path,'ContentEncoder'
          :For e :In Config.SupportedEncodings
              class←⎕SE.SALT.Load path,e
              ms.Encoders,←⎕NEW class
          :EndFor
          :If ∨/mask←0≠1⊃¨res←ms.Encoders.Init
              2 ms.Log'Content Encoding Initialization failed for:',∊' ',¨mask/ms.Encoders.Encoding
              ms.Encoders←(~mask)/ms.Encoders
          :EndIf
      :EndIf
      Config.UseContentEncoding∧←0≠⍴ms.Encoders

      :If 0≠⍴Config.Logger
          class←⎕SE.SALT.Load path,Config.Logger
          ms.Logger←⎕NEW class ms
      :EndIf
    ∇

    ∇ End;classes;z;m
      ⍝ Clean up the workspace

      :If 9=⎕NC'ms'
          :Trap 0
              ms.End
          :EndTrap
          {}try'⎕EX⍕⊃⊃⎕CLASS ms.SessionHandler'
          {}try'⎕EX⍕⊃⊃⎕CLASS ms.Authentication'
          {}try'⎕EX⍕⊃⊃⎕CLASS ms.Logger'
          {}try'⎕EX⍕¨∪enlist ⎕CLASS¨ms.Encoders'
          ⎕EX⍕⊃⊃⎕CLASS ms
          ⎕EX'ms'
      :EndIf

      :If 9=⎕NC'SQA'
          {}try'SQA.Close''.'''
      :EndIf

      :If 0≠⍴classes←↓#.⎕NL 9.4
      :AndIf 0≠⍴classes←(m←2=⊃∘⍴¨z←⎕CLASS¨#⍎¨classes)/classes
      :AndIf 0≠⎕NC'#.MiPage'
          classes←(#.MiPage≡¨2 1∘⊃¨m/z)/classes
          #.⎕EX↑⍕¨classes ⍝ Erase loaded classes
      :EndIf

      Load 0
      ⎕EX'#.MiPage'
      ⎕EX'AppRoot'
      {}try'#.DRC.Close ''.'''
    ∇

    ∇ BuildEAWC;src;sources;fields;source;list;mask;refs;target
     ⍝ Build the Easy As ⎕WC namespace from core classes and its own source
     ⍝ Also build the #._ namespace with shortcuts
      sources←#._html #._SF #._JQ #._DC #._JS
⍝      fields←''
      '_'#.⎕NS''
      :For source :In sources
          list←source.⎕NL ¯9.4
          list←list/⍨'_'≠⊃¨list
          :If ∨/mask←0≠refs←source{6::0 ⋄ (⍕⍺){('.'∊1↓s↓r)<⍺≡(s←⍴⍺)↑r←⍕⍵}t←⍺⍎⍵:t ⋄ 0}¨list
              #._⍎∊(mask/list){'⋄',⍺,'←',⍕⍵}¨mask/refs
          :EndIf
      :EndFor
    ∇

    :endsection

    :section Configuration

    ∇ r←ns Setting pars;name;num;default;mask
    ⍝ returns setting from a config style namespace or provides a default if it doesn't exist
    ⍝ pars - name [num] [default]
    ⍝ ns - namespace reference
    ⍝ name - name of the setting
    ⍝ num - 1 if setting is numeric scalar, (,1) if numeric vector is allowed, 0 otherwise
    ⍝ default - default value if not found
      pars←eis pars
      (name num)←2↑pars,(⍴pars)↓'' 0 ''
      :If 2<⍴pars ⋄ default←3⊃pars
      :Else ⋄ default←(1+num)⊃''⍬
      :EndIf
      r←(⍴ns)⍴⊂default
      :If ∨/mask←0≠⊃¨ns.⎕NC⊂name
          (mask/r)←(((⍴⍴num)∘tonum)⍣(⊃num))¨(mask/ns).⍎⊂name
      :EndIf
      :If 0=⍴⍴r ⋄ r←⊃r ⋄ :EndIf
    ∇

    ∇ config←{element}ReadConfiguration type;serverconfig;file;siteconfig;thing;ind;mask
    ⍝ Attempt to read configuration file
    ⍝ 1) from server root MSRoot
    ⍝ 2) from site root AppRoot
    ⍝ merging the two if they both exist - site settings overrule server settings
      config←''
      :If #.Files.Exists file←MSRoot,'Config/',type,'.xml'
          config←serverconfig←(#.XML.ToNS #.Files.GetText file)⍎type
      :EndIf
      :If #.Files.Exists file←AppRoot,'Config/',type,'.xml'
          siteconfig←(#.XML.ToNS #.Files.GetText file)⍎type
          :If 0∊⍴config
              config←siteconfig
          :ElseIf 0=⎕NC'element'
              {}{try'serverconfig.',⍵,'←siteconfig.',⍵}¨siteconfig.⎕NL ¯2
          :Else ⍝ element specifies the element(s) to search on
              :For thing :In siteconfig
                  :If 0≠thing.⎕NC element
                      :If ∨/mask←0≠∊serverconfig.⎕NC⊂element
                          :If (+/mask)<ind←((mask/serverconfig)⍎¨⊂element)⍳⊂thing⍎element
                              serverconfig,←thing
                          :Else
                              serverconfig[(mask/⍳⍴mask)[ind]]←thing
                          :EndIf
                      :Else
                          serverconfig,←thing
                      :EndIf
                  :EndIf
              :EndFor
              config←serverconfig
          :EndIf
      :EndIf
    ∇

    ∇ Config←ConfigureServer AppRoot;file
    ⍝ configure server level settings, setting defaults for needed ones that are not supplied
      Config←ReadConfiguration'Server'

      Config.AllowedHttpCommands←{⎕ML←3 ⋄ ⍵⊂⍨~⍵∊' ,'}#.Strings.lc Config Setting'AllowedHttpCommands' 0 'get,post'
      Config.AppRoot←AppRoot
      Config.Authentication←Config Setting'Authentication' 0 'SimpleAuth'
      Config.CertFile←Config Setting'CertFile' 0 ''
      Config.ClassName←Config Setting'ClassName' 0 'MiServer'
      Config.Debug←Config Setting'Debug' 1 0
      Config.DefaultExtension←Config Setting'DefaultExtension' 0 '.mipage'
      Config.DefaultPage←Config Setting'DefaultPage' 0 'index.mipage'
      Config.FormatHtml←Config Setting'FormatHtml' 1 0
      Config.Host←Config Setting'Host' 0 'localhost'
      Config.HttpCacheTime←Config Setting'HttpCacheTime' 1 0 ⍝ default to off (0)
      Config.IPVersion←Config Setting'IPVersion' 0 'IPV4'
      Config.IdleTimeOut←Config Setting'IdleTimeOut' 1 0 ⍝ default to none (0)
      Config.KeyFile←Config Setting'KeyFile' 0 ''
      Config.Lang←Config Setting'Lang' 0 'en'
      Config.LogMessageLevel←Config Setting'LogMessageLevel' 1 1 ⍝ default to error messages only
      Config.Logger←Config Setting'Logger' 0 ''
      Config.MSRoot←MSRoot
      Config.Name←Config Setting'Name' 0 'MiServer'
      Config.Port←Config Setting'Port' 1 8080
      Config.Ports←Config Setting'Ports'(,1)⍬
      Config.Production←Config Setting'Production' 1 0 ⍝ production mode?  (0/1 = development debug framework en/disabled)
      Config.RESTful←Config Setting'RESTful' 1 0 ⍝ RESTful web service?
      Config.RootCertDir←Config Setting'RootCertDir' 0 ''
      Config.Root←AppRoot ⍝ folderize MSRoot{((isRelPath ⍵)/⍺),⍵}AppRoot
      Config.SSLFlags←Config Setting'SSLFlags' 1(32+64)  ⍝ Accept Without Validating, RequestClientCertificate
      Config.Secure←Config Setting'Secure' 1 0
      Config.Server←Config Setting'Server' 0 ''
      Config.SessionHandler←Config Setting'SessionHandler' 0 'SimpleSessions'
      Config.SessionTimeOut←Config Setting'SessionTimeOut' 1 30 ⍝ 30 minute timeout
      Config.SupportedEncodings←{(⊂'')~⍨1↓¨(⍵=⊃⍵)⊂⍵}',',Config Setting'SupportedEncodings' 0
      Config.TempFolder←folderize Config.Root{0∊⍴⍵:⍵ ⋄ ((isRelPath ⍵)/⍺),⍵}Config Setting'TempFolder' 0
      Config.TrapErrors←Config Setting'TrapErrors' 1 0
      Config.UseContentEncoding←Config Setting'UseContentEncoding' 1 0 ⍝ aka HTTP Compression default off (0)

      :If 0≠⎕NC'#.DrA' ⍝ Transfer DrA config options
          {}#.DrA.SetDefaults
          #.DrA.AppName←Config.Name
          #.DrA.MailMethod←Config Setting'MailMethod' 0 'NONE'
          #.DrA.MailRecipient←Config Setting'MailRecipient' 0
          #.DrA.Mode←Config.Debug
          #.DrA.NoUser←Config Setting'NoUser' 1 1 ⍝ run without user interaction
          #.DrA.Path←AppRoot ⍝ Where to put log files
          #.DrA.SMTP_Gateway←Config Setting'SMTP_Gateway' 0
          #.DrA.UseHTTP←Config Setting'UseHTTP' 1 0
      :EndIf
    ∇

    ∇ ConfigureDatasources ms;file;ds;name;tmp;orig;dyalog
      ⍝ load any datasource definitions
      :If ~0∊⍴ms.Datasources←'Name'ReadConfiguration'Datasources'
          :For ds :In ms.Datasources
              :For name :In ds.⎕NL ¯2
                  orig←tmp←ds.⍎name
                  tmp←SubstPath tmp
                  :If orig≢tmp
                      ⍎'ds.',name,'←tmp'
                  :EndIf
              :EndFor
          :EndFor

          :Trap 0
              :If 0=#.⎕NC'SQA'
                  dyalog←2 ⎕NQ'.' 'GetEnvironment' 'DYALOG'
                  'SQA'#.⎕CY dyalog,'\ws\sqapl' ⍝ copy in SQA
              :EndIf
              :If 0≠1⊃#.SQA.Init'' ⍝ and initialize
                  1 ms.Log'SQA failed to initialize'
              :EndIf
          :Else
              1 ms.Log'SQA failed to initialize'
          :EndTrap
      :EndIf
    ∇

    ∇ ConfigureVirtual ms;file;virtual;mask;inds;v
      ⍝ load virtual (alias) definitions if any
      ms.Config.Virtual←''
      :If ~0∊⍴virtual←'alias'ReadConfiguration'Virtual'
          :If 0∊mask←{⍵=⍳⍴⍵}inds←{⍵⍳⍵}virtual.alias ⍝ check for duplicate aliases, keep first
              1 ms.Log'Duplicate virtual aliases defined (first occurrence will be used): ',1↓enlist',',¨(~mask)/virtual.alias
              virtual←mask/virtual
          :EndIf
          ⍝ substitute server and site root if found
          virtual.path←SubstPath¨virtual.path
          :For v :In virtual
              :If ~#.Files.DirExists v.path ⍝ check if mapped path exists
                  1 ms.Log'Virtual path not found: ',v.path
                  virtual~←v
              :Else
                  :If #.Files.DirExists AppRoot,v.alias ⍝ check if alias conflicts with local path
                      1 ms.Log'Virtual alias "',v.alias,'" overrides site path of same name.'
                  :EndIf
              :EndIf
          :EndFor
          ms.Config.Virtual←virtual
      :EndIf
    ∇

    ∇ ConfigureResources ms;file;mask;inds;names;uses;map;n;res;f;missing;order
      ⍝ load resource definitions if any
      ms.Config.Resources←0 3⍴⊂''
      :If ~0∊⍴res←'name'ReadConfiguration'Resources'
          :If 0∊mask←{⍵=⍳⍴⍵}inds←{⍵⍳⍵}names←res.name ⍝ check for duplicate aliases, keep first
              1 ms.Log'Duplicate resources defined (first occurence will be used): ',1↓enlist',',¨(~mask)/res.name
              res←mask/res
          :EndIf
          ⍝ build the dependency map
          uses←{(eis⍣(⊃0<⍴⍵))⍵}¨res Setting'uses'
          inds←res.name∘⍳¨uses
          n←⊃⍴res
          :If ∨/mask←∨/¨missing←n<inds
              1 ms.Log'Invalid resource dependency found for:',enlist' ',¨mask/res.name
              inds←(~missing)/¨inds
          :EndIf
          order←OrderResources inds
          inds←(order∘⍳¨inds)[order]
          res←res[order]
          map←,(2⍴n)⍴(1+n)↑1 ⍝ identity matrix
          map[enlist(n×¯1+⍳n)+¨inds]←1
          map←(2⍴n)⍴map
          map←↓{({⍺∨⍺∨.∧⍵}⍣≡)⍨⍵}map
          ⍝ ms.Config.Resources[;1] resource name, [;2] scripts used [;3] styles used
          f←{SubstPath¨{⍵~⊂''}∘∪¨⊃¨,/¨map/¨⊂eis¨⍵}
          ms.Config.Resources←res.name,(f res Setting'script'),[1.1]f res Setting'style'
      :EndIf
    ∇

    ∇ r←OrderResources inds;n;mask
      n←⍳⍴inds
      r←n/⍨mask←0∘∊∘⍴¨inds ⍝ roots with no dependencies
      inds/⍨←~mask
      n/⍨←~mask
      :While ~0∊⍴inds
          mask←∧/¨inds∊¨⊂r
          r,←mask/n
          inds/⍨←~mask
          n/⍨←~mask
      :EndWhile
    ∇

    ∇ ConfigureContentTypes ms;file;ct;inds;exts;types;mask;exp;n
      ⍝ load supported content types
      ms.Config.ContentTypes←0 2⍴⊂''
      :If ~0∊⍴ct←ReadConfiguration'ContentTypes'
          exts←#.Strings.lc¨ct Setting'ext'
          types←ct Setting'type'
          :If ~0∊⍴inds←{⍵/⍳⍴⍵}enlist','∊¨exts
              mask←(⍴exts)⍴1
              exp←{⎕ML←3 ⋄ (⍵≠',')⊂⍵}¨exts[inds]
              mask[inds]←n←⍬∘⍴∘⍴¨exp
              (exts types)←mask∘/¨exts types
              exts[(n/inds)++\~enlist n↑¨1]←⊃,/exp
          :EndIf
          ms.Config.ContentTypes←exts,[1.1]types
      :Else
          1 ms.Log'No content types defined?'
      :EndIf
    ∇

    ∇ ConfigureLogger ms;file;log
      ⍝ load logger information
      ms.Config.Logger←⎕NS''
      ms.Config.Logger.active←0
      :If ~0∊⍴log←ReadConfiguration'Logger'
          ms.Config.Logger.active←log Setting'active' 1 0
          ms.Config.Logger.directory←SubstPath log Setting'directory' 0 ''
          ms.Config.Logger.frequency←log Setting'frequency' 0 ''
          ms.Config.Logger.sizelimit←log Setting'sizelimit' 1 0
      :EndIf
    ∇

    :endsection

    :section Utilities
    disperror←{}∘{326=⎕DR ⍵:'' ⋄ '***'≡3↑⍵:⎕←⍵ ⋄ ''}
    isWin←('.' ⎕WG 'APLVersion')[3]≡⊂,'W'
    fileSep←'/\'[1+isWin]
    MSRoot←{(1-⌊/'/\'⍳⍨⌽⍵)↓⍵}⎕WSID
    isRelPath←{{~'/\'∊⍨(⎕IO+2×isWin∧':'∊⍵)⊃⍵}3↑⍵}
    enlist←{⎕ML←1⋄∊⍵}
      tonum←{⍺←0
          1∊⍺:tonumvec ⍵
          w←⍵ ⋄ ((w='-')/w)←'¯'
          ⊃⊃{~∧/⍺:⎕SIGNAL 11 ⋄ ⍵}/⎕VFI w}
    try←{0::'' ⋄⍎⍵}
    empty←{0∊⍴⍵}
    notEmpty←~∘empty
    eis←{(,∘⊂)⍣((326∊⎕DR ⍵)<2>|≡⍵),⍵} ⍝ Enclose if simple
    isRef←{(0∊⍴⍴⍵)∧326=⎕DR ⍵}
    folderize←{¯1↑⍵∊'/\':⍵ ⋄ ⍵,fileSep}
    makeSitePath←{⍺{((isRelPath ⍵)/⍺),⍵},folderize ⍵}

    ∇ r←tonumvec v;to;minus;digits;c;mask
    ⍝ tonum vector version
    ⍝ allows for specific of ranges and comma or space delimited numbers
    ⍝ tonumvec '8080-8090'  or '5,7-9,11-15'
      r←⍬
      ⎕SIGNAL 11/⍨~∧/v∊⎕D,'., -¯'
      to←{⍺←⍵ ⋄ ⍺,⍺+(¯1*⍺>⍵)×⍳|⍺-⍵}
      v←('^\s*|\s*$'⎕R'')('\s+'⎕R' ')('\s*-\s*'⎕R'-')v
      minus←'-'=v
      digits←v∊⎕D,'.'
      ((minus>(minus∨{1↓⍵,0}digits)∧{¯1↓0,⍵}digits)/v)←⊂'¯'
      ((' '=v)/v)←','
      (('-'=v)/v)←⊂' to '
      :Trap 0
          :For c :In {⎕ML←3 ⋄ ⍵⊂⍨⍵≠','}∊v
              r,←⍎∊c
          :EndFor
      :Else
          ⎕SIGNAL 11
      :EndTrap
    ∇

    ∇ r←SubstPath r
      r←(#.Strings.subst∘('%ServerRoot%'(¯1↓MSRoot)))r
      r←(#.Strings.subst∘('%SiteRoot%'(¯1↓AppRoot)))r
    ∇

    ∇ r←isRunning
      :Trap r←0
          r←ms.TID∊⎕TNUMS
      :EndTrap
    ∇

    ∇ r←Oops;dmx;ends;xsi
    ⍝ debugging framework to bubble up to user's code when rendering fails
      r←'⎕SIGNAL 811'
      ends←{(,⍺)≡(-⍴,⍺)↑⍵}
      :If {0::0 ⋄ #.HtmlPage∊∊⎕CLASS ⍵}⊃⊃⎕RSI
          r←'⎕TRAP←(800 ''C'' ''→FAIL'')(811 ''E'' ''⎕SIGNAL 801'')(813 ''E'' ''⎕SIGNAL 803'')(812 ''S'')(85 ''N'')(0 ''S'')'
          ⎕←''
          ⎕←'*** MiServer Debug ***'
          ⎕←↑⎕DMX.DM
          ⎕←''
          ⎕←'      ⎕SIGNAL 800 ⍝ to ignore this error and carry on'
          ⎕←'      or Press Ctrl-Enter to invoke debugger'
      :Else
          :Select ⎕DMX.EN
          :Case 801
              xsi←⎕XSI
              :If '.HandleMSP'ends 2⊃xsi
                  r←'→FAIL'
              :ElseIf '.HandleMSP'ends 4⊃xsi
                  :If '.Render'ends 3⊃xsi
                      r←'⎕SIGNAL 813'
                  :EndIf
              :ElseIf '.HandleMSP'ends 3⊃xsi
                  :If '.Wrap'ends 2⊃xsi
                      r←'⎕SIGNAL 813'
                  :EndIf
              :Else
                  r←'⎕SIGNAL 811'
              :EndIf
          :Case 803
              r←'⎕SIGNAL 812'
              ⎕←'Press Ctrl-Enter to invoke debugger'
          :Else
              :Trap 0
                  dmx←⎕DMX
                  ⎕←'*** MiServer Debug ***'
                  ⎕←'' 'occurred at:',⍪dmx.(EM(2⊃DM))
                  ⎕←'' 'SI Stack is ',(⍕¯1+⍴⎕XSI),' levels deep'
                  ⎕←''
              :EndTrap
              ⎕←'      ⎕SIGNAL 800 ⍝ to ignore this error and carry on'
              ⎕←'      ⎕SIGNAL 801 ⍝ to cut back and debug'
              r←''
          :EndSelect
      :EndIf
    ∇
    :endsection

:EndNamespace

:Namespace Boot

    (⎕ML ⎕IO)←1

    :section Startup/Shutdown

    ∇ Run root

      AppRoot←folderize root  ⍝ application (website) root
      AppRoot Load 1 ⍝ load essential objects
      #.Utils.SetupCompatibility
      ms←Init ConfigureServer AppRoot ⍝ read configuration and create server instance
      ConfigureDatasources ms
      ConfigureVirtual ms
      ConfigureResources ms
      ConfigureContentTypes ms
      ConfigureLogger ms

      ms.Run
    ∇

    ∇ {AppRoot}Load yes;files;f;classes;class;utils;t;disperror;core;HTML;extensions;HTMLsubdirs
      ⍝ Load required objects for MiServer
      ⍝ Note: DRC namespace is not SALTed
      ⍝ yes - 1 to perform load, 0 to clean up

      disperror←{}∘{326=⎕DR ⍵:'' ⋄ '***'≡3↑⍵:⎕←⍵ ⋄ ''}

      classes←''
      :If 0≠⎕NC'AppRoot'
          classes←(⎕SE.SALT.List AppRoot,'Code -raw')[;1 2] ⍝ Classes in application folder
          classes←(empty¨classes[;1])/classes[;2] ⍝ remove directory entries (have <DIR> in [;1])
      :EndIf
      utils←(⎕SE.SALT.List MSRoot,'Utils -raw')[;2]   ⍝ find utility libraries
      core←(⊂'Boot')~⍨(⎕SE.SALT.List MSRoot,'Core -raw')[;2]
      extensions←(⎕SE.SALT.List MSRoot,'Extensions -raw')[;2]
      HTML←(⎕SE.SALT.List MSRoot,'HTML -raw')[;2] ⍝ force to be loaded first
      HTMLsubdirs←HTML∩,⎕SE.SALT.List MSRoot,'HTML -folder'
      :If yes

          files←'Core/'∘,¨core
          files,←'Utils/'∘,¨utils
          files,←'Extensions/'∘,¨extensions

          :For f :In files
              disperror 1 ⎕SE.SALT.Load MSRoot,f,' -target=#' ⍝ do not reload already loaded spaces
          :EndFor

          #.SupportedHtml5Elements.Build_html_namespace

          :For f :In HTML
              disperror ⎕SE.SALT.Load MSRoot,'HTML/',f,' -target=#'
              :If (⊂f)∊HTMLsubdirs
                  disperror¨⎕SE.SALT.Load MSRoot,'HTML/',f,'/* -target=#.',f
              :EndIf
          :EndFor

⍝          :For f :In HTML∩,⎕SE.SALT.List MSRoot,'HTML -folder'
⍝              ⎕SE.SALT.Load MSRoot,'HTML/',f,'/* -target=#.',f
⍝          :EndFor

          :If 0≠⍴classes
              :For class :In classes
                  disperror ⎕SE.SALT.Load AppRoot,'Code/',class,' -target=#'
              :EndFor
          :EndIf

          'Pages'#.⎕NS'' ⍝ Container Space for loaded classes
          #.Pages.(MiPage MildPage RESTful)←#.(MiPage MildPage RESTful)
          BuildEAWC ⍝ build the Easy As ⎕WC namespace
          :If #.Files.DirExists AppRoot,'/Code/Templates/'
              disperror ⎕SE.SALT.Load AppRoot,'/Code/Templates/* -target=#.Pages'
          :EndIf

      :Else ⍝ Cleanup
          #.⎕EX¨classes
          #.⎕EX¨utils
          #.⎕EX'Pages'
          #.⎕EX¨'MiServer' 'HTTPRequest'
      :EndIf
    ∇

    ∇ ms←Init Config;path;class;classes;e;res;mask
     ⍝ Create instances of MiServer, Session and Authentication Handlers

      ms←⎕NEW(#⍎Config.ClassName)Config
      path←MSRoot,'Extensions/'

      :If 0≠⍴Config.SessionHandler
          class←⎕SE.SALT.Load path,Config.SessionHandler
          ms.SessionHandler←⎕NEW class ms
      :EndIf

      :If 0≠⍴Config.Authentication
          class←⎕SE.SALT.Load path,Config.Authentication
          ms.Authentication←⎕NEW class ms
      :EndIf

      :If 0≠⍴Config.SupportedEncodings
          {}⎕SE.SALT.Load path,'ContentEncoder'
          :For e :In Config.SupportedEncodings
              class←⎕SE.SALT.Load path,e
              ms.Encoders,←⎕NEW class
          :EndFor
          :If ∨/mask←0≠1⊃¨res←ms.Encoders.Init
              2 ms.Log'Content Encoding Initialization failed for:',∊' ',¨mask/ms.Encoders.Encoding
              ms.Encoders←(~mask)/ms.Encoders
          :EndIf
      :EndIf
      Config.UseContentEncoding∧←0≠⍴ms.Encoders

      :If 0≠⍴Config.Logger
          class←⎕SE.SALT.Load path,Config.Logger
          ms.Logger←⎕NEW class ms
      :EndIf
    ∇

    ∇ End;classes;z;m
      ⍝ Clean up the workspace

      :If 9=⎕NC'ms'
          :Trap 0
              ms.End
          :EndTrap
          {}try'⎕EX⍕⊃⊃⎕CLASS ms.SessionHandler'
          {}try'⎕EX⍕⊃⊃⎕CLASS ms.Authentication'
          {}try'⎕EX⍕⊃⊃⎕CLASS ms.Logger'
          {}try'⎕EX⍕¨∪enlist ⎕CLASS¨ms.Encoders'
          ⎕EX⍕⊃⊃⎕CLASS ms
          ⎕EX'ms'
      :EndIf

      :If 9=⎕NC'SQA'
          {}try'SQA.Close''.'''
      :EndIf

      :If 0≠⍴classes←↓#.⎕NL 9.4
      :AndIf 0≠⍴classes←(m←2=⊃∘⍴¨z←⎕CLASS¨#⍎¨classes)/classes
      :AndIf 0≠⎕NC'#.MiPage'
          classes←(#.MiPage≡¨2 1∘⊃¨m/z)/classes
          #.⎕EX↑⍕¨classes ⍝ Erase loaded classes
      :EndIf

      Load 0
      ⎕EX'#.MiPage'
      ⎕EX'AppRoot'
      {}try'#.DRC.Close ''.'''
    ∇

    ∇ BuildEAWC;src;sources;fields;source;list;mask;refs;target
     ⍝ Build the Easy As ⎕WC namespace from core classes and its own source
     ⍝ Also build the #._ namespace with shortcuts
      sources←#._html #._SF #._JQ #._DC #._JS
⍝      fields←''
      '_'#.⎕NS''
      :For source :In sources
          list←source.⎕NL ¯9.4
          list←list/⍨'_'≠⊃¨list
          :If ∨/mask←0≠refs←source{6::0 ⋄ (⍕⍺){('.'∊1↓s↓r)<⍺≡(s←⍴⍺)↑r←⍕⍵}t←⍺⍎⍵:t ⋄ 0}¨list
              #._⍎∊(mask/list){'⋄',⍺,'←',⍕⍵}¨mask/refs
          :EndIf
      :EndFor
    ∇

    :endsection

    :section Configuration

    ∇ r←ns Setting pars;name;num;default;mask
    ⍝ returns setting from a config style namespace or provides a default if it doesn't exist
    ⍝ pars - name [num] [default]
    ⍝ ns - namespace reference
    ⍝ name - name of the setting
    ⍝ num - 1 if setting is numeric scalar, (,1) if numeric vector is allowed, 0 otherwise
    ⍝ default - default value if not found
      pars←eis pars
      (name num)←2↑pars,(⍴pars)↓'' 0 ''
      :If 2<⍴pars ⋄ default←3⊃pars
      :Else ⋄ default←(1+num)⊃''⍬
      :EndIf
      r←(⍴ns)⍴⊂default
      :If ∨/mask←0≠⊃¨ns.⎕NC⊂name
          (mask/r)←(((⍴⍴num)∘tonum)⍣(⊃num))¨(mask/ns).⍎⊂name
      :EndIf
      :If 0=⍴⍴r ⋄ r←⊃r ⋄ :EndIf
    ∇

    ∇ config←{element}ReadConfiguration type;serverconfig;file;siteconfig;thing;ind;mask
    ⍝ Attempt to read configuration file
    ⍝ 1) from server root MSRoot
    ⍝ 2) from site root AppRoot
    ⍝ merging the two if they both exist - site settings overrule server settings
      config←''
      :If #.Files.Exists file←MSRoot,'Config/',type,'.xml'
          config←serverconfig←(#.XML.ToNS #.Files.GetText file)⍎type
      :EndIf
      :If #.Files.Exists file←AppRoot,'Config/',type,'.xml'
          siteconfig←(#.XML.ToNS #.Files.GetText file)⍎type
          :If 0∊⍴config
              config←siteconfig
          :ElseIf 0=⎕NC'element'
              {}{try'serverconfig.',⍵,'←siteconfig.',⍵}¨siteconfig.⎕NL ¯2
          :Else ⍝ element specifies the element(s) to search on
              :For thing :In siteconfig
                  :If 0≠thing.⎕NC element
                      :If ∨/mask←0≠∊serverconfig.⎕NC⊂element
                          :If (+/mask)<ind←((mask/serverconfig)⍎¨⊂element)⍳⊂thing⍎element
                              serverconfig,←thing
                          :Else
                              serverconfig[(mask/⍳⍴mask)[ind]]←thing
                          :EndIf
                      :Else
                          serverconfig,←thing
                      :EndIf
                  :EndIf
              :EndFor
              config←serverconfig
          :EndIf
      :EndIf
    ∇

    ∇ Config←ConfigureServer AppRoot;file
    ⍝ configure server level settings, setting defaults for needed ones that are not supplied
      Config←ReadConfiguration'Server'

      Config.AllowedHttpCommands←{⎕ML←3 ⋄ ⍵⊂⍨~⍵∊' ,'}#.Strings.lc Config Setting'AllowedHttpCommands' 0 'get,post'
      Config.AppRoot←AppRoot
      Config.Authentication←Config Setting'Authentication' 0 'SimpleAuth'
      Config.CertFile←Config Setting'CertFile' 0 ''
      Config.ClassName←Config Setting'ClassName' 0 'MiServer'
      Config.Debug←Config Setting'Debug' 1 0
      Config.DefaultExtension←Config Setting'DefaultExtension' 0 '.mipage'
      Config.DefaultPage←Config Setting'DefaultPage' 0 'index.mipage'
      Config.FormatHtml←Config Setting'FormatHtml' 1 0
      Config.Host←Config Setting'Host' 0 'localhost'
      Config.HttpCacheTime←Config Setting'HttpCacheTime' 1 0 ⍝ default to off (0)
      Config.IPVersion←Config Setting'IPVersion' 0 'IPV4'
      Config.IdleTimeOut←Config Setting'IdleTimeOut' 1 0 ⍝ default to none (0)
      Config.KeyFile←Config Setting'KeyFile' 0 ''
      Config.Lang←Config Setting'Lang' 0 'en'
      Config.LogMessageLevel←Config Setting'LogMessageLevel' 1 1 ⍝ default to error messages only
      Config.Logger←Config Setting'Logger' 0 ''
      Config.MSRoot←MSRoot
      Config.Name←Config Setting'Name' 0 'MiServer'
      Config.Port←Config Setting'Port' 1 8080
      Config.Ports←Config Setting'Ports'(,1)⍬
      Config.Production←Config Setting'Production' 1 0 ⍝ production mode?  (0/1 = development debug framework en/disabled)
      Config.RESTful←Config Setting'RESTful' 1 0 ⍝ RESTful web service?
      Config.RootCertDir←Config Setting'RootCertDir' 0 ''
      Config.Root←AppRoot ⍝ folderize MSRoot{((isRelPath ⍵)/⍺),⍵}AppRoot
      Config.SSLFlags←Config Setting'SSLFlags' 1(32+64)  ⍝ Accept Without Validating, RequestClientCertificate
      Config.Secure←Config Setting'Secure' 1 0
      Config.Server←Config Setting'Server' 0 ''
      Config.SessionHandler←Config Setting'SessionHandler' 0 'SimpleSessions'
      Config.SessionTimeOut←Config Setting'SessionTimeOut' 1 30 ⍝ 30 minute timeout
      Config.SupportedEncodings←{(⊂'')~⍨1↓¨(⍵=⊃⍵)⊂⍵}',',Config Setting'SupportedEncodings' 0
      Config.TempFolder←folderize Config.Root{0∊⍴⍵:⍵ ⋄ ((isRelPath ⍵)/⍺),⍵}Config Setting'TempFolder' 0
      Config.TrapErrors←Config Setting'TrapErrors' 1 0
      Config.UseContentEncoding←Config Setting'UseContentEncoding' 1 0 ⍝ aka HTTP Compression default off (0)

      :If 0≠⎕NC'#.DrA' ⍝ Transfer DrA config options
          {}#.DrA.SetDefaults
          #.DrA.AppName←Config.Name
          #.DrA.MailMethod←Config Setting'MailMethod' 0 'NONE'
          #.DrA.MailRecipient←Config Setting'MailRecipient' 0
          #.DrA.Mode←Config.Debug
          #.DrA.NoUser←Config Setting'NoUser' 1 1 ⍝ run without user interaction
          #.DrA.Path←AppRoot ⍝ Where to put log files
          #.DrA.SMTP_Gateway←Config Setting'SMTP_Gateway' 0
          #.DrA.UseHTTP←Config Setting'UseHTTP' 1 0
      :EndIf
    ∇

    ∇ ConfigureDatasources ms;file;ds;name;tmp;orig;dyalog
      ⍝ load any datasource definitions
      :If ~0∊⍴ms.Datasources←'Name'ReadConfiguration'Datasources'
          :For ds :In ms.Datasources
              :For name :In ds.⎕NL ¯2
                  orig←tmp←ds.⍎name
                  tmp←SubstPath tmp
                  :If orig≢tmp
                      ⍎'ds.',name,'←tmp'
                  :EndIf
              :EndFor
          :EndFor

          :Trap 0
              :If 0=#.⎕NC'SQA'
                  dyalog←2 ⎕NQ'.' 'GetEnvironment' 'DYALOG'
                  'SQA'#.⎕CY dyalog,'\ws\sqapl' ⍝ copy in SQA
              :EndIf
              :If 0≠1⊃#.SQA.Init'' ⍝ and initialize
                  1 ms.Log'SQA failed to initialize'
              :EndIf
          :Else
              1 ms.Log'SQA failed to initialize'
          :EndTrap
      :EndIf
    ∇

    ∇ ConfigureVirtual ms;file;virtual;mask;inds;v
      ⍝ load virtual (alias) definitions if any
      ms.Config.Virtual←''
      :If ~0∊⍴virtual←'alias'ReadConfiguration'Virtual'
          :If 0∊mask←{⍵=⍳⍴⍵}inds←{⍵⍳⍵}virtual.alias ⍝ check for duplicate aliases, keep first
              1 ms.Log'Duplicate virtual aliases defined (first occurrence will be used): ',1↓enlist',',¨(~mask)/virtual.alias
              virtual←mask/virtual
          :EndIf
          ⍝ substitute server and site root if found
          virtual.path←SubstPath¨virtual.path
          :For v :In virtual
              :If ~#.Files.DirExists v.path ⍝ check if mapped path exists
                  1 ms.Log'Virtual path not found: ',v.path
                  virtual~←v
              :Else
                  :If #.Files.DirExists AppRoot,v.alias ⍝ check if alias conflicts with local path
                      1 ms.Log'Virtual alias "',v.alias,'" overrides site path of same name.'
                  :EndIf
              :EndIf
          :EndFor
          ms.Config.Virtual←virtual
      :EndIf
    ∇

    ∇ ConfigureResources ms;file;mask;inds;names;uses;map;n;res;f;missing;order
      ⍝ load resource definitions if any
      ms.Config.Resources←0 3⍴⊂''
      :If ~0∊⍴res←'name'ReadConfiguration'Resources'
          :If 0∊mask←{⍵=⍳⍴⍵}inds←{⍵⍳⍵}names←res.name ⍝ check for duplicate aliases, keep first
              1 ms.Log'Duplicate resources defined (first occurence will be used): ',1↓enlist',',¨(~mask)/res.name
              res←mask/res
          :EndIf
          ⍝ build the dependency map
          uses←{(eis⍣(⊃0<⍴⍵))⍵}¨res Setting'uses'
          inds←res.name∘⍳¨uses
          n←⊃⍴res
          :If ∨/mask←∨/¨missing←n<inds
              1 ms.Log'Invalid resource dependency found for:',enlist' ',¨mask/res.name
              inds←(~missing)/¨inds
          :EndIf
          order←OrderResources inds
          inds←(order∘⍳¨inds)[order]
          res←res[order]
          map←,(2⍴n)⍴(1+n)↑1 ⍝ identity matrix
          map[enlist(n×¯1+⍳n)+¨inds]←1
          map←(2⍴n)⍴map
          map←↓{({⍺∨⍺∨.∧⍵}⍣≡)⍨⍵}map
          ⍝ ms.Config.Resources[;1] resource name, [;2] scripts used [;3] styles used
          f←{SubstPath¨{⍵~⊂''}∘∪¨⊃¨,/¨map/¨⊂eis¨⍵}
          ms.Config.Resources←res.name,(f res Setting'script'),[1.1]f res Setting'style'
      :EndIf
    ∇

    ∇ r←OrderResources inds;n;mask
      n←⍳⍴inds
      r←n/⍨mask←0∘∊∘⍴¨inds ⍝ roots with no dependencies
      inds/⍨←~mask
      n/⍨←~mask
      :While ~0∊⍴inds
          mask←∧/¨inds∊¨⊂r
          r,←mask/n
          inds/⍨←~mask
          n/⍨←~mask
      :EndWhile
    ∇

    ∇ ConfigureContentTypes ms;file;ct;inds;exts;types;mask;exp;n
      ⍝ load supported content types
      ms.Config.ContentTypes←0 2⍴⊂''
      :If ~0∊⍴ct←ReadConfiguration'ContentTypes'
          exts←#.Strings.lc¨ct Setting'ext'
          types←ct Setting'type'
          :If ~0∊⍴inds←{⍵/⍳⍴⍵}enlist','∊¨exts
              mask←(⍴exts)⍴1
              exp←{⎕ML←3 ⋄ (⍵≠',')⊂⍵}¨exts[inds]
              mask[inds]←n←⍬∘⍴∘⍴¨exp
              (exts types)←mask∘/¨exts types
              exts[(n/inds)++\~enlist n↑¨1]←⊃,/exp
          :EndIf
          ms.Config.ContentTypes←exts,[1.1]types
      :Else
          1 ms.Log'No content types defined?'
      :EndIf
    ∇

    ∇ ConfigureLogger ms;file;log
      ⍝ load logger information
      ms.Config.Logger←⎕NS''
      ms.Config.Logger.active←0
      :If ~0∊⍴log←ReadConfiguration'Logger'
          ms.Config.Logger.active←log Setting'active' 1 0
          ms.Config.Logger.directory←SubstPath log Setting'directory' 0 ''
          ms.Config.Logger.frequency←log Setting'frequency' 0 ''
          ms.Config.Logger.sizelimit←log Setting'sizelimit' 1 0
      :EndIf
    ∇

    :endsection

    :section Utilities
    isWin←('.' ⎕WG 'APLVersion')[3]≡⊂,'W'
    fileSep←'/\'[1+isWin]
    MSRoot←{(1-⌊/'/\'⍳⍨⌽⍵)↓⍵}⎕WSID
    isRelPath←{{~'/\'∊⍨(⎕IO+2×isWin∧':'∊⍵)⊃⍵}3↑⍵}
    enlist←{⎕ML←1⋄∊⍵}
      tonum←{⍺←0
          1∊⍺:tonumvec ⍵
          w←⍵ ⋄ ((w='-')/w)←'¯'
          ⊃⊃{~∧/⍺:⎕SIGNAL 11 ⋄ ⍵}/⎕VFI w}
    try←{0::'' ⋄⍎⍵}
    empty←{0∊⍴⍵}
    notEmpty←~∘empty
    eis←{(,∘⊂)⍣((326∊⎕DR ⍵)<2>|≡⍵),⍵} ⍝ Enclose if simple
    isRef←{(0∊⍴⍴⍵)∧326=⎕DR ⍵}
    folderize←{¯1↑⍵∊'/\':⍵ ⋄ ⍵,fileSep}
    makeSitePath←{⍺{((isRelPath ⍵)/⍺),⍵},folderize ⍵}

    ∇ r←tonumvec v;to;minus;digits;c;mask
    ⍝ tonum vector version
    ⍝ allows for specific of ranges and comma or space delimited numbers
    ⍝ tonumvec '8080-8090'  or '5,7-9,11-15'
      r←⍬
      ⎕SIGNAL 11/⍨~∧/v∊⎕D,'., -¯'
      to←{⍺←⍵ ⋄ ⍺,⍺+(¯1*⍺>⍵)×⍳|⍺-⍵}
      v←('^\s*|\s*$'⎕R'')('\s+'⎕R' ')('\s*-\s*'⎕R'-')v
      minus←'-'=v
      digits←v∊⎕D,'.'
      ((minus>(minus∨{1↓⍵,0}digits)∧{¯1↓0,⍵}digits)/v)←⊂'¯'
      ((' '=v)/v)←','
      (('-'=v)/v)←⊂' to '
      :Trap 0
          :For c :In {⎕ML←3 ⋄ ⍵⊂⍨⍵≠','}∊v
              r,←⍎∊c
          :EndFor
      :Else
          ⎕SIGNAL 11
      :EndTrap
    ∇

    ∇ r←SubstPath r
      r←(#.Strings.subst∘('%ServerRoot%'(¯1↓MSRoot)))r
      r←(#.Strings.subst∘('%SiteRoot%'(¯1↓AppRoot)))r
    ∇

    ∇ r←isRunning
      :Trap r←0
          r←ms.TID∊⎕TNUMS
      :EndTrap
    ∇

    ∇ r←Oops;dmx;ends;xsi
    ⍝ debugging framework to bubble up to user's code when rendering fails
      r←'⎕SIGNAL 811'
      ends←{(,⍺)≡(-⍴,⍺)↑⍵}
      :If {0::0 ⋄ #.HtmlPage∊∊⎕CLASS ⍵}⊃⊃⎕RSI
          r←'⎕TRAP←(800 ''C'' ''→FAIL'')(811 ''E'' ''⎕SIGNAL 801'')(813 ''E'' ''⎕SIGNAL 803'')(812 ''S'')(85 ''N'')(0 ''S'')'
          ⎕←''
          ⎕←'*** MiServer Debug ***'
          ⎕←↑⎕DMX.DM
          ⎕←''
          ⎕←'      ⎕SIGNAL 800 ⍝ to ignore this error and carry on'
          ⎕←'      or Press Ctrl-Enter to invoke debugger'
      :Else
          :Select ⎕DMX.EN
          :Case 801
              xsi←⎕XSI
              :If '.HandleMSP'ends 2⊃xsi
                  r←'→FAIL'
              :ElseIf '.HandleMSP'ends 4⊃xsi
                  :If '.Render'ends 3⊃xsi
                      r←'⎕SIGNAL 813'
                  :EndIf
              :ElseIf '.HandleMSP'ends 3⊃xsi
                  :If '.Wrap'ends 2⊃xsi
                      r←'⎕SIGNAL 813'
                  :EndIf
              :Else
                  r←'⎕SIGNAL 811'
              :EndIf
          :Case 803
              r←'⎕SIGNAL 812'
              ⎕←'Press Ctrl-Enter to invoke debugger'
          :Else
              :Trap 0
                  dmx←⎕DMX
                  ⎕←'*** MiServer Debug ***'
                  ⎕←'' 'occurred at:',⍪dmx.(EM(2⊃DM))
                  ⎕←'' 'SI Stack is ',(⍕¯1+⍴⎕XSI),' levels deep'
                  ⎕←''
              :EndTrap
              ⎕←'      ⎕SIGNAL 800 ⍝ to ignore this error and carry on'
              ⎕←'      ⎕SIGNAL 801 ⍝ to cut back and debug'
              r←''
          :EndSelect
      :EndIf
    ∇
    :endsection

:EndNamespace