Init WebCG

This commit is contained in:
2026-08-12 14:21:28 +09:00
commit 5c01660f97
170 changed files with 46248 additions and 0 deletions
@@ -0,0 +1,94 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>SPX - PREVIEW</title>
<link rel="stylesheet" type="text/css" href="/css/spx_renderpopups.css">
<script src="/js/socket.io.js"></script>
<script>
var socket = io();
socket.on('connect', function () {
data={};
data.name = 'SPX_PREVIEW'; // <---- Name of this socket connection
data.spxcmd = 'identifyClient';
socket.emit('SPXMessage2Server', data);
});
socket.on('SPXMessage2Client', function (data) {
// Handles messages coming from server to this client.
// All comms using 'SPXMessage2Client' as a conduit with data object and
// data.spxcmd as function identifier. Additional object values are payload.
// console.log('SPXMessage2Client received', data)
switch (data.spxcmd) {
case 'closepreview':
// console.log('Close preview window request received from Controller');
window.close();
break;
default:
// console.log('Unknown SPXMessage2Client command: ' + data.command);
}
}); // end SPXMessage2Client
</script>
</head>
<body onresize="resizePreview();" onunload="return windowClosing();">
<!-- maybe in the future
<div id="toolbar">
<div>PREVIEW</div>
<div>Clear</div>
<div>Layers</div>
</div>
-->
<div class="watermark prev"></div>
<div id="previewBG" style="background-color: rgb(0, 0, 0); overflow: hidden;">
<iframe id="previewIF"
src="/renderer?preview=true"
class="renderer"
style="width: {{width}}px; height: {{height}}px;"></iframe>
</div>
<script>
function windowClosing() {
console.log('========= Closing ============');
return true;
}
function resizePreview() {
// Preview renderer size set by SPX to {{width}}x{{height}}
let DefaultX = 1920; // Math always defaults to 16:9
let DefaultY = 1080;
const vw = Math.max(document.documentElement.clientWidth || 0, window.innerWidth || 0)
const vh = Math.max(document.documentElement.clientHeight || 0, window.innerHeight || 0)
if (DefaultX.includes('vw')) {
// Responsive settings, use window size! Added in v1.3.0
DefaultX = vw;
DefaultY = vy;
}
let Wpx = vw// document.getElementById('previewWidthRuler').offsetWidth;
let Sca = Wpx/DefaultX;
let Hpx = parseInt(DefaultY * Sca);
document.getElementById('previewBG').style.width=Wpx+'px';
document.getElementById('previewBG').style.height=Hpx+'px';
document.getElementById('previewIF').style.transform="scale("+Sca+")";
document.getElementById('previewBG').style.display='block';
// console.log('Resized to ' + Wpx + ' x ' + Hpx + ' px, scale multiplier: ' + Sca);
}
resizePreview();
</script>
</body>
</html>
@@ -0,0 +1,103 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>SPX - PROGRAM EMULATOR</title>
<link rel="stylesheet" type="text/css" href="/css/spx_renderpopups.css">
<style>
.renderer {
/* rendererscalable hardcoded to 1920x1080 */
margin: 0;
padding: 0;
border: 0px;
outline: 0px;
width: 1920px;
height: 1080px;
background-size: 10vw;
transform-origin: top left;
background-image: url('/img/checker60.png'); /* bgimage for local preview */
}
</style>
<script src="/js/socket.io.js"></script>
<script>
var socket = io();
socket.on('connect', function () {
data={};
data.name = 'SPX_PROGRAM'; // <---- Name of this socket connection
data.spxcmd = 'identifyClient';
socket.emit('SPXMessage2Server', data);
});
socket.on('SPXMessage2Client', function (data) {
// Handles messages coming from server to this client.
// All comms using 'SPXMessage2Client' as a conduit with data object and
// data.spxcmd as function identifier. Additional object values are payload.
// console.log('SPXMessage2Client received', data)
switch (data.spxcmd) {
case 'closeprogram':
// console.log('Close program window request received from Controller');
window.close();
break;
default:
// console.log('Unknown SPXMessage2Client command: ' + data.command);
}
}); // end SPXMessage2Client
</script>
</head>
<body onresize="resizePreview();" > <!-- onunload="sendWinClosedMessage('unload');" onload="sendWinClosedMessage('load');" -->
<!-- maybe in the future
<div id="toolbar">
<div>PROGRAM</div>
<div>Clear</div>
<div>Layers</div>
</div>
-->
<div class="watermark prog"></div>
<div id="previewBG" style="background-color: rgb(0, 0, 0); overflow: hidden;">
<iframe id="previewIF"
src="/renderer"
class="renderer"
ZZstyle="width: {{width}}; height: {{height}}; aspect-ratio: {{aspect}}; border: 1px solid purple;"
></iframe>
</div>
<script>
function resizePreview() {
return
// Program renderer size set by SPX to {{width}}x{{height}}
let DefaultX = 1920; // Math always defaults to 16:9
let DefaultY = 1080;
const vw = Math.max(document.documentElement.clientWidth || 0, window.innerWidth || 0)
const vh = Math.max(document.documentElement.clientHeight || 0, window.innerHeight || 0)
if (DefaultX.includes('vw')) {
// Responsive settings, use window size! Added in v1.3.0
DefaultX = vw;
DefaultY = vy;
}
let Wpx = vw// document.getElementById('previewWidthRuler').offsetWidth;
let Sca = Wpx/DefaultX;
let Hpx = parseInt(DefaultY * Sca);
document.getElementById('previewBG').style.width=Wpx+'px';
document.getElementById('previewBG').style.height=Hpx+'px';
document.getElementById('previewIF').style.transform="scale("+Sca+")";
document.getElementById('previewBG').style.display='block';
// console.log('Resized to ' + Wpx + ' x ' + Hpx + ' px, scale multiplier: ' + Sca);
}
resizePreview();
</script>
</body>
</html>
+68
View File
@@ -0,0 +1,68 @@
<!doctype html>
<html>
<head>
<meta charset="utf-8" />
<title>Example App</title>
<style>
.itemrow {
border: 0px solid green;
border-radius: 0px;
width: 100%;
padding: 10px;
margin-bottom: 1px;
box-sizing: border-box;
}
.itemrow:nth-child(odd) {
background: rgb(75, 75, 75);
}
.itemrow:nth-child(even) {
background: rgb(46, 43, 43);
}
.list {
border: 0px solid red;
width: 500px;
padding: 2px
}
.spx_input {
font-family: 'Gill Sans', 'Gill Sans MT', Calibri, 'Trebuchet MS', sans-serif;
font-size: 1.2rem;
line-height: 12px;
border: 0px;
color: white;
font-weight: bold;
padding: 2px;
width: 47%;
margin-right: 2px;
}
.input_active {
background-color: rgba(0, 0, 0, 0.5);
color: rgb(236, 224, 171);
outline: blue 2px;
}
.input_deactive {
background-color: rgba(1, 1, 1, 0);
outline: none;
}
</style>
</head>
<body>
{{>navi}}
{{{body}}}
</body>
</html>
+172
View File
@@ -0,0 +1,172 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Choose a file</title>
<style>
body {
background-color: #444;
margin: 5px;
}
div {
box-sizing: border-box;
font-family: Arial, Helvetica, sans-serif;
font-weight: bold;
font-size: 16px;
font-size: 1.6vw;
}
.spxMainPanel {
height: 55vw;
display: table;
width: 100%;
max-width: 2000px;
text-align: center;
border: 0px solid yellow;
border-collapse: separate;
border-spacing: 2px;
margin: auto;
}
.spxTopRow {
display: table-row;
height: 4em;
color: white;
border: 1px solid yellowgreen;
}
.spxBottomRow {
display: table-row;
height: 12.5em;
/* background: rgb(9, 36, 9); */
color: beige;
vertical-align: middle;
}
.spxMainSection {
height: 100%;
display: table-cell;
width: 100%;
vertical-align: middle;
border: 2px solid green;
}
.spxCell {
display: table-cell;
vertical-align: middle;
margin: 10px !important;
text-align: left;
padding: 10px;
}
.spxPanelL {
width: 50%;
color: white;
}
.spxPanelC {
width: 0.1em;
/* background: red; */
color: white;
}
.spxPanelR {
color: white;
border-left: 2px solid grey;
margin-left: 10px;
}
.headlineArea {
border-top: 2px solid grey;
vertical-align: top;
}
.btn {
padding: 0.5em 1.0em;
font-family: Arial, Helvetica, sans-serif;
font-weight: bold;
font-size: 1em;
color: white;
border: 2px solid rgb(255, 255, 255, 0.0);
}
.btn:hover {
border: 2px solid white;
}
.btn:active {
border: 2px solid black;
color: #000000;
}
.darkBg {
background-color: rgba(0, 0, 0, 0.2)
}
.bg_green {
background-color: #54af3c;
}
.bg_yellow {
background-color: #e5c310;
}
.bg_blue {
background-color: #375ea3;
}
.bg_red {
background-color: #c80019;
}
.bg_black {
background-color: #000000;
}
.bg_white {
background-color: #FFFFFF;
}
.bg_grey60 {
background-color: #2f303e;
}
.bg_grey40 {
background-color: #444758;
}
.bg_grey15 {
background-color: #706e83;
}
.headlineinput {
padding: 0.2em;
background-color: inherit;
border: 0;
font-family: inherit;
font-size: 1.4em;
margin: 0px;
color: inherit;
}
</style>
</head>
<body class="bg_black">
<div class="spxMainPanel bg_grey60">
<H1></H1>
{{{body}}}
</div>
</body>
</html>
View File
+15
View File
@@ -0,0 +1,15 @@
<!--
╔═══════════════════════════════════════════════════════════╗
║ SPX GRAPHICS CONTROLLER ║
╟───────────────────────────────────────────────────────────╢
║ (c) 2020- SPX Graphics ║
║ https://spxgraphics.com ║
║ ║
╚═══════════════════════════════════════════════════════════╝
╭───────────────────────────────────────────────────────────╮
│ Painstakingly hand coded, no frameworks. │
│ "Some extract of vanilla, enough to feed Godzilla." │
╰───────────────────────────────────────────────────────────╯
Made in 🇫🇮
-->
+80
View File
@@ -0,0 +1,80 @@
<!-- footer start -->
<div class="footerTable footer" style="background-color: rgba(0,0,0,0.2); padding:0 10 0 10px;">
<table class="wide footerTable" style="border: 0px solid green;" cellpadding=0 cellspacing=0>
<tr class="footerTable">
<td class="footerTable">
<span id="statusbar" class="footerTable statusbar"></span> <span id="working" class="working" style="display:none;"></span>
</td>
<td class="footerTable aright">
<!--
style="padding: 2px 10px;
background-color: rgb(39, 95, 58);
border-radius: 4px;
margin-right: 10px;
font-size: 90%;
cursor: pointer;
display: inline;
"
-->
<a id="upgradeBubble"
href="https://spxgc.tawk.help/article/latest-changes"
class="newVersionBubble"
title="{{lang 'hover.newversavailable'}}"
onmouseover="hvr('{{lang 'hover.newversavailable'}}')";
onmouseout="hvr('')";
>
<i class="fas fa-bullhorn"></i>&nbsp;<span id="upgradeVersion">v.X.X.X</span>&nbsp;<span id="upg"></span></a></span>
{{!-- </span> --}}
<span class="footerTable statusbar" style="opacity:0.53;">
SPX Solo v.<span id="footerVerDisplay">
<span ondblclick="forceWM()" title="Click click!">{{{ShowVersion}}}</span>
{{#ifValue width '3840px'}}
&nbsp;<span style="color: #f80;"><B> 4K </B></span>&nbsp;
{{/ifValue}}
{{#ifValue width '100vw'}}
&nbsp;<span style="color: #f80;"><B> AUTO </B></span>&nbsp;
{{/ifValue}}
<!-- add a link to register -->
{{#if config.registration.updated }}
<!-- Reg ok -->
{{else}}
<A class="footerRegister" HREF="/register"><i class="fas fa-crown"></i>&nbsp; Register</A>
{{/if}}
</span>
&nbsp;
{{#if easteregg}}
{{{easterText}}}
{{else}}
<A href="http://spxgraphics.com" target="_spxgccom">spxgraphics.com</A></span>
<span class="footerTable statusbar" style="opacity:0.33;"> | 💡 <A href="https://spxgraphics.com/contact" target="_feedback">Contact us</A></span>
{{/if}}
<!-- <span class="beta">beta</span> -->
</td>
</tr>
</table>
</div><!-- footer end -->
<script>
// show upgrade bubble if stored
if (localStorage.getItem('SPX-GC-NewVersion')){
document.getElementById('upgradeBubble').style.display="inline";
document.getElementById('upgradeVersion').innerText=localStorage.getItem('SPX-GC-NewVersion');
document.getElementById('newVersionMenuItem').style.display="block"; // Added in 1.1.0
} else {
document.getElementById('upgradeBubble').style.display="none"; // none
document.getElementById('newVersionMenuItem').style.display="none"; // none
}
function hvr(msg){
document.getElementById('upg').innerText=msg;
}
</script>
+153
View File
@@ -0,0 +1,153 @@
<div id="messageSlider" style="opacity:0;"></div>
<div class="headerRow">
<div id="logoZone" class="dropdown">
<img src="/img/spx_offline.png" style="position: absolute; display:none;" id="logo_off">
<img src="/img/spx_online.png" style="position: absolute; display:none;" id="logo_on">
<img src="/img/spx_online.png" id="logo" style="cursor: pointer;">
<div class="scrollingMenuArea scroller">
<div class="dropdown-content">
<A HREF="/"><i class="fas fa-home"></i>&nbsp; {{lang 'pagetitle.home'}}</A><br>
<A HREF="/shows/"><i class="fas fa-folder"></i>&nbsp; {{lang 'pagetitle.shows'}}...</A><BR>
<div style="font-size: 0.8em; padding-left: 1.0em; border-left: 10px solid rgba(0, 0, 0, 0.205);">
{{#each recents}}
<div id="recent{{@index}}"><i class="fas fa-file-alt"></i><A id="recent{{@index}}link" HREF="/gc/{{this}}">&nbsp; {{this}}</A></div>
{{/each}}
</div>
{{#ifValue disableConfigUI true}}
<!-- disableConfigUI:true -->
{{else}}
<HR class="menuDivider">
<A HREF="/config"><i class="fas fa-wrench"></i>&nbsp; {{lang 'pagetitle.configapp'}}...</A><BR>
<A HREF="/api/v1" target="apiWin"><i class="fas fa-file-code"></i>&nbsp; API {{lang 'button.docs'}}...</A><BR>
{{/ifValue}}
{{#ifValue page "controller"}}
<A HREF="" onClick="ModalOn('overlayHelp');return false;"><i class="far fa-keyboard"></i>&nbsp; {{lang 'button.shortcuts'}}...</A>
{{/ifValue}}
<HR class="menuDivider">
<A HREF="" onClick="spx_system('openWebRenderer');return false;"><i class="far fa-window-maximize"></i>&nbsp; {{lang 'button.showwebrenderer'}}</A><BR>
<A HREF="" onClick="spx_system('OPENWEBRENDERER_SCALABLE');return false;"><i class="far fa-window-maximize"></i>&nbsp; {{lang 'button.showwebrenderer'}} (scalable)</A>
{{{GenerateOpenFolderCommands}}}
{{#ifValue user "default"}}
<!--default user -->
{{else}}
<HR class="menuDivider">
<A HREF="/logout">Logout {{{user}}}...</A>
{{/ifValue}}
<HR class="menuDivider">
<A HREF="" onClick="spx_system('restartServer');return false;"><i class="fas fa-power-off"></i>&nbsp; Kill (restart SPX-server)</A>
<HR class="menuDivider">
<A HREF="https://html.graphics" target="_new"><i class="fas fa-shopping-cart"></i>&nbsp; html.graphics/store</A><BR>
<A HREF="https://bit.ly/joinspx" target="_new"><i class="fab fa-discord"></i>&nbsp; Join SPX Discord</A><br>
<A HREF="https://spxcloud.app" target="_new"><i class="fas fa-cloud"></i>&nbsp; Run SPX in the Cloud</A><br>
<A id="newVersionMenuItem" class="newVersionBubble" HREF="https://spxgc.tawk.help/article/latest-changes" target="_new"><i class="fas fa-bullhorn"></i>&nbsp; New version available!</A>
<!-- add a link to register -->
{{#if config.registration.updated }}
<!-- Reg ok -->
{{else}}
<A style="display: block;" class="newVersionBubble" HREF="/register"><i class="fas fa-crown"></i>&nbsp; Register for free...</A>
{{!-- <div class="registrationDiv">
<A HREF="/register/"><i class="fas fa-crown"></i>&nbsp; Register for free...</A><BR>
</div> --}}
{{/if}}
</div>
</div>
</div>
<div id="breadCrumsZone">
{{#ifValue page "appconfig"}}
<span class="caret-icon">&#9654;</span>
<span class="breadcrumbs">SPX Solo - {{lang 'pagetitle.configapp'}}</span>
{{/ifValue}}
{{#ifValue page "shows"}}
<span class="caret-icon">&#9654;</span>
<span class="breadcrumbs">{{lang 'pagetitle.shows'}}</span>
{{/ifValue}}
{{#ifValue page "showconfig"}}
<span class="caret-icon">&#9654;</span>
<span class="breadcrumbs"><A HREF="/shows" onmouseover="tip('{{lang 'hover.navishows'}}');">{{lang 'pagetitle.shows'}}</A></span>
<span class="caret-icon">&#9654;</span>
<span class="breadcrumbs"><a href="/show/{{folder}}" onmouseover="tip('{{lang 'hover.navishow'}}');">{{prettyfyBreadcrumb folder}}</a></span>
<span class="caret-icon">&#9654;</span>
<span class="breadcrumbs">{{lang 'pagetitle.configshow'}}</span>
{{/ifValue}}
{{#ifValue page "episodes"}}
<span class="caret-icon">&#9654;</span>
<span class="breadcrumbs"><A HREF="/shows" onmouseover="tip('{{lang 'hover.navishows'}}');">{{lang 'pagetitle.shows'}}</A></span>
<span class="caret-icon">&#9654;</span>
<span class="breadcrumbs">{{prettyfyBreadcrumb folder}}</span>
<div class="breadIcon">
<span onClick="projectSettings();" class="breadcrumbGear" title="Project settings"><i class="fas fa-cog"></i></span>
</div>
{{/ifValue}}
{{#ifValue page "controller"}}
<span class="caret-icon">&#9654;</span>
<span class="breadcrumbs"><A HREF="/shows" onmouseover="tip('{{lang 'hover.navishows'}}');">{{lang 'pagetitle.shows'}}</A></span>
<span class="caret-icon">&#9654;</span>
<span class="breadcrumbs"><a href="/show/{{folder}}" onmouseover="tip('{{lang 'hover.navishow'}}');">{{prettyfyBreadcrumb folder}}</a></span>
<div class="breadIcon">
<a class="breadcrumbGear"
href="/show/{{folder}}/config"
onmouseover="tip('{{lang 'button.settings'}}');"
title="{{lang 'button.settings'}}"><i class="fa fa-cog"></i></a>
</div>
<span class="caret-icon">&#9654;</span>
<span class="breadcrumbs">{{prettyfyBreadcrumb filebasename}}</span>
<div class="breadIcon">
<a class="breadcrumbGear" href="#" onClick="toggleRundownSettings(); return false;" title="Rundown options"><i class="fa fa-bars"></i></a>
</div>
{{/ifValue}}
{{{DetectCustomContentPackages}}}
<!-- What is DetectCustomContentPackages?
You probably don't need to worry about this, but:
For development purposes we can have custom "content repositories"
that are not part of the standard SPX installation. These repositories
consists of complete ASSETS and DATAROOT subfolders and both of them
will need to have a file "spx-content-repo.txt" in the root of the
folders. This file will contain the name of the repository.
The spx-content-repo.txt textfile in the "normal" content repository
should contain "CONTENT_FULL". This is used to distinguish the standard
SPX content repository from custom ones.
There is a utility batch file "SWITCH_CONTENT.BAT" that can be used
to switch between the standard SPX content repository and a custom one
while developing. When using this utility the custom content repositories
are kept in a folder called "CONTENT_REPOSITORIES" in the root of SPX
installation. Each custom repository is kept in a subfolder of this
folder and the name of the subfolder is the name of the repository and
it should also be the content of the spx-content-repo.txt text files.
When a custom content repository is detected the name of the repository
is shown in the header of the SPX UI.
Fun eh?
-->
</div>
<div id="CasparZone" class="serverButtons">
{{#ifValue page "controller"}}
{{{ServerStatus}}}
{{/ifValue}}
</div>
</div>
@@ -0,0 +1,49 @@
<!-- This is the file browser modal overlay (used by view-showconfig) -->
<div class="overlay" id="filebrowserModal">
<div class="modal wrapper">
<div class="modal_top height4">
<table class="wide" style="height:50%; border: 0px solid white; border-spacing: 0px;">
<tr style="background-color: rgba(255,255,255,0.2);">
<td valign="middle" style="font-size: 0.8em;">
&nbsp;{{lang 'pagetitle.selectfile'}}
</td>
<td valign="middle" class="aright">
<i style="font-size: 1.5em; cursor: pointer;" class="fas fa-window-close" onClick="ModalOff('filebrowserModal')"></i>&nbsp;
</td>
</tr>
</table>
<div class="curfolder" id="curfolder"></div>
</div>
<div class="modal_content content scroller">
<div id="treeview" class="filebrowser">
<UL>
<LI style="user-select: none;" id="homefolder" class="filebrowser_up" onDblClick="goHome();"><SPAN class="icon home"></i>{{lang 'general.home'}}</SPAN></LI>
<LI style="user-select: none;" id="prefolder" class="filebrowser_up" onDblClick="goUp();"><SPAN class="icon back">{{lang 'general.up'}}</SPAN></LI>
</UL>
<UL id="folderList">
</UL>
<UL id="fileList">
</UL>
</div>
</div>
<div class="modal_bottom">
<table class="wide">
<tr>
<td width="10%">
<a href="https://spxgraphics.com/store" target="_blank"><img border="0" alt="Template Store" title="SPX Store" src="/img/storeicon.png"></a></span>
</td>
<td width="60%" valign="middle">
<span id="browserMessage" class="browserMessage"></span>
</td>
<td class="spxFlexContainer aright" >
<button style="width:25%;" class="spxformtext btn spxFlexItem bg_grey15 ripple" onClick="ModalOff('filebrowserModal')">{{lang 'button.cancel'}}</button>
<button style="width:25%; opacity:0.2;" id="btnChooseTemplate" class="spxformtext btn spxFlexItem bg_blue ripple" onClick="openSelectedCSVFile();">{{lang 'button.selectemplate'}}</button>
</td>
</tr>
</table>
</div>
</div>
</div><!-- overlay ended-->
@@ -0,0 +1,53 @@
<style>
#extrabrowserHeaderBar {
cursor: move;
}
</style>
<script>
// Make the DIV element draggable:
document.addEventListener("DOMContentLoaded", function() {
dragElement(document.getElementById("extrabrowser"));
});
</script>
<!-- This is the file browser modal overlay (used by view-showconfig) -->
<div class="overlay" id="extraoverlay" id="extrabrowserModal">
<div class="modal wrapper" id="extrabrowser">
<div class="modal_top height4" id="extrabrowserHeaderBar">
<table class="wide" style="height:50%; border: 0px solid white; border-spacing: 0px;">
<tr style="background-color: rgba(255,255,255,0.2);">
<td valign="middle" style="font-size: 0.8em;">
&nbsp;{{lang 'pagetitle.selectshowextra'}}
</td>
<td valign="middle" class="aright">
<i style="font-size: 1.5em; cursor: pointer;" class="fas fa-window-close" onClick="ModalOff('extraoverlay');"></i>&nbsp;
</td>
</tr>
</table>
</div>
<div class="modal_content content scroller">
<div class="addTemplateItem row_accentExtras">
<button type="button" class="addbtn bg_blue ripple" onClick="addExtra('button');" style="float:right;">+</button>
<span class="templateName">{{lang 'config.extrabutton'}}</span><BR>
<span class="templateDesc">{{lang 'config.extrabuttondescription'}}</span>
</div>
<div class="addTemplateItem row_accentExtras">
<button type="button" class="addbtn bg_blue ripple" onClick="addExtra('togglebutton');" style="float:right;">+</button>
<span class="templateName">{{lang 'config.extratoggle'}}</span><BR>
<span class="templateDesc">{{lang 'config.extratoggledescription'}}</span>
</div>
<div class="addTemplateItem row_accentExtras">
<button type="button" class="addbtn bg_blue ripple" onClick="addExtra('selectbutton');" style="float:right;">+</button>
<span class="templateName">{{lang 'config.extraselectbutton'}}</span><BR>
<span class="templateDesc">{{lang 'config.extraselectdescription'}}</span>
</div>
</div>
</div>
</div><!-- overlay ended-->
@@ -0,0 +1,201 @@
<style>
.modal{
left: 55%;
}
.dialogHeaderBar {
cursor: move;
}
</style>
<script>
// Make the DIV element draggable:
document.addEventListener("DOMContentLoaded", function() {
dragElement(document.getElementById("dialog"));
});
</script>
<!-- This is the file browser modal overlay (used by view-showconfig) -->
<div class="overlay" id="fileoverlay" idzzz="filebrowserModal">
<div class="modal wrapper">
<div class="dialogHeaderBar" >
<div>{{lang 'pagetitle.selectemplate'}} ({{showconfig.projectFormat}})</div>
<div class="dialogHeaderBarClose">
<i class="fas fa-window-close dialogCloseButton" onClick="ModalOff('fileoverlay')"></i>
</div>
</div>
<div class="tabButtonRow" id="AddTemplatesTabBar">
<button onclick="AddTemplatesTab(1)" id="tabBtn1" class="tablinks tabSelected" >Installed templates</button>
<button id="formatSPX" class="tablinks {{#ifValue showconfig.projectFormat "SPX"}}tabSelected{{/ifValue}}">SPX</button>
<button id="formatOGRAF" class="tablinks {{#ifValue showconfig.projectFormat "OGRAF"}}tabSelected{{/ifValue}}">OGRAF</button>
<div>
<button onclick="AddTemplatesTab(2)" id="tabBtn2" class="tablinks">SPX Store</button>
<button onclick="AddTemplatesTab(3)" id="tabBtn3" class="tablinks">Loopic</button>
<button onclick="AddTemplatesTab(4)" id="tabBtn4" class="tablinks">Ferryman</button>
</div>
</div>
<div id="tab1">
<div class="modal_top">
<div class="curfolder" id="curfolder"></div>
</div>
<div class="scroller filebrowser">
<UL>
<LI style="user-select: none;" id="homefolder" class="filebrowser_up" onDblClick="goHome();"><SPAN class="icon home"></i>{{lang 'general.home'}}</SPAN></LI>
<LI style="user-select: none;" id="prefolder" class="filebrowser_up" onDblClick="goUp();"><SPAN class="icon back">{{lang 'general.up'}}</SPAN></LI>
</UL>
<UL id="folderList">
</UL>
<UL id="fileList">
</UL>
</div>
<div class="modal_bottom">
<div class="storeIcon"
onmouseover="showMessage('Visit SPX Store for more templates')"
onclick="window.open('https://spxgraphics.com/store', '_blank');"
></div>
<div style="flex-grow: 1;" >
<span id="browserMessage" class="browserMessage"></span>
</div>
<div>
<button class="btn bg_grey15 ripple" onClick="ModalOff('fileoverlay')">{{lang 'button.cancel'}}</button>
<button style="opacity:0.2;" id="btnChooseTemplate" class="btn bg_blue ripple" onClick="openSelectedFiles();">{{lang 'button.selectemplate'}}</button>
<button class="spxformtext btn bg_blue ripple"
onClick="toggleAll();"
id="allToggle"
data-all-label={{lang 'general.all'}}
data-none-label={{lang 'general.none'}}
>{{lang 'general.all'}}</button>
</div>
</div>
</div>
<div style="display: none;" id="tab2">
<div class="promoBox">
<div class="promoBoxCols">
<div class="promoLeft">
<div class="promoBoxHead">More graphics and plugins</div>
<p>
SPX Store is SPX Graphics' official Store for free and premium SPX
templates and application extensions.</p>
<br>
▶ <a href="https://spxgraphics.com/store" target="store">spxgraphics.com/store</a>
<br>
<h2>Creative Services</h2>
<p>
SPX Graphics offers creative services for businesses.
Our team of designers and developers
can help you with your next project.
</p>
<br>
<ul>
<li>Custom templates and Extensions</li>
<li>Workflow development</li>
<li>System integrations</li>
<li>SLA and Support</li>
</ul>
<br>
▶ <a href="https://spxgraphics.com/contact" target="_blank">Get in touch</a>
</div>
<div class="promoRight">
<img src="/img/SPXstore600_2023_240px.png" alt="SPX Store" title="SPX Store">
<a href="/img/promo-store.png" target="_blank">
<img src="/img/promo-store.png"
alt="SPX Store screenshot"
border=0>
</a>
</div>
</div>
</div>
</div>
<div style="display: none;" id="tab3">
<div class="promoBox">
<div class="promoBoxCols">
<div class="promoLeft">
<div class="promoBoxHead">HTML animation tool</div>
<p>
Create SPX templates with a browser based animation tool.
</p>
<p>
Loopic supports SPX template definition and generated templates
are stored straight into SPX template folder, so you can go
on-air instantly.
</p>
<br>
▶ <a href="https://app.loopic.io" target="loopic">Start Loopic</a>
<br>
<br>
<p>
Resources:<br>
▶ <a href="https://docs.loopic.io/user-guide" target="ldocs">User Guide</a><br>
▶ <a href="https://docs.spxgraphics.com/Guides/Tutorials/How+to+create+HTML+graphics+using+Loopic" target="ldocs">SPX templates using Loopic</a>
</p>
</div>
<div class="promoRight">
<img src="/img/LoopicLogov2White.svg" alt="Loopic logo">
<br><br>
<a href="/img/LoopicSPXScreenshot.png" target="_blank">
<img src="/img/LoopicSPXScreenshot.png"
alt="Loopic screenshot"
border=0>
</a>
</div>
</div>
</div>
</div>
<div style="display: none;" id="tab4">
<div class="promoBox">
<div class="promoBoxCols">
<div class="promoLeft">
<div class="promoBoxHead">SPX templates from After Effects</div>
<p>
StreamShapers' Ferryman enables graphic designers to transform their creative
ideas from animated Lottie files from Adobe After Effects into dynamic HTML templates
without any need for HTML, CSS, or JavaScript development.
</p>
<p>
Ferryman supports SPX template definition, allowing the generated templates to be
seamlessly integrated into SPX for instant playback!</p>
<br>
▶ <a href="https://www.ferryman.streamshapers.com/" target="ferryman">ferryman.streamshapers.com</a>
<br>
<br>
<p>
Resources<br>
▶ <a href="https://www.streamshapers.com/docs/streamshapers-converter/tutorial" target="fdocs">Ferryman tutorial</a><br>
▶ <a href="https://docs.spxgraphics.com/Guides/Tutorials/How+to+create+HTML+graphics+in+After+Effects" target="fdocs">SPX templates using Ferryman</a>
</p>
<br>
</div>
<div class="promoRight">
<img src="/img/promo-converter-logo.png" alt="StreamShapers logo">
<br><br>
<a href="/img/snap-ferryman.png" target="_blank">
<img src="/img/snap-ferryman.png"
alt="Ferryman screenshot"
border=0
>
</a>
</div>
</div>
</div>
</div>
</div>
</div><!-- overlay ended-->
+145
View File
@@ -0,0 +1,145 @@
<!-- This is the file browser modal overlay (used by view-showconfig) -->
<style>
#projectWrapper {
padding: 0;
font-size: 1em;
border-radius: 0.4em;
border: 0px solid #ccc;
width: clamp(300px, 50vw, 800px);
height: clamp(250px, 25vh, 500px);
height: fit-content;
box-sizing: border-box;
}
#projectWrapperHeaderBar {
width: 100%;
cursor: move;
color: white;
padding: 0.8em;
font-size: 0.8em;
background-color: rgba(0, 0, 0, 0.5);
position: relative;
}
.formTable {
width: 100%;
border-collapse: collapse;
padding: 0.5em;
border: 0px solid #ccc;
box-sizing: border-box;
}
.formTable tr td {
padding: 0.75em;
vertical-align: top;
border: 0px solid #ccc;
}
.infoTD {
width: 10%;
text-align: left;
padding: 1.5em 1.5em;
font-size: 0.8em;
white-space: nowrap;
}
#projectName{
width: 100%;
padding: 0.2em 0.5em;
font-family: 'UI-Regular';
font-size: 1.0em;
border-radius: 0.2em;
}
input[type="radio"] {
accent-color: #43c748; /* Change the color of the radio button */
height: 1.2em;
width: 1.5em;
cursor: pointer;
}
#radios {
display: flex;
flex-direction: column;
align-items: flex-start;
justify-content: center;
gap: 0.4em;
}
.thisBtns {
margin: 0.5em;
padding: 0.5em 1em;
border-radius: 0.1em;
cursor: pointer;
}
#helpBtn {
right: 0.5em;
border: none;
color: white;
cursor: pointer;
font-size: 1.2em;
position: absolute;
background-color: transparent;
}
#helpBtn:hover {
color: #43c748;
}
label{
cursor: pointer;
}
.vcenter {
vertical-align: middle !important;
}
</style>
<script>
// Make the DIV element draggable:
document.addEventListener("DOMContentLoaded", function() {
dragElement(document.getElementById("projectWrapper"));
});
</script>
<div class="overlay" id="projectFormOverlay">
<div class="modal wrapper" id="projectWrapper">
<div id="projectWrapperHeaderBar">
<span>{{lang 'hover.addshow'}}</span>
<button id="helpBtn" title="{{lang 'general.help'}}" onClick="help('projects');"><i class="far fa-question-circle"></i>&nbsp;</button>
</div>
<form id="projectForm">
<table class="formTable" >
<tr>
<td class="infoTD vcenter">Project name</td>
<td>
<input type="text" class="gcinput" id="projectName" name="projectName" required placeholder="Name of the project"/>
</td>
</tr>
<tr>
<td class="infoTD">Format</td>
<td>
<div id="radios">
<div>
<input type="radio" id="formatSPX" name="projectFormat" value="SPX" checked>
<label for="formatSPX">SPX Template</label>
</div>
<div>
<input type="radio" id="formatOGRAF" name="projectFormat" value="OGRAF">
<label for="formatOGRAF">OGraf</label>
</div>
</div>
</td>
</tr>
<tr>
<td colspan="2" style="text-align: right;">
<button type="button" class="thisBtns btn ripple bg_grey" id="cancelForm">{{lang 'button.cancel'}}</button>
<button type="submit" class="thisBtns btn ripple bg_green">Create</button>
</td>
</tr>
</table>
</form>
</div>
</div><!-- overlay ended-->
@@ -0,0 +1,70 @@
<div id="commentRow"
class = "commentrow row_accent0"
ondblclick = "ToggleExpand(this);"
onmouseover = "tip('{{lang 'hover.item'}}');">
<form name="comment" id="comment" method="post" action="/gc/saveItemChanges">
<div class="handle wide" id="commentItem" style="padding: 6px; padding-left:12px;">
<!-- COLLAPSED to small line-->
<div id="Collapsed" style="position:relative;">
<div class="vmid spxTableHeadsmall" style="text-transform:uppercase;">Rundown comment</div>
</div>
<!-- EXPANDED to an editor -->
<div id="Expanded" style="display: none;">
<table class="wide" style="border-spacing: 2px; margin-top:2px;">
<TR>
<TD colspan="3" class="vmid spxTableHeadsmall" style="height: 1.6em; text-transform:uppercase;">
<span>Opened comment</span>&nbsp;&nbsp;
</TD>
</TR>
<TR>
<TD width="65%" class="vtop">
<div class="wide HIDE">
<table class="wide spxTableHeadsmall">
<tr>
<td class="aright">ftype</td>
<td>
<input type="text" class="wide" name="RundownItem[DataFields][{{@index}}][ftype]" value="textarea"></td></tr>
</table>
</div>
<textarea rows="8"
name="commentContent"
ondblclick="event.stopPropagation();"
class="gcinput"> {{content}}</textarea><BR>
</TD>
<TD class="vtop">
<table class="wide" style="border-spacing: 5px; margin-top:0px;">
<tr>
<td colspan="2" class="vtop">
<button type="button"
onClick="saveTemplateItemChangesByElement(this.closest('.itemrow'));"
class="wide spxFlexItem btnsmall bg_grey15 ripple"
onmouseover="tip('{{lang 'hover.submitthis'}}');"
>{{lang 'button.save'}} </button>
</td>
</tr>
<tr>
<td>
Foo
</td>
<td>
Bar
</td>
</tr>
</table>
</TD>
<TD class="vtop center">
<i id="deleteLarge{{@index}}" class="l-icon far fa-trash-alt" data-spx-name="deletelarge" onClick="removeItemFromRundown(this.closest('.itemrow'));" onmouseover="tip('{{lang 'hover.deletethis'}}');"></i><BR>
</TD>
</TR>
</table>
</div>
</div>
</form>
</div>
+130
View File
@@ -0,0 +1,130 @@
<!-- This is the file browser modal overlay (used by view-showconfig) -->
<style>
#rundownWrapper {
padding: 0;
font-size: 1em;
border-radius: 0.4em;
border: 0px solid #ccc;
width: clamp(300px, 50vw, 800px);
height: clamp(250px, 25vh, 500px);
height: fit-content;
box-sizing: border-box;
}
#rundownWrapperHeaderBar {
width: 100%;
cursor: move;
color: white;
padding: 0.8em;
font-size: 0.8em;
background-color: rgba(0, 0, 0, 0.5);
position: relative;
}
.formTable {
width: 100%;
border-collapse: collapse;
padding: 0.5em;
border: 0px solid #ccc;
box-sizing: border-box;
}
.formTable tr td {
padding: 0.75em;
vertical-align: top;
border: 0px solid #ccc;
}
.infoTD {
width: 10%;
text-align: left;
padding: 1.5em 1.5em;
font-size: 0.8em;
white-space: nowrap;
}
#rundownName{
width: 100%;
padding: 0.2em 0.5em;
font-family: 'UI-Regular';
font-size: 1.0em;
border-radius: 0.2em;
}
input[type="radio"] {
accent-color: #43c748; /* Change the color of the radio button */
height: 1.2em;
width: 1.5em;
cursor: pointer;
}
#radios {
display: flex;
flex-direction: column;
align-items: flex-start;
justify-content: center;
gap: 0.4em;
}
.thisBtns {
margin: 0.5em;
padding: 0.5em 1em;
border-radius: 0.1em;
cursor: pointer;
}
#helpBtn {
right: 0.5em;
border: none;
color: white;
cursor: pointer;
font-size: 1.2em;
position: absolute;
background-color: transparent;
}
#helpBtn:hover {
color: #43c748;
}
label{
cursor: pointer;
}
.vcenter {
vertical-align: middle !important;
}
</style>
<script>
// Make the DIV element draggable:
document.addEventListener("DOMContentLoaded", function() {
dragElement(document.getElementById("rundownWrapper"));
});
</script>
<div class="overlay" id="rundownFormOverlay">
<div class="modal wrapper" id="rundownWrapper">
<div id="rundownWrapperHeaderBar">
<span>{{lang 'hover.addrundown'}}</span>
<button id="helpBtn" title="{{lang 'general.help'}}" onClick="help('projects');"><i class="far fa-question-circle"></i>&nbsp;</button>
</div>
<form id="rundownForm">
<table class="formTable" >
<tr>
<td class="infoTD vcenter">Rundown name</td>
<td>
<input type="text" class="gcinput" id="rundownName" name="rundownName" required placeholder="Name of the rundown"/>
</td>
</tr>
<tr>
<td colspan="2" style="text-align: right;">
<button type="button" class="thisBtns btn ripple bg_grey" id="cancelForm">{{lang 'button.cancel'}}</button>
<button type="submit" class="thisBtns btn ripple bg_green">{{lang 'button.addplaylist'}}</button>
</td>
</tr>
</table>
</form>
</div>
</div><!-- overlay ended-->
+50
View File
@@ -0,0 +1,50 @@
<div id="rdOptionsCollapsible">
<div style="padding: 1.0rem; font-size: 0.9rem;">
<table style="width: 100%;" cellspacing="12" border="0">
<tr>
<td width="20%" style="vertical-align: top;">
<span class="spxConfigHead">Comment</span>
</td>
<td>
<input type="text" class="wide bg_grey15 RDoptionsText" value="{{comment}}" placeholder="Experimental feature" />
</td>
</tr>
<tr>
<td width="20%" style="vertical-align: top;">
<span class="spxConfigHead inlineHelp"
onClick="help('lightmode');"
onmouseover="tip('{{lang 'hover.help'}}');"
>Light mode <i class="far fa-question-circle"></i></span>
</td>
<td class="spxConfigBody">
Light mode is an experimental, simplified version of the rundown, with less features
and it can be used for example on mobile devices or as a custom dock in OBS.<br>
<button class="btn ripple bg_blue"
style="margin-top: 0.5em; padding: 0.2em 1em;"
onClick="openLightModePanel();"
>Open panel</button>
</td>
</tr>
<!--
<tr>
<td width="30%">
<span class="spxConfigHead">Lock rundown from</span>
</td>
<td class="spxConfigHead">
[X] Content editing [X] Sorting [X] Adding and removing
</td>
</tr>
<tr>
<td width="30%">
<span class="spxConfigHead">Static background</span>
</td>
<td class="spxConfigHead">
Dropdown [V]
</td>
</tr>
-->
</table>
</div>
</div>
+5
View File
@@ -0,0 +1,5 @@
<script src="/js/socket.io.js"></script>
<script>
var serverAddress = 'ws://{{ getServerAddress }}';
var socket = io(serverAddress);
</script>
+82
View File
@@ -0,0 +1,82 @@
<!DOCTYPE html>
{{>copyright }}
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>SPX {{SPX.title}} | {{lang 'pagetitle.admin'}}</title>
<link rel="icon" type="image/png" href="/img/spx-gc.ico">
<link rel="stylesheet" type="text/css" href="/css/spx_fonts.css">
<link rel="stylesheet" type="text/css" href="/css/spx_layout.css">
<link rel="stylesheet" type="text/css" href="/css/spx_forms.css">
<link rel="stylesheet" type="text/css" href="/css/spx_colors.css">
<link rel="stylesheet" type="text/css" href="/css/spx_modal.css">
<style>
.l:link, .l:visited{color:#000000}
.l:link:hover{color:#ffd000}
.login {
width: 60%;
position: absolute;
left: 50%;
top: 1em;
transform: translate(-50%, 0%);
padding: 0.5em;
background-color: #414C5A;
color: white;
box-shadow: rgba(0, 0, 0, 0.3) 0px 0px 20px;
}
</style>
</head>
<body class="bg_body">
<form name="login" id="login" method="post" action="/">
<div class="login" style="width: 80%; border:0px solid yellow; border-radius:0.6em; padding:10px;">
<table style="width: 100%;" cellspacing="20" border="0">
<tr>
<td align="left">
<h1 style="padding:0px; margin:0px;">SPX</h1>
SPX Graphics Controller Admin Panel<BR><BR>
</td>
</tr>
</table>
<table style="width: 100%; border-radius:0.5em; background-color: #ffffff11; padding:0.4em; margin-bottom:0.7em;" cellspacing="5" border="1">
<tr style="background-color:#00000020;">
<td colspan="2" style="padding:0.4em;">LOG</td></tr>
<tr>
<td style="padding:0.4em; width:20%;" class="vtop">Last 10 logins</td>
<td style="padding:0.4em;">
12.12.2020 klo 12.22 - Windows<BR>
12.12.2020 klo 12.22 - Windows<BR>
12.12.2020 klo 12.22 - Windows<BR>
</td>
</tr>
</table>
<table style="width: 100%; border-radius:0.5em; background-color: #ffffff11; padding:0.4em; margin-bottom:0.7em;" cellspacing="5" border="1">
<tr style="background-color:#00000020;">
<td colspan="2" style="padding:0.4em;">RESET</td></tr>
<tr>
<td style="padding:0.4em; width:20%;" class="vtop">Last 10 logins</td>
<td style="padding:0.4em;">
12.12.2020 klo 12.22 - Windows<BR>
12.12.2020 klo 12.22 - Windows<BR>
12.12.2020 klo 12.22 - Windows<BR>
</td>
</tr>
</table>
</div>
</form>
</body>
</html>
+340
View File
@@ -0,0 +1,340 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>SPX API v1 Documentation</title>
<link rel="icon" type="image/png" href="/img/spx-gc.ico">
<link rel="stylesheet" type="text/css" href="/css/spx_fonts.css">
<link href="/css/bootstrap.min.css" rel="stylesheet">
<script src="/js/bootstrap.bundle.min.js"></script>
<script>
function toggleCheckbox() {
const checkbox = document.getElementById("useLocalhost");
checkbox.checked = !checkbox.checked; // Toggle the checkbox status
localhostToggle(); // Call the existing localhostToggle function
}
function localhostToggle() {
if (document.getElementById("useLocalhost").checked) {
ipAddess = "localhost:" + document.getElementById("currServerAddress").value.split(":")[1];
} else {
ipAddess = document.getElementById("currServerAddress").value;
}
document.querySelectorAll('.apiLink').forEach(function (el) {
el.innerHTML = el.innerHTML.replace(/http:\/\/[^\/]+/, "http://" + ipAddess);
let link = el.firstChild;
link.textContent = link.textContent.replace(/http:\/\/[^\/]+/, "http://" + ipAddess);
});
}
</script>
<style>
body {
font-family: "UI-Regular", Arial, sans-serif;
background-color: #e6e6e6;
padding: 2rem;
}
h1, h2, h3 {
font-family: "UI-Bold", Arial, sans-serif;
}
button:focus, .accordion-button:focus, .accordion-button:active {
box-shadow: none;
}
a,
.text-primary {
/* color: #295aaf !important; */
color: rgb(46, 126, 65) !important;
/* color: #54af3c !important; */
}
a {
transition: all 0.1s ease;
text-underline-offset: 2px;
}
a:hover {
transition: all 0.1s ease;
color: rgb(0, 0, 0) !important;
text-underline-offset: 6px;
}
.localhost-check {
margin-top: 2em;
margin-bottom: 0.5em;
background-color: transparent;
font-family: "UI-Regular", Arial, sans-serif;
font-size: 1em;
padding: 0.5em 1em;
border: 1px solid #00000000;
border-radius: 0.5rem;
cursor: pointer;
transition: background-color 0.3s ease;
width: fit-content;
user-select: none;
}
.localhost-check:hover {
/* background-color: #e9ecef; */
}
.localhost-check input[type="checkbox"] {
pointer-events: none; /* Prevents direct interaction with the checkbox */
accent-color: #295aaf;
}
.spx-host-id {
border: 1px solid #dee2e6;
border-radius: 0.5em;
padding: 0.5em 1em;
font-family: "UI-Regular", Arial, sans-serif;
background-color: #fff;
}
.strong {
font-family: "UI-Bold", Arial, sans-serif;
}
.text-muted {
font-family: 'UI-Regular', Arial, sans-serif;
}
.section-description {
font-size: 1rem;
color: rgb(33, 37, 41, 0.65) !important;
line-height: 1.5;
}
.section-title {
margin-bottom: 0.5em;
}
.accordion {
overflow: visible;
}
.accordion-item .accordion-button {
border-radius: 0.5em !important;
}
.accordion-item {
border-radius: 0.5em !important;
border: 1px solid #dee2e6;
overflow: visible;
}
.accordion-item:not(:first-of-type) {
border-top: 1px solid #dee2e6;
}
.accordion-item:not(:last-child) {
margin-bottom: 1em;
}
.accordion-button:not(.collapsed) {
background-color: transparent;
color: #000;
}
.message-info {
/* border-left-color: #295aaf !important; */
border-left-color: rgb(42, 107, 57) !important;
/* border-left-color: #54af3c !important; */
border-left: 8px solid rgb(42, 107, 57);
}
.accordion-button {
font-size: 1.2rem;
padding: 1.5em 1em;
font-weight: bold;
font-family: "UI-Bold", Arial, sans-serif;
overflow: visible;
}
.api-card {
border: 1px solid #dee2e6;
border-radius: 0.5rem;
margin-bottom: 1rem;
padding: 1rem;
background-color: #ffffff;
}
.api-card .method {
font-weight: bold;
text-transform: uppercase;
padding: 0.2rem 0.5rem;
border-radius: 0.25rem;
color: #fff;
font-family: "UI-Bold", Arial, sans-serif;
}
.api-card .method.GET {
background-color: #295aaf;
}
.api-card .method.POST {
background-color: #e57010;
}
.api-card .method.DELETE {
background-color: #dc3545;
}
.api-card .method.PUT {
background-color: #ffc107;
}
.api-card pre {
background-color: #f8f9fa;
padding: 1rem;
border-radius: 0.5rem;
font-family: "UI-Light", Arial, sans-serif;
}
.api-card a {
text-decoration: none;
color: #007bff;
font-size: 0.9em;
font-family: "UI-Regular", Arial, sans-serif;
word-break: break-all;
overflow-wrap: anywhere;
display: block;
}
.api-card a:hover {
text-decoration: underline;
}
footer {
font-family: "UI-Light", Arial, sans-serif;
}
.back, .back a {
position: absolute;
transform: translateX(-1.0em);
font-family: 'UI-Regular', Arial, sans-serif;
text-decoration: none;
color: rgb(42, 107, 57);
transition: all 0.3s ease;
}
.back:hover {
transition: all 0.3s ease;
transform: translateX(-1.1em);
color: rgb(204, 135, 6) !important;
}
code {
font-family: monospace;
background-color: hsla(214, 29%, 64%, 0.301);
padding: 0.1em 0.5em;
border-radius: 0.1em;
color: #000;
font-size: 1.05em;
}
pre > code {
background-color: transparent;
}
#titleCont {
padding-bottom: 0.5em;
display: flex;
flex-direction: row;
align-items: center;
}
#restrictionCont {
color: white;
display: flex;
font-size: 0.75em;
margin-left: 1em;
align-items: center;
padding: 0.2em 0.8em;
border-radius: 1000px;
justify-content: center;
background-color: rgba(228, 112, 16);
text-shadow: 0 0.1em 0.2em rgba(0, 0, 0, 0.5);
font-family: 'UI-Regular', Arial, sans-serif;
}
#linkCont {
display: flex;
flex-direction: row;
align-items: center;
}
</style>
</head>
<body>
<input type="hidden" id="currServerAddress" value="{{{getServerAddress}}}">
<div class="container">
<header class="mb-4">
<h1 class="text-primary"><a class="back" href="/">&lt;</a>SPX API v1</h1>
<p class="text-muted">Explore the available API endpoints provided by this SPX Graphics instance. Use the expandable sections below to find the endpoints you need. See <a href="https://docs.spxgraphics.com/" target="_blank">SPX Docs</a> for an introduction to the Control API.</p>
<small class="spx-host-id">{{license.productName}} HOST-ID: <span class="strong">{{{getHostID}}}</span></small><BR>
<div class="localhost-check" onclick="toggleCheckbox()"><input class="checkbox" type="checkbox" id="useLocalhost" onchange="localhostToggle()"> Use "localhost" addresses in examples</div>
</header>
<div class="accordion" id="apiAccordion">
{{#each functionList.sections}}
<div class="accordion-item message-info">
<h2 class="accordion-header" id="heading-{{@index}}">
<button class="accordion-button collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#collapse-{{@index}}" aria-expanded="false" aria-controls="collapse-{{@index}}">
<span class="d-flex flex-column">
<div id="titleCont">
<div class="fw-bold">{{section}}</div>
{{#if restriction}}<div id="restrictionCont"><div>{{restriction}}</div></div>{{/if}}
</div>
<p class="text-muted section-description">{{info}}</p>
</span>
</button>
</h2>
<div id="collapse-{{@index}}" class="accordion-collapse collapse" aria-labelledby="heading-{{@index}}" data-bs-parent="#apiAccordion">
<div class="accordion-body">
{{#each commands}}
<div class="api-card">
<div class="d-flex justify-content-between align-items-center">
<span class="method {{method}}">{{method}}</span>
<span class="badge bg-secondary">{{vers}}</span>
</div>
<h5 class="mt-2">
<td class="apiLink" colspan="3">
<div id="linkCont">
{{#ifValue active false}}
<div>🔒</div>
{{/ifValue}}
<A class="apiLink" HREF="http://{{{getServerAddress}}}{{../endpoint}}{{param}}">
http://{{{getServerAddress}}}{{../endpoint}}{{param}}
</A>
</div></td>
</h5>
<p class="text-muted">{{{info}}}</p>
{{#if code}}
<pre><code>{{myStringify code}}</code></pre>
{{/if}}
</div>
{{/each}}
</div>
</div>
</div>
{{/each}}
</div>
<footer class="mt-5">
<p class="text-center text-muted">
Need custom development or system integration?<BR>
<a href="https://spxgraphics.com/contact" target="_blank">Contact us</a>.<BR><BR>
<small>&copy; 2021- SPX Graphics</small>
</p>
</footer>
</div>
</body>
</html>
+651
View File
@@ -0,0 +1,651 @@
<!DOCTYPE html>
{{>copyright }}
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<script src="/js/axios.min.js"></script>
<script src="/js/fontawesome.all.min.js"></script>
<script src="/js/Sortable.min.js"></script>
<script src="/js/anime.min.js"></script>
<script src="/js/socket.io.js"></script>
<script src="/js/spx_gc.js"></script>
<title>SPX | {{lang 'pagetitle.configapp'}}</title>
<link rel="icon" type="image/png" href="/img/spx-gc.ico">
<link rel="stylesheet" type="text/css" href="/css/spx_fonts.css">
<link rel="stylesheet" type="text/css" href="/css/spx_layout.css">
<link rel="stylesheet" type="text/css" href="/css/spx_forms.css">
<link rel="stylesheet" type="text/css" href="/css/spx_colors.css">
<link rel="stylesheet" type="text/css" href="/css/spx_modal.css">
<style>
.locked {
opacity: 0.3;
pointer-events: none;
}
#locked {
padding: 1em;
display: block;
font-size: 0.7em;
text-align: center;
color: rgb(255, 255, 255);
background-color: rgb(85, 3, 3);
}
.soloInfo {
color: #aaa;
padding: 0.6em;
border-radius: 0.4em;
background-color: rgba(0, 0, 0, 0.1);
border: 1px solid rgba(0, 0, 0, 0.5);
border-bottom: 1px solid rgba(255, 255, 255, 0.2);
box-shadow: inset 0 0 0.5em rgba(0, 0, 0, 0.5);
background-image: linear-gradient(
178deg,
transparent 30%,
rgba(0, 0, 0, 0.1) 55%
);
background-repeat: no-repeat;
}
.soloInfo h1 {
margin: 0;
font-size: 0.7em;
padding-bottom: 0.5em;
}
.soloInfo p {
font-size: clamp(11px, 1vw, 20px);
opacity: 0.8;
}
</style>
</head>
<body onload="pageInit();" class="bg_body" onmouseover="tip('');">
{{#ifValue config.general.disableConfigUI true}}
<div id="locked">Configuration locked by admin.</div>
{{/ifValue}}
<div class="wrapper">
<div class="header">
{{>header page="appconfig"}}
</div><!-- header ended -->
{{#ifValue config.general.disableConfigUI true}}
<div class="content locked">
{{else}}
<div class="content">
{{/ifValue}}
<div class="centerColumn pM wrapper ">
<div id="itemList" class="content pM scroller" style="border: 0px solid yellow;">
{{#if message}}
<div class="message">{{message}}</div>
{{/if}}
{{#if error}}
<div class="messagerror">{{error}}</div>
{{/if}}
<form name="config" id="config" method="post">
<input type="hidden" name="general[username]" value="{{config.general.username}}">
<input type="hidden" name="general[password]" value="{{config.general.password}}">
<input type="hidden" name="general[hostname]" value="{{config.general.hostname}}">
<input type="hidden" name="general[greeting]" value="{{config.general.greeting}}">
<input type="hidden" name="general[showusercommapass]" value="{{config.general.showusercommapass}}">
<table class="wide" border=0>
<TR>
<td colspan="2">
<div class="soloInfo" >
<h1>SPX Solo</h1>
<p>
This SPX Solo v.{{{ShowVersion}}} is a limited version of SPX Graphics for personal use and light productions. For more advanced professional features and capabilities, consider SPX Production or SPX Broadcast.
<a href="https://spxgraphics.com/software/#compare">Read more</a>
</div>
<br>
</td>
</TR>
<TR>
<td class="vmid tableheaderText">{{lang 'config.general'}} </td>
<td class="vmid tableheaderHelp aright" title="{{lang 'general.help'}}" onClick="help('config');" onmouseover="tip('{{lang 'hover.help'}}');"><i class="far fa-question-circle"></i>&nbsp;</td>
</TR>
</table>
<hr class="h2hr"><!-- =========================================== -->
<table style="width: 100%;" cellspacing="10" border="0">
<!--
<tr onmouseover="tip('{{lang 'hover.openbrowser'}}');">
<td width="40%">
<span class="spxConfigHead">{{lang 'config.openchrome'}}</span>
</td>
<td>
{{{ OpenChromeCheck }}}
</td>
</tr>
-->
<td width="40%">
<span class="spxConfigHead">Config file</span>
</td>
<td>
<span class="spxConfigHead">{{configfile}}</span>
</td>
</tr>
<tr onmouseover="tip('{{lang 'hover.lang'}}');">
<td width="40%">
<span class="spxConfigHead">{{lang 'config.language'}}</span>
</td>
<td>
<select class="wide drop bg_grey15" name="general[langfile]">
{{{ DropdownOptionsLANG }}}
</select>
<BR>
</td>
</tr>
<tr onmouseover="tip('{{lang 'hover.loglevel'}}');">
<td width="40%">
<span class="spxConfigHead">{{lang 'config.loglevel'}} *</span>
</td>
<td>
<select class="wide drop bg_grey15" name="general[loglevel]">
<!-- error, warn, info, verbose, debug -->
{{#ifValue config.general.loglevel "error"}}
<option selected>error</option>
{{else}}
<option>error</option>
{{/ifValue}}
{{#ifValue config.general.loglevel "warn"}}
<option selected>warn</option>
{{else}}
<option>warn</option>
{{/ifValue}}
{{#ifValue config.general.loglevel "info"}}
<option selected>info</option>
{{else}}
<option>info</option>
{{/ifValue}}
{{#ifValue config.general.loglevel "verbose"}}
<option selected>verbose</option>
{{else}}
<option>verbose</option>
{{/ifValue}}
{{#ifValue config.general.loglevel "debug"}}
<option selected>debug</option>
{{else}}
<option>debug</option>
{{/ifValue}}
</select>
</td>
</tr>
<tr onmouseover="tip('{{lang 'hover.logfolder'}}');">
<td>
<span class="spxConfigHead">{{lang 'config.logfolder'}} *</span>
</td>
<td>
<input type="text" class="wide bg_grey15 cfg" placeholder="Log folder" name="general[logfolder]" value="{{config.general.logfolder}}">
</td>
</tr>
<tr onmouseover="tip('{{lang 'hover.port'}}');">
<td>
<span class="spxConfigHead">{{lang 'config.serverport'}} *&nbsp;&nbsp;<i style="color:rgba(255, 166, 0, 0.472);" class="fas fa-exclamation-triangle" title="URL port must be changed after reload, otherwise this page will not load!"></i></span>
</td>
<td>
<input type="text" class="wide bg_grey15 cfg" placeholder="port" name="general[port]" value="{{config.general.port}}">
</td>
</tr>
<tr onmouseover="tip('{{lang 'hover.dataroot'}}');">
<td>
<span class="spxConfigHead">{{lang 'config.dataroot'}} *</span>
</td>
<td>
<input type="text" class="wide bg_grey15 cfg" placeholder="Dataroot folder" name="general[dataroot]" value="{{config.general.dataroot}}">
</td>
</tr>
<tr onmouseover="tip('{{lang 'hover.preview'}}');">
<td class="vtop">
<span class="spxConfigHead">{{lang 'config.preview'}}</span>
</td>
<td>
<select class="wide drop bg_grey15" name="general[preview]">
<!-- error, warn, info, verbose, debug -->
<option value="next" disabled {{#ifValue config.general.preview "next" }}selected{{/ifValue}}>Preview next item on play</option>
<option value="selected" {{#ifValue config.general.preview "selected"}}selected{{/ifValue}}>Preview selected item on focus</option><!-- this is the default -->
<option value="none" {{#ifValue config.general.preview "none"}}selected{{/ifValue}}>Disable preview</option>
</select>
</P>
</td>
</tr>
<tr onmouseover="tip('Should local renderer play items to match the rundown state?');">
<td class="vtop">
<span class="spxConfigHead">Local renderer autoplay</span>
</td>
<td>
<select class="wide drop bg_grey15" name="general[autoplayLocalRenderer]">
<option value="true" {{#ifValue config.general.autoplayLocalRenderer "true"}}selected{{/ifValue}}>Play (if saved state is playing)</option>
<option value="false" {{#ifValue config.general.autoplayLocalRenderer "false"}}selected{{/ifValue}}>Do nothing</option>
</select>
</P>
</td>
</tr>
<tr onmouseover="tip('{{lang 'hover.resolution'}}');">
<td class="vtop">
<span class="spxConfigHead">{{lang 'config.resolution'}}</span>
</td>
<td>
<select class="wide drop bg_grey15" name="general[resolution]">
{{{generateResolutionOptions config.general.resolution}}}
</select>
<P class="spxConfigHead" style="white-space: normal; line-height: 100%; opacity:0.5; margin-top: 0.2em; margin-bottom: 0.6em;">
<small>
{{lang 'config.resolutioninfo'}}
</small>
</P>
</td>
</tr>
<!-- external audio player is disabled:
<tr onmouseover="tip('{{lang 'hover.dataroot'}}');">
<td>
<span class="spxConfigHead">{{lang 'config.audioplayer'}}</span>
</td>
<td>
<input type="text" class="wide bg_grey15 cfg" placeholder="Audioplayer path" name="general[audioplayer]" value="{{config.general.audioplayer}}">
</td>
</tr>
<tr onmouseover="tip('{{lang 'hover.htmlfolder'}}');">
<td width="40%">
<span class="spxConfigHead">{{lang 'config.playerflags'}}</span>
</td>
<td>
<input type="text" class="wide bg_grey15 cfg" placeholder="Audioplayer arguments (%FILE%)" name="general[playerflags]" value="{{config.general.playerflags}}">
</td>
</tr>
audio player -->
</table>
<BR><BR>
<table class="wide" border=0>
<TR>
<td class="vmid tableheaderText">{{lang 'config.webplayout'}}</td>
<td class="vmid tableheaderHelp aright" title="{{lang 'general.help'}}" onClick="help('webplayout');" onmouseover="tip('{{lang 'hover.help'}}');"><i class="far fa-question-circle"></i>&nbsp;</td>
</TR>
</table>
<hr class="h2hr"><!-- =========================================== -->
<table style="width: 100%;" cellspacing="5" border="0">
<tr onmouseover="tip('{{lang 'hover.playouturl'}}');">
<td width="40%">
<a href="/renderer/" target="_player"><span style="border-bottom: 1px dashed rgb(0, 140, 255);" class="spxConfigHead">{{lang 'config.webplayurl'}} *</span></a>
</td>
<td>
<input type="text" class="wide bg_grey15 cfg" style="border: 1px dashed rgba(0, 140, 255, 0.507); background-color: #575757;" placeholder="URL for OBS / vMIX" id="rendererLink" value="">
</td>
</tr>
<!--
<tr onmouseover="tip('{{lang 'hover.remoteplayouturl'}}');">
<td width="40%">
<span class="spxConfigHead">DEMO RemoteRenderer</span>
</td>
<td>
<input type="text" class="wide bg_grey15 cfg" style="border: 1px dashed rgba(0, 140, 255, 0.507); background-color: #575757;" placeholder="URL for OBS / vMIX" value="remoteserver.address.com/webrenderer/index.html?gc={{{ getServerAddress }}}">
</td>
</tr>
-->
</table>
<BR><BR>
<table class="wide" border=0>
<TR>
<td class="vmid tableheaderText">API</td>
<td class="vmid tableheaderHelp aright" title="{{lang 'general.help'}}" onClick="help('api');" onmouseover="tip('{{lang 'hover.help'}}');"><i class="far fa-question-circle"></i>&nbsp;</td>
</TR>
</table>
<hr class="h2hr"><!-- =========================================== -->
<table style="width: 100%;" cellspacing="5" border="0">
<tr onmouseover="tip('API Commands');">
<td width="40%">
<a href="/api/v1/" target="_api"><span style="border-bottom: 1px dashed rgb(0, 140, 255);" class="spxConfigHead">API endpoints</span></a>
</td>
<td>
<input type="text" class="wide bg_grey15 cfg" style="border: 1px dashed rgba(0, 140, 255, 0.507); background-color: #575757;" placeholder="List of API endpoints" id="apiLink" value="">
</P>
</td>
</tr>
</table>
<BR><BR>
<table class="wide" border=0>
<TR>
<td class="vmid tableheaderText">{{lang 'config.casparcg'}} *</td>
<td class="vmid tableheaderHelp aright" title="{{lang 'general.help'}}" onClick="help('casparcg');" onmouseover="tip('{{lang 'hover.help'}}');"><i class="far fa-question-circle"></i>&nbsp;</td>
</TR>
</table>
<hr class="h2hr"><!-- =========================================== -->
<table style="width: 100%;" cellspacing="5" border="0">
<!-- THIS NOT NEEDED, HUH?
<tr onmouseover="tip('{{lang 'hover.ccgtemplatepath'}}');">
<td width="40%">
<span class="spxConfigHead">{{lang 'config.ccgtemplates'}}*</span>
</td>
<td>
<input type="text" class="wide bg_grey15 cfg" placeholder="From casparcg.config" name="casparcg[relativeTemplateFolder]" value="{{config.casparcg.relativeTemplateFolder}}">
</td>
</tr>
-->
{{#each config.casparcg.servers}}
<tr>
<td width="40%">
<span class="spxConfigHead">
{{#ifValue @index 0}}
{{lang 'config.ccgservers'}}
{{else}}
&nbsp;
{{/ifValue}}
</span>
</td>
<td class="spxFlexContainer" style="padding:0px; margin:0px;" >
<span class="spxConfigHead">{{@index}}</span>
<input type="text" class="bg_grey15 cfg" style="margin:0px 4px 0px 0px; width:30%;" name="casparcg[servers][{{@index}}][name]" onmouseover="tip('{{lang 'hover.ccgserver'}}');" placeholder="Server name" value="{{name}}">
<input type="text" class="bg_grey15 cfg" style="margin:0px 4px 0px 0px; width:30%;" name="casparcg[servers][{{@index}}][host]" onmouseover="tip('{{lang 'hover.ccghost'}}');" placeholder="ip" value="{{host}}">
<input type="text" class="bg_grey15 cfg" style="margin:0px 0px 0px 0px; width:30%;" name="casparcg[servers][{{@index}}][port]" onmouseover="tip('{{lang 'hover.ccgport'}}');" placeholder="port" value="{{port}}">
</td>
</tr>
{{/each}}
<tr>
<td width="40%">
&nbsp;
</td>
<td class="spxFlexContainer" style="padding:0px; margin:0px;" onmouseover="tip('{{lang 'hover.addccgserver'}}');">
<span class="spxConfigHead">+</span>
<input type="text" class="bg_grey15 cfg" style="margin:0px 0px 0px 0px; width:30%;" name="NewName" placeholder="Server name" value="">
<input type="text" class="bg_grey15 cfg" style="margin:0px 0px 0px 5px; width:30%;" name="NewHost" placeholder="ip" value="">
<input type="text" class="bg_grey15 cfg" style="margin:0px 0px 0px 5px; width:30%;" name="NewPort" placeholder="port" value="">
</td>
</tr>
<tr>
<td width="40%">
&nbsp;
</td>
<td class="spxFlexContainer" style="padding:0px; margin:0px;" onmouseover="tip('{{lang 'hover.addccgserver'}}');">
<P class="spxConfigHead"
style="white-space: normal; line-height: 100%; opacity:0.5;">
<small><br>{{lang 'config.restartinfo'}}</small>
</td>
</tr>
</table>
<BR>
<!-- /////////////// extras starts ///////////////// -->
<table class="wide" border=0>
<TR>
<td class="vmid tableheaderText">{{lang 'config.globalbuttons'}} *</td>
<td class="vmid tableheaderHelp aright" title="{{lang 'general.help'}}" onClick="help('globalextras');" onmouseover="tip('{{lang 'hover.help'}}');"><i class="far fa-question-circle"></i>&nbsp;</td>
</TR>
</table>
<hr class="h2hr"><!-- =========================================== -->
<!-- script linker -->
<table style="width: 100%;" cellspacing="5" border="0">
<tr onmouseover="tip('{{lang 'hover.confjsfile'}}');">
<td width="40%">
<span class="spxConfigHead">{{lang 'config.functionlibrary'}}</span>
</td>
<td>
<input type="text" class="wide bg_grey15 cfg" placeholder="/path/in/ASSETS/filename.js" name="globalExtras[customscript]" value="{{config.globalExtras.customscript}}">
</td>
</tr>
</table>
<table style="width: 100%;" cellspacing="5" border="0">
<tr>
<td width="40%" class="vtop">
<span class="spxConfigHead">{{lang 'config.globalbuttons'}}</span>
</td>
<td><!-- button list starts -->
{{#each config.globalExtras.CustomControls}}
<!-- each extra button -->
<div id="extra{{@index}}">
<button class="collapsible globalCollapsible bg_grey15 row_accentExtras" type="button">{{description}}</button>
<div class="collapsiblecontent spxConfigHead" >
<input type="hidden" name="globalExtras[CustomControls][{{@index}}][ftype]" value="{{ftype}}">
<table class="wide" style="border: 0px solid pink; border-spacing: 10px; border-collapse: separate;" border="0">
<tr onmouseover="tip('{{lang 'hover.buttontype'}}');">
<td><span class="spxConfigHead">{{lang 'config.buttontype'}}</span></td>
<td><span class="spxConfigHead">{{ftype}}</span></td>
</tr>
<tr onmouseover="tip('{{lang 'hover.colors'}}');">
<td width="40%"><span class="spxConfigHead">{{lang 'config.buttoncolor'}}</span></td>
<td>
<select class="wide drop bg_grey15" name="globalExtras[CustomControls][{{@index}}][bgclass]">
{{{generateButtonColorClasses bgclass}}}
</select>
</td>
</tr>
{{#ifValue ftype "button"}}
<!-- something like click here -->
<tr onmouseover="tip('{{lang 'hover.text'}}');">
<td><span class="spxConfigHead">{{lang 'config.buttontext'}}</span></td>
<td>
<input type="text" class="wide bg_grey15 cfg" placeholder="Caption" name="globalExtras[CustomControls][{{@index}}][text]" value="{{text}}">
</td>
</tr>
<tr onmouseover="tip('{{lang 'hover.fcall'}}');">
<td><span class="spxConfigHead">{{lang 'config.functionhandler'}}</span></td>
<td><input type="text" class="wide bg_grey15 cfg" placeholder="clickHandler()" name="globalExtras[CustomControls][{{@index}}][fcall]" value="{{fcall}}"></td>
</tr>
{{/ifValue}}
{{#ifValue ftype "selectbutton"}}
<!-- something like choose an item here -->
<tr onmouseover="tip('{{lang 'hover.text'}}');">
<td><span class="spxConfigHead">{{lang 'config.buttontext'}}</span></td>
<td>
<input type="text" class="wide bg_grey15 cfg" placeholder="Caption" name="globalExtras[CustomControls][{{@index}}][text]" value="{{text}}">
</td>
</tr>
<tr onmouseover="tip('{{lang 'hover.fcall'}}');">
<td><span class="spxConfigHead">{{lang 'config.functionhandler'}}</span></td>
<td><input type="text" class="wide bg_grey15 cfg" placeholder="clickHandler()" name="globalExtras[CustomControls][{{@index}}][fcall]" value="{{fcall}}"></td>
</tr>
{{/ifValue}}
{{#ifValue ftype "togglebutton"}}
<!-- something like turn it on or off -->
<tr>
<td><span class="spxConfigHead">{{lang 'config.buttontexts'}}</span></td>
<td>
<input onmouseover="tip('{{lang 'hover.text'}}');" type="text" style="width: 49%;" class="bg_grey15 cfg" placeholder="ON" name="globalExtras[CustomControls][{{@index}}][text0]" value="{{text0}}">
<input onmouseover="tip('{{lang 'hover.text1'}}');" type="text" style="width: 49%;" class="bg_grey15 cfg" placeholder="OFF" name="globalExtras[CustomControls][{{@index}}][text1]" value="{{text1}}">
</td>
</tr>
<tr onmouseover="tip('{{lang 'hover.fcall'}}');">
<td><span class="spxConfigHead">{{lang 'config.functionhandler'}}</span></td>
<td><input type="text" class="wide bg_grey15 cfg" placeholder="toggleHandler()" name="globalExtras[CustomControls][{{@index}}][fcall]" value="{{fcall}}"></td>
</tr>
{{/ifValue}}
<tr onmouseover="tip('{{lang 'hover.extradescr'}}');">
<td><span class="spxConfigHead">{{lang 'config.functiondescription'}}</span></td>
<td><input type="text" class="wide bg_grey15 cfg" placeholder="Description" name="globalExtras[CustomControls][{{@index}}][description]" value="{{description}}"></td>
</tr>
</table>
<table class="wide" style="padding: 10px; margin-bottom:10px;">
<tr>
<td width="25%">
<button type="button" onmouseover="tip('{{lang 'hover.removeglobalextra'}}');" class="wide spxFlexItem btnsmall bg_grey10 ripple black" onClick="notImplemented();">{{lang 'button.deletebutton'}}</button>
</td>
<td>&nbsp;</td>
<td width="25%" class="center">
<!-- <button type="button" class="wide spxFlexItem btnsmall bg_grey10 ripple black" onClick="alert('Not implemented. Modify config.json manually.');">{{lang 'button.save'}}</button> -->
</td>
</tr>
</table>
</form>
</div><!--ended extra item -->
</div><!-- extra index ended -->
<!-- //each extra button -->
{{/each}}
<div class="wide" style="text-align: center; padding:0.5rem;">
<button type="button" class="addbtn bg_blue ripple" onmouseover="tip('{{lang 'hover.browseglobalextras'}}');" onClick="notImplemented();">+</button>
</div>
</td><!-- button list ended -->
</tr>
</table>
<!-- /////////////// extras ended ///////////////// -->
<HR><!-- save / cancel buttons -->
<center>
<span class="spxConfigHead">* {{lang 'config.restartinfo'}}</span>
</center>
<BR>
<div class="spxFlexContainer">
<button onmouseover="tip('{{lang 'hover.saveconfig'}}');" class="wide spxFlexItem btn bg_green ripple" type="submit" form="config">{{lang 'button.save'}}</button>
<button type="button" onmouseover="tip('{{lang 'hover.cancelconfig'}}');" class="wide spxFlexItem btn bg_red ripple" onClick="location.href='/';">{{lang 'button.cancel'}}</button>
</div>
</form>
</div><!-- itemlist ended -->
</div><!-- center wrapper ended -->
</div><!-- content ended -->
{{>footer}}
</div><!-- MAINwrapper ended -->
<script>
var RendererUrl = location.protocol+'//'+location.hostname+(location.port ? ':'+location.port: '') + '/renderer/';
document.getElementById('rendererLink').value = RendererUrl;
var APIUrl = location.protocol+'//'+location.hostname+(location.port ? ':'+location.port: '') + '/api/v1/';
document.getElementById('apiLink').value = APIUrl;
// COLLAPSIPLE HANDLERS
var coll = document.getElementsByClassName("collapsible");
var i;
for (i = 0; i < coll.length; i++) {
coll[i].addEventListener("click", function() {
this.classList.toggle("active");
var content = this.nextElementSibling;
if (content.style.maxHeight){
content.style.maxHeight = null;
} else {
content.style.maxHeight = content.scrollHeight + "px";
}
});
}
function notImplemented() {
// request .....
// returns .....
// Describe the function here
alert('Not implemented yet.\n\nPlease modify config.json manually. Read help for additional information.');
} // NotImplemented ed
// See spx_gc.js
function pageInit(){
heartbeat(203); // page identifier
spxInit('');
}
</script>
</body>
</html>
+130
View File
@@ -0,0 +1,130 @@
<!DOCTYPE html>
{{>copyright }}
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>SPX {{SPX.title}} | {{lang 'pagetitle.authpolicy'}}</title>
<link rel="icon" type="image/png" href="/img/spx-gc.ico">
<link rel="stylesheet" type="text/css" href="/css/spx_fonts.css">
<link rel="stylesheet" type="text/css" href="/css/spx_layout.css">
<link rel="stylesheet" type="text/css" href="/css/spx_forms.css">
<link rel="stylesheet" type="text/css" href="/css/spx_colors.css">
<link rel="stylesheet" type="text/css" href="/css/spx_modal.css">
<style>
.l:link, .l:visited{color:#000000}
.l:link:hover{color:#ffd000}
.login {
width: 50%;
position: absolute;
left: 50%;
top: 40%;
transform: translate(-50%, -40%);
z-index: 20;
padding: 0.3em;
background-color: #414C5A;
color: white;
box-shadow: rgba(0, 0, 0, 0.3) 0px 0px 20px;
vertical-align: middle;
}
</style>
<script>
function validate(){
uField = document.getElementById('user');
pField = document.getElementById('pass');
rField = document.getElementById('radio1');
if (rField.checked){
if (uField.value=="" || pField.value==""){
alert('To use login, both username and password must be given. Please try again.');
return false;
}
}
return true;
}
</script>
</head>
<body class="bg_body">
<form name="login" id="login" method="post" action="/saveauthpolicy" onsubmit="return validate()">
<div>
<div>
<div class="login" style="border-radius:0.6em;">
<table style="width: 100%; height:100%; border:0px solid yellow;" cellspacing="20" border="0">
<tr>
<td align="left">
SPX Graphics Controller Config<BR><BR>
<span class="spxInfotext">
{{lang 'config.authpolicy'}}
</span>
<BR>
</td>
</tr>
<tr>
<td align="center">
<table style="width: 100%; border-radius:0em; background-color: #0000003a; border:0px solid yellow;" cellspacing="15" border="0">
<tr>
<td class="vmid spxInfotext" style="width: 30%;">
<label class="radiocontainer">{{lang 'general.yes'}}
<input type="radio" name="policy" value="1" id="radio1">
<span class="checkmark"></span>
</label>
</td>
<td class="vmid spxInfotext">
<input type="text" class="wide gcinput" name="username" id="user" value="{{user}}">
<input type="password" class="wide gcinput" name="password" id="pass" value="">
</td>
</tr>
</table>
<table style="width: 100%; border-radius:0em; background-color: #0000003a; border:0px solid yellow; margin-top: 0.2em;" cellspacing="15" border="0">
<tr>
<td class="vmid spxInfotext" style="width: 30%;">
<label class="radiocontainer">{{lang 'general.no'}}
<input type="radio" checked="checked" name="policy" value="0">
<span class="checkmark"></span>
</label>
</td>
<td class="vmid spxInfotext">
{{lang 'config.authnone' }}
</td>
</tr>
</table>
<table style="width: 100%; border-radius:0em; background-color: #0000003a; border:0px solid yellow; margin-top: 0.2em;" cellspacing="25" border="0">
<tr>
<td class="vmid">
<div class="wide spxInfotext center" style="font-size: 0.55em;">
{{lang 'config.authinfo' }}
<BR>
<BR>
<input type="submit" class="spxformtext btn w50 bg_green ripple" value="{{lang 'button.save'}}" style="margin:0.0em 0em 0.4em; font-size:1.2em;">
</div>
</td>
</tr>
</table>
<span style="font-size: 0.55em; opacity:0.9; color:#000000;">
v.{{{ShowVersion}}} | <a class="l" href="https://spxgraphics.com">&copy; 2020- SPX Graphics</a></span>
</td>
</tr>
</table>
</div><!-- center wrapper ended -->
</div><!-- content ended -->
</div><!-- MAINwrapper ended -->
</form>
</body>
</html>
File diff suppressed because it is too large Load Diff
+822
View File
@@ -0,0 +1,822 @@
<!DOCTYPE html>
{{>copyright }}
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<script src="/js/axios.min.js"></script>
<script src="/js/fontawesome.all.min.js"></script>
<!-- <script src="/js/Sortable.min.js"></script> Not in LIGHT -->
<script src="/js/socket.io.js"></script>
<script src="/js/vanilla-js-tooltip.js"></script>
<!-- <script src="/js/spx_controllerImportFunctions.js"></script> -->
<script src="/js/spx_gc.js"></script>
<script src="/js/anime.min.js"></script>
<script src="/js/lib/vanilla-picker.csp.min.js"></script> <!-- added in 1.1.1 -->
<script>
var socket = io();
function toggle(btn) {
btn.classList.toggle("toggled");
if (btn.id == "btnPrv") {
if ( btn.classList.contains("toggled")) {
document.getElementById("previewBG").style.display = "block";
document.getElementById("previewIF").src = "/renderer?preview=true";
} else {
document.getElementById("previewBG").style.display = "none";
document.getElementById("previewIF").src = "/templates/empty.html";
}
}
if (btn.id == "btnRun") {
if ( btn.classList.contains("toggled")) {
document.getElementById("rundownElements").style.display = "block";
} else {
document.getElementById("rundownElements").style.display = "none";
}
}
if (btn.id == "btnPlu") {
if ( btn.classList.contains("toggled")) {
document.getElementById("controllerPluginButtons").style.display = "block";
} else {
document.getElementById("controllerPluginButtons").style.display = "none";
}
}
}
function loadRundown(element) {
let rundown = element.value;
let url = "/gc/" + rundown + "/light";
window.location.href = url;
}
</script>
<title>SPX Light | {{lang 'pagetitle.controller'}}</title>
<link rel="icon" type="image/png" href="/img/spx-gc.ico">
<link rel="stylesheet" type="text/css" href="/css/spx_fonts.css">
<link rel="stylesheet" type="text/css" href="/css/spx_layout-mini.css">
<link rel="stylesheet" type="text/css" href="/css/spx_forms.css">
<link rel="stylesheet" type="text/css" href="/css/spx_colors.css">
<link rel="stylesheet" type="text/css" href="/css/spx_modal.css">
<link rel="stylesheet" type="text/css" href="/css/spx_accents.css">
<link rel="stylesheet" type="text/css" href="/css/vanilla-js-tooltip.css" />
<link rel="stylesheet" type="text/css" href="/css/vanilla-picker.csp.css" >
<link rel="stylesheet" type="text/css" href="/css/toggle2.css">
<link rel="stylesheet" type="text/css" href="/css/toggle.css">
<style>
.headlineRow {
display: flex;
font-size: 1.1em;
padding: 0.4em;
gap: 0.3em;
font-family: 'UI-Regular';
align-items: center;
justify-content: center;
}
.headlineRow > * {
height:90%;
}
#logo {
border: 0px solid yellow;
}
.headlineRow > button {
font-size: 0.6em;
padding: 0.1em 0.7em;
text-transform: uppercase;
color: rgba(255, 255, 255, 0.479);
font-family: 'UI-Light';
border: 0;
opacity: 0.8;
border-radius: 0.3em;
background-color: rgba(0, 0, 0, 0.1);
border-top: 1px solid rgba(255, 255, 255, 0.25);
}
.headlineRow > button:hover {
cursor: pointer;
opacity: 1.0;
}
.toggled {
border: 0 !important;
color: #fff !important;
box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.4);
background-color: rgba(22, 150, 22, 0.2)!important;
border-bottom: 1px solid rgba(255, 255, 255, 0.25) !important;
margin-top: 1px;
}
#previewWidthRuler {
border: 0;
opacity: 0 !important;
margin: 0;
}
#footer > button {
flex-basis: 50%;
margin: 0.2em;
}
#rundownSelector {
flex-grow: 1;
margin: 0.1em;
padding: 0.1em;
font-size: 0.9em;
background-color: #0008;
color: white;
font-family: 'UI-Regular';
border: 0;
}
#controllerPluginButtons {
display: none;
padding: 1em;
}
select option:disabled {
color:rgb(246, 246, 243);
font-weight:bold;
opacity: 1;
}
option.optionProject {
font-family: 'UI-Bold';
color: white;
}
option.optionRundown {
color: #fff !important;
margin-left: 1em !important;
}
option.optionEmpty {
{{!-- user-select: none;
color: #00f !important; --}}
}
</style>
</head>
<body onresize="resizePreview();" class="bg_body">
<input type="hidden" id="foldername" value="{{folder}}"> <!-- just the foldername "showname" -->
<input type="hidden" id="filebasename" value="{{filebasename}}"> <!-- just the filename "listname" -->
<input type="hidden" id="datafile" value="{{datafile}}"> <!-- full file path "C:/dataroot/showname/listname.json" -->
<input type="hidden" id="profiledata" value="{{profiledata}}"> <!-- full contents of the "gc_list.json" profilefile as stringified json -->
<input type="hidden" id="background" value="{{background}}"> <!-- background.png -->
<input type="hidden" id="csvlist" value="{{csvFileList}}"> <!-- tree data from ASSETS/csv -->
<input type="hidden" id="previewMode" value="{{previewMode}}"> <!-- previewMode (next/selected) -->
<input type="hidden" id="assetsroot" value="{{assetsFolder}}"> <!-- used by file browser -->
<input type="hidden" id="templaterootmassage" value="{{lang 'error.preventBrowseUp'}}">
<div id="messageSlider" style="opacity:0;"></div>
<div class="wrapper" onmouseover="tip('');">
<div class="headlineRow">
<img src="/img/spx_offline.png" style="position: absolute; display:none;" id="logo_off">
<img src="/img/spx_online.png" style="position: absolute; display:none;" id="logo_on">
<a href="" onClick="loadController">
<img src="/img/spx_online.png" id="logo">
</a>
<select id="rundownSelector" onChange="loadRundown(this)">
</select>
<button id="btnPrv"onclick="toggle(this);" class="toggled">Preview</button>
<button id="btnRun"onclick="toggle(this);" class="toggled">Rundown</button>
<button id="btnPlu"onclick="toggle(this);" class="">Plugins</button>
</div>
<div class="viewer">
<HR id="previewWidthRuler"> <!-- =========================================================== -->
<div id="previewBG"
style="background-color: rgb(0, 0, 0);
width:100%;
height:27%;
overflow: hidden;
display: block;
opacity:1.0;">
<iframe
width="{{{localPreviewSize width}}}"
height="{{{localPreviewSize height}}}"
id="previewIF"
src="/renderer?preview=true"
class="rendererMini"
></iframe>
</div>
</div><!-- viewer -->
<div id="itemList" class="minicontent scroller">
<div id="rundownElements">
{{#if filedata.templates}}
<!-- // -->
{{else}}
<div style="margin: 3%; padding: 2%; font-size: 0.7em; background-color: rgba(0,0,0,0.2); border-radius: 1em; text-align: center; opacity: 0.5;" >
<h2>Empty rundown</h2><BR>
</div>
{{/if}}
<!-- RUNDOWN ITEMS aka TEMPLATES -->
{{#each filedata.templates}}
{{#if DataFields}}
<div id="playlistitem{{@index}}"
class = "itemrow row_accent{{uicolor}}"
data-spx-epoch = {{itemID}}
data-spx-index = {{@index}}
data-spx-changed = "false"
data-spx-onair = "{{onair}}"
data-spx-timerid = ""
data-spx-stepsleft = "{{steps}}"
onClick = "focusRow(this);"
ondblclick = "ToggleExpand(this);"
old-ondblclick = "ToggleExpand({{@index}});"
onmouseover = "tip('{{lang 'hover.item'}}');">
<form name="templates[{{@index}}]" id="form{{@index}}" method="post" action="/gc/saveItemChanges">
<input type="hidden" name="foldername" value="{{../folder}}">
<input type="hidden" name="listname" value="{{../filebasename}}">
<input type="hidden" name="rundownfile" value="{{../datafile}}">
<input type="hidden" name="TemplIndex" value="{{@index}}">
<div class="handle wide" id="item{{@index}}" style="padding: 6px; margin-bottom:5px; padding-left:12px; padding-bottom:12px;">
<!-- COLLAPSED to small line-->
<div id="Collapsed" style="position:relative;">
<div style="position:absolute; right:10px; z-index:50; font-size:1.5em; top:0.6em;">
<i id="playIcon{{@index}}" data-spx-name="icon" class="fas fa-play {{{playIconClass onair}}}"></i>
<!-- Normal play="fas fa-play", Auto play="far fa-play-circle" -->
</div>
<table class="wide" style="border-spacing: 2px; margin-top: 2px;">
<TR>
<TD class="vmid spxTableHeadsmall" style="height: 1.6em; text-transform:uppercase;">
<span><!--item {{@index}} &nbsp;&middot;&nbsp;-->{{{ nameFromTemplatePath relpath }}}
{{#if description}}
<span class="delim"></span> <span style="opacity:0.5;">{{description}}</span>
{{/if}}
</span>
</TD>
<TD class="vtop aright spxTableHeadsmall">
&nbsp;
<!-- hide delete in LIGHT MODE
<i id="deleteSmall{{@index}}" data-spx-name="deletesmall" class="fas fa-times" onClick="removeItemFromRundown(this.closest('.itemrow'));" onmouseover="tip('{{lang 'hover.delete'}}');"></i>
-->
</TD>
</TR>
</table>
<div style="width:100%; " class="truncate">
{{{ generateCollapsedHeadline DataFields }}}
</div>
</div>
<!-- EXPANDED to an editor -->
<div id="Expanded" style="display: none;">
<table class="wide" style="border-spacing: 2px; margin-top:2px;">
<TR>
<TD colspan="3" class="vmid spxTableHeadsmall" style="height: 1.6em; text-transform:uppercase;">
<span>{{{nameFromTemplatePath relpath}}}</span>&nbsp;&nbsp;
<i class="l-icon fas fa-edit" onClick="openRelpathFolder(this.closest('.itemrow'));" onmouseover="tip('{{lang 'button.edit'}}');"></i>
</TD>
</TR>
<TR>
<TD width="65%" class="vtop">
{{#each DataFields}}
<!-- COMMON FIELD DATA -->
<div class="wide HIDE">
<table class="wide spxTableHeadsmall">
<tr><td class="aright">field</td> <td><input type="text" class="wide" name="RundownItem[DataFields][{{@index}}][field]" value="{{field}}" ></td></tr>
<tr><td class="aright">title</td> <td><input type="text" class="wide" name="RundownItem[DataFields][{{@index}}][title]" value="{{title}}" ></td></tr>
</table>
</div>
{{#ifValue ftype "spacer"}}
<br><br>
{{/ifValue}}
{{#ifValue ftype "number"}}
<!-- "NUMBER" TYPE OF TEMPLATE FIELD (added in 1.0.7) -->
<div class="wide HIDE">
<table class="wide spxTableHeadsmall">
<tr><td class="aright">ftype</td> <td><input type="text" class="wide" name="RundownItem[DataFields][{{@index}}][ftype]" value="number"></td></tr>
</table>
</div>
<input type="number" data-tooltip="{{title}}" data-position="right center" name="RundownItem[DataFields][{{@index}}][value]" data-update="{{field}}" data-role="userEditable" value="{{value}}" placeholder="{{title}}" ondblclick="event.stopPropagation();" onkeypress="javascript:applyTextEditChanges(event,this);" class="gcinput"><BR>
{{/ifValue}}
{{#ifValue ftype "instruction"}}
<!-- "INSTRUCTION" TYPE OF TEMPLATE FIELD -->
<div class="wide HIDE">
<table class="wide spxTableHeadsmall">
<tr><td class="aright">ftype</td> <td><input type="text" class="wide" name="RundownItem[DataFields][{{@index}}][ftype]" value="instruction"></td></tr>
</table>
</div>
<div class="instruction">{{value}}</div>
<input type="hidden" name="RundownItem[DataFields][{{@index}}][value]" value="{{value}}">
{{/ifValue}}
{{#ifValue ftype "caption"}}
<!-- "CAPTION" TYPE OF TEMPLATE FIELD -->
<div class="wide HIDE">
<table class="wide spxTableHeadsmall">
<tr><td class="aright">ftype</td> <td><input type="text" class="wide" name="RundownItem[DataFields][{{@index}}][ftype]" value="caption"></td></tr>
</table>
</div>
<span class="caption">{{value}}</span><BR>
<input type="hidden" name="RundownItem[DataFields][{{@index}}][value]" value="{{value}}">
{{/ifValue}}
{{#ifValue ftype "hidden"}}
<!-- "HIDDEN" TYPE OF TEMPLATE FIELD -->
<div class="wide HIDE">
<table class="wide spxTableHeadsmall">
<tr><td class="aright">ftype</td> <td><input type="text" class="wide" name="RundownItem[DataFields][{{@index}}][ftype]" value="hidden"></td></tr>
</table>
</div>
<span class="caption">{{title}}<BR><BR>{{field}} value:<BR>{{value}}</span><BR>
<input type="hidden" name="RundownItem[DataFields][{{@index}}][value]" value="{{value}}">
{{/ifValue}}
{{#ifValue ftype "textfield"}}
<!-- "TEXTFIELD" TYPE OF TEMPLATE FIELD -->
<div class="wide HIDE">
<table class="wide spxTableHeadsmall">
<tr><td class="aright">ftype</td> <td><input type="text" class="wide" name="RundownItem[DataFields][{{@index}}][ftype]" value="textfield"></td></tr>
</table>
</div>
<input type="text" data-tooltip="{{title}}" data-position="right center" name="RundownItem[DataFields][{{@index}}][value]" data-update="{{field}}" data-role="userEditable" value="{{value}}" placeholder="{{title}}" ondblclick="event.stopPropagation();" onkeypress="javascript:applyTextEditChanges(event,this);" class="gcinput"><BR>
{{/ifValue}}
{{#ifValue ftype "checkbox"}}
<!-- "CHECKBOX" TYPE OF TEMPLATE FIELD (added in 1.0.10) -->
<div class="wide HIDE">
<table class="wide spxTableHeadsmall">
<tr><td class="aright">ftype</td> <td><input type="text" class="wide" name="RundownItem[DataFields][{{@index}}][ftype]" value="checkbox"></td></tr>
</table>
</div>
<div class="gccheckboxdiv" style="white-space: nowrap;">
<label class="gccheckcontainer">{{title}}
<BR>
{{#ifValue value "1"}}
<input type="checkbox" name="RundownItem[DataFields][{{@index}}][value]" data-update="{{field}}" data-role="userEditable" class="gccheckbox" checked>
{{else}}
<input type="checkbox" name="RundownItem[DataFields][{{@index}}][value]" data-update="{{field}}" data-role="userEditable" class="gccheckbox">
{{/ifValue}}
<span class="gccheckmark"></span>
</label>
</div>
<!--<div style="white-space: nowrap;">
<label for="checkbox{{@index}}">{{title}}</label><br>
</div> -->
{{/ifValue}}
{{#ifValue ftype "button"}}
<!-- "BUTTON" TYPE OF TEMPLATE FIELD (added in 1.0.11) -->
<div class="wide HIDE">
<table class="wide spxTableHeadsmall">
<tr><td class="aright">ftype</td> <td><input type="text" class="wide" name="RundownItem[DataFields][{{@index}}][ftype]" value="button"></td></tr>
<tr><td class="aright">fcall</td> <td><input type="text" class="wide" name="RundownItem[DataFields][{{@index}}][fcall]" value="{{fcall}}"></td></tr>
<tr><td class="aright">title</td> <td><input type="text" class="wide" name="RundownItem[DataFields][{{@index}}][title]" value="{{title}}"></td></tr>
<tr><td class="aright">descr</td> <td><input type="text" class="wide" name="RundownItem[DataFields][{{@index}}][descr]" value="{{descr}}"></td></tr>
</table>
</div>
<div class="gcpluginbuttondiv" style="white-space: nowrap;">
<button type="button"
onClick="{{fcall}}"
class="btnsmall ripple bg_grey15"
>{{title}}</button>&nbsp; &nbsp;<span class="gcplugindescription">{{descr}}</span>
</div>
{{/ifValue}}
{{#ifValue ftype "filebrowser"}}
<!-- "FILEBROWSER" TYPE OF TEMPLATE FIELD (added in 1.1.1) -->
<div class="wide HIDE" >
<table class="wide spxTableHeadsmall">
<tr><td class="aright">ftype</td> <td><input type="text" class="wide" name="RundownItem[DataFields][{{@index}}][ftype]" value="filebrowser"></td></tr>
<tr><td class="aright">title</td> <td><input type="text" class="wide" name="RundownItem[DataFields][{{@index}}][title]" value="{{title}}"></td></tr>
<tr><td class="aright">assetfolder</td> <td><input type="text" class="wide" name="RundownItem[DataFields][{{@index}}][assetfolder]" value="{{assetfolder}}"></td></tr>
<tr><td class="aright">extension</td> <td><input type="text" class="wide" name="RundownItem[DataFields][{{@index}}][extension]" value="{{extension}}"></td></tr>
</table>
</div>
<div class="gcpluginbuttondiv" style="white-space: nowrap;">
{{title}}&nbsp;
<input type="text" data-position="right center" name="RundownItem[DataFields][{{@index}}][value]" data-update="{{field}}" data-role="userEditable" value="{{value}}" placeholder="{{title}}" ondblclick="event.stopPropagation();" onkeypress="javascript:applyTextEditChanges(event,this);" class="gcinput"><BR>
<button type="button"
onClick="alert('{{assetfolder}} {{extension}} {{value}}')";
class="btnsmall ripple bg_grey15"
>Browse</button>&nbsp; &nbsp;
</div>
{{/ifValue}}
{{#ifValue ftype "textarea"}}
<!-- "TEXTAREA" TYPE OF TEMPLATE FIELD -->
<div class="wide HIDE">
<table class="wide spxTableHeadsmall">
<tr><td class="aright">ftype</td> <td><input type="text" class="wide" name="RundownItem[DataFields][{{@index}}][ftype]" value="textarea"></td></tr>
</table>
</div>
<textarea rows="8" data-tooltip="{{title}}" data-position="right center" name="RundownItem[DataFields][{{@index}}][value]" data-update="{{field}}" data-role="userEditable" ondblclick="event.stopPropagation();" class="gcinput">{{injectNewlines value}}</textarea><BR>
{{/ifValue}}
{{#ifValue ftype "dropdown"}}
<!-- "DROPDOWN" TYPE OF TEMPLATE FIELD -->
<!-- first, hidden fields -->
<table class="HIDE wide brd spxTableHeadsmall">
<tr><td class="aright">fType</td> <td><input type="text" class="wide" name="RundownItem[DataFields][{{@index}}][ftype]" value="dropdown"><BR><BR></td></tr>
</table>
<!-- then visible DOM elements -->
<select data-update="{{field}}" data-role="userEditable" class="gcinput select" name="DropdownValues[{{field}}]" data-tooltip="{{title}}" data-position="right center">
<option disabled>{{title}}:</option>
{{#each items}}
{{#ifValue value ../value}}
<option selected value="{{value}}">{{text}}</option>
{{else}}
<option value="{{value}}">{{text}}</option>
{{/ifValue}}
{{/each}}
</select>
{{/ifValue}}
{{#ifValue ftype "filelist"}}
<!-- "FILELIST" TYPE OF TEMPLATE FIELD -->
<div class="wide HIDE">
<table class="wide spxTableHeadsmall">
<tr><td class="aright">ftype</td> <td><input type="text" class="wide" name="RundownItem[DataFields][{{@index}}][ftype]" value="textfield"></td></tr>
<tr><td class="aright">assetfolder</td> <td><input type="text" class="wide" name="RundownItem[DataFields][{{@index}}][assetfolder]" value="{{assetfolder}}"></td></tr>
<tr><td class="aright">extension</td> <td><input type="text" class="wide" name="RundownItem[DataFields][{{@index}}][extension]" value="{{extension}}"></td></tr>
</table>
</div>
<!-- then render visible DOM elements with a helper-->
<!-- value set as fField value {{value}} -->
<select data-update="{{field}}" data-role="userEditable" class="gcinput select" name="FilelistValues[{{field}}]" data-tooltip="{{title}} {{lang 'general.fromFolder'}}: {{assetfolder}}" data-position="right center">
<option disabled>{{title}} {{lang 'general.fromFolder'}}:</option>
<option disabled>{{assetfolder}}</option>
<option disabled></option>
<option value="none">-</option>
{{{ PopulateFilelistOptions assetfolder extension value ../relpath }}}
</select>
{{/ifValue}}
{{#ifValue ftype "divider"}}
<!-- "DIVIDER" TYPE OF TEMPLATE FIELD -->
<div class="wide HIDE">
<table class="wide spxTableHeadsmall">
<tr><td class="aright">ftype</td> <td><input type="text" class="wide" name="RundownItem[DataFields][{{@index}}][ftype]" value="divider"></td></tr>
</table>
</div>
<div class="divider">{{title}}</div>
{{/ifValue}}
{{#ifValue ftype "color"}}
<!-- "COLOR PICKER" TYPE OF TEMPLATE FIELD -->
<div class="colorRoot">
<div class="wide HIDE">
<table class="wide spxTableHeadsmall">
<tr><td class="aright">ftype</td> <td><input type="text" class="wide" name="RundownItem[DataFields][{{@index}}][ftype]" value="color"></td></tr>
<tr><td class="aright">value</td> <td><input type="text" class="wide" name="RundownItem[DataFields][{{@index}}][value]" id="colorvalue" value="{{value}}" data-role="userEditable" data-update="{{field}}"></td></tr>
</table>
</div>
<div class="gcpluginbuttondiv wide" style="white-space: nowrap; position:relative;">
&nbsp; <span class="gcplugindescription">{{title}}</span>
<!--see handler at the bottom of file -->
<button type="button"
onClick="colorPicker(this,'{{value}}')";
class="colorbtnsmall ripple"
style="background-color: {{value}}; right:0;">{{value}}</button>
</div>
</div>
{{/ifValue}}
{{/each}} <!-- ftype check ended -->
</TD>
<TD class="vtop">
<table class="wide" style="border-spacing: 5px; margin-top:0px;">
<tr>
<td colspan="2" class="vtop">
<!-- SAVE CHANGES BUTTON -->
<!-- onClick="saveTemplateItemChanges({{@index}});" -->
<button type="button"
onClick="saveTemplateItemChangesByElement(this.closest('.itemrow'));"
class="wide spxFlexItem btnsmall bg_grey15 ripple"
onmouseover="tip('{{lang 'hover.submitthis'}}');"
>{{lang 'button.save'}} </button><!-- FORM SUBMIT MODEL -->
</td>
</tr>
<tr>
<td>
<!-- PLAY BUTTON -->
<button type="button"
style="display: none;"
onClick="playItem(this.closest('.itemrow'));"
id="tplay{{@index}}"
class="wide spxFlexItem btnsmall ripple {{{playButtonClass onair}}}"
data-spx-stoptext="{{lang 'button.stop'}}"
data-spx-playtext="{{lang 'button.play'}}"
data-spx-name="playbutton"
onmouseover="tip('{{lang 'hover.playthis'}}');"
>{{{playButtonText onair}}}</button>
</td>
<td>
<!-- UPDATE BUTTON -->
<button type="button"
onClick="updateItem(this.closest('.itemrow'));"
class="wide spxFlexItem btnsmall bg_blue ripple disabled"
data-spx-name="updatebutton"
onmouseover="tip('{{lang 'hover.updatethis'}}');"
>{{lang 'button.update'}}</button>
</td>
</tr>
</table>
</TD>
<!-- Not in LIGHT MODE
<TD class="vtop center">
<i id="deleteLarge{{@index}}" class="l-icon far fa-trash-alt" data-spx-name="deletelarge" onClick="removeItemFromRundown(this.closest('.itemrow'));" onmouseover="tip('{{lang 'hover.deletethis'}}');"></i><BR>
<i id="duplicLarge{{@index}}" class="l-icon far fa-clone" onClick="duplicateRundownItem(this.closest('.itemrow'));" onmouseover="tip('{{lang 'hover.duplicateitem'}}');"></i><BR>
<i id="exportCSV{{@index}}" class="l-icon fas fa-file-export" onClick="exportItemAsCSV(this.closest('.itemrow'));" onmouseover="tip('{{lang 'hover.exportitem'}}');"></i><BR>
<i id="importCSV{{@index}}" class="l-icon fas fa-file-import" onClick="appendItemsFromCSV();" onmouseover="tip('{{lang 'hover.importitems'}}');"></i>
</TD>
-->
</TR>
<TR>
<TD colspan="2" class="aright spxTableHeadsmall">
{{{GeneratePlayoutInfo playserver playchannel playlayer webplayout out itemID}}}
</TD>
<TD>
&nbsp;
</TD>
</TR>
</table>
<div class="wide HIDE">
<table class="wide spxTableHeadsmall">
<tr><td class="aright">relpath</td> <td><input style="width: 100%;" type="text" id="relpath{{@index}}" name="RundownItem[relpath]" value="{{relpath}}"></td></tr>
<tr><td class="aright">description</td> <td><input style="width: 100%;" type="text" id="description{{@index}}" name="RundownItem[description]" value="{{description}}"></td></tr>
<tr><td class="aright">playserver</td> <td><input style="width: 100%;" type="text" id="playserver{{@index}}" name="RundownItem[playserver]" value="{{playserver}}"></td></tr>
<tr><td class="aright">playchannel</td> <td><input style="width: 100%;" type="text" id="playchannel{{@index}}" name="RundownItem[playchannel]" value="{{playchannel}}"></td></tr>
<tr><td class="aright">playlayer</td> <td><input style="width: 100%;" type="text" id="playlayer{{@index}}" name="RundownItem[playlayer]" value="{{playlayer}}"></td></tr>
<tr><td class="aright">dataformat</td> <td><input style="width: 100%;" type="text" id="dataformat{{@index}}" name="RundownItem[dataformat]" value="{{dataformat}}"></td></tr>
<tr><td class="aright">webplayout</td> <td><input style="width: 100%;" type="text" id="webplayout{{@index}}" name="RundownItem[webplayout]" value="{{webplayout}}"></td></tr>
<tr><td class="aright">onair</td> <td><input style="width: 100%;" type="text" id="onair{{@index}}" name="RundownItem[onair]" value="{{onair}}"></td></tr>
<tr><td class="aright">out</td> <td><input style="width: 100%;" type="text" id="out{{@index}}" name="RundownItem[out]" value="{{out}}"></td></tr>
<tr><td class="aright">uicolor</td> <td><input style="width: 100%;" type="text" id="uicolor{{@index}}" name="RundownItem[uicolor]" value="{{uicolor}}"></td></tr>
<tr><td class="aright">steps</td> <td><input style="width: 100%;" type="text" id="steps{{@index}}" name="RundownItem[steps]" value="{{steps}}"></td></tr>
<tr><td class="aright">imported</td> <td><input style="width: 100%;" type="text" id="imported{{@index}}" name="RundownItem[imported]" value="{{imported}}"></td></tr>
<!-- special event triggers -->
<tr><td class="aright">onPlay</td> <td><input style="width: 100%;" type="text" id="onPlay{{@index}}" name="RundownItem[function_onPlay]" value="{{function_onPlay}}"></td></tr>
<tr><td class="aright">onNext</td> <td><input style="width: 100%;" type="text" id="onNext{{@index}}" name="RundownItem[function_onNext]" value="{{function_onNext}}"></td></tr>
<tr><td class="aright">onStop</td> <td><input style="width: 100%;" type="text" id="onStop{{@index}}" name="RundownItem[function_onStop]" value="{{function_onStop}}"></td></tr>
</table>
</div>
</div>
</div>
</form>
</div>
{{else}}
<!-- object had no DataFields, so it as skipped. NULL item? -->
{{/if}}
{{/each}}
<!-- GC ITEMS ENDED -->
<!-- No ADD button in LIGHT mode
<div class="wide" style="text-align: center; padding: 0.5em 1.0em;">
<button type="button"
data-tooltip="{{lang 'hover.addrundownitem'}}"
data-position="right center"
class="addbtn bg_blue ripple"
onClick="ModalOn('overlayTemplatePicker');"
style="padding: 0.05em 0.75em; border-radius: 0.2em;"
onmouseover="tip('{{lang 'hover.addrundownitem'}}');">+</button>
</div>
-->
</div>
<div id="controllerPluginButtons">
{{{ hookLoadControllerPlugins }}}
</div>
</div><!-- itemList end -->
<div class="footerAndInfo">
<div id="footer">
<button
id="MasterTOGGLE"
class="btn bg_green ripple disabled"
data-spx-graphic="name_left"
data-spx-playtext="{{lang 'button.playmaster'}}"
data-spx-stoptext="{{lang 'button.stopmaster'}}"
onClick="playItem();"
onmouseover="tip('{{lang 'hover.masterplay'}}');"
>{{lang 'button.playmaster'}}</button>
<button
id="MasterCONTINUE"
class="btn bg_green ripple disabled"
onClick="nextItem();"
onmouseover="tip('{{lang 'hover.mastercontinue'}}');"
>{{lang 'button.continue'}}</button>
</div><!-- footer end -->
<div class="experimentalInfo">
Light mode is an experimental feature. Please report any issues.
</div>
</div>
</div><!-- wrapper end -->
<script>
// See spx_gc.js
// console.log('Init tooltip');
var tooltip = new Tooltip();
function colorPicker(sourceButton, initialColor) {
// Color picker
// alert('Picker to ' + initialColor)
sourceButton.textContent = '...';
var picker = new Picker({
color: initialColor,
parent: sourceButton,
popup: 'bottom',
editorFormat: 'rgb'
});
picker.onChange = function(color) {
sourceButton.style.backgroundColor = color.rgbaString;
sourceButton.closest('.colorRoot').querySelector('#colorvalue').value=String(color.rgbaString);
}
picker.onDone = function(color) {
sourceButton.textContent = color.rgbaString;
sourceButton.style.backgroundColor = color.rgbaString;
sourceButton.closest('.colorRoot').querySelector('#colorvalue').value=String(color.rgbaString);
};
}
function pageInit() {
// COLLAPSIPLE HANDLERS
var coll = document.getElementsByClassName("collapsibleZone");
var i;
for (i = 0; i < coll.length; i++) {
coll[i].addEventListener("click", function() {
this.classList.toggle("activeZone");
var content = this.nextElementSibling;
if (content.style.maxHeight){
content.style.maxHeight = null;
} else {
content.style.maxHeight = content.scrollHeight + "px";
}
});
}
// Check msg from URL
var url = new URL(window.location.href);
var msg = url.searchParams.get("msg");
if (msg) {
showMessageSlider(msg) // msg, info|warn|error
}
// POPULATE RUNDOWN LIST
let projects = fetch('/api/v1/allrundowns')
.then(response => response.json())
.then(projects => {
document.getElementById('rundownSelector').innerHTML = '';
projects.forEach(function(project, index) {
let option = document.createElement('option');
option.value = project.project;
option.innerHTML = project.project;
option.disabled = true;
option.classList.add("optionProject");
document.getElementById('rundownSelector').appendChild(option);
project.rundowns.forEach(function(rundown, index) {
let rd = document.createElement('option');
let CurrentRD = '{{folder}}/{{filebasename}}';
if (CurrentRD == project.project + '/' + rundown) {
rd.selected = true;
}
rd.value = project.project + '/' + rundown;
rd.innerHTML = "&nbsp; " + rundown;
rd.classList.add("optionRundown");
document.getElementById('rundownSelector').appendChild(rd);
});
let empty = document.createElement('option');
empty.value = "none";
empty.innerHTML = '';
empty.disabled = true;
empty.classList.add("optionEmpty");
document.getElementById('rundownSelector').appendChild(empty);
});
});
resizePreview();
heartbeat(212); // page identifier
spxInit('');
}
function resizePreview() {
// let DefaultX = 1920 // LightMode HD by default;
// let DefaultY = 1080 //
// let Wpx = document.getElementById('previewWidthRuler').offsetWidth;
// let Sca = Wpx/DefaultX;
// let Hpx = parseInt(DefaultY * Sca);
// document.getElementById('previewBG').style.width=Wpx+'px';
// document.getElementById('previewBG').style.height=Hpx+'px';
// document.getElementById('previewIF').style.transform="scale("+Sca+")";
// Controller preview resize
let DefaultX = '{{width}}'; // 1920 by default;
let DefaultY = '{{height}}'; // 1080 by default;
if (DefaultX.includes('vw')) {
// Responsive settings, use window size! Added in v1.3.0
// console.log('Responsive settings, but forcing 16:9 in Controller');
DefaultX = '1920px';
DefaultY = '1080px';
}
let Wpx = document.getElementById('previewWidthRuler').offsetWidth;
let Sca = Wpx/parseInt(DefaultX);
let Hpx = parseInt(parseInt(DefaultY) * Sca);
document.getElementById('previewBG').style.width=Wpx+'px';
document.getElementById('previewBG').style.height=Hpx+'px';
document.getElementById('previewIF').style.transform="scale("+Sca+")";
document.getElementById('previewBG').style.opacity=1;
// document.getElementById('controllerFunctionsZone').style.opacity=1;
// console.log('Resized to ' + Wpx + ' x ' + Hpx + ' px, scale multiplier: ' + Sca);
}
document.addEventListener('DOMContentLoaded', () => {
pageInit();
})
socket.on('connect', function () {
data={};
data.spxcmd = 'identifyClient';
data.name = 'SPX_CONTROLLER_MINI'; // <---- Name of this socket connection
socket.emit('SPXMessage2Server', data);
});
</script>
</body>
</html>
+48
View File
@@ -0,0 +1,48 @@
<!DOCTYPE html>
<!-- empty -->
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Empty</title>
<style>
html, body {
margin: 0;
padding: 0;
height: 100%;
overflow: hidden;
background-color: transparent;
}
</style>
<script>
function play() { emptyMessage('play') }
function stop() { emptyMessage('stop') }
function invoke() { emptyMessage('invoke') }
function update() { emptyMessage('update') }
function data() { emptyMessage('data') }
function preview() { emptyMessage('preview') }
function emptyMessage(cmd) {
// console.log('Empty layer received command "' + cmd + '", which is ignored');
}
</script>
</head>
<body>
<!-- nothing here -->
</body>
</html>
+165
View File
@@ -0,0 +1,165 @@
<!DOCTYPE html>
{{>copyright }}
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>SPX{{SPX.title}} | {{lang 'pagetitle.select'}}</title>
<link rel="icon" type="image/png" href="/img/spx-gc.ico">
<link rel="stylesheet" type="text/css" href="/css/spx_fonts.css">
<link rel="stylesheet" type="text/css" href="/css/spx_layout.css">
<link rel="stylesheet" type="text/css" href="/css/spx_forms.css">
<link rel="stylesheet" type="text/css" href="/css/spx_colors.css">
<link rel="stylesheet" type="text/css" href="/css/spx_modal.css">
<script src="/js/axios.min.js"></script>
<script src="/js/fontawesome.all.min.js"></script>
<script src="/js/anime.min.js"></script>
<script src="/js/socket.io.js"></script>
<script src="/js/spx_gc.js"></script>
<style>
.box {
position: relative;
display: inline-block;
background: rgba(255, 255, 255, 0.671);
padding: 0.6em 1em;
text-align: center;
color: rgba(0, 0, 0, 0.842);
bottom: 2px;
left: 50px;
font-size: 0.7em;
border-radius: 0.5em;
}
.sb4:before {
content: "";
width: 0px;
height: 0px;
position: absolute;
border-right: 10px solid transparent;
border-left: 10px solid rgba(255, 255, 255, 0.671);;
border-top: 10px solid rgba(255, 255, 255, 0.671);;
border-bottom: 10px solid transparent;
left: 40px;
bottom: -20px;
}
</style>
</head>
{{>rundownForm}}
<body onload="pageInit();" class="bg_body" onmouseover="tip('');">
<input type="hidden" id="hidden_folder" value="{{folder}}">
<div class="wrapper">
<div class="header">
{{>header page="episodes"}}
</div><!-- header ended -->
<div class="content">
<div class="centerColumn pM wrapper ">
<div id="itemList" class="content pM scroller" style="border: 0px solid yellow;">
{{#if message}}
<div class="message">{{message}}</div>
{{/if}}
{{#if error}}
<div class="messagerror">{{error}}</div>
{{/if}}
<div style="display: flex;">
<div style="flex-grow: 1" class="vmidTD tableheaderText">{{lang 'headline.selectfile'}}</div>
<div class="tableheaderHelp">
{{#ifValue showconfig.projectFormat 'OGRAF'}}
<a onClick="help('ograf');" onmouseenter="tip('What is OGraf?');" onmouseleave="tip('');">
<img src="/img/ograf-logo-mono-white.svg" style="width: 5vw;">
</a>
{{/ifValue}}
&nbsp;
</div>
<div class="tableheaderHelp"><a title="{{lang 'general.help'}}" onClick="help('playlists');">
<i class="far fa-question-circle"></i>
</a></div>
</div>
{{!-- <table class="wide" border=0>
<TR>
<td class="vmid tableheaderText">{{lang 'headline.selectfile'}}</td>
<td class="vmid tableheaderHelp aright" title="{{lang 'general.help'}}" onClick="help('playlists');"><i class="far fa-question-circle"></i>&nbsp;</td>
</TR>
</table> --}}
<hr class="h2hr"><!-- =========================================== -->
<table style="width: 100%;" cellspacing="10" border="0">
<tr>
<td>
<select id="lists" size=14 class="spxformtext bg_grey40"
style="width: 100%; height:100%; color:white; border:0px; padding:0.2em; line-height:3;"
onmouseover="tip('{{lang 'hover.epslist'}}');"
ondblclick="cas();" autofocus>
{{#each files}}
<option>{{this}}</option>
{{/each}}
</select><BR>
</td>
</tr>
</table>
<HR>
{{#if files}}
<!-- // -->
{{else}}
<div class="box sb4">{{lang 'headline.createrundowntip'}}</div>
{{/if}}
<table style="width: 100%;" cellspacing="10" border="0">
<tr>
<td valign="top" width="50%">
<button onmouseover="tip('{{lang 'hover.addrundown'}}');" class="spxformtext btn bg_blue ripple" style="margin:2px; width: 48%;" onClick="add('rundownFormOverlay');">{{lang 'button.addplaylist'}}</button>
<button onmouseover="tip('{{lang 'hover.duplicaterundown'}}');" class="spxformtext btn bg_blue ripple" style="margin:2px; width: 48%;" onClick="duplicateRundown();">{{lang 'button.duplicate'}}</button>
<button onmouseover="tip('{{lang 'hover.renameepisode'}}');" class="spxformtext btn bg_blue ripple" style="margin:2px; width: 48%;" onClick="renameRundown();">{{lang 'button.rename'}}</button>
<button onmouseover="tip('{{lang 'hover.deletepisode'}}');" class="spxformtext btn bg_blue ripple" style="margin:2px; width: 48%;" onClick="del();">{{lang 'button.delete'}}...</button>
</td>
<td valign="top">
<button onmouseover="tip('{{lang 'hover.openrundown'}}');" class="spxformtext btn wide bg_orange ripple" style="margin:2px;" onClick="cas();">{{lang 'button.open'}}</button>
<button onmouseover="tip('{{lang 'hover.profile'}}');" class="spxformtext btn wide bg_grey15 ripple" style="margin:2px;" onClick="cfg();"><i class="fas fa-cog"></i> {{lang 'button.settings'}}</button>
</td>
</tr>
</table>
</div><!-- itemlist ended -->
</div><!-- center wrapper ended -->
</div><!-- content ended -->
{{>footer}}
</div><!-- MAINwrapper ended -->
<script>
window.onload = function () {
document.getElementById('lists').selectedIndex = "0";
heartbeat(208); // page identifier
}
</script>
</body>
</html>
+76
View File
@@ -0,0 +1,76 @@
<!DOCTYPE html>
{{>copyright }}
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Filebrowser</title>
<!-- local -->
<script src="/js/lib/luxon.min.js"></script>
<script src="/js/lib/lodash.min.js"></script>
<link rel="stylesheet" type="text/css" href="/css/spx_filebrowser.css">
<!-- from SPX -->
<script src="/js/axios.min.js"></script>
<script src="/js/anime.min.js"></script>
<script src="/js/fontawesome.all.min.js"></script>
<script src="/js/Sortable.min.js"></script>
<link rel="icon" type="image/png" href="/img/spx-gc.ico">
<link rel="stylesheet" type="text/css" href="/css/spx_fonts.css">
<link rel="stylesheet" type="text/css" href="/css/spx_layout.css">
<link rel="stylesheet" type="text/css" href="/css/spx_forms.css">
<link rel="stylesheet" type="text/css" href="/css/spx_colors.css">
<link rel="stylesheet" type="text/css" href="/css/spx_modal.css">
<link rel="stylesheet" type="text/css" href="/css/spx_accents.css">
</head>
<body style="background-color: #2A3641;">
<div id="messageSlider" style="opacity:0;"></div>
<div class="page">
<div class="page-content">
<div class="well darken skroll filebrowser">
<div id="navipath">
<!-- path generated here-->&nbsp;
</div>
<div class="filebrowser">
<UL>
<LI style="user-select: none;" id="homefolder" class="filebrowser_up" onDblClick="goHome();"><SPAN class="icon home"></i>ROOT</SPAN></LI>
<LI style="user-select: none;" id="prefolder" class="filebrowser_up" onDblClick="goUp();"><SPAN class="icon back">UP</SPAN></LI>
</UL>
</div>
<div id="folderinbox">
<!-- folder items generated here -->
</div>
<div id="fileinbox">
<!-- folder items generated here -->
</div>
<div >
</div>
</div>
</div>
<div class="page-footer">
<div class="footitem1">
<button
id="playButton"
class="btn bg_grey ripple disable"
style="width:80%; margin-bottom:0.5em;"
onClick="playItem();">
SELECT
</button>
</div>
<div class="footitem1">
<button
id="stopButton"
class="btn bg_grey ripple"
style="width:80%; margin-bottom:0.5em;"
onClick="stopItem();">
CANCEL
</button>
</div>
</div>
<script defer src="/js/spx_fileBrowser.js"></script>
</body>
</html>
+320
View File
@@ -0,0 +1,320 @@
<!DOCTYPE html>
{{>copyright }}
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>SPX {{SPX.title}} | {{lang 'pagetitle.home'}}</title>
<link rel="icon" type="image/png" href="/img/spx-gc.ico">
<link rel="stylesheet" type="text/css" href="/css/spx_fonts.css">
<link rel="stylesheet" type="text/css" href="/css/spx_layout.css">
<link rel="stylesheet" type="text/css" href="/css/spx_forms.css">
<link rel="stylesheet" type="text/css" href="/css/spx_colors.css">
<script src="/js/fontawesome.all.min.js"></script>
<script src="/js/socket.io.js"></script>
<script src="/js/spx_gc.js"></script>
<script src="/js/axios.min.js"></script>
<script src="/js/anime.min.js"></script>
<script>
function website() {
window.open('https://spxgraphics.com', '_spx');
}
function start() {
document.location = '/shows';
}
function appconfig() {
document.location = '/config';
}
</script>
<style>
.auto-resizable-iframe {
max-width: 100%;
margin: 0px auto;
}
.auto-resizable-iframe > div {
position: relative;
padding-bottom: 56.25%;
height: 0px;
background-color: #000000;
}
.auto-resizable-iframe iframe {
position: absolute;
background-color: #000000;
top: 0px;
left: 0px;
width: 100%;
height: 100%;
}
.box {
/* width: 150px; */
margin-right: 10px;
background: #00000088;
padding: 0.6em 1em;
text-align: center;
color: rgba(255, 255, 255, 0.671);
position: absolute;
bottom: 60px;
right: 50px;
font-size: 0.7em;
border-radius: 0.5em;
}
.sb4:before {
content: "";
width: 0px;
height: 0px;
position: absolute;
border-left: 10px solid transparent;
border-right: 10px solid #00000088;
border-top: 10px solid #00000088;
border-bottom: 10px solid transparent;
right: 19px;
bottom: -19px;
}
#upgradeinfo, #messageinfo {
display: block;
margin: 2.0em 0em 1.5em 0em;
border: 1px solid rgba(0, 0, 0, 0);
padding: 0.8em 2.5em 1.6em 2.0em;
border-radius: 0.5em;
font-size: 0.8em;
background-color: #ffffff17;
box-shadow: rgba(0, 0, 0, 0.418) 0px 0px 10px;
border-left: 8px solid rgba(229, 16, 16, 0)
}
.messagedate {
color: rgba(255, 255, 255, 0.493);
font-size: 0.8em;
margin-top: 0.4em;
margin-bottom: 0.0em;
}
.messageheadline {
color: #fff;
margin-top: 0.2em;
margin-bottom: 1.0em;
font-size: 1.5em;
}
.messagebody {
font-size: 0.7;
color: rgba(255, 255, 255, 0.685);
}
#messagelink, #notificationlink{
display: inline-block;
margin-top: 1em;
padding: 0.5em 1.2em;
border-radius: 0.0em;
font-size: 1.0em;
background-color: #00000081;
border-left: 4px solid #00000000;
color: rgba(255, 255, 255, 0.76);
}
#messagelink > a:link, #notificationlink > a:link {
font-size: 1.2em;
color: rgb(255, 255, 255);
margin: 0.5em 1.2em;
text-shadow: 0;
}
#messagelink > a:hover, #notificationlink> a:hover{
color: #e6dfd4;
text-decoration: underline;
}
/* none | upgrade | info | warning */
.message-upgrade { border-left-color: #e57010a6 !important; }
.message-info { border-left-color: #106ce5b4 !important; }
.message-warn { border-left-color: #e51010a9 !important; }
.message-none { border-left-color: #106ce5b4 !important; }
.greeting {
margin-top: 1em;
margin-bottom: 1em;
display: block;
padding: 12px;
background-color: rgba(72, 128, 212, 0.2);
color: #fff;
border-radius: 8px;
font-size: 1.0em;
text-shadow: rgba(0, 0, 0, 0.7) 1px 1px 4px;
{{!-- border: 1px solid rgba(0, 0, 0, 0.384); --}}
border-left: 8px solid rgba(0, 140, 255, 0.76);
box-shadow: rgba(0, 0, 0, 0.267) 0px 0px 6px;
}
</style>
</head>
<body class="bg_body">
<div class="wrapper">
<div class="header">
{{>header page="home"}}
</div><!-- header ended -->
<div class="content">
<div class="centerColumn pM wrapper ">
<div id="itemList" class="content pM scroller" style="border: 0px solid yellow;">
{{#if message}}
<div class="message">{{message}}</div>
{{/if}}
{{#if error}}
<div class="messagerror">{{error}}</div>
{{/if}}
<table class="wide" border=0>
<TR>
<td class="vmid tableheaderText">
{{lang 'pagetitle.home'}}
{{#ifValue user "default"}}
<!--default user -->
{{else}}
{{user}}
{{/ifValue}}
</td>
<td class="vmid tableheaderHelp aright" title="{{lang 'general.help'}}" onClick="help('intro');"><i class="far fa-question-circle"></i>&nbsp;</td>
</TR>
</table>
<hr class="h2hr"><!-- =========================================== -->
<table style="width: 100%;" cellspacing="10" border="0">
<tr>
<td>
<div>
<img src="/img/SPX_glossy_255_Transparent_Shadow.png" style="height: 4.0em; float:right; margin-right: 1em;">
<h1 style="padding:0px; margin:0px; font-size: 1.0em;">SPX Graphics </h1>
<span class="spxInfotext" style="font-size: 0.8em; opacity:0.5;">
{{lang 'general.slogan'}}<BR>
</span>
</div>
<div style="clear:both;">&nbsp;</div>
{{#if greeting}}
<div class="greeting" id="greeting">{{greeting}}</div>
{{/if}}
<div class="spxFlexContainer">
<button onmouseover="tip('{{lang 'hover.navishows'}}');" class="spxformtext btn spxFlexItem bg_orange ripple" onclick="start();">{{lang 'button.launch'}}</button>
<button onmouseover="tip('{{lang 'hover.config'}}');" class="spxformtext btn spxFlexItem bg_grey15 ripple" onclick="appconfig()"><i class="fas fa-wrench"></i>&nbsp;{{lang 'button.config'}}</button>
<button onmouseover="tip('{{lang 'hover.docs'}}');" class="spxformtext btn spxFlexItem bg_blue ripple" onclick="help('/')">{{lang 'button.help'}}</button>
<button onmouseover="tip('spxgraphics.com');" class="spxformtext btn spxFlexItem bg_blue ripple" onclick="website()">website</button>
</div>
<!-- Notifications from the SPX messaging service: -->
<div class="message-upgrade" id="upgradeinfo" style="display:none">
<div class="messagedate" id="upgrade_date"></div>
<div class="messageheadline" id="upgrade_head"></div>
<div class="messagebody" id="upgrade_body"></div><BR>
<div id="notificationlink" class="message-upgrade"><span id="upgrade_call"></span><a href="" id="upgrade_link"></a></div>
</div>
<div class="message-info" id="messageinfo" style="display:none">
<div class="messagedate" id="message_date"></div>
<div class="messageheadline" id="message_head"></div>
<div class="messagebody" id="message_body"></div><BR>
<div id="messagelink" class="message-info"><span id="message_call"></span><a href="" id="message_link"></a></div>
</div>
<BR>
<!-- server / msgsrvs / notifications -->
<div id="promo" style="opacity: 0;">
<A id="homepagepromolink" href="https://spxgraphics.com/software" target="_mothership">
<img id="homepagepromopict" class="auto-resizable-iframe" SRC="/img/welcome-splash-tiers.png" alt="Splash screen promo">
</A>
</div>
</td>
</tr>
</table>
</div><!-- itemlist ended -->
</div><!-- center wrapper ended -->
</div><!-- content ended -->
{{>footer}}
<div class="box sb4">Questions or comments?</div>
</div><!-- MAINwrapper ended -->
<script>
const css1 =
"box-sizing: border-box;" +
"font-size: 12px;" +
"background-image: linear-gradient(180deg, rgba(255, 255, 255, 0.1) 50%, rgba(0, 0, 0, 0.3) 50%), linear-gradient(160deg, rgba(238, 255, 238, 0.5) 0%, #00aa00 20%, #112233 70%);" +
"border-bottom: 2px solid rgba(0,0,0,0.5);" +
"border-top: 2px solid rgba(255,255,255,0.5);" +
"border-left: 4px solid rgba(0,0,0,0.5);" +
"border-right: 2px solid rgba(255,255,255,0.2);" +
"margin: 2em 0;" +
"text-shadow: 0 2px 4px black;" +
"font-family: Arial;" +
"color: white;" +
"text-align: center;" +
"padding: 0.3em 2em;" +
"width: 300px;" +
"letter-spacing: 0.1em;" +
"border-radius: 1em;";
let YYYY = new Date().getFullYear();
const txt1 = "%c© " + YYYY + " SPX Graphics 👋 All Rights Reserved.";
console.log(txt1, css1);
// check for upgrade
document.getElementById('upgradeBubble').style.display="none";
heartbeat(202); // page identifier
{{#ifValue config.general.allowstats false}}
console.log('No version checking.');
{{else}}
// console.log('stats permitted');
getMessages('{{{curVerInfo}}}');
{{/ifValue}}
// fade in promo after a little delay
anime({
targets: '#promo',
easing: 'linear',
opacity: 1,
duration: 300,
delay: 200
});
// show message when page loaded
window.addEventListener('load', (event) => {
if ('{{showMessage}}'!='') {
hideMessageSlider();
setTimeout(function(){
showMessageSlider('{{showMessage}}','happy');
}, 100);
}
});
</script>
</body>
</html>
+107
View File
@@ -0,0 +1,107 @@
<!DOCTYPE html>
{{>copyright }}
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>SPX {{SPX.title}} | {{lang 'pagetitle.login'}}</title>
<link rel="icon" type="image/png" href="/img/spx-gc.ico">
<link rel="stylesheet" type="text/css" href="/css/spx_fonts.css">
<link rel="stylesheet" type="text/css" href="/css/spx_layout.css">
<link rel="stylesheet" type="text/css" href="/css/spx_forms.css">
<link rel="stylesheet" type="text/css" href="/css/spx_colors.css">
<link rel="stylesheet" type="text/css" href="/css/spx_modal.css">
<style>
.l:link, .l:visited{color:#000000}
.l:link:hover{color:#ffd000}
.login {
width: 60%;
position: absolute;
left: 50%;
top: 40%;
transform: translate(-50%, -40%);
z-index: 20;
padding: 0.3em;
background-color: #414C5A;
color: white;
box-shadow: rgba(0, 0, 0, 0.3) 0px 0px 20px;
vertical-align: middle;
}
</style>
</head>
<body class="bg_body">
<form name="login" id="login" method="post" action="/">
<div class="">
<div class="contentXX">
<div class="login" style="width: 40%; border:0px solid yellow; border-radius:0.8em;">
<table style="width: 100%; height:100%; border:0px solid yellow;" cellspacing="20" border="0">
<tr>
<td align="center">
<a href="https://spxgraphics.com" target="_blank"><img src="/img/SPX_glossy_255_Transparent_Shadow.png" style="height: 7.0em;"></a>
{{!-- <h1 style="padding:0px; margin:0px;">SPX Graphics</h1> --}}
<BR>
<div class="spxTableHeadsmall" style="margin: 1em; font-size: 0.7em; opacity:0.6; line-height: 1.2em; width:80%;">
{{lang 'general.slogan'}}<BR>
</div>
</td>
</tr>
<tr>
<td align="center">
<table style="width: 90%; border-radius:1em; background-color: #ffffff11;" cellspacing="25" border="1">
{{{ShowDemoLoginInfo}}}
<tr>
<td class="vmid spxTableHeadsmall" style="height: 1.6em; text-transform:uppercase;">Username</td>
<td ><input type="text" class="wide gcinput" name="username" autofocus value=""></td>
</tr>
<tr>
<td class="vmid spxTableHeadsmall" style="height: 1.6em; text-transform:uppercase;">password</td>
<td ><input type="password" class="wide gcinput" name="password" value=""></td>
</tr>
<tr>
<td colspan="2" class="vmid center">
<div class="spxFlexContainer">
<input type="submit" class="spxformtext btn spxFlexItem bg_blue ripple" value="Login" style="margin:0.0em 0em 0.7em;">
{{!-- <button class="spxformtext btn spxFlexItem bg_blue ripple" onclick="github()">Github</button> --}}
</div>
<span style="font-size: 0.55em; opacity:0.9; color:#000000;">
v.{{{ShowVersion}}} | <a class="l" href="https://spxgraphics.com/contact">&copy; 2020- SPX Graphics</a></span>
</td>
</tr>
</table>
</td>
</tr>
</table>
</div><!-- center wrapper ended -->
</div><!-- content ended -->
</div><!-- MAINwrapper ended -->
</form>
<!--
BUG! BUG! BUG!
If you are seeing this text in SPX terminal / console, you
are facing a bug in action. This is not a normal behavior.
The reason is mosty likely that SPX has been configured to
only work when a user is logged in. If you are seeing this
text, it means that SPX is trying to show you the login page
but in an incorrect way.
This will be fixed in a future release. In the meantime,
disable logging (remove config) and restart the server.
### SORRY! YOU ARE WITNESSING A BUG!! ###
### Please read the text above here.. ###
-->
</body></html>
+513
View File
@@ -0,0 +1,513 @@
<!DOCTYPE html>
{{>copyright }}
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>SPX {{SPX.title}} | Register</title>
<link rel="icon" type="image/png" href="/img/spx-gc.ico">
<link rel="stylesheet" type="text/css" href="/css/spx_fonts.css">
<link rel="stylesheet" type="text/css" href="/css/spx_layout.css">
<link rel="stylesheet" type="text/css" href="/css/spx_forms.css">
<link rel="stylesheet" type="text/css" href="/css/spx_colors.css">
<link rel="stylesheet" type="text/css" href="/css/spx_modal.css">
<style>
a,
a:visited{color:inherit}
a:hover{color:#ff8c00}
.login {
left: 50%;
width: 60%;
z-index: 20;
color: white;
padding: 0.3em;
margin-top: 1em;
position: absolute;
vertical-align: top;
transform: translateX(-50%);
background-color: #495666;
box-shadow: rgba(0, 0, 0, 0.3) 0px 0px 20px;
}
body {
overflow: auto !important;
}
#email,
#freetext {
padding: 0.25em;
}
#email::placeholder,
#freetext::placeholder {
color: #ffffff5b;
text-shadow: none;
}
</style>
<script>
function checkEmail(e) {
if (e.value=='') {
document.getElementById('newsletter').disabled=true;
document.getElementById('inviteDiscord').disabled=true;
document.getElementById('checks').style.opacity=0.3;
} else {
document.getElementById('newsletter').disabled=false;
document.getElementById('inviteDiscord').disabled=false;
document.getElementById('checks').style.opacity=1;
}
}
function skip() {
msg = "Really?\n\nRegistration is an important " +
"feedback mechanism for us to understand who is using SPX " +
"and we would like encourage to help us by submitting some " +
"info about you. Everything is optional. All help is highly appreciated!\n\n" +
"Click OK to skip registration.";
if (confirm(msg)) {
document.location = '/';
}
}
</script>
</head>
<body class="bg_body">
<form name="login" id="login" method="post">
<input type="hidden" name="task" value="spxSoloAppRegistration">
<input type="hidden" name="platform" id="platform">
<div>
<div class="login" style="border-radius:0.6em;">
<table style="width: 100%; height:100%; border:0px solid yellow;" cellspacing="20" border="0">
<tr>
<td align="left">
SPX Graphics Registration<BR><BR>
<div class="spxInfoRegtext">
<!--{{lang 'config.authpolicy'}} -->
Registration is completely voluntary but helps us to keep track of the number of users
and give some insight into the usage of SPX. This information is <b>anonymous</b> and
<b>non-identifiable</b>. You can leave any field empty.
<a href="https://docs.spxgraphics.com/Guides/Getting+Started/Registeration#Registration+feature">Read more about registration and related policies</a>.</span>
</div>
</td>
</tr>
<tr>
<td align="center">
<table style="width: 100%; border-radius:0em; background-color: #0000003a; margin-top: 0.2em;" cellspacing="15" border="0">
<tr>
<td class="vmid spxConfigHead wrap" style="width: 30%;">
Country
<div style="font-size: 0.7em; margin-top: 1em; opacity: 0.7;">
Where are you located?
</div>
</td>
<td class="vmid">
<select class="wide drop bg_grey" name="country">
<option value=""></option>
<option selected value="{{config.registration.country}}">{{config.registration.country}}</option>
<option value="" disabled></option>
<option value="Afganistan">Afghanistan</option>
<option value="Albania">Albania</option>
<option value="Algeria">Algeria</option>
<option value="American Samoa">American Samoa</option>
<option value="Andorra">Andorra</option>
<option value="Angola">Angola</option>
<option value="Anguilla">Anguilla</option>
<option value="Antigua & Barbuda">Antigua & Barbuda</option>
<option value="Argentina">Argentina</option>
<option value="Armenia">Armenia</option>
<option value="Aruba">Aruba</option>
<option value="Australia">Australia</option>
<option value="Austria">Austria</option>
<option value="Azerbaijan">Azerbaijan</option>
<option value="Bahamas">Bahamas</option>
<option value="Bahrain">Bahrain</option>
<option value="Bangladesh">Bangladesh</option>
<option value="Barbados">Barbados</option>
<option value="Belarus">Belarus</option>
<option value="Belgium">Belgium</option>
<option value="Belize">Belize</option>
<option value="Benin">Benin</option>
<option value="Bermuda">Bermuda</option>
<option value="Bhutan">Bhutan</option>
<option value="Bolivia">Bolivia</option>
<option value="Bonaire">Bonaire</option>
<option value="Bosnia & Herzegovina">Bosnia & Herzegovina</option>
<option value="Botswana">Botswana</option>
<option value="Brazil">Brazil</option>
<option value="British Indian Ocean Ter">British Indian Ocean Ter</option>
<option value="Brunei">Brunei</option>
<option value="Bulgaria">Bulgaria</option>
<option value="Burkina Faso">Burkina Faso</option>
<option value="Burundi">Burundi</option>
<option value="Cambodia">Cambodia</option>
<option value="Cameroon">Cameroon</option>
<option value="Canada">Canada</option>
<option value="Canary Islands">Canary Islands</option>
<option value="Cape Verde">Cape Verde</option>
<option value="Cayman Islands">Cayman Islands</option>
<option value="Central African Republic">Central African Republic</option>
<option value="Chad">Chad</option>
<option value="Channel Islands">Channel Islands</option>
<option value="Chile">Chile</option>
<option value="China">China</option>
<option value="Christmas Island">Christmas Island</option>
<option value="Cocos Island">Cocos Island</option>
<option value="Colombia">Colombia</option>
<option value="Comoros">Comoros</option>
<option value="Congo">Congo</option>
<option value="Cook Islands">Cook Islands</option>
<option value="Costa Rica">Costa Rica</option>
<option value="Cote DIvoire">Cote DIvoire</option>
<option value="Croatia">Croatia</option>
<option value="Cuba">Cuba</option>
<option value="Curaco">Curacao</option>
<option value="Cyprus">Cyprus</option>
<option value="Czech Republic">Czech Republic</option>
<option value="Denmark">Denmark</option>
<option value="Djibouti">Djibouti</option>
<option value="Dominica">Dominica</option>
<option value="Dominican Republic">Dominican Republic</option>
<option value="East Timor">East Timor</option>
<option value="Ecuador">Ecuador</option>
<option value="Egypt">Egypt</option>
<option value="El Salvador">El Salvador</option>
<option value="Equatorial Guinea">Equatorial Guinea</option>
<option value="Eritrea">Eritrea</option>
<option value="Estonia">Estonia</option>
<option value="Ethiopia">Ethiopia</option>
<option value="Falkland Islands">Falkland Islands</option>
<option value="Faroe Islands">Faroe Islands</option>
<option value="Fiji">Fiji</option>
<option value="Finland">Finland</option>
<option value="France">France</option>
<option value="French Guiana">French Guiana</option>
<option value="French Polynesia">French Polynesia</option>
<option value="French Southern Ter">French Southern Ter</option>
<option value="Gabon">Gabon</option>
<option value="Gambia">Gambia</option>
<option value="Georgia">Georgia</option>
<option value="Germany">Germany</option>
<option value="Ghana">Ghana</option>
<option value="Gibraltar">Gibraltar</option>
<option value="Great Britain">Great Britain</option>
<option value="Greece">Greece</option>
<option value="Greenland">Greenland</option>
<option value="Grenada">Grenada</option>
<option value="Guadeloupe">Guadeloupe</option>
<option value="Guam">Guam</option>
<option value="Guatemala">Guatemala</option>
<option value="Guinea">Guinea</option>
<option value="Guyana">Guyana</option>
<option value="Haiti">Haiti</option>
<option value="Hawaii">Hawaii</option>
<option value="Honduras">Honduras</option>
<option value="Hong Kong">Hong Kong</option>
<option value="Hungary">Hungary</option>
<option value="Iceland">Iceland</option>
<option value="Indonesia">Indonesia</option>
<option value="India">India</option>
<option value="Iran">Iran</option>
<option value="Iraq">Iraq</option>
<option value="Ireland">Ireland</option>
<option value="Isle of Man">Isle of Man</option>
<option value="Israel">Israel</option>
<option value="Italy">Italy</option>
<option value="Jamaica">Jamaica</option>
<option value="Japan">Japan</option>
<option value="Jordan">Jordan</option>
<option value="Kazakhstan">Kazakhstan</option>
<option value="Kenya">Kenya</option>
<option value="Kiribati">Kiribati</option>
<option value="Korea North">Korea North</option>
<option value="Korea Sout">Korea South</option>
<option value="Kuwait">Kuwait</option>
<option value="Kyrgyzstan">Kyrgyzstan</option>
<option value="Laos">Laos</option>
<option value="Latvia">Latvia</option>
<option value="Lebanon">Lebanon</option>
<option value="Lesotho">Lesotho</option>
<option value="Liberia">Liberia</option>
<option value="Libya">Libya</option>
<option value="Liechtenstein">Liechtenstein</option>
<option value="Lithuania">Lithuania</option>
<option value="Luxembourg">Luxembourg</option>
<option value="Macau">Macau</option>
<option value="Macedonia">Macedonia</option>
<option value="Madagascar">Madagascar</option>
<option value="Malaysia">Malaysia</option>
<option value="Malawi">Malawi</option>
<option value="Maldives">Maldives</option>
<option value="Mali">Mali</option>
<option value="Malta">Malta</option>
<option value="Marshall Islands">Marshall Islands</option>
<option value="Martinique">Martinique</option>
<option value="Mauritania">Mauritania</option>
<option value="Mauritius">Mauritius</option>
<option value="Mayotte">Mayotte</option>
<option value="Mexico">Mexico</option>
<option value="Midway Islands">Midway Islands</option>
<option value="Moldova">Moldova</option>
<option value="Monaco">Monaco</option>
<option value="Mongolia">Mongolia</option>
<option value="Montserrat">Montserrat</option>
<option value="Morocco">Morocco</option>
<option value="Mozambique">Mozambique</option>
<option value="Myanmar">Myanmar</option>
<option value="Nambia">Nambia</option>
<option value="Nauru">Nauru</option>
<option value="Nepal">Nepal</option>
<option value="Netherland Antilles">Netherland Antilles</option>
<option value="Netherlands">Netherlands (Holland, Europe)</option>
<option value="Nevis">Nevis</option>
<option value="New Caledonia">New Caledonia</option>
<option value="New Zealand">New Zealand</option>
<option value="Nicaragua">Nicaragua</option>
<option value="Niger">Niger</option>
<option value="Nigeria">Nigeria</option>
<option value="Niue">Niue</option>
<option value="Norfolk Island">Norfolk Island</option>
<option value="Norway">Norway</option>
<option value="Oman">Oman</option>
<option value="Pakistan">Pakistan</option>
<option value="Palau Island">Palau Island</option>
<option value="Palestine">Palestine</option>
<option value="Panama">Panama</option>
<option value="Papua New Guinea">Papua New Guinea</option>
<option value="Paraguay">Paraguay</option>
<option value="Peru">Peru</option>
<option value="Phillipines">Philippines</option>
<option value="Pitcairn Island">Pitcairn Island</option>
<option value="Poland">Poland</option>
<option value="Portugal">Portugal</option>
<option value="Puerto Rico">Puerto Rico</option>
<option value="Qatar">Qatar</option>
<option value="Republic of Montenegro">Republic of Montenegro</option>
<option value="Republic of Serbia">Republic of Serbia</option>
<option value="Reunion">Reunion</option>
<option value="Romania">Romania</option>
<option value="Russia">Russia</option>
<option value="Rwanda">Rwanda</option>
<option value="St Barthelemy">St Barthelemy</option>
<option value="St Eustatius">St Eustatius</option>
<option value="St Helena">St Helena</option>
<option value="St Kitts-Nevis">St Kitts-Nevis</option>
<option value="St Lucia">St Lucia</option>
<option value="St Maarten">St Maarten</option>
<option value="St Pierre & Miquelon">St Pierre & Miquelon</option>
<option value="St Vincent & Grenadines">St Vincent & Grenadines</option>
<option value="Saipan">Saipan</option>
<option value="Samoa">Samoa</option>
<option value="Samoa American">Samoa American</option>
<option value="San Marino">San Marino</option>
<option value="Sao Tome & Principe">Sao Tome & Principe</option>
<option value="Saudi Arabia">Saudi Arabia</option>
<option value="Senegal">Senegal</option>
<option value="Seychelles">Seychelles</option>
<option value="Sierra Leone">Sierra Leone</option>
<option value="Singapore">Singapore</option>
<option value="Slovakia">Slovakia</option>
<option value="Slovenia">Slovenia</option>
<option value="Solomon Islands">Solomon Islands</option>
<option value="Somalia">Somalia</option>
<option value="South Africa">South Africa</option>
<option value="Spain">Spain</option>
<option value="Sri Lanka">Sri Lanka</option>
<option value="Sudan">Sudan</option>
<option value="Suriname">Suriname</option>
<option value="Swaziland">Swaziland</option>
<option value="Sweden">Sweden</option>
<option value="Switzerland">Switzerland</option>
<option value="Syria">Syria</option>
<option value="Tahiti">Tahiti</option>
<option value="Taiwan">Taiwan</option>
<option value="Tajikistan">Tajikistan</option>
<option value="Tanzania">Tanzania</option>
<option value="Thailand">Thailand</option>
<option value="Togo">Togo</option>
<option value="Tokelau">Tokelau</option>
<option value="Tonga">Tonga</option>
<option value="Trinidad & Tobago">Trinidad & Tobago</option>
<option value="Tunisia">Tunisia</option>
<option value="Turkey">Turkey</option>
<option value="Turkmenistan">Turkmenistan</option>
<option value="Turks & Caicos Is">Turks & Caicos Is</option>
<option value="Tuvalu">Tuvalu</option>
<option value="Uganda">Uganda</option>
<option value="United Kingdom">United Kingdom</option>
<option value="Ukraine">Ukraine</option>
<option value="United Arab Emirates">United Arab Emirates</option>
<option value="United States of America">United States of America</option>
<option value="Uraguay">Uruguay</option>
<option value="Uzbekistan">Uzbekistan</option>
<option value="Vanuatu">Vanuatu</option>
<option value="Vatican City State">Vatican City State</option>
<option value="Venezuela">Venezuela</option>
<option value="Vietnam">Vietnam</option>
<option value="Virgin Islands (Brit)">Virgin Islands (Brit)</option>
<option value="Virgin Islands (USA)">Virgin Islands (USA)</option>
<option value="Wake Island">Wake Island</option>
<option value="Wallis & Futana Is">Wallis & Futana Is</option>
<option value="Yemen">Yemen</option>
<option value="Zaire">Zaire</option>
<option value="Zambia">Zambia</option>
<option value="Zimbabwe">Zimbabwe</option>
<option value="other">Other</option>
</select>
</td>
</tr>
</table>
<table style="width: 100%; border-radius:0em; background-color: #0000003a; margin-top: 0.2em;" cellspacing="15" border="0">
<tr>
<td class="vmid spxConfigHead wrap" style="width: 30%;">
Industry
<div style="font-size: 0.7em; margin-top: 1em; opacity: 0.7;">
What is your main focus?
</div>
</td>
<td class="vmid">
<select class="wide drop bg_grey" name="indus">
<option {{{ifValueMatch config.registration.indus "empty" "selected" ""}}} value = "empty"></option>
<option {{{ifValueMatch config.registration.indus "broad" "selected" ""}}} value = "broad">Broadcast TV</option>
<option {{{ifValueMatch config.registration.indus "strea" "selected" ""}}} value = "strea">Streaming</option>
<option {{{ifValueMatch config.registration.indus "event" "selected" ""}}} value = "event">Events</option>
<option {{{ifValueMatch config.registration.indus "educa" "selected" ""}}} value = "educa">Education</option>
<option {{{ifValueMatch config.registration.indus "other" "selected" ""}}} value = "other">Other</option>
</select>
</td>
</tr>
</table>
<table style="width: 100%; border-radius:0em; background-color: #0000003a; margin-top: 0.2em;" cellspacing="15" border="0">
<tr>
<td class="vmid spxConfigHead wrap" style="width: 30%;">
Usage
<div style="font-size: 0.7em; margin-top: 1em; opacity: 0.7;">
What is or will most likely be the main use of SPX?
</div>
</td>
<td class="vmid">
<select class="wide drop bg_grey" name="usage">
<option {{{ifValueMatch config.registration.usage "empty" "selected" ""}}} value = "empty"></option>
<option {{{ifValueMatch config.registration.usage "perso" "selected" ""}}} value = "perso">For personal use</option>
<option {{{ifValueMatch config.registration.usage "nonpr" "selected" ""}}} value = "nonpr">For non-profit projects</option>
<option {{{ifValueMatch config.registration.usage "educa" "selected" ""}}} value = "educa">For education related projects</option>
<option {{{ifValueMatch config.registration.usage "mybus" "selected" ""}}} value = "mybus">For my own business</option>
<option {{{ifValueMatch config.registration.usage "workb" "selected" ""}}} value = "workb">For my employer's internal projects</option>
<option {{{ifValueMatch config.registration.usage "servi" "selected" ""}}} value = "servi">For selling production services</option>
<option {{{ifValueMatch config.registration.usage "other" "selected" ""}}} value = "other">Other</option>
</select>
</td>
</tr>
</table>
<table style="width: 100%; border-radius:0em; background-color: #0000003a; margin-top: 0.2em;" cellspacing="15" border="0">
<tr>
<td class="vmid spxConfigHead wrap" style="width: 30%;">
Developer level
<div style="font-size: 0.7em; margin-top: 1em; opacity: 0.7;">
What is your (organizations's) HTML/CSS template development skill level?
</div>
</td>
<td class="vmid">
<select class="wide drop bg_grey" name="skill">
<option {{{ifValueMatch config.registration.devlevel "empty" "selected" ""}}} value = "empty"></option>
<option {{{ifValueMatch config.registration.devlevel "highl" "selected" ""}}} value = "highl">Highly skilled in web development</option>
<option {{{ifValueMatch config.registration.devlevel "somew" "selected" ""}}} value = "somew">Some web development skills</option>
<option {{{ifValueMatch config.registration.devlevel "modif" "selected" ""}}} value = "modif">Can modify existing code</option>
<option {{{ifValueMatch config.registration.devlevel "learn" "selected" ""}}} value = "learn">Learning</option>
<option {{{ifValueMatch config.registration.devlevel "avoid" "selected" ""}}} value = "avoid">Trying to avoid</option>
<option {{{ifValueMatch config.registration.devlevel "noint" "selected" ""}}} value = "noint">Not interested</option>
</select>
</td>
</tr>
</table>
<table style="width: 100%; border-radius:0em; background-color: #0000003a; margin-top: 0.2em;" cellspacing="15" border="0">
<tr>
<td class="vmid spxConfigHead wrap" style="width: 30%;">
Anything else?
<div style="font-size: 0.7em; margin-top: 1em; opacity: 0.7;">
If there is something you would like us to know about, feel free to add it here.
</div>
</td>
<td class="vmid">
<textarea class="wide drop bg_grey"
style="height: 6em;"
name="freetext"
style=""
placeholder="Feedback, comments, ideas, etc."
id="freetext">{{config.registration.freetext}}</textarea>
</td>
</tr>
</table>
<table style="width: 100%; border-radius:0em; background-color: #0000003a; margin-top: 0.2em;" cellspacing="15" border="0">
<tr>
<td class="vmid spxConfigHead wrap" style="width: 30%;">
Email
<div style="font-size: 0.7em; margin-top: 1em; opacity: 0.7;">
Your email is never shared with any third parties
add you will not be spammed by us either.
</div>
</td>
<td class="vmid">
<input type="text" class="wide drop bg_grey"
placeholder="name@inter.net"
name="email" id="email" value="{{config.registration.email}}" oninput="checkEmail(this)">
<div style="font-size: 0.6em; margin-top: 1em; opacity: 0.5;">
<div id="checks">
<input {{{ifValueMatch config.registration.newsletter true "checked" ""}}}
type="checkbox"
name="newsletter"
id="newsletter"
style="transform: scale(1.5);"
>&nbsp;&nbsp;Join SPX newsletter (max 4 messages/year)
<BR>
</div>
</div>
</td>
</tr>
</table>
<table style="width: 100%; border-radius:0em; background-color: #0000003a; border:0px solid yellow; margin-top: 0.2em;" cellspacing="25" border="0">
<tr>
<td class="vmid">
<div class="wide spxInfotext center" style="font-size: 0.55em;">
This information is stored in <i>config.json</i> and if removed a menu item appears again.
<BR>
<BR>
<input type="submit" class="spxformtext btn w50 bg_green ripple" value="Submit" style="margin:0.0em 0em 0.4em; font-size:1.2em;">
<br>
<div style="text-align: center; opacity: 0.4; margin-top: 1em; font-size: 0.8em;">
<a href="#" onClick="skip();">Skip registration</a></span>
</div>
</div>
</td>
</tr>
</table>
<span style="font-size: 0.55em; opacity:0.9; color:#000000;">
v.{{{ShowVersion}}} | <a href="https://spxgraphics.com">&copy; 2020- SPX Graphics</a>
</span>
</td>
</tr>
</table>
</div><!-- center wrapper ended -->
</div><!-- content ended -->
</form>
</body>
</html>
+616
View File
@@ -0,0 +1,616 @@
<!DOCTYPE html>
<!-- DO NOT MODIFY THIS FILE -->
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<!-- xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx -->
<!-- SPX GRAPHICS CONTROLLER -->
<!-- ¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨¨ -->
<!-- (c) 2020- SPX Graphics -->
<!-- http://spxgraphics.com -->
<!-- -->
<!-- xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx -->
<!--
Scripts, no frameworks.
"Some extract of vanilla, enough to feed Godzilla."
-->
<script src="./js/socket.io.js"></script>
<script src="/js/lib/dotlottie-player.js"></script>
<script src="/js/spx_rendererUtils.js" type="module"></script> <!-- added in 1.3.3 RC-->
<script src="/js/ograf_functions.js"></script>
<script src="/js/spx_gc.js"></script>
<title>SPX Renderer</title>
<style>
* {
box-sizing: border-box;
overflow: hidden;
margin: 0;
padding: 0px;
}
body, html {
margin: 0;
padding: 0px;
background-color: transparent;
background-color: rgba(0,0,0,0);
color: black;
font-size: 3em;
}
#root {
padding: 0px;
/* MAIN RENDERER size set by SPX to {{width}}x{{height}} */
width: {{width}};
height: {{height}};
position: relative;
border: 0px dashed red;
background-size: 100% 100%;
background-color: rgba(0,0,0,0);
}
#overlays {
top: 0;
left: 0;
width: 100%;
height: 100%;
z-index: 200;
position: absolute;
border: 0px solid red;
display: flex;
justify-content: center;
align-items: center;
}
.frame {
margin: 0;
width: 100%;
height: 100%;
padding: 0px;
position: absolute;
border: 0px solid red;
}
.ografRenderTarget > iframe,
.ografRenderTarget {
margin: 0;
width: 100%;
height: 100%;
padding: 0px;
overflow: hidden;
position: absolute;
border: 0px dashed green;
}
.debugMessage{
top: 5%;
left: 10%;
opacity: 0.0;
z-index: 500;
font-size: 2em;
font-weight: bold;
position: absolute;
padding: 0.1em 0.5em;
display: inline-block;
color: rgb(3, 42, 3);
transform: rotate(1deg);
border: 1px solid green;
background-color: rgba(205, 255, 218, 0.85);
font-family: 'Courier New', Courier, monospace;
}
#cursorHider {
z-index: 200;
background-color: rgba(0,0,0,0.0);
cursor: none;
}
#wm {
top: 0;
left: 0;
width: 100%;
height: 100%;
z-index: 200;
position: absolute;
border: 0px dashed red;
background-color: rgba(0,0,0,0);
}
@keyframes rotate {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}
</style>
<script>
// Renderer info to console
console.log('RENDERER: ' + navigator?.userAgent);
// Renderer info to console
console.log('RENDERER: ' + navigator?.userAgent);
let incomingData; // a placeholder for templateData
let WMtimer = null;
let startTime = new Date().getTime();
function setTarget(LayerNro, projectFormat) {
if (projectFormat === "OGRAF") {
return 'div' + LayerNro;
} else {
return 'layer' + LayerNro;
}
}
function allowLayerControl(LayerNro) {
let playMode = incomingData.playmode || 'notFoundSoMustBeProgramCommand' ;
if ( isPreviewMode() && playMode != 'preview') {
return false;
}
if ( !isPreviewMode() && playMode == 'preview') {
return false;
}
const queryString = window.location.search;
const urlParams = new URLSearchParams(queryString);
const limitLayers = urlParams.get('layers');
LayerNro = Math.min(parseInt(LayerNro), 5);
if (limitLayers) {
let allowedLayers = JSON.parse(limitLayers);
if ( allowedLayers.includes(LayerNro) ) {
return true;
} else {
console.log('Layer ' + LayerNro + ' blocked, allows ', allowedLayers);
return false;
}
} else {
return true
}
}
function validateLayerNro(LayerNro) {
if (!allowLayerControl(LayerNro)) { return false }
if ( isPreviewMode() ) {
return 1
} else {
// console.log('Validating LayerNro ' + LayerNro); // debug
return Math.min(parseInt(LayerNro),5);
}
}
function isPreviewMode() {
const url = new URL(window.location.href);
let previewFlag = url.searchParams.get('preview') || '';
let previewPage = url.pathname || '';
if ( previewFlag == 'true' || previewPage.includes('preview') ) {
return true
} else {
return false
}
}
function isLocalRenderer() {
if (this.name == 'LOCAL') {
return true
} else {
return false
}
}
function clearLayer(LayerNro){
let ValidatedLayerNro = validateLayerNro(LayerNro);
// v1.1.4 allow clearing of all layers (even restricted ones)
if (!ValidatedLayerNro) {
ValidatedLayerNro = LayerNro
}
populateLayer(ValidatedLayerNro,'',true); // clear layer forcefully
}
function echo(msg) {
document.getElementById('debugMessage').innerText=msg;
}
function spxWM(mode=0,ref=0, minMins=15, maxMins=30) {
clearTimeout(WMtimer);
WMtimer = null;
let MODES = ['init','wait', 'play','stop', 'force'];
const player = document.getElementById('wm');
let file = '/img/json/wm-' + ref + '.json';
let fadeOutAfter = 8000; // Anim duration
switch (MODES[mode]) {
case 'init':
console.log('0: spxWM init');
player.style.opacity=0;
player.load(file);
spxWM(1,ref, minMins, maxMins);
return;
break;
case 'wait':
// DEBUG #######
// minMins = 0.2
// maxMins = 0.5
// #############
let minMS = (parseFloat(minMins) * 60 * 1000); // default 15 min
let maxMS = (parseFloat(maxMins) * 60 * 1000); // default 30 min
minMS = Math.max(minMS, fadeOutAfter+500)
let rnd = Math.floor(Math.random() * maxMS) + minMS;
console.log('1: spxWM delay sec', (rnd/1000));
WMtimer = setTimeout(function () {
spxWM(2,ref, minMins, maxMins);
}, rnd);
return;
break;
case 'play':
console.log('2: spxWM play');
player.style.opacity=1;
player.seek(0);
player.play();
spxWM(1,ref, minMins, maxMins);
setTimeout(function () {
player.style.opacity=0;
}, fadeOutAfter);
break;
case 'stop':
console.log('3: spxWM stop');
clearTimeout(WMtimer);
WMtimer = null;
player.stop();
player.style.opacity=0;
break;
case 'force':
console.log('4: force play wm');
player.load(file);
setTimeout(function () {
player.seek(0);
player.style.opacity=1;
player.play();
}, 200);
setTimeout(function () {
player.style.opacity=0;
if (WMtimer) {
clearTimeout(WMtimer);
spxWM(1,ref);
} else {
clearTimeout(WMtimer);
WMtimer = null;
}
}, fadeOutAfter*1000);
break;
}
}
function protocolCheck() {
// check if we are http or file.
let protocol = location.protocol;
if (protocol != "http:" && protocol != "https:") {
alert('Please use this via server! Functions and CORS may not work when used as a file. (Current protocol: ' + protocol + ')');
}
}
function remoteFunction(LayerNro, functionName, arg) {
LayerNro = validateLayerNro(LayerNro);
if (!LayerNro) { return }
let iframe = 'layer' + LayerNro;
document.getElementById(iframe).contentWindow.echo(arg);
}
function populateLayer(LayerNro,TemplateFileRef='', forced=false) {
let projectFormat = incomingData.projectFormat || 'SPX'; // default value
if (!forced) {
LayerNro = validateLayerNro(LayerNro);
if (!LayerNro) { return }
}
let renderTarget = setTarget(LayerNro, projectFormat);
let templa = '';
if (TemplateFileRef!='') {
templa = TemplateRoot + TemplateFileRef;
} else {
templa = TemplateRoot + 'empty.html';
}
templa = templa.replace(/\/\//g, "/");
let urlParams = new URLSearchParams(window.location.search);
let customParams = '';
for(let param of urlParams) {
customParams += '&' + param[0] + '=' + param[1];
}
let ranNro = (new Date()).getTime() + Math.floor(Math.random() * 1000000);
document.getElementById(renderTarget).src=templa + "?random=" + ranNro + customParams;
}
function playLayer(LayerNro,fieldData, projectFormat = 'SPX', incomingData) {
LayerNro = validateLayerNro(LayerNro);
if (!LayerNro) { return }
let renderTarget = setTarget(LayerNro, projectFormat)
let formattedJSON = formatJSONAndSetDomFields(fieldData, renderTarget);
let manifestPath = incomingData.relpath;
if (projectFormat === "OGRAF") {
loadTheGraphic("/templates" + manifestPath, renderTarget, incomingData);
} else {
document.getElementById(renderTarget).contentWindow?.update(formattedJSON);
document.getElementById(renderTarget).contentWindow?.play();
}
}
async function updateLayer(LayerNro,fieldData, projectFormat = "SPX") {
LayerNro = validateLayerNro(LayerNro);
if (!LayerNro) { return }
let renderTarget = setTarget(LayerNro, projectFormat)
let formattedJSON = formatJSONAndSetDomFields(fieldData, renderTarget);
if (projectFormat === "OGRAF") {
updateItem()
} else {
document.getElementById(renderTarget).contentWindow?.update(formattedJSON);
}
}
function formatJSONAndSetDomFields(fieldData, iframeID) {
let formattedJsonOut = {};
if (fieldData) {
var keys = [];
for (var k in fieldData) keys.push(k);
fieldData.forEach((item,index) => {
let KEY = Object.keys(item)[0];
let VAL = fieldData[index][Object.keys(item)[0]]
formattedJsonOut[KEY]=VAL;
});
return JSON.stringify(formattedJsonOut)
}
}
function stopLayer(LayerNro, projectFormat = "SPX") {
if (!allowLayerControl(LayerNro)) return; // check layer param
LayerNro = validateLayerNro(LayerNro);
let renderTarget = setTarget(LayerNro, projectFormat)
if (projectFormat === "OGRAF") {
graphicInstances[renderTarget]?.stopAction({})
} else {
document.getElementById(renderTarget).contentWindow?.stop();
}
}
var url_string = window.location.href;
var url = new URL(url_string);
let TemplateRoot;
var GCserver = url.searchParams.get("gc") || undefined;
if (GCserver==undefined) {
TemplateRoot = "/templates/";
var socket = io();
} else {
TemplateRoot = "templates/";
var socket = io(GCserver);
}
socket.on('connect', function () {
data={};
data.name = 'SPX_RENDERER'; // <---- Name of this socket connection
data.spxcmd = 'identifyClient';
socket.emit('SPXMessage2Server', data);
});
socket.on('SPXMessage2Client', function (data) {
incomingData = data;
let LayerNro;
let projectFormat = ""
let customCommand = ""
switch (data.spxcmd) {
case 'reloadRendererWithLayers':
let currURL = window.location.href;
const queryString = window.location.search;
const urlParams = new URLSearchParams(queryString);
let userName = urlParams.get('name') || 'NO_NAME';
let nameMatch = (userName.toUpperCase() === data?.name?.toUpperCase());
if (urlParams.get('remote')=='true' && nameMatch) {
let baseURL = currURL.split('?')[0];
let layerList = "[" + data.layers.join(',') + "]";
if (data?.layers?.length>0 ) {
window.location.href = baseURL + '?layers=' + layerList + '&remote=true&name=' + userName;
} else {
window.location.href = baseURL + '?remote=true&name=' + userName;
}
}
break;
case 'clearAllLayers':
spxWM(3);
document.getElementById('root').style.opacity=0;
document.querySelectorAll('.htmlFrame').forEach(function(frame,index) {
LayerNro = (index+1);
clearLayer(LayerNro,'');
});
document.querySelectorAll('.ografFrame').forEach(function(frame,index) {
LayerNro = (index+1);
frame.innerHTML = '';
});
setTimeout(function() {
document.getElementById('root').style.opacity=1;
}, 200);
break;
case 'clearLayer':
clearLayer(data.layerno);
break;
case 'loadTemplate':
populateLayer(data.webplayout,data.relpath);
break;
case 'forceWM':
spxWM(4);
break;
case 'playTemplate':
projectFormat = incomingData.projectFormat || 'SPX';
if (data.modify && data.modify==='autoPlayLocal' ) {
if (!isLocalRenderer()) {
return;
}
}
LayerNro = validateLayerNro(data.webplayout);
if (!LayerNro) { return }
if (projectFormat === "OGRAF") {
playLayer(LayerNro, data.fields, projectFormat, incomingData);
}else {
document.getElementById('layer' + LayerNro).onload = function() {
playLayer(LayerNro, data.fields, projectFormat, incomingData);
};
}
populateLayer(data.webplayout,data.relpath);
break;
case 'nextTemplate':
projectFormat = incomingData.projectFormat || 'SPX';
LayerNro = validateLayerNro(data.webplayout);
let renderTarget = setTarget(LayerNro, projectFormat);
if (!LayerNro) { return }
if (projectFormat === "OGRAF") {
graphicInstances[renderTarget].playAction();
} else {
document.getElementById(renderTarget).contentWindow?.next();
}
break;
case 'updateTemplate':
projectFormat = incomingData.projectFormat || 'OGRAF'; // default value
LayerNro = validateLayerNro(data.webplayout);
setTimeout(function(){ updateLayer(data.webplayout, data.fields, projectFormat); }, 10);
break;
case 'stopTemplate':
projectFormat = incomingData.projectFormat || 'SPX'; // default value
LayerNro = validateLayerNro(data.webplayout);
if (!LayerNro) { return }
stopLayer(LayerNro, projectFormat);
break;
case 'customAction':
LayerNro = validateLayerNro(data.webplayout)
graphicInstances['div' + LayerNro].customAction({id: incomingData.id})
case 'invokeFunction':
LayerNro = validateLayerNro(data.webplayout);
if (!LayerNro) { return }
try {
// Important change! --------------------------------------------------
// Starting from version 1.2.0 it is recommended to use
// "data.function" and "data.params" instead of "data.invoke"
// that was a string representation of a function call with
// parameters. Server will log all uses of the old "data.invoke"
// and will recommend to use the new "data.function" and "data.params"
// instead. The old "data.invoke" will be deprecated in the future.
// --------------------------------------------------------------------
if (data.function) {
var myFunction = data.function;
var myParamets = data.params || null;
if (typeof document.getElementById('layer' + LayerNro).contentWindow[myFunction] === "function") {
let paramPreview = '';
if (myParamets && myParamets!="undefined") {
paramPreview = 'First 30 chars of args: ' + myParamets.substring(0, 30);
} else {
paramPreview = 'No args.';
}
document.getElementById('layer' + LayerNro).contentWindow[myFunction](myParamets);
} else {
console.warn('Function "' + myFunction + '" not found on layer ' + LayerNro + '. Verify function and loaded template.');
}
let ografLayer = document.getElementById('div' + LayerNro)
let ografFrame = ografLayer.querySelector('.ografRenderTarget').querySelector('iframe')
let ografContent = ografFrame.contentWindow
if (typeof ografContent[myFunction] === "function") {
ografContent[myFunction](myParamets);
} else {
console.warn('Function "' + myFunction + '" not found on OGraf ' + LayerNro + '. Verify function and loaded graphic.');
}
} else {
if (data.invoke) {
let warning = '\DEPRECATION WARNING\n';
warning += '`data.invoke` will be deprecated in the next SPX version. Refactor your extensions to '
warning += 'use `data.function` and `data.params` instead when calling the /gc/playout API or use ';
warning += 'the public API endpoint /api/v1/invokeTemplateFunction instead.\n';
warning += 'You had a call [' + data.invoke + '].\n\n';
console.warn(warning);
let myFunction = data.invoke.split('(')[0]; // Checking if template has this function
if (typeof document.getElementById('layer' + LayerNro).contentWindow[myFunction] === "function") {
eval("document.getElementById('layer' + " + LayerNro + ").contentWindow." + data.invoke);
} else {
console.warn('Function "' + myFunction + '" not found on layer ' + LayerNro + '. Verify function and loaded template.');
}
}
}
} catch(err) {
console.warn('Error invoking "' + myFunction + '" on layer ' + LayerNro + '. Verify function and loaded template.');
}
break;
case 'setRendererBackgroundImage':
if (data.background != '' ) {
document.getElementById('root').style.backgroundImage = 'url(/' + data.background + ')';
} else {
document.getElementById('root').style.backgroundImage = '';
}
break;
default:
// console.log('Unknown SPXMessage2Client command in renderer: ' + data.spxcmd);
}
});
socket.on('SPXWebRendererMessage', function (data) {
// Handles messages coming from server to this client.
// All comms using 'SPXWebRendererMessage' as a conduit with data object and
// data.spxcmd as function identifier. Additional object values are payload.
// console.log('SPXWebRendererMessage received (but doing nothing to it)', data)
}); // socketMessage handler ended
window.addEventListener('load',protocolCheck,false);
</script>
</head>
<body>
<div id="root">
<dotlottie-player id="wm"></dotlottie-player>
{{#ifValue hideCursor "true"}}
<div id="cursorHider"></div>
{{/ifValue}}
<!-- SPX Solo does not support in-layer transitions -->
<iframe class="frame htmlFrame" allow="autoplay" id="layer1" style="z-index: 101;" src="about:blank"></iframe>
<iframe class="frame htmlFrame" allow="autoplay" id="layer2" style="z-index: 102;" src="about:blank"></iframe>
<iframe class="frame htmlFrame" allow="autoplay" id="layer3" style="z-index: 103;" src="about:blank"></iframe>
<iframe class="frame htmlFrame" allow="autoplay" id="layer4" style="z-index: 104;" src="about:blank"></iframe>
<iframe class="frame htmlFrame" allow="autoplay" id="layer5" style="z-index: 105;" src="about:blank"></iframe>
<div class="frame ografFrame" allow="autoplay" id="div1" style="z-index: 201"></div>
<div class="frame ografFrame" allow="autoplay" id="div2" style="z-index: 202"></div>
<div class="frame ografFrame" allow="autoplay" id="div3" style="z-index: 203"></div>
<div class="frame ografFrame" allow="autoplay" id="div4" style="z-index: 204"></div>
<div class="frame ografFrame" allow="autoplay" id="div5" style="z-index: 205" ></div>
</div>
<div class="debugMessage" id="debugMessage" style="opacity:0; position: absolute; z-index:1000;"></div>
</body>
</html>
+179
View File
@@ -0,0 +1,179 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" type="text/css" href="/css/spx_renderpopups.css">
<title>SPX - scalable renderer view</title>
<script src="/js/anime.min.js"></script>
<style>
html,
body {
width: 100vw;
height: 100vh;
padding: 0;
margin: 0;
background-color: rgb(0,0,0);
display: flex;
align-items: center;
justify-content: center;
overflow: hidden;
}
*, *:before, *:after {
box-sizing: inherit;
margin: 0;
padding: 0;
user-select: none;
}
.renderer {
/* rendererscalable hardcoded to 1920x1080 data received {{width}}x{{height}}*/
margin: 0;
padding: 0;
border: 0px;
outline: 0px;
width: {{width}};
height: {{height}};
ZZZwidth: 1920px;
ZZZheight: 1080px;
background-size: 10vw;
transform-origin: top left;
background-image: url('/img/checker60.png'); /* bgimage for local preview */
}
#message {
width: 100%;
height: 100%;
z-index: 100;
display: flex;
color: orange;
font-size: 2.0vw;
line-height: 1.5em;
position: absolute;
align-items: center;
flex-direction: column;
justify-content: center;
text-shadow: 2px 2px 4px black;
font-family: Arial, Helvetica, sans-serif;
}
.pale {
color: #aaa;
}
#previewBG {
display: flex;
align-items: center;
justify-content: center;
border: 0px solid yellowgreen;
}
#previewIF {
transform-origin: top left;
}
</style>
<script src="/js/socket.io.js"></script>
<script>
var socket = io();
let socData={};
socket.on('connect', function () {
{{#ifValue mode 'preview'}}
socData.name = 'SPX_PREVIEW';
{{/ifValue}}
{{#ifValue mode 'program'}}
socData.name = 'SPX_PROGRAM';
{{/ifValue}}
socData.spxcmd = 'identifyClient';
socket.emit('SPXMessage2Server', socData);
});
socket.on('SPXMessage2Client', function (data) {
if (data.spxcmd == 'closepreview' && socData.name == 'SPX_PREVIEW') {
window.close();
}
if (data.spxcmd == 'closeprogram' && socData.name == 'SPX_PROGRAM') {
window.close();
}
}); // end SPXMessage2Client
</script>
</head>
<body onresize="resizePreview();">
{{#ifValue mode 'preview'}}
<div class="watermark prev"></div>
{{/ifValue}}
{{#ifValue mode 'program'}}
<div class="watermark prog"></div>
{{/ifValue}}
<div id="previewBG" style="background-color: rgb(0, 0, 0); overflow: hidden;">
{{#ifValue mode 'preview'}}
<iframe id="previewIF" src="/renderer?preview=true" class="renderer"></iframe>
{{else}}
<iframe id="previewIF" src="/renderer" class="renderer"></iframe>
{{/ifValue}}
</div>
<div id="message">
THIS SCALABLE VIEW IS FOR SPX OPERATOR ONLY
<br>
<small class="pale">Use transparent <span style="color:#fff;">/renderer</span> for production.</small>
</div>
<script>
anime({
targets: '#message',
opacity: 0,
duration: 1000,
easing: 'linear',
delay: 2000
});
function resizePreview() {
// renderscalable
// Scalable renderer size set by SPX to {{width}}x{{height}}
let DefaultX = '{{width}}'; // Math always defaults to 16:9
let DefaultY = '{{height}}';
// console.log('Resizer ' + DefaultX + ' x ' + DefaultY + ' px');
const vw = Math.max(document.documentElement.clientWidth || 0, window.innerWidth || 0)
const vh = Math.max(document.documentElement.clientHeight || 0, window.innerHeight || 0)
if (DefaultX.includes('vw')) {
// Responsive settings, use window size! Added in v1.3.0
// console.log('Responsive settings, use window size! Added in v1.3.0');
DefaultX = vw;
DefaultY = vh;
}
let Wpx = vw// document.getElementById('previewWidthRuler').offsetWidth;
let Sca = Wpx/parseInt(DefaultX);
let Hpx = parseInt(parseInt(DefaultY) * Sca);
document.getElementById('previewBG').style.width=Wpx+'px';
document.getElementById('previewBG').style.height=Hpx+'px';
document.getElementById('previewIF').style.transform="scale("+Sca+")";
document.getElementById('previewBG').style.display='block';
// console.log('Resized to ' + Wpx + ' x ' + Hpx + ' px, scale multiplier: ' + Sca);
}
resizePreview();
</script>
</body>
</html>
+540
View File
@@ -0,0 +1,540 @@
<!DOCTYPE html>
{{>copyright }}
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<script src="/js/axios.min.js"></script>
<script src="/js/fontawesome.all.min.js"></script>
<script src="/js/Sortable.min.js"></script>
<script src="/js/anime.min.js"></script>
<script src="/js/socket.io.js"></script>
<script src="/js/spx_gc.js"></script>
<script src="/js/spx_showConfig.js"></script>
<title>SPX | {{lang 'pagetitle.configshow'}}</title>
<link rel="icon" type="image/png" href="/img/spx-gc.ico">
<link rel="stylesheet" type="text/css" href="/css/spx_fonts.css">
<link rel="stylesheet" type="text/css" href="/css/spx_layout.css">
<link rel="stylesheet" type="text/css" href="/css/spx_forms.css">
<link rel="stylesheet" type="text/css" href="/css/spx_colors.css">
<link rel="stylesheet" type="text/css" href="/css/spx_accents.css">
<link rel="stylesheet" type="text/css" href="/css/spx_modal.css">
</head>
<body onload="pageInit();" class="bg_body" onmouseover="tip('');">
<input type="hidden" id="treedata" value="{{TemplateFiles}}">
<input type="hidden" id="showfolder" value="{{folder}}">
<input type="hidden" id="templateroot" value="{{TemplateRootFolder}}">
<input type="hidden" id="templaterootmassage" value="{{lang 'error.preventBrowseUp'}}">
<input type="hidden" id="projectFormat" value="{{showconfig.projectFormat}}">
{{!-- <h1>format: {{showconfig.projectFormat}}</h1> --}}
{{>partial-templatebrowsermodal}}
{{>partial-showextrabrowsermodal}}
<div class="wrapper">
<div class="header">
{{>header page="showconfig"}}
</div><!-- header ended -->
<div class="content">
<div class="centerColumn pM wrapper">
<div id="itemList" class="content pM scroller" style="border: 0px solid yellow;">
{{#if messageCode}}
<div class="message">{{messageCode}}</div>
{{/if}}
{{#if errorCode}}
<div class="messagerror"><B>{{errorCode}}</B><BR><BR>{{lang errorCode}}</div>
{{/if}}
<div style="display: flex;">
<div style="flex-grow: 1" class="vmidTD tableheaderText">{{lang 'config.templates'}}</div>
<div class="tableheaderHelp">
{{#ifValue showconfig.projectFormat 'OGRAF'}}
<a onClick="help('ograf');" onmouseover="tip('What is OGraf?');">
<img src="/img/ograf-logo-mono-white.svg" style="width: 5vw;">
</a>
{{/ifValue}}
&nbsp;
</div>
<div class="tableheaderHelp"><a title="{{lang 'general.help'}}" onClick="help('projecttemplates');"
onmouseover="tip('{{lang 'hover.help'}}');">
<i class="far fa-question-circle"></i>
</a></div>
</div>
<!--
Move this nag to documentation instead...
<span style="display: block; padding: 0.6em 1.7em; background: #0000004b; color:#ffffff; font-size:0.7em; border-radius:9px; margin_5px; text-align:center;">
{{lang 'warning.showprofilebackup'}}
</span><BR>
-->
{{!-- <table class="wide" border=0>
<TR>
<td class="vmid tableheaderText">{{lang 'config.templates'}}</td>
<td class="vmid tableheaderHelp aright" title="{{lang 'general.help'}}" onClick="help('projecttemplates');" onmouseover="tip('{{lang 'hover.help'}}');"><i class="far fa-question-circle"></i>&nbsp;</td>
</TR>
</table> --}}
<hr class="h2hr"><!-- =========================================== -->
<!-- iterate all templates from showconfig ========================================================================= -->
{{#each showconfig.templates}}
<div id="template{{@index}}">
<button class="collapsible bg_grey15 row_accent{{uicolor}}" type="button" onmouseover="tip('{{lang 'hover.confitem'}}');">
{{{nameFromTemplatePath relpath }}}
<SPAN style="background: #fff; padding: 0.1em 0.5em; border-radius: 0.5em; border: 0px solid yellow; float:right; opacity:0.3; color: #000">{{playlayer}} | {{{max5 webplayout}}}</SPAN>
</button>
<div class="collapsiblecontent spxConfigHead" >
<form name="config{{@index}}" id="config{{@index}}" method="post" action="config/saveTemplate">
<input type="hidden" name="showFolder" value="{{../folder}}">
<input type="hidden" name="TemplIndex" value="{{@index}}">
<input type="hidden" name="templateData[DataFields]" value="{{{DataToJSONString DataFields}}}">
<table class="wide" style="border: 0px solid pink; border-spacing: 10px; border-collapse: separate;" border="0">
<tr>
<td><span class="spxConfigHead">{{lang 'config.templatefile'}}</span></td>
<td><input type="text" class="wide bg_grey15 cfg" id="templateIndex{{@index}}" placeholder="Template file" name="templateData[relpath]" value="{{relpath}}" onmouseover="tip('{{lang 'hover.templatepath'}}');"></td>
</tr>
<tr>
<td><span class="spxConfigHead">{{lang 'config.description'}}</span></td>
<td><input type="text" class="wide bg_grey15 cfg" placeholder="Description" name="templateData[description]" value="{{description}}" onmouseover="tip('{{lang 'hover.templatedescr'}}');"></td>
</tr>
<tr>
<td><span class="spxConfigHead">{{lang 'config.inputfields'}}</span></td>
<td onmouseover="tip('{{lang 'hover.fields'}}');">
<span class="spxConfigHead">
{{DataFields.length}} {{lang 'general.pcs'}}
<!-- removed in 1.0.14
{{#each DataFields}}
&nbsp;{{title}}
{{/each}}
-->
</span>
</td>
</tr>
<tr>
<td width="40%"><span class="spxConfigHead">{{lang 'config.ccgplayout'}}</span></td>
<td>
<div class="" style="width: 100%; border: 0px solid blue;">
<select style="width: 50%;" class="drop bg_grey15" name="templateData[playserver]" onmouseover="tip('{{lang 'hover.ccgserver'}}');">
{{{generateCasparCgPlayoutOptions playserver}}}
</select>
<input style="width: 23%;" type="text" class="bg_grey15 cfg" onmouseover="tip('{{lang 'hover.ccgchannel'}}');" placeholder="Channel" name="templateData[playchannel]" value="{{playchannel}}">
<input style="width: 23%;" type="text" class="bg_grey15 cfg" onmouseover="tip('{{lang 'hover.ccglayer'}}');" placeholder="Layer" name="templateData[playlayer]" value="{{playlayer}}">
</div>
</td>
</tr>
<tr>
<td width="40%"><span class="spxConfigHead">{{lang 'config.dataformat'}}</span></td>
<td>
<select class="wide drop bg_grey15" name="templateData[dataformat]" onmouseover="tip('{{lang 'hover.dataformat'}}');">
{{{generateDataFormatOptions dataformat}}}
</select>
</td>
</tr>
<tr>
<td width="40%"><span class="spxConfigHead">{{lang 'config.webplayout'}}</span></td>
<td>
<select class="wide drop bg_grey15" name="templateData[webplayout]" onmouseover="tip('{{lang 'hover.weblayer'}}');">
{{{generateWebPlayoutOptions webplayout}}}
</select>
</td>
</tr>
<tr>
<td width="40%"><span class="spxConfigHead">{{lang 'config.stopmode'}}</span></td>
<td>
<select class="wide drop bg_grey15" name="templateData[out]" onmouseover="tip('{{lang 'hover.outmode'}}');">
{{{generateOutOptions out}}}
</select>
</td>
</tr>
<tr>
<td width="40%"><span class="spxConfigHead">{{lang 'config.coloraccent'}}</span></td>
<td onmouseover="tip('{{lang 'hover.accent'}}');">
{{{generateColorAccents uicolor @index}}}
</td>
</tr>
{{#if imported}}
<tr>
<td width="40%"><span class="spxConfigHead">{{lang 'config.updated'}}</span></td>
<td onmouseover="tip('{{lang 'hover.updated'}}');">
<span class="spxConfigHead">
{{{generateTimestamp imported}}}
</span>
</td>
</tr>
{{/if}}
</table>
<table class="wide" style="padding: 10px; margin-bottom:10px;">
<tr>
<td width="25%">
<button type="button"
class="wide spxFlexItem btnsmall bg_grey10 ripple black"
onclick="removeTemplateIndex({{@index}});"
onmouseover="tip('{{lang 'hover.removetemplate'}}');"
>{{lang 'button.deletetemplate'}}</button>
</td>
<td width="25%">
<button type="button"
class="wide spxFlexItem btnsmall bg_grey10 ripple black"
onclick="reimportTemplateIndex({{@index}});"
onmouseover="tip('{{lang 'hover.replacetemplate'}}');"
>{{lang 'button.replacetemplate'}}</button>
</td>
<td>&nbsp;</td>
<td width="25%" class="aright">
<button type="submit"
class="wide spxFlexItem btnsmall bg_grey10 ripple black"
onmouseover="tip('{{lang 'hover.saveprofile'}}');"
>{{lang 'button.save'}}</button>
</td>
</tr>
</table>
</form>
</div><!--ended item -->
</div><!-- index ended -->
{{/each}}
<!-- templates ended from showconfig ========================================================================= -->
<div class="wide" style="text-align: center; padding:0.5rem;">
{{#if showconfig.templates}}
<!-- templates above -->
{{else}}
<span style="display: inline-block; margin: 10px;padding: 0.6em 1.7em; background: #0000001c; color:#ffffff; font-size:0.7em; border-radius:2em; margin_5px; text-align:center;">
{{lang 'config.addfirsttemplate'}}
</span><BR>
{{/if}}
<button type="button" class="addbtn bg_blue ripple" onClick="ModalOn('fileoverlay');" onmouseover="tip('{{lang 'hover.browsetemplate'}}');">+</button>
</div>
<BR><BR>
<table class="wide" border=0>
<TR>
<td class="vmid tableheaderText">{{lang 'config.general'}}</td>
<td class="vmid tableheaderHelp aright" title="{{lang 'general.help'}}" onClick="help('projectgeneral');" onmouseover="tip('{{lang 'hover.help'}}');"><i class="far fa-question-circle"></i>&nbsp;</td>
</TR>
</table>
<hr class="h2hr"><!-- =========================================== -->
<div class="configGeneralContent spxConfigHead" >
<form name="configGeneral" id="configGeneral" method="post" action="config/saveGeneralSettings">
<input type="hidden" name="showFolder" value="{{folder}}">
<!-- background option -->
<table class="wide" style="border: 0px solid pink; border-spacing: 10px; border-collapse: separate;" border="0">
<tr>
<td width="50%"><span class="spxConfigHead">{{lang 'config.backgroundImage'}}</span></td>
<td width="30%">
<select style="width: 100%; color:#white; background-color: #656E7A;" class="drop" name="background" onmouseover="tip('Choose an image');">
<option value="">{{lang 'general.none'}} ({{lang 'color.transparent'}})</option>
<option class="generalSettingDropdown" disabled></option>
<option class="generalSettingDropdown" disabled>{{lang 'config.optionalBgImage'}}:</option>
<option class="generalSettingDropdown" disabled>ASSETS/media/images/hd</option>
<option class="generalSettingDropdown" disabled></option>
{{{ PopulateFilelistOptions 'media/images/hd' 'png' showconfig.general.background }}}
</select>
</td>
<td width="20%">
<button type="submit" onmouseover="tip('{{lang 'hover.cancelconfig'}}');" class="wide btn bg_grey10 ripple">SET</button>
</td>
</tr>
</table>
</form>
</div>
<BR><BR>
<!-- Project Variables are not used right now
<table class="wide" border=0>
<TR>
<td class="vmid tableheaderText">{{lang 'config.projectvariables'}}</td>
<td class="vmid tableheaderHelp aright" title="{{lang 'general.help'}}" onClick="help('projectvariables');" onmouseover="tip('{{lang 'hover.help'}}');"><i class="far fa-question-circle"></i>&nbsp;</td>
</TR>
</table>
<hr class="h2hr">
<div class="configGeneralContent" >
<table class="wide" style="border: 0px solid pink; border-spacing: 10px; border-collapse: separate;" border="0">
<tr>
<td width="25%" class="spxTableHeadsmall">Variable description</td>
<td width="25%" class="spxTableHeadsmall" align="right">Name: pcs&nbsp;&nbsp;&nbsp;</td>
<td width="50%" class="spxTableHeadsmall">&nbsp;Value</td>
</tr>
{{#each showconfig.variables}}
<tr>
<td width="25%"><span class="spxConfigHead">{{title}}</span></span></td>
<td width="25%"><span class="spxConfigHead"><span class="prvar">{{prvar}}: {{{itemCount users}}}</span></span></td>
<td width="50%"><input type="text" class="wide bg_grey15 cfg" id="templateIndex{{@index}}" name="templateData[relpath]" value="{{value}}" onmouseover="tip('{{lang 'hover.templatepath'}}');"></td>
</tr>
{{/each}}
</table>
</div>
<BR><BR>
-->
<table class="wide" border=0>
<TR>
<td class="vmid tableheaderText">{{lang 'config.showextras'}}</td>
<td class="vmid tableheaderHelp aright" title="{{lang 'general.help'}}" onClick="help('projectextras');" onmouseover="tip('{{lang 'hover.help'}}');"><i class="far fa-question-circle"></i>&nbsp;</td>
</TR>
</table>
<hr class="h2hr"><!-- =========================================== -->
<!-- script linker -->
<div id="scriptLinker">
<button class="collapsible bg_grey15 row_accentExtras" type="button">{{lang 'config.functionlibrary'}}</button>
<div class="collapsiblecontent spxConfigHead" >
<form name="extrascript" id="extrascript" method="post" action="config/saveExtraScript">
<table class="wide" style="border: 0px solid pink; border-spacing: 10px; border-collapse: separate;" border="0">
<tr>
<td><span class="spxConfigHead">{{lang 'config.javascriptfile'}}</span></td>
<td>
<input type="text" class="wide bg_grey15 cfg" placeholder="ExtraFunctions/myExtraFunctions.js" id="customscript" name="customscript"
{{#if showconfig.showExtras.customscript}}
value="{{showconfig.showExtras.customscript}}"
{{else}}
value="ExtraFunctions/demoFunctions.js"
{{/if}}
onmouseover="tip('{{lang 'hover.showjsfile'}}');">
</td>
</tr>
<tr>
<td>&nbsp;</td>
<td><button type="button" class="wide spxFlexItem btnsmall bg_grey10 ripple black" onClick="addShowScript();" onmouseover="tip('{{lang 'hover.saveprofile'}}');">{{lang 'button.save'}}</button></td>
</td>
</tr>
</table>
</form>
</div><!--ended extra item -->
</div><!-- extra index ended -->
<!-- //script linker -->
{{#each showconfig.showExtras.CustomControls}}
<!-- each extra button -->
<div id="extra{{@index}}">
<button class="collapsible bg_grey15 row_accentExtras" type="button">{{description}}</button>
<div class="collapsiblecontent spxConfigHead" >
<form name="extra{{@index}}" id="extra{{@index}}" method="post" action="config/saveExtra">
<input type="hidden" name="ExtraIndex" value="{{@index}}">
<input type="hidden" name="showFolder" value="{{../folder}}">
<input type="hidden" name="extraData[ftype]" value="{{ftype}}">
<table class="wide" style="border: 0px solid pink; border-spacing: 10px; border-collapse: separate;" border="0">
<tr onmouseover="tip('{{lang 'hover.buttontype'}}');">
<td><span class="spxConfigHead">{{lang 'config.buttontype'}}</span></td>
<td><span class="spxConfigHead">{{ftype}}</span></td>
</tr>
<tr>
<td><span class="spxConfigHead">{{lang 'config.functiondescription'}}</span></td>
<td><input type="text" class="wide bg_grey15 cfg" placeholder="Description" name="extraData[description]" value="{{description}}" onmouseover="tip('{{lang 'hover.extradescr'}}');"></td>
</tr>
<tr onmouseover="tip('{{lang 'hover.colors'}}');">
<td width="40%"><span class="spxConfigHead">{{lang 'config.buttoncolor'}}</span></td>
<td>
<select class="wide drop bg_grey15" name="extraData[bgclass]">
{{{generateButtonColorClasses bgclass}}}
</select>
</td>
</tr>
{{#ifValue ftype "button"}}
<tr onmouseover="tip('{{lang 'hover.text'}}');">
<td><span class="spxConfigHead">{{lang 'config.buttontext'}}</span></td>
<td>
<input type="text" class="wide bg_grey15 cfg" placeholder="Caption" name="extraData[text]" value="{{text}}" >
</td>
</tr>
<tr onmouseover="tip('{{lang 'hover.fcall'}}');">
<td><span class="spxConfigHead">{{lang 'config.functionhandler'}}</span></td>
<td><input type="text" class="wide bg_grey15 cfg" placeholder="clickHandler" name="extraData[fcall]" value="{{fcall}}" ></td>
</tr>
{{/ifValue}}
{{#ifValue ftype "togglebutton"}}
<tr>
<td><span class="spxConfigHead">{{lang 'config.buttontexts'}}</span></td>
<td>
<input type="text" style="width: 49%;" class="bg_grey15 cfg" placeholder="ON" name="extraData[text0]" value="{{text0}}" onmouseover="tip('{{lang 'hover.text'}}');">
<input type="text" style="width: 49%;" class="bg_grey15 cfg" placeholder="OFF" name="extraData[text1]" value="{{text1}}" onmouseover="tip('{{lang 'hover.text1'}}');">
</td>
</tr>
<tr onmouseover="tip('{{lang 'hover.fcall'}}');">
<td><span class="spxConfigHead">{{lang 'config.functionhandler'}}</span></td>
<td><input type="text" class="wide bg_grey15 cfg" placeholder="toggleHandler" name="extraData[fcall]" value="{{fcall}}" ></td>
</tr>
{{/ifValue}}
{{#ifValue ftype "selectbutton"}}
<input type="hidden" name="value" value="{{value}}">
<tr onmouseover="tip('{{lang 'hover.text'}}');">
<td><span class="spxConfigHead">{{lang 'config.buttontext'}}</span></td>
<td><input type="text" class="wide bg_grey15 cfg" placeholder="Caption" name="extraData[text]" value="{{text}}" ></td>
</tr>
<tr onmouseover="tip('{{lang 'hover.fcall'}}');">
<td><span class="spxConfigHead">{{lang 'config.functionhandler'}}</span></td>
<td><input type="text" class="wide bg_grey15 cfg" placeholder="toggleHandler" name="extraData[fcall]" value="{{fcall}}" ></td>
</tr>
<tr onmouseover="tip('{{lang 'hover.defaultvalue'}}');">
<td><span class="spxConfigHead">{{lang 'config.defaultvalue'}}</span></td>
<td><input type="text" class="wide bg_grey15 cfg" placeholder="" name="extraData[value]" value="{{value}}" ></td>
</tr>
<tr onmouseover="tip('{{lang 'hover.options'}}');">
<td><span class="spxConfigHead vtop">{{lang 'config.options'}}</span></td>
<td>
{{#each items}}
<input type="text" class="w50 bg_grey15 cfg" placeholder="{{lang 'config.text'}}" name="extraData[items][{{@index}}][text]" value="{{text}}" >
<input type="text" class="w50 bg_grey15 cfg" placeholder="{{lang 'config.value'}}" name="extraData[items][{{@index}}][value]" value="{{value}}" ><BR>
<!-- name="casparcg[servers][{{@index}}][name]" -->
{{/each}}
<input type="text" class="w50 bg_grey15 cfg" placeholder="{{lang 'config.text'}}" name="newTXT" value="" >
<input type="text" class="w50 bg_grey15 cfg" placeholder="{{lang 'config.value'}}" name="newVAL" value="" ><BR>
<span style="display:block; font-size: 0.7em; text-align:center;">{{lang 'config.addnewtip'}}</span>
</td>
</tr>
{{/ifValue}}
</table>
<table class="wide" style="padding: 10px; margin-bottom:10px;">
<tr >
<td width="25%" onmouseover="tip('{{lang 'hover.removetemplate'}}');">
<button type="button" class="wide spxFlexItem btnsmall bg_grey10 ripple black" onclick="removeExtraIndex({{@index}});" >{{lang 'button.deletebutton'}}</button>
</td>
<td>&nbsp;</td>
<td width="25%" class="aright" onmouseover="tip('{{lang 'hover.saveprofile'}}');">
<button type="submit" class="wide spxFlexItem btnsmall bg_grey10 ripple black" >{{lang 'button.save'}}</button>
</td>
</tr>
</table>
</form>
</div><!--ended extra item -->
</div><!-- extra index ended -->
<!-- //each extra button -->
{{/each}}
<div class="wide" style="text-align: center; padding:0.5rem;">
{{#if showconfig.showExtras.CustomControls}}
<!-- templates above -->
{{else}}
<span style="display: inline-block; margin: 10px;padding: 0.6em 1.7em; background: #0000001c; color:#ffffff; font-size:0.7em; border-radius:2em; margin_5px; text-align:center;">
{{lang 'config.addfirstextra'}}
</span><BR>
{{/if}}
<button type="button" class="addbtn bg_blue ripple" onClick="ModalOn('extraoverlay');" onmouseover="tip('{{lang 'hover.browseextras'}}');">+</button>
</div>
</div><!-- itemlist ended -->
</div><!-- center wrapper ended -->
</div><!-- content ended -->
{{>footer}}
</div><!-- MAINwrapper ended -->
<script>
// See spx_gc.js
function pageInit(){
RenderFolder(JSON.parse(document.getElementById('treedata').value));
// COLLAPSIPLE HANDLERS
var coll = document.getElementsByClassName("collapsible");
var i;
for (i = 0; i < coll.length; i++) {
coll[i].addEventListener("click", function() {
this.classList.toggle("active");
var content = this.nextElementSibling;
if (content.style.maxHeight){
content.style.maxHeight = null;
} else {
content.style.maxHeight = content.scrollHeight + "px";
}
});
}
// TREE HANDLERS
// https://www.w3schools.com/howto/howto_js_treeview.asp
var toggler = document.getElementsByClassName("caret");
var z;
for (z = 0; z < toggler.length; z++) {
toggler[z].addEventListener("click", function() {
this.parentElement.querySelector(".nested").classList.toggle("opened");
this.classList.toggle("caret-down");
});
}
spxInit('');
heartbeat(206); // page identifier (was missing in 1.1.0)
AppState("EDITING");
}
function AddTemplatesTab(tabNro) {
let tabs = [1, 2, 3, 4]
tabs.forEach((tab) => {
let thisBtn = document.getElementById('tabBtn' + tab);
thisBtn.classList.remove('tabSelected')
let thisTab = document.getElementById('tab' + tab);
thisTab.style.display = "none";
if (thisBtn.id === 'tabBtn' + tabNro) {
document.getElementById('tabBtn' + tabNro).classList.add('tabSelected')
document.getElementById('tab' + tabNro).style.display = "flex";
}
});
}
</script>
</body>
</html>
+101
View File
@@ -0,0 +1,101 @@
<!DOCTYPE html>
{{>copyright }}
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>SPX {{SPX.title}} | {{lang 'pagetitle.selectshow'}}</title>
<link rel="icon" type="image/png" href="/img/spx-gc.ico">
<link rel="stylesheet" type="text/css" href="/css/spx_fonts.css">
<link rel="stylesheet" type="text/css" href="/css/spx_layout.css">
<link rel="stylesheet" type="text/css" href="/css/spx_forms.css">
<link rel="stylesheet" type="text/css" href="/css/spx_colors.css">
<link rel="stylesheet" type="text/css" href="/css/spx_modal.css">
<script src="/js/axios.min.js"></script>
<script src="/js/fontawesome.all.min.js"></script>
<script src="/js/anime.min.js"></script>
<script src="/js/socket.io.js"></script>
<script src="/js/spx_gc.js"></script>
</head>
<body onload="pageInit();" class="bg_body" onmouseover="tip('');">
{{>projectForm}}
<div class="wrapper">
<div class="header">
{{>header page="shows"}}
</div><!-- header ended -->
<div class="content">
<div class="centerColumn pM wrapper ">
<div id="itemList" class="content pM scroller" style="border: 0px solid yellow;">
{{#if message}}
<div class="message">{{message}}</div>
{{/if}}
{{#if error}}
<div class="messagerror">{{error}}</div>
{{/if}}
<table class="wide" border=0>
<TR>
<td class="vmid tableheaderText">{{lang 'headline.select'}}</td>
<td class="vmid tableheaderHelp aright" title="{{lang 'general.help'}}" onClick="help('projects');"><i class="far fa-question-circle"></i>&nbsp;</td>
</TR>
</table>
<hr class="h2hr"><!-- =========================================== -->
<table style="width: 100%;" cellspacing="10" border="0">
<tr>
<td>
<select id="allProjects" style="display:none;">
{{#each folders}}
<option>{{this}}</option>
{{/each}}
</select>
{{#ifGreater folders.length 5}}
<!-- more than 5 projects (above), so enable filtering -->
<input type="text"
id="projectFilter"
placeholder="{{lang 'button.filterprojects'}}"
onkeyup="filterProjects()"
class="wide gcinputFilter bg_grey40 spxformtext"
style="color:white; border-radius: 0.2em; ::placeholder {color: rgb(156, 0, 0);}"
autofocus>
{{/ifGreater}}
</td>
</tr>
<tr>
<td>
<select id="lists"
size=14 class="spxformtext bg_grey40"
style="width: 100%; height:100%; color:white; border:0px; padding:0.2em; line-height:3;"
onmouseover="tip('{{lang 'hover.showlist'}}');"
ondblclick="eps();"
{{#ifGreater folders.length 5}}
<!-- filtering enabled -->
{{else}}
autofocus
{{/ifGreater}}
>
{{#each folders}}
<option>{{this}}</option>
{{/each}}
</select><BR>
</td>
</tr>
</table>
<HR>
<div class="spxFlexContainer">
<button onmouseover="tip('{{lang 'hover.addshow'}}');" class="spxformtext btn spxFlexItem bg_blue ripple" onClick="addshow('projectFormOverlay');">{{lang 'button.add'}}</button>
<button onmouseover="tip('{{lang 'hover.deleteshow'}}');" class="spxformtext btn spxFlexItem bg_blue ripple" onClick="delshow();">{{lang 'button.delete'}}...</button>
<button onmouseover="tip('{{lang 'hover.navishow'}}');" class="spxformtext btn spxFlexItem bg_orange ripple" onClick="eps();">{{lang 'button.select'}}</button>
</div>
</div><!-- itemlist ended -->
</div><!-- center wrapper ended -->
</div><!-- content ended -->
{{>footer}}
</div><!-- MAINwrapper ended -->
<script>
window.onload = function () {
document.getElementById('lists').selectedIndex = "0";
heartbeat(204); // page identifier
}
</script>
</body>
</html>