Skip to content

Redactor 4 Vendor Customizations

This document tracks all customizations made to the Redactor 4 vendor files. Review and re-apply these when upgrading Redactor.

Vendor Location: client/vendor/redactor4/ Current Version: 4.5.3 (with patches) Last Updated: 2026-01-26 Patches By: Roman Mateja-Cabello (original), updated by AI assistant


Customization 1: Source Mode Scroll Jump Fix

Section titled “Customization 1: Source Mode Scroll Jump Fix”

File: redactor.js Location: SourceModule._setHeight() (around line 13009) Type: Bug fix Status: Applied in 4.5.3

When switching to source mode, the original code sets height: auto which causes the page to scroll/jump unexpectedly.

Clone the element to measure height without affecting the visible element, then restore scroll position.

_setHeight() {
this.$source.get().style.height = 'auto';
this.$source.get().style.height = `${this.$source.get().scrollHeight}px`;
}
_setHeight() {
const el = this.$source.get();
// Get current scroll position
const scrollTop = window.pageYOffset || document.documentElement.scrollTop;
// Create a clone to measure the natural height without affecting the page
// This avoids the flash caused by setting height='auto' on the real element
const clone = el.cloneNode(true);
clone.style.cssText = `
position: absolute;
visibility: hidden;
height: auto;
width: ${el.offsetWidth}px;
overflow: hidden;
`;
el.parentNode.appendChild(clone);
const newHeight = clone.scrollHeight;
clone.remove();
// Only update if height needs to change
const currentHeight = el.offsetHeight;
if (newHeight !== currentHeight) {
el.style.height = `${newHeight}px`;
}
// Restore scroll position immediately
window.scrollTo(0, scrollTop);
}

Search for _setHeight() in the new version’s SourceModule. If still using height: auto, this patch is still needed.


Customization 2: Block Focus Undefined Guard

Section titled “Customization 2: Block Focus Undefined Guard”

File: redactor.js Location: BlockModule focus handling (around line 21525) Type: Bug fix Status: Applied in 4.5.3

After reassigning this.instance = this.instance.getParent(), the instance could be undefined, causing errors.

Add a guard clause after the reassignment.

// Guard against undefined instance after reassignment (bug fix)
if (!this.instance) return;

Search for this.instance.getParent() and verify proper null handling exists.


Customization 3: Image Inline-Block Option

Section titled “Customization 3: Image Inline-Block Option”

File: redactor.js Location: ImageManager and block.image (multiple locations) Type: Feature addition (WarmlyYours specific) Status: Custom feature, always needs re-application

Adds an “Inline block (for centering)” checkbox to the image properties dialog. This allows users to set display: inline-block on images for centering purposes.

  1. ImageManager form items (~line 23170):
inlineBlock: { type: 'checkbox', text: 'Inline block (for centering)', observer: 'image.observeInlineBlock', auto: true },
  1. ImageManager observer (~line 23262):
observeInlineBlock(obj) {
// Always show the inline-block option (WY custom feature)
return obj;
}
  1. Block image props (~line 25623):
inlineBlock: { getter: 'getInlineBlock', setter: 'setInlineBlock' },
  1. Block image methods (~line 25684):
getInlineBlock() {
const display = this.$image.css('display');
return display === 'inline-block';
},
setInlineBlock(value) {
if (value) {
this.$image.css('display', 'inline-block');
} else {
this.$image.css('display', '');
if (this.$image.attr('style') === '') {
this.$image.removeAttr('style');
}
}
},

Customization 4: Email Container Centering Fix

Section titled “Customization 4: Email Container Centering Fix”

File: plugins/email/email.js Location: _buildMainEmail() (around line 1097) Type: Bug fix / Email compatibility Status: Applied in 4.5.3

The email plugin’s _buildMainEmail() creates the 600px container table but doesn’t add centering attributes. While the parent <td> has align="center", some email clients (especially Apple Mail) don’t properly center nested tables without explicit centering on the table itself.

Add align="center" attribute and margin: 0 auto CSS to the email-container table.

