MagicPlanetWebClient.js:264 
        
        
       POST http://localhost:1337/api/json/storyteller/rotating net::ERR_CONNECTION_REFUSED
postJSONToSJAXServer @ MagicPlanetWebClient.js:264
set @ MagicPlanetWebClient.js:194
executeSelection @ videoPlayer.js:112
onclick @ index.html:26
MagicPlanetWebClient.js:264 Uncaught (in promise) NetworkError: Failed to execute 'send' on 'XMLHttpRequest': Failed to load 'http://localhost:1337/api/json/storyteller/rotating'.
    at postJSONToSJAXServer (MagicPlanetWebClient.js:264:7)
    at storyControlWebObject.set (MagicPlanetWebClient.js:194:3)
    at executeSelection (videoPlayer.js:112:24)
    at HTMLImageElement.onclick (index.html:26:214) this is the error and this /* –– Server settings ––––––––––––––––––––––––––––––––––––––––––––––––––––––– */

var serverName = 'Magic Planet';
var serverPort = 1337;
var serviceType = 'magicplanet';
var showWelcomeMessage = true;
var showLogMessages = true;
var useMachineName = false;
var oleGarbageCollection = true;
var oleGarbageCollectionThreshold = 250;

/* –– End ––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––– */




/* –– DO NOT EDIT BELOW THIS LINE ––––––––––––––––––––––––––––––––––––––––––– */

// Server version
var VERSION = '0.7.5';

// Import Node.js modules
var win32ole = require('win32ole');
var express = require('express');
var bodyParser = require('body-parser');
var argv = require('optimist').argv;
var mdns = require('mdns');
var crypto = require('crypto');
var os = require('os');

// Instantiate StoryControl ActiveXObject
var storyTeller = win32ole.client.Dispatch('StoryControl.StoryControl');

// Load defaults from command line
if((argv.serverName != null) && (argv.serverName != '')) {
  serverName = argv.serverName;
}
if((argv.serverPort != null) && (argv.serverPort != '')) {
  serverPort = argv.serverPort;
}
if((argv.serviceType != null) && (argv.serviceType != '')) {
  serviceType = argv.serviceType;
}
if((argv.showWelcomeMessage != null) && (argv.showWelcomeMessage != '')) {
  showWelcomeMessage = argv.showWelcomeMessage;
}
if((argv.showLogMessages != null) && (argv.showLogMessages != '')) {
  showLogMessages = argv.showLogMessages;
}
if((argv.useMachineName != null) && (argv.useMachineName != '')) {
  useMachineName = argv.useMachineName;
}
if((argv.oleGarbageCollection != null) && (argv.oleGarbageCollection != '')) {
  oleGarbageCollection = argv.oleGarbageCollection;
}
if((argv.oleGarbageCollectionThreshold != null) && (argv.oleGarbageCollectionThreshold != '')) {
  oleGarbageCollectionThreshold = argv.oleGarbageCollectionThreshold;
}

// Instantiate Express.js
var app = express();
app.use(bodyParser.json());

// Check if machine name should be used as server name
if(useMachineName) {
  serverName = os.hostname();
}

// Generate unique server serial number
var serverID = ('mp' + generateRandomString(8));

// Instantiate other variables
var servicePeers = [];
var oleGarbageCollectionCounter = 0;

// Allow Cross-Origin Resource Sharing (CORS)
app.all('*', function(req, res, next) {
  res.header('Access-Control-Allow-Origin', '*');
  res.header('Access-Control-Allow-Methods', 'PUT, GET, POST, DELETE, OPTIONS');
  res.header('Access-Control-Allow-Headers', 'Content-Type');
  next();
});

// HTML routing
app.get('/', function(req, res) {
  res.send('Magic Planet Web API (v'+ VERSION + '), ©2015 Global Imagination.');
});

