2018-05-27 01:29:02 +02:00
if ( Debug . debug ) {
2018-07-16 22:30:52 +02:00
console . log ( "Loading Comms.js" ) ;
2018-05-27 01:29:02 +02:00
}
2018-05-26 23:08:49 +02:00
class CommsClient {
2018-08-22 23:16:08 +02:00
constructor ( name , settings ) {
2018-07-16 22:30:52 +02:00
if ( BrowserDetect . firefox ) {
this . port = browser . runtime . connect ( { name : name } ) ;
} else if ( BrowserDetect . chrome ) {
this . port = chrome . runtime . connect ( { name : name } ) ;
2018-08-07 23:31:28 +02:00
} else if ( BrowserDetect . edge ) {
this . port = browser . runtime . connect ( { name : name } )
2018-07-16 22:30:52 +02:00
}
2018-05-26 23:08:49 +02:00
var ths = this ;
this . port . onMessage . addListener ( m => ths . processReceivedMessage ( m ) ) ;
2018-08-07 23:31:28 +02:00
2018-08-22 23:16:08 +02:00
this . settings = settings ;
2018-08-22 23:46:59 +02:00
this . pageInfo = undefined ;
2018-05-26 23:08:49 +02:00
}
setPageInfo ( pageInfo ) {
this . pageInfo = pageInfo ;
}
processReceivedMessage ( message ) {
if ( Debug . debug && Debug . comms ) {
console . log ( "[CommsClient.js::processMessage] Received message from background script!" , message ) ;
}
2018-08-23 01:04:37 +02:00
if ( ! this . pageInfo || this . settings . active ) {
2018-08-22 23:46:59 +02:00
if ( Debug . debug && Debug . comms ) {
console . log ( "[CommsClient.js::processMessage] this.pageInfo not defined. Extension is probably disabled for this site." ) ;
}
return ;
}
2018-07-15 16:22:32 +02:00
if ( message . cmd === "set-ar" ) {
2018-05-27 01:29:02 +02:00
this . pageInfo . setAr ( message . ratio ) ;
2018-07-15 16:22:32 +02:00
} else if ( message . cmd === 'set-video-float' ) {
2018-08-22 23:16:08 +02:00
this . settings . active . miscFullscreenSettings . videoFloat = message . newFloat ;
2018-07-15 16:22:32 +02:00
this . pageInfo . restoreAr ( ) ;
2018-05-26 23:08:49 +02:00
} else if ( message . cmd === "has-videos" ) {
} else if ( message . cmd === "set-config" ) {
this . hasSettings = true ;
2018-08-05 23:48:56 +02:00
this . settings . active = message . conf ;
2018-06-27 23:55:37 +02:00
// this.pageInfo.reset();
2018-05-26 23:08:49 +02:00
} else if ( message . cmd === "set-stretch" ) {
2018-05-27 21:41:08 +02:00
this . pageInfo . setStretchMode ( StretchMode [ message . mode ] ) ;
2018-06-15 00:33:10 +02:00
} else if ( message . cmd === "autoar-start" ) {
2018-05-26 23:08:49 +02:00
if ( message . enabled !== false ) {
this . pageInfo . initArDetection ( ) ;
this . pageInfo . startArDetection ( ) ;
} else {
this . pageInfo . stopArDetection ( ) ;
}
} else if ( message . cmd === "pause-processing" ) {
this . pageInfo . pauseProcessing ( ) ;
} else if ( message . cmd === "resume-processing" ) {
// todo: autoArStatus
this . pageInfo . resumeProcessing ( message . autoArStatus ) ;
2018-06-15 00:33:10 +02:00
} else if ( message . cmd === "reload-settings" ) {
2018-08-05 23:48:56 +02:00
this . settings . active = message . newConf ;
2018-06-15 00:33:10 +02:00
this . pageInfo . reset ( ) ;
2018-08-05 23:48:56 +02:00
if ( this . settings . active . arDetect . mode === "disabled" ) {
2018-06-27 23:55:37 +02:00
this . pageInfo . stopArDetection ( ) ;
} else {
this . pageInfo . startArDetection ( ) ;
}
2018-05-26 23:08:49 +02:00
}
}
async waitForSettings ( ) {
var t = this ;
return new Promise ( async ( resolve , reject ) => {
while ( true ) {
await t . sleep ( 100 ) ;
if ( this . hasSettings ) {
resolve ( ) ;
break ;
}
}
} ) ;
}
async sleep ( n ) {
return new Promise ( ( resolve , reject ) => setTimeout ( resolve , n ) ) ;
}
async sendMessage _nonpersistent ( message ) {
if ( BrowserDetect . firefox ) {
return browser . runtime . sendMessage ( message )
} else {
return new Promise ( ( resolve , reject ) => {
try {
if ( BrowserDetect . edge ) {
browser . runtime . sendMessage ( message , function ( response ) {
var r = response ;
resolve ( r ) ;
} ) ;
} else {
chrome . runtime . sendMessage ( message , function ( response ) {
// Chrome/js shittiness mitigation — remove this line and an empty array will be returned
var r = response ;
resolve ( r ) ;
} ) ;
}
}
catch ( e ) {
reject ( e ) ;
}
} ) ;
}
}
async requestSettings ( ) {
if ( Debug . debug ) {
2018-05-27 01:29:02 +02:00
console . log ( "%c[CommsClient::requestSettings] sending request for congif!" , "background: #11D; color: #aad" ) ;
2018-05-26 23:08:49 +02:00
}
var response = await this . sendMessage _nonpersistent ( { cmd : 'get-config' } ) ;
if ( Debug . debug ) {
2018-05-27 01:29:02 +02:00
console . log ( "%c[CommsClient::requestSettings] received settings response!" , "background: #11D; color: #aad" , response ) ;
2018-05-26 23:08:49 +02:00
}
if ( ! response || response . extensionConf ) {
return Promise . resolve ( false ) ;
}
2018-08-05 23:48:56 +02:00
this . settings . active = JSON . parse ( response . extensionConf ) ;
2018-05-26 23:08:49 +02:00
return Promise . resolve ( true ) ;
}
async requestSettings _fallback ( ) {
this . port . postMessage ( { cmd : "get-config" } ) ;
}
registerVideo ( ) {
this . port . postMessage ( { cmd : "has-video" } ) ;
}
unregisterVideo ( ) {
this . port . postMessage ( { cmd : "noVideo" } ) ; // ayymd
}
}
class CommsServer {
constructor ( server ) {
this . server = server ;
2018-08-07 23:31:28 +02:00
this . settings = server . settings ;
2018-05-26 23:08:49 +02:00
this . ports = [ ] ;
var ths = this ;
if ( BrowserDetect . firefox ) {
browser . runtime . onConnect . addListener ( p => ths . onConnect ( p ) ) ;
browser . runtime . onMessage . addListener ( m => ths . processReceivedMessage _nonpersistent _ff ( m ) ) ;
} else {
chrome . runtime . onConnect . addListener ( p => ths . onConnect ( p ) ) ;
chrome . runtime . onMessage . addListener ( ( msg , sender , callback ) => ths . processReceivedMessage _nonpersistent _chrome ( m , sender , callback ) ) ;
}
}
2018-06-27 23:55:37 +02:00
async getCurrentTabUrl ( ) {
}
2018-05-26 23:56:50 +02:00
sendToAll ( message ) {
2018-06-28 23:43:52 +02:00
for ( var p of this . ports ) {
for ( var frame in p ) {
2018-05-26 23:56:50 +02:00
p [ frame ] . postMessage ( message ) ;
}
}
}
2018-07-16 22:30:52 +02:00
async _getActiveTab ( ) {
if ( BrowserDetect . firefox ) {
return await browser . tabs . query ( { currentWindow : true , active : true } ) ;
} else {
return await new Promise ( ( resolve , reject ) => {
chrome . tabs . query ( { currentWindow : true , active : true } , function ( res ) {
resolve ( res ) ;
} ) ;
} ) ;
2018-05-27 01:29:02 +02:00
}
2018-07-16 22:30:52 +02:00
}
2018-05-27 01:29:02 +02:00
2018-07-16 22:30:52 +02:00
async sendToActive ( message ) {
if ( Debug . debug && Debug . comms ) {
console . log ( "%c[CommsServer::sendToActive] trying to send a message to active tab. Message:" , "background: #dda; color: #11D" , message ) ;
2018-05-26 23:56:50 +02:00
}
2018-07-16 22:30:52 +02:00
var tabs = await this . _getActiveTab ( ) ;
2018-05-27 01:29:02 +02:00
if ( Debug . debug && Debug . comms ) {
console . log ( "[CommsServer::_sendToActive_ff] currently active tab(s)?" , tabs ) ;
for ( var key in this . ports [ tabs [ 0 ] . id ] ) {
console . log ( "key?" , key , this . ports [ tabs [ 0 ] . id ] ) ;
// this.ports[tabs[0].id][key].postMessage(message);
}
}
for ( var key in this . ports [ tabs [ 0 ] . id ] ) {
2018-05-26 23:56:50 +02:00
this . ports [ tabs [ 0 ] . id ] [ key ] . postMessage ( message ) ;
}
}
2018-05-26 23:08:49 +02:00
onConnect ( port ) {
2018-05-27 01:29:02 +02:00
var ths = this ;
// poseben primer | special case
if ( port . name === 'popup-port' ) {
this . popupPort = port ;
this . popupPort . onMessage . addListener ( ( m , p ) => ths . processReceivedMessage ( m , p ) ) ;
return ;
}
2018-05-26 23:08:49 +02:00
var tabId = port . sender . tab . id ;
2018-05-26 23:56:50 +02:00
var frameId = port . sender . frameId ;
if ( ! this . ports [ tabId ] ) {
this . ports [ tabId ] = { } ;
}
this . ports [ tabId ] [ frameId ] = port ;
this . ports [ tabId ] [ frameId ] . onMessage . addListener ( ( m , p ) => ths . processReceivedMessage ( m , p ) ) ;
this . ports [ tabId ] [ frameId ] . onDisconnect . addListener ( ( p ) => {
delete ths . ports [ p . sender . tab . id ] [ p . sender . frameId ] ;
if ( Object . keys ( ths . ports [ p . sender . tab . id ] ) . length === 0 ) {
ths . ports [ tabId ] = undefined ;
}
} ) ;
2018-05-26 23:08:49 +02:00
}
processReceivedMessage ( message , port ) {
if ( Debug . debug && Debug . comms ) {
2018-08-23 01:04:37 +02:00
console . log ( "[CommsServer.js::processMessage] Received message from background script!" , message , "port" , port , "\nsettings and server:" , this . settings , this . server ) ;
2018-05-26 23:08:49 +02:00
}
if ( message . cmd === 'get-config' ) {
2018-08-21 23:48:47 +02:00
if ( Debug . debug ) {
console . log ( "CommsServer: received get-config. Active settings?" , this . settings . active , "\n(settings:" , this . settings , ")" )
}
2018-08-05 23:48:56 +02:00
port . postMessage ( { cmd : "set-config" , conf : this . settings . active , site : this . server . currentSite } )
2018-06-15 00:33:10 +02:00
} else if ( message . cmd === 'set-stretch' ) {
2018-05-27 21:41:08 +02:00
this . sendToActive ( message ) ;
2018-06-29 00:30:42 +02:00
} else if ( message . cmd === 'set-stretch-default' ) {
2018-08-05 23:48:56 +02:00
this . settings . active . stretch . initialMode = message . mode ;
2018-08-07 23:31:28 +02:00
this . settings . save ( ) ;
2018-08-05 23:48:56 +02:00
this . sendToAll ( { cmd : 'reload-settings' , newConf : this . settings . active } ) ;
2018-06-15 00:33:10 +02:00
} else if ( message . cmd === 'set-ar' ) {
2018-05-27 01:29:02 +02:00
this . sendToActive ( message ) ;
2018-07-09 23:30:11 +02:00
} else if ( message . cmd === 'set-custom-ar' ) {
2018-08-05 23:48:56 +02:00
this . settings . active . keyboard . shortcuts . q . arg = message . ratio ;
2018-08-07 23:31:28 +02:00
this . settings . save ( ) ;
2018-08-05 23:48:56 +02:00
this . sendToAll ( { cmd : 'reload-settings' , newConf : this . settings . active } ) ;
2018-07-15 16:22:32 +02:00
} else if ( message . cmd === 'set-video-float' ) {
this . sendToActive ( message ) ;
2018-08-05 23:48:56 +02:00
this . settings . active . miscFullscreenthis . settings . videoFloat = message . newFloat ;
2018-08-07 23:31:28 +02:00
this . settings . save ( ) ;
2018-08-05 23:48:56 +02:00
this . sendToAll ( { cmd : 'reload-settings' , newConf : this . settings . active } ) ;
2018-07-15 16:22:32 +02:00
2018-06-15 00:33:10 +02:00
} else if ( message . cmd === 'autoar-start' ) {
2018-05-27 01:29:02 +02:00
this . sendToActive ( message ) ;
2018-06-27 23:55:37 +02:00
} else if ( message . cmd === "autoar-enable" ) { // LEGACY - can be removed prolly?
2018-08-05 23:48:56 +02:00
this . settings . active . arDetect . mode = "blacklist" ;
2018-08-07 23:31:28 +02:00
this . settings . save ( ) ;
2018-08-05 23:48:56 +02:00
this . sendToAll ( { cmd : 'reload-settings' , newConf : this . settings . active } ) ;
2018-06-27 23:55:37 +02:00
} else if ( message . cmd === "autoar-disable" ) { // LEGACY - can be removed prolly?
2018-08-05 23:48:56 +02:00
this . settings . active . arDetect . mode = "disabled" ;
2018-06-15 00:33:10 +02:00
if ( message . reason ) {
2018-08-05 23:48:56 +02:00
this . settings . active . arDetect . disabledReason = message . reason ;
2018-06-15 00:33:10 +02:00
} else {
2018-08-05 23:48:56 +02:00
this . settings . active . arDetect . disabledReason = 'User disabled' ;
2018-06-15 00:33:10 +02:00
}
2018-08-07 23:31:28 +02:00
this . settings . save ( ) ;
2018-08-05 23:48:56 +02:00
this . sendToAll ( { cmd : 'reload-settings' , newConf : this . settings . active } ) ;
2018-06-15 00:33:10 +02:00
} else if ( message . cmd === "autoar-set-interval" ) {
if ( Debug . debug )
console . log ( "[uw-bg] trying to set new interval for autoAr. New interval is" , message . timeout , "ms" ) ;
// set fairly liberal limit
var timeout = message . timeout < 4 ? 4 : message . timeout ;
2018-08-05 23:48:56 +02:00
this . settings . active . arDetect . timer _playing = timeout ;
2018-08-07 23:31:28 +02:00
this . settings . save ( ) ;
2018-08-05 23:48:56 +02:00
this . sendToAll ( { cmd : 'reload-settings' , newConf : this . settings . active } ) ;
2018-06-27 23:55:37 +02:00
} else if ( message . cmd === "set-autoar-defaults" ) {
2018-08-05 23:48:56 +02:00
this . settings . active . arDetect . mode = message . mode ;
2018-08-07 23:31:28 +02:00
this . settings . save ( ) ;
2018-06-27 23:55:37 +02:00
this . sendToAll ( { cmd : "reload-settings" , sender : "uwbg" } )
} else if ( message . cmd === "set-autoar-for-site" ) {
2018-08-05 23:48:56 +02:00
if ( this . settings . active . sites [ this . server . currentSite ] ) {
this . settings . active . sites [ this . server . currentSite ] . arStatus = message . mode ;
2018-08-22 23:46:59 +02:00
console . log ( "SAVING AUTOAR MODE FOR SITE" , this . server . currentSite , "\nnew site obj" , this . settings . active . sites [ this . server . currentSite ] )
2018-08-07 23:31:28 +02:00
this . settings . save ( ) ;
2018-06-28 23:43:52 +02:00
} else {
2018-08-05 23:48:56 +02:00
this . settings . active . sites [ this . server . currentSite ] = {
2018-06-28 23:43:52 +02:00
status : "default" ,
arStatus : message . mode ,
statusEmbedded : "default"
} ;
2018-08-07 23:31:28 +02:00
this . settings . save ( ) ;
2018-06-28 23:43:52 +02:00
}
2018-06-27 23:55:37 +02:00
this . sendToAll ( { cmd : "reload-settings" , sender : "uwbg" } ) ;
} else if ( message . cmd === "set-extension-defaults" ) {
2018-08-05 23:48:56 +02:00
this . settings . active . extensionMode = message . mode ;
2018-08-07 23:31:28 +02:00
this . settings . save ( ) ;
2018-06-27 23:55:37 +02:00
this . sendToAll ( { cmd : "reload-settings" , sender : "uwbg" } )
} else if ( message . cmd === "set-extension-for-site" ) {
2018-08-05 23:48:56 +02:00
if ( this . settings . active . sites [ this . server . currentSite ] ) {
this . settings . active . sites [ this . server . currentSite ] . status = message . mode ;
2018-08-07 23:31:28 +02:00
this . settings . save ( ) ;
2018-06-28 23:43:52 +02:00
} else {
2018-08-05 23:48:56 +02:00
this . settings . active . sites [ this . server . currentSite ] = {
2018-06-28 23:43:52 +02:00
status : message . mode ,
arStatus : "default" ,
statusEmbedded : message . mode
} ;
2018-08-07 23:31:28 +02:00
this . settings . save ( ) ;
2018-08-05 23:48:56 +02:00
console . log ( "SAVING PER-SITE OPTIONS," , this . server . currentSite , this . settings . active . sites [ this . server . currentSite ] )
2018-06-28 23:43:52 +02:00
}
2018-06-27 23:55:37 +02:00
this . sendToAll ( { cmd : "reload-settings" , sender : "uwbg" } ) ;
2018-05-27 01:29:02 +02:00
}
2018-06-28 23:43:52 +02:00
if ( message . cmd . startsWith ( 'set-' ) ) {
2018-08-05 23:48:56 +02:00
port . postMessage ( { cmd : "set-config" , conf : this . settings . active , site : this . server . currentSite } ) ;
2018-06-28 23:43:52 +02:00
}
2018-05-26 23:08:49 +02:00
}
processReceivedMessage _nonpersistent _ff ( message , sender ) {
if ( Debug . debug && Debug . comms ) {
2018-05-27 01:29:02 +02:00
console . log ( "%c[CommsServer.js::processMessage_nonpersistent_ff] Received message from background script!" , "background-color: #11D; color: #aad" , message , sender ) ;
2018-05-26 23:08:49 +02:00
}
if ( message . cmd === 'get-config' ) {
2018-08-05 23:48:56 +02:00
var ret = { extensionConf : JSON . stringify ( this . settings . active ) } ;
2018-05-26 23:08:49 +02:00
if ( Debug . debug && Debug . comms ) {
2018-05-27 01:29:02 +02:00
console . log ( "%c[CommsServer.js::processMessage_nonpersistent_ff] Returning this:" , "background-color: #11D; color: #aad" , ret ) ;
2018-05-26 23:08:49 +02:00
}
Promise . resolve ( ret ) ;
2018-06-15 00:33:10 +02:00
} else if ( message . cmd === "autoar-enable" ) {
2018-08-05 23:48:56 +02:00
this . settings . active . arDetect . mode = "blacklist" ;
2018-08-07 23:31:28 +02:00
this . settings . save ( ) ;
2018-06-15 00:33:10 +02:00
this . sendToAll ( { cmd : "reload-settings" , sender : "uwbg" } )
if ( Debug . debug ) {
2018-08-05 23:48:56 +02:00
console . log ( "[uw-bg] autoar set to enabled (blacklist). evidenz:" , this . settings . active ) ;
2018-06-15 00:33:10 +02:00
}
} else if ( message . cmd === "autoar-disable" ) {
2018-08-05 23:48:56 +02:00
this . settings . active . arDetect . mode = "disabled" ;
2018-06-15 00:33:10 +02:00
if ( message . reason ) {
2018-08-05 23:48:56 +02:00
this . settings . active . arDetect . disabledReason = message . reason ;
2018-06-15 00:33:10 +02:00
} else {
2018-08-05 23:48:56 +02:00
this . settings . active . arDetect . disabledReason = 'User disabled' ;
2018-06-15 00:33:10 +02:00
}
2018-08-07 23:31:28 +02:00
this . settings . save ( ) ;
2018-08-05 23:48:56 +02:00
this . sendToAll ( { cmd : 'reload-settings' , newConf : this . settings . active } ) ;
2018-06-15 00:33:10 +02:00
if ( Debug . debug ) {
2018-08-05 23:48:56 +02:00
console . log ( "[uw-bg] autoar set to disabled. evidenz:" , this . settings . active ) ;
2018-06-15 00:33:10 +02:00
}
} else if ( message . cmd === "autoar-set-interval" ) {
if ( Debug . debug )
console . log ( "[uw-bg] trying to set new interval for autoAr. New interval is" , message . timeout , "ms" ) ;
// set fairly liberal limit
var timeout = message . timeout < 4 ? 4 : message . timeout ;
2018-08-05 23:48:56 +02:00
this . settings . active . arDetect . timer _playing = timeout ;
2018-08-07 23:31:28 +02:00
this . settings . save ( ) ;
2018-08-05 23:48:56 +02:00
this . sendToAll ( { cmd : 'reload-settings' , newConf : this . settings . active } ) ;
2018-05-26 23:56:50 +02:00
}
2018-05-26 23:08:49 +02:00
}
processReceivedMessage _nonpersistent _chrome ( message , sender , sendResponse ) {
if ( Debug . debug && Debug . comms ) {
console . log ( "[CommsServer.js::processMessage_nonpersistent_chrome] Received message from background script!" , message ) ;
}
if ( message . cmd === 'get-config' ) {
2018-08-05 23:48:56 +02:00
sendResponse ( { extensionConf : JSON . stringify ( this . settings . active ) , site : getCurrentTabUrl ( ) } ) ;
2018-05-26 23:08:49 +02:00
// return true;
2018-06-15 00:33:10 +02:00
} else if ( message . cmd === "autoar-enable" ) {
2018-08-05 23:48:56 +02:00
this . settings . active . arDetect . mode = "blacklist" ;
2018-08-07 23:31:28 +02:00
this . settings . save ( ) ;
2018-06-15 00:33:10 +02:00
this . sendToAll ( { cmd : "reload-settings" , sender : "uwbg" } )
if ( Debug . debug ) {
2018-08-05 23:48:56 +02:00
console . log ( "[uw-bg] autoar set to enabled (blacklist). evidenz:" , this . settings . active ) ;
2018-06-15 00:33:10 +02:00
}
} else if ( message . cmd === "autoar-disable" ) {
2018-08-05 23:48:56 +02:00
this . settings . active . arDetect . mode = "disabled" ;
2018-06-15 00:33:10 +02:00
if ( message . reason ) {
2018-08-05 23:48:56 +02:00
this . settings . active . arDetect . disabledReason = message . reason ;
2018-06-15 00:33:10 +02:00
} else {
2018-08-05 23:48:56 +02:00
this . settings . active . arDetect . disabledReason = 'User disabled' ;
2018-06-15 00:33:10 +02:00
}
2018-08-07 23:31:28 +02:00
this . settings . save ( ) ;
2018-08-05 23:48:56 +02:00
this . sendToAll ( { cmd : 'reload-settings' , newConf : this . settings . active } ) ;
2018-06-15 00:33:10 +02:00
if ( Debug . debug ) {
2018-08-05 23:48:56 +02:00
console . log ( "[uw-bg] autoar set to disabled. evidenz:" , this . settings . active ) ;
2018-06-15 00:33:10 +02:00
}
} else if ( message . cmd === "autoar-set-interval" ) {
if ( Debug . debug )
console . log ( "[uw-bg] trying to set new interval for autoAr. New interval is" , message . timeout , "ms" ) ;
// set fairly liberal limit
var timeout = message . timeout < 4 ? 4 : message . timeout ;
2018-08-05 23:48:56 +02:00
this . settings . active . arDetect . timer _playing = timeout ;
2018-08-07 23:31:28 +02:00
this . settings . save ( ) ;
2018-08-05 23:48:56 +02:00
this . sendToAll ( { cmd : 'reload-settings' , newConf : this . settings . active } ) ;
2018-05-26 23:08:49 +02:00
}
}
}
2018-05-27 01:29:02 +02:00
class Comms {
static async sendMessage ( message ) {
if ( BrowserDetect . firefox ) {
return browser . runtime . sendMessage ( message )
} else {
return new Promise ( ( resolve , reject ) => {
try {
if ( BrowserDetect . edge ) {
browser . runtime . sendMessage ( message , function ( response ) {
var r = response ;
resolve ( r ) ;
} ) ;
} else {
chrome . runtime . sendMessage ( message , function ( response ) {
// Chrome/js shittiness mitigation — remove this line and an empty array will be returned
var r = response ;
resolve ( r ) ;
} ) ;
}
}
catch ( e ) {
reject ( e ) ;
}
} ) ;
}
}
2018-01-06 22:58:31 +01:00
2018-05-27 01:29:02 +02:00
}