diff --git a/app/static/js/app/app.js b/app/static/js/app/app.js index 92a5c5462d5a1c608f3e8ce36fed4de09b032ab7..fefabd1a80cd076820e760a509527cbf8a51d676 100644 --- a/app/static/js/app/app.js +++ b/app/static/js/app/app.js @@ -1,3 +1,8 @@ +// IDEA: Split the App logic into seperate units +// - App.Data +// - App.IO (name is WIP) +// - App.UI + App.App = class App { constructor() { this.data = { @@ -101,4 +106,94 @@ App.App = class App { // Apply Patch jsonpatch.applyPatch(this.data, filteredPatch); } + + init() { + this.initUi(); + } + + initUi() { + /* Pre-Initialization fixes */ + // #region + + // Flask-WTF sets the standard HTML maxlength Attribute on input/textarea + // elements to specify their maximum length (in characters). Unfortunatly + // Materialize won't recognize the maxlength Attribute, instead it uses + // the data-length Attribute. It's conversion time :) + for (let elem of document.querySelectorAll('input[maxlength], textarea[maxlength]')) { + elem.dataset.length = elem.getAttribute('maxlength'); + elem.removeAttribute('maxlength'); + } + + // To work around some limitations with the Form setup of Flask-WTF. + // HTML option elements with an empty value are considered as placeholder + // elements. The user should not be able to actively select these options. + // So they get the disabled attribute. + for (let optionElement of document.querySelectorAll('option[value=""]')) { + optionElement.disabled = true; + } + + // TODO: Check why we are doing this. + for (let optgroupElement of document.querySelectorAll('optgroup[label=""]')) { + for (let c of optgroupElement.children) { + optgroupElement.parentElement.insertAdjacentElement('afterbegin', c); + } + optgroupElement.remove(); + } + + // #endregion + + /* Initialize Materialize Components */ + // #region + + // Automatically initialize Materialize Components that do not require + // additional configuration. + M.AutoInit(); + + // CharacterCounters + // Materialize didn't include the CharacterCounter plugin within the + // AutoInit method (maybe they forgot it?). Anyway... We do it here. :) + M.CharacterCounter.init(document.querySelectorAll('input[data-length]:not(.no-autoinit), textarea[data-length]:not(.no-autoinit)')); + + // Header navigation "more" Dropdown. + M.Dropdown.init( + document.querySelector('#nav-more-dropdown-trigger'), + { + alignment: 'right', + constrainWidth: false, + coverTrigger: false + } + ); + + // Manual modal + M.Modal.init( + document.querySelector('#manual-modal'), + { + onOpenStart: (modalElement, modalTriggerElement) => { + if ('manualModalChapter' in modalTriggerElement.dataset) { + let manualModalTocElement = document.querySelector('#manual-modal-toc'); + let manualModalToc = M.Tabs.getInstance(manualModalTocElement); + manualModalToc.select(modalTriggerElement.dataset.manualModalChapter); + } + } + } + ); + + // Terms of use modal + M.Modal.init( + document.querySelector('#terms-of-use-modal'), + { + dismissible: false, + onCloseEnd: (modalElement) => { + Requests.users.entity.acceptTermsOfUse(); + } + } + ); + // #endregion + + // #region Nopaque Components + ResourceDisplays.AutoInit(); + ResourceLists.AutoInit(); + Forms.AutoInit(); + // #endregion Nopaque Components + } }; diff --git a/app/static/js/forms/index.js b/app/static/js/forms/index.js index 0290944846e0894efb2659f64aa3a5a6b4dcae08..a6d098ddf28d01701cc2e9e0f643dfc2066f39a6 100644 --- a/app/static/js/forms/index.js +++ b/app/static/js/forms/index.js @@ -1,6 +1,6 @@ var Forms = {}; -Forms.autoInit = () => { +Forms.AutoInit = () => { for (let propertyName in Forms) { let property = Forms[propertyName]; // Call autoInit of all properties that are subclasses of Forms.BaseForm. diff --git a/app/static/js/resource-displays/index.js b/app/static/js/resource-displays/index.js index 8cc8809e9a247dca203db199e45dfdfebcafe1cd..a559189d21bc9950b546ca4f7556d37c9b882233 100644 --- a/app/static/js/resource-displays/index.js +++ b/app/static/js/resource-displays/index.js @@ -1,6 +1,6 @@ var ResourceDisplays = {}; -ResourceDisplays.autoInit = () => { +ResourceDisplays.AutoInit = () => { for (let propertyName in ResourceDisplays) { let property = ResourceDisplays[propertyName]; // Call autoInit of all properties that are subclasses of `ResourceDisplays.ResourceDisplay`. diff --git a/app/static/js/resource-lists/index.js b/app/static/js/resource-lists/index.js index 513da46b90cee965b35b4fb45c77ca9464a660eb..1683a6a5a92e811490b2cab2bb04744084f4e269 100644 --- a/app/static/js/resource-lists/index.js +++ b/app/static/js/resource-lists/index.js @@ -1,6 +1,6 @@ var ResourceLists = {}; -ResourceLists.autoInit = () => { +ResourceLists.AutoInit = () => { for (let propertyName in ResourceLists) { let property = ResourceLists[propertyName]; // Call autoInit of all properties that are subclasses of `ResourceLists.ResourceList`. diff --git a/app/templates/_scripts.html.j2 b/app/templates/_scripts.html.j2 index 1fd32f7e377bb00ff905dd5f331f21739fb90d4f..78b7af601d75192d48d8682d75ba4d60002e4a91 100644 --- a/app/templates/_scripts.html.j2 +++ b/app/templates/_scripts.html.j2 @@ -119,76 +119,29 @@ <script> // TODO: Implement an app.run method and use this for all of the following const app = new App.App(); + app.init(); + + // Check if the current user is authenticated {%- if current_user.is_authenticated %} + // TODO: Set this as a property of the app object const currentUserId = {{ current_user.hashid|tojson }}; - // Initialize components for current user + // Subscribe to the current user's data events app.subscribeUser(currentUserId) .catch((error) => {throw JSON.stringify(error);}); + + // Get the current user's data app.getUser(currentUserId, true, true) .catch((error) => {throw JSON.stringify(error);}); - {%- endif %} - - // Disable all option elements with no value - for (let optionElement of document.querySelectorAll('option[value=""]')) { - optionElement.disabled = true; - } - for (let optgroupElement of document.querySelectorAll('optgroup[label=""]')) { - for (let c of optgroupElement.children) { - optgroupElement.parentElement.insertAdjacentElement('afterbegin', c); - } - optgroupElement.remove(); - - } - // Set the data-length attribute on textareas/inputs with the maxlength attribute - for (let inputElement of document.querySelectorAll('textarea[maxlength], input[maxlength]')) { - inputElement.dataset.length = inputElement.getAttribute('maxlength'); - } - // Initialize components - M.AutoInit(); - M.CharacterCounter.init(document.querySelectorAll('input[data-length], textarea[data-length]')); - M.Dropdown.init( - document.querySelectorAll('#nav-more-dropdown-trigger'), - {alignment: 'right', constrainWidth: false, coverTrigger: false} - ); - ResourceDisplays.autoInit(); - ResourceLists.autoInit(); - Forms.autoInit(); + // Check if the current user hasn't accepted the terms of use yet + {%- if not current_user.terms_of_use_accepted %} + M.Modal.getInstance(document.querySelector('#terms-of-use-modal')).open(); + {%- endif %} + {%- endif %} // Display flashed messages for (let [category, message] of {{ get_flashed_messages(with_categories=True)|tojson }}) { app.flash(message, message); } - - // Initialize manual modal - let manualModalTableOfContentsElement = document.querySelector('#manual-modal-table-of-contents'); - let manualModalTableOfContents = M.Tabs.init(manualModalTableOfContentsElement); - let manualModalElement = document.querySelector('#manual-modal'); - let manualModal = M.Modal.init( - manualModalElement, - { - onOpenStart: (manualModalElement, modalTriggerElement) => { - if ('manualModalChapter' in modalTriggerElement.dataset) { - manualModalTableOfContents.select(modalTriggerElement.dataset.manualModalChapter); - } - } - } - ); - - // Initialize terms of use modal - const termsOfUseModal = document.getElementById('terms-of-use-modal'); - M.Modal.init( - termsOfUseModal, - { - dismissible: false, - onCloseEnd: () => { - requests.users.entity.acceptTermsOfUse(); - } - } - ); - {% if current_user.is_authenticated and not current_user.terms_of_use_accepted %} - termsOfUseModal.M_Modal.open(); - {% endif %} - </script> diff --git a/app/templates/main/_manual_modal.html.j2 b/app/templates/main/_manual_modal.html.j2 index 3eb5d4a1c95ba1454192a820521489525a3ed31d..9233fb0d0567e8c4a00ff52f41cd98e01f000ef7 100644 --- a/app/templates/main/_manual_modal.html.j2 +++ b/app/templates/main/_manual_modal.html.j2 @@ -1,7 +1,7 @@ <div id="manual-modal" class="modal no-autoinit"> <div class="modal-content"> <h2>Manual</h2> - <ul class="tabs no-autoinit" id="manual-modal-table-of-contents"> + <ul class="tabs" id="manual-modal-toc"> <li class="tab"><a href="#manual-modal-introduction">Introduction</a></li> <li class="tab"><a href="#manual-modal-registration-and-log-in">Registration and Log in</a></li> <li class="tab"><a href="#manual-modal-dashboard">Dashboard</a></li>