Lots of small improvements, websockets fallback

This commit is contained in:
Robin Linus 2015-12-23 13:57:13 +01:00
parent e5eab64c6b
commit 22be7c5cb9
35 changed files with 2672 additions and 916 deletions

View file

@ -1,97 +1,100 @@
<link rel="import" href="../../../bower_components/iron-ajax/iron-ajax.html">
<link rel="import" href="../../../bower_components/paper-styles/paper-styles.html">
<link rel="import" href="../../bower_components/iron-ajax/iron-ajax.html">
<link rel="import" href="../../bower_components/paper-styles/paper-styles.html">
<link rel="import" href="../file-sharing/file-input.html">
<link rel="import" href="user-avatar.html">
<link rel="import" href="personal-avatar.html">
<dom-module id="buddy-finder">
<template>
<style>
:host {
display: block;
background-color: white;
background-color: transparent;
@apply(--layout-fit);
@apply(--layout-horizontal);
@apply(--layout-center-center);
overflow: hidden;
}
.paper-font-display1 {
color: black;
text-align: center;
margin-bottom: 16px;
display: none;
position: relative;
height: 100%;
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
margin: 0;
}
.buddies {
z-index: 1;
@apply(--layout-horizontal);
@apply(--layout-center-center);
@apply(--layout-wrap);
}
.buddy {
cursor: pointer;
}
.circles {
position: absolute;
bottom: -50px;
left: 50%;
width: 1140px;
margin-left: -570px;
height: 700px;
transform-origin: 570px 570px;
animation: grow 1.5s ease-out;
fill: transparent;
}
.me {
position: absolute;
bottom: 30px;
bottom: 24px;
left: 50%;
margin-left: -60px;
margin-left: -180px;
}
.explanation {
@apply(--paper-font-headline);
color: #4285f4;
text-align: center;
}
</style>
<div class="paper-font-display1">People near by</div>
<div class="buddies">
<template is="dom-repeat" items="{{buddies}}">
<file-input on-file-selected="_fileDropped">
<user-avatar contact="{{item.peerId}}" class="buddy"></user-avatar>
<file-input on-file-selected="_fileSelected">
<user-avatar contact="{{item}}" class="buddy"></user-avatar>
</file-input>
</template>
</div>
<user-avatar contact="{{me}}" class="me"></user-avatar>
<svg class="circles" viewBox="-0.5 -0.5 1140 700">
<circle class="circle" cx="570" cy="570" r="120" stroke="rgba(160,160,160,.15)"></circle>
<circle class="circle" cx="570" cy="570" r="210" stroke="rgba(160,160,160,.2)"></circle>
<circle class="circle" cx="570" cy="570" r="300" stroke="rgba(160,160,160,.3)"></circle>
<circle class="circle" cx="570" cy="570" r="390" stroke="rgba(160,160,160,.35)"></circle>
<circle class="circle" cx="570" cy="570" r="480" stroke="rgba(160,160,160,.4)"></circle>
<circle class="circle" cx="570" cy="570" r="570" stroke="rgba(160,160,160,.43)"></circle>
</svg>
<iron-ajax id="ajax" auto url="https://yawim.com/findbuddies/{{me}}" handle-as="json" last-response="{{buddies}}"></iron-ajax>
<div hidden$="{{buddies.length}}" class="explanation">
Open this page on another device
<wbr>to share files.
</div>
<personal-avatar class="me"></personal-avatar>
<!-- <iron-ajax id="ajax" auto url="https://yawim.com/findbuddies/{{me}}" handle-as="json" last-response="{{buddies}}"></iron-ajax> -->
</template>
<script>
'use strict';
Polymer({
is: 'buddy-finder',
properties: {
buddies: Array,
buddies: {
type: Array,
value: []
},
me: {
type: String,
}
},
attached: function() {
//Ask server every second for changes
setInterval(function() {
this.$.ajax.generateRequest();
}.bind(this), 1000);
var ajax = this.$.ajax;
function request() {
//ajax.generateRequest();
}
var intervalId = setInterval(request, 1000);
document.addEventListener('visibilitychange', function() {
if (document.hidden) {
clearInterval(intervalId);
intervalId = 0;
} else {
if (!intervalId) {
intervalId = setInterval(request, 1000);
}
}
});
},
_fileDropped: function(e) {
_fileSelected: function(e) {
var peerId = e.model.item.peerId;
var file = e.detail;
app.p2p.connectToPeer(peerId, function() {
app.p2p.sendFile(peerId, file);
});
console.log('Send:', file);
console.log('To:', peerId);
}
});
</script>

View file

@ -0,0 +1,41 @@
<link rel="import" href="../../bower_components/iron-icon/iron-icon.html">
<link rel="import" href="../../styles/icons.html">
<dom-module id="personal-avatar">
<template>
<style>
:host {
@apply(--layout-vertical);
@apply(--layout-center);
width: 360px;
}
iron-icon {
width: 80px;
height: 80px;
color: #4285f4;
}
.paper-font-body1 {
font-size: 13px;
margin-top: 6px;
}
.discover {
color: #4285f4;
}
</style>
<iron-icon icon="chat:wifi-tethering"></iron-icon>
<div class="paper-font-body1">
SnapDrop lets you share instantly with people near by.
</div>
<div class="paper-font-body1 discover">
Allow me to be discovered by: Everyone in this network.
</div>
</template>
<script>
'use strict';
Polymer({
is: 'personal-avatar'
});
</script>
</dom-module>

View file

@ -1,4 +1,4 @@
<link rel="import" href="../contact-item/anonymous-contact-behavior.html">
<link rel="import" href="../../bower_components/paper-icon-button/paper-icon-button.html">
<dom-module id="user-avatar">
<template>
<style>
@ -10,34 +10,120 @@
height: 120px;
}
.avatar {
paper-icon-button {
display: inline-block;
width: 52px;
height: 52px;
width: 64px !important;
height: 64px !important;
border-radius: 50%;
overflow: hidden;
background: #ccc;
@apply(--shadow-elevation-2dp);
padding: 12px;
margin-bottom: 4px;
background-color: #4285f4;
color: white;
}
.paper-font-subhead{
:host:hover paper-icon-button {
transform: scale(1.05);
}
.paper-font-subhead {
text-align: center;
}
.paper-font-body1 {
text-align: center;
width: 100%;
font-size: 13px;
color: grey;
margin-top: 2px;
}
:host,
.paper-font-subhead,
.paper-font-body1 {
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
margin: 4px;
}
</style>
<div class="avatar" id="avatar" item-icon></div>
<paper-icon-button icon="{{_displayIcon}}"></paper-icon-button>
<div class="paper-font-subhead">{{_displayName}}</div>
<div class="paper-font-body1">{{status}}</div>
</template>
<script>
'use strict';
Polymer({
is: 'user-avatar',
behaviors:[Chat.AnonymousContactBehavior],
observers:['_computeBackgroundImg(contact.*)'],
_computeBackgroundImg:function(){
console.log('avatar changed');
var avatar = this.anonymousAccount(this.contact).avatar;
var style = this.$.avatar.style;
style.backgroundImage='url('+avatar.url+')';
style.backgroundPosition=avatar.left+'px '+avatar.top+'px';
properties: {
contact: Object,
_displayName: {
computed: '_computeDisplayName(contact)'
},
_displayIcon: {
computed: '_computeDisplayIcon(contact)'
},
status: {
type: String,
value: ''
}
},
_computeDisplayName: function(contact) {
contact = contact.name;
if (contact.model) {
return contact.os + ' ' + contact.model;
}
contact.os = contact.os.replace('Mac OS', 'Mac');
return contact.os + ' ' + contact.browser;
},
_computeDisplayIcon: function(contact) {
contact = contact.name;
if (contact.type === 'mobile') {
return 'chat:phone-iphone';
}
if (contact.type === 'tablet') {
return 'chat:tablet-mac';
}
return 'chat:desktop-mac';
},
attached: function() {
this.async(function() {
app.p2p.addEventListener('file-offered', function(e) {
if (e.detail.to === this.contact.peerId) {
this.status = 'Waiting to accept...';
}
}.bind(this), false);
app.p2p.addEventListener('upload-started', function(e) {
if (e.detail.to === this.contact.peerId) {
this.status = 'Uploading...';
}
}.bind(this), false);
app.p2p.addEventListener('download-started', function(e) {
if (e.detail.from === this.contact.peerId) {
this.status = 'Downloading...';
}
}.bind(this), false);
app.p2p.addEventListener('upload-complete', function(e) {
if (e.detail.from === this.contact.peerId) {
this.status = '';
}
}.bind(this), false);
app.p2p.addEventListener('download-complete', function(e) {
if (e.detail.from === this.contact.peerId) {
this.status = '';
}
}.bind(this), false);
app.p2p.addEventListener('file-declined', function(e) {
if (e.detail.from === this.contact.peerId) {
this.status = '';
}
}.bind(this), false);
app.p2p.addEventListener('upload-error', function(e) {
this.status = '';
}.bind(this), false);
}, 200);
}
});
</script>

View file

@ -1,337 +0,0 @@
<script>
'use strict';
window.Chat = window.Chat || {};
var djb2Code = function(str) {
var hash = 5381;
for (var i = 0; i < str.length; i++) {
var character = str.charCodeAt(i);
hash = ((hash << 5) + hash) + character; /* hash * 33 + c */
}
return hash > 0 ? hash : -hash;
};
var animals = [
'Adelie',
'Penguin',
'Akita',
'Bulldog',
'Ant',
'Fox',
'Hare',
'Wolf',
'Terrier',
'Avocet',
'Baboon',
'Camel',
'Badger',
'Barb',
'Basenji',
'Basking',
'Bat',
'Beagle',
'Bear',
'Collie',
'Beaver',
'Beetle',
'Bichon',
'Bird',
'Birman',
'Bison',
'Bobcat',
'Bombay',
'Bongo',
'Bonobo',
'Booby',
'Boykin',
'Budgie',
'Buffalo',
'Burmese',
'Fish',
'Caiman',
'Lizard',
'Canaan',
'Caracal',
'Cat',
'Catfish',
'Cesky',
'Fousek',
'Chamois',
'Cheetah',
'Chicken',
'Chinook',
'Cichlid',
'Leopard',
'Clumber',
'Coati',
'Coral',
'Tamarin',
'Cougar',
'Cow',
'Coyote',
'Crab',
'Macaque',
'Crane',
'Cuscus',
'Frog',
'Deer',
'Bracke',
'Dhole',
'Dingo',
'Discus',
'Dodo',
'Dog',
'Dogo',
'Dolphin',
'Donkey',
'Drever',
'Duck',
'Dugong',
'Dunker',
'Dusky',
'Eagle',
'Earwig ',
'Gorilla',
'Echidna',
'Emu',
'Falcon',
'Fennec',
'Ferret',
'Spitz',
'Fly',
'Fossa',
'Gecko',
'Gerbil',
'Gharial',
'Gibbon',
'Giraffe',
'Goat',
'Oriole',
'Goose',
'Gopher',
'Grouse',
'Guppy',
'Shark',
'Hamster',
'Harrier',
'Heron',
'Horse',
'Human',
'Hyena',
'Ibis',
'Iguana',
'Impala',
'Indri',
'Insect',
'Setter',
'Jackal',
'Jaguar',
'Kakapo',
'Kiwi',
'Koala',
'Lemming',
'Lemur',
'Liger',
'Lion',
'Llama',
'Lobster',
'Owl',
'Lynx',
'Mayfly',
'Meerkat',
'Molly',
'Mongrel',
'Monkey',
'Moorhen',
'Moose',
'Mouse',
'Mule',
'Numbat',
'Ocelot',
'Octopus',
'Okapi',
'Opossum',
'Ostrich',
'Otter',
'Oyster',
'Panther',
'Parrot',
'Peacock',
'Pelican',
'Persian',
'Pig',
'Piranha',
'Pointer',
'Poodle',
'Possum',
'Prawn',
'Puffin',
'Pug',
'Puma',
'Pygmy',
'Quail',
'Quetzal',
'Quokka',
'Quoll',
'Rabbit',
'Raccoon',
'Ragdoll',
'Rat',
'Robin',
'Saola',
'Seal',
'Serval',
'Sheep',
'Shrimp',
'Siamese',
'Skunk',
'Sloth',
'Snail',
'Snake',
'Somali',
'Sparrow',
'Dogfish',
'Sponge',
'Squid',
'Stoat',
'Swan',
'Tang',
'Tapir',
'Tarsier',
'Termite',
'Tetra',
'Tiffany',
'Tiger',
'Toucan',
'Tuatara',
'Turkey',
'Uakari',
'Uguisu',
'Vulture',
'Wallaby',
'Walrus',
'Warthog',
'Wasp',
'Weasel',
'Whippet',
'Wombat',
'Wrasse',
'Yak',
'Yorkie',
'Zebra',
'Zebu',
'Zonkey',
'Zorse'
];
var bb = [
'Walter White',
'Skyler White',
'Jesse Pinkman',
'Hank Schrader',
'Marie Schrader',
'Walter White, Jr.',
'Saul Goodman',
'Gustavo Fring',
'Mike Ehrmantraut',
'Lydia Rodarte-Quayle',
'Todd Alquist',
'Steven Gomez',
'Detectives Kalanchoe & Munn',
'George Merkert',
'Sac Ramey',
'Tim Roberts',
'Maximino Arciniega',
'Gale Boetticher',
'Duane Chow',
'Ron Forenall',
'Barry Goodman',
'Tyrus Kitt',
'Chris Mara',
'Dennis Markowski',
'Victor',
'Dan Wachsberger',
'Don Eladio Vuente',
'Juan Bolsa',
'Hector Salamanca',
'Tuco Salamanca',
'Leonel Salamanca',
'Marco Salamanca',
'Gonzo',
'Emilio Koyama',
'Krazy-8 Molina',
'Jack Welker',
'Andrea Cantillo',
'Brock Cantillo',
'Jane Margolis',
'Brandon Mayhew',
'Combo Ortega',
'Skinny Pete',
'Adam Pinkman',
'Mrs. Pinkman',
'Jake Pinkman',
'Wendy',
'Huell Babineaux',
'Ed',
'Francesca',
'Patrick Kuby',
'Hugo Archuleta',
'Ted Beneke',
'Clovis',
'Louis Corbett',
'Dr. Delcavoli',
'Lawson',
'Donald Margolis',
'Carmen Molina',
'Old Joe',
'Pamela',
'Gretchen Schwartz',
'Elliott Schwartz',
'Drew Sharp',
'Spooge',
'Holly White',
'Bogdan Wolynetz'
];
Chat.AnonymousContactBehavior = {
properties: {
contact: {
type: Object,
notify: true
},
_displayName: {
computed: '_computeDisplayName(contact)'
}
},
_computeDisplayName: function(contact) {
if (contact === undefined || contact === null) {
return 'connecting...';
}
if (contact === 'error' || contact === 'invite') {
return '';
}
if (!contact.name) {
return this.anonymousAccount(contact).name;
}
return contact.name;
},
get names() {
return bb;
},
anonymousAccount: function(contact) {
if (contact && !contact.name) {
var peer = contact.peer || contact;
var hash = djb2Code(peer);
var i = hash % this.names.length;
var name = this.names[i];
var marginTop = i % 2;
var marginLeft = Math.floor(i / 2) % 5;
return {
name: name,
peer: peer,
avatar: {
url: 'images/avatars.jpg',
left: -14 + 80 * marginLeft,
top: -19 + 95 * marginTop
}
};
}
}
};
</script>

View file

@ -1,15 +1,15 @@
<link rel="import" href="../bower_components/platinum-sw/platinum-sw-cache.html">
<link rel="import" href="../bower_components/platinum-sw/platinum-sw-register.html">
<link rel="import" href="../bower_components/paper-toast/paper-toast.html">
<link rel="import" href="../bower_components/paper-progress/paper-progress.html">
<!-- Configure your routes here -->
<!-- Configure your routes here
<link rel="import" href="routing.html">
-->
<!-- Add your elements here -->
<link rel="import" href="../styles/app-theme.html">
<link rel="import" href="../styles/shared-styles.html">
<link rel="import" href="buddy-finder/buddy-finder.html">
<link rel="import" href="p2p-network/p2p-network.html">
<link rel="import" href="p2p-network/connection-wrapper.html">
<link rel="import" href="file-sharing/file-receiver.html">

View file

@ -8,6 +8,7 @@ Chat.FileButtonBehaviorImpl = {
if (!fileInput) {
fileInput = document.createElement('input');
fileInput.type = 'file';
fileInput.multiple = 'true';
fileInput.className = 'fileInput';
fileInput.style.position = 'fixed';
fileInput.style.top = '-10000px';

View file

@ -1,4 +1,4 @@
<link rel="import" href="../../../bower_components/paper-icon-button/paper-icon-button.html">
<link rel="import" href="../../bower_components/paper-icon-button/paper-icon-button.html">
<link rel="import" href="file-button-behavior.html">
<dom-module id="file-button">
<template>

View file

@ -36,5 +36,13 @@ Chat.FileDropBehaviorImpl = {
});
}
};
document.body.addEventListener('dragover', function(e) {
e.stopPropagation();
e.preventDefault();
}, false);
document.body.addEventListener('drop', function(event) {
event.stopPropagation();
event.preventDefault();
});
Chat.FileDropBehavior = [Chat.FileDropBehaviorImpl, Chat.FileSelectionBehavior];
</script>

View file

@ -2,51 +2,94 @@
<link rel="import" href="../../bower_components/paper-button/paper-button.html">
<link rel="import" href="../../bower_components/neon-animation/animations/scale-up-animation.html">
<link rel="import" href="../../bower_components/neon-animation/animations/fade-out-animation.html">
<link rel="import" href="../../bower_components/iron-pages/iron-pages.html">
<link rel="import" href="../../bower_components/paper-spinner/paper-spinner.html">
<dom-module id="file-receiver">
<template>
<style>
:host {
display: block;
position: fixed;
z-index: 100;
}
#dialog,
#download {
width: 300px;
z-index: 101;
}
b {
word-break: break-word;
}
</style>
<paper-dialog id="dialog" entry-animation="scale-up-animation" exit-animation="fade-out-animation" with-backdro>
<h2>File Received</h2>
<p>You received file {{file.name}}</p>
<paper-dialog id="dialog" entry-animation="scale-up-animation" exit-animation="fade-out-animation" with-backdrop modal>
<h2>Download File</h2>
<p><b>{{file.name}}</b></p>
<div class="buttons">
<paper-button dialog-dismiss>Dismiss</paper-button>
<paper-button dialog-confirm on-tap="_download">Download</paper-button>
<paper-button dialog-dismiss on-tap="_decline">Discard</paper-button>
<paper-button dialog-confirm on-tap="_accept" autofocus>Download</paper-button>
</div>
</paper-dialog>
<paper-dialog id="download" entry-animation="scale-up-animation" exit-animation="fade-out-animation" with-backdrop modal>
<h2>File Received</h2>
<p>Right Click and "Save as"...</p>
<div class="buttons">
<paper-button dialog-dismiss>Discard</paper-button>
<a href="{{dataUri}}" target="_blank">
<paper-button dialog-confirm autofocus>Download</paper-button>
</a>
</div>
</paper-dialog>
</template>
<script>
'use strict';
(function() {
Polymer({
is: 'file-receiver',
attached: function() {
this.async(function() {
app.p2p.addEventListener('file-received', function(e) {
this.fileReceived(e.detail);
}.bind(this), false);
},200);
},
fileReceived: function(file) {
this.set('file', file);
app.p2p.addEventListener('file-offer', function(e) {
this.file = e.detail;
this.$.dialog.open();
}.bind(this), false);
app.p2p.addEventListener('file-received', function(e) {
this._fileReceived(e.detail);
}.bind(this), false);
app.p2p.addEventListener('file-declined', function(e) {
app.displayToast('User declined file ' + e.detail.name);
}.bind(this), false);
app.p2p.addEventListener('upload-complete', function(e) {
app.displayToast('User received file ' + e.detail.name);
}.bind(this), false);
app.p2p.addEventListener('upload-error', function(e) {
app.displayToast('The other device did not respond. Please try again.');
}.bind(this), false);
}, 200);
},
_download: function() {
_fileReceived: function(file) {
this.downloadURI(file);
},
_decline: function() {
app.p2p.decline(this.file);
},
_accept: function() {
app.p2p.accept(this.file);
},
downloadURI: function(file) {
var link = document.createElement('a');
link.download = this.file.name;
// Construct the uri
var uri = this.file.dataURI;
var uri = (window.URL || window.webkitURL).createObjectURL(file.blob);
if (typeof link.download !== 'undefined') {
//download attribute is supported
link.href = uri;
link.download = file.name || 'blank';
document.body.appendChild(link);
link.click();
// Cleanup the DOM
document.body.removeChild(link);
//delete link;
} else {
this.dataUri = uri;
this.$.download.open();
}
}
});
}());
</script>
</dom-module>

