Add -nolink to all framework SALT.Loads

Add -nolink to all framework SALT.Loads

:Namespace Boot

    (⎕ML ⎕IO)←1

    :section Startup/Shutdown

    ∇ {msroot}Run root
      :If 0≠⎕NC'msroot' ⋄ MSRoot←msroot ⋄ :EndIf
      AppRoot←folderize root  ⍝ application (website) root
      Load AppRoot ⍝ load essential objects
      ms←Init ConfigureServer AppRoot ⍝ read configuration and create server instance
      Configure ms
      ms.Run
    ∇

    ∇ {msroot}RunWC2 root
      :If 0≠⎕NC'msroot' ⋄ MSRoot←msroot ⋄ :EndIf
      AppRoot←folderize root  ⍝ application (website) root
      Load AppRoot ⍝ load essential objects
      !!!ms←1 Init ConfigureServer AppRoot ⍝ read configuration and create server instance
      Configure ms
      {ms.Run}
    ∇

    ∇ Load AppRoot;filterOut;files;HTML;f;failed;dir;name;file;folder;callingEnv
      ⍝ Load required objects for MiServer

      HtmlRenderer←{0::⍵ ⋄ HtmlRenderer}0

      :If 0=#.⎕NC'Files' ⋄ ⎕SE.SALT.Load MSRoot,'Utils/Files -target=# -nolink' ⋄ :EndIf

      filterOut←{⍺←'' ⋄ ⍺{0∊⍴⍺:⍵ ⋄ ⍺{∊¨↓⍵⌿⍨~⍵[;2]∊eis ⍺}↑⎕NPARTS¨⍵}⊃#.Files.Dir ⍵,'/*.dyalog'}

      files←'Boot'filterOut MSRoot,'Core'
      files,←'Files'filterOut MSRoot,'Utils' ⍝ find utility libraries
      files,←filterOut MSRoot,'Extensions'

      failed←''
      :For f :In files
          {326=⎕DR ⍵: ⋄ '***'≡3↑⍵:failed,←⊂(('<.+>'⎕S{1↓¯1↓⍵.Match})⍵)}⎕SE.SALT.Load f,' -target=# -nolink' ⍝ do not reload already loaded spaces
      :EndFor

      :For file :In failed
          disperror ⎕SE.SALT.Load∊'"',file,'" -target=# -nolink'
      :EndFor

      HTML←'_JQ' '_JS'{⍵[⍋⍺⍳(↑⎕NPARTS¨⍵)[;2]]}filterOut MSRoot,'HTML' ⍝ prioritize loading of _JQ and _JS

      #.SupportedHtml5Elements.Build_html_namespace

      ⍝↓↓↓ Some controls may require controls in other folders.
      ⍝    So we attempt to load everything, and keep track of what failed
      ⍝    and then go back and try to load the failed controls again their
      failed←''
      :For f :In HTML
          (folder name)←2↑⎕NPARTS f
          disperror ⎕SE.SALT.Load f,' -target=# -nolink'
          :If #.Files.DirExists dir←folder,name,'/'
              dir∘{326=⎕DR ⍵: ⋄ '***'≡3↑⍵:failed,←⊂⍺(('<.+>'⎕S{1↓¯1↓⍵.Match})⍵)}¨⎕SE.SALT.Load dir,'* -nolink -target=#.',name
          :EndIf
      :EndFor

      :For (f file) :In failed
          disperror ⎕SE.SALT.Load∊'"',file,'" -nolink -target=#.',f
      :EndFor

      LoadFromFolder MSRoot,'Loadable'

      'Pages'#.⎕NS'' ⍝ Container Space for loaded classes
      #.Pages.(MiPage RESTfulPage)←#.(MiPage RESTfulPage)

      'CachedPages'#.⎕NS'' ⍝ Container for cached pages

      BuildEAWC ⍝ build the Easy As ⎕WC namespace

⍝ Now load any code from the MiSite

      :If ~0∊⍴AppRoot
          :Trap 22
              :For class :In filterOut AppRoot,'Code' ⍝ Classes in application folder
                  disperror ⎕SE.SALT.Load class,' -target=#'
              :EndFor

              :If #.Files.DirExists AppRoot,'/Code/Templates/'
                  disperror ⎕SE.SALT.Load AppRoot,'/Code/Templates/* -target=#.Pages'
              :EndIf
          :EndTrap
      :EndIf
    ∇

    ∇ Cleanup
      #.⎕EX¨classes
      #.⎕EX¨utils
      #.⎕EX'Pages'
      #.⎕EX'CachedPages'
      #.⎕EX¨'MiServer' 'HTTPRequest'
    ∇

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

      HtmlRenderer←{0::⍵ ⋄ HtmlRenderer}0  ⍝ using HTMLRenderer?

      :If 0≠HtmlRenderer
          ms←⎕NS''
          ms.Config←Config
          ms.Log←{⎕←⍵}
      :Else
          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
      :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⍕¨∪∊ ⎕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

      ⎕EX'#.MiPage'
      ⎕EX'AppRoot'
      {}try'#.DRC.Close ''.'''
      ⎕EX'#.DRC'
      #.Conga.UnloadSharedLib
      {}⎕WA
    ∇

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

    ∇ {root}LoadFromFolder path;type;name;nsName;parts;ns
    ⍝ Loads an APL "project" folder
      root←{6::⍵ ⋄ root}#
      :For name type :In ↓{⍵[⍒⍵[;2];]}⍉↑0 1 #.Files.Dir path,'/*'
          nsName←∊1↓parts←1 ⎕NPARTS name
          :If 1=type ⍝ directory?
              :Select ⊃root.⎕NC nsName
              :Case 9 ⋄ ns←⍕root⍎nsName
              :Case 0 ⋄ ns←nsName root.⎕NS''
              :Else ⋄ Log'"',name,'" is not a namespace'
              :EndSelect
              ⎕SE.SALT.Load name,'/* -target=',⍕ns
          :Else
              :If ~∨/∊(⎕NSI,¨'.')⍷⍨¨⊂'.',(2⊃1 ⎕NPARTS name),'.' ⍝ don't load it if we're being called from it
                  ⎕SE.SALT.Load name,' -target=',⍕root
              :EndIf
          :EndIf
      :EndFor
    ∇

    :endsection

    :section Configuration

    ∇ Configure ms
      ConfigureDatasources ms
      ConfigureVirtual ms
      ConfigureResources ms
      ConfigureContentTypes ms
      ms AddConfiguration'MappingHandlers'
    ∇

    ∇ ms AddConfiguration name;conf
      conf←ReadConfiguration name
      {ms.Config⍎name,'←⍵'}conf
    ∇

    ∇ 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.ReadText file)⍎type
      :EndIf
      :If #.Files.Exists file←AppRoot,'Config/',type,'.xml'
          siteconfig←(#.XML.ToNS #.Files.ReadText 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.AllowedHTTPMethods←{⍵⊆⍨~⍵∊' ,'}#.Strings.lc Config Setting'AllowedHTTPMethods' 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.CloseOnCrash←Config Setting'CloseOnCrash' 1 0
      Config.Debug←Config Setting'Debug' 1 0
      Config.DecodeBuffers←Config Setting'DecodeBuffers' 1 1 ⍝ allow Conga to decode HTTP messages (1)
      Config.DefaultExtension←Config Setting'DefaultExtension' 0 '.mipage'
      Config.DefaultPage←Config Setting'DefaultPage' 0 'index.mipage'
      Config.DirectFileSize←{⍵[⍋⍵]}0⌈⌊2↑Config Setting'DirectFileSize'(,1)⍬
      Config.FIFOMode←Config Setting'FIFOMode' 1 1 ⍝ Conga FIFO mode default to on (1)
      Config.FormatHtml←Config Setting'FormatHtml' 1 0
      Config.Host←Config Setting'Host' 0 'localhost'
      Config.HTTPCacheTime←'m'#.Dates.ParseTime Config Setting'HTTPCacheTime' 0 '0' ⍝ default to off (0)
      Config.IdleTimeout←'s'#.Dates.ParseTime Config Setting'IdleTimeout' 0 '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
      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←'m'#.Dates.ParseTime Config Setting'SessionTimeout' 0 '30m' ⍝ 30 minute timeout
      Config.SupportedEncodings←{(⊂'')~⍨1↓¨(⍵=⊃⍵)⊂⍵}',',Config Setting'SupportedEncodings' 0
      Config.TrapErrors←Config Setting'TrapErrors' 1 0
      Config.WaitTimeout←#.Dates.ParseTime Config Setting'WaitTimeout' 0 '5000ms' ⍝ 5000 msec (5 second timeout)
      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←('/\'[1+'Win'≡3↑1⊃#.⎕WG'APLVersion']){⍵,(-⍺=¯1↑⍵)↓⍺}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
          :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↓∊',',¨(~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;resources;files;which
      ⍝ 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↓∊',',¨(~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:',∊' ',¨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[∊(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'
          resources←∪⊃,/,0 1↓ms.Config.Resources
          files←ms.Config Virtual¨resources
          :If ∨/missing←~#.Files.Exists∘{⍵/⍨∧\⍵≠'?'}¨files
              which←(0 1↓ms.Config.Resources)∊¨⊂missing/resources
              mask←∨/∨/¨which
              1 ms.Log'Resource files not found:'
              1 ms.Log¨↓⍕(1,mask⌿which)/¨mask⌿ms.Config.Resources
              (mask⌿ms.Config.Resources)←(mask⌿ms.Config.Resources)~¨⊂missing/resources
          :EndIf
      :EndIf
    ∇

    ∇ file←Config Virtual page;mask;f;ind;t;path;root
      :Access public shared
    ⍝ checks for virtual directory
      root←(-'/\'∊⍨¯1↑root)↓root←Config.Root
      page←('/\'∊⍨1↑page)↓page
      file←root,'/',page
      :If 0<⍴Config.Virtual
          ind←Config.Virtual.alias⍳⊂t←{(¯1+⍵⍳'/')⍴⍵}page
          :If ind≤⍴Config.Virtual.alias
              path←ind⊃Config.Virtual.path
              file←#.Files.Normalize path,('/\'∊⍨¯1↑path)↓(⍴t)↓page
          :EndIf
      :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←{⍵/⍳⍴⍵}∊','∊¨exts
              mask←(⍴exts)⍴1
              exp←{⎕ML←3 ⋄ (⍵≠',')⊂⍵}¨exts[inds]
              mask[inds]←n←⍬∘⍴∘⍴¨exp
              (exts types)←mask∘/¨exts types
              exts[(n/inds)++\~∊n↑¨1]←⊃,/exp
          :EndIf
          ms.Config.ContentTypes←exts,[1.1]types
      :Else
          1 ms.Log'No content types defined?'
      :EndIf
    ∇

    :Class ConfigSpace
        :field public config

        ∇ make filename;ns;n
          :Access public
          :Implements constructor
          ns←#.XML.ToNS #.Files.ReadText filename
          'Config file needs a single root node'⎕SIGNAL 11/⍨1≠⍴n←ns.⎕NL ¯9
          config←ns⍎⊃n
        ∇

        ∇ r←Get args
          :Access public
          r←config Setting args
        ∇

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

        eis←{(,∘⊂)⍣((326∊⎕DR ⍵)<2>|≡⍵),⍵} ⍝ Enclose if simple

          tonum←{⍺←0
              1∊⍺:tonumvec ⍵
              w←⍵ ⋄ ((w='-')/w)←'¯'
              ⊃⊃{~∧/⍺:⎕SIGNAL 11 ⋄ ⍵}/⎕VFI w}

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

    :Endclass

    :endsection

    :section Utilities
    disperror←{326=⎕DR ⍵: ⋄ '***'≡3↑⍵:⎕←⍵}
    isWin←'Win'≡3↑1⊃#.⎕WG'APLVersion'
    fileSep←'/\'[1+isWin]
    isRelPath←{{~'/\'∊⍨(⎕IO+2×isWin∧':'∊⍵)⊃⍵}3↑⍵}
        MSRoot←{('.'=⊃⍵)∨isRelPath ⍵:'.',fileSep,⍵ ⋄ ⍵}{(1-⌊/'/\'⍳⍨⌽⍵)↓⍵}⎕WSID
      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←{{11 19 22::⍵,'/'↓⍨'/\'∊⍨¯1↑⍵ ⋄ ∊1 ⎕NPARTS⊃{⍺,(('/'=¯1↑⍺)<⍵=1)/'/'}/0 1 ⎕NINFO ⍵}∊⍕⍵} ⍝ append trailing file separator unless empty and left arg←1
    makeSitePath←{folderize ⍺{((isRelPath ⍵)/⍺),⍵},(2×'./'≡2↑⍵)↓⍵}
    subdirs←{⊃{(⍵=1)/⍺}/0 1(⎕NINFO⍠1)⍵,'/*'}
    Log←{⎕←⍵}

    ∇ {r}←AutoStatus setting
      ⍝ Set Dyalog/Windows AutoStatus setting
      :If r←isWin
          :Trap 0
              :If setting≠r←⎕SE.mb.tools.status_error.Checked
                  1 ⎕NQ ⎕SE.mb.tools.status_error'Select'
              :EndIf
          :EndTrap
      :EndIf
    ∇

    ∇ 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

    ∇ {msroot}Run root
      :If 0≠⎕NC'msroot' ⋄ MSRoot←msroot ⋄ :EndIf
      AppRoot←folderize root  ⍝ application (website) root
      Load AppRoot ⍝ load essential objects
      ms←Init ConfigureServer AppRoot ⍝ read configuration and create server instance
      Configure ms
      ms.Run
    ∇

    ∇ {msroot}RunWC2 root
      :If 0≠⎕NC'msroot' ⋄ MSRoot←msroot ⋄ :EndIf
      AppRoot←folderize root  ⍝ application (website) root
      Load AppRoot ⍝ load essential objects
      !!!ms←1 Init ConfigureServer AppRoot ⍝ read configuration and create server instance
      Configure ms
      {ms.Run}
    ∇

    ∇ Load AppRoot;filterOut;files;HTML;f;failed;dir;name;file;folder;callingEnv
      ⍝ Load required objects for MiServer

      HtmlRenderer←{0::⍵ ⋄ HtmlRenderer}0

      :If 0=#.⎕NC'Files' ⋄ ⎕SE.SALT.Load MSRoot,'Utils/Files -target=#' ⋄ :EndIf

      filterOut←{⍺←'' ⋄ ⍺{0∊⍴⍺:⍵ ⋄ ⍺{∊¨↓⍵⌿⍨~⍵[;2]∊eis ⍺}↑⎕NPARTS¨⍵}⊃#.Files.Dir ⍵,'/*.dyalog'}

      files←'Boot'filterOut MSRoot,'Core'
      files,←'Files'filterOut MSRoot,'Utils' ⍝ find utility libraries
      files,←filterOut MSRoot,'Extensions'

      failed←''
      :For f :In files
          {326=⎕DR ⍵: ⋄ '***'≡3↑⍵:failed,←⊂(('<.+>'⎕S{1↓¯1↓⍵.Match})⍵)}⎕SE.SALT.Load f,' -target=#' ⍝ do not reload already loaded spaces
      :EndFor

      :For file :In failed
          disperror ⎕SE.SALT.Load∊'"',file,'" -target=#'
      :EndFor

      HTML←'_JQ' '_JS'{⍵[⍋⍺⍳(↑⎕NPARTS¨⍵)[;2]]}filterOut MSRoot,'HTML' ⍝ prioritize loading of _JQ and _JS

      #.SupportedHtml5Elements.Build_html_namespace

      ⍝↓↓↓ Some controls may require controls in other folders.
      ⍝    So we attempt to load everything, and keep track of what failed
      ⍝    and then go back and try to load the failed controls again their
      failed←''
      :For f :In HTML
          (folder name)←2↑⎕NPARTS f
          disperror ⎕SE.SALT.Load f,' -target=#'
          :If #.Files.DirExists dir←folder,name,'/'
              dir∘{326=⎕DR ⍵: ⋄ '***'≡3↑⍵:failed,←⊂⍺(('<.+>'⎕S{1↓¯1↓⍵.Match})⍵)}¨⎕SE.SALT.Load dir,'* -target=#.',name
          :EndIf
      :EndFor

      :For (f file) :In failed
          disperror ⎕SE.SALT.Load∊'"',file,'" -nolink -target=#.',f
      :EndFor

      LoadFromFolder MSRoot,'Loadable'

      'Pages'#.⎕NS'' ⍝ Container Space for loaded classes
      #.Pages.(MiPage RESTfulPage)←#.(MiPage RESTfulPage)

      'CachedPages'#.⎕NS'' ⍝ Container for cached pages

      BuildEAWC ⍝ build the Easy As ⎕WC namespace

⍝ Now load any code from the MiSite

      :If ~0∊⍴AppRoot
          :Trap 22
              :For class :In filterOut AppRoot,'Code' ⍝ Classes in application folder
                  disperror ⎕SE.SALT.Load class,' -target=#'
              :EndFor

              :If #.Files.DirExists AppRoot,'/Code/Templates/'
                  disperror ⎕SE.SALT.Load AppRoot,'/Code/Templates/* -target=#.Pages'
              :EndIf
          :EndTrap
      :EndIf
    ∇

    ∇ Cleanup
      #.⎕EX¨classes
      #.⎕EX¨utils
      #.⎕EX'Pages'
      #.⎕EX'CachedPages'
      #.⎕EX¨'MiServer' 'HTTPRequest'
    ∇

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

      HtmlRenderer←{0::⍵ ⋄ HtmlRenderer}0  ⍝ using HTMLRenderer?

      :If 0≠HtmlRenderer
          ms←⎕NS''
          ms.Config←Config
          ms.Log←{⎕←⍵}
      :Else
          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
      :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⍕¨∪∊ ⎕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

      ⎕EX'#.MiPage'
      ⎕EX'AppRoot'
      {}try'#.DRC.Close ''.'''
      ⎕EX'#.DRC'
      #.Conga.UnloadSharedLib
      {}⎕WA
    ∇

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

    ∇ {root}LoadFromFolder path;type;name;nsName;parts;ns
    ⍝ Loads an APL "project" folder
      root←{6::⍵ ⋄ root}#
      :For name type :In ↓{⍵[⍒⍵[;2];]}⍉↑0 1 #.Files.Dir path,'/*'
          nsName←∊1↓parts←1 ⎕NPARTS name
          :If 1=type ⍝ directory?
              :Select ⊃root.⎕NC nsName
              :Case 9 ⋄ ns←⍕root⍎nsName
              :Case 0 ⋄ ns←nsName root.⎕NS''
              :Else ⋄ Log'"',name,'" is not a namespace'
              :EndSelect
              ⎕SE.SALT.Load name,'/* -target=',⍕ns
          :Else
              :If ~∨/∊(⎕NSI,¨'.')⍷⍨¨⊂'.',(2⊃1 ⎕NPARTS name),'.' ⍝ don't load it if we're being called from it
                  ⎕SE.SALT.Load name,' -target=',⍕root
              :EndIf
          :EndIf
      :EndFor
    ∇

    :endsection

    :section Configuration

    ∇ Configure ms
      ConfigureDatasources ms
      ConfigureVirtual ms
      ConfigureResources ms
      ConfigureContentTypes ms
      ms AddConfiguration'MappingHandlers'
    ∇

    ∇ ms AddConfiguration name;conf
      conf←ReadConfiguration name
      {ms.Config⍎name,'←⍵'}conf
    ∇

    ∇ 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.ReadText file)⍎type
      :EndIf
      :If #.Files.Exists file←AppRoot,'Config/',type,'.xml'
          siteconfig←(#.XML.ToNS #.Files.ReadText 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.AllowedHTTPMethods←{⍵⊆⍨~⍵∊' ,'}#.Strings.lc Config Setting'AllowedHTTPMethods' 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.CloseOnCrash←Config Setting'CloseOnCrash' 1 0
      Config.Debug←Config Setting'Debug' 1 0
      Config.DecodeBuffers←Config Setting'DecodeBuffers' 1 1 ⍝ allow Conga to decode HTTP messages (1)
      Config.DefaultExtension←Config Setting'DefaultExtension' 0 '.mipage'
      Config.DefaultPage←Config Setting'DefaultPage' 0 'index.mipage'
      Config.DirectFileSize←{⍵[⍋⍵]}0⌈⌊2↑Config Setting'DirectFileSize'(,1)⍬
      Config.FIFOMode←Config Setting'FIFOMode' 1 1 ⍝ Conga FIFO mode default to on (1)
      Config.FormatHtml←Config Setting'FormatHtml' 1 0
      Config.Host←Config Setting'Host' 0 'localhost'
      Config.HTTPCacheTime←'m'#.Dates.ParseTime Config Setting'HTTPCacheTime' 0 '0' ⍝ default to off (0)
      Config.IdleTimeout←'s'#.Dates.ParseTime Config Setting'IdleTimeout' 0 '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
      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←'m'#.Dates.ParseTime Config Setting'SessionTimeout' 0 '30m' ⍝ 30 minute timeout
      Config.SupportedEncodings←{(⊂'')~⍨1↓¨(⍵=⊃⍵)⊂⍵}',',Config Setting'SupportedEncodings' 0
      Config.TrapErrors←Config Setting'TrapErrors' 1 0
      Config.WaitTimeout←#.Dates.ParseTime Config Setting'WaitTimeout' 0 '5000ms' ⍝ 5000 msec (5 second timeout)
      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←('/\'[1+'Win'≡3↑1⊃#.⎕WG'APLVersion']){⍵,(-⍺=¯1↑⍵)↓⍺}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
          :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↓∊',',¨(~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;resources;files;which
      ⍝ 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↓∊',',¨(~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:',∊' ',¨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[∊(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'
          resources←∪⊃,/,0 1↓ms.Config.Resources
          files←ms.Config Virtual¨resources
          :If ∨/missing←~#.Files.Exists∘{⍵/⍨∧\⍵≠'?'}¨files
              which←(0 1↓ms.Config.Resources)∊¨⊂missing/resources
              mask←∨/∨/¨which
              1 ms.Log'Resource files not found:'
              1 ms.Log¨↓⍕(1,mask⌿which)/¨mask⌿ms.Config.Resources
              (mask⌿ms.Config.Resources)←(mask⌿ms.Config.Resources)~¨⊂missing/resources
          :EndIf
      :EndIf
    ∇

    ∇ file←Config Virtual page;mask;f;ind;t;path;root
      :Access public shared
    ⍝ checks for virtual directory
      root←(-'/\'∊⍨¯1↑root)↓root←Config.Root
      page←('/\'∊⍨1↑page)↓page
      file←root,'/',page
      :If 0<⍴Config.Virtual
          ind←Config.Virtual.alias⍳⊂t←{(¯1+⍵⍳'/')⍴⍵}page
          :If ind≤⍴Config.Virtual.alias
              path←ind⊃Config.Virtual.path
              file←#.Files.Normalize path,('/\'∊⍨¯1↑path)↓(⍴t)↓page
          :EndIf
      :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←{⍵/⍳⍴⍵}∊','∊¨exts
              mask←(⍴exts)⍴1
              exp←{⎕ML←3 ⋄ (⍵≠',')⊂⍵}¨exts[inds]
              mask[inds]←n←⍬∘⍴∘⍴¨exp
              (exts types)←mask∘/¨exts types
              exts[(n/inds)++\~∊n↑¨1]←⊃,/exp
          :EndIf
          ms.Config.ContentTypes←exts,[1.1]types
      :Else
          1 ms.Log'No content types defined?'
      :EndIf
    ∇

    :Class ConfigSpace
        :field public config

        ∇ make filename;ns;n
          :Access public
          :Implements constructor
          ns←#.XML.ToNS #.Files.ReadText filename
          'Config file needs a single root node'⎕SIGNAL 11/⍨1≠⍴n←ns.⎕NL ¯9
          config←ns⍎⊃n
        ∇

        ∇ r←Get args
          :Access public
          r←config Setting args
        ∇

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

        eis←{(,∘⊂)⍣((326∊⎕DR ⍵)<2>|≡⍵),⍵} ⍝ Enclose if simple

          tonum←{⍺←0
              1∊⍺:tonumvec ⍵
              w←⍵ ⋄ ((w='-')/w)←'¯'
              ⊃⊃{~∧/⍺:⎕SIGNAL 11 ⋄ ⍵}/⎕VFI w}

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

    :Endclass

    :endsection

    :section Utilities
    disperror←{326=⎕DR ⍵: ⋄ '***'≡3↑⍵:⎕←⍵}
    isWin←'Win'≡3↑1⊃#.⎕WG'APLVersion'
    fileSep←'/\'[1+isWin]
    isRelPath←{{~'/\'∊⍨(⎕IO+2×isWin∧':'∊⍵)⊃⍵}3↑⍵}
        MSRoot←{('.'=⊃⍵)∨isRelPath ⍵:'.',fileSep,⍵ ⋄ ⍵}{(1-⌊/'/\'⍳⍨⌽⍵)↓⍵}⎕WSID
      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←{{11 19 22::⍵,'/'↓⍨'/\'∊⍨¯1↑⍵ ⋄ ∊1 ⎕NPARTS⊃{⍺,(('/'=¯1↑⍺)<⍵=1)/'/'}/0 1 ⎕NINFO ⍵}∊⍕⍵} ⍝ append trailing file separator unless empty and left arg←1
    makeSitePath←{folderize ⍺{((isRelPath ⍵)/⍺),⍵},(2×'./'≡2↑⍵)↓⍵}
    subdirs←{⊃{(⍵=1)/⍺}/0 1(⎕NINFO⍠1)⍵,'/*'}
    Log←{⎕←⍵}

    ∇ {r}←AutoStatus setting
      ⍝ Set Dyalog/Windows AutoStatus setting
      :If r←isWin
          :Trap 0
              :If setting≠r←⎕SE.mb.tools.status_error.Checked
                  1 ⎕NQ ⎕SE.mb.tools.status_error'Select'
              :EndIf
          :EndTrap
      :EndIf
    ∇

    ∇ 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


List items have hanging indent

List items have hanging indent

:Class index : MiPageSample

    :SECTION GLOBALS ⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝

    :Field TYPES←'Simple' 'Advanced'⍝ Types of samples (if not App)
    :Field GROUPS      ⍝ Names of groups of elements
    :Field REFS        ⍝ ... their refs
    :Field APPS        ⍝ List of all apps
    :Field SAMPLES     ⍝ List of all samples (well, groups and controls really)
    :Field INFOLONG    ⍝ Long desc of each control
    :Field INFOSHORT   ⍝ Short (1-3 words) desc of each control
    :Field NAMESLONG   ⍝ Elements that have long info
    :Field NAMESSHORT  ⍝ Elements that have short info
    :Field SEARCH←''   ⍝ Last-search cache
    :Field CONTROLS←'' ⍝ All control names
    :Field APPDESCS    ⍝ Sample Apps' Description::
    :Field APPCTRLS    ⍝ Sample Apps' Control:: (i.e. the title)
    :Field CORE        ⍝ Core subset of controls

    :ENDSECTION

    :SECTION UTILITIES ⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝

    Q←'"'∘,,∘'"'  ⍝ Surround with quotes, a.k.a. APL dragon

    D←{(×≢⍵)/' – ',⍵} ⍝ Prepend a dash if non-empty

    P←{1⌽(×≢#.HTMLInput.dtlb ⍵)/') (',⍵} ⍝ Surround with parens if non-empty

    Words←{(1↓¨(' '∘=⊂⊢)' ',⍵)~⊂''} ⍝ Split at spaces

    SpaceToDir←{'Examples/',1↓⍵} ⍝ Convert Space name to Folder name

    Frame←{'.iframed' ('src=',Q⍵,'?NoWrapper=1') New _.iframe}

    NewWinA←{('target' '_blank')('href=',Q⊃⌽⍵) New _.a (⊃⍵)} ⍝ New link that opens in a new window/tab

    NewDiv←{⍵ New _.div} ⍝ Make a div

    Horz←{⍺←⊢ ⋄ r⊣(r←⍺ New _.StackPanel ⍵).Horizontal←1} ⍝ Horizontal StackPanel

    Type←{P(1⍳⍨∨/¨TYPES⍷¨⊂⍺)⊃(TYPES,¨⊂' sample for ',⍵),⊂'Sample app'} ⍝ Simple/Advanced/App?

    Dread←{0::,⊂'[file read failed]' ⋄ #.UnicodeFile.ReadNestedText #.Boot.AppRoot,⍵,'.dyalog'} ⍝ Read dyalog file

    Dlist←{0::⍬ ⋄ ¯7↓¨6⊃#.Files.DirX #.Boot.AppRoot,⍵,'/*.dyalog'} ⍝ List dyalog files

    NodeID←{⍺←⊢ ⋄ ('#node',⊃⍵)∘,¨⍕¨⍺+⍳⍴⊃⌽⍵} ⍝ Generate node ids (⍵='L' items) optional (⍺=offset)

    Split←{{⍵↑⍨+/∨\' '≠⌽⍵}¨('_'⎕R' ')⍵((⍳↑⊣){⍺⍵}(⍳↓⊣))'_'} ⍝ Split at first _ and fix _s

    In←{∨/¨⊃⍷¨/{⎕SE.Dyalog.Utils.lcase¨eis ⍵}¨⍺ ⍵} ⍝ Case-insensitive find

    Names←{1↓¨⍵↑¨⍨¯1+⍵⍳¨':'} ⍝ Extract section names from set of several 'name:: description'

    AddShortInfo←{⍵,¨P¨(4+⍴¨⍵)↓¨(INFOSHORT,⊂'')[NAMESSHORT⍳⍵]} ⍝ ⍵ (ShortDesc)

    AddLongInfo←{(New¨_.strong,¨⍵),¨D¨{⍵,'[no info available]'/⍨0∊⍴⍵}¨(4+⍴¨⍵)↓¨(INFOLONG,⊂'')[NAMESLONG⍳⍵]} ⍝ ⍵ - LongDesc

      Section←{ ⍝ extract section ⍺:: from code ⍵
          regex←'^\s*⍝\s*',⍺,':(:.*?)$((\R^⍝(?!\s*\w+::).*?$)*)'
          opts←('Mode' 'M')('DotAll' 1)
          res←(regex ⎕S'\1\2'⍠opts)⍵
          (1↓⊃res)~'⍝'
      }

      JSsafe←{ ⍝ Make text JS safe by escaping problematic chars
          fr←'''' '"' '\\' '\n' '\r',⎕UCS 9 8 12               ⍝ ' " \ NL CR TB BS FF
          to←'\\''' '\\"' '\\\\' '\\n' '\\r' '\\t' '\\b' '\\f' ⍝ problematic in regex too :-)
          Q(fr ⎕R to⍠'Mode' 'D')⍵ ⍝ Replace problematic chars and sourround with quotes
      }

    ∇ js←APLtoJS vtv;act;data
      :Access public shared
     ⍝ Convert vectors of pseudo JS (generated by Replace, Execute, et al.) to proper JS

      js←''
      :For act data :In eis vtv
          data←JSsafe⊃⌽eis data ⍝ the last or only one
          :Select ⊃eis act      ⍝ the first or only one
          :Case 'assign'
              js,←(⊃⌽act),' = ',data,';' ⍝ variable = "data"
          :Case 'execute'
              js,←'eval(',data,');' ⍝ eval("data")
          :Case 'replace'
              js,←'$("',(⊃⌽act),'").html(',data,');' ⍝ $("#myid").html("data")
          :Else
              js,←'$("',(⊃⌽act),'").',(⊃act),'(',data,');' ⍝ $("#myid").append("data")
          :EndSelect
      :EndFor
    ∇

    :ENDSECTION

    :SECTION MAIN ⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝

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

     ⍝ Initialize globals
      SAMPLES←0 3⍴⍬
      GROUPS←'Dyalog Controls' 'Base HTML' 'SyncFusion' 'JQueryUI'
      REFS←_DC _html _SF _JQ
      INFOSHORT←'⍝',¨Dread'Examples/Data/infoShort'
      INFOLONG←'⍝',¨Dread'Examples/Data/infoLong'
      NAMESSHORT←Names INFOSHORT
      NAMESLONG←Names INFOLONG

      '#title'Add _.title'MS3: About' ⍝ After the : will be updated

      Add _.StyleSheet'/Styles/homepage.css'
      style←''
      style,←'#leftBar {background-color:inherit;margin-right:6px;} '
      style,←'.menu {padding-left,padding-right:0;} '
      style,←'.cat {font-size:12pt;cursor:pointer;border:1px solid transparent;padding:0 4px 2px 4px;} '
      style,←'.cat:hover {background:linear-gradient(to bottom,#ffbb60 0%,#f37603 100%);border:1px solid #f9cb59;font-weight:bold;} '
      style,←'.menuitem {margin-bottom,margin-left:0px;padding-left:2px;cursor:pointer;} '
      style,←'.menuitem:hover {background-color:highlight;} '
      style,←'.framed {width:730px;max-height:600px;min-height:400px;border-left:1px inset;overflow-y:auto;} '
      style,←'.iframed {width:730px;max-height:600px;min-height:400px;border:2px inset;overflow-y:auto;background-color:white;} '
      style,←'.listitem {margin:0px;padding:4px 4px 4px 26px;text-indent:-22px;cursor:pointer;box-sizing:border-box;} '
      style,←'.listitem:before {content:"▶";padding: 1px 3px 3px 4px;'
      style,←'    background: ThreeDFace;'
      style,←'    color: ButtonText;'
      style,←'    border-radius: 4px;'
      style,←'    border: solid 1px ThreeDLightShadow;'
      style,←'    box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.4), 0 1px 1px rgba(0, 0, 0, 0.2);'
      style,←'    transition-duration: 0.2s;'
      style,←'    margin-right: 4px;'
      style,←'}'
      style,←'.listitem:active:before {'
      style,←'    box-shadow: inset 0 1px 4px rgba(0, 0, 0, 0.6);'
      style,←'    background: ThreeDShadow;'
      style,←'    border: solid 1px ThreeDDarkShadow;'
      style,←'}'
      style,←'.listitem:hover:before {'
      style,←'    background: ThreeDHighlight;'
      style,←'    border: solid 1px ThreeDFace;'
      style,←'    text-decoration: none;'
      style,←'}'
      style,←'.noitems {margin:0px;padding:4px;cursor:not-allowed;} '
      style,←'.samplesource {overflow-x:auto;width:730px;background-color:#e5e5cc;border:2px inset;} '
      Add _.style style

      (left mid)←NewDiv¨'#leftBar' '#midBar' ⍝ Create panes
      sp←Add'mainSP'Horz left mid

      sp.Items[1].style←⊂'width: 200px; max-height: 450px;'
      sp.Items[2].style←⊂'margin: 5px;'
      sp.style←'height: 450px;background-color:inherit;'

      PopulateLeft left
      PopulateMid mid
    ∇

    ∇ PopulateLeft thediv;class;depths;group;items;ref;samples;vp;search;style;menu;i;fs;ac;text;stuff;treeall;treecore;names;tree

      stuff←''
      tree←0 3⍴0 '' ''

         ⍝ SEARCH FIELD ⍝⍝⍝
      stuff,←New _.EditField'searchfield'⍝).On'change' 'OnSearch'('str' 'val')
      (stuff,←'#searchbutton' '.menu'New _.button'Search').On'click' 'OnSearch'('str' '#searchfield' 'val')

      thediv.Add Horz stuff
      thediv.Add _.hr

      ⍝ SAMPLE APPS ⍝⍝⍝
      APPS←(Dlist'Examples/Apps')~⊂'index'
      APPDESCS←(⊂'Description')Section¨Dread¨'Examples/Apps/'∘,¨APPS
      APPCTRLS←(⊂'Control')Section¨Dread¨'Examples/Apps/'∘,¨APPS
      ('.cat'thediv.Add _.span'Sample Apps').On'click' 'OnAppHeader'
      APPS{tree⍪←1 ⍵('nodeA',⍺)}¨APPCTRLS
      tree←'#treeA'thediv.Add _.ejTreeView tree
      tree.On'nodeSelect' 'OnApp'('node' 'eval' 'argument.id')

      thediv.Add _.hr

      ('#node01All' '.cat'thediv.Add _.span'Controls').On'click' 'OnTree'
      CORE←⎕SE.SALT.Load #.Boot.AppRoot,'Examples/Data/core -noname'
      treecore←1 3⍴1 'Core' 'node0_Core'
      treeall←1 3⍴1 'All' 'node00All'

      :For group class :InEach GROUPS(3↓¨⍕¨REFS) ⍝ Remove leading #._
          names←⎕THIS.⎕SE.SALT.Load #.Boot.AppRoot,'Examples/Data/tree',class,' -noname'
          CONTROLS,←⊂names[;2]/⍨(⊢=⌈/)names[;1]
          ⍎class,'←names'

          :If ∨/names[;2]∊CORE
              treecore⍪←2(group,P'_',class)('node__',class)
              treecore⍪←3,0 1↓names⌿⍨names[;2]∊CORE
          :EndIf

          treeall⍪←2(group,P'_',class)('node_',class)
          treeall⍪←⍎class

      :EndFor

      tree←'#treeC' 'style="max-height:260px"'thediv.Add _.ejTreeView,⊂treecore⍪treeall
      tree.On'nodeSelect' 'OnTree'('node' 'eval' 'argument.id')
    ∇

    ∇ PopulateMid mid;url;code;frame;mya

     ⍝ Read framed pages
      url←'Examples/Apps/About'
      code←Dread url
     ⍝ Create and fill placeholder for title header
      mya←('#SampleTitle'mid.Add _.h2).Add NewWinA('Control'Section code)url
     ⍝ Create and fill placeholder for description line
      '#SampleDesc'mid.Add _.p('Description'Section code)
     ⍝ Create and fill placeholder for embedded page
      frame←mid.Add NewDiv'#SampleFrame'
      frame.Add Frame url
      '#SampleSource' '.samplesource'mid.Add _.div⍝,⊂'x-small;border:none'#.HTMLInput.APLToHTMLColour code ⍝ No source on page load
    ∇

    :ENDSECTION

    :SECTION CALLBACKS ⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝

    ∇ r←OnTree;node;descs;out;items;spacectrl;control;spacedir;files;file;i;url;code;ctrlsec;desc;item;title;currctrls;core;names;class;group;cat
      :Access Public
      node←GetNode
      :If '0'∊node ⍝ 'All'
          core←'_'=2⊃node
          currctrls←(CORE∘∩⍣core)⊃,/CONTROLS ⍝ Filter if core-only
          out←NewDiv'.framed'
          descs←AddLongInfo currctrls
          items←(currctrls,⍨¨⊂'list',1⌽core↓node)out.Add¨_.p,¨descs ⍝ make IDs like 'DC_Button'
          items.Set⊂'.listitem'
          items.On⊂'click' 'OnTree'
          r←(2↓node)(2↓node,' Controls')((⍕⍴currctrls),(core/' core'),' controls')out''
      :ElseIf '_'=⊃node ⍝ Class or core subset thereof
          core←'_'=2⊃node
          node↓⍨←core ⍝ Remove additional _ from '__html' if core-only
          i←REFS⍳⍎node
          currctrls←(CORE∘∩⍣core)i⊃CONTROLS ⍝ Filter if core-only
          out←NewDiv'.framed'
          descs←AddLongInfo currctrls
          items←(currctrls,⍨¨⊂'list',1⌽node)out.Add¨_.p,¨descs ⍝ make IDs like 'DC_Button'
          items.Set⊂'.listitem'
          items.On⊂'click' 'OnTree'
          r←node((i⊃GROUPS),P node)((⍕⍴currctrls),(core/' core'),' controls')out''
      :ElseIf '_'=⊃⌽node ⍝ Category
          (class cat)←Split node
          names←⍎class
          i←(names[;3]/⍨(⊢=⌊/)names[;1])⍳⊂'node',node
          currctrls←1↓i⊃names[;2]⊂⍨(⊢=⌊/)names[;1]
          group←GROUPS⊃⍨REFS⍳⍎'_',class
          out←NewDiv'.framed'
          descs←AddLongInfo currctrls
          items←(currctrls,⍨¨⊂'list',class)out.Add¨_.p,¨descs ⍝ make IDs like 'DC_Button'
          items.Set⊂'.listitem'
          items.On⊂'click' 'OnTree'
          r←('_',class)(group,(P'_',class),' ',cat)(' controls',⍨⍕⍴currctrls)out''
      :Else ⍝ Control
          spacectrl←'_',('_'⎕R'.')node
          (class control)←Split node
          spacedir←'Examples/',class
          files←Dlist spacedir
          out←NewDiv'.framed'
          CURRFILES←''
          i←0
          :For file :In files
              url←spacedir,'/',file
              code←Dread url
              ctrlsec←Words'Control'Section code
              :If (⊂spacectrl)∊ctrlsec ⍝ Extract space-separated words
                  i+←1
                  CURRFILES,←⊂url
                  desc←⊂'Description'Section code
                  desc←∊desc,file Type⊃ctrlsec
                  item←('#nodeS',⍕i)'.listitem'out.Add _.p desc
                  item.On'click' 'OnSample'
              :EndIf
          :EndFor
          :If ~×i
              '.noitems'out.Add _.p,⊂'[no samples using ',spacectrl,']'
          :EndIf
          title←(⍕≢CURRFILES),' results for ',spacectrl
          desc←'Click on a button below to view it embedded in this page or double-click to see it as a stand-alone page.'
          r←spacectrl title desc out''
      :EndIf
      r←GenJS r
    ∇

    ∇ r←OnAppHeader;descs;out;items
      :Access Public
     ⍝ Gets called upon clicking the "Sample Apps" header
      descs←APPCTRLS,¨' – '∘,¨APPDESCS
      out←NewDiv'.framed'
      items←APPS{('nodeB',⍺)out.Add _.p ⍵}¨descs
      items.Set⊂'.listitem'
      items.On⊂'click' 'OnApp'
      r←GenJS'Apps' 'Small applications to showcase MiServer 3.0 functionality'(' apps',⍨≢APPS)out''
    ∇

    ∇ r←OnCtrlHeader;descs;out;items
      :Access Public
     ⍝ Gets called upon clicking the "Sample Apps" header
      r←GenJS'Controls' 'Something about controls' 'Controls' 'list of all controls with long descs' ''
    ∇

    ∇ r←OnApp
      :Access Public
     ⍝ Gets called upon selection in Sample Apps tree
      r←GenJS Update'Examples/Apps/',1↓GetNode
    ∇

    ∇ r←OnSample
      :Access Public
      :Select _event
      :Case 'click'
          r←GenJS Update CURRFILES⊃⍨⍎5↓_what ⍝ id="nodeS23"
      :Case 'dblclick'
          r←Execute'window.location.href=',Q CURRFILES⊃⍨⍎5↓_what ⍝ id="nodeS23"
      :EndSelect
    ∇

    ∇ r←OnSearch;str;terms;out;i;dir;files;file;url;code;ctrlsec;desc;item;title
      :Access Public
      :If ×≢str←Get'str'  ⍝ get search string
      ⍝⍝⍝ Controls
          terms←INFOSHORT/⍨str In INFOSHORT
          terms,←terms~⍨INFOLONG/⍨str In INFOLONG
          terms←'.',¨∪Names terms
      ⍝⍝⍝ Samples
          out←NewDiv'.framed'
          CURRFILES←''
          i←0
          :For dir :In 'Apps' 'html' 'HTMLplus' 'DC' 'JQ' 'SF'
              files←Dlist'Examples/',dir
              :For file :In files~⊂'index'
                  url←'Examples/',dir,'/',file
                  code←Dread url
                  ctrlsec←'Control'Section code
                  desc←⊂'Description'Section code
                  :If ∨/str In desc
                  :OrIf ∨/∊(terms,⊂str)In¨⊂ctrlsec ⍝ always case sensitive
                      i+←1
                      CURRFILES,←⊂url
                      desc←∊desc,file Type⊃Words ctrlsec
                      item←('#nodeS',⍕i)'.listitem'out.Add _.p desc
                      item.On'click' 'OnSample'
                  :EndIf
              :EndFor
          :EndFor
          :If ~×i
              '.noitems'out.Add _.p,⊂'[no results for ',str,']'
          :EndIf
          title←(⍕≢CURRFILES),' results for ',Q str
          desc←'Click on a button below to view it embedded in this page or double-click to see it as a stand-alone page.'
          SEARCH←(Q str)title desc out''
      :EndIf
      r←GenJS SEARCH

    ∇

    ∇ (page title desc iframe code)←Update url;ctrlsec
     ⍝ Create new placeholder values
      page←'Sample'
      iframe←Frame url
      code←Dread url
      ctrlsec←'Control'Section code
      desc←'Description'Section code
      title←NewWinA ctrlsec url

    ∇

    ∇ r←GenJS(page title desc iframe code)
     ⍝ Generate JavaScript for filling placeholders
      r←'#title'Replace'MS3: ',page
      r,←'#SampleTitle'Replace title
      r,←'#SampleDesc'Replace desc
      r,←'#SampleFrame'Replace iframe
      r,←'#SampleSource'Replace(×≢code)/'x-small;border:none'#.HTMLInput.APLToHTMLColour code
    ∇

    ∇ node←GetNode
      node←4↓(1+'tree'≡4↑_what)⊃_what(Get'node')
    ∇
    :ENDSECTION

:EndClass

:Class index : MiPageSample

    :SECTION GLOBALS ⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝

    :Field TYPES←'Simple' 'Advanced'⍝ Types of samples (if not App)
    :Field GROUPS      ⍝ Names of groups of elements
    :Field REFS        ⍝ ... their refs
    :Field APPS        ⍝ List of all apps
    :Field SAMPLES     ⍝ List of all samples (well, groups and controls really)
    :Field INFOLONG    ⍝ Long desc of each control
    :Field INFOSHORT   ⍝ Short (1-3 words) desc of each control
    :Field NAMESLONG   ⍝ Elements that have long info
    :Field NAMESSHORT  ⍝ Elements that have short info
    :Field SEARCH←''   ⍝ Last-search cache
    :Field CONTROLS←'' ⍝ All control names
    :Field APPDESCS    ⍝ Sample Apps' Description::
    :Field APPCTRLS    ⍝ Sample Apps' Control:: (i.e. the title)
    :Field CORE        ⍝ Core subset of controls

    :ENDSECTION

    :SECTION UTILITIES ⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝

    Q←'"'∘,,∘'"'  ⍝ Surround with quotes, a.k.a. APL dragon

    D←{(×≢⍵)/' – ',⍵} ⍝ Prepend a dash if non-empty

    P←{1⌽(×≢#.HTMLInput.dtlb ⍵)/') (',⍵} ⍝ Surround with parens if non-empty

    Words←{(1↓¨(' '∘=⊂⊢)' ',⍵)~⊂''} ⍝ Split at spaces

    SpaceToDir←{'Examples/',1↓⍵} ⍝ Convert Space name to Folder name

    Frame←{'.iframed' ('src=',Q⍵,'?NoWrapper=1') New _.iframe}

    NewWinA←{('target' '_blank')('href=',Q⊃⌽⍵) New _.a (⊃⍵)} ⍝ New link that opens in a new window/tab

    NewDiv←{⍵ New _.div} ⍝ Make a div

    Horz←{⍺←⊢ ⋄ r⊣(r←⍺ New _.StackPanel ⍵).Horizontal←1} ⍝ Horizontal StackPanel

    Type←{P(1⍳⍨∨/¨TYPES⍷¨⊂⍺)⊃(TYPES,¨⊂' sample for ',⍵),⊂'Sample app'} ⍝ Simple/Advanced/App?

    Dread←{0::,⊂'[file read failed]' ⋄ #.UnicodeFile.ReadNestedText #.Boot.AppRoot,⍵,'.dyalog'} ⍝ Read dyalog file

    Dlist←{0::⍬ ⋄ ¯7↓¨6⊃#.Files.DirX #.Boot.AppRoot,⍵,'/*.dyalog'} ⍝ List dyalog files

    NodeID←{⍺←⊢ ⋄ ('#node',⊃⍵)∘,¨⍕¨⍺+⍳⍴⊃⌽⍵} ⍝ Generate node ids (⍵='L' items) optional (⍺=offset)

    Split←{{⍵↑⍨+/∨\' '≠⌽⍵}¨('_'⎕R' ')⍵((⍳↑⊣){⍺⍵}(⍳↓⊣))'_'} ⍝ Split at first _ and fix _s

    In←{∨/¨⊃⍷¨/{⎕SE.Dyalog.Utils.lcase¨eis ⍵}¨⍺ ⍵} ⍝ Case-insensitive find

    Names←{1↓¨⍵↑¨⍨¯1+⍵⍳¨':'} ⍝ Extract section names from set of several 'name:: description'

    AddShortInfo←{⍵,¨P¨(4+⍴¨⍵)↓¨(INFOSHORT,⊂'')[NAMESSHORT⍳⍵]} ⍝ ⍵ (ShortDesc)

    AddLongInfo←{(New¨_.strong,¨⍵),¨D¨{⍵,'[no info available]'/⍨0∊⍴⍵}¨(4+⍴¨⍵)↓¨(INFOLONG,⊂'')[NAMESLONG⍳⍵]} ⍝ ⍵ - LongDesc

      Section←{ ⍝ extract section ⍺:: from code ⍵
          regex←'^\s*⍝\s*',⍺,':(:.*?)$((\R^⍝(?!\s*\w+::).*?$)*)'
          opts←('Mode' 'M')('DotAll' 1)
          res←(regex ⎕S'\1\2'⍠opts)⍵
          (1↓⊃res)~'⍝'
      }

      JSsafe←{ ⍝ Make text JS safe by escaping problematic chars
          fr←'''' '"' '\\' '\n' '\r',⎕UCS 9 8 12               ⍝ ' " \ NL CR TB BS FF
          to←'\\''' '\\"' '\\\\' '\\n' '\\r' '\\t' '\\b' '\\f' ⍝ problematic in regex too :-)
          Q(fr ⎕R to⍠'Mode' 'D')⍵ ⍝ Replace problematic chars and sourround with quotes
      }

    ∇ js←APLtoJS vtv;act;data
      :Access public shared
     ⍝ Convert vectors of pseudo JS (generated by Replace, Execute, et al.) to proper JS

      js←''
      :For act data :In eis vtv
          data←JSsafe⊃⌽eis data ⍝ the last or only one
          :Select ⊃eis act      ⍝ the first or only one
          :Case 'assign'
              js,←(⊃⌽act),' = ',data,';' ⍝ variable = "data"
          :Case 'execute'
              js,←'eval(',data,');' ⍝ eval("data")
          :Case 'replace'
              js,←'$("',(⊃⌽act),'").html(',data,');' ⍝ $("#myid").html("data")
          :Else
              js,←'$("',(⊃⌽act),'").',(⊃act),'(',data,');' ⍝ $("#myid").append("data")
          :EndSelect
      :EndFor
    ∇

    :ENDSECTION

    :SECTION MAIN ⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝

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

     ⍝ Initialize globals
      SAMPLES←0 3⍴⍬
      GROUPS←'Dyalog Controls' 'Base HTML' 'SyncFusion' 'JQueryUI'
      REFS←_DC _html _SF _JQ
      INFOSHORT←'⍝',¨Dread'Examples/Data/infoShort'
      INFOLONG←'⍝',¨Dread'Examples/Data/infoLong'
      NAMESSHORT←Names INFOSHORT
      NAMESLONG←Names INFOLONG

      '#title'Add _.title'MS3: About' ⍝ After the : will be updated

      Add _.StyleSheet'/Styles/homepage.css'
      style←''
      style,←'#leftBar {background-color:inherit;margin-right:6px;} '
      style,←'.menu {padding-left,padding-right:0;} '
      style,←'.cat {font-size:12pt;cursor:pointer;border:1px solid transparent;padding:0 4px 2px 4px;} '
      style,←'.cat:hover {background:linear-gradient(to bottom,#ffbb60 0%,#f37603 100%);border:1px solid #f9cb59;font-weight:bold;} '
      style,←'.menuitem {margin-bottom,margin-left:0px;padding-left:2px;cursor:pointer;} '
      style,←'.menuitem:hover {background-color:highlight;} '
      style,←'.framed {width:730px;max-height:600px;min-height:400px;border-left:1px inset;overflow-y:auto;} '
      style,←'.iframed {width:730px;max-height:600px;min-height:400px;border:2px inset;overflow-y:auto;background-color:white;} '
      style,←'.listitem {margin:0px;padding:4px;cursor:pointer;box-sizing:border-box;} '
      style,←'.listitem:before {content:"▶";padding: 1px 3px 3px 4px;'
      style,←'    background: ThreeDFace;'
      style,←'    color: ButtonText;'
      style,←'    border-radius: 4px;'
      style,←'    border: solid 1px ThreeDLightShadow;'
      style,←'    box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.4), 0 1px 1px rgba(0, 0, 0, 0.2);'
      style,←'    transition-duration: 0.2s;'
      style,←'    margin-right: 4px;'
      style,←'}'
      style,←'.listitem:active:before {'
      style,←'    box-shadow: inset 0 1px 4px rgba(0, 0, 0, 0.6);'
      style,←'    background: ThreeDShadow;'
      style,←'    border: solid 1px ThreeDDarkShadow;'
      style,←'}'
      style,←'.listitem:hover:before {'
      style,←'    background: ThreeDHighlight;'
      style,←'    border: solid 1px ThreeDFace;'
      style,←'    text-decoration: none;'
      style,←'}'
      style,←'.noitems {margin:0px;padding:4px;cursor:not-allowed;} '
      style,←'.samplesource {overflow-x:auto;width:730px;background-color:#e5e5cc;border:2px inset;} '
      Add _.style style

      (left mid)←NewDiv¨'#leftBar' '#midBar' ⍝ Create panes
      sp←Add'mainSP'Horz left mid

      sp.Items[1].style←⊂'width: 200px; max-height: 450px;'
      sp.Items[2].style←⊂'margin: 5px;'
      sp.style←'height: 450px;background-color:inherit;'

      PopulateLeft left
      PopulateMid mid
    ∇

    ∇ PopulateLeft thediv;class;depths;group;items;ref;samples;vp;search;style;menu;i;fs;ac;text;stuff;treeall;treecore;names;tree

      stuff←''
      tree←0 3⍴0 '' ''

         ⍝ SEARCH FIELD ⍝⍝⍝
      stuff,←New _.EditField'searchfield'⍝).On'change' 'OnSearch'('str' 'val')
      (stuff,←'#searchbutton' '.menu'New _.button'Search').On'click' 'OnSearch'('str' '#searchfield' 'val')

      thediv.Add Horz stuff
      thediv.Add _.hr

      ⍝ SAMPLE APPS ⍝⍝⍝
      APPS←(Dlist'Examples/Apps')~⊂'index'
      APPDESCS←(⊂'Description')Section¨Dread¨'Examples/Apps/'∘,¨APPS
      APPCTRLS←(⊂'Control')Section¨Dread¨'Examples/Apps/'∘,¨APPS
      ('.cat'thediv.Add _.span'Sample Apps').On'click' 'OnAppHeader'
      APPS{tree⍪←1 ⍵('nodeA',⍺)}¨APPCTRLS
      tree←'#treeA'thediv.Add _.ejTreeView tree
      tree.On'nodeSelect' 'OnApp'('node' 'eval' 'argument.id')

      thediv.Add _.hr

      ('#node01All' '.cat'thediv.Add _.span'Controls').On'click' 'OnTree'
      CORE←⎕SE.SALT.Load #.Boot.AppRoot,'Examples/Data/core -noname'
      treecore←1 3⍴1 'Core' 'node0_Core'
      treeall←1 3⍴1 'All' 'node00All'

      :For group class :InEach GROUPS(3↓¨⍕¨REFS) ⍝ Remove leading #._
          names←⎕THIS.⎕SE.SALT.Load #.Boot.AppRoot,'Examples/Data/tree',class,' -noname'
          CONTROLS,←⊂names[;2]/⍨(⊢=⌈/)names[;1]
          ⍎class,'←names'

          :If ∨/names[;2]∊CORE
              treecore⍪←2(group,P'_',class)('node__',class)
              treecore⍪←3,0 1↓names⌿⍨names[;2]∊CORE
          :EndIf

          treeall⍪←2(group,P'_',class)('node_',class)
          treeall⍪←⍎class

      :EndFor

      tree←'#treeC' 'style="max-height:260px"'thediv.Add _.ejTreeView,⊂treecore⍪treeall
      tree.On'nodeSelect' 'OnTree'('node' 'eval' 'argument.id')
    ∇

    ∇ PopulateMid mid;url;code;frame;mya

     ⍝ Read framed pages
      url←'Examples/Apps/About'
      code←Dread url
     ⍝ Create and fill placeholder for title header
      mya←('#SampleTitle'mid.Add _.h2).Add NewWinA('Control'Section code)url
     ⍝ Create and fill placeholder for description line
      '#SampleDesc'mid.Add _.p('Description'Section code)
     ⍝ Create and fill placeholder for embedded page
      frame←mid.Add NewDiv'#SampleFrame'
      frame.Add Frame url
      '#SampleSource' '.samplesource'mid.Add _.div⍝,⊂'x-small;border:none'#.HTMLInput.APLToHTMLColour code ⍝ No source on page load
    ∇

    :ENDSECTION

    :SECTION CALLBACKS ⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝

    ∇ r←OnTree;node;descs;out;items;spacectrl;control;spacedir;files;file;i;url;code;ctrlsec;desc;item;title;currctrls;core;names;class;group;cat
      :Access Public
      node←GetNode
      :If '0'∊node ⍝ 'All'
          core←'_'=2⊃node
          currctrls←(CORE∘∩⍣core)⊃,/CONTROLS ⍝ Filter if core-only
          out←NewDiv'.framed'
          descs←AddLongInfo currctrls
          items←(currctrls,⍨¨⊂'list',1⌽node)out.Add¨_.p,¨descs ⍝ make IDs like 'DC_Button'
          items.Set⊂'.listitem'
          items.On⊂'click' 'OnTree'
          r←(2↓node)(2↓node,' Controls')((⍕⍴currctrls),(core/' core'),' controls')out''
      :ElseIf '_'=⊃node ⍝ Class or core subset thereof
          core←'_'=2⊃node
          node↓⍨←core ⍝ Remove additional _ from '__html' if core-only
          i←REFS⍳⍎node
          currctrls←(CORE∘∩⍣core)i⊃CONTROLS ⍝ Filter if core-only
          out←NewDiv'.framed'
          descs←AddLongInfo currctrls
          items←(currctrls,⍨¨⊂'list',1⌽node)out.Add¨_.p,¨descs ⍝ make IDs like 'DC_Button'
          items.Set⊂'.listitem'
          items.On⊂'click' 'OnTree'
          r←node((i⊃GROUPS),P node)((⍕⍴currctrls),(core/' core'),' controls')out''
      :ElseIf '_'=⊃⌽node ⍝ Category
          (class cat)←Split node
          names←⍎class
          i←(names[;3]/⍨(⊢=⌊/)names[;1])⍳⊂'node',node
          currctrls←1↓i⊃names[;2]⊂⍨(⊢=⌊/)names[;1]
          group←GROUPS⊃⍨REFS⍳⍎'_',class
          out←NewDiv'.framed'
          descs←AddLongInfo currctrls
          items←(currctrls,⍨¨⊂'list',class)out.Add¨_.p,¨descs ⍝ make IDs like 'DC_Button'
          items.Set⊂'.listitem'
          items.On⊂'click' 'OnTree'
          r←('_',class)(group,(P'_',class),' ',cat)(' controls',⍨⍕⍴currctrls)out''
      :Else ⍝ Control
          spacectrl←'_',('_'⎕R'.')node
          (class control)←Split node
          spacedir←'Examples/',class
          files←Dlist spacedir
          out←NewDiv'.framed'
          CURRFILES←''
          i←0
          :For file :In files
              url←spacedir,'/',file
              code←Dread url
              ctrlsec←Words'Control'Section code
              :If (⊂spacectrl)∊ctrlsec ⍝ Extract space-separated words
                  i+←1
                  CURRFILES,←⊂url
                  desc←⊂'Description'Section code
                  desc←∊desc,file Type⊃ctrlsec
                  item←('#nodeS',⍕i)'.listitem'out.Add _.p desc
                  item.On'click' 'OnSample'
              :EndIf
          :EndFor
          :If ~×i
              '.noitems'out.Add _.p,⊂'[no samples using ',spacectrl,']'
          :EndIf
          title←(⍕≢CURRFILES),' results for ',spacectrl
          desc←'Click on a button below to view it embedded in this page or double-click to see it as a stand-alone page.'
          r←spacectrl title desc out''
      :EndIf
      r←GenJS r
    ∇

    ∇ r←OnAppHeader;descs;out;items
      :Access Public
     ⍝ Gets called upon clicking the "Sample Apps" header
      descs←APPCTRLS,¨' – '∘,¨APPDESCS
      out←NewDiv'.framed'
      items←APPS{('nodeB',⍺)out.Add _.p ⍵}¨descs
      items.Set⊂'.listitem'
      items.On⊂'click' 'OnApp'
      r←GenJS'Apps' 'Small applications to showcase MiServer 3.0 functionality'(' apps',⍨≢APPS)out''
    ∇

    ∇ r←OnCtrlHeader;descs;out;items
      :Access Public
     ⍝ Gets called upon clicking the "Sample Apps" header
      r←GenJS'Controls' 'Something about controls' 'Controls' 'list of all controls with long descs' ''
    ∇

    ∇ r←OnApp
      :Access Public
     ⍝ Gets called upon selection in Sample Apps tree
      r←GenJS Update'Examples/Apps/',1↓GetNode
    ∇

    ∇ r←OnSample
      :Access Public
      :Select _event
      :Case 'click'
          r←GenJS Update CURRFILES⊃⍨⍎5↓_what ⍝ id="nodeS23"
      :Case 'dblclick'
          r←Execute'window.location.href=',Q CURRFILES⊃⍨⍎5↓_what ⍝ id="nodeS23"
      :EndSelect
    ∇

    ∇ r←OnSearch;str;terms;out;i;dir;files;file;url;code;ctrlsec;desc;item;title
      :Access Public
      :If ×≢str←Get'str'  ⍝ get search string
      ⍝⍝⍝ Controls
          terms←INFOSHORT/⍨str In INFOSHORT
          terms,←terms~⍨INFOLONG/⍨str In INFOLONG
          terms←'.',¨∪Names terms
      ⍝⍝⍝ Samples
          out←NewDiv'.framed'
          CURRFILES←''
          i←0
          :For dir :In 'Apps' 'html' 'HTMLplus' 'DC' 'JQ' 'SF'
              files←Dlist'Examples/',dir
              :For file :In files~⊂'index'
                  url←'Examples/',dir,'/',file
                  code←Dread url
                  ctrlsec←'Control'Section code
                  desc←⊂'Description'Section code
                  :If ∨/str In desc
                  :OrIf ∨/∊(terms,⊂str)In¨⊂ctrlsec ⍝ always case sensitive
                      i+←1
                      CURRFILES,←⊂url
                      desc←∊desc,file Type⊃Words ctrlsec
                      item←('#nodeS',⍕i)'.listitem'out.Add _.p desc
                      item.On'click dblclick' 'OnSample'
                  :EndIf
              :EndFor
          :EndFor
          :If ~×i
              '.noitems'out.Add _.p,⊂'[no results for ',str,']'
          :EndIf
          title←(⍕≢CURRFILES),' results for ',Q str
          desc←'Click on a button below to view it embedded in this page or double-click to see it as a stand-alone page.'
          SEARCH←(Q str)title desc out''
      :EndIf
      r←GenJS SEARCH

    ∇

    ∇ (page title desc iframe code)←Update url;ctrlsec
     ⍝ Create new placeholder values
      page←'Sample'
      iframe←Frame url
      code←Dread url
      ctrlsec←'Control'Section code
      desc←'Description'Section code
      title←NewWinA ctrlsec url

    ∇

    ∇ r←GenJS(page title desc iframe code)
     ⍝ Generate JavaScript for filling placeholders
      r←'#title'Replace'MS3: ',page
      r,←'#SampleTitle'Replace title
      r,←'#SampleDesc'Replace desc
      r,←'#SampleFrame'Replace iframe
      r,←'#SampleSource'Replace(×≢code)/'x-small;border:none'#.HTMLInput.APLToHTMLColour code
    ∇

    ∇ node←GetNode
      node←4↓(1+'tree'≡4↑_what)⊃_what(Get'node')
    ∇
    :ENDSECTION

:EndClass


Typos.

Typos.

:Class ejDateTimePicker : #._SF._ejWidget
⍝ Description:: Syncfusion DateTimePicker widget
⍝ Constructor:: [datetime [format]]
⍝ datetime      -  datetime in either JavaScript format or n↑⎕TS (see: http://www.w3schools.com/js/js_dates.asp)  (default 6↑⎕TS)
⍝ format        -  datetime format, a combination of a date format and a time format (default 'dd-MMM-yyyy hh:mm:ss')
⍝ Public Fields::
⍝ DateTime      - Datetime in either JavaScript format or n↑⎕TS
⍝ Format        - Datetime format, a combination of a date format and a time format (default 'dd-MMM-yyyy hh:mm:ss')
⍝ Examples::
⍝ ejDateTimePicker
⍝ ejDateTimePicker '17:53:59 11/27/66 (GMT-5)'
⍝ ejDateTimePicker (1999 12 31 23 59 59)('dd-MMM-yyyy hh:mm:ss')

    :Field Public Shared Readonly DocBase←'http://help.syncfusion.com/UG/JS_CR/ejDateTimePicker.html'
    :Field Public Shared Readonly ApiLevel←2
    :Field Public Shared Readonly DocDyalog←'/Documentation/DyalogAPIs/Syncfusion/ejDateTimePicker.html'
    :Field Public Shared ReadOnly IntEvt←'change'  'close'  'create'  'destroy'  'focusIn'  'focusOut'  'open'
    :field public DateTime←''
    :field public Format←'dd-MMM-yyyy hh:mm:ss'

    ∇ make0
      :Access Public
      JQueryFn←Uses←'ejDateTimePicker'
      ContainerTag←'input'
      :Implements Constructor
      Container.type←'text'
      InternalEvents←IntEvt
      DateTime←6↑⎕TS
    ∇

    ∇ make args;val;fmt
      :Access Public
      JQueryFn←Uses←'ejDateTimePicker'
      ContainerTag←'input'
      :Implements Constructor
      (DateTime Format)←args defaultArgs(6↑⎕TS)'dd-MMM-yyyy hh:mm:ss'
      Container.type←'text'
      InternalEvents←IntEvt
    ∇

    ∇ r←Render;dt
      :Access public
      SetId
      :If 0∊⍴DateTime ⋄ DateTime←6↑⎕TS ⋄ :EndIf
      :If (0 2∊⍨10|⎕DR DateTime) ⋄ dt←'"',DateTime,'"'
      :Else ⋄ dt←1↓∊','∘,∘⍕¨DateTime-(⍴,DateTime)↑0 1 ⍝ adjust for JavaScript having 0-origin months (Jan-Dec = 0-11)
      :EndIf
      ('value' 'dateTimeFormat')Set(('⍎new Date(',dt,')')Format)
      r←⎕BASE.Render
    ∇
:EndClass

:Class ejDateTimePicker : #._SF._ejWidget
⍝ Description:: Syncfusion DatePicker widget
⍝ Constructor:: [datetime] [format]
⍝ datetime      -  datetime in either JavaScript format or n↑⎕TS (see: http://www.w3schools.com/js/js_dates.asp)  (default 6↑⎕TS)
⍝ format        -  datetime format, a combination of a date format and a time format (default 'dd-MMM-yyyy hh:mm:ss')
⍝ Public Fields::
⍝ DateTime      - Datetime in either JavaScript format or n↑⎕TS
⍝ Format        - Datetime format, a combination of a date format and a time format (default 'dd-MMM-yyyy hh:mm:ss')
⍝ Examples::
⍝ ejDateTimePicker
⍝ ejDateTimePicker '17:53:59 11/27/66 (GMT-5)'
⍝ ejDateTimePicker (1999 12 31 23 59 59)('dd-MMM-yyyy hh:mm:ss')

    :Field Public Shared Readonly DocBase←'http://help.syncfusion.com/UG/JS_CR/ejDateTimePicker.html'
    :Field Public Shared Readonly ApiLevel←2
    :Field Public Shared Readonly DocDyalog←'/Documentation/DyalogAPIs/Syncfusion/ejDateTimePicker.html'
    :Field Public Shared ReadOnly IntEvt←'change'  'close'  'create'  'destroy'  'focusIn'  'focusOut'  'open'
    :field public DateTime←''
    :field public Format←'dd-MMM-yyyy hh:mm:ss'

    ∇ make0
      :Access Public
      JQueryFn←Uses←'ejDateTimePicker'
      ContainerTag←'input'
      :Implements Constructor
      Container.type←'text'
      InternalEvents←IntEvt
      DateTime←6↑⎕TS
    ∇

    ∇ make args;val;fmt
      :Access Public
      JQueryFn←Uses←'ejDateTimePicker'
      ContainerTag←'input'
      :Implements Constructor
      (DateTime Format)←args defaultArgs(6↑⎕TS)'dd-MMM-yyyy hh:mm:ss'
      Container.type←'text'
      InternalEvents←IntEvt
    ∇

    ∇ r←Render;dt
      :Access public
      SetId
      :If 0∊⍴DateTime ⋄ DateTime←6↑⎕TS ⋄ :EndIf
      :If (0 2∊⍨10|⎕DR DateTime) ⋄ dt←'"',DateTime,'"'
      :Else ⋄ dt←1↓∊','∘,∘⍕¨DateTime-(⍴,DateTime)↑0 1 ⍝ adjust for JavaScript having 0-origin months (Jan-Dec = 0-11)
      :EndIf
      ('value' 'dateTimeFormat')Set(('⍎new Date(',dt,')')Format)
      r←⎕BASE.Render
    ∇
:EndClass