chameleon-backgrounds 2.0.1 → 2.1.1

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
@@ -157,6 +157,23 @@ Stops any running slider, removes all injected DOM and styles, and restores the
157
157
 
158
158
  Returns a read-only copy of the resolved options.
159
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
+
160
177
  ---
161
178
 
162
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(){if(this.#a)return;this.#a=!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.#o.querySelector(`#cbg-inner-${this.#r}`);if(e&&e.parentNode===this.#o){for(;e.firstChild;)this.#o.insertBefore(e.firstChild,e);this.#o.removeChild(e)}const t=this.#o.querySelector(`.cbg-loader-${this.#r}`);t&&t.remove(),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=document.createElement("div");for(t.id=`cbg-inner-${e}`;this.#o.firstChild;)t.appendChild(this.#o.firstChild);const n=document.createElement("div");n.classList.add(`cbg-loader-${e}`),this.#o.appendChild(t),this.#o.appendChild(n)}#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.#y(),this.#n.matches("body")?queueMicrotask(()=>this.#p()):"complete"===document.readyState?this.#p():window.addEventListener("load",()=>this.#p(),{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}#y(){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)}#p(){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.#v(e[t]),t++,t>=e.length&&(this.#i.sliderLoop?t=0:(clearInterval(this.#l),this.#l=null)))},i)})}#v(e){if(this.#d)return;const t=this.#n.querySelector(`.cbg-loader-${this.#o}`);t&&(t.style.opacity="1"),setTimeout(()=>{this.#d||this.#g(e,!1)},this.#i.transitionDuration)}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.1",
3
+ "version": "2.1.1",
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",
@@ -292,14 +292,30 @@ class ChameleonBackgrounds {
292
292
  });
293
293
  }
294
294
 
295
+ /**
295
296
  /**
296
297
  * Reload background with a fade-out → swap → fade-in cycle.
297
- * @param {string} src - New image URL.
298
+ * @param {string} [src] - Optional new image URL. If omitted, reloads using current options.
298
299
  * @returns {Promise<void>}
299
300
  */
300
301
  reloadBackground(src) {
301
302
  if (this.#destroyed) return Promise.resolve();
302
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
+
303
319
  const loader = this.#element.querySelector(`.cbg-loader-${this.#uid}`);
304
320
  if (loader) {
305
321
  loader.style.opacity = '1';
@@ -308,11 +324,38 @@ class ChameleonBackgrounds {
308
324
  return new Promise((resolve) => {
309
325
  setTimeout(() => {
310
326
  if (this.#destroyed) return resolve();
311
- 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
+ }
312
335
  }, this.#options.transitionDuration);
313
336
  });
314
337
  }
315
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
+
316
359
  // ---------------------------------------------------------------------------
317
360
  // Slider
318
361
  // ---------------------------------------------------------------------------
@@ -345,7 +388,7 @@ class ChameleonBackgrounds {
345
388
  return;
346
389
  }
347
390
 
348
- this.reloadBackground(sources[index]);
391
+ this.#cycleSliderSlide(sources[index]);
349
392
  index++;
350
393
 
351
394
  if (index >= sources.length) {
@@ -360,6 +403,24 @@ class ChameleonBackgrounds {
360
403
  });
361
404
  }
362
405
 
406
+ /**
407
+ * Cycle to a specific slide without resetting the slider instance.
408
+ * @param {string} src - The image URL to load.
409
+ */
410
+ #cycleSliderSlide(src) {
411
+ if (this.#destroyed) return;
412
+
413
+ const loader = this.#element.querySelector(`.cbg-loader-${this.#uid}`);
414
+ if (loader) {
415
+ loader.style.opacity = '1';
416
+ }
417
+
418
+ setTimeout(() => {
419
+ if (this.#destroyed) return;
420
+ this.#loadBackground(src, false);
421
+ }, this.#options.transitionDuration);
422
+ }
423
+
363
424
  // ---------------------------------------------------------------------------
364
425
  // Static Helpers
365
426
  // ---------------------------------------------------------------------------