Omniplus

Enhanced Omnivox Experience

Fra 08.04.2022. Se den seneste versjonen.

// ==UserScript==
// @name         Omniplus
// @version      0.3.6
// @description  Enhanced Omnivox Experience
// @author       Jerry
// @match        https://*.omnivox.ca/*
// @icon         https://www.google.com/s2/favicons?domain=omnivox.ca
// @grant        none
// @license      MIT
// @namespace https://greasyfork.org/users/881234
// ==/UserScript==

/******/ (() => { // webpackBootstrap
/******/ 	"use strict";
/******/ 	var __webpack_modules__ = ({

/***/ 277:
/***/ ((__unused_webpack_module, exports, __webpack_require__) => {


Object.defineProperty(exports, "__esModule", ({ value: true }));
exports.LeaDocumentsContainer = void 0;
const renderable_1 = __webpack_require__(843);
const course_document_list_1 = __webpack_require__(984);
const dom_builder_1 = __webpack_require__(205);
class LeaDocumentsContainer extends renderable_1.Renderable {
    constructor(documents) {
        // Start with the loading class.
        super('div', 'course-list-loading');
        this.courses = [];
        // Whether the courses have been loaded or not.
        this.ready = false;
        // Add the documents asynchronously.
        documents.then((courses) => {
            // Mark as loaded.
            this.ready = true;
            courses.forEach((course) => this.courses.push(course));
            // Call for a re-render if the container has been rendered already.
            if (this.lastRenderInfo) {
                this.rerender();
            }
        });
    }
    // Loads all courses and all documents from the document overview page on Lea.
    static loadFromDocumentOverviewPage(page) {
        const coursePromises = [];
        // Courses are placed within either table row elements with either the itemDataGrid or the
        // .itemDataGridAltern class, which correspond to even and odd-numbered documents.
        page.querySelectorAll('.itemDataGrid, .itemDataGridAltern').forEach((rowElement) => {
            // The link to the course is stored in the <a> element with the class .DisDoc_Sommaire_NomCours.
            const courseLink = rowElement.querySelector('.DisDoc_Sommaire_NomCours').href;
            // Await everything together to load faster.
            coursePromises.push(course_document_list_1.CourseDocumentList.loadFromCourseDocumentsURL(courseLink));
        });
        // Wait for all of them at once.
        return new LeaDocumentsContainer(Promise.all(coursePromises));
    }
    updateDomElement(renderInfo) {
        if (this.ready) {
            if (this.domElement.classList.contains('course-list-loading')) {
                this.domElement.classList.remove('course-list-loading');
            }
            if (!this.domElement.classList.contains('course-list')) {
                // Restore the tag after loading is complete.
                this.domElement.classList.add('course-list');
            }
            // Render courses only after everything have been loaded.
            // Display only courses that have documents that match the search term.
            this.courses.filter((course) => course.hasDocumentMatch(renderInfo.search))
                .forEach((course) => this.domElement.appendChild(course.render(renderInfo)));
        }
        else {
            // While the container is loading, insert the loading element.
            this.domElement.appendChild(new dom_builder_1.ElementBuilder('div')
                .withStyleClasses('loading-spinner')
                .build());
        }
    }
    markAllDocumentsAsRead() {
        this.courses.forEach((course) => course.markAllDocumentsAsRead());
    }
}
exports.LeaDocumentsContainer = LeaDocumentsContainer;


/***/ }),

/***/ 984:
/***/ ((__unused_webpack_module, exports, __webpack_require__) => {


Object.defineProperty(exports, "__esModule", ({ value: true }));
exports.CourseDocumentList = void 0;
const document_1 = __webpack_require__(774);
const util_1 = __webpack_require__(794);
const dom_builder_1 = __webpack_require__(205);
const renderable_1 = __webpack_require__(843);
const spaceRegex = new RegExp('\\s');
// Represents a course rendered on the documents overview..
class CourseDocumentList extends renderable_1.Renderable {
    constructor(name, courseCode, documents = []) {
        super('div', 'course');
        this.courseName = name;
        this.courseCode = courseCode;
        this.documents = documents;
        this.sortDocuments();
    }
    // Load a course from its *components* page.
    static fromDocumentPage(page) {
        // The course code and course name are placed inside the second line of title on the top of the course
        // components page.
        const courseTitle = page.querySelector('.TitrePageLigne2').innerText;
        // The course title has the following format:
        // <Course Code> <Course Name> section <section number>
        // To extract the course code and the name, split the title by 'section', pick the first part and then trim
        // off the extra space on the right.
        // Pre-split the course code and names, note that Omnivox sometimes uses non-breaking space instead of
        // regular space, hence the use of the \s regex.
        const courseCodeAndName = courseTitle.split('section')[0].trim().split(spaceRegex);
        // The course code and name are separated by a space, the first element is the course code.
        const courseCode = courseCodeAndName[0];
        // Since course names may contain spaces, the rest of the elements make up the course.
        // Convert the whole thing from all caps to title case.
        const courseName = (0, util_1.toTitleCase)(courseCodeAndName.slice(1).join(' ')
            // The course name itself is structured as follows:
            // [Program] [-] <Course Name>
            // The program and the dash may not exist, but as an unfortunate Arts and Science student, it bothers me
            // that it blocks the course name.
            // Split the course name by '-', pick the last part and trim off the extra space on the left.
            .split('-')
            // Meaning to pick the last element here but since there's no implementation of it this will suffice.
            .reverse()[0].trim());
        // Extract the documents.
        const documents = document_1.LeaDocument.loadFromCourseDocumentPage(page);
        return new CourseDocumentList(courseName, courseCode, documents);
    }
    // Extracts a course and its documents from a url to a course components page.
    static loadFromCourseDocumentsURL(url) {
        return (0, util_1.fetchDocumentFrom)(url).then((parsedDocument) => CourseDocumentList.fromDocumentPage(parsedDocument));
    }
    // Sorts the documents based on their read-status and their upload date.
    sortDocuments() {
        this.documents.sort((a, b) => {
            // Note that the comparison is in reverse because we want to display later documents (with greater time
            // value) first.
            // If the two have the same read status, compare the date.
            if (a.read == b.read) {
                return b.uploadDate.valueOf() - a.uploadDate.valueOf();
            }
            // Otherwise compare the read status.
            else {
                return (b.read ? 0 : 1) - (a.read ? 0 : 1);
            }
        });
    }
    updateDomElement(renderInfo) {
        this.domElement.append(new dom_builder_1.ElementBuilder('div')
            .withStyleClasses('course-name')
            .withText(this.courseName)
            .build(), new dom_builder_1.ElementBuilder('div')
            .withStyleClasses('documents-list')
            // Filter the documents to show only ones that match.
            .withChildren(...this.documents.filter((leaDocument) => leaDocument.nameContains(renderInfo.search))
            .map((leaDocument) => leaDocument.render(renderInfo)))
            .build());
    }
    // Returns true if the course has any documents with a name that includes the search term.
    hasDocumentMatch(search) {
        return this.documents.some((leaDocument) => leaDocument.nameContains(search));
    }
    markAllDocumentsAsRead() {
        this.documents.forEach((leaDocument) => leaDocument.markAsRead());
    }
}
exports.CourseDocumentList = CourseDocumentList;


/***/ }),

/***/ 717:
/***/ ((__unused_webpack_module, exports) => {


Object.defineProperty(exports, "__esModule", ({ value: true }));
exports.LeaDocumentType = void 0;
var LeaDocumentType;
(function (LeaDocumentType) {
    LeaDocumentType[LeaDocumentType["Link"] = 0] = "Link";
    LeaDocumentType[LeaDocumentType["File"] = 1] = "File";
    LeaDocumentType[LeaDocumentType["Video"] = 2] = "Video";
    LeaDocumentType[LeaDocumentType["YouTube"] = 3] = "YouTube";
})(LeaDocumentType = exports.LeaDocumentType || (exports.LeaDocumentType = {}));


/***/ }),

/***/ 774:
/***/ ((__unused_webpack_module, exports, __webpack_require__) => {


Object.defineProperty(exports, "__esModule", ({ value: true }));
exports.LeaDocument = void 0;
const util_1 = __webpack_require__(794);
const dom_builder_1 = __webpack_require__(205);
const renderable_1 = __webpack_require__(843);
const document_type_1 = __webpack_require__(717);
// Represents a components under a course on Lea.
class LeaDocument extends renderable_1.Renderable {
    constructor(name, description, read, uploadDate, originalOpenAction, type) {
        super('div', 'mdl-card', 'mdl-shadow--2dp', 'lea-document');
        this.name = name;
        this.description = description;
        this.read = read;
        this.uploadDate = uploadDate;
        this.originalOpenAction = originalOpenAction;
        this.type = type;
        // The url takes a bit of time to obtain, setting it to '#' as a filler.
        this.url = '#';
        // Extract the url asynchronously.
        LeaDocument.extractDocumentURL(originalOpenAction, type)
            .then((url) => {
            // Update the url.
            this.url = url;
            // And call for a rerender if the document has been rendered already.
            if (this.lastRenderInfo) {
                this.rerender();
            }
        });
    }
    // Loads relevant information of a components from its table row element.
    static fromElement(rowElement) {
        // Fetch the name from the <a> element responsible for the title of the components.
        const name = rowElement.querySelector('.lblTitreDocumentDansListe').innerText.trim();
        // The description of the child element is in a text node that is the parent of the name element.
        const description = rowElement.querySelector('.divDescriptionDocumentDansListe')
            // This means that innerText of the element will also include the name of the components.
            // Luckily, the description text node is the last child of the element.
            // However, this description node's content also includes many `\t` characters at its start and end, which
            // need to be removed with `trim()`.
            .lastChild.textContent.trim();
        // The status of the components is indicated by a star icon that appears if it has not been read. Check if that
        // icon is present to see if the components has been read.
        const read = rowElement.querySelector('.classeEtoileNouvDoc') == null;
        const date = LeaDocument.extractDocumentDate(rowElement.querySelector('.DocDispo').innerText);
        // The download url of the components is placed in an <a> element that is in the element with the class
        // .colVoirTelecharger.
        const originalOpenAction = rowElement.querySelector('.colVoirTelecharger a').href;
        const type = LeaDocument.determineDocumentTypeFromOpenAction(originalOpenAction);
        return new LeaDocument(name, description, read, date, originalOpenAction, type);
    }
    // Scrapes all elements from documents page of a given course.
    static loadFromCourseDocumentPage(page) {
        const documents = [];
        // Documents are placed within either table row elements with either the itemDataGrid or the
        // .itemDataGridAltern class, which correspond to even and odd-numbered documents.
        page.querySelectorAll('.itemDataGrid, .itemDataGridAltern').forEach((rowElement) => documents.push(LeaDocument.fromElement(rowElement)));
        return documents;
    }
    // Extracts all documents on the page from an url to a course components page.
    static loadFromCourseDocumentsURL(url) {
        return (0, util_1.fetchDocumentFrom)(url).then((parsedDocument) => LeaDocument.loadFromCourseDocumentPage(parsedDocument));
    }
    // Determines the type of the document from the href of the anchor element that opens it.
    static determineDocumentTypeFromOpenAction(href) {
        // Both file and link documents have a link in their href that includes
        // VisualiseDocument.aspx.
        if (href.includes('VisualiseDocument.aspx')) {
            // Link documents though refer to VisualiseDocument.aspx, have a
            // JavaScript execution in their href. More specifically, they have a
            // special line of code that contains the link to be redirected to.
            // Their href look something like this:
            // javascript:CallPageVisualiseDocument('VisualiseDocument.aspx?...');
            // ValiderLienDocExterne('Link Encoded in URI');
            // which means that they can be identified by their use of
            // 'ValiderLienDocExterne'.
            if (href.includes('ValiderLienDocExterne')) {
                return document_type_1.LeaDocumentType.Link;
            }
            // File documents have a direct link in their href that sends the user to
            // a new tab to directly download the document. Their links look
            // something like this:
            // VisualiseDocument.aspx?...
            else {
                return document_type_1.LeaDocumentType.File;
            }
        }
        // Both YouTube and media document types have the href containing a
        // JavaScript execution that contains the link to VisualiseVideo.aspx.
        else if (href.includes('VisualiseVideo.aspx')) {
            // Links that open a media has the second argument of the JavaScript
            // code set to false, which makes it looks like this:
            // javascript:VisualiserVideo('VisualiseVideo.aspx?...', false);
            if (href.includes('false')) {
                return document_type_1.LeaDocumentType.Video;
            }
            // While links that open a YouTube video has the second argument of the
            // JavaScript code set to true, which makes it look like this:
            // javascript:VisualiserVideo('VisualiseVideo.aspx?...', true);
            else if (href.includes('true')) {
                return document_type_1.LeaDocumentType.YouTube;
            }
        }
        // Default to file if all checks fail.
        return document_type_1.LeaDocumentType.File;
    }
    // Extracts the link of the document based on its type. Used to fetch the link and YouTube url for non-files.
    static extractDocumentURL(openAction, type) {
        // Decoding the component before matching because SOME browsers decide to encode the quotation
        // marks in URI too.
        const openActionDecoded = decodeURIComponent(openAction);
        switch (type) {
            case document_type_1.LeaDocumentType.File:
                // Files do not need any modifications.
                return new Promise((resolve, reject) => resolve(openAction));
            case document_type_1.LeaDocumentType.Link:
                // Link documents have a href that looks like this:
                // javascript:CallPageVisualiseDocument('VisualiseDocument.aspx?...');
                // ValiderLienDocExterne('Link Encoded in URI');
                return new Promise((resolve, reject) => {
                    // The link can be extracted by matching for the second string contained in single quotation marks.
                    resolve(openActionDecoded.match(LeaDocument.quotationMarksRegex)[1]
                        // Remove the quotation marks.
                        .replaceAll("'", ''));
                });
            case document_type_1.LeaDocumentType.Video:
                // Video href have the following format:
                // javascript:VisualiserVideo('VisualiseVideo.aspx?...', false);
                // The link to the video preview can be obtained by matching for
                // everything inside the quotation marks and trimming out the quotation
                // marks.
                return new Promise((res, rej) => res(openActionDecoded.match(this.quotationMarksRegex)[0].replaceAll("'", '')
                    // However, since the tokens and info are exactly the same after
                    // the .aspx, the true download link can simply be obtained by
                    // swapping the 'VisualiseVideo' with 'VisualiseDocument'.
                    .replace('VisualiseVideo', 'VisualiseDocument')));
            case document_type_1.LeaDocumentType.YouTube:
                // YouTube href have the following format.
                // javascript:VisualiserVideo('VisualiseVideo.aspx?...', true);
                // The link in the script can be extracted by matching for quotation
                // marks, trimming them out, and then adding to the root link.
                return (0, util_1.fetchDocumentFrom)(openActionDecoded.match(this.quotationMarksRegex)[0].replaceAll("'", ''))
                    // The true YouTube link is stored in the href of the anchor element
                    // with the class .Gen_Btn...
                    .then((document) => document.querySelector('.Gen_Btn'))
                    .then((anchor) => anchor.href)
                    // in the following format:
                    // javascript:OpenCentre('YouTube Link Encoded in URI', ...); Close();
                    .then((href) => decodeURIComponent(href.match(LeaDocument.quotationMarksRegex)[0].replaceAll("'", '')));
        }
    }
    static extractDocumentDate(dateString) {
        // On Marianopolis's documents page, document dates are formatted in one
        // of the two following ways:
        // since<Month> <Day>, <Year>
        // from <Month> <Day>, <Year> to <Month> <Date>, <Year>
        // For the first case, there is no space between the "since" and the month, so it is necessary to trim the
        // since out and then fetch the month, day, and year from the indices 0, 1, and 2.
        if (dateString.startsWith('since')) {
            // \u00A0 is a non-breaking space.
            // Trim out the first 5 characters ('since').
            const words = dateString.substring(5).split(/[\u00A0 \n]/g);
            // Months on Omnivox are presented in their abbreviated form.
            const month = (0, util_1.getMonthFromShortened)(words[0]);
            // Remove the comma by removing the last character.
            const day = parseInt(words[1].substring(0, words[1].length - 1));
            const year = parseInt(words[2]);
            return new Date(year, month, day);
        }
        else {
            const words = dateString.split(/[\u00A0 \n]/g);
            const month = (0, util_1.getMonthFromShortened)(words[1]);
            const day = parseInt(words[2].substring(0, words[2].length - 1));
            const year = parseInt(words[3]);
            return new Date(year, month, day);
        }
    }
    // Marks the document as read locally and remotely.
    markAsRead() {
        // Condition to avoid sending too many requests to server.
        if (!this.read) {
            this.read = true;
            // If this is a file, fetch on the download link directly.
            if (this.type == document_type_1.LeaDocumentType.File) {
                fetch(this.url);
            }
            else {
                // Otherwise fetch the first quoted part in the open action to mark the document as read.
                // Remove the quotation marks after matching.
                fetch(this.originalOpenAction.match(LeaDocument.quotationMarksRegex)[0].replaceAll("'", ''));
            }
            // Ignoring the responses of the fetch because the request is only sent to notify the server.
        }
    }
    // Returns the style class (colour) that corresponds to the document type.
    get documentIconTypeStyleClass() {
        switch (this.type) {
            case document_type_1.LeaDocumentType.File:
                return 'file-background';
            case document_type_1.LeaDocumentType.Link:
                return 'link-background';
            case document_type_1.LeaDocumentType.Video:
            case document_type_1.LeaDocumentType.YouTube:
                return 'video-background';
        }
    }
    // Returns the material icon displayed on the document type badge.
    get documentTypeIcon() {
        switch (this.type) {
            case document_type_1.LeaDocumentType.File:
                return 'description';
            case document_type_1.LeaDocumentType.Link:
                return 'link';
            case document_type_1.LeaDocumentType.Video:
                return 'video_file';
            case document_type_1.LeaDocumentType.YouTube:
                return 'video_library';
        }
    }
    // Returns the material icon displayed on the download badge.
    get documentActionIcon() {
        switch (this.type) {
            case document_type_1.LeaDocumentType.File:
                return 'file_download';
            case document_type_1.LeaDocumentType.Link:
                return 'open_in_new';
            case document_type_1.LeaDocumentType.Video:
                return 'play_circle';
            case document_type_1.LeaDocumentType.YouTube:
                return 'open_in_new';
        }
    }
    get formattedDate() {
        const dateStringParts = this.uploadDate.toDateString().split(' ');
        // The date string has the following format:
        // Weekday Month Date Year
        // We desire the following format:
        // Month Date, Year
        return `${dateStringParts[1]} ${dateStringParts[2]}, ${dateStringParts[3]}`;
    }
    updateDomElement(renderInfo) {
        this.domElement.append(new dom_builder_1.ElementBuilder('div')
            .withStyleClasses('badged-card', 'document')
            .withChildren(new dom_builder_1.ElementBuilder('div')
            .withStyleClasses('badge-holder')
            .withChildren(new dom_builder_1.ElementBuilder('div')
            // The first badge represents the type of the document.
            .withStyleClasses('badge', 'material-icons', this.documentIconTypeStyleClass)
            .withText(this.documentTypeIcon)
            .build(), new dom_builder_1.ElementBuilder('a')
            // The second badge is the download button for the document.
            .withStyleClasses('badge', 'material-icons', 'clickable')
            .withText(this.documentActionIcon)
            // Open the link in a new tab.
            .withAttribute('target', '_blank')
            .withAttribute('href', this.url)
            .withEventListener('click', (event) => {
            // Mark the document as read.
            this.markAsRead();
            // Call for a re-render when the read status has been updated.
            this.rerender();
            // Unfocus after the click has been processed.
            event.target.blur();
        })
            .build())
            .build(), new dom_builder_1.ElementBuilder('div')
            .withStyleClasses('card')
            .withChildren(
        // Render the date with the title inside another div so the gap from the card do not
        // separate them.
        new dom_builder_1.ElementBuilder('div')
            .withChildren(
        // Document Name
        this.renderNameHighlight(renderInfo.search), 
        // Upload Date
        new dom_builder_1.ElementBuilder('div')
            .withStyleClasses('date')
            .withText(this.formattedDate)
            .build()).build(), 
        // Only render the description if there is a description.
        ...this.description.length > 0 ? [new dom_builder_1.ElementBuilder('div')
                .withStyleClasses('description')
                .withText(this.description)
                .build()] : [])
            .build())
            .build());
    }
    // Renders the name of the document in DOM while highlighting the search term.
    renderNameHighlight(search) {
        const titleElement = new dom_builder_1.ElementBuilder('div')
            .withStyleClasses('name', 
        // Add the bold tag if the document has not been read.
        ...(this.read ? [] : ['bold']))
            .build();
        // Save the trouble of building the regex and dealing with 0-length matches here.
        if (search.length > 0) {
            // Add the ig flags to ignore case and match globally.
            // Since we are ignoring case, use replace to maintain the original match.
            titleElement.innerHTML = this.name.replace(new RegExp((0, util_1.regexEscape)(search), 'ig'), 
            // God I have sinned in using HTML to make elements. Please forgive me.
            (match) => `<mark>${match}</mark>`);
        }
        else {
            titleElement.appendChild(document.createTextNode(this.name));
        }
        return titleElement;
    }
    nameContains(search) {
        // Ignore casing for this check.
        return this.name.toLowerCase().includes(search.toLowerCase());
    }
}
exports.LeaDocument = LeaDocument;
LeaDocument.quotationMarksRegex = new RegExp("'.+?'", 'g');


/***/ }),

/***/ 824:
/***/ ((__unused_webpack_module, exports, __webpack_require__) => {


Object.defineProperty(exports, "__esModule", ({ value: true }));
exports.injectDocumentsOverviewButtonToLea = void 0;
// Adds a document overview button to the topleft section on Lea.
const dom_builder_1 = __webpack_require__(205);
function injectDocumentsOverviewButtonToLea() {
    // The link to the overview page is stored in the element with the id lienDDLE.
    const summaryAnchor = document.querySelector('#lienDDLE');
    // Potential fix for the teacher version of the website where the summary may not be present.
    if (summaryAnchor) {
        const overviewLink = summaryAnchor.href;
        // All the buttons are contained in this element.
        const buttonsContainer = document.querySelector('#region-raccourcis-services-skytech');
        buttonsContainer.insertBefore(new dom_builder_1.ElementBuilder('a')
            // Mirror the style of the Lea button.
            .withStyleClasses('raccourci', 'id-service_CVIP', 'code-groupe_lea', 'documents-overview-icon-parent')
            .withAttribute('href', overviewLink)
            .withAttribute('title', 'Documents Overview')
            .withChildren(new dom_builder_1.ElementBuilder('div')
            // Mirror the style of the Lea button, but with the material icons class added.
            .withStyleClasses('svg-icon', 'material-icons')
            // Documents icon name
            .withText('description')
            .build(), new dom_builder_1.ElementBuilder('div')
            .withStyleClasses('titre')
            .withText('Docs')
            .build())
            .build(), 
        // The last child is the MIO button, put the overview before the MIO button so it's together with Lea.
        buttonsContainer.lastElementChild);
    }
}
exports.injectDocumentsOverviewButtonToLea = injectDocumentsOverviewButtonToLea;


/***/ }),

/***/ 916:
/***/ ((__unused_webpack_module, exports, __webpack_require__) => {


Object.defineProperty(exports, "__esModule", ({ value: true }));
exports.LeaDocumentsOverview = void 0;
const dom_builder_1 = __webpack_require__(205);
const renderable_1 = __webpack_require__(843);
const render_info_1 = __webpack_require__(426);
const page_patcher_1 = __webpack_require__(50);
class LeaDocumentsOverview extends renderable_1.Renderable {
    constructor(documentsContainer) {
        super('div', 'omniplus-documents-overview', 'omniplus-lea-container');
        this.container = documentsContainer;
    }
    // Injects the container into the document overview page.
    injectToDocumentOverviewPage() {
        // The printer friendly version button blocks the view, not sure why it's there, why it exists, or what's
        // the purpose of printing out an overview like that.
        (0, page_patcher_1.removePrinterFriendlyButton)();
        // Fetch the original container of the overview table.
        const overviewContainer = document.querySelector('.cvirContenuCVIR');
        // Get rid of the centre align.
        overviewContainer.removeAttribute('align');
        // Clear everything off.
        while (overviewContainer.hasChildNodes()) {
            overviewContainer.removeChild(overviewContainer.firstChild);
        }
        overviewContainer.appendChild(this.domElement);
    }
    updateDomElement() {
        this.domElement.append(new dom_builder_1.ElementBuilder('div')
            .withStyleClasses('control-bar')
            .withChildren(new dom_builder_1.ElementBuilder('input')
            .withStyleClasses('search-bar')
            .withAttribute('type', 'text')
            .withAttribute('placeholder', 'Search')
            .withEventListener('input', (event) => {
            // Call for a rerender on the container whenever the input changes.
            // (because the inputs do not need to be rebuilt.)
            this.container.render(new render_info_1.OverviewRenderInfo(event.target.value));
        })
            .build(), 
        // Using an anchor so the element unfocuses after the mouse button has been lifted.
        // Otherwise the focused style will remain active.
        new dom_builder_1.ElementBuilder('a')
            .withStyleClasses('button')
            .withText('Mark All as Read')
            // Add a href so the focus can apply.
            .withAttribute('href', '#')
            .withEventListener('click', (event) => {
            this.container.markAllDocumentsAsRead();
            // Call a rerender.
            this.container.render(new render_info_1.OverviewRenderInfo(
            // The button and the input share a common parent.
            event.target.parentElement.firstElementChild.value));
            // Unfocus after the click has been processed.
            event.target.blur();
        })
            .build()).build(), this.container.render(new render_info_1.OverviewRenderInfo("")));
    }
}
exports.LeaDocumentsOverview = LeaDocumentsOverview;


/***/ }),

/***/ 426:
/***/ ((__unused_webpack_module, exports) => {


Object.defineProperty(exports, "__esModule", ({ value: true }));
exports.OverviewRenderInfo = void 0;
class OverviewRenderInfo {
    constructor(search) {
        this.search = search;
    }
}
exports.OverviewRenderInfo = OverviewRenderInfo;


/***/ }),

/***/ 205:
/***/ ((__unused_webpack_module, exports) => {


Object.defineProperty(exports, "__esModule", ({ value: true }));
exports.ElementBuilder = void 0;
class ElementBuilder {
    constructor(tag) {
        this.styleClasses = [];
        this.children = [];
        this.text = "";
        this.attributes = new Map();
        this.styleRules = new Map();
        this.eventListeners = new Map();
        this.tag = tag;
    }
    withStyleClasses(...classes) {
        classes.forEach((styleClass) => this.styleClasses.push(styleClass));
        return this;
    }
    withChildren(...children) {
        children.forEach((child) => this.children.push(child));
        return this;
    }
    withText(text) {
        this.text = text;
        return this;
    }
    withAttribute(attribute, value) {
        this.attributes.set(attribute, value);
        return this;
    }
    withEventListener(event, listener) {
        this.eventListeners.set(event, listener);
        return this;
    }
    withStyle(rule, value) {
        this.styleRules.set(rule, value);
        return this;
    }
    withInnerHTML(html) {
        this.innerHTML = html;
        return this;
    }
    build() {
        const element = document.createElement(this.tag);
        this.styleClasses.forEach((styleClass) => element.classList.add(styleClass));
        this.children.forEach((child) => element.appendChild(child));
        if (this.innerHTML) {
            element.innerHTML = this.innerHTML;
        }
        if (this.text.length > 0) {
            element.appendChild(document.createTextNode(this.text));
        }
        this.attributes.forEach((value, attribute) => element.setAttribute(attribute, value));
        this.styleRules.forEach((value, rule) => element.style.setProperty(rule, value));
        this.eventListeners.forEach((listener, event) => element.addEventListener(event, listener));
        return element;
    }
}
exports.ElementBuilder = ElementBuilder;


/***/ }),

/***/ 21:
/***/ ((__unused_webpack_module, exports) => {


Object.defineProperty(exports, "__esModule", ({ value: true }));
exports.FontType = void 0;
// The general category of typeface used for the text.
var FontType;
(function (FontType) {
    FontType[FontType["Serif"] = 0] = "Serif";
    FontType[FontType["SansSerif"] = 1] = "SansSerif";
    FontType[FontType["Monospace"] = 2] = "Monospace";
})(FontType = exports.FontType || (exports.FontType = {}));


/***/ }),

/***/ 360:
/***/ ((__unused_webpack_module, exports, __webpack_require__) => {


Object.defineProperty(exports, "__esModule", ({ value: true }));
exports.FormattedText = void 0;
const font_type_1 = __webpack_require__(21);
const dom_builder_1 = __webpack_require__(205);
// List of fonts supported by Omnivox Forum Posts:
// Sans Serif fonts: Arial, MS Sans Serif, Segoe UI, Tahoma, Verdana
// Serif fonts: Garamond, Georgia, Times New Roman
// Monospace: Courier New
// Though there are subtle differences between the fonts, we will be using variants of Roboto for all of
// them for the sake of consistency.
const sansSerifTypefaces = ['Arial', 'MS Sans Serif', 'Segoe UI', 'Tahoma', 'Verdana'];
const serifTypefaces = ['Garamond', 'Georgia', 'Times New Roman'];
const monospaceTypefaces = (/* unused pure expression or super */ null && (['Courier New']));
class FormattedText {
    constructor(isTextNode, tag, content, fontType, additionalDecoration, children) {
        this.isTextNode = false;
        this.children = [];
        this.isTextNode = isTextNode;
        this.tag = tag;
        this.content = content;
        this.fontType = fontType;
        this.additionalTextDecoration = additionalDecoration;
        this.children = children;
    }
    static textNode(content) {
        return new FormattedText(true, null, content);
    }
    static element(tag, fontType, additionalDecoration, children) {
        return new FormattedText(false, tag, null, fontType, additionalDecoration, children);
    }
    // Extracts text formatting from the formatted text.
    static fromContentNode(node) {
        // If this is an element.
        if (node instanceof HTMLElement) {
            // Copy the tag.
            let tag = node.tagName.toLowerCase();
            const elementStyle = getComputedStyle(node);
            // CSS font rules are complex and differ in different browsers, using if contains for the best
            // compatibility.
            const fontFamilyCSSRule = elementStyle.getPropertyValue('font-family');
            let fontType = font_type_1.FontType.SansSerif;
            if (sansSerifTypefaces.some((typeface) => fontFamilyCSSRule.includes(typeface))) {
                fontType = font_type_1.FontType.SansSerif;
            }
            else if (serifTypefaces.some((typeface) => fontFamilyCSSRule.includes(typeface))) {
                fontType = font_type_1.FontType.Serif;
            }
            else {
                fontType = font_type_1.FontType.Monospace;
            }
            // Copy any special text decorations.
            const additionalTextDecoration = elementStyle.getPropertyValue('text-decoration');
            const children = [];
            // Dotted borders means that the element is a quote, which means that it should be organised differently
            // from the rest.
            if (elementStyle.getPropertyValue('border-style').includes('dotted')) {
                // Use the block quote tag instead when the message is a quote.
                tag = 'blockquote';
                // The author's name is contained in the first div element in the dotted container with an image.
                // Using innerText and trim to extract it.
                const authorSaidElement = node.querySelector('div');
                const authorSaid = authorSaidElement.innerText.trim();
                // Since the author name is getting special treatment, remove it so it doesn't get counted twice.
                node.removeChild(authorSaidElement);
                children.push(FormattedText.element('div', font_type_1.FontType.SansSerif, '', [
                    FormattedText.element('strong', font_type_1.FontType.SansSerif, '', [
                        FormattedText.textNode(authorSaid)
                    ])
                ]));
            }
            // And copy all the node's children.
            Array.from(node.childNodes).forEach((node) => children.push(FormattedText.fromContentNode(node)));
            return FormattedText.element(tag, fontType, additionalTextDecoration, children);
        }
        // If this is a text node.
        else {
            return FormattedText.textNode(node.nodeValue);
        }
    }
    // Returns the corresponding Roboto family based on the font category.
    get fontFamily() {
        switch (this.fontType) {
            case font_type_1.FontType.SansSerif:
                return `'Roboto', 'Helvetica', sans-serif`;
            case font_type_1.FontType.Serif:
                return `'Roboto Slab', 'Times New Roman', serif`;
            case font_type_1.FontType.Monospace:
                return `'Roboto Mono', monospace`;
        }
    }
    render() {
        if (this.isTextNode) {
            return document.createTextNode(this.content);
        }
        else {
            return new dom_builder_1.ElementBuilder(this.tag)
                .withStyle('font-family', this.fontFamily ? this.fontFamily : 'inherit')
                .withStyle('text-decoration', this.additionalTextDecoration ? this.additionalTextDecoration : 'inherit')
                .withChildren(...this.children.map((node) => node.render()))
                .build();
        }
    }
}
exports.FormattedText = FormattedText;


/***/ }),

/***/ 141:
/***/ ((__unused_webpack_module, exports, __webpack_require__) => {


Object.defineProperty(exports, "__esModule", ({ value: true }));
exports.ForumMessage = void 0;
// Represents a message within a forum post.
const renderable_1 = __webpack_require__(843);
const dom_builder_1 = __webpack_require__(205);
const formatted_text_1 = __webpack_require__(360);
class ForumMessage extends renderable_1.Renderable {
    constructor(postTime, author, originalContentElement, quoteAction) {
        super('div', 'badged-card', 'message');
        this.contentExceedsHeight = false;
        this.expanded = false;
        this.postTime = postTime;
        this.author = author;
        this.content = formatted_text_1.FormattedText.fromContentNode(originalContentElement);
        this.quoteAction = quoteAction;
        const contentElement = new dom_builder_1.ElementBuilder('div')
            .withStyleClasses('content')
            .withChildren(this.content.render())
            .build();
        // Add the element to the document so the height can be measured.
        document.body.appendChild(contentElement);
        // Cap content height at 200px.
        if (!this.expanded && contentElement.clientHeight > 200) {
            this.contentExceedsHeight = true;
        }
        // Get rid of it after.
        document.body.removeChild(contentElement);
    }
    // Forum messages are not grouped together under common elements per message. Instead a list of table rows are
    // placed under one table to display everything. This makes each forum message take two elements.
    static fromPostElements(timeElement, messageElement) {
        // The post time of the message is stored in the element with the class .titreMsg. Parse the information
        // from it to extract the post time.
        const postTime = ForumMessage.extractMessagePostTime(timeElement.querySelector('.titreMsg').innerText);
        // The name of the author is placed in the element with the class .nomAuteur alongside with the profile
        // button and the MIO button. They can be excluded by accessing the innerText property and trimming out the
        // extra tabs and spaces.
        const author = messageElement.querySelector('.nomAuteur').innerText.trim();
        // The actual message of the message is stored in an element with the class .Msg inside the message element
        // alongside a "quote" button. Choosing the first child to select the message and exclude the button.
        // Not confusing at all, right?
        const contentElement = messageElement.querySelector('.Msg').firstElementChild;
        // The reply button is stored in the anchor element in the message section in an element with the class
        // .liensMsg.
        const quoteAction = messageElement.querySelector('.liensMsg a').href;
        return new ForumMessage(postTime, author, contentElement, quoteAction);
    }
    // Extracts the time when the message was posted from its time string.
    static extractMessagePostTime(time) {
        const tokens = time.split(' ');
        // The time string on forum messages are formatted as follows:
        // YYYY-MM-DD at HH:MM AM/PM OR <yesterday/today>
        const dateStringTokens = tokens[0].split('-');
        let year, monthIndex, day;
        if (tokens[0] === 'yesterday') {
            // Subtract one day to obtain yesterday.
            const yesterday = new Date(Date.now() - 86400000);
            year = yesterday.getFullYear();
            monthIndex = yesterday.getMonth();
            day = yesterday.getDate();
        }
        else if (tokens[0] === 'today') {
            const today = new Date(Date.now());
            year = today.getFullYear();
            monthIndex = today.getMonth();
            day = today.getDate();
        }
        else {
            year = parseInt(dateStringTokens[0]);
            // Months start counting from 0. Subtract 1 from the parsed number.
            monthIndex = parseInt(dateStringTokens[1]) - 1;
            day = parseInt(dateStringTokens[2]);
        }
        const timeStringTokens = tokens[2].split(':');
        const isPM = tokens[3] == 'PM';
        // Mod the parsed hours by 12 before so 12AM becomes 0, and 12PM becomes 0 + 12.
        // Add 12 hours to the hour count if the post is made in PM.
        const hour = parseInt(timeStringTokens[0]) % 12 + (isPM ? 12 : 0);
        const minute = parseInt(timeStringTokens[1]);
        return new Date(year, monthIndex, day, hour, minute);
    }
    // Scrapes all forum messages from documents page of a given course.
    static loadFromForumPostPage(page) {
        // The time of the forum elements have class .enteteMsg and are always table row elements. Only selecting
        // the class adds noise.
        const timeElements = page.querySelectorAll('tr.enteteMsg');
        // Everything else is contained in row elements with .Msg class. Only selecting the class adds noise.
        const messageElements = page.querySelectorAll('tr.Msg');
        // Iterate through both at the same time.
        return Array.from(timeElements).map((timeElement, index) => ForumMessage.fromPostElements(timeElement, messageElements[index]));
    }
    // Formatted post time.
    get formattedTime() {
        // The date string has the following format:
        // Weekday Month Date Year
        // We desire the following format:
        // Month Date, Year
        const dateStringParts = this.postTime.toDateString().split(' ');
        // The time string has the following format:
        // HH:MM:SS GMT-NNNN (Time Zone Name)
        // We desire the following format:
        // HH:MM
        // To obtain the parts, first split by space, then by colon.
        const timeStringParts = this.postTime.toTimeString().split(' ')[0].split(':');
        return `${timeStringParts[0]}:${timeStringParts[1]} ${dateStringParts[1]} ${dateStringParts[2]}, ${dateStringParts[3]}`;
    }
    get contentElement() {
        const element = new dom_builder_1.ElementBuilder('div')
            .withStyleClasses('content')
            .withChildren(this.content.render())
            .build();
        // Shorten if the content exceeds the height limit and the current message has not been expanded.
        if (!this.expanded && this.contentExceedsHeight) {
            element.classList.add('shortened');
        }
        return element;
    }
    get expandButton() {
        return new dom_builder_1.ElementBuilder('a')
            .withStyleClasses('badge', 'material-icons', 'clickable', 'expand')
            // Unfocus after click.
            .withEventListener('click', event => event.target.blur())
            .withEventListener('click', () => {
            // Invert the state
            this.expanded = !this.expanded;
            // And call for a rerender.
            this.render();
        })
            // Change icon based on whether the element has been expanded or not.
            .withText(this.expanded ? 'expand_less' : 'expand_more')
            .build();
    }
    updateDomElement() {
        this.domElement.append(new dom_builder_1.ElementBuilder('div')
            .withStyleClasses('badge-holder')
            .withChildren(new dom_builder_1.ElementBuilder('a')
            .withStyleClasses('badge', 'material-icons', 'clickable')
            .withAttribute('href', this.quoteAction)
            // Unfocus after click.
            .withEventListener('click', event => event.target.blur())
            .withText('format_quote')
            .build(), 
        // Add the expand button if the content exceeds the height limit.
        ...this.contentExceedsHeight ? [this.expandButton] : [])
            .build(), new dom_builder_1.ElementBuilder('div')
            .withStyleClasses('card')
            .withChildren(new dom_builder_1.ElementBuilder('div')
            .withStyleClasses('header')
            .withChildren(
        // Boldface the author name.
        new dom_builder_1.ElementBuilder('b')
            .withStyleClasses('author')
            .withText(this.author)
            .build(), new dom_builder_1.ElementBuilder('span')
            .withStyleClasses('filler')
            .build(), new dom_builder_1.ElementBuilder('span')
            .withStyleClasses('time')
            .withText(this.formattedTime)
            .build())
            .build(), this.contentElement)
            .build());
    }
}
exports.ForumMessage = ForumMessage;


/***/ }),

/***/ 802:
/***/ ((__unused_webpack_module, exports, __webpack_require__) => {


Object.defineProperty(exports, "__esModule", ({ value: true }));
exports.ForumSubject = void 0;
// Represents a whole forum subject.
const forum_message_1 = __webpack_require__(141);
const renderable_1 = __webpack_require__(843);
const page_patcher_1 = __webpack_require__(50);
const dom_builder_1 = __webpack_require__(205);
class ForumSubject extends renderable_1.Renderable {
    constructor(messages, replyAction) {
        super('div', 'omniplus-forum-subject', 'omniplus-lea-container');
        this.expandedAll = false;
        this.scrolledToBottom = false;
        this.messages = messages;
        this.replyAction = replyAction;
    }
    static loadFromForumPostPage(page) {
        // The reply button is an anchor element in the toolbar.
        const replyAction = document.querySelector('.toolbarStrip a').href;
        return new ForumSubject(forum_message_1.ForumMessage.loadFromForumPostPage(page), replyAction);
    }
    updateDomElement() {
        this.domElement.append(new dom_builder_1.ElementBuilder('div')
            .withStyleClasses('controls')
            .withChildren(
        // Reply to subject button
        new dom_builder_1.ElementBuilder('a')
            .withStyleClasses('button', 'primary', 'material-icons')
            .withAttribute('title', 'Reply to Subject')
            .withAttribute('href', this.replyAction)
            // Unfocus after click.
            .withEventListener('click', (event) => event.target.blur())
            .withText('comment')
            .build(), 
        // Scroll to top/buttom button.
        new dom_builder_1.ElementBuilder('a')
            .withStyleClasses('button', 'primary', 'material-icons')
            .withEventListener('click', () => {
            // Toggle expand all and rerender.
            this.scrolledToBottom = !this.scrolledToBottom;
            if (this.scrolledToBottom) {
                this.domElement.scrollTo(0, this.domElement.scrollHeight);
            }
            else {
                this.domElement.scrollTo(0, 0);
            }
            this.render();
        })
            .withText(this.scrolledToBottom ? 'vertical_align_top' : 'vertical_align_bottom')
            .withAttribute('title', this.scrolledToBottom ?
            'Scroll to Bottom' : 'Scroll to Top')
            .build(), 
        // Expand/collapse all button.
        new dom_builder_1.ElementBuilder('a')
            .withStyleClasses('button', 'secondary', 'material-icons')
            .withEventListener('click', () => {
            // Toggle expand all and rerender.
            this.expandedAll = !this.expandedAll;
            this.messages.forEach((message) => {
                message.expanded = this.expandedAll;
                // Rerender on the messages.
                message.render();
            });
            this.render();
        })
            .withText(this.expandedAll ? 'expand_less' : 'expand_more')
            .withAttribute('title', this.expandedAll ? 'Collapse All' : 'Expand All')
            .build())
            .build(), ...this.messages.map((msg) => msg.render()));
    }
    // Injects the container into the document overview page.
    injectToForumSubjectPage() {
        (0, page_patcher_1.removePrinterFriendlyButton)();
        // Fetch the original container of the subject.
        const overviewContainer = document.querySelector('.cvirContenuCVIR');
        // Get rid of the centre align.
        overviewContainer.removeAttribute('align');
        // Clear everything off.
        while (overviewContainer.hasChildNodes()) {
            overviewContainer.removeChild(overviewContainer.firstChild);
        }
        overviewContainer.appendChild(this.domElement);
    }
}
exports.ForumSubject = ForumSubject;


/***/ }),

/***/ 311:
/***/ ((__unused_webpack_module, exports) => {


Object.defineProperty(exports, "__esModule", ({ value: true }));
exports.Assessment = void 0;
// Represents an assessment on Lea.
class Assessment {
    constructor(name, weight, counted, mark, average) {
        this.name = name;
        this.weight = weight;
        this.counted = counted;
        this.grade = mark;
        this.average = average;
    }
    // Extracts a grade represented on Lea as a ratio.
    static extractGrade(gradeText) {
        // Grades have the following format: (grade/total)
        // Start by removing the parentheses.
        const gradeAndTotal = gradeText.substring(1, gradeText.length - 1)
            // Then split by slash to break them apart
            .split('/')
            // And parse them into numbers.
            .map((str) => parseFloat(str));
        // Simply divide them to get the ratio.
        return gradeAndTotal[0] / gradeAndTotal[1];
    }
    // Loads an assessment from its corresponding table row.
    static fromElement(element) {
        // A row that contains the grades has the following hierarchy
        // tr[bg-colour = eeeeee]
        //   td
        //   td.td-nombre
        //   td
        //     font<assessment name>
        //     br font<This grade will be dropped> (only added if this grade is being dropped)
        //   td
        //     font<(personal grade/total)> OR font< - > if there's no grade
        //     br
        //     font<grade%>
        //   td
        //     font<average grade/total> OR font< - > if there's no average
        //   td
        //     font<weight%>
        //     font(weighted grade/total weight)
        // To obtain the information we need, we have to extract the 3rd to the 6th element of the row.
        const nameElement = element.childNodes.item(2);
        const name = nameElement.firstElementChild.innerText;
        // If the grade is being dropped, there will be more elements in the name element that indicate that the
        // grade has been dropped.
        const counted = !(nameElement.childElementCount > 1);
        const gradeElement = element.childNodes.item(3);
        // If there is a grade, there will be more than one element specifying the grade marking and the percent grade.
        const grade = gradeElement.childNodes.length > 1 ?
            Assessment.extractGrade(gradeElement.firstElementChild.innerText) : undefined;
        // If there is an average, there will be more than one element specifying them.
        const averageElement = element.childNodes.item(4);
        const average = averageElement.childNodes.length > 1 ?
            Assessment.extractGrade(averageElement.firstElementChild.innerText) : undefined;
        const weightElement = element.childNodes.item(5);
        // The weight is displayed first in percentage "Weight%" alongside with the weighted vs. actual grade.
        // Extract the percentage from the first element, remove the percentage, and divide it by 100 to get the
        // actual ratio.
        const weight = parseFloat(weightElement.firstElementChild.innerText.replace('%', '')) / 100;
        return new Assessment(name, weight, counted, grade, average);
    }
    // Loads all assessments from a course assessments page.
    static loadFromCourseAssessmentsPage(page) {
        // All grades are stored in a table with the class .table-notes.
        // Using the bgcolor requirement to select only ones that can contain grade.
        return Array.from(page.querySelectorAll(`.table-notes tr[bgcolor="#EEEEEE"]`))
            // However, some of these are empty and function as padding for some reason, so a second filter needs to
            // be applied. These padding rows have two children that fill out the row, so checking for more than 2
            // children should do the job.
            .filter((tr) => tr.childElementCount > 2)
            .map((tr) => Assessment.fromElement(tr));
    }
    get hasGrade() {
        return this.grade != undefined;
    }
    get hasAverage() {
        return this.average != undefined;
    }
}
exports.Assessment = Assessment;


/***/ }),

/***/ 347:
/***/ ((__unused_webpack_module, exports) => {


Object.defineProperty(exports, "__esModule", ({ value: true }));
exports.injectOmniplusLogo = void 0;
// 6-petaled Omnivox Flower
const omniplusLogoSource = `<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" viewBox="0 0 34 34" style="enable-background:new 0 0 34 34;" xml:space="preserve">
<style type="text/css">
.flower{fill-rule:evenodd;clip-rule:evenodd;fill:#FC8D33;}
</style>
<path class="flower" d="M17,23.2c0.2,17.3,20.2,5.7,5.4-3.1c-0.1,0-0.2-0.1-0.3-0.2c0.1,0,0.2,0.1,0.3,0.1c15.1,8.4,15.1-14.7,0-6.2
\tl0,0l0,0c14.8-8.8-5.2-20.3-5.4-3c-0.2-17.3-20.2-5.7-5.4,3.1l0,0c-15.1-8.5-15.1,14.7,0,6.2l0,0C-3.3,29,16.8,40.5,17,23.2z
\t M11.7,17c0-0.9,0.2-1.8,0.6-2.5c0.9-1.6,2.6-2.8,4.6-2.8s3.7,1.1,4.6,2.8c0.4,0.7,0.6,1.6,0.6,2.5c0,0.5-0.1,1.1-0.2,1.6
\tc-0.7,2.2-2.7,3.7-5,3.7C14.1,22.3,11.7,19.9,11.7,17z"/>
</svg>`;
const domParser = new DOMParser();
function getOmniplusLogoElement() {
    const xmlDocument = domParser.parseFromString(omniplusLogoSource, 'image/svg+xml');
    const svg = xmlDocument.firstElementChild;
    // Set the svg to its correct size.
    // No need to touch the size because the element maintains its ratio.
    svg.style.width = '36px';
    return svg;
}
function injectOmniplusLogo() {
    const omnivoxLogoContainer = document.querySelector('#headerOmnivoxLogo');
    if (omnivoxLogoContainer) {
        const originalLogo = omnivoxLogoContainer.querySelector('img');
        // Put the new logo before the original logo.
        omnivoxLogoContainer.insertBefore(getOmniplusLogoElement(), originalLogo);
        // Remove the original logo.
        omnivoxLogoContainer.removeChild(originalLogo);
    }
}
exports.injectOmniplusLogo = injectOmniplusLogo;


/***/ }),

/***/ 50:
/***/ ((__unused_webpack_module, exports) => {


Object.defineProperty(exports, "__esModule", ({ value: true }));
exports.autoLogin = exports.removeLeaAnchorHoverCSSRule = exports.removePrinterFriendlyButton = exports.removeAllLineBreaks = exports.removeHeaderImage = void 0;
// Removes the header image on Omnivox to free up more space.
function removeHeaderImage() {
    const headerImageElement = document.querySelector('#headerImage');
    if (headerImageElement) {
        // Set its height to 0.
        headerImageElement.style.height = '0';
    }
}
exports.removeHeaderImage = removeHeaderImage;
// Removes all line break elements that extend the size of the page for no reason.
function removeAllLineBreaks() {
    Array.from(document.querySelectorAll('br'))
        .forEach((element) => element.parentElement.removeChild(element));
}
exports.removeAllLineBreaks = removeAllLineBreaks;
// Certain Lea pages have an unnecessary print version button.
function removePrinterFriendlyButton() {
    const printerFriendlyButton = document.querySelector('.td-liens');
    if (printerFriendlyButton) {
        printerFriendlyButton.style.display = 'none';
    }
}
exports.removePrinterFriendlyButton = removePrinterFriendlyButton;
// Lea's stylesheet makes all hovered <a> elements red. Remove the rule if it exists.
function removeLeaAnchorHoverCSSRule() {
    for (const styleSheet of document.styleSheets) {
        for (let i = 0; i < styleSheet.cssRules.length; i++) {
            if (styleSheet.cssRules[i].cssText.includes('a:hover:not(.btn.waves-effect)')) {
                styleSheet.deleteRule(i);
            }
        }
    }
}
exports.removeLeaAnchorHoverCSSRule = removeLeaAnchorHoverCSSRule;
function autoLogin() {
    const usernameElement = document.querySelector('#Identifiant');
    const passwordElement = document.querySelector('#Password');
    // If the elements exist and they have been filled.
    if (usernameElement && passwordElement) {
        const username = usernameElement.value;
        const password = passwordElement.value;
        if (username && password) {
            // Click the login button.
            document.querySelector('.btn.green').click();
        }
    }
}
exports.autoLogin = autoLogin;


/***/ }),

/***/ 843:
/***/ ((__unused_webpack_module, exports) => {


Object.defineProperty(exports, "__esModule", ({ value: true }));
exports.Renderable = void 0;
// Represents an objected that can be rendered on a DOM element.
// T represents the render information passed down to the object for it to adjust its rendering.
class Renderable {
    constructor(tag, ...styleClasses) {
        this.domElement = document.createElement(tag);
        styleClasses.forEach((styleClass) => this.domElement.classList.add(styleClass));
    }
    clearDomElement() {
        while (this.domElement.hasChildNodes()) {
            this.domElement.removeChild(this.domElement.firstChild);
        }
    }
    // Updates the domElement based on the given information and returns it.
    render(renderInfo) {
        this.lastRenderInfo = renderInfo;
        this.clearDomElement();
        this.updateDomElement(renderInfo);
        return this.domElement;
    }
    // Updates the domElement with the last render info.
    rerender() {
        this.render(this.lastRenderInfo);
    }
}
exports.Renderable = Renderable;


/***/ }),

/***/ 794:
/***/ ((__unused_webpack_module, exports) => {


Object.defineProperty(exports, "__esModule", ({ value: true }));
exports.getMonthFromShortened = exports.regexEscape = exports.toTitleCase = exports.fetchDocumentFrom = exports.leaRoot = void 0;
// The root URL of Lea pages.
exports.leaRoot = 'https://www-mpo-ovx.omnivox.ca/cvir/ddle/';
// Fetches and parses a components from the given url.
function fetchDocumentFrom(url) {
    return fetch(url).then((response) => response.text())
        .then((text) => new DOMParser().parseFromString(text, 'text/html'));
}
exports.fetchDocumentFrom = fetchDocumentFrom;
function toTitleCase(text) {
    return text.split(' ').map((word) => word.charAt(0).toUpperCase() + word.substring(1).toLowerCase()).join(' ');
}
exports.toTitleCase = toTitleCase;
// Escapes regular tex to regex expression.
function regexEscape(text) {
    return text.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, '\\$&');
}
exports.regexEscape = regexEscape;
const monthsShortened = [
    'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun',
    'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'
];
// Returns the month from its shortened, 3-character representation.
function getMonthFromShortened(month) {
    return monthsShortened.indexOf(month);
}
exports.getMonthFromShortened = getMonthFromShortened;


/***/ }),

/***/ 807:
/***/ ((module, __webpack_exports__, __webpack_require__) => {

/* harmony export */ __webpack_require__.d(__webpack_exports__, {
/* harmony export */   "Z": () => (__WEBPACK_DEFAULT_EXPORT__)
/* harmony export */ });
/* harmony import */ var _node_modules_css_loader_dist_runtime_sourceMaps_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(663);
/* harmony import */ var _node_modules_css_loader_dist_runtime_sourceMaps_js__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(_node_modules_css_loader_dist_runtime_sourceMaps_js__WEBPACK_IMPORTED_MODULE_0__);
/* harmony import */ var _node_modules_css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(638);
/* harmony import */ var _node_modules_css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(_node_modules_css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1__);
// Imports


var ___CSS_LOADER_EXPORT___ = _node_modules_css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1___default()((_node_modules_css_loader_dist_runtime_sourceMaps_js__WEBPACK_IMPORTED_MODULE_0___default()));
// Module
___CSS_LOADER_EXPORT___.push([module.id, ".omniplus-forum-subject {\r\n    /* Vertical flexbox */\r\n    display: flex;\r\n    flex-direction: column;\r\n    justify-content: flex-start;\r\n    align-items: center;\r\n\r\n    /* Space between the posts */\r\n    gap: 20px;\r\n\r\n    overflow-y: scroll;\r\n\r\n    user-select: none;\r\n}\r\n\r\n\r\n.omniplus-forum-subject .message {\r\n    /* Prevent the messages from being squished when the page overflows. */\r\n    flex-shrink: 0;\r\n    height: auto;\r\n    /* A few sources suggest that the width of each line should not exceed 75 characters.\r\n    With the padding this limit will be a bit shorter. */\r\n    font-size: 16px;\r\n    line-height: 20px;\r\n    max-width: 75ex;\r\n}\r\n\r\n.omniplus-forum-subject .message .card .header {\r\n    /* Horizontal flexbox */\r\n    display: flex;\r\n    flex-direction: row;\r\n    justify-content: flex-start;\r\n}\r\n\r\n.omniplus-forum-subject .message .card .header .filler {\r\n    /* Make the filler fill the space and push the time to the right. */\r\n    flex-grow: 1;\r\n}\r\n.omniplus-forum-subject .message .card .header .author {\r\n    /* Prevent from getting squished. */\r\n    flex-shrink: 0;\r\n}\r\n.omniplus-forum-subject .message .card .header .time {\r\n    /* Prevent from getting squished. */\r\n    flex-shrink: 0;\r\n    color: var(--grey-700);\r\n\r\n    /* Put the fonts on a smaller size */\r\n    font-size: 14px;\r\n}\r\n\r\n.omniplus-forum-subject .message .card .shortened {\r\n    max-height: 200px;\r\n    overflow: hidden;\r\n}\r\n\r\n.omniplus-forum-subject .message .card blockquote {\r\n    /* Block quotes should have a light background and a left border to show that they're block quotes. */\r\n    background-color: rgba(0, 0, 0, 0.03); /* Using translucent background so nested quotes still stand out. */\r\n    border-left: rgba(0, 0, 0, 0.1) 4px solid;\r\n\r\n    /* Space for text handled by the padding instead as the border already exists. */\r\n    margin: 0;\r\n    padding: 8px;\r\n}\r\n\r\n/* Give the expand button the secondary colour so it doesn't look too similar. */\r\n.omniplus-forum-subject .badged-card .badge-holder .expand {\r\n    --badge-clickable-background: var(--secondary);\r\n    --badge-clickable-background-hovered: var(--secondary-dark);\r\n    --badge-clickable-background-focused: var(--secondary-darker);\r\n}\r\n\r\n.omniplus-forum-subject .controls {\r\n    position: absolute;\r\n    z-index: 99;\r\n    /* Align this to the right */\r\n    align-self: end;\r\n\r\n    /* Vertical flexbox */\r\n    display: flex;\r\n    flex-direction: column;\r\n    justify-content: flex-start;\r\n    align-items: center;\r\n\r\n    gap: 12px;\r\n\r\n    --button-size: 48px;\r\n}\r\n\r\n.omniplus-forum-subject .controls .button {\r\n    height: var(--button-size);\r\n    width: var(--button-size);\r\n\r\n    border-radius: 4px;\r\n    /* Material 2dp shadow */\r\n    box-shadow: 0 2px 2px 0 rgba(0,0,0,.14),0 3px 1px -2px rgba(0,0,0,.2),0 1px 5px 0 rgba(0,0,0,.12);\r\n\r\n    /* Centre-align horizontally */\r\n    text-align: center;\r\n    /* Using flexbox to centre-align vertically */\r\n    display: flex;\r\n    flex-direction: column;\r\n    justify-content: center;\r\n\r\n    /* icon size and colour */\r\n    color: white;\r\n    font-size: calc(var(--button-size) * 0.67);\r\n\r\n    text-decoration: none !important;\r\n\r\n    /* Pointer indicating that the file can be downloaded. */\r\n    cursor: pointer;\r\n    background-color: var(--button-clickable-background);\r\n\r\n    transition-duration: 0.2s;\r\n}\r\n.omniplus-forum-subject .controls .button:hover {\r\n    background-color: var(--button-clickable-background-hovered);\r\n}\r\n.omniplus-forum-subject .controls .button:focus {\r\n    background-color: var(--button-clickable-background-focused);\r\n}\r\n\r\n.omniplus-forum-subject .controls .primary {\r\n    --button-clickable-background: var(--primary);\r\n    --button-clickable-background-hovered: var(--primary-dark);\r\n    --button-clickable-background-focused: var(--primary-darker);\r\n}\r\n.omniplus-forum-subject .controls .secondary {\r\n    --button-clickable-background: var(--secondary);\r\n    --button-clickable-background-hovered: var(--secondary-dark);\r\n    --button-clickable-background-focused: var(--secondary-darker);\r\n}", "",{"version":3,"sources":["webpack://./scripts/forum.css"],"names":[],"mappings":"AAAA;IACI,qBAAqB;IACrB,aAAa;IACb,sBAAsB;IACtB,2BAA2B;IAC3B,mBAAmB;;IAEnB,4BAA4B;IAC5B,SAAS;;IAET,kBAAkB;;IAElB,iBAAiB;AACrB;;;AAGA;IACI,sEAAsE;IACtE,cAAc;IACd,YAAY;IACZ;wDACoD;IACpD,eAAe;IACf,iBAAiB;IACjB,eAAe;AACnB;;AAEA;IACI,uBAAuB;IACvB,aAAa;IACb,mBAAmB;IACnB,2BAA2B;AAC/B;;AAEA;IACI,mEAAmE;IACnE,YAAY;AAChB;AACA;IACI,mCAAmC;IACnC,cAAc;AAClB;AACA;IACI,mCAAmC;IACnC,cAAc;IACd,sBAAsB;;IAEtB,oCAAoC;IACpC,eAAe;AACnB;;AAEA;IACI,iBAAiB;IACjB,gBAAgB;AACpB;;AAEA;IACI,qGAAqG;IACrG,qCAAqC,EAAE,mEAAmE;IAC1G,yCAAyC;;IAEzC,gFAAgF;IAChF,SAAS;IACT,YAAY;AAChB;;AAEA,gFAAgF;AAChF;IACI,8CAA8C;IAC9C,2DAA2D;IAC3D,6DAA6D;AACjE;;AAEA;IACI,kBAAkB;IAClB,WAAW;IACX,4BAA4B;IAC5B,eAAe;;IAEf,qBAAqB;IACrB,aAAa;IACb,sBAAsB;IACtB,2BAA2B;IAC3B,mBAAmB;;IAEnB,SAAS;;IAET,mBAAmB;AACvB;;AAEA;IACI,0BAA0B;IAC1B,yBAAyB;;IAEzB,kBAAkB;IAClB,wBAAwB;IACxB,iGAAiG;;IAEjG,8BAA8B;IAC9B,kBAAkB;IAClB,6CAA6C;IAC7C,aAAa;IACb,sBAAsB;IACtB,uBAAuB;;IAEvB,yBAAyB;IACzB,YAAY;IACZ,0CAA0C;;IAE1C,gCAAgC;;IAEhC,wDAAwD;IACxD,eAAe;IACf,oDAAoD;;IAEpD,yBAAyB;AAC7B;AACA;IACI,4DAA4D;AAChE;AACA;IACI,4DAA4D;AAChE;;AAEA;IACI,6CAA6C;IAC7C,0DAA0D;IAC1D,4DAA4D;AAChE;AACA;IACI,+CAA+C;IAC/C,4DAA4D;IAC5D,8DAA8D;AAClE","sourcesContent":[".omniplus-forum-subject {\r\n    /* Vertical flexbox */\r\n    display: flex;\r\n    flex-direction: column;\r\n    justify-content: flex-start;\r\n    align-items: center;\r\n\r\n    /* Space between the posts */\r\n    gap: 20px;\r\n\r\n    overflow-y: scroll;\r\n\r\n    user-select: none;\r\n}\r\n\r\n\r\n.omniplus-forum-subject .message {\r\n    /* Prevent the messages from being squished when the page overflows. */\r\n    flex-shrink: 0;\r\n    height: auto;\r\n    /* A few sources suggest that the width of each line should not exceed 75 characters.\r\n    With the padding this limit will be a bit shorter. */\r\n    font-size: 16px;\r\n    line-height: 20px;\r\n    max-width: 75ex;\r\n}\r\n\r\n.omniplus-forum-subject .message .card .header {\r\n    /* Horizontal flexbox */\r\n    display: flex;\r\n    flex-direction: row;\r\n    justify-content: flex-start;\r\n}\r\n\r\n.omniplus-forum-subject .message .card .header .filler {\r\n    /* Make the filler fill the space and push the time to the right. */\r\n    flex-grow: 1;\r\n}\r\n.omniplus-forum-subject .message .card .header .author {\r\n    /* Prevent from getting squished. */\r\n    flex-shrink: 0;\r\n}\r\n.omniplus-forum-subject .message .card .header .time {\r\n    /* Prevent from getting squished. */\r\n    flex-shrink: 0;\r\n    color: var(--grey-700);\r\n\r\n    /* Put the fonts on a smaller size */\r\n    font-size: 14px;\r\n}\r\n\r\n.omniplus-forum-subject .message .card .shortened {\r\n    max-height: 200px;\r\n    overflow: hidden;\r\n}\r\n\r\n.omniplus-forum-subject .message .card blockquote {\r\n    /* Block quotes should have a light background and a left border to show that they're block quotes. */\r\n    background-color: rgba(0, 0, 0, 0.03); /* Using translucent background so nested quotes still stand out. */\r\n    border-left: rgba(0, 0, 0, 0.1) 4px solid;\r\n\r\n    /* Space for text handled by the padding instead as the border already exists. */\r\n    margin: 0;\r\n    padding: 8px;\r\n}\r\n\r\n/* Give the expand button the secondary colour so it doesn't look too similar. */\r\n.omniplus-forum-subject .badged-card .badge-holder .expand {\r\n    --badge-clickable-background: var(--secondary);\r\n    --badge-clickable-background-hovered: var(--secondary-dark);\r\n    --badge-clickable-background-focused: var(--secondary-darker);\r\n}\r\n\r\n.omniplus-forum-subject .controls {\r\n    position: absolute;\r\n    z-index: 99;\r\n    /* Align this to the right */\r\n    align-self: end;\r\n\r\n    /* Vertical flexbox */\r\n    display: flex;\r\n    flex-direction: column;\r\n    justify-content: flex-start;\r\n    align-items: center;\r\n\r\n    gap: 12px;\r\n\r\n    --button-size: 48px;\r\n}\r\n\r\n.omniplus-forum-subject .controls .button {\r\n    height: var(--button-size);\r\n    width: var(--button-size);\r\n\r\n    border-radius: 4px;\r\n    /* Material 2dp shadow */\r\n    box-shadow: 0 2px 2px 0 rgba(0,0,0,.14),0 3px 1px -2px rgba(0,0,0,.2),0 1px 5px 0 rgba(0,0,0,.12);\r\n\r\n    /* Centre-align horizontally */\r\n    text-align: center;\r\n    /* Using flexbox to centre-align vertically */\r\n    display: flex;\r\n    flex-direction: column;\r\n    justify-content: center;\r\n\r\n    /* icon size and colour */\r\n    color: white;\r\n    font-size: calc(var(--button-size) * 0.67);\r\n\r\n    text-decoration: none !important;\r\n\r\n    /* Pointer indicating that the file can be downloaded. */\r\n    cursor: pointer;\r\n    background-color: var(--button-clickable-background);\r\n\r\n    transition-duration: 0.2s;\r\n}\r\n.omniplus-forum-subject .controls .button:hover {\r\n    background-color: var(--button-clickable-background-hovered);\r\n}\r\n.omniplus-forum-subject .controls .button:focus {\r\n    background-color: var(--button-clickable-background-focused);\r\n}\r\n\r\n.omniplus-forum-subject .controls .primary {\r\n    --button-clickable-background: var(--primary);\r\n    --button-clickable-background-hovered: var(--primary-dark);\r\n    --button-clickable-background-focused: var(--primary-darker);\r\n}\r\n.omniplus-forum-subject .controls .secondary {\r\n    --button-clickable-background: var(--secondary);\r\n    --button-clickable-background-hovered: var(--secondary-dark);\r\n    --button-clickable-background-focused: var(--secondary-darker);\r\n}"],"sourceRoot":""}]);
// Exports
/* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = (___CSS_LOADER_EXPORT___);


/***/ }),

/***/ 535:
/***/ ((module, __webpack_exports__, __webpack_require__) => {

/* harmony export */ __webpack_require__.d(__webpack_exports__, {
/* harmony export */   "Z": () => (__WEBPACK_DEFAULT_EXPORT__)
/* harmony export */ });
/* harmony import */ var _node_modules_css_loader_dist_runtime_sourceMaps_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(663);
/* harmony import */ var _node_modules_css_loader_dist_runtime_sourceMaps_js__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(_node_modules_css_loader_dist_runtime_sourceMaps_js__WEBPACK_IMPORTED_MODULE_0__);
/* harmony import */ var _node_modules_css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(638);
/* harmony import */ var _node_modules_css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(_node_modules_css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1__);
/* harmony import */ var _node_modules_css_loader_dist_cjs_js_overview_css__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(397);
/* harmony import */ var _node_modules_css_loader_dist_cjs_js_forum_css__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(807);
// Imports




var ___CSS_LOADER_EXPORT___ = _node_modules_css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1___default()((_node_modules_css_loader_dist_runtime_sourceMaps_js__WEBPACK_IMPORTED_MODULE_0___default()));
___CSS_LOADER_EXPORT___.push([module.id, "@import url(https://fonts.googleapis.com/css2?family=Roboto+Mono:wght@300;400;500&family=Roboto+Slab:wght@300;400;500&family=Roboto:ital,wght@0,300;0,400;0,500;1,300;1,400;1,500&display=swap);"]);
___CSS_LOADER_EXPORT___.push([module.id, "@import url(https://fonts.googleapis.com/icon?family=Material+Icons);"]);
___CSS_LOADER_EXPORT___.i(_node_modules_css_loader_dist_cjs_js_overview_css__WEBPACK_IMPORTED_MODULE_2__/* ["default"] */ .Z);
___CSS_LOADER_EXPORT___.i(_node_modules_css_loader_dist_cjs_js_forum_css__WEBPACK_IMPORTED_MODULE_3__/* ["default"] */ .Z);
// Module
___CSS_LOADER_EXPORT___.push([module.id, "/* Roboto, Roboto Slab, and Roboto Mono */\r\n\r\n/* Define the colours here. */\r\n/* Source: https://material.io/resources/color/ */\r\nbody {\r\n    /* Lea Green */\r\n    --primary: #8bc34a;\r\n    --primary-light: #bef67a;\r\n    --primary-dark: #5a9216;\r\n    --primary-darker: #255d00;\r\n\r\n    /* MIO Blue */\r\n    --secondary: #0288d1;\r\n    --secondary-light: #5eb8ff;\r\n    --secondary-dark: #005b9f;\r\n    --secondary-darker: #003270;\r\n\r\n    /* Dark grey, used to indicate the file document type. */\r\n    --dark-grey: #1b1b1b;\r\n    /* Dark blue, used to indicate the link document type. */\r\n    --dark-blue: #000045;\r\n    /* Dark red, used to indicate the video document type. */\r\n    --dark-red: #300000;\r\n\r\n    /* Greyscales */\r\n    --grey-100: #f5f5f5;\r\n    --grey-200: #eeeeee;\r\n    --grey-300: #e0e0e0;\r\n    --grey-400: #bdbdbd;\r\n    --grey-500: #9e9e9e;\r\n    --grey-600: #757575;\r\n    --grey-700: #616161;\r\n    --grey-800: #424242;\r\n    --grey-900: #212121;\r\n}\r\n\r\n/* Container for all Lea content injections. */\r\n.omniplus-lea-container {\r\n    padding: 20px;\r\n\r\n    /* Since the body scroll is disabled, ensure that the container is in the screen so its horizontal scroll bar shows up. */\r\n    height: calc(100vh - 120px);\r\n    /* Leave space for the left lea toolbar */\r\n    max-width: calc(100vw - 400px);\r\n\r\n    /* Roboto */\r\n    font-family: \"Roboto\", \"Helvetica\", \"Arial\", sans-serif;\r\n}\r\n\r\n.omniplus-lea-container .badged-card {\r\n    /* Horizontal Flexbox */\r\n    display: flex;\r\n    /* Reverse the order to put the icons on the right and the card itself on the left.\r\n     This is done instead of reversing the order of the elements so the card is always\r\n     above the badges without having to specify indixes. */\r\n    flex-direction: row-reverse;\r\n    justify-content: flex-start;\r\n    align-items: stretch;\r\n\r\n    /* Prevent long words from overflowing the container. */\r\n    overflow-wrap: anywhere;\r\n\r\n    --badge-size: 36px;\r\n    --badge-background: var(--dark-grey);\r\n    --badge-clickable-background: var(--primary);\r\n    --badge-clickable-background-hovered: var(--primary-dark);\r\n    --badge-clickable-background-focused: var(--primary-darker);\r\n}\r\n\r\n.omniplus-lea-container .badged-card .badge-holder {\r\n    /* Prevent the badges from being squished by the description. */\r\n    flex-shrink: 0;\r\n\r\n    width: var(--badge-size);\r\n    /* Give the badges a bit of space vertically. */\r\n    /* Also to match the padding of the card's content. */\r\n    margin-top: 12px;\r\n    margin-bottom: 12px;\r\n\r\n    /* Vertical Flexbox */\r\n    display: flex;\r\n    flex-direction: column;\r\n    justify-content: flex-start;\r\n    align-items: stretch;\r\n\r\n    /* Give the badges a bit of space from each other. */\r\n    gap: 8px;\r\n}\r\n\r\n.omniplus-lea-container .badged-card .badge-holder .badge {\r\n    height: var(--badge-size);\r\n\r\n    /* Rounded corners on top and bottom left. */\r\n    /* border-radius: 2px 0 0 2px; */\r\n    border-radius: 0 2px 2px 0;\r\n\r\n    /* Centre-align horizontally */\r\n    text-align: center;\r\n    /* Using flexbox to centre-align vertically */\r\n    display: flex;\r\n    flex-direction: column;\r\n    justify-content: center;\r\n\r\n    /* icon size and colour */\r\n    color: white;\r\n    font-size: calc(var(--badge-size) * 0.67);\r\n\r\n    text-decoration: none !important;\r\n}\r\n\r\n\r\n.omniplus-lea-container .badged-card .badge-holder .clickable {\r\n    /* Pointer indicating that the file can be downloaded. */\r\n    cursor: pointer;\r\n    background-color: var(--badge-clickable-background);\r\n\r\n    transition-duration: 0.2s;\r\n}\r\n.omniplus-lea-container .badged-card .badge-holder .clickable:hover {\r\n    background-color: var(--badge-clickable-background-hovered);\r\n}\r\n.omniplus-lea-container .badged-card .badge-holder .clickable:focus {\r\n    background-color: var(--badge-clickable-background-focused);\r\n}\r\n\r\n.omniplus-lea-container .badged-card .card {\r\n    /* The card fills the horizontal content. */\r\n    flex-grow: 1;\r\n    border-radius: 2px;\r\n    /* Material 2dp shadow */\r\n    box-shadow: 0 2px 2px 0 rgba(0,0,0,.14),0 3px 1px -2px rgba(0,0,0,.2),0 1px 5px 0 rgba(0,0,0,.12);\r\n\r\n    padding: 12px;\r\n\r\n    /* Vertical Flexbox */\r\n    display: flex;\r\n    flex-direction: column;\r\n    justify-content: flex-start;\r\n    align-items: stretch;\r\n\r\n    /* Added space between title and descriptions. */\r\n    gap: 8px;\r\n}", "",{"version":3,"sources":["webpack://./scripts/omniplus.css"],"names":[],"mappings":"AAAA,yCAAyC;;AAOzC,6BAA6B;AAC7B,iDAAiD;AACjD;IACI,cAAc;IACd,kBAAkB;IAClB,wBAAwB;IACxB,uBAAuB;IACvB,yBAAyB;;IAEzB,aAAa;IACb,oBAAoB;IACpB,0BAA0B;IAC1B,yBAAyB;IACzB,2BAA2B;;IAE3B,wDAAwD;IACxD,oBAAoB;IACpB,wDAAwD;IACxD,oBAAoB;IACpB,wDAAwD;IACxD,mBAAmB;;IAEnB,eAAe;IACf,mBAAmB;IACnB,mBAAmB;IACnB,mBAAmB;IACnB,mBAAmB;IACnB,mBAAmB;IACnB,mBAAmB;IACnB,mBAAmB;IACnB,mBAAmB;IACnB,mBAAmB;AACvB;;AAEA,8CAA8C;AAC9C;IACI,aAAa;;IAEb,yHAAyH;IACzH,2BAA2B;IAC3B,yCAAyC;IACzC,8BAA8B;;IAE9B,WAAW;IACX,uDAAuD;AAC3D;;AAEA;IACI,uBAAuB;IACvB,aAAa;IACb;;0DAEsD;IACtD,2BAA2B;IAC3B,2BAA2B;IAC3B,oBAAoB;;IAEpB,uDAAuD;IACvD,uBAAuB;;IAEvB,kBAAkB;IAClB,oCAAoC;IACpC,4CAA4C;IAC5C,yDAAyD;IACzD,2DAA2D;AAC/D;;AAEA;IACI,+DAA+D;IAC/D,cAAc;;IAEd,wBAAwB;IACxB,+CAA+C;IAC/C,qDAAqD;IACrD,gBAAgB;IAChB,mBAAmB;;IAEnB,qBAAqB;IACrB,aAAa;IACb,sBAAsB;IACtB,2BAA2B;IAC3B,oBAAoB;;IAEpB,oDAAoD;IACpD,QAAQ;AACZ;;AAEA;IACI,yBAAyB;;IAEzB,4CAA4C;IAC5C,gCAAgC;IAChC,0BAA0B;;IAE1B,8BAA8B;IAC9B,kBAAkB;IAClB,6CAA6C;IAC7C,aAAa;IACb,sBAAsB;IACtB,uBAAuB;;IAEvB,yBAAyB;IACzB,YAAY;IACZ,yCAAyC;;IAEzC,gCAAgC;AACpC;;;AAGA;IACI,wDAAwD;IACxD,eAAe;IACf,mDAAmD;;IAEnD,yBAAyB;AAC7B;AACA;IACI,2DAA2D;AAC/D;AACA;IACI,2DAA2D;AAC/D;;AAEA;IACI,2CAA2C;IAC3C,YAAY;IACZ,kBAAkB;IAClB,wBAAwB;IACxB,iGAAiG;;IAEjG,aAAa;;IAEb,qBAAqB;IACrB,aAAa;IACb,sBAAsB;IACtB,2BAA2B;IAC3B,oBAAoB;;IAEpB,gDAAgD;IAChD,QAAQ;AACZ","sourcesContent":["/* Roboto, Roboto Slab, and Roboto Mono */\r\n@import url('https://fonts.googleapis.com/css2?family=Roboto+Mono:wght@300;400;500&family=Roboto+Slab:wght@300;400;500&family=Roboto:ital,wght@0,300;0,400;0,500;1,300;1,400;1,500&display=swap');\r\n@import url('https://fonts.googleapis.com/icon?family=Material+Icons');\r\n\r\n@import \"overview.css\";\r\n@import \"forum.css\";\r\n\r\n/* Define the colours here. */\r\n/* Source: https://material.io/resources/color/ */\r\nbody {\r\n    /* Lea Green */\r\n    --primary: #8bc34a;\r\n    --primary-light: #bef67a;\r\n    --primary-dark: #5a9216;\r\n    --primary-darker: #255d00;\r\n\r\n    /* MIO Blue */\r\n    --secondary: #0288d1;\r\n    --secondary-light: #5eb8ff;\r\n    --secondary-dark: #005b9f;\r\n    --secondary-darker: #003270;\r\n\r\n    /* Dark grey, used to indicate the file document type. */\r\n    --dark-grey: #1b1b1b;\r\n    /* Dark blue, used to indicate the link document type. */\r\n    --dark-blue: #000045;\r\n    /* Dark red, used to indicate the video document type. */\r\n    --dark-red: #300000;\r\n\r\n    /* Greyscales */\r\n    --grey-100: #f5f5f5;\r\n    --grey-200: #eeeeee;\r\n    --grey-300: #e0e0e0;\r\n    --grey-400: #bdbdbd;\r\n    --grey-500: #9e9e9e;\r\n    --grey-600: #757575;\r\n    --grey-700: #616161;\r\n    --grey-800: #424242;\r\n    --grey-900: #212121;\r\n}\r\n\r\n/* Container for all Lea content injections. */\r\n.omniplus-lea-container {\r\n    padding: 20px;\r\n\r\n    /* Since the body scroll is disabled, ensure that the container is in the screen so its horizontal scroll bar shows up. */\r\n    height: calc(100vh - 120px);\r\n    /* Leave space for the left lea toolbar */\r\n    max-width: calc(100vw - 400px);\r\n\r\n    /* Roboto */\r\n    font-family: \"Roboto\", \"Helvetica\", \"Arial\", sans-serif;\r\n}\r\n\r\n.omniplus-lea-container .badged-card {\r\n    /* Horizontal Flexbox */\r\n    display: flex;\r\n    /* Reverse the order to put the icons on the right and the card itself on the left.\r\n     This is done instead of reversing the order of the elements so the card is always\r\n     above the badges without having to specify indixes. */\r\n    flex-direction: row-reverse;\r\n    justify-content: flex-start;\r\n    align-items: stretch;\r\n\r\n    /* Prevent long words from overflowing the container. */\r\n    overflow-wrap: anywhere;\r\n\r\n    --badge-size: 36px;\r\n    --badge-background: var(--dark-grey);\r\n    --badge-clickable-background: var(--primary);\r\n    --badge-clickable-background-hovered: var(--primary-dark);\r\n    --badge-clickable-background-focused: var(--primary-darker);\r\n}\r\n\r\n.omniplus-lea-container .badged-card .badge-holder {\r\n    /* Prevent the badges from being squished by the description. */\r\n    flex-shrink: 0;\r\n\r\n    width: var(--badge-size);\r\n    /* Give the badges a bit of space vertically. */\r\n    /* Also to match the padding of the card's content. */\r\n    margin-top: 12px;\r\n    margin-bottom: 12px;\r\n\r\n    /* Vertical Flexbox */\r\n    display: flex;\r\n    flex-direction: column;\r\n    justify-content: flex-start;\r\n    align-items: stretch;\r\n\r\n    /* Give the badges a bit of space from each other. */\r\n    gap: 8px;\r\n}\r\n\r\n.omniplus-lea-container .badged-card .badge-holder .badge {\r\n    height: var(--badge-size);\r\n\r\n    /* Rounded corners on top and bottom left. */\r\n    /* border-radius: 2px 0 0 2px; */\r\n    border-radius: 0 2px 2px 0;\r\n\r\n    /* Centre-align horizontally */\r\n    text-align: center;\r\n    /* Using flexbox to centre-align vertically */\r\n    display: flex;\r\n    flex-direction: column;\r\n    justify-content: center;\r\n\r\n    /* icon size and colour */\r\n    color: white;\r\n    font-size: calc(var(--badge-size) * 0.67);\r\n\r\n    text-decoration: none !important;\r\n}\r\n\r\n\r\n.omniplus-lea-container .badged-card .badge-holder .clickable {\r\n    /* Pointer indicating that the file can be downloaded. */\r\n    cursor: pointer;\r\n    background-color: var(--badge-clickable-background);\r\n\r\n    transition-duration: 0.2s;\r\n}\r\n.omniplus-lea-container .badged-card .badge-holder .clickable:hover {\r\n    background-color: var(--badge-clickable-background-hovered);\r\n}\r\n.omniplus-lea-container .badged-card .badge-holder .clickable:focus {\r\n    background-color: var(--badge-clickable-background-focused);\r\n}\r\n\r\n.omniplus-lea-container .badged-card .card {\r\n    /* The card fills the horizontal content. */\r\n    flex-grow: 1;\r\n    border-radius: 2px;\r\n    /* Material 2dp shadow */\r\n    box-shadow: 0 2px 2px 0 rgba(0,0,0,.14),0 3px 1px -2px rgba(0,0,0,.2),0 1px 5px 0 rgba(0,0,0,.12);\r\n\r\n    padding: 12px;\r\n\r\n    /* Vertical Flexbox */\r\n    display: flex;\r\n    flex-direction: column;\r\n    justify-content: flex-start;\r\n    align-items: stretch;\r\n\r\n    /* Added space between title and descriptions. */\r\n    gap: 8px;\r\n}"],"sourceRoot":""}]);
// Exports
/* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = (___CSS_LOADER_EXPORT___);


/***/ }),

/***/ 397:
/***/ ((module, __webpack_exports__, __webpack_require__) => {

/* harmony export */ __webpack_require__.d(__webpack_exports__, {
/* harmony export */   "Z": () => (__WEBPACK_DEFAULT_EXPORT__)
/* harmony export */ });
/* harmony import */ var _node_modules_css_loader_dist_runtime_sourceMaps_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(663);
/* harmony import */ var _node_modules_css_loader_dist_runtime_sourceMaps_js__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(_node_modules_css_loader_dist_runtime_sourceMaps_js__WEBPACK_IMPORTED_MODULE_0__);
/* harmony import */ var _node_modules_css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(638);
/* harmony import */ var _node_modules_css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(_node_modules_css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1__);
// Imports


var ___CSS_LOADER_EXPORT___ = _node_modules_css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1___default()((_node_modules_css_loader_dist_runtime_sourceMaps_js__WEBPACK_IMPORTED_MODULE_0___default()));
// Module
___CSS_LOADER_EXPORT___.push([module.id, ".omniplus-documents-overview {\r\n    display: flex;\r\n    flex-direction: column;\r\n    justify-content: flex-start;\r\n    align-content: stretch;\r\n\r\n    gap: 20px;\r\n\r\n    user-select: none;\r\n}\r\n\r\n.omniplus-documents-overview .control-bar {\r\n    /* Prevent the course list from squishing the control bar. */\r\n    flex-shrink: 0;\r\n\r\n    padding: 4px;\r\n\r\n    display: flex;\r\n    flex-direction: row;\r\n    justify-content: flex-start;\r\n    align-items: stretch;\r\n\r\n    gap: 10px;\r\n}\r\n\r\n.omniplus-documents-overview .control-bar .search-bar {\r\n    flex-grow: 1;\r\n\r\n    font-size: 20px;\r\n    /* Give the text some space */\r\n    line-height: 28px;\r\n    /* Lots and lots of space */\r\n    padding: 4px;\r\n\r\n    border-style: none none solid none;\r\n    border-width: 3px;\r\n    border-color: var(--grey-400);\r\n    outline: none;\r\n\r\n    transition-duration: 0.2s;\r\n}\r\n\r\n.omniplus-documents-overview .control-bar .search-bar:hover {\r\n    border-color: var(--grey-600);\r\n}\r\n.omniplus-documents-overview .control-bar .search-bar:focus {\r\n    border-color: black;\r\n}\r\n\r\n.omniplus-documents-overview .control-bar .button {\r\n    flex-shrink: 0;\r\n\r\n    font-size: 20px;\r\n    /* Give the text some space */\r\n    line-height: 28px;\r\n\r\n    /* Extra horizontal padding */\r\n    padding: 4px 8px 4px 8px;\r\n\r\n    text-transform: uppercase;\r\n    font-weight: 400;\r\n    color: white;\r\n\r\n    background-color: var(--primary);\r\n    border: none;\r\n    border-radius: 2px;\r\n    /* Material 2dp shadow */\r\n    box-shadow: 0 2px 2px 0 rgba(0,0,0,.14),0 3px 1px -2px rgba(0,0,0,.2),0 1px 5px 0 rgba(0,0,0,.12);\r\n\r\n    /* Using flexbox to centre-align vertically */\r\n    display: flex;\r\n    flex-direction: column;\r\n    justify-content: center;\r\n\r\n    transition-duration: 0.2s;\r\n}\r\n\r\n.omniplus-documents-overview .control-bar .button:hover {\r\n    background-color: var(--primary-dark);\r\n}\r\n.omniplus-documents-overview .control-bar .button:focus {\r\n    background-color: var(--primary-darker);\r\n}\r\n\r\n.omniplus-documents-overview .course-list {\r\n    flex-grow: 1;\r\n\r\n    display: flex;\r\n    flex-direction: row;\r\n    justify-content: flex-start;\r\n    align-items: stretch;\r\n\r\n    gap: 20px;\r\n\r\n    overflow-x: auto;\r\n\r\n    /* Scrollbar styling for Firefox */\r\n    scrollbar-color: grey rgba(0, 0, 0, 0);\r\n}\r\n\r\n.omniplus-documents-overview .course-list-loading {\r\n    flex-grow: 1;\r\n\r\n    /* Center the animated loader */\r\n    display: flex;\r\n    justify-content: center;\r\n    align-items: center;\r\n}\r\n.omniplus-documents-overview .course-list-loading .loading-spinner {\r\n    width: 120px;\r\n    height: 120px;\r\n\r\n    border: 8px solid white;\r\n    border-top: 8px solid var(--grey-600);\r\n    border-radius: 50%;\r\n\r\n    animation: spin 1s linear infinite;\r\n}\r\n\r\n@keyframes spin {\r\n    0% {\r\n        transform: rotate(0deg);\r\n    }\r\n    100% {\r\n        transform: rotate(360deg);\r\n    }\r\n}\r\n\r\n/* Scrollbar styling for webkit browsers. */\r\n.omniplus-documents-overview .course-list::-webkit-scrollbar {\r\n    background: none;\r\n    width: 8px;\r\n}\r\n\r\n.omniplus-documents-overview .course-list::-webkit-scrollbar-track {\r\n    background: none;\r\n}\r\n\r\n.omniplus-documents-overview .course-list::-webkit-scrollbar-thumb {\r\n    background: gray;\r\n    border-radius: 4px;\r\n}\r\n\r\n.omniplus-documents-overview .course-list .course-name {\r\n    font-size: 24px;\r\n    line-height: 30px;\r\n\r\n\r\n    /* Double line height for up to 2 lines of text. */\r\n    min-height: 60px;\r\n    /* Using flexbox to bottom-align vertically */\r\n    display: flex;\r\n    flex-direction: column;\r\n    justify-content: flex-end;\r\n\r\n    font-weight: 500;\r\n    /* Give the title some extra space. */\r\n    margin: 4px;\r\n}\r\n\r\n.omniplus-documents-overview .course {\r\n    min-width: 300px;\r\n    width: 300px;\r\n\r\n    display: flex;\r\n    flex-direction: column;\r\n    justify-content: flex-start;\r\n    align-items: stretch;\r\n}\r\n\r\n.omniplus-documents-overview .documents-list {\r\n    /* Make the list itself fill the content of the course node. */\r\n    flex-grow: 1;\r\n\r\n    display: flex;\r\n    flex-direction: column;\r\n    justify-content: flex-start;\r\n    align-items: stretch;\r\n\r\n    overflow-x: auto;\r\n}\r\n\r\n/* Scrollbar styling for webkit browsers. */\r\n.omniplus-documents-overview .documents-list::-webkit-scrollbar {\r\n    background: none;\r\n    width: 8px;\r\n}\r\n\r\n.omniplus-documents-overview .documents-list::-webkit-scrollbar-track {\r\n    background: none;\r\n}\r\n\r\n.omniplus-documents-overview .documents-list::-webkit-scrollbar-thumb {\r\n    background: gray;\r\n    border-radius: 4px;\r\n}\r\n\r\n.omniplus-documents-overview .bold {\r\n    font-weight: 500 !important;\r\n}\r\n\r\n.omniplus-documents-overview mark {\r\n    background-color: var(--primary-light);\r\n    border-radius: 3px;\r\n}\r\n\r\n.omniplus-documents-overview .document {\r\n    margin: 4px;\r\n    /* Auto-size */\r\n    width: auto;\r\n    height: auto;\r\n}\r\n\r\n.omniplus-documents-overview .document .badge-holder .file-background {\r\n    background-color: var(--dark-grey);\r\n}\r\n.omniplus-documents-overview .document .badge-holder .link-background {\r\n    background-color: var(--dark-blue);\r\n}\r\n.omniplus-documents-overview .document .badge-holder .video-background {\r\n    background-color: var(--dark-red);\r\n}\r\n\r\n.omniplus-documents-overview .document .card .name {\r\n    font-size: 20px;\r\n\r\n    font-weight: 400;\r\n}\r\n\r\n.omniplus-documents-overview .document .card .date {\r\n    font-size: 14px;\r\n    color: rgba(0, 0, 0, 0.4);\r\n\r\n    font-weight: 400;\r\n}\r\n\r\n.omniplus-documents-overview .badged-card .card .description {\r\n    font-size: 16px;\r\n    color: rgba(0, 0, 0, 0.6);\r\n\r\n    font-weight: 400;\r\n}\r\n\r\n/* Icon displayed on the left section as a quick link to the documents overview page. */\r\n.documents-overview-icon-parent .material-icons {\r\n    /* Copied from the colour of the lea icon. */\r\n    color: #7fd91b;\r\n    font-size: 30px;\r\n}\r\n/* Icon displayed on the left section as a quick link to the documents overview page. */\r\n.documents-overview-icon-parent:hover .material-icons {\r\n    color: white;\r\n}", "",{"version":3,"sources":["webpack://./scripts/overview.css"],"names":[],"mappings":"AAAA;IACI,aAAa;IACb,sBAAsB;IACtB,2BAA2B;IAC3B,sBAAsB;;IAEtB,SAAS;;IAET,iBAAiB;AACrB;;AAEA;IACI,4DAA4D;IAC5D,cAAc;;IAEd,YAAY;;IAEZ,aAAa;IACb,mBAAmB;IACnB,2BAA2B;IAC3B,oBAAoB;;IAEpB,SAAS;AACb;;AAEA;IACI,YAAY;;IAEZ,eAAe;IACf,6BAA6B;IAC7B,iBAAiB;IACjB,2BAA2B;IAC3B,YAAY;;IAEZ,kCAAkC;IAClC,iBAAiB;IACjB,6BAA6B;IAC7B,aAAa;;IAEb,yBAAyB;AAC7B;;AAEA;IACI,6BAA6B;AACjC;AACA;IACI,mBAAmB;AACvB;;AAEA;IACI,cAAc;;IAEd,eAAe;IACf,6BAA6B;IAC7B,iBAAiB;;IAEjB,6BAA6B;IAC7B,wBAAwB;;IAExB,yBAAyB;IACzB,gBAAgB;IAChB,YAAY;;IAEZ,gCAAgC;IAChC,YAAY;IACZ,kBAAkB;IAClB,wBAAwB;IACxB,iGAAiG;;IAEjG,6CAA6C;IAC7C,aAAa;IACb,sBAAsB;IACtB,uBAAuB;;IAEvB,yBAAyB;AAC7B;;AAEA;IACI,qCAAqC;AACzC;AACA;IACI,uCAAuC;AAC3C;;AAEA;IACI,YAAY;;IAEZ,aAAa;IACb,mBAAmB;IACnB,2BAA2B;IAC3B,oBAAoB;;IAEpB,SAAS;;IAET,gBAAgB;;IAEhB,kCAAkC;IAClC,sCAAsC;AAC1C;;AAEA;IACI,YAAY;;IAEZ,+BAA+B;IAC/B,aAAa;IACb,uBAAuB;IACvB,mBAAmB;AACvB;AACA;IACI,YAAY;IACZ,aAAa;;IAEb,uBAAuB;IACvB,qCAAqC;IACrC,kBAAkB;;IAElB,kCAAkC;AACtC;;AAEA;IACI;QACI,uBAAuB;IAC3B;IACA;QACI,yBAAyB;IAC7B;AACJ;;AAEA,2CAA2C;AAC3C;IACI,gBAAgB;IAChB,UAAU;AACd;;AAEA;IACI,gBAAgB;AACpB;;AAEA;IACI,gBAAgB;IAChB,kBAAkB;AACtB;;AAEA;IACI,eAAe;IACf,iBAAiB;;;IAGjB,kDAAkD;IAClD,gBAAgB;IAChB,6CAA6C;IAC7C,aAAa;IACb,sBAAsB;IACtB,yBAAyB;;IAEzB,gBAAgB;IAChB,qCAAqC;IACrC,WAAW;AACf;;AAEA;IACI,gBAAgB;IAChB,YAAY;;IAEZ,aAAa;IACb,sBAAsB;IACtB,2BAA2B;IAC3B,oBAAoB;AACxB;;AAEA;IACI,8DAA8D;IAC9D,YAAY;;IAEZ,aAAa;IACb,sBAAsB;IACtB,2BAA2B;IAC3B,oBAAoB;;IAEpB,gBAAgB;AACpB;;AAEA,2CAA2C;AAC3C;IACI,gBAAgB;IAChB,UAAU;AACd;;AAEA;IACI,gBAAgB;AACpB;;AAEA;IACI,gBAAgB;IAChB,kBAAkB;AACtB;;AAEA;IACI,2BAA2B;AAC/B;;AAEA;IACI,sCAAsC;IACtC,kBAAkB;AACtB;;AAEA;IACI,WAAW;IACX,cAAc;IACd,WAAW;IACX,YAAY;AAChB;;AAEA;IACI,kCAAkC;AACtC;AACA;IACI,kCAAkC;AACtC;AACA;IACI,iCAAiC;AACrC;;AAEA;IACI,eAAe;;IAEf,gBAAgB;AACpB;;AAEA;IACI,eAAe;IACf,yBAAyB;;IAEzB,gBAAgB;AACpB;;AAEA;IACI,eAAe;IACf,yBAAyB;;IAEzB,gBAAgB;AACpB;;AAEA,uFAAuF;AACvF;IACI,4CAA4C;IAC5C,cAAc;IACd,eAAe;AACnB;AACA,uFAAuF;AACvF;IACI,YAAY;AAChB","sourcesContent":[".omniplus-documents-overview {\r\n    display: flex;\r\n    flex-direction: column;\r\n    justify-content: flex-start;\r\n    align-content: stretch;\r\n\r\n    gap: 20px;\r\n\r\n    user-select: none;\r\n}\r\n\r\n.omniplus-documents-overview .control-bar {\r\n    /* Prevent the course list from squishing the control bar. */\r\n    flex-shrink: 0;\r\n\r\n    padding: 4px;\r\n\r\n    display: flex;\r\n    flex-direction: row;\r\n    justify-content: flex-start;\r\n    align-items: stretch;\r\n\r\n    gap: 10px;\r\n}\r\n\r\n.omniplus-documents-overview .control-bar .search-bar {\r\n    flex-grow: 1;\r\n\r\n    font-size: 20px;\r\n    /* Give the text some space */\r\n    line-height: 28px;\r\n    /* Lots and lots of space */\r\n    padding: 4px;\r\n\r\n    border-style: none none solid none;\r\n    border-width: 3px;\r\n    border-color: var(--grey-400);\r\n    outline: none;\r\n\r\n    transition-duration: 0.2s;\r\n}\r\n\r\n.omniplus-documents-overview .control-bar .search-bar:hover {\r\n    border-color: var(--grey-600);\r\n}\r\n.omniplus-documents-overview .control-bar .search-bar:focus {\r\n    border-color: black;\r\n}\r\n\r\n.omniplus-documents-overview .control-bar .button {\r\n    flex-shrink: 0;\r\n\r\n    font-size: 20px;\r\n    /* Give the text some space */\r\n    line-height: 28px;\r\n\r\n    /* Extra horizontal padding */\r\n    padding: 4px 8px 4px 8px;\r\n\r\n    text-transform: uppercase;\r\n    font-weight: 400;\r\n    color: white;\r\n\r\n    background-color: var(--primary);\r\n    border: none;\r\n    border-radius: 2px;\r\n    /* Material 2dp shadow */\r\n    box-shadow: 0 2px 2px 0 rgba(0,0,0,.14),0 3px 1px -2px rgba(0,0,0,.2),0 1px 5px 0 rgba(0,0,0,.12);\r\n\r\n    /* Using flexbox to centre-align vertically */\r\n    display: flex;\r\n    flex-direction: column;\r\n    justify-content: center;\r\n\r\n    transition-duration: 0.2s;\r\n}\r\n\r\n.omniplus-documents-overview .control-bar .button:hover {\r\n    background-color: var(--primary-dark);\r\n}\r\n.omniplus-documents-overview .control-bar .button:focus {\r\n    background-color: var(--primary-darker);\r\n}\r\n\r\n.omniplus-documents-overview .course-list {\r\n    flex-grow: 1;\r\n\r\n    display: flex;\r\n    flex-direction: row;\r\n    justify-content: flex-start;\r\n    align-items: stretch;\r\n\r\n    gap: 20px;\r\n\r\n    overflow-x: auto;\r\n\r\n    /* Scrollbar styling for Firefox */\r\n    scrollbar-color: grey rgba(0, 0, 0, 0);\r\n}\r\n\r\n.omniplus-documents-overview .course-list-loading {\r\n    flex-grow: 1;\r\n\r\n    /* Center the animated loader */\r\n    display: flex;\r\n    justify-content: center;\r\n    align-items: center;\r\n}\r\n.omniplus-documents-overview .course-list-loading .loading-spinner {\r\n    width: 120px;\r\n    height: 120px;\r\n\r\n    border: 8px solid white;\r\n    border-top: 8px solid var(--grey-600);\r\n    border-radius: 50%;\r\n\r\n    animation: spin 1s linear infinite;\r\n}\r\n\r\n@keyframes spin {\r\n    0% {\r\n        transform: rotate(0deg);\r\n    }\r\n    100% {\r\n        transform: rotate(360deg);\r\n    }\r\n}\r\n\r\n/* Scrollbar styling for webkit browsers. */\r\n.omniplus-documents-overview .course-list::-webkit-scrollbar {\r\n    background: none;\r\n    width: 8px;\r\n}\r\n\r\n.omniplus-documents-overview .course-list::-webkit-scrollbar-track {\r\n    background: none;\r\n}\r\n\r\n.omniplus-documents-overview .course-list::-webkit-scrollbar-thumb {\r\n    background: gray;\r\n    border-radius: 4px;\r\n}\r\n\r\n.omniplus-documents-overview .course-list .course-name {\r\n    font-size: 24px;\r\n    line-height: 30px;\r\n\r\n\r\n    /* Double line height for up to 2 lines of text. */\r\n    min-height: 60px;\r\n    /* Using flexbox to bottom-align vertically */\r\n    display: flex;\r\n    flex-direction: column;\r\n    justify-content: flex-end;\r\n\r\n    font-weight: 500;\r\n    /* Give the title some extra space. */\r\n    margin: 4px;\r\n}\r\n\r\n.omniplus-documents-overview .course {\r\n    min-width: 300px;\r\n    width: 300px;\r\n\r\n    display: flex;\r\n    flex-direction: column;\r\n    justify-content: flex-start;\r\n    align-items: stretch;\r\n}\r\n\r\n.omniplus-documents-overview .documents-list {\r\n    /* Make the list itself fill the content of the course node. */\r\n    flex-grow: 1;\r\n\r\n    display: flex;\r\n    flex-direction: column;\r\n    justify-content: flex-start;\r\n    align-items: stretch;\r\n\r\n    overflow-x: auto;\r\n}\r\n\r\n/* Scrollbar styling for webkit browsers. */\r\n.omniplus-documents-overview .documents-list::-webkit-scrollbar {\r\n    background: none;\r\n    width: 8px;\r\n}\r\n\r\n.omniplus-documents-overview .documents-list::-webkit-scrollbar-track {\r\n    background: none;\r\n}\r\n\r\n.omniplus-documents-overview .documents-list::-webkit-scrollbar-thumb {\r\n    background: gray;\r\n    border-radius: 4px;\r\n}\r\n\r\n.omniplus-documents-overview .bold {\r\n    font-weight: 500 !important;\r\n}\r\n\r\n.omniplus-documents-overview mark {\r\n    background-color: var(--primary-light);\r\n    border-radius: 3px;\r\n}\r\n\r\n.omniplus-documents-overview .document {\r\n    margin: 4px;\r\n    /* Auto-size */\r\n    width: auto;\r\n    height: auto;\r\n}\r\n\r\n.omniplus-documents-overview .document .badge-holder .file-background {\r\n    background-color: var(--dark-grey);\r\n}\r\n.omniplus-documents-overview .document .badge-holder .link-background {\r\n    background-color: var(--dark-blue);\r\n}\r\n.omniplus-documents-overview .document .badge-holder .video-background {\r\n    background-color: var(--dark-red);\r\n}\r\n\r\n.omniplus-documents-overview .document .card .name {\r\n    font-size: 20px;\r\n\r\n    font-weight: 400;\r\n}\r\n\r\n.omniplus-documents-overview .document .card .date {\r\n    font-size: 14px;\r\n    color: rgba(0, 0, 0, 0.4);\r\n\r\n    font-weight: 400;\r\n}\r\n\r\n.omniplus-documents-overview .badged-card .card .description {\r\n    font-size: 16px;\r\n    color: rgba(0, 0, 0, 0.6);\r\n\r\n    font-weight: 400;\r\n}\r\n\r\n/* Icon displayed on the left section as a quick link to the documents overview page. */\r\n.documents-overview-icon-parent .material-icons {\r\n    /* Copied from the colour of the lea icon. */\r\n    color: #7fd91b;\r\n    font-size: 30px;\r\n}\r\n/* Icon displayed on the left section as a quick link to the documents overview page. */\r\n.documents-overview-icon-parent:hover .material-icons {\r\n    color: white;\r\n}"],"sourceRoot":""}]);
// Exports
/* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = (___CSS_LOADER_EXPORT___);


/***/ }),

/***/ 638:
/***/ ((module) => {



/*
  MIT License http://www.opensource.org/licenses/mit-license.php
  Author Tobias Koppers @sokra
*/
module.exports = function (cssWithMappingToString) {
  var list = []; // return the list of modules as css string

  list.toString = function toString() {
    return this.map(function (item) {
      var content = "";
      var needLayer = typeof item[5] !== "undefined";

      if (item[4]) {
        content += "@supports (".concat(item[4], ") {");
      }

      if (item[2]) {
        content += "@media ".concat(item[2], " {");
      }

      if (needLayer) {
        content += "@layer".concat(item[5].length > 0 ? " ".concat(item[5]) : "", " {");
      }

      content += cssWithMappingToString(item);

      if (needLayer) {
        content += "}";
      }

      if (item[2]) {
        content += "}";
      }

      if (item[4]) {
        content += "}";
      }

      return content;
    }).join("");
  }; // import a list of modules into the list


  list.i = function i(modules, media, dedupe, supports, layer) {
    if (typeof modules === "string") {
      modules = [[null, modules, undefined]];
    }

    var alreadyImportedModules = {};

    if (dedupe) {
      for (var k = 0; k < this.length; k++) {
        var id = this[k][0];

        if (id != null) {
          alreadyImportedModules[id] = true;
        }
      }
    }

    for (var _k = 0; _k < modules.length; _k++) {
      var item = [].concat(modules[_k]);

      if (dedupe && alreadyImportedModules[item[0]]) {
        continue;
      }

      if (typeof layer !== "undefined") {
        if (typeof item[5] === "undefined") {
          item[5] = layer;
        } else {
          item[1] = "@layer".concat(item[5].length > 0 ? " ".concat(item[5]) : "", " {").concat(item[1], "}");
          item[5] = layer;
        }
      }

      if (media) {
        if (!item[2]) {
          item[2] = media;
        } else {
          item[1] = "@media ".concat(item[2], " {").concat(item[1], "}");
          item[2] = media;
        }
      }

      if (supports) {
        if (!item[4]) {
          item[4] = "".concat(supports);
        } else {
          item[1] = "@supports (".concat(item[4], ") {").concat(item[1], "}");
          item[4] = supports;
        }
      }

      list.push(item);
    }
  };

  return list;
};

/***/ }),

/***/ 663:
/***/ ((module) => {



module.exports = function (item) {
  var content = item[1];
  var cssMapping = item[3];

  if (!cssMapping) {
    return content;
  }

  if (typeof btoa === "function") {
    var base64 = btoa(unescape(encodeURIComponent(JSON.stringify(cssMapping))));
    var data = "sourceMappingURL=data:application/json;charset=utf-8;base64,".concat(base64);
    var sourceMapping = "/*# ".concat(data, " */");
    var sourceURLs = cssMapping.sources.map(function (source) {
      return "/*# sourceURL=".concat(cssMapping.sourceRoot || "").concat(source, " */");
    });
    return [content].concat(sourceURLs).concat([sourceMapping]).join("\n");
  }

  return [content].join("\n");
};

/***/ }),

/***/ 450:
/***/ ((module) => {



var stylesInDOM = [];

function getIndexByIdentifier(identifier) {
  var result = -1;

  for (var i = 0; i < stylesInDOM.length; i++) {
    if (stylesInDOM[i].identifier === identifier) {
      result = i;
      break;
    }
  }

  return result;
}

function modulesToDom(list, options) {
  var idCountMap = {};
  var identifiers = [];

  for (var i = 0; i < list.length; i++) {
    var item = list[i];
    var id = options.base ? item[0] + options.base : item[0];
    var count = idCountMap[id] || 0;
    var identifier = "".concat(id, " ").concat(count);
    idCountMap[id] = count + 1;
    var indexByIdentifier = getIndexByIdentifier(identifier);
    var obj = {
      css: item[1],
      media: item[2],
      sourceMap: item[3],
      supports: item[4],
      layer: item[5]
    };

    if (indexByIdentifier !== -1) {
      stylesInDOM[indexByIdentifier].references++;
      stylesInDOM[indexByIdentifier].updater(obj);
    } else {
      var updater = addElementStyle(obj, options);
      options.byIndex = i;
      stylesInDOM.splice(i, 0, {
        identifier: identifier,
        updater: updater,
        references: 1
      });
    }

    identifiers.push(identifier);
  }

  return identifiers;
}

function addElementStyle(obj, options) {
  var api = options.domAPI(options);
  api.update(obj);

  var updater = function updater(newObj) {
    if (newObj) {
      if (newObj.css === obj.css && newObj.media === obj.media && newObj.sourceMap === obj.sourceMap && newObj.supports === obj.supports && newObj.layer === obj.layer) {
        return;
      }

      api.update(obj = newObj);
    } else {
      api.remove();
    }
  };

  return updater;
}

module.exports = function (list, options) {
  options = options || {};
  list = list || [];
  var lastIdentifiers = modulesToDom(list, options);
  return function update(newList) {
    newList = newList || [];

    for (var i = 0; i < lastIdentifiers.length; i++) {
      var identifier = lastIdentifiers[i];
      var index = getIndexByIdentifier(identifier);
      stylesInDOM[index].references--;
    }

    var newLastIdentifiers = modulesToDom(newList, options);

    for (var _i = 0; _i < lastIdentifiers.length; _i++) {
      var _identifier = lastIdentifiers[_i];

      var _index = getIndexByIdentifier(_identifier);

      if (stylesInDOM[_index].references === 0) {
        stylesInDOM[_index].updater();

        stylesInDOM.splice(_index, 1);
      }
    }

    lastIdentifiers = newLastIdentifiers;
  };
};

/***/ }),

/***/ 199:
/***/ ((module) => {



var memo = {};
/* istanbul ignore next  */

function getTarget(target) {
  if (typeof memo[target] === "undefined") {
    var styleTarget = document.querySelector(target); // Special case to return head of iframe instead of iframe itself

    if (window.HTMLIFrameElement && styleTarget instanceof window.HTMLIFrameElement) {
      try {
        // This will throw an exception if access to iframe is blocked
        // due to cross-origin restrictions
        styleTarget = styleTarget.contentDocument.head;
      } catch (e) {
        // istanbul ignore next
        styleTarget = null;
      }
    }

    memo[target] = styleTarget;
  }

  return memo[target];
}
/* istanbul ignore next  */


function insertBySelector(insert, style) {
  var target = getTarget(insert);

  if (!target) {
    throw new Error("Couldn't find a style target. This probably means that the value for the 'insert' parameter is invalid.");
  }

  target.appendChild(style);
}

module.exports = insertBySelector;

/***/ }),

/***/ 994:
/***/ ((module) => {



/* istanbul ignore next  */
function insertStyleElement(options) {
  var element = document.createElement("style");
  options.setAttributes(element, options.attributes);
  options.insert(element, options.options);
  return element;
}

module.exports = insertStyleElement;

/***/ }),

/***/ 458:
/***/ ((module, __unused_webpack_exports, __webpack_require__) => {



/* istanbul ignore next  */
function setAttributesWithoutAttributes(styleElement) {
  var nonce =  true ? __webpack_require__.nc : 0;

  if (nonce) {
    styleElement.setAttribute("nonce", nonce);
  }
}

module.exports = setAttributesWithoutAttributes;

/***/ }),

/***/ 653:
/***/ ((module) => {



/* istanbul ignore next  */
var replaceText = function replaceText() {
  var textStore = [];
  return function replace(index, replacement) {
    textStore[index] = replacement;
    return textStore.filter(Boolean).join("\n");
  };
}();
/* istanbul ignore next  */


function apply(styleElement, index, remove, obj) {
  var css;

  if (remove) {
    css = "";
  } else {
    css = "";

    if (obj.supports) {
      css += "@supports (".concat(obj.supports, ") {");
    }

    if (obj.media) {
      css += "@media ".concat(obj.media, " {");
    }

    var needLayer = typeof obj.layer !== "undefined";

    if (needLayer) {
      css += "@layer".concat(obj.layer.length > 0 ? " ".concat(obj.layer) : "", " {");
    }

    css += obj.css;

    if (needLayer) {
      css += "}";
    }

    if (obj.media) {
      css += "}";
    }

    if (obj.supports) {
      css += "}";
    }
  } // For old IE

  /* istanbul ignore if  */


  if (styleElement.styleSheet) {
    styleElement.styleSheet.cssText = replaceText(index, css);
  } else {
    var cssNode = document.createTextNode(css);
    var childNodes = styleElement.childNodes;

    if (childNodes[index]) {
      styleElement.removeChild(childNodes[index]);
    }

    if (childNodes.length) {
      styleElement.insertBefore(cssNode, childNodes[index]);
    } else {
      styleElement.appendChild(cssNode);
    }
  }
}

var singletonData = {
  singleton: null,
  singletonCounter: 0
};
/* istanbul ignore next  */

function domAPI(options) {
  // eslint-disable-next-line no-undef,no-use-before-define
  var styleIndex = singletonData.singletonCounter++;
  var styleElement = // eslint-disable-next-line no-undef,no-use-before-define
  singletonData.singleton || ( // eslint-disable-next-line no-undef,no-use-before-define
  singletonData.singleton = options.insertStyleElement(options));
  return {
    update: function update(obj) {
      apply(styleElement, styleIndex, false, obj);
    },
    remove: function remove(obj) {
      apply(styleElement, styleIndex, true, obj);
    }
  };
}

module.exports = domAPI;

/***/ }),

/***/ 99:
/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {

__webpack_require__.r(__webpack_exports__);
/* harmony export */ __webpack_require__.d(__webpack_exports__, {
/* harmony export */   "default": () => (__WEBPACK_DEFAULT_EXPORT__)
/* harmony export */ });
/* harmony import */ var _node_modules_style_loader_dist_runtime_injectStylesIntoStyleTag_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(450);
/* harmony import */ var _node_modules_style_loader_dist_runtime_injectStylesIntoStyleTag_js__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(_node_modules_style_loader_dist_runtime_injectStylesIntoStyleTag_js__WEBPACK_IMPORTED_MODULE_0__);
/* harmony import */ var _node_modules_style_loader_dist_runtime_singletonStyleDomAPI_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(653);
/* harmony import */ var _node_modules_style_loader_dist_runtime_singletonStyleDomAPI_js__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(_node_modules_style_loader_dist_runtime_singletonStyleDomAPI_js__WEBPACK_IMPORTED_MODULE_1__);
/* harmony import */ var _node_modules_style_loader_dist_runtime_insertBySelector_js__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(199);
/* harmony import */ var _node_modules_style_loader_dist_runtime_insertBySelector_js__WEBPACK_IMPORTED_MODULE_2___default = /*#__PURE__*/__webpack_require__.n(_node_modules_style_loader_dist_runtime_insertBySelector_js__WEBPACK_IMPORTED_MODULE_2__);
/* harmony import */ var _node_modules_style_loader_dist_runtime_setAttributesWithoutAttributes_js__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(458);
/* harmony import */ var _node_modules_style_loader_dist_runtime_setAttributesWithoutAttributes_js__WEBPACK_IMPORTED_MODULE_3___default = /*#__PURE__*/__webpack_require__.n(_node_modules_style_loader_dist_runtime_setAttributesWithoutAttributes_js__WEBPACK_IMPORTED_MODULE_3__);
/* harmony import */ var _node_modules_style_loader_dist_runtime_insertStyleElement_js__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(994);
/* harmony import */ var _node_modules_style_loader_dist_runtime_insertStyleElement_js__WEBPACK_IMPORTED_MODULE_4___default = /*#__PURE__*/__webpack_require__.n(_node_modules_style_loader_dist_runtime_insertStyleElement_js__WEBPACK_IMPORTED_MODULE_4__);
/* harmony import */ var _node_modules_css_loader_dist_cjs_js_omniplus_css__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(535);

      
      
      
      
      
      
      
      
      

var options = {};

;
options.setAttributes = (_node_modules_style_loader_dist_runtime_setAttributesWithoutAttributes_js__WEBPACK_IMPORTED_MODULE_3___default());

      options.insert = _node_modules_style_loader_dist_runtime_insertBySelector_js__WEBPACK_IMPORTED_MODULE_2___default().bind(null, "head");
    
options.domAPI = (_node_modules_style_loader_dist_runtime_singletonStyleDomAPI_js__WEBPACK_IMPORTED_MODULE_1___default());
options.insertStyleElement = (_node_modules_style_loader_dist_runtime_insertStyleElement_js__WEBPACK_IMPORTED_MODULE_4___default());

var update = _node_modules_style_loader_dist_runtime_injectStylesIntoStyleTag_js__WEBPACK_IMPORTED_MODULE_0___default()(_node_modules_css_loader_dist_cjs_js_omniplus_css__WEBPACK_IMPORTED_MODULE_5__/* ["default"] */ .Z, options);




       /* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = (_node_modules_css_loader_dist_cjs_js_omniplus_css__WEBPACK_IMPORTED_MODULE_5__/* ["default"] */ .Z && _node_modules_css_loader_dist_cjs_js_omniplus_css__WEBPACK_IMPORTED_MODULE_5__/* ["default"].locals */ .Z.locals ? _node_modules_css_loader_dist_cjs_js_omniplus_css__WEBPACK_IMPORTED_MODULE_5__/* ["default"].locals */ .Z.locals : undefined);


/***/ })

/******/ 	});
/************************************************************************/
/******/ 	// The module cache
/******/ 	var __webpack_module_cache__ = {};
/******/ 	
/******/ 	// The require function
/******/ 	function __webpack_require__(moduleId) {
/******/ 		// Check if module is in cache
/******/ 		var cachedModule = __webpack_module_cache__[moduleId];
/******/ 		if (cachedModule !== undefined) {
/******/ 			return cachedModule.exports;
/******/ 		}
/******/ 		// Create a new module (and put it into the cache)
/******/ 		var module = __webpack_module_cache__[moduleId] = {
/******/ 			id: moduleId,
/******/ 			// no module.loaded needed
/******/ 			exports: {}
/******/ 		};
/******/ 	
/******/ 		// Execute the module function
/******/ 		__webpack_modules__[moduleId](module, module.exports, __webpack_require__);
/******/ 	
/******/ 		// Return the exports of the module
/******/ 		return module.exports;
/******/ 	}
/******/ 	
/************************************************************************/
/******/ 	/* webpack/runtime/compat get default export */
/******/ 	(() => {
/******/ 		// getDefaultExport function for compatibility with non-harmony modules
/******/ 		__webpack_require__.n = (module) => {
/******/ 			var getter = module && module.__esModule ?
/******/ 				() => (module['default']) :
/******/ 				() => (module);
/******/ 			__webpack_require__.d(getter, { a: getter });
/******/ 			return getter;
/******/ 		};
/******/ 	})();
/******/ 	
/******/ 	/* webpack/runtime/define property getters */
/******/ 	(() => {
/******/ 		// define getter functions for harmony exports
/******/ 		__webpack_require__.d = (exports, definition) => {
/******/ 			for(var key in definition) {
/******/ 				if(__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) {
/******/ 					Object.defineProperty(exports, key, { enumerable: true, get: definition[key] });
/******/ 				}
/******/ 			}
/******/ 		};
/******/ 	})();
/******/ 	
/******/ 	/* webpack/runtime/hasOwnProperty shorthand */
/******/ 	(() => {
/******/ 		__webpack_require__.o = (obj, prop) => (Object.prototype.hasOwnProperty.call(obj, prop))
/******/ 	})();
/******/ 	
/******/ 	/* webpack/runtime/make namespace object */
/******/ 	(() => {
/******/ 		// define __esModule on exports
/******/ 		__webpack_require__.r = (exports) => {
/******/ 			if(typeof Symbol !== 'undefined' && Symbol.toStringTag) {
/******/ 				Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
/******/ 			}
/******/ 			Object.defineProperty(exports, '__esModule', { value: true });
/******/ 		};
/******/ 	})();
/******/ 	
/************************************************************************/
var __webpack_exports__ = {};
// This entry need to be wrapped in an IIFE because it need to be isolated against other modules in the chunk.
(() => {
var exports = __webpack_exports__;
var __webpack_unused_export__;

__webpack_unused_export__ = ({ value: true });
const overview_1 = __webpack_require__(916);
const container_1 = __webpack_require__(277);
const logo_patcher_1 = __webpack_require__(347);
const overview_button_1 = __webpack_require__(824);
const page_patcher_1 = __webpack_require__(50);
const forum_subject_1 = __webpack_require__(802);
const assessment_1 = __webpack_require__(311);
__webpack_require__(99);
(0, page_patcher_1.removeHeaderImage)();
(0, logo_patcher_1.injectOmniplusLogo)();
// @ts-ignore
window['Assessment'] = assessment_1.Assessment;
// If the script is being run on Lea.
if (window.location.href.includes('ovx.omnivox.ca')) {
    (0, overview_button_1.injectDocumentsOverviewButtonToLea)();
    (0, page_patcher_1.removeLeaAnchorHoverCSSRule)();
    // If the script is being run on the document overview page.
    if (window.location.href.includes('SommaireDocuments.aspx')) {
        (0, page_patcher_1.removeAllLineBreaks)();
        const overview = new overview_1.LeaDocumentsOverview(container_1.LeaDocumentsContainer.loadFromDocumentOverviewPage(document));
        overview.injectToDocumentOverviewPage();
        overview.render();
    }
    // If the script is being ran on the class forum.
    if (window.location.href.includes('ForumClasse.aspx')) {
        // If this is a forum subject page.
        if (window.location.href.includes('a=msg')) {
            const subject = forum_subject_1.ForumSubject.loadFromForumPostPage(document);
            subject.injectToForumSubjectPage();
            subject.render();
        }
    }
}
// If the script is being run on Omnivox/MIO
else {
    // If this is the login page.
    if (window.location.href.includes('Login')) {
        (0, page_patcher_1.autoLogin)();
    }
}

})();

/******/ })()
;
//# sourceMappingURL=main.bundle.js.map