client/general: clean up, refactor

This commit is contained in:
rr- 2016-08-05 20:09:11 +02:00
parent 9304e309f6
commit 1b62daed9a
16 changed files with 283 additions and 169 deletions

View File

@ -4,13 +4,26 @@
<div class='input'> <div class='input'>
<ul> <ul>
<li> <li>
<%= ctx.makeTextInput({text: 'User name', id: 'user-name', name: 'name', required: true, pattern: ctx.userNamePattern}) %> <%= ctx.makeTextInput({
text: 'User name',
name: 'name',
required: true,
pattern: ctx.userNamePattern,
}) %>
</li> </li>
<li> <li>
<%= ctx.makePasswordInput({text: 'Password', id: 'user-password', name: 'password', required: true, pattern: ctx.passwordPattern}) %> <%= ctx.makePasswordInput({
text: 'Password',
name: 'password',
required: true,
pattern: ctx.passwordPattern,
}) %>
</li> </li>
<li> <li>
<%= ctx.makeCheckbox({text: 'Remember me', id: 'remember-user', name: 'remember-user'}) %> <%= ctx.makeCheckbox({
text: 'Remember me',
name: 'remember-user',
}) %>
</li> </li>
</ul> </ul>
</div> </div>

View File

@ -4,7 +4,11 @@
<div class='input'> <div class='input'>
<ul> <ul>
<li> <li>
<%= ctx.makeTextInput({text: 'User name or e-mail address', id: 'user-name', name: 'user-name', required: true}) %> <%= ctx.makeTextInput({
text: 'User name or e-mail address',
name: 'user-name',
required: true,
}) %>
</li> </li>
</ul> </ul>
</div> </div>

View File

@ -2,32 +2,62 @@
<form> <form>
<strong>Browsing settings</strong> <strong>Browsing settings</strong>
<p>These settings are saved to the browser's local storage and are not coupled to the user account, so they don't apply to other devices or browsers alike.</p> <p>These settings are saved to the browser's local storage and are not coupled to the user account, so they don't apply to other devices or browsers alike.</p>
<div class='input'> <ul class='input'>
<ul>
<li> <li>
<%= ctx.makeCheckbox({text: 'Enable keyboard shortcuts', id: 'keyboard-shortcuts', name: 'keyboard-shortcuts', checked: ctx.browsingSettings.keyboardShortcuts}) %> <%= ctx.makeCheckbox({
text: 'Enable keyboard shortcuts',
name: 'keyboard-shortcuts',
checked: ctx.browsingSettings.keyboardShortcuts,
}) %>
<a class='append icon' href='/help/keyboard'><i class='fa fa-question-circle-o'></i></a> <a class='append icon' href='/help/keyboard'><i class='fa fa-question-circle-o'></i></a>
</li> </li>
<li> <li>
<%= ctx.makeNumericInput({text: 'Number of posts per page', id: 'posts-per-page', name: 'posts-per-page', checked: ctx.browsingSettings.postCount, min: 10, max: 100, value: ctx.browsingSettings.postsPerPage}) %> <%= ctx.makeNumericInput({
text: 'Number of posts per page',
name: 'posts-per-page',
checked: ctx.browsingSettings.postCount,
value: ctx.browsingSettings.postsPerPage,
min: 10,
max: 100,
}) %>
</li> </li>
<li> <li>
<%= ctx.makeCheckbox({text: 'Upscale small posts', id: 'upscale-small-posts', name: 'upscale-small-posts', checked: ctx.browsingSettings.upscaleSmallPosts}) %> <%= ctx.makeCheckbox({
text: 'Upscale small posts',
name: 'upscale-small-posts',
checked: ctx.browsingSettings.upscaleSmallPosts}) %>
</li> </li>
<li> <li>
<%= ctx.makeCheckbox({text: 'Endless scroll', id: 'endless-scroll', name: 'endless-scroll', checked: ctx.browsingSettings.endlessScroll}) %> <%= ctx.makeCheckbox({
text: 'Endless scroll',
name: 'endless-scroll',
checked: ctx.browsingSettings.endlessScroll,
}) %>
<p class='hint'>Rather than using a paged navigation, smoothly scrolls through the content.</p> <p class='hint'>Rather than using a paged navigation, smoothly scrolls through the content.</p>
</li> </li>
<li> <li>
<%= ctx.makeCheckbox({text: 'Enable transparency grid', id: 'transparency-grid', name: 'transparency-grid', checked: ctx.browsingSettings.transparencyGrid}) %> <%= ctx.makeCheckbox({
text: 'Enable transparency grid',
name: 'transparency-grid',
checked: ctx.browsingSettings.transparencyGrid,
}) %>
<p class='hint'>Renders a checkered pattern behind posts with transparent background.</p> <p class='hint'>Renders a checkered pattern behind posts with transparent background.</p>
</li> </li>
<li> <li>
<%= ctx.makeCheckbox({text: 'Show tag suggestions', id: 'tag-suggestions', name: 'tag-suggestions', checked: ctx.browsingSettings.tagSuggestions}) %> <%= ctx.makeCheckbox({
text: 'Show tag suggestions',
name: 'tag-suggestions',
checked: ctx.browsingSettings.tagSuggestions,
}) %>
<p class='hint'>Shows a popup with suggested tags in edit forms.</p> <p class='hint'>Shows a popup with suggested tags in edit forms.</p>
</li> </li>
</ul> </ul>
</div>
<div class='messages'></div> <div class='messages'></div>
<div class='buttons'> <div class='buttons'>
<input type='submit' value='Save settings'/> <input type='submit' value='Save settings'/>

