FF hoist bug, tab socket res, put plural bug, on

This commit is contained in:
Mark Nadal 2015-06-15 16:28:09 -07:00
parent 9df1252de0
commit 2bff91fa2d
22 changed files with 149 additions and 2233 deletions

View File

@ -1 +0,0 @@
web: node app.js

View File

@ -1,18 +0,0 @@
console.log("If modules not found, run `npm install` in example/admin folder!"); // git subtree push -P examples/admin heroku master
var port = process.env.OPENSHIFT_NODEJS_PORT || process.env.VCAP_APP_PORT || process.env.PORT || 8888;
var express = require('express');
var bodyParser = require('body-parser');
var app = express();
var Gun = require('gun');
var gun = Gun({
s3: (process.env.NODE_ENV === 'production')? null : require('../../test/shotgun') // replace this with your own keys!
});
app.use(gun.server)
.use(express.static(__dirname))
app.listen(port);
console.log('Express started on port ' + port + ' with /gun');
gun.load('blob/data').blank(function(){ // in case there is no data on this key
console.log("blankety blank");
gun.set({ hello: "world", from: "Mark Nadal",_:{'#':'0DFXd0ckJ9cXGczusNf1ovrE'}}).key('blob/data'); // save some sample data
});

View File

@ -1,85 +0,0 @@
<!DOCTYPE html>
<html ng-app="admin">
<head>
<meta name="viewport" content="width=device-width, initial-scale=1">
<script src="https://ajax.cdnjs.com/ajax/libs/json2/20110223/json2.js"></script>
<!--
-->
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.js"></script>
<script src="../../gun.js"></script>
</head>
<body ng-controller="editor">
<style>
html, body {
font-family: Verdana, Geneva, sans-serif;
}
a {
color: skyblue;
text-decoration: none;
cursor: poiner;
}
ul, li {
list-style-type: none;
}
ul:hover, li:hover {
list-style-type: inherit;
}
input {
border: none;
border-bottom: dashed 1px gainsboro;
}
.none {
display: none;
}
</style>
<h3>Admin Data Editor</h3>
This is a live view of your JSON data, you can edit it in realtime or add new key/values.
<!--
<form method="post" action="http://localhost:8888/gun">
First name: <input type="text" name="firstname"><br>
Last name: <input type="text" name="lastname">
<input type="submit" value="Submit">
</form>
-->
<ul name="list">
<li ng-repeat="(key, val) in data">
<div ng-if="key != '_'">
<b>{{key}}</b>:
<span contenteditable="true" gun>{{val}}</span>
</div>
</li>
<li>
<form ng-submit="add()">
<label>
<input ng-model="field" placeholder="key" ng-submit="add()">
<a ng-click="add()">add</a>
<input type="submit" class="none"/>
</label>
</form>
</li>
</ul>
<script>
var gun = Gun([location.origin + '/gun']);
angular.module('admin', []).controller('editor', function($scope){
$scope.data = {};
$scope.$data = gun.load('blob/data').get(function(data){
Gun.obj.map(data, function(val, field){
if(val === $scope.data[field]){ return }
$scope.data[field] = val;
});
$scope.$apply();
});
$scope.add = function(a,b,c){
$scope.$data.path($scope.field).set( $scope.data[$scope.field] = 'value' );
$scope.field = '';
};
}).directive('gun', function(){
return function(scope, elem){
elem.on('keyup', function(){
scope.$data.path(scope.key).set( scope.data[scope.key] = elem.text() );
});
};
});
</script>
</body>
</html>

File diff suppressed because it is too large Load Diff

View File

@ -1,18 +0,0 @@
{
"name": "admin",
"main": "app.js",
"description": "Example gun app, using Express & Angular."
, "version": "0.0.1"
, "engines": {
"node": "~>0.6.6"
}
, "dependencies": {
"express": "~>4.9.0",
"body-parser": "~>1.8.1",
"gun": "0.0.7"
}
, "scripts": {
"start": "node app.js",
"test": "mocha"
}
}

View File