// JSON routing
app.post('/api/json/servers', function(req, res) {
  if(req.body.requestType == 'GET') {
    logMessageWithTimestamp('Incoming request from ' + req.ip +' - [JSON GET] Fetch Servers');
    res.json({ responseType: 'SUCCESS', serverName: serverName, servicePeers: servicePeers });
  }
  else {
    logMessageWithTimestamp('WARNING: Malformed request from ' + req.ip +' - [JSON GET] Fetch Servers');
    res.json({ responseType: 'FAIL' });
  }
});
app.post('/api/json/storyteller/autoadvance', function(req, res) {
  if(req.body.requestType == 'SET') {
    var newValue = req.body.requestValue;
    logMessageWithTimestamp('Incoming request from ' + req.ip +' - [JSON SET] StoryTeller.AutoAdvance = ' + newValue);
    try {
      storyTeller.set('AutoAdvance', newValue);
      res.json({ responseType: 'SUCCESS' });
    }
    catch(e) {
      logMessageWithTimestamp('WARNING: Error communicating with StoryTeller (' + e + ')');
      res.json({ responseType: 'FAIL' });
    }
    checkGarbageCollection();
  }
  else if(req.body.requestType == 'GET') {
    logMessageWithTimestamp('Incoming request from ' + req.ip +' - [JSON GET] StoryTeller.AutoAdvance');
    try {
      res.json({ responseType: 'SUCCESS', responseValue: (storyTeller.get('AutoAdvance') == true) });
    }
    catch(e) {
      logMessageWithTimestamp('WARNING: Error communicating with StoryTeller (' + e + ')');
      res.json({ responseType: 'FAIL' });
    }
    checkGarbageCollection();
  }
  else {
    logMessageWithTimestamp('WARNING: Malformed request from ' + req.ip +' - [JSON GET] StoryTeller.AutoAdvance');
    res.json({ responseType: 'FAIL' });
  }
  newValue = null;
});
app.post('/api/json/storyteller/chapter', function(req, res) {
  if(req.body.requestType == 'SET') {
    var newValue = req.body.requestValue;
    logMessageWithTimestamp('Incoming request from ' + req.ip +' - [JSON SET] StoryTeller.Chapter = ' + newValue);
    try {
      storyTeller.set('Chapter', newValue);
      res.json({ responseType: 'SUCCESS' });
    }
    catch(e) {
      logMessageWithTimestamp('WARNING: Error communicating with StoryTeller (' + e + ')');
      res.json({ responseType: 'FAIL' });
    }
    checkGarbageCollection();
  }
  else if(req.body.requestType == 'GET') {
    logMessageWithTimestamp('Incoming request from ' + req.ip +' - [JSON GET] StoryTeller.Chapter');
    try {
      res.json({ responseType: 'SUCCESS', responseValue: parseInt(storyTeller.get('Chapter')) });
    }
    catch(e) {
      logMessageWithTimestamp('WARNING: Error communicating with StoryTeller (' + e + ')');
      res.json({ responseType: 'FAIL' });
    }
    checkGarbageCollection();
  }
  else {
    logMessageWithTimestamp('WARNING: Malformed request from ' + req.ip +' - [JSON GET] StoryTeller.Chapter');
    res.json({ responseType: 'FAIL' });
  }
  newValue = null;
});
app.post('/api/json/storyteller/chaptername', function(req, res) {
  if(req.body.requestType == 'SET') {
    var newValue = req.body.requestValue;
    logMessageWithTimestamp('Incoming request from ' + req.ip +' - [JSON SET] StoryTeller.ChapterName = ' + newValue);
    try {
      storyTeller.set('ChapterName', newValue);
      res.json({ responseType: 'SUCCESS' });
    }
    catch(e) {
      logMessageWithTimestamp('WARNING: Error communicating with StoryTeller (' + e + ')');
      res.json({ responseType: 'FAIL' });
    }
    checkGarbageCollection();
  }
  else if(req.body.requestType == 'GET') {
    logMessageWithTimestamp('Incoming request from ' + req.ip +' - [JSON GET] StoryTeller.ChapterName');
    try {
      res.json({ responseType: 'SUCCESS', responseValue: (storyTeller.get('ChapterName').toString()) });
    }
    catch(e) {
      logMessageWithTimestamp('WARNING: Error communicating with StoryTeller (' + e + ')');
      res.json({ responseType: 'FAIL' });
    }
    checkGarbageCollection();
  }
  else {
    logMessageWithTimestamp('WARNING: Malformed request from ' + req.ip +' - [JSON GET] StoryTeller.ChapterName');
    res.json({ responseType: 'FAIL' });
  }
  newValue = null;
});
app.post('/api/json/storyteller/flippoles', function(req, res) {
  if(req.body.requestType == 'SET') {
    var newValue = req.body.requestValue;
    logMessageWithTimestamp('Incoming request from ' + req.ip +' - [JSON SET] StoryTeller.FlipPoles = ' + newValue);
    try {
      storyTeller.set('FlipPoles', newValue);
      res.json({ responseType: 'SUCCESS' });
    }
    catch(e) {
      logMessageWithTimestamp('WARNING: Error communicating with StoryTeller (' + e + ')');
      res.json({ responseType: 'FAIL' });
    }
    checkGarbageCollection();
  }
  else if(req.body.requestType == 'GET') {
    logMessageWithTimestamp('Incoming request from ' + req.ip +' - [JSON GET] StoryTeller.FlipPoles');
    try {
      res.json({ responseType: 'SUCCESS', responseValue: (storyTeller.get('FlipPoles') == true) });
    }
    catch(e) {
      logMessageWithTimestamp('WARNING: Error communicating with StoryTeller (' + e + ')');
      res.json({ responseType: 'FAIL' });
    }
    checkGarbageCollection();
  }
  else {
    logMessageWithTimestamp('WARNING: Malformed request from ' + req.ip +' - [JSON GET] StoryTeller.FlipPoles');
    res.json({ responseType: 'FAIL' });
  }
  newValue = null;
});
app.post('/api/json/storyteller/frame', function(req, res) {
  if(req.body.requestType == 'SET') {
    var newValue = req.body.requestValue;
    logMessageWithTimestamp('Incoming request from ' + req.ip +' - [JSON SET] StoryTeller.Frame = ' + newValue);
    try {
      storyTeller.set('Frame', newValue);
      res.json({ responseType: 'SUCCESS' });
    }
    catch(e) {
      logMessageWithTimestamp('WARNING: Error communicating with StoryTeller (' + e + ')');
      res.json({ responseType: 'FAIL' });
    }
    checkGarbageCollection();
  }
  else if(req.body.requestType == 'GET') {
    logMessageWithTimestamp('Incoming request from ' + req.ip +' - [JSON GET] StoryTeller.Frame');
    try {
      res.json({ responseType: 'SUCCESS', responseValue: parseInt(storyTeller.get('Frame')) });
    }
    catch(e) {
      logMessageWithTimestamp('WARNING: Error communicating with StoryTeller (' + e + ')');
      res.json({ responseType: 'FAIL' });
    }
    checkGarbageCollection();
  }
  else {
    logMessageWithTimestamp('WARNING: Malformed request from ' + req.ip +' - [JSON GET] StoryTeller.Frame');
    res.json({ responseType: 'FAIL' });
  }
  newValue = null;
});
app.post('/api/json/storyteller/framerate', function(req, res) {
  if(req.body.requestType == 'SET') {
    var newValue = req.body.requestValue;
    logMessageWithTimestamp('Incoming request from ' + req.ip +' - [JSON SET] StoryTeller.FrameRate = ' + newValue);
    try {
      storyTeller.set('FrameRate', newValue);
      res.json({ responseType: 'SUCCESS' });
    }
    catch(e) {
      logMessageWithTimestamp('WARNING: Error communicating with StoryTeller (' + e + ')');
      res.json({ responseType: 'FAIL' });
    }
    checkGarbageCollection();
  }
  else if(req.body.requestType == 'GET') {
    logMessageWithTimestamp('Incoming request from ' + req.ip +' - [JSON GET] StoryTeller.FrameRate');
    try {
      res.json({ responseType: 'SUCCESS', responseValue: parseInt(storyTeller.get('FrameRate')) });
    }
    catch(e) {
      logMessageWithTimestamp('WARNING: Error communicating with StoryTeller (' + e + ')');
      res.json({ responseType: 'FAIL' });
    }
    checkGarbageCollection();
  }
  else {
    logMessageWithTimestamp('WARNING: Malformed request from ' + req.ip +' - [JSON GET] StoryTeller.FrameRate');
    res.json({ responseType: 'FAIL' });
  }
  newValue = null;
});
app.post('/api/json/storyteller/page', function(req, res) {
  if(req.body.requestType == 'SET') {
    var newValue = req.body.requestValue;
    logMessageWithTimestamp('Incoming request from ' + req.ip +' - [JSON SET] StoryTeller.Page = ' + newValue);
    try {
      storyTeller.set('Page', newValue);
      res.json({ responseType: 'SUCCESS' });
    }
    catch(e) {
      logMessageWithTimestamp('WARNING: Error communicating with StoryTeller (' + e + ')');
      res.json({ responseType: 'FAIL' });
    }
    checkGarbageCollection();
  }
  else if(req.body.requestType == 'GET') {
    logMessageWithTimestamp('Incoming request from ' + req.ip +' - [JSON GET] StoryTeller.Page');
    try {
      res.json({ responseType: 'SUCCESS', responseValue: parseInt(storyTeller.get('Page')) });
    }
    catch(e) {
      logMessageWithTimestamp('WARNING: Error communicating with StoryTeller (' + e + ')');
      res.json({ responseType: 'FAIL' });
    }
    checkGarbageCollection();
  }
  else {
    logMessageWithTimestamp('WARNING: Malformed request from ' + req.ip +' - [JSON GET] StoryTeller.Page');
    res.json({ responseType: 'FAIL' });
  }
  newValue = null;
});
app.post('/api/json/storyteller/pagelatitudeofinterest', function(req, res) {
  if(req.body.requestType == 'SET') {
    var newValue = req.body.requestValue;
    logMessageWithTimestamp('Incoming request from ' + req.ip +' - [JSON SET] StoryTeller.PageLatitudeOfInterest = ' + newValue);
    try {
      storyTeller.set('PageLatitudeOfInterest', newValue);
      res.json({ responseType: 'SUCCESS' });
    }
    catch(e) {
      logMessageWithTimestamp('WARNING: Error communicating with StoryTeller (' + e + ')');
      res.json({ responseType: 'FAIL' });
    }
    checkGarbageCollection();
  }
  else if(req.body.requestType == 'GET') {
    logMessageWithTimestamp('Incoming request from ' + req.ip +' - [JSON GET] StoryTeller.PageLatitudeOfInterest');
    try {
      res.json({ responseType: 'SUCCESS', responseValue: parseInt(storyTeller.get('PageLatitudeOfInterest')) });
    }
    catch(e) {
      logMessageWithTimestamp('WARNING: Error communicating with StoryTeller (' + e + ')');
      res.json({ responseType: 'FAIL' });
    }
    checkGarbageCollection();
  }
  else {
    logMessageWithTimestamp('WARNING: Malformed request from ' + req.ip +' - [JSON GET] StoryTeller.PageLatitudeOfInterest');
    res.json({ responseType: 'FAIL' });
  }
  newValue = null;
});
app.post('/api/json/storyteller/pagelongitudeofinterest', function(req, res) {
  if(req.body.requestType == 'SET') {
    var newValue = req.body.requestValue;
    logMessageWithTimestamp('Incoming request from ' + req.ip +' - [JSON SET] StoryTeller.PageLongitudeOfInterest = ' + newValue);
    try {
      storyTeller.set('PageLongitudeOfInterest', newValue);
      res.json({ responseType: 'SUCCESS' });
    }
    catch(e) {
      logMessageWithTimestamp('WARNING: Error communicating with StoryTeller (' + e + ')');
      res.json({ responseType: 'FAIL' });
    }
    checkGarbageCollection();
  }
  else if(req.body.requestType == 'GET') {
    logMessageWithTimestamp('Incoming request from ' + req.ip +' - [JSON GET] StoryTeller.PageLongitudeOfInterest');
    try {
      res.json({ responseType: 'SUCCESS', responseValue: parseInt(storyTeller.get('PageLongitudeOfInterest')) });
    }
    catch(e) {
      logMessageWithTimestamp('WARNING: Error communicating with StoryTeller (' + e + ')');
      res.json({ responseType: 'FAIL' });
    }
    checkGarbageCollection();
  }
  else {
    logMessageWithTimestamp('WARNING: Malformed request from ' + req.ip +' - [JSON GET] StoryTeller.PageLongitudeOfInterest');
    res.json({ responseType: 'FAIL' });
  }
  newValue = null;
});
app.post('/api/json/storyteller/pagename', function(req, res) {
  if(req.body.requestType == 'SET') {
    var newValue = req.body.requestValue;
    logMessageWithTimestamp('Incoming request from ' + req.ip +' - [JSON SET] StoryTeller.PageName = ' + newValue);
    try {
      storyTeller.set('PageName', newValue);
      res.json({ responseType: 'SUCCESS' });
    }
    catch(e) {
      logMessageWithTimestamp('WARNING: Error communicating with StoryTeller (' + e + ')');
      res.json({ responseType: 'FAIL' });
    }
    checkGarbageCollection();
  }
  else if(req.body.requestType == 'GET') {
    logMessageWithTimestamp('Incoming request from ' + req.ip +' - [JSON GET] StoryTeller.PageName');
    try {
      res.json({ responseType: 'SUCCESS', responseValue: (storyTeller.get('PageName').toString()) });
    }
    catch(e) {
      logMessageWithTimestamp('WARNING: Error communicating with StoryTeller (' + e + ')');
      res.json({ responseType: 'FAIL' });
    }
    checkGarbageCollection();
  }
  else {
    logMessageWithTimestamp('WARNING: Malformed request from ' + req.ip +' - [JSON GET] StoryTeller.PageName');
    res.json({ responseType: 'FAIL' });
  }
  newValue = null;
});
app.post('/api/json/storyteller/playaudio', function(req, res) {
  if(req.body.requestType == 'SET') {
    var newValue = req.body.requestValue;
    logMessageWithTimestamp('Incoming request from ' + req.ip +' - [JSON SET] StoryTeller.PlayAudio = ' + newValue);
    try {
      storyTeller.set('PlayAudio', newValue);
      res.json({ responseType: 'SUCCESS' });
    }
    catch(e) {
      logMessageWithTimestamp('WARNING: Error communicating with StoryTeller (' + e + ')');
      res.json({ responseType: 'FAIL' });
    }
    checkGarbageCollection();
  }
  else if(req.body.requestType == 'GET') {
    logMessageWithTimestamp('Incoming request from ' + req.ip +' - [JSON GET] StoryTeller.PlayAudio');
    try {
      res.json({ responseType: 'SUCCESS', responseValue: (storyTeller.get('PlayAudio') == true) });
    }
    catch(e) {
      logMessageWithTimestamp('WARNING: Error communicating with StoryTeller (' + e + ')');
      res.json({ responseType: 'FAIL' });
    }
    checkGarbageCollection();
  }
  else {
    logMessageWithTimestamp('WARNING: Malformed request from ' + req.ip +' - [JSON GET] StoryTeller.PlayAudio');
    res.json({ responseType: 'FAIL' });
  }
  newValue = null;
});
app.post('/api/json/storyteller/polelatitude', function(req, res) {
  if(req.body.requestType == 'SET') {
    var newValue = req.body.requestValue;
    logMessageWithTimestamp('Incoming request from ' + req.ip +' - [JSON SET] StoryTeller.PoleLatitude = ' + newValue);
    try {
      storyTeller.set('PoleLatitude', newValue);
      res.json({ responseType: 'SUCCESS' });
    }
    catch(e) {
      logMessageWithTimestamp('WARNING: Error communicating with StoryTeller (' + e + ')');
      res.json({ responseType: 'FAIL' });
    }
    checkGarbageCollection();
  }
  else if(req.body.requestType == 'GET') {
    logMessageWithTimestamp('Incoming request from ' + req.ip +' - [JSON GET] StoryTeller.PoleLatitude');
    try {
      res.json({ responseType: 'SUCCESS', responseValue: parseInt(storyTeller.get('PoleLatitude')) });
    }
    catch(e) {
      logMessageWithTimestamp('WARNING: Error communicating with StoryTeller (' + e + ')');
      res.json({ responseType: 'FAIL' });
    }
    checkGarbageCollection();
  }
  else {
    logMessageWithTimestamp('WARNING: Malformed request from ' + req.ip +' - [JSON GET] StoryTeller.PoleLatitude');
    res.json({ responseType: 'FAIL' });
  }
  newValue = null;
});
app.post('/api/json/storyteller/polelongitude', function(req, res) {
  if(req.body.requestType == 'SET') {
    var newValue = req.body.requestValue;
    logMessageWithTimestamp('Incoming request from ' + req.ip +' - [JSON SET] StoryTeller.PoleLongitude = ' + newValue);
    try {
      storyTeller.set('PoleLongitude', newValue);
      res.json({ responseType: 'SUCCESS' });
    }
    catch(e) {
      logMessageWithTimestamp('WARNING: Error communicating with StoryTeller (' + e + ')');
      res.json({ responseType: 'FAIL' });
    }
    checkGarbageCollection();
  }
  else if(req.body.requestType == 'GET') {
    logMessageWithTimestamp('Incoming request from ' + req.ip +' - [JSON GET] StoryTeller.PoleLongitude');
    try {
      res.json({ responseType: 'SUCCESS', responseValue: parseInt(storyTeller.get('PoleLongitude')) });
    }
    catch(e) {
      logMessageWithTimestamp('WARNING: Error communicating with StoryTeller (' + e + ')');
      res.json({ responseType: 'FAIL' });
    }
    checkGarbageCollection();
  }
  else {
    logMessageWithTimestamp('WARNING: Malformed request from ' + req.ip +' - [JSON GET] StoryTeller.PoleLongitude');
    res.json({ responseType: 'FAIL' });
  }
  newValue = null;
});
app.post('/api/json/storyteller/rotateclockwise', function(req, res) {
  if(req.body.requestType == 'SET') {
    var newValue = req.body.requestValue;
    logMessageWithTimestamp('Incoming request from ' + req.ip +' - [JSON SET] StoryTeller.RotateClockwise = ' + newValue);
    try {
      storyTeller.set('RotateClockwise', newValue);
      res.json({ responseType: 'SUCCESS' });
    }
    catch(e) {
      logMessageWithTimestamp('WARNING: Error communicating with StoryTeller (' + e + ')');
      res.json({ responseType: 'FAIL' });
    }
    checkGarbageCollection();
  }
  else if(req.body.requestType == 'GET') {
    logMessageWithTimestamp('Incoming request from ' + req.ip +' - [JSON GET] StoryTeller.RotateClockwise');
    try {
      res.json({ responseType: 'SUCCESS', responseValue: (storyTeller.get('RotateClockwise') == true) });
    }
    catch(e) {
      logMessageWithTimestamp('WARNING: Error communicating with StoryTeller (' + e + ')');
      res.json({ responseType: 'FAIL' });
    }
    checkGarbageCollection();
  }
  else {
    logMessageWithTimestamp('WARNING: Malformed request from ' + req.ip +' - [JSON GET] StoryTeller.RotateClockwise');
    res.json({ responseType: 'FAIL' });
  }
  newValue = null;
});
app.post('/api/json/storyteller/rotating', function(req, res) {
  if(req.body.requestType == 'SET') {
    var newValue = req.body.requestValue;
    logMessageWithTimestamp('Incoming request from ' + req.ip +' - [JSON SET] StoryTeller.Rotating = ' + newValue);
    try {
      storyTeller.set('Rotating', newValue);
      res.json({ responseType: 'SUCCESS' });
    }
    catch(e) {
      logMessageWithTimestamp('WARNING: Error communicating with StoryTeller (' + e + ')');
      res.json({ responseType: 'FAIL' });
    }
    checkGarbageCollection();
  }
  else if(req.body.requestType == 'GET') {
    logMessageWithTimestamp('Incoming request from ' + req.ip +' - [JSON GET] StoryTeller.Rotating');
    try {
      res.json({ responseType: 'SUCCESS', responseValue: (storyTeller.get('Rotating') == true) });
    }
    catch(e) {
      logMessageWithTimestamp('WARNING: Error communicating with StoryTeller (' + e + ')');
      res.json({ responseType: 'FAIL' });
    }
    checkGarbageCollection();
  }
  else {
    logMessageWithTimestamp('WARNING: Malformed request from ' + req.ip +' - [JSON GET] StoryTeller.Rotating');
    res.json({ responseType: 'FAIL' });
  }
  newValue = null;
});
app.post('/api/json/storyteller/rotationangle', function(req, res) {
  if(req.body.requestType == 'SET') {
    var newValue = req.body.requestValue;
    logMessageWithTimestamp('Incoming request from ' + req.ip +' - [JSON SET] StoryTeller.RotationAngle = ' + newValue);
    try {
      storyTeller.set('RotationAngle', newValue);
      res.json({ responseType: 'SUCCESS' });
    }
    catch(e) {
      logMessageWithTimestamp('WARNING: Error communicating with StoryTeller (' + e + ')');
      res.json({ responseType: 'FAIL' });
    }
    checkGarbageCollection();
  }
  else if(req.body.requestType == 'GET') {
    logMessageWithTimestamp('Incoming request from ' + req.ip +' - [JSON GET] StoryTeller.RotationAngle');
    try {
      res.json({ responseType: 'SUCCESS', responseValue: parseInt(storyTeller.get('RotationAngle')) });
    }
    catch(e) {
      logMessageWithTimestamp('WARNING: Error communicating with StoryTeller (' + e + ')');
      res.json({ responseType: 'FAIL' });
    }
    checkGarbageCollection();
  }
  else {
    logMessageWithTimestamp('WARNING: Malformed request from ' + req.ip +' - [JSON GET] StoryTeller.RotationAngle');
    res.json({ responseType: 'FAIL' });
  }
  newValue = null;
});
app.post('/api/json/storyteller/rotationrate', function(req, res) {
  if(req.body.requestType == 'SET') {
    var newValue = req.body.requestValue;
    logMessageWithTimestamp('Incoming request from ' + req.ip +' - [JSON SET] StoryTeller.RotationRate = ' + newValue);
    try {
      storyTeller.set('RotationRate', newValue);
      res.json({ responseType: 'SUCCESS' });
    }
    catch(e) {
      logMessageWithTimestamp('WARNING: Error communicating with StoryTeller (' + e + ')');
      res.json({ responseType: 'FAIL' });
    }
    checkGarbageCollection();
  }
  else if(req.body.requestType == 'GET') {
    logMessageWithTimestamp('Incoming request from ' + req.ip +' - [JSON GET] StoryTeller.RotationRate');
    try {
      res.json({ responseType: 'SUCCESS', responseValue: parseInt(storyTeller.get('RotationRate')) });
    }
    catch(e) {
      logMessageWithTimestamp('WARNING: Error communicating with StoryTeller (' + e + ')');
      res.json({ responseType: 'FAIL' });
    }
    checkGarbageCollection();
  }
  else {
    logMessageWithTimestamp('WARNING: Malformed request from ' + req.ip +' - [JSON GET] StoryTeller.RotationRate');
    res.json({ responseType: 'FAIL' });
  }
  newValue = null;
});
app.post('/api/json/storyteller/showcontent', function(req, res) {
  if(req.body.requestType == 'SET') {
    var newValue = req.body.requestValue;
    logMessageWithTimestamp('Incoming request from ' + req.ip +' - [JSON SET] StoryTeller.ShowContent = ' + newValue);
    try {
      storyTeller.set('ShowContent', newValue);
      res.json({ responseType: 'SUCCESS' });
    }
    catch(e) {
      logMessageWithTimestamp('WARNING: Error communicating with StoryTeller (' + e + ')');
      res.json({ responseType: 'FAIL' });
    }
    checkGarbageCollection();
  }
  else if(req.body.requestType == 'GET') {
    logMessageWithTimestamp('Incoming request from ' + req.ip +' - [JSON GET] StoryTeller.ShowContent');
    try {
      res.json({ responseType: 'SUCCESS', responseValue: (storyTeller.get('ShowContent') == true) });
    }
    catch(e) {
      logMessageWithTimestamp('WARNING: Error communicating with StoryTeller (' + e + ')');
      res.json({ responseType: 'FAIL' });
    }
    checkGarbageCollection();
  }
  else {
    logMessageWithTimestamp('WARNING: Malformed request from ' + req.ip +' - [JSON GET] StoryTeller.ShowContent');
    res.json({ responseType: 'FAIL' });
  }
  newValue = null;
});
app.post('/api/json/storyteller/story', function(req, res) {
  if(req.body.requestType == 'SET') {
    var newValue = req.body.requestValue;
    logMessageWithTimestamp('Incoming request from ' + req.ip +' - [JSON SET] StoryTeller.Story = ' + newValue);
    try {
      storyTeller.set('Story', newValue);
      res.json({ responseType: 'SUCCESS' });
    }
    catch(e) {
      logMessageWithTimestamp('WARNING: Error communicating with StoryTeller (' + e + ')');
      res.json({ responseType: 'FAIL' });
    }
    checkGarbageCollection();
  }
  else if(req.body.requestType == 'GET') {
    logMessageWithTimestamp('Incoming request from ' + req.ip +' - [JSON GET] StoryTeller.Story');
    try {
      res.json({ responseType: 'SUCCESS', responseValue: (storyTeller.get('Story').toString()) });
    }
    catch(e) {
      logMessageWithTimestamp('WARNING: Error communicating with StoryTeller (' + e + ')');
      res.json({ responseType: 'FAIL' });
    }
    checkGarbageCollection();
  }
  else {
    logMessageWithTimestamp('WARNING: Malformed request from ' + req.ip +' - [JSON GET] StoryTeller.Story');
    res.json({ responseType: 'FAIL' });
  }
  newValue = null;
});
app.post('/api/json/storyteller/chapterandpage', function(req, res) {
  if(req.body.requestType == 'RMI') {
    var newValue1 = req.body.requestValue1;
    var newValue2 = req.body.requestValue2;
    logMessageWithTimestamp('Incoming request from ' + req.ip +' - [JSON RMI] StoryTeller.ChapterAndPage(' + newValue1 + ', ' + newValue2 + ')');
    try {
      storyTeller.call('ChapterAndPage', [newValue1, newValue2]);
      res.json({ responseType: 'SUCCESS' });
    }
    catch(e) {
      logMessageWithTimestamp('WARNING: Error communicating with StoryTeller (' + e + ')');
      res.json({ responseType: 'FAIL' });
    }
    checkGarbageCollection();
  }
  else {
    logMessageWithTimestamp('WARNING: Malformed request from ' + req.ip +' - [JSON RMI] StoryTeller.ChapterAndPage()');
    res.json({ responseType: 'FAIL' });
  }
  newValue1 = null;
  newValue2 = null;
});
app.post('/api/json/storyteller/chapterandpagenames', function(req, res) {
  if(req.body.requestType == 'RMI') {
    var newValue1 = req.body.requestValue1;
    var newValue2 = req.body.requestValue2;
    logMessageWithTimestamp('Incoming request from ' + req.ip +' - [JSON RMI] StoryTeller.ChapterAndPageNames(' + newValue1 + ', ' + newValue2 + ')');
    try {
      storyTeller.call('ChapterAndPageNames', [newValue1, newValue2]);
      res.json({ responseType: 'SUCCESS' });
    }
    catch(e) {
      logMessageWithTimestamp('WARNING: Error communicating with StoryTeller (' + e + ')');
      res.json({ responseType: 'FAIL' });
    }
    checkGarbageCollection();
  }
  else {
    logMessageWithTimestamp('WARNING: Malformed request from ' + req.ip +' - [JSON RMI] StoryTeller.ChapterAndPageNames()');
    res.json({ responseType: 'FAIL' });
  }
  newValue1 = null;
  newValue2 = null;
});
app.post('/api/json/storyteller/firstframe', function(req, res) {
  if(req.body.requestType == 'RMI') {
    logMessageWithTimestamp('Incoming request from ' + req.ip +' - [JSON RMI] StoryTeller.FirstFrame()');
    try {
      storyTeller.call('FirstFrame');
      res.json({ responseType: 'SUCCESS' });
    }
    catch(e) {
      logMessageWithTimestamp('WARNING: Error communicating with StoryTeller (' + e + ')');
      res.json({ responseType: 'FAIL' });
    }
    checkGarbageCollection();
  }
  else {
    logMessageWithTimestamp('WARNING: Malformed request from ' + req.ip +' - [JSON RMI] StoryTeller.FirstFrame()');
    res.json({ responseType: 'FAIL' });
  }
});
app.post('/api/json/storyteller/lastframe', function(req, res) {
  if(req.body.requestType == 'RMI') {
    logMessageWithTimestamp('Incoming request from ' + req.ip +' - [JSON RMI] StoryTeller.LastFrame()');
    try {
      storyTeller.call('LastFrame');
      res.json({ responseType: 'SUCCESS' });
    }
    catch(e) {
      logMessageWithTimestamp('WARNING: Error communicating with StoryTeller (' + e + ')');
      res.json({ responseType: 'FAIL' });
    }
    checkGarbageCollection();
  }


  else {
    logMessageWithTimestamp('WARNING: Malformed request from ' + req.ip +' - [JSON RMI] StoryTeller.LastFrame()');
    res.json({ responseType: 'FAIL' });
  }
});
app.post('/api/json/storyteller/nextpage', function(req, res) {
  if(req.body.requestType == 'RMI') {
    logMessageWithTimestamp('Incoming request from ' + req.ip +' - [JSON RMI] StoryTeller.NextPage()');
    try {
      storyTeller.call('NextPage');
      res.json({ responseType: 'SUCCESS' });
    }
    catch(e) {
      logMessageWithTimestamp('WARNING: Error communicating with StoryTeller (' + e + ')');
      res.json({ responseType: 'FAIL' });
    }
    checkGarbageCollection();
  }
  else {
    logMessageWithTimestamp('WARNING: Malformed request from ' + req.ip +' - [JSON RMI] StoryTeller.NextPage()');
    res.json({ responseType: 'FAIL' });
  }
});
app.post('/api/json/storyteller/pause', function(req, res) {
  if(req.body.requestType == 'RMI') {
    logMessageWithTimestamp('Incoming request from ' + req.ip +' - [JSON RMI] StoryTeller.Pause()');
    try {
      storyTeller.call('Pause');
      res.json({ responseType: 'SUCCESS' });
    }
    catch(e) {
      logMessageWithTimestamp('WARNING: Error communicating with StoryTeller (' + e + ')');
      res.json({ responseType: 'FAIL' });
    }
    checkGarbageCollection();
  }
  else {
    logMessageWithTimestamp('WARNING: Malformed request from ' + req.ip +' - [JSON RMI] StoryTeller.Pause()');
    res.json({ responseType: 'FAIL' });
  }
});
app.post('/api/json/storyteller/play', function(req, res) {
  if(req.body.requestType == 'RMI') {
    logMessageWithTimestamp('Incoming request from ' + req.ip +' - [JSON RMI] StoryTeller.Play()');
    try {
      storyTeller.call('Play');
      res.json({ responseType: 'SUCCESS' });
    }
    catch(e) {
      logMessageWithTimestamp('WARNING: Error communicating with StoryTeller (' + e + ')');
      res.json({ responseType: 'FAIL' });
    }
    checkGarbageCollection();
  }
  else {
    logMessageWithTimestamp('WARNING: Malformed request from ' + req.ip +' - [JSON RMI] StoryTeller.Play()');
    res.json({ responseType: 'FAIL' });
  }
});
app.post('/api/json/storyteller/previouspage', function(req, res) {
  if(req.body.requestType == 'RMI') {
    logMessageWithTimestamp('Incoming request from ' + req.ip +' - [JSON RMI] StoryTeller.PreviousPage()');
    try {
      storyTeller.call('PreviousPage');
      res.json({ responseType: 'SUCCESS' });
    }
    catch(e) {
      logMessageWithTimestamp('WARNING: Error communicating with StoryTeller (' + e + ')');
      res.json({ responseType: 'FAIL' });
    }
    checkGarbageCollection();
  }
  else {
    logMessageWithTimestamp('WARNING: Malformed request from ' + req.ip +' - [JSON RMI] StoryTeller.PreviousPage()');
    res.json({ responseType: 'FAIL' });
  }
});
app.post('/api/json/storyteller/reloadcurrentchapter', function(req, res) {
  if(req.body.requestType == 'RMI') {
    logMessageWithTimestamp('Incoming request from ' + req.ip +' - [JSON RMI] StoryTeller.ReloadCurrentChapter()');
    try {
      storyTeller.call('ReloadCurrentChapter');
      res.json({ responseType: 'SUCCESS' });
    }
    catch(e) {
      logMessageWithTimestamp('WARNING: Error communicating with StoryTeller (' + e + ')');
      res.json({ responseType: 'FAIL' });
    }
    checkGarbageCollection();
  }
  else {
    logMessageWithTimestamp('WARNING: Malformed request from ' + req.ip +' - [JSON RMI] StoryTeller.ReloadCurrentChapter()');
    res.json({ responseType: 'FAIL' });
  }
});
app.post('/api/json/storyteller/reloadcurrentpage', function(req, res) {
  if(req.body.requestType == 'RMI') {
    logMessageWithTimestamp('Incoming request from ' + req.ip +' - [JSON RMI] StoryTeller.ReloadCurrentPage()');
    try {
      storyTeller.call('ReloadCurrentPage');
      res.json({ responseType: 'SUCCESS' });
    }
    catch(e) {
      logMessageWithTimestamp('WARNING: Error communicating with StoryTeller (' + e + ')');
      res.json({ responseType: 'FAIL' });
    }
    checkGarbageCollection();
  }
  else {
    logMessageWithTimestamp('WARNING: Malformed request from ' + req.ip +' - [JSON RMI] StoryTeller.ReloadCurrentPage()');
    res.json({ responseType: 'FAIL' });
  }
});
app.post('/api/json/storyteller/rotatetoviewer', function(req, res) {
  if(req.body.requestType == 'RMI') {
    logMessageWithTimestamp('Incoming request from ' + req.ip +' - [JSON RMI] StoryTeller.RotateToViewer()');
    try {
      storyTeller.call('RotateToViewer');
      res.json({ responseType: 'SUCCESS' });
    }
    catch(e) {
      logMessageWithTimestamp('WARNING: Error communicating with StoryTeller (' + e + ')');
      res.json({ responseType: 'FAIL' });
    }
    checkGarbageCollection();
  }
  else {
    logMessageWithTimestamp('WARNING: Malformed request from ' + req.ip +' - [JSON RMI] StoryTeller.RotateToViewer()');
    res.json({ responseType: 'FAIL' });
  }
});
app.post('/api/json/storyteller/singlestep', function(req, res) {
  if(req.body.requestType == 'RMI') {
    logMessageWithTimestamp('Incoming request from ' + req.ip +' - [JSON RMI] StoryTeller.SingleStep()');
    try {
      storyTeller.call('SingleStep');
      res.json({ responseType: 'SUCCESS' });
    }
    catch(e) {
      logMessageWithTimestamp('WARNING: Error communicating with StoryTeller (' + e + ')');
      res.json({ responseType: 'FAIL' });
    }
    checkGarbageCollection();
  }
  else {
    logMessageWithTimestamp('WARNING: Malformed request from ' + req.ip +' - [JSON RMI] StoryTeller.SingleStep()');
    res.json({ responseType: 'FAIL' });
  }
});

