{"version":3,"file":"course.min.js","sources":["https:\/\/kundenportal.comm-unity.at\/course\/format\/tiles\/amd\/src\/course.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\n\/* eslint space-before-function-paren: 0 *\/\n\n\/**\n * Main Javascript module for format_tiles for when user is *NOT* editing.\n * See course_edit for if they are editing.\n * Handles the UI changes when tiles are selected and anything else not\n * covered by the specific modules\n *\n * @module format_tiles\/course\n * @copyright 2018 David Watson {@link http:\/\/evolutioncode.uk}\n * @license http:\/\/www.gnu.org\/copyleft\/gpl.html GNU GPL v3 or later\n *\/\n\ndefine([\"jquery\", \"core\/templates\", \"core\/ajax\", \"format_tiles\/browser_storage\",\n \"core\/notification\", \"core\/str\", \"format_tiles\/tile_fitter\", 'core\/fragment'],\n function ($, Templates, ajax, browserStorage, Notification, str, tileFitter, Fragment) {\n \"use strict\";\n\n var isMobile;\n var loadingIconHtml;\n var stringStore = [];\n var HEADER_BAR_HEIGHT = 60; \/\/ This varies by theme and version so will be reset once pages loads below.\n var reopenLastVisitedSection = false;\n var courseId;\n var courseContextId;\n let resizeTimeout;\n var enableCompletion;\n var reorgSectionsDisabledUntil = 0;\n\n \/\/ Keep a record of which tile is currently open.\n var openTile = 0;\n\n var Selector = {\n BODY: \"body\",\n PAGE: \"#page\",\n TILE: \".tile\",\n TILEID: \"#tile-\",\n MOVEABLE_SECTION: \".moveablesection\",\n FILTER_BUTTON: \".filterbutton\",\n TILE_LOADING_ICON: \".tile-loading-icon\",\n TILE_LOADING_ICON_ID: \"#loading-icon-\",\n TILE_COLLAPSED: \".tile-collapsed\",\n TILE_CLICKABLE: \".tile-clickable\",\n TILES: \"ul.tiles\",\n ACTIVITY: \".activity\",\n ACTIVITY_NAME: \".activityname\",\n ABOVE_TILES: \"#abovetiles\",\n FOCUSABLE_ELEMS: 'button, a, input:not([type=\"hidden\"]), select, textarea, [tabindex]:not([tabindex=\"-1\"])',\n INSTANCE_NAME: \".instancename\",\n SPACER: \".spacer\",\n SECTION_ID: \"#section-\",\n SECTION_TITLE: \".sectiontitle\",\n SECTION_MAIN: \".section.main\",\n SECTION_BUTTONS: \".sectionbuttons\",\n CLOSE_SEC_BTN: \".closesectionbtn\",\n HIDE_SEC0_BTN: \".buttonhidesec0\",\n SECTION_ZERO: \"#section-0\",\n MOODLE_VIDEO: \".mediaplugin.mediaplugin_videojs\",\n MOODLE_DIALOGUE: \".moodle-dialogue-confirm\", \/\/ E.g. glossary entry.\n MANUAL_COMPLETION: '[data-action=\"toggle-manual-completion\"]',\n TOOLTIP: \"[data-toggle=tooltip]\",\n MATHJAX_EQUATION: \".filter_mathjaxloader_equation\"\n };\n var ClassNames = {\n SELECTED: \"selected\",\n OPEN: \"open\",\n CLOSED: \"closed\",\n STATE_VISIBLE: 'state-visible', \/\/ This is a Snap theme class. Was added to make this format cooperate better with it.\n HAS_OPEN_TILE: 'format-tiles-tile-open',\n ON_TILE_CONTROL: 'on-tile-control' \/\/ Tiles may have controls on them which do not open the section when clicked.\n };\n\n var Event = {\n CLICK: \"click\",\n KEYDOWN: \"keydown\",\n };\n\n var CSS = {\n DISPLAY: \"display\",\n Z_INDEX: \"z-index\",\n HEIGHT: \"height\",\n BG_COLOUR: \"background-color\"\n };\n var Keyboard = {\n TAB: 9,\n ENTER: 13\n };\n\n const OVERLAY_ID = 'format_tiles_overlay';\n\n \/**\n * If we have embedded video in section, stop it.\n * Runs when section is closed.\n * @param {number} section sec number where the video is.\n * @param {number} sectionId sec ID where the video is.\n *\/\n var stopVideoPlaying = function(section, sectionId) {\n var contentSection = $(Selector.SECTION_ID + section);\n\n \/\/ First iframes (e.g. embedded YouTube).\n contentSection.find(\"iframe\").each(function (index, iframe) {\n iframe = $(iframe);\n \/\/ Remove the src from the iframe but keep it in case the section is re-opened.\n if (iframe.attr('src')) {\n iframe.data('src', iframe.attr(\"src\"));\n iframe.attr('src', \"\");\n }\n });\n\n \/\/ Then Moodle media player.\n var mediaPlayers = contentSection.find(Selector.MOODLE_VIDEO);\n if (mediaPlayers.length > 0) {\n contentSection.html(\"\");\n getSectionContentFromServer(courseContextId, sectionId).done(function (html, js) {\n setCourseContentHTML(contentSection, html, js);\n });\n }\n };\n\n \/**\n * When JS navigation is being used, when a user un-selects a tile, we have to un-fade other tiles\n * @param {number} sectionToFocus if we want to focus a tile after closing, which one\n *\/\n var cancelTileSelections = function (sectionToFocus) {\n $(Selector.MOVEABLE_SECTION).each(function (index, sec) {\n sec = $(sec);\n if (sec.is(\":visible\")) {\n stopVideoPlaying(sec.data(\"section\"), sec.data(\"sectionid\"));\n sec.slideUp().removeClass(ClassNames.STATE_VISIBLE); \/\/ Excludes section 0.\n }\n });\n $(Selector.TILE).removeClass(ClassNames.SELECTED).css(CSS.Z_INDEX, \"\").css(CSS.BG_COLOUR, \"\");\n $(\".section \" + ClassNames.SELECTED).removeClass(ClassNames.SELECTED).css(CSS.Z_INDEX, \"\");\n\n if (sectionToFocus !== undefined && sectionToFocus !== 0) {\n $(Selector.TILEID + sectionToFocus).focus();\n }\n $(Selector.TILE_LOADING_ICON).fadeOut(300, function () {\n $(Selector.TILE_LOADING_ICON).html(\"\");\n });\n openTile = 0;\n $(Selector.BODY).removeClass(ClassNames.HAS_OPEN_TILE);\n overlay.fadeOut(300);\n\n \/\/ If any moodle dialogues are open, close them (e.g. glossary auto links).\n $(Selector.MOODLE_DIALOGUE).remove();\n };\n\n const overlay = $('#' + OVERLAY_ID);\n overlay.attr('aria-hidden', true);\n\n \/**\n * Used where the user clicks the window overlay but we want the active click to be behind the\n * overlay e.g. the tile or custom menu item behind it. So we get the co-ordinates of the click\n * on the overlay and then repeat the click at that spot ignoring the overlay\n * @param {object} e the click event object\n *\/\n var clickItemBehind = function (e) {\n var clickedItem = $(e.currentTarget);\n if (clickedItem.attr(\"id\") === OVERLAY_ID) {\n \/\/ We need to know what is behind the modal, so hide it for an instant to find out.\n clickedItem.hide();\n var BottomElement = $(document.elementFromPoint(e.clientX, e.clientY));\n clickedItem.show();\n if (BottomElement.hasClass(\"filterbutton\") || BottomElement.hasClass(\"list-group-item\")) {\n \/\/ Must ba a filter button clicked or a nav drawer item.\n BottomElement.click();\n } else {\n \/\/ Must be a tile clicked.\n var clickedTile = BottomElement.closest(Selector.TILE);\n if (clickedTile) {\n clickedTile.click();\n }\n }\n }\n };\n\n \/**\n * Set the HTML for a course section to the correct div in the page\n * @param {Object} contentArea the jquery object for the content area\n * @param {String} html the HTML\n * @param {String} js Any additional JS for the new HTML.\n *\/\n var setCourseContentHTML = function (contentArea, html, js) {\n if (html) {\n contentArea.html(html);\n $(Selector.TILE_LOADING_ICON).fadeOut(300, function () {\n $(Selector.TILE_LOADING_ICON).html(\"\");\n });\n\n if (contentArea.attr(\"id\") !== Selector.SECTION_ZERO) {\n \/\/ Trap the tab key navigation in the content bearing section.\n \/\/ Until the user clicks the close button.\n \/\/ When user reaches last item, send them back to first.\n \/\/ And vice versa if going backwards.\n\n const activityLinks = contentArea.find(Selector.ACTIVITY).not(Selector.SPACER).find('a');\n activityLinks.on(Event.KEYDOWN, function (e) {\n if (e.keyCode === Keyboard.ENTER) {\n var toClick = $(e.currentTarget).find(\"a\");\n window.location.href = toClick.attr(\"href\");\n }\n });\n\n if (!isMobile) {\n const focusableElements = contentArea.find(Selector.FOCUSABLE_ELEMS);\n const firstFocusableElement = focusableElements.eq(0);\n const lastFocusableElement = focusableElements.eq(focusableElements.length - 1);\n lastFocusableElement.on(Event.KEYDOWN, function (e) {\n if (e.keyCode === Keyboard.TAB && !e.shiftKey\n && $(e.relatedTarget).closest(Selector.SECTION_MAIN).attr(\"id\") !== contentArea.attr(\"id\")) {\n \/\/ RelatedTarget is the item we tabbed to.\n \/\/ If we reached here, the item we are on is not a member of the section we were in.\n \/\/ (I.e. we are trying to tab out of bottom of section) so move tab to first item instead.\n setTimeout(function () {\n \/\/ Allow very short delay so we don't skip forward on the basis of our last key press.\n firstFocusableElement.focus();\n contentArea.find(Selector.SECTION_BUTTONS).css(\"top\", \"\");\n }, 100);\n }\n });\n firstFocusableElement.on(Event.KEYDOWN, function (e) {\n if (e.keyCode === Keyboard.TAB && e.shiftKey\n && $(e.relatedTarget).closest(Selector.SECTION_MAIN).attr(\"id\") !== contentArea.attr(\"id\")) {\n \/\/ See explanation previous block.\n \/\/ Here we are trying to tab backwards out of the top of our section.\n \/\/ So take us to last item instead.\n setTimeout(function () {\n lastFocusableElement.focus();\n }, 100);\n }\n });\n }\n }\n\n if (!isMobile) {\n \/\/ Activate tooltips for completion toggle and any \"restricted\" items in this content.\n setTimeout(function () {\n \/\/ Manual forms, auto icons and \"Restricted until ...\" etc.\n try {\n const tooltipItems = contentArea.find(\".badge-info\");\n if (tooltipItems.length > 0 && typeof tooltipItems.tooltip == 'function') {\n tooltipItems.tooltip();\n }\n } catch (err) {\n require([\"core\/log\"], function(log) {\n log.debug(err);\n });\n }\n }, 500);\n }\n\n setTimeout(() => {\n \/\/ If subtile title is long, it overlaps background image.\n \/\/ Check heights to see if any subtile backgrounds need dimming.\n \/\/ Allow short delay for content to be added first.\n const MAX_HEIGHT = 110;\n contentArea.find(\n Selector.ACTIVITY_NAME).each((i, el) => {\n el = $(el);\n if (el.height() > MAX_HEIGHT) {\n el.closest(Selector.INSTANCE_NAME).addClass('opaque-bg');\n }\n });\n }, 100);\n }\n setTimeout(() => {\n if (js) {\n \/\/ User may be opening same section multiple times so avoid adding same script again.\n const head = $('head');\n const existingScripts = head.find('script').filter(\n (index, script) => {\n return $(script).html() === js;\n }\n );\n if (existingScripts.length === 0) {\n Templates.runTemplateJS(js);\n }\n }\n\n applyMathJax(contentArea);\n\n const moodleVideos = contentArea.find(Selector.MOODLE_VIDEO);\n if (moodleVideos.length > 0) {\n \/\/ This already happens once on page load, but we repeat since reloaded HTML containing lazy load videos.\n require([\"media_videojs\/loader\"], function (videoJS) {\n videoJS.setUp();\n });\n\n \/\/ Issue 87 - If video fullscreen button is pressed, temporarily disable tile re-orgs on screen resize.\n const fsEvents = ['fullscreenchange', 'webkitfullscreenchang', 'mozfullscreenchange', 'msfullscreenchange'];\n fsEvents.forEach(function (ev) {\n document.addEventListener(ev, function () {\n const disableDurationMilliSeconds = 1000;\n reorgSectionsDisabledUntil = Date.now() + disableDurationMilliSeconds;\n });\n });\n }\n }, 1000);\n\n $(document).trigger('format-tiles-section-content-changed', {\n courseId: parseInt(courseId),\n section: contentArea.data('section'),\n sectionid: contentArea.data('sectionid')\n });\n };\n\n \/**\n * Find Mathjax equations in a content area and queue them for processing.\n * @param {Object} contentArea the jquery object for the content area\n *\/\n const applyMathJax = function(contentArea) {\n if (typeof window.MathJax !== \"undefined\") {\n try {\n const mathJaxElems = contentArea.find(Selector.MATHJAX_EQUATION);\n if (mathJaxElems.length) {\n mathJaxElems.each((i, node) => {\n window.MathJax.Hub.Queue([\"Typeset\", window.MathJax.Hub, node]);\n });\n }\n } catch (err) {\n require([\"core\/log\"], function (log) {\n log.debug(err);\n });\n }\n }\n };\n\n \/**\n * Expand a content containing section (e.g. on tile click)\n * @param {object} contentArea\n * @param {number} sectionNumber to expand\n *\/\n var expandSection = function (contentArea, sectionNumber) {\n const tile = $(\"#tile-\" + sectionNumber);\n var expandAndScroll = function () {\n \/\/ Scroll to the top of content bearing section\n \/\/ we have to wait until possible reOrg and slide down totally before calling this, else co-ords are wrong.\n var scrollTo = (tile.offset().top) - $('#section-zero-container').offset().top + HEADER_BAR_HEIGHT;\n if (scrollTo === $(window).scrollTop) {\n \/\/ Scroll by at least one pixel otherwise z-index on selected tile is not changed.\n \/\/ Until mouse moves.\n scrollTo += 1;\n }\n\n \/\/ If user tries to scroll during animation, stop animation.\n var events = \"mousedown wheel DOMMouseScroll mousewheel keyup touchmove\";\n const page = $(\"html, body\");\n page.on(events, function () {\n page.stop();\n });\n\n page.animate({scrollTop: scrollTo}, \"slow\", \"swing\", function () {\n \/\/ Animation complete, remove stop handler.\n page.off(events, function () {\n page.stop();\n });\n \/\/ For users with screen readers, move focus to the first item within the tile.\n contentArea.find(Selector.FOCUSABLE_ELEMS).eq(0).focus();\n });\n\n \/\/ For users with screen readers, move focus to the first item within the tile.\n \/\/ Short timeout for this to allow for animation to finish.\n \/\/ (Not relying on the animation callback alone for the delay as it's slightly too slow.)\n setTimeout(() => {\n contentArea.find(Selector.FOCUSABLE_ELEMS).eq(0).focus();\n }, 300);\n\n openTile = sectionNumber;\n\n \/\/ If we have any iframes in the section which were previous emptied out, re-populate.\n \/\/ This will happen if we have previously closed a section with videos in, and they were muted.\n const iframes = contentArea.find(\"iframe\");\n if (iframes.length > 0) {\n iframes.each(function (index, iframe) {\n iframe = $(iframe);\n \/\/ If iframe has no src, add it from data-src.\n if (iframe.attr('src') === '' && iframe.data('src') !== undefined) {\n iframe.attr('src', iframe.data(\"src\"));\n }\n });\n\n if (enableCompletion) {\n \/\/ Some iframes may load content set to mark as complete on view.\n \/\/ So maybe need to update tile completion info. E.g. applies with H5P filter.\n setTimeout(() => {\n $(document).trigger('format-tiles-completion-changed', {\n courseid: courseId,\n section: sectionNumber\n });\n }, 1000);\n }\n }\n };\n\n contentArea.addClass(ClassNames.STATE_VISIBLE);\n overlay.fadeIn(300);\n tile.addClass(ClassNames.SELECTED);\n $(Selector.BODY).addClass(ClassNames.HAS_OPEN_TILE);\n contentArea.slideDown(350, function () {\n \/\/ Wait until we have finished sliding down before we work out where the top is for scroll.\n expandAndScroll();\n });\n openTile = sectionNumber;\n };\n\n \/**\n * We find out what section is open, collapse them all, then run the re-org.\n * Finally we re-open the section.\n * This is to ensure that the content bearing section is on the row under the tile clicked.\n * It is run at page load and again if window is re-sized etc.\n * @param {boolean} fitTilesToScreenWidth whether we need to resize the tiles window while tiles are closed.\n * @returns {Promise}\n *\/\n var reOrgSections = function (fitTilesToScreenWidth) {\n var dfd = new $.Deferred();\n if (reorgSectionsDisabledUntil > Date.now()) {\n dfd.resolve();\n }\n const disableDurationMilliSeconds = 1000;\n reorgSectionsDisabledUntil = Date.now() + disableDurationMilliSeconds;\n\n var reOrgFunc = function() {\n tileFitter.runReOrg()\n .done(function(result) {\n dfd.resolve(result);\n })\n .fail(function(result) {\n dfd.reject(result);\n });\n };\n\n if (fitTilesToScreenWidth) {\n tileFitter.resizeTilesDivWidth(courseId).done(() => {\n \/\/ Wait until resize is done to start re-org to allow tiles to settle.\n reOrgFunc();\n });\n } else {\n reOrgFunc();\n }\n return dfd.promise();\n };\n\n var failedLoadSectionNotify = function(sectionNum, failResult, contentArea) {\n if (failResult) {\n \/\/ Notify the user and invite them to refresh. We did get a \"failResult\" from server,\n \/\/ So it looks like we do have a connection and can launch this.\n Notification.confirm(\n stringStore.sectionerrortitle,\n stringStore.sectionerrorstring,\n stringStore.refresh,\n stringStore.cancel,\n function () {\n window.location.reload();\n },\n null\n );\n contentArea.html(\"\"); \/\/ Clear loading icon.\n } else {\n \/\/ It looks like we may not have a connection so we can't launch notifications.\n \/\/ We can warn the user like this instead.\n setCourseContentHTML(contentArea, \"

\" + stringStore.noconnectionerror + \"<\/p>\", '');\n setTimeout(function () {\n expandSection(contentArea, sectionNum);\n }, 500);\n }\n require([\"core\/log\"], function(log) {\n log.debug(failResult);\n });\n throw new Error(\"Not successful retrieving tile content by AJAX for section \" + sectionNum);\n };\n\n \/**\n * For a given section, get the content from the server, add it to the store and maybe UI and maybe show it\n * @param {number} courseContextId the id for the affected course context\n * @param {number} sectionId the section ID we are wanting to populate\n * @return {Promise} promise to resolve when the ajax call returns.\n *\/\n var getSectionContentFromServer = function (courseContextId, sectionId) {\n if (!courseContextId || !sectionId) {\n require([\"core\/log\"], function(log) {\n log.debug(`No course context ID '${courseContextId.toString()}' or section ID '${sectionId.toString()}'`);\n });\n }\n \/\/ This gets the fragment from format_tiles_output_fragment_get_cm_list().\n return Fragment.loadFragment(\n 'format_tiles', 'get_cm_list', courseContextId, {sectionid: sectionId}\n );\n };\n\n \/**\n * If the user had section zero collapsed in this course previously, collapse it now\n *\/\n var setSectionZeroFromUserPref = function () {\n var buttonHideSecZero = $(Selector.HIDE_SEC0_BTN);\n var sectionZero = $(Selector.SECTION_ZERO);\n if (browserStorage.storageEnabledLocal()) {\n \/\/ Collapse section zero if user had it collapsed before - relies on local storage so only if enabled.\n if (browserStorage.getSecZeroCollapseStatus() === true) {\n sectionZero.slideUp(0);\n buttonHideSecZero.addClass(ClassNames.CLOSED).removeClass(ClassNames.OPEN); \/\/ Button image.\n $(Selector.ABOVE_TILES).addClass('sec-zero-closed');\n } else {\n sectionZero.slideDown(300);\n buttonHideSecZero.addClass(ClassNames.OPEN).removeClass(ClassNames.CLOSED); \/\/ Button image.\n $(Selector.ABOVE_TILES).removeClass('sec-zero-closed');\n }\n } else {\n \/\/ Storage not available so we dont know if sec zero was previously collapsed - expand it.\n buttonHideSecZero.addClass(ClassNames.OPEN).removeClass(ClassNames.CLOSED);\n sectionZero.slideDown(300);\n $(Selector.ABOVE_TILES).removeClass('sec-zero-closed');\n }\n };\n\n \/**\n * To be called when a tile is clicked. Get content from server or storage and display or store it.\n * @param {number} courseContextId course context id of this course.\n * @param {number} sectionId section Id for the clicked section.\n * @param {number} sectionNumber the section number of the tile.\n *\/\n var populateAndExpandSection = function(courseContextId, sectionId, sectionNumber) {\n $(Selector.TILE).removeClass(ClassNames.SELECTED);\n openTile = sectionNumber;\n \/\/ Then close all open secs.\n \/\/ Timed to finish in 200 so that it completes well before the opening next.\n $(Selector.MOVEABLE_SECTION).each(function (index, sec) {\n sec = $(sec);\n if (sec.is(\":visible\")) {\n stopVideoPlaying(sec.data(\"section\"), sec.data(\"sectionid\"));\n sec.slideUp(200).removeClass(ClassNames.STATE_VISIBLE); \/\/ Excludes section 0.\n }\n });\n \/\/ Log the fact we viewed the section.\n ajax.call([{\n methodname: \"format_tiles_log_tile_click\", args: {\n coursecontextid: courseContextId,\n sectionnumber: sectionNumber,\n sectionid: sectionId\n }\n }])[0].fail(Notification.exception);\n \/\/ Get the content - use locally stored content first if available.\n var relatedContentArea = $(Selector.SECTION_ID + sectionNumber);\n if (relatedContentArea.find(Selector.ACTIVITY).length > 0) {\n \/\/ There is already some content on the screen so display immediately.\n expandSection(relatedContentArea, sectionNumber);\n\n \/\/ Still contact the server in case content has changed (e.g. restrictions now satisfied).\n getSectionContentFromServer(courseContextId, sectionId).done(function (html, js) {\n setCourseContentHTML(relatedContentArea, html, js);\n });\n } else {\n relatedContentArea.html(loadingIconHtml);\n \/\/ Get from server.\n getSectionContentFromServer(courseContextId, sectionId).done(function (html, js) {\n setCourseContentHTML(relatedContentArea, html, js);\n expandSection(relatedContentArea, sectionNumber);\n }).fail(function (failResult) {\n failedLoadSectionNotify(sectionNumber, failResult, relatedContentArea);\n cancelTileSelections(sectionNumber);\n });\n }\n browserStorage.setLastVisitedSection(sectionNumber);\n\n \/\/ If any moodle dialogues are open, close them (e.g. glossary auto links).\n $(Selector.MOODLE_DIALOGUE).remove();\n };\n\n const removeUrlParam = function (pattern) {\n if ((window.location.href).match(pattern)) {\n history.pushState(null, null, (window.location.href).replace(pattern, ''));\n }\n };\n\n return {\n init: function (\n courseIdInit,\n useJavascriptNav, \/\/ Set by site admin see settings.php.\n isMobileInit,\n sectionNum,\n useFilterButtons, \/\/ If > 0 then are using filters (value depends on filter type).\n assumeDataStoreConsent, \/\/ Set by site admin see settings.php.\n reopenLastSectionInit, \/\/ Set by site admin see settings.php.\n userId,\n fitTilesToWidth,\n enableCompletionInit,\n useSubTiles,\n courseContextIdInit\n ) {\n courseId = parseInt(courseIdInit);\n courseContextId = courseContextIdInit;\n isMobile = isMobileInit;\n \/\/ Some args are strings or ints but we prefer bool. Change to bool now as they are passed on elsewhere.\n reopenLastVisitedSection = reopenLastSectionInit === \"1\";\n assumeDataStoreConsent = assumeDataStoreConsent === \"1\";\n enableCompletion = enableCompletionInit === \"1\";\n \/\/ We want to initialise the browser storage JS module for storing user settings.\n browserStorage.init(\n courseId,\n false,\n sectionNum,\n assumeDataStoreConsent,\n userId\n );\n $(document).ready(function () {\n const isMultiSectionPage = $(Selector.TILES).length === 1;\n\n if (useSubTiles) {\n \/\/ We need to be able to style tooltips outside of ul.tiles element.\n $(Selector.BODY).addClass('format-tiles-subtiles');\n }\n var pageContent = $(\"#page-content\");\n if (pageContent.length === 0) {\n \/\/ Some themes e.g. RemUI do not have a #page-content div, so use #region-main.\n pageContent = $(\"#region-main\");\n }\n\n \/\/ If we are being told to launch a section number from the URL, use that.\n if (sectionNum !== 0) {\n openTile = sectionNum;\n } else {\n \/\/ Don't use the URL param - check local storage instead.\n if (reopenLastVisitedSection && browserStorage.storageEnabledLocal) {\n openTile = browserStorage.getLastVisitedSection();\n \/\/ If user is not on mobile, retrieve last visited section id from browser storage (if present).\n \/\/ And click it.\n }\n }\n\n \/\/ If there is an anchor e.g. #module-123 then open relevant section.\n const anchorMatches = (window.location.href).match(\/#module-(\\d+)$\/gi);\n if (anchorMatches && anchorMatches.length) {\n const anchorCmId = parseInt(anchorMatches[0].split('-')[1]);\n if (anchorCmId) {\n \/\/ Set openTile to null for now so that null is passed to tile fitter before AJAX below returns.\n openTile = null;\n ajax.call([{\n methodname: \"format_tiles_get_course_mod_info\", args: {cmid: anchorCmId}\n }])[0].done(function (data) {\n if (data && data.courseid === courseId) {\n openTile = data.sectionnumber;\n if (useJavascriptNav) {\n populateAndExpandSection(\n data.coursecontextid, data.sectionid, data.sectionnumber\n );\n }\n }\n });\n }\n }\n\n if (openTile !== 0) {\n tileFitter.init(courseId, openTile, fitTilesToWidth, false);\n } else {\n tileFitter.init(courseId, null, fitTilesToWidth, false);\n }\n\n \/\/ We are going to watch for changes to size of main tiles window.\n \/\/ This allows us to call the tile fitter to re-org tiles if needed.\n const pageContentElem = $('#page-content');\n \/\/ In case some themes don't have a page content div, use window as alternative.\n const widthObservedElement = pageContentElem.length ? pageContentElem : $(window);\n var observedElementWidth = widthObservedElement.outerWidth();\n\n if (useJavascriptNav) {\n \/\/ User is not editing but is usingJS nav to view.\n\n \/\/ On a tile click, decide what to do an do it.\n \/\/ (Collapse if already expanded, or expand it and fill with content).\n pageContent.on(Event.CLICK, Selector.TILE_CLICKABLE, function (e) {\n \/\/ Prevent the link being followed to reload the PHP page as we are using JS instead.\n if (!useJavascriptNav) {\n return;\n }\n const target = $(e.target);\n if (target.hasClass(ClassNames.ON_TILE_CONTROL)) {\n \/\/ The user has clicked a control on the tile so we don't expand it.\n return;\n }\n e.preventDefault();\n \/\/ If other tiles have loading icons, fade them out (on the tile not the content sec).\n $(Selector.TILE_LOADING_ICON).fadeOut(300, function () {\n $(Selector.TILE_LOADING_ICON).html();\n });\n var thisTile = $(e.currentTarget).closest(Selector.TILE);\n var dataSection = parseInt(thisTile.data(\"section\"));\n if (thisTile.hasClass(ClassNames.SELECTED)) {\n \/\/ This tile is already expanded so collapse it.\n cancelTileSelections(dataSection);\n browserStorage.setLastVisitedSection(0);\n overlay.fadeOut(300);\n } else {\n populateAndExpandSection(courseContextId, thisTile.data('true-sectionid'), dataSection);\n }\n });\n\n overlay.on(Event.CLICK, function(e) {\n cancelTileSelections(0);\n browserStorage.setLastVisitedSection(0);\n clickItemBehind(e);\n });\n\n \/\/ When window is re-sized, content sections under the tiles may be in wrong place.\n \/\/ So remove them and re-initialise them.\n \/\/ Collapse the selected section before doing this.\n \/\/ Otherwise the re-organisation won't work as the tiles' flow will be out when they are analysed.\n \/\/ We use the multi_section_tiles element to capture left and right drawer opening\/closing.\n const resizeObserver = new ResizeObserver(() => {\n \/\/ On iOS resize events are triggered often on scroll because the address bar hides itself.\n \/\/ Avoid this using observedElementWidth here.\n if (observedElementWidth === widthObservedElement.outerWidth()) {\n return;\n }\n\n \/\/ We wait for a short time before doing anything, as user may still be dragging window size change.\n \/\/ We don't want to react to say 20 resize events happening over a single drag.\n clearTimeout(resizeTimeout);\n resizeTimeout = setTimeout(function() {\n\n if (reorgSectionsDisabledUntil > Date.now()) {\n \/\/ We wait until inside our timeout for this as we may be responding to a fullscreen event.\n return;\n }\n\n \/\/ First assume that we are going to resize, but we have checks to make below.\n var resizeRequired = true;\n\n \/\/ If we have an iframe in the section in fullscreen, ignore this resize event.\n \/\/ It was probably caused when user pressed the full screen button.\n \/\/ This could be a Moodle media player div, or a YouTube embed or other.\n var openContentSection = $(\".moveablesection:visible\");\n if (openContentSection.length !== 0) {\n var iframes = openContentSection.find(\"iframe\");\n if (iframes.length !== 0) {\n iframes.each(function (index, player) {\n player = $(player);\n if (player.outerWidth() > openContentSection.outerWidth()) {\n \/\/ Video is present and playing full screen so don't react to resize event.\n resizeRequired = false;\n }\n });\n }\n }\n if (resizeRequired) {\n \/\/ Set global for comparison next time.\n observedElementWidth = widthObservedElement.outerWidth();\n reOrgSections(fitTilesToWidth);\n }\n }, 100);\n });\n\n resizeObserver.observe(document.getElementById('page-content'));\n\n \/\/ When user clicks to close a section using cross at top right in section.\n pageContent.on(Event.CLICK, Selector.CLOSE_SEC_BTN, function (e) {\n const currentSectionNumber = $(e.currentTarget).data(\"section\");\n cancelTileSelections(currentSectionNumber);\n \/\/ For screen readers, move focus back to tile just closed so they can advance from there.\n $('#sectionlink-' + currentSectionNumber).focus();\n });\n\n setSectionZeroFromUserPref();\n \/\/ Most filter button related JS is in filter_buttons.js module which is required below.\n\n if (isMultiSectionPage) {\n \/\/ Remove section and cmid URL params if present as we are using JS nav and showing all tiles.\n removeUrlParam(\/(&|\\\\?)cmid=\\d+\/gi);\n removeUrlParam(\/(&|\\\\?)section=\\d+\/gi);\n }\n } else if (fitTilesToWidth) {\n tileFitter.resizeTilesDivWidth(courseId);\n }\n\n \/\/ If this event is triggered, user has updated a completion check box.\n \/\/ We need to retrieve section content from server in case availability of items has changed.\n \/\/ Will also be triggered on focus change e.g. user has returned to this tab from a new window.\n $(document).on('format-tiles-completion-changed', function(e, data) {\n if (data.courseid && parseInt(courseId) !== parseInt(data.courseid)) {\n return;\n }\n const allSectionNums = $(Selector.TILE).not(Selector.SPACER).map((i, tile) => {\n return parseInt($(tile).data('section'));\n }).toArray();\n \/\/ Need to include sec zero as may have completion tracked items.\n allSectionNums.push(0);\n\n \/\/ Get the section ID from section number.\n const contentArea = $(Selector.SECTION_ID + data.section);\n const sectionId = contentArea.data('sectionid')\n ?? contentArea.data('section-id');\n \/\/ This gets the fragment from format_tiles_output_fragment_get_cm_list().\n Fragment.loadFragment(\n 'format_tiles', 'get_cm_list', courseContextId, {sectionid: sectionId}\n )\n .done((html, js) => {\n setCourseContentHTML(contentArea, html, js);\n })\n .catch(err => {\n require([\"core\/log\"], function(log) {\n log.debug(err);\n });\n });\n\n ajax.call([\n {\n methodname: \"format_tiles_get_section_information\",\n args: {\n courseid: courseId,\n sectionnums: allSectionNums\n }\n }\n ])[0]\n .done((response) => {\n require([\"format_tiles\/completion\"], function (completion) {\n completion.updateSectionsInfo(\n response.sections, response.overall.complete, response.overall.outof\n );\n });\n\n })\n .catch(err => {\n require([\"core\/log\"], function(log) {\n log.debug(err);\n });\n });\n });\n\n if (enableCompletion) {\n \/\/ We use pageContent for listener here, as completion button is replaced by core JS when it's clicked.\n \/\/ This is for non-subtiles only.\n \/\/ We wait half a second to enable the completion change to be registered first.\n pageContent.on(Event.CLICK, Selector.MANUAL_COMPLETION, function(e) {\n const currentTarget = $(e.currentTarget);\n const sectionNum = currentTarget.closest(Selector.SECTION_MAIN).data(\"section\");\n const cmid = currentTarget.data(\"cmid\");\n require([\"format_tiles\/completion\"], function (completion) {\n setTimeout(() => {\n completion.triggerCompletionChangedEvent(\n sectionNum ? parseInt(sectionNum) : 0,\n cmid ? parseInt(cmid) : 0\n );\n }, 500);\n });\n });\n }\n\n const sectionZero = $(Selector.SECTION_ZERO);\n\n \/\/ When the user presses the button to collapse or expand Section zero (section at the top of the course).\n pageContent.on(Event.CLICK, Selector.HIDE_SEC0_BTN, function (e) {\n if (sectionZero.css(CSS.DISPLAY) === \"none\") {\n \/\/ Sec zero is collapsed so expand it on user click.\n sectionZero.slideDown(250);\n $(Selector.ABOVE_TILES).removeClass('sec-zero-closed');\n $(e.currentTarget).addClass(ClassNames.OPEN).removeClass(ClassNames.CLOSED);\n browserStorage.setSecZeroCollapseStatus(\"collapsed\");\n } else {\n \/\/ Sec zero is expanded so collapse it on user click.\n sectionZero.slideUp(250);\n $(Selector.ABOVE_TILES).addClass('sec-zero-closed');\n $(e.currentTarget).addClass(ClassNames.CLOSED).removeClass(ClassNames.OPEN);\n browserStorage.setSecZeroCollapseStatus(\"expanded\");\n }\n });\n\n if (useFilterButtons) {\n require([\"format_tiles\/filter_buttons\"], function (filterButtons) {\n filterButtons.init(courseId, browserStorage.storageEnabledLocal);\n });\n if (useJavascriptNav) {\n pageContent.on(Event.CLICK, Selector.FILTER_BUTTON, function () {\n cancelTileSelections(0);\n reOrgSections(false);\n });\n }\n\n }\n \/\/ If theme is displaying the .tiles_coursenav class items, show items with this class.\n \/\/ They will be hidden otherwise.\n \/\/ They are hidden when initially rendered from PHP as we only want them shown if browser supports JS.\n \/\/ See lib.php extend_course_navigation.\n $(\".tiles_coursenav\").removeClass(\"hidden\");\n\n \/\/ Render the loading icon and store its HTML globally so that we can use it where needed later.\n Templates.render(\"format_tiles\/loading\", {}).done(function (html) {\n loadingIconHtml = html;\n });\n\n \/\/ Get these strings now, in case we need them.\n \/\/ E.g. after we lose connection and cannot display content on a user tile click.\n var stringKeys = [\n {key: \"sectionerrortitle\", component: \"format_tiles\"},\n {key: \"sectionerrorstring\", component: \"format_tiles\"},\n {key: \"refresh\"},\n {key: \"cancel\", component: \"moodle\"},\n {key: \"noconnectionerror\", component: \"format_tiles\"},\n {key: \"show\"},\n {key: \"hide\"},\n {key: \"other\", component: \"format_tiles\"}\n ];\n str.get_strings(stringKeys).done(function (s) {\n s.forEach(function(str, index) {\n if (str) {\n stringStore[stringKeys[index].key] = str;\n } else {\n stringStore[stringKeys[index].key] = 'Error.';\n require([\"core\/log\"], function(log) {\n log.debug(`Format tiles get_strings error ${index}`);\n log.debug(s);\n });\n }\n });\n })\n .fail(function(err) {\n require([\"core\/log\"], function(log) {\n log.debug(err);\n });\n });\n\n \/\/ When a section is open, fix close\/edit buttons to top of screen (else hidden on scroll).\n let fixButtonsDisabled = false;\n $(window).scroll(function() {\n if (!fixButtonsDisabled) {\n try {\n if ($(window).scrollTop() >= 300) {\n $('.moveablesection.state-visible').each((i, s) => {\n s = $(s);\n const section = document.getElementById('section-' + s.data('section'));\n const sectionRect = section.getBoundingClientRect();\n const right = document.body.clientWidth - sectionRect.right + 30;\n const sectionButtons = s.find('.sectionbuttons');\n const topMargin = $(\"#page\").offset().top;\n if (sectionRect.top + topMargin < 0 && sectionRect.bottom - topMargin > 0) {\n sectionButtons.addClass('position-fixed');\n sectionButtons.css({'top': topMargin + 10, 'right': right});\n } else {\n sectionButtons.removeClass('position-fixed');\n sectionButtons.css({'top': 'unset', 'right': 'unset'});\n }\n });\n }\n } catch (err) {\n require([\"core\/log\"], function(log) {\n log.debug(err);\n });\n fixButtonsDisabled = true;\n }\n }\n });\n\n \/\/ The URL may include a section ID in the form \"#sectionid-xx-title\" where xx is section ID.\n \/\/ This would be from a section \"permalink\".\n \/\/ We cannot get that value in PHP so try redirect from here instead.\n \/\/ This is not needed from Moodle 4.4+ as then the section.php URL is used for permalinks.\n const urlPattern = \/.*\\\/course\\\/view\\.php\\?id=([\\d]+)#sectionid-([\\d+]+)-title\/;\n const urlMatches = window.location.href.match(urlPattern);\n if (urlMatches && urlMatches.length === 3) {\n const sectionId = urlMatches[2];\n const redirectUrl = urlMatches[0].replace(\n `#sectionid-${sectionId}-title`, `§ionid=${sectionId}`\n );\n window.location.replace(redirectUrl);\n }\n });\n },\n populateAndExpandSection(courseContextId, sectionId, sectionNumber) {\n populateAndExpandSection(courseContextId, sectionId, sectionNumber);\n }\n };\n }\n);"],"names":["define","$","Templates","ajax","browserStorage","Notification","str","tileFitter","Fragment","isMobile","loadingIconHtml","courseId","courseContextId","stringStore","reopenLastVisitedSection","resizeTimeout","enableCompletion","reorgSectionsDisabledUntil","openTile","Selector","ClassNames","Event","CSS","Keyboard","stopVideoPlaying","section","sectionId","contentSection","find","each","index","iframe","attr","data","length","html","getSectionContentFromServer","done","js","setCourseContentHTML","cancelTileSelections","sectionToFocus","sec","is","slideUp","removeClass","css","undefined","focus","fadeOut","overlay","remove","contentArea","not","on","e","keyCode","toClick","currentTarget","window","location","href","focusableElements","firstFocusableElement","eq","lastFocusableElement","shiftKey","relatedTarget","closest","setTimeout","tooltipItems","tooltip","err","require","log","debug","i","el","height","addClass","filter","script","runTemplateJS","applyMathJax","videoJS","setUp","forEach","ev","document","addEventListener","Date","now","trigger","parseInt","sectionid","MathJax","mathJaxElems","node","Hub","Queue","expandSection","sectionNumber","tile","fadeIn","slideDown","scrollTo","offset","top","scrollTop","events","page","stop","animate","off","iframes","courseid","expandAndScroll","reOrgSections","fitTilesToScreenWidth","dfd","Deferred","resolve","reOrgFunc","runReOrg","result","fail","reject","resizeTilesDivWidth","promise","toString","loadFragment","populateAndExpandSection","call","methodname","args","coursecontextid","sectionnumber","exception","relatedContentArea","failResult","sectionNum","confirm","sectionerrortitle","sectionerrorstring","refresh","cancel","reload","noconnectionerror","Error","failedLoadSectionNotify","setLastVisitedSection","removeUrlParam","pattern","match","history","pushState","replace","init","courseIdInit","useJavascriptNav","isMobileInit","useFilterButtons","assumeDataStoreConsent","reopenLastSectionInit","userId","fitTilesToWidth","enableCompletionInit","useSubTiles","courseContextIdInit","ready","isMultiSectionPage","pageContent","storageEnabledLocal","getLastVisitedSection","anchorMatches","anchorCmId","split","cmid","pageContentElem","widthObservedElement","observedElementWidth","outerWidth","target","hasClass","preventDefault","thisTile","dataSection","clickedItem","hide","BottomElement","elementFromPoint","clientX","clientY","show","click","clickedTile","clickItemBehind","ResizeObserver","clearTimeout","resizeRequired","openContentSection","player","observe","getElementById","currentSectionNumber","buttonHideSecZero","sectionZero","getSecZeroCollapseStatus","setSectionZeroFromUserPref","allSectionNums","map","toArray","push","catch","sectionnums","response","completion","updateSectionsInfo","sections","overall","complete","outof","triggerCompletionChangedEvent","setSecZeroCollapseStatus","filterButtons","render","stringKeys","key","component","get_strings","s","fixButtonsDisabled","scroll","sectionRect","getBoundingClientRect","right","body","clientWidth","sectionButtons","topMargin","bottom","urlMatches","redirectUrl"],"mappings":"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"}