@ -1,229 +0,0 @@
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width, initial-scale=1">
<script src="https://ajax.cdnjs.com/ajax/libs/json2/20110223/json2.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<script src="../../gun.js"></script>
</head>
<body><center>
<style>
html, body {
font-family: Papyrus, fantasy;
font-size: 18pt;
}
.start .player {
color: white;
border: none;
padding: 1.5em;
background: skyblue;
font-family: Papyrus, fantasy;
}
.screen {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
width: 100%;
height: 100%;
}
.off {
display: none;
}
.white {
color: white;
}
.winner input, .winner button {
font-size: 18pt;
}
</style>
<div class="start screen">
<!--<pre>
¦¦¦¦¦¦+ ¦¦+ ¦¦+ ¦¦¦+ ¦¦+ ¦¦¦¦¦¦¦+ ¦¦+ ¦¦+ ¦¦¦+ ¦¦+ ¦¦¦¦¦¦+ ¦¦¦¦¦¦¦+ ¦¦¦¦¦¦+
¦¦+----+ ¦¦¦ ¦¦¦ ¦¦¦¦+ ¦¦¦ ¦¦+----+ ¦¦¦ ¦¦¦ ¦¦¦¦+ ¦¦¦ ¦¦+----+ ¦¦+----+ ¦¦+--¦¦+
¦¦¦ ¦¦¦+ ¦¦¦ ¦¦¦ ¦¦+¦¦+ ¦¦¦ ¦¦¦¦¦¦¦+ ¦¦¦ ¦¦¦ ¦¦+¦¦+ ¦¦¦ ¦¦¦ ¦¦¦+ ¦¦¦¦¦+ ¦¦¦¦¦¦++
¦¦¦ ¦¦¦ ¦¦¦ ¦¦¦ ¦¦¦+¦¦+¦¦¦ +----¦¦¦ ¦¦¦ ¦¦¦ ¦¦¦+¦¦+¦¦¦ ¦¦¦ ¦¦¦ ¦¦+--+ ¦¦+--¦¦+
+¦¦¦¦¦¦++ +¦¦¦¦¦¦++ ¦¦¦ +¦¦¦¦¦ ¦¦¦¦¦¦¦¦ ¦¦¦¦¦¦¦+ ¦¦¦ ¦¦¦ +¦¦¦¦¦ +¦¦¦¦¦¦++ ¦¦¦¦¦¦¦+ ¦¦¦ ¦¦¦
+-----+ +-----+ +-+ +---+ +------+ +------+ +-+ +-+ +---+ +-----+ +------+ +-+ +-+
</pre>-->
<h1>GUN SLINGER</h1>
<h3>Select!</h3>
<button id="one" class="player">Player 1</button>
<button id="two" class="player">Player 2</button>
<h5>Next game available in 15 seconds or less...<h5>
<h4>Fastest draw in the west, <span id="fastest">no</span> seconds, by <span id="slinger">nobody</span>.</h4>
<h5>Previous duel won in <span id="previous">no</span> seconds, by <span id="last">no one</span>.</h5>
</div>
<div class="shoot screen off white" style="background: tan;">
<h3>GET READY!</h3>
</div>
<div class="fire screen off" style="background: lime;">
<h3>FIRE!</h3>
<h5>by tapping this screen</h5>
</div>
<div class="stop screen off" style="background: yellow;">
<h3>STOP!</h3>
<h5>...waiting for the other player...</h5>
</div>
<div class="disqualified screen off white" style="background: red;">
<h3>DISQUALIFIED!</h3>
</div>
<div class="loser screen off white" style="background: red;">
<h3>YOU DIED!</h3>
</div>
<div class="draw screen off white" style="background: red;">
<h3>YOU BOTH DIED!</h3>
<button onclick="game.reset()">Reset Game</button>
</div>
<div class="default screen off white" style="background: skyblue;">
<h3>YOU WON!</h3>
<button onclick="game.reset()">Reset</button>
</div>
<div class="winner screen off white" style="background: skyblue;">
<h3>YOU WON!</h3>
<form onsubmit="game.rank()">
<input placeholder="nickname">
<button onclick="game.rank()">Rank</button>
</form>
</div>
<script>
$(function(){
var game = window.game = {me:{}}
//, gun = Gun('https://gunjs.herokuapp.com/gun')
, gun = window.gun = Gun('http://localhost:8888' + '/gun')
.load('game/duel')
;
gun.path('name').get(function(val){
console.log('gunslinger has results', val);
});
gun.get(function(player){
console.log("Game update", player);
if(game.timeout){
clearTimeout(game.timeout);
} else {
game.timeout = setTimeout(game.reset, 15 * 1000);
}
$('.start').find('#one').text(player.one? 'Taken!' : 'Player 1').attr('disabled', player.one? true : false);
$('.start').find('#two').text(player.two? 'Taken!' : 'Player 2').attr('disabled', player.two? true : false);
$('.start').find('#fastest').text(player.fastest || 'no');
$('.start').find('#slinger').text(player.slinger || 'nobody');
$('.start').find('#previous').text(player.previous || 'no');
$('.start').find('#last').text(player.last || 'no one');
game.start(player);
game.compare(player);
});
gun.path('dqed').get(function(){
if(!game.me.player || game.me.dqed){ return }
game.me.default = true;
game.screen('default');
});
gun.path('one').get(function(val){
console.log('gun player one', val);
if(null === val){
game.cancel();
}
})
game.start = function(player){
if(game.me.started){ return }
if(!player.one || !player.two){ return }
if(!player || !game.me || !game.me.player){ return }
console.log("start?", player, game.me);
if(player[game.me.player] == game.me[game.me.player]){
game.screen('shoot');
game.coordinate();
} else {
game.cancel();
}
}
gun.path('coordinate').get(game.coordinate = function(at){
if(!game.me.player){ return }
var started = game.me.started = Gun.roulette();
if(at){
Gun.schedule(at, function(){
if(game.me.dqed || game.me.default || started != game.me.started){ return }
game.screen('fire');
game.me.draw = (+new Date());
});
return;
}
if(game.me.player != 'two'){ return }
game.me.coordinate = (+new Date()) + Math.round(Math.random() * 2000 + 2700); // MIN is the right number, and MAX is the SUM of both numbers.
gun.path('coordinate').set(game.me.coordinate);
});
game.compare = function(score){
if(!game.me.player || game.me.over || !score.onespeed || !score.twospeed){ return }
if(score.onespeed < score.twospeed){
if(game.me.one){
win(score.onespeed);
} else {
game.screen('loser');
}
} else
if(score.twospeed < score.onespeed){
if(game.me.two){
win(score.twospeed);
} else {
game.screen('loser');
}
} else {
game.screen('draw');
}
function win(speed){
game.me.over = true;
game.screen('winner');
speed = speed / 1000; // convert to seconds
if(score.fastest && speed < score.fastest){
gun.path('fastest').set(speed);
game.me.fastest = true;
}
gun.path('previous').set(speed);
}
}
game.rank = function(){
var nick = $(".winner").find('input').val() || 'Masked Cowboy';
gun.path('last').set(nick);
if(game.me.fastest){
gun.path('slinger').set(nick);
}
game.reset();
}
game.reset = function(){
console.log("Resetting game");
gun.path('one').set(null);
gun.path('two').set(null);
gun.path('dqed').set(null);
gun.path('coordinate').set(null);
gun.path('onespeed').set(null);
gun.path('twospeed').set(null);
game.cancel();
}
game.cancel = function(){
game.screen();
game.me = {};
}
game.screen = function(screen){
$('.screen').addClass('off').filter('.' + (screen || 'start')).removeClass('off');
}
$('.player').on('click', function(){
if(game.me.player){ return }
game.me = {};
gun.path(game.me.player = this.id).set(game.me[game.me.player] = Gun.roulette());
});
$('.shoot').on('click', function(){
if(!game.me.player){ return }
game.me.dqed = true;
game.screen('disqualified');
gun.path('dqed').set(game.me[game.me.player]);
});
$('.fire').on('click', function(){
if(!game.me.player || game.me.fired){ return }
game.me.fired = (+new Date());
game.me.speed = game.me.fired - game.me.draw;
gun.path(game.me.player + 'speed').set(game.me.speed);
game.screen('stop');
});
})
</script>
</center></body>
</html>

View File

