├── .gitignore ├── ASSETS ├── ExtraFunctions │ └── demoFunctions.js ├── csv │ └── csv-folder.md ├── excel │ └── ticker-demo │ │ └── basic-news-ticker.xlsx ├── media │ └── images │ │ ├── hd │ │ ├── BluishBlurry.png │ │ ├── black.png │ │ ├── chroma.png │ │ └── nature.png │ │ ├── logo │ │ ├── DemoLogo_Black_300x300.png │ │ ├── DemoLogo_Grey_300x300.png │ │ ├── DemoLogo_PurpleDisc.png │ │ ├── DemoLogo_Red_300x300.png │ │ ├── DemoLogo_White_300x300.png │ │ ├── DemoLogo_Yellow_300x300.png │ │ ├── blurange.png │ │ ├── demologo_blue.png │ │ ├── demologo_outline.png │ │ └── demologo_red.png │ │ └── spxgc_welcome_help.png ├── plugins │ ├── animateAllOut │ │ └── init.js │ ├── lib │ │ └── ui.js │ ├── panicButton │ │ └── init.js │ ├── plugins.md │ ├── spxLinks │ │ └── init.js │ └── welcomeOverlay │ │ ├── img │ │ └── spxgc_welcome_help.png │ │ └── init.js └── templates │ └── softpix │ └── Template_Pack_1.3.2 │ ├── BUMPER.html │ ├── INFO_LEFT.html │ ├── INFO_RIGHT.html │ ├── NAME_LEFT.html │ ├── NAME_RIGHT.html │ ├── README.md │ ├── README.pdf │ ├── TICKER_EXCEL.html │ ├── TICKER_RSS.html │ ├── TITLE_2_STEPS.html │ ├── css │ └── styles.css │ ├── fonts │ ├── Amaranth-Bold.ttf │ ├── Amaranth-BoldItalic.ttf │ ├── Amaranth-Italic.ttf │ ├── Amaranth-Regular.ttf │ ├── BarlowSemiCondensed-Black.ttf │ ├── BarlowSemiCondensed-Bold.ttf │ ├── BarlowSemiCondensed-Light.ttf │ ├── BarlowSemiCondensed-Regular.ttf │ ├── EncodeSansSemiExpanded-Black.ttf │ ├── EncodeSansSemiExpanded-Bold.ttf │ ├── EncodeSansSemiExpanded-Light.ttf │ ├── EncodeSansSemiExpanded-Regular.ttf │ ├── Epilogue-ExtraBold.ttf │ ├── Epilogue-Medium.ttf │ ├── Montserrat-Bold.ttf │ ├── Montserrat-Medium.ttf │ ├── MontserratAlternates-Black.ttf │ ├── MontserratAlternates-Bold.ttf │ ├── MontserratAlternates-Light.ttf │ ├── MontserratAlternates-Regular.ttf │ ├── NordeaSansSmall-Regular.woff2 │ ├── Oswald-Bold.ttf │ ├── Oswald-ExtraLight.ttf │ ├── Oswald-Light.ttf │ ├── Oswald-Regular.ttf │ ├── Oswald-SemiBold.ttf │ ├── Teko-Bold.ttf │ ├── Teko-Light.ttf │ ├── Teko-Medium.ttf │ ├── Teko-Regular.ttf │ ├── Teko-SemiBold.ttf │ ├── barlow.black.ttf │ ├── barlow.bold.ttf │ ├── barlow.light.ttf │ ├── barlow.medium.ttf │ ├── barlow.regular.ttf │ ├── fa-brands-400.ttf │ ├── fa-regular-400.ttf │ └── fa-solid-900.ttf │ ├── img │ ├── banklogo.png │ ├── flares │ │ ├── bluish.png │ │ ├── flare-hoop.png │ │ ├── golden.png │ │ ├── reddish.png │ │ └── whitey.png │ ├── gloss.png │ └── square-white-50.png │ ├── js │ ├── lib │ │ ├── anime.min.js │ │ ├── axios.min.js │ │ └── lottie.min.js │ └── spx_interface.js │ ├── lottie │ ├── live-dayspa.json │ ├── live-default.json │ ├── live-motorway.json │ └── live-news.json │ ├── snapshots │ └── pack1_demo_customizations.png │ └── themes │ ├── Bank.css │ ├── Default.css │ ├── Holiday.css │ ├── LiveOS.css │ ├── LiveU_Dark.css │ ├── LiveU_Light.css │ ├── Motorsport.css │ └── News.css ├── CONTRIBUTING.md ├── DATAROOT └── MyFirstProject │ ├── data │ ├── MyFirstRundown.json │ └── MySecondRundown.json │ └── profile.json ├── LICENSE.txt ├── LOG └── .gitignore ├── README.md ├── RELEASE_NOTES.md ├── SECURITY.md ├── ecosystem.config.js ├── locales ├── dutch.json ├── english.json ├── finnish.json ├── german.json ├── japanese.json ├── portuguese.json ├── simplifiedChinese.json └── swedish.json ├── package-lock.json ├── package.json ├── routes ├── routes-api-v1.js ├── routes-api.js ├── routes-application.js ├── routes-casparcg.js ├── routes-osc-disabled.js ├── routes-osc.js └── routes-webplayer.js ├── screenshots ├── 00-spx-gc-principle.png ├── 00-spx-gc-principle2.png ├── 01-spx-gc-projectlist.png ├── 01-spx-loginpolicy.png ├── 02-spx-gc-rundowlist.png ├── 03-spx-gc-controller-empty-rundown.png ├── 04-spx-gc-controller-addtemplate.png ├── 05-spx-gc-controller-rundown.png ├── 06-spx-gc-controller-editor.png ├── 07-spx-gc-controller-rundown.png ├── 08-spx-gc-appconfig.png ├── 09-spx-gc-projectsettings.png ├── anatomy-of-an-item.png ├── ccgservers.png ├── helloworld.png ├── hq.gif ├── source-files.png ├── spx-gc-help-workinprogress.png ├── spx-gc-ui-anim-v1-0.gif ├── spx-zoom.png ├── streamdeck.png ├── windows-installation.png ├── yt_mockup-showreel.png └── yt_mockup.png ├── server.js ├── static ├── css │ ├── spx_accents.css │ ├── spx_colors.css │ ├── spx_filebrowser.css │ ├── spx_flex.css │ ├── spx_fonts.css │ ├── spx_forms.css │ ├── spx_layout-mini.css │ ├── spx_layout.css │ ├── spx_modal.css │ ├── spx_renderpopups.css │ ├── ticker.css │ ├── toggle.css │ ├── toggle2.css │ ├── tree.css │ ├── vanilla-js-tooltip.css │ └── vanilla-picker.csp.css ├── favicon.ico ├── fonts │ ├── EncodeSansSemiExpanded-Black.ttf │ ├── EncodeSansSemiExpanded-Bold.ttf │ ├── EncodeSansSemiExpanded-Light.ttf │ ├── EncodeSansSemiExpanded-Regular.ttf │ ├── OFL.txt │ ├── fa-brands-400.ttf │ ├── fa-regular-400.ttf │ └── fa-solid-900.ttf ├── img │ ├── LoopicLogov2White.svg │ ├── LoopicSPXScreenshot.png │ ├── SPX_glossy_255_Transparent_Shadow.png │ ├── SPXgraphicsLogo64px.png │ ├── SPXstore600_2023_240px.png │ ├── checker60.png │ ├── json │ │ └── wm-0.json │ ├── promo-converter-logo.png │ ├── promo-loopic.png │ ├── promo-store.png │ ├── shadabot.png │ ├── shadatop.png │ ├── snap-ferryman.png │ ├── spx-gc-home-256.png │ ├── spx-gc.ico │ ├── spx-gc_org.ico │ ├── spx.svg │ ├── spx_offline.png │ ├── spx_online.png │ ├── spx_preview.png │ ├── spx_program.png │ ├── storeicon-org.png │ ├── storeicon.png │ └── yt_mockup.png ├── js │ ├── Sortable.min.js │ ├── anime.min.js │ ├── axios.min.js │ ├── fontawesome.all.min.js │ ├── lib │ │ ├── dotlottie-player.js │ │ ├── lodash.min.js │ │ ├── luxon.min.js │ │ └── vanilla-picker.csp.min.js │ ├── socket.io.js │ ├── socket.io.js.map │ ├── spx_controllerImportFunctions.js │ ├── spx_dragdrop.js │ ├── spx_fileBrowser.js │ ├── spx_fileBrowserItemFactory.js │ ├── spx_fileBrowserModule.js │ ├── spx_gc.js │ ├── spx_rendererUtils.js │ ├── spx_showConfig.js │ └── vanilla-js-tooltip.js ├── renderer │ └── js │ │ ├── socket.io.js │ │ └── socket.io.js.map └── this-is-public-root ├── utils ├── GDDtoSPXconverter.js ├── api-handlers.js ├── logger.js ├── playout_casparCG.js ├── playout_webplayer.js ├── sockets.js ├── spx_auth.js ├── spx_getconf.js ├── spx_server_functions.js └── talk.vbs └── views ├── _old ├── view-renderwindow_preview.handlebars └── view-renderwindow_program.handlebars ├── layouts ├── __main.handlebars ├── filelist.handlebars └── main.handlebars ├── partials ├── copyright.handlebars ├── footer.handlebars ├── header.handlebars ├── partial-genericfilebrowsermodal.handlebars ├── partial-showextrabrowsermodal.handlebars ├── partial-templatebrowsermodal.handlebars ├── rundownComment copy.handlebars ├── rundownOptions.handlebars └── socketserver.handlebars ├── view-admin.handlebars ├── view-api-osc.handlebars ├── view-api-v1.handlebars ├── view-appconfig.handlebars ├── view-authpolicy.handlebars ├── view-controller.handlebars ├── view-controllermini.handlebars ├── view-empty.handlebars ├── view-episodes.handlebars ├── view-fileBrowser.handlebars ├── view-home.handlebars ├── view-login.handlebars ├── view-registration.handlebars ├── view-renderer.handlebars ├── view-rendererscalable.handlebars ├── view-showconfig.handlebars └── view-shows.handlebars /.gitignore: -------------------------------------------------------------------------------- 1 | ################## 2 | # UNWANTED FILES # 3 | ################## 4 | ._* 5 | *.gz 6 | *.md 7 | *.7z 8 | *.mov 9 | *.mp4 10 | *.mp3 11 | *.txt 12 | *.dmg 13 | *.iso 14 | *.jar 15 | *.rar 16 | *.tar 17 | *.zip 18 | *.log 19 | *.env 20 | *.bat 21 | *.lnk 22 | cg20.* 23 | *.rest 24 | env.json 25 | thumbs.db 26 | Thumbs.db 27 | *env.json 28 | .gitignore 29 | desktop.ini 30 | *config*.json 31 | static/*.json 32 | *.pdf 33 | sea-*.* 34 | pro--*.* 35 | PRO--*.* 36 | 37 | ## Temp files or drafts 38 | zzz*.* 39 | ORG*.* 40 | 41 | ## But do these 42 | !CONTRIBUTING.md 43 | 44 | 45 | ############################ 46 | # UNWANTED GENERAL FOLDERS # 47 | ############################ 48 | viz/ 49 | bin/ 50 | notes/ 51 | notes2/ 52 | Distro/ 53 | DEV_*/** 54 | DEF_*/** 55 | .Trashes 56 | .vscode/ 57 | ZIP_TEMP/ 58 | DATAROOT* 59 | .DS_Store 60 | pkg-builds/ 61 | zip_builds/ 62 | kubernetes/ 63 | node_modules/ 64 | .VSCodeCounter/ 65 | .Spotlight-V100 66 | static/templateviewer/ 67 | CONTENT_REPOSITORIES*/** 68 | spx-content-repo.txt 69 | 70 | 71 | # IGNORE SPX DATAROOT 72 | # ...but allow specific files and folders for GitHub 73 | DATAROOT/ 74 | !DATAROOT/MyFirstProject/ 75 | 76 | 77 | # IGNORE SPX ASSETS 78 | # ...but allow specific files and folders for GitHub 79 | ASSETS/** 80 | !ASSETS/csv/csv-folder.md 81 | !ASSETS/excel/ticker-demo/basic-news-ticker.xlsx 82 | # Add templates with: 83 | # git add ASSETS/templates/softpix/Template_Pack_1.3.2/** -f 84 | 85 | # ...allow some PLUGIN files for GitHub 86 | !ASSETS/plugins/lib 87 | !ASSETS/plugins/spxLinks 88 | !ASSETS/plugins/panicButton 89 | !ASSETS/plugins/animateAllOut 90 | 91 | # ..and include some PLUGIN files for GitHub 92 | !ASSETS/ExtraFunctions/modules 93 | !ASSETS/ExtraFunctions/spx_internal_demo_functions.js 94 | 95 | 96 | -------------------------------------------------------------------------------- /ASSETS/csv/csv-folder.md: -------------------------------------------------------------------------------- 1 | # CSV folder 2 | 3 | SPX will generate template specific example.csv files here when CSV export button is clicked in the template. 4 | 5 | More info: https://spxgc.tawk.help/article/help-csv-files -------------------------------------------------------------------------------- /ASSETS/excel/ticker-demo/basic-news-ticker.xlsx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TuomoKu/SPX-GC/9ad2d01e4d2af1390ff67109f71f9c2f444200fb/ASSETS/excel/ticker-demo/basic-news-ticker.xlsx -------------------------------------------------------------------------------- /ASSETS/media/images/hd/BluishBlurry.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TuomoKu/SPX-GC/9ad2d01e4d2af1390ff67109f71f9c2f444200fb/ASSETS/media/images/hd/BluishBlurry.png -------------------------------------------------------------------------------- /ASSETS/media/images/hd/black.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TuomoKu/SPX-GC/9ad2d01e4d2af1390ff67109f71f9c2f444200fb/ASSETS/media/images/hd/black.png -------------------------------------------------------------------------------- /ASSETS/media/images/hd/chroma.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TuomoKu/SPX-GC/9ad2d01e4d2af1390ff67109f71f9c2f444200fb/ASSETS/media/images/hd/chroma.png -------------------------------------------------------------------------------- /ASSETS/media/images/hd/nature.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TuomoKu/SPX-GC/9ad2d01e4d2af1390ff67109f71f9c2f444200fb/ASSETS/media/images/hd/nature.png -------------------------------------------------------------------------------- /ASSETS/media/images/logo/DemoLogo_Black_300x300.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TuomoKu/SPX-GC/9ad2d01e4d2af1390ff67109f71f9c2f444200fb/ASSETS/media/images/logo/DemoLogo_Black_300x300.png -------------------------------------------------------------------------------- /ASSETS/media/images/logo/DemoLogo_Grey_300x300.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TuomoKu/SPX-GC/9ad2d01e4d2af1390ff67109f71f9c2f444200fb/ASSETS/media/images/logo/DemoLogo_Grey_300x300.png -------------------------------------------------------------------------------- /ASSETS/media/images/logo/DemoLogo_PurpleDisc.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TuomoKu/SPX-GC/9ad2d01e4d2af1390ff67109f71f9c2f444200fb/ASSETS/media/images/logo/DemoLogo_PurpleDisc.png -------------------------------------------------------------------------------- /ASSETS/media/images/logo/DemoLogo_Red_300x300.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TuomoKu/SPX-GC/9ad2d01e4d2af1390ff67109f71f9c2f444200fb/ASSETS/media/images/logo/DemoLogo_Red_300x300.png -------------------------------------------------------------------------------- /ASSETS/media/images/logo/DemoLogo_White_300x300.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TuomoKu/SPX-GC/9ad2d01e4d2af1390ff67109f71f9c2f444200fb/ASSETS/media/images/logo/DemoLogo_White_300x300.png -------------------------------------------------------------------------------- /ASSETS/media/images/logo/DemoLogo_Yellow_300x300.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TuomoKu/SPX-GC/9ad2d01e4d2af1390ff67109f71f9c2f444200fb/ASSETS/media/images/logo/DemoLogo_Yellow_300x300.png -------------------------------------------------------------------------------- /ASSETS/media/images/logo/blurange.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TuomoKu/SPX-GC/9ad2d01e4d2af1390ff67109f71f9c2f444200fb/ASSETS/media/images/logo/blurange.png -------------------------------------------------------------------------------- /ASSETS/media/images/logo/demologo_blue.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TuomoKu/SPX-GC/9ad2d01e4d2af1390ff67109f71f9c2f444200fb/ASSETS/media/images/logo/demologo_blue.png -------------------------------------------------------------------------------- /ASSETS/media/images/logo/demologo_outline.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TuomoKu/SPX-GC/9ad2d01e4d2af1390ff67109f71f9c2f444200fb/ASSETS/media/images/logo/demologo_outline.png -------------------------------------------------------------------------------- /ASSETS/media/images/logo/demologo_red.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TuomoKu/SPX-GC/9ad2d01e4d2af1390ff67109f71f9c2f444200fb/ASSETS/media/images/logo/demologo_red.png -------------------------------------------------------------------------------- /ASSETS/media/images/spxgc_welcome_help.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TuomoKu/SPX-GC/9ad2d01e4d2af1390ff67109f71f9c2f444200fb/ASSETS/media/images/spxgc_welcome_help.png -------------------------------------------------------------------------------- /ASSETS/plugins/animateAllOut/init.js: -------------------------------------------------------------------------------- 1 | 2 | import * as UI from "../lib/ui.js"; 3 | 4 | function PluginInstance() { 5 | var pluginRootFolder = '/plugins/animateAllOut/' 6 | var plug = this; 7 | 8 | this.render = () => { 9 | console.log('PLUGIN loading: animateAllOut...'); 10 | let options = { 11 | description: 'Animate all graphics out', 12 | overToolTip: 'Send STOP command to all layers on this rundown', 13 | caption: 'STOP ALL', 14 | color: 'black' 15 | } 16 | this.btn = UI.button(options); 17 | var app = document.getElementById("controllerPluginButtons"); 18 | app.appendChild(this.btn); 19 | this.btn.querySelector('#btn').addEventListener ("click", function() { 20 | plug.stopAllLayers(); 21 | }); 22 | } 23 | 24 | this.stopAllLayers = () => { 25 | let ITEMS = document.querySelectorAll('.itemrow'); 26 | ITEMS.forEach(function (templateItem, itemNro) { 27 | setTimeout(function(){ 28 | continueUpdateStop('stop', templateItem); 29 | }, (itemNro + 1)); // 50, 100, 150, 200ms etc... 30 | }); 31 | } 32 | } 33 | 34 | var plugin = plugin || new PluginInstance; 35 | plugin.render(); 36 | 37 | -------------------------------------------------------------------------------- /ASSETS/plugins/lib/ui.js: -------------------------------------------------------------------------------- 1 | 2 | // UI elements for plugins 3 | // 4 | // Usage: 5 | // import * as UI from "../spxgc_lib/ui.js"; 6 | // 7 | // History 8 | // XX.XX.2021 Original version added in v.1.0.xx 9 | // 13.11.2021 selectbutton added 10 | // 16.02.2022 overToolTip option added 11 | 12 | // ------------------------------------------- 13 | // More UI controls may be added in the future 14 | // ------------------------------------------- 15 | 16 | const style = { 17 | red: 'bg_red', 18 | blue: 'bg_blue', 19 | green: 'bg_green', 20 | grey: 'bg_grey15', 21 | gray: 'bg_grey15', 22 | black: 'bg_black', 23 | white: 'bg_white', 24 | orange: 'bg_orange', 25 | yellow: 'bg_yellow' 26 | } 27 | 28 | export function button (options) { 29 | // A normal SPX button 30 | let root = document.createElement('div'); 31 | let left = document.createElement('div'); 32 | let righ = document.createElement('div'); 33 | let butn = document.createElement('button'); 34 | root.style.display = 'flex'; 35 | root.style.marginBottom = '2px'; 36 | left.style.flex = '1'; 37 | left.classList = 'spxTableHead'; 38 | left.innerHTML = options.description || 'Plugin Description missing'; 39 | righ.style.flex = '1'; 40 | righ.classList = 'pluginButtonDiv'; 41 | butn.innerHTML = options.caption || 'Click'; 42 | butn.classList = 'btn wide ripple'; 43 | butn.classList.add(style[options.color]); 44 | butn.id="btn"; 45 | root.appendChild(left); 46 | root.appendChild(righ); 47 | righ.appendChild(butn); 48 | 49 | // add hover eventListener 50 | root.addEventListener ("mouseover", function() { 51 | let toolTip = options.overToolTip || ''; 52 | tip(toolTip); 53 | }); 54 | return root 55 | } 56 | 57 | 58 | 59 | export function selectbutton (options) { 60 | // Dropdown + select list 61 | let root = document.createElement('table'); 62 | root.classList = 'wide'; 63 | root.style.margin = '0'; 64 | root.style.border = '0'; 65 | root.style.padding = '0'; 66 | root.style.borderCollapse = 'collapse'; 67 | root.style.marginTop = '4px'; 68 | root.style.marginBottom = '2px'; 69 | 70 | let tbo1 = document.createElement('tbody'); 71 | let t1r1 = document.createElement('tr'); 72 | t1r1.id="t1r1"; 73 | root.appendChild(tbo1); 74 | tbo1.appendChild(t1r1); 75 | let t1c1 = document.createElement('td'); 76 | t1c1.id="t1c1"; 77 | t1c1.width = '50%'; 78 | 79 | // description 80 | let desc = document.createElement('span'); 81 | desc.classList = 'spxTableHead'; 82 | desc.innerText = options.description; 83 | t1c1.appendChild(desc); 84 | t1r1.appendChild(t1c1); 85 | 86 | 87 | // Start new TD and put new table in there 88 | let t1c2 = document.createElement('td'); 89 | t1c2.id="t1c2"; 90 | t1c2.classList = 'pluginButtonDiv'; 91 | t1r1.appendChild(t1c2); 92 | let tab2 = document.createElement('table'); 93 | tab2.classList = 'wide'; 94 | tab2.style.border = '0'; 95 | tab2.style.padding = '0'; 96 | tab2.style.borderSpacing = '0'; 97 | tab2.style.borderCollapse = 'separate'; 98 | 99 | let tbo2 = document.createElement('tbody'); 100 | let t2r1 = document.createElement('tr'); 101 | t2r1.id="t2r1"; 102 | tab2.appendChild(tbo2); 103 | tbo2.appendChild(t2r1); 104 | t1c2.appendChild(tab2); 105 | 106 | // td 107 | let t2c1 = document.createElement('td'); 108 | t2c1.id="t2c1"; 109 | t2c1.width="60%"; 110 | let drop = document.createElement('select'); 111 | drop.id = 'list'; 112 | drop.classList = 'btn'; 113 | drop.style.width = '99%'; 114 | drop.style.paddingLeft="0.4em"; 115 | drop.classList.add(style[options.color]); 116 | options.items.forEach((item,index) => { 117 | let optn = document.createElement('option'); 118 | optn.text = item.text; 119 | optn.value = item.value; 120 | drop.appendChild(optn); 121 | }); 122 | t2c1.appendChild(drop); 123 | t2r1.appendChild(t2c1); 124 | 125 | let t2c2 = document.createElement('td'); 126 | t2c2.id="t2c2"; 127 | let butn = document.createElement('button'); 128 | butn.innerHTML = options.text || 'Click'; 129 | butn.classList = 'wide btn ripple ' + style[options.color]; 130 | butn.id='btn'; 131 | t2c2.appendChild(butn); 132 | t2r1.appendChild(t2c2); 133 | 134 | // add hover eventListener 135 | root.addEventListener ("mouseover", function() { 136 | let toolTip = options.overToolTip || ''; 137 | tip(toolTip); 138 | }); 139 | 140 | 141 | return root 142 | } 143 | 144 | 145 | -------------------------------------------------------------------------------- /ASSETS/plugins/panicButton/init.js: -------------------------------------------------------------------------------- 1 | 2 | import * as UI from "../lib/ui.js"; 3 | 4 | function PluginInstance() { 5 | var pluginRootFolder = '/plugins/panicButton/' 6 | var plug = this; 7 | 8 | this.render = () => { 9 | console.log('PLUGIN loading: panicButton...'); 10 | let options = { 11 | description: 'Clear playout channels', 12 | overToolTip: 'Send command to SPX to clear all output layers', 13 | caption: 'PANIC', 14 | color: 'red' 15 | } 16 | this.btn = UI.button(options); 17 | var app = document.getElementById("controllerPluginButtons"); 18 | app.appendChild(this.btn); 19 | this.btn.querySelector('#btn').addEventListener ("click", function() { 20 | plug.panic(); 21 | }); 22 | } 23 | 24 | this.panic = () => { 25 | // Requires SPX 1.1.0+ 26 | fetch('/api/v1/panic') 27 | .then(response => { 28 | console.log('Sent Panic command.', response) 29 | if (response.status==200) { 30 | showMessageSlider('Layers cleared!', 'info', false) 31 | } else { 32 | showMessageSlider('Unable to send PANIC command to SPX!', 'error', false) 33 | } 34 | }) 35 | .catch(error => { 36 | // handle the error 37 | }); 38 | 39 | 40 | // let ITEMS = document.querySelectorAll('.itemrow'); 41 | // ITEMS.forEach(function (templateItem, itemNro) { 42 | // setTimeout(function(){ 43 | // continueUpdateStop('stop', templateItem); 44 | // }, (itemNro + 1)); // 50, 100, 150, 200ms etc... 45 | // }); 46 | } 47 | } 48 | 49 | var plugin = plugin || new PluginInstance; 50 | plugin.render(); 51 | 52 | -------------------------------------------------------------------------------- /ASSETS/plugins/plugins.md: -------------------------------------------------------------------------------- 1 | # SPX-GC plugins 2 | > From v.1.0.10 onwards. 3 | 4 | --- 5 | 6 | Each subfolder in `/ASSETS/plugins` -folder is a considered a plugin. Plugins are injected to client side using ` 65 | ``` 66 | 67 |
68 | 69 | ## Customization options 70 | Each template has a **visual theme** CSS selector. The CSS files define fonts and colors for each element in the templates. You can duplicate CSS files in the `themes` subfolder. Once the SPX UI is reoaded, it will list all the CSS files in that folder and those can be assigned to each rundown item. 71 | 72 | Some examples below: 73 | 74 | ![snapshot](snapshots/pack1_demo_customizations.png) 75 | 76 | **Remember**, these customizations only effect some visual aspects of these demo templates. Animations and layout stays mostly the same. For production purposes a custom made template pack is _the way to go_. [Get in contact](https://spx.graphics/contact) if you need help in creating bespoke templates with advanced features (social media integration, impactful animations, responsive "linked" layers, image galleries, map animations, graphs, particle effects etc...) 77 | 78 | 79 | 80 |
81 | 82 | ## MIT License 83 | Copyright 2020- Softpix Ltd 84 | 85 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 86 | 87 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 88 | 89 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /ASSETS/templates/softpix/Template_Pack_1.3.2/README.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TuomoKu/SPX-GC/9ad2d01e4d2af1390ff67109f71f9c2f444200fb/ASSETS/templates/softpix/Template_Pack_1.3.2/README.pdf -------------------------------------------------------------------------------- /ASSETS/templates/softpix/Template_Pack_1.3.2/css/styles.css: -------------------------------------------------------------------------------- 1 | *, 2 | *:before, 3 | *:after { 4 | box-sizing: border-box; 5 | margin: 0; 6 | padding: 0; 7 | -webkit-font-smoothing: antialiased; 8 | -moz-osx-font-smoothing: grayscale; 9 | } 10 | 11 | body, 12 | html { 13 | margin: 0; 14 | padding: 0; 15 | overflow: hidden; 16 | background-color: rgba(255, 255, 255, 0); 17 | } 18 | 19 | .SPXWindow { 20 | width: 100vw; 21 | height: 100vh; 22 | overflow: hidden; 23 | position: absolute; 24 | box-sizing: border-box; 25 | background-size: 100% 100%; 26 | background-color: rgba(255, 255, 255, 0); 27 | 28 | border-style: solid; 29 | border-color: rgba(12, 221, 12, 0.0); /* MARGIN COLOR */ 30 | border-top-width: var(--theme-viewport-padding-top); 31 | border-left-width: var(--theme-viewport-padding-sides); 32 | border-right-width: var(--theme-viewport-padding-sides); 33 | border-bottom-width: var(--theme-viewport-padding-bottom); 34 | } 35 | 36 | #gfx { 37 | position: relative; 38 | } 39 | 40 | div { 41 | white-space: nowrap; 42 | overflow: hidden; 43 | } 44 | 45 | #hiddenSpxData { 46 | top: 50%; 47 | left: 50%; 48 | width: 50vw; 49 | opacity: 0; /* <----- widget opacity */ 50 | padding: 1em; 51 | display: flex; 52 | z-index: 1000; 53 | font-size: 3em; 54 | border-radius: 1em; 55 | position: absolute; 56 | color: yellowgreen; 57 | flex-direction: column; 58 | border: 2px solid yellowgreen; 59 | background: rgba(0, 0, 0, 0.7); 60 | transform: translate(-50%, -50%); 61 | font-family: monospace !important; 62 | } 63 | 64 | .liteOnDark { 65 | color: var(--theme-lite-color); 66 | background-color: var(--theme-dark-color); 67 | } 68 | 69 | .darkOnLite { 70 | color: var(--theme-dark-color); 71 | background-color: var(--theme-lite-color); 72 | } 73 | 74 | .darkOnColor { 75 | color: var(--theme-dark-color); 76 | background-color: var(--theme-brandColor); 77 | } 78 | 79 | .liteOnColor { 80 | color: var(--theme-lite-color); 81 | background-color: var(--theme-brandColor); 82 | } 83 | -------------------------------------------------------------------------------- /ASSETS/templates/softpix/Template_Pack_1.3.2/fonts/Amaranth-Bold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TuomoKu/SPX-GC/9ad2d01e4d2af1390ff67109f71f9c2f444200fb/ASSETS/templates/softpix/Template_Pack_1.3.2/fonts/Amaranth-Bold.ttf -------------------------------------------------------------------------------- /ASSETS/templates/softpix/Template_Pack_1.3.2/fonts/Amaranth-BoldItalic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TuomoKu/SPX-GC/9ad2d01e4d2af1390ff67109f71f9c2f444200fb/ASSETS/templates/softpix/Template_Pack_1.3.2/fonts/Amaranth-BoldItalic.ttf -------------------------------------------------------------------------------- /ASSETS/templates/softpix/Template_Pack_1.3.2/fonts/Amaranth-Italic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TuomoKu/SPX-GC/9ad2d01e4d2af1390ff67109f71f9c2f444200fb/ASSETS/templates/softpix/Template_Pack_1.3.2/fonts/Amaranth-Italic.ttf -------------------------------------------------------------------------------- /ASSETS/templates/softpix/Template_Pack_1.3.2/fonts/Amaranth-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TuomoKu/SPX-GC/9ad2d01e4d2af1390ff67109f71f9c2f444200fb/ASSETS/templates/softpix/Template_Pack_1.3.2/fonts/Amaranth-Regular.ttf -------------------------------------------------------------------------------- /ASSETS/templates/softpix/Template_Pack_1.3.2/fonts/BarlowSemiCondensed-Black.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TuomoKu/SPX-GC/9ad2d01e4d2af1390ff67109f71f9c2f444200fb/ASSETS/templates/softpix/Template_Pack_1.3.2/fonts/BarlowSemiCondensed-Black.ttf -------------------------------------------------------------------------------- /ASSETS/templates/softpix/Template_Pack_1.3.2/fonts/BarlowSemiCondensed-Bold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TuomoKu/SPX-GC/9ad2d01e4d2af1390ff67109f71f9c2f444200fb/ASSETS/templates/softpix/Template_Pack_1.3.2/fonts/BarlowSemiCondensed-Bold.ttf -------------------------------------------------------------------------------- /ASSETS/templates/softpix/Template_Pack_1.3.2/fonts/BarlowSemiCondensed-Light.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TuomoKu/SPX-GC/9ad2d01e4d2af1390ff67109f71f9c2f444200fb/ASSETS/templates/softpix/Template_Pack_1.3.2/fonts/BarlowSemiCondensed-Light.ttf -------------------------------------------------------------------------------- /ASSETS/templates/softpix/Template_Pack_1.3.2/fonts/BarlowSemiCondensed-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TuomoKu/SPX-GC/9ad2d01e4d2af1390ff67109f71f9c2f444200fb/ASSETS/templates/softpix/Template_Pack_1.3.2/fonts/BarlowSemiCondensed-Regular.ttf -------------------------------------------------------------------------------- /ASSETS/templates/softpix/Template_Pack_1.3.2/fonts/EncodeSansSemiExpanded-Black.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TuomoKu/SPX-GC/9ad2d01e4d2af1390ff67109f71f9c2f444200fb/ASSETS/templates/softpix/Template_Pack_1.3.2/fonts/EncodeSansSemiExpanded-Black.ttf -------------------------------------------------------------------------------- /ASSETS/templates/softpix/Template_Pack_1.3.2/fonts/EncodeSansSemiExpanded-Bold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TuomoKu/SPX-GC/9ad2d01e4d2af1390ff67109f71f9c2f444200fb/ASSETS/templates/softpix/Template_Pack_1.3.2/fonts/EncodeSansSemiExpanded-Bold.ttf -------------------------------------------------------------------------------- /ASSETS/templates/softpix/Template_Pack_1.3.2/fonts/EncodeSansSemiExpanded-Light.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TuomoKu/SPX-GC/9ad2d01e4d2af1390ff67109f71f9c2f444200fb/ASSETS/templates/softpix/Template_Pack_1.3.2/fonts/EncodeSansSemiExpanded-Light.ttf -------------------------------------------------------------------------------- /ASSETS/templates/softpix/Template_Pack_1.3.2/fonts/EncodeSansSemiExpanded-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TuomoKu/SPX-GC/9ad2d01e4d2af1390ff67109f71f9c2f444200fb/ASSETS/templates/softpix/Template_Pack_1.3.2/fonts/EncodeSansSemiExpanded-Regular.ttf -------------------------------------------------------------------------------- /ASSETS/templates/softpix/Template_Pack_1.3.2/fonts/Epilogue-ExtraBold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TuomoKu/SPX-GC/9ad2d01e4d2af1390ff67109f71f9c2f444200fb/ASSETS/templates/softpix/Template_Pack_1.3.2/fonts/Epilogue-ExtraBold.ttf -------------------------------------------------------------------------------- /ASSETS/templates/softpix/Template_Pack_1.3.2/fonts/Epilogue-Medium.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TuomoKu/SPX-GC/9ad2d01e4d2af1390ff67109f71f9c2f444200fb/ASSETS/templates/softpix/Template_Pack_1.3.2/fonts/Epilogue-Medium.ttf -------------------------------------------------------------------------------- /ASSETS/templates/softpix/Template_Pack_1.3.2/fonts/Montserrat-Bold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TuomoKu/SPX-GC/9ad2d01e4d2af1390ff67109f71f9c2f444200fb/ASSETS/templates/softpix/Template_Pack_1.3.2/fonts/Montserrat-Bold.ttf -------------------------------------------------------------------------------- /ASSETS/templates/softpix/Template_Pack_1.3.2/fonts/Montserrat-Medium.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TuomoKu/SPX-GC/9ad2d01e4d2af1390ff67109f71f9c2f444200fb/ASSETS/templates/softpix/Template_Pack_1.3.2/fonts/Montserrat-Medium.ttf -------------------------------------------------------------------------------- /ASSETS/templates/softpix/Template_Pack_1.3.2/fonts/MontserratAlternates-Black.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TuomoKu/SPX-GC/9ad2d01e4d2af1390ff67109f71f9c2f444200fb/ASSETS/templates/softpix/Template_Pack_1.3.2/fonts/MontserratAlternates-Black.ttf -------------------------------------------------------------------------------- /ASSETS/templates/softpix/Template_Pack_1.3.2/fonts/MontserratAlternates-Bold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TuomoKu/SPX-GC/9ad2d01e4d2af1390ff67109f71f9c2f444200fb/ASSETS/templates/softpix/Template_Pack_1.3.2/fonts/MontserratAlternates-Bold.ttf -------------------------------------------------------------------------------- /ASSETS/templates/softpix/Template_Pack_1.3.2/fonts/MontserratAlternates-Light.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TuomoKu/SPX-GC/9ad2d01e4d2af1390ff67109f71f9c2f444200fb/ASSETS/templates/softpix/Template_Pack_1.3.2/fonts/MontserratAlternates-Light.ttf -------------------------------------------------------------------------------- /ASSETS/templates/softpix/Template_Pack_1.3.2/fonts/MontserratAlternates-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TuomoKu/SPX-GC/9ad2d01e4d2af1390ff67109f71f9c2f444200fb/ASSETS/templates/softpix/Template_Pack_1.3.2/fonts/MontserratAlternates-Regular.ttf -------------------------------------------------------------------------------- /ASSETS/templates/softpix/Template_Pack_1.3.2/fonts/NordeaSansSmall-Regular.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TuomoKu/SPX-GC/9ad2d01e4d2af1390ff67109f71f9c2f444200fb/ASSETS/templates/softpix/Template_Pack_1.3.2/fonts/NordeaSansSmall-Regular.woff2 -------------------------------------------------------------------------------- /ASSETS/templates/softpix/Template_Pack_1.3.2/fonts/Oswald-Bold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TuomoKu/SPX-GC/9ad2d01e4d2af1390ff67109f71f9c2f444200fb/ASSETS/templates/softpix/Template_Pack_1.3.2/fonts/Oswald-Bold.ttf -------------------------------------------------------------------------------- /ASSETS/templates/softpix/Template_Pack_1.3.2/fonts/Oswald-ExtraLight.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TuomoKu/SPX-GC/9ad2d01e4d2af1390ff67109f71f9c2f444200fb/ASSETS/templates/softpix/Template_Pack_1.3.2/fonts/Oswald-ExtraLight.ttf -------------------------------------------------------------------------------- /ASSETS/templates/softpix/Template_Pack_1.3.2/fonts/Oswald-Light.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TuomoKu/SPX-GC/9ad2d01e4d2af1390ff67109f71f9c2f444200fb/ASSETS/templates/softpix/Template_Pack_1.3.2/fonts/Oswald-Light.ttf -------------------------------------------------------------------------------- /ASSETS/templates/softpix/Template_Pack_1.3.2/fonts/Oswald-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TuomoKu/SPX-GC/9ad2d01e4d2af1390ff67109f71f9c2f444200fb/ASSETS/templates/softpix/Template_Pack_1.3.2/fonts/Oswald-Regular.ttf -------------------------------------------------------------------------------- /ASSETS/templates/softpix/Template_Pack_1.3.2/fonts/Oswald-SemiBold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TuomoKu/SPX-GC/9ad2d01e4d2af1390ff67109f71f9c2f444200fb/ASSETS/templates/softpix/Template_Pack_1.3.2/fonts/Oswald-SemiBold.ttf -------------------------------------------------------------------------------- /ASSETS/templates/softpix/Template_Pack_1.3.2/fonts/Teko-Bold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TuomoKu/SPX-GC/9ad2d01e4d2af1390ff67109f71f9c2f444200fb/ASSETS/templates/softpix/Template_Pack_1.3.2/fonts/Teko-Bold.ttf -------------------------------------------------------------------------------- /ASSETS/templates/softpix/Template_Pack_1.3.2/fonts/Teko-Light.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TuomoKu/SPX-GC/9ad2d01e4d2af1390ff67109f71f9c2f444200fb/ASSETS/templates/softpix/Template_Pack_1.3.2/fonts/Teko-Light.ttf -------------------------------------------------------------------------------- /ASSETS/templates/softpix/Template_Pack_1.3.2/fonts/Teko-Medium.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TuomoKu/SPX-GC/9ad2d01e4d2af1390ff67109f71f9c2f444200fb/ASSETS/templates/softpix/Template_Pack_1.3.2/fonts/Teko-Medium.ttf -------------------------------------------------------------------------------- /ASSETS/templates/softpix/Template_Pack_1.3.2/fonts/Teko-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TuomoKu/SPX-GC/9ad2d01e4d2af1390ff67109f71f9c2f444200fb/ASSETS/templates/softpix/Template_Pack_1.3.2/fonts/Teko-Regular.ttf -------------------------------------------------------------------------------- /ASSETS/templates/softpix/Template_Pack_1.3.2/fonts/Teko-SemiBold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TuomoKu/SPX-GC/9ad2d01e4d2af1390ff67109f71f9c2f444200fb/ASSETS/templates/softpix/Template_Pack_1.3.2/fonts/Teko-SemiBold.ttf -------------------------------------------------------------------------------- /ASSETS/templates/softpix/Template_Pack_1.3.2/fonts/barlow.black.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TuomoKu/SPX-GC/9ad2d01e4d2af1390ff67109f71f9c2f444200fb/ASSETS/templates/softpix/Template_Pack_1.3.2/fonts/barlow.black.ttf -------------------------------------------------------------------------------- /ASSETS/templates/softpix/Template_Pack_1.3.2/fonts/barlow.bold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TuomoKu/SPX-GC/9ad2d01e4d2af1390ff67109f71f9c2f444200fb/ASSETS/templates/softpix/Template_Pack_1.3.2/fonts/barlow.bold.ttf -------------------------------------------------------------------------------- /ASSETS/templates/softpix/Template_Pack_1.3.2/fonts/barlow.light.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TuomoKu/SPX-GC/9ad2d01e4d2af1390ff67109f71f9c2f444200fb/ASSETS/templates/softpix/Template_Pack_1.3.2/fonts/barlow.light.ttf -------------------------------------------------------------------------------- /ASSETS/templates/softpix/Template_Pack_1.3.2/fonts/barlow.medium.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TuomoKu/SPX-GC/9ad2d01e4d2af1390ff67109f71f9c2f444200fb/ASSETS/templates/softpix/Template_Pack_1.3.2/fonts/barlow.medium.ttf -------------------------------------------------------------------------------- /ASSETS/templates/softpix/Template_Pack_1.3.2/fonts/barlow.regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TuomoKu/SPX-GC/9ad2d01e4d2af1390ff67109f71f9c2f444200fb/ASSETS/templates/softpix/Template_Pack_1.3.2/fonts/barlow.regular.ttf -------------------------------------------------------------------------------- /ASSETS/templates/softpix/Template_Pack_1.3.2/fonts/fa-brands-400.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TuomoKu/SPX-GC/9ad2d01e4d2af1390ff67109f71f9c2f444200fb/ASSETS/templates/softpix/Template_Pack_1.3.2/fonts/fa-brands-400.ttf -------------------------------------------------------------------------------- /ASSETS/templates/softpix/Template_Pack_1.3.2/fonts/fa-regular-400.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TuomoKu/SPX-GC/9ad2d01e4d2af1390ff67109f71f9c2f444200fb/ASSETS/templates/softpix/Template_Pack_1.3.2/fonts/fa-regular-400.ttf -------------------------------------------------------------------------------- /ASSETS/templates/softpix/Template_Pack_1.3.2/fonts/fa-solid-900.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TuomoKu/SPX-GC/9ad2d01e4d2af1390ff67109f71f9c2f444200fb/ASSETS/templates/softpix/Template_Pack_1.3.2/fonts/fa-solid-900.ttf -------------------------------------------------------------------------------- /ASSETS/templates/softpix/Template_Pack_1.3.2/img/banklogo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TuomoKu/SPX-GC/9ad2d01e4d2af1390ff67109f71f9c2f444200fb/ASSETS/templates/softpix/Template_Pack_1.3.2/img/banklogo.png -------------------------------------------------------------------------------- /ASSETS/templates/softpix/Template_Pack_1.3.2/img/flares/bluish.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TuomoKu/SPX-GC/9ad2d01e4d2af1390ff67109f71f9c2f444200fb/ASSETS/templates/softpix/Template_Pack_1.3.2/img/flares/bluish.png -------------------------------------------------------------------------------- /ASSETS/templates/softpix/Template_Pack_1.3.2/img/flares/flare-hoop.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TuomoKu/SPX-GC/9ad2d01e4d2af1390ff67109f71f9c2f444200fb/ASSETS/templates/softpix/Template_Pack_1.3.2/img/flares/flare-hoop.png -------------------------------------------------------------------------------- /ASSETS/templates/softpix/Template_Pack_1.3.2/img/flares/golden.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TuomoKu/SPX-GC/9ad2d01e4d2af1390ff67109f71f9c2f444200fb/ASSETS/templates/softpix/Template_Pack_1.3.2/img/flares/golden.png -------------------------------------------------------------------------------- /ASSETS/templates/softpix/Template_Pack_1.3.2/img/flares/reddish.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TuomoKu/SPX-GC/9ad2d01e4d2af1390ff67109f71f9c2f444200fb/ASSETS/templates/softpix/Template_Pack_1.3.2/img/flares/reddish.png -------------------------------------------------------------------------------- /ASSETS/templates/softpix/Template_Pack_1.3.2/img/flares/whitey.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TuomoKu/SPX-GC/9ad2d01e4d2af1390ff67109f71f9c2f444200fb/ASSETS/templates/softpix/Template_Pack_1.3.2/img/flares/whitey.png -------------------------------------------------------------------------------- /ASSETS/templates/softpix/Template_Pack_1.3.2/img/gloss.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TuomoKu/SPX-GC/9ad2d01e4d2af1390ff67109f71f9c2f444200fb/ASSETS/templates/softpix/Template_Pack_1.3.2/img/gloss.png -------------------------------------------------------------------------------- /ASSETS/templates/softpix/Template_Pack_1.3.2/img/square-white-50.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TuomoKu/SPX-GC/9ad2d01e4d2af1390ff67109f71f9c2f444200fb/ASSETS/templates/softpix/Template_Pack_1.3.2/img/square-white-50.png -------------------------------------------------------------------------------- /ASSETS/templates/softpix/Template_Pack_1.3.2/js/spx_interface.js: -------------------------------------------------------------------------------- 1 | // ---------------------------------------------------------------- 2 | // (c) Copyright 2021- SPX Graphics (https://spx.graphics) 3 | // ---------------------------------------------------------------- 4 | 5 | // Receive item data from SPX Graphics Controller 6 | // and store values in hidden DOM elements for 7 | // use in the template. 8 | 9 | function update(data) { 10 | var templateData = JSON.parse(data); 11 | console.log('----- Update handler called with data:', templateData) 12 | for (var dataField in templateData) { 13 | var idField = document.getElementById(dataField); 14 | if (idField) { 15 | let fString = templateData[dataField]; 16 | if ( fString != 'undefined' && fString != 'null' ) { 17 | idField.innerText = fString 18 | } else { 19 | idField.innerText = ''; 20 | } 21 | } else { 22 | switch (dataField) { 23 | case 'comment': 24 | case 'epochID': 25 | // console.warn('FYI: Optional #' + dataField + ' missing from SPX template...'); 26 | break; 27 | default: 28 | console.error('ERROR Placeholder #' + dataField + ' missing from SPX template.'); 29 | } 30 | } 31 | } 32 | 33 | if (typeof runTemplateUpdate === "function") { 34 | runTemplateUpdate() // Play will follow 35 | } else { 36 | console.error('runTemplateUpdate() function missing from SPX template.') 37 | } 38 | } 39 | 40 | // Play handler 41 | function play() { 42 | // console.log('----- Play handler called.') 43 | // if (typeof runAnimationIN === "function") { 44 | // runAnimationIN() 45 | // } else { 46 | // console.error('runAnimationIN() function missing from SPX template.') 47 | // } 48 | } 49 | 50 | // Stop handler 51 | function stop() { 52 | // console.log('----- Stop handler called.') 53 | if (typeof runAnimationOUT === "function") { 54 | runAnimationOUT() 55 | } else { 56 | console.error('runAnimationOUT() function missing from SPX template.') 57 | } 58 | } 59 | 60 | // Continue handler 61 | function next(data) { 62 | console.log('----- Next handler called.') 63 | if (typeof runAnimationNEXT === "function") { 64 | runAnimationNEXT() 65 | } else { 66 | console.error('runAnimationNEXT() function missing from SPX template.') 67 | } 68 | } 69 | 70 | // Encoded text to HTML 71 | function htmlDecode(txt) { 72 | var doc = new DOMParser().parseFromString(txt, "text/html"); 73 | return doc.documentElement.textContent; 74 | } 75 | 76 | // Utility function 77 | function e(elementID) { 78 | if (!elementID) { 79 | console.warn('Element ID is falsy, returning null.'); 80 | return null; 81 | } 82 | if (!document.getElementById(elementID)) { 83 | console.warn('Element ' + elementID + ' not found, returning null.'); 84 | return null; 85 | } 86 | return document.getElementById(elementID); 87 | } 88 | 89 | window.onerror = function (msg, url, row, col, error) { 90 | let err = {}; 91 | err.file = url; 92 | err.message = msg; 93 | err.line = row; 94 | console.log('%c' + 'SPX Template Error Detected:', 'font-weight:bold; font-size: 1.2em; margin-top: 2em;'); 95 | console.table(err); 96 | // spxlog('Template Error Auto Detected: file: ' + url + ', line: ' + row + ', msg; ' + msg,'WARN') 97 | }; 98 | 99 | function validString(str) { 100 | let S = str.toUpperCase(); 101 | // console.log('checking validString(' + S +');'); 102 | switch (S) { 103 | case "UNDEFINED": 104 | case "NULL": 105 | case "": 106 | return false // not a valid string 107 | break; 108 | } 109 | return true; // is a valid string 110 | } 111 | -------------------------------------------------------------------------------- /ASSETS/templates/softpix/Template_Pack_1.3.2/lottie/live-dayspa.json: -------------------------------------------------------------------------------- 1 | {"v":"5.8.1","fr":60,"ip":0,"op":121,"w":600,"h":300,"nm":"Live-Dayspa","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":1,"nm":"Medium Yellow Solid 1","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":105,"s":[100]},{"t":120,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[300,150,0],"ix":2,"l":2},"a":{"a":0,"k":[300,150,0],"ix":1,"l":2},"s":{"a":1,"k":[{"i":{"x":[0.2,0.2,0.667],"y":[1,1,1]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,17.105]},"t":55,"s":[0,0,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,1.439]},"o":{"x":[0.333,0.333,0.333],"y":[0,0,0]},"t":105,"s":[39,39,100]},{"t":120,"s":[0,0,100]}],"ix":6,"l":2}},"ao":0,"hasMask":true,"masksProperties":[{"inv":false,"mode":"a","pt":{"a":0,"k":{"i":[[69.588,0],[0,-69.588],[-69.588,0],[0,69.588]],"o":[[-69.588,0],[0,69.588],[69.588,0],[0,-69.588]],"v":[[298,23],[172,149],[298,275],[424,149]],"c":true},"ix":1},"o":{"a":0,"k":100,"ix":3},"x":{"a":0,"k":0,"ix":4},"nm":"Mask 1"}],"sw":600,"sh":300,"sc":"#f5f0ad","ip":55,"op":120,"st":55,"bm":0},{"ddd":0,"ind":2,"ty":1,"nm":"Medium Yellow Solid 1","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[300,150,0],"ix":2,"l":2},"a":{"a":0,"k":[300,150,0],"ix":1,"l":2},"s":{"a":1,"k":[{"i":{"x":[0.2,0.2,0.667],"y":[1,1,1]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,16.856]},"t":40,"s":[0,0,100]},{"t":90,"s":[89,89,100]}],"ix":6,"l":2}},"ao":0,"hasMask":true,"masksProperties":[{"inv":false,"mode":"a","pt":{"a":0,"k":{"i":[[69.588,0],[0,-69.588],[-69.588,0],[0,69.588]],"o":[[-69.588,0],[0,69.588],[69.588,0],[0,-69.588]],"v":[[298,23],[172,149],[298,275],[424,149]],"c":true},"ix":1},"o":{"a":0,"k":100,"ix":3},"x":{"a":0,"k":0,"ix":4},"nm":"Mask 1"}],"sw":600,"sh":300,"sc":"#f5f0ad","ip":40,"op":161,"st":40,"bm":0},{"ddd":0,"ind":3,"ty":1,"nm":"Medium Yellow Solid 1","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":65,"s":[100]},{"t":90,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[300,150,0],"ix":2,"l":2},"a":{"a":0,"k":[300,150,0],"ix":1,"l":2},"s":{"a":1,"k":[{"i":{"x":[0.2,0.2,0.667],"y":[1,1,1]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,16.856]},"t":30,"s":[0,0,100]},{"t":80,"s":[89,89,100]}],"ix":6,"l":2}},"ao":0,"hasMask":true,"masksProperties":[{"inv":false,"mode":"a","pt":{"a":0,"k":{"i":[[69.588,0],[0,-69.588],[-69.588,0],[0,69.588]],"o":[[-69.588,0],[0,69.588],[69.588,0],[0,-69.588]],"v":[[298,23],[172,149],[298,275],[424,149]],"c":true},"ix":1},"o":{"a":0,"k":100,"ix":3},"x":{"a":0,"k":0,"ix":4},"nm":"Mask 1"}],"sw":600,"sh":300,"sc":"#f5f0ad","ip":30,"op":90,"st":30,"bm":0},{"ddd":0,"ind":4,"ty":1,"nm":"Deep Orange Solid 1","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":65,"s":[100]},{"t":90,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[300,150,0],"ix":2,"l":2},"a":{"a":0,"k":[300,150,0],"ix":1,"l":2},"s":{"a":1,"k":[{"i":{"x":[0.2,0.2,0.667],"y":[1,1,1]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,16.856]},"t":20,"s":[0,0,100]},{"t":70,"s":[89,89,100]}],"ix":6,"l":2}},"ao":0,"hasMask":true,"masksProperties":[{"inv":false,"mode":"a","pt":{"a":0,"k":{"i":[[69.588,0],[0,-69.588],[-69.588,0],[0,69.588]],"o":[[-69.588,0],[0,69.588],[69.588,0],[0,-69.588]],"v":[[298,23],[172,149],[298,275],[424,149]],"c":true},"ix":1},"o":{"a":0,"k":100,"ix":3},"x":{"a":0,"k":0,"ix":4},"nm":"Mask 1"}],"sw":600,"sh":300,"sc":"#582e09","ip":20,"op":90,"st":20,"bm":0},{"ddd":0,"ind":5,"ty":1,"nm":"Medium Yellow Solid 1","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":65,"s":[100]},{"t":90,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[300,150,0],"ix":2,"l":2},"a":{"a":0,"k":[300,150,0],"ix":1,"l":2},"s":{"a":1,"k":[{"i":{"x":[0.2,0.2,0.667],"y":[1,1,1]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,16.856]},"t":9,"s":[0,0,100]},{"t":59,"s":[89,89,100]}],"ix":6,"l":2}},"ao":0,"hasMask":true,"masksProperties":[{"inv":false,"mode":"a","pt":{"a":0,"k":{"i":[[69.588,0],[0,-69.588],[-69.588,0],[0,69.588]],"o":[[-69.588,0],[0,69.588],[69.588,0],[0,-69.588]],"v":[[298,23],[172,149],[298,275],[424,149]],"c":true},"ix":1},"o":{"a":0,"k":100,"ix":3},"x":{"a":0,"k":0,"ix":4},"nm":"Mask 1"}],"sw":600,"sh":300,"sc":"#f5f0ad","ip":9,"op":90,"st":9,"bm":0},{"ddd":0,"ind":6,"ty":1,"nm":"Deep Orange Solid 1","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":65,"s":[100]},{"t":90,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[300,150,0],"ix":2,"l":2},"a":{"a":0,"k":[300,150,0],"ix":1,"l":2},"s":{"a":1,"k":[{"i":{"x":[0.2,0.2,0.667],"y":[1,1,1]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,16.856]},"t":0,"s":[0,0,100]},{"t":50,"s":[89,89,100]}],"ix":6,"l":2}},"ao":0,"hasMask":true,"masksProperties":[{"inv":false,"mode":"a","pt":{"a":0,"k":{"i":[[69.588,0],[0,-69.588],[-69.588,0],[0,69.588]],"o":[[-69.588,0],[0,69.588],[69.588,0],[0,-69.588]],"v":[[298,23],[172,149],[298,275],[424,149]],"c":true},"ix":1},"o":{"a":0,"k":100,"ix":3},"x":{"a":0,"k":0,"ix":4},"nm":"Mask 1"}],"sw":600,"sh":300,"sc":"#582e09","ip":0,"op":90,"st":0,"bm":0}],"markers":[]} -------------------------------------------------------------------------------- /ASSETS/templates/softpix/Template_Pack_1.3.2/snapshots/pack1_demo_customizations.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TuomoKu/SPX-GC/9ad2d01e4d2af1390ff67109f71f9c2f444200fb/ASSETS/templates/softpix/Template_Pack_1.3.2/snapshots/pack1_demo_customizations.png -------------------------------------------------------------------------------- /ASSETS/templates/softpix/Template_Pack_1.3.2/themes/Bank.css: -------------------------------------------------------------------------------- 1 | :root { 2 | 3 | /* COLORS */ 4 | --theme-lite-color: rgb(216, 216, 216); 5 | --theme-dark-color: rgb(7, 32, 107); 6 | --theme-brandColor: rgb(9, 67, 137); 7 | --theme-tickerBack: white; 8 | 9 | --theme-accent-width: 8vw; 10 | --theme-ticker-height: 4.8vh; 11 | 12 | --theme-viewport-padding-top: 3vw; 13 | --theme-viewport-padding-sides: 2vw; 14 | --theme-viewport-padding-bottom: 1vw; 15 | } 16 | 17 | @font-face { font-family: "LITE"; src: url("../fonts/NordeaSansSmall-Regular.woff2"); } 18 | @font-face { font-family: "REGU"; src: url("../fonts/NordeaSansSmall-Regular.woff2"); } 19 | @font-face { font-family: "BOLD"; src: url("../fonts/NordeaSansSmall-Regular.woff2"); } 20 | @font-face { font-family: "BLAC"; src: url("../fonts/NordeaSansSmall-Regular.woff2"); } 21 | 22 | 23 | #logoLeft { 24 | 25 | /* Adjust until satisfied */ 26 | 27 | width: 8vw; 28 | bottom: 4vh; 29 | opacity: 0; /* animated */ 30 | left: 0.0vw; 31 | z-index: 100; 32 | aspect-ratio: 1/1; 33 | position: absolute; 34 | background-size: contain; 35 | background-position: center; 36 | background-repeat: no-repeat; 37 | transform-origin: bottom left; 38 | background-image: url('../img/banklogo.png'); 39 | animation: 500ms ease-out 200ms logoAnimateIn forwards; 40 | } 41 | 42 | @keyframes logoAnimateIn { 43 | from { 44 | opacity: 0; 45 | scale: 1.2; 46 | } 47 | 48 | to { 49 | opacity: 1; 50 | scale: 1.0; 51 | } 52 | } 53 | 54 | .right #gfx { 55 | border-right: calc(var(--theme-accent-width)/8) 56 | solid 57 | var(--theme-brandColor); 58 | } 59 | 60 | .right #details { 61 | justify-content: flex-end; 62 | } 63 | 64 | #title1 { 65 | background-color: var(--theme-lite-color); 66 | color: var(--theme-dark-color); 67 | font-family: 'REGU'; 68 | font-size: 1.2em; 69 | padding: 0.2em 0.8em; 70 | border-bottom: 0.1em solid var(--theme-brandColor); 71 | } 72 | 73 | #title2{ 74 | color: var(--theme-lite-color); 75 | text-shadow: none; 76 | font-family: 'REGU'; 77 | font-size: 0.75em; 78 | padding: 0.5em 2.0em; 79 | margin-top: 0.2em; 80 | border-top: 0.1em solid var(--theme-lite-color); 81 | background-color: var(--theme-brandColor); 82 | 83 | } 84 | 85 | #rightInfo, 86 | #leftInfo { 87 | background-color: var(--theme-lite-color); 88 | color: var(--theme-dark-color); 89 | font-family: 'REGU'; 90 | font-size: 2.3em; 91 | padding: 0.2em 0.8em; 92 | border-left: calc(var(--theme-accent-width)/8) 93 | solid 94 | var(--theme-brandColor); 95 | text-transform: uppercase; 96 | } 97 | 98 | #rightInfo { 99 | border-right: calc(var(--theme-accent-width)/8) 100 | solid 101 | var(--theme-brandColor); 102 | border-left: 0; 103 | 104 | } -------------------------------------------------------------------------------- /ASSETS/templates/softpix/Template_Pack_1.3.2/themes/Holiday.css: -------------------------------------------------------------------------------- 1 | :root { 2 | 3 | /* COLORS */ 4 | --theme-lite-color: rgb(245, 240, 173); 5 | --theme-dark-color: rgb(43, 24, 5); 6 | --theme-brandColor: rgb(88, 46, 9); 7 | --theme-tickerBack: rgb(245, 240, 173); 8 | 9 | --theme-accent-width: 0vw; 10 | --theme-ticker-height: 4.7vh; 11 | 12 | --theme-viewport-padding-top: 4vw; 13 | --theme-viewport-padding-sides: 3vw; 14 | --theme-viewport-padding-bottom: 4vw; 15 | } 16 | 17 | @font-face { font-family: "LITE"; src: url("../fonts/Amaranth-Italic.ttf"); } 18 | @font-face { font-family: "REGU"; src: url("../fonts/Amaranth-Regular.ttf"); } 19 | @font-face { font-family: "BOLD"; src: url("../fonts/Amaranth-BoldItalic.ttf"); } 20 | @font-face { font-family: "BLAC"; src: url("../fonts/Amaranth-Bold.ttf"); } 21 | 22 | 23 | #box1 { 24 | background-color: transparent; 25 | padding-left: 0; 26 | } 27 | 28 | #text1 { 29 | width: 100%; 30 | color: white; 31 | font-size: 1.2em; 32 | padding-left: 0.4em; 33 | margin-left: -0.4em; 34 | text-shadow: 0.07em 0.07em var(--theme-brandColor); 35 | border-left: 0.1em solid var(--theme-brandColor); 36 | } 37 | 38 | .right #details { 39 | justify-content: flex-end; 40 | } 41 | 42 | .right #box1 { 43 | padding-right: 0; 44 | } 45 | 46 | 47 | .right #text1 { 48 | padding-right: 0.4em; 49 | margin-left: 1.4em; 50 | border-left: 0; 51 | border-right: 0.1em solid var(--theme-brandColor); 52 | } 53 | 54 | 55 | 56 | #title1 { 57 | color: white; 58 | font-size: 1.2em; 59 | font-family: 'BOLD'; 60 | padding: 0.2em 1.5em; 61 | text-shadow: 0.07em 0.07em var(--theme-brandColor); 62 | border-bottom: 0.07em solid var(--theme-brandColor); 63 | } 64 | 65 | #title2 { 66 | margin-top: 0.1em; 67 | font-size: 0.75em; 68 | font-family: 'REGU'; 69 | padding: 0.4em 1.5em; 70 | color: var(--theme-lite-color); 71 | background-color: var(--theme-brandColor); 72 | } 73 | 74 | #rightInfo, 75 | #leftInfo { 76 | background-color: transparent; 77 | color: white; 78 | font-family: 'REGU'; 79 | font-size: 2.3em; 80 | padding: 0.2em 0.8em; 81 | border-left: 0.2em 82 | solid 83 | var(--theme-brandColor); 84 | } 85 | 86 | #rightInfo { 87 | border-right: 0.2em 88 | solid 89 | var(--theme-brandColor); 90 | border-left: 0; 91 | 92 | } -------------------------------------------------------------------------------- /ASSETS/templates/softpix/Template_Pack_1.3.2/themes/LiveU_Dark.css: -------------------------------------------------------------------------------- 1 | :root { 2 | 3 | /* 4 | 5 | ORANGE #F0592B 6 | BLUE #0061C6 7 | LIGHT GR #C4C4C4 8 | DARK BLUE #111833 9 | 10 | */ 11 | 12 | /* COLORS */ 13 | --theme-lite-color: #ffffff; 14 | --theme-dark-color: #111833; 15 | --theme-brandColor: #F0592B; 16 | --theme-tickerBack: white; 17 | 18 | --theme-corner-radius: 0.5vw; 19 | 20 | --theme-accent-width: 1vw; 21 | --theme-ticker-height: 4.7vh; 22 | 23 | --theme-viewport-padding-top: 4vw; 24 | --theme-viewport-padding-sides: 3vw; 25 | --theme-viewport-padding-bottom: 2vh; 26 | } 27 | 28 | @font-face { font-family: "LITE"; src: url("../fonts/Epilogue-Medium.ttf"); } 29 | @font-face { font-family: "REGU"; src: url("../fonts/Epilogue-Medium.ttf"); } 30 | @font-face { font-family: "BOLD"; src: url("../fonts/Epilogue-ExtraBold.ttf"); } 31 | @font-face { font-family: "BLAC"; src: url("../fonts/Epilogue-ExtraBold.ttf"); } 32 | 33 | 34 | .right #details { 35 | justify-content: flex-end; 36 | } 37 | 38 | #title1 { 39 | background-color: var(--theme-dark-color); 40 | color: var(--theme-lite-color); 41 | font-family: 'BOLD'; 42 | font-size: 1.5em; 43 | padding-top: 0.15em; 44 | padding-left: 0.6em; 45 | padding-right: 0.6em; 46 | text-transform: uppercase; 47 | border-bottom: 0.1em solid var(--theme-brandColor); 48 | } 49 | 50 | #title2 { 51 | color: var(--theme-dark-color); 52 | text-shadow: none; 53 | font-family: 'REGU'; 54 | font-size: 0.75em; 55 | padding: 0.5em 2.0em; 56 | background-color: var(--theme-brandColor); 57 | } 58 | 59 | #rightInfo, 60 | #leftInfo { 61 | background-color: var(--theme-dark-color); 62 | color: var(--theme-lite-color); 63 | font-family: 'REGU'; 64 | font-size: 2.3em; 65 | padding: 0.2em 0.8em; 66 | border-left: var(--theme-accent-width) 67 | solid 68 | var(--theme-brandColor); 69 | } 70 | 71 | #rightInfo { 72 | border-right: var(--theme-accent-width) 73 | solid 74 | var(--theme-brandColor); 75 | border-left: 0; 76 | 77 | } 78 | 79 | #box1 { 80 | background-color: var(--theme-dark-color); 81 | color: var(--theme-lite-color); 82 | } 83 | 84 | #box3 { 85 | background-color: var(--theme-lite-color); 86 | color: var(--theme-dark-color); 87 | } 88 | 89 | #box4 { 90 | background-color: var(--theme-brandColor); 91 | color: var(--theme-dark-color); 92 | } 93 | 94 | #box3, #box4 { 95 | padding: 0.3em !important; 96 | } 97 | 98 | .left #box1, 99 | .left #box4 { 100 | border-top-right-radius: var(--theme-corner-radius); 101 | border-bottom-right-radius: var(--theme-corner-radius); 102 | } 103 | 104 | .right #box1, 105 | .right #box3 { 106 | border-top-left-radius: var(--theme-corner-radius); 107 | border-bottom-left-radius: var(--theme-corner-radius); 108 | } 109 | 110 | .ticker #headline { 111 | border-left: var(--theme-accent-width) solid var(--theme-brandColor); 112 | background-color: var(--theme-dark-color); 113 | padding: 0.3em !important; 114 | } 115 | 116 | .ticker #tickerText { 117 | padding: 0.3em !important; 118 | } 119 | 120 | -------------------------------------------------------------------------------- /ASSETS/templates/softpix/Template_Pack_1.3.2/themes/LiveU_Light.css: -------------------------------------------------------------------------------- 1 | :root { 2 | 3 | /* 4 | 5 | ORANGE #F0592B 6 | BLUE #0061C6 7 | LIGHT GR #C4C4C4 8 | DARK BLUE #111833 9 | 10 | */ 11 | 12 | /* COLORS */ 13 | --theme-lite-color: #ffffff; 14 | --theme-dark-color: #0061C6; 15 | --theme-brandColor: #F0592B; 16 | --theme-tickerBack: white; 17 | 18 | --theme-corner-radius: 0.5vw; 19 | 20 | --theme-accent-width: 1vw; 21 | --theme-ticker-height: 4.7vh; 22 | 23 | --theme-viewport-padding-top: 4vw; 24 | --theme-viewport-padding-sides: 3vw; 25 | --theme-viewport-padding-bottom: 2vh; 26 | } 27 | 28 | @font-face { font-family: "LITE"; src: url("../fonts/Epilogue-Medium.ttf"); } 29 | @font-face { font-family: "REGU"; src: url("../fonts/Epilogue-Medium.ttf"); } 30 | @font-face { font-family: "BOLD"; src: url("../fonts/Epilogue-ExtraBold.ttf"); } 31 | @font-face { font-family: "BLAC"; src: url("../fonts/Epilogue-ExtraBold.ttf"); } 32 | 33 | 34 | .right #details { 35 | justify-content: flex-end; 36 | } 37 | 38 | #title1 { 39 | background-color: var(--theme-dark-color); 40 | color: var(--theme-lite-color); 41 | font-family: 'BOLD'; 42 | font-size: 1.5em; 43 | padding-top: 0.15em; 44 | padding-left: 0.6em; 45 | padding-right: 0.6em; 46 | text-transform: uppercase; 47 | border-bottom: 0.1em solid var(--theme-brandColor); 48 | } 49 | 50 | #title2 { 51 | color: var(--theme-lite-color); 52 | text-shadow: none; 53 | font-family: 'REGU'; 54 | font-size: 0.75em; 55 | padding: 0.5em 2.0em; 56 | background-color: var(--theme-brandColor); 57 | } 58 | 59 | #rightInfo, 60 | #leftInfo { 61 | background-color: var(--theme-lite-color); 62 | color: var(--theme-dark-color); 63 | font-family: 'REGU'; 64 | font-size: 2.3em; 65 | padding: 0.2em 0.8em; 66 | border-left: var(--theme-accent-width) 67 | solid 68 | var(--theme-brandColor); 69 | } 70 | 71 | #rightInfo { 72 | border-right: var(--theme-accent-width) 73 | solid 74 | var(--theme-brandColor); 75 | border-left: 0; 76 | 77 | } 78 | 79 | #box1 { 80 | background-color: var(--theme-lite-color); 81 | color: var(--theme-dark-color); 82 | } 83 | 84 | #box3 { 85 | background-color: var(--theme-dark-color); 86 | color: var(--theme-lite-color); 87 | } 88 | 89 | #box4 { 90 | background-color: var(--theme-brandColor); 91 | color: var(--theme-lite-color); 92 | } 93 | 94 | #box3, #box4 { 95 | padding: 0.3em !important; 96 | } 97 | 98 | .left #box1, 99 | .left #box4 { 100 | border-top-right-radius: var(--theme-corner-radius); 101 | border-bottom-right-radius: var(--theme-corner-radius); 102 | } 103 | 104 | .right #box1, 105 | .right #box3 { 106 | border-top-left-radius: var(--theme-corner-radius); 107 | border-bottom-left-radius: var(--theme-corner-radius); 108 | } 109 | 110 | .ticker #headline { 111 | border-left: var(--theme-accent-width) solid var(--theme-brandColor); 112 | background-color: var(--theme-dark-color); 113 | padding: 0.3em !important; 114 | } 115 | 116 | .ticker #tickerText { 117 | padding: 0.3em !important; 118 | } 119 | 120 | -------------------------------------------------------------------------------- /ASSETS/templates/softpix/Template_Pack_1.3.2/themes/Motorsport.css: -------------------------------------------------------------------------------- 1 | :root { 2 | --theme-lite-color: black; 3 | --theme-dark-color: white; 4 | --theme-brandColor: rgb(198, 0, 0); 5 | --theme-tickerBack: white; 6 | 7 | --theme-accent-width: 1vw; 8 | --theme-ticker-height: 4vh; 9 | 10 | --theme-viewport-padding-top: 4vw; 11 | --theme-viewport-padding-sides: 3vw; 12 | --theme-viewport-padding-bottom: 2vw; 13 | 14 | --theme-skew: -10deg; 15 | 16 | } 17 | 18 | @font-face { font-family: "LITE"; src: url("../fonts/Teko-Light.ttf"); } 19 | @font-face { font-family: "REGU"; src: url("../fonts/Teko-Regular.ttf"); } 20 | @font-face { font-family: "BOLD"; src: url("../fonts/Teko-SemiBold.ttf"); } 21 | @font-face { font-family: "BLAC"; src: url("../fonts/Teko-Bold.ttf"); } 22 | 23 | #gfx { 24 | transform: skewX(var(--theme-skew)); 25 | } 26 | 27 | #rightInfo, 28 | #leftInfo, 29 | #box1 { 30 | background-image: 31 | linear-gradient( 32 | rgb(47, 47, 47) 18%, 33 | rgb(74, 76, 77) 42%, 34 | rgb(14, 14, 14) 60%, 35 | rgb(23, 12, 12) 94%, 36 | rgb(41, 39, 39) 100% 37 | ); 38 | font-size: 1.4em; 39 | text-transform: uppercase; 40 | } 41 | 42 | #title2, 43 | #box3, 44 | #box4 { 45 | background-color: rgb(40, 42, 42); 46 | text-transform: uppercase; 47 | text-shadow: 0 4px 4px rgb(0, 0, 0); 48 | border-bottom: 2px solid red; 49 | background-image: linear-gradient( 50 | 180deg, 51 | rgb(14, 14, 14) 25%, 52 | rgb(47, 47, 47) 60%); 53 | } 54 | 55 | 56 | 57 | 58 | #title1, 59 | #text1 { 60 | padding-right: 1em; 61 | background-image: linear-gradient(0deg, rgba(172,172,172,1) 45%, rgba(255,255,255,1) 55%); 62 | -webkit-text-fill-color: transparent; 63 | -webkit-background-clip: text; 64 | filter: drop-shadow( 0.07em 0.07em 0.04em black ); 65 | } 66 | 67 | #text2 { 68 | color: rgb(255, 255, 255); 69 | font-family: 'BOLD'; 70 | } 71 | 72 | #text2:after { 73 | content: '‣'; 74 | color: rgb(198, 0, 0); 75 | font-family: 'BLAC'; 76 | font-size: 1.4em; 77 | margin-left: 0.5em; 78 | line-height: 1em; 79 | text-shadow: 0 0 0.2em rgb(0, 0, 0); 80 | } 81 | 82 | #text3 { 83 | color: rgb(255, 0, 0); 84 | } 85 | 86 | #box4 { 87 | border-radius: 0 0 0.6em 0; 88 | } 89 | 90 | .right #gfx { 91 | right: 2vw; /* move gfx to the left */ 92 | } 93 | 94 | .right #details { 95 | justify-content: flex-end; 96 | } 97 | 98 | .right #box3 { 99 | border-radius: 0 0 0 0.6em; 100 | } 101 | .right #box4 { 102 | border-radius: 0; 103 | } 104 | 105 | .right #text1 { 106 | padding-right: 0; 107 | padding-left: 1em; 108 | } 109 | 110 | 111 | #title1 { 112 | font-size: 2.0em; 113 | font-family: 'BOLD'; 114 | text-transform: uppercase; 115 | padding: 0.01em 1em !important; 116 | } 117 | 118 | #fxLine { 119 | position: absolute; 120 | opacity: 0; 121 | bottom: 0; 122 | width: 70%; 123 | height: 0.1em; 124 | background-color: rgba(255,255,255,0.5); 125 | background-size: 300%; 126 | background-repeat: no-repeat; 127 | background-image: 128 | linear-gradient( 129 | 90deg, 130 | red 40%, 131 | white 50%, 132 | red 60% 133 | ); 134 | animation: AnimationKitt 3s ease-in-out infinite; 135 | } 136 | 137 | 138 | @keyframes AnimationKitt { 139 | 0% { 140 | background-position: 100% 0% 141 | } 142 | 143 | 100% { 144 | background-position: 0% -100% 145 | } 146 | } 147 | 148 | #title2 { 149 | color: white; 150 | font-size: 1.2em; 151 | letter-spacing: 0.1em; 152 | padding: 0.5em 1em 0.25em 1em !important; 153 | font-family: 'REGU'; 154 | text-shadow: 0 0 0.2em rgba(0, 0, 0, 0.8); 155 | } 156 | 157 | 158 | #leftInfo, 159 | #rightInfo { 160 | transform: skewX(var(--theme-skew)); 161 | font-family: 'REGU'; 162 | font-size: 5vh; 163 | color: white; 164 | padding: 0 1.0em; 165 | display: flex; 166 | align-items: center; 167 | justify-content: center; 168 | border: 0; 169 | } 170 | 171 | #leftInfo { 172 | margin-left: 1vw; 173 | border-left: var(--theme-accent-width) 174 | solid 175 | var(--theme-brandColor); 176 | } 177 | 178 | #rightInfo { 179 | margin-right: 1vw; 180 | border-right: var(--theme-accent-width) 181 | solid 182 | var(--theme-brandColor); 183 | } 184 | 185 | #icon { 186 | margin: 0 1.5em; 187 | } 188 | 189 | #leftText, 190 | #rightText { 191 | margin-top: 0.7vh; /* magic number */ 192 | padding: 0 0.2em; 193 | height: 100%; 194 | display: flex; 195 | align-items: center; 196 | justify-content: center; 197 | /* background-image: linear-gradient(0deg, rgba(172,172,172,1) 45%, rgba(255,255,255,1) 55%); 198 | -webkit-text-fill-color: transparent; 199 | -webkit-background-clip: text; 200 | filter: drop-shadow( 0.07em 0.07em 0.04em black ); */ 201 | } 202 | 203 | #tickerZone { 204 | margin-left: 5vw; 205 | background-color: rgb(40, 42, 42); 206 | text-transform: uppercase; 207 | clip-path: inset(1px 1px 1px 1px); 208 | border-bottom: 0; 209 | background-repeat: no-repeat; 210 | background-image: linear-gradient( 211 | 180deg, 212 | rgb(14, 14, 14) 25%, 213 | rgb(47, 47, 47) 60%); 214 | } 215 | 216 | #headline { 217 | color: white !important; 218 | background-size: 100% 100%; 219 | background-image: linear-gradient( 220 | 180deg, 221 | var(--theme-brandColor) 25%, 222 | rgb(111, 1, 1) 60% 223 | ); 224 | } 225 | #tickerText{ 226 | color: white !important; 227 | } -------------------------------------------------------------------------------- /ASSETS/templates/softpix/Template_Pack_1.3.2/themes/News.css: -------------------------------------------------------------------------------- 1 | :root { 2 | 3 | /* COLORS */ 4 | --theme-lite-color: white; 5 | --theme-dark-color: black; 6 | --theme-brandColor: rgb(218, 11, 11); 7 | --theme-tickerBack: white; 8 | 9 | --theme-accent-width: 1vw; 10 | --theme-ticker-height: 4.7vh; 11 | 12 | --theme-viewport-padding-top: 4vw; 13 | --theme-viewport-padding-sides: 3vw; 14 | --theme-viewport-padding-bottom: 2vh; 15 | } 16 | 17 | @font-face { font-family: "LITE"; src: url("../fonts/BarlowSemiCondensed-Regular.ttf"); } 18 | @font-face { font-family: "REGU"; src: url("../fonts/barlow.medium.ttf"); } 19 | @font-face { font-family: "BOLD"; src: url("../fonts/barlow.bold.ttf"); } 20 | @font-face { font-family: "BLAC"; src: url("../fonts/barlow.black.ttf"); } 21 | 22 | 23 | .right #details { 24 | justify-content: flex-end; 25 | } 26 | 27 | #title1 { 28 | background-color: var(--theme-lite-color); 29 | color: var(--theme-dark-color); 30 | font-family: 'BLAC'; 31 | font-size: 1.5em; 32 | padding: 0.05em 0.8em; 33 | text-transform: uppercase; 34 | border-bottom: 0.1em solid var(--theme-brandColor); 35 | } 36 | 37 | #title2{ 38 | color: var(--theme-lite-color); 39 | text-shadow: none; 40 | font-family: 'REGU'; 41 | font-size: 0.75em; 42 | padding: 0.5em 2.0em; 43 | background-color: var(--theme-brandColor); 44 | } 45 | 46 | #rightInfo, 47 | #leftInfo { 48 | background-color: var(--theme-lite-color); 49 | color: var(--theme-dark-color); 50 | font-family: 'REGU'; 51 | font-size: 2.3em; 52 | padding: 0.2em 0.8em; 53 | border-left: var(--theme-accent-width) 54 | solid 55 | var(--theme-brandColor); 56 | } 57 | 58 | #rightInfo { 59 | border-right: var(--theme-accent-width) 60 | solid 61 | var(--theme-brandColor); 62 | border-left: 0; 63 | 64 | } -------------------------------------------------------------------------------- /DATAROOT/MyFirstProject/data/MySecondRundown.json: -------------------------------------------------------------------------------- 1 | { 2 | "warning": "Modifications done in the SPX will overwrite this file.", 3 | "copyright": "(c) 2020- SPX Graphics (https://spx.graphics)", 4 | "updated": "2024-12-15T16:39:43.942Z", 5 | "templates": [], 6 | "project": "MyFirstProject", 7 | "rundown": "MySecondRundown" 8 | } -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | 2 | Copyright (c) 2020- Tuomo Kulomaa 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the "Software"), to deal 6 | in the Software without restriction, including without limitation the rights 7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in all 12 | copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | SOFTWARE. -------------------------------------------------------------------------------- /LOG/.gitignore: -------------------------------------------------------------------------------- 1 | # Ignore everything in this directory 2 | * 3 | # Except this file 4 | !.gitignore -------------------------------------------------------------------------------- /SECURITY.md: -------------------------------------------------------------------------------- 1 | # Security Policy 2 | 3 | ## SPX and security 4 | 5 | Most SPX installations run locally on-prem, but more and more SPX instances are becoming virtualized in-the-cloud. We are aware of some vulnerabilities and welcome you to tell us if you find one. 6 | 7 | ## Reporting a Vulnerability 8 | 9 | If you find a vulnerability you have two options: 10 | 11 | * Use the Security Advisory feature (preferred), or 12 | * Email [info@softpix.io](mailto:info@softpix.io) 13 | 14 | 15 | In your report, 16 | 17 | * describe the issue 18 | * showcase how it can be used 19 | * let us know if you would like to be credited in the README file and with what name? 20 | 21 | We will try reply to your email within 72 hours. If the issue is confirmed, we will release a patch as soon as possible depending on complexity but typically within a few days, in the next binary release at the latest. 22 | -------------------------------------------------------------------------------- /ecosystem.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | apps : [ 3 | { 4 | 'name': 'GC1', 5 | 'script': 'server.js', 6 | 'args': 'config.json' 7 | }, 8 | { 9 | 'name': 'GC2', 10 | 'script': 'server.js', 11 | 'args': 'config2.json' 12 | } 13 | ] 14 | }; 15 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "spx-gc", 3 | "version": "1.3.3", 4 | "description": "SPX | Softpix Graphics Controller", 5 | "homepage": "https://spx.graphics", 6 | "main": "server.js", 7 | "bin": "server.js", 8 | "pkg": { 9 | "assets": [ 10 | "views/**/*", 11 | "static/**/*" 12 | ] 13 | }, 14 | "scripts": { 15 | "test": "echo \"Error: no test specified\" && exit 1", 16 | "devORG": "nodemon server.js -i config.json -i DATAROOT/", 17 | "dev": "nodemon server.js -i DATAROOT/* -i ASSETS/* -i config.json -i DATAROOT_VIDEO/*", 18 | "devdemo": "nodemon server.js config_demodataroot.json -i DATAROOT_DEMO/* -i config_demodataroot.json", 19 | "dev2": "nodemon server.js config2.json -i DATAROOT/* -i config2.json", 20 | "start": "pm2 start server.js --name GC", 21 | "stop": "pm2 stop server.js", 22 | "build": "BUILD_PACKAGES.BAT" 23 | }, 24 | "keywords": [], 25 | "author": "Tuomo Kulomaa (https://spx.graphics)", 26 | "license": "MIT", 27 | "dependencies": { 28 | "axios": "^0.30.0", 29 | "body-parser": "^1.20.3", 30 | "cors": "^2.8.5", 31 | "express": "^4.21.2", 32 | "express-handlebars": "^8.0.1", 33 | "express-session": "^1.18.1", 34 | "glob": "^11.0.0", 35 | "ip": "^2.0.1", 36 | "jsdom": "^25.0.1", 37 | "macaddress": "^0.5.1", 38 | "moment": "^2.29.4", 39 | "morgan": "^1.9.1", 40 | "node-html-parser": "^6.1.13", 41 | "node-wav-player": "^1.0.0", 42 | "node-xlsx": "^0.24.0", 43 | "open": "^10.1.0", 44 | "openurl": "^1.1.1", 45 | "osc": "^2.4.5", 46 | "path": "^0.12.7", 47 | "rotating-file-stream": "^3.2.5", 48 | "socket.io": "^4.8.0", 49 | "sox-play": "0.0.2", 50 | "winston": "^3.17.0" 51 | }, 52 | "devDependencies": { 53 | "nodemon": "^3.1.7" 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /routes/routes-osc-disabled.js: -------------------------------------------------------------------------------- 1 | 2 | var express = require("express"); 3 | const router = express.Router(); 4 | 5 | router.get('/', function (req, res) { 6 | let functionsDoc = { 7 | "sections" : [ 8 | 9 | { 10 | "section" : "osc disabled", 11 | "info" : "See config.json to enable OSC.", 12 | "endpoint" : "/osc", 13 | "commands": [ 14 | { 15 | "vers" : "v1.3.0", 16 | "param" : "", 17 | "info" : "See config.json to enable OSC." 18 | }, 19 | 20 | ] 21 | }, 22 | 23 | ] 24 | } 25 | res.render('view-api-osc', { layout: false, functionList:functionsDoc }); 26 | }); 27 | 28 | module.exports = router; 29 | 30 | 31 | -------------------------------------------------------------------------------- /routes/routes-osc.js: -------------------------------------------------------------------------------- 1 | 2 | /* ------------------------------------------ 3 | 4 | OCS routes for external controllers. 5 | 6 | /osc/ 7 | 8 | Home route is a list of available commands. 9 | 10 | --------------------------------------------- */ 11 | 12 | var express = require("express"); 13 | const router = express.Router(); 14 | const path = require('path'); 15 | const fs = require('fs'); 16 | const moment = require('moment'); 17 | const directoryPath = path.normalize(config.general.dataroot); 18 | const logger = require('../utils/logger'); 19 | logger.debug('OSC route loading...'); 20 | const spx = require('../utils/spx_server_functions.js'); 21 | const axios = require('axios') 22 | const PlayoutCCG = require('../utils/playout_casparCG.js'); 23 | const { query } = require("../utils/logger"); 24 | const spxAuth = require('../utils/spx_auth.js'); 25 | const osc = require("osc") 26 | 27 | // ROUTES FOR OSC DOCUMENTATION ---------------------------------------------------------- 28 | router.get('/', function (req, res) { 29 | let functionsDoc = { 30 | "sections" : [ 31 | 32 | { 33 | "section" : "osc commands", 34 | "info" : "OSC is experimental! Probably does not work. No warranty, use at your own risk.", 35 | "commands": [ 36 | { 37 | "vers" : "v1.3.0", 38 | "param" : "/panic", 39 | "info" : "Force clear to all output layers without out-animations. (Note, this does NOT save on-air state of rundown items to false, so when UI is reloaded the items will show the state before panic was triggered.) This is to be used for emergency situations only and not as a normal STOP command substitute." 40 | }, 41 | 42 | ] 43 | }, 44 | 45 | ] 46 | } 47 | res.render('view-api-osc', { layout: false, functionList:functionsDoc }); 48 | }); 49 | 50 | module.exports = router; 51 | 52 | // OSC over UDP ********************************************************* 53 | 54 | var udpPort = new osc.UDPPort({ 55 | localAddress: '0.0.0.0', 56 | localPort: config.osc.port || 57121, 57 | metadata: true 58 | }); 59 | 60 | // Listen for incoming OSC messages. 61 | udpPort.on("message", function (oscMsg, timeTag, info) { 62 | // console.log("An OSC message just arrived!", oscMsg); 63 | // console.log("Path: ", oscMsg.address); 64 | // console.log("Args: ", oscMsg.args); 65 | // console.log("Remote info is: ", info); 66 | 67 | switch (oscMsg.address) { 68 | case '/panic': 69 | spx.httpGet('http://localhost:5656/api/v1/panic'); 70 | break; 71 | } 72 | }); 73 | 74 | // Open the socket. 75 | udpPort.open(); 76 | 77 | udpPort.on("ready", function () { 78 | logger.verbose("OSC listening in UDP port " + udpPort.options.localPort + "\n"); 79 | }); 80 | 81 | 82 | 83 | -------------------------------------------------------------------------------- /routes/routes-webplayer.js: -------------------------------------------------------------------------------- 1 | 2 | // // ----------------------------------------- 3 | // // Handle Express server routes for the API. 4 | // // ----------------------------------------- 5 | // var express = require("express"); 6 | // const router = express.Router(); 7 | // const path = require('path'); 8 | // const fs = require('fs'); 9 | // const moment = require('moment'); 10 | // const directoryPath = path.normalize(config.general.dataroot); 11 | // const logger = require('../utils/logger'); 12 | // logger.debug('WebPlayer-route loading...'); 13 | // const spx = require('../utils/spx_server_functions.js'); 14 | 15 | // // ROUTES ------------------------------------------------------------------------------------------- 16 | // router.get('/', function (reg, res) { 17 | // let htmlFile = path.join(__dirname+'/../http/renderer/index.html'); 18 | // // res.sendFile(htmlFile); 19 | // res.send(htmlFile); 20 | // }); 21 | 22 | // module.exports = router; -------------------------------------------------------------------------------- /screenshots/00-spx-gc-principle.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TuomoKu/SPX-GC/9ad2d01e4d2af1390ff67109f71f9c2f444200fb/screenshots/00-spx-gc-principle.png -------------------------------------------------------------------------------- /screenshots/00-spx-gc-principle2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TuomoKu/SPX-GC/9ad2d01e4d2af1390ff67109f71f9c2f444200fb/screenshots/00-spx-gc-principle2.png -------------------------------------------------------------------------------- /screenshots/01-spx-gc-projectlist.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TuomoKu/SPX-GC/9ad2d01e4d2af1390ff67109f71f9c2f444200fb/screenshots/01-spx-gc-projectlist.png -------------------------------------------------------------------------------- /screenshots/01-spx-loginpolicy.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TuomoKu/SPX-GC/9ad2d01e4d2af1390ff67109f71f9c2f444200fb/screenshots/01-spx-loginpolicy.png -------------------------------------------------------------------------------- /screenshots/02-spx-gc-rundowlist.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TuomoKu/SPX-GC/9ad2d01e4d2af1390ff67109f71f9c2f444200fb/screenshots/02-spx-gc-rundowlist.png -------------------------------------------------------------------------------- /screenshots/03-spx-gc-controller-empty-rundown.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TuomoKu/SPX-GC/9ad2d01e4d2af1390ff67109f71f9c2f444200fb/screenshots/03-spx-gc-controller-empty-rundown.png -------------------------------------------------------------------------------- /screenshots/04-spx-gc-controller-addtemplate.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TuomoKu/SPX-GC/9ad2d01e4d2af1390ff67109f71f9c2f444200fb/screenshots/04-spx-gc-controller-addtemplate.png -------------------------------------------------------------------------------- /screenshots/05-spx-gc-controller-rundown.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TuomoKu/SPX-GC/9ad2d01e4d2af1390ff67109f71f9c2f444200fb/screenshots/05-spx-gc-controller-rundown.png -------------------------------------------------------------------------------- /screenshots/06-spx-gc-controller-editor.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TuomoKu/SPX-GC/9ad2d01e4d2af1390ff67109f71f9c2f444200fb/screenshots/06-spx-gc-controller-editor.png -------------------------------------------------------------------------------- /screenshots/07-spx-gc-controller-rundown.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TuomoKu/SPX-GC/9ad2d01e4d2af1390ff67109f71f9c2f444200fb/screenshots/07-spx-gc-controller-rundown.png -------------------------------------------------------------------------------- /screenshots/08-spx-gc-appconfig.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TuomoKu/SPX-GC/9ad2d01e4d2af1390ff67109f71f9c2f444200fb/screenshots/08-spx-gc-appconfig.png -------------------------------------------------------------------------------- /screenshots/09-spx-gc-projectsettings.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TuomoKu/SPX-GC/9ad2d01e4d2af1390ff67109f71f9c2f444200fb/screenshots/09-spx-gc-projectsettings.png -------------------------------------------------------------------------------- /screenshots/anatomy-of-an-item.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TuomoKu/SPX-GC/9ad2d01e4d2af1390ff67109f71f9c2f444200fb/screenshots/anatomy-of-an-item.png -------------------------------------------------------------------------------- /screenshots/ccgservers.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TuomoKu/SPX-GC/9ad2d01e4d2af1390ff67109f71f9c2f444200fb/screenshots/ccgservers.png -------------------------------------------------------------------------------- /screenshots/helloworld.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TuomoKu/SPX-GC/9ad2d01e4d2af1390ff67109f71f9c2f444200fb/screenshots/helloworld.png -------------------------------------------------------------------------------- /screenshots/hq.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TuomoKu/SPX-GC/9ad2d01e4d2af1390ff67109f71f9c2f444200fb/screenshots/hq.gif -------------------------------------------------------------------------------- /screenshots/source-files.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TuomoKu/SPX-GC/9ad2d01e4d2af1390ff67109f71f9c2f444200fb/screenshots/source-files.png -------------------------------------------------------------------------------- /screenshots/spx-gc-help-workinprogress.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TuomoKu/SPX-GC/9ad2d01e4d2af1390ff67109f71f9c2f444200fb/screenshots/spx-gc-help-workinprogress.png -------------------------------------------------------------------------------- /screenshots/spx-gc-ui-anim-v1-0.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TuomoKu/SPX-GC/9ad2d01e4d2af1390ff67109f71f9c2f444200fb/screenshots/spx-gc-ui-anim-v1-0.gif -------------------------------------------------------------------------------- /screenshots/spx-zoom.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TuomoKu/SPX-GC/9ad2d01e4d2af1390ff67109f71f9c2f444200fb/screenshots/spx-zoom.png -------------------------------------------------------------------------------- /screenshots/streamdeck.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TuomoKu/SPX-GC/9ad2d01e4d2af1390ff67109f71f9c2f444200fb/screenshots/streamdeck.png -------------------------------------------------------------------------------- /screenshots/windows-installation.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TuomoKu/SPX-GC/9ad2d01e4d2af1390ff67109f71f9c2f444200fb/screenshots/windows-installation.png -------------------------------------------------------------------------------- /screenshots/yt_mockup-showreel.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TuomoKu/SPX-GC/9ad2d01e4d2af1390ff67109f71f9c2f444200fb/screenshots/yt_mockup-showreel.png -------------------------------------------------------------------------------- /screenshots/yt_mockup.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TuomoKu/SPX-GC/9ad2d01e4d2af1390ff67109f71f9c2f444200fb/screenshots/yt_mockup.png -------------------------------------------------------------------------------- /static/css/spx_accents.css: -------------------------------------------------------------------------------- 1 | 2 | /* inspired by https://codepen.io/wildbeard/pen/eZZPZr */ 3 | 4 | 5 | 6 | input[type="radio"] { 7 | display: none; 8 | } 9 | input[type="radio"]:checked + label span { transform: scale(1.25); } 10 | 11 | input[type="radio"]:checked + label .gra { border: 2px solid #ffffff;} 12 | input[type="radio"]:checked + label .red { border: 2px solid #ffffff;} 13 | input[type="radio"]:checked + label .ora { border: 2px solid #ffffff;} 14 | input[type="radio"]:checked + label .gre { border: 2px solid #ffffff;} 15 | input[type="radio"]:checked + label .blu { border: 2px solid #ffffff;} 16 | input[type="radio"]:checked + label .pin { border: 2px solid #ffffff;} 17 | input[type="radio"]:checked + label .vio { border: 2px solid #ffffff;} 18 | input[type="radio"]:checked + label .bla { border: 2px solid #ffffff;} 19 | 20 | label { 21 | display: inline-block; 22 | width: 20px; 23 | height: 20px; 24 | margin-right: 10px; 25 | cursor: pointer; 26 | } 27 | label:hover span { 28 | /* transform: scale(1.25); */ 29 | } 30 | label span { 31 | display: block; 32 | width: 100%; 33 | height: 100%; 34 | transition: transform .2s ease-in-out; 35 | } 36 | 37 | /* grey, red, orange, green, blue, pink, violet, black */ 38 | label span.gra { background: #7a7a7a;} 39 | label span.red { background: #DB2828;} 40 | label span.ora { background: #F2711C;} 41 | label span.gre { background: #21BA45;} 42 | label span.blu { background: #2185D0;} 43 | label span.pin { background: #E03997;} 44 | label span.vio { background: #8840FF;} 45 | label span.bla { background: #000000;} 46 | 47 | .row_accent0 {border-left: 10px solid #696969; } 48 | .row_accent1 {border-left: 10px solid rgb(114, 38, 38); } 49 | .row_accent2 {border-left: 10px solid rgb(117, 70, 38); } 50 | .row_accent3 {border-left: 10px solid rgb(42, 107, 57); } 51 | .row_accent4 {border-left: 10px solid rgb(41, 87, 122); } 52 | .row_accent5 {border-left: 10px solid rgb(124, 57, 95); } 53 | .row_accent6 {border-left: 10px solid rgb(91, 67, 129); } 54 | .row_accent7 {border-left: 10px solid #181818; } 55 | .row_accentExtras {border-left: 10px solid #acacac; } 56 | 57 | .accent_{ 58 | background-color: rgba(255,255,255,0.05); 59 | border: 0px solid yellow; 60 | width:100%; 61 | padding:10px; 62 | margin-bottom:15px; 63 | border-left: 12px solid white; 64 | box-shadow: rgba(0, 0, 0, 0.7) 0px 0px 10px; 65 | } -------------------------------------------------------------------------------- /static/css/spx_colors.css: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2020 Softpix 3 | CSS file for (bg)colors. Must be loaded after spx_layout.css 4 | */ 5 | 6 | .disabled { 7 | opacity: 0.2; 8 | cursor: not-allowed; 9 | } 10 | 11 | .bg_body { 12 | background-color: #414C5A; 13 | } 14 | 15 | 16 | .darkBg { 17 | background-color: rgba(0, 0, 0, 0.2); 18 | } 19 | 20 | .bg_green { 21 | background-color: #54af3c; 22 | } 23 | 24 | .green { 25 | color: #5f9750; 26 | } 27 | 28 | .bg_yellow { 29 | background-color: #e5c310; 30 | } 31 | 32 | .bg_orange { 33 | background-color: #e57010; 34 | } 35 | 36 | .bg_blue { 37 | background-color: #375ea3; 38 | } 39 | 40 | .bg_red { 41 | background-color: #9c1a29; 42 | } 43 | 44 | .bg_black { 45 | background-color: #000000; 46 | } 47 | 48 | .bg_white { 49 | background-color: #ffffff; 50 | } 51 | 52 | .bg_grey80 { 53 | background-color: #272833; 54 | } 55 | 56 | .bg_grey { 57 | background-color: #485a6b; 58 | } 59 | 60 | .bg_grey60 { 61 | background-color: #2f303e; 62 | } 63 | 64 | .bg_grey40 { 65 | background-color: #343D48; 66 | } 67 | 68 | .bg_grey15 { 69 | background-color: #656e7a; 70 | /* #706e83; */ 71 | } 72 | 73 | .bg_grey10 { 74 | background-color: #9aa4b1; 75 | /* #706e83; */ 76 | } 77 | 78 | .black{ 79 | color: black; 80 | } 81 | 82 | .bg_darken{ 83 | background-color: rgba(0,0,0,0.07); 84 | } 85 | 86 | .bg_lighten{ 87 | background-color: rgba(255,255,255,0.05); 88 | } 89 | 90 | .newVersionBubble { 91 | padding: 2px 10px; 92 | background-color: rgb(39, 95, 58); 93 | border-radius: 4px; 94 | /* margin-right: 10px; */ 95 | font-size: 90%; 96 | cursor: pointer; 97 | display: inline-block; 98 | margin-top: 5px; 99 | } 100 | 101 | .fg_orange { 102 | color: #e57010; 103 | } -------------------------------------------------------------------------------- /static/css/spx_flex.css: -------------------------------------------------------------------------------- 1 | html, body { 2 | box-sizing: border-box; 3 | height: 100%; 4 | margin: 0; 5 | overflow: hidden; 6 | background-color: #414C5A; 7 | font-family: 'Lucida Sans', 'Lucida Sans Regular', 'Lucida Grande', 'Lucida Sans Unicode', Geneva, Verdana, sans-serif; 8 | font-size: 1.5vw; 9 | } 10 | 11 | *, *:before, *:after { 12 | box-sizing: inherit; 13 | margin: 0; 14 | padding: 0; 15 | } 16 | 17 | .wrapper { 18 | height: 100%; 19 | display: flex; 20 | flex-direction: column; 21 | overflow: hidden; 22 | } 23 | 24 | .header{ 25 | height: 100px; 26 | } 27 | 28 | .footer { 29 | height: 60px; 30 | } 31 | 32 | .content { 33 | flex: 1; 34 | overflow: auto; 35 | } 36 | 37 | .left { 38 | width: 60%; 39 | height: 100%; 40 | float: left; 41 | overflow: auto; 42 | } 43 | 44 | .right { 45 | margin-left: 60%; 46 | height: 100%; 47 | overflow: auto; 48 | } 49 | 50 | .pM 51 | { 52 | padding: 10px; 53 | } 54 | 55 | 56 | .item 57 | { 58 | width: 100%; 59 | height: 100px; 60 | margin-bottom: 10px; 61 | background-color: black; 62 | } 63 | 64 | 65 | .itemlist 66 | { 67 | background-color: #2A3641; 68 | 69 | } 70 | 71 | td 72 | { 73 | vertical-align: middle; 74 | } 75 | 76 | /* .statusbar 77 | { 78 | font-family: Arial, Helvetica, sans-serif; 79 | font-size: 70%; 80 | padding: 3px 12px; 81 | color: #aaaaaa; 82 | } */ 83 | 84 | 85 | .scroller::-webkit-scrollbar-track 86 | { 87 | -webkit-box-shadow: inset 0 0 6px rgba(0,0,0,0.0); 88 | border-radius: 0px; 89 | background-color: #414C5A; 90 | } 91 | 92 | .scroller::-webkit-scrollbar 93 | { 94 | width: 14px; 95 | background-color: #414C5A; 96 | } 97 | 98 | .scroller::-webkit-scrollbar-thumb 99 | { 100 | border-radius: 10px; 101 | -webkit-box-shadow: inset 0 0 6px rgba(0,0,0,.0); 102 | background-color: #677180; 103 | } 104 | -------------------------------------------------------------------------------- /static/css/spx_fonts.css: -------------------------------------------------------------------------------- 1 | /* @font-face { 2 | font-family: "Yle-Black"; 3 | src: url("/fonts/Yle-Black.ttf"); 4 | } 5 | 6 | @font-face { 7 | font-family: "Yle-Medium"; 8 | src: url("/fonts/Yle-Medium.ttf"); 9 | } 10 | 11 | @font-face { 12 | font-family: "Yle-Regular"; 13 | src: url("/fonts/Yle-Regular.ttf"); 14 | } */ 15 | 16 | 17 | @font-face { font-family: "UI-Light"; src: url("/fonts/EncodeSansSemiExpanded-Light.ttf"); } 18 | @font-face { font-family: "UI-Regular"; src: url("/fonts/EncodeSansSemiExpanded-Regular.ttf"); } 19 | @font-face { font-family: "UI-Bold"; src: url("/fonts/EncodeSansSemiExpanded-Bold.ttf"); } 20 | @font-face { font-family: "UI-Black"; src: url("/fonts/EncodeSansSemiExpanded-Black.ttf"); } 21 | 22 | 23 | @font-face { font-family: "FontAwesomeReg400"; src: url("/fonts/fa-regular-400.ttf"); } 24 | @font-face { font-family: "FontAwesomeSol900"; src: url("/fonts/fa-solid-900.ttf"); } -------------------------------------------------------------------------------- /static/css/spx_renderpopups.css: -------------------------------------------------------------------------------- 1 | html, 2 | body { 3 | margin: 0; 4 | padding: 0; 5 | width: 100vw; 6 | height: 100vh; 7 | display: flex; 8 | color: white; 9 | overflow: hidden; 10 | align-items: center; 11 | flex-direction: column; 12 | justify-content: center; 13 | font-family: 'UI-Regular'; 14 | background-color: rgb(0,0,0); 15 | } 16 | 17 | *, *:before, *:after { 18 | box-sizing: inherit; 19 | margin: 0; 20 | padding: 0; 21 | user-select: none; 22 | } 23 | 24 | .renderer { 25 | background-image: url('/img/checker60.png'); /* bgimage for local preview */ 26 | background-size: 10vw; 27 | transform-origin: top left; 28 | border: 0px; 29 | outline: 0px; 30 | padding: 0; 31 | margin: 0; 32 | } 33 | 34 | #toolbar { 35 | flex-grow: 0; 36 | width: 100%; 37 | display: flex; 38 | align-items: left; 39 | flex-direction: row; 40 | align-self: flex-start; 41 | border: 1px solid yellow; 42 | } 43 | 44 | .watermark { 45 | top: 0; 46 | left: 0; 47 | top: 50%; 48 | left: 50%; 49 | opacity: 1; /* WATERMARK if 1 = visible, 0 = invisible */ 50 | width: 50vw; 51 | z-index:100; 52 | height: 50vw; 53 | position: absolute; 54 | background-size: contain; 55 | border: 0px solid yellow; 56 | background-position: center; 57 | background-repeat: no-repeat; 58 | transform: translate(-50%, -50%); 59 | } 60 | 61 | .prev { 62 | background-image: url("/img/spx_preview.png"); 63 | } 64 | 65 | .prog { 66 | background-image: url("/img/spx_program.png"); 67 | } -------------------------------------------------------------------------------- /static/css/ticker.css: -------------------------------------------------------------------------------- 1 | :root { 2 | /* Define a variable for the margin of the overlay, ~70px at 1080p */ 3 | --margin: 3.65vmax; 4 | } 5 | 6 | .main { 7 | position: absolute; 8 | top: calc(12 * var(--margin)); 9 | left: var(--margin); 10 | width: calc(100vw - 2 * var(--margin) - 12vw); 11 | opacity: 0; 12 | } 13 | 14 | .f1_bg { 15 | background: var(--main-color); 16 | display: table; 17 | position: relative; 18 | padding: 0.2em 0.4em; 19 | } 20 | 21 | .f1_text { 22 | color: var(--white-color); 23 | font-family: 'UI-Black'; 24 | font-size: 0.8rem; 25 | line-height: 1; 26 | width: 100%; 27 | } 28 | 29 | .f2_bg { 30 | background: var(--white-color); 31 | display: table; 32 | position: relative; 33 | padding: 0.1em 0.4em 0em; 34 | width: 100%; 35 | } 36 | 37 | .f2_text { 38 | color: var(--black-color); 39 | font-family: 'UI-Black'; 40 | font-size: 1.45rem; 41 | line-height: 1.25; 42 | overflow: hidden; 43 | white-space: nowrap; 44 | letter-spacing: -0.05rem; 45 | } 46 | 47 | .f3_bg { 48 | z-index: 2; 49 | background: #252525; 50 | float: left; 51 | position: relative; 52 | padding: 0.1em 0.4em; 53 | } 54 | 55 | .f3_text { 56 | color: var(--white-color); 57 | font-family: 'UI-Regular'; 58 | font-size: 0.9rem; 59 | line-height: 1.25; 60 | } 61 | 62 | .ticker_bg { 63 | z-index: -1; 64 | background: var(--dark-color); 65 | width: 100%; 66 | padding-left: 0.5em; 67 | padding-right: 0.5em; 68 | } 69 | 70 | .ticker { 71 | z-index: 1; 72 | } 73 | 74 | .webTicker { 75 | color: var(--white-color); 76 | float: left; 77 | position: relative; 78 | } 79 | 80 | .webTicker li { 81 | font-size: 1rem; 82 | line-height: 1.2; 83 | font-family: 'UI-Regular'; 84 | } 85 | -------------------------------------------------------------------------------- /static/css/toggle.css: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | /* 6 | Rec toggle switch and 7 | volume sliders 8 | 9 | */ 10 | 11 | :root { 12 | --vc-off-color: #2A3641; /* linear-gradient(190deg, rgba(0, 0, 0, 0.39) 5%, rgba(0, 0, 0, 0.157) 50%); */ 13 | --vc-on-color: #2A3641; 14 | --vc-animation-speed: 0.2s ease-out; 15 | --vc-font-family: "UI-Regular"; 16 | --vc-font-size: 1.2em; 17 | --vc-font-weight: unset; 18 | --vc-on-font-color: rgba(255, 255, 255, 1.0); 19 | --vc-off-font-color: rgb(255, 255, 255); 20 | --vc-label-position-off: 14px; 21 | --vc-label-position-on: 14px; 22 | --vc-height: 1.3em; /* 2.0em*/ 23 | --vc-width: 100%; 24 | --vc-handle-width: 60%; 25 | --vc-handle-height: 90%; 26 | --vc-box-border-radius: 0px; 27 | --vc-handle-border-radius: 0px; 28 | --vc-handle-shadow: 0px 0px 5px rgba(0, 0, 0, 0.5); 29 | --vc-handle-color: #485a6b; /* rgb(61, 61, 61); */ 30 | --vc-onclick-width: 80%; 31 | --vc-handle-top: 0.1em; /* 0.2 */ 32 | --vc-handle-fontsize: 0.7em; /* 0.8 */ 33 | --vc-handle-lineHeight: 1.7em; /* 2.2 */ 34 | } 35 | 36 | .toggle-container * { 37 | font-family: var(--vc-font-family); 38 | transition: var(--vc-animation-speed); 39 | cursor: pointer; 40 | /* background: darkgreen; */ 41 | } 42 | 43 | .vc-switch { 44 | width: var(--vc-width); 45 | height: var(--vc-height); 46 | margin-top: 5px; 47 | } 48 | 49 | .toggle-container label { 50 | position: relative; 51 | display: inline-block; 52 | vertical-align: top; 53 | border-radius: var(--vc-box-border-radius); 54 | /* cursor: pointer; */ 55 | } 56 | 57 | .vc-switch-input { 58 | position: absolute; 59 | transform: translate3d(5px, 5px, 0); 60 | opacity: 0; 61 | } 62 | 63 | .vc-switch-label { 64 | position: relative; 65 | display: block; 66 | height: inherit; 67 | font-size: var(--vc-font-size); 68 | font-weight: var(--vc-font-weight); 69 | background: var(--vc-off-color); 70 | border-radius: inherit; 71 | } 72 | 73 | .vc-switch-label:before, 74 | .vc-switch-label:after { 75 | position: absolute; 76 | top: 50%; 77 | margin-top: -0.5em; 78 | line-height: 1.1; 79 | } 80 | 81 | .vc-switch-label:before { 82 | content: attr(data-off); 83 | color: var(--vc-on-font-color); 84 | } 85 | 86 | .vc-switch-label:after { 87 | content: attr(data-on); 88 | color: var(--vc-off-font-color); 89 | opacity: 0; 90 | } 91 | 92 | .vc-switch-label:before { 93 | right: var(--vc-label-position-off); 94 | top: 16px; 95 | } 96 | 97 | .vc-switch-label:after { 98 | left: var(--vc-label-position-on);; 99 | } 100 | 101 | .vc-switch-input:checked ~ .vc-switch-label { 102 | background: var(--vc-on-color); 103 | text-shadow: 0px 0px 6px rgba(255, 255, 255, 0.6); 104 | } 105 | 106 | .vc-switch-input:checked ~ .vc-switch-label:before { 107 | opacity: 0; 108 | } 109 | 110 | .vc-switch-input:checked ~ .vc-switch-label:after { 111 | opacity: 1; 112 | } 113 | 114 | .vc-handle { 115 | left: 2px; 116 | font-size: var(--vc-handle-fontsize); 117 | position: absolute !important; 118 | top: var(--vc-handle-top); 119 | background: var(--vc-handle-color); 120 | border-radius: var(--vc-handle-border-radius); 121 | box-shadow: var(--vc-handle-shadow); 122 | width: var(--vc-handle-width); 123 | height: var(--vc-handle-height); 124 | line-height: var(--vc-handle-lineHeight); 125 | } 126 | 127 | 128 | .vc-handle:before { 129 | content: ""; 130 | top: 50%; 131 | left: 50%; 132 | position: absolute !important; 133 | margin: -6px 0 0 -6px; 134 | width: 12px; 135 | height: 12px; 136 | border-radius: 6px; 137 | } 138 | 139 | .vc-switch-label:active ~ .vc-handle, .vc-handle:active { 140 | width: var(--vc-onclick-width); 141 | } 142 | 143 | .vc-switch-input:checked ~ .vc-handle { 144 | left: unset; 145 | right: 2px; 146 | color: var(--vc-on-font-color); 147 | text-shadow: 0 0 6px var(--vc-on-font-color), 148 | 0 4px 8px green, 149 | 0 0px 12px rgb(20, 209, 20); 150 | } 151 | 152 | 153 | -------------------------------------------------------------------------------- /static/css/toggle2.css: -------------------------------------------------------------------------------- 1 | .can-toggle { 2 | position: relative; 3 | border: 1px solid yellow; 4 | padding: 0; 5 | } 6 | .can-toggle *, .can-toggle *:before, .can-toggle *:after { 7 | -moz-box-sizing: border-box; 8 | box-sizing: border-box; 9 | } 10 | .can-toggle input[type="checkbox"] { 11 | top: 0; 12 | left: 0; 13 | opacity: 0; 14 | position: absolute; 15 | } 16 | .can-toggle input[type="checkbox"][disabled] ~ label { 17 | pointer-events: none; 18 | } 19 | .can-toggle input[type="checkbox"][disabled] ~ label .can-toggle__switch { 20 | opacity: 0.4; 21 | } 22 | .can-toggle input[type="checkbox"]:checked ~ label .can-toggle__switch:before { 23 | left: 0; 24 | content: attr(data-unchecked); 25 | } 26 | .can-toggle input[type="checkbox"]:checked ~ label .can-toggle__switch:after { 27 | content: attr(data-checked); 28 | } 29 | .can-toggle label { 30 | display: flex; 31 | user-select: none; 32 | position: relative; 33 | align-items: center; 34 | } 35 | .can-toggle label .can-toggle__label-text { 36 | /* not visible */ 37 | flex: 1; 38 | padding-left: 32px; 39 | } 40 | 41 | .can-toggle label .can-toggle__switch { 42 | position: relative; 43 | } 44 | 45 | .can-toggle label .can-toggle__switch:before { 46 | top: 0px; 47 | text-align: center; 48 | position: absolute; 49 | content: attr(data-checked); 50 | text-transform: uppercase; 51 | background-color: blue; 52 | } 53 | .can-toggle label .can-toggle__switch:after { 54 | z-index: 5; 55 | text-align: center; 56 | position: absolute; 57 | background: #485a6b; 58 | text-transform: uppercase; 59 | content: attr(data-unchecked); 60 | transform: translate3d(0, 0, 0); 61 | } 62 | 63 | .can-toggle label .can-toggle__label-text { 64 | flex: 1; 65 | } 66 | .can-toggle label .can-toggle__switch { 67 | transition: background-color 0.3s cubic-bezier(0, 1, 0.5, 1); 68 | background: rgba(0, 0, 0, 0.5); /* bg color */ 69 | } 70 | .can-toggle label .can-toggle__switch:before { 71 | color: rgb(160, 160, 160); /* text color on the back */ 72 | } 73 | .can-toggle label .can-toggle__switch:after { 74 | transition: transform 0.3s cubic-bezier(0, 1, 0.5, 1); 75 | color: rgb(255, 255, 255); /* text color on the sliding button */ 76 | } 77 | .can-toggle input[type="checkbox"]:focus ~ label .can-toggle__switch:after, .can-toggle input[type="checkbox"]:hover ~ label .can-toggle__switch:after { 78 | box-shadow: 0 3px 3px rgba(0, 0, 0, 0.4); 79 | } 80 | .can-toggle input[type="checkbox"]:checked ~ label .can-toggle__switch:after { 81 | transform: translate3d(80px, 0, 0); /*65px*/ 82 | /*margin-left: 80px; */ 83 | } 84 | /* 85 | .can-toggle input[type="checkbox"]:checked:focus ~ label .can-toggle__switch:after, .can-toggle input[type="checkbox"]:checked:hover ~ label .can-toggle__switch:after { 86 | box-shadow: 0 3px 3px rgba(0, 0, 0, 0.4); 87 | } 88 | */ 89 | .can-toggle label { 90 | /* font-size: 14px; */ 91 | } 92 | .can-toggle label .can-toggle__switch { 93 | flex: 1 0; 94 | /* background-color: hotpink; */ 95 | /*height: 4em; 36px; */ 96 | border-radius: 4px; 97 | } 98 | .can-toggle label .can-toggle__switch:before { 99 | /* non-active one */ 100 | left: max(67px, 50%); /*67px*/ 101 | font-size: 0.7em; /*12px;*/ 102 | line-height: 1; 103 | width: max(67px, 50%); /* 67px; */ 104 | /* background-color: red; */ 105 | padding: 0 0.2em; /*12px*/ 106 | } 107 | 108 | .can-toggle label .can-toggle__switch:after { 109 | /* active one */ 110 | top: 0px; 111 | left: 0px; 112 | border-radius: 2px; 113 | width: max(67px, 50%); /* 67px; */ 114 | line-height: 1; 115 | font-size: 0.7em; /*12px;*/ 116 | /* background-color: greenyellow; */ 117 | 118 | } 119 | 120 | .can-toggle label .can-toggle__switch:hover:after { 121 | box-shadow: 0 3px 3px rgba(0, 0, 0, 0.4); 122 | } 123 | 124 | -------------------------------------------------------------------------------- /static/css/tree.css: -------------------------------------------------------------------------------- 1 | /* simple tree */ 2 | 3 | body{ 4 | font-family: Arial, Helvetica, sans-serif; 5 | } 6 | 7 | .simple-tree { 8 | user-select: none; 9 | -moz-user-select: none; 10 | } 11 | .simple-tree>details>summary { 12 | display: none; 13 | } 14 | .simple-tree a, 15 | .simple-tree summary { 16 | display: block; 17 | width: fit-content; 18 | width: -moz-fit-content; 19 | border: solid 1px transparent; 20 | padding: 0 2px; 21 | outline: none; 22 | cursor: pointer; 23 | } 24 | .simple-tree a { 25 | text-decoration: none; 26 | color: inherit; 27 | } 28 | .simple-tree ::-webkit-details-marker { 29 | display: none; 30 | } 31 | .simple-tree summary { 32 | list-style-type: none; 33 | background-color: #eee; 34 | outline: none; 35 | } 36 | .simple-tree.dark summary { 37 | background-color: #444; 38 | } 39 | .simple-tree details>:not(details), 40 | .simple-tree details { 41 | position: relative; 42 | } 43 | .simple-tree details :not(summary) { 44 | margin-left: 20px; 45 | } 46 | .simple-tree.nodots details :not(summary) { 47 | margin-left: 12px; 48 | } 49 | .simple-tree details::before, 50 | .simple-tree details>:not(details)::before { 51 | content: ''; 52 | width: 10px; 53 | display: block; 54 | position: absolute; 55 | } 56 | .simple-tree details::before, 57 | .simple-tree details>:not(details)::before { 58 | background: url('data:image/svg+xml;utf8,') left top / 2px 2px; 59 | } 60 | .simple-tree.dark details::before, 61 | .simple-tree.dark details>:not(summary)::before { 62 | background-image: url('data:image/svg+xml;utf8,'); 63 | } 64 | .simple-tree.nodots details::before, 65 | .simple-tree.nodots details>:not(summary)::before { 66 | background-image: none; 67 | } 68 | .simple-tree details::before { 69 | top: 0; 70 | height: 100%; 71 | background-repeat: repeat-y; 72 | left: 5px; 73 | z-index: -1; 74 | } 75 | .simple-tree details>:not(details)::before { 76 | top: 8px; 77 | height: calc(100% - 8px); 78 | background-repeat: repeat-x; 79 | left: -15px; 80 | } 81 | .simple-tree details>summary::before { 82 | background: url('data:image/svg+xml;utf8,') center center / 12px 12px no-repeat; 83 | left: -22px; 84 | top: 2px; 85 | width: 16px; 86 | height: 16px; 87 | } 88 | .simple-tree details[open]>summary::before { 89 | background-image: url('data:image/svg+xml;utf8,'); 90 | } 91 | /* async tree */ 92 | .async-tree details[open][data-loaded=false] { 93 | pointer-events: none; 94 | } 95 | .async-tree details[open][data-loaded=false]>summary::before { 96 | background-image: url('data:image/svg+xml;utf8,'); 97 | } 98 | .async-tree.black details[open][data-loaded=false]>summary::before { 99 | background-image: url('data:image/svg+xml;utf8,'); 100 | } 101 | /* select tree */ 102 | .select-tree .selected { 103 | background-color: #beebff; 104 | border-color: #99defd; 105 | z-index: 1; 106 | } 107 | 108 | .select-tree.dark .selected { 109 | background-color: #3a484e; 110 | border-color: #99defd; 111 | } 112 | -------------------------------------------------------------------------------- /static/css/vanilla-js-tooltip.css: -------------------------------------------------------------------------------- 1 | 2 | :root { 3 | --tooltipbg: rgba(0, 0, 0, 0.7); 4 | } 5 | 6 | .b-tooltip { 7 | font-size: 0.6em; 8 | padding: 0.3em; 9 | border-radius: 0.2em; 10 | color: rgba(255, 255, 255, 0.6); 11 | text-align: center; 12 | position: absolute; 13 | background: var(--tooltipbg); 14 | display: inline-block; 15 | opacity: 1; 16 | } 17 | 18 | 19 | .b-tooltip:after { 20 | content: " "; 21 | position: absolute; 22 | left: -10px; 23 | top: 4px; 24 | border-top: 10px solid transparent; 25 | border-right: 10px solid var(--tooltipbg); 26 | border-left: none; 27 | border-bottom: 10px solid transparent; 28 | } 29 | -------------------------------------------------------------------------------- /static/css/vanilla-picker.csp.css: -------------------------------------------------------------------------------- 1 | .picker_wrapper.no_alpha .picker_alpha{display:none}.picker_wrapper.no_editor .picker_editor{position:absolute;z-index:-1;opacity:0}.picker_wrapper.no_cancel .picker_cancel{display:none}.layout_default.picker_wrapper{display:flex;flex-flow:row wrap;justify-content:space-between;align-items:stretch;font-size:10px;width:25em;padding:.5em}.layout_default.picker_wrapper input,.layout_default.picker_wrapper button{font-size:1rem}.layout_default.picker_wrapper>*{margin:.5em}.layout_default.picker_wrapper::before{content:"";display:block;width:100%;height:0;order:1}.layout_default .picker_slider,.layout_default .picker_selector{padding:1em}.layout_default .picker_hue{width:100%}.layout_default .picker_sl{flex:1 1 auto}.layout_default .picker_sl::before{content:"";display:block;padding-bottom:100%}.layout_default .picker_editor{order:1;width:6.5rem}.layout_default .picker_editor input{width:100%;height:100%}.layout_default .picker_sample{order:1;flex:1 1 auto}.layout_default .picker_done,.layout_default .picker_cancel{order:1}.picker_wrapper{box-sizing:border-box;background:#f2f2f2;box-shadow:0 0 0 1px silver;cursor:default;font-family:sans-serif;color:#444;pointer-events:auto}.picker_wrapper:focus{outline:none}.picker_wrapper button,.picker_wrapper input{box-sizing:border-box;border:none;box-shadow:0 0 0 1px silver;outline:none}.picker_wrapper button:focus,.picker_wrapper button:active,.picker_wrapper input:focus,.picker_wrapper input:active{box-shadow:0 0 2px 1px #1e90ff}.picker_wrapper button{padding:.4em .6em;cursor:pointer;background-color:#f5f5f5;background-image:linear-gradient(0deg, gainsboro, transparent)}.picker_wrapper button:active{background-image:linear-gradient(0deg, transparent, gainsboro)}.picker_wrapper button:hover{background-color:#fff}.picker_selector{position:absolute;z-index:1;display:block;-webkit-transform:translate(-50%, -50%);transform:translate(-50%, -50%);border:2px solid #fff;border-radius:100%;box-shadow:0 0 3px 1px #67b9ff;background:currentColor;cursor:pointer}.picker_slider .picker_selector{border-radius:2px}.picker_hue{position:relative;background-image:linear-gradient(90deg, red, yellow, lime, cyan, blue, magenta, red);box-shadow:0 0 0 1px silver}.picker_sl{position:relative;box-shadow:0 0 0 1px silver;background-image:linear-gradient(180deg, white, rgba(255, 255, 255, 0) 50%),linear-gradient(0deg, black, rgba(0, 0, 0, 0) 50%),linear-gradient(90deg, #808080, rgba(128, 128, 128, 0))}.picker_alpha,.picker_sample{position:relative;background:linear-gradient(45deg, lightgrey 25%, transparent 25%, transparent 75%, lightgrey 75%) 0 0/2em 2em,linear-gradient(45deg, lightgrey 25%, white 25%, white 75%, lightgrey 75%) 1em 1em/2em 2em;box-shadow:0 0 0 1px silver}.picker_alpha .picker_selector,.picker_sample .picker_selector{background:none}.picker_editor input{font-family:monospace;padding:.2em .4em}.picker_sample::before{content:"";position:absolute;display:block;width:100%;height:100%;background:currentColor}.picker_arrow{position:absolute;z-index:-1}.picker_wrapper.popup{position:absolute;z-index:2;margin:1.5em}.picker_wrapper.popup,.picker_wrapper.popup .picker_arrow::before,.picker_wrapper.popup .picker_arrow::after{background:#f2f2f2;box-shadow:0 0 10px 1px rgba(0,0,0,.4)}.picker_wrapper.popup .picker_arrow{width:3em;height:3em;margin:0}.picker_wrapper.popup .picker_arrow::before,.picker_wrapper.popup .picker_arrow::after{content:"";display:block;position:absolute;top:0;left:0;z-index:-99}.picker_wrapper.popup .picker_arrow::before{width:100%;height:100%;-webkit-transform:skew(45deg);transform:skew(45deg);-webkit-transform-origin:0 100%;transform-origin:0 100%}.picker_wrapper.popup .picker_arrow::after{width:150%;height:150%;box-shadow:none}.popup.popup_top{bottom:100%;left:0}.popup.popup_top .picker_arrow{bottom:0;left:0;-webkit-transform:rotate(-90deg);transform:rotate(-90deg)}.popup.popup_bottom{top:100%;left:0}.popup.popup_bottom .picker_arrow{top:0;left:0;-webkit-transform:rotate(90deg) scale(1, -1);transform:rotate(90deg) scale(1, -1)}.popup.popup_left{top:0;right:100%}.popup.popup_left .picker_arrow{top:0;right:0;-webkit-transform:scale(-1, 1);transform:scale(-1, 1)}.popup.popup_right{top:0;left:100%}.popup.popup_right .picker_arrow{top:0;left:0} -------------------------------------------------------------------------------- /static/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TuomoKu/SPX-GC/9ad2d01e4d2af1390ff67109f71f9c2f444200fb/static/favicon.ico -------------------------------------------------------------------------------- /static/fonts/EncodeSansSemiExpanded-Black.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TuomoKu/SPX-GC/9ad2d01e4d2af1390ff67109f71f9c2f444200fb/static/fonts/EncodeSansSemiExpanded-Black.ttf -------------------------------------------------------------------------------- /static/fonts/EncodeSansSemiExpanded-Bold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TuomoKu/SPX-GC/9ad2d01e4d2af1390ff67109f71f9c2f444200fb/static/fonts/EncodeSansSemiExpanded-Bold.ttf -------------------------------------------------------------------------------- /static/fonts/EncodeSansSemiExpanded-Light.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TuomoKu/SPX-GC/9ad2d01e4d2af1390ff67109f71f9c2f444200fb/static/fonts/EncodeSansSemiExpanded-Light.ttf -------------------------------------------------------------------------------- /static/fonts/EncodeSansSemiExpanded-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TuomoKu/SPX-GC/9ad2d01e4d2af1390ff67109f71f9c2f444200fb/static/fonts/EncodeSansSemiExpanded-Regular.ttf -------------------------------------------------------------------------------- /static/fonts/OFL.txt: -------------------------------------------------------------------------------- 1 | Copyright 2012 The Encode Project Authors (impallari@gmail.com), with Reserved Font Name "Encode Sans”. 2 | 3 | This Font Software is licensed under the SIL Open Font License, Version 1.1. 4 | This license is copied below, and is also available with a FAQ at: 5 | http://scripts.sil.org/OFL 6 | 7 | 8 | ----------------------------------------------------------- 9 | SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 10 | ----------------------------------------------------------- 11 | 12 | PREAMBLE 13 | The goals of the Open Font License (OFL) are to stimulate worldwide 14 | development of collaborative font projects, to support the font creation 15 | efforts of academic and linguistic communities, and to provide a free and 16 | open framework in which fonts may be shared and improved in partnership 17 | with others. 18 | 19 | The OFL allows the licensed fonts to be used, studied, modified and 20 | redistributed freely as long as they are not sold by themselves. The 21 | fonts, including any derivative works, can be bundled, embedded, 22 | redistributed and/or sold with any software provided that any reserved 23 | names are not used by derivative works. The fonts and derivatives, 24 | however, cannot be released under any other type of license. The 25 | requirement for fonts to remain under this license does not apply 26 | to any document created using the fonts or their derivatives. 27 | 28 | DEFINITIONS 29 | "Font Software" refers to the set of files released by the Copyright 30 | Holder(s) under this license and clearly marked as such. This may 31 | include source files, build scripts and documentation. 32 | 33 | "Reserved Font Name" refers to any names specified as such after the 34 | copyright statement(s). 35 | 36 | "Original Version" refers to the collection of Font Software components as 37 | distributed by the Copyright Holder(s). 38 | 39 | "Modified Version" refers to any derivative made by adding to, deleting, 40 | or substituting -- in part or in whole -- any of the components of the 41 | Original Version, by changing formats or by porting the Font Software to a 42 | new environment. 43 | 44 | "Author" refers to any designer, engineer, programmer, technical 45 | writer or other person who contributed to the Font Software. 46 | 47 | PERMISSION & CONDITIONS 48 | Permission is hereby granted, free of charge, to any person obtaining 49 | a copy of the Font Software, to use, study, copy, merge, embed, modify, 50 | redistribute, and sell modified and unmodified copies of the Font 51 | Software, subject to the following conditions: 52 | 53 | 1) Neither the Font Software nor any of its individual components, 54 | in Original or Modified Versions, may be sold by itself. 55 | 56 | 2) Original or Modified Versions of the Font Software may be bundled, 57 | redistributed and/or sold with any software, provided that each copy 58 | contains the above copyright notice and this license. These can be 59 | included either as stand-alone text files, human-readable headers or 60 | in the appropriate machine-readable metadata fields within text or 61 | binary files as long as those fields can be easily viewed by the user. 62 | 63 | 3) No Modified Version of the Font Software may use the Reserved Font 64 | Name(s) unless explicit written permission is granted by the corresponding 65 | Copyright Holder. This restriction only applies to the primary font name as 66 | presented to the users. 67 | 68 | 4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font 69 | Software shall not be used to promote, endorse or advertise any 70 | Modified Version, except to acknowledge the contribution(s) of the 71 | Copyright Holder(s) and the Author(s) or with their explicit written 72 | permission. 73 | 74 | 5) The Font Software, modified or unmodified, in part or in whole, 75 | must be distributed entirely under this license, and must not be 76 | distributed under any other license. The requirement for fonts to 77 | remain under this license does not apply to any document created 78 | using the Font Software. 79 | 80 | TERMINATION 81 | This license becomes null and void if any of the above conditions are 82 | not met. 83 | 84 | DISCLAIMER 85 | THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 86 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF 87 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT 88 | OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE 89 | COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 90 | INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL 91 | DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 92 | FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM 93 | OTHER DEALINGS IN THE FONT SOFTWARE. 94 | -------------------------------------------------------------------------------- /static/fonts/fa-brands-400.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TuomoKu/SPX-GC/9ad2d01e4d2af1390ff67109f71f9c2f444200fb/static/fonts/fa-brands-400.ttf -------------------------------------------------------------------------------- /static/fonts/fa-regular-400.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TuomoKu/SPX-GC/9ad2d01e4d2af1390ff67109f71f9c2f444200fb/static/fonts/fa-regular-400.ttf -------------------------------------------------------------------------------- /static/fonts/fa-solid-900.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TuomoKu/SPX-GC/9ad2d01e4d2af1390ff67109f71f9c2f444200fb/static/fonts/fa-solid-900.ttf -------------------------------------------------------------------------------- /static/img/LoopicSPXScreenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TuomoKu/SPX-GC/9ad2d01e4d2af1390ff67109f71f9c2f444200fb/static/img/LoopicSPXScreenshot.png -------------------------------------------------------------------------------- /static/img/SPX_glossy_255_Transparent_Shadow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TuomoKu/SPX-GC/9ad2d01e4d2af1390ff67109f71f9c2f444200fb/static/img/SPX_glossy_255_Transparent_Shadow.png -------------------------------------------------------------------------------- /static/img/SPXgraphicsLogo64px.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TuomoKu/SPX-GC/9ad2d01e4d2af1390ff67109f71f9c2f444200fb/static/img/SPXgraphicsLogo64px.png -------------------------------------------------------------------------------- /static/img/SPXstore600_2023_240px.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TuomoKu/SPX-GC/9ad2d01e4d2af1390ff67109f71f9c2f444200fb/static/img/SPXstore600_2023_240px.png -------------------------------------------------------------------------------- /static/img/checker60.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TuomoKu/SPX-GC/9ad2d01e4d2af1390ff67109f71f9c2f444200fb/static/img/checker60.png -------------------------------------------------------------------------------- /static/img/promo-converter-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TuomoKu/SPX-GC/9ad2d01e4d2af1390ff67109f71f9c2f444200fb/static/img/promo-converter-logo.png -------------------------------------------------------------------------------- /static/img/promo-loopic.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TuomoKu/SPX-GC/9ad2d01e4d2af1390ff67109f71f9c2f444200fb/static/img/promo-loopic.png -------------------------------------------------------------------------------- /static/img/promo-store.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TuomoKu/SPX-GC/9ad2d01e4d2af1390ff67109f71f9c2f444200fb/static/img/promo-store.png -------------------------------------------------------------------------------- /static/img/shadabot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TuomoKu/SPX-GC/9ad2d01e4d2af1390ff67109f71f9c2f444200fb/static/img/shadabot.png -------------------------------------------------------------------------------- /static/img/shadatop.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TuomoKu/SPX-GC/9ad2d01e4d2af1390ff67109f71f9c2f444200fb/static/img/shadatop.png -------------------------------------------------------------------------------- /static/img/snap-ferryman.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TuomoKu/SPX-GC/9ad2d01e4d2af1390ff67109f71f9c2f444200fb/static/img/snap-ferryman.png -------------------------------------------------------------------------------- /static/img/spx-gc-home-256.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TuomoKu/SPX-GC/9ad2d01e4d2af1390ff67109f71f9c2f444200fb/static/img/spx-gc-home-256.png -------------------------------------------------------------------------------- /static/img/spx-gc.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TuomoKu/SPX-GC/9ad2d01e4d2af1390ff67109f71f9c2f444200fb/static/img/spx-gc.ico -------------------------------------------------------------------------------- /static/img/spx-gc_org.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TuomoKu/SPX-GC/9ad2d01e4d2af1390ff67109f71f9c2f444200fb/static/img/spx-gc_org.ico -------------------------------------------------------------------------------- /static/img/spx.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 10 | 12 | 13 | 14 | 21 | 25 | 27 | 28 | 29 | 31 | 32 | -------------------------------------------------------------------------------- /static/img/spx_offline.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TuomoKu/SPX-GC/9ad2d01e4d2af1390ff67109f71f9c2f444200fb/static/img/spx_offline.png -------------------------------------------------------------------------------- /static/img/spx_online.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TuomoKu/SPX-GC/9ad2d01e4d2af1390ff67109f71f9c2f444200fb/static/img/spx_online.png -------------------------------------------------------------------------------- /static/img/spx_preview.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TuomoKu/SPX-GC/9ad2d01e4d2af1390ff67109f71f9c2f444200fb/static/img/spx_preview.png -------------------------------------------------------------------------------- /static/img/spx_program.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TuomoKu/SPX-GC/9ad2d01e4d2af1390ff67109f71f9c2f444200fb/static/img/spx_program.png -------------------------------------------------------------------------------- /static/img/storeicon-org.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TuomoKu/SPX-GC/9ad2d01e4d2af1390ff67109f71f9c2f444200fb/static/img/storeicon-org.png -------------------------------------------------------------------------------- /static/img/storeicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TuomoKu/SPX-GC/9ad2d01e4d2af1390ff67109f71f9c2f444200fb/static/img/storeicon.png -------------------------------------------------------------------------------- /static/img/yt_mockup.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TuomoKu/SPX-GC/9ad2d01e4d2af1390ff67109f71f9c2f444200fb/static/img/yt_mockup.png -------------------------------------------------------------------------------- /static/js/spx_dragdrop.js: -------------------------------------------------------------------------------- 1 | window.onload = function () { 2 | // GET ALL THE PLAYERS - DRAGGABLE AND DROP ZONES 3 | var draggable = document.getElementById("item0"); // was draggable 4 | var dropzones = document.getElementsByClassName("dropzone"); 5 | 6 | 7 | 8 | // DRAG START - HIGHLIGHT DROP ZONES WITH CSS CLASS 9 | //console.log(draggable); 10 | if (draggable != null) { 11 | draggable.addEventListener("dragstart", function () { 12 | for (let i = 0; i < dropzones.length; i++) { 13 | dropzones[i].classList.add("active"); 14 | } 15 | }); 16 | 17 | // DRAG END - REMOVE ALL ADDED ACTIVE & OVER CSS CLASS 18 | draggable.addEventListener("dragend", function () { 19 | for (let i = 0; i < dropzones.length; i++) { 20 | dropzones[i].classList.remove("active"); 21 | dropzones[i].classList.remove("over"); 22 | } 23 | }); 24 | 25 | // DRAG - AS YOU ARE DRAGGING 26 | draggable.addEventListener("drag", function () { 27 | // DO SOMETHING... IF YOU WANT 28 | }); 29 | 30 | 31 | } 32 | 33 | 34 | for (let i = 0; i < dropzones.length; i++) { 35 | // DRAG ENTER - HIGHLIGHT THIS ZONE 36 | dropzones[i].addEventListener("dragenter", function () { 37 | dropzones[i].classList.add("over"); 38 | }); 39 | 40 | // DRAG LEAVE - REMOVE HIGHLIGHT ON THIS ZONE 41 | dropzones[i].addEventListener("dragleave", function () { 42 | dropzones[i].classList.remove("over"); 43 | }); 44 | 45 | // DRAG OVER - PREVENT THE DEFAULT "DROP", SO WE CAN DO OUR OWN 46 | dropzones[i].addEventListener("dragover", function (evt) { 47 | evt.preventDefault(); 48 | }); 49 | 50 | // ON DROP - MOVE THE DRAGGABLE ELEMENT 51 | dropzones[i].addEventListener("drop", function (evt) { 52 | evt.preventDefault(); 53 | // Will move the draggable element only if dropped into a different box 54 | if (evt.target != draggable.parentNode && evt.target != draggable) { 55 | draggable.parentNode.removeChild(draggable); 56 | evt.target.appendChild(draggable); 57 | } 58 | }); 59 | } 60 | }; -------------------------------------------------------------------------------- /static/js/spx_fileBrowserItemFactory.js: -------------------------------------------------------------------------------- 1 | 2 | // This facory is used by source modules to generate DOM objects. 3 | 4 | 5 | function makeImageItem(spxData) { 6 | 7 | let serverUrl = spxData.base; 8 | let remotePath = spxData.path.replace('//',''); 9 | let imageUrl = serverUrl + '/' + remotePath + '/' + spxData.file; 10 | 11 | console.log('ImageURL', imageUrl); 12 | 13 | let html = '' 14 | html += '
\n'; 15 | html += '
' + spxData.file + '
\n'; 16 | html += '
\n'; 17 | return html 18 | } // makeImageItem 19 | 20 | 21 | function makeFolderItem(spxData) { 22 | let html = '' 23 | html = ''; 24 | return html 25 | } // makeFolderItem 26 | 27 | 28 | class ImageItem extends HTMLElement { 29 | set spxData(spxData) { 30 | this.setAttribute('onclick',"focusItem(this);"); 31 | this.innerHTML = makeImageItem(spxData) 32 | } 33 | } 34 | 35 | class FolderItem extends HTMLElement { 36 | // let targetPath = spxData.curr + '/' + ; 37 | set spxData(spxData) { 38 | let currentFolder = e('navipath').innerText; 39 | 40 | if (currentFolder =='/') {currentFolder = ''}; // strip just "/" 41 | 42 | this.setAttribute('ondblclick',"navigateTo('" + currentFolder + '/' + spxData.fold + "')"); 43 | this.innerHTML = makeFolderItem(spxData) 44 | } 45 | } 46 | 47 | customElements.define('image-item', ImageItem) 48 | customElements.define('folder-item', FolderItem) 49 | 50 | export { ImageItem, FolderItem } 51 | 52 | -------------------------------------------------------------------------------- /static/js/spx_fileBrowserModule.js: -------------------------------------------------------------------------------- 1 | 2 | // ================================================================ 3 | // Office Hours module for SPX Imagelist 4 | // ================================================================ 5 | // (c) 2022 SPX Graphics 6 | // 7 | // Change history: 8 | // 9 | // 04.03.2022 Original (refactored from one of social's) 10 | // 11 | // ================================================================ 12 | 13 | import * as spxFactory from "/js/spx_fileBrowserItemFactory.js"; 14 | var json; 15 | 16 | async function fetchData(url) { 17 | showMessageSlider('Loading images...', 'happy', true) 18 | e('folderinbox').innerHTML=''; 19 | e('fileinbox').innerHTML=''; 20 | try { 21 | console.log('source: ' + url ); 22 | 23 | axios.post('/api/browseFiles', { 24 | curFolder: fromFolder, 25 | tgtFolder: toFolder, 26 | rootFolder: rootFolder, 27 | extension: 'CSV' 28 | }) 29 | .then(function (response) { 30 | // console.log(response); 31 | RenderFolder(response.data); // controllerImportCSV 32 | 33 | }) 34 | .catch(function (error) { 35 | console.log(error); 36 | }); 37 | 38 | 39 | const res = await fetch(url); 40 | const txt = await res.text(); 41 | json = JSON.parse(txt); 42 | 43 | 44 | console.log('json', json); 45 | 46 | // if (json.code && json.code!=200) { 47 | // console.warn('Error occurred while SPX Social reading data source. Return code: ' + json.code + '.', json.errors); 48 | // } 49 | 50 | // if (!json || !json.articles) { 51 | // console.error('No data in "'+projName+'". Verify Flocker SiteID [' + siteID + '] and SectionID [' + sectID + ']. See SPX Social README file for troubleshooting instructions.'); 52 | // showMessageSlider('No data, verify source configuration. (See console for details)', 'error', true) // last param is "persist" 53 | // return; 54 | // } 55 | 56 | json.folders.forEach(itemData => { 57 | const el = document.createElement('folder-item'); 58 | let spxData = { 59 | base: json.baseUrl, 60 | curr: json.currentPath, 61 | fold: itemData 62 | } 63 | el.spxData = spxData; 64 | e('folderinbox').appendChild(el); 65 | }); 66 | 67 | console.log('files', json.files); 68 | 69 | json.files.forEach(itemData => { 70 | const el = document.createElement('image-item'); 71 | let spxData = { 72 | base: json.baseUrl, 73 | path: json.currentPath, 74 | file: itemData, 75 | fullImageUrl: 'http://visitools.co.uk/OHGraphics/graphics/ATEM/ATEM-Mini-Extreme-ISO-Rear.png' 76 | } 77 | el.spxData = spxData; 78 | e('fileinbox').appendChild(el); 79 | }); 80 | 81 | hideMessageSlider() 82 | } catch (error) { 83 | showMessageSlider('Error while loading, see console.', 'error', false) 84 | console.error('SPX Itemlist error in OfficeHours module.', error); 85 | } 86 | 87 | } // fetchFlocklerData 88 | 89 | export { fetchData } -------------------------------------------------------------------------------- /static/js/spx_rendererUtils.js: -------------------------------------------------------------------------------- 1 | 2 | // This script runs when the SPX Renderer URL is loaded 3 | // and checks for a runScript parameter in the URL and 4 | // if found loads the script and executes it. 5 | 6 | onload = function() { 7 | // console.log('spx_rendererUtils.js onload'); 8 | let docParams = new URLSearchParams(document.location.search); 9 | let parParams = new URLSearchParams(parent.location.search); 10 | let topParams = new URLSearchParams(top.location.search); 11 | let curParams = null; 12 | 13 | if (docParams.size>0) { 14 | curParams = docParams; 15 | } else if (parParams.size>0) { 16 | curParams = parParams; 17 | } else if (topParams.size>0) { 18 | curParams = topParams; 19 | } 20 | 21 | if (curParams && curParams.has('runScript')) { 22 | let jsFile = '/ExtraFunctions/' + curParams.get('runScript') + '.js'; 23 | console.log('SPX rendererUtils ->', jsFile , '-> init()'); 24 | import(jsFile) 25 | .then(module => { 26 | module.init(); 27 | }) 28 | .catch(err => { 29 | console.error('SPX Error - loading init script required by the SPX Renderer URL failed: ' + jsFile); 30 | }) 31 | ; 32 | } 33 | } -------------------------------------------------------------------------------- /static/js/vanilla-js-tooltip.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @fileOverview 3 | * @author Zoltan Toth 4 | * @version 0.1 5 | */ 6 | 7 | /** 8 | * @description 9 | * Vanilla Javascript tooltip. 10 | * 11 | * @class 12 | * @param {string} [options.theme=dark] - Selects one of the pre-defined tooltip styles - light or dark. 13 | * @param {number} [options.dist=10] - Specifies the distance in pixels from trigger to tooltip. 14 | * @param {number} [options.delay=0] - Specifies how long the tooltip remains visible after the mouse leaves the trigger. 15 | */ 16 | 17 | Tooltip = function() { 18 | 19 | var theme = "dak"; 20 | var delay = 50; 21 | var dist = 15; 22 | 23 | document.querySelectorAll('[data-tooltip').forEach((item,index) => { 24 | item.addEventListener("mouseover", function(e) { 25 | var tooltip = document.createElement("div"); 26 | tooltip.className = "b-tooltip"; 27 | tooltip.innerHTML = e.target.getAttribute('data-tooltip'); 28 | document.body.appendChild(tooltip); 29 | var pos = e.target.getAttribute('data-position') || "center top", 30 | posHorizontal = pos.split(" ")[0]; 31 | posVertical = pos.split(" ")[1]; 32 | positionAt(e.target, tooltip, posHorizontal, posVertical); 33 | }); 34 | }); 35 | 36 | document.body.addEventListener("mouseout", function(e) { 37 | if (e.target.hasAttribute('data-tooltip')) { 38 | setTimeout(function() { 39 | document.body.removeChild(document.querySelector(".b-tooltip")); 40 | }, delay); 41 | } 42 | }); 43 | 44 | /** 45 | * Positions the tooltip. 46 | * 47 | * @param {object} parent - The trigger of the tooltip. 48 | * @param {object} tooltip - The tooltip itself. 49 | * @param {string} posHorizontal - Desired horizontal position of the tooltip relatively to the trigger (left/center/right) 50 | * @param {string} posVertical - Desired vertical position of the tooltip relatively to the trigger (top/center/bottom) 51 | * 52 | */ 53 | function positionAt(parent, tooltip, posHorizontal, posVertical) { 54 | var parentCoords = parent.getBoundingClientRect(), left, top; 55 | 56 | switch (posHorizontal) { 57 | case "left": 58 | left = parseInt(parentCoords.left) - dist - tooltip.offsetWidth; 59 | if (parseInt(parentCoords.left) - tooltip.offsetWidth < 0) { 60 | left = dist; 61 | } 62 | break; 63 | 64 | case "right": 65 | left = parentCoords.right + dist; 66 | if (parseInt(parentCoords.right) + tooltip.offsetWidth > document.documentElement.clientWidth) { 67 | left = document.documentElement.clientWidth - tooltip.offsetWidth - dist; 68 | } 69 | break; 70 | 71 | default: 72 | case "center": 73 | left = parseInt(parentCoords.left) + ((parent.offsetWidth - tooltip.offsetWidth) / 2); 74 | } 75 | 76 | switch (posVertical) { 77 | case "center": 78 | top = (parseInt(parentCoords.top) + parseInt(parentCoords.bottom)) / 2 - tooltip.offsetHeight / 2; 79 | break; 80 | 81 | case "bottom": 82 | top = parseInt(parentCoords.bottom) + dist; 83 | break; 84 | 85 | default: 86 | case "top": 87 | top = parseInt(parentCoords.top) - tooltip.offsetHeight - dist; 88 | } 89 | 90 | left = (left < 0) ? parseInt(parentCoords.left) : left; 91 | top = (top < 0) ? parseInt(parentCoords.bottom) + dist : top; 92 | 93 | tooltip.style.left = left + "px"; 94 | tooltip.style.top = top + pageYOffset + "px"; 95 | } 96 | }; 97 | -------------------------------------------------------------------------------- /static/this-is-public-root: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TuomoKu/SPX-GC/9ad2d01e4d2af1390ff67109f71f9c2f444200fb/static/this-is-public-root -------------------------------------------------------------------------------- /utils/GDDtoSPXconverter.js: -------------------------------------------------------------------------------- 1 | 2 | // Utility for converting GDD TemplateDefinition to SPX TemplateDefinition 3 | // TODO: Converter is not implemented yet, just a dummy function 4 | 5 | module.exports = { 6 | 7 | convertGddtoSpx: function (gddDef) { 8 | 9 | let spxDef = {}; 10 | try { 11 | console.log('converting GDD to SPXGCTemplateDefinition!'); 12 | spxDef.description = gddDef.description; 13 | spxDef.playserver = "OVERLAY", 14 | spxDef.playchannel = "1", 15 | spxDef.playlayer = "14", 16 | spxDef.webplayout = "14", 17 | spxDef.steps = "999", 18 | spxDef.out = "manual", 19 | spxDef.uicolor = "3", 20 | spxDef.dataformat = "json", 21 | 22 | spxDef.DataFields = []; 23 | spxDef.DataFields.push( 24 | { 25 | ftype: "instruction", 26 | value: "Converted template", 27 | } 28 | ); 29 | 30 | 31 | return spxDef; 32 | } catch (error) { 33 | logger.error('convertGddtoSpx: ' + error); 34 | }; 35 | 36 | } // convertGddtoSpx 37 | } // end of module.exports -------------------------------------------------------------------------------- /utils/api-handlers.js: -------------------------------------------------------------------------------- 1 | const spx = require('./spx_server_functions.js'); 2 | const PlayoutCCG = require('./playout_casparCG.js'); 3 | 4 | // Experimental. Not in use yet. 5 | 6 | 7 | module.exports = { 8 | 9 | panic: function () { 10 | try { 11 | io.emit('SPXMessage2Client', {spxcmd: 'clearAllLayers'}); // clear webrenderers 12 | io.emit('SPXMessage2Controller', {APIcmd:'RundownAllStatesToStopped'}); // stop UI and save stopped values to rundown 13 | if (spx.CCGServersConfigured){ 14 | PlayoutCCG.clearChannelsFromGCServer() // server is optional, so doing ALL!!!!! 15 | } 16 | console.log('PANIC HANDLER'); 17 | return true 18 | } catch (error) { 19 | console.log('Panic error' + error); 20 | return false 21 | } 22 | }, 23 | 24 | } // end of exports -------------------------------------------------------------------------------- /utils/logger.js: -------------------------------------------------------------------------------- 1 | // Import modules 2 | const path = require('path') 3 | const { createLogger, format, transports } = require('winston'); 4 | const { combine, timestamp, label, printf } = format; 5 | 6 | const myFormat = printf(({ level, message, label, timestamp }) => { 7 | return `${timestamp} [${label}] ${level}: ${message}`; 8 | }); 9 | 10 | 11 | // logger.error("hello world!, this is error 0"); 12 | // logger.warn("hello world!, this is warn 1"); 13 | // logger.info("hello world!, this is info 2"); 14 | // logger.verbose("hello world!, this is verbose 3"); 15 | // logger.debug("hello world!, this is debug 4"); 16 | // logger.silly("hello world!, this is silly 5"); 17 | 18 | let rootPath 19 | if ( process.pkg ) { 20 | // PKG process 21 | rootPath = path.resolve(process.execPath + '/..'); 22 | } else { 23 | // NODE process 24 | rootPath = process.cwd(); 25 | } 26 | 27 | let LOGLEVEL = config.general.loglevel || 'debug'; 28 | let LOGFOLDER = config.general.logfolder || rootPath + '/LOG'; 29 | var logFile = path.resolve(LOGFOLDER, 'access.log'); 30 | 31 | const logger = createLogger({ 32 | format: combine( 33 | label({ label: 'SPX' }), 34 | timestamp(), 35 | myFormat 36 | ), 37 | transports: [ 38 | new transports.Console({ 39 | level: LOGLEVEL 40 | }), 41 | new transports.File({ 42 | level: LOGLEVEL, 43 | filename: logFile 44 | }) 45 | ] 46 | }); 47 | 48 | // Export the logger 49 | module.exports = logger; -------------------------------------------------------------------------------- /utils/playout_webplayer.js: -------------------------------------------------------------------------------- 1 | 2 | // ================== functions alphabetical order ================================================== 3 | 4 | const logger = require('./logger.js'); 5 | const fs = require('fs'); 6 | const path = require('path') 7 | const moment = require('moment'); 8 | 9 | 10 | module.exports = { 11 | 12 | 13 | webPlayoutController: function (data){ 14 | // We pass the data object to be processed by the web renderer 15 | // First dataformat sanity check. 16 | // THIS FORMATS JSON AND SENDS IT FORWARDS in all cases: play, stop, update... 17 | 18 | 19 | 20 | let TEMPLATEDATA = []; 21 | //console.log('Format [' + DataType + '] data.fields coming in before emit', data.fields); 22 | if (data.fields && data.fields.length > 0) { 23 | data.fields.forEach((item,index) => { 24 | let tempObj={}; 25 | tempObj[item.field] = item.value; 26 | TEMPLATEDATA.push(tempObj); 27 | }); 28 | data.fields=TEMPLATEDATA; 29 | } 30 | // console.log('webPlayoutController data.fields going out for emit', data); 31 | io.emit('SPXMessage2Client', data); 32 | } 33 | 34 | 35 | } // end of exports PlayoutWEB. -------------------------------------------------------------------------------- /utils/sockets.js: -------------------------------------------------------------------------------- 1 | module.exports = function (io) { 2 | 3 | io.sockets.on('connection', function (socket) { 4 | 5 | console.log('*** Socket connection (' + socket.id + ") Connections: " + io.engine.clientsCount); 6 | clients[socket.id] = socket; 7 | // send stuff out 8 | data = [{ color: '#FFFF00' }, { color: '#FF00FF' }]; 9 | socket.broadcast.emit('ServerIndicatorUpdate', data); 10 | 11 | 12 | socket.on('IncomingNamedCall', spxMessage); 13 | function spxMessage(data) { 14 | // this data was received via socket from client! 15 | console.log(data); 16 | 17 | // send stuff out 18 | data = [{ color: '#FFFF00' }, { color: '#FF00FF' }]; 19 | socket.broadcast.emit('ServerIndicatorUpdate', data); 20 | }; 21 | 22 | socket.on('disconnect', function () { 23 | console.log('*** Socket disconnected (' + socket.id + ") Connections: " + io.engine.clientsCount); 24 | delete clients[socket.id]; 25 | }); 26 | }); 27 | }; -------------------------------------------------------------------------------- /utils/spx_auth.js: -------------------------------------------------------------------------------- 1 | 2 | const logger = require('./logger.js'); 3 | const spx = require('../utils/spx_server_functions.js'); 4 | 5 | 6 | async function validateIfApiKey(req) { 7 | // Added in 1.3.2 8 | // TODO: Test carefully and write documentation 9 | // about user/pass/apikey, for devs also! 10 | // console.log("validateIfApiKey", req.method, req.body, req.query); 11 | let message = ''; 12 | if (config.general.username && !config.general.apikey) { 13 | message = 'Warning! When username is configured in SPX, apikey is required then also.' 14 | logger.warn(message); 15 | return [false, message]; 16 | } 17 | 18 | 19 | if (!config.general.apikey || config.general.apikey=='') { 20 | message = 'No api key SPX in config, allow all.' 21 | logger.verbose(message); 22 | return [true, message]; 23 | } 24 | 25 | let KEY = req.method=='POST' ? (req.body?.apikey || null) : (req.query?.apikey || null); 26 | if ( !KEY ) { 27 | message = 'API key configured in SPX, but apikey missing from the ' + req.method + ' API request. Access denied.' 28 | logger.warn(message); 29 | return [false, message]; 30 | } 31 | 32 | if ( KEY==config.general.apikey) { 33 | message = 'API key matches. Access allowed.' 34 | logger.verbose(message); 35 | return [true, message]; 36 | } else { 37 | message = 'API key does not match. Access denied.' 38 | logger.warn(message); 39 | return [false, message]; 40 | } 41 | 42 | } // validateIfApiKey utility 43 | 44 | 45 | async function CheckAPIKey(req,res,next) { 46 | // Improved in 1.3.2 47 | // Middleware to check API key in each API endpoint 48 | var apiChecked = await validateIfApiKey(req); 49 | 50 | // console.log("Api Checked with in CheckAPIKey", apiChecked); 51 | 52 | if ( apiChecked[0]===true ) { 53 | // console.log("CheckAPIKey YEAH"); 54 | next(); 55 | return true; 56 | } else { 57 | // console.log("CheckAPIKey NOPE"); 58 | let dataOut = {}; 59 | dataOut.error = apiChecked[1] 60 | res.status(200).json(dataOut); 61 | return false; 62 | } 63 | } // CheckAPIKey 64 | 65 | 66 | async function CheckLogin(req,res,next) { 67 | // Middleware to check auth in each router. 68 | // require ..... username 69 | // returns ..... true / false 70 | 71 | let USER = req.body.username || ''; 72 | let PASS = req.body.password || ''; 73 | let AllowedUser = config.general.username || ''; 74 | 75 | // let apiChecked = await validateIfApiKey(req); 76 | // if ( apiChecked[0]===true ) { 77 | // next(); 78 | // return; 79 | // } else { 80 | // return; 81 | // } 82 | 83 | // See if auth is used (if user in config is present) 84 | if (!AllowedUser){ 85 | logger.verbose('CheckLogin: No username in config, authorize "default" user...'); 86 | req.session.user = 'default'; 87 | next(); 88 | return; 89 | } 90 | 91 | // See if only user is present in config: ask for auth policy 92 | if (AllowedUser && !config.general.password || AllowedUser && config.general.password==''){ 93 | logger.verbose('CheckLogin: No password in config, prompt for auth policy...'); 94 | res.render('view-authpolicy', { layout: false, user: AllowedUser}); 95 | return; 96 | } 97 | 98 | 99 | if (req.session.user && req.session.user==AllowedUser) { 100 | // correct user in session 101 | logger.verbose('CheckLogin: User "' + req.session.user + '" authorized ok.'); 102 | next(); 103 | return; 104 | } 105 | 106 | if (USER==AllowedUser) { 107 | if (spx.hashcompare(PASS,config.general.password)) { 108 | logger.info('CheckLogin: User "' + USER + '" logged in.'); 109 | req.session.user = USER; 110 | next(); 111 | return; 112 | } 113 | } 114 | logger.verbose('CheckLogin: Not authenticated (or wrong user/pass), redirect to login'); 115 | res.redirect('/login'); 116 | // res.status(403); 117 | // res.render('view-login', { layout: false }); 118 | } // CheckLogin 119 | 120 | 121 | function Logout(req,res,next) { 122 | // logout user 123 | logger.info('CheckLogin: User "' + req.session.user + '" logged out.'); 124 | req.session.user = ''; 125 | res.redirect('/'); 126 | } 127 | 128 | module.exports = { 129 | CheckAPIKey, 130 | CheckLogin, 131 | Logout 132 | } 133 | 134 | 135 | -------------------------------------------------------------------------------- /utils/talk.vbs: -------------------------------------------------------------------------------- 1 | 2 | ' Tiny Text to Speech (only works on Windows)! 3 | ' (c) 2020 tuomo@smartpx.fi 4 | ' 5 | ' Commandline usage: 6 | ' wscript talk.vbs Hello world! 7 | 8 | sub Talk(message) 9 | Dim sapi 10 | Set sapi=CreateObject("sapi.spvoice") 11 | sapi.Speak message 12 | end sub 13 | 14 | If WScript.Arguments.Count > 0 Then 15 | dim message 16 | for x=0 to WScript.Arguments.Count-1 17 | message = message & " " & WScript.Arguments.Item(x) 18 | next 19 | Talk(message) 20 | End If 21 | 22 | -------------------------------------------------------------------------------- /views/_old/view-renderwindow_preview.handlebars: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | SPX - PREVIEW 8 | 9 | 10 | 37 | 38 | 39 | 40 | 41 | 48 | 49 | 50 |
51 | 55 |
56 | 57 | 92 | 93 | 94 | -------------------------------------------------------------------------------- /views/_old/view-renderwindow_program.handlebars: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | SPX - PROGRAM EMULATOR 8 | 9 | 24 | 25 | 52 | 53 | 54 | 61 | 62 |
63 |
64 | 69 |
70 | 71 | 101 | 102 | 103 | -------------------------------------------------------------------------------- /views/layouts/__main.handlebars: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Example App 7 | 8 | 56 | 57 | 58 | 59 | 60 | 61 | {{>navi}} 62 | 63 | 64 | {{{body}}} 65 | 66 | 67 | 68 | -------------------------------------------------------------------------------- /views/layouts/filelist.handlebars: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Choose a file 8 | 158 | 159 | 160 | 161 | 162 | 163 |
164 | 165 |

