Clean up demo site: remove manual IDs and frontend-only approach
- Remove all manual data-content-id attributes from HTML files - Archive old insertr JS/CSS assets to demo-site/archive/ - Remove hardcoded script includes and CSS links - Remove old authentication controls and mock API - Enable pure zero-config approach with class='insertr' only - Parser now generates all 40 content IDs automatically
This commit is contained in:
409
demo-site/archive/insertr-old/insertr.js
Normal file
409
demo-site/archive/insertr-old/insertr.js
Normal file
@@ -0,0 +1,409 @@
|
||||
/**
|
||||
* Insertr - Element-Level Edit-in-place CMS Library
|
||||
* Modular architecture with configuration system
|
||||
*/
|
||||
|
||||
class Insertr {
|
||||
constructor(options = {}) {
|
||||
this.options = {
|
||||
apiEndpoint: options.apiEndpoint || '/api/content',
|
||||
authEndpoint: options.authEndpoint || '/api/auth',
|
||||
autoInit: options.autoInit !== false,
|
||||
...options
|
||||
};
|
||||
|
||||
// Core state
|
||||
this.state = {
|
||||
isAuthenticated: false,
|
||||
editMode: false,
|
||||
currentUser: null,
|
||||
activeEditor: null
|
||||
};
|
||||
|
||||
this.editableElements = new Map();
|
||||
this.statusIndicator = null;
|
||||
|
||||
// Initialize modules
|
||||
this.config = new InsertrConfig(options.config);
|
||||
this.validation = new InsertrValidation(this.config);
|
||||
this.formRenderer = new InsertrFormRenderer(this.validation);
|
||||
this.contentManager = new InsertrContentManager(options);
|
||||
this.markdownProcessor = new InsertrMarkdownProcessor();
|
||||
|
||||
if (this.options.autoInit) {
|
||||
this.init();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize the CMS system
|
||||
*/
|
||||
async init() {
|
||||
console.log('🚀 Insertr initializing with modular architecture...');
|
||||
|
||||
// Scan for editable elements
|
||||
this.scanForEditableElements();
|
||||
|
||||
// Setup authentication controls
|
||||
this.setupAuthenticationControls();
|
||||
|
||||
// Create status indicator
|
||||
this.createStatusIndicator();
|
||||
|
||||
// Apply initial state
|
||||
this.updateBodyClasses();
|
||||
|
||||
console.log(`📝 Found ${this.editableElements.size} editable elements`);
|
||||
}
|
||||
|
||||
/**
|
||||
* Scan for editable elements and set them up
|
||||
*/
|
||||
scanForEditableElements() {
|
||||
const elements = document.querySelectorAll('.insertr');
|
||||
|
||||
elements.forEach(element => {
|
||||
const contentId = element.getAttribute('data-content-id');
|
||||
if (!contentId) {
|
||||
console.warn('Insertr element missing data-content-id:', element);
|
||||
return;
|
||||
}
|
||||
|
||||
this.editableElements.set(contentId, element);
|
||||
this.setupEditableElement(element, contentId);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Setup individual editable element
|
||||
* @param {HTMLElement} element - Element to setup
|
||||
* @param {string} contentId - Content identifier
|
||||
*/
|
||||
setupEditableElement(element, contentId) {
|
||||
// Generate field configuration
|
||||
const fieldConfig = this.config.generateFieldConfig(element);
|
||||
element._insertrConfig = fieldConfig;
|
||||
|
||||
// Add edit button
|
||||
this.addEditButton(element, contentId);
|
||||
|
||||
// Load saved content if available
|
||||
const savedContent = this.contentManager.getContent(contentId);
|
||||
if (savedContent) {
|
||||
this.contentManager.applyContentToElement(element, savedContent);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add edit button to element
|
||||
* @param {HTMLElement} element - Element to add button to
|
||||
* @param {string} contentId - Content identifier
|
||||
*/
|
||||
addEditButton(element, contentId) {
|
||||
// Create edit button
|
||||
const editBtn = document.createElement('button');
|
||||
editBtn.className = 'insertr-edit-btn';
|
||||
editBtn.innerHTML = '✏️';
|
||||
editBtn.title = `Edit ${element._insertrConfig.label}`;
|
||||
editBtn.addEventListener('click', (e) => {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
this.startEditing(contentId);
|
||||
});
|
||||
|
||||
// Position relative for button placement
|
||||
if (getComputedStyle(element).position === 'static') {
|
||||
element.style.position = 'relative';
|
||||
}
|
||||
|
||||
element.appendChild(editBtn);
|
||||
}
|
||||
|
||||
/**
|
||||
* Start editing an element
|
||||
* @param {string} contentId - Content identifier
|
||||
*/
|
||||
startEditing(contentId) {
|
||||
const element = this.editableElements.get(contentId);
|
||||
if (!element || !this.state.editMode) return;
|
||||
|
||||
// Close any active editor
|
||||
if (this.state.activeEditor && this.state.activeEditor !== contentId) {
|
||||
this.cancelEditing(this.state.activeEditor);
|
||||
}
|
||||
|
||||
const config = element._insertrConfig;
|
||||
const currentContent = this.contentManager.extractContentFromElement(element);
|
||||
|
||||
// Create and show edit form
|
||||
const form = this.formRenderer.createEditForm(contentId, config, currentContent);
|
||||
const overlay = this.formRenderer.showEditForm(element, form);
|
||||
|
||||
// Setup form event handlers
|
||||
this.setupFormHandlers(overlay, contentId);
|
||||
|
||||
this.state.activeEditor = contentId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Setup form event handlers
|
||||
* @param {HTMLElement} overlay - Form overlay
|
||||
* @param {string} contentId - Content identifier
|
||||
*/
|
||||
setupFormHandlers(overlay, contentId) {
|
||||
const saveBtn = overlay.querySelector('.insertr-btn-save');
|
||||
const cancelBtn = overlay.querySelector('.insertr-btn-cancel');
|
||||
|
||||
if (saveBtn) {
|
||||
saveBtn.addEventListener('click', () => {
|
||||
this.saveElementContent(contentId, overlay);
|
||||
});
|
||||
}
|
||||
|
||||
if (cancelBtn) {
|
||||
cancelBtn.addEventListener('click', () => {
|
||||
this.cancelEditing(contentId);
|
||||
});
|
||||
}
|
||||
|
||||
// Handle Enter to save, Escape to cancel
|
||||
overlay.addEventListener('keydown', (e) => {
|
||||
if (e.key === 'Enter' && (e.ctrlKey || e.metaKey)) {
|
||||
e.preventDefault();
|
||||
this.saveElementContent(contentId, overlay);
|
||||
} else if (e.key === 'Escape') {
|
||||
e.preventDefault();
|
||||
this.cancelEditing(contentId);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Save element content
|
||||
* @param {string} contentId - Content identifier
|
||||
* @param {HTMLElement} overlay - Form overlay
|
||||
*/
|
||||
async saveElementContent(contentId, overlay) {
|
||||
const element = this.editableElements.get(contentId);
|
||||
const form = overlay.querySelector('.insertr-edit-form');
|
||||
const config = element._insertrConfig;
|
||||
|
||||
if (!element || !form) return;
|
||||
|
||||
// Extract form data
|
||||
const formData = this.formRenderer.extractFormData(form, config);
|
||||
|
||||
// Validate the data
|
||||
const validation = this.validateFormData(formData, config);
|
||||
if (!validation.valid) {
|
||||
alert(validation.message);
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
// Show saving state
|
||||
element.classList.add('insertr-saving');
|
||||
|
||||
// Save to server (mock for now)
|
||||
await this.contentManager.saveToServer(contentId, formData);
|
||||
|
||||
// Apply content to element
|
||||
this.contentManager.applyContentToElement(element, formData);
|
||||
|
||||
// Close form
|
||||
this.formRenderer.hideEditForm(overlay);
|
||||
this.state.activeEditor = null;
|
||||
|
||||
// Show success feedback
|
||||
element.classList.add('insertr-save-success');
|
||||
setTimeout(() => {
|
||||
element.classList.remove('insertr-save-success');
|
||||
}, 2000);
|
||||
|
||||
} catch (error) {
|
||||
console.error('Failed to save content:', error);
|
||||
alert('Failed to save content. Please try again.');
|
||||
} finally {
|
||||
element.classList.remove('insertr-saving');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate form data before saving
|
||||
* @param {string|Object} data - Form data to validate
|
||||
* @param {Object} config - Field configuration
|
||||
* @returns {Object} Validation result
|
||||
*/
|
||||
validateFormData(data, config) {
|
||||
if (config.type === 'link' && config.includeUrl) {
|
||||
// Validate link data
|
||||
const textValidation = this.validation.validateInput(data.text, 'text');
|
||||
if (!textValidation.valid) return textValidation;
|
||||
|
||||
const urlValidation = this.validation.validateInput(data.url, 'link');
|
||||
if (!urlValidation.valid) return urlValidation;
|
||||
|
||||
return { valid: true };
|
||||
} else {
|
||||
// Validate single content
|
||||
return this.validation.validateInput(data, config.type);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Cancel editing
|
||||
* @param {string} contentId - Content identifier
|
||||
*/
|
||||
cancelEditing(contentId) {
|
||||
const overlay = document.querySelector('.insertr-form-overlay');
|
||||
if (overlay) {
|
||||
this.formRenderer.hideEditForm(overlay);
|
||||
}
|
||||
|
||||
if (this.state.activeEditor === contentId) {
|
||||
this.state.activeEditor = null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Update markdown preview (called by form renderer)
|
||||
* @param {HTMLElement} previewElement - Preview container
|
||||
* @param {string} markdown - Markdown content
|
||||
*/
|
||||
updateMarkdownPreview(previewElement, markdown) {
|
||||
if (this.markdownProcessor.isReady()) {
|
||||
const html = this.markdownProcessor.createPreview(markdown);
|
||||
previewElement.innerHTML = html;
|
||||
} else {
|
||||
previewElement.innerHTML = '<p><em>Markdown processor not available</em></p>';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Render markdown content (called by content manager)
|
||||
* @param {HTMLElement} element - Element to update
|
||||
* @param {string} markdownText - Markdown content
|
||||
*/
|
||||
renderMarkdown(element, markdownText) {
|
||||
if (this.markdownProcessor.isReady()) {
|
||||
this.markdownProcessor.applyToElement(element, markdownText);
|
||||
} else {
|
||||
console.warn('Markdown processor not available');
|
||||
element.textContent = markdownText;
|
||||
}
|
||||
}
|
||||
|
||||
// Authentication and UI methods (simplified)
|
||||
|
||||
/**
|
||||
* Setup authentication controls
|
||||
*/
|
||||
setupAuthenticationControls() {
|
||||
const authToggle = document.getElementById('auth-toggle');
|
||||
const editToggle = document.getElementById('edit-mode-toggle');
|
||||
|
||||
if (authToggle) {
|
||||
authToggle.addEventListener('click', () => this.toggleAuthentication());
|
||||
}
|
||||
|
||||
if (editToggle) {
|
||||
editToggle.addEventListener('click', () => this.toggleEditMode());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Toggle authentication state
|
||||
*/
|
||||
toggleAuthentication() {
|
||||
this.state.isAuthenticated = !this.state.isAuthenticated;
|
||||
this.state.currentUser = this.state.isAuthenticated ? { name: 'Demo User' } : null;
|
||||
|
||||
if (!this.state.isAuthenticated) {
|
||||
this.state.editMode = false;
|
||||
}
|
||||
|
||||
this.updateBodyClasses();
|
||||
this.updateStatusIndicator();
|
||||
|
||||
const authBtn = document.getElementById('auth-toggle');
|
||||
if (authBtn) {
|
||||
authBtn.textContent = this.state.isAuthenticated ? 'Logout' : 'Login as Client';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Toggle edit mode
|
||||
*/
|
||||
toggleEditMode() {
|
||||
if (!this.state.isAuthenticated) return;
|
||||
|
||||
this.state.editMode = !this.state.editMode;
|
||||
|
||||
if (!this.state.editMode && this.state.activeEditor) {
|
||||
this.cancelEditing(this.state.activeEditor);
|
||||
}
|
||||
|
||||
this.updateBodyClasses();
|
||||
this.updateStatusIndicator();
|
||||
|
||||
const editBtn = document.getElementById('edit-mode-toggle');
|
||||
if (editBtn) {
|
||||
editBtn.textContent = `Edit Mode: ${this.state.editMode ? 'On' : 'Off'}`;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Update body CSS classes based on state
|
||||
*/
|
||||
updateBodyClasses() {
|
||||
document.body.classList.toggle('insertr-authenticated', this.state.isAuthenticated);
|
||||
document.body.classList.toggle('insertr-edit-mode', this.state.editMode);
|
||||
|
||||
const editToggle = document.getElementById('edit-mode-toggle');
|
||||
if (editToggle) {
|
||||
editToggle.style.display = this.state.isAuthenticated ? 'inline-block' : 'none';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create status indicator
|
||||
*/
|
||||
createStatusIndicator() {
|
||||
// Implementation similar to original, simplified for brevity
|
||||
this.updateStatusIndicator();
|
||||
}
|
||||
|
||||
/**
|
||||
* Update status indicator
|
||||
*/
|
||||
updateStatusIndicator() {
|
||||
// Implementation similar to original, simplified for brevity
|
||||
console.log(`Status: Auth=${this.state.isAuthenticated}, Edit=${this.state.editMode}`);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get configuration instance (for external customization)
|
||||
* @returns {InsertrConfig} Configuration instance
|
||||
*/
|
||||
getConfig() {
|
||||
return this.config;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get content manager instance
|
||||
* @returns {InsertrContentManager} Content manager instance
|
||||
*/
|
||||
getContentManager() {
|
||||
return this.contentManager;
|
||||
}
|
||||
}
|
||||
|
||||
// Export for module usage
|
||||
if (typeof module !== 'undefined' && module.exports) {
|
||||
module.exports = Insertr;
|
||||
}
|
||||
|
||||
// Auto-initialize when DOM is ready
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
window.insertr = new Insertr();
|
||||
});
|
||||
Reference in New Issue
Block a user