File diff suppressed because one or more lines are too long

View file

@ -3,22 +3,16 @@
window.Chat = window.Chat || {};
Chat.FileSelectionBehavior = {
notifyFilesSelection: function(files) {
if(!files){
if (!files) {
console.log('no files selected...');
return;
}
for (var i = 0; i < files.length; i++) {
var file = files[i];
var reader = new FileReader();
reader.onload = function(e2) {
// finished reading file data.
console.log('file dropped');
this.fire('file-selected', {
dataURI: e2.target.result,
file: file,
name: file.name
});
}.bind(this);
reader.readAsDataURL(file); // start reading the file data.
}
}
};

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,59 @@
<link rel="import" href="p2p-network.html">
<link rel="import" href="web-socket.html">
<dom-module id="connection-wrapper">
<template>
<p2p-network id="p2p" me="{{me}}"></p2p-network>
<web-socket id="ws" me="{{me}}"></web-socket>
</template>
<script>
'use strict';
(function() {
function guid() {
function s4() {
return Math.floor((1 + Math.random()) * 0x10000)
.toString(16)
.substring(1);
}
return s4() + s4() + '-' + s4() + '-' + s4() + '-' +
s4() + '-' + s4() + s4() + s4();
}
var webRTCSupported = window.RTCPeerConnection || window.mozRTCPeerConnection || window.webkitRTCPeerConnection || window.webkitRTCPeerConnection;
function rtcConnectionSupported(peerId) {
return webRTCSupported && (peerId.indexOf('rtc_') === 0);
}
Polymer({
is: 'connection-wrapper',
properties: {
me: {
notify: true,
value: (webRTCSupported ? 'rtc_' : 'ws_') + guid()
}
},
behaviors: [Chat.FileTransferProtocol],
_sendFile: function(toPeer, file) {
if (!rtcConnectionSupported(toPeer)) {
this.$.ws._sendFile(toPeer, file);
} else {
this.$.p2p._sendFile(toPeer, file);
}
},
_sendSystemEvent: function(toPeer, event) {
console.log('system event', toPeer, event);
if (!rtcConnectionSupported(toPeer)) {
this.$.ws._sendSystemEvent(toPeer, event);
} else {
this.$.p2p._sendSystemEvent(toPeer, event);
}
},
connectToPeer: function(toPeer, callback) {
if (!rtcConnectionSupported(toPeer)) {
callback();
} else {
this.$.p2p.connectToPeer(toPeer,callback);
}
},
});
})();
</script>
</dom-module>

View file

@ -0,0 +1,137 @@
<script>
'use strict';
window.Chat = window.Chat || {};
Chat.FileTransferProtocol = {
properties: {
loading: {
type: Boolean,
notify: true,
value: false,
observer: '_loadingChanged'
},
buddies: {
notify: true
}
},
listeners: {
'system-event': '_onSystemMsg',
'file-received': '_onFileReceived',
},
_onSystemMsg: function(event) {
var msg = event.detail;
console.log('FTP received sysMsg:', msg);
switch (msg.type) {
case 'offer':
this._onOffered(msg);
break;
case 'decline':
this._onDeclined(msg);
break;
case 'accept':
this._onAccepted(msg);
break;
case 'transfer':
this._onTransfer(msg);
break;
case 'received':
this._onReceived(msg);
break;
case 'buddies':
this._onBuddies(msg);
break;
}
},
sendFile: function(peerId, file) {
this.set('loading', true);
this.fileToSend = file;
this.fire('file-offered', {
to: peerId
});
this.connectToPeer(peerId, function() {
this._offer(peerId, file);
}.bind(this));
//set 15sec timeout
this._timeoutTimer = this.async(function() {
this._onError();
}, 15000);
},
_offer: function(toPeer, file) {
console.log('FTP offer file:', file, 'To:', toPeer);
this._sendSystemEvent(toPeer, {
type: 'offer',
name: file.name
});
},
_onOffered: function(offer) {
console.log('FTP offered file:', offer.name, 'From:', offer.from);
this.fire('file-offer', {
from: offer.from,
name: offer.name
});
},
decline: function(offer) {
this._sendSystemEvent(offer.from, {
type: 'decline',
name: offer.name
});
},
_onDeclined: function(offer) {
this.cancelAsync(this._timeoutTimer);
delete this.fileToSend;
this.set('loading', false);
this.fire('file-declined', offer);
},
accept: function(offer) {
this._sendSystemEvent(offer.from, {
type: 'accept',
name: offer.name
});
this.fire('download-started', {
from: offer.from
});
},
_onAccepted: function(offer) {
this.cancelAsync(this._timeoutTimer);
this._sendSystemEvent(offer.from, {
type: 'transfer',
name: offer.name
});
this.fire('upload-started', {
to: offer.from
});
this._sendFile(offer.from, this.fileToSend);
},
_onTransfer: function() {
this.loading = true;
},
_onFileReceived: function(event) {
var file = event.detail;
this.loading = false;
this._sendSystemEvent(file.from, {
type: 'received',
name: file.name
});
this.fire('download-complete', {
from: file.from
});
console.log('FTP received:', file);
},
_onReceived: function(offer) {
this.loading = false;
this.fire('upload-complete', offer);
},
_onError: function() {
this.loading = false;
this.fire('upload-error');
},
_loadingChanged: function(loading) {
window.anim(loading);
},
_onBuddies: function(msg) {
this.set('buddies', msg.buddies);
}
};
</script>

View file

@ -1,4 +1,5 @@
<script src="../../../bower_components/peerjs/peer.min.js"></script>
<script src="../../bower_components/peerjs/peer.min.js"></script>
<link rel="import" href="file-transfer-protocol.html">
<dom-module id="p2p-network">
<template>
</template>
@ -30,7 +31,7 @@
path: 'peerjs',
secure: true
};
this._peer = new Peer(options);
this._peer = new Peer(this.me,options);
this._peer.on('open', function(id) {
console.log('My peer ID is: ' + id);
this.set('me', id);
@ -65,12 +66,22 @@
if (c.label === 'file') {
c.on('data', function(data) {
console.log('received!', data);
console.log(data);
var dataView = new Uint8Array(data.file);
var dataBlob = new Blob([dataView]);
this.fire('file-received', {
peer: peer,
dataURI: data.dataURI,
from: peer,
blob: dataBlob,
name: data.name,
});
}.bind(this));
}
if (c.label === 'system') {
c.on('data', function(data) {
data.from = peer;
this.fire('system-event', data);
}.bind(this));
}
},
@ -78,15 +89,32 @@
function request(requestedPeer, callback) {
return function() {
//system messages channel
var s = this._peer.connect(requestedPeer, {
label: 'system'
});
s.on('open', function() {
this.connect(s);
if (callback) {
callback();
}
}.bind(this));
s.on('error', function(err) {
console.log(err);
if (err.message.indexOf('Connection is not open') > -1) {
console.err('Handle this error!!');
}
});
//files channel
var f = this._peer.connect(requestedPeer, {
label: 'file',
reliable: true
});
f.on('open', function() {
this.connect(f);
if (callback) {
callback();
}
}.bind(this));
f.on('error', function(err) {
console.log(err);
@ -98,7 +126,6 @@
callback();
return;
}
this.set('loading', true);
if (this._peerOpen) {
request(requestedPeer, callback).bind(this)();
} else {
@ -107,7 +134,7 @@
};
}()),
sendFile: function(peerId, file) {
_sendFile: function(peerId, file) {
var conns = this._peer.connections[peerId];
if (conns) {
conns.forEach(function(conn) {
@ -115,7 +142,17 @@
conn.send(file);
console.log('file send');
}
});
}.bind(this));
}
},
_sendSystemEvent: function(peerId, msg) {
var conns = this._peer.connections[peerId];
if (conns) {
conns.forEach(function(conn) {
if (conn.label === 'system') {
conn.send(msg);
}
}.bind(this));
}
}
});