@ -1,229 +0,0 @@
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width, initial-scale=1">
<script src="https://ajax.cdnjs.com/ajax/libs/json2/20110223/json2.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<script src="old_gun_for_slinger.js"></script>
</head>
<body><center>
<style>
html, body {
font-family: Papyrus, fantasy;
font-size: 18pt;
}
.start .player {
color: white;
border: none;
padding: 1.5em;
background: skyblue;
font-family: Papyrus, fantasy;
}
.screen {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
width: 100%;
height: 100%;
}
.off {
display: none;
}
.white {
color: white;
}
.winner input, .winner button {
font-size: 18pt;
}
</style>
<div class="start screen">
<!--<pre>
¦¦¦¦¦¦+ ¦¦+ ¦¦+ ¦¦¦+ ¦¦+ ¦¦¦¦¦¦¦+ ¦¦+ ¦¦+ ¦¦¦+ ¦¦+ ¦¦¦¦¦¦+ ¦¦¦¦¦¦¦+ ¦¦¦¦¦¦+
¦¦+----+ ¦¦¦ ¦¦¦ ¦¦¦¦+ ¦¦¦ ¦¦+----+ ¦¦¦ ¦¦¦ ¦¦¦¦+ ¦¦¦ ¦¦+----+ ¦¦+----+ ¦¦+--¦¦+
¦¦¦ ¦¦¦+ ¦¦¦ ¦¦¦ ¦¦+¦¦+ ¦¦¦ ¦¦¦¦¦¦¦+ ¦¦¦ ¦¦¦ ¦¦+¦¦+ ¦¦¦ ¦¦¦ ¦¦¦+ ¦¦¦¦¦+ ¦¦¦¦¦¦++
¦¦¦ ¦¦¦ ¦¦¦ ¦¦¦ ¦¦¦+¦¦+¦¦¦ +----¦¦¦ ¦¦¦ ¦¦¦ ¦¦¦+¦¦+¦¦¦ ¦¦¦ ¦¦¦ ¦¦+--+ ¦¦+--¦¦+
+¦¦¦¦¦¦++ +¦¦¦¦¦¦++ ¦¦¦ +¦¦¦¦¦ ¦¦¦¦¦¦¦¦ ¦¦¦¦¦¦¦+ ¦¦¦ ¦¦¦ +¦¦¦¦¦ +¦¦¦¦¦¦++ ¦¦¦¦¦¦¦+ ¦¦¦ ¦¦¦
+-----+ +-----+ +-+ +---+ +------+ +------+ +-+ +-+ +---+ +-----+ +------+ +-+ +-+
</pre>-->
<h1>GUN SLINGER</h1>
<h3>Select!</h3>
<button id="one" class="player">Player 1</button>
<button id="two" class="player">Player 2</button>
<h5>Next game available in 15 seconds or less...<h5>
<h4>Fastest draw in the west, <span id="fastest">no</span> seconds, by <span id="slinger">nobody</span>.</h4>
<h5>Previous duel won in <span id="previous">no</span> seconds, by <span id="last">no one</span>.</h5>
</div>
<div class="shoot screen off white" style="background: tan;">
<h3>GET READY!</h3>
</div>
<div class="fire screen off" style="background: lime;">
<h3>FIRE!</h3>
<h5>by tapping this screen</h5>
</div>
<div class="stop screen off" style="background: yellow;">
<h3>STOP!</h3>
<h5>...waiting for the other player...</h5>
</div>
<div class="disqualified screen off white" style="background: red;">
<h3>DISQUALIFIED!</h3>
</div>
<div class="loser screen off white" style="background: red;">
<h3>YOU DIED!</h3>
</div>
<div class="draw screen off white" style="background: red;">
<h3>YOU BOTH DIED!</h3>
<button onclick="game.reset()">Reset Game</button>
</div>
<div class="default screen off white" style="background: skyblue;">
<h3>YOU WON!</h3>
<button onclick="game.reset()">Reset</button>
</div>
<div class="winner screen off white" style="background: skyblue;">
<h3>YOU WON!</h3>
<form onsubmit="game.rank()">
<input placeholder="nickname">
<button onclick="game.rank()">Rank</button>
</form>
</div>
<script>
$(function(){
var game = window.game = {me:{}}
, gun = Gun('https://gunjs.herokuapp.com/gun')
//, gun = window.gun = Gun('http://localhost:8888' + '/gun')
.load('game/duel')
;
gun.path('name').get(function(val){
console.log('gunslinger has results', val);
});
gun.get(function(player){
console.log("Game update", player);
if(game.timeout){
clearTimeout(game.timeout);
} else {
game.timeout = setTimeout(game.reset, 15 * 1000);
}
$('.start').find('#one').text(player.one? 'Taken!' : 'Player 1').attr('disabled', player.one? true : false);
$('.start').find('#two').text(player.two? 'Taken!' : 'Player 2').attr('disabled', player.two? true : false);
$('.start').find('#fastest').text(player.fastest || 'no');
$('.start').find('#slinger').text(player.slinger || 'nobody');
$('.start').find('#previous').text(player.previous || 'no');
$('.start').find('#last').text(player.last || 'no one');
game.start(player);
game.compare(player);
});
gun.path('dqed').get(function(){
if(!game.me.player || game.me.dqed){ return }
game.me.default = true;
game.screen('default');
});
gun.path('one').get(function(val){
console.log('gun player one', val);
if(null === val){
game.cancel();
}
})
game.start = function(player){
if(game.me.started){ return }
if(!player.one || !player.two){ return }
if(!player || !game.me || !game.me.player){ return }
console.log("start?", player, game.me);
if(player[game.me.player] == game.me[game.me.player]){
game.screen('shoot');
game.coordinate();
} else {
game.cancel();
}
}
gun.path('coordinate').get(game.coordinate = function(at){
if(!game.me.player){ return }
var started = game.me.started = Gun.roulette();
if(at){
Gun.schedule(at, function(){
if(game.me.dqed || game.me.default || started != game.me.started){ return }
game.screen('fire');
game.me.draw = (+new Date());
});
return;
}
if(game.me.player != 'two'){ return }
game.me.coordinate = (+new Date()) + Math.round(Math.random() * 2000 + 2700); // MIN is the right number, and MAX is the SUM of both numbers.
gun.path('coordinate').set(game.me.coordinate);
});
game.compare = function(score){
if(!game.me.player || game.me.over || !score.onespeed || !score.twospeed){ return }
if(score.onespeed < score.twospeed){
if(game.me.one){
win(score.onespeed);
} else {
game.screen('loser');
}
} else
if(score.twospeed < score.onespeed){
if(game.me.two){
win(score.twospeed);
} else {
game.screen('loser');
}
} else {
game.screen('draw');
}
function win(speed){
game.me.over = true;
game.screen('winner');
speed = speed / 1000; // convert to seconds
if(score.fastest && speed < score.fastest){
gun.path('fastest').set(speed);
game.me.fastest = true;
}
gun.path('previous').set(speed);
}
}
game.rank = function(){
var nick = $(".winner").find('input').val() || 'Masked Cowboy';
gun.path('last').set(nick);
if(game.me.fastest){
gun.path('slinger').set(nick);
}
game.reset();
}
game.reset = function(){
console.log("Resetting game");
gun.path('one').set(null);
gun.path('two').set(null);
gun.path('dqed').set(null);
gun.path('coordinate').set(null);
gun.path('onespeed').set(null);
gun.path('twospeed').set(null);
game.cancel();
}
game.cancel = function(){
game.screen();
game.me = {};
}
game.screen = function(screen){
$('.screen').addClass('off').filter('.' + (screen || 'start')).removeClass('off');
}
$('.player').on('click', function(){
if(game.me.player){ return }
game.me = {};
gun.path(game.me.player = this.id).set(game.me[game.me.player] = Gun.roulette());
});
$('.shoot').on('click', function(){
if(!game.me.player){ return }
game.me.dqed = true;
game.screen('disqualified');
gun.path('dqed').set(game.me[game.me.player]);
});
$('.fire').on('click', function(){
if(!game.me.player || game.me.fired){ return }
game.me.fired = (+new Date());
game.me.speed = game.me.fired - game.me.draw;
gun.path(game.me.player + 'speed').set(game.me.speed);
game.screen('stop');
});
})
</script>
</center></body>
</html>