// Start and advertise server
app.listen(serverPort, function() {
  if(showWelcomeMessage) {
    console.log('\nMagic Planet Web API (v'+ VERSION + '), ©2015 Global Imagination.\n');
  }
  if((serverName != '') && (serverName != null)) {
    console.log(getTimestamp() + ' Node.js server "' + serverName + '" now running on port ' + serverPort + ' <<Press Ctrl+C to stop>>');
    var mdnsAdvertisement = mdns.createAdvertisement(mdns.tcp(serviceType), serverPort, { name: serverName, txtRecord: { server_id: serverID } });
  }
  else {
    console.log(getTimestamp() + ' Node.js server now running on port ' + serverPort + ' <<Press Ctrl+C to stop...>>');
    var mdnsAdvertisement = mdns.createAdvertisement(mdns.tcp(serviceType), serverPort, { txtRecord: { server_id: serverID } });
  }
  mdnsAdvertisement.start();
  if(showWelcomeMessage) {
    console.log(getTimestamp() + ' Bonjour(mDNS/zeroconf) service advertising started');
  }
  var mdnsBrowser = mdns.createBrowser(mdns.tcp(serviceType));
  
  // mdnsBrowser.on('serviceUp', function(service) {
  //   console.log("service up: ", service);
  //   addDiscoveredService(service);
  // })
  // mdnsBrowser.on('serviceDown', function(service) {
  //   removeDiscoveredService(service);
  //   console.log("service down: ", service);
  // })
  // mdnsBrowser.start();
  if(showWelcomeMessage) {
    console.log(getTimestamp() + ' Bonjour(mDNS/zeroconf) service discovery started\n');
  }
});

