diff --git a/client/css/forms.styl b/client/css/forms.styl
index dc05c80..b0c3b49 100644
--- a/client/css/forms.styl
+++ b/client/css/forms.styl
@@ -137,7 +137,8 @@ select,
textarea,
input[type=text],
input[type=email],
-input[type=password]
+input[type=password],
+input[type=number]
vertical-align: top
font-family: 'Droid Sans', sans-serif
font-size: 100%
diff --git a/client/html/settings.tpl b/client/html/settings.tpl
index dd453e5..743d6cd 100644
--- a/client/html/settings.tpl
+++ b/client/html/settings.tpl
@@ -8,6 +8,9 @@
<%= ctx.makeCheckbox({text: 'Enable keyboard shortcuts', id: 'keyboard-shortcuts', name: 'keyboard-shortcuts', checked: ctx.browsingSettings.keyboardShortcuts}) %>
+
+ <%= 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.makeCheckbox({text: 'Upscale small posts', id: 'upscale-small-posts', name: 'upscale-small-posts', checked: ctx.browsingSettings.upscaleSmallPosts}) %>
diff --git a/client/js/controllers/post_list_controller.js b/client/js/controllers/post_list_controller.js
index 0ab6173..3da718d 100644
--- a/client/js/controllers/post_list_controller.js
+++ b/client/js/controllers/post_list_controller.js
@@ -29,7 +29,7 @@ class PostListController {
requestPage: page => {
return PostList.search(
this._decorateSearchQuery(ctx.parameters.query),
- page, 40, fields);
+ page, settings.get().postsPerPage, fields);
},
headerRenderer: headerCtx => {
Object.assign(headerCtx, {
diff --git a/client/js/models/settings.js b/client/js/models/settings.js
index 1324e6a..6e49332 100644
--- a/client/js/models/settings.js
+++ b/client/js/models/settings.js
@@ -14,6 +14,7 @@ const defaultSettings = {
transparencyGrid: true,
fitMode: 'fit-both',
tagSuggestions: true,
+ postsPerPage: 40,
};
class Settings extends events.EventTarget {
diff --git a/client/js/util/misc.js b/client/js/util/misc.js
index 21647c3..5c05d73 100644
--- a/client/js/util/misc.js
+++ b/client/js/util/misc.js
@@ -244,7 +244,7 @@ function makeCssName(text, suffix) {
}
function escapeHtml(unsafe) {
- return unsafe
+ return unsafe.toString()
.replace(/&/g, '&')
.replace(//g, '>')
diff --git a/client/js/util/views.js b/client/js/util/views.js
index 41fde35..49a62c7 100644
--- a/client/js/util/views.js
+++ b/client/js/util/views.js
@@ -148,6 +148,11 @@ function makeColorInput(options) {
'label', {class: 'color'}, colorInput + textInput);
}
+function makeNumericInput(options) {
+ options.type = 'number';
+ return makeInput(options);
+}
+
function getPostUrl(id, parameters) {
let url = '/post/' + encodeURIComponent(id);
if (parameters && parameters.query) {
@@ -344,6 +349,7 @@ function getTemplate(templatePath) {
makeFlexboxAlign: makeFlexboxAlign,
makeAccessKey: makeAccessKey,
makeCssName: misc.makeCssName,
+ makeNumericInput: makeNumericInput,
});
return htmlToDom(templateFactory(ctx));
};
diff --git a/client/js/views/settings_view.js b/client/js/views/settings_view.js
index b8e3555..6b534c1 100644
--- a/client/js/views/settings_view.js
+++ b/client/js/views/settings_view.js
@@ -41,6 +41,8 @@ class SettingsView extends events.EventTarget {
'#transparency-grid').checked,
tagSuggestions: this._formNode.querySelector(
'#tag-suggestions').checked,
+ postsPerPage: this._formNode.querySelector(
+ '#posts-per-page').value,
},
},
}));