client/views: move form controls to HB helpers
This commit is contained in:
parent
7c1876dd5c
commit
e268d679d3
10 changed files with 148 additions and 56 deletions
|
@ -4,16 +4,13 @@
|
|||
<div class='input'>
|
||||
<ul>
|
||||
<li>
|
||||
<label for='user-name'>User name</label>
|
||||
<input id='user-name' name='name' type='text' required/>
|
||||
{{textInput text='User name' id='user-name' name='name' required=true pattern=this.userNamePattern}}
|
||||
</li>
|
||||
<li>
|
||||
<label for='user-password'>Password</label>
|
||||
<input id='user-password' name='password' type='password' required/>
|
||||
{{passwordInput text='Password' id='user-password' name='password' required=true pattern=this.passwordPattern}}
|
||||
</li>
|
||||
<li>
|
||||
<input id='remember-user' name='remember-user' type='checkbox'/>
|
||||
<label for='remember-user' class='checkbox'>Remember me</label>
|
||||
{{checkbox text='Remember me' id='remember-user' name='remember-user'}}
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
|
|
@ -4,8 +4,7 @@
|
|||
<div class='input'>
|
||||
<ul>
|
||||
<li>
|
||||
<label for='user-name'>User name or e-mail address</label>
|
||||
<input id='user-name' name='user-name' type='text' autocomplete='off' required/>
|
||||
{{textInput text='User name or e-mail address' id='user-name' name='user-name' required=true}}
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
|
|
@ -3,10 +3,7 @@
|
|||
<div class='input'>
|
||||
<ul>
|
||||
<li>
|
||||
<input id='confirm-deletion' name='confirm-deletion' type='checkbox' required/>
|
||||
<label for='confirm-deletion' class='checkbox'>
|
||||
I confirm that I want to delete {{#if this.isLoggedIn}}my{{else}}this{{/if}} account.
|
||||
</label>
|
||||
{{checkbox id='confirm-deletion' name='confirm-deletion' required=true text='I confirm that I want to delete this account.'}}
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
|
|
@ -4,33 +4,25 @@
|
|||
<ul>
|
||||
{{#if this.canEditName}}
|
||||
<li>
|
||||
<label for='user-name'>User name</label>
|
||||
<input id='user-name' name='name' type='text' value='{{this.user.name}}'/>
|
||||
{{textInput text='User name' id='user-name' name='name' value=this.user.name}}
|
||||
</li>
|
||||
{{/if}}
|
||||
|
||||
{{#if this.canEditPassword}}
|
||||
<li>
|
||||
<label for='user-password'>Password</label>
|
||||
<input id='user-password' name='password' type='password' placeholder='leave blank if not changing'/>
|
||||
{{passwordInput text='Password' id='user-password' name='password' placeholder='leave blank if not changing'}}
|
||||
</li>
|
||||
{{/if}}
|
||||
|
||||
{{#if this.canEditEmail}}
|
||||
<li>
|
||||
<label for='user-email'>Email</label>
|
||||
<input id='user-email' name='email' type='email' value='{{this.user.email}}'/>
|
||||
{{emailInput text='Email' id='user-email' name='email' value=this.user.email}}
|
||||
</li>
|
||||
{{/if}}
|
||||
|
||||
{{#if this.canEditRank}}
|
||||
<li>
|
||||
<label for='user-rank'>Rank</label>
|
||||
<select id='user-rank' name='rank'>
|
||||
{{#each this.ranks}}
|
||||
<option value='{{@key}}'>{{this}}</option>
|
||||
{{/each}}
|
||||
</select>
|
||||
{{select text='Rank' id='user-rank' name='rank' keyValues=this.ranks selectedKey=this.user.rank}}
|
||||
</li>
|
||||
{{/if}}
|
||||
</ul>
|
||||
|
|
|
@ -4,16 +4,13 @@
|
|||
<div class='input'>
|
||||
<ul>
|
||||
<li>
|
||||
<label for='user-name'>User name</label>
|
||||
<input id='user-name' name='user-name' type='text' autocomplete='off' placeholder='letters, digits, _, -' required/>
|
||||
{{textInput text='User name' id='user-name' name='user-name' placeholder='letters, digits, _, -' required=true pattern=this.userNamePattern}}
|
||||
</li>
|
||||
<li>
|
||||
<label for='user-password'>Password</label>
|
||||
<input id='user-password' name='user-password' type='password' autocomplete='off' placeholder='5+ characters' required/>
|
||||
{{passwordInput text='Password' id='user-password' name='user-password' placeholder='5+ characters' required=true pattern=this.passwordPattern}}
|
||||
</li>
|
||||
<li>
|
||||
<label for='user-email'>Email</label>
|
||||
<input id='user-email' name='user-email' type='email' autocomplete='off' placeholder='optional'/>
|
||||
{{emailInput text='Email' id='user-email' name='user-email' placeholder='optional'}}
|
||||
<p class='hint'>Used for password reminder and to show a <a href='http://gravatar.com/'>Gravatar</a>. Leave blank for random Gravatar.</p>
|
||||
</li>
|
||||
</ul>
|
||||
|
|
|
@ -1,23 +1,101 @@
|
|||
'use strict';
|
||||
|
||||
const views = require('../util/views.js');
|
||||
const handlebars = require('handlebars');
|
||||
const misc = require('./misc.js');
|
||||
|
||||
handlebars.registerHelper('reltime', function(time) {
|
||||
return new handlebars.SafeString(
|
||||
'<time datetime="' + time + '" title="' + time + '">' +
|
||||
misc.formatRelativeTime(time) +
|
||||
'</time>');
|
||||
views.makeNonVoidElement(
|
||||
'time',
|
||||
{datetime: time, title: time},
|
||||
misc.formatRelativeTime(time)));
|
||||
});
|
||||
|
||||
handlebars.registerHelper('thumbnail', function(url) {
|
||||
return new handlebars.SafeString(
|
||||
'<div class="thumbnail" ' +
|
||||
'style="background-image: url(\'' + url + '\')">' +
|
||||
'<img alt="thumbnail" src="' + url + '"/>' +
|
||||
'</div>');
|
||||
views.makeNonVoidElement('div', {
|
||||
class: 'thumbnail',
|
||||
style: 'background-image: url(\'{0}\')'.format(url)
|
||||
}, views.makeVoidElement('img', {alt: 'thumbnail', src: url})));
|
||||
});
|
||||
|
||||
handlebars.registerHelper('toLowerCase', function(str) {
|
||||
return str.toLowerCase();
|
||||
});
|
||||
|
||||
handlebars.registerHelper('radio', function(options) {
|
||||
return new handlebars.SafeString('{0}{1}'.format(
|
||||
views.makeVoidElement('input', {
|
||||
id: options.hash.id,
|
||||
name: options.hash.name,
|
||||
value: options.hash.value,
|
||||
type: 'radio',
|
||||
required: options.hash.required,
|
||||
}),
|
||||
views.makeNonVoidElement('label', {
|
||||
for: options.hash.id,
|
||||
class: 'radio',
|
||||
}, options.hash.text)));
|
||||
});
|
||||
|
||||
handlebars.registerHelper('checkbox', function(options) {
|
||||
return new handlebars.SafeString('{0}{1}'.format(
|
||||
views.makeVoidElement('input', {
|
||||
id: options.hash.id,
|
||||
name: options.hash.name,
|
||||
value: options.hash.value,
|
||||
type: 'checkbox',
|
||||
required: options.hash.required,
|
||||
}),
|
||||
views.makeNonVoidElement('label', {
|
||||
for: options.hash.id,
|
||||
class: 'checkbox',
|
||||
}, options.hash.text)));
|
||||
});
|
||||
|
||||
handlebars.registerHelper('select', function(options) {
|
||||
return new handlebars.SafeString('{0}{1}'.format(
|
||||
views.makeNonVoidElement(
|
||||
'label', {for: options.hash.id}, options.hash.text),
|
||||
views.makeNonVoidElement(
|
||||
'select',
|
||||
{id: options.hash.id, name: options.hash.name},
|
||||
Object.keys(options.hash.keyValues).map(key => {
|
||||
return views.makeNonVoidElement(
|
||||
'option',
|
||||
{value: key, selected: key === options.hash.selectedKey},
|
||||
options.hash.keyValues[key]);
|
||||
}).join(''))));
|
||||
});
|
||||
|
||||
handlebars.registerHelper('input', function(options) {
|
||||
return new handlebars.SafeString('{0}{1}'.format(
|
||||
views.makeNonVoidElement(
|
||||
'label', {for: options.hash.id}, options.hash.text),
|
||||
views.makeVoidElement(
|
||||
'input', {
|
||||
type: options.hash.inputType,
|
||||
name: options.hash.name,
|
||||
id: options.hash.id,
|
||||
value: options.hash.value || '',
|
||||
required: options.hash.required,
|
||||
pattern: options.hash.pattern,
|
||||
placeholder: options.hash.placeholder,
|
||||
})));
|
||||
});
|
||||
|
||||
handlebars.registerHelper('textInput', function(options) {
|
||||
options.hash.inputType = 'text';
|
||||
return handlebars.helpers.input(options);
|
||||
});
|
||||
|
||||
handlebars.registerHelper('passwordInput', function(options) {
|
||||
options.hash.inputType = 'password';
|
||||
return handlebars.helpers.input(options);
|
||||
});
|
||||
|
||||
handlebars.registerHelper('emailInput', function(options) {
|
||||
options.hash.inputType = 'email';
|
||||
return handlebars.helpers.input(options);
|
||||
});
|
||||
|
|
|
@ -37,3 +37,21 @@ Promise.prototype.always = function(onResolveOrReject) {
|
|||
throw reason;
|
||||
});
|
||||
};
|
||||
|
||||
if (!String.prototype.format) {
|
||||
String.prototype.format = function() {
|
||||
let str = this.toString();
|
||||
if (!arguments.length) {
|
||||
return str;
|
||||
}
|
||||
const type = typeof arguments[0];
|
||||
const args = (type == 'string' || type == 'number') ?
|
||||
arguments : arguments[0];
|
||||
for (let arg in args) {
|
||||
str = str.replace(
|
||||
new RegExp('\\{' + arg + '\\}', 'gi'),
|
||||
() => { return args[arg]; });
|
||||
}
|
||||
return str;
|
||||
};
|
||||
}
|
||||
|
|
|
@ -22,6 +22,29 @@ function _messageHandler(target, message, className) {
|
|||
messagesHolder.appendChild(node);
|
||||
}
|
||||
|
||||
function _serializeElement(name, attributes) {
|
||||
return [name]
|
||||
.concat(Object.keys(attributes).map(key => {
|
||||
if (attributes[key] === true) {
|
||||
return key;
|
||||
} else if (attributes[key] === false ||
|
||||
attributes[key] === undefined) {
|
||||
return '';
|
||||
}
|
||||
return '{0}="{1}"'.format(key, attributes[key]);
|
||||
}))
|
||||
.join(' ');
|
||||
}
|
||||
|
||||
function makeNonVoidElement(name, attributes, content) {
|
||||
return '<{0}>{1}</{0}>'.format(
|
||||
_serializeElement(name, attributes), content);
|
||||
}
|
||||
|
||||
function makeVoidElement(name, attributes) {
|
||||
return '<{0}/>'.format(_serializeElement(name, attributes));
|
||||
}
|
||||
|
||||
function listenToMessages(target) {
|
||||
events.unlisten(events.Success);
|
||||
events.unlisten(events.Error);
|
||||
|
@ -114,4 +137,6 @@ module.exports = {
|
|||
listenToMessages: listenToMessages,
|
||||
clearMessages: clearMessages,
|
||||
decorateValidator: decorateValidator,
|
||||
makeVoidElement: makeVoidElement,
|
||||
makeNonVoidElement: makeNonVoidElement,
|
||||
};
|
||||
|
|
|
@ -9,8 +9,11 @@ class RegistrationView {
|
|||
}
|
||||
|
||||
render(ctx) {
|
||||
ctx.userNamePattern = config.userNameRegex;
|
||||
ctx.passwordPattern = config.passwordRegex;
|
||||
|
||||
const target = document.getElementById('content-holder');
|
||||
const source = this.template();
|
||||
const source = this.template(ctx);
|
||||
|
||||
const form = source.querySelector('form');
|
||||
const userNameField = source.querySelector('#user-name');
|
||||
|
@ -18,8 +21,6 @@ class RegistrationView {
|
|||
const emailField = source.querySelector('#user-email');
|
||||
|
||||
views.decorateValidator(form);
|
||||
userNameField.setAttribute('pattern', config.userNameRegex);
|
||||
passwordField.setAttribute('pattern', config.passwordRegex);
|
||||
|
||||
form.addEventListener('submit', e => {
|
||||
e.preventDefault();
|
||||
|
|
|
@ -9,6 +9,9 @@ class UserEditView {
|
|||
}
|
||||
|
||||
render(ctx) {
|
||||
ctx.userNamePattern = config.userNameRegex + /|^$/.source;
|
||||
ctx.passwordPattern = config.passwordRegex + /|^$/.source;
|
||||
|
||||
const target = ctx.target;
|
||||
const source = this.template(ctx);
|
||||
|
||||
|
@ -17,25 +20,10 @@ class UserEditView {
|
|||
const emailField = source.querySelector('#user-email');
|
||||
const userNameField = source.querySelector('#user-name');
|
||||
const passwordField = source.querySelector('#user-password');
|
||||
const avatarStyleField = source.querySelector('#avatar-style');
|
||||
|
||||
views.decorateValidator(form);
|
||||
|
||||
if (userNameField) {
|
||||
userNameField.setAttribute(
|
||||
'pattern',
|
||||
config.userNameRegex + /|^$/.source);
|
||||
}
|
||||
|
||||
if (passwordField) {
|
||||
passwordField.setAttribute(
|
||||
'pattern',
|
||||
config.passwordRegex + /|^$/.source);
|
||||
}
|
||||
|
||||
if (rankField) {
|
||||
rankField.value = ctx.user.rank;
|
||||
}
|
||||
|
||||
/* TODO: avatar */
|
||||
|
||||
form.addEventListener('submit', e => {
|
||||
|
|
Loading…
Reference in a new issue