View file

@ -0,0 +1,82 @@
<link rel="import" href="binaryjs.html">
<dom-module id="web-socket">
<template>
<style>
:host {
display: block;
}
</style>
</template>
<script>
'use strict';
Polymer({
is: 'web-socket',
attached: function() {
this.init();
},
init: function() {
var websocketUrl = (window.location.protocol === 'https:' ? 'wss://' : 'ws://') + document.location.hostname + ':9001';
this.client = new BinaryClient(websocketUrl);
this.client.on('stream', function(stream, meta) {
// collect stream data
var parts = [];
stream.on('data', function(data) {
console.log('part received', meta, data);
if (data.isSystemEvent) {
if (meta) {
data.from = meta.from;
}
this.fire('system-event', data);
} else {
parts.push(data);
}
}.bind(this));
// when finished, set it as the background image
stream.on('end', function() {
var blob = new Blob(parts, {
type: meta.type
});
console.log('file received', blob, meta);
this.fire('file-received', {
blob: blob,
name: meta.name,
from: meta.from
});
}.bind(this));
}.bind(this));
this.client.on('open', function(e) {
console.log(e);
this.client.send({}, {
handshake: this.me
});
}.bind(this));
this.client.on('error', function(e) {
console.log(e);
});
this.client.on('close', function(e) {
console.log(e);
//try to reconnect after 3s
this.async(this.init, 3000);
}.bind(this));
},
_sendFile: function(toPeer, file) {
console.log('send file!', file);
this.client.send(file.file, {
name: file.file.name,
type: file.file.type,
toPeer: toPeer
});
},
connectToPeer: function(peer, callback) {
callback();
},
_sendSystemEvent: function(toPeer, event) {
console.log('system event', toPeer, event);
event.isSystemEvent = true;
this.client.send(event, {
toPeer: toPeer
});
}
});
</script>
</dom-module>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 611 B

