summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'MLEB/UniversalLanguageSelector/resources/js/ext.uls.dialog.js')
-rw-r--r--MLEB/UniversalLanguageSelector/resources/js/ext.uls.dialog.js205
1 files changed, 205 insertions, 0 deletions
diff --git a/MLEB/UniversalLanguageSelector/resources/js/ext.uls.dialog.js b/MLEB/UniversalLanguageSelector/resources/js/ext.uls.dialog.js
new file mode 100644
index 00000000..737f6522
--- /dev/null
+++ b/MLEB/UniversalLanguageSelector/resources/js/ext.uls.dialog.js
@@ -0,0 +1,205 @@
+/*!
+ * A simple dialog to be used inside ULS.
+ *
+ * @private
+ * @since 2020.01
+ *
+ * Copyright (C) 2019-2020 Alolita Sharma, Amir Aharoni, Arun Ganesh, Brandon Harris,
+ * Niklas Laxström, Pau Giner, Santhosh Thottingal, Siebrand Mazeland and other
+ * contributors. See CREDITS for a list.
+ *
+ * UniversalLanguageSelector is dual licensed GPLv2 or later and MIT. You don't
+ * have to do anything special to choose one license or the other and you don't
+ * have to notify anyone which license you are using. You are free to use
+ * UniversalLanguageSelector in commercial projects as long as the copyright
+ * header is left intact. See files GPL-LICENSE and MIT-LICENSE for details.
+ *
+ * @file
+ * @ingroup Extensions
+ * @licence GNU General Public Licence 2.0 or later
+ * @licence MIT License
+ */
+
+( function () {
+ 'use strict';
+
+ var ULSDialog = function ( options ) {
+ var $dialog = options.container,
+ hasOverlay = options.hasOverlay,
+ $overlay,
+ // Source: https://github.com/ghosh/Micromodal/blob/master/lib/src/index.js#L4
+ FOCUSABLE_NODES = [
+ 'a[href]',
+ 'area[href]',
+ 'input:not([disabled]):not([type="hidden"]):not([aria-hidden])',
+ 'select:not([disabled]):not([aria-hidden])',
+ 'textarea:not([disabled]):not([aria-hidden])',
+ 'button:not([disabled]):not([aria-hidden])',
+ 'iframe',
+ 'object',
+ 'embed',
+ '[contenteditable]',
+ '[tabindex]:not([tabindex^="-"])'
+ ],
+ afterOpen = options.afterOpen,
+ afterClose = options.afterClose;
+
+ function getFocusableNodes() {
+ return $dialog.find( FOCUSABLE_NODES.join( ', ' ) );
+ }
+
+ function isElementInDialog( targetElement ) {
+ return $dialog.get( 0 ).contains( targetElement );
+ }
+
+ function focusOverlay() {
+ if ( $overlay ) {
+ $overlay.get( 0 ).focus();
+ }
+ }
+
+ function focusFirstNodeOrOverlay( $focusableNodes ) {
+ if ( $focusableNodes === undefined ) {
+ $focusableNodes = getFocusableNodes();
+ }
+
+ if ( $focusableNodes.length ) {
+ $focusableNodes.get( 0 ).focus();
+ } else {
+ focusOverlay();
+ }
+ }
+
+ function maintainFocus( event ) {
+ var $focusableNodes = getFocusableNodes(),
+ focusedItemIndex;
+
+ if ( !hasOverlay ) {
+ // overlay is not present, so let tabbing flow as normal.
+ return;
+ }
+
+ if ( !$focusableNodes.length ) {
+ // no focusable node in the dialog, focus on the overlay.
+ focusOverlay();
+ return;
+ }
+
+ if ( !isElementInDialog( document.activeElement ) ) {
+ focusFirstNodeOrOverlay( $focusableNodes );
+ } else {
+ focusedItemIndex = $focusableNodes.index( document.activeElement );
+
+ if ( event.shiftKey && focusedItemIndex === 0 ) {
+ $focusableNodes.get( -1 ).focus();
+ event.preventDefault();
+ } else if ( !event.shiftKey && focusedItemIndex === $focusableNodes.length - 1 ) {
+ focusFirstNodeOrOverlay( $focusableNodes );
+ event.preventDefault();
+ }
+ }
+ }
+
+ function handleFirstFocus( event ) {
+ if ( !hasOverlay ) {
+ // Overlay is not present, so let tabbing flow as normal.
+ return;
+ }
+
+ if ( isElementInDialog( event.target ) ) {
+ return;
+ }
+
+ focusFirstNodeOrOverlay();
+ }
+
+ function onKeydown( event ) {
+ switch ( event.key ) {
+ case 'Esc':
+ case 'Escape':
+ // eslint-disable-next-line no-use-before-define
+ close();
+ event.preventDefault();
+ break;
+ case 'Tab':
+ maintainFocus( event );
+ break;
+ }
+ }
+
+ function addEvents() {
+ $( document )
+ .on( 'keydown', onKeydown )
+ .on( 'focusin', handleFirstFocus );
+ }
+
+ function removeEvents() {
+ $( document )
+ .off( 'keydown', onKeydown )
+ .off( 'focusin', handleFirstFocus );
+ }
+
+ function showOverlay() {
+ if ( $overlay ) {
+ $overlay.show();
+ $( document.body ).addClass( 'uls-no-overflow' );
+ }
+ }
+
+ function hideOverlay() {
+ if ( $overlay ) {
+ $overlay.hide();
+ $( document.body ).removeClass( 'uls-no-overflow' );
+ }
+ }
+
+ function open() {
+ $dialog.show();
+ addEvents();
+ showOverlay();
+ focusFirstNodeOrOverlay();
+ if ( afterOpen ) {
+ afterOpen();
+ }
+ }
+
+ function close() {
+ $dialog.hide();
+ removeEvents();
+ hideOverlay();
+ if ( afterClose ) {
+ afterClose();
+ }
+ }
+
+ function elem() {
+ return $dialog;
+ }
+
+ function addOverlay() {
+ // Check if overlay is already there.
+ if ( !$overlay ) {
+ $overlay = $( '<div>' )
+ .addClass( 'uls-overlay' )
+ .prop( 'tabindex', '-1' )
+ .on( 'click', close )
+ .appendTo( document.body );
+ }
+ }
+
+ $dialog.addClass( 'uls-dialog' );
+
+ if ( hasOverlay ) {
+ addOverlay();
+ }
+
+ return {
+ open: open,
+ close: close,
+ elem: elem
+ };
+ };
+
+ mw.uls = mw.uls || {};
+ mw.uls.Dialog = ULSDialog;
+}() );