├── .gitignore
├── src
├── config.js
├── thin-client.js
├── viral-addition.js
├── utils
│ └── js-reader.js
├── client-scripts-templates
│ ├── viral-addition-template.js
│ ├── thin-client-template.js
│ └── transport-creation-template.js
└── viral-container.js
├── figures
├── flyer.psd
├── logo.png
├── logo.psd
├── p2p.png
├── p2p_m.png
├── page.psd
├── normal.png
├── logo-small.png
├── normal_m.png
├── p2p_world.png
├── normal_world.png
├── p2p_world_m.png
├── normal_world_m.png
├── page-centered.psd
└── viraljs-scheme.png
├── demo
├── css
│ └── style.css
├── renderer.js
├── package.json
├── index.html
├── js
│ ├── graph.js
│ └── main.js
└── server.js
├── package.json
└── README.md
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | .idea
--------------------------------------------------------------------------------
/src/config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | port: 3030,
3 | trickle: true
4 | };
--------------------------------------------------------------------------------
/figures/flyer.psd:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PixelsCommander/ViralJS/HEAD/figures/flyer.psd
--------------------------------------------------------------------------------
/figures/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PixelsCommander/ViralJS/HEAD/figures/logo.png
--------------------------------------------------------------------------------
/figures/logo.psd:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PixelsCommander/ViralJS/HEAD/figures/logo.psd
--------------------------------------------------------------------------------
/figures/p2p.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PixelsCommander/ViralJS/HEAD/figures/p2p.png
--------------------------------------------------------------------------------
/figures/p2p_m.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PixelsCommander/ViralJS/HEAD/figures/p2p_m.png
--------------------------------------------------------------------------------
/figures/page.psd:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PixelsCommander/ViralJS/HEAD/figures/page.psd
--------------------------------------------------------------------------------
/figures/normal.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PixelsCommander/ViralJS/HEAD/figures/normal.png
--------------------------------------------------------------------------------
/figures/logo-small.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PixelsCommander/ViralJS/HEAD/figures/logo-small.png
--------------------------------------------------------------------------------
/figures/normal_m.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PixelsCommander/ViralJS/HEAD/figures/normal_m.png
--------------------------------------------------------------------------------
/figures/p2p_world.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PixelsCommander/ViralJS/HEAD/figures/p2p_world.png
--------------------------------------------------------------------------------
/figures/normal_world.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PixelsCommander/ViralJS/HEAD/figures/normal_world.png
--------------------------------------------------------------------------------
/figures/p2p_world_m.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PixelsCommander/ViralJS/HEAD/figures/p2p_world_m.png
--------------------------------------------------------------------------------
/figures/normal_world_m.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PixelsCommander/ViralJS/HEAD/figures/normal_world_m.png
--------------------------------------------------------------------------------
/figures/page-centered.psd:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PixelsCommander/ViralJS/HEAD/figures/page-centered.psd
--------------------------------------------------------------------------------
/figures/viraljs-scheme.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PixelsCommander/ViralJS/HEAD/figures/viraljs-scheme.png
--------------------------------------------------------------------------------
/demo/css/style.css:
--------------------------------------------------------------------------------
1 | html, body {
2 | width: 100%;
3 | height: 100%;
4 | padding: 0;
5 | margin: 0;
6 | font-family:
7 | }
8 |
9 | .container {
10 | margin: auto;
11 | width: 600px;
12 | text-align: center;
13 | }
--------------------------------------------------------------------------------
/demo/renderer.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | var fs = require('fs');
4 |
5 | class App {
6 | constructor(){
7 | this.layout = fs.readFileSync('./index.html');
8 | }
9 |
10 | render(){
11 | return this.layout;
12 | }
13 | }
14 |
15 | module.exports = App;
--------------------------------------------------------------------------------
/src/thin-client.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | var thinClientTemplate = require('./client-scripts-templates/thin-client-template.js');
4 |
5 | class ViralThinClient {
6 | constructor(peerSocket) {
7 | this.peerSocketId = peerSocket.id;
8 | }
9 |
10 | getContent() {
11 | return thinClientTemplate(this.peerSocketId);
12 | }
13 | }
14 |
15 | module.exports = ViralThinClient;
--------------------------------------------------------------------------------
/src/viral-addition.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | var viralAdditionTemplate = require('./client-scripts-templates/viral-addition-template.js');
4 |
5 |
6 | class ViralClientAddition {
7 | constructor(peerId) {
8 | this.peerId = peerId;
9 | }
10 |
11 | getContent() {
12 | return viralAdditionTemplate();
13 | }
14 | }
15 |
16 | module.exports = ViralClientAddition;
--------------------------------------------------------------------------------
/demo/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "viral-js-demo",
3 | "version": "1.0.0",
4 | "description": "Demonstartion on how ViralJS distributes application over p2p",
5 | "main": "index.js",
6 | "scripts": {
7 | "start": "nodemon ./server.js"
8 | },
9 | "keywords": [
10 | "p2p",
11 | "app",
12 | "container"
13 | ],
14 | "author": "Denis Radin aka PixelsCommander",
15 | "license": "ISC",
16 | "devDependencies": {
17 | "express": "^4.13.3"
18 | },
19 | "dependencies": {
20 | "vis": "^4.9.0"
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/src/utils/js-reader.js:
--------------------------------------------------------------------------------
1 | var fs = require('fs');
2 | var cachedFiles = {};
3 |
4 | module.exports = {
5 | cache: function (fileName) {
6 | try {
7 | var location = require.resolve(fileName);
8 | cachedFiles[fileName] = fs.readFileSync(location, 'utf8');
9 | } catch (e) {
10 | console.error('Error reading ' + fileName);
11 | }
12 | },
13 | get: function (fileName) {
14 | if (!cachedFiles[fileName]) {
15 | this.cache(fileName);
16 | }
17 | //console.log(cachedFiles[fileName]);
18 | return cachedFiles[fileName];
19 | }
20 | }
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "viraljs",
3 | "version": "0.1.2",
4 | "description": "P2P distributed application made easy",
5 | "main": "./src/viral-container.js",
6 | "scripts": {
7 | "demo": "cd ./demo/ && nodemon --watch ./ --watch ../src/ ./server.js"
8 | },
9 | "keywords": [
10 | "p2p",
11 | "distribution",
12 | "applications",
13 | "viral",
14 | "javascript"
15 | ],
16 | "author": "Denis Radin aka PixelsCommander",
17 | "license": "ISC",
18 | "dependencies": {
19 | "express-useragent": "^0.2.0",
20 | "inline-source": "4.1.0",
21 | "mongoose": "^4.2.9",
22 | "socket.io": "^1.3.7",
23 | "socket.io-client": "^1.3.7",
24 | "socket.io-p2p": "^2.2.0",
25 | "socket.io-p2p-server": "^1.2.0"
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/demo/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | ViralJS demo
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
ViralJS demo
14 |
The first peer gets application from Express server and then deliver it to second and so on peers
15 |
Please, login via Facebook and keep this tab alive for some time to seed app to some more users from your browser
16 |
17 |
18 |
19 |
20 |
--------------------------------------------------------------------------------
/demo/js/graph.js:
--------------------------------------------------------------------------------
1 | function drawGraph(connections) {
2 | var nodes = [];
3 | var edges = [];
4 | var network = null;
5 |
6 | var nodes = [],
7 | nodesHash = {};
8 |
9 | function addNode(nodeObject) {
10 | nodesHash[nodeObject.id] = {
11 | id: nodeObject.id,
12 | shape: 'circularImage',
13 | image: nodeObject.avatar,
14 | label: nodeObject.name
15 | };
16 | }
17 |
18 | function getNodeIndex(id) {
19 | var node = nodesHash[id];
20 | var index = nodes.indexOf(node);
21 | return index;
22 | }
23 |
24 | Object.keys(connections).forEach(function (connectionId) {
25 | var connection = connections[connectionId];
26 | addNode(connection.a);
27 | addNode(connection.b);
28 | });
29 |
30 | nodes = Object.keys(nodesHash).map(function (nodeId) {
31 | return nodesHash[nodeId];
32 | });
33 |
34 | edges = Object.keys(connections).map(function (connectionId) {
35 | var connection = connections[connectionId];
36 |
37 | return {
38 | from: connection.a.id,
39 | to: connection.b.id
40 | };
41 | });
42 |
43 | // create a network
44 | var container = document.body;
45 | var data = {
46 | nodes: nodes,
47 | edges: edges
48 | };
49 | var options = {
50 | nodes: {
51 | borderWidth: 4,
52 | size: 30,
53 | color: {
54 | border: '#222222',
55 | background: '#FFFFFF'
56 | },
57 | font: {color: '#000000'}
58 | },
59 | edges: {
60 | color: 'lightgray'
61 | }
62 | };
63 | network = new vis.Network(container, data, options);
64 | }
--------------------------------------------------------------------------------
/src/client-scripts-templates/viral-addition-template.js:
--------------------------------------------------------------------------------
1 | var transportCreationTemplate = require('./transport-creation-template.js');
2 |
3 | module.exports = function (p2pScriptContent, ioScriptContent) {
4 | return `${transportCreationTemplate()}
5 |
6 | var headData = document.head.innerHTML;
7 | var bodyData = document.body.innerHTML;
8 | var metaData = '';
9 |
10 | window.ViralContainer.p2p = window.ViralContainer.p2p || new P2P(window.ViralContainer.socket, opts);
11 |
12 | window.ViralContainer.p2p.useSockets = false;
13 | window.ViralContainer.p2p.upgrade();
14 |
15 | function sendData(data, name) {
16 | var chunkLength = 16000;
17 | var chunksNumber = Math.ceil(data.length / chunkLength);
18 |
19 | for (var i = 0; i < chunksNumber; i++) {
20 | var chunkStart = i * chunkLength;
21 | var chunkEnd = Math.min((i + 1) * chunkLength, data.length);
22 | var chunk = data.substring(chunkStart, chunkEnd);
23 | window.ViralContainer.p2p.emit(name, chunk);
24 | }
25 |
26 | window.ViralContainer.p2p.emit(name, 'end');
27 | }
28 |
29 | window.ViralContainer.p2p.on('getApp', function(data){
30 | if (data === window.ViralContainer.socket.id) {
31 | console.log('Sending app');
32 | sendData(metaData + headData, 'head');
33 | sendData(bodyData, 'body');
34 | }
35 | });
36 |
37 | window.onbeforeunload = function(){
38 | window.ViralContainer.socket.disconnect();
39 | };
40 |
41 | window.ViralContainer.writeMeta = function (metaObject) {
42 | var metaString = JSON.stringify(metaObject);
43 | metaData = '`;
50 | };
--------------------------------------------------------------------------------
/demo/server.js:
--------------------------------------------------------------------------------
1 | var express = require('express'),
2 | Renderer = require('./renderer.js'),
3 | renderer = new Renderer(),
4 | ViralContainer = require('../src/viral-container.js'),
5 | mongoose = require('mongoose'),
6 | Schema = mongoose.Schema;
7 |
8 | mongoose.connect('mongodb://localhost/viraljs-demo');
9 |
10 | var connectionSchema = mongoose.Schema({
11 | data: String
12 | });
13 |
14 | var Connection = mongoose.model('Connection', connectionSchema);
15 |
16 | var express = require('express');
17 | var app = express();
18 | var viralContainer = new ViralContainer();
19 |
20 | var connections = {};
21 |
22 | function isAlreadyThere(connectionData) {
23 |
24 | for (var i = 0; i < Object.keys(connections).length; i++) {
25 | var connectionKey = Object.keys(connections)[i],
26 | connectionToCheck = connections[connectionKey];
27 |
28 | if (connectionToCheck.a.name === connectionData.a.name && connectionToCheck.b.name === connectionData.b.name) {
29 | return true;
30 | }
31 |
32 | //console.log(connectionToCheck.a.name + ' !== ' + connectionData.a.name);
33 | //console.log(connectionToCheck.b.name + ' !== ' + connectionData.b.name);
34 | }
35 |
36 | return false;
37 | }
38 |
39 | function addConnectionToDB(connectionData) {
40 | var data = JSON.stringify(connectionData);
41 | var connection = new Connection({data: data});
42 | connection.save();
43 | }
44 |
45 | Connection.find().exec(function (error, response) {
46 | response.forEach(function (connection) {
47 | connections[Math.random()] = JSON.parse(connection.data);
48 | });
49 |
50 | viralContainer.socket.on('connection', function (socket) {
51 | socket.on('addConnection', function (msg) {
52 | if (!isAlreadyThere(msg)) {
53 | console.log('Adding connection ' + msg.a.name + ' to ' + msg.b.name);
54 | addConnectionToDB(msg);
55 | connections[socket.id] = msg;
56 | }
57 |
58 | console.log('Sending connections to ' + socket.id);
59 | viralContainer.socket.emit('connectionsGraph', connections);
60 | });
61 | });
62 | });
63 |
64 | app.use(viralContainer.middleware);
65 |
66 | app.get('/', function (req, res) {
67 | res.header('Content-Type', 'text/html');
68 | res.send(renderer.render() + '');
69 | });
70 |
71 | app.use(express.static('./'));
72 |
73 | var server = app.listen(3000, function () {
74 | var host = server.address().address;
75 | var port = server.address().port;
76 |
77 | console.log('Example app listening at http://%s:%s', host, port);
78 | });
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | 
2 |
3 | ExpressJS middleware for P2P Web apps distribution
4 | ==================================================
5 | To reduce server load, latency and establish self-maintainable CDN based on your users browsers.
6 |
7 | [](https://gitter.im/PixelsCommander/ViralJS?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
8 |
9 | - [Website](http://pixelscommander.github.io/Viral.JS/)
10 | - [Demo](http://pixelscommander.com:3000)
11 | - [Article about](http://pixelscommander.com/en/interactive-revolution/what-is-beyond-isomorphic/)
12 |
13 | Installing from NPM
14 | -------------------
15 | `npm -i viraljs`
16 |
17 | How to use?
18 | -----------
19 |
20 | ```js
21 | var ViralContainer = require('viraljs');
22 | var viralContainer = new ViralContainer();
23 | myExpressApp.use(viralContainer.middleware);
24 | ```
25 |
26 | Isomorphism blurred boundaries between server and client. The only difference between them currently is server\`s ability to distribute application to clients. What if we go further enabling client to do this? What if we erase boundaries between server and client completely? In this case every client which got application\`s code becomes it`s distributor or carrier. And drawing the analogy with spreading microorganisms in the nature this technique perfectly matches “**viral JavaScript**“ naming.
27 |
28 | Motivation
29 | ----------
30 | P2P content distribution allows to reduce server load and decrease network latency since peering could be setup in the way content to be delivered from the nearest peer available. For example after hitting corporative network application will be delivered inside of it using high speed internal channels without creating a load on company`s internet channel.
31 |
32 | 
33 |
34 | Traditional app distribution. Server sends package many times, corporative internet channels are loaded appropriately
35 |
36 | 
37 |
38 | In case of P2P distribution application hits corporative network once and then is distributed using high speed internal network. This reduces server load and corporative internet channel load
39 |
40 | Or another case – once application got from USA to Europe it is delivered inside of European networks only without creating transatlantic traffic.
41 |
42 | 
43 |
44 | It takes a lot of transatlantic trips to transmit an app when doing it in a traditional way
45 |
46 | 
47 |
48 | P2P allows to reduce number of transcontinental transfers and reduce server load
49 |
50 | By distributing application via P2P you create a self-establishing and self-evolving CDN which moves data closer to client.
51 |
52 | Bug tracker
53 | -----------
54 |
55 | Have a bug? Please create an issue here on GitHub!
56 |
57 | https://github.com/PixelsCommander/ViralJS/issues
58 |
59 | License
60 | -------
61 | MIT: http://mit-license.org/
62 |
63 | Copyright 2015 Denis Radin aka [PixelsCommander](http://pixelscommander.com)
64 |
--------------------------------------------------------------------------------
/demo/js/main.js:
--------------------------------------------------------------------------------
1 | window.onbeforeunload = function(e) {
2 | return ('After closing the tab you will stop seeding application to the people near you.');
3 | }
4 |
5 | var sentByData = {};
6 | var appId = location.href.indexOf('localhost') !== -1 ? '1214376798578200' : '1194624743886739';
7 |
8 | if (typeof ViralContainer !== 'undefined') {
9 | console.log('And our meta is...');
10 | console.log(ViralContainer.meta || 'No meta information');
11 |
12 | sentByData = ViralContainer.meta;
13 | } else {
14 | sentByData = {
15 | name: 'Express server with ViralJS middleware',
16 | avatar: 'http://publicdomainvectors.org/photos/1313181674.png',
17 | id: 0
18 | };
19 | }
20 |
21 | function statusChangeCallback(response) {
22 | // The response object is returned with a status field that lets the
23 | // app know the current login status of the person.
24 | // Full docs on the response object can be found in the documentation
25 | // for FB.getLoginStatus().
26 | if (response.status === 'connected') {
27 | // Logged into your app and Facebook.
28 | testAPI();
29 | } else if (response.status === 'not_authorized') {
30 | // The person is logged into Facebook, but not your app.
31 | document.getElementById('status').innerHTML = 'Please log ' +
32 | 'into this app.';
33 | } else {
34 | // The person is not logged into Facebook, so we're not sure if
35 | // they are logged into this app or not.
36 | document.getElementById('status').innerHTML = 'Please log ' +
37 | 'into Facebook.';
38 | }
39 | }
40 |
41 | function checkLoginState() {
42 | console.log('checkLoginState');
43 | FB.getLoginStatus(function (response) {
44 | statusChangeCallback(response);
45 | });
46 | }
47 |
48 | window.fbAsyncInit = function () {
49 | console.log('fbAsyncInit ' + appId);
50 |
51 | FB.init({
52 | appId: appId,
53 | xfbml: true,
54 | version: 'v2.2',
55 | cookie: true
56 | });
57 |
58 | //debugger;
59 |
60 | var timeoutId = setTimeout(testAPI, 3000);
61 |
62 | FB.getLoginStatus(function (response) {
63 | clearTimeout(timeoutId);
64 | statusChangeCallback(response);
65 | });
66 |
67 | };
68 |
69 | (function (d, s, id) {
70 | var js, fjs = d.getElementsByTagName(s)[0];
71 | if (d.getElementById(id)) {
72 | return;
73 | }
74 | js = d.createElement(s);
75 | js.id = id;
76 | js.src = "//connect.facebook.net/en_US/all.js#xfbml=1";
77 | fjs.parentNode.insertBefore(js, fjs);
78 | }(document, 'script', 'facebook-jssdk'));
79 |
80 | function testAPI() {
81 | console.log('Welcome! Fetching your information.... ');
82 | FB.api('/me', function (response) {
83 |
84 | FB.api('/me/picture', function (imageResponse) {
85 | console.log('Successful login for: ' + response.name);
86 | console.log('Id: ' + response.id);
87 |
88 | var thisUserData = {
89 | avatar: imageResponse.data.url,
90 | name: response.name,
91 | id: response.id
92 | };
93 |
94 | ViralContainer.writeMeta(thisUserData);
95 |
96 | ViralContainer.socket.on('connectionsGraph', function (data) {
97 | console.log('Received graph data');
98 | drawGraph(data);
99 | });
100 |
101 | ViralContainer.socket.emit('addConnection', {
102 | a: sentByData,
103 | b: thisUserData
104 | });
105 |
106 | if (sentByData) {
107 | console.log('You received application from ' + sentByData.name);
108 | }
109 | });
110 | });
111 | }
112 |
--------------------------------------------------------------------------------
/src/viral-container.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | var http = require('http'),
4 | p2p = require('socket.io-p2p-server'),
5 | socketio = require('socket.io'),
6 | ViralThinClient = require('./thin-client.js'),
7 | ViralClientAddition = require('./viral-addition.js'),
8 | inline = require('inline-source'),
9 | fs = require('fs'),
10 | path = require('path'),
11 | useragent = require('express-useragent'),
12 | config = require('./config.js');
13 |
14 | var browsersAllowed = {
15 | 'chrome': true,
16 | 'firefox': true,
17 | 'safari': false,
18 | 'edge': false,
19 | 'ie': false
20 | };
21 |
22 | class ViralContainer {
23 | constructor() {
24 | this.server = http.createServer();
25 | this.p2pserver = p2p.Server;
26 | this.socket = socketio(this.server);
27 | this.socket.use(this.p2pserver);
28 | this.peers = [];
29 |
30 | this.socket.on('connection', this.onConnection.bind(this));
31 |
32 | this.server.listen(config.port, function () {
33 | console.log("Viral container socket is listening on " + config.port);
34 | });
35 |
36 | this.middleware = this.middleware.bind(this);
37 | }
38 |
39 | onConnection(socket) {
40 | /*var recommendedPeer = this.getPeerForSocket(socket);
41 | var recommendedPeerId = recommendedPeer ? recommendedPeer.id : null;
42 | console.log('Joining socket ' + socket.id + ' to room ' + recommendedPeerId);
43 |
44 | if (recommendedPeerId) {
45 | socket.join(recommendedPeerId);
46 | }*/
47 |
48 | console.log('Connected ID ' + socket.id + ', adress ' + socket.request.connection.remoteAddress);
49 | socket.on('disconnect', this.onDisconnection.bind(this, socket));
50 | socket.on('error', this.onDisconnection.bind(this, socket));
51 | socket.on('readyToSeed', this.onPeerReady.bind(this, socket));
52 | }
53 |
54 | onPeerReady(socket) {
55 | //console.log('Joining socket ' + socket.id + ' to it`s own room');
56 | //socket.join(socket.id);
57 | console.log('Ready to seed #' + this.peers.length + ', ID ' + socket.id + ', adress ' + socket.request.connection.remoteAddress);
58 | this.peers.push(socket);
59 | }
60 |
61 | onDisconnection(socket) {
62 | var peerIndex = this.peers.indexOf(socket);
63 |
64 | if (peerIndex !== -1) {
65 | console.log('Disconnected ' + peerIndex);
66 | this.peers.splice(peerIndex, 1);
67 | }
68 | }
69 |
70 | getPeerForSocket(socket) {
71 | var peerIndex = Math.floor(Math.random() * (this.peers.length));
72 |
73 | if (this.peers[peerIndex] !== socket) {
74 | return this.peers[peerIndex];
75 | }
76 | }
77 |
78 | middleware(req, res, next) {
79 | console.log('Middleware worked');
80 |
81 | //Return just clean app if browser do not support WebRTC
82 | var browser = useragent.parse(req.headers['user-agent']).browser.toLowerCase();
83 | if (!browsersAllowed[browser]) {
84 | next();
85 | return;
86 | }
87 |
88 | //Return what should return if asked for favicon
89 | if (req.path.indexOf('favicon.ico') !== -1) {
90 | next();
91 | return;
92 | }
93 |
94 | var peerAvailable = this.getPeerForSocket(req.socket);
95 |
96 | if (peerAvailable && !req.query.forceDirect) {
97 | console.log('Sending thin client');
98 | res.send(new ViralThinClient(peerAvailable).getContent());
99 | res.end();
100 | } else {
101 | console.log('Sending real app + addition');
102 | res.__send = res.send;
103 | res.send = function (data) {
104 | data += new ViralClientAddition(req).getContent();
105 | inline(data, {compress: false, attribute: '*'}, function (err, html) {
106 | res.__send(html);
107 | res.end();
108 | });
109 | };
110 |
111 | next();
112 | }
113 | }
114 | }
115 |
116 | module.exports = ViralContainer;
--------------------------------------------------------------------------------
/src/client-scripts-templates/thin-client-template.js:
--------------------------------------------------------------------------------
1 | var transportCreationTemplate = require('./transport-creation-template.js');
2 |
3 | module.exports = function (recommendedPeerId) {
4 | return `
5 |
6 |
7 | ${transportCreationTemplate()}
8 |
9 | var recommendedPeerId = '${recommendedPeerId}';
10 |
11 | console.log('Requesting app from ' + recommendedPeerId);
12 |
13 | var timeout = setTimeout(function(){
14 | location.href = location.href + '?forceDirect=true';
15 | }, 5000);
16 |
17 | var receivedBodyChunks = [];
18 | var receivedHeadChunks = [];
19 |
20 | var bodyReceived = false;
21 | var headReceived = false;
22 |
23 | var allReceived = function() {
24 | clearTimeout(timeout);
25 |
26 | if (bodyReceived && headReceived) {
27 |
28 | executeScripts(document.head);
29 | executeScripts(document.body);
30 |
31 | var evt = document.createEvent('Event');
32 | evt.initEvent('load', false, false);
33 | window.dispatchEvent(evt);
34 |
35 | /* Object.keys(window.ViralContainer.p2p._peers).forEach(function(peerId){
36 | var peer = window.ViralContainer.p2p._peers[peerId];
37 | peer.destroy();
38 | }); */
39 | }
40 | }
41 |
42 | var p2p = window.ViralContainer.p2p = new P2P(window.ViralContainer.socket, opts, function (data) {
43 | p2p.emit('getApp', recommendedPeerId);
44 |
45 | p2p.on('body', function(data){
46 | if (data !== 'end') {
47 | receivedBodyChunks.push(data);
48 | } else {
49 | p2p.off('body');
50 | console.log('Received body');
51 | bodyReceived = true;
52 | document.body.innerHTML = receivedBodyChunks.join('');
53 | allReceived();
54 | }
55 | });
56 |
57 | p2p.on('head', function(data){
58 | if (data !== 'end') {
59 | receivedHeadChunks.push(data);
60 | } else {
61 | p2p.off('head');
62 | console.log('Received head');
63 | headReceived = true;
64 | document.head.innerHTML = receivedHeadChunks.join('');
65 | allReceived();
66 | }
67 | });
68 | });
69 |
70 | window.addEventListener('beforeunload', function(){
71 | window.ViralContainer.socket.disconnect();
72 | });
73 |
74 | window.ViralContainer.p2p.useSockets = false;
75 | window.ViralContainer.p2p.upgrade();
76 |
77 |
130 | `;
131 | }
--------------------------------------------------------------------------------
/src/client-scripts-templates/transport-creation-template.js:
--------------------------------------------------------------------------------
1 | var config = require('../config.js');
2 | var jsReader = require('../utils/js-reader.js');
3 | var p2pScriptContent = jsReader.get('../../node_modules/socket.io-p2p/socketiop2p.min.js');
4 | var ioScriptContent = jsReader.get('../../node_modules/socket.io-client/socket.io.js');
5 |
6 | module.exports = function(){
7 | return `
10 |
13 | */
--------------------------------------------------------------------------------