Fixed pasting of HTML, preserving visuals as best as possible
authorAlexander Ebert <ebert@woltlab.com>
Thu, 29 May 2014 20:16:22 +0000 (22:16 +0200)
committerAlexander Ebert <ebert@woltlab.com>
Thu, 29 May 2014 20:16:22 +0000 (22:16 +0200)
com.woltlab.wcf/templates/wysiwyg.tpl
wcfsetup/install/files/js/3rdParty/redactor/plugins/wbbcode.js
wcfsetup/install/files/js/3rdParty/redactor/plugins/wmonkeypatch.js

index d8c30281caa88b5a2ab77d18fe573b7c5a072838..66b8b5b018325284b5664206a0d8bbce35f803eb 100644 (file)
@@ -36,6 +36,7 @@ $(function() {
                var $config = {
                        buttons: $buttons,
                        minHeight: 200,
+                       imageResizable: false,
                        plugins: [ 'wutil',  'wmonkeypatch', 'wbutton', 'wbbcode',  'wfontcolor', 'wfontfamily', 'wfontsize' ],
                        wautosave: {
                                active: ($autosave) ? true : false,
index bedf4ea58e23b0397e98cabe0c9ee924bd1d4afd..cc615d81615b953fdac44c107474c93a89600e08 100644 (file)
@@ -26,6 +26,9 @@ RedactorPlugins.wbbcode = {
                                this.toggle();
                        }
                }, this);
+               
+               this.opts.pasteBeforeCallback = $.proxy(this._wPasteBeforeCallback, this);
+               this.opts.pasteAfterCallback = $.proxy(this._wPasteAfterCallback, this);
        },
        
        /**
@@ -142,6 +145,28 @@ RedactorPlugins.wbbcode = {
                html = html.replace(/<br>\n<pre>\n/g, '');
                html = html.replace(/<\/pre>\n<br>\n/g, '');
                
+               html = html.replace(/<br>(?:\n<br>)+/g, function(match) {
+                       var $count = match.match(/<br>/g);
+                       return '@@@' + $count.length + '@@@';
+               });
+               html = html.replace(/\n@@@/g, '@@@');
+               html = html.replace(/@@@(\d+)@@@/g, function(match, times) {
+                       var $tmp = '<br>';
+                       for (var $i = 1; $i < times; $i++) {
+                               $tmp += '\n<br>';
+                       }
+                       
+                       return $tmp;
+               });
+               
+               // drop line break if there is a succeeding <br>
+               /*html = html.replace(/(<br>)?\n<br>/g, function(match, br) {
+                       console.debug("br was matched:");
+                       console.debug(match);
+                       
+                       return br ? match : '<br>';
+               });*/
+               
                // drop <br>, they are pointless because the editor already adds a newline after them
                html = html.replace(/<br>/g, '');
                html = html.replace(/&nbsp;/gi," ");
@@ -519,5 +544,56 @@ RedactorPlugins.wbbcode = {
                data = data.replace(/\[code\][\S\s]*?\[\/code\]/, '<pre>$&</pre>');
                
                this.$source.val(data);
+       },
+       
+       /**
+        * Converts certain HTML elements prior to paste in order to preserve formattings.
+        * 
+        * @param       string          html
+        * @return      string
+        */
+       _wPasteBeforeCallback: function(html) {
+               var $levels = {
+                       1: 24,
+                       2: 22,
+                       3: 18,
+                       4: 14,
+                       5: 12,
+                       6: 10
+               };
+               
+               // replace <h1> ... </h6> tags
+               html = html.replace(/<h([1-6])[^>]+>/g, function(match, level) {
+                       return '[size=' + $levels[level] + ']';
+               });
+               html = html.replace(/<\/h[1-6]>/g, '[/size]');
+               
+               return html;
+       },
+       
+       /**
+        * Restores and fixes formatting before inserting pasted HTML into the editor.
+        * 
+        * @param       string          html
+        * @return      string
+        */
+       _wPasteAfterCallback: function(html) {
+               // restore font size
+               html = html.replace(/\[size=(\d+)\]/g, '<p><inline style="font-size: $1pt">');
+               html = html.replace(/\[\/size\]/g, '</inline></p>');
+               
+               // replace <p /> with <p>...<br><br></p>
+               html = html.replace(/<p>([\s\S]*?)<\/p>/g, '<p>$1<br><br></p>');
+               
+               // drop <header />
+               html = html.replace(/<header[^>]*>/g, '');
+               html = html.replace(/<\/header>/g, '');
+               
+               html = html.replace(/<div>.*?<\/div>/g, '<p>$1<br></p>');
+               
+               // drop lonely divs
+               html = html.replace(/<\/?div>/g, '');
+               
+               return html;
        }
 };
index 807cb949ed3556afaae04c100371171bbf717ebe..6c36e361d6745f69c57211a0c52ec61a66b18806 100644 (file)
@@ -23,8 +23,9 @@ RedactorPlugins.wmonkeypatch = {
                
                var $mpIndentingStart = this.indentingStart;
                this.indentingStart = function(cmd) {
-                       $mpIndentingStart.call(self, cmd);
-                       self.mpIndentingStart(cmd);
+                       if (self.mpIndentingStart(cmd)) {
+                               $mpIndentingStart.call(self, cmd);
+                       }
                };
                
                var $mpBuildEventKeydown = this.buildEventKeydown;
@@ -50,11 +51,6 @@ RedactorPlugins.wmonkeypatch = {
                        $mpModalInit.call(self, title, content, width, callback);
                };
                
-               var $mpImageResizeControls = this.imageResizeControls;
-               this.imageResizeControls = function(image) {
-                       return $mpImageResizeControls.call(self, image).hide();
-               };
-               
                this.setOption('modalOpenedCallback', $.proxy(this.modalOpenedCallback, this));
                
                this.modalTemplatesInit();
@@ -98,19 +94,11 @@ RedactorPlugins.wmonkeypatch = {
         * @param       string          cmd
         */
        mpIndentingStart: function(cmd) {
-               if (cmd === 'indent') {
-                       var block = this.getBlock();
-                       if (block.tagName === 'DIV' && block.getAttribute('data-tagblock') !== null) {
-                               this.selectionSave();
-                               
-                               // drop the indention block again. bye bye block
-                               block = $(block);
-                               block.replaceWith(block.html());
-                               
-                               this.selectionRestore();
-                               this.sync();
-                       }
+               if (this.getBlock().tagName == 'LI') {
+                       return true;
                }
+               
+               return false;
        },
        
        /**