From 3a0be85aebd395c0c6d62b1e77407ff13b66600d Mon Sep 17 00:00:00 2001 From: rwijaya Date: Tue, 10 Dec 2013 15:48:48 +0800 Subject: [PATCH] MDL-42104: trap tab focus within dialog modal, for filepicker and filemanager Conflicts: lib/form/filemanager.js --- lib/form/filemanager.js | 34 +++++++++++++++++++++++++++++++++ repository/filepicker.js | 41 +++++++++++++++++++++++++++++++++++++++- 2 files changed, 74 insertions(+), 1 deletion(-) diff --git a/lib/form/filemanager.js b/lib/form/filemanager.js index 3e00c704f83..8af21f8d7c0 100644 --- a/lib/form/filemanager.js +++ b/lib/form/filemanager.js @@ -255,6 +255,7 @@ M.form_filemanager.init = function(Y, options) { this.msg_dlg_node.one('.fp-msg-butok').on('click', function(e) { e.preventDefault(); this.msg_dlg.hide(); + this.detachKeyDelegation(this.msg_dlg); }, this); } @@ -262,6 +263,7 @@ M.form_filemanager.init = function(Y, options) { this.msg_dlg_node.removeClass('fp-msg-info').removeClass('fp-msg-error').addClass('fp-msg-'+type) this.msg_dlg_node.one('.fp-msg-text').setContent(Y.Escape.html(msg)); this.msg_dlg.show(); + this.keyDelegation(this.msg_dlg); }, is_disabled: function() { return this.filemanager.ancestor('.fitem.disabled') != null; @@ -357,6 +359,7 @@ M.form_filemanager.init = function(Y, options) { } Y.one('#fm-newname-'+scope.client_id).set('value', foldername); Y.bind(validate_folder_name, this)(); + this.keyDelegation(this.mkdir_dialog); Y.one('#fm-newname-'+scope.client_id).focus().select(); Y.all('#fm-curpath-'+scope.client_id).setContent(this.currentpath); }, this); @@ -712,6 +715,7 @@ M.form_filemanager.init = function(Y, options) { if (!action) { // no changes this.selectui.hide(); + this.detachKeyDelegation(this.selectui); return; } selectnode.addClass('loading'); @@ -757,6 +761,7 @@ M.form_filemanager.init = function(Y, options) { var dlgopt = this.confirm_dlg.dlgopt; ev.preventDefault(); this.confirm_dlg.hide(); + this.detachKeyDelegation(this.confirm_dlg); if (dlgopt.callback) { if (dlgopt.callbackargs) { dlgopt.callback.apply(dlgopt.scope || this, dlgopt.callbackargs); @@ -768,6 +773,7 @@ M.form_filemanager.init = function(Y, options) { var handle_cancel = function(ev) { ev.preventDefault(); this.confirm_dlg.hide(); + this.detachKeyDelegation(this.confirm_dlg); } node.one('.fp-dlg-butconfirm').on('click', handle_confirm, this); node.one('.fp-dlg-butcancel').on('click', handle_cancel, this); @@ -775,6 +781,7 @@ M.form_filemanager.init = function(Y, options) { this.confirm_dlg.dlgopt = dialog_options; this.confirm_dlg_node.one('.fp-dlg-text').setContent(dialog_options.message); this.confirm_dlg.show(); + this.keyDelegation(this.confirm_dlg); }, setup_select_file: function() { var selectnode = this.selectnode; @@ -911,6 +918,7 @@ M.form_filemanager.init = function(Y, options) { e.preventDefault(); // TODO if changed asked to confirm, the same with close button this.selectui.hide(); + this.detachKeyDelegation(this.selectui); }, this); }, get_parent_folder_name: function(node) { @@ -1029,6 +1037,7 @@ M.form_filemanager.init = function(Y, options) { Y.one('#fm-dialog-label_'+selectnode.get('id')).setContent(Y.Escape.html(M.str.moodle.edit+' '+nodename)); // show panel this.selectui.show(); + this.keyDelegation(this.selectui); Y.one('#'+selectnode.get('id')).focus(); }, render: function() { @@ -1044,6 +1053,31 @@ M.form_filemanager.init = function(Y, options) { } } return false; + }, + keyDelegation: function (dialog) { + var bb = dialog.get('boundingBox'); + var can_receive_focus_selector = 'input:not([type="hidden"]), a[href], button, textarea, select'; + bb.delegate('key', function(e) { + var target = e.target; + var firstitem = bb.one(can_receive_focus_selector); + var lastitem = bb.all(can_receive_focus_selector).pop(); + + if (e.shiftKey) { + if (target === firstitem) { + lastitem.focus(); + e.preventDefault(); + } + } else { + if (target === lastitem) { + firstitem.focus(); + e.preventDefault(); + } + } + }, 'down:9', can_receive_focus_selector, this); + }, + detachKeyDelegation: function(dialog) { + var bb = dialog.get('boundingBox'); + bb.detach('key', this.keyDelegation); } }); diff --git a/repository/filepicker.js b/repository/filepicker.js index 56065ec8986..755c6aa420b 100644 --- a/repository/filepicker.js +++ b/repository/filepicker.js @@ -710,7 +710,9 @@ M.core_filepicker.init = function(Y, options) { } }, false); this.process_dlg.hide(); + this.deleteKeyDelegation(this.process_dlg); this.selectui.hide(); + this.deleteKeyDelegation(this.selectui); } if (!this.process_dlg) { this.process_dlg_node = Y.Node.createWithFilesSkin(M.core_filepicker.templates.processexistingfile); @@ -740,6 +742,7 @@ M.core_filepicker.init = function(Y, options) { this.process_dlg.dialogdata = data; this.process_dlg_node.one('.fp-dlg-butrename').setContent(M.util.get_string('renameto', 'repository', data.newfile.filename)); this.process_dlg.show(); + this.keyDelegation(this.process_dlg); }, /** displays error instead of filepicker contents */ display_error: function(errortext, errorcode) { @@ -771,6 +774,7 @@ M.core_filepicker.init = function(Y, options) { this.msg_dlg_node.one('.fp-msg-butok').on('click', function(e) { e.preventDefault(); this.msg_dlg.hide(); + this.deleteKeyDelegation(this.msg_dlg); }, this); } @@ -778,6 +782,7 @@ M.core_filepicker.init = function(Y, options) { this.msg_dlg_node.removeClass('fp-msg-info').removeClass('fp-msg-error').addClass('fp-msg-'+type) this.msg_dlg_node.one('.fp-msg-text').setContent(Y.Escape.html(msg)); this.msg_dlg.show(); + this.keyDelegation(this.msg_dlg); }, view_files: function(appenditems) { this.viewbar_set_enabled(true); @@ -897,7 +902,7 @@ M.core_filepicker.init = function(Y, options) { } }, false); }, - classnamecallback : function(node) { + classnamecallback : function(node) { var classname = ''; if (node.children) { classname = classname + ' fp-folder'; @@ -1077,6 +1082,7 @@ M.core_filepicker.init = function(Y, options) { Y.one('#fp-file_label_'+this.options.client_id).setContent(Y.Escape.html(M.str.repository.select+' '+argstitle)); this.selectui.show(); + this.keyDelegation(this.selectui); Y.one('#'+this.selectnode.get('id')).focus(); var client_id = this.options.client_id; var selectnode = this.selectnode; @@ -1229,6 +1235,7 @@ M.core_filepicker.init = function(Y, options) { cancel.on('click', function(e) { e.preventDefault(); this.selectui.hide(); + this.deleteKeyDelegation(this.selectui); }, this); }, wait: function() { @@ -1317,6 +1324,7 @@ M.core_filepicker.init = function(Y, options) { // allow to move the panel dragging it by it's header: this.mainui.plug(Y.Plugin.Drag,{handles:['#filepicker-'+client_id+' .yui3-widget-hd']}); this.mainui.show(); + this.keyDelegation(this.mainui); if (this.mainui.get('y') < 0) { this.mainui.set('y', 0); } @@ -1340,6 +1348,7 @@ M.core_filepicker.init = function(Y, options) { this.selectui.plug(Y.Plugin.Drag,{handles:['#filepicker-select-'+client_id+' .yui3-widget-hd']}); Y.one('#'+this.selectnode.get('id')).setAttribute('aria-labelledby', fplabel); this.selectui.hide(); + this.deleteKeyDelegation(this.selectui); // event handler for lazy loading of thumbnails and next page this.fpnode.one('.fp-content').on(['scroll','resize'], this.content_scrolled, this); // save template for one path element and location of path bar @@ -1913,13 +1922,17 @@ M.core_filepicker.init = function(Y, options) { }, hide: function() { this.selectui.hide(); + this.deleteKeyDelegation(this.selectui); if (this.process_dlg) { this.process_dlg.hide(); + this.deleteKeyDelegation(this.process_dlg); } if (this.msg_dlg) { this.msg_dlg.hide(); + this.deleteKeyDelegation(this.msg_dlg); } this.mainui.hide(); + this.deleteKeyDelegation(this.mainui); }, show: function() { if (this.fpnode) { @@ -1929,6 +1942,7 @@ M.core_filepicker.init = function(Y, options) { } else { this.launch(); } + this.keyDelegation(this.mainui); }, launch: function() { this.render(); @@ -1957,6 +1971,31 @@ M.core_filepicker.init = function(Y, options) { M.util.set_user_preference('filepicker_' + name, value); this.options.userprefs[name] = value; } + }, + keyDelegation: function (dialog) { + var bb = dialog.get('boundingBox'); + var can_receive_focus_selector = 'input:not([type="hidden"]), a[href], button, textarea, select'; + bb.delegate('key', function(e) { + var target = e.target; + var firstitem = bb.one(can_receive_focus_selector); + var lastitem = bb.all(can_receive_focus_selector).pop(); + + if (e.shiftKey) { + if (target === firstitem) { + lastitem.focus(); + e.preventDefault(); + } + } else { + if (target === lastitem) { + firstitem.focus(); + e.preventDefault(); + } + } + }, 'down:9', can_receive_focus_selector, this); + }, + detachKeyDelegation: function(dialog) { + var bb = dialog.get('boundingBox'); + bb.detach('key', this.keyDelegation); } }); var loading = Y.one('#filepicker-loading-'+options.client_id);