nodecg
Version:
Dynamic broadcast graphics rendered in a browser
362 lines (329 loc) • 10.2 kB
JavaScript
import "@polymer/iron-collapse/iron-collapse.js";
import "@polymer/iron-icons/hardware-icons.js";
import "@polymer/iron-icons/iron-icons.js";
import "@polymer/iron-media-query/iron-media-query.js";
import "@polymer/paper-button/paper-button.js";
// These get elided unless we do this hacky stuff to force typescript and webpack to keep them.
import * as keep1 from "./ncg-graphic-instance";
keep1;
import * as Polymer from "@polymer/polymer";
import { MutableData } from "@polymer/polymer/lib/mixins/mutable-data";
import Clipboard from "clipboard";
/**
* @customElement
* @polymer
* @appliesMixin MutableData
*/
class NcgGraphic extends MutableData(Polymer.PolymerElement) {
static get template() {
return Polymer.html `
<style include="nodecg-theme">
:host {
@apply --layout-vertical;
@apply --layout-flex-none;
white-space: nowrap;
}
:host(:not(:last-child)) {
margin-bottom: 20px;
}
#details {
@apply --layout-horizontal;
@apply --layout-flex-none;
height: 60px;
}
#indicator {
@apply --layout-flex-none;
background-color: #CACACA;
width: 9px;
}
:host([worst-status="nominal"]) #indicator {
background-color: #00A651;
}
:host([worst-status="out-of-date"]) #indicator {
background-color: #FFC700;
}
#counter {
@apply --layout-center-center;
@apply --layout-flex-none;
@apply --layout-horizontal;
background-color: #525F78;
color: #FFFFFF;
font-size: 24px;
font-style: normal;
font-weight: 500;
left: 9px;
line-height: normal;
text-align: center;
width: 38px;
}
#urlAndResolution {
@apply --layout-flex;
background-color: #525F78;
margin-right: 1px;
min-width: 0;
}
#url {
color: white;
font-size: 16px;
font-style: normal;
font-weight: 500;
letter-spacing: 0.018em;
line-height: normal;
max-width: 100%;
min-width: 0;
overflow: hidden;
text-decoration: underline;
text-overflow: ellipsis;
text-transform: uppercase;
}
#resolution {
font-size: 14px;
font-style: normal;
font-weight: 500;
line-height: normal;
overflow: hidden;
text-overflow: ellipsis;
}
#copyButton,
#reloadButton {
--paper-button: {
--nodecg-background-color: #6155BD;
}
}
#reloadButton iron-icon {
--iron-icon-height: 29px;
--iron-icon-width: 29px;
}
#collapseButton {
--paper-button: {
--nodecg-background-color: #525F78;
}
}
#collapseButton iron-icon {
--iron-icon-width: 40px;
--iron-icon-height: 40px;
}
paper-button {
@apply --layout-center-center;
@apply --layout-flex-none;
@apply --layout-horizontal;
border-radius: 0;
font-size: 14px;
font-style: normal;
font-weight: 500;
line-height: normal;
margin: 0 1px;
min-width: 0;
padding: 0;
width: 60px;
}
paper-button:last-child {
margin-right: 0;
}
/* Start wide styles */
:host([responsive-mode="wide"]) #urlAndResolution {
@apply --layout-horizontal;
@apply --layout-center;
}
:host([responsive-mode="wide"]) #resolution {
@apply --layout-flex-none;
box-sizing: border-box;
margin-left: auto;
padding: 0 4px;
text-align: center;
width: 90px;
}
:host([responsive-mode="wide"]) #resolution:before,
:host([responsive-mode="wide"]) #resolution:after {
background: #EFF0EC;
height: 50px;
width: 1px;
}
:host([responsive-mode="wide"]) #reloadButton {
width: 115px;
}
:host([responsive-mode="wide"]) #reloadButton iron-icon {
margin-left: -6px;
margin-right: -1px;
}
/* End wide styles */
:host(:not([responsive-mode="wide"])) #urlAndResolution {
@apply --layout-center-justified;
@apply --layout-start;
@apply --layout-vertical;
padding-right: 6px;
}
:host(:not([responsive-mode="narrow"])) #copyButton {
width: 115px;
}
:host([responsive-mode="narrow"]) #copyButton-text {
display: none;
}
:host(:not([responsive-mode="wide"])) #reloadButton-text {
display: none;
}
:host(:not([responsive-mode="narrow"])) #counter {
padding-right: 8px;
}
</style>
<iron-media-query query="(min-width: 641px)" query-matches="{{_wide}}"></iron-media-query>
<iron-media-query query="(min-width: 521px) and (max-width: 640px)" query-matches="{{_medium}}"></iron-media-query>
<iron-media-query query="(max-width: 520px)" query-matches="{{_narrow}}"></iron-media-query>
<div id="details">
<div id="indicator"></div>
<div id="counter">[[_calcCount(graphic.singleInstance, instances)]]</div>
<div id="urlAndResolution">
<a id="url" href\$="[[_computeFullGraphicUrl(graphic.url)]]" target="_blank" title="[[_calcShortUrl(graphic.url)]]">
[[_calcShortUrl(graphic.url)]]
</a>
<div id="resolution">[[graphic.width]]x[[graphic.height]]</div>
</div>
<paper-button id="copyButton" data-clipboard-text\$="[[_computeFullGraphicUrl(graphic.url)]]">
<iron-icon icon="content-copy"></iron-icon>
<span id="copyButton-text"> Copy URL</span>
</paper-button>
<paper-button id="reloadButton" on-tap="reloadAll" disabled="[[_calcReloadAllDisabled(instances)]]">
<iron-icon icon="refresh"></iron-icon>
<span id="reloadButton-text"> Reload</span>
</paper-button>
<paper-button id="collapseButton" on-tap="toggleCollapse">
<iron-icon icon="[[_computeCollapseIcon(_collapseOpened)]]"></iron-icon>
</paper-button>
</div>
<iron-collapse id="instancesCollapse" opened="{{_collapseOpened}}" no-animation="">
<template is="dom-repeat" items="[[instances]]" as="instance" mutable-data="">
<ncg-graphic-instance responsive-mode="[[responsiveMode]]" graphic="[[graphic]]" instance="[[instance]]">
</ncg-graphic-instance>
</template>
</iron-collapse>
`;
}
static get is() {
return "ncg-graphic";
}
static get properties() {
return {
graphic: {
type: Object,
},
instances: {
type: Array,
},
worstStatus: {
type: String,
reflectToAttribute: true,
computed: "_computeWorstStatus(instances)",
},
responsiveMode: {
type: String,
reflectToAttribute: true,
computed: "_computeResponsiveMode(_wide, _medium, _narrow)",
},
_collapseOpened: {
type: Boolean,
},
_wide: {
type: Boolean,
},
_medium: {
type: Boolean,
},
_narrow: {
type: Boolean,
},
};
}
ready() {
super.ready();
const clipboard = new Clipboard(this.$["copyButton"]);
this._initClipboard(clipboard);
this.$["url"].addEventListener("dragstart", this._onDrag.bind(this));
}
reloadAll() {
this.$["reloadButton"].disabled = true;
window.socket.emit("graphic:requestRefreshAll", this["graphic"], () => {
this.$["reloadButton"].disabled = false;
});
}
toggleCollapse() {
this.$["instancesCollapse"].toggle();
}
/* istanbul ignore next: we dont currently test responsiveness */
_computeResponsiveMode(_wide, _medium, _narrow) {
if (_wide) {
return "wide";
}
if (_medium) {
return "medium";
}
if (_narrow) {
return "narrow";
}
return "";
}
_initClipboard(clipboard) {
/* istanbul ignore next: cant figure out how to test these */
clipboard.on("success", () => {
this.dispatchEvent(new CustomEvent("url-copy-success", { bubbles: true, composed: true }));
});
/* istanbul ignore next: cant figure out how to test these */
clipboard.on("error", (e) => {
this.dispatchEvent(new CustomEvent("url-copy-error", { bubbles: true, composed: true }));
console.error(e);
});
}
_calcShortUrl(graphicUrl) {
return graphicUrl.split("/").slice(4).join("/");
}
_computeFullGraphicUrl(url) {
var _a;
const a = document.createElement("a");
a.href = url;
let absUrl = a.href;
if (((_a = window.ncgConfig.login) === null || _a === void 0 ? void 0 : _a.enabled) && window.token) {
absUrl += `?key=${window.token}`;
}
return absUrl;
}
_computeWorstStatus(instances) {
if (!instances) {
return "none";
}
const openInstances = instances.filter((instance) => instance.open);
if (openInstances.length <= 0) {
return "none";
}
const outOfDateInstance = openInstances.find((instance) => instance.potentiallyOutOfDate);
return outOfDateInstance ? "out-of-date" : "nominal";
}
_calcCount(singleInstance, instances) {
var _a;
if (singleInstance) {
return "S";
}
return (_a = instances === null || instances === void 0 ? void 0 : instances.filter((instance) => instance.open).length) !== null && _a !== void 0 ? _a : "?";
}
_computeCollapseIcon(_collapseOpened) {
return _collapseOpened ? "unfold-less" : "unfold-more";
}
_calcReloadAllDisabled(instances) {
return !instances || instances.length <= 0;
}
_onDrag(event) {
if (!event.target || !event.dataTransfer) {
return;
}
const dragged = event.target;
let obsURL;
if (window.ncgConfig.login.enabled && window.token) {
obsURL = `${dragged.href}&`;
}
else {
obsURL = `${dragged.href}?`;
}
obsURL += `layer-name=${this["graphic"].file.replace(".html", "")}&layer-height=${this["graphic"].height}&layer-width=${this["graphic"].width}`;
event.dataTransfer.setData("text/uri-list", obsURL);
}
}
customElements.define(NcgGraphic.is, NcgGraphic);
//# sourceMappingURL=ncg-graphic.js.map