Rev 1 | AutorÃa | Comparar con el anterior | 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.g
nu.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 {prependPageTitle} from 'core/page_title';\nimport Pending from 'core/pending';\nimport {getString} 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-bs-toggle=\"tab\"]:not(.disabled)',\n tabContent: '.dynamictabs .tab-pane [data-tab-content]',\n tabToggle: 'a[data-bs-toggle=\"tab\"]',\n tabPane: '.dynamictabs .tab-pane',\n};\n\nSELECTORS.forTabName = tabName => `.dynamictabs [data-tab-content=\"${tabName}\"]`;\nSELECTORS.forTabId = tabName => `.dynamictabs [data-bs-toggle=\"tab\"][href=\"#${tabName}\"]`;\n
\nlet watchedFormDirtyNotification = false;\n\n/**\n * Initialises the tabs view on the page (only one tabs view per page is supported)\n */\nexport const init = () => {\n const tabToggles = document.querySelectorAll(SELECTORS.tabToggle);\n tabToggles.forEach(tabToggle => {\n // Listen to click, warn user if they are navigating away with unsaved form changes.\n tabToggle.addEventListener('show.bs.tab', (event) => {\n if (isAnyWatchedFormDirty()) {\n event.preventDefault();\n event.stopPropagation();\n\n // Prevent double execution of event listener.\n if (!watchedFormDirtyNotification) {\n watchedFormDirtyNotification = true;\n\n Notification.saveCancelPromise(\n getString('changesmade'),\n getString('changesmadereallygoaway'),\n getString('confirm'),\n {triggerElement: tabToggle}\n
).then(() => {\n // Reset form dirty state on confirmation, re-trigger the event.\n resetAllFormDirtyStates();\n tabToggle.dispatchEvent(new Event('click', {bubbles: true}));\n return;\n }).catch(() => {\n // User cancelled the dialogue.\n }).finally(() => {\n watchedFormDirtyNotification = false;\n });\n }\n\n return;\n }\n\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\n tabToggle.addEventListener('shown.bs.tab', () => {\n const tabPane = document.getElementById(tabToggle.getAttr
ibute('href').replace(/^#/, ''));\n if (tabPane) {\n loadTab(tabPane.id);\n }\n });\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 const tabLabelledBy = document.getElementById(tab.getAttribute('aria-labelledby'));\n prependPageTitle(tabLabelledBy.innerText);\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(tabArg
s));\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 = (ta
bName) => {\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","watchedFormDirtyNotification","document","querySelectorAll","forEach","addEventListener","event","preventDefault","stopPropagation","saveCancelPromise","triggerElement","then","dispatchEvent","Event","bubbles","catch","finally","previousTabName","getActiveTabName","querySelector","forTabName","textContent","getElementById","getAttribute","replace","loadTab","id","
openTabFromHash","tabs","openTab","classList","add","element","dataset","getFirstTabName","tab","pendingPromise","Pending","tabLabelledBy","innerText","tabArgs","tabClass","JSON","stringify","response","Promise","all","$","parseHTML","javascript","map","node","innerHTML","join","Templates","renderForPromise","template","parse","content","_ref2","responseJs","html","js","replaceNodeContents","resolve","Notification","exception","forTabId","getTab","getTabPane","hash","location","match"],"mappings":";;;;;;;4QAiCMA,UAAY,CACdC,YAAa,eACbC,UAAW,gCACXC,cAAe,8DACfC,WAAY,4CACZC,UAAW,0BACXC,QAAS,yBAGbN,WAAuBO,mDAA8CA,cACrEP,SAAqBO,8DAAyDA,mBAE1EC,8BAA+B,gBAKf,QACGC,SAASC,iBAAiBV,UAAUK,WAC5CM,SAAQN,YAEfA,UAAUO,iBAAiB,eAAgBC,YACnC,iDACAA,MAAMC,iBACND,MAAME,uBAGDP,+BACDA,8BAA+B,wBAElBQ,mBACT,kBAAU,gBACV,kBAAU,4BACV,kBAAU,WACV,CAACC,eAAgBZ,YACnBa,MAAK,kDAGHb,UAAUc,cAAc,IAAIC,MAAM,QAAS,CAACC,SAAS,QAEtDC,OAAM,SAENC,SAAQ,KACPf,8BAA+B,aAQrCgB,gBAAkBC,sBACpBD,gBAAiB,CACGf,SAASiB,cAAc1B,UAAU2B,WAAWH,kBACpDI,YAAc,OAIlCvB,UAAUO,i
BAAiB,gBAAgB,WACjCN,QAAUG,SAASoB,eAAexB,UAAUyB,aAAa,QAAQC,QAAQ,KAAM,KACjFzB,SACA0B,QAAQ1B,QAAQ2B,WAKvBC,kBAAmB,OACdC,KAAO1B,SAASiB,cAAc1B,UAAUG,kBAC1CgC,KACAC,QAAQD,KAAKL,aAAa,sBACvB,OAEGxB,QAAUG,SAASiB,cAAc1B,UAAUM,SAC7CA,UACAA,QAAQ+B,UAAUC,IAAI,SAAU,QAChCN,QAAQ1B,QAAQwB,aAAa,iBAWvCL,iBAAmB,WACfc,QAAU9B,SAASiB,cAAc1B,UAAUE,kBAC1CqC,MAAAA,eAAAA,QAAST,aAAa,mBAAoB,MAkB/CE,QAAWzB,4BAEbA,sCAAUA,qCAAWkB,wCAZD,YACdc,QAAU9B,SAASiB,cAAc1B,UAAUI,mBAC1CmC,MAAAA,eAAAA,QAASC,QAAQpC,aAAc,MAUKqC,SACrCC,IAAMjC,SAASiB,cAAc1B,UAAU2B,WAAWpB,cACnDmC,iBAICC,eAAiB,IAAIC,iBAAQ,6BAA+BrC,SAE5DsC,cAAgBpC,SAASoB,eAAea,IAAIZ,aAAa,qDAC9Ce,cAAcC,+CAEZJ,KAClBxB,MAAK,SACE6B,QAAU,IAAIL,IAAIF,uBACfO,QAAQC,gBACRD,QAAQ3C,YACR,4BAAWsC,IAAIF,QAAQQ,SAAUC,KAAKC,UAAUH,aAE1D7B,MAAKiC,UAAYC,QAAQC,IAAI,CAC1BC,gBAAEC,UAAUJ,SAASK,WAAY,MAAM,GAAMC,KAAIC,MAAQA,KAAKC,YAAWC,KAAK,MAC9EC,mBAAUC,iBAAiBX,SAASY,SAAUd,KAAKe,MAAMb,SAASc,cAErE/C,MAAKgD,YAAEC,YAAYC,KAACA,KAADC,GAAOA,kBAASR,mBAAUS,oBAAoB5B,IAAK0B,KAAMC,GAAKF,eACjFjD,MAAK,IAAMyB,eAAe4B,YAC1BjD,MAAMkD,
sBAAaC,YA6BlBrC,QAAW7B,gBACPmC,IArBMnC,CAAAA,SACLE,SAASiB,cAAc1B,UAAU0E,SAASnE,UAoBrCoE,CAAOpE,iBACdmC,MAILV,QAAQzB,SACRmC,IAAIL,UAAUC,IAAI,UAjBF/B,CAAAA,SACTE,SAASoB,eAAetB,SAiB/BqE,CAAWrE,SAAS8B,UAAUC,IAAI,SAAU,SACrC,IAQLJ,gBAAkB,WACd2C,KAAOpE,SAASqE,SAASD,aAC3BA,KAAKE,MAAM,YACJ3C,QAAQyC,KAAK9C,QAAQ,MAAO"}