View File

@ -4,7 +4,11 @@
<ul> <ul>
<li> <li>
<%= ctx.makeCheckbox({id: 'confirm-deletion', name: 'confirm-deletion', required: true, text: 'I confirm that I want to delete this tag.'}) %> <%= ctx.makeCheckbox({
name: 'confirm-deletion',
text: 'I confirm that I want to delete this tag.',
required: true,
}) %>
</li> </li>
</ul> </ul>

View File

@ -3,7 +3,11 @@
<div class='input'> <div class='input'>
<ul> <ul>
<li> <li>
<%= ctx.makeCheckbox({id: 'confirm-deletion', name: 'confirm-deletion', required: true, text: 'I confirm that I want to delete this account.'}) %> <%= ctx.makeCheckbox({
name: 'confirm-deletion',
text: 'I confirm that I want to delete this account.',
required: true,
}) %>
</li> </li>
</ul> </ul>
</div> </div>

View File

@ -1,27 +1,46 @@
<div id='user-edit'> <div id='user-edit'>
<form> <form>
<ul> <ul class='input'>
<% if (ctx.canEditName) { %> <% if (ctx.canEditName) { %>
<li> <li>
<%= ctx.makeTextInput({text: 'User name', id: 'user-name', name: 'name', value: ctx.user.name, pattern: ctx.userNamePattern}) %> <%= ctx.makeTextInput({
text: 'User name',
name: 'name',
value: ctx.user.name,
pattern: ctx.userNamePattern,
}) %>
</li> </li>
<% } %> <% } %>
<% if (ctx.canEditPassword) { %> <% if (ctx.canEditPassword) { %>
<li> <li>
<%= ctx.makePasswordInput({text: 'Password', id: 'user-password', name: 'password', placeholder: 'leave blank if not changing', pattern: ctx.passwordPattern}) %> <%= ctx.makePasswordInput({
text: 'Password',
name: 'password',
placeholder: 'leave blank if not changing',
pattern: ctx.passwordPattern,
}) %>
</li> </li>
<% } %> <% } %>
<% if (ctx.canEditEmail) { %> <% if (ctx.canEditEmail) { %>
<li> <li>
<%= ctx.makeEmailInput({text: 'Email', id: 'user-email', name: 'email', value: ctx.user.email}) %> <%= ctx.makeEmailInput({
text: 'Email',
name: 'email',
value: ctx.user.email,
}) %>
</li> </li>
<% } %> <% } %>
<% if (ctx.canEditRank) { %> <% if (ctx.canEditRank) { %>
<li> <li>
<%= ctx.makeSelect({text: 'Rank', id: 'user-rank', name: 'rank', keyValues: ctx.ranks, selectedKey: ctx.user.rank}) %> <%= ctx.makeSelect({
text: 'Rank',
name: 'rank',
keyValues: ctx.ranks,
selectedKey: ctx.user.rank,
}) %>
</li> </li>
<% } %> <% } %>
@ -30,8 +49,19 @@
<label>Avatar</label> <label>Avatar</label>
<div id='avatar-content'></div> <div id='avatar-content'></div>
<div id='avatar-radio'> <div id='avatar-radio'>
<%= ctx.makeRadio({text: 'Gravatar', id: 'gravatar-radio', name: 'avatar-style', value: 'gravatar', selectedValue: ctx.user.avatarStyle}) %> <%= ctx.makeRadio({
<%= ctx.makeRadio({text: 'Manual avatar', id: 'avatar-radio', name: 'avatar-style', value: 'manual', selectedValue: ctx.user.avatarStyle}) %> text: 'Gravatar',
name: 'avatar-style',
value: 'gravatar',
selectedValue: ctx.user.avatarStyle,
}) %>
<%= ctx.makeRadio({
text: 'Manual avatar',
name: 'avatar-style',
value: 'manual',
selectedValue: ctx.user.avatarStyle,
}) %>
</div> </div>
</li> </li>
<% } %> <% } %>

