1 |
efrain |
1 |
{"version":3,"file":"popover_region_controller.min.js","sources":["../src/popover_region_controller.js"],"sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle. If not, see <http://www.gnu.org/licenses/>.\n\n/**\n * Controls the popover region element.\n *\n * See template: core/popover_region\n *\n * @module core/popover_region_controller\n * @copyright 2015 Ryan Wyllie <ryan@moodle.com>\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n * @since 3.2\n */\ndefine(['jquery', 'core/str', 'core/custom_interaction_events'],\n function($, str, customEvents) {\n\n var SELECTORS = {\n CONTENT: '.popover-region-content',\n CONTENT_CONTAINER: '.popover-region-content-container',\n MENU_CONTAINER: '.popover-region-container',\n MENU_TOGGLE: '.popover-region-toggle',\n CAN_RECEIVE_FOCUS: 'input:not([type=\"hidden\"]), a[href], button, textarea, select, [tabindex]',\n };\n\n /**\n * Constructor for the PopoverRegionController.\n *\n * @param {jQuery} element object root element of the popover\n */\n var PopoverRegionController = function(element) {\n this.root = $(element);\n this.content = this.root.find(SELECTORS.CONTENT);\n this.contentContainer = this.root.find(SELECTORS.CONTENT_CONTAINER);\n this.menuContainer = this.root.find(SELECTORS.MENU_CONTAINER);\n this.menuToggle = this.root.find(SELECTORS.MENU_TOGGLE);\n this.isLoading = false;\n this.promises = {\n closeHandlers: $.Deferred(),\n navigationHandlers: $.Deferred(),\n };\n\n // Core event listeners to open and close.\n this.registerBaseEventListeners();\n };\n\n /**\n * The collection of events triggered by this controller.\n *\n * @returns {object}\n */\n PopoverRegionController.prototype.events = function() {\n return {\n menuOpened: 'popoverregion:menuopened',\n menuClosed: 'popoverregion:menuclosed',\n startLoading: 'popoverregion:startLoading',\n stopLoading: 'popoverregion:stopLoading',\n };\n };\n\n /**\n * Return the container element for the content element.\n *\n * @method getContentContainer\n * @return {jQuery} object\n */\n PopoverRegionController.prototype.getContentContainer = function() {\n return this.contentContainer;\n };\n\n /**\n * Return the content element.\n *\n * @method getContent\n * @return {jQuery} object\n */\n PopoverRegionController.prototype.getContent = function() {\n return this.content;\n };\n\n /**\n * Checks if the popover is displayed.\n *\n * @method isMenuOpen\n * @return {bool}\n */\n PopoverRegionController.prototype.isMenuOpen = function() {\n return !this.root.hasClass('collapsed');\n };\n\n /**\n * Toggle the visibility of the popover.\n *\n * @method toggleMenu\n */\n PopoverRegionController.prototype.toggleMenu = function() {\n if (this.isMenuOpen()) {\n this.closeMenu();\n } else {\n this.openMenu();\n }\n };\n\n /**\n * Hide the popover.\n *\n * Note: This triggers the menuClosed event.\n *\n * @method closeMenu\n */\n PopoverRegionController.prototype.closeMenu = function() {\n // We're already closed.\n if (!this.isMenuOpen()) {\n return;\n }\n\n this.root.addClass('collapsed');\n this.menuContainer.attr('aria-expanded', 'false');\n this.menuContainer.attr('aria-hidden', 'true');\n this.updateButtonAriaLabel();\n this.updateFocusItemTabIndex();\n this.root.trigger(this.events().menuClosed);\n };\n\n /**\n * Show the popover.\n *\n * Note: This triggers the menuOpened event.\n *\n * @method openMenu\n */\n PopoverRegionController.prototype.openMenu = function() {\n // We're already open.\n if (this.isMenuOpen()) {\n return;\n }\n\n this.root.removeClass('collapsed');\n this.menuContainer.attr('aria-expanded', 'true');\n this.menuContainer.attr('aria-hidden', 'false');\n this.updateButtonAriaLabel();\n this.updateFocusItemTabIndex();\n // Resolve the promises to allow the handlers to be added\n // to the DOM, if they have been requested.\n this.promises.closeHandlers.resolve();\n this.promises.navigationHandlers.resolve();\n this.root.trigger(this.events().menuOpened);\n };\n\n /**\n * Set the appropriate aria label on the popover toggle.\n *\n * @method updateButtonAriaLabel\n */\n PopoverRegionController.prototype.updateButtonAriaLabel = function() {\n if (this.isMenuOpen()) {\n str.get_string('hidepopoverwindow').done(function(string) {\n this.menuToggle.attr('aria-label', string);\n }.bind(this));\n } else {\n str.get_string('showpopoverwindow').done(function(string) {\n this.menuToggle.attr('aria-label', string);\n }.bind(this));\n }\n };\n\n /**\n * Set the loading state on this popover.\n *\n * Note: This triggers the startLoading event.\n *\n * @method startLoading\n */\n PopoverRegionController.prototype.startLoading = function() {\n this.isLoading = true;\n this.getContentContainer().addClass('loading');\n this.getContentContainer().attr('aria-busy', 'true');\n this.root.trigger(this.events().startLoading);\n };\n\n /**\n * Undo the loading state on this popover.\n *\n * Note: This triggers the stopLoading event.\n *\n * @method stopLoading\n */\n PopoverRegionController.prototype.stopLoading = function() {\n this.isLoading = false;\n this.getContentContainer().removeClass('loading');\n this.getContentContainer().attr('aria-busy', 'false');\n this.root.trigger(this.events().stopLoading);\n };\n\n /**\n * Sets the focus on the menu toggle.\n *\n * @method focusMenuToggle\n */\n PopoverRegionController.prototype.focusMenuToggle = function() {\n this.menuToggle.focus();\n };\n\n /**\n * Check if a content item has focus.\n *\n * @method contentItemHasFocus\n * @return {bool}\n */\n PopoverRegionController.prototype.contentItemHasFocus = function() {\n return this.getContentItemWithFocus().length > 0;\n };\n\n /**\n * Return the currently focused content item.\n *\n * @method getContentItemWithFocus\n * @return {jQuery} object\n */\n PopoverRegionController.prototype.getContentItemWithFocus = function() {\n var currentFocus = $(document.activeElement);\n var items = this.getContent().children();\n var currentItem = items.filter(currentFocus);\n\n if (!currentItem.length) {\n currentItem = items.has(currentFocus);\n }\n\n return currentItem;\n };\n\n /**\n * Focus the given content item or the first focusable element within\n * the content item.\n *\n * @method focusContentItem\n * @param {object} item The content item jQuery element\n */\n PopoverRegionController.prototype.focusContentItem = function(item) {\n if (item.is(SELECTORS.CAN_RECEIVE_FOCUS)) {\n item.focus();\n } else {\n item.find(SELECTORS.CAN_RECEIVE_FOCUS).first().focus();\n }\n };\n\n /**\n * Set focus on the first content item in the list.\n *\n * @method focusFirstContentItem\n */\n PopoverRegionController.prototype.focusFirstContentItem = function() {\n this.focusContentItem(this.getContent().children().first());\n };\n\n /**\n * Set focus on the last content item in the list.\n *\n * @method focusLastContentItem\n */\n PopoverRegionController.prototype.focusLastContentItem = function() {\n this.focusContentItem(this.getContent().children().last());\n };\n\n /**\n * Set focus on the content item after the item that currently has focus\n * in the list.\n *\n * @method focusNextContentItem\n */\n PopoverRegionController.prototype.focusNextContentItem = function() {\n var currentItem = this.getContentItemWithFocus();\n\n if (currentItem.length && currentItem.next()) {\n this.focusContentItem(currentItem.next());\n }\n };\n\n /**\n * Set focus on the content item preceding the item that currently has focus\n * in the list.\n *\n * @method focusPreviousContentItem\n */\n PopoverRegionController.prototype.focusPreviousContentItem = function() {\n var currentItem = this.getContentItemWithFocus();\n\n if (currentItem.length && currentItem.prev()) {\n this.focusContentItem(currentItem.prev());\n }\n };\n\n /**\n * Register the minimal amount of listeners for the popover to function.\n *\n * @method registerBaseEventListeners\n */\n PopoverRegionController.prototype.registerBaseEventListeners = function() {\n customEvents.define(this.root, [\n customEvents.events.activate,\n customEvents.events.escape,\n ]);\n\n // Toggle the popover visibility on activation (click/enter/space) of the toggle button.\n this.root.on(customEvents.events.activate, SELECTORS.MENU_TOGGLE, function() {\n this.toggleMenu();\n }.bind(this));\n\n // Delay the binding of these handlers until the region has been opened.\n this.promises.closeHandlers.done(function() {\n // Close the popover if escape is pressed.\n this.root.on(customEvents.events.escape, function() {\n this.closeMenu();\n this.focusMenuToggle();\n }.bind(this));\n\n // Close the popover if any other part of the page is clicked.\n $('html').click(function(e) {\n var target = $(e.target);\n if (!this.root.is(target) && !this.root.has(target).length) {\n this.closeMenu();\n }\n }.bind(this));\n\n customEvents.define(this.getContentContainer(), [\n customEvents.events.scrollBottom\n ]);\n }.bind(this));\n };\n\n /**\n * Set up the event listeners for keyboard navigating a list of content items.\n *\n * @method registerListNavigationEventListeners\n */\n PopoverRegionController.prototype.registerListNavigationEventListeners = function() {\n customEvents.define(this.root, [\n customEvents.events.down\n ]);\n\n // If the down arrow is pressed then open the menu and focus the first content\n // item or focus the next content item if the menu is open.\n this.root.on(customEvents.events.down, function(e, data) {\n if (!this.isMenuOpen()) {\n this.openMenu();\n this.focusFirstContentItem();\n } else {\n if (this.contentItemHasFocus()) {\n this.focusNextContentItem();\n } else {\n this.focusFirstContentItem();\n }\n }\n\n data.originalEvent.preventDefault();\n }.bind(this));\n\n // Delay the binding of these handlers until the region has been opened.\n this.promises.navigationHandlers.done(function() {\n customEvents.define(this.root, [\n customEvents.events.up,\n customEvents.events.home,\n customEvents.events.end,\n ]);\n\n // Shift focus to the previous content item if the up key is pressed.\n this.root.on(customEvents.events.up, function(e, data) {\n this.focusPreviousContentItem();\n data.originalEvent.preventDefault();\n }.bind(this));\n\n // Jump focus to the first content item if the home key is pressed.\n this.root.on(customEvents.events.home, function(e, data) {\n this.focusFirstContentItem();\n data.originalEvent.preventDefault();\n }.bind(this));\n\n // Jump focus to the last content item if the end key is pressed.\n this.root.on(customEvents.events.end, function(e, data) {\n this.focusLastContentItem();\n data.originalEvent.preventDefault();\n }.bind(this));\n }.bind(this));\n };\n\n /**\n * Set the appropriate tabindex attribute on the popover toggle.\n *\n * @method updateFocusItemTabIndex\n */\n PopoverRegionController.prototype.updateFocusItemTabIndex = function() {\n if (this.isMenuOpen()) {\n this.menuContainer.find(SELECTORS.CAN_RECEIVE_FOCUS).removeAttr('tabindex');\n } else {\n this.menuContainer.find(SELECTORS.CAN_RECEIVE_FOCUS).attr('tabindex', '-1');\n }\n };\n\n return PopoverRegionController;\n});\n"],"names":["define","$","str","customEvents","SELECTORS","PopoverRegionController","element","root","content","this","find","contentContainer","menuContainer","menuToggle","isLoading","promises","closeHandlers","Deferred","navigationHandlers","registerBaseEventListeners","prototype","events","menuOpened","menuClosed","startLoading","stopLoading","getContentContainer","getContent","isMenuOpen","hasClass","toggleMenu","closeMenu","openMenu","addClass","attr","updateButtonAriaLabel","updateFocusItemTabIndex","trigger","removeClass","resolve","get_string","done","string","bind","focusMenuToggle","focus","contentItemHasFocus","getContentItemWithFocus","length","currentFocus","document","activeElement","items","children","currentItem","filter","has","focusContentItem","item","is","first","focusFirstContentItem","focusLastContentItem","last","focusNextContentItem","next","focusPreviousContentItem","prev","activate","escape","on","click","e","target","scrollBottom","registerListNavigationEventListeners","down","data","originalEvent","preventDefault","up","home","end","removeAttr"],"mappings":"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"}
|