Added support for attachments
authorAlexander Ebert <ebert@woltlab.com>
Mon, 6 Jul 2015 08:58:05 +0000 (10:58 +0200)
committerAlexander Ebert <ebert@woltlab.com>
Mon, 6 Jul 2015 08:58:05 +0000 (10:58 +0200)
wcfsetup/install/files/js/3rdParty/redactor/plugins/wbbcode.js
wcfsetup/install/files/js/3rdParty/redactor/plugins/wmonkeypatch.js
wcfsetup/install/files/js/WoltLab/WCF/BBCode/FromHtml.js
wcfsetup/install/files/js/WoltLab/WCF/BBCode/ToHtml.js

index 9f9eb20a12e6058df20d568e5c7407ec1a90c9ab..e8a2b8f026fd4ae1a99a5ab10d8f037e932fa3a7 100644 (file)
@@ -295,78 +295,20 @@ RedactorPlugins.wbbcode = function() {
                 * @param       string          message
                 */
                convertFromHtml: function(message) {
-                       // DEBUG ONLY
-                       return __REDACTOR_AMD_DEPENDENCIES.BBCodeFromHTML.convert(message);
+                       var obj = { html: message };
                        
-                       var $searchFor = [ ];
+                       /** @deprecated legacy event */
+                       WCF.System.Event.fireEvent('com.woltlab.wcf.redactor', 'beforeConvertFromHtml', obj);
                        
-                       WCF.System.Event.fireEvent('com.woltlab.wcf.redactor', 'beforeConvertFromHtml', { html: html });
+                       /** @deprecated legacy event */
+                       WCF.System.Event.fireEvent('com.woltlab.wcf.redactor', 'convertFromHtml', obj);
                        
-                       // remove zero-width space sometimes slipping through
-                       html = html.replace(/&#(8203|x200b);/g, '');
+                       obj.html = __REDACTOR_AMD_DEPENDENCIES.BBCodeFromHTML.convert(obj.html);
                        
-                       // revert conversion of special characters
-                       html = html.replace(/&trade;/gi, '\u2122');
-                       html = html.replace(/&copy;/gi, '\u00a9');
-                       html = html.replace(/&hellip;/gi, '\u2026');
-                       html = html.replace(/&mdash;/gi, '\u2014');
-                       html = html.replace(/&dash;/gi, '\u2010');
+                       /** @deprecated legacy event */
+                       WCF.System.Event.fireEvent('com.woltlab.wcf.redactor', 'afterConvertFromHtml', obj);
                        
-                       // attachments
-                       html = html.replace(/<img([^>]*?)class="[^"]*redactorEmbeddedAttachment[^"]*"([^>]*?)>/gi, function(match, attributesBefore, attributesAfter) {
-                               var $attributes = attributesBefore + ' ' + attributesAfter;
-                               var $attachmentID;
-                               if ($attributes.match(/data-attachment-id="(\d+)"/)) {
-                                       $attachmentID = RegExp.$1;
-                               }
-                               else {
-                                       return match;
-                               }
-                               
-                               var $float = 'none';
-                               var $width = null;
-                               
-                               if ($attributes.match(/style="([^"]+)"/)) {
-                                       var $styles = RegExp.$1.split(';');
-                                       
-                                       for (var $i = 0; $i < $styles.length; $i++) {
-                                               var $style = $.trim($styles[$i]);
-                                               if ($style.match(/^float: (left|right)$/)) {
-                                                       $float = RegExp.$1;
-                                               }
-                                               else if ($style.match(/^width: (\d+)px$/)) {
-                                                       $width = RegExp.$1;
-                                               }
-                                       }
-                                       
-                                       if ($width !== null) {
-                                               return '[attach=' + $attachmentID + ',' + $float + ',' + $width + '][/attach]';
-                                       }
-                                       else if ($float !== 'none') {
-                                               return '[attach=' + $attachmentID + ',' + $float + '][/attach]';
-                                       }
-                               }
-                               
-                               return '[attach=' + $attachmentID + '][/attach]';
-                       });
-                       
-                       WCF.System.Event.fireEvent('com.woltlab.wcf.redactor', 'convertFromHtml', { html: html });
-                       
-                       // Remove remaining tags.
-                       html = html.replace(/<[^(<|>)]+>/g, '');
-                       
-                       // Restore <, > and &
-                       html = html.replace(/&lt;/g, '<');
-                       html = html.replace(/&gt;/g, '>');
-                       html = html.replace(/&amp;/g, '&');
-                       
-                       // Restore ( and )
-                       html = html.replace(/%28/g, '(');
-                       html = html.replace(/%29/g, ')');
-                       
-                       WCF.System.Event.fireEvent('com.woltlab.wcf.redactor', 'afterConvertFromHtml', { html: html });
-                       
-                       return html;
+                       return obj.html;
                },
                
                /**
@@ -375,85 +317,23 @@ RedactorPlugins.wbbcode = function() {
                 * @param       string          message
                 */
                convertToHtml: function(message) {
-                       // DEBUG ONLY
-                       return __REDACTOR_AMD_DEPENDENCIES.BBCodeToHTML.convert(message);
+                       var obj = { data: message };
                        
-                       WCF.System.Event.fireEvent('com.woltlab.wcf.redactor', 'beforeConvertToHtml', { data: data });
-                       
-                       // remove 0x200B (unicode zero width space)
-                       data = this.wutil.removeZeroWidthSpace(data);
-                       
-                       // attachments
-                       var $attachmentUrl = this.wutil.getOption('woltlab.attachmentUrl');
-                       var $attachmentThumbnailUrl = this.wutil.getOption('woltlab.attachmentThumbnailUrl');
-                       if ($attachmentUrl) {
-                               var $imageAttachments = this.wbbcode._getImageAttachments();
-                               
-                               data = data.replace(/\[attach=(\d+)\]\[\/attach\]/g, function(match, attachmentID, alignment) {
-                                       attachmentID = parseInt(attachmentID);
-                                       
-                                       if ($imageAttachments[attachmentID] !== undefined) {
-                                               return '<img src="' + $attachmentThumbnailUrl.replace(/987654321/, attachmentID) + '" class="redactorEmbeddedAttachment redactorDisableResize" data-attachment-id="' + attachmentID + '" />';
-                                       }
-                                       
-                                       return match;
-                               });
-                               
-                               data = data.replace(/\[attach=(\d+),(left|right|none)\]\[\/attach\]/g, function(match, attachmentID, alignment) {
-                                       attachmentID = parseInt(attachmentID);
-                                       
-                                       if ($imageAttachments[attachmentID] !== undefined) {
-                                               var $style = '';
-                                               if (alignment === 'left' || alignment === 'right') {
-                                                       $style = 'float: ' + alignment + ';';
-                                                       
-                                                       if (alignment === 'left') {
-                                                               $style += 'margin: 0 15px 7px 0';
-                                                       }
-                                                       else {
-                                                               $style += 'margin: 0 0 7px 15px';
-                                                       }
-                                               }
-                                               
-                                               $style = ' style="' + $style + '"';
-                                               
-                                               return '<img src="' + $attachmentThumbnailUrl.replace(/987654321/, attachmentID) + '" class="redactorEmbeddedAttachment redactorDisableResize" data-attachment-id="' + attachmentID + '"' + $style + ' />';
-                                       }
-                                       
-                                       return match;
-                               });
-                               
-                               data = data.replace(/\[attach=(\d+),(left|right|none),(\d+)\]\[\/attach\]/g, function(match, attachmentID, alignment, width) {
-                                       attachmentID = parseInt(attachmentID);
-                                       
-                                       if ($imageAttachments[attachmentID] !== undefined) {
-                                               var $style = 'width: ' + width + 'px; max-height: ' + $imageAttachments[attachmentID].height + 'px; max-width: ' + $imageAttachments[attachmentID].width + 'px;';
-                                               if (alignment === 'left' || alignment === 'right') {
-                                                       $style += 'float: ' + alignment + ';';
-                                                       
-                                                       if (alignment === 'left') {
-                                                               $style += 'margin: 0 15px 7px 0';
-                                                       }
-                                                       else {
-                                                               $style += 'margin: 0 0 7px 15px';
-                                                       }
-                                               }
-                                               
-                                               $style = ' style="' + $style + '"';
-                                               
-                                               return '<img src="' + $attachmentUrl.replace(/987654321/, attachmentID) + '" class="redactorEmbeddedAttachment" data-attachment-id="' + attachmentID + '"' + $style + ' />';
-                                       }
-                                       
-                                       return match;
-                               });
-                       }
+                       /** @deprecated legacy event */
+                       WCF.System.Event.fireEvent('com.woltlab.wcf.redactor', 'beforeConvertToHtml', obj);
                        
-                       // remove "javascript:"
-                       data = data.replace(/(javascript):/gi, '$1<span></span>:');
+                       obj.data = __REDACTOR_AMD_DEPENDENCIES.BBCodeToHTML.convert(obj.data, {
+                               attachments: {
+                                       images: this.wbbcode._getImageAttachments(),
+                                       thumbnailUrl: this.wutil.getOption('woltlab.attachmentThumbnailUrl'),
+                                       url: this.wutil.getOption('woltlab.attachmentUrl')
+                               }
+                       });
                        
-                       WCF.System.Event.fireEvent('com.woltlab.wcf.redactor', 'afterConvertToHtml', { data: data });
+                       /** @deprecated legacy event */
+                       WCF.System.Event.fireEvent('com.woltlab.wcf.redactor', 'afterConvertToHtml', obj);
                        
-                       return data;
+                       return obj.data;
                },
                
                /**
index d83e38fcd88150e5e06c88902b8aef29bab9207f..7e5c6e2e7d41773da8de384d870b9a58a6592654 100644 (file)
@@ -225,14 +225,9 @@ RedactorPlugins.wmonkeypatch = function() {
                                                                }
                                                        }
                                                        else if (container.nextSibling === container.nextElementSibling) {
-                                                               console.debug("this!");
                                                                if (container.nextElementSibling.nodeName === 'KBD' || container.nextElementSibling.nodeName === 'BR') {
                                                                        setCaretBeforeOrAfter(container, false);
                                                                }
-                                                               else {
-                                                                       console.debug(container.nextSibling);
-                                                                       console.debug(container.nextElementSibling);
-                                                               }
                                                        }
                                                        
                                                        if (container === editor.lastElementChild) {
@@ -662,21 +657,12 @@ RedactorPlugins.wmonkeypatch = function() {
                        }).bind(this);
                        
                        // image.update
-                       var $moveImage = (function(image) {
-                               var $parent = image.parent();
-                               image = image.detach();
-                               image.prependTo($parent);
-                               
-                               this.caret.setAfter(image);
-                       }).bind(this);
-                       
                        this.image.update = (function(image) {
                                this.image.hideResize();
                                this.buffer.set();
                                
                                image.attr('src', $('#redactor-image-link-source').val());
                                this.image.setFloating(image);
-                               $moveImage(image);
                                
                                var link = $('#redactor-image-link-href').val().trim();
                                var anchor = image.closest('a', this.$editor[0]);
@@ -1113,14 +1099,13 @@ RedactorPlugins.wmonkeypatch = function() {
                                
                                var element = this.selection.getCurrent();
                                if (element === false) {
-                                       console.debug("nope");
                                        return;
                                }
                                
                                if (element.nodeType === Node.TEXT_NODE) {
                                        element = element.parentNode;
                                }
-                               console.debug(element);
+                               
                                if (element === this.$editor[0]) {
                                        return;
                                }
index 71aefab41308a5dfa8b405805758c567b0406bbb..f05b2bef0a7cb3fb84626f1457079c9d94f923f1 100644 (file)
@@ -188,15 +188,29 @@ define(['EventHandler', 'StringUtil', 'DOM/Traverse'], function(EventHandler, St
                        if (element.classList.contains('smiley')) {
                                // smiley
                                element.outerHTML = (addSmileyPadding(element, true) ? ' ' : '') + element.getAttribute('alt') + (addSmileyPadding(element, false) ? ' ' : '');
+                               return;
                        }
-                       else if (element.classList.contains('redactorEmbeddedAttachment')) {
-                               // TODO: handle attachments
+                       
+                       var float = element.style.getPropertyValue('float') || 'none';
+                       var width = element.style.getPropertyValue('width');
+                       width = (typeof width === 'string') ? ~~width.replace(/px$/, '') : 0;
+                       
+                       if (element.classList.contains('redactorEmbeddedAttachment')) {
+                               var attachmentId = element.getAttribute('data-attachment-id');
+                               
+                               if (width > 0) {
+                                       element.outerHTML = "[attach=" + attachmentId + "," + float + "," + width + "][/attach]";
+                               }
+                               else if (float !== 'none') {
+                                       element.outerHTML = "[attach=" + attachmentId + "," + float + "][/attach]";
+                               }
+                               else {
+                                       element.outerHTML = "[attach=" + attachmentId + "][/attach]";
+                               }
                        }
                        else {
                                // regular image
-                               var float = element.style.getPropertyValue('float') || 'none';
                                var source = element.src.trim();
-                               var width = ~~element.style.getPropertyValue('width').replace(/px$/, '') || 0;
                                
                                if (width > 0) {
                                        element.outerHTML = "[img='" + source + "'," + float + "," + width + "][/img]";
index ab11409b1fd6f5dffff5c16b44d567ef2443d7d5..7d7d87af7515766cf6a5cc429f4198947ee2d621 100644 (file)
@@ -1,7 +1,8 @@
-define(['EventHandler', 'Language', 'StringUtil', 'WoltLab/WCF/BBCode/Parser'], function(EventHandler, Language, StringUtil, BBCodeParser) {
+define(['Core', 'EventHandler', 'Language', 'StringUtil', 'WoltLab/WCF/BBCode/Parser'], function(Core, EventHandler, Language, StringUtil, BBCodeParser) {
        "use strict";
        
        var _bbcodes = null;
+       var _options = {};
        var _removeNewlineAfter = [];
        var _removeNewlineBefore = [];
        
@@ -10,7 +11,15 @@ define(['EventHandler', 'Language', 'StringUtil', 'WoltLab/WCF/BBCode/Parser'],
        function isHighlighter(value) { return __REDACTOR_CODE_HIGHLIGHTERS.hasOwnProperty(value); }
        
        var BBCodeToHtml = {
-               convert: function(message) {
+               convert: function(message, options) {
+                       _options = Core.extend({
+                               attachments: {
+                                       images: {},
+                                       thumbnailUrl: '',
+                                       url: ''
+                               }
+                       }, options);
+                       
                        this._convertSpecials(message);
                        
                        var stack = BBCodeParser.parse(message);
@@ -19,12 +28,19 @@ define(['EventHandler', 'Language', 'StringUtil', 'WoltLab/WCF/BBCode/Parser'],
                                this._initBBCodes();
                        }
                        
-                       var item;
+                       var item, value;
                        for (var i = 0, length = stack.length; i < length; i++) {
                                item = stack[i];
                                
                                if (typeof item === 'object') {
-                                       stack[i] = this._replace(stack, item, i);
+                                       value = this._replace(stack, item, i);
+                                       if (Array.isArray(value)) {
+                                               stack[i] = (value[0] === null ? item.source : value[0]);
+                                               stack[item.pair] = (value[1] === null ? stack[item.pair].source : value[1]);
+                                       }
+                                       else {
+                                               stack[i] = value;
+                                       }
                                }
                        }
                        
@@ -62,6 +78,7 @@ define(['EventHandler', 'Language', 'StringUtil', 'WoltLab/WCF/BBCode/Parser'],
                                tt: 'kbd',
                                
                                // callback replacement
+                               attach: this._replaceAttachment.bind(this),
                                color: this._replaceColor.bind(this),
                                code: this._replaceCode.bind(this),
                                email: this._replaceEmail.bind(this),
@@ -86,11 +103,7 @@ define(['EventHandler', 'Language', 'StringUtil', 'WoltLab/WCF/BBCode/Parser'],
                        
                        if (replace === undefined) {
                                // treat as plain text
-                               console.debug(item);
-                               console.debug(stack);
-                               stack[item.pair] = stack[item.pair].source;
-                               
-                               return item.source;
+                               return [null, null];
                        }
                        
                        if (_removeNewlineAfter.indexOf(item.name) !== -1) {
@@ -125,15 +138,57 @@ define(['EventHandler', 'Language', 'StringUtil', 'WoltLab/WCF/BBCode/Parser'],
                        this._replaceSmilies(stack);
                        
                        if (typeof replace === 'string') {
-                               stack[item.pair] = '</' + replace + '>';
-                               
-                               return '<' + replace + '>';
+                               return ['<' + replace + '>', '</' + replace + '>'];
                        }
                        else {
                                return replace(stack, item, index);
                        }
                },
                
+               _replaceAttachment: function(stack, item, index) {
+                       var attachmentId = 0, attributes = item.attributes, length = attributes.length;
+                       if (!_options.attachments.url) {
+                               length = 0;
+                       }
+                       else if (length > 0) {
+                               attachmentId = ~~attributes[0];
+                               if (!_options.attachments.images.hasOwnProperty(attachmentId)) {
+                                       length = 0;
+                               }
+                       }
+                       
+                       if (length === 0) {
+                               return [null, null];
+                       }
+                       
+                       var maxHeight = ~~_options.attachments.images[attachmentId].height;
+                       var maxWidth = ~~_options.attachments.images[attachmentId].width;
+                       var styles = ['max-height: ' + maxHeight + 'px', 'max-width: ' + maxWidth + 'px'];
+                       
+                       if (length > 1) {
+                               if (item.attributes[1] === 'left' || attributes[1] === 'right') {
+                                       styles.push('float: ' + attributes[1]);
+                                       styles.push('margin: ' + (attributes[1] === 'left' ? '0 15px 7px 0' : '0 0 7px 15px'));
+                               }
+                       }
+                       
+                       var baseUrl = _options.attachments.thumbnailUrl;
+                       if (length > 2) {
+                               width = ~~attributes[2] || 0;
+                               if (width) {
+                                       if (width > maxWidth) width = maxWidth;
+                                       
+                                       styles.push('width: ' + width + 'px');
+                                       baseUrl = _options.attachments.url;
+                               }
+                       }
+                       
+                       return [
+                               '<img src="' + baseUrl.replace(/987654321/, attachmentId) + '" class="redactorEmbeddedAttachment redactorDisableResize" data-attachment-id="' + attachmentId + '"' + (styles.length ? ' style="' + styles.join(';') + '"' : '') + '>',
+                               ''
+                       ];
+               },
+               
                _replaceCode: function(stack, item, index) {
                        var attributes = item.attributes, filename = '', highlighter = 'auto', lineNumber = 0;
                        
@@ -206,26 +261,23 @@ define(['EventHandler', 'Language', 'StringUtil', 'WoltLab/WCF/BBCode/Parser'],
                                }
                        }
                        
-                       stack[item.pair] = '</ol></div></div>';
-                       
-                       return '<div class="codeBox container" contenteditable="false" data-highlighter="' + highlighter + '" data-filename="' + (filename ? StringUtil.escapeHTML(filename) : '') + '">'
+                       return [
+                               '<div class="codeBox container" contenteditable="false" data-highlighter="' + highlighter + '" data-filename="' + (filename ? StringUtil.escapeHTML(filename) : '') + '">'
                                        + '<div>'
                                        + '<div>'
                                                + '<h3>' + __REDACTOR_CODE_HIGHLIGHTERS[highlighter] + (filename ? ': ' + StringUtil.escapeHTML(filename) : '') + '</h3>'
                                        + '</div>'
-                                       + '<ol start="' + (lineNumber > 1 ? lineNumber : 1) + '">';
+                                       + '<ol start="' + (lineNumber > 1 ? lineNumber : 1) + '">',
+                               '</ol></div></div>'
+                       ];
                },
                
                _replaceColor: function(stack, item, index) {
                        if (!item.attributes.length || !item.attributes[0].match(/^[a-z0-9#]+$/i)) {
-                               stack[item.pair] = '';
-                               
-                               return '';
+                               return [null, null];
                        }
                        
-                       stack[item.pair] = '</span>';
-                       
-                       return '<span style="color: ' + StringUtil.escapeHTML(item.attributes[0]) + '">';
+                       return ['<span style="color: ' + StringUtil.escapeHTML(item.attributes[0]) + '">', '</span>'];
                },
                
                _replaceEmail: function(stack, item, index) {
@@ -249,20 +301,14 @@ define(['EventHandler', 'Language', 'StringUtil', 'WoltLab/WCF/BBCode/Parser'],
                                
                                // no attribute present and element is empty, handle as plain text
                                if (email.trim() === '') {
-                                       stack[item.pair] = stack[item.pair].source;
-                                       
-                                       return item.source;
+                                       return [null, null];
                                }
                        }
                        
-                       stack[item.pair] = '</a>';
-                       
-                       return '<a href="mailto:' + StringUtil.escapeHTML(email) + '">';
+                       return ['<a href="mailto:' + StringUtil.escapeHTML(email) + '">', '</a>'];
                },
                
                _replaceImage: function(stack, item, index) {
-                       stack[item.pair] = '';
-                       
                        var float = 'none', source = '', width = 0;
                        
                        switch (item.attributes.length) {
@@ -305,7 +351,7 @@ define(['EventHandler', 'Language', 'StringUtil', 'WoltLab/WCF/BBCode/Parser'],
                                styles.push('margin: ' + (float === 'left' ? '0 15px 7px 0' : '0 0 7px 15px'));
                        }
                        
-                       return '<img src="' + StringUtil.escapeHTML(source) + '"' + (styles.length ? ' style="' + styles.join(';') + '"' : '') + '>';
+                       return ['<img src="' + StringUtil.escapeHTML(source) + '"' + (styles.length ? ' style="' + styles.join(';') + '"' : '') + '>', ''];
                },
                
                _replaceList: function(stack, item, index) {
@@ -319,17 +365,14 @@ define(['EventHandler', 'Language', 'StringUtil', 'WoltLab/WCF/BBCode/Parser'],
                        }
                        
                        if (type == '1' || type === 'decimal') {
-                               stack[item.pair] = '</ol>';
-                               
-                               return '<ol>';
+                               return ['<ol>', '</ol>'];
                        }
                        
-                       stack[item.pair] = '</ul>';
                        if (type.length && type.match(/^(?:none|circle|square|disc|decimal|lower-roman|upper-roman|decimal-leading-zero|lower-greek|lower-latin|upper-latin|armenian|georgian)$/)) {
-                               return '<ul style="list-style-type: ' + type + '">';
+                               return ['<ul style="list-style-type: ' + type + '">', '</ul>'];
                        }
                        
-                       return '<ul>';
+                       return ['<ul>', '</ul>'];
                },
                
                _replaceQuote: function(stack, item, index) {
@@ -342,8 +385,6 @@ define(['EventHandler', 'Language', 'StringUtil', 'WoltLab/WCF/BBCode/Parser'],
                                author = item.attributes[0];
                        }
                        
-                       stack[item.pair] = '</div></blockquote>';
-                       
                        // get rid of the trailing newline for quote content
                        for (var i = item.pair - 1; i > index; i--) {
                                if (typeof stack[i] === 'string') {
@@ -362,14 +403,17 @@ define(['EventHandler', 'Language', 'StringUtil', 'WoltLab/WCF/BBCode/Parser'],
                                header = '<small>' + Language.get('wcf.bbcode.quote.title.clickToSet') + '</small>';
                        }
                        
-                       return '<blockquote class="quoteBox container containerPadding quoteBoxSimple" cite="' + StringUtil.escapeHTML(link) + '" data-author="' + StringUtil.escapeHTML(author) + '">'
+                       return [
+                               '<blockquote class="quoteBox container containerPadding quoteBoxSimple" cite="' + StringUtil.escapeHTML(link) + '" data-author="' + StringUtil.escapeHTML(author) + '">'
                                        + '<header contenteditable="false">'
                                                + '<h3>'
                                                        + header
                                                + '</h3>'
                                                + '<a class="redactorQuoteEdit"></a>'
                                        + '</header>'
-                                       + '<div>\u200b';
+                                       + '<div>\u200b',
+                               '</div></blockquote>'
+                       ];
                },
                
                _replaceSmilies: function(stack) {
@@ -403,9 +447,7 @@ define(['EventHandler', 'Language', 'StringUtil', 'WoltLab/WCF/BBCode/Parser'],
                                return '';
                        }
                        
-                       stack[item.pair] = '</a>';
-                       
-                       return '<a href="' + StringUtil.escapeHTML(item.attributes[0]) + '">';
+                       return ['<a href="' + StringUtil.escapeHTML(item.attributes[0]) + '">', '</a>'];
                }
        };