kashi 2.0.0 β†’ 3.0.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
@@ -16,7 +16,7 @@ This project is a dependency-free library that aims to provide a way to correctl
16
16
  ## πŸ“œ Features
17
17
 
18
18
  - [x] 🎡 Process and list song lyrics in .lrc files
19
- - [x] πŸ’ͺ Supports both directly inputting the .lrc file and a URL that returns it
19
+ - [x] πŸ’ͺ Easily integrates with React and other frameworks
20
20
  - [x] βœ‰οΈ Implements the Observer pattern and emits events at each step of the process whenever something changes
21
21
  - [x] ✏️ Allows you to enter custom text when the lyrics line is empty
22
22
  - [x] 🎀 Synchronizes the lyrics with the music that is playing
@@ -24,6 +24,10 @@ This project is a dependency-free library that aims to provide a way to correctl
24
24
  - [ ] 🧞 Supports the [Walaoke extension](https://en.wikipedia.org/wiki/LRC_(file_format)#Walaoke_extension)
25
25
  - [ ] πŸ•–οΈ Supports the [A2 extension](https://en.wikipedia.org/wiki/LRC_(file_format)#A2_extension_(Enhanced_LRC_format))
26
26
 
27
+ > **Note:** Support for fetching lyrics using a URL has been removed because it only supported public URLs.
28
+ >
29
+ > Therefore, instead of refactoring to handle requests to private APIs, I opted to support only files. If a request is necessary, you can make it externally and then pass the returned file to Kashi!
30
+
27
31
  ## 🎨 How to use it in my project?
28
32
 
29
33
  ### <span id="classic-scripts">πŸ™Œ Classic scripts</span>
@@ -53,9 +57,7 @@ The project is also exported using [UMD](https://github.com/umdjs/umd), which me
53
57
  ```js
54
58
  "use strict";
55
59
 
56
- // Using the file or using a url, both works fine
57
60
  new Kashi({
58
- // url, // Public link that returns the file when a GET request is made OR...
59
61
  file. // Loaded from an input[type="file"] or anywhere else
60
62
  container: document.getElementById("kashi"),
61
63
  });
@@ -80,9 +82,7 @@ import { Kashi } from "kashi";
80
82
 
81
83
  // Usage is essentially the same as in the previous section
82
84
 
83
- // Using the file or using a url, both works fine
84
85
  new Kashi({
85
- // url, // Public link that returns the file when a GET request is made OR...
86
86
  file. // Loaded from an input[type="file"] or anywhere else
87
87
  container: document.getElementById("kashi"),
88
88
  });
@@ -105,7 +105,7 @@ import { useEffect, useRef } from "react";
105
105
  import { Kashi, KashiProps } from "kashi";
106
106
 
107
107
  // Example using Vite, React and TypeScript
108
- export const KashiWrapper = (props: KashiProps) => {
108
+ export const KashiWrapper = (props: Omit<KashiProps, "container">) => {
109
109
  const ref = useRef<HTMLDivElement>(null);
110
110
 
111
111
  useEffect(() => {
@@ -134,12 +134,10 @@ You must pass some properties to Kashi to define what lyrics display and where.
134
134
 
135
135
  | Property | Type | Default value | Is required? | Description |
136
136
  | --------------- | ------------------ | ------------- | ------------ | ----------------------------------------- |
137
- | `file` | `Blob` (or `File`) | - | No | Lyrics file |
138
- | `url` | `string` | - | No | Lyrics url |
137
+ | `file` | `Blob` (or `File`) | - | Yes | Lyrics file |
139
138
  | `container` | `HTMLDivElement` | - | Yes | Element where the lyrics will be inserted |
140
- | `emptyLineText` | `string` | `🎝` | No | Custom text for empty lines of the lyrics |
141
-
142
- > Neither `file` nor `url` are β€œmandatory”, but **at least one of these properties must be specified**, otherwise an error will be thrown.
139
+ | `emptyLineText` | `string` | `...` | No | Custom text for empty lines of the lyrics |
140
+ | `noLyricsText` | `string` | - | No | Custom text for when there are no lyrics |
143
141
 
144
142
  ## πŸ‘Ύ Generated HTML structure
145
143
 
@@ -186,12 +184,12 @@ The instance generated by `Kashi` has some public methods and attributes that ca
186
184
 
187
185
  | Name | Type | Description |
188
186
  | ------------------ | --------- | ------------------------------------------------------------ |
189
- | `url` | Attribute | Returns the url from the current lyrics if it was fetched from a link |
190
187
  | `file` | Attribute | Returns the file from the current lyrics |
191
- | `emptyLineText` | Attribute | Returns the default text set for empty lines |
192
- | `setUrl` | Method | Function capable of changing the current lyrics file by passing the **url** of the new file |
188
+ | `emptyLineText` | Attribute | Returns the text set for empty lines |
189
+ | `noLyricsText` | Attribute | Returns the text set for when there are no lyrics |
193
190
  | `setFile` | Method | Function capable of changing the current lyrics file by passing the the new **file** |
194
191
  | `setEmptyLineText` | Method | Function capable of changing the text defined for empty lines |
192
+ | `noLyricsText` | Method | Function capable of changing the text defined for when there are no lyrics |
195
193
  | `subscribe` | Method | Function capable of defining a callback to be executed when a given event is triggered |
196
194
  | `unsubscribe` | Method | Function capable of making a callback to stop listening to an event |
197
195
  | `notify` | Method | Function capable of triggering an event |
@@ -200,12 +198,12 @@ The instance generated by `Kashi` has some public methods and attributes that ca
200
198
 
201
199
  When creating a new instance using `Kashi` you will have access to the `subscribe`, `unsubscribe` and `notify` methods, these methods can be used respectively to listen for an event, stop listening for an event and manually trigger an event. Below is the list of events triggered internally:
202
200
 
203
- | Event | Data | Trigger |
204
- | ------------------- | --------------------------- | ------------------------------------------------------------ |
205
- | `urlSet` | `{ url: string }` | When instantiating by informing `url` or calling the `setUrl` method |
206
- | `fileSet` | `{ file: Blob }` | When instantiating or calling the `setFile` method (when calling the `setUrl` method the file will be fetched and the `setFile` method called) |
207
- | `emptyLineTextSet` | `{ emptyLineText: string }` | When instantiating by informing `emptyLineText` or calling the `setEmptyLineText` method |
208
- | `lyricLinesUpdated` | `{ lyricLines: string[] }` | When inserting/updating lyrics in HTML |
201
+ | Event | Data | Trigger |
202
+ | ------------------- | --------------------------- | ------------------------------------------ |
203
+ | `fileSet` | `{ file: Blob }` | When calling the `setFile` method |
204
+ | `emptyLineTextSet` | `{ emptyLineText: string }` | When calling the `setEmptyLineText` method |
205
+ | `noLyricsText` | `{ noLyricsText: text }` | When calling the `setNoLyricsText` method |
206
+ | `lyricLinesUpdated` | `{ lyricLines: string[] }` | When inserting/updating lyrics in HTML |
209
207
 
210
208
  ## πŸ€” How do I run the project on my machine?
211
209
 
package/kashi.d.ts CHANGED
@@ -1,12 +1,10 @@
1
1
  // Generated by dts-bundle-generator v9.5.1
2
2
 
3
- export type KashiUrl = string | null;
4
- export type KashiFile = Blob | null;
3
+ export type KashiFile = Blob | undefined;
5
4
  export type EventName = string;
6
5
  export type EventData = any;
7
6
  export type EventCallback = (data: EventData) => void;
8
7
  export interface KashiProps {
9
- url?: KashiUrl;
10
8
  file?: KashiFile;
11
9
  container: HTMLDivElement;
12
10
  emptyLineText?: string;
@@ -16,12 +14,12 @@ export interface KashiProps {
16
14
  export declare class Kashi {
17
15
  #private;
18
16
  constructor(props: KashiProps);
19
- get url(): KashiUrl;
20
17
  get file(): KashiFile;
21
18
  get emptyLineText(): string;
22
- setUrl(url: string): Promise<void>;
23
- setFile(file: Blob): Promise<void>;
19
+ get noLyricsText(): string;
20
+ setFile(file: KashiFile): Promise<void>;
24
21
  setEmptyLineText(text: string): void;
22
+ setNoLyricsText(text: string): void;
25
23
  subscribe(event: EventName, fn: EventCallback): void;
26
24
  unsubscribe(event: EventName, fn: EventCallback): void;
27
25
  notify(event: EventName, data?: EventData): void;
package/kashi.js CHANGED
@@ -1 +1 @@
1
- !function(e,t){if("object"==typeof exports&&"object"==typeof module)module.exports=t();else if("function"==typeof define&&define.amd)define([],t);else{var r=t();for(var i in r)("object"==typeof exports?exports:e)[i]=r[i]}}(self,()=>(()=>{"use strict";var e={d:(t,r)=>{for(var i in r)e.o(r,i)&&!e.o(t,i)&&Object.defineProperty(t,i,{enumerable:!0,get:r[i]})},o:(e,t)=>Object.prototype.hasOwnProperty.call(e,t),r:e=>{"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})}},t={};e.r(t),e.d(t,{Kashi:()=>E});const r=/\[\d{2}:\d{2}.\d{2}\]/,i=/^\[\d{2}:\d{2}.\d{2}\](.*)$/;var n,s,a,o,l,f,h,c,u,d,p,y,m,w=function(e,t,r,i,n){if("m"===i)throw new TypeError("Private method is not writable");if("a"===i&&!n)throw new TypeError("Private accessor was defined without a setter");if("function"==typeof t?e!==t||!n:!t.has(e))throw new TypeError("Cannot write private member to an object whose class did not declare it");return"a"===i?n.call(e,r):n?n.value=r:t.set(e,r),r},b=function(e,t,r,i){if("a"===r&&!i)throw new TypeError("Private accessor was defined without a getter");if("function"==typeof t?e!==t||!i:!t.has(e))throw new TypeError("Cannot read private member from an object whose class did not declare it");return"m"===r?i:"a"===r?i.call(e):i?i.value:t.get(e)};class E{constructor(e){if(n.add(this),s.set(this,null),a.set(this,null),o.set(this,[]),l.set(this,void 0),f.set(this,new Map),h.set(this,void 0),c.set(this,null),u.set(this,!1),e.url&&e.file)throw new Error("Cannot specify both url and file.");if(!e.url&&!e.file)throw new Error("Must specify either url or file.");if(!(e.container&&e.container instanceof HTMLDivElement))throw new Error("Container must be an instance of HTMLDivElement.");if(e.emptyLineText&&this.setEmptyLineText(e.emptyLineText),e.audioPlayer&&!(e.audioPlayer instanceof HTMLAudioElement))throw new Error("Audio player must be an instance of HTMLAudioElement.");e.audioPlayer&&(w(this,c,e.audioPlayer,"f"),b(this,c,"f").addEventListener("timeupdate",()=>{b(this,n,"m",m).call(this)}),b(this,c,"f").addEventListener("seeked",()=>{b(this,l,"f").querySelectorAll("p > span").forEach(e=>{e instanceof HTMLSpanElement&&(e.dataset.ariaCurrent="false")}),b(this,n,"m",m).call(this)})),e.enableAutoScroll&&w(this,u,e.enableAutoScroll,"f"),w(this,l,e.container,"f"),this.subscribe("fileSet",b(this,n,"m",y).bind(this)),this.subscribe("emptyLineTextSet",b(this,n,"m",y).bind(this)),this.subscribe("lyricLinesUpdated",b(this,n,"m",m).bind(this)),e.url?this.setUrl(e.url):e.file&&this.setFile(e.file)}get url(){return b(this,s,"f")}get file(){return b(this,a,"f")}get emptyLineText(){return b(this,h,"f")??"..."}async setUrl(e){try{const t=await fetch(e);if(!t.ok)throw new Error(`HTTP error! Status: ${t.status}.`);const r=t.headers.get("Content-Type");if(!r||!r.includes("text/plain"))throw new Error("Invalid content type. Expected text/plain.");const i=await t.blob();w(this,s,e,"f"),this.notify("urlSet",{url:e}),this.setFile(i)}catch(e){throw new Error((e instanceof Error&&e.message.length?e.message+" ":"")+"Failed to fetch the lyric file.")}}async setFile(e){const t=await b(this,n,"m",d).call(this,e);w(this,o,b(this,n,"m",p).call(this,t),"f"),w(this,a,e,"f"),this.notify("fileSet",{file:e})}setEmptyLineText(e){w(this,h,e,"f"),this.notify("emptyLineTextSet",{emptyLineText:e})}subscribe(e,t){b(this,f,"f").set(e,[...b(this,f,"f").get(e)??[],t])}unsubscribe(e,t){const r=b(this,f,"f").get(e);r&&r.length>1?b(this,f,"f").set(e,[...r.filter(e=>e!==t)]):b(this,f,"f").delete(e)}notify(e,t){[...b(this,f,"f").get(e)??[]].forEach(e=>{e(t)})}}return s=new WeakMap,a=new WeakMap,o=new WeakMap,l=new WeakMap,f=new WeakMap,h=new WeakMap,c=new WeakMap,u=new WeakMap,n=new WeakSet,d=async function(e){return new Promise((t,r)=>{const i=new FileReader;i.onload=e=>{"string"==typeof e.target?.result&&e.target?.result.trim().length?t(e.target.result):r(new Error("Failed to read file content."))},i.onerror=()=>r(new Error("Error reading file.")),i.readAsText(e)})},p=function(e){const t=e.split("\n").reduce((e,t)=>{const r=t.trim();return i.test(r)?[...e,r]:e},[]);if(0===t.length)throw new Error("No valid lyric lines found in the file.");return t},y=function(){const e=b(this,o,"f").map(e=>{const t=e.replace(r,""),i=e.match(r)?.[0]?.slice(1,-1);if(!i)return"";const n=i.split(":"),s=n[1].split(".");return`\n <span \n data-time="${i}" \n data-ms-time="${60*parseInt(n[0],10)*1e3+1e3*parseInt(s[0],10)+10*parseInt(s[1],10)}"\n data-empty="${0===t.length}"\n data-aria-current="false"\n >\n ${t||(b(this,h,"f")??"...")}\n </span>\n `}).join("<br/>");b(this,l,"f").innerHTML=`<p>${e}</p>`,this.notify("lyricLinesUpdated",{lyricLines:b(this,o,"f")})},m=function(){if(!b(this,c,"f"))return;const e=1e3*b(this,c,"f").currentTime,t=b(this,l,"f").querySelectorAll("p > span");let r=null;for(let i=0;i<t.length;i++){const n=t[i],s=parseInt(n.getAttribute("data-ms-time")||"0",10);if(n.dataset.ariaCurrent="false",!(e>=s))break;r=n}r&&(r.dataset.ariaCurrent="true",b(this,u,"f")&&r.scrollIntoView({behavior:"smooth",block:"center"}))},t})());
1
+ !function(e,t){if("object"==typeof exports&&"object"==typeof module)module.exports=t();else if("function"==typeof define&&define.amd)define([],t);else{var i=t();for(var n in i)("object"==typeof exports?exports:e)[n]=i[n]}}(self,()=>(()=>{"use strict";var e={d:(t,i)=>{for(var n in i)e.o(i,n)&&!e.o(t,n)&&Object.defineProperty(t,n,{enumerable:!0,get:i[n]})},o:(e,t)=>Object.prototype.hasOwnProperty.call(e,t),r:e=>{"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})}},t={};e.r(t),e.d(t,{Kashi:()=>T});const i=/\[\d{2}:\d{2}.\d{2}\]/,n=/^\[\d{2}:\d{2}.\d{2}\](.*)$/;var r,s,a,o,l,f,h,c,d,u,p,y,m,w=function(e,t,i,n,r){if("m"===n)throw new TypeError("Private method is not writable");if("a"===n&&!r)throw new TypeError("Private accessor was defined without a setter");if("function"==typeof t?e!==t||!r:!t.has(e))throw new TypeError("Cannot write private member to an object whose class did not declare it");return"a"===n?r.call(e,i):r?r.value=i:t.set(e,i),i},b=function(e,t,i,n){if("a"===i&&!n)throw new TypeError("Private accessor was defined without a getter");if("function"==typeof t?e!==t||!n:!t.has(e))throw new TypeError("Cannot read private member from an object whose class did not declare it");return"m"===i?n:"a"===i?n.call(e):n?n.value:t.get(e)};class T{constructor(e){if(r.add(this),s.set(this,void 0),a.set(this,[]),o.set(this,void 0),l.set(this,new Map),f.set(this,void 0),h.set(this,null),c.set(this,!1),d.set(this,void 0),!(e.container&&e.container instanceof HTMLDivElement))throw new Error("Container must be an instance of HTMLDivElement.");if(e.emptyLineText&&this.setEmptyLineText(e.emptyLineText),e.audioPlayer&&!(e.audioPlayer instanceof HTMLAudioElement))throw new Error("Audio player must be an instance of HTMLAudioElement.");e.audioPlayer&&(w(this,h,e.audioPlayer,"f"),b(this,h,"f").addEventListener("timeupdate",()=>{b(this,r,"m",m).call(this)}),b(this,h,"f").addEventListener("seeked",()=>{b(this,o,"f").querySelectorAll("p > span").forEach(e=>{e instanceof HTMLSpanElement&&(e.dataset.ariaCurrent="false")}),b(this,r,"m",m).call(this)})),e.enableAutoScroll&&w(this,c,e.enableAutoScroll,"f"),w(this,o,e.container,"f"),this.subscribe("fileSet",b(this,r,"m",y).bind(this)),this.subscribe("emptyLineTextSet",b(this,r,"m",y).bind(this)),this.subscribe("lyricLinesUpdated",b(this,r,"m",m).bind(this)),this.setFile(e.file)}get file(){return b(this,s,"f")}get emptyLineText(){return b(this,f,"f")??"..."}get noLyricsText(){return b(this,d,"f")}async setFile(e){if(e){const t=await b(this,r,"m",u).call(this,e);w(this,a,b(this,r,"m",p).call(this,t),"f")}else w(this,a,[],"f");w(this,s,e,"f"),this.notify("fileSet",{file:e})}setEmptyLineText(e){w(this,f,e,"f"),this.notify("emptyLineTextSet",{emptyLineText:e})}setNoLyricsText(e){w(this,d,e,"f"),this.notify("noLyricsTextSet",{noLyricsText:e})}subscribe(e,t){b(this,l,"f").set(e,[...b(this,l,"f").get(e)??[],t])}unsubscribe(e,t){const i=b(this,l,"f").get(e);i&&i.length>1?b(this,l,"f").set(e,[...i.filter(e=>e!==t)]):b(this,l,"f").delete(e)}notify(e,t){[...b(this,l,"f").get(e)??[]].forEach(e=>{e(t)})}}return s=new WeakMap,a=new WeakMap,o=new WeakMap,l=new WeakMap,f=new WeakMap,h=new WeakMap,c=new WeakMap,d=new WeakMap,r=new WeakSet,u=async function(e){return new Promise((t,i)=>{const n=new FileReader;n.onload=e=>{"string"==typeof e.target?.result&&e.target?.result.trim().length?t(e.target.result):i(new Error("Failed to read file content."))},n.onerror=()=>i(new Error("Error reading file.")),n.readAsText(e)})},p=function(e){const t=e.split("\n").reduce((e,t)=>{const i=t.trim();return n.test(i)?[...e,i]:e},[]);if(0===t.length)throw new Error("No valid lyric lines found in the file.");return t},y=function(){const e=b(this,a,"f").map(e=>{const t=e.replace(i,""),n=e.match(i)?.[0]?.slice(1,-1);if(!n)return"";const r=n.split(":"),s=r[1].split(".");return`\n <span \n data-time="${n}" \n data-ms-time="${60*parseInt(r[0],10)*1e3+1e3*parseInt(s[0],10)+10*parseInt(s[1],10)}"\n data-empty="${0===t.length}"\n data-aria-current="false"\n >\n ${t||(b(this,f,"f")??"...")}\n </span>\n `}).join("<br/>");b(this,o,"f").innerHTML=`<p>${e.length?e:`\n <span data-no-lyrics="true">${b(this,d,"f")??""}</span>\n `}</p>`,this.notify("lyricLinesUpdated",{lyricLines:b(this,a,"f")})},m=function(){if(!b(this,h,"f")||!b(this,a,"f").length)return;const e=1e3*b(this,h,"f").currentTime,t=b(this,o,"f").querySelectorAll("p > span");let i=null;for(let n=0;n<t.length;n++){const r=t[n],s=parseInt(r.getAttribute("data-ms-time")||"0",10);if(r.dataset.ariaCurrent="false",!(e>=s))break;i=r}i&&(i.dataset.ariaCurrent="true",b(this,c,"f")&&i.scrollIntoView({behavior:"smooth",block:"center"}))},t})());
package/kashi.mjs CHANGED
@@ -1 +1 @@
1
- const t=/\[\d{2}:\d{2}.\d{2}\]/,e=/^\[\d{2}:\d{2}.\d{2}\](.*)$/;var i,r,n,s,a,o,l,h,f,c,u,d,p,w=function(t,e,i,r,n){if("m"===r)throw new TypeError("Private method is not writable");if("a"===r&&!n)throw new TypeError("Private accessor was defined without a setter");if("function"==typeof e?t!==e||!n:!e.has(t))throw new TypeError("Cannot write private member to an object whose class did not declare it");return"a"===r?n.call(t,i):n?n.value=i:e.set(t,i),i},m=function(t,e,i,r){if("a"===i&&!r)throw new TypeError("Private accessor was defined without a getter");if("function"==typeof e?t!==e||!r:!e.has(t))throw new TypeError("Cannot read private member from an object whose class did not declare it");return"m"===i?r:"a"===i?r.call(t):r?r.value:e.get(t)};class y{constructor(t){if(i.add(this),r.set(this,null),n.set(this,null),s.set(this,[]),a.set(this,void 0),o.set(this,new Map),l.set(this,void 0),h.set(this,null),f.set(this,!1),t.url&&t.file)throw new Error("Cannot specify both url and file.");if(!t.url&&!t.file)throw new Error("Must specify either url or file.");if(!(t.container&&t.container instanceof HTMLDivElement))throw new Error("Container must be an instance of HTMLDivElement.");if(t.emptyLineText&&this.setEmptyLineText(t.emptyLineText),t.audioPlayer&&!(t.audioPlayer instanceof HTMLAudioElement))throw new Error("Audio player must be an instance of HTMLAudioElement.");t.audioPlayer&&(w(this,h,t.audioPlayer,"f"),m(this,h,"f").addEventListener("timeupdate",()=>{m(this,i,"m",p).call(this)}),m(this,h,"f").addEventListener("seeked",()=>{m(this,a,"f").querySelectorAll("p > span").forEach(t=>{t instanceof HTMLSpanElement&&(t.dataset.ariaCurrent="false")}),m(this,i,"m",p).call(this)})),t.enableAutoScroll&&w(this,f,t.enableAutoScroll,"f"),w(this,a,t.container,"f"),this.subscribe("fileSet",m(this,i,"m",d).bind(this)),this.subscribe("emptyLineTextSet",m(this,i,"m",d).bind(this)),this.subscribe("lyricLinesUpdated",m(this,i,"m",p).bind(this)),t.url?this.setUrl(t.url):t.file&&this.setFile(t.file)}get url(){return m(this,r,"f")}get file(){return m(this,n,"f")}get emptyLineText(){return m(this,l,"f")??"..."}async setUrl(t){try{const e=await fetch(t);if(!e.ok)throw new Error(`HTTP error! Status: ${e.status}.`);const i=e.headers.get("Content-Type");if(!i||!i.includes("text/plain"))throw new Error("Invalid content type. Expected text/plain.");const n=await e.blob();w(this,r,t,"f"),this.notify("urlSet",{url:t}),this.setFile(n)}catch(t){throw new Error((t instanceof Error&&t.message.length?t.message+" ":"")+"Failed to fetch the lyric file.")}}async setFile(t){const e=await m(this,i,"m",c).call(this,t);w(this,s,m(this,i,"m",u).call(this,e),"f"),w(this,n,t,"f"),this.notify("fileSet",{file:t})}setEmptyLineText(t){w(this,l,t,"f"),this.notify("emptyLineTextSet",{emptyLineText:t})}subscribe(t,e){m(this,o,"f").set(t,[...m(this,o,"f").get(t)??[],e])}unsubscribe(t,e){const i=m(this,o,"f").get(t);i&&i.length>1?m(this,o,"f").set(t,[...i.filter(t=>t!==e)]):m(this,o,"f").delete(t)}notify(t,e){[...m(this,o,"f").get(t)??[]].forEach(t=>{t(e)})}}r=new WeakMap,n=new WeakMap,s=new WeakMap,a=new WeakMap,o=new WeakMap,l=new WeakMap,h=new WeakMap,f=new WeakMap,i=new WeakSet,c=async function(t){return new Promise((e,i)=>{const r=new FileReader;r.onload=t=>{"string"==typeof t.target?.result&&t.target?.result.trim().length?e(t.target.result):i(new Error("Failed to read file content."))},r.onerror=()=>i(new Error("Error reading file.")),r.readAsText(t)})},u=function(t){const i=t.split("\n").reduce((t,i)=>{const r=i.trim();return e.test(r)?[...t,r]:t},[]);if(0===i.length)throw new Error("No valid lyric lines found in the file.");return i},d=function(){const e=m(this,s,"f").map(e=>{const i=e.replace(t,""),r=e.match(t)?.[0]?.slice(1,-1);if(!r)return"";const n=r.split(":"),s=n[1].split(".");return`\n <span \n data-time="${r}" \n data-ms-time="${60*parseInt(n[0],10)*1e3+1e3*parseInt(s[0],10)+10*parseInt(s[1],10)}"\n data-empty="${0===i.length}"\n data-aria-current="false"\n >\n ${i||(m(this,l,"f")??"...")}\n </span>\n `}).join("<br/>");m(this,a,"f").innerHTML=`<p>${e}</p>`,this.notify("lyricLinesUpdated",{lyricLines:m(this,s,"f")})},p=function(){if(!m(this,h,"f"))return;const t=1e3*m(this,h,"f").currentTime,e=m(this,a,"f").querySelectorAll("p > span");let i=null;for(let r=0;r<e.length;r++){const n=e[r],s=parseInt(n.getAttribute("data-ms-time")||"0",10);if(n.dataset.ariaCurrent="false",!(t>=s))break;i=n}i&&(i.dataset.ariaCurrent="true",m(this,f,"f")&&i.scrollIntoView({behavior:"smooth",block:"center"}))};export{y as Kashi};
1
+ const t=/\[\d{2}:\d{2}.\d{2}\]/,e=/^\[\d{2}:\d{2}.\d{2}\](.*)$/;var i,n,s,r,a,o,l,h,f,c,d,u,p,m=function(t,e,i,n,s){if("m"===n)throw new TypeError("Private method is not writable");if("a"===n&&!s)throw new TypeError("Private accessor was defined without a setter");if("function"==typeof e?t!==e||!s:!e.has(t))throw new TypeError("Cannot write private member to an object whose class did not declare it");return"a"===n?s.call(t,i):s?s.value=i:e.set(t,i),i},w=function(t,e,i,n){if("a"===i&&!n)throw new TypeError("Private accessor was defined without a getter");if("function"==typeof e?t!==e||!n:!e.has(t))throw new TypeError("Cannot read private member from an object whose class did not declare it");return"m"===i?n:"a"===i?n.call(t):n?n.value:e.get(t)};class y{constructor(t){if(i.add(this),n.set(this,void 0),s.set(this,[]),r.set(this,void 0),a.set(this,new Map),o.set(this,void 0),l.set(this,null),h.set(this,!1),f.set(this,void 0),!(t.container&&t.container instanceof HTMLDivElement))throw new Error("Container must be an instance of HTMLDivElement.");if(t.emptyLineText&&this.setEmptyLineText(t.emptyLineText),t.audioPlayer&&!(t.audioPlayer instanceof HTMLAudioElement))throw new Error("Audio player must be an instance of HTMLAudioElement.");t.audioPlayer&&(m(this,l,t.audioPlayer,"f"),w(this,l,"f").addEventListener("timeupdate",()=>{w(this,i,"m",p).call(this)}),w(this,l,"f").addEventListener("seeked",()=>{w(this,r,"f").querySelectorAll("p > span").forEach(t=>{t instanceof HTMLSpanElement&&(t.dataset.ariaCurrent="false")}),w(this,i,"m",p).call(this)})),t.enableAutoScroll&&m(this,h,t.enableAutoScroll,"f"),m(this,r,t.container,"f"),this.subscribe("fileSet",w(this,i,"m",u).bind(this)),this.subscribe("emptyLineTextSet",w(this,i,"m",u).bind(this)),this.subscribe("lyricLinesUpdated",w(this,i,"m",p).bind(this)),this.setFile(t.file)}get file(){return w(this,n,"f")}get emptyLineText(){return w(this,o,"f")??"..."}get noLyricsText(){return w(this,f,"f")}async setFile(t){if(t){const e=await w(this,i,"m",c).call(this,t);m(this,s,w(this,i,"m",d).call(this,e),"f")}else m(this,s,[],"f");m(this,n,t,"f"),this.notify("fileSet",{file:t})}setEmptyLineText(t){m(this,o,t,"f"),this.notify("emptyLineTextSet",{emptyLineText:t})}setNoLyricsText(t){m(this,f,t,"f"),this.notify("noLyricsTextSet",{noLyricsText:t})}subscribe(t,e){w(this,a,"f").set(t,[...w(this,a,"f").get(t)??[],e])}unsubscribe(t,e){const i=w(this,a,"f").get(t);i&&i.length>1?w(this,a,"f").set(t,[...i.filter(t=>t!==e)]):w(this,a,"f").delete(t)}notify(t,e){[...w(this,a,"f").get(t)??[]].forEach(t=>{t(e)})}}n=new WeakMap,s=new WeakMap,r=new WeakMap,a=new WeakMap,o=new WeakMap,l=new WeakMap,h=new WeakMap,f=new WeakMap,i=new WeakSet,c=async function(t){return new Promise((e,i)=>{const n=new FileReader;n.onload=t=>{"string"==typeof t.target?.result&&t.target?.result.trim().length?e(t.target.result):i(new Error("Failed to read file content."))},n.onerror=()=>i(new Error("Error reading file.")),n.readAsText(t)})},d=function(t){const i=t.split("\n").reduce((t,i)=>{const n=i.trim();return e.test(n)?[...t,n]:t},[]);if(0===i.length)throw new Error("No valid lyric lines found in the file.");return i},u=function(){const e=w(this,s,"f").map(e=>{const i=e.replace(t,""),n=e.match(t)?.[0]?.slice(1,-1);if(!n)return"";const s=n.split(":"),r=s[1].split(".");return`\n <span \n data-time="${n}" \n data-ms-time="${60*parseInt(s[0],10)*1e3+1e3*parseInt(r[0],10)+10*parseInt(r[1],10)}"\n data-empty="${0===i.length}"\n data-aria-current="false"\n >\n ${i||(w(this,o,"f")??"...")}\n </span>\n `}).join("<br/>");w(this,r,"f").innerHTML=`<p>${e.length?e:`\n <span data-no-lyrics="true">${w(this,f,"f")??""}</span>\n `}</p>`,this.notify("lyricLinesUpdated",{lyricLines:w(this,s,"f")})},p=function(){if(!w(this,l,"f")||!w(this,s,"f").length)return;const t=1e3*w(this,l,"f").currentTime,e=w(this,r,"f").querySelectorAll("p > span");let i=null;for(let n=0;n<e.length;n++){const s=e[n],r=parseInt(s.getAttribute("data-ms-time")||"0",10);if(s.dataset.ariaCurrent="false",!(t>=r))break;i=s}i&&(i.dataset.ariaCurrent="true",w(this,h,"f")&&i.scrollIntoView({behavior:"smooth",block:"center"}))};export{y as Kashi};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "kashi",
3
- "version": "2.0.0",
3
+ "version": "3.0.0",
4
4
  "description": "Singing at the top of my lungs",
5
5
  "type": "module",
6
6
  "author": "lucasmc64",