Merge pull request #2 from bitpay/insight-bitcore-api
Insight bitcore api -- This commit convert insight in an API (without front-end)
This commit is contained in:
commit
492abec636
11
.ctags
11
.ctags
|
@ -1,11 +0,0 @@
|
|||
--extra=+f
|
||||
--exclude=*jquery*
|
||||
--exclude=node_modules/a*
|
||||
--exclude=node_modules/[c-z]*
|
||||
--exclude=*grunt*
|
||||
--exclude=*bower*
|
||||
--exclude=.swp
|
||||
--exclude=public
|
||||
--links=yes
|
||||
--totals=yes
|
||||
|
|
@ -1,21 +0,0 @@
|
|||
# EditorConfig helps developers define and maintain consistent
|
||||
# coding styles between different editors and IDEs
|
||||
# editorconfig.org
|
||||
|
||||
root = true
|
||||
|
||||
|
||||
[*]
|
||||
|
||||
# Change these settings to your own preference
|
||||
indent_style = space
|
||||
indent_size = 2
|
||||
|
||||
# We recommend you to keep these unchanged
|
||||
end_of_line = lf
|
||||
charset = utf-8
|
||||
trim_trailing_whitespace = true
|
||||
insert_final_newline = true
|
||||
|
||||
[*.md]
|
||||
trim_trailing_whitespace = false
|
|
@ -27,7 +27,6 @@ npm-debug.log
|
|||
.nodemonignore
|
||||
|
||||
.DS_Store
|
||||
public/lib/*
|
||||
db/txs/*
|
||||
db/txs
|
||||
db/testnet/txs/*
|
||||
|
@ -37,10 +36,4 @@ db/blocks
|
|||
db/testnet/blocks/*
|
||||
db/testnet/blocks
|
||||
|
||||
public/js/angularjs-all.js
|
||||
public/js/main.js
|
||||
public/js/vendors.js
|
||||
|
||||
public/css/main.css
|
||||
|
||||
README.html
|
||||
|
|
91
Gruntfile.js
91
Gruntfile.js
|
@ -5,9 +5,6 @@ module.exports = function(grunt) {
|
|||
//Load NPM tasks
|
||||
grunt.loadNpmTasks('grunt-contrib-watch');
|
||||
grunt.loadNpmTasks('grunt-contrib-jshint');
|
||||
grunt.loadNpmTasks('grunt-contrib-uglify');
|
||||
grunt.loadNpmTasks('grunt-contrib-concat');
|
||||
grunt.loadNpmTasks('grunt-css');
|
||||
grunt.loadNpmTasks('grunt-mocha-test');
|
||||
grunt.loadNpmTasks('grunt-nodemon');
|
||||
grunt.loadNpmTasks('grunt-concurrent');
|
||||
|
@ -22,37 +19,12 @@ module.exports = function(grunt) {
|
|||
files: ['README.md'],
|
||||
tasks: ['markdown']
|
||||
},
|
||||
jade: {
|
||||
files: ['app/views/**'],
|
||||
options: {
|
||||
livereload: true,
|
||||
},
|
||||
},
|
||||
js: {
|
||||
files: ['Gruntfile.js', 'insight.js', 'app/**/*.js', 'public/js/**'],
|
||||
files: ['Gruntfile.js', 'insight.js', 'app/**/*.js'],
|
||||
tasks: ['jshint'],
|
||||
options: {
|
||||
livereload: true,
|
||||
},
|
||||
},
|
||||
assets: {
|
||||
files: ['public/src/**/*.js', 'public/**/*.css'],
|
||||
tasks: ['compile'],
|
||||
options: {
|
||||
livereload: true,
|
||||
},
|
||||
},
|
||||
html: {
|
||||
files: ['public/views/**'],
|
||||
options: {
|
||||
livereload: true,
|
||||
},
|
||||
},
|
||||
css: {
|
||||
files: ['public/css/**'],
|
||||
options: {
|
||||
livereload: true
|
||||
}
|
||||
},
|
||||
// we monitor only app/models/* because we have test for models only now
|
||||
// test: {
|
||||
|
@ -62,64 +34,12 @@ module.exports = function(grunt) {
|
|||
},
|
||||
jshint: {
|
||||
all: {
|
||||
src: ['Gruntfile.js', 'insight.js', 'app/**/*.js', 'public/src/js/**/*.js','lib/*.js'],
|
||||
src: ['Gruntfile.js', 'insight.js', 'app/**/*.js', 'lib/*.js', 'config/*.js'],
|
||||
options: {
|
||||
jshintrc: true
|
||||
}
|
||||
}
|
||||
},
|
||||
concat: {
|
||||
options: {
|
||||
process: function(src, filepath) {
|
||||
if (filepath.substr(filepath.length - 2) === 'js') {
|
||||
return '// Source: ' + filepath + '\n' +
|
||||
src.replace(/(^|\n)[ \t]*('use strict'|"use strict");?\s*/g, '$1');
|
||||
} else {
|
||||
return src;
|
||||
}
|
||||
}
|
||||
},
|
||||
vendors: {
|
||||
src: ['public/lib/momentjs/min/moment.min.js', 'public/lib/qrcode-generator/js/qrcode.js', 'public/lib/zeroclipboard/ZeroClipboard.min.js'],
|
||||
dest: 'public/js/vendors.js'
|
||||
},
|
||||
angular: {
|
||||
src: ['public/lib/angular/angular.min.js', 'public/lib/angular-resource/angular-resource.min.js', 'public/lib/angular-route/angular-route.min.js', 'public/lib/angular-qrcode/qrcode.js', 'public/lib/angular-animate/angular-animate.min.js', 'public/lib/angular-bootstrap/ui-bootstrap.min.js', 'public/lib/angular-bootstrap/ui-bootstrap-tpls.min.js', 'public/lib/angular-ui-utils/ui-utils.min.js', 'public/lib/ngprogress/build/ngProgress.min.js'],
|
||||
dest: 'public/js/angularjs-all.js'
|
||||
},
|
||||
main: {
|
||||
src: ['public/src/js/app.js', 'public/src/js/controllers/*.js', 'public/src/js/services/*.js', 'public/src/js/directives.js', 'public/src/js/filters.js', 'public/src/js/config.js', 'public/src/js/init.js'],
|
||||
dest: 'public/js/main.js'
|
||||
},
|
||||
css: {
|
||||
src: ['public/src/css/**/*.css'],
|
||||
dest: 'public/css/main.css'
|
||||
}
|
||||
},
|
||||
uglify: {
|
||||
options: {
|
||||
banner: '/*! <%= pkg.name %> <%= pkg.version %> */\n',
|
||||
mangle: false
|
||||
},
|
||||
vendors: {
|
||||
src: 'public/js/vendors.js',
|
||||
dest: 'public/js/vendors.min.js'
|
||||
},
|
||||
angular: {
|
||||
src: 'public/js/angularjs-all.js',
|
||||
dest: 'public/js/angularjs-all.min.js'
|
||||
},
|
||||
main: {
|
||||
src: 'public/js/main.js',
|
||||
dest: 'public/js/main.min.js'
|
||||
}
|
||||
},
|
||||
cssmin: {
|
||||
css: {
|
||||
src: 'public/css/main.css',
|
||||
dest: 'public/css/main.min.css'
|
||||
}
|
||||
},
|
||||
mochaTest: {
|
||||
options: {
|
||||
reporter: 'spec',
|
||||
|
@ -131,7 +51,7 @@ module.exports = function(grunt) {
|
|||
script: 'insight.js',
|
||||
options: {
|
||||
args: [],
|
||||
ignore: ['public/**/*.html','public/**/*.css', 'public/**/*.js', 'test/**/*','util/**/*', ,'dev-util/**/*'],
|
||||
ignore: ['test/**/*', 'util/**/*', 'dev-util/**/*'],
|
||||
// nodeArgs: ['--debug'],
|
||||
delayTime: 1,
|
||||
env: {
|
||||
|
@ -170,10 +90,7 @@ module.exports = function(grunt) {
|
|||
grunt.option('force', true);
|
||||
|
||||
//Default task(s).
|
||||
grunt.registerTask('default', ['jshint', 'compile', 'concurrent']);
|
||||
|
||||
//Compile task (concat + minify)
|
||||
grunt.registerTask('compile', ['concat', 'uglify', 'cssmin']);
|
||||
grunt.registerTask('default', ['jshint', 'concurrent']);
|
||||
|
||||
//Test task.
|
||||
grunt.registerTask('test', ['env:test', 'mochaTest']);
|
||||
|
|
29
README.md
29
README.md
|
@ -1,8 +1,7 @@
|
|||
# *insight*
|
||||
# *insight API*
|
||||
|
||||
*insight* is an open-source bitcoin blockchain explorer with complete REST
|
||||
and websocket APIs. Insight runs in NodeJS, uses AngularJS for the
|
||||
front-end and LevelDB for storage.
|
||||
and websocket APIs. Insight runs in NodeJS and use LevelDB for storage.
|
||||
|
||||
Check some screenshots and more details at [insight's project homepage](http://insight.bitcore.io).
|
||||
|
||||
|
@ -16,7 +15,7 @@ thru the RPC API, Peer-to-peer protocol and will even read its raw .dat files fo
|
|||
|
||||
Configure bitcoind to listen to RPC calls and set `txindex` to true.
|
||||
The easiest way to do this is by copying `./etc/bitcoind/bitcoin.conf` to your
|
||||
bitcoin data directory (usually `"~/.bitcoin"` on Linux, `"%appdata%\Bitcoin\"` on Windows,
|
||||
bitcoin data directory (usually `"~/.bitcoin"` on Linux, `"%appdata%\Bitcoin\"` on Windows,
|
||||
or `"~/Library/Application Support/Bitcoin"` on Mac OS X).
|
||||
|
||||
bitcoind must be running and must have finished downloading the blockchain **before** running *insight*.
|
||||
|
@ -36,7 +35,7 @@ bitcoind must be running and must have finished downloading the blockchain **bef
|
|||
Install dependencies:
|
||||
|
||||
$ npm install
|
||||
|
||||
|
||||
Run the main application:
|
||||
|
||||
$ node insight.js
|
||||
|
@ -48,7 +47,7 @@ bitcoind must be running and must have finished downloading the blockchain **bef
|
|||
Please note that the app will need to sync its internal database
|
||||
with the blockchain state, which may take some time. You can check
|
||||
sync progress from within the web interface.
|
||||
|
||||
|
||||
|
||||
## Configuration
|
||||
|
||||
|
@ -74,11 +73,11 @@ In case the network is changed (testnet to livenet or vice versa) levelDB databa
|
|||
|
||||
The initial synchronization process scans the blockchain from the paired bitcoind server to update addresses and balances. *insight* needs one (and only one) trusted bitcoind node to run. This node must have finished downloading the blockchain befure running *insight*.
|
||||
|
||||
While *insight* is synchronizing the website can be accessed (the sync process is embedded in the webserver), but there may be missing data or incorrect balances for addresses. The 'sync' status is shown on the top-right of all pages.
|
||||
While *insight* is synchronizing the website can be accessed (the sync process is embedded in the webserver), but there may be missing data or incorrect balances for addresses. The 'sync' status is shown on the top-right of all pages.
|
||||
|
||||
The blockchain can be read from bitcoind's raw `.dat` files or RPC interface. Reading the information from the `.dat` files is much faster so it's the recommended (and default) alternative. `.dat` files are scanned in the default location for each platform. In case a non-standard location is used, it needs to be defined (see the Configuration section). The synchronization type being used can be seen at the [Status page](http://localhost:3001/status). As of February 2014, using `.dat` files the sync process takes 7 hrs. for livenet and 20 mins. for testnet.
|
||||
|
||||
While synchronizing the blockchain, *insight* listens for new blocks and transactions relayed by the bitcoind node. Those are also stored on *insight*'s database. In case *insight* is shutdown for a period of time, restarting it will trigger a partial (historic) synchronization of the blockchain. Depending on the size of that synchronization task, a reverse RPC or forward `.dat` syncing strategy will be used.
|
||||
While synchronizing the blockchain, *insight* listens for new blocks and transactions relayed by the bitcoind node. Those are also stored on *insight*'s database. In case *insight* is shutdown for a period of time, restarting it will trigger a partial (historic) synchronization of the blockchain. Depending on the size of that synchronization task, a reverse RPC or forward `.dat` syncing strategy will be used.
|
||||
|
||||
If bitcoind is shutdown, *insight* needs to be stopped and restarted once bitcoind is restarted.
|
||||
|
||||
|
@ -95,9 +94,9 @@ If bitcoind is shutdown, *insight* needs to be stopped and restarted once bitcoi
|
|||
|
||||
### DB storage requirement
|
||||
|
||||
To store the blockchain and address related information, *insight* uses LevelDB. Two DBs are created: txs and blocks. By default these are stored on
|
||||
```<insight root>/db```
|
||||
|
||||
To store the blockchain and address related information, *insight* uses LevelDB. Two DBs are created: txs and blocks. By default these are stored on
|
||||
```<insight root>/db```
|
||||
|
||||
this can be changed on config/config.js. As of February 2014, storing the livenet blockchain takes ~30GB of disk space (2GB for the testnet).
|
||||
|
||||
## Development
|
||||
|
@ -106,10 +105,6 @@ To run insight locally for development with grunt:
|
|||
|
||||
```$ NODE_ENV=development grunt```
|
||||
|
||||
To compile and minify the web application's assets:
|
||||
|
||||
```$ grunt compile```
|
||||
|
||||
To run the tests
|
||||
|
||||
```$ grunt test```
|
||||
|
@ -120,7 +115,9 @@ Contributions and suggestions are welcomed at [insight github repository](https:
|
|||
|
||||
## API
|
||||
|
||||
A REST API is provided at /api. The entry points are:
|
||||
By default, insight provides a REST API at /api, but this prefix is configurable from the var `apiPrefix` in the `config.js` file.
|
||||
|
||||
The end-points are:
|
||||
|
||||
|
||||
### Block
|
||||
|
|
|
@ -23,7 +23,7 @@ var getAddr = function(req, res, next) {
|
|||
exports.show = function(req, res, next) {
|
||||
var a = getAddr(req, res, next);
|
||||
|
||||
if (a)
|
||||
if (a)
|
||||
a.update(function(err) {
|
||||
if (err) {
|
||||
return common.handleErrors(err, res);
|
||||
|
@ -39,7 +39,7 @@ exports.show = function(req, res, next) {
|
|||
exports.utxo = function(req, res, next) {
|
||||
var a = getAddr(req, res, next);
|
||||
|
||||
if (a)
|
||||
if (a)
|
||||
a.getUtxo(function(err, utxo) {
|
||||
if (err)
|
||||
return common.handleErrors(err, res);
|
||||
|
|
|
@ -1,11 +1,17 @@
|
|||
'use strict';
|
||||
|
||||
var _getVersion = function() {
|
||||
var pjson = require('../../package.json');
|
||||
return pjson.version;
|
||||
};
|
||||
|
||||
exports.render = function(req, res) {
|
||||
res.render('index');
|
||||
var version = _getVersion();
|
||||
res.send('insight API v' + version);
|
||||
};
|
||||
|
||||
exports.version = function(req, res) {
|
||||
var pjson = require('../../package.json');
|
||||
res.json({version: pjson.version});
|
||||
var version = _getVersion();
|
||||
res.json({ version: version });
|
||||
};
|
||||
|
||||
|
|
|
@ -22,7 +22,7 @@ module.exports.broadcastTx = function(tx) {
|
|||
t = {
|
||||
txid: tx
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
else {
|
||||
t = {
|
||||
|
|
|
@ -47,7 +47,7 @@ var getTransaction = function(txid, cb) {
|
|||
if (err) console.log(err);
|
||||
|
||||
if (!tx || !tx.info) {
|
||||
console.log('[transactions.js.48]:: TXid %s not found in RPC. CHECK THIS.', txid);
|
||||
console.log('[transactions.js.48]:: TXid %s not found in RPC. CHECK THIS.', txid);
|
||||
return ({ txid: txid });
|
||||
}
|
||||
|
||||
|
|
|
@ -1,12 +0,0 @@
|
|||
extends layouts/default
|
||||
|
||||
block main
|
||||
h1 Oops something went wrong
|
||||
br
|
||||
span 404
|
||||
|
||||
block content
|
||||
#error-message-box
|
||||
#error-stack-trace
|
||||
pre
|
||||
code!= error
|
|
@ -1,12 +0,0 @@
|
|||
extends layouts/default
|
||||
|
||||
block main
|
||||
h1 Oops something went wrong
|
||||
br
|
||||
span 500
|
||||
|
||||
block content
|
||||
#error-message-box
|
||||
#error-stack-trace
|
||||
pre
|
||||
code!= error
|
|
@ -1,7 +0,0 @@
|
|||
#footer(data-ng-include="'/views/includes/footer.html'", role='navigation')
|
||||
|
||||
script(type='text/javascript', src='/socket.io/socket.io.js')
|
||||
script(type='text/javascript', src='/js/vendors.min.js')
|
||||
script(type='text/javascript', src='/js/angularjs-all.min.js')
|
||||
script(type='text/javascript', src='/js/main.min.js')
|
||||
|
|
@ -1,22 +0,0 @@
|
|||
head
|
||||
meta(charset='utf-8')
|
||||
meta(http-equiv='X-UA-Compatible', content='IE=edge,chrome=1')
|
||||
meta(name='viewport', content='width=device-width,initial-scale=1.0')
|
||||
meta(name="fragment", content="!")
|
||||
|
||||
title(data-ng-bind="$root.title + $root.titleDetail + ' | #{appName}'")= appName
|
||||
meta(http-equiv='Content-type', content='text/html;charset=UTF-8')
|
||||
meta(name="keywords", content="bitcoins, transactions, blocks, address, block chain, best block, mining difficulty, hash serialized")
|
||||
meta(name="description", content="Bitcoin Insight. View detailed information on all bitcoin transactions and block. {{ $root.title + $root.titleDetail }}")
|
||||
|
||||
link(rel='shortcut icon', href='/img/icons/favicon.ico', type='image/x-icon')
|
||||
|
||||
link(rel='stylesheet', href='//fonts.googleapis.com/css?family=Ubuntu:300,400,500,700,400italic')
|
||||
link(rel='stylesheet', href='/lib/bootstrap/dist/css/bootstrap.min.css')
|
||||
link(rel='stylesheet', href='/css/main.min.css')
|
||||
|
||||
if (config.keys.segmentio)
|
||||
script(type='text/javascript').
|
||||
window.analytics||(window.analytics=[]),window.analytics.methods=['identify','track','trackLink','trackForm','trackClick','trackSubmit','page','pageview','ab','alias','ready','group','on','once','off'],window.analytics.factory=function(t){return function(){var a=Array.prototype.slice.call(arguments);return a.unshift(t),window.analytics.push(a),window.analytics}};for(var i=0;i<window.analytics.methods.length;i++){var method=window.analytics.methods[i];window.analytics[method]=window.analytics.factory(method)}window.analytics.load=function(t){var a=document.createElement('script');a.type='text/javascript',a.async=!0,a.src=('https:'===document.location.protocol?'https://':'http://')+'d2dq2ahtl5zl1z.cloudfront.net/analytics.js/v1/'+t+'/analytics.min.js';var n=document.getElementsByTagName('script')[0];n.parentNode.insertBefore(a,n)},window.analytics.SNIPPET_VERSION='2.0.8',
|
||||
window.analytics.load('#{config.keys.segmentio}');
|
||||
window.analytics.page();
|
|
@ -1 +0,0 @@
|
|||
.navbar.navbar-default.navbar-fixed-top(data-ng-include="'/views/includes/header.html'", role='navigation')
|
|
@ -1,4 +0,0 @@
|
|||
extends layouts/default
|
||||
|
||||
block content
|
||||
section.container(data-ng-view)
|
|
@ -1,8 +0,0 @@
|
|||
doctype
|
||||
html(lang='en', data-ng-app='insight' data-ng-csp)
|
||||
include ../includes/head
|
||||
body
|
||||
#wrap
|
||||
include ../includes/navbar
|
||||
block content
|
||||
include ../includes/foot
|
18
bower.json
18
bower.json
|
@ -1,18 +0,0 @@
|
|||
{
|
||||
"name": "Insight",
|
||||
"version": "0.0.1",
|
||||
"dependencies": {
|
||||
"angular": "latest",
|
||||
"angular-resource": "latest",
|
||||
"angular-mocks": "latest",
|
||||
"angular-route": "latest",
|
||||
"bootstrap": "3.0.3",
|
||||
"angular-bootstrap": "0.9.0",
|
||||
"angular-ui-utils": "0.1.0",
|
||||
"angular-qrcode": "latest",
|
||||
"angular-animate": "latest",
|
||||
"momentjs": "~2.5.0",
|
||||
"zeroclipboard": "~1.3.0-beta.1",
|
||||
"ngprogress": "~1.0.4"
|
||||
}
|
||||
}
|
|
@ -17,9 +17,9 @@ if (process.env.INSIGHT_NETWORK === 'livenet') {
|
|||
}
|
||||
else {
|
||||
env = 'testnet';
|
||||
db = './db/testnet',
|
||||
port = '3001',
|
||||
b_port = '18332',
|
||||
db = './db/testnet';
|
||||
port = '3001';
|
||||
b_port = '18332';
|
||||
p2p_port = '18333';
|
||||
}
|
||||
|
||||
|
@ -35,6 +35,8 @@ switch(process.env.NODE_ENV) {
|
|||
break;
|
||||
}
|
||||
|
||||
var network = process.env.INSIGHT_NETWORK || 'testnet';
|
||||
|
||||
var dataDir = process.env.BITCOIND_DATADIR;
|
||||
var isWin = /^win/.test(process.platform);
|
||||
var isMac = /^darwin/.test(process.platform);
|
||||
|
@ -44,11 +46,12 @@ if (!dataDir) {
|
|||
if (isMac) dataDir = process.env.HOME + '/Library/Application Support/Bitcoin/';
|
||||
if (isLinux) dataDir = process.env.HOME + '/.bitcoin/';
|
||||
}
|
||||
dataDir += ((process.env.INSIGHT_NETWORK || 'testnet')==='testnet'?'testnet3':'');
|
||||
dataDir += network === 'testnet' ? 'testnet3' : '';
|
||||
|
||||
module.exports = {
|
||||
root: rootPath,
|
||||
appName: 'Insight ' + env,
|
||||
apiPrefix: '/api',
|
||||
port: port,
|
||||
leveldb: db,
|
||||
bitcoind: {
|
||||
|
@ -62,14 +65,14 @@ module.exports = {
|
|||
// DO NOT CHANGE THIS!
|
||||
disableAgent: true
|
||||
},
|
||||
network: process.env.INSIGHT_NETWORK || 'testnet',
|
||||
network: network,
|
||||
disableP2pSync: false,
|
||||
disableHistoricSync: false,
|
||||
poolMatchFile: './etc/minersPoolStrings.json',
|
||||
|
||||
// Time to refresh the currency rate. In minutes
|
||||
currencyRefresh: 10
|
||||
, keys: {
|
||||
currencyRefresh: 10,
|
||||
keys: {
|
||||
segmentio: process.env.INSIGHT_SEGMENTIO_KEY
|
||||
}
|
||||
};
|
||||
|
|
|
@ -4,46 +4,45 @@
|
|||
* Module dependencies.
|
||||
*/
|
||||
var express = require('express'),
|
||||
helpers = require('view-helpers'),
|
||||
config = require('./config');
|
||||
config = require('./config'),
|
||||
path = require('path');
|
||||
|
||||
module.exports = function(app, historicSync, peerSync) {
|
||||
|
||||
//custom middleware
|
||||
function setHistoric(req, res, next) {
|
||||
|
||||
//custom middleware
|
||||
var setHistoric = function(req, res, next) {
|
||||
req.historicSync = historicSync;
|
||||
next();
|
||||
}
|
||||
function setPeer(req, res, next) {
|
||||
};
|
||||
|
||||
var setPeer = function(req, res, next) {
|
||||
req.peerSync = peerSync;
|
||||
next();
|
||||
}
|
||||
};
|
||||
|
||||
app.set('showStackError', true);
|
||||
|
||||
//Set views path, template engine and default layout
|
||||
app.set('views', config.root + '/app/views');
|
||||
app.set('view engine', 'jade');
|
||||
|
||||
// Compress JSON outputs
|
||||
app.set('json spaces', 0);
|
||||
|
||||
//Enable jsonp
|
||||
app.enable('jsonp callback');
|
||||
|
||||
app.use('/api/sync', setHistoric);
|
||||
app.use('/api/peer', setPeer);
|
||||
app.use(config.apiPrefix + '/sync', setHistoric);
|
||||
app.use(config.apiPrefix + '/peer', setPeer);
|
||||
app.use(express.logger('dev'));
|
||||
app.use(express.json());
|
||||
app.use(express.urlencoded());
|
||||
app.use(express.methodOverride());
|
||||
app.use(express.compress());
|
||||
|
||||
// IMPORTANT: for html5mode, this line must to be before app.router
|
||||
app.use(express.static(config.root + '/public'));
|
||||
if (process.env.INSIGHT_PUBLIC_PATH) {
|
||||
var staticPath = path.normalize(config.rootPath + '/../../' + process.env.INSIGHT_PUBLIC_PATH);
|
||||
|
||||
//dynamic helpers
|
||||
app.use(helpers(config.appName));
|
||||
//IMPORTANT: for html5mode, this line must to be before app.router
|
||||
app.use(express.static(staticPath));
|
||||
}
|
||||
|
||||
// manual helpers
|
||||
app.use(function(req, res, next) {
|
||||
|
@ -63,14 +62,16 @@ module.exports = function(app, historicSync, peerSync) {
|
|||
console.error(err.stack);
|
||||
|
||||
//Error page
|
||||
res.status(500).render('500', {
|
||||
res.status(500).jsonp({
|
||||
status: 500,
|
||||
error: err.stack
|
||||
});
|
||||
});
|
||||
|
||||
//Assume 404 since no middleware responded
|
||||
app.use(function(req, res) {
|
||||
res.status(404).render('404', {
|
||||
res.status(404).jsonp({
|
||||
status: 404,
|
||||
url: req.originalUrl,
|
||||
error: 'Not found'
|
||||
});
|
||||
|
|
|
@ -1,42 +1,49 @@
|
|||
'use strict';
|
||||
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
var config = require('./config');
|
||||
|
||||
module.exports = function(app) {
|
||||
|
||||
var apiPrefix = config.apiPrefix;
|
||||
|
||||
//Block routes
|
||||
var blocks = require('../app/controllers/blocks');
|
||||
app.get('/api/blocks', blocks.list);
|
||||
app.get(apiPrefix + '/blocks', blocks.list);
|
||||
|
||||
|
||||
app.get('/api/block/:blockHash', blocks.show);
|
||||
app.get(apiPrefix + '/block/:blockHash', blocks.show);
|
||||
app.param('blockHash', blocks.block);
|
||||
|
||||
app.get('/api/block-index/:height', blocks.blockindex);
|
||||
app.get(apiPrefix + '/block-index/:height', blocks.blockindex);
|
||||
app.param('height', blocks.blockindex);
|
||||
|
||||
// Transaction routes
|
||||
var transactions = require('../app/controllers/transactions');
|
||||
app.get('/api/tx/:txid', transactions.show);
|
||||
app.get(apiPrefix + '/tx/:txid', transactions.show);
|
||||
app.param('txid', transactions.transaction);
|
||||
app.get('/api/txs', transactions.list);
|
||||
app.get(apiPrefix + '/txs', transactions.list);
|
||||
|
||||
// Address routes
|
||||
var addresses = require('../app/controllers/addresses');
|
||||
app.get('/api/addr/:addr', addresses.show);
|
||||
app.get('/api/addr/:addr/utxo', addresses.utxo);
|
||||
app.get(apiPrefix + '/addr/:addr', addresses.show);
|
||||
app.get(apiPrefix + '/addr/:addr/utxo', addresses.utxo);
|
||||
|
||||
// Status route
|
||||
var st = require('../app/controllers/status');
|
||||
app.get('/api/status', st.show);
|
||||
app.get(apiPrefix + '/status', st.show);
|
||||
|
||||
app.get('/api/sync', st.sync);
|
||||
app.get('/api/peer', st.peer);
|
||||
app.get(apiPrefix + '/sync', st.sync);
|
||||
app.get(apiPrefix + '/peer', st.peer);
|
||||
|
||||
// Currency
|
||||
var currency = require('../app/controllers/currency');
|
||||
app.get('/api/currency', currency.index);
|
||||
app.get(apiPrefix + '/currency', currency.index);
|
||||
|
||||
//Home route
|
||||
var index = require('../app/controllers/index');
|
||||
app.get('/api/version', index.version);
|
||||
app.get('*', index.render);
|
||||
app.get(apiPrefix + '/version', index.version);
|
||||
app.get('/', index.render);
|
||||
};
|
||||
|
|
|
@ -85,7 +85,7 @@ require('./app/controllers/socket.js').init(expressApp, ios);
|
|||
|
||||
//Start the app by listening on <port>
|
||||
server.listen(config.port, function(){
|
||||
console.log('Express server listening on port %d in %s mode', server.address().port, process.env.NODE_ENV);
|
||||
console.log('insight server listening on port %d in %s mode', server.address().port, process.env.NODE_ENV);
|
||||
});
|
||||
|
||||
//expose app
|
||||
|
|
|
@ -25,7 +25,6 @@ function spec(b) {
|
|||
var Rpc = b.rpc || require('./Rpc').class();
|
||||
var PoolMatch = b.poolMatch || require('./PoolMatch').class(config);
|
||||
|
||||
var buffertools = require('buffertools');
|
||||
var TransactionDb = require('./TransactionDb.js').class();
|
||||
|
||||
var BlockDb = function() {
|
||||
|
@ -176,9 +175,9 @@ function spec(b) {
|
|||
|
||||
if (a.isCoinBase) {
|
||||
var coinbaseHexBuffer = new Buffer(a.vin[0].coinbase, 'hex');
|
||||
var a = self.poolMatch.match(coinbaseHexBuffer);
|
||||
var aa = self.poolMatch.match(coinbaseHexBuffer);
|
||||
|
||||
return cb(a);
|
||||
return cb(aa);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
|
|
@ -29,7 +29,7 @@ function spec(b) {
|
|||
if (buffertools.indexOf(buffer, k) >= 0) {
|
||||
return self.strings[k];
|
||||
}
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
return PoolMatch;
|
||||
|
|
|
@ -19,21 +19,21 @@ function spec(b) {
|
|||
var b = new Buffer(info.hex,'hex');
|
||||
|
||||
// remove fields we dont need, to speed and adapt the information
|
||||
delete info['hex'];
|
||||
delete info.hex;
|
||||
|
||||
// Inputs => add index + coinBase flag
|
||||
var n =0;
|
||||
info.vin.forEach(function(i) {
|
||||
i.n = n++;
|
||||
if (i.coinbase) info.isCoinBase = true;
|
||||
if (i.scriptSig) delete i.scriptSig['hex'];
|
||||
if (i.scriptSig) delete i.scriptSig.hex;
|
||||
});
|
||||
|
||||
// Outputs => add total
|
||||
var valueOutSat = 0;
|
||||
info.vout.forEach( function(o) {
|
||||
valueOutSat += o.value * bitcoreUtil.COIN;
|
||||
delete o.scriptPubKey['hex'];
|
||||
delete o.scriptPubKey.hex;
|
||||
});
|
||||
info.valueOut = parseInt(valueOutSat) / bitcoreUtil.COIN;
|
||||
info.size = b.length;
|
||||
|
|
21
package.json
21
package.json
|
@ -1,12 +1,12 @@
|
|||
{
|
||||
"name": "insight-bitcore",
|
||||
"name": "insight-bitcore-api",
|
||||
"description": "An open-source bitcoin blockchain API. The Insight API provides you with a convenient, powerful and simple way to query and broadcast data on the bitcoin network and build your own services with it.",
|
||||
"version": "0.1.2",
|
||||
"author": {
|
||||
"name": "Ryan X Charles",
|
||||
"email": "ryan@bitpay.com"
|
||||
},
|
||||
"repository": "git://github.com/bitpay/insight.git",
|
||||
"repository": "git://github.com/bitpay/insight-api.git",
|
||||
"contributors": [
|
||||
{
|
||||
"name": "Matias Alejo Garcia",
|
||||
|
@ -30,9 +30,9 @@
|
|||
}
|
||||
],
|
||||
"bugs": {
|
||||
"url": "https://github.com/bitpay/insight/issues"
|
||||
"url": "https://github.com/bitpay/insight-api/issues"
|
||||
},
|
||||
"homepage": "https://github.com/bitpay/insight",
|
||||
"homepage": "https://github.com/bitpay/insight-api",
|
||||
"license": "MIT",
|
||||
"keywords": [
|
||||
"insight",
|
||||
|
@ -41,7 +41,9 @@
|
|||
"riddle",
|
||||
"mystification",
|
||||
"puzzle",
|
||||
"conundrum"
|
||||
"conundrum",
|
||||
"api",
|
||||
"bitcore"
|
||||
],
|
||||
"engines": {
|
||||
"node": "*"
|
||||
|
@ -59,11 +61,8 @@
|
|||
"commander": "*",
|
||||
"bignum": "*",
|
||||
"express": "~3.4.7",
|
||||
"jade": "~1.0.2",
|
||||
"lodash": "~2.4.1",
|
||||
"buffertools": "*",
|
||||
"should": "~2.1.1",
|
||||
"view-helpers": "latest",
|
||||
"socket.io": "~0.9.16",
|
||||
"moment": "~2.5.0",
|
||||
"sinon": "~1.7.3",
|
||||
|
@ -73,7 +72,6 @@
|
|||
"xmlhttprequest": "~1.6.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"bower": "~1.2.8",
|
||||
"grunt": "~0.4.2",
|
||||
"grunt-cli": "~0.1.11",
|
||||
"grunt-env": "~0.4.1",
|
||||
|
@ -82,10 +80,7 @@
|
|||
"grunt-concurrent": "~0.4.2",
|
||||
"grunt-nodemon": "~0.2.0",
|
||||
"grunt-mocha-test": "~0.8.1",
|
||||
"grunt-contrib-concat": "~0.3.0",
|
||||
"grunt-contrib-uglify": "~0.3.2",
|
||||
"should": "latest",
|
||||
"grunt-css": "~0.5.4",
|
||||
"should": "2.1.1",
|
||||
"grunt-markdown": "~0.5.0"
|
||||
}
|
||||
}
|
||||
|
|
File diff suppressed because one or more lines are too long
Binary file not shown.
Before Width: | Height: | Size: 14 KiB |
Binary file not shown.
Before Width: | Height: | Size: 148 B |
Binary file not shown.
Before Width: | Height: | Size: 1.1 KiB |
Binary file not shown.
Before Width: | Height: | Size: 12 KiB |
Binary file not shown.
Before Width: | Height: | Size: 1.8 KiB |
Binary file not shown.
Before Width: | Height: | Size: 3.2 KiB |
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
Binary file not shown.
|
@ -1,711 +0,0 @@
|
|||
/* Sticky footer styles
|
||||
-------------------------------------------------- */
|
||||
@charset "UTF-8";
|
||||
|
||||
html,
|
||||
body {
|
||||
color: #373D42;
|
||||
font-family: 'Ubuntu', sans-serif;
|
||||
height: 100%;
|
||||
/* The html and body elements cannot have any padding or margin. */
|
||||
}
|
||||
|
||||
h1, h2, h3, h4, h5, h6, .h1, .h2, .h3, .h4, .h5, .h6 {
|
||||
color: #373D42;
|
||||
font-family: 'Ubuntu', sans-serif;
|
||||
}
|
||||
|
||||
[ng\:cloak], [ng-cloak], [data-ng-cloak], [x-ng-cloak],
|
||||
.ng-cloak, .x-ng-cloak,
|
||||
.ng-hide {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
/* Styling for the ngProgress itself */
|
||||
#ngProgress {
|
||||
background-color: #6C9032 !important;
|
||||
box-shadow: none !important;
|
||||
color: #373D42 !important;
|
||||
height: 3px !important;
|
||||
margin: 0;
|
||||
opacity: 0;
|
||||
padding: 0;
|
||||
z-index: 99998;
|
||||
|
||||
/* Add CSS3 styles for transition smoothing */
|
||||
-webkit-transition: all 0.5s ease-in-out;
|
||||
-moz-transition: all 0.5s ease-in-out;
|
||||
-o-transition: all 0.5s ease-in-out;
|
||||
transition: all 0.5s ease-in-out;
|
||||
}
|
||||
|
||||
/* Styling for the ngProgress-container */
|
||||
#ngProgress-container {
|
||||
position: fixed;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
top: 63px;
|
||||
left: 0;
|
||||
right: 0;
|
||||
z-index: 99999;
|
||||
}
|
||||
|
||||
/* Wrapper for page content to push down footer */
|
||||
#wrap {
|
||||
min-height: 100%;
|
||||
height: auto;
|
||||
/* Negative indent footer by its height */
|
||||
margin: 0 auto -51px;
|
||||
/* Pad bottom by footer height */
|
||||
padding: 0 0 75px;
|
||||
}
|
||||
|
||||
.m10h { margin: 0 10px; }
|
||||
.m20h { margin: 0 20px; }
|
||||
.m5v { margin: 5px 0; }
|
||||
.m20v { margin: 20px 0; }
|
||||
.m10v { margin: 10px 0; }
|
||||
.m50v { margin: 50px 0; }
|
||||
.m10b { margin-bottom: 10px; }
|
||||
.m10l { margin-left: 10px; }
|
||||
|
||||
.vm { vertical-align: middle; }
|
||||
|
||||
.bgwhite {
|
||||
background-color: white;
|
||||
}
|
||||
|
||||
.btn-group .btn+.btn, .btn-group .btn+.btn-group, .btn-group .btn-group+.btn, .btn-group .btn-group+.btn-group {
|
||||
margin-left: 0;
|
||||
}
|
||||
|
||||
.table-hover>tbody>tr:hover>td, .table-hover>tbody>tr:hover>th {
|
||||
background-color: #F0F7E2;
|
||||
}
|
||||
|
||||
.navbar-default .navbar-toggle {
|
||||
border-color: #fff;
|
||||
margin-top: 15px;
|
||||
}
|
||||
|
||||
.navbar-default .navbar-toggle .icon-bar { background-color: #fff; }
|
||||
.navbar-default .navbar-toggle:hover, .navbar-default .navbar-toggle:focus {background-color: #373D42;}
|
||||
|
||||
.navbar-default {
|
||||
background-color: #8DC429;
|
||||
margin: 0;
|
||||
border: 0;
|
||||
}
|
||||
|
||||
.navbar-default .navbar-nav>li>a {
|
||||
color: #F4FBE8;
|
||||
font-family: 'Ubuntu', sans-serif;
|
||||
padding-left: 25px;
|
||||
padding-right: 25px;
|
||||
}
|
||||
|
||||
.navbar-default .navbar-nav>.active>a, .navbar-default .navbar-nav>.active>a:focus {
|
||||
background-color: #6C9032;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.navbar-default .navbar-nav>li>a:hover, .navbar-default .navbar-nav>.active>a:hover {
|
||||
background-color: #fff;
|
||||
}
|
||||
|
||||
.navbar-form .form-group {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.navbar-form {
|
||||
width: 35%;
|
||||
margin-top: 15px;
|
||||
}
|
||||
|
||||
@media (max-width: 991px) {
|
||||
.status {
|
||||
margin: 0;
|
||||
}
|
||||
.navbar-form {
|
||||
width: 15%;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 767px) {
|
||||
.navbar-form {
|
||||
width: auto;
|
||||
}
|
||||
}
|
||||
|
||||
.nav-tabs.nav-justified>li>a:hover {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.insight {
|
||||
font-family: 'Ubuntu', sans-serif;
|
||||
font-size: 34px;
|
||||
font-style: italic;
|
||||
font-weight: 700;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.navbar-default .navbar-brand:hover, .navbar-default .navbar-brand:focus {
|
||||
color: #fffffe;
|
||||
}
|
||||
|
||||
.navbar-default .navbar-brand {
|
||||
color: #FFFFFF;
|
||||
padding: 22px 15px;
|
||||
}
|
||||
|
||||
.navbar-form .form-control {
|
||||
background-color: #7CAD23;
|
||||
border-radius: 3px;
|
||||
-webkit-border-radius: 3px;
|
||||
-moz-border-radius: 3px;
|
||||
border: 0;
|
||||
-webkit-box-shadow: 1px 1px 0px 0px rgba(255,255,255,0.41), inset 1px 1px 3px 0px rgba(0,0,0,0.10);
|
||||
-moz-box-shadow: 1px 1px 0px 0px rgba(255,255,255,0.41), inset 1px 1px 3px 0px rgba(0,0,0,0.10);
|
||||
box-shadow: 1px 1px 0px 0px rgba(255,255,255,0.41), inset 1px 1px 3px 0px rgba(0,0,0,0.10);
|
||||
}
|
||||
|
||||
.navbar-nav>li>a {
|
||||
padding-top: 22px;
|
||||
padding-bottom: 22px;
|
||||
}
|
||||
|
||||
#search {
|
||||
color: #fff;
|
||||
font-family: 'Ubuntu', sans-serif;
|
||||
}
|
||||
|
||||
#search.loading {
|
||||
background-image: url('/img/loading.gif');
|
||||
background-position: 5px center;
|
||||
background-repeat: no-repeat;
|
||||
padding-left: 25px;
|
||||
}
|
||||
|
||||
#search::-webkit-input-placeholder {
|
||||
color: #BCDF7E;
|
||||
font-family: 'Ubuntu', sans-serif;
|
||||
font-size: 14px;
|
||||
font-style: italic;
|
||||
font-weight: 100;
|
||||
}
|
||||
|
||||
#search::-moz-placeholder {
|
||||
color: #BCDF7E;
|
||||
font-family: 'Ubuntu', sans-serif;
|
||||
font-size: 14px;
|
||||
font-weight: 100;
|
||||
}
|
||||
|
||||
.status {
|
||||
-moz-border-radius: 3px;
|
||||
-webkit-border-radius: 3px;
|
||||
background-color: #597338;
|
||||
border-radius: 3px;
|
||||
margin: 15px 0;
|
||||
padding: 8px 10px;
|
||||
font-size: 12px;
|
||||
color: #eee;
|
||||
text-align: center;
|
||||
margin-right: 10px;
|
||||
}
|
||||
|
||||
.status .tooltip {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.col-gray {
|
||||
-moz-border-radius: 5px;
|
||||
-webkit-border-radius: 5px;
|
||||
background-color: #F4F4F4;
|
||||
border-radius: 5px;
|
||||
padding: 14px;
|
||||
border: 1px solid #eee;
|
||||
}
|
||||
|
||||
.col-gray-responsive {
|
||||
width: auto;
|
||||
}
|
||||
|
||||
.col-gray-fixed {
|
||||
margin-top: 15px;
|
||||
position: fixed;
|
||||
width: 250px;
|
||||
border: 1px solid #eee;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.col-gray-fixed {
|
||||
width:100%;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 995px) {
|
||||
.col-gray-fixed {
|
||||
position:static;
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
@media (min-width: 1200px) {
|
||||
.col-gray-fixed {
|
||||
width: 280px;
|
||||
}
|
||||
}
|
||||
|
||||
.ellipsis {
|
||||
display: block;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.line20 {
|
||||
border: 1px solid #D4D4D4;
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
|
||||
.line10 {
|
||||
border: 1px solid #EAEAEA;
|
||||
margin: 10px 0;
|
||||
}
|
||||
|
||||
.block-id {
|
||||
background-color: #373D42;
|
||||
border: 3px solid #FFFFFF;
|
||||
margin: 0 auto;
|
||||
width: 165px;
|
||||
color: #fff;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.block-id span {
|
||||
font-size: 40px;
|
||||
margin: 30px 0;
|
||||
}
|
||||
|
||||
.block-id h2 {
|
||||
color: #FFFFFF;
|
||||
font-weight: bold;
|
||||
line-height: 30px;
|
||||
font-size: 24px;
|
||||
margin-top: 0;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.icon-block {
|
||||
color: #FFFFFF;
|
||||
font-size: 35px;
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
.icon-block h3 {
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.block-tx {
|
||||
-moz-border-radius: 2px;
|
||||
-webkit-border-radius: 2px;
|
||||
background-color: #F4F4F4;
|
||||
border-radius: 2px;
|
||||
margin: 20px 0 10px;
|
||||
overflow: hidden;
|
||||
padding: 15px;
|
||||
border: 1px solid #eee;
|
||||
}
|
||||
|
||||
.btn {
|
||||
border-radius: 2px;
|
||||
}
|
||||
.btn-primary {
|
||||
background-color: #8DC429;
|
||||
border: 2px solid #76AF0F;
|
||||
}
|
||||
|
||||
.btn-primary:hover, .btn-primary:focus, .btn-primary:active,
|
||||
.btn-primary.active, .open .dropdown-toggle.btn-primary,
|
||||
.btn-success:hover, .btn-success:focus, .btn-success:active,
|
||||
.btn-success.active, .open .dropdown-toggle.btn-success,
|
||||
.btn-danger:hover, .btn-danger:focus, .btn-danger:active,
|
||||
.btn-danger.active, .open .dropdown-toggle.btn-danger {
|
||||
background-color: #fff;
|
||||
border: 2px solid #ccc;
|
||||
color: #373D42;
|
||||
}
|
||||
|
||||
.btn-default:hover, .btn-default:focus, .btn-default:active, .btn-default.active, .open .dropdown-toggle.btn-default {
|
||||
background-color: #fff;
|
||||
}
|
||||
|
||||
.btn-default {
|
||||
background-color: #E7E7E7;
|
||||
}
|
||||
|
||||
.btn-success {
|
||||
background-color: #2FA4D7;
|
||||
border: 2px solid #237FA7;
|
||||
}
|
||||
|
||||
.btn-danger {
|
||||
background-color: #AC0015;
|
||||
border: 2px solid #6C0000;
|
||||
}
|
||||
|
||||
.txvalues {
|
||||
display: inline-block;
|
||||
padding: .7em 2em;
|
||||
font-size: 13px;
|
||||
text-transform: uppercase;
|
||||
font-weight:100;
|
||||
color: #fff;
|
||||
text-align: center;
|
||||
white-space: nowrap;
|
||||
vertical-align: baseline;
|
||||
border-radius: .25em;
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.txvalues {
|
||||
display: block;
|
||||
margin: 5px;
|
||||
}
|
||||
}
|
||||
|
||||
.txvalues-primary {
|
||||
background-color: #8DC429;
|
||||
}
|
||||
|
||||
.txvalues-default {
|
||||
background-color: #EBEBEB;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.txvalues-success {
|
||||
background-color: #2FA4D7;
|
||||
}
|
||||
|
||||
.txvalues-danger {
|
||||
background-color: #AC0015;
|
||||
}
|
||||
|
||||
.txvalues-normal {
|
||||
background-color: transparent;
|
||||
text-transform: none;
|
||||
color: #333;
|
||||
font-size: 14px;
|
||||
font-weight: normal;
|
||||
}
|
||||
.progress-bar-info { background-color: #8DC429; }
|
||||
|
||||
/* Set the fixed height of the footer here */
|
||||
#footer {
|
||||
background-color: #373D42;
|
||||
color: #fff;
|
||||
height: 51px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
#footer a.insight {
|
||||
font-size: 20px;
|
||||
text-decoration: none;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
#footer a.insight:hover {
|
||||
color: #fffffe;
|
||||
}
|
||||
|
||||
#footer a.insight small { font-size: 11px; }
|
||||
.line-footer { border-top: 2px dashed #ccc; }
|
||||
|
||||
.line-bot {
|
||||
border-bottom: 2px solid #EAEAEA;
|
||||
padding: 0 0 10px 0;
|
||||
}
|
||||
.line-mid { padding: 15px 0;}
|
||||
.line-top {
|
||||
border-top: 1px solid #EAEAEA;
|
||||
padding: 15px 0 0 0;
|
||||
}
|
||||
|
||||
/* Custom page CSS
|
||||
-------------------------------------------------- */
|
||||
/* Not required for template or sticky footer method. */
|
||||
|
||||
#wrap > .container { padding: 70px 15px 0; }
|
||||
|
||||
#footer > .container { padding: auto 15px; }
|
||||
|
||||
.code { font-size: 80%; }
|
||||
|
||||
.address { font-size: 11px; }
|
||||
|
||||
.no_matching {
|
||||
-moz-border-radius-bottomleft: 2px;
|
||||
-moz-border-radius-bottomright: 2px;
|
||||
-webkit-border-bottom-left-radius: 2px;
|
||||
-webkit-border-bottom-right-radius: 2px;
|
||||
background-color: #FFFFFF;
|
||||
border-bottom-left-radius: 2px;
|
||||
border-bottom-right-radius: 2px;
|
||||
border-top: none;
|
||||
border: 1px solid #64920F;
|
||||
padding: 10px 20px;
|
||||
position: absolute;
|
||||
text-align: center;
|
||||
top: 45px;
|
||||
width: 300px;
|
||||
}
|
||||
|
||||
/*Animations*/
|
||||
.fader.ng-enter {
|
||||
opacity: 0;
|
||||
-webkit-transition: opacity 1s;
|
||||
-moz-transition: opacity 1s;
|
||||
-o-transition: opacity 1s;
|
||||
transition: opacity 1s;
|
||||
}
|
||||
|
||||
.fader.ng-enter-active { opacity: 1; }
|
||||
|
||||
.tx-bg {
|
||||
background-color: #F4F4F4;
|
||||
left: 0;
|
||||
min-height: 340px;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
width: 100%;
|
||||
z-index: -9999;
|
||||
}
|
||||
|
||||
.badge {
|
||||
-moz-border-radius: 9px;
|
||||
-webkit-border-radius: 9px;
|
||||
background-color: #999999;
|
||||
border-radius: 9px;
|
||||
color: #ffffff;
|
||||
font-size: 12.025px;
|
||||
font-weight: bold;
|
||||
padding: 1px 9px 2px;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.badge:hover {
|
||||
color: #ffffff;
|
||||
text-decoration: none;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.badge-error { background-color: #b94a48; }
|
||||
.badge-error:hover { background-color: #953b39; }
|
||||
.badge-warning { background-color: #f89406; }
|
||||
.badge-warning:hover { background-color: #c67605; }
|
||||
.badge-success { background-color: #468847; }
|
||||
.badge-success:hover { background-color: #356635; }
|
||||
.badge-info { background-color: #3a87ad; }
|
||||
.badge-info:hover { background-color: #2d6987; }
|
||||
.badge-inverse { background-color: #333333; }
|
||||
.badge-inverse:hover { background-color: #1a1a1a; }
|
||||
|
||||
.status .t {
|
||||
color: white;
|
||||
}
|
||||
|
||||
.status .text-danger { background: red; }
|
||||
|
||||
.status .text-warning {
|
||||
background: yellow;
|
||||
color: black;
|
||||
}
|
||||
|
||||
.btn-copy {
|
||||
color: #9b9b9b;
|
||||
display: inline-block;
|
||||
height: 16px;
|
||||
width: 16px;
|
||||
outline: none;
|
||||
vertical-align: sub;
|
||||
}
|
||||
|
||||
.btn-expand {
|
||||
color: #9b9b9b;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
.btn-copy:hover, .btn-expand:hover {
|
||||
color: #000;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.btn-copy {
|
||||
background: transparent url('/img/icons/copy.png') center center no-repeat;
|
||||
}
|
||||
|
||||
.btn-copy .tooltip {
|
||||
display: block;
|
||||
margin-left: 20px;
|
||||
margin-top: -2px;
|
||||
opacity: 0;
|
||||
}
|
||||
.btn-copy.zeroclipboard-is-hover { color: #2a6496; }
|
||||
.btn-copy.zeroclipboard-is-active .tooltip { opacity: 1; }
|
||||
|
||||
@media (max-width: 991px) {
|
||||
.btn-copy {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
.txid {
|
||||
line-height: 26px;
|
||||
}
|
||||
|
||||
.tx-id {
|
||||
background-color: #373D42;
|
||||
border: 3px solid #FFFFFF;
|
||||
margin: 0 auto;
|
||||
width: 165px;
|
||||
color: #FFFFFF;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.tx-id span {
|
||||
font-size: 40px;
|
||||
margin: 30px 0;
|
||||
}
|
||||
|
||||
.page-header { margin-top: 0; }
|
||||
|
||||
/* Index */
|
||||
#home .btn-more {
|
||||
border-top: 1px solid #ddd;
|
||||
margin: 30px auto 0;
|
||||
text-align: center;
|
||||
width: 90%;
|
||||
}
|
||||
#home .btn-more .btn-default {
|
||||
margin-top: -23px;
|
||||
}
|
||||
|
||||
#powered .powered-text {
|
||||
border-top: 1px solid #ddd;
|
||||
margin: 30px auto 0;
|
||||
text-align: center;
|
||||
width: 90%;
|
||||
}
|
||||
#powered .powered-text small {
|
||||
background-color: #f4f4f4;
|
||||
padding: 4px;
|
||||
position: relative;
|
||||
top: -12px;
|
||||
}
|
||||
|
||||
#powered a {
|
||||
background-repeat: no-repeat;
|
||||
background-position: center center;
|
||||
display: inline-block;
|
||||
float: left;
|
||||
height: 45px;
|
||||
}
|
||||
#powered a.bitcore {
|
||||
background-image: url('http://bitcore.io/images/logo.svg');
|
||||
background-size: 80px;
|
||||
width: 30%;
|
||||
}
|
||||
#powered a.nodejs {
|
||||
background-image: url('/img/nodejs.png');
|
||||
background-size: 80px;
|
||||
width: 30%;
|
||||
}
|
||||
#powered a.angularjs {
|
||||
background-image: url('/img/angularjs.png');
|
||||
background-size: 50px;
|
||||
width: 20%;
|
||||
}
|
||||
#powered a.leveldb {
|
||||
background-image: url('/img/leveldb.png');
|
||||
background-size: 50px;
|
||||
width: 20%;
|
||||
}
|
||||
|
||||
@keyframes rotateThis {
|
||||
from { transform: scale( 1 ) rotate( 0deg ); }
|
||||
to { transform: scale( 1 ) rotate( 360deg ); }
|
||||
}
|
||||
|
||||
@-webkit-keyframes rotateThis {
|
||||
from { -webkit-transform: scale( 1 ) rotate( 0deg ); }
|
||||
to { -webkit-transform: scale( 1 ) rotate( 360deg ); }
|
||||
}
|
||||
|
||||
.icon-rotate {
|
||||
animation-name: rotateThis;
|
||||
animation-duration: 2s;
|
||||
animation-iteration-count: infinite;
|
||||
animation-timing-function: linear;
|
||||
-webkit-animation-name: rotateThis;
|
||||
-webkit-animation-duration: 2s;
|
||||
-webkit-animation-iteration-count: infinite;
|
||||
-webkit-animation-timing-function: linear;
|
||||
}
|
||||
|
||||
.transaction-vin-vout {
|
||||
line-height: 1.8em;
|
||||
}
|
||||
|
||||
.v_highlight {
|
||||
background-color: #8DC429;
|
||||
overflow: hidden;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
a.v_highlight_more {
|
||||
background-color: #8DC429;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.secondary_navbar {
|
||||
width: 100%;
|
||||
background: #fff;
|
||||
position: fixed;
|
||||
top: 64px;
|
||||
left: 0;
|
||||
text-align: center;
|
||||
z-index: 1000;
|
||||
margin: 0 auto;
|
||||
-moz-box-shadow: 0px 1px 4px 0px rgba(0,0,0,0.20);
|
||||
-webkit-box-shadow: 0px 1px 4px 0px rgba(0,0,0,0.20);
|
||||
box-shadow: 0px 1px 4px 0px rgba(0,0,0,0.20);
|
||||
}
|
||||
|
||||
.secondary_navbar .container {
|
||||
margin: 0 auto;
|
||||
padding: 1.8em 0;
|
||||
}
|
||||
|
||||
.secondary_navbar h3, .secondary_navbar p, .secondary_navbar .lead {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.secondary_navbar p {
|
||||
line-height: 1.9em;
|
||||
}
|
||||
|
||||
.hide_snavbar {
|
||||
border-bottom-right-radius: 0.3em;
|
||||
border-bottom-left-radius: 0.3em;
|
||||
position: absolute;
|
||||
right: 25px;
|
||||
padding: 5px 10px;
|
||||
background: #fff;
|
||||
-moz-box-shadow: 0px 2px 3px 0px rgba(0,0,0,0.20);
|
||||
-webkit-box-shadow: 0px 2px 3px 0px rgba(0,0,0,0.20);
|
||||
box-shadow: 0px 2px 3px 0px rgba(0,0,0,0.20);
|
||||
}
|
||||
|
||||
|
|
@ -1,30 +0,0 @@
|
|||
'use strict';
|
||||
|
||||
angular.module('insight',[
|
||||
'ngAnimate',
|
||||
'ngResource',
|
||||
'ngRoute',
|
||||
'ngProgress',
|
||||
'ui.bootstrap',
|
||||
'ui.route',
|
||||
'monospaced.qrcode',
|
||||
'insight.system',
|
||||
'insight.socket',
|
||||
'insight.blocks',
|
||||
'insight.transactions',
|
||||
'insight.address',
|
||||
'insight.search',
|
||||
'insight.status',
|
||||
'insight.connection',
|
||||
'insight.currency'
|
||||
]);
|
||||
|
||||
angular.module('insight.system', []);
|
||||
angular.module('insight.socket', []);
|
||||
angular.module('insight.blocks', []);
|
||||
angular.module('insight.transactions', []);
|
||||
angular.module('insight.address', []);
|
||||
angular.module('insight.search', []);
|
||||
angular.module('insight.status', []);
|
||||
angular.module('insight.connection', []);
|
||||
angular.module('insight.currency', []);
|
|
@ -1,64 +0,0 @@
|
|||
'use strict';
|
||||
|
||||
//Setting up route
|
||||
angular.module('insight').config(function($routeProvider) {
|
||||
$routeProvider.
|
||||
when('/block/:blockHash', {
|
||||
templateUrl: '/views/block.html',
|
||||
title: 'Bitcoin Block '
|
||||
}).
|
||||
when('/block-index/:blockHeight', {
|
||||
controller: 'BlocksController',
|
||||
templateUrl: '/views/redirect.html'
|
||||
}).
|
||||
when('/tx/:txId/:v_type?/:v_index?', {
|
||||
templateUrl: '/views/transaction.html',
|
||||
title: 'Bitcoin Transaction '
|
||||
}).
|
||||
when('/', {
|
||||
templateUrl: '/views/index.html',
|
||||
title: 'Home'
|
||||
}).
|
||||
when('/blocks', {
|
||||
templateUrl: '/views/block_list.html',
|
||||
title: 'Bitcoin Blocks solved Today'
|
||||
}).
|
||||
when('/blocks-date/:blockDate', {
|
||||
templateUrl: '/views/block_list.html',
|
||||
title: 'Bitcoin Blocks solved '
|
||||
}).
|
||||
when('/address/:addrStr', {
|
||||
templateUrl: '/views/address.html',
|
||||
title: 'Bitcoin Address '
|
||||
}).
|
||||
when('/status', {
|
||||
templateUrl: '/views/status.html',
|
||||
title: 'Status'
|
||||
})
|
||||
.otherwise({
|
||||
templateUrl: '/views/404.html',
|
||||
title: 'Error'
|
||||
});
|
||||
});
|
||||
|
||||
//Setting HTML5 Location Mode
|
||||
angular.module('insight')
|
||||
.config(function($locationProvider) {
|
||||
$locationProvider.html5Mode(true);
|
||||
$locationProvider.hashPrefix('!');
|
||||
})
|
||||
.run(function($rootScope, $route, ngProgress) {
|
||||
$rootScope.$on('$routeChangeStart', function() {
|
||||
ngProgress.start();
|
||||
});
|
||||
|
||||
$rootScope.$on('$routeChangeSuccess', function() {
|
||||
ngProgress.complete();
|
||||
|
||||
//Change page title, based on Route information
|
||||
$rootScope.titleDetail = '';
|
||||
$rootScope.title = $route.current.title;
|
||||
$rootScope.isCollapsed = true;
|
||||
$rootScope.currentAddr = null;
|
||||
});
|
||||
});
|
|
@ -1,41 +0,0 @@
|
|||
'use strict';
|
||||
|
||||
angular.module('insight.address').controller('AddressController',
|
||||
function($scope, $rootScope, $routeParams, $location, Global, Address, getSocket) {
|
||||
$scope.global = Global;
|
||||
|
||||
$scope.findOne = function() {
|
||||
$rootScope.currentAddr = $routeParams.addrStr;
|
||||
|
||||
Address.get({
|
||||
addrStr: $routeParams.addrStr
|
||||
},
|
||||
function(address) {
|
||||
$rootScope.titleDetail = address.addrStr.substring(0, 7) + '...';
|
||||
$rootScope.flashMessage = null;
|
||||
$scope.address = address;
|
||||
},
|
||||
function(e) {
|
||||
if (e.status === 400) {
|
||||
$rootScope.flashMessage = 'Invalid Address: ' + $routeParams.addrStr;
|
||||
} else if (e.status === 503) {
|
||||
$rootScope.flashMessage = 'Backend Error. ' + e.data;
|
||||
} else {
|
||||
$rootScope.flashMessage = 'Address Not Found';
|
||||
}
|
||||
$location.path('/');
|
||||
});
|
||||
};
|
||||
|
||||
var socket = getSocket($scope);
|
||||
socket.on('connect', function() {
|
||||
socket.emit('subscribe', $routeParams.addrStr);
|
||||
socket.on($routeParams.addrStr, function(tx) {
|
||||
console.log('AddressTx event received ' + tx);
|
||||
$rootScope.$broadcast('tx', tx);
|
||||
});
|
||||
});
|
||||
|
||||
$scope.params = $routeParams;
|
||||
|
||||
});
|
|
@ -1,90 +0,0 @@
|
|||
'use strict';
|
||||
|
||||
angular.module('insight.blocks').controller('BlocksController',
|
||||
function($scope, $rootScope, $routeParams, $location, Global, Block, Blocks, BlockByHeight) {
|
||||
$scope.global = Global;
|
||||
$scope.loading = false;
|
||||
|
||||
if ($routeParams.blockHeight) {
|
||||
BlockByHeight.get({
|
||||
blockHeight: $routeParams.blockHeight
|
||||
}, function(hash) {
|
||||
$location.path('/block/' + hash.blockHash);
|
||||
}, function() {
|
||||
$rootScope.flashMessage = 'Bad Request';
|
||||
$location.path('/');
|
||||
});
|
||||
}
|
||||
|
||||
//Datepicker
|
||||
var _formatTimestamp = function (date) {
|
||||
var yyyy = date.getUTCFullYear().toString();
|
||||
var mm = (date.getUTCMonth() + 1).toString(); // getMonth() is zero-based
|
||||
var dd = date.getUTCDate().toString();
|
||||
|
||||
return yyyy + '-' + (mm[1] ? mm : '0' + mm[0]) + '-' + (dd[1] ? dd : '0' + dd[0]); //padding
|
||||
};
|
||||
|
||||
$scope.$watch('dt', function(newValue, oldValue) {
|
||||
if (newValue !== oldValue) {
|
||||
$location.path('/blocks-date/' + _formatTimestamp(newValue));
|
||||
}
|
||||
});
|
||||
|
||||
$scope.openCalendar = function($event) {
|
||||
$event.preventDefault();
|
||||
$event.stopPropagation();
|
||||
|
||||
$scope.opened = true;
|
||||
};
|
||||
|
||||
$scope.humanSince = function(time) {
|
||||
var m = moment.unix(time).startOf('day');
|
||||
var b = moment().startOf('day');
|
||||
return m.max().from(b);
|
||||
};
|
||||
|
||||
|
||||
$scope.list = function() {
|
||||
$scope.loading = true;
|
||||
|
||||
if ($routeParams.blockDate) {
|
||||
$rootScope.titleDetail = 'on ' + $routeParams.blockDate;
|
||||
}
|
||||
|
||||
Blocks.get({
|
||||
blockDate: $routeParams.blockDate
|
||||
}, function(res) {
|
||||
$scope.loading = false;
|
||||
$scope.blocks = res.blocks;
|
||||
$scope.pagination = res.pagination;
|
||||
});
|
||||
};
|
||||
|
||||
$scope.findOne = function() {
|
||||
$scope.loading = true;
|
||||
|
||||
Block.get({
|
||||
blockHash: $routeParams.blockHash
|
||||
}, function(block) {
|
||||
$rootScope.titleDetail = block.height;
|
||||
$rootScope.flashMessage = null;
|
||||
$scope.loading = false;
|
||||
$scope.block = block;
|
||||
}, function(e) {
|
||||
if (e.status === 400) {
|
||||
$rootScope.flashMessage = 'Invalid Transaction ID: ' + $routeParams.txId;
|
||||
}
|
||||
else if (e.status === 503) {
|
||||
$rootScope.flashMessage = 'Backend Error. ' + e.data;
|
||||
}
|
||||
else {
|
||||
$rootScope.flashMessage = 'Block Not Found';
|
||||
}
|
||||
$location.path('/');
|
||||
});
|
||||
};
|
||||
|
||||
$scope.params = $routeParams;
|
||||
|
||||
});
|
|
@ -1,53 +0,0 @@
|
|||
'use strict';
|
||||
|
||||
angular.module('insight.connection').controller('ConnectionController',
|
||||
function($scope, $window, Status, getSocket, PeerSync) {
|
||||
|
||||
// Set initial values
|
||||
$scope.apiOnline = true;
|
||||
$scope.serverOnline = true;
|
||||
$scope.clienteOnline = true;
|
||||
|
||||
var socket = getSocket($scope);
|
||||
|
||||
// Check for the node server connection
|
||||
socket.on('connect', function() {
|
||||
$scope.serverOnline = true;
|
||||
socket.on('disconnect', function() {
|
||||
$scope.serverOnline = false;
|
||||
});
|
||||
});
|
||||
|
||||
// Check for the api connection
|
||||
$scope.getConnStatus = function() {
|
||||
PeerSync.get({},
|
||||
function(peer) {
|
||||
$scope.apiOnline = peer.connected;
|
||||
$scope.host = peer.host;
|
||||
$scope.port = peer.port;
|
||||
},
|
||||
function() {
|
||||
$scope.apiOnline = false;
|
||||
});
|
||||
};
|
||||
|
||||
socket.emit('subscribe', 'sync');
|
||||
socket.on('status', function(sync) {
|
||||
$scope.sync = sync;
|
||||
$scope.apiOnline = (sync.status !== 'aborted' && sync.status !== 'error');
|
||||
});
|
||||
|
||||
// Check for the client conneciton
|
||||
$window.addEventListener('offline', function() {
|
||||
$scope.$apply(function() {
|
||||
$scope.clienteOnline = false;
|
||||
});
|
||||
}, true);
|
||||
|
||||
$window.addEventListener('online', function() {
|
||||
$scope.$apply(function() {
|
||||
$scope.clienteOnline = true;
|
||||
});
|
||||
}, true);
|
||||
|
||||
});
|
|
@ -1,51 +0,0 @@
|
|||
'use strict';
|
||||
|
||||
angular.module('insight.currency').controller('CurrencyController',
|
||||
function($scope, $rootScope, Currency) {
|
||||
|
||||
var _roundFloat = function(x, n) {
|
||||
if(!parseInt(n, 10) || !parseFloat(x)) n = 0;
|
||||
|
||||
return Math.round(x * Math.pow(10, n)) / Math.pow(10, n);
|
||||
};
|
||||
|
||||
$rootScope.currency.getConvertion = function(value) {
|
||||
if (typeof value !== 'undefined' && value !== null) {
|
||||
var response;
|
||||
|
||||
if (this.symbol === 'USD') {
|
||||
response = _roundFloat((value * this.factor), 2);
|
||||
} else if (this.symbol === 'mBTC') {
|
||||
this.factor = 1000;
|
||||
response = _roundFloat((value * this.factor), 5);
|
||||
} else {
|
||||
this.factor = 1;
|
||||
response = value;
|
||||
}
|
||||
|
||||
return response + ' ' + this.symbol;
|
||||
}
|
||||
|
||||
return 'value error';
|
||||
};
|
||||
|
||||
$scope.setCurrency = function(currency) {
|
||||
$rootScope.currency.symbol = currency;
|
||||
|
||||
if (currency === 'USD') {
|
||||
Currency.get({}, function(res) {
|
||||
$rootScope.currency.factor = $rootScope.currency.bitstamp = res.data.bitstamp;
|
||||
});
|
||||
} else if (currency === 'mBTC') {
|
||||
$rootScope.currency.factor = 1000;
|
||||
} else {
|
||||
$rootScope.currency.factor = 1;
|
||||
}
|
||||
};
|
||||
|
||||
// Get initial value
|
||||
Currency.get({}, function(res) {
|
||||
$rootScope.currency.bitstamp = res.data.bitstamp;
|
||||
});
|
||||
|
||||
});
|
|
@ -1,15 +0,0 @@
|
|||
'use strict';
|
||||
|
||||
angular.module('insight.system').controller('FooterController',
|
||||
function($scope, Version) {
|
||||
|
||||
var _getVersion = function() {
|
||||
Version.get({},
|
||||
function(res) {
|
||||
$scope.version = res.version;
|
||||
});
|
||||
};
|
||||
|
||||
$scope.version = _getVersion();
|
||||
|
||||
});
|
|
@ -1,40 +0,0 @@
|
|||
'use strict';
|
||||
|
||||
angular.module('insight.system').controller('HeaderController',
|
||||
function($scope, $rootScope, getSocket, Global, Block) {
|
||||
$scope.global = Global;
|
||||
|
||||
$rootScope.currency = {
|
||||
factor: 1,
|
||||
bitstamp: 0,
|
||||
symbol: 'BTC'
|
||||
};
|
||||
|
||||
$scope.menu = [{
|
||||
'title': 'Blocks',
|
||||
'link': 'blocks'
|
||||
}, {
|
||||
'title': 'Status',
|
||||
'link': 'status'
|
||||
}];
|
||||
|
||||
var _getBlock = function(hash) {
|
||||
Block.get({
|
||||
blockHash: hash
|
||||
}, function(res) {
|
||||
$scope.totalBlocks = res.height;
|
||||
});
|
||||
};
|
||||
|
||||
var socket = getSocket($scope);
|
||||
socket.on('connect', function() {
|
||||
socket.emit('subscribe', 'inv');
|
||||
|
||||
socket.on('block', function(block) {
|
||||
var blockHash = block.toString();
|
||||
_getBlock(blockHash);
|
||||
});
|
||||
});
|
||||
|
||||
$rootScope.isCollapsed = true;
|
||||
});
|
|
@ -1,47 +0,0 @@
|
|||
'use strict';
|
||||
|
||||
var TRANSACTION_DISPLAYED = 10;
|
||||
var BLOCKS_DISPLAYED = 5;
|
||||
|
||||
angular.module('insight.system').controller('IndexController',
|
||||
function($scope, Global, getSocket, Blocks) {
|
||||
$scope.global = Global;
|
||||
|
||||
var _getBlocks = function() {
|
||||
Blocks.get({
|
||||
limit: BLOCKS_DISPLAYED
|
||||
}, function(res) {
|
||||
$scope.blocks = res.blocks;
|
||||
$scope.blocksLength = res.lenght;
|
||||
});
|
||||
};
|
||||
|
||||
var socket = getSocket($scope);
|
||||
socket.on('connect', function() {
|
||||
socket.emit('subscribe', 'inv');
|
||||
|
||||
socket.on('tx', function(tx) {
|
||||
$scope.txs.unshift(tx);
|
||||
if (parseInt($scope.txs.length, 10) >= parseInt(TRANSACTION_DISPLAYED, 10)) {
|
||||
$scope.txs = $scope.txs.splice(0, TRANSACTION_DISPLAYED);
|
||||
}
|
||||
});
|
||||
|
||||
socket.on('block', function() {
|
||||
_getBlocks();
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
$scope.humanSince = function(time) {
|
||||
var m = moment.unix(time);
|
||||
return m.max().fromNow();
|
||||
};
|
||||
|
||||
$scope.index = function() {
|
||||
_getBlocks();
|
||||
};
|
||||
|
||||
$scope.txs = [];
|
||||
$scope.blocks = [];
|
||||
});
|
|
@ -1,63 +0,0 @@
|
|||
'use strict';
|
||||
|
||||
angular.module('insight.search').controller('SearchController',
|
||||
function($scope, $routeParams, $location, $timeout, Global, Block, Transaction, Address, BlockByHeight) {
|
||||
$scope.global = Global;
|
||||
$scope.loading = false;
|
||||
|
||||
var _badQuery = function() {
|
||||
$scope.badQuery = true;
|
||||
|
||||
$timeout(function() {
|
||||
$scope.badQuery = false;
|
||||
}, 2000);
|
||||
};
|
||||
|
||||
var _resetSearch = function() {
|
||||
$scope.q = '';
|
||||
$scope.loading = false;
|
||||
};
|
||||
|
||||
$scope.search = function() {
|
||||
var q = $scope.q;
|
||||
$scope.badQuery = false;
|
||||
$scope.loading = true;
|
||||
|
||||
Block.get({
|
||||
blockHash: q
|
||||
}, function() {
|
||||
_resetSearch();
|
||||
$location.path('block/' + q);
|
||||
}, function() { //block not found, search on TX
|
||||
Transaction.get({
|
||||
txId: q
|
||||
}, function() {
|
||||
_resetSearch();
|
||||
$location.path('tx/' + q);
|
||||
}, function() { //tx not found, search on Address
|
||||
Address.get({
|
||||
addrStr: q
|
||||
}, function() {
|
||||
_resetSearch();
|
||||
$location.path('address/' + q);
|
||||
}, function() { // block by height not found
|
||||
if (isFinite(q)) { // ensure that q is a finite number. A logical height value.
|
||||
BlockByHeight.get({
|
||||
blockHeight: q
|
||||
}, function(hash) {
|
||||
_resetSearch();
|
||||
$location.path('/block/' + hash.blockHash);
|
||||
}, function() { //not found, fail :(
|
||||
_badQuery();
|
||||
});
|
||||
}
|
||||
else {
|
||||
$scope.loading = false;
|
||||
_badQuery();
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
});
|
|
@ -1,49 +0,0 @@
|
|||
'use strict';
|
||||
|
||||
angular.module('insight.status').controller('StatusController',
|
||||
function($scope, $routeParams, $location, Global, Status, Sync, getSocket) {
|
||||
$scope.global = Global;
|
||||
|
||||
$scope.getStatus = function(q) {
|
||||
Status.get({
|
||||
q: 'get' + q
|
||||
},
|
||||
function(d) {
|
||||
$scope.loaded = 1;
|
||||
angular.extend($scope, d);
|
||||
},
|
||||
function(e) {
|
||||
$scope.error = 'API ERROR: ' + e.data;
|
||||
});
|
||||
};
|
||||
|
||||
$scope.humanSince = function(time) {
|
||||
var m = moment.unix(time / 1000);
|
||||
return m.max().fromNow();
|
||||
};
|
||||
|
||||
var _onSyncUpdate = function(sync) {
|
||||
$scope.sync = sync;
|
||||
};
|
||||
|
||||
$scope.getSync = function() {
|
||||
Sync.get({},
|
||||
function(sync) {
|
||||
_onSyncUpdate(sync);
|
||||
},
|
||||
function(e) {
|
||||
var err = 'Could not get sync information' + e.toString();
|
||||
$scope.sync = {
|
||||
error: err
|
||||
};
|
||||
});
|
||||
};
|
||||
|
||||
var socket = getSocket($scope);
|
||||
socket.on('connect', function() {
|
||||
socket.emit('subscribe', 'sync');
|
||||
socket.on('status', function(sync) {
|
||||
_onSyncUpdate(sync);
|
||||
});
|
||||
});
|
||||
});
|
|
@ -1,172 +0,0 @@
|
|||
'use strict';
|
||||
|
||||
angular.module('insight.transactions').controller('transactionsController',
|
||||
function($scope, $rootScope, $routeParams, $location, Global, Transaction, TransactionsByBlock, TransactionsByAddress) {
|
||||
$scope.global = Global;
|
||||
$scope.loading = false;
|
||||
$scope.loadedBy = null;
|
||||
|
||||
var pageNum = 0;
|
||||
var pagesTotal = 1;
|
||||
var COIN = 100000000;
|
||||
|
||||
var _aggregateItems = function(items) {
|
||||
if (!items) return [];
|
||||
|
||||
var l = items.length;
|
||||
|
||||
var ret = [];
|
||||
var tmp = {};
|
||||
var u = 0;
|
||||
|
||||
for(var i=0; i < l; i++) {
|
||||
|
||||
var notAddr = false;
|
||||
// non standard input
|
||||
if (items[i].scriptSig && !items[i].addr) {
|
||||
items[i].addr = 'Unparsed address [' + u++ + ']';
|
||||
items[i].notAddr = true;
|
||||
notAddr = true;
|
||||
}
|
||||
|
||||
// non standard output
|
||||
if (items[i].scriptPubKey && !items[i].scriptPubKey.addresses) {
|
||||
items[i].scriptPubKey.addresses = ['Unparsed address [' + u++ + ']'];
|
||||
items[i].notAddr = true;
|
||||
notAddr = true;
|
||||
}
|
||||
|
||||
// multiple addr at output
|
||||
if (items[i].scriptPubKey && items[i].scriptPubKey.addresses.length > 1) {
|
||||
items[i].addr = items[i].scriptPubKey.addresses.join(',');
|
||||
ret.push(items[i]);
|
||||
continue;
|
||||
}
|
||||
|
||||
var addr = items[i].addr || (items[i].scriptPubKey && items[i].scriptPubKey.addresses[0]);
|
||||
|
||||
if (!tmp[addr]) {
|
||||
tmp[addr] = {};
|
||||
tmp[addr].valueSat = 0;
|
||||
tmp[addr].count = 0;
|
||||
tmp[addr].addr = addr;
|
||||
tmp[addr].items = [];
|
||||
}
|
||||
tmp[addr].isSpent = items[i].spentTxId;
|
||||
|
||||
tmp[addr].doubleSpentTxID = tmp[addr].doubleSpentTxID || items[i].doubleSpentTxID;
|
||||
tmp[addr].doubleSpentIndex = tmp[addr].doubleSpentIndex || items[i].doubleSpentIndex;
|
||||
tmp[addr].unconfirmedInput += items[i].unconfirmedInput;
|
||||
tmp[addr].dbError = tmp[addr].dbError || items[i].dbError;
|
||||
tmp[addr].valueSat += Math.round(items[i].value * COIN);
|
||||
tmp[addr].items.push(items[i]);
|
||||
tmp[addr].notAddr = notAddr;
|
||||
tmp[addr].count++;
|
||||
}
|
||||
|
||||
angular.forEach(tmp, function(v) {
|
||||
v.value = v.value || parseInt(v.valueSat) / COIN;
|
||||
ret.push(v);
|
||||
});
|
||||
return ret;
|
||||
};
|
||||
|
||||
var _processTX = function(tx) {
|
||||
tx.vinSimple = _aggregateItems(tx.vin);
|
||||
tx.voutSimple = _aggregateItems(tx.vout);
|
||||
};
|
||||
|
||||
var _paginate = function(data) {
|
||||
$scope.loading = false;
|
||||
|
||||
pagesTotal = data.pagesTotal;
|
||||
pageNum += 1;
|
||||
|
||||
data.txs.forEach(function(tx) {
|
||||
_processTX(tx);
|
||||
$scope.txs.push(tx);
|
||||
});
|
||||
};
|
||||
|
||||
var _byBlock = function() {
|
||||
TransactionsByBlock.get({
|
||||
block: $routeParams.blockHash,
|
||||
pageNum: pageNum
|
||||
}, function(data) {
|
||||
_paginate(data);
|
||||
});
|
||||
};
|
||||
|
||||
var _byAddress = function () {
|
||||
TransactionsByAddress.get({
|
||||
address: $routeParams.addrStr,
|
||||
pageNum: pageNum
|
||||
}, function(data) {
|
||||
_paginate(data);
|
||||
});
|
||||
};
|
||||
|
||||
var _findTx = function(txid) {
|
||||
Transaction.get({
|
||||
txId: txid
|
||||
}, function(tx) {
|
||||
$rootScope.titleDetail = tx.txid.substring(0,7) + '...';
|
||||
$rootScope.flashMessage = null;
|
||||
$scope.tx = tx;
|
||||
_processTX(tx);
|
||||
$scope.txs.unshift(tx);
|
||||
}, function(e) {
|
||||
if (e.status === 400) {
|
||||
$rootScope.flashMessage = 'Invalid Transaction ID: ' + $routeParams.txId;
|
||||
}
|
||||
else if (e.status === 503) {
|
||||
$rootScope.flashMessage = 'Backend Error. ' + e.data;
|
||||
}
|
||||
else {
|
||||
$rootScope.flashMessage = 'Transaction Not Found';
|
||||
}
|
||||
|
||||
$location.path('/');
|
||||
});
|
||||
};
|
||||
|
||||
$scope.findThis = function() {
|
||||
_findTx($routeParams.txId);
|
||||
};
|
||||
|
||||
//Initial load
|
||||
$scope.load = function(from) {
|
||||
$scope.loadedBy = from;
|
||||
$scope.loadMore();
|
||||
};
|
||||
|
||||
//Load more transactions for pagination
|
||||
$scope.loadMore = function() {
|
||||
if (pageNum < pagesTotal && !$scope.loading) {
|
||||
$scope.loading = true;
|
||||
|
||||
if ($scope.loadedBy === 'address') {
|
||||
_byAddress();
|
||||
}
|
||||
else {
|
||||
_byBlock();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Highlighted txout
|
||||
if ($routeParams.v_type == '>' || $routeParams.v_type == '<') {
|
||||
$scope.from_vin = $routeParams.v_type == '<' ? true : false;
|
||||
$scope.from_vout = $routeParams.v_type == '>' ? true : false;
|
||||
$scope.v_index = parseInt($routeParams.v_index);
|
||||
$scope.itemsExpanded = true;
|
||||
}
|
||||
|
||||
//Init without txs
|
||||
$scope.txs = [];
|
||||
|
||||
$scope.$on('tx', function(event, txid) {
|
||||
_findTx(txid);
|
||||
});
|
||||
|
||||
});
|
|
@ -1,75 +0,0 @@
|
|||
'use strict';
|
||||
|
||||
var ZeroClipboard = window.ZeroClipboard;
|
||||
|
||||
angular.module('insight')
|
||||
.directive("scroll", function ($window) {
|
||||
return function(scope, element, attrs) {
|
||||
angular.element($window).bind("scroll", function() {
|
||||
if (this.pageYOffset >= 200) {
|
||||
scope.secondaryNavbar = true;
|
||||
} else {
|
||||
scope.secondaryNavbar = false;
|
||||
}
|
||||
scope.$apply();
|
||||
});
|
||||
};
|
||||
})
|
||||
.directive('whenScrolled', function($window) {
|
||||
return {
|
||||
restric: 'A',
|
||||
link: function(scope, elm, attr) {
|
||||
var pageHeight, clientHeight, scrollPos;
|
||||
$window = angular.element($window);
|
||||
|
||||
var handler = function() {
|
||||
pageHeight = window.document.documentElement.scrollHeight;
|
||||
clientHeight = window.document.documentElement.clientHeight;
|
||||
scrollPos = window.pageYOffset;
|
||||
|
||||
if (pageHeight - (scrollPos + clientHeight) === 0) {
|
||||
scope.$apply(attr.whenScrolled);
|
||||
}
|
||||
};
|
||||
|
||||
$window.on('scroll', handler);
|
||||
|
||||
scope.$on('$destroy', function() {
|
||||
return $window.off('scroll', handler);
|
||||
});
|
||||
}
|
||||
};
|
||||
})
|
||||
.directive('clipCopy', function() {
|
||||
ZeroClipboard.config({
|
||||
moviePath: '/lib/zeroclipboard/ZeroClipboard.swf',
|
||||
trustedDomains: ['*'],
|
||||
allowScriptAccess: 'always',
|
||||
forceHandCursor: true
|
||||
});
|
||||
|
||||
return {
|
||||
restric: 'A',
|
||||
scope: { clipCopy: '=clipCopy' },
|
||||
template: '<div class="tooltip fade right in"><div class="tooltip-arrow"></div><div class="tooltip-inner">Copied!</div></div>',
|
||||
link: function(scope, elm) {
|
||||
var clip = new ZeroClipboard(elm);
|
||||
|
||||
clip.on('load', function(client) {
|
||||
var onMousedown = function(client) {
|
||||
client.setText(scope.clipCopy);
|
||||
};
|
||||
|
||||
client.on('mousedown', onMousedown);
|
||||
|
||||
scope.$on('$destroy', function() {
|
||||
client.off('mousedown', onMousedown);
|
||||
});
|
||||
});
|
||||
|
||||
clip.on('noFlash wrongflash', function() {
|
||||
return elm.remove();
|
||||
});
|
||||
}
|
||||
};
|
||||
});
|
|
@ -1,9 +0,0 @@
|
|||
'use strict';
|
||||
|
||||
angular.module('insight')
|
||||
.filter('startFrom', function() {
|
||||
return function(input, start) {
|
||||
start = +start; //parse to int
|
||||
return input.slice(start);
|
||||
}
|
||||
});
|
|
@ -1,6 +0,0 @@
|
|||
'use strict';
|
||||
|
||||
angular.element(document).ready(function() {
|
||||
// Init the app
|
||||
// angular.bootstrap(document, ['insight']);
|
||||
});
|
|
@ -1,23 +0,0 @@
|
|||
'use strict';
|
||||
|
||||
angular.module('insight.address').factory('Address',
|
||||
function($resource) {
|
||||
return $resource('/api/addr/:addrStr', {
|
||||
addrStr: '@addStr'
|
||||
}, {
|
||||
get: {
|
||||
method: 'GET',
|
||||
interceptor: {
|
||||
response: function (res) {
|
||||
return res.data;
|
||||
},
|
||||
responseError: function (res) {
|
||||
if (res.status === 404) {
|
||||
return res;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
|
@ -1,31 +0,0 @@
|
|||
'use strict';
|
||||
|
||||
angular.module('insight.blocks')
|
||||
.factory('Block',
|
||||
function($resource) {
|
||||
return $resource('/api/block/:blockHash', {
|
||||
blockHash: '@blockHash'
|
||||
}, {
|
||||
get: {
|
||||
method: 'GET',
|
||||
interceptor: {
|
||||
response: function (res) {
|
||||
return res.data;
|
||||
},
|
||||
responseError: function (res) {
|
||||
if (res.status === 404) {
|
||||
return res;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
})
|
||||
.factory('Blocks',
|
||||
function($resource) {
|
||||
return $resource('/api/blocks');
|
||||
})
|
||||
.factory('BlockByHeight',
|
||||
function($resource) {
|
||||
return $resource('/api/block-index/:blockHeight');
|
||||
});
|
|
@ -1,6 +0,0 @@
|
|||
'use strict';
|
||||
|
||||
angular.module('insight.currency').factory('Currency',
|
||||
function($resource) {
|
||||
return $resource('/api/currency');
|
||||
});
|
|
@ -1,12 +0,0 @@
|
|||
'use strict';
|
||||
|
||||
//Global service for global variables
|
||||
angular.module('insight.system')
|
||||
.factory('Global',[
|
||||
function() {
|
||||
}
|
||||
])
|
||||
.factory('Version',
|
||||
function($resource) {
|
||||
return $resource('/api/version');
|
||||
});
|
|
@ -1,71 +0,0 @@
|
|||
'use strict';
|
||||
|
||||
var ScopedSocket = function(socket, $rootScope) {
|
||||
this.socket = socket;
|
||||
this.$rootScope = $rootScope;
|
||||
this.listeners = [];
|
||||
};
|
||||
|
||||
ScopedSocket.prototype.removeAllListeners = function(opts) {
|
||||
if (!opts) opts = {};
|
||||
for (var i = 0; i < this.listeners.length; i++) {
|
||||
var details = this.listeners[i];
|
||||
if (opts.skipConnect && details.event === 'connect') {
|
||||
continue;
|
||||
}
|
||||
this.socket.removeListener(details.event, details.fn);
|
||||
}
|
||||
this.listeners = [];
|
||||
};
|
||||
|
||||
ScopedSocket.prototype.on = function(event, callback) {
|
||||
var socket = this.socket;
|
||||
var $rootScope = this.$rootScope;
|
||||
|
||||
var wrapped_callback = function() {
|
||||
var args = arguments;
|
||||
$rootScope.$apply(function() {
|
||||
callback.apply(socket, args);
|
||||
});
|
||||
};
|
||||
socket.on(event, wrapped_callback);
|
||||
|
||||
this.listeners.push({
|
||||
event: event,
|
||||
fn: wrapped_callback
|
||||
});
|
||||
};
|
||||
|
||||
ScopedSocket.prototype.emit = function(event, data, callback) {
|
||||
var socket = this.socket;
|
||||
var $rootScope = this.$rootScope;
|
||||
|
||||
socket.emit(event, data, function() {
|
||||
var args = arguments;
|
||||
$rootScope.$apply(function() {
|
||||
if (callback) {
|
||||
callback.apply(socket, args);
|
||||
}
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
angular.module('insight.socket').factory('getSocket',
|
||||
function($rootScope) {
|
||||
var socket = io.connect(null, {
|
||||
'reconnect': true,
|
||||
'reconnection delay': 500,
|
||||
});
|
||||
return function(scope) {
|
||||
var scopedSocket = new ScopedSocket(socket, $rootScope);
|
||||
scope.$on('$destroy', function() {
|
||||
scopedSocket.removeAllListeners();
|
||||
});
|
||||
socket.on('connect', function() {
|
||||
scopedSocket.removeAllListeners({
|
||||
skipConnect: true
|
||||
});
|
||||
});
|
||||
return scopedSocket;
|
||||
};
|
||||
});
|
|
@ -1,17 +0,0 @@
|
|||
'use strict';
|
||||
|
||||
angular.module('insight.status')
|
||||
.factory('Status',
|
||||
function($resource) {
|
||||
return $resource('/api/status', {
|
||||
q: '@q'
|
||||
});
|
||||
})
|
||||
.factory('Sync',
|
||||
function($resource) {
|
||||
return $resource('/api/sync');
|
||||
})
|
||||
.factory('PeerSync',
|
||||
function($resource) {
|
||||
return $resource('/api/peer');
|
||||
});
|
|
@ -1,39 +0,0 @@
|
|||
'use strict';
|
||||
|
||||
angular.module('insight.transactions')
|
||||
.factory('Transaction',
|
||||
function($resource) {
|
||||
return $resource('/api/tx/:txId', {
|
||||
txId: '@txId'
|
||||
}, {
|
||||
get: {
|
||||
method: 'GET',
|
||||
interceptor: {
|
||||
response: function (res) {
|
||||
return res.data;
|
||||
},
|
||||
responseError: function (res) {
|
||||
if (res.status === 404) {
|
||||
return res;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
})
|
||||
.factory('TransactionsByBlock',
|
||||
function($resource) {
|
||||
return $resource('/api/txs', {
|
||||
block: '@block'
|
||||
});
|
||||
})
|
||||
.factory('TransactionsByAddress',
|
||||
function($resource) {
|
||||
return $resource('/api/txs', {
|
||||
address: '@address'
|
||||
});
|
||||
})
|
||||
.factory('Transactions',
|
||||
function($resource) {
|
||||
return $resource('/api/txs');
|
||||
});
|
|
@ -1,6 +0,0 @@
|
|||
<div data-ng-include src="'/views/includes/connection.html'"></div>
|
||||
<div class="jumbotron">
|
||||
<h1>Ooops!</h1>
|
||||
<h2 class="text-muted">404 Page not found :(</h2>
|
||||
<p><a href="/" class="pull-right">Go to home</a></p>
|
||||
</div>
|
|
@ -1,80 +0,0 @@
|
|||
<div data-ng-include src="'/views/includes/connection.html'"></div>
|
||||
<section data-ng-controller="AddressController" data-ng-init="findOne()">
|
||||
<div class="secondary_navbar hidden-xs hidden-sm" scroll data-ng-class="{'hidden': !secondaryNavbar}" data-ng-show="address.addrStr" data-ng-init="hideSNavbar=0">
|
||||
<div class="container" data-ng-if="!hideSNavbar">
|
||||
<div class="col-md-8 text-left">
|
||||
<h3>Address</h3> {{address.addrStr}}
|
||||
<span class="btn-copy" clip-copy="address.addrStr"></span>
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<span class="txvalues txvalues-primary"><strong>Final Balance</strong> {{$root.currency.getConvertion(address.balance) || address.balance + ' BTC' }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="hide_snavbar">
|
||||
<a href="#" data-ng-click="hideSNavbar=!hideSNavbar">
|
||||
<span data-ng-show="hideSNavbar"><span class="text-muted glyphicon glyphicon-chevron-down"></span></span>
|
||||
<span data-ng-show="!hideSNavbar"><span class="text-muted glyphicon glyphicon-chevron-up"></span></span>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<h1>Address <small data-ng-show="address.addrStr">{{$root.currency.getConvertion(address.balance) || address.balance + ' BTC'}}</small></h1>
|
||||
<div class="text-muted" data-ng-if="!address.addrStr">
|
||||
<span>Loading Address Information...</span>
|
||||
</div>
|
||||
<div data-ng-if="address.addrStr">
|
||||
<div class="well well-sm ellipsis">
|
||||
<strong>Address</strong>
|
||||
<span class="text-muted">{{address.addrStr}}</span>
|
||||
<span class="btn-copy" clip-copy="address.addrStr"></span>
|
||||
</div>
|
||||
<h2>Summary <small>confirmed</small></h2>
|
||||
<div class="ng-cloak row" data-ng-hide="!address.addrStr" data-ng-cloak>
|
||||
<div class="col-md-10">
|
||||
<table class="table">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td><strong>Total Received</strong></td>
|
||||
<td class="ellipsis text-right">{{$root.currency.getConvertion(address.totalReceived) || address.totalReceived + ' BTC'}}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><strong>Total Sent</strong></td>
|
||||
<td class="ellipsis text-right">{{$root.currency.getConvertion(address.totalSent) || address.totalSent + ' BTC'}}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><strong>Final Balance</strong></td>
|
||||
<td class="ellipsis text-right">{{$root.currency.getConvertion(address.balance) || address.balance + ' BTC'}}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><strong>No. Transactions</strong></td>
|
||||
<td class="ellipsis text-right">{{address.txApperances}}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<div class="col-md-2 text-center">
|
||||
<qrcode size="160" data="{{address.addrStr}}"></qrcode>
|
||||
</div>
|
||||
</div>
|
||||
<div data-ng-show="address.unconfirmedTxApperances">
|
||||
<h3>Unconfirmed</h3>
|
||||
<table class="table">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td class="small">Unconfirmed Txs Balance</td>
|
||||
<td class="address ellipsis text-right">{{$root.currency.getConvertion(address.unconfirmedBalance)}}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="small">No. Transactions</td>
|
||||
<td class="address ellipsis text-right">{{address.unconfirmedTxApperances}}</td>
|
||||
</tr>
|
||||
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
<h2>Transactions</h2>
|
||||
<div data-ng-controller="transactionsController" data-ng-init="load('address')">
|
||||
<div data-ng-include src="'/views/transaction/list.html'" when-scrolled="loadMore()"></div>
|
||||
</div>
|
||||
</section>
|
||||
|
|
@ -1,129 +0,0 @@
|
|||
<div data-ng-include src="'/views/includes/connection.html'"></div>
|
||||
<section data-ng-controller="BlocksController" data-ng-init="findOne()">
|
||||
<div class="secondary_navbar hidden-xs hidden-sm" scroll data-ng-class="{'hidden': !secondaryNavbar}" data-ng-show="block.hash" data-ng-init="hideSNavbar=0">
|
||||
<div class="container" data-ng-if="!hideSNavbar">
|
||||
<div class="row">
|
||||
<div class="col-md-1">
|
||||
<a href="/block/{{block.previousblockhash}}"><span class="lead glyphicon glyphicon-chevron-left"></span></a>
|
||||
</div>
|
||||
<div class="col-md-10">
|
||||
<div class="row">
|
||||
<div class="col-md-5">
|
||||
<h3 class="text-left">Block #{{block.height}}</h3>
|
||||
</div>
|
||||
<p class="col-md-6 ellipsis text-left">
|
||||
<strong>Hash</strong> {{block.hash}}
|
||||
</p>
|
||||
<div class="col-md-1 text-left">
|
||||
<span class="btn-copy" clip-copy="block.hash"></span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-1">
|
||||
<a data-ng-show="block.nextblockhash" href="/block/{{block.nextblockhash}}"><span class="lead glyphicon glyphicon-chevron-right"></span></a>
|
||||
</div>
|
||||
</div>
|
||||
</div> <!-- END OF CONTAINER -->
|
||||
<div class="hide_snavbar">
|
||||
<a href="#" data-ng-click="hideSNavbar=!hideSNavbar">
|
||||
<span data-ng-show="hideSNavbar"><span class="text-muted glyphicon glyphicon-chevron-down"></span></span>
|
||||
<span data-ng-show="!hideSNavbar"><span class="text-muted glyphicon glyphicon-chevron-up"></span></span>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<h1>Block #{{block.height}}</h1>
|
||||
<div class="text-muted" data-ng-if="!block.hash">
|
||||
<span>Loading Block Information...</span>
|
||||
</div>
|
||||
<div class="ng-cloak" data-ng-cloak data-ng-if="block.hash">
|
||||
<div class="well well-sm ellipsis">
|
||||
<strong>BlockHash</strong>
|
||||
<span class="txid text-muted">{{block.hash}}</span>
|
||||
<span class="btn-copy" clip-copy="block.hash"></span>
|
||||
</div>
|
||||
<h2>Summary</h2>
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<table class="table" style="table-layout: fixed">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td><strong>Number Of Transactions</strong></td>
|
||||
<td class="text-right text-muted">{{block.tx.length}}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><strong>Height</strong></td>
|
||||
<td class="text-right text-muted">{{block.height}}
|
||||
<span data-ng-show="block.isMainChain" class="text-success">(Mainchain)</span>
|
||||
<span data-ng-show="!block.isMainChain" class="text-danger"> <span class="glyphicon glyphicon-warning-sign"></span> (Orphaned)</span>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><strong>Block Reward</strong></td>
|
||||
<td class="text-right text-muted">{{$root.currency.getConvertion(block.reward) || block.reward + ' BTC'}}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><strong>Timestamp</strong></td>
|
||||
<td class="text-right text-muted">{{block.time * 1000 | date:'medium'}}</td>
|
||||
</tr>
|
||||
<tr data-ng-show="block.poolInfo">
|
||||
<td><strong>Mined by</strong></td>
|
||||
<td class="text-right text-muted">
|
||||
<a href="{{block.poolInfo.url}}" target="_blank" title="{{block.poolInfo.poolName}}">{{block.poolInfo.poolName}}</a>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><strong>Merkle Root</strong></td>
|
||||
<td class="text-right text-muted">
|
||||
<div class="ellipsis">
|
||||
<span class="btn-copy" clip-copy="block.merkleroot"></span>
|
||||
<span>{{block.merkleroot}}</span>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr data-ng-show="block.previousblockhash">
|
||||
<td><strong>Previous Block</strong></td>
|
||||
<td class="text-right"><a href="/block/{{block.previousblockhash}}">{{block.height-1}}</a></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<table class="table">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td> <strong> Difficulty </strong></td>
|
||||
<td class="text-right text-muted">{{block.difficulty}}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td> <strong> Bits </strong></td>
|
||||
<td class="text-right text-muted">{{block.bits}}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td> <strong> Size (bytes) </strong></td>
|
||||
<td class="text-right text-muted">{{block.size}}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td> <strong> Version </strong></td>
|
||||
<td class="text-right text-muted">{{block.version}}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td> <strong> Nonce </strong></td>
|
||||
<td class="text-right text-muted">{{block.nonce}}</td>
|
||||
</tr>
|
||||
<tr data-ng-show="block.nextblockhash">
|
||||
<td><strong>Next Block</strong></td>
|
||||
<td class="text-right"><a href="/block/{{block.nextblockhash}}">{{block.height+1}}</a></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div data-ng-controller="transactionsController" data-ng-init="load('block')">
|
||||
<h3>Transactions</h3>
|
||||
<div data-ng-include src="'/views/transaction/list.html'" when-scrolled="loadMore()"></div>
|
||||
</div>
|
||||
|
||||
</section>
|
||||
|
|
@ -1,63 +0,0 @@
|
|||
<div data-ng-include src="'/views/includes/connection.html'"></div>
|
||||
<section data-ng-controller="BlocksController" data-ng-init="list()">
|
||||
<div class="row">
|
||||
<div class="col-xs-12 col-gray col-gray-fixed">
|
||||
<div class="block-id">
|
||||
<div class="icon-block text-center">
|
||||
<span class="glyphicon glyphicon-list"></span>
|
||||
<h3>Blocks <br> mined on:</h3>
|
||||
</div>
|
||||
</div>
|
||||
<p class="lead text-center m20v">
|
||||
{{pagination.current}} UTC
|
||||
<a href="#" class="btn btn-primary btn-xs" datepicker-popup show-button-bar="false" data-ng-click="openCalendar($event)" data-ng-model="dt" is-open="opened" data-ng-required="true"><span class="glyphicon glyphicon-calendar"></span></a>
|
||||
</p>
|
||||
<div class="m20v text-center text-muted" data-ng-if="!pagination.current">
|
||||
<span>Loading Selected Date...</span>
|
||||
</div>
|
||||
<div class="ng-cloak" data-ng-cloak data-ng-if="pagination.current">
|
||||
<p class="lead text-center m20v" data-ng-show="loading"> </p>
|
||||
<p class="text-center m20v" data-ng-show="pagination.isToday && !loading">Today</p>
|
||||
<p class="text-center m20v" data-ng-show="!pagination.isToday && !loading">{{humanSince(pagination.currentTs)}}
|
||||
<p class="text-center m20v" data-ng-show="loading"> </p>
|
||||
<div class="m50v text-center">
|
||||
<a class="btn btn-primary" href="/blocks-date/{{pagination.prev}}"><small>← {{pagination.prev}}</small></a>
|
||||
<a class="btn btn-primary" href="/blocks-date/{{pagination.next}}" data-ng-show="!pagination.isToday"><small>{{pagination.next}} →</small></a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-xs-12 col-md-9 col-md-offset-3">
|
||||
<div class="page-header">
|
||||
<h1>
|
||||
Blocks
|
||||
<small>by date</small>
|
||||
</h1>
|
||||
</div>
|
||||
<table class="table table-hover table-striped">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Height</th>
|
||||
<th>Timestamp</th>
|
||||
<th class="text-right">Transactions</th>
|
||||
<th class="text-right">Mined by</th>
|
||||
<th class="text-right">Size</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr data-ng-show="loading">
|
||||
<td colspan="4">Waiting for blocks...</td>
|
||||
</tr>
|
||||
<tr class="fader" data-ng-repeat='b in blocks'>
|
||||
<td><a href="/block/{{b.hash}}">{{b.height}}</a></td>
|
||||
<td>{{b.time * 1000 | date:'medium'}}</td>
|
||||
<td class="text-right">{{b.txlength}}</td>
|
||||
<td class="text-right"><a href="{{b.poolInfo.url}}" title="{{b.poolInfo.poolName}}" target="_blank" data-ng-show="b.poolInfo">{{b.poolInfo.poolName}}</a></td>
|
||||
<td class="text-right">{{b.size}}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
<h2 class="text-center text-muted" data-ng-show="!blocks.length && !loading">No blocks yet.</h2>
|
||||
</section>
|
||||
|
|
@ -1,23 +0,0 @@
|
|||
<div class="connection-status container" data-ng-controller="ConnectionController">
|
||||
|
||||
<div class="alert alert-danger"
|
||||
data-ng-show="!serverOnline || !clienteOnline || !apiOnline"
|
||||
data-ng-init="getConnStatus()">
|
||||
|
||||
<strong>Error!</strong>
|
||||
|
||||
<p data-ng-show="!apiOnline">
|
||||
Can't connect to bitcoind to get live updates from the p2p network.
|
||||
(Tried connecting to bitcoind at {{host}}:{{port}} and failed.)
|
||||
</p>
|
||||
|
||||
<p data-ng-show="!serverOnline">
|
||||
Can't connect to insight server. Attempting to reconnect...
|
||||
</p>
|
||||
|
||||
<p data-ng-show="!clienteOnline">
|
||||
Can't connect to internet. Please, check your connection.
|
||||
</p>
|
||||
|
||||
</div>
|
||||
</div>
|
|
@ -1,15 +0,0 @@
|
|||
<a class="dropdown-toggle" data-toggle="dropdown" href="#">
|
||||
{{currency.symbol}} <span class="caret"></span>
|
||||
</a>
|
||||
<ul class="dropdown-menu">
|
||||
<li>
|
||||
<a href="#" data-ng-click="setCurrency('USD')" data-ng-class="{active: currency.symbol == 'USD'}">USD</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="#" data-ng-click="setCurrency('BTC')" data-ng-class="{active: currency.symbol == 'BTC'}">BTC</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="#" data-ng-click="setCurrency('mBTC')" data-ng-class="{active: currency.symbol == 'mBTC'}">mBTC</a>
|
||||
</li>
|
||||
</ul>
|
||||
|
|
@ -1,3 +0,0 @@
|
|||
<div class="container" data-ng-controller="FooterController">
|
||||
<a class="insight m10v pull-right" href="/">insight <small>API v{{version}}</small></a>
|
||||
</div>
|
|
@ -1,49 +0,0 @@
|
|||
<div class="container">
|
||||
<div data-ng-controller="HeaderController">
|
||||
<div class="navbar-header">
|
||||
<button type="button" class="navbar-toggle" data-toggle="collapse" data-target=".navbar-collapse" data-ng-click="$root.isCollapsed = !$root.isCollapsed">
|
||||
<span class="sr-only">Toggle navigation</span>
|
||||
<span class="icon-bar"></span>
|
||||
<span class="icon-bar"></span>
|
||||
<span class="icon-bar"></span>
|
||||
</button>
|
||||
<a class="insight navbar-brand" href="/">insight</a>
|
||||
</div>
|
||||
<div class="navbar-collapse collapse" collapse="$root.isCollapsed">
|
||||
<ul class="nav navbar-nav">
|
||||
<li data-ng-repeat="item in menu" ui-route="/{{item.link}}" data-ng-class="{active: $uiRoute}">
|
||||
<a href="/{{item.link}}">{{item.title}}</a>
|
||||
</li>
|
||||
</ul>
|
||||
<form data-ng-controller="SearchController" class="navbar-form navbar-left hidden-xs" role="search" data-ng-submit="search()">
|
||||
<div class="form-group" data-ng-class="{'has-error': badQuery}">
|
||||
<input id="search" type="text" class="form-control" data-ng-model="q" data-ng-class="{'loading': loading}" placeholder="Search for block, transaction or address">
|
||||
</div>
|
||||
<div class="no_matching text-danger" data-ng-show="badQuery">No matching records found!</div>
|
||||
</form>
|
||||
<ul class="nav navbar-nav navbar-right">
|
||||
<li>
|
||||
<div class="status" data-ng-controller="StatusController">
|
||||
<div data-ng-init="getSync()" class="pull-left">
|
||||
<span class="t text-danger" data-ng-show="sync.error" tooltip="{{sync.error}}" tooltip-placement="bottom">
|
||||
<span class="glyphicon glyphicon-warning-sign"></span>
|
||||
ERROR
|
||||
</span>
|
||||
<span class="t" tooltip="{{sync.syncedBlocks}} / {{sync.blockChainHeight}} synced. {{sync.skippedBlocks}} skipped" tooltip-placement="bottom" data-ng-show="sync.status==='syncing'">
|
||||
<span class="glyphicon glyphicon-refresh icon-rotate"></span>
|
||||
{{sync.status}} {{sync.syncPercentage}}%
|
||||
</span>
|
||||
<span class="t text-default" tooltip="Historic sync finished" tooltip-placement="bottom" data-ng-show="sync.status==='finished'"> Synched </span>
|
||||
</div>
|
||||
·
|
||||
<span data-ng-init="getStatus('Info')">
|
||||
<strong>Conn</strong> {{info.connections}}
|
||||
</span> ·
|
||||
<strong>Height</strong> {{totalBlocks || info.blocks}}
|
||||
</div>
|
||||
</li>
|
||||
<li class="dropdown" data-ng-controller="CurrencyController" data-ng-include src="'/views/includes/currency.html'"></li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
|
@ -1,4 +0,0 @@
|
|||
<span class="text-warning" data-ng-show="!loaded && !error">Loading...</span>
|
||||
<span class="text-danger" data-ng-show="error">{{error}}</span>
|
||||
|
||||
|
|
@ -1,6 +0,0 @@
|
|||
<form data-ng-controller="SearchController" class="visible-xs" role="search" data-ng-submit="search()">
|
||||
<div class="form-group" data-ng-class="{'has-error': badQuery}">
|
||||
<input id="search" type="text" class="form-control" data-ng-model="q" data-ng-class="{'loading': loading}" placeholder="Search for block, transaction or address">
|
||||
</div>
|
||||
<div class="no_matching text-danger" data-ng-show="badQuery">No matching records found!</div>
|
||||
</form>
|
|
@ -1,83 +0,0 @@
|
|||
<div class="alert alert-danger" data-ng-show="flashMessage">
|
||||
{{$root.flashMessage}}
|
||||
</div>
|
||||
<div data-ng-include src="'/views/includes/connection.html'"></div>
|
||||
<section data-ng-controller="IndexController" data-ng-init="index()">
|
||||
<div class="container">
|
||||
<div id="home" class="row">
|
||||
<div class="col-xs-12 col-md-8">
|
||||
|
||||
<div data-ng-include src="'/views/includes/search.html'"></div>
|
||||
|
||||
<h1>Latest Blocks</h1>
|
||||
<table class="table table-hover table-striped" style="table-layout: fixed">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Height</th>
|
||||
<th>Age</th>
|
||||
<th class="text-right"><span class="ellipsis">Transactions</span></th>
|
||||
<th class="text-right"><span class="ellipsis">Mined by</span></th>
|
||||
<th class="text-right">Size</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr data-ng-show="!blocks.length"><td colspan="4">Waiting for blocks...</td></tr>
|
||||
<tr class="fader" data-ng-repeat='b in blocks'>
|
||||
<td>
|
||||
<a href="/block/{{b.hash}}">{{b.height}}</a>
|
||||
</td>
|
||||
<td><span class="ellipsis">{{humanSince(b.time)}}</span></td>
|
||||
<td class="text-right">{{b.txlength}}</td>
|
||||
<td class="text-right"><a href="{{b.poolInfo.url}}" title="{{b.poolInfo.poolName}}" target="_blank" data-ng-show="b.poolInfo">{{b.poolInfo.poolName}}</a></td>
|
||||
<td class="text-right">{{b.size}} bytes</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<div class="btn-more">
|
||||
<a href="/blocks" class="btn btn-default">See all blocks</a>
|
||||
</div>
|
||||
|
||||
<h2>Latest Transactions</h2>
|
||||
|
||||
<table class="table table-hover table-striped" style="table-layout: fixed;">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Hash</th>
|
||||
<th class="text-right">Size</th>
|
||||
<th class="text-right">Value Out</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr data-ng-show="!txs.length"><td colspan="3">Waiting for transactions...</td></tr>
|
||||
<tr class="fader" data-ng-repeat='tx in txs'>
|
||||
<td>
|
||||
<a class="ellipsis" href="/tx/{{tx.txid}}">{{tx.txid}}</a>
|
||||
</td>
|
||||
<td class="text-right"><span class="ellipsis">{{tx.size}} bytes</span></td>
|
||||
<td class="text-right"><span class="ellipsis">{{tx.valueOut}} BTC</span></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="col-xs-12 col-md-4 col-gray">
|
||||
<h2> About </h2>
|
||||
<p><strong>insight</strong> is an <a href="http://insight.bitcore.io/" target="_blank">open-source Bitcoin blockchain explorer</a> with complete REST
|
||||
and websocket APIs that can be used for writing web wallets and other apps
|
||||
that need more advanced blockchain queries than provided by bitcoind RPC.
|
||||
Check out the <a href="http://github.com/bitpay/insight" target="_blank">source code</a>.</p>
|
||||
<p><strong>insight</strong> is still in development, so be sure to report any bugs and provide feedback for improvement at our <a href="https://github.com/bitpay/insight/issues" target="_blank">github issue tracker</a>.</p>
|
||||
<div id="powered" class="row">
|
||||
<div class="powered-text">
|
||||
<small class="text-muted">Powered by</small>
|
||||
</div>
|
||||
<a href="http://bitcore.io" target="_blank" class="bitcore" title="Bitcore"></a>
|
||||
<a href="http://angularjs.org" target="_blank" class="angularjs" title="AngularJS"></a>
|
||||
<a href="https://code.google.com/p/leveldb/" target="_blank" class="leveldb" title="LevelDB"></a>
|
||||
<a href="http://nodejs.org" target="_blank" class="nodejs" title="NodeJs"></a>
|
||||
</div>
|
||||
</div> <!-- END OF COL-3 -->
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
|
@ -1 +0,0 @@
|
|||
<div class="text-center">Redirecting...</div>
|
|
@ -1,165 +0,0 @@
|
|||
<div data-ng-include src="'/views/includes/connection.html'"></div>
|
||||
<section>
|
||||
<div class="page-header">
|
||||
<h1>
|
||||
Application Status
|
||||
</h1>
|
||||
</div>
|
||||
<div id="status" class="row">
|
||||
|
||||
<div class="col-xs-12 col-md-8">
|
||||
<h2>Sync Status</h2>
|
||||
<table class="table" data-ng-controller="StatusController" data-ng-init="getSync()">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>Sync Progress</td>
|
||||
<td>
|
||||
<div class="progress">
|
||||
<div class="progress-bar progress-bar-info" role="progressbar" aria-valuenow="40" aria-valuemin="0" aria-valuemax="100" style="width: {{ sync.syncPercentage}}%">
|
||||
<span data-ng-show="sync.syncPercentage>0">{{sync.syncPercentage}}% Complete</span>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Current Sync Status</td>
|
||||
<td class="text-right">
|
||||
<span data-ng-show="!sync.error">{{sync.status}}</span>
|
||||
<span class="text-danger" data-ng-show="sync.error">
|
||||
<span class="glyphicon glyphicon-warning-sign"></span>
|
||||
{{sync.error}}
|
||||
</span>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Start Date</td>
|
||||
<td class="text-right"><time title="{{sync.startTs | date:'medium'}}">{{humanSince(sync.startTs)}}</time></td>
|
||||
</tr>
|
||||
<tr data-ng-show="sync.endTs">
|
||||
<td>Finish Date</td>
|
||||
<td class="text-right"><time title="{{sync.startTs | date:'medium'}}" >{{humanSince(sync.endTs)}}</time></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Initial Block Chain Height</td>
|
||||
<td class="text-right">{{sync.blockChainHeight}}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Synced Blocks</td>
|
||||
<td class="text-right">{{sync.syncedBlocks}}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Skipped Blocks (previously synced)</td>
|
||||
<td class="text-right">{{sync.skippedBlocks}}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Sync Type</td>
|
||||
<td class="text-right">{{sync.type}}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<h2>Last Block</h2>
|
||||
<table class="table" style="table-layout: fixed" data-ng-controller="StatusController" data-ng-init="getStatus('LastBlockHash')">
|
||||
<thead data-ng-include src="'/views/includes/infoStatus.html'"></thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>Last Block Hash (Bitcoind)</td>
|
||||
<td class="text-right ellipsis"><a href="/block/{{lastblockhash}}">{{lastblockhash}}</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Current Blockchain Tip (insight)</td>
|
||||
<td class="text-right ellipsis"><a href="/block/{{syncTipHash}}">{{syncTipHash}}</a></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<h2>Transaction Output Set Information</h2>
|
||||
<div data-ng-controller="StatusController">
|
||||
<button data-ng-click="txoutLoading=1;getStatus('TxOutSetInfo')" class="btn btn-default" data-ng-show="!txoutsetinfo.height">
|
||||
Show Transaction Output data
|
||||
<span data-ng-show="txoutLoading" class="glyphicon glyphicon-refresh icon-rotate"></span>
|
||||
</button >
|
||||
|
||||
<table class="table" data-ng-show="txoutsetinfo.height" style="table-layout: fixed" >
|
||||
<thead data-ng-include src="'/views/includes/infoStatus.html'"></thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>Height</td>
|
||||
<td class="text-right"><a href="/block-index/{{txoutsetinfo.height}}">{{txoutsetinfo.height}}</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Best Block</td>
|
||||
<td class="text-right ellipsis"><a href="/block/{{txoutsetinfo.bestblock}}">{{txoutsetinfo.bestblock}}</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Transactions</td>
|
||||
<td class="text-right"> {{txoutsetinfo.transactions}}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Transaction Outputs</td>
|
||||
<td class="text-right">{{txoutsetinfo.txouts}}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Bytes Serialized</td>
|
||||
<td class="text-right">{{txoutsetinfo.bytes_serialized}}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Hash Serialized</td>
|
||||
<td class="text-right ellipsis">{{txoutsetinfo.hash_serialized}}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Total Amount</td>
|
||||
<td class="text-right">{{txoutsetinfo.total_amount}}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div> <!-- END OF COL-8 -->
|
||||
|
||||
<div class="col-xs-12 col-md-4 col-gray">
|
||||
<h2>Bitcoin node information</h2>
|
||||
<table class="table" data-ng-controller="StatusController" data-ng-init="getStatus('Info')">
|
||||
<thead data-ng-include src="'/views/includes/infoStatus.html'"></thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>Version</td>
|
||||
<td class="text-right">{{info.version}}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Protocol version</td>
|
||||
<td class="text-right">{{info.protocolversion}}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Blocks</td>
|
||||
<td class="text-right"><a href="/block-index/{{info.blocks}}">{{info.blocks}}</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Time Offset</td>
|
||||
<td class="text-right">{{info.timeoffset}}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Connections to other nodes</td>
|
||||
<td class="text-right">{{info.connections}}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Mining Difficulty</td>
|
||||
<td class="text-right">{{info.difficulty}}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Testnet</td>
|
||||
<td class="text-right">{{info.testnet}}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Proxy setting</td>
|
||||
<td class="text-right">{{info.proxy}}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Info Errors</td>
|
||||
<td class="text-right">{{info.infoErrors}}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div> <!-- END OF COL-GRAY -->
|
||||
</div>
|
||||
</section>
|
||||
|
|
@ -1,67 +0,0 @@
|
|||
<div data-ng-include src="'/views/includes/connection.html'"></div>
|
||||
<section data-ng-controller="transactionsController" data-ng-init="findThis()">
|
||||
<div class="secondary_navbar hidden-xs hidden-sm" scroll data-ng-class="{'hidden': !secondaryNavbar}" data-ng-show="tx.txid" data-ng-init="hideSNavbar=0">
|
||||
<div class="container" data-ng-if="!hideSNavbar">
|
||||
<div class="col-md-8 text-left">
|
||||
<h3>Transaction</h3> {{tx.txid}}
|
||||
<span class="btn-copy" clip-copy="tx.txid"></span>
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<span data-ng-show="tx.confirmations" class="txvalues txvalues-success">{{tx.confirmations}} Confirmations</span>
|
||||
<span data-ng-show="!tx.confirmations" class="txvalues txvalues-danger">Unconfirmed Transaction!</span>
|
||||
<span class="txvalues txvalues-primary">{{$root.currency.getConvertion(tx.valueOut) || tx.valueOut + ' BTC' }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="hide_snavbar">
|
||||
<a href="#" data-ng-click="hideSNavbar=!hideSNavbar">
|
||||
<span data-ng-show="hideSNavbar"><span class="text-muted glyphicon glyphicon-chevron-down"></span></span>
|
||||
<span data-ng-show="!hideSNavbar"><span class="text-muted glyphicon glyphicon-chevron-up"></span></span>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="ng-cloak" data-ng-cloak data-ng-if="tx.txid">
|
||||
<h1>Transaction
|
||||
<small data-ng-show="from_vin || from_vout">
|
||||
<span data-ng-show="from_vin">Input</span>
|
||||
<span data-ng-show="from_vout">Output</span>
|
||||
<span>{{v_index}}</span>
|
||||
</small>
|
||||
</h1>
|
||||
<div class="progress progress-striped active" data-ng-if="!tx.txid">
|
||||
<div class="progress-bar progress-bar-info" style="width: 100%">
|
||||
<span>Loading Transaction Details...</span>
|
||||
</div>
|
||||
</div>
|
||||
<div data-ng-if="tx.txid">
|
||||
<div class="well well-sm ellipsis">
|
||||
<strong>Transaction</strong>
|
||||
<span class="txid text-muted">{{tx.txid}}</span>
|
||||
<span class="btn-copy" clip-copy="tx.txid"></span>
|
||||
</div>
|
||||
<h2>Summary</h2>
|
||||
<table class="table" style="table-layout: fixed">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td><strong> Size </strong></td>
|
||||
<td class="text-muted text-right">{{tx.size}} (bytes)</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><strong>Received Time </strong></td>
|
||||
<td data-ng-show="tx.firstSeenTs" class="text-muted text-right">{{tx.firstSeenTs * 1000|date:'medium'}}</td>
|
||||
<td data-ng-show="!tx.firstSeenTs" class="text-muted text-right">N/A</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><strong>Mined Time </strong></td>
|
||||
<td data-ng-show="tx.time" class="text-muted text-right">{{tx.time * 1000|date:'medium'}}</td>
|
||||
<td data-ng-show="!tx.time" class="text-muted text-right">N/A</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<h2>Details</h2>
|
||||
<div class="block-tx ng-cloak" data-ng-cloak data-ng-if="tx.txid">
|
||||
<div data-ng-include src="'/views/transaction/tx.html'"></div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
|
@ -1,9 +0,0 @@
|
|||
<div class="alert alert-warning" data-ng-show="!txs[0].txid && !loading">There are not transactions involving this address.</div>
|
||||
<div class="block-tx" data-ng-show="txs && txs[0].txid" data-ng-repeat="tx in txs">
|
||||
<div data-ng-include src="'/views/transaction/tx.html'"></div>
|
||||
</div>
|
||||
<div class="progress progress-striped active" data-ng-show="loading">
|
||||
<div class="progress-bar progress-bar-info" style="width: 100%">
|
||||
<span>Loading Transactions...</span>
|
||||
</div>
|
||||
</div>
|
|
@ -1,184 +0,0 @@
|
|||
<div class="line-bot row ng-cloak" data-ng-hide="!tx" data-ng-cloak>
|
||||
<div class="col-xs-12 col-md-6">
|
||||
<div class="ellipsis">
|
||||
<a class="btn-expand" href="#" title="Show/Hide items details" data-ng-click="itemsExpanded = !itemsExpanded">
|
||||
<span class="glyphicon glyphicon-plus-sign" data-ng-class="{'glyphicon-minus-sign': itemsExpanded}"></span>
|
||||
</a>
|
||||
<a class="txid" href="/tx/{{tx.txid}}">{{tx.txid}}</a>
|
||||
<span class="btn-copy" clip-copy="tx.txid"></span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-xs-12 col-md-6 text-right">
|
||||
<div data-ng-show="tx.firstSeenTs">
|
||||
first seen at
|
||||
<time>{{tx.firstSeenTs * 1000 | date:'medium'}}</time>
|
||||
</div>
|
||||
<div data-ng-show="tx.time && !tx.firstSeenTs">
|
||||
mined at
|
||||
<time>{{tx.time * 1000 | date:'medium'}}</time>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row line-mid">
|
||||
<div class="col-md-5">
|
||||
<div class="row" data-ng-if="tx.isCoinBase">
|
||||
<div class="col-md-12 transaction-vin-vout" data-ng-repeat="vin in tx.vin">
|
||||
<div class="ellipsis">
|
||||
<span>No Inputs (Newly Generated Coins)</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row" data-ng-if="!tx.isCoinBase">
|
||||
|
||||
<!-- Simple view -->
|
||||
<div data-ng-if="!itemsExpanded" data-ng-init="currentInNoExpanded=0; sizeInNoExpanded=5">
|
||||
<div data-ng-repeat="vin in tx.vinSimple| startFrom:currentInNoExpanded*sizeInNoExpanded | limitTo:sizeInNoExpanded">
|
||||
<div class="col-md-12 transaction-vin-vout">
|
||||
<div class="pull-right btc-value" data-ng-class="{'text-danger': $root.currentAddr == vin.addr}">
|
||||
<p>{{$root.currency.getConvertion(vin.value) || vin.value + ' BTC'}}</p>
|
||||
</div>
|
||||
<div class="ellipsis">
|
||||
<span data-ng-show="vin.notAddr">{{vin.addr}}</span>
|
||||
<span class="text-muted" title="Current Bitcoin Address" data-ng-show="vin.addr == $root.currentAddr">{{vin.addr}}</span>
|
||||
<a href="/address/{{vin.addr}}" data-ng-show="!vin.notAddr && vin.addr != $root.currentAddr">{{vin.addr}}</a>
|
||||
</div>
|
||||
<div data-ng-show="vin.unconfirmedInput" class="text-danger"> <span class="glyphicon glyphicon-warning-sign"></span> (Input unconfirmed)</div>
|
||||
<div data-ng-show="vin.dbError" class="text-danger"> <span class="glyphicon glyphicon-warning-sign"></span> Incoherence in levelDB detected, please resync</div>
|
||||
<div data-ng-show="vin.doubleSpentTxID" class="text-danger"> <span class="glyphicon glyphicon-warning-sign"></span> Double spent attempt detected. From tx:
|
||||
<a href="/tx/{{vin.doubleSpentTxID}}">{{vin.doubleSpentTxID}},{{vin.doubleSpentIndex}}</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="showmore_collapse text-right" data-ng-show="tx.vinSimple.length > 5" data-ng-class="{ 'hidden': itemsExpanded}">
|
||||
<a href="#" ng-hide="sizeInNoExpanded != tx.vinSimple.length" ng-click="currentInNoExpanded=0; sizeInNoExpanded=5"><small>...less</small></a>
|
||||
<a href="#" ng-hide="currentInNoExpanded >= tx.vinSimple.length/sizeInNoExpanded - 1" ng-click="currentInNoExpanded=0; sizeInNoExpanded=tx.vinSimple.length"><small>more...</small></a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Full view -->
|
||||
<div data-ng-if="itemsExpanded" data-ng-init="currentInExpanded=0; sizeInExpanded=(from_vin) ? tx.vin.length : 5; fromVinCollapsed=(from_vin)">
|
||||
<a href="#" data-ng-show="(from_vin) && tx.vin.length > 1" data-ng-class="{'text-muted': fromVinCollapsed}" data-ng-click="currentInExpanded=0; sizeInExpanded=tx.vin.length;fromVinCollapsed=1"><small>show input {{ v_index }}</small></a>
|
||||
<a href="#" data-ng-show="(from_vin) && tx.vin.length > 1" data-ng-class="{'text-muted': !fromVinCollapsed}" data-ng-click="currentInExpanded=0; sizeInExpanded=tx.vin.length;fromVinCollapsed=0"><small>show all</small></a>
|
||||
<div data-ng-repeat="vin in tx.vin| startFrom:currentInExpanded*sizeInExpanded | limitTo:sizeInExpanded" data-ng-if="fromVinCollapsed ? v_index == vin.n : 1">
|
||||
<div class="col-md-12 transaction-vin-vout">
|
||||
<div class="pull-right btc-value"><p>{{$root.currency.getConvertion(vin.value) || vin.value + ' BTC'}}</p></div>
|
||||
<div class="ellipsis">
|
||||
<a class="glyphicon glyphicon-chevron-right" href="/tx/{{vin.txid}}/>/{{vin.vout}}" title="Outpoint: {{vin.txid}},{{vin.vout}}"></a>
|
||||
<span data-ng-show="vin.notAddr">{{vin.addr}}</span>
|
||||
<a href="/address/{{vin.addr}}" data-ng-show="!vin.notAddr">{{vin.addr}}</a>
|
||||
</div>
|
||||
<div data-ng-show="vin.unconfirmedInput" class="text-danger"> <span class="glyphicon glyphicon-warning-sign"></span> (Input unconfirmed)</div>
|
||||
<div data-ng-show="vin.dbError" class="text-danger"> <span class="glyphicon glyphicon-warning-sign"></span> Incoherence in levelDB detected, please resync</div>
|
||||
<div data-ng-show="vin.doubleSpentTxID" class="text-danger"> <span class="glyphicon glyphicon-warning-sign"></span> Double spent attempt detected. From tx:
|
||||
<a href="/tx/{{vin.doubleSpentTxID}}">{{vin.doubleSpentTxID}},{{vin.doubleSpentIndex}}</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-12">
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-body" style="word-wrap:break-word" data-ng-class="{true: 'v_highlight', false: ''}[from_vin == true && v_index == vin.n]">
|
||||
<small>
|
||||
<strong>scriptSig</strong>
|
||||
{{vin.scriptSig.asm}}
|
||||
</small>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="showmore_collapse text-right" data-ng-show="tx.vin.length > 5 && !fromVinCollapsed" data-ng-class="{ 'hidden': !itemsExpanded}">
|
||||
<a href="#" ng-hide="sizeInExpanded != tx.vin.length" ng-click="currentInExpanded=0; sizeInExpanded=5"><small>...less</small></a>
|
||||
<a href="#" data-ng-class="{true: 'v_highlight_more', false: ''}[from_vin == true && v_index > 5]" ng-hide="currentInExpanded >= tx.vin.length/sizeInExpanded - 1" ng-click="currentInExpanded=0; sizeInExpanded=tx.vin.length"><small>more...</small></a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-1 col-xs-12">
|
||||
<div class="hidden-xs hidden-sm text-center">
|
||||
<span class="glyphicon glyphicon-chevron-right text-primary"></span>
|
||||
</div>
|
||||
<div class="hidden-md hidden-lg text-center">
|
||||
<span class="glyphicon glyphicon-chevron-down text-primary"></span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<div class="row">
|
||||
|
||||
<!-- Simple view -->
|
||||
<div data-ng-if="!itemsExpanded" data-ng-init="currentOutNoExpanded=0; sizeOutNoExpanded=5">
|
||||
<div data-ng-repeat="vout in tx.voutSimple| startFrom:currentOutNoExpanded*sizeOutNoExpanded | limitTo:sizeOutNoExpanded">
|
||||
<div class="col-md-12 transaction-vin-vout">
|
||||
<div class="pull-right btc-value" data-ng-class="{'text-success': $root.currentAddr == vout.addr}">
|
||||
{{$root.currency.getConvertion(vout.value) || vout.value + ' BTC' }}
|
||||
<span class="text-danger" data-ng-show="vout.isSpent" tooltip="Output is spent" tooltip-placement="left">(S)</span>
|
||||
<span class="text-success" data-ng-show="!vout.isSpent" tooltip="Output is unspent" tooltip-placement="left">(U)</span>
|
||||
</div>
|
||||
|
||||
<div class="ellipsis">
|
||||
<span data-ng-show="vout.notAddr">{{vout.addr}}</span>
|
||||
<span class="text-muted" title="Current Bitcoin Address" data-ng-show="address == $root.currentAddr" data-ng-repeat="address in vout.addr.split(',')">{{vout.addr}}</span>
|
||||
<a href="/address/{{address}}" data-ng-show="!vout.notAddr && address != $root.currentAddr" data-ng-repeat="address in vout.addr.split(',')">{{address}}</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="showmore_collapse text-right" data-ng-show="tx.voutSimple.length > 5" data-ng-class="{ 'hidden': itemsExpanded}">
|
||||
<a href="#" ng-hide="sizeOutNoExpanded != tx.voutSimple.length" ng-click="currentOutNoExpanded=0; sizeOutNoExpanded=5"><small>...less</small></a>
|
||||
<a href="#" ng-hide="currentOutNoExpanded >= tx.voutSimple.length/sizeOutNoExpanded - 1" ng-click="currentOutNoExpanded=0; sizeOutNoExpanded=tx.voutSimple.length"><small>more...</small></a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Full view -->
|
||||
<div data-ng-if="itemsExpanded" data-ng-init="currentOutExpanded=0; sizeOutExpanded=(from_vout) ? tx.vout.length : 5; fromVoutCollapsed=(from_vout)">
|
||||
<a href="#" data-ng-show="(from_vout) && tx.vout.length > 1" data-ng-class="{'text-muted': fromVoutCollapsed}" data-ng-click="currentOutExpanded=0; sizeOutExpanded=tx.vout.length;fromVoutCollapsed=1"><small>show output {{ v_index }}</small></a>
|
||||
<a href="#" data-ng-show="(from_vout) && tx.vout.length > 1" data-ng-class="{'text-muted': !fromVoutCollapsed}" data-ng-click="currentOutExpanded=0; sizeOutExpanded=tx.vout.length;fromVoutCollapsed=0"><small>show all</small></a>
|
||||
<div data-ng-repeat="vout in tx.vout| startFrom:currentOutExpanded*sizeOutExpanded | limitTo:sizeOutExpanded" data-ng-if="fromVoutCollapsed ? v_index == vout.n : 1">
|
||||
<div class="col-md-12 transaction-vin-vout">
|
||||
<div class="pull-right btc-value">
|
||||
<p>{{$root.currency.getConvertion(vout.value) || vout.value + ' BTC'}}
|
||||
<span class="text-success" data-ng-show="!vout.spentTxId" tooltip="Output is unspent" tooltip-placement="left">(U)</span>
|
||||
<a class="glyphicon glyphicon-chevron-right" data-ng-show="vout.spentTxId" href="/tx/{{vout.spentTxId}}/</{{vout.spentIndex}}" title="Spent at: {{vout.spentTxId}},{{vout.spentIndex}}"></a>
|
||||
</p>
|
||||
</div>
|
||||
<div class="ellipsis">
|
||||
<a href="/address/{{address}}" data-ng-repeat="address in vout.scriptPubKey.addresses">{{address}}</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-12">
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-body" style="word-wrap:break-word" data-ng-class="{true: 'v_highlight', false: ''}[from_vout == true && v_index == vout.n]">
|
||||
<small>
|
||||
<p>
|
||||
<strong>Type</strong>
|
||||
{{vout.scriptPubKey.type}}
|
||||
</p>
|
||||
<p>
|
||||
<strong>scriptPubKey</strong>
|
||||
{{vout.scriptPubKey.asm}}
|
||||
</p>
|
||||
</small>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="showmore_collapse text-right" data-ng-show="tx.vout.length > 5 && !fromVoutCollapsed" data-ng-class="{ 'hidden': !itemsExpanded}">
|
||||
<a href="#" ng-hide="sizeOutExpanded != tx.vout.length" ng-click="currentOutExpanded=0; sizeOutExpanded=5"><small>...less</small></a>
|
||||
<a href="#" data-ng-class="{true: 'v_highlight_more', false: ''}[from_vout == true && v_index > 5]" ng-hide="currentOutExpanded >= tx.vout.length/sizeOutExpanded - 1" ng-click="currentOutExpanded=0; sizeOutExpanded=tx.vout.length"><small>more...</small></a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="well well-sm bgwhite ellipsis" data-ng-if="itemsExpanded && !block.hash">
|
||||
<strong>BlockHash</strong> <a class="text-muted" href="/block/{{tx.blockhash}}">{{tx.blockhash}}</a>
|
||||
<span class="btn-copy" clip-copy="tx.blockhash"></span>
|
||||
</div>
|
||||
|
||||
<div class="line-top row ng-cloak" data-ng-hide="!tx" data-ng-cloak>
|
||||
<div class="col-xs-12 col-sm-4 col-md-4">
|
||||
<span data-ng-show="!tx.isCoinBase && !isNaN(parseFloat(tx.fees))" class="txvalues txvalues-default">Fees: {{$root.currency.getConvertion(tx.fees) || tx.fees + 'BTC'}} </span>
|
||||
</div>
|
||||
<div class="col-xs-12 col-sm-8 col-md-8 text-right">
|
||||
<span data-ng-show="tx.confirmations" class="txvalues txvalues-success">{{tx.confirmations}} Confirmations</span>
|
||||
<span data-ng-show="!tx.confirmations" class="txvalues txvalues-danger">Unconfirmed Transaction!</span>
|
||||
<span class="txvalues txvalues-primary">{{$root.currency.getConvertion(tx.valueOut) || tx.valueOut + ' BTC' }}</span>
|
||||
</div>
|
||||
</div>
|
Loading…
Reference in New Issue