Proyectos de Subversion Moodle

Rev

Autoría | Ultima modificación | Ver Log |

{"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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"}