:Class index : MiPageTemplate

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

      Use'ejTab' ⍝ May get added by callbacks

      newdiv←{⍵ New _.div}                                  ⍝ Make a div
      textspan←{'.textspan'New _.span ⍵}                    ⍝ And a span
      horz←{⍺←⊣ ⋄ r⊣(r←⍺ New _.StackPanel ⍵).Horizontal←1}  ⍝ Horizontal StackPanel

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

      (left mid right)←newdiv¨'leftBar' 'midBar' 'rightBar'

      sp←'mainSP'horz left mid right
      sp.Items[1 3].style←⊂'width: 150px; max-height: 300px;'
      sp.Items[2].style←⊂'margin: 5px;'
      sp.style←'height: 250px; width: 100%;'

      vp←Add _.StackPanel sp(newdiv'divSampleTab')
      vp.style←'width:100%'

      PopulateLeft left
      PopulateMid mid
      PopulateRight right
    ∇

    ∇ PopulateLeft thediv;class;depths;group;items;names;ref;samples;tv;vp
     ⍝ Populate the Left Bar

      names←{0::⍬ ⋄ ¯7↓¨6⊃#.Files.DirX #.Boot.AppRoot,⍵}'/Examples/Apps/*.dyalog'
      items←(1,(⍴names)⍴2),(⍪(⊂'Apps'),names),⊂'Apps'

      :For (group ref) :In ('Base HTML'_html)('Wrapped HTML'_HTML)('Dyalog Controls'_DC)('JQueryUI'_JQ)('SyncFusion'_SF)
          names←{({#.HtmlElement=⊃⊃⌽⎕CLASS ⍵}¨⍵)/⍵}ref.(⍎¨⎕NL ¯9.4)
          class←2↓⍕ref               ⍝ Remove leading #.
          names←(3+⍴class)↓¨⍕¨names  ⍝
          samples←class FindSamples names
          names←(⊂group),⊃,/(⊂¨names),¨samples
          depths←1,∊2,⍪(⍴¨samples)⍴¨3
          items⍪←depths,(⍪names),(⊂class)
      :EndFor

      thediv.Add textspan'Samples'
      tv←thediv.Add _.ejTreeView(0 ¯1↓Samples←items)
      tv.style←'max-height: 300px'
      tv.On'nodeSelect' 'onSelectSample'('node' 'eval' 'argument.id')
    ∇

    ∇ r←space FindSamples names;folder;i;samples;suffix
      space,←(space≡'_HTML')/'plus' ⍝ _HTML is in the HTMLplus folder
      folder←#.Boot.AppRoot,'Examples/',(1↓space),'/'
      samples←{0::⍬ ⋄ ¯7↓¨6⊃#.Files.DirX ⍵}folder,'*.dyalog'
      r←(⍴names)⍴⊂''

      :For suffix :In 'Simple' 'Advanced'
          i←(((-⍴suffix)↑¨samples)∊⊂suffix)/⍳⍴samples
          i←{(⍵≤⍴names)/⍵}names⍳(-⍴suffix)↓¨samples[i]
          r[i]←r[i],¨⊂⊂suffix
      :EndFor
    ∇

    ∇ PopulateRight thediv;pages;ul
     ⍝ Populate the Right Bar

      thediv.Add textspan'Documentation'
      pages←1 2⍴'SyncFusion APIs' 'http://helpjs.syncfusion.com/js/api/ejaccordion'
      pages⍪←'SyncFusion Demos' 'http://js.syncfusion.com/demos/web/'
      pages⍪←'JQueryUI' 'https://jqueryui.com/'
      pages⍪←2 2⍴'HTML' 'http://www.w3schools.com/html/' 'CSS' 'http://www.w3schools.com/css/'
      pages[;2]←↓pages[;,2],⊂'target=_blank"'
      ul←thediv.Add _.Ul pages
      ul.style←'font-size: 10px;'

      thediv.Add _.br

      thediv.Add textspan'Resources'
      pages←1 2⍴'MiServer Forum' 'http://www.dyalog.com/forum/viewforum.php?f=34'
      pages⍪←'Blog' 'http://www.dyalog.com/blog/category/miserver/'
      pages⍪←'GitHub Repo' 'https://github.com/Dyalog/MiServer'
      pages⍪←'dyalog.com' 'http://www.dyalog.com'
      pages[;2]←↓pages[;,2],⊂'target=_blank"'
      ul←thediv.Add _.Ul pages
      ul.style←'font-size: 10px;'
    ∇

    ∇ PopulateMid mid;btns;size

      mid.Add¨3⍴_.br
      btns←LinkButton'Download MiServer' '/styles/images/download-zone.png' '/download...'
      btns,←LinkButton'Read More' '/styles/images/support.png' '/readmore...'
      mid.Add horz btns
    ∇

    ∇ r←LinkButton(label image link);a;d;size

      d←newdiv'.linkbutton'
      size←'height: 32px; width: 32px;'
      'src' 'style'(d.Add _.img).Set image size
      d.Add textspan label
      r←New _.A d link
    ∇

    ∇ r←onSelectSample;code;control;depth;folder;html;i;iframe;node;p;page;sample;samples;section;simple;source;sp;space;t;tab;text;titles;url
      :Access Public
     ⍝ When a sample is selected, call this
      node←⊃2⊃⎕VFI{((+\⍵='_')⍳2)↓⍵}⊃_PageData.node

      (control space section)←3↑,Samples[1+node-(⌽node↑Samples[;1])⍳2 1;2 3]
      (depth sample)←Samples[node;1 2]

      :If (depth=2)∧3 'Simple'≡Samples[(1↑⍴Samples)⌊node+1;1 3]
          (depth sample)←3 'Simple' ⍝ Clicked on control which has a Simple Sample
      :EndIf
      space,←(space≡'_HTML')/'plus' ⍝ _HTML is in the HTMLplus folder

      :If (depth=2)∧section≢'Apps'  ⍝ Depth 2 outside Apps means no sample
          r←'#divSampleTab'Replace''
      :Else
          folder←#.Boot.AppRoot
          url←'Examples/',(1↓space),'/',control,sample,'.dyalog'
          code←{0::,⊂'[file read failed]' ⋄ #.UnicodeFile.ReadNestedText ⍵}folder,url
          (code←New _.div(#.HTMLInput.APLToHTMLColour code)).Set'id="codeblock"'
          iframe←'src' 'width'(New _.iframe).Set('/',url,'?NoWrapper=1')800
          sp←New horz code iframe
          r←'#divSampleTab'Replace sp
      :EndIf
    ∇

:EndClass

:Class index : MiPageTemplate

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

      Use'ejTab' ⍝ May get added by callbacks

      newdiv←{⍵ New _.div}                                  ⍝ Make a div
      textspan←{'.textspan'New _.span ⍵}                    ⍝ And a span
      horz←{⍺←⊣ ⋄ r⊣(r←⍺ New _.StackPanel ⍵).Horizontal←1}  ⍝ Horizontal StackPanel

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

      (left mid right)←newdiv¨'leftBar' 'midBar' 'rightBar'

      sp←'mainSP'horz left mid right
      sp.Items[1 3].style←⊂'width: 150px; max-height: 300px;'
      sp.Items[2].style←⊂'margin: 5px;'
      sp.style←'height: 250px; width: 100%;'

      vp←Add _.StackPanel sp(newdiv'divSampleTab')
      vp.style←'width:100%'

      PopulateLeft left
      PopulateMid mid
      PopulateRight right
    ∇

    ∇ PopulateLeft thediv;class;depths;group;items;names;ref;samples;tv;vp
     ⍝ Populate the Left Bar

      names←{0::⍬ ⋄ ¯7↓¨6⊃#.Files.DirX #.Boot.AppRoot,⍵}'/Examples/Apps/*.dyalog'
      items←(1,(⍴names)⍴2),(⍪(⊂'Apps'),names),⊂'Apps'

      :For (group ref) :In ('Base HTML'_html)('Wrapped HTML'_HTML)('Dyalog Controls'_DC)('JQueryUI'_JQ)('SyncFusion'_SF)
          names←{({#.HtmlElement=⊃⊃⌽⎕CLASS ⍵}¨⍵)/⍵}ref.(⍎¨⎕NL ¯9.4)
          class←2↓⍕ref               ⍝ Remove leading #.
          names←(3+⍴class)↓¨⍕¨names  ⍝
          samples←class FindSamples names
          names←(⊂group),⊃,/(⊂¨names),¨samples
          depths←1,∊2,⍪(⍴¨samples)⍴¨3
          items⍪←depths,(⍪names),(⊂class)
      :EndFor

      thediv.Add textspan'Samples'
      tv←thediv.Add _.ejTreeView(0 ¯1↓Samples←items)
      tv.style←'max-height: 300px'
      tv.On'nodeSelect' 'onSelectSample'('node' 'eval' 'argument.id')
    ∇

    ∇ r←space FindSamples names;folder;i;samples;suffix
      space,←(space≡'_HTML')/'plus' ⍝ _HTML is in the HTMLplus folder
      folder←#.Boot.AppRoot,'Examples/',(1↓space),'/'
      samples←{0::⍬ ⋄ ¯7↓¨6⊃#.Files.DirX ⍵}folder,'*.dyalog'
      r←(⍴names)⍴⊂''

      :For suffix :In 'Simple' 'Advanced'
          i←(((-⍴suffix)↑¨samples)∊⊂suffix)/⍳⍴samples
          i←{(⍵≤⍴names)/⍵}names⍳(-⍴suffix)↓¨samples[i]
          r[i]←r[i],¨⊂⊂suffix
      :EndFor
    ∇

    ∇ PopulateRight thediv;pages;ul
     ⍝ Populate the Right Bar

      thediv.Add textspan'Documentation'
      pages←1 2⍴'SyncFusion APIs' 'http://helpjs.syncfusion.com/js/api/ejaccordion'
      pages⍪←'SyncFusion Demos' 'http://js.syncfusion.com/demos/web/'
      pages⍪←'JQueryUI' 'https://jqueryui.com/'
      pages⍪←2 2⍴'HTML' 'http://www.w3schools.com/html/' 'CSS' 'http://www.w3schools.com/css/'
      pages[;2]←↓pages[;,2],⊂'target=_blank"'
      ul←thediv.Add _.Ul pages
      ul.style←'font-size: 10px;'

      thediv.Add _.br

      thediv.Add textspan'Resources'
      pages←1 2⍴'MiServer Forum' 'http://www.dyalog.com/forum/viewforum.php?f=34'
      pages⍪←'Blog' 'http://www.dyalog.com/blog/category/miserver/'
      pages⍪←'GitHub Repo' 'https://github.com/Dyalog/MiServer'
      pages⍪←'dyalog.com' 'http://www.dyalog.com'
      pages[;2]←↓pages[;,2],⊂'target=_blank"'
      ul←thediv.Add _.Ul pages
      ul.style←'font-size: 10px;'
    ∇

    ∇ PopulateMid mid;btns;size

      mid.Add¨3⍴_.br
      btns←LinkButton'Download MiServer' '/styles/images/download-zone.png' '/download...'
      btns,←LinkButton'Read More' '/styles/images/support.png' '/readmore...'
      mid.Add horz btns
    ∇

    ∇ r←LinkButton(label image link);a;d;size

      d←newdiv'.linkbutton'
      size←'height: 32px; width: 32px;'
      'src' 'style'(d.Add _.img).Set image size
      d.Add textspan label
      r←New _.A d link
    ∇

    ∇ r←onSelectSample;code;control;depth;folder;html;i;iframe;node;p;page;sample;samples;section;simple;source;sp;space;t;tab;text;titles;url
      :Access Public
     ⍝ When a sample is selected, call this
      node←⊃2⊃⎕VFI{((+\⍵='_')⍳2)↓⍵}⊃_PageData.node

      (control space section)←3↑,Samples[1+node-(⌽node↑Samples[;1])⍳2 1;2 3]
      (depth sample)←Samples[node;1 2]

      :If (depth=2)∧3 'Simple'≡Samples[(⊃⍴Samples)⌊node+1;1 2]
          (depth sample)←3 'Simple' ⍝ Clicked on control which has a Simple Sample
      :EndIf
      space,←(space≡'_HTML')/'plus' ⍝ _HTML is in the HTMLplus folder
      :If (depth=2)∧section≢'Apps'  ⍝ Depth 2 outside Apps means no sample
          r←'#divSampleTab'Replace''
      :Else
          folder←#.Boot.AppRoot
          url←'Examples/',(1↓space),'/',control,sample,'.dyalog'
          code←{0::,⊂'[file read failed]' ⋄ #.UnicodeFile.ReadNestedText ⍵}folder,url
          (code←New _.div(#.HTMLInput.APLToHTMLColour code)).Set'id="codeblock"'
          iframe←'src' 'width'(New _.iframe).Set('/',url,'?NoWrapper=1')800
          sp←New horz code iframe
          r←'#divSampleTab'Replace sp
      :EndIf
    ∇

:EndClass

Allow click on a control in index page to show Simple sample if it exists.


:Class EditFieldSimple : MiPageSample
⍝ Control:: _DC.EditField _DC.Button _html.label
⍝ Description:: Collect input and echo it on a button press

    ∇ Compose;btn;F1;label;name
      :Access Public

      F1←'myform'Add _.Form ⍝ Create a form
      label←('for"name"')F1.Add _.label'Please enter your name'
      name←'name'F1.Add _.EditField
      done←'done'F1.Add _.Button'Done'
      done.On'click' 'CallbackFn'

      'result'F1.Add _.div ⍝ a div to contain output, updated by CallbackFn
    ∇

    ∇ r←CallbackFn
      :Access Public
      r←'#result'Replace _.p('Hello, ',(Get'name'),'!')
    ∇

:EndClass

:Class EditFieldSimple : MiPageSample
⍝ Control:: _DC.EditField _DC.Button _html.label
⍝ Description:: Collect input and echo it on a button press

    ∇ Compose;btn;F1;label;name
      :Access Public

      F1←'myform'Add _.Form ⍝ Create a form
      label←('for="name"')F1.Add _.label'Please enter your name'
      name←'name'F1.Add _.EditField
      done←'done'F1.Add _.Button'Done'
      done.On'click' 'CallbackFn'

      'result'F1.Add _.div ⍝ a div to contain output, updated by CallbackFn
    ∇

    ∇ r←CallbackFn
      :Access Public
      r←'#result'Replace _.p('Hello, ',(Get'name'),'!')
    ∇

:EndClass

Add missing '=' in MS3/Examples/DC/EditFieldSample


:Namespace _JSS ⍝ Javascript Snippets

    (⎕ML ⎕IO)←1
⍝ this list will grow over time as usage patterns are discovered
    eis←{(,∘⊂)⍣((326∊⎕DR ⍵)<2>|≡⍵),⍵} ⍝ Enclose if simple
    quote←{0∊⍴⍵: '' ⋄ '"',(('"' ⎕R '\\\0')⍕⍵),'"'}
    ine←{0∊⍴⍺:'' ⋄ ⍵} ⍝ if not empty
    fmtSelector←{{'this'≡⍵:⍵ ⋄quote ⍵}¯2↓∊{⍵,', '}¨eis ⍵}
    fmtData←#.JSON.fromAPL ⍝ {{(326=⍵)<0=2|⍵}⎕DR ⍵:quote ⍵ ⋄ ⍕⍵}
    JAchars←#.JSON.JAchars

    ∇ r←Alert txt
    ⍝ popup alert text
      r←'alert(',(quote txt),');'
    ∇

    ∇ r←CloseWindow
    ⍝ close the browser window
      r←'var w=window.open(window.location,''_self''); w.close();'
    ∇

    ∇ r←sel Css args ⍝ JQuery css cover
      r←(sel JQuery'css')args
    ∇

    ∇ r←sel Val args ⍝ JQuery val cover
      r←(sel JQuery'val')args
    ∇

    ∇ r←sel Prop args ⍝ JQuery prop cover
      r←(sel JQuery'prop')args
    ∇

    ∇ r←sel Attr args ⍝ JQuery attr cover
      r←(sel JQuery'attr')args
    ∇

    ∇ r←sel RemoveClass args
      r←(sel JQuery'removeClass')args
    ∇

    ∇ r←sel RemoveAttr args
      r←(sel JQuery'removeAttr')args
    ∇

    ∇ r←sel Html args ⍝ JQuery html cover
      r←(sel JQuery'html')args
    ∇

    ∇ r←sel Show args
      r←(sel JQuery'show')args
    ∇

    ∇ r←sel Hide args
      r←(sel JQuery'hide')args
    ∇

    ∇ r←sel Toggle args
      r←(sel JQuery'toggle')args
    ∇

    ∇ r←sel Trigger args
      r←(sel JQuery'trigger')args
    ∇

    ∇ r←Submit sel
      r←(sel JQuery'submit')''
    ∇

    ∇ r←sel Position args;Position;inds;parameters;q;mask
      parameters←'my' 'at' 'of' 'collision' 'within'
      q←{1⌽'''''',{⍵/⍨1+''''=⍵}⍕⍵}
      Position←⎕NS ⍬
      :If 2=⍴⍴args ⍝ matrix
          args←,args
      :ElseIf 3=≡args
          args←⊃,/args
      :EndIf
      args←eis args
      inds←parameters⍳args
      :If ∨/mask←inds≤⍴parameters
          :If mask≡(2×+/mask)⍴1 0
              parameters←mask/args
              args←(1⌽mask)/args
          :EndIf
      :Else
          parameters←(⍴args)↑parameters
      :EndIf
      mask←⍬∘≢¨args
      args←mask/args
      parameters←mask/parameters
      parameters({⍎'Position.',⍺,'←',q ⍵})¨args
      :If ~0∊⍴Position.⎕NL-2
          r←0 #.JQ.JQueryfn'position'sel Position
      :EndIf
    ∇

    ∇ r←{eval}JSDate date
    ⍝ snippet to create a JS date (JavaScript months are 0-11!!!)
    ⍝ date is in 6↑⎕TS form
    ⍝ eval is optional Boolean to indicate whether to prepend '⍎' to the snippet (default is 0=do not prepend)
    ⍝         prepending ⍎ tells MiServer that this is an executable phrase and not simply a string

      :If 0=⎕NC'eval' ⋄ eval←0 ⋄ :EndIf
      :If 0 2∊⍨10|⎕DR date  ⍝ char?
          date←'"',date,'"'
      :Else
          date←1↓∊','∘,∘⍕¨0 ¯1 0 0 0 0+6↑date
      :EndIf
⍝      r←(~eval)↓'⍎new Date(',date,')'
⍝ ⍝ 180221, MBaas: ⍎ no longer supported
      :If eval
          r←⊂'new Date(',date,')'
      :Else
          r←'new Date(',date,')'
      :EndIf
    ∇

    ∇ r←{varname}JSData data
    ⍝ return var definition to build a JavaScript object based on data
    ⍝ varname is the name of the variable
    ⍝ data is either a matrix of [1;] column names, (1↓) data
    ⍝      or a vector of namespaces
      :If 0=⎕NC'varname' ⋄ varname←'' ⋄ :EndIf
      :If 2=⍴⍴data
          data←#.JSON.fromTable data
      :EndIf
      r←(((~0∊⍴varname)/'var ',varname,' = '),0 0 1 #.JSON.fromAPL data),';'
    ∇

    ∇ r←{val}(sel JQuery fn)args;opt
    ⍝ construct JavaScript to call a jQuery function - eg val(), html(), css(), prop(), or attr()
    ⍝ optionally setting a value for
    ⍝ Get a jQuery parameter:
    ⍝    ('"#id"' JQuery 'attr') '"data-role"'
    ⍝ Set a jQuery parameter:
    ⍝    '"blue"' ('#id' JQuery 'css') 'background-color'
    ⍝
      args←eis args
      :If 0=⎕NC'val'
          (opt val)←2↑args,(⍴args)↓''⎕NULL
      :Else
          opt←⊃args
      :EndIf
      :If val≡⎕NULL
          r←'$(',(fmtSelector sel),').',fn,'(',(quote opt),');'
      :Else
          r←'$(',(fmtSelector sel),').',fn,'(',(quote opt),',',(fmtData val),');'
      :EndIf
    ∇

    ∇ r←{val}(fn JQueryOpt sel)opt
      :If 0=⎕NC'val'
          r←'$(',(fmtSelector sel),').',fn,'("option",',(quote opt),');'
      :Else
          r←'$(',(fmtSelector sel),').',fn,'("option",',(quote opt),',',(fmtData val),');'
      :EndIf
    ∇

    :Class StorageObject
    ⍝ !!!Prototype!!!

        ∇ r←{what}Set(type value);name;w;v
          :Access public shared
        ⍝ value may be
          :Access public shared
          r←''
          :If 9.1=⎕NC⊂'value' ⍝ namespace?
              :For name :In value.⎕NL-2
                  r,←formatSet(type name(value⍎name))
              :EndFor
          :Else
              value←eis value
              :If 2=⎕NC'what' ⍝ value is list of name value pairs
                  what←eis what
              :Else
                  (what value)←↓[1]((⌊0.5×⍴value),2)⍴value
              :EndIf
              :For (w v) :InEach (what value)
                  r,←formatSet(type w v)
              :EndFor
          :EndIf
        ∇

        ∇ r←formatSet(type what value)
          :Access public shared
          r←type,'.setItem("',what,'",JSON.stringify(',(#.JSON.fromAPL value),');'
        ∇

        ∇ r←type Remove what;w;ww
          :Access public shared
          what←eis what
          r←''
          :For w :In what
              :If ' '∊w←#.Strings.deb w
                  :For ww :In ' '#.Utils.penclose w
                      r,←type,'.removeItem("',w,'");'
                  :EndFor
              :Else
                  r,←type,'.removeItem("',w,'");'
              :EndIf
          :EndFor
        ∇

        ∇ r←type Get what
          :Access public shared
          r←''
          r,←'<input type="hidden" name="',w,'" value="',JSON
        ∇
    :endclass

    :class localStorage : StorageObject

        ∇ r←{what}Set value
          :If 0=⎕NC'what'
              r←⎕BASE.Set('localStorage'value)
          :Else
              r←what ⎕BASE.Set('localStorage'value)
          :EndIf
        ∇

        ∇ r←Remove what
          :Access public shared
          r←
        ∇

        ∇ r←{name}Get what
          :Access public shared
        ∇

    :EndClass

    :Class sessionStorage
        ∇ r←{what}Set value
          :Access public shared
        ∇
    :endclass

    :class sessionStorage
    :endclass

:EndNamespace

:Namespace _JSS ⍝ Javascript Snippets

    (⎕ML ⎕IO)←1
⍝ this list will grow over time as usage patterns are discovered
    eis←{(,∘⊂)⍣((326∊⎕DR ⍵)<2>|≡⍵),⍵} ⍝ Enclose if simple
    quote←{0∊⍴⍵: '' ⋄ '"',(('"' ⎕R '\\\0')⍕⍵),'"'}
    ine←{0∊⍴⍺:'' ⋄ ⍵} ⍝ if not empty
    fmtSelector←{{'this'≡⍵:⍵ ⋄quote ⍵}¯2↓∊{⍵,', '}¨eis ⍵}
    fmtData←#.JSON.fromAPL ⍝ {{(326=⍵)<0=2|⍵}⎕DR ⍵:quote ⍵ ⋄ ⍕⍵}
    JAchars←#.JSON.JAchars

    ∇ r←Alert txt
    ⍝ popup alert text
      r←'alert(',(quote txt),');'
    ∇

    ∇ r←CloseWindow
    ⍝ close the browser window
      r←'var w=window.open(window.location,''_self''); w.close();'
    ∇

    ∇ r←sel Css args ⍝ JQuery css cover
      r←(sel JQuery'css')args
    ∇

    ∇ r←sel Val args ⍝ JQuery val cover
      r←(sel JQuery'val')args
    ∇

    ∇ r←sel Prop args ⍝ JQuery prop cover
      r←(sel JQuery'prop')args
    ∇

    ∇ r←sel Attr args ⍝ JQuery attr cover
      r←(sel JQuery'attr')args
    ∇

    ∇ r←sel RemoveAttr args
      r←(sel JQuery'removeAttr')args
    ∇

    ∇ r←sel Html args ⍝ JQuery html cover
      r←(sel JQuery'html')args
    ∇

    ∇ r←sel Show args
      r←(sel JQuery'show')args
    ∇

    ∇ r←sel Hide args
      r←(sel JQuery'hide')args
    ∇

    ∇ r←sel Toggle args
      r←(sel JQuery'toggle')args
    ∇

    ∇ r←sel Trigger args
      r←(sel JQuery'trigger')args
    ∇

    ∇ r←Submit sel
      r←(sel JQuery'submit')''
    ∇

    ∇ r←sel Position args;Position;inds;parameters;q;mask
      parameters←'my' 'at' 'of' 'collision' 'within'
      q←{1⌽'''''',{⍵/⍨1+''''=⍵}⍕⍵}
      Position←⎕NS ⍬
      :If 2=⍴⍴args ⍝ matrix
          args←,args
      :ElseIf 3=≡args
          args←⊃,/args
      :EndIf
      args←eis args
      inds←parameters⍳args
      :If ∨/mask←inds≤⍴parameters
          :If mask≡(2×+/mask)⍴1 0
              parameters←mask/args
              args←(1⌽mask)/args
          :EndIf
      :Else
          parameters←(⍴args)↑parameters
      :EndIf
      mask←⍬∘≢¨args
      args←mask/args
      parameters←mask/parameters
      parameters({⍎'Position.',⍺,'←',q ⍵})¨args
      :If ~0∊⍴Position.⎕NL-2
          r←0 #.JQ.JQueryfn'position'sel Position
      :EndIf
    ∇

    ∇ r←{eval}JSDate date
    ⍝ snippet to create a JS date (JavaScript months are 0-11!!!)
    ⍝ date is in 6↑⎕TS form
    ⍝ eval is optional Boolean to indicate whether to prepend '⍎' to the snippet (default is 0=do not prepend)
    ⍝         prepending ⍎ tells MiServer that this is an executable phrase and not simply a string

      :If 0=⎕NC'eval' ⋄ eval←0 ⋄ :EndIf
      :If 0 2∊⍨10|⎕DR date  ⍝ char?
          date←'"',date,'"'
      :Else
          date←1↓∊','∘,∘⍕¨0 ¯1 0 0 0 0+6↑date
      :EndIf
⍝      r←(~eval)↓'⍎new Date(',date,')'
⍝ ⍝ 180221, MBaas: ⍎ no longer supported
      :If eval
          r←⊂'new Date(',date,')'
      :Else
          r←'new Date(',date,')'
      :EndIf
    ∇

    ∇ r←{varname}JSData data
    ⍝ return var definition to build a JavaScript object based on data
    ⍝ varname is the name of the variable
    ⍝ data is either a matrix of [1;] column names, (1↓) data
    ⍝      or a vector of namespaces
      :If 0=⎕NC'varname' ⋄ varname←'' ⋄ :EndIf
      :If 2=⍴⍴data
          data←#.JSON.fromTable data
      :EndIf
      r←(((~0∊⍴varname)/'var ',varname,' = '),0 0 1 #.JSON.fromAPL data),';'
    ∇

    ∇ r←{val}(sel JQuery fn)args;opt
    ⍝ construct JavaScript to call a jQuery function - eg val(), html(), css(), prop(), or attr()
    ⍝ optionally setting a value for
    ⍝ Get a jQuery parameter:
    ⍝    ('"#id"' JQuery 'attr') '"data-role"'
    ⍝ Set a jQuery parameter:
    ⍝    '"blue"' ('#id' JQuery 'css') 'background-color'
    ⍝
      args←eis args
      :If 0=⎕NC'val'
          (opt val)←2↑args,(⍴args)↓''⎕NULL
      :Else
          opt←⊃args
      :EndIf
      :If val≡⎕NULL
          r←'$(',(fmtSelector sel),').',fn,'(',(quote opt),');'
      :Else
          r←'$(',(fmtSelector sel),').',fn,'(',(quote opt),',',(fmtData val),');'
      :EndIf
    ∇

    ∇ r←{val}(fn JQueryOpt sel)opt
      :If 0=⎕NC'val'
          r←'$(',(fmtSelector sel),').',fn,'("option",',(quote opt),');'
      :Else
          r←'$(',(fmtSelector sel),').',fn,'("option",',(quote opt),',',(fmtData val),');'
      :EndIf
    ∇

    :Class StorageObject
    ⍝ !!!Prototype!!!

        ∇ r←{what}Set(type value);name;w;v
          :Access public shared
        ⍝ value may be
          :Access public shared
          r←''
          :If 9.1=⎕NC⊂'value' ⍝ namespace?
              :For name :In value.⎕NL-2
                  r,←formatSet(type name(value⍎name))
              :EndFor
          :Else
              value←eis value
              :If 2=⎕NC'what' ⍝ value is list of name value pairs
                  what←eis what
              :Else
                  (what value)←↓[1]((⌊0.5×⍴value),2)⍴value
              :EndIf
              :For (w v) :InEach (what value)
                  r,←formatSet(type w v)
              :EndFor
          :EndIf
        ∇

        ∇ r←formatSet(type what value)
          :Access public shared
          r←type,'.setItem("',what,'",JSON.stringify(',(#.JSON.fromAPL value),');'
        ∇

        ∇ r←type Remove what;w;ww
          :Access public shared
          what←eis what
          r←''
          :For w :In what
              :If ' '∊w←#.Strings.deb w
                  :For ww :In ' '#.Utils.penclose w
                      r,←type,'.removeItem("',w,'");'
                  :EndFor
              :Else
                  r,←type,'.removeItem("',w,'");'
              :EndIf
          :EndFor
        ∇

        ∇ r←type Get what
          :Access public shared
          r←''
          r,←'<input type="hidden" name="',w,'" value="',JSON
        ∇
    :endclass

    :class localStorage : StorageObject

        ∇ r←{what}Set value
          :If 0=⎕NC'what'
              r←⎕BASE.Set('localStorage'value)
          :Else
              r←what ⎕BASE.Set('localStorage'value)
          :EndIf
        ∇

        ∇ r←Remove what
          :Access public shared
          r←
        ∇

        ∇ r←{name}Get what
          :Access public shared
        ∇

    :EndClass

    :Class sessionStorage
        ∇ r←{what}Set value
          :Access public shared
        ∇
    :endclass

    :class sessionStorage
    :endclass

:EndNamespace

Revert "Added _JSS.RemoveClass"

Revert "Added _JSS.RemoveClass"

This reverts commit d3e328a8550be6fa274a8778708866417d79d28e.


: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
      :Access public
      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
    ∇

    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

reset _.Icon Content, Spec, and class after Render


:class InputGrid : #._html.table

    :field public Border←0
    :field public Labels←''
    :field public Inputs←''
    :field public Headings←''
    :field public LabelPosition←'left' ⍝ valid values are 'left' 'right'
    :field public Horizontal←0 ⍝ default is vertical

    ∇ make
      :Access public
      :Implements constructor :base
    ∇

    ∇ make1 args;data;sel
      :Access public
      :Implements constructor
      args←eis args
      :Select ⊃⍴args
      :Case 1
          :Select ⊃⍴⍴args←⊃args
          :Case 1
              Labels←Values←data
          :Case 2
              (Labels Values)←↓[1]data
          :EndSelect
      :Case 2 ⍝ Labels Inputs
          (Labels Inputs)←args
      :EndSelect
    ∇

    ∇ html←Render;cells;rows
      :Access public
      cells←{⎕NEW #._html.td(⍵)}¨renderIt¨Labels,[1.1]Inputs
      cells[;1].style←⊂'text-align:right'
      cells←(⍉⍣Horizontal)cells
      rows←⎕NEW¨(⊃⍴cells)⍴#._html.tr
      rows.Add↓cells
      Content←rows.Render
      Set'border'(⍕Border)
      html←⎕BASE.Render
    ∇
:endclass

:class InputGrid : #._html.table

    :field public Border←0
    :field public Labels←''
    :field public Inputs←''
    :field public Headings←''
    :field public LabelPosition←'left' ⍝ valid values are 'left' 'right'
    :field public Horizontal←0 ⍝ default is vertical

    ∇ make
      :Access public
      :Implements constructor :base
    ∇

    ∇ make1 args;data;sel
      :Access public
      :Implements constructor
      args←eis args
      :Select ⊃⍴args
      :Case 1
          :Select ⊃⍴⍴args←⊃args
          :Case 1
              Labels←Values←data
          :Case 2
              (Labels Values)←↓[1]data
          :EndSelect
      :Case 2 ⍝ Labels Inputs
          (Labels Inputs)←args
      :EndSelect
    ∇

    ∇ html←Render;cells;rows
      :Access public
      cells←{⎕NEW #._html.td(⍵)}¨renderIt¨(,Labels),[1.1],Inputs
      cells[;1].style←⊂'text-align:right'
      cells←(⍉⍣Horizontal)cells
      rows←⎕NEW¨(⊃⍴cells)⍴#._html.tr
      rows.Add↓cells
      Content←rows.Render
      Set'border'(⍕Border)
      html←⎕BASE.Render
    ∇
:endclass

make sure InputGrid labels and inputs are vector


revert an accident change to demo.apl

[APL]
revert an accident change to demo.apl

git-svn-id: 6e74a02f85675cec270f5d931b0f6998666294a3@12645 d31e2699-5ff4-0310-a27c-f18f2fbe73fe


:Namespace QA
⍝ MiServer 3 QA suite
    ∇ Run
      RenderHTML
      RenderPages
    ∇

    ∇ result←RenderHTML;nss;ns;class;c;name;res;f;root
⍝ make sure all HTML-generating classes render
      nss←#.(_html _HTML _JQ _SF) ⍝ add _JQM when it's ready
      root←#.Boot.MSRoot
      result←0 6⍴'' 0 0 0 0 0  ⍝ [1] name [2] DocBase? [3] DocDyalog? [3] ApiLevel [4] Renders [5] id=constructor[1]
      :For ns :In nss
          :For class :In (ns.⎕NL ¯9)~(⊂'SALT_Data'),'_'ns.⎕NL ¯9
              res←0 0 0 0 'N/A'
              name←⍕c←ns⍎class
              res[1]←0≠c.⎕NC⊂'DocBase'
              :If res[2]←0≠f←c.⎕NC⊂'DocDyalog'
                  res[2]×←¯1*#.Files.Exists root,c.DocDyalog
              :EndIf
              res[3]←{6::0 ⋄ ⍵.ApiLevel}c
              res[4]←{0::0 ⋄ 1⊣(⎕NEW ⍵).Render}c
              :If #._html≢ns
                  res[5]←{0::0 ⋄ 'xyz'≡(⎕NEW ⍵'xyz').id}c
              :EndIf
              result⍪←(⊂name),res
          :EndFor
      :EndFor
      result←'Element/Widget' 'DocBase?' 'DocDyalog?' 'ApiLevel' 'Renders?' 'id=args[1]?'⍪result
    ∇

    ∇ r←RenderPages;class
      r←0 2⍴'' 0
      r⍪←{⍵(_Test_Page ⍵)}#.Boot.MSRoot,'QA/TestMiPageTemplate'
      r⍪←{⍵(_Test_Page ⍵)}#.Boot.MSRoot,'QA/TestMiPageSample'
      r⍪←{⍵(_Test_Page ⍵)}#.Boot.MSRoot,'QA/TestMS3Page'
      r⍪←{⍵(_Test_Page ⍵)}#.Boot.MSRoot,'QA/TestMS2Page'
    ∇

    ∇ r←_Test_Page page;class;z
      r←'Passed'
      :Trap 0
          class←⎕SE.SALT.Load page
          z←⎕NEW class
          z._Request←⎕NEW #.HTTPRequest('' '')
          z._Request.Server←#.Boot.ms
          {}⎕XML z.Wrap
      :Else
          r←∊⎕DM
      :EndTrap
    ∇
:EndNamespace

:Namespace QA
⍝ MiServer 3 QA suite
    ∇ Run
      RenderHTML
      RenderPages
    ∇

    ∇ result←RenderHTML;nss;ns;class;c;name;res;f;root
⍝ make sure all HTML-generating classes render
      nss←#.(_html _HTML _DC _JQ _SF) ⍝ add _JQM when it's ready
      root←#.Boot.MSRoot
      result←0 6⍴'' 0 0 0 0 0  ⍝ [1] name [2] DocBase? [3] DocDyalog? [3] ApiLevel [4] Renders [5] id=constructor[1]
      :For ns :In nss
          :For class :In (ns.⎕NL ¯9)~(⊂'SALT_Data'),'_'ns.⎕NL ¯9
              res←0 0 0 0 'N/A'
              name←⍕c←ns⍎class
              res[1]←0≠c.⎕NC⊂'DocBase'
              :If res[2]←0≠f←c.⎕NC⊂'DocDyalog'
                  res[2]×←¯1*#.Files.Exists root,c.DocDyalog
              :EndIf
              res[3]←{6::0 ⋄ ⍵.ApiLevel}c
              res[4]←{0::0 ⋄ 1⊣(⎕NEW ⍵).Render}c
              :If #._html≢ns
                  res[5]←{0::0 ⋄ 'xyz'≡(⎕NEW ⍵'xyz').id}c
              :EndIf
              result⍪←(⊂name),res
          :EndFor
      :EndFor
      result←'Element/Widget' 'DocBase?' 'DocDyalog?' 'ApiLevel' 'Renders?' 'id=args[1]?'⍪result
    ∇

    ∇ r←RenderPages;class
      r←0 2⍴'' 0
      r⍪←{⍵(_Test_Page ⍵)}#.Boot.MSRoot,'QA/TestMiPageTemplate'
      r⍪←{⍵(_Test_Page ⍵)}#.Boot.MSRoot,'QA/TestMiPageSample'
      r⍪←{⍵(_Test_Page ⍵)}#.Boot.MSRoot,'QA/TestMS3Page'
      r⍪←{⍵(_Test_Page ⍵)}#.Boot.MSRoot,'QA/TestMS2Page'
    ∇

    ∇ r←_Test_Page page;class;z
      r←'Passed'
      :Trap 0
          class←⎕SE.SALT.Load page
          z←⎕NEW class
          z._Request←⎕NEW #.HTTPRequest('' '')
          z._Request.Server←#.Boot.ms
          {}⎕XML z.Wrap
      :Else
          r←∊⎕DM
      :EndTrap
    ∇
:EndNamespace

Add Insert method to HtmlPage

Add Insert method to HtmlPage


:Class FontIconsBase :  #.HtmlElement
    :field public shared readonly DocBase←'http://fortawesome.github.io/Font-Awesome/icons/'
    :field public Family←'fa'  ⍝ bt=Blacktie or other prefixes for FontIcons
    :field public Container←'i'
    :field public icon←''
    :field public StackSize←'1x'    ⍝ only relevant for the outer tag in stacks!
    :field private Args←''
    :field private Stack←⍳0

    ∇ Make0
    ⍝ empty constructor mostly used for stacks, but possibly for "normal" icons as well (assign icon after construction!)
      :Access public
      :Implements Constructor
    ∇

    ∇ Make1 args
      :Access Public
    ⍝ args is the name of the icon (w/o prefix) and possibly other specs
    ⍝ (such as 2x - again, w/o prefix, space-separated, ie "camera 2x"!)
      icon←args
      Args←args
      :Implements Constructor    :Base args
    ∇

    ∇ Make2(cnt atts)
      :Access public
      Args←cnt atts
      icon←cnt
      :Implements Constructor    :Base'' ('' atts)
      Content←''
⍝ setting Content to '' shouldn't be neccessary, but without it, we're getting wrong results as seen below:
⍝ d←⎕new MiPage
⍝ d.Add _.FontAwesome 'camera' '#bla'
⍝ +d.Render
⍝<!DOCTYPE html>
⍝#bla
⍝                                       ↑↑↑↑ somehow the id made it into the Content!
⍝
⍝
    ∇

    ∇ {r}←stack arg
      :Access public
      r←New FontIconsBase arg
      r.Family←Family
      Stack,←r

    ∇

    ∇ ul items;item;ulCnt;Cnt;atts;iTag
      :Access public
 ⍝ pairs is a nested vector of class/text,
 ⍝ i.e. (('check-square' 'List icons')('check-square' 'can be used')...)
 ⍝ it is also possibly to add attributes after the 1st two elems to assign an id or style to the li
 ⍝ (to add attributes to the inner i-Attribute, nest the 2nd element!)
 ⍝ And when you need to assign id to the content, you'll have to embed it into a span yourself:
 ⍝ ('check-square' 'List Icons')
 ⍝ (NB: it would also be possible to override the class which will ruin the purpose,
 ⍝  but I wasn't sure whether we should take care to avoid that by eliminating '.'-elems
 ⍝  or if it wouldn't be better (and easier) to just create the mess the user asked for ;-))
 ⍝ For discussion: only collect the items here, so that multiple ul-Calls
 ⍝ are possible to add to a single list (so all the "work" should be done in Render instead)
      :If 2=≡items ⋄ items←,⊂items ⋄ :EndIf
      ulCnt←'' ⋄ Content←''
      :For item :In items
          (icon Cnt)←2↑item
          atts←'' ⋄ :If 1<|≡Cnt ⋄ (Cnt atts)←Cnt ⋄ :EndIf
          icon←'li ',icon
          iTag←(New _.i''((⊂'.',setClass 1),eis atts)).Render,Cnt
          ulCnt,←(New _.li iTag(2↓item)).Render
      :EndFor
      icon←''
      Content←ulCnt
      class←'fa-ul'
      Container←'ul'
    ∇

    ∇ r←Render;icon3;i1;i2;Stack∆
      :Access Public

    ⍝ unfortunately this usage of "Uses" does not work, if has no effect on the header (also see my email from Oct17th)
      :Select Family
      :Case 'fa' ⋄ Uses←'FontAwesome'
      :Case 'bt' ⋄ Uses←'BlackTie'
      :Else
             ⍝ when user uses FontIcon-app, he needs to handle "Use" on his own!
      :EndSelect

      :If 0<⍴Stack
          Stack∆←Stack
          Stack←⍳0
          :If 0<⍴icon
              icon←(RemoveSize icon),' stack',GetSize
              r←Render  ⍝ first elem
          :Else
              r←''
          :EndIf
          ⎕←'first=',r
          :For obj :In Stack∆
              obj.icon←(RemoveSize obj.icon),' stack',obj.GetSize
              r∆←obj.Render
              r,←r∆
          :EndFor
          icon←'stack ',StackSize ⋄ Container←'span'
          Content←r
      :EndIf
      Tag←Container
      :If 0<⍴icon ⋄ icon←SetSize ⋄ class←setClass(0=⎕NC'Stack∆') ⋄ :EndIf
      r←⎕BASE.Render
    ∇

    ∇ r←GetSize
    ⍝ searches icon for a size-specification
      :Access public
      :If 0=⍴r←('-(lg|[12345]x)'⎕S'\1')icon
          r←'-1x'
      :EndIf
    ∇

    ∇ res←SetSize
    ⍝ searches icon for a size-specification
      :Access public
      res←icon
      :If 0=⍴('-(lg|[12345]x)'⎕S'\1')icon     ⍝ do not use GetSize here - we're interested to see if it has been set or not
          res←icon,' 1x'
      :EndIf
    ∇

    ∇ res←RemoveSize res
    ⍝ remove Size attribute from element
      :Access public
      res←('-(lg|[12345]x)'⎕R'')res
    ∇

    ∇ R←setClass includeFamily
      R←Family{⎕ML←1 ⋄ (includeFamily/⍺),∊(' ',⍺,'-')∘,¨1↓¨(⍵=' ')⊂⍵}' ',icon
    ∇
:EndClass

:Class FontIconsBase :  #.HtmlElement
    :field public shared readonly DocBase←'http://fontawesome.io/icons/'
    :field public Family←'fa'  ⍝ bt=Blacktie or other prefixes for FontIcons
    :field public Resource←'placeholder'
    :field public Container←'i'
    :field public icon←''
    :field public StackSize←'1x'    ⍝ only relevant for the outer tag in stacks!
    :field private Args←''
    :field private Stack←⍳0

    ∇ Make0
    ⍝ empty constructor mostly used for stacks, but possibly for "normal" icons as well (assign icon after construction!)
      :Access public
      :Implements Constructor
    ∇

    ∇ Make1 args
      :Access Public
    ⍝ args is the name of the icon (w/o prefix) and possibly other specs
    ⍝ (such as 2x - again, w/o prefix, space-separated, ie "camera 2x"!)
      icon←args
      Args←args
      :Implements Constructor    :Base args
    ∇

    ∇ Make2(cnt atts)
      :Access public
      Args←cnt atts
      icon←cnt
      :Implements Constructor    :Base'' ('' atts)
      Content←''
    ∇

    ∇ {r}←stack arg
      :Access public
      r←New FontIconsBase arg
      r.Family←Family
      Stack,←r

    ∇

    ∇ ul items;item;ulCnt;Cnt;atts;iTag
      :Access public
 ⍝ pairs is a nested vector of class/text,
 ⍝ i.e. (('check-square' 'List icons')('check-square' 'can be used')...)
 ⍝ it is also possibly to add attributes after the 1st two elems to assign an id or style to the li
 ⍝ (to add attributes to the inner i-Attribute, nest the 2nd element!)
 ⍝ And when you need to assign id to the content, you'll have to embed it into a span yourself:
 ⍝ ('check-square' 'List Icons')
 ⍝ (NB: it would also be possible to override the class which will ruin the purpose,
 ⍝  but I wasn't sure whether we should take care to avoid that by eliminating '.'-elems
 ⍝  or if it wouldn't be better (and easier) to just create the mess the user asked for ;-))
 ⍝ For discussion: only collect the items here, so that multiple ul-Calls
 ⍝ are possible to add to a single list (so all the "work" should be done in Render instead)
      :If 2=≡items ⋄ items←,⊂items ⋄ :EndIf
      ulCnt←'' ⋄ Content←''
      :For item :In items
          (icon Cnt)←2↑item
          atts←'' ⋄ :If 1<|≡Cnt ⋄ (Cnt atts)←Cnt ⋄ :EndIf
          icon←'li ',icon
          iTag←(New _.i''((⊂'.',setClass 1),eis atts)).Render,Cnt
          ulCnt,←(New _.li iTag(2↓item)).Render
      :EndFor
      icon←''
      Content←ulCnt
      class←'fa-ul'
      Container←'ul'
    ∇

    ∇ r←Render;icon3;i1;i2;Stack∆
      :Access Public

    ⍝ Brian: unfortunately this usage of "Uses" does not work, if has no effect on the header (also see my email from Oct17th)
      Uses←Resource

      :If 0<⍴Stack
          Stack∆←Stack
          Stack←⍳0
          :If 0<⍴icon
              icon←(RemoveSize icon),' stack',GetSize
              r←Render  ⍝ first elem
          :Else
              r←''
          :EndIf
          :For obj :In Stack∆
              obj.icon←(RemoveSize obj.icon),' stack',obj.GetSize
              r∆←obj.Render
              r,←r∆
          :EndFor
          icon←'stack ',StackSize ⋄ Container←'span'
          Content←r
      :EndIf
      Tag←Container
      :If 0<⍴icon ⋄ icon←SetSize ⋄ class←setClass(0=⎕NC'Stack∆') ⋄ :EndIf
      r←⎕BASE.Render
    ∇

    ∇ r←GetSize
    ⍝ searches icon for a size-specification
      :Access public
      :If 0=⍴r←('-(lg|[12345]x)'⎕S'\1')icon
          r←'-1x'
      :EndIf
    ∇

    ∇ res←SetSize
    ⍝ searches icon for a size-specification
      :Access public
      res←icon
      :If 0=⍴('-(lg|[12345]x)'⎕S'\1')icon     ⍝ do not use GetSize here - we're interested to see if it has been set or not
          res←icon,' 1x'
      :EndIf
    ∇

    ∇ res←RemoveSize res
    ⍝ remove Size attribute from element
      :Access public
      res←('-(lg|[12345]x)'⎕R'')res
    ∇

    ∇ R←setClass includeFamily
      R←Family{⎕ML←1 ⋄ (includeFamily/⍺),∊(' ',⍺,'-')∘,¨1↓¨(⍵=' ')⊂⍵}' ',icon
    ∇

    ∇ {R}←Classname DeriveClass(rsc fmly);cls
      :Access public shared
      ⍝ example: 'myfa'DeriveClass'FontAwesome'  'fa'
      ⍝          Add _.myfa'camera'
      :If rsc[1]=':'
          rsc←'⍎https://use.fonticons.com/',(1↓rsc),'.js'
      :EndIf
      cls←ScriptFollows
      ⍝ :Class %Classname : #._DC.FontIconsBase
      ⍝   :field public readonly family←'%family'
      ⍝   :field public readonly Resource←'%Resource'
      ⍝   ∇ make0
      ⍝     :access public
      ⍝      Resource {6::⍺∇1↓⍵ ⋄ (1⊃⍵).Use ⍺}⎕rsi ⍝ find environment where we can execute "Use"
      ⍝     :implements constructor :base
      ⍝   ∇
      ⍝
      ⍝   ∇ make1 arg
      ⍝     :access public
      ⍝      Resource {6::⍺∇1↓⍵ ⋄ (1⊃⍵).Use ⍺}⎕rsi ⍝ find environment where we can execute "Use"
      ⍝     :implements constructor :base arg
      ⍝   ∇
      ⍝
      ⍝   ∇ make2 (arg1 arg2)
      ⍝     :access public
      ⍝      Resource {6::⍺∇1↓⍵ ⋄ (1⊃⍵).Use ⍺}⎕rsi ⍝ find environment where we can execute "Use"
      ⍝     :implements constructor :base (arg1 arg2)
      ⍝   ∇
      ⍝ :EndClass
      cls←(('%Classname'Classname)('%family'fmly)('%Resource'rsc))Subst cls

      ⍝ Morten: ⎕fix did not like to fix the class as vector with NL - perhaps an extension for the future???
      cls←(⎕UCS 13)#.Utils.penclose cls~⎕UCS 10

      :If 0=⎕NC'#._' ⋄ '#._'⎕NS'' ⋄ :EndIf ⍝ might not be there yet during boot, when FontAwesome-Class is being set up...
      R←#._.⎕FIX cls
    ∇

   ⍝ ideally FontAwesome should be made available right after loading this class...
   ⍝ 'FontAwesome'DeriveClass'FontAwesome' 'fa'
:EndClass

Update FontIconsBase.dyalog

Update FontIconsBase.dyalog


:Namespace _JSS ⍝ Javascript Snippets

    (⎕ML ⎕IO)←1
⍝ this list will grow over time as usage patterns are discovered
    eis←{(,∘⊂)⍣((326∊⎕DR ⍵)<2>|≡⍵),⍵} ⍝ Enclose if simple
    enlist←{⎕ML←1 ⋄ ∊⍵}
    quote←{0∊⍴⍵: '' ⋄ '"',(('"' ⎕R '\\\0')⍕⍵),'"'}
    ine←{0∊⍴⍺:'' ⋄ ⍵} ⍝ if not empty
    fmtSelector←{{'this'≡⍵:⍵ ⋄quote ⍵}¯2↓enlist{⍵,', '}¨eis ⍵}
    fmtData←{{(326=⍵)<0=2|⍵}⎕DR ⍵:quote ⍵ ⋄ ⍕⍵}
    JAchars←#.JSON.JAchars

    ∇ r←Alert txt
    ⍝ popup alert text
      r←'alert(',(quote txt),');'
    ∇

    ∇ r←CloseWindow
    ⍝ close the browser window
      r←'var w=window.open(window.location,''_self''); w.close();'
    ∇

    ∇ r←sel Css args ⍝ JQuery css cover
      r←(sel JQuery'css')args
    ∇

    ∇ r←sel Val args ⍝ JQuery val cover
      r←(sel JQuery'val')args
    ∇

    ∇ r←sel Prop args ⍝ JQuery prop cover
      r←(sel JQuery'prop')args
    ∇

    ∇ r←sel Attr args ⍝ JQuery attr cover
      r←(sel JQuery'attr')args
    ∇

    ∇ r←sel RemoveAttr args
      r←(sel JQuery'removeAttr')args
    ∇

    ∇ r←sel Html args ⍝ JQuery html cover
      r←(sel JQuery'html')args
    ∇

    ∇ r←sel Show args
      r←(sel JQuery'show')args
    ∇

    ∇ r←sel Hide args
      r←(sel JQuery'hide')args
    ∇

    ∇ r←sel Toggle args
      r←(sel JQuery'toggle')args
    ∇

    ∇ r←sel Trigger args
      r←(sel JQuery'trigger')args
    ∇

    ∇ r←Submit sel
      r←(sel JQuery'submit')''
    ∇

    ∇ r←sel Position args;Position;inds;parameters;q;mask
      parameters←'my' 'at' 'of' 'collision' 'within'
      q←{1⌽'''''',{⍵/⍨1+''''=⍵}⍕⍵}
      Position←⎕NS ⍬
      :If 2=⍴⍴args ⍝ matrix
          args←,args
      :ElseIf 3=≡args
          args←⊃,/args
      :EndIf
      args←eis args
      inds←parameters⍳args
      :If ∨/mask←inds≤⍴parameters
          :If mask≡(2×+/mask)⍴1 0
              parameters←mask/args
              args←(1⌽mask)/args
          :EndIf
      :Else
          parameters←(⍴args)↑parameters
      :EndIf
      mask←⍬∘≢¨args
      args←mask/args
      parameters←mask/parameters
      parameters({⍎'Position.',⍺,'←',q ⍵})¨args
      :If ~0∊⍴Position.⎕NL-2
          r←0 #.JQ.JQueryfn'position'sel Position
      :EndIf
    ∇

    ∇ r←{eval}JSDate date
    ⍝ snippet to create a JS date (JavaScript months are 0-11!!!)
    ⍝ date is in 6↑⎕TS form
    ⍝ eval is optional Boolean to indicate whether to prepend '⍎' to the snippet (default is 0=do not prepend)
    ⍝         prepending ⍎ tells MiServer that this is an executable phrase and not simply a string

      :If 0=⎕NC'eval' ⋄ eval←0 ⋄ :EndIf
      :If 0 2∊⍨10|⎕DR date  ⍝ char?
          date←'"',date,'"'
      :Else
          date←1↓∊','∘,∘⍕¨0 ¯1 0 0 0 0+6↑date
      :EndIf
      r←(~eval)↓'⍎new Date(',date,')'
    ∇

    ∇ r←{varname}JSData data
    ⍝ return var definition to build a JavaScript object based on data
    ⍝ varname is the name of the variable
    ⍝ data is either a matrix of [1;] column names, (1↓) data
    ⍝      or a vector of namespaces
      :If 0=⎕NC'varname' ⋄ varname←'' ⋄ :EndIf
      :If 2=⍴⍴data
          data←#.JSON.fromTable data
      :EndIf
      r←(((~0∊⍴varname)/'var ',varname,' = '),0 0 1 #.JSON.fromAPL data),';'
    ∇

    ∇ r←{val}(sel JQuery fn)args;opt
    ⍝ construct JavaScript to call a jQuery function - eg val(), html(), css(), prop(), or attr()
    ⍝ optionally setting a value for
    ⍝ Get a jQuery parameter:
    ⍝    ('"#id"' JQuery 'attr') '"data-role"'
    ⍝ Set a jQuery parameter:
    ⍝    '"blue"' ('#id' JQuery 'css') 'background-color'
    ⍝
      args←eis args
      :If 0=⎕NC'val'
          (opt val)←2↑args,(⍴args)↓''⎕NULL
      :Else
          opt←⊃args
      :EndIf
      :If val≡⎕NULL
          r←'$(',(fmtSelector sel),').',fn,'(',(quote opt),');'
      :Else
          r←'$(',(fmtSelector sel),').',fn,'(',(quote opt),',',(fmtData val),');'
      :EndIf
    ∇

    ∇ r←{val}(fn JQueryOpt sel)opt
      :If 0=⎕NC'val'
          r←'$(',(fmtSelector sel),').',fn,'("option",',(quote opt),');'
      :Else
          r←'$(',(fmtSelector sel),').',fn,'("option",',(quote opt),',',(fmtData val),');'
      :EndIf
    ∇

    :Class StorageObject
    ⍝ !!!Prototype!!!

        ∇ r←{what}Set(type value);name;w;v
          :Access public shared
        ⍝ value may be
          :Access public shared
          r←''
          :If 9.1=⎕NC⊂'value' ⍝ namespace?
              :For name :In value.⎕NL-2
                  r,←formatSet(type name(value⍎name))
              :EndFor
          :Else
              value←eis value
              :If 2=⎕NC'what' ⍝ value is list of name value pairs
                  what←eis what
              :Else
                  (what value)←↓[1]((⌊0.5×⍴value),2)⍴value
              :EndIf
              :For (w v) :InEach (what value)
                  r,←formatSet(type w v)
              :EndFor
          :EndIf
        ∇

        ∇ r←formatSet(type what value)
          :Access public shared
          r←type,'.setItem("',what,'",JSON.stringify(',(#.JSON.fromAPL value),');'
        ∇

        ∇ r←type Remove what;w;ww
          :Access public shared
          what←eis what
          r←''
          :For w :In what
              :If ' '∊w←#.Strings.deb w
                  :For ww :In ' '#.Utils.penclose w
                      r,←type,'.removeItem("',w,'");'
                  :EndFor
              :Else
                  r,←type,'.removeItem("',w,'");'
              :EndIf
          :EndFor
        ∇

        ∇ r←type Get what
          :Access public shared
          r←''
          r,←'<input type="hidden" name="',w,'" value="',JSON
        ∇
    :endclass

    :class localStorage : StorageObject

        ∇ r←{what}Set value
          :If 0=⎕NC'what'
              r←⎕BASE.Set('localStorage'value)
          :Else
              r←what ⎕BASE.Set('localStorage'value)
          :EndIf
        ∇

        ∇ r←Remove what
          :Access public shared
          r←
        ∇

        ∇ r←{name}Get what
          :Access public shared
        ∇

    :EndClass

    :Class sessionStorage
        ∇ r←{what}Set value
          :Access public shared
        ∇
    :endclass

    :class sessionStorage
    :endclass

:EndNamespace

:Namespace _JSS ⍝ Javascript Snippets

    (⎕ML ⎕IO)←1
⍝ this list will grow over time as usage patterns are discovered
    eis←{(,∘⊂)⍣((326∊⎕DR ⍵)<2>|≡⍵),⍵} ⍝ Enclose if simple
    enlist←{⎕ML←1 ⋄ ∊⍵}
    quote←{0∊⍴⍵: '' ⋄ '"',(('"' ⎕R '\\\0')⍕⍵),'"'}
    ine←{0∊⍴⍺:'' ⋄ ⍵} ⍝ if not empty
    fmtSelector←{{'this'≡⍵:⍵ ⋄quote ⍵}¯2↓enlist{⍵,', '}¨eis ⍵}
    fmtData←#.JSON.fromAPL ⍝ {{(326=⍵)<0=2|⍵}⎕DR ⍵:quote ⍵ ⋄ ⍕⍵}
    JAchars←#.JSON.JAchars

    ∇ r←Alert txt
    ⍝ popup alert text
      r←'alert(',(quote txt),');'
    ∇

    ∇ r←CloseWindow
    ⍝ close the browser window
      r←'var w=window.open(window.location,''_self''); w.close();'
    ∇

    ∇ r←sel Css args ⍝ JQuery css cover
      r←(sel JQuery'css')args
    ∇

    ∇ r←sel Val args ⍝ JQuery val cover
      r←(sel JQuery'val')args
    ∇

    ∇ r←sel Prop args ⍝ JQuery prop cover
      r←(sel JQuery'prop')args
    ∇

    ∇ r←sel Attr args ⍝ JQuery attr cover
      r←(sel JQuery'attr')args
    ∇

    ∇ r←sel RemoveAttr args
      r←(sel JQuery'removeAttr')args
    ∇

    ∇ r←sel Html args ⍝ JQuery html cover
      r←(sel JQuery'html')args
    ∇

    ∇ r←sel Show args
      r←(sel JQuery'show')args
    ∇

    ∇ r←sel Hide args
      r←(sel JQuery'hide')args
    ∇

    ∇ r←sel Toggle args
      r←(sel JQuery'toggle')args
    ∇

    ∇ r←sel Trigger args
      r←(sel JQuery'trigger')args
    ∇

    ∇ r←Submit sel
      r←(sel JQuery'submit')''
    ∇

    ∇ r←sel Position args;Position;inds;parameters;q;mask
      parameters←'my' 'at' 'of' 'collision' 'within'
      q←{1⌽'''''',{⍵/⍨1+''''=⍵}⍕⍵}
      Position←⎕NS ⍬
      :If 2=⍴⍴args ⍝ matrix
          args←,args
      :ElseIf 3=≡args
          args←⊃,/args
      :EndIf
      args←eis args
      inds←parameters⍳args
      :If ∨/mask←inds≤⍴parameters
          :If mask≡(2×+/mask)⍴1 0
              parameters←mask/args
              args←(1⌽mask)/args
          :EndIf
      :Else
          parameters←(⍴args)↑parameters
      :EndIf
      mask←⍬∘≢¨args
      args←mask/args
      parameters←mask/parameters
      parameters({⍎'Position.',⍺,'←',q ⍵})¨args
      :If ~0∊⍴Position.⎕NL-2
          r←0 #.JQ.JQueryfn'position'sel Position
      :EndIf
    ∇

    ∇ r←{eval}JSDate date
    ⍝ snippet to create a JS date (JavaScript months are 0-11!!!)
    ⍝ date is in 6↑⎕TS form
    ⍝ eval is optional Boolean to indicate whether to prepend '⍎' to the snippet (default is 0=do not prepend)
    ⍝         prepending ⍎ tells MiServer that this is an executable phrase and not simply a string

      :If 0=⎕NC'eval' ⋄ eval←0 ⋄ :EndIf
      :If 0 2∊⍨10|⎕DR date  ⍝ char?
          date←'"',date,'"'
      :Else
          date←1↓∊','∘,∘⍕¨0 ¯1 0 0 0 0+6↑date
      :EndIf
      r←(~eval)↓'⍎new Date(',date,')'
    ∇

    ∇ r←{varname}JSData data
    ⍝ return var definition to build a JavaScript object based on data
    ⍝ varname is the name of the variable
    ⍝ data is either a matrix of [1;] column names, (1↓) data
    ⍝      or a vector of namespaces
      :If 0=⎕NC'varname' ⋄ varname←'' ⋄ :EndIf
      :If 2=⍴⍴data
          data←#.JSON.fromTable data
      :EndIf
      r←(((~0∊⍴varname)/'var ',varname,' = '),0 0 1 #.JSON.fromAPL data),';'
    ∇

    ∇ r←{val}(sel JQuery fn)args;opt
    ⍝ construct JavaScript to call a jQuery function - eg val(), html(), css(), prop(), or attr()
    ⍝ optionally setting a value for
    ⍝ Get a jQuery parameter:
    ⍝    ('"#id"' JQuery 'attr') '"data-role"'
    ⍝ Set a jQuery parameter:
    ⍝    '"blue"' ('#id' JQuery 'css') 'background-color'
    ⍝
      args←eis args
      :If 0=⎕NC'val'
          (opt val)←2↑args,(⍴args)↓''⎕NULL
      :Else
          opt←⊃args
      :EndIf
      :If val≡⎕NULL
          r←'$(',(fmtSelector sel),').',fn,'(',(quote opt),');'
      :Else
          r←'$(',(fmtSelector sel),').',fn,'(',(quote opt),',',(fmtData val),');'
      :EndIf
    ∇

    ∇ r←{val}(fn JQueryOpt sel)opt
      :If 0=⎕NC'val'
          r←'$(',(fmtSelector sel),').',fn,'("option",',(quote opt),');'
      :Else
          r←'$(',(fmtSelector sel),').',fn,'("option",',(quote opt),',',(fmtData val),');'
      :EndIf
    ∇

    :Class StorageObject
    ⍝ !!!Prototype!!!

        ∇ r←{what}Set(type value);name;w;v
          :Access public shared
        ⍝ value may be
          :Access public shared
          r←''
          :If 9.1=⎕NC⊂'value' ⍝ namespace?
              :For name :In value.⎕NL-2
                  r,←formatSet(type name(value⍎name))
              :EndFor
          :Else
              value←eis value
              :If 2=⎕NC'what' ⍝ value is list of name value pairs
                  what←eis what
              :Else
                  (what value)←↓[1]((⌊0.5×⍴value),2)⍴value
              :EndIf
              :For (w v) :InEach (what value)
                  r,←formatSet(type w v)
              :EndFor
          :EndIf
        ∇

        ∇ r←formatSet(type what value)
          :Access public shared
          r←type,'.setItem("',what,'",JSON.stringify(',(#.JSON.fromAPL value),');'
        ∇

        ∇ r←type Remove what;w;ww
          :Access public shared
          what←eis what
          r←''
          :For w :In what
              :If ' '∊w←#.Strings.deb w
                  :For ww :In ' '#.Utils.penclose w
                      r,←type,'.removeItem("',w,'");'
                  :EndFor
              :Else
                  r,←type,'.removeItem("',w,'");'
              :EndIf
          :EndFor
        ∇

        ∇ r←type Get what
          :Access public shared
          r←''
          r,←'<input type="hidden" name="',w,'" value="',JSON
        ∇
    :endclass

    :class localStorage : StorageObject

        ∇ r←{what}Set value
          :If 0=⎕NC'what'
              r←⎕BASE.Set('localStorage'value)
          :Else
              r←what ⎕BASE.Set('localStorage'value)
          :EndIf
        ∇

        ∇ r←Remove what
          :Access public shared
          r←
        ∇

        ∇ r←{name}Get what
          :Access public shared
        ∇

    :EndClass

    :Class sessionStorage
        ∇ r←{what}Set value
          :Access public shared
        ∇
    :endclass

    :class sessionStorage
    :endclass

:EndNamespace

use JSON.fromAPL to format data

use JSON.fromAPL to format data


:Namespace Dates

    (⎕ML ⎕IO)←1

    ∇ r←CookieFmt date;day;date;mon
         ⍝ Format date for cookie expiry
         ⍝ "expires Mon, 09-Dec-2002 13:46:00 GMT"

      day←(7 3⍴'MonTueWedThuFriSatSun')[WeekDay date;]
      mon←(12 3⍴'janfebmaraprmayjunjulaugsepoctnovdec')[2⊃date;]
      r←,('<',day,', >,ZI2,<-',mon,'->,ZI4,< >,ZI2,<:>,ZI2,<:>,ZI2,< GMT>')⎕FMT 1 5⍴(6↑date)[3 1 4 5 6]
    ∇

    ∇ SetUtcOffset;ver;⎕USING
      :If 0=⎕NC'_UtcOffset'
          :If 'Win'≡ver←3↑1⊃'.'⎕WG'APLVersion'
              :Hold '_UtcOffset'
                  ⎕USING←,⊂''
                  _UtcOffset←¯60×(System.TimeZone.CurrentTimeZone.GetUtcOffset ⎕NEW System.DateTime(⎕TS)).Hours
              :EndHold
          :Else
              :If (⊂ver)∊'Lin' 'AIX' 'Sol' 'Mac'
                  _UtcOffset←¯60×⍎¯2↓{⍵↑⍨-' '⍳⍨⌽⍵}⊃⎕SH'date -R'
              :Else
                  _UtcOffset←0 ⍝ otherwise, assume GMT
              :EndIf
          :EndIf
      :EndIf
    ∇

    ∇ r←{minOffset}HTTPDate ts;sign;day;mon;ver;⎕USING;t
    ⍝ return RCF 1123/822 compliant date
    ⍝ minOffset is option number of minutes to offset time with (used for HTTP caching expirations)
      minOffset←{0::0 ⋄ minOffset}''
      ts←6↑ts
      SetUtcOffset
      ts←IDNToDate((_UtcOffset+minOffset)÷24×60)+t←DateToIDN ts
      day←(7 3⍴'MonTueWedThuFriSatSun')[1+7|(⌊t)-1;]
      mon←(12 3⍴'JanFebMarAprMayJunJulAugSepOctNovDec')[2⊃ts;]
      r←,('<',day,', >,ZI2,< ',mon,' >,ZI4,< >,ZI2,<:>,ZI2,<:>,ZI2,< GMT>')⎕FMT 1 5⍴ts[3 1 4 5 6]
    ∇

    ∇ SM←DateToIDN TS                  ⍝ Convert date format
      SM←2 ⎕NQ'.' 'DateToIDN'(3↑TS)
      :If 3<⍴TS
          SM+←(24 60 60 1000⊥4↑3↓TS)÷86400000
      :EndIf
    ∇

    ∇ TS←IDNToDate SM                ⍝ Convert IDN to date format : 3↑⎕TS ← IDN (akd TS_SM)
      TS←3↑2 ⎕NQ'.' 'IDNToDate'(⌊SM)
      TS,←⌊0.5+24 60 60 1000⊤86400000×1|SM
    ∇

    ∇ new←ts IdnAdd t
    ⍝ T is D HH MM SS
      new←ts+(0 24 60 60 1000⊥¯5↑t,0)÷86400000
    ∇

    ∇ r←Now
      r←DateToIDN ⎕TS
    ∇

    ∇ r←TSFmt ts
      r←,'ZI4,<->,ZI2,<->,ZI2,< >,ZI2,<:>,ZI2,<:>,ZI2'⎕FMT 1 6⍴6↑ts
    ∇

    ∇ r←TSFmtNice ts;now;yday;today;z;i;m;idn;s
    ⍝ Format a vector of IDN's nicely
      s←⍴ts
      yday←¯1+today←⌊now←DateToIDN ⎕TS
      r←↑IDNToDate¨,ts←,ts ⍝ Make matrix
      r←'ZI4,<->,ZI2,<->,ZI2,< >,ZI2,<:>,ZI2'⎕FMT 5↑[2]r
      :If 0≠⍴i←((ts≥yday)∧~m←ts≥today)/⍳⍴ts
          r[i;]←r[i;11+⍳5],' ',((⍴i),10)⍴10↑'Yesterday'
      :EndIf
      :If 0≠⍴i←m/⍳⍴ts
          r[i;]←r[i;11+⍳5],' ',((⍴i),10)⍴10↑'Today'
      :EndIf
      :If 0≠⍴i←(100>z←⌊(now-ts)×24×60)/⍳⍴ts
          r[i;]←16↑[2]↑(⍕¨⌊z[i]),¨(' minutes ago' ' minute ago')[1+1=z[i]]
      :EndIf
      :If 0≠⍴i←(z<1)/⍳⍴ts
          r[i;]←((⍴i),16)⍴16↑'Now'
      :EndIf
      r←(s,¯1↑⍴r)⍴r
    ∇

    ∇ r←WeekDay Date
    ⍝ Return weekday (Monday=1, Sunday=7)
      r←1+7|(DateToIDN 3↑Date)-1
    ∇

    ∇ r←{opt}DateFormat ymd
    ⍝ opt - 0 dd MMM yyyy
    ⍝       1 dd Month yyyy
      :If 0=⎕NC'opt' ⋄ opt←0 ⋄ :EndIf
      :Select ⊃opt
      :Case 0
          r←(⍕ymd[3]),(1⌽'  ',3↑(3×¯1+ymd[2])↓'JANFEBMARAPRMAYJUNJULAUGSEPOCTNOVDEC'),⍕ymd[1]
      :Case 1
          r←(⍕ymd[3]),(1⌽'  ',ymd[2]⊃'January' 'February' 'March' 'April' 'May' 'June' 'July' 'August' 'September' 'October' 'November' 'December'),⍕ymd[1]
      :Else
          r←⍕ymd
      :EndSelect
    ∇

    ∇ r←LogFmtNow;ver
    ⍝ returns now UTC adjusted, and formatted for log files (Common Log Format)
      :If 'Win'≡ver←3↑1⊃'.'⎕WG'APLVersion'
          r←((⍎⎕NA'kernel32|GetSystemTime >{U2 U2 U2 U2 U2 U2 U2 U2}')⊂8⍴0)[4 2 1 5 6 7]
          r[2]←⊂(12 3⍴'JanFebMarAprMayJunJulAugSepOctNovDec')[,2⊃r;]
          r←,'< [>,ZI2,</>,3A1,</>,ZI4,<:>,ZI2,<:>,ZI2,<:>,ZI2,< +0000] >'⎕FMT r
      :Else
          r←1⊃⎕SH'date +" [%d/%b/%Y:%T %z] "'
      :EndIf
    ∇

    ∇ r←LogFmt ymdhms
      r←ymdhms[3 2 1 4 5 6]
      r[2]←⊂(12 3⍴'JanFebMarAprMayJunJulAugSepOctNovDec')[,2⊃r;]
      r←,'< [>,ZI2,</>,3A1,</>,ZI4,<:>,ZI2,<:>,ZI2,<:>,ZI2,< +0000] >'⎕FMT r
    ∇

    ∇ ts←FTT fts;md;shape;tmp;yr;z;⎕IO
     ⍝ *** convert ⎕FRDCI/⎕FHIST timestamp(s) to ⎕ts-format for Dyalog/W ***
     ⍝ right argument: ⎕FRDCI-type timestamp(s) of any shape
     ⍝ result: ⎕ts-type timestamp(s) with shape <(⍴argument),7>
      ⎕IO←0 ⋄ shape←⍴fts ⋄ ts←,fts+18626112000
      md←365.2501|1+1461|yr←⌊ts÷5184000
      tmp←31 61 92 122 153 184 214 245 275 306 337 366
      z←(,⍉<⍀tmp∘.≥md)/,((⍴md),12)⍴⍳12
      md←(1+12|z+2),[0.1]⌈md-(0,tmp)[z]
      ts←(1960+⌊(yr+60)÷365.25),md,⍉24 60 60 60⊤ts
      ts[;6]←⌊0.5+ts[;6]×100÷6
      ts←(shape,7)⍴ts
    ∇

    ∇ r←TTF ts;l;h;d;m;y
       ⍝ convert a ⎕TS style timestamp into 60ths of a second since 1st January 1970 a la ⎕FRDCI
      l←⌈60×ts[7]÷1000                                         ⍝ convert milliseconds to 60ths
      h←3600 60 60⊥ts[4 5 6]                                   ⍝ hours minutes seconds to to the nearest seconds total
      d←ts[3]-1                                                ⍝ days since start of month
      m←(2⊃ts)⊃++\0 31,(28+0=4|⊃ts),31 30 31 30 31 31 30 31 30 ⍝ days in completed months
      y←{{(365×⍵)+⌈4÷⍨⍵-2}⍵-1970}⊃ts                           ⍝ days in years since 1970, leap years since 1972
      r←l+60×h+86400×y+m+d                                     ⍝ sum, convert to seconds, add seconds in the day convert to 60ths, add on 60ths
    ∇

    tonum←{(b v)←⎕vfi ⍵ ⋄ b/v}

    ∇ dt←ParseDate str;pos;mon;t;ymd
     ⍝ str is of the genre "Wed Aug 05 2015 07:30:21 GMT-0400 (Eastern Daylight Time)"
     ⍝ We need to weed out the day of the week and the time
      str←(+/∧\' '=str)↓str           ⍝ remove the leading spaces
     ⍝ What kind of string is this?
      :If ~∧/1⊃(dt dt)←{b←~⍵∊'/-:' ⋄ ⎕VFI b\b/⍵}str  ⍝ yyyy/mm/dd hh:mm:ss ?
          :If 0∊⍴t←'Jan' 'Feb' 'Mar' 'Apr' 'May' 'Jun' 'Jul' 'Aug' 'Sep' 'Oct' 'Nov' 'Dec'⎕S 0 3⊢str      ⍝ look for the month as a string. If not found
              ymd←3↑tonum str                ⍝ grab the 1st 3 numbers found
              ymd←ymd[⍒(2×31<ymd)+ymd<12] ⍝ put in correct order
          :Else                           ⍝ otherwise (if found)
              (pos mon)←0 1+1⊃t
              :If ~0∊⍴t←tonum pos↑str        ⍝ any number before the month? (e.g. 2 May 2021)
                  ymd←⌽⍣(31<⍬⍴t)⊢(1↑tonum pos↓str),mon,t
              :Else
                  ymd←¯1⌽mon,2↑tonum pos↓str
              :EndIf
          :EndIf
     ⍝ Now grab the time
          dt←ymd,tonum⍕'(\d+):(\d+):(\d+)'⎕S'\1 \2 \3'⊢str
      :EndIf
    ∇

    ∇ ts←ParseISODate str
      SetUtcOffset
      ts←IDNToDate(-_UtcOffset÷24×60)+DateToIDN 7↑str{⊃(//)⎕VFI ⍵\⍵/⍺}str∊⎕D
    ∇

    ∇ r←{unit}ParseTime string;chunks;units;factors
     ⍝ Parse time string into units (default is ms)
     ⍝ String is a string like '5d4h47m6s266ms'
      :If 0=⎕NC'unit' ⋄ unit←'ms' ⋄ :EndIf
      units←(1 1 1 1 1 0⊂'dhmsms'),⊂''
      factors←86400000 3600000 60000 1000 1 1
      chunks←{⎕ML←3 ⋄ (1+⍵∊⎕D)⊂⍵}string
      :Trap 0/0
          r←(⍎¨{⍵∩⎕D}¨chunks)+.×factors[units⍳{#.Strings.lc ⍵~⎕D}¨chunks]
      :Else
          'Invalid time string'⎕SIGNAL 11
      :EndTrap
      r←r÷factors[units⍳⊂,unit]
    ∇

:EndNamespace

:Namespace Dates

    (⎕ML ⎕IO)←1

    ∇ r←CookieFmt date;day;date;mon
         ⍝ Format date for cookie expiry
         ⍝ "expires Mon, 09-Dec-2002 13:46:00 GMT"

      day←(7 3⍴'MonTueWedThuFriSatSun')[WeekDay date;]
      mon←(12 3⍴'janfebmaraprmayjunjulaugsepoctnovdec')[2⊃date;]
      r←,('<',day,', >,ZI2,<-',mon,'->,ZI4,< >,ZI2,<:>,ZI2,<:>,ZI2,< GMT>')⎕FMT 1 5⍴(6↑date)[3 1 4 5 6]
    ∇

    ∇ SetUtcOffset;ver;⎕USING
      :If 0=⎕NC'_UtcOffset'
          :If 'Win'≡ver←3↑1⊃'.'⎕WG'APLVersion'
              :Hold '_UtcOffset'
                  ⎕USING←,⊂''
                  _UtcOffset←¯60×(System.TimeZone.CurrentTimeZone.GetUtcOffset ⎕NEW System.DateTime(⎕TS)).Hours
              :EndHold
          :Else
              :If (⊂ver)∊'Lin' 'AIX' 'Sol' 'Mac'
                  _UtcOffset←¯60×.01×⍎⊃⎕SH'date +%z'
              :Else
                  _UtcOffset←0 ⍝ otherwise, assume GMT
              :EndIf
          :EndIf
      :EndIf
    ∇

    ∇ r←{minOffset}HTTPDate ts;sign;day;mon;ver;⎕USING;t
    ⍝ return RCF 1123/822 compliant date
    ⍝ minOffset is option number of minutes to offset time with (used for HTTP caching expirations)
      minOffset←{0::0 ⋄ minOffset}''
      ts←6↑ts
      SetUtcOffset
      ts←IDNToDate((_UtcOffset+minOffset)÷24×60)+t←DateToIDN ts
      day←(7 3⍴'MonTueWedThuFriSatSun')[1+7|(⌊t)-1;]
      mon←(12 3⍴'JanFebMarAprMayJunJulAugSepOctNovDec')[2⊃ts;]
      r←,('<',day,', >,ZI2,< ',mon,' >,ZI4,< >,ZI2,<:>,ZI2,<:>,ZI2,< GMT>')⎕FMT 1 5⍴ts[3 1 4 5 6]
    ∇

    ∇ SM←DateToIDN TS                  ⍝ Convert date format
      SM←2 ⎕NQ'.' 'DateToIDN'(3↑TS)
      :If 3<⍴TS
          SM+←(24 60 60 1000⊥4↑3↓TS)÷86400000
      :EndIf
    ∇

    ∇ TS←IDNToDate SM                ⍝ Convert IDN to date format : 3↑⎕TS ← IDN (akd TS_SM)
      TS←3↑2 ⎕NQ'.' 'IDNToDate'(⌊SM)
      TS,←⌊0.5+24 60 60 1000⊤86400000×1|SM
    ∇

    ∇ new←ts IdnAdd t
    ⍝ T is D HH MM SS
      new←ts+(0 24 60 60 1000⊥¯5↑t,0)÷86400000
    ∇

    ∇ r←Now
      r←DateToIDN ⎕TS
    ∇

    ∇ r←TSFmt ts
      r←,'ZI4,<->,ZI2,<->,ZI2,< >,ZI2,<:>,ZI2,<:>,ZI2'⎕FMT 1 6⍴6↑ts
    ∇

    ∇ r←TSFmtNice ts;now;yday;today;z;i;m;idn;s
    ⍝ Format a vector of IDN's nicely
      s←⍴ts
      yday←¯1+today←⌊now←DateToIDN ⎕TS
      r←↑IDNToDate¨,ts←,ts ⍝ Make matrix
      r←'ZI4,<->,ZI2,<->,ZI2,< >,ZI2,<:>,ZI2'⎕FMT 5↑[2]r
      :If 0≠⍴i←((ts≥yday)∧~m←ts≥today)/⍳⍴ts
          r[i;]←r[i;11+⍳5],' ',((⍴i),10)⍴10↑'Yesterday'
      :EndIf
      :If 0≠⍴i←m/⍳⍴ts
          r[i;]←r[i;11+⍳5],' ',((⍴i),10)⍴10↑'Today'
      :EndIf
      :If 0≠⍴i←(100>z←⌊(now-ts)×24×60)/⍳⍴ts
          r[i;]←16↑[2]↑(⍕¨⌊z[i]),¨(' minutes ago' ' minute ago')[1+1=z[i]]
      :EndIf
      :If 0≠⍴i←(z<1)/⍳⍴ts
          r[i;]←((⍴i),16)⍴16↑'Now'
      :EndIf
      r←(s,¯1↑⍴r)⍴r
    ∇

    ∇ r←WeekDay Date
    ⍝ Return weekday (Monday=1, Sunday=7)
      r←1+7|(DateToIDN 3↑Date)-1
    ∇

    ∇ r←{opt}DateFormat ymd
    ⍝ opt - 0 dd MMM yyyy
    ⍝       1 dd Month yyyy
      :If 0=⎕NC'opt' ⋄ opt←0 ⋄ :EndIf
      :Select ⊃opt
      :Case 0
          r←(⍕ymd[3]),(1⌽'  ',3↑(3×¯1+ymd[2])↓'JANFEBMARAPRMAYJUNJULAUGSEPOCTNOVDEC'),⍕ymd[1]
      :Case 1
          r←(⍕ymd[3]),(1⌽'  ',ymd[2]⊃'January' 'February' 'March' 'April' 'May' 'June' 'July' 'August' 'September' 'October' 'November' 'December'),⍕ymd[1]
      :Else
          r←⍕ymd
      :EndSelect
    ∇

    ∇ r←LogFmtNow;ver
    ⍝ returns now UTC adjusted, and formatted for log files (Common Log Format)
      :If 'Win'≡ver←3↑1⊃'.'⎕WG'APLVersion'
          r←((⍎⎕NA'kernel32|GetSystemTime >{U2 U2 U2 U2 U2 U2 U2 U2}')⊂8⍴0)[4 2 1 5 6 7]
          r[2]←⊂(12 3⍴'JanFebMarAprMayJunJulAugSepOctNovDec')[,2⊃r;]
          r←,'< [>,ZI2,</>,3A1,</>,ZI4,<:>,ZI2,<:>,ZI2,<:>,ZI2,< +0000] >'⎕FMT r
      :Else
          r←1⊃⎕SH'date +" [%d/%b/%Y:%T %z] "'
      :EndIf
    ∇

    ∇ r←LogFmt ymdhms
      r←ymdhms[3 2 1 4 5 6]
      r[2]←⊂(12 3⍴'JanFebMarAprMayJunJulAugSepOctNovDec')[,2⊃r;]
      r←,'< [>,ZI2,</>,3A1,</>,ZI4,<:>,ZI2,<:>,ZI2,<:>,ZI2,< +0000] >'⎕FMT r
    ∇

    ∇ ts←FTT fts;md;shape;tmp;yr;z;⎕IO
     ⍝ *** convert ⎕FRDCI/⎕FHIST timestamp(s) to ⎕ts-format for Dyalog/W ***
     ⍝ right argument: ⎕FRDCI-type timestamp(s) of any shape
     ⍝ result: ⎕ts-type timestamp(s) with shape <(⍴argument),7>
      ⎕IO←0 ⋄ shape←⍴fts ⋄ ts←,fts+18626112000
      md←365.2501|1+1461|yr←⌊ts÷5184000
      tmp←31 61 92 122 153 184 214 245 275 306 337 366
      z←(,⍉<⍀tmp∘.≥md)/,((⍴md),12)⍴⍳12
      md←(1+12|z+2),[0.1]⌈md-(0,tmp)[z]
      ts←(1960+⌊(yr+60)÷365.25),md,⍉24 60 60 60⊤ts
      ts[;6]←⌊0.5+ts[;6]×100÷6
      ts←(shape,7)⍴ts
    ∇

    ∇ r←TTF ts;l;h;d;m;y
       ⍝ convert a ⎕TS style timestamp into 60ths of a second since 1st January 1970 a la ⎕FRDCI
      l←⌈60×ts[7]÷1000                                         ⍝ convert milliseconds to 60ths
      h←3600 60 60⊥ts[4 5 6]                                   ⍝ hours minutes seconds to to the nearest seconds total
      d←ts[3]-1                                                ⍝ days since start of month
      m←(2⊃ts)⊃++\0 31,(28+0=4|⊃ts),31 30 31 30 31 31 30 31 30 ⍝ days in completed months
      y←{{(365×⍵)+⌈4÷⍨⍵-2}⍵-1970}⊃ts                           ⍝ days in years since 1970, leap years since 1972
      r←l+60×h+86400×y+m+d                                     ⍝ sum, convert to seconds, add seconds in the day convert to 60ths, add on 60ths
    ∇

    tonum←{(b v)←⎕vfi ⍵ ⋄ b/v}

    ∇ dt←ParseDate str;pos;mon;t;ymd
     ⍝ str is of the genre "Wed Aug 05 2015 07:30:21 GMT-0400 (Eastern Daylight Time)"
     ⍝ We need to weed out the day of the week and the time
      str←(+/∧\' '=str)↓str           ⍝ remove the leading spaces
     ⍝ What kind of string is this?
      :If ~∧/1⊃(dt dt)←{b←~⍵∊'/-:' ⋄ ⎕VFI b\b/⍵}str  ⍝ yyyy/mm/dd hh:mm:ss ?
          :If 0∊⍴t←'Jan' 'Feb' 'Mar' 'Apr' 'May' 'Jun' 'Jul' 'Aug' 'Sep' 'Oct' 'Nov' 'Dec'⎕S 0 3⊢str      ⍝ look for the month as a string. If not found
              ymd←3↑tonum str                ⍝ grab the 1st 3 numbers found
              ymd←ymd[⍒(2×31<ymd)+ymd<12] ⍝ put in correct order
          :Else                           ⍝ otherwise (if found)
              (pos mon)←0 1+1⊃t
              :If ~0∊⍴t←tonum pos↑str        ⍝ any number before the month? (e.g. 2 May 2021)
                  ymd←⌽⍣(31<⍬⍴t)⊢(1↑tonum pos↓str),mon,t
              :Else
                  ymd←¯1⌽mon,2↑tonum pos↓str
              :EndIf
          :EndIf
     ⍝ Now grab the time
          dt←ymd,tonum⍕'(\d+):(\d+):(\d+)'⎕S'\1 \2 \3'⊢str
      :EndIf
    ∇

    ∇ ts←ParseISODate str
      SetUtcOffset
      ts←IDNToDate(-_UtcOffset÷24×60)+DateToIDN 7↑str{⊃(//)⎕VFI ⍵\⍵/⍺}str∊⎕D
    ∇

    ∇ r←{unit}ParseTime string;chunks;units;factors
     ⍝ Parse time string into units (default is ms)
     ⍝ String is a string like '5d4h47m6s266ms'
      :If 0=⎕NC'unit' ⋄ unit←'ms' ⋄ :EndIf
      units←(1 1 1 1 1 0⊂'dhmsms'),⊂''
      factors←86400000 3600000 60000 1000 1 1
      chunks←{⎕ML←3 ⋄ (1+⍵∊⎕D)⊂⍵}string
      :Trap 0/0
          r←(⍎¨{⍵∩⎕D}¨chunks)+.×factors[units⍳{#.Strings.lc ⍵~⎕D}¨chunks]
      :Else
          'Invalid time string'⎕SIGNAL 11
      :EndTrap
      r←r÷factors[units⍳⊂,unit]
    ∇

:EndNamespace

Fix to Dates.SetUTCOffset for Mac

Fix to Dates.SetUTCOffset for Mac


:Namespace HtmlUtils

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

:EndNamespace

:Namespace HtmlUtils

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

:EndNamespace

Add ∇to to APLToHTMLColor

Add ∇to to APLToHTMLColor


:Class mortgage : MiPageSample

    ⍝ define the fields that MiServer will keep up-to-date using form data
    :Field Public prin←'100000' ⍝ principal field
    :Field Public rate←'4.5'    ⍝ rate
    :Field Public term←'30'     ⍝ term (years)
    :Field Public pmt←''        ⍝ payment

    tonum←{{(,1)≡1⊃⍵:2⊃⍵ ⋄ ''}⎕VFI ⍵} ⍝ very simple check for a single number

  ⍝ ↓ calculate payment based on principal, rate, and term
    calcpmt←{0::'Error' ⋄ p r n←⍵÷1 1200 (÷12) ⋄ .01×⌈100×p×r÷1-(1+r)*-n}

  ⍝ ↓ calculate how much you can borrow based on rate, term, and payment
    calcprin←{0::'Error' ⋄ r n m←⍵÷1200 (÷12) 1 ⋄ .01×⌈100×m÷r÷1-(1+r)*-n}

    ∇ Render;f;inputs;labels;h ⍝ render the initial page
      :Access Public

      :If 0=⍴pmt ⋄ pmt←calcpmt tonum¨prin rate term ⋄ :EndIf ⍝ First calculation

      Add h2'Mortgage Calculator'
      Add'Modify principal, rate or term to recalculate payment.'
      Add¨br,(⊂'Change payment to recalculate the principal'),2⍴br

      (frm←Add Form).id←'mtg'
      (ig←frm.Add InputGrid).Border←0
      ig.Labels←'Principal' 'Interest Rate' 'Term (years)' 'Payment'
      ig.Inputs←EditField,¨{⊂⍵ (⍎⍵)}¨'prin' 'rate' 'term' 'pmt'

      h←Add _JQ.Handler                         ⍝ add an event handler
      h.Callback←'Calc'                         ⍝ specify the callback function to run
      h.Events←'change'                         ⍝ listen for the "change" event
      h.Selectors←'#mtg input'                  ⍝ on the input elements within the form with id "mtg"
      h.ClientData←'formdata' '#mtg' 'serialize'
      ⍝ ↑ Return serialized data for form #mtg as "formdata"
      ⍝ ↑ MiServer maps named data in serialized form data to public fields in the page object
    ∇

    ∇ resp←Calc;p;r;n;m    ⍝ callback function for the handler defined in Render
      :Access public
      p r n m←tonum¨prin rate term pmt ⍝ convert input fields to numbers
      resp←''

      :Select _what ⍝ what field changed?

      :CaseList 'prin' 'rate' 'term' ⍝ one of prin rate or term changed, calculate payment
          :If ~∨/{0∊⍴⍵}¨p r n ⍝ if we have values for all inputs...
          ⍝ ... calculate the payment and replace the value attribute in the input element
              resp←Execute'#pmt'_JSS.Val calcpmt p r n
          :EndIf

      :Case 'pmt' ⍝ payment changed, calculate principal
          :If ~∨/{0∊⍴⍵}¨r n m ⍝ if we have values for all inputs...
          ⍝ ... calculate the principal and replace the value attribute in the input element
              resp←Execute'#prin'_JSS.Val calcprin r n m
          :EndIf

      :EndSelect
     resp,←Execute 'alert("hello")'
    ∇

:EndClass

:Class mortgage : MiPageSample

    ⍝ define the fields that MiServer will keep up-to-date using form data
    :Field Public prin←'100000' ⍝ principal field
    :Field Public rate←'4.5'    ⍝ rate
    :Field Public term←'30'     ⍝ term (years)
    :Field Public pmt←''        ⍝ payment

    tonum←{{(,1)≡1⊃⍵:2⊃⍵ ⋄ ''}⎕VFI ⍵} ⍝ very simple check for a single number

  ⍝ ↓ calculate payment based on principal, rate, and term
    calcpmt←{0::'Error' ⋄ p r n←⍵÷1 1200 (÷12) ⋄ .01×⌈100×p×r÷1-(1+r)*-n}

  ⍝ ↓ calculate how much you can borrow based on rate, term, and payment
    calcprin←{0::'Error' ⋄ r n m←⍵÷1200 (÷12) 1 ⋄ .01×⌈100×m÷r÷1-(1+r)*-n}

    ∇ Render;f;inputs;labels;h ⍝ render the initial page
      :Access Public

      :If 0=⍴pmt ⋄ pmt←calcpmt tonum¨prin rate term ⋄ :EndIf ⍝ First calculation

      Add h2'Mortgage Calculator'
      Add'Modify principal, rate or term to recalculate payment.'
      Add¨br,(⊂'Change payment to recalculate the principal'),2⍴br

      (frm←Add Form).id←'mtg'
      (ig←frm.Add InputGrid).Border←0
      ig.Labels←'Principal' 'Interest Rate' 'Term (years)' 'Payment'
      ig.Inputs←EditField,¨{⊂⍵ (⍎⍵)}¨'prin' 'rate' 'term' 'pmt'

      h←Add _JQ.Handler                         ⍝ add an event handler
      h.Callback←'Calc'                         ⍝ specify the callback function to run
      h.Events←'change'                         ⍝ listen for the "change" event
      h.Selectors←'#mtg input'                  ⍝ on the input elements within the form with id "mtg"
      h.ClientData←'formdata' '#mtg' 'serialize'
      ⍝ ↑ Return serialized data for form #mtg as "formdata"
      ⍝ ↑ MiServer maps named data in serialized form data to public fields in the page object
    ∇

    ∇ resp←Calc;p;r;n;m    ⍝ callback function for the handler defined in Render
      :Access public
      p r n m←tonum¨prin rate term pmt ⍝ convert input fields to numbers
      resp←''

      :Select _what ⍝ what field changed?

      :CaseList 'prin' 'rate' 'term' ⍝ one of prin rate or term changed, calculate payment
          :If ~∨/{0∊⍴⍵}¨p r n ⍝ if we have values for all inputs...
          ⍝ ... calculate the payment and replace the value attribute in the input element
              resp←Execute'#pmt'_JSS.Val calcpmt p r n
          :EndIf

      :Case 'pmt' ⍝ payment changed, calculate principal
          :If ~∨/{0∊⍴⍵}¨r n m ⍝ if we have values for all inputs...
          ⍝ ... calculate the principal and replace the value attribute in the input element
              resp←Execute'#prin'_JSS.Val calcprin r n m
          :EndIf

      :EndSelect
    ∇

:EndClass

Remove test message from mortgage.dyalog


:Class jBox : #._JQ._jqWidget
⍝ Description:: jBox widget
⍝
⍝ Constructor:: [type [message [content]]]
⍝ type - one of 'Tooltip', 'Mouse', 'Modal', 'Confirm', 'Notice', 'Image'
⍝ message - the text that will appear in the popup
⍝ content - the content for the target element
⍝
⍝ Public Fields::
⍝ Type - one of 'Tooltip', 'Mouse', 'Modal', 'Confirm', 'Notice', 'Image'
⍝ Message - the text that will appear in the popup
⍝ Content - the content for the target element
⍝
⍝ Notes::
⍝ For more information see https://github.com/StephanWagner/jBox
⍝ Type is a public field in the base class

    :field public shared readonly DocBase←'http://stephanwagner.me/jBox/documentation'
    :field public shared readonly ApiLevel←3

    :field public shared readonly Types←'Tooltip' 'Mouse' 'Modal' 'Confirm' 'Notice' 'Image'
    :field public Content←''
    :field public Message←''

    ∇ make
      :Access public
      JQueryFn←Uses←'jBox'
      :Implements constructor
      Type←'Tooltip'
      ContainerTag←'span'
    ∇

    ∇ make1 args
      :Access public
      JQueryFn←Uses←'jBox'
      :Implements constructor
      args←eis args
      (Type Message Content)←args defaultArgs'Tooltip'Message Content
      ContainerTag←'span'
    ∇

    ∇ r←Render;ind
      :Access public
      ind←Types⍳⊂#.Strings.firstCap Type
      'Invalid jBox Type'⎕SIGNAL(ind>⍴Types)/11
      Type←ind⊃Types
      Container.Content←Content
      'content'Set renderIt Message
      BuildHTML←~0∊⍴Content
      r←⎕BASE.Render
    ∇
:EndClass

:Class jBox : #._JQ._jqWidget
⍝ Description:: jBox widget
⍝
⍝ Constructor:: [type [message [content]]]
⍝ type - one of 'Tooltip', 'Mouse', 'Modal', 'Confirm', 'Notice', 'Image'
⍝ message - the text that will appear in the popup
⍝ content - the content for the target element
⍝
⍝ Public Fields::
⍝ Type - one of 'Tooltip', 'Mouse', 'Modal', 'Confirm', 'Notice', 'Image'
⍝ Message - the text that will appear in the popup
⍝ Content - the content for the target element
⍝ Theme - name of a jBox-Theme (one of 'ModalBorder','NoticeBorder','TooltipBorder' or 'TooltipDark')
⍝
⍝ Notes::
⍝ For more information see https://github.com/StephanWagner/jBox
⍝ Type is a public field in the base class

    :field public shared readonly DocBase←'http://stephanwagner.me/jBox/documentation'
    :field public shared readonly ApiLevel←3

    :field public shared readonly Types←'Tooltip' 'Mouse' 'Modal' 'Confirm' 'Notice' 'Image'
    :field public Content←''
    :field public Message←''
    :field public Theme←''

    ∇ make
      :Access public
      JQueryFn←Uses←'jBox'
      :Implements constructor
      Type←'Tooltip'
      ContainerTag←'span'
    ∇

    ∇ make1 args
      :Access public
      JQueryFn←Uses←'jBox'
      :Implements constructor
      args←eis args
      (Type Message Content)←args defaultArgs'Tooltip'Message Content
      ContainerTag←'span'
    ∇

    ∇ r←Render;ind
      :Access public
      ind←Types⍳⊂#.Strings.firstCap Type
      'Invalid jBox Type'⎕SIGNAL(ind>⍴Types)/11
      Type←ind⊃Types
      Container.Content←Content
      'content'Set renderIt Message
      BuildHTML←~0∊⍴Content
      :If 0<⍴Theme
          'theme'Set Theme
          Use'⍕/jBox/themes/',Theme,'.css'
      :EndIf
      r←⎕BASE.Render
    ∇
:EndClass

Update jBox.dyalog

Update jBox.dyalog

Added public field Theme, reformatted file.


:class HtmlPage : #.HtmlElement

    ⍝∇:require =\HtmlElement.dyalog

    :field public Head
    :field public Body
    :field public Scripts
    :field public StylesLinks

    ∇ make
      :Access public
      :Implements constructor
      _init
      Tag←'html'
    ∇

    ∇ r←RenderBody;scr;mask
    ⍝ Called by MiPage because rendering can add scripts to the head element
      :Access public
      :If ~0∊⍴scr←∪Scripts
      :AndIf ∨/mask←{~0∊⍴⍵}¨scr.Content
          Body.Add¨mask/scr
      :EndIf
      r←Body.Render
    ∇

    ∇ r←RenderPage b;scr;mask;content
      :Access public
      content←Content
      :If ~0∊⍴scr←∪Scripts
      :AndIf ∨/mask←{0∊⍴⍵}¨scr.Content
          Head.Add¨mask/scr
      :EndIf
      :If ~0∊⍴StylesLinks
          Head.Add¨StylesLinks
      :EndIf
      Content←(Head.Render),b
      r←'<!DOCTYPE html>',∊⎕BASE.Render
      Content←content
    ∇

    ∇ r←Render;s;b;mask;scr;sty;content;headContent
      :Access public
      content←Content
      headContent←Head.Content
      :If ~0∊⍴scr←∪Scripts
      :AndIf ∨/mask←{~0∊⍴⍵}¨scr.Content
          Body.Add¨mask/scr
      :EndIf
      b←Body.Render
      :If ~0∊⍴scr←∪Scripts
      :AndIf ∨/mask←{0∊⍴⍵}¨scr.Content
          Head.Add¨mask/scr
      :EndIf
      :If ~0∊⍴sty←∪⌽StylesLinks
          Head.Add¨StylesLinks
      :EndIf
      Content←(Head.Render),b
      r←'<!DOCTYPE html>',∊⎕BASE.Render
      Content←content
      Head.Content←headContent
    ∇

    ∇ _init
      :Access public
      Body←⎕NEW #.HtmlElement'body'
      Head←⎕NEW #.HtmlElement'head'
      (Body Head)._PageRef←⎕THIS
      Scripts←''
      StylesLinks←''
      Handlers←''
      Content←Head,Body
    ∇

    ∇ {r}←{attr}Add what;c
      :Access public
      :If 0=⎕NC'attr' ⋄ attr←'' ⋄ :EndIf
      :If isClass⊃what
⍝          :If #._html.script∊c←∊⎕CLASS⊃what
⍝              r←Scripts,←{(⎕NEW(⊃⍵)((⊃⍣(2=⊃⍴⍵))1↓⍵))}what
          :If #._JQ.Handler∊c←∊⎕CLASS⊃what
              r←Body.Handlers,←{(⎕NEW(⊃⍵)((⊃⍣(2=⊃⍴⍵))1↓⍵))}what
              :If 0∊⍴r.Selector ⋄ r.Selector←'html' ⋄ :EndIf ⍝ if no selector specified, use page level
          :ElseIf ⊃∨/c∊¨⊂#._html.(style link)
              r←StylesLinks,←{(⎕NEW(⊃⍵)((⊃⍣(2=⊃⍴⍵))1↓⍵))}what
          :ElseIf ⊃∨/c∊¨⊂#._html.(title meta noscript base) ⍝ elements that belong exclusively or primarily in the <head> element
              r←attr Head.Add what
          :Else
              r←attr Body.Add what
          :EndIf
          r._PageRef←⎕THIS
      :Else
          r←attr Body.Add what
      :EndIf
    ∇

    ∇ {r}←{attr}Insert what;c
      :Access public
      :If 0=⎕NC'attr' ⋄ attr←'' ⋄ :EndIf
      :If isClass⊃what
          :If #._html.script∊c←∊⎕CLASS⊃what
              r←Scripts,⍨←{(⎕NEW(⊃⍵)((⊃⍣(2=⊃⍴⍵))1↓⍵))}what
          :ElseIf #._JQ.Handler∊c
              r←Body.Handlers,⍨←{(⎕NEW(⊃⍵)((⊃⍣(2=⊃⍴⍵))1↓⍵))}what
              :If 0∊⍴r.Selector ⋄ r.Selector←'html' ⋄ :EndIf ⍝ if no selector specified, use page level
          :ElseIf ⊃∨/c∊¨⊂#._html.(style link)
              r←StylesLinks,⍨←{(⎕NEW(⊃⍵)((⊃⍣(2=⊃⍴⍵))1↓⍵))}what
          :ElseIf ⊃∨/c∊¨⊂#._html.(title meta noscript base) ⍝ elements that belong exclusively or primarily in the <head> element
              r←attr Head.Insert what
          :Else
              r←attr Body.Insert what
          :EndIf
          r._PageRef←⎕THIS
      :Else
          r←attr Body.Insert what
      :EndIf
    ∇

    ∇ {r}←On args
      :Access public
      r←Body.On args
      r.Selector←'⍎document'
    ∇

    ∇ {r}←{attr}New what
      :Access public
      :If 0=⎕NC'attr' ⋄ attr←'' ⋄ :EndIf
      r←attr ⎕BASE.New what
      r._PageRef←⎕THIS
    ∇

    dtlb←{⍵{((∨\⍵)∧⌽∨\⌽⍵)/⍺}' '≠⍵}

    ∇ r←Style style
      :Access public
      r←{(⎕NEW #._html.link).Set(('href'⍵)('rel' 'stylesheet')('type' 'text/css'))}style
    ∇

:endclass

:class HtmlPage : #.HtmlElement

    ⍝∇:require =\HtmlElement.dyalog

    :field public Head
    :field public Body
    :field public Scripts
    :field public StylesLinks

    ∇ make
      :Access public
      :Implements constructor
      _init
      Tag←'html'
    ∇

    ∇ r←RenderBody;scr;mask
    ⍝ Called by MiPage because rendering can add scripts to the head element
      :Access public
      :If ~0∊⍴scr←∪Scripts
      :AndIf ∨/mask←{~0∊⍴⍵}¨scr.Content
          Body.Add¨mask/scr
      :EndIf
      r←Body.Render
    ∇

    ∇ r←RenderPage b;scr;mask;content
      :Access public
      content←Content
      :If ~0∊⍴scr←∪Scripts
      :AndIf ∨/mask←{0∊⍴⍵}¨scr.Content
          Head.Add¨mask/scr
      :EndIf
      :If ~0∊⍴StylesLinks
          Head.Add¨StylesLinks
      :EndIf
      Content←(Head.Render),b
      r←'<!DOCTYPE html>',∊⎕BASE.Render
      Content←content
    ∇

    ∇ r←Render;s;b;mask;scr;sty;content;headContent
      :Access public
      content←Content
      headContent←Head.Content
      :If ~0∊⍴scr←∪Scripts
      :AndIf ∨/mask←{~0∊⍴⍵}¨scr.Content
          Body.Add¨mask/scr
      :EndIf
      b←Body.Render
      :If ~0∊⍴scr←∪Scripts
      :AndIf ∨/mask←{0∊⍴⍵}¨scr.Content
          Head.Add¨mask/scr
      :EndIf
      :If ~0∊⍴sty←∪⌽StylesLinks
          Head.Add¨StylesLinks
      :EndIf
      Content←(Head.Render),b
      r←'<!DOCTYPE html>',∊⎕BASE.Render
      Content←content
      Head.Content←headContent
    ∇

    ∇ _init
      :Access public
      Body←⎕NEW #.HtmlElement'body'
      Head←⎕NEW #.HtmlElement'head'
      (Body Head)._PageRef←⎕THIS
      Scripts←''
      StylesLinks←''
      Handlers←''
      Content←Head,Body
    ∇

    ∇ {r}←{attr}Add what;c
      :Access public
      :If 0=⎕NC'attr' ⋄ attr←'' ⋄ :EndIf
      :If isClass⊃what
⍝          :If #._html.script∊c←∊⎕CLASS⊃what
⍝              r←Scripts,←{(⎕NEW(⊃⍵)((⊃⍣(2=⊃⍴⍵))1↓⍵))}what
          :If #._JQ.Handler∊c←∊⎕CLASS⊃what
              r←Body.Handlers,←{(⎕NEW(⊃⍵)((⊃⍣(2=⊃⍴⍵))1↓⍵))}what
              :If 0∊⍴r.Selector ⋄ r.Selector←'html' ⋄ :EndIf ⍝ if no selector specified, use page level
          :ElseIf ⊃∨/c∊¨⊂#._html.(style link)
              r←StylesLinks,←attr New what
          :ElseIf ⊃∨/c∊¨⊂#._html.(title meta noscript base) ⍝ elements that belong exclusively or primarily in the <head> element
              r←attr Head.Add what
          :Else
              r←attr Body.Add what
          :EndIf
          r._PageRef←⎕THIS
      :Else
          r←attr Body.Add what
      :EndIf
    ∇

    ∇ {r}←{attr}Insert what;c
      :Access public
      :If 0=⎕NC'attr' ⋄ attr←'' ⋄ :EndIf
      :If isClass⊃what
          :If #._html.script∊c←∊⎕CLASS⊃what
              r←Scripts,⍨←{(⎕NEW(⊃⍵)((⊃⍣(2=⊃⍴⍵))1↓⍵))}what
          :ElseIf #._JQ.Handler∊c
              r←Body.Handlers,⍨←{(⎕NEW(⊃⍵)((⊃⍣(2=⊃⍴⍵))1↓⍵))}what
              :If 0∊⍴r.Selector ⋄ r.Selector←'html' ⋄ :EndIf ⍝ if no selector specified, use page level
          :ElseIf ⊃∨/c∊¨⊂#._html.(style link)
              r←StylesLinks,⍨←{(⎕NEW(⊃⍵)((⊃⍣(2=⊃⍴⍵))1↓⍵))}what
          :ElseIf ⊃∨/c∊¨⊂#._html.(title meta noscript base) ⍝ elements that belong exclusively or primarily in the <head> element
              r←attr Head.Insert what
          :Else
              r←attr Body.Insert what
          :EndIf
          r._PageRef←⎕THIS
      :Else
          r←attr Body.Insert what
      :EndIf
    ∇

    ∇ {r}←On args
      :Access public
      r←Body.On args
      r.Selector←'⍎document'
    ∇

    ∇ {r}←{attr}New what
      :Access public
      :If 0=⎕NC'attr' ⋄ attr←'' ⋄ :EndIf
      r←attr ⎕BASE.New what
      r._PageRef←⎕THIS
    ∇

    dtlb←{⍵{((∨\⍵)∧⌽∨\⌽⍵)/⍺}' '≠⍵}

    ∇ r←Style style
      :Access public
      r←{(⎕NEW #._html.link).Set(('href'⍵)('rel' 'stylesheet')('type' 'text/css'))}style
    ∇

:endclass

Fix to make sure attributes render properly for style and link elements