166 | 167 | {{{body}}} 168 | 169 |
170 | 171 | 172 | -------------------------------------------------------------------------------- /views/layouts/main.handlebars: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TuomoKu/SPX-GC/9ad2d01e4d2af1390ff67109f71f9c2f444200fb/views/layouts/main.handlebars -------------------------------------------------------------------------------- /views/partials/copyright.handlebars: -------------------------------------------------------------------------------- 1 | 15 | -------------------------------------------------------------------------------- /views/partials/footer.handlebars: -------------------------------------------------------------------------------- 1 | 2 | 3 | 58 | 59 | 60 | -------------------------------------------------------------------------------- /views/partials/partial-genericfilebrowsermodal.handlebars: -------------------------------------------------------------------------------- 1 | 2 |
3 | 49 |
-------------------------------------------------------------------------------- /views/partials/partial-showextrabrowsermodal.handlebars: -------------------------------------------------------------------------------- 1 | 2 |
3 | 38 |
-------------------------------------------------------------------------------- /views/partials/rundownComment copy.handlebars: -------------------------------------------------------------------------------- 1 |
5 | 6 |
7 | 8 |
9 | 10 | 11 |
12 |
Rundown comment
13 |
14 | 15 | 16 | 66 |
67 |
68 |
69 | 70 | 71 | -------------------------------------------------------------------------------- /views/partials/rundownOptions.handlebars: -------------------------------------------------------------------------------- 1 |
2 |
3 | 4 | 5 | 6 | 9 | 12 | 13 | 14 | 20 | 28 | 29 | 47 |
7 | Comment 8 | 10 | 11 |
15 | Light mode 19 | 21 | Light mode is an experimental, simplified version of the rundown, with less features 22 | and it can be used for example on mobile devices or as a custom dock in OBS.
23 | 27 |
48 | 49 |
50 |
51 | -------------------------------------------------------------------------------- /views/partials/socketserver.handlebars: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /views/view-admin.handlebars: -------------------------------------------------------------------------------- 1 | 2 | {{>copyright }} 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | SPX {{SPX.title}} | {{lang 'pagetitle.admin'}} 11 | 12 | 13 | 14 | 15 | 16 | 17 | 35 | 36 | 37 | 38 | 39 | 40 |
41 | 80 |
81 | 82 | -------------------------------------------------------------------------------- /views/view-api-osc.handlebars: -------------------------------------------------------------------------------- 1 | 2 | {{>copyright }} 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | SPX - OSC interface 11 | 12 | 13 | 14 | 91 | 92 | 93 | 110 | 111 | 112 | 113 | 114 | 115 | 116 |

