From f9f88f87ba4c81e6781c62e7a95b56c184b60009 Mon Sep 17 00:00:00 2001 From: Alexander Ebert Date: Wed, 1 Jul 2015 12:32:52 +0200 Subject: [PATCH] Upgraded to Redactor 10.2 and added support for linked images --- .../3rdParty/redactor/plugins/wmonkeypatch.js | 50 +- .../files/js/3rdParty/redactor/redactor.js | 1277 +++++++++++------ .../js/3rdParty/redactor/redactor.min.js | 6 +- 3 files changed, 860 insertions(+), 473 deletions(-) diff --git a/wcfsetup/install/files/js/3rdParty/redactor/plugins/wmonkeypatch.js b/wcfsetup/install/files/js/3rdParty/redactor/plugins/wmonkeypatch.js index d0cd2e9aaf..9cf41559cf 100644 --- a/wcfsetup/install/files/js/3rdParty/redactor/plugins/wmonkeypatch.js +++ b/wcfsetup/install/files/js/3rdParty/redactor/plugins/wmonkeypatch.js @@ -565,14 +565,23 @@ RedactorPlugins.wmonkeypatch = function() { // image.setEditable var $mpSetEditable = this.image.setEditable; this.image.setEditable = (function($image) { - if (!$image.hasClass('smiley')) { - $mpSetEditable.call(this, $image); + if ($image.hasClass('smiley')) { + return; } + + // workaround for #2675 + $image.off('click.redactor touchstart.redactor'); + + $mpSetEditable.call(this, $image); }).bind(this); // image.loadEditableControls var $mpLoadEditableControls = this.image.loadEditableControls; this.image.loadEditableControls = (function($image) { + if ($image[0].parentNode.id === 'redactor-image-box') { + return $('#redactor-image-resizer', this.$editor[0]); + } + var $returnValue = $mpLoadEditableControls.call(this, $image); if ($image.hasClass('redactorDisableResize') && $returnValue !== false) { @@ -588,6 +597,8 @@ RedactorPlugins.wmonkeypatch = function() { var $button = this.modal.createActionButton(this.lang.get('insert')); $button.click($.proxy(this.wbutton._insertImage, this)); + $('#redactorImageLinkHrefContainer').hide(); + this.selection.save(); this.modal.show(); }).bind(this); @@ -601,11 +612,16 @@ RedactorPlugins.wmonkeypatch = function() { this.image.update(image); }).bind(this)); + var link = image.closest('a', this.$editor[0]); + // set overlay values $('#redactor-image-link-source').val(image.attr('src')); + $('#redactor-image-link-href').val(link.length ? link.attr('href') : ''); $('#redactor-image-align').val(image.css('float')); this.modal.show(); + + setTimeout(function() { $('.redactor-link-tooltip').remove(); }, 1); }).bind(this); // image.update @@ -625,6 +641,30 @@ RedactorPlugins.wmonkeypatch = function() { this.image.setFloating(image); $moveImage(image); + var link = $('#redactor-image-link-href').val().trim(); + var anchor = image.closest('a', this.$editor[0]); + + if (anchor.length) { + if (link === '') { + anchor = anchor[0]; + var i = anchor.children.length, parent = anchor.parentNode; + while (i--) { + parent.insertBefore(anchor.children[0], anchor); + } + + parent.removeChild(anchor); + } + else { + anchor.attr('href', link); + } + } + else { + anchor = document.createElement('a'); + anchor.href = link; + image[0].parentNode.insertBefore(anchor, image[0]); + anchor.appendChild(image[0]); + } + this.modal.close(); this.observe.images(); }).bind(this); @@ -1211,9 +1251,13 @@ RedactorPlugins.wmonkeypatch = function() { this.opts.modal.image = '
' + '
' - + '
' + + '
' + '
' + '
' + + '
' + + '
' + + '
' + + '
' + '
' + '
' + '
' diff --git a/wcfsetup/install/files/js/3rdParty/redactor/redactor.js b/wcfsetup/install/files/js/3rdParty/redactor/redactor.js index 9bb202c967..3aa3de51ea 100644 --- a/wcfsetup/install/files/js/3rdParty/redactor/redactor.js +++ b/wcfsetup/install/files/js/3rdParty/redactor/redactor.js @@ -1,6 +1,6 @@ /* - Redactor v10.1.3 - Updated: June 4, 2015 + Redactor 10.2 + Updated: June 26, 2015 http://imperavi.com/redactor/ @@ -91,13 +91,13 @@ // Functionality $.Redactor = Redactor; - $.Redactor.VERSION = '10.1.3'; + $.Redactor.VERSION = '10.2'; $.Redactor.modules = ['alignment', 'autosave', 'block', 'buffer', 'build', 'button', 'caret', 'clean', 'code', 'core', 'dropdown', 'file', 'focus', 'image', 'indent', 'inline', 'insert', 'keydown', 'keyup', - 'lang', 'line', 'link', 'list', 'modal', 'observe', 'paragraphize', + 'lang', 'line', 'link', 'linkify', 'list', 'modal', 'observe', 'paragraphize', 'paste', 'placeholder', 'progress', 'selection', 'shortcuts', - 'tabifier', 'tidy', 'toolbar', 'upload', 'utils', 'linkify']; + 'tabifier', 'tidy', 'toolbar', 'upload', 'utils']; $.Redactor.opts = { @@ -191,11 +191,11 @@ deniedTags: ['script', 'style'], allowedTags: false, // or array - + paragraphizeBlocks: ['table', 'div', 'pre', 'form', 'ul', 'ol', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'dl', 'blockquote', 'figcaption', 'address', 'section', 'header', 'footer', 'aside', 'article', 'object', 'style', 'script', 'iframe', 'select', 'input', 'textarea', 'button', 'option', 'map', 'area', 'math', 'hr', 'fieldset', 'legend', 'hgroup', 'nav', 'figure', 'details', 'menu', 'summary', 'p'], - + removeComments: false, replaceTags: [ ['strike', 'del'], @@ -252,7 +252,10 @@ inlineTags: ['strong', 'b', 'u', 'em', 'i', 'code', 'del', 'ins', 'samp', 'kbd', 'sup', 'sub', 'mark', 'var', 'cite', 'small'], alignmentTags: ['P', 'H1', 'H2', 'H3', 'H4', 'H5', 'H6', 'DL', 'DT', 'DD', 'DIV', 'TD', 'BLOCKQUOTE', 'OUTPUT', 'FIGCAPTION', 'ADDRESS', 'SECTION', 'HEADER', 'FOOTER', 'ASIDE', 'ARTICLE'], blockLevelElements: ['PRE', 'UL', 'OL', 'LI'], - + highContrast: false, + observe: { + dropdowns: [] + }, // lang langs: { @@ -328,7 +331,6 @@ filename: 'Name (optional)', edit: 'Edit', upload_label: 'Drop file here or ' - } }, @@ -350,6 +352,7 @@ keyCode: { BACKSPACE: 8, DELETE: 46, + UP: 38, DOWN: 40, ENTER: 13, SPACE: 32, @@ -451,7 +454,6 @@ this[module][methods[z]] = this[module][methods[z]].bind(this); } }, - alignment: function() { return { @@ -562,7 +564,6 @@ this.autosave.source = this.code.get(); if (this.autosave.html === this.autosave.source) return; - //if (this.utils.isEmpty(this.autosave.source)) return; // data var data = {}; @@ -654,6 +655,23 @@ // focus if (!this.utils.browser('msie')) this.$editor.focus(); + var html = $.trim(this.$editor.html()); + this.block.isEmpty = this.utils.isEmpty(html); + + // FF focus + if (this.utils.browser('mozilla') && !this.focus.isFocused()) + { + if (this.block.isEmpty) + { + var $first; + if (!this.opts.linebreaks) + { + $first = this.$editor.children().first(); + this.caret.setEnd($first); + } + } + } + this.block.blocks = this.selection.getBlocks(); this.block.blocksSize = this.block.blocks.length; @@ -671,6 +689,7 @@ }, set: function(tag) { + this.selection.get(); this.block.containerTag = this.range.commonAncestorContainer.tagName; @@ -685,6 +704,16 @@ }, setCollapsed: function(tag) { + if (this.opts.linebreaks && this.block.isEmpty && tag != 'p') + { + var node = document.createElement(tag); + this.$editor.html(node); + this.caret.setEnd(node); + + return; + } + + var block = this.block.blocks[0]; if (block === false) return; @@ -699,7 +728,6 @@ var isContainerTable = (this.block.containerTag == 'TD' || this.block.containerTag == 'TH'); if (isContainerTable && !this.opts.linebreaks) { - document.execCommand('formatblock', false, '<' + tag + '>'); block = this.selection.getBlock(); @@ -710,7 +738,7 @@ { if (this.opts.linebreaks && tag == 'p') { - $(block).prepend('
').append('
'); + $(block).append('
'); this.utils.replaceWithContents(block); } else @@ -731,7 +759,7 @@ // blockquote off if (this.opts.linebreaks) { - $(block).prepend('
').append('
'); + $(block).append('
'); this.utils.replaceWithContents(block); } else @@ -745,6 +773,8 @@ this.block.toggle($(block)); } + + if (typeof this.block.type == 'undefined' && typeof this.block.value == 'undefined') { $(block).removeAttr('class').removeAttr('style'); @@ -763,7 +793,7 @@ // blockquote off if (this.opts.linebreaks) { - $(block).prepend('
').append('
'); + $(block).append('
'); this.utils.replaceWithContents(block); } else @@ -1010,11 +1040,6 @@ var $elements = $formatted.find(this.opts.blockLevelElements.join(',') + ', td, table, thead, tbody, tfoot, th, tr'); - if ((this.opts.linebreaks && tag == 'p') || tag == 'pre' || tag == 'blockquote') - { - $elements.append('
'); - } - $elements.contents().unwrap(); if (tag != 'p' && tag != 'blockquote') $formatted.find('img').remove(); @@ -1041,6 +1066,17 @@ this.utils.replaceWithContents($formatted); } + if (this.opts.linebreaks) + { + var $next = $formatted.next().next(); + if ($next.size() != 0 && $next[0].tagName === 'BR') + { + $next.remove(); + } + } + + + }, formatTableWrapping: function($formatted) { @@ -1214,7 +1250,7 @@ }, createContainerBox: function() { - this.$box = $('
'); + this.$box = $('
'); }, createTextarea: function() { @@ -1362,14 +1398,14 @@ this.$editor.on('paste.redactor', $.proxy(this.paste.init, this)); // cut - this.$editor.on('cut.redactor', $.proxy(this.code.sync, this)); - + this.$editor.on('cut.redactor', $.proxy(this.code.sync, this)); + // keydown this.$editor.on('keydown.redactor', $.proxy(this.keydown.init, this)); // keyup this.$editor.on('keyup.redactor', $.proxy(this.keyup.init, this)); - + // textarea keydown if ($.isFunction(this.opts.codeKeydownCallback)) { @@ -1388,23 +1424,26 @@ this.$editor.on('focus.redactor', $.proxy(this.opts.focusCallback, this)); } - var clickedElement; - $(document).on('mousedown', function(e) { clickedElement = e.target; }); + $(document).on('mousedown.redactor', $.proxy(function(e) { this.blurClickedElement = e.target; }, this)); + // blur this.$editor.on('blur.redactor', $.proxy(function(e) { + if (this.start) return; if (this.rtePaste) return; - if (!this.build.isBlured(clickedElement)) return; + if (!this.build.isBlured()) return; this.utils.disableSelectAll(); if ($.isFunction(this.opts.blurCallback)) this.core.setCallback('blur', e); }, this)); }, - isBlured: function(clickedElement) + isBlured: function() { - var $el = $(clickedElement); + if (this.blurClickedElement === true) return true; + + var $el = $(this.blurClickedElement); return (!$el.hasClass('redactor-toolbar, redactor-dropdown') && !$el.is('#redactor-modal') && $el.parents('.redactor-toolbar, .redactor-dropdown, #redactor-modal').length === 0); }, @@ -1415,7 +1454,7 @@ { this.linkify.format(); } - + // placeholder this.placeholder.enable(); @@ -1427,21 +1466,17 @@ plugins: function() { if (!this.opts.plugins) return; - if (!RedactorPlugins) return; $.each(this.opts.plugins, $.proxy(function(i, s) { - if (typeof RedactorPlugins[s] === 'undefined') return; + var func = (typeof RedactorPlugins !== 'undefined' && typeof RedactorPlugins[s] !== 'undefined') ? RedactorPlugins : Redactor.fn; - if ($.inArray(s, $.Redactor.modules) !== -1) + if (!$.isFunction(func[s])) { - $.error('Plugin name "' + s + '" matches the name of the Redactor\'s module.'); return; } - if (!$.isFunction(RedactorPlugins[s])) return; - - this[s] = RedactorPlugins[s](); + this[s] = func[s](); // get methods var methods = this.getModuleMethods(this[s]); @@ -1453,7 +1488,10 @@ this[s][methods[z]] = this[s][methods[z]].bind(this); } - if ($.isFunction(this[s].init)) this[s].init(); + if ($.isFunction(this[s].init)) + { + this[s].init(); + } }, this)); @@ -1472,7 +1510,7 @@ disableIeLinks: function() { if (!this.utils.browser('ie')) return; - + // IE prevent converting links document.execCommand("AutoUrlDetect", false, false); } @@ -1483,7 +1521,7 @@ return { build: function(btnName, btnObject) { - var $button = $('').attr('tabindex', '-1'); + var $button = $('').attr({'role': 'button', 'aria-label': btnObject.title, 'tabindex': '-1'}); // click if (btnObject.func || btnObject.command || btnObject.dropdown) @@ -1494,6 +1532,8 @@ // dropdown if (btnObject.dropdown) { + $button.addClass('redactor-toolbar-link-dropdown').attr('aria-haspopup', true); + var $dropdown = $('