Merge branch 'MDL-70075-master-2' of git://github.com/andrewnicols/moodle

This commit is contained in:
Jun Pataleta 2020-11-05 12:51:31 +08:00
commit 2927457dbf
6 changed files with 171 additions and 52 deletions

View File

@ -1,2 +1,2 @@
define ("core/custom_interaction_events",["jquery","core/key_codes"],function(a,b){var c={activate:"cie:activate",keyboardActivate:"cie:keyboardactivate",escape:"cie:escape",down:"cie:down",up:"cie:up",home:"cie:home",end:"cie:end",next:"cie:next",previous:"cie:previous",asterix:"cie:asterix",scrollLock:"cie:scrollLock",scrollTop:"cie:scrollTop",scrollBottom:"cie:scrollBottom",ctrlPageUp:"cie:ctrlPageUp",ctrlPageDown:"cie:ctrlPageDown",enter:"cie:enter",accessibleChange:"cie:accessibleChange"},d={},e=function(a,b){b=b||[];if(b.length&&-1!==b.indexOf(a)){return!0}return!1},f=function(a){return a.shiftKey||a.metaKey||a.altKey||a.ctrlKey},g=function(b,c){var e="";if(!c.hasOwnProperty("originalEvent")){e=""+b+c.type+c.timeStamp;if(!d.hasOwnProperty(e)){d[e]=!0;a(c.target).trigger(b,[{originalEvent:c}])}return}e="triggeredCustom_"+b;if(!c.originalEvent.hasOwnProperty(e)){c.originalEvent[e]=!0;a(c.target).trigger(b,[{originalEvent:c}])}},h=function(a,b,c){a.off("keydown."+b).on("keydown."+b,function(a){if(!f(a)){if(a.keyCode==c){g(b,a)}}})},i=function(a){a.off("click.cie.activate").on("click.cie.activate",function(a){g(c.activate,a)});a.off("keydown.cie.activate").on("keydown.cie.activate",function(a){if(!f(a)){if(a.keyCode==b.enter||a.keyCode==b.space){g(c.activate,a)}}})},j=function(a){a.off("keydown.cie.keyboardactivate").on("keydown.cie.keyboardactivate",function(a){if(!f(a)){if(a.keyCode==b.enter||a.keyCode==b.space){g(c.keyboardActivate,a)}}})},k=function(a){h(a,c.escape,b.escape)},l=function(a){h(a,c.down,b.arrowDown)},m=function(a){h(a,c.up,b.arrowUp)},n=function(a){h(a,c.home,b.home)},o=function(a){h(a,c.end,b.end)},p=function(d){var e="rtl"==a("html").attr("dir")?b.arrowLeft:b.arrowRight;h(d,c.next,e)},q=function(d){var e="rtl"==a("html").attr("dir")?b.arrowRight:b.arrowLeft;h(d,c.previous,e)},r=function(a){h(a,c.asterix,b.asterix)},s=function(a){a.off("scroll.cie.scrollTop").on("scroll.cie.scrollTop",function(b){var d=a.scrollTop();if(0===d){g(c.scrollTop,b)}})},t=function(a){a.off("scroll.cie.scrollBottom").on("scroll.cie.scrollBottom",function(b){var d=a.scrollTop(),e=a.innerHeight(),f=a[0].scrollHeight;if(d+e>=f){g(c.scrollBottom,b)}})},u=function(a){a.off("DOMMouseScroll.cie.DOMMouseScrollLock mousewheel.cie.mousewheelLock").on("DOMMouseScroll.cie.DOMMouseScrollLock mousewheel.cie.mousewheelLock",function(b){var d=a.scrollTop(),e=a[0].scrollHeight,f=a.height(),h="DOMMouseScroll"==b.type?-40*b.originalEvent.detail:b.originalEvent.wheelDelta,i=0<h;if(!i&&-h>e-f-d){a.scrollTop(e);b.stopPropagation();b.preventDefault();b.returnValue=!1;g(c.scrollLock,b);return!1}else if(i&&h>d){a.scrollTop(0);b.stopPropagation();b.preventDefault();b.returnValue=!1;g(c.scrollLock,b);return!1}return!0})},v=function(a){a.off("keydown.cie.ctrlpageup").on("keydown.cie.ctrlpageup",function(a){if(a.ctrlKey){if(a.keyCode==b.pageUp){g(c.ctrlPageUp,a)}}})},w=function(a){a.off("keydown.cie.ctrlpagedown").on("keydown.cie.ctrlpagedown",function(a){if(a.ctrlKey){if(a.keyCode==b.pageDown){g(c.ctrlPageDown,a)}}})},x=function(a){h(a,c.enter,b.enter)},y=function(d){var e=-1!==navigator.userAgent.indexOf("Macintosh"),f="ontouchstart"in window||"msMaxTouchPoints"in navigator&&0<navigator.msMaxTouchPoints;if(e||f){d.on("change",function(a){g(c.accessibleChange,a)})}else{d.on("focusin",function(b){a(b.target).data("initValue",b.target.value)});d.on("focusout",function(b){var d=a(b.target).data("initValue");a(b.target).removeData("initValue");if(b.target.value!==d){g(c.accessibleChange,b)}});d.on("keydown",function(d){if(d.which===b.enter&&d.target.value!==a(d.target).data("initValue")){g(c.accessibleChange,d)}else if(d.which===b.escape){d.target.value=a(d.target).data("initValue")}});d.on("click",function(b){var d=a(b.target).data("initValue");if("undefined"!=typeof d&&d!=b.target.value){g(c.accessibleChange,b)}})}},z=function(){var a={};a[c.activate]=i;a[c.keyboardActivate]=j;a[c.escape]=k;a[c.down]=l;a[c.up]=m;a[c.home]=n;a[c.end]=o;a[c.next]=p;a[c.previous]=q;a[c.asterix]=r;a[c.scrollLock]=u;a[c.scrollTop]=s;a[c.scrollBottom]=t;a[c.ctrlPageUp]=v;a[c.ctrlPageDown]=w;a[c.enter]=x;a[c.accessibleChange]=y;return a};return{define:function define(b,c){b=a(b);c=c||[];if(!b.length||!c.length){return}a.each(z(),function(a,d){if(e(a,c)){d(b)}})},events:c}});
define ("core/custom_interaction_events",["jquery","core/key_codes"],function(a,b){var c={activate:"cie:activate",keyboardActivate:"cie:keyboardactivate",escape:"cie:escape",down:"cie:down",up:"cie:up",home:"cie:home",end:"cie:end",next:"cie:next",previous:"cie:previous",asterix:"cie:asterix",scrollLock:"cie:scrollLock",scrollTop:"cie:scrollTop",scrollBottom:"cie:scrollBottom",ctrlPageUp:"cie:ctrlPageUp",ctrlPageDown:"cie:ctrlPageDown",enter:"cie:enter",accessibleChange:"cie:accessibleChange"},d={},e=function(a,b){b=b||[];if(b.length&&-1!==b.indexOf(a)){return!0}return!1},f=function(a){return a.shiftKey||a.metaKey||a.altKey||a.ctrlKey},g=function(b,c){var e="";if(!c.hasOwnProperty("originalEvent")){e=""+b+c.type+c.timeStamp;if(!d.hasOwnProperty(e)){d[e]=!0;a(c.target).trigger(b,[{originalEvent:c}])}return}e="triggeredCustom_"+b;if(!c.originalEvent.hasOwnProperty(e)){c.originalEvent[e]=!0;a(c.target).trigger(b,[{originalEvent:c}])}},h=function(a,b,c){a.off("keydown."+b).on("keydown."+b,function(a){if(!f(a)){if(a.keyCode==c){g(b,a)}}})},i=function(a){a.off("click.cie.activate").on("click.cie.activate",function(a){g(c.activate,a)});a.off("keydown.cie.activate").on("keydown.cie.activate",function(a){if(!f(a)){if(a.keyCode==b.enter||a.keyCode==b.space){g(c.activate,a)}}})},j=function(a){a.off("keydown.cie.keyboardactivate").on("keydown.cie.keyboardactivate",function(a){if(!f(a)){if(a.keyCode==b.enter||a.keyCode==b.space){g(c.keyboardActivate,a)}}})},k=function(a){h(a,c.escape,b.escape)},l=function(a){h(a,c.down,b.arrowDown)},m=function(a){h(a,c.up,b.arrowUp)},n=function(a){h(a,c.home,b.home)},o=function(a){h(a,c.end,b.end)},p=function(d){var e="rtl"==a("html").attr("dir")?b.arrowLeft:b.arrowRight;h(d,c.next,e)},q=function(d){var e="rtl"==a("html").attr("dir")?b.arrowRight:b.arrowLeft;h(d,c.previous,e)},r=function(a){h(a,c.asterix,b.asterix)},s=function(a){a.off("scroll.cie.scrollTop").on("scroll.cie.scrollTop",function(b){var d=a.scrollTop();if(0===d){g(c.scrollTop,b)}})},t=function(a){a.off("scroll.cie.scrollBottom").on("scroll.cie.scrollBottom",function(b){var d=a.scrollTop(),e=a.innerHeight(),f=a[0].scrollHeight;if(d+e>=f){g(c.scrollBottom,b)}})},u=function(a){a.off("DOMMouseScroll.cie.DOMMouseScrollLock mousewheel.cie.mousewheelLock").on("DOMMouseScroll.cie.DOMMouseScrollLock mousewheel.cie.mousewheelLock",function(b){var d=a.scrollTop(),e=a[0].scrollHeight,f=a.height(),h="DOMMouseScroll"==b.type?-40*b.originalEvent.detail:b.originalEvent.wheelDelta,i=0<h;if(!i&&-h>e-f-d){a.scrollTop(e);b.stopPropagation();b.preventDefault();b.returnValue=!1;g(c.scrollLock,b);return!1}else if(i&&h>d){a.scrollTop(0);b.stopPropagation();b.preventDefault();b.returnValue=!1;g(c.scrollLock,b);return!1}return!0})},v=function(a){a.off("keydown.cie.ctrlpageup").on("keydown.cie.ctrlpageup",function(a){if(a.ctrlKey){if(a.keyCode==b.pageUp){g(c.ctrlPageUp,a)}}})},w=function(a){a.off("keydown.cie.ctrlpagedown").on("keydown.cie.ctrlpagedown",function(a){if(a.ctrlKey){if(a.keyCode==b.pageDown){g(c.ctrlPageDown,a)}}})},x=function(a){h(a,c.enter,b.enter)},y=function(a){var d=-1!==navigator.userAgent.indexOf("Macintosh"),e="ontouchstart"in window||"msMaxTouchPoints"in navigator&&0<navigator.msMaxTouchPoints;if(d||e){a.on("change",function(a){g(c.accessibleChange,a)})}else{var f=function(a){a.dataset.initValue=a.value},h=function(a){if("initValue"in a.dataset){a.value=a.dataset.initValue}},i=function(a){if(!("initValue"in a.target.dataset)){return}if(a.target.value!==a.target.dataset.initValue){a.target.dataset.initValue=a.target.value;g(c.accessibleChange,a)}},j=a.get()[0];j.addEventListener("focus",function(a){f(a.target)},!0);j.addEventListener("blur",function(a){i(a)},!0);a.on("keydown",function(a){if(a.which===b.enter){i(a)}else if(a.which===b.escape){h(a.target);a.target.dataset.ignoreChange=!0}else{a.target.dataset.ignoreChange=!0}});a.on("change",function(a){if(a.target.dataset.ignoreChange){return}i(a)});a.on("keyup",function(a){delete a.target.dataset.ignoreChange});a.on("click",function(a){i(a)})}},z=function(){var a={};a[c.activate]=i;a[c.keyboardActivate]=j;a[c.escape]=k;a[c.down]=l;a[c.up]=m;a[c.home]=n;a[c.end]=o;a[c.next]=p;a[c.previous]=q;a[c.asterix]=r;a[c.scrollLock]=u;a[c.scrollTop]=s;a[c.scrollBottom]=t;a[c.ctrlPageUp]=v;a[c.ctrlPageDown]=w;a[c.enter]=x;a[c.accessibleChange]=y;return a};return{define:function define(b,c){b=a(b);c=c||[];if(!b.length||!c.length){return}a.each(z(),function(a,d){if(e(a,c)){d(b)}})},events:c}});
//# sourceMappingURL=custom_interaction_events.min.js.map

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -427,33 +427,95 @@ define(['jquery', 'core/key_codes'], function($, keyCodes) {
var onMac = navigator.userAgent.indexOf('Macintosh') !== -1;
var touchEnabled = ('ontouchstart' in window) || (('msMaxTouchPoints' in navigator) && (navigator.msMaxTouchPoints > 0));
if (onMac || touchEnabled) {
// On Mac devices, and touch-enabled devices, the change event seems to be handled correctly and
// consistently at this time.
element.on('change', function(e) {
triggerEvent(events.accessibleChange, e);
});
} else {
element.on('focusin', function(e) {
$(e.target).data('initValue', e.target.value);
});
element.on('focusout', function(e) {
var initValue = $(e.target).data('initValue');
$(e.target).removeData('initValue');
if (e.target.value !== initValue) {
// Some browsers have non-normalised behaviour for handling the selection of values in a <select> element.
// When using Chrome on Linux (and possibly others), a 'change' event is fired when pressing the Escape key.
// When using Firefox on Linux (and possibly others), a 'change' event is fired when navigating through the
// list with a keyboard.
//
// To normalise these behaviours:
// - the initial value is stored in a data attribute when focusing the element
// - the current value is checked against the stored initial value when and the accessibleChange event fired when:
// --- blurring the element
// --- the 'Enter' key is pressed
// --- the element is clicked
// --- the 'change' event is fired, except where it is from a keyboard interaction
//
// To facilitate the change event keyboard interaction check, the 'keyDown' handler sets a flag to ignore
// the change event handler which is unset on the 'keyUp' event.
//
// Unfortunately we cannot control this entirely as some browsers (Chrome) trigger a change event when
// pressign the Escape key, and this is considered to be the correct behaviour.
// Chrome https://bugs.chromium.org/p/chromium/issues/detail?id=839717
//
// Our longer-term solution to this should be to switch away from using <select> boxes as a single-select,
// and make use of a dropdown of action links like the Bootstrap Dropdown menu.
var setInitialValue = function(target) {
target.dataset.initValue = target.value;
};
var resetToInitialValue = function(target) {
if ('initValue' in target.dataset) {
target.value = target.dataset.initValue;
}
};
var checkAndTriggerAccessibleChange = function(e) {
if (!('initValue' in e.target.dataset)) {
// Some browsers trigger click before focus, therefore it is possible that initValue is undefined.
// In this case it's likely that it's being focused for the first time and we should therefore not submit.
return;
}
if (e.target.value !== e.target.dataset.initValue) {
// Update the initValue when the event is triggered.
// This means that if the click handler fires before the focus handler on a subsequent interaction
// with the element, the currently dispalyed value will be the best guess current value.
e.target.dataset.initValue = e.target.value;
triggerEvent(events.accessibleChange, e);
}
});
};
var nativeElement = element.get()[0];
// The `focus` and `blur` events do not support bubbling. Use Event Capture instead.
nativeElement.addEventListener('focus', function(e) {
setInitialValue(e.target);
}, true);
nativeElement.addEventListener('blur', function(e) {
checkAndTriggerAccessibleChange(e);
}, true);
element.on('keydown', function(e) {
if ((e.which === keyCodes.enter) && e.target.value !== $(e.target).data('initValue')) {
triggerEvent(events.accessibleChange, e);
if ((e.which === keyCodes.enter)) {
checkAndTriggerAccessibleChange(e);
} else if (e.which === keyCodes.escape) {
e.target.value = $(e.target).data('initValue');
resetToInitialValue(e.target);
e.target.dataset.ignoreChange = true;
} else {
// Firefox triggers a change event when using the keyboard to scroll through the selection.
// Set a data- attribute that the change listener can use to ignore the change event where it was
// generated from a keyboard change such as typing to complete a value, or using arrow keys.
e.target.dataset.ignoreChange = true;
}
});
element.on('change', function(e) {
if (e.target.dataset.ignoreChange) {
// This change event was triggered from a keyboard change which is not yet complete.
// Do not trigger the accessibleChange event until the selection is completed using the [return]
// key.
return;
}
checkAndTriggerAccessibleChange(e);
});
element.on('keyup', function(e) {
// The key has been lifted. Stop ignoring the change event.
delete e.target.dataset.ignoreChange;
});
element.on('click', function(e) {
var initValue = $(e.target).data('initValue');
// Some browsers trigger onclick before onblur, therefore it is possible that initValue is undefined.
if (typeof initValue !== 'undefined' && initValue != e.target.value) {
triggerEvent(events.accessibleChange, e);
}
checkAndTriggerAccessibleChange(e);
});
}
};

View File

@ -69,15 +69,65 @@ function($, log, str, templates, notification, LoadingIcon, Aria) {
var itemId = state.selectionId + '-' + index;
// Deselect all the selections.
selectionElement.children().attr('data-active-selection', false).attr('id', '');
selectionElement.children().attr('data-active-selection', null).attr('id', '');
// Select only this suggestion and assign it the id.
element.attr('data-active-selection', true).attr('id', itemId);
// Tell the input field it has a new active descendant so the item is announced.
selectionElement.attr('aria-activedescendant', itemId);
selectionElement.attr('data-active-value', element.attr('data-value'));
return $.Deferred().resolve();
};
/**
* Get the actively selected element from the state object.
*
* @param {Object} state
* @returns {jQuery}
*/
var getActiveElementFromState = function(state) {
var selectionRegion = $(document.getElementById(state.selectionId));
var activeId = selectionRegion.attr('aria-activedescendant');
if (activeId) {
var activeElement = $(document.getElementById(activeId));
if (activeElement.length) {
// The active descendent still exists.
return activeElement;
}
}
var activeValue = selectionRegion.attr('data-active-value');
return selectionRegion.find('[data-value="' + activeValue + '"]');
};
/**
* Update the active selection from the given state object.
*
* @param {Object} state
*/
var updateActiveSelectionFromState = function(state) {
var activeElement = getActiveElementFromState(state);
var activeValue = activeElement.attr('data-value');
var selectionRegion = $(document.getElementById(state.selectionId));
if (activeValue) {
// Find the index of the currently selected index.
var activeIndex = selectionRegion.find('[aria-selected=true]').index(activeElement);
if (activeIndex !== -1) {
activateSelection(activeIndex, state);
return;
}
}
// Either the active index was not set, or it could not be found.
// Select the first value instead.
activateSelection(0, state);
};
/**
* Update the element that shows the currently selected items.
*
@ -95,12 +145,6 @@ function($, log, str, templates, notification, LoadingIcon, Aria) {
// Build up a valid context to re-render the template.
var items = [];
var newSelection = $(document.getElementById(state.selectionId));
var activeId = newSelection.attr('aria-activedescendant');
var activeValue = false;
if (activeId) {
activeValue = $(document.getElementById(activeId)).attr('data-value');
}
originalSelect.children('option').each(function(index, ele) {
if ($(ele).prop('selected')) {
var label;
@ -114,23 +158,24 @@ function($, log, str, templates, notification, LoadingIcon, Aria) {
}
}
});
var context = $.extend({items: items}, options, state);
if (!hasItemListChanged(state, items)) {
M.util.js_complete(pendingKey);
return Promise.resolve();
}
state.items = items;
var context = $.extend(options, state);
// Render the template.
return templates.render(options.templates.items, context)
.then(function(html, js) {
// Add it to the page.
templates.replaceNodeContents(newSelection, html, js);
if (activeValue !== false) {
// Reselect any previously selected item.
newSelection.children('[aria-selected=true]').each(function(index, ele) {
if ($(ele).attr('data-value') === activeValue) {
activateSelection(index, state);
}
});
}
updateActiveSelectionFromState(state);
return activeValue;
return;
})
.then(function() {
return M.util.js_complete(pendingKey);
@ -138,6 +183,21 @@ function($, log, str, templates, notification, LoadingIcon, Aria) {
.catch(notification.exception);
};
/**
* Check whether the list of items stored in the state has changed.
*
* @param {Object} state
* @param {Array} items
*/
var hasItemListChanged = function(state, items) {
if (state.items.length !== items.length) {
return true;
}
// Check for any items in the state items which are not present in the new items list.
return state.items.filter(item => items.indexOf(item) === -1).length > 0;
};
/**
* Notify of a change in the selection.
*
@ -263,7 +323,7 @@ function($, log, str, templates, notification, LoadingIcon, Aria) {
// Find the list of selections.
var selectionsElement = $(document.getElementById(state.selectionId));
// Find the active one.
var element = selectionsElement.children('[data-active-selection=true]');
var element = selectionsElement.children('[data-active-selection]');
if (!element) {
return activateSelection(0, state);
}
@ -286,7 +346,7 @@ function($, log, str, templates, notification, LoadingIcon, Aria) {
var selectionsElement = $(document.getElementById(state.selectionId));
// Find the active one.
var element = selectionsElement.children('[data-active-selection=true]');
var element = selectionsElement.children('[data-active-selection]');
var current = 0;
if (element) {
@ -805,6 +865,7 @@ function($, log, str, templates, notification, LoadingIcon, Aria) {
.catch();
});
var selectionElement = $(document.getElementById(state.selectionId));
// Handle clicks on the selected items (will unselect an item).
selectionElement.on('click', '[role=option]', function(e) {
var pendingPromise = addPendingJSPromise('form-autocomplete-clicks');
@ -812,16 +873,12 @@ function($, log, str, templates, notification, LoadingIcon, Aria) {
// Remove it from the selection.
pendingPromise.resolve(deselectItem(options, state, $(e.currentTarget), originalSelect));
});
// When listbox is focused, focus on the first option if there is no focused option.
selectionElement.on('focus', function() {
// Find the list of selections.
var selectionsElement = $(document.getElementById(state.selectionId));
// Find the active one.
var element = selectionsElement.children('[data-active-selection=true]');
if (!element.length) {
return activateSelection(0, state);
}
updateActiveSelectionFromState(state);
});
// Keyboard navigation for the selection list.
selectionElement.on('keydown', function(e) {
var pendingPromise = addPendingJSPromise('form-autocomplete-keydown-' + e.keyCode);
@ -833,7 +890,7 @@ function($, log, str, templates, notification, LoadingIcon, Aria) {
// Choose the next selection item.
pendingPromise.resolve(activateNextSelection(state));
return false;
return;
case KEYS.LEFT:
case KEYS.UP:
// We handled this event, so prevent it.
@ -841,23 +898,22 @@ function($, log, str, templates, notification, LoadingIcon, Aria) {
// Choose the previous selection item.
pendingPromise.resolve(activatePreviousSelection(state));
return false;
return;
case KEYS.SPACE:
case KEYS.ENTER:
// Get the item that is currently selected.
var selectedItem = $(document.getElementById(state.selectionId)).children('[data-active-selection=true]');
var selectedItem = $(document.getElementById(state.selectionId)).children('[data-active-selection]');
if (selectedItem) {
e.preventDefault();
// Unselect this item.
pendingPromise.resolve(deselectItem(options, state, selectedItem, originalSelect));
}
return false;
return;
}
// Not handled. Resolve the promise.
pendingPromise.resolve();
return true;
});
// Whenever the input field changes, update the suggestion list.
if (options.showSuggestions) {
@ -1055,7 +1111,8 @@ function($, log, str, templates, notification, LoadingIcon, Aria) {
inputId: 'form_autocomplete_input-' + uniqueId,
suggestionsId: 'form_autocomplete_suggestions-' + uniqueId,
selectionId: 'form_autocomplete_selection-' + uniqueId,
downArrowId: 'form_autocomplete_downarrow-' + uniqueId
downArrowId: 'form_autocomplete_downarrow-' + uniqueId,
items: [],
};
// Increment the unique counter so we don't get duplicates ever.