kashi 3.0.2 → 4.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 +63 -51
- package/kashi.d.ts +3 -3
- package/kashi.js +1 -1
- package/kashi.mjs +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -20,9 +20,9 @@ This project is a dependency-free library that aims to provide a way to correctl
|
|
|
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
|
|
23
|
-
- [
|
|
24
|
-
- [ ] 🧞 Supports the [Walaoke extension](https://en.wikipedia.org/wiki/LRC_(file_format)#Walaoke_extension)
|
|
25
|
-
- [ ] 🕖️ Supports the [A2 extension](https://en.wikipedia.org/wiki/LRC_(file_format)#A2_extension_(Enhanced_LRC_format))
|
|
23
|
+
- [x] 🎩 Supports multiple lyrics for the same song (useful to keep track of the original lyrics and their translation)
|
|
24
|
+
- [ ] 🧞 Supports the [Walaoke extension](<https://en.wikipedia.org/wiki/LRC_(file_format)#Walaoke_extension>)
|
|
25
|
+
- [ ] 🕖️ Supports the [A2 extension](<https://en.wikipedia.org/wiki/LRC_(file_format)#A2_extension_(Enhanced_LRC_format)>)
|
|
26
26
|
|
|
27
27
|
> **Note:** Support for fetching lyrics using a URL has been removed because it only supported public URLs.
|
|
28
28
|
>
|
|
@@ -58,7 +58,7 @@ The project is also exported using [UMD](https://github.com/umdjs/umd), which me
|
|
|
58
58
|
"use strict";
|
|
59
59
|
|
|
60
60
|
new Kashi({
|
|
61
|
-
|
|
61
|
+
files. // Loaded from some input[type="file"] or anywhere else
|
|
62
62
|
container: document.getElementById("kashi"),
|
|
63
63
|
});
|
|
64
64
|
```
|
|
@@ -83,7 +83,7 @@ import { Kashi } from "kashi";
|
|
|
83
83
|
// Usage is essentially the same as in the previous section
|
|
84
84
|
|
|
85
85
|
new Kashi({
|
|
86
|
-
|
|
86
|
+
files. // Loaded from some input[type="file"] or anywhere else
|
|
87
87
|
container: document.getElementById("kashi"),
|
|
88
88
|
});
|
|
89
89
|
```
|
|
@@ -129,7 +129,7 @@ export const KashiWrapper = memo(
|
|
|
129
129
|
|
|
130
130
|
new Kashi({
|
|
131
131
|
...rest,
|
|
132
|
-
|
|
132
|
+
files: [response.data],
|
|
133
133
|
container: ref.current,
|
|
134
134
|
});
|
|
135
135
|
} catch (error) {
|
|
@@ -150,6 +150,14 @@ export const KashiWrapper = memo(
|
|
|
150
150
|
return <div className="kashi-wrapper" ref={ref} />;
|
|
151
151
|
},
|
|
152
152
|
(prevProps, nextProps) => {
|
|
153
|
+
function compareArrays(arr1: unknown[], arr2: unknown[]) {
|
|
154
|
+
if (arr1.length !== arr2.length) {
|
|
155
|
+
return false;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
return arr1.every((item, index) => item === arr2[index]);
|
|
159
|
+
}
|
|
160
|
+
|
|
153
161
|
function compareObjects(
|
|
154
162
|
obj1: Omit<KashiProps, "container">,
|
|
155
163
|
obj2: Omit<KashiProps, "container">,
|
|
@@ -166,7 +174,14 @@ export const KashiWrapper = memo(
|
|
|
166
174
|
}
|
|
167
175
|
|
|
168
176
|
for (const key of keys1) {
|
|
169
|
-
|
|
177
|
+
const val1 = obj1[key];
|
|
178
|
+
const val2 = obj2[key];
|
|
179
|
+
|
|
180
|
+
if (Array.isArray(val1) && Array.isArray(val2)) {
|
|
181
|
+
if (!compareArrays(val1, val2)) {
|
|
182
|
+
return false;
|
|
183
|
+
}
|
|
184
|
+
} else if (val1 !== val2) {
|
|
170
185
|
return false;
|
|
171
186
|
}
|
|
172
187
|
}
|
|
@@ -183,49 +198,46 @@ export const KashiWrapper = memo(
|
|
|
183
198
|
|
|
184
199
|
You must pass some properties to Kashi to define what lyrics display and where. Here are its specifications:
|
|
185
200
|
|
|
186
|
-
| Property | Type
|
|
187
|
-
| --------------- |
|
|
188
|
-
| `
|
|
189
|
-
| `container` | `HTMLDivElement`
|
|
190
|
-
| `emptyLineText` | `string`
|
|
191
|
-
| `noLyricsText` | `string`
|
|
201
|
+
| Property | Type | Default value | Is required? | Description |
|
|
202
|
+
| --------------- | ---------------- | ------------- | ------------ | ----------------------------------------- |
|
|
203
|
+
| `files` | `Blob[]` | - | Yes | Lyrics files |
|
|
204
|
+
| `container` | `HTMLDivElement` | - | Yes | Element where the lyrics will be inserted |
|
|
205
|
+
| `emptyLineText` | `string` | `...` | No | Custom text for empty lines of the lyrics |
|
|
206
|
+
| `noLyricsText` | `string` | - | No | Custom text for when there are no lyrics |
|
|
192
207
|
|
|
193
208
|
## 👾 Generated HTML structure
|
|
194
209
|
|
|
195
210
|
The `div#kashi` represents the `container` passed to `Kashi` where the song lyrics will be inserted.
|
|
196
211
|
|
|
197
|
-
Each line of lyrics present in the lrc
|
|
212
|
+
Each line of lyrics present in the lrc files will be wrapped by a `<div></div>` tag and inserted into the `container`.
|
|
198
213
|
|
|
199
214
|
Here's an example:
|
|
200
215
|
|
|
201
216
|
```html
|
|
202
217
|
<div id="kashi">
|
|
203
|
-
<
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
<
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
<span
|
|
221
|
-
|
|
222
|
-
</span>
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
</span>
|
|
227
|
-
<!-- ... -->
|
|
228
|
-
</p>
|
|
218
|
+
<div
|
|
219
|
+
data-time="00:17.55"
|
|
220
|
+
data-ms-time="17550"
|
|
221
|
+
data-empty="false"
|
|
222
|
+
data-aria-current="false"
|
|
223
|
+
>
|
|
224
|
+
<span>Telling myself, "I won't go there"</span>
|
|
225
|
+
<br />
|
|
226
|
+
<span> Dizendo a mim mesmo, "eu não vou lá" </span>
|
|
227
|
+
</div>
|
|
228
|
+
|
|
229
|
+
<div
|
|
230
|
+
data-time="00:21.24"
|
|
231
|
+
data-ms-time="21240"
|
|
232
|
+
data-empty="false"
|
|
233
|
+
data-aria-current="false"
|
|
234
|
+
>
|
|
235
|
+
<span>Oh, but I know that I won't care</span>
|
|
236
|
+
<br />
|
|
237
|
+
<span>Oh, mas eu sei que não vou me importar</span>
|
|
238
|
+
</div>
|
|
239
|
+
|
|
240
|
+
<!-- ... -->
|
|
229
241
|
</div>
|
|
230
242
|
```
|
|
231
243
|
|
|
@@ -233,17 +245,17 @@ Here's an example:
|
|
|
233
245
|
|
|
234
246
|
The instance generated by `Kashi` has some public methods and attributes that can be used to query or change properties on the fly.
|
|
235
247
|
|
|
236
|
-
| Name | Type | Description
|
|
237
|
-
| ------------------ | --------- |
|
|
238
|
-
| `
|
|
239
|
-
| `emptyLineText` | Attribute | Returns the text set for empty lines
|
|
240
|
-
| `noLyricsText` | Attribute | Returns the text set for when there are no lyrics
|
|
241
|
-
| `
|
|
242
|
-
| `setEmptyLineText` | Method | Function capable of changing the text defined for empty lines
|
|
243
|
-
| `setNoLyricsText` | Method | Function capable of changing the text defined for when there are no lyrics
|
|
248
|
+
| Name | Type | Description |
|
|
249
|
+
| ------------------ | --------- | -------------------------------------------------------------------------------------- |
|
|
250
|
+
| `files` | Attribute | Returns the files from the current lyrics |
|
|
251
|
+
| `emptyLineText` | Attribute | Returns the text set for empty lines |
|
|
252
|
+
| `noLyricsText` | Attribute | Returns the text set for when there are no lyrics |
|
|
253
|
+
| `setFiles` | Method | Function capable of changing the current lyrics files by passing the the new **files** |
|
|
254
|
+
| `setEmptyLineText` | Method | Function capable of changing the text defined for empty lines |
|
|
255
|
+
| `setNoLyricsText` | Method | Function capable of changing the text defined for when there are no lyrics |
|
|
244
256
|
| `subscribe` | Method | Function capable of defining a callback to be executed when a given event is triggered |
|
|
245
|
-
| `unsubscribe` | Method | Function capable of making a callback to stop listening to an event
|
|
246
|
-
| `notify` | Method | Function capable of triggering an event
|
|
257
|
+
| `unsubscribe` | Method | Function capable of making a callback to stop listening to an event |
|
|
258
|
+
| `notify` | Method | Function capable of triggering an event |
|
|
247
259
|
|
|
248
260
|
## 🍾 Events
|
|
249
261
|
|
|
@@ -251,7 +263,7 @@ When creating a new instance using `Kashi` you will have access to the `subscrib
|
|
|
251
263
|
|
|
252
264
|
| Event | Data | Trigger |
|
|
253
265
|
| ------------------- | --------------------------- | ------------------------------------------ |
|
|
254
|
-
| `
|
|
266
|
+
| `filesSet` | `{ files: Blob[] }` | When calling the `setFiles` method |
|
|
255
267
|
| `emptyLineTextSet` | `{ emptyLineText: string }` | When calling the `setEmptyLineText` method |
|
|
256
268
|
| `noLyricsTextSet` | `{ noLyricsText: text }` | When calling the `setNoLyricsText` method |
|
|
257
269
|
| `lyricLinesUpdated` | `{ lyricLines: string[] }` | When inserting/updating lyrics in HTML |
|
package/kashi.d.ts
CHANGED
|
@@ -5,7 +5,7 @@ export type EventName = string;
|
|
|
5
5
|
export type EventData = any;
|
|
6
6
|
export type EventCallback = (data: EventData) => void;
|
|
7
7
|
export interface KashiProps {
|
|
8
|
-
|
|
8
|
+
files?: KashiFile[];
|
|
9
9
|
container: HTMLDivElement;
|
|
10
10
|
emptyLineText?: string;
|
|
11
11
|
audioPlayer?: HTMLAudioElement;
|
|
@@ -14,10 +14,10 @@ export interface KashiProps {
|
|
|
14
14
|
export declare class Kashi {
|
|
15
15
|
#private;
|
|
16
16
|
constructor(props: KashiProps);
|
|
17
|
-
get
|
|
17
|
+
get files(): KashiFile[];
|
|
18
18
|
get emptyLineText(): string;
|
|
19
19
|
get noLyricsText(): string;
|
|
20
|
-
|
|
20
|
+
setFiles(files?: KashiFile[]): Promise<void>;
|
|
21
21
|
setEmptyLineText(text: string): void;
|
|
22
22
|
setNoLyricsText(text: string): void;
|
|
23
23
|
subscribe(event: EventName, fn: EventCallback): 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 i=t();for(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 s in i)("object"==typeof exports?exports:e)[s]=i[s]}}(self,()=>(()=>{"use strict";var e={d:(t,i)=>{for(var s in i)e.o(i,s)&&!e.o(t,s)&&Object.defineProperty(t,s,{enumerable:!0,get:i[s]})},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:()=>v});const i=/\[\d{2}:\d{2}.\d{2}\]/,s=/^\[\d{2}:\d{2}.\d{2}\](.*)$/;var n,r,a,o,l,f,h,c,d,u,m,p,y,w=function(e,t,i,s,n){if("m"===s)throw new TypeError("Private method is not writable");if("a"===s&&!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"===s?n.call(e,i):n?n.value=i:t.set(e,i),i},b=function(e,t,i,s){if("a"===i&&!s)throw new TypeError("Private accessor was defined without a getter");if("function"==typeof t?e!==t||!s:!t.has(e))throw new TypeError("Cannot read private member from an object whose class did not declare it");return"m"===i?s:"a"===i?s.call(e):s?s.value:t.get(e)};class v{constructor(e){if(n.add(this),r.set(this,[]),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,n,"m",y).call(this)}),b(this,h,"f").addEventListener("seeked",()=>{b(this,o,"f").querySelectorAll("div[data-ms-time]").forEach(e=>{e instanceof HTMLDivElement&&(e.dataset.ariaCurrent="false")}),b(this,n,"m",y).call(this)})),e.enableAutoScroll&&w(this,c,e.enableAutoScroll,"f"),w(this,o,e.container,"f"),this.subscribe("filesSet",b(this,n,"m",p).bind(this)),this.subscribe("emptyLineTextSet",b(this,n,"m",p).bind(this)),this.subscribe("lyricsLinesUpdated",b(this,n,"m",y).bind(this)),this.setFiles(e.files)}get files(){return b(this,r,"f")}get emptyLineText(){return b(this,f,"f")??"..."}get noLyricsText(){return b(this,d,"f")}async setFiles(e=[]){if(e.length>0){const t=e.map(e=>e?b(this,n,"m",u).call(this,e):Promise.resolve("")),i=await Promise.all(t);w(this,a,i.map(e=>b(this,n,"m",m).call(this,e)),"f")}else w(this,a,[],"f");w(this,r,e,"f"),this.notify("filesSet",{files: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 r=new WeakMap,a=new WeakMap,o=new WeakMap,l=new WeakMap,f=new WeakMap,h=new WeakMap,c=new WeakMap,d=new WeakMap,n=new WeakSet,u=async function(e){return new Promise((t,i)=>{const s=new FileReader;s.onload=e=>{"string"==typeof e.target?.result&&e.target?.result.trim().length?t(e.target.result):i(new Error("Failed to read file content."))},s.onerror=()=>i(new Error("Error reading file.")),s.readAsText(e)})},m=function(e){const t=e.split("\n").reduce((e,t)=>{const i=t.trim();return s.test(i)?[...e,i]:e},[]);if(0===t.length)throw new Error("No valid lyric lines found in the file.");return t},p=function(){const e=b(this,a,"f").length,t=b(this,a,"f")[0]?.length??0;if(!b(this,a,"f").every(e=>e.length===t))throw new Error("All lyric files must have the same number of lines.");let s="";for(let n=0;n<t;n++){const t=[],r=[];for(let s=0;s<e;s++){const e=b(this,a,"f")[s][n],o=e.replace(i,"");r.push(o);const l=e.match(i)?.[0]?.slice(1,-1);t.push(l)}if(new Set(t).size>1)throw new Error(`Timestamp mismatch at line ${n+1} across files}`);const o=t[0],l=o.split(":"),h=l[1].split("."),c=60*parseInt(l[0],10)*1e3+1e3*parseInt(h[0],10)+10*parseInt(h[1],10),d=r.every(e=>0===e.length);s+=`\n <div\n data-time="${o}"\n data-ms-time="${c}"\n data-empty="${d}"\n data-aria-current="false"\n >\n ${(d?"":r.map(e=>`<span>${e}</span>`).join("<br/>"))||(b(this,f,"f")??"...")}\n </div>\n `}b(this,o,"f").innerHTML=s.length?s:`\n <div>\n <span data-no-lyrics="true">\n ${b(this,d,"f")??""}\n </span>\n </div>\n `,this.notify("lyricsLinesUpdated",{lyricsLines:b(this,a,"f")})},y=function(){if(!b(this,h,"f")||!b(this,a,"f").length||b(this,a,"f").some(e=>0===e.length))return;const e=1e3*b(this,h,"f").currentTime,t=b(this,o,"f").querySelectorAll("div[data-ms-time]");let i=null;for(let s=0;s<t.length;s++){const n=t[s],r=parseInt(n.getAttribute("data-ms-time")||"0",10);if(n.dataset.ariaCurrent="false",!(e>=r))break;i=n}i&&(i.dataset.ariaCurrent="true",b(this,c,"f")&&i.scrollIntoView({behavior:"smooth",block:"center"}))},t})());
|
package/kashi.mjs
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
const
|
|
1
|
+
const e=/\[\d{2}:\d{2}.\d{2}\]/,t=/^\[\d{2}:\d{2}.\d{2}\](.*)$/;var i,s,n,r,a,o,l,h,f,c,d,u,m,p=function(e,t,i,s,n){if("m"===s)throw new TypeError("Private method is not writable");if("a"===s&&!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"===s?n.call(e,i):n?n.value=i:t.set(e,i),i},w=function(e,t,i,s){if("a"===i&&!s)throw new TypeError("Private accessor was defined without a getter");if("function"==typeof t?e!==t||!s:!t.has(e))throw new TypeError("Cannot read private member from an object whose class did not declare it");return"m"===i?s:"a"===i?s.call(e):s?s.value:t.get(e)};class y{constructor(e){if(i.add(this),s.set(this,[]),n.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),!(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&&(p(this,l,e.audioPlayer,"f"),w(this,l,"f").addEventListener("timeupdate",()=>{w(this,i,"m",m).call(this)}),w(this,l,"f").addEventListener("seeked",()=>{w(this,r,"f").querySelectorAll("div[data-ms-time]").forEach(e=>{e instanceof HTMLDivElement&&(e.dataset.ariaCurrent="false")}),w(this,i,"m",m).call(this)})),e.enableAutoScroll&&p(this,h,e.enableAutoScroll,"f"),p(this,r,e.container,"f"),this.subscribe("filesSet",w(this,i,"m",u).bind(this)),this.subscribe("emptyLineTextSet",w(this,i,"m",u).bind(this)),this.subscribe("lyricsLinesUpdated",w(this,i,"m",m).bind(this)),this.setFiles(e.files)}get files(){return w(this,s,"f")}get emptyLineText(){return w(this,o,"f")??"..."}get noLyricsText(){return w(this,f,"f")}async setFiles(e=[]){if(e.length>0){const t=e.map(e=>e?w(this,i,"m",c).call(this,e):Promise.resolve("")),s=await Promise.all(t);p(this,n,s.map(e=>w(this,i,"m",d).call(this,e)),"f")}else p(this,n,[],"f");p(this,s,e,"f"),this.notify("filesSet",{files:e})}setEmptyLineText(e){p(this,o,e,"f"),this.notify("emptyLineTextSet",{emptyLineText:e})}setNoLyricsText(e){p(this,f,e,"f"),this.notify("noLyricsTextSet",{noLyricsText:e})}subscribe(e,t){w(this,a,"f").set(e,[...w(this,a,"f").get(e)??[],t])}unsubscribe(e,t){const i=w(this,a,"f").get(e);i&&i.length>1?w(this,a,"f").set(e,[...i.filter(e=>e!==t)]):w(this,a,"f").delete(e)}notify(e,t){[...w(this,a,"f").get(e)??[]].forEach(e=>{e(t)})}}s=new WeakMap,n=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(e){return new Promise((t,i)=>{const s=new FileReader;s.onload=e=>{"string"==typeof e.target?.result&&e.target?.result.trim().length?t(e.target.result):i(new Error("Failed to read file content."))},s.onerror=()=>i(new Error("Error reading file.")),s.readAsText(e)})},d=function(e){const i=e.split("\n").reduce((e,i)=>{const s=i.trim();return t.test(s)?[...e,s]:e},[]);if(0===i.length)throw new Error("No valid lyric lines found in the file.");return i},u=function(){const t=w(this,n,"f").length,i=w(this,n,"f")[0]?.length??0;if(!w(this,n,"f").every(e=>e.length===i))throw new Error("All lyric files must have the same number of lines.");let s="";for(let r=0;r<i;r++){const i=[],a=[];for(let s=0;s<t;s++){const t=w(this,n,"f")[s][r],o=t.replace(e,"");a.push(o);const l=t.match(e)?.[0]?.slice(1,-1);i.push(l)}if(new Set(i).size>1)throw new Error(`Timestamp mismatch at line ${r+1} across files}`);const l=i[0],h=l.split(":"),f=h[1].split("."),c=60*parseInt(h[0],10)*1e3+1e3*parseInt(f[0],10)+10*parseInt(f[1],10),d=a.every(e=>0===e.length);s+=`\n <div\n data-time="${l}"\n data-ms-time="${c}"\n data-empty="${d}"\n data-aria-current="false"\n >\n ${(d?"":a.map(e=>`<span>${e}</span>`).join("<br/>"))||(w(this,o,"f")??"...")}\n </div>\n `}w(this,r,"f").innerHTML=s.length?s:`\n <div>\n <span data-no-lyrics="true">\n ${w(this,f,"f")??""}\n </span>\n </div>\n `,this.notify("lyricsLinesUpdated",{lyricsLines:w(this,n,"f")})},m=function(){if(!w(this,l,"f")||!w(this,n,"f").length||w(this,n,"f").some(e=>0===e.length))return;const e=1e3*w(this,l,"f").currentTime,t=w(this,r,"f").querySelectorAll("div[data-ms-time]");let i=null;for(let s=0;s<t.length;s++){const n=t[s],r=parseInt(n.getAttribute("data-ms-time")||"0",10);if(n.dataset.ariaCurrent="false",!(e>=r))break;i=n}i&&(i.dataset.ariaCurrent="true",w(this,h,"f")&&i.scrollIntoView({behavior:"smooth",block:"center"}))};export{y as Kashi};
|