View File

@ -4,14 +4,33 @@
<div class='input'> <div class='input'>
<ul> <ul>
<li> <li>
<%= ctx.makeTextInput({text: 'User name', id: 'user-name', name: 'user-name', placeholder: 'letters, digits, _, -', required: true, pattern: ctx.userNamePattern}) %> <%= ctx.makeTextInput({
text: 'User name',
name: 'name',
placeholder: 'letters, digits, _, -',
required: true,
pattern: ctx.userNamePattern,
}) %>
</li> </li>
<li> <li>
<%= ctx.makePasswordInput({text: 'Password', id: 'user-password', name: 'user-password', placeholder: '5+ characters', required: true, pattern: ctx.passwordPattern}) %> <%= ctx.makePasswordInput({
text: 'Password',
name: 'password',
placeholder: '5+ characters',
required: true,
pattern: ctx.passwordPattern,
}) %>
</li> </li>
<li> <li>
<%= ctx.makeEmailInput({text: 'Email', id: 'user-email', name: 'user-email', placeholder: 'optional'}) %> <%= ctx.makeEmailInput({
<p class='hint'>Used for password reminder and to show a <a href='http://gravatar.com/'>Gravatar</a>. Leave blank for random Gravatar.</p> text: 'Email',
name: '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> </li>
</ul> </ul>
</div> </div>

View File

@ -16,7 +16,7 @@ class SettingsController {
_evtChange(e) { _evtChange(e) {
this._view.clearMessages(); this._view.clearMessages();
settings.save(e.detail.settings); settings.save(e.detail);
this._view.showSuccess('Settings saved.'); this._view.showSuccess('Settings saved.');
} }
}; };

View File

@ -111,14 +111,14 @@ function getSuggestions(tagName) {
return actualTag.suggestions || []; return actualTag.suggestions || [];
} }
module.exports = { module.exports = misc.arrayToObject([
getAllCategories: getAllCategories, getAllCategories,
getAllTags: getAllTags, getAllTags,
getTagByName: getTagByName, getTagByName,
getCategoryByName: getCategoryByName, getCategoryByName,
getNameToTagMap: getNameToTagMap, getNameToTagMap,
getOriginalTagName: getOriginalTagName, getOriginalTagName,
refreshExport: refreshExport, refreshExport,
getAllImplications: getAllImplications, getAllImplications,
getSuggestions: getSuggestions, getSuggestions,
}; ], func => func.name);

View File

