Merge branch '5.3'
[GitHub/WoltLab/WCF.git] / com.woltlab.wcf / templates / wysiwyg.tpl
1 {event name='beforeEditorJavaScript'}
2
3 <script data-relocate="true">
4 head.load([
5 {if ENABLE_DEBUG_MODE}
6 {* Imperavi *}
7 '{@$__wcf->getPath()}js/3rdParty/redactor2/redactor.js?v={@LAST_UPDATE_TIME}',
8 '{@$__wcf->getPath()}js/3rdParty/redactor2/plugins/alignment.js?v={@LAST_UPDATE_TIME}',
9 '{@$__wcf->getPath()}js/3rdParty/redactor2/plugins/source.js?v={@LAST_UPDATE_TIME}',
10 '{@$__wcf->getPath()}js/3rdParty/redactor2/plugins/table.js?v={@LAST_UPDATE_TIME}',
11
12 {* WoltLab *}
13 '{@$__wcf->getPath()}js/3rdParty/redactor2/plugins/WoltLabAttachment.js?v={@LAST_UPDATE_TIME}',
14 '{@$__wcf->getPath()}js/3rdParty/redactor2/plugins/WoltLabAutosave.js?v={@LAST_UPDATE_TIME}',
15 '{@$__wcf->getPath()}js/3rdParty/redactor2/plugins/WoltLabBlock.js?v={@LAST_UPDATE_TIME}',
16 '{@$__wcf->getPath()}js/3rdParty/redactor2/plugins/WoltLabButton.js?v={@LAST_UPDATE_TIME}',
17 '{@$__wcf->getPath()}js/3rdParty/redactor2/plugins/WoltLabCaret.js?v={@LAST_UPDATE_TIME}',
18 '{@$__wcf->getPath()}js/3rdParty/redactor2/plugins/WoltLabClean.js?v={@LAST_UPDATE_TIME}',
19 '{@$__wcf->getPath()}js/3rdParty/redactor2/plugins/WoltLabCode.js?v={@LAST_UPDATE_TIME}',
20 '{@$__wcf->getPath()}js/3rdParty/redactor2/plugins/WoltLabColor.js?v={@LAST_UPDATE_TIME}',
21 '{@$__wcf->getPath()}js/3rdParty/redactor2/plugins/WoltLabDragAndDrop.js?v={@LAST_UPDATE_TIME}',
22 '{@$__wcf->getPath()}js/3rdParty/redactor2/plugins/WoltLabEvent.js?v={@LAST_UPDATE_TIME}',
23 '{@$__wcf->getPath()}js/3rdParty/redactor2/plugins/WoltLabFont.js?v={@LAST_UPDATE_TIME}',
24 '{@$__wcf->getPath()}js/3rdParty/redactor2/plugins/WoltLabFullscreen.js?v={@LAST_UPDATE_TIME}',
25 '{@$__wcf->getPath()}js/3rdParty/redactor2/plugins/WoltLabHtml.js?v={@LAST_UPDATE_TIME}',
26 '{@$__wcf->getPath()}js/3rdParty/redactor2/plugins/WoltLabImage.js?v={@LAST_UPDATE_TIME}',
27 '{@$__wcf->getPath()}js/3rdParty/redactor2/plugins/WoltLabIndent.js?v={@LAST_UPDATE_TIME}',
28 //'{@$__wcf->getPath()}js/3rdParty/redactor2/plugins/WoltLabInlineCode.js?v={@LAST_UPDATE_TIME}',
29 '{@$__wcf->getPath()}js/3rdParty/redactor2/plugins/WoltLabInsert.js?v={@LAST_UPDATE_TIME}',
30 '{@$__wcf->getPath()}js/3rdParty/redactor2/plugins/WoltLabKeydown.js?v={@LAST_UPDATE_TIME}',
31 '{@$__wcf->getPath()}js/3rdParty/redactor2/plugins/WoltLabKeyup.js?v={@LAST_UPDATE_TIME}',
32 '{@$__wcf->getPath()}js/3rdParty/redactor2/plugins/WoltLabLine.js?v={@LAST_UPDATE_TIME}',
33 '{@$__wcf->getPath()}js/3rdParty/redactor2/plugins/WoltLabLink.js?v={@LAST_UPDATE_TIME}',
34 '{@$__wcf->getPath()}js/3rdParty/redactor2/plugins/WoltLabList.js?v={@LAST_UPDATE_TIME}',
35 '{@$__wcf->getPath()}js/3rdParty/redactor2/plugins/WoltLabMedia.js?v={@LAST_UPDATE_TIME}',
36 '{@$__wcf->getPath()}js/3rdParty/redactor2/plugins/WoltLabMention.js?v={@LAST_UPDATE_TIME}',
37 '{@$__wcf->getPath()}js/3rdParty/redactor2/plugins/WoltLabModal.js?v={@LAST_UPDATE_TIME}',
38 '{@$__wcf->getPath()}js/3rdParty/redactor2/plugins/WoltLabObserve.js?v={@LAST_UPDATE_TIME}',
39 '{@$__wcf->getPath()}js/3rdParty/redactor2/plugins/WoltLabPaste.js?v={@LAST_UPDATE_TIME}',
40 '{@$__wcf->getPath()}js/3rdParty/redactor2/plugins/WoltLabQuote.js?v={@LAST_UPDATE_TIME}',
41 '{@$__wcf->getPath()}js/3rdParty/redactor2/plugins/WoltLabReply.js?v={@LAST_UPDATE_TIME}',
42 '{@$__wcf->getPath()}js/3rdParty/redactor2/plugins/WoltLabSize.js?v={@LAST_UPDATE_TIME}',
43 '{@$__wcf->getPath()}js/3rdParty/redactor2/plugins/WoltLabSmiley.js?v={@LAST_UPDATE_TIME}',
44 '{@$__wcf->getPath()}js/3rdParty/redactor2/plugins/WoltLabSource.js?v={@LAST_UPDATE_TIME}',
45 '{@$__wcf->getPath()}js/3rdParty/redactor2/plugins/WoltLabSpoiler.js?v={@LAST_UPDATE_TIME}',
46 '{@$__wcf->getPath()}js/3rdParty/redactor2/plugins/WoltLabTable.js?v={@LAST_UPDATE_TIME}',
47 '{@$__wcf->getPath()}js/3rdParty/redactor2/plugins/WoltLabUtils.js?v={@LAST_UPDATE_TIME}'
48 {else}
49 '{@$__wcf->getPath()}js/3rdParty/redactor2/redactor.combined.min.js?v={@LAST_UPDATE_TIME}'
50 {/if}
51
52 {if $__redactorJavaScript|isset}{@$__redactorJavaScript}{/if}
53 {assign var=__redactorJavaScript value=''}
54
55 {event name='redactorJavaScript'}
56 ], function () {
57 require(['Environment', 'Language', 'WoltLabSuite/Core/Ui/Redactor/Autosave', 'WoltLabSuite/Core/Ui/Redactor/Metacode'], function(Environment, Language, UiRedactorAutosave, UiRedactorMetacode) {
58 Language.addObject({
59 'wcf.attachment.dragAndDrop.dropHere': '{jslang}wcf.attachment.dragAndDrop.dropHere{/jslang}',
60 'wcf.attachment.dragAndDrop.dropNow': '{jslang}wcf.attachment.dragAndDrop.dropNow{/jslang}',
61
62 'wcf.editor.autosave.discard': '{jslang}wcf.editor.autosave.discard{/jslang}',
63 'wcf.editor.autosave.keep': '{jslang}wcf.editor.autosave.keep{/jslang}',
64 'wcf.editor.autosave.restored': '{jslang}wcf.editor.autosave.restored{/jslang}',
65
66 'wcf.editor.code.edit': '{jslang}wcf.editor.code.edit{/jslang}',
67 'wcf.editor.code.file': '{jslang}wcf.editor.code.file{/jslang}',
68 'wcf.editor.code.file.description': '{jslang}wcf.editor.code.file.description{/jslang}',
69 'wcf.editor.code.highlighter': '{jslang}wcf.editor.code.highlighter{/jslang}',
70 'wcf.editor.code.highlighter.description': '{jslang}wcf.editor.code.highlighter.description{/jslang}',
71 'wcf.editor.code.highlighter.detect': '{jslang}wcf.editor.code.highlighter.detect{/jslang}',
72 'wcf.editor.code.highlighter.plain': '{jslang}wcf.editor.code.highlighter.plain{/jslang}',
73 'wcf.editor.code.line': '{jslang}wcf.editor.code.line{/jslang}',
74 'wcf.editor.code.line.description': '{jslang}wcf.editor.code.line.description{/jslang}',
75 'wcf.editor.code.title': '{jslang __literal=true}wcf.editor.code.title{/jslang}',
76
77 'wcf.editor.html.description': '{jslang}wcf.editor.html.description{/jslang}',
78 'wcf.editor.html.title': '{jslang}wcf.editor.html.title{/jslang}',
79
80 'wcf.editor.image.edit': '{jslang}wcf.editor.image.edit{/jslang}',
81 'wcf.editor.image.insert': '{jslang}wcf.editor.image.insert{/jslang}',
82 'wcf.editor.image.link': '{jslang}wcf.editor.image.link{/jslang}',
83 'wcf.editor.image.link.error.invalid': '{jslang}wcf.editor.image.link.error.invalid{/jslang}',
84 'wcf.editor.image.float': '{jslang}wcf.editor.image.float{/jslang}',
85 'wcf.editor.image.float.left': '{jslang}wcf.editor.image.float.left{/jslang}',
86 'wcf.editor.image.float.right': '{jslang}wcf.editor.image.float.right{/jslang}',
87 'wcf.editor.image.source': '{jslang}wcf.editor.image.source{/jslang}',
88 'wcf.editor.image.source.error.blocked': '{jslang}wcf.editor.image.source.error.blocked{/jslang}',
89 'wcf.editor.image.source.error.insecure': '{jslang}wcf.editor.image.source.error.insecure{/jslang}',
90 'wcf.editor.image.source.error.invalid': '{jslang}wcf.editor.image.source.error.invalid{/jslang}',
91
92 'wcf.editor.link.add': '{jslang}wcf.editor.link.add{/jslang}',
93 'wcf.editor.link.edit': '{jslang}wcf.editor.link.edit{/jslang}',
94 'wcf.editor.link.error.invalid': '{jslang}wcf.editor.link.error.invalid{/jslang}',
95 'wcf.editor.link.url': '{jslang}wcf.editor.link.url{/jslang}',
96 'wcf.editor.link.text': '{jslang}wcf.editor.link.text{/jslang}',
97
98 'wcf.editor.list.indent': '{jslang}wcf.editor.list.indent{/jslang}',
99 'wcf.editor.list.outdent': '{jslang}wcf.editor.list.outdent{/jslang}',
100
101 'wcf.editor.quote.author': '{jslang}wcf.editor.quote.author{/jslang}',
102 'wcf.editor.quote.edit': '{jslang}wcf.editor.quote.edit{/jslang}',
103 'wcf.editor.quote.title': '{jslang __literal=true}wcf.editor.quote.title{/jslang}',
104 'wcf.editor.quote.url': '{jslang}wcf.editor.quote.url{/jslang}',
105 'wcf.editor.quote.url.description': '{jslang}wcf.editor.quote.url.description{/jslang}',
106 'wcf.editor.quote.url.error.invalid': '{jslang}wcf.editor.quote.url.error.invalid{/jslang}',
107
108 'wcf.editor.table.cols': '{jslang}wcf.editor.table.cols{/jslang}',
109 'wcf.editor.table.insertTable': '{jslang}wcf.editor.table.insertTable{/jslang}',
110 'wcf.editor.table.rows': '{jslang}wcf.editor.table.rows{/jslang}',
111
112 'wcf.editor.source.error.active': '{jslang}wcf.editor.source.error.active{/jslang}',
113
114 'wcf.editor.spoiler.label': '{jslang}wcf.editor.spoiler.label{/jslang}',
115 'wcf.editor.spoiler.label.description': '{jslang}wcf.editor.spoiler.label.description{/jslang}',
116 'wcf.editor.spoiler.edit': '{jslang}wcf.editor.spoiler.edit{/jslang}',
117 'wcf.editor.spoiler.title': '{jslang __literal=true}wcf.editor.spoiler.title{/jslang}'
118 });
119
120 var allowedInlineStyles = [], buttons = [], buttonMobile = [], buttonOptions = [], customButtons = [];
121 {include file='wysiwygToolbar'}
122
123 var highlighters = '{@MESSAGE_PUBLIC_HIGHLIGHTERS|encodeJS}'.split(/\n/).filter(function (item) { return item != ''; });
124
125 {include file='mediaJavaScript'}
126
127 var element = elById('{if $wysiwygSelector|isset}{$wysiwygSelector|encodeJS}{else}text{/if}');
128
129 var autosave = elData(element, 'autosave') || null;
130 if (autosave) {
131 autosave = new UiRedactorAutosave(element);
132 element.value = autosave.getInitialValue();
133 }
134
135 var disableMedia = elDataBool(element, 'disable-media');
136
137 var config = {
138 buttons: buttons,
139 clipboardImageUpload: {if $__wcf->getBBCodeHandler()->isAvailableBBCode('attach')}true{else}false{/if},
140 direction: '{jslang}wcf.global.pageDirection{/jslang}',
141 formatting: ['p', 'h2', 'h3', 'h4'],
142 imageCaption: false,
143 imageUpload: {if $__wcf->getBBCodeHandler()->isAvailableBBCode('attach')}true{else}false{/if},
144 lang: 'wsc', // fake language to offload phrases
145 langs: {
146 wsc: {
147 // general
148 edit: '{jslang}wcf.global.button.edit{/jslang}',
149
150 // formatting dropdown
151 heading2: '{jslang}wcf.editor.format.heading2{/jslang}',
152 heading3: '{jslang}wcf.editor.format.heading3{/jslang}',
153 heading4: '{jslang}wcf.editor.format.heading4{/jslang}',
154 paragraph: '{jslang}wcf.editor.format.paragraph{/jslang}',
155
156 // links
157 'link-edit': '{jslang}wcf.editor.link.edit{/jslang}',
158 'link-insert': '{jslang}wcf.editor.link.add{/jslang}',
159 unlink: '{jslang}wcf.editor.link.unlink{/jslang}',
160
161 // text alignment
162 'align-center': '{jslang}wcf.editor.alignment.center{/jslang}',
163 'align-justify': '{jslang}wcf.editor.alignment.justify{/jslang}',
164 'align-left': '{jslang}wcf.editor.alignment.left{/jslang}',
165 'align-right': '{jslang}wcf.editor.alignment.right{/jslang}',
166
167 // table plugin
168 'add-head': '{jslang}wcf.editor.table.addHead{/jslang}',
169 'delete-column': '{jslang}wcf.editor.table.deleteColumn{/jslang}',
170 'delete-head': '{jslang}wcf.editor.table.deleteHead{/jslang}',
171 'delete-row': '{jslang}wcf.editor.table.deleteRow{/jslang}',
172 'delete-table': '{jslang}wcf.editor.table.deleteTable{/jslang}',
173 'insert-table': '{jslang}wcf.editor.table.insertTable{/jslang}',
174 'insert-column-left': '{jslang}wcf.editor.table.insertColumnLeft{/jslang}',
175 'insert-column-right': '{jslang}wcf.editor.table.insertColumnRight{/jslang}',
176 'insert-row-above': '{jslang}wcf.editor.table.insertRowAbove{/jslang}',
177 'insert-row-below': '{jslang}wcf.editor.table.insertRowBelow{/jslang}',
178
179 // size
180 'remove-size': '{jslang}wcf.editor.button.size.removeSize{/jslang}',
181
182 // color
183 'remove-color': '{jslang}wcf.editor.button.color.removeColor{/jslang}',
184
185 // font
186 'remove-font': '{jslang}wcf.editor.button.font.removeFont{/jslang}'
187 }
188 },
189 linkify: false,
190 linkSize: 0xBADC0DED, // some random value to disable truncating
191 minHeight: 200,
192 pasteImages: {if $__wcf->getBBCodeHandler()->isAvailableBBCode('attach')}true{else}false{/if},
193 pastePlainText: {if !$__wcf->user->userID || $__wcf->user->editorPastePreserveFormatting}false{else}true{/if},
194 plugins: [
195 // Imperavi
196 'alignment',
197 'source',
198 'table',
199
200 // WoltLab specials
201 'WoltLabBlock',
202 'WoltLabEvent',
203 'WoltLabKeydown',
204
205 // WoltLab core
206 'WoltLabAttachment',
207 'WoltLabAutosave',
208 'WoltLabCaret',
209 'WoltLabClean',
210 'WoltLabCode',
211 {if $__wcf->getBBCodeHandler()->isAvailableBBCode('color')}'WoltLabColor',{/if}
212 'WoltLabDragAndDrop',
213 {if $__wcf->getBBCodeHandler()->isAvailableBBCode('font')}'WoltLabFont',{/if}
214 'WoltLabFullscreen',
215 {if $__wcf->getBBCodeHandler()->isAvailableBBCode('html')}'WoltLabHtml',{/if}
216 'WoltLabImage',
217 'WoltLabIndent',
218 //'WoltLabInlineCode',
219 'WoltLabInsert',
220 'WoltLabKeyup',
221 'WoltLabLine',
222 {if $__wcf->getBBCodeHandler()->isAvailableBBCode('url')}'WoltLabLink',{/if}
223 'WoltLabList',
224 'WoltLabModal',
225 'WoltLabObserve',
226 'WoltLabPaste',
227 'WoltLabQuote',
228 'WoltLabReply',
229 {if $__wcf->getBBCodeHandler()->isAvailableBBCode('size')}'WoltLabSize',{/if}
230 'WoltLabSmiley',
231 'WoltLabSource',
232 'WoltLabSpoiler',
233 'WoltLabTable',
234 'WoltLabUtils'
235 ],
236 toolbarFixed: false,
237 woltlab: {
238 allowImages: {if $__wcf->getBBCodeHandler()->isAvailableBBCode('img')}true{else}false{/if},
239 attachments: (elDataBool(element, 'disable-attachments') === false),
240 attachmentThumbnailWidth: {@ATTACHMENT_THUMBNAIL_WIDTH},
241 autosave: autosave,
242 allowedInlineStyles: allowedInlineStyles,
243 buttons: buttonOptions,
244 buttonMobile: buttonMobile,
245 customButtons: customButtons,
246 forceSecureImages: {if MESSAGE_FORCE_SECURE_IMAGES}true{else}false{/if},
247 highlighters: highlighters,
248 images: {
249 external: {if IMAGE_ALLOW_EXTERNAL_SOURCE}true{else}false{/if},
250 secureOnly: {if MESSAGE_FORCE_SECURE_IMAGES}true{else}false{/if},
251 whitelist: [
252 {implode from=$__wcf->getBBCodeHandler()->getImageExternalSourceWhitelist() item=$hostname}'{$hostname|encodeJS}'{/implode}
253 ]
254 },
255 media: {if $__wcf->session->getPermission('admin.content.cms.canUseMedia')}true{else}false{/if},
256 mediaUrl: '{link controller='Media' id=-123456789 thumbnail='void' forceFrontend=true}{/link}'
257 }
258 };
259
260 // The caret is misaligned in Safari 13+ when using \u200b.
261 if (Environment.browser() === 'safari') {
262 config.emptyHtml = '<p><br></p>';
263 }
264
265 // user mentions
266 if (elDataBool(element, 'support-mention')) {
267 config.plugins.push('WoltLabMention');
268 }
269
270 // media
271 {if $__wcf->session->getPermission('admin.content.cms.canUseMedia')}
272 if (disableMedia) {
273 var index = config.buttons.indexOf('woltlabMedia');
274 if (index !== -1) {
275 config.buttons.splice(index, 1);
276 }
277 }
278 else {
279 config.plugins.push('WoltLabMedia');
280 }
281 {/if}
282
283 {if $__redactorConfig|isset}{@$__redactorConfig}{/if}
284 {assign var=__redactorConfig value=''}
285
286 {event name='redactorConfig'}
287
288 // load the button plugin last to ensure all buttons have been initialized
289 // already and we can safely add all icons
290 config.plugins.push('WoltLabButton');
291
292 var content = element.value;
293 element.value = '';
294
295 config.callbacks = config.callbacks || { };
296 config.callbacks.init = function() {
297 // slight delay to allow Redactor to initialize itself
298 window.setTimeout(function() {
299 if (content === '' && (Environment.platform() === 'ios' || Environment.browser() === 'safari')) {
300 content = '<p><br></p>';
301 }
302
303 content = UiRedactorMetacode.convertFromHtml(element.id, content);
304
305 var redactor = $(element).data('redactor');
306
307 // set code
308 redactor.code.start(content);
309 redactor.WoltLabImage.validateImages();
310
311 // set value
312 redactor.core.textarea().val(redactor.clean.onSync(redactor.$editor.html()));
313 redactor.code.html = false;
314
315 // work-around for autosave notice being stuck
316 window.setTimeout(function() {
317 var autosaveNotice = elBySel('.redactorAutosaveRestored.active', element.parentNode);
318 if (autosaveNotice) {
319 autosaveNotice.style.setProperty('position', 'static', '');
320
321 // force layout
322 //noinspection BadExpressionStatementJS
323 autosaveNotice.offsetTop;
324
325 autosaveNotice.style.removeProperty('position');
326 }
327 }, 10);
328 }, 10);
329 };
330
331 $(function () {
332 $(element).redactor(config);
333 });
334 });
335 });
336 </script>