Lots of small improvements, websockets fallback
|
@ -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>
|
||||
|
|
41
app/elements/buddy-finder/personal-avatar.html
Normal 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>
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
|
@ -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">
|
||||
|
||||
|
|
|
@ -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';
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
4
app/elements/file-sharing/file-saver.html
Normal 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.
|
||||
}
|
||||
}
|
||||
};
|
||||
|
|
1572
app/elements/p2p-network/binaryjs.html
Normal file
59
app/elements/p2p-network/connection-wrapper.html
Normal 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>
|
137
app/elements/p2p-network/file-transfer-protocol.html
Normal 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>
|
|
@ -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));
|
||||
}
|
||||
}
|
||||
});
|
||||
|
|
82
app/elements/p2p-network/web-socket.html
Normal 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>
|
BIN
app/favicon.ico
Before Width: | Height: | Size: 611 B After Width: | Height: | Size: 1.1 KiB |
Before Width: | Height: | Size: 5.2 KiB After Width: | Height: | Size: 29 KiB |
Before Width: | Height: | Size: 11 KiB After Width: | Height: | Size: 82 KiB |
Before Width: | Height: | Size: 6 KiB After Width: | Height: | Size: 18 KiB |
Before Width: | Height: | Size: 4 KiB After Width: | Height: | Size: 22 KiB |
BIN
app/images/touch/logo.png
Normal file
After Width: | Height: | Size: 58 KiB |
Before Width: | Height: | Size: 4.4 KiB After Width: | Height: | Size: 27 KiB |
Before Width: | Height: | Size: 4.4 KiB After Width: | Height: | Size: 27 KiB |
|
@ -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.">
|
||||
|
|
|
@ -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"
|
||||
}
|
||||
|
|
64
app/scripts/animated-bg.js
Normal 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();
|
||||
}());
|
|
@ -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);
|
||||
|
|
|
@ -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
|
@ -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>
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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>
|
34
gulpfile.js
|
@ -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);
|
||||
});
|
||||
|
||||
|
|
|
@ -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
|
@ -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');
|