2019-05-21 04:06:20 +00:00
// this is our global websocket, used to communicate from/to Stream Deck software
// and some info about our plugin, as sent by Stream Deck software
var websocket = null ,
uuid = null ,
actionInfo = { } ,
settings = { } ,
2019-05-21 23:16:00 +00:00
globalSettings = { } ,
2019-05-21 04:06:20 +00:00
isQT = navigator . appVersion . includes ( 'QtWebEngine' ) ; // 'oninput'; // change this, if you want interactive elements act on any change, or while they're modified
2019-05-21 23:16:00 +00:00
const websiteAction = 'tf.meow.remote.website' ;
2019-05-21 04:06:20 +00:00
function connectSocket (
inPort ,
inUUID ,
inMessageType ,
inApplicationInfo ,
inActionInfo
) {
connectElgatoStreamDeckSocket (
inPort ,
inUUID ,
inMessageType ,
inApplicationInfo ,
inActionInfo
) ;
}
function connectElgatoStreamDeckSocket ( inPort , inUUID , inRegisterEvent , inInfo , inActionInfo ) {
uuid = inUUID ;
// please note: the incoming arguments are of type STRING, so
// in case of the inActionInfo, we must parse it into JSON first
actionInfo = JSON . parse ( inActionInfo ) ; // cache the info
inInfo = JSON . parse ( inInfo ) ;
websocket = new WebSocket ( 'ws://localhost:' + inPort ) ;
/ * * S i n c e t h e P I d o e s n ' t h a v e a c c e s s t o y o u r O S n a t i v e s e t t i n g s
* Stream Deck sends some color settings to PI
* We use these to adjust some styles ( e . g . highlight - colors for checkboxes )
* /
addDynamicStyles ( inInfo . colors , 'connectElgatoStreamDeckSocket' ) ;
/** let's see, if we have some settings */
settings = getPropFromString ( actionInfo , 'payload.settings' ) ;
console . log ( settings , actionInfo ) ;
initPropertyInspector ( 5 ) ;
// if connection was established, the websocket sends
// an 'onopen' event, where we need to register our PI
websocket . onopen = function ( ) {
var json = {
event : inRegisterEvent ,
uuid : inUUID
} ;
websocket . send ( JSON . stringify ( json ) ) ;
2019-05-21 23:16:00 +00:00
if ( isAction ( websiteAction ) ) {
getGlobalSettings ( ) ;
}
2019-05-21 04:06:20 +00:00
} ;
websocket . onmessage = function ( evt ) {
// Received message from Stream Deck
2019-05-21 23:16:00 +00:00
let jsonObj = JSON . parse ( evt . data ) ;
let event = jsonObj [ 'event' ] ;
console . log ( 'Got event' , event ) ;
switch ( event ) {
case 'didReceiveGlobalSettings' :
didReceiveGlobalSettings ( jsonObj ) ;
break ;
}
2019-05-21 04:06:20 +00:00
} ;
}
function initPropertyInspector ( initDelay ) {
2019-05-21 23:50:47 +00:00
const action = actionInfo [ 'action' ] ;
$ ( '[data-action=' + action + ']' ) . removeClass ( 'hidden' ) ;
2019-05-21 04:06:20 +00:00
Object . keys ( settings ) . forEach ( function ( item ) {
2019-05-21 23:16:00 +00:00
let $item = $ ( '#' + item ) ,
value = settings [ item ] ;
switch ( $item . attr ( 'type' ) ) {
case 'checkbox' :
let itemVal = $item . attr ( 'value' ) ;
if ( itemVal == 'false' || itemVal == 'true' ) {
itemVal = ( /^true$/i ) . test ( itemVal ) ;
}
if ( itemVal === value ) {
$item . prop ( 'checked' , true ) ;
}
break ;
default :
$item . val ( value ) ;
}
2019-05-21 04:06:20 +00:00
} ) ;
$ ( 'input' ) . each ( function ( ) {
2019-05-21 23:16:00 +00:00
let $this = $ ( this ) ,
2019-05-21 04:06:20 +00:00
id = $this . attr ( 'id' ) ;
let $item = $this . closest ( '.sdpi-item' ) ;
2019-05-21 23:16:00 +00:00
$this . on ( 'change' , function ( ) {
2019-05-21 04:06:20 +00:00
const type = $this . attr ( 'type' ) ;
let val = $this . val ( ) ;
switch ( type ) {
case 'checkbox' :
2019-05-21 23:16:00 +00:00
// If unchecked, unset the setting
if ( ! this . checked ) {
removeSetting ( id ) ;
return ;
}
2019-05-21 04:06:20 +00:00
if ( val == 'false' || val == 'true' ) {
val = ( /^true$/i ) . test ( val ) ;
}
break ;
2019-05-21 23:16:00 +00:00
case 'file' :
const info = $item . find ( '.sdpi-file-info' ) ;
if ( info ) {
const s = decodeURIComponent ( $this . val ( ) . replace ( /^C:\\fakepath\\/ , '' ) ) . split ( '/' ) . pop ( ) ;
info . text ( s . length > 28
? s . substr ( 0 , 10 )
+ '...'
+ s . substr ( s . length - 10 , s . length )
: s ) ;
}
break ;
2019-05-21 04:06:20 +00:00
}
updateSetting ( id , val ) ;
2019-05-21 23:16:00 +00:00
if ( isAction ( websiteAction ) && ( id == 'remote_host' || id == 'remote_token' ) ) {
updateGlobalSetting ( id , val ) ;
}
2019-05-21 04:06:20 +00:00
} ) ;
} ) ;
$ ( '#ssh_key' ) . change ( function ( e ) {
if ( e . target . files . length < 1 ) {
// Hide passphrase field
$ ( '#ssh_key_passphrase_container' ) . hide ( ) ;
$ ( '#ssh_password_container' ) . show ( ) ;
2019-05-21 23:16:00 +00:00
return ;
2019-05-21 04:06:20 +00:00
} else {
$ ( '#ssh_password_container' ) . hide ( ) ;
}
2019-05-21 23:16:00 +00:00
let f = e . target . files [ 0 ] ;
2019-05-21 04:06:20 +00:00
2019-05-21 23:16:00 +00:00
let reader = new FileReader ( ) ;
2019-05-21 04:06:20 +00:00
// Closure to capture the file information.
reader . onload = function ( e ) {
2019-05-21 23:16:00 +00:00
let result = e . target . result ;
2019-05-21 04:06:20 +00:00
2019-05-21 23:16:00 +00:00
let pki = forge . pki ;
2019-05-21 04:06:20 +00:00
try {
pki . privateKeyFromPem ( result ) ;
$ ( '#ssh_key_passphrase_container' ) . hide ( ) ;
} catch ( ex ) {
if ( ex . message . indexOf ( 'PEM is encrypted.' ) !== - 1 ) {
$ ( '#ssh_key_passphrase_container' ) . show ( ) ;
} else {
// Show error message?
}
}
} ;
// Read in the image file as a data URL.
reader . readAsText ( f ) ;
} ) ;
}
if ( ! isQT ) {
document . addEventListener ( 'DOMContentLoaded' , function ( ) {
initPropertyInspector ( 100 ) ;
} ) ;
}
/ * * S t r e a m D e c k s o f t w a r e p a s s e s s y s t e m - h i g h l i g h t c o l o r i n f o r m a t i o n
* to Property Inspector . Here we 'inject' the CSS styles into the DOM
* when we receive this information . * /
function addDynamicStyles ( clrs , fromWhere ) {
const node = document . getElementById ( '#sdpi-dynamic-styles' ) || document . createElement ( 'style' ) ;
if ( ! clrs . mouseDownColor ) clrs . mouseDownColor = fadeColor ( clrs . highlightColor , - 100 ) ;
const clr = clrs . highlightColor . slice ( 0 , 7 ) ;
const clr1 = fadeColor ( clr , 100 ) ;
const clr2 = fadeColor ( clr , 60 ) ;
const metersActiveColor = fadeColor ( clr , - 60 ) ;
node . setAttribute ( 'id' , 'sdpi-dynamic-styles' ) ;
node . innerHTML = `
input [ type = "radio" ] : checked + label span ,
input [ type = "checkbox" ] : checked + label span {
background - color : $ { clrs . highlightColor } ;
}
input [ type = "radio" ] : active : checked + label span ,
input [ type = "radio" ] : active + label span ,
input [ type = "checkbox" ] : active : checked + label span ,
input [ type = "checkbox" ] : active + label span {
background - color : $ { clrs . mouseDownColor } ;
}
input [ type = "radio" ] : active + label span ,
input [ type = "checkbox" ] : active + label span {
background - color : $ { clrs . buttonPressedBorderColor } ;
}
td . selected ,
td . selected : hover ,
li . selected : hover ,
li . selected {
color : white ;
background - color : $ { clrs . highlightColor } ;
}
. sdpi - file - label > label : active ,
. sdpi - file - label . file : active ,
label . sdpi - file - label : active ,
label . sdpi - file - info : active ,
input [ type = "file" ] : : - webkit - file - upload - button : active ,
button : active {
background - color : $ { clrs . buttonPressedBackgroundColor } ;
color : $ { clrs . buttonPressedTextColor } ;
border - color : $ { clrs . buttonPressedBorderColor } ;
}
: : - webkit - progress - value ,
meter : : - webkit - meter - optimum - value {
background : linear - gradient ( $ { clr2 } , $ { clr1 } 20 % , $ { clr } 45 % , $ { clr } 55 % , $ { clr2 } )
}
: : - webkit - progress - value : active ,
meter : : - webkit - meter - optimum - value : active {
background : linear - gradient ( $ { clr } , $ { clr2 } 20 % , $ { metersActiveColor } 45 % , $ { metersActiveColor } 55 % , $ { clr } )
}
` ;
document . body . appendChild ( node ) ;
} ;
/** UTILITIES */
/ * * g e t a J S O N p r o p e r t y f r o m a ( d o t - s e p a r a t e d ) s t r i n g
* Works on nested JSON , e . g . :
* jsn = {
* propA : 1 ,
* propB : 2 ,
* propC : {
* subA : 3 ,
* subB : {
* testA : 5 ,
* testB : 'Hello'
* }
* }
* }
* getPropFromString ( jsn , 'propC.subB.testB' ) will return 'Hello' ;
* /
const getPropFromString = ( jsn , str , sep = '.' ) => {
const arr = str . split ( sep ) ;
return arr . reduce ( ( obj , key ) =>
( obj && obj . hasOwnProperty ( key ) ) ? obj [ key ] : undefined , jsn ) ;
} ;
/ *
Quick utility to lighten or darken a color ( doesn ' t take color - drifting , etc . into account )
Usage :
fadeColor ( '#061261' , 100 ) ; // will lighten the color
fadeColor ( '#200867' ) , - 100 ) ; // will darken the color
* /
function fadeColor ( col , amt ) {
const min = Math . min , max = Math . max ;
const num = parseInt ( col . replace ( /#/g , '' ) , 16 ) ;
const r = min ( 255 , max ( ( num >> 16 ) + amt , 0 ) ) ;
const g = min ( 255 , max ( ( num & 0x0000FF ) + amt , 0 ) ) ;
const b = min ( 255 , max ( ( ( num >> 8 ) & 0x00FF ) + amt , 0 ) ) ;
return '#' + ( g | ( b << 8 ) | ( r << 16 ) ) . toString ( 16 ) . padStart ( 6 , 0 ) ;
}
function updateSetting ( setting , value ) {
if ( ! settings ) {
settings = { } ;
}
settings [ setting ] = value ;
setSettings ( settings ) ;
}
2019-05-21 23:16:00 +00:00
function removeSetting ( setting ) {
if ( ! settings ) {
settings = { } ;
}
delete settings [ setting ] ;
setSettings ( settings ) ;
}
2019-05-21 04:06:20 +00:00
function setSettings ( settings ) {
2019-05-21 23:16:00 +00:00
let json = {
2019-05-21 04:06:20 +00:00
"event" : "setSettings" ,
"context" : uuid ,
"payload" : settings
} ;
if ( websocket ) {
websocket . send ( JSON . stringify ( json ) ) ;
}
2019-05-21 23:16:00 +00:00
}
function updateGlobalSetting ( id , val ) {
globalSettings [ id ] = val ;
setGlobalSettings ( globalSettings ) ;
}
function getGlobalSettings ( ) {
let json = {
"event" : "getGlobalSettings" ,
"context" : uuid
} ;
if ( websocket ) {
websocket . send ( JSON . stringify ( json ) ) ;
}
}
function setGlobalSettings ( settings ) {
let json = {
"event" : "setGlobalSettings" ,
"context" : uuid ,
"payload" : settings
} ;
if ( websocket ) {
websocket . send ( JSON . stringify ( json ) ) ;
}
}
function didReceiveGlobalSettings ( obj ) {
globalSettings = getPropFromString ( obj , 'payload.settings' ) ;
// Load defaults for fields not set
Object . keys ( globalSettings ) . forEach ( function ( item ) {
if ( ! ( item in settings ) ) {
$ ( '#' + item ) . val ( globalSettings [ item ] ) ;
}
} ) ;
}
function isAction ( action ) {
return actionInfo [ 'action' ] === action ;
2019-05-21 04:06:20 +00:00
}