const $container = this.dom('<table>');
$container.addClass('email-container');
$container.attr({
'width': widthAttr,
'cellpadding': '0',
'cellspacing': '0',
'role': 'presentation'
});
const $container = this.dom('<table>');
$container.addClass('email-container');
$container.css({ 'margin': '0 auto' });
$container.attr({
'width': widthAttr,
'cellpadding': '0',
'cellspacing': '0',
'align': 'center',
'role': 'presentation'
});

Search for _buildMainEmail in the email plugin. If the container table doesn’t have align: 'center' and margin: 0 auto, this patch is still needed.


Terminal window
cp -r client/vendor/redactor4 client/vendor/redactor4-backup-$(date +%Y%m%d)
Terminal window
# Core Redactor
cp extras/redactor/redactor-X-X-X/redactor.js client/vendor/redactor4/
cp extras/redactor/redactor-X-X-X/redactor.css client/vendor/redactor4/
cp extras/redactor/redactor-X-X-X/redactor.min.js client/vendor/redactor4/
cp extras/redactor/redactor-X-X-X/redactor.min.css client/vendor/redactor4/
# Plugins (copy folders as needed)
cp -r extras/redactor/redactor-X-X-X/plugins/* client/vendor/redactor4/plugins/
# Updated email plugin (if separate)
cp extras/redactor/email/* client/vendor/redactor4/plugins/email/
# Updated AI plugin (if separate)
cp extras/redactor/ai/* client/vendor/redactor4/plugins/ai/

For each customization above:

  1. Check if the issue is fixed in the new version
  2. If not fixed, apply the patch manually
  3. Update line numbers in this document
  • Source mode toggle (no scroll jump)
  • Image inline-block option works
  • Email templates display correctly in editor
  • Email templates render correctly when sent
  • Blog editor works with all plugins (AI, FAQ, Video, Snippets)
  • Image insertion works (WY Image plugin)
  • Video insertion works (WY Video plugin)
  • No console errors
  • New version number
  • Updated line numbers for patches
  • Any new customizations added

Scroll Jump Fix (Could be runtime-patched)

Section titled “Scroll Jump Fix (Could be runtime-patched)”

This fix modifies an internal method and could potentially be applied at runtime after Redactor loads. This would avoid modifying vendor files:

// Hypothetical runtime patch in redactor4.js
// Note: Requires understanding Redactor's module structure
// After Redactor is loaded, patch the SourceModule
const originalSetHeight = Redactor.modules?.Source?.prototype?._setHeight;
if (originalSetHeight) {
Redactor.modules.Source.prototype._setHeight = function() {
const el = this.$source.get();
const scrollTop = window.pageYOffset || document.documentElement.scrollTop;
const clone = el.cloneNode(true);
clone.style.cssText = `position:absolute;visibility:hidden;height:auto;width:${el.offsetWidth}px;overflow:hidden`;
el.parentNode.appendChild(clone);
const newHeight = clone.scrollHeight;
clone.remove();
if (newHeight !== el.offsetHeight) {
el.style.height = `${newHeight}px`;
}
window.scrollTo(0, scrollTop);
};
}

Caveat: Redactor’s internal structure may change between versions, making this fragile. Direct patching is currently more reliable.

The inline-block checkbox feature could potentially be extracted into a standalone Redactor plugin (wyinlineblock.js) that extends the image block’s behavior. This would require:

  1. Understanding Redactor’s plugin API for extending existing blocks
  2. Creating a proper plugin structure
  3. Testing compatibility with each Redactor version

This is a larger effort but would make upgrades trivial.


  • Redactor config: client/js/crm/editors/redactor4.js
  • Stimulus controller: app/javascript/controllers/redactor4_init_controller.js
  • WY Image plugin: client/js/common/wyimage4.js
  • WY Video plugin: client/js/common/wyvideo4.js
  • WY FAQ plugin: client/js/common/wyfaq4.js
  • WY Embed plugin: client/js/common/wyembed4.js
  • WY Email Blocks plugin: client/js/common/wyemailblocks.js