kashi 2.0.0 → 3.0.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 +89 -40
- package/kashi.d.ts +4 -6
- package/kashi.js +1 -1
- package/kashi.mjs +1 -1
- package/package.json +1 -1
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] 💪
|
|
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
|
});
|
|
@@ -101,31 +101,82 @@ Since the library was designed primarily to be used with vanilla JS, a _helper_
|
|
|
101
101
|
> **Note**: There is TypeScript support, and even if your project doesn't use the JS superset, it should help VSCode and other editors provide autocomplete/code suggestions.
|
|
102
102
|
|
|
103
103
|
```tsx
|
|
104
|
-
import { useEffect, useRef } from "react";
|
|
104
|
+
import { memo, useEffect, useRef } from "react";
|
|
105
105
|
import { Kashi, KashiProps } from "kashi";
|
|
106
106
|
|
|
107
|
+
import { api } from "@/services/axios";
|
|
108
|
+
|
|
107
109
|
// Example using Vite, React and TypeScript
|
|
108
|
-
export const KashiWrapper = (
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
110
|
+
export const KashiWrapper = memo(
|
|
111
|
+
(props: Omit<KashiProps, "container"> & { url: string }) => {
|
|
112
|
+
const ref = useRef<HTMLDivElement>(null);
|
|
113
|
+
|
|
114
|
+
useEffect(() => {
|
|
115
|
+
async function loadKashi() {
|
|
116
|
+
if (!ref.current) {
|
|
117
|
+
return;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
if (!props.url) {
|
|
121
|
+
ref.current.innerHTML = "";
|
|
122
|
+
return;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
try {
|
|
126
|
+
const { url, ...rest } = props;
|
|
127
|
+
|
|
128
|
+
const response = await api.get(url, { responseType: "blob" });
|
|
129
|
+
|
|
130
|
+
new Kashi({
|
|
131
|
+
...rest,
|
|
132
|
+
file: response.data,
|
|
133
|
+
container: ref.current,
|
|
134
|
+
});
|
|
135
|
+
} catch (error) {
|
|
136
|
+
console.error("Error loading Kashi:", error);
|
|
137
|
+
}
|
|
138
|
+
}
|
|
118
139
|
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
140
|
+
loadKashi();
|
|
141
|
+
|
|
142
|
+
return () => {
|
|
143
|
+
// Required to avoid duplication when React is in Strict Mode
|
|
144
|
+
if (ref.current) {
|
|
145
|
+
ref.current.innerHTML = "";
|
|
146
|
+
}
|
|
147
|
+
};
|
|
148
|
+
}, [ref.current, props]);
|
|
149
|
+
|
|
150
|
+
return <div className="kashi-wrapper" ref={ref} />;
|
|
151
|
+
},
|
|
152
|
+
(prevProps, nextProps) => {
|
|
153
|
+
function compareObjects(
|
|
154
|
+
obj1: Omit<KashiProps, "container">,
|
|
155
|
+
obj2: Omit<KashiProps, "container">,
|
|
156
|
+
) {
|
|
157
|
+
const keys1 = Object.keys(obj1) as Array<
|
|
158
|
+
keyof Omit<KashiProps, "container">
|
|
159
|
+
>;
|
|
160
|
+
const keys2 = Object.keys(obj2) as Array<
|
|
161
|
+
keyof Omit<KashiProps, "container">
|
|
162
|
+
>;
|
|
163
|
+
|
|
164
|
+
if (keys1.length !== keys2.length) {
|
|
165
|
+
return false;
|
|
123
166
|
}
|
|
124
|
-
};
|
|
125
|
-
}, [ref.current]);
|
|
126
167
|
|
|
127
|
-
|
|
128
|
-
|
|
168
|
+
for (const key of keys1) {
|
|
169
|
+
if (obj1[key] !== obj2[key]) {
|
|
170
|
+
return false;
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
return true;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
return compareObjects(prevProps, nextProps);
|
|
178
|
+
},
|
|
179
|
+
);
|
|
129
180
|
```
|
|
130
181
|
|
|
131
182
|
## 🧐 Constructor properties
|
|
@@ -134,12 +185,10 @@ You must pass some properties to Kashi to define what lyrics display and where.
|
|
|
134
185
|
|
|
135
186
|
| Property | Type | Default value | Is required? | Description |
|
|
136
187
|
| --------------- | ------------------ | ------------- | ------------ | ----------------------------------------- |
|
|
137
|
-
| `file` | `Blob` (or `File`) | - |
|
|
138
|
-
| `url` | `string` | - | No | Lyrics url |
|
|
188
|
+
| `file` | `Blob` (or `File`) | - | Yes | Lyrics file |
|
|
139
189
|
| `container` | `HTMLDivElement` | - | Yes | Element where the lyrics will be inserted |
|
|
140
|
-
| `emptyLineText` | `string` |
|
|
141
|
-
|
|
142
|
-
> Neither `file` nor `url` are “mandatory”, but **at least one of these properties must be specified**, otherwise an error will be thrown.
|
|
190
|
+
| `emptyLineText` | `string` | `...` | No | Custom text for empty lines of the lyrics |
|
|
191
|
+
| `noLyricsText` | `string` | - | No | Custom text for when there are no lyrics |
|
|
143
192
|
|
|
144
193
|
## 👾 Generated HTML structure
|
|
145
194
|
|
|
@@ -151,7 +200,7 @@ Here's an example:
|
|
|
151
200
|
|
|
152
201
|
```html
|
|
153
202
|
<div id="kashi">
|
|
154
|
-
<
|
|
203
|
+
<p>
|
|
155
204
|
<span data-time="00:17.55" data-ms-time="17550" data-empty="false" data-aria-current="false">
|
|
156
205
|
Telling myself, "I won't go there"
|
|
157
206
|
</span>
|
|
@@ -176,7 +225,7 @@ Here's an example:
|
|
|
176
225
|
Souls tied, intertwined by our pride and guilt
|
|
177
226
|
</span>
|
|
178
227
|
<!-- ... -->
|
|
179
|
-
</
|
|
228
|
+
</p>
|
|
180
229
|
</div>
|
|
181
230
|
```
|
|
182
231
|
|
|
@@ -186,12 +235,12 @@ The instance generated by `Kashi` has some public methods and attributes that ca
|
|
|
186
235
|
|
|
187
236
|
| Name | Type | Description |
|
|
188
237
|
| ------------------ | --------- | ------------------------------------------------------------ |
|
|
189
|
-
| `url` | Attribute | Returns the url from the current lyrics if it was fetched from a link |
|
|
190
238
|
| `file` | Attribute | Returns the file from the current lyrics |
|
|
191
|
-
| `emptyLineText` | Attribute | Returns the
|
|
192
|
-
| `
|
|
239
|
+
| `emptyLineText` | Attribute | Returns the text set for empty lines |
|
|
240
|
+
| `noLyricsText` | Attribute | Returns the text set for when there are no lyrics |
|
|
193
241
|
| `setFile` | Method | Function capable of changing the current lyrics file by passing the the new **file** |
|
|
194
242
|
| `setEmptyLineText` | Method | Function capable of changing the text defined for empty lines |
|
|
243
|
+
| `noLyricsText` | Method | Function capable of changing the text defined for when there are no lyrics |
|
|
195
244
|
| `subscribe` | Method | Function capable of defining a callback to be executed when a given event is triggered |
|
|
196
245
|
| `unsubscribe` | Method | Function capable of making a callback to stop listening to an event |
|
|
197
246
|
| `notify` | Method | Function capable of triggering an event |
|
|
@@ -200,12 +249,12 @@ The instance generated by `Kashi` has some public methods and attributes that ca
|
|
|
200
249
|
|
|
201
250
|
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
251
|
|
|
203
|
-
| Event | Data | Trigger
|
|
204
|
-
| ------------------- | --------------------------- |
|
|
205
|
-
| `
|
|
206
|
-
| `
|
|
207
|
-
| `
|
|
208
|
-
| `lyricLinesUpdated` | `{ lyricLines: string[] }` | When inserting/updating lyrics in HTML
|
|
252
|
+
| Event | Data | Trigger |
|
|
253
|
+
| ------------------- | --------------------------- | ------------------------------------------ |
|
|
254
|
+
| `fileSet` | `{ file: Blob }` | When calling the `setFile` method |
|
|
255
|
+
| `emptyLineTextSet` | `{ emptyLineText: string }` | When calling the `setEmptyLineText` method |
|
|
256
|
+
| `noLyricsText` | `{ noLyricsText: text }` | When calling the `setNoLyricsText` method |
|
|
257
|
+
| `lyricLinesUpdated` | `{ lyricLines: string[] }` | When inserting/updating lyrics in HTML |
|
|
209
258
|
|
|
210
259
|
## 🤔 How do I run the project on my machine?
|
|
211
260
|
|
package/kashi.d.ts
CHANGED
|
@@ -1,12 +1,10 @@
|
|
|
1
1
|
// Generated by dts-bundle-generator v9.5.1
|
|
2
2
|
|
|
3
|
-
export type
|
|
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
|
-
|
|
23
|
-
setFile(file:
|
|
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
|
|
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,
|
|
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};
|