chameleon-backgrounds 2.0.0 → 2.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -24,10 +24,17 @@ Large background images slow down initial page loads. ChameleonBackgrounds **def
24
24
 
25
25
  ## Installation
26
26
 
27
- ### npm
27
+ ### Package Managers (npm / yarn / bun)
28
28
 
29
29
  ```bash
30
+ # npm
30
31
  npm install chameleon-backgrounds
32
+
33
+ # yarn
34
+ yarn add chameleon-backgrounds
35
+
36
+ # bun
37
+ bun add chameleon-backgrounds
31
38
  ```
32
39
 
33
40
  ### CDN / Script Tag
@@ -150,6 +157,23 @@ Stops any running slider, removes all injected DOM and styles, and restores the
150
157
 
151
158
  Returns a read-only copy of the resolved options.
152
159
 
160
+ ### `.reloadOptions(newOptions)`
161
+
162
+ Update options on the fly without destroying the instance. For example, you can change the transition duration or even convert a single background into a slider dynamically:
163
+ ```javascript
164
+ bg.reloadOptions({
165
+ type: 'slider',
166
+ src: [
167
+ './img/slide1.jpg',
168
+ './img/slide2.jpg'
169
+ ],
170
+ transitionDuration: 5000,
171
+ overlayColor: '#ff0000'
172
+ });
173
+ // To apply the updated options, simply reload the background
174
+ bg.reloadBackground();
175
+ ```
176
+
153
177
  ---
154
178
 
155
179
  ## Migration from v1
@@ -1,2 +1,2 @@
1
1
  /*! For license information please see chameleon-backgrounds.min.js.LICENSE.txt */