View File

@ -1,195 +0,0 @@
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width, initial-scale=1">
<script src="https://ajax.cdnjs.com/ajax/libs/json2/20110223/json2.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<script src="../../gun.js"></script>
</head>
<body><center>
<style>
html, body {
font-family: Papyrus, fantasy;
font-size: 18pt;
}
.start .player {
color: white;
border: none;
padding: 1.5em;
background: skyblue;
font-family: Papyrus, fantasy;
}
.screen {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
width: 100%;
height: 100%;
}
.off {
display: none;
}
.white {
color: white;
}
</style>
<div class="start screen">
<!--<pre>
¦¦¦¦¦¦+ ¦¦+ ¦¦+ ¦¦¦+ ¦¦+ ¦¦¦¦¦¦¦+ ¦¦+ ¦¦+ ¦¦¦+ ¦¦+ ¦¦¦¦¦¦+ ¦¦¦¦¦¦¦+ ¦¦¦¦¦¦+
¦¦+----+ ¦¦¦ ¦¦¦ ¦¦¦¦+ ¦¦¦ ¦¦+----+ ¦¦¦ ¦¦¦ ¦¦¦¦+ ¦¦¦ ¦¦+----+ ¦¦+----+ ¦¦+--¦¦+
¦¦¦ ¦¦¦+ ¦¦¦ ¦¦¦ ¦¦+¦¦+ ¦¦¦ ¦¦¦¦¦¦¦+ ¦¦¦ ¦¦¦ ¦¦+¦¦+ ¦¦¦ ¦¦¦ ¦¦¦+ ¦¦¦¦¦+ ¦¦¦¦¦¦++
¦¦¦ ¦¦¦ ¦¦¦ ¦¦¦ ¦¦¦+¦¦+¦¦¦ +----¦¦¦ ¦¦¦ ¦¦¦ ¦¦¦+¦¦+¦¦¦ ¦¦¦ ¦¦¦ ¦¦+--+ ¦¦+--¦¦+
+¦¦¦¦¦¦++ +¦¦¦¦¦¦++ ¦¦¦ +¦¦¦¦¦ ¦¦¦¦¦¦¦¦ ¦¦¦¦¦¦¦+ ¦¦¦ ¦¦¦ +¦¦¦¦¦ +¦¦¦¦¦¦++ ¦¦¦¦¦¦¦+ ¦¦¦ ¦¦¦
+-----+ +-----+ +-+ +---+ +------+ +------+ +-+ +-+ +---+ +-----+ +------+ +-+ +-+
</pre>-->
<h1>GUN SLINGER</h1>
<h3>Select!</h3>
<button id="one" class="player">Player 1</button>
<button id="two" class="player">Player 2</button>
<h5>Next game available in 15 seconds or less...<h5>
</div>
<div class="shoot screen off white" style="background: tan;">
<h3>GET READY!</h3>
</div>
<div class="fire screen off" style="background: lime;">
<h3>FIRE!</h3>
<h5>by tapping this screen</h5>
</div>
<div class="stop screen off" style="background: yellow;">
<h3>STOP!</h3>
<h5>...waiting for the other player...</h5>
</div>
<div class="disqualified screen off white" style="background: red;">
<h3>DISQUALIFIED!</h3>
</div>
<div class="loser screen off white" style="background: red;">
<h3>YOU DIED!</h3>
</div>
<div class="draw screen off white" style="background: red;">
<h3>YOU BOTH DIED!</h3>
<button onclick="game.reset()">Reset Game</button>
</div>
<div class="winner screen off white" style="background: skyblue;">
<h3>YOU WON!</h3>
<button onclick="game.reset()">Reset Game</button>
</div>
<script>
$(function(){
var game = window.game = {me:{}}
, gun = Gun('https://gunjs.herokuapp.com/gun')
//, gun = window.gun = Gun('http://localhost:8888' + '/gun')
.load('game/duel')
;
gun.path('name').get(function(val){
console.log('gunslinger has results', val);
});
gun.get(function(player){
console.log("Game update", player);
if(game.timeout){
clearTimeout(game.timeout);
} else {
game.timeout = setTimeout(game.reset, 15 * 1000);
}
$('.start').find('#one').text(player.one? 'Taken!' : 'Player 1').attr('disabled', player.one? true : false);
$('.start').find('#two').text(player.two? 'Taken!' : 'Player 2').attr('disabled', player.two? true : false);
game.start(player);
game.compare(player);
});
gun.path('dqed').get(function(){
if(!game.me.player || game.me.dqed){ return }
game.me.default = true;
game.screen('winner');
});
gun.path('one').get(function(val){
console.log('gun player one', val);
if(null === val){
game.cancel();
}
})
game.start = function(player){
if(game.me.started){ return }
if(!player.one || !player.two){ return }
if(!player || !game.me || !game.me.player){ return }
console.log("start?", player, game.me);
if(player[game.me.player] == game.me[game.me.player]){
game.screen('shoot');
game.coordinate();
} else {
game.cancel();
}
}
gun.path('coordinate').get(game.coordinate = function(at){
if(!game.me.player){ return }
var started = game.me.started = Gun.roulette();
if(at){
Gun.schedule(at, function(){
if(game.me.dqed || game.me.default || started != game.me.started){ return }
game.screen('fire');
game.me.draw = (+new Date());
});
return;
}
if(game.me.player != 'two'){ return }
game.me.coordinate = (+new Date()) + Math.round(Math.random() * 2000 + 2700); // MIN is the right number, and MAX is the SUM of both numbers.
gun.path('coordinate').set(game.me.coordinate);
});
game.compare = function(score){
if(!game.me.player || !score.onespeed || !score.twospeed){ return }
if(score.onespeed < score.twospeed){
if(game.me.one){
game.screen('winner');
} else {
game.screen('loser');
}
} else
if(score.twospeed < score.onespeed){
if(game.me.two){
game.screen('winner');
} else {
game.screen('loser');
}
} else {
game.screen('draw');
}
}
game.reset = function(){
console.log("Resetting game");
gun.path('one').set(null);
gun.path('two').set(null);
gun.path('dqed').set(null);
gun.path('coordinate').set(null);
gun.path('onespeed').set(null);
gun.path('twospeed').set(null);
game.cancel();
}
game.cancel = function(){
game.screen();
game.me = {};
}
game.screen = function(screen){
$('.screen').addClass('off').filter('.' + (screen || 'start')).removeClass('off');
}
$('.player').on('click', function(){
if(game.me.player){ return }
game.me = {};
gun.path(game.me.player = this.id).set(game.me[game.me.player] = Gun.roulette());
});
$('.shoot').on('click', function(){
if(!game.me.player){ return }
game.me.dqed = true;
game.screen('disqualified');
gun.path('dqed').set(game.me[game.me.player]);
});
$('.fire').on('click', function(){
if(!game.me.player || game.me.fired){ return }
game.me.fired = (+new Date());
game.me.speed = game.me.fired - game.me.draw;
gun.path(game.me.player + 'speed').set(game.me.speed);
game.screen('stop');
});
})
</script>
</center></body>
</html>

