@@ -876,7 +876,7 @@ function web_editor(config) {
876876 $ ( '.fs-file-list table tbody' ) . append (
877877 '<tr><td>' + name + '</td>' +
878878 '<td>' + ( micropythonFs . size ( filename ) / 1024 ) . toFixed ( 2 ) + ' Kb</td>' +
879- '<td><button id="' + pseudoUniqueId + '_remove" class="action save-button remove ' + disabled + '" title=' + loadStrings [ "remove-but" ] + '><i class="fa fa-trash"></i></button>' +
879+ '<td><button id="' + pseudoUniqueId + '_remove" class="action save-button remove ' + disabled + '" title=' + loadStrings [ "remove-but" ] + ' ' + disabled + '><i class="fa fa-trash"></i></button>' +
880880 '<button id="' + pseudoUniqueId + '_save" class="action save-button save" title=' + loadStrings [ "save-but" ] + '><i class="fa fa-download"></i></button></td></tr>'
881881 ) ;
882882 $ ( '#' + pseudoUniqueId + '_save' ) . click ( function ( e ) {
@@ -918,6 +918,39 @@ function web_editor(config) {
918918 return fullHex ;
919919 }
920920
921+ // Trap focus in modal and pass focus to first actionable element
922+ function focusModal ( ) {
923+ document . querySelector ( 'body > :not(.vex)' ) . setAttribute ( 'aria-hidden' , true ) ;
924+ var dialog = document . querySelector ( '.modal-div' ) ;
925+ var focusableEls = dialog . querySelectorAll ( 'a[href]:not([disabled]), button:not([disabled]), textarea:not([disabled]), input[type="text"]:not([disabled]), input[type="radio"]:not([disabled]), input[type="checkbox"]:not([disabled]), select:not([disabled])' ) ;
926+ $ ( focusableEls ) . each ( function ( ) {
927+ $ ( this ) . attr ( 'tabindex' , '0' ) ;
928+ } ) ;
929+ dialog . focus ( ) ;
930+ dialog . onkeydown = function ( event ) {
931+ if ( event . which == 9 ) {
932+ // if tab key is pressed
933+ var focusedEl = document . activeElement ;
934+ var numberOfFocusableEls = focusableEls . length ;
935+ var focusedElIndex = Array . prototype . indexOf . call ( focusableEls , focusedEl ) ;
936+
937+ if ( event . which == 16 ) {
938+ // if focused on first item and user shift-tabs back, go to the last focusable item
939+ if ( focusedElIndex == 0 ) {
940+ focusableEls . item ( numberOfFocusableEls - 1 ) . focus ( ) ;
941+ event . preventDefault ( ) ;
942+ }
943+ } else {
944+ // if focused on the last item and user tabs forward, go to the first focusable item
945+ if ( focusedElIndex == numberOfFocusableEls - 1 ) {
946+ focusableEls [ 0 ] . focus ( ) ;
947+ event . preventDefault ( ) ;
948+ }
949+ }
950+ }
951+ } ;
952+ }
953+
921954 // This function describes what to do when the download button is clicked.
922955 function doDownload ( ) {
923956 try {
@@ -945,7 +978,6 @@ function web_editor(config) {
945978 modalMsg ( config [ 'translate' ] [ 'load' ] [ 'invalid-file-title' ] , config [ 'translate' ] [ 'load' ] [ 'extension-warning' ] , "" ) ;
946979 }
947980 }
948-
949981 // Describes what to do when the save/load button is clicked.
950982 function doFiles ( ) {
951983 var template = $ ( '#files-template' ) . html ( ) ;
@@ -955,6 +987,7 @@ function web_editor(config) {
955987 vex . open ( {
956988 content : Mustache . render ( template , loadStrings ) ,
957989 afterOpen : function ( vexContent ) {
990+ focusModal ( ) ;
958991 $ ( "#show-files" ) . attr ( "title" , loadStrings [ "show-files" ] + " (" + micropythonFs . ls ( ) . length + ")" ) ;
959992 document . getElementById ( "show-files" ) . innerHTML = loadStrings [ "show-files" ] + " (" + micropythonFs . ls ( ) . length + ") <i class='fa fa-caret-down'>" ;
960993 $ ( '#save-hex' ) . click ( function ( ) {
@@ -968,12 +1001,14 @@ function web_editor(config) {
9681001 }
9691002 downloadFileFromFilesystem ( 'main.py' ) ;
9701003 } ) ;
971- $ ( "#files-expand-help " ) . click ( function ( ) {
1004+ $ ( "#expandHelpPara " ) . click ( function ( ) {
9721005 if ( $ ( "#fileHelpPara" ) . css ( "display" ) == "none" ) {
9731006 $ ( "#fileHelpPara" ) . show ( ) ;
1007+ $ ( "#expandHelpPara" ) . attr ( "aria-expanded" , "true" ) ;
9741008 $ ( "#addFile" ) . css ( "margin-bottom" , "10px" ) ;
9751009 } else {
9761010 $ ( "#fileHelpPara" ) . hide ( ) ;
1011+ $ ( "#expandHelpPara" ) . attr ( "aria-expanded" , "false" ) ;
9771012 $ ( "#addFile" ) . css ( "margin-bottom" , "22px" ) ;
9781013 }
9791014 } ) ;
@@ -982,11 +1017,13 @@ function web_editor(config) {
9821017 if ( content . style . maxHeight ) {
9831018 content . style . maxHeight = null ;
9841019 $ ( "#hide-files" ) . attr ( "id" , "show-files" ) ;
1020+ $ ( "#show-files" ) . attr ( "aria-expanded" , "false" ) ;
9851021 $ ( "#show-files" ) . attr ( "title" , loadStrings [ "show-files" ] + " (" + micropythonFs . ls ( ) . length + ")" ) ;
9861022 document . getElementById ( "show-files" ) . innerHTML = loadStrings [ "show-files" ] + " (" + micropythonFs . ls ( ) . length + ") <i class='fa fa-caret-down'>" ;
9871023 } else {
9881024 content . style . maxHeight = content . scrollHeight + "px" ;
9891025 $ ( "#show-files" ) . attr ( "id" , "hide-files" ) ;
1026+ $ ( "#hide-files" ) . attr ( "aria-expanded" , "true" ) ;
9901027 $ ( "#hide-files" ) . attr ( "title" , loadStrings [ "hide-files" ] ) ;
9911028 document . getElementById ( "hide-files" ) . innerHTML = loadStrings [ "hide-files" ] + " <i class='fa fa-caret-up'>" ;
9921029 }
@@ -1136,6 +1173,7 @@ function web_editor(config) {
11361173 vex . open ( {
11371174 content : Mustache . render ( template , context ) ,
11381175 afterOpen : function ( vexContent ) {
1176+ focusModal ( ) ;
11391177 $ ( vexContent ) . find ( '.snippet-selection' ) . click ( function ( e ) {
11401178 var snippet_name = $ ( this ) . find ( '.snippet-name' ) . text ( ) ;
11411179 EDITOR . triggerSnippet ( snippet_name ) ;
@@ -1680,7 +1718,7 @@ function web_editor(config) {
16801718 var overlayContainer = "#modal-msg-overlay-container" ;
16811719 $ ( overlayContainer ) . css ( "display" , "block" ) ;
16821720 $ ( "#modal-msg-title" ) . text ( title ) ;
1683- $ ( "#modal-msg-content" ) . html ( content ) ;
1721+ $ ( "#modal-msg-content" ) . html ( content ) ;
16841722 var modalLinks = [ ] ;
16851723 if ( links ) {
16861724 Object . keys ( links ) . forEach ( function ( key ) {
0 commit comments