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
Problem
Section titled “Problem”When switching to source mode, the original code sets height: auto which causes
the page to scroll/jump unexpectedly.
Solution
Section titled “Solution”Clone the element to measure height without affecting the visible element, then restore scroll position.
Original Code
Section titled “Original Code”_setHeight() { this.$source.get().style.height = 'auto'; this.$source.get().style.height = `${this.$source.get().scrollHeight}px`;}Patched Code
Section titled “Patched Code”_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);}Upgrade Check
Section titled “Upgrade Check”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
Problem
Section titled “Problem”After reassigning this.instance = this.instance.getParent(), the instance
could be undefined, causing errors.
Solution
Section titled “Solution”Add a guard clause after the reassignment.
Patched Code
Section titled “Patched Code”// Guard against undefined instance after reassignment (bug fix)if (!this.instance) return;Upgrade Check
Section titled “Upgrade Check”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
Purpose
Section titled “Purpose”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.
Changes Required
Section titled “Changes Required”- ImageManager form items (~line 23170):
inlineBlock: { type: 'checkbox', text: 'Inline block (for centering)', observer: 'image.observeInlineBlock', auto: true },- ImageManager observer (~line 23262):
observeInlineBlock(obj) { // Always show the inline-block option (WY custom feature) return obj;}- Block image props (~line 25623):
inlineBlock: { getter: 'getInlineBlock', setter: 'setInlineBlock' },- 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
Problem
Section titled “Problem”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.
Solution
Section titled “Solution”Add align="center" attribute and margin: 0 auto CSS to the email-container table.
Original Code
Section titled “Original Code”const $container = this.dom('<table>');$container.addClass('email-container');$container.attr({ 'width': widthAttr, 'cellpadding': '0', 'cellspacing': '0', 'role': 'presentation'});Patched Code
Section titled “Patched Code”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'});Upgrade Check
Section titled “Upgrade Check”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.
Upgrade Procedure
Section titled “Upgrade Procedure”1. Backup current vendor files
Section titled “1. Backup current vendor files”cp -r client/vendor/redactor4 client/vendor/redactor4-backup-$(date +%Y%m%d)2. Copy new version files
Section titled “2. Copy new version files”# Core Redactorcp 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/3. Re-apply customizations
Section titled “3. Re-apply customizations”For each customization above:
- Check if the issue is fixed in the new version
- If not fixed, apply the patch manually
- Update line numbers in this document
4. Test checklist
Section titled “4. Test checklist”- 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
5. Update this document
Section titled “5. Update this document”- New version number
- Updated line numbers for patches
- Any new customizations added
Future Consideration: Extractable Patches
Section titled “Future Consideration: Extractable Patches”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 SourceModuleconst 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.
Inline-Block Feature (Could be a plugin)
Section titled “Inline-Block Feature (Could be a plugin)”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:
- Understanding Redactor’s plugin API for extending existing blocks
- Creating a proper plugin structure
- Testing compatibility with each Redactor version
This is a larger effort but would make upgrades trivial.
Related Files
Section titled “Related Files”- 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