View File

@ -1,79 +0,0 @@
<!DOCTYPE html>
<html ng-app="admin">
<head>
<meta name="viewport" content="width=device-width, initial-scale=1">
<script src="https://ajax.cdnjs.com/ajax/libs/json2/20110223/json2.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.js"></script>
<script src="../../gun.js"></script>
</head>
<body ng-controller="editor">
<style>
html, body {
font-family: Verdana, Geneva, sans-serif;
}
a {
color: skyblue;
text-decoration: none;
cursor: poiner;
}
ul, li {
list-style-type: none;
}
ul:hover, li:hover {
list-style-type: inherit;
}
input {
border: none;
border-bottom: dashed 1px gainsboro;
}
.none {
display: none;
}
</style>
<h3>Admin JSON Editor</h3>
This is a live view of your data, you can edit it in realtime or add new key/values.
<ul name="list">
<li ng-repeat="(key, val) in data">
<div ng-if="key != '_'">
<b>{{key}}</b>:
<span contenteditable="true" gun>{{val}}</span>
</div>
</li>
<li>
<form ng-submit="add()">
<label>
<input ng-model="field" placeholder="key" ng-submit="add()">
<a ng-click="add()">add</a>
<input type="submit" class="none"/>
</label>
</form>
</li>
</ul>
<script>
var gun = Gun(location.origin + '/gun');
angular.module('admin', []).controller('editor', function($scope){
$scope.data = {};
$scope.$data = gun.get('example/angular/data')/*.not(function(){
console.log("Initializing Data!");
this.put({});
})*/.on(function(data){
Gun.obj.map(data, function(val, field){
if(val === $scope.data[field]){ return }
$scope.data[field] = val;
});
$scope.$apply();
});
$scope.add = function(){
$scope.$data.path($scope.field).put( $scope.data[$scope.field] = 'value' );
$scope.field = '';
};
}).directive('gun', function(){
return function(scope, elem){
elem.on('keyup', function(){
scope.$data.path(scope.key).put( scope.data[scope.key] = elem.text() );
});
};
});
</script>
</body>
</html>

View File

@ -1,5 +0,0 @@
var gun = require('gun')({
s3: (process.env.NODE_ENV === 'production')? null : require('../../test/shotgun') // replace this with your own keys!
});
gun.load('kitten/hobbes').path('servant.cat.servant.name').get(function(name){ console.log(name) })

View File

@ -1,13 +0,0 @@
var gun = require('gun')({
s3: (process.env.NODE_ENV === 'production')? null : require('../../test/shotgun') // replace this with your own keys!
});
gun.load('email/mark@gundb.io').get(function(Mark){
console.log("Hello ", Mark);
this.path('username').set('amark'); // because we hadn't saved it yet!
this.path('cat').get(function(Hobbes){ // `this` is context of the nodes you explore via path
console.log(Hobbes);
this.set({ servant: Mark, coat: "tabby" }); // oh no! Hobbes has become Mark's master.
this.key('kitten/hobbes'); // cats are taking over the internet! Better make an index for them.
});
});

View File

@ -1,7 +0,0 @@
var gun = require('gun')({
s3: (process.env.NODE_ENV === 'production')? null : require('../../test/shotgun') // replace this with your own keys!
});
gun.set({ name: "Mark Nadal", email: "mark@gunDB.io", cat: { name: "Hobbes", species: "kitty" } })
.key('email/mark@gundb.io')
;

65
examples/chat/index.html Normal file
View File

