diff --git a/client/html/user_summary.hbs b/client/html/user_summary.hbs
index f95eb2de..a87a0e5a 100644
--- a/client/html/user_summary.hbs
+++ b/client/html/user_summary.hbs
@@ -1,22 +1,22 @@
- {{thumbnail this.user.avatarUrl}}
+ <%= makeThumbnail(user.avatarUrl) %>
- - Registered: {{reltime this.user.creationTime}}
- - Last seen: {{reltime this.user.lastLoginTime}}
- - Rank: {{toLowerCase this.user.rankName}}
+ - Registered: <%= makeRelativeTime(user.creationTime) %>
+ - Last seen: <%= makeRelativeTime(user.lastLoginTime) %>
+ - Rank: <%= user.rankName.toLowerCase() %>
- {{#if this.isLoggedIn}}
+ <% if (isLoggedIn) { %>
- {{/if}}
+ <% } %>
diff --git a/client/js/util/handlebars-helpers.js b/client/js/util/handlebars-helpers.js
deleted file mode 100644
index d08a1d97..00000000
--- a/client/js/util/handlebars-helpers.js
+++ /dev/null
@@ -1,113 +0,0 @@
-'use strict';
-
-const views = require('../util/views.js');
-const handlebars = require('handlebars');
-const misc = require('./misc.js');
-
-function makeLabel(options, attrs) {
- if (!options.hash.text) {
- return '';
- }
- if (!attrs) {
- attrs = {};
- }
- attrs.for = options.hash.id;
- return views.makeNonVoidElement('label', attrs, options.hash.text);
-}
-
-handlebars.registerHelper('reltime', function(time) {
- return new handlebars.SafeString(
- views.makeNonVoidElement(
- 'time',
- {datetime: time, title: time},
- misc.formatRelativeTime(time)));
-});
-
-handlebars.registerHelper('thumbnail', function(url) {
- return new handlebars.SafeString(
- views.makeNonVoidElement('span', {
- 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',
- checked: options.hash.selectedValue === options.hash.value,
- required: options.hash.required,
- }),
- makeLabel(options, {class: 'radio'})));
-});
-
-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',
- checked: options.hash.checked !== undefined ?
- options.hash.checked : false,
- required: options.hash.required,
- }),
- makeLabel(options, {class: 'checkbox'})));
-});
-
-handlebars.registerHelper('select', function(options) {
- return new handlebars.SafeString('{0}{1}'.format(
- makeLabel(options),
- 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(
- makeLabel(options),
- 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);
-});
-
-handlebars.registerHelper('alignFlexbox', function(options) {
- return new handlebars.SafeString(
- Array.from(misc.range(20))
- .map(() => '
').join(''));
-});
diff --git a/client/js/util/views.js b/client/js/util/views.js
index c7c297fa..09f97935 100644
--- a/client/js/util/views.js
+++ b/client/js/util/views.js
@@ -1,9 +1,114 @@
'use strict';
require('../util/polyfill.js');
-const handlebars = require('handlebars');
+const underscore = require('underscore');
const events = require('../events.js');
const domParser = new DOMParser();
+const misc = require('./misc.js');
+
+function _makeLabel(options, attrs) {
+ if (!options.text) {
+ return '';
+ }
+ if (!attrs) {
+ attrs = {};
+ }
+ attrs.for = options.id;
+ return makeNonVoidElement('label', attrs, options.text);
+}
+
+function makeRelativeTime(time) {
+ return makeNonVoidElement(
+ 'time',
+ {datetime: time, title: time},
+ misc.formatRelativeTime(time));
+}
+
+function makeThumbnail(url) {
+ return makeNonVoidElement(
+ 'span',
+ {
+ class: 'thumbnail',
+ style: 'background-image: url(\'{0}\')'.format(url)
+ },
+ makeVoidElement('img', {alt: 'thumbnail', src: url}));
+}
+
+function makeRadio(options) {
+ return makeVoidElement(
+ 'input',
+ {
+ id: options.id,
+ name: options.name,
+ value: options.value,
+ type: 'radio',
+ checked: options.selectedValue === options.value,
+ required: options.required,
+ }) +
+ _makeLabel(options, {class: 'radio'});
+}
+
+function makeCheckbox(options) {
+ return makeVoidElement(
+ 'input',
+ {
+ id: options.id,
+ name: options.name,
+ value: options.value,
+ type: 'checkbox',
+ checked: options.checked !== undefined ?
+ options.checked : false,
+ required: options.required,
+ }) +
+ _makeLabel(options, {class: 'checkbox'});
+}
+
+function makeSelect(options) {
+ return _makeLabel(options) +
+ makeNonVoidElement(
+ 'select',
+ {id: options.id, name: options.name},
+ Object.keys(options.keyValues).map(key => {
+ return makeNonVoidElement(
+ 'option',
+ {value: key, selected: key === options.selectedKey},
+ options.keyValues[key]);
+ }).join(''));
+}
+
+function makeInput(options) {
+ return _makeLabel(options)
+ + makeVoidElement(
+ 'input', {
+ type: options.inputType,
+ name: options.name,
+ id: options.id,
+ value: options.value || '',
+ required: options.required,
+ pattern: options.pattern,
+ placeholder: options.placeholder,
+ });
+}
+
+function makeTextInput(options) {
+ options.inputType = 'text';
+ return makeInput(options);
+}
+
+function makePasswordInput(options) {
+ options.inputType = 'password';
+ return makeInput(options);
+}
+
+function makeEmailInput(options) {
+ options.inputType = 'email';
+ return makeInput(options);
+}
+
+function makeFlexboxAlign(options) {
+ return Array.from(misc.range(20))
+ .map(() => '
').join('');
+}
function _messageHandler(target, message, className) {
if (!message) {
@@ -81,9 +186,24 @@ function getTemplate(templatePath) {
return null;
}
const templateText = templates[templatePath].trim();
- const templateFactory = handlebars.compile(templateText);
- return (...args) => {
- return htmlToDom(templateFactory(...args));
+ const templateFactory = underscore.template(templateText);
+ return ctx => {
+ if (!ctx) {
+ ctx = {};
+ }
+ underscore.extend(ctx, {
+ makeRelativeTime: makeRelativeTime,
+ makeThumbnail: makeThumbnail,
+ makeRadio: makeRadio,
+ makeCheckbox: makeCheckbox,
+ makeSelect: makeSelect,
+ makeInput: makeInput,
+ makeTextInput: makeTextInput,
+ makePasswordInput: makePasswordInput,
+ makeEmailInput: makeEmailInput,
+ makeFlexboxAlign: makeFlexboxAlign,
+ });
+ return htmlToDom(templateFactory(ctx));
};
}
diff --git a/client/js/views/file_dropper_control.js b/client/js/views/file_dropper_control.js
index 780d240e..45b753ae 100644
--- a/client/js/views/file_dropper_control.js
+++ b/client/js/views/file_dropper_control.js
@@ -10,6 +10,7 @@ class FileDropperControl {
render(ctx) {
const target = ctx.target;
const source = this.template({
+ allowMultiple: ctx.allowMultiple,
id: 'file-' + Math.random().toString(36).substring(7),
});
diff --git a/client/js/views/login_view.js b/client/js/views/login_view.js
index 9a1763cb..10ba67a2 100644
--- a/client/js/views/login_view.js
+++ b/client/js/views/login_view.js
@@ -10,7 +10,11 @@ class LoginView {
render(ctx) {
const target = document.getElementById('content-holder');
- const source = this.template({canSendMails: config.canSendMails});
+ const source = this.template({
+ userNamePattern: config.userNameRegex,
+ passwordPattern: config.passwordRegex,
+ canSendMails: config.canSendMails,
+ });
const form = source.querySelector('form');
const userNameField = source.querySelector('#user-name');
diff --git a/client/js/views/top_nav_view.js b/client/js/views/top_nav_view.js
index 6fa4c8a2..9a1d2796 100644
--- a/client/js/views/top_nav_view.js
+++ b/client/js/views/top_nav_view.js
@@ -1,6 +1,5 @@
'use strict';
-require('../util/handlebars-helpers.js');
const views = require('../util/views.js');
class TopNavView {
diff --git a/client/package.json b/client/package.json
index 76a14ad8..cc62f770 100644
--- a/client/package.json
+++ b/client/package.json
@@ -14,7 +14,6 @@
"camelcase-keys": "*",
"csso": "^1.8.0",
"glob": "^7.0.3",
- "handlebars": "^4.0.5",
"html-minifier": "^1.3.1",
"js-cookie": "^2.1.0",
"js-yaml": "^3.5.5",
@@ -24,7 +23,8 @@
"page": "^1.7.1",
"stylus": "^0.54.2",
"superagent": "^1.8.3",
- "uglify-js": "git://github.com/mishoo/UglifyJS2.git#harmony"
+ "uglify-js": "git://github.com/mishoo/UglifyJS2.git#harmony",
+ "underscore": "^1.8.3"
},
"devDependencies": {
"watch": "latest"