After

Width:  |  Height:  |  Size: 1.1 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.2 KiB

After

Width:  |  Height:  |  Size: 29 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

After

Width:  |  Height:  |  Size: 82 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6 KiB

After

Width:  |  Height:  |  Size: 18 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4 KiB

After

Width:  |  Height:  |  Size: 22 KiB

Before After
Before After

BIN
app/images/touch/logo.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 58 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.4 KiB

After

Width:  |  Height:  |  Size: 27 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.4 KiB

After

Width:  |  Height:  |  Size: 27 KiB

Before After
Before After

View file

@ -4,12 +4,12 @@
<head>
<meta charset="utf-8">
<meta name="description" content="">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="generator" content="Share With Me!">
<title>Share With Me!</title>
<meta name="viewport" content="initial-scale=1,user-scalable=no,maximum-scale=1">
<meta name="generator" content="SnapDrop!">
<title>SnapDrop!</title>
<!-- Place favicon.ico in the `app/` directory -->
<!-- Chrome for Android theme color -->
<meta name="theme-color" content="#2E3AA1">
<meta name="theme-color" content="#3367d6">
<!-- Web Application Manifest -->
<link rel="manifest" href="manifest.json">
<!-- Tile color for Win8 -->
@ -21,7 +21,7 @@
<!-- Add to homescreen for Safari on iOS -->
<meta name="apple-mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-app-status-bar-style" content="black">
<meta name="apple-mobile-web-app-title" content="Share With Me!">
<meta name="apple-mobile-web-app-title" content="SnapDrop!">
<link rel="apple-touch-icon" href="images/touch/apple-touch-icon.png">
<!-- Tile icon for Win8 (144x144) -->
<meta name="msapplication-TileImage" content="images/touch/ms-touch-icon-144x144-precomposed.png">
@ -29,23 +29,23 @@
<link rel="stylesheet" href="styles/main.css">
<!-- endbuild-->
<!-- build:js bower_components/webcomponentsjs/webcomponents-lite.min.js -->
<script src="bower_components/webcomponentsjs/webcomponents-lite.js"></script>
<script src="bower_components/webcomponentsjs/webcomponents-lite.js" async></script>
<!-- endbuild -->
<!-- Because this project uses vulcanize this should be your only html import
in this file. All other imports should go in elements.html -->
<link rel="import" href="elements/elements.html">
<!-- For shared styles, shared-styles.html import in elements.html -->
<style is="custom-style" include="shared-styles"></style>
<link rel="import" href="elements/elements.html" async>
<meta name="description" content="SnapDrop lets you instantly share files with people near by. It is a web-based clone of Apple's Airdrop.">
</head>
<body unresolved class="fullbleed layout vertical">
<body class="fullbleed layout vertical" loading>
<script src="scripts/animated-bg.js" inline></script>
<span id="browser-sync-binding"></span>
<template is="dom-bind" id="app">
<buddy-finder me="{{me}}"></buddy-finder>
<p2p-network me="{{me}}"></p2p-network>
<paper-progress indeterminate hidden$="{{!loading}}"></paper-progress>
<buddy-finder me="{{me}}" active$="{{loading}}" buddies="{{buddies}}"></buddy-finder>
<connection-wrapper me="{{me}}" loading="{{loading}}" buddies="{{buddies}}"></connection-wrapper>
<file-receiver></file-receiver>
<paper-toast id="toast">
<span class="toast-hide-button" role="button" tabindex="0" onclick="app.$.toast.hide()">Ok</span>
<paper-toast id="toast" duration="6000">
</paper-toast>
<!-- Uncomment next block to enable Service Worker support (1/2) -->
<paper-toast id="caching-complete" duration="6000" text="Caching complete! This app will work offline.">