@ -260,21 +260,35 @@ function arraysDiffer(source1, source2) {
source2.filter(value => !source1.includes(value)).length > 0); source2.filter(value => !source1.includes(value)).length > 0);
} }
module.exports = { function arrayToObject(array, keySelector, valueSelector) {
range: range, if (keySelector === undefined) {
formatUrlParameters: formatUrlParameters, keySelector = item => item;
parseUrlParameters: parseUrlParameters, }
parseUrlParametersRoute: parseUrlParametersRoute, if (valueSelector === undefined) {
formatRelativeTime: formatRelativeTime, valueSelector = item => item;
formatFileSize: formatFileSize, }
formatMarkdown: formatMarkdown, return array.reduce((obj, item) => {
unindent: unindent, obj[keySelector(item)] = valueSelector(item);
enableExitConfirmation: enableExitConfirmation, return obj;
disableExitConfirmation: disableExitConfirmation, }, {});
confirmPageExit: confirmPageExit, }
escapeHtml: escapeHtml,
makeCssName: makeCssName, module.exports = arrayToObject([
splitByWhitespace: splitByWhitespace, range,
arraysDiffer: arraysDiffer, formatUrlParameters,
decamelize: decamelize, parseUrlParameters,
}; parseUrlParametersRoute,
formatRelativeTime,
formatFileSize,
formatMarkdown,
unindent,
enableExitConfirmation,
disableExitConfirmation,
confirmPageExit,
escapeHtml,
makeCssName,
splitByWhitespace,
arraysDiffer,
decamelize,
arrayToObject,
], func => func.name);

View File

@ -204,7 +204,7 @@ function makeUserLink(user) {
} }
function makeFlexboxAlign(options) { function makeFlexboxAlign(options) {
return Array.from(misc.range(20)) return [...misc.range(20)]
.map(() => '<li class="flexbox-dummy"></li>').join(''); .map(() => '<li class="flexbox-dummy"></li>').join('');
} }
@ -326,31 +326,31 @@ function getTemplate(templatePath) {
if (!ctx) { if (!ctx) {
ctx = {}; ctx = {};
} }
Object.assign(ctx, { Object.assign(ctx, misc.arrayToObject([
getPostUrl: getPostUrl, getPostUrl,
getPostEditUrl: getPostEditUrl, getPostEditUrl,
makeRelativeTime: makeRelativeTime, makeRelativeTime,
makeFileSize: makeFileSize, makeFileSize,
makeMarkdown: makeMarkdown, makeMarkdown,
makeThumbnail: makeThumbnail, makeThumbnail,
makeRadio: makeRadio, makeRadio,
makeCheckbox: makeCheckbox, makeCheckbox,
makeSelect: makeSelect, makeSelect,
makeInput: makeInput, makeInput,
makeButton: makeButton, makeButton,
makeTextarea: makeTextarea, makeTextarea,
makeTextInput: makeTextInput, makeTextInput,
makePasswordInput: makePasswordInput, makePasswordInput,
makeEmailInput: makeEmailInput, makeEmailInput,
makeColorInput: makeColorInput, makeColorInput,
makePostLink: makePostLink, makePostLink,
makeTagLink: makeTagLink, makeTagLink,
makeUserLink: makeUserLink, makeUserLink,
makeFlexboxAlign: makeFlexboxAlign, makeFlexboxAlign,
makeAccessKey: makeAccessKey, makeAccessKey,
makeCssName: misc.makeCssName, misc.makeCssName,
makeNumericInput: makeNumericInput, makeNumericInput,
}); ], func => func.name));
return htmlToDom(templateFactory(ctx)); return htmlToDom(templateFactory(ctx));
}; };
} }
@ -389,7 +389,7 @@ function replaceContent(target, source) {
target.removeChild(target.lastChild); target.removeChild(target.lastChild);
} }
if (source instanceof NodeList) { if (source instanceof NodeList) {
for (let child of Array.from(source)) { for (let child of [...source]) {
target.appendChild(child); target.appendChild(child);
} }
} else if (source instanceof Node) { } else if (source instanceof Node) {
@ -468,21 +468,21 @@ document.addEventListener('input', e => {
} }
}); });
module.exports = { module.exports = misc.arrayToObject([
htmlToDom: htmlToDom, htmlToDom,
getTemplate: getTemplate, getTemplate,
replaceContent: replaceContent, replaceContent,
enableForm: enableForm, enableForm,
disableForm: disableForm, disableForm,
decorateValidator: decorateValidator, decorateValidator,
makeVoidElement: makeVoidElement, makeVoidElement,
makeNonVoidElement: makeNonVoidElement, makeNonVoidElement,
syncScrollPosition: syncScrollPosition, syncScrollPosition,
slideDown: slideDown, slideDown,
slideUp: slideUp, slideUp,
monitorNodeRemoval: monitorNodeRemoval, monitorNodeRemoval,
clearMessages: clearMessages, clearMessages,
showError: showError, showError,
showSuccess: showSuccess, showSuccess,
showInfo: showInfo, showInfo,
}; ], func => func.name);

