nodebb-plugin-composer-default 10.3.20 → 10.3.22
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/library.js +3 -2
- package/package.json +3 -3
- package/static/lib/client.js +1 -1
- package/static/lib/composer/autocomplete.js +9 -9
- package/static/lib/composer/categoryList.js +18 -13
- package/static/lib/composer/controls.js +19 -20
- package/static/lib/composer/drafts.js +5 -2
- package/static/lib/composer/formatting.js +11 -11
- package/static/lib/composer/preview.js +6 -6
- package/static/lib/composer/resize.js +36 -36
- package/static/lib/composer/tags.js +15 -15
- package/static/lib/composer/uploads.js +27 -28
- package/static/lib/composer.js +57 -60
- package/static/templates/composer.tpl +1 -1
package/library.js
CHANGED
|
@@ -241,7 +241,7 @@ function generateDiscardRoute(req, topicData) {
|
|
|
241
241
|
}
|
|
242
242
|
|
|
243
243
|
async function generateBody(req, postData) {
|
|
244
|
-
let body
|
|
244
|
+
let body;
|
|
245
245
|
// Quoted reply
|
|
246
246
|
if (req.query.toPid && parseInt(req.query.quoted, 10) === 1 && postData) {
|
|
247
247
|
const username = await user.getUserField(postData.uid, 'username');
|
|
@@ -250,8 +250,9 @@ async function generateBody(req, postData) {
|
|
|
250
250
|
`> ${postData ? `${postData.content.replace(/\n/g, '\n> ')}\n\n` : ''}`;
|
|
251
251
|
} else if (req.query.body || req.query.content) {
|
|
252
252
|
body = validator.escape(String(req.query.body || req.query.content));
|
|
253
|
+
} else {
|
|
254
|
+
body = postData ? postData.content : '';
|
|
253
255
|
}
|
|
254
|
-
body = postData ? postData.content : '';
|
|
255
256
|
return translator.escape(body);
|
|
256
257
|
}
|
|
257
258
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "nodebb-plugin-composer-default",
|
|
3
|
-
"version": "10.3.
|
|
3
|
+
"version": "10.3.22",
|
|
4
4
|
"description": "Default composer for NodeBB",
|
|
5
5
|
"main": "library.js",
|
|
6
6
|
"repository": {
|
|
@@ -33,8 +33,8 @@
|
|
|
33
33
|
"validator": "^13.7.0"
|
|
34
34
|
},
|
|
35
35
|
"devDependencies": {
|
|
36
|
-
"eslint": "^
|
|
37
|
-
"eslint-config-nodebb": "^
|
|
36
|
+
"eslint": "^10.0.0",
|
|
37
|
+
"eslint-config-nodebb": "^2.0.1",
|
|
38
38
|
"eslint-plugin-import": "^2.31.0"
|
|
39
39
|
}
|
|
40
40
|
}
|
package/static/lib/client.js
CHANGED
|
@@ -74,7 +74,7 @@ $(document).ready(function () {
|
|
|
74
74
|
data.title = data.title || data.topicName;
|
|
75
75
|
if (config['composer-default'].composeRouteEnabled !== 'on') {
|
|
76
76
|
require(['composer'], function (composer) {
|
|
77
|
-
|
|
77
|
+
const topicUUID = composer.findByTid(data.tid);
|
|
78
78
|
composer.addQuote({
|
|
79
79
|
tid: data.tid,
|
|
80
80
|
toPid: data.pid,
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
define('composer/autocomplete', [
|
|
4
4
|
'composer/preview', 'autocomplete',
|
|
5
5
|
], function (preview, Autocomplete) {
|
|
6
|
-
|
|
6
|
+
const autocomplete = {
|
|
7
7
|
_active: {},
|
|
8
8
|
};
|
|
9
9
|
|
|
@@ -15,9 +15,9 @@ define('composer/autocomplete', [
|
|
|
15
15
|
});
|
|
16
16
|
|
|
17
17
|
autocomplete.init = function (postContainer, post_uuid) {
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
18
|
+
const element = postContainer.find('.write');
|
|
19
|
+
const dropdownClass = 'composer-autocomplete-dropdown-' + post_uuid;
|
|
20
|
+
let timer;
|
|
21
21
|
|
|
22
22
|
if (!element.length) {
|
|
23
23
|
/**
|
|
@@ -29,7 +29,7 @@ define('composer/autocomplete', [
|
|
|
29
29
|
return;
|
|
30
30
|
}
|
|
31
31
|
|
|
32
|
-
|
|
32
|
+
const data = {
|
|
33
33
|
element: element,
|
|
34
34
|
strategies: [],
|
|
35
35
|
options: {
|
|
@@ -46,13 +46,13 @@ define('composer/autocomplete', [
|
|
|
46
46
|
element.on('keyup', function () {
|
|
47
47
|
clearTimeout(timer);
|
|
48
48
|
timer = setTimeout(function () {
|
|
49
|
-
|
|
49
|
+
const dropdown = document.querySelector('.' + dropdownClass);
|
|
50
50
|
if (dropdown) {
|
|
51
|
-
|
|
51
|
+
const pos = dropdown.getBoundingClientRect();
|
|
52
52
|
|
|
53
|
-
|
|
53
|
+
const margin = parseFloat(dropdown.style.marginTop, 10) || 0;
|
|
54
54
|
|
|
55
|
-
|
|
55
|
+
const offset = window.innerHeight + margin - 10 - pos.bottom;
|
|
56
56
|
dropdown.style.marginTop = Math.min(offset, 0) + 'px';
|
|
57
57
|
}
|
|
58
58
|
}, 0);
|
|
@@ -3,12 +3,13 @@
|
|
|
3
3
|
define('composer/categoryList', [
|
|
4
4
|
'categorySelector', 'taskbar', 'api',
|
|
5
5
|
], function (categorySelector, taskbar, api) {
|
|
6
|
-
|
|
6
|
+
const categoryList = {};
|
|
7
7
|
|
|
8
|
-
|
|
8
|
+
let selector;
|
|
9
|
+
let mobileSelector;
|
|
9
10
|
|
|
10
11
|
categoryList.init = function (postContainer, postData) {
|
|
11
|
-
|
|
12
|
+
const listContainer = postContainer.find('.category-list-container');
|
|
12
13
|
if (!listContainer.length) {
|
|
13
14
|
return;
|
|
14
15
|
}
|
|
@@ -25,31 +26,35 @@ define('composer/categoryList', [
|
|
|
25
26
|
onSelect: function (selectedCategory) {
|
|
26
27
|
if (postData.hasOwnProperty('cid')) {
|
|
27
28
|
changeCategory(postContainer, postData, selectedCategory);
|
|
29
|
+
mobileSelector.selectCategory(selectedCategory.cid, selectedCategory.categoryEl);
|
|
28
30
|
}
|
|
29
31
|
},
|
|
30
32
|
});
|
|
31
33
|
if (!selector) {
|
|
32
34
|
return;
|
|
33
35
|
}
|
|
34
|
-
if (postData.cid && postData.category) {
|
|
35
|
-
selector.selectedCategory = { cid: postData.cid, name: postData.category.name };
|
|
36
|
-
} else if (ajaxify.data.template.compose && ajaxify.data.selectedCategory) {
|
|
37
|
-
// separate composer route
|
|
38
|
-
selector.selectedCategory = { cid: ajaxify.data.cid, name: ajaxify.data.selectedCategory };
|
|
39
|
-
}
|
|
40
|
-
|
|
41
36
|
// this is the mobile category selector
|
|
42
|
-
categorySelector.init(
|
|
37
|
+
mobileSelector = categorySelector.init(
|
|
43
38
|
postContainer.find('.mobile-navbar [component="category-selector"]'), {
|
|
44
39
|
privilege: 'topics:create',
|
|
45
40
|
states: ['watching', 'tracking', 'notwatching', 'ignoring'],
|
|
46
41
|
onSelect: function (selectedCategory) {
|
|
47
42
|
if (postData.hasOwnProperty('cid')) {
|
|
48
43
|
changeCategory(postContainer, postData, selectedCategory);
|
|
44
|
+
selector.selectCategory(selectedCategory.cid, selectedCategory.categoryEl);
|
|
49
45
|
}
|
|
50
46
|
},
|
|
51
47
|
});
|
|
52
48
|
|
|
49
|
+
if (postData.cid && postData.category) {
|
|
50
|
+
selector.selectedCategory = { cid: postData.cid, name: postData.category.name };
|
|
51
|
+
mobileSelector.selectedCategory = { cid: postData.cid, name: postData.category.name };
|
|
52
|
+
} else if (ajaxify.data.template.compose && ajaxify.data.selectedCategory) {
|
|
53
|
+
// separate composer route
|
|
54
|
+
selector.selectedCategory = { cid: ajaxify.data.cid, name: ajaxify.data.selectedCategory };
|
|
55
|
+
mobileSelector.selectedCategory = { cid: ajaxify.data.cid, name: ajaxify.data.selectedCategory };
|
|
56
|
+
}
|
|
57
|
+
|
|
53
58
|
toggleDropDirection(postContainer);
|
|
54
59
|
};
|
|
55
60
|
|
|
@@ -58,7 +63,7 @@ define('composer/categoryList', [
|
|
|
58
63
|
}
|
|
59
64
|
|
|
60
65
|
categoryList.getSelectedCid = function () {
|
|
61
|
-
|
|
66
|
+
let selectedCategory;
|
|
62
67
|
if (selector) {
|
|
63
68
|
selectedCategory = selector.getSelectedCategory();
|
|
64
69
|
}
|
|
@@ -75,7 +80,7 @@ define('composer/categoryList', [
|
|
|
75
80
|
|
|
76
81
|
function updateTaskbarByCategory(postContainer, category) {
|
|
77
82
|
if (category) {
|
|
78
|
-
|
|
83
|
+
const uuid = postContainer.attr('data-uuid');
|
|
79
84
|
taskbar.update('composer', uuid, {
|
|
80
85
|
image: category.backgroundImage,
|
|
81
86
|
color: category.color,
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
3
|
define('composer/controls', ['composer/preview'], function (preview) {
|
|
4
|
-
|
|
4
|
+
const controls = {};
|
|
5
5
|
|
|
6
6
|
/** ********************************************** */
|
|
7
7
|
/* Rich Textarea Controls */
|
|
8
8
|
/** ********************************************** */
|
|
9
9
|
controls.insertIntoTextarea = function (textarea, value) {
|
|
10
|
-
|
|
10
|
+
const payload = {
|
|
11
11
|
context: this,
|
|
12
12
|
textarea: textarea,
|
|
13
13
|
value: value,
|
|
@@ -19,9 +19,9 @@ define('composer/controls', ['composer/preview'], function (preview) {
|
|
|
19
19
|
return;
|
|
20
20
|
}
|
|
21
21
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
22
|
+
const $textarea = $(payload.textarea);
|
|
23
|
+
const currentVal = $textarea.val();
|
|
24
|
+
const postContainer = $textarea.parents('[component="composer"]');
|
|
25
25
|
|
|
26
26
|
$textarea.val(
|
|
27
27
|
currentVal.slice(0, payload.textarea.selectionStart) +
|
|
@@ -33,7 +33,7 @@ define('composer/controls', ['composer/preview'], function (preview) {
|
|
|
33
33
|
};
|
|
34
34
|
|
|
35
35
|
controls.replaceSelectionInTextareaWith = function (textarea, value) {
|
|
36
|
-
|
|
36
|
+
const payload = {
|
|
37
37
|
context: this,
|
|
38
38
|
textarea: textarea,
|
|
39
39
|
value: value,
|
|
@@ -45,9 +45,9 @@ define('composer/controls', ['composer/preview'], function (preview) {
|
|
|
45
45
|
return;
|
|
46
46
|
}
|
|
47
47
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
48
|
+
const $textarea = $(payload.textarea);
|
|
49
|
+
const currentVal = $textarea.val();
|
|
50
|
+
const postContainer = $textarea.parents('[component="composer"]');
|
|
51
51
|
|
|
52
52
|
$textarea.val(
|
|
53
53
|
currentVal.slice(0, payload.textarea.selectionStart) +
|
|
@@ -59,7 +59,7 @@ define('composer/controls', ['composer/preview'], function (preview) {
|
|
|
59
59
|
};
|
|
60
60
|
|
|
61
61
|
controls.wrapSelectionInTextareaWith = function (textarea, leading, trailing) {
|
|
62
|
-
|
|
62
|
+
const payload = {
|
|
63
63
|
context: this,
|
|
64
64
|
textarea: textarea,
|
|
65
65
|
leading: leading,
|
|
@@ -76,10 +76,10 @@ define('composer/controls', ['composer/preview'], function (preview) {
|
|
|
76
76
|
trailing = leading;
|
|
77
77
|
}
|
|
78
78
|
|
|
79
|
-
|
|
80
|
-
|
|
79
|
+
const $textarea = $(textarea);
|
|
80
|
+
const currentVal = $textarea.val();
|
|
81
81
|
|
|
82
|
-
|
|
82
|
+
let matches = /^(\s*)([\s\S]*?)(\s*)$/.exec(currentVal.slice(textarea.selectionStart, textarea.selectionEnd));
|
|
83
83
|
|
|
84
84
|
if (!matches[2]) {
|
|
85
85
|
// selection is entirely whitespace
|
|
@@ -100,7 +100,7 @@ define('composer/controls', ['composer/preview'], function (preview) {
|
|
|
100
100
|
};
|
|
101
101
|
|
|
102
102
|
controls.updateTextareaSelection = function (textarea, start, end) {
|
|
103
|
-
|
|
103
|
+
const payload = {
|
|
104
104
|
context: this,
|
|
105
105
|
textarea: textarea,
|
|
106
106
|
start: start,
|
|
@@ -119,12 +119,11 @@ define('composer/controls', ['composer/preview'], function (preview) {
|
|
|
119
119
|
|
|
120
120
|
controls.getBlockData = function (textareaEl, query, selectionStart) {
|
|
121
121
|
// Determines whether the cursor is sitting inside a block-type element (bold, italic, etc.)
|
|
122
|
-
|
|
122
|
+
let value = textareaEl.value;
|
|
123
123
|
query = query.replace(/[-[\]/{}()*+?.\\^$|]/g, '\\$&');
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
var payload;
|
|
124
|
+
const regex = new RegExp(query, 'g');
|
|
125
|
+
let match;
|
|
126
|
+
const matchIndices = [];
|
|
128
127
|
|
|
129
128
|
// Isolate the line the cursor is on
|
|
130
129
|
value = value.split('\n').reduce(function (memo, line) {
|
|
@@ -146,7 +145,7 @@ define('composer/controls', ['composer/preview'], function (preview) {
|
|
|
146
145
|
matchIndices.push(match.index);
|
|
147
146
|
}
|
|
148
147
|
|
|
149
|
-
payload = {
|
|
148
|
+
const payload = {
|
|
150
149
|
in: !!(matchIndices.reduce(function (memo, cur) {
|
|
151
150
|
if (selectionStart >= cur + 2) {
|
|
152
151
|
memo += 1;
|
|
@@ -259,6 +259,7 @@ define('composer/drafts', ['api', 'alerts'], function (api, alerts) {
|
|
|
259
259
|
require(['composer'], function (composer) {
|
|
260
260
|
if (draft.action === 'topics.post') {
|
|
261
261
|
composer.newTopic({
|
|
262
|
+
fromDraft: true,
|
|
262
263
|
save_id: draft.save_id,
|
|
263
264
|
cid: draft.cid,
|
|
264
265
|
handle: app.user && app.user.uid ? undefined : utils.escapeHTML(draft.handle),
|
|
@@ -274,6 +275,7 @@ define('composer/drafts', ['api', 'alerts'], function (api, alerts) {
|
|
|
274
275
|
}
|
|
275
276
|
|
|
276
277
|
composer.newReply({
|
|
278
|
+
fromDraft: true,
|
|
277
279
|
save_id: draft.save_id,
|
|
278
280
|
tid: draft.tid,
|
|
279
281
|
toPid: draft.toPid,
|
|
@@ -283,6 +285,7 @@ define('composer/drafts', ['api', 'alerts'], function (api, alerts) {
|
|
|
283
285
|
});
|
|
284
286
|
} else if (draft.action === 'posts.edit') {
|
|
285
287
|
composer.editPost({
|
|
288
|
+
fromDraft: true,
|
|
286
289
|
save_id: draft.save_id,
|
|
287
290
|
pid: draft.pid,
|
|
288
291
|
title: draft.title ? utils.escapeHTML(draft.title) : undefined,
|
|
@@ -295,10 +298,10 @@ define('composer/drafts', ['api', 'alerts'], function (api, alerts) {
|
|
|
295
298
|
|
|
296
299
|
// Feature detection courtesy of: https://developer.mozilla.org/en-US/docs/Web/API/Web_Storage_API/Using_the_Web_Storage_API
|
|
297
300
|
function canSave(type) {
|
|
298
|
-
|
|
301
|
+
let storage;
|
|
299
302
|
try {
|
|
300
303
|
storage = window[type];
|
|
301
|
-
|
|
304
|
+
const x = '__storage_test__';
|
|
302
305
|
storage.setItem(x, x);
|
|
303
306
|
storage.removeItem(x);
|
|
304
307
|
return true;
|
|
@@ -3,18 +3,18 @@
|
|
|
3
3
|
define('composer/formatting', [
|
|
4
4
|
'composer/preview', 'composer/resize', 'topicThumbs', 'screenfull',
|
|
5
5
|
], function (preview, resize, topicThumbs, screenfull) {
|
|
6
|
-
|
|
6
|
+
const formatting = {};
|
|
7
7
|
|
|
8
|
-
|
|
8
|
+
const formattingDispatchTable = {
|
|
9
9
|
picture: function () {
|
|
10
|
-
|
|
10
|
+
const postContainer = this;
|
|
11
11
|
postContainer.find('#files')
|
|
12
12
|
.attr('accept', 'image/*')
|
|
13
13
|
.click();
|
|
14
14
|
},
|
|
15
15
|
|
|
16
16
|
upload: function () {
|
|
17
|
-
|
|
17
|
+
const postContainer = this;
|
|
18
18
|
postContainer.find('#files')
|
|
19
19
|
.attr('accept', '')
|
|
20
20
|
.click();
|
|
@@ -22,7 +22,7 @@ define('composer/formatting', [
|
|
|
22
22
|
|
|
23
23
|
thumbs: function () {
|
|
24
24
|
formatting.exitFullscreen();
|
|
25
|
-
|
|
25
|
+
const postContainer = this;
|
|
26
26
|
require(['composer'], function (composer) {
|
|
27
27
|
const uuid = postContainer.get(0).getAttribute('data-uuid');
|
|
28
28
|
const composerObj = composer.posts[uuid];
|
|
@@ -39,12 +39,12 @@ define('composer/formatting', [
|
|
|
39
39
|
},
|
|
40
40
|
|
|
41
41
|
tags: function () {
|
|
42
|
-
|
|
42
|
+
const postContainer = this;
|
|
43
43
|
postContainer.find('.tags-container').toggleClass('hidden');
|
|
44
44
|
},
|
|
45
45
|
|
|
46
46
|
zen: function () {
|
|
47
|
-
|
|
47
|
+
const postContainer = this;
|
|
48
48
|
$(window).one('resize', function () {
|
|
49
49
|
function onResize() {
|
|
50
50
|
if (!screenfull.isFullscreen) {
|
|
@@ -70,7 +70,7 @@ define('composer/formatting', [
|
|
|
70
70
|
},
|
|
71
71
|
};
|
|
72
72
|
|
|
73
|
-
|
|
73
|
+
const buttons = [];
|
|
74
74
|
|
|
75
75
|
formatting.exitFullscreen = function () {
|
|
76
76
|
if (screenfull.isEnabled && screenfull.isFullscreen) {
|
|
@@ -82,7 +82,7 @@ define('composer/formatting', [
|
|
|
82
82
|
const formattingBarEl = $('.formatting-bar');
|
|
83
83
|
const fileForm = formattingBarEl.find('.formatting-group #fileForm');
|
|
84
84
|
buttons.forEach((btn) => {
|
|
85
|
-
let markup
|
|
85
|
+
let markup;
|
|
86
86
|
if (Array.isArray(btn.dropdownItems) && btn.dropdownItems.length) {
|
|
87
87
|
markup = generateFormattingDropdown(btn);
|
|
88
88
|
} else {
|
|
@@ -178,8 +178,8 @@ define('composer/formatting', [
|
|
|
178
178
|
|
|
179
179
|
formatting.addHandler = function (postContainer) {
|
|
180
180
|
postContainer.on('click', '.formatting-bar [data-format]', function (event) {
|
|
181
|
-
|
|
182
|
-
|
|
181
|
+
const format = $(this).attr('data-format');
|
|
182
|
+
const textarea = $(this).parents('[component="composer"]').find('textarea')[0];
|
|
183
183
|
|
|
184
184
|
if (formattingDispatchTable.hasOwnProperty(format)) {
|
|
185
185
|
formattingDispatchTable[format].call(
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
3
|
define('composer/preview', ['hooks'], function (hooks) {
|
|
4
|
-
|
|
4
|
+
const preview = {};
|
|
5
5
|
|
|
6
6
|
preview.render = function (postContainer, callback) {
|
|
7
7
|
callback = callback || function () {};
|
|
@@ -9,7 +9,7 @@ define('composer/preview', ['hooks'], function (hooks) {
|
|
|
9
9
|
return callback();
|
|
10
10
|
}
|
|
11
11
|
|
|
12
|
-
|
|
12
|
+
const textarea = postContainer.find('textarea');
|
|
13
13
|
|
|
14
14
|
socket.emit('plugins.composer.renderPreview', textarea.val(), function (err, preview) {
|
|
15
15
|
if (err) {
|
|
@@ -27,17 +27,17 @@ define('composer/preview', ['hooks'], function (hooks) {
|
|
|
27
27
|
if (!postContainer.find('.preview-container').is(':visible')) {
|
|
28
28
|
return;
|
|
29
29
|
}
|
|
30
|
-
|
|
31
|
-
|
|
30
|
+
const textarea = postContainer.find('textarea');
|
|
31
|
+
const preview = postContainer.find('.preview');
|
|
32
32
|
|
|
33
33
|
if (textarea.length && preview.length) {
|
|
34
|
-
|
|
34
|
+
const diff = textarea[0].scrollHeight - textarea.height();
|
|
35
35
|
|
|
36
36
|
if (diff === 0) {
|
|
37
37
|
return;
|
|
38
38
|
}
|
|
39
39
|
|
|
40
|
-
|
|
40
|
+
const scrollPercent = textarea.scrollTop() / diff;
|
|
41
41
|
|
|
42
42
|
preview.scrollTop(Math.max(preview[0].scrollHeight - preview.height(), 0) * scrollPercent);
|
|
43
43
|
}
|
|
@@ -2,18 +2,18 @@
|
|
|
2
2
|
'use strict';
|
|
3
3
|
|
|
4
4
|
define('composer/resize', ['taskbar'], function (taskbar) {
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
5
|
+
const resize = {};
|
|
6
|
+
let oldRatio = 0;
|
|
7
|
+
const minimumRatio = 0.3;
|
|
8
|
+
const snapMargin = 0.05;
|
|
9
|
+
const smallMin = 768;
|
|
10
|
+
|
|
11
|
+
const $body = $('body');
|
|
12
|
+
const $window = $(window);
|
|
13
|
+
const $headerMenu = $('[component="navbar"]');
|
|
14
14
|
const content = document.getElementById('content');
|
|
15
15
|
|
|
16
|
-
|
|
16
|
+
const header = $headerMenu[0];
|
|
17
17
|
|
|
18
18
|
function getSavedRatio() {
|
|
19
19
|
return localStorage.getItem('composer:resizeRatio') || 0.5;
|
|
@@ -24,7 +24,7 @@ define('composer/resize', ['taskbar'], function (taskbar) {
|
|
|
24
24
|
}
|
|
25
25
|
|
|
26
26
|
function getBounds() {
|
|
27
|
-
|
|
27
|
+
let headerRect;
|
|
28
28
|
if (header) {
|
|
29
29
|
headerRect = header.getBoundingClientRect();
|
|
30
30
|
} else {
|
|
@@ -32,9 +32,9 @@ define('composer/resize', ['taskbar'], function (taskbar) {
|
|
|
32
32
|
headerRect = { bottom: 0 };
|
|
33
33
|
}
|
|
34
34
|
|
|
35
|
-
|
|
35
|
+
const headerBottom = Math.max(headerRect.bottom, 0);
|
|
36
36
|
|
|
37
|
-
|
|
37
|
+
const rect = {
|
|
38
38
|
top: 0,
|
|
39
39
|
left: 0,
|
|
40
40
|
right: window.innerWidth,
|
|
@@ -51,24 +51,24 @@ define('composer/resize', ['taskbar'], function (taskbar) {
|
|
|
51
51
|
}
|
|
52
52
|
|
|
53
53
|
function doResize(postContainer, ratio) {
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
54
|
+
const bounds = getBounds();
|
|
55
|
+
const elem = postContainer[0];
|
|
56
|
+
const style = window.getComputedStyle(elem);
|
|
57
57
|
|
|
58
58
|
// Adjust minimumRatio for shorter viewports
|
|
59
|
-
|
|
60
|
-
|
|
59
|
+
const minHeight = parseInt(style.getPropertyValue('min-height'), 10);
|
|
60
|
+
const adjustedMinimum = Math.max(minHeight / window.innerHeight, minimumRatio);
|
|
61
61
|
|
|
62
62
|
if (bounds.width >= smallMin) {
|
|
63
63
|
const boundedDifference = (bounds.height - bounds.boundedHeight) / bounds.height;
|
|
64
64
|
ratio = Math.min(Math.max(ratio, adjustedMinimum + boundedDifference), 1);
|
|
65
65
|
|
|
66
|
-
|
|
66
|
+
const top = ratio * bounds.boundedHeight / bounds.height;
|
|
67
67
|
elem.style.top = ((1 - top) * 100).toString() + '%';
|
|
68
68
|
|
|
69
69
|
// Add some extra space at the bottom of the body so that
|
|
70
70
|
// the user can still scroll to the last post w/ composer open
|
|
71
|
-
|
|
71
|
+
const rect = elem.getBoundingClientRect();
|
|
72
72
|
content.style.paddingBottom = (rect.bottom - rect.top).toString() + 'px';
|
|
73
73
|
} else {
|
|
74
74
|
elem.style.top = 0;
|
|
@@ -80,8 +80,8 @@ define('composer/resize', ['taskbar'], function (taskbar) {
|
|
|
80
80
|
taskbar.updateActive(postContainer.attr('data-uuid'));
|
|
81
81
|
}
|
|
82
82
|
|
|
83
|
-
|
|
84
|
-
|
|
83
|
+
let resizeIt = doResize;
|
|
84
|
+
const raf = window.requestAnimationFrame ||
|
|
85
85
|
window.webkitRequestAnimationFrame ||
|
|
86
86
|
window.mozRequestAnimationFrame;
|
|
87
87
|
|
|
@@ -99,7 +99,7 @@ define('composer/resize', ['taskbar'], function (taskbar) {
|
|
|
99
99
|
}
|
|
100
100
|
|
|
101
101
|
resize.reposition = function (postContainer) {
|
|
102
|
-
|
|
102
|
+
let ratio = getSavedRatio();
|
|
103
103
|
|
|
104
104
|
if (ratio >= 1 - snapMargin) {
|
|
105
105
|
ratio = 1;
|
|
@@ -118,15 +118,15 @@ define('composer/resize', ['taskbar'], function (taskbar) {
|
|
|
118
118
|
};
|
|
119
119
|
|
|
120
120
|
resize.handleResize = function (postContainer) {
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
121
|
+
let resizeOffset = 0;
|
|
122
|
+
let resizeBegin = 0;
|
|
123
|
+
let resizeEnd = 0;
|
|
124
|
+
const $resizer = postContainer.find('.resizer');
|
|
125
|
+
const resizer = $resizer[0];
|
|
126
126
|
|
|
127
127
|
function resizeStart(e) {
|
|
128
|
-
|
|
129
|
-
|
|
128
|
+
const resizeRect = resizer.getBoundingClientRect();
|
|
129
|
+
const resizeCenterY = (resizeRect.top + resizeRect.bottom) / 2;
|
|
130
130
|
|
|
131
131
|
resizeOffset = (resizeCenterY - e.clientY) / 2;
|
|
132
132
|
resizeBegin = e.clientY;
|
|
@@ -137,9 +137,9 @@ define('composer/resize', ['taskbar'], function (taskbar) {
|
|
|
137
137
|
}
|
|
138
138
|
|
|
139
139
|
function resizeAction(e) {
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
140
|
+
const position = e.clientY - resizeOffset;
|
|
141
|
+
const bounds = getBounds();
|
|
142
|
+
const ratio = (bounds.height - position) / (bounds.boundedHeight);
|
|
143
143
|
|
|
144
144
|
resizeIt(postContainer, ratio);
|
|
145
145
|
}
|
|
@@ -153,9 +153,9 @@ define('composer/resize', ['taskbar'], function (taskbar) {
|
|
|
153
153
|
$window.off('mouseup', resizeStop);
|
|
154
154
|
$body.off('touchmove', resizeTouchAction);
|
|
155
155
|
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
156
|
+
const position = resizeEnd - resizeOffset;
|
|
157
|
+
const bounds = getBounds();
|
|
158
|
+
let ratio = (bounds.height - position) / (bounds.boundedHeight);
|
|
159
159
|
|
|
160
160
|
if (resizeEnd - resizeBegin === 0 && postContainer.hasClass('maximized')) {
|
|
161
161
|
postContainer.removeClass('maximized');
|
|
@@ -2,13 +2,13 @@
|
|
|
2
2
|
'use strict';
|
|
3
3
|
|
|
4
4
|
define('composer/tags', ['alerts'], function (alerts) {
|
|
5
|
-
|
|
5
|
+
const tags = {};
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
|
|
7
|
+
let minTags;
|
|
8
|
+
let maxTags;
|
|
9
9
|
|
|
10
10
|
tags.init = function (postContainer, postData) {
|
|
11
|
-
|
|
11
|
+
const tagEl = postContainer.find('.tags');
|
|
12
12
|
if (!tagEl.length) {
|
|
13
13
|
return;
|
|
14
14
|
}
|
|
@@ -58,9 +58,9 @@ define('composer/tags', ['alerts'], function (alerts) {
|
|
|
58
58
|
addTags(postData.tags, tagEl);
|
|
59
59
|
|
|
60
60
|
tagEl.on('beforeItemAdd', function (event) {
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
61
|
+
const reachedMaxTags = maxTags && maxTags <= tags.getTags(postContainer.attr('data-uuid')).length;
|
|
62
|
+
const cleanTag = utils.cleanUpTag(event.item, config.maximumTagLength);
|
|
63
|
+
const different = cleanTag !== event.item;
|
|
64
64
|
event.cancel = different ||
|
|
65
65
|
event.item.length < config.minimumTagLength ||
|
|
66
66
|
event.item.length > config.maximumTagLength ||
|
|
@@ -73,7 +73,7 @@ define('composer/tags', ['alerts'], function (alerts) {
|
|
|
73
73
|
} else if (reachedMaxTags) {
|
|
74
74
|
return alerts.error('[[error:too-many-tags, ' + maxTags + ']]');
|
|
75
75
|
}
|
|
76
|
-
|
|
76
|
+
const cid = postData.hasOwnProperty('cid') ? postData.cid : ajaxify.data.cid;
|
|
77
77
|
$(window).trigger('action:tag.beforeAdd', {
|
|
78
78
|
cid,
|
|
79
79
|
tagEl,
|
|
@@ -109,7 +109,7 @@ define('composer/tags', ['alerts'], function (alerts) {
|
|
|
109
109
|
if (event.options && event.options.skipAddCheck) {
|
|
110
110
|
return;
|
|
111
111
|
}
|
|
112
|
-
|
|
112
|
+
const cid = postData.hasOwnProperty('cid') ? postData.cid : ajaxify.data.cid;
|
|
113
113
|
socket.emit('topics.isTagAllowed', { tag: event.item, cid: cid || 0 }, function (err, allowed) {
|
|
114
114
|
if (err) {
|
|
115
115
|
return alerts.error(err);
|
|
@@ -136,7 +136,7 @@ define('composer/tags', ['alerts'], function (alerts) {
|
|
|
136
136
|
});
|
|
137
137
|
|
|
138
138
|
$('[component="composer/tag/dropdown"]').on('click', 'li', function () {
|
|
139
|
-
|
|
139
|
+
const tag = $(this).attr('data-tag');
|
|
140
140
|
if (tag) {
|
|
141
141
|
addTags([tag], tagEl);
|
|
142
142
|
}
|
|
@@ -153,7 +153,7 @@ define('composer/tags', ['alerts'], function (alerts) {
|
|
|
153
153
|
};
|
|
154
154
|
|
|
155
155
|
tags.onChangeCategory = function (postContainer, postData, cid, categoryData) {
|
|
156
|
-
|
|
156
|
+
const tagDropdown = postContainer.find('[component="composer/tag/dropdown"]');
|
|
157
157
|
if (!tagDropdown.length) {
|
|
158
158
|
return;
|
|
159
159
|
}
|
|
@@ -168,8 +168,8 @@ define('composer/tags', ['alerts'], function (alerts) {
|
|
|
168
168
|
};
|
|
169
169
|
|
|
170
170
|
function toggleTagInput(postContainer, postData, data) {
|
|
171
|
-
|
|
172
|
-
|
|
171
|
+
const tagEl = postContainer.find('.tags');
|
|
172
|
+
const input = postContainer.find('.bootstrap-tagsinput input');
|
|
173
173
|
if (!input.length) {
|
|
174
174
|
return;
|
|
175
175
|
}
|
|
@@ -212,7 +212,7 @@ define('composer/tags', ['alerts'], function (alerts) {
|
|
|
212
212
|
|
|
213
213
|
function triggerEnter(input) {
|
|
214
214
|
// http://stackoverflow.com/a/3276819/583363
|
|
215
|
-
|
|
215
|
+
const e = jQuery.Event('keypress');
|
|
216
216
|
e.which = 13;
|
|
217
217
|
e.keyCode = 13;
|
|
218
218
|
setTimeout(function () {
|
|
@@ -222,7 +222,7 @@ define('composer/tags', ['alerts'], function (alerts) {
|
|
|
222
222
|
|
|
223
223
|
function addTags(tags, tagEl) {
|
|
224
224
|
if (tags && tags.length) {
|
|
225
|
-
for (
|
|
225
|
+
for (let i = 0; i < tags.length; ++i) {
|
|
226
226
|
tagEl.tagsinput('add', tags[i]);
|
|
227
227
|
}
|
|
228
228
|
}
|
|
@@ -8,11 +8,11 @@ define('composer/uploads', [
|
|
|
8
8
|
'uploadHelpers',
|
|
9
9
|
'jquery-form',
|
|
10
10
|
], function (preview, categoryList, translator, alerts, uploadHelpers) {
|
|
11
|
-
|
|
11
|
+
const uploads = {
|
|
12
12
|
inProgress: {},
|
|
13
13
|
};
|
|
14
14
|
|
|
15
|
-
|
|
15
|
+
let uploadingText = '';
|
|
16
16
|
|
|
17
17
|
uploads.initialize = function (post_uuid) {
|
|
18
18
|
initializeDragAndDrop(post_uuid);
|
|
@@ -26,10 +26,10 @@ define('composer/uploads', [
|
|
|
26
26
|
};
|
|
27
27
|
|
|
28
28
|
function addChangeHandlers(post_uuid) {
|
|
29
|
-
|
|
29
|
+
const postContainer = $('.composer[data-uuid="' + post_uuid + '"]');
|
|
30
30
|
|
|
31
31
|
postContainer.find('#files').on('change', function (e) {
|
|
32
|
-
|
|
32
|
+
const files = (e.target || {}).files ||
|
|
33
33
|
($(this).val() ? [{ name: $(this).val(), type: utils.fileMimeType($(this).val()) }] : null);
|
|
34
34
|
if (files) {
|
|
35
35
|
uploadContentFiles({ files: files, post_uuid: post_uuid, route: '/api/post/upload' });
|
|
@@ -38,7 +38,7 @@ define('composer/uploads', [
|
|
|
38
38
|
}
|
|
39
39
|
|
|
40
40
|
function initializeDragAndDrop(post_uuid) {
|
|
41
|
-
|
|
41
|
+
const postContainer = $('.composer[data-uuid="' + post_uuid + '"]');
|
|
42
42
|
uploadHelpers.handleDragDrop({
|
|
43
43
|
container: postContainer,
|
|
44
44
|
callback: function (upload) {
|
|
@@ -53,7 +53,7 @@ define('composer/uploads', [
|
|
|
53
53
|
}
|
|
54
54
|
|
|
55
55
|
function initializePaste(post_uuid) {
|
|
56
|
-
|
|
56
|
+
const postContainer = $('.composer[data-uuid="' + post_uuid + '"]');
|
|
57
57
|
uploadHelpers.handlePaste({
|
|
58
58
|
container: postContainer,
|
|
59
59
|
callback: function (upload) {
|
|
@@ -77,35 +77,34 @@ define('composer/uploads', [
|
|
|
77
77
|
}
|
|
78
78
|
|
|
79
79
|
function uploadContentFiles(params) {
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
80
|
+
const files = [...params.files];
|
|
81
|
+
const post_uuid = params.post_uuid;
|
|
82
|
+
const postContainer = $('.composer[data-uuid="' + post_uuid + '"]');
|
|
83
|
+
const textarea = postContainer.find('textarea');
|
|
84
|
+
let text = textarea.val();
|
|
85
|
+
const uploadForm = postContainer.find('#fileForm');
|
|
86
|
+
let doneUploading = false;
|
|
87
87
|
uploadForm.attr('action', config.relative_path + params.route);
|
|
88
88
|
|
|
89
|
-
|
|
89
|
+
let cid = categoryList.getSelectedCid();
|
|
90
90
|
if (!cid && ajaxify.data.cid) {
|
|
91
91
|
cid = ajaxify.data.cid;
|
|
92
92
|
}
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
isImage = files[i].type.match(/image./);
|
|
93
|
+
|
|
94
|
+
for (let i = 0; i < files.length; ++i) {
|
|
95
|
+
const isImage = files[i].type.match(/image./);
|
|
97
96
|
if ((isImage && !app.user.privileges['upload:post:image']) || (!isImage && !app.user.privileges['upload:post:file'])) {
|
|
98
97
|
return alerts.error('[[error:no-privileges]]');
|
|
99
98
|
}
|
|
100
99
|
}
|
|
101
100
|
|
|
102
|
-
|
|
101
|
+
const filenameMapping = [];
|
|
103
102
|
let filesText = '';
|
|
104
|
-
for (i = 0; i < files.length; ++i) {
|
|
103
|
+
for (let i = 0; i < files.length; ++i) {
|
|
105
104
|
// The filename map has datetime and iterator prepended so that they can be properly tracked even if the
|
|
106
105
|
// filenames are identical.
|
|
107
106
|
filenameMapping.push(i + '_' + Date.now() + '_' + (params.fileNames ? params.fileNames[i] : files[i].name));
|
|
108
|
-
isImage = files[i].type.match(/image./);
|
|
107
|
+
const isImage = files[i].type.match(/image./);
|
|
109
108
|
|
|
110
109
|
if (!app.user.isAdmin && files[i].size > parseInt(config.maximumFileSize, 10) * 1024) {
|
|
111
110
|
uploadForm[0].reset();
|
|
@@ -136,12 +135,12 @@ define('composer/uploads', [
|
|
|
136
135
|
|
|
137
136
|
uploadForm.off('submit').submit(function () {
|
|
138
137
|
function updateTextArea(filename, text, trim) {
|
|
139
|
-
|
|
138
|
+
let newFilename;
|
|
140
139
|
if (trim) {
|
|
141
140
|
newFilename = filename.replace(/^\d+_\d{13}_/, '');
|
|
142
141
|
}
|
|
143
|
-
|
|
144
|
-
|
|
142
|
+
const current = textarea.val();
|
|
143
|
+
const re = new RegExp(escapeRegExp(filename) + ']\\([^)]+\\)', 'g');
|
|
145
144
|
textarea.val(current.replace(re, (newFilename || filename) + '](' + text + ')'));
|
|
146
145
|
|
|
147
146
|
$(window).trigger('action:composer.uploadUpdate', {
|
|
@@ -171,7 +170,7 @@ define('composer/uploads', [
|
|
|
171
170
|
doneUploading = true;
|
|
172
171
|
postContainer.find('[data-action="post"]').prop('disabled', false);
|
|
173
172
|
const errorMsg = onUploadError(xhr, post_uuid);
|
|
174
|
-
for (
|
|
173
|
+
for (let i = 0; i < files.length; ++i) {
|
|
175
174
|
updateTextArea(filenameMapping[i], errorMsg, true);
|
|
176
175
|
}
|
|
177
176
|
preview.render(postContainer);
|
|
@@ -182,7 +181,7 @@ define('composer/uploads', [
|
|
|
182
181
|
if (doneUploading) {
|
|
183
182
|
return;
|
|
184
183
|
}
|
|
185
|
-
for (
|
|
184
|
+
for (let i = 0; i < files.length; ++i) {
|
|
186
185
|
updateTextArea(filenameMapping[i], translated);
|
|
187
186
|
}
|
|
188
187
|
});
|
|
@@ -192,7 +191,7 @@ define('composer/uploads', [
|
|
|
192
191
|
const uploads = res.response.images;
|
|
193
192
|
doneUploading = true;
|
|
194
193
|
if (uploads && uploads.length) {
|
|
195
|
-
for (
|
|
194
|
+
for (let i = 0; i < uploads.length; ++i) {
|
|
196
195
|
uploads[i].filename = filenameMapping[i].replace(/^\d+_\d{13}_/, '');
|
|
197
196
|
uploads[i].isImage = /image./.test(files[i].type);
|
|
198
197
|
updateTextArea(filenameMapping[i], uploads[i].url, true);
|
|
@@ -221,7 +220,7 @@ define('composer/uploads', [
|
|
|
221
220
|
}
|
|
222
221
|
|
|
223
222
|
function onUploadError(xhr, post_uuid) {
|
|
224
|
-
|
|
223
|
+
let msg = (xhr.responseJSON &&
|
|
225
224
|
(xhr.responseJSON.error || (xhr.responseJSON.status && xhr.responseJSON.status.message))) ||
|
|
226
225
|
'[[error:parse-error]]';
|
|
227
226
|
|
package/static/lib/composer.js
CHANGED
|
@@ -25,7 +25,7 @@ define('composer', [
|
|
|
25
25
|
], function (taskbar, translator, uploads, formatting, drafts, tags,
|
|
26
26
|
categoryList, preview, resize, autocomplete, scheduler, postQueue, scrollStop,
|
|
27
27
|
topicThumbs, api, bootbox, alerts, hooks, messagesModule, search, screenfull) {
|
|
28
|
-
|
|
28
|
+
const composer = {
|
|
29
29
|
active: undefined,
|
|
30
30
|
posts: {},
|
|
31
31
|
bsEnvironment: undefined,
|
|
@@ -41,7 +41,7 @@ define('composer', [
|
|
|
41
41
|
});
|
|
42
42
|
|
|
43
43
|
window.addEventListener('popstate', function () {
|
|
44
|
-
|
|
44
|
+
const env = utils.findBootstrapEnvironment();
|
|
45
45
|
if (composer.active && (env === 'xs' || env === 'sm')) {
|
|
46
46
|
if (!composer.posts[composer.active].modified) {
|
|
47
47
|
composer.discard(composer.active);
|
|
@@ -64,15 +64,15 @@ define('composer', [
|
|
|
64
64
|
});
|
|
65
65
|
|
|
66
66
|
function removeComposerHistory() {
|
|
67
|
-
|
|
67
|
+
const env = composer.bsEnvironment;
|
|
68
68
|
if (ajaxify.data.template.compose === true || env === 'xs' || env === 'sm') {
|
|
69
69
|
history.back();
|
|
70
70
|
}
|
|
71
71
|
}
|
|
72
72
|
|
|
73
73
|
function onWindowResize() {
|
|
74
|
-
|
|
75
|
-
|
|
74
|
+
const env = utils.findBootstrapEnvironment();
|
|
75
|
+
const isMobile = env === 'xs' || env === 'sm';
|
|
76
76
|
|
|
77
77
|
if (preview.toggle && preview.env !== env) {
|
|
78
78
|
preview.env = env;
|
|
@@ -93,9 +93,7 @@ define('composer', [
|
|
|
93
93
|
|
|
94
94
|
function alreadyOpen(post) {
|
|
95
95
|
// If a composer for the same cid/tid/pid is already open, return the uuid, else return bool false
|
|
96
|
-
|
|
97
|
-
var id;
|
|
98
|
-
|
|
96
|
+
let type;
|
|
99
97
|
if (post.hasOwnProperty('cid')) {
|
|
100
98
|
type = 'cid';
|
|
101
99
|
} else if (post.hasOwnProperty('tid')) {
|
|
@@ -104,11 +102,11 @@ define('composer', [
|
|
|
104
102
|
type = 'pid';
|
|
105
103
|
}
|
|
106
104
|
|
|
107
|
-
id = post[type];
|
|
105
|
+
const id = String(post[type]);
|
|
108
106
|
|
|
109
107
|
// Find a match
|
|
110
108
|
for (const uuid of Object.keys(composer.posts)) {
|
|
111
|
-
if (composer.posts[uuid].hasOwnProperty(type) && id === composer.posts[uuid][type]) {
|
|
109
|
+
if (composer.posts[uuid].hasOwnProperty(type) && id === String(composer.posts[uuid][type])) {
|
|
112
110
|
return uuid;
|
|
113
111
|
}
|
|
114
112
|
}
|
|
@@ -122,12 +120,10 @@ define('composer', [
|
|
|
122
120
|
return;
|
|
123
121
|
}
|
|
124
122
|
|
|
125
|
-
|
|
126
|
-
var existingUUID = alreadyOpen(post);
|
|
127
|
-
|
|
123
|
+
const existingUUID = alreadyOpen(post);
|
|
128
124
|
if (existingUUID) {
|
|
129
125
|
taskbar.updateActive(existingUUID);
|
|
130
|
-
if (post.body) {
|
|
126
|
+
if (post.body && !post.fromDraft) {
|
|
131
127
|
const postContainer = $('.composer[data-uuid="' + existingUUID + '"]');
|
|
132
128
|
const bodyEl = postContainer.find('textarea');
|
|
133
129
|
const prevText = bodyEl.val();
|
|
@@ -137,18 +133,16 @@ define('composer', [
|
|
|
137
133
|
}
|
|
138
134
|
return composer.load(existingUUID);
|
|
139
135
|
}
|
|
140
|
-
|
|
141
|
-
|
|
136
|
+
const uuid = utils.generateUUID();
|
|
137
|
+
let actionText = '[[topic:composer.new-topic]]';
|
|
142
138
|
if (post.action === 'posts.reply') {
|
|
143
|
-
actionText = '
|
|
139
|
+
actionText = translator.compile('topic:composer.replying-to', `"${post.title}"`);
|
|
144
140
|
} else if (post.action === 'posts.edit') {
|
|
145
|
-
actionText = '
|
|
141
|
+
actionText = translator.compile('topic:composer.editing-in', `"${post.title}"`);
|
|
146
142
|
}
|
|
147
143
|
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
title: translatedAction.replace('%1', '"' + post.title + '"'),
|
|
151
|
-
});
|
|
144
|
+
taskbar.push('composer', uuid, {
|
|
145
|
+
title: actionText,
|
|
152
146
|
});
|
|
153
147
|
|
|
154
148
|
composer.posts[uuid] = post;
|
|
@@ -188,6 +182,7 @@ define('composer', [
|
|
|
188
182
|
|
|
189
183
|
composer.newTopic = async (data) => {
|
|
190
184
|
let pushData = {
|
|
185
|
+
fromDraft: data.fromDraft,
|
|
191
186
|
save_id: data.save_id,
|
|
192
187
|
action: 'topics.post',
|
|
193
188
|
cid: data.cid,
|
|
@@ -212,7 +207,7 @@ define('composer', [
|
|
|
212
207
|
// tid, toPid, selectedPid, title, username, text, uuid
|
|
213
208
|
data.uuid = data.uuid || composer.active;
|
|
214
209
|
|
|
215
|
-
|
|
210
|
+
const escapedTitle = (data.title || '')
|
|
216
211
|
.replace(/([\\`*_{}[\]()#+\-.!])/g, '\\$1')
|
|
217
212
|
.replace(/\[/g, '[')
|
|
218
213
|
.replace(/\]/g, ']')
|
|
@@ -246,9 +241,9 @@ define('composer', [
|
|
|
246
241
|
composer.load(data.uuid);
|
|
247
242
|
}
|
|
248
243
|
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
244
|
+
const postContainer = $('.composer[data-uuid="' + data.uuid + '"]');
|
|
245
|
+
const bodyEl = postContainer.find('textarea');
|
|
246
|
+
const prevText = bodyEl.val();
|
|
252
247
|
|
|
253
248
|
translator.translate(quoteKey, config.defaultLang, function (translated) {
|
|
254
249
|
composer.posts[data.uuid].body = (prevText.length ? prevText + '\n\n' : '') + translated + data.body;
|
|
@@ -261,6 +256,7 @@ define('composer', [
|
|
|
261
256
|
composer.newReply = function (data) {
|
|
262
257
|
translator.translate(data.body, config.defaultLang, function (translated) {
|
|
263
258
|
push({
|
|
259
|
+
fromDraft: data.fromDraft,
|
|
264
260
|
save_id: data.save_id,
|
|
265
261
|
action: 'posts.reply',
|
|
266
262
|
tid: data.tid,
|
|
@@ -279,6 +275,7 @@ define('composer', [
|
|
|
279
275
|
if (err) {
|
|
280
276
|
return alerts.error(err);
|
|
281
277
|
}
|
|
278
|
+
postData.fromDraft = data.fromDraft;
|
|
282
279
|
postData.save_id = data.save_id;
|
|
283
280
|
postData.action = 'posts.edit';
|
|
284
281
|
postData.pid = data.pid;
|
|
@@ -296,7 +293,7 @@ define('composer', [
|
|
|
296
293
|
};
|
|
297
294
|
|
|
298
295
|
composer.load = function (post_uuid) {
|
|
299
|
-
|
|
296
|
+
const postContainer = $('.composer[data-uuid="' + post_uuid + '"]');
|
|
300
297
|
if (postContainer.length) {
|
|
301
298
|
activate(post_uuid);
|
|
302
299
|
resize.reposition(postContainer);
|
|
@@ -432,12 +429,12 @@ define('composer', [
|
|
|
432
429
|
}
|
|
433
430
|
|
|
434
431
|
async function createNewComposer(post_uuid) {
|
|
435
|
-
|
|
432
|
+
let postData = composer.posts[post_uuid];
|
|
436
433
|
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
434
|
+
const isTopic = postData ? postData.hasOwnProperty('cid') : false;
|
|
435
|
+
const isMain = postData ? !!postData.isMain : false;
|
|
436
|
+
const isEditing = postData ? !!postData.pid : false;
|
|
437
|
+
const isGuestPost = postData ? parseInt(postData.uid, 10) === 0 : false;
|
|
441
438
|
const isScheduled = postData.timestamp > Date.now();
|
|
442
439
|
|
|
443
440
|
postData.category = await getSelectedCategory(postData);
|
|
@@ -512,7 +509,7 @@ define('composer', [
|
|
|
512
509
|
|
|
513
510
|
$(document.body).append(composerTemplate);
|
|
514
511
|
|
|
515
|
-
|
|
512
|
+
const postContainer = $(composerTemplate[0]);
|
|
516
513
|
|
|
517
514
|
resize.reposition(postContainer);
|
|
518
515
|
composer.enhance(postContainer, post_uuid, postData);
|
|
@@ -535,10 +532,10 @@ define('composer', [
|
|
|
535
532
|
resize.handleResize(postContainer);
|
|
536
533
|
|
|
537
534
|
if (composer.bsEnvironment === 'xs' || composer.bsEnvironment === 'sm') {
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
535
|
+
const submitBtns = postContainer.find('.composer-submit');
|
|
536
|
+
const mobileSubmitBtn = postContainer.find('.mobile-navbar .composer-submit');
|
|
537
|
+
const textareaEl = postContainer.find('.write');
|
|
538
|
+
const idx = textareaEl.attr('tabindex');
|
|
542
539
|
|
|
543
540
|
submitBtns.removeAttr('tabindex');
|
|
544
541
|
mobileSubmitBtn.attr('tabindex', parseInt(idx, 10) + 1);
|
|
@@ -582,7 +579,7 @@ define('composer', [
|
|
|
582
579
|
message: '[[modules:composer.remote-pid-content-immutable]]',
|
|
583
580
|
timeout: 15000,
|
|
584
581
|
});
|
|
585
|
-
|
|
582
|
+
const container = postContainer.find('.write-container');
|
|
586
583
|
container.addClass('hidden');
|
|
587
584
|
}
|
|
588
585
|
|
|
@@ -605,10 +602,10 @@ define('composer', [
|
|
|
605
602
|
}
|
|
606
603
|
|
|
607
604
|
function handleSearch(postContainer) {
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
605
|
+
const uuid = postContainer.attr('data-uuid');
|
|
606
|
+
const isEditing = composer.posts[uuid] && composer.posts[uuid].action === 'posts.edit';
|
|
607
|
+
const env = utils.findBootstrapEnvironment();
|
|
608
|
+
const isMobile = env === 'xs' || env === 'sm';
|
|
612
609
|
if (isEditing || isMobile) {
|
|
613
610
|
return;
|
|
614
611
|
}
|
|
@@ -642,7 +639,7 @@ define('composer', [
|
|
|
642
639
|
|
|
643
640
|
function focusElements(postContainer) {
|
|
644
641
|
setTimeout(function () {
|
|
645
|
-
|
|
642
|
+
const title = postContainer.find('input.title');
|
|
646
643
|
|
|
647
644
|
if (title.length) {
|
|
648
645
|
title.focus();
|
|
@@ -653,13 +650,13 @@ define('composer', [
|
|
|
653
650
|
}
|
|
654
651
|
|
|
655
652
|
async function post(post_uuid) {
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
653
|
+
const postData = composer.posts[post_uuid];
|
|
654
|
+
const postContainer = $('.composer[data-uuid="' + post_uuid + '"]');
|
|
655
|
+
const handleEl = postContainer.find('.handle');
|
|
656
|
+
const titleEl = postContainer.find('.title');
|
|
657
|
+
const bodyEl = postContainer.find('textarea');
|
|
658
|
+
const thumbEl = postContainer.find('input#topic-thumb-url');
|
|
659
|
+
const onComposeRoute = postData.hasOwnProperty('template') && postData.template.compose === true;
|
|
663
660
|
const submitBtn = postContainer.find('.composer-submit');
|
|
664
661
|
|
|
665
662
|
titleEl.val(titleEl.val().trim());
|
|
@@ -668,13 +665,13 @@ define('composer', [
|
|
|
668
665
|
thumbEl.val(thumbEl.val().trim());
|
|
669
666
|
}
|
|
670
667
|
|
|
671
|
-
|
|
668
|
+
const action = postData.action;
|
|
672
669
|
|
|
673
|
-
|
|
674
|
-
|
|
670
|
+
const checkTitle = (postData.hasOwnProperty('cid') || parseInt(postData.pid, 10)) && postContainer.find('input.title').length;
|
|
671
|
+
const isCategorySelected = !checkTitle || (checkTitle && postData.cid);
|
|
675
672
|
|
|
676
673
|
// Specifically for checking title/body length via plugins
|
|
677
|
-
|
|
674
|
+
const payload = {
|
|
678
675
|
post_uuid: post_uuid,
|
|
679
676
|
postData: postData,
|
|
680
677
|
postContainer: postContainer,
|
|
@@ -751,7 +748,7 @@ define('composer', [
|
|
|
751
748
|
timestamp: scheduler.getTimestamp(),
|
|
752
749
|
};
|
|
753
750
|
}
|
|
754
|
-
|
|
751
|
+
const submitHookData = {
|
|
755
752
|
composerEl: postContainer,
|
|
756
753
|
action: action,
|
|
757
754
|
composerData: composerData,
|
|
@@ -763,8 +760,8 @@ define('composer', [
|
|
|
763
760
|
hooks.fire('action:composer.submit', Object.freeze(submitHookData));
|
|
764
761
|
|
|
765
762
|
// Minimize composer (and set textarea as readonly) while submitting
|
|
766
|
-
|
|
767
|
-
|
|
763
|
+
const taskbarIconEl = $('#taskbar .composer[data-uuid="' + post_uuid + '"] i');
|
|
764
|
+
const textareaEl = postContainer.find('.write');
|
|
768
765
|
taskbarIconEl.removeClass('fa-plus').addClass('fa-circle-o-notch fa-spin');
|
|
769
766
|
composer.minimize(post_uuid);
|
|
770
767
|
textareaEl.prop('readonly', true);
|
|
@@ -830,8 +827,8 @@ define('composer', [
|
|
|
830
827
|
|
|
831
828
|
composer.discard = function (post_uuid) {
|
|
832
829
|
if (composer.posts[post_uuid]) {
|
|
833
|
-
|
|
834
|
-
|
|
830
|
+
const postData = composer.posts[post_uuid];
|
|
831
|
+
const postContainer = $('.composer[data-uuid="' + post_uuid + '"]');
|
|
835
832
|
postContainer.remove();
|
|
836
833
|
drafts.removeDraft(postData.save_id);
|
|
837
834
|
topicThumbs.deleteAll(post_uuid);
|
|
@@ -854,7 +851,7 @@ define('composer', [
|
|
|
854
851
|
composer.close = composer.discard;
|
|
855
852
|
|
|
856
853
|
composer.minimize = function (post_uuid) {
|
|
857
|
-
|
|
854
|
+
const postContainer = $('.composer[data-uuid="' + post_uuid + '"]');
|
|
858
855
|
postContainer.css('visibility', 'hidden');
|
|
859
856
|
composer.active = undefined;
|
|
860
857
|
taskbar.minimize('composer', post_uuid);
|
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
|
|
18
18
|
<div class="d-flex gap-1 flex-nowrap">
|
|
19
19
|
<button class="btn btn-sm btn-primary display-scheduler fs-5 {{{ if !canSchedule }}} hidden{{{ end }}}">
|
|
20
|
-
<i class="fa fa-clock-o"></i>
|
|
20
|
+
<i class="fa fa-fw fa-clock-o"></i>
|
|
21
21
|
</button>
|
|
22
22
|
<button class="btn btn-sm btn-primary composer-submit fs-5" data-action="post" tabindex="-1"><i class="fa fa-fw fa-chevron-right"></i></button>
|
|
23
23
|
</div>
|