SPX Graphics / OSC interface

117 | SPX Host-ID: {{{getHostID}}} 118 |
119 |
120 | Supported osc commands for external systems to trigger SPX over OSC port (configured in config.json)
121 | Since graphics can have a lot of properties, the use of REST API is highly recommended. 122 |

123 | Note! OSC is not implemented yet. Please keep osc.enable:false in config!
124 |

125 | 126 | {{#each functionList.sections}} 127 |
128 |

{{section}}

129 |

{{info}}

130 | 131 | {{#each commands}} 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | {{#if code}} 140 | 141 | 144 | 145 | {{/if}} 146 | 147 | 148 | 149 | 150 | {{/each}} 151 |
{{vers}}{{info}}
142 |
{{myStringify code}}
143 |
 
152 | {{/each}} 153 | 154 |
155 |
156 | If you have needs for additional integrations or graphics, please contact us. 157 | 158 | 159 | -------------------------------------------------------------------------------- /views/view-api-v1.handlebars: -------------------------------------------------------------------------------- 1 | 2 | {{>copyright }} 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | SPX API v1 11 | 12 | 13 | 14 | 100 | 101 | 102 | 119 | 120 | 121 | 122 | 123 | 124 | 125 |

SPX Graphics / API v1

126 | SPX Host-ID: {{{getHostID}}} 127 |
128 |
129 | http/get commands available for external systems to trigger SPX events. See also osc. 130 |
131 | SPX will need to be open in a browser (in the same network as the server) for most of these to work. 132 |

133 | API connections can be restricted with an apikey parameter set in the config file and in the URL ?apikey=1234567890 134 |

135 | Remember, SPX is primarily targeted to live, manually operated 136 | workflows and API allows only limited modifications of template data or other advanced procedures. 137 |

138 | Use "localhost" addresses in examples 139 |

140 | 141 | {{#each functionList.sections}} 142 |
143 |

{{section}}

144 |

{{info}}

145 | 146 | {{#each commands}} 147 | 148 | {{#ifValue method "POST"}} 149 | 150 | {{else}} 151 | 152 | {{/ifValue}} 153 | 154 | 155 | 156 | 157 | 158 | 159 | {{#if code}} 160 | 161 | 164 | 165 | {{/if}} 166 | 167 | 168 | 169 | 170 | {{/each}} 171 |
{{method}}{{vers}}{{{info}}}
162 |
{{myStringify code}}
163 |
 
172 | {{/each}} 173 | 174 |
175 |
176 | If you have needs for additional integrations or graphics, please contact us. 177 | 178 | 179 | -------------------------------------------------------------------------------- /views/view-authpolicy.handlebars: -------------------------------------------------------------------------------- 1 | 2 | {{>copyright }} 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | SPX {{SPX.title}} | {{lang 'pagetitle.authpolicy'}} 11 | 12 | 13 | 14 | 15 | 16 | 17 | 35 | 36 | 54 | 55 | 56 | 57 | 58 | 59 | 60 |
61 |
62 |
63 | 126 |
127 |
128 |
129 | 130 | -------------------------------------------------------------------------------- /views/view-empty.handlebars: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Empty 8 | 18 | 19 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | -------------------------------------------------------------------------------- /views/view-episodes.handlebars: -------------------------------------------------------------------------------- 1 | 2 | {{>copyright }} 3 | 4 | 5 | 6 | 7 | 8 | 9 | SPX{{SPX.title}} | {{lang 'pagetitle.select'}} 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 |
59 |
60 | {{>header page="episodes"}} 61 |
62 | 63 | 64 |
65 |
66 | 67 | 68 |
69 | 70 | {{#if message}} 71 |
{{message}}
72 | {{/if}} 73 | 74 | {{#if error}} 75 |
{{error}}
76 | {{/if}} 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 |
{{lang 'headline.selectfile'}} 
85 |
86 | 87 | 88 | 89 | 99 | 100 |
90 |
98 |
101 | 102 |
103 | 104 | {{#if files}} 105 | 106 | {{else}} 107 |
{{lang 'headline.createrundowntip'}}
108 | {{/if}} 109 | 110 | 111 | 112 | 113 | 114 | 120 | 124 | 125 |
115 | 116 | 117 | 118 | 119 | 121 | 122 | 123 |
126 | 127 | 128 |
129 |
130 |
131 | 132 | 133 | {{>footer}} 134 |
135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 149 | 150 | -------------------------------------------------------------------------------- /views/view-fileBrowser.handlebars: -------------------------------------------------------------------------------- 1 | 2 | {{>copyright }} 3 | 4 | 5 | 6 | 7 | 8 | Filebrowser 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 |
31 |
32 |
33 |
34 | 37 |
38 |
    39 |
  • ROOT
  • 40 |
  • UP
  • 41 |
42 |
43 |
44 | 45 |
46 |
47 | 48 |
49 |
50 |
51 |
52 |
53 | 73 | 74 | 75 | 76 | 77 | -------------------------------------------------------------------------------- /views/view-login.handlebars: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | {{>copyright }} 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | SPX {{SPX.title}} | {{lang 'pagetitle.login'}} 14 | 15 | 16 | 17 | 18 | 19 | 20 | 40 | 41 | 42 | 43 | 44 | 45 |
46 |
47 |
48 | 87 |
88 |
89 |
90 | 107 | -------------------------------------------------------------------------------- /views/view-shows.handlebars: -------------------------------------------------------------------------------- 1 | 2 | {{>copyright }} 3 | 4 | 5 | 6 | 7 | 8 | SPX {{SPX.title}} | {{lang 'pagetitle.selectshow'}} 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 |
22 |
23 | {{>header page="shows"}} 24 |
25 |
26 |
27 |
28 | {{#if message}} 29 |
{{message}}
30 | {{/if}} 31 | {{#if error}} 32 |
{{error}}
33 | {{/if}} 34 | 35 | 36 | 37 | 38 | 39 |
{{lang 'headline.select'}} 
40 |
41 | 42 | 43 | 60 | 61 | 62 | 79 | 80 |
44 | 49 | {{#ifGreater folders.length 5}} 50 | 51 | 58 | {{/ifGreater}} 59 |
63 |
78 |
81 |
82 |
83 | 84 | 85 | 86 |
87 |
88 |
89 |
90 | {{>footer}} 91 |
92 | 98 | 99 | --------------------------------------------------------------------------------