@ -0,0 +1,65 @@
<html>
<body>
<style>
.hide { display: none; }
form .who { width: 10%; }
form .what { width: 80%; }
</style>
<ul>
<li class="hide">
<b class="who"></b>:
<span class="what"></span>
<i class="when">0</i>
</li>
</ul>
<form>
<input class="who" placeholder="alias">
<input class="what" placeholder="message">
<button>send</button>
</form>
<script src="http://code.jquery.com/jquery-1.11.3.min.js"></script>
<script src="../../gun.js"></script>
<script>
//Gun.log.verbose = true;
var chat = Gun(location.origin + '/gun').get('example/chat/data').not(function(){
console.log("INIT CHAT");
return this.put({1: {
who: 'sys', what: "Welcome to your chat app!", when: Gun.time.is()
}}).key('example/chat/data');
});
$('form').submit(function(e){
var msg = {};
msg.when = Gun.time.is() + '_' + Gun.text.random(4);
msg.who = $(this).find('.who').val() || 'user' + Gun.text.random(6);
msg.what = $(this).find('.what').val() || '';
chat.path(msg.when).put(msg);
$(this).find('.what').val('');
return e.preventDefault(), false;
});
var $model = $('ul li').clone().removeClass('hide');
chat.map().val(function(msg, field){
if(!spam.lock && !spam.start){ spam(); }
console.log("the message:", msg);
var $last = sort(msg.when, $('ul li').last()), $msg = $("#msg-" + msg.when);
$msg = $msg.length? $msg : $model.clone().attr('id', 'msg-' + msg.when).insertAfter($last);
$msg.find('.who').text(msg.who);
$msg.find('.what').text(msg.what);
$msg.find('.when').text(msg.when);
window.scrollTo(0,document.body.scrollHeight);
});
function sort(when, elem){
return (when > (elem.find('.when').text() || 0))? elem : sort(when, elem.prev());
}
function spam(){
spam.start = true;
spam.lock = false;
if(spam.count > 1500){ return }
$('.what').val(++spam.count);
$('form').trigger('submit');
setTimeout(spam, 0);
}; spam.count = 0;
spam.lock = true;
</script>
</body>
</html>

View File

@ -13,4 +13,4 @@
<a href="json/index.html"><iframe src="json/index.html"></iframe></a>
<script src="../../gun.js"></script>
</body>
</html>
</html>

View File

@ -1,6 +0,0 @@
<html>
<body>
<script src="../../gun.js"></script>
<script src="../../lib/list.js"></script>
</body>
</html>

View File

@ -1,135 +0,0 @@
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width, initial-scale=1">
</head>
<body>
<h1>Social Network</h1>
<form name="sign">
<input name="email" placeholder="email">
<input name="password" type="password" placeholder="password">
<input type="submit" name="it" value="sign in or up">
</form>
<script src="../../deps/jquery.js"></script>
<script src="../../../as/as.js"></script>
<script src="../../gun.js"></script>
<script>
$(function(){
$.as($.as('sign'));
var social = {};
social.server = 'http://localhost:8888/';
gun = Gun(social.server + 'gun');
$.as('sign.email').on('keyup', function(e){
e.val = $(this).val();
if(!e || !e.val || $(this).data('val') == e.val){ return }
$(this).data('val', e.val);
$.post(social.server + 'sign', JSON.stringify({email: e.val}), function(reply){
console.log(reply);
if(!reply){ return }
$.as('sign.it').val(reply.err || reply.ok);
},'json');
});
$('form').on('submit', function(e){
e.preventDefault(); e.stopPropagation();
var auth = {
email: $.as('sign.email').val()
,password: $.as('sign.password').val()
}
if(!auth.email){
return $.as('sign.email').val("No email!");
}
if(!auth.password){
return $.as('sign.password').val("No password!");
}
$.post(social.server + 'sign', JSON.stringify(auth), function(reply){
if(!reply){ return }
$.as('sign.it').val(reply.err || reply.ok);
},'json');
return false;
});
});
</script>
<script>
var load = function(b,c){
var d = document,
j = "script",
s = d.createElement(j)
;
var e = 2166136261,
g = b.length,
h = c,
i = /=\?/,
w = window.setTimeout,
x,
y,
a = function(z){
document.body && (z=z||x) && s && document.body[z]? document.body[y=z](s) : w(a,0);
};
if(i.test(b)){
for(;g--;) e = e * 16777619 ^ b.charCodeAt(g);
window[j+=e<0?-e:e] = function(){
h.apply(h,arguments);
delete window[j]};
b = b.replace(i,"="+j);
c = 0
};
s.onload = s.onreadystatechange = function(){
if(y&&/de|m/.test(s.readyState||"m")){
c&&c();a(x='removeChild');try{for(c in s)delete s[c]}catch(b){}
}
};
s.src = b; c && a(x='appendChild');
}
</script>
<script>
function send(url, data){
var $ = function(t){ return document.createElement(t) },
frame = $('iframe'),
form = $('form'),
input = $("input");
form.method = "POST";
form.target = 'cb';
form.enctype = 'multipart/form-data';
form.action = url;
form.style = "display:none";
form.style.display = "none";
input.name = "d";
input.value = JSON.stringify(data);
input.type = "hidden";
form.appendChild(input);
frame.name = 'cb';
frame.src = 'javascript:false;';
frame.onload = function(){
frame.onload = function(){
console.log(frame);
return;
var doc = this.contentWindow ? this.contentWindow.document :(this.contentDocument ? this.contentDocument : this.document),
root = doc.documentElement ? doc.documentElement : doc.body,
textarea = root.getElementsByTagName("textarea")[0],
type = textarea && textarea.getAttribute("data-type") || null,
status = textarea && textarea.getAttribute("data-status") || 200,
statusText = textarea && textarea.getAttribute("data-statusText") || "OK",
content = {
html: root.innerHTML,
text: type ?
textarea.value :
root ? (root.textContent || root.innerText) : null
};
console.log(doc, root, textarea, type, status, statusText, content);
return;
cleanUp();
completeCallback(status, statusText, content, type ?("Content-Type: " + type) : null);
}
form.submit();
}
form.appendChild(frame);
document.body.appendChild(form);
}
</script>
</body>
</html>

View File

