From: Alexander Ebert Date: Sun, 30 Mar 2014 11:14:33 +0000 (+0200) Subject: Improved Redactor integration X-Git-Tag: 2.1.0_Alpha_1~935 X-Git-Url: https://git.stricted.de/?a=commitdiff_plain;h=22459e140be32e67492181aa5599ccb7972dd277;p=GitHub%2FWoltLab%2FWCF.git Improved Redactor integration The biggest change was the transition to use

instead of
(disabled linebreaks mode), because attempts to style a lonely textnode (with no actual parent element except the editor container) causes a lot of issues. Furthermore I have started to modify the HTML of Redactor's modal overlays, they (partially) mimic the appearance of WCF's overlays. --- diff --git a/com.woltlab.wcf/templates/wysiwyg.tpl b/com.woltlab.wcf/templates/wysiwyg.tpl index 91903e4b03..384f49d3ac 100644 --- a/com.woltlab.wcf/templates/wysiwyg.tpl +++ b/com.woltlab.wcf/templates/wysiwyg.tpl @@ -19,9 +19,8 @@ $(function() { var $autosave = $textarea.data('autosave'); var $config = { buttons: $buttons, - linebreaks: true, minHeight: 200, - plugins: [ 'wutil', 'wmonkeypatch', 'wbutton', 'wbbcode', 'wfontcolor' ], + plugins: [ 'wutil', 'wmonkeypatch', 'wbutton', 'wbbcode', 'wfontcolor', 'wfontfamily', 'wfontsize' ], wautosave: { active: ($autosave) ? true : false, key: ($autosave) ? $autosave : '', @@ -39,6 +38,8 @@ $(function() { '{@$__wcf->getPath()}js/3rdParty/redactor/plugins/wbbcode.js', '{@$__wcf->getPath()}js/3rdParty/redactor/plugins/wbutton.js', '{@$__wcf->getPath()}js/3rdParty/redactor/plugins/wfontcolor.js', + '{@$__wcf->getPath()}js/3rdParty/redactor/plugins/wfontfamily.js', + '{@$__wcf->getPath()}js/3rdParty/redactor/plugins/wfontsize.js', '{@$__wcf->getPath()}js/3rdParty/redactor/plugins/wmonkeypatch.js', '{@$__wcf->getPath()}js/3rdParty/redactor/plugins/wutil.js' {event name='javascriptFiles'} diff --git a/wcfsetup/install/files/js/3rdParty/redactor/plugins/wbbcode.js b/wcfsetup/install/files/js/3rdParty/redactor/plugins/wbbcode.js index 747f93df8a..31cd080b63 100644 --- a/wcfsetup/install/files/js/3rdParty/redactor/plugins/wbbcode.js +++ b/wcfsetup/install/files/js/3rdParty/redactor/plugins/wbbcode.js @@ -105,6 +105,7 @@ RedactorPlugins.wbbcode = { */ toggle: function(direct) { if (this.opts.visual) { + this._convertParagraphs(); this.toggleCode(direct); this._convertFromHtml(); @@ -137,21 +138,27 @@ RedactorPlugins.wbbcode = { return $string; }, + _convertParagraphs: function() { + this.$editor.find('p').replaceWith(function() { + var $html = $(this).html(); + if ($html == '
') { + // an empty line is presented by


but in the textarea this equals only a single new line + return $html; + } + + return $html + '
';; + }); + this.sync(); + }, + /** * Converts source contents from HTML into BBCode. */ _convertFromHtml: function() { var html = this.$source.val(); - if (html == '
' || html == '


') { - return ""; - } - - // Convert
to line breaks. - html = html.replace(/
<\/p>/gi,"\n"); - html = html.replace(/]).*?>/gi, '\r\n'); - html = html.replace(/

/gi,""); - html = html.replace(/<\/p>/gi,"\n"); + // drop
, they are pointless because the editor already adds a newline after them + html = html.replace(/
/g, ''); html = html.replace(/ /gi," "); // [email] @@ -212,7 +219,9 @@ RedactorPlugins.wbbcode = { html = html.replace(/([\s\S]*?)<\/span>/gi, "[size=$1]$2[/size]"); // [font] - html = html.replace(/([\s\S]*?)<\/span>/gi, "[font='$1']$2[/font]"); + html = html.replace(/([\s\S]*?)<\/span>/gi, function(match, fontFamily, text) { + return "[font='" + fontFamily.replace(/'/g, '') + "']" + text + "[/font]"; + }); // [align] html = html.replace(/

([\s\S]*?)<\/div>/gi, "[align=$1]$2[/align]"); @@ -275,6 +284,13 @@ RedactorPlugins.wbbcode = { // Restore %20 html = html.replace(/%20/g, ' '); + // trim leading tabs + var $tmp = html.split("\n"); + for (var $i = 0, $length = $tmp.length; $i < $length; $i++) { + $tmp[$i] = $tmp[$i].replace(/^\s*/, ''); + } + html = $tmp.join("\n"); + this.$source.val(html); }, @@ -296,9 +312,6 @@ RedactorPlugins.wbbcode = { data = data.replace(/>/g, '>'); //} - // Convert line breaks to
. - data = data.replace(/(?:\r\n|\n|\r)/g, '
'); - /*if ($pasted) { $pasted = false; // skip @@ -396,6 +409,21 @@ RedactorPlugins.wbbcode = { // remove "javascript:" data = data.replace(/(javascript):/gi, '$1:'); + // unify line breaks + data = data.replace(/(\r|\r\n)/, "\n"); + + // convert line breaks into

or empty lines to


+ var $tmp = data.split("\n"); + data = ''; + for (var $i = 0, $length = $tmp.length; $i < $length; $i++) { + var $line = $.trim($tmp[$i]); + if (!$line) { + $line = '
'; + } + + data += '

' + $line + '

'; + } + // insert codes if ($.getLength($cachedCodes)) { for (var $key in $cachedCodes) { diff --git a/wcfsetup/install/files/js/3rdParty/redactor/plugins/wbutton.js b/wcfsetup/install/files/js/3rdParty/redactor/plugins/wbutton.js index 4f6bf590de..19338cb0ff 100644 --- a/wcfsetup/install/files/js/3rdParty/redactor/plugins/wbutton.js +++ b/wcfsetup/install/files/js/3rdParty/redactor/plugins/wbutton.js @@ -24,14 +24,24 @@ RedactorPlugins.wbutton = { this._addBBCodeButton(__REDACTOR_BUTTONS[$i]); } + // this list contains overrides for built-in buttons, if a button is not present + // Redactor's own icon will be used instead. This solves the problem of FontAwesome + // not providing an icon for everything we need (especially the core stuff) var $faIcons = { - 'alignment': 'align-left', - 'deleted': 'strikethrough', - 'html': 'square-o', - 'image': 'picture-o', - 'orderedlist': 'list-ol', - 'smiley': 'smile-o', - 'unorderedlist': 'list-ul' + 'html': 'fa-square-o', + 'bold': 'fa-bold', + 'italic': 'fa-italic', + 'underline': 'fa-underline', + 'deleted': 'fa-strikethrough', + 'subscript': 'fa-subscript', + 'superscript': 'fa-superscript', + 'orderedlist': 'fa-list-ol', + 'unorderedlist': 'fa-list-ul', + 'outdent': 'fa-outdent', + 'indent': 'fa-indent', + 'link': 'fa-link', + 'alignment': 'fa-align-left', + 'table': 'fa-table' }; var $buttons = this.getOption('buttons'); @@ -47,26 +57,29 @@ RedactorPlugins.wbutton = { // check if button does not exist var $buttonObj = this.buttonGet($button); - var $className = ($faIcons[$button]) ? $faIcons[$button] : $button; if ($buttonObj.length) { - this.buttonAwesome($button, 'fa-' + $className); + if ($faIcons[$button]) { + this.buttonAwesome($button, $faIcons[$button]); + } } else { - this._addCoreButton($button, $className, $lastButton); + this._addCoreButton($button, ($faIcons[$button] ? $faIcons[$button] : null), $lastButton); } $lastButton = $button; } }, - _addCoreButton: function(buttonName, className, insertAfter) { + _addCoreButton: function(buttonName, faIcon, insertAfter) { var $button = this.buttonBuild(buttonName, { title: buttonName, exec: buttonName }, false); $('
  • ').append($button).insertAfter(this.buttonGet(insertAfter).parent()); - this.buttonAwesome(buttonName, 'fa-' + className); + if (faIcon !== null) { + this.buttonAwesome(buttonName, faIcon); + } }, /** diff --git a/wcfsetup/install/files/js/3rdParty/redactor/plugins/wfontcolor.js b/wcfsetup/install/files/js/3rdParty/redactor/plugins/wfontcolor.js index fe03e2c9d8..f2468f12b3 100644 --- a/wcfsetup/install/files/js/3rdParty/redactor/plugins/wfontcolor.js +++ b/wcfsetup/install/files/js/3rdParty/redactor/plugins/wfontcolor.js @@ -14,9 +14,10 @@ RedactorPlugins.wfontcolor = { init: function() { this._createFontColorDropdown(); - this.buttonAdd('fontcolor', this.opts.curLang.fontcolor, $.proxy(function(btnName, $button, btnObject, e) { + this.buttonReplace('fontcolor', 'fontcolor', this.opts.curLang.fontcolor, $.proxy(function(btnName, $button, btnObject, e) { this.dropdownShow(e, btnName); - }, this)) + }, this)); + //this.buttonAwesome('fontcolor', 'fa-font'); }, /** @@ -55,14 +56,19 @@ RedactorPlugins.wfontcolor = { _onColorPick: function(event) { event.preventDefault(); - this.bufferSet(); + //this.bufferSet(); - this.$editor.focus(); - this.inlineRemoveStyle('color'); + //this.$editor.focus(); - var $type = $(event.currentTarget).data('color'); - if ($type !== 'none') this.inlineSetStyle('color', $type); - if (this.opts.air) this.$air.fadeOut(100); - this.sync(); + var $color = $(event.currentTarget).data('color'); + if ($color === 'none') { + this.inlineRemoveStyle('color'); + } + else { + this.inlineSetStyle('color', $color); + } + + /*if (this.opts.air) this.$air.fadeOut(100); + this.sync();*/ } }; \ No newline at end of file diff --git a/wcfsetup/install/files/js/3rdParty/redactor/plugins/wfontfamily.js b/wcfsetup/install/files/js/3rdParty/redactor/plugins/wfontfamily.js new file mode 100644 index 0000000000..88a1e1cc75 --- /dev/null +++ b/wcfsetup/install/files/js/3rdParty/redactor/plugins/wfontfamily.js @@ -0,0 +1,57 @@ +if (!RedactorPlugins) var RedactorPlugins = {}; + +/** + * Provides a font family picker, this is actually a heavily modified version of Imperavi's 'fontfamily' plugin. + * + * @author Alexander Ebert + * @copyright 2001-2014 WoltLab GmbH + * @license GNU Lesser General Public License + */ +RedactorPlugins.wfontfamily = { + init: function () { + var $fonts = { + 'Arial': "Arial, Helvetica, sans-serif", + 'Comic Sans MS': "Comic Sans MS, cursive", + 'Courier New': "Consolas, Courier New, Courier, monospace", + 'Georgia': "Georgia, serif", + 'Lucida Sans Unicode': "Lucida Sans Unicode, Lucida Grande, sans-serif", + 'Tahoma': "Tahoma, Geneva, sans-serif", + 'Times New Roman': "Times New Roman, Times, serif", + 'Trebuchet MS': "Trebuchet MS, Helvetica, sans-serif", + 'Verdana': "Verdana, Geneva, sans-serif" + }; + + var $dropdown = { }; + var $i = 0; + var self = this; + $.each($fonts, function(title, value) { + $dropdown['fontFamily' + $i] = { + title: title, + className: 'wfontfamily-' + $i, + callback: function() { + self.inlineSetStyle('font-family', value); + } + }; + + $i++; + }); + $dropdown['separator'] = { name: 'separator' }; + $dropdown['remove'] = { + title: 'remove font', + callback: function() { + this.inlineRemoveStyle('font-family'); + } + }; + + this.buttonReplace('fontfamily', 'wfontfamily', 'Change font family', false, $dropdown); + this.buttonGet('wfontfamily').addClass('re-fontfamily'); + + // modify dropdown to reflect each font family + $dropdown = this.$toolbar.find('.redactor_dropdown_box_wfontfamily'); + $i = 0; + $.each($fonts, function(title, value) { + $dropdown.children('.wfontfamily-' + $i).removeClass('wfontfamily-' + $i).css('font-family', value); + $i++; + }); + } +}; \ No newline at end of file diff --git a/wcfsetup/install/files/js/3rdParty/redactor/plugins/wfontsize.js b/wcfsetup/install/files/js/3rdParty/redactor/plugins/wfontsize.js new file mode 100644 index 0000000000..870465487b --- /dev/null +++ b/wcfsetup/install/files/js/3rdParty/redactor/plugins/wfontsize.js @@ -0,0 +1,50 @@ +if (!RedactorPlugins) var RedactorPlugins = {}; + +/** + * Provides a font size picker, this is actually a heavily modified version of Imperavi's 'fontsize' plugin. + * + * @author Alexander Ebert + * @copyright 2001-2014 WoltLab GmbH + * @license GNU Lesser General Public License + */ +RedactorPlugins.wfontsize = { + init: function () { + var $fontSizes = [ 8, 10, 12, 14, 18, 24, 36 ]; + + var $dropdown = { }; + var self = this; + for (var $i = 0, $length = $fontSizes.length; $i < $length; $i++) { + var $fontSize = $fontSizes[$i]; + + $dropdown['fontSize' + $i] = { + title: $fontSize, + className: 'wfontsize-' + $fontSize, + callback: function() { + self.inlineSetStyle('font-size', $fontSize + 'pt'); + } + }; + } + + $dropdown['separator'] = { name: 'separator' }; + $dropdown['remove'] = { + title: 'remove font size', + callback: function() { + this.inlineRemoveStyle('font-size'); + } + }; + + this.buttonReplace('fontsize', 'wfontsize', 'Change font size', false, $dropdown); + this.buttonGet('wfontsize').addClass('re-fontsize'); + + // modify dropdown to reflect each font family + $dropdown = this.$toolbar.find('.redactor_dropdown_box_wfontsize'); + for (var $i = 0, $length = $fontSizes.length; $i < $length; $i++) { + var $fontSize = $fontSizes[$i]; + + var $listItem = $dropdown.children('a.wfontsize-' + $fontSize).removeClass('wfontsize-' + $fontSizes).css('font-size', $fontSize + 'pt'); + if ($fontSize > 18) { + $listItem.css('line-height', '1em'); + } + } + } +}; diff --git a/wcfsetup/install/files/js/3rdParty/redactor/plugins/wmonkeypatch.js b/wcfsetup/install/files/js/3rdParty/redactor/plugins/wmonkeypatch.js index 431c045a10..1c6f9374d5 100644 --- a/wcfsetup/install/files/js/3rdParty/redactor/plugins/wmonkeypatch.js +++ b/wcfsetup/install/files/js/3rdParty/redactor/plugins/wmonkeypatch.js @@ -42,6 +42,17 @@ RedactorPlugins.wmonkeypatch = { self.$source.height($height); }; + + var $mpModalInit = this.modalInit; + this.modalInit = function(title, content, width, callback) { + self.mpModalInit(); + + $mpModalInit.call(self, title, content, width, callback); + }; + + this.setOption('modalOpenedCallback', $.proxy(this.modalOpenedCallback, this)); + + this.modalTemplatesInit(); }, /** @@ -92,4 +103,135 @@ RedactorPlugins.wmonkeypatch = { } } }, + + /** + * Provides WCF-like overlays. + */ + modalTemplatesInit: function() { + this.setOption('modal_image', + '
    ' + + '
    ' + + '
    ' + + '
    ' + + '
    ' + + '
    ' + + '
    ' + + '' + + '
    ' + ); + + this.setOption('modal_table', + '
    ' + + '
    ' + + '
    ' + + '
    ' + + '
    ' + + '
    ' + + '
    ' + + '
    ' + + '
    ' + + '
    ' + + '
    ' + + '' + + '
    ' + ); + + $.extend( this.opts, { + modal_file: String() + + '
    ' + + '' + + '
    ' + + '' + + '' + + '
    ' + + '' + + '
    ' + + '
    ' + + '
    ', + + modal_image_edit: String() + + '
    ' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '
    ' + + '
    ' + + '' + + '' + + '' + + '
    ', + + // img + + modal_link: String() + + '' + + '
    ' + + '' + + '' + + '
    ', + + modal_video: String() + + '
    ' + + '
    ' + + '' + + '' + + '
    ' + + '
    ' + + '
    ' + + '' + + '' + + '
    ' + + }); + }, + + mpModalInit: function() { + // modal overlay + if (!$('#redactor_modal_overlay').length) { + this.$overlay = $('
    ').css({ height: '100%', zIndex: 50000 }).hide().appendTo(document.body); + } + + if (!$('#redactor_modal').length) { + this.$modal = $('
    ').css({ display: 'none', zIndex: 50001 }).appendTo(document.body); + $('
    ').appendTo(this.$modal); + $('
    ').appendTo(this.$modal); + } + + this.$modal.children('.dialogContent').removeClass('dialogForm'); + }, + + modalOpenedCallback: function() { + // handle positioning of form submit controls + var $heightDifference = 0; + if (this.$modal.find('.formSubmit').length) { + $heightDifference = this.$modal.find('.formSubmit').outerHeight(); + + this.$modal.children('.dialogContent').addClass('dialogForm').css({ marginBottom: $heightDifference + 'px' }); + } + else { + this.$modal.children('.dialogContent').removeClass('dialogForm').css({ marginBottom: '0px' }); + } + + // fix position + var $dimensions = this.$modal.getDimensions('outer'); + this.$modal.css({ + marginLeft: -1 * Math.round($dimensions.width / 2) + 'px', + marginTop: -1 * Math.round($dimensions.height / 2) + 'px' + }); + } } \ No newline at end of file diff --git a/wcfsetup/install/files/js/3rdParty/redactor/redactor.css b/wcfsetup/install/files/js/3rdParty/redactor/redactor.css index 074abff8a6..3da3de1c2c 100644 --- a/wcfsetup/install/files/js/3rdParty/redactor/redactor.css +++ b/wcfsetup/install/files/js/3rdParty/redactor/redactor.css @@ -188,6 +188,11 @@ body .redactor_box_fullscreen { background: none !important; box-shadow: none !important; } + +.redactor_editor p { + margin-bottom: 0 !important; +} + .redactor_editor iframe, .redactor_editor object, .redactor_editor hr { @@ -685,7 +690,7 @@ body .redactor_air .redactor_toolbar { /* MODAL */ -#redactor_modal_overlay { +/*#redactor_modal_overlay { position: fixed; top: 0; left: 0; @@ -780,7 +785,7 @@ body .redactor_air .redactor_toolbar { } #redactor_modal_close:hover { color: #000; -} +}*/ .redactor_input { width: 99%; font-size: 14px; diff --git a/wcfsetup/install/files/js/3rdParty/redactor/redactor.js b/wcfsetup/install/files/js/3rdParty/redactor/redactor.js index 73665de1b1..8f1e5dd092 100644 --- a/wcfsetup/install/files/js/3rdParty/redactor/redactor.js +++ b/wcfsetup/install/files/js/3rdParty/redactor/redactor.js @@ -4136,10 +4136,12 @@ if ((range.collapsed || range.startContainer === range.endContainer) && el && !this.nodeTestBlocks(el)) { + console.debug("1"); $(el)[type](attr, value); } else { + console.debug("2"); this.document.execCommand('fontSize', false, 4 ); var fonts = this.$editor.find('font'); diff --git a/wcfsetup/install/files/style/message.less b/wcfsetup/install/files/style/message.less index 383ad85302..d127b7b499 100644 --- a/wcfsetup/install/files/style/message.less +++ b/wcfsetup/install/files/style/message.less @@ -937,7 +937,7 @@ li:nth-child(2n+1) .message { } > li { - &:last-child, + &:last-of-type, &.separator { border-right: 1px solid @wcfButtonBorderColor; } @@ -968,7 +968,7 @@ li:nth-child(2n+1) .message { } } - &.re-icon:before { + &.fa-redactor-btn:before { content: ""; } }