// Utility functions
function logMessage(messageText) {
  if(showLogMessages) {
    console.log('         ' + messageText);
  }
}

function logMessageWithTimestamp(messageText) {
  if(showLogMessages) {
    console.log(getTimestamp() + ' ' + messageText);
  }
}

function getTimestamp() {
  var currentTime = new Date();
  var currentHours = currentTime.getHours();
  var currentMinutes = currentTime.getMinutes();
  var currentSeconds = currentTime.getSeconds();
  currentTime = null;
  if(currentHours < 10) {
    currentHours = '0' + currentHours;
  }
  if(currentMinutes < 10) {
    currentMinutes = '0' + currentMinutes;
  }
  if(currentSeconds < 10) {
    currentSeconds = '0' + currentSeconds;
  }
  return (currentHours + ':' + currentMinutes + ':' + currentSeconds);
}

function generateRandomString(stringLength) {
  var dictionary = 'abcdefgh12345678';
  var randomBytes = crypto.randomBytes(stringLength);
  var randomString = new Array(stringLength);
  var position = 0;
  for(var i = 0; i < stringLength; i++) {
    position += randomBytes[i];
    randomString[i] = dictionary[position % dictionary.length];
  }
  dictionary = null;
  randomBytes = null;
  position = null;
  return randomString.join('');
}

