Proyectos de Subversion Moodle

Rev

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

{"version":3,"file":"tool_card_controller.min.js","sources":["../src/tool_card_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 all of the behaviour and interaction with a tool type card. These are\n * listed on the LTI tool type management page.\n *\n * See template: mod_lti/tool_card\n *\n * @module     mod_lti/tool_card_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.1\n */\n define(['jquery', 'core/ajax', 'core/notification', 'core/templates', 'core/modal',\n        'mod_lti/tool_type', 'mod_lti/events', 'mod_lti/keys',\n        'core/str'],\n        function($, ajax, notification, templates, Modal, toolType, ltiEvents, KEYS, str) {\n\n    var SELECTORS = {\n        DELETE_BUTTON: '.delete',\n        NAME_ELEMENT: '.name',\n        DESCRIPTION_ELEMENT: '.description',\n        CAPABILITIES_CONTAINER: '.capabilities-container',\n        ACTIVATE_BUTTON: '.tool-card-footer a.activate',\n    };\n\n    // Timeout in seconds.\n    var ANNOUNCEMENT_TIMEOUT = 2000;\n\n    /**\n     * Return the delete button element.\n     *\n     * @method getDeleteButton\n     * @private\n     * @param {JQuery} element jQuery object representing the tool card.\n     * @return {JQuery} jQuery object\n     */\n    var getDeleteButton = function(element) {\n        return element.find(SELECTORS.DELETE_BUTTON);\n    };\n\n    /**\n     * Return the element representing the tool type name.\n     *\n     * @method getNameElement\n     * @private\n     * @param {JQuery} element jQuery object representing the tool card.\n     * @return {JQuery} jQuery object\n     */\n    var getNameElement = function(element) {\n        return element.find(SELECTORS.NAME_ELEMENT);\n    };\n\n    /**\n     * Return the element representing the tool type description.\n     *\n     * @method getDescriptionElement\n     * @private\n     * @param {JQuery} element jQuery object representing the tool card.\n     * @return {JQuery} jQuery object\n     */\n    var getDescriptionElement = function(element) {\n        return element.find(SELECTORS.DESCRIPTION_ELEMENT);\n    };\n\n    /**\n     * Return the activate button for the type.\n     *\n     * @method getActivateButton\n     * @private\n     * @param {Object} element jQuery object representing the tool card.\n     * @return {Object} jQuery object\n     */\n    var getActivateButton = function(element) {\n        return element.find(SELECTORS.ACTIVATE_BUTTON);\n    };\n\n    /**\n     * Checks if the type card has an activate button.\n     *\n     * @method hasActivateButton\n     * @private\n     * @param {JQuery} element jQuery object representing the tool card.\n     * @return {Boolean} true if has active buton\n     */\n    var hasActivateButton = function(element) {\n        return getActivateButton(element).length ? true : false;\n    };\n\n    /**\n     * Return the element that contains the capabilities approval for\n     * the user.\n     *\n     * @method getCapabilitiesContainer\n     * @private\n     * @param {Object} element jQuery object representing the tool card.\n     * @return {Object} The element\n     */\n    var getCapabilitiesContainer = function(element) {\n        return element.find(SELECTORS.CAPABILITIES_CONTAINER);\n    };\n\n    /**\n     * Checks if the tool type has capabilities that need approval. If it\n     * does then the container will be present.\n     *\n     * @method hasCapabilitiesContainer\n     * @private\n     * @param {JQuery} element jQuery object representing the tool card.\n     * @return {Boolean} true if has capbilities.\n     */\n    var hasCapabilitiesContainer = function(element) {\n        return getCapabilitiesContainer(element).length ? true : false;\n    };\n\n    /**\n     * Get the type id.\n     *\n     * @method getTypeId\n     * @private\n     * @param {Object} element jQuery object representing the tool card.\n     * @return {String} Type ID\n     */\n    var getTypeId = function(element) {\n        return element.attr('data-type-id');\n    };\n\n    /**\n     * Stop any announcement currently visible on the card.\n     *\n     * @method clearAllAnnouncements\n     * @private\n     * @param {JQuery} element jQuery object representing the tool card.\n     */\n    var clearAllAnnouncements = function(element) {\n        element.removeClass('announcement loading success fail capabilities');\n    };\n\n    /**\n     * Show the loading announcement.\n     *\n     * @method startLoading\n     * @private\n     * @param {JQuery} element jQuery object representing the tool card.\n     */\n    var startLoading = function(element) {\n        clearAllAnnouncements(element);\n        element.addClass('announcement loading');\n    };\n\n    /**\n     * Hide the loading announcement.\n     *\n     * @method stopLoading\n     * @private\n     * @param {JQuery} element jQuery object representing the tool card.\n     */\n    var stopLoading = function(element) {\n        element.removeClass('announcement loading');\n    };\n\n    /**\n     * Show the success announcement. The announcement is only\n     * visible for 2 seconds.\n     *\n     * @method announceSuccess\n     * @private\n     * @param {JQuery} element jQuery object representing the tool card.\n     * @return {Promise} jQuery Deferred object\n     */\n    var announceSuccess = function(element) {\n        var promise = $.Deferred();\n\n        clearAllAnnouncements(element);\n        element.addClass('announcement success');\n        setTimeout(function() {\n            element.removeClass('announcement success');\n            promise.resolve();\n        }, ANNOUNCEMENT_TIMEOUT);\n\n        return promise;\n    };\n\n    /**\n     * Show the failure announcement. The announcement is only\n     * visible for 2 seconds.\n     *\n     * @method announceFailure\n     * @private\n     * @param {JQuery} element jQuery object representing the tool card.\n     * @return {Promise} jQuery Deferred object\n     */\n    var announceFailure = function(element) {\n        var promise = $.Deferred();\n\n        clearAllAnnouncements(element);\n        element.addClass('announcement fail');\n        setTimeout(function() {\n            element.removeClass('announcement fail');\n            promise.resolve();\n        }, ANNOUNCEMENT_TIMEOUT);\n\n        return promise;\n    };\n\n    /**\n     * Delete the tool type from the Moodle server. Triggers a success\n     * or failure announcement depending on the result.\n     *\n     * @method deleteType\n     * @private\n     * @param {JQuery} element jQuery object representing the tool card.\n     * @return {Promise} jQuery Deferred object\n     */\n    var deleteType = function(element) {\n        var promise = $.Deferred();\n        var typeId = getTypeId(element);\n        startLoading(element);\n\n        if (typeId === \"\") {\n            return $.Deferred().resolve();\n        }\n\n        str.get_strings([\n                {\n                    key: 'delete',\n                    component: 'mod_lti'\n                },\n                {\n                    key: 'delete_confirmation',\n                    component: 'mod_lti'\n                },\n                {\n                    key: 'delete',\n                    component: 'mod_lti'\n                },\n                {\n                    key: 'cancel',\n                    component: 'core'\n                },\n            ])\n            .done(function(strs) {\n                    notification.confirm(strs[0], strs[1], strs[2], strs[3], function() {\n                            toolType.delete(typeId)\n                                .done(function() {\n                                        stopLoading(element);\n                                        announceSuccess(element)\n                                            .done(function() {\n                                                    element.remove();\n                                                })\n                                            .fail(notification.exception)\n                                            .always(function() {\n                                                    // Always resolve because even if the announcement fails the type was deleted.\n                                                    promise.resolve();\n                                                });\n                                    })\n                                .fail(function(error) {\n                                        announceFailure(element);\n                                        promise.reject(error);\n                                    });\n                        }, function() {\n                                stopLoading(element);\n                                promise.resolve();\n                            });\n                })\n            .fail(function(error) {\n                    stopLoading(element);\n                    notification.exception(error);\n                    promise.reject(error);\n                });\n\n        return promise;\n    };\n\n    /**\n     * Save a given value in a data attribute on the element.\n     *\n     * @method setValueSnapshot\n     * @private\n     * @param {JQuery} element jQuery object representing the element.\n     * @param {String} value to be saved.\n     */\n    var setValueSnapshot = function(element, value) {\n        element.attr('data-val-snapshot', value);\n    };\n\n    /**\n     * Return the saved value from the element.\n     *\n     * @method getValueSnapshot\n     * @private\n     * @param {JQuery} element jQuery object representing the element.\n     * @return {String} the saved value.\n     */\n    var getValueSnapshot = function(element) {\n        return element.attr('data-val-snapshot');\n    };\n\n    /**\n     * Save the current value of the tool description.\n     *\n     * @method snapshotDescription\n     * @private\n     * @param {JQuery} element jQuery object representing the tool card.\n     */\n    var snapshotDescription = function(element) {\n        var descriptionElement = getDescriptionElement(element);\n\n        if (descriptionElement.hasClass('loading')) {\n            return;\n        }\n\n        var description = descriptionElement.text().trim();\n        setValueSnapshot(descriptionElement, description);\n    };\n\n    /**\n     * Send a request to update the description value for this tool\n     * in the Moodle server.\n     *\n     * @method updateDescription\n     * @private\n     * @param {JQuery} element jQuery object representing the tool card.\n     * @return {Promise} jQuery Deferred object\n     */\n    var updateDescription = function(element) {\n        var typeId = getTypeId(element);\n\n        // Return early if we don't have an id because it's\n        // required to save the changes.\n        if (typeId === \"\") {\n            return $.Deferred().resolve();\n        }\n\n        var descriptionElement = getDescriptionElement(element);\n\n        // Return early if we're already saving a value.\n        if (descriptionElement.hasClass('loading')) {\n            return $.Deferred().resolve();\n        }\n\n        var description = descriptionElement.text().trim();\n        var snapshotVal = getValueSnapshot(descriptionElement);\n\n        // If the value hasn't change then don't bother sending the\n        // update request.\n        if (snapshotVal == description) {\n            return $.Deferred().resolve();\n        }\n\n        descriptionElement.addClass('loading');\n\n        var promise = toolType.update({id: typeId, description: description});\n\n        promise.done(function(type) {\n            descriptionElement.removeClass('loading');\n            // Make sure the text is updated with the description from the\n            // server, just in case the update didn't work.\n            descriptionElement.text(type.description);\n        }).fail(notification.exception);\n\n        // Probably need to handle failures better so that we can revert\n        // the value in the input for the user.\n        promise.fail(function() {\n          descriptionElement.removeClass('loading');\n        });\n\n        return promise;\n    };\n\n    /**\n     * Save the current value of the tool name.\n     *\n     * @method snapshotName\n     * @private\n     * @param {JQuery} element jQuery object representing the tool card.\n     */\n    var snapshotName = function(element) {\n        var nameElement = getNameElement(element);\n\n        if (nameElement.hasClass('loading')) {\n            return;\n        }\n\n        var name = nameElement.text().trim();\n        setValueSnapshot(nameElement, name);\n    };\n\n    /**\n     * Send a request to update the name value for this tool\n     * in the Moodle server.\n     *\n     * @method updateName\n     * @private\n     * @param {JQuery} element jQuery object representing the tool card.\n     * @return {Promise} jQuery Deferred object\n     */\n    var updateName = function(element) {\n        var typeId = getTypeId(element);\n\n        // Return if we don't have an id.\n        if (typeId === \"\") {\n            return $.Deferred().resolve();\n        }\n\n        var nameElement = getNameElement(element);\n\n        // Return if we're already saving.\n        if (nameElement.hasClass('loading')) {\n            return $.Deferred().resolve();\n        }\n\n        var name = nameElement.text().trim();\n        var snapshotVal = getValueSnapshot(nameElement);\n\n        // If the value hasn't change then don't bother sending the\n        // update request.\n        if (snapshotVal == name) {\n            return $.Deferred().resolve();\n        }\n\n        nameElement.addClass('loading');\n        var promise = toolType.update({id: typeId, name: name});\n\n        promise.done(function(type) {\n            nameElement.removeClass('loading');\n            // Make sure the text is updated with the name from the\n            // server, just in case the update didn't work.\n            nameElement.text(type.name);\n        });\n\n        // Probably need to handle failures better so that we can revert\n        // the value in the input for the user.\n        promise.fail(function() {\n          nameElement.removeClass('loading');\n        });\n\n        return promise;\n    };\n\n    /**\n     * Send a request to update the state for this tool to be configured (active)\n     * in the Moodle server. A success or failure announcement is triggered depending\n     * on the result.\n     *\n     * @method setStatusActive\n     * @private\n     * @param {JQuery} element jQuery object representing the tool card.\n     * @return {Promise} jQuery Deferred object\n     */\n    var setStatusActive = function(element) {\n        var id = getTypeId(element);\n\n        // Return if we don't have an id.\n        if (id === \"\") {\n            return $.Deferred().resolve();\n        }\n\n        startLoading(element);\n\n        var promise = toolType.update({\n            id: id,\n            state: toolType.constants.state.configured\n        });\n\n        promise.then(function(toolTypeData) {\n            stopLoading(element);\n            announceSuccess(element);\n            return toolTypeData;\n        }).then(function(toolTypeData) {\n            return templates.render('mod_lti/tool_card', toolTypeData);\n        }).then(function(html, js) {\n            templates.replaceNode(element, html, js);\n            return;\n        }).catch(function() {\n            stopLoading(element);\n            announceFailure(element);\n        });\n\n        return promise;\n    };\n\n    /**\n     * Show the capabilities approval screen to show which groups of data this\n     * type requires access to in Moodle (if any).\n     *\n     * @method displayCapabilitiesApproval\n     * @private\n     * @param {JQuery} element jQuery object representing the tool card.\n     */\n    var displayCapabilitiesApproval = function(element) {\n        element.addClass('announcement capabilities');\n    };\n\n    /**\n     * Hide the capabilities approval screen.\n     *\n     * @method hideCapabilitiesApproval\n     * @private\n     * @param {JQuery} element jQuery object representing the tool card.\n     */\n    var hideCapabilitiesApproval = function(element) {\n        element.removeClass('announcement capabilities');\n    };\n\n    /**\n     * The user wishes to activate this tool so show them the capabilities that\n     * they need to agree to or if there are none then set the tool type's state\n     * to active.\n     *\n     * @method activateToolType\n     * @private\n     * @param {JQuery} element jQuery object representing the tool card.\n     */\n    var activateToolType = function(element) {\n        if (hasCapabilitiesContainer(element)) {\n            displayCapabilitiesApproval(element);\n        } else {\n            setStatusActive(element);\n        }\n    };\n\n    /**\n     * Sets up the listeners for user interaction on this tool type card.\n     *\n     * @method registerEventListeners\n     * @private\n     * @param {JQuery} element jQuery object representing the tool card.\n     */\n    var registerEventListeners = function(element) {\n        var deleteButton = getDeleteButton(element);\n        deleteButton.click(function(e) {\n            e.preventDefault();\n            deleteType(element);\n        });\n        deleteButton.keypress(function(e) {\n            if (!e.metaKey && !e.shiftKey && !e.altKey && !e.ctrlKey) {\n                if (e.keyCode == KEYS.ENTER || e.keyCode == KEYS.SPACE) {\n                    e.preventDefault();\n                    deleteButton.click();\n                }\n            }\n        });\n\n        var descriptionElement = getDescriptionElement(element);\n        descriptionElement.focus(function(e) {\n            e.preventDefault();\n            // Save a copy of the current value for the description so that\n            // we can check if the user has changed it before sending a request to\n            // the server.\n            snapshotDescription(element);\n        });\n        descriptionElement.blur(function(e) {\n            e.preventDefault();\n            updateDescription(element);\n        });\n        descriptionElement.keypress(function(e) {\n            if (!e.metaKey && !e.shiftKey && !e.altKey && !e.ctrlKey) {\n                if (e.keyCode == KEYS.ENTER) {\n                    e.preventDefault();\n                    descriptionElement.blur();\n                }\n            }\n        });\n\n        var nameElement = getNameElement(element);\n        nameElement.focus(function(e) {\n            e.preventDefault();\n            // Save a copy of the current value for the name so that\n            // we can check if the user has changed it before sending a request to\n            // the server.\n            snapshotName(element);\n        });\n        nameElement.blur(function(e) {\n            e.preventDefault();\n            updateName(element);\n        });\n        nameElement.keypress(function(e) {\n            if (!e.metaKey && !e.shiftKey && !e.altKey && !e.ctrlKey) {\n                if (e.keyCode == KEYS.ENTER) {\n                    e.preventDefault();\n                    nameElement.blur();\n                }\n            }\n        });\n\n        // Only pending tool type cards have an activate button.\n        if (hasActivateButton(element)) {\n            var activateButton = getActivateButton(element);\n            activateButton.click(function(e) {\n                e.preventDefault();\n                activateToolType(element);\n            });\n            activateButton.keypress(function(e) {\n                if (!e.metaKey && !e.shiftKey && !e.altKey && !e.ctrlKey) {\n                    if (e.keyCode == KEYS.ENTER || e.keyCode == KEYS.SPACE) {\n                        e.preventDefault();\n                        activateButton.click();\n                    }\n                }\n            });\n        }\n\n        if (hasCapabilitiesContainer(element)) {\n            var capabilitiesContainer = getCapabilitiesContainer(element);\n\n            capabilitiesContainer.on(ltiEvents.CAPABILITIES_AGREE, function() {\n                setStatusActive(element);\n            });\n\n            capabilitiesContainer.on(ltiEvents.CAPABILITIES_DECLINE, function() {\n                hideCapabilitiesApproval(element);\n            });\n        }\n    };\n\n    /**\n     * Sets up the templates for the tool configuration modal on this tool type card.\n     *\n     * @method registerModal\n     * @private\n     * @param {JQuery} element jQuery object representing the tool card.\n     */\n    var registerModal = function(element) {\n        const configurationLink = element.find('#' + element.data('uniqid') + '-' + element.data('deploymentid'));\n        if (!configurationLink.length) {\n            return;\n        }\n        const trigger = configurationLink.get(0);\n        trigger.addEventListener('click', (e) => {\n            e.preventDefault();\n            var context = {\n                'uniqid': element.data('uniqid'),\n                'platformid': element.data('platformid'),\n                'clientid': element.data('clientid'),\n                'deploymentid': element.data('deploymentid'),\n                'urls': {\n                    'publickeyset': element.data('publickeyseturl'),\n                    'accesstoken': element.data('accesstokenurl'),\n                    'authrequest': element.data('authrequesturl')\n                }\n            };\n            var bodyPromise = templates.render('mod_lti/tool_config_modal_body', context);\n            var mailTo = 'mailto:?subject=' + encodeURIComponent(element.data('mailtosubject')) +\n                '&body=' + encodeURIComponent(element.data('platformidstr')) + ':%20' +\n                encodeURIComponent(element.data('platformid')) + '%0D%0A' +\n                encodeURIComponent(element.data('clientidstr')) + ':%20' +\n                encodeURIComponent(element.data('clientid')) + '%0D%0A' +\n                encodeURIComponent(element.data('deploymentidstr')) + ':%20' +\n                encodeURIComponent(element.data('deploymentid')) + '%0D%0A' +\n                encodeURIComponent(element.data('publickeyseturlstr')) + ':%20' +\n                encodeURIComponent(element.data('publickeyseturl')) + '%0D%0A' +\n                encodeURIComponent(element.data('accesstokenurlstr')) + ':%20' +\n                encodeURIComponent(element.data('accesstokenurl')) + '%0D%0A' +\n                encodeURIComponent(element.data('authrequesturlstr')) + ':%20' +\n                encodeURIComponent(element.data('authrequesturl')) + '%0D%0A';\n            context = {\n                'mailto': mailTo\n            };\n            var footerPromise = templates.render('mod_lti/tool_config_modal_footer', context);\n            Modal.create({\n                large: true,\n                title: element.data('modaltitle'),\n                body: bodyPromise,\n                footer: footerPromise,\n                show: true\n            });\n        });\n    };\n\n    return /** @alias module:mod_lti/tool_card_controller */ {\n\n        /**\n         * Initialise this module.\n         *\n         * @param {JQuery} element jQuery object representing the tool card.\n         */\n        init: function(element) {\n            registerEventListeners(element);\n            registerModal(element);\n        }\n    };\n});\n"],"names":["define","$","ajax","notification","templates","Modal","toolType","ltiEvents","KEYS","str","SELECTORS","getNameElement","element","find","getDescriptionElement","getActivateButton","getCapabilitiesContainer","hasCapabilitiesContainer","length","getTypeId","attr","clearAllAnnouncements","removeClass","startLoading","addClass","stopLoading","announceSuccess","promise","Deferred","setTimeout","resolve","announceFailure","setValueSnapshot","value","getValueSnapshot","setStatusActive","id","update","state","constants","configured","then","toolTypeData","render","html","js","replaceNode","catch","registerEventListeners","deleteButton","getDeleteButton","click","e","preventDefault","typeId","get_strings","key","component","done","strs","confirm","delete","remove","fail","exception","always","error","reject","deleteType","keypress","metaKey","shiftKey","altKey","ctrlKey","keyCode","ENTER","SPACE","descriptionElement","focus","hasClass","description","text","trim","snapshotDescription","blur","type","updateDescription","nameElement","name","snapshotName","updateName","hasActivateButton","activateButton","displayCapabilitiesApproval","activateToolType","capabilitiesContainer","on","CAPABILITIES_AGREE","CAPABILITIES_DECLINE","hideCapabilitiesApproval","init","configurationLink","data","get","addEventListener","context","bodyPromise","encodeURIComponent","footerPromise","create","large","title","body","footer","show","registerModal"],"mappings":";;;;;;;;;;;AA0BCA,sCAAO,CAAC,SAAU,YAAa,oBAAqB,iBAAkB,aAC/D,oBAAqB,iBAAkB,eACvC,aACA,SAASC,EAAGC,KAAMC,aAAcC,UAAWC,MAAOC,SAAUC,UAAWC,KAAMC,SAE7EC,wBACe,UADfA,uBAEc,QAFdA,8BAGqB,eAHrBA,iCAIwB,0BAJxBA,0BAKiB,+BA0BjBC,eAAiB,SAASC,gBACnBA,QAAQC,KAAKH,yBAWpBI,sBAAwB,SAASF,gBAC1BA,QAAQC,KAAKH,gCAWpBK,kBAAoB,SAASH,gBACtBA,QAAQC,KAAKH,4BAwBpBM,yBAA2B,SAASJ,gBAC7BA,QAAQC,KAAKH,mCAYpBO,yBAA2B,SAASL,iBAC7BI,yBAAyBJ,SAASM,QAWzCC,UAAY,SAASP,gBACdA,QAAQQ,KAAK,iBAUpBC,sBAAwB,SAAST,SACjCA,QAAQU,YAAY,mDAUpBC,aAAe,SAASX,SACxBS,sBAAsBT,SACtBA,QAAQY,SAAS,yBAUjBC,YAAc,SAASb,SACvBA,QAAQU,YAAY,yBAYpBI,gBAAkB,SAASd,aACvBe,QAAU1B,EAAE2B,kBAEhBP,sBAAsBT,SACtBA,QAAQY,SAAS,wBACjBK,YAAW,WACPjB,QAAQU,YAAY,wBACpBK,QAAQG,YAtJW,KAyJhBH,SAYPI,gBAAkB,SAASnB,aACvBe,QAAU1B,EAAE2B,kBAEhBP,sBAAsBT,SACtBA,QAAQY,SAAS,qBACjBK,YAAW,WACPjB,QAAQU,YAAY,qBACpBK,QAAQG,YA5KW,KA+KhBH,SAgFPK,iBAAmB,SAASpB,QAASqB,OACrCrB,QAAQQ,KAAK,oBAAqBa,QAWlCC,iBAAmB,SAAStB,gBACrBA,QAAQQ,KAAK,sBA2JpBe,gBAAkB,SAASvB,aACvBwB,GAAKjB,UAAUP,YAGR,KAAPwB,UACOnC,EAAE2B,WAAWE,UAGxBP,aAAaX,aAETe,QAAUrB,SAAS+B,OAAO,CAC1BD,GAAIA,GACJE,MAAOhC,SAASiC,UAAUD,MAAME,oBAGpCb,QAAQc,MAAK,SAASC,qBAClBjB,YAAYb,SACZc,gBAAgBd,SACT8B,gBACRD,MAAK,SAASC,qBACNtC,UAAUuC,OAAO,oBAAqBD,iBAC9CD,MAAK,SAASG,KAAMC,IACnBzC,UAAU0C,YAAYlC,QAASgC,KAAMC,OAEtCE,OAAM,WACLtB,YAAYb,SACZmB,gBAAgBnB,YAGbe,SAkDPqB,uBAAyB,SAASpC,aAC9BqC,aA7ec,SAASrC,gBACpBA,QAAQC,KAAKH,yBA4eDwC,CAAgBtC,SACnCqC,aAAaE,OAAM,SAASC,GACxBA,EAAEC,iBA9TO,SAASzC,aAClBe,QAAU1B,EAAE2B,WACZ0B,OAASnC,UAAUP,SACvBW,aAAaX,SAEE,KAAX0C,OACOrD,EAAE2B,WAAWE,UAGxBrB,IAAI8C,YAAY,CACR,CACIC,IAAK,SACLC,UAAW,WAEf,CACID,IAAK,sBACLC,UAAW,WAEf,CACID,IAAK,SACLC,UAAW,WAEf,CACID,IAAK,SACLC,UAAW,UAGlBC,MAAK,SAASC,MACPxD,aAAayD,QAAQD,KAAK,GAAIA,KAAK,GAAIA,KAAK,GAAIA,KAAK,IAAI,WACjDrD,SAASuD,OAAOP,QACXI,MAAK,WACEjC,YAAYb,SACZc,gBAAgBd,SACX8C,MAAK,WACE9C,QAAQkD,YAEfC,KAAK5D,aAAa6D,WAClBC,QAAO,WAEAtC,QAAQG,gBAG3BiC,MAAK,SAASG,OACPnC,gBAAgBnB,SAChBe,QAAQwC,OAAOD,aAE5B,WACKzC,YAAYb,SACZe,QAAQG,gBAG3BiC,MAAK,SAASG,OACPzC,YAAYb,SACZT,aAAa6D,UAAUE,OACvBvC,QAAQwC,OAAOD,UAyQvBE,CAAWxD,YAEfqC,aAAaoB,UAAS,SAASjB,GACtBA,EAAEkB,SAAYlB,EAAEmB,UAAanB,EAAEoB,QAAWpB,EAAEqB,SACzCrB,EAAEsB,SAAWlE,KAAKmE,OAASvB,EAAEsB,SAAWlE,KAAKoE,QAC7CxB,EAAEC,iBACFJ,aAAaE,gBAKrB0B,mBAAqB/D,sBAAsBF,SAC/CiE,mBAAmBC,OAAM,SAAS1B,GAC9BA,EAAEC,iBAjPgB,SAASzC,aAC3BiE,mBAAqB/D,sBAAsBF,aAE3CiE,mBAAmBE,SAAS,gBAI5BC,YAAcH,mBAAmBI,OAAOC,OAC5ClD,iBAAiB6C,mBAAoBG,cA6OjCG,CAAoBvE,YAExBiE,mBAAmBO,MAAK,SAAShC,GAC7BA,EAAEC,iBApOc,SAASzC,aACzB0C,OAASnC,UAAUP,YAIR,KAAX0C,cACOrD,EAAE2B,WAAWE,cAGpB+C,mBAAqB/D,sBAAsBF,YAG3CiE,mBAAmBE,SAAS,kBACrB9E,EAAE2B,WAAWE,cAGpBkD,YAAcH,mBAAmBI,OAAOC,UAC1BhD,iBAAiB2C,qBAIhBG,mBACR/E,EAAE2B,WAAWE,UAGxB+C,mBAAmBrD,SAAS,eAExBG,QAAUrB,SAAS+B,OAAO,CAACD,GAAIkB,OAAQ0B,YAAaA,cAExDrD,QAAQ+B,MAAK,SAAS2B,MAClBR,mBAAmBvD,YAAY,WAG/BuD,mBAAmBI,KAAKI,KAAKL,gBAC9BjB,KAAK5D,aAAa6D,WAIrBrC,QAAQoC,MAAK,WACXc,mBAAmBvD,YAAY,cA8L7BgE,CAAkB1E,YAEtBiE,mBAAmBR,UAAS,SAASjB,GAC5BA,EAAEkB,SAAYlB,EAAEmB,UAAanB,EAAEoB,QAAWpB,EAAEqB,SACzCrB,EAAEsB,SAAWlE,KAAKmE,QAClBvB,EAAEC,iBACFwB,mBAAmBO,eAK3BG,YAAc5E,eAAeC,YACjC2E,YAAYT,OAAM,SAAS1B,GACvBA,EAAEC,iBA9LS,SAASzC,aACpB2E,YAAc5E,eAAeC,aAE7B2E,YAAYR,SAAS,gBAIrBS,KAAOD,YAAYN,OAAOC,OAC9BlD,iBAAiBuD,YAAaC,OA0L1BC,CAAa7E,YAEjB2E,YAAYH,MAAK,SAAShC,GACtBA,EAAEC,iBAjLO,SAASzC,aAClB0C,OAASnC,UAAUP,YAGR,KAAX0C,cACOrD,EAAE2B,WAAWE,cAGpByD,YAAc5E,eAAeC,YAG7B2E,YAAYR,SAAS,kBACd9E,EAAE2B,WAAWE,cAGpB0D,KAAOD,YAAYN,OAAOC,UACZhD,iBAAiBqD,cAIhBC,YACRvF,EAAE2B,WAAWE,UAGxByD,YAAY/D,SAAS,eACjBG,QAAUrB,SAAS+B,OAAO,CAACD,GAAIkB,OAAQkC,KAAMA,OAEjD7D,QAAQ+B,MAAK,SAAS2B,MAClBE,YAAYjE,YAAY,WAGxBiE,YAAYN,KAAKI,KAAKG,SAK1B7D,QAAQoC,MAAK,WACXwB,YAAYjE,YAAY,cA6ItBoE,CAAW9E,YAEf2E,YAAYlB,UAAS,SAASjB,GACrBA,EAAEkB,SAAYlB,EAAEmB,UAAanB,EAAEoB,QAAWpB,EAAEqB,SACzCrB,EAAEsB,SAAWlE,KAAKmE,QAClBvB,EAAEC,iBACFkC,YAAYH,WAhfJ,SAASxE,iBACtBG,kBAAkBH,SAASM,OAqf9ByE,CAAkB/E,SAAU,KACxBgF,eAAiB7E,kBAAkBH,SACvCgF,eAAezC,OAAM,SAASC,GAC1BA,EAAEC,iBA5ES,SAASzC,SACxBK,yBAAyBL,SAzBC,SAASA,SACvCA,QAAQY,SAAS,6BAyBbqE,CAA4BjF,SAE5BuB,gBAAgBvB,SAyEZkF,CAAiBlF,YAErBgF,eAAevB,UAAS,SAASjB,GACxBA,EAAEkB,SAAYlB,EAAEmB,UAAanB,EAAEoB,QAAWpB,EAAEqB,SACzCrB,EAAEsB,SAAWlE,KAAKmE,OAASvB,EAAEsB,SAAWlE,KAAKoE,QAC7CxB,EAAEC,iBACFuC,eAAezC,eAM3BlC,yBAAyBL,SAAU,KAC/BmF,sBAAwB/E,yBAAyBJ,SAErDmF,sBAAsBC,GAAGzF,UAAU0F,oBAAoB,WACnD9D,gBAAgBvB,YAGpBmF,sBAAsBC,GAAGzF,UAAU2F,sBAAsB,YA7GlC,SAAStF,SACpCA,QAAQU,YAAY,6BA6GZ6E,CAAyBvF,oBA2DoB,CAOrDwF,KAAM,SAASxF,SACXoC,uBAAuBpC,SAvDX,SAASA,eACnByF,kBAAoBzF,QAAQC,KAAK,IAAMD,QAAQ0F,KAAK,UAAY,IAAM1F,QAAQ0F,KAAK,qBACpFD,kBAAkBnF,cAGPmF,kBAAkBE,IAAI,GAC9BC,iBAAiB,SAAUpD,IAC/BA,EAAEC,qBACEoD,QAAU,QACA7F,QAAQ0F,KAAK,qBACT1F,QAAQ0F,KAAK,uBACf1F,QAAQ0F,KAAK,yBACT1F,QAAQ0F,KAAK,qBACrB,cACY1F,QAAQ0F,KAAK,+BACd1F,QAAQ0F,KAAK,8BACb1F,QAAQ0F,KAAK,oBAGhCI,YAActG,UAAUuC,OAAO,iCAAkC8D,SAcrEA,QAAU,QAbG,mBAAqBE,mBAAmB/F,QAAQ0F,KAAK,kBAC9D,SAAWK,mBAAmB/F,QAAQ0F,KAAK,kBAAoB,OAC/DK,mBAAmB/F,QAAQ0F,KAAK,eAAiB,SACjDK,mBAAmB/F,QAAQ0F,KAAK,gBAAkB,OAClDK,mBAAmB/F,QAAQ0F,KAAK,aAAe,SAC/CK,mBAAmB/F,QAAQ0F,KAAK,oBAAsB,OACtDK,mBAAmB/F,QAAQ0F,KAAK,iBAAmB,SACnDK,mBAAmB/F,QAAQ0F,KAAK,uBAAyB,OACzDK,mBAAmB/F,QAAQ0F,KAAK,oBAAsB,SACtDK,mBAAmB/F,QAAQ0F,KAAK,sBAAwB,OACxDK,mBAAmB/F,QAAQ0F,KAAK,mBAAqB,SACrDK,mBAAmB/F,QAAQ0F,KAAK,sBAAwB,OACxDK,mBAAmB/F,QAAQ0F,KAAK,mBAAqB,cAIrDM,cAAgBxG,UAAUuC,OAAO,mCAAoC8D,SACzEpG,MAAMwG,OAAO,CACTC,OAAO,EACPC,MAAOnG,QAAQ0F,KAAK,cACpBU,KAAMN,YACNO,OAAQL,cACRM,MAAM,OAcVC,CAAcvG"}