{"version":3,"file":"tabtree.min.js","sources":["https:\/\/moodle.tau.ac.il\/2023\/course\/format\/multitopic\/amd\/src\/courseformat\/contenttabs\/tabtree.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 .\n\nimport {BaseComponent} from 'core\/reactive';\nimport {getCurrentCourseEditor} from 'core_courseformat\/courseeditor';\nimport Tab from 'format_multitopic\/courseformat\/contenttabs\/tab';\nimport Templates from 'core\/templates';\n\n\n\/**\n * Course section tabs updater.\n *\n * @module format_multitopic\/courseformat\/contenttabs\/tabtree\n * @class format_multitopic\/courseformat\/contenttabs\/tabtree\n * @copyright 2022 Jeremy FitzPatrick and Te W\u0101nanga o Aotearoa\n * @copyright 2023 James Calder and Otago Polytechnic\n * @copyright based on work by 2021 Ferran Recio \n * @license http:\/\/www.gnu.org\/copyleft\/gpl.html GNU GPL v3 or later\n *\/\n\nexport default class Component extends BaseComponent {\n\n \/**\n * Constructor hook.\n *\/\n create() {\n \/\/ Optional component name for debugging.\n this.name = 'contenttabs';\n \/\/ Default query selectors.\n this.selectors = {\n TAB: `ul:first-of-type li`,\n CHILDTAB: `ul:nth-child(2) li`,\n SECTION_ITEM: `a.nav-link`,\n };\n \/\/ Default classes\n this.classes = {\n ACTIVETAB: 'active'\n };\n \/\/ Objects to keep tabs on the tabs\n this.tabs = {};\n this.childtabs = {};\n this.activetab = [null, null];\n }\n\n static init(target) {\n return new this({\n element: document.getElementById(target),\n reactive: getCurrentCourseEditor(),\n });\n }\n\n \/**\n * Initial state ready method.\n *\n *\/\n stateReady() {\n this._indexContents();\n }\n\n getWatchers() {\n return [\n \/\/ Sections sorting.\n {watch: `course.sectionlist:updated`, handler: this._refreshCourseSectionTabs},\n ];\n }\n\n \/**\n * Refresh the section tabs.\n *\n * @param {object} param\n * @param {Object} param.element\n *\/\n async _refreshCourseSectionTabs({element}) {\n \/\/ Change the active top-level tab, if necessary.\n const activeTab1 = this.reactive.get('section', this.activetab[1]);\n let newActiveTab0id = (activeTab1.levelsan >= 1) ? activeTab1.parentid : activeTab1.id;\n if (newActiveTab0id != this.activetab[0]) {\n let section = this.reactive.get(\"section\", this.activetab[0]);\n let anchor = this.element.querySelector('ul:first-of-type div[data-itemid=\"' + this.activetab[0] + '\"]').parentElement;\n anchor.classList.remove(\"active\");\n anchor.href = section.sectionurl.replace(\"&\", \"&\");\n this.activetab[0] = newActiveTab0id;\n anchor = this.element.querySelector('ul:first-of-type div[data-itemid=\"' + this.activetab[0] + '\"]').parentElement;\n anchor.classList.add(\"active\");\n anchor.removeAttribute(\"href\");\n const addAnchor = this.element.querySelector('ul:nth-of-type(2) li:last-of-type a');\n const addLink = addAnchor.href.replace(\/&insertparentid=\\d+\/, \"&insertparentid=\" + this.activetab[0]);\n addAnchor.setAttribute(\"href\", addLink);\n }\n\n \/\/ Do things that make the first row tabs match firstsectionlist.\n const toptabslist = element.firstsectionlist ?? [];\n const childtabslist = element.secondsectionlist ?? [];\n let toptabs = this.element.querySelector('ul:first-of-type');\n await this._fixOrder(toptabs, toptabslist, this.selectors.TAB, 0, childtabslist[this.activetab[0]].length > 1);\n\n \/\/ And the second row tabs match secondsectionlist.\n let childtabs = this.element.querySelector('ul:nth-of-type(2)');\n if (childtabs) {\n await this._fixOrder(childtabs, childtabslist[this.activetab[0]], this.selectors.CHILDTAB, 1, false);\n }\n\n this._indexContents();\n }\n\n \/**\n * Regenerate content indexes.\n *\n * This method is used when a legacy action refresh some content element.\n *\/\n _indexContents() {\n \/\/ Find unindexed tabs.\n this._scanIndex(\n this.selectors.TAB,\n this.tabs,\n (item) => {\n return new Tab(item);\n },\n 0\n );\n\n \/\/ Find unindexed child tabs.\n this._scanIndex(\n this.selectors.CHILDTAB,\n this.childtabs,\n (item) => {\n return new Tab(item);\n },\n 1\n );\n }\n\n \/**\n * Reindex a tab.\n *\n * This method is used internally by _indexContents.\n *\n * @param {string} selector the DOM selector to scan\n * @param {*} index the index attribute to update\n * @param {*} creationhandler method to create a new indexed element\n * @param {int} level tab level\n *\/\n _scanIndex(selector, index, creationhandler, level) {\n const items = this.getElements(`${selector}:not([data-indexed])`);\n items.forEach((item) => {\n if (!item?.dataset?.id) {\n return;\n }\n \/\/ Delete previous item component.\n if (index[item.dataset.id] !== undefined) {\n index[item.dataset.id].unregister();\n }\n \/\/ Create the new component.\n index[item.dataset.id] = creationhandler({\n ...this,\n element: item,\n });\n \/\/ Update selected tab\n let classes = item.querySelector(\"a\").classList.value;\n if (classes.indexOf(this.classes.ACTIVETAB) !== -1) {\n if (level <= 0) {\n this.activetab[0] = item.dataset.id;\n }\n this.activetab[1] = item.dataset.id;\n }\n \/\/ Mark as indexed.\n item.dataset.indexed = true;\n });\n }\n\n \/**\n * Fix\/reorder the section or cms order.\n *\n * @param {Element} container the HTML element to reorder.\n * @param {Array} neworder an array with the ids order\n * @param {string} selector the element selector\n * @param {int} level the tab level\n * @param {boolean} hassubtree\n *\/\n async _fixOrder(container, neworder, selector, level, hassubtree) {\n\n \/\/ Empty lists should not be visible.\n if (!neworder.length) {\n container.classList.add('hidden');\n container.innerHTML = '';\n return;\n }\n\n \/\/ Grant the list is visible (in case it was empty).\n container.classList.remove('hidden');\n\n \/\/ Move the elements in order at the beginning of the list.\n for (const [index, itemid] of Object.entries(neworder)) {\n const section = this.reactive.get(\"section\", itemid);\n const visible = (section.visible && section.available || section.section == 0)\n && (neworder.length > 1 || hassubtree);\n const current = (section.currentnestedlevel != undefined && section.currentnestedlevel >= level);\n let item = this.getElement(selector, itemid);\n if (item === null) {\n \/\/ If we don't have an item, create it.\n let data = {\n \"sectionid\": itemid,\n \"level\": level,\n \"active\": 0,\n \"inactive\": 0,\n \"link\": [{\n \"link\": section.sectionurl\n }],\n \"title\": section.name,\n \"text\": '
' + section.title + '<\/div>'\n };\n item = document.createElement(\"li\");\n container.insertBefore(item, container.lastElementChild);\n let html = await Templates.render(\"format_multitopic\/courseformat\/contenttabs\/tab\", data);\n item = Templates.replaceNode(item, html, \"\")[0];\n }\n\n \/\/ Update visibility & current marker\n const content = item.querySelector(\"div.tab_content\");\n if (content && content.classList.contains(\"dimmed\") == visible) {\n if (visible) {\n content.classList.remove(\"dimmed\");\n } else {\n content.classList.add(\"dimmed\");\n }\n }\n if (content && content.classList.contains(\"marker\") != current) {\n if (current) {\n content.classList.add(\"marker\");\n } else {\n content.classList.remove(\"marker\");\n }\n }\n\n \/\/ Get the current element at that position.\n const currentitem = container.children[index];\n if (currentitem === undefined) {\n container.append(item);\n return;\n }\n if (currentitem !== item) {\n container.insertBefore(item, currentitem);\n }\n }\n \/\/ Remove the remaining elements.\n \/\/ But we don't want the \"Add\" blown away.\n while (container.children.length > neworder.length + 1) {\n container.removeChild(container.lastElementChild.previousSibling);\n }\n\n }\n\n}"],"names":["Component","BaseComponent","create","name","selectors","TAB","CHILDTAB","SECTION_ITEM","classes","ACTIVETAB","tabs","childtabs","activetab","target","this","element","document","getElementById","reactive","stateReady","_indexContents","getWatchers","watch","handler","_refreshCourseSectionTabs","activeTab1","get","newActiveTab0id","levelsan","parentid","id","section","anchor","querySelector","parentElement","classList","remove","href","sectionurl","replace","add","removeAttribute","addAnchor","addLink","setAttribute","toptabslist","firstsectionlist","childtabslist","secondsectionlist","toptabs","_fixOrder","length","_scanIndex","item","Tab","selector","index","creationhandler","level","getElements","forEach","dataset","_item$dataset","undefined","unregister","value","indexOf","indexed","container","neworder","hassubtree","innerHTML","itemid","Object","entries","visible","available","current","currentnestedlevel","getElement","data","title","createElement","insertBefore","lastElementChild","html","Templates","render","replaceNode","content","contains","currentitem","children","append","removeChild","previousSibling"],"mappings":";;;;;;;;;;2KAgCqBA,kBAAkBC,wBAKnCC,cAESC,KAAO,mBAEPC,UAAY,CACbC,0BACAC,8BACAC,gCAGCC,QAAU,CACXC,UAAW,eAGVC,KAAO,QACPC,UAAY,QACZC,UAAY,CAAC,KAAM,kBAGhBC,eACD,IAAIC,KAAK,CACZC,QAASC,SAASC,eAAeJ,QACjCK,UAAU,4CAQlBC,kBACSC,iBAGTC,oBACW,CAEH,CAACC,mCAAqCC,QAAST,KAAKU,sHAU5BT,QAACA,oBAEvBU,WAAaX,KAAKI,SAASQ,IAAI,UAAWZ,KAAKF,UAAU,QAC3De,gBAAmBF,WAAWG,UAAY,EAAKH,WAAWI,SAAWJ,WAAWK,MAChFH,iBAAmBb,KAAKF,UAAU,GAAI,KAClCmB,QAAUjB,KAAKI,SAASQ,IAAI,UAAWZ,KAAKF,UAAU,IACtDoB,OAASlB,KAAKC,QAAQkB,cAAc,qCAAuCnB,KAAKF,UAAU,GAAK,MAAMsB,cACzGF,OAAOG,UAAUC,OAAO,UACxBJ,OAAOK,KAAON,QAAQO,WAAWC,QAAQ,QAAS,UAC7C3B,UAAU,GAAKe,gBACpBK,OAASlB,KAAKC,QAAQkB,cAAc,qCAAuCnB,KAAKF,UAAU,GAAK,MAAMsB,cACrGF,OAAOG,UAAUK,IAAI,UACrBR,OAAOS,gBAAgB,cACjBC,UAAY5B,KAAKC,QAAQkB,cAAc,uCACvCU,QAAUD,UAAUL,KAAKE,QAAQ,sBAAuB,mBAAqBzB,KAAKF,UAAU,IAClG8B,UAAUE,aAAa,OAAQD,eAI7BE,0CAAc9B,QAAQ+B,wEAAoB,GAC1CC,4CAAgBhC,QAAQiC,yEAAqB,OAC\/CC,QAAUnC,KAAKC,QAAQkB,cAAc,0BACnCnB,KAAKoC,UAAUD,QAASJ,YAAa\/B,KAAKV,UAAUC,IAAK,EAAG0C,cAAcjC,KAAKF,UAAU,IAAIuC,OAAS,OAGxGxC,UAAYG,KAAKC,QAAQkB,cAAc,qBACvCtB,iBACMG,KAAKoC,UAAUvC,UAAWoC,cAAcjC,KAAKF,UAAU,IAAKE,KAAKV,UAAUE,SAAU,GAAG,QAG7Fc,iBAQTA,sBAESgC,WACDtC,KAAKV,UAAUC,IACfS,KAAKJ,MACJ2C,MACU,IAAIC,aAAID,OAEnB,QAICD,WACDtC,KAAKV,UAAUE,SACfQ,KAAKH,WACJ0C,MACU,IAAIC,aAAID,OAEnB,GAcRD,WAAWG,SAAUC,MAAOC,gBAAiBC,OAC3B5C,KAAK6C,sBAAeJ,kCAC5BK,SAASP,4BACNA,MAAAA,4BAAAA,KAAMQ,mCAANC,cAAehC,eAIWiC,IAA3BP,MAAMH,KAAKQ,QAAQ\/B,KACnB0B,MAAMH,KAAKQ,QAAQ\/B,IAAIkC,aAG3BR,MAAMH,KAAKQ,QAAQ\/B,IAAM2B,gBAAgB,IAClC3C,KACHC,QAASsC,QAIoC,IADnCA,KAAKpB,cAAc,KAAKE,UAAU8B,MACpCC,QAAQpD,KAAKN,QAAQC,aACzBiD,OAAS,SACJ9C,UAAU,GAAKyC,KAAKQ,QAAQ\/B,SAEhClB,UAAU,GAAKyC,KAAKQ,QAAQ\/B,IAGrCuB,KAAKQ,QAAQM,SAAU,qBAafC,UAAWC,SAAUd,SAAUG,MAAOY,gBAG7CD,SAASlB,cACViB,UAAUjC,UAAUK,IAAI,eACxB4B,UAAUG,UAAY,IAK1BH,UAAUjC,UAAUC,OAAO,cAGtB,MAAOoB,MAAOgB,UAAWC,OAAOC,QAAQL,UAAW,OAC9CtC,QAAUjB,KAAKI,SAASQ,IAAI,UAAW8C,QACvCG,SAAW5C,QAAQ4C,SAAW5C,QAAQ6C,WAAgC,GAAnB7C,QAAQA,WACzDsC,SAASlB,OAAS,GAAKmB,YACzBO,QAAyCd,MAA9BhC,QAAQ+C,oBAAmC\/C,QAAQ+C,oBAAsBpB,UACtFL,KAAOvC,KAAKiE,WAAWxB,SAAUiB,WACxB,OAATnB,KAAe,KAEX2B,KAAO,WACMR,aACJd,aACC,WACE,OACJ,CAAC,MACG3B,QAAQO,mBAEXP,QAAQ5B,UACT,2BAA6BwE,QAAU,GAAK,YAAcE,QAAU,UAAY,IAClF,kBAAoB9C,QAAQD,GAAK,KAAOC,QAAQkD,MAAQ,UAElE5B,KAAOrC,SAASkE,cAAc,MAC9Bd,UAAUe,aAAa9B,KAAMe,UAAUgB,sBACnCC,WAAaC,mBAAUC,OAAO,iDAAkDP,MACpF3B,KAAOiC,mBAAUE,YAAYnC,KAAMgC,KAAM,IAAI,SAI3CI,QAAUpC,KAAKpB,cAAc,mBAC\/BwD,SAAWA,QAAQtD,UAAUuD,SAAS,WAAaf,UAC\/CA,QACAc,QAAQtD,UAAUC,OAAO,UAEzBqD,QAAQtD,UAAUK,IAAI,WAG1BiD,SAAWA,QAAQtD,UAAUuD,SAAS,WAAab,UAC\/CA,QACAY,QAAQtD,UAAUK,IAAI,UAEtBiD,QAAQtD,UAAUC,OAAO,iBAK3BuD,YAAcvB,UAAUwB,SAASpC,eACnBO,IAAhB4B,wBACAvB,UAAUyB,OAAOxC,MAGjBsC,cAAgBtC,MAChBe,UAAUe,aAAa9B,KAAMsC,kBAK9BvB,UAAUwB,SAASzC,OAASkB,SAASlB,OAAS,GAC7CiB,UAAU0B,YAAY1B,UAAUgB,iBAAiBW"}