Fix to GetVTV that caused files containing ⎕UCS 13 to be read wrong

:Namespace Files

⍝ Provides cover functions for many file operations
⍝ Eventually all will be implemented for both Windows and *nix

    (⎕IO ⎕ML)←1

    ∇ r←data Append name
     ⍝ Append data to existing file
      r←{(⎕NUNTIE ⍵)⊢data ⎕NAPPEND ⍵}Nopen name
    ∇

    ∇ r←text AppendText name;tn
     ⍝ Append text to existing file (must be single byte text)
      tn←Nopen name
      r←text ⎕NAPPEND tn(⎕DR' ')
      ⎕NUNTIE tn
    ∇

    ∇ (tn name)←{ext}CreateTemp folder;cnt
     ⍝ Create a temp file
     ⍝ folder - folder to create temp file in
     ⍝ ext - file extension
      :If 0=⎕NC'ext' ⋄ ext←'tmp' ⋄ :EndIf
      :If 0∊⍴ext ⋄ ext←'tmp' ⋄ :EndIf
      cnt←tn←0
      :Repeat
          name←folder,('/\'∊⍨¯1↑folder)↓'/',⎕A[?8⍴⍴⎕A],'.',ext
          :Trap 22 ⋄ tn←name ⎕NCREATE 0 ⋄ :EndTrap
          cnt+←1
      :Until (cnt>20)∨tn≠0
      'Unable to create temporary file'⎕SIGNAL tn↓22
    ∇

    ∇ {protect}Copy FmTo;CopyFileX;GetLastError ⍝ Copy file Fm -> To
      :Select APLVersion
      :Case '*nix'
          ∘
      :Case 'Win'
          :If 0=⎕NC'protect'     ⍝ Copy fails if <protect> and 'To' exists.
              protect←0          ⍝ Default unprotected copy.
          :EndIf
          'CopyFileX'⎕NA'I kernel32.C32∣CopyFile* <0T <0T I'
          :If 0=CopyFileX FmTo,protect
              ⎕NA'I4 kernel32.C32|GetLastError'
              11 ⎕SIGNAL⍨'CopyFile error:',⍕GetLastError
          :EndIf
      :EndSelect
    ∇

    ∇ Delete name;DeleteFileX;GetLastError;FindFirstFile;FindNextFile;FindClose;handle;rslt;ok;next;⎕IO;path
      ⎕IO←0
      :Select APLVersion
      :CaseList '*nix' 'Mac'
          _SH'rm ',unixfix name
      :Case 'Win'
          'DeleteFileX'⎕NA'I kernel32.C32∣DeleteFile* <0T'
          ⎕NA'I4 kernel32.C32|GetLastError'
          :If ∨/'*?'∊name ⍝ wildcards?
              path←{(⌽∨\⌽⍵∊'\/')/⍵}name
              _FindDefine
              handle rslt←_FindFirstFile name
              :If 0=handle
                  :Return ⍝ ('ntdir error:',⍕rslt)⎕SIGNAL 102      ⍝ file not found
              :EndIf
              :If '.'≠⊃6⊃rslt
                  {}DeleteFileX⊂path,6⊃rslt
              :EndIf
              :While 1=0⊃ok next←_FindNextFile handle
                  :If '.'≠⊃6⊃next
                      {}DeleteFileX⊂path,6⊃next
                  :EndIf
              :EndWhile
              :If 0 18∨.≠ok next
                  ('ntdir error:',⍕next)⎕SIGNAL 11   ⍝ DOMAIN
              :EndIf
              {}FindClose handle
          :Else
              :If 0=DeleteFileX⊂name
                  11 ⎕SIGNAL⍨'DeleteFile error:',⍕GetLastError
              :EndIf
          :EndIf
      :EndSelect
    ∇

    ∇ r←DirExists path
      r←0
      :Select APLVersion
      :CaseList '*nix' 'Mac'
          :Trap 11
              r←0<⍴_SH'ls -adl ',unixfix path
          :EndTrap
      :Case 'Win'
          r←0<⍬⍴⍴'.'List path
      :EndSelect
    ∇

    ∇ r←Exists name
     ⍝ Does file exist?
      r←1
      :Trap 22
          :Trap 19 ⍝ file access error means file exists
              ⎕NUNTIE(unixfix name)⎕NTIE 0
          :EndTrap
      :Else
          r←0
      :EndTrap
    ∇

    ∇ r←GetCurrentDirectory;GCD;GetLastError
     ⍝ Get Current Directory
      :Select APLVersion
      :CaseList '*nix' 'Mac'
          r←⊃_SH'pwd'
      :Case 'Win'
          'GCD'⎕NA'I kernel32.C32∣GetCurrentDirectory* I4 >0T'
          :If 0≠1⊃r←GCD 256 256
              r←2⊃r
          :Else
              ⎕NA'I4 kernel32.C32|GetLastError'
              11 ⎕SIGNAL⍨'GetCurrentDirectory error:',⍕GetLastError
          :EndIf
      :EndSelect
    ∇

    ∇ Chars←GetText name;nid;signature;nums;sz
     ⍝ Read ANSI or Unicode character file
      sz←⎕NSIZE nid←(unixfix name)⎕NTIE 0
      signature←⎕NREAD nid 83 3 0
      :If signature≡¯17 ¯69 ¯65 ⍝ UTF-8?
          nums←⎕NREAD nid 83 sz
          Chars←'UTF-8'⎕UCS 256|nums ⍝ Signed ints
      :ElseIf ∨/b←(2↑signature)∧.=2 2⍴¯1 ¯2 ¯2 ⍝ Unicode (UTF-16)
          Chars←{,⌽(2,⍨2÷⍨⍴⍵)⍴⍵}⍣(⊣/b)⎕NREAD nid 83 sz 2
          Chars←'UTF-16'⎕UCS(2*16)|163 ⎕DR Chars
      :Else ⍝ ANSI or UTF-8
          Chars←{11::⎕UCS ⍵ ⋄ 'UTF-8'⎕UCS ⍵}256|⎕NREAD nid 83 sz 0
      :EndIf
      ⎕NUNTIE nid
    ∇

    ∇ vtv←GetVTV name
     ⍝ Read ANSI or Unicode character file as vector of text vectors
      vtv←{1↓¨(v=n)⊂v←(n←⎕UCS 10),⍵}(GetText name)~⎕UCS 13
    ∇

    ∇ r←LikelyURL w
      →0↓⍨r←(⎕DR w)∊80 82
      r←{(0∊⍴⍵)<∧/⍵∊'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789%-._~:/?#[]@!$&''()*+,;='}w ⍝ identify likely URIs
    ∇

    ∇ r←{file}List path;z;rslt;handle;next;ok;attrs;⎕IO;FindFirstFileX;FindNextFileX;FindClose;FileTimeToLocalFileTime;FileTimeToSystemTime;GetLastError;isFile;filter
    ⍝ Return matrix containing
    ⍝ [;0] Name [;1] Length [;2] LastAccessTime [;3] IsDirectory
      ⎕IO←0
      :If isFile←2=⎕NC'file' ⋄ filter←(0<⍴,file)/'/',file ⋄ :Else ⋄ filter←'/*' ⋄ :EndIf
      filter←('/\'∊⍨¯1↑path)↓filter
      r←0 4⍴'' 0 0 0

      :Select APLVersion
      :Case '*nix'
          →(0∊⍴rslt←1 _SH'ls -al --time-style=full-iso ',unixfix path,isFile/filter)⍴0
          rslt←↑rslt
          rslt←' ',('total '≡6⍴rslt)↓[0]rslt
          →(0=1↑⍴r←((1↑⍴rslt),4)⍴0)⍴0
          z←∧⌿' '=rslt ⍝ entirely blank columns
          z←z∧10>+\z    ⍝ Do not split file names
          rslt←z⊂rslt
          r[;3]←'d'=(0⊃rslt)[;1]                 ⍝ IsDirectory
          r[;1]←(~r[;3])×1⊃⎕VFI,4⊃rslt ⍝ Size
          z←,(5⊃rslt),6⊃rslt ⋄ ((z∊'-:')/z)←' ' ⋄ z←((1↑⍴r),6)⍴1⊃⎕VFI z
          r[;2]←↓⌊z,1000×1|z[;5]                ⍝ Add msec to Timestamp
          r[;0]←{(⌽~∨\⌽⍵='/')/⍵}¨{(-+/∧\' '=⌽⍵)↓¨↓⍵}0 1↓8⊃rslt    ⍝ Name

      :Case 'Mac'
          →(0∊⍴rslt←1 _SH'stat -lt "%F %T %z" ',unixfix path,isFile/filter)⍴0
          r←((1↑⍴rslt),4)⍴0
          rslt←↑{⎕ML←3 ⋄ ⍵⊂⍨~{⍵∧9>+\⍵}' '=⍵}¨rslt
          r[;3]←'d'=0⊃¨rslt[;1]                 ⍝ IsDirectory
          r[;1]←(~r[;3])×1⊃¨⎕VFI¨rslt[;4]        ⍝ Size
          z←↑∊¨↓{w←⍵ ⋄ ((w∊'-:')/w)←' ' ⋄ 1⊃⎕VFI w}¨rslt[;5 6] ⍝
          r[;2]←↓z,0                            ⍝ 0 msec for MacOS to Timestamp
          r[;0]←rslt[;8]                        ⍝ Name

      :Case 'Win'
      ⍝ See DirX for explanations of results of _FindNextFile etc
          _FindDefine
          handle rslt←_FindFirstFile path,filter
          :If 0=handle
              :Return ⍝ ('ntdir error:',⍕rslt)⎕SIGNAL 102      ⍝ file not found
          :EndIf
          rslt←,⊂rslt
          :While 1=0⊃ok next←_FindNextFile handle
              rslt,←⊂next
          :EndWhile
          :If 0 18∨.≠ok next
              ('ntdir error:',⍕next)⎕SIGNAL 11   ⍝ DOMAIN
          :EndIf
          ok←FindClose handle
          rslt←↓[0]↑rslt
          →(0=1↑⍴r←((1↑⍴0⊃rslt),4)⍴0)⍴0
          (0⊃rslt)←⍉attrs←(32⍴2)⊤0⊃rslt                ⍝ Get attributes into bits
          r[;3]←(0⊃rslt)[;27]              ⍝ IsDirectory?
          r[;1]←0(2*32)⊥⍉↑4⊃rslt          ⍝ combine size elements
          r[;2]←_Filetime_to_TS¨3⊃rslt     ⍝ As ⎕TS vector
          r[;0]←6⊃rslt                     ⍝ Name
      :EndSelect
      r←r[⍋↑r[;0];]
    ∇

    ∇ MkDir path;CreateDirectory;GetLastError;err
      ⍝ Create a folder
      :Select APLVersion
      :CaseList '*nix' 'Mac'
          :If ~DirExists path
              1 _SH'mkdir ',unixfix path
              ('mkdir error on ',path)⎕SIGNAL 11/⍨~DirExists path
          :EndIf
      :Case 'Win'
          ⎕NA'I kernel32.C32∣CreateDirectory* <0T I4' ⍝ Try for best function
          →(0≠CreateDirectory path 0)⍴0 ⍝ 0 means "default security attributes"
          ⎕NA'I4 kernel32.C32|GetLastError'
          err ⎕SIGNAL⍨'CreateDirectory error:',⍕err←GetLastError
      :EndSelect
    ∇

    ∇ Move filenames;MoveFileX;MoveFileExA;GetLastError;err
    ⍝ Move (from to) - move/rename file
      :Select APLVersion
      :Case '*nix' 'Mac'
          ∘
      :Case 'Win'
          ⎕NA'I kernel32.C32∣MoveFileEx* <0T <0T I4' ⍝ Try for best function
          :If 0≠MoveFileExA filenames,3 ⍝ REPLACE_EXISTING (1) + COPY_ALLOWED (2)
              :Return
          :EndIf
          ⎕NA'I4 kernel32.C32|GetLastError'
          :Select err←GetLastError
          :Case 120                     ⍝ ERROR_CALL_NOT_IMPLIMENTED
              'MoveFileX'⎕NA'I Kernel32.C32∣MoveFile* <0T <0T' ⍝ accept 2nd best - win 95
              :If 0≠MoveFileX filenames
                  :Return
              :EndIf
              err←GetLastError
          :EndSelect
          11 ⎕SIGNAL⍨'MoveFile error:',⍕err
      :EndSelect
    ∇

    ∇ tn←Nopen name
      :Trap 0
          tn←name ⎕NCREATE 0
      :Else
          tn←name ⎕NTIE 0
      :EndTrap
    ∇

    ∇ r←text PutText name;tn
     ⍝ Write text to file (must be single byte text)
      tn←Nopen name
      0 ⎕NRESIZE tn
      r←text ⎕NAPPEND tn(⎕DR' ')
      ⎕NUNTIE tn
    ∇

    ∇ RmDir path;RemoveDirectoryA;GetLastError
     ⍝ Remove folder/directory
      :Select APLVersion
      :CaseList '*nix' 'Mac'
          ∘
      :Case 'Win'
          ⎕NA'I kernel32.C32∣RemoveDirectory* <0T'
          →(0≠RemoveDirectory,⊂path)⍴0
          ⎕NA'I4 kernel32.C32|GetLastError'
          11 ⎕SIGNAL⍨'RemoveDirectory error:',⍕GetLastError
      :EndSelect
    ∇

    ∇ SetCurrentDirectory path;SCD;GetLastError
     ⍝ Set Current Directory
      :Select APLVersion
      :CaseList '*nix' 'Mac'
          ∘
      :Case 'Win'
          'SCD'⎕NA'I kernel32.C32∣SetCurrentDirectory* <0T'
          →(0≠SCD,⊂path)⍴0
          ⎕NA'I4 kernel32.C32|GetLastError'
          11 ⎕SIGNAL⍨'SetCurrentDirectory error:',⍕GetLastError
      :EndSelect
    ∇

    ∇ r←{noext}SplitFilename filename;filesep;mask;path;file;ext
    ⍝ splits a filename into: path name ext
      noext←{6::0 ⋄ noext}''
      filesep←(~isWin)↓'\/'
      mask←⌽∨\⌽filename∊filesep
      path←mask/filename
      file←(~mask)/filename
      :If noext
          r←path file
      :Else
          mask←∨\⌽<\⌽'.'=file
          ext←mask/file
          file←(~mask)/file
          r←path file ext
      :EndIf
    ∇

    :Section Windows
    ∇ rslt←_Filetime_to_TS filetime;⎕IO
      :If 1≠0⊃rslt←FileTimeToLocalFileTime filetime(⎕IO←0)
      :OrIf 1≠0⊃rslt←FileTimeToSystemTime(1⊃rslt)0
          rslt←0 0                   ⍝ if either call failed then zero the time elements
      :EndIf
      rslt←1 1 0 1 1 1 1 1/1⊃rslt    ⍝ remove day of week
    ∇

    ∇ _FindDefine;WIN32_FIND_DATA
      WIN32_FIND_DATA←'{I4 {I4 I4} {I4 I4} {I4 I4} {U4 U4} {I4 I4} T[260] T[14]}'
      'FindFirstFileX'⎕NA'I4 kernel32.C32|FindFirstFile* <0T >',WIN32_FIND_DATA
      'FindNextFileX'⎕NA'U4 kernel32.C32|FindNextFile* I4 >',WIN32_FIND_DATA
      ⎕NA'kernel32.C32|FindClose I4'
      ⎕NA'I4 kernel32.C32|FileTimeToLocalFileTime <{I4 I4} >{I4 I4}'
      ⎕NA'I4 kernel32.C32|FileTimeToSystemTime <{I4 I4} >{I2 I2 I2 I2 I2 I2 I2 I2}'
      ⎕NA'I4 kernel32.C32∣GetLastError'
    ∇

    ∇ rslt←_FindFirstFile name;⎕IO
      rslt←FindFirstFileX name(⎕IO←0)
      :If ¯1=0⊃rslt                   ⍝ INVALID_HANDLE_VALUE
          rslt←0 GetLastError
      :Else
          (1 6⊃rslt)_FindTrim←0        ⍝ shorten the file name at the null delimiter
          (1 7⊃rslt)_FindTrim←0        ⍝ and for the alternate name
      :EndIf
    ∇

    ∇ rslt←_FindNextFile handle;⎕IO
      rslt←FindNextFileX handle(⎕IO←0)
      :If 1≠0⊃rslt
          rslt←0 GetLastError
      :Else
          (1 6⊃rslt)_FindTrim←0             ⍝ shorten the filename
          (1 7⊃rslt)_FindTrim←0             ⍝ shorten the alternate name
      :EndIf
    ∇

    ∇ name←name _FindTrim ignored;⎕IO
     ⍝ Truncates a character vector at the null delimiting byte.
     ⍝ The null is not included in the result.
      ⎕IO←0
      name↑⍨←name⍳⎕UCS 0
    ∇

    ∇ {r}←{suppress}_CMD cmd
      :If 0=⎕NC'suppress' ⋄ suppress←0 ⋄ :EndIf
      r←''
      :Trap 0
          r←⎕CMD cmd
      :Else
          ('shell command failed: ',cmd)⎕SIGNAL 11/⍨~suppress
      :EndTrap
    ∇

    ∇ r←isWin
      r←'Win'≡APLVersion
    ∇

    :endsection

    :section *nix
    ∇ {r}←{suppress}_SH cmd
    ⍝ SH cover to suppress any error messages
    ⍝ suppress will suppress error from being signaled
      :If 0=⎕NC'suppress' ⋄ suppress←0 ⋄ :EndIf
      r←''
      :Trap 0
          r←⎕SH cmd,' 2>/dev/null'
      :Else
          ('shell command failed: ',cmd)⎕SIGNAL 11/⍨~suppress
      :EndTrap
    ∇

    ∇ f←unixfix f
    ⍝ replaces Windows file separator \ with Unix file separator /
    ⍝ this approach is mindnumbingly simple and probably dangerous
    ⍝ which is why we call unixfix very cautiously
      :If (⊂APLVersion)∊'*nix' 'Mac' ⋄ ((f='\')/f)←'/' ⋄ :EndIf
    ∇

    ∇ r←APLVersion
      :Select 3↑⊃'.'⎕WG'APLVersion'
      :CaseList 'Lin' 'AIX' 'Sol'
          r←'*nix'
      :Case 'Win'
          r←'Win'
      :Case 'Mac'
          r←'Mac'
      :Else
          ∘∘∘ ⍝ unknown version
      :EndSelect
    ∇
    :endsection

    ∇ r←a FREAD w;t
      t←w ⎕FSTIE 0
      r←⎕FREAD t a
      ⎕FUNTIE t
    ∇
:EndNamespace

:Namespace Files

⍝ Provides cover functions for many file operations
⍝ Eventually all will be implemented for both Windows and *nix

    (⎕IO ⎕ML)←1

    ∇ r←data Append name
     ⍝ Append data to existing file
      r←{(⎕NUNTIE ⍵)⊢data ⎕NAPPEND ⍵}Nopen name
    ∇

    ∇ r←text AppendText name;tn
     ⍝ Append text to existing file (must be single byte text)
      tn←Nopen name
      r←text ⎕NAPPEND tn(⎕DR' ')
      ⎕NUNTIE tn
    ∇

    ∇ (tn name)←{ext}CreateTemp folder;cnt
     ⍝ Create a temp file
     ⍝ folder - folder to create temp file in
     ⍝ ext - file extension
      :If 0=⎕NC'ext' ⋄ ext←'tmp' ⋄ :EndIf
      :If 0∊⍴ext ⋄ ext←'tmp' ⋄ :EndIf
      cnt←tn←0
      :Repeat
          name←folder,('/\'∊⍨¯1↑folder)↓'/',⎕A[?8⍴⍴⎕A],'.',ext
          :Trap 22 ⋄ tn←name ⎕NCREATE 0 ⋄ :EndTrap
          cnt+←1
      :Until (cnt>20)∨tn≠0
      'Unable to create temporary file'⎕SIGNAL tn↓22
    ∇

    ∇ {protect}Copy FmTo;CopyFileX;GetLastError ⍝ Copy file Fm -> To
      :Select APLVersion
      :Case '*nix'
          ∘
      :Case 'Win'
          :If 0=⎕NC'protect'     ⍝ Copy fails if <protect> and 'To' exists.
              protect←0          ⍝ Default unprotected copy.
          :EndIf
          'CopyFileX'⎕NA'I kernel32.C32∣CopyFile* <0T <0T I'
          :If 0=CopyFileX FmTo,protect
              ⎕NA'I4 kernel32.C32|GetLastError'
              11 ⎕SIGNAL⍨'CopyFile error:',⍕GetLastError
          :EndIf
      :EndSelect
    ∇

    ∇ Delete name;DeleteFileX;GetLastError;FindFirstFile;FindNextFile;FindClose;handle;rslt;ok;next;⎕IO;path
      ⎕IO←0
      :Select APLVersion
      :CaseList '*nix' 'Mac'
          _SH'rm ',unixfix name
      :Case 'Win'
          'DeleteFileX'⎕NA'I kernel32.C32∣DeleteFile* <0T'
          ⎕NA'I4 kernel32.C32|GetLastError'
          :If ∨/'*?'∊name ⍝ wildcards?
              path←{(⌽∨\⌽⍵∊'\/')/⍵}name
              _FindDefine
              handle rslt←_FindFirstFile name
              :If 0=handle
                  :Return ⍝ ('ntdir error:',⍕rslt)⎕SIGNAL 102      ⍝ file not found
              :EndIf
              :If '.'≠⊃6⊃rslt
                  {}DeleteFileX⊂path,6⊃rslt
              :EndIf
              :While 1=0⊃ok next←_FindNextFile handle
                  :If '.'≠⊃6⊃next
                      {}DeleteFileX⊂path,6⊃next
                  :EndIf
              :EndWhile
              :If 0 18∨.≠ok next
                  ('ntdir error:',⍕next)⎕SIGNAL 11   ⍝ DOMAIN
              :EndIf
              {}FindClose handle
          :Else
              :If 0=DeleteFileX⊂name
                  11 ⎕SIGNAL⍨'DeleteFile error:',⍕GetLastError
              :EndIf
          :EndIf
      :EndSelect
    ∇

    ∇ r←DirExists path
      r←0
      :Select APLVersion
      :CaseList '*nix' 'Mac'
          :Trap 11
              r←0<⍴_SH'ls -adl ',unixfix path
          :EndTrap
      :Case 'Win'
          r←0<⍬⍴⍴'.'List path
      :EndSelect
    ∇

    ∇ r←Exists name
     ⍝ Does file exist?
      r←1
      :Trap 22
          :Trap 19 ⍝ file access error means file exists
              ⎕NUNTIE(unixfix name)⎕NTIE 0
          :EndTrap
      :Else
          r←0
      :EndTrap
    ∇

    ∇ r←GetCurrentDirectory;GCD;GetLastError
     ⍝ Get Current Directory
      :Select APLVersion
      :CaseList '*nix' 'Mac'
          r←⊃_SH'pwd'
      :Case 'Win'
          'GCD'⎕NA'I kernel32.C32∣GetCurrentDirectory* I4 >0T'
          :If 0≠1⊃r←GCD 256 256
              r←2⊃r
          :Else
              ⎕NA'I4 kernel32.C32|GetLastError'
              11 ⎕SIGNAL⍨'GetCurrentDirectory error:',⍕GetLastError
          :EndIf
      :EndSelect
    ∇

    ∇ Chars←GetText name;nid;signature;nums;sz
     ⍝ Read ANSI or Unicode character file
      sz←⎕NSIZE nid←(unixfix name)⎕NTIE 0
      signature←⎕NREAD nid 83 3 0
      :If signature≡¯17 ¯69 ¯65 ⍝ UTF-8?
          nums←⎕NREAD nid 83 sz
          Chars←'UTF-8'⎕UCS 256|nums ⍝ Signed ints
      :ElseIf ∨/b←(2↑signature)∧.=2 2⍴¯1 ¯2 ¯2 ⍝ Unicode (UTF-16)
          Chars←{,⌽(2,⍨2÷⍨⍴⍵)⍴⍵}⍣(⊣/b)⎕NREAD nid 83 sz 2
          Chars←'UTF-16'⎕UCS(2*16)|163 ⎕DR Chars
      :Else ⍝ ANSI or UTF-8
          Chars←{11::⎕UCS ⍵ ⋄ 'UTF-8'⎕UCS ⍵}256|⎕NREAD nid 83 sz 0
      :EndIf
      ⎕NUNTIE nid
    ∇

    ∇ vtv←GetVTV name
     ⍝ Read ANSI or Unicode character file as vector of text vectors
      vtv←{1↓¨(v=n)⊂v←(n←⎕UCS 10),⍵}GetText name
    ∇

    ∇ r←LikelyURL w
      →0↓⍨r←(⎕DR w)∊80 82
      r←{(0∊⍴⍵)<∧/⍵∊'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789%-._~:/?#[]@!$&''()*+,;='}w ⍝ identify likely URIs
    ∇

    ∇ r←{file}List path;z;rslt;handle;next;ok;attrs;⎕IO;FindFirstFileX;FindNextFileX;FindClose;FileTimeToLocalFileTime;FileTimeToSystemTime;GetLastError;isFile;filter
    ⍝ Return matrix containing
    ⍝ [;0] Name [;1] Length [;2] LastAccessTime [;3] IsDirectory
      ⎕IO←0
      :If isFile←2=⎕NC'file' ⋄ filter←(0<⍴,file)/'/',file ⋄ :Else ⋄ filter←'/*' ⋄ :EndIf
      filter←('/\'∊⍨¯1↑path)↓filter
      r←0 4⍴'' 0 0 0

      :Select APLVersion
      :Case '*nix'
          →(0∊⍴rslt←1 _SH'ls -al --time-style=full-iso ',unixfix path,isFile/filter)⍴0
          rslt←↑rslt
          rslt←' ',('total '≡6⍴rslt)↓[0]rslt
          →(0=1↑⍴r←((1↑⍴rslt),4)⍴0)⍴0
          z←∧⌿' '=rslt ⍝ entirely blank columns
          z←z∧10>+\z    ⍝ Do not split file names
          rslt←z⊂rslt
          r[;3]←'d'=(0⊃rslt)[;1]                 ⍝ IsDirectory
          r[;1]←(~r[;3])×1⊃⎕VFI,4⊃rslt ⍝ Size
          z←,(5⊃rslt),6⊃rslt ⋄ ((z∊'-:')/z)←' ' ⋄ z←((1↑⍴r),6)⍴1⊃⎕VFI z
          r[;2]←↓⌊z,1000×1|z[;5]                ⍝ Add msec to Timestamp
          r[;0]←{(⌽~∨\⌽⍵='/')/⍵}¨{(-+/∧\' '=⌽⍵)↓¨↓⍵}0 1↓8⊃rslt    ⍝ Name

      :Case 'Mac'
          →(0∊⍴rslt←1 _SH'stat -lt "%F %T %z" ',unixfix path,isFile/filter)⍴0
          r←((1↑⍴rslt),4)⍴0
          rslt←↑{⎕ML←3 ⋄ ⍵⊂⍨~{⍵∧9>+\⍵}' '=⍵}¨rslt
          r[;3]←'d'=0⊃¨rslt[;1]                 ⍝ IsDirectory
          r[;1]←(~r[;3])×1⊃¨⎕VFI¨rslt[;4]        ⍝ Size
          z←↑∊¨↓{w←⍵ ⋄ ((w∊'-:')/w)←' ' ⋄ 1⊃⎕VFI w}¨rslt[;5 6] ⍝
          r[;2]←↓z,0                            ⍝ 0 msec for MacOS to Timestamp
          r[;0]←rslt[;8]                        ⍝ Name

      :Case 'Win'
      ⍝ See DirX for explanations of results of _FindNextFile etc
          _FindDefine
          handle rslt←_FindFirstFile path,filter
          :If 0=handle
              :Return ⍝ ('ntdir error:',⍕rslt)⎕SIGNAL 102      ⍝ file not found
          :EndIf
          rslt←,⊂rslt
          :While 1=0⊃ok next←_FindNextFile handle
              rslt,←⊂next
          :EndWhile
          :If 0 18∨.≠ok next
              ('ntdir error:',⍕next)⎕SIGNAL 11   ⍝ DOMAIN
          :EndIf
          ok←FindClose handle
          rslt←↓[0]↑rslt
          →(0=1↑⍴r←((1↑⍴0⊃rslt),4)⍴0)⍴0
          (0⊃rslt)←⍉attrs←(32⍴2)⊤0⊃rslt                ⍝ Get attributes into bits
          r[;3]←(0⊃rslt)[;27]              ⍝ IsDirectory?
          r[;1]←0(2*32)⊥⍉↑4⊃rslt          ⍝ combine size elements
          r[;2]←_Filetime_to_TS¨3⊃rslt     ⍝ As ⎕TS vector
          r[;0]←6⊃rslt                     ⍝ Name
      :EndSelect
      r←r[⍋↑r[;0];]
    ∇

    ∇ MkDir path;CreateDirectory;GetLastError;err
      ⍝ Create a folder
      :Select APLVersion
      :CaseList '*nix' 'Mac'
          :If ~DirExists path
              1 _SH'mkdir ',unixfix path
              ('mkdir error on ',path)⎕SIGNAL 11/⍨~DirExists path
          :EndIf
      :Case 'Win'
          ⎕NA'I kernel32.C32∣CreateDirectory* <0T I4' ⍝ Try for best function
          →(0≠CreateDirectory path 0)⍴0 ⍝ 0 means "default security attributes"
          ⎕NA'I4 kernel32.C32|GetLastError'
          err ⎕SIGNAL⍨'CreateDirectory error:',⍕err←GetLastError
      :EndSelect
    ∇

    ∇ Move filenames;MoveFileX;MoveFileExA;GetLastError;err
    ⍝ Move (from to) - move/rename file
      :Select APLVersion
      :Case '*nix' 'Mac'
          ∘
      :Case 'Win'
          ⎕NA'I kernel32.C32∣MoveFileEx* <0T <0T I4' ⍝ Try for best function
          :If 0≠MoveFileExA filenames,3 ⍝ REPLACE_EXISTING (1) + COPY_ALLOWED (2)
              :Return
          :EndIf
          ⎕NA'I4 kernel32.C32|GetLastError'
          :Select err←GetLastError
          :Case 120                     ⍝ ERROR_CALL_NOT_IMPLIMENTED
              'MoveFileX'⎕NA'I Kernel32.C32∣MoveFile* <0T <0T' ⍝ accept 2nd best - win 95
              :If 0≠MoveFileX filenames
                  :Return
              :EndIf
              err←GetLastError
          :EndSelect
          11 ⎕SIGNAL⍨'MoveFile error:',⍕err
      :EndSelect
    ∇

    ∇ tn←Nopen name
      :Trap 0
          tn←name ⎕NCREATE 0
      :Else
          tn←name ⎕NTIE 0
      :EndTrap
    ∇

    ∇ r←text PutText name;tn
     ⍝ Write text to file (must be single byte text)
      tn←Nopen name
      0 ⎕NRESIZE tn
      r←text ⎕NAPPEND tn(⎕DR' ')
      ⎕NUNTIE tn
    ∇

    ∇ RmDir path;RemoveDirectoryA;GetLastError
     ⍝ Remove folder/directory
      :Select APLVersion
      :CaseList '*nix' 'Mac'
          ∘
      :Case 'Win'
          ⎕NA'I kernel32.C32∣RemoveDirectory* <0T'
          →(0≠RemoveDirectory,⊂path)⍴0
          ⎕NA'I4 kernel32.C32|GetLastError'
          11 ⎕SIGNAL⍨'RemoveDirectory error:',⍕GetLastError
      :EndSelect
    ∇

    ∇ SetCurrentDirectory path;SCD;GetLastError
     ⍝ Set Current Directory
      :Select APLVersion
      :CaseList '*nix' 'Mac'
          ∘
      :Case 'Win'
          'SCD'⎕NA'I kernel32.C32∣SetCurrentDirectory* <0T'
          →(0≠SCD,⊂path)⍴0
          ⎕NA'I4 kernel32.C32|GetLastError'
          11 ⎕SIGNAL⍨'SetCurrentDirectory error:',⍕GetLastError
      :EndSelect
    ∇

    ∇ r←{noext}SplitFilename filename;filesep;mask;path;file;ext
    ⍝ splits a filename into: path name ext
      noext←{6::0 ⋄ noext}''
      filesep←(~isWin)↓'\/'
      mask←⌽∨\⌽filename∊filesep
      path←mask/filename
      file←(~mask)/filename
      :If noext
          r←path file
      :Else
          mask←∨\⌽<\⌽'.'=file
          ext←mask/file
          file←(~mask)/file
          r←path file ext
      :EndIf
    ∇

    :Section Windows
    ∇ rslt←_Filetime_to_TS filetime;⎕IO
      :If 1≠0⊃rslt←FileTimeToLocalFileTime filetime(⎕IO←0)
      :OrIf 1≠0⊃rslt←FileTimeToSystemTime(1⊃rslt)0
          rslt←0 0                   ⍝ if either call failed then zero the time elements
      :EndIf
      rslt←1 1 0 1 1 1 1 1/1⊃rslt    ⍝ remove day of week
    ∇

    ∇ _FindDefine;WIN32_FIND_DATA
      WIN32_FIND_DATA←'{I4 {I4 I4} {I4 I4} {I4 I4} {U4 U4} {I4 I4} T[260] T[14]}'
      'FindFirstFileX'⎕NA'I4 kernel32.C32|FindFirstFile* <0T >',WIN32_FIND_DATA
      'FindNextFileX'⎕NA'U4 kernel32.C32|FindNextFile* I4 >',WIN32_FIND_DATA
      ⎕NA'kernel32.C32|FindClose I4'
      ⎕NA'I4 kernel32.C32|FileTimeToLocalFileTime <{I4 I4} >{I4 I4}'
      ⎕NA'I4 kernel32.C32|FileTimeToSystemTime <{I4 I4} >{I2 I2 I2 I2 I2 I2 I2 I2}'
      ⎕NA'I4 kernel32.C32∣GetLastError'
    ∇

    ∇ rslt←_FindFirstFile name;⎕IO
      rslt←FindFirstFileX name(⎕IO←0)
      :If ¯1=0⊃rslt                   ⍝ INVALID_HANDLE_VALUE
          rslt←0 GetLastError
      :Else
          (1 6⊃rslt)_FindTrim←0        ⍝ shorten the file name at the null delimiter
          (1 7⊃rslt)_FindTrim←0        ⍝ and for the alternate name
      :EndIf
    ∇

    ∇ rslt←_FindNextFile handle;⎕IO
      rslt←FindNextFileX handle(⎕IO←0)
      :If 1≠0⊃rslt
          rslt←0 GetLastError
      :Else
          (1 6⊃rslt)_FindTrim←0             ⍝ shorten the filename
          (1 7⊃rslt)_FindTrim←0             ⍝ shorten the alternate name
      :EndIf
    ∇

    ∇ name←name _FindTrim ignored;⎕IO
     ⍝ Truncates a character vector at the null delimiting byte.
     ⍝ The null is not included in the result.
      ⎕IO←0
      name↑⍨←name⍳⎕UCS 0
    ∇

    ∇ {r}←{suppress}_CMD cmd
      :If 0=⎕NC'suppress' ⋄ suppress←0 ⋄ :EndIf
      r←''
      :Trap 0
          r←⎕CMD cmd
      :Else
          ('shell command failed: ',cmd)⎕SIGNAL 11/⍨~suppress
      :EndTrap
    ∇

    ∇ r←isWin
      r←'Win'≡APLVersion
    ∇

    :endsection

    :section *nix
    ∇ {r}←{suppress}_SH cmd
    ⍝ SH cover to suppress any error messages
    ⍝ suppress will suppress error from being signaled
      :If 0=⎕NC'suppress' ⋄ suppress←0 ⋄ :EndIf
      r←''
      :Trap 0
          r←⎕SH cmd,' 2>/dev/null'
      :Else
          ('shell command failed: ',cmd)⎕SIGNAL 11/⍨~suppress
      :EndTrap
    ∇

    ∇ f←unixfix f
    ⍝ replaces Windows file separator \ with Unix file separator /
    ⍝ this approach is mindnumbingly simple and probably dangerous
    ⍝ which is why we call unixfix very cautiously
      :If (⊂APLVersion)∊'*nix' 'Mac' ⋄ ((f='\')/f)←'/' ⋄ :EndIf
    ∇

    ∇ r←APLVersion
      :Select 3↑⊃'.'⎕WG'APLVersion'
      :CaseList 'Lin' 'AIX' 'Sol'
          r←'*nix'
      :Case 'Win'
          r←'Win'
      :Case 'Mac'
          r←'Mac'
      :Else
          ∘∘∘ ⍝ unknown version
      :EndSelect
    ∇
    :endsection

    ∇ r←a FREAD w;t
      t←w ⎕FSTIE 0
      r←⎕FREAD t a
      ⎕FUNTIE t
    ∇
:EndNamespace


Hints and colourful features

Hints and colourful features

:class Hebrew : MiPageSample
⍝ Control:: Kids' Keyboard
⍝ Description:: Children's educative application for Hebrew alphabet and keyboard

    Sound←{'#sound'Replace'autoplay' ''New _.Audio,⊂'/Examples/Data/Hebrew/',⍵,'.mp3'}
    LetBut←{('.letter',⍕⍵∊'שבגדכפת') ('#letter',⍕⎕UCS ⍵) (RandCol 0) New _.Button ⍵}
    Indent←{('style="margin-left:',(⍕⍵÷2-0∊⍴Get'nowrapper'),'pt;margin-bottom:2pt"')New _.span}

      RandCol←{
          ×⍵:'style="color:rgb(',(1↓∊',',¨⍕¨(1-⍵)⌽127 ¯1 ¯1+?3/128),');"'
          'style="color:rgb(',(1↓∊',',¨⍕¨(¯1 127 ¯1+?1 128 256)[3?3]),');"'
      }

    ∇ Compose;style
      :Access public
      {}16807⌶2
      TASK←''

      style←''
      :If 0∊⍴Get'nowrapper' ⍝ Full-size
          style,←'button {width: 60pt; height: 60pt; font-size:40pt; '
      :Else ⍝ Half-size
          style,←'button {width: 30pt; height: 30pt; font-size:20pt; '
      :EndIf
      style,←'margin:2pt; font-family: serif;} button:hover {font-weight:bold;',' cursor:pointer;'⊢'}'
      Add _.style style

      Add _.h2'Click a letter to hear it or › to hear which letter to find. Hover over ? to repeat what to find, and click to see the letter to find.'

      Add'#play'(Indent 100).Add _.Button'›'
      Add'#new'(Indent 100).Add _.Button'×'
      Add'#hint'(Indent 100).Add _.Button'?'

      Add _.hr

      keys←'#keys'Add _.div
      keys.Add Keyboard

      Add _.Handler'#hint' 'mouseover mouseout' 'Hint'
      Add _.Handler'#hint' 'click' 'OnClick'
      Add _.Handler'#play' 'click' 'OnClick'
      Add _.Handler'#new' 'click' 'OnClick'

      '#sound'Add _.div
    ∇

    ∇ kb←Keyboard
      kb←New _.div
      (kb.Add Indent 120).Add¨LetBut¨'קראטוןםפ'
      kb.Add _.br
      (kb.Add Indent 0).Add¨LetBut¨'שדגכעיחלךף'
      kb.Add _.br
      (kb.Add Indent 40).Add¨LetBut¨'זסבהנמצתץ'
      kb.Add _.br
      '#feedback' 'style="font-size:45pt;"' 'dir="ltr"'kb.Add _.bdo
      kb.Add _.Handler'.letter0' 'click' 'OnClick'
      kb.Add _.Handler'.letter1' 'click' 'OnClick'
      kb.Add _.Handler'.letter1' 'mouseover mouseout' 'Bold'

    ∇

    ∇ r←OnClick;sound
      :Access Public
      :If ×≢TASK
          :Select _what
          :Case 'play'
              TASK←'letter',⍕1487+?27
              r←Sound TASK
          :Case 'hint'
              r←'#hint'Replace ⎕UCS⍎6↓TASK
              r,←'#feedback'Prepend(RandCol 3)New _.span'? '
          :Case TASK
              r←Sound'Yay'
              r,←'#feedback'Prepend(RandCol 2)New _.span'☺ '
              TASK←''
          :Case 'new'
              r←'#keys'Replace Keyboard
              TASK←''
          :Else
              r←Sound'Boo'
              r,←'#feedback'Prepend(RandCol 1)New _.span'☹ '
          :EndSelect
      :Else
          :Select _what
          :Case 'play'
              TASK←'letter',⍕1487+?27
              r←Sound TASK
          :Case 'hint'
              r←''
          :Case 'new'
              r←'#keys'Replace Keyboard
          :Else
              r←Sound _what
              r,←'#feedback'Prepend(RandCol 3)New _.span(⎕UCS⍎6↓_what)
          :EndSelect
      :EndIf
    ∇

    ∇ r←Bold
      :Access Public
      r←('#',_what)Replace('שבגדכפת'⍳⎕UCS⍎6↓_what)⊃'שבגדכפתשׁבּגּדּכּפּתּ'⌽⍨7×_event≡'mouseover'
    ∇

    ∇ r←Hint
      :Access Public
      :If ×≢TASK
          :Select _event
          :Case 'mouseover'
              r←Sound TASK
          :Case 'mouseout'
              r←'#hint'Replace'?'
          :EndSelect
      :Else
          r←''
      :EndIf
    ∇
:endclass

:class Hebrew : MiPageSample
⍝ Control:: Kids' Keyboard
⍝ Description:: Children's educative application for Hebrew alphabet and keyboard

    Sound←{'#sound'Replace'autoplay' ''New _.Audio,⊂'/Examples/Data/Hebrew/',⍵,'.mp3'}
    LetBut←{('.letter',⍕⍵∊'שבגדכפת') ('#letter',⍕⎕UCS ⍵) (RandCol 0) New _.Button ⍵}
    Indent←{('style="margin-left:',(⍕⍵÷2-0∊⍴Get'nowrapper'),'pt;margin-bottom:2pt"')Add _.span}

      RandCol←{
          ×⍵:'style="color:rgb(',(1↓∊',',¨⍕¨(1-⍵)⌽127 ¯1 ¯1+?3/128),');"'
          'style="color:rgb(',(1↓∊',',¨⍕¨(¯1 127 ¯1+?1 128 256)[3?3]),');"'
      }

    ∇ Compose;style
      :Access public
      {}16807⌶2
      TASK←''

      style←''
      :If 0∊⍴Get'nowrapper' ⍝ Full-size
          style,←'button {width: 60pt; height: 60pt; font-size:40pt; '
      :Else ⍝ Half-size
          style,←'button {width: 30pt; height: 30pt; font-size:20pt; '
      :EndIf
      style,←'margin:2pt; font-family: serif;} button:hover {font-weight:bold;',' cursor:pointer;'⊢'}'
      Add _.style style

      Add _.h2'Click a letter to hear it or › to hear which letter to find. Hover over ? to repeat what to find, and click to see the letter to find.'

      '#play' 'style="font-size:30pt;"'Add _.Button'›'
      '#hint' '.menu'(Indent 400).Add _.Button'?'

      Add _.hr
      BUTTONS←''
      BUTTONS,←(Indent 120).Add¨LetBut¨'קראטוןםפ'
      Add _.br
      BUTTONS,←(Indent 0).Add¨LetBut¨'שדגכעיחלךף'
      Add _.br
      BUTTONS,←(Indent 40).Add¨LetBut¨'זסבהנמצתץ'

      Add _.Handler'button' 'click' 'OnClick'
      Add _.Handler'.letter1' 'mouseover mouseout' 'Bold'
      Add _.Handler'#hint' 'mouseover mouseout' 'Hint'

      '#sound'Add _.div
      '#feedback' 'style="font-size:30pt;"' 'dir="ltr"'Add _.bdo
    ∇

    ∇ r←OnClick;sound
      :Access Public
      :If ×≢TASK
          :Select _what
          :Case 'play'
              TASK←'letter',⍕1487+?27
              r←Sound TASK
          :Case 'hint'
              r←'#hint'Replace ⎕UCS⍎6↓TASK
          :Case TASK
              r←Sound'Yay'
              r,←'#feedback'Prepend(RandCol 2)New _.span'☺ '
              TASK←''
          :Else
              r←Sound'Boo'
              r,←'#feedback'Prepend(RandCol 1)New _.span'☹ '
          :EndSelect
      :Else
          :Select _what
          :Case 'play'
              TASK←'letter',⍕1487+?27
              r←Sound TASK
          :Case 'hint'
              r←''
          :Else
              r←Sound _what
              r,←'#feedback'Prepend(RandCol 3)New _.span(⎕UCS⍎6↓_what)
          :EndSelect
      :EndIf
    ∇

    ∇ r←Bold
      :Access Public
      r←('#',_what)Replace('שבגדכפת'⍳⎕UCS⍎6↓_what)⊃'שבגדכפתשׁבּגּדּכּפּתּ'⌽⍨7×_event≡'mouseover'
    ∇

    ∇ r←Hint
      :Access Public
      :If ×≢TASK
          :Select _event
          :Case 'mouseover'
              r←Sound TASK
          :Case 'mouseout'
              r←'#hint'Replace'?'
          :EndSelect
      :Else
          r←''
      :EndIf
    ∇
:endclass


added ⎕a and ⎕á

added ⎕a and ⎕á

⍬←()
⍝ ⍬     ←→ 0⍴0
⍝ ⍴⍬    ←→ ,0
⍝# ⍬←5   !!!
⍝ ⍳0    ←→ ⍬
⍝ ⍴0    ←→ ⍬
⍝ ⍬     ←→ ⍬
⍝ ⍬⍬    ←→ ⍬ ⍬
⍝ 1⍬2⍬3 ←→ 1 ⍬ 2 ⍬ 3

⎕a←'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
⎕á←'ÁÂÃÇÈÊËÌÍÎÏÐÒÓÔÕÙÚÛÝþãìðòõ'

~←~⍠{(~⍺∊⍵)/⍺}
⍝ "ABCDEFGHIJKLMNOPQRSTUVWXYZ"~"AEIOU" ←→ 'BCDFGHJKLMNPQRSTVWXYZ'
⍝ 1 2 3 4 5 6~2 4 6                    ←→ 1 3 5
⍝ "THIS IS TEXT"~" "                   ←→ 'THISISTEXT'
⍝ "THIS" "AND" "THAT"~"T"              ←→ 'THIS' 'AND' 'THAT'
⍝ "THIS" "AND" "THAT"~"AND"            ←→ 'THIS' 'AND' 'THAT'
⍝ "THIS" "AND" "THAT"~⊂"AND"           ←→ 'THIS' 'THAT'
⍝ "THIS" "AND" "THAT"~"TH" "AND"       ←→ 'THIS' 'THAT'
⍝ 11 12 13 14 15 16~2 3⍴1 2 3 14 5 6   ←→ 11 12 13 15 16
⍝ (2 2⍴⍳4)~2 !!! RANK ERROR

_hook←{⍵⍶⍹⍵;⍺⍶⍹⍵}
⍝ (+÷)\3 7 16 ¯294 ←→ (3 3.142857142857143 3.1415929203539825 3.141592653921421)
⍝ (=⌊)123 ←→ 1
⍝ (=⌊)123.4 ←→ 0
⍝ (÷⍟)1000 ←→ 144.76482730108395

⊃←{
  0=⍴⍴⍵:↑⍵
  0=×/⍴⍵:⍵
  shape←⍴⍵ ⋄ ⍵←,⍵
  r←⌈/≢¨shapes←⍴¨⍵ ⍝ maximum rank of all shapes
  max←↑⌈/shapes←(⍴ ↓ (r⍴1)∘,)¨shapes ⍝ maximum shape of rank adjusted shapes
  (shape,max)⍴↑⍪/shapes{max↑⍺⍴⍵}¨⍵
  ;
  1<⍴⍴⍺:↗'RANK ERROR'
  x←⍵
  {
    1<⍴⍴⍵:↗'RANK ERROR'
    ⍵←,⍵
    (⍴⍵)≠⍴⍴x:↗'RANK ERROR'
    ∨/⍵≥⍴x:↗'INDEX ERROR'
    x←⊃⍵⌷x
  }¨⍺
  x
}
⍝ ⊃3            ←→ 3
⍝ ⊃(1 2)(3 4)   ←→ 2 2⍴1 2 3 4
⍝ ⊃(1 2)(3 4 5) ←→ 2 3⍴1 2 0 3 4 5
⍝ ⊃1 2          ←→ 1 2
⍝ ⊃(1 2)3       ←→ 2 2⍴1 2 3 0
⍝ ⊃1(2 3)       ←→ 2 2⍴1 0 2 3
⍝ ⊃2 2⍴1(1 1 2⍴3 4)(5 6)(2 0⍴0) ←→ 2 2 1 2 2⍴1 0 0 0 3 4 0 0 5 6 0 0 0 0 0 0
⍝ ⊃⍬            ←→ ⍬
⍝ ⊃2 3 0⍴0      ←→ 2 3 0⍴0
⍝ ⍬⊃3               ←→ 3
⍝ 2⊃'PICK'          ←→ 'C'
⍝ (⊂1 0)⊃2 2⍴'ABCD' ←→ 'C'
⍝ 1⊃'foo' 'bar'     ←→ 'bar'
⍝ 1 2⊃'foo' 'bar'   ←→ 'r'
⍝ (2 2⍴0)⊃1 2       !!! RANK ERROR
⍝ (⊂2 1⍴0)⊃2 2⍴0    !!! RANK ERROR
⍝ (⊂2 2⍴0)⊃1 2      !!! RANK ERROR
⍝ (⊂2 2)⊃1 2        !!! RANK ERROR
⍝ (⊂0 2)⊃2 2⍴'ABCD' !!! INDEX ERROR

⊂←⊂⍠{ ⍝ ⍺⊂⍵ definition
  1<⍴⍴⍺:↗'RANK ERROR' ⋄ 1≠⍴⍴⍵:↗'NONCE ERROR' ⋄ ⍺←,⍺=0
  keep←~1 1⍷⍺ ⋄ sel←keep/⍺ ⋄ dat←keep/⍵
  {1=1↑sel:{sel←1↓sel ⋄ dat←1↓dat}⍬}⍬
  {1=¯1↑sel:{sel←¯1↓sel ⋄ dat←¯1↓dat}⍬}⍬
  sel←(⍴sel),⍨sel/⍳⍴sel ⋄ drop←0
  sel{∆←drop↓⍺↑⍵ ⋄ drop←⍺+1 ⋄ ∆}¨⊂dat
}
⍝ a←' this is a test ' ⋄ (a≠' ')⊂a ←→ 'this' 'is' (,'a') 'test'

↓←{
  0=⍴⍴⍵:⍵ ⋄ ⊂[¯1+⍴⍴⍵]⍵
  ;
  1<⍴⍴⍺:↗'RANK ERROR'
  a←,⍺
  ⍵←{0=⍴⍴⍵:((⍴a)⍴1)⍴⍵⋄⍵}⍵
  (⍴a)>⍴⍴⍵:↗'RANK ERROR'
  a←(⍴⍴⍵)↑a
  a←((a>0)×0⌊a-⍴⍵)+(a≤0)×0⌈a+⍴⍵
  a↑⍵
}
⍝ ↓1 2 3 ←→ ⊂1 2 3
⍝ ↓(1 2)(3 4) ←→ ⊂(1 2)(3 4)
⍝ ↓2 2⍴⍳4 ←→ (0 1)(2 3)
⍝ ↓2 3 4⍴⍳24 ←→ 2 3⍴(0 1 2 3)(4 5 6 7)(8 9 10 11)(12 13 14 15)(16 17 18 19)(20 21 22 23)
⍝ 4↓'OVERBOARD'         ←→ 'BOARD'
⍝ ¯5↓'OVERBOARD'        ←→ 'OVER'
⍝ ⍴10↓'OVERBOARD'       ←→ ,0
⍝ 0 ¯2↓3 3⍴'ONEFATFLY'  ←→ 3 1⍴'OFF'
⍝ ¯2 ¯1↓3 3⍴'ONEFATFLY' ←→ 1 2⍴'ON'
⍝ 1↓3 3⍴'ONEFATFLY'     ←→ 2 3⍴'FATFLY'
⍝ ⍬↓3 3⍴⍳9              ←→ 3 3⍴⍳9
⍝ 1 1↓2 3 4⍴"ABCDEFGHIJKLMNOPQRSTUVWXYZ"   ←→ 1 2 4⍴'QRSTUVWX'
⍝ ¯1 ¯1↓2 3 4⍴"ABCDEFGHIJKLMNOPQRSTUVWXYZ" ←→ 1 2 4⍴'ABCDEFGH'
⍝ 1↓0                   ←→ ⍬
⍝ 0 1↓2                 ←→ 1 0⍴0
⍝ 1 2↓3                 ←→ 0 0⍴0
⍝ ⍬↓0                   ←→ 0

⍪←{(≢⍵)(×/1↓⍴⍵)⍴⍵; ⍺,[0]⍵}
⍝ ⍪2 3 4 ←→ 3 1⍴2 3 4
⍝ ⍪0 ←→ 1 1⍴0
⍝ ⍪2 2⍴2 3 4 5 ←→ 2 2⍴2 3 4 5
⍝ ⍴⍪2 3⍴⍳6 ←→ 2 3
⍝ ⍴⍪2 3 4⍴⍳24 ←→ 2 12
⍝ (2 3⍴⍳6)⍪9 ←→ 3 3⍴(0 1 2 3 4 5 9 9 9)
⍝ 1⍪2 ←→ 1 2

⊢←{⍵}
⍝ 123⊢456 ←→ 456
⍝ ⊢456 ←→ 456

⊣←{;⍺}
⍝ 123⊣456 ←→ 123
⍝ ⊣456 ←→ ⍬

≢←{⍬⍴(⍴⍵),1; ~⍺≡⍵}
⍝ ≢0 ←→ 1
⍝ ≢0 0 0 ←→ 3
⍝ ≢⍬ ←→ 0
⍝ ≢2 3⍴⍳6 ←→ 2
⍝ 3≢3 ←→ 0

,←{(×/⍴⍵)⍴⍵}⍠,
⍝ ,2 3 4⍴'abcdefghijklmnopqrstuvwx' ←→ 'abcdefghijklmnopqrstuvwx'
⍝ ,123 ←→ 1⍴123

⌹←{
  norm←{(⍵+.×+⍵)*0.5}

  QR←{ ⍝ QR decomposition
    n←(⍴⍵)[1]
    1≥n:{t←norm,⍵ ⋄ (⍵÷t)(⍪t)}⍵
    m←⌈n÷2
    a0←((1↑⍴⍵),m)↑⍵
    a1←(0,m)↓⍵
    (q0 r0)←∇a0
    c←(+⍉q0)+.×a1
    (q1 r1)←∇a1-q0+.×c
    (q0,q1)((r0,c)⍪((⌊n÷2),-n)↑r1)
  }

  Rinv←{ ⍝ Inverse of an upper triangular matrix
    1=n←1↑⍴⍵:÷⍵
    m←⌈n÷2
    ai←∇(m,m)↑⍵
    di←∇(m,m)↓⍵
    b←(m,m-n)↑⍵
    bx←-ai+.×b+.×di
    (ai,bx)⍪((⌊n÷2),-n)↑di
  }

  0=⍴⍴⍵:÷⍵
  1=⍴⍴⍵:,∇⍪⍵
  2≠⍴⍴⍵:↗'RANK ERROR'
  0∊≥/⍴⍵:↗'LENGTH ERROR'
  (Q R)←QR ⍵
  (Rinv R)+.×+⍉Q
  ;
  (⌹⍵)+.×⍺
}
⍝ ⌹2 ←→ .5
⍝ ⌹2 2⍴4 3 3 2 ←→ 2 2⍴¯2 3 3 ¯4
⍝ (4 4⍴12 1 4 10 ¯6 ¯5 4 7 ¯4 9 3 4 ¯2 ¯6 7 7)⌹93 81 93.5 120.5 ←→
⍝ ... .0003898888816687221 ¯.005029566573526544 .04730651764247189 .0705568912859835
⍝ ⌹2 2 2⍴⍳8 !!! RANK ERROR
⍝ ⌹2 3⍴⍳6 !!! LENGTH ERROR

⍨←{⍵⍶⍵;⍵⍶⍺}
⍝ 17-⍨23 ←→ 6
⍝ 7⍴⍨2 3 ←→ 2 3⍴7
⍝ +⍨2    ←→ 4
⍝ -⍨123  ←→ 0

⍝ Aliases:
−←- ⍝ U+2212 MINUS SIGN        for U+002D HYPHEN-MINUS
⋆←* ⍝ U+22C6 STAR OPERATOR     for U+002A ASTERISK
∣←| ⍝ U+2223 DIVIDES           for U+007C VERTICAL LINE
^←∧ ⍝ U+005E CIRCUMFLEX ACCENT for U+2227 LOGICAL AND
∈←∊ ⍝ U+2208 ELEMENT OF        for U+220A SMALL ELEMENT OF

⍬←()
⍝ ⍬     ←→ 0⍴0
⍝ ⍴⍬    ←→ ,0
⍝# ⍬←5   !!!
⍝ ⍳0    ←→ ⍬
⍝ ⍴0    ←→ ⍬
⍝ ⍬     ←→ ⍬
⍝ ⍬⍬    ←→ ⍬ ⍬
⍝ 1⍬2⍬3 ←→ 1 ⍬ 2 ⍬ 3

~←~⍠{(~⍺∊⍵)/⍺}
⍝ "ABCDEFGHIJKLMNOPQRSTUVWXYZ"~"AEIOU" ←→ 'BCDFGHJKLMNPQRSTVWXYZ'
⍝ 1 2 3 4 5 6~2 4 6                    ←→ 1 3 5
⍝ "THIS IS TEXT"~" "                   ←→ 'THISISTEXT'
⍝ "THIS" "AND" "THAT"~"T"              ←→ 'THIS' 'AND' 'THAT'
⍝ "THIS" "AND" "THAT"~"AND"            ←→ 'THIS' 'AND' 'THAT'
⍝ "THIS" "AND" "THAT"~⊂"AND"           ←→ 'THIS' 'THAT'
⍝ "THIS" "AND" "THAT"~"TH" "AND"       ←→ 'THIS' 'THAT'
⍝ 11 12 13 14 15 16~2 3⍴1 2 3 14 5 6   ←→ 11 12 13 15 16
⍝ (2 2⍴⍳4)~2 !!! RANK ERROR

_hook←{⍵⍶⍹⍵;⍺⍶⍹⍵}
⍝ (+÷)\3 7 16 ¯294 ←→ (3 3.142857142857143 3.1415929203539825 3.141592653921421)
⍝ (=⌊)123 ←→ 1
⍝ (=⌊)123.4 ←→ 0
⍝ (÷⍟)1000 ←→ 144.76482730108395

⊃←{
  0=⍴⍴⍵:↑⍵
  0=×/⍴⍵:⍵
  shape←⍴⍵ ⋄ ⍵←,⍵
  r←⌈/≢¨shapes←⍴¨⍵ ⍝ maximum rank of all shapes
  max←↑⌈/shapes←(⍴ ↓ (r⍴1)∘,)¨shapes ⍝ maximum shape of rank adjusted shapes
  (shape,max)⍴↑⍪/shapes{max↑⍺⍴⍵}¨⍵
  ;
  1<⍴⍴⍺:↗'RANK ERROR'
  x←⍵
  {
    1<⍴⍴⍵:↗'RANK ERROR'
    ⍵←,⍵
    (⍴⍵)≠⍴⍴x:↗'RANK ERROR'
    ∨/⍵≥⍴x:↗'INDEX ERROR'
    x←⊃⍵⌷x
  }¨⍺
  x
}
⍝ ⊃3            ←→ 3
⍝ ⊃(1 2)(3 4)   ←→ 2 2⍴1 2 3 4
⍝ ⊃(1 2)(3 4 5) ←→ 2 3⍴1 2 0 3 4 5
⍝ ⊃1 2          ←→ 1 2
⍝ ⊃(1 2)3       ←→ 2 2⍴1 2 3 0
⍝ ⊃1(2 3)       ←→ 2 2⍴1 0 2 3
⍝ ⊃2 2⍴1(1 1 2⍴3 4)(5 6)(2 0⍴0) ←→ 2 2 1 2 2⍴1 0 0 0 3 4 0 0 5 6 0 0 0 0 0 0
⍝ ⊃⍬            ←→ ⍬
⍝ ⊃2 3 0⍴0      ←→ 2 3 0⍴0
⍝ ⍬⊃3               ←→ 3
⍝ 2⊃'PICK'          ←→ 'C'
⍝ (⊂1 0)⊃2 2⍴'ABCD' ←→ 'C'
⍝ 1⊃'foo' 'bar'     ←→ 'bar'
⍝ 1 2⊃'foo' 'bar'   ←→ 'r'
⍝ (2 2⍴0)⊃1 2       !!! RANK ERROR
⍝ (⊂2 1⍴0)⊃2 2⍴0    !!! RANK ERROR
⍝ (⊂2 2⍴0)⊃1 2      !!! RANK ERROR
⍝ (⊂2 2)⊃1 2        !!! RANK ERROR
⍝ (⊂0 2)⊃2 2⍴'ABCD' !!! INDEX ERROR

⊂←⊂⍠{ ⍝ ⍺⊂⍵ definition
  1<⍴⍴⍺:↗'RANK ERROR' ⋄ 1≠⍴⍴⍵:↗'NONCE ERROR' ⋄ ⍺←,⍺=0
  keep←~1 1⍷⍺ ⋄ sel←keep/⍺ ⋄ dat←keep/⍵
  {1=1↑sel:{sel←1↓sel ⋄ dat←1↓dat}⍬}⍬
  {1=¯1↑sel:{sel←¯1↓sel ⋄ dat←¯1↓dat}⍬}⍬
  sel←(⍴sel),⍨sel/⍳⍴sel ⋄ drop←0
  sel{∆←drop↓⍺↑⍵ ⋄ drop←⍺+1 ⋄ ∆}¨⊂dat
}
⍝ a←' this is a test ' ⋄ (a≠' ')⊂a ←→ 'this' 'is' (,'a') 'test'

↓←{
  0=⍴⍴⍵:⍵ ⋄ ⊂[¯1+⍴⍴⍵]⍵
  ;
  1<⍴⍴⍺:↗'RANK ERROR'
  a←,⍺
  ⍵←{0=⍴⍴⍵:((⍴a)⍴1)⍴⍵⋄⍵}⍵
  (⍴a)>⍴⍴⍵:↗'RANK ERROR'
  a←(⍴⍴⍵)↑a
  a←((a>0)×0⌊a-⍴⍵)+(a≤0)×0⌈a+⍴⍵
  a↑⍵
}
⍝ ↓1 2 3 ←→ ⊂1 2 3
⍝ ↓(1 2)(3 4) ←→ ⊂(1 2)(3 4)
⍝ ↓2 2⍴⍳4 ←→ (0 1)(2 3)
⍝ ↓2 3 4⍴⍳24 ←→ 2 3⍴(0 1 2 3)(4 5 6 7)(8 9 10 11)(12 13 14 15)(16 17 18 19)(20 21 22 23)
⍝ 4↓'OVERBOARD'         ←→ 'BOARD'
⍝ ¯5↓'OVERBOARD'        ←→ 'OVER'
⍝ ⍴10↓'OVERBOARD'       ←→ ,0
⍝ 0 ¯2↓3 3⍴'ONEFATFLY'  ←→ 3 1⍴'OFF'
⍝ ¯2 ¯1↓3 3⍴'ONEFATFLY' ←→ 1 2⍴'ON'
⍝ 1↓3 3⍴'ONEFATFLY'     ←→ 2 3⍴'FATFLY'
⍝ ⍬↓3 3⍴⍳9              ←→ 3 3⍴⍳9
⍝ 1 1↓2 3 4⍴"ABCDEFGHIJKLMNOPQRSTUVWXYZ"   ←→ 1 2 4⍴'QRSTUVWX'
⍝ ¯1 ¯1↓2 3 4⍴"ABCDEFGHIJKLMNOPQRSTUVWXYZ" ←→ 1 2 4⍴'ABCDEFGH'
⍝ 1↓0                   ←→ ⍬
⍝ 0 1↓2                 ←→ 1 0⍴0
⍝ 1 2↓3                 ←→ 0 0⍴0
⍝ ⍬↓0                   ←→ 0

⍪←{(≢⍵)(×/1↓⍴⍵)⍴⍵; ⍺,[0]⍵}
⍝ ⍪2 3 4 ←→ 3 1⍴2 3 4
⍝ ⍪0 ←→ 1 1⍴0
⍝ ⍪2 2⍴2 3 4 5 ←→ 2 2⍴2 3 4 5
⍝ ⍴⍪2 3⍴⍳6 ←→ 2 3
⍝ ⍴⍪2 3 4⍴⍳24 ←→ 2 12
⍝ (2 3⍴⍳6)⍪9 ←→ 3 3⍴(0 1 2 3 4 5 9 9 9)
⍝ 1⍪2 ←→ 1 2

⊢←{⍵}
⍝ 123⊢456 ←→ 456
⍝ ⊢456 ←→ 456

⊣←{;⍺}
⍝ 123⊣456 ←→ 123
⍝ ⊣456 ←→ ⍬

≢←{⍬⍴(⍴⍵),1; ~⍺≡⍵}
⍝ ≢0 ←→ 1
⍝ ≢0 0 0 ←→ 3
⍝ ≢⍬ ←→ 0
⍝ ≢2 3⍴⍳6 ←→ 2
⍝ 3≢3 ←→ 0

,←{(×/⍴⍵)⍴⍵}⍠,
⍝ ,2 3 4⍴'abcdefghijklmnopqrstuvwx' ←→ 'abcdefghijklmnopqrstuvwx'
⍝ ,123 ←→ 1⍴123

⌹←{
  norm←{(⍵+.×+⍵)*0.5}

  QR←{ ⍝ QR decomposition
    n←(⍴⍵)[1]
    1≥n:{t←norm,⍵ ⋄ (⍵÷t)(⍪t)}⍵
    m←⌈n÷2
    a0←((1↑⍴⍵),m)↑⍵
    a1←(0,m)↓⍵
    (q0 r0)←∇a0
    c←(+⍉q0)+.×a1
    (q1 r1)←∇a1-q0+.×c
    (q0,q1)((r0,c)⍪((⌊n÷2),-n)↑r1)
  }

  Rinv←{ ⍝ Inverse of an upper triangular matrix
    1=n←1↑⍴⍵:÷⍵
    m←⌈n÷2
    ai←∇(m,m)↑⍵
    di←∇(m,m)↓⍵
    b←(m,m-n)↑⍵
    bx←-ai+.×b+.×di
    (ai,bx)⍪((⌊n÷2),-n)↑di
  }

  0=⍴⍴⍵:÷⍵
  1=⍴⍴⍵:,∇⍪⍵
  2≠⍴⍴⍵:↗'RANK ERROR'
  0∊≥/⍴⍵:↗'LENGTH ERROR'
  (Q R)←QR ⍵
  (Rinv R)+.×+⍉Q
  ;
  (⌹⍵)+.×⍺
}
⍝ ⌹2 ←→ .5
⍝ ⌹2 2⍴4 3 3 2 ←→ 2 2⍴¯2 3 3 ¯4
⍝ (4 4⍴12 1 4 10 ¯6 ¯5 4 7 ¯4 9 3 4 ¯2 ¯6 7 7)⌹93 81 93.5 120.5 ←→
⍝ ... .0003898888816687221 ¯.005029566573526544 .04730651764247189 .0705568912859835
⍝ ⌹2 2 2⍴⍳8 !!! RANK ERROR
⍝ ⌹2 3⍴⍳6 !!! LENGTH ERROR

⍨←{⍵⍶⍵;⍵⍶⍺}
⍝ 17-⍨23 ←→ 6
⍝ 7⍴⍨2 3 ←→ 2 3⍴7
⍝ +⍨2    ←→ 4
⍝ -⍨123  ←→ 0

⍝ Aliases:
−←- ⍝ U+2212 MINUS SIGN        for U+002D HYPHEN-MINUS
⋆←* ⍝ U+22C6 STAR OPERATOR     for U+002A ASTERISK
∣←| ⍝ U+2223 DIVIDES           for U+007C VERTICAL LINE
^←∧ ⍝ U+005E CIRCUMFLEX ACCENT for U+2227 LOGICAL AND
∈←∊ ⍝ U+2208 ELEMENT OF        for U+220A SMALL ELEMENT OF


Fix Boolean option for _.Select.(Selected Disabled)

:class Select : #._html.select
⍝ Description:: Dyalog Enhanced HTML select
⍝ Constructor:: [options [[selected] [[disabled] [prompt]]]]
⍝ options   - vector of options or 2 column matrix of displayed[;1] and returned[;2] values
⍝ selected  - Boolean or integer array indicating pre-selected options(s)
⍝ disabled  - Boolean or integer array indicating disabled options(s)
⍝ prompt    - first item to display (has no value) (default '[Select]')
⍝
⍝ Public Fields::
⍝ Options   - vector of options or 2 column matrix of displayed[;1] and returned[;2] values
⍝ Selected  - Boolean or integer array indicating pre-selected options(s)
⍝ Disabled  - Boolean or integer array indicating disabled options(s)
⍝ Prompt      - first item to display (has no value) (default '[Select]')
⍝
⍝ Examples::
⍝ Select  ('Choice 1' 'Choice 2' 'Choice 3')
⍝ Select  (3 2⍴'One' 'c1' 'Two' 'c2' 'Three' 'c3')
⍝ Select ((3 2⍴'One' 'c1' 'Two' 'c2' 'Three' 'c3') 2) ⍝ second item is selected
⍝ Select ((3 2⍴'One' 'c1' 'Two' 'c2' 'Three' 'c3') (0 1 0)) ⍝ second item is selected
⍝ Select ((3 2⍴'One' 'c1' 'Two' 'c2' 'Three' 'c3') 2 3 'Pick One') ⍝ second item is selected, third item is disabled
⍝ Select ((3 2⍴'One' 'c1' 'Two' 'c2' 'Three' 'c3') (2 3) 1) ⍝ second and third items are selected, first item is disabled

    :field public Options←0 2⍴⊂''     ⍝ vector or matrix [;1] display, [;2] value
    :field public Selected←⍬          ⍝ either Boolean integer vector indicating
    :field public Prompt←'[Select]'   ⍝ character vector "Prompt" choice - the first choice on the list and does not have a value
    :field public Disabled←⍬          ⍝ either Boolean integer vector indicating

    ∇ make
      :Access public
      :Implements constructor
    ∇

    ∇ make1 args;attr
      :Access public
      :Implements constructor
      args←eis args
      :If (|≡args)∊0 1 2
      :AndIf ~0∊⍴⊃args
          args←,⊂args
      :EndIf
      (Options Selected Disabled Prompt)←args defaultArgs Options Selected Disabled Prompt
    ∇

    ∇ r←Render;opts;sel
      :Access public

      Content←opts←''
      SetInputName
      Add BuildOptions(Options Selected Disabled)

      r←⎕BASE.Render
    ∇

    ∇ r←BuildOptions(opts sel dis);v
      r←''
      :If ~0∊⍴opts
          opts←eis opts
          :If 1=⍴⍴opts
              opts←opts,⍪opts
          :EndIf
      :EndIf

      v←⍳⍬⍴⍴opts
      (sel dis)←v∘∊∘{∧/⍵∊0 1:⍵/⍳⍴⍵ ⋄ ⍵}∘,¨sel dis

      :If 1<+/sel                         ⍝ if we have multiple items selected
      :AndIf 0∊⍴⊃Attrs[⊂'multiple']       ⍝ and the multiple attribute is not set
          Attrs[⊂'multiple']←⊂'multiple'  ⍝ then set it
      :EndIf

      :If ~0∊⍴Prompt
      :AndIf 0∊⍴⊃Attrs[⊂'multiple']  ⍝ prompt makes no sense if multiple selections are allowed
          r,←('disabled="disabled" selected="selected"'New #._html.option Prompt).Render
      :EndIf

      r,←FormatOptions(opts sel dis)
    ∇

    ∇ r←FormatOptions(opts sel dis);o;s;d
      :Access Public Shared
      r←''
      :If 1=⍴⍴opts ⋄ opts←opts,⍪opts ⋄ :EndIf
      :For o s d :InEach (↓opts)sel dis
          r,←'<option',({⍵ ine' value="',(HtmlSafeText ⍵),'"'}2⊃o),(s/' selected="selected"'),(d/' disabled="disabled"'),'>',(1⊃o),'</option>'
      :EndFor
    ∇

    ∇ r←{selector}ReplaceOptions args;sel;opts;dis
⍝     Replaces select elements options - used by callback functions
⍝ Ex: r←Execute ReplaceOptions ('New Option 1' 'New Option 2') 1
⍝     arg = options [[selected] [disabled]]
      :Access public
      :If 0=⎕NC'selector' ⋄ selector←'#',id ⋄ :EndIf
      args←eis args
      :If (|≡args)∊0 1 2
      :AndIf ~0∊⍴⊃args
          args←,⊂args
      :EndIf
      (opts sel dis)←args defaultArgs Options ⍬ ⍬
      r←selector #.JQ.Replace BuildOptions(opts sel dis)
    ∇
:endclass

:class Select : #._html.select
⍝ Description:: Dyalog Enhanced HTML select
⍝ Constructor:: [options [[selected] [[disabled] [prompt]]]]
⍝ options   - vector of options or 2 column matrix of displayed[;1] and returned[;2] values
⍝ selected  - Boolean or integer array indicating pre-selected options(s)
⍝ disabled  - Boolean or integer array indicating disabled options(s)
⍝ prompt    - first item to display (has no value) (default '[Select]')
⍝
⍝ Public Fields::
⍝ Options   - vector of options or 2 column matrix of displayed[;1] and returned[;2] values
⍝ Selected  - Boolean or integer array indicating pre-selected options(s)
⍝ Disabled  - Boolean or integer array indicating disabled options(s)
⍝ Prompt      - first item to display (has no value) (default '[Select]')
⍝
⍝ Examples::
⍝ Select  ('Choice 1' 'Choice 2' 'Choice 3')
⍝ Select  (3 2⍴'One' 'c1' 'Two' 'c2' 'Three' 'c3')
⍝ Select ((3 2⍴'One' 'c1' 'Two' 'c2' 'Three' 'c3') 2) ⍝ second item is selected
⍝ Select ((3 2⍴'One' 'c1' 'Two' 'c2' 'Three' 'c3') (0 1 0)) ⍝ second item is selected
⍝ Select ((3 2⍴'One' 'c1' 'Two' 'c2' 'Three' 'c3') 2 3 'Pick One') ⍝ second item is selected, third item is disabled
⍝ Select ((3 2⍴'One' 'c1' 'Two' 'c2' 'Three' 'c3') (2 3) 1) ⍝ second and third items are selected, first item is disabled

    :field public Options←0 2⍴⊂''     ⍝ vector or matrix [;1] display, [;2] value
    :field public Selected←⍬          ⍝ either Boolean integer vector indicating
    :field public Prompt←'[Select]'   ⍝ character vector "Prompt" choice - the first choice on the list and does not have a value
    :field public Disabled←⍬          ⍝ either Boolean integer vector indicating

    ∇ make
      :Access public
      :Implements constructor
    ∇

    ∇ make1 args;attr
      :Access public
      :Implements constructor
      args←eis args
      :If (|≡args)∊0 1 2
      :AndIf ~0∊⍴⊃args
          args←,⊂args
      :EndIf
      (Options Selected Disabled Prompt)←args defaultArgs Options Selected Disabled Prompt
    ∇

    ∇ r←Render;opts;sel
      :Access public

      Content←opts←''
      SetInputName
      Add BuildOptions(Options Selected Disabled)

      r←⎕BASE.Render
    ∇

    ∇ r←BuildOptions(opts sel dis);v
      r←''
      :If ~0∊⍴opts
          opts←eis opts
          :If 1=⍴⍴opts
              opts←opts,⍪opts
          :EndIf
      :EndIf

      v←⍳⍬⍴⍴opts
      (sel dis)←v∘∊¨sel dis

      :If 1<+/sel                         ⍝ if we have multiple items selected
      :AndIf 0∊⍴⊃Attrs[⊂'multiple']       ⍝ and the multiple attribute is not set
          Attrs[⊂'multiple']←⊂'multiple'  ⍝ then set it
      :EndIf

      :If ~0∊⍴Prompt
      :AndIf 0∊⍴⊃Attrs[⊂'multiple']  ⍝ prompt makes no sense if multiple selections are allowed
          r,←('disabled="disabled" selected="selected"'New #._html.option Prompt).Render
      :EndIf

      r,←FormatOptions(opts sel dis)
    ∇

    ∇ r←FormatOptions(opts sel dis);o;s;d
      :Access Public Shared
      r←''
      :If 1=⍴⍴opts ⋄ opts←opts,⍪opts ⋄ :EndIf
      :For o s d :InEach (↓opts)sel dis
          r,←'<option',({⍵ ine' value="',(HtmlSafeText ⍵),'"'}2⊃o),(s/' selected="selected"'),(d/' disabled="disabled"'),'>',(1⊃o),'</option>'
      :EndFor
    ∇

    ∇ r←{selector}ReplaceOptions args;sel;opts;dis
⍝     Replaces select elements options - used by callback functions
⍝ Ex: r←Execute ReplaceOptions ('New Option 1' 'New Option 2') 1
⍝     arg = options [[selected] [disabled]]
      :Access public
      :If 0=⎕NC'selector' ⋄ selector←'#',id ⋄ :EndIf
      args←eis args
      :If (|≡args)∊0 1 2
      :AndIf ~0∊⍴⊃args
          args←,⊂args
      :EndIf
      (opts sel dis)←args defaultArgs Options ⍬ ⍬
      r←selector #.JQ.Replace BuildOptions(opts sel dis)
    ∇
:endclass


TogglePopup

TogglePopup

Fiddling

:Class TogglePopup : MiPageSample ⍝ EAWC : MiPage : HtmlPage
    ∇ Render;ct;CliDa;h
      :Access Public
      :If _Request.IsPost
          Add'You clicked me!'
      :Else
          ct←Add Form
          ct.Add'Click ',(bn←⎕NEW ejToggleButton),' to toggle status.'
          ⍝bn.On'click' 'myCallback'('mybutt' 'ejModel')
          Add ct
          Wp←Add ejWaitingPopup'#mywp' 'Waiting...'
          'showOnInit'Wp.Option _false
          h←Add Handler
          CliDa←'MyDa' '#mywp' 'ejModel' 'showOnInit'
          h.(Selectors Events Callback ClientData)←'bn' 'click' 'myCallback'CliDa
      :EndIf
    ∇

    ∇ r←myCallback;active
      :Access public
      r←⍬
      active←_true≡_PageData.MyDa
      'showOnInit'Wp.Update _true _false[1+active] ⍝ Need Brian's post-render option setting mechanism
    ∇

:EndClass

:Class TogglePopup : MiPageSample ⍝ EAWC : MiPage : HtmlPage
    ∇ Render;ct
      :Access Public
      :If _Request.IsPost
          Add'You clicked me!'
      :Else
          ct←⎕NEW p
          ct.Add'Click ',(bn←⎕NEW ejToggleButton),' to toggle status.'
          bn.On'click' 'myCallback'('mybutt' 'ejModel')
          Add ct
          Wp←Add ejWaitingPopup'' 'Waiting...'
          'showOnInit'Wp.Option _false
      :EndIf
    ∇

    ∇ r←myCallback;active
      :Access public
      r←⍬
      active←_true≡_PageData.mybutt.model.showOnInit
      'showOnInit'Wp.Update _true _false[1+active] ⍝ Need Brian's post-render option setting mechanism
    ∇

:EndClass


now it works

now it works

:Class Menuexample2 : MiPageSample
⍝ Demonstrate the SyncFusion ejMenu widget

    ∇ r←Render
      :Access Public
      'width'(Add _SF.ejMenu fixExample).Option 632
    ∇

    ∇ r3←fixExample;tmp;b;i
      :Access public shared
      tmp←⎕XML∊r3←example
      tmp[;3]←1⌽tmp[;3]
      i←b/⍳⍴b←tmp[;2]∊⊂,'a'
      tmp[i-1;4]⍪←tmp[i;4]
      tmp←(b⍱tmp[;2]∊⊂'ul')⌿tmp
      tmp[;1]÷←2
      r3←↓⍉tmp[;1 3],⊂''
    ∇

    ∇ lines←example
      :Access public shared
     L10:lines←1↓¨1↓L10↓⎕NR'example'
⍝

⍝  Products
⍝

⍝
ASP.NET

⍝
ASP.NET MVC

⍝
Mobile MVC

⍝
Silverlight

⍝
Windows Forms

⍝
Windows Phone

⍝
WinRT (XMAL)

⍝
WPF

⍝
Orubase Studio

⍝
Metro Studio

⍝
What's New
⍝

⍝
WinRT (XMAL)

⍝
WPF

⍝
Silverlight

⍝
Windows Forms

⍝
Windows Phone

⍝
ASP.NET MVC

⍝
ASP.NET

⍝

⍝

⍝

⍝

⍝

⍝  Support
⍝

⍝
Direct-Trac Support

⍝
Community Forums

⍝
Knowledge Base

⍝
Online Documentation

⍝
Services
⍝

⍝
Consulting

⍝
Training

⍝

⍝

⍝

⍝

⍝

⍝  Purchase

⍝

⍝  Downloads
⍝

⍝
Evaluation

⍝
Free E-Books

⍝
Metro Studio

⍝
Latest Version

⍝
Version History

⍝

⍝

⍝

⍝  Resources
⍝

⍝
Technology Resource Portal
⍝

⍝
E-books

⍝
White Papers

⍝

⍝

⍝
Case Studies

⍝
Bouchers + Datasheets

⍝
FAQ

⍝

⍝

⍝

⍝  Company
⍝

⍝
About Us
⍝

⍝
More About Us

⍝
Management

⍝
News + Events

⍝
Customer Quotes

⍝
Customer Lists

⍝
Case Studies

⍝
Awards

⍝
Media Kit

⍝

⍝

⍝
Company Blog

⍝
Technical Blog

⍝
Newsletter

⍝
Partners
⍝

⍝
Technology Partners

⍝
Training Partners

⍝
Consulting Partners

⍝

⍝

⍝
Locations
⍝

⍝
RDU

⍝
Chennai

⍝

⍝

⍝
Contact Us

⍝
Careers

⍝

⍝

    ∇

:EndClass
⍝)(!example!!0 0 0 0 0 0 0
⍝)(!fixExample!!0 0 0 0 0 0 0

:Class Menuexample2 : MiPageSample
⍝ Demonstrate the SyncFusion ejMenu widget

    ∇ r←Render
      :Access Public
      'width'(Add _SF.ejMenu fixExample).Option 632
    ∇

    ∇ r3←fixExample;tmp;b;i
      :Access public shared
      tmp←⎕XML example
      tmp[;3]←1⌽tmp[;3]
      i←b/⍳⍴b←tmp[;2]∊⊂,'a'
      tmp[i-1;4]⍪←tmp[i;4]
      tmp←(b⍱tmp[;2]∊⊂'ul')⌿tmp
      tmp[;1]÷←2
      r3←↓⍉tmp[;1 3],⊂''
    ∇

    ∇ lines←example
      :Access public shared
      lines←ScriptFollows
⍝

⍝  Products
⍝

⍝
ASP.NET

⍝
ASP.NET MVC

⍝
Mobile MVC

⍝
Silverlight

⍝
Windows Forms

⍝
Windows Phone

⍝
WinRT (XMAL)

⍝
WPF

⍝
Orubase Studio

⍝
Metro Studio

⍝
What's New
⍝

⍝
WinRT (XMAL)

⍝
WPF

⍝
Silverlight

⍝
Windows Forms

⍝
Windows Phone

⍝
ASP.NET MVC

⍝
ASP.NET

⍝

⍝

⍝

⍝

⍝

⍝  Support
⍝

⍝
Direct-Trac Support

⍝
Community Forums

⍝
Knowledge Base

⍝
Online Documentation

⍝
Services
⍝

⍝
Consulting

⍝
Training

⍝

⍝

⍝

⍝

⍝

⍝  Purchase

⍝

⍝  Downloads
⍝

⍝
Evaluation

⍝
Free E-Books

⍝
Metro Studio

⍝
Latest Version

⍝
Version History

⍝

⍝

⍝

⍝  Resources
⍝

⍝
Technology Resource Portal
⍝

⍝
E-books

⍝
White Papers

⍝

⍝

⍝
Case Studies

⍝
Bouchers + Datasheets

⍝
FAQ

⍝

⍝

⍝

⍝  Company
⍝

⍝
About Us
⍝

⍝
More About Us

⍝
Management

⍝
News + Events

⍝
Customer Quotes

⍝
Customer Lists

⍝
Case Studies

⍝
Awards

⍝
Media Kit

⍝

⍝

⍝
Company Blog

⍝
Technical Blog

⍝
Newsletter

⍝
Partners
⍝

⍝
Technology Partners

⍝
Training Partners

⍝
Consulting Partners

⍝

⍝

⍝
Locations
⍝

⍝
RDU

⍝
Chennai

⍝

⍝

⍝
Contact Us

⍝
Careers

⍝

⍝

    ∇

:EndClass