diff options
| author | rr- | 2016-04-09 18:54:23 +0200 |
|---|---|---|
| committer | rr- | 2016-04-09 22:23:24 +0200 |
| commit | 2e1823b7080aa0b3de986eb8f35f68837d04e023 (patch) | |
| tree | aad254b860d4078b9d5b829a50cf5ddd3b837deb /client/js/views | |
| parent | 4821f2ac6ca1fd8e8a08d236815d8c964e473316 (diff) | |
client/views: replace inheritance with composition
Diffstat (limited to 'client/js/views')
| -rw-r--r-- | client/js/views/base_view.js | 115 | ||||
| -rw-r--r-- | client/js/views/help_view.js | 20 | ||||
| -rw-r--r-- | client/js/views/home_view.js | 13 | ||||
| -rw-r--r-- | client/js/views/login_view.js | 21 | ||||
| -rw-r--r-- | client/js/views/password_reset_view.js | 21 | ||||
| -rw-r--r-- | client/js/views/registration_view.js | 21 | ||||
| -rw-r--r-- | client/js/views/top_nav_view.js | 9 | ||||
| -rw-r--r-- | client/js/views/user_deletion_view.js | 16 | ||||
| -rw-r--r-- | client/js/views/user_edit_view.js | 18 | ||||
| -rw-r--r-- | client/js/views/user_summary_view.js | 10 | ||||
| -rw-r--r-- | client/js/views/user_view.js | 12 |
11 files changed, 79 insertions, 197 deletions
diff --git a/client/js/views/base_view.js b/client/js/views/base_view.js deleted file mode 100644 index 98ae406..0000000 --- a/client/js/views/base_view.js +++ /dev/null @@ -1,115 +0,0 @@ -'use strict'; - -const handlebars = require('handlebars'); -const events = require('../events.js'); -const contentHolder = document.getElementById('content-holder'); -require('../util/polyfill.js'); - -function messageHandler(message, className) { - if (!message) { - message = 'Unknown message'; - } - const messagesHolder = contentHolder.querySelector('.messages'); - if (!messagesHolder) { - alert(message); - return; - } - /* TODO: animate this */ - const node = document.createElement('div'); - node.innerHTML = message.replace(/\n/g, '<br/>'); - node.classList.add('message'); - node.classList.add(className); - messagesHolder.appendChild(node); -} - -events.listen(events.Success, msg => { messageHandler(msg, 'success'); }); -events.listen(events.Error, msg => { messageHandler(msg, 'error'); }); - -class BaseView { - constructor() { - this.contentHolder = contentHolder; - this.domParser = new DOMParser(); - } - - htmlToDom(html) { - const parsed = this.domParser.parseFromString(html, 'text/html').body; - return parsed.childNodes.length > 1 ? - parsed.childNodes : - parsed.firstChild; - } - - getTemplate(templatePath) { - const templateElement = document.getElementById(templatePath); - if (!templateElement) { - console.error('Missing template: ' + templatePath); - return null; - } - const templateText = templateElement.innerHTML.trim(); - const templateFactory = handlebars.compile(templateText); - return (...args) => { - return this.htmlToDom(templateFactory(...args)); - }; - } - - clearMessages() { - const messagesHolder = this.contentHolder.querySelector('.messages'); - /* TODO: animate that */ - while (messagesHolder.lastChild) { - messagesHolder.removeChild(messagesHolder.lastChild); - } - } - - decorateValidator(form) { - // postpone showing form fields validity until user actually tries - // to submit it (seeing red/green form w/o doing anything breaks POLA) - const submitButton = form.querySelector('.buttons input'); - submitButton.addEventListener('click', e => { - form.classList.add('show-validation'); - }); - form.addEventListener('submit', e => { - form.classList.remove('show-validation'); - }); - } - - disableForm(form) { - for (let input of form.querySelectorAll('input')) { - input.disabled = true; - } - } - - enableForm(form) { - for (let input of form.querySelectorAll('input')) { - input.disabled = false; - } - } - - emptyView(target) { - return this.showView( - target, - this.htmlToDom('<div class="messages"></div>')); - } - - showView(target, source) { - return new Promise((resolve, reject) => { - let observer = new MutationObserver(mutations => { - resolve(); - observer.disconnect(); - }); - observer.observe(target, {childList: true}); - while (target.lastChild) { - target.removeChild(target.lastChild); - } - if (source instanceof NodeList) { - for (let child of source) { - target.appendChild(child); - } - } else if (source instanceof Node) { - target.appendChild(source); - } else { - console.error('Invalid view source', source); - } - }); - } -} - -module.exports = BaseView; diff --git a/client/js/views/help_view.js b/client/js/views/help_view.js index d3f198c..833e548 100644 --- a/client/js/views/help_view.js +++ b/client/js/views/help_view.js @@ -1,31 +1,30 @@ 'use strict'; const config = require('../config.js'); -const BaseView = require('./base_view.js'); +const views = require('../util/views.js'); -class HelpView extends BaseView { +class HelpView { constructor() { - super(); - this.template = this.getTemplate('help-template'); + this.template = views.getTemplate('help'); this.sectionTemplates = {}; const sectionKeys = ['about', 'keyboard', 'search', 'comments', 'tos']; for (let section of sectionKeys) { - const templateName = 'help-' + section + '-template'; - this.sectionTemplates[section] = this.getTemplate(templateName); + const templateName = 'help-' + section; + this.sectionTemplates[section] = views.getTemplate(templateName); } } render(ctx) { - const target = this.contentHolder; + const target = document.getElementById('content-holder'); const source = this.template(); ctx.section = ctx.section || 'about'; if (!(ctx.section in this.sectionTemplates)) { - this.emptyView(this.contentHolder); + views.emptyView(target); return; } - this.showView( + views.showView( source.querySelector('.content'), this.sectionTemplates[ctx.section]({ name: config.name, @@ -40,7 +39,8 @@ class HelpView extends BaseView { } } - this.showView(target, source); + views.listenToMessages(target); + views.showView(target, source); } } diff --git a/client/js/views/home_view.js b/client/js/views/home_view.js index 341982a..b27fb14 100644 --- a/client/js/views/home_view.js +++ b/client/js/views/home_view.js @@ -1,22 +1,23 @@ 'use strict'; const config = require('../config.js'); -const BaseView = require('./base_view.js'); +const views = require('../util/views.js'); -class HomeView extends BaseView { +class HomeView { constructor() { - super(); - this.template = this.getTemplate('home-template'); + this.template = views.getTemplate('home'); } render(ctx) { - const target = this.contentHolder; + const target = document.getElementById('content-holder'); const source = this.template({ name: config.name, version: config.meta.version, buildDate: config.meta.buildDate, }); - this.showView(target, source); + + views.listenToMessages(target); + views.showView(target, source); } } diff --git a/client/js/views/login_view.js b/client/js/views/login_view.js index f0e90c0..9a1763c 100644 --- a/client/js/views/login_view.js +++ b/client/js/views/login_view.js @@ -1,17 +1,15 @@ 'use strict'; const config = require('../config.js'); -const events = require('../events.js'); -const BaseView = require('./base_view.js'); +const views = require('../util/views.js'); -class LoginView extends BaseView { +class LoginView { constructor() { - super(); - this.template = this.getTemplate('login-template'); + this.template = views.getTemplate('login'); } render(ctx) { - const target = this.contentHolder; + const target = document.getElementById('content-holder'); const source = this.template({canSendMails: config.canSendMails}); const form = source.querySelector('form'); @@ -19,22 +17,23 @@ class LoginView extends BaseView { const passwordField = source.querySelector('#user-password'); const rememberUserField = source.querySelector('#remember-user'); - this.decorateValidator(form); + views.decorateValidator(form); userNameField.setAttribute('pattern', config.userNameRegex); passwordField.setAttribute('pattern', config.passwordRegex); form.addEventListener('submit', e => { e.preventDefault(); - this.clearMessages(); - this.disableForm(form); + views.clearMessages(target); + views.disableForm(form); ctx.login( userNameField.value, passwordField.value, rememberUserField.checked) - .always(() => { this.enableForm(form); }); + .always(() => { views.enableForm(form); }); }); - this.showView(target, source); + views.listenToMessages(target); + views.showView(target, source); } } diff --git a/client/js/views/password_reset_view.js b/client/js/views/password_reset_view.js index 12729bc..14f0737 100644 --- a/client/js/views/password_reset_view.js +++ b/client/js/views/password_reset_view.js @@ -1,32 +1,31 @@ 'use strict'; -const events = require('../events.js'); -const BaseView = require('./base_view.js'); +const views = require('../util/views.js'); -class PasswordResetView extends BaseView { +class PasswordResetView { constructor() { - super(); - this.template = this.getTemplate('password-reset-template'); + this.template = views.getTemplate('password-reset'); } render(ctx) { - const target = this.contentHolder; + const target = document.getElementById('content-holder'); const source = this.template(); const form = source.querySelector('form'); const userNameOrEmailField = source.querySelector('#user-name'); - this.decorateValidator(form); + views.decorateValidator(form); form.addEventListener('submit', e => { e.preventDefault(); - this.clearMessages(); - this.disableForm(form); + views.clearMessages(target); + views.disableForm(form); ctx.proceed(userNameOrEmailField.value) - .catch(() => { this.enableForm(form); }); + .catch(() => { views.enableForm(form); }); }); - this.showView(target, source); + views.listenToMessages(target); + views.showView(target, source); } } diff --git a/client/js/views/registration_view.js b/client/js/views/registration_view.js index 7c58ab5..55ad0a1 100644 --- a/client/js/views/registration_view.js +++ b/client/js/views/registration_view.js @@ -1,17 +1,15 @@ 'use strict'; const config = require('../config.js'); -const events = require('../events.js'); -const BaseView = require('./base_view.js'); +const views = require('../util/views.js'); -class RegistrationView extends BaseView { +class RegistrationView { constructor() { - super(); - this.template = this.getTemplate('user-registration-template'); + this.template = views.getTemplate('user-registration'); } render(ctx) { - const target = this.contentHolder; + const target = document.getElementById('content-holder'); const source = this.template(); const form = source.querySelector('form'); @@ -19,22 +17,23 @@ class RegistrationView extends BaseView { const passwordField = source.querySelector('#user-password'); const emailField = source.querySelector('#user-email'); - this.decorateValidator(form); + views.decorateValidator(form); userNameField.setAttribute('pattern', config.userNameRegex); passwordField.setAttribute('pattern', config.passwordRegex); form.addEventListener('submit', e => { e.preventDefault(); - this.clearMessages(); - this.disableForm(form); + views.clearMessages(target); + views.disableForm(form); ctx.register( userNameField.value, passwordField.value, emailField.value) - .always(() => { this.enableForm(form); }); + .always(() => { views.enableForm(form); }); }); - this.showView(target, source); + views.listenToMessages(target); + views.showView(target, source); } } diff --git a/client/js/views/top_nav_view.js b/client/js/views/top_nav_view.js index 7e9c271..3210564 100644 --- a/client/js/views/top_nav_view.js +++ b/client/js/views/top_nav_view.js @@ -1,11 +1,10 @@ 'use strict'; -const BaseView = require('./base_view.js'); +const views = require('../util/views.js'); -class TopNavView extends BaseView { +class TopNavView { constructor() { - super(); - this.template = this.getTemplate('top-nav-template'); + this.template = views.getTemplate('top-nav'); this.navHolder = document.getElementById('top-nav-holder'); } @@ -21,7 +20,7 @@ class TopNavView extends BaseView { '<span class="access-key" data-accesskey="$1">$1</span>'); } - this.showView(this.navHolder, source); + views.showView(this.navHolder, source); } activate(itemName) { diff --git a/client/js/views/user_deletion_view.js b/client/js/views/user_deletion_view.js index 3861685..20c950f 100644 --- a/client/js/views/user_deletion_view.js +++ b/client/js/views/user_deletion_view.js @@ -1,11 +1,10 @@ 'use strict'; -const BaseView = require('./base_view.js'); +const views = require('../util/views.js'); -class UserDeletionView extends BaseView { +class UserDeletionView { constructor() { - super(); - this.template = this.getTemplate('user-deletion-template'); + this.template = views.getTemplate('user-deletion'); } render(ctx) { @@ -14,16 +13,17 @@ class UserDeletionView extends BaseView { const form = source.querySelector('form'); - this.decorateValidator(form); + views.decorateValidator(form); form.addEventListener('submit', e => { e.preventDefault(); - this.clearMessages(); - this.disableForm(form); + views.clearMessages(target); + views.disableForm(form); ctx.delete(); }); - this.showView(target, source); + views.listenToMessages(target); + views.showView(target, source); } } diff --git a/client/js/views/user_edit_view.js b/client/js/views/user_edit_view.js index 9d5c3ae..21ac7ba 100644 --- a/client/js/views/user_edit_view.js +++ b/client/js/views/user_edit_view.js @@ -1,12 +1,11 @@ 'use strict'; const config = require('../config.js'); -const BaseView = require('./base_view.js'); +const views = require('../util/views.js'); -class UserEditView extends BaseView { +class UserEditView { constructor() { - super(); - this.template = this.getTemplate('user-edit-template'); + this.template = views.getTemplate('user-edit'); } render(ctx) { @@ -19,7 +18,7 @@ class UserEditView extends BaseView { const userNameField = source.querySelector('#user-name'); const passwordField = source.querySelector('#user-password'); - this.decorateValidator(form); + views.decorateValidator(form); if (userNameField) { userNameField.setAttribute( @@ -41,17 +40,18 @@ class UserEditView extends BaseView { form.addEventListener('submit', e => { e.preventDefault(); - this.clearMessages(); - this.disableForm(form); + views.clearMessages(target); + views.disableForm(form); ctx.edit( userNameField.value, passwordField.value, emailField.value, rankField.value) - .always(() => { this.enableForm(form); }); + .always(() => { views.enableForm(form); }); }); - this.showView(target, source); + views.listenToMessages(target); + views.showView(target, source); } } diff --git a/client/js/views/user_summary_view.js b/client/js/views/user_summary_view.js index ef3029a..9369376 100644 --- a/client/js/views/user_summary_view.js +++ b/client/js/views/user_summary_view.js @@ -1,17 +1,17 @@ 'use strict'; -const BaseView = require('./base_view.js'); +const views = require('../util/views.js'); -class UserSummaryView extends BaseView { +class UserSummaryView { constructor() { - super(); - this.template = this.getTemplate('user-summary-template'); + this.template = views.getTemplate('user-summary'); } render(ctx) { const target = ctx.target; const source = this.template(ctx); - this.showView(target, source); + views.listenToMessages(target); + views.showView(target, source); } } diff --git a/client/js/views/user_view.js b/client/js/views/user_view.js index cb38fc1..c547549 100644 --- a/client/js/views/user_view.js +++ b/client/js/views/user_view.js @@ -1,21 +1,20 @@ 'use strict'; -const BaseView = require('./base_view.js'); +const views = require('../util/views.js'); const UserDeletionView = require('./user_deletion_view.js'); const UserSummaryView = require('./user_summary_view.js'); const UserEditView = require('./user_edit_view.js'); -class UserView extends BaseView { +class UserView { constructor() { - super(); - this.template = this.getTemplate('user-template'); + this.template = views.getTemplate('user'); this.deletionView = new UserDeletionView(); this.summaryView = new UserSummaryView(); this.editView = new UserEditView(); } render(ctx) { - const target = this.contentHolder; + const target = document.getElementById('content-holder'); const source = this.template(ctx); ctx.section = ctx.section || 'summary'; @@ -39,7 +38,8 @@ class UserView extends BaseView { ctx.target = source.querySelector('#user-content-holder'); view.render(ctx); - this.showView(target, source); + views.listenToMessages(target); + views.showView(target, source); } } |