View file

@ -1,6 +1,6 @@
{
"name": "Share With Me",
"short_name": "Share With Me",
"name": "SnapDrop",
"short_name": "SnapDrop",
"icons": [{
"src": "images/touch/icon-128x128.png",
"sizes": "128x128",
@ -17,12 +17,14 @@
"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": "#3E4EB8",
"background_color": "#3367d6",
"start_url": "index.html",
"display": "standalone",
"theme_color": "#2E3AA1"
"theme_color": "#3367d6",
"orientation": "portrait"
}

View file

@ -0,0 +1,64 @@
'use strict';
(function() {
var requestAnimFrame = (function() {
return window.requestAnimationFrame || window.webkitRequestAnimationFrame || window.mozRequestAnimationFrame || window.oRequestAnimationFrame || window.msRequestAnimationFrame ||
function(callback) {
window.setTimeout(callback, 1000 / 60);
};
})();
var c = document.createElement('canvas');
document.body.appendChild(c);
var style = c.style;
style.width = '100%';
style.position = 'absolute';
var ctx = c.getContext('2d');
var x0, y0, w, h, dw;
function init() {
w = window.innerWidth;
h = window.innerHeight;
c.width = w;
c.height = h;
x0 = w / 2;
y0 = h - 103;
dw = Math.max(w, h, 1000) / 13;
drawCircles();
}
window.onresize = init;
function drawCicrle(radius) {
ctx.beginPath();
var color = Math.round(255 * (1- radius / Math.max(w, h)));
ctx.strokeStyle = 'rgba(' + color + ',' + color + ',' + color + ',0.1)';
ctx.arc(x0, y0, radius, 0, 2 * Math.PI);
ctx.stroke();
ctx.lineWidth = 2;
}
var step = 0;
function drawCircles() {
ctx.clearRect(0, 0, w, h);
for (var i = 0; i < 8; i++) {
drawCicrle(dw * i + step % dw);
}
step += 1;
}
var loading = true;
function animate() {
if (loading || step % dw < dw - 5) {
requestAnimFrame(function() {
drawCircles();
animate();
});
}
}
window.anim = function(l) {
loading = l;
animate();
};
init();
animate();
}());

View file

@ -21,17 +21,25 @@
}
};
app.displayToast = function(msg) {
var toast = Polymer.dom(document).querySelector('#toast');
toast.text = msg;
toast.show();
};
// Listen for template bound event to know when bindings
// have resolved and content has been stamped to the page
app.addEventListener('dom-change', function() {
console.log('Our app is ready to rock!');
app.p2p = document.querySelector('connection-wrapper');
});
// See https://github.com/Polymer/polymer/issues/1381
window.addEventListener('WebComponentsReady', function() {
// imports are loaded and elements have been registered
app.p2p = document.querySelector('p2p-network');
});
})(document);

