├── .gitignore ├── order-book ├── price-server │ ├── .npmrc │ ├── package.json │ ├── price-server.js │ └── lib │ │ └── db.js ├── front-end │ ├── images │ │ └── img-header.jpg │ ├── fonts │ │ ├── glyphicons-halflings-regular.eot │ │ ├── glyphicons-halflings-regular.ttf │ │ └── glyphicons-halflings-regular.woff │ ├── js │ │ ├── npm.js │ │ ├── orderbook │ │ │ ├── pricechart.js │ │ │ └── orderbook.js │ │ ├── bootstrap.min.js │ │ └── moment.min.js │ ├── css │ │ ├── main.css │ │ ├── bootstrap-theme.min.css │ │ ├── bootstrap-theme.css │ │ └── bootstrap-theme.css.map │ └── index.html └── README.md ├── README.md ├── peer-announce ├── README.md ├── package.json ├── peer-announce.js ├── index.js └── lib │ └── db.js └── db-replication ├── cr-node ├── package.json └── cr-node.js ├── peer-node ├── package.json └── peer-node.js ├── master-node ├── package.json └── master-node.js ├── firewalled-node ├── package.json └── firewalled-node.js └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | **/orbitdb/** 3 | -------------------------------------------------------------------------------- /order-book/price-server/.npmrc: -------------------------------------------------------------------------------- 1 | package-lock = false 2 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Each directory contains a different prototype of using Orbit DB. 2 | -------------------------------------------------------------------------------- /order-book/front-end/images/img-header.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/christroutner/orbit-tests/HEAD/order-book/front-end/images/img-header.jpg -------------------------------------------------------------------------------- /order-book/README.md: -------------------------------------------------------------------------------- 1 | This is a quick prototype demonstrating the use of [OrbitDB](https://github.com/orbitdb/orbit-db) 2 | to create a buy-sell order book. 3 | -------------------------------------------------------------------------------- /order-book/front-end/fonts/glyphicons-halflings-regular.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/christroutner/orbit-tests/HEAD/order-book/front-end/fonts/glyphicons-halflings-regular.eot -------------------------------------------------------------------------------- /order-book/front-end/fonts/glyphicons-halflings-regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/christroutner/orbit-tests/HEAD/order-book/front-end/fonts/glyphicons-halflings-regular.ttf -------------------------------------------------------------------------------- /order-book/front-end/fonts/glyphicons-halflings-regular.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/christroutner/orbit-tests/HEAD/order-book/front-end/fonts/glyphicons-halflings-regular.woff -------------------------------------------------------------------------------- /peer-announce/README.md: -------------------------------------------------------------------------------- 1 | This directory contains a protoype of using the [Event Store or Logging type DB](https://github.com/orbitdb/orbit-db-eventstore) in OrbitDB as a way for new peers joining a network to announce themselves and distribute the information needed for other peers to connect to them. 2 | -------------------------------------------------------------------------------- /db-replication/cr-node/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "cr-node", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "cr-node.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1", 8 | "start": "node cr-node.js" 9 | }, 10 | "author": "Chris Troutner ", 11 | "license": "MIT", 12 | "dependencies": { 13 | "ipfs": "^0.33.1", 14 | "orbit-db": "^0.19.9" 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /db-replication/peer-node/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "peer-node", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "peer-node.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1", 8 | "start": "node peer-node.js" 9 | }, 10 | "author": "Chris Troutner ", 11 | "license": "MIT", 12 | "dependencies": { 13 | "ipfs": "^0.33.1", 14 | "orbit-db": "^0.19.9" 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /order-book/price-server/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "price-server", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "price-server.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1", 8 | "start": "node price-server.js" 9 | }, 10 | "author": "", 11 | "license": "ISC", 12 | "dependencies": { 13 | "express": "^4.16.3", 14 | "ipfs": "^0.31.2", 15 | "orbit-db": "^0.19.9" 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /db-replication/master-node/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "master-node", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "master-node.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1", 8 | "start": "node master-node.js" 9 | }, 10 | "author": "Chris Troutner ", 11 | "license": "MIT", 12 | "dependencies": { 13 | "ipfs": "^0.37.1", 14 | "orbit-db": "^0.22.0-rc4" 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /db-replication/firewalled-node/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "firewalled-node", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "firewalled-node.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1", 8 | "start": "node firewalled-node.js" 9 | }, 10 | "author": "Chris Troutner ", 11 | "license": "MIT", 12 | "dependencies": { 13 | "ipfs": "^0.37.1", 14 | "orbit-db": "^0.22.0-rc4" 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /order-book/front-end/js/npm.js: -------------------------------------------------------------------------------- 1 | // This file is autogenerated via the `commonjs` Grunt task. You can require() this file in a CommonJS environment. 2 | require('../../js/transition.js') 3 | require('../../js/alert.js') 4 | require('../../js/button.js') 5 | require('../../js/carousel.js') 6 | require('../../js/collapse.js') 7 | require('../../js/dropdown.js') 8 | require('../../js/modal.js') 9 | require('../../js/tooltip.js') 10 | require('../../js/popover.js') 11 | require('../../js/scrollspy.js') 12 | require('../../js/tab.js') 13 | require('../../js/affix.js') -------------------------------------------------------------------------------- /peer-announce/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "peer-announce", 3 | "version": "1.0.0", 4 | "description": "This directory contains a protoype of using the [Event Store or Logging type DB](https://github.com/orbitdb/orbit-db-eventstore) in OrbitDB as a way for new peers joining a network to announce themselves and distribute the information needed for other peers to connect to them.", 5 | "main": "index.js", 6 | "directories": { 7 | "lib": "lib" 8 | }, 9 | "scripts": { 10 | "test": "echo \"Error: no test specified\" && exit 1" 11 | }, 12 | "author": "", 13 | "license": "ISC", 14 | "dependencies": { 15 | "ipfs": "^0.33.1", 16 | "orbit-db": "^0.19.9" 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /order-book/front-end/css/main.css: -------------------------------------------------------------------------------- 1 | /* 2 | *= require bootstrap/dist/css/bootstrap.min.css 3 | *= require_self 4 | */ 5 | 6 | 7 | .features .glyphicon { 8 | font-size: 32px; 9 | } 10 | 11 | .row { 12 | margin-bottom: 20px; 13 | } 14 | 15 | .quote { 16 | background-color: #444; 17 | color: #e1e1e1; 18 | } 19 | .quote blockquote { 20 | border: none; 21 | margin: 0; 22 | } 23 | .quote footer { 24 | color: #c1c1c1; 25 | } 26 | section, header { 27 | background-color: #fff; 28 | } 29 | body { 30 | 31 | } 32 | .footer { 33 | padding: 20px 0; 34 | } 35 | 36 | 37 | #demo-nav { 38 | display: none; 39 | } 40 | 41 | .show-nav #demo-nav { 42 | display: block; 43 | } 44 | 45 | 46 | 47 | 48 | 49 | 50 | /* Sticky footer styles 51 | -------------------------------------------------- */ 52 | html { 53 | position: relative; 54 | min-height: 100%; 55 | } 56 | body { 57 | /* Margin bottom by footer height */ 58 | margin-bottom: 200px; 59 | } 60 | .footer { 61 | position: absolute; 62 | bottom: 0; 63 | width: 100%; 64 | /* Set the fixed height of the footer here */ 65 | height: 200px; 66 | } 67 | .footer { 68 | color: #efefef; 69 | background-color: #333; 70 | } 71 | .footer a { 72 | color: #9af7fe; 73 | } 74 | @media only screen and (max-width: 768px) { 75 | body { margin-bottom: 400px; } 76 | .footer { height: 400px; } 77 | } 78 | -------------------------------------------------------------------------------- /db-replication/README.md: -------------------------------------------------------------------------------- 1 | This folder contains node.js test code I used to dig into the function and limitation of database replication in OrbitDB. Three folders contain code for three classes of nodes. Each one is based on [this event log example](https://github.com/orbitdb/orbit-db/blob/master/examples/eventlog.js). 2 | 3 | ## Test 1 - Replication Behind a Firewall 4 | 5 | - The master-node is considered the 'first' node. The one that initially creates the database and sets write access to it. It is assumed this node is running on an un-firewalled server, like a Digital Ocean Droplet. 6 | 7 | - The peer-node is assumed to also be an un-firewalled server. It is also assumed that the peer has some way to explicitly retrieve the IPFS peer ID hash of the *master node* and the OrbitDB address needed to replicate the database. In this example, these are saved as constants at the top of the file. 8 | 9 | - The firewalled node is assumed to be run on a computer behind a firewall, so it doesn't have open ports to the internet. It depends on Circuit Relay settings of the peer-node and master-node to communicate with other nodes in the network. 10 | 11 | ## Test 2 - Circuit Relay Between Two Firewalled Peers 12 | 13 | - The Circuit Relay node (cr-node) is similar to the firewalled node, as it is expected to be behind a firewall. The circuit-relay features of the master node are used to connect to the firewalled node. 14 | -------------------------------------------------------------------------------- /order-book/front-end/js/orderbook/pricechart.js: -------------------------------------------------------------------------------- 1 | /* 2 | This library contains code for managing the price chart. 3 | */ 4 | 5 | "use strict" 6 | 7 | // Chart variables. 8 | const prices = [1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000,]; 9 | const labels = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20]; 10 | let cfg; 11 | let ctx; 12 | let chart; 13 | 14 | 15 | // Update the price every 2 seconds. 16 | const timer = setInterval(function() { 17 | updatePrice(); 18 | }, 2000); 19 | 20 | 21 | // Update the price array. Keeps the length of the array fixed. 22 | async function updatePrice() { 23 | try { 24 | //debugger; 25 | 26 | const newPrice = await getPrice(); 27 | 28 | // Manipulate the arrays to ensure there are always a fixed number of data points. 29 | prices.shift(); 30 | prices.push(newPrice); 31 | chart.data.datasets[0].data.shift(); 32 | chart.data.datasets[0].data.push(newPrice); 33 | 34 | // Update the chart. 35 | chart.update(); 36 | 37 | } catch(err) { 38 | //console.error(`Error in updatePrice(): `, err); 39 | } 40 | } 41 | 42 | 43 | // Chart configuration settings. 44 | function configChart() { 45 | const cfg = { 46 | type: 'bar', 47 | data: { 48 | labels: labels, 49 | datasets: [{ 50 | label: 'RWLK - Random Walk Price', 51 | data: prices, 52 | type: 'line', 53 | pointRadius: 0, 54 | fill: false, 55 | lineTension: 0, 56 | borderWidth: 2 57 | }] 58 | }, 59 | options: { 60 | scales: { 61 | yAxes: [{ 62 | scaleLabel: { 63 | display: true, 64 | labelString: 'Closing price ($)' 65 | } 66 | }] 67 | } 68 | } 69 | }; 70 | 71 | return cfg; 72 | } 73 | -------------------------------------------------------------------------------- /db-replication/peer-node/peer-node.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | // CUSTOMIZE THESE VARIABLES 4 | const MASTER_MULTIADDR = "/ip4/162.243.158.213/tcp/4002/ipfs/QmQWgBQWpJkf93NW7rgVvycmot6arDxtuJGnArbyuznC6b" 5 | const DB_ADDRESS = "/orbitdb/QmdSeUenVxgwsgbo3ZidV2oxW1RFr5XxGNfVtSRN4p4Eda/example5343234" 6 | 7 | const IPFS = require('ipfs') 8 | const OrbitDB = require('orbit-db') 9 | 10 | const creatures = ['🐙', '🐷', '🐬', '🐞', '🐈', '🙉', '🐸', '🐓'] 11 | 12 | console.log("Starting...") 13 | 14 | const ipfs = new IPFS({ 15 | repo: './orbitdb/examples/ipfs', 16 | start: true, 17 | EXPERIMENTAL: { 18 | pubsub: true, 19 | }, 20 | relay: { 21 | enabled: true, // enable circuit relay dialer and listener 22 | hop: { 23 | enabled: true // enable circuit relay HOP (make this node a relay) 24 | } 25 | }, 26 | pubsub: true 27 | }) 28 | 29 | ipfs.on('error', (err) => console.error(err)) 30 | 31 | ipfs.on("replicated", () => { 32 | console.log(`replication event fired`); 33 | }) 34 | 35 | ipfs.on('ready', async () => { 36 | let db 37 | 38 | await ipfs.swarm.connect(MASTER_MULTIADDR) 39 | 40 | try { 41 | const orbitdb = new OrbitDB(ipfs, './orbitdb/examples/eventlog') 42 | db = await orbitdb.eventlog(DB_ADDRESS) 43 | await db.load() 44 | } catch (e) { 45 | console.error(e) 46 | process.exit(1) 47 | } 48 | 49 | const query = async () => { 50 | const index = Math.floor(Math.random() * creatures.length) 51 | const userId = Math.floor(Math.random() * 900 + 100) 52 | 53 | try { 54 | const entry = { avatar: creatures[index], userId: userId } 55 | console.log(`Adding ${entry.avatar} ${entry.userId} to DB.`) 56 | await db.add(entry) 57 | const latest = db.iterator({ limit: 5 }).collect() 58 | let output = `` 59 | output += `[Latest Visitors]\n` 60 | output += `--------------------\n` 61 | output += `ID | Visitor\n` 62 | output += `--------------------\n` 63 | output += latest.reverse().map((e) => e.payload.value.userId + ' | ' + e.payload.value.avatar + ')').join('\n') + `\n` 64 | console.log(output) 65 | } catch (e) { 66 | console.error(e) 67 | process.exit(1) 68 | } 69 | } 70 | 71 | setInterval(query, 1000*30) 72 | }) 73 | -------------------------------------------------------------------------------- /peer-announce/peer-announce.js: -------------------------------------------------------------------------------- 1 | /* 2 | A random walk price server based on Express.js. 3 | */ 4 | 5 | "use strict" 6 | 7 | const express = require("express"); 8 | const dbLib = require("./lib/db.js"); // handle all db stuff. 9 | 10 | const util = require("util"); 11 | util.inspect.defaultOptions = { 12 | showHidden: true, 13 | colors: true, 14 | }; 15 | 16 | // Global Variables 17 | const app = express(); 18 | const port = 6543; 19 | let db; 20 | 21 | async function start() { 22 | try { 23 | 24 | // Open the Database 25 | db = await dbLib.openDb(); 26 | 27 | // Start up the Express web server 28 | app.listen(port).on("error", expressErrorHandler); 29 | console.log('Express started on port ' + port); 30 | 31 | // Handle generic errors thrown by the express application. 32 | function expressErrorHandler(err) { 33 | if (err.code === "EADDRINUSE") 34 | console.error(`Port ${port} is already in use. Is this program already running?`); 35 | else console.error(JSON.stringify(err, null, 2)); 36 | 37 | console.error("Express could not start!"); 38 | process.exit(0); 39 | } 40 | 41 | // Allow any computer to access this API. 42 | app.use(function(req, res, next) { 43 | res.header("Access-Control-Allow-Origin", "*"); 44 | res.header("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept"); 45 | next(); 46 | }); 47 | 48 | // Initial price 49 | let price = 1000.00; 50 | 51 | // Periodically adjust the price, randomly. 52 | setInterval(function() { 53 | // Generate a random number between -1% an 1%. 54 | const rndNum = (2*Math.random() - 1)/100; 55 | 56 | // Calculate a new price. 57 | price = price * (1 - rndNum); 58 | 59 | // Do not allow the price to become negative. 60 | if(price < 0) price = 0; 61 | 62 | //console.log(`price: ${price}`); 63 | }, 1000); 64 | 65 | // Price API endpoint. 66 | app.get("/price", function(request, response, next) { 67 | response.send(`${price}`); 68 | }); 69 | 70 | app.get("/orderbook", function(request, response, next) { 71 | response.send(`${db.address.toString()}`) 72 | }) 73 | 74 | } catch(err) { 75 | console.error(`Error in price-server.js: `,err); 76 | } 77 | } 78 | start(); 79 | 80 | function sleep(ms) { 81 | return new Promise(resolve => setTimeout(resolve, ms)); 82 | } 83 | -------------------------------------------------------------------------------- /order-book/price-server/price-server.js: -------------------------------------------------------------------------------- 1 | /* 2 | A random walk price server based on Express.js. 3 | */ 4 | 5 | "use strict" 6 | 7 | const express = require("express"); 8 | const dbLib = require("./lib/db.js"); // handle all db stuff. 9 | 10 | const util = require("util"); 11 | util.inspect.defaultOptions = { 12 | showHidden: true, 13 | colors: true, 14 | }; 15 | 16 | // Global Variables 17 | const app = express(); 18 | const port = 6543; 19 | let db; 20 | 21 | async function start() { 22 | try { 23 | 24 | // Open the Database 25 | db = await dbLib.openDb(); 26 | 27 | // Start up the Express web server 28 | app.listen(port).on("error", expressErrorHandler); 29 | console.log('Express started on port ' + port); 30 | 31 | // Handle generic errors thrown by the express application. 32 | function expressErrorHandler(err) { 33 | if (err.code === "EADDRINUSE") 34 | console.error(`Port ${port} is already in use. Is this program already running?`); 35 | else console.error(JSON.stringify(err, null, 2)); 36 | 37 | console.error("Express could not start!"); 38 | process.exit(0); 39 | } 40 | 41 | // Allow any computer to access this API. 42 | app.use(function(req, res, next) { 43 | res.header("Access-Control-Allow-Origin", "*"); 44 | res.header("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept"); 45 | next(); 46 | }); 47 | 48 | // Initial price 49 | let price = 1000.00; 50 | 51 | // Periodically adjust the price, randomly. 52 | setInterval(function() { 53 | // Generate a random number between -1% an 1%. 54 | const rndNum = (2*Math.random() - 1)/100; 55 | 56 | // Calculate a new price. 57 | price = price * (1 - rndNum); 58 | 59 | // Do not allow the price to become negative. 60 | if(price < 0) price = 0; 61 | 62 | //console.log(`price: ${price}`); 63 | }, 1000); 64 | 65 | // Price API endpoint. 66 | app.get("/price", function(request, response, next) { 67 | response.send(`${price}`); 68 | }); 69 | 70 | app.get("/orderbook", function(request, response, next) { 71 | response.send(`${db.address.toString()}`) 72 | }) 73 | 74 | } catch(err) { 75 | console.error(`Error in price-server.js: `,err); 76 | } 77 | } 78 | start(); 79 | 80 | function sleep(ms) { 81 | return new Promise(resolve => setTimeout(resolve, ms)); 82 | } 83 | -------------------------------------------------------------------------------- /peer-announce/index.js: -------------------------------------------------------------------------------- 1 | /* 2 | orbitDB test 3 | 4 | Creates a publically writable DB. 5 | Adds a series of test key-value pairs. 6 | */ 7 | 8 | "use strict" 9 | 10 | const IPFS = require('ipfs') 11 | const OrbitDB = require('orbit-db') 12 | let ipfs // handle for IPFS instance. 13 | let db // handle for orbit DB. 14 | 15 | const util = require("util"); 16 | util.inspect.defaultOptions = { 17 | showHidden: true, 18 | colors: true, 19 | }; 20 | 21 | 22 | module.exports = { 23 | openDb 24 | } 25 | 26 | 27 | // OrbitDB uses Pubsub which is an experimental feature 28 | // and need to be turned on manually. 29 | // Note that these options need to be passed to IPFS in 30 | // all examples in this document even if not specfied so. 31 | const ipfsOptions = { 32 | EXPERIMENTAL: { 33 | pubsub: true 34 | } 35 | } 36 | 37 | // Returns a promise that resolves to a handle of the DB. 38 | function openDb() { 39 | return new Promise(function(resolve, reject) { 40 | try { 41 | // Create IPFS instance 42 | ipfs = new IPFS(ipfsOptions) 43 | 44 | ipfs.on('ready', async () => { 45 | //ipfs.swarm.connect('/dns4/ws-star.discovery.libp2p.io/tcp/443/wss/p2p-websocket-star'); 46 | ipfs.swarm.connect('/ip4/198.46.197.197/tcp/4001/ipfs/QmdXiwDtfKsfnZ6RwEcovoWsdpyEybmmRpVDXmpm5cpk2s'); // Connect to ipfs.p2pvps.net 47 | 48 | // Create OrbitDB instance 49 | const orbitdb = new OrbitDB(ipfs) 50 | 51 | const access = { 52 | // Give write access to everyone 53 | write: ['*'], 54 | } 55 | 56 | // Load the DB. 57 | db = await orbitdb.keyvalue('orderbook01', access) 58 | await db.load() 59 | 60 | console.log(`database string: ${db.address.toString()}`) 61 | console.log(`db public key: ${db.key.getPublic('hex')}`) 62 | 63 | // React when the database is updated. 64 | db.events.on('replicated', () => { 65 | console.log(`Databse replicated. Check for new prices.`) 66 | }) 67 | 68 | return resolve(db); 69 | }) 70 | 71 | } catch(err) { 72 | return reject(err); 73 | } 74 | }) 75 | } 76 | 77 | 78 | // Example key-value pairs: 79 | const value1 = { 80 | qty: 10, 81 | price: 99, 82 | buysell: true // true = buy, false = sell 83 | } 84 | 85 | const value2 = { 86 | qty: 9, 87 | price: 90, 88 | buysell: true 89 | } 90 | 91 | const value3 = { 92 | qty: 10, 93 | price: 110, 94 | buysell: false 95 | } 96 | -------------------------------------------------------------------------------- /peer-announce/lib/db.js: -------------------------------------------------------------------------------- 1 | /* 2 | orbitDB test 3 | 4 | Creates a publically writable DB. 5 | Adds a series of test key-value pairs. 6 | */ 7 | 8 | "use strict" 9 | 10 | const IPFS = require('ipfs') 11 | const OrbitDB = require('orbit-db') 12 | let ipfs // handle for IPFS instance. 13 | let db // handle for orbit DB. 14 | 15 | const util = require("util"); 16 | util.inspect.defaultOptions = { 17 | showHidden: true, 18 | colors: true, 19 | }; 20 | 21 | 22 | module.exports = { 23 | openDb 24 | } 25 | 26 | 27 | // OrbitDB uses Pubsub which is an experimental feature 28 | // and need to be turned on manually. 29 | // Note that these options need to be passed to IPFS in 30 | // all examples in this document even if not specfied so. 31 | const ipfsOptions = { 32 | EXPERIMENTAL: { 33 | pubsub: true 34 | } 35 | } 36 | 37 | // Returns a promise that resolves to a handle of the DB. 38 | function openDb() { 39 | return new Promise(function(resolve, reject) { 40 | try { 41 | // Create IPFS instance 42 | ipfs = new IPFS(ipfsOptions) 43 | 44 | ipfs.on('ready', async () => { 45 | //ipfs.swarm.connect('/dns4/ws-star.discovery.libp2p.io/tcp/443/wss/p2p-websocket-star'); 46 | ipfs.swarm.connect('/ip4/198.46.197.197/tcp/4001/ipfs/QmdXiwDtfKsfnZ6RwEcovoWsdpyEybmmRpVDXmpm5cpk2s'); // Connect to ipfs.p2pvps.net 47 | 48 | // Create OrbitDB instance 49 | const orbitdb = new OrbitDB(ipfs) 50 | 51 | const access = { 52 | // Give write access to everyone 53 | write: ['*'], 54 | } 55 | 56 | // Load the DB. 57 | db = await orbitdb.keyvalue('orderbook01', access) 58 | await db.load() 59 | 60 | console.log(`database string: ${db.address.toString()}`) 61 | console.log(`db public key: ${db.key.getPublic('hex')}`) 62 | 63 | // React when the database is updated. 64 | db.events.on('replicated', () => { 65 | console.log(`Databse replicated. Check for new prices.`) 66 | }) 67 | 68 | return resolve(db); 69 | }) 70 | 71 | } catch(err) { 72 | return reject(err); 73 | } 74 | }) 75 | } 76 | 77 | 78 | // Example key-value pairs: 79 | const value1 = { 80 | qty: 10, 81 | price: 99, 82 | buysell: true // true = buy, false = sell 83 | } 84 | 85 | const value2 = { 86 | qty: 9, 87 | price: 90, 88 | buysell: true 89 | } 90 | 91 | const value3 = { 92 | qty: 10, 93 | price: 110, 94 | buysell: false 95 | } 96 | -------------------------------------------------------------------------------- /order-book/price-server/lib/db.js: -------------------------------------------------------------------------------- 1 | /* 2 | orbitDB test 3 | 4 | Creates a publically writable DB. 5 | Adds a series of test key-value pairs. 6 | */ 7 | 8 | "use strict" 9 | 10 | const IPFS = require('ipfs') 11 | const OrbitDB = require('orbit-db') 12 | let ipfs // handle for IPFS instance. 13 | let db // handle for orbit DB. 14 | 15 | const util = require("util"); 16 | util.inspect.defaultOptions = { 17 | showHidden: true, 18 | colors: true, 19 | }; 20 | 21 | 22 | module.exports = { 23 | openDb 24 | } 25 | 26 | 27 | // OrbitDB uses Pubsub which is an experimental feature 28 | // and need to be turned on manually. 29 | // Note that these options need to be passed to IPFS in 30 | // all examples in this document even if not specfied so. 31 | const ipfsOptions = { 32 | EXPERIMENTAL: { 33 | pubsub: true 34 | } 35 | } 36 | 37 | // Returns a promise that resolves to a handle of the DB. 38 | function openDb() { 39 | return new Promise(function(resolve, reject) { 40 | try { 41 | // Create IPFS instance 42 | ipfs = new IPFS(ipfsOptions) 43 | 44 | ipfs.on('ready', async () => { 45 | //ipfs.swarm.connect('/dns4/ws-star.discovery.libp2p.io/tcp/443/wss/p2p-websocket-star'); 46 | ipfs.swarm.connect('/ip4/198.46.197.197/tcp/4001/ipfs/QmdXiwDtfKsfnZ6RwEcovoWsdpyEybmmRpVDXmpm5cpk2s'); // Connect to ipfs.p2pvps.net 47 | 48 | // Create OrbitDB instance 49 | const orbitdb = new OrbitDB(ipfs) 50 | 51 | const access = { 52 | // Give write access to everyone 53 | write: ['*'], 54 | } 55 | 56 | // Load the DB. 57 | db = await orbitdb.keyvalue('orderbook01', access) 58 | await db.load() 59 | 60 | console.log(`database string: ${db.address.toString()}`) 61 | console.log(`db public key: ${db.key.getPublic('hex')}`) 62 | 63 | // React when the database is updated. 64 | db.events.on('replicated', () => { 65 | console.log(`Databse replicated. Check for new prices.`) 66 | }) 67 | 68 | return resolve(db); 69 | }) 70 | 71 | } catch(err) { 72 | return reject(err); 73 | } 74 | }) 75 | } 76 | 77 | 78 | // Example key-value pairs: 79 | const value1 = { 80 | qty: 10, 81 | price: 99, 82 | buysell: true // true = buy, false = sell 83 | } 84 | 85 | const value2 = { 86 | qty: 9, 87 | price: 90, 88 | buysell: true 89 | } 90 | 91 | const value3 = { 92 | qty: 10, 93 | price: 110, 94 | buysell: false 95 | } 96 | -------------------------------------------------------------------------------- /db-replication/firewalled-node/firewalled-node.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | // CUSTOMIZE THESE VARIABLES 4 | const MASTER_MULTIADDR = "/ip4/138.68.212.34/tcp/4002/ipfs/QmVmrqFPYLXcTR6tkFzqxvjCFD3wSGWzLU382gGh4CLfW2" 5 | const DB_ADDRESS = "/orbitdb/zdpuArJQHJMkUd8U9j1kPfGpd6TKSPpuq7By6Uto72MBezVc2/example878" 6 | 7 | const IPFS = require('ipfs') 8 | const OrbitDB = require('orbit-db') 9 | 10 | const creatures = ['🐙', '🐷', '🐬', '🐞', '🐈', '🙉', '🐸', '🐓'] 11 | 12 | console.log("Starting...") 13 | 14 | const ipfs = new IPFS({ 15 | repo: './orbitdb/examples/ipfs', 16 | start: true, 17 | EXPERIMENTAL: { 18 | pubsub: true, 19 | }, 20 | relay: { 21 | enabled: true, // enable circuit relay dialer and listener 22 | hop: { 23 | enabled: true // enable circuit relay HOP (make this node a relay) 24 | } 25 | } 26 | }) 27 | 28 | ipfs.on('error', (err) => console.error(err)) 29 | 30 | ipfs.on("replicated", () => { 31 | console.log(`replication event fired`); 32 | }) 33 | 34 | ipfs.on('ready', async () => { 35 | console.log(`ipfs ready.`) 36 | 37 | let db 38 | 39 | await ipfs.swarm.connect(MASTER_MULTIADDR) 40 | 41 | try { 42 | const orbitdb = await OrbitDB.createInstance(ipfs, { 43 | directory: "./orbitdb/examples/eventlog" 44 | }); 45 | 46 | // Make the DB public, so that anyone can write. 47 | const options = { 48 | accessController: { 49 | write: ["*"] 50 | } 51 | } 52 | 53 | db = await orbitdb.eventlog(DB_ADDRESS, options); 54 | await db.load(); 55 | 56 | } catch (e) { 57 | console.error(e) 58 | process.exit(1) 59 | } 60 | 61 | const query = async () => { 62 | const index = Math.floor(Math.random() * creatures.length) 63 | const userId = Math.floor(Math.random() * 900 + 100) 64 | 65 | try { 66 | const entry = { avatar: creatures[index], userId: userId } 67 | console.log(`Adding ${entry.avatar} ${entry.userId} to DB.`) 68 | await db.add(entry) 69 | const latest = db.iterator({ limit: 5 }).collect() 70 | let output = `` 71 | output += `[Latest Visitors]\n` 72 | output += `--------------------\n` 73 | output += `ID | Visitor\n` 74 | output += `--------------------\n` 75 | output += latest.reverse().map((e) => e.payload.value.userId + ' | ' + e.payload.value.avatar + ')').join('\n') + `\n` 76 | console.log(output) 77 | } catch (e) { 78 | console.error(e) 79 | process.exit(1) 80 | } 81 | } 82 | 83 | setInterval(query, 1000*30) 84 | }) 85 | -------------------------------------------------------------------------------- /db-replication/master-node/master-node.js: -------------------------------------------------------------------------------- 1 | /* 2 | This is the master node that creates the initial database. It is configured to 3 | use the Circuit Relay feature to help nodes communicate with one another. 4 | */ 5 | 6 | "use strict"; 7 | 8 | // CUSTOMIZE THESE VARIABLES 9 | const DB_NAME = "example878"; 10 | 11 | const IPFS = require("ipfs"); 12 | const OrbitDB = require("orbit-db"); 13 | 14 | const creatures = ["🐙", "🐷", "🐬", "🐞", "🐈", "🙉", "🐸", "🐓"]; 15 | 16 | console.log("Starting..."); 17 | 18 | const ipfs = new IPFS({ 19 | repo: "./orbitdb/examples/ipfs", 20 | start: true, 21 | EXPERIMENTAL: { 22 | pubsub: true 23 | }, 24 | relay: { 25 | enabled: true, // enable circuit relay dialer and listener 26 | hop: { 27 | enabled: true // enable circuit relay HOP (make this node a relay) 28 | } 29 | } 30 | }); 31 | 32 | ipfs.on("error", err => console.error(err)); 33 | 34 | ipfs.on("replicated", () => { 35 | console.log(`replication event fired`); 36 | }); 37 | 38 | ipfs.on("ready", async () => { 39 | let db; 40 | 41 | try { 42 | const orbitdb = await OrbitDB.createInstance(ipfs, { 43 | directory: "./orbitdb/examples/eventlog" 44 | }); 45 | 46 | const options = { 47 | accessController: { 48 | write: ["*"] 49 | } 50 | } 51 | 52 | db = await orbitdb.eventlog(DB_NAME, options); 53 | await db.load(); 54 | 55 | console.log(`db id: ${db.id}`); 56 | } catch (e) { 57 | console.error(e); 58 | process.exit(1); 59 | } 60 | 61 | const query = async () => { 62 | const index = Math.floor(Math.random() * creatures.length); 63 | const userId = Math.floor(Math.random() * 900 + 100); 64 | 65 | try { 66 | const entry = { avatar: creatures[index], userId: userId }; 67 | console.log(`Adding ${entry.avatar} ${entry.userId} to DB.`); 68 | await db.add(entry); 69 | const latest = db.iterator({ limit: 5 }).collect(); 70 | let output = ``; 71 | output += `[Latest Visitors]\n`; 72 | output += `--------------------\n`; 73 | output += `ID | Visitor\n`; 74 | output += `--------------------\n`; 75 | output += 76 | latest 77 | .reverse() 78 | .map( 79 | e => e.payload.value.userId + " | " + e.payload.value.avatar + ")" 80 | ) 81 | .join("\n") + `\n`; 82 | console.log(output); 83 | } catch (e) { 84 | console.error(e); 85 | process.exit(1); 86 | } 87 | }; 88 | 89 | setInterval(query, 1000 * 30); 90 | }); 91 | -------------------------------------------------------------------------------- /db-replication/cr-node/cr-node.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | // CUSTOMIZE THESE VARIABLES 4 | const FIREWALL_PEER_HASH = "QmUrGqa9ZFjg1rYcqpnxHdkPFn2e1GKmkV1F9s5vFrVB5V" 5 | const FIREWALL_PEER_MULTIADDR = `/p2p-circuit/ipfs/${FIREWALL_PEER_HASH}` 6 | const MASTER_MULTIADDR = "/ip4/162.243.158.213/tcp/4002/ipfs/QmQWgBQWpJkf93NW7rgVvycmot6arDxtuJGnArbyuznC6b" 7 | const DB_ADDRESS = "/orbitdb/QmdSeUenVxgwsgbo3ZidV2oxW1RFr5XxGNfVtSRN4p4Eda/example5343234" 8 | 9 | const IPFS = require('ipfs') 10 | const OrbitDB = require('orbit-db') 11 | 12 | const creatures = ['🐙', '🐷', '🐬', '🐞', '🐈', '🙉', '🐸', '🐓'] 13 | 14 | console.log("Starting...") 15 | 16 | const ipfs = new IPFS({ 17 | repo: './orbitdb/examples/ipfs', 18 | start: true, 19 | EXPERIMENTAL: { 20 | pubsub: true, 21 | }, 22 | relay: { 23 | enabled: true, // enable circuit relay dialer and listener 24 | hop: { 25 | enabled: true // enable circuit relay HOP (make this node a relay) 26 | } 27 | }, 28 | pubsub: true 29 | }) 30 | 31 | ipfs.on('error', (err) => console.error(err)) 32 | 33 | ipfs.on("replicated", () => { 34 | console.log(`replication event fired`); 35 | }) 36 | 37 | ipfs.on('ready', async () => { 38 | let db 39 | 40 | await ipfs.swarm.connect(MASTER_MULTIADDR) 41 | console.log(`Connected to Master node`) 42 | 43 | await sleep(10000) 44 | 45 | console.log(`Attempting to connect to firewalled peer ${FIREWALL_PEER_MULTIADDR}`) 46 | await ipfs.swarm.connect(FIREWALL_PEER_MULTIADDR) 47 | console.log(`Connected to firewalled peer node`) 48 | 49 | 50 | try { 51 | const orbitdb = new OrbitDB(ipfs, './orbitdb/examples/eventlog') 52 | db = await orbitdb.eventlog(DB_ADDRESS) 53 | await db.load() 54 | } catch (e) { 55 | console.error(e) 56 | process.exit(1) 57 | } 58 | 59 | const query = async () => { 60 | const index = Math.floor(Math.random() * creatures.length) 61 | const userId = Math.floor(Math.random() * 900 + 100) 62 | 63 | try { 64 | const entry = { avatar: creatures[index], userId: userId } 65 | console.log(`Adding ${entry.avatar} ${entry.userId} to DB.`) 66 | await db.add(entry) 67 | const latest = db.iterator({ limit: 5 }).collect() 68 | let output = `` 69 | output += `[Latest Visitors]\n` 70 | output += `--------------------\n` 71 | output += `ID | Visitor\n` 72 | output += `--------------------\n` 73 | output += latest.reverse().map((e) => e.payload.value.userId + ' | ' + e.payload.value.avatar + ')').join('\n') + `\n` 74 | console.log(output) 75 | } catch (e) { 76 | console.error(e) 77 | process.exit(1) 78 | } 79 | } 80 | 81 | setInterval(query, 1000*30) 82 | }) 83 | 84 | function sleep(ms) { 85 | return new Promise(resolve => setTimeout(resolve, ms)); 86 | } 87 | -------------------------------------------------------------------------------- /order-book/front-end/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | orbitDB Order Book Demo 8 | 9 | 10 | 11 | 12 | 13 | 14 | 18 | 19 | 20 | 21 | 28 | 29 |
30 |
31 |
32 |
33 |

P2P Order Book Demo

34 |

35 | OrbitDB 36 | is a peer-to-peer database running over 37 | IPFS. 38 | This app is a prototype implementation of a buy-sell order book 39 | using this technology. 40 |

41 |

42 | Fork this demo on GitHub! 43 |

44 |
45 |
46 | Blast off with Bootstrap 47 |
48 |
49 | 50 |
51 |
52 | 53 |
54 |
55 |
56 |
57 | 58 |
59 |
60 |
61 | 62 |
63 | 64 |
65 |
66 |
67 |
68 |
69 | 70 | 71 | 72 |
73 |
74 |
75 |
76 | 77 |
78 |
79 |
80 |
81 | 82 | 86 |
87 | 88 |
89 | 90 | 91 |
92 | 93 |
94 | 95 | 96 |
97 | 98 | 99 |
100 |
101 |
102 |
103 | 104 |
105 | 106 |
107 |
108 |
109 |

Open Orders

110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 |
PeerOrderPriceQtyCancel
126 |
127 |
128 |
129 | 130 |
131 | 132 | 133 | 134 | 135 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 333 | 334 | 335 | -------------------------------------------------------------------------------- /order-book/front-end/js/orderbook/orderbook.js: -------------------------------------------------------------------------------- 1 | /* 2 | This library is concerned with DOM management and display of the order book 3 | database. 4 | */ 5 | 6 | "use strict" 7 | 8 | // This function resets the order book table to the way it was when the DOM 9 | // was first loaded. 10 | function resetTable() { 11 | //console.log('resetting the table.'); 12 | 13 | const orderTable = $('#orderTable'); 14 | const allRows = orderTable.find('tr'); 15 | 16 | // Loop through each row in the table. 17 | for(var i=0; i < allRows.length; i++) { 18 | const thisRow = $(allRows[i]); 19 | const thisRowsId = thisRow.prop('id'); 20 | 21 | if(thisRowsId === "trHeader") continue; 22 | if(thisRowsId === "trScaffold") continue; 23 | 24 | thisRow.remove(); 25 | } 26 | } 27 | 28 | 29 | // Display orders from the orderbook in the table. 30 | async function showOrders() { 31 | try { 32 | //console.log('showing orders'); 33 | 34 | // Exit if the DB can not be validated. 35 | const dbIsValid = await validateDb(); 36 | if(!dbIsValid) return; 37 | 38 | // Get the list of peers. 39 | let peers = db.get('peers') 40 | if(!peers) return; // Exit if DB has not peers. 41 | 42 | const myHandle = $('#peerId').val(); 43 | 44 | // Get orders for each peer. 45 | for(var i = 0; i < peers.length; i++) { 46 | const thisPeer = peers[i] 47 | 48 | let orders = db.get(thisPeer) 49 | if(!orders) orders = []; 50 | 51 | //console.log(`Order belonging to ${thisPeer}: ${JSON.stringify(orders)}`) 52 | 53 | // Loop through each order. 54 | for(var j=0; j < orders.length; j++) { 55 | const thisOrder = orders[j]; 56 | const thisRow = $('#trScaffold').clone(); 57 | thisRow.prop('id', ''); // Remove the ID from the clone. 58 | 59 | thisRow.find('.tdPeer').text(thisPeer); 60 | 61 | if(thisOrder.buysell) 62 | thisRow.find('.tdOrder').text('Buy') 63 | else 64 | thisRow.find('.tdOrder').text('Sell') 65 | 66 | thisRow.find('.tdPrice').text(thisOrder.price) 67 | 68 | thisRow.find('.tdQty').text(thisOrder.qty) 69 | 70 | const cancelButton = thisRow.find('.tdCancel').find('button') 71 | if(thisPeer === myHandle) { 72 | cancelButton.show() 73 | cancelButton.prop('data-handle', myHandle); 74 | cancelButton.prop('data-price', thisOrder.price); 75 | cancelButton.prop('data-buysell', thisOrder.buysell); 76 | cancelButton.click(cancelOrder); 77 | } 78 | 79 | $('#orderTable').append(thisRow) 80 | } 81 | } 82 | 83 | } catch(err) { 84 | console.error(`Error in showOrders(): `, err); 85 | throw err; 86 | } 87 | } 88 | 89 | // Called when the user clicks on the Cancel Button next to an order. 90 | async function cancelOrder(event) { 91 | const thisBtn = $(this); 92 | const handle = thisBtn.prop('data-handle'); 93 | const buysell = thisBtn.prop('data-buysell'); 94 | const price = thisBtn.prop('data-price'); 95 | 96 | // Get all orders associated with this user. 97 | const orders = db.get(handle); 98 | 99 | // Filter out the current order. 100 | const newOrders = orders.filter(order => order.buysell !== buysell || order.price !== price); 101 | 102 | // Update the database. 103 | await db.put(handle, newOrders); 104 | 105 | resetTable(); 106 | showOrders(); 107 | } 108 | 109 | // Called when the user clicks the 'Add Order' button. Adds an order 110 | // to the database, then refreshes the order table. 111 | async function addOrder() { 112 | try { 113 | //console.log(`addOrder button clicked!`) 114 | 115 | const buysell = $('#orderType').val() === "buy"; 116 | let price = $('#price').val(); 117 | let qty = $('#qty').val(); 118 | 119 | // Input Validation 120 | if(price === "") return; 121 | if(qty === "") return; 122 | 123 | // Convert qty and price to numbers. 124 | price = Number(price); 125 | if(isNaN(price)) return; 126 | qty = Number(qty); 127 | if(isNaN(qty)) return; 128 | 129 | // Exit if the DB can not be validated. 130 | const dbIsValid = await validateDb(); 131 | if(!dbIsValid) return; 132 | 133 | // Retrieve data from the web forms. 134 | const myHandle = $('#peerId').val(); 135 | const myOrder = { 136 | buysell: buysell, 137 | price: price, 138 | qty: qty, 139 | peer: myHandle 140 | } 141 | 142 | // Get the list of peers 143 | let peers = db.get('peers') 144 | 145 | // Get existing orders for this peer. 146 | let myOrders = db.get(myHandle); 147 | 148 | myOrders.push(myOrder); 149 | 150 | // Save the new order to the DB. 151 | await db.put(myHandle, myOrders); 152 | 153 | console.log(`Order added to DB!`) 154 | 155 | resetTable(); 156 | showOrders(); 157 | 158 | // Try to match orders. 159 | matchOrders(); 160 | 161 | } catch(err) { 162 | console.error(`Error in addOrder(): `,err); 163 | throw err; 164 | } 165 | } 166 | 167 | // Navigates the database and attempts to match buy-sell orders. 168 | async function matchOrders() { 169 | try { 170 | // Exit if the DB can not be validated. 171 | const dbIsValid = await validateDb(); 172 | if(!dbIsValid) return; 173 | 174 | // Retrieve data from the web forms. 175 | const myHandle = $('#peerId').val(); 176 | 177 | // Get order data from the DB. 178 | const peers = db.get('peers'); 179 | const userOrders = db.get(myHandle); 180 | 181 | // handling a bug. 182 | if(!userOrders) { 183 | //debugger; 184 | return; 185 | } 186 | 187 | // Loop through each order associated with this user. 188 | for(var k=0; k < userOrders.length; k++) { 189 | const userOrder = userOrders[k]; 190 | 191 | // BUY ORDERS 192 | if(userOrder.buysell) { 193 | 194 | // Loop through each peer in the db. 195 | for(var i=0; i < peers.length; i++) { 196 | const thisPeer = peers[i]; 197 | 198 | // if the current peer is the current user, then skip. 199 | if(thisPeer === myHandle) continue; 200 | 201 | // Loop through each order associated with this peer. 202 | const orders = db.get(thisPeer); 203 | for(var j=0; j < orders.length; j++) { 204 | const thisOrder = orders[j]; 205 | 206 | // If this is a buy order, skip 207 | if(thisOrder.buysell) continue; 208 | 209 | // Match if buy price is greater than sell price. 210 | if(userOrder.price >= thisOrder.price) { 211 | //console.log(`userOrder: ${JSON.stringify(userOrder,null,2)}`); 212 | //console.log(`thisOrder: ${JSON.stringify(thisOrder,null,2)}`); 213 | 214 | console.log(`Match with ${thisPeer} as seller at ${thisOrder.price} and ${myHandle} as buyer at ${userOrder.price}`); 215 | 216 | alert(`You've been matched! 217 | Seller: ${thisPeer} 218 | Buyer: You (${myHandle})`); 219 | 220 | // Update DB with results of trade. 221 | tradeDb(thisOrder, userOrder); 222 | } 223 | } 224 | } 225 | 226 | // SELL ORDERS 227 | } else { 228 | 229 | // Loop through each peer in the db. 230 | for(var i=0; i < peers.length; i++) { 231 | const thisPeer = peers[i]; 232 | 233 | // if the current peer is the current user, then skip. 234 | if(thisPeer === myHandle) continue; 235 | 236 | // Loop through each order associated with this peer. 237 | const orders = db.get(thisPeer); 238 | for(var j=0; j < orders.length; j++) { 239 | const thisOrder = orders[j]; 240 | 241 | // If this is a sell order, skip 242 | if(!thisOrder.buysell) continue; 243 | 244 | // Match if buy price is greater than sell price. 245 | if(thisOrder.price >= userOrder.price) { 246 | //console.log(`userOrder: ${JSON.stringify(userOrder,null,2)}`); 247 | //console.log(`thisOrder: ${JSON.stringify(thisOrder,null,2)}`); 248 | 249 | console.log(`Match with ${thisPeer} as buyer at ${thisOrder.price} and ${myHandle} as seller at ${userOrder.price}`); 250 | 251 | alert(`You've been matched! 252 | Seller: You (${myHandle}) 253 | Buyer: ${thisPeer}`); 254 | 255 | // Update DB with results of trade. 256 | tradeDb(userOrder, thisOrder); 257 | } 258 | } 259 | } 260 | } 261 | 262 | } 263 | } catch(err) { 264 | console.error(`Error in matchOrders(): `, err); 265 | debugger; 266 | throw err; 267 | } 268 | } 269 | 270 | // Updated the DB with the results of a matched trade. 271 | async function tradeDb(sellOrder, buyOrder) { 272 | try { 273 | console.log(`Resolving trade in DB.`); 274 | 275 | // Guards/Validation 276 | if(sellOrder.buysell) return; 277 | if(!buyOrder.buysell) return; 278 | 279 | const buyer = buyOrder.peer; 280 | const seller = sellOrder.peer; 281 | const buyerOrders = db.get(buyer); 282 | const sellerOrders = db.get(seller); 283 | 284 | // Special case where they are equal, remove both entries from the DB. 285 | if(sellOrder.qty === buyOrder.qty) { 286 | const newBuyerOrders = buyerOrders.filter(order => (order.price !== buyOrder.price && order.qty !== buyOrder.qty)); 287 | await db.put(buyer, newBuyerOrders); 288 | 289 | const newSellerOrders = sellerOrders.filter(order => (order.price !== sellOrder.price && order.qty !== sellOrder.qty)); 290 | await db.put(seller, newSellerOrders); 291 | 292 | // If sell qty < buy qty, sell all. 293 | } else if(sellOrder.qty < buyOrder.qty) { 294 | // Remove the sell order since this has been fulfilled. 295 | const newSellerOrders = sellerOrders.filter(order => (order.price !== sellOrder.price && order.qty !== sellOrder.qty)); 296 | await db.put(seller, newSellerOrders); 297 | 298 | // Reduce the buy order by the amount sold. 299 | const newBuyerOrders = buyerOrders.map(order => { 300 | if(order.price === buyOrder.price && order.qty === buyOrder.qty) { 301 | order.qty = buyOrder.qty - sellOrder.qty; 302 | } 303 | return order; 304 | }); 305 | await db.put(buyer, newBuyerOrders); 306 | 307 | // else sell partial, buy all. 308 | } else { 309 | // Remove the buy order since this has been fulfilled. 310 | const newBuyerOrders = buyerOrders.filter(order => (order.price !== buyOrder.price && order.qty !== buyOrder.qty)); 311 | await db.put(buyer, newBuyerOrders); 312 | 313 | // Reduce the sell order by the amount bought. 314 | const newSellerOrders = sellerOrders.map(order => { 315 | if(order.price === sellOrder.price && order.qty === sellOrder.qty) { 316 | order.qty = sellOrder.qty - buyOrder.qty; 317 | } 318 | return order; 319 | }); 320 | await db.put(seller, newSellerOrders); 321 | } 322 | 323 | // Refresh the DOM. 324 | resetTable(); 325 | showOrders(); 326 | 327 | } catch(err) { 328 | console.error(`Error in tradeDb(): `, err); 329 | throw err; 330 | } 331 | } 332 | 333 | // This is a 'guard' that is used ensure the DB exists and the current users exists 334 | // in it. It returns true on success or false if there is a problem that can not 335 | // be automatically handled. 336 | async function validateDb() { 337 | // Exit if DB is not ready. 338 | if(!dbReady) return false; 339 | 340 | // Retrieve data from the web forms. 341 | const myHandle = $('#peerId').val(); 342 | if(myHandle === "") return false; 343 | 344 | // Get the list of peers 345 | let peers = db.get('peers') 346 | 347 | // Handle a new DB. 348 | if(!peers) { 349 | peers = [myHandle] 350 | await db.put('peers', peers); 351 | 352 | // Add user to peers list if they aren't already there. 353 | } else { 354 | if(peers.indexOf(myHandle) === -1) { 355 | 356 | // Add current user to the list of peer. 357 | peers.push(myHandle); 358 | await db.put('peers', peers); 359 | } 360 | } 361 | 362 | // Get orders associated with this peer 363 | const myOrders = db.get(myHandle); 364 | if(!myOrders) { 365 | // Create an empty array to store this users orders. 366 | await db.put(myHandle, []); 367 | } 368 | 369 | return true; 370 | } 371 | -------------------------------------------------------------------------------- /order-book/front-end/css/bootstrap-theme.min.css: -------------------------------------------------------------------------------- 1 | /*! 2 | * Bootstrap v3.3.1 (http://getbootstrap.com) 3 | * Copyright 2011-2014 Twitter, Inc. 4 | * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) 5 | */.btn-default,.btn-primary,.btn-success,.btn-info,.btn-warning,.btn-danger{text-shadow:0 -1px 0 rgba(0,0,0,.2);-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,.15),0 1px 1px rgba(0,0,0,.075);box-shadow:inset 0 1px 0 rgba(255,255,255,.15),0 1px 1px rgba(0,0,0,.075)}.btn-default:active,.btn-primary:active,.btn-success:active,.btn-info:active,.btn-warning:active,.btn-danger:active,.btn-default.active,.btn-primary.active,.btn-success.active,.btn-info.active,.btn-warning.active,.btn-danger.active{-webkit-box-shadow:inset 0 3px 5px rgba(0,0,0,.125);box-shadow:inset 0 3px 5px rgba(0,0,0,.125)}.btn-default .badge,.btn-primary .badge,.btn-success .badge,.btn-info .badge,.btn-warning .badge,.btn-danger .badge{text-shadow:none}.btn:active,.btn.active{background-image:none}.btn-default{text-shadow:0 1px 0 #fff;background-image:-webkit-linear-gradient(top,#fff 0,#e0e0e0 100%);background-image:-o-linear-gradient(top,#fff 0,#e0e0e0 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#fff),to(#e0e0e0));background-image:linear-gradient(to bottom,#fff 0,#e0e0e0 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff', endColorstr='#ffe0e0e0', GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);background-repeat:repeat-x;border-color:#dbdbdb;border-color:#ccc}.btn-default:hover,.btn-default:focus{background-color:#e0e0e0;background-position:0 -15px}.btn-default:active,.btn-default.active{background-color:#e0e0e0;border-color:#dbdbdb}.btn-default:disabled,.btn-default[disabled]{background-color:#e0e0e0;background-image:none}.btn-primary{background-image:-webkit-linear-gradient(top,#337ab7 0,#265a88 100%);background-image:-o-linear-gradient(top,#337ab7 0,#265a88 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#337ab7),to(#265a88));background-image:linear-gradient(to bottom,#337ab7 0,#265a88 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff265a88', GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);background-repeat:repeat-x;border-color:#245580}.btn-primary:hover,.btn-primary:focus{background-color:#265a88;background-position:0 -15px}.btn-primary:active,.btn-primary.active{background-color:#265a88;border-color:#245580}.btn-primary:disabled,.btn-primary[disabled]{background-color:#265a88;background-image:none}.btn-success{background-image:-webkit-linear-gradient(top,#5cb85c 0,#419641 100%);background-image:-o-linear-gradient(top,#5cb85c 0,#419641 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#5cb85c),to(#419641));background-image:linear-gradient(to bottom,#5cb85c 0,#419641 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5cb85c', endColorstr='#ff419641', GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);background-repeat:repeat-x;border-color:#3e8f3e}.btn-success:hover,.btn-success:focus{background-color:#419641;background-position:0 -15px}.btn-success:active,.btn-success.active{background-color:#419641;border-color:#3e8f3e}.btn-success:disabled,.btn-success[disabled]{background-color:#419641;background-image:none}.btn-info{background-image:-webkit-linear-gradient(top,#5bc0de 0,#2aabd2 100%);background-image:-o-linear-gradient(top,#5bc0de 0,#2aabd2 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#5bc0de),to(#2aabd2));background-image:linear-gradient(to bottom,#5bc0de 0,#2aabd2 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5bc0de', endColorstr='#ff2aabd2', GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);background-repeat:repeat-x;border-color:#28a4c9}.btn-info:hover,.btn-info:focus{background-color:#2aabd2;background-position:0 -15px}.btn-info:active,.btn-info.active{background-color:#2aabd2;border-color:#28a4c9}.btn-info:disabled,.btn-info[disabled]{background-color:#2aabd2;background-image:none}.btn-warning{background-image:-webkit-linear-gradient(top,#f0ad4e 0,#eb9316 100%);background-image:-o-linear-gradient(top,#f0ad4e 0,#eb9316 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#f0ad4e),to(#eb9316));background-image:linear-gradient(to bottom,#f0ad4e 0,#eb9316 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff0ad4e', endColorstr='#ffeb9316', GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);background-repeat:repeat-x;border-color:#e38d13}.btn-warning:hover,.btn-warning:focus{background-color:#eb9316;background-position:0 -15px}.btn-warning:active,.btn-warning.active{background-color:#eb9316;border-color:#e38d13}.btn-warning:disabled,.btn-warning[disabled]{background-color:#eb9316;background-image:none}.btn-danger{background-image:-webkit-linear-gradient(top,#d9534f 0,#c12e2a 100%);background-image:-o-linear-gradient(top,#d9534f 0,#c12e2a 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#d9534f),to(#c12e2a));background-image:linear-gradient(to bottom,#d9534f 0,#c12e2a 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9534f', endColorstr='#ffc12e2a', GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);background-repeat:repeat-x;border-color:#b92c28}.btn-danger:hover,.btn-danger:focus{background-color:#c12e2a;background-position:0 -15px}.btn-danger:active,.btn-danger.active{background-color:#c12e2a;border-color:#b92c28}.btn-danger:disabled,.btn-danger[disabled]{background-color:#c12e2a;background-image:none}.thumbnail,.img-thumbnail{-webkit-box-shadow:0 1px 2px rgba(0,0,0,.075);box-shadow:0 1px 2px rgba(0,0,0,.075)}.dropdown-menu>li>a:hover,.dropdown-menu>li>a:focus{background-color:#e8e8e8;background-image:-webkit-linear-gradient(top,#f5f5f5 0,#e8e8e8 100%);background-image:-o-linear-gradient(top,#f5f5f5 0,#e8e8e8 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#f5f5f5),to(#e8e8e8));background-image:linear-gradient(to bottom,#f5f5f5 0,#e8e8e8 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff5f5f5', endColorstr='#ffe8e8e8', GradientType=0);background-repeat:repeat-x}.dropdown-menu>.active>a,.dropdown-menu>.active>a:hover,.dropdown-menu>.active>a:focus{background-color:#2e6da4;background-image:-webkit-linear-gradient(top,#337ab7 0,#2e6da4 100%);background-image:-o-linear-gradient(top,#337ab7 0,#2e6da4 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#337ab7),to(#2e6da4));background-image:linear-gradient(to bottom,#337ab7 0,#2e6da4 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff2e6da4', GradientType=0);background-repeat:repeat-x}.navbar-default{background-image:-webkit-linear-gradient(top,#fff 0,#f8f8f8 100%);background-image:-o-linear-gradient(top,#fff 0,#f8f8f8 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#fff),to(#f8f8f8));background-image:linear-gradient(to bottom,#fff 0,#f8f8f8 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff', endColorstr='#fff8f8f8', GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);background-repeat:repeat-x;border-radius:4px;-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,.15),0 1px 5px rgba(0,0,0,.075);box-shadow:inset 0 1px 0 rgba(255,255,255,.15),0 1px 5px rgba(0,0,0,.075)}.navbar-default .navbar-nav>.open>a,.navbar-default .navbar-nav>.active>a{background-image:-webkit-linear-gradient(top,#dbdbdb 0,#e2e2e2 100%);background-image:-o-linear-gradient(top,#dbdbdb 0,#e2e2e2 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#dbdbdb),to(#e2e2e2));background-image:linear-gradient(to bottom,#dbdbdb 0,#e2e2e2 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdbdbdb', endColorstr='#ffe2e2e2', GradientType=0);background-repeat:repeat-x;-webkit-box-shadow:inset 0 3px 9px rgba(0,0,0,.075);box-shadow:inset 0 3px 9px rgba(0,0,0,.075)}.navbar-brand,.navbar-nav>li>a{text-shadow:0 1px 0 rgba(255,255,255,.25)}.navbar-inverse{background-image:-webkit-linear-gradient(top,#3c3c3c 0,#222 100%);background-image:-o-linear-gradient(top,#3c3c3c 0,#222 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#3c3c3c),to(#222));background-image:linear-gradient(to bottom,#3c3c3c 0,#222 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff3c3c3c', endColorstr='#ff222222', GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);background-repeat:repeat-x}.navbar-inverse .navbar-nav>.open>a,.navbar-inverse .navbar-nav>.active>a{background-image:-webkit-linear-gradient(top,#080808 0,#0f0f0f 100%);background-image:-o-linear-gradient(top,#080808 0,#0f0f0f 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#080808),to(#0f0f0f));background-image:linear-gradient(to bottom,#080808 0,#0f0f0f 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff080808', endColorstr='#ff0f0f0f', GradientType=0);background-repeat:repeat-x;-webkit-box-shadow:inset 0 3px 9px rgba(0,0,0,.25);box-shadow:inset 0 3px 9px rgba(0,0,0,.25)}.navbar-inverse .navbar-brand,.navbar-inverse .navbar-nav>li>a{text-shadow:0 -1px 0 rgba(0,0,0,.25)}.navbar-static-top,.navbar-fixed-top,.navbar-fixed-bottom{border-radius:0}@media (max-width:767px){.navbar .navbar-nav .open .dropdown-menu>.active>a,.navbar .navbar-nav .open .dropdown-menu>.active>a:hover,.navbar .navbar-nav .open .dropdown-menu>.active>a:focus{color:#fff;background-image:-webkit-linear-gradient(top,#337ab7 0,#2e6da4 100%);background-image:-o-linear-gradient(top,#337ab7 0,#2e6da4 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#337ab7),to(#2e6da4));background-image:linear-gradient(to bottom,#337ab7 0,#2e6da4 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff2e6da4', GradientType=0);background-repeat:repeat-x}}.alert{text-shadow:0 1px 0 rgba(255,255,255,.2);-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,.25),0 1px 2px rgba(0,0,0,.05);box-shadow:inset 0 1px 0 rgba(255,255,255,.25),0 1px 2px rgba(0,0,0,.05)}.alert-success{background-image:-webkit-linear-gradient(top,#dff0d8 0,#c8e5bc 100%);background-image:-o-linear-gradient(top,#dff0d8 0,#c8e5bc 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#dff0d8),to(#c8e5bc));background-image:linear-gradient(to bottom,#dff0d8 0,#c8e5bc 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdff0d8', endColorstr='#ffc8e5bc', GradientType=0);background-repeat:repeat-x;border-color:#b2dba1}.alert-info{background-image:-webkit-linear-gradient(top,#d9edf7 0,#b9def0 100%);background-image:-o-linear-gradient(top,#d9edf7 0,#b9def0 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#d9edf7),to(#b9def0));background-image:linear-gradient(to bottom,#d9edf7 0,#b9def0 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9edf7', endColorstr='#ffb9def0', GradientType=0);background-repeat:repeat-x;border-color:#9acfea}.alert-warning{background-image:-webkit-linear-gradient(top,#fcf8e3 0,#f8efc0 100%);background-image:-o-linear-gradient(top,#fcf8e3 0,#f8efc0 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#fcf8e3),to(#f8efc0));background-image:linear-gradient(to bottom,#fcf8e3 0,#f8efc0 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffcf8e3', endColorstr='#fff8efc0', GradientType=0);background-repeat:repeat-x;border-color:#f5e79e}.alert-danger{background-image:-webkit-linear-gradient(top,#f2dede 0,#e7c3c3 100%);background-image:-o-linear-gradient(top,#f2dede 0,#e7c3c3 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#f2dede),to(#e7c3c3));background-image:linear-gradient(to bottom,#f2dede 0,#e7c3c3 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff2dede', endColorstr='#ffe7c3c3', GradientType=0);background-repeat:repeat-x;border-color:#dca7a7}.progress{background-image:-webkit-linear-gradient(top,#ebebeb 0,#f5f5f5 100%);background-image:-o-linear-gradient(top,#ebebeb 0,#f5f5f5 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#ebebeb),to(#f5f5f5));background-image:linear-gradient(to bottom,#ebebeb 0,#f5f5f5 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffebebeb', endColorstr='#fff5f5f5', GradientType=0);background-repeat:repeat-x}.progress-bar{background-image:-webkit-linear-gradient(top,#337ab7 0,#286090 100%);background-image:-o-linear-gradient(top,#337ab7 0,#286090 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#337ab7),to(#286090));background-image:linear-gradient(to bottom,#337ab7 0,#286090 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff286090', GradientType=0);background-repeat:repeat-x}.progress-bar-success{background-image:-webkit-linear-gradient(top,#5cb85c 0,#449d44 100%);background-image:-o-linear-gradient(top,#5cb85c 0,#449d44 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#5cb85c),to(#449d44));background-image:linear-gradient(to bottom,#5cb85c 0,#449d44 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5cb85c', endColorstr='#ff449d44', GradientType=0);background-repeat:repeat-x}.progress-bar-info{background-image:-webkit-linear-gradient(top,#5bc0de 0,#31b0d5 100%);background-image:-o-linear-gradient(top,#5bc0de 0,#31b0d5 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#5bc0de),to(#31b0d5));background-image:linear-gradient(to bottom,#5bc0de 0,#31b0d5 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5bc0de', endColorstr='#ff31b0d5', GradientType=0);background-repeat:repeat-x}.progress-bar-warning{background-image:-webkit-linear-gradient(top,#f0ad4e 0,#ec971f 100%);background-image:-o-linear-gradient(top,#f0ad4e 0,#ec971f 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#f0ad4e),to(#ec971f));background-image:linear-gradient(to bottom,#f0ad4e 0,#ec971f 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff0ad4e', endColorstr='#ffec971f', GradientType=0);background-repeat:repeat-x}.progress-bar-danger{background-image:-webkit-linear-gradient(top,#d9534f 0,#c9302c 100%);background-image:-o-linear-gradient(top,#d9534f 0,#c9302c 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#d9534f),to(#c9302c));background-image:linear-gradient(to bottom,#d9534f 0,#c9302c 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9534f', endColorstr='#ffc9302c', GradientType=0);background-repeat:repeat-x}.progress-bar-striped{background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent)}.list-group{border-radius:4px;-webkit-box-shadow:0 1px 2px rgba(0,0,0,.075);box-shadow:0 1px 2px rgba(0,0,0,.075)}.list-group-item.active,.list-group-item.active:hover,.list-group-item.active:focus{text-shadow:0 -1px 0 #286090;background-image:-webkit-linear-gradient(top,#337ab7 0,#2b669a 100%);background-image:-o-linear-gradient(top,#337ab7 0,#2b669a 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#337ab7),to(#2b669a));background-image:linear-gradient(to bottom,#337ab7 0,#2b669a 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff2b669a', GradientType=0);background-repeat:repeat-x;border-color:#2b669a}.list-group-item.active .badge,.list-group-item.active:hover .badge,.list-group-item.active:focus .badge{text-shadow:none}.panel{-webkit-box-shadow:0 1px 2px rgba(0,0,0,.05);box-shadow:0 1px 2px rgba(0,0,0,.05)}.panel-default>.panel-heading{background-image:-webkit-linear-gradient(top,#f5f5f5 0,#e8e8e8 100%);background-image:-o-linear-gradient(top,#f5f5f5 0,#e8e8e8 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#f5f5f5),to(#e8e8e8));background-image:linear-gradient(to bottom,#f5f5f5 0,#e8e8e8 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff5f5f5', endColorstr='#ffe8e8e8', GradientType=0);background-repeat:repeat-x}.panel-primary>.panel-heading{background-image:-webkit-linear-gradient(top,#337ab7 0,#2e6da4 100%);background-image:-o-linear-gradient(top,#337ab7 0,#2e6da4 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#337ab7),to(#2e6da4));background-image:linear-gradient(to bottom,#337ab7 0,#2e6da4 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff2e6da4', GradientType=0);background-repeat:repeat-x}.panel-success>.panel-heading{background-image:-webkit-linear-gradient(top,#dff0d8 0,#d0e9c6 100%);background-image:-o-linear-gradient(top,#dff0d8 0,#d0e9c6 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#dff0d8),to(#d0e9c6));background-image:linear-gradient(to bottom,#dff0d8 0,#d0e9c6 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdff0d8', endColorstr='#ffd0e9c6', GradientType=0);background-repeat:repeat-x}.panel-info>.panel-heading{background-image:-webkit-linear-gradient(top,#d9edf7 0,#c4e3f3 100%);background-image:-o-linear-gradient(top,#d9edf7 0,#c4e3f3 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#d9edf7),to(#c4e3f3));background-image:linear-gradient(to bottom,#d9edf7 0,#c4e3f3 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9edf7', endColorstr='#ffc4e3f3', GradientType=0);background-repeat:repeat-x}.panel-warning>.panel-heading{background-image:-webkit-linear-gradient(top,#fcf8e3 0,#faf2cc 100%);background-image:-o-linear-gradient(top,#fcf8e3 0,#faf2cc 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#fcf8e3),to(#faf2cc));background-image:linear-gradient(to bottom,#fcf8e3 0,#faf2cc 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffcf8e3', endColorstr='#fffaf2cc', GradientType=0);background-repeat:repeat-x}.panel-danger>.panel-heading{background-image:-webkit-linear-gradient(top,#f2dede 0,#ebcccc 100%);background-image:-o-linear-gradient(top,#f2dede 0,#ebcccc 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#f2dede),to(#ebcccc));background-image:linear-gradient(to bottom,#f2dede 0,#ebcccc 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff2dede', endColorstr='#ffebcccc', GradientType=0);background-repeat:repeat-x}.well{background-image:-webkit-linear-gradient(top,#e8e8e8 0,#f5f5f5 100%);background-image:-o-linear-gradient(top,#e8e8e8 0,#f5f5f5 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#e8e8e8),to(#f5f5f5));background-image:linear-gradient(to bottom,#e8e8e8 0,#f5f5f5 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffe8e8e8', endColorstr='#fff5f5f5', GradientType=0);background-repeat:repeat-x;border-color:#dcdcdc;-webkit-box-shadow:inset 0 1px 3px rgba(0,0,0,.05),0 1px 0 rgba(255,255,255,.1);box-shadow:inset 0 1px 3px rgba(0,0,0,.05),0 1px 0 rgba(255,255,255,.1)} -------------------------------------------------------------------------------- /order-book/front-end/css/bootstrap-theme.css: -------------------------------------------------------------------------------- 1 | /*! 2 | * Bootstrap v3.3.1 (http://getbootstrap.com) 3 | * Copyright 2011-2014 Twitter, Inc. 4 | * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) 5 | */ 6 | 7 | .btn-default, 8 | .btn-primary, 9 | .btn-success, 10 | .btn-info, 11 | .btn-warning, 12 | .btn-danger { 13 | text-shadow: 0 -1px 0 rgba(0, 0, 0, .2); 14 | -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, .15), 0 1px 1px rgba(0, 0, 0, .075); 15 | box-shadow: inset 0 1px 0 rgba(255, 255, 255, .15), 0 1px 1px rgba(0, 0, 0, .075); 16 | } 17 | .btn-default:active, 18 | .btn-primary:active, 19 | .btn-success:active, 20 | .btn-info:active, 21 | .btn-warning:active, 22 | .btn-danger:active, 23 | .btn-default.active, 24 | .btn-primary.active, 25 | .btn-success.active, 26 | .btn-info.active, 27 | .btn-warning.active, 28 | .btn-danger.active { 29 | -webkit-box-shadow: inset 0 3px 5px rgba(0, 0, 0, .125); 30 | box-shadow: inset 0 3px 5px rgba(0, 0, 0, .125); 31 | } 32 | .btn-default .badge, 33 | .btn-primary .badge, 34 | .btn-success .badge, 35 | .btn-info .badge, 36 | .btn-warning .badge, 37 | .btn-danger .badge { 38 | text-shadow: none; 39 | } 40 | .btn:active, 41 | .btn.active { 42 | background-image: none; 43 | } 44 | .btn-default { 45 | text-shadow: 0 1px 0 #fff; 46 | background-image: -webkit-linear-gradient(top, #fff 0%, #e0e0e0 100%); 47 | background-image: -o-linear-gradient(top, #fff 0%, #e0e0e0 100%); 48 | background-image: -webkit-gradient(linear, left top, left bottom, from(#fff), to(#e0e0e0)); 49 | background-image: linear-gradient(to bottom, #fff 0%, #e0e0e0 100%); 50 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff', endColorstr='#ffe0e0e0', GradientType=0); 51 | filter: progid:DXImageTransform.Microsoft.gradient(enabled = false); 52 | background-repeat: repeat-x; 53 | border-color: #dbdbdb; 54 | border-color: #ccc; 55 | } 56 | .btn-default:hover, 57 | .btn-default:focus { 58 | background-color: #e0e0e0; 59 | background-position: 0 -15px; 60 | } 61 | .btn-default:active, 62 | .btn-default.active { 63 | background-color: #e0e0e0; 64 | border-color: #dbdbdb; 65 | } 66 | .btn-default:disabled, 67 | .btn-default[disabled] { 68 | background-color: #e0e0e0; 69 | background-image: none; 70 | } 71 | .btn-primary { 72 | background-image: -webkit-linear-gradient(top, #337ab7 0%, #265a88 100%); 73 | background-image: -o-linear-gradient(top, #337ab7 0%, #265a88 100%); 74 | background-image: -webkit-gradient(linear, left top, left bottom, from(#337ab7), to(#265a88)); 75 | background-image: linear-gradient(to bottom, #337ab7 0%, #265a88 100%); 76 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff265a88', GradientType=0); 77 | filter: progid:DXImageTransform.Microsoft.gradient(enabled = false); 78 | background-repeat: repeat-x; 79 | border-color: #245580; 80 | } 81 | .btn-primary:hover, 82 | .btn-primary:focus { 83 | background-color: #265a88; 84 | background-position: 0 -15px; 85 | } 86 | .btn-primary:active, 87 | .btn-primary.active { 88 | background-color: #265a88; 89 | border-color: #245580; 90 | } 91 | .btn-primary:disabled, 92 | .btn-primary[disabled] { 93 | background-color: #265a88; 94 | background-image: none; 95 | } 96 | .btn-success { 97 | background-image: -webkit-linear-gradient(top, #5cb85c 0%, #419641 100%); 98 | background-image: -o-linear-gradient(top, #5cb85c 0%, #419641 100%); 99 | background-image: -webkit-gradient(linear, left top, left bottom, from(#5cb85c), to(#419641)); 100 | background-image: linear-gradient(to bottom, #5cb85c 0%, #419641 100%); 101 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5cb85c', endColorstr='#ff419641', GradientType=0); 102 | filter: progid:DXImageTransform.Microsoft.gradient(enabled = false); 103 | background-repeat: repeat-x; 104 | border-color: #3e8f3e; 105 | } 106 | .btn-success:hover, 107 | .btn-success:focus { 108 | background-color: #419641; 109 | background-position: 0 -15px; 110 | } 111 | .btn-success:active, 112 | .btn-success.active { 113 | background-color: #419641; 114 | border-color: #3e8f3e; 115 | } 116 | .btn-success:disabled, 117 | .btn-success[disabled] { 118 | background-color: #419641; 119 | background-image: none; 120 | } 121 | .btn-info { 122 | background-image: -webkit-linear-gradient(top, #5bc0de 0%, #2aabd2 100%); 123 | background-image: -o-linear-gradient(top, #5bc0de 0%, #2aabd2 100%); 124 | background-image: -webkit-gradient(linear, left top, left bottom, from(#5bc0de), to(#2aabd2)); 125 | background-image: linear-gradient(to bottom, #5bc0de 0%, #2aabd2 100%); 126 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5bc0de', endColorstr='#ff2aabd2', GradientType=0); 127 | filter: progid:DXImageTransform.Microsoft.gradient(enabled = false); 128 | background-repeat: repeat-x; 129 | border-color: #28a4c9; 130 | } 131 | .btn-info:hover, 132 | .btn-info:focus { 133 | background-color: #2aabd2; 134 | background-position: 0 -15px; 135 | } 136 | .btn-info:active, 137 | .btn-info.active { 138 | background-color: #2aabd2; 139 | border-color: #28a4c9; 140 | } 141 | .btn-info:disabled, 142 | .btn-info[disabled] { 143 | background-color: #2aabd2; 144 | background-image: none; 145 | } 146 | .btn-warning { 147 | background-image: -webkit-linear-gradient(top, #f0ad4e 0%, #eb9316 100%); 148 | background-image: -o-linear-gradient(top, #f0ad4e 0%, #eb9316 100%); 149 | background-image: -webkit-gradient(linear, left top, left bottom, from(#f0ad4e), to(#eb9316)); 150 | background-image: linear-gradient(to bottom, #f0ad4e 0%, #eb9316 100%); 151 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff0ad4e', endColorstr='#ffeb9316', GradientType=0); 152 | filter: progid:DXImageTransform.Microsoft.gradient(enabled = false); 153 | background-repeat: repeat-x; 154 | border-color: #e38d13; 155 | } 156 | .btn-warning:hover, 157 | .btn-warning:focus { 158 | background-color: #eb9316; 159 | background-position: 0 -15px; 160 | } 161 | .btn-warning:active, 162 | .btn-warning.active { 163 | background-color: #eb9316; 164 | border-color: #e38d13; 165 | } 166 | .btn-warning:disabled, 167 | .btn-warning[disabled] { 168 | background-color: #eb9316; 169 | background-image: none; 170 | } 171 | .btn-danger { 172 | background-image: -webkit-linear-gradient(top, #d9534f 0%, #c12e2a 100%); 173 | background-image: -o-linear-gradient(top, #d9534f 0%, #c12e2a 100%); 174 | background-image: -webkit-gradient(linear, left top, left bottom, from(#d9534f), to(#c12e2a)); 175 | background-image: linear-gradient(to bottom, #d9534f 0%, #c12e2a 100%); 176 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9534f', endColorstr='#ffc12e2a', GradientType=0); 177 | filter: progid:DXImageTransform.Microsoft.gradient(enabled = false); 178 | background-repeat: repeat-x; 179 | border-color: #b92c28; 180 | } 181 | .btn-danger:hover, 182 | .btn-danger:focus { 183 | background-color: #c12e2a; 184 | background-position: 0 -15px; 185 | } 186 | .btn-danger:active, 187 | .btn-danger.active { 188 | background-color: #c12e2a; 189 | border-color: #b92c28; 190 | } 191 | .btn-danger:disabled, 192 | .btn-danger[disabled] { 193 | background-color: #c12e2a; 194 | background-image: none; 195 | } 196 | .thumbnail, 197 | .img-thumbnail { 198 | -webkit-box-shadow: 0 1px 2px rgba(0, 0, 0, .075); 199 | box-shadow: 0 1px 2px rgba(0, 0, 0, .075); 200 | } 201 | .dropdown-menu > li > a:hover, 202 | .dropdown-menu > li > a:focus { 203 | background-color: #e8e8e8; 204 | background-image: -webkit-linear-gradient(top, #f5f5f5 0%, #e8e8e8 100%); 205 | background-image: -o-linear-gradient(top, #f5f5f5 0%, #e8e8e8 100%); 206 | background-image: -webkit-gradient(linear, left top, left bottom, from(#f5f5f5), to(#e8e8e8)); 207 | background-image: linear-gradient(to bottom, #f5f5f5 0%, #e8e8e8 100%); 208 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff5f5f5', endColorstr='#ffe8e8e8', GradientType=0); 209 | background-repeat: repeat-x; 210 | } 211 | .dropdown-menu > .active > a, 212 | .dropdown-menu > .active > a:hover, 213 | .dropdown-menu > .active > a:focus { 214 | background-color: #2e6da4; 215 | background-image: -webkit-linear-gradient(top, #337ab7 0%, #2e6da4 100%); 216 | background-image: -o-linear-gradient(top, #337ab7 0%, #2e6da4 100%); 217 | background-image: -webkit-gradient(linear, left top, left bottom, from(#337ab7), to(#2e6da4)); 218 | background-image: linear-gradient(to bottom, #337ab7 0%, #2e6da4 100%); 219 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff2e6da4', GradientType=0); 220 | background-repeat: repeat-x; 221 | } 222 | .navbar-default { 223 | background-image: -webkit-linear-gradient(top, #fff 0%, #f8f8f8 100%); 224 | background-image: -o-linear-gradient(top, #fff 0%, #f8f8f8 100%); 225 | background-image: -webkit-gradient(linear, left top, left bottom, from(#fff), to(#f8f8f8)); 226 | background-image: linear-gradient(to bottom, #fff 0%, #f8f8f8 100%); 227 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff', endColorstr='#fff8f8f8', GradientType=0); 228 | filter: progid:DXImageTransform.Microsoft.gradient(enabled = false); 229 | background-repeat: repeat-x; 230 | border-radius: 4px; 231 | -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, .15), 0 1px 5px rgba(0, 0, 0, .075); 232 | box-shadow: inset 0 1px 0 rgba(255, 255, 255, .15), 0 1px 5px rgba(0, 0, 0, .075); 233 | } 234 | .navbar-default .navbar-nav > .open > a, 235 | .navbar-default .navbar-nav > .active > a { 236 | background-image: -webkit-linear-gradient(top, #dbdbdb 0%, #e2e2e2 100%); 237 | background-image: -o-linear-gradient(top, #dbdbdb 0%, #e2e2e2 100%); 238 | background-image: -webkit-gradient(linear, left top, left bottom, from(#dbdbdb), to(#e2e2e2)); 239 | background-image: linear-gradient(to bottom, #dbdbdb 0%, #e2e2e2 100%); 240 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdbdbdb', endColorstr='#ffe2e2e2', GradientType=0); 241 | background-repeat: repeat-x; 242 | -webkit-box-shadow: inset 0 3px 9px rgba(0, 0, 0, .075); 243 | box-shadow: inset 0 3px 9px rgba(0, 0, 0, .075); 244 | } 245 | .navbar-brand, 246 | .navbar-nav > li > a { 247 | text-shadow: 0 1px 0 rgba(255, 255, 255, .25); 248 | } 249 | .navbar-inverse { 250 | background-image: -webkit-linear-gradient(top, #3c3c3c 0%, #222 100%); 251 | background-image: -o-linear-gradient(top, #3c3c3c 0%, #222 100%); 252 | background-image: -webkit-gradient(linear, left top, left bottom, from(#3c3c3c), to(#222)); 253 | background-image: linear-gradient(to bottom, #3c3c3c 0%, #222 100%); 254 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff3c3c3c', endColorstr='#ff222222', GradientType=0); 255 | filter: progid:DXImageTransform.Microsoft.gradient(enabled = false); 256 | background-repeat: repeat-x; 257 | } 258 | .navbar-inverse .navbar-nav > .open > a, 259 | .navbar-inverse .navbar-nav > .active > a { 260 | background-image: -webkit-linear-gradient(top, #080808 0%, #0f0f0f 100%); 261 | background-image: -o-linear-gradient(top, #080808 0%, #0f0f0f 100%); 262 | background-image: -webkit-gradient(linear, left top, left bottom, from(#080808), to(#0f0f0f)); 263 | background-image: linear-gradient(to bottom, #080808 0%, #0f0f0f 100%); 264 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff080808', endColorstr='#ff0f0f0f', GradientType=0); 265 | background-repeat: repeat-x; 266 | -webkit-box-shadow: inset 0 3px 9px rgba(0, 0, 0, .25); 267 | box-shadow: inset 0 3px 9px rgba(0, 0, 0, .25); 268 | } 269 | .navbar-inverse .navbar-brand, 270 | .navbar-inverse .navbar-nav > li > a { 271 | text-shadow: 0 -1px 0 rgba(0, 0, 0, .25); 272 | } 273 | .navbar-static-top, 274 | .navbar-fixed-top, 275 | .navbar-fixed-bottom { 276 | border-radius: 0; 277 | } 278 | @media (max-width: 767px) { 279 | .navbar .navbar-nav .open .dropdown-menu > .active > a, 280 | .navbar .navbar-nav .open .dropdown-menu > .active > a:hover, 281 | .navbar .navbar-nav .open .dropdown-menu > .active > a:focus { 282 | color: #fff; 283 | background-image: -webkit-linear-gradient(top, #337ab7 0%, #2e6da4 100%); 284 | background-image: -o-linear-gradient(top, #337ab7 0%, #2e6da4 100%); 285 | background-image: -webkit-gradient(linear, left top, left bottom, from(#337ab7), to(#2e6da4)); 286 | background-image: linear-gradient(to bottom, #337ab7 0%, #2e6da4 100%); 287 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff2e6da4', GradientType=0); 288 | background-repeat: repeat-x; 289 | } 290 | } 291 | .alert { 292 | text-shadow: 0 1px 0 rgba(255, 255, 255, .2); 293 | -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, .25), 0 1px 2px rgba(0, 0, 0, .05); 294 | box-shadow: inset 0 1px 0 rgba(255, 255, 255, .25), 0 1px 2px rgba(0, 0, 0, .05); 295 | } 296 | .alert-success { 297 | background-image: -webkit-linear-gradient(top, #dff0d8 0%, #c8e5bc 100%); 298 | background-image: -o-linear-gradient(top, #dff0d8 0%, #c8e5bc 100%); 299 | background-image: -webkit-gradient(linear, left top, left bottom, from(#dff0d8), to(#c8e5bc)); 300 | background-image: linear-gradient(to bottom, #dff0d8 0%, #c8e5bc 100%); 301 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdff0d8', endColorstr='#ffc8e5bc', GradientType=0); 302 | background-repeat: repeat-x; 303 | border-color: #b2dba1; 304 | } 305 | .alert-info { 306 | background-image: -webkit-linear-gradient(top, #d9edf7 0%, #b9def0 100%); 307 | background-image: -o-linear-gradient(top, #d9edf7 0%, #b9def0 100%); 308 | background-image: -webkit-gradient(linear, left top, left bottom, from(#d9edf7), to(#b9def0)); 309 | background-image: linear-gradient(to bottom, #d9edf7 0%, #b9def0 100%); 310 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9edf7', endColorstr='#ffb9def0', GradientType=0); 311 | background-repeat: repeat-x; 312 | border-color: #9acfea; 313 | } 314 | .alert-warning { 315 | background-image: -webkit-linear-gradient(top, #fcf8e3 0%, #f8efc0 100%); 316 | background-image: -o-linear-gradient(top, #fcf8e3 0%, #f8efc0 100%); 317 | background-image: -webkit-gradient(linear, left top, left bottom, from(#fcf8e3), to(#f8efc0)); 318 | background-image: linear-gradient(to bottom, #fcf8e3 0%, #f8efc0 100%); 319 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffcf8e3', endColorstr='#fff8efc0', GradientType=0); 320 | background-repeat: repeat-x; 321 | border-color: #f5e79e; 322 | } 323 | .alert-danger { 324 | background-image: -webkit-linear-gradient(top, #f2dede 0%, #e7c3c3 100%); 325 | background-image: -o-linear-gradient(top, #f2dede 0%, #e7c3c3 100%); 326 | background-image: -webkit-gradient(linear, left top, left bottom, from(#f2dede), to(#e7c3c3)); 327 | background-image: linear-gradient(to bottom, #f2dede 0%, #e7c3c3 100%); 328 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff2dede', endColorstr='#ffe7c3c3', GradientType=0); 329 | background-repeat: repeat-x; 330 | border-color: #dca7a7; 331 | } 332 | .progress { 333 | background-image: -webkit-linear-gradient(top, #ebebeb 0%, #f5f5f5 100%); 334 | background-image: -o-linear-gradient(top, #ebebeb 0%, #f5f5f5 100%); 335 | background-image: -webkit-gradient(linear, left top, left bottom, from(#ebebeb), to(#f5f5f5)); 336 | background-image: linear-gradient(to bottom, #ebebeb 0%, #f5f5f5 100%); 337 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffebebeb', endColorstr='#fff5f5f5', GradientType=0); 338 | background-repeat: repeat-x; 339 | } 340 | .progress-bar { 341 | background-image: -webkit-linear-gradient(top, #337ab7 0%, #286090 100%); 342 | background-image: -o-linear-gradient(top, #337ab7 0%, #286090 100%); 343 | background-image: -webkit-gradient(linear, left top, left bottom, from(#337ab7), to(#286090)); 344 | background-image: linear-gradient(to bottom, #337ab7 0%, #286090 100%); 345 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff286090', GradientType=0); 346 | background-repeat: repeat-x; 347 | } 348 | .progress-bar-success { 349 | background-image: -webkit-linear-gradient(top, #5cb85c 0%, #449d44 100%); 350 | background-image: -o-linear-gradient(top, #5cb85c 0%, #449d44 100%); 351 | background-image: -webkit-gradient(linear, left top, left bottom, from(#5cb85c), to(#449d44)); 352 | background-image: linear-gradient(to bottom, #5cb85c 0%, #449d44 100%); 353 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5cb85c', endColorstr='#ff449d44', GradientType=0); 354 | background-repeat: repeat-x; 355 | } 356 | .progress-bar-info { 357 | background-image: -webkit-linear-gradient(top, #5bc0de 0%, #31b0d5 100%); 358 | background-image: -o-linear-gradient(top, #5bc0de 0%, #31b0d5 100%); 359 | background-image: -webkit-gradient(linear, left top, left bottom, from(#5bc0de), to(#31b0d5)); 360 | background-image: linear-gradient(to bottom, #5bc0de 0%, #31b0d5 100%); 361 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5bc0de', endColorstr='#ff31b0d5', GradientType=0); 362 | background-repeat: repeat-x; 363 | } 364 | .progress-bar-warning { 365 | background-image: -webkit-linear-gradient(top, #f0ad4e 0%, #ec971f 100%); 366 | background-image: -o-linear-gradient(top, #f0ad4e 0%, #ec971f 100%); 367 | background-image: -webkit-gradient(linear, left top, left bottom, from(#f0ad4e), to(#ec971f)); 368 | background-image: linear-gradient(to bottom, #f0ad4e 0%, #ec971f 100%); 369 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff0ad4e', endColorstr='#ffec971f', GradientType=0); 370 | background-repeat: repeat-x; 371 | } 372 | .progress-bar-danger { 373 | background-image: -webkit-linear-gradient(top, #d9534f 0%, #c9302c 100%); 374 | background-image: -o-linear-gradient(top, #d9534f 0%, #c9302c 100%); 375 | background-image: -webkit-gradient(linear, left top, left bottom, from(#d9534f), to(#c9302c)); 376 | background-image: linear-gradient(to bottom, #d9534f 0%, #c9302c 100%); 377 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9534f', endColorstr='#ffc9302c', GradientType=0); 378 | background-repeat: repeat-x; 379 | } 380 | .progress-bar-striped { 381 | background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); 382 | background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); 383 | background-image: linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); 384 | } 385 | .list-group { 386 | border-radius: 4px; 387 | -webkit-box-shadow: 0 1px 2px rgba(0, 0, 0, .075); 388 | box-shadow: 0 1px 2px rgba(0, 0, 0, .075); 389 | } 390 | .list-group-item.active, 391 | .list-group-item.active:hover, 392 | .list-group-item.active:focus { 393 | text-shadow: 0 -1px 0 #286090; 394 | background-image: -webkit-linear-gradient(top, #337ab7 0%, #2b669a 100%); 395 | background-image: -o-linear-gradient(top, #337ab7 0%, #2b669a 100%); 396 | background-image: -webkit-gradient(linear, left top, left bottom, from(#337ab7), to(#2b669a)); 397 | background-image: linear-gradient(to bottom, #337ab7 0%, #2b669a 100%); 398 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff2b669a', GradientType=0); 399 | background-repeat: repeat-x; 400 | border-color: #2b669a; 401 | } 402 | .list-group-item.active .badge, 403 | .list-group-item.active:hover .badge, 404 | .list-group-item.active:focus .badge { 405 | text-shadow: none; 406 | } 407 | .panel { 408 | -webkit-box-shadow: 0 1px 2px rgba(0, 0, 0, .05); 409 | box-shadow: 0 1px 2px rgba(0, 0, 0, .05); 410 | } 411 | .panel-default > .panel-heading { 412 | background-image: -webkit-linear-gradient(top, #f5f5f5 0%, #e8e8e8 100%); 413 | background-image: -o-linear-gradient(top, #f5f5f5 0%, #e8e8e8 100%); 414 | background-image: -webkit-gradient(linear, left top, left bottom, from(#f5f5f5), to(#e8e8e8)); 415 | background-image: linear-gradient(to bottom, #f5f5f5 0%, #e8e8e8 100%); 416 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff5f5f5', endColorstr='#ffe8e8e8', GradientType=0); 417 | background-repeat: repeat-x; 418 | } 419 | .panel-primary > .panel-heading { 420 | background-image: -webkit-linear-gradient(top, #337ab7 0%, #2e6da4 100%); 421 | background-image: -o-linear-gradient(top, #337ab7 0%, #2e6da4 100%); 422 | background-image: -webkit-gradient(linear, left top, left bottom, from(#337ab7), to(#2e6da4)); 423 | background-image: linear-gradient(to bottom, #337ab7 0%, #2e6da4 100%); 424 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff2e6da4', GradientType=0); 425 | background-repeat: repeat-x; 426 | } 427 | .panel-success > .panel-heading { 428 | background-image: -webkit-linear-gradient(top, #dff0d8 0%, #d0e9c6 100%); 429 | background-image: -o-linear-gradient(top, #dff0d8 0%, #d0e9c6 100%); 430 | background-image: -webkit-gradient(linear, left top, left bottom, from(#dff0d8), to(#d0e9c6)); 431 | background-image: linear-gradient(to bottom, #dff0d8 0%, #d0e9c6 100%); 432 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdff0d8', endColorstr='#ffd0e9c6', GradientType=0); 433 | background-repeat: repeat-x; 434 | } 435 | .panel-info > .panel-heading { 436 | background-image: -webkit-linear-gradient(top, #d9edf7 0%, #c4e3f3 100%); 437 | background-image: -o-linear-gradient(top, #d9edf7 0%, #c4e3f3 100%); 438 | background-image: -webkit-gradient(linear, left top, left bottom, from(#d9edf7), to(#c4e3f3)); 439 | background-image: linear-gradient(to bottom, #d9edf7 0%, #c4e3f3 100%); 440 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9edf7', endColorstr='#ffc4e3f3', GradientType=0); 441 | background-repeat: repeat-x; 442 | } 443 | .panel-warning > .panel-heading { 444 | background-image: -webkit-linear-gradient(top, #fcf8e3 0%, #faf2cc 100%); 445 | background-image: -o-linear-gradient(top, #fcf8e3 0%, #faf2cc 100%); 446 | background-image: -webkit-gradient(linear, left top, left bottom, from(#fcf8e3), to(#faf2cc)); 447 | background-image: linear-gradient(to bottom, #fcf8e3 0%, #faf2cc 100%); 448 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffcf8e3', endColorstr='#fffaf2cc', GradientType=0); 449 | background-repeat: repeat-x; 450 | } 451 | .panel-danger > .panel-heading { 452 | background-image: -webkit-linear-gradient(top, #f2dede 0%, #ebcccc 100%); 453 | background-image: -o-linear-gradient(top, #f2dede 0%, #ebcccc 100%); 454 | background-image: -webkit-gradient(linear, left top, left bottom, from(#f2dede), to(#ebcccc)); 455 | background-image: linear-gradient(to bottom, #f2dede 0%, #ebcccc 100%); 456 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff2dede', endColorstr='#ffebcccc', GradientType=0); 457 | background-repeat: repeat-x; 458 | } 459 | .well { 460 | background-image: -webkit-linear-gradient(top, #e8e8e8 0%, #f5f5f5 100%); 461 | background-image: -o-linear-gradient(top, #e8e8e8 0%, #f5f5f5 100%); 462 | background-image: -webkit-gradient(linear, left top, left bottom, from(#e8e8e8), to(#f5f5f5)); 463 | background-image: linear-gradient(to bottom, #e8e8e8 0%, #f5f5f5 100%); 464 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffe8e8e8', endColorstr='#fff5f5f5', GradientType=0); 465 | background-repeat: repeat-x; 466 | border-color: #dcdcdc; 467 | -webkit-box-shadow: inset 0 1px 3px rgba(0, 0, 0, .05), 0 1px 0 rgba(255, 255, 255, .1); 468 | box-shadow: inset 0 1px 3px rgba(0, 0, 0, .05), 0 1px 0 rgba(255, 255, 255, .1); 469 | } 470 | /*# sourceMappingURL=bootstrap-theme.css.map */ 471 | -------------------------------------------------------------------------------- /order-book/front-end/js/bootstrap.min.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * Bootstrap v3.3.1 (http://getbootstrap.com) 3 | * Copyright 2011-2014 Twitter, Inc. 4 | * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) 5 | */ 6 | if("undefined"==typeof jQuery)throw new Error("Bootstrap's JavaScript requires jQuery");+function(a){var b=a.fn.jquery.split(" ")[0].split(".");if(b[0]<2&&b[1]<9||1==b[0]&&9==b[1]&&b[2]<1)throw new Error("Bootstrap's JavaScript requires jQuery version 1.9.1 or higher")}(jQuery),+function(a){"use strict";function b(){var a=document.createElement("bootstrap"),b={WebkitTransition:"webkitTransitionEnd",MozTransition:"transitionend",OTransition:"oTransitionEnd otransitionend",transition:"transitionend"};for(var c in b)if(void 0!==a.style[c])return{end:b[c]};return!1}a.fn.emulateTransitionEnd=function(b){var c=!1,d=this;a(this).one("bsTransitionEnd",function(){c=!0});var e=function(){c||a(d).trigger(a.support.transition.end)};return setTimeout(e,b),this},a(function(){a.support.transition=b(),a.support.transition&&(a.event.special.bsTransitionEnd={bindType:a.support.transition.end,delegateType:a.support.transition.end,handle:function(b){return a(b.target).is(this)?b.handleObj.handler.apply(this,arguments):void 0}})})}(jQuery),+function(a){"use strict";function b(b){return this.each(function(){var c=a(this),e=c.data("bs.alert");e||c.data("bs.alert",e=new d(this)),"string"==typeof b&&e[b].call(c)})}var c='[data-dismiss="alert"]',d=function(b){a(b).on("click",c,this.close)};d.VERSION="3.3.1",d.TRANSITION_DURATION=150,d.prototype.close=function(b){function c(){g.detach().trigger("closed.bs.alert").remove()}var e=a(this),f=e.attr("data-target");f||(f=e.attr("href"),f=f&&f.replace(/.*(?=#[^\s]*$)/,""));var g=a(f);b&&b.preventDefault(),g.length||(g=e.closest(".alert")),g.trigger(b=a.Event("close.bs.alert")),b.isDefaultPrevented()||(g.removeClass("in"),a.support.transition&&g.hasClass("fade")?g.one("bsTransitionEnd",c).emulateTransitionEnd(d.TRANSITION_DURATION):c())};var e=a.fn.alert;a.fn.alert=b,a.fn.alert.Constructor=d,a.fn.alert.noConflict=function(){return a.fn.alert=e,this},a(document).on("click.bs.alert.data-api",c,d.prototype.close)}(jQuery),+function(a){"use strict";function b(b){return this.each(function(){var d=a(this),e=d.data("bs.button"),f="object"==typeof b&&b;e||d.data("bs.button",e=new c(this,f)),"toggle"==b?e.toggle():b&&e.setState(b)})}var c=function(b,d){this.$element=a(b),this.options=a.extend({},c.DEFAULTS,d),this.isLoading=!1};c.VERSION="3.3.1",c.DEFAULTS={loadingText:"loading..."},c.prototype.setState=function(b){var c="disabled",d=this.$element,e=d.is("input")?"val":"html",f=d.data();b+="Text",null==f.resetText&&d.data("resetText",d[e]()),setTimeout(a.proxy(function(){d[e](null==f[b]?this.options[b]:f[b]),"loadingText"==b?(this.isLoading=!0,d.addClass(c).attr(c,c)):this.isLoading&&(this.isLoading=!1,d.removeClass(c).removeAttr(c))},this),0)},c.prototype.toggle=function(){var a=!0,b=this.$element.closest('[data-toggle="buttons"]');if(b.length){var c=this.$element.find("input");"radio"==c.prop("type")&&(c.prop("checked")&&this.$element.hasClass("active")?a=!1:b.find(".active").removeClass("active")),a&&c.prop("checked",!this.$element.hasClass("active")).trigger("change")}else this.$element.attr("aria-pressed",!this.$element.hasClass("active"));a&&this.$element.toggleClass("active")};var d=a.fn.button;a.fn.button=b,a.fn.button.Constructor=c,a.fn.button.noConflict=function(){return a.fn.button=d,this},a(document).on("click.bs.button.data-api",'[data-toggle^="button"]',function(c){var d=a(c.target);d.hasClass("btn")||(d=d.closest(".btn")),b.call(d,"toggle"),c.preventDefault()}).on("focus.bs.button.data-api blur.bs.button.data-api",'[data-toggle^="button"]',function(b){a(b.target).closest(".btn").toggleClass("focus",/^focus(in)?$/.test(b.type))})}(jQuery),+function(a){"use strict";function b(b){return this.each(function(){var d=a(this),e=d.data("bs.carousel"),f=a.extend({},c.DEFAULTS,d.data(),"object"==typeof b&&b),g="string"==typeof b?b:f.slide;e||d.data("bs.carousel",e=new c(this,f)),"number"==typeof b?e.to(b):g?e[g]():f.interval&&e.pause().cycle()})}var c=function(b,c){this.$element=a(b),this.$indicators=this.$element.find(".carousel-indicators"),this.options=c,this.paused=this.sliding=this.interval=this.$active=this.$items=null,this.options.keyboard&&this.$element.on("keydown.bs.carousel",a.proxy(this.keydown,this)),"hover"==this.options.pause&&!("ontouchstart"in document.documentElement)&&this.$element.on("mouseenter.bs.carousel",a.proxy(this.pause,this)).on("mouseleave.bs.carousel",a.proxy(this.cycle,this))};c.VERSION="3.3.1",c.TRANSITION_DURATION=600,c.DEFAULTS={interval:5e3,pause:"hover",wrap:!0,keyboard:!0},c.prototype.keydown=function(a){if(!/input|textarea/i.test(a.target.tagName)){switch(a.which){case 37:this.prev();break;case 39:this.next();break;default:return}a.preventDefault()}},c.prototype.cycle=function(b){return b||(this.paused=!1),this.interval&&clearInterval(this.interval),this.options.interval&&!this.paused&&(this.interval=setInterval(a.proxy(this.next,this),this.options.interval)),this},c.prototype.getItemIndex=function(a){return this.$items=a.parent().children(".item"),this.$items.index(a||this.$active)},c.prototype.getItemForDirection=function(a,b){var c="prev"==a?-1:1,d=this.getItemIndex(b),e=(d+c)%this.$items.length;return this.$items.eq(e)},c.prototype.to=function(a){var b=this,c=this.getItemIndex(this.$active=this.$element.find(".item.active"));return a>this.$items.length-1||0>a?void 0:this.sliding?this.$element.one("slid.bs.carousel",function(){b.to(a)}):c==a?this.pause().cycle():this.slide(a>c?"next":"prev",this.$items.eq(a))},c.prototype.pause=function(b){return b||(this.paused=!0),this.$element.find(".next, .prev").length&&a.support.transition&&(this.$element.trigger(a.support.transition.end),this.cycle(!0)),this.interval=clearInterval(this.interval),this},c.prototype.next=function(){return this.sliding?void 0:this.slide("next")},c.prototype.prev=function(){return this.sliding?void 0:this.slide("prev")},c.prototype.slide=function(b,d){var e=this.$element.find(".item.active"),f=d||this.getItemForDirection(b,e),g=this.interval,h="next"==b?"left":"right",i="next"==b?"first":"last",j=this;if(!f.length){if(!this.options.wrap)return;f=this.$element.find(".item")[i]()}if(f.hasClass("active"))return this.sliding=!1;var k=f[0],l=a.Event("slide.bs.carousel",{relatedTarget:k,direction:h});if(this.$element.trigger(l),!l.isDefaultPrevented()){if(this.sliding=!0,g&&this.pause(),this.$indicators.length){this.$indicators.find(".active").removeClass("active");var m=a(this.$indicators.children()[this.getItemIndex(f)]);m&&m.addClass("active")}var n=a.Event("slid.bs.carousel",{relatedTarget:k,direction:h});return a.support.transition&&this.$element.hasClass("slide")?(f.addClass(b),f[0].offsetWidth,e.addClass(h),f.addClass(h),e.one("bsTransitionEnd",function(){f.removeClass([b,h].join(" ")).addClass("active"),e.removeClass(["active",h].join(" ")),j.sliding=!1,setTimeout(function(){j.$element.trigger(n)},0)}).emulateTransitionEnd(c.TRANSITION_DURATION)):(e.removeClass("active"),f.addClass("active"),this.sliding=!1,this.$element.trigger(n)),g&&this.cycle(),this}};var d=a.fn.carousel;a.fn.carousel=b,a.fn.carousel.Constructor=c,a.fn.carousel.noConflict=function(){return a.fn.carousel=d,this};var e=function(c){var d,e=a(this),f=a(e.attr("data-target")||(d=e.attr("href"))&&d.replace(/.*(?=#[^\s]+$)/,""));if(f.hasClass("carousel")){var g=a.extend({},f.data(),e.data()),h=e.attr("data-slide-to");h&&(g.interval=!1),b.call(f,g),h&&f.data("bs.carousel").to(h),c.preventDefault()}};a(document).on("click.bs.carousel.data-api","[data-slide]",e).on("click.bs.carousel.data-api","[data-slide-to]",e),a(window).on("load",function(){a('[data-ride="carousel"]').each(function(){var c=a(this);b.call(c,c.data())})})}(jQuery),+function(a){"use strict";function b(b){var c,d=b.attr("data-target")||(c=b.attr("href"))&&c.replace(/.*(?=#[^\s]+$)/,"");return a(d)}function c(b){return this.each(function(){var c=a(this),e=c.data("bs.collapse"),f=a.extend({},d.DEFAULTS,c.data(),"object"==typeof b&&b);!e&&f.toggle&&"show"==b&&(f.toggle=!1),e||c.data("bs.collapse",e=new d(this,f)),"string"==typeof b&&e[b]()})}var d=function(b,c){this.$element=a(b),this.options=a.extend({},d.DEFAULTS,c),this.$trigger=a(this.options.trigger).filter('[href="#'+b.id+'"], [data-target="#'+b.id+'"]'),this.transitioning=null,this.options.parent?this.$parent=this.getParent():this.addAriaAndCollapsedClass(this.$element,this.$trigger),this.options.toggle&&this.toggle()};d.VERSION="3.3.1",d.TRANSITION_DURATION=350,d.DEFAULTS={toggle:!0,trigger:'[data-toggle="collapse"]'},d.prototype.dimension=function(){var a=this.$element.hasClass("width");return a?"width":"height"},d.prototype.show=function(){if(!this.transitioning&&!this.$element.hasClass("in")){var b,e=this.$parent&&this.$parent.find("> .panel").children(".in, .collapsing");if(!(e&&e.length&&(b=e.data("bs.collapse"),b&&b.transitioning))){var f=a.Event("show.bs.collapse");if(this.$element.trigger(f),!f.isDefaultPrevented()){e&&e.length&&(c.call(e,"hide"),b||e.data("bs.collapse",null));var g=this.dimension();this.$element.removeClass("collapse").addClass("collapsing")[g](0).attr("aria-expanded",!0),this.$trigger.removeClass("collapsed").attr("aria-expanded",!0),this.transitioning=1;var h=function(){this.$element.removeClass("collapsing").addClass("collapse in")[g](""),this.transitioning=0,this.$element.trigger("shown.bs.collapse")};if(!a.support.transition)return h.call(this);var i=a.camelCase(["scroll",g].join("-"));this.$element.one("bsTransitionEnd",a.proxy(h,this)).emulateTransitionEnd(d.TRANSITION_DURATION)[g](this.$element[0][i])}}}},d.prototype.hide=function(){if(!this.transitioning&&this.$element.hasClass("in")){var b=a.Event("hide.bs.collapse");if(this.$element.trigger(b),!b.isDefaultPrevented()){var c=this.dimension();this.$element[c](this.$element[c]())[0].offsetHeight,this.$element.addClass("collapsing").removeClass("collapse in").attr("aria-expanded",!1),this.$trigger.addClass("collapsed").attr("aria-expanded",!1),this.transitioning=1;var e=function(){this.transitioning=0,this.$element.removeClass("collapsing").addClass("collapse").trigger("hidden.bs.collapse")};return a.support.transition?void this.$element[c](0).one("bsTransitionEnd",a.proxy(e,this)).emulateTransitionEnd(d.TRANSITION_DURATION):e.call(this)}}},d.prototype.toggle=function(){this[this.$element.hasClass("in")?"hide":"show"]()},d.prototype.getParent=function(){return a(this.options.parent).find('[data-toggle="collapse"][data-parent="'+this.options.parent+'"]').each(a.proxy(function(c,d){var e=a(d);this.addAriaAndCollapsedClass(b(e),e)},this)).end()},d.prototype.addAriaAndCollapsedClass=function(a,b){var c=a.hasClass("in");a.attr("aria-expanded",c),b.toggleClass("collapsed",!c).attr("aria-expanded",c)};var e=a.fn.collapse;a.fn.collapse=c,a.fn.collapse.Constructor=d,a.fn.collapse.noConflict=function(){return a.fn.collapse=e,this},a(document).on("click.bs.collapse.data-api",'[data-toggle="collapse"]',function(d){var e=a(this);e.attr("data-target")||d.preventDefault();var f=b(e),g=f.data("bs.collapse"),h=g?"toggle":a.extend({},e.data(),{trigger:this});c.call(f,h)})}(jQuery),+function(a){"use strict";function b(b){b&&3===b.which||(a(e).remove(),a(f).each(function(){var d=a(this),e=c(d),f={relatedTarget:this};e.hasClass("open")&&(e.trigger(b=a.Event("hide.bs.dropdown",f)),b.isDefaultPrevented()||(d.attr("aria-expanded","false"),e.removeClass("open").trigger("hidden.bs.dropdown",f)))}))}function c(b){var c=b.attr("data-target");c||(c=b.attr("href"),c=c&&/#[A-Za-z]/.test(c)&&c.replace(/.*(?=#[^\s]*$)/,""));var d=c&&a(c);return d&&d.length?d:b.parent()}function d(b){return this.each(function(){var c=a(this),d=c.data("bs.dropdown");d||c.data("bs.dropdown",d=new g(this)),"string"==typeof b&&d[b].call(c)})}var e=".dropdown-backdrop",f='[data-toggle="dropdown"]',g=function(b){a(b).on("click.bs.dropdown",this.toggle)};g.VERSION="3.3.1",g.prototype.toggle=function(d){var e=a(this);if(!e.is(".disabled, :disabled")){var f=c(e),g=f.hasClass("open");if(b(),!g){"ontouchstart"in document.documentElement&&!f.closest(".navbar-nav").length&&a('