Fix position of body and OnLoad handlers

:Class MiPage : #.HtmlPage

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

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

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

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

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

    ∇ MakeCommon
      _PageData←⎕NS''
    ∇

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

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

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

      b←RenderBody

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

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

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

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

      :If ''≢OnLoad

      :EndIf

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

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

      r←RenderPage b

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    :endsection

    :section Position

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    :endsection

:EndClass

:Class MiPage : #.HtmlPage

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

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

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

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

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

    ∇ MakeCommon
      _PageData←⎕NS''
    ∇

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

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

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

      b←RenderBody

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

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

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

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

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

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

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

      r←RenderPage b

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    :endsection

    :section Position

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    :endsection

:EndClass


HTML → JSON escaped Unicode converter now handles all numbers

:class ejTreeView : #._SF._ejWidget
⍝ Description:: Syncfusion TreeView widget
⍝ Constructor::  [items [levels [links]]]
⍝ items  - vector of character vectors containing the tree node texts
⍝ levels - the levels of the corresponding items
⍝ links  - the href string to be used when the corresponding element is chosen (clicked)
⍝ Public Fields::
⍝ Items        - vector of character vectors containing the tree node texts
⍝ Levels       - the levels of the corresponding items
⍝ Links        - the href string to be used when the corresponding element is chosen (clicked)
⍝ MakeIds      - 1 to generate IDs
⍝ onNodeSelect - 1 to call APLJax, or the name of the callback function
⍝
⍝ Public Method to request model data to be return to the server::
⍝    r←name getModel subsel - Passes the TreeView model back as a variable called 'model' as input to a callback
⍝
⍝ Public Methods to be used in Callbacks::
⍝    r←addNode name - Will add a node with id "tv_n", where n starts at 0 and increases each time a node is added
⍝    r←removeNode node - Removes the identified node
⍝    r←checkAll - Check everything below the current node
⍝    r←checkNode node - Check a node
⍝    r←uncheckAll - Uncheck everything below the current node
⍝    r←uncheckNode node - Uncheck a node
⍝    r←expandAll - Expands everything below the current node
⍝    r←expandNode node - Expands everything below a node
⍝    r←collapseAll - Collapses everything below the current node
⍝    r←collapseNode node - Collapses everything below a node
⍝    r←selectNode node - Select a node
⍝    r←unselectNode node - Unselect a node
⍝

    :field public shared readonly DocBase←'http://help.syncfusion.com/js/api/ejTreeView.html'
    :field public shared readonly ApiLevel←3
    :field public shared readonly DocDyalog←'/Documentation/DyalogAPIs/Syncfusion/ejTreeView.html'
    :field public shared readonly IntEvt←'beforeCollapse' 'beforeEdit' 'beforeExpand' 'created' 'destroyed' 'inlineEditValidation' 'keyPress' 'nodeCheck' 'nodeClick' 'nodeCollapse' 'nodeDrag' 'nodeDragStart' 'nodeDragStop' 'nodeDropped' 'nodeExpand' 'nodeSelect' 'nodeUncheck'
    :field public Items←,⊂''
    :field public Levels←⍬
    :field public Links←,⊂''
    :field public MakeIds←0
    :field public onNodeSelect←0  ⍝ set to 1 to call APLJax, or the name of the callback function
 ⍝   :field public Ordered←0  ⍝ Ordered      - insert numbering before item names (1 1.1 1.1.1 2 etc.)

    ∇ make
      :Access public
      JQueryFn←Uses←'ejTreeView'
      :Implements constructor
      InternalEvents←IntEvt
      ContainerTag←'ul'
    ∇

    ∇ make1 args;fixed
      :Access public
      JQueryFn←Uses←'ejTreeView'
      :Implements constructor
      InternalEvents←IntEvt
      ContainerTag←'ul'

      fixed←FixArgs args
      Items Levels Links←↓⍉fixed,⍤1⊢(⊃⌽⍴fixed)↓'' 0 ''
    ∇

    ∇ r←Render;mat
      :Access Public
      mat←FixArgs Items Levels Links

      mat←((≢mat),4)↑mat,⊂''
      mat←mat[;2 1 4 3]

      mat[;2]←'\&#x([[:xdigit:]]+);'⎕R'\\u\1'⊢mat[;2]
      mat[;2]←'\&#(\d+);'⎕R{'\u',∊#.Utils.tohex⍎⍵.Match∩⎕D}mat[;2]

      SetId
      mat[;3]←id #.Utils.levels2ids mat[;1] ⍝ auto-generate node ids

⍝      mat[;2]←((,¨'_' '$')⎕R'.' '\ '⊢(1+≢id)↓¨mat[;3]),¨⍣Ordered⊢mat[;2] ⍝ add numbering if ordered
⍝
      :If onNodeSelect≢0
          On'nodeSelect'onNodeSelect('node' 'eval' 'JSON.stringify(argument.id)')
      :EndIf
      r←#.HTMLInput.JS id FormatItems mat
      'fields'Set'⍎{dataSource: ',(id,'_data'),',id:"id",parentId:"pid",text:"name",hasChild:"hasChild",linkAttribute:"link"}'
      r,←⎕BASE.Render
    ∇

    ∇ r←id FormatItems items;isparent;pid;level;txt;iid;url;i;pind;p
      :Access public shared
      r←'var ',id,'_data = ['
      :If ~0∊⍴items
          :Select n←0⌈4-2⊃⍴items
          :CaseList 3 4
              :Return  ⍝ Not enough data to render ejTreeView
          :Case 2 ⍝ no ids or links
              items,←(id,'_item_')∘,∘⍕¨⍳1⊃⍴items ⍝ generate ids
              items,←⊂''
          :Case 1
              items,←⊂''
          :EndSelect
          :If ~0∊⍴items ⍝ Unnecessary, as we never get here if empty
              items,←isparent←{2</⍵,0}items[;1]
              items,←{⌽+/∨\(∘.<⍨⍳⍴⍵)∧∘.>⍨⌽⍵}items[;1]
              :For i :In ⍳⊃⍴items
                  (level txt iid url p pind)←items[i;]
                  r,←'{id:"',(⍕iid),'",name:"',txt,'"'
                  :If p ⍝ parent?
                      r,←',hasChild: true'
                  :EndIf
                  :If 0≠pind
                      r,←',pid:"',(⍕(⊂pind 3)⊃items),'"'
                  :EndIf
                  :If ~0∊⍴url
                      r,←',link:"',url,'"'
                  :EndIf
                  r,←'},'
              :EndFor
              r←¯1↓r
          :EndIf
      :EndIf
      r,←'];'
    ∇

    :Section SF APIs
    ⍝ This section contains methods that are suitable results for call-back functions
    ⍝ which want to call SyncFusion API calls. The set of functions is not complete, please
    ⍝ see the Syncfusion documentation for details and feel free to implement additions

    ∇ r←addNode name
      ⍝ Will add a node with id "tv_n", where n starts at 0 and increases each time a node is added
      ⍝ /// NB due to a bug in the Syncfusion widget (August 2015), the "model" is not updated
      :Access Public
      SetId ⋄ r←Execute'$("#',id,'").data("ejTreeView").addNode("',name,'");'
    ∇

    ∇ r←removeNode node
      ⍝ Removes the identified node
      :Access Public
      SetId ⋄ r←Execute'$("#',id,'").data("ejTreeView").removeNode($("#',node,'"));'
    ∇

    ∇ r←checkAll
      ⍝ Check everything below the current node
      :Access Public
      SetId ⋄ r←Execute'$("#',id,'").data("ejTreeView").checkAll();'
    ∇

    ∇ r←checkNode node
      ⍝ Check a node
      :Access Public
      SetId ⋄ r←Execute'$("#',id,'").data("ejTreeView").checkNode($("#',node,'"));'
    ∇

    ∇ r←uncheckAll
      ⍝ Uncheck everything below the current node
      :Access Public
      SetId ⋄ r←Execute'$("#',id,'").data("ejTreeView").uncheckAll();'
    ∇

    ∇ r←uncheckNode node
      ⍝ Uncheck a node
      :Access Public
      SetId ⋄ r←Execute'$("#',id,'").data("ejTreeView").uncheckNode($("#',node,'"));'
    ∇

    ∇ r←expandAll
      ⍝ Expands everything below the current node
      :Access Public
      SetId ⋄ r←Execute'$("#',id,'").data("ejTreeView").expandAll();'
    ∇

    ∇ r←expandNode node
      ⍝ Expands everything below a node
      :Access Public
      SetId ⋄ r←Execute'$("#',id,'").data("ejTreeView").expandNode($("#',node,'"));'
    ∇

    ∇ r←collapseAll
      ⍝ Collapses everything below the current node
      :Access Public
      SetId ⋄ r←Execute'$("#',id,'").data("ejTreeView").collapseAll();'
    ∇

    ∇ r←collapseNode node
      ⍝ Collapses everything below a node
      :Access Public
      SetId ⋄ r←Execute'$("#',id,'").data("ejTreeView").collapseNode($("#',node,'"));'
    ∇

    ∇ r←selectNode node
      ⍝ Select a node
      :Access Public
      SetId ⋄ r←Execute'$("#',id,'").data("ejTreeView").selectNode($("#',node,'"));'
    ∇

    ∇ r←unselectNode node
      ⍝ Unselect a node
      :Access Public
      SetId ⋄ r←Execute'$("#',id,'").data("ejTreeView").unselectNode($("#',node,'"));'
    ∇

    ∇ r←name getModel subsel;model
      ⍝ Passes the TreeView model back as a variable called 'model' as input to a callback
      ⍝ Usage:     aButton.On 'click' 'onButtonClick' ('model' tvinstance.getModel '')
      :Access Public
      SetId ⋄ model←'model',(0≠⍴subsel)/'.',subsel ⍝ "model" or "model.subsel"
      r←(name'eval'('JSON.stringify($("#',id,'").ejTreeView("',model,'"))'))
    ∇

    :EndSection

    :section test
    ∇ r←test;c;m
      :Access public shared
      ⎕SHADOW,∊¨'cm'∘.,⊂¨⍕¨⍳14

      c1←'abc' 'def'                          ⍝
      m1←⍪c1

      c2←('abc' 'def')(1 2)                   ⍝
      m2←⍉↑c2

      c3←('abc' 'def')(1 2)('www' 'www1')     ⍝
      m3←⍉↑c3

      c4←2 1⍴⊂¨'abc' 'def'                    ⍝
      m4←c4

      c5←⍉↑('abc' 'def')(1 2)                 ⍝
      m5←c5

      c6←⍉↑('abc' 'def')(1 2)('www' 'www1')   ⍝
      m6←c6

      c7←('abc' 'def')1
      m7←⍉↑('abc' 'def')(1 1)

      c8←('abc' 'def')1('www' 'www1')
      m8←⍉↑('abc' 'def')(1 1)('www' 'www1')

      c9←('abc' 'def')(1 2)(,⊂'www')
      m9←⍉↑('abc' 'def')(1 2)('www' 'www')

      c10←('abc' 'def')(1 2)'www'
      m10←m9

      c11←('abc' 'def')1(,⊂'www')
      m11←⍉↑('abc' 'def')(1 1)('www' 'www')

      c12←('abc' 'def')1 'www'
      m12←m11

      c13←'abc' 1
      m13←1 2⍴c13

      c14←'abc' 1 'www'
      m14←1 3⍴c14

      r←⍬
      :For c m :InEach ↓∊¨'cm'∘.,⊂¨⍕¨⍳14
          r,←÷(⍎m)≡FixArgs⍎c
      :EndFor
    ∇

  ⍝  eis←{⊂⍣(1=≡⍵)⊢⍵}
    link←{(⊂⍺),eis ⍵}

    ∇ m←FixArgs c
      :If 2=≢⍴c ⍝ matrix
          m←c
          →0
      :EndIf

      :If (1=≢∪⎕DR¨c)∧∧/1≥|≡¨c  ⍝ 'abc' 'def'
          m←⍪,¨c
          →0
      :EndIf

      :If 1=≢∪≢¨c ⍝ ('abc' 'def') (1 2)
          m←⍉↑c
          →0
      :EndIf

      m←↑⊃link¨/eis¨c

    ∇

    :endsection
:EndClass

:class ejTreeView : #._SF._ejWidget
⍝ Description:: Syncfusion TreeView widget
⍝ Constructor::  [items [levels [links]]]
⍝ items  - vector of character vectors containing the tree node texts
⍝ levels - the levels of the corresponding items
⍝ links  - the href string to be used when the corresponding element is chosen (clicked)
⍝ Public Fields::
⍝ Items        - vector of character vectors containing the tree node texts
⍝ Levels       - the levels of the corresponding items
⍝ Links        - the href string to be used when the corresponding element is chosen (clicked)
⍝ MakeIds      - 1 to generate IDs
⍝ onNodeSelect - 1 to call APLJax, or the name of the callback function
⍝
⍝ Public Method to request model data to be return to the server::
⍝    r←name getModel subsel - Passes the TreeView model back as a variable called 'model' as input to a callback
⍝
⍝ Public Methods to be used in Callbacks::
⍝    r←addNode name - Will add a node with id "tv_n", where n starts at 0 and increases each time a node is added
⍝    r←removeNode node - Removes the identified node
⍝    r←checkAll - Check everything below the current node
⍝    r←checkNode node - Check a node
⍝    r←uncheckAll - Uncheck everything below the current node
⍝    r←uncheckNode node - Uncheck a node
⍝    r←expandAll - Expands everything below the current node
⍝    r←expandNode node - Expands everything below a node
⍝    r←collapseAll - Collapses everything below the current node
⍝    r←collapseNode node - Collapses everything below a node
⍝    r←selectNode node - Select a node
⍝    r←unselectNode node - Unselect a node
⍝

    :field public shared readonly DocBase←'http://help.syncfusion.com/js/api/ejTreeView.html'
    :field public shared readonly ApiLevel←3
    :field public shared readonly DocDyalog←'/Documentation/DyalogAPIs/Syncfusion/ejTreeView.html'
    :field public shared readonly IntEvt←'beforeCollapse' 'beforeEdit' 'beforeExpand' 'created' 'destroyed' 'inlineEditValidation' 'keyPress' 'nodeCheck' 'nodeClick' 'nodeCollapse' 'nodeDrag' 'nodeDragStart' 'nodeDragStop' 'nodeDropped' 'nodeExpand' 'nodeSelect' 'nodeUncheck'
    :field public Items←,⊂''
    :field public Levels←⍬
    :field public Links←,⊂''
    :field public MakeIds←0
    :field public onNodeSelect←0  ⍝ set to 1 to call APLJax, or the name of the callback function
 ⍝   :field public Ordered←0  ⍝ Ordered      - insert numbering before item names (1 1.1 1.1.1 2 etc.)

    ∇ make
      :Access public
      JQueryFn←Uses←'ejTreeView'
      :Implements constructor
      InternalEvents←IntEvt
      ContainerTag←'ul'
    ∇

    ∇ make1 args;fixed
      :Access public
      JQueryFn←Uses←'ejTreeView'
      :Implements constructor
      InternalEvents←IntEvt
      ContainerTag←'ul'

      fixed←FixArgs args
      Items Levels Links←↓⍉fixed,⍤1⊢(⊃⌽⍴fixed)↓'' 0 ''
    ∇

    ∇ r←Render;mat
      :Access Public
      mat←FixArgs Items Levels Links

      mat←((≢mat),4)↑mat,⊂''
      mat←mat[;2 1 4 3]

      mat[;2]←'\&#x(\d{4});'⎕R'\\u\1'⊢mat[;2]
      mat[;2]←'\&#(\d{4});'⎕R{'\u',∊#.Utils.tohex⍎4↑2⌽⍵.Match}⊢mat[;2]

      SetId
      mat[;3]←id #.Utils.levels2ids mat[;1] ⍝ auto-generate node ids

⍝      mat[;2]←((,¨'_' '$')⎕R'.' '\ '⊢(1+≢id)↓¨mat[;3]),¨⍣Ordered⊢mat[;2] ⍝ add numbering if ordered
⍝
      :If onNodeSelect≢0
          On'nodeSelect'onNodeSelect('node' 'eval' 'JSON.stringify(argument.id)')
      :EndIf
      r←#.HTMLInput.JS id FormatItems mat
      'fields'Set'⍎{dataSource: ',(id,'_data'),',id:"id",parentId:"pid",text:"name",hasChild:"hasChild",linkAttribute:"link"}'
      r,←⎕BASE.Render
    ∇

    ∇ r←id FormatItems items;isparent;pid;level;txt;iid;url;i;pind;p
      :Access public shared
      r←'var ',id,'_data = ['
      :If ~0∊⍴items
          :Select n←0⌈4-2⊃⍴items
          :CaseList 3 4
              :Return  ⍝ Not enough data to render ejTreeView
          :Case 2 ⍝ no ids or links
              items,←(id,'_item_')∘,∘⍕¨⍳1⊃⍴items ⍝ generate ids
              items,←⊂''
          :Case 1
              items,←⊂''
          :EndSelect
          :If ~0∊⍴items ⍝ Unnecessary, as we never get here if empty
              items,←isparent←{2</⍵,0}items[;1]
              items,←{⌽+/∨\(∘.<⍨⍳⍴⍵)∧∘.>⍨⌽⍵}items[;1]
              :For i :In ⍳⊃⍴items
                  (level txt iid url p pind)←items[i;]
                  r,←'{id:"',(⍕iid),'",name:"',txt,'"'
                  :If p ⍝ parent?
                      r,←',hasChild: true'
                  :EndIf
                  :If 0≠pind
                      r,←',pid:"',(⍕(⊂pind 3)⊃items),'"'
                  :EndIf
                  :If ~0∊⍴url
                      r,←',link:"',url,'"'
                  :EndIf
                  r,←'},'
              :EndFor
              r←¯1↓r
          :EndIf
      :EndIf
      r,←'];'
    ∇

    :Section SF APIs
    ⍝ This section contains methods that are suitable results for call-back functions
    ⍝ which want to call SyncFusion API calls. The set of functions is not complete, please
    ⍝ see the Syncfusion documentation for details and feel free to implement additions

    ∇ r←addNode name
      ⍝ Will add a node with id "tv_n", where n starts at 0 and increases each time a node is added
      ⍝ /// NB due to a bug in the Syncfusion widget (August 2015), the "model" is not updated
      :Access Public
      SetId ⋄ r←Execute'$("#',id,'").data("ejTreeView").addNode("',name,'");'
    ∇

    ∇ r←removeNode node
      ⍝ Removes the identified node
      :Access Public
      SetId ⋄ r←Execute'$("#',id,'").data("ejTreeView").removeNode($("#',node,'"));'
    ∇

    ∇ r←checkAll
      ⍝ Check everything below the current node
      :Access Public
      SetId ⋄ r←Execute'$("#',id,'").data("ejTreeView").checkAll();'
    ∇

    ∇ r←checkNode node
      ⍝ Check a node
      :Access Public
      SetId ⋄ r←Execute'$("#',id,'").data("ejTreeView").checkNode($("#',node,'"));'
    ∇

    ∇ r←uncheckAll
      ⍝ Uncheck everything below the current node
      :Access Public
      SetId ⋄ r←Execute'$("#',id,'").data("ejTreeView").uncheckAll();'
    ∇

    ∇ r←uncheckNode node
      ⍝ Uncheck a node
      :Access Public
      SetId ⋄ r←Execute'$("#',id,'").data("ejTreeView").uncheckNode($("#',node,'"));'
    ∇

    ∇ r←expandAll
      ⍝ Expands everything below the current node
      :Access Public
      SetId ⋄ r←Execute'$("#',id,'").data("ejTreeView").expandAll();'
    ∇

    ∇ r←expandNode node
      ⍝ Expands everything below a node
      :Access Public
      SetId ⋄ r←Execute'$("#',id,'").data("ejTreeView").expandNode($("#',node,'"));'
    ∇

    ∇ r←collapseAll
      ⍝ Collapses everything below the current node
      :Access Public
      SetId ⋄ r←Execute'$("#',id,'").data("ejTreeView").collapseAll();'
    ∇

    ∇ r←collapseNode node
      ⍝ Collapses everything below a node
      :Access Public
      SetId ⋄ r←Execute'$("#',id,'").data("ejTreeView").collapseNode($("#',node,'"));'
    ∇

    ∇ r←selectNode node
      ⍝ Select a node
      :Access Public
      SetId ⋄ r←Execute'$("#',id,'").data("ejTreeView").selectNode($("#',node,'"));'
    ∇

    ∇ r←unselectNode node
      ⍝ Unselect a node
      :Access Public
      SetId ⋄ r←Execute'$("#',id,'").data("ejTreeView").unselectNode($("#',node,'"));'
    ∇

    ∇ r←name getModel subsel;model
      ⍝ Passes the TreeView model back as a variable called 'model' as input to a callback
      ⍝ Usage:     aButton.On 'click' 'onButtonClick' ('model' tvinstance.getModel '')
      :Access Public
      SetId ⋄ model←'model',(0≠⍴subsel)/'.',subsel ⍝ "model" or "model.subsel"
      r←(name'eval'('JSON.stringify($("#',id,'").ejTreeView("',model,'"))'))
    ∇

    :EndSection

    :section test
    ∇ r←test;c;m
      :Access public shared
      ⎕SHADOW,∊¨'cm'∘.,⊂¨⍕¨⍳14

      c1←'abc' 'def'                          ⍝
      m1←⍪c1

      c2←('abc' 'def')(1 2)                   ⍝
      m2←⍉↑c2

      c3←('abc' 'def')(1 2)('www' 'www1')     ⍝
      m3←⍉↑c3

      c4←2 1⍴⊂¨'abc' 'def'                    ⍝
      m4←c4

      c5←⍉↑('abc' 'def')(1 2)                 ⍝
      m5←c5

      c6←⍉↑('abc' 'def')(1 2)('www' 'www1')   ⍝
      m6←c6

      c7←('abc' 'def')1
      m7←⍉↑('abc' 'def')(1 1)

      c8←('abc' 'def')1('www' 'www1')
      m8←⍉↑('abc' 'def')(1 1)('www' 'www1')

      c9←('abc' 'def')(1 2)(,⊂'www')
      m9←⍉↑('abc' 'def')(1 2)('www' 'www')

      c10←('abc' 'def')(1 2)'www'
      m10←m9

      c11←('abc' 'def')1(,⊂'www')
      m11←⍉↑('abc' 'def')(1 1)('www' 'www')

      c12←('abc' 'def')1 'www'
      m12←m11

      c13←'abc' 1
      m13←1 2⍴c13

      c14←'abc' 1 'www'
      m14←1 3⍴c14

      r←⍬
      :For c m :InEach ↓∊¨'cm'∘.,⊂¨⍕¨⍳14
          r,←÷(⍎m)≡FixArgs⍎c
      :EndFor
    ∇

  ⍝  eis←{⊂⍣(1=≡⍵)⊢⍵}
    link←{(⊂⍺),eis ⍵}

    ∇ m←FixArgs c
      :If 2=≢⍴c ⍝ matrix
          m←c
          →0
      :EndIf

      :If (1=≢∪⎕DR¨c)∧∧/1≥|≡¨c  ⍝ 'abc' 'def'
          m←⍪,¨c
          →0
      :EndIf

      :If 1=≢∪≢¨c ⍝ ('abc' 'def') (1 2)
          m←⍉↑c
          →0
      :EndIf

      m←↑⊃link¨/eis¨c

    ∇

    :endsection
:EndClass


Commented out Selenium background music

Commented out Selenium background music

:Namespace SeleniumTests

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

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

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

    ∇ r←FindAllFiles root;folders;ext
      :If 0=⎕NC'Config.DefaultExtension' ⋄ ext←'.dyalog'
      :Else ⋄ ext←Config.DefaultExtension
      :EndIf
      r←(⊂root,'/'),¨(('*',ext)#.Files.List root)[;1]
      :If 0≠⍴folders←{(('.'≠⊃¨⍵[;1])∧⍵[;4])/⍵[;1]}#.Files.List root
          r←r,⊃,/FindAllFiles¨(⊂root,'/'),¨folders
      :EndIf
    ∇

    ∇ {stopOnError}Test site;count;ctl;examples;f;fail;nodot;start;t;time;z;i;START;COUNT;FAIL;Config;selpath;files;n;ext;filter;⎕PATH;keynames;maxlen;⎕USING

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

      selpath←(AppRoot↓⍨-3⍳⍨+\'/\'∊⍨⌽AppRoot),'/Selenium/'
      :If 0=⎕NC'Selenium'
          :Trap 0 ⋄ ⎕SE.SALT.Load selpath,'/Selenium'
          :Else ⋄ ⎕←'Unable to load Selenium' ⋄ →0
          :EndTrap
      :EndIf
      ⎕PATH,←' Selenium'
      Selenium.DLLPATH←selpath

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

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

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

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

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

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

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

      Selenium.BROWSER.Quit
    ∇

:EndNamespace

:Namespace SeleniumTests

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

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

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

    ∇ r←FindAllFiles root;folders;ext
      :If 0=⎕NC'Config.DefaultExtension' ⋄ ext←'.dyalog'
      :Else ⋄ ext←Config.DefaultExtension
      :EndIf
      r←(⊂root,'/'),¨(('*',ext)#.Files.List root)[;1]
      :If 0≠⍴folders←{(('.'≠⊃¨⍵[;1])∧⍵[;4])/⍵[;1]}#.Files.List root
          r←r,⊃,/FindAllFiles¨(⊂root,'/'),¨folders
      :EndIf
    ∇

    ∇ {stopOnError}Test site;count;ctl;examples;f;fail;nodot;start;t;time;z;i;START;COUNT;FAIL;Config;selpath;files;n;ext;filter;⎕PATH;keynames;maxlen;⎕USING

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

      selpath←(AppRoot↓⍨-3⍳⍨+\'/\'∊⍨⌽AppRoot),'/Selenium/'
      :If 0=⎕NC'Selenium'
          :Trap 0 ⋄ ⎕SE.SALT.Load selpath,'/Selenium'
          :Else ⋄ ⎕←'Unable to load Selenium' ⋄ →0
          :EndTrap
      :EndIf
      ⎕PATH,←' Selenium'
      Selenium.DLLPATH←selpath

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

      :If site filter≡'MS3' ''
          ⎕CMD('"\Program Files (x86)\Windows Media Player\wmplayer.exe" "',AppRoot,'\Examples\Data\tellintro.mp3"')''
      :EndIf

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

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

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

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

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

      Selenium.BROWSER.Quit
    ∇

:EndNamespace


_DC.Fieldset - insert Legend element, don't add it

:Class Fieldset: #._html.fieldset
⍝ Description:: Same as base HTML5 fieldset, but accepts the legend for the fieldset as an argument
⍝ Constructor:: [legend]
⍝ legend   - legend/heading to display
⍝ Public Fields::
⍝ Legend   - legend/heading to display

    :Field Public Legend←''

    ∇ Make0
      :Access Public
      :Implements constructor
    ∇

    ∇ Make1 args
      :Access Public
      :Implements constructor
      Legend←args
    ∇

    ∇ html←Render
      :Access Public
      Insert _.legend Legend
      html←⎕BASE.Render
    ∇

:EndClass

:Class Fieldset: #._html.fieldset
⍝ Description:: Same as base HTML5 fieldset, but accepts the legend for the fieldset as an argument
⍝ Constructor:: [legend]
⍝ legend   - legend/heading to display
⍝ Public Fields::
⍝ Legend   - legend/heading to display

    :Field Public Legend←''

    ∇ Make0
      :Access Public
      :Implements constructor
    ∇

    ∇ Make1 args
      :Access Public
      :Implements constructor
      Legend←args
    ∇

    ∇ html←Render
      :Access Public

      Add _.legend Legend
      html←⎕BASE.Render
    ∇

:EndClass