View file

@ -1,214 +1,31 @@
<!--
@license
Copyright (c) 2015 The Polymer Project Authors. All rights reserved.
This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
Code distributed by Google as part of the polymer project is also
subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
-->
<link rel="import" href="../bower_components/polymer/polymer.html">
<style is="custom-style">
/*
Polymer includes a shim for CSS Custom Properties that we can use for application theming.
Below, you'll find the default palette for the Share With Me layout. Feel free to play
with changing the colors used or generate your own palette of colours at MaterialPalette.com.
See https://www.polymer-project.org/1.0/docs/devguide/styling.html#xscope-styling-details
for further information on custom CSS properties.
*/
/* Application theme */
:root {
:root {
--dark-primary-color: #303F9F;
--default-primary-color: #3F51B5;
--light-primary-color: #C5CAE9;
--text-primary-color: #ffffff; /*text/icons*/
--text-primary-color: #ffffff;
/*text/icons*/
--accent-color: #FF4081;
--primary-background-color: #c5cae9;
--primary-text-color: #212121;
--secondary-text-color: #727272;
--disabled-text-color: #bdbdbd;
--divider-color: #B6B6B6;
/* Components */
/* paper-drawer-panel */
--drawer-menu-color: #ffffff;
--drawer-border-color: 1px solid #ccc;
--drawer-toolbar-border-color: 1px solid rgba(0, 0, 0, 0.22);
/* paper-menu */
--paper-menu-background-color: #fff;
--menu-link-color: #111111;
}
}
/* General styles */
#drawerToolbar {
color: var(--secondary-text-color);
background-color: var(--drawer-menu-color);
border-bottom: var(--drawer-toolbar-border-color);
}
paper-scroll-header-panel {
height: 100%;
}
paper-material {
border-radius: 2px;
height: 100%;
padding: 16px 0 16px 0;
width: calc(98.66% - 16px);
margin: 16px auto;
background: white;
}
paper-menu iron-icon {
margin-right: 33px;
opacity: 0.54;
}
.paper-menu > .iron-selected {
color: var(--default-primary-color);
}
paper-menu a {
@apply(--layout-horizontal);
@apply(--layout-center);
text-decoration: none;
color: var(--menu-link-color);
font-family: 'Roboto', 'Noto', sans-serif;
-webkit-font-smoothing: antialiased;
text-rendering: optimizeLegibility;
font-size: 14px;
font-weight: 400;
line-height: 24px;
min-height: 48px;
padding: 0 16px;
}
paper-toolbar.tall .app-name {
font-size: 40px;
font-weight: 300;
/* Required for main area's paper-scroll-header-panel custom condensing transformation */
-webkit-transform-origin: left center;
transform-origin: left center;
}
#mainToolbar .middle-container {
height: 100%;
margin-left: 48px;
}
#mainToolbar:not(.tall) .middle {
font-size: 18px;
padding-bottom: 0;
}
#mainToolbar .bottom {
margin-left: 48px;
/* Required for main area's paper-scroll-header-panel custom condensing transformation */
-webkit-transform-origin: left center;
transform-origin: left center;
}
/* Height of the scroll area */
.content {
height: 900px;
}
#toast .toast-hide-button {
color: #eeff41;
margin: 10px;
}
/* Breakpoints */
/* Small */
@media (max-width: 600px) {
paper-material {
--menu-container-display: none;
width: calc(97.33% - 32px);
padding-left: 16px;
padding-right: 16px;
}
paper-toolbar.tall .app-name {
font-size: 24px;
font-weight: 400;
}
#drawer .paper-toolbar {
margin-left: 16px;
}
}
/* Tablet+ */
@media (min-width: 601px) {
paper-material {
width: calc(98% - 46px);
margin-bottom: 32px;
padding-left: 30px;
padding-right: 30px;
}
#drawer.paper-drawer-panel > [drawer] {
border-right: 1px solid rgba(0, 0, 0, 0.14);
}
iron-pages {
padding: 48px 62px;
}
}
/* Material Design Adaptive Breakpoints */
/*
Below you'll find CSS media queries based on the breakpoint guidance
published by the Material Design team. You can choose to use, customise
or remove these breakpoints based on your needs.
http://www.google.com/design/spec/layout/adaptive-ui.html#adaptive-ui-breakpoints
*/
/* mobile-small */
@media all and (min-width: 0) and (max-width: 360px) and (orientation: portrait) { }
/* mobile-large */
@media all and (min-width: 361px) and (orientation: portrait) { }
/* mobile-small-landscape */
@media all and (min-width: 0) and (max-width: 480px) and (orientation: landscape) { }
/* mobile-large-landscape */
@media all and (min-width: 481px) and (orientation: landscape) { }
/* tablet-small-landscape */
@media all and (min-width: 600px) and (max-width: 960px) and (orientation: landscape) { }
/* tablet-large-landscape */
@media all and (min-width: 961px) and (orientation: landscape) { }
/* tablet-small */
@media all and (min-width: 600px) and (orientation: portrait) { }
/* tablet-large */
@media all and (min-width: 601px) and (max-width: 840px) and (orientation : portrait) { }
/* desktop-x-small-landscape */
@media all and (min-width: 0) and (max-width: 480px) and (orientation: landscape) { }
/* desktop-x-small */
@media all and (min-width: 0) and (max-width: 480px) and (max-aspect-ratio: 4/3) { }
/* desktop-small-landscape */
@media all and (min-width: 481px) and (max-width: 840px) and (orientation: landscape) { }
/* desktop-small */
@media all and (min-width: 481px) and (max-width: 840px) and (max-aspect-ratio: 4/3) { }
/* desktop-medium-landscape */
@media all and (min-width: 841px) and (max-width: 1280px) and (orientation: landscape) { }
/* desktop-medium */
@media all and (min-width: 841px) and (max-width: 1280px) and (max-aspect-ratio: 4/3) { }
/* desktop-large */
@media all and (min-width: 1281px) and (max-width: 1600px) { }
/* desktop-xlarge */
@media all and (min-width: 1601px) and (max-width: 1920px) { }
paper-progress {
width: 100%;
z-index: 1;
position: absolute;
top: 0;
}
</style>

