UNPKG

senangwebs-story

Version:

Lightweight, dependency-free JavaScript library for creating interactive, visual novel-style story experiences.

1 lines 8.19 kB
!function(e,t){"object"==typeof exports&&"object"==typeof module?module.exports=t():"function"==typeof define&&define.amd?define([],t):"object"==typeof exports?exports.SWS=t():e.SWS=t()}(this,()=>(()=>{"use strict";var e={d:(t,n)=>{for(var s in n)e.o(n,s)&&!e.o(t,s)&&Object.defineProperty(t,s,{enumerable:!0,get:n[s]})},o:(e,t)=>Object.prototype.hasOwnProperty.call(e,t)},t={};e.d(t,{default:()=>s});class n{constructor(e,t){this.element=e,this.config=t,this.storyId=null,this.scenes=[],this.currentSceneIndex=0,this.currentDialogIndex=0,this.typewriterTimeout=null,this.isTypewriterRunning=!1,this.dialogSpeed=50,this._currentTypewriterText="",this._currentTypewriterElement=null,this._boundNext=()=>this.next(),this._boundBack=()=>this.back(),this._boundKeydown=e=>this._handleKeydown(e),this.config?this._initFromJSON():this._initFromHTML(),this._setupUI(),this._updateUI(),this._attachEventListeners()}_initFromHTML(){if(this.storyId=this.element.dataset.swsId,this.element.dataset.swsDialogSpeed){const e=parseInt(this.element.dataset.swsDialogSpeed,10);!isNaN(e)&&e>0&&(this.dialogSpeed=e)}Array.from(this.element.querySelectorAll("*")).filter(e=>Array.from(e.attributes).some(e=>/^data-sws-scene-\d+$/.test(e.name))).sort((e,t)=>{const n=e=>parseInt(Array.from(e.attributes).find(e=>e.name.startsWith("data-sws-scene-"))?.name.split("-").pop()||"0");return n(e)-n(t)}).forEach((e,t)=>{const n={element:e,sceneStart:e.dataset.swsSceneStart||null,background:e.querySelector("[data-sws-background] img")?.src,subjects:[],dialogs:[]},s=Array.from(e.querySelectorAll("[data-sws-subject-id]"));n.subjects=s.map(e=>({id:e.dataset.swsSubjectId,name:e.dataset.swsSubjectName,element:e}));Array.from(e.querySelectorAll("*")).filter(e=>Array.from(e.attributes).some(e=>/^data-sws-dialog-\d+$/.test(e.name))).sort((e,t)=>{const n=e=>parseInt(Array.from(e.attributes).find(e=>e.name.startsWith("data-sws-dialog-"))?.name.split("-").pop()||"0");return n(e)-n(t)}).forEach((e,t)=>{const s=e.querySelector("p");n.dialogs.push({element:e,text:s?s.innerHTML.trim():"",subjectId:e.dataset.swsSubject||null,dialogStart:e.dataset.swsDialogStart||null})}),this.scenes.push(n)})}_initFromJSON(){if(this.storyId=this.config.id,this.config.dialogSpeed){const e=parseInt(this.config.dialogSpeed,10);!isNaN(e)&&e>0&&(this.dialogSpeed=e)}this.element.dataset.sws=!0,this.element.dataset.swsId=this.storyId,this.element.innerHTML="",this.config.scenes.forEach((e,t)=>{const n=document.createElement("div");n.setAttribute(`data-sws-scene-${t+1}`,""),e.sceneStart&&(n.dataset.swsSceneStart=e.sceneStart),t>0&&(n.style.display="none");const s=document.createElement("div");s.dataset.swsBackground="";const i=document.createElement("img");i.src=e.background,i.alt=`Scene Background ${t+1}`,s.appendChild(i),n.appendChild(s);const r=document.createElement("div");r.dataset.swsSubjects="";const a=[];e.subjects.forEach(e=>{const t=document.createElement("img");t.src=e.src,t.dataset.swsSubjectId=e.id,t.dataset.swsSubjectName=e.name,t.alt=e.name,r.appendChild(t),a.push({id:e.id,name:e.name,element:t})}),n.appendChild(r);const c=document.createElement("div");c.dataset.swsDialogBox="";const o=document.createElement("h4");o.dataset.swsActiveSubjectName="",c.appendChild(o);const d=[];e.dialogs.forEach((e,t)=>{const n=document.createElement("div");n.dataset.swsDialog=t+1,n.setAttribute(`data-sws-dialog-${t+1}`,""),e.subjectId&&(n.dataset.swsSubject=e.subjectId),e.dialogStart&&(n.dataset.swsDialogStart=e.dialogStart);const s=document.createElement("p");s.innerHTML=e.text,n.appendChild(s),c.appendChild(n),d.push({element:n,text:e.text,subjectId:e.subjectId||null,dialogStart:e.dialogStart||null})}),n.appendChild(c),this.element.appendChild(n);const l={element:n,sceneStart:e.sceneStart||null,background:e.background,subjects:a,dialogs:d};this.scenes.push(l)});const e=document.createElement("div");e.dataset.swsActions="";const t=document.createElement("button");t.dataset.swsButton="back",t.textContent="Back";const n=document.createElement("button");n.dataset.swsButton="next",n.textContent="Next",e.appendChild(t),e.appendChild(n),this.element.appendChild(e)}_setupUI(){this.scenes.forEach((e,t)=>{e.element.style.display=0===t?"":"none",e.dialogs.forEach((e,t)=>{e.element&&(e.element.style.display="none")})})}_attachEventListeners(){const e=this.element.querySelector('[data-sws-button="next"]'),t=this.element.querySelector('[data-sws-button="back"]');e&&e.addEventListener("click",this._boundNext),t&&t.addEventListener("click",this._boundBack),document.addEventListener("keydown",this._boundKeydown)}_handleKeydown(e){this.element&&("ArrowRight"===e.key||" "===e.key?(e.preventDefault(),this.next()):"ArrowLeft"===e.key&&(e.preventDefault(),this.back()))}next(){if(this.isTypewriterRunning)return void this._completeTypewriter();const e=this.scenes[this.currentSceneIndex];this.currentDialogIndex<e.dialogs.length-1?(this.currentDialogIndex++,this._updateUI()):this.currentSceneIndex<this.scenes.length-1&&(this.currentSceneIndex++,this.currentDialogIndex=0,this._updateUI(!0))}back(){this._completeTypewriter(),this.currentDialogIndex>0?(this.currentDialogIndex--,this._updateUI()):this.currentSceneIndex>0&&(this.currentSceneIndex--,this.currentDialogIndex=this.scenes[this.currentSceneIndex].dialogs.length-1,this._updateUI(!0))}_updateUI(e=!1){e&&(this.scenes.forEach((e,t)=>{e.element.style.display=t===this.currentSceneIndex?"":"none"}),this._executeCallback(this.scenes[this.currentSceneIndex].sceneStart));const t=this.scenes[this.currentSceneIndex];if(!t||!t.dialogs||this.currentDialogIndex>=t.dialogs.length)return void console.error("Scene or dialog not found",{sceneIndex:this.currentSceneIndex,dialogIndex:this.currentDialogIndex,totalScenes:this.scenes.length,scene:t});const n=t.dialogs[this.currentDialogIndex];t.dialogs.forEach((e,t)=>{e.element&&(e.element.style.display="none")});const s=t.element.querySelector(`[data-sws-dialog-${this.currentDialogIndex+1}]`),i=t.element.querySelector("[data-sws-active-subject-name]"),r=t.subjects.find(e=>e.id===n.subjectId);t.subjects.forEach(e=>e.element?.classList.remove("active")),r?(i.textContent=r.name,r.element?.classList.add("active")):i.textContent="",s?(this._typewriter(n.text,s.querySelector("p")),this._executeCallback(n.dialogStart)):console.error("Active dialog element not found",{sceneIndex:this.currentSceneIndex,dialogIndex:this.currentDialogIndex,selector:`[data-sws-dialog-${this.currentDialogIndex+1}]`,sceneElement:t.element})}_typewriter(e,t){if(!t)return;t.parentElement.style.display="",t.innerHTML="",this.isTypewriterRunning=!0,this._currentTypewriterText=e,this._currentTypewriterElement=t;let n=0;const s=document.createElement("div");s.innerHTML=e;const i=s.textContent||s.innerText||"",r=()=>{n<i.length?(t.textContent+=i.charAt(n),n++,this.typewriterTimeout=setTimeout(r,this.dialogSpeed)):(t.innerHTML=e,this.isTypewriterRunning=!1,this._currentTypewriterText="",this._currentTypewriterElement=null)};r()}_completeTypewriter(){if(this.typewriterTimeout){clearTimeout(this.typewriterTimeout),this.typewriterTimeout=null,this.isTypewriterRunning=!1;const e=this.scenes[this.currentSceneIndex],t=e.dialogs[this.currentDialogIndex],n=e.element.querySelector(`[data-sws-dialog-${this.currentDialogIndex+1}]`);if(n){const e=n.querySelector("p");e&&(e.innerHTML=t.text)}this._currentTypewriterText="",this._currentTypewriterElement=null}}destroy(){this.typewriterTimeout&&(clearTimeout(this.typewriterTimeout),this.typewriterTimeout=null);const e=this.element?.querySelector('[data-sws-button="next"]'),t=this.element?.querySelector('[data-sws-button="back"]');e&&e.removeEventListener("click",this._boundNext),t&&t.removeEventListener("click",this._boundBack),document.removeEventListener("keydown",this._boundKeydown),this.scenes=[],this.isTypewriterRunning=!1,this._currentTypewriterText="",this._currentTypewriterElement=null,this.element=null,this.config=null}_executeCallback(e){if(e)try{new Function(e)()}catch(e){console.error("Error executing callback:",e)}}}document.addEventListener("DOMContentLoaded",()=>{document.querySelectorAll("[data-sws]").forEach(e=>new n(e))});const s=n;return t=t.default})());