...
 
Commits (3)
......@@ -7,4 +7,4 @@ target
*.stackdump
*.un~
*.dll
/node_modules
node_modules
......@@ -3,6 +3,9 @@ module.exports = {
// 3600000)
time_scale: 100,
// the port we run our event log off of (which vizz uses to show things)
http_port: 3001,
// where to find our api
endpoint: 'http://127.0.0.1:13007/api',
......
......@@ -22,6 +22,17 @@ const products_idx_id = {};
var start = null;
var last_time = null;
var root_bus = null;
var event_id = 0;
const event = function(type, data) {
const id = event_id++;
root_bus.emit('event', {id, type, data});
};
exports.set_bus = function(bus) {
root_bus = bus;
};
exports.get = function(name) {
return companies[name];
......@@ -95,6 +106,7 @@ exports.create = async function(name, options) {
labor: {},
bus: new Emitter(),
};
event('company:create', company);
company.bus.setMaxListeners(9999)
companies[name] = company;
exports.index(company);
......@@ -153,6 +165,7 @@ exports.create_product = async function(company_name, product_name, options) {
company_name,
inputs: options.inputs || [],
};
event('product:create', product);
companies[company_name].products[product_name] = product;
products_idx_id[product.id] = product;
};
......@@ -192,6 +205,7 @@ async function order_product(company_id_from, product_id, amount, category, opti
company_from.bus.once('order:'+order_id+':delivered', options.delivered || function() {});
} else {
throw new Error('order: '+res.description);
event('error', {type: 'order', err: res.description});
}
return res;
}
......@@ -207,6 +221,10 @@ async function receive_order(order_id) {
const amount = order.products[0].quantity;
// custom order-processing logic. obvis should be defined when the company
// is created, but for now we work with this
event('order:incoming', {order});
const emit = function(evname) {
event(evname, {order});
};
switch(company.name) {
case 'grocer':
case 'power-planty':
......@@ -223,10 +241,14 @@ async function receive_order(order_id) {
inc_inventory(company, inp_product.id, -inp.amount * amount);
});
company.bus.emit('order:start');
emit('order:start');
company.bus.emit('order:complete');
emit('order:complete');
company.bus.emit('order:ship');
emit('order:ship');
company_from.bus.emit('order:'+order_id+':complete');
company_from.bus.emit('order:'+order_id+':delivered');
emit('order:delivered');
break;
case 'farmy':
var res = await trans.send_as(worker, tx.order.TxUpdateStatus, {
......@@ -242,11 +264,15 @@ async function receive_order(order_id) {
inc_inventory(company, inp_product.id, -inp.amount);
});
company.bus.emit('order:start');
emit('order:start');
company.bus.emit('order:complete');
emit('order:completed');
company_from.bus.emit('order:'+order_id+':complete');
company.bus.emit('order:ship');
emit('order:ship');
await wait_ticks(company, 1 * 24);
company_from.bus.emit('order:'+order_id+':delivered');
emit('order:delivered');
break;
default:
var res = await trans.send_as(worker, tx.order.TxUpdateStatus, {
......@@ -262,15 +288,19 @@ async function receive_order(order_id) {
inc_inventory(company, inp_product.id, -inp.amount);
});
company.bus.emit('order:start');
emit('order:start');
// wait 3 days. we kind of just assume infinite scale at each company
// for now (obviously eventually we'll want to implement some for of
// order queuing)
await wait_ticks(company, 3 * 24);
company.bus.emit('order:complete');
emit('order:complete');
company_from.bus.emit('order:'+order_id+':complete');
company.bus.emit('order:ship');
emit('order:ship');
await wait_ticks(company, 1 * 24);
company_from.bus.emit('order:'+order_id+':delivered');
emit('order:delivered');
break;
}
}
......@@ -304,6 +334,7 @@ function setup_inventory(company) {
function set_inventory(company, product_id, amount) {
if(!company.inventory) company.inventory = {};
company.inventory[product_id] = amount;
event('company:set-inventory', {company_id: company.id, product_id, amount});
}
function inc_inventory(company, product_id, amount) {
......@@ -312,6 +343,7 @@ function inc_inventory(company, product_id, amount) {
throw new Error('inc_inventory() -- trying to increase inventory on product that is not in inventory');
}
company.inventory[product_id] += amount;
event('company:set-inventory', {company_id: company.id, product_id, amount: company.inventory[product_id]});
}
function manage_inventory(company) {
......@@ -334,7 +366,7 @@ function manage_inventory(company) {
orders.push(promise);
}
});
return orders;
return Promise.all(orders);
}
function manage_recurring_orders(now, company) {
......@@ -364,8 +396,10 @@ function manage_labor(now, company) {
}).then(function(res) {
if(res.success) {
//console.log(`> clock in: ${company.name} -- ${company.workers[worker_id]}`);
event('worker:clock-in', {worker_id, company_id: company.id, labor_id});
company.labor[worker_id] = labor_id;
} else {
event('error', {action: 'clock-in', err: res.description});
throw new Error('labor: clock-in: '+res.description);
}
});
......@@ -383,8 +417,10 @@ function manage_labor(now, company) {
}).then(function(res) {
if(res.success) {
//console.log(`> clock out: ${company.name} -- ${company.workers[worker_id]}`);
event('worker:clock-out', {worker_id, company_id: company.id, labor_id});
delete company.workers[worker_id].labor_id;
} else {
event('error', {action: 'clock-out', err: res.description});
throw new Error('labor: clock-out: '+res.description);
}
});
......@@ -437,7 +473,10 @@ function company_runner(bus, name) {
});
});
company.bus.on('order:start', function() {
manage_inventory(company, name);
manage_inventory(company, name)
.catch((err) => {
console.log('manage inventory: err: ', err);
});
});
return new Promise(function(resolve) {
bus.on('quit', resolve);
......
......@@ -7,9 +7,14 @@ const moment = require('moment');
const trans = Basis.transactions;
const Companies = require('./companies');
const Consumers = require('./consumers');
const http = require('http');
const uuid = require('uuid/v4');
const TIME_SCALE = config.time_scale;
const events = [];
const instance_id = uuid();
trans.clear_users();
trans.add_user('root', config.bootstrap_user.pub, config.bootstrap_user.sec);
......@@ -17,6 +22,13 @@ const bus = new Emitter();
bus.setMaxListeners(9999);
const start = new Date(moment().startOf('hour').toISOString());
bus.on('event', function(data) {
events.push(Object.assign({}, data, {_instance_id: instance_id}));
while(events.length > 500) {
events.shift();
}
});
async function run() {
const companies_thread = Companies.run(bus, start);
const consumers_thread = Consumers.run(bus, start);
......@@ -29,7 +41,23 @@ async function run() {
await Promise.all([companies_thread, consumers_thread]);
}
async function start_server() {
http.createServer(function(req, res) {
res.writeHead(200, {
'Content-Type': 'application/json',
'Access-Control-Allow-Origin': '*',
'Access-Control-Allow-Methods': 'GET,PUT,POST,DELETE,PATCH,OPTIONS',
'Access-Control-Allow-Headers': 'Authorization,Content-Type,Accept,Origin,User-Agent,DNT,Cache-Control,X-Mx-ReqToken,Keep-Alive,X-Requested-With,If-Modified-Since',
});
res.write(JSON.stringify(events));
res.end();
}).listen(config.http_port);
}
async function main() {
await start_server();
Companies.set_bus(bus);
await Companies.create('coal-miney', {
workers: [
'jerry',
......
config/local.js
build/
.PHONY: all clean watch
# non-versioned include
VARS ?= vars.mk
-include $(VARS)
mkdir = @mkdir -p $(dir $@)
NODE := $(shell which node)
BUILD := build
PROTOBUF_PATH ?= ../../basis/bundle/models/src/proto
allproto := $(shell find $(PROTOBUF_PATH) -name "*.proto")
protoout = $(subst $(PROTOBUF_PATH)/,$(BUILD)/proto/,$(allproto))
staticproto = $(shell find data/ -name "*.proto")
alljs := $(shell find ./src -name "*.js")
all: $(BUILD)/main.js
$(BUILD)/proto/%.proto: $(allproto) $(staticproto)
$(mkdir)
cp $^ $(dir $@)
$(BUILD)/protobuf.js: src/protobuf.js $(protoout)
$(mkdir)
cat src/protobuf.js > $@
echo "const _protofiles='$(protoout)'.split(' ');" >> $@
$(BUILD)/main.js: $(BUILD)/protobuf.js $(alljs) package.json
node_modules/.bin/browserify src/main.js -o $@
watch:
@"$(NODE)" ./scripts/fswatch
clean:
rm -rf $(BUILD)/
const config = {
// where to find our api
endpoint: 'http://127.0.0.1:13007/api',
};
// Copyright 2019 The Exonum Team
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
syntax = "proto3";
package exonum;
message Hash { bytes data = 1; }
message PublicKey { bytes data = 1; }
message Signature { bytes data = 1; }
message BitVec {
bytes data = 1;
uint64 len = 2;
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<script src="vnd/d3.js"></script>
<script src="vnd/sexhr.js"></script>
<script src="config/default.js"></script>
<script src="config/local.js"></script>
<script src="build/main.js"></script>
</head>
<body>
<div id="app">
<div class="network"></div>
<div class="logs">
<div class="consumer-orders">
</div>
<div class="company-orders">
</div>
</div>
</div>
</body>
</html>
This diff is collapsed.
{
"name": "basis-vizz",
"description": "An VISUAL LAYER FOR THE economic simulator running atop Basis",
"version": "0.1.0",
"license": "GPLv3",
"dependencies": {
"bluebird": "^3.7.1",
"protobufjs": "^6.8.8"
},
"devDependencies": {
"browserify": "^16.5.0",
"chokidar": "^3.2.3"
}
}
const exec = require('child_process').exec;
const chokidar = require('chokidar');
//const notifier = require('node-notifier');
function grep(str) {
return str.split(/[\r\n]/m)
.filter(function(part) {
var v = [
/nothing to be done/i,
/(entering|leaving) directory/i,
/^[ \s]*$/,
];
for(var i = 0; i < v.length; i++) {
if(part.match(v[i])) return false;
}
return true;
})
.join('\n');
}
var timeout = null;
var is_making = false;
var run_on_end = false;
function do_make() {
if(timeout) clearTimeout(timeout);
if(is_making) {
run_on_end = true;
return;
}
timeout = setTimeout(function() {
timeout = null;
is_making = true;
exec('make', function(err, stdout, stderr) {
if(err) console.error('error: ', err);
if(stdout) {
stdout = grep(stdout).trim();
if(stdout) console.log(stdout);
}
if(stderr) console.error(stderr);
// if changes happened while we were making, run make again
is_making = false;
if(run_on_end) {
do_make();
/*
notifier.notify({
message: 'make done! ' + (Math.round(Math.random() * 100)),
sound: false,
});
*/
console.log('(make done, watching)');
}
run_on_end = false;
});
}, 100);
}
function main() {
// start eh actual monitor
var options = {
ignored: [
/(^|[\/\\])\../,
/^build/,
],
ignoreInitial: true,
};
console.log('Start fs monitor');
do_make();
chokidar.watch('.', options)
.on('all', function(ev, path) {
console.log('fs: ', ev, path);
do_make();
});
}
main();
const def = require('../config/default.js');
const loc = require('../config/local.js');
module.exports = Object.assign({}, def, loc);
"use strict";
const Promise = require('bluebird');
const util = require('./util');
module.exports = function Events() {
let _events = {};
const grab_block = async (height) => {
var [res] = await Sexhr({
url: `${config.endpoint}/explorer/v1/block?height=${height}`,
});
return JSON.parse(res).txs;
};
const grab_tx = async (txid) => {
var [res] = await Sexhr({
url: `${config.endpoint}/explorer/v1/transactions?hash=${txid}`,
});
const transaction = JSON.parse(res);
return transaction;
};
let initial_load_complete = false;
const initial_load = async (seen) => {
// load blocks and all the transactions therein
let block_latest = null;
let block_earliest = 0;
while(true) {
try {
let url = `${config.endpoint}/explorer/v1/blocks?count=1000&skip_empty_blocks=true`;
let qs = {
//'latest': block_latest,
'earliest': block_earliest,
};
url += '&'+Object.keys(qs)
.filter((k) => qs[k])
.map((k) => k+'='+encodeURIComponent(qs[k]))
.join('&');
var [res] = await Sexhr({
url: url,
});
const parsed = JSON.parse(res);
const blocks = parsed.blocks;
if(blocks.length == 0) {
break;
}
block_latest = '';
block_earliest = blocks.reduce((acc, x) => Math.max(acc, x.height), 0) + 1;
const transactions_array = await Promise.mapSeries(blocks.reverse().map((x) => x.height), grab_block);
const transaction_ids = [];
transactions_array.forEach((transarray) => {
transarray.forEach((trans) => transaction_ids.push(trans.tx_hash));
});
await Promise.mapSeries(transaction_ids, async (txid) => {
const transaction = await grab_tx(txid);
seen[txid] = true;
emit('tx:raw', transaction);
});
} catch(e) {
console.warn('Events.source() -- grab: ', e);
emit('error', util.error('events.source', e));
await util.sleep(3000);
}
}
initial_load_complete = true;
};
const subscribe = async (seen) => {
const ws_url = config.endpoint.replace(/^https?:/i, 'ws:');
try {
const sock = new WebSocket(`${ws_url}/explorer/v1/transactions/subscribe`);
let pre_initial_load = [];
sock.onmessage = async (event) => {
try {
const transaction_meta = JSON.parse(event.data);
const txid = transaction_meta.tx_hash;
if(!initial_load_complete) {
pre_initial_load.push(txid);
return;
}
if(pre_initial_load) {
pre_initial_load.push(txid);
const unseen = pre_initial_load
.filter((txid) => !seen[txid]);
pre_initial_load = null;
const transactions = await Promise.mapSeries(unseen, grab_tx);
transactions.forEach((transaction) => {
emit('tx:raw', transaction);
});
return;
}
const transaction = await grab_tx(txid);
emit('tx:raw', transaction);
} catch(e) {
console.warn('Events.source() -- subscribe: ', e);
emit('error', util.error('events.source', e));
}
};
} catch(e) {
console.warn('Events.source() -- subscribe: ', e);
emit('error', util.error('events.source', e));
await util.sleep(3000);
subscribe(seen);
}
};
const source = async () => {
let last_event_id = -1;
let last_instance_id = null;
const seen = {};
// first, subscribe to transactions, and save them until we've caught up
// our block history parsing...then we event out the transactions that
// we haven't seen yet.
subscribe(seen);
// next, load all past transactions
await initial_load(seen);
};
const on = (evname, fn) => {
const events = _events;
if(!events[evname]) events[evname] = [];
events[evname].push(fn);
};
const once = (evname, fn) => {
var _called = false;
on(evname, (...args) => {
off(evname, fn);
if(_called) return;
_called = true;
fn.apply(this, args);
});
};
const off = (evname, fn) => {
const events = _events;
if(!events[evname]) return true;
const length = events[evname].length;
if(fn instanceof Function) {
events[evname] = events[evname].filter((evfn) => evfn != fn);
return events[evname].length != length;
} else {
events[evname] = [];
return length > 0;
}
};
const emit = (evname, ...args) => {
const events = _events;
if(evname != '*') {
emit('*', evname, args);
}
if(!events[evname]) return;
const scope = {};
events[evname].forEach((evfn) => {
evfn.apply(scope, args);
});
};
const feed = async (url) => {
var [res, _xhr] = await Sexhr({url});
const events = JSON.parse(res);
for(var i = 0, n = events.length; i < n; i++) {
const ev = events[i];
const next = events[i + 1];
emit.apply(this, [ev.ev].concat(ev.args));
if(next && ev.time && next.time) {
await util.sleep(next.time - ev.time);
}
}
};
return {
source,
on,
once,
off,
emit,
feed,
};
};
const proto = require('../build/protobuf');
const Events = require('./events');
const Vizz = require('./vizz');
const Transactions = require('./transactions');
document.addEventListener('DOMContentLoaded', async () => {
await proto.load();
const bus = new Events();
bus.on('error', ({type, err}) => {
console.error('---', type, err.stack);
});
const trans_parser = new Transactions({bus: bus});
const vizz = new Vizz({bus: bus});
bus.source();
});
"use strict";
const protobuf = require('protobufjs');
const root = new protobuf.Root();
exports.root = () => root;
exports.load = async function() {
await root.load(_protofiles, {keepCase: true});
};
// const _protofiles = [] ... filled in by makefile
"use strict";
const proto = require('../build/protobuf');
const util = require('./util');
const message_id_map = (function() {
// these MUST be ordered in the same way as basis/src/block/transactions/mod.rs
const transactions = [
'user.TxCreate',
'user.TxUpdate',
'user.TxSetPubkey',
'user.TxSetRoles',
'user.TxDelete',
'company.TxCreatePrivate',
'company.TxUpdate',
'company.TxSetType',
'company.TxDelete',
'company_member.TxCreate',
'company_member.TxSetRoles',
'company_member.TxDelete',
'labor.TxCreate',
'labor.TxSetTime',
'product.TxCreate',
'product.TxUpdate',
'product.TxDelete',
'resource_tag.TxCreate',
'resource_tag.TxDelete',
'order.TxCreate',
'order.TxUpdateStatus',
'order.TxUpdateCostCategory',
];
const map = {};
let i = 0;
transactions.forEach((t) => { map[i++] = t; });
return map;
})();
module.exports = function Transactions(options) {
options || (options = {});
const bus = options.bus;
if(!bus) throw new Error('Transactions() -- missing `options.bus`');
const type_map = {};
const root = proto.root();
const tx_parser = (tx) => {
if(tx.status.type != 'success') return;
try {
const msg_bin = util.hex_to_bin(tx.content.message);
const pubkey = util.bin_to_hex(msg_bin.slice(0, 32));
const msg_exo_class = msg_bin.slice(32, 33)[0];
const msg_exo_type = msg_bin.slice(33, 34)[0];
// we only care about transactions
if(msg_exo_class !== 0 || msg_exo_type !== 0) return;
const msg_payload = msg_bin.slice(34, -64);
const msg_service = new Uint16Array(msg_payload.slice(0, 2).buffer)[0];
const msg_type = new Uint16Array(msg_payload.slice(2, 4).buffer)[0];
const msg_body = msg_payload.slice(4);
const mapped_type = message_id_map[msg_type];
const type = root.lookupType(`basis.${mapped_type}`);
const msg = type.decode(msg_body);
bus.emit('tx', {type: mapped_type, body: msg, pubkey, debug: tx.content.debug});
} catch(e) {
bus.emit('error', util.error('tx-parse', e));
}
};
bus.on('tx:raw', tx_parser);
};
exports.sleep = (ms, val) => new Promise((resolve) => setTimeout(resolve.bind(this, val), ms));
exports.error = (type, err) => ({type, err});
exports.hex_to_bin = (hexstr) => {
return new Uint8Array(hexstr.match(/.{1,2}/g).map(byte => parseInt(byte, 16)));
};
exports.bin_to_hex = (uarr) => {
return Array.prototype.map.call(uarr, x => ('00' + x.toString(16)).slice(-2)).join('');
};
"use strict";
const util = require('./util');
module.exports = function Vizz(options) {
options || (options = {});
const bus = options.bus;
if(!bus) throw new Error('Vizz() -- missing `options.bus`');
let width = 1024;
let height = 500;
const svg = d3.select('.network')
.append('svg')
.attr('width', width)
.attr('height', height)
.append('g')
.attr('class', 'container');
svg.append('g').attr('class', 'links');
svg.append('g').attr('class', 'nodes');
var d3_nodes = null;
var d3_links = null;
const ticked = () => {
if(d3_links) {
d3_links
.select('line')
.attr("x1", function(d) { return d.source.x; })
.attr("y1", function(d) { return d.source.y; })
.attr("x2", function(d) { return d.target.x; })
.attr("y2", function(d) { return d.target.y; });
}
if(d3_nodes) {
d3_nodes
.attr("transform", function(d) { return "translate(" + d.x + "," + d.y + ")"; });
}
};
const scale = 1 / 768;
const linkforce = d3.forceLink()
.id((d) => d.id)
.strength((d) => {
const map = {
'member': 1 / 2,
'order': 1 / 40,
'consumer-order': 0,
'_': 0,
};
const entry = map[d.type] || map['_'];
return entry;
})
.distance((d) => {
const dist_map = {
'member': 70,
'order': 300,
'consumer-order': 100,
'_': 100,
};
const dist = dist_map[d.type] || dist_map['_'];
return Math.min(width, height) * dist * scale;
});
const forcex = d3.forceX(() => width / 2);
const forcey = d3.forceY(() => height / 2);
const force = d3.forceSimulation()
.force('link', linkforce)
.force('charge', d3.forceManyBody().strength(() => Math.min(width, height) * -300 * scale))
.force('xPos', forcex)
.force('yPos', forcey)
.on('tick', ticked);
const nodes = [];
const links = [];
const user_map = {};
const company_map = {};
const pubkey_map = {};
const consumer_company_map = {
to_company: {},
to_consumer: {},
};
const timeouts = {};
const exists = (collection, id) => collection.find((x) => x.id === id);
const remove = (collection, id) => {
const idx = collection.findIndex((x) => x.id === id);
if(id < 0) return;
collection.splice(idx, 1);
};
const add_node = (id, type, name) => {
if(exists(nodes, id)) return;
nodes.push({
id,
type,
name,
x: width / 2,
y: height / 2,
});
const attr_map = {
'consumer': {
r: 15,
c: '#448822',
},
'company': {
r: 20,
c: '#333399',
},
'_': {
r: 5,
c: '#666',
},
};
const attr = attr_map[type] || attr_map['_'];
d3_nodes = svg.select('g.nodes')
.selectAll('g.node')
.data(nodes, (d) => d.id);
const nodes_enter = d3_nodes
.enter()
.append('g')
.attr('class', 'node');
nodes_enter
.append('circle')
.attr('id', `entity-${id}`)
.attr('r', attr.r)
.style('fill', attr.c);
nodes_enter
.append('text')
.text((d) => name.substr(0, 3))
.attr('title', name)
.style('text-anchor', 'middle')
.attr('transform', `translate(0, ${attr.r / 4})`)
.style('fill', '#fff');
d3_nodes = nodes_enter.merge(d3_nodes);
force.stop();
force.nodes(nodes);
force.alpha(1);
force.restart();
};
const add_link = (id, id_from, id_to, {type, name, timeout}) => {
const existing_link = exists(links, id);
if(timeout) {
// unlink the order after a set amount of time
const existing_timeout = timeouts[id];
if(existing_timeout) {
clearTimeout(existing_timeout);
delete existing_timeout[id];
}
const new_timeout = setTimeout(() => remove_link(id), timeout);
timeouts[id] = new_timeout;
}
if(existing_link) return;
links.push({
id,
type,
name,
source: id_from,
target: id_to,
});
const attr_map = {
'member': {
c: '#aaa',
w: 1,
},
'order': {
c: '#f00',
w: 2,
},
'consumer-order': {
c: '#58c',
w: 1,
},
'_': {
c: '#666',
w: 1,
},
};
const attr = attr_map[type] || attr_map['_'];
d3_links = svg
.select('g.links')
.selectAll('g.link')
.data(links, (d) => d.id);
const links_enter = d3_links
.enter()
.append('g')
.attr('class', 'link');
links_enter
.append('line')
.attr('id', `link-${id}`)
.attr('stroke', attr.c)
.attr('stroke-width', attr.w);
d3_links = links_enter.merge(d3_links);
force.stop();
force.force('link').links(links);
force.alpha(1);
force.restart();
};
const remove_link = (id) => {
if(!exists(links, id)) return;
remove(links, id);
d3_links = svg
.select('g.links')
.selectAll('g.link')
.data(links, (d) => d.id);
const links_exit = d3_links.exit();
links_exit.remove();
//d3_links = links_exit.merge(d3_links);
force.stop();
force.force('link').links(links);
force.alpha(1);
force.restart();
};
const action_johnnnyy = {
'user.TxCreate': (tx) => {
user_map[tx.id] = {id: tx.id, name: tx.name};
const pubkey = util.bin_to_hex(tx.pubkey.data);
pubkey_map[pubkey] = tx.id;
add_node(tx.id, 'consumer', tx.name);
},
'company.TxCreatePrivate': (tx, pubkey) => {
company_map[tx.id] = {id: tx.id, name: tx.name};
if(tx.email.match(/consumer-company/i)) {
const consumer_id = tx.email.replace(/@.*/, '')
.split('-')
.slice(3)
.join('-');
consumer_company_map.to_consumer[tx.id] = consumer_id;
consumer_company_map.to_company[consumer_id] = tx.id;
return;
}
add_node(tx.id, 'company', tx.name);
const user_id = pubkey_map[pubkey];
const link_id = [user_id, tx.id].join('::');
add_link(link_id, user_id, tx.id, {
type: 'member',
name: user_map[user_id].name+' -> '+tx.name,
});
},
'company_member.TxCreate': (tx) => {
const user_id = tx.user_id;
const company_id = tx.company_id;
const link_id = [user_id, company_id, 'member'].join('::');
add_link(link_id, user_id, company_id, {
type: 'member',
name: user_map[user_id].name+' -> '+company_map[company_id].name,
});
},
'order.TxCreate': (tx) => {
let c_from = tx.company_id_from;
let c_to = tx.company_id_to;
const name_from = company_map[c_from].name;
const name_to = company_map[c_to].name;
let type = 'order';
let timeout = 1000;
if(consumer_company_map.to_consumer[c_from]) {
c_from = consumer_company_map.to_consumer[c_from];
type = 'consumer-order';
timeout = 1000;
}
const link_id = [tx.id, 'order'].join('::');
add_link(link_id, c_from, c_to, {
type,
name: name_from+' -> '+name_to,
timeout,
});
},
};
bus.on('tx', ({type, body, pubkey, debug: _debug}) => {
//console.log('tx: ', type, body); // DEBUG: remove
const fn = action_johnnnyy[type];
if(!fn) return;
fn(body, pubkey);
});
};
This diff is collapsed.
/**
* Sexhr.js
*
* A simple but useful promise-enabled wrapper around XHR. Takes care of a lot
* of common tasks without abstracting away any power or freedom.
* -----------------------------------------------------------------------------
*
* Copyright (c) 2015, Lyon Bros LLC. (http://www.lyonbros.com)
*
* Licensed under The MIT License.
* Redistributions of files must retain the above copyright notice.
*/
(function() {
"use strict";
this.Sexhr = function(options)
{
options || (options = {});
return new Promise(function(resolve, reject) {
var url = options.url;
var method = (options.method || 'get').toUpperCase();
var emulate = options.emulate || true;
if(!options.url) throw new Error('no url given');
url = url.replace(/#.*$/, '');
var qs = [];
if(options.querydata)
{
qs = Object.keys(options.querydata)
.map(function(key) {
return key + '=' + encodeURIComponent(options.querydata[key]);
});
}
if(emulate && ['GET', 'POST'].indexOf(method) < 0)
{
qs.push('_method='+method);
method = 'POST';
}
if(qs.length)
{
var querystring = qs.join('&');
if(url.match(/\?/))
{
url = url.replace(/&$/) + '&' + querystring;
}
else
{
url += '?' + querystring;
}
}
var xhr = new XMLHttpRequest();
xhr.open(method, url, true);
xhr.responseType = options.response_type || '';
if(options.timeout) xhr.timeout = options.timeout;
Object.keys(options.headers || {}).forEach(function(k) {
xhr.setRequestHeader(k, options.headers[k]);
});
xhr.onload = function(e)
{
if((xhr.status >= 200 && xhr.status < 300) || xhr.status == 304)
{
var value = xhr.response;
return resolve([value, xhr]);
}
else if(xhr.status >= 400)
{
reject({xhr: xhr, code: xhr.status, msg: xhr.response});
}
};
xhr.onabort = function(e)
{
reject({xhr: xhr, code: -2, msg: 'aborted'});
};
xhr.onerror = function(e)
{
reject({xhr: xhr, code: -1, msg: 'error'});
};
xhr.ontimeout = function(e)
{
reject({xhr: xhr, code: -3, msg: 'timeout'});
};
// set xhr.on[progress|abort|etc]
Object.keys(options).forEach(function(k) {
if(k.substr(0, 2) != 'on') return false;
if(['onload', 'onerror', 'onabort', 'ontimeout'].indexOf(k) >= 0) return false;
var fn = options[k];
xhr[k] = function(e) { fn(e, xhr); };
});
// set xhr.upload.on[progress|abort|etc]
Object.keys(options.upload || {}).forEach(function(k) {
if(k.substr(0, 2) != 'on') return false;
var fn = options[k];
xhr.upload[k] = function(e) { fn(e, xhr); };
});
if(options.override) options.override(xhr);
xhr.send(options.data);
});
};
}).apply((typeof exports != 'undefined') ? exports : this);