function addDiscoveredService(service) {
  var discoveredServerID = service.txtRecord.server_id;
  var discoveredServerName = service.name;
  var discoveredServerAddressA = service.addresses[0];
  var discoveredServerAddressB = service.addresses[1];
  var discoveredServerHost = service.host;
  var discoveredServerPort = service.port;
  if(discoveredServerID != serverID) {
    logMessageWithTimestamp('Bonjour(mDNS/zeroconf) service discovered: "' + discoveredServerName + '"');
    if(discoveredServerAddressA.indexOf(':') == -1) {
      logMessageWithTimestamp('Bonjour(mDNS/zeroconf) adding service');
      addPeer(discoveredServerName, discoveredServerAddressA, discoveredServerPort);
    }
    else {
      logMessageWithTimestamp('Bonjour(mDNS/zeroconf) found IPv6 address [' + discoveredServerAddressA + ']');
      logMessageWithTimestamp('Bonjour(mDNS/zeroconf) looking for IPv4 address');
      if((discoveredServerAddressB != null) && (discoveredServerAddressB != '') && (discoveredServerAddressB.indexOf(':') == -1)) {
        logMessageWithTimestamp('Bonjour(mDNS/zeroconf) found IPv4 address [' + discoveredServerAddressB + ']');
        logMessageWithTimestamp('Bonjour(mDNS/zeroconf) adding service');
        addPeer(discoveredServerName, discoveredServerAddressB, discoveredServerPort);
      }
      else {
        logMessageWithTimestamp('Bonjour(mDNS/zeroconf) no IPv4 address available');
        logMessageWithTimestamp('Bonjour(mDNS/zeroconf) looking for hostname instead');
        if((discoveredServerHost != null) && (discoveredServerHost != '')) {
          logMessageWithTimestamp('Bonjour(mDNS/zeroconf) found hostname: "' + discoveredServerHost + '"');
          if(discoveredServerHost.slice(-1) == '.') {
            discoveredServerHost = discoveredServerHost.slice(0, -1);
          }
          addPeer(discoveredServerName, discoveredServerHost, discoveredServerPort);
        }
        else {
          logMessageWithTimestamp('Bonjour(mDNS/zeroconf) no hostname available');
          logMessageWithTimestamp('Bonjour(mDNS/zeroconf) unable to add service');
        }
      }
    }
    listDiscoveredServices();
  }
  discoveredServerID = null;
  discoveredServerName = null;
  discoveredServerAddressA = null;
  discoveredServerAddressB = null;
  discoveredServerHost = null;
  discoveredServerPort = null;
}

