GIF94;
| Path : /var/www/wordpress/wp-content/plugins/oxymade/assets/js/modules/ |
| Current File : /var/www/wordpress/wp-content/plugins/oxymade/assets/js/modules/color-panel.js |
/**
* Color Panel Module
* Provides color panel functionality for OxyMade
*/
window.OxyMade = window.OxyMade || {};
window.OxyMade.ColorPanel = {
// Helper function to safely call spectrum methods
safeSpectrum(element, method, ...args) {
const $ = window.parent.jQuery || window.jQuery;
if (!$ || !$.fn.spectrum) {
console.warn('Spectrum not loaded, skipping spectrum call');
return null;
}
return element.spectrum(method, ...args);
},
// Sync CSS variables from iframe (where CSS is loaded) to parent window
syncCSSVariablesToParent() {
const iframeStyles = getComputedStyle(document.documentElement);
const parentRoot = window.parent.document.documentElement;
// Define all the CSS variables we need to sync
const colorGroups = ['primary', 'secondary', 'tertiary', 'accent', 'neutral'];
const shades = ['bg', 'surface', 'subtle', 'border', 'muted', 'base', 'hover', 'text', 'active', 'heading', 'dark'];
const alphaValues = [10, 20, 30, 40, 50, 60, 70, 80, 90];
// Sync color shades
colorGroups.forEach(color => {
// Sync base color and RGB
const baseVar = `--base-${color}`;
const baseValue = iframeStyles.getPropertyValue(baseVar).trim();
if (baseValue) {
parentRoot.style.setProperty(baseVar, baseValue);
}
const rgbVar = `--${color}-rgb`;
const rgbValue = iframeStyles.getPropertyValue(rgbVar).trim();
if (rgbValue) {
parentRoot.style.setProperty(rgbVar, rgbValue);
}
// Sync all shades
shades.forEach(shade => {
const varName = `--${shade}-${color}`;
const value = iframeStyles.getPropertyValue(varName).trim();
if (value) {
parentRoot.style.setProperty(varName, value);
}
});
// Sync alpha values
alphaValues.forEach(alpha => {
const varName = `--${color}-${alpha}`;
const value = iframeStyles.getPropertyValue(varName).trim();
if (value) {
parentRoot.style.setProperty(varName, value);
}
});
});
// Sync base-white and base-black
['base-white', 'base-black'].forEach(color => {
const varName = `--${color}`;
const value = iframeStyles.getPropertyValue(varName).trim();
if (value) {
parentRoot.style.setProperty(varName, value);
}
});
// Sync white and black RGB
['white-rgb', 'black-rgb'].forEach(varName => {
const fullVar = `--${varName}`;
const value = iframeStyles.getPropertyValue(fullVar).trim();
if (value) {
parentRoot.style.setProperty(fullVar, value);
}
});
// Sync white and black alpha values
['white', 'black'].forEach(color => {
alphaValues.forEach(alpha => {
const varName = `--${color}-${alpha}`;
const value = iframeStyles.getPropertyValue(varName).trim();
if (value) {
parentRoot.style.setProperty(varName, value);
}
});
});
},
// Apply CSS variables to document
applyCSSVariables(palette) {
const root = window.parent.document.documentElement;
Object.keys(palette).forEach(key => {
// Key already includes -- prefix, so use it directly
root.style.setProperty(key, palette[key]);
});
},
// Create color panel HTML using the working structure from oxymade.js
createColorPanelHTML(primaryColor = '#667eea') {
// Define color data structure (same as oxymade.js)
const colorData = [
{
name: 'primary',
title: 'Primary Color',
defaultChecked: true
},
{
name: 'secondary',
title: 'Secondary Color',
defaultChecked: true
},
{
name: 'tertiary',
title: 'Tertiary Color',
defaultChecked: true
},
{
name: 'accent',
title: 'Accent Color',
defaultChecked: true
},
{
name: 'neutral',
title: 'Neutral Color',
defaultChecked: true
},
{
name: 'base-white',
title: 'White Color',
defaultChecked: true,
isSimpleColor: true
},
{
name: 'base-black',
title: 'Black Color',
defaultChecked: true,
isSimpleColor: true
}
];
// Define shade data with aliases (same as oxymade.js)
const shadeData = [
{ shade: 'bg', alias: '50' },
{ shade: 'surface', alias: '100' },
{ shade: 'subtle', alias: '200' },
{ shade: 'border', alias: '300' },
{ shade: 'muted', alias: '400' },
{ shade: 'base', alias: '500' },
{ shade: 'hover', alias: '600' },
{ shade: 'text', alias: '700' },
{ shade: 'active', alias: '800' },
{ shade: 'heading', alias: '900' },
{ shade: 'dark', alias: '950' }
];
// Build the panel HTML (exact structure from oxymade.js)
let colorPanelHTML = `
<div class="color-panel-header">
<h3 style="display: flex; align-items: center; gap: 8px;"><div class="oxymade-icon" style="width: 32px; height: 32px;">
<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" fill="none" viewBox="0 0 63 64">
<rect width="62.837" height="63.526" fill="#EE6A5F" rx="12.348" transform="matrix(1 0 0 -1 0 63.527)"/>
<path fill="#fff" d="M28.726 12.038v12.769l-1.893-1.893c-2.366-2.35-6.951-6.878-6.976-6.878-.008 0-.914.897-2.015 1.99l-2 1.991 1.078 1.053c.595.57 2.586 2.513 4.422 4.3 1.844 1.794 3.362 3.288 3.37 3.328.016.041-2.636.066-6.323.066l-6.348-.017-.025 2.783L12 34.312h12.81l-2.31 2.26-4.462 4.406-2.162 2.146 1.958 1.958c1.077 1.077 1.99 1.958 2.04 1.958.04 0 2.056-1.975 4.47-4.39l4.39-4.381-.016 6.609-.008 6.608 2.782.025 2.782.016V38.35l4.39 4.39 4.39 4.39 1.974-2.024 1.974-2.023-2.676-2.627a957 957 0 0 0-4.463-4.366l-1.795-1.737 6.389-.025 6.38-.016v-5.548h-6.323c-3.558 0-6.324-.033-6.324-.074s.343-.391.767-.791c.947-.906 7.996-7.808 8.029-7.866.008-.024-.881-.938-1.983-2.04l-1.999-1.998-4.348 4.349c-2.383 2.382-4.357 4.34-4.382 4.34s-.032-2.847-.016-6.323l.016-6.323-.506-.017c-1.15-.032-5.042-.024-5.042.017"/>
</svg>
</div> Color Palette Generator</h3>
<div class="color-panel-close">
<svg xmlns="https://www.w3.org/2000/svg" viewBox="0 0 320 512">
<path d="M310.6 361.4c12.5 12.5 12.5 32.75 0 45.25-6.2 6.25-14.4 9.35-22.6 9.35s-16.38-3.125-22.62-9.375L160 301.3 54.63 406.6C48.38 412.9 40.19 416 32 416s-16.37-3.1-22.625-9.4c-12.5-12.5-12.5-32.75 0-45.25l105.4-105.4L9.375 150.6c-12.5-12.5-12.5-32.75 0-45.25s32.75-12.5 45.25 0L160 210.8l105.4-105.4c12.5-12.5 32.75-12.5 45.25 0s12.5 32.75 0 45.25l-105.4 105.4L310.6 361.4z"/>
</svg>
</div>
</div>
<div class="color-panel-content">
<div class="color-panel-section">
<h4 class="oxymade-heading">Primary Color</h4>
<p class="oxymade-description">Select a primary color to generate a complete color palette.</p>
<div class="oxymade-color-picker-container">
<div class="oxymade-color-field">
<div class="oxymade-color-preview" style="background-color: var(--base-primary)"></div>
<input type="text" id="primary-color-picker" class="oxymade-color-input" value="${primaryColor}">
<div class="oxymade-color-value">${primaryColor}</div>
</div>
</div>
<!-- Save Status Notification -->
<div id="oxymade-save-status" class="oxymade-save-status" style="display: none;">
<svg class="save-icon" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<polyline points="20 6 9 17 4 12"></polyline>
</svg>
<span class="save-message">Colors saved!</span>
</div>
</div>
`;
// Generate color sections dynamically (same as oxymade.js)
colorData.forEach(color => {
if (color.isSimpleColor) {
// For white and black, just show a simple color picker without shades
colorPanelHTML += `
<div class="color-panel-section" data-color="${color.name}">
<div class="color-section-header" data-color="${color.name}">
<p class="color-title">${color.title}</p>
<div class="color-swatch-preview color-main-preview" data-color="${color.name}" style="background-color: var(--${color.name});"></div>
</div>
</div>
`;
} else {
// For regular colors, show the accordion with all shades
colorPanelHTML += `
<div class="color-panel-section" data-color="${color.name}">
<div class="color-section-header" data-color="${color.name}">
<p class="color-title">${color.title}</p>
<div class="color-swatch-preview color-main-preview" data-color="${color.name}" style="background-color: var(--base-${color.name});"></div>
</div>
<div class="color-section-content" data-color="${color.name}">
`;
// Generate shades for each color
shadeData.forEach(shade => {
colorPanelHTML += `
<div class="color-swatch" data-color="${color.name}" data-shade="${shade.shade}">
<div class="color-swatch-preview" style="background-color: var(--${shade.shade}-${color.name});"></div>
<div class="color-swatch-label">
<span class="shade-name">${shade.shade}</span>
</div>
<input type="text" class="shade-color-picker oxymade-color-input" data-color="${color.name}" data-shade="${shade.shade}">
</div>
`;
});
colorPanelHTML += `
</div>
</div>
`;
}
});
// Add close button at the bottom
colorPanelHTML += `
</div>
<div class="color-panel-footer">
<div class="color-panel-actions">
<button id="cancel-palette" class="oxymade-button oxymade-button-secondary">
<svg xmlns="https://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<line x1="18" y1="6" x2="6" y2="18"></line>
<line x1="6" y1="6" x2="18" y2="18"></line>
</svg>
Close
</button>
</div>
</div>
`;
return colorPanelHTML;
},
// Initialize color panel (based on oxymade.js structure)
init() {
// Check if we're in Oxygen builder iframe
if (!window.location.search.includes('breakdance_iframe=true')) {
return;
}
// Check if panel already exists
if (window.parent.document.getElementById('color-panel-slider')) {
return;
}
// First, sync CSS variables from iframe to parent window
this.syncCSSVariablesToParent();
// Get computed primary color from iframe (where the CSS is loaded)
const computedPrimaryColor = getComputedStyle(document.documentElement)
.getPropertyValue('--base-primary').trim() || '#3b82f6'; // Default blue if not set
// Create panel element (exact structure from oxymade.js)
const panelElement = window.parent.document.createElement('div');
panelElement.id = 'color-panel-slider';
panelElement.className = 'color-panel-slider';
panelElement.innerHTML = this.createColorPanelHTML(computedPrimaryColor);
// Safely set panel visibility in localStorage
try {
if (window.parent.localStorage) {
window.parent.localStorage.setItem('oxymade_panel_visible', 'false');
}
} catch (e) {
// Silently fail if localStorage is not available
}
// Append panel to the parent window
window.parent.document.body.appendChild(panelElement);
// Load jQuery first, then load saved palette
this.ensureJQueryLoaded(() => {
// Load saved palette from database first
this.loadSavedPalette((paletteLoaded) => {
if (!paletteLoaded) {
// Only generate initial color palette if no saved data was found
this.updatePalette(computedPrimaryColor);
}
});
});
// Load external CSS file
if (!window.parent.document.getElementById('oxymade-external-css')) {
const linkElement = window.parent.document.createElement('link');
linkElement.id = 'oxymade-external-css';
linkElement.rel = 'stylesheet';
linkElement.href = oxymadeSettings.pluginUrl + '/assets/css/oxymade.css';
linkElement.type = 'text/css';
window.parent.document.head.appendChild(linkElement);
}
// CSS is now loaded from external oxymade.css file
// Inject spectrum picker styling for horizontal layout
if (!window.parent.document.getElementById('oxymade-spectrum-css')) {
const spectrumCSS = `
.oxymade-spectrum-container .sp-container {
width: 350px !important;
min-width: 350px !important;
}
.oxymade-spectrum-container .sp-palette {
width: 300px !important;
height: 200px !important;
}
.oxymade-spectrum-container .sp-palette-row {
display: flex !important;
flex-direction: row !important;
flex-wrap: wrap !important;
}
.oxymade-spectrum-container .sp-palette-button {
width: 20px !important;
height: 20px !important;
margin: 2px !important;
}
.oxymade-spectrum-container .sp-spectrum {
width: 200px !important;
height: 150px !important;
}
`;
const style = window.parent.document.createElement('style');
style.id = 'oxymade-spectrum-css';
style.textContent = spectrumCSS;
window.parent.document.head.appendChild(style);
}
// Add event listeners (same as oxymade.js)
const panel = window.parent.document.getElementById('color-panel-slider');
const closeBtn = panel.querySelector('.color-panel-close');
const colorInput = panel.querySelector('#primary-color-picker');
// Close button
closeBtn.addEventListener('click', () => {
panel.style.right = '-350px';
});
// Color picker
colorInput.addEventListener('input', (e) => {
const newColor = e.target.value;
this.updatePalette(newColor);
});
// Toggle color sections - header click should toggle shades
const colorSectionHeaders = panel.querySelectorAll('.color-section-header');
colorSectionHeaders.forEach(header => {
// Header click should toggle shades
header.addEventListener('click', (e) => {
// Don't toggle if clicking on the color preview
if (e.target.closest('.color-swatch-preview')) {
return;
}
const colorName = header.dataset.color;
const content = panel.querySelector(`.color-section-content[data-color="${colorName}"]`);
if (content) {
content.classList.toggle('show');
header.classList.toggle('expanded');
}
});
});
// Color field click should open color picker (matching breakmade.js approach)
const colorFields = panel.querySelectorAll('.oxymade-color-field');
colorFields.forEach(colorField => {
colorField.addEventListener('click', (e) => {
e.stopPropagation();
const colorInput = colorField.querySelector('.oxymade-color-input');
if (colorInput) {
// Trigger spectrum picker if available
if (window.parent.jQuery && window.parent.jQuery.fn.spectrum) {
const $ = window.parent.jQuery;
const $colorInput = $(colorInput);
// Check if picker is already open
const isOpen = this.safeSpectrum($colorInput, 'get') !== null && $('.sp-container').is(':visible');
if (isOpen) {
// Close the picker
this.safeSpectrum($colorInput, 'hide');
} else {
// Store click position for positioning
window.lastClickPosition = { x: e.pageX, y: e.pageY };
// Show the picker
this.safeSpectrum($colorInput, 'show');
}
}
}
});
});
// Add clipboard functionality to color swatches
// Initialize color pickers
this.initializeColorPickers(panel);
// Initialize cancel button
this.initializeCancelButton(panel);
},
// Update palette when primary color changes
updatePalette(primaryColor) {
// Check if ColorPalette module is available
if (!window.OxyMade || !window.OxyMade.ColorPalette) {
console.error('🎨 OxyMade: ColorPalette module not available');
return;
}
const palette = window.OxyMade.ColorPalette.generateColorPalette(primaryColor);
// Apply CSS variables to the document root
this.applyCSSVariables(palette);
// Update primary color input and preview
const primaryInput = window.parent.document.querySelector('#primary-color-picker');
const primaryPreview = window.parent.document.querySelector('.oxymade-color-preview');
if (primaryInput) primaryInput.value = primaryColor;
if (primaryPreview) primaryPreview.style.backgroundColor = primaryColor;
// Update all color swatches
const colorSwatches = window.parent.document.querySelectorAll('.color-swatch-preview');
colorSwatches.forEach(swatch => {
const colorName = swatch.closest('[data-color]')?.dataset.color;
const shadeName = swatch.closest('[data-shade]')?.dataset.shade;
if (colorName && shadeName) {
const cssVar = `--${shadeName}-${colorName}`;
const colorValue = palette[cssVar] || palette[`--base-${colorName}`] || primaryColor;
swatch.style.backgroundColor = colorValue;
// Update corresponding input field
const input = swatch.closest('.color-swatch')?.querySelector('.shade-color-picker');
if (input) {
input.value = colorValue;
}
}
});
},
// Initialize color pickers for all color inputs
initializeColorPickers(panel) {
// Load jQuery in parent window if needed
const loadParentJQuery = () => {
const script = window.parent.document.createElement('script');
script.src = 'https://code.jquery.com/jquery-3.7.1.min.js';
script.type = 'text/javascript';
script.onload = initPickers;
window.parent.document.head.appendChild(script);
};
// Fallback: Load spectrum directly if WordPress enqueue fails
const loadSpectrumFallback = () => {
// Load Spectrum CSS
const cssLink = window.parent.document.createElement('link');
cssLink.rel = 'stylesheet';
cssLink.href = window.oxymadeSettings.pluginUrl + 'assets/css/sp.css';
window.parent.document.head.appendChild(cssLink);
// Load Spectrum JS
const script = window.parent.document.createElement('script');
script.src = window.oxymadeSettings.pluginUrl + 'assets/js/spectrum.js';
script.type = 'text/javascript';
script.onload = () => {
initializeSpectrumPickers();
};
script.onerror = () => {
console.error('Failed to load Spectrum JS');
};
window.parent.document.head.appendChild(script);
};
// Initialize color pickers
const initPickers = () => {
if (!window.parent.jQuery) {
loadParentJQuery();
return;
}
// Check if spectrum is already loaded
if (window.parent.jQuery.fn.spectrum) {
initializeSpectrumPickers();
return;
}
// Load spectrum directly via JavaScript (simpler approach)
loadSpectrumFallback();
};
// Initialize spectrum pickers (separated for clarity)
const initializeSpectrumPickers = () => {
const $ = window.parent.jQuery;
// Get the current primary color from the input field
const currentPrimaryColor = $('#primary-color-picker', window.parent.document).val() || '#3b82f6';
// Function to update color swatches and main color previews
const updateColorSwatches = (palette) => {
Object.keys(palette).forEach(key => {
const colorValue = palette[key];
// Parse key like "--bg-primary" or "--base-primary"
const cleanKey = key.replace(/^--/, '');
const parts = cleanKey.split('-');
// For keys like "bg-primary", shade is first part, color is second
// For keys like "base-primary", shade is "base", color is second
if (parts.length >= 2) {
const shadeName = parts[0];
const colorName = parts.slice(1).join('-'); // Handle multi-part color names like "base-white"
// Update individual swatch preview
$(`.color-swatch[data-color="${colorName}"][data-shade="${shadeName}"] .color-swatch-preview`, window.parent.document)
.css('background-color', colorValue);
// Update main color previews (for the base shade of each color)
if (shadeName === 'base') {
$(`.color-main-preview[data-color="${colorName}"]`, window.parent.document)
.css('background-color', colorValue);
}
}
});
// Update all main color previews with their base colors
const colorGroups = ['primary', 'secondary', 'tertiary', 'accent', 'neutral'];
colorGroups.forEach(colorName => {
const baseColor = palette[`--base-${colorName}`] || palette[`--${colorName}`];
if (baseColor) {
$(`.color-main-preview[data-color="${colorName}"]`, window.parent.document)
.css('background-color', baseColor);
}
});
};
// Common options for all pickers (exact copy from breakmade.js)
const commonOptions = {
type: 'component',
showInitial: true,
showPalette: true,
showPaletteOnly: true,
togglePaletteOnly: true,
showInput: true,
preferredFormat: 'hex',
palette: [
['#F9FAFB', '#F3F4F6', '#E5E7EB', '#D1D5DB', '#9CA3AF', '#6B7280', '#4B5563', '#374151', '#1F2937', '#111827'],
['#FAFAFA', '#F5F5F5', '#E5E5E5', '#D4D4D4', '#A3A3A3', '#737373', '#525252', '#404040', '#262626', '#171717'],
['#FEF2F2', '#FEE2E2', '#FECACA', '#FCA5A5', '#F87171', '#EF4444', '#DC2626', '#B91C1C', '#991B1B', '#7F1D1D'],
['#FFFBEB', '#FEF3C7', '#FDE68A', '#FCD34D', '#FBBF24', '#F59E0B', '#D97706', '#B45309', '#92400E', '#78350F'],
['#F0FDF4', '#DCFCE7', '#BBF7D0', '#86EFAC', '#4ADE80', '#22C55E', '#16A34A', '#15803D', '#166534', '#14532D'],
['#F0FDFA', '#CCFBF1', '#99F6E4', '#5EEAD4', '#2DD4BF', '#14B8A6', '#0D9488', '#0F766E', '#115E59', '#134E4A'],
['#F0F9FF', '#E0F2FE', '#BAE6FD', '#7DD3FC', '#38BDF8', '#0EA5E9', '#0284C7', '#0369A1', '#075985', '#0C4A6E'],
['#EFF6FF', '#DBEAFE', '#BFDBFE', '#93C5FD', '#60A5FA', '#3B82F6', '#2563EB', '#1D4ED8', '#1E40AF', '#1E3A8A'],
['#EEF2FF', '#E0E7FF', '#C7D2FE', '#A5B4FC', '#818CF8', '#6366F1', '#4F46E5', '#4338CA', '#3730A3', '#312E81'],
['#F5F3FF', '#EDE9FE', '#DDD6FE', '#C4B5FD', '#A78BFA', '#8B5CF6', '#7C3AED', '#6D28D9', '#5B21B6', '#4C1D95'],
['#FDF2F8', '#FCE7F3', '#FBCFE8', '#F9A8D4', '#F472B6', '#EC4899', '#DB2777', '#BE185D', '#9D174D', '#831843'],
['#FFF1F2', '#FFE4E6', '#FECDD3', '#FDA4AF', '#FB7185', '#F43F5E', '#E11D48', '#BE123C', '#9F1239', '#881337'],
['#ffffff', '#000000']
]
};
// Initialize primary color picker (exact copy from breakmade.js)
const $primaryColorPicker = $('#primary-color-picker', window.parent.document);
if ($primaryColorPicker.length && $.fn.spectrum) {
$primaryColorPicker.spectrum({
...commonOptions,
color: currentPrimaryColor,
showAlpha: false,
change: function (color) {
const primaryColor = color.toHexString();
// Update the main primary color preview directly (not using CSS variable)
$(this).closest('.oxymade-color-field').find('.oxymade-color-preview').css('background-color', primaryColor);
$(this).closest('.oxymade-color-field').find('.oxymade-color-value').text(primaryColor);
// Generate and apply palette automatically
if (!window.OxyMade || !window.OxyMade.ColorPalette) {
console.warn('🎨 OxyMade: ColorPalette module not available, waiting...');
// Wait for ColorPalette module to load
setTimeout(() => {
if (window.OxyMade && window.OxyMade.ColorPalette) {
const palette = window.OxyMade.ColorPalette.generateColorPalette(primaryColor);
// Apply palette
Object.keys(palette).forEach(key => {
const cssVarName = key.startsWith('--') ? key : `--${key}`;
window.parent.document.documentElement.style.setProperty(cssVarName, palette[key]);
updateBreakdanceVariables(key, palette[key], window.parent);
});
// Update color swatches
updateColorSwatches(palette);
updateIframeColors(palette);
} else {
console.error('🎨 OxyMade: ColorPalette module still not available after timeout');
}
}, 500);
return;
}
const palette = window.OxyMade.ColorPalette.generateColorPalette(primaryColor);
// Apply to CSS variables in parent window
Object.keys(palette).forEach(key => {
const cssVarName = key.startsWith('--') ? key : `--${key}`;
window.parent.document.documentElement.style.setProperty(cssVarName, palette[key]);
// Update Breakdance variables if sync is enabled
if (window.OxyMade.ColorPanel.isSyncWithOxygenEnabled()) {
updateBreakdanceVariables(key, palette[key], window.parent);
}
});
// Update color swatches
updateColorSwatches(palette);
// Update the main color preview for primary color
$(`.color-main-preview[data-color="primary"]`, window.parent.document)
.css('background-color', primaryColor);
// Update the main color picker for primary color
const $primaryMainPicker = $(`.color-main-preview[data-color="primary"]`, window.parent.document);
if ($primaryMainPicker.length && $primaryMainPicker.hasClass('sp-replacer')) {
$primaryMainPicker.spectrum('set', primaryColor);
}
updateIframeColors(palette);
// Auto-save the palette when primary color changes
setTimeout(() => {
window.OxyMade.ColorPanel.savePaletteToDatabase(null, true);
}, 100);
// Close the picker after selection
$(this).spectrum('hide');
}
});
}
// Initialize the MAIN COLOR PREVIEW pickers first - these will update ALL shades (exact copy from breakmade.js)
$('.color-main-preview', window.parent.document).each(function () {
const $this = $(this);
const colorName = $this.data('color');
// For neutral color, main shade is 700, for others it's 500
const mainShade = colorName === 'neutral' ? 'text' : 'base';
// Get current color from CSS variable
const currentColor = getComputedStyle(window.parent.document.documentElement)
.getPropertyValue(`--${mainShade}-${colorName}`).trim();
$this.spectrum({
...commonOptions,
color: currentColor,
showAlpha: false,
change: function (color) {
const newColor = color.toHexString();
// Update CSS variable for the main shade
window.parent.document.documentElement.style.setProperty(`--${mainShade}-${colorName}`, newColor);
window.parent.document.documentElement.style.setProperty(`--${colorName}`, newColor);
// Update main preview background
$(`.color-main-preview[data-color="${colorName}"]`, window.parent.document)
.css('background-color', newColor);
// // Update the swatch for the main shade
// $(`.color-swatch[data-color="${colorName}"][data-shade="${mainShade}"] .color-swatch-preview`, window.parent.document)
// .css('background-color', newColor);
// Update the spectrum picker for the main shade
$(`.shade-color-picker[data-color="${colorName}"][data-shade="${mainShade}"]`, window.parent.document)
.spectrum('set', newColor);
// Generate all shades based on this new color
let shades = {};
if (colorName === 'neutral') {
shades = window.OxyMade.ColorPalette.generateNeutralShades(newColor, colorName);
} else {
shades = window.OxyMade.ColorPalette.generateShadesForColor(newColor);
}
// Apply all shades to CSS variables and update UI
Object.keys(shades).forEach(shadeKey => {
const shadeColor = shades[shadeKey];
// Fix: Use proper CSS variable format with color name prefix
const varName = `--${shadeKey}-${colorName}`;
// Update CSS variable
window.parent.document.documentElement.style.setProperty(varName, shadeColor);
// Update swatch preview
$(`.color-swatch[data-color="${colorName}"][data-shade="${shadeKey}"] .color-swatch-preview`, window.parent.document)
.css('background-color', shadeColor);
// Update spectrum picker if it exists
const $picker = $(`.shade-color-picker[data-color="${colorName}"][data-shade="${shadeKey}"]`, window.parent.document);
if ($picker.length) {
$picker.spectrum('set', shadeColor);
}
});
// Update Breakdance variables
updateBreakdanceVariables(`${mainShade}-${colorName}`, newColor, window.parent);
// If this is the primary color, update the main primary color picker
if (colorName === 'primary') {
// Update the main primary color picker
const $mainPrimaryPicker = $('#primary-color-picker', window.parent.document);
if ($mainPrimaryPicker.length) {
$mainPrimaryPicker.spectrum('set', newColor);
}
// Update the main primary color preview
$(`.oxymade-color-preview`, window.parent.document)
.css('background-color', newColor);
// Update the main primary color value display
$(`.oxymade-color-value`, window.parent.document)
.text(newColor);
}
// Update iframes with the new shades
updateIframeColors(shades);
// Update Breakdance variables if sync is enabled
if (window.OxyMade.ColorPanel.isSyncWithOxygenEnabled()) {
Object.keys(shades).forEach(shadeKey => {
const shadeColor = shades[shadeKey];
updateBreakdanceVariables(`${shadeKey}-${colorName}`, shadeColor, window.parent);
});
}
// Auto-save the palette when main color changes
setTimeout(() => {
window.OxyMade.ColorPanel.savePaletteToDatabase(null, true);
}, 100);
// Close the picker after selection
$(this).spectrum('hide');
}
});
});
// Initialize the INDIVIDUAL SHADE pickers - these will only update their specific shade (exact copy from breakmade.js)
$('.shade-color-picker', window.parent.document).each(function () {
const $this = $(this);
const colorName = $this.data('color');
const shade = $this.data('shade');
// Get current color from CSS variable
const currentColor = getComputedStyle(window.parent.document.documentElement)
.getPropertyValue(`--${shade}-${colorName}`).trim();
$this.spectrum({
...commonOptions,
color: currentColor,
showAlpha: false,
change: function (color) {
const newColor = color.toHexString();
// Update CSS variable
window.parent.document.documentElement.style.setProperty(`--${shade}-${colorName}`, newColor);
// Update swatch preview
$(`.color-swatch[data-color="${colorName}"][data-shade="${shade}"] .color-swatch-preview`, window.parent.document)
.css('background-color', newColor);
// For neutral color, main shade is 700, for others it's 500
const mainShade = colorName === 'neutral' ? 'text' : 'base';
// If this is the main shade, update the main color preview as well
if (shade === mainShade) {
// Update base variable too
window.parent.document.documentElement.style.setProperty(`--${colorName}`, newColor);
// Update main preview
$(`.color-main-preview[data-color="${colorName}"]`, window.parent.document)
.css('background-color', newColor);
// Update the main color picker if it exists
const $mainPreview = $(`.color-main-preview[data-color="${colorName}"]`, window.parent.document);
if ($mainPreview.length && $mainPreview.hasClass('sp-replacer')) {
$mainPreview.spectrum('set', newColor);
}
// Update section header preview
$(`.color-section-header[data-color="${colorName}"] .color-swatch-preview`, window.parent.document)
.css('background-color', newColor);
}
// Update Breakdance variables if sync is enabled
if (window.OxyMade.ColorPanel.isSyncWithOxygenEnabled()) {
updateBreakdanceVariables(`${shade}-${colorName}`, newColor, window.parent);
}
// Update iframes with the new color
updateIframeColors({ [`--${shade}-${colorName}`]: newColor });
// Auto-save the palette when individual shade changes
setTimeout(() => {
window.OxyMade.ColorPanel.savePaletteToDatabase(null, true);
}, 100);
// Close the picker after selection
$(this).spectrum('hide');
}
});
});
// Setup click handlers for color previews (exact copy from breakmade.js)
$('.color-main-preview', window.parent.document).on('click', function (e) {
e.stopPropagation(); // Prevent section toggle
// Open the spectrum picker directly attached to this element (not the shade picker)
$(this).spectrum('show');
});
$('.color-swatch-preview', window.parent.document).on('click', function (e) {
e.stopPropagation(); // Prevent section toggle
const colorName = $(this).parent().data('color');
const shade = $(this).parent().data('shade');
// Find the relevant color picker and show it
const $picker = $(`.shade-color-picker[data-color="${colorName}"][data-shade="${shade}"]`, window.parent.document);
if ($picker.length) {
$picker.spectrum('show');
}
});
// Make color preview divs clickable to open the picker (exact copy from breakmade.js)
$('.oxymade-color-preview, .oxymade-color-value, .oxymade-color-field', window.parent.document).on('click', function () {
// Find the associated color picker and trigger it
const colorPicker = $(this).closest('.oxymade-color-field').find('.oxymade-color-input');
colorPicker.spectrum('show');
return false;
});
// Add ESC key functionality to close spectrum pickers
$(document).on('keydown', function(e) {
if (e.key === 'Escape' && $('.sp-container').is(':visible')) {
$('.sp-container').hide();
}
});
};
// Function to update Breakdance variables based on the color key (exact copy from breakmade.js)
function updateBreakdanceVariables(key, value, parentWindow) {
// Map our CSS variables to Breakdance CSS variables
const breakdanceVariableMap = {
'base-primary': '--bde-brand-primary-color',
'hover-primary': '--bde-brand-primary-color-hover',
'text-neutral': '--bde-body-text-color',
'heading-neutral': '--bde-headings-color',
'bg-neutral': '--bde-background-color',
'base-secondary': '--bde-brand-secondary-color',
'base-tertiary': '--bde-brand-tertiary-color',
'base-accent': '--bde-brand-accent-color'
};
// Check if this key has a corresponding Breakdance variable
if (breakdanceVariableMap[key]) {
parentWindow.document.documentElement.style.setProperty(
breakdanceVariableMap[key],
value
);
}
}
// Function to update iframe colors (exact copy from breakmade.js)
function updateIframeColors(palette) {
// First, update the parent window's CSS variables
Object.keys(palette).forEach(key => {
const value = palette[key];
if (value) {
// Key already contains -- prefix, don't add another one
const cssVarName = key.startsWith('--') ? key : `--${key}`;
window.parent.document.documentElement.style.setProperty(cssVarName, value);
}
});
// Then find all iframes in the parent window and update them
const iframes = window.parent.document.querySelectorAll('iframe');
// Loop through each iframe
iframes.forEach(iframe => {
try {
// Make sure we have access to the iframe's document
if (iframe.contentDocument && iframe.contentWindow) {
// For each color variable in our palette
Object.keys(palette).forEach(key => {
const value = palette[key];
if (value) {
// Key already contains -- prefix, don't add another one
const cssVarName = key.startsWith('--') ? key : `--${key}`;
iframe.contentDocument.documentElement.style.setProperty(cssVarName, value);
}
});
// If the iframe has its own iframes, update those too
const nestedIframes = iframe.contentDocument.querySelectorAll('iframe');
if (nestedIframes.length > 0) {
nestedIframes.forEach(nestedFrame => {
try {
if (nestedFrame.contentDocument) {
Object.keys(palette).forEach(key => {
const value = palette[key];
if (value) {
// Key already contains -- prefix, don't add another one
const cssVarName = key.startsWith('--') ? key : `--${key}`;
nestedFrame.contentDocument.documentElement.style.setProperty(cssVarName, value);
}
});
}
} catch (e) {
// Ignore cross-origin errors
}
});
}
}
} catch (e) {
// Ignore cross-origin errors
}
});
}
// Now use the functions after they're defined
if (!window.parent.jQuery) {
loadParentJQuery();
} else {
initPickers();
}
},
// Initialize cancel button
initializeCancelButton(panel) {
// Cancel button handler - just close the panel
const cancelButton = panel.querySelector('#cancel-palette');
if (cancelButton) {
cancelButton.addEventListener('click', () => {
panel.style.right = '-350px';
});
}
},
// Save palette to database
savePalette() {
const $ = window.parent.jQuery;
// Get all CSS color variables
const styles = getComputedStyle(window.parent.document.documentElement);
const flatPalette = {};
// Collect all color variables
const colorGroups = ['primary', 'secondary', 'tertiary', 'accent', 'neutral', 'base-white', 'base-black'];
const shades = ['bg', 'surface', 'subtle', 'border', 'muted', 'base', 'hover', 'text', 'active', 'heading', 'dark'];
const simpleColors = ['base-white', 'base-black'];
colorGroups.forEach(color => {
let colorValue;
if (color === 'base-white' || color === 'base-black') {
colorValue = styles.getPropertyValue(`--${color}`).trim();
} else {
colorValue = styles.getPropertyValue(`--base-${color}`).trim();
}
if (!simpleColors.includes(color)) {
// Convert hex to RGB and store only RGB values
const rgbColor = window.OxyMade.ColorUtils.hexToRgb(colorValue);
flatPalette[color + '-rgb'] = rgbColor ? `${rgbColor.r}, ${rgbColor.g}, ${rgbColor.b}` : colorValue;
}
// Only add shades for colors that aren't white or black
if (!simpleColors.includes(color)) {
shades.forEach(shade => {
flatPalette[`${shade}-${color}`] = styles.getPropertyValue(`--${shade}-${color}`).trim();
});
}
if (color === 'base-white') {
flatPalette['base-white'] = styles.getPropertyValue('--base-white').trim();
const whiteRgb = window.OxyMade.ColorUtils.hexToRgb(colorValue);
flatPalette['white-rgb'] = whiteRgb ? `${whiteRgb.r}, ${whiteRgb.g}, ${whiteRgb.b}` : colorValue;
}
if (color === 'base-black') {
flatPalette['base-black'] = styles.getPropertyValue('--base-black').trim();
const blackRgb = window.OxyMade.ColorUtils.hexToRgb(colorValue);
flatPalette['black-rgb'] = blackRgb ? `${blackRgb.r}, ${blackRgb.g}, ${blackRgb.b}` : colorValue;
}
});
// Format the palette the way the server expects it
const formattedPalette = {
primary: {
base: flatPalette.primary,
status: 'active',
shades: {
bg: flatPalette['bg-primary'],
surface: flatPalette['surface-primary'],
subtle: flatPalette['subtle-primary'],
border: flatPalette['border-primary'],
muted: flatPalette['muted-primary'],
base: flatPalette['base-primary'],
hover: flatPalette['hover-primary'],
text: flatPalette['text-primary'],
active: flatPalette['active-primary'],
heading: flatPalette['heading-primary'],
dark: flatPalette['dark-primary']
}
},
secondary: {
base: flatPalette.secondary,
status: 'active',
shades: {
bg: flatPalette['bg-secondary'],
surface: flatPalette['surface-secondary'],
subtle: flatPalette['subtle-secondary'],
border: flatPalette['border-secondary'],
muted: flatPalette['muted-secondary'],
base: flatPalette['base-secondary'],
hover: flatPalette['hover-secondary'],
text: flatPalette['text-secondary'],
active: flatPalette['active-secondary'],
heading: flatPalette['heading-secondary'],
dark: flatPalette['dark-secondary']
}
},
tertiary: {
base: flatPalette.tertiary,
status: 'active',
shades: {
bg: flatPalette['bg-tertiary'],
surface: flatPalette['surface-tertiary'],
subtle: flatPalette['subtle-tertiary'],
border: flatPalette['border-tertiary'],
muted: flatPalette['muted-tertiary'],
base: flatPalette['base-tertiary'],
hover: flatPalette['hover-tertiary'],
text: flatPalette['text-tertiary'],
active: flatPalette['active-tertiary'],
heading: flatPalette['heading-tertiary'],
dark: flatPalette['dark-tertiary']
}
},
accent: {
base: flatPalette.accent,
status: 'active',
shades: {
bg: flatPalette['bg-accent'],
surface: flatPalette['surface-accent'],
subtle: flatPalette['subtle-accent'],
border: flatPalette['border-accent'],
muted: flatPalette['muted-accent'],
base: flatPalette['base-accent'],
hover: flatPalette['hover-accent'],
text: flatPalette['text-accent'],
active: flatPalette['active-accent'],
heading: flatPalette['heading-accent'],
dark: flatPalette['dark-accent']
}
},
neutral: {
base: flatPalette.neutral,
status: 'active',
shades: {
bg: flatPalette['bg-neutral'],
surface: flatPalette['surface-neutral'],
subtle: flatPalette['subtle-neutral'],
border: flatPalette['border-neutral'],
muted: flatPalette['muted-neutral'],
base: flatPalette['base-neutral'],
hover: flatPalette['hover-neutral'],
text: flatPalette['text-neutral'],
active: flatPalette['active-neutral'],
heading: flatPalette['heading-neutral'],
dark: flatPalette['dark-neutral']
}
},
'base-white': {
base: flatPalette['base-white'] || '#ffffff'
},
'base-black': {
base: flatPalette['base-black'] || '#101010'
}
};
// Save via AJAX
$.ajax({
url: window.oxymadeSettings.ajaxUrl,
type: 'POST',
data: {
action: 'oxymade_save_palette',
nonce: window.oxymadeSettings.nonce,
palette: formattedPalette,
flatPalette: flatPalette
},
success: function(response) {
if (response.success) {
// Show success notification
// You can add a visual notification here
} else {
console.error('🎨 OxyMade: Failed to save palette');
}
},
error: function(xhr, status, error) {
console.error('🎨 OxyMade: Error saving palette:', error);
}
});
},
// Check if sync with Oxygen colors is enabled
isSyncWithOxygenEnabled() {
// Check both iframe and parent window for settings
const settings = window.oxymadeSettings || window.parent.oxymadeSettings;
// Check if we have the setting in oxymadeSettings
if (settings?.syncWithOxygen !== undefined) {
return settings.syncWithOxygen;
}
// Default to true if setting not found
return true;
},
// Save palette to database
savePaletteToDatabase(button = null, showNotification = false) {
const colorData = this.collectCurrentColorData();
const flatPalette = this.createFlatPalette(colorData);
const $ = window.parent.jQuery;
if (!$) {
return;
}
// Check both iframe and parent window for settings
const settings = window.oxymadeSettings || window.parent.oxymadeSettings;
const ajaxUrl = settings?.ajaxUrl || '/wp-admin/admin-ajax.php';
const nonce = settings?.nonce || '';
const syncEnabled = window.OxyMade.ColorPanel.isSyncWithOxygenEnabled();
// Show loading state on button
if (button) {
window.OxyMade.ColorPanel.setButtonLoading(button, true);
}
$.ajax({
url: ajaxUrl,
type: 'POST',
data: {
action: 'oxymade_save_palette',
palette: colorData,
flatPalette: flatPalette,
nonce: nonce
},
success: function(response) {
if (response.success) {
// Check if we should sync with Oxygen colors
if (syncEnabled) {
// Sync with Oxygen colors by updating the main primary color picker
const primaryColor = flatPalette['base-primary'] || flatPalette['primary'];
if (primaryColor) {
const $primaryPicker = $('#primary-color-picker', window.parent.document);
if ($primaryPicker.length) {
$primaryPicker.spectrum('set', primaryColor);
}
}
}
// Show notification if requested
if (showNotification) {
const message = syncEnabled
? 'Colors saved & synced! Refresh builder to see changes.'
: 'Colors saved! Refresh builder to see changes.';
window.OxyMade.ColorPanel.showNotification(message, 'success');
}
} else {
window.OxyMade.ColorPanel.showNotification('Failed to save colors.', 'error');
}
// Reset button state
if (button) {
window.OxyMade.ColorPanel.setButtonLoading(button, false);
}
},
error: function(xhr, status, error) {
window.OxyMade.ColorPanel.showNotification('Error saving colors.', 'error');
// Reset button state
if (button) {
window.OxyMade.ColorPanel.setButtonLoading(button, false);
}
}
});
},
// Cancel palette changes (revert to saved state and close panel)
cancelPaletteChanges(button = null) {
if (button) {
window.OxyMade.ColorPanel.setButtonLoading(button, true);
}
const $ = window.parent.jQuery;
// Check both iframe and parent window for settings
const settings = window.oxymadeSettings || window.parent.oxymadeSettings;
$.ajax({
url: settings?.ajaxUrl || '/wp-admin/admin-ajax.php',
type: 'GET',
data: {
action: 'oxymade_get_palette',
nonce: settings?.nonce || ''
},
success: (response) => {
if (response.success && response.data) {
window.OxyMade.ColorPanel.applySavedPalette(response.data);
window.OxyMade.ColorPanel.showNotification('Changes reverted to saved state', 'success');
} else {
window.OxyMade.ColorPanel.showNotification('No saved palette found', 'info');
}
},
error: () => {
window.OxyMade.ColorPanel.showNotification('Error loading saved palette', 'error');
},
complete: () => {
if (button) {
window.OxyMade.ColorPanel.setButtonLoading(button, false);
}
// Close the panel after resetting
const panel = document.querySelector('.oxymade-color-panel');
if (panel) {
panel.style.right = '-350px';
}
}
});
},
// Collect current color data from the panel (read from CSS variables like breakmade)
collectCurrentColorData() {
const styles = getComputedStyle(window.parent.document.documentElement);
const colorData = {};
// Collect all color variables (exact same as breakmade)
const colorGroups = ['primary', 'secondary', 'tertiary', 'accent', 'neutral', 'base-white', 'base-black'];
const shades = ['bg', 'surface', 'subtle', 'border', 'muted', 'base', 'hover', 'text', 'active', 'heading', 'dark'];
colorGroups.forEach(color => {
colorData[color] = {};
if (color === 'base-white' || color === 'base-black') {
colorData[color].base = styles.getPropertyValue(`--${color}`).trim();
} else {
colorData[color].base = styles.getPropertyValue(`--base-${color}`).trim();
colorData[color].shades = {};
shades.forEach(shade => {
colorData[color].shades[shade] = styles.getPropertyValue(`--${shade}-${color}`).trim();
});
}
});
return colorData;
},
// Create flat palette format for PHP (exact match with breakmade format)
createFlatPalette(colorData) {
const flatPalette = {};
const simpleColors = ['base-white', 'base-black']; // Colors without shades
Object.keys(colorData).forEach(colorName => {
const colorInfo = colorData[colorName];
if (colorName === 'base-white' || colorName === 'base-black') {
// Simple colors - store direct values
flatPalette[colorName] = colorInfo.base;
// Add RGB values for white and black
const rgbColor = this.hexToRgb(colorInfo.base);
if (colorName === 'base-white') {
flatPalette['white-rgb'] = rgbColor ? `${rgbColor.r}, ${rgbColor.g}, ${rgbColor.b}` : colorInfo.base;
} else if (colorName === 'base-black') {
flatPalette['black-rgb'] = rgbColor ? `${rgbColor.r}, ${rgbColor.g}, ${rgbColor.b}` : colorInfo.base;
}
} else {
// Colors with shades - store base color and RGB
flatPalette[`base-${colorName}`] = colorInfo.base;
// Add RGB values for main colors (not base-white and base-black)
const rgbColor = this.hexToRgb(colorInfo.base);
flatPalette[colorName + '-rgb'] = rgbColor ? `${rgbColor.r}, ${rgbColor.g}, ${rgbColor.b}` : colorInfo.base;
// Add all shades
if (colorInfo.shades) {
Object.keys(colorInfo.shades).forEach(shade => {
flatPalette[`${shade}-${colorName}`] = colorInfo.shades[shade];
});
}
}
});
return flatPalette;
},
// Helper function to convert hex to RGB (matching breakmade's ColorUtils)
hexToRgb(hex) {
// Remove # if present
hex = hex.replace(/^#/, '');
// Parse hex values
let r, g, b;
if (hex.length === 3) {
r = parseInt(hex.charAt(0) + hex.charAt(0), 16);
g = parseInt(hex.charAt(1) + hex.charAt(1), 16);
b = parseInt(hex.charAt(2) + hex.charAt(2), 16);
} else {
r = parseInt(hex.substring(0, 2), 16);
g = parseInt(hex.substring(2, 4), 16);
b = parseInt(hex.substring(4, 6), 16);
}
return { r, g, b };
},
// Ensure jQuery is loaded before proceeding
ensureJQueryLoaded(callback) {
if (window.parent.jQuery) {
callback();
return;
}
const script = window.parent.document.createElement('script');
script.src = 'https://code.jquery.com/jquery-3.7.1.min.js';
script.onload = callback;
window.parent.document.head.appendChild(script);
},
// Load saved palette from database
loadSavedPalette(callback) {
const $ = window.parent.jQuery;
// Use iframe's oxymadeSettings first, fallback to parent window
const settings = window.oxymadeSettings || window.parent.oxymadeSettings;
if (!settings || !settings.ajaxUrl) {
console.error('🎨 OxyMade: oxymadeSettings not available, creating fallback...');
// Create fallback settings
const fallbackSettings = {
ajaxUrl: window.parent.location.origin + '/wp-admin/admin-ajax.php',
nonce: 'fallback-nonce'
};
settings = fallbackSettings;
}
$.ajax({
url: settings.ajaxUrl,
type: 'GET',
data: {
action: 'oxymade_get_palette',
nonce: settings.nonce
},
success: function(response) {
let paletteLoaded = false;
if (response.success && response.data && response.data.flatPalette) {
// Apply loaded palette to CSS variables
Object.keys(response.data.flatPalette).forEach(key => {
const value = response.data.flatPalette[key];
if (value) {
const cssVarName = key.startsWith('--') ? key : `--${key}`;
window.parent.document.documentElement.style.setProperty(cssVarName, value);
}
});
// Update color picker fields with loaded values
window.OxyMade.ColorPanel.updateColorPickerFields(response.data.flatPalette);
paletteLoaded = true;
}
if (callback) callback(paletteLoaded);
},
error: function(xhr, status, error) {
console.error('🎨 OxyMade: Error loading palette:', {xhr, status, error});
if (callback) callback(false);
}
});
},
// Update color picker fields with loaded values
updateColorPickerFields(flatPalette, retryCount = 0) {
const $ = window.parent.jQuery;
// Check if Spectrum is loaded before calling .spectrum()
if (!$.fn.spectrum) {
if (retryCount < 10) { // Max 10 retries (1 second total)
setTimeout(() => {
this.updateColorPickerFields(flatPalette, retryCount + 1);
}, 100);
return;
} else {
console.error('Spectrum color picker failed to load after 10 attempts, skipping color updates');
return;
}
}
// Update main primary color picker
if (flatPalette['base-primary']) {
const primaryInput = $('#primary-color-picker', window.parent.document);
if (primaryInput.length) {
primaryInput.spectrum('set', flatPalette['base-primary']);
}
}
// Update main color pickers
const mainColors = ['primary', 'secondary', 'tertiary', 'accent', 'neutral'];
mainColors.forEach(color => {
const baseKey = `base-${color}`;
if (flatPalette[baseKey]) {
const input = $(`#${color}-color-picker`, window.parent.document);
if (input.length) {
input.spectrum('set', flatPalette[baseKey]);
}
}
});
// Update individual shade pickers
const shades = ['bg', 'surface', 'subtle', 'border', 'muted', 'base', 'hover', 'text', 'active', 'heading', 'dark'];
mainColors.forEach(color => {
shades.forEach(shade => {
const key = `${shade}-${color}`;
if (flatPalette[key]) {
const input = $(`#${shade}-${color}-picker`, window.parent.document);
if (input.length) {
input.spectrum('set', flatPalette[key]);
}
}
});
});
},
// Apply saved palette to the document
applySavedPalette(paletteData) {
// Apply each color and its shades
Object.keys(paletteData).forEach(colorName => {
const colorData = paletteData[colorName];
if (colorName === 'base-white' || colorName === 'base-black') {
// Simple colors
window.parent.document.documentElement.style.setProperty(`--${colorName}`, colorData.base);
} else {
// Colors with shades
window.parent.document.documentElement.style.setProperty(`--base-${colorName}`, colorData.base);
if (colorData.shades) {
Object.keys(colorData.shades).forEach(shade => {
window.parent.document.documentElement.style.setProperty(`--${shade}-${colorName}`, colorData.shades[shade]);
});
}
}
});
// Update the color panel UI to reflect the changes
this.updateColorPanelUI(paletteData);
},
// Update color panel UI with new values
updateColorPanelUI(paletteData) {
// Update color previews and input values
Object.keys(paletteData).forEach(colorName => {
const colorData = paletteData[colorName];
// Update main color input
const colorInput = window.parent.document.querySelector(`.oxymade-color-input[data-color="${colorName}"]`);
if (colorInput) {
colorInput.value = colorData.base;
// Update preview
const preview = colorInput.closest('.oxymade-color-field').querySelector('.oxymade-color-preview');
if (preview) {
preview.style.backgroundColor = colorData.base;
}
}
// Update shade inputs if they exist
if (colorData.shades) {
Object.keys(colorData.shades).forEach(shade => {
const shadeInput = window.parent.document.querySelector(`.oxymade-color-input[data-color="${colorName}"][data-shade="${shade}"]`);
if (shadeInput) {
shadeInput.value = colorData.shades[shade];
// Update preview
const preview = shadeInput.closest('.oxymade-color-field').querySelector('.oxymade-color-preview');
if (preview) {
preview.style.backgroundColor = colorData.shades[shade];
}
}
});
}
});
},
// Set button loading state
setButtonLoading(button, isLoading) {
// Handle both jQuery objects and DOM elements
const buttonElement = button.jquery ? button[0] : button;
if (isLoading) {
// Store original content
buttonElement.setAttribute('data-original-html', buttonElement.innerHTML);
buttonElement.setAttribute('data-original-text', buttonElement.textContent);
// Show loading state
if (buttonElement.id === 'save-palette') {
// Icon button - show spinner
buttonElement.innerHTML = `
<svg xmlns="https://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="spinner">
<path d="M21 12a9 9 0 11-6.219-8.56"/>
</svg>
`;
} else {
// Full button - show text with spinner
buttonElement.innerHTML = `
<svg xmlns="https://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="spinner">
<path d="M21 12a9 9 0 11-6.219-8.56"/>
</svg>
Saving...
`;
}
// Disable button
buttonElement.disabled = true;
buttonElement.style.opacity = '0.7';
buttonElement.style.cursor = 'not-allowed';
// Add spinner animation
const spinner = buttonElement.querySelector('.spinner');
if (spinner) {
spinner.style.animation = 'spin 1s linear infinite';
}
} else {
// Restore original content
const originalHtml = buttonElement.getAttribute('data-original-html');
if (originalHtml) {
buttonElement.innerHTML = originalHtml;
buttonElement.removeAttribute('data-original-html');
buttonElement.removeAttribute('data-original-text');
}
// Re-enable button
buttonElement.disabled = false;
buttonElement.style.opacity = '1';
buttonElement.style.cursor = 'pointer';
}
},
// Show notification to user inside the color panel
showNotification(message, type = 'success') {
try {
const saveStatus = window.parent.document.getElementById('oxymade-save-status');
if (!saveStatus) return;
const saveMessage = saveStatus.querySelector('.save-message');
const saveIcon = saveStatus.querySelector('.save-icon');
// Update message
if (saveMessage) {
saveMessage.textContent = message;
}
// Update icon and background color based on type
if (saveIcon) {
if (type === 'success') {
// Success checkmark icon
saveIcon.innerHTML = '<polyline points="20 6 9 17 4 12"></polyline>';
saveStatus.style.background = 'linear-gradient(135deg, #10b981 0%, #059669 100%)';
saveStatus.style.boxShadow = '0 2px 8px rgba(16, 185, 129, 0.25)';
} else if (type === 'error') {
// Error X icon
saveIcon.innerHTML = '<circle cx="12" cy="12" r="10"></circle><line x1="15" y1="9" x2="9" y2="15"></line><line x1="9" y1="9" x2="15" y2="15"></line>';
saveStatus.style.background = 'linear-gradient(135deg, #ef4444 0%, #dc2626 100%)';
saveStatus.style.boxShadow = '0 2px 8px rgba(239, 68, 68, 0.25)';
} else {
// Info icon
saveIcon.innerHTML = '<circle cx="12" cy="12" r="10"></circle><line x1="12" y1="16" x2="12" y2="12"></line><line x1="12" y1="8" x2="12.01" y2="8"></line>';
saveStatus.style.background = 'linear-gradient(135deg, #3b82f6 0%, #2563eb 100%)';
saveStatus.style.boxShadow = '0 2px 8px rgba(59, 130, 246, 0.25)';
}
}
// Show with fade in
saveStatus.style.display = 'flex';
saveStatus.style.opacity = '0';
setTimeout(() => {
saveStatus.style.opacity = '1';
}, 10);
// Hide after 2 seconds
setTimeout(() => {
saveStatus.style.opacity = '0';
setTimeout(() => {
saveStatus.style.display = 'none';
}, 300);
}, 2000);
} catch (error) {
// Silently fail
}
}
};