class TracksPanel extends LayoutNode
{
constructor()
{
super(`<div class="tracks-panel">`);
this._rightX = 0;
this._bottomY = 0;
}
get presented()
{
return !!this.parent;
}
presentInParent(node)
{
if (this.parent === node)
return;
this.children = this._childrenFromDataSource();
node.addChild(this);
this.element.removeEventListener("transitionend", this);
this.element.classList.remove("fade-out");
this._mousedownTarget().addEventListener("mousedown", this, true);
window.addEventListener("keydown", this, true);
this._focusedTrackNode = null;
}
hide()
{
if (!this.presented)
return;
this._mousedownTarget().removeEventListener("mousedown", this, true);
window.removeEventListener("keydown", this, true);
this.element.addEventListener("transitionend", this);
this.element.classList.add("fade-out");
}
get bottomY()
{
return this._bottomY;
}
set bottomY(bottomY)
{
if (this._bottomY === bottomY)
return;
this._bottomY = bottomY;
this.markDirtyProperty("bottomY");
}
get rightX()
{
return this._rightX;
}
set rightX(x)
{
if (this._rightX === x)
return;
this._rightX = x;
this.markDirtyProperty("rightX");
}
trackNodeSelectionAnimationDidEnd(trackNode)
{
if (this.uiDelegate && typeof this.uiDelegate.tracksPanelSelectionDidChange === "function")
this.uiDelegate.tracksPanelSelectionDidChange(trackNode.index, trackNode.sectionIndex);
}
mouseMovedOverTrackNode(trackNode)
{
this._focusTrackNode(trackNode);
}
mouseExitedTrackNode(trackNode)
{
this._focusedTrackNode.element.blur();
delete this._focusedTrackNode;
}
commitProperty(propertyName)
{
if (propertyName === "rightX")
this.element.style.right = `${this._rightX}px`;
else if (propertyName === "bottomY")
this.element.style.bottom = `${this._bottomY}px`;
else
super.commitProperty(propertyName);
}
handleEvent(event)
{
switch (event.type) {
case "mousedown":
this._handleMousedown(event);
break;
case "keydown":
this._handleKeydown(event);
break;
case "transitionend":
this.remove();
break;
}
}
_mousedownTarget()
{
const mediaControls = this.parentOfType(MacOSFullscreenMediaControls);
if (mediaControls)
return mediaControls.element;
return window;
}
_childrenFromDataSource()
{
const children = [];
this._trackNodes = [];
const dataSource = this.dataSource;
if (!dataSource)
return children;
const numberOfSections = dataSource.tracksPanelNumberOfSections();
if (numberOfSections == 0)
return children;
for (let sectionIndex = 0; sectionIndex < numberOfSections; ++sectionIndex) {
let sectionNode = new LayoutNode(`<div class="tracks-panel-section"></div>`);
sectionNode.addChild(new LayoutNode(`<h3>${dataSource.tracksPanelTitleForSection(sectionIndex)}</h3>`));
let tracksListNode = sectionNode.addChild(new LayoutNode(`<ul></ul>`));
let numberOfTracks = dataSource.tracksPanelNumberOfTracksInSection(sectionIndex);
for (let trackIndex = 0; trackIndex < numberOfTracks; ++trackIndex) {
let trackTitle = dataSource.tracksPanelTitleForTrackInSection(trackIndex, sectionIndex);
let trackSelected = dataSource.tracksPanelIsTrackInSectionSelected(trackIndex, sectionIndex)
let trackNode = tracksListNode.addChild(new TrackNode(trackIndex, sectionIndex, trackTitle, trackSelected, this));
this._trackNodes.push(trackNode);
}
children.push(sectionNode);
}
return children;
}
_handleMousedown(event)
{
if (this.element.contains(event.target))
return;
this._dismiss();
event.preventDefault();
event.stopPropagation();
}
_handleKeydown(event)
{
switch (event.key) {
case "Home":
case "PageUp":
this._focusFirstTrackNode();
break;
case "End":
case "PageDown":
this._focusLastTrackNode();
break;
case "ArrowDown":
if (event.altKey || event.metaKey)
this._focusLastTrackNode();
else
this._focusNextTrackNode();
break;
case "ArrowUp":
if (event.altKey || event.metaKey)
this._focusFirstTrackNode();
else
this._focusPreviousTrackNode();
break;
case " ":
case "Enter":
if (this._focusedTrackNode)
this._focusedTrackNode.activate();
break;
case "Escape":
this._dismiss();
break;
}
}
_dismiss()
{
if (this.parent && typeof this.parent.hideTracksPanel === "function")
this.parent.hideTracksPanel();
}
_focusTrackNode(trackNode)
{
if (!trackNode || trackNode === this._focusedTrackNode)
return;
trackNode.element.focus();
this._focusedTrackNode = trackNode;
}
_focusPreviousTrackNode()
{
const previousIndex = this._focusedTrackNode ? this._trackNodes.indexOf(this._focusedTrackNode) - 1 : this._trackNodes.length - 1;
this._focusTrackNode(this._trackNodes[previousIndex]);
}
_focusNextTrackNode()
{
this._focusTrackNode(this._trackNodes[this._trackNodes.indexOf(this._focusedTrackNode) + 1]);
}
_focusFirstTrackNode()
{
this._focusTrackNode(this._trackNodes[0]);
}
_focusLastTrackNode()
{
this._focusTrackNode(this._trackNodes[this._trackNodes.length - 1]);
}
}
class TrackNode extends LayoutNode
{
constructor(index, sectionIndex, title, selected, panel)
{
super(`<li tabindex="0">${title}</li>`);
this.index = index;
this.sectionIndex = sectionIndex;
this._panel = panel;
this._selected = selected;
if (selected)
this.element.classList.add("selected");
this.element.addEventListener("mousemove", this);
this.element.addEventListener("mouseleave", this);
this.element.addEventListener("click", this);
}
activate()
{
this.element.addEventListener("animationend", this);
this.element.classList.add("animated");
}
handleEvent(event)
{
switch (event.type) {
case "mousemove":
this._panel.mouseMovedOverTrackNode(this);
break;
case "mouseleave":
this._panel.mouseExitedTrackNode(this);
break;
case "click":
this.activate();
break;
case "animationend":
this._animationDidEnd();
break;
}
}
_animationDidEnd()
{
this.element.removeEventListener("animationend", this);
this._panel.trackNodeSelectionAnimationDidEnd(this);
}
}