function removeDiscoveredService(service) {
  var lostServerName = service.name;
  logMessageWithTimestamp('Bonjour(mDNS/zeroconf) service lost: "' + lostServerName + '"');
  removePeer(lostServerName);
  lostServerName = null;
  listDiscoveredServices();
}

function listDiscoveredServices() {
  logMessageWithTimestamp('Bonjour(mDNS/zeroconf) listing known peers...');
  logMessage('------------------------------------------------------------');
  if(servicePeers.length > 0) {
    for(var i = 0; i < servicePeers.length; i++) {
      logMessage((i + 1) + ') "' + servicePeers[i].serverName + '" [' + servicePeers[i].serverAddress + ']');
    }
  }
  else {
    logMessage('None');
  }
  logMessage('------------------------------------------------------------');
}

function addPeer(peerName, peerAddress, peerPort) {
  var peerUnknown = true;
  var tempAddress = '';
  for(var i = 0; i < servicePeers.length; i++) {
    if(servicePeers[i].serverName == peerName) {
      peerUnknown = false;
    }
  }
  if(peerUnknown) {
    servicePeers.push({ serverName: peerName, serverAddress: (peerAddress + ':' + peerPort) });
  }
  peerUnknown = null;
}

function removePeer(peerName) {
  for(var i = 0; i < servicePeers.length; i++) {
    if(servicePeers[i].serverName == peerName) {
      servicePeers.splice(i, 1);
    }
  }
}

function checkGarbageCollection() {
  if(oleGarbageCollection) {
    oleGarbageCollectionCounter++;
    if(oleGarbageCollectionCounter >= oleGarbageCollectionThreshold) {
      oleGarbageCollectionCounter = 0;
      logMessageWithTimestamp('INFORMATION: OLE garbage collection threshold reached');
      win32ole.force_gc_internal(1);
      logMessageWithTimestamp('INFORMATION: OLE garbage collection completed');
    }
  }
}

/* –– End ––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––– */ is my magicplanetwebserver.js and 
/* –– Client settings ––––––––––––––––––––––––––––––––––––––––––––––––––––––– */

var masterServerAddress = 'localhost';
var masterServerPort = 1337;
var masterServerName = 'Magic Planet';
var searchForOtherServers = true;
var searchForOtherServersAtStart = false;

/* –– End ––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––– */

/* –– Client callback functions ––––––––––––––––––––––––––––––––––––––––––––– */

function serverListUpdated() {
  // Add code here to update client UI when new servers are found
  document.getElementById('serversDropdownList').innerHTML = '';
  for(var i = 0; i < getNumberOfServers(); i++) {
    document.getElementById('serversDropdownList').innerHTML += ('<li><a href="#" onclick="setSelectedServerIndex(' + i + ')">' + getServerName(i) + '</a></li>');
  }
}

function errorMessageReceived(messageText) {
  // Add code here to display error messages in your client UI
  // Supplied argument messageText will be a String
}

function refreshUI() {
  // Add code here to be executed when the body HTML has finished loading
  // Can also be called by client code
  serverListUpdated();
}

/* –– End ––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––– */




/* –– DO NOT EDIT BELOW THIS LINE ––––––––––––––––––––––––––––––––––––––––––– */

// Client version
var VERSION = '0.7.5';

// Instantiate server array
var availableServers = [{ serverName: masterServerName, serverAddress: getMasterServerAddress() }];
var selectedServerIndex = 0;
if(searchForOtherServersAtStart) {
  refreshServersAsync();
}

// Helper function to insert new server information into server array
function addServer(newServerName, newServerAddress) {
  availableServers.push({ serverName: newServerName, serverAddress: newServerAddress });
}

// Helper function that returns the number a available servers
function getNumberOfServers() {
  return availableServers.length;
}

// Helper function that returns the name of the server at the provided index
function getServerName(index) {
  return availableServers[index].serverName;
}

// Helper function that returns the address of the server at the provided index
function getServerAddress(index) {
  return availableServers[index].serverAddress;
}

// Helper function to set the selected server to the provided index
function setSelectedServerIndex(index) {
  selectedServerIndex = index;
}

// Helper function that returns the currently selected server address and port
function getSelectedServerAddress() {
  return availableServers[selectedServerIndex].serverAddress;
}