@ -1,58 +0,0 @@
var fs = require('fs');
var http = require('http');
var qs = require('querystring');
var Gun = require('gun');
var gun = Gun({
peers: 'http://localhost:8888/gun'
,s3: require('../../test/shotgun') // replace this with your own keys!
});
http.route = function(url){
console.log(url);
var path = __dirname + url;
if(!url){ return http.route }
if(gun.server.regex.test(url)){
return gun;
}
if(fs.existsSync(path)){
return ((path = require(path)) && path.server)? path : http.route;
} else
if(url.slice(-3) !== '.js'){
return http.route(url + '.js');
}
return http.route;
}
http.route.server = function(req, res){
console.log("/ no route found");
}
http.createServer(function(req, res){
console.log(req.headers);
console.log(req.method, req.url);
var body = {};
body.length = 0;
body.data = new require('buffer').Buffer('');
req.on('data', function(buffer){
if(body.data.length >= body.length + buffer.length){
buffer.copy(body.data, body.length);
} else {
body.data = Buffer.concat([body.data, buffer]);
}
body.length += buffer.length;
});
req.on('end', function(x){
body.text = body.data.toString('utf8');
try{body.json = JSON.parse(body.text);
}catch(e){}
delete body.data;
req.body = body.json || body.text;
http.route(req.url).server(req, res);
});
res.on('data', function(data){
res.write(JSON.stringify(data) + '\n');
});
res.on('end', function(data){
res.end(JSON.stringify(data));
});
}).listen(8888);
console.log("listening");
//process.on("uncaughtException", function(e){console.log('!!!!!!!!!!!!!!!!!!!!!!');console.log(e);console.log('!!!!!!!!!!!!!!!!!!!!!!')});

View File

@ -1,77 +0,0 @@
var sign = {};
var Gun = require('gun');
var gun = Gun({
peers: 'http://localhost:8888/gun'
,s3: require('../../test/shotgun') // replace this with your own keys!
});
sign.user = {}
sign.user.create = function(form, cb, shell){
sign.crypto(form, function(err, user){
if(err || !user){ return cb(err) }
user = {key: user.key, salt: user.salt};
user.account = {email: form.email, registered: new Date().getTime()};
gun.set(user).key('email/' + user.account.email);
cb(null, user);
});
};
sign.server = function(req, res){
console.log("sign.server", req.headers, req.body);
if(!req.body || !req.body.email){ return res.emit('end', {err: "That email does not exist."}) }
var user = gun.load('email/' + req.body.email, function(data){ // this callback is called the magazine, since it holds the clip
console.log("data from key", data);
if(!req.body.password){
return res.emit('end', {ok: 'sign in'});
}
crypto({password: req.body.password, salt: data.salt }, function(error, valid){
if(error){ return res.emit('end', {err: "Something went wrong! Try again."}) }
if(data.key === valid.key){ // authorized
return res.emit('end', {ok: 'Signed in!'});
} else { // unauthorized
return res.emit('end', {err: "Wrong password."});
}
});
}).blank(function(){
if(!req.body.password){
return res.emit('end', {ok: 'sign up'});
}
return sign.user.create(req.body, function(err, user){
if(err || !user){ return res.emit('end', {err: "Something went wrong, please try again."}) }
console.log('yay we made the user', user);
res.emit('end', {err: "Registered!"});
}, user);
});
}
;var crypto = function(context, callback, option){
option = option || {};
option.hash = option.hash || 'sha1';
option.strength = option.strength || 10000;
option.crypto = option.crypto || require('crypto');
if(!context.password){
option.crypto.randomBytes(8,function(error, buffer){
if(error){ return callback(error) }
context.pass = buffer.toString('base64');
crypto(context, callback);
}); return callback;
}
if(!context.salt){
option.crypto.randomBytes(64, function(error, buffer){
if(error){ return callback(error) }
context.salt = buffer.toString('base64');
crypto(context, callback);
});
return callback;
}
option.crypto.pbkdf2(context.password, context.salt, option.strength, context.salt.length, function(error, buffer){
if(!buffer || error){ return callback(error) }
delete context.password;
context.key = buffer.toString('base64');
callback(null, context);
});
return callback;
};
sign.crypto = crypto;
module.exports = sign;

27
gun.js
View File

