Enforced strict attr pairing rules.

Enforced strict attr pairing rules.

:Class index : MiPageSample
⍝ Note from Censors: This code is not fit for human consumption.
⍝ If you must see it, scroll down

⍝ Further down... at your own risk!

    :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
    :Field STR←''      ⍝ String to search for
    :Field ALLFILES    ⍝ List of pathnames for all sample files
    :Field SAMPLE←1    ⍝ The current view is a sample (i.e. has source-code and related samples)

    :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⊃⌽⍵) 'title="Click to pop out"'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?

    ∇ r←Dread page;i
    ⍝ Read a page via the DREADCACHE
      :Trap 0
          :If 0=⎕NC'#.DREADCACHE' ⋄ #.DREADCACHE←⎕NS'' ⋄ #.DREADCACHE.(Keys←Data←⍬) ⋄ :EndIf
          :If (≢#.DREADCACHE.Keys)≥i←#.DREADCACHE.Keys⍳⊂page
              r←i⊃#.DREADCACHE.Data
          :Else
              #.DREADCACHE.Data,←⊂r←#.UnicodeFile.ReadNestedText #.Boot.AppRoot,page,'.dyalog'
              #.DREADCACHE.Keys,←⊂page
          :EndIf
      :Else
          r←,⊂'[file read failed]'
      :EndTrap
    ∇

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

    Dlist←{0::⍬ ⋄ (Slist #.Boot.AppRoot,⍵,' -raw')[;2]} ⍝ List dyalog files

    Slist←{l←⎕SE.SALT.List ⍵ ⋄ l[;2]←('\\'⎕R'/')(l[;2]) ⋄ l}

  ⍝  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)

    ShortInfo←{(4+⍴⍵)↓⊃(INFOSHORT,⊂'')[NAMESSHORT⍳⊂⍵]}

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

    ScoreIn←{(100⊥⌽∨/¨TYPES⍷¨⊂⍵)×(⊂⍺)(∊×⍳⍨)Words'Control'Section Dread ⍵} ⍝ ⍺'s relevance in ⍵ (0=none 1=high 2=lower...)

    Tail←{⍵ ⍺⍺⍨1-(⌽⍵)⍳'/'} ⍝ ↑Tail or ↓Tail

    End←{⍺⍺{⍵↑⍨¯1+⍵⍳'_'}⍺⍺ ⍵} ⍝ ⊢End: until first .   ⌽End: from last .

    NodeToCtrl←{'_',(⊢End 1↓⍵),'.',⌽End ⍵}

    CtrlToNode←{'#',('\.' ⎕R '_')⍵}

    Clean←'[_\-\.]'⎕R' '

    DelEmpty←{((0,⍨2</⍵[;1])∨'/'≠⊃¨⌽¨⍵[;3])⌿⍵}

      DirTree←{⍺←''
          parent←1(↑Tail ⍵)('/',⍵,'/')
          list←Slist #.Boot.AppRoot,⍵,' -rec -raw',(0≠⍴⍺)/' -ext=',⍺
          0∊⍴list:0 3⍴0 '' ''
          nodir←~list[;1]∊⍨⊂'<DIR>'
          prefix←'/',(↓Tail ⍵),nodir/(↑Tail ⍵),'/' ⍝ Quirk in SALT.List includes path only if has subfolders
          files←prefix∘,¨list[;2]
          levels←2+(+/¨'/'=files)-+/'/'=3⊃parent
          ((list[;1]≡¨⊂'<DIR>')/files),←'/'
          DelEmpty⍣≡parent⍪⍉↑levels(↑Tail¨list[;2])files
      }

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

    :ENDSECTION

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

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

     ⍝ Initialize globals
      CURRFILES←⊂'/About-MiServer'
      CURRCTRL←''
      SAMPLES←0 3⍴⍬
      GROUPS←'Dyalog Controls' 'SyncFusion' 'JQueryUI' 'Base HTML'
      REFS←_DC _SF _JQ _html
      INFOSHORT←'⍝',¨Dread'Examples/Data/infoShort'
      INFOLONG←'⍝',¨Dread'Examples/Data/infoLong'
      NAMESSHORT←Names INFOSHORT
      NAMESLONG←Names INFOLONG
      list←⊃⍪/{Slist #.Boot.AppRoot,'Examples/',⍵,' -rec -raw -full'}¨'DC' 'SF' 'JQ' 'html'
      ALLFILES←list[;2]/⍨list[;1]≢¨⊂'<DIR>'
      TREE←⎕SE.SALT.Load #.Boot.AppRoot,'Examples/Data/tree -noname'
      CORE←⎕SE.SALT.Load #.Boot.AppRoot,'Examples/Data/core -noname'

      Add _.StyleSheet'/Styles/homepage.css'

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

    ∇ PopulateLeft thediv;class;depths;group;items;ref;samples;vp;search;menu;i;fs;ac;text;treeall;treecore;names;tree;dirs;levels;core;enames;t

      :If 1
      ⍝ SEARCH FIELD ⍝⍝⍝
          search←New _.EditField'searchfield'⍝).On'change' 'OnSearch'('str' 'val')
          (search,←'#searchbutton' '.menu'New _.button'Search').On'click' 'OnSearch'('str' '#searchfield' 'val')
          thediv.Add Horz search
          thediv.Add _.hr
      :EndIf

      ⍝ GETTING STARTED ⍝⍝⍝
      '.cat'thediv.Add _.p'Getting Started'
      tree←0 3⍴0 '' ''
      tree⍪←1 'Start here',CURRFILES
      tree⍪←'pdf'DirTree'Documentation'
      tree⍪←DirTree'Examples/Techniques'
      tree⍪←DirTree'Examples/Applications'
      tree[;2]←Clean¨tree[;2]
      tree←'#treeG'thediv.Add _.ejTreeView tree
      tree.On'nodeSelect' 'OnGS'('node' 'eval' 'argument.id')
      thediv.Add _.hr

      ⍝ CONTROLS ⍝⍝⍝
      '.cat'thediv.Add _.p'Controls'
      tree←0 3⍴0 '' ''
      names←TREE
      dirs←'_'=(⊃⌽)¨names
      ⍝ All
      tree⍪←1 'All' '_'
      levels←1+dirs-⍨+/¨'_'=names
      enames←⌽End¨1↓¨names↓¨⍨-dirs
      tree⍪←⍉↑levels enames names
      ⍝ Core
      core←(2=levels)∨(enames∊CORE) ⍝∧(~dirs) not necessary because no category name appears in core list
      enames/⍨←core
      names/⍨←core
      levels/⍨←core
      levels⌊←3
      core←⍉↑levels enames(names,¨'(')
      tree⍪⍨←⊃⍪/{⍵[1,1+⍋↑1↓⍵[;2];]}¨(2=core[;1])⊂[1]core ⍝ Sort classes
      tree⍪⍨←1 'Core' '_(' ⍝ ( is core suffix
      i←(tree[;2]∊t←'DC' 'SF' 'JQ' 'html')/⍳1↑⍴tree
      tree[i;2]←('Dyalog Controls' 'Syncfusion' 'JQueryUI' 'HTML')[t⍳tree[i;2]]
      tree[;2]←Clean¨tree[;2]
      ⍝ Add the whole tree
      tree←'#treeA'thediv.Add _.ejTreeView tree
      tree.On'nodeSelect' 'OnCA'('node' 'eval' 'argument.id')
    ∇

    ∇ PopulateMid mid;url;code;frame;title;navbuts;t;sr;desc

     ⍝ Read framed pages
      url←⊃CURRFILES
      code←Dread url

     ⍝ Create and fill placeholders for title and header
      title←Clean'Control'Section code

     ⍝ _html.code
      '#title'Add _.title('MS3: ',title) ⍝ after the : will be updated later on
      '#SampleTitle'mid.Add _.h2 title

      desc←'#CtlDescs' 'style="display:none"'mid.Add _.div ⍝ All descriptions will go here

     ⍝ Control: Code fragment [View documentation]
      '#ControlDesc'desc.Add _.span
      ('#DocLink'desc.Add _.span).Add'target="_blank'New _.A'View documentation'
      desc.Add _.br

     ⍝ Sample: Embed a code fragment [Pop out]
      '#SampleDesc'desc.Add _.span('Description'Section code)
      ('#PopLink'desc.Add _.span).Add NewWinA'Pop out'url
      desc.Add _.br

     ⍝ [ ] Show related samples
      t←'onchange="$("#rel").toggle()"'desc.Add _.Input'checkbox' '' 'Show related samples' 'right'
      t.id←'ShowRelated'
      t.style←'margin-left: 0px; margin-top: 10px; margin-bottom: 10px;'
      '#rel' 'style="display: none;"'desc.Add _.div
      desc.Add _.hr

     ⍝ Create and fill placeholder for embedded page and its source code
      (mid.Add NewDiv'#SampleFrame').Add Frame url
      mid.Add _.hr
      t←(⊂'onchange' '$("#SampleSource").toggle()')mid.Add _.Input'checkbox' '' 'Show source code' 'right'
      t.style←'margin-left: 0px;'
      '#SampleSource' '.samplesource' 'style="display: none;"'mid.Add _.div('small;border:none'#.HTMLInput.APLToHTMLColour Dread⊃CURRFILES)

    ∇

    :ENDSECTION

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

    ∇ r←OnGS;node;list;nodir;prefix;files;out;file;name;item;title;desc;toggle;fromframe
      :Access Public

     ⍝ Callback for Getting Started
      node←(1+'tree'≡4↑_what)⊃_what(Get'node') ⍝ get id of node if tree or id of what if not
      fromframe←'_'=⊃⌽node
      node~←'_' ⍝ _ is frame suffix
      :If '/'=⊃⌽node ⍝ Folder (i.e. Category)
          r←''
          :Return
          CURRCTRL←''
          SAMPLE←0
          node←1↓¯1↓node ⍝ Strip outer slashes
          list←Slist #.Boot.AppRoot,node,' -rec -raw'
          nodir←~list[;1]∊⍨⊂'<DIR>'
          prefix←(↓Tail node),nodir/(Clean↑Tail node),'/' ⍝ Quirk in SALT.List includes path only if has subfolders
          files←prefix∘,¨list[;2]
          files/⍨←list[;1]≢¨⊂'<DIR>'
          CURRFILES←files
          out←NewDiv'.framed'
          :For file :In files
              item←('#/',file,'_')'.listitem' 'title="Click here to open"'out.Add _.p
              item.Add _.strong(Clean↑Tail file)
              item.Add' ',#.HTMLInput.dtlb'Description'Section Dread file
          :EndFor
          out.Add _.Handler'.listitem' 'click' 'OnGS'
          title←Clean node
          desc←'Click on a button below to select a sample.'
          r←title title desc out''
          toggle←';scroll(0,0);$("#CtlDescs").hide()'
      :Else ⍝ File (i.e. Page)
          SAMPLE←1
          toggle←(1+fromframe)⊃'$("#CtlDescs").hide();' '$("#CtlDescs").show();'
          r←Update node
      :EndIf

      r←(Execute toggle),GenJS r
    ∇

    ∇ r←OnCA;node;control;files;list;file;scores;code;desc;score;controls;out;item;title;coreonly;toggle
      :Access Public
     ⍝ Callback for Core/All Controls
      node←(1+'tree'≡4↑_what)⊃_what(Get'node') ⍝ get id of node if tree or id of what if not
      COREONLY←coreonly←'('∊node
      node↓⍨←-coreonly ⍝ drop ( is core suffix
      :If '_'=⊃⌽node ⍝ Category
          r←''
          :Return
          CURRCTRL←''
          SAMPLE←0
          title←('All '/⍨coreonly<1=⍴node),(coreonly/'Core '),(Clean node),' controls'
          r←title title'' '' ''
          toggle←'$("#CtlDescs").hide();'
      :Else ⍝ Control
          SAMPLE←1
          CURRCTRL←node
          control←NodeToCtrl node
          scores←⍬
          :For file :In ALLFILES
              controls←Words'Control'Section Dread'Examples/',file
              score←102-2×(50↑controls)⍳⊂control ⍝ 100=most relevant, 0=not relevant at all
              score-←∨/'Advanced'⍷file           ⍝ subtract 1 if advanced example
              scores,←0⌈score
          :EndFor
          CURRFILES←'/Examples/'∘,¨(ALLFILES/⍨×scores)[⍒scores/⍨×scores] ⍝ sort descending by score
          :If 0=⍴CURRFILES
              r←control control'' '' ''
              toggle←'$("#CtlDescs").hide();'
          :Else
              r←control Update⊃CURRFILES
              toggle←'$("#CtlDescs").show();'
          :EndIf

      :EndIf
      r←(GenJS r),Execute'scroll(0,0);',toggle
    ∇

    ∇ r←OnList;out;file;item;title;node;desc
      :Access Public
     ⍝ Called when user wants the entire list of relevant samples for the current control
      node←¯1↓_what ⍝ Remove desc _ suffix
      out←NewDiv'.framed'
      :If 0=⍴CURRFILES
          '.noitems'out.Add _.p,⊂'[no samples for ',(¯1↓node),' controls]'
              ⍝(2⊃r)←'_',class,'.',AddShortInfo⊂control
              ⍝(3⊃r)←(4↓⊃AddLongInfo,⊂control),' ',('0'=⊃2⊃r)/3⊃r
          r←control(control,' Short Info')'Long info'out''
      :Else
          :For file :In CURRFILES
              item←('#',file,'_')'.listitem' 'title="Click here to open"'out.Add _.p
              item.Add _.strong(↑Tail file)
              item.Add' ',#.HTMLInput.dtlb'Description'Section Dread file
          :EndFor
          out.Add _.Handler'.listitem' 'click' 'OnGS'
          title←NodeToCtrl node
          desc←'Click on a button below to select a sample.'
          r←title title desc out''
      :EndIf
      r←GenJS r
    ∇

    ∇ r←OnSample
      :Access Public
      r←GenJS Update CURRFILES⊃⍨⍎5↓_what ⍝ id="nodeS23"
    ∇

    ∇ r←OnSearch;str;terms;out;i;dir;files;file;url;code;ctrlsec;desc;item;title
      :Access Public

      str←(1+STR≡'')⊃STR(Get'str') ⍝ get search string
      :If str≡'Clear The Cache' ⋄ ⎕EX'#.DREADCACHE' ⋄ :EndIf

      :If ×≢str ⍝ Continue only if there is a 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 'Applications' '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),' sample pages for ',Q str
          desc←'Click on a button below to select a sample.'
          ⍝r←(Q str)title desc out''
          r←'#SampleFrame'Replace out
          r,←'#SampleTitle'Replace'Samples related to ',Q str
          r,←'#SampleSource'Replace'[No source to display]'
          r,←Execute'$("#CtlDescs").hide();'
          ⍝r,←'#title'Replace(Q str)
          ⍝r,←Execute'$("#SampleFrame").show();'

          :Return
          SEARCH←GenJS⍣(STR≡'')⊢r ⍝ Do not finish up if called by OnTree
          STR←'' ⍝ Reset
      :EndIf
      r←SEARCH
    ∇

    ∇ (page title desc url code)←{spacectrl}Update url;ctrlsec;control;space
     ⍝ Create new placeholder values

      code←Dread url
      :If ×⎕NC'spacectrl'
          page←spacectrl
          title←('All '/⍨2≥⍴page),('Core '/⍨'('=⊃⌽page),⌽End page
      :ElseIf '/Doc'≡4↑url
          desc←0/page←title←url
      :Else
          page←Clean'Control'Section code
          title←page
      :EndIf
      desc←#.HTMLInput.dtlb'Description'Section code
    ∇

    ∇ r←GenJS(page title desc url code);cd;sd;file;scores;controls;score;out;item;control;coreonly
     ⍝ Generate JavaScript for filling placeholders
      r←'#title'Replace'MS3: ',page

      :If 'treeA'≢_what ⍝ NOT a control selection

          control←'grebmorKnetroM' ⍝ really hope that doesn't find anything!
          :If '/Doc'≡4↑url
              r←'#ControlDesc'Replace('width=740 height=900 data="',url,'.pdf"')Add _.object
              r,←'#SampleDesc'Replace''
              r,←'#PopLink'Replace''
              r,←'#DocLink'Replace''
              r,←'#SampleSource'Replace''
              r,←'#SampleFrame'Replace''
              r,←'#SampleTitle'Replace''
              r,←Execute'$("#SampleFrame").hide();$("#CtlDescs").show();'
              →0
          :Else
              :If 'treeG'≡_what ⋄ title←url ⍝ title←'Application ',url
              :Else ⋄ title←'Sample ',url ⋄ :EndIf
              r,←'#ControlDesc'Replace''
              r,←'#SampleTitle'Replace title
              (sd←New _.span).Add¨(_.strong'Description: ')(_.em desc)
              r,←'#SampleDesc'Replace sd
              r,←Execute'$("#CtlDescs").',((1+_what≡'treeG')⊃'show' 'hide'),'();'
          :EndIf
      :Else
          control←NodeToCtrl CURRCTRL
          r,←(×≢title)/'#SampleTitle'Replace control
          (cd←New _.span).Add¨(_.strong'Control: ')(_.em,⊂ShortInfo⌽End CURRCTRL)
          r,←'#ControlDesc'Replace cd
          (sd←New _.span).Add¨(_.strong'Sample: ')(_.em desc)
          r,←'#SampleDesc'Replace sd
          r,←Execute'$("#SampleFrame").show();'
      :EndIf

      :If 0
          :If ''≡desc
              coreonly←COREONLY
              control←NodeToCtrl¨TREE/⍨∨/¨(⊂node)⍷¨TREE
              control←(CORE∊⍨{⍵↓⍨⌽⍵⍳'.'}¨control)/⍣coreonly⊢control
              scores←⍬
              :For file :In files←ALLFILES[⍋↑ALLFILES]
                  controls←Words'Control'Section Dread'Examples/',file
                  score←2×∨/control∊controls         ⍝ 2=relevant Simple, 0=not relevant
                  score-←∨/'Advanced'⍷file           ⍝ 1=relevant Advanced
                  scores,←0⌈score
              :EndFor
              CURRFILES←'/Examples/'∘,¨(files/⍨×scores)[⍒scores/⍨×scores] ⍝ put Simple before Advanced
              out←NewDiv'.framed'
              :If 0=⍴CURRFILES
                  '.noitems'out.Add _.p,⊂'[no samples for ',(¯1↓node),' controls]'
              ⍝(2⊃r)←'_',class,'.',AddShortInfo⊂control
              ⍝(3⊃r)←(4↓⊃AddLongInfo,⊂control),' ',('0'=⊃2⊃r)/3⊃r
                  r←control(control,' Short Info')'Long info'out''
              :Else
                  :For file :In CURRFILES
                      item←('#',file,'_')'.listitem' 'title="Click here to open"'out.Add _.p
                      item.Add _.strong(↑Tail file)
                      item.Add' ',#.HTMLInput.dtlb'Description'Section Dread file
                  :EndFor
                  out.Add _.Handler'.listitem' 'click' 'OnGS'
                  title←('All '/⍨coreonly<1=⍴node),(coreonly/'Core '),(Clean node),' controls'
              ⍝desc←'Click on a button below to select a sample.'

              :EndIf
          :Else

          :EndIf
      :EndIf

      scores←⍬
      :For file :In ALLFILES
          controls←Words'Control'Section Dread'Examples/',file
          score←102-2×(50↑controls)⍳⊂control ⍝ 100=most relevant, 0=not relevant at all
          score-←∨/'Advanced'⍷file           ⍝ subtract 1 if advanced example
          scores,←0⌈score
      :EndFor
      CURRFILES←'/Examples/'∘,¨(ALLFILES/⍨×scores)[⍒scores/⍨×scores] ⍝ sort descending by score
      out←NewDiv'.framed'
      :If 1<⍴CURRFILES
          :For file :In 1↓CURRFILES
              item←('#',file,'_')'.listitem' 'title="Click here to open"'out.Add _.p
              item.Add _.strong(↑Tail file)
              item.Add' ',#.HTMLInput.dtlb'Description'Section Dread file
          :EndFor
          out.Add _.Handler'.listitem' 'click' 'OnGS'
      :EndIf

      r,←'#rel'Replace out

      r,←(×≢url)/'#SampleFrame'Replace Frame url

      r,←'#PopLink'Replace NewWinA'Pop out'url
      r,←'#SampleSource'Replace'small;border:none'#.HTMLInput.APLToHTMLColour code
      url←∊'/Documentation/DyalogAPIs/WidgetDoc?namespace=_' '&widget=',¨(⊢End{⍺ ⍵}⌽End)1↓CURRCTRL
      :If _what≡'treeA' ⋄ r,←'#DocLink'Replace NewWinA'View documentation'url
      :Else ⋄ r,←'#DocLink'Replace'' ⋄ :EndIf
      r,←'[for=''ShowRelated'']'Replace{'Show ',(⍕⍵),' related sample',((⍵≠1)/'s'),'.'}0⌈¯1+⍴CURRFILES
      :If (0=⍴CURRFILES)∧_what≡'treeA'
          r,←Execute'$("#CtlDescs").show();'
          r,←'#SampleDesc'Replace'.noitems'New _.p,⊂'[no samples using ',control,']'
          r,←'#PopLink'Replace''
          r,←'#SampleSource'Replace''
          r,←'#SampleFrame'Replace''
      :EndIf
    ∇

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

    :SECTION OBSOLETE ⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝
      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
      }

    ∇ r←OnNavBut;file;item;out;title;desc
      :Access Public
      r←''
      :Select 2↓_what
      :Case 'related'
          SAMPLE←0
          out←NewDiv'.framed'
          :For file :In CURRFILES
              item←('#',file,'_')'.listitem' 'title="Click here to open"'out.Add _.p
              item.Add _.strong(Clean↑Tail file)
              item.Add' ',#.HTMLInput.dtlb'Description'Section Dread file
          :EndFor
          out.Add _.Handler'.listitem' 'click' 'OnGS'
          title←''⍝NodeToCtrl node
          desc←'Click on a button below to select a sample.'
          r←GenJS title title desc out''
      :Case 'source'
          :If SAMPLE
              r←'#SampleSource'Replace'small;border:none'#.HTMLInput.APLToHTMLColour Dread⊃CURRFILES
              r,←Execute'location.replace("#source");'
          :EndIf
      :Case 'info'
          :If SAMPLE∧''≢CURRCTRL
              r←Execute'window.open',P Q∊∊'/Documentation/DyalogAPIs/WidgetDoc?namespace=_' '&widget=',¨(⊢End{⍺ ⍵}⌽End)CURRCTRL
          :EndIf
      :Case 'out'
          r←SAMPLE/Execute'window.open',P Q⊃CURRFILES
      :Case 'urce' ⍝ n.b. 2↓
          r←Execute'scroll(0,0);'
      :EndSelect
    ∇

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

    ∇ r←OnTree;node;descs;out;items;spacectrl;control;spacedir;files;file;i;url;code;ctrlsec;desc;item;title;currctrls;core;names;class;group;cat;score
      :Access Public
      node←GetNode
      :If '0'∊node ⍝ Core or 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⌽core↓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←0 2⍴0 'url' ⍝ Score URL
          :For file :In files
              url←spacedir,'/',file
              CURRFILES⍪←(spacectrl ScoreIn url)url
          :EndFor
          CURRFILES⌿⍨←×CURRFILES[;1] ⍝ Weed out 0-score samples
          :If 0∊⍴CURRFILES ⍝ Failed to find a sample, so perform a search instead
              STR←spacectrl ⍝ Pass implicit argument to OnSearch
              r←OnSearch ⍝ Capture and modify result
              (1⊃r)←spacectrl
              (2⊃r)←'_',class,'.',AddShortInfo⊂control
              (3⊃r)←(4↓⊃AddLongInfo,⊂control),' ',('0'=⊃2⊃r)/3⊃r
          :Else
              CURRFILES←CURRFILES[⍋CURRFILES[;1];2] ⍝ Sort by score
              r←control Update⊃CURRFILES
          :EndIf
      :EndIf
      r←GenJS r
    ∇
    :ENDSECTION

:EndClass

:Class index : MiPageSample
⍝ Note from Censors: This code is not fit for human consumption.
⍝ If you must see it, scroll down

⍝ Further down... at your own risk!

    :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
    :Field STR←''      ⍝ String to search for
    :Field ALLFILES    ⍝ List of pathnames for all sample files
    :Field SAMPLE←1    ⍝ The current view is a sample (i.e. has source-code and related samples)

    :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⊃⌽⍵) 'title="Click to pop out"'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?

    ∇ r←Dread page;i
    ⍝ Read a page via the DREADCACHE
      :Trap 0
          :If 0=⎕NC'#.DREADCACHE' ⋄ #.DREADCACHE←⎕NS'' ⋄ #.DREADCACHE.(Keys←Data←⍬) ⋄ :EndIf
          :If (≢#.DREADCACHE.Keys)≥i←#.DREADCACHE.Keys⍳⊂page
              r←i⊃#.DREADCACHE.Data
          :Else
              #.DREADCACHE.Data,←⊂r←#.UnicodeFile.ReadNestedText #.Boot.AppRoot,page,'.dyalog'
              #.DREADCACHE.Keys,←⊂page
          :EndIf
      :Else
          r←,⊂'[file read failed]'
      :EndTrap
    ∇

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

    Dlist←{0::⍬ ⋄ (Slist #.Boot.AppRoot,⍵,' -raw')[;2]} ⍝ List dyalog files

    Slist←{l←⎕SE.SALT.List ⍵ ⋄ l[;2]←('\\'⎕R'/')(l[;2]) ⋄ l}

  ⍝  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)

    ShortInfo←{(4+⍴⍵)↓⊃(INFOSHORT,⊂'')[NAMESSHORT⍳⊂⍵]}

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

    ScoreIn←{(100⊥⌽∨/¨TYPES⍷¨⊂⍵)×(⊂⍺)(∊×⍳⍨)Words'Control'Section Dread ⍵} ⍝ ⍺'s relevance in ⍵ (0=none 1=high 2=lower...)

    Tail←{⍵ ⍺⍺⍨1-(⌽⍵)⍳'/'} ⍝ ↑Tail or ↓Tail

    End←{⍺⍺{⍵↑⍨¯1+⍵⍳'_'}⍺⍺ ⍵} ⍝ ⊢End: until first .   ⌽End: from last .

    NodeToCtrl←{'_',(⊢End 1↓⍵),'.',⌽End ⍵}

    CtrlToNode←{'#',('\.' ⎕R '_')⍵}

    Clean←'[_\-\.]'⎕R' '

    DelEmpty←{((0,⍨2</⍵[;1])∨'/'≠⊃¨⌽¨⍵[;3])⌿⍵}

      DirTree←{⍺←''
          parent←1(↑Tail ⍵)('/',⍵,'/')
          list←Slist #.Boot.AppRoot,⍵,' -rec -raw',(0≠⍴⍺)/' -ext=',⍺
          0∊⍴list:0 3⍴0 '' ''
          nodir←~list[;1]∊⍨⊂'<DIR>'
          prefix←'/',(↓Tail ⍵),nodir/(↑Tail ⍵),'/' ⍝ Quirk in SALT.List includes path only if has subfolders
          files←prefix∘,¨list[;2]
          levels←2+(+/¨'/'=files)-+/'/'=3⊃parent
          ((list[;1]≡¨⊂'<DIR>')/files),←'/'
          DelEmpty⍣≡parent⍪⍉↑levels(↑Tail¨list[;2])files
      }

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

    :ENDSECTION

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

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

     ⍝ Initialize globals
      CURRFILES←⊂'/About-MiServer'
      CURRCTRL←''
      SAMPLES←0 3⍴⍬
      GROUPS←'Dyalog Controls' 'SyncFusion' 'JQueryUI' 'Base HTML'
      REFS←_DC _SF _JQ _html
      INFOSHORT←'⍝',¨Dread'Examples/Data/infoShort'
      INFOLONG←'⍝',¨Dread'Examples/Data/infoLong'
      NAMESSHORT←Names INFOSHORT
      NAMESLONG←Names INFOLONG
      list←⊃⍪/{Slist #.Boot.AppRoot,'Examples/',⍵,' -rec -raw -full'}¨'DC' 'SF' 'JQ' 'html'
      ALLFILES←list[;2]/⍨list[;1]≢¨⊂'<DIR>'
      TREE←⎕SE.SALT.Load #.Boot.AppRoot,'Examples/Data/tree -noname'
      CORE←⎕SE.SALT.Load #.Boot.AppRoot,'Examples/Data/core -noname'

      Add _.StyleSheet'/Styles/homepage.css'

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

    ∇ PopulateLeft thediv;class;depths;group;items;ref;samples;vp;search;menu;i;fs;ac;text;treeall;treecore;names;tree;dirs;levels;core;enames;t

      :If 1
      ⍝ SEARCH FIELD ⍝⍝⍝
          search←New _.EditField'searchfield'⍝).On'change' 'OnSearch'('str' 'val')
          (search,←'#searchbutton' '.menu'New _.button'Search').On'click' 'OnSearch'('str' '#searchfield' 'val')
          thediv.Add Horz search
          thediv.Add _.hr
      :EndIf

      ⍝ GETTING STARTED ⍝⍝⍝
      '.cat'thediv.Add _.p'Getting Started'
      tree←0 3⍴0 '' ''
      tree⍪←1 'Start here',CURRFILES
      tree⍪←'pdf'DirTree'Documentation'
      tree⍪←DirTree'Examples/Techniques'
      tree⍪←DirTree'Examples/Applications'
      tree[;2]←Clean¨tree[;2]
      tree←'#treeG'thediv.Add _.ejTreeView tree
      tree.On'nodeSelect' 'OnGS'('node' 'eval' 'argument.id')
      thediv.Add _.hr

      ⍝ CONTROLS ⍝⍝⍝
      '.cat'thediv.Add _.p'Controls'
      tree←0 3⍴0 '' ''
      names←TREE
      dirs←'_'=(⊃⌽)¨names
      ⍝ All
      tree⍪←1 'All' '_'
      levels←1+dirs-⍨+/¨'_'=names
      enames←⌽End¨1↓¨names↓¨⍨-dirs
      tree⍪←⍉↑levels enames names
      ⍝ Core
      core←(2=levels)∨(enames∊CORE) ⍝∧(~dirs) not necessary because no category name appears in core list
      enames/⍨←core
      names/⍨←core
      levels/⍨←core
      levels⌊←3
      core←⍉↑levels enames(names,¨'(')
      tree⍪⍨←⊃⍪/{⍵[1,1+⍋↑1↓⍵[;2];]}¨(2=core[;1])⊂[1]core ⍝ Sort classes
      tree⍪⍨←1 'Core' '_(' ⍝ ( is core suffix
      i←(tree[;2]∊t←'DC' 'SF' 'JQ' 'html')/⍳1↑⍴tree
      tree[i;2]←('Dyalog Controls' 'Syncfusion' 'JQueryUI' 'HTML')[t⍳tree[i;2]]
      tree[;2]←Clean¨tree[;2]
      ⍝ Add the whole tree
      tree←'#treeA'thediv.Add _.ejTreeView tree
      tree.On'nodeSelect' 'OnCA'('node' 'eval' 'argument.id')
    ∇

    ∇ PopulateMid mid;url;code;frame;title;navbuts;t;sr;desc

     ⍝ Read framed pages
      url←⊃CURRFILES
      code←Dread url

     ⍝ Create and fill placeholders for title and header
      title←Clean'Control'Section code

     ⍝ _html.code
      '#title'Add _.title('MS3: ',title) ⍝ after the : will be updated later on
      '#SampleTitle'mid.Add _.h2 title

      desc←'#CtlDescs' 'style="display:none"'mid.Add _.div ⍝ All descriptions will go here

     ⍝ Control: Code fragment [View documentation]
      '#ControlDesc'desc.Add _.span
      ('#DocLink'desc.Add _.span).Add'target="_blank'New _.A'View documentation'
      desc.Add _.br

     ⍝ Sample: Embed a code fragment [Pop out]
      '#SampleDesc'desc.Add _.span('Description'Section code)
      ('#PopLink'desc.Add _.span).Add NewWinA'Pop out'url
      desc.Add _.br

     ⍝ [ ] Show related samples
      t←'onchange="$("#rel").toggle()"'desc.Add _.Input'checkbox' '' 'Show related samples' 'right'
      t.id←'ShowRelated'
      t.style←'margin-left: 0px; margin-top: 10px; margin-bottom: 10px;'
      '#rel' 'style="display: none;"'desc.Add _.div
      desc.Add _.hr

     ⍝ Create and fill placeholder for embedded page and its source code
      (mid.Add NewDiv'#SampleFrame').Add Frame url
      mid.Add _.hr
      t←'onchange' '$("#SampleSource").toggle()'mid.Add _.Input'checkbox' '' 'Show source code' 'right'
      t.style←'margin-left: 0px;'
      '#SampleSource' '.samplesource' 'style="display: none;"'mid.Add _.div('small;border:none'#.HTMLInput.APLToHTMLColour Dread⊃CURRFILES)

    ∇

    :ENDSECTION

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

    ∇ r←OnGS;node;list;nodir;prefix;files;out;file;name;item;title;desc;toggle;fromframe
      :Access Public

     ⍝ Callback for Getting Started
      node←(1+'tree'≡4↑_what)⊃_what(Get'node') ⍝ get id of node if tree or id of what if not
      fromframe←'_'=⊃⌽node
      node~←'_' ⍝ _ is frame suffix
      :If '/'=⊃⌽node ⍝ Folder (i.e. Category)
          r←''
          :Return
          CURRCTRL←''
          SAMPLE←0
          node←1↓¯1↓node ⍝ Strip outer slashes
          list←Slist #.Boot.AppRoot,node,' -rec -raw'
          nodir←~list[;1]∊⍨⊂'<DIR>'
          prefix←(↓Tail node),nodir/(Clean↑Tail node),'/' ⍝ Quirk in SALT.List includes path only if has subfolders
          files←prefix∘,¨list[;2]
          files/⍨←list[;1]≢¨⊂'<DIR>'
          CURRFILES←files
          out←NewDiv'.framed'
          :For file :In files
              item←('#/',file,'_')'.listitem' 'title="Click here to open"'out.Add _.p
              item.Add _.strong(Clean↑Tail file)
              item.Add' ',#.HTMLInput.dtlb'Description'Section Dread file
          :EndFor
          out.Add _.Handler'.listitem' 'click' 'OnGS'
          title←Clean node
          desc←'Click on a button below to select a sample.'
          r←title title desc out''
          toggle←';scroll(0,0);$("#CtlDescs").hide()'
      :Else ⍝ File (i.e. Page)
          SAMPLE←1
          toggle←(1+fromframe)⊃'$("#CtlDescs").hide();' '$("#CtlDescs").show();'
          r←Update node
      :EndIf

      r←(Execute toggle),GenJS r
    ∇

    ∇ r←OnCA;node;control;files;list;file;scores;code;desc;score;controls;out;item;title;coreonly;toggle
      :Access Public
     ⍝ Callback for Core/All Controls
      node←(1+'tree'≡4↑_what)⊃_what(Get'node') ⍝ get id of node if tree or id of what if not
      COREONLY←coreonly←'('∊node
      node↓⍨←-coreonly ⍝ drop ( is core suffix
      :If '_'=⊃⌽node ⍝ Category
          r←''
          :Return
          CURRCTRL←''
          SAMPLE←0
          title←('All '/⍨coreonly<1=⍴node),(coreonly/'Core '),(Clean node),' controls'
          r←title title'' '' ''
          toggle←'$("#CtlDescs").hide();'
      :Else ⍝ Control
          SAMPLE←1
          CURRCTRL←node
          control←NodeToCtrl node
          scores←⍬
          :For file :In ALLFILES
              controls←Words'Control'Section Dread'Examples/',file
              score←102-2×(50↑controls)⍳⊂control ⍝ 100=most relevant, 0=not relevant at all
              score-←∨/'Advanced'⍷file           ⍝ subtract 1 if advanced example
              scores,←0⌈score
          :EndFor
          CURRFILES←'/Examples/'∘,¨(ALLFILES/⍨×scores)[⍒scores/⍨×scores] ⍝ sort descending by score
          :If 0=⍴CURRFILES
              r←control control'' '' ''
              toggle←'$("#CtlDescs").hide();'
          :Else
              r←control Update⊃CURRFILES
              toggle←'$("#CtlDescs").show();'
          :EndIf

      :EndIf
      r←(GenJS r),Execute'scroll(0,0);',toggle
    ∇

    ∇ r←OnList;out;file;item;title;node;desc
      :Access Public
     ⍝ Called when user wants the entire list of relevant samples for the current control
      node←¯1↓_what ⍝ Remove desc _ suffix
      out←NewDiv'.framed'
      :If 0=⍴CURRFILES
          '.noitems'out.Add _.p,⊂'[no samples for ',(¯1↓node),' controls]'
              ⍝(2⊃r)←'_',class,'.',AddShortInfo⊂control
              ⍝(3⊃r)←(4↓⊃AddLongInfo,⊂control),' ',('0'=⊃2⊃r)/3⊃r
          r←control(control,' Short Info')'Long info'out''
      :Else
          :For file :In CURRFILES
              item←('#',file,'_')'.listitem' 'title="Click here to open"'out.Add _.p
              item.Add _.strong(↑Tail file)
              item.Add' ',#.HTMLInput.dtlb'Description'Section Dread file
          :EndFor
          out.Add _.Handler'.listitem' 'click' 'OnGS'
          title←NodeToCtrl node
          desc←'Click on a button below to select a sample.'
          r←title title desc out''
      :EndIf
      r←GenJS r
    ∇

    ∇ r←OnSample
      :Access Public
      r←GenJS Update CURRFILES⊃⍨⍎5↓_what ⍝ id="nodeS23"
    ∇

    ∇ r←OnSearch;str;terms;out;i;dir;files;file;url;code;ctrlsec;desc;item;title
      :Access Public

      str←(1+STR≡'')⊃STR(Get'str') ⍝ get search string
      :If str≡'Clear The Cache' ⋄ ⎕EX'#.DREADCACHE' ⋄ :EndIf

      :If ×≢str ⍝ Continue only if there is a 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 'Applications' '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),' sample pages for ',Q str
          desc←'Click on a button below to select a sample.'
          ⍝r←(Q str)title desc out''
          r←'#SampleFrame'Replace out
          r,←'#SampleTitle'Replace'Samples related to ',Q str
          r,←'#SampleSource'Replace'[No source to display]'
          r,←Execute'$("#CtlDescs").hide();'
          ⍝r,←'#title'Replace(Q str)
          ⍝r,←Execute'$("#SampleFrame").show();'

          :Return
          SEARCH←GenJS⍣(STR≡'')⊢r ⍝ Do not finish up if called by OnTree
          STR←'' ⍝ Reset
      :EndIf
      r←SEARCH
    ∇

    ∇ (page title desc url code)←{spacectrl}Update url;ctrlsec;control;space
     ⍝ Create new placeholder values

      code←Dread url
      :If ×⎕NC'spacectrl'
          page←spacectrl
          title←('All '/⍨2≥⍴page),('Core '/⍨'('=⊃⌽page),⌽End page
      :ElseIf '/Doc'≡4↑url
          desc←0/page←title←url
      :Else
          page←Clean'Control'Section code
          title←page
      :EndIf
      desc←#.HTMLInput.dtlb'Description'Section code
    ∇

    ∇ r←GenJS(page title desc url code);cd;sd;file;scores;controls;score;out;item;control;coreonly
     ⍝ Generate JavaScript for filling placeholders
      r←'#title'Replace'MS3: ',page

      :If 'treeA'≢_what ⍝ NOT a control selection

          control←'grebmorKnetroM' ⍝ really hope that doesn't find anything!
          :If '/Doc'≡4↑url
              r←'#ControlDesc'Replace('width=740 height=900 data="',url,'.pdf"')Add _.object
              r,←'#SampleDesc'Replace''
              r,←'#PopLink'Replace''
              r,←'#DocLink'Replace''
              r,←'#SampleSource'Replace''
              r,←'#SampleFrame'Replace''
              r,←'#SampleTitle'Replace''
              r,←Execute'$("#SampleFrame").hide();$("#CtlDescs").show();'
              →0
          :Else
              :If 'treeG'≡_what ⋄ title←url ⍝ title←'Application ',url
              :Else ⋄ title←'Sample ',url ⋄ :EndIf
              r,←'#ControlDesc'Replace''
              r,←'#SampleTitle'Replace title
              (sd←New _.span).Add¨(_.strong'Description: ')(_.em desc)
              r,←'#SampleDesc'Replace sd
              r,←Execute'$("#CtlDescs").',((1+_what≡'treeG')⊃'show' 'hide'),'();'
          :EndIf
      :Else
          control←NodeToCtrl CURRCTRL
          r,←(×≢title)/'#SampleTitle'Replace control
          (cd←New _.span).Add¨(_.strong'Control: ')(_.em,⊂ShortInfo⌽End CURRCTRL)
          r,←'#ControlDesc'Replace cd
          (sd←New _.span).Add¨(_.strong'Sample: ')(_.em desc)
          r,←'#SampleDesc'Replace sd
          r,←Execute'$("#SampleFrame").show();'
      :EndIf

      :If 0
          :If ''≡desc
              coreonly←COREONLY
              control←NodeToCtrl¨TREE/⍨∨/¨(⊂node)⍷¨TREE
              control←(CORE∊⍨{⍵↓⍨⌽⍵⍳'.'}¨control)/⍣coreonly⊢control
              scores←⍬
              :For file :In files←ALLFILES[⍋↑ALLFILES]
                  controls←Words'Control'Section Dread'Examples/',file
                  score←2×∨/control∊controls         ⍝ 2=relevant Simple, 0=not relevant
                  score-←∨/'Advanced'⍷file           ⍝ 1=relevant Advanced
                  scores,←0⌈score
              :EndFor
              CURRFILES←'/Examples/'∘,¨(files/⍨×scores)[⍒scores/⍨×scores] ⍝ put Simple before Advanced
              out←NewDiv'.framed'
              :If 0=⍴CURRFILES
                  '.noitems'out.Add _.p,⊂'[no samples for ',(¯1↓node),' controls]'
              ⍝(2⊃r)←'_',class,'.',AddShortInfo⊂control
              ⍝(3⊃r)←(4↓⊃AddLongInfo,⊂control),' ',('0'=⊃2⊃r)/3⊃r
                  r←control(control,' Short Info')'Long info'out''
              :Else
                  :For file :In CURRFILES
                      item←('#',file,'_')'.listitem' 'title="Click here to open"'out.Add _.p
                      item.Add _.strong(↑Tail file)
                      item.Add' ',#.HTMLInput.dtlb'Description'Section Dread file
                  :EndFor
                  out.Add _.Handler'.listitem' 'click' 'OnGS'
                  title←('All '/⍨coreonly<1=⍴node),(coreonly/'Core '),(Clean node),' controls'
              ⍝desc←'Click on a button below to select a sample.'

              :EndIf
          :Else

          :EndIf
      :EndIf

      scores←⍬
      :For file :In ALLFILES
          controls←Words'Control'Section Dread'Examples/',file
          score←102-2×(50↑controls)⍳⊂control ⍝ 100=most relevant, 0=not relevant at all
          score-←∨/'Advanced'⍷file           ⍝ subtract 1 if advanced example
          scores,←0⌈score
      :EndFor
      CURRFILES←'/Examples/'∘,¨(ALLFILES/⍨×scores)[⍒scores/⍨×scores] ⍝ sort descending by score
      out←NewDiv'.framed'
      :If 1<⍴CURRFILES
          :For file :In 1↓CURRFILES
              item←('#',file,'_')'.listitem' 'title="Click here to open"'out.Add _.p
              item.Add _.strong(↑Tail file)
              item.Add' ',#.HTMLInput.dtlb'Description'Section Dread file
          :EndFor
          out.Add _.Handler'.listitem' 'click' 'OnGS'
      :EndIf

      r,←'#rel'Replace out

      r,←(×≢url)/'#SampleFrame'Replace Frame url

      r,←'#PopLink'Replace NewWinA'Pop out'url
      r,←'#SampleSource'Replace'small;border:none'#.HTMLInput.APLToHTMLColour code
      url←∊'/Documentation/DyalogAPIs/WidgetDoc?namespace=_' '&widget=',¨(⊢End{⍺ ⍵}⌽End)1↓CURRCTRL
      :If _what≡'treeA' ⋄ r,←'#DocLink'Replace NewWinA'View documentation'url
      :Else ⋄ r,←'#DocLink'Replace'' ⋄ :EndIf
      r,←'[for=''ShowRelated'']'Replace{'Show ',(⍕⍵),' related sample',((⍵≠1)/'s'),'.'}0⌈¯1+⍴CURRFILES
      :If (0=⍴CURRFILES)∧_what≡'treeA'
          r,←Execute'$("#CtlDescs").show();'
          r,←'#SampleDesc'Replace'.noitems'New _.p,⊂'[no samples using ',control,']'
          r,←'#PopLink'Replace''
          r,←'#SampleSource'Replace''
          r,←'#SampleFrame'Replace''
      :EndIf
    ∇

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

    :SECTION OBSOLETE ⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝⍝
      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
      }

    ∇ r←OnNavBut;file;item;out;title;desc
      :Access Public
      r←''
      :Select 2↓_what
      :Case 'related'
          SAMPLE←0
          out←NewDiv'.framed'
          :For file :In CURRFILES
              item←('#',file,'_')'.listitem' 'title="Click here to open"'out.Add _.p
              item.Add _.strong(Clean↑Tail file)
              item.Add' ',#.HTMLInput.dtlb'Description'Section Dread file
          :EndFor
          out.Add _.Handler'.listitem' 'click' 'OnGS'
          title←''⍝NodeToCtrl node
          desc←'Click on a button below to select a sample.'
          r←GenJS title title desc out''
      :Case 'source'
          :If SAMPLE
              r←'#SampleSource'Replace'small;border:none'#.HTMLInput.APLToHTMLColour Dread⊃CURRFILES
              r,←Execute'location.replace("#source");'
          :EndIf
      :Case 'info'
          :If SAMPLE∧''≢CURRCTRL
              r←Execute'window.open',P Q∊∊'/Documentation/DyalogAPIs/WidgetDoc?namespace=_' '&widget=',¨(⊢End{⍺ ⍵}⌽End)CURRCTRL
          :EndIf
      :Case 'out'
          r←SAMPLE/Execute'window.open',P Q⊃CURRFILES
      :Case 'urce' ⍝ n.b. 2↓
          r←Execute'scroll(0,0);'
      :EndSelect
    ∇

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

    ∇ r←OnTree;node;descs;out;items;spacectrl;control;spacedir;files;file;i;url;code;ctrlsec;desc;item;title;currctrls;core;names;class;group;cat;score
      :Access Public
      node←GetNode
      :If '0'∊node ⍝ Core or 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⌽core↓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←0 2⍴0 'url' ⍝ Score URL
          :For file :In files
              url←spacedir,'/',file
              CURRFILES⍪←(spacectrl ScoreIn url)url
          :EndFor
          CURRFILES⌿⍨←×CURRFILES[;1] ⍝ Weed out 0-score samples
          :If 0∊⍴CURRFILES ⍝ Failed to find a sample, so perform a search instead
              STR←spacectrl ⍝ Pass implicit argument to OnSearch
              r←OnSearch ⍝ Capture and modify result
              (1⊃r)←spacectrl
              (2⊃r)←'_',class,'.',AddShortInfo⊂control
              (3⊃r)←(4↓⊃AddLongInfo,⊂control),' ',('0'=⊃2⊃r)/3⊃r
          :Else
              CURRFILES←CURRFILES[⍋CURRFILES[;1];2] ⍝ Sort by score
              r←control Update⊃CURRFILES
          :EndIf
      :EndIf
      r←GenJS r
    ∇
    :ENDSECTION

:EndClass


strings.commaFmt: replace high minus by low

:Namespace Strings

    (⎕IO ⎕ML)←1

    lower←'abcdefghijklmnopqrstuvwxyzáâãçèêëìíîïðòóôõùúûýàäåæéñöøü'
    upper←'ABCDEFGHIJKLMNOPQRSTUVWXYZÁÂÃÇÈÊËÌÍÎÏÐÒÓÔÕÙÚÛÝÀÄÅÆÉÑÖØÜ'

    ∇ r←lc r;m
      m←r∊upper
      (m/r)←lower[upper⍳m/r]
    ∇

    ∇ r←uc r;m
      m←r∊lower
      (m/r)←upper[lower⍳m/r]
    ∇

    vtom←{⎕ML←3 ⋄ ⍺←' ' ⋄ ⊃⍵⊂⍨~⍵∊⍺} ⍝ vector to matrix

    ∇ r←dlb x
    ⍝ Delete Leading Blanks
      r←(+/∧\' '=x)↓x
    ∇

    ∇ r←dtb x
    ⍝ Delete Trailing Blanks
      r←(-+/∧\' '=⌽x)↓x
    ∇

    ∇ r←deb x
    ⍝ Delete extraneous blanks
      r←{1↓¯1↓(~'  '⍷⍵)/⍵}' ',x,' '
    ∇

    ∇ r←dmb x;m
    ⍝ Delete multiple blanks
      r←(m⍲1⌽m←' '=x)/x
    ∇

    ∇ str←str subst(from to);m;⎕IO;i
    ⍝ simple string substitution
      ⎕IO←0
      :Trap 0
          str←((esc from)⎕R(esc to))str
      :Else
          →(∨/m←from⍷str)↓0
          i←m⍳1
          str←((i↑str),to,(i+⍴from)↓str)subst(from to)
      :EndTrap
    ∇

    esc←{m←∊(-1+⍵∊'%\')↑¨1 ⋄ r←m\⍵ ⋄ ((~m)/r)←'\' ⋄ r }

    scaledFmt←{0=⍵:' 0' ⋄ ⍵{((1+⍵)⊃(⊂''),'KMGTQP'),⍨(1⌊⍵)⍕⍺÷10*3×⍵}⌊3÷⍨10⍟⍵}

    tonum←{0∊⍴⍵:⍬ ⋄ w←⍵ ⋄ ((w='-')/w)←'¯'⋄2 1⊃⎕VFI w}

    ∇ r←{d}commaFmt n
      :If 0=⎕NC'd'
          r←(,¨'CI20'∘⎕FMT¨n)~¨' '
      :Else
          r←(,¨('CF20.',⍕d)∘⎕FMT¨n)~¨' '
      :EndIf
      (∊r)←{w←⍵ ⋄ w⊣((w='¯')/w)←'-'}∊r
    ∇

    beginsWith←{(,⍺){⍵≡(⍴⍵)↑⍺},⍵}
    endsWith←{(,⍺){⍵≡(-⍴⍵)↑⍺},⍵}

:EndNamespace

:Namespace Strings

    (⎕IO ⎕ML)←1

    lower←'abcdefghijklmnopqrstuvwxyzáâãçèêëìíîïðòóôõùúûýàäåæéñöøü'
    upper←'ABCDEFGHIJKLMNOPQRSTUVWXYZÁÂÃÇÈÊËÌÍÎÏÐÒÓÔÕÙÚÛÝÀÄÅÆÉÑÖØÜ'

    ∇ r←lc r;m
      m←r∊upper
      (m/r)←lower[upper⍳m/r]
    ∇

    ∇ r←uc r;m
      m←r∊lower
      (m/r)←upper[lower⍳m/r]
    ∇

    vtom←{⎕ML←3 ⋄ ⍺←' ' ⋄ ⊃⍵⊂⍨~⍵∊⍺} ⍝ vector to matrix

    ∇ r←dlb x
    ⍝ Delete Leading Blanks
      r←(+/∧\' '=x)↓x
    ∇

    ∇ r←dtb x
    ⍝ Delete Trailing Blanks
      r←(-+/∧\' '=⌽x)↓x
    ∇

    ∇ r←deb x
    ⍝ Delete extraneous blanks
      r←{1↓¯1↓(~'  '⍷⍵)/⍵}' ',x,' '
    ∇

    ∇ r←dmb x;m
    ⍝ Delete multiple blanks
      r←(m⍲1⌽m←' '=x)/x
    ∇

    ∇ str←str subst(from to);m;⎕IO;i
    ⍝ simple string substitution
      ⎕IO←0
      :Trap 0
          str←((esc from)⎕R(esc to))str
      :Else
          →(∨/m←from⍷str)↓0
          i←m⍳1
          str←((i↑str),to,(i+⍴from)↓str)subst(from to)
      :EndTrap
    ∇

    esc←{m←∊(-1+⍵∊'%\')↑¨1 ⋄ r←m\⍵ ⋄ ((~m)/r)←'\' ⋄ r }

    scaledFmt←{0=⍵:' 0' ⋄ ⍵{((1+⍵)⊃(⊂''),'KMGTQP'),⍨(1⌊⍵)⍕⍺÷10*3×⍵}⌊3÷⍨10⍟⍵}

    tonum←{0∊⍴⍵:⍬ ⋄ w←⍵ ⋄ ((w='-')/w)←'¯'⋄2 1⊃⎕VFI w}

    ∇ r←{d}commaFmt n
      :If 0=⎕NC'd'
          r←(,¨'CI20'∘⎕FMT¨n)~¨' '
      :Else
          r←(,¨('CF20.',⍕d)∘⎕FMT¨n)~¨' '
      :EndIf
    ∇

    beginsWith←{(,⍺){⍵≡(⍴⍵)↑⍺},⍵}
    endsWith←{(,⍺){⍵≡(-⍴⍵)↑⍺},⍵}

:EndNamespace


Fixed typo

Fixed typo

:Namespace WC2 ⍝ Web Content Construction for use with HTMLRenderer
⍝ Populated from /HTML/WC2/

    Initialized←0

    APLVersion←{⊃(//)⎕VFI ⍵/⍨2>+\'.'=⍵}2⊃#.⎕WG 'APLVersion'
    SourceFile←{0::'' ⋄ 6:: 4⊃⊃5177⌶⍬ ⋄ SALT_Data.SourceFile}

    ∇ r←{force}Init arg;msPath;path;boot;appPath;app;t
    ⍝ Bootstrap load from
      →0⍴⍨1=⊃r←(16>APLVersion)/1 'Dyalog v16.0 or later is required to use HTMLRenderer-based features'

      force←{6::⍵ ⋄ force}0
      :If Initialized>force
          →0⊣r←¯1 'Already initialized'
      :EndIf

      arg←,⊆arg
      (msPath appPath)←2↑arg,(⍴arg)↓'' ''

      :If 0∊⍴msPath ⍝ if no MiServer path specified, try looking at source path
      :AndIf ~0∊⍴t←SourceFile ⍬
          msPath←⊃1 ⎕NPARTS t,'/../../'
      :EndIf

      :If ~⎕NEXISTS path←∊1 ⎕NPARTS msPath,'/'
          →0⊣r←2 'MiServer path not found: "',msPath,'"'
      :EndIf

      :If ~0∊⍴app←appPath
      :AndIf ~⎕NEXISTS app←∊1 ⎕NPARTS appPath,'/'
          →0⊣r←3 'Application path not found: "',appPath,'"'
      :EndIf

      :If ~⎕NEXISTS boot←path,'Core/Boot.dyalog'
          →0⊣r←4 'Path does not appear to be MiServer: "',msPath,'"'
      :EndIf

      ⎕SE.SALT.Load boot,' -target=#'
      #.Boot.(MSRoot AppRoot)←path app
      #.Boot.Load app
      #.Boot.ms←1 #.Boot.Init #.Boot.ConfigureServer app
      #.Boot.(Configure ms)
      Initialized←1
      r←0 'WC2 initialized'
    ∇

:EndNamespace

:Namespace WC2 ⍝ Web Content Contruction for use with HTMLRenderer
⍝ Populated from /HTML/WC2/

    Initialized←0

    APLVersion←{⊃(//)⎕VFI ⍵/⍨2>+\'.'=⍵}2⊃#.⎕WG 'APLVersion'
    SourceFile←{0::'' ⋄ 6:: 4⊃⊃5177⌶⍬ ⋄ SALT_Data.SourceFile}

    ∇ r←{force}Init arg;msPath;path;boot;appPath;app;t
    ⍝ Bootstrap load from
      →0⍴⍨1=⊃r←(16>APLVersion)/1 'Dyalog v16.0 or later is required to use HTMLRenderer-based features'

      force←{6::⍵ ⋄ force}0
      :If Initialized>force
          →0⊣r←¯1 'Already initialized'
      :EndIf

      arg←,⊆arg
      (msPath appPath)←2↑arg,(⍴arg)↓'' ''

      :If 0∊⍴msPath ⍝ if no MiServer path specified, try looking at source path
      :AndIf ~0∊⍴t←SourceFile ⍬
          msPath←⊃1 ⎕NPARTS t,'/../../'
      :EndIf

      :If ~⎕NEXISTS path←∊1 ⎕NPARTS msPath,'/'
          →0⊣r←2 'MiServer path not found: "',msPath,'"'
      :EndIf

      :If ~0∊⍴app←appPath
      :AndIf ~⎕NEXISTS app←∊1 ⎕NPARTS appPath,'/'
          →0⊣r←3 'Application path not found: "',appPath,'"'
      :EndIf

      :If ~⎕NEXISTS boot←path,'Core/Boot.dyalog'
          →0⊣r←4 'Path does not appear to be MiServer: "',msPath,'"'
      :EndIf

      ⎕SE.SALT.Load boot,' -target=#'
      #.Boot.(MSRoot AppRoot)←path app
      #.Boot.Load app
      #.Boot.ms←1 #.Boot.Init #.Boot.ConfigureServer app
      #.Boot.(Configure ms)
      Initialized←1
      r←0 'WC2 initialized'
    ∇

:EndNamespace


Improved 'bench'

Improved 'bench'

$dot ← {
  WA ← (1↓⍴⍵),⍴⍺
  KA ← (⊃⍴⍴⍺)-1
  VA ← ⍳ ⊃ ⍴WA
  ZA ← (KA⌽¯1↓VA),¯1↑VA
  TA ← ZA⍉WA⍴⍺    ⍝ Replicate, transpose
  WB ← (¯1↓⍴⍺),⍴⍵
  KB ← ⊃ ⍴⍴⍺
  VB ← ⍳ ⊃ ⍴WB
  ZB0 ← (-KB) ↓ KB ⌽ ⍳(⊃⍴VB)
  ZB ← (¯1↓(⍳ KB)),ZB0,KB
  TB ← ZB⍉WB⍴⍵    ⍝ Replicate, transpose
  ⍺⍺ / TA ⍵⍵ TB   ⍝ Compute the result
}

$out ← {
  A ← ⍺
  B ← ⍵
  T ← (⊃(⍴⍴A))⌽⍳⊃⍴(⍴B),⍴A
  x ← T ⍉ ((⍴B),(⍴A)) ⍴ A
  y ← ((⍴A),(⍴B)) ⍴ B
  x ⍺⍺ y
}

$slashbar ← {
  ⍉ ⍺⍺ / ⍉ ⍵
}

$log ← {
  ⍝ Dyadic logarithm
  (⍟⍵)÷⍟⍺
}

ReadCSVDouble ← ⎕ReadDoubleVecFile
ReadCSVInt ← ⎕ReadIntVecFile
xor ← ⎕INT32XOR
and ← ⎕INT32AND
sll ← ⎕INT32SHL
srl ← ⎕INT32SHR
testBit ← { 0≠⍵ and 1 sll (⍺-1) }

now ← ⎕NOW

bench ← {
  init ← ⍵
  f ← ⍺⍺
  g ← { ⍵ ⋄ f init }
  r ← g init
  t0 ← now 0
  r ← (g ⍣ ⍺) r
  t1 ← now 1
  ⎕ ← 'ITERATIONS: ' , ⍕ ⍺
  ⎕ ← 'RESULT: ' , ⍕ ⍵⍵ r
  ⎕ ← 'AVGTIMING: ' , ⍕ (t1-t0)÷⍺
  1.0
}

$dot ← {
  WA ← (1↓⍴⍵),⍴⍺
  KA ← (⊃⍴⍴⍺)-1
  VA ← ⍳ ⊃ ⍴WA
  ZA ← (KA⌽¯1↓VA),¯1↑VA
  TA ← ZA⍉WA⍴⍺    ⍝ Replicate, transpose
  WB ← (¯1↓⍴⍺),⍴⍵
  KB ← ⊃ ⍴⍴⍺
  VB ← ⍳ ⊃ ⍴WB
  ZB0 ← (-KB) ↓ KB ⌽ ⍳(⊃⍴VB)
  ZB ← (¯1↓(⍳ KB)),ZB0,KB
  TB ← ZB⍉WB⍴⍵    ⍝ Replicate, transpose
  ⍺⍺ / TA ⍵⍵ TB   ⍝ Compute the result
}

$out ← {
  A ← ⍺
  B ← ⍵
  T ← (⊃(⍴⍴A))⌽⍳⊃⍴(⍴B),⍴A
  x ← T ⍉ ((⍴B),(⍴A)) ⍴ A
  y ← ((⍴A),(⍴B)) ⍴ B
  x ⍺⍺ y
}

$slashbar ← {
  ⍉ ⍺⍺ / ⍉ ⍵
}

$log ← {
  ⍝ Dyadic logarithm
  (⍟⍵)÷⍟⍺
}

ReadCSVDouble ← ⎕ReadDoubleVecFile
ReadCSVInt ← ⎕ReadIntVecFile
xor ← ⎕INT32XOR
and ← ⎕INT32AND
sll ← ⎕INT32SHL
srl ← ⎕INT32SHR
testBit ← { 0≠⍵ and 1 sll (⍺-1) }

now ← ⎕NOW

bench ← {
  g ← ⍺⍺
  f ← { ⍵ ⋄ ⌷ g 0 }
  t0 ← now 0
  r ← (f ⍣ ⍵) 0
  t1 ← now 1
  r,(t1-t0)÷⍵
}


Revert "rm "0=⍴⍴⍵:"; the joys of being dimension-agnostic"

This reverts commit c7ece6626849698c9480e23228bb2f48cd945f15.

issue #67


jqSortable now accepts list of items, including empty list. Updated internal doc.

:Class jqSortable : #._JQ._jqUIWidget

⍝ Description:: jQueryUI Sortable widget
⍝ Constructor:: [items|selector]
⍝ items     - vector of items to make sortable
⍝ selector - the jQuery/CSS selector to which to apply sortable interaction
⍝ Public Fields::
⍝ Items     - vector of items to make sortable
⍝ Selector - the jQuery/CSS selector to which to apply sortable interaction
⍝ Examples::
⍝ Add _.jqSortable '#myul'             ⍝ jqSortable will use the already existing element #myul
⍝ Add _.jqSortable 'One' 'Two' 'Three' ⍝ jqSortable will build a <ul> / <li> structure
⍝ Add _.jqSortable ''                  ⍝ jqSortable will insert an empty <ul> structure where items can be dropped

    :field public shared readonly DocBase←'https://jqueryui.com/sortable/'
    :field public shared readonly ApiLevel←3
    :field public shared readonly IntEvt←'activate' 'beforeStop' 'change' 'create' 'deactivate' 'out' 'over' 'receive' 'remove' 'sort' 'start' 'stop' 'update'
    :Field Public Items←UNDEF         ⍝ vector of items

    ∇ Make
      :Access public
      :Implements constructor
      JQueryFn←'sortable'
      InternalEvents←IntEvt
    ∇

    ∇ Make1 arg
      :Access public
      :Implements constructor
      JQueryFn←'sortable'
      InternalEvents←IntEvt
      :If 326≠⎕DR arg
      :AndIf ''≢arg
          Selector←arg
      :Else
          Items←arg
      :EndIf
    ∇

    ∇ r←Render
      :Access Public
      r←''
      :If UNDEF≢Items
          :If ''≢Selector
              id←Selector↓⍨'#'=⊃Selector
          :Else
              Selector←'#',SetId
          :EndIf
          r,←(id New _.Ul,⊂⊂Items).Render
      :EndIf
      r,←⎕BASE.Render
    ∇

:EndClass

:Class jqSortable : #._JQ._jqUIWidget

⍝ Description:: jQueryUI Sortable widget
⍝ Constructor:: [Selector]
⍝ Selector - the jQuery/CSS selector to which to apply sortable interaction
⍝ Public Fields::
⍝ Selector - the jQuery/CSS selector to which to apply sortable interaction

    :field public shared readonly DocBase←'https://jqueryui.com/sortable/'
    :field public shared readonly ApiLevel←3
    :field public shared readonly IntEvt←'activate' 'beforeStop' 'change' 'create' 'deactivate' 'out' 'over' 'receive' 'remove' 'sort' 'start' 'stop' 'update'

    ∇ Make
      :Access public
      :Implements constructor
      JQueryFn←'sortable'
      InternalEvents←IntEvt
    ∇

    ∇ Make1 arg
      :Access public
      :Implements constructor
      JQueryFn←'sortable'
      Selector←arg
      InternalEvents←IntEvt
    ∇

:EndClass


complete script tag

complete script tag

tag was missing

:Class Menuexample3 : MiPageSample
⍝ Demonstrate the SyncFusion ejMenu widget

    ⎕ml←⎕io←1

    ∇ r←Render;items;t;tmp;a;i;m;opt
      :Access Public
⍝      tmp←⎕XML∊example
⍝      i←a/⍳⍴a←tmp[;2]∊⊂,'a' ⋄ tmp[i-1;3]←tmp[i;3] ⋄ tmp←(~a)⌿tmp
⍝      items←↓⍉tmp[;1 3],⊂''
      Add build
      m←Add _SF.ejMenu
      opt←'{dataSource: data, id: "id", parentId: "parentId", text: "text", spriteCssClass: "sprite"}'
      'fields'm.Option opt
⍝      'datasource'm.Option'data'
 ⍝     'id'm.Option'id'
  ⍝    'parentid'm.Option'parentId'
   ⍝   'text'm.Option'text'
    ⍝  'spriteCssClass'm.Option'sprite'
     ⍝ Add m
    ∇

    ∇ m←build;P;F;C;cr
    ⍝ This data recreates SuncFusion's http://js.syncfusion.com/demos/web/#!/azure/menu/databinding-localdata page example
⍝[{id:1,parentid:null,text:"Group A"}
⍝,{id:2,parentid:null,text:"Group B"}
⍝,{id:3,parentid:null,text:"Group C"}
⍝,{id:4,parentid:null,text:"Group D"}
⍝,{id:5,parentid:null,text:"Group E"}
⍝,{id:11,parentId:1,sprite:"flag-dz",text:"Algeria"}
⍝,{id:12,parentId:1,sprite:"flag-am",text:"Armenia"}
⍝,{id:13,parentId:1,sprite:"flag-bd",text:"Bangladesh"}
⍝,{id:14,parentId:1,sprite:"flag-cu",text:"Cuba"}
⍝,{id:15,parentId:2,sprite:"flag-dk",text:"Denmark"}
⍝,{id:16,parentId:2,sprite:"flag-eg",text:"Egypt"}
⍝,{id:17,parentId:3,sprite:"flag-fi",text:"Finland"}
⍝,{id:18,parentId:3,sprite:"flag-in",text:"India"}
⍝,{id:19,parentId:3,sprite:"flag-my",text:"Malaysia"}
⍝,{id:20,parentId:4,sprite:"flag-nz",text:"New Zealand"}
⍝,{id:21,parentId:4,sprite:"flag-no",text:"Norway"}
⍝,{id:22,parentId:4,sprite:"flag-pl",text:"Poland"}
⍝,{id:23,parentId:5,sprite:"flag-ro",text:"Romania"}
⍝,{id:24,parentId:5,sprite:"flag-sg",text:"Singapore"}
⍝,{id:25,parentId:5,sprite:"flag-th",text:"Thailand"}
⍝,{id:26,parentId:5,sprite:"flag-ua",text:"Ukraine"}
⍝,{id:111,parentId:11,text:"First Place"}
⍝,{id:112,parentId:12,text:"Second Place"}
⍝,{id:113,parentId:13,text:"Third Place"}
⍝,{id:114,parentId:14,text:"Fourth Place"}
⍝,{id:115,parentId:15,text:"First Place"}
⍝,{id:116,parentId:16,text:"Second Place"}
⍝,{id:117,parentId:17,text:"Third Place"}
⍝,{id:118,parentId:18,text:"First Place"}
⍝,{id:119,parentId:19,text:"Second Place"}
⍝,{id:120,parentId:20,text:"First Place"}
⍝,{id:121,parentId:21,text:"Second Place"}
⍝,{id:122,parentId:22,text:"Third Place"}
⍝,{id:123,parentId:23,text:"Fourth Place"}
⍝,{id:124,parentId:24,text:"First Place"}
⍝,{id:125,parentId:25,text:"Second Place"}
⍝,{id:126,parentId:26,text:"Third Place"}]

      m←{'{id:',(⍕⍵),',text:"Group ',⎕A[⍵],'",parentid:null},'}¨⍳5
      C←'Algeria' 'Armenia' 'Bangladesh' 'Cuba' 'Denmark' 'Egypt' 'Finland' 'India'
      C,←'Malaysia' 'New Zealand' 'Norway' 'Poland' 'Romania' 'Singapore' 'Thailand' 'Ukraine'
      F←'flag-'∘,¨↓16 2⍴'dzambdcudkegfiinmynznoplrosgthua'
      m,←(4 2 3 3 4/⍳5){'{id:',(⍕10+⍵),',parentId:',(⍕⍺),',text:"',(⍵⊃C),'",sprite:"',(⍵⊃F),'"},'}¨⍳16
      P←'First' 'Second' 'Third' 'Fourth'[1 2 3 4 1 2 3 1 2 1 2 3 4 1 2 3],¨⊂' Place'
      m,←{'{id:',(⍕110+⍵),',parentId:',(⍕10+⍵),',text:"',(⍵⊃P),'"},'}¨⍳16
      m[⍴m]↓⍨←¯1
      m←m,¨cr←⎕UCS 13
      m←∊2⌽('];' '</script>' '<script type="text/javascript">var data = [',¨cr),m
    ∇

:EndClass

:Class Menuexample3 : MiPageSample
⍝ Demonstrate the SyncFusion ejMenu widget

    ⎕ml←⎕io←1

    ∇ r←Render;items;t;tmp;a;i;m;opt
      :Access Public
⍝      tmp←⎕XML∊example
⍝      i←a/⍳⍴a←tmp[;2]∊⊂,'a' ⋄ tmp[i-1;3]←tmp[i;3] ⋄ tmp←(~a)⌿tmp
⍝      items←↓⍉tmp[;1 3],⊂''
      Add build
      m←Add _SF.ejMenu
      opt←'{dataSource: data, id: "id", parentId: "parentId", text: "text", spriteCssClass: "sprite"}'
      'fields'm.Option opt
⍝      'datasource'm.Option'data'
 ⍝     'id'm.Option'id'
  ⍝    'parentid'm.Option'parentId'
   ⍝   'text'm.Option'text'
    ⍝  'spriteCssClass'm.Option'sprite'
     ⍝ Add m
    ∇

    ∇ m←build;P;F;C;cr
    ⍝ This data recreates SuncFusion's http://js.syncfusion.com/demos/web/#!/azure/menu/databinding-localdata page example
⍝[{id:1,parentid:null,text:"Group A"}
⍝,{id:2,parentid:null,text:"Group B"}
⍝,{id:3,parentid:null,text:"Group C"}
⍝,{id:4,parentid:null,text:"Group D"}
⍝,{id:5,parentid:null,text:"Group E"}
⍝,{id:11,parentId:1,sprite:"flag-dz",text:"Algeria"}
⍝,{id:12,parentId:1,sprite:"flag-am",text:"Armenia"}
⍝,{id:13,parentId:1,sprite:"flag-bd",text:"Bangladesh"}
⍝,{id:14,parentId:1,sprite:"flag-cu",text:"Cuba"}
⍝,{id:15,parentId:2,sprite:"flag-dk",text:"Denmark"}
⍝,{id:16,parentId:2,sprite:"flag-eg",text:"Egypt"}
⍝,{id:17,parentId:3,sprite:"flag-fi",text:"Finland"}
⍝,{id:18,parentId:3,sprite:"flag-in",text:"India"}
⍝,{id:19,parentId:3,sprite:"flag-my",text:"Malaysia"}
⍝,{id:20,parentId:4,sprite:"flag-nz",text:"New Zealand"}
⍝,{id:21,parentId:4,sprite:"flag-no",text:"Norway"}
⍝,{id:22,parentId:4,sprite:"flag-pl",text:"Poland"}
⍝,{id:23,parentId:5,sprite:"flag-ro",text:"Romania"}
⍝,{id:24,parentId:5,sprite:"flag-sg",text:"Singapore"}
⍝,{id:25,parentId:5,sprite:"flag-th",text:"Thailand"}
⍝,{id:26,parentId:5,sprite:"flag-ua",text:"Ukraine"}
⍝,{id:111,parentId:11,text:"First Place"}
⍝,{id:112,parentId:12,text:"Second Place"}
⍝,{id:113,parentId:13,text:"Third Place"}
⍝,{id:114,parentId:14,text:"Fourth Place"}
⍝,{id:115,parentId:15,text:"First Place"}
⍝,{id:116,parentId:16,text:"Second Place"}
⍝,{id:117,parentId:17,text:"Third Place"}
⍝,{id:118,parentId:18,text:"First Place"}
⍝,{id:119,parentId:19,text:"Second Place"}
⍝,{id:120,parentId:20,text:"First Place"}
⍝,{id:121,parentId:21,text:"Second Place"}
⍝,{id:122,parentId:22,text:"Third Place"}
⍝,{id:123,parentId:23,text:"Fourth Place"}
⍝,{id:124,parentId:24,text:"First Place"}
⍝,{id:125,parentId:25,text:"Second Place"}
⍝,{id:126,parentId:26,text:"Third Place"}]

      m←{'{id:',(⍕⍵),',text:"Group ',⎕A[⍵],'",parentid:null},'}¨⍳5
      C←'Algeria' 'Armenia' 'Bangladesh' 'Cuba' 'Denmark' 'Egypt' 'Finland' 'India'
      C,←'Malaysia' 'New Zealand' 'Norway' 'Poland' 'Romania' 'Singapore' 'Thailand' 'Ukraine'
      F←'flag-'∘,¨↓16 2⍴'dzambdcudkegfiinmynznoplrosgthua'
      m,←(4 2 3 3 4/⍳5){'{id:',(⍕10+⍵),',parentId:',(⍕⍺),',text:"',(⍵⊃C),'",sprite:"',(⍵⊃F),'"},'}¨⍳16
      P←'First' 'Second' 'Third' 'Fourth'[1 2 3 4 1 2 3 1 2 1 2 3 4 1 2 3],¨⊂' Place'
      m,←{'{id:',(⍕110+⍵),',parentId:',(⍕10+⍵),',text:"',(⍵⊃P),'"},'}¨⍳16
      m[⍴m]↓⍨←¯1
      m←m,¨cr←⎕UCS 13
      m←∊1⌽('];' '<script type="text/javascript">var data = [',¨cr),m
    ∇

:EndClass


Speedup by not requiring SF (incl. jQueryUI) for every MiPageSample.

:Class MiPageSample : #.MiPage
   ⍝ This is a template that "wraps" the page content by
   ⍝ - adding a header and footer
   ⍝ - adding a handler that will toggle the display of the web page and its APL source code

    ∇ {r}←Wrap;lang;server;c;src;ctrlsdiv
      :Access Public

      :If 0∊⍴Get'nowrapper'
          server←_Request.Server

        ⍝ we use Syncfusion (which uses jQuery) to set up the controls to do cool stuff
         ⍝ Use'Syncfusion' ⍝ this is a resource defined in Config/Resources.xml ⍝ Will be loaded anyway if needed - speedup if not

        ⍝ set the title display in the browser to the name of the application defined in Config/Server.xml
          Add _.title server.Config.Name

        ⍝ add a link to our CSS stylesheet
          _CssReset←'/Styles/cssreset.css'
          _CssOverride←'/Styles/sampleStyles.css'

        ⍝ set a meta tag to make it explicitly UTF-8
          (Add _.meta).Set'http-equiv="content-type" content="text/html;charset=UTF-8"'

        ⍝ wrap the content of the  element in a div
          Body.Push _.div'id="contentblock"'

        ⍝ add a hidden division to the body containing the APL source code
          (Add _.div(#.HTMLInput.APLToHTMLColour src←⎕SRC⊃⊃⎕CLASS ⎕THIS)).Set'id="codeblock"' 'style="display: none;"'

        ⍝ create a division with info about the controls used
          ctrlsdiv←CtrlsDiv src

        ⍝ extract the content and re-write it in parallel to the controls info div
          c←Body.Content
          Body.Content←''
          (Body.Add _.StackPanel ctrlsdiv c).Horizontal←1

        ⍝ wrap the content of the  element in a div
          Body.Push _.div'class="bodyblock"'

        ⍝ add the footer to the bottom of the page
          Add #.Files.GetText server.Config.Root,'Styles\footer.txt'

        ⍝ add the header to the top of the page and wrap the body in a div with id="wrapper"
          Body.Push #.Files.GetText server.Config.Root,'Styles\banner.txt'

          Body.Push _.div'id="wrapper"'

        ⍝ add a JQuery event handler to toggle the web page/APL source code
          Add _.Script'$(function(){$("#bannerimage").on("click", function(evt){$("#contentblock,#codeblock,.widgethelp").toggle(400,"swing");});});'

        ⍝ set the language for the page
          lang←server.Config.Lang ⍝ use the language specified in Server.xml
          Set'lang="',lang,'" xml:lang="',lang,'" xmlns="http://www.w3.org/1999/xhtml"'
      :Else
          Head.Add _.StyleSheet'/Styles/sampleStyles.css'
          Body.class←'bodyblock'
      :EndIf
     ⍝ call the base class Wrap function

      r←⎕BASE.Wrap
    ∇

    ∇ r←CtrlsDiv source;control;controls;lia;ns;ul;cali;h
     ⍝ Creates a div with info on the controls used in source

     ⍝ First we extract proper lists of controls
      controls←4/⊂''
      source←(⍳∘'⍝'↑⊢)¨source      ⍝ remove comments
      source←{⍵/⍨~≠\''''=⍵}¨source ⍝ remove strings but leaves one ' as separator
      :For control :In 'Handler' 'Position'~⍨∪('_(DC|SF|JQ|html|)\.(\w+)'⎕S'\2')source ⍝ find controls
         ⍝ add to right list: /¯¯¯¯¯SF¯¯¯¯¯\  /¯¯¯¯¯JQ¯¯¯¯¯\  /¯¯¯¯DC¯¯¯¯\ /¯html
          controls[⊃2 3 1 4/⍨('ej'≡2↑control)('jq'≡2↑control)(⎕A∊⍨⊃control)1],←⊂⊂control
      :EndFor
      controls←{⍵[⍋↑⍵]}¨controls   ⍝ sort the lists

     ⍝ Now we create and populate the info div
      r←'.widgethelp'New _.div'This Page Contains
'
      lia←'
',control,'
'
              :Select ns
              :Case 1 ⋄ ul.Add lia,'/Documentation/DyalogAPIs/WidgetDoc?namespace=_DC&widget=',control,cali           ⍝ DC
              :Case 2 ⋄ ul.Add lia,h,'help.syncfusion.com/js/',(#.Strings.lc(2×'ej'≡2↑control)↓control,'/overview'),cali ⍝ SF
              :Case 3 ⋄ ul.Add lia,h,'jqueryui.com/',(2↓control),cali                                                 ⍝ JQ
              :Case 4 ⋄ ul.Add lia,h,'www.w3schools.com/tags/tag_',(('\d'⎕R'n')control),'.asp',cali                   ⍝ html
              :EndSelect
          :EndFor
      :EndFor
    ∇

    ∇ r←Walk content;e
      r←⍬
      :For e :In content
          :If (#.HtmlElement.isClass e)∨#.HtmlElement.isInstance e
              r←r,⊃⎕CLASS e
              :Trap 6
                  r,←Walk e.Content
              :EndTrap
          :EndIf
      :EndFor
    ∇

⍝    ∇ r←FormatControls controls;ctrls;ns;desc;field;n;ctrl;i;c;ref;l;u;nss;item;cn
⍝      ctrls←⊃{⍺ ⍵}#.Utils.∆key/↓[1]0 1↓↑{⎕ML←3 ⋄ {⍵⊂⍨⍵≠'.'}⍕⍵}¨controls
⍝      field←{0::'' ⋄ ⍺⍎⍵}
⍝      nss←'_DC' '_SF' '_JQ' '_html'
⍝      desc←'Dyalog Controls' 'Syncfusion Widgets' 'jQuery Widgets' 'Native HTML5 Elements'
⍝      (r←⎕NEW _.div).class←'widgethelp'
⍝      r.Add'This Page Contains
'
⍝      :Trap 0
⍝          :For (ns ctrl i) :In ctrls{↓(⍺,⍵)[⍋⍵;]}nss⍳{(⊃⍣(¯1+≡⍵))⍵}¨ctrls[;1]
⍝              (r.Add _.span(i⊃desc)).class←'widgetNs'
⍝              u←r.Add _.ul
⍝              n←#.⍎ns←⊃ns
⍝              :For c :In ctrl
⍝                  ref←n.⍎c
⍝                  item←⍬
⍝                  :Select ns
⍝                  :Case '_DC'
⍝                      item←u.Add _.li(New _.a(c(('href=/Documentation/DyalogAPIs/WidgetDoc?namespace=',ns,'&widget=',c)'target=_blank')))
⍝                  :Case '_SF'
⍝                      item←u.Add _.li(New _.a c(('href'('http://js.syncfusion.com/demos/web/default.htm#!/azure/',#.Strings.lc(2×'ej'≡2↑c)↓c,'/'))('target' '_blank')))
⍝                  :Case '_JQ'
⍝                      item←u.Add _.li(New _.a c(('href'('http://jqueryui.com/',(2×'jq'≡2↑c)↓c))('target' '_blank')))
⍝                  :Case '_html'
⍝                      cn←c
⍝                      :If ~0∊⍴('^h[1-6]$'⎕S{⍵.Match})c
⍝                          cn←'hn'
⍝                      :EndIf
⍝                      item←u.Add _.li(New _.a c(('href'('http://www.w3schools.com/tags/tag_',cn,'.asp'))('target' '_blank')))
⍝                  :EndSelect
⍝                  :If 0∊⍴item
⍝                      u.Add _.li c
⍝                  :EndIf
⍝              :EndFor
⍝          :EndFor
⍝      :EndTrap
⍝    ∇

:EndClass

:Class MiPageSample : #.MiPage
   ⍝ This is a template that "wraps" the page content by
   ⍝ - adding a header and footer
   ⍝ - adding a handler that will toggle the display of the web page and its APL source code

    ∇ {r}←Wrap;lang;server;c;src;ctrlsdiv
      :Access Public

      :If 0∊⍴Get'nowrapper'
          server←_Request.Server

        ⍝ we use Syncfusion (which uses jQuery) to set up the controls to do cool stuff
          Use'Syncfusion' ⍝ this is a resource defined in Config/Resources.xml

        ⍝ set the title display in the browser to the name of the application defined in Config/Server.xml
          Add _.title server.Config.Name

        ⍝ add a link to our CSS stylesheet
          _CssReset←'/Styles/cssreset.css'
          _CssOverride←'/Styles/sampleStyles.css'

        ⍝ set a meta tag to make it explicitly UTF-8
          (Add _.meta).Set'http-equiv="content-type" content="text/html;charset=UTF-8"'

        ⍝ wrap the content of the  element in a div
          Body.Push _.div'id="contentblock"'

        ⍝ add a hidden division to the body containing the APL source code
          (Add _.div(#.HTMLInput.APLToHTMLColour src←⎕SRC⊃⊃⎕CLASS ⎕THIS)).Set'id="codeblock"' 'style="display: none;"'

        ⍝ create a division with info about the controls used
          ctrlsdiv←CtrlsDiv src

        ⍝ extract the content and re-write it in parallel to the controls info div
          c←Body.Content
          Body.Content←''
          (Body.Add _.StackPanel ctrlsdiv c).Horizontal←1

        ⍝ wrap the content of the  element in a div
          Body.Push _.div'class="bodyblock"'

        ⍝ add the footer to the bottom of the page
          Add #.Files.GetText server.Config.Root,'Styles\footer.txt'

        ⍝ add the header to the top of the page and wrap the body in a div with id="wrapper"
          Body.Push #.Files.GetText server.Config.Root,'Styles\banner.txt'

          Body.Push _.div'id="wrapper"'

        ⍝ add a JQuery event handler to toggle the web page/APL source code
          Add _.Script'$(function(){$("#bannerimage").on("click", function(evt){$("#contentblock,#codeblock,.widgethelp").toggle(400,"swing");});});'

        ⍝ set the language for the page
          lang←server.Config.Lang ⍝ use the language specified in Server.xml
          Set'lang="',lang,'" xml:lang="',lang,'" xmlns="http://www.w3.org/1999/xhtml"'
      :Else
          Head.Add _.StyleSheet'/Styles/sampleStyles.css'
          Body.class←'bodyblock'
      :EndIf
     ⍝ call the base class Wrap function

      r←⎕BASE.Wrap
    ∇

    ∇ r←CtrlsDiv source;control;controls;lia;ns;ul;cali;h
     ⍝ Creates a div with info on the controls used in source

     ⍝ First we extract proper lists of controls
      controls←4/⊂''
      source←(⍳∘'⍝'↑⊢)¨source      ⍝ remove comments
      source←{⍵/⍨~≠\''''=⍵}¨source ⍝ remove strings but leaves one ' as separator
      :For control :In 'Handler' 'Position'~⍨∪('_(DC|SF|JQ|html|)\.(\w+)'⎕S'\2')source ⍝ find controls
         ⍝ add to right list: /¯¯¯¯¯SF¯¯¯¯¯\  /¯¯¯¯¯JQ¯¯¯¯¯\  /¯¯¯¯DC¯¯¯¯\ /¯html
          controls[⊃2 3 1 4/⍨('ej'≡2↑control)('jq'≡2↑control)(⎕A∊⍨⊃control)1],←⊂⊂control
      :EndFor
      controls←{⍵[⍋↑⍵]}¨controls   ⍝ sort the lists

     ⍝ Now we create and populate the info div
      r←'.widgethelp'New _.div'This Page Contains
'
      lia←'
',control,'
'
              :Select ns
              :Case 1 ⋄ ul.Add lia,'/Documentation/DyalogAPIs/WidgetDoc?namespace=_DC&widget=',control,cali           ⍝ DC
              :Case 2 ⋄ ul.Add lia,h,'help.syncfusion.com/js/',(#.Strings.lc(2×'ej'≡2↑control)↓control,'/overview'),cali ⍝ SF
              :Case 3 ⋄ ul.Add lia,h,'jqueryui.com/',(2↓control),cali                                                 ⍝ JQ
              :Case 4 ⋄ ul.Add lia,h,'www.w3schools.com/tags/tag_',(('\d'⎕R'n')control),'.asp',cali                   ⍝ html
              :EndSelect
          :EndFor
      :EndFor
    ∇

    ∇ r←Walk content;e
      r←⍬
      :For e :In content
          :If (#.HtmlElement.isClass e)∨#.HtmlElement.isInstance e
              r←r,⊃⎕CLASS e
              :Trap 6
                  r,←Walk e.Content
              :EndTrap
          :EndIf
      :EndFor
    ∇

⍝    ∇ r←FormatControls controls;ctrls;ns;desc;field;n;ctrl;i;c;ref;l;u;nss;item;cn
⍝      ctrls←⊃{⍺ ⍵}#.Utils.∆key/↓[1]0 1↓↑{⎕ML←3 ⋄ {⍵⊂⍨⍵≠'.'}⍕⍵}¨controls
⍝      field←{0::'' ⋄ ⍺⍎⍵}
⍝      nss←'_DC' '_SF' '_JQ' '_html'
⍝      desc←'Dyalog Controls' 'Syncfusion Widgets' 'jQuery Widgets' 'Native HTML5 Elements'
⍝      (r←⎕NEW _.div).class←'widgethelp'
⍝      r.Add'This Page Contains
'
⍝      :Trap 0
⍝          :For (ns ctrl i) :In ctrls{↓(⍺,⍵)[⍋⍵;]}nss⍳{(⊃⍣(¯1+≡⍵))⍵}¨ctrls[;1]
⍝              (r.Add _.span(i⊃desc)).class←'widgetNs'
⍝              u←r.Add _.ul
⍝              n←#.⍎ns←⊃ns
⍝              :For c :In ctrl
⍝                  ref←n.⍎c
⍝                  item←⍬
⍝                  :Select ns
⍝                  :Case '_DC'
⍝                      item←u.Add _.li(New _.a(c(('href=/Documentation/DyalogAPIs/WidgetDoc?namespace=',ns,'&widget=',c)'target=_blank')))
⍝                  :Case '_SF'
⍝                      item←u.Add _.li(New _.a c(('href'('http://js.syncfusion.com/demos/web/default.htm#!/azure/',#.Strings.lc(2×'ej'≡2↑c)↓c,'/'))('target' '_blank')))
⍝                  :Case '_JQ'
⍝                      item←u.Add _.li(New _.a c(('href'('http://jqueryui.com/',(2×'jq'≡2↑c)↓c))('target' '_blank')))
⍝                  :Case '_html'
⍝                      cn←c
⍝                      :If ~0∊⍴('^h[1-6]$'⎕S{⍵.Match})c
⍝                          cn←'hn'
⍝                      :EndIf
⍝                      item←u.Add _.li(New _.a c(('href'('http://www.w3schools.com/tags/tag_',cn,'.asp'))('target' '_blank')))
⍝                  :EndSelect
⍝                  :If 0∊⍴item
⍝                      u.Add _.li c
⍝                  :EndIf
⍝              :EndFor
⍝          :EndFor
⍝      :EndTrap
⍝    ∇

:EndClass


_DC.Icon fix

_DC.Icon fix

:class Icon : #._html.span
⍝ Description:: Dyalog Icon widget
⍝ Constructor:: [spec]
⍝ spec - either a single or pair of string which specify the icon(s) to use
⍝        each consists of a vendor-prefix, dash, icon-name, and optionally space-separated modifiers
⍝        when two strings are used, it represents a "stacked" icon, with second icon overlayed on the first
⍝        "stacked" icons are a feature of FontAwesome icons - using other icons may or may not have the desired visual effect
⍝
⍝ Public Fields::
⍝ Spec  - either a single or pair of string which specify the icon(s) to use
⍝         each consists of a vendor-prefix, dash, icon-name, and optionally space-separated modifiers
⍝         when two strings are used, it represents a "stacked" icon, with second icon overlayed on the first
⍝         "stacked" icons are a feature of FontAwesome icons - using other icons may or may not have the desired visual effect
⍝
⍝ Examples::
⍝ Add _.Icon 'fa-cloud-upload'    ⍝ FontAwesome: http://fontawesome.io/icons/
⍝ Add _.Icon 'md-fingerprint'     ⍝ Google Material Design: https://design.google.com/icons/
⍝ Add _.Icon 'e-delete-column_01' ⍝ Syncfusion Essential JavaScript: http://js.syncfusion.com/demos/web/#!/azure/icon/EJIcons
⍝ '.fa-spin' Add _.Icon 'md-track_changes' ⍝ FontAwesome effects (works on non-FA icons too)
⍝ 'style="color: red;"' Add _.Icon 'e-stop' ⍝ Applying own styling
⍝ Add _.Icon 'fa-square fa-2x' 'fa-terminal fa-inverse' ⍝ Stacking inverse small on large
⍝ Add _.Icon 'fa-camera' 'fa-ban fa-2x'  ⍝ Stacking large on small

    :field public shared readonly ApiLevel←3
    :Field public Spec←⍬
    :field public shared readonly DocBase←'http://fontawesome.io/examples/'

    ∇ Make
      :Access public
      :Implements constructor
    ∇

    ∇ Make1 args;last
      :Access public
      :Implements constructor
      :If 2|⎕DR last←⊃¯1↑Spec←args
          Order←last ⋄ Spec←¯1↓args
      :EndIf
    ∇

    ∇ r←Render;prefix;spec;icon;classes;n;origContent;origSpec;origClass
      :Access public
      (origSpec origContent origClass)←Spec Content class
      Spec←eis Spec
      :If ~0∊⍴Content ⋄ Spec,←eis Content ⋄ :EndIf
      Content←⍬
      :If 1=⍴Spec ⍝ Simple icon
          Spec←{⊃⍣(1<≡⍵)⊢⍵}Spec ⍝ Disclose if nested (eis⍣¯1)
          :If isInstance Spec
              Spec←Spec.Spec
          :EndIf
          (prefix spec)←Spec SplitOn1st'-'
          :Select ¯1↓prefix
          :Case 'fa' ⍝ FontAwesome
              Use'faIcons'
              AddClass'fa ',Spec

          :Case 'md' ⍝ Google
              Use'mdIcons'
              (icon classes)←(spec,' ')SplitOn1st' '
              AddClass'material-icons ',classes
              Content←¯1↓icon

          :CaseList (,'e')'ej' ⍝ Syncfusion
              Use'ejIcons'
              AddClass'e-icon e-',spec
              AddStyle'display: inline-block'  ⍝ add this because default Syncfusion is "block"
          :Else
              Content←Spec

          :EndSelect

      :Else
          Use'faIcons'
          AddClass'fa-stack'
          :For spec :In Spec
              :If isInstance spec
                  spec.AddClass'fa-stack-1x'
                  Add spec
              :Else
                  (Add _.Icon spec).AddClass'fa-stack-1x'
              :EndIf
          :EndFor
      :EndIf
      SetUse
      r←⎕BASE.Render
      (Spec Content class)←origSpec origContent((1+origClass≡UNDEF)⊃origClass'') ⍝ cannot just "unset" class
    ∇

    SplitOn1st←{(l↑⍺)((l←⍺⍳⍵)↓⍺)}
:endclass

:class Icon : #._html.span
⍝ Description:: Dyalog Icon widget
⍝ Constructor:: [spec]
⍝ spec - either a single or pair of string which specify the icon(s) to use
⍝        each consists of a vendor-prefix, dash, icon-name, and optionally space-separated modifiers
⍝        when two strings are used, it represents a "stacked" icon, with second icon overlayed on the first
⍝        "stacked" icons are a feature of FontAwesome icons - using other icons may or may not have the desired visual effect
⍝
⍝ Public Fields::
⍝ Spec  - either a single or pair of string which specify the icon(s) to use
⍝         each consists of a vendor-prefix, dash, icon-name, and optionally space-separated modifiers
⍝         when two strings are used, it represents a "stacked" icon, with second icon overlayed on the first
⍝         "stacked" icons are a feature of FontAwesome icons - using other icons may or may not have the desired visual effect
⍝
⍝ Examples::
⍝ Add _.Icon 'fa-cloud-upload'    ⍝ FontAwesome: http://fontawesome.io/icons/
⍝ Add _.Icon 'md-fingerprint'     ⍝ Google Material Design: https://design.google.com/icons/
⍝ Add _.Icon 'e-delete-column_01' ⍝ Syncfusion Essential JavaScript: http://js.syncfusion.com/demos/web/#!/azure/icon/EJIcons
⍝ '.fa-spin' Add _.Icon 'md-track_changes' ⍝ FontAwesome effects (works on non-FA icons too)
⍝ 'style="color: red;"' Add _.Icon 'e-stop' ⍝ Applying own styling
⍝ Add _.Icon 'fa-square fa-2x' 'fa-terminal fa-inverse' ⍝ Stacking inverse small on large
⍝ Add _.Icon 'fa-camera' 'fa-ban fa-2x'  ⍝ Stacking large on small

    :field public shared readonly ApiLevel←3
    :Field public Spec←⍬
    :field public shared readonly DocBase←'http://fontawesome.io/examples/'

    ∇ Make
      :Access public
      :Implements constructor
    ∇

    ∇ Make1 args;last
      :Access public
      :Implements constructor
      :If 2|⎕DR last←⊃¯1↑Spec←args
          Order←last ⋄ Spec←¯1↓args
      :EndIf
    ∇

    ∇ r←Render;prefix;spec;icon;classes;n;origContent;origSpec;origClass
      :Access public
      (origSpec origContent origClass)←Spec Content class
      Spec←eis Spec
      Spec,←eis Content
      Content←⍬
      :If 1=⍴Spec ⍝ Simple icon
          Spec←{⊃⍣(1<≡⍵)⊢⍵}Spec ⍝ Disclose if nested (eis⍣¯1)
          :If isInstance Spec
              Spec←Spec.Spec
          :EndIf
          (prefix spec)←Spec SplitOn1st'-'
          :Select ¯1↓prefix
          :Case 'fa' ⍝ FontAwesome
              Use'faIcons'
              AddClass'fa ',Spec

          :Case 'md' ⍝ Google
              Use'mdIcons'
              (icon classes)←(spec,' ')SplitOn1st' '
              AddClass'material-icons ',classes
              Content←¯1↓icon

          :CaseList (,'e')'ej' ⍝ Syncfusion
              Use'ejIcons'
              AddClass'e-icon e-',spec
              AddStyle'display: inline-block'  ⍝ add this because default Syncfusion is "block"
          :Else
              Content←Spec

          :EndSelect

      :Else
          Use'faIcons'
          AddClass'fa-stack'
          :For spec :In Spec
              :If isInstance spec
                  spec.AddClass'fa-stack-1x'
                  Add spec
              :Else
                  (Add _.Icon spec).AddClass'fa-stack-1x'
              :EndIf
          :EndFor
      :EndIf
      SetUse
      r←⎕BASE.Render
      (Spec Content class)←origSpec origContent((1+origClass≡UNDEF)⊃origClass'') ⍝ cannot just "unset" class
    ∇

    SplitOn1st←{(l↑⍺)((l←⍺⍳⍵)↓⍺)}
:endclass


Escaping & in Subst

Escaping & in Subst

:Namespace Strings

    (⎕IO ⎕ML)←1

    lc←(819⌶) ⍝ :Includable Lower-casification of simple array
    uc←1∘(819⌶) ⍝ Ditto Upper-casification

    cis←{~0∊⍴(⍺ ⎕S 0 ⎕OPT'IC' 1)⍵} ⍝ Case Insensitive Search

    nocase←{(lc ⍺)⍺⍺ lc ⍵} ⍝ case insensitive operator

    firstCap←('(?<first>\w)(?<remainder>\w*)'⎕R'\u<first>\l<remainder>')

    beginsWith←{(,⍺){⍵≡(⍴⍵)↑⍺},⍵}
    endsWith←{(,⍺){⍵≡(-⍴⍵)↑⍺},⍵}
    stringify←{¯2↓∊(↓⍕↑⍵),¨⊂⎕UCS 13 10}

    split←{⎕ML←3 ⋄ ⍺←' ' ⋄ ⍵⊂⍨~⍵∊⍺}
    vtom←↑split  ⍝ vector to matrix

    dlb←{(+/∧\' '=⍵)↓⍵}               ⍝ Delete Leading Blanks
    dtb←{(-+/∧\' '=⌽⍵)↓⍵}             ⍝ Delete Trailing Blanks
    deb←{{1↓¯1↓(~'  '⍷⍵)/⍵}' ',⍵,' '} ⍝ Delete extraneous blanks

    vtv←{{1↓¨(v=n)⊂v←(n←⎕UCS 10),⍵}⍵~⎕UCS 13} ⍝ vector of text vectors

    stringToHex←{⎕IO←0⋄∊⍉(16⍴⎕D,⎕A)[16 16⊤⎕UCS ⍵]}

    ∇ str←str subst(from to);m;⎕IO;i
    ⍝ simple string substitution
      ⎕IO←0
      :Trap 0
          str←((esc from)⎕R(esc to))str
      :Else
          →(∨/m←from⍷str)↓0
          i←m⍳1
          str←((i↑str),to,(i+⍴from)↓str)subst(from to)
      :EndTrap
    ∇

    esc←{m←∊(-1+⍵∊'&%\')↑¨1 ⋄ r←m\⍵ ⋄ ((~m)/r)←'\' ⋄ r }

    scaledFmt←{0=⍵:' 0' ⋄ ⍵{((1+⍵)⊃(⊂''),'KMGTQP'),⍨(1⌊⍵)⍕⍺÷10*3×⍵}⌊3÷⍨10⍟⍵}

    tonum←{0∊⍴⍵:⍬ ⋄ w←⍕⍵ ⋄ ((w='-')/w)←'¯' ⋄ ⊃(//)⎕VFI w}

    fmtNum←{⍺←⊢ ⋄ w←⍺⍕⍵ ⋄ (('¯'=,w)/,w)←'-' ⋄ w}

    ∇ r←{d}commaFmt n
      :If 0=⎕NC'd'
          r←(,¨'CI20'∘⎕FMT¨n)~¨' '
      :Else
          r←(,¨('CF20.',⍕d)∘⎕FMT¨n)~¨' '
      :EndIf
      (∊r)←{w←⍵ ⋄ w⊣((w='¯')/w)←'-'}∊r
    ∇

:EndNamespace

:Namespace Strings

    (⎕IO ⎕ML)←1

    lc←(819⌶) ⍝ :Includable Lower-casification of simple array
    uc←1∘(819⌶) ⍝ Ditto Upper-casification

    cis←{~0∊⍴(⍺ ⎕S 0 ⎕OPT'IC' 1)⍵} ⍝ Case Insensitive Search

    nocase←{(lc ⍺)⍺⍺ lc ⍵} ⍝ case insensitive operator

    firstCap←('(?<first>\w)(?<remainder>\w*)'⎕R'\u<first>\l<remainder>')

    beginsWith←{(,⍺){⍵≡(⍴⍵)↑⍺},⍵}
    endsWith←{(,⍺){⍵≡(-⍴⍵)↑⍺},⍵}
    stringify←{¯2↓∊(↓⍕↑⍵),¨⊂⎕UCS 13 10}

    split←{⎕ML←3 ⋄ ⍺←' ' ⋄ ⍵⊂⍨~⍵∊⍺}
    vtom←↑split  ⍝ vector to matrix

    dlb←{(+/∧\' '=⍵)↓⍵}               ⍝ Delete Leading Blanks
    dtb←{(-+/∧\' '=⌽⍵)↓⍵}             ⍝ Delete Trailing Blanks
    deb←{{1↓¯1↓(~'  '⍷⍵)/⍵}' ',⍵,' '} ⍝ Delete extraneous blanks

    vtv←{{1↓¨(v=n)⊂v←(n←⎕UCS 10),⍵}⍵~⎕UCS 13} ⍝ vector of text vectors

    stringToHex←{⎕IO←0⋄∊⍉(16⍴⎕D,⎕A)[16 16⊤⎕UCS ⍵]}

    ∇ str←str subst(from to);m;⎕IO;i
    ⍝ simple string substitution
      ⎕IO←0
      :Trap 0
          str←((esc from)⎕R(esc to))str
      :Else
          →(∨/m←from⍷str)↓0
          i←m⍳1
          str←((i↑str),to,(i+⍴from)↓str)subst(from to)
      :EndTrap
    ∇

    esc←{m←∊(-1+⍵∊'%\')↑¨1 ⋄ r←m\⍵ ⋄ ((~m)/r)←'\' ⋄ r }

    scaledFmt←{0=⍵:' 0' ⋄ ⍵{((1+⍵)⊃(⊂''),'KMGTQP'),⍨(1⌊⍵)⍕⍺÷10*3×⍵}⌊3÷⍨10⍟⍵}

    tonum←{0∊⍴⍵:⍬ ⋄ w←⍕⍵ ⋄ ((w='-')/w)←'¯' ⋄ ⊃(//)⎕VFI w}

    fmtNum←{⍺←⊢ ⋄ w←⍺⍕⍵ ⋄ (('¯'=,w)/,w)←'-' ⋄ w}

    ∇ r←{d}commaFmt n
      :If 0=⎕NC'd'
          r←(,¨'CI20'∘⎕FMT¨n)~¨' '
      :Else
          r←(,¨('CF20.',⍕d)∘⎕FMT¨n)~¨' '
      :EndIf
      (∊r)←{w←⍵ ⋄ w⊣((w='¯')/w)←'-'}∊r
    ∇

:EndNamespace