// Function (synchronous) to request a list of peers from the master server
function refreshServers() {
  availableServers = [];
  addServer(masterServerName, getMasterServerAddress());
  selectedServerIndex = 0;
  if(searchForOtherServers) {
    var fetchedServerInfo = postJSONToSJAXServer(JSON.stringify({ requestType: 'GET' }), (getSelectedServerAddress() + '/api/json/servers'));
    if(fetchedServerInfo.responseType != 'FAIL') {
      availableServers[0].serverName = fetchedServerInfo.serverName;
      for(var i = 0; i < fetchedServerInfo.servicePeers.length; i++) {
        addServer(fetchedServerInfo.servicePeers[i].serverName, fetchedServerInfo.servicePeers[i].serverAddress);
      }
    }
  }
  serverListUpdated();
}

// Function (asynchronous) to request a list of peers from the master server
function refreshServersAsync() {
  availableServers = [];
  addServer(masterServerName, getMasterServerAddress());
  selectedServerIndex = 0;
  if(searchForOtherServers) {
    postJSONToAJAXServer(JSON.stringify({ requestType: 'GET' }), (getSelectedServerAddress() + '/api/json/servers'), refreshServersAsyncCallback);
  }
}

// Callback function for asynchronous response from refreshServersAsync()
function refreshServersAsyncCallback(fetchedServerInfo) {
  if(fetchedServerInfo.responseType != 'FAIL') {
    availableServers[0].serverName = fetchedServerInfo.serverName;
    for(var i = 0; i < fetchedServerInfo.servicePeers.length; i++) {
      addServer(fetchedServerInfo.servicePeers[i].serverName, fetchedServerInfo.servicePeers[i].serverAddress);
    }
  }
  serverListUpdated();
}

// Instantiate StoryTeller pseudo object
var StoryTeller = new storyControlWebObject();

// Define properties with getter/setter functions for StoryTeller pseudo object
Object.defineProperty(StoryTeller, 'AutoAdvance', { set: function(newValue) {
  postJSONToSJAXServer(JSON.stringify({ requestType: 'SET', requestValue: newValue }), (getSelectedServerAddress() + '/api/json/storyteller/autoadvance'));
}, get: function() {
  return (postJSONToSJAXServer(JSON.stringify({ requestType: 'GET' }), (getSelectedServerAddress() + '/api/json/storyteller/autoadvance'))).responseValue;
} });
Object.defineProperty(StoryTeller, 'Chapter', { set: function(newValue) {
  postJSONToSJAXServer(JSON.stringify({ requestType: 'SET', requestValue: newValue }), (getSelectedServerAddress() + '/api/json/storyteller/chapter'));
}, get: function() {
  return (postJSONToSJAXServer(JSON.stringify({ requestType: 'GET' }), (getSelectedServerAddress() + '/api/json/storyteller/chapter'))).responseValue;
} });
Object.defineProperty(StoryTeller, 'ChapterName', { set: function(newValue) {
  postJSONToSJAXServer(JSON.stringify({ requestType: 'SET', requestValue: newValue }), (getSelectedServerAddress() + '/api/json/storyteller/chaptername'));
}, get: function() {
  return (postJSONToSJAXServer(JSON.stringify({ requestType: 'GET' }), (getSelectedServerAddress() + '/api/json/storyteller/chaptername'))).responseValue;
} });
Object.defineProperty(StoryTeller, 'FlipPoles', { set: function(newValue) {
  postJSONToSJAXServer(JSON.stringify({ requestType: 'SET', requestValue: newValue }), (getSelectedServerAddress() + '/api/json/storyteller/flippoles'));
}, get: function() {
  return (postJSONToSJAXServer(JSON.stringify({ requestType: 'GET' }), (getSelectedServerAddress() + '/api/json/storyteller/flippoles'))).responseValue;
} });
Object.defineProperty(StoryTeller, 'Frame', { set: function(newValue) {
  postJSONToSJAXServer(JSON.stringify({ requestType: 'SET', requestValue: newValue }), (getSelectedServerAddress() + '/api/json/storyteller/frame'));
}, get: function() {
  return (postJSONToSJAXServer(JSON.stringify({ requestType: 'GET' }), (getSelectedServerAddress() + '/api/json/storyteller/frame'))).responseValue;
} });

Object.defineProperty(StoryTeller, 'FrameRate', { set: function(newValue) {
  postJSONToSJAXServer(JSON.stringify({ requestType: 'SET', requestValue: newValue }), (getSelectedServerAddress() + '/api/json/storyteller/framerate'));
}, get: function() {
  return (postJSONToSJAXServer(JSON.stringify({ requestType: 'GET' }), (getSelectedServerAddress() + '/api/json/storyteller/framerate'))).responseValue;
} });
Object.defineProperty(StoryTeller, 'Page', { set: function(newValue) {
  postJSONToSJAXServer(JSON.stringify({ requestType: 'SET', requestValue: newValue }), (getSelectedServerAddress() + '/api/json/storyteller/page'));
}, get: function() {
  return (postJSONToSJAXServer(JSON.stringify({ requestType: 'GET' }), (getSelectedServerAddress() + '/api/json/storyteller/page'))).responseValue;
} });
Object.defineProperty(StoryTeller, 'PageLatitudeOfInterest', { set: function(newValue) {
  postJSONToSJAXServer(JSON.stringify({ requestType: 'SET', requestValue: newValue }), (getSelectedServerAddress() + '/api/json/storyteller/pagelatitudeofinterest'));
}, get: function() {
  return (postJSONToSJAXServer(JSON.stringify({ requestType: 'GET' }), (getSelectedServerAddress() + '/api/json/storyteller/pagelatitudeofinterest'))).responseValue;
} });
Object.defineProperty(StoryTeller, 'PageLongitudeOfInterest', { set: function(newValue) {
  postJSONToSJAXServer(JSON.stringify({ requestType: 'SET', requestValue: newValue }), (getSelectedServerAddress() + '/api/json/storyteller/pagelongitudeofinterest'));
}, get: function() {
  return (postJSONToSJAXServer(JSON.stringify({ requestType: 'GET' }), (getSelectedServerAddress() + '/api/json/storyteller/pagelongitudeofinterest'))).responseValue;
} });
Object.defineProperty(StoryTeller, 'PageName', { set: function(newValue) {
  postJSONToSJAXServer(JSON.stringify({ requestType: 'SET', requestValue: newValue }), (getSelectedServerAddress() + '/api/json/storyteller/pagename'));
}, get: function() {
  return (postJSONToSJAXServer(JSON.stringify({ requestType: 'GET' }), (getSelectedServerAddress() + '/api/json/storyteller/pagename'))).responseValue;
} });
Object.defineProperty(StoryTeller, 'PlayAudio', { set: function(newValue) {
  postJSONToSJAXServer(JSON.stringify({ requestType: 'SET', requestValue: newValue }), (getSelectedServerAddress() + '/api/json/storyteller/playaudio'));
}, get: function() {
  return (postJSONToSJAXServer(JSON.stringify({ requestType: 'GET' }), (getSelectedServerAddress() + '/api/json/storyteller/playaudio'))).responseValue;
} });
Object.defineProperty(StoryTeller, 'PoleLatitude', { set: function(newValue) {
  postJSONToSJAXServer(JSON.stringify({ requestType: 'SET', requestValue: newValue }), (getSelectedServerAddress() + '/api/json/storyteller/polelatitude'));
}, get: function() {
  return (postJSONToSJAXServer(JSON.stringify({ requestType: 'GET' }), (getSelectedServerAddress() + '/api/json/storyteller/polelatitude'))).responseValue;
} });
Object.defineProperty(StoryTeller, 'PoleLongitude', { set: function(newValue) {
  postJSONToSJAXServer(JSON.stringify({ requestType: 'SET', requestValue: newValue }), (getSelectedServerAddress() + '/api/json/storyteller/polelongitude'));
}, get: function() {
  return (postJSONToSJAXServer(JSON.stringify({ requestType: 'GET' }), (getSelectedServerAddress() + '/api/json/storyteller/polelongitude'))).responseValue;
} });
Object.defineProperty(StoryTeller, 'RotateClockwise', { set: function(newValue) {
  postJSONToSJAXServer(JSON.stringify({ requestType: 'SET', requestValue: newValue }), (getSelectedServerAddress() + '/api/json/storyteller/rotateclockwise'));
}, get: function() {
  return (postJSONToSJAXServer(JSON.stringify({ requestType: 'GET' }), (getSelectedServerAddress() + '/api/json/storyteller/rotateclockwise'))).responseValue;
} });
Object.defineProperty(StoryTeller, 'Rotating', { set: function(newValue) {
  postJSONToSJAXServer(JSON.stringify({ requestType: 'SET', requestValue: newValue }), (getSelectedServerAddress() + '/api/json/storyteller/rotating'));
}, get: function() {
  return (postJSONToSJAXServer(JSON.stringify({ requestType: 'GET' }), (getSelectedServerAddress() + '/api/json/storyteller/rotating'))).responseValue;
} });
Object.defineProperty(StoryTeller, 'RotationAngle', { set: function(newValue) {
  postJSONToSJAXServer(JSON.stringify({ requestType: 'SET', requestValue: newValue }), (getSelectedServerAddress() + '/api/json/storyteller/rotationangle'));
}, get: function() {
  return (postJSONToSJAXServer(JSON.stringify({ requestType: 'GET' }), (getSelectedServerAddress() + '/api/json/storyteller/rotationangle'))).responseValue;
} });
Object.defineProperty(StoryTeller, 'RotationRate', { set: function(newValue) {
  postJSONToSJAXServer(JSON.stringify({ requestType: 'SET', requestValue: newValue }), (getSelectedServerAddress() + '/api/json/storyteller/rotationrate'));
}, get: function() {
  return (postJSONToSJAXServer(JSON.stringify({ requestType: 'GET' }), (getSelectedServerAddress() + '/api/json/storyteller/rotationrate'))).responseValue;
} });
Object.defineProperty(StoryTeller, 'ShowContent', { set: function(newValue) {
  postJSONToSJAXServer(JSON.stringify({ requestType: 'SET', requestValue: newValue }), (getSelectedServerAddress() + '/api/json/storyteller/showcontent'));
}, get: function() {
  return (postJSONToSJAXServer(JSON.stringify({ requestType: 'GET' }), (getSelectedServerAddress() + '/api/json/storyteller/showcontent'))).responseValue;
} });
Object.defineProperty(StoryTeller, 'Story', { set: function(newValue) {
  postJSONToSJAXServer(JSON.stringify({ requestType: 'SET', requestValue: newValue }), (getSelectedServerAddress() + '/api/json/storyteller/story'));
}, get: function() {
  return (postJSONToSJAXServer(JSON.stringify({ requestType: 'GET' }), (getSelectedServerAddress() + '/api/json/storyteller/story'))).responseValue;
} });