37
app/styles/icons.html Normal file
View file

@ -0,0 +1,37 @@
<link rel="import" href="../bower_components/iron-iconset-svg/iron-iconset-svg.html">
<iron-iconset-svg name="chat" size="24">
<svg>
<defs>
<g id="notifications-off">
<path d="M11.5 22c1.1 0 2-.9 2-2h-4c0 1.1.9 2 2 2zM18 10.5c0-3.07-2.13-5.64-5-6.32V3.5c0-.83-.67-1.5-1.5-1.5S10 2.67 10 3.5v.68c-.51.12-.99.32-1.45.56L18 14.18V10.5zm-.27 8.5l2 2L21 19.73 4.27 3 3 4.27l2.92 2.92C5.34 8.16 5 9.29 5 10.5V16l-2 2v1h14.73z" />
</g>
<g id="share">
<path d="M18 16.08c-.76 0-1.44.3-1.96.77L8.91 12.7c.05-.23.09-.46.09-.7s-.04-.47-.09-.7l7.05-4.11c.54.5 1.25.81 2.04.81 1.66 0 3-1.34 3-3s-1.34-3-3-3-3 1.34-3 3c0 .24.04.47.09.7L8.04 9.81C7.5 9.31 6.79 9 6 9c-1.66 0-3 1.34-3 3s1.34 3 3 3c.79 0 1.5-.31 2.04-.81l7.12 4.16c-.05.21-.08.43-.08.65 0 1.61 1.31 2.92 2.92 2.92 1.61 0 2.92-1.31 2.92-2.92s-1.31-2.92-2.92-2.92z" />
</g>
<g id="call">
<path d="M6.62 10.79c1.44 2.83 3.76 5.14 6.59 6.59l2.2-2.2c.27-.27.67-.36 1.02-.24 1.12.37 2.33.57 3.57.57.55 0 1 .45 1 1V20c0 .55-.45 1-1 1-9.39 0-17-7.61-17-17 0-.55.45-1 1-1h3.5c.55 0 1 .45 1 1 0 1.25.2 2.45.57 3.57.11.35.03.74-.25 1.02l-2.2 2.2z" />
</g>
<g id="wifi-tethering">
<path d="M12 11c-1.1 0-2 .9-2 2s.9 2 2 2 2-.9 2-2-.9-2-2-2zm6 2c0-3.31-2.69-6-6-6s-6 2.69-6 6c0 2.22 1.21 4.15 3 5.19l1-1.74c-1.19-.7-2-1.97-2-3.45 0-2.21 1.79-4 4-4s4 1.79 4 4c0 1.48-.81 2.75-2 3.45l1 1.74c1.79-1.04 3-2.97 3-5.19zM12 3C6.48 3 2 7.48 2 13c0 3.7 2.01 6.92 4.99 8.65l1-1.73C5.61 18.53 4 15.96 4 13c0-4.42 3.58-8 8-8s8 3.58 8 8c0 2.96-1.61 5.53-4 6.92l1 1.73c2.99-1.73 5-4.95 5-8.65 0-5.52-4.48-10-10-10z" />
</g>
<g id="attach-file">
<path d="M16.5 6v11.5c0 2.21-1.79 4-4 4s-4-1.79-4-4V5c0-1.38 1.12-2.5 2.5-2.5s2.5 1.12 2.5 2.5v10.5c0 .55-.45 1-1 1s-1-.45-1-1V6H10v9.5c0 1.38 1.12 2.5 2.5 2.5s2.5-1.12 2.5-2.5V5c0-2.21-1.79-4-4-4S7 2.79 7 5v12.5c0 3.04 2.46 5.5 5.5 5.5s5.5-2.46 5.5-5.5V6h-1.5z" />
</g>
<g id="desktop-mac">
<path d="M21 2H3c-1.1 0-2 .9-2 2v12c0 1.1.9 2 2 2h7l-2 3v1h8v-1l-2-3h7c1.1 0 2-.9 2-2V4c0-1.1-.9-2-2-2zm0 12H3V4h18v10z" />
</g>
<g id="desktop-windows">
<path d="M21 2H3c-1.1 0-2 .9-2 2v12c0 1.1.9 2 2 2h7v2H8v2h8v-2h-2v-2h7c1.1 0 2-.9 2-2V4c0-1.1-.9-2-2-2zm0 14H3V4h18v12z" />
</g>
<g id="smartphone">
<path d="M17 1.01L7 1c-1.1 0-2 .9-2 2v18c0 1.1.9 2 2 2h10c1.1 0 2-.9 2-2V3c0-1.1-.9-1.99-2-1.99zM17 19H7V5h10v14z" />
</g>
<g id="phone-iphone">
<path d="M15.5 1h-8C6.12 1 5 2.12 5 3.5v17C5 21.88 6.12 23 7.5 23h8c1.38 0 2.5-1.12 2.5-2.5v-17C18 2.12 16.88 1 15.5 1zm-4 21c-.83 0-1.5-.67-1.5-1.5s.67-1.5 1.5-1.5 1.5.67 1.5 1.5-.67 1.5-1.5 1.5zm4.5-4H7V4h9v14z" />
</g>
<g id="tablet-mac">
<path d="M18.5 0h-14C3.12 0 2 1.12 2 2.5v19C2 22.88 3.12 24 4.5 24h14c1.38 0 2.5-1.12 2.5-2.5v-19C21 1.12 19.88 0 18.5 0zm-7 23c-.83 0-1.5-.67-1.5-1.5s.67-1.5 1.5-1.5 1.5.67 1.5 1.5-.67 1.5-1.5 1.5zm7.5-4H4V3h15v16z" />
</g>
</defs>
</svg>
</iron-iconset-svg>

