Proyectos de Subversion Moodle

Rev

Rev 1 | | Comparar con el anterior | Ultima modificación | Ver Log |

Rev Autor Línea Nro. Línea
11 efrain 1
{"version":3,"file":"message_drawer_router.min.js","sources":["../src/message_drawer_router.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 * A simple router for the message drawer that allows navigating between\n * the \"pages\" in the drawer.\n *\n * This module will maintain a linear history of the unique pages access\n * to allow navigating back.\n *\n * @module     core_message/message_drawer_router\n * @copyright  2018 Ryan Wyllie <ryan@moodle.com>\n * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\ndefine(\n[\n    'jquery',\n    'core/pubsub',\n    'core/str',\n    'core_message/message_drawer_events',\n    'core/aria',\n    'core/pending',\n],\nfunction(\n    $,\n    PubSub,\n    Str,\n    MessageDrawerEvents,\n    Aria,\n    PendingPromise,\n) {\n\n    /* @var {object} routes Message drawer route elements and callbacks. */\n    var routes = {};\n\n    /* @var {object} history Store for route objects history. */\n    var history = {};\n\n    var SELECTORS = {\n        CAN_RECEIVE_FOCUS: 'input:not([type=\"hidden\"]), a[href], button, textarea, select, [tabindex]',\n        ROUTES_BACK: '[data-route-back]'\n    };\n\n    /**\n     * Add a route.\n     *\n     * @param {String} namespace Unique identifier for the Routes\n     * @param {string} route Route config name.\n     * @param {array} parameters Route parameters.\n     * @param {callback} onGo Route initialization function.\n     * @param {callback} getDescription Route initialization function.\n     */\n    var add = function(namespace, route, parameters, onGo, getDescription) {\n        if (!routes[namespace]) {\n            routes[namespace] = [];\n        }\n\n        routes[namespace][route] =\n            {\n                parameters: parameters,\n                onGo: onGo,\n                getDescription: getDescription\n            };\n    };\n\n    /**\n     * Go to a defined route and run the route callbacks.\n     *\n     * @param {String} namespace Unique identifier for the Routes\n     * @param {string} newRoute Route config name.\n     * @return {object} record Current route record with route config name and parameters.\n     */\n    var changeRoute = function(namespace, newRoute) {\n        var newConfig;\n        var pendingPromise = new PendingPromise(`message-drawer-router-${namespace}-${newRoute}`);\n\n        // Check if the Route change call is made from an element in the app panel.\n        var fromPanel = [].slice.call(arguments).some(function(arg) {\n            return arg == 'frompanel';\n        });\n        // Get the rest of the arguments, if any.\n        var args = [].slice.call(arguments, 2);\n        var renderPromise = $.Deferred().resolve().promise();\n\n        Object.keys(routes[namespace]).forEach(function(route) {\n            var config = routes[namespace][route];\n            var isMatch = route === newRoute;\n\n            if (isMatch) {\n                newConfig = config;\n            }\n\n            config.parameters.forEach(function(element) {\n                // Some parameters may be null, or not an element.\n                if (typeof element !== 'object' || element === null) {\n                    return;\n                }\n\n                element.removeClass('previous');\n                element.attr('data-from-panel', false);\n\n                if (isMatch) {\n                    if (fromPanel) {\n                        // Set this attribute to let the conversation renderer know not to show a back button.\n                        element.attr('data-from-panel', true);\n                    }\n                    element.removeClass('hidden');\n                    Aria.unhide(element.get());\n                } else {\n                    // For the message index page elements in the left panel should not be hidden.\n                    if (!element.attr('data-in-panel')) {\n                        element.addClass('hidden');\n                        Aria.hide(element.get());\n                    } else if (newRoute == 'view-search' || newRoute == 'view-overview') {\n                        element.addClass('hidden');\n                        Aria.hide(element.get());\n                    }\n                }\n            });\n        });\n\n        if (newConfig) {\n            if (newConfig.onGo) {\n                renderPromise = newConfig.onGo.apply(undefined, newConfig.parameters.concat(args));\n                var currentFocusElement = $(document.activeElement);\n                var hasFocus = false;\n                var firstFocusable = null;\n\n                // No need to start at 0 as we know that is the namespace.\n                for (var i = 1; i < newConfig.parameters.length; i++) {\n                    var element = newConfig.parameters[i];\n\n                    // Some parameters may be null, or not an element.\n                    if (typeof element !== 'object' || element === null) {\n                        continue;\n                    }\n\n                    if (!firstFocusable) {\n                        firstFocusable = element;\n                    }\n\n                    if (element.has(currentFocusElement).length) {\n                        hasFocus = true;\n                        break;\n                    }\n                }\n\n                if (!hasFocus) {\n                    // This page doesn't have focus yet so focus the first focusable\n                    // element in the new view.\n                    firstFocusable.find(SELECTORS.CAN_RECEIVE_FOCUS).filter(':visible').first().focus();\n                }\n            }\n        }\n\n        var record = {\n            route: newRoute,\n            params: args,\n            renderPromise: renderPromise\n        };\n\n        PubSub.publish(MessageDrawerEvents.ROUTE_CHANGED, record);\n\n        renderPromise.then(() => pendingPromise.resolve());\n        return record;\n    };\n\n    /**\n     * Go to a defined route and store the route history.\n     *\n     * @param {String} namespace Unique identifier for the Routes\n     * @return {object} record Current route record with route config name and parameters.\n     */\n    var go = function(namespace) {\n        var currentFocusElement = $(document.activeElement);\n\n        var record = changeRoute.apply(namespace, arguments);\n        var inHistory = false;\n\n        if (!history[namespace]) {\n            history[namespace] = [];\n        }\n\n        // History stores a unique list of routes. Check to see if the new route\n        // is already in the history, if it is then forget all history after it.\n        // This ensures there are no duplicate routes in history and that it represents\n        // a linear path of routes (it never stores something like [foo, bar, foo])).\n        history[namespace] = history[namespace].reduce(function(carry, previous) {\n            if (previous.route === record.route) {\n                inHistory = true;\n            }\n\n            if (!inHistory) {\n                carry.push(previous);\n            }\n\n            return carry;\n        }, []);\n\n        var historylength = history[namespace].length;\n        var previousRecord = historylength ? history[namespace][historylength - 1] : null;\n\n        if (previousRecord) {\n            var prevConfig = routes[namespace][previousRecord.route];\n            var elements = prevConfig.parameters;\n\n            // The first one will be the namespace, skip it.\n            for (var i = 1; i < elements.length; i++) {\n                // Some parameters may be null, or not an element.\n                if (typeof elements[i] !== 'object' || elements[i] === null) {\n                    continue;\n                }\n\n                elements[i].addClass('previous');\n            }\n\n            previousRecord.focusElement = currentFocusElement;\n\n            if (prevConfig.getDescription) {\n                // If the route has a description then set it on the back button for\n                // the new page we're displaying.\n                prevConfig.getDescription.apply(null, prevConfig.parameters.concat(previousRecord.params))\n                    .then(function(description) {\n                        return Str.get_string('backto', 'core_message', description);\n                    })\n                    .then(function(label) {\n                        // Wait for the new page to finish rendering so that we know\n                        // that the back button is visible.\n                        return record.renderPromise.then(function() {\n                            // Find the elements for the new route we displayed.\n                            routes[namespace][record.route].parameters.forEach(function(element) {\n                                // Some parameters may be null, or not an element.\n                                if (typeof element !== 'object' || !element) {\n                                    return;\n                                }\n                                // Update the aria label for the back button.\n                                element.find(SELECTORS.ROUTES_BACK).attr('aria-label', label);\n                            });\n                        });\n                    })\n                    .catch(function() {\n                        // Silently ignore.\n                    });\n            }\n        }\n        history[namespace].push(record);\n        return record;\n    };\n\n    /**\n     * Go back to the previous route record stored in history.\n     *\n     * @param {String} namespace Unique identifier for the Routes\n     */\n    var back = function(namespace) {\n        if (history[namespace].length) {\n            // Remove the current route.\n            history[namespace].pop();\n            var previous = history[namespace].pop();\n\n            if (previous) {\n                // If we have a previous route then show it.\n                go.apply(undefined, [namespace, previous.route].concat(previous.params));\n                // Delay the focus 50 milliseconds otherwise it doesn't correctly\n                // focus the element for some reason...\n                window.setTimeout(function() {\n                    previous.focusElement.focus();\n                }, 50);\n            }\n        }\n    };\n\n    return {\n        add: add,\n        go: go,\n        back: back\n    };\n});\n"],"names":["define","$","PubSub","Str","MessageDrawerEvents","Aria","PendingPromise","routes","history","SELECTORS","changeRoute","namespace","newRoute","newConfig","pendingPromise","fromPanel","slice","call","arguments","some","arg","args","renderPromise","Deferred","resolve","promise","Object","keys","forEach","route","config","isMatch","parameters","element","removeClass","attr","unhide","get","addClass","hide","onGo","apply","undefined","concat","currentFocusElement","document","activeElement","hasFocus","firstFocusable","i","length","has","find","filter","first","focus","record","params","publish","ROUTE_CHANGED","then","go","inHistory","reduce","carry","previous","push","historylength","previousRecord","prevConfig","elements","focusElement","getDescription","description","get_string","label","catch","add","back","pop","window","setTimeout"],"mappings":"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"}