// Constructor method for StoryTeller pseudo object
function storyControlWebObject() {
  this.ChapterAndPage = function(newValue1, newValue2) {
    postJSONToSJAXServer(JSON.stringify({ requestType: 'RMI', requestValue1: newValue1, requestValue2: newValue2 }), (getSelectedServerAddress() + '/api/json/storyteller/chapterandpage'));
	}
	this.ChapterAndPageNames = function(newValue1, newValue2) {
    postJSONToSJAXServer(JSON.stringify({ requestType: 'RMI', requestValue1: newValue1, requestValue2: newValue2 }), (getSelectedServerAddress() + '/api/json/storyteller/chapterandpagenames'));
	}
	this.FirstFrame = function() {
    postJSONToSJAXServer(JSON.stringify({ requestType: 'RMI' }), (getSelectedServerAddress() + '/api/json/storyteller/firstframe'));
	}
	this.LastFrame = function() {
    postJSONToSJAXServer(JSON.stringify({ requestType: 'RMI' }), (getSelectedServerAddress() + '/api/json/storyteller/lastframe'));
	}
	this.NextPage = function() {
    postJSONToSJAXServer(JSON.stringify({ requestType: 'RMI' }), (getSelectedServerAddress() + '/api/json/storyteller/nextpage'));
	}
	this.Pause = function() {
    postJSONToSJAXServer(JSON.stringify({ requestType: 'RMI' }), (getSelectedServerAddress() + '/api/json/storyteller/pause'));
	}
	this.Play = function() {
    postJSONToSJAXServer(JSON.stringify({ requestType: 'RMI' }), (getSelectedServerAddress() + '/api/json/storyteller/play'));
	}
	this.PreviousPage = function() {
    postJSONToSJAXServer(JSON.stringify({ requestType: 'RMI' }), (getSelectedServerAddress() + '/api/json/storyteller/previouspage'));
	}
	this.ReloadCurrentChapter = function() {
    postJSONToSJAXServer(JSON.stringify({ requestType: 'RMI' }), (getSelectedServerAddress() + '/api/json/storyteller/reloadcurrentchapter'));
	}
	this.ReloadCurrentPage = function() {
    postJSONToSJAXServer(JSON.stringify({ requestType: 'RMI' }), (getSelectedServerAddress() + '/api/json/storyteller/reloadcurrentpage'));
	}
	this.RotateToViewer = function() {
    postJSONToSJAXServer(JSON.stringify({ requestType: 'RMI' }), (getSelectedServerAddress() + '/api/json/storyteller/rotatetoviewer'));
	}
	this.SingleStep = function() {
    postJSONToSJAXServer(JSON.stringify({ requestType: 'RMI' }), (getSelectedServerAddress() + '/api/json/storyteller/singlestep'));
	}
}

// Function to create a synchronous JSON transaction
function postJSONToSJAXServer(messageJSON, serverURI) {
  var xhr = new XMLHttpRequest();
  xhr.open('POST', checkProtocol(serverURI), false);
  xhr.setRequestHeader('Content-Type', 'application/json');
  xhr.send(messageJSON);
  var xhrResponse = JSON.parse(xhr.responseText);
  if(xhrResponse.responseType == 'SUCCESS') {
    return xhrResponse;
  }
  else {
    errorMessageReceived('Cannot connect to the selected server');
    return { responseType: 'FAIL', responseValue: '' };
  }
}

// Function to create an asynchronous JSON transaction
function postJSONToAJAXServer(messageJSON, serverURI, callbackFunction) {
  var xhr = new XMLHttpRequest();
  xhr.onreadystatechange = function() {
    if(xhr.readyState == 4 && xhr.status == 200) {
      var xhrResponse = JSON.parse(xhr.responseText);
      if(xhrResponse.responseType == 'SUCCESS') {
        callbackFunction(xhrResponse);
      }
      else {
        errorMessageReceived('Cannot connect to the selected server');
        callbackFunction({ responseType: 'FAIL', responseValue: '' });
      }
    }
    else {
      errorMessageReceived('Cannot connect to the selected server');
      callbackFunction({ responseType: 'FAIL', responseValue: '' });
    }
  }
  xhr.open('POST', checkProtocol(serverURI), true);
  xhr.setRequestHeader('Content-Type', 'application/json');
  xhr.send(messageJSON);
}

// Function to build master server address
function getMasterServerAddress() {
  return (masterServerAddress + ':' + masterServerPort)
}

// Function to check and prepend URI protocol
function checkProtocol(testURI) {
  if(testURI.slice(0, 6) != 'http://') {
    return ('http://' + testURI);
  }
  else {
    return testURI;
  }
}

/* –– End ––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––– */ this is my magicplanet webclient.js and this is my main.js starting server : function startServer() {

  const serverPath = path.join(__dirname, 'server', 'MagicPlanetWebServer.js');
  
  
  // const nodePath = 'C:\\Users\\U201\\AppData\\Roaming\\nvm\\v0.10.36\\node.exe';
  const nodePath = 'C:\\Users\\MagicPlanet\\AppData\\Roaming\\nvm\\v0.10.36\\node.exe';
  
  serverProcess = spawn(nodePath, [serverPath]);
  
  serverProcess.stdout.on('data', (data) => {
    console.log(`Server output: ${data}`);

    const output = data.toString();
    if (output.includes('Node.js server "Magic Planet" now running on port 1337')) {
      console.log("inside server sending signal")
      mainWindow.webContents.send('serverLoaded');
    }

  });

  serverProcess.stderr.on('data', (data) => {
    console.error(`Server error: ${data}`);
  });

  serverProcess.on('close', (code) => {
    console.log(`Server exited with code ${code}`);
  }); how to fix this issue