\ No newline at end of file
diff --git a/dist/public/index.html b/dist/public/index.html
new file mode 100644
index 0000000..3fc7401
--- /dev/null
+++ b/dist/public/index.html
@@ -0,0 +1,18 @@
+
+Snapdrop
+
diff --git a/dist/public/manifest.json b/dist/public/manifest.json
new file mode 100644
index 0000000..05ab83b
--- /dev/null
+++ b/dist/public/manifest.json
@@ -0,0 +1,29 @@
+{
+ "name": "Snapdrop",
+ "short_name": "Snapdrop",
+ "icons": [{
+ "src": "images/touch/icon-128x128.png",
+ "sizes": "128x128",
+ "type": "image/png"
+ }, {
+ "src": "images/touch/apple-touch-icon.png",
+ "sizes": "152x152",
+ "type": "image/png"
+ }, {
+ "src": "images/touch/ms-touch-icon-144x144-precomposed.png",
+ "sizes": "144x144",
+ "type": "image/png"
+ }, {
+ "src": "images/touch/chrome-touch-icon-192x192.png",
+ "sizes": "192x192",
+ "type": "image/png"
+ }, {
+ "src": "images/touch/chrome-splashscreen-icon-384x384.png",
+ "sizes": "384x384",
+ "type": "image/png"
+ }],
+ "background_color": "#3367d6",
+ "start_url": "index.html",
+ "display": "standalone",
+ "theme_color": "#3367d6"
+}
diff --git a/dist/public/scripts/app.js b/dist/public/scripts/app.js
new file mode 100644
index 0000000..27ba397
--- /dev/null
+++ b/dist/public/scripts/app.js
@@ -0,0 +1 @@
+!function(e){"use strict";var o=e.querySelector("#app");o.baseUrl="/",""===window.location.port,o.displayInstalledToast=function(){Polymer.dom(e).querySelector("platinum-sw-cache").disabled||Polymer.dom(e).querySelector("#caching-complete").show()},o.displayToast=function(o){var t=Polymer.dom(e).querySelector("#toast");t.text=o,t.show()},o.addEventListener("dom-change",function(){console.log("Our app is ready to rock!"),o.conn=e.querySelector("connection-wrapper")}),window.addEventListener("WebComponentsReady",function(){}),o._showAbout=function(){e.querySelector("#pages").select(1)},o._showAbout=function(){e.querySelector("#pages").select(0)}}(document);
\ No newline at end of file
diff --git a/dist/public/sounds/blop.mp3 b/dist/public/sounds/blop.mp3
new file mode 100755
index 0000000..28a6244
Binary files /dev/null and b/dist/public/sounds/blop.mp3 differ
diff --git a/dist/public/sounds/blop.ogg b/dist/public/sounds/blop.ogg
new file mode 100644
index 0000000..d1ce0c2
Binary files /dev/null and b/dist/public/sounds/blop.ogg differ
diff --git a/dist/public/styles/main.css b/dist/public/styles/main.css
new file mode 100644
index 0000000..6e71009
--- /dev/null
+++ b/dist/public/styles/main.css
@@ -0,0 +1 @@
+body,html{height:100%;width:100%;padding:0;margin:0}body{background:#fafafa;font-family:Roboto,'Helvetica Neue',Helvetica,Arial,sans-serif;color:#333;-webkit-font-smoothing:antialiased;overflow-x:hidden}#ads,#ads2{display:none}@media screen and (min-width:520px){#ads{display:block;position:absolute;top:8px;left:50%;margin-left:-150px}}@media screen and (min-width:720px){#ads{display:none}#ads2{display:block;position:absolute;bottom:4px;left:4px}}
\ No newline at end of file
diff --git a/dist/readme.md b/dist/readme.md
new file mode 100644
index 0000000..6c01c5f
--- /dev/null
+++ b/dist/readme.md
@@ -0,0 +1,5 @@
+# Run a Snapdrop Server
+- `npm install`
+- `node index.js`
+- TODO: SSL connection (i.e nginx)
+ - ( Please do a PR if you've build an alternative index.js with a self-signed cert )
\ No newline at end of file
diff --git a/dist/server/ws-server.js b/dist/server/ws-server.js
new file mode 100644
index 0000000..c067053
--- /dev/null
+++ b/dist/server/ws-server.js
@@ -0,0 +1,154 @@
+'use strict';
+var parser = require('ua-parser-js');
+
+// Start Binary.js server
+var BinaryServer = require('binaryjs').BinaryServer;
+
+exports.create = function(server) {
+
+ // link it to express
+ var bs = BinaryServer({
+ server: server,
+ path: '/binary'
+ });
+
+ function guid() {
+ function s4() {
+ return Math.floor((1 + Math.random()) * 0x10000)
+ .toString(16)
+ .substring(1);
+ }
+ return s4() + s4() + '-' + s4() + '-' + s4() + '-' +
+ s4() + '-' + s4() + s4() + s4();
+ }
+
+ function getDeviceName(req) {
+ var ua = parser(req.headers['user-agent']);
+ return {
+ model: ua.device.model,
+ os: ua.os.name,
+ browser: ua.browser.name,
+ type: ua.device.type
+ };
+ }
+
+ function hash(text) {
+ // A string hashing function based on Daniel J. Bernstein's popular 'times 33' hash algorithm.
+ var h = 5381,
+ index = text.length;
+ while (index) {
+ h = (h * 33) ^ text.charCodeAt(--index);
+ }
+ return h >>> 0;
+ }
+
+ function getIP(socket) {
+ return socket.upgradeReq.headers['x-forwarded-for'] || socket.upgradeReq.connection.remoteAddress;
+ }
+ // Wait for new user connections
+ bs.on('connection', function(client) {
+
+ client.uuidRaw = guid();
+ //ip is hashed to prevent injections by spoofing the 'x-forwarded-for' header
+ // client.hashedIp = 1; //use this to test locally
+ client.hashedIp = hash(getIP(client._socket));
+
+ client.deviceName = getDeviceName(client._socket.upgradeReq);
+
+ // Incoming stream from browsers
+ client.on('stream', function(stream, meta) {
+ if (meta && meta.serverMsg === 'rtc-support') {
+ client.uuid = (meta.rtc ? 'rtc_' : '') + client.uuidRaw;
+ client.send({
+ isSystemEvent: true,
+ type: 'handshake',
+ name: client.deviceName,
+ uuid: client.uuid
+ });
+ return;
+ }
+ if (meta && meta.serverMsg === 'device-name') {
+ //max name length = 40
+ if (meta.name && meta.name.length > 40) {
+ return;
+ }
+ client.name = meta.name;
+ return;
+ }
+
+ meta.from = client.uuid;
+
+ // broadcast to the other client
+ for (var id in bs.clients) {
+ if (bs.clients.hasOwnProperty(id)) {
+ var otherClient = bs.clients[id];
+ if (otherClient !== client && meta.toPeer === otherClient.uuid) {
+ var send = otherClient.createStream(meta);
+ stream.pipe(send, meta);
+ }
+ }
+ }
+ });
+ });
+
+ function forEachClient(fn) {
+ for (var id in bs.clients) {
+ if (bs.clients.hasOwnProperty(id)) {
+ var client = bs.clients[id];
+ fn(client);
+ }
+ }
+ }
+
+
+
+
+
+ function notifyBuddies() {
+ var locations = {};
+ //group all clients by location (by public ip address)
+ forEachClient(function(client) {
+ var ip = client.hashedIp;
+ locations[ip] = locations[ip] || [];
+ locations[ip].push({
+ socket: client,
+ contact: {
+ peerId: client.uuid,
+ name: client.name || client.deviceName,
+ device: client.name ? client.deviceName : undefined
+ }
+ });
+ });
+ //notify every location
+ Object.keys(locations).forEach(function(locationKey) {
+ //notify every client of all other clients in this location
+ var location = locations[locationKey];
+ location.forEach(function(client) {
+ //all other clients
+ var buddies = location.reduce(function(result, otherClient) {
+ if (otherClient !== client) {
+ result.push(otherClient.contact);
+ }
+ return result;
+ }, []);
+ var currState = hash(JSON.stringify(buddies));
+ console.log(currState);
+ var socket = client.socket;
+ //protocol
+ var msg = {
+ buddies: buddies,
+ isSystemEvent: true,
+ type: 'buddies'
+ };
+ //send only if state changed
+ if (currState !== socket.lastState) {
+ socket.send(msg);
+ socket.lastState = currState;
+ return;
+ }
+ });
+ });
+ }
+
+ setInterval(notifyBuddies, 3000);
+};