UNPKG

@quick-game/cli

Version:

Command line interface for rapid qg development

206 lines 9.17 kB
// Copyright 2019 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. import * as i18n from '../../core/i18n/i18n.js'; import * as UI from '../../ui/legacy/legacy.js'; import playerListViewStyles from './playerListView.css.js'; const UIStrings = { /** *@description A right-click context menu entry which when clicked causes the menu entry for that player to be removed. */ hidePlayer: 'Hide player', /** *@description A right-click context menu entry which should keep the element selected, while hiding all other entries. */ hideAllOthers: 'Hide all others', /** *@description Context menu entry which downloads the json dump when clicked */ savePlayerInfo: 'Save player info', /** *@description Side-panel entry title text for the players section. */ players: 'Players', }; const str_ = i18n.i18n.registerUIStrings('panels/media/PlayerListView.ts', UIStrings); const i18nString = i18n.i18n.getLocalizedString.bind(undefined, str_); export class PlayerListView extends UI.Widget.VBox { playerEntryFragments; playerEntriesWithHostnameFrameTitle; mainContainer; currentlySelectedEntry; constructor(mainContainer) { super(true); this.playerEntryFragments = new Map(); this.playerEntriesWithHostnameFrameTitle = new Set(); // Container where new panels can be added based on clicks. this.mainContainer = mainContainer; this.currentlySelectedEntry = null; this.contentElement.createChild('div', 'player-entry-header').textContent = i18nString(UIStrings.players); } createPlayerListEntry(playerID) { const entry = UI.Fragment.Fragment.build ` <div class="player-entry-row hbox"> <div class="player-entry-status-icon vbox"> <div $="icon" class="player-entry-status-icon-centering"></div> </div> <div $="frame-title" class="player-entry-frame-title">FrameTitle</div> <div $="player-title" class="player-entry-player-title">PlayerTitle</div> </div> `; const element = entry.element(); element.addEventListener('click', this.selectPlayer.bind(this, playerID, element)); element.addEventListener('contextmenu', this.rightClickPlayer.bind(this, playerID)); entry.$('icon').appendChild(UI.Icon.Icon.create('pause', 'media-player')); return entry; } selectPlayer(playerID, element) { this.mainContainer.renderMainPanel(playerID); if (this.currentlySelectedEntry !== null) { this.currentlySelectedEntry.classList.remove('selected'); this.currentlySelectedEntry.classList.remove('force-white-icons'); } element.classList.add('selected'); element.classList.add('force-white-icons'); this.currentlySelectedEntry = element; } rightClickPlayer(playerID, event) { const contextMenu = new UI.ContextMenu.ContextMenu(event); contextMenu.headerSection().appendItem(i18nString(UIStrings.hidePlayer), this.mainContainer.markPlayerForDeletion.bind(this.mainContainer, playerID)); contextMenu.headerSection().appendItem(i18nString(UIStrings.hideAllOthers), this.mainContainer.markOtherPlayersForDeletion.bind(this.mainContainer, playerID)); contextMenu.headerSection().appendItem(i18nString(UIStrings.savePlayerInfo), this.mainContainer.exportPlayerData.bind(this.mainContainer, playerID)); void contextMenu.show(); return true; } setMediaElementFrameTitle(playerID, frameTitle, isHostname) { // Only remove the title from the set if we arent setting a hostname title. // Otherwise, if it has a non-hostname title, and the requested new title is // a hostname, just drop it. if (this.playerEntriesWithHostnameFrameTitle.has(playerID)) { if (!isHostname) { this.playerEntriesWithHostnameFrameTitle.delete(playerID); } } else if (isHostname) { return; } if (!this.playerEntryFragments.has(playerID)) { return; } const fragment = this.playerEntryFragments.get(playerID); if (fragment === undefined || fragment.element() === undefined) { return; } fragment.$('frame-title').textContent = frameTitle; } setMediaElementPlayerTitle(playerID, playerTitle) { if (!this.playerEntryFragments.has(playerID)) { return; } const fragment = this.playerEntryFragments.get(playerID); if (fragment === undefined) { return; } fragment.$('player-title').textContent = playerTitle; } setMediaElementPlayerIcon(playerID, iconName) { if (!this.playerEntryFragments.has(playerID)) { return; } const fragment = this.playerEntryFragments.get(playerID); if (fragment === undefined) { return; } const icon = fragment.$('icon'); if (icon === undefined) { return; } icon.textContent = ''; icon.appendChild(UI.Icon.Icon.create(iconName, 'media-player')); } formatAndEvaluate(playerID, func, candidate, min, max) { if (candidate.length <= min) { return; } if (candidate.length >= max) { candidate = candidate.substring(0, max - 3) + '...'; } func.bind(this)(playerID, candidate); } addMediaElementItem(playerID) { const sidebarEntry = this.createPlayerListEntry(playerID); this.contentElement.appendChild(sidebarEntry.element()); this.playerEntryFragments.set(playerID, sidebarEntry); this.playerEntriesWithHostnameFrameTitle.add(playerID); } deletePlayer(playerID) { if (!this.playerEntryFragments.has(playerID)) { return; } const fragment = this.playerEntryFragments.get(playerID); if (fragment === undefined || fragment.element() === undefined) { return; } this.contentElement.removeChild(fragment.element()); this.playerEntryFragments.delete(playerID); } onEvent(playerID, event) { const parsed = JSON.parse(event.value); const eventType = parsed.event; // Load events provide the actual underlying URL for the video, which makes // a great way to identify a specific video within a page that potentially // may have many videos. MSE videos have a special blob:http(s) protocol // that we'd like to keep mind of, so we do prepend blob: if (eventType === 'kLoad') { const url = parsed.url; const videoName = url.substring(url.lastIndexOf('/') + 1); this.formatAndEvaluate(playerID, this.setMediaElementPlayerTitle, videoName, 1, 20); return; } if (eventType === 'kPlay') { this.setMediaElementPlayerIcon(playerID, 'play'); return; } if (eventType === 'kPause' || eventType === 'kEnded') { this.setMediaElementPlayerIcon(playerID, 'pause'); return; } if (eventType === 'kWebMediaPlayerDestroyed') { this.setMediaElementPlayerIcon(playerID, 'cross'); return; } } onProperty(playerID, property) { // FrameUrl is always present, and we can generate a basic frame title from // it by grabbing the hostname. It's not possible to generate a "good" player // title from the FrameUrl though, since the page location itself might not // have any relevance to the video being played, and would be shared by all // videos on the page. if (property.name === "kFrameUrl" /* PlayerPropertyKeys.FrameUrl */) { const frameTitle = new URL(property.value).hostname; this.formatAndEvaluate(playerID, this.setMediaElementFrameTitle, frameTitle, 1, 20); return; } // On the other hand, the page may set a title, which usually makes for a // better frame title than a hostname. Unfortunately, its only "usually", // since the site is free to set the title to _anything_, it might just be // junk, or it might be super long. If it's empty, or 1 character, It's // preferable to just drop it. Titles longer than 20 will have the first // 17 characters kept and an elipsis appended. if (property.name === "kFrameTitle" /* PlayerPropertyKeys.FrameTitle */ && property.value) { this.formatAndEvaluate(playerID, this.setMediaElementFrameTitle, property.value, 1, 20); return; } } onError(_playerID, _error) { // TODO(tmathmeyer) show an error icon next to the player name } onMessage(_playerID, _message) { // TODO(tmathmeyer) show a message count number next to the player name. } wasShown() { super.wasShown(); this.registerCSSFiles([playerListViewStyles]); } } //# sourceMappingURL=PlayerListView.js.map