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:
2025-09-03 12:18:43 +02:00
parent c2591e4fdd
commit 9bc2751ff2
10 changed files with 44 additions and 67 deletions

View File

@@ -0,0 +1,194 @@
/**
* Insertr Validation Module
* Client-side validation for user experience (not security)
*/
class InsertrValidation {
constructor(config, domPurify = null) {
this.config = config;
this.DOMPurify = domPurify;
this.limits = config.getValidationLimits();
}
/**
* Validate input based on field type
* @param {string} input - Input to validate
* @param {string} fieldType - Type of field
* @returns {Object} Validation result
*/
validateInput(input, fieldType) {
if (!input || typeof input !== 'string') {
return { valid: false, message: 'Content cannot be empty' };
}
// Basic length validation
if (input.length > this.limits.maxContentLength) {
return {
valid: false,
message: `Content is too long (max ${this.limits.maxContentLength.toLocaleString()} characters)`
};
}
// Field-specific validation
switch (fieldType) {
case 'text':
return this.validateTextInput(input);
case 'textarea':
return this.validateTextInput(input);
case 'link':
return this.validateLinkInput(input);
case 'markdown':
return this.validateMarkdownInput(input);
default:
return { valid: true };
}
}
/**
* Validate plain text input
* @param {string} input - Text to validate
* @returns {Object} Validation result
*/
validateTextInput(input) {
// Check for obvious HTML that users might accidentally include
if (input.includes('<script>') || input.includes('</script>')) {
return {
valid: false,
message: 'Script tags are not allowed for security reasons'
};
}
if (input.includes('<') && input.includes('>')) {
return {
valid: false,
message: 'HTML tags are not allowed in text fields. Use markdown collections for formatted content.'
};
}
return { valid: true };
}
/**
* Validate link/URL input
* @param {string} input - URL to validate
* @returns {Object} Validation result
*/
validateLinkInput(input) {
// Basic URL validation for user feedback
const urlPattern = /^(https?:\/\/)?([\da-z\.-]+)\.([a-z\.]{2,6})([\/\w \.-]*)*\/?$/;
if (input.startsWith('http') && !urlPattern.test(input)) {
return {
valid: false,
message: 'Please enter a valid URL (e.g., https://example.com)'
};
}
return { valid: true };
}
/**
* Validate markdown input
* @param {string} input - Markdown to validate
* @returns {Object} Validation result
*/
validateMarkdownInput(input) {
// Check for potentially problematic content
if (input.includes('<script>') || input.includes('javascript:')) {
return {
valid: false,
message: 'Script content is not allowed for security reasons'
};
}
// Warn about excessive HTML (user might be pasting from Word/etc)
const htmlTagCount = (input.match(/<[^>]+>/g) || []).length;
if (htmlTagCount > this.limits.maxHtmlTags) {
return {
valid: false,
message: 'Too much HTML detected. Please use markdown formatting instead of HTML tags.'
};
}
return { valid: true };
}
/**
* Show validation message to user
* @param {HTMLElement} element - Element to show message near
* @param {string} message - Message to show
* @param {boolean} isError - Whether this is an error message
*/
showValidationMessage(element, message, isError = true) {
// Remove existing message
const existingMsg = element.parentNode.querySelector('.insertr-validation-message');
if (existingMsg) {
existingMsg.remove();
}
if (!message) return;
// Create new message
const msgElement = document.createElement('div');
msgElement.className = `insertr-validation-message ${isError ? 'error' : 'success'}`;
msgElement.textContent = message;
// Insert after the element
element.parentNode.insertBefore(msgElement, element.nextSibling);
// Auto-remove after delay
if (!isError) {
setTimeout(() => {
if (msgElement.parentNode) {
msgElement.remove();
}
}, 3000);
}
}
/**
* Sanitize content for display (basic client-side sanitization)
* @param {string} content - Content to sanitize
* @param {string} type - Content type
* @returns {string} Sanitized content
*/
sanitizeForDisplay(content, type) {
if (!content) return '';
switch (type) {
case 'text':
return this.escapeHtml(content);
case 'url':
// Basic URL sanitization
if (content.startsWith('javascript:') || content.startsWith('data:')) {
return '';
}
return content;
case 'markdown':
// For markdown, we'll let marked.js handle it with our safe config
return content;
default:
return this.escapeHtml(content);
}
}
/**
* Escape HTML characters
* @param {string} text - Text to escape
* @returns {string} Escaped text
*/
escapeHtml(text) {
const div = document.createElement('div');
div.textContent = text;
return div.innerHTML;
}
}
// Export for module usage
if (typeof module !== 'undefined' && module.exports) {
module.exports = InsertrValidation;
}
// Global export for browser usage
if (typeof window !== 'undefined') {
window.InsertrValidation = InsertrValidation;
}