Compare commits
7 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
1013473d6b | ||
|
0e587e93d7 | ||
|
60d77071c5 | ||
|
d6549ea19d | ||
|
fe28173fa7 | ||
|
2653eb5bb2 | ||
|
4732e448b2 |
1
.gitignore
vendored
@ -4,4 +4,3 @@ msg/*
|
||||
index
|
||||
interfaceip6adress
|
||||
nodes/*
|
||||
*.log
|
||||
|
220
README.md
@ -1,4 +1,4 @@
|
||||
Meshenger
|
||||
meshenger
|
||||
=========
|
||||
|
||||
Meshenger is a Forban-inspired messaging software used for a speculative broadcast communication project. The starting point is an electronic messaging system running on a wireless mesh network. The messages propagate through the network when devices that come in contact with each other synchronize their content. It is non-hierarchical, every node receives, relays and broadcasts messages.
|
||||
@ -26,18 +26,6 @@ You are going to need to have an internet connection to your router, the easiest
|
||||
|
||||
Alternatively if you use OSX you can enable internet sharing (make sure to set your OSX machine as the gateway and DNS server for your router in /etc/config/network)
|
||||
|
||||
*If after flashing something goes wrong, you can reset the whole router in failsafe mode* see [here](http://wiki.openwrt.org/doc/howto/generic.failsafe).
|
||||
|
||||
### Lazy Install
|
||||
|
||||
Below you will find the manual configuration steps necessary to install Meshenger. We did however create some scripts to help you automate the process.
|
||||
|
||||
Read more [here](https://github.com/rscmbbng/meshenger/blob/master/lazyinstall/README.md)
|
||||
|
||||
To add: stop Luci from running!
|
||||
`$ /etc/init.d/uhttpd disable`
|
||||
Saves a lot of resources!
|
||||
|
||||
### System configuration
|
||||
|
||||
To use your router for Meshenger you're going to need to run the whole filesystem from a USB-Drive.
|
||||
@ -98,7 +86,7 @@ Edit fstab again now make the swap look like this:
|
||||
```
|
||||
config mount
|
||||
option device /dev/sda2
|
||||
option enabled
|
||||
option enabled
|
||||
```
|
||||
### Wireless configuration
|
||||
|
||||
@ -286,40 +274,8 @@ Restart dnsmasq to apply the changes:
|
||||
`$ /etc/init.d/dnsmasq restart`
|
||||
|
||||
|
||||
Now all http requests will be directed to Meshenger! If it doesn't work, check your DNS settings of the client your're using (aka your computer, phone, fapfapfaplet). Make sure google's 8.8.8.8 is not there and set 192.168.2.1 as the dns server.
|
||||
Now all http requests will be directed to Meshenger!
|
||||
|
||||
### Run meshenger on boot
|
||||
|
||||
Create a file in `$ /etc/init.d/` called meshenger and paste the script below:
|
||||
|
||||
```
|
||||
#!/bin/sh /etc/rc.common
|
||||
#meshenger startup script
|
||||
|
||||
START=101
|
||||
#STOP=15
|
||||
|
||||
start() {
|
||||
echo 'starting Meshenger'
|
||||
/usr/bin/python /root/meshenger/main.py &
|
||||
}
|
||||
|
||||
stop() {
|
||||
echo 'Killing Meshenger'
|
||||
killall python
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
Make the file executable
|
||||
|
||||
`$ chmod a+x /etc/init.d/meshenger`
|
||||
|
||||
Now you can start/stop meshenger as a service, to enable the meshenger as srevice on boot run
|
||||
|
||||
`$ /etc/init.d/meshenger enable`
|
||||
|
||||
To start/stop/disable replace 'enable' with start, stop or disable.
|
||||
|
||||
|
||||
### Installing meshenger
|
||||
@ -329,173 +285,3 @@ Get the dependencies and clone the git
|
||||
`$ opkg install python git `
|
||||
|
||||
`$ git clone git://github.com/jngrt/meshenger.git `
|
||||
|
||||
|
||||
### Running Meshenger on Boot
|
||||
|
||||
To launch the Meshenger script (or python script in this case), we have to run it as a 'Service'.
|
||||
Befor we can do so we need to know some variable.
|
||||
|
||||
#### Path to python
|
||||
Find out where your Python binary is located:
|
||||
|
||||
```$ which python```
|
||||
|
||||
This command outputs your path, for example: ` /usr/bin/python`, remember this path
|
||||
|
||||
#### Boot order
|
||||
Alot of processes are started at boot, and we want to make sure our script runs after the system has booted completely. To find out the boot order, look in the rc.d folder:
|
||||
|
||||
```$ ls /etc/rc.d```
|
||||
|
||||
This will output a list of startup sctipts with prefixes like S10-, S20-, S30-. The numbers define the boot order, the higher, the later. Remember the highest 'S'(cript) number. We need to run our script after the last one.
|
||||
|
||||
|
||||
#### Startup script
|
||||
Create a new file in `/etc/init.d/`
|
||||
|
||||
```$ vi /etc/init.d/meshenger```
|
||||
|
||||
And paste the script below and correct your path to pyton and boot order number, both found above.
|
||||
|
||||
```
|
||||
#!/bin/sh /etc/rc.common
|
||||
#meshenger startup script
|
||||
|
||||
START=99 #the boot order number, note in our case the SAME number as the highest one found.
|
||||
SERVICE_DAEMONIZE=1 #start as service, important!
|
||||
|
||||
start(){
|
||||
sleep 10 #make sure booting is finished
|
||||
echo 'starting meshenger'
|
||||
/usr/bin/python /root/meshenger/main.py & #path to python binary, space, full path to your script
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
stop(){
|
||||
echo 'stopping meshenger'
|
||||
killall python
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
Make sure both your script (main.py) and the init.d script are executable!
|
||||
|
||||
```$ chmod +x /etc/init.d/meshenger```
|
||||
```$ chmod +x /root/meshenger/main.py```
|
||||
|
||||
|
||||
|
||||
#### Enabling the service
|
||||
Now we have to activate the script we just pasted and make it run as service, on every (re)boot.
|
||||
|
||||
```$ /etc/init.d/meshenger enable```
|
||||
|
||||
This creates a symlink in `/etc/rc.d` with the boot order number prefix you provided in the init.d script (S99-meshenger). You can also start and stop the service manually with:
|
||||
|
||||
```$ /etc/init.d/meshenger start```
|
||||
```$ /etc/init.d/meshenger stop```
|
||||
|
||||
That's all, reboot and see if it works ( `$ ps | grep python` )!
|
||||
|
||||
|
||||
|
||||
### SSH Access from Internet (wan)
|
||||
|
||||
If you want, you can hook your box up to the internet and manage it remotely. This is not in the scope of this project but I'll share the steps with you:
|
||||
|
||||
#### Configure your firewall
|
||||
|
||||
|
||||
```
|
||||
$ vi /etc/config/firewall
|
||||
|
||||
|
||||
config 'rule'
|
||||
option 'src' 'wan'
|
||||
option 'dest_port' '22'
|
||||
option 'target' 'ACCEPT'
|
||||
option 'proto' 'tcp'
|
||||
|
||||
|
||||
$ /etc/init.d/firewall restart
|
||||
|
||||
```
|
||||
|
||||
Enable user iptable script:
|
||||
|
||||
|
||||
```
|
||||
$ vi vi /etc/firewall.user
|
||||
|
||||
|
||||
iptables -t nat -A prerouting_wan -p tcp --dport 22 -j ACCEPT
|
||||
iptables -A input_wan -p tcp --dport 22 -j ACCEPT
|
||||
|
||||
|
||||
|
||||
$ /etc/init.d/firewall restart
|
||||
|
||||
```
|
||||
|
||||
|
||||
#### Configure open ssh service
|
||||
|
||||
Add the line below in your dropbear config
|
||||
|
||||
```
|
||||
$ vi /etc/config/dropbear
|
||||
|
||||
|
||||
option 'GatewayPorts' 'on'
|
||||
|
||||
|
||||
$ /etc/init.d/dropbear restart
|
||||
|
||||
```
|
||||
Now you can acces your router, via ssh, from the internet. Don't forget to add portforwarding on you're modem/router!
|
||||
Next up, http access from the internet!
|
||||
|
||||
|
||||
### HTTP Access from Internet (wan)
|
||||
|
||||
If you want, you can host Meshenger or your own homepage on the internet. This is not in the scope of this project but I'll share the steps with you:
|
||||
|
||||
#### Configure your firewall
|
||||
|
||||
Add the following lines in your firewall config, and restart the firewall:
|
||||
|
||||
|
||||
```
|
||||
$ vi /etc/config/firewall
|
||||
|
||||
|
||||
|
||||
config 'redirect'
|
||||
option 'src' 'wan'
|
||||
option 'src_dport' '80'
|
||||
option 'dest' 'lan'
|
||||
option 'dest_ip' '192.168.1.1'
|
||||
option 'dest_port' '80'
|
||||
option 'proto' 'tcp'
|
||||
|
||||
config 'rule'
|
||||
option 'src' 'wan'
|
||||
option 'dest_port' '80'
|
||||
option 'target' 'ACCEPT'
|
||||
option 'proto' 'tcp'
|
||||
|
||||
|
||||
|
||||
$ /etc/init.d/firewall restart
|
||||
```
|
||||
|
||||
Now either run Meshenger, or run a simple http server (to share files, or whatever) from any directory with this Python oneliner:
|
||||
|
||||
``` python -m SimpleHTTPServer 80 ```
|
||||
|
||||
|
||||
After forwarding the correct ports on your home router/modem (from any ip on port 80 to your openwrt box (lan) ip on port 80) your website will now be accessible from outside (wan) through your external IP!
|
||||
|
||||
|
@ -1,46 +1,3 @@
|
||||
This is a very primitive script to automate the openwrt setup.
|
||||
It's probably best not used at this point.
|
||||
|
||||
However if you choose to use it make sure you have the following things set up:
|
||||
|
||||
- The OpenWRT router has a connection to the internet (by attaching it to another router via ethernet)
|
||||
- You have a properly formatted usb drive (one ext4 partition, one 32mb swap partiton)
|
||||
- Make sure the usb flash drive is inserted in the router
|
||||
|
||||
The lazy install scripts works by moving config files from this folder to replace the ones on the target router's filesystem.
|
||||
Therefore you need to edit the following files to suit your own needs:
|
||||
|
||||
network file:
|
||||
lines 11 and 13, the ip adress you want to use plus the ip adress of the gateway that provides this router with its internet connection.
|
||||
|
||||
wireless file:
|
||||
line 11,the wireless SSID that the router will get
|
||||
|
||||
Once you've set all of this up copy the lazyinstall directory to your open-wrt router:
|
||||
|
||||
scp -r /path/to/meshenger/lazyinstall/ root@target.router.ip.adress:~/
|
||||
|
||||
ssh into the target router:
|
||||
|
||||
ssh root@target.router.ip.address
|
||||
|
||||
navigate to where you copied the lazyinstall directory:
|
||||
cd ~/lazyinstall
|
||||
|
||||
first execute lazyinstall1, it will set up the ext_root on ths usb drive
|
||||
|
||||
./lazyinstall1.sh
|
||||
|
||||
once that's done reboot the router:
|
||||
|
||||
reboot -f
|
||||
|
||||
once it is up again, ssh back in. the router has now booted from the external usb drive. you can verify that by doing:
|
||||
|
||||
df -h
|
||||
|
||||
which should show you that rootfs is the size of your external usb drive
|
||||
|
||||
run lazyinstall2. this will copy all the config files, download python and git and clone the meshenger project. This can take a while.
|
||||
|
||||
After this is done you can reboot and you will have a fully functioning meshenger node!
|
||||
|
@ -1,38 +0,0 @@
|
||||
config dnsmasq
|
||||
option domainneeded 1
|
||||
option boguspriv 1
|
||||
option filterwin2k 0 # enable for dial on demand
|
||||
option localise_queries 1
|
||||
option rebind_protection 1 # disable if upstream must serve RFC1918 addresses
|
||||
option rebind_localhost 1 # enable for RBL checking and similar services
|
||||
#list rebind_domain example.lan # whitelist RFC1918 responses for domains
|
||||
option local '/lan/'
|
||||
option domain 'lan'
|
||||
option expandhosts 1
|
||||
option nonegcache 0
|
||||
option authoritative 1
|
||||
option readethers 1
|
||||
option leasefile '/tmp/dhcp.leases'
|
||||
option resolvfile '/tmp/resolv.conf.auto'
|
||||
#list server '/mycompany.local/1.2.3.4'
|
||||
#option nonwildcard 1
|
||||
#list interface br-lan
|
||||
#list notinterface lo
|
||||
#list bogusnxdomain '64.94.110.11'
|
||||
list server '//192.168.2.1'
|
||||
|
||||
config dhcp lan
|
||||
option interface lan
|
||||
option start 100
|
||||
option limit 150
|
||||
option leasetime 12h
|
||||
|
||||
config dhcp wan
|
||||
option interface wan
|
||||
option ignore 1
|
||||
|
||||
config dhcp hotspot
|
||||
option 'interface' 'hotspot'
|
||||
option 'start' '100'
|
||||
option 'limit' '150'
|
||||
option 'dynamicdhcp' '1'
|
@ -1,34 +0,0 @@
|
||||
config 'zone'
|
||||
option 'name' 'hotspot'
|
||||
option 'input' 'ACCEPT'
|
||||
option 'forward' 'ACCEPT' #was REJECT
|
||||
option 'output' 'ACCEPT'
|
||||
|
||||
config 'rule'
|
||||
|
||||
option 'src' 'hotspot'
|
||||
option 'dest_port' '53'
|
||||
option 'proto' 'tcpudp'
|
||||
option 'target' 'ACCEPT'
|
||||
|
||||
config 'rule'
|
||||
option 'src' 'hotspot'
|
||||
option 'src_port' '67-68'
|
||||
option 'dest_port' '67-68'
|
||||
option 'proto' 'udp'
|
||||
option 'target' 'ACCEPT'
|
||||
|
||||
config 'rule'
|
||||
option 'target' 'ACCEPT'
|
||||
option 'src' 'hotspot' # guest wifi interface
|
||||
option 'proto' 'tcp'
|
||||
option '_name' 'Website' # this can maybe go?
|
||||
option 'dest_port' '80'
|
||||
|
||||
config 'redirect'
|
||||
option 'proto' 'tcp'
|
||||
option '_name' 'Website' # this can maybe go?
|
||||
option 'src' 'hotspot' # guest wifi interface
|
||||
option 'src_dport' '80'
|
||||
option 'dest_port' '80'
|
||||
option 'dest_ip' '192.168.2.1' # ip of webserver
|
@ -17,7 +17,7 @@ mount -t ext4 /dev/sda1 /mnt/sda1
|
||||
|
||||
echo 'Cleaning USB drive'
|
||||
|
||||
rm -r /mnt/sda1/*
|
||||
rm -r /mnt/sda1/
|
||||
|
||||
sleep 4
|
||||
|
||||
@ -25,7 +25,7 @@ echo 'Copying filesystem to USB drive'
|
||||
mkdir -p /tmp/cproot
|
||||
mount --bind / /tmp/cproot
|
||||
sleep 1
|
||||
tar -C /tmp/cproot -cvf - . | tar -C /mnt/sda1/ -xf -
|
||||
tar -C /tmp/cproot -cvf - . | tar -C /mnt/sda1 -xf -
|
||||
sleep 1
|
||||
umount /tmp/cproot
|
||||
|
||||
|
@ -10,18 +10,10 @@ swapon /dev/sda2
|
||||
|
||||
mv fstab_extroot /etc/config/fstab
|
||||
|
||||
echo 'Configuring wireless and hotspot'
|
||||
|
||||
head -n 10 /etc/config/wireless >> tmp_wireless
|
||||
cat wireless >> tmp_wireless
|
||||
mv tmp_wireless /etc/config/wireless
|
||||
|
||||
cat firewall >>ls /etc/config/firewall
|
||||
|
||||
mv dhcp /etc/config/dhcp
|
||||
|
||||
echo "address=/#/192.168.2.1 " >> /etc/dnsmasq.conf
|
||||
|
||||
sleep 1
|
||||
|
||||
mv network /etc/config/network
|
||||
@ -32,13 +24,10 @@ sed -i -e "s/option 'interfaces' 'mesh'/option 'interfaces' 'adhoc0'/g" /etc/con
|
||||
|
||||
opkg install python git
|
||||
sleep 1
|
||||
git clone git://github.com/rscmbbng/meshenger /root/meshenger
|
||||
git clone git://github.com/jngrt/meshenger.git
|
||||
|
||||
mv uhttpd /etc/config/uhttpd
|
||||
|
||||
mv meshenger /etc/init.d/meshenger
|
||||
/etc/init.d/meshenger enable
|
||||
|
||||
echo 'my ip address is:' #klopt nog niet
|
||||
ifconfig br-lan | grep 'inet addr'
|
||||
|
||||
|
@ -1,15 +0,0 @@
|
||||
#!/bin/sh /etc/rc.common
|
||||
#meshenger startup script
|
||||
|
||||
START=101
|
||||
#STOP=15
|
||||
|
||||
start() {
|
||||
echo 'starting Meshenger'
|
||||
/usr/bin/python /root/meshenger/main.py &
|
||||
}
|
||||
|
||||
stop() {
|
||||
echo 'Killing Meshenger'
|
||||
killall python
|
||||
}
|
@ -18,9 +18,3 @@ config interface 'mesh'
|
||||
option mtu '1528'
|
||||
option proto 'none'
|
||||
|
||||
config interface 'hotspot'
|
||||
option 'iface' 'radio0' #use your existing wifi device (look in config/wireless below)
|
||||
option 'proto' 'static'
|
||||
option 'ipaddr' '192.168.2.1'
|
||||
option 'netmask' '255.255.255.0'
|
||||
option 'type' 'bridge'
|
@ -1,15 +1,8 @@
|
||||
|
||||
config wifi-iface 'wmesh'
|
||||
option device 'radio0'
|
||||
option ifname 'adhoc0'
|
||||
option network 'mesh'
|
||||
option mode 'adhoc'
|
||||
option ssid 'mesh'
|
||||
option bssid '66:66:66:66:66:66'
|
||||
|
||||
config wifi-iface
|
||||
option 'device' 'radio0' #use your existing wifi device, look in the list above.
|
||||
option 'ssid' 'meshenger_node' #use a unique name for your network?
|
||||
option 'network' 'hotspot'
|
||||
option 'mode' 'ap'
|
||||
option 'encryption' 'none'
|
||||
option 'isolate' '1'
|
||||
option bssid '66:66:66:66:66:66'
|
4
log/.gitignore
vendored
@ -1,4 +0,0 @@
|
||||
# Ignore everything in this directory
|
||||
*
|
||||
# Except this file
|
||||
!.gitignore
|
262
main.py
@ -1,39 +1,25 @@
|
||||
#!/usr/bin/python
|
||||
|
||||
import socket, os, time, select, urllib, sys, threading, json, logging, logging.config
|
||||
|
||||
os.chdir(os.path.dirname(__file__)) # change present working directory to the one where this file is
|
||||
|
||||
logging.config.fileConfig('pylog.conf')
|
||||
logger = logging.getLogger('meshenger'+'.main')
|
||||
import socket, os, time, select, urllib, sys, threading, subprocess
|
||||
|
||||
class Meshenger:
|
||||
devices = {} #the dictionary of all the nodes this this node has seen. each ip-entry has a tuple of 'foreign index update time' and time when last seen
|
||||
devices_old = {}
|
||||
devices = {} #the dictionary of all the nodes this this node has seen
|
||||
serve_port = "13338"
|
||||
announce_port = 13337
|
||||
#own_ip = "0.0.0.0"
|
||||
msg_dir = os.path.relpath('msg/')
|
||||
exitapp = False #to kill all threads on
|
||||
index_last_update = str(int(time.time()))
|
||||
node_expiry = 15 #the time to wait before removing a node form the discovered nodelist
|
||||
index_last_update = "0" #str(int(time.time()))
|
||||
|
||||
def __init__(self):
|
||||
|
||||
os.system("echo 1 >> /proc/sys/net/ipv6/conf/br-lan/disable_ipv6")
|
||||
os.system("echo 1 >> /proc/sys/net/ipv6/conf/br-hotspot/disable_ipv6")
|
||||
|
||||
|
||||
self.own_ip = self.get_ip_adress().strip()
|
||||
# this hash is needed in clientserve, so client can generate color
|
||||
self.own_hash = self.hasj(self.own_ip)
|
||||
self.own_ip = self.get_ip_adress()
|
||||
|
||||
if not os.path.exists(self.msg_dir):
|
||||
os.mkdir(self.msg_dir)
|
||||
logger.info('Making message directory')
|
||||
|
||||
self.init_index()
|
||||
self.make_alias()
|
||||
print 'Making message directory'
|
||||
|
||||
try:
|
||||
d = threading.Thread(target=self.discover)
|
||||
@ -52,66 +38,63 @@ class Meshenger:
|
||||
c.daemon = True
|
||||
c.start()
|
||||
|
||||
b = threading.Thread(target=self.build_index)
|
||||
b.daemon = True
|
||||
b.start()
|
||||
|
||||
#os.system("python meshenger_clientserve.py")
|
||||
|
||||
except (KeyboardInterrupt, SystemExit):
|
||||
logger.info('exiting discovery thread')
|
||||
print 'exiting discovery thread'
|
||||
d.join()
|
||||
a.join()
|
||||
b.join()
|
||||
n.join()
|
||||
c.join()
|
||||
sys.exit()
|
||||
except Exception as e:
|
||||
logger.warning( 'Main __init__ thread exception: %s', repr(e) )
|
||||
except:
|
||||
logger.warning( 'Main __init__ unknown thread exception')
|
||||
|
||||
while True:
|
||||
logger.debug('Entering main loop')
|
||||
print 'Entering main loop'
|
||||
#
|
||||
if len(self.devices) > 0:
|
||||
logger.info('found %s device(s)', len(self.devices))
|
||||
print 'found', len(self.devices),'device(s)'
|
||||
|
||||
for device in self.devices.keys():
|
||||
nodehash = self.hasj(device)
|
||||
nodepath = os.path.join(os.path.abspath('nodes'), nodehash)
|
||||
nodeupdatepath = os.path.join(nodepath, 'lastupdate')
|
||||
nodepath = self.ip_to_hash_path(device) #make a folder for the node (nodes/'hash'/)
|
||||
nodeupdatepath = os.path.join(self.ip_to_hash_path(device), 'lastupdate')
|
||||
|
||||
logger.info('Checking age of foreign node index')
|
||||
logger.info('%s Foreign announce timestamp', self.devices[device])
|
||||
|
||||
print 'Checking age of foreign node index'
|
||||
print self.devices[device], 'Foreign announce timestamp'
|
||||
try:
|
||||
foreign_node_update = open(nodeupdatepath).read()
|
||||
except:
|
||||
foreign_node_update = 0 #means it was never seen before
|
||||
|
||||
logger.info('%s Locally stored timestamp for device', foreign_node_update)
|
||||
print foreign_node_update, 'Locally stored timestamp for device'
|
||||
|
||||
|
||||
if self.devices[device] > foreign_node_update:
|
||||
logger.info('Foreign node"s index is newer, proceed to download index')
|
||||
self.get_index(device, nodepath)
|
||||
logger.info('downloading messages')
|
||||
self.get_messages(device, nodepath, nodehash)
|
||||
self.build_index()
|
||||
self.node_timestamp(device)
|
||||
print 'Foreign node"s index is newer, proceed to download index'
|
||||
if not self.get_index(device, nodepath):
|
||||
print 'index wget failed'
|
||||
continue
|
||||
|
||||
self.devices_old = dict(self.devices)
|
||||
print 'downloading messages'
|
||||
if not self.get_messages(device, nodepath):
|
||||
print 'getting messages failed'
|
||||
continue
|
||||
|
||||
#check to see if a node has been missing for a while, if so remove it from self.devices
|
||||
for device in self.devices_old.keys():
|
||||
update_time = int(self.devices[device][1])
|
||||
time_delta = int(time.time())- update_time
|
||||
if time_delta >= self.node_expiry:
|
||||
logger.info('Node '+device+' missing for '+str(self.node_expiry)+' seconds. Removing from list')
|
||||
del self.devices[device]
|
||||
print 'updating node timestamp'
|
||||
self.node_timestamp(device)
|
||||
|
||||
time.sleep(5) #free process or ctrl+c
|
||||
|
||||
|
||||
def node_timestamp(self, ip):
|
||||
nodepath = os.path.abspath(os.path.join('nodes', self.hasj(ip)))
|
||||
updatepath = os.path.join(nodepath, 'lastupdate')
|
||||
with open(updatepath, 'wb') as lastupdate:
|
||||
lastupdate.write(self.devices[ip][0])
|
||||
lastupdate.write(self.devices[ip])
|
||||
#return updatepath
|
||||
|
||||
|
||||
@ -120,22 +103,19 @@ class Meshenger:
|
||||
"""
|
||||
Announce the node's existance to other nodes
|
||||
"""
|
||||
logger.info('Announcing')
|
||||
print 'Announcing'
|
||||
while not self.exitapp:
|
||||
try:
|
||||
sock = socket.socket(socket.AF_INET6, socket.SOCK_DGRAM, socket.IPPROTO_UDP)
|
||||
sock.sendto(self.index_last_update, ("ff02::1", self.announce_port))
|
||||
#sock.close()
|
||||
time.sleep(5)
|
||||
except:
|
||||
logger.warning('Failed to announce myself!')
|
||||
sock = socket.socket(socket.AF_INET6, socket.SOCK_DGRAM, socket.IPPROTO_UDP)
|
||||
sock.sendto(self.index_last_update, ("ff02::1", self.announce_port))
|
||||
sock.close()
|
||||
time.sleep(5)
|
||||
|
||||
def discover(self):
|
||||
"""
|
||||
Discover other devices by listening to the Meshenger announce port
|
||||
"""
|
||||
|
||||
logger.info('Discovering')
|
||||
print 'Discovering'
|
||||
bufferSize = 1024 # whatever you need?
|
||||
|
||||
s = socket.socket(socket.AF_INET6, socket.SOCK_DGRAM, socket.IPPROTO_UDP)
|
||||
@ -144,19 +124,19 @@ Discover other devices by listening to the Meshenger announce port
|
||||
while not self.exitapp:
|
||||
result = select.select([s],[],[])[0][0].recvfrom(bufferSize)
|
||||
ip = result[1][0]
|
||||
#logger.debug('%s %s', ip, 'discovered')
|
||||
print ip, "*"*45
|
||||
node_path = os.path.join(os.path.abspath('nodes'), self.hasj(ip))
|
||||
announce_time = str(int(time.time()))
|
||||
|
||||
if not os.path.exists(node_path) and ip != self.own_ip:
|
||||
#loop for first time
|
||||
#loop for first timef
|
||||
self.ip_to_hash_path(ip) #make a folder /nodes/hash
|
||||
self.devices[ip] = result[0], announce_time
|
||||
self.devices[ip] = result[0]
|
||||
#self.node_timestamp(ip) #make a local copy of the timestamp in /nodes/hash/updatetimestamp
|
||||
logger.info('New node %s', ip)
|
||||
print 'New node', ip
|
||||
|
||||
elif os.path.exists(node_path) and ip != self.own_ip:
|
||||
logger.info('Known node %s', ip)
|
||||
self.devices[ip] = result[0], announce_time
|
||||
print 'Known node', ip
|
||||
self.devices[ip] = result[0]
|
||||
|
||||
|
||||
time.sleep(1)
|
||||
@ -165,7 +145,7 @@ Discover other devices by listening to the Meshenger announce port
|
||||
"""
|
||||
Initialize the nodeserver
|
||||
"""
|
||||
logger.info('Serving to nodes')
|
||||
print 'Serving to nodes'
|
||||
import meshenger_nodeserve
|
||||
meshenger_nodeserve.main()
|
||||
|
||||
@ -173,91 +153,119 @@ Initialize the nodeserver
|
||||
"""
|
||||
Initialize the clientserver
|
||||
"""
|
||||
logger.info('Serving to client')
|
||||
print 'Serving to client'
|
||||
import meshenger_clientserve
|
||||
# set a reference to this object
|
||||
meshenger_clientserve.meshenger = self
|
||||
meshenger_clientserve.main()
|
||||
# meshenger_clientserve.build_index_callback = self.build_index
|
||||
|
||||
def init_index(self):
|
||||
|
||||
def build_index(self):
|
||||
"""
|
||||
Initialize the index. Read from disk or create new.
|
||||
Make an index file of all the messages present on the node.
|
||||
Save the time of the last update.
|
||||
"""
|
||||
logger.info('Checking own index for the first time\n')
|
||||
|
||||
|
||||
index_file = os.path.relpath( 'index' )
|
||||
previous_index = []
|
||||
if not os.path.exists( index_file ):
|
||||
with open('index','wb') as index:
|
||||
index.write('')
|
||||
else:
|
||||
previous_index = open( index_file ).read().split()
|
||||
|
||||
index_last_update_file = os.path.relpath( 'index_last_update' )
|
||||
if os.path.exists( index_last_update_file ):
|
||||
self.index_last_update = open( index_last_update_file ).read()
|
||||
|
||||
while not self.exitapp:
|
||||
|
||||
current_index = []
|
||||
for root, folders, files in os.walk( self.msg_dir ):
|
||||
if root == 'msg':
|
||||
folders.sort()
|
||||
else:
|
||||
files.sort()
|
||||
current_index += [root + '/' + f for f in files]
|
||||
|
||||
if current_index != previous_index:
|
||||
with open( index_file, 'w' ) as f:
|
||||
f.write( '\n'.join( current_index ))
|
||||
|
||||
self.index_last_update = str( int( time.time()))
|
||||
print 'Index updated:', current_index
|
||||
|
||||
with open( os.path.relpath('index_last_update'), 'w') as f:
|
||||
f.write( self.index_last_update )
|
||||
|
||||
previous_index = current_index
|
||||
|
||||
time.sleep( 5 )
|
||||
|
||||
"""
|
||||
print 'Building own index for the first time\n'
|
||||
|
||||
if not os.path.exists('index'):
|
||||
with open('index','wb') as index:
|
||||
index.write('')
|
||||
self.previous_index = []
|
||||
previous_index = []
|
||||
else:
|
||||
self.previous_index = open('index').readlines()
|
||||
self.build_index()
|
||||
previous_index = open('index').readlines()
|
||||
|
||||
def build_index(self):
|
||||
"""
|
||||
Make an index file of all the messages present on the node.
|
||||
Save the time of the last update.
|
||||
"""
|
||||
logger.debug('build_index')
|
||||
current_index = os.listdir(self.msg_dir)
|
||||
if current_index != self.previous_index:
|
||||
with open('index', 'wb') as index:
|
||||
for message in os.listdir(self.msg_dir):
|
||||
index.write(message)
|
||||
index.write('\n')
|
||||
self.index_last_update = str(int(time.time()))
|
||||
while not self.exitapp:
|
||||
current_index = os.listdir(self.msg_dir)
|
||||
if current_index != previous_index:
|
||||
with open('index', 'wb') as index:
|
||||
for message in os.listdir(self.msg_dir):
|
||||
index.write(message)
|
||||
index.write('\n')
|
||||
self.index_last_update = str(int(time.time()))
|
||||
|
||||
logger.info('Index updated: %s', current_index)
|
||||
|
||||
with open('index_last_update', 'wb') as indexupdate:
|
||||
indexupdate.write(self.index_last_update) ### misschien moet dit index_last_update zijn
|
||||
|
||||
self.previous_index = current_index
|
||||
|
||||
def make_alias(self):
|
||||
"""
|
||||
See if the node already has an alias (nickname) if not just use the IP-Hash
|
||||
"""
|
||||
if not os.path.exists('alias'):
|
||||
with open('alias','wb') as alias:
|
||||
self.alias = self.own_hash
|
||||
alias.write(self.own_hash)
|
||||
else:
|
||||
self.alias = open('alias','rb').read().replace('\n','') #should we replace the newline?
|
||||
pass
|
||||
logger.debug('Alias is "'+self.alias+'"')
|
||||
print 'Index updated:', current_index
|
||||
with open('index_last_update', 'wb') as indexupdate:
|
||||
indexupdate.write(self.index_last_update) ### misschien moet dit index_last_update zijn
|
||||
|
||||
previous_index = current_index
|
||||
time.sleep(5)
|
||||
"""
|
||||
|
||||
def get_index(self,ip, path):
|
||||
"""
|
||||
Download the indices from other nodes.
|
||||
"""
|
||||
Download the indices from other nodes.
|
||||
"""
|
||||
# time.sleep(0) # hack to prevent wget bug
|
||||
# os.system('wget http://['+ip+'%adhoc0]:'+self.serve_port+'/index -O '+os.path.join(path,'index'))
|
||||
command = 'wget http://['+ip+'%adhoc0]:'+self.serve_port+'/index -O '+os.path.join(path,'index')
|
||||
print 'get_index: ', command
|
||||
status = subprocess.call( command, shell=True )
|
||||
return status == 0
|
||||
|
||||
os.system('wget http://['+ip+'%adhoc0]:'+self.serve_port+'/index -O '+os.path.join(path,'index'))
|
||||
|
||||
|
||||
def get_messages(self, ip, path, hash):
|
||||
def get_messages(self, ip, path ):
|
||||
"""
|
||||
Get new messages from other node based on it's index file
|
||||
"""
|
||||
|
||||
|
||||
try:
|
||||
with open(os.path.join(path,'index')) as index:
|
||||
index = index.read().split('\n')
|
||||
for message in index:
|
||||
messagepath = os.path.join(os.path.abspath(self.msg_dir), message)
|
||||
parts = message.split('/')
|
||||
dirpath = os.path.join( parts[0], parts[1])
|
||||
if not os.path.isdir( dirpath ):
|
||||
os.mkdir( dirpath )
|
||||
|
||||
messagepath = os.path.relpath( message )
|
||||
if not os.path.exists(messagepath):
|
||||
logger.info('downloading %s to %s', message, messagepath)
|
||||
os.system('wget http://['+ip+'%adhoc0]:'+self.serve_port+'/msg/'+message+' -O '+messagepath)
|
||||
with open(messagepath, 'r+') as f:
|
||||
data=json.load(f)
|
||||
data['hops']=str(int(data['hops'])+1)
|
||||
data['node']=hash
|
||||
f.seek(0)
|
||||
json.dump(data, f)
|
||||
print 'downloading', message, 'to', messagepath
|
||||
command = 'wget http://['+ip+'%adhoc0]:' + self.serve_port + '/' + message+' -O ' + messagepath
|
||||
status = subprocess.call( command, shell=True)
|
||||
if status != 0:
|
||||
return False
|
||||
# succesfuly downloaded all messages, return true
|
||||
return True
|
||||
except:
|
||||
logger.info('Failed to download messages')
|
||||
pass
|
||||
print 'Failed to download messages'
|
||||
return False
|
||||
|
||||
def ip_to_hash_path(self, ip):
|
||||
"""
|
||||
@ -272,7 +280,6 @@ Convert a node's ip into a hash and make a directory to store it's files
|
||||
|
||||
return nodepath
|
||||
|
||||
|
||||
def hasj(self, ip):
|
||||
"""
|
||||
Convert a node's ip into a hash
|
||||
@ -285,16 +292,15 @@ Convert a node's ip into a hash and make a directory to store it's files
|
||||
"""
|
||||
Hack to adhoc0's inet6 adress
|
||||
"""
|
||||
#TODO possibly check if it's an empty file
|
||||
if not os.path.isfile('interfaceip6adress'):
|
||||
os.system('ifconfig -a adhoc0 | grep inet6 > /root/meshenger/interfaceip6adress')
|
||||
with open('/root/meshenger/interfaceip6adress', 'r') as a:
|
||||
with open('interfaceip6adress', 'r') as a:
|
||||
return a.read().split()[2].split('/')[0]
|
||||
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
logger.info("starting main...")
|
||||
print "test"
|
||||
try:
|
||||
meshenger = Meshenger()
|
||||
except (KeyboardInterrupt, SystemExit):
|
||||
|
@ -8,73 +8,42 @@ from BaseHTTPServer import HTTPServer
|
||||
import SimpleHTTPServer
|
||||
import urlparse
|
||||
import unicodedata
|
||||
import logging, logging.config
|
||||
|
||||
logging.config.fileConfig('pylog.conf')
|
||||
logger = logging.getLogger('meshenger'+'.clientserve')
|
||||
|
||||
class ClientServeHandler(SimpleHTTPServer.SimpleHTTPRequestHandler):
|
||||
|
||||
messageDir = "msg"
|
||||
|
||||
"""
|
||||
Serve index and messages
|
||||
"""
|
||||
def do_GET(self):
|
||||
"""
|
||||
Serve index and messages
|
||||
"""
|
||||
|
||||
if self.path == '/index' or self.path.startswith( "/"+self.messageDir ):
|
||||
if self.path.startswith( '/'+self.messageDir ):
|
||||
parts = self.path.split('/');
|
||||
if len( parts ) == 2:
|
||||
|
||||
self.wfile.write('give index')
|
||||
elif len( parts ) == 3:
|
||||
self.wfile.write('give msg')
|
||||
elif self.path == '/index' or self.path.startswith( "/"+self.messageDir ):
|
||||
return SimpleHTTPServer.SimpleHTTPRequestHandler.do_GET(self)
|
||||
elif self.path == '/id':
|
||||
if meshenger and meshenger.own_hash:
|
||||
self.send_response(200)
|
||||
self.send_header('Content-type', 'text-html')
|
||||
self.end_headers()
|
||||
self.wfile.write(meshenger.own_hash)
|
||||
else:
|
||||
self.send_error(404,'Id not yet available')
|
||||
elif self.path == '/alias':
|
||||
if meshenger and meshenger.alias:
|
||||
self.send_response(200)
|
||||
self.send_header('Content-type', 'text-html')
|
||||
self.end_headers()
|
||||
self.wfile.write(meshenger.alias)
|
||||
else:
|
||||
self.send_error(404,'Alias not yet available')
|
||||
|
||||
elif self.path == '/log':
|
||||
self.send_response(200)
|
||||
self.send_header('Content-type', 'text-html')
|
||||
self.end_headers()
|
||||
f = os.path.relpath('log/meshenger.log')
|
||||
with open( f, 'r') as the_file:
|
||||
self.wfile.write(the_file.read())
|
||||
elif self.path.startswith('/web'):
|
||||
|
||||
f = os.path.relpath(self.path[1:])
|
||||
if not os.path.exists( f ) or os.path.isdir( f ):
|
||||
f = os.path.join('web', 'index.html')
|
||||
|
||||
self.path = '/'+f
|
||||
return SimpleHTTPServer.SimpleHTTPRequestHandler.do_GET(self)
|
||||
elif self.path == '/old':
|
||||
else:
|
||||
self.send_response(200) #serve the webapp on every url requested
|
||||
self.send_header('Content-type', 'text/html')
|
||||
self.end_headers()
|
||||
f = os.path.relpath( 'webapp.html')
|
||||
f = os.path.join( "webapp.html")
|
||||
with open( f, 'r') as the_file:
|
||||
self.wfile.write(the_file.read())
|
||||
|
||||
else:
|
||||
self.path = '/' + os.path.join('web', 'index.html')
|
||||
return SimpleHTTPServer.SimpleHTTPRequestHandler.do_GET(self)
|
||||
|
||||
|
||||
"""
|
||||
Allow clients to post messages
|
||||
"""
|
||||
def do_POST(self):
|
||||
"""
|
||||
Allow clients to post messages
|
||||
"""
|
||||
if self.path == '/send':
|
||||
logger.info('incoming message from client!')
|
||||
|
||||
length = int(self.headers['Content-Length'])
|
||||
post_data = urlparse.parse_qs(self.rfile.read(length).decode('utf-8'))
|
||||
|
||||
@ -85,18 +54,8 @@ Allow clients to post messages
|
||||
self.end_headers()
|
||||
self.wfile.write('message created')
|
||||
|
||||
#let main rebuild message index
|
||||
try:
|
||||
logger.debug('try to call meshenger.build_index: %s', repr(meshenger))
|
||||
meshenger.build_index()
|
||||
except:
|
||||
logger.error('failed to call meshenger.build_index')
|
||||
pass
|
||||
|
||||
def writeMessage(self, time, message):
|
||||
"""
|
||||
Write message to disk
|
||||
"""
|
||||
|
||||
f = os.path.join( self.messageDir, time)
|
||||
if os.path.isfile( f ):
|
||||
return
|
||||
@ -106,7 +65,6 @@ Write message to disk
|
||||
|
||||
class ClientServe():
|
||||
def __init__(self, port):
|
||||
logger.info('ClientServe.__init__')
|
||||
server = HTTPServer( ('', port), ClientServeHandler)
|
||||
server.serve_forever()
|
||||
|
||||
|
@ -4,56 +4,36 @@ import logging
|
||||
import cgi
|
||||
import os
|
||||
import socket
|
||||
import BaseHTTPServer
|
||||
from BaseHTTPServer import HTTPServer
|
||||
import SimpleHTTPServer
|
||||
import urlparse
|
||||
import logging
|
||||
import logging.config
|
||||
|
||||
logging.config.fileConfig('pylog.conf')
|
||||
logger = logging.getLogger('meshenger'+'.nodeserve')
|
||||
|
||||
#GOTTMITTUNS
|
||||
|
||||
def _bare_address_string(self):
|
||||
host, port = self.client_address[:2]
|
||||
return '%s' % host
|
||||
|
||||
BaseHTTPServer.BaseHTTPRequestHandler.address_string = \
|
||||
_bare_address_string
|
||||
|
||||
#END
|
||||
|
||||
class NodeServeHandler(SimpleHTTPServer.SimpleHTTPRequestHandler):
|
||||
|
||||
messageDir = "/msg"
|
||||
|
||||
"""
|
||||
Serve index and messages
|
||||
"""
|
||||
def do_GET(self):
|
||||
"""
|
||||
Serve index and messages
|
||||
"""
|
||||
if self.path == '/':
|
||||
self.path = "/index"
|
||||
|
||||
if self.path == '/index' or self.path.startswith( self.messageDir ):
|
||||
return SimpleHTTPServer.SimpleHTTPRequestHandler.do_GET(self)
|
||||
|
||||
if self.path == '/alias' or self.path.startswith( self.messageDir ):
|
||||
return SimpleHTTPServer.SimpleHTTPRequestHandler.do_GET(self)
|
||||
|
||||
|
||||
else:
|
||||
self.send_response(404)
|
||||
self.send_header('Content-type', 'text/html')
|
||||
self.end_headers()
|
||||
self.wfile.write('404 - Not Found')
|
||||
|
||||
class HTTPServerV6(BaseHTTPServer.HTTPServer):
|
||||
class HTTPServerV6(HTTPServer):
|
||||
address_family = socket.AF_INET6
|
||||
|
||||
class NodeServe():
|
||||
def __init__(self, port):
|
||||
logger.info('NodeServe.__init__')
|
||||
server = HTTPServerV6(('::', port), NodeServeHandler)
|
||||
server.serve_forever()
|
||||
|
||||
|
@ -7,6 +7,14 @@
|
||||
|
||||
-perhaps replace the functionality of self.devices with something based on crawling nodes/hash/
|
||||
|
||||
-sometimes node tries to connect to itself (something wrong with receiving broadcast maybe??)
|
||||
|
||||
- HTTP calls the client needs:
|
||||
- /msg/<uid> -> give list of all messages for this uid
|
||||
- /msg/<uid>/<timestamp> -> get message for this timestamp
|
||||
- /clients -> return list of all known clients (uid's + usernames)
|
||||
- /getUID -> return uid for client ( unique id based on what? mac address? )
|
||||
|
||||
# Cryptoshit
|
||||
|
||||
encrypt / decrypt:
|
||||
@ -14,3 +22,5 @@ http://www.davedoesdev.com/a-simple-and-consistent-wrapper-for-javascript-crypto
|
||||
|
||||
generating keys:
|
||||
https://bitbucket.org/adrianpasternak/js-rsa-pem/wiki/Home
|
||||
|
||||
|
||||
|
34
pylog.conf
@ -1,34 +0,0 @@
|
||||
[loggers]
|
||||
keys=root,meshenger
|
||||
|
||||
[handlers]
|
||||
keys=consoleHandler, fileHandler
|
||||
|
||||
[formatters]
|
||||
keys=simpleFormatter
|
||||
|
||||
[logger_root]
|
||||
level=DEBUG
|
||||
handlers=consoleHandler
|
||||
|
||||
[logger_meshenger]
|
||||
level=DEBUG
|
||||
handlers=consoleHandler, fileHandler
|
||||
qualname=meshenger
|
||||
propagate=0
|
||||
|
||||
[handler_fileHandler]
|
||||
class=handlers.RotatingFileHandler
|
||||
level=INFO
|
||||
args=('log/meshenger.log','a','maxBytes=10000','backupCount=5')
|
||||
formatter=simpleFormatter
|
||||
|
||||
[handler_consoleHandler]
|
||||
class=StreamHandler
|
||||
level=DEBUG
|
||||
formatter=simpleFormatter
|
||||
args=(sys.stdout,)
|
||||
|
||||
[formatter_simpleFormatter]
|
||||
format=%(asctime)s - %(name)s - %(levelname)s - %(message)s
|
||||
datefmt=
|
45
web/emoji.js
@ -1,45 +0,0 @@
|
||||
function parseEmoticons( inputStr ){
|
||||
|
||||
var emoticons = [
|
||||
{
|
||||
text: ':)',
|
||||
image: '1.png'
|
||||
},
|
||||
{
|
||||
text: '<iframe',
|
||||
image: 'iframe-open.png'
|
||||
},
|
||||
{
|
||||
text: '</iframe>',
|
||||
image: 'iframe-close.png'
|
||||
},
|
||||
{
|
||||
text: '<script',
|
||||
image: 'script-open.gif'
|
||||
},
|
||||
{
|
||||
text: '</script>',
|
||||
image: 'script-close.gif'
|
||||
},
|
||||
{
|
||||
text: '<style',
|
||||
image: 'style-open.png'
|
||||
},
|
||||
{
|
||||
text: '</style>',
|
||||
image: 'style-close.png'
|
||||
},
|
||||
{
|
||||
text:';)',
|
||||
image: '2.png'
|
||||
},
|
||||
{
|
||||
text:':rock:',
|
||||
image: '3.png'
|
||||
}
|
||||
];
|
||||
for ( var i = 0; i < emoticons.length; i++ ){
|
||||
inputStr = inputStr.split(emoticons[i].text).join('<img class="emo" src="/web/emoticons/'+emoticons[i].image+'">');
|
||||
}
|
||||
return inputStr;
|
||||
}
|
Before Width: | Height: | Size: 5.8 KiB |
Before Width: | Height: | Size: 5.1 KiB |
Before Width: | Height: | Size: 6.3 KiB |
Before Width: | Height: | Size: 7.1 KiB |
Before Width: | Height: | Size: 6.8 KiB |
Before Width: | Height: | Size: 3.8 KiB |
Before Width: | Height: | Size: 3.4 KiB |
Before Width: | Height: | Size: 2.6 KiB |
Before Width: | Height: | Size: 2.2 KiB |
@ -1,70 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta http-equiv="cache-control" content="max-age=0" />
|
||||
<meta http-equiv="cache-control" content="no-cache" />
|
||||
<meta http-equiv="expires" content="0" />
|
||||
<meta http-equiv="expires" content="Tue, 01 Jan 1980 1:00:00 GMT" />
|
||||
<meta http-equiv="pragma" content="no-cache" />
|
||||
<meta name="viewport" content="width=device-width, user-scalable=no">
|
||||
<link href="/web/style.css" rel="stylesheet" type="text/css">
|
||||
<title>meshenger</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="message-page">
|
||||
<button id="message-back" class="back-button">Back</button><br><br>
|
||||
<form id="message-form" action="" accept-charset="utf-8">
|
||||
<textarea id="name" rows="1" placeholder="Your Name"></textarea>
|
||||
<textarea id="message" rows="2" placeholder="Your Message"></textarea>
|
||||
<br>
|
||||
<input type="submit" class="send-button" value="Send">
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<div id="photo-page" class="photo-page">
|
||||
<button id="photo-back" class="back-button">Back</button><br>
|
||||
<input id="fileInput" accept="image/*" capture="camera" type="file">
|
||||
<br>
|
||||
<p>
|
||||
<span id="progress">--</span>
|
||||
<span id="total">--</span>
|
||||
</p>
|
||||
|
||||
<!--Squashed source<br>-->
|
||||
<img id="sourceImage" src="" alt="capture" height="200" width="200">
|
||||
<!--Offset done<br>-->
|
||||
<canvas id="canvas1" width="200" height="200"></canvas>
|
||||
<!--Dithered<br>-->
|
||||
<canvas id="canvas2" width="200" height="200"></canvas>
|
||||
<!--Rotated<br>-->
|
||||
<canvas id="canvas3" width="200" height="200"></canvas>
|
||||
<br>
|
||||
|
||||
<button id="rot-left" class="rot-left">Rotate left</button>
|
||||
<button id="rot-right" class="rot-right">Rotate right</button>
|
||||
<br><br>
|
||||
<textarea id="photo-name" rows="1" placeholder="Your Name"></textarea>
|
||||
<br>
|
||||
<button id="submit-photo" class="photo-buttons">Send</button>
|
||||
|
||||
</div>
|
||||
|
||||
<div id="overview-page"><br>
|
||||
<div class="messages">
|
||||
<ul id="inbox"></ul>
|
||||
<ul id="outbox"></ul>
|
||||
</div>
|
||||
<div id="input-footer">
|
||||
<button id="new-photo">Photo</button>
|
||||
<button id="new-message">Message</button>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<script src="/web/main.js"></script>
|
||||
<script src="/web/emoji.js"></script>
|
||||
<script src="/web/photostuff.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
325
web/main.js
@ -1,325 +0,0 @@
|
||||
localStorage.clear();
|
||||
|
||||
|
||||
//These need to be obtained from the node
|
||||
var ownId, ownColor, ownAlias;
|
||||
|
||||
/*
|
||||
* INIT
|
||||
*/
|
||||
|
||||
document.addEventListener('DOMContentLoaded', function(){
|
||||
|
||||
function update(){
|
||||
if ( !ownId ){
|
||||
getOwnId();
|
||||
}
|
||||
if ( !ownAlias){
|
||||
getOwnAlias();
|
||||
}
|
||||
checkInbox();
|
||||
// also check for outbox items on interval,
|
||||
// necessary in case connection is lost and messages are not yet sent
|
||||
checkOutbox();
|
||||
}
|
||||
|
||||
document.getElementById('message-form').onsubmit = onSubmitMessage;
|
||||
|
||||
//update everything to initialize
|
||||
updateInboxView();
|
||||
update();
|
||||
|
||||
//check for new messages every 7 seconds
|
||||
window.setInterval( update, 7000 );
|
||||
|
||||
initState();
|
||||
initPhotoStuff(); //all photostuff is found in photostuff.js
|
||||
});
|
||||
|
||||
/*
|
||||
* STATE CHANGES
|
||||
*/
|
||||
|
||||
function initState(){
|
||||
showOverview();
|
||||
|
||||
document.getElementById('new-photo').onclick = showNewPhoto;
|
||||
document.getElementById('new-message').onclick = showNewMessage;
|
||||
document.getElementById('message-back').onclick = showOverview;
|
||||
document.getElementById('photo-back').onclick = showOverview;
|
||||
}
|
||||
|
||||
function showNewPhoto(){
|
||||
document.getElementById('photo-page').style.display = "block";
|
||||
document.getElementById('overview-page').style.display = "none";
|
||||
document.getElementById('message-page').style.display = "none";
|
||||
document.getElementById('fileInput').click();
|
||||
}
|
||||
|
||||
function showNewMessage(){
|
||||
document.getElementById('photo-page').style.display = "none";
|
||||
document.getElementById('overview-page').style.display = "none";
|
||||
document.getElementById('message-page').style.display = "block";
|
||||
}
|
||||
|
||||
function showOverview(){
|
||||
document.getElementById('photo-page').style.display = "none";
|
||||
document.getElementById('overview-page').style.display = "block";
|
||||
document.getElementById('message-page').style.display = "none";
|
||||
}
|
||||
|
||||
/*
|
||||
* OUTBOX STUFF
|
||||
*/
|
||||
function onSubmitMessage(){
|
||||
var msg = document.getElementById('message').value.replace(/\r?\n/g, "<br />");
|
||||
if ( !msg || msg === "" ) { /* prevent sending of empty messages */
|
||||
return false;
|
||||
}
|
||||
var namm = document.getElementById('name').value;
|
||||
if ( !namm || namm === "" || namm.length > 20) { /* prevent too long usernames */
|
||||
namm = "anonymous";
|
||||
}
|
||||
addOutboxItem( namm, "<p class='text-message'>"+msg+"</p>" );
|
||||
return false;
|
||||
}
|
||||
|
||||
function addOutboxItem( namm, message ){
|
||||
var outStr = localStorage.getItem( 'outbox' ) || '';
|
||||
var newMsgs = {};
|
||||
var ddata = new Date().getTime();
|
||||
var alias = ownAlias;
|
||||
|
||||
var contento = {
|
||||
"time" : ddata,
|
||||
"message" : message,
|
||||
"name" : namm,
|
||||
"node" : "local",
|
||||
"alias": alias,
|
||||
"hops" : "0"
|
||||
};
|
||||
newMsgs.message = contento;
|
||||
|
||||
localStorage.setItem( 'outbox', JSON.stringify(newMsgs) );
|
||||
checkOutbox();
|
||||
document.getElementById('message').value = '';
|
||||
|
||||
showOverview();
|
||||
}
|
||||
|
||||
function checkOutbox() {
|
||||
var outStr = localStorage.getItem( 'outbox' );
|
||||
if ( ! outStr ) {
|
||||
return;
|
||||
}
|
||||
var lines = outStr.split( /\n/ );
|
||||
for ( var i in lines ) {
|
||||
if ( lines[i].length === 0 ) {
|
||||
continue;
|
||||
}
|
||||
var obj = JSON.parse(lines[i]);
|
||||
var ts = obj.message.time;
|
||||
delete obj.message.time;
|
||||
var msg = JSON.stringify(obj.message);
|
||||
sendMessage( ts, msg );
|
||||
}
|
||||
}
|
||||
function sendMessage( timestamp, message ) {
|
||||
var xhr = new XMLHttpRequest();
|
||||
var data = 'time=' + encodeURIComponent( timestamp ) +
|
||||
'&message=' + encodeURIComponent( message );
|
||||
|
||||
xhr.onreadystatechange = function(){
|
||||
if ( xhr.readyState == 4){
|
||||
if ( xhr.status == 200 ) {
|
||||
removeOutboxItem( timestamp );
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
xhr.open('POST', 'send', true);
|
||||
xhr.setRequestHeader('Content-type', 'application/x-www-form-urlencoded');
|
||||
xhr.setRequestHeader('If-Modified-Since', 'Sat, 1 Jan 2005 00:00:00 GMT');
|
||||
xhr.send(data);
|
||||
}
|
||||
function removeOutboxItem( timestamp ) {
|
||||
var outStr = localStorage.getItem( 'outbox' ) || '';
|
||||
var lines = outStr.split( /\n/ );
|
||||
for ( var i in lines ) {
|
||||
var obj = JSON.parse(lines[i]);
|
||||
var ts = obj.message.time;
|
||||
if ( ts === timestamp ) {
|
||||
lines.splice( i, 1 );
|
||||
break;
|
||||
}
|
||||
}
|
||||
var newOutStr = lines.join('\n');
|
||||
localStorage.setItem('outbox', newOutStr);
|
||||
}
|
||||
|
||||
/*
|
||||
* INBOX STUFF
|
||||
*/
|
||||
|
||||
|
||||
function updateInboxView() {
|
||||
var localStorageArray = [];
|
||||
var contentString = '';
|
||||
|
||||
if (localStorage.length>0) {
|
||||
for (i=0;i<localStorage.length;i++){
|
||||
|
||||
element=localStorage.getItem(localStorage.key(i));
|
||||
|
||||
if ( localStorage.key(i).length < 10 || element === 'outbox' ) {
|
||||
continue;
|
||||
}
|
||||
try {
|
||||
elementj = JSON.parse(element);
|
||||
} catch (e) {
|
||||
continue;
|
||||
}
|
||||
|
||||
localStorageArray[i] = {
|
||||
time:localStorage.key(i),
|
||||
user:elementj.name,
|
||||
message:elementj.message,
|
||||
node:elementj.node,
|
||||
hops:elementj.hops,
|
||||
alias:elementj.alias
|
||||
};
|
||||
}
|
||||
}
|
||||
orderStorage = localStorageArray.sort(function(a,b) { return b.time - a.time; } );
|
||||
|
||||
for(var i in orderStorage) {
|
||||
if ( i.length === 0 || i === 'outbox' ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
var datereadable = getReadableDate( new Date(parseInt(orderStorage[i].time)) );
|
||||
var color = getNodeColor( orderStorage[i].node );
|
||||
contentString += '<li><div class="message-block" style="background-color:'+color+'">'+
|
||||
'<div class="date-sender">On ' + datereadable +
|
||||
' <b>' + parseEmoticons(orderStorage[i].user) +'</b> wrote:</div>' +
|
||||
'<div class="message-text">' + parseEmoticons( orderStorage[i].message ) + '</div>' + //parseEmoticons is found in emoji.js
|
||||
' <div class="nodehops"><div class="node '+orderStorage[i].node+'">from '+orderStorage[i].alias + '</div>' +
|
||||
' <div class="hops '+orderStorage[i].hops+'">via '+orderStorage[i].hops+' nodes</div></div></div></li>';
|
||||
}
|
||||
document.getElementById( 'inbox' ).innerHTML = contentString;
|
||||
}
|
||||
|
||||
function getReadableDate( date ) {
|
||||
var day = date.getDate();
|
||||
if (day < 10) day = '0' + day;
|
||||
var month = date.getMonth()+1;
|
||||
if (month < 10) month = '0' + month;
|
||||
var year = date.getFullYear();
|
||||
var hrs = date.getHours();
|
||||
if (hrs < 10) hrs = '0' + hrs;
|
||||
var min = date.getMinutes();
|
||||
if (min < 10) min = '0' + min;
|
||||
|
||||
return day + '/' + month + '/' + year + ' ' + hrs + ':' + min;
|
||||
}
|
||||
function getNodeColor( nodeId ) {
|
||||
if( nodeId === 'local' ) {
|
||||
return ownColor || '#fff';
|
||||
}
|
||||
return colorLuminance(nodeId.substr(0,6), 0.5);
|
||||
}
|
||||
function colorLuminance(hex, lum) {
|
||||
|
||||
// validate hex string
|
||||
hex = String(hex).replace(/[^0-9a-f]/gi, '');
|
||||
if (hex.length < 6) {
|
||||
hex = hex[0]+hex[0]+hex[1]+hex[1]+hex[2]+hex[2];
|
||||
}
|
||||
lum = lum || 0;
|
||||
|
||||
// convert to decimal and change luminosity
|
||||
var rgb = "#", c, i;
|
||||
for (i = 0; i < 3; i++) {
|
||||
c = parseInt(hex.substr(i*2,2), 16);
|
||||
c = Math.round(Math.min(Math.max(0, c + (c * lum)), 255)).toString(16);
|
||||
rgb += ("00"+c).substr(c.length);
|
||||
}
|
||||
|
||||
return rgb;
|
||||
}
|
||||
|
||||
function onMessageDownload( msg, filename ) {
|
||||
if ( localStorage.getItem( filename ) === null ) {
|
||||
localStorage.setItem( filename, msg );
|
||||
}
|
||||
updateInboxView();
|
||||
}
|
||||
function onIndex( index ) {
|
||||
var lines = index.split( /\n/ );
|
||||
var k,i,l,f;
|
||||
for( k in localStorage){
|
||||
l = 1;
|
||||
for ( i in lines ) {
|
||||
f = lines[i];
|
||||
if (f == k){ l = 0; }
|
||||
}
|
||||
if (l == 1){
|
||||
localStorage.removeItem(k);
|
||||
}
|
||||
}
|
||||
updateInboxView();
|
||||
|
||||
for ( i in lines ) {
|
||||
var fname = lines[i];
|
||||
if ( localStorage.getItem( fname ) === null ) {
|
||||
//localStorage.setItem( ts, lines[i].substr(lines[i].indexOf(' ')) );
|
||||
downloadMessage( fname );
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
function downloadMessage(filename) {
|
||||
var xhr = new XMLHttpRequest();
|
||||
xhr.onreadystatechange = function(){
|
||||
if (xhr.readyState == 4 && xhr.status == 200){
|
||||
onMessageDownload( xhr.responseText, filename );
|
||||
}
|
||||
};
|
||||
xhr.open( "GET", 'msg/'+filename, true);
|
||||
xhr.send();
|
||||
|
||||
}
|
||||
function checkInbox() {
|
||||
var xhr = new XMLHttpRequest();
|
||||
xhr.onreadystatechange = function(){
|
||||
if (xhr.readyState == 4 && xhr.status == 200){
|
||||
onIndex( xhr.responseText );
|
||||
}
|
||||
};
|
||||
xhr.open( "GET", 'index', true);
|
||||
xhr.send();
|
||||
}
|
||||
function getOwnId() {
|
||||
var xhr = new XMLHttpRequest();
|
||||
xhr.onreadystatechange = function(){
|
||||
if (xhr.readyState == 4 && xhr.status == 200){
|
||||
ownId = xhr.responseText;
|
||||
ownColor = getNodeColor( ownId );
|
||||
}
|
||||
};
|
||||
xhr.open( "GET", 'id', true);
|
||||
xhr.send();
|
||||
}
|
||||
|
||||
function getOwnAlias() {
|
||||
var xhr = new XMLHttpRequest();
|
||||
xhr.onreadystatechange = function(){
|
||||
if (xhr.readyState == 4 && xhr.status == 200){
|
||||
ownAlias = xhr.responseText;
|
||||
}
|
||||
};
|
||||
xhr.open( "GET", 'alias', true);
|
||||
xhr.send();
|
||||
}
|
||||
|
@ -1,225 +0,0 @@
|
||||
/*
|
||||
* PHOTO STUFF
|
||||
*/
|
||||
var imgDim = 200;
|
||||
var canvas1;
|
||||
var context1;
|
||||
var canvas2;
|
||||
var context2;
|
||||
var imgObj;
|
||||
var fileReader;
|
||||
var rotation = 0;
|
||||
|
||||
var sourceImage = document.getElementById('sourceImage'); // get reference to img
|
||||
var fileInput = document.getElementById('fileInput'); // get reference to file select
|
||||
var progressEl = document.getElementById('progress');
|
||||
var totalEl = document.getElementById('total');
|
||||
|
||||
|
||||
function initPhotoStuff(){
|
||||
canvas1 = document.getElementById('canvas1');
|
||||
context1 = canvas1.getContext('2d');
|
||||
canvas2 = document.getElementById('canvas2');
|
||||
context2 = canvas2.getContext('2d');
|
||||
canvas3 = document.getElementById('canvas3');
|
||||
context3 = canvas3.getContext('2d');
|
||||
initCanvas(context1);
|
||||
initCanvas(context2);
|
||||
initCanvas(context3);
|
||||
|
||||
fileInput.onchange = onFileInputChange;
|
||||
|
||||
document.getElementById('rot-left').onclick = onRotateLeft;
|
||||
document.getElementById('rot-right').onclick = onRotateRight;
|
||||
document.getElementById('submit-photo').onclick = submitImage;
|
||||
|
||||
}
|
||||
|
||||
|
||||
function submitImage(){
|
||||
var image = new Image(); //create new image holder
|
||||
|
||||
var canvas = document.getElementById('canvas3'); // choose canvas element to convert
|
||||
var dataURL = canvas.toDataURL(); // convert cabvas to data url we can handle
|
||||
image.src = dataURL;
|
||||
image.className = 'photo-message';
|
||||
if ( !image.src || image.src === "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAMgAAADICAYAAACtWK6eAAACEUlEQVR4nO3TMQEAIAzAsPk3DQrWF45EQZ/OAVbzOgB+ZhAIBoFgEAgGgWAQCAaBYBAIBoFgEAgGgWAQCAaBYBAIBoFgEAgGgWAQCAaBYBAIBoFgEAgGgWAQCAaBYBAIBoFgEAgGgWAQCAaBYBAIBoFgEAgGgWAQCAaBYBAIBoFgEAgGgWAQCAaBYBAIBoFgEAgGgWAQCAaBYBAIBoFgEAgGgWAQCAaBYBAIBoFgEAgGgWAQCAaBYBAIBoFgEAgGgWAQCAaBYBAIBoFgEAgGgWAQCAaBYBAIBoFgEAgGgWAQCAaBYBAIBoFgEAgGgWAQCAaBYBAIBoFgEAgGgWAQCAaBYBAIBoFgEAgGgWAQCAaBYBAIBoFgEAgGgWAQCAaBYBAIBoFgEAgGgWAQCAaBYBAIBoFgEAgGgWAQCAaBYBAIBoFgEAgGgWAQCAaBYBAIBoFgEAgGgWAQCAaBYBAIBoFgEAgGgWAQCAaBYBAIBoFgEAgGgWAQCAaBYBAIBoFgEAgGgWAQCAaBYBAIBoFgEAgGgWAQCAaBYBAIBoFgEAgGgWAQCAaBYBAIBoFgEAgGgWAQCAaBYBAIBoFgEAgGgWAQCAaBYBAIBoFgEAgGgWAQCAaBYBAIBoFgEAgGgWAQCAaBYBAIBoFgEAgGgWAQCAaBYBAIBoFgEAgGgWAQCAaBYBAIBoFgEAgGgWAQCAaBcAEODrNzcVUZ+gAAAABJRU5ErkJggg==" ) { /* prevent sending of empty messages (data url of blank, thus white, canvas)*/
|
||||
return false;
|
||||
}
|
||||
|
||||
var namm = document.getElementById('photo-name').value;
|
||||
if( !namm || namm == "" ){
|
||||
namm = "anonymous";
|
||||
}
|
||||
addOutboxItem( parseEmoticons(namm), image.outerHTML );
|
||||
|
||||
showOverview();
|
||||
return false;
|
||||
}
|
||||
|
||||
function initCanvas(context){
|
||||
// Debug filling
|
||||
context.fillStyle ="#dbbd7a";
|
||||
context.fill();
|
||||
context.beginPath();
|
||||
context.rect(0,0, imgDim, imgDim);
|
||||
context.fillStyle = 'white';
|
||||
context.fill();
|
||||
// context.lineWidth = 7;
|
||||
// context.strokeStyle = 'black';
|
||||
// context.stroke();
|
||||
}
|
||||
|
||||
// create file reader
|
||||
function onFileInputChange(){
|
||||
var file = fileInput.files[0]; // get selected file (camera capture)
|
||||
fileReader = new FileReader();
|
||||
fileReader.onload = onFileReaderLoad; // add onload listener
|
||||
fileReader.onprogress = onFileReaderProgress; // add progress listener
|
||||
fileReader.readAsDataURL( file ); // get captured image as data URI
|
||||
}
|
||||
|
||||
function onFileReaderProgress(e) {
|
||||
if (e.lengthComputable) {
|
||||
var progress = parseInt( ((e.loaded / e.total) * 100), 10 );
|
||||
console.log(progress);
|
||||
progressEl.innerHTML = e.loaded;
|
||||
totalEl.innerHTML = e.total;
|
||||
}
|
||||
}
|
||||
function onFileReaderLoad() {
|
||||
imgObj = new Image();
|
||||
imgObj.src = fileReader.result;
|
||||
imgObj.onload = onImageLoad;
|
||||
|
||||
//for debugging: show image on screen, will be squashed
|
||||
sourceImage.src = fileReader.result;
|
||||
}
|
||||
|
||||
function onImageLoad() {
|
||||
var w,h;
|
||||
var xOffset = 0;
|
||||
var yOffset = 0;
|
||||
if ( imgObj.width > imgObj.height ) {
|
||||
h = imgDim;
|
||||
w = imgDim * imgObj.width / imgObj.height;
|
||||
xOffset = (imgDim - w) / 2;
|
||||
} else {
|
||||
w = imgDim;
|
||||
h = imgDim * imgObj.height / imgObj.width;
|
||||
yOffset = (imgDim - h) / 2;
|
||||
}
|
||||
context1.drawImage(imgObj, xOffset, yOffset, w, h);
|
||||
var imageData = context1.getImageData( 0, 0, canvas1.width, canvas1.height);
|
||||
context2.putImageData( monochrome(imageData,128,"atkinson"), 0, 0);
|
||||
drawRotated();
|
||||
}
|
||||
|
||||
function onRotateRight(){
|
||||
rotation += 90;
|
||||
drawRotated();
|
||||
}
|
||||
function onRotateLeft(){
|
||||
rotation -= 90;
|
||||
drawRotated();
|
||||
}
|
||||
function drawRotated(){
|
||||
|
||||
context3.clearRect(0,0,canvas3.width,canvas3.height);
|
||||
context3.save();
|
||||
context3.translate(canvas3.width/2,canvas3.height/2);
|
||||
context3.rotate(rotation * Math.PI / 180);
|
||||
context3.translate(-canvas3.width/2,-canvas3.height/2);
|
||||
context3.drawImage( canvas2, 0, 0 );
|
||||
context3.restore();
|
||||
}
|
||||
|
||||
|
||||
|
||||
var bayerThresholdMap = [
|
||||
[ 15, 135, 45, 165 ],
|
||||
[ 195, 75, 225, 105 ],
|
||||
[ 60, 180, 30, 150 ],
|
||||
[ 240, 120, 210, 90 ]
|
||||
];
|
||||
|
||||
var lumR = [];
|
||||
var lumG = [];
|
||||
var lumB = [];
|
||||
for (var i=0; i<256; i++) {
|
||||
lumR[i] = i*0.299;
|
||||
lumG[i] = i*0.587;
|
||||
lumB[i] = i*0.114;
|
||||
}
|
||||
|
||||
function monochrome(imageData, threshold, type){
|
||||
|
||||
var imageDataLength = imageData.data.length;
|
||||
|
||||
// Greyscale luminance (sets r pixels to luminance of rgb)
|
||||
for (var i = 0; i <= imageDataLength; i += 4) {
|
||||
imageData.data[i] = Math.floor(lumR[imageData.data[i]] + lumG[imageData.data[i+1]] + lumB[imageData.data[i+2]]);
|
||||
|
||||
|
||||
}
|
||||
var w = imageData.width;
|
||||
var newPixel, err;
|
||||
|
||||
for (var currentPixel = 0; currentPixel <= imageDataLength; currentPixel+=4) {
|
||||
|
||||
if (type === "none") {
|
||||
// No dithering
|
||||
imageData.data[currentPixel] = imageData.data[currentPixel] < threshold ? 0 : 255;
|
||||
} else if (type === "bayer") {
|
||||
// 4x4 Bayer ordered dithering algorithm
|
||||
var x = currentPixel/4 % w;
|
||||
var y = Math.floor(currentPixel/4 / w);
|
||||
var map = Math.floor( (imageData.data[currentPixel] + bayerThresholdMap[x%4][y%4]) / 2 );
|
||||
imageData.data[currentPixel] = (map < threshold) ? 0 : 255;
|
||||
} else if (type === "floydsteinberg") {
|
||||
// Floyd–Steinberg dithering algorithm
|
||||
newPixel = imageData.data[currentPixel] < 129 ? 0 : 255;
|
||||
err = Math.floor((imageData.data[currentPixel] - newPixel) / 16);
|
||||
imageData.data[currentPixel] = newPixel;
|
||||
|
||||
imageData.data[currentPixel + 4 ] += err*7;
|
||||
imageData.data[currentPixel + 4*w - 4 ] += err*3;
|
||||
imageData.data[currentPixel + 4*w ] += err*5;
|
||||
imageData.data[currentPixel + 4*w + 4 ] += err*1;
|
||||
} else {
|
||||
// Bill Atkinson's dithering algorithm
|
||||
newPixel = imageData.data[currentPixel] < 129 ? 0 : 255;
|
||||
err = Math.floor((imageData.data[currentPixel] - newPixel) / 8);
|
||||
imageData.data[currentPixel] = newPixel;
|
||||
|
||||
imageData.data[currentPixel + 4 ] += err;
|
||||
imageData.data[currentPixel + 8 ] += err;
|
||||
imageData.data[currentPixel + 4*w - 4 ] += err;
|
||||
imageData.data[currentPixel + 4*w ] += err;
|
||||
imageData.data[currentPixel + 4*w + 4 ] += err;
|
||||
imageData.data[currentPixel + 8*w ] += err;
|
||||
}
|
||||
|
||||
// Set g and b pixels equal to r
|
||||
imageData.data[currentPixel + 1] = imageData.data[currentPixel + 2] = imageData.data[currentPixel];
|
||||
}
|
||||
// Alpha: make white pixels transparent!
|
||||
var newColor = {r:0,g:0,b:0, a:0};
|
||||
|
||||
for ( i = 0, n = imageData.data.length; i <n; i += 4) {
|
||||
var r = imageData.data[i],
|
||||
g = imageData.data[i+1],
|
||||
b = imageData.data[i+2];
|
||||
|
||||
// If its white, or close to white then change it
|
||||
if(r >=200 && g >= 200 && b >= 200){
|
||||
// Change the white to whatever.
|
||||
imageData.data[i] = newColor.r;
|
||||
imageData.data[i+1] = newColor.g;
|
||||
imageData.data[i+2] = newColor.b;
|
||||
imageData.data[i+3] = newColor.a;
|
||||
}
|
||||
}
|
||||
|
||||
return imageData;
|
||||
}
|
523
web/style.css
@ -1,523 +0,0 @@
|
||||
body, html{
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
body{
|
||||
font-size:20px;
|
||||
text-shadow: 1px 1px #ddd;
|
||||
font-family: times, 'times new roman', helvetica,serif;
|
||||
line-height:1.5em;
|
||||
color:#444;
|
||||
background:#fff;
|
||||
padding:20px;
|
||||
}
|
||||
|
||||
|
||||
/* resize images on mobile */
|
||||
@media all and (max-width: 768px) {
|
||||
|
||||
.photo-message{ /* rounded corner imgs...*/
|
||||
width:100%;
|
||||
height:100%;
|
||||
overflow:hidden;
|
||||
line-height: 0;
|
||||
-webkit-border-top-left-radius:123px;
|
||||
-webkit-border-top-right-radius:123px;
|
||||
-webkit-border-bottom-right-radius:123px;
|
||||
-webkit-border-bottom-left-radius:123px;
|
||||
-moz-border-radius-bottomright:123px;
|
||||
-moz-border-radius-bottomleft:123px;
|
||||
-moz-border-top-left-radius:123px;
|
||||
-moz-border-top-right-radius:123px;
|
||||
border-top-left-radius:123px;
|
||||
border-top-right-radius:123px;
|
||||
border-bottom-right-radius:123px;
|
||||
border-bottom-left-radius:123px;
|
||||
}
|
||||
|
||||
img.photo-message{
|
||||
width:100%;
|
||||
line-height: 0;
|
||||
vertical-align:middle; /* otherwise produces bottom border the size of the body 'line-height' */
|
||||
/* disable anti aliasing */
|
||||
image-rendering: optimizeSpeed;
|
||||
image-rendering: -moz-crisp-edges;
|
||||
image-rendering: -o-crisp-edges;
|
||||
image-rendering: -webkit-optimize-contrast;
|
||||
image-rendering: pixelated;
|
||||
image-rendering: optimize-contrast;
|
||||
-ms-interpolation-mode: nearest-neighbor;
|
||||
}
|
||||
|
||||
.nodehops{
|
||||
position: absolute;
|
||||
float: right;
|
||||
right:20px;
|
||||
text-align: right;
|
||||
padding: 0px;
|
||||
margin-top:-50px;
|
||||
-moz-box-shadow:0 0 23px #bbb;
|
||||
-webkit-box-shadow:0 0 23px #bbb;
|
||||
box-shadow:0 0 23px #bbb;
|
||||
-webkit-border-top-left-radius:123px;
|
||||
-webkit-border-top-right-radius:123px;
|
||||
-webkit-border-bottom-right-radius:123px;
|
||||
-webkit-border-bottom-left-radius:123px;
|
||||
-moz-border-radius-bottomright:123px;
|
||||
-moz-border-radius-bottomleft:123px;
|
||||
-moz-border-top-left-radius:123px;
|
||||
-moz-border-top-right-radius:123px;
|
||||
border-top-left-radius:123px;
|
||||
border-top-right-radius:123px;
|
||||
border-bottom-right-radius:123px;
|
||||
border-bottom-left-radius:123px;
|
||||
background:#fff;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/* resize images on desktop */
|
||||
@media all and (min-width: 768px) {
|
||||
img.photo-message{
|
||||
width:50%;
|
||||
display: block;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
/* disable anti aliasing */
|
||||
image-rendering: optimizeSpeed;
|
||||
image-rendering: -moz-crisp-edges;
|
||||
image-rendering: -o-crisp-edges;
|
||||
image-rendering: -webkit-optimize-contrast;
|
||||
image-rendering: pixelated;
|
||||
image-rendering: optimize-contrast;
|
||||
-ms-interpolation-mode: nearest-neighbor;
|
||||
}
|
||||
|
||||
|
||||
img.photo-message{
|
||||
/* disable anti aliasing */
|
||||
image-rendering: optimizeSpeed;
|
||||
image-rendering: -moz-crisp-edges;
|
||||
image-rendering: -o-crisp-edges;
|
||||
image-rendering: -webkit-optimize-contrast;
|
||||
image-rendering: pixelated;
|
||||
image-rendering: optimize-contrast;
|
||||
-ms-interpolation-mode: nearest-neighbor;
|
||||
|
||||
}
|
||||
|
||||
.photo-message{ /* rounded corner imgs on desktop...*/
|
||||
padding:30px;
|
||||
width:100%;
|
||||
height:100%;
|
||||
overflow:hidden;
|
||||
-webkit-border-top-left-radius:123px;
|
||||
-webkit-border-top-right-radius:123px;
|
||||
-webkit-border-bottom-right-radius:123px;
|
||||
-webkit-border-bottom-left-radius:123px;
|
||||
-moz-border-radius-bottomright:123px;
|
||||
-moz-border-radius-bottomleft:123px;
|
||||
-moz-border-top-left-radius:123px;
|
||||
-moz-border-top-right-radius:123px;
|
||||
border-top-left-radius:123px;
|
||||
border-top-right-radius:123px;
|
||||
border-bottom-right-radius:123px;
|
||||
border-bottom-left-radius:123px;
|
||||
}
|
||||
|
||||
.nodehops{
|
||||
position: absolute;
|
||||
float: right;
|
||||
right:20px;
|
||||
text-align: right;
|
||||
padding: 4px;
|
||||
margin-top:-60px;
|
||||
-moz-box-shadow:0 0 23px #bbb;
|
||||
-webkit-box-shadow:0 0 23px #bbb;
|
||||
box-shadow:0 0 23px #bbb;
|
||||
-webkit-border-top-left-radius:123px;
|
||||
-webkit-border-top-right-radius:123px;
|
||||
-webkit-border-bottom-right-radius:123px;
|
||||
-webkit-border-bottom-left-radius:123px;
|
||||
-moz-border-radius-bottomright:123px;
|
||||
-moz-border-radius-bottomleft:123px;
|
||||
-moz-border-top-left-radius:123px;
|
||||
-moz-border-top-right-radius:123px;
|
||||
border-top-left-radius:123px;
|
||||
border-top-right-radius:123px;
|
||||
border-bottom-right-radius:123px;
|
||||
border-bottom-left-radius:123px;
|
||||
background:#fff;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
img.emo {
|
||||
}
|
||||
|
||||
.date-sender .emo {
|
||||
width:22px;
|
||||
height:22px;
|
||||
}
|
||||
|
||||
p.text-message{
|
||||
padding: 30px;
|
||||
padding-top: 50px;
|
||||
}
|
||||
|
||||
#input-footer{
|
||||
position: fixed;
|
||||
top:0px;
|
||||
width:100%;
|
||||
margin:10px;
|
||||
}
|
||||
|
||||
|
||||
#new-photo{
|
||||
display:inline;
|
||||
float:left;
|
||||
width:30%;
|
||||
-webkit-appearance: none;
|
||||
-moz-appearance: none;
|
||||
outline: none;
|
||||
border:0;
|
||||
-webkit-border-radius: 13px;
|
||||
-moz-border-radius: 13px;
|
||||
border-radius: 13px;
|
||||
height:40px;
|
||||
display: block;
|
||||
font-family: inherit;
|
||||
font-size: 20px;
|
||||
font-weight:bold;
|
||||
text-shadow: 0 0 23px #888;
|
||||
background-color:#fff;
|
||||
-moz-box-shadow:0 0 23px #bbb;
|
||||
-webkit-box-shadow:0 0 23px #bbb;
|
||||
box-shadow:0 0 23px #bbb;
|
||||
}
|
||||
|
||||
#new-message{
|
||||
margin-right: 60px;
|
||||
display:inline;
|
||||
width:30%;
|
||||
float:right;
|
||||
-webkit-appearance: none;
|
||||
-moz-appearance: none;
|
||||
outline: none;
|
||||
border:0;
|
||||
-webkit-border-radius: 13px;
|
||||
-moz-border-radius: 13px;
|
||||
border-radius: 13px;
|
||||
height:40px;
|
||||
display: block;
|
||||
font-family: inherit;
|
||||
font-size: 20px;
|
||||
font-weight:bold;
|
||||
text-shadow: 0 0 23px #888;
|
||||
background-color:#fff;
|
||||
-moz-box-shadow:0 0 23px #bbb;
|
||||
-webkit-box-shadow:0 0 23px #bbb;
|
||||
box-shadow:0 0 23px #bbb;
|
||||
}
|
||||
|
||||
.back-button{
|
||||
display:inline;
|
||||
float:left;
|
||||
width:100%;
|
||||
-webkit-appearance: none;
|
||||
-moz-appearance: none;
|
||||
outline: none;
|
||||
border:0;
|
||||
-webkit-border-radius: 13px;
|
||||
-moz-border-radius: 13px;
|
||||
border-radius: 13px;
|
||||
height:40px;
|
||||
display: block;
|
||||
font-family: inherit;
|
||||
font-size: 20px;
|
||||
font-weight:bold;
|
||||
text-shadow: 0 0 23px #888;
|
||||
background-color:#fff;
|
||||
-moz-box-shadow:0 0 23px #bbb;
|
||||
-webkit-box-shadow:0 0 23px #bbb;
|
||||
box-shadow:0 0 23px #bbb;
|
||||
}
|
||||
|
||||
|
||||
.rot-left{
|
||||
display:inline;
|
||||
float:left;
|
||||
width:50%;
|
||||
-webkit-appearance: none;
|
||||
-moz-appearance: none;
|
||||
outline: none;
|
||||
border:0;
|
||||
-webkit-border-radius: 13px;
|
||||
-moz-border-radius: 13px;
|
||||
border-radius: 13px;
|
||||
height:40px;
|
||||
display: block;
|
||||
font-family: inherit;
|
||||
font-size: 20px;
|
||||
font-weight:bold;
|
||||
text-shadow: 0 0 23px #888;
|
||||
background-color:#fff;
|
||||
-moz-box-shadow:0 0 23px #bbb;
|
||||
-webkit-box-shadow:0 0 23px #bbb;
|
||||
box-shadow:0 0 23px #bbb;
|
||||
}
|
||||
|
||||
.rot-right{
|
||||
display:inline;
|
||||
width:50%;
|
||||
float:right;
|
||||
-webkit-appearance: none;
|
||||
-moz-appearance: none;
|
||||
outline: none;
|
||||
border:0;
|
||||
-webkit-border-radius: 13px;
|
||||
-moz-border-radius: 13px;
|
||||
border-radius: 13px;
|
||||
height:40px;
|
||||
display: block;
|
||||
font-family: inherit;
|
||||
font-size: 20px;
|
||||
font-weight:bold;
|
||||
text-shadow: 0 0 23px #888;
|
||||
background-color:#fff;
|
||||
-moz-box-shadow:0 0 23px #bbb;
|
||||
-webkit-box-shadow:0 0 23px #bbb;
|
||||
box-shadow:0 0 23px #bbb;
|
||||
}
|
||||
|
||||
|
||||
.photo-buttons {
|
||||
-webkit-appearance: none;
|
||||
-moz-appearance: none;
|
||||
outline: none;
|
||||
border:0;
|
||||
-webkit-border-radius: 13px;
|
||||
-moz-border-radius: 13px;
|
||||
border-radius: 13px;
|
||||
height:40px;
|
||||
width:100%;
|
||||
display: block;
|
||||
font-family: inherit;
|
||||
font-size: 20px;
|
||||
font-weight:bold;
|
||||
text-shadow: 0 0 23px #888;
|
||||
background-color:#fff;
|
||||
-moz-box-shadow:0 0 23px #bbb;
|
||||
-webkit-box-shadow:0 0 23px #bbb;
|
||||
box-shadow:0 0 23px #bbb;
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* hide calculation canvases */
|
||||
#sourceImage{
|
||||
display: none;
|
||||
}
|
||||
#canvas1{
|
||||
display: none;
|
||||
}
|
||||
#canvas2{
|
||||
display: none;
|
||||
}
|
||||
|
||||
#progress{
|
||||
display: none;
|
||||
}
|
||||
|
||||
#total{
|
||||
display: none;
|
||||
}
|
||||
|
||||
|
||||
#message-form textarea{
|
||||
-webkit-appearance: none;
|
||||
-moz-appearance: none;
|
||||
|
||||
display: block;
|
||||
outline:none;
|
||||
width: 98%;
|
||||
margin: 0 auto;
|
||||
line-height: 40px;
|
||||
border:none;
|
||||
-webkit-border-radius: 13px;
|
||||
-moz-border-radius: 13px;
|
||||
border-radius: 13px;
|
||||
font-family: inherit;
|
||||
font-size: 20px;
|
||||
|
||||
-moz-box-shadow:0 0 23px #bbb;
|
||||
-webkit-box-shadow:0 0 23px #bbb;
|
||||
box-shadow:0 0 23px #bbb;
|
||||
|
||||
}
|
||||
|
||||
#photo-name{ /* text field */
|
||||
-webkit-appearance: none;
|
||||
-moz-appearance: none;
|
||||
|
||||
display: block;
|
||||
outline:none;
|
||||
width: 98%;
|
||||
margin: 0 auto;
|
||||
line-height: 40px;
|
||||
border:none;
|
||||
-webkit-border-radius: 13px;
|
||||
-moz-border-radius: 13px;
|
||||
border-radius: 13px;
|
||||
font-family: inherit;
|
||||
font-size: 20px;
|
||||
|
||||
-moz-box-shadow:0 0 23px #bbb;
|
||||
-webkit-box-shadow:0 0 23px #bbb;
|
||||
box-shadow:0 0 23px #bbb;
|
||||
|
||||
}
|
||||
|
||||
#message-form .send-button {
|
||||
-webkit-appearance: none;
|
||||
-moz-appearance: none;
|
||||
outline: none;
|
||||
border:0;
|
||||
-webkit-border-radius: 13px;
|
||||
-moz-border-radius: 13px;
|
||||
border-radius: 13px;
|
||||
height:40px;
|
||||
width:100%;
|
||||
display: block;
|
||||
font-family: inherit;
|
||||
font-size: 20px;
|
||||
font-weight:bold;
|
||||
text-shadow: 0 0 23px #888;
|
||||
background-color:#fff;
|
||||
|
||||
-moz-box-shadow:0 0 23px #bbb;
|
||||
-webkit-box-shadow:0 0 23px #bbb;
|
||||
box-shadow:0 0 23px #bbb;
|
||||
|
||||
}
|
||||
|
||||
select{
|
||||
width:100%;
|
||||
display: block;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
margin: 1.5em 0;
|
||||
font-family: inherit;
|
||||
font-size: inherit;
|
||||
font-size: 20px;
|
||||
|
||||
}
|
||||
|
||||
h2{
|
||||
margin: 0 0 20px;
|
||||
font-size: 40px;
|
||||
text-shadow: 0px 0px 23px #bbb;
|
||||
}
|
||||
|
||||
hr{
|
||||
height:10px;
|
||||
border: 10;
|
||||
width:87%;
|
||||
background-image: url(css/bg1.gif);
|
||||
|
||||
}
|
||||
|
||||
.messages ul{
|
||||
padding:0px;
|
||||
margin: 0;
|
||||
}
|
||||
.messages li{
|
||||
list-style: none;
|
||||
margin: 0;
|
||||
}
|
||||
.messages .message-block {
|
||||
margin-top:30px;
|
||||
word-wrap:break-word;
|
||||
overflow: visible;
|
||||
-moz-box-shadow:0 0 23px #bbb;
|
||||
-webkit-box-shadow:0 0 23px #bbb;
|
||||
box-shadow:0 0 23px #bbb;
|
||||
-webkit-border-top-left-radius:123px;
|
||||
-webkit-border-top-right-radius:123px;
|
||||
-webkit-border-bottom-right-radius:123px;
|
||||
-webkit-border-bottom-left-radius:123px;
|
||||
-moz-border-radius-bottomright:123px;
|
||||
-moz-border-radius-bottomleft:123px;
|
||||
-moz-border-top-left-radius:123px;
|
||||
-moz-border-top-right-radius:123px;
|
||||
border-top-left-radius:123px;
|
||||
border-top-right-radius:123px;
|
||||
border-bottom-right-radius:123px;
|
||||
border-bottom-left-radius:123px;
|
||||
background:#fff;
|
||||
}
|
||||
|
||||
|
||||
|
||||
.photoBox {
|
||||
width: 100%;
|
||||
margin: 0 auto;
|
||||
line-height: 40px;
|
||||
display: none;
|
||||
position: absolute;
|
||||
background-color:#fff;
|
||||
z-index:1002;
|
||||
overflow: visible;
|
||||
background:#fff;
|
||||
}
|
||||
|
||||
|
||||
.node, .hops {
|
||||
font-size:10px;
|
||||
display: inline-block;
|
||||
word-wrap:break-word;
|
||||
overflow: visible;
|
||||
|
||||
}
|
||||
|
||||
|
||||
.message-text {
|
||||
font-size: 25px;
|
||||
}
|
||||
|
||||
.date-sender {
|
||||
position: absolute;
|
||||
font-size: 15px;
|
||||
padding: 4px;
|
||||
word-wrap:break-word;
|
||||
overflow: visible;
|
||||
-moz-box-shadow:0 0 23px #bbb;
|
||||
-webkit-box-shadow:0 0 23px #bbb;
|
||||
box-shadow:0 0 23px #bbb;
|
||||
-webkit-border-top-left-radius:123px;
|
||||
-webkit-border-top-right-radius:123px;
|
||||
-webkit-border-bottom-right-radius:123px;
|
||||
-webkit-border-bottom-left-radius:123px;
|
||||
-moz-border-radius-bottomright:123px;
|
||||
-moz-border-radius-bottomleft:123px;
|
||||
-moz-border-top-left-radius:123px;
|
||||
-moz-border-top-right-radius:123px;
|
||||
border-top-left-radius:123px;
|
||||
border-top-right-radius:123px;
|
||||
border-bottom-right-radius:123px;
|
||||
border-bottom-left-radius:123px;
|
||||
background:#fff;
|
||||
}
|
||||
|
||||
#outbox{
|
||||
display:none;
|
||||
}
|
||||
#header{
|
||||
width:100%;
|
||||
text-align: center;
|
||||
}
|
||||
|
248
webapp.html
@ -7,181 +7,35 @@
|
||||
<meta http-equiv="expires" content="Tue, 01 Jan 1980 1:00:00 GMT" />
|
||||
<meta http-equiv="pragma" content="no-cache" />
|
||||
<meta name="viewport" content="width=device-width, user-scalable=no">
|
||||
|
||||
<style>
|
||||
body, html{margin: 0;padding: 0;}
|
||||
|
||||
body{
|
||||
font-size:20px;
|
||||
text-shadow: 1px 1px orange;
|
||||
font-family: times, 'times new roman', helvetica,serif;
|
||||
line-height:1.5em;
|
||||
color:#444;
|
||||
background:#fff;
|
||||
background-image: url(css/marble.jpg);
|
||||
}
|
||||
|
||||
textarea{
|
||||
-webkit-appearance: none; -moz-appearance: none;
|
||||
display: block;
|
||||
|
||||
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
width: 90%; height: 50px;
|
||||
line-height: 40px; font-size: 17px;
|
||||
border: 1px solid #bbb;
|
||||
-webkit-border-radius: 3px; -moz-border-radius: 3px; border-radius: 3px;
|
||||
background-image: url(css/aqua.jpg);
|
||||
font-family: inherit;
|
||||
font-size: inherit;
|
||||
font-size: 20px;
|
||||
}
|
||||
|
||||
|
||||
button {
|
||||
display: block;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
margin: 1.5em 0;
|
||||
font-family: inherit;
|
||||
font-size: inherit;
|
||||
font-size: 20px;
|
||||
}
|
||||
|
||||
select{
|
||||
width:100%;
|
||||
display: block;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
margin: 1.5em 0;
|
||||
font-family: inherit;
|
||||
font-size: inherit;
|
||||
font-size: 20px;
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
h2{
|
||||
font-size: 40px;
|
||||
}
|
||||
|
||||
hr{
|
||||
height:10px;
|
||||
border: 10;
|
||||
width:87%;
|
||||
background-image: url(css/bg1.gif);
|
||||
|
||||
}
|
||||
ul{
|
||||
width:95%;
|
||||
//margin-left: 0px;
|
||||
padding-left:30px;
|
||||
}
|
||||
li{
|
||||
|
||||
word-wrap:break-word;
|
||||
list-style: none;
|
||||
overflow: visible;
|
||||
padding:30px;
|
||||
-moz-box-shadow:0 0 23px #bbb;
|
||||
-webkit-box-shadow:0 0 23px #bbb;
|
||||
box-shadow:0 0 23px #bbb;
|
||||
-webkit-border-top-left-radius:123px;
|
||||
-webkit-border-top-right-radius:123px;
|
||||
-webkit-border-bottom-right-radius:123px;
|
||||
-webkit-border-bottom-left-radius:123px;
|
||||
-moz-border-radius-bottomright:123px;
|
||||
-moz-border-radius-bottomleft:123px;
|
||||
-moz-border-top-left-radius:123px;
|
||||
-moz-border-top-right-radius:123px;
|
||||
border-top-left-radius:123px;
|
||||
border-top-right-radius:123px;
|
||||
border-bottom-right-radius:123px;
|
||||
border-bottom-left-radius:123px;
|
||||
left:-35px;
|
||||
|
||||
width:100%;
|
||||
//overflow:hidden;
|
||||
position:relative;
|
||||
background:#fff;
|
||||
//background:-webkit-gradient(linear, left top, left bottom, from(#fff), to(#eee));
|
||||
//background:-moz-linear-gradient(top, #fff, #eee);
|
||||
}
|
||||
#outbox{
|
||||
display:none;
|
||||
}
|
||||
#header{
|
||||
width:100%;
|
||||
text-align: center;
|
||||
}
|
||||
#name{
|
||||
|
||||
}
|
||||
.hops .node{
|
||||
// display:hidden;
|
||||
}
|
||||
|
||||
</style>
|
||||
|
||||
<title>meshed up</title>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div id="header">
|
||||
<h2>Meshenger</h2>
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
<textarea id="name" rows="1" placeholder="Your Name (leave empty for anonymous)"></textarea>
|
||||
<textarea id="message" rows="3" placeholder="Your Message"></textarea>
|
||||
<button style="width:100%" id="send">Send</button>
|
||||
|
||||
<select id="messagesort" name="messageSort">
|
||||
<option value="dateReceived">Sort by Date Received</option>
|
||||
<option value="dateSend">Sort by Date Send</option>
|
||||
|
||||
</select>
|
||||
<ul id="inbox"></ul>
|
||||
|
||||
<!--<h2>outbox</h2>-->
|
||||
<h2>message</h2>
|
||||
<textarea id="message" rows="3" style="width:100%"></textarea>
|
||||
<button style="width:100%" id="send">Send</button>
|
||||
<h2>outbox</h2>
|
||||
<ul id="outbox"></ul>
|
||||
|
||||
<h2>inbox</h2>
|
||||
<ul id="inbox"></ul>
|
||||
|
||||
<script type="text/javascript">
|
||||
|
||||
localStorage.clear();
|
||||
/*
|
||||
* OUTBOX STUFF
|
||||
*/
|
||||
document.getElementById( 'send' ).onclick = function() {
|
||||
|
||||
var outStr = localStorage.getItem( 'outbox' ) || '';
|
||||
if (document.getElementById('name').value == ""){
|
||||
var namm= "anonymous";
|
||||
}
|
||||
else{
|
||||
var namm= document.getElementById('name').value;
|
||||
}
|
||||
var mess = document.getElementById('message').value.replace(/\r?\n/g, "<br />");
|
||||
var newMsgs ={};
|
||||
var ddata= new Date().getTime();
|
||||
var contento = {
|
||||
"time" : ddata,
|
||||
"message" : mess,
|
||||
"name" : namm,
|
||||
"node" : "local",
|
||||
"hops" : "0"
|
||||
}
|
||||
newMsgs.message = contento;
|
||||
|
||||
localStorage.setItem( 'outbox', JSON.stringify(newMsgs) );
|
||||
outStr += new Date().getTime() + ' ' + document.getElementById('message').value + '\n';
|
||||
localStorage.setItem( 'outbox', outStr );
|
||||
updateOutboxView();
|
||||
checkOutbox();
|
||||
document.getElementById('message').value = '';
|
||||
//localStorage.setItem(
|
||||
// new Date().getTime(),
|
||||
// document.getElementById('message').value );
|
||||
//updateList();
|
||||
};
|
||||
function checkOutbox() {
|
||||
var outStr = localStorage.getItem( 'outbox' );
|
||||
@ -193,10 +47,8 @@ function checkOutbox() {
|
||||
if ( lines[i].length === 0 ) {
|
||||
continue;
|
||||
}
|
||||
var obj = JSON.parse(lines[i]);
|
||||
var ts = obj.message.time;
|
||||
delete obj.message.time;
|
||||
var msg = JSON.stringify(obj.message);
|
||||
var ts = lines[ i ].substr( 0, lines[ i ].indexOf( ' ' ));
|
||||
var msg = lines[ i ].substr( lines[ i ].indexOf( ' ' ));
|
||||
sendMessage( ts, msg );
|
||||
}
|
||||
}
|
||||
@ -222,8 +74,7 @@ function removeOutboxItem( timestamp ) {
|
||||
var outStr = localStorage.getItem( 'outbox' ) || '';
|
||||
var lines = outStr.split( /\n/ );
|
||||
for ( var i in lines ) {
|
||||
var obj = JSON.parse(lines[i]);
|
||||
var ts = obj.message.time;
|
||||
var ts = lines[ i ].substr( 0, lines[ i ].indexOf( ' ' ));
|
||||
if ( ts === timestamp ) {
|
||||
lines.splice( i, 1 );
|
||||
break;
|
||||
@ -241,54 +92,36 @@ function updateOutboxView() {
|
||||
if ( lines[ i ].length === 0 ) {
|
||||
continue;
|
||||
}
|
||||
var obj = JSON.parse(lines[i]);
|
||||
var ts = obj.message.time;
|
||||
delete obj.message.time;
|
||||
var msg = JSON.stringify(obj.message);
|
||||
|
||||
var ts = lines[ i ].substr( 0, lines[ i ].indexOf( ' ' ));
|
||||
var msg = lines[ i ].substr( lines[ i ].indexOf( ' ' ));
|
||||
contentString += '<li><b>' + ts + ' </b>' + msg + '</li>';
|
||||
}
|
||||
document.getElementById( 'outbox' ).innerHTML = contentString;
|
||||
}
|
||||
|
||||
function doStuffForEach( entriesString, stuffFunction ) {
|
||||
if ( ! entriesString ) {
|
||||
return;
|
||||
}
|
||||
var lines = entriesString.split( /\n/ );
|
||||
for ( var i in lines ) {
|
||||
var ts = lines[ i ].substr( 0, lines[ i ].indexOf( ' ' ));
|
||||
var msg = lines[ i ].substr( lines[ i ].indexOf( ' ' ) + 1 );
|
||||
stuffFunction( ts, msg );
|
||||
}
|
||||
}
|
||||
/*
|
||||
* INBOX STUFF
|
||||
*/
|
||||
|
||||
|
||||
function updateInboxView() {
|
||||
var localStorageArray = new Array();
|
||||
|
||||
var contentString = '';
|
||||
|
||||
if (localStorage.length>0) {
|
||||
for (i=0;i<localStorage.length;i++){
|
||||
|
||||
element=localStorage.getItem(localStorage.key(i));
|
||||
|
||||
if ( localStorage.key(i).length < 10 || element === 'outbox' ) {
|
||||
continue;
|
||||
}
|
||||
// alert(element);
|
||||
try {
|
||||
elementj = JSON.parse(element);
|
||||
} catch (e) {
|
||||
continue;
|
||||
}
|
||||
|
||||
localStorageArray[i] = { time:localStorage.key(i), user:elementj.name, message:elementj.message, node:elementj.node, hops:elementj.hops };
|
||||
}
|
||||
}
|
||||
orderStorage = localStorageArray.sort(function(a,b) { return b.time - a.time } );
|
||||
|
||||
for(var i in orderStorage)
|
||||
for(var i in localStorage)
|
||||
{
|
||||
if ( i.length === 0 || i === 'outbox' ) {
|
||||
continue;
|
||||
}
|
||||
var date = new Date(parseInt(orderStorage[i].time));
|
||||
// date.setHours(date.getHours() + 2);
|
||||
var datereadable = date.getDate()+"/"+(date.getMonth()+1)+"/"+date.getFullYear()+" "+date.getHours()+":"+date.getMinutes();
|
||||
contentString += '<li><b>' + datereadable + ' </b>' + ' <i>'+ orderStorage[i].user +'</i><br/> '+orderStorage[i].message+' <span class="node '+orderStorage[i].node+'">'+orderStorage[i].node+'</span> <span class="hops '+orderStorage[i].hops+'">'+orderStorage[i].hops+'</span></li>';
|
||||
contentString += '<li><b>' + i + ' </b>' + localStorage[i] + '</li>';
|
||||
}
|
||||
document.getElementById( 'inbox' ).innerHTML = contentString;
|
||||
}
|
||||
@ -300,29 +133,14 @@ function onMessageDownload( msg, filename ) {
|
||||
updateInboxView();
|
||||
}
|
||||
function onIndex( index ) {
|
||||
var lines = index.split( /\n/ );
|
||||
|
||||
for(var k in localStorage){
|
||||
var l = 1;
|
||||
for ( var i in lines ) {
|
||||
var f = lines[i];
|
||||
if (f == k){ l = 0; }
|
||||
}
|
||||
if (l == 1){
|
||||
localStorage.removeItem(k);
|
||||
}
|
||||
}
|
||||
updateInboxView();
|
||||
|
||||
var lines = index.split( /\n/ );
|
||||
for ( var i in lines ) {
|
||||
var fname = lines[i];
|
||||
if ( localStorage.getItem( fname ) === null ) {
|
||||
//localStorage.setItem( ts, lines[i].substr(lines[i].indexOf(' ')) );
|
||||
downloadMessage( fname );
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
function downloadMessage(filename) {
|
||||
var xhr = new XMLHttpRequest();
|
||||
@ -355,7 +173,7 @@ updateOutboxView();
|
||||
window.setInterval( function(){
|
||||
checkInbox();
|
||||
checkOutbox();
|
||||
}, 7000 );
|
||||
}, 10000 );
|
||||
|
||||
</script>
|
||||
|
||||
|