Code clean-up

Code clean-up

: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
    :Field STR←''      ⍝ String to search for
    :Field ALLFILES    ⍝ List of pathnames for all sample files

    :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?

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

    Dlist←{0::⍬ ⋄ (⎕se.SALT.List #.Boot.AppRoot,⍵,' -raw')[;2]} ⍝ 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¨(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 '_')⍵}

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

      DirTree←{
          parent←1(↑Tail ⍵)('/',⍵,'/')
          list←⎕SE.SALT.List #.Boot.AppRoot,⍵,' -rec -raw'
          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]
          ids←('\\'⎕R'/')files
          levels←2+(+/¨ids∊¨⊂'/\')-+/'/\'∊⍨3⊃parent
          ((list[;1]≡¨⊂'<DIR>')/ids),←'/'
          DelEmpty⍣≡parent⍪⍉↑levels(↑Tail¨list[;2])ids
      }

      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;list
      :Access public

     ⍝ Initialize globals
      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←⊃⍪/{⎕SE.SALT.List #.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'

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

      (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

      ⍝ 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

      ⍝ GETTING STARTED ⍝⍝⍝
      '.cat'thediv.Add _.p'Getting Started'
      tree←0 3⍴0 '' ''
      tree⍪←1 'Start here' '/Examples/Applications/About'
      tree⍪←DirTree'Documentation'
      tree⍪←DirTree'Examples/Techniques'
      tree⍪←DirTree'Examples/Applications'
      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
      ⍝ Add the whole tree
      tree←'#treeA'thediv.Add _.ejTreeView tree
      tree.On'nodeSelect' 'OnCA'('node' 'eval' 'argument.id')
    ∇

    ∇ PopulateMid mid;url;code;frame;mya

     ⍝ Read framed pages
      url←'Examples/Applications/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
      mid.Add _.hr
      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←OnGS;node;list;nodir;prefix;files;out;file;name;item;title;desc
      :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
      node~←'_' ⍝ _ is frame suffix
      :If '/'=⊃⌽node ⍝ Folder (i.e. Category)
          node←1↓¯1↓node ⍝ Strip outer slashes
          list←⎕SE.SALT.List #.Boot.AppRoot,node,' -rec -raw'
          nodir←~list[;1]∊⍨⊂'<DIR>'
          prefix←(↓Tail node),nodir/(↑Tail node),'/' ⍝ Quirk in SALT.List includes path only if has subfolders
          files←('\\'⎕R'/')prefix∘,¨list[;2]
          files/⍨←list[;1]≢¨⊂'<DIR>'
          out←NewDiv'.framed'
          :For file :In files
              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←↑Tail node
          desc←'Click on a button below to select a sample.'
          r←title title desc out''
      :Else ⍝ File (i.e. Page)
          r←Update node
      :EndIf
      r←GenJS r
    ∇

    ∇ r←OnCA;node;control;files;list;file;scores;code;desc;score;controls;out;item;title;coreonly
      :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←'('∊node
      node↓⍨←-coreonly ⍝ drop ( is core suffix
      :If '_'=⊃⌽node ⍝ Category
          control←NodeToCtrl¨TREE/⍨∨/¨(⊂node)⍷¨TREE
          control←(CORE∊⍨{⍵↓⍨⌽⍵⍳'.'}¨control)/⍣coreonly⊢control
          scores←⍬
          :For file :In files←ALLFILES[⍋↑ALLFILES] ⍝ !!! make global ALLFILESSORTED !!!
              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←↑Tail node
              desc←'Click on a button below to select a sample.'
              r←title title desc out''
          :EndIf
      :Else ⍝ Control
          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
              out←NewDiv'.framed'
              '.noitems'out.Add _.p,⊂'[no samples using ',control,']'
              ⍝(2⊃r)←'_',class,'.',AddShortInfo⊂control
              ⍝(3⊃r)←(4↓⊃AddLongInfo,⊂control),' ',('0'=⊃2⊃r)/3⊃r
              r←control(control,' Short Info')'Long info'out''
          :Else
              r←control Update⊃CURRFILES
          :EndIf
      :EndIf
      r←GenJS r
    ∇

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

    ∇ r←OnSample
      :Access Public
      :Select _event
      :Case 'click'
          r←GenJS Update CURRFILES⊃⍨⍎5↓_what ⍝ id="nodeS23"
      :Case 'contextmenu'
          r←Execute'window.open("','");',⍨CURRFILES⊃⍨⍎5↓_what ⍝ id="nodeS23"
      :EndSelect
    ∇

    ∇ r←OnSearch2
      ⎕SE.SALT.List #.Boot.AppRoot,node,' -rec -raw'
      r←''
    ∇

    ∇ 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 ⍝ 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 contextmenu' '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''
          SEARCH←GenJS⍣(STR≡'')⊢r ⍝ Do not finish up if called by OnTree
          STR←'' ⍝ Reset
      :EndIf
      r←SEARCH
    ∇

    ∇ (page title desc iframe code)←{spacectrl}Update url;ctrlsec;control;space;a
     ⍝ Create new placeholder values
      iframe←Frame url
      code←Dread url
      :If a←×⎕NC'spacectrl'
          page←spacectrl
      :Else
          page←⊃Words'Control'Section code
      :EndIf
      desc←(#.HTMLInput.dtlb'Description'Section code),' '
      desc←NewWinA desc url
      title←page,⍨a/'This example demonstrates mainly '
      title←('_',⍨CtrlToNode page)(a/'title=',Q'Click here for all ',page,' examples')New _.span title
      title.On'click' 'OnList'
    ∇

    ∇ 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
    :Field STR←''      ⍝ String to search for
    :Field ALLFILES    ⍝ List of pathnames for all sample files

    :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?

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

    Dlist←{0::⍬ ⋄ (⎕se.SALT.List #.Boot.AppRoot,⍵,' -raw')[;2]} ⍝ 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¨(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 '_')⍵}

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

      DirTree←{
          parent←1(↑Tail ⍵)('/',⍵,'/')
          list←⎕SE.SALT.List #.Boot.AppRoot,⍵,' -rec -raw'
          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]
          ids←('\\'⎕R'/')files
          levels←2+(+/¨ids∊¨⊂'/\')-+/'/\'∊⍨3⊃parent
          ((list[;1]≡¨⊂'<DIR>')/ids),←'/'
          DelEmpty⍣≡parent⍪⍉↑levels(↑Tail¨list[;2])ids
      }

      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;list
      :Access public

     ⍝ Initialize globals
      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←⊃⍪/{⎕SE.SALT.List #.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'

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

      (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

      ⍝ 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

      ⍝ GETTING STARTED ⍝⍝⍝
      '.cat'thediv.Add _.p'Getting Started'
      tree←0 3⍴0 '' ''
      tree⍪←1 'Start here' '/Examples/Applications/About'
      tree⍪←DirTree'Documentation'
      tree⍪←DirTree'Examples/Techniques'
      tree⍪←DirTree'Examples/Applications'
      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
      ⍝ Add the whole tree
      tree←'#treeA'thediv.Add _.ejTreeView tree
      tree.On'nodeSelect' 'OnCA'('node' 'eval' 'argument.id')

⍝      Old Controls ⍝⍝⍝
⍝      ('#node01All' '.cat'thediv.Add _.p'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'thediv.Add _.ejTreeView,⊂treecore⍪treeall
⍝      tree.On'nodeSelect' 'OnTree'('node' 'eval' 'argument.id')
    ∇

    ∇ PopulateMid mid;url;code;frame;mya

     ⍝ Read framed pages
      url←'Examples/Applications/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
      mid.Add _.hr
      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←OnGS;node;list;nodir;prefix;files;out;file;name;item;title;desc
      :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
      node~←'_' ⍝ _ is frame suffix
      :If '/'=⊃⌽node ⍝ Folder (i.e. Category)
          node←1↓¯1↓node ⍝ Strip outer slashes
          list←⎕SE.SALT.List #.Boot.AppRoot,node,' -rec -raw'
          nodir←~list[;1]∊⍨⊂'<DIR>'
          prefix←(↓Tail node),nodir/(↑Tail node),'/' ⍝ Quirk in SALT.List includes path only if has subfolders
          files←('\\'⎕R'/')prefix∘,¨list[;2]
          files/⍨←list[;1]≢¨⊂'<DIR>'
          out←NewDiv'.framed'
          :For file :In files
              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←↑Tail node
          desc←'Click on a button below to select a sample.'
          r←title title desc out''
      :Else ⍝ File (i.e. Page)
          r←Update node
      :EndIf
      r←GenJS r
    ∇

    ∇ r←OnCA;node;control;files;list;file;scores;code;desc;score;controls;out;item;title;coreonly
      :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←'('∊node
      node↓⍨←-coreonly ⍝ drop ( is core suffix
      :If '_'=⊃⌽node ⍝ Category
          control←NodeToCtrl¨TREE/⍨∨/¨(⊂node)⍷¨TREE
          control←(CORE∊⍨{⍵↓⍨⌽⍵⍳'.'}¨control)/⍣coreonly⊢control
          scores←⍬
          :For file :In files←ALLFILES[⍋↑ALLFILES] ⍝ !!! make global ALLFILESSORTED !!!
              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←↑Tail node
              desc←'Click on a button below to select a sample.'
              r←title title desc out''
          :EndIf
      :Else ⍝ Control
          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
              out←NewDiv'.framed'
              '.noitems'out.Add _.p,⊂'[no samples using ',control,']'
              ⍝(2⊃r)←'_',class,'.',AddShortInfo⊂control
              ⍝(3⊃r)←(4↓⊃AddLongInfo,⊂control),' ',('0'=⊃2⊃r)/3⊃r
              r←control(control,' Short Info')'Long info'out''
          :Else
              r←control Update⊃CURRFILES
          :EndIf
      :EndIf
      r←GenJS r
    ∇

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

    ∇ r←OnSample
      :Access Public
      :Select _event
      :Case 'click'
          r←GenJS Update CURRFILES⊃⍨⍎5↓_what ⍝ id="nodeS23"
      :Case 'contextmenu'
          r←Execute'window.open("','");',⍨CURRFILES⊃⍨⍎5↓_what ⍝ id="nodeS23"
      :EndSelect
    ∇

    ∇ r←OnSearch2
      ⎕SE.SALT.List #.Boot.AppRoot,node,' -rec -raw'
      r←''
    ∇

    ∇ 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 ⍝ 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 contextmenu' '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''
          SEARCH←GenJS⍣(STR≡'')⊢r ⍝ Do not finish up if called by OnTree
          STR←'' ⍝ Reset
      :EndIf
      r←SEARCH
    ∇

    ∇ (page title desc iframe code)←{spacectrl}Update url;ctrlsec;control;space;a
     ⍝ Create new placeholder values
      iframe←Frame url
      code←Dread url
      :If a←×⎕NC'spacectrl'
          page←spacectrl
      :Else
          page←⊃Words'Control'Section code
      :EndIf
      desc←(#.HTMLInput.dtlb'Description'Section code),' '
      desc←NewWinA desc url
      title←page,⍨a/'This example demonstrates mainly '
      title←('_',⍨CtrlToNode page)(a/'title=',Q'Click here for all ',page,' examples')New _.span title
      title.On'click' 'OnList'
    ∇

    ∇ 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


Proper(er) styling

Proper(er) styling

:Class index : MiPageSample

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

    :Field TYPES←'Simple' 'Advanced' ⍝ Types of samples (if not App)
    :Field GROUPS←'Base HTML' 'Wrapped HTML' 'Dyalog Controls' 'JQueryUI' 'SyncFusion' ⍝ 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)

    :ENDSECTION

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

    P←' ('∘,,∘')' ⍝ Surround with parens, a.k.a. APL monkey

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

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

    SpaceToDir←{'Examples/',1↓⍵,'plus'/⍨⍵≡'_HTML'} ⍝ _HTML is in the HTMLplus folder

    Frame←{'.framed' ('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,⊂'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)

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

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

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

    AddLongInfo←{⍵,¨{⍵,⍨' – [no info available]'↓⍨¯19××≢⍵}¨(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;vp;style
      :Access public

     ⍝ Initialize globals
      SAMPLES←0 3⍴⍬
      REFS←_html _HTML _DC _JQ _SF
      INFOSHORT←'⍝',¨Dread'Examples/Data/infoShort'
      INFOLONG←'⍝',¨Dread'Examples/Data/infoLong'
      NAMESSHORT←Names INFOSHORT
      NAMESLONG←Names INFOLONG
     ⍝ Use'ejTab' ⍝ May get added by callbacks

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

      Add _.StyleSheet'/Styles/homepage.css'
      style←'.menu {cursor:pointer;margin-left:2pt;}'
      style,←'.submenu {max-height:100pt;overflow-y:scroll;}'
      style,←'.menuitem {margin-bottom:0;margin-left:13pt;cursor:pointer;} '
      style,←'.framed {width:726px;height:400px;border:2px inset;overflow-y:scroll;background-color:white;;} '
      style,←'.listitem {margin:8px;cursor:pointer;}'
      style,←'.samplesource {overflow-x:scroll;width:546pt;background-color:#e5e5cc;border:1px #f0f0e0 solid;}'
      Add _.style style

      (left mid)←NewDiv¨'leftBar' 'midBar' ⍝ Create panes

      sp←'mainSP'Horz left mid
      sp.Items[1].style←⊂'width: 200px; max-height: 450px;'
      sp.Items[2].style←⊂'margin: 5px;'
      sp.style←'height: 450px; width: 100%;'

      vp←Add _.StackPanel sp
      vp.style←'width:100%'

      PopulateLeft left
      PopulateMid mid
    ∇

    ∇ PopulateLeft thediv;class;depths;group;items;names;ref;samples;tv;vp;search;case;style;menu;i

      ⍝ SEARCH FIELD ⍝⍝⍝
      (search←New _.EditField'str').On'change' 'OnSearch'('str' 'val')
      (case←'#case'New _.button'Search').On'click' 'OnSearch'
      thediv.Add Horz search case

      thediv.Add _.hr

      ⍝ SAMPLE APPS ⍝⍝⍝
      APPS←(Dlist'Examples/Apps')~⊂'index'
      APPDESCS←(⊂'Description')Section¨Dread¨'Examples/Apps/'∘,¨APPS
      APPCTRLS←(⊂'Control')Section¨Dread¨'Examples/Apps/'∘,¨APPS
      ('#nodeA0' '.menu'thediv.Add _.span'Sample Apps').On'click' 'OnApp'
      (((⊂¨NodeID'A'APPS),¨⊂⊂'.menuitem')thediv.Add¨_.p,¨APPCTRLS).On⊂'click' 'OnApp'

      thediv.Add _.hr

      ⍝ CONTROLS ⍝⍝⍝
      'style="margin-left:4pt;"'thediv.Add _.span'Controls'
      i←1
      :For (group ref) :InEach GROUPS REFS
          names←{({#.HtmlElement=⊃⊃⌽⎕CLASS ⍵}¨⍵)/⍵}ref.(⍎¨⎕NL ¯9.4)
          class←2↓⍕ref              ⍝ Remove leading #.
          names←(3+⍴class)↓¨⍕¨names ⍝ Remove leading #.class.
          CONTROLS,←⊂names
          depths←1,(⍴names)⍴2
          SAMPLES⍪←depths,(⍪(⊂group),names),(⊂class)
          menu←'.submenu'thediv.Add _.details
          (('#nodeL',⍕i)'.menu'menu.Add _.summary,⊂class,P group).On'click' 'OnControl'
          names←AddShortInfo names ⍝ add short info
          (((⊂¨i NodeID'L'names),¨⊂⊂'.menuitem')menu.Add¨_.p,¨names).On⊂'click' 'OnControl'
          i+←1+⍴names
          names←(⊂group),names
      :EndFor
    ∇

    ∇ r←space FindSamples names;i;samples;suffix
      samples←Dlist SpaceToDir space
      r←(⍴names)⍴⊂''
      :For suffix :In TYPES
          i←(((-⍴suffix)↑¨samples)∊⊂suffix)/⍳⍴samples
          i←{(⍵≤⍴names)/⍵}names⍳(-⍴suffix)↓¨samples[i]
          r[i]←r[i],¨⊂⊂suffix
      :EndFor
    ∇

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

    :ENDSECTION

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

    ∇ r←OnApp;node;descs;out;items;time;title;i
      :Access Public
     ⍝ Gets called upon selection in Sample Apps tree
      :If ×node←⍎5↓_what ⍝ ≥1 is an actual app
          r←Update'Examples/Apps'(node⊃APPS)
      :Else ⍝ =0 is the header
          descs←APPCTRLS,¨' – '∘,¨APPDESCS
          out←NewDiv'.framed'
          items←(NodeID'M'APPS)out.Add¨_.p,¨descs
          items.Set⊂'.listitem'
          items.On⊂'click' 'OnApp'
          r←'Apps' 'Sample Apps' 'Small applications to showcase MiServer 3.0 functionality'out''
      :EndIf
      r←GenJS r
    ∇

    ∇ r←OnControl;node;depth;sample;control;space;spacedir;files;out;file;code;ctrlsec;url;iframe;page;desc;title;item;spacectrl;number;names;name;descs;items
      :Access Public
     ⍝ Gets called upon selection in Controls tree

     ⍝ Get details
      node←⍎5↓_what ⍝ id="nodeA23" or id="nodeL23" or id="nodeM23"
      depth←⊃SAMPLES[node;1]
      control←depth⊃''(⊃SAMPLES[node;2])(⊃SAMPLES[1⌈1+node-2⍳⍨⌽node↑SAMPLES[;1];2])
      sample←depth⊃'index' ''(control,⊃SAMPLES[node;2])
      space←⊃SAMPLES[node;3]
      spacedir←SpaceToDir space

      :Select depth
      :Case 1 ⍝ Group
          number←+/1=SAMPLES[⍳node;1]
          names←number⊃CONTROLS
          descs←AddLongInfo names
          out←NewDiv'.framed'
          items←(node NodeID'M'names)out.Add¨_.p,¨descs
          items.Set⊂'.listitem'
          items.On⊂'click' 'OnControl'
          r←space(space,P number⊃GROUPS)(' controls',⍨⍕⍴names)out''
      :Case 2 ⍝ Element
          spacectrl←space,'.',control
          files←Dlist spacedir
          out←NewDiv'.framed'
          :For file :In files
              url←spacedir,'/',file
              code←Dread url
              ctrlsec←'Control'Section code
              :If (⊂spacectrl)∊Words ctrlsec ⍝ Extract space-separated words
                  desc←⊂'Description'Section code
                  iframe←Frame url
                  title←NewWinA desc,⊂url
                  ctrlsec←New _.em(⍕ctrlsec)
                  desc←∊desc,Type file
                  item←'.listitem'out.Add _.p desc
                  item.On'click' 0 ''(APLtoJS GenJS file title ctrlsec iframe code)
              :EndIf
          :EndFor
          title←1↓control Section INFOSHORT
          title←spacectrl,(×≢title)/P title
          desc←control Section INFOLONG
          r←spacectrl title desc out''
      :Else ⍝ Sample (Obsolete)
          r←Update spacedir sample
      :EndSelect
      r←GenJS r
    ∇

    ∇ (page title desc iframe code)←Update(spacedir page);ctrlsec;url
     ⍝ Create new placeholder values
      url←spacedir,'/',page
      iframe←Frame url
      code←Dread url
      ctrlsec←'Control'Section code
      desc←'Description'Section code
      title←'target' 'href'(New _.a ctrlsec).Set'_blank'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
    ∇

    ∇ r←OnSearch;dir;files;file;code;desc;str;terms;entry;url;ctrlsec;iframe;page;title;item;out;i;time;types;type
      :Access Public
      :If 0∊⍴str←Get'str'  ⍝ get search string
          r←SEARCH         ⍝ re-display old search
      :Else
          time←0.001×3⊃⎕AI ⍝ prepare search statistics

      ⍝⍝⍝ Controls
          terms←INFOSHORT/⍨str In INFOSHORT
          terms,←terms~⍨INFOLONG/⍨str In INFOLONG
          terms←'.',¨∪Names terms
      ⍝⍝⍝ Samples
          out←NewDiv'.framed'
          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
                  desc←⊂'Description'Section code
                  ctrlsec←'Control'Section code
                  :If ∨/str In desc
                  :OrIf ∨/∊(str,terms)⍷¨⊂ctrlsec ⍝ always case sensitive
                      i+←1
                      :If 16≥i
                          iframe←Frame url
                          title←NewWinA url desc
                          ctrlsec←New _.em(⍕ctrlsec)
                          desc←∊desc,Type file
                          item←'.listitem'out.Add _.p desc
                          item.On'click' 0 ''(APLtoJS GenJS file title ctrlsec iframe code)
                      :EndIf
                  :EndIf
              :EndFor
          :EndFor
          page←Q str
          desc←New _.span'Showing results for '
          desc.Add _.em(⍕str,1↓¨terms)
          i←(⍕16⌊i),(16<i)/' of ',(⍕i)
          time-⍨←0.001×3⊃⎕AI
          title←(⍕i),' results (',time,' seconds)'
          r←SEARCH←GenJS page title desc out''
      :EndIf
    ∇

    :ENDSECTION

:EndClass

:Class index : MiPageSample

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

    :Field TYPES←'Simple' 'Advanced' ⍝ Types of samples (if not App)
    :Field GROUPS←'Base HTML' 'Wrapped HTML' 'Dyalog Controls' 'JQueryUI' 'SyncFusion' ⍝ 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)

    :ENDSECTION

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

    P←' ('∘,,∘')' ⍝ Surround with parens, a.k.a. APL monkey

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

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

    SpaceToDir←{'Examples/',1↓⍵,'plus'/⍨⍵≡'_HTML'} ⍝ _HTML is in the HTMLplus folder

    Frame←{'.framed' ('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,⊂'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)

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

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

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

    AddLongInfo←{⍵,¨{⍵,⍨' – [no info available]'↓⍨¯19××≢⍵}¨(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;vp;style
      :Access public

     ⍝ Initialize globals
      SAMPLES←0 3⍴⍬
      REFS←_html _HTML _DC _JQ _SF
      INFOSHORT←'⍝',¨Dread'Examples/Data/infoShort'
      INFOLONG←'⍝',¨Dread'Examples/Data/infoLong'
      NAMESSHORT←Names INFOSHORT
      NAMESLONG←Names INFOLONG
     ⍝ Use'ejTab' ⍝ May get added by callbacks

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

      Add _.StyleSheet'/Styles/homepage.css'
      style←'.menu {cursor:pointer;margin-left:2pt;} .menuitem {margin-bottom:0;margin-left:13pt;cursor:pointer;}'
      style,←'.framed {width:725px;height:400px;border:2px inset} .listitem {margin:8px;cursor:pointer;}'
      Add _.style style

      (left mid)←NewDiv¨'leftBar' 'midBar' ⍝ Create panes

      sp←'mainSP'Horz left mid
      sp.Items[1].style←⊂'width: 200px; max-height: 450px;'
      sp.Items[2].style←⊂'margin: 5px;'
      sp.style←'height: 450px; width: 100%;'

      vp←Add _.StackPanel sp
      vp.style←'width:100%'

      PopulateLeft left
      PopulateMid mid
    ∇

    ∇ PopulateLeft thediv;class;depths;group;items;names;ref;samples;tv;vp;search;case;style;menu;i

      ⍝ SEARCH FIELD ⍝⍝⍝
      (search←New _.EditField'str').On'change' 'OnSearch'('str' 'val')
      (case←'#case'New _.button'Search').On'click' 'OnSearch'
      thediv.Add Horz search case

      thediv.Add _.hr

      ⍝ SAMPLE APPS ⍝⍝⍝
      APPS←(Dlist'Examples/Apps')~⊂'index'
      APPDESCS←(⊂'Description')Section¨Dread¨'Examples/Apps/'∘,¨APPS
      APPCTRLS←(⊂'Control')Section¨Dread¨'Examples/Apps/'∘,¨APPS
      ('#nodeA0' '.menu'thediv.Add _.span'Sample Apps').On'click' 'OnApp'
      (((⊂¨NodeID'A'APPS),¨⊂⊂'.menuitem')thediv.Add¨_.p,¨APPCTRLS).On⊂'click' 'OnApp'

      thediv.Add _.hr

      ⍝ CONTROLS ⍝⍝⍝
      'style="margin-left:4pt;"'thediv.Add _.span'Controls'
      i←1
      :For (group ref) :InEach GROUPS REFS
          names←{({#.HtmlElement=⊃⊃⌽⎕CLASS ⍵}¨⍵)/⍵}ref.(⍎¨⎕NL ¯9.4)
          class←2↓⍕ref              ⍝ Remove leading #.
          names←(3+⍴class)↓¨⍕¨names ⍝ Remove leading #.class.
          CONTROLS,←⊂names
          depths←1,(⍴names)⍴2
          SAMPLES⍪←depths,(⍪(⊂group),names),(⊂class)
          menu←thediv.Add _.details
          (('#nodeL',⍕i)'.menu'menu.Add _.summary,⊂class,P group).On'click' 'OnControl'
          names←AddShortInfo names ⍝ add short info
          (((⊂¨i NodeID'L'names),¨⊂⊂'.menuitem')menu.Add¨_.p,¨names).On⊂'click' 'OnControl'
          i+←1+⍴names
          names←(⊂group),names
      :EndFor
    ∇

    ∇ r←space FindSamples names;i;samples;suffix
      samples←Dlist SpaceToDir space
      r←(⍴names)⍴⊂''
      :For suffix :In TYPES
          i←(((-⍴suffix)↑¨samples)∊⊂suffix)/⍳⍴samples
          i←{(⍵≤⍴names)/⍵}names⍳(-⍴suffix)↓¨samples[i]
          r[i]←r[i],¨⊂⊂suffix
      :EndFor
    ∇

    ∇ 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' 'style="background-color:white"'
      frame.Add Frame url
      '#SampleSource'mid.Add _.div,⊂#.HTMLInput.APLToHTMLColour code
    ∇

    :ENDSECTION

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

    ∇ r←OnApp;node;descs;out;items;time;title;i
      :Access Public
     ⍝ Gets called upon selection in Sample Apps tree
      :If ×node←⍎5↓_what ⍝ ≥1 is an actual app
          r←Update'Examples/Apps'(node⊃APPS)
      :Else ⍝ =0 is the header
          descs←APPCTRLS,¨' – '∘,¨APPDESCS
          out←NewDiv'.framed'
          items←(NodeID'M'APPS)out.Add¨_.p,¨descs
          items.Set⊂'.listitem'
          items.On⊂'click' 'OnApp'
          r←'Apps' 'Sample Apps' 'Small applications to showcase MiServer 3.0 functionality'out''
      :EndIf
      r←GenJS r
    ∇

    ∇ r←OnControl;node;depth;sample;control;space;spacedir;files;out;file;code;ctrlsec;url;iframe;page;desc;title;item;spacectrl;number;names;name;descs;items
      :Access Public
     ⍝ Gets called upon selection in Controls tree

     ⍝ Get details
      node←⍎5↓_what ⍝ id="nodeA23" or id="nodeL23" or id="nodeM23"
      depth←⊃SAMPLES[node;1]
      control←depth⊃''(⊃SAMPLES[node;2])(⊃SAMPLES[1⌈1+node-2⍳⍨⌽node↑SAMPLES[;1];2])
      sample←depth⊃'index' ''(control,⊃SAMPLES[node;2])
      space←⊃SAMPLES[node;3]
      spacedir←SpaceToDir space

      :Select depth
      :Case 1 ⍝ Group
          number←+/1=SAMPLES[⍳node;1]
          names←number⊃CONTROLS
          descs←AddLongInfo names
          out←NewDiv'.framed'
          items←(node NodeID'M'names)out.Add¨_.p,¨descs
          items.Set⊂'.listitem'
          items.On⊂'click' 'OnControl'
          r←space(space,P number⊃GROUPS)(' controls',⍨⍕⍴names)out''
      :Case 2 ⍝ Element
          spacectrl←space,'.',control
          files←Dlist spacedir
          out←NewDiv'.framed'
          :For file :In files
              url←spacedir,'/',file
              code←Dread url
              ctrlsec←'Control'Section code
              :If (⊂spacectrl)∊Words ctrlsec ⍝ Extract space-separated words
                  desc←⊂'Description'Section code
                  iframe←Frame url
                  title←NewWinA desc,⊂url
                  ctrlsec←New _.em(⍕ctrlsec)
                  desc←∊desc,Type file
                  item←'.listitem'out.Add _.p desc
                  item.On'click' 0 ''(APLtoJS GenJS file title ctrlsec iframe code)
              :EndIf
          :EndFor
          title←1↓control Section INFOSHORT
          title←spacectrl,(×≢title)/P title
          desc←control Section INFOLONG
          r←spacectrl title desc out''
      :Else ⍝ Sample (Obsolete)
          r←Update spacedir sample
      :EndSelect
      r←GenJS r
    ∇

    ∇ (page title desc iframe code)←Update(spacedir page);ctrlsec;url
     ⍝ Create new placeholder values
      url←spacedir,'/',page
      iframe←Frame url
      code←Dread url
      ctrlsec←'Control'Section code
      desc←'Description'Section code
      title←'target' 'href'(New _.a ctrlsec).Set'_blank'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)/#.HTMLInput.APLToHTMLColour code
    ∇

    ∇ r←OnSearch;dir;files;file;code;desc;str;terms;entry;url;ctrlsec;iframe;page;title;item;out;i;time;types;type
      :Access Public
      :If 0∊⍴str←Get'str'  ⍝ get search string
          r←SEARCH         ⍝ re-display old search
      :Else
          time←0.001×3⊃⎕AI ⍝ prepare search statistics

      ⍝⍝⍝ Controls
          terms←INFOSHORT/⍨str In INFOSHORT
          terms,←terms~⍨INFOLONG/⍨str In INFOLONG
          terms←'.',¨∪Names terms
      ⍝⍝⍝ Samples
          out←NewDiv'.framed'
          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
                  desc←⊂'Description'Section code
                  ctrlsec←'Control'Section code
                  :If ∨/str In desc
                  :OrIf ∨/∊(str,terms)⍷¨⊂ctrlsec ⍝ always case sensitive
                      i+←1
                      :If 16≥i
                          iframe←Frame url
                          title←NewWinA url desc
                          ctrlsec←New _.em(⍕ctrlsec)
                          desc←∊desc,Type file
                          item←'.listitem'out.Add _.p desc
                          item.On'click' 0 ''(APLtoJS GenJS file title ctrlsec iframe code)
                      :EndIf
                  :EndIf
              :EndFor
          :EndFor
          page←Q str
          desc←New _.span'Showing results for '
          desc.Add _.em(⍕str,1↓¨terms)
          i←(⍕16⌊i),(16<i)/' of ',(⍕i)
          time-⍨←0.001×3⊃⎕AI
          title←(⍕i),' results (',time,' seconds)'
          r←SEARCH←GenJS page title desc out''
      :EndIf
    ∇

    :ENDSECTION

:EndClass


Deleting Git-messed up togglepopup.dyalog

Brian: save a copy of the file outside the repository, blow the file
away...

:Class TogglePopup : MiPageSample ⍝ EAWC : MiPage : HtmlPage

    ∇ Render;button;p;wp
      :Access Public
      :If _Request.IsPost
          Add'You clicked me!'
      :Else
          p←Add _html.p
          p.Add'Click '
          p.Add button←(Add _HTML.Form).Add _SF.ejToggleButton'' 'here'
          p.Add' to toggle status.'
          button.On'click' 'myCallback'('model' 'ejModel')
          Add wp←_SF.ejWaitingPopup(GenId'Waiting...')
      :EndIf
    ∇

    ∇ r←myCallback
      :Access public
      :If False≡PageData.model.showOnInit
        ⍝'showOnInit' wp.Prop True ⍝ Need Brian's post-render option setting mechanism
      :Else
        ⍝'showOnInit' wp.Prop False ⍝ Need Brian's post-render option setting mechanism
      :End
    ∇

:EndClass


First round of ejTreeViewAdvanced "finished". Pity SyncFusion doesn't update the TreeView model with added nodes :-(

:Class ejTreeViewAdvanced : MiPageSample
⍝ Control:: _SF.ejTreeView
⍝ Description:: A TreeView with checkable nodes and the ability to add & delete nodes

    ∇ Compose;add;del;exp;mod;tv
      :Access Public

      tvid←'tv'      ⍝ id of the treeview we will create
      count←⊃⍴items←8 3⍴⊂'' ⍝ NB global, used by onAddBranch
      items[;1]←1 2 2 3 3 1 2 1                ⍝ Depth of nesting
      items[;2]←'Item '∘,∘⍕¨⍳8                 ⍝ Node texts
      items[;3]←(⊂tvid),∘⍕¨⍳count              ⍝ Original node ids are tvNNN
      checked←count⍴0
      added←0                                  ⍝ Count added nodes
      current←⍬                                ⍝ No current node

      tv←tvid New _.ejTreeView items
      ⍝ Enable checkboxes, but without auto-check of parents & children
      tv.Set('showCheckbox'_true)('autoCheck'_false)
      ⍝ Set up callbacks which all return the selected node as information
      {tv.On ⍵'onNode'('node' 'eval' 'argument.id')}¨'nodeSelect' 'nodeCheck' 'nodeUncheck'

      (add←'Add'New _.Button'Add Child Node').On'click' 'onBranch'
      (del←'Del'New _.Button'Delete Branch').On'click' 'onBranch'
      (exp←'Exp'New _.Button'Expand Children').On'click' 'onBranch'
      (mod←'Mod'New _.Button'Display Model').On'click' 'onModel'('treeModel' 'eval' 'JSON.stringify($("#tv").ejTreeView("model"))')

      ⍝ Create layout: TreeView on left, buttons vertically on the right
      (Add _.StackPanel tv(New _.StackPanel add del exp mod)('tvModel'New _.div)).Horizontal←1
      'output'Add _.div ⍝ Add a place to output messages
    ∇

    ∇ r←onNode;index;item;on
      ⍝ Handle node select or check/uncheck
      :Access Public

      current←⊃Get'node'
      item←⊃items[index←items[;3]⍳⊂current;2]

      :Select _event
      :Case 'nodeSelect'
          r←'#output'Replace _.p('Node "',item,'", id="',current,'"')
      :CaseList 'nodeCheck' 'nodeUncheck'
          checked[index]←_event≡'nodeCheck' ⍝ On or off
          r←'#output'Replace _.p((⍕+/checked),' nodes checked.')
      :EndSelect
    ∇

    ∇ r←onBranch;exec;id;index;insert;m;name
      ⍝ Add to, or delete current branch
      :Access Public

      :If 0=⍴current ⋄ index←depth←0
      :Else ⋄ index←items[;3]⍳⊂current ⋄ depth←items[index;1]
      :EndIf

      :Select _what

      :Case 'Add'
          id←'tv_',⍕added ⍝ SyncFusion will give new nodes these id's
          added+←1
          name←'Added Item ',⍕added

          exec←'$("#tv").data("ejTreeView").addNode("',name,'");' ⍝ Adds child of current node
          insert←index+(index↓items[;1])⍳depth ⍝ Insertion point will be just before next node at same level
          m←insert≠⍳1+1↑⍴items ⍝ Expansion mask
          items←m⍀items ⋄ checked←m\checked
          items[insert;]←(1+depth)name id

      :Case 'Del'
          exec←'$("#tv").data("ejTreeView").removeNode($("#',current,'"));'
          m←index≠⍳1↑⍴items
          m[index+⍳+/∧\index↓items[;1]>items[index;1]]←0 ⍝ Child nodes will go too
          items←m⌿items ⋄ checked←m/checked
          current←⍬ ⍝ No current node

      :Case 'Exp'
          exec←'$("#tv").data("ejTreeView").expandAll();'

      :Else
          ∘∘∘
      :EndSelect
      r←Execute exec
    ∇

    ∇ r←onModel;checked;depth;depths;ids;names;ns;parent
      :Access Public
      ⍝ Display the model

      ns←#.JSON.toAPL∊Get'treeModel'
      (ids names parent)←↓[1]↑ns.fields.dataSource.(id name({0::'' ⋄ pid}0))
      checked←(⍳⍴ids)∊1+ns.checkedNodes
      ((~parent∊ids,⊂'')/parent)←⍬⍴ids ⍝ patch up any invalid parents to point to first node (paranoia)
      depths←0=⊃∘⍴¨parent ⍝ Find top level nodes
      depth←1
      :While (0∊depths)∧depth<99
          depths[(parent∊(depths=depth)/ids)/⍳⍴ids]←depth+1
          depth←depth+1
      :EndWhile

      r←'#tvModel'Replace _.Table('Depth' 'Name' 'ID' 'Checked'⍪depths,names,ids,⍪checked)
    ∇

:EndClass

:Class ejTreeViewAdvanced : MiPageSample
⍝ Control:: _SF.ejTreeView
⍝ Description:: A TreeView with checkable nodes and the ability to add & delete nodes

    ∇ Compose;add;del;exp;mod;tv
      :Access Public

      tvid←'tv'      ⍝ id of the treeview we will create
      count←⊃⍴items←8 3⍴⊂'' ⍝ NB global, used by onAddBranch
      items[;1]←1 2 2 3 3 1 2 1                ⍝ Depth of nesting
      items[;2]←'Item '∘,∘⍕¨⍳8                 ⍝ Node texts
      items[;3]←(⊂tvid),∘⍕¨⍳count              ⍝ Original node ids are tvNNN
      checked←count⍴0
      added←0                                  ⍝ Count added nodes
      current←⍬                                ⍝ No current node

      tv←tvid New _.ejTreeView items
      ⍝ Enable checkboxes, but without auto-check of parents & children
      tv.Set('showCheckbox'_true)('autoCheck'_false)
      ⍝ Set up callbacks which all return the selected node as information
      {tv.On ⍵'onNode'('node' 'eval' 'argument.id')}¨'nodeSelect' 'nodeCheck' 'nodeUncheck'

      (add←'Add'New _.Button'Add Child Node').On'click' 'onBranch'
      (del←'Del'New _.Button'Delete Branch').On'click' 'onBranch'
      (exp←'Exp'New _.Button'Expand Children').On'click' 'onBranch'
      (mod←'Mod'New _.Button'Display Model').On'click' 'onModel'('treeModel' 'eval' '$("#tv").ejTreeView("model")')

      ⍝ Create layout: TreeView on left, buttons vertically on the right
      (Add _.StackPanel tv(New _.StackPanel add del exp mod)).Horizontal←1
      'output'Add _.div ⍝ Add a place to output messages
    ∇

    ∇ r←onNode;index;item;on
      ⍝ Handle node select or check/uncheck
      :Access Public

      current←⊃Get'node'
      item←⊃items[index←items[;3]⍳⊂current;2]

      :Select _event
      :Case 'nodeSelect'
          r←'#output'Replace _.p('Node "',item,'", id="',current,'"')
      :CaseList 'nodeCheck' 'nodeUncheck'
          checked[index]←_event≡'nodeCheck' ⍝ On or off
          r←'#output'Replace _.p((⍕+/checked),' nodes checked.')
      :EndSelect
    ∇

    ∇ r←onBranch;exec;id;index;insert;m;name
      ⍝ Add to, or delete current branch
      :Access Public

      :If 0=⍴current ⋄ index←depth←0
      :Else ⋄ index←items[;3]⍳⊂current ⋄ depth←items[index;1]
      :EndIf

      :Select _what

      :Case 'Add'
          id←'tv_',⍕added ⍝ SyncFusion will give new nodes these id's
          added+←1
          name←'Added Item ',⍕added

          exec←'$("#tv").data("ejTreeView").addNode("',name,'");' ⍝ Adds child of current node
          insert←index+(index↓items[;1])⍳depth ⍝ Insertion point will be just before next node at same level
          m←insert≠⍳1+1↑⍴items ⍝ Expansion mask
          items←m⍀items ⋄ checked←m\checked
          items[insert;]←(1+depth)name id
          ⎕←items

      :Case 'Del'
          exec←'$("#tv").data("ejTreeView").removeNode($("#',current,'"));'
          m←index≠⍳1↑⍴items
          m[index+⍳+/∧\index↓items[;1]>items[index;1]]←0 ⍝ Child nodes will go too
          items←m⌿items ⋄ checked←m/checked
          current←⍬ ⍝ No current node

      :Case 'Exp'
          exec←'$("#tv").data("ejTreeView").expandAll();'

      :Else
          ∘∘∘
      :EndSelect
      r←Execute exec
    ∇

    ∇ r←onModel
      :Access Public
      ⍝ Display the model
      ∘∘∘
    ∇

:EndClass


Fixed DC.Panel

Fixed DC.Panel

:Class Panel : #._html.div
⍝ Description:: Panel widget
⍝
⍝ Constructor:: [Message [Type [Title]]]
⍝ Message - the text that will appear in the popup
⍝ Type - one of 'info', 'warn', 'erro[r]', 'succ[ess]', 'warnRed' OR .ClassName for custom styling
⍝ Title - Text of the title (Box is rendered without title-section if no title is specified)
⍝
⍝ Public Fields::
⍝ Message - the text that will appear in the popup
⍝ Type - one of 'info', 'warn', 'erro[r]', 'succ[ess]', 'warnRed' OR .ClassName for custom styling
⍝ Title - Text of the title (Box is rendered without title-section if no title is specified)
⍝ Icon - name of an Icon (as accepted by the Icon-Class) OR a ref to an existing Icon-Object
⍝ Radius - (0=square,1=defaults, any other number is measure, unit given in RadiusUnit)
⍝ RadiusUnit - defaults to "em", but you can also specify a different unit
⍝ Radius and RadiusUnit can be defined a single elements or up to 4 elements (for each corner).
⍝ If less than 4 elems are supplied, missing elems will be filled according to CSS-Rules
⍝  Four values: first value applies to top-left, second value applies to top-right, third value applies to bottom-right, and fourth value applies to bottom-left corner
⍝  Three values: first value applies to top-left, second value applies to top-right and bottom-left, and third value applies to bottom-right
⍝  Two values: first value applies to top-left and bottom-right corner, and the second value applies to top-right and bottom-left corner
⍝  One value: all four corners are rounded equally
⍝
⍝ Notes::
⍝ To use custom-styling, provide the "base-name" of your style and add a style-sheet in which
⍝ you use selectors that combine your style with .dc-panel-icon, dc-panel-title to specifically
⍝ style these sections of the panel. (dc-panel-content can be skipped if you specify the others.)
⍝ See the Advanced Example for a practical example.

    :field public Message←''
    :field public Type←'info'       ⍝ info | warn | errorr] | success | warnRed (Warning-Symbol with red color - if you don't like normal "error"-style)
    :field public Title←''
    :field public Icon←''
    :field public Radius←1          ⍝ boolean or width (up to 4 elements)
    :field public RadiusUnit←'em'   ⍝ unit of radius

    ∇ make0
      :Access public
      :Implements constructor :base
      Uses←'dcPanel'
    ∇

    ∇ make1 arg
      :Access public
      :Implements constructor :base
      (Message Type Title)←(eis arg)defaultArgs Message Type Title
      Uses←'dcPanel'
    ∇

    ∇ r←Render;icon;class;c;cclass;d;ic;theStyle;i
      :Access public
      cclass←'dc-panel-dfltStyle'
      SetId
      SetUse
      :Select ∪,Radius
      :Case ,0 ⋄ class←'dc-panel-s'
      :Case ,1 ⋄ class←'dc-panel-r'
      :Else
          class←'dc-panel-c',GenId  ⍝ create a custom class
          :Select ≢Radius
          :CaseList 1 2 ⋄ Radius←4⍴Radius
          :Case 3 ⋄ Radius←Radius,Radius[2]
          :EndSelect
          :Select ≢RadiusUnit←,eis RadiusUnit
          :CaseList 1 2 ⋄ RadiusUnit←4⍴RadiusUnit
          :Case 3 ⋄ RadiusUnit←RadiusUnit,RadiusUnit[2]
          :EndSelect

          theStyle←ScriptFollows
⍝ .%class% {
⍝  border-top-left-radius:     %Radius1%%RadiusUnit1%;
⍝  border-top-right-radius:    %Radius2%%RadiusUnit2%;
⍝  border-bottom-left-radius:  %Radius3%%RadiusUnit3%;
⍝  border-bottom-right-radius: %Radius4%%RadiusUnit4%;
⍝}
⍝ .%class%  > .dc-panel-icon  + .dc-panel-content
⍝{
⍝  border-top-left-radius:     %Radius1%%RadiusUnit1%;
⍝  border-top-right-radius:    %Radius2%%RadiusUnit2%;
⍝  border-bottom-left-radius:  %Radius3%%RadiusUnit3%;
⍝  border-bottom-right-radius: %Radius4%%RadiusUnit4%;
⍝}
⍝ .%class% > .dc-panel-title {
⍝  border-top-left-radius:     %Radius1%%RadiusUnit1%;
⍝  border-top-right-radius:    %Radius2%%RadiusUnit2%;
⍝}
⍝.%class% > .dc-panel-title +.dc-panel-icon +.dc-panel-content {
⍝  border-top-left-radius: 0em;
⍝  border-top-right-radius: 0em;
⍝  border-bottom-left-radius:  %Radius3%%RadiusUnit3%;
⍝  border-bottom-right-radius: %Radius4%%RadiusUnit4%;
⍝}

          :For i :In ⍳4
              theStyle←(theStyle #.Strings.subst('%Radius',(⍕i),'%')(⍕Radius[i]))#.Strings.subst('%RadiusUnit',(⍕i),'%')(i⊃RadiusUnit)
          :EndFor
          theStyle←theStyle #.Strings.subst'%class%'class
          _PageRef.Head.Add _.style theStyle
          class,←' dc-panel-c'
      :EndSelect
      AddClass(class,' dc-panel')
      :Select 4↑Type
      :Case 'info' ⋄ icon←'info-circle' ⋄ class←'dc-panel-info'
      :Case 'warn'
          :If 'Red'≡¯3↑Type
              icon←'times-circle' ⋄ class←'dc-panel-error'
          :Else
              icon←'exclamation-circle' ⋄ class←'dc-panel-warning'
          :EndIf
      :Case 'erro' ⋄ icon←'times-circle' ⋄ class←'dc-panel-error'
      :Case 'succ' ⋄ icon←'check' ⋄ class←'dc-panel-success'
      :Else
          :If '.'=⍬⍴Type  ⍝ specified custom class
              cclass←1↓Type ⋄ class←''
          :EndIf
      :EndSelect
      c←''
      :If ~0∊⍴Title ⋄ c,←('class="',class,' ',cclass,' dc-panel-title" id="',id,'_title"')New _.div Title ⋄ :EndIf

      :If Icon≡''
          ic←('.dc-panel-icon ',{(' '⎕r ' .')' ',#.Strings.deb ⍵}class,' ')New _.Icon('fa-',icon)
      :ElseIf isInstance Icon
          Icon.SetAttr⊂'class="dc-panel-icon"'
          ic←Icon
      :Else
          ic←('.dc-panel-icon',{(' '⎕r ' .')' ',#.Strings.deb ⍵}class,' ',cclass)New _.Icon Icon
      :EndIf

      c,←ic.Render
      d←('class="',class,' ',cclass,' dc-panel-content" id="',id,'_content"')New _.div Message
      d.Add savedContent←Content   ⍝ save original content
      Content←c,d
      r←⎕BASE.Render       ⍝ restore original content to avoid issues
      Content←savedContent ⍝ when re-rendering (as in MetaControl)
    ∇

:endclass

:Class Panel : #._html.div
⍝ Description:: Panel widget
⍝
⍝ Constructor:: [Message [Type [Title]]]
⍝ Message - the text that will appear in the popup
⍝ Type - one of 'info', 'warn', 'erro[r]', 'succ[ess]', 'warnRed' OR .ClassName for custom styling
⍝ Title - Text of the title (Box is rendered without title-section if no title is specified)
⍝
⍝ Public Fields::
⍝ Message - the text that will appear in the popup
⍝ Type - one of 'info', 'warn', 'erro[r]', 'succ[ess]', 'warnRed' OR .ClassName for custom styling
⍝ Title - Text of the title (Box is rendered without title-section if no title is specified)
⍝ Icon - name of an Icon (as accepted by the Icon-Class) OR a ref to an existing Icon-Object
⍝ Radius - (0=square,1=defaults, any other number is measure, unit given in RadiusUnit)
⍝ RadiusUnit - defaults to "em", but you can also specify a different unit
⍝ Radius and RadiusUnit can be defined a single elements or up to 4 elements (for each corner).
⍝ If less than 4 elems are supplied, missing elems will be filled according to CSS-Rules
⍝  Four values: first value applies to top-left, second value applies to top-right, third value applies to bottom-right, and fourth value applies to bottom-left corner
⍝  Three values: first value applies to top-left, second value applies to top-right and bottom-left, and third value applies to bottom-right
⍝  Two values: first value applies to top-left and bottom-right corner, and the second value applies to top-right and bottom-left corner
⍝  One value: all four corners are rounded equally
⍝
⍝ Notes::
⍝ To use custom-styling, provide the "base-name" of your style and add a style-sheet in which
⍝ you use selectors that combine your style with .dc-panel-icon, dc-panel-title to specifically
⍝ style these sections of the panel. (dc-panel-content can be skipped if you specify the others.)
⍝ See the Advanced Example for a practical example.

    :field public Message←''
    :field public Type←'info'       ⍝ info | warn | errorr] | success | warnRed (Warning-Symbol with red color - if you don't like normal "error"-style)
    :field public Title←''
    :field public Icon←''
    :field public Radius←1          ⍝ boolean or width (up to 4 elements)
    :field public RadiusUnit←'em'   ⍝ unit of radius

    ∇ make0
      :Access public
      :Implements constructor :base
      Uses←'dcPanel'
    ∇

    ∇ make1 arg
      :Access public
      :Implements constructor :base
      (Message Type Title)←(eis arg)defaultArgs Message Type Title
      Uses←'dcPanel'
    ∇

    ∇ r←Render;icon;class;c;cclass;d;ic;theStyle;i
      :Access public
      cclass←'dc-panel-dfltStyle'
      SetId
      SetUse
      :Select ∪,Radius
      :Case ,0 ⋄ class←'dc-panel-s'
      :Case ,1 ⋄ class←'dc-panel-r'
      :Else
          class←'dc-panel-c',GenId  ⍝ create a custom class
          :Select ≢Radius
          :CaseList 1 2 ⋄ Radius←4⍴Radius
          :Case 3 ⋄ Radius←Radius,Radius[2]
          :EndSelect
          :Select ≢RadiusUnit←,eis RadiusUnit
          :CaseList 1 2 ⋄ RadiusUnit←4⍴RadiusUnit
          :Case 3 ⋄ RadiusUnit←RadiusUnit,RadiusUnit[2]
          :EndSelect

          theStyle←ScriptFollows
⍝ .%class% {
⍝  border-top-left-radius:     %Radius1%%RadiusUnit1%;
⍝  border-top-right-radius:    %Radius2%%RadiusUnit2%;
⍝  border-bottom-left-radius:  %Radius3%%RadiusUnit3%;
⍝  border-bottom-right-radius: %Radius4%%RadiusUnit4%;
⍝}
⍝ .%class%  > .dc-panel-icon  + .dc-panel-content
⍝{
⍝  border-top-left-radius:     %Radius1%%RadiusUnit1%;
⍝  border-top-right-radius:    %Radius2%%RadiusUnit2%;
⍝  border-bottom-left-radius:  %Radius3%%RadiusUnit3%;
⍝  border-bottom-right-radius: %Radius4%%RadiusUnit4%;
⍝}
⍝ .%class% > .dc-panel-title {
⍝  border-top-left-radius:     %Radius1%%RadiusUnit1%;
⍝  border-top-right-radius:    %Radius2%%RadiusUnit2%;
⍝}
⍝.%class% > .dc-panel-title +.dc-panel-icon +.dc-panel-content {
⍝  border-top-left-radius: 0em;
⍝  border-top-right-radius: 0em;
⍝  border-bottom-left-radius:  %Radius3%%RadiusUnit3%;
⍝  border-bottom-right-radius: %Radius4%%RadiusUnit4%;
⍝}

          :For i :In ⍳4
              theStyle←(theStyle #.Strings.subst('%Radius',(⍕i),'%')(⍕Radius[i]))#.Strings.subst('%RadiusUnit',(⍕i),'%')(i⊃RadiusUnit)
          :EndFor
          theStyle←theStyle #.Strings.subst'%class%'class
          _PageRef.Head.Add _.style theStyle
          class,←' dc-panel-c'
      :EndSelect
      AddClass(class,' dc-panel')
      :Select 4↑Type
      :Case 'info' ⋄ icon←'info-circle' ⋄ class←'dc-panel-info'
      :Case 'warn'
          :If 'Red'≡¯3↑Type
              icon←'warning' ⋄ class←'dc-panel-error'
          :Else
              icon←'warning' ⋄ class←'dc-panel-warning'
          :EndIf
      :Case 'erro' ⋄ icon←'times-circle' ⋄ class←'dc-panel-error'
      :Case 'succ' ⋄ icon←'check' ⋄ class←'dc-panel-success'
      :Else
          :If '.'=⍬⍴Type  ⍝ specified custom class
              cclass←1↓Type ⋄ class←''
          :EndIf
      :EndSelect
      c←''
      :If ~0∊⍴Title ⋄ c,←('class="',class,' ',cclass,' dc-panel-title" id="',id,'_title"')New _.div Title ⋄ :EndIf

      :If Icon≡''
          ic←('.dc-panel-icon ',class)New _.Icon('fa-',icon)
      :ElseIf isInstance Icon
          Icon.SetAttr⊂'class="dc-panel-icon"'
          ic←Icon
      :Else
          ic←('.dc-panel-icon ',class,' ',cclass)New _.Icon Icon
      :EndIf

      c,←ic.Render
      d←('class="',class,' ',cclass,' dc-panel-content" id="',id,'_content"')New _.div Message
      d.Add savedContent←Content   ⍝ save original content
      Content←c,d
      r←⎕BASE.Render       ⍝ restore original content to avoid issues
      Content←savedContent ⍝ when re-rendering (as in MetaControl)
    ∇

:endclass


Add missing variable localizations in parse_st_mode.