Nikolay Suslov 6 years ago
parent
commit
54df87359b
100 changed files with 1892 additions and 3164 deletions
  1. 16 3
      .gitignore
  2. 0 36
      LICENSE
  3. 22 31
      README.md
  4. 0 2
      documents/.gitignore
  5. 88 0
      index.js
  6. 0 321
      lib/nodejs/admin.js
  7. 0 53
      lib/nodejs/application.js
  8. 0 163
      lib/nodejs/file-cache.js
  9. 0 102
      lib/nodejs/helpers.js
  10. 0 86
      lib/nodejs/parse-url.js
  11. 0 320
      lib/nodejs/persistence.js
  12. 0 499
      lib/nodejs/reflector.js
  13. 0 34
      lib/nodejs/serve-handler.js
  14. 0 138
      lib/nodejs/serve.js
  15. 0 170
      lib/nodejs/storagefs.js
  16. 0 266
      lib/nodejs/vwf.js
  17. 0 28
      lib/nodejs/vwfCli.js
  18. 0 77
      node-server.js
  19. 0 150
      node_vwf.js
  20. 429 523
      package-lock.json
  21. 26 30
      package.json
  22. 0 14
      public/404.html
  23. 1289 0
      public/app.js
  24. 0 113
      public/assets/duck/duck.dae
  25. BIN
      public/assets/duck/duck.png
  26. 0 0
      public/defaults/assets/avatars/female/avatar1.gltf
  27. 0 0
      public/defaults/assets/avatars/female/best_avatar2-all.bin
  28. 0 0
      public/defaults/assets/avatars/female/brown_eye.png
  29. 0 0
      public/defaults/assets/avatars/female/eyebrow001.png
  30. 0 0
      public/defaults/assets/avatars/female/eyelashes01.png
  31. 0 0
      public/defaults/assets/avatars/female/long01_diffuse.png
  32. 0 0
      public/defaults/assets/avatars/female/young_lightskinned_female_diffuse.png
  33. 0 0
      public/defaults/assets/avatars/ico/female.jpg
  34. 0 0
      public/defaults/assets/avatars/ico/male.jpg
  35. 0 0
      public/defaults/assets/avatars/ico/simple.jpg
  36. 0 0
      public/defaults/assets/avatars/male/avatar1.bin
  37. 0 0
      public/defaults/assets/avatars/male/avatar1.gltf
  38. 0 0
      public/defaults/assets/avatars/male/brown_eye.png
  39. 0 0
      public/defaults/assets/avatars/male/eyebrow001.png
  40. 0 0
      public/defaults/assets/avatars/male/male_casualsuit01_diffuse.png
  41. 0 0
      public/defaults/assets/avatars/male/male_casualsuit01_normal.png
  42. 0 0
      public/defaults/assets/avatars/male/middleage_lightskinned_male_diffuse.png
  43. 0 0
      public/defaults/assets/avatars/male/short02_diffuse.png
  44. 0 0
      public/defaults/assets/bg.jpg
  45. 0 0
      public/defaults/assets/checker.jpg
  46. 0 0
      public/defaults/assets/lsys/Koch.jpg
  47. 0 0
      public/defaults/assets/lsys/SierpinskiCurve.jpg
  48. 0 0
      public/defaults/assets/lsys/SierpinskiTriangle.jpg
  49. 0 0
      public/defaults/assets/lsys/dragon.jpg
  50. 0 0
      public/defaults/assets/models/nwtree/materials.mtl
  51. 0 0
      public/defaults/assets/models/nwtree/model.obj
  52. 0 0
      public/defaults/assets/models/plane/PUSHILIN_PLANE.png
  53. 0 0
      public/defaults/assets/models/plane/PUSHILIN_Plane.mtl
  54. 0 0
      public/defaults/assets/models/plane/PUSHILIN_Plane.obj
  55. 0 0
      public/defaults/assets/skyes/sky1.jpg
  56. 0 0
      public/defaults/assets/skyes/sky2.jpg
  57. 0 0
      public/defaults/assets/skyes/sky3.jpg
  58. 0 0
      public/defaults/assets/test.dae
  59. 0 0
      public/defaults/proxy/vwf.example.com/aframe/a-asset-audio-item.vwf.yaml
  60. 0 0
      public/defaults/proxy/vwf.example.com/aframe/a-asset-image-item.vwf.yaml
  61. 0 0
      public/defaults/proxy/vwf.example.com/aframe/a-asset-item.vwf.yaml
  62. 0 0
      public/defaults/proxy/vwf.example.com/aframe/a-asset-video-item.vwf.yaml
  63. 0 0
      public/defaults/proxy/vwf.example.com/aframe/a-sound-component.js
  64. 0 0
      public/defaults/proxy/vwf.example.com/aframe/a-sound-component.vwf.yaml
  65. 0 0
      public/defaults/proxy/vwf.example.com/aframe/a-sun-sky.vwf.yaml
  66. 0 0
      public/defaults/proxy/vwf.example.com/aframe/aMaterialComponent.js
  67. 0 0
      public/defaults/proxy/vwf.example.com/aframe/aMaterialComponent.vwf.yaml
  68. 0 0
      public/defaults/proxy/vwf.example.com/aframe/aSceneFogComponent.vwf.yaml
  69. 0 0
      public/defaults/proxy/vwf.example.com/aframe/aanimation.vwf.yaml
  70. 0 0
      public/defaults/proxy/vwf.example.com/aframe/abox.vwf.yaml
  71. 0 0
      public/defaults/proxy/vwf.example.com/aframe/acamera.vwf.yaml
  72. 0 0
      public/defaults/proxy/vwf.example.com/aframe/acolladamodel.js
  73. 0 0
      public/defaults/proxy/vwf.example.com/aframe/acolladamodel.vwf.yaml
  74. 0 0
      public/defaults/proxy/vwf.example.com/aframe/acone.vwf.yaml
  75. 0 0
      public/defaults/proxy/vwf.example.com/aframe/acursor.vwf.yaml
  76. 0 0
      public/defaults/proxy/vwf.example.com/aframe/acylinder.vwf.yaml
  77. 0 0
      public/defaults/proxy/vwf.example.com/aframe/aentity.js
  78. 0 0
      public/defaults/proxy/vwf.example.com/aframe/aentity.vwf.yaml
  79. 0 0
      public/defaults/proxy/vwf.example.com/aframe/aentityComponent.vwf.yaml
  80. 0 0
      public/defaults/proxy/vwf.example.com/aframe/agltfmodel.js
  81. 0 0
      public/defaults/proxy/vwf.example.com/aframe/agltfmodel.vwf.yaml
  82. 0 0
      public/defaults/proxy/vwf.example.com/aframe/alight.vwf.yaml
  83. 0 0
      public/defaults/proxy/vwf.example.com/aframe/anim-mixer-component.vwf.yaml
  84. 0 0
      public/defaults/proxy/vwf.example.com/aframe/aobjmodel.js
  85. 0 0
      public/defaults/proxy/vwf.example.com/aframe/aobjmodel.vwf.yaml
  86. 0 0
      public/defaults/proxy/vwf.example.com/aframe/aplane.vwf.yaml
  87. 0 0
      public/defaults/proxy/vwf.example.com/aframe/app-avatarbvh-component.vwf.yaml
  88. 0 0
      public/defaults/proxy/vwf.example.com/aframe/app-cursor-listener-component.vwf.yaml
  89. 0 0
      public/defaults/proxy/vwf.example.com/aframe/app-envmap-component.vwf.yaml
  90. 0 0
      public/defaults/proxy/vwf.example.com/aframe/app-raycaster-listener-component.vwf.yaml
  91. 0 0
      public/defaults/proxy/vwf.example.com/aframe/app-skyshader-component.vwf.yaml
  92. 0 0
      public/defaults/proxy/vwf.example.com/aframe/app-sun-component.vwf.yaml
  93. 0 0
      public/defaults/proxy/vwf.example.com/aframe/ascene.js
  94. 0 0
      public/defaults/proxy/vwf.example.com/aframe/ascene.vwf.yaml
  95. 0 0
      public/defaults/proxy/vwf.example.com/aframe/asky.vwf.yaml
  96. 0 0
      public/defaults/proxy/vwf.example.com/aframe/asphere.vwf.yaml
  97. 0 0
      public/defaults/proxy/vwf.example.com/aframe/atext.vwf.yaml
  98. 17 4
      public/defaults/proxy/vwf.example.com/aframe/avatar.js
  99. 5 1
      public/defaults/proxy/vwf.example.com/aframe/avatar.vwf.yaml
  100. 0 0
      public/defaults/proxy/vwf.example.com/aframe/componentNode.vwf.yaml

+ 16 - 3
.gitignore

@@ -1,5 +1,18 @@
-# Node.js
+# See http://help.github.com/ignore-files/ for more about ignoring files.
+
+# dependencies
 node_modules
-/npm-debug.log
-cert
+
+# testing
+coverage
+
+# production
+build
+certs
+
+
+# misc
+.DS_Store
+.env
+npm-debug.log
 .vscode

+ 0 - 36
LICENSE

@@ -1,36 +0,0 @@
-LiveCoding.space
-Copyright (c) 2015 Krestianstvo.org
-The MIT License (MIT)
-
-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:
-
-The above copyright notice and this permission notice shall be included in all
-copies or substantial portions of the Software.
-
-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.
-
-
-VWF License
-Copyright 2012 United States Government, as represented by the Secretary of Defense, Under Secretary
-of Defense (Personnel & Readiness).
-
-Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in
-compliance with the License. You may obtain a copy of the License at
-
-    http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software distributed under the License is
-distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
-implied. See the License for the specific language governing permissions and limitations under the
-License.

+ 22 - 31
README.md

@@ -1,40 +1,31 @@
-# LiveCoding.space repository
+# LiveCoding.space
 
-> Collaborative Live Coding Space with support of user-defined languages and WebVR ready 3D graphics
+> Collaborative Live Coding Space with support of user-defined languages and **WebVR** ready 3D graphics
+Based on: **Virtual World Framework** | **A-Frame** | **Ohm language** | **OSC.js** | **Cell.js** | **GunDB** and more...
 