View file

@ -1,14 +1,12 @@
/*
Copyright (c) 2015 The Polymer Project Authors. All rights reserved.
This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
Code distributed by Google as part of the polymer project is also
subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
*/
html,
body {
height: 100%;
width: 100%;
}
body {
background: #fafafa;
font-family: 'Roboto', 'Helvetica Neue', Helvetica, Arial, sans-serif;
color: #333;
-webkit-font-smoothing: antialiased;
}

View file

@ -1,23 +0,0 @@
<link rel="import" href="../bower_components/polymer/polymer.html">
<link rel="import" href="../bower_components/paper-styles/typography.html">
<!-- shared styles for all elements and index.html -->
<dom-module id="shared-styles">
<template>
<style>
.page-title {
@apply(--paper-font-display2);
}
paper-menu a > *, paper-menu paper-item > *, paper-menu paper-icon-item > * {
pointer-events: none;
}
@media (max-width: 600px) {
.page-title {
font-size: 24px!important;
}
}
</style>
</template>
</dom-module>

View file

@ -1,5 +1,3 @@
'use strict';
// Include Gulp & tools we'll use
@ -17,6 +15,7 @@ var historyApiFallback = require('connect-history-api-fallback');
var packageJson = require('./package.json');
var crypto = require('crypto');
var ensureFiles = require('./tasks/ensure-files.js');
var inlinesource = require('gulp-inline-source');
// var ghPages = require('gulp-gh-pages');
@ -42,12 +41,16 @@ var styleTask = function(stylesPath, srcs) {
return gulp.src(srcs.map(function(src) {
return path.join('app', stylesPath, src);
}))
.pipe($.changed(stylesPath, {extension: '.css'}))
.pipe($.changed(stylesPath, {
extension: '.css'
}))
.pipe($.autoprefixer(AUTOPREFIXER_BROWSERS))
.pipe(gulp.dest('.tmp/' + stylesPath))
.pipe($.minifyCss())
.pipe(gulp.dest(dist(stylesPath)))
.pipe($.size({title: stylesPath}));
.pipe($.size({
title: stylesPath
}));
};
var imageOptimizeTask = function(src, dest) {
@ -57,7 +60,9 @@ var imageOptimizeTask = function(src, dest) {
interlaced: true
}))
.pipe(gulp.dest(dest))
.pipe($.size({title: 'images'}));
.pipe($.size({
title: 'images'
}));
};
var optimizeHtmlTask = function(src, dest) {
@ -82,6 +87,7 @@ var optimizeHtmlTask = function(src, dest) {
empty: true,
spare: true
})))
.pipe($.if('*.html', inlinesource()))
// Output files
.pipe(gulp.dest(dest))
.pipe($.size({
@ -185,7 +191,9 @@ gulp.task('vulcanize', function() {
inlineScripts: true
}))
.pipe(gulp.dest(dist('elements')))
.pipe($.size({title: 'vulcanize'}));
.pipe($.size({
title: 'vulcanize'
}));
});
// Generate config data for the <sw-precache-cache> element.
@ -206,8 +214,10 @@ gulp.task('cache-config', function(callback) {
'index.html',
'./',
'bower_components/webcomponentsjs/webcomponents-lite.min.js',
'{elements,scripts,styles}/**/*.*'],
{cwd: dir}, function(error, files) {
'{elements,scripts,styles}/**/*.*'
], {
cwd: dir
}, function(error, files) {
if (error) {
callback(error);
} else {
@ -229,11 +239,12 @@ gulp.task('clean', function() {
});
// Watch files for changes & reload
gulp.task('serve', [ 'styles', 'elements', 'images'], function() {
gulp.task('serve', ['styles', 'elements', 'images'], function() {
browserSync({
port: 5000,
notify: false,
logPrefix: 'PSK',
ghostMode: false,
snippetOptions: {
rule: {
match: '<span id="browser-sync-binding"></span>',
@ -287,9 +298,8 @@ gulp.task('default', ['clean'], function(cb) {
// Uncomment 'cache-config' if you are going to use service workers.
runSequence(
['copy', 'styles'],
'elements',
['lint', 'images', 'fonts', 'html'],
'vulcanize', // 'cache-config',
'elements', ['images', 'fonts', 'html'], //'lint',
'vulcanize', 'cache-config',
cb);
});

View file

@ -13,6 +13,7 @@
"gulp-html-extract": "^0.0.3",
"gulp-if": "^2.0.0",
"gulp-imagemin": "^2.2.1",
"gulp-inline-source": "^2.1.0",
"gulp-jscs": "^3.0.0",
"gulp-jscs-stylish": "^1.1.2",
"gulp-jshint": "^1.6.3",
@ -39,5 +40,11 @@
},
"engines": {
"node": ">=0.10.0"
},
"dependencies": {
"binaryjs": "^0.2.1",
"express": "^4.13.3",
"ua-parser-js": "^0.7.10",
"ws": "^0.8.1"
}
}

106
server/ws-server.js Normal file
View file

@ -0,0 +1,106 @@
'use strict';
var fs = require('fs');
var parser = require('ua-parser-js');
// Serve client side statically
var express = require('express');
var app = express();
app.use(express.static(__dirname + '/public'));
var https = require('https');
var server = https.createServer({
key: fs.readFileSync('/var/www/sharewithme/ssl/privkey.pem').toString(),
cert: fs.readFileSync('/var/www/sharewithme/ssl/fullchain.pem').toString()
}, app);
// var http = require('http');
// var server = http.createServer(app);
// Start Binary.js server
var BinaryServer = require('binaryjs').BinaryServer;
// link it to express
var bs = BinaryServer({
server: server
});
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
};
}
// Wait for new user connections
bs.on('connection', function(client) {
console.log('connection received!');
client.deviceName = getDeviceName(client._socket.upgradeReq);
// Incoming stream from browsers
client.on('stream', function(stream, meta) {
console.log('stream received!', meta);
if (meta.handshake) {
client.uuid = meta.handshake;
return;
}
meta.from = client.uuid;
// broadcast to all other clients
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 getIP(socket) {
return socket.upgradeReq.headers['x-forwarded-for'] || socket.upgradeReq.connection.remoteAddress;
}
function notifyBuddies() {
//TODO: This should be possible in linear time
forEachClient(function(client1) {
var buddies = [];
var myIP = getIP(client1._socket);
forEachClient(function(client2) {
var otherIP = getIP(client2._socket);
console.log(myIP, otherIP);
if (client1 !== client2 && myIP === otherIP) {
buddies.push({
peerId: client2.uuid,
name: client2.deviceName
});
}
});
var msg = {
buddies: buddies,
isSystemEvent: true,
type: 'buddies'
};
client1.send(msg);
});
}
setInterval(notifyBuddies, 4000);
server.listen(9001);
console.log('HTTP and BinaryJS server started on port 9001');