View File

@ -19,15 +19,15 @@ class LoginView extends events.EventTarget {
views.syncScrollPosition(); views.syncScrollPosition();
views.decorateValidator(this._formNode); views.decorateValidator(this._formNode);
this._userNameFieldNode.setAttribute('pattern', config.userNameRegex); this._userNameInputNode.setAttribute('pattern', config.userNameRegex);
this._passwordFieldNode.setAttribute('pattern', config.passwordRegex); this._passwordInputNode.setAttribute('pattern', config.passwordRegex);
this._formNode.addEventListener('submit', e => { this._formNode.addEventListener('submit', e => {
e.preventDefault(); e.preventDefault();
this.dispatchEvent(new CustomEvent('submit', { this.dispatchEvent(new CustomEvent('submit', {
detail: { detail: {
name: this._userNameFieldNode.value, name: this._userNameInputNode.value,
password: this._passwordFieldNode.value, password: this._passwordInputNode.value,
remember: this._rememberFieldNode.checked, remember: this._rememberInputNode.checked,
}, },
})); }));
}); });
@ -37,16 +37,16 @@ class LoginView extends events.EventTarget {
return this._hostNode.querySelector('form'); return this._hostNode.querySelector('form');
} }
get _userNameFieldNode() { get _userNameInputNode() {
return this._formNode.querySelector('#user-name'); return this._formNode.querySelector('[name=name]');
} }
get _passwordFieldNode() { get _passwordInputNode() {
return this._formNode.querySelector('#user-password'); return this._formNode.querySelector('[name=password]');
} }
get _rememberFieldNode() { get _rememberInputNode() {
return this._formNode.querySelector('#remember-user'); return this._formNode.querySelector('[name=remember-user]');
} }
disableForm() { disableForm() {

View File

@ -49,7 +49,7 @@ class PasswordResetView extends events.EventTarget {
} }
get _userNameOrEmailFieldNode() { get _userNameOrEmailFieldNode() {
return this._formNode.querySelector('#user-name'); return this._formNode.querySelector('[name=user-name]');
} }
} }

View File

@ -51,15 +51,15 @@ class RegistrationView extends events.EventTarget {
} }
get _userNameFieldNode() { get _userNameFieldNode() {
return this._formNode.querySelector('#user-name'); return this._formNode.querySelector('[name=name]');
} }
get _passwordFieldNode() { get _passwordFieldNode() {
return this._formNode.querySelector('#user-password'); return this._formNode.querySelector('[name=password]');
} }
get _emailFieldNode() { get _emailFieldNode() {
return this._formNode.querySelector('#user-email'); return this._formNode.querySelector('[name=email]');
} }
} }

View File

