Proyectos de Subversion Moodle

Rev

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

{"version":3,"file":"dynamic_tabs.min.js","sources":["../src/dynamic_tabs.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 * Dynamic Tabs UI element with AJAX loading of tabs content\n *\n * @module      core/dynamic_tabs\n * @copyright   2021 David Matamoros <davidmc@moodle.com> based on code from Marina Glancy\n * @license     http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport $ from 'jquery';\nimport Templates from 'core/templates';\nimport {addIconToContainer} from 'core/loadingicon';\nimport Notification from 'core/notification';\nimport Pending from 'core/pending';\nimport {getStrings} from 'core/str';\nimport {getContent} from 'core/local/repository/dynamic_tabs';\nimport {isAnyWatchedFormDirty, resetAllFormDirtyStates} from 'core_form/changechecker';\n\nconst SELECTORS = {\n    dynamicTabs: '.dynamictabs',\n    activeTab: '.dynamictabs .nav-link.active',\n    allActiveTabs: '.dynamictabs .nav-link[data-toggle=\"tab\"]:not(.disabled)',\n    tabContent: '.dynamictabs .tab-pane [data-tab-content]',\n    tabToggle: 'a[data-toggle=\"tab\"]',\n    tabPane: '.dynamictabs .tab-pane',\n};\n\nSELECTORS.forTabName = tabName => `.dynamictabs [data-tab-content=\"${tabName}\"]`;\nSELECTORS.forTabId = tabName => `.dynamictabs [data-toggle=\"tab\"][href=\"#${tabName}\"]`;\n\n/**\n * Initialises the tabs view on the page (only one tabs view per page is supported)\n */\nexport const init = () => {\n    const tabToggle = $(SELECTORS.tabToggle);\n\n    // Listen to click, warn user if they are navigating away with unsaved form changes.\n    tabToggle.on('click', (event) => {\n        if (!isAnyWatchedFormDirty()) {\n            return;\n        }\n\n        event.preventDefault();\n        event.stopPropagation();\n\n        getStrings([\n            {key: 'changesmade', component: 'moodle'},\n            {key: 'changesmadereallygoaway', component: 'moodle'},\n            {key: 'confirm', component: 'moodle'},\n        ]).then(([strChangesMade, strChangesMadeReally, strConfirm]) =>\n            // Reset form dirty state on confirmation, re-trigger the event.\n            Notification.confirm(strChangesMade, strChangesMadeReally, strConfirm, null, () => {\n                resetAllFormDirtyStates();\n                $(event.target).trigger(event.type);\n            })\n        ).catch(Notification.exception);\n    });\n\n    // This code listens to Bootstrap events 'show.bs.tab' and 'shown.bs.tab' which is triggered using JQuery and\n    // can not be converted yet to native events.\n    tabToggle\n        .on('show.bs.tab', function() {\n            // Clean content from previous tab.\n            const previousTabName = getActiveTabName();\n            if (previousTabName) {\n                const previousTab = document.querySelector(SELECTORS.forTabName(previousTabName));\n                previousTab.textContent = '';\n            }\n        })\n        .on('shown.bs.tab', function() {\n            const tab = $($(this).attr('href'));\n            if (tab.length !== 1) {\n                return;\n            }\n            loadTab(tab.attr('id'));\n        });\n\n    if (!openTabFromHash()) {\n        const tabs = document.querySelector(SELECTORS.allActiveTabs);\n        if (tabs) {\n            openTab(tabs.getAttribute('aria-controls'));\n        } else {\n            // We may hide tabs if there is only one available, just load the contents of the first tab.\n            const tabPane = document.querySelector(SELECTORS.tabPane);\n            if (tabPane) {\n                tabPane.classList.add('active', 'show');\n                loadTab(tabPane.getAttribute('id'));\n            }\n        }\n    }\n};\n\n/**\n * Returns id/name of the currently active tab\n *\n * @return {String|null}\n */\nconst getActiveTabName = () => {\n    const element = document.querySelector(SELECTORS.activeTab);\n    return element?.getAttribute('aria-controls') || null;\n};\n\n/**\n * Returns the id/name of the first tab\n *\n * @return {String|null}\n */\nconst getFirstTabName = () => {\n    const element = document.querySelector(SELECTORS.tabContent);\n    return element?.dataset.tabContent || null;\n};\n\n/**\n * Loads contents of a tab using an AJAX request\n *\n * @param {String} tabName\n */\nconst loadTab = (tabName) => {\n    // If tabName is not specified find the active tab, or if is not defined, the first available tab.\n    tabName = tabName ?? getActiveTabName() ?? getFirstTabName();\n    const tab = document.querySelector(SELECTORS.forTabName(tabName));\n    if (!tab) {\n        return;\n    }\n\n    const pendingPromise = new Pending('core/dynamic_tabs:loadTab:' + tabName);\n\n    addIconToContainer(tab)\n    .then(() => {\n        let tabArgs = {...tab.dataset};\n        delete tabArgs.tabClass;\n        delete tabArgs.tabContent;\n        return getContent(tab.dataset.tabClass, JSON.stringify(tabArgs));\n    })\n    .then(response => Promise.all([\n        $.parseHTML(response.javascript, null, true).map(node => node.innerHTML).join(\"\\n\"),\n        Templates.renderForPromise(response.template, JSON.parse(response.content)),\n    ]))\n    .then(([responseJs, {html, js}]) => Templates.replaceNodeContents(tab, html, js + responseJs))\n    .then(() => pendingPromise.resolve())\n    .catch(Notification.exception);\n};\n\n/**\n * Return the tab given the tab name\n *\n * @param {String} tabName\n * @return {HTMLElement}\n */\nconst getTab = (tabName) => {\n    return document.querySelector(SELECTORS.forTabId(tabName));\n};\n\n/**\n * Return the tab pane given the tab name\n *\n * @param {String} tabName\n * @return {HTMLElement}\n */\nconst getTabPane = (tabName) => {\n    return document.getElementById(tabName);\n};\n\n/**\n * Open the tab on page load. If this script loads before theme_boost/tab we need to open tab ourselves\n *\n * @param {String} tabName\n * @return {Boolean}\n */\nconst openTab = (tabName) => {\n    const tab = getTab(tabName);\n    if (!tab) {\n        return false;\n    }\n\n    loadTab(tabName);\n    tab.classList.add('active');\n    getTabPane(tabName).classList.add('active', 'show');\n    return true;\n};\n\n/**\n * If there is a location hash that is the same as the tab name - open this tab.\n *\n * @return {Boolean}\n */\nconst openTabFromHash = () => {\n    const hash = document.location.hash;\n    if (hash.match(/^#\\w+$/g)) {\n        return openTab(hash.replace(/^#/g, ''));\n    }\n\n    return false;\n};\n"],"names":["SELECTORS","dynamicTabs","activeTab","allActiveTabs","tabContent","tabToggle","tabPane","tabName","on","event","preventDefault","stopPropagation","key","component","then","_ref","strChangesMade","strChangesMadeReally","strConfirm","Notification","confirm","target","trigger","type","catch","exception","previousTabName","getActiveTabName","document","querySelector","forTabName","textContent","tab","this","attr","length","loadTab","openTabFromHash","tabs","openTab","getAttribute","classList","add","element","dataset","getFirstTabName","pendingPromise","Pending","tabArgs","tabClass","JSON","stringify","response","Promise","all","$","parseHTML","javascript","map","node","innerHTML","join","Templates","renderForPromise","template","parse","content","_ref3","responseJs","html","js","replaceNodeContents","resolve","forTabId","getTab","getElementById","getTabPane","hash","location","match","replace"],"mappings":";;;;;;;4QAgCMA,UAAY,CACdC,YAAa,eACbC,UAAW,gCACXC,cAAe,2DACfC,WAAY,4CACZC,UAAW,uBACXC,QAAS,yBAGbN,WAAuBO,mDAA8CA,cACrEP,SAAqBO,2DAAsDA,6BAKvD,WACVF,WAAY,mBAAEL,UAAUK,cAG9BA,UAAUG,GAAG,SAAUC,SACd,4CAILA,MAAMC,iBACND,MAAME,sCAEK,CACP,CAACC,IAAK,cAAeC,UAAW,UAChC,CAACD,IAAK,0BAA2BC,UAAW,UAC5C,CAACD,IAAK,UAAWC,UAAW,YAC7BC,MAAKC,WAAEC,eAAgBC,qBAAsBC,wBAE5CC,sBAAaC,QAAQJ,eAAgBC,qBAAsBC,WAAY,MAAM,sEAEvET,MAAMY,QAAQC,QAAQb,MAAMc,YAEpCC,MAAML,sBAAaM,eAKzBpB,UACKG,GAAG,eAAe,iBAETkB,gBAAkBC,sBACpBD,gBAAiB,CACGE,SAASC,cAAc7B,UAAU8B,WAAWJ,kBACpDK,YAAc,OAGjCvB,GAAG,gBAAgB,iBACVwB,KAAM,oBAAE,mBAAEC,MAAMC,KAAK,SACR,IAAfF,IAAIG,QAGRC,QAAQJ,IAAIE,KAAK,WAGpBG,kBAAmB,OACdC,KAAOV,SAASC,cAAc7B,UAAUG,kBAC1CmC,KACAC,QAAQD,KAAKE,aAAa,sBACvB,OAEGlC,QAAUsB,SAASC,cAAc7B,UAAUM,SAC7CA,UACAA,QAAQmC,UAAUC,IAAI,SAAU,QAChCN,QAAQ9B,QAAQkC,aAAa,iBAWvCb,iBAAmB,WACfgB,QAAUf,SAASC,cAAc7B,UAAUE,kBAC1CyC,MAAAA,eAAAA,QAASH,aAAa,mBAAoB,MAkB/CJ,QAAW7B,6BAEbA,uCAAUA,qCAAWoB,0CAZD,YACdgB,QAAUf,SAASC,cAAc7B,UAAUI,mBAC1CuC,MAAAA,eAAAA,QAASC,QAAQxC,aAAc,MAUKyC,SACrCb,IAAMJ,SAASC,cAAc7B,UAAU8B,WAAWvB,cACnDyB,iBAICc,eAAiB,IAAIC,iBAAQ,6BAA+BxC,6CAE/CyB,KAClBlB,MAAK,SACEkC,QAAU,IAAIhB,IAAIY,uBACfI,QAAQC,gBACRD,QAAQ5C,YACR,4BAAW4B,IAAIY,QAAQK,SAAUC,KAAKC,UAAUH,aAE1DlC,MAAKsC,UAAYC,QAAQC,IAAI,CAC1BC,gBAAEC,UAAUJ,SAASK,WAAY,MAAM,GAAMC,KAAIC,MAAQA,KAAKC,YAAWC,KAAK,MAC9EC,mBAAUC,iBAAiBX,SAASY,SAAUd,KAAKe,MAAMb,SAASc,cAErEpD,MAAKqD,YAAEC,YAAYC,KAACA,KAADC,GAAOA,kBAASR,mBAAUS,oBAAoBvC,IAAKqC,KAAMC,GAAKF,eACjFtD,MAAK,IAAMgC,eAAe0B,YAC1BhD,MAAML,sBAAaM,YA6BlBc,QAAWhC,gBACPyB,IArBMzB,CAAAA,SACLqB,SAASC,cAAc7B,UAAUyE,SAASlE,UAoBrCmE,CAAOnE,iBACdyB,MAILI,QAAQ7B,SACRyB,IAAIS,UAAUC,IAAI,UAjBFnC,CAAAA,SACTqB,SAAS+C,eAAepE,SAiB/BqE,CAAWrE,SAASkC,UAAUC,IAAI,SAAU,SACrC,IAQLL,gBAAkB,WACdwC,KAAOjD,SAASkD,SAASD,aAC3BA,KAAKE,MAAM,YACJxC,QAAQsC,KAAKG,QAAQ,MAAO"}