2
- !function(e,t){"object"==typeof exports&&"object"==typeof module?module.exports=t():"function"==typeof define&&define.amd?define([],t):"object"==typeof exports?exports.ChameleonBackgrounds=t():e.ChameleonBackgrounds=t()}(this,()=>(()=>{"use strict";const e={d:(t,n)=>{if(Array.isArray(n))for(var o=0;o<n.length;){var r=n[o++],i=n[o++];e.o(t,r)?0===i&&o++:0===i?Object.defineProperty(t,r,{enumerable:!0,value:n[o++]}):Object.defineProperty(t,r,{enumerable:!0,get:i})}else for(var r in n)e.o(n,r)&&!e.o(t,r)&&Object.defineProperty(t,r,{enumerable:!0,get:n[r]})},o:(e,t)=>Object.prototype.hasOwnProperty.call(e,t)};let t={};class n{static#e=Object.freeze({element:"body",type:"single",src:"",overlayColor:"#0f1e25",overlayImage:null,minOverlay:0,transitionDuration:2e3,sliderDuration:8e3,sliderLoop:!1});static#t=Object.freeze({transition_duration:"transitionDuration",slider_duration:"sliderDuration",slider_loop:"sliderLoop",min_overlay:"minOverlay",overlay_color:"overlayColor",overlay_image:"overlayImage"});#n;#o;#r;#i;#s=null;#l=null;#a=!1;constructor(e={}){const t=n.#d(e);this.#n={...n.#e,...t},this.#r=n.#c(),this.#o=n.#h(this.#n.element),this.#i=this.#o.innerHTML,this.#u()}destroy(){this.#a||(this.#a=!0,null!==this.#l&&(clearInterval(this.#l),this.#l=null),this.#s?.parentNode&&(this.#s.parentNode.removeChild(this.#s),this.#s=null),this.#o.innerHTML=this.#i,this.#o.style.backgroundImage="")}get options(){return{...this.#n}}#u(){this.#m(),this.#y(),this.#o.matches("body")?queueMicrotask(()=>this.#p()):"complete"===document.readyState?this.#p():window.addEventListener("load",()=>this.#p(),{once:!0})}#m(){const e=this.#r,t="body"===this.#n.element||this.#o.matches("body")?"body":this.#n.element,n=this.#n.transitionDuration/1e3,o=`\n ${"string"==typeof t?t:`.cbg-host-${e}`} {\n position: relative;\n }\n\n #cbg-inner-${e} {\n z-index: 2;\n position: relative;\n }\n\n .cbg-loader-${e} {\n height: 100%;\n width: 100%;\n position: ${this.#o.matches("body")?"fixed":"absolute"};\n background-image: ${this.#n.overlayImage?`url(${this.#n.overlayImage})`:"none"};\n background-color: ${this.#n.overlayColor};\n opacity: 1;\n z-index: 1;\n transition: opacity ${n}s ease;\n top: 0;\n left: 0;\n }\n `,r=document.createElement("style");r.dataset.chameleonUid=e,r.textContent=o,document.head.appendChild(r),this.#s=r}#y(){const e=this.#r,t=this.#o.innerHTML,n=document.createElement("div");n.id=`cbg-inner-${e}`,n.innerHTML=t;const o=document.createElement("div");o.classList.add(`cbg-loader-${e}`),this.#o.innerHTML="",this.#o.appendChild(n),this.#o.appendChild(o)}#p(){this.#a||("single"===this.#n.type?this.#g("string"==typeof this.#n.src?this.#n.src:this.#n.src[0]):"slider"===this.#n.type&&this.#f())}#g(e,t=!0){return this.#a?Promise.resolve():new Promise(t=>{const n=new Image;n.onload=()=>{if(this.#a)return t();this.#o.style.backgroundImage=`url(${e})`,this.#o.style.backgroundSize="cover";const n=this.#o.querySelector(`.cbg-loader-${this.#r}`);n&&(n.style.opacity=String(this.#n.minOverlay)),t()},n.onerror=()=>{console.warn(`[ChameleonBackgrounds] Failed to load image: ${e}`),t()},n.src=e})}reloadBackground(e){if(this.#a)return Promise.resolve();const t=this.#o.querySelector(`.cbg-loader-${this.#r}`);return t&&(t.style.opacity="1"),new Promise(t=>{setTimeout(()=>{if(this.#a)return t();this.#g(e,!1).then(t)},this.#n.transitionDuration)})}#f(){if(this.#a)return;const e=this.#n.src;if(!Array.isArray(e)||0===e.length)return void console.warn("[ChameleonBackgrounds] Slider mode requires an array of image URLs in `src`.");let t=0;this.#g(e[t]).then(()=>{if(this.#a)return;if(t=1,1===e.length)return;const n=this.#n.sliderDuration+2*this.#n.transitionDuration;this.#l=setInterval(()=>{this.#a?clearInterval(this.#l):(this.reloadBackground(e[t]),t++,t>=e.length&&(this.#n.sliderLoop?t=0:(clearInterval(this.#l),this.#l=null)))},n)})}static#d(e){const t={};for(const[o,r]of Object.entries(e))t[n.#t[o]??o]=r;return t}static#h(e){if(e instanceof HTMLElement)return e;if("string"==typeof e){if("body"===e)return document.body;const t=document.querySelector(e);if(!t)throw new Error(`[ChameleonBackgrounds] Element not found: ${e}`);return t}throw new TypeError(`[ChameleonBackgrounds] Invalid element: ${e}`)}static#c(){return"undefined"!=typeof crypto&&"function"==typeof crypto.randomUUID?crypto.randomUUID().slice(0,8):Array.from({length:8},()=>"abcdefghijklmnopqrstuvwxyz0123456789".charAt(Math.floor(36*Math.random()))).join("")}}const o=n;return e.d(t,["default",0,o]),t=t.default,t})());
2
+ !function(e,t){"object"==typeof exports&&"object"==typeof module?module.exports=t():"function"==typeof define&&define.amd?define([],t):"object"==typeof exports?exports.ChameleonBackgrounds=t():e.ChameleonBackgrounds=t()}(this,()=>(()=>{"use strict";const e={d:(t,i)=>{if(Array.isArray(i))for(var n=0;n<i.length;){var o=i[n++],r=i[n++];e.o(t,o)?0===r&&n++:0===r?Object.defineProperty(t,o,{enumerable:!0,value:i[n++]}):Object.defineProperty(t,o,{enumerable:!0,get:r})}else for(var o in i)e.o(i,o)&&!e.o(t,o)&&Object.defineProperty(t,o,{enumerable:!0,get:i[o]})},o:(e,t)=>Object.prototype.hasOwnProperty.call(e,t)};let t={};class i{static#e=Object.freeze({element:"body",type:"single",src:"",overlayColor:"#0f1e25",overlayImage:null,minOverlay:0,transitionDuration:2e3,sliderDuration:8e3,sliderLoop:!1});static#t=Object.freeze({transition_duration:"transitionDuration",slider_duration:"sliderDuration",slider_loop:"sliderLoop",min_overlay:"minOverlay",overlay_color:"overlayColor",overlay_image:"overlayImage"});#i;#n;#o;#r;#s=null;#l=null;#d=!1;constructor(e={}){const t=i.#a(e);this.#i={...i.#e,...t},this.#o=i.#h(),this.#n=i.#c(this.#i.element),this.#r=this.#n.innerHTML,this.#u()}destroy(){if(this.#d)return;this.#d=!0,null!==this.#l&&(clearInterval(this.#l),this.#l=null),this.#s?.parentNode&&(this.#s.parentNode.removeChild(this.#s),this.#s=null);const e=this.#n.querySelector(`#cbg-inner-${this.#o}`);if(e&&e.parentNode===this.#n){for(;e.firstChild;)this.#n.insertBefore(e.firstChild,e);this.#n.removeChild(e)}const t=this.#n.querySelector(`.cbg-loader-${this.#o}`);t&&t.remove(),this.#n.style.backgroundImage=""}get options(){return{...this.#i}}#u(){this.#m(),this.#p(),this.#n.matches("body")?queueMicrotask(()=>this.#y()):"complete"===document.readyState?this.#y():window.addEventListener("load",()=>this.#y(),{once:!0})}#m(){const e=this.#o,t="body"===this.#i.element||this.#n.matches("body")?"body":this.#i.element,i=this.#i.transitionDuration/1e3,n=`\n ${"string"==typeof t?t:`.cbg-host-${e}`} {\n position: relative;\n }\n\n #cbg-inner-${e} {\n z-index: 2;\n position: relative;\n }\n\n .cbg-loader-${e} {\n height: 100%;\n width: 100%;\n position: ${this.#n.matches("body")?"fixed":"absolute"};\n background-image: ${this.#i.overlayImage?`url(${this.#i.overlayImage})`:"none"};\n background-color: ${this.#i.overlayColor};\n opacity: 1;\n z-index: 1;\n transition: opacity ${i}s ease;\n top: 0;\n left: 0;\n }\n `,o=document.createElement("style");o.dataset.chameleonUid=e,o.textContent=n,document.head.appendChild(o),this.#s=o}#p(){const e=this.#o,t=document.createElement("div");for(t.id=`cbg-inner-${e}`;this.#n.firstChild;)t.appendChild(this.#n.firstChild);const i=document.createElement("div");i.classList.add(`cbg-loader-${e}`),this.#n.appendChild(t),this.#n.appendChild(i)}#y(){this.#d||("single"===this.#i.type?this.#g("string"==typeof this.#i.src?this.#i.src:this.#i.src[0]):"slider"===this.#i.type&&this.#f())}#g(e,t=!0){return this.#d?Promise.resolve():new Promise(t=>{const i=new Image;i.onload=()=>{if(this.#d)return t();this.#n.style.backgroundImage=`url(${e})`,this.#n.style.backgroundSize="cover";const i=this.#n.querySelector(`.cbg-loader-${this.#o}`);i&&(i.style.opacity=String(this.#i.minOverlay)),t()},i.onerror=()=>{console.warn(`[ChameleonBackgrounds] Failed to load image: ${e}`),t()},i.src=e})}reloadBackground(e){if(this.#d)return Promise.resolve();void 0!==e&&("slider"===this.#i.type&&"string"==typeof e?this.#i.src=e.includes(",")?e.split(",").map(e=>e.trim()):[e]:this.#i.src=e),null!==this.#l&&(clearInterval(this.#l),this.#l=null);const t=this.#n.querySelector(`.cbg-loader-${this.#o}`);return t&&(t.style.opacity="1"),new Promise(e=>{setTimeout(()=>{if(this.#d)return e();if("single"===this.#i.type){const t="string"==typeof this.#i.src?this.#i.src:this.#i.src[0];this.#g(t,!1).then(e)}else"slider"===this.#i.type&&(this.#f(),e())},this.#i.transitionDuration)})}reloadOptions(e){if(this.#d)return;const t=i.#a(e);this.#i={...this.#i,...t},this.#s?.parentNode&&(this.#s.parentNode.removeChild(this.#s),this.#s=null),this.#m()}#f(){if(this.#d)return;const e=this.#i.src;if(!Array.isArray(e)||0===e.length)return void console.warn("[ChameleonBackgrounds] Slider mode requires an array of image URLs in `src`.");let t=0;this.#g(e[t]).then(()=>{if(this.#d)return;if(t=1,1===e.length)return;const i=this.#i.sliderDuration+2*this.#i.transitionDuration;this.#l=setInterval(()=>{this.#d?clearInterval(this.#l):(this.reloadBackground(e[t]),t++,t>=e.length&&(this.#i.sliderLoop?t=0:(clearInterval(this.#l),this.#l=null)))},i)})}static#a(e){const t={};for(const[n,o]of Object.entries(e))t[i.#t[n]??n]=o;return t}static#c(e){if(e instanceof HTMLElement)return e;if("string"==typeof e){if("body"===e)return document.body;const t=document.querySelector(e);if(!t)throw new Error(`[ChameleonBackgrounds] Element not found: ${e}`);return t}throw new TypeError(`[ChameleonBackgrounds] Invalid element: ${e}`)}static#h(){return"undefined"!=typeof crypto&&"function"==typeof crypto.randomUUID?crypto.randomUUID().slice(0,8):Array.from({length:8},()=>"abcdefghijklmnopqrstuvwxyz0123456789".charAt(Math.floor(36*Math.random()))).join("")}}const n=i;return e.d(t,["default",0,n]),t=t.default,t})());
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "chameleon-backgrounds",
3
- "version": "2.0.0",
3
+ "version": "2.1.0",
4
4
  "description": "A zero-dependency JavaScript library to dynamically load background images with elegant fade-in transitions and slideshow support.",
5
5
  "type": "module",
6
6
  "main": "dist/chameleon-backgrounds.js",
@@ -119,8 +119,22 @@ class ChameleonBackgrounds {
119
119
  this.#styleElement = null;
120
120
  }
121
121
 
122
- // Restore original HTML and remove inline background-image
123
- this.#element.innerHTML = this.#originalHTML;
122
+ // Unwrap the original content instead of resetting innerHTML
123
+ const wrapper = this.#element.querySelector(`#cbg-inner-${this.#uid}`);
124
+ if (wrapper && wrapper.parentNode === this.#element) {
125
+ while (wrapper.firstChild) {
126
+ this.#element.insertBefore(wrapper.firstChild, wrapper);
127
+ }
128
+ this.#element.removeChild(wrapper);
129
+ }
130
+
131
+ // Remove loader
132
+ const loader = this.#element.querySelector(`.cbg-loader-${this.#uid}`);
133
+ if (loader) {
134
+ loader.remove();
135
+ }
136
+
137
+ // Remove inline background-image
124
138
  this.#element.style.backgroundImage = '';
125
139
  }
126
140
 
@@ -209,17 +223,19 @@ class ChameleonBackgrounds {
209
223
  */
210
224
  #buildDOM() {
211
225
  const uid = this.#uid;
212
- const content = this.#element.innerHTML;
213
226
 
214
227
  const wrapper = document.createElement('div');
215
228
  wrapper.id = `cbg-inner-${uid}`;
216
- wrapper.innerHTML = content;
229
+
230
+ // Move all child nodes from the element into the wrapper
231
+ while (this.#element.firstChild) {
232
+ wrapper.appendChild(this.#element.firstChild);
233
+ }
217
234
 
218
235
  const loader = document.createElement('div');
219
236
  loader.classList.add(`cbg-loader-${uid}`);
220
237
 
221
- // Replace element contents
222
- this.#element.innerHTML = '';
238
+ // Append wrapper and loader
223
239
  this.#element.appendChild(wrapper);
224
240
  this.#element.appendChild(loader);
225
241
  }
@@ -276,14 +292,30 @@ class ChameleonBackgrounds {
276
292
  });
277
293
  }
278
294
 
295
+ /**
279
296
  /**
280
297
  * Reload background with a fade-out → swap → fade-in cycle.
281
- * @param {string} src - New image URL.
298
+ * @param {string} [src] - Optional new image URL. If omitted, reloads using current options.
282
299
  * @returns {Promise<void>}
283
300
  */
284
301
  reloadBackground(src) {
285
302
  if (this.#destroyed) return Promise.resolve();
286
303
 
304
+ if (src !== undefined) {
305
+ if (this.#options.type === 'slider' && typeof src === 'string') {
306
+ // If the user passes a comma-separated string, split it. Otherwise wrap it.
307
+ this.#options.src = src.includes(',') ? src.split(',').map(s => s.trim()) : [src];
308
+ } else {
309
+ this.#options.src = src;
310
+ }
311
+ }
312
+
313
+ // Stop any running slider
314
+ if (this.#sliderIntervalId !== null) {
315
+ clearInterval(this.#sliderIntervalId);
316
+ this.#sliderIntervalId = null;
317
+ }
318
+
287
319
  const loader = this.#element.querySelector(`.cbg-loader-${this.#uid}`);
288
320
  if (loader) {
289
321
  loader.style.opacity = '1';
@@ -292,11 +324,38 @@ class ChameleonBackgrounds {
292
324
  return new Promise((resolve) => {
293
325
  setTimeout(() => {
294
326
  if (this.#destroyed) return resolve();
295
- this.#loadBackground(src, false).then(resolve);
327
+
328
+ if (this.#options.type === 'single') {
329
+ const singleSrc = typeof this.#options.src === 'string' ? this.#options.src : this.#options.src[0];
330
+ this.#loadBackground(singleSrc, false).then(resolve);
331
+ } else if (this.#options.type === 'slider') {
332
+ this.#startSlider();
333
+ resolve();
334
+ }
296
335
  }, this.#options.transitionDuration);
297
336
  });
298
337
  }
299
338
 
339
+ /**
340
+ * Update options on the fly without destroying the instance.
341
+ * @param {Object} newOptions - The new options to merge.
342
+ */
343
+ reloadOptions(newOptions) {
344
+ if (this.#destroyed) return;
345
+
346
+ const normalized = ChameleonBackgrounds.#normalizeOptions(newOptions);
347
+ this.#options = { ...this.#options, ...normalized };
348
+
349
+ // Remove old styles
350
+ if (this.#styleElement?.parentNode) {
351
+ this.#styleElement.parentNode.removeChild(this.#styleElement);
352
+ this.#styleElement = null;
353
+ }
354
+
355
+ // Inject updated styles
356
+ this.#injectStyles();
357
+ }
358
+
300
359
  // ---------------------------------------------------------------------------
301
360
  // Slider
302
361
  // ---------------------------------------------------------------------------