summaryrefslogtreecommitdiff
path: root/Source/WebCore/Modules/modern-media-controls/controls/tracks-panel.js
diff options
context:
space:
mode:
Diffstat (limited to 'Source/WebCore/Modules/modern-media-controls/controls/tracks-panel.js')
-rw-r--r--Source/WebCore/Modules/modern-media-controls/controls/tracks-panel.js305
1 files changed, 305 insertions, 0 deletions
diff --git a/Source/WebCore/Modules/modern-media-controls/controls/tracks-panel.js b/Source/WebCore/Modules/modern-media-controls/controls/tracks-panel.js
new file mode 100644
index 000000000..4a69bb6e2
--- /dev/null
+++ b/Source/WebCore/Modules/modern-media-controls/controls/tracks-panel.js
@@ -0,0 +1,305 @@
+
+class TracksPanel extends LayoutNode
+{
+
+ constructor()
+ {
+ super(`<div class="tracks-panel"></div>`);
+ this._backgroundTint = new BackgroundTint;
+ this._rightX = 0;
+ this._bottomY = 0;
+ }
+
+ // Public
+
+ 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);
+
+ // Ensure a transition will indeed happen by starting it only on the next frame.
+ window.requestAnimationFrame(() => { 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");
+ }
+
+ // Protected
+
+ 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;
+ }
+ }
+
+ // Private
+
+ _mousedownTarget()
+ {
+ const mediaControls = this.parentOfType(MacOSFullscreenMediaControls);
+ if (mediaControls)
+ return mediaControls.element;
+ return window;
+ }
+
+ _childrenFromDataSource()
+ {
+ const children = [this._backgroundTint];
+
+ 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(`<section></section>`);
+ 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);
+ }
+
+ // Public
+
+ activate()
+ {
+ this.element.addEventListener("animationend", this);
+ this.element.classList.add("animated");
+ }
+
+ // Protected
+
+ 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;
+ }
+ }
+
+ // Private
+
+ _animationDidEnd()
+ {
+ this.element.removeEventListener("animationend", this);
+ this._panel.trackNodeSelectionAnimationDidEnd(this);
+ }
+
+}