/** * Site Builder - GrapesJS Editor Configuration */ (function() { 'use strict'; // Storage key for localStorage const STORAGE_KEY = 'sitebuilder-project'; // Initialize GrapesJS Editor const editor = grapesjs.init({ container: '#gjs', height: '100%', width: 'auto', fromElement: false, // Storage configuration - localStorage storageManager: { type: 'local', autosave: true, autoload: true, stepsBeforeSave: 1, options: { local: { key: STORAGE_KEY } } }, // Device manager for responsive preview deviceManager: { devices: [ { name: 'Desktop', width: '' }, { name: 'Tablet', width: '768px', widthMedia: '992px' }, { name: 'Mobile', width: '375px', widthMedia: '480px' } ] }, // Layer manager configuration layerManager: { appendTo: '#layers-container' }, // Block manager configuration blockManager: { appendTo: '#blocks-container' }, // Style manager configuration styleManager: { appendTo: '#advanced-styles', sectors: [ { name: 'Dimension', open: false, properties: [ 'width', 'min-width', 'max-width', 'height', 'min-height', 'max-height', 'padding', 'padding-top', 'padding-right', 'padding-bottom', 'padding-left', 'margin', 'margin-top', 'margin-right', 'margin-bottom', 'margin-left' ] }, { name: 'Typography', open: false, properties: [ { property: 'font-family', type: 'select', options: [ { id: 'Inter, sans-serif', label: 'Inter' }, { id: 'Roboto, sans-serif', label: 'Roboto' }, { id: 'Open Sans, sans-serif', label: 'Open Sans' }, { id: 'Poppins, sans-serif', label: 'Poppins' }, { id: 'Montserrat, sans-serif', label: 'Montserrat' }, { id: 'Playfair Display, serif', label: 'Playfair Display' }, { id: 'Merriweather, serif', label: 'Merriweather' }, { id: 'Source Code Pro, monospace', label: 'Source Code Pro' }, { id: 'Arial, Helvetica, sans-serif', label: 'Arial' }, { id: 'Georgia, serif', label: 'Georgia' }, { id: 'Times New Roman, serif', label: 'Times New Roman' } ] }, 'font-size', { property: 'font-weight', type: 'select', options: [ { id: '100', label: 'Thin (100)' }, { id: '200', label: 'Extra Light (200)' }, { id: '300', label: 'Light (300)' }, { id: '400', label: 'Normal (400)' }, { id: '500', label: 'Medium (500)' }, { id: '600', label: 'Semi Bold (600)' }, { id: '700', label: 'Bold (700)' }, { id: '800', label: 'Extra Bold (800)' }, { id: '900', label: 'Black (900)' } ] }, { property: 'letter-spacing', type: 'number', units: ['px', 'em', 'rem'], default: '0', step: 0.1 }, { property: 'line-height', type: 'number', units: ['px', 'em', '%', ''], default: 'normal', step: 0.1 }, { property: 'text-align', type: 'radio', options: [ { id: 'left', label: 'Left', className: 'fa fa-align-left' }, { id: 'center', label: 'Center', className: 'fa fa-align-center' }, { id: 'right', label: 'Right', className: 'fa fa-align-right' }, { id: 'justify', label: 'Justify', className: 'fa fa-align-justify' } ] }, 'text-decoration', 'text-transform', 'color' ] }, { name: 'Background', open: false, properties: [ 'background-color', 'background-image', 'background-repeat', 'background-position', 'background-size', 'background-attachment' ] }, { name: 'Border', open: false, properties: [ 'border-radius', 'border-radius-top-left', 'border-radius-top-right', 'border-radius-bottom-left', 'border-radius-bottom-right', 'border', 'border-width', 'border-style', 'border-color' ] }, { name: 'Effects', open: false, properties: [ 'opacity', 'box-shadow', 'transition' ] }, { name: 'Layout', open: false, properties: [ 'display', 'position', 'top', 'right', 'bottom', 'left', 'flex-direction', 'flex-wrap', 'justify-content', 'align-items', 'align-content', 'gap', 'order', 'flex-basis', 'flex-grow', 'flex-shrink' ] } ] }, // Trait manager configuration traitManager: { appendTo: '#traits-container' }, // Selector manager (CSS classes) selectorManager: { appendTo: '#advanced-styles', componentFirst: true }, // Plugins - using global function references from CDN scripts plugins: [ window['gjs-blocks-basic'], window['grapesjs-preset-webpage'], window['grapesjs-plugin-forms'], window['grapesjs-style-gradient'] ].filter(Boolean), // Filter out any undefined plugins pluginsOpts: { [window['gjs-blocks-basic']]: { flexGrid: true, blocks: ['column1', 'column2', 'column3', 'column3-7', 'text', 'link', 'map'] }, [window['grapesjs-preset-webpage']]: { modalImportTitle: 'Import Template', modalImportLabel: '
Paste HTML/CSS here
', modalImportContent: '', importViewerRecords: true, textCleanCanvas: 'Are you sure you want to clear the canvas?', showStylesOnChange: true, useCustomTheme: false, blocks: ['link-block', 'quote', 'text-basic'] }, [window['grapesjs-plugin-forms']]: { blocks: ['form', 'input', 'textarea', 'select', 'button', 'label', 'checkbox', 'radio'] }, [window['grapesjs-style-gradient']]: { colorPicker: 'default' } }, // Canvas configuration canvas: { styles: [ // Google Fonts - Popular choices 'https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&family=Roboto:wght@300;400;500;700&family=Open+Sans:wght@300;400;500;600;700&family=Poppins:wght@300;400;500;600;700&family=Montserrat:wght@300;400;500;600;700&family=Playfair+Display:wght@400;500;600;700&family=Merriweather:wght@300;400;700&family=Source+Code+Pro:wght@400;500;600&display=swap', // Responsive images and base styles injected inline 'data:text/css,' + encodeURIComponent(` /* Responsive images */ img { max-width: 100%; height: auto; } /* Responsive video containers */ video { max-width: 100%; height: auto; } /* Responsive columns on mobile */ @media (max-width: 480px) { .row { flex-direction: column !important; } .row .cell { flex-basis: 100% !important; width: 100% !important; } } /* Editor-only anchor visualization */ .editor-anchor { position: relative; display: inline-flex; align-items: center; gap: 6px; min-height: 28px; border: 1px dashed #9ca3af; padding: 4px 8px; background: rgba(59,130,246,0.05); border-radius: 4px; } .editor-anchor .anchor-icon { font-size: 14px; color: #6b7280; line-height: 1; } .editor-anchor .anchor-name-input { border: none; background: transparent; color: #374151; font-size: 12px; font-family: Inter, sans-serif; font-weight: 500; padding: 2px 4px; outline: none; min-width: 80px; } .editor-anchor .anchor-name-input:focus { background: rgba(255,255,255,0.5); border-radius: 2px; } `) ], scripts: [] } }); // ========================================== // UI Elements // ========================================== const saveStatus = document.getElementById('save-status'); const statusText = saveStatus.querySelector('.status-text'); // ========================================== // Add Custom Blocks (Columns, Hero, etc.) // ========================================== const blockManager = editor.BlockManager; // Remove plugin-provided Image/Video blocks that duplicate the Media section's // custom blocks (which have browse-assets support and proper wrappers) blockManager.remove('image'); blockManager.remove('video'); // Section Block blockManager.add('section', { label: 'Section', category: 'Layout', content: { tagName: 'section', style: { 'padding': '60px 20px', 'min-height': '200px', 'background-color': '#ffffff' }, components: [ { tagName: 'div', style: { 'max-width': '1200px', 'margin': '0 auto' }, components: [] } ] }, attributes: { class: 'fa fa-columns' } }); // Logo Block blockManager.add('logo', { label: 'Logo', category: 'Layout', content: ``, attributes: { class: 'fa fa-bookmark' } }); // Navigation Menu (will be updated dynamically based on pages) blockManager.add('navbar', { label: 'Navigation', category: 'Layout', content: { type: 'navbar', tagName: 'nav', attributes: { class: 'site-navbar', 'data-dynamic-nav': 'true' }, style: { 'display': 'flex', 'align-items': 'center', 'justify-content': 'space-between', 'padding': '16px 24px', 'background': '#ffffff', 'border-bottom': '1px solid #e5e7eb' }, components: [ { tagName: 'a', attributes: { href: '#', class: 'nav-logo' }, style: { 'font-size': '20px', 'font-weight': '700', 'color': '#1f2937', 'text-decoration': 'none', 'font-family': 'Inter, sans-serif' }, content: 'Logo' }, { tagName: 'div', attributes: { class: 'nav-links' }, style: { 'display': 'flex', 'gap': '24px', 'align-items': 'center' }, components: [ { tagName: 'a', attributes: { href: '#' }, style: { 'color': '#4b5563', 'text-decoration': 'none', 'font-size': '15px', 'font-family': 'Inter, sans-serif' }, content: 'Home' }, { tagName: 'a', attributes: { href: '#' }, style: { 'color': '#4b5563', 'text-decoration': 'none', 'font-size': '15px', 'font-family': 'Inter, sans-serif' }, content: 'About' }, { tagName: 'a', attributes: { href: '#' }, style: { 'color': '#4b5563', 'text-decoration': 'none', 'font-size': '15px', 'font-family': 'Inter, sans-serif' }, content: 'Contact' }, { tagName: 'a', attributes: { href: '#', class: 'nav-cta' }, style: { 'display': 'inline-block', 'padding': '10px 20px', 'background': '#3b82f6', 'color': '#fff', 'text-decoration': 'none', 'border-radius': '6px', 'font-size': '14px', 'font-weight': '500', 'font-family': 'Inter, sans-serif' }, content: 'Get Started' } ] } ] }, attributes: { class: 'fa fa-bars' } }); // Section with Background (Image/Video + Overlay) blockManager.add('section-bg', { label: 'Section (Background)', category: 'Layout', content: { tagName: 'section', attributes: { class: 'section-with-bg', 'data-bg-section': 'true' }, style: { 'position': 'relative', 'min-height': '400px', 'display': 'flex', 'align-items': 'center', 'justify-content': 'center', 'background-image': 'url(https://images.unsplash.com/photo-1557683316-973673baf926?w=1920)', 'background-size': 'cover', 'background-position': 'center', 'padding': '60px 20px', 'overflow': 'hidden' }, components: [ // Overlay { tagName: 'div', attributes: { class: 'bg-overlay' }, style: { 'position': 'absolute', 'top': '0', 'left': '0', 'right': '0', 'bottom': '0', 'background': 'rgba(0,0,0,0.5)', 'z-index': '1' }, selectable: true, hoverable: true }, // Content container { tagName: 'div', attributes: { class: 'bg-content' }, style: { 'position': 'relative', 'z-index': '2', 'max-width': '800px', 'text-align': 'center' }, components: [ { tagName: 'h2', style: { 'color': '#ffffff', 'font-size': '36px', 'font-weight': '700', 'margin-bottom': '16px', 'font-family': 'Inter, sans-serif' }, content: 'Your Heading Here' }, { tagName: 'p', style: { 'color': 'rgba(255,255,255,0.9)', 'font-size': '18px', 'line-height': '1.6', 'font-family': 'Inter, sans-serif' }, content: 'Add your content here. This section supports background images and videos with customizable overlay.' } ] } ] }, attributes: { class: 'fa fa-image' } }); // Section with Video Background blockManager.add('section-video-bg', { label: 'Section (Video BG)', category: 'Layout', content: `
Click this section, then add Video URL in Settings →

Video Background

This section has a looping video background with an overlay.

`, attributes: { class: 'fa fa-play-circle' } }); // Footer blockManager.add('footer', { label: 'Footer', category: 'Layout', content: ``, attributes: { class: 'fa fa-window-minimize' } }); // Column Layouts blockManager.add('column-1', { label: '1 Column', category: 'Layout', content: `
`, attributes: { class: 'gjs-fonts gjs-f-b1' } }); blockManager.add('column-2', { label: '2 Columns', category: 'Layout', content: `
`, attributes: { class: 'gjs-fonts gjs-f-b2' } }); blockManager.add('column-3', { label: '3 Columns', category: 'Layout', content: `
`, attributes: { class: 'gjs-fonts gjs-f-b3' } }); blockManager.add('column-4', { label: '4 Columns', category: 'Layout', content: `
`, attributes: { class: 'gjs-fonts gjs-f-b4' } }); blockManager.add('column-3-7', { label: '2 Columns 3/7', category: 'Layout', content: `
`, attributes: { class: 'gjs-fonts gjs-f-b37' } }); // Image Block blockManager.add('image-block', { label: 'Image', category: 'Media', content: 'Image', attributes: { class: 'fa fa-image' } }); // Unified Video Block (YouTube, Vimeo, or direct file) blockManager.add('video-block', { label: 'Video', category: 'Media', content: `
Select container & add Video URL in Settings
Supports YouTube, Vimeo, or direct video files
`, attributes: { class: 'fa fa-play-circle' } }); // Hero with Image Background blockManager.add('hero-image', { label: 'Hero (Image)', category: 'Sections', content: `

Your Headline Here

Add your compelling subheadline or description text here to engage your visitors.

Get Started
`, attributes: { class: 'fa fa-image' } }); // Hero with Video Background blockManager.add('hero-video', { label: 'Hero (Video)', category: 'Sections', content: `
Click this section, then add Video URL in Settings →

Video Background Hero

Create stunning video backgrounds for your hero sections.

Learn More
`, attributes: { class: 'fa fa-play-circle' } }); // Simple Hero Section blockManager.add('hero-simple', { label: 'Hero (Simple)', category: 'Sections', content: `

Welcome

Your introductory text goes here.

Call to Action
`, attributes: { class: 'fa fa-star' } }); // Features Section blockManager.add('features-section', { label: 'Features Grid', category: 'Sections', content: `

Features

Feature One

Description of your first amazing feature goes here.

Feature Two

Description of your second amazing feature goes here.

Feature Three

Description of your third amazing feature goes here.

`, attributes: { class: 'fa fa-th-large' } }); // Testimonials Section blockManager.add('testimonials-section', { label: 'Testimonials', category: 'Sections', content: `

What People Say

Hear from our satisfied customers

"This product has completely transformed how we work. The results speak for themselves."

JD
John Doe
CEO, Company Inc

"Exceptional quality and outstanding customer service. I couldn't be happier with my experience."

JS
Jane Smith
Designer, Studio Co

"A game-changer for our business. The ROI has been incredible from day one."

MB
Mike Brown
Founder, StartupXYZ
`, attributes: { class: 'fa fa-comments' } }); // Pricing Section blockManager.add('pricing-section', { label: 'Pricing Table', category: 'Sections', content: `

Simple Pricing

Choose the plan that's right for you

Starter

Perfect for individuals

$9 /month
  • ✓ 5 Projects
  • ✓ Basic Support
  • ✓ 1GB Storage
  • ✓ Community Access
Get Started
MOST POPULAR

Professional

Best for growing teams

$29 /month
  • ✓ Unlimited Projects
  • ✓ Priority Support
  • ✓ 10GB Storage
  • ✓ Advanced Analytics
Get Started

Enterprise

For large organizations

$99 /month
  • ✓ Everything in Pro
  • ✓ Dedicated Support
  • ✓ Unlimited Storage
  • ✓ Custom Integrations
Contact Sales
`, attributes: { class: 'fa fa-credit-card' } }); // Contact Section blockManager.add('contact-section', { label: 'Contact Section', category: 'Sections', content: `

Get in Touch

Have questions? We'd love to hear from you. Send us a message and we'll respond as soon as possible.

📍
Address
123 Business Street, City, ST 12345
📧
Email
hello@example.com
📞
Phone
(555) 123-4567
`, attributes: { class: 'fa fa-envelope' } }); // Call to Action Section blockManager.add('cta-section', { label: 'Call to Action', category: 'Sections', content: `

Ready to Get Started?

Join thousands of satisfied customers and take your business to the next level.

Start Free Trial Learn More
`, attributes: { class: 'fa fa-bullhorn' } }); // Text Block blockManager.add('text-block', { label: 'Text', category: 'Basic', content: '

Insert your text here. You can edit this content directly.

', attributes: { class: 'gjs-fonts gjs-f-text' } }); // Heading Block blockManager.add('heading', { label: 'Heading', category: 'Basic', content: '

Heading

', attributes: { class: 'fa fa-header' } }); // Button Block blockManager.add('button-block', { label: 'Button', category: 'Basic', content: 'Button', attributes: { class: 'fa fa-link' } }); // Divider Block - Resizable with height control blockManager.add('divider', { label: 'Divider', category: 'Basic', content: { tagName: 'hr', style: { 'border': 'none', 'border-top': '2px solid #e5e7eb', 'margin': '20px 0', 'width': '100%', 'height': '0' }, resizable: { tl: 0, tc: 0, tr: 0, cl: 1, cr: 1, bl: 0, bc: 0, br: 0, keyWidth: 'width' } }, attributes: { class: 'fa fa-minus' } }); // Spacer Block blockManager.add('spacer', { label: 'Spacer', category: 'Basic', content: '
', attributes: { class: 'fa fa-arrows-alt-v' } }); // Anchor Point Block blockManager.add('anchor-point', { label: 'Anchor Point', category: 'Basic', content: `
`, attributes: { class: 'fa fa-anchor' } }); // PDF / File Embed Block blockManager.add('file-embed', { label: 'File / PDF', category: 'Media', content: `
📄
Select this element, then enter File URL in Settings
Supports PDF, DOC, and other files
`, attributes: { class: 'fa fa-file-pdf' } }); // Text Box Block (for overlaying on backgrounds) blockManager.add('text-box', { label: 'Text Box', category: 'Basic', content: { tagName: 'div', attributes: { class: 'text-box' }, style: { 'padding': '24px', 'background': 'rgba(255,255,255,0.95)', 'border-radius': '8px', 'max-width': '600px', 'box-shadow': '0 4px 6px rgba(0,0,0,0.1)' }, components: [ { tagName: 'h3', style: { 'color': '#1f2937', 'font-size': '24px', 'font-weight': '600', 'margin-bottom': '12px', 'font-family': 'Inter, sans-serif' }, content: 'Text Box Title' }, { tagName: 'p', style: { 'color': '#4b5563', 'font-size': '16px', 'line-height': '1.6', 'font-family': 'Inter, sans-serif' }, content: 'Add your content here. This text box can be placed over images or video backgrounds.' } ] }, attributes: { class: 'fa fa-file-text' } }); // ========================================== // Enhanced Block Library // ========================================== // Image Gallery blockManager.add('image-gallery', { label: 'Image Gallery', category: 'Sections', content: `

Gallery

Gallery image 1
Gallery image 2
Gallery image 3
Gallery image 4
Gallery image 5
Gallery image 6
`, attributes: { class: 'fa fa-th' } }); // FAQ Accordion blockManager.add('faq-section', { label: 'FAQ Accordion', category: 'Sections', content: `

Frequently Asked Questions

What is your return policy?

We offer a 30-day money-back guarantee on all purchases. If you're not satisfied, contact our support team for a full refund.

How long does shipping take?

Standard shipping takes 5-7 business days. Express shipping is available for 2-3 business day delivery.

Do you offer customer support?

Yes! Our support team is available 24/7 via email and live chat. Phone support is available Mon-Fri, 9am-5pm EST.

Can I cancel my subscription?

You can cancel your subscription at any time from your account settings. No cancellation fees apply.

`, attributes: { class: 'fa fa-question-circle' } }); // Stats/Counter Section blockManager.add('stats-section', { label: 'Stats Counter', category: 'Sections', content: `
10K+
Happy Customers
500+
Projects Completed
99%
Satisfaction Rate
24/7
Support Available
`, attributes: { class: 'fa fa-bar-chart' } }); // Team Grid blockManager.add('team-section', { label: 'Team Grid', category: 'Sections', content: `

Meet Our Team

The talented people behind our success

AJ

Alex Johnson

CEO & Founder

Visionary leader with 15+ years of experience.

SK

Sarah Kim

Lead Designer

Award-winning designer creating beautiful experiences.

MP

Mike Patel

CTO

Full-stack engineer building scalable systems.

`, attributes: { class: 'fa fa-users' } }); // Newsletter Signup blockManager.add('newsletter-section', { label: 'Newsletter', category: 'Sections', content: `

Stay in the Loop

Subscribe to our newsletter for the latest updates and exclusive offers.

No spam, unsubscribe anytime.

`, attributes: { class: 'fa fa-newspaper' } }); // Logo Cloud / Trusted By blockManager.add('logo-cloud', { label: 'Logo Cloud', category: 'Sections', content: `

Trusted by leading companies

Company 1
Company 2
Company 3
Company 4
Company 5
`, attributes: { class: 'fa fa-building' } }); // ========================================== // Custom Component Types for Better Editing // ========================================== // Helper to convert YouTube/Vimeo URLs to embed format function convertToEmbedUrl(url, isBackground = false) { if (!url) return null; // YouTube: youtube.com/watch?v=ID or youtu.be/ID const youtubeMatch = url.match(/(?:youtube\.com\/watch\?v=|youtu\.be\/|youtube\.com\/embed\/)([a-zA-Z0-9_-]{11})/); if (youtubeMatch) { const videoId = youtubeMatch[1]; // Use youtube-nocookie.com to avoid Error 153 (referrer requirements) // The referrerpolicy attribute in the iframe handles the referrer header if (isBackground) { // Background video: autoplay, muted, looped, no controls return { type: 'youtube', url: `https://www.youtube-nocookie.com/embed/${videoId}?autoplay=1&mute=1&loop=1&playlist=${videoId}&controls=0&modestbranding=1&rel=0&showinfo=0` }; } else { // Regular video: user controls, no autoplay return { type: 'youtube', url: `https://www.youtube-nocookie.com/embed/${videoId}?rel=0` }; } } // Vimeo: vimeo.com/ID or player.vimeo.com/video/ID const vimeoMatch = url.match(/(?:vimeo\.com\/|player\.vimeo\.com\/video\/)(\d+)/); if (vimeoMatch) { if (isBackground) { // Background video parameters return { type: 'vimeo', url: `https://player.vimeo.com/video/${vimeoMatch[1]}?muted=1&loop=1&background=1&autoplay=1` }; } else { // Regular video with controls return { type: 'vimeo', url: `https://player.vimeo.com/video/${vimeoMatch[1]}` }; } } // Direct video file if (url.match(/\.(mp4|webm|ogg|mov)(\?.*)?$/i)) { return { type: 'file', url: url }; } // Assume it's an embed URL if nothing else matches return { type: 'embed', url: url }; } // Helper to apply video URL to a video wrapper component function applyVideoUrl(component, url) { if (!url) return; // Detect if this is a background video by checking for bg-video classes const iframe = component.components().find(c => c.getClasses().includes('video-frame') || c.getClasses().includes('bg-video-frame')); const isBackground = iframe && iframe.getClasses().includes('bg-video-frame'); const result = convertToEmbedUrl(url, isBackground); if (!result) return; console.log('Applying video URL:', url); console.log('Converted to:', result.url); console.log('Video type:', result.type); console.log('Is background:', isBackground); // Find child elements (iframe already found above) const video = component.components().find(c => c.getClasses().includes('video-player') || c.getClasses().includes('bg-video-player')); const placeholder = component.components().find(c => c.getClasses().includes('video-placeholder') || c.getClasses().includes('bg-video-placeholder')); if (result.type === 'file') { // Use HTML5 video if (video) { video.addAttributes({ src: result.url }); video.addStyle({ display: 'block' }); const videoEl = video.getEl(); if (videoEl) { videoEl.src = result.url; videoEl.style.display = 'block'; } } if (iframe) { iframe.addStyle({ display: 'none' }); const iframeEl = iframe.getEl(); if (iframeEl) iframeEl.style.display = 'none'; } } else { // Use iframe for YouTube/Vimeo/embeds if (iframe) { iframe.addAttributes({ src: result.url }); iframe.addStyle({ display: 'block' }); const iframeEl = iframe.getEl(); if (iframeEl) { iframeEl.src = result.url; iframeEl.style.display = 'block'; } } if (video) { video.addStyle({ display: 'none' }); const videoEl = video.getEl(); if (videoEl) videoEl.style.display = 'none'; } } // Hide placeholder in the model (so preview/export shows the video, not the placeholder) if (placeholder) { placeholder.addStyle({ display: 'none' }); // In the editor canvas, GrapesJS renders