-Based on:  [Virtual World Framework](https://github.com/virtual-world-framework/vwf) | [A-Frame](https://aframe.io) | [Ohm language](https://github.com/harc/ohm) | [OSC.js](https://github.com/colinbdclark/osc.js/) | [Cell.js](https://github.com/intercellular/cell) and more...  
-Online at: **https://livecoding.space**
+> Online at: **[https://livecoding.space](https://livecoding.space)**
 
-![](https://livecoding.space/aframe2/webimg.jpg)
+![logo](https://krestianstvo.org/books/doc/sdk3/_images/webimg.jpg)
 
-**To run**
+## Architecture
 
-```
-npm install
-```
-```
-npm run
-```
+**[LiveCoding.space](https://livecoding.space)** ```v0.2``` architecture moves **[Virtual World Framework](https://en.wikipedia.org/wiki/Virtual_world_framework)** architecture towards pure-decentralized application by introducing:
 
-## Features
+- **single page web application**
+- **client-side router** (generating **instances IDs** by client) ([about Page.js](https://visionmedia.github.io/page.js/))
+- file storage independent **Reflector**
+- **GunDB storage system** for serving ```Proxy VWF components```, ```Worlds prototypes```, ```World save states```, ```User Inventories``` ect. in fully decentralized (peer-to-peer or multi-master) DB ([about GunDB](https://gun.eco/docs/Introduction))
+- **GunDB SEA** (Security, Encryption, Authorization) framework for user authorization and **P2P identities** ([about SEA](https://gun.eco/docs/Auth))
 
-- Decentralized network model for A-Frame components and entities based on VWF replicated computation architecture
-- Ohm language driver for sharing user-defined grammars, parsers, tokenisers inside virtual space
-- In browser Code and Properties editor based on Cell.js
-- OSC messaging through [OSC relay](https://github.com/NikolaySuslov/osc-relay-lcs) on the client
-- Avatars (Simple and GLTF models with animation)
-- Multi-window or multi-monitor/multi-machine setups with view offset cameras
-- WebRTC for video/audio streaming, 3D positional audio support
-- GearVR, Windows MixedReality motion controllers
-..
+alongside with the existed features from the initial version ```v0.1```:
 
-<img src="./public/doc/images/codeeditor.jpg" width="400">
-<br>
-<img src="./public/doc/images/osc.jpg" width="400">
-<br>
-<img src="./public/doc/images/avatar.jpg" width="400">
-<br>
+- **Decentralized network model for A-Frame components** and entities based on VWF replicated computation architecture
+- **Ohm language driver** for sharing user-defined grammars, parsers, tokenisers inside virtual space
+- **In browser Code and Properties editor** based on Cell.js
+- **OSC messaging** through OSC relay on the client
+- **Avatars** (Simple and GLTF models with animation)
+- Multi-window or multi-monitor/multi-machine setups with view **offset cameras**
+- **WebRTC** for video/audio streaming, 3D positional audio support
+- GearVR, Windows MixedReality motion **controllers** ..
 
-## Docs
-
-Soon..
+## [Documentation](https://krestianstvo.org/books/doc/sdk3)

+ 0 - 2
documents/.gitignore

@@ -1,2 +0,0 @@
-*
-!.gitignore

+ 88 - 0
index.js

@@ -0,0 +1,88 @@
+var express = require('express'),
+    compression = require('compression'),
+    serveStatic = require('serve-static'),
+    serveIndex = require('serve-index'),
+    cors = require('cors'),
+    morgan = require('morgan'),
+    path = require('path'),
+    fs = require('fs'),
+    argv = require('optimist').argv,
+    http = require('http'),
+    https = require('https');
+
+//  var privateKey  = fs.readFileSync('sslcert/server.key', 'utf8');
+// var certificate = fs.readFileSync('sslcert/server.crt', 'utf8');
+
+var app = express();
+var port = 3007;
+
+app.use(compression());
+app.use(serveStatic(__dirname + '/public'));
+app.use(cors());
+
+// app.use(function(req, res, next) {
+//   res.header("Access-Control-Allow-Origin", "*");
+//   res.header("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept, Authorization");
+//   next();
+// });
+
+app.use(morgan('combined'));
+
+/*=====Site specific paths=====*/
+
+// optional functions to load defaults (not required if DB is already bootstrapped)
+
+function readDirR(dir) {
+
+    if (fs.statSync(dir).isDirectory()) 
+    {
+      return Array.prototype.concat(...fs.readdirSync(dir).map(f => readDirR(path.join(dir, f))
+    ))
+    } else {
+      if ((dir.indexOf('.yaml') !== -1) || (dir.indexOf('.js') !== -1) || (dir.indexOf('.html') !== -1)
+      || (dir.indexOf('.json') !== -1))
+       return dir.replace(__dirname + '/public/', "/")
+    }
+}
+
+app.get('/proxy-files', function (req, res) {
+ // console.log(allFilesSync(__dirname + '/public/proxy/'));
+  res.writeHead(200, {"Content-Type": "application/json"});
+  let json = JSON.stringify(readDirR(__dirname + '/public/defaults/proxy/'));
+  res.end(json);
+});
+
+app.get('/world-files', function (req, res) {
+ // console.log(allFilesSync(__dirname + '/public/defaults/templates/'));
+  res.writeHead(200, {"Content-Type": "application/json"});
+  let json = JSON.stringify(readDirR(__dirname + '/public/defaults/worlds/'));
+  res.end(json);
+});
+
+
+// send all requests to index.html so browserHistory in React Router works
+app.get('*', function (req, res) {
+  res.sendFile(path.join(__dirname + '/public/', 'index.html'))
+})
+
+//=========end of specific===========
+
+// app.listen(port);
+// console.log('Web server is started on port: '+ port);
+
+
+
+var ssl = ( argv.s  || argv.ssl );
+var pass = ( ( argv.w) ? ( argv.w) : undefined );
+var sslOptions = {
+    key: ( ( argv.k || argv.key ) ? fs.readFileSync( argv.k || argv.key ) : undefined ),
+    cert: ( ( argv.c || argv.cert ) ? fs.readFileSync( argv.c || argv.cert ) : undefined ),
+    ca: ( ( argv.t || argv.ca ) ? fs.readFileSync( argv.t || argv.ca ) : undefined ),
+    passphrase: JSON.stringify(pass)
+};
+
+//create the server
+var port = ( ( argv.p || argv.port ) ? ( argv.p || argv.port ) : 3007 );
+
+var srv = ssl ? https.createServer( sslOptions, app ).listen( port ) : http.createServer( app ).listen( port );
+console.log( 'Serving on port ' + port );

+ 0 - 321
lib/nodejs/admin.js

@@ -1,321 +0,0 @@
-//  admin.js
-//  Helper functions for VWF node server that handle all /admin related requests.
-
-var helpers = require( './helpers'),
-    servehandler = require( './serve-handler' ),
-    fs = require( 'fs' ),
-    libpath = require( 'path' ),
-    url = require( 'url' ),
-    querystring = require( 'querystring' ),
-    serve = require( './serve' );
-
-
-
-// ServeTimeStateJSON is a helper function for serving the current 'time' status of 
-// an instance as a JSON object to the client.
-function ServeTimeStateJSON( request, response, instanceHash ) {
-    var instanceState = { "time": global.instances[ instanceHash ].getTime( ), "rate": global.instances[ instanceHash ].rate, "playing": global.instances[ instanceHash ].isPlaying( ), "paused": global.instances[ instanceHash ].isPaused( ), "stopped": global.instances[ instanceHash ].isStopped( ) };
-    serve.JSON( instanceState, response, url.parse( request.url, true ) );
-}
-    
-// HandleAdminState is a handler for dealing with the state GET requests
-// of the admin interface.
-// It returns the current time state of the instance as a JSON object.
-// If a query string is included, and the query string includes a rate value
-// the rate will be set first.
-// Returns true if it properly handles a properly formed request, otherwise
-// returns false.
-function HandleAdminState( request, response, instanceHash, queries ) {
-    if ( ( instanceHash ) && ( request.method == 'GET' ) ) {
-        if ( queries ) {
-            var floatRate = parseFloat( queries.rate );                            
-            if ( ( floatRate ) && ( floatRate > 0.0 ) ) {
-                global.instances[ instanceHash ].rate = floatRate;
-                global.instances[ instanceHash ].play( );
-            }
-        }
-        ServeTimeStateJSON( request, response, instanceHash );
-        return true;
-    }
-    return false;
-}
-
-// HandleAdminRate is a handler for dealing with the rate POST requests
-// of the admin interface.
-// After verifying that the post is for a valid instance (by testing if
-// instanceHash is set), and testing that this was an actual POST request,
-// then deal with the post itself. Store the posted data (break after a limited
-// amount of time to avoid malicious infinite POST type issues ). Once the
-// post ends, attempt to parse the data as a float, use said float to set
-// the rate.
-// Returns true if it properly handles a properly formed request, otherwise
-// returns false.
-function HandleAdminRate( request, response, instanceHash ) {
-    if ( instanceHash ) {
-        if ( request.method == 'POST' ) {
-            var rateData = "";
-            request.on( 'data', function ( data ) {
-                rateData += data;
-                if ( rateData.length > 1000 ) {
-                    rateData = "";
-                    response.writeHead( 413, { 'Content-Type': 'text/plain' } ).end( );
-                    request.connection.destroy();
-                }
-            } );
-            request.on( 'end', function( ) {
-                var parsedRate;
-                parsedRate = parseFloat( rateData );
-                if ( ( parsedRate ) && ( parsedRate > 0 ) ) {
-                    global.instances[ instanceHash ].rate = parsedRate;
-                    global.instances[ instanceHash ].play( );
-                    ServeTimeStateJSON( request, response, instanceHash );
-                }
-            } );
-            return true;
-        }
-    }
-    return false;
-}
-
-// HandleAdminPlay is a handler for dealing with the play POST requests
-// of the admin interface.
-// Despite not actually needing any data posted, allow some to be sent
-// but cap and destroy the connection after a brief period since this
-// shouldn't need any posted data, and we want to avoid malicious
-// endless POSTs.
-// Update the appropriate instance's flags so that it is playing.
-// Returns true if it properly handles a properly formed request, otherwise
-// returns false.
-function HandleAdminPlay( request, response, instanceHash ) {
-    if ( instanceHash ) {
-        if ( request.method == 'POST' ) {
-            var rateData = "";
-            request.on( 'data', function ( data ) {
-                if ( rateData.length > 10 ) {
-                    rateData = "";
-                    response.writeHead( 413, { 'Content-Type': 'text/plain' } ).end( );
-                    request.connection.destroy();
-                }
-            } );
-            global.instances[ instanceHash ].play( );
-            ServeTimeStateJSON( request, response, instanceHash );
-            return true;
-        }
-    }
-}
-
-// HandleAdminPause is a handler for dealing with the pause POST requests
-// of the admin interface.
-// Despite not actually needing any data posted, allow some to be sent
-// but cap and destroy the connection after a brief period since this
-// shouldn't need any posted data, and we want to avoid malicious
-// endless POSTs.
-// Update the appropriate instance's flags so that it is paused.
-// Returns true if it properly handles a properly formed request, otherwise
-// returns false.
-function HandleAdminPause( request, response, instanceHash ) {
-    if ( instanceHash ) {
-        if ( request.method == 'POST' ) {
-            global.instances[ instanceHash ].pause( );
-            ServeTimeStateJSON( request, response, instanceHash );
-            return true;
-        }
-    }
-}
-
-// HandleAdminPlay is a handler for dealing with the stop POST requests
-// of the admin interface.
-// Despite not actually needing any data posted, allow some to be sent
-// but cap and destroy the connection after a brief period since this
-// shouldn't need any posted data, and we want to avoid malicious
-// endless POSTs.
-// Update the appropriate instance's flags and time so that it is stopped.
-// Returns true if it properly handles a properly formed request, otherwise
-// returns false.
-function HandleAdminStop( request, response, instanceHash ) {
-    if ( instanceHash ) {
-        if ( request.method == 'POST' ) {
-            global.instances[ instanceHash ].stop( );
-            ServeTimeStateJSON( request, response, instanceHash );
-            return true;
-        }
-    }
-}
-
-// HandleAdminInstances is a handler for dealing with the 'instances' GET requests
-// of the admin interface.
-// This request returns a JSON object containing a list of the current instances of the
-// application it was invoked within the path of. (Each instance also contains a list of
-// clients attached to that instance).
-// Returns true if it properly handles a properly formed request, otherwise
-// returns false.
-function HandleAdminInstances( request, response, parsedRequest ) {
-    if ( ( parsedRequest[ 'public_path' ] ) && ( parsedRequest[ 'application' ] ) && ( request.method == "GET" ) ) {
-        var data = { };
-        var applicationInstanceRegexp = new RegExp("^" + helpers.JoinPath( parsedRequest[ 'public_path' ], parsedRequest[ 'application' ] ) + "/[0-9A-Za-z]{16}$");
-        for ( var i in global.instances ) {
-            if ( i.match( applicationInstanceRegexp ) ) {
-                data[ i ] = { "clients": { } };
-                for ( var j in global.instances[ i ].clients ) {
-                    data[ i ].clients[ j ] = null;
-                }
-            }
-        }
-        serve.JSON( data, response, url.parse( request.url, true ) );
-        return true;
-    }
-    return false;
-}
-
-// HandleAdminModels is a handler for dealing with the 'models' GET requests
-// of the admin interface.
-// The models request returns a JSON object containing a list of files stored in the 'public/models' directory.
-// Returns true if it properly handles a properly formed request, otherwise
-// returns false.
-function HandleAdminModels( request, response ) {
-    if ( request.method == "GET" ) {
-        var filenames = fs.readdirSync( "." + libpath.sep + "public" + libpath.sep + "demos" + libpath.sep +"models" + libpath.sep );
-        var data = [];
-        for ( var i in filenames ) {
-            var filedata = {};
-            var filedetails = fs.statSync( "." + libpath.sep + "public" + libpath.sep + "demos" + libpath.sep + "models" + libpath.sep + filenames[ i ] );
-            if ( filedetails.isFile( ) ) {
-                filedata[ 'url' ] = "http://" + request.headers.host + "/demos/models/" + filenames[ i ];
-                filedata[ 'basename' ] = filenames[ i ];
-                filedata[ 'size' ] = filedetails.size;
-                filedata[ 'mtime' ] = filedetails.mtime.toGMTString( );
-                data.push( filedata );
-            }
-        }
-        serve.JSON( data, response, url.parse( request.url, true ) );
-        return true;
-    }
-    return false;
-}
-
-// HandleAdminFiles is a handler for dealing with the 'files' GET requests
-// of the admin interface.
-// This request returns the list of json files found in the public_path directory of this
-// request.
-// Returns true if it properly handles a properly formed request, otherwise
-// returns false.
-function HandleAdminFiles( request, response, parsedRequest ) {
-    if ( ( parsedRequest[ 'public_path' ] ) && ( request.method == "GET" ) ) {
-        var filenames = fs.readdirSync( helpers.JoinPath( global.applicationRoot, parsedRequest[ 'public_path' ] ).replace( /\//g, libpath.sep ) );
-        var data = [];
-        for ( var i in filenames ) {
-            if ( filenames[ i ].match( /\.[jJ][sS][oO][nN]$/ ) ) {
-                var filedata = {};
-                var filedetails = fs.statSync( helpers.JoinPath( global.applicationRoot, parsedRequest[ 'public_path' ], filenames[ i ] ).replace( /\//g, libpath.sep ) );
-                if ( filedetails.helpers.IsFile( ) ) {
-                    filedata[ 'url' ] = "http://" + request.headers.host + helpers.JoinPath( parsedRequest[ 'public_path' ], filenames[ i ] );
-                    filedata[ 'basename' ] = filenames[ i ];
-                    filedata[ 'size' ] = filedetails.size;
-                    filedata[ 'mtime' ] = filedetails.mtime.toGMTString( );
-                    data.push( filedata );
-                }
-            }
-        }
-        serve.JSON( data, response, url.parse( request.url, true ) );
-        return true;
-    }
-    return false;
-}
-
-// HandleAdminConfig is a handler for dealing with the 'config' GET requests
-// of the admin interface.
-// This request returns contents of the config file for the application, whether it is
-// YAML or JSON. 
-// Returns true if it properly handles a properly formed request and the config file exists, otherwise
-// returns false.
-function HandleAdminConfig( request, response, parsedRequest ) {
-    if ( ( parsedRequest[ 'public_path' ] ) && ( parsedRequest[ 'application' ] ) && ( request.method == "GET" ) ) {
-        var filenameRoot = helpers.JoinPath( global.applicationRoot, parsedRequest[ 'public_path' ], parsedRequest[ 'application' ] );
-        if ( helpers.IsFile(  filenameRoot + ".config.yaml" ) ) {
-            serve.YAML( ( filenameRoot + ".config.yaml" ).replace( /\//g, libpath.sep ), response, url.parse( request.url, true ) );
-            return true;
-        }
-        else if ( helpers.IsFile( filenameRoot + ".config.json" ) ) {
-            serve.JSONFile( ( filenameRoot + ".config.json" ).replace( /\//g, libpath.sep ), response, url.parse( request.url, true ) );
-            return true;
-        }
-        else {
-            // If there is no config file, return an empty string to prevent 404 errors
-            serve.JSON("", response);
-        }
-    }
-    return false;
-}
-
-// HandleAdminChrome is a handler for dealing with the 'chrome' GET requests
-// of the admin interface.
-// This request returns the application's "chrome" HTML overlay or an empty string if non-existant.
-// Returns true if it properly handles a properly formed request, otherwise returns false. 
-function HandleAdminChrome( request, response, parsedRequest ) {
-    var publicPath = parsedRequest[ 'public_path' ];
-    var appName = parsedRequest[ 'application' ];
-    if ( publicPath && appName && ( request.method == "GET" ) ) {
-        var fileBasePath = helpers.JoinPath( global.applicationRoot, publicPath, appName );
-        var filePath = fileBasePath + ".html";
-        if ( helpers.IsFile( filePath ) ) {
-            servehandler.File( request, response, filePath );
-        } else {
-            response.end( "" );
-        }
-        return true;
-    }
-    return false;
-}
-
-// The Serve function in admin takes in the nodeJS request and nnodeJS response as well as 
-// the parsedRequest information.
-// It tests if the request is actually an admin request, and if calls an appropriate handler
-// to serve the appropriate response.
-// If the request is succesfully served, it returns TRUE, if the request is not succesfully
-// served (whether due to a file not being present, the request being malformed, or simply it
-// not being an admin request), then the function will return FALSE.
-function Serve( request, response, parsedRequest ) {
-    if ( parsedRequest[ 'private_path' ] ) {
-        var segments = helpers.GenerateSegments( parsedRequest[ 'private_path' ] );
-        if ( ( segments.length > 0 ) && ( segments[ 0 ] == "admin" ) ) {
-            if ( segments.length == 2 ) {
-                var queries = url.parse( request.url ).query;
-                if ( queries ) {
-                    queries = querystring.parse( queries );
-                }
-                var instanceHash = undefined;
-                if ( ( parsedRequest[ 'instance' ] ) && ( parsedRequest[ 'public_path' ] ) && ( parsedRequest[ 'application' ] ) ) {
-                    instanceHash = helpers.JoinPath( parsedRequest[ 'public_path' ], parsedRequest[ 'application' ], parsedRequest[ 'instance' ] );
-                }
-                if ( instanceHash && ( global.instances[ instanceHash ] == undefined ) ) {
-                    instanceHash = undefined;
-                }
-                switch ( segments[ 1 ] ) {
-                    case "state":
-                        return HandleAdminState( request, response, instanceHash, queries );
-                    case "rate":
-                        return HandleAdminRate( request, response, instanceHash );
-                    case "play":
-                        return HandleAdminPlay( request, response, instanceHash );
-                    case "pause":
-                        return HandleAdminPause( request, response, instanceHash );
-                    case "stop":
-                        return HandleAdminStop( request, response, instanceHash );
-                    case "instances":
-                        return HandleAdminInstances( request, response, parsedRequest );
-                    case "models":
-                        return HandleAdminModels( request, response );
-                    case "files":
-                        return HandleAdminFiles( request, response, parsedRequest );
-                    case "config":
-                        return HandleAdminConfig( request, response, parsedRequest );
-                    case "chrome":
-                        return HandleAdminChrome( request, response, parsedRequest );
-                }
-            }
-        }
-    }
-    return false;
-}
-
-exports.Serve = Serve;

+ 0 - 53
lib/nodejs/application.js

@@ -1,53 +0,0 @@
-// application.js
-// This file contains a function for serving the application specific requests.
-
-
-var servehandler = require( './serve-handler' ),
-    helpers = require( './helpers' ),
-    persistence = require( './persistence' ),
-    admin = require( './admin' );
-
-// Requests are attempted to be served as if they pertain to a specific application.
-// If any of these attempts succesfully serve the request, true is returned.
-// If these attempts do not succesfully serve the request, false is returned.
-function Serve( request, response, parsedRequest ) {
-    // The first step is to attempt to serve the file out of the support/client/lib directory structure.
-    // If there is no private path, and we have an application request, then we're the initial request and
-    // thus we should be serving the default 'load the application' index page, which lives at
-    // support/client/lib/index.html, hence the fileName logic.
-    var fileName = parsedRequest[ 'private_path' ];
-    if ( fileName == undefined ) {
-        fileName = "index.html";
-    }
-    if ( servehandler.File( request, response, helpers.JoinPath( global.vwfRoot + "/support/client/lib/", fileName ) ) ) {
-        return true;
-    }
-    // If the file was not served out of the support/client/lib path, the next attempt is to test if the file can be served out of
-    // the actual application path in the public directory.
-    if ( servehandler.File( request, response, helpers.JoinPath( global.applicationRoot, parsedRequest[ 'public_path' ], parsedRequest[ 'private_path' ] ) ) ) {
-        return true;
-    }
-    // If the file was not found in the application path within the public directory, the next step is to see if it is a component within the
-    // application path within the public directory.
-    if ( servehandler.Component( request, response, helpers.JoinPath( global.applicationRoot, parsedRequest[ 'public_path' ], parsedRequest[ 'private_path' ] ) ) ) {
-        return true;
-    }
-    // If the request was still not served, try and see if the persistence functionality will serve this request.
-    if ( persistence.Serve( request, response, parsedRequest ) ) {
-        return true;
-    }
-    // Finally, test if the admin functionality can serve this request.
-    if ( admin.Serve( request, response, parsedRequest ) ) {
-        return true;
-    }
-    //Nothing managed to serve this request, return false.
-    return false;
-}
-
-// Make the application functionality available via global since
-// persistence needs to be able to call this, and this cannot
-// require persistence AND have persistence require this.
-global.application = {};
-global.application.Serve = Serve;
-
-exports.Serve = Serve;

+ 0 - 163
lib/nodejs/file-cache.js

@@ -1,163 +0,0 @@
-//   file-cache.js
-//   This file contains the implementation of the file cache system used by the VWF nodeJS server.
-
-
-var fs = require( 'fs' ),
-    mime = require( 'mime' ),
-    zlib = require( 'zlib' );
-
-
-// Helper function to generate a hash for a string.
-function hash( str ) {
-    return require( 'crypto' ).createHash( 'md5' ).update( str ).digest( "hex" );
-}
-
-function _FileCache( ) {
-    this.files = [ ];
-    this.enabled = true;
-    this.clear = function ( ) {
-        this.files.length = 0;
-    };
-    
-    // Function for determining whether to treat file as binary or text
-    // based on file extentsion.
-    this.getDataType = function ( file ) {
-        var type = file.substr( file.lastIndexOf( '.' ) + 1 ).toLowerCase( );
-        if ( type === 'js' || type === 'html' || type === 'xml' || type === 'txt' || type === 'xhtml' || type === 'css' ) {
-            return "utf8";
-        }
-        else return "binary";
-    };
-
-    // Function for getting a file from the file cache.
-    // Asynchronous, returns file to the callback function.
-    // Passes null to the callback function if there is no such file.
-    this.getFile = function ( path, callback ) {
-        //First, check if we have already served this file
-        // and have it already cached, if so, return the
-        // already cached file.
-        for ( var i = 0; i < this.files.length; i++ ) {
-            if( this.files[ i ].path == path ) {    
-                global.log( 'serving from cache: ' + path, 2 );
-                callback( this.files[ i ] );
-                return;
-            }
-        }
-
-        // if got here, have no record of this file yet.
-        var datatype = this.getDataType( path );
-        var file = fs.readFileSync( path );
-        var stats = fs.statSync( path );
-
-        if ( file ) {
-            // The file was not already in the cache, but does exist.
-            // Gzip the file, which is an async process, and callback
-            // with the gzipped file entry when finished.
-            var self = this;
-            zlib.gzip( file, function ( _, zippeddata ) {
-                
-                var newentry = { };
-                newentry.path = path;
-                newentry.data = file;
-                newentry.stats = stats;
-                newentry.zippeddata = zippeddata;
-                newentry.contentlength = file.length;
-                newentry.datatype = datatype;
-                newentry.hash = hash( file );
-                
-                global.log( newentry.hash, 2 );
-                global.log( 'loading into cache: ' + path, 2 );
-                if ( self.enabled == true ) {
-                    self.files.push( newentry );
-                    var watcher = fs.watchFile( path, { }, function ( event, filename ) {
-                        global.log( this.entry.path + ' has changed on disk', 2 );
-                        self.files.splice( self.files.indexOf( this.entry ), 1 );
-                        fs.unwatchFile(path); //make sure not to create new watchers at every reload
-                    } );
-                    watcher.entry = newentry;
-                }
-                callback( newentry );
-                return;
-            } );
-
-            // Just returning since we're waiting on the async gzip process.
-            return;
-        }
-        // File was not in cache, and did not exists, return null.
-        callback( null );
-    };
-    
-    // Function to serve a file out of the filecache.
-    this.ServeFile = function ( request, filename, response, URL ) {
-        FileCache.getFile( filename, function ( file ) {
-            if ( !file ) {
-                response.writeHead( 500, {
-                    "Content-Type": "text/plain"
-                } );
-                response.write( 'file load error' + '\n');
-                response.end( );
-                return;
-            }
-
-            var type = mime.getType( filename );
-
-            if ( request.headers[ 'if-none-match' ] === file.hash ) {
-                response.writeHead( 304, {
-                    "Content-Type": type,
-                    "Last-Modified": encodeURIComponent(file.stats.mtime),
-                    "ETag": file.hash,
-                    "Cache-Control":"public; max-age=31536000" ,
-                } );
-                response.end( );
-                return;
-            }
-
-            if ( request.headers[ 'accept-encoding' ] && request.headers[ 'accept-encoding' ].indexOf( 'gzip' ) >= 0 ) {
-                response.writeHead( 200, {
-                    "Content-Type": type,
-                    "Last-Modified": encodeURIComponent(file.stats.mtime),
-                    "ETag": file.hash,
-                    "Cache-Control":"public; max-age=31536000" ,
-                    'Content-Encoding': 'gzip'
-                } );
-                response.write(file.zippeddata, file.datatype);
-            }
-            else if ( request.headers['range'] ) {
-                // To support the HTML 5 audio tag, the server needs to respond with the Content-Range in the header
-                    var range = request.headers['range'];
-                    range = range.split('=')[1];
-                    var ranges = range.split('-');
-                    var start = parseInt(ranges[0]);
-                    var end = parseInt(ranges[1]) || (file.contentlength - 1);
-
-
-                    var bytesheader = 'bytes ' + start + '-' + end + '/' + file.contentlength;
-
-                    response.writeHead(206, {
-                        "Content-Type": type,
-                        "Content-Length": (end - start) + 1,
-                        "Last-Modified": encodeURIComponent(file.stats.mtime),
-                        "ETag": file.hash,
-                        "Cache-Control": "public; max-age=31536000",
-                        "Accept-Ranges": "bytes",
-                        "Content-Range": bytesheader
-                    });
-                    var newdata = file.data.slice(start, end + 1);
-                    response.write(newdata, file.datatype);
-            }
-            else {
-                response.writeHead( 200, {
-                    "Content-Type": type,
-                    "Last-Modified": encodeURIComponent(file.stats.mtime),
-                    "ETag": file.hash,
-                    "Cache-Control":"public; max-age=31536000"
-                } );
-                response.write( file.data, file.datatype );
-            }
-            response.end( );
-        } );    
-    };
-}
-
-
-exports._FileCache = _FileCache;

+ 0 - 102
lib/nodejs/helpers.js

@@ -1,102 +0,0 @@
-//   helpers.js
-//   This file contains some low level helper functions for the VWF nodeJS server.
-
-var libpath = require( 'path' ),
-    fs = require( 'fs' );
-
-    
-// List of valid ID characters for use in an instance.
-var ValidIDChars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
-
-// IsInstanceID tests if the passed in potential Instance ID 
-// is a valid instance id.
-function IsInstanceID( potentialInstanceID ) {
-    if ( potentialInstanceID.match(/^[0-9A-Za-z]{16}$/) ) {
-        return true;
-    }
-    return false;
-}
-
-// GenerateInstanceID function creates a randomly generated instance ID.
-function GenerateInstanceID( ) {
-    var text = "";
-    
-    for( var i=0; i < 16; i++ )
-        text += ValidIDChars.charAt( Math.floor( Math.random( ) * ValidIDChars.length ) );
-
-    return text;
-}
-
-// JoinPath
-// Takes multiple arguments, joins them together into one path.
-function JoinPath( /* arguments */ ) {
-    var result = "";
-    if ( arguments.length > 0 ) {
-        if ( arguments[ 0 ] ) {
-            result = arguments[ 0 ];
-        }
-        for ( var index = 1; index < arguments.length; index++ ) {
-            var newSegment = arguments[ index ];
-            if ( newSegment == undefined ) {
-                newSegment = "";
-            }
-
-            if ( ( newSegment[ 0 ] == "/" ) && ( result[ result.length - 1 ] == "/" ) ) {
-                result = result + newSegment.slice( 1 );
-            } else if ( ( newSegment[ 0 ] == "/" ) || ( result[ result.length - 1 ] == "/" ) ) {
-                result = result + newSegment;
-            } else {
-                result = result + "/" + newSegment;
-            }
-            //result = libpath.join( result, newSegment );
-        }
-    }
-    return result;
-}
-
-// IsDirectory tests if the passed in path exists, and if it is a directory.
-function IsDirectory( path ) {
-    var seperatorFixedPath = path.replace( /\//g, libpath.sep );
-    if ( ! fs.existsSync( seperatorFixedPath ) ) {
-        return false;
-    }
-    return fs.statSync( seperatorFixedPath ).isDirectory();
-}
-
-
-// IsFile tests if the passed in path exists, and if it is a file.
-function IsFile( path ) {
-    var seperatorFixedPath = path.replace( /\//g, libpath.sep );
-    if ( ! fs.existsSync( seperatorFixedPath ) ) {
-        return false;
-    }
-    return fs.statSync( seperatorFixedPath ).isFile();
-}
-
-
-// GenerateSegments takes a string, breaks it into
-// '/' separated segments, and removes potential
-// blank first and last segments. 
-function GenerateSegments( argument ) {
-    var result = argument.split("/");
-    if ( result.length > 0 ) {
-        if ( result[ 0 ] == "" ) {
-            result.shift();
-        }
-    }
-    if ( result.length > 0 ) {
-        if ( result[ result.length - 1 ] == "" ) {
-            result.pop();
-        }
-    }
-    return result;
-}
-
-
-
-exports.JoinPath = JoinPath;
-exports.IsDirectory = IsDirectory;
-exports.IsFile = IsFile;
-exports.IsInstanceID = IsInstanceID;
-exports.GenerateSegments = GenerateSegments;
-exports.GenerateInstanceID = GenerateInstanceID;

+ 0 - 86
lib/nodejs/parse-url.js

@@ -1,86 +0,0 @@
-//   parse-url.js
-//   This file defines a helper function for parsing an incoming URL into
-//   the  Public Path, Application, Instance and Private Path components.
-
-
-//   incoming path                                       public_path             application     instance        private_path            
-
-//   /path/to/component                                       "/path/to/component"    "index.vwf"     nil             nil                     
-//   /path/to/component/                                      "/path/to/component"    "index.vwf"     nil             nil                     
-//   /path/to/component/path/to/client/file                   "/path/to/component"    "index.vwf"     nil             "path/to/client/file"   
-//   /path/to/component/path/to/component/file                "/path/to/component"    "index.vwf"     nil             "path/to/component/file"
-//   /path/to/component/socket/path                           "/path/to/component"    "index.vwf"     nil             "socket/path"           
-
-//   /path/to/component.vwf                                   "/path/to"              "component.vwf" nil             nil                     
-//   /path/to/component.vwf/                                  "/path/to"              "component.vwf" nil             nil                     
-//   /path/to/component.vwf/path/to/client/file               "/path/to"              "component.vwf" nil             "path/to/client/file"   
-//   /path/to/component.vwf/path/to/component/file            "/path/to"              "component.vwf" nil             "path/to/component/file"
-//   /path/to/component.vwf/socket/path                       "/path/to"              "component.vwf" nil             "socket/path"           
-
-//   /path/to/component/instance                              "/path/to/component"    "index.vwf"     "instance"      nil                     
-//   /path/to/component/instance/                             "/path/to/component"    "index.vwf"     "instance"      nil                     
-//   /path/to/component/instance/path/to/client/file          "/path/to/component"    "index.vwf"     "instance"      "path/to/client/file"   
-//   /path/to/component/instance/path/to/component/file       "/path/to/component"    "index.vwf"     "instance"      "path/to/component/file"
-//   /path/to/component/instance/socket/path                  "/path/to/component"    "index.vwf"     "instance"      "socket/path"           
-
-//   /path/to/component.vwf/instance                          "/path/to"              "component.vwf" "instance"      nil                     
-//   /path/to/component.vwf/instance/                         "/path/to"              "component.vwf" "instance"      nil                     
-//   /path/to/component.vwf/instance/path/to/client/file      "/path/to"              "component.vwf" "instance"      "path/to/client/file"   
-//   /path/to/component.vwf/instance/path/to/component/file   "/path/to"              "component.vwf" "instance"      "path/to/component/file"
-//   /path/to/component.vwf/instance/socket/path              "/path/to"              "component.vwf" "instance"      "socket/path"     
-
-
-var helpers = require( './helpers' );
-
-// List of valid extensions for VWF components.
-var template_extensions = [ "", ".yaml", ".json" ];
-
-
-function GetExtension( path ) {
-    if ( path.match( /\.vwf$/ ) ) {
-        for ( var index = 0; index < template_extensions.length; index++ ) {
-            if ( helpers.IsFile( helpers.JoinPath( global.applicationRoot, path + template_extensions[ index ] ) ) ) {
-                return template_extensions[ index ];
-            }
-        }
-    }
-    // else if ( path.match( /\.(dae|unity3d)$/ ) ) {
-    //     if ( helpers.IsFile( helpers.JoinPath( global.applicationRoot, path ) ) ) {
-    //         return "";
-    //     }
-    // }
-    return undefined;
-}
-
-function Process( updatedURL ) {
-    var result = { 'public_path': "/", 'application': undefined, 'instance': undefined, 'private_path': undefined };
-    var segments = helpers.GenerateSegments( updatedURL );
-    var extension = undefined;
-
-    while ( ( segments.length > 0 ) && ( helpers.IsDirectory( helpers.JoinPath( global.applicationRoot + result[ 'public_path' ], segments[ 0 ] ) ) ) ) {
-        result[ 'public_path' ] = helpers.JoinPath( result[ 'public_path' ], segments.shift( ) );
-    }
-
-    if ( ( segments.length > 0 ) && ( extension = GetExtension( helpers.JoinPath( result[ 'public_path' ], segments[ 0 ] ) ) ) ) {
-        result[ 'application' ] = segments.shift( );
-    } else if ( extension = GetExtension( helpers.JoinPath( result[ 'public_path' ], "index.vwf" ) ) ) {
-        result[ 'application' ] = "index.vwf";
-    } else if ( extension = GetExtension( helpers.JoinPath( result[ 'public_path' ], "index.dae" ) ) ) {
-        result[ 'application' ] = "index.dae";
-    } else if ( extension = GetExtension( helpers.JoinPath( result[ 'public_path' ], "index.unity3d" ) ) ) {
-        result[ 'application' ] = "index.unity3d";
-    }
-
-    if ( extension ) {
-        if ( ( segments.length > 0 ) && ( helpers.IsInstanceID( segments[ 0 ] ) ) ) {
-            result[ 'instance' ] = segments.shift();
-        }
-        if ( segments.length > 0 ) {
-            result[ 'private_path' ] = segments.join("/");
-        }
-    }
-    
-    return result;
-}
-
-exports.Process = Process;

+ 0 - 320
lib/nodejs/persistence.js

@@ -1,320 +0,0 @@
-//  persistence.js
-//  Helper functions for VWF node server that handles all persitence related requests
-
-var helpers = require( './helpers' ),
-    serve = require( './serve' ),
-    storage = require( './storagefs' ),
-    fs = require( 'fs' ),
-    url = require( 'url' ),
-    libpath = require( 'path' ),
-    querystring = require( 'querystring' );
-
-
-
-function CreateSaveDirectory( application_path, save_name, save_revision ) {
-    var application_segments = helpers.GenerateSegments( application_path );
-    var current_directory = "./documents";
-    while ( application_segments.length > 0 ) {
-        current_directory = helpers.JoinPath( current_directory, application_segments.shift() );
-        if ( ! helpers.IsDirectory( current_directory ) ) {
-            fs.mkdirSync( current_directory );
-        }
-    }
-    current_directory = helpers.JoinPath( current_directory, save_name );
-    if ( ! helpers.IsDirectory( current_directory ) ) {
-        fs.mkdirSync( current_directory );
-    }
-}
-
-// LoadSaveObject function is designed to take the load information provided by the
-// GetLoadInformation function, load and parse the specified file, and return that object
-// (or undefined, if there is no such file ).
-function LoadSaveObject( loadInfo ) {
-    if ( loadInfo[ 'save_name' ] ) {
-        var fileName = helpers.JoinPath( "./documents", loadInfo[ 'application_path' ], loadInfo[ 'save_name' ], "saveState_" + loadInfo[ 'save_revision' ] + ".vwf.json" );
-        if ( helpers.IsFile( fileName ) ) {
-            var fileContents = fs.readFileSync( fileName, "utf8" );
-            return JSON.parse( fileContents );
-        }
-    }
-    return undefined;
-}
-
-// LookupSaveRevisions takes the public path and the name of a save, and provides
-// an array of all revisions for that save. (If the save does not exist, this will be
-// an empty array).
-function LookupSaveRevisions( public_path, save_name ) {
-    var result = [ ];
-    var directoryName = helpers.JoinPath( "./documents/", public_path, save_name );
-    if ( helpers.IsDirectory( directoryName ) ) {
-        var potentialSaves = fs.readdirSync( directoryName.replace( /\//g, libpath.sep ) );
-        for ( var index = 0; index < potentialSaves.length; index++ ) {
-            if ( potentialSaves[ index ].match( /^saveState_\d+\.vwf\.json$/ ) ) {
-                result.push( parseInt( potentialSaves[ index ].slice( 10, potentialSaves[ index ].length - 9 ) ) );
-            }
-        }
-    }
-    return result;
-}
-
-// GetLoadInformation receives a parsed request {private_path, public_path, instance, application} and returns the
-// details of the save that is designated by the initial request. The details are returned in an object
-// composed of: save_name (name of the save) save_revision (revision of the save), explicit_revision (boolean, true if the request
-// explicitly specified the revision, false if it did not), and application_path (the public_path of the application this is a save for).
-function GetLoadInformation( parsedRequest ) {
-    var result = {'save_name': undefined, 'save_revision': undefined, 'explicit_revision': undefined, 'application_path': undefined };
-    if ( parsedRequest[ 'private_path' ] ) {
-        var segments = helpers.GenerateSegments( parsedRequest[ 'private_path' ] );
-        if ( ( segments.length > 1 ) && ( segments[ 0 ] == "load" ) ) {
-            var potentialRevisions = LookupSaveRevisions( parsedRequest[ 'public_path' ], segments[ 1 ] );
-            if ( potentialRevisions.length > 0 ) {
-                result[ 'save_name' ] = segments[ 1 ];
-                
-                if ( segments.length > 2 ) {
-                    var requestedRevision = parseInt( segments[ 2 ] );
-                    if ( requestedRevision ) {
-                       if ( potentialRevisions.indexOf( requestedRevision ) > -1 ) {
-                           result[ 'save_revision' ] = requestedRevision;
-                           result[ 'explicit_revision' ] = true;
-                           result[ 'application_path' ] = parsedRequest[ 'public_path' ];
-                       }
-                    }
-                }
-                if ( result[ 'explicit_revision' ] == undefined ) {
-                    result[ 'explicit_revision' ] = false;
-                    potentialRevisions.sort( );
-                    result[ 'save_revision' ] = potentialRevisions.pop();
-                    result[ 'application_path' ] = parsedRequest[ 'public_path' ];
-                }
-            }
-        }
-    }
-    return result;
-}
-
-// GetSaveInformation is a helper function that takes a NodeJS request, a NodeJS response,
-// and the application_path (/path/to/application). It returns an array of all saves found for that
-// application (including separate entries for individual revisions of saves ).
-function GetSaveInformation( request, response, application_path ) {
-    var result = [ ];
-    var potentialSaveNames = fs.readdirSync( helpers.JoinPath("./documents", application_path ) );
-    for ( var index = 0; index < potentialSaveNames.length; index++ ) {
-        if ( helpers.IsDirectory( helpers.JoinPath( "./documents", application_path, potentialSaveNames[ index ] ) ) ) {
-            var revisionList = LookupSaveRevisions( application_path, potentialSaveNames[ index ] );
-            var latestsave = true;
-            revisionList.sort();
-            while ( revisionList.length > 0 ) {
-                var newEntry = {};
-                newEntry[ 'applicationpath' ] = application_path;
-                newEntry[ 'savename' ] = potentialSaveNames[ index ];
-                newEntry[ 'revision' ] = revisionList.pop().toString();
-                newEntry[ 'latestsave' ] = latestsave;
-                if ( latestsave ) {
-                    newEntry[ 'url' ] = helpers.JoinPath( "http://" + request.headers.host, application_path, "load", potentialSaveNames[ index ] + "/" );
-                }
-                else {
-                    newEntry[ 'url' ] = helpers.JoinPath(  "http://" + request.headers.host, application_path, "load", potentialSaveNames[ index ] + "/", newEntry[ 'revision' ] + "/" );
-                }
-                latestsave = false;
-                result.push( newEntry );
-            }
-        }
-    }
-    return result;
-}
-
-// GetSaveInformationRecursive is a helper function that recursively calls the
-// GetSaveInformation function on a directory, and all directories within it (and within those, and so on).
-function GetSaveInformationRecursive( request, response, application_path ) {
-    var result = [];
-    var subDirectories = fs.readdirSync( helpers.JoinPath("./documents", application_path ) );
-    for ( var index = 0; index < subDirectories.length; index++ ) {
-        if ( helpers.IsDirectory( helpers.JoinPath( "./documents", application_path, subDirectories[ index ] ) ) ) {
-            result = result.concat( GetSaveInformationRecursive( request, response, helpers.JoinPath( application_path, subDirectories[ index ] ) ) );
-        }
-    }
-    result = result.concat( GetSaveInformation( request, response, application_path ) );
-    return result;
-}
-
-// HandlePersistenceLoad attempts to deal with a request which contains load arguments in the
-// private_path.
-// If the URL is properly formed, it will either (if the load portion of the URL is the last
-// portion of the URL, and there is no trailing slash) redirect to the same URL with a trailing
-// slash added, or if the URL is otherwise properly formed, it will remove the load specific
-// portions of the private_path, and pass this updated parsedRequest on to the normal application
-// level Serve function (effectively, load URL's are treated as if all load specific portions
-// of the URL don't exist, except for the websocket connection, which uses it to load the initial
-// state for the first client. ).
-function HandlePersistenceLoad( request, response, parsedRequest, segments ) {
-    var loadInformation = GetLoadInformation( parsedRequest );
-    if ( loadInformation[ 'save_name' ] != undefined ) {
-        segments.shift();
-        segments.shift();
-        if ( loadInformation[ 'explicit_revision' ] ) {
-            segments.shift();
-        }
-        var subParsedRequest = { };
-        subParsedRequest[ 'public_path' ] = parsedRequest[ 'public_path' ];
-        subParsedRequest[ 'application' ] = parsedRequest[ 'application' ];
-        if ( segments.length > 0 ) {
-            subParsedRequest[ 'private_path' ] = segments.join("/");
-        }
-        else {
-            subParsedRequest[ 'private_path' ] = undefined;
-        }
-        subParsedRequest[ 'instance' ] = parsedRequest[ 'instance' ];
-        if ( subParsedRequest[ 'instance' ] ) {
-            return global.application.Serve( request, response, subParsedRequest );
-        }
-        else if ( request.method == "GET" ) {
-            var redirectPath = parsedRequest[ 'public_path' ];
-            if ( parsedRequest[ 'application' ] ) {
-                redirectPath = helpers.JoinPath( redirectPath, parsedRequest[ 'application' ] );
-            }
-            redirectPath = helpers.JoinPath( redirectPath, helpers.GenerateInstanceID( ), parsedRequest[ 'private_path' ] );
-            if ( segments.length == 0 ) {
-                redirectPath = helpers.JoinPath( redirectPath, "/");
-            }
-            serve.Redirect( redirectPath, response );
-            return true;
-        }
-    }
-    return false;
-}
-
-function GenerateSaveObject( request, public_path, application, instance, save_name ) {
-    var result = {};
-    result[ "name" ] = save_name;
-    result[ "url" ] = helpers.JoinPath( "http://" + request.headers.host, public_path, application, instance, "saves", save_name );
-    result[ "vwf_info" ] = {};
-    result[ "vwf_info" ][ "public_path" ] = public_path;
-    result[ "vwf_info" ][ "application" ] = application;
-    result[ "vwf_info" ][ "path_to_application" ] = helpers.JoinPath( public_path, application );
-    result[ "vwf_info" ][ "instance" ] = instance;
-    var metadata = storage.GetSaveMetadata( public_path, application, instance, save_name );
-    if ( metadata ) {
-        result[ "metadata" ] = metadata;
-    }
-    else {
-        result[ "metadata" ] = {}
-    }
-    return result;
-    
-}
-
-function HandleSavesLoad( request, response, parsedRequest, segments ) {
-    // Saves are listed/loaded via GET, and saved via POST
-    if ( request.method == "GET" ) {
-        //Dealing with either loading a save state, or listing save states,
-        // or a malformed request.
-        if ( segments.length == 1 ) {
-            //No arguments beyond saves, must be listing save states.
-            // If instance was set, listing saves for that instance,
-            // otherwise listing saves for all instances of this application.
-            if ( parsedRequest[ 'instance' ] ) {
-                //Listing for specific instance.
-                var save_name_list = storage.ListInstanceSaveStates( parsedRequest[ 'public_path' ], parsedRequest[ 'application' ], parsedRequest[ 'instance' ] );
-                var save_objects = [ ];
-                for ( var index = 0; index < save_name_list.length; index++ ) {
-                    save_objects.push( GenerateSaveObject( request, parsedRequest[ 'public_path' ], parsedRequest[ 'application' ], parsedRequest[ 'instance' ], save_name_list[ index ] ) );
-                }
-                serve.JSON( save_objects, response, url.parse( request.url, true ) );
-                return true;
-            }
-            else {
-                var save_hash = storage.ListApplicationSaveStates( parsedRequest[ 'public_path' ], parsedRequest[ 'application' ] );
-                var instance_hash = { };
-                for ( instance_id in save_hash ) {
-                    var save_objects = [ ];
-                    for ( var index = 0; index < save_hash[ instance_id ].length; index++ ) {
-                        save_objects.push( GenerateSaveObject( request, parsedRequest[ 'public_path' ], parsedRequest[ 'application' ], instance_id, save_hash[ instance_id ][ index ] ) );
-                    }
-                    instance_hash[ instance_id ] = save_objects;
-                }
-                serve.JSON( instance_hash, response, url.parse( request.url, true ) );
-                return true;
-            }
-        } else  if ( segements.length == 2 ) {
-        }
-    } else if ( request.method == "POST" ) {
-    }
-    return false;
-}
-
-function HandlePersistenceSave( request, response, parsedRequest, segments ) {
-    if ( segments.length == 2 ) {
-        var saveName = segments[ 1 ];
-        var saveRevision = new Date().valueOf();
-        CreateSaveDirectory( parsedRequest[ 'public_path' ], saveName, saveRevision );
-        var persistenceData = "";
-        var persistenceLength = 0;
-        request.on( 'data', function ( data ) {
-            persistenceData += data;
-            if ( persistenceData.length > 50000000 ) {
-                persistenceData = "";
-                response.writeHead( 413, { 'Content-Type': 'text/plain' } ).end( );
-                request.connection.destroy();
-            }
-        } );
-        request.on( 'end', function( ) {
-            if ( persistenceData.length > 0 ) {
-                var processedPost = querystring.parse( persistenceData );
-                fs.writeFileSync( helpers.JoinPath( './documents', parsedRequest[ 'public_path' ], saveName, 'saveState' + processedPost[ "extension" ] ), processedPost[ "jsonState" ] );
-                fs.writeFileSync( helpers.JoinPath( './documents', parsedRequest[ 'public_path' ], saveName, 'saveState_' + saveRevision + processedPost[ "extension" ] ), processedPost[ "jsonState" ] );
-            }
-        } );
-        return true;
-    }
-    return false;
-}
-
-
-// The Serve function takes the nodeJS request, nodeJS response and the parsedRequest, and
-// attempts to see if it is a properly formed persistence related request.
-// If so, it serves the request, and returns true.
-// If it is an incorrectly formed persistence request, or not a persistence request
-// at all, then false will be returned. 
-function Serve( request, response, parsedRequest ) {
-    if ( parsedRequest[ 'private_path' ] ) {
-        var segments = helpers.GenerateSegments( parsedRequest[ 'private_path' ] );
-        if ( segments.length > 0 ) {
-            switch ( segments[ 0 ] ) {
-                case "listallsaves":
-                    if ( request.method == "GET" ) {
-                        var saveInfo = GetSaveInformationRecursive( request, response, "/" );
-                        serve.JSON( saveInfo, response, url.parse( request.url, true ) );
-                        return true;
-                     }
-                     return false;
-                case "listsaves":
-                    if ( request.method == "GET" ) {
-                        var saveInfo = GetSaveInformation( request, response, parsedRequest[ 'public_path' ] );
-                        serve.JSON( saveInfo, response, url.parse( request.url, true ) );
-                        return true;
-                    }
-                    return false;
-                case "listdescendentsaves":
-                    if ( request.method == "GET" ) {
-                        var saveInfo = GetSaveInformationRecursive( request, response, parsedRequest[ 'public_path' ] );
-                        serve.JSON( saveInfo, response, url.parse( request.url, true ) );
-                        return true;
-                    }
-                    return false;
-                case "load":
-                    return HandlePersistenceLoad( request, response, parsedRequest, segments );
-                case "saves":
-                    return HandleSavesLoad( request, response, parsedRequest, segments );
-                case "save":
-                    if ( ( request.method == "POST" ) && ( segments.length > 1 ) ) {
-                        return HandlePersistenceSave( request, response, parsedRequest, segments );
-                    }
-            }
-        }
-    }
-    return false;
-}
-
-exports.GetLoadInformation = GetLoadInformation;
-exports.Serve = Serve;
-exports.LoadSaveObject = LoadSaveObject;

+ 0 - 499
lib/nodejs/reflector.js

@@ -1,499 +0,0 @@
-"use strict";
-// reflector.js
-// 
-
-var parseurl = require( './parse-url' ),
-    persistence = require( './persistence' ),
-    helpers = require( './helpers' ),
-    fs = require( 'fs' );
-
-
-function parseSocketUrl( socket ) {
-
-    try
-    {
-        var query = require('url')
-            .parse(socket.handshake.url)
-            .query;
-       var referer = require('querystring')
-            .parse(query)
-            .pathname;
-        var resObj = require('querystring')
-        .parse(query)
-        .path;
-
-        var namespace = referer;
-        if(!namespace) return null;
-        if (namespace[namespace.length - 1] != "/")
-            namespace += "/";
-
-        let parsedPath = JSON.parse(resObj);
-            
-        if (parsedPath) {
-            return parsedPath
-        }
-        // else {
-        //     return parseurl.Process(namespace);
-        // } 
-
-       
-    }
-    catch (e)
-    {
-        return null;
-    }
-}
-
-
-
-function GetLoadForSocket( processedURL ) {
-    if ( processedURL[ 'private_path' ] ) {
-        return persistence.GetLoadInformation( processedURL );
-    }
-    return { 'save_name': undefined, 'save_revision': undefined, 'explicit_revision': undefined, 'application_path': undefined };
-}
-
-//Get the instance ID from the handshake headers for a socket
-function GetNamespace( processedURL ) {
-    if ( ( processedURL[ 'instance' ] ) && ( processedURL[ 'public_path' ] ) ) {
-        return helpers.JoinPath( processedURL[ 'public_path' ], processedURL[ 'application' ], processedURL[ 'instance' ] );
-    }
-    return undefined;
-}
-
-function GetNow( ) {
-    return new Date( ).getTime( ) / 1000.0;
-}
-
-function OnConnection( socket ) {
-
-    let resObj = parseSocketUrl( socket );
-    
-    if (resObj == null) {
-
-        setInterval(function() {
-
-   var address = socket.conn.request.headers.host;
-             var obj = {};
-             for (var prop in global.instances) {
-                obj[prop] =  {
-                    "instance":address + prop,
-                    "clients": Object.keys(global.instances[prop].clients).length
-                };
-            }
-            var json = JSON.stringify(obj);
-            socket.emit('getWebAppUpdate', json);
-}, 3000);
-
-//          socket.on('getWebAppUpdate', function(msg){
-          
-//   });
-
-        return
-    }
-
-    let processedURL = resObj.path;
-    //get instance for new connection
-    var namespace = GetNamespace( processedURL );
-    if ( namespace == undefined ) {
-        return;
-    }
-
-    //prepare for persistence request in case that's what this is
-    var loadInfo = resObj.loadInfo //GetLoadForSocket( processedURL );
-    var saveObject = resObj.saveObject //persistence.LoadSaveObject( loadInfo );
-	   
-    //if it's a new instance, setup record 
-    if( !global.instances[ namespace ] ) {
-        global.instances[ namespace ] = { };
-        global.instances[ namespace ].clients = { };
-        global.instances[ namespace ].pendingList = [ ];
-        global.instances[ namespace ].start_time = undefined;
-        global.instances[ namespace ].pause_time = undefined;
-        global.instances[ namespace ].rate = 1.0;
-        global.instances[ namespace ].setTime = function( time ) {
-            this.start_time = GetNow( ) - time;
-            this.pause_time = undefined;
-            this.rate = 1.0;
-        };
-        global.instances[ namespace ].isPlaying = function( ) {
-            if ( ( this.start_time != undefined ) && ( this.pause_time == undefined ) ) {
-                return true;
-            }
-            return false
-        };
-        global.instances[ namespace ].isPaused = function( ) {
-            if ( ( this.start_time != undefined ) && ( this.pause_time != undefined ) ) {
-                return true;
-            }
-            return false
-        };
-        global.instances[ namespace ].isStopped = function( ) {
-            if ( this.start_time == undefined ) {
-                return true;
-            }
-            return false;
-        };
-        global.instances[ namespace ].getTime = function( ) {
-            if ( this.isPlaying( ) ) {
-                return ( GetNow( ) - this.start_time ) * this.rate;
-            } else if ( this.isPaused( ) ) {
-                return ( this.pause_time - this.start_time ) * this.rate;
-            }
-            else {
-                return 0.0;
-            }
-        };
-        global.instances[ namespace ].play = function( ) {
-            if ( this.isStopped( ) ) {
-                this.start_time = GetNow( );
-                this.pause_time = undefined;
-            } else if ( this.isPaused( ) ) {
-                this.start_time = this.start_time + ( GetNow( ) - this.pause_time );
-                this.pause_time = undefined;
-            }
-        };
-        global.instances[ namespace ].pause = function( ) {
-            if ( this.isPlaying( ) ) {
-                this.pause_time = GetNow( );
-            }
-        };
-        global.instances[ namespace ].stop = function( ) {
-            if ( ( this.isPlaying( ) ) || ( this.isPaused( ) ) ) {
-                this.start_time = undefined;
-                this.pause_time = undefined;
-            }
-        };
-        global.instances[ namespace ].setTime( 0.0 );
-        if ( saveObject ) {
-            if ( saveObject[ "queue" ] ) {
-                if ( saveObject[ "queue" ][ "time" ] ) {
-                    global.instances[ namespace ].setTime( saveObject[ "queue" ][ "time" ] );
-                }
-             }
-        }
-        
-
-        global.instances[ namespace ].state = { };
-        
-        var log;
-        function generateLogFile() {
-            try {
-                if ( !fs.existsSync( './/log/' ) ) {
-                    fs.mkdir( './/log/', function ( err ) {
-                        if ( err ) {
-                            console.log ( err );
-                        } 
-                    })
-                }
-                log = fs.createWriteStream( './/log/' + namespace.replace( /[\\\/]/g, '_' ), { 'flags': 'a' } );
-            } catch( err ) {
-                console.log( 'Error generating Node Server Log File\n');
-            }
-        }
-
-        global.instances[ namespace ].Log = function ( message, level ) {
-            if( global.logLevel >= level ) {
-                if ( !log ) {
-                    generateLogFile();
-                }
-                log.write( message + '\n' );
-                global.log( message + '\n' );
-            }
-        };
-        
-        global.instances[ namespace ].Error = function ( message, level ) {
-            var red, brown, reset;
-            red   = '\u001b[31m';
-            brown  = '\u001b[33m';
-            reset = '\u001b[0m';
-            if ( global.logLevel >= level ) {
-                if ( !log ) {
-                    generateLogFile();
-                }
-                log.write( message + '\n' );
-                global.log( red + message + reset + '\n' );
-            }
-        };
-
-
-        //keep track of the timer for this instance
-        global.instances[ namespace ].timerID = setInterval( function ( ) {
-            var message = { parameters: [ ], time: global.instances[ namespace ].getTime( ) };
-            for ( var i in global.instances[ namespace ].clients ) {
-                var client = global.instances[ namespace ].clients[ i ];
-                if ( ! client.pending ) {
-                    client.emit( 'message', message );
-                }
-            }
-                if(global.instances[ namespace ]){
-            if ( global.instances[ namespace ].pendingList.pending ) {
-                global.instances[ namespace ].pendingList.push( message );
-            }
-        }
-        }, 50 );
-
-    }
-
-    //add the new client to the instance data
-    global.instances[ namespace ].clients[ socket.id ] = socket;	 
-
-    socket.pending = true;
-
-    //Get the descriptor for the `clients.vwf` child.
-    var clientDescriptor = GetClientDescriptor( socket );
-    
-        // The time for the setState message should be the time the new client joins, so save that time
-        var setStateTime = global.instances[ namespace ].getTime( );
-    
-        // If this client is the first, it can just load the application, and mark it not pending
-        if ( Object.keys( global.instances[ namespace ].clients ).length === 1 ) {
-    
-            if ( saveObject ) {
-                socket.emit( 'message', {
-                    action: "setState",
-                    parameters: [ saveObject ],
-                    time: global.instances[ namespace ].getTime( )
-                } );
-            }
-            else {
-                var instance = namespace;
-                //Get the state and load it.
-                //Now the server has a rough idea of what the simulation is
-    
-                socket.emit( 'message', { 
-                    action: "createNode", 
-                    parameters: [ "http://vwf.example.com/clients.vwf" ], 
-                    time: global.instances[ namespace ].getTime( ) 
-                } );
-    
-                socket.emit( 'message', { 
-                    action: "createNode", 
-                    parameters: [
-                        ( processedURL.public_path === "/" ? "" : processedURL.public_path ) + "/" + processedURL.application,
-                        "application"
-                    ],
-                    time: global.instances[ namespace ].getTime( )
-                } );
-            }
-    
-            socket.pending = false;
-    
-            //xapi.logClient( saveObject, loadInfo[ 'application_path' ], loadInfo[ 'save_name' ], namespace, clientDescriptor.properties || {}, true, true );
-        }
-        else {  //this client is not the first, we need to get the state and mark it pending
-            if ( ! global.instances[ namespace ].pendingList.pending ) {
-                var firstclient = Object.keys( global.instances[ namespace ].clients )[ 0 ];
-                firstclient = global.instances[ namespace ].clients[ firstclient ];
-                firstclient.emit( 'message', {
-                    action: "getState",
-                    respond: true,
-                    time: global.instances[ namespace ].getTime( )
-                } );
-                global.instances[ namespace ].Log( 'GetState from Client', 2 );
-                global.instances[ namespace ].pendingList.pending = true;
-            }
-            socket.pending = true;
-    
-        }
-    
-        //Create a child in the application's 'clients.vwf' global to represent this client.
-        var clientNodeMessage = {
-            action: "createChild",
-            parameters: [ "http://vwf.example.com/clients.vwf", socket.id, clientDescriptor ],
-            time: global.instances[ namespace ].getTime( )
-        };
-    
-        // Send messages to all the existing clients (that are not pending),
-        // telling them to create a new node under the "clients" parent for the new client
-        for ( var i in global.instances[ namespace ].clients ) {
-            var client = global.instances[ namespace ].clients[ i ];
-            if ( !client.pending ) {
-                client.emit ( 'message',  clientNodeMessage );
-            }
-        }
-        if ( global.instances[ namespace ].pendingList.pending ) {
-            global.instances[ namespace ].pendingList.push( clientNodeMessage );
-        }
-
-
-        socket.on( 'message', function ( msg ) {
-            
-                    //need to add the client identifier to all outgoing messages
-                    try {
-                        var message = JSON.parse( msg );
-                    }
-                    catch ( e ) {
-                        console.error( "Error on socket message: ", e );
-                        return;
-                    }
-            
-                    message.client = socket.id;
-                    message.time = global.instances[ namespace ].getTime( );
-            
-                    if ( message.result === undefined ) {
-            
-                        //distribute message to all clients on given instance
-                        for ( var i in global.instances[ namespace ].clients ) {
-                            var client = global.instances[ namespace ].clients[ i ];
-            
-                            //just a regular message, so push if the client is pending a load, otherwise just send it.
-                            if ( ! client.pending ) {
-                                client.emit( 'message', message );
-                            }
-                        }
-            
-                        if (global.instances[ namespace ]) {
-                        if ( global.instances[ namespace ].pendingList.pending ) {
-                            global.instances[ namespace ].pendingList.push( message );
-                        }
-                    }
-
-                    } else if ( message.action == "getState" ) {
-            
-                        //distribute message to all clients on given instance
-                        for ( var i in global.instances[ namespace ].clients ) {
-                            var client = global.instances[ namespace ].clients[ i ];
-            
-                            //if the message was get state, then fire all the pending messages after firing the setState
-                            if ( client.pending ) {
-                                global.instances[ namespace ].Log( 'Got State', 2 );
-                                var state = message.result;
-                                global.instances[ namespace ].Log( state, 2 );
-                                client.emit( 'message', { action: "setState", parameters: [ state ], time: setStateTime } );
-                                client.pending = false;
-                                for ( var j = 0; j < global.instances[ namespace ].pendingList.length; j++ ) {
-                                    client.emit( 'message', global.instances[ namespace ].pendingList[ j ] );
-                                }
-                                //xapi.logClient( state, undefined, undefined, namespace, GetClientDescriptor( client ).properties || {}, true, false );
-                            }
-                        }
-            
-                        global.instances[ namespace ].pendingList = [ ];
-            
-                    } else if ( message.action === "execute" ) {
-            
-                        var evaluation = socket.pendingEvaluations && socket.pendingEvaluations.shift();
-            
-                        if ( evaluation ) {
-                            evaluation.resolve( message.result );
-                            clearTimeout( evaluation.timeout );
-                        }
-            
-                    }
-            
-                } );
-
-    // When a client disconnects, go ahead and remove the instance data
-    socket.on( 'disconnect', function ( ) {
-        
-        // Remove the disconnecting client
-        var leavingClient = global.instances[ namespace ].clients[ socket.id ];
-        global.instances[ namespace ].clients[ socket.id ] = null;  
-        delete global.instances[ namespace ].clients[ socket.id ];
-        if ( leavingClient.pendingEvaluations ) {
-            leavingClient.pendingEvaluations.forEach( function( evaluation ) {
-                evaluation.reject( new Error( "connection closed" ) );
-                clearTimeout( evaluation.timeout );
-            } );
-        }
-
-        // Notify others of the disconnecting client.  Delete the child representing this client in the application's `clients.vwf` global.
-        var clientMessage = { action: "deleteChild", parameters: [ "http://vwf.example.com/clients.vwf", socket.id ], time: global.instances[ namespace ].getTime( ) };
-        for ( var i in global.instances[ namespace ].clients ) {
-            var client = global.instances[ namespace ].clients[ i ];
-            if ( ! client.pending ) {
-                client.emit ( 'message', clientMessage );
-            }
-        }
-        if ( global.instances[ namespace ].pendingList.pending ) {
-            global.instances[ namespace ].pendingList.push( clientMessage );
-        }
-        
-        // If it's the last client, delete the data and the timer
-        if ( Object.keys( global.instances[ namespace ].clients ).length == 0 ) {
-            clearInterval( global.instances[ namespace ].timerID );
-            delete global.instances[ namespace ];
-           // xapi.logClient( undefined, loadInfo[ 'application_path' ], loadInfo[ 'save_name' ], namespace, clientDescriptor.properties || {}, false, true );
-        } else {
-           // xapi.logClient( undefined, loadInfo[ 'application_path' ], loadInfo[ 'save_name' ], namespace, clientDescriptor.properties || {}, false, false );
-        }
-    } );
-}
-
-function Evaluate( namespace, node, expression ) {
-
-    return new Promise( function( resolve, reject ) {
-
-        var firstClientID = Object.keys( global.instances[ namespace ].clients )[ 0 ];
-        var firstClient = global.instances[ namespace ].clients[ firstClientID ];
-
-        if ( firstClient ) {
-            firstClient.pendingEvaluations = firstClient.pendingEvaluations || [];
-            firstClient.pendingEvaluations.push( {
-                resolve: resolve,
-                reject: reject,
-                timeout: setTimeout( function() { reject( new Error( "timeout" ) ) }, 1000 ),
-            } );
-            firstClient.emit( "message", { node: node, action: "execute", parameters: [ expression ], respond: true, time: global.instances[ namespace ].getTime() } );
-        } else {
-            reject( new Error( "no clients are connected" ) );
-        }
-
-    } );
-
-}
-
-/// Get a descriptor for the `clients.vwf` child for a new client. An authenticator may set a
-/// descriptor in the session at `session.vwf.client`. If the authenticator doesn't provide a
-/// descriptor, use an empty node inheriting from `client.vwf`.
-
-function GetClientDescriptor( socket ) {
-
-    // socket.io doesn't provide access to the request and the session, but we do have the cookies.
-    // Create a mock request and run it through the session middleware to recreate the session. This
-    // creates a session object at `mockRequest.session`.
-
-    var mockRequest = {
-        headers: { cookie: socket.handshake.headers.cookie },
-        connection: {},
-        session: {},
-    };
-
-    var mockResponse = {
-        getHeader: function() {},
-        setHeader: function() {},
-    };
-
-    sessionStack.forEach( function( middleware ) {
-        middleware( mockRequest, mockResponse, function() {} );
-    } );
-
-    // Get the descriptor from `vwf.client` in the session.
-
-    var descriptor = ( mockRequest.session.vwf || {} ).client || {};
-
-    // Set the default prototype.
-
-    if ( ! descriptor.extends ) {
-        descriptor.extends = "http://vwf.example.com/client.vwf";
-    }
-
-    return descriptor;
-
-}
-
-/// Middleware stack to parse a cookie session from `req.headers.cookie` into `req.session`.
-
-var sessionStack = [
-    //cookieParser(),
-    //cookieSession( { secret: config.get( 'session.secret' ) } ),
-];
-
-function GetInstances() {
-    return global.instances;
-}
-
-exports.OnConnection = OnConnection;
-exports.Evaluate = Evaluate;
-exports.GetInstances = GetInstances;

+ 0 - 34
lib/nodejs/serve-handler.js

@@ -1,34 +0,0 @@
-//   serve-handler.js
-//   This file defines a helper function for parsing an incoming URL into
-//   the  Public Path, Application, Instance and Private Path components.
-
-var serve = require( './serve' ),
-    helpers = require( './helpers' ),
-    url = require( 'url' ),
-    libpath = require( 'path' );
-
-function File( request, response, filename ) {
-    if ( ( helpers.IsFile( filename ) ) && ( request.method == "GET" ) ) {
-        serve.File( request, filename.replace( /\//g, libpath.sep ), response, url.parse( request.url, true ) );
-        return true;
-    }
-    return false;
-}
-
-function Component( request, response, filename ) {
-    if ( request.method == "GET" ) {
-        if ( helpers.IsFile( filename + ".yaml" ) ) {
-            serve.YAML( filename.replace( /\//g, libpath.sep ) + ".yaml", response, url.parse( request.url, true ) );
-            return true;
-        }
-        if ( helpers.IsFile ( filename + ".json" ) ) {
-            serve.JSONFile( filename.replace( /\//g, libpath.sep ) + ".json", response, url.parse( request.url, true ) );
-            return true;
-        }
-    }
-    return false;
-}
-
-
-exports.File = File;
-exports.Component = Component;

+ 0 - 138
lib/nodejs/serve.js

@@ -1,138 +0,0 @@
-//   serve.js
-//   This file contains the low level helper functions that the VWF nodeJS server uses to
-//   actually serve content.
-
-
-var filecache = require( './file-cache' ),
-    YAML = require( 'js-yaml' ),
-    fs = require( 'fs' ),
-    helpers = require( './helpers' );
-
-// First, if the FileCache has not been instantiated, do so.
-if ( global.FileCache == undefined ) {
-    var FileCache = new filecache._FileCache( );
-    global.FileCache = FileCache;
-}
-
-//  Basic helper function to redirect a request.
-function ServeRedirect( url, response ) {
-    response.writeHead( 302, { 'Location': url } );
-    response.end();
-}
-
-//Just serve a simple file, using the FileCache implementation.
-function ServeFile( request, filename, response, URL ) {
-    FileCache.ServeFile( request, filename, response, URL );
-}
-
-//Return a 404 file and not found code
-function _404 ( response, file404 ) {
-    var url404 = helpers.JoinPath( global.applicationRoot, file404 );
-    if ( helpers.IsFile( url404 ) ) {
-        fs.readFile( url404, function( error, data ) {
-            response.writeHead( 404, {'content-type': 'text/html'} );
-            response.end( data );
-        });
-    } else {
-        response.writeHead( 404, {
-            "Content-Type": "text/plain",
-            "Access-Control-Allow-Origin": "*"
-        } );
-        response.write( "404 Not Found\n" );
-        response.end();        
-    }
-}
-
-// Parse and serve a JSON file
-// Note: If we receive a 'callback' query, we need to
-//       serve the file as application/javascript and
-//       adjust slightly.
-function ServeJSONFile( filename, response, URL ) {
-    fs.readFile( filename, "utf8", function ( err, data ) {
-        if ( err ) {
-            response.writeHead( 500, {
-                "Content-Type": "text/plain"
-            } );
-            response.write( err + "\n" );
-            response.end();
-            return;
-        }
-
-        var type = "application/json";
-
-        var jsonString = data;
-
-        var callback = URL.query.callback;
-        if ( callback ) {
-            jsonString = callback + "(" + data + ")";
-            type = "application/javascript";
-        }
-        
-        response.writeHead( 200, {
-            "Content-Type": type
-        } );
-        
-        response.write( jsonString, "utf-8" );
-        
-        response.end();
-    } );
-}
-
-//Parse and serve a YAML file
-function ServeYAML( filename, response, URL ) {
-    var tf = filename;
-    fs.readFile( filename, "utf8", function ( err, data ) {
-        if (err) {
-            response.writeHead( 500, {
-                "Content-Type": "text/plain"
-            } );
-            response.write( err + "\n" );
-            response.end();
-            return;
-        }
-
-        // Remove the Byte Order Mark (BOM) if one exists
-        var file = data.replace( /^\uFEFF/, '' );
-
-        //global.log(tf);
-        try {
-            var deYAML = JSON.stringify( YAML.load( file ) );
-        } catch ( e ) {
-            global.log( "error parsing YAML " + filename );
-            _404( response );
-            return;
-        }
-
-        var type = "application/json";
-
-        var callback = URL.query.callback;
-
-        if ( callback ) {
-            deYAML = callback + "(" + deYAML + ")";
-            type = "application/javascript";
-        }
-
-        response.writeHead( 200, {
-            "Content-Type": type
-        } );
-        response.write( deYAML, "utf8" );
-        response.end();			
-    } );
-}
-
-
-//Serve a JSON object
-function ServeJSON( jsonobject, response, URL ) {
-    response.writeHead( 200, {
-        "Content-Type": "application/json"
-    } );
-    response.write( JSON.stringify( jsonobject ), "utf8" );
-    response.end();	
-}
-
-exports.Redirect = ServeRedirect;
-exports.File = ServeFile;
-exports._404 = _404;
-exports.JSONFile = ServeJSONFile;
-exports.YAML = ServeYAML;
-exports.JSON = ServeJSON;

+ 0 - 170
lib/nodejs/storagefs.js

@@ -1,170 +0,0 @@
-// storagefs.js
-// This file contains the functions that handle filesystem based storage.
-
-var helpers = require( './helpers' ),
-    fs = require( 'fs' ),
-    url = require( 'url' ),
-    libpath = require( 'path' );
-
-
-var storage_root = "./documents";
-
-function list_application_instances( public_path, application ) {
-    var result = [ ];
-    var potential_app_dir = helpers.JoinPath( storage_root, public_path, application );
-    if ( helpers.IsDirectory( potential_app_dir ) ) {
-        var potential_instances = fs.readdirSync( potential_app_dir.replace( /\//g, libpath.sep ) );
-        for ( var index = 0; index < potential_instances; index++ ) {
-            if ( ( helpers.IsDirectory( helpers.JoinPath( potential_app_dir, potential_instances[ index ] ) ) ) && ( potential_instances[ index ].match( /^instance_*/ ) ) ) {
-                var potential_instance_id = potential_instances[ index ].slice( 9 );
-                if ( ( get_instance_metadata( public_path, application, potential_instance_id ) != undefined ) && ( get_persistence_state( public_path, application, potential_instance_id ) != undefined ) && ( list_instance_save_states( public_path, application, potential_instance_id ).length == 0 ) ) {
-                    result.push( potential_instance_id );
-                }
-            }
-        }
-    }
-    return result.sort();
-}
-
-function get_instance_metadata( public_path, application, instance ) {
-    result = undefined;
-    potential_filename = helpers.JoinPath( storage_root, public_path, application, "instance_" + instance, "metadata.json" );
-    if ( helpers.IsFile( potential_filename ) ) {
-        var file_contents = fs.readFileSync( potential_filename, "utf8" );
-        result = JSON.parse( file_contents );
-    }
-    return result;
-}
-
-function set_instance_metadata( public_path, application, instance, metadata ) {
-    var segments = helpers.generateSegments( public_path );
-    segments.push( application );
-    segments.push( "instance_" + instance );
-    var current_path = storage_root;
-    for ( var index = 0; index < segments.length; index++ ) {
-        current_path = helpers.JoinPath( current_path, segments[ index ] );
-        if ( !( helpers.IsDirectory( current_path ) ) ) {
-            fs.mkdirSync( current_path );
-        }
-    }
-    fs.writeFileSync( helpers.JoinPath( storage_root, public_path, application, "instance_" + instance, "metadata.json" ), JSON.stringify( metadata ) );    
-}
-
-
-function get_persistence_state( public_path, application, instance ) {
-    result = undefined;
-    potential_filename = helpers.JoinPath( storage_root, public_path, application, "instance_" + instance, "persistenceState.vwf.json" );
-    if ( helpers.IsFile( potential_filename ) ) {
-        var file_contents = fs.readFileSync( potential_filename, "utf8" );
-        result = JSON.parse( file_contents );
-    }
-    return result;
-}
-
-// WARNING: Take note, if an instance already has metadata, but you provide undefined metadata, the
-//          old metadata will persist.
-function set_persistence_state( public_path, application, instance, state, metadata ) {
-    var segments = helpers.generateSegments( public_path );
-    segments.push( application );
-    segments.push( "instance_" + instance );
-    var current_path = storage_root;
-    for ( var index = 0; index < segments.length; index++ ) {
-        current_path = helpers.JoinPath( current_path, segments[ index ] );
-        if ( !( helpers.IsDirectory( current_path ) ) ) {
-            fs.mkdirSync( current_path );
-        }
-    }
-    fs.writeFileSync( helpers.JoinPath( storage_root, public_path, application, "instance_" + instance, "persistenceState.vwf.json" ), JSON.stringify( state ) );
-    if ( metadata ) {
-        fs.writeFileSync( helpers.JoinPath( storage_root, public_path, application, "instance_" + instance, "metadata.json" ), JSON.stringify( metadata ) ); 
-    }
-}
-
-function list_instance_save_states( public_path, application, instance ) {
-    var result = [ ];
-    var potential_dir_name = helpers.JoinPath( storage_root, public_path, application, "instance_" + instance );
-    if ( helpers.IsDirectory( potential_dir_name ) ) {
-        var potential_save_names = fs.readdirSync( potential_dir_name.replace( /\//g, libpath.sep ) );
-        for ( var index = 0; index < potential_save_names.length; index++ ) {
-            if ( ( potential_save_names[ index ].match( /^save_*/ ) ) && ( helpers.IsDirectory( helpers.JoinPath( potential_dir_name, potential_save_names[ index ] ) ) ) ) {
-                if ( helpers.IsFile( helpers.JoinPath( potential_dir_name, potential_save_names[ index ], "saveState.vwf.json" ) ) ) {
-                    result.push( potential_save_names[ index ].slice( 5 ) );
-                }
-            }
-        }
-    }
-    return result.sort();
-}
-
-function list_application_save_states( public_path, application ) {
-    result = {};
-    var application_dir_name = helpers.JoinPath( storage_root, public_path, application );
-    if ( helpers.IsDirectory( application_dir_name ) ) {
-        var potential_instances = fs.readdirSync( application_dir_name.replace( /\//g, libpath.sep ) );
-        for ( var index = 0; index < potential_instances.length; index++ ) {
-            if ( potential_instances[ index ].match( /^instance_*/ ) ) {
-                var instance_id = potential_instances[ index ].slice( 9 );
-                var instance_saves = list_instance_save_states( public_path, application, instance_id );
-                if ( instance_saves.length > 0 ) {
-                    result[ instance_id ] = instance_saves;
-                }
-            }
-        }
-    }
-    return result;
-}
-
-function get_save_metadata( public_path, application, instance, save_name ) {
-    result = undefined;
-    potential_filename = helpers.JoinPath( storage_root, public_path, application, "instance_" + instance, "save_" + save_name, "metadata.json" );
-    if ( helpers.IsFile( potential_filename ) ) {
-        var file_contents = fs.readFileSync( potential_filename, "utf8" );
-        result = JSON.parse( file_contents );
-    } else {
-        potential_filename = helpers.JoinPath( storage_root, public_path, application, "instance_" + instance, "metadata.json" );
-        if ( helpers.IsFile( potential_filename ) ) {
-            var file_contents = fs.readFileSync( potential_filename, "utf8" );
-            result = JSON.parse( file_contents );
-        }
-    }
-    return result;
-}
-
-function get_save_state( public_path, application, instance, save_name ) {
-    result = undefined;
-    potential_filename = helpers.JoinPath( storage_root, public_path, application, "instance_" + instance, "save_" + save_name, "saveState.vwf.json" );
-    if ( helpers.IsFile( potential_filename ) ) {
-        var file_contents = fs.readFileSync( potential_filename, "utf8" );
-        result = JSON.parse( file_contents );
-    }
-    return result;
-}
-
-function set_save_state( public_path, application, instance, save_name, save_state, metadata ) {
-    var segments = helpers.generateSegments( public_path );
-    segments.push( application );
-    segments.push( "instance_" + instance );
-    segments.push( "save_" + save_name );
-    var current_path = storage_root;
-    for ( var index = 0; index < segments.length; index++ ) {
-        current_path = helpers.JoinPath( current_path, segments[ index ] );
-        if ( !( helpers.IsDirectory( current_path ) ) ) {
-            fs.mkdirSync( current_path );
-        }
-    }
-    fs.writeFileSync( helpers.JoinPath( storage_root, public_path, application, "instance_" + instance, "save_" + save_name, "saveState.vwf.json" ), JSON.stringify( save_state ) );
-    if ( metadata ) {
-        fs.writeFileSync( helpers.JoinPath( storage_root, public_path, application, "instance_" + instance, "save_" + save_name, "metadata.json" ), JSON.stringify( metadata ) ); 
-    }
-}
-
-exports.ListApplicationInstances = list_application_instances;
-exports.GetInstanceMetadata = get_instance_metadata;
-exports.SetInstanceMetadata = set_instance_metadata;
-exports.GetPersistenceState = get_persistence_state;
-exports.SetPersistenceState = set_persistence_state;
-exports.ListInstanceSaveStates = list_instance_save_states;
-exports.ListApplicationSaveStates = list_application_save_states;
-exports.GetSaveMetadata = get_save_metadata;
-exports.GetSaveState = get_save_state;
-exports.SetSaveState = set_save_state;

+ 0 - 266
lib/nodejs/vwf.js

@@ -1,266 +0,0 @@
-"use strict";
-// vwf.js
-// This file contains the functions that handle the top level parsing and responses to
-// requests for the VWF nodeJS server.
-
-var parseurl = require( './parse-url' ),
-    serve = require( './serve' ),
-    persistence = require( './persistence' ),
-    servehandler = require( './serve-handler' ),
-    helpers = require( './helpers' ),
-    application = require( './application' ),
-    url = require( 'url' );
-
-// HandleParsableRequest takes the incoming request, and uses the helper library functions to parse the
-// URL into its 'public_path, application, instance and private_path' components, and then attempts to redirect
-// or serve that request as appropriate. If it succesfully completes the request, it returns true, otherwise it
-// returns false.
-function HandleParsableRequest( request, response ) {
-    var parsedRequest = parseurl.Process( url.parse(request.url).pathname );
-
-    // if (parsedRequest.application == undefined) {
-    //     return false
-    // }
-
-    // Used to check if the URL referer was an application instance. Components added by the "includes" keyword 
-    // in yaml are loaded using jQuery which appends a query parameter to handle the callback. Checking the referer
-    // allows those URLs to be handled correctly, instead of treating them as a new application that needs an instance ID.
-    var parsedReferer = request.headers.referer ? parseurl.Process( url.parse(request.headers.referer).pathname ) : undefined;
-
-    if ( ( request.url[ request.url.length - 1 ] != "/" ) && ( parsedRequest[ 'private_path' ] == undefined ) && ( url.parse( request.url ).search == undefined || 
-        ( parsedReferer && parsedReferer[ 'instance' ] != undefined ) ) ) { // If the referer was an application, allow it even if it has query parameters
-        
-        var browserIsIE8 = ( request.headers['user-agent'] ) && ( request.headers['user-agent'].indexOf("MSIE 8.0" ) > -1 );
-        var urlIsUnsupportedPage = ( request.url.indexOf("/web/unsupported.html") !== -1 );
-        var refererIsUnsupportedPage = ( request.headers.referer && ( request.headers.referer.indexOf( "/web/unsupported.html" ) !== -1 ) );
-        if ( browserIsIE8 && !( urlIsUnsupportedPage || refererIsUnsupportedPage ) ) {
-            serve.Redirect( "/web/unsupported.html", response ); // Redirect unsupported browsers to web/docs/unsupported.html
-            return true;
-        }
-        else if ( ( parsedRequest[ 'instance' ] == undefined ) && ( request.headers['accept'].indexOf( "text/html" ) == -1 ) ) {
-            return servehandler.Component( request, response, helpers.JoinPath( global.applicationRoot, parsedRequest[ 'public_path' ], parsedRequest[ 'application' ] ) );
-        }
-        else if ( parsedRequest[ 'instance' ] == undefined && request.headers['accept'].indexOf( "text/html" ) != -1 && helpers.IsFile( helpers.JoinPath( global.applicationRoot, request.url ) ) ) {
-            return servehandler.File( request, response, helpers.JoinPath( global.applicationRoot, request.url ) );
-        }
-        else {
-            serve.Redirect( request.url + "/", response );
-            return true;
-        }
-    }
-    else if ( ( parsedRequest[ 'instance' ] == undefined ) && ( parsedRequest[ 'private_path' ] == undefined ) ) {
-
-        if ( request.url != "/" ) {
-
-
-            // Redirect if the requested url is either a specified directory or application 
-            if ( helpers.IsDirectory( helpers.JoinPath( global.applicationRoot + request.url )) || parsedRequest['application'] != undefined ) {
-                
-                // Get the driver specific url parameters if applicable
-                var queryString = url.parse( request.url ).search;
-                if ( queryString == undefined ) {
-                   serve.Redirect( request.url + helpers.GenerateInstanceID( ), response );
-                   return true;
-               }
-               else {
-
-                   // Tack on the driver specific configuration parameters 
-                   serve.Redirect( helpers.JoinPath( url.parse( request.url ).pathname, helpers.GenerateInstanceID( ), queryString.replace( /\/$/, '' ) ), response );
-                   return true;
-
-               }
-            }
-        }
-        else if ( isDefaultApp( request.url ) ) {
-
-            // Redirect if the url request does not include an application/file && a default 'index.vwf.yaml' exists
-            serve.Redirect( request.url + helpers.GenerateInstanceID( ), response );
-            return true;
-        } else {
-            return false;
-        }
-          
-    }
-    else {
-        return application.Serve( request, response, parsedRequest );
-    }
-}
-
-// Assuming no application or file was specified in the url request, check for the existance of 
-// the default 'index.vwf.yaml' in either applicationRoot or cwd.
-function isDefaultApp ( requestURL ) {
-
-    if ( helpers.IsFile( helpers.JoinPath( global.applicationRoot, "/index.vwf.yaml" ) )  
-        || helpers.IsFile( helpers.JoinPath( process.cwd(), "/index.vwf.yaml" ) ) ) {
-        return true;
-    }
-     
-    return false;    
-}
-
-// HandleProxyRequest attempts to identify any of the 'proxy' based URL paths and serves then attempts to
-// serve them out of the the support/proxy subdirectories.
-// If the request is identified as being a proxy request and succesfully served, this returns true,
-// if it is not a proxy request (or it is a proxy request, but fails due to the file not being present),
-// then this will return false.
-function HandleProxyRequest( request, response ) {
-    var updatedURL = url.parse( request.url ).pathname;
-    var segments = helpers.GenerateSegments( updatedURL );
-    if ( ( segments.length > 0 ) && ( segments[ 0 ] == "proxy" ) ) {
-        if ( servehandler.File( request, response, helpers.JoinPath( global.vwfRoot + "/support/", updatedURL ) ) ) {
-            return true;
-        }
-        if ( servehandler.Component( request, response, helpers.JoinPath( global.vwfRoot + "/support/", updatedURL ) ) ) {
-            return true;
-        }
-    }
-    return false;
-}
-
-// HandleFileRequest simply attempts to handle the incoming URL as if it is a direct request for a file within the public directory
-// structure.
-// The function returns true if a file is succesfully served, false if it is not.
-function HandleFileRequest( request, response ) {
-    var updatedURL = url.parse( request.url ).pathname;
-    var segments = helpers.GenerateSegments( updatedURL );
-    if ( segments.length == 0 ) {
-        updatedURL = "/index.html";
-    }
-    return servehandler.File( request, response, helpers.JoinPath( global.applicationRoot, updatedURL ) );
-}
-
-function HandleWebAppRequest(request, response) {
-    var updatedURL = url.parse(request.url).pathname;
-
-    var address = request.headers.host;
-    var res = false;
-
-    switch (updatedURL) {
-        case '/app':
-            response.writeHead(200, { 'content-type': 'text/html' });
-            console.log(global.instances);
-            for (var prop in global.instances) {
-                response.write("<a href=http://" + address + prop + ">" + prop + "</a>" + "<br>");
-            }
-            response.end();
-            res = true;
-            break;
-
-         case '/allinstances.json':
-            response.writeHead(200, {"Content-Type": "application/json"});
-            var obj = {};
-            console.log(global.instances);
-            for (var prop in global.instances) {
-                //var name = prop.split('/');
-
-                obj[prop] =  {
-                    "instance":address + prop,
-                    "clients": Object.keys(global.instances[prop].clients).length
-                };
-                //response.write("<a href=http://" + address + prop + ">" + prop + "</a>" + "<br>");
-            }
-            var json = JSON.stringify(obj);
-            response.end(json);
-            res = true;
-            break;
-
-        default:
-            break;
-
-     }
-
-    return res;
-}
-
-function parseUrlForReflector( pathName ) {
-
-    try
-    {
-        var namespace = pathName;
-        if(!namespace) return null;
-        if (namespace[namespace.length - 1] != "/")
-            namespace += "/";
-
-        return parseurl.Process( namespace );
-       
-    }
-    catch (e)
-    {
-        return null;
-    }
-}
-
-function GetLoadForSocket( processedURL ) {
-    if ( processedURL[ 'private_path' ] ) {
-        return persistence.GetLoadInformation( processedURL );
-    }
-    return { 'save_name': undefined, 'save_revision': undefined, 'explicit_revision': undefined, 'application_path': undefined };
-}
-
-
-function HandleReflectorRequest(request, response) {
-    //var updatedURL = url.parse(request.url).pathname;
-
-   // var address = request.headers.host;
-    var res = false;
-
-    if (request.method === 'POST' && request.url === '/parseurl') {
-        let body = [];
-        request.on('data', (chunk) => {
-          body.push(chunk);
-        }).on('end', () => {
-          body = Buffer.concat(body).toString();
-          let pathUrl = JSON.parse(body);
-          let refPath = parseUrlForReflector( pathUrl.url );
-
-        //prepare for persistence request in case that's what this is
-        let loadInfo = GetLoadForSocket( refPath );
-        let saveObject = persistence.LoadSaveObject( loadInfo );
-
-        let resObj = {
-            path: refPath,
-            loadInfo: loadInfo,
-            saveObject: saveObject
-        }
-          //console.log(resObj);
-        let json = JSON.stringify(resObj);
-         response.writeHead(200, {"Content-Type": "application/json"});
-          response.end(json);
-        });
-      } else {
-        //response.statusCode = 404;
-        //response.end();
-      }
-
-    return res;
-}
-
-
-// Serve is the top level function for serving requests. It first attempts to 
-// serve the request based on parsing the incoming URL.
-// If that fails, it continues to attempt to serve the request as a 'proxy' request,
-// if that also does not serve anything to the request, then an attempt is made
-// to handle the request as a simple direct request for a file within the public
-// directory structure.
-// If all that fails, serve up a 404 response since the request was not handled.
-function Serve( request, response ) {
-    var handledRequest = HandleReflectorRequest( request, response );
-    if ( ! ( handledRequest ) ) {
-        handledRequest = HandleParsableRequest( request, response ); }
-    if ( ! ( handledRequest ) ) {
-        handledRequest = HandleProxyRequest( request, response );
-    }
-    if ( ! ( handledRequest ) ) {
-        handledRequest = HandleFileRequest( request, response );
-    }
-    if ( ! ( handledRequest ) ) {
-        handledRequest = HandleWebAppRequest( request, response );
-    }
-    if ( ! ( handledRequest ) ) {
-        global.log("404 : " + url.parse( request.url ).pathname )
-        serve._404( response, "404.html" );
-
-    }
-}
-
-exports.Serve = Serve;

+ 0 - 28
lib/nodejs/vwfCli.js

@@ -1,28 +0,0 @@
-var fs   = require( 'fs-extra' ),
-    path = require( 'path' );
-
-var create = function( applicationPath ) {
-	if ( fs.existsSync ( applicationPath ) ) {
-		return false;
-	}
-
-	fs.mkdirpSync( applicationPath );
-
-        var home = ( process.env.HOME || process.env.USERPROFILE );
-        var vwfHome = path.join( home, ".vwf" );
-     
-        if ( fs.existsSync( path.join( process.cwd(), "support/cli/sample_app/" ) ) ) {
-            fs.copySync( path.join( process.cwd(), "support/cli/sample_app/" ), applicationPath );
-        } else if ( fs.existsSync( path.join( vwfHome, "support/cli/sample_app/" ) ) ) {
-            fs.copySync( path.join( vwfHome, "support/cli/sample_app/" ), applicationPath );
-        } else if ( process.env.VWF_DIR && fs.existsSync( path.join( process.env.VWF_DIR, "support/cli/sample_app/" ) ) ) {
-            fs.copySync( path.join( process.env.VWF_DIR, "support/cli/sample_app/" ), applicationPath );
-        } else {
-            consoleError( "Could not find VWF support files." );
-            return false;
-        }
-
-	return true;
-}
-
-module.exports.create = create;

+ 0 - 77
node-server.js

@@ -1,77 +0,0 @@
-#!/usr/bin/env node
-
-var server = require( './node_vwf' ),
-    path   = require( 'path' ),
-    fs 	   = require( 'fs' ),
-    cli    = require( './lib/nodejs/vwfCli.js' );
-
-var argv = require( 'optimist' ).argv;
-
-function printGeneralHelp() {
-	console.log("Usage: vwf [--help] <command> [<args>]");
-	console.log("");
-	console.log("With no arguments, 'vwf' runs the application in the current");
-	console.log("directory in local development mode.");
-	console.log("");
-	console.log("Use 'vwf create <name>' to create a new VWF project.");
-	console.log("");
-	console.log("Commands:");
-	console.log("  run                      [default] Start the VWF server");
-	console.log("  create                   Create a new VWF application");
-	console.log("");
-	console.log("Options:");
-	console.log("  -a, --applicationPath    Path to VWF application. Default: current directory.");
-	console.log("  -v, --vwfPath            Path to VWF support files. Default: current directory, then \"$HOME/.vwf\".");
-	console.log("  -p, --port               Port to start server on. Default: 3000");
-	console.log("  -l, --log                Log level for server. Default: 1");
-	console.log("  -h, --help               Output usage information");
-	console.log("  -s, --ssl                Enables SSL");
-	console.log("  -k, --key                Path to private key");
-	console.log("  -c, --cert               Path to certificate");
-}
-
-function printCreateHelp() {
-	console.log("Usage: vwf create APP_PATH");
-	console.log("");
-	console.log("The `vwf create` command creates a new VWF application with a");
-	console.log("default directory structure at the path you specified in APP_PATH.");
-	console.log("");
-	console.log("Example:");
-	console.log("  vwf create ~/code/my-new-app");
-}
-
-
-if ( argv._[0] == 'create' && argv._.length == 1 ) {
-	console.log("'create' requires a PATH to create the new VWF application.");
-	console.log("");
-	printCreateHelp();	
-} else if ( argv._[0] == 'create' && argv._.length > 1 ) {
-	var applicationPath = argv._[1];
-
-	if ( cli.create( applicationPath ) ) {
-		console.log("VWF application created at '" + applicationPath + "'.");
-		console.log("");
-		console.log("To get started quickly:");
-		console.log("  $ cd " + applicationPath);
-		console.log("  $ vwf");
-		console.log("");
-		console.log("See the Getting Started documentation at: ");
-		console.log("https://virtual.wf/getting_started.html");
-	} else {
-		console.log("VWF application could not be created at '" + applicationPath + "'");
-	}
-} else if ( argv.help || argv.h || ( argv._[0] == 'help' && argv._.length == 1 ) ) {
-	printGeneralHelp();
-} else if ( argv._[0] == 'help' && argv._[1] == 'create' ) {
-	printCreateHelp();
-} else if ( argv._[0] == 'help' && argv._[1] == 'run' ) {
-	printGeneralHelp();
-} else if ( argv._[0] == 'help' ) {
-	console.log("VWF can't find help on that command.");
-	console.log("");
-	printGeneralHelp();
-} else if ( argv._[0] == 'run' || argv._.length == 0 ) {
-	server.startVWF();
-} else {
-	console.log( "'" + argv._[0] + "' is not a VWF command. See 'vwf --help'." );
-}

+ 0 - 150
node_vwf.js

@@ -1,150 +0,0 @@
-var path = require( 'path' ),
-    http = require( 'http' ),
-    https = require( 'https' ),
-    fs = require( 'fs' ),
-    url = require( 'url' ),
-    sio = require( 'socket.io' ),
-    reflector = require( './lib/nodejs/reflector' ),
-    vwf = require( './lib/nodejs/vwf' ),
-    argv = require('optimist').argv;
-
-
-// Basic logging function.
-global.log = function () {
-    var args = Array.prototype.slice.call( arguments );
-    var level = args.splice( args.length - 1 )[ 0 ];
-
-    if ( !isNaN( parseInt( level ) ) ) {
-        level = parseInt( level );
-    } else {
-        args.push( level )
-        level = 1;
-    };
-
-    if ( level <= global.logLevel ) {
-        console.log.apply( this, args );
-    }
-};
-
-function consoleNotice( string ) {
-    var brown = '\u001b[33m';
-    var reset = '\u001b[0m';
-    global.log( brown + string + reset );
-}
-
-function consoleError( string ) {
-    var red   = '\u001b[31m';
-    var reset = '\u001b[0m';
-    global.log( red + string + reset );
-}
-
-// Set the root directory where applications will be served from. Default
-// to the current directory if none is specified.
-// Use --applicationPath or -a to specify an alternative path.
-function parseApplicationPath () {
-
-    if ( argv.applicationPath || argv.a ) {
-
-        var applicationPath = argv.applicationPath || argv.a;
-
-        if ( fs.existsSync( applicationPath ) && fs.statSync( applicationPath ).isDirectory() ) {
-            consoleNotice( "Serving VWF applications from " + applicationPath );
-            return applicationPath;
-        } else {
-            consoleError( applicationPath + " is NOT a directory! Serving VWF applications from " + process.cwd() );
-            return process.cwd();
-        }
-
-    } else {
-        consoleNotice( "Serving VWF applications from " + process.cwd() );
-        return process.cwd();
-    }
-}
-
-// Set the VWF directory where VWF files will be served from. Default to
-// user specified directory if defined by the command line "-v" or "--vwfPath"
-// options, then current working directory, and finally if not found at either,
-// try the "$HOME/.vwf" directory.
-function parseVWFPath () {
-    var home = ( process.env.HOME || process.env.USERPROFILE );
-    var vwfHome = path.join( home, ".vwf" );
-    var vwfPath = ( argv.v  || argv.vwfPath );
-     
-    if ( vwfPath != undefined && fs.existsSync( path.join( vwfPath, "support/client/lib" ) ) ) {
-        return vwfPath;
-    } else if ( fs.existsSync( path.join( process.cwd(), "support/client/lib" ) ) ) {
-        return process.cwd();
-    } else if ( fs.existsSync( path.join( vwfHome, "support/client/lib" ) ) ) {
-        return vwfHome;
-    } else if ( process.env.VWF_DIR && fs.existsSync( path.join( process.env.VWF_DIR, "support/client/lib" ) ) ) {
-        return process.env.VWF_DIR;
-    } else {
-        consoleError( "Could not find VWF support files." );
-        return false;
-    }
-}
-
-
-//Start the VWF server
-function startVWF() {
-
-    global.logLevel = ( ( argv.l || argv.log ) ? ( argv.l || argv.log ) : 1 );
-
-    global.vwfRoot = parseVWFPath();
-
-    global.instances = {};
-
-    if ( !global.vwfRoot ) {
-        // Should not hit this path since the VWF script checks for the existence
-        // of the VWF support files before running this script.
-        consoleError("Exiting.");
-        process.exit();
-    }
-
-    function OnRequest( request, response ) {
-        try {
-            vwf.Serve( request, response );
-        } catch ( e ) {
-            response.writeHead( 500, {
-                "Content-Type": "text/plain"
-            } );
-            response.write( e.toString(), "utf8" );
-            response.end();
-        }
-    } // close onRequest
-
-    consoleNotice( 'LogLevel = ' +  global.logLevel );  
-
-    consoleNotice( 'Serving VWF support files from ' + global.vwfRoot );
-
-    if ( argv.nocache ) {
-        FileCache.enabled = false;
-        consoleNotice( 'server cache disabled' );
-    }
-
-    global.applicationRoot = parseApplicationPath( );
-
-    var ssl = ( argv.s  || argv.ssl );
-    var pass = ( ( argv.w) ? ( argv.w) : undefined );
-    var sslOptions = {
-        key: ( ( argv.k || argv.key ) ? fs.readFileSync( argv.k || argv.key ) : undefined ),
-        cert: ( ( argv.c || argv.cert ) ? fs.readFileSync( argv.c || argv.cert ) : undefined ),
-        passphrase: JSON.stringify(pass)
-    };
-
-    //create the server
-    var port = ( ( argv.p || argv.port ) ? ( argv.p || argv.port ) : 3001 );
-
-    var srv = ssl ? https.createServer( sslOptions, OnRequest ).listen( port ) : http.createServer( OnRequest ).listen( port );
-    consoleNotice( 'Serving on port ' + port );
-
- var socketManager = sio.listen(srv, 
- { 
-             log: false
-    } );
-  
-    socketManager.set('transports', ['websocket']);
-    socketManager.sockets.on( 'connection', reflector.OnConnection );
-}
-
-exports.startVWF = startVWF;

+ 429 - 523
package-lock.json

@@ -1,106 +1,115 @@
 {
-  "name": "livecodingspace",
+  "name": "lcs2",
   "version": "0.0.1",
   "lockfileVersion": 1,
   "requires": true,
   "dependencies": {
     "accepts": {
-      "version": "1.3.3",
-      "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.3.tgz",
-      "integrity": "sha1-w8p0NJOGSMPg2cHjKN1otiLChMo=",
+      "version": "1.3.5",
+      "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.5.tgz",
+      "integrity": "sha1-63d99gEXI6OxTopywIBcjoZ0a9I=",
       "requires": {
-        "mime-types": "2.1.17",
+        "mime-types": "~2.1.18",
         "negotiator": "0.6.1"
       }
     },
-    "after": {
-      "version": "0.8.2",
-      "resolved": "https://registry.npmjs.org/after/-/after-0.8.2.tgz",
-      "integrity": "sha1-/ts5T58OAqqXaOcCvaI7UF+ufh8="
+    "array-flatten": {
+      "version": "1.1.1",
+      "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz",
+      "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI="
     },
-    "argparse": {
-      "version": "1.0.9",
-      "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.9.tgz",
-      "integrity": "sha1-c9g7wmP4bpf4zE9rrhsOkKfSLIY=",
+    "basic-auth": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/basic-auth/-/basic-auth-2.0.0.tgz",
+      "integrity": "sha1-AV2z81PgLlY3d1X5YnQuiYHnu7o=",
       "requires": {
-        "sprintf-js": "1.0.3"
+        "safe-buffer": "5.1.1"
       }
     },
-    "arraybuffer.slice": {
-      "version": "0.0.6",
-      "resolved": "https://registry.npmjs.org/arraybuffer.slice/-/arraybuffer.slice-0.0.6.tgz",
-      "integrity": "sha1-8zshWfBTKj8xB6JywMz70a0peco="
+    "batch": {
+      "version": "0.6.1",
+      "resolved": "https://registry.npmjs.org/batch/-/batch-0.6.1.tgz",
+      "integrity": "sha1-3DQxT05nkxgJP8dgJyUl+UvyXBY="
     },
-    "async": {
-      "version": "2.6.0",
-      "resolved": "https://registry.npmjs.org/async/-/async-2.6.0.tgz",
-      "integrity": "sha512-xAfGg1/NTLBBKlHFmnd7PlmUW9KhVQIUuSrYem9xzFUZy13ScvtyGGejaae9iAVRiRq9+Cx7DPFaAAhCpyxyPw==",
+    "body-parser": {
+      "version": "1.18.3",
+      "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.18.3.tgz",
+      "integrity": "sha1-WykhmP/dVTs6DyDe0FkrlWlVyLQ=",
       "requires": {
-        "lodash": "4.17.4"
+        "bytes": "3.0.0",
+        "content-type": "~1.0.4",
+        "debug": "2.6.9",
+        "depd": "~1.1.2",
+        "http-errors": "~1.6.3",
+        "iconv-lite": "0.4.23",
+        "on-finished": "~2.3.0",
+        "qs": "6.5.2",
+        "raw-body": "2.3.3",
+        "type-is": "~1.6.16"
       }
     },
-    "async-limiter": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/async-limiter/-/async-limiter-1.0.0.tgz",
-      "integrity": "sha512-jp/uFnooOiO+L211eZOoSyzpOITMXx1rBITauYykG3BRYPu8h0UcxsPNB04RR5vo4Tyz3+ay17tR6JVf9qzYWg=="
-    },
-    "backo2": {
-      "version": "1.0.2",
-      "resolved": "https://registry.npmjs.org/backo2/-/backo2-1.0.2.tgz",
-      "integrity": "sha1-MasayLEpNjRj41s+u2n038+6eUc="
-    },
-    "base64-arraybuffer": {
-      "version": "0.1.5",
-      "resolved": "https://registry.npmjs.org/base64-arraybuffer/-/base64-arraybuffer-0.1.5.tgz",
-      "integrity": "sha1-c5JncZI7Whl0etZmqlzUv5xunOg="
+    "bytes": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz",
+      "integrity": "sha1-0ygVQE1olpn4Wk6k+odV3ROpYEg="
     },
-    "base64id": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/base64id/-/base64id-1.0.0.tgz",
-      "integrity": "sha1-R2iMuZu2gE8OBtPnY7HDLlfY5rY="
-    },
-    "better-assert": {
-      "version": "1.0.2",
-      "resolved": "https://registry.npmjs.org/better-assert/-/better-assert-1.0.2.tgz",
-      "integrity": "sha1-QIZrnhueC1W0gYlDEeaPr/rrxSI=",
+    "compressible": {
+      "version": "2.0.14",
+      "resolved": "https://registry.npmjs.org/compressible/-/compressible-2.0.14.tgz",
+      "integrity": "sha1-MmxfUH+7BV9UEWeCuWmoG2einac=",
       "requires": {
-        "callsite": "1.0.0"
+        "mime-db": ">= 1.34.0 < 2"
+      },
+      "dependencies": {
+        "mime-db": {
+          "version": "1.34.0",
+          "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.34.0.tgz",
+          "integrity": "sha1-RS0Oz/XDA0am3B5kseruDTcZ/5o="
+        }
       }
     },
-    "blob": {
-      "version": "0.0.4",
-      "resolved": "https://registry.npmjs.org/blob/-/blob-0.0.4.tgz",
-      "integrity": "sha1-vPEwUspURj8w+fx+lbmkdjCpSSE="
-    },
-    "callsite": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/callsite/-/callsite-1.0.0.tgz",
-      "integrity": "sha1-KAOY5dZkvXQDi28JBRU+borxvCA="
-    },
-    "component-bind": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/component-bind/-/component-bind-1.0.0.tgz",
-      "integrity": "sha1-AMYIq33Nk4l8AAllGx06jh5zu9E="
+    "compression": {
+      "version": "1.7.2",
+      "resolved": "http://registry.npmjs.org/compression/-/compression-1.7.2.tgz",
+      "integrity": "sha1-qv+81qr4VLROuygDU9WtFlH1mmk=",
+      "requires": {
+        "accepts": "~1.3.4",
+        "bytes": "3.0.0",
+        "compressible": "~2.0.13",
+        "debug": "2.6.9",
+        "on-headers": "~1.0.1",
+        "safe-buffer": "5.1.1",
+        "vary": "~1.1.2"
+      }
     },
-    "component-emitter": {
-      "version": "1.2.1",
-      "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.2.1.tgz",
-      "integrity": "sha1-E3kY1teCg/ffemt8WmPhQOaUJeY="
+    "content-disposition": {
+      "version": "0.5.2",
+      "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.2.tgz",
+      "integrity": "sha1-DPaLud318r55YcOoUXjLhdunjLQ="
     },
-    "component-inherit": {
-      "version": "0.0.3",
-      "resolved": "https://registry.npmjs.org/component-inherit/-/component-inherit-0.0.3.tgz",
-      "integrity": "sha1-ZF/ErfWLcrZJ1crmUTVhnbJv8UM="
+    "content-type": {
+      "version": "1.0.4",
+      "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz",
+      "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA=="
     },
     "cookie": {
       "version": "0.3.1",
       "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.3.1.tgz",
       "integrity": "sha1-5+Ch+e9DtMi6klxcWpboBtFoc7s="
     },
-    "crypto": {
-      "version": "1.0.1",
-      "resolved": "https://registry.npmjs.org/crypto/-/crypto-1.0.1.tgz",
-      "integrity": "sha512-VxBKmeNcqQdiUQUW2Tzq0t377b54N2bMtXO/qiLa+6eRRmmC4qT3D4OnTGoT/U6O9aklQ/jTwbOtRMTTY8G0Ig=="
+    "cookie-signature": {
+      "version": "1.0.6",
+      "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz",
+      "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw="
+    },
+    "cors": {
+      "version": "2.8.4",
+      "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.4.tgz",
+      "integrity": "sha1-K9OB8usgECAQXNUOpZ2mMJBpRoY=",
+      "requires": {
+        "object-assign": "^4",
+        "vary": "^1"
+      }
     },
     "debug": {
       "version": "2.6.9",
@@ -108,377 +117,328 @@
       "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
       "requires": {
         "ms": "2.0.0"
-      },
-      "dependencies": {
-        "ms": {
-          "version": "2.0.0",
-          "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
-          "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="
-        }
       }
     },
-    "engine.io": {
-      "version": "3.1.4",
-      "resolved": "https://registry.npmjs.org/engine.io/-/engine.io-3.1.4.tgz",
-      "integrity": "sha1-PQIRtwpVLOhB/8fahiezAamkFi4=",
+    "depd": {
+      "version": "1.1.2",
+      "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz",
+      "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak="
+    },
+    "destroy": {
+      "version": "1.0.4",
+      "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz",
+      "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA="
+    },
+    "ee-first": {
+      "version": "1.1.1",
+      "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz",
+      "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0="
+    },
+    "encodeurl": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz",
+      "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k="
+    },
+    "escape-html": {
+      "version": "1.0.3",
+      "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz",
+      "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg="
+    },
+    "etag": {
+      "version": "1.8.1",
+      "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz",
+      "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc="
+    },
+    "express": {
+      "version": "4.16.3",
+      "resolved": "https://registry.npmjs.org/express/-/express-4.16.3.tgz",
+      "integrity": "sha1-avilAjUNsyRuzEvs9rWjTSL37VM=",
       "requires": {
-        "accepts": "1.3.3",
-        "base64id": "1.0.0",
+        "accepts": "~1.3.5",
+        "array-flatten": "1.1.1",
+        "body-parser": "1.18.2",
+        "content-disposition": "0.5.2",
+        "content-type": "~1.0.4",
         "cookie": "0.3.1",
+        "cookie-signature": "1.0.6",
         "debug": "2.6.9",
-        "engine.io-parser": "2.1.1",
-        "uws": "0.14.5",
-        "ws": "3.3.3"
+        "depd": "~1.1.2",
+        "encodeurl": "~1.0.2",
+        "escape-html": "~1.0.3",
+        "etag": "~1.8.1",
+        "finalhandler": "1.1.1",
+        "fresh": "0.5.2",
+        "merge-descriptors": "1.0.1",
+        "methods": "~1.1.2",
+        "on-finished": "~2.3.0",
+        "parseurl": "~1.3.2",
+        "path-to-regexp": "0.1.7",
+        "proxy-addr": "~2.0.3",
+        "qs": "6.5.1",
+        "range-parser": "~1.2.0",
+        "safe-buffer": "5.1.1",
+        "send": "0.16.2",
+        "serve-static": "1.13.2",
+        "setprototypeof": "1.1.0",
+        "statuses": "~1.4.0",
+        "type-is": "~1.6.16",
+        "utils-merge": "1.0.1",
+        "vary": "~1.1.2"
+      },
+      "dependencies": {
+        "body-parser": {
+          "version": "1.18.2",
+          "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.18.2.tgz",
+          "integrity": "sha1-h2eKGdhLR9hZuDGZvVm84iKxBFQ=",
+          "requires": {
+            "bytes": "3.0.0",
+            "content-type": "~1.0.4",
+            "debug": "2.6.9",
+            "depd": "~1.1.1",
+            "http-errors": "~1.6.2",
+            "iconv-lite": "0.4.19",
+            "on-finished": "~2.3.0",
+            "qs": "6.5.1",
+            "raw-body": "2.3.2",
+            "type-is": "~1.6.15"
+          }
+        },
+        "iconv-lite": {
+          "version": "0.4.19",
+          "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.19.tgz",
+          "integrity": "sha512-oTZqweIP51xaGPI4uPa56/Pri/480R+mo7SeU+YETByQNhDG55ycFyNLIgta9vXhILrxXDmF7ZGhqZIcuN0gJQ=="
+        },
+        "qs": {
+          "version": "6.5.1",
+          "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.1.tgz",
+          "integrity": "sha512-eRzhrN1WSINYCDCbrz796z37LOe3m5tmW7RQf6oBntukAG1nmovJvhnwHHRMAfeoItc1m2Hk02WER2aQ/iqs+A=="
+        },
+        "raw-body": {
+          "version": "2.3.2",
+          "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.3.2.tgz",
+          "integrity": "sha1-vNYMd9Prk83gBQKVw/N5OJvIj4k=",
+          "requires": {
+            "bytes": "3.0.0",
+            "http-errors": "1.6.2",
+            "iconv-lite": "0.4.19",
+            "unpipe": "1.0.0"
+          },
+          "dependencies": {
+            "depd": {
+              "version": "1.1.1",
+              "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.1.tgz",
+              "integrity": "sha1-V4O04cRZ8G+lyif5kfPQbnoxA1k="
+            },
+            "http-errors": {
+              "version": "1.6.2",
+              "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.2.tgz",
+              "integrity": "sha1-CgAsyFcHGSp+eUbO7cERVfYOxzY=",
+              "requires": {
+                "depd": "1.1.1",
+                "inherits": "2.0.3",
+                "setprototypeof": "1.0.3",
+                "statuses": ">= 1.3.1 < 2"
+              }
+            },
+            "setprototypeof": {
+              "version": "1.0.3",
+              "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.0.3.tgz",
+              "integrity": "sha1-ZlZ+NwQ+608E2RvWWMDL77VbjgQ="
+            }
+          }
+        },
+        "statuses": {
+          "version": "1.4.0",
+          "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.4.0.tgz",
+          "integrity": "sha512-zhSCtt8v2NDrRlPQpCNtw/heZLtfUDqxBM1udqikb/Hbk52LK4nQSwr10u77iopCW5LsyHpuXS0GnEc48mLeew=="
+        }
       }
     },
-    "engine.io-client": {
-      "version": "3.1.4",
-      "resolved": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-3.1.4.tgz",
-      "integrity": "sha1-T88TcLRxY70s6b4nM5ckMDUNTqE=",
+    "finalhandler": {
+      "version": "1.1.1",
+      "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.1.tgz",
+      "integrity": "sha512-Y1GUDo39ez4aHAw7MysnUD5JzYX+WaIj8I57kO3aEPT1fFRL4sr7mjei97FgnwhAyyzRYmQZaTHb2+9uZ1dPtg==",
       "requires": {
-        "component-emitter": "1.2.1",
-        "component-inherit": "0.0.3",
         "debug": "2.6.9",
-        "engine.io-parser": "2.1.1",
-        "has-cors": "1.1.0",
-        "indexof": "0.0.1",
-        "parseqs": "0.0.5",
-        "parseuri": "0.0.5",
-        "ws": "3.3.3",
-        "xmlhttprequest-ssl": "1.5.4",
-        "yeast": "0.1.2"
+        "encodeurl": "~1.0.2",
+        "escape-html": "~1.0.3",
+        "on-finished": "~2.3.0",
+        "parseurl": "~1.3.2",
+        "statuses": "~1.4.0",
+        "unpipe": "~1.0.0"
+      },
+      "dependencies": {
+        "statuses": {
+          "version": "1.4.0",
+          "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.4.0.tgz",
+          "integrity": "sha512-zhSCtt8v2NDrRlPQpCNtw/heZLtfUDqxBM1udqikb/Hbk52LK4nQSwr10u77iopCW5LsyHpuXS0GnEc48mLeew=="
+        }
       }
     },
-    "engine.io-parser": {
-      "version": "2.1.1",
-      "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-2.1.1.tgz",
-      "integrity": "sha1-4Ps/DgRi9/WLt3waUun1p+JuRmg=",
+    "forwarded": {
+      "version": "0.1.2",
+      "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.1.2.tgz",
+      "integrity": "sha1-mMI9qxF1ZXuMBXPozszZGw/xjIQ="
+    },
+    "fresh": {
+      "version": "0.5.2",
+      "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz",
+      "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac="
+    },
+    "http-errors": {
+      "version": "1.6.3",
+      "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.3.tgz",
+      "integrity": "sha1-i1VoC7S+KDoLW/TqLjhYC+HZMg0=",
       "requires": {
-        "after": "0.8.2",
-        "arraybuffer.slice": "0.0.6",
-        "base64-arraybuffer": "0.1.5",
-        "blob": "0.0.4",
-        "has-binary2": "1.0.2"
+        "depd": "~1.1.2",
+        "inherits": "2.0.3",
+        "setprototypeof": "1.1.0",
+        "statuses": ">= 1.4.0 < 2"
       }
     },
-    "esprima": {
-      "version": "4.0.0",
-      "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.0.tgz",
-      "integrity": "sha512-oftTcaMu/EGrEIu904mWteKIv8vMuOgGYo7EhVJJN00R/EED9DCua/xxHRdYnKtcECzVg7xOWhflvJMnqcFZjw=="
-    },
-    "fs-extra": {
-      "version": "5.0.0",
-      "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-5.0.0.tgz",
-      "integrity": "sha512-66Pm4RYbjzdyeuqudYqhFiNBbCIuI9kgRqLPSHIlXHidW8NIQtVdkM1yeZ4lXwuhbTETv3EUGMNHAAw6hiundQ==",
+    "iconv-lite": {
+      "version": "0.4.23",
+      "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.23.tgz",
+      "integrity": "sha512-neyTUVFtahjf0mB3dZT77u+8O0QB89jFdnBkd5P1JgYPbPaia3gXXOVL2fq8VyU2gMMD7SaN7QukTB/pmXYvDA==",
       "requires": {
-        "graceful-fs": "4.1.11",
-        "jsonfile": "4.0.0",
-        "universalify": "0.1.1"
+        "safer-buffer": ">= 2.1.2 < 3"
       }
     },
-    "graceful-fs": {
-      "version": "4.1.11",
-      "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.11.tgz",
-      "integrity": "sha1-Dovf5NHduIVNZOBOp8AOKgJuVlg="
-    },
-    "has-binary2": {
-      "version": "1.0.2",
-      "resolved": "https://registry.npmjs.org/has-binary2/-/has-binary2-1.0.2.tgz",
-      "integrity": "sha1-6D26SfC5vk0CbSc2U1DZ8D9Uvpg=",
-      "requires": {
-        "isarray": "2.0.1"
-      }
+    "inherits": {
+      "version": "2.0.3",
+      "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz",
+      "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4="
     },
-    "has-cors": {
-      "version": "1.1.0",
-      "resolved": "https://registry.npmjs.org/has-cors/-/has-cors-1.1.0.tgz",
-      "integrity": "sha1-XkdHk/fqmEPRu5nCPu9J/xJv/zk="
+    "ipaddr.js": {
+      "version": "1.6.0",
+      "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.6.0.tgz",
+      "integrity": "sha1-4/o1e3c9phnybpXwSdBVxyeW+Gs="
     },
-    "he": {
-      "version": "1.1.1",
-      "resolved": "https://registry.npmjs.org/he/-/he-1.1.1.tgz",
-      "integrity": "sha1-k0EP0hsAlzUVH4howvJx80J+I/0=",
-      "dev": true
-    },
-    "indexof": {
-      "version": "0.0.1",
-      "resolved": "https://registry.npmjs.org/indexof/-/indexof-0.0.1.tgz",
-      "integrity": "sha1-gtwzbSMrkGIXnQWrMpOmYFn9Q10="
-    },
-    "isarray": {
-      "version": "2.0.1",
-      "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.1.tgz",
-      "integrity": "sha1-o32U7ZzaLVmGXJ92/llu4fM4dB4="
-    },
-    "js-yaml": {
-      "version": "3.10.0",
-      "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.10.0.tgz",
-      "integrity": "sha512-O2v52ffjLa9VeM43J4XocZE//WT9N0IiwDa3KSHH7Tu8CtH+1qM8SIZvnsTh6v+4yFy5KUY3BHUVwjpfAWsjIA==",
-      "requires": {
-        "argparse": "1.0.9",
-        "esprima": "4.0.0"
-      }
+    "media-typer": {
+      "version": "0.3.0",
+      "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz",
+      "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g="
     },
-    "jsonfile": {
-      "version": "4.0.0",
-      "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz",
-      "integrity": "sha1-h3Gq4HmbZAdrdmQPygWPnBDjPss=",
-      "requires": {
-        "graceful-fs": "4.1.11"
-      }
+    "merge-descriptors": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz",
+      "integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E="
     },
-    "lodash": {
-      "version": "4.17.4",
-      "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.4.tgz",
-      "integrity": "sha1-eCA6TRwyiuHYbcpkYONptX9AVa4="
+    "methods": {
+      "version": "1.1.2",
+      "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz",
+      "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4="
     },
     "mime": {
-      "version": "2.2.0",
-      "resolved": "https://registry.npmjs.org/mime/-/mime-2.2.0.tgz",
-      "integrity": "sha512-0Qz9uF1ATtl8RKJG4VRfOymh7PyEor6NbrI/61lRfuRe4vx9SNATrvAeTj2EWVRKjEQGskrzWkJBBY5NbaVHIA=="
+      "version": "1.4.1",
+      "resolved": "https://registry.npmjs.org/mime/-/mime-1.4.1.tgz",
+      "integrity": "sha512-KI1+qOZu5DcW6wayYHSzR/tXKCDC5Om4s1z2QJjDULzLcmf3DvzS7oluY4HCTrc+9FiKmWUgeNLg7W3uIQvxtQ=="
     },
     "mime-db": {
-      "version": "1.30.0",
-      "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.30.0.tgz",
-      "integrity": "sha1-dMZD2i3Z1qRTmZY0ZbJtXKfXHwE="
+      "version": "1.33.0",
+      "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.33.0.tgz",
+      "integrity": "sha512-BHJ/EKruNIqJf/QahvxwQZXKygOQ256myeN/Ew+THcAa5q+PjyTTMMeNQC4DZw5AwfvelsUrA6B67NKMqXDbzQ=="
     },
     "mime-types": {
-      "version": "2.1.17",
-      "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.17.tgz",
-      "integrity": "sha1-Cdejk/A+mVp5+K+Fe3Cp4KsWVXo=",
+      "version": "2.1.18",
+      "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.18.tgz",
+      "integrity": "sha512-lc/aahn+t4/SWV/qcmumYjymLsWfN3ELhpmVuUFjgsORruuZPVSwAQryq+HHGvO/SI2KVX26bx+En+zhM8g8hQ==",
       "requires": {
-        "mime-db": "1.30.0"
+        "mime-db": "~1.33.0"
       }
     },
-    "mocha": {
-      "version": "5.1.1",
-      "resolved": "https://registry.npmjs.org/mocha/-/mocha-5.1.1.tgz",
-      "integrity": "sha512-kKKs/H1KrMMQIEsWNxGmb4/BGsmj0dkeyotEvbrAuQ01FcWRLssUNXCEUZk6SZtyJBi6EE7SL0zDDtItw1rGhw==",
-      "dev": true,
+    "minimist": {
+      "version": "0.0.10",
+      "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.10.tgz",
+      "integrity": "sha1-3j+YVD2/lggr5IrRoMfNqDYwHc8="
+    },
+    "morgan": {
+      "version": "1.9.0",
+      "resolved": "https://registry.npmjs.org/morgan/-/morgan-1.9.0.tgz",
+      "integrity": "sha1-0B+mxlhZt2/PMbPLU6OCGjEdgFE=",
       "requires": {
-        "browser-stdout": "1.3.1",
-        "commander": "2.11.0",
-        "debug": "3.1.0",
-        "diff": "3.5.0",
-        "escape-string-regexp": "1.0.5",
-        "glob": "7.1.2",
-        "growl": "1.10.3",
-        "he": "1.1.1",
-        "minimatch": "3.0.4",
-        "mkdirp": "0.5.1",
-        "supports-color": "4.4.0"
-      },
-      "dependencies": {
-        "balanced-match": {
-          "version": "1.0.0",
-          "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz",
-          "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=",
-          "dev": true
-        },
-        "brace-expansion": {
-          "version": "1.1.11",
-          "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
-          "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
-          "dev": true,
-          "requires": {
-            "balanced-match": "1.0.0",
-            "concat-map": "0.0.1"
-          }
-        },
-        "browser-stdout": {
-          "version": "1.3.1",
-          "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz",
-          "integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==",
-          "dev": true
-        },
-        "commander": {
-          "version": "2.11.0",
-          "resolved": "https://registry.npmjs.org/commander/-/commander-2.11.0.tgz",
-          "integrity": "sha512-b0553uYA5YAEGgyYIGYROzKQ7X5RAqedkfjiZxwi0kL1g3bOaBNNZfYkzt/CL0umgD5wc9Jec2FbB98CjkMRvQ==",
-          "dev": true
-        },
-        "concat-map": {
-          "version": "0.0.1",
-          "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
-          "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=",
-          "dev": true
-        },
-        "debug": {
-          "version": "3.1.0",
-          "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz",
-          "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==",
-          "dev": true,
-          "requires": {
-            "ms": "2.0.0"
-          }
-        },
-        "diff": {
-          "version": "3.5.0",
-          "resolved": "https://registry.npmjs.org/diff/-/diff-3.5.0.tgz",
-          "integrity": "sha512-A46qtFgd+g7pDZinpnwiRJtxbC1hpgf0uzP3iG89scHk0AUC7A1TGxf5OiiOUv/JMZR8GOt8hL900hV0bOy5xA==",
-          "dev": true
-        },
-        "escape-string-regexp": {
-          "version": "1.0.5",
-          "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
-          "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=",
-          "dev": true
-        },
-        "fs.realpath": {
-          "version": "1.0.0",
-          "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
-          "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=",
-          "dev": true
-        },
-        "glob": {
-          "version": "7.1.2",
-          "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz",
-          "integrity": "sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ==",
-          "dev": true,
-          "requires": {
-            "fs.realpath": "1.0.0",
-            "inflight": "1.0.6",
-            "inherits": "2.0.3",
-            "minimatch": "3.0.4",
-            "once": "1.4.0",
-            "path-is-absolute": "1.0.1"
-          }
-        },
-        "growl": {
-          "version": "1.10.3",
-          "resolved": "https://registry.npmjs.org/growl/-/growl-1.10.3.tgz",
-          "integrity": "sha512-hKlsbA5Vu3xsh1Cg3J7jSmX/WaW6A5oBeqzM88oNbCRQFz+zUaXm6yxS4RVytp1scBoJzSYl4YAEOQIt6O8V1Q==",
-          "dev": true
-        },
-        "has-flag": {
-          "version": "2.0.0",
-          "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-2.0.0.tgz",
-          "integrity": "sha1-6CB68cx7MNRGzHC3NLXovhj4jVE=",
-          "dev": true
-        },
-        "inflight": {
-          "version": "1.0.6",
-          "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
-          "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=",
-          "dev": true,
-          "requires": {
-            "once": "1.4.0",
-            "wrappy": "1.0.2"
-          }
-        },
-        "inherits": {
-          "version": "2.0.3",
-          "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz",
-          "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=",
-          "dev": true
-        },
-        "minimatch": {
-          "version": "3.0.4",
-          "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz",
-          "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==",
-          "dev": true,
-          "requires": {
-            "brace-expansion": "1.1.11"
-          }
-        },
-        "minimist": {
-          "version": "0.0.8",
-          "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz",
-          "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=",
-          "dev": true
-        },
-        "mkdirp": {
-          "version": "0.5.1",
-          "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz",
-          "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=",
-          "dev": true,
-          "requires": {
-            "minimist": "0.0.8"
-          }
-        },
-        "ms": {
-          "version": "2.0.0",
-          "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
-          "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=",
-          "dev": true
-        },
-        "once": {
-          "version": "1.4.0",
-          "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
-          "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=",
-          "dev": true,
-          "requires": {
-            "wrappy": "1.0.2"
-          }
-        },
-        "path-is-absolute": {
-          "version": "1.0.1",
-          "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
-          "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=",
-          "dev": true
-        },
-        "supports-color": {
-          "version": "4.4.0",
-          "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-4.4.0.tgz",
-          "integrity": "sha512-rKC3+DyXWgK0ZLKwmRsrkyHVZAjNkfzeehuFWdGGcqGDTZFH73+RH6S/RDAAxl9GusSjZSUWYLmT9N5pzXFOXQ==",
-          "dev": true,
-          "requires": {
-            "has-flag": "2.0.0"
-          }
-        },
-        "wrappy": {
-          "version": "1.0.2",
-          "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
-          "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=",
-          "dev": true
-        }
+        "basic-auth": "~2.0.0",
+        "debug": "2.6.9",
+        "depd": "~1.1.1",
+        "on-finished": "~2.3.0",
+        "on-headers": "~1.0.1"
       }
     },
+    "ms": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
+      "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="
+    },
     "negotiator": {
       "version": "0.6.1",
       "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.1.tgz",
       "integrity": "sha1-KzJxhOiZIQEXeyhWP7XnECrNDKk="
     },
-    "object-component": {
-      "version": "0.0.3",
-      "resolved": "https://registry.npmjs.org/object-component/-/object-component-0.0.3.tgz",
-      "integrity": "sha1-8MaapQ78lbhmwYb0AKM3acsvEpE="
+    "object-assign": {
+      "version": "4.1.1",
+      "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
+      "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM="
+    },
+    "on-finished": {
+      "version": "2.3.0",
+      "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz",
+      "integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=",
+      "requires": {
+        "ee-first": "1.1.1"
+      }
+    },
+    "on-headers": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.0.1.tgz",
+      "integrity": "sha1-ko9dD0cNSTQmUepnlLCFfBAGk/c="
     },
     "optimist": {
       "version": "0.6.1",
       "resolved": "https://registry.npmjs.org/optimist/-/optimist-0.6.1.tgz",
       "integrity": "sha1-2j6nRob6IaGaERwybpDrFaAZZoY=",
       "requires": {
-        "minimist": "0.0.10",
-        "wordwrap": "0.0.3"
-      },
-      "dependencies": {
-        "minimist": {
-          "version": "0.0.10",
-          "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.10.tgz",
-          "integrity": "sha1-3j+YVD2/lggr5IrRoMfNqDYwHc8="
-        },
-        "wordwrap": {
-          "version": "0.0.3",
-          "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.3.tgz",
-          "integrity": "sha1-o9XabNXAvAAI03I0u68b7WMFkQc="
-        }
+        "minimist": "~0.0.1",
+        "wordwrap": "~0.0.2"
       }
     },
-    "parseqs": {
-      "version": "0.0.5",
-      "resolved": "https://registry.npmjs.org/parseqs/-/parseqs-0.0.5.tgz",
-      "integrity": "sha1-1SCKNzjkZ2bikbouoXNoSSGouJ0=",
+    "parseurl": {
+      "version": "1.3.2",
+      "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.2.tgz",
+      "integrity": "sha1-/CidTtiZMRlGDBViUyYs3I3mW/M="
+    },
+    "path-to-regexp": {
+      "version": "0.1.7",
+      "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz",
+      "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w="
+    },
+    "proxy-addr": {
+      "version": "2.0.3",
+      "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.3.tgz",
+      "integrity": "sha512-jQTChiCJteusULxjBp8+jftSQE5Obdl3k4cnmLA6WXtK6XFuWRnvVL7aCiBqaLPM8c4ph0S4tKna8XvmIwEnXQ==",
       "requires": {
-        "better-assert": "1.0.2"
+        "forwarded": "~0.1.2",
+        "ipaddr.js": "1.6.0"
       }
     },
-    "parseuri": {
-      "version": "0.0.5",
-      "resolved": "https://registry.npmjs.org/parseuri/-/parseuri-0.0.5.tgz",
-      "integrity": "sha1-gCBKUNTbt3m/3G6+J3jZDkvOMgo=",
+    "qs": {
+      "version": "6.5.2",
+      "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz",
+      "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA=="
+    },
+    "range-parser": {
+      "version": "1.2.0",
+      "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.0.tgz",
+      "integrity": "sha1-9JvmtIeJTdxA3MlKMi9hEJLgDV4="
+    },
+    "raw-body": {
+      "version": "2.3.3",
+      "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.3.3.tgz",
+      "integrity": "sha512-9esiElv1BrZoI3rCDuOuKCBRbuApGGaDPQfjSflGxdy4oyzqghxu6klEkkVIvBje+FF0BX9coEv8KqW6X/7njw==",
       "requires": {
-        "better-assert": "1.0.2"
+        "bytes": "3.0.0",
+        "http-errors": "1.6.3",
+        "iconv-lite": "0.4.23",
+        "unpipe": "1.0.0"
       }
     },
     "safe-buffer": {
@@ -486,155 +446,101 @@
       "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.1.tgz",
       "integrity": "sha512-kKvNJn6Mm93gAczWVJg7wH+wGYWNrDHdWvpUmHyEsgCtIwwo3bqPtV4tR5tuPaUhTOo/kvhVwd8XwwOllGYkbg=="
     },
-    "should": {
-      "version": "13.2.1",
-      "resolved": "https://registry.npmjs.org/should/-/should-13.2.1.tgz",
-      "integrity": "sha512-l+/NwEMO+DcstsHEwPHRHzC9j4UOE3VQwJGcMWSsD/vqpqHbnQ+1iSHy64Ihmmjx1uiRPD9pFadTSc3MJtXAgw==",
-      "dev": true,
+    "safer-buffer": {
+      "version": "2.1.2",
+      "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
+      "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="
+    },
+    "send": {
+      "version": "0.16.2",
+      "resolved": "https://registry.npmjs.org/send/-/send-0.16.2.tgz",
+      "integrity": "sha512-E64YFPUssFHEFBvpbbjr44NCLtI1AohxQ8ZSiJjQLskAdKuriYEP6VyGEsRDH8ScozGpkaX1BGvhanqCwkcEZw==",
       "requires": {
-        "should-equal": "2.0.0",
-        "should-format": "3.0.3",
-        "should-type": "1.4.0",
-        "should-type-adaptors": "1.1.0",
-        "should-util": "1.0.0"
+        "debug": "2.6.9",
+        "depd": "~1.1.2",
+        "destroy": "~1.0.4",
+        "encodeurl": "~1.0.2",
+        "escape-html": "~1.0.3",
+        "etag": "~1.8.1",
+        "fresh": "0.5.2",
+        "http-errors": "~1.6.2",
+        "mime": "1.4.1",
+        "ms": "2.0.0",
+        "on-finished": "~2.3.0",
+        "range-parser": "~1.2.0",
+        "statuses": "~1.4.0"
       },
       "dependencies": {
-        "should-equal": {
-          "version": "2.0.0",
-          "resolved": "https://registry.npmjs.org/should-equal/-/should-equal-2.0.0.tgz",
-          "integrity": "sha512-ZP36TMrK9euEuWQYBig9W55WPC7uo37qzAEmbjHz4gfyuXrEUgF8cUvQVO+w+d3OMfPvSRQJ22lSm8MQJ43LTA==",
-          "dev": true,
-          "requires": {
-            "should-type": "1.4.0"
-          }
-        },
-        "should-format": {
-          "version": "3.0.3",
-          "resolved": "https://registry.npmjs.org/should-format/-/should-format-3.0.3.tgz",
-          "integrity": "sha1-m/yPdPo5IFxT04w01xcwPidxJPE=",
-          "dev": true,
-          "requires": {
-            "should-type": "1.4.0",
-            "should-type-adaptors": "1.1.0"
-          }
-        },
-        "should-type": {
+        "statuses": {
           "version": "1.4.0",
-          "resolved": "https://registry.npmjs.org/should-type/-/should-type-1.4.0.tgz",
-          "integrity": "sha1-B1bYzoRt/QmEOmlHcZ36DUz/XPM=",
-          "dev": true
-        },
-        "should-type-adaptors": {
-          "version": "1.1.0",
-          "resolved": "https://registry.npmjs.org/should-type-adaptors/-/should-type-adaptors-1.1.0.tgz",
-          "integrity": "sha512-JA4hdoLnN+kebEp2Vs8eBe9g7uy0zbRo+RMcU0EsNy+R+k049Ki+N5tT5Jagst2g7EAja+euFuoXFCa8vIklfA==",
-          "dev": true,
-          "requires": {
-            "should-type": "1.4.0",
-            "should-util": "1.0.0"
-          }
-        },
-        "should-util": {
-          "version": "1.0.0",
-          "resolved": "https://registry.npmjs.org/should-util/-/should-util-1.0.0.tgz",
-          "integrity": "sha1-yYzaN0qmsZDfi6h8mInCtNtiAGM=",
-          "dev": true
+          "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.4.0.tgz",
+          "integrity": "sha512-zhSCtt8v2NDrRlPQpCNtw/heZLtfUDqxBM1udqikb/Hbk52LK4nQSwr10u77iopCW5LsyHpuXS0GnEc48mLeew=="
         }
       }
     },
-    "socket.io": {
-      "version": "2.0.4",
-      "resolved": "https://registry.npmjs.org/socket.io/-/socket.io-2.0.4.tgz",
-      "integrity": "sha1-waRZDO/4fs8TxyZS8Eb3FrKeYBQ=",
+    "serve-index": {
+      "version": "1.9.1",
+      "resolved": "https://registry.npmjs.org/serve-index/-/serve-index-1.9.1.tgz",
+      "integrity": "sha1-03aNabHn2C5c4FD/9bRTvqEqkjk=",
       "requires": {
+        "accepts": "~1.3.4",
+        "batch": "0.6.1",
         "debug": "2.6.9",
-        "engine.io": "3.1.4",
-        "socket.io-adapter": "1.1.1",
-        "socket.io-client": "2.0.4",
-        "socket.io-parser": "3.1.2"
+        "escape-html": "~1.0.3",
+        "http-errors": "~1.6.2",
+        "mime-types": "~2.1.17",
+        "parseurl": "~1.3.2"
       }
     },
-    "socket.io-adapter": {
-      "version": "1.1.1",
-      "resolved": "https://registry.npmjs.org/socket.io-adapter/-/socket.io-adapter-1.1.1.tgz",
-      "integrity": "sha1-KoBeihTWNyEk3ZFZrUUC+MsH8Gs="
-    },
-    "socket.io-client": {
-      "version": "2.0.4",
-      "resolved": "https://registry.npmjs.org/socket.io-client/-/socket.io-client-2.0.4.tgz",
-      "integrity": "sha1-CRilUkBtxeVAs4Dc2Xr8SmQzL44=",
+    "serve-static": {
+      "version": "1.13.2",
+      "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.13.2.tgz",
+      "integrity": "sha512-p/tdJrO4U387R9oMjb1oj7qSMaMfmOyd4j9hOFoxZe2baQszgHcSWjuya/CiT5kgZZKRudHNOA0pYXOl8rQ5nw==",
       "requires": {
-        "backo2": "1.0.2",
-        "base64-arraybuffer": "0.1.5",
-        "component-bind": "1.0.0",
-        "component-emitter": "1.2.1",
-        "debug": "2.6.9",
-        "engine.io-client": "3.1.4",
-        "has-cors": "1.1.0",
-        "indexof": "0.0.1",
-        "object-component": "0.0.3",
-        "parseqs": "0.0.5",
-        "parseuri": "0.0.5",
-        "socket.io-parser": "3.1.2",
-        "to-array": "0.1.4"
+        "encodeurl": "~1.0.2",
+        "escape-html": "~1.0.3",
+        "parseurl": "~1.3.2",
+        "send": "0.16.2"
       }
     },
-    "socket.io-parser": {
-      "version": "3.1.2",
-      "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-3.1.2.tgz",
-      "integrity": "sha1-28IoIVH8T6675Aru3Ady66YZ9/I=",
+    "setprototypeof": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.0.tgz",
+      "integrity": "sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ=="
+    },
+    "statuses": {
+      "version": "1.5.0",
+      "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz",
+      "integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow="
+    },
+    "type-is": {
+      "version": "1.6.16",
+      "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.16.tgz",
+      "integrity": "sha512-HRkVv/5qY2G6I8iab9cI7v1bOIdhm94dVjQCPFElW9W+3GeDOSHmy2EBYe4VTApuzolPcmgFTN3ftVJRKR2J9Q==",
       "requires": {
-        "component-emitter": "1.2.1",
-        "debug": "2.6.9",
-        "has-binary2": "1.0.2",
-        "isarray": "2.0.1"
+        "media-typer": "0.3.0",
+        "mime-types": "~2.1.18"
       }
     },
-    "sprintf-js": {
-      "version": "1.0.3",
-      "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz",
-      "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw="
-    },
-    "to-array": {
-      "version": "0.1.4",
-      "resolved": "https://registry.npmjs.org/to-array/-/to-array-0.1.4.tgz",
-      "integrity": "sha1-F+bBH3PdTz10zaek/zI46a2b+JA="
+    "unpipe": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz",
+      "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw="
     },
-    "ultron": {
-      "version": "1.1.1",
-      "resolved": "https://registry.npmjs.org/ultron/-/ultron-1.1.1.tgz",
-      "integrity": "sha512-UIEXBNeYmKptWH6z8ZnqTeS8fV74zG0/eRU9VGkpzz+LIJNs8W/zM/L+7ctCkRrgbNnnR0xxw4bKOr0cW0N0Og=="
-    },
-    "universalify": {
-      "version": "0.1.1",
-      "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.1.tgz",
-      "integrity": "sha1-+nG63UQ3r0wUiEHjs7Fl+enlkLc="
-    },
-    "uws": {
-      "version": "0.14.5",
-      "resolved": "https://registry.npmjs.org/uws/-/uws-0.14.5.tgz",
-      "integrity": "sha1-Z6rzPEaypYel9mZtAPdpEyjxSdw=",
-      "optional": true
-    },
-    "ws": {
-      "version": "3.3.3",
-      "resolved": "https://registry.npmjs.org/ws/-/ws-3.3.3.tgz",
-      "integrity": "sha512-nnWLa/NwZSt4KQJu51MYlCcSQ5g7INpOrOMt4XV8j4dqTXdmlUmSHQ8/oLC069ckre0fRsgfvsKwbTdtKLCDkA==",
-      "requires": {
-        "async-limiter": "1.0.0",
-        "safe-buffer": "5.1.1",
-        "ultron": "1.1.1"
-      }
+    "utils-merge": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz",
+      "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM="
     },
-    "xmlhttprequest-ssl": {
-      "version": "1.5.4",
-      "resolved": "https://registry.npmjs.org/xmlhttprequest-ssl/-/xmlhttprequest-ssl-1.5.4.tgz",
-      "integrity": "sha1-BPVgkVcks4kIhxXMDteBPpZ3v1c="
+    "vary": {
+      "version": "1.1.2",
+      "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz",
+      "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw="
     },
-    "yeast": {
-      "version": "0.1.2",
-      "resolved": "https://registry.npmjs.org/yeast/-/yeast-0.1.2.tgz",
-      "integrity": "sha1-AI4G2AlDIMNy28L47XagymyKxBk="
+    "wordwrap": {
+      "version": "0.0.3",
+      "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.3.tgz",
+      "integrity": "sha1-o9XabNXAvAAI03I0u68b7WMFkQc="
     }
   }
 }

+ 26 - 30
package.json

@@ -1,35 +1,31 @@
 {
-  "name": "livecodingspace",
-  "description": "LiveCoding.Space",
-  "version": "0.0.1",
-  "author": "Nikolai Suslov",
-  "scripts": {
-    "start": "node ./node-server.js --applicationPath=./public -p 3001",
-    "startSSL": "node ./node-server.js --applicationPath=./public -p 3001 -s -k ./cert/key.pem -c ./cert/cert.pem",
-    "test": "echo \"Error: no test specified\" && exit 1"
-  },
-  "directories": {
-    "lib": "lib/nodejs"
-  },
-  "main": "node-server.js",
-  "homepage": "http://livecoding.space",
-  "repository": {
-    "type": "git",
-    "url": "https://github.com/NikolaySuslov/livecodingspace.git"
-  },
+  "name": "lcs2",
+  "version": "0.0.2",
+  "description": "LiveCoding.space",
+  "homepage": "http://www.krestianstvo.org",
+  "main": "index.js",
   "dependencies": {
-    "crypto": "1.0.1",
-    "socket.io": "2.0.4",
-    "socket.io-client": "^2.0.4",
-    "async": "2.6.0",
-    "mime": "2.2.0",
-    "js-yaml": "3.10.0",
-    "optimist": "0.6.1",
-    "fs-extra": "5.0.0"
+    "body-parser": "^1.18.3",
+    "compression": "^1.7.3",
+    "cors": "^2.8.4",
+    "express": "^4.16.3",
+    "morgan": "^1.9.0",
+    "serve-index": "^1.9.1",
+    "serve-static": "^1.13.2",
+    "optimist": "0.6.1"
   },
-  "devDependencies": {
-    "mocha": "x.x.x",
-    "should": "x.x.x"
+  "devDependencies": {},
+  "scripts": {
+    "start": "node index.js -p 3007",
+    "startSSL": "node index.js -p 3007 -s -k ./certs/server-key.pem -c ./certs/server-crt.pem -t ./certs/ca-crt.pem -w 12345"
   },
-  "license": "Apache"
+  "keywords": [
+    "krestianstvo"
+  ],
+  "author": "Nikolai Suslov",
+  "license": "MIT",
+  "readmeFilename": "README.md",
+  "repository": {
+    "type": "git"
+  }
 }

+ 0 - 14
public/404.html

@@ -1,14 +0,0 @@
-<html xmlns="http://www.w3.org/1999/xhtml">    
-  <head>      
-    <title>LiveCoding.space</title>
-    <link href="/web/css/redirect.css" rel="stylesheet" type="text/css" />
-  </head>
-  <body>
-    <p>
-      Unfortunately, your path in this virtual world hit a dead end. 
-    </p>
-    <p>
-      Check out the <a href="http://livecoding.space">demo page</a> for more apps.
-    </p> 
-  </body>  
-</html>

+ 1289 - 0
public/app.js

@@ -0,0 +1,1289 @@
+import { Lang } from '/lib/polyglot/language.js';
+import { Helpers } from '/helpers.js';
+import { IndexApp } from '/web/index-app.js';
+import { Widgets } from '/lib/widgets.js';
+
+
+class App {
+  constructor() {
+    console.log("app constructor");
+    this.widgets = new Widgets;
+
+    //globals
+    window._app = this;
+    window._cellWidgets = this.widgets;
+    window._LangManager = new Lang;
+    window._noty = new Noty;
+
+    _LangManager.setLanguage().then(res => {
+      return this.initDB()
+    }).then(res => {
+
+      this.helpers = new Helpers;
+      this.initUser();
+      
+      //client routes
+      page('/', this.HandleIndex);
+      page('/setup', this.HandleSetupIndex);
+      page('/profile', this.HandleUserIndex);
+      page('/worlds', this.HandleIndex);
+      page('/:user/worlds', this.HandleUserWorlds);
+      page('/:user/worlds/:type', this.HandleUserWorldsWithType);
+      page('/:user/:type/:name/edit/:file', this.HandleFileEdit);
+      page('/:user/:space', this.HandleParsableRequestGenID);
+      page('/:user/:space/:id', this.HandleParsableRequestWithID);
+      page('/:user/:space/index.vwf/:id', this.HandleParsableRequestWithID);
+      page('/:user/:space/load/:savename', this.HandleParsableLoadRequest);
+      page('/:user/:space/:id/load/:savename', this.HandleParsableRequestWithID);
+
+      page('/:user/:space/load/:savename/:rev', this.HandleParsableLoadRequestWithRev);
+      page('/:user/:space/:id/load/:savename/:rev', this.HandleParsableRequestWithID);
+
+      page('*', this.HandleNoPage);
+
+      page();
+
+
+    })
+
+
+  }
+
+  initDB() {
+
+    var config = JSON.parse(localStorage.getItem('lcs_config'));
+    if (!config) {
+      config = {
+        'dbhost': 'https://' + window.location.hostname + ':8080/gun', //'http://localhost:8080/gun',
+        'reflector': 'https://' + window.location.hostname + ':3002',
+        'language': 'en'
+      }
+      localStorage.setItem('lcs_config', JSON.stringify(config));
+    }
+
+    const dbConnection = new Promise((resolve, reject) => {
+
+      this.db = Gun(this.dbHost);
+      this.user = this.db.user();
+      window._LCSDB = this.db;
+      window._LCSUSER = this.user;
+      window._LCS_SYS_USER = undefined;
+      window._LCS_WORLD_USER = undefined;
+
+      _LCSDB.get('lcs/app').get('pub').once(res => {
+
+        if (res) {
+          window._LCS_SYS_USER = this.db.user(res);
+        }
+      });
+
+      _LCSDB.on('hi', function (peer) {
+
+        let msg = 'Connected to ' + peer.url;
+
+        let noty = new Noty({
+          text: msg,
+          timeout: 2000,
+          theme: 'mint',
+          layout: 'bottomRight',
+          type: 'success'
+        });
+        noty.show();
+        console.log(msg)
+      })
+
+      _LCSDB.on('bye', function (peer) {
+
+        let msg = 'No connection to ' + peer.url;
+        let noty = new Noty({
+          text: msg,
+          timeout: 1000,
+          theme: 'mint',
+          layout: 'bottomRight',
+          type: 'error'
+        });
+        noty.show();
+        console.log(msg)
+      })
+      resolve('ok');
+    });
+    return dbConnection
+  }
+
+  initUser() {
+    _LCSUSER.recall({ sessionStorage: 1 });
+  }
+
+
+  get reflectorHost() {
+
+    var res = "";
+    let config = localStorage.getItem('lcs_config');
+    if (config) {
+      res = JSON.parse(config).reflector;
+    }
+    return res;
+  }
+
+  get dbHost() {
+
+    var res = "";
+    let config = localStorage.getItem('lcs_config');
+    if (config) {
+      res = JSON.parse(config).dbhost;
+    }
+    return res;
+  }
+
+  async loadProxyDefaults() {
+
+    //load to DB default proxy files (VWF & A-Frame components)
+
+    let proxyResponse = await fetch('/proxy-files', { method: 'get' });
+    let proxyFiles = await proxyResponse.json();
+    let filterProxyFiles = proxyFiles.filter(el => (el !== null));
+    console.log(filterProxyFiles);
+
+    var origin = window.location.origin;
+    //var userPub = _LCSUSER.is.pub;
+
+    let proxyObj = {};
+
+    for (var index in filterProxyFiles) {
+
+      let el = filterProxyFiles[index];
+
+      if (el) {
+        var url = origin + el;
+        var entryName = url.replace(origin + '/defaults/', "").split(".").join("_");
+        let proxyFile = await fetch(url, { method: 'get' });
+        let responseText = await proxyFile.text();
+
+        if (responseText) {
+          let obj = {
+            //'owner': userPub,
+            'file': responseText
+          }
+          proxyObj[entryName] = obj;
+        }
+      }
+    }
+    console.log(proxyObj);
+
+    Object.keys(proxyObj).forEach(el => {
+      _LCSDB.user().get('proxy').get(el).put(proxyObj[el]);
+    })
+
+  }
+
+  async loadWorldsDefaults() {
+
+   //load to DB default worlds
+
+    let worldsResponse = await fetch('/world-files', { method: 'get' });
+    let worldFiles = await worldsResponse.json();
+    let filterworldFiles = worldFiles.filter(el => (el !== null));
+    console.log(filterworldFiles);
+
+    let worldsObj = {};
+
+    for (var index in filterworldFiles) {
+
+      let el = filterworldFiles[index];
+
+      if (el) {
+        let url = window.location.origin + el;
+        var entryName = url.replace(window.location.origin + '/defaults/worlds/', "").split(".").join("_");
+
+        let worldName = entryName.split("/")[0];
+        let userPub = _LCSUSER.is.pub;
+
+        let worldFile = await fetch(url, { method: 'get' });
+        let worldSource = await worldFile.text();
+        if (worldSource) {
+          let modified = new Date().valueOf();
+
+          let obj = {
+            'file': worldSource,
+            'modified': modified
+
+          }
+
+          if (!worldsObj[worldName]) {
+            worldsObj[worldName] = {
+              'parent': '-',
+              'owner': userPub,
+              'featured': true,
+              'published': true
+            }
+          }
+
+          let entry = entryName.replace(worldName + '/', "");
+          worldsObj[worldName][entry] = obj;
+
+        }
+      }
+    }
+
+    console.log(worldsObj);
+
+    Object.entries(worldsObj).forEach(el => {
+
+      let worldName = el[0];
+      let files = el[1];
+      Object.entries(files).forEach(file => {
+
+        _LCSDB.user().get('worlds').get(worldName).get(file[0]).put(file[1]);
+
+      })
+    })
+  }
+
+  async loadEmptyDefaultProto() {
+
+     //empty proto world
+     let userPub = _LCSUSER.is.pub;
+     let worldsObj = {};
+     let emptyWorld = {
+
+      "index_vwf_yaml": YAML.stringify(
+        {
+          "extends": "http://vwf.example.com/aframe/ascene.vwf"
+        }, 4),
+
+      "index_vwf_config_yaml": YAML.stringify(
+        {
+        "info": {
+          "title": "Empty World"
+        },
+        "model": {
+          "vwf/model/aframe": null
+        },
+        "view": {
+          "vwf/view/aframe": null,
+          "vwf/view/editor-new": null
+        } 
+      }, 4),
+
+      "index_vwf_html": "",
+      "appui_js": "",
+      "info_json": JSON.stringify ({
+        "info": {
+          "en": {
+              "title": "Empty World",
+              "imgUrl": "",
+              "text": "Empty World"
+          },
+          "ru": {
+              "title": "Новый Мир",
+              "imgUrl": "",
+              "text": "Новый Мир"
+          }
+      }
+      })
+    }
+
+    worldsObj['empty'] = {
+      'parent': '-',
+      'owner': userPub,
+      'featured': true,
+      'published': true
+    }
+
+    Object.keys(emptyWorld).forEach(el=>{
+      let modified = new Date().valueOf();
+      let obj = {
+        'file': emptyWorld[el],
+        'modified': modified
+      }
+      worldsObj['empty'][el] = obj;
+    }) 
+
+    console.log(worldsObj);
+
+    Object.entries(worldsObj).forEach(el => {
+
+      let worldName = el[0];
+      let files = el[1];
+      Object.entries(files).forEach(file => {
+
+        _LCSDB.user().get('worlds').get(worldName).get(file[0]).put(file[1]);
+
+      })
+    })
+
+  }
+
+  //load defaults for first registered user running ./setup
+
+  HandleSetupIndex() {
+
+    window._app.hideProgressBar();
+    window._app.hideUIControl();
+
+    let el = document.createElement("div");
+    el.setAttribute("id", "admin");
+    document.body.appendChild(el);
+
+   
+    _LCSDB.on('auth',
+      async function (ack) {
+
+        if (_LCSUSER.is) {
+
+          let setPubKey = {
+
+            $cell: true,
+            $components: [
+              {
+                $type: "p",
+                class: "mdc-typography--headline5",
+                $text: "1. Set app system user PUB key"
+            },
+              {
+                $type: "button",
+                class: "mdc-button mdc-button--raised",
+                $text: "Set app PUB key",
+                onclick: function (e) {
+                  console.log("admin action");
+                  _LCSDB.get('lcs/app').get('pub').put(_LCSUSER.is.pub);
+                }
+              }
+            ]
+      
+          }
+      
+          let adminComponents = [];
+      
+
+          let defaultPub = await _LCSDB.get('lcs/app').get('pub').once().then();
+          if (!defaultPub) {
+            adminComponents.push(setPubKey);
+          }
+
+          if (_LCSUSER.is.pub == defaultPub) {
+
+            let loadEmpty = {
+              $cell: true,
+              $components: [
+                {
+                  $type: "p",
+                  class: "mdc-typography--headline5",
+                  $text: "3. Initialize empty World proto"
+              },
+                {
+                  $type: "button",
+                  id: "loadDefaults",
+                  class: "mdc-button mdc-button--raised",
+                  $text: "Init empty world",
+                  onclick: function (e) {
+                    console.log("admin action");
+                    window._app.loadEmptyDefaultProto();
+                  }
+                }
+              ]
+            }
+            
+
+            let loadDefaults = {
+              $cell: true,
+              $components: [
+                {
+                  $type: "p",
+                  class: "mdc-typography--headline5",
+                  $text: "4. Load Sample Worlds protos from server (optional)"
+              },
+                {
+                  $type: "button",
+                  id: "loadDefaults",
+                  class: "mdc-button mdc-button--raised",
+                  $text: "Load default worlds (from server)",
+                  onclick: function (e) {
+                    console.log("admin action");
+                    window._app.loadWorldsDefaults();
+                  }
+                }
+              ]
+            }
+
+            let loadDefaultsProxy = {
+              $cell: true,
+              $components: [
+                {
+                  $type: "p",
+                  class: "mdc-typography--headline5",
+                  $text: "3. Load VWF & A-Frame default components"
+              },
+                {
+                  $type: "button",
+                  class: "mdc-button mdc-button--raised",
+                  $text: "Load defaults Proxy",
+                  onclick: function (e) {
+                    console.log("admin action");
+                    window._app.loadProxyDefaults();
+                  }
+                }
+              ]
+            }
+            adminComponents.push(setPubKey, loadDefaultsProxy, loadEmpty, loadDefaults);
+          }
+
+          document.querySelector("#admin").$cell({
+            $cell: true,
+            id: 'adminComponents',
+            $type: "div",
+            $components: adminComponents
+          });
+
+        }
+      })
+  }
+
+  //TODO: profile
+
+  HandleUserIndex(ctx) {
+
+    console.log("USER INDEX");
+    window._app.hideProgressBar();
+    window._app.hideUIControl();
+
+    _LCSDB.on('auth',
+      async function (ack) {
+        if(ack.pub){
+          document.querySelector("#profile")._status = "User: " + _LCSUSER.is.alias //+' pub: ' + _LCSUSER.is.pub;
+          document.querySelector("#profile").$update();
+        }
+      })
+
+      let el = document.createElement("div");
+      el.setAttribute("id", "userProfile");
+      document.body.appendChild(el);
+
+      let userProfile = {
+        $type: 'div',
+        id: "profile",
+        _status: "",
+        $init: function(){
+          this._status = "user is not signed in..."
+        },
+        $update: function(){
+          this.$components = [
+            {
+                $type: "h1",
+                class: "mdc-typography--headline4",
+                $text: this._status //"Profile for: " + _LCSUSER.is.alias
+            }
+          ]
+        }
+      }
+
+      document.querySelector("#userProfile").$cell({
+        $cell: true,
+        $type: "div",
+        $components: [userProfile]
+      })
+  }
+
+
+  async HandleUserWorlds(ctx) {
+
+    console.log("USER WORLDS INDEX");
+    console.log(ctx.params);
+    let user = ctx.params.user;
+
+    page.redirect('/' + user + '/worlds/protos');
+
+  }
+
+  async HandleFileEdit(ctx) {
+
+    console.log("USER WORLD FILE EDIT");
+
+    let user = ctx.params.user;
+    let worldName = ctx.params.name;
+    let fileOriginal = ctx.params.file;
+    let type = ctx.params.type;
+
+    window._app.hideProgressBar();
+    window._app.hideUIControl();
+
+
+    _LCSDB.on('auth',
+      async function (ack) {
+
+        if (_LCSUSER.is) {
+
+          if (_LCSUSER.is.alias == user) {
+
+            var worldType = 'worlds';
+            var file = fileOriginal;
+            if (type == 'state') {
+              worldType = 'documents';
+              file = _app.helpers.replaceSubStringALL(fileOriginal, "~", '/');
+            }
+
+            let worldFile = await _LCSUSER.get(worldType).get(worldName).get(file).once().then();
+            if (worldFile) {
+              console.log(worldFile.file);
+
+              let el = document.createElement("div");
+              el.setAttribute("id", "worldFILE");
+              document.body.appendChild(el);
+
+
+              let aceEditorCell = {
+
+                $type: "div",
+                $components: [
+                  {
+                    class: "aceEditor",
+                    id: "aceEditor",
+                    //style: "width:1200px; height: 800px",
+                    $type: "div",
+                    $text: worldFile.file,
+                    $init: function () {
+                      var mode = "ace/mode/json";
+                      if (file.includes('_yaml'))
+                        mode = "ace/mode/yaml"
+                      if (file.includes('_js'))
+                        mode = "ace/mode/javascript"
+
+                      var editor = ace.edit("aceEditor");
+                      editor.setTheme("ace/theme/monokai");
+                      editor.setFontSize(16);
+                      editor.getSession().setMode(mode);
+                      editor.setOptions({
+                        maxLines: Infinity
+                    });
+                    }
+                  },
+                  {
+                    $type: "button",
+                    class: "mdc-button mdc-button--raised",
+                    $text: "Save",
+                    onclick: async function (e) {
+                      console.log("save new info");
+                      let editor = document.querySelector("#aceEditor").env.editor;
+                      let newInfo = editor.getValue();
+                      _LCSUSER.get(worldType).get(worldName).get(file).get('file').put(newInfo, res => {
+                        if (res) {
+                          let modified = new Date().valueOf();
+                          _LCSUSER.get(worldType).get(worldName).get(file).get('modified').put(modified);
+                        }
+                      })
+                    }
+                  },
+                  {
+                    $type: "button",
+                    class: "mdc-button mdc-button--raised",
+                    $text: "Close",
+                    onclick: function (e) {
+                      console.log("close");
+                      if (type == "proto")
+                        window.location.pathname = "/" + user + '/worlds/protos'
+                      if (type == "state")
+                        window.location.pathname = "/" + user + '/worlds/states'
+                    }
+                  }
+                ]
+              }
+              document.querySelector("#worldFILE").$cell({
+                $cell: true,
+                $type: "div",
+                $components: [aceEditorCell
+                ]
+              })
+            }
+          }
+        }
+      })
+  }
+
+  async HandleUserWorldsWithType(ctx) {
+
+    console.log("USER WORLDS INDEX");
+    console.log(ctx.params);
+    let user = ctx.params.user;
+    let type = ctx.params.type;
+
+    window._app.hideProgressBar();
+    window._app.hideUIControl();
+
+    if (!_app.indexApp) {
+      _app.indexApp = new IndexApp;
+      document.querySelector('head').innerHTML += '<link rel="stylesheet" href="/web/index-app.css">';
+    }
+
+    if (!document.querySelector('#app')) {
+      await _app.indexApp.initApp();
+    }
+
+    if (type == 'protos') {
+      await _app.indexApp.getWorldsProtosFromUserDB(user);
+    } else if (type == 'states') {
+      await _app.indexApp.getWorldsFromUserDB(user);
+    }
+
+  }
+
+  async HandleIndex() {
+
+    console.log("INDEX");
+
+    window._app.hideProgressBar();
+    window._app.hideUIControl();
+
+    _app.indexApp = new IndexApp;
+    document.querySelector('head').innerHTML += '<link rel="stylesheet" href="/web/index-app.css">';
+
+    await _app.indexApp.generateFrontPage();
+    await _app.indexApp.initApp();
+    await _app.indexApp.getAppDetailsFromDB();
+
+  }
+
+
+  HandleNoPage() {
+
+    console.log("no such page")
+
+  }
+
+  //handle parcable requests
+
+  HandleParsableLoadRequest(ctx) {
+
+    let app = window._app;
+    console.log(ctx.params);
+    //var pathname = ctx.pathname;
+    var spaceName = ctx.params.space;
+    var saveName = ctx.params.savename;
+    let user = ctx.params.user;
+
+    page.redirect('/' + user + '/' + spaceName + '/' + app.helpers.GenerateInstanceID() + '/load/' + saveName);
+
+  }
+
+  HandleParsableLoadRequestWithRev(ctx) {
+
+    let app = window._app;
+    console.log(ctx.params);
+    //var pathname = ctx.pathname;
+    var spaceName = ctx.params.space;
+    var saveName = ctx.params.savename;
+    var rev = ctx.params.rev;
+    let user = ctx.params.user;
+
+    page.redirect('/' + user + '/' + spaceName + '/' + app.helpers.GenerateInstanceID() + '/load/' + saveName + '/' + rev);
+
+  }
+
+  async setUserPaths(user) {
+
+    await _LCSDB.get('users').get(user).get('pub').once(res => {
+      if (res)
+        window._LCS_WORLD_USER = _LCSDB.user(res);
+    }).then();
+
+  }
+
+  async HandleParsableRequestGenID(ctx) {
+
+    let app = window._app;
+    console.log(ctx.params);
+    let user = ctx.params.user;
+    var pathname = ctx.pathname;
+
+    await app.setUserPaths(user);
+
+    if (pathname[pathname.length - 1] == '/') {
+      pathname = pathname.slice(0, -1)
+    }
+
+    let pathToParse = pathname.replace('/' + user, "");
+
+    app.helpers.Process(pathToParse).then(parsedRequest => {
+
+      localStorage.setItem('lcs_app', JSON.stringify({ path: parsedRequest }));
+      console.log(parsedRequest);
+
+      if ((parsedRequest['instance'] == undefined) && (parsedRequest['private_path'] == undefined) && (parsedRequest['public_path'] !== "/") && (parsedRequest['application'] !== undefined)) {
+
+        page.redirect(pathname + '/' + app.helpers.GenerateInstanceID());
+
+      }
+    });
+
+  }
+
+  
+  async HandleParsableRequestWithID(ctx) {
+
+    let app = window._app;
+    console.log(ctx.params);
+    var pathname = ctx.pathname;
+    let user = ctx.params.user;
+
+    if (pathname[pathname.length - 1] == '/') {
+      pathname = pathname.slice(0, -1)
+    }
+
+    await app.setUserPaths(user);
+
+    let pathToParse = pathname.replace('/' + user, "");
+
+    app.helpers.Process(pathToParse).then(parsedRequest => {
+
+      localStorage.setItem('lcs_app', JSON.stringify({ path: parsedRequest }));
+      console.log(parsedRequest);
+
+      var userLibraries = { model: {}, view: {} };
+      var application;
+
+      vwf.loadConfiguration(application, userLibraries, compatibilityCheck);
+    });
+  }
+
+  async HandleParsableRequest(ctx) {
+
+    let app = window._app;
+    console.log(ctx.params);
+    var pathname = ctx.pathname;
+
+    if (pathname[pathname.length - 1] == '/') {
+      pathname = pathname.slice(0, -1)
+    }
+
+    var parsedRequest = await app.helpers.Process(pathname);
+    localStorage.setItem('lcs_app', JSON.stringify({ path: parsedRequest }));
+    console.log(parsedRequest);
+
+    if ((parsedRequest['instance'] == undefined) && (parsedRequest['private_path'] == undefined) && (parsedRequest['public_path'] !== "/") && (parsedRequest['application'] !== undefined)) {
+
+      // Redirect if the url request does not include an application/file && a default 'index.vwf.yaml' exists
+      // page.redirect(pathname + '/' + app.helpers.GenerateInstanceID());
+      window.location.pathname = pathname + '/' + app.helpers.GenerateInstanceID() 
+
+      //return true;
+    } else {
+
+      //return false;
+    }
+
+    var userLibraries = { model: {}, view: {} };
+    var application;
+
+    vwf.loadConfiguration(application, userLibraries, compatibilityCheck);
+
+  }
+
+  //get DB application state information for reflector (called from VWF)
+
+  async getApplicationState() {
+
+    let dataJson = JSON.parse(localStorage.getItem('lcs_app'));
+    if (dataJson) {
+      if (!dataJson.path['instance']) return undefined;
+    }
+
+    let userAlias = await _LCS_WORLD_USER.get('alias').once().then();
+    let userPub = await _LCSDB.get('users').get(userAlias).get('pub').once().then();
+
+    let loadInfo = await this.getLoadInformation(dataJson);
+    let saveInfo = await this.loadSaveObject(loadInfo);
+    let loadObj = {
+      loadInfo: loadInfo,
+      path: dataJson.path,
+      saveObject: saveInfo,
+      user: userAlias
+    }
+    console.log(loadObj);
+
+    //temporary solution for syncing DB replicas using Gun.load()
+    await _LCS_SYS_USER.get('proxy').load(res=>{}, {wait: 200}).then();
+    await _LCSDB.user(userPub).get('worlds').get(loadObj.path.public_path.slice(1)).load(res=>{}, {wait: 200}).then();
+
+    return loadObj
+
+  }
+
+  // LookupSaveRevisions takes the public path and the name of a save, and provides
+  // an array of all revisions for that save. (If the save does not exist, this will be
+  // an empty array).
+
+  async lookupSaveRevisions(public_path, save_name) {
+    var result = [];
+    var states = [];
+    let docName = 'savestate_/' + public_path + '/' + save_name + '_vwf_json';
+
+    let revs = await _LCS_WORLD_USER.get('documents').get(public_path).get(docName).get('revs').once().then();
+    if (revs) {
+      for (const res of Object.keys(revs)) {
+        if (res !== '_') {
+          let el = await _LCS_WORLD_USER.get('documents').get(public_path).get(docName).get('revs').get(res).once().then();
+          if (el)
+            result.push(parseInt(el.revision));
+        }
+      }
+      return result
+    }
+  }
+
+  // GetLoadInformation receives a parsed request {private_path, public_path, instance, application} and returns the
+  // details of the save that is designated by the initial request. The details are returned in an object
+  // composed of: save_name (name of the save) save_revision (revision of the save), explicit_revision (boolean, true if the request
+  // explicitly specified the revision, false if it did not), and application_path (the public_path of the application this is a save for).
+
+  async getLoadInformation(response) {
+    let parsedRequest = response.path;
+    var result = { 'save_name': undefined, 'save_revision': undefined, 'explicit_revision': undefined, 'application_path': undefined };
+    if (parsedRequest['private_path']) {
+      var segments = this.helpers.GenerateSegments(parsedRequest['private_path']);
+      if ((segments.length > 1) && (segments[0] == "load")) {
+        var potentialRevisions = await this.lookupSaveRevisions((parsedRequest['public_path']).slice(1), segments[1]);
+
+        console.log('!!!!! - ', potentialRevisions);
+        if (potentialRevisions.length > 0) {
+          result['save_name'] = segments[1];
+
+          if (segments.length > 2) {
+            var requestedRevision = parseInt(segments[2]);
+            if (requestedRevision) {
+              if (potentialRevisions.indexOf(requestedRevision) > -1) {
+                result['save_revision'] = requestedRevision;
+                result['explicit_revision'] = true;
+                result['application_path'] = parsedRequest['public_path'];
+              }
+            }
+          }
+          if (result['explicit_revision'] == undefined) {
+            result['explicit_revision'] = false;
+            potentialRevisions.sort();
+            result['save_revision'] = potentialRevisions.pop();
+            result['application_path'] = parsedRequest['public_path'];
+          }
+        }
+      }
+
+    }
+    return result;
+  }
+
+  async loadSaveObject(loadInfo) {
+
+    //let objName = loadInfo[ 'save_name' ] +'/'+ "savestate_" + loadInfo[ 'save_revision' ];
+
+    let objName = "savestate_" + loadInfo['application_path'] + '/' + loadInfo['save_name'] + '_vwf_json';
+    let objNameRev = "savestate_" + loadInfo['save_revision'] + loadInfo['application_path'] + '/' + loadInfo['save_name'] + '_vwf_json';
+
+    // if(loadInfo[ 'save_revision' ]){
+    // } 
+
+    let worldName = this.helpers.appPath //loadInfo[ 'application_path' ].slice(1);
+
+    let saveObject = await _LCS_WORLD_USER.get('documents').get(worldName).get(objName).get('revs').get(objNameRev).once().then();
+    let saveInfo = saveObject ? JSON.parse(saveObject.jsonState) : saveObject;
+
+    return saveInfo;
+
+  }
+
+  // GetSaveInformation is a helper function that takes the application_path (/path/to/application).
+  // It returns an array of all saves found for that
+  // application (including separate entries for individual revisions of saves ).
+  async getSaveInformation(application_path, userPUB) {
+    var result = [];
+    let user = _LCSDB.user(userPUB);
+
+    var docName = application_path.slice(1);
+    let potentialSaveNames = await user.get('documents').get(docName).once().then();
+
+    if (potentialSaveNames) {
+
+      for (const res of Object.keys(potentialSaveNames)) {
+        if (res !== '_') {
+
+          let el = await user.get('documents').path(docName).get(res).once().then();
+
+          let revisionList = await this.lookupSaveRevisions(application_path.slice(1), el.filename);
+          var latestsave = true;
+          revisionList.sort();
+          while (revisionList.length > 0) {
+            var newEntry = {};
+            newEntry['applicationpath'] = application_path;
+            newEntry['savename'] = el.filename;
+            newEntry['revision'] = revisionList.pop().toString();
+            newEntry['latestsave'] = latestsave;
+            if (latestsave) {
+              newEntry['url'] = this.helpers.JoinPath(window.location.origin, application_path, "load", el.filename + "/");
+            }
+            else {
+              newEntry['url'] = this.helpers.JoinPath(window.location.origin, application_path, "load", el.filename + "/", newEntry['revision'] + "/");
+            }
+            latestsave = false;
+            result.push(newEntry);
+          }
+        }
+      }
+    }
+    return result;
+  }
+
+  async getProtoWorldFiles(userPub, worldName, date) {
+
+    let fileNamesAll = await _LCSDB.user(userPub).get('worlds').get(worldName).once().then();
+    let worldFileNames = Object.keys(fileNamesAll).filter(el => (el !== '_') && (el !== 'owner') && (el !== 'parent')
+      && (el !== 'info_json'));
+
+    let worldObj = {};
+    for (var el in worldFileNames) {
+      let fn = worldFileNames[el];
+      let res = await _LCSDB.user(userPub).get('worlds').get(worldName).get(fn).once().then();
+      var data = {
+        'file': res.file,
+        'modified': res.modified
+      }
+      if (!date) {
+        data = {
+          'file': res.file
+        }
+      }
+      worldObj[fn] = data;
+    }
+    console.log(worldObj);
+    return worldObj
+  }
+
+  async cloneWorldPrototype(worldName, userName, newWorldName) {
+
+    let userPub = await _LCSDB.get('users').get(userName).get('pub').once().then();
+    //let worldProto = await _LCSDB.user(userPub).get('worlds').get(worldName).once().then();
+
+    var worldID = window._app.helpers.GenerateInstanceID().toString();
+    if (newWorldName) {
+      worldID = newWorldName
+    }
+
+    //let modified = new Date().valueOf();
+    console.log('clone: ' + worldName + 'to: ' + worldID);
+
+    let newOwner = _LCSUSER.is.pub;
+
+    let worldObj = {
+      'owner': newOwner,
+      'parent': userName + '/' + worldName
+    };
+
+    let fileNamesAll = await _LCSDB.user(userPub).get('worlds').get(worldName).once().then();
+    let worldFileNames = Object.keys(fileNamesAll).filter(el => (el !== '_') && (el !== 'owner') && (el !== 'parent'));
+
+    for (var el in worldFileNames) {
+
+      let fn = worldFileNames[el];
+      let res = await _LCSDB.user(userPub).get('worlds').get(worldName).get(fn).once().then();
+      let data = {
+        'file': res.file,
+        'modified': res.modified
+      }
+      worldObj[fn] = data;
+    }
+    console.log(worldObj);
+
+    Object.keys(worldObj).forEach(el => {
+      _LCSUSER.get('worlds').get(worldID).get(el).put(worldObj[el]);
+    })
+  }
+
+  async cloneWorldState(filename) {
+
+    let myWorldProtos = await _LCSUSER.get('worlds').once().then();
+    let userName = this.helpers.worldUser;
+    let userPub = await _LCSDB.get('users').get(userName).get('pub').once().then();
+    let protoUserRoot = this.helpers.getRoot(true).root;
+    //let myName = _LCSUSER.is.alias;
+
+    //let proto = Object.keys(myWorldProtos).filter(el => el == protoUserRoot);
+
+    var protosKeys = [];
+    if (myWorldProtos)
+      protosKeys = Object.keys(myWorldProtos);
+
+    if (protosKeys.includes(protoUserRoot)) {
+
+      let userProtoFiles = await this.getProtoWorldFiles(userPub, protoUserRoot);
+      let myProtoFiles = await this.getProtoWorldFiles(_LCSUSER.is.pub, protoUserRoot);
+
+      let hashUP = await this.helpers.sha256(JSON.stringify(userProtoFiles));
+      let hashMP = await this.helpers.sha256(JSON.stringify(myProtoFiles));
+
+      if (hashUP == hashMP) {
+        this.saveStateAsFile(filename);
+      } else {
+
+        let noty = new Noty({
+          text: 'world prototype is modified.. could not clone world state',
+          timeout: 2000,
+          theme: 'mint',
+          layout: 'bottomRight',
+          type: 'error'
+        });
+        noty.show();
+      }
+    } else {
+      await this.cloneWorldPrototype(protoUserRoot, userName, protoUserRoot);
+      this.saveStateAsFile(filename);
+    }
+  }
+
+  //TODO: refactor and config save
+  saveStateAsFile(filename, otherProto) // invoke with the view as "this"
+  {
+    console.log("Saving: " + filename);
+
+    //var clients = this.nodes["http://vwf.example.com/clients.vwf"];
+
+    // Save State Information
+    var state = vwf.getState();
+    state.nodes[0].children = {};
+
+    var timestamp = state["queue"].time;
+    timestamp = Math.round(timestamp * 1000);
+
+    var objectIsTypedArray = function (candidate) {
+      var typedArrayTypes = [
+        Int8Array,
+        Uint8Array,
+        // Uint8ClampedArray,
+        Int16Array,
+        Uint16Array,
+        Int32Array,
+        Uint32Array,
+        Float32Array,
+        Float64Array
+      ];
+
+      var isTypedArray = false;
+
+      if (typeof candidate == "object" && candidate != null) {
+        typedArrayTypes.forEach(function (typedArrayType) {
+          isTypedArray = isTypedArray || candidate instanceof typedArrayType;
+        });
+      }
+
+      return isTypedArray;
+    };
+
+    var transitTransformation = function (object) {
+      return objectIsTypedArray(object) ?
+        Array.prototype.slice.call(object) : object;
+    };
+
+
+    let jsonValuePure = require("vwf/utility").transform(
+      state, transitTransformation
+    );
+
+    //remove all Ohm generated grammarsfrom state
+
+    let jsonValue = _app.helpers.removeGrammarObj(jsonValuePure);
+    var jsonState = JSON.stringify(jsonValue);
+
+    let rootPath = this.helpers.getRoot(true);
+
+    var inst = rootPath.inst;
+    if (filename == '') filename = inst;
+    //if (root.indexOf('.vwf') != -1) root = root.substring(0, root.lastIndexOf('/'));
+    var root = rootPath.root;
+
+    var json = jsonState;
+
+    if (otherProto) {
+
+      console.log('need to modify state...');
+      json = this.helpers.replaceSubStringALL(jsonState, '/' + root + '/', '/' + otherProto + '/');//jsonState.replace(('/' + root + '/'), ('/' + otherProto +'/') );
+      root = otherProto;
+      console.log(json);
+    }
+
+    //var documents = _LCSUSER.get('documents');
+
+    var saveRevision = new Date().valueOf();
+
+    var stateForStore = {
+      "root": root,
+      "filename": filename,
+      "inst": inst,
+      "timestamp": timestamp,
+      "extension": ".vwf.json",
+      "jsonState": json,
+      "publish": true
+    };
+
+    //let objName = loadInfo[ 'save_name' ] +'/'+ "savestate_" + loadInfo[ 'save_revision' ];
+    // "savestate_" + loadInfo[ 'save_revision' ] + '/' + loadInfo[ 'save_name' ] + '_vwf_json'
+
+    var docName = 'savestate_/' + root + '/' + filename + '_vwf_json';
+    _LCSUSER.get('documents').get(root).get(docName).put(stateForStore, res => {
+
+      if (res) {
+
+        let noty = new Noty({
+          text: 'Saved to ' + docName,
+          timeout: 2000,
+          theme: 'mint',
+          layout: 'bottomRight',
+          type: 'success'
+        });
+        noty.show();
+      }
+    });
+
+    _LCSUSER.get('worlds').get(root).get('info_json').once(res => {
+
+      if (res) {
+
+        let modified = new Date().valueOf();
+        let newOwner = _LCSUSER.is.pub;
+        let userName = _LCSUSER.is.alias;
+
+        let obj = {
+          'parent': userName + '/' + root,
+          'owner': newOwner,
+          'file': res.file,
+          'modified': modified
+
+        }
+
+        let docInfoName = 'savestate_/' + root + '/' + filename + '_info_vwf_json';
+
+        _LCSUSER.get('documents').get(root).get(docInfoName).not(res => {
+          _LCSUSER.get('documents').get(root).get(docInfoName).put(obj);
+        });
+      }
+    });
+
+    var docNameRev = 'savestate_' + saveRevision.toString() + '/' + root + '/' + filename + '_vwf_json';
+    _LCSUSER.get('documents').get(root).get(docName).get('revs').get(docNameRev).put(stateForStore)
+      .path("revision").put(saveRevision);
+
+
+    // Save Config Information
+    var config = { "info": {}, "model": {}, "view": {} };
+
+    // Save browser title
+    config["info"]["title"] = document.title//$('title').html();
+
+    // Save model drivers
+    Object.keys(vwf_view.kernel.kernel.models).forEach(function (modelDriver) {
+      if (modelDriver.indexOf('vwf/model/') != -1) config["model"][modelDriver] = "";
+    });
+
+    // If neither glge or threejs model drivers are defined, specify nodriver
+    if (config["model"]["vwf/model/glge"] === undefined && config["model"]["vwf/model/threejs"] === undefined) config["model"]["nodriver"] = "";
+
+    // Save view drivers and associated parameters, if any
+    Object.keys(vwf_view.kernel.kernel.views).forEach(function (viewDriver) {
+      if (viewDriver.indexOf('vwf/view/') != -1) {
+        if (vwf_view.kernel.kernel.views[viewDriver].parameters) {
+          config["view"][viewDriver] = vwf_view.kernel.kernel.views[viewDriver].parameters;
+        }
+        else config["view"][viewDriver] = "";
+      }
+    });
+
+    //var jsonConfig = $.encoder.encodeForURL(JSON.stringify(config));
+    var jsonConfig = JSON.stringify(config);
+
+
+
+    let configStateForStore = {
+      "root": root,
+      "filename": filename,
+      "inst": inst,
+      "timestamp": timestamp,
+      "extension": "config.vwf.json",
+      "jsonState": jsonConfig
+    };
+
+    //let objName = loadInfo[ 'save_name' ] +'/'+ "savestate_" + loadInfo[ 'save_revision' ];
+    // "savestate_" + loadInfo[ 'save_revision' ] + '/' + loadInfo[ 'save_name' ] + '_vwf_json'
+
+    // let configName = 'savestate_/' + root + '/' + filename + '_config_vwf_json';
+    // let documentSaveConfigState = _LCSUSER.get(configName).put(configStateForStore);
+    // //documents.path(root).set(documentSaveConfigState);
+
+    // let configNameRev = 'savestate_' + saveRevision.toString() + '/' + root + '/' + filename + '_config_vwf_json';
+    // _LCSUSER.get(configNameRev).put(configStateForStore);
+    // _LCSUSER.get(configNameRev).path("revision").put(saveRevision);
+
+    //documentSaveConfigState.path('revs').set(documentSaveStateRevision);
+
+
+    // Save config file to server
+    // var xhrConfig = new XMLHttpRequest();
+    // xhrConfig.open("POST", "/" + root + "/save/" + filename, true);
+    // xhrConfig.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
+    // xhrConfig.send("root=" + root + "/" + filename + "&filename=saveState&inst=" + inst + "&timestamp=" + timestamp + "&extension=.vwf.config.json" + "&jsonState=" + jsonConfig);
+
+
+  }
+
+  // LoadSavedState 
+
+  async loadSavedState(filename, applicationpath, revision) {
+    console.log("Loading: " + filename);
+
+    let userName = await _LCS_WORLD_USER.get('alias').once().then();
+
+    if (revision) {
+      window.location.pathname = '/' + userName + applicationpath + '/load/' + filename + '/' + revision + '/';
+    }
+    else { // applicationpath + "/" + inst + '/load/' + filename + '/';
+      window.location.pathname = '/' + userName + applicationpath + '/load/' + filename + '/';
+    }
+
+  }
+
+  hideUIControl() {
+
+    var el = document.getElementById("ui-controls");
+    if (el) {
+      el.classList.remove("visible");
+      el.classList.add("not-visible");
+    }
+  }
+
+  showUIControl() {
+
+    var el = document.getElementById("ui-controls");
+    if (el) {
+      el.classList.remove("not-visible");
+      el.classList.add("visible");
+    }
+  }
+
+
+  hideProgressBar() {
+
+    var progressbar = document.getElementById("load-progressbar");
+    if (progressbar) {
+      progressbar.classList.remove("visible");
+      progressbar.classList.add("not-visible");
+      progressbar.classList.add("mdc-linear-progress--closed");
+
+    }
+
+  }
+
+  showProgressBar() {
+
+    let progressbar = document.getElementById("load-progressbar");
+    if (progressbar) {
+      progressbar.classList.add("visible");
+      progressbar.classList.add("mdc-linear-progress--indeterminate");
+    }
+  }
+
+}
+
+export { App }

File diff suppressed because it is too large
+ 0 - 113
public/assets/duck/duck.dae


BIN
public/assets/duck/duck.png


+ 0 - 0
public/assets/avatars/female/avatar1.gltf → public/defaults/assets/avatars/female/avatar1.gltf


+ 0 - 0
public/assets/avatars/female/best_avatar2-all.bin → public/defaults/assets/avatars/female/best_avatar2-all.bin


+ 0 - 0
public/assets/avatars/female/brown_eye.png → public/defaults/assets/avatars/female/brown_eye.png


+ 0 - 0
public/assets/avatars/female/eyebrow001.png → public/defaults/assets/avatars/female/eyebrow001.png


+ 0 - 0
public/assets/avatars/female/eyelashes01.png → public/defaults/assets/avatars/female/eyelashes01.png


+ 0 - 0
public/assets/avatars/female/long01_diffuse.png → public/defaults/assets/avatars/female/long01_diffuse.png


+ 0 - 0
public/assets/avatars/female/young_lightskinned_female_diffuse.png → public/defaults/assets/avatars/female/young_lightskinned_female_diffuse.png


+ 0 - 0
public/assets/avatars/ico/female.jpg → public/defaults/assets/avatars/ico/female.jpg


+ 0 - 0
public/assets/avatars/ico/male.jpg → public/defaults/assets/avatars/ico/male.jpg


+ 0 - 0
public/assets/avatars/ico/simple.jpg → public/defaults/assets/avatars/ico/simple.jpg


+ 0 - 0
public/assets/avatars/male/avatar1.bin → public/defaults/assets/avatars/male/avatar1.bin


+ 0 - 0
public/assets/avatars/male/avatar1.gltf → public/defaults/assets/avatars/male/avatar1.gltf


+ 0 - 0
public/assets/avatars/male/brown_eye.png → public/defaults/assets/avatars/male/brown_eye.png


+ 0 - 0
public/assets/avatars/male/eyebrow001.png → public/defaults/assets/avatars/male/eyebrow001.png


+ 0 - 0
public/assets/avatars/male/male_casualsuit01_diffuse.png → public/defaults/assets/avatars/male/male_casualsuit01_diffuse.png


+ 0 - 0
public/assets/avatars/male/male_casualsuit01_normal.png → public/defaults/assets/avatars/male/male_casualsuit01_normal.png


+ 0 - 0
public/assets/avatars/male/middleage_lightskinned_male_diffuse.png → public/defaults/assets/avatars/male/middleage_lightskinned_male_diffuse.png


+ 0 - 0
public/assets/avatars/male/short02_diffuse.png → public/defaults/assets/avatars/male/short02_diffuse.png


+ 0 - 0
public/assets/bg.jpg → public/defaults/assets/bg.jpg


+ 0 - 0
public/assets/checker.jpg → public/defaults/assets/checker.jpg


+ 0 - 0
public/ohmlang-lsys/lsys/Koch.jpg → public/defaults/assets/lsys/Koch.jpg


+ 0 - 0
public/ohmlang-lsys/lsys/SierpinskiCurve.jpg → public/defaults/assets/lsys/SierpinskiCurve.jpg


+ 0 - 0
public/ohmlang-lsys/lsys/SierpinskiTriangle.jpg → public/defaults/assets/lsys/SierpinskiTriangle.jpg


+ 0 - 0
public/ohmlang-lsys/lsys/dragon.jpg → public/defaults/assets/lsys/dragon.jpg


+ 0 - 0
public/assets/models/nwtree/materials.mtl → public/defaults/assets/models/nwtree/materials.mtl


+ 0 - 0
public/assets/models/nwtree/model.obj → public/defaults/assets/models/nwtree/model.obj


+ 0 - 0
public/assets/models/plane/PUSHILIN_PLANE.png → public/defaults/assets/models/plane/PUSHILIN_PLANE.png


+ 0 - 0
public/assets/models/plane/PUSHILIN_Plane.mtl → public/defaults/assets/models/plane/PUSHILIN_Plane.mtl


+ 0 - 0
public/assets/models/plane/PUSHILIN_Plane.obj → public/defaults/assets/models/plane/PUSHILIN_Plane.obj


+ 0 - 0
public/assets/skyes/sky1.jpg → public/defaults/assets/skyes/sky1.jpg


+ 0 - 0
public/assets/skyes/sky2.jpg → public/defaults/assets/skyes/sky2.jpg


+ 0 - 0
public/assets/skyes/sky3.jpg → public/defaults/assets/skyes/sky3.jpg


+ 0 - 0
public/assets/test.dae → public/defaults/assets/test.dae


+ 0 - 0
support/proxy/vwf.example.com/aframe/a-asset-audio-item.vwf.yaml → public/defaults/proxy/vwf.example.com/aframe/a-asset-audio-item.vwf.yaml


+ 0 - 0
support/proxy/vwf.example.com/aframe/a-asset-image-item.vwf.yaml → public/defaults/proxy/vwf.example.com/aframe/a-asset-image-item.vwf.yaml


+ 0 - 0
support/proxy/vwf.example.com/aframe/a-asset-item.vwf.yaml → public/defaults/proxy/vwf.example.com/aframe/a-asset-item.vwf.yaml


+ 0 - 0
support/proxy/vwf.example.com/aframe/a-asset-video-item.vwf.yaml → public/defaults/proxy/vwf.example.com/aframe/a-asset-video-item.vwf.yaml


+ 0 - 0
support/proxy/vwf.example.com/aframe/a-sound-component.js → public/defaults/proxy/vwf.example.com/aframe/a-sound-component.js


+ 0 - 0
support/proxy/vwf.example.com/aframe/a-sound-component.vwf.yaml → public/defaults/proxy/vwf.example.com/aframe/a-sound-component.vwf.yaml


+ 0 - 0
support/proxy/vwf.example.com/aframe/a-sun-sky.vwf.yaml → public/defaults/proxy/vwf.example.com/aframe/a-sun-sky.vwf.yaml


+ 0 - 0
support/proxy/vwf.example.com/aframe/aMaterialComponent.js → public/defaults/proxy/vwf.example.com/aframe/aMaterialComponent.js


+ 0 - 0
support/proxy/vwf.example.com/aframe/aMaterialComponent.vwf.yaml → public/defaults/proxy/vwf.example.com/aframe/aMaterialComponent.vwf.yaml


+ 0 - 0
support/proxy/vwf.example.com/aframe/aSceneFogComponent.vwf.yaml → public/defaults/proxy/vwf.example.com/aframe/aSceneFogComponent.vwf.yaml


+ 0 - 0
support/proxy/vwf.example.com/aframe/aanimation.vwf.yaml → public/defaults/proxy/vwf.example.com/aframe/aanimation.vwf.yaml


+ 0 - 0
support/proxy/vwf.example.com/aframe/abox.vwf.yaml → public/defaults/proxy/vwf.example.com/aframe/abox.vwf.yaml


+ 0 - 0
support/proxy/vwf.example.com/aframe/acamera.vwf.yaml → public/defaults/proxy/vwf.example.com/aframe/acamera.vwf.yaml


+ 0 - 0
support/proxy/vwf.example.com/aframe/acolladamodel.js → public/defaults/proxy/vwf.example.com/aframe/acolladamodel.js


+ 0 - 0
support/proxy/vwf.example.com/aframe/acolladamodel.vwf.yaml → public/defaults/proxy/vwf.example.com/aframe/acolladamodel.vwf.yaml


+ 0 - 0
support/proxy/vwf.example.com/aframe/acone.vwf.yaml → public/defaults/proxy/vwf.example.com/aframe/acone.vwf.yaml


+ 0 - 0
support/proxy/vwf.example.com/aframe/acursor.vwf.yaml → public/defaults/proxy/vwf.example.com/aframe/acursor.vwf.yaml


+ 0 - 0
support/proxy/vwf.example.com/aframe/acylinder.vwf.yaml → public/defaults/proxy/vwf.example.com/aframe/acylinder.vwf.yaml


+ 0 - 0
support/proxy/vwf.example.com/aframe/aentity.js → public/defaults/proxy/vwf.example.com/aframe/aentity.js


+ 0 - 0
support/proxy/vwf.example.com/aframe/aentity.vwf.yaml → public/defaults/proxy/vwf.example.com/aframe/aentity.vwf.yaml


+ 0 - 0
support/proxy/vwf.example.com/aframe/aentityComponent.vwf.yaml → public/defaults/proxy/vwf.example.com/aframe/aentityComponent.vwf.yaml


+ 0 - 0
support/proxy/vwf.example.com/aframe/agltfmodel.js → public/defaults/proxy/vwf.example.com/aframe/agltfmodel.js


+ 0 - 0
support/proxy/vwf.example.com/aframe/agltfmodel.vwf.yaml → public/defaults/proxy/vwf.example.com/aframe/agltfmodel.vwf.yaml


+ 0 - 0
support/proxy/vwf.example.com/aframe/alight.vwf.yaml → public/defaults/proxy/vwf.example.com/aframe/alight.vwf.yaml


+ 0 - 0
support/proxy/vwf.example.com/aframe/anim-mixer-component.vwf.yaml → public/defaults/proxy/vwf.example.com/aframe/anim-mixer-component.vwf.yaml


+ 0 - 0
support/proxy/vwf.example.com/aframe/aobjmodel.js → public/defaults/proxy/vwf.example.com/aframe/aobjmodel.js


+ 0 - 0
support/proxy/vwf.example.com/aframe/aobjmodel.vwf.yaml → public/defaults/proxy/vwf.example.com/aframe/aobjmodel.vwf.yaml


+ 0 - 0
support/proxy/vwf.example.com/aframe/aplane.vwf.yaml → public/defaults/proxy/vwf.example.com/aframe/aplane.vwf.yaml


+ 0 - 0
support/proxy/vwf.example.com/aframe/app-avatarbvh-component.vwf.yaml → public/defaults/proxy/vwf.example.com/aframe/app-avatarbvh-component.vwf.yaml


+ 0 - 0
support/proxy/vwf.example.com/aframe/app-cursor-listener-component.vwf.yaml → public/defaults/proxy/vwf.example.com/aframe/app-cursor-listener-component.vwf.yaml


+ 0 - 0
support/proxy/vwf.example.com/aframe/app-envmap-component.vwf.yaml → public/defaults/proxy/vwf.example.com/aframe/app-envmap-component.vwf.yaml


+ 0 - 0
support/proxy/vwf.example.com/aframe/app-raycaster-listener-component.vwf.yaml → public/defaults/proxy/vwf.example.com/aframe/app-raycaster-listener-component.vwf.yaml


+ 0 - 0
support/proxy/vwf.example.com/aframe/app-skyshader-component.vwf.yaml → public/defaults/proxy/vwf.example.com/aframe/app-skyshader-component.vwf.yaml


+ 0 - 0
support/proxy/vwf.example.com/aframe/app-sun-component.vwf.yaml → public/defaults/proxy/vwf.example.com/aframe/app-sun-component.vwf.yaml


+ 0 - 0
support/proxy/vwf.example.com/aframe/ascene.js → public/defaults/proxy/vwf.example.com/aframe/ascene.js


+ 0 - 0
support/proxy/vwf.example.com/aframe/ascene.vwf.yaml → public/defaults/proxy/vwf.example.com/aframe/ascene.vwf.yaml


+ 0 - 0
support/proxy/vwf.example.com/aframe/asky.vwf.yaml → public/defaults/proxy/vwf.example.com/aframe/asky.vwf.yaml


+ 0 - 0
support/proxy/vwf.example.com/aframe/asphere.vwf.yaml → public/defaults/proxy/vwf.example.com/aframe/asphere.vwf.yaml


+ 0 - 0
support/proxy/vwf.example.com/aframe/atext.vwf.yaml → public/defaults/proxy/vwf.example.com/aframe/atext.vwf.yaml


+ 17 - 4
support/proxy/vwf.example.com/aframe/avatar.js → public/defaults/proxy/vwf.example.com/aframe/avatar.js

@@ -37,7 +37,7 @@ this.modelBodyDef = {
 }
 
 
-this.createAvatarBody = function (modelSrc) {
+this.createAvatarBody = function (nodeDef, modelSrc) {
 
     var userHeight = -1.6;
 
@@ -51,7 +51,7 @@ this.createAvatarBody = function (modelSrc) {
 
     myBodyDef.children.material.properties.color = myColor;
 
-    var newNode = {
+    var defaultNode = {
         "extends": "http://vwf.example.com/aframe/aentity.vwf",
         "properties": {
             "position": [0, userHeight, 0] //-userHeight
@@ -187,7 +187,7 @@ this.createAvatarBody = function (modelSrc) {
                 "extends": "http://vwf.example.com/aframe/atext.vwf",
                 "properties": {
                     "color": myColor,
-                    "value": this.id,
+                    "value": this.displayName,
                     "side": "double",
                     "rotation": "0 180 0",
                     "position": "0.3 2.05 0.5"
@@ -198,6 +198,12 @@ this.createAvatarBody = function (modelSrc) {
         }
     }
 
+    var newNode  = Object.assign({}, defaultNode);
+
+    if (nodeDef){
+       newNode = Object.assign(defaultNode, nodeDef);
+    }
+
     if (modelSrc) {
 
         let myBodyDef = this.modelBodyDef;
@@ -283,6 +289,13 @@ this.followAvatarControl = function (position, rotation) {
     // this.avatarCamera.rotation = [rotation.x, myHeadRot.y, rotation.z];  
 }
 
+this.setUserAvatar = function(aName){
+
+    this.displayName = aName;
+    this.avatarNode.myName.value = aName;
+
+}
+
 this.createSimpleAvatar = function(){
        if (this.avatarNode.myBody) {
         this.avatarNode.children.delete(this.avatarNode.myBody);
@@ -291,7 +304,7 @@ this.createSimpleAvatar = function(){
             myColor = this.avatarNode.myHead.visual.material.color;
         }
         let myBodyDef = this.simpleBodyDef;
-        myBodyDef.material.color = myColor;
+        myBodyDef.children.material.properties.color = myColor;
 
         this.avatarNode.children.create("myBody", myBodyDef);
         this.avatarNode.myHead.visual.properties.visible = true;

+ 5 - 1
support/proxy/vwf.example.com/aframe/avatar.vwf.yaml → public/defaults/proxy/vwf.example.com/aframe/avatar.vwf.yaml

@@ -24,6 +24,7 @@ methods:
             - modelSrc
     createAvatarBody:
         parameters:
+            - nodeDef
             - modelSrc
     getRandomColor:
     followAvatarControl:
@@ -45,9 +46,12 @@ methods:
         parameters:
             - val
     setSoundWebRTC:
-         parameters:
+        parameters:
             - val
     removeSoundWebRTC:
     removeVideoTexture:
+    setUserAvatar:
+        parameters:
+            - val
 scripts:
 - source: "http://vwf.example.com/aframe/avatar.js"

+ 0 - 0
support/proxy/vwf.example.com/aframe/componentNode.vwf.yaml → public/defaults/proxy/vwf.example.com/aframe/componentNode.vwf.yaml


Some files were not shown because too many files changed in this diff