@ -297,7 +297,7 @@
gun._.at('soul').emit({soul: ctx.soul});
} else { return cb.call(gun, {err: Gun.log('No soul on data!') }, data) }
if(err = Gun.union(gun, data).err){ return cb.call(gun, err) }
if(!Gun.obj.map(data, function(val, field){
if(!Gun.obj.map(data, function(val, field){ // TODO: turn this into a utility function?
if(Gun._.meta === field){ return }
return true;
})){ gun.__.flag.end[ctx.soul] = true }
@ -417,7 +417,7 @@
return gun;
}
Chain.val = function(cb, opt){ // TODO: MARK!!! COME BACK HERE!! You're trying to get val to be unique per soul+field.
Chain.val = function(cb, opt){
var gun = this, ctx = {};
cb = cb || root.console.log.bind(root.console);
opt = opt || {};
@ -444,6 +444,10 @@
ctx[$.soul] = gun.__.on($.soul).event(on);
function on(delta){ // TODO: Filter out end events except where option wanted.
var node = gun.__.graph[$.soul];
if(!opt.end && delta && !Gun.obj.map(delta, function(val, field){ // TODO: turn this into a utility function?
if(Gun._.meta === field){ return }
return true;
})){ return }
if(opt.change){
cb.call(gun, Gun.obj.copy(delta || node), $.field);
} else {
@ -509,12 +513,13 @@
env.graph[at.node._[Gun._.soul] = at.soul = $.soul] = at.node;
cb(at, at.soul);
} else {
$.empty? path() : gun.back.path(at.path.join('.'), path); // TODO: clean this up.
function path(err, data){
if(at.soul){ return }
at.soul = Gun.is.soul.on(data) || Gun.is.soul.on(at.obj) || Gun.roulette.call(gun);
env.graph[at.node._[Gun._.soul] = at.soul] = at.node;
cb(at, at.soul);
};
$.empty? path() : gun.back.path(at.path.join('.'), path); // TODO: clean this up.
}
}
if(!at.node._[Gun._.HAM]){
@ -897,7 +902,7 @@
window.tab = tab; // for debugging purposes
opt = opt || {};
tab.headers = opt.headers || {};
tab.headers['gun-sid'] = tab.headers['gun-sid'] || Gun.text.random();
tab.headers['gun-sid'] = tab.headers['gun-sid'] || Gun.text.random(); // stream id
tab.prefix = tab.prefix || opt.prefix || 'gun/';
tab.prekey = tab.prekey || opt.prekey || '';
tab.prenode = tab.prenode || opt.prenode || '_/nodes/';
@ -912,16 +917,20 @@
} else {
o.url.pathname = '/' + key;
}
Gun.log("gun get", key);
Gun.log("tab get --->", key);
(function local(key, cb){
var node, lkey = key[Gun._.soul]? tab.prefix + tab.prenode + key[Gun._.soul]
: tab.prefix + tab.prekey + key
if((node = store.get(lkey)) && node[Gun._.soul]){ return local(node, cb) }
if(cb.node = node){ Gun.log('via cache', key); setTimeout(function(){cb(null, node)},0) }
if(cb.node = node){ Gun.log('tab via cache <---', key); setTimeout(function(){
cb(null, node);
cb(null, {_: {'#': Gun.is.soul.on(node) }}); // TODO: Don't have the symbols hard coded.
},0) }
}(key, cb));
Gun.obj.map(gun.__.opt.peers, function(peer, url){
request(url, null, function(err, reply){
Gun.log('via', url, key, reply.body);
reply.body = reply.body || reply.chunk || reply.end || reply.write;
Gun.log('tab via', url, key, '<--', reply.body);
if(err || !reply || (err = reply.body && reply.body.err)){
cb({err: Gun.log(err || "Error: Get failed through " + url) });
} else {
@ -952,6 +961,7 @@
store.put(tab.prefix + tab.prekey + key, meta);
Gun.obj.map(gun.__.opt.peers, function(peer, url){
request(url, meta, function(err, reply){
reply.body = reply.body || reply.chunk || reply.end || reply.write;
if(err || !reply || (err = reply.body && reply.body.err)){
// tab.key(key, soul, cb); // naive implementation of retry TODO: BUG: need backoff and anti-infinite-loop!
cb({err: Gun.log(err || "Error: Key failed to be made on " + url) });
@ -972,6 +982,7 @@
});
Gun.obj.map(gun.__.opt.peers, function(peer, url){
request(url, nodes, function(err, reply){
reply.body = reply.body || reply.chunk || reply.end || reply.write;
console.log("PUT success?", err, reply);
if(err || !reply || (err = reply.body && reply.body.err)){
return cb({err: Gun.log(err || "Error: Put failed on " + url) });
@ -1035,7 +1046,7 @@
if(opt.url){ req.url = opt.url }
req.headers = req.headers || {};
r.ws.cbs[req.headers['ws-rid'] = 'WS' + (+ new Date()) + '.' + Math.floor((Math.random()*65535)+1)] = function(err,res){
delete r.ws.cbs[req.headers['ws-rid']];
if(res.body || res.end){ delete r.ws.cbs[req.headers['ws-rid']] }
cb(err,res);
}
ws.send(JSON.stringify(req));

View File

@ -21,7 +21,7 @@ module.exports = function(req, res, next){
res.statusCode = reply.statusCode || reply.status;
}
if(reply.headers){
if(!res._headerSent){
if(!res._headerSent){ // TODO: BUG? There was an edge case where this was not safe.
Gun.obj.map(reply.headers, function(val, field){
res.setHeader(field, val);
});
@ -47,4 +47,4 @@ module.exports = function(req, res, next){
post(null, body);
});
form.parse(req);
}
}

View File

@ -32,7 +32,7 @@
}
return gun;
}
gun.server = gun.server || function(req, res, next){
gun.server = gun.server || function(req, res, next){ // http
//Gun.log("\n\n GUN SERVER!", req);
next = next || function(){};
if(!req || !res){ return next(), false }
@ -101,7 +101,7 @@
tran.get = function(req, cb){
var key = req.url.key
, reply = {headers: {'Content-Type': tran.json}};
console.log(req);
//console.log(req);
if(req && req.url && Gun.obj.has(req.url.query, '*')){
return gun.all(req.url.key + req.url.search, function(err, list){
console.log("reply all with", err, list);
@ -115,10 +115,11 @@
key = {};
key[Gun._.soul] = req.url.query[Gun._.soul];
}
//Gun.log("transport.getting key ->", key, gun.__.graph, gun.__.keys);
//console.log("transport.getting key ->", key);
gun.get(key, function(err, node){
//tran.sub.scribe(req.tab, node._[Gun._.soul]);
cb({headers: reply.headers, body: (err? (err.err? err : {err: err || "Unknown error."}) : node || null)});
cb({headers: reply.headers, chunk: (err? (err.err? err : {err: err || "Unknown error."}) : node || null)});
cb({headers: reply.headers, body: {_: {'#': Gun.is.soul.on(node) }} }); // TODO: symbol shouldn't be hard coded!
});
}
tran.put = function(req, cb){
@ -127,7 +128,6 @@
var reply = {headers: {'Content-Type': tran.json}};
if(!req.body){ return cb({headers: reply.headers, body: {err: "No body"}}) }
if(tran.put.key(req, cb)){ return }
// some NEW code that should get revised.
if(Gun.is.node(req.body) || Gun.is.graph(req.body)){
console.log("tran.put", req.body);

57
web/2015/15.html Normal file
View File

@ -0,0 +1,57 @@
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width, initial-scale=1">
</head>
<body>
<a href="https://github.com/amark/gun" target="_blank"><img style="position: absolute; top: 0; right: 0; border: 0;" src="https://camo.githubusercontent.com/365986a132ccd6a44c23a9169022c0b5c890c387/68747470733a2f2f73332e616d617a6f6e6177732e636f6d2f6769746875622f726962626f6e732f666f726b6d655f72696768745f7265645f6161303030302e706e67" alt="Fork me on GitHub" data-canonical-src="https://s3.amazonaws.com/github/ribbons/forkme_right_red_aa0000.png"></a>
<a href="https://gunDB.io/" style="font-family: Arial;">Home</a>
<!-- BLOG POST HERE -->
<style>
#main {
min-width: 250px;
max-width: 700px;
width: 75%;
margin: 7% auto;
padding: 2% 5%;
background: white;
background: rgba(100%,100%,100%,.6);
font-family: Arial;
font-size: 18pt;
text-shadow: 0px 0px 7px #DDD;
line-height: 20pt;
}
#main p {
text-indent: 2em;
}
</style>
<div id="main">
<h3>Do You have Time to Chat?</h3>
<p>
Let's build a chat app. But we're going to do it in a mind boggling way.
Conversations take time to have, therefore rather than storing every message
individually, we are going to store them in time. What does that even mean?
</p>
<p>
The first requirement is understanding immutable data.
Most database systems overwrite old data with new data when there is an update.
This preserves data in space, but loses its history.
Immutable data is the idea of never changing data,
but appending a new record every time. Such approach preserves history.
</p>
<p>
</p>
</div>
<!-- END OLD BLOG POST -->
</body>
</html>