vidply 1.0.3 → 1.0.4
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/dist/vidply.css +24 -0
- package/dist/vidply.esm.js +217 -34
- package/dist/vidply.esm.js.map +2 -2
- package/dist/vidply.esm.min.js +7 -6
- package/dist/vidply.esm.min.meta.json +7 -7
- package/dist/vidply.js +217 -34
- package/dist/vidply.js.map +2 -2
- package/dist/vidply.min.css +1 -1
- package/dist/vidply.min.js +7 -6
- package/dist/vidply.min.meta.json +7 -7
- package/package.json +57 -54
- package/src/controls/ControlBar.js +2026 -1988
- package/src/core/Player.js +36 -0
- package/src/features/PlaylistManager.js +611 -437
- package/src/styles/vidply.css +24 -0
package/dist/vidply.css
CHANGED
|
@@ -1393,6 +1393,8 @@
|
|
|
1393
1393
|
}
|
|
1394
1394
|
|
|
1395
1395
|
.vidply-playlist-list {
|
|
1396
|
+
list-style: none;
|
|
1397
|
+
margin: 0;
|
|
1396
1398
|
padding: 4px 0;
|
|
1397
1399
|
}
|
|
1398
1400
|
|
|
@@ -1405,6 +1407,7 @@
|
|
|
1405
1407
|
display: flex;
|
|
1406
1408
|
gap: 12px;
|
|
1407
1409
|
padding: 12px 16px;
|
|
1410
|
+
position: relative;
|
|
1408
1411
|
transition: all 0.2s ease;
|
|
1409
1412
|
}
|
|
1410
1413
|
|
|
@@ -1416,9 +1419,23 @@
|
|
|
1416
1419
|
.vidply-playlist-item:focus {
|
|
1417
1420
|
background: var(--vidply-white-08);
|
|
1418
1421
|
border-left-color: var(--vidply-primary);
|
|
1422
|
+
outline: 2px solid var(--vidply-primary-light);
|
|
1423
|
+
outline-offset: -2px;
|
|
1424
|
+
z-index: 1;
|
|
1425
|
+
}
|
|
1426
|
+
|
|
1427
|
+
.vidply-playlist-item:focus:not(:focus-visible) {
|
|
1419
1428
|
outline: none;
|
|
1420
1429
|
}
|
|
1421
1430
|
|
|
1431
|
+
.vidply-playlist-item:focus-visible {
|
|
1432
|
+
background: var(--vidply-white-08);
|
|
1433
|
+
border-left-color: var(--vidply-primary);
|
|
1434
|
+
outline: 2px solid var(--vidply-primary-light);
|
|
1435
|
+
outline-offset: -2px;
|
|
1436
|
+
z-index: 1;
|
|
1437
|
+
}
|
|
1438
|
+
|
|
1422
1439
|
.vidply-playlist-item-active {
|
|
1423
1440
|
background: var(--vidply-primary-15);
|
|
1424
1441
|
border-left-color: var(--vidply-primary);
|
|
@@ -1428,6 +1445,13 @@
|
|
|
1428
1445
|
background: var(--vidply-primary-20);
|
|
1429
1446
|
}
|
|
1430
1447
|
|
|
1448
|
+
.vidply-playlist-item-active:focus,
|
|
1449
|
+
.vidply-playlist-item-active:focus-visible {
|
|
1450
|
+
background: var(--vidply-primary-20);
|
|
1451
|
+
outline: 2px solid var(--vidply-primary-light);
|
|
1452
|
+
outline-offset: -2px;
|
|
1453
|
+
}
|
|
1454
|
+
|
|
1431
1455
|
/* Playlist Thumbnail */
|
|
1432
1456
|
.vidply-playlist-thumbnail {
|
|
1433
1457
|
align-items: center;
|
package/dist/vidply.esm.js
CHANGED
|
@@ -1651,12 +1651,26 @@ var ControlBar = class {
|
|
|
1651
1651
|
}
|
|
1652
1652
|
createTimeDisplay() {
|
|
1653
1653
|
const container = DOMUtils.createElement("div", {
|
|
1654
|
-
className: `${this.player.options.classPrefix}-time
|
|
1654
|
+
className: `${this.player.options.classPrefix}-time`,
|
|
1655
|
+
attributes: {
|
|
1656
|
+
"role": "group",
|
|
1657
|
+
"aria-label": "Time display"
|
|
1658
|
+
}
|
|
1655
1659
|
});
|
|
1656
1660
|
this.controls.currentTimeDisplay = DOMUtils.createElement("span", {
|
|
1657
1661
|
className: `${this.player.options.classPrefix}-current-time`,
|
|
1658
|
-
|
|
1662
|
+
attributes: {
|
|
1663
|
+
"aria-label": "0 seconds"
|
|
1664
|
+
}
|
|
1665
|
+
});
|
|
1666
|
+
const currentTimeVisual = DOMUtils.createElement("span", {
|
|
1667
|
+
textContent: "00:00",
|
|
1668
|
+
attributes: {
|
|
1669
|
+
"aria-hidden": "true"
|
|
1670
|
+
}
|
|
1659
1671
|
});
|
|
1672
|
+
this.controls.currentTimeDisplay.appendChild(currentTimeVisual);
|
|
1673
|
+
this.controls.currentTimeVisual = currentTimeVisual;
|
|
1660
1674
|
const separator = DOMUtils.createElement("span", {
|
|
1661
1675
|
textContent: " / ",
|
|
1662
1676
|
attributes: {
|
|
@@ -1665,8 +1679,18 @@ var ControlBar = class {
|
|
|
1665
1679
|
});
|
|
1666
1680
|
this.controls.durationDisplay = DOMUtils.createElement("span", {
|
|
1667
1681
|
className: `${this.player.options.classPrefix}-duration`,
|
|
1668
|
-
|
|
1682
|
+
attributes: {
|
|
1683
|
+
"aria-label": "Duration: 0 seconds"
|
|
1684
|
+
}
|
|
1685
|
+
});
|
|
1686
|
+
const durationVisual = DOMUtils.createElement("span", {
|
|
1687
|
+
textContent: "00:00",
|
|
1688
|
+
attributes: {
|
|
1689
|
+
"aria-hidden": "true"
|
|
1690
|
+
}
|
|
1669
1691
|
});
|
|
1692
|
+
this.controls.durationDisplay.appendChild(durationVisual);
|
|
1693
|
+
this.controls.durationVisual = durationVisual;
|
|
1670
1694
|
container.appendChild(this.controls.currentTimeDisplay);
|
|
1671
1695
|
container.appendChild(separator);
|
|
1672
1696
|
container.appendChild(this.controls.durationDisplay);
|
|
@@ -2490,13 +2514,17 @@ var ControlBar = class {
|
|
|
2490
2514
|
const percent = this.player.state.currentTime / this.player.state.duration * 100;
|
|
2491
2515
|
this.controls.played.style.width = `${percent}%`;
|
|
2492
2516
|
this.controls.progress.setAttribute("aria-valuenow", String(Math.round(percent)));
|
|
2493
|
-
if (this.controls.
|
|
2494
|
-
|
|
2517
|
+
if (this.controls.currentTimeVisual) {
|
|
2518
|
+
const currentTime = this.player.state.currentTime;
|
|
2519
|
+
this.controls.currentTimeVisual.textContent = TimeUtils.formatTime(currentTime);
|
|
2520
|
+
this.controls.currentTimeDisplay.setAttribute("aria-label", TimeUtils.formatDuration(currentTime));
|
|
2495
2521
|
}
|
|
2496
2522
|
}
|
|
2497
2523
|
updateDuration() {
|
|
2498
|
-
if (this.controls.
|
|
2499
|
-
|
|
2524
|
+
if (this.controls.durationVisual) {
|
|
2525
|
+
const duration = this.player.state.duration;
|
|
2526
|
+
this.controls.durationVisual.textContent = TimeUtils.formatTime(duration);
|
|
2527
|
+
this.controls.durationDisplay.setAttribute("aria-label", "Duration: " + TimeUtils.formatDuration(duration));
|
|
2500
2528
|
}
|
|
2501
2529
|
}
|
|
2502
2530
|
updateVolumeDisplay() {
|
|
@@ -4558,6 +4586,17 @@ var Player = class extends EventEmitter {
|
|
|
4558
4586
|
this.captionManager.destroy();
|
|
4559
4587
|
this.captionManager = new CaptionManager(this);
|
|
4560
4588
|
}
|
|
4589
|
+
if (this.transcriptManager) {
|
|
4590
|
+
const wasVisible = this.transcriptManager.isVisible;
|
|
4591
|
+
this.transcriptManager.destroy();
|
|
4592
|
+
this.transcriptManager = new TranscriptManager(this);
|
|
4593
|
+
if (wasVisible) {
|
|
4594
|
+
this.transcriptManager.showTranscript();
|
|
4595
|
+
}
|
|
4596
|
+
}
|
|
4597
|
+
if (this.controlBar) {
|
|
4598
|
+
this.updateControlBar();
|
|
4599
|
+
}
|
|
4561
4600
|
this.emit("sourcechange", config);
|
|
4562
4601
|
this.log("Media loaded successfully");
|
|
4563
4602
|
} catch (error) {
|
|
@@ -4569,6 +4608,17 @@ var Player = class extends EventEmitter {
|
|
|
4569
4608
|
* @param {string} src - New source URL
|
|
4570
4609
|
* @returns {boolean}
|
|
4571
4610
|
*/
|
|
4611
|
+
/**
|
|
4612
|
+
* Update control bar to refresh button visibility based on available features
|
|
4613
|
+
*/
|
|
4614
|
+
updateControlBar() {
|
|
4615
|
+
if (!this.controlBar) return;
|
|
4616
|
+
const controlBar = this.controlBar;
|
|
4617
|
+
controlBar.element.innerHTML = "";
|
|
4618
|
+
controlBar.createControls();
|
|
4619
|
+
controlBar.attachEvents();
|
|
4620
|
+
controlBar.setupAutoHide();
|
|
4621
|
+
}
|
|
4572
4622
|
shouldChangeRenderer(src) {
|
|
4573
4623
|
if (!this.renderer) return true;
|
|
4574
4624
|
const isYouTube = src.includes("youtube.com") || src.includes("youtu.be");
|
|
@@ -5028,7 +5078,9 @@ var PlaylistManager = class {
|
|
|
5028
5078
|
this.trackInfoElement = null;
|
|
5029
5079
|
this.handleTrackEnd = this.handleTrackEnd.bind(this);
|
|
5030
5080
|
this.handleTrackError = this.handleTrackError.bind(this);
|
|
5081
|
+
this.player.playlistManager = this;
|
|
5031
5082
|
this.init();
|
|
5083
|
+
this.updatePlayerControls();
|
|
5032
5084
|
}
|
|
5033
5085
|
init() {
|
|
5034
5086
|
this.player.on("ended", this.handleTrackEnd);
|
|
@@ -5037,6 +5089,17 @@ var PlaylistManager = class {
|
|
|
5037
5089
|
this.createUI();
|
|
5038
5090
|
}
|
|
5039
5091
|
}
|
|
5092
|
+
/**
|
|
5093
|
+
* Update player controls to add playlist navigation buttons
|
|
5094
|
+
*/
|
|
5095
|
+
updatePlayerControls() {
|
|
5096
|
+
if (!this.player.controlBar) return;
|
|
5097
|
+
const controlBar = this.player.controlBar;
|
|
5098
|
+
controlBar.element.innerHTML = "";
|
|
5099
|
+
controlBar.createControls();
|
|
5100
|
+
controlBar.attachEvents();
|
|
5101
|
+
controlBar.setupAutoHide();
|
|
5102
|
+
}
|
|
5040
5103
|
/**
|
|
5041
5104
|
* Load a playlist
|
|
5042
5105
|
* @param {Array} tracks - Array of track objects
|
|
@@ -5057,8 +5120,9 @@ var PlaylistManager = class {
|
|
|
5057
5120
|
/**
|
|
5058
5121
|
* Play a specific track
|
|
5059
5122
|
* @param {number} index - Track index
|
|
5123
|
+
* @param {boolean} userInitiated - Whether this was triggered by user action (default: false)
|
|
5060
5124
|
*/
|
|
5061
|
-
play(index) {
|
|
5125
|
+
play(index, userInitiated = false) {
|
|
5062
5126
|
if (index < 0 || index >= this.tracks.length) {
|
|
5063
5127
|
console.warn("VidPly Playlist: Invalid track index", index);
|
|
5064
5128
|
return;
|
|
@@ -5078,6 +5142,9 @@ var PlaylistManager = class {
|
|
|
5078
5142
|
item: track,
|
|
5079
5143
|
total: this.tracks.length
|
|
5080
5144
|
});
|
|
5145
|
+
if (userInitiated && this.player.container) {
|
|
5146
|
+
this.player.container.focus();
|
|
5147
|
+
}
|
|
5081
5148
|
setTimeout(() => {
|
|
5082
5149
|
this.player.play();
|
|
5083
5150
|
}, 100);
|
|
@@ -5139,12 +5206,17 @@ var PlaylistManager = class {
|
|
|
5139
5206
|
return;
|
|
5140
5207
|
}
|
|
5141
5208
|
this.trackInfoElement = DOMUtils.createElement("div", {
|
|
5142
|
-
className: "vidply-track-info"
|
|
5209
|
+
className: "vidply-track-info",
|
|
5210
|
+
role: "status",
|
|
5211
|
+
"aria-live": "polite",
|
|
5212
|
+
"aria-atomic": "true"
|
|
5143
5213
|
});
|
|
5144
5214
|
this.trackInfoElement.style.display = "none";
|
|
5145
5215
|
this.container.appendChild(this.trackInfoElement);
|
|
5146
5216
|
this.playlistPanel = DOMUtils.createElement("div", {
|
|
5147
|
-
className: "vidply-playlist-panel"
|
|
5217
|
+
className: "vidply-playlist-panel",
|
|
5218
|
+
role: "region",
|
|
5219
|
+
"aria-label": "Media playlist"
|
|
5148
5220
|
});
|
|
5149
5221
|
this.playlistPanel.style.display = "none";
|
|
5150
5222
|
this.container.appendChild(this.playlistPanel);
|
|
@@ -5156,10 +5228,14 @@ var PlaylistManager = class {
|
|
|
5156
5228
|
if (!this.trackInfoElement) return;
|
|
5157
5229
|
const trackNumber = this.currentIndex + 1;
|
|
5158
5230
|
const totalTracks = this.tracks.length;
|
|
5231
|
+
const trackTitle = track.title || "Untitled";
|
|
5232
|
+
const trackArtist = track.artist || "";
|
|
5233
|
+
const announcement = `Now playing: Track ${trackNumber} of ${totalTracks}. ${trackTitle}${trackArtist ? " by " + trackArtist : ""}`;
|
|
5159
5234
|
this.trackInfoElement.innerHTML = `
|
|
5160
|
-
<
|
|
5161
|
-
<div class="vidply-track-
|
|
5162
|
-
|
|
5235
|
+
<span class="vidply-sr-only">${DOMUtils.escapeHTML(announcement)}</span>
|
|
5236
|
+
<div class="vidply-track-number" aria-hidden="true">Track ${trackNumber} of ${totalTracks}</div>
|
|
5237
|
+
<div class="vidply-track-title" aria-hidden="true">${DOMUtils.escapeHTML(trackTitle)}</div>
|
|
5238
|
+
${trackArtist ? `<div class="vidply-track-artist" aria-hidden="true">${DOMUtils.escapeHTML(trackArtist)}</div>` : ""}
|
|
5163
5239
|
`;
|
|
5164
5240
|
this.trackInfoElement.style.display = "block";
|
|
5165
5241
|
}
|
|
@@ -5169,14 +5245,29 @@ var PlaylistManager = class {
|
|
|
5169
5245
|
renderPlaylist() {
|
|
5170
5246
|
if (!this.playlistPanel) return;
|
|
5171
5247
|
this.playlistPanel.innerHTML = "";
|
|
5172
|
-
const header = DOMUtils.createElement("
|
|
5173
|
-
className: "vidply-playlist-header"
|
|
5248
|
+
const header = DOMUtils.createElement("h2", {
|
|
5249
|
+
className: "vidply-playlist-header",
|
|
5250
|
+
id: "vidply-playlist-heading"
|
|
5174
5251
|
});
|
|
5175
5252
|
header.textContent = `Playlist (${this.tracks.length})`;
|
|
5176
5253
|
this.playlistPanel.appendChild(header);
|
|
5177
|
-
const
|
|
5178
|
-
className: "vidply-
|
|
5179
|
-
|
|
5254
|
+
const instructions = DOMUtils.createElement("div", {
|
|
5255
|
+
className: "vidply-sr-only",
|
|
5256
|
+
"aria-hidden": "false"
|
|
5257
|
+
});
|
|
5258
|
+
instructions.textContent = "Use arrow keys to navigate between tracks. Press Enter or Space to play a track. Press Home or End to jump to first or last track.";
|
|
5259
|
+
this.playlistPanel.appendChild(instructions);
|
|
5260
|
+
const list = DOMUtils.createElement("ul", {
|
|
5261
|
+
className: "vidply-playlist-list",
|
|
5262
|
+
"aria-labelledby": "vidply-playlist-heading",
|
|
5263
|
+
"aria-describedby": "vidply-playlist-instructions"
|
|
5264
|
+
});
|
|
5265
|
+
const listDescription = DOMUtils.createElement("div", {
|
|
5266
|
+
className: "vidply-sr-only",
|
|
5267
|
+
id: "vidply-playlist-instructions"
|
|
5268
|
+
});
|
|
5269
|
+
listDescription.textContent = `Playlist with ${this.tracks.length} ${this.tracks.length === 1 ? "track" : "tracks"}`;
|
|
5270
|
+
this.playlistPanel.appendChild(listDescription);
|
|
5180
5271
|
this.tracks.forEach((track, index) => {
|
|
5181
5272
|
const item = this.createPlaylistItem(track, index);
|
|
5182
5273
|
list.appendChild(item);
|
|
@@ -5188,20 +5279,39 @@ var PlaylistManager = class {
|
|
|
5188
5279
|
* Create playlist item element
|
|
5189
5280
|
*/
|
|
5190
5281
|
createPlaylistItem(track, index) {
|
|
5191
|
-
const
|
|
5282
|
+
const trackPosition = `Track ${index + 1} of ${this.tracks.length}`;
|
|
5283
|
+
const trackTitle = track.title || `Track ${index + 1}`;
|
|
5284
|
+
const trackArtist = track.artist ? ` by ${track.artist}` : "";
|
|
5285
|
+
const isActive = index === this.currentIndex;
|
|
5286
|
+
const statusText = isActive ? "Currently playing" : "Not playing";
|
|
5287
|
+
const actionText = isActive ? "Press Enter to restart" : "Press Enter to play";
|
|
5288
|
+
const item = DOMUtils.createElement("li", {
|
|
5192
5289
|
className: "vidply-playlist-item",
|
|
5193
|
-
|
|
5194
|
-
|
|
5195
|
-
"aria-label":
|
|
5196
|
-
|
|
5197
|
-
|
|
5290
|
+
tabIndex: index === 0 ? 0 : -1,
|
|
5291
|
+
// Only first item is in tab order initially
|
|
5292
|
+
"aria-label": `${trackPosition}. ${trackTitle}${trackArtist}. ${statusText}. ${actionText}.`,
|
|
5293
|
+
"aria-posinset": index + 1,
|
|
5294
|
+
"aria-setsize": this.tracks.length,
|
|
5295
|
+
"data-playlist-index": index
|
|
5296
|
+
});
|
|
5297
|
+
if (isActive) {
|
|
5198
5298
|
item.classList.add("vidply-playlist-item-active");
|
|
5299
|
+
item.setAttribute("aria-current", "true");
|
|
5300
|
+
item.setAttribute("tabIndex", "0");
|
|
5199
5301
|
}
|
|
5302
|
+
const positionInfo = DOMUtils.createElement("span", {
|
|
5303
|
+
className: "vidply-sr-only"
|
|
5304
|
+
});
|
|
5305
|
+
positionInfo.textContent = `${trackPosition}: `;
|
|
5306
|
+
item.appendChild(positionInfo);
|
|
5200
5307
|
const thumbnail = DOMUtils.createElement("div", {
|
|
5201
|
-
className: "vidply-playlist-thumbnail"
|
|
5308
|
+
className: "vidply-playlist-thumbnail",
|
|
5309
|
+
"aria-hidden": "true"
|
|
5202
5310
|
});
|
|
5203
5311
|
if (track.poster) {
|
|
5204
5312
|
thumbnail.style.backgroundImage = `url(${track.poster})`;
|
|
5313
|
+
thumbnail.setAttribute("role", "img");
|
|
5314
|
+
thumbnail.setAttribute("aria-label", `${trackTitle} thumbnail`);
|
|
5205
5315
|
} else {
|
|
5206
5316
|
const icon = createIconElement("music");
|
|
5207
5317
|
icon.classList.add("vidply-playlist-thumbnail-icon");
|
|
@@ -5209,12 +5319,13 @@ var PlaylistManager = class {
|
|
|
5209
5319
|
}
|
|
5210
5320
|
item.appendChild(thumbnail);
|
|
5211
5321
|
const info = DOMUtils.createElement("div", {
|
|
5212
|
-
className: "vidply-playlist-item-info"
|
|
5322
|
+
className: "vidply-playlist-item-info",
|
|
5323
|
+
"aria-hidden": "true"
|
|
5213
5324
|
});
|
|
5214
5325
|
const title = DOMUtils.createElement("div", {
|
|
5215
5326
|
className: "vidply-playlist-item-title"
|
|
5216
5327
|
});
|
|
5217
|
-
title.textContent =
|
|
5328
|
+
title.textContent = trackTitle;
|
|
5218
5329
|
info.appendChild(title);
|
|
5219
5330
|
if (track.artist) {
|
|
5220
5331
|
const artist = DOMUtils.createElement("div", {
|
|
@@ -5224,20 +5335,64 @@ var PlaylistManager = class {
|
|
|
5224
5335
|
info.appendChild(artist);
|
|
5225
5336
|
}
|
|
5226
5337
|
item.appendChild(info);
|
|
5338
|
+
if (isActive) {
|
|
5339
|
+
const statusIndicator = DOMUtils.createElement("span", {
|
|
5340
|
+
className: "vidply-sr-only"
|
|
5341
|
+
});
|
|
5342
|
+
statusIndicator.textContent = " (Currently playing)";
|
|
5343
|
+
item.appendChild(statusIndicator);
|
|
5344
|
+
}
|
|
5227
5345
|
const playIcon = createIconElement("play");
|
|
5228
5346
|
playIcon.classList.add("vidply-playlist-item-icon");
|
|
5347
|
+
playIcon.setAttribute("aria-hidden", "true");
|
|
5229
5348
|
item.appendChild(playIcon);
|
|
5230
5349
|
item.addEventListener("click", () => {
|
|
5231
|
-
this.play(index);
|
|
5350
|
+
this.play(index, true);
|
|
5232
5351
|
});
|
|
5233
5352
|
item.addEventListener("keydown", (e) => {
|
|
5234
|
-
|
|
5235
|
-
e.preventDefault();
|
|
5236
|
-
this.play(index);
|
|
5237
|
-
}
|
|
5353
|
+
this.handlePlaylistItemKeydown(e, index);
|
|
5238
5354
|
});
|
|
5239
5355
|
return item;
|
|
5240
5356
|
}
|
|
5357
|
+
/**
|
|
5358
|
+
* Handle keyboard navigation in playlist items
|
|
5359
|
+
*/
|
|
5360
|
+
handlePlaylistItemKeydown(e, index) {
|
|
5361
|
+
const items = Array.from(this.playlistPanel.querySelectorAll(".vidply-playlist-item"));
|
|
5362
|
+
let newIndex = -1;
|
|
5363
|
+
switch (e.key) {
|
|
5364
|
+
case "Enter":
|
|
5365
|
+
case " ":
|
|
5366
|
+
e.preventDefault();
|
|
5367
|
+
this.play(index, true);
|
|
5368
|
+
break;
|
|
5369
|
+
case "ArrowDown":
|
|
5370
|
+
e.preventDefault();
|
|
5371
|
+
if (index < items.length - 1) {
|
|
5372
|
+
newIndex = index + 1;
|
|
5373
|
+
}
|
|
5374
|
+
break;
|
|
5375
|
+
case "ArrowUp":
|
|
5376
|
+
e.preventDefault();
|
|
5377
|
+
if (index > 0) {
|
|
5378
|
+
newIndex = index - 1;
|
|
5379
|
+
}
|
|
5380
|
+
break;
|
|
5381
|
+
case "Home":
|
|
5382
|
+
e.preventDefault();
|
|
5383
|
+
newIndex = 0;
|
|
5384
|
+
break;
|
|
5385
|
+
case "End":
|
|
5386
|
+
e.preventDefault();
|
|
5387
|
+
newIndex = items.length - 1;
|
|
5388
|
+
break;
|
|
5389
|
+
}
|
|
5390
|
+
if (newIndex !== -1 && newIndex !== index) {
|
|
5391
|
+
items[index].setAttribute("tabIndex", "-1");
|
|
5392
|
+
items[newIndex].setAttribute("tabIndex", "0");
|
|
5393
|
+
items[newIndex].focus();
|
|
5394
|
+
}
|
|
5395
|
+
}
|
|
5241
5396
|
/**
|
|
5242
5397
|
* Update playlist UI (highlight current track)
|
|
5243
5398
|
*/
|
|
@@ -5245,11 +5400,25 @@ var PlaylistManager = class {
|
|
|
5245
5400
|
if (!this.playlistPanel) return;
|
|
5246
5401
|
const items = this.playlistPanel.querySelectorAll(".vidply-playlist-item");
|
|
5247
5402
|
items.forEach((item, index) => {
|
|
5403
|
+
const track = this.tracks[index];
|
|
5404
|
+
const trackPosition = `Track ${index + 1} of ${this.tracks.length}`;
|
|
5405
|
+
const trackTitle = track.title || `Track ${index + 1}`;
|
|
5406
|
+
const trackArtist = track.artist ? ` by ${track.artist}` : "";
|
|
5248
5407
|
if (index === this.currentIndex) {
|
|
5249
5408
|
item.classList.add("vidply-playlist-item-active");
|
|
5409
|
+
item.setAttribute("aria-current", "true");
|
|
5410
|
+
item.setAttribute("tabIndex", "0");
|
|
5411
|
+
const statusText = "Currently playing";
|
|
5412
|
+
const actionText = "Press Enter to restart";
|
|
5413
|
+
item.setAttribute("aria-label", `${trackPosition}. ${trackTitle}${trackArtist}. ${statusText}. ${actionText}.`);
|
|
5250
5414
|
item.scrollIntoView({ behavior: "smooth", block: "nearest" });
|
|
5251
5415
|
} else {
|
|
5252
5416
|
item.classList.remove("vidply-playlist-item-active");
|
|
5417
|
+
item.removeAttribute("aria-current");
|
|
5418
|
+
item.setAttribute("tabIndex", "-1");
|
|
5419
|
+
const statusText = "Not playing";
|
|
5420
|
+
const actionText = "Press Enter to play";
|
|
5421
|
+
item.setAttribute("aria-label", `${trackPosition}. ${trackTitle}${trackArtist}. ${statusText}. ${actionText}.`);
|
|
5253
5422
|
}
|
|
5254
5423
|
});
|
|
5255
5424
|
}
|
|
@@ -5267,10 +5436,24 @@ var PlaylistManager = class {
|
|
|
5267
5436
|
currentIndex: this.currentIndex,
|
|
5268
5437
|
totalTracks: this.tracks.length,
|
|
5269
5438
|
currentTrack: this.getCurrentTrack(),
|
|
5270
|
-
hasNext: this.
|
|
5271
|
-
hasPrevious: this.
|
|
5439
|
+
hasNext: this.hasNext(),
|
|
5440
|
+
hasPrevious: this.hasPrevious()
|
|
5272
5441
|
};
|
|
5273
5442
|
}
|
|
5443
|
+
/**
|
|
5444
|
+
* Check if there is a next track
|
|
5445
|
+
*/
|
|
5446
|
+
hasNext() {
|
|
5447
|
+
if (this.options.loop) return true;
|
|
5448
|
+
return this.currentIndex < this.tracks.length - 1;
|
|
5449
|
+
}
|
|
5450
|
+
/**
|
|
5451
|
+
* Check if there is a previous track
|
|
5452
|
+
*/
|
|
5453
|
+
hasPrevious() {
|
|
5454
|
+
if (this.options.loop) return true;
|
|
5455
|
+
return this.currentIndex > 0;
|
|
5456
|
+
}
|
|
5274
5457
|
/**
|
|
5275
5458
|
* Add track to playlist
|
|
5276
5459
|
*/
|