@ -30,20 +30,12 @@ class SettingsView extends events.EventTarget {
e.preventDefault(); e.preventDefault();
this.dispatchEvent(new CustomEvent('change', { this.dispatchEvent(new CustomEvent('change', {
detail: { detail: {
settings: { upscaleSmallPosts: this._find('upscale-small-posts').checked,
upscaleSmallPosts: this._formNode.querySelector( endlessScroll: this._find('endless-scroll').checked,
'#upscale-small-posts').checked, keyboardShortcuts: this._find('keyboard-shortcuts').checked,
endlessScroll: this._formNode.querySelector( transparencyGrid: this._find('transparency-grid').checked,
'#endless-scroll').checked, tagSuggestions: this._find('tag-suggestions').checked,
keyboardShortcuts: this._formNode.querySelector( postsPerPage: this._find('posts-per-page').value,
'#keyboard-shortcuts').checked,
transparencyGrid: this._formNode.querySelector(
'#transparency-grid').checked,
tagSuggestions: this._formNode.querySelector(
'#tag-suggestions').checked,
postsPerPage: this._formNode.querySelector(
'#posts-per-page').value,
},
}, },
})); }));
} }
@ -51,6 +43,10 @@ class SettingsView extends events.EventTarget {
get _formNode() { get _formNode() {
return this._hostNode.querySelector('form'); return this._hostNode.querySelector('form');
} }
_find(nodeName) {
return this._formNode.querySelector('[name=' + nodeName + ']');
}
} }
module.exports = SettingsView; module.exports = SettingsView;

View File

@ -20,9 +20,9 @@ class UserEditView extends events.EventTarget {
views.decorateValidator(this._formNode); views.decorateValidator(this._formNode);
this._avatarContent = null; this._avatarContent = null;
if (this._avatarContentFieldNode) { if (this._avatarContentInputNode) {
new FileDropperControl( new FileDropperControl(
this._avatarContentFieldNode, this._avatarContentInputNode,
{ {
lock: true, lock: true,
resolve: files => { resolve: files => {
@ -62,24 +62,24 @@ class UserEditView extends events.EventTarget {
detail: { detail: {
user: this._user, user: this._user,
name: this._userNameFieldNode ? name: this._userNameInputNode ?
this._userNameFieldNode.value : this._userNameInputNode.value :
undefined, undefined,
email: this._emailFieldNode ? email: this._emailInputNode ?
this._emailFieldNode.value : this._emailInputNode.value :
undefined, undefined,
rank: this._rankFieldNode ? rank: this._rankInputNode ?
this._rankFieldNode.value : this._rankInputNode.value :
undefined, undefined,
avatarStyle: this._avatarStyleFieldNode ? avatarStyle: this._avatarStyleInputNode ?
this._avatarStyleFieldNode.value : this._avatarStyleInputNode.value :
undefined, undefined,
password: this._passwordFieldNode ? password: this._passwordInputNode ?
this._passwordFieldNode.value : this._passwordInputNode.value :
undefined, undefined,
avatarContent: this._avatarContent, avatarContent: this._avatarContent,
@ -91,27 +91,27 @@ class UserEditView extends events.EventTarget {
return this._hostNode.querySelector('form'); return this._hostNode.querySelector('form');
} }
get _rankFieldNode() { get _rankInputNode() {
return this._formNode.querySelector('#user-rank'); return this._formNode.querySelector('[name=rank]');
} }
get _emailFieldNode() { get _emailInputNode() {
return this._formNode.querySelector('#user-email'); return this._formNode.querySelector('[name=email]');
} }
get _userNameFieldNode() { get _userNameInputNode() {
return this._formNode.querySelector('#user-name'); return this._formNode.querySelector('[name=name]');
} }
get _passwordFieldNode() { get _passwordInputNode() {
return this._formNode.querySelector('#user-password'); return this._formNode.querySelector('[name=password]');
} }
get _avatarContentFieldNode() { get _avatarContentInputNode() {
return this._formNode.querySelector('#avatar-content'); return this._formNode.querySelector('#avatar-content');
} }
get _avatarStyleFieldNode() { get _avatarStyleInputNode() {
return this._formNode.querySelector('[name=avatar-style]:checked'); return this._formNode.querySelector('[name=avatar-style]:checked');
} }
} }