mirror of
https://github.com/hensm/fx_cast.git
synced 2026-06-09 00:59:59 +00:00
Add <google-cast-launcher> custom element + misc changes
This commit is contained in:
@@ -33,7 +33,7 @@ import { onMessage, sendMessageResponse } from "../messageBridge";
|
||||
|
||||
type ReceiverActionListener = (
|
||||
receiver: Receiver
|
||||
, receiverAction: typeof ReceiverAction) => void;
|
||||
, receiverAction: string) => void;
|
||||
|
||||
type RequestSessionSuccessCallback = (
|
||||
session: Session
|
||||
|
||||
78
ext/src/shim/framework/GoogleCastLauncher.ts
Normal file
78
ext/src/shim/framework/GoogleCastLauncher.ts
Normal file
@@ -0,0 +1,78 @@
|
||||
"use strict";
|
||||
|
||||
/**
|
||||
* Custom element for a cast button used by sites that injects
|
||||
* a cast icon and manages visibility state and event handling.
|
||||
*/
|
||||
export default class GoogleCastLauncher extends HTMLElement {
|
||||
constructor () {
|
||||
super();
|
||||
|
||||
this.style.display = "none";
|
||||
|
||||
const style = document.createElement("style");
|
||||
style.textContent = `
|
||||
.cast_caf_state_c {
|
||||
fill: var(--connected-color, #4285f4);
|
||||
}
|
||||
.cast_caf_state_d {
|
||||
fill: var(--disconnected-color, #7d7d7d);
|
||||
}
|
||||
.cast_caf_state_h {
|
||||
opacity: 0;
|
||||
}
|
||||
`;
|
||||
|
||||
// tslint:disable:max-line-length
|
||||
|
||||
const SVG_NAMESPACE = "http://www.w3.org/2000/svg";
|
||||
|
||||
const icon = document.createElementNS(SVG_NAMESPACE, "svg");
|
||||
const iconArch0 = document.createElementNS(SVG_NAMESPACE, "path");
|
||||
const iconArch1 = document.createElementNS(SVG_NAMESPACE, "path");
|
||||
const iconArch2 = document.createElementNS(SVG_NAMESPACE, "path");
|
||||
const iconBox = document.createElementNS(SVG_NAMESPACE, "path");
|
||||
const iconBoxFill = document.createElementNS(SVG_NAMESPACE, "path");
|
||||
|
||||
// Set SVG attributes
|
||||
icon.setAttribute("x", "0");
|
||||
icon.setAttribute("y", "0");
|
||||
icon.setAttribute("width", "100%");
|
||||
icon.setAttribute("height", "100%");
|
||||
icon.setAttribute("viewBox", "0 0 24 24");
|
||||
|
||||
iconArch0.classList.add("cast_caf_state_d");
|
||||
iconArch0.setAttribute("id", "cast_caf_icon_arch0");
|
||||
iconArch0.setAttribute("d", "M1 18v3h3c0-1.7-1.34-3-3-3z");
|
||||
|
||||
iconArch1.classList.add("cast_caf_state_d");
|
||||
iconArch1.setAttribute("id", "cast_caf_icon_arch1");
|
||||
iconArch1.setAttribute("d", "M1 14v2c2.76 0 5 2.2 5 5h2c0-3.87-3.13-7-7-7z");
|
||||
|
||||
iconArch2.classList.add("cast_caf_state_d");
|
||||
iconArch2.setAttribute("id", "cast_caf_icon_arch2");
|
||||
iconArch2.setAttribute("d", "M1 10v2c4.97 0 9 4 9 9h2c0-6.08-4.93-11-11-11z");
|
||||
|
||||
iconBox.classList.add("cast_caf_state_d");
|
||||
iconBox.setAttribute("id", "cast_caf_icon_box");
|
||||
iconBox.setAttribute("d", "M21 3H3c-1.1 0-2 .9-2 2v3h2V5h18v14h-7v2h7c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2z");
|
||||
|
||||
iconBoxFill.classList.add("cast_caf_state_h");
|
||||
iconBoxFill.setAttribute("id", "cast_caf_icon_boxfill");
|
||||
iconBoxFill.setAttribute("d", "M5 7v1.63C8 8.6 13.37 14 13.37 17H19V7z");
|
||||
|
||||
// Add icon paths to SVG
|
||||
icon.append(iconArch0, iconArch1, iconArch2, iconBox, iconBoxFill);
|
||||
|
||||
// tslint:enable:max-line-length
|
||||
|
||||
|
||||
const shadow = this.attachShadow({ mode: "open" });
|
||||
shadow.append(icon, style);
|
||||
|
||||
|
||||
this.addEventListener("click", ev => {
|
||||
console.info("<google-cast-launcher> onClick");
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -1,20 +1,14 @@
|
||||
"use strict";
|
||||
|
||||
import * as cast from "../../cast";
|
||||
|
||||
import CastOptions from "./CastOptions";
|
||||
import CastSession from "./CastSession";
|
||||
import CastStateEventData from "./CastStateEventData";
|
||||
import SessionStateEventData from "./SessionStateEventData";
|
||||
|
||||
|
||||
type EventHandler = (eventData:
|
||||
CastStateEventData
|
||||
| SessionStateEventData) => void;
|
||||
|
||||
export default class CastContext {
|
||||
public addEventListener (type: string, handler: EventHandler): void {
|
||||
console.info("STUB :: CastContext#addEventListener");
|
||||
}
|
||||
|
||||
export default class CastContext extends EventTarget {
|
||||
public endCurrentSession (stopCasting: boolean): void {
|
||||
console.info("STUB :: CastContext#endCurrentSession");
|
||||
}
|
||||
@@ -34,10 +28,6 @@ export default class CastContext {
|
||||
console.info("STUB :: CastContext#getSessionState");
|
||||
}
|
||||
|
||||
public removeEventListener (type: string, handler: EventHandler): void {
|
||||
console.info("STUB :: CastContext#removeEventListener");
|
||||
}
|
||||
|
||||
// @ts-ignore
|
||||
public requestSession (): Promise<string> {
|
||||
console.info("STUB :: CastContext#requestSession");
|
||||
@@ -47,3 +37,5 @@ export default class CastContext {
|
||||
console.info("STUB :: CastContext#setOptions");
|
||||
}
|
||||
}
|
||||
|
||||
export const instance = new CastContext();
|
||||
|
||||
@@ -10,28 +10,15 @@ import MediaSessionEventData from "./MediaSessionEventData";
|
||||
import VolumeEventData from "./VolumeEventData";
|
||||
|
||||
|
||||
type EventHandler = (eventData:
|
||||
ApplicationStatusEventData
|
||||
| ApplicationMetadataEventData
|
||||
| ActiveInputStateEventData
|
||||
| MediaSessionEventData
|
||||
| VolumeEventData) => void;
|
||||
|
||||
type MessageListener = (namespace: string, message: string) => void;
|
||||
|
||||
|
||||
export default class CastSession {
|
||||
export default class CastSession extends EventTarget {
|
||||
constructor (sessionObj: cast.Session, state: string) {
|
||||
super();
|
||||
console.info("STUB :: CastSession#constructor");
|
||||
}
|
||||
|
||||
public addEventListener (
|
||||
type: string
|
||||
, handler: EventHandler): void {
|
||||
|
||||
console.info("STUB :: CastSession#addEventListener");
|
||||
}
|
||||
|
||||
public addMessageListener (
|
||||
namespace: string
|
||||
, listener: MessageListener): void {
|
||||
@@ -98,13 +85,6 @@ export default class CastSession {
|
||||
console.info("STUB :: CastSession#loadMedia");
|
||||
}
|
||||
|
||||
public removeEventListener (
|
||||
type: string
|
||||
, handler: EventHandler): void {
|
||||
|
||||
console.info("STUB :: CastSession#removeEventListener");
|
||||
}
|
||||
|
||||
public removeMessageListener (
|
||||
namespace: string
|
||||
, listener: MessageListener): void {
|
||||
|
||||
@@ -4,17 +4,12 @@ import RemotePlayer from "./RemotePlayer";
|
||||
import RemotePlayerChangedEvent from "./RemotePlayerChangedEvent";
|
||||
|
||||
|
||||
type EventHandler = (event: RemotePlayerChangedEvent) => void;
|
||||
|
||||
export default class RemotePlayerController {
|
||||
export default class RemotePlayerController extends EventTarget {
|
||||
constructor (player: RemotePlayer) {
|
||||
super();
|
||||
console.info("STUB :: RemotePlayerController#constructor");
|
||||
}
|
||||
|
||||
public addEventListener (type: string, handler: EventHandler): void {
|
||||
console.info("STUB :: RemotePlayerContoller#addEventListener");
|
||||
}
|
||||
|
||||
public getFormattedTime (timeInSec: number): string {
|
||||
const hours = Math.floor(timeInSec / 3600) % 24;
|
||||
const minutes = Math.floor(timeInSec / 60) % 60;
|
||||
@@ -41,10 +36,6 @@ export default class RemotePlayerController {
|
||||
console.info("STUB :: RemotePlayerController#playOrPause");
|
||||
}
|
||||
|
||||
public removeEventListener (type: string, handler: EventHandler): void {
|
||||
console.info("STUB :: RemotePlayerController#removeEventListener");
|
||||
}
|
||||
|
||||
public seek (): void {
|
||||
console.info("STUB :: RemotePlayerController#seek");
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@ import ActiveInputStateEventData from "./classes/ActiveInputStateEventData";
|
||||
import ApplicationMetadata from "./classes/ApplicationMetadata";
|
||||
import ApplicationMetadataEventData from "./classes/ApplicationMetadataEventData";
|
||||
import ApplicationStatusEventData from "./classes/ApplicationStatusEventData";
|
||||
import CastContext from "./classes/CastContext";
|
||||
import CastContext, { instance } from "./classes/CastContext";
|
||||
import CastOptions from "./classes/CastOptions";
|
||||
import CastSession from "./classes/CastSession";
|
||||
import CastStateEventData from "./classes/CastStateEventData";
|
||||
@@ -26,12 +26,11 @@ import { ActiveInputState
|
||||
, SessionEventType
|
||||
, SessionState } from "./enums";
|
||||
|
||||
import GoogleCastLauncher from "./GoogleCastLauncher";
|
||||
|
||||
import { onMessage } from "../messageBridge";
|
||||
|
||||
|
||||
let castContext: CastContext = null;
|
||||
|
||||
export default {
|
||||
// Enums
|
||||
ActiveInputState, CastContextEventType, CastState, LoggerLevel
|
||||
@@ -52,12 +51,7 @@ export default {
|
||||
...CastContext
|
||||
|
||||
, getInstance () {
|
||||
if (castContext) {
|
||||
return castContext;
|
||||
}
|
||||
|
||||
castContext = new CastContext();
|
||||
return castContext;
|
||||
return instance;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -67,3 +61,19 @@ export default {
|
||||
console.info("STUB :: cast.framework.setLoggerLevel");
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* The Framework API defines a <google-cast-launcher> element
|
||||
* and a <button is="google-cast-button"> element extension,
|
||||
* both of which produce the same result.
|
||||
*
|
||||
* Chrome allowed custom elements to extend <button> elements
|
||||
* via Element#createShadowRoot, but the standard
|
||||
* Element#attachShadow method supported in Firefox specifies a
|
||||
* limited whitelist of elements that are extendable.
|
||||
*
|
||||
* It's not officially advertised in the cast docs, so it
|
||||
* shouldn't be much of a compatibility issue to ignore it.
|
||||
*/
|
||||
customElements.define("google-cast-launcher", GoogleCastLauncher);
|
||||
|
||||
@@ -5,13 +5,13 @@ import * as cast from "./cast";
|
||||
import { onMessage } from "./messageBridge";
|
||||
|
||||
|
||||
const global = (window as any);
|
||||
const _window = (window as any);
|
||||
|
||||
if (!global.chrome) {
|
||||
global.chrome = {};
|
||||
if (!_window.chrome) {
|
||||
_window.chrome = {};
|
||||
}
|
||||
|
||||
global.chrome.cast = cast;
|
||||
_window.chrome.cast = cast;
|
||||
|
||||
/**
|
||||
* If loaded within a page via a <script> element,
|
||||
@@ -25,12 +25,12 @@ if (document.currentScript) {
|
||||
|
||||
// Load Framework API if requested
|
||||
if (currentScriptParams.get("loadCastFramework") === "1") {
|
||||
if (!global.cast) {
|
||||
global.cast = {};
|
||||
if (!_window.cast) {
|
||||
_window.cast = {};
|
||||
}
|
||||
|
||||
import("./framework").then(framework => {
|
||||
global.cast.framework = framework.default;
|
||||
_window.cast.framework = framework.default;
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -42,7 +42,7 @@ onMessage(message => {
|
||||
const bridgeInfo = message.data;
|
||||
|
||||
// Call page's API loaded function if defined
|
||||
const readyFunction = global.__onGCastApiAvailable;
|
||||
const readyFunction = _window.__onGCastApiAvailable;
|
||||
if (readyFunction && typeof readyFunction === "function") {
|
||||
readyFunction(bridgeInfo && bridgeInfo.isVersionCompatible);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user