separated insight API from the front-end

This commit is contained in:
Mario Colque 2014-02-25 01:44:19 -03:00
parent 94249a214d
commit c28dd990b0
71 changed files with 51 additions and 2778 deletions

View File

@ -1,3 +0,0 @@
{
"directory": "public/lib"
}

View File

@ -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'],
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']);

View File

@ -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);

View File

@ -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 });
};

View File

@ -22,7 +22,7 @@ module.exports.broadcastTx = function(tx) {
t = {
txid: tx
};
}
}
else {
t = {

View File

@ -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 });
}

View File

@ -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

View File

@ -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

View File

@ -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')

View File

@ -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();

View File

@ -1 +0,0 @@
.navbar.navbar-default.navbar-fixed-top(data-ng-include="'/views/includes/header.html'", role='navigation')

View File

@ -1,4 +0,0 @@
extends layouts/default
block content
section.container(data-ng-view)

View File

@ -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

View File

@ -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"
}
}

View File

@ -44,11 +44,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 += process.env.INSIGHT_NETWORK === 'testnet' ? 'testnet3' : '';
module.exports = {
root: rootPath,
appName: 'Insight ' + env,
apiPrefix: '/api',
port: port,
leveldb: db,
bitcoind: {

View File

@ -21,10 +21,6 @@ module.exports = function(app, historicSync, peerSync) {
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);
@ -63,14 +59,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'
});

View File

@ -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);
};

View File

@ -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);
}
});
};

View File

@ -29,7 +29,7 @@ function spec(b) {
if (buffertools.indexOf(buffer, k) >= 0) {
return self.strings[k];
}
};
}
};
return PoolMatch;

View File

@ -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;

View File

@ -1,5 +1,5 @@
{
"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": {
@ -59,8 +59,6 @@
"commander": "*",
"bignum": "*",
"express": "~3.4.7",
"jade": "~1.0.2",
"lodash": "~2.4.1",
"buffertools": "*",
"should": "~2.1.1",
"view-helpers": "latest",
@ -73,7 +71,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 +79,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

View File

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.

View File

@ -1,682 +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;
}
.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 {
}
.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;
border: 1px solid #eee;
padding: 10px;
text-align: center;
z-index: 1000;
}

View File

@ -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', []);

View File

@ -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;
});
});

View File

@ -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;
});

View File

@ -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;
});

View File

@ -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);
});

View File

@ -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;
});
});

View File

@ -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();
});

View File

@ -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;
});

View File

@ -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 = [];
});

View File

@ -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();
}
});
});
});
};
});

View File

@ -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);
});
});
});

View File

@ -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);
});
});

View File

@ -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();
});
}
};
});

View File

@ -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);
}
});

View File

@ -1,6 +0,0 @@
'use strict';
angular.element(document).ready(function() {
// Init the app
// angular.bootstrap(document, ['insight']);
});

View File

@ -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;
}
}
}
}
});
});

View File

@ -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');
});

View File

@ -1,6 +0,0 @@
'use strict';
angular.module('insight.currency').factory('Currency',
function($resource) {
return $resource('/api/currency');
});

View File

@ -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');
});

View File

@ -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;
};
});

View File

@ -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');
});

View File

@ -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');
});

View File

@ -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>

View File

@ -1,76 +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" scroll data-ng-class="{'hidden': !secondaryNavbar}" data-ng-show="address.addrStr">
<div class="row">
<div class="col-md-8 text-center">
<span class="ellipsis txvalues txvalues-normal">
<strong>Address</strong> {{address.addrStr}}
<span class="btn-copy" clip-copy="address.addrStr"></span>
</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>
<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>

View File

@ -1,119 +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" scroll data-ng-class="{'hidden': !secondaryNavbar}">
<div class="row">
<div class="col-md-2 text-right">
<span class="ellipsis txvalues txvalues-normal">
<a href="/block/{{block.previousblockhash}}">&larr; Previous Block</a>
</span>
</div>
<div class="col-md-8 text-center">
<div class="ellipsis txvalues txvalues-normal">
<strong>Block</strong> #{{block.height}} &middot;
<strong>Hash</strong> {{block.hash}}
<span class="btn-copy" clip-copy="block.hash"></span>
</div>
</div>
<div class="col-md-2 text-left">
<span class="ellipsis txvalues txvalues-normal">
<a href="/block/{{block.nextblockhash}}">Next Block &rarr;</a>
</span>
</div>
</div>
</div>
<h1>Block #{{block.height}}</h1>
<div class="progress progress-striped active" data-ng-if="!block.hash">
<div class="progress-bar progress-bar-info" style="width: 100%">
<span>Loading Block...</span>
</div>
</div>
<div class="row 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="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><!-- END OF ROW -->
<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>

View File

@ -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">&nbsp;</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">&nbsp;</p>
<div class="m50v text-center">
<a class="btn btn-primary" href="/blocks-date/{{pagination.prev}}"><small>&larr; {{pagination.prev}}</small></a>
<a class="btn btn-primary" href="/blocks-date/{{pagination.next}}" data-ng-show="!pagination.isToday"><small>{{pagination.next}} &rarr;</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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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" 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>
&nbsp; &middot;
<span data-ng-init="getStatus('Info')">
<strong>Conn</strong> {{info.connections}}
</span> &middot;
<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>

View File

@ -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>

View File

@ -1,80 +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">
<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>

View File

@ -1 +0,0 @@
<div class="text-center">Redirecting...</div>

View File

@ -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>

View File

@ -1,63 +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" scroll data-ng-class="{'hidden': !secondaryNavbar}">
<div class="row">
<div class="col-md-8 text-center">
<span class="ellipsis txvalues txvalues-normal">
<strong>Transaction</strong> {{tx.txid}}
<span class="btn-copy" clip-copy="tx.txid"></span>
</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>
<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>

View File

@ -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>

View File

@ -1,185 +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>&nbsp;&nbsp;
<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}">
<p>
{{$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>
</p>
</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>&nbsp;&nbsp;
</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>