1import { Controller } from "@hotwired/stimulus" 2 3// Connects to data-controller="content-loader" 4export default class extends Controller { 5 static values = { 6 url: String 7 } 8 9 connect() { 10 this.shadowRoot = this.element.attachShadow({ mode: 'open' }); 11 this.loadContent(); 12 } 13 14 async loadContent() { 15 if (!this.hasUrlValue) { 16 console.error("Content loader controller requires a 'url' value."); 17 this.shadowRoot.innerHTML = `<p>Error: URL not provided.</p>`; 18 return; 19 } 20 21 try { 22 const response = await fetch(this.urlValue); 23 if (!response.ok) { 24 throw new Error(`Failed to fetch content: ${response.statusText}`); 25 } 26 const html = await response.text(); 27 this.renderContent(html); 28 } catch (error) { 29 console.error("Error loading content:", error); 30 this.shadowRoot.innerHTML = `<p>Error loading content.</p>`; 31 } 32 } 33 34 renderContent(html) { 35 const parser = new DOMParser(); 36 const doc = parser.parseFromString(html, 'text/html'); 37 38 // Clear previous content 39 this.shadowRoot.innerHTML = ''; 40 41 const style = document.createElement('style'); 42 style.textContent = ` 43:host { 44 --bs-black: #000000; 45 --bs-white: #ffffff; 46 --bs-gray-100: #f8f9fa; 47 --bs-gray-200: #e9ecef; 48 --bs-gray-300: #dee2e6; 49 --bs-gray-400: #ced4da; 50 --bs-gray-500: #adb5bd; 51 --bs-gray-600: #6c757d; 52 --bs-gray-700: #495057; 53 --bs-gray-800: #343a40; 54 --bs-gray-900: #212529; 55 --bs-primary: #373dff; 56 --bs-secondary: #3b4252; 57 --bs-highlight: #b48ead; 58 --bs-success: #a3be8c; 59 --bs-info: #88c0d0; 60 --bs-warning: #ebcb8b; 61 --bs-danger: #bf616a; 62 --bs-light: #e5e9f0; 63 --bs-dark: #4c566a; 64 --bs-primary-rgb: 55, 61, 255; 65 --bs-secondary-rgb: 59, 66, 82; 66 --bs-highlight-rgb: 180, 142, 173; 67 --bs-success-rgb: 163, 190, 140; 68 --bs-info-rgb: 136, 192, 208; 69 --bs-warning-rgb: 235, 203, 139; 70 --bs-danger-rgb: 191, 97, 106; 71 --bs-light-rgb: 229, 233, 240; 72 --bs-dark-rgb: 76, 86, 106; 73 --bs-primary-text-emphasis: rgb(22, 24.4, 102); 74 --bs-secondary-text-emphasis: rgb(23.6, 26.4, 32.8); 75 --bs-success-text-emphasis: rgb(65.2, 76, 56); 76 --bs-info-text-emphasis: rgb(54.4, 76.8, 83.2); 77 --bs-warning-text-emphasis: rgb(94, 81.2, 55.6); 78 --bs-danger-text-emphasis: rgb(76.4, 38.8, 42.4); 79 --bs-light-text-emphasis: #495057; 80 --bs-dark-text-emphasis: #495057; 81 --bs-primary-bg-subtle: rgb(215, 216.2, 255); 82 --bs-secondary-bg-subtle: rgb(215.8, 217.2, 220.4); 83 --bs-success-bg-subtle: rgb(236.6, 242, 232); 84 --bs-info-bg-subtle: rgb(231.2, 242.4, 245.6); 85 --bs-warning-bg-subtle: rgb(251, 244.6, 231.8); 86 --bs-danger-bg-subtle: rgb(242.2, 223.4, 225.2); 87 --bs-light-bg-subtle: rgb(251.5, 252, 252.5); 88 --bs-dark-bg-subtle: #ced4da; 89 --bs-primary-border-subtle: rgb(175, 177.4, 255); 90 --bs-secondary-border-subtle: rgb(176.6, 179.4, 185.8); 91 --bs-success-border-subtle: rgb(218.2, 229, 209); 92 --bs-info-border-subtle: rgb(207.4, 229.8, 236.2); 93 --bs-warning-border-subtle: rgb(247, 234.2, 208.6); 94 --bs-danger-border-subtle: rgb(229.4, 191.8, 195.4); 95 --bs-light-border-subtle: #e9ecef; 96 --bs-dark-border-subtle: #adb5bd; 97 --bs-white-rgb: 255, 255, 255; 98 --bs-black-rgb: 0, 0, 0; 99 --bs-font-sans-serif: system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", "Noto Sans", "Liberation Sans", Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"; 100 --bs-font-monospace: SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace; 101 --bs-gradient: linear-gradient(180deg, rgba(255, 255, 255, 0.15), rgba(255, 255, 255, 0)); 102 --bs-body-font-family: var(--bs-font-sans-serif); 103 --bs-body-font-size: 0.875rem; 104 --bs-body-font-weight: 400; 105 --bs-body-line-height: 1.5; 106 --bs-body-color: #212529; 107 --bs-body-color-rgb: 33, 37, 41; 108 --bs-body-bg: #fff; 109 --bs-body-bg-rgb: 255, 255, 255; 110 --bs-emphasis-color: #000; 111 --bs-emphasis-color-rgb: 0, 0, 0; 112 --bs-secondary-color: rgba(33, 37, 41, 0.75); 113 --bs-secondary-color-rgb: 33, 37, 41; 114 --bs-secondary-bg: #e9ecef; 115 --bs-secondary-bg-rgb: 233, 236, 239; 116 --bs-tertiary-color: rgba(33, 37, 41, 0.5); 117 --bs-tertiary-color-rgb: 33, 37, 41; 118 --bs-tertiary-bg: #f8f9fa; 119 --bs-tertiary-bg-rgb: 248, 249, 250; 120 --bs-heading-color: inherit; 121 --bs-link-color: #373dff; 122 --bs-link-color-rgb: 55, 61, 255; 123 --bs-link-decoration: underline; 124 --bs-link-hover-color: rgb(44, 48.8, 204); 125 --bs-link-hover-color-rgb: 44, 49, 204; 126 --bs-code-color: #d63384; 127 --bs-highlight-color: #212529; 128 --bs-highlight-bg: rgb(255, 242.6, 205.4); 129 --bs-border-width: 1px; 130 --bs-border-style: solid; 131 --bs-border-color: #dee2e6; 132 --bs-border-color-translucent: rgba(0, 0, 0, 0.175); 133 --bs-border-radius: 0.375rem; 134 --bs-border-radius-sm: 0.25rem; 135 --bs-border-radius-lg: 0.5rem; 136 --bs-border-radius-xl: 1rem; 137 --bs-border-radius-xxl: 2rem; 138 --bs-border-radius-2xl: var(--bs-border-radius-xxl); 139 --bs-border-radius-pill: 50rem;
140 --bs-box-shadow: 0 0.5rem 1rem rgba(0, 0, 0, 0.15); 141 --bs-box-shadow-sm: 0 0.125rem 0.25rem rgba(0, 0, 0, 0.075); 142 --bs-box-shadow-lg: 0 1rem 3rem rgba(0, 0, 0, 0.175); 143 --bs-box-shadow-inset: inset 0 1px 2px rgba(0, 0, 0, 0.075); 144 --bs-focus-ring-width: 0.25rem; 145 --bs-focus-ring-opacity: 0.25; 146 --bs-focus-ring-color: rgba(55, 61, 255, 0.25); 147 --bs-form-valid-color: #a3be8c; 148 --bs-form-valid-border-color: #a3be8c; 149 --bs-form-invalid-color: #bf616a; 150 --bs-form-invalid-border-color: #bf616a; 151 --theme-primary: #5e81ac; 152 --theme-primary-text: #ffffff; 153 --theme-secondary: #3b4252; 154 --theme-secondary-text: #ffffff; 155 } 156 ` 157 this.shadowRoot.appendChild(style); 158 159 // Import styles from both <head> and <body> of the fetched document. 160 const addStyles = (parent) => { 161 parent.querySelectorAll('link[rel="stylesheet"], style').forEach(styleElement => { 162 if (styleElement.tagName.toLowerCase() === 'link') { 163 const newLink = document.createElement('link'); 164 newLink.rel = 'stylesheet'; 165 newLink.href = new URL(styleElement.getAttribute('href'), this.urlValue).href; 166 this.shadowRoot.appendChild(newLink); 167 } else if (styleElement.tagName.toLowerCase() === 'style') { 168 const newStyle = document.createElement('style'); 169 newStyle.textContent = styleElement.textContent; 170 this.shadowRoot.appendChild(newStyle); 171 } 172 }); 173 }; 174 addStyles(doc.head); 175 addStyles(doc.body); 176 177 // Import body content 178 const bodyContent = doc.body.innerHTML; 179 const contentContainer = document.createElement('div'); 180 contentContainer.innerHTML = bodyContent; 181 this.shadowRoot.appendChild(contentContainer); 182 } 183}
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.