Merge remote-tracking branch 'upstream/next' into next-merge

This commit is contained in:
tenthirtyone 2017-08-21 10:19:52 -04:00
commit e3d6ef0039
50 changed files with 1158 additions and 261 deletions

3
.gitignore vendored
View File

@ -1,3 +1,4 @@
node_modules
package-lock.json
*.log
*.log
.DS_Store

View File

@ -7,17 +7,13 @@ describe('InsightApp', () => {
});
it('should have a title', () => {
expect(browser.getTitle()).toEqual('Blocks');
expect(browser.getTitle()).toEqual('Home');
});
it('should have {nav}', () => {
expect(element(by.css('ion-navbar')).isPresent()).toEqual(true);
});
it('should have correct nav text for Home', () => {
expect(element(by.css('ion-navbar:first-child')).getText()).toContain('Blocks');
});
it('has a menu button that displays the left menu', () => {
element(by.css('.bar-button-menutoggle')).click()
.then(() => {
@ -26,11 +22,11 @@ describe('InsightApp', () => {
});
});
it('the left menu has a link with title Blocks', () => {
it('the left menu has a link with title Home', () => {
element(by.css('.bar-button-menutoggle')).click()
.then(() => {
browser.driver.sleep(2000); // wait for the animation
expect(element.all(by.css('ion-label')).first().getText()).toEqual('Blocks');
expect(element.all(by.css('ion-label')).first().getText()).toEqual('Home');
});
});
});

View File

@ -10,7 +10,7 @@ describe('BroadcastTxPage', () => {
element(by.css('.bar-button-menutoggle')).click().then(() => {
browser.driver.sleep(2000); // wait for the animation
element.all(by.className('input-wrapper')).then((items) => {
items[1].click();
items[2].click();
browser.driver.sleep(2000); // wait for the animation
let theElem = element.all(by.css('ion-label')).first;
console.log(theElem);

View File

@ -1,5 +1,8 @@
{
"name": "insight-ui",
"app_id": "",
"type": "ionic-angular"
"type": "ionic-angular",
"integrations": {
"cordova": {}
}
}

View File

@ -27,6 +27,7 @@
"@ionic-native/splash-screen": "3.10.2",
"@ionic-native/status-bar": "3.10.2",
"@ionic/storage": "2.0.1",
"angular2-moment": "^1.6.0",
"angular2-qrcode": "^2.0.1",
"ionic-angular": "3.4.2",
"ionicons": "3.0.0",
@ -42,7 +43,7 @@
"@types/jasmine": "2.5.41",
"@types/node": "7.0.4",
"codecov": "2.2.0",
"ionic": "3.7.0",
"ionic": "3.9.1",
"jasmine-core": "2.5.2",
"jasmine-spec-reporter": "3.2.0",
"karma": "1.4.1",

View File

@ -3,6 +3,7 @@ import { Platform, MenuController, Nav } from 'ionic-angular';
import { StatusBar } from '@ionic-native/status-bar';
import { SplashScreen } from '@ionic-native/splash-screen';
import {
HomePage,
BlocksPage,
BroadcastTxPage,
NodeStatusPage,
@ -31,11 +32,12 @@ export class InsightApp {
this.splash = splash;
this.status = status;
this.rootPage = BlocksPage;
this.rootPage = HomePage;
this.initializeApp();
// set our app's pages
this.pages = [
{ title: 'Home', component: HomePage },
{ title: 'Blocks', component: BlocksPage },
{ title: 'Broadcast Transaction', component: BroadcastTxPage },
{ title: 'Verify Signed Message', component: VerifyMessagePage },

View File

@ -5,9 +5,11 @@ import { IonicApp, IonicModule, IonicErrorHandler } from 'ionic-angular';
import { StatusBar } from '@ionic-native/status-bar';
import { SplashScreen } from '@ionic-native/splash-screen';
import { InsightApp } from './app.component';
import { PagesModule, BlocksPage, BroadcastTxPage, NodeStatusPage, VerifyMessagePage } from '../pages';
import { PagesModule, HomePage, BlocksPage, BroadcastTxPage, NodeStatusPage, VerifyMessagePage } from '../pages';
import { BlocksService, StorageService } from '../services';
import { ApiProvider } from '../providers/api/api';
import { CurrencyProvider } from '../providers/currency/currency';
import { BlocksProvider } from '../providers/blocks/blocks';
@NgModule({
declarations: [
@ -22,6 +24,7 @@ import { ApiProvider } from '../providers/api/api';
bootstrap: [IonicApp],
entryComponents: [
InsightApp,
HomePage,
BlocksPage,
BroadcastTxPage,
NodeStatusPage,
@ -33,7 +36,9 @@ import { ApiProvider } from '../providers/api/api';
StorageService,
BlocksService,
{provide: ErrorHandler, useClass: IonicErrorHandler},
ApiProvider
ApiProvider,
CurrencyProvider,
BlocksProvider
]
})

View File

@ -15,6 +15,20 @@
white-space: nowrap;
}
.summary ion-label {
color: #333;
font-weight: bold;
}
.summary ion-item {
color: #999;
font-size: 1.4rem;
}
body {
user-select: text;
}
// Shared Sass variables, which can be used to adjust Ionic's
// default Sass variables, belong in "theme/variables.scss".
//

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

View File

@ -4,9 +4,12 @@
</button>
<ion-title>{{title}}</ion-title>
<ion-buttons end>
<button ion-button icon-only>
<button ion-button (click)="changeCurrency()">
<ion-icon name="logo-bitcoin" *ngIf="currency.currencySymbol !== 'USD'"></ion-icon><ion-icon name="logo-usd" *ngIf="currency.currencySymbol === 'USD'"></ion-icon>&nbsp;{{ currency.currencySymbol }}
</button>
<button ion-button icon-only (click)="toggleSearch()">
<ion-icon name="search"></ion-icon>
</button>
</ion-buttons>
</ion-navbar>
<ion-searchbar (ionInput)="search($event)" placeholder="{{ 'Search for block, transaction or address' }}" [(ngModel)]="q" [debounce]="1000"></ion-searchbar>
<ion-searchbar [hidden]="!showSearch" (ionInput)="search($event)" placeholder="{{ 'Search for block, transaction or address' }}" [(ngModel)]="q" [debounce]="1500"></ion-searchbar>

View File

@ -4,10 +4,10 @@ import { HeadNavComponent } from './head-nav';
@NgModule({
declarations: [
HeadNavComponent,
HeadNavComponent
],
imports: [
IonicModule,
IonicModule
],
exports: [
HeadNavComponent

View File

@ -3,6 +3,8 @@ import { Input } from '@angular/core';
import { NavController } from 'ionic-angular';
import { Http } from '@angular/http';
import { ApiProvider } from '../../providers/api/api';
import { CurrencyProvider } from '../../providers/currency/currency';
import { ActionSheetController } from 'ionic-angular';
/**
* Generated class for the HeadNavComponent component.
@ -16,24 +18,21 @@ import { ApiProvider } from '../../providers/api/api';
})
export class HeadNavComponent {
public showSearch: boolean = false;
public loading: boolean;
@Input() public title: string;
public q: string;
public badQuery: boolean = false;
constructor(private navCtrl: NavController, private http: Http, private api: ApiProvider) {
}
private resetSearch(): void {
this.q = '';
this.loading = false;
constructor(private navCtrl: NavController, private http: Http, private api: ApiProvider, public currency: CurrencyProvider, public actionSheetCtrl: ActionSheetController) {
}
public search(): void {
this.showSearch = false;
let apiPrefix: string = this.api.apiPrefix;
this.http.get(apiPrefix + 'block/' + this.q).subscribe(
function (data: any) {
function (data: any): void {
this.resetSearch();
console.log('block', data);
let parsedData: any = JSON.parse(data._body);
@ -43,7 +42,7 @@ export class HeadNavComponent {
}.bind(this),
() => {
this.http.get(apiPrefix + 'tx/' + this.q).subscribe(
function (data: any) {
function (data: any): void {
this.resetSearch();
console.log('tx', data);
let parsedData: any = JSON.parse(data._body);
@ -53,7 +52,7 @@ export class HeadNavComponent {
}.bind(this),
() => {
this.http.get(apiPrefix + 'addr/' + this.q).subscribe(
function (data: any) {
function (data: any): void {
this.resetSearch();
console.log('addr', data);
let parsedData: any = JSON.parse(data._body);
@ -96,6 +95,51 @@ export class HeadNavComponent {
2000
);
};
private resetSearch(): void {
this.q = '';
this.loading = false;
}
/* tslint:enable:no-unused-variable */
public changeCurrency(): void {
let actionSheet: any = this.actionSheetCtrl.create({
title: 'Change Denomination',
buttons: [
{
text: 'USD',
handler: () => {
this.currency.setCurrency('USD');
}
},
{
text: 'BTC',
handler: () => {
this.currency.setCurrency('BTC');
}
},
{
text: 'mBTC',
handler: () => {
this.currency.setCurrency('mBTC');
}
},
{
text: 'bits',
handler: () => {
this.currency.setCurrency('bits');
}
},
{
text: 'Cancel',
role: 'cancel'
}
]
});
actionSheet.present();
}
public toggleSearch(): void {
this.showSearch = !this.showSearch;
}
}

View File

@ -0,0 +1,45 @@
<!-- Generated template for the LatestBlocksComponent component -->
<div>
<div *ngIf="loading">
<ion-spinner name="crescent"></ion-spinner>
</div>
<div *ngIf="!loading">
<ion-grid>
<ion-row>
<ion-col><b>Height</b></ion-col>
<ion-col *ngIf="showTimeAs === 'age'"><b>Age</b></ion-col>
<ion-col *ngIf="showTimeAs === 'timestamp'"><b>Timestamp</b></ion-col>
<ion-col text-right><b>Transactions</b></ion-col>
<ion-col hideWhen="portrait"><b>Mined By</b></ion-col>
<ion-col text-right><b>Size</b></ion-col>
</ion-row>
<ion-row *ngFor="let block of getBlocks()">
<ion-col>
<a (click)="goToBlock(block.hash)">{{block.height}}</a>
</ion-col>
<ion-col *ngIf="showTimeAs === 'age'">
{{ block.time | amFromUnix | amTimeAgo }}
</ion-col>
<ion-col *ngIf="showTimeAs === 'timestamp'">
{{ block.time * 1000 | date:'medium' }}
</ion-col>
<ion-col text-right>
{{block.txlength}}
</ion-col>
<ion-col hideWhen="portrait">
<a *ngIf="block.poolInfo.poolName" href="{{block.poolInfo.url}}">{{block.poolInfo.poolName}}</a>
</ion-col>
<ion-col text-right>
{{ block.size }}
</ion-col>
</ion-row>
<ion-row *ngIf="showAllBlocksButton">
<ion-col text-center>
<button ion-button (click)="goToBlocks()">See all blocks</button>
</ion-col>
</ion-row>
</ion-grid>
</div>
</div>

View File

@ -0,0 +1,18 @@
import { NgModule } from '@angular/core';
import { IonicModule } from 'ionic-angular';
import { LatestBlocksComponent } from './latest-blocks';
import { MomentModule } from 'angular2-moment';
@NgModule({
declarations: [
LatestBlocksComponent
],
imports: [
IonicModule,
MomentModule
],
exports: [
LatestBlocksComponent
]
})
export class LatestBlocksComponentModule {}

View File

@ -0,0 +1,23 @@
latest-blocks {
ion-grid {
// border: 2px solid green;
margin: 10px 0 20px;
ion-row {
border-top: 1px solid #ccc;
}
ion-row:nth-child(even) {
background-color: #f4f4f4;
}
ion-row:first-child {
background-color: white;
border-top: none;
}
ion-row:last-child {
background-color: white;
}
}
}

View File

@ -0,0 +1,65 @@
import { Component, NgZone, Input } from '@angular/core';
import { BlocksProvider } from '../../providers/blocks/blocks';
import { NavController } from 'ionic-angular';
/**
* Generated class for the LatestBlocksComponent component.
*
* See https://angular.io/docs/ts/latest/api/core/index/ComponentMetadata-class.html
* for more info on Angular Components.
*/
@Component({
selector: 'latest-blocks',
templateUrl: 'latest-blocks.html'
})
export class LatestBlocksComponent {
public loading: boolean = true;
public blocks: Array<any> = [];
@Input() public numBlocks: number;
@Input() public showAllBlocksButton: boolean;
@Input() public showTimeAs: string;
constructor(private blocksProvider: BlocksProvider, private navCtrl: NavController, ngZone: NgZone) {
this.loadBlocks();
ngZone.runOutsideAngular(() => {
setInterval(
function (): void {
ngZone.run(function (): void {
this.loadBlocks.call(this);
}.bind(this));
}.bind(this),
1000 * 30
);
});
}
private loadBlocks(): void {
this.blocksProvider.getBlocks().subscribe(
(data) => {
this.blocks = JSON.parse(data['_body']).blocks;
this.loading = false;
},
(err) => {
console.log('err', err);
this.loading = false;
}
);
}
public goToBlock(blockHash: string): void {
this.navCtrl.push('block-detail', {
'blockHash': blockHash
});
}
public getBlocks(): Array<any> {
/* tslint:disable:no-unused-variable */
return this.blocks.filter((block, index) => index < this.numBlocks);
/* tslint:enable:no-unused-variable */
}
public goToBlocks(): void {
this.navCtrl.push('blocks');
}
}

View File

@ -0,0 +1,4 @@
<!-- Generated template for the LatestTransactionsComponent component -->
<div>
{{text}}
</div>

View File

@ -0,0 +1,16 @@
import { NgModule } from '@angular/core';
import { IonicModule } from 'ionic-angular';
import { LatestTransactionsComponent } from './latest-transactions';
@NgModule({
declarations: [
LatestTransactionsComponent
],
imports: [
IonicModule
],
exports: [
LatestTransactionsComponent
]
})
export class LatestTransactionsComponentModule {}

View File

@ -0,0 +1,3 @@
latest-transactions {
}

View File

@ -0,0 +1,52 @@
import { Component } from '@angular/core';
// import { Http } from '@angular/http';
// import { ApiProvider } from '../../providers/api/api';
/**
* Generated class for the LatestTransactionsComponent component.
*
* See https://angular.io/docs/ts/latest/api/core/index/ComponentMetadata-class.html
* for more info on Angular Components.
*/
@Component({
selector: 'latest-transactions',
templateUrl: 'latest-transactions.html'
})
export class LatestTransactionsComponent {
private text: string;
constructor(/*private http: Http, private api: ApiProvider*/) {
console.log('Hello LatestTransactionsComponent Component');
this.text = 'Hello Latest Transactions';
/*
let url: string = this.api.apiPrefix + 'txs?' + this.queryType + '=' + this.queryValue;
this.http.get(url).subscribe(
(data) => {
this.transactions = JSON.parse(data['_body']);
this.loading = false;
},
(err) => {
console.log('err is', err);
this.loading = false;
}
);
*/
/*
this.http.get(this.api.apiPrefix + 'tx/' + this.txId).subscribe(
(data) => {
this.tx = JSON.parse(data['_body']);
this.loading = false;
},
(err) => {
console.log('err is', err);
this.loading = false;
}
);
*/
}
}

View File

@ -1,7 +1,14 @@
<ion-grid>
<ion-row *ngFor="let tx of transactions.txs">
<ion-col col-12>
<transaction [tx]="tx"></transaction>
</ion-col>
</ion-row>
</ion-grid>
<div *ngIf="loading">
<ion-spinner name="crescent"></ion-spinner>
</div>
<div *ngIf="!loading">
<ion-grid>
<ion-row *ngFor="let tx of transactions.txs">
<ion-col col-12>
<transaction [tx]="tx"></transaction>
</ion-col>
</ion-row>
</ion-grid>
</div>

View File

@ -18,7 +18,7 @@
</ion-row>
<ion-row>
<ion-col col-5>
<ion-col col-12 col-md-5>
<ion-list [hidden]="!tx.isCoinBase">
<ion-item>
@ -27,42 +27,56 @@
</ion-list>
<ion-list [hidden]="tx.isCoinBase">
<ion-item *ngFor="let vin of tx.vin">
<ion-item *ngFor="let vin of aggregateItems(tx.vin)">
<div>
<p><a (click)="goToAddress(vin.addr)">{{ vin.addr }}</a> <span item-end>{{ vin.value + ' BTC' }}</span></p>
<div [hidden]="!expanded">
<p><b>Confirmations</b> {{vin.confirmations}}</p>
<p><b>scriptSig</b></p>
<div *ngIf="vin.scriptSig">
<div *ngFor="let item of vin.scriptSig.asm | split:' '" class="ellipsis">
<p>{{item}}</p>
</div>
</div>
<div class="ellipsis">
<p><a (click)="goToAddress(vin.addr)">{{ vin.addr }}</a></p>
</div>
</div>
</ion-item>
</ion-list>
</ion-col>
<ion-col col-2 text-center>
<ion-icon name="arrow-forward"></ion-icon>
</ion-col>
<ion-col col-5>
<ion-list>
<ion-item *ngFor="let vout of tx.vout">
<div>
<p><a (click)="goToAddress(getAddress(vout))">{{ getAddress(vout) }}</a></p>
<div [hidden]="!expanded">
<p><b>Type</b> {{vout.scriptPubKey.type}}</p>
<p><b>scriptPubKey</b></p>
<div class="ellipsis">
<span>{{vout.scriptPubKey.asm}}</span>
<p *ngIf="vin.confirmations"><b>Confirmations</b> {{vin.confirmations}}</p>
<p><b>scriptSig</b></p>
<div *ngFor="let item of vin.items">
<div *ngIf="item.scriptSig">
<div *ngFor="let scriptSig of item.scriptSig.asm | split:' '">
<div class="ellipsis">
<p>{{ scriptSig }}</p>
</div>
</div>
</div>
</div>
</div>
</div>
<div item-end>
{{ vout.value + ' BTC' }}
{{ currency.getConversion(vin.value) }}
</div>
</ion-item>
</ion-list>
</ion-col>
<ion-col col-12 col-md-1 text-center>
<ion-icon name="arrow-forward"></ion-icon>
</ion-col>
<ion-col col-12 col-md-6>
<ion-list>
<ion-item *ngFor="let vout of tx.vout">
<div>
<div class="ellipsis">
<p><a (click)="goToAddress(getAddress(vout))">{{ getAddress(vout) }}</a></p>
</div>
<div [hidden]="!expanded">
<p><b>Type</b> {{vout.scriptPubKey.type}}</p>
<p><b>scriptPubKey</b></p>
<div class="ellipsis">
<p>{{vout.scriptPubKey.asm}}</p>
</div>
</div>
</div>
<div item-end>
{{ currency.getConversion(vout.value) }}
<span [hidden]="!vout.spentTxId">(S)</span>
<span [hidden]="vout.spentTxId">(U)</span>
</div>
@ -72,12 +86,14 @@
</ion-row>
<ion-row>
<ion-col col-6>
<span [hidden]="tx.isCoinBase">Fee {{ tx.fees + ' BTC' }}</span>
<ion-col col-3>
<span [hidden]="tx.isCoinBase">Fee {{ currency.getConversion(tx.fees) }}</span>
</ion-col>
<ion-col col-6 text-right>
<ion-col col-6 text-center>
{{ tx.confirmations }} Confirmations
<span class="">{{ tx.valueOut + ' BTC' }}</span>
</ion-col>
<ion-col col-3 text-right>
<span class="">{{ currency.getConversion(tx.valueOut) }}</span>
</ion-col>
</ion-row>
</ion-grid>

View File

@ -1,4 +1,7 @@
transaction {
// TODO Customize the grid to have 13 columns so that we have 6-col inputs, 6-col outputs, 1 col arrow
// See http://ionicframework.com/docs/api/components/grid/Grid/#customizing-the-grid
ion-grid {
background-color: #F4F4F4;
border: 1px solid #eee;

View File

@ -1,6 +1,7 @@
import { Component } from '@angular/core';
import { Input } from '@angular/core';
import { NavController } from 'ionic-angular';
import { CurrencyProvider } from '../../providers/currency/currency';
/**
* Generated class for the TransactionComponent component.
@ -14,10 +15,12 @@ import { NavController } from 'ionic-angular';
})
export class TransactionComponent {
private COIN: number = 100000000;
public expanded: boolean = false;
@Input() public tx: any = {};
constructor(private navCtrl: NavController) {
constructor(private navCtrl: NavController, public currency: CurrencyProvider) {
}
public getAddress(vout: any): string {
@ -43,4 +46,70 @@ export class TransactionComponent {
public toggleExpanded(): void {
this.expanded = !this.expanded;
}
public aggregateItems(items: Array<any>): Array<any> {
if (!items) return [];
let l: number = items.length;
let ret: Array<any> = [];
let tmp: any = {};
let u: number = 0;
for (let i: number = 0; i < l; i++) {
let notAddr: boolean = 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;
}
let addr: string = 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].dbError = tmp[addr].dbError || items[i].dbError;
tmp[addr].valueSat += Math.round(items[i].value * this.COIN);
tmp[addr].items.push(items[i]);
tmp[addr].notAddr = notAddr;
if (items[i].unconfirmedInput)
tmp[addr].unconfirmedInput = true;
tmp[addr].count++;
}
for (let v in tmp) {
let obj: any = tmp[v];
obj.value = obj.value || parseInt(obj.valueSat) / this.COIN;
ret.push(obj);
}
return ret;
};
}

View File

@ -3,45 +3,51 @@
</ion-header>
<ion-content padding>
<h1>Address</h1>
<p class="ellipsis"><b>Address</b> {{ address.addrStr }}</p>
<p>{{ address.balance }} BTC</p>
<div *ngIf="loading">
<ion-spinner name="crescent"></ion-spinner>
</div>
<h2>Summary</h2>
<div *ngIf="!loading">
<h1>Address</h1>
<p class="ellipsis"><b>Address</b> {{ address.addrStr }}</p>
<p>{{ currency.getConversion(address.balance) }}</p>
<ion-list>
<ion-item>
Total Received
<span item-end>
{{ address.totalReceived }} BTC
</span>
</ion-item>
<ion-item>
Total Sent
<span item-end>
{{ address.totalSent }} BTC
</span>
</ion-item>
<ion-item>
Final Balance
<span item-end>
{{ address.balance }} BTC
</span>
</ion-item>
<ion-item>
No. Transactions
<span item-end>
{{ address.txApperances }}
</span>
</ion-item>
</ion-list>
<h2>Summary</h2>
<p text-center>
<qr-code [value]="address.addrStr" [size]="160"></qr-code>
<p>
<ion-list class="summary">
<ion-item>
Total Received
<span item-end>
{{ currency.getConversion(address.totalReceived) }}
</span>
</ion-item>
<ion-item>
Total Sent
<span item-end>
{{ currency.getConversion(address.totalSent) }}
</span>
</ion-item>
<ion-item>
Final Balance
<span item-end>
{{ currency.getConversion(address.balance) }}
</span>
</ion-item>
<ion-item>
No. Transactions
<span item-end>
{{ address.txApperances }}
</span>
</ion-item>
</ion-list>
<h2>Transactions</h2>
<p text-center>
<qr-code [value]="address.addrStr" [size]="160"></qr-code>
<p>
<transaction-list [queryType]="'address'" [queryValue]="addrStr"></transaction-list>
<h2>Transactions</h2>
<transaction-list [queryType]="'address'" [queryValue]="addrStr"></transaction-list>
</div>
</ion-content>

View File

@ -2,6 +2,7 @@ import { Component } from '@angular/core';
import { IonicPage, NavController, NavParams } from 'ionic-angular';
import { Http } from '@angular/http';
import { ApiProvider } from '../../providers/api/api';
import { CurrencyProvider } from '../../providers/currency/currency';
/**
* Generated class for the AddressPage page.
@ -23,7 +24,7 @@ export class AddressPage {
private addrStr: string;
public address: any = {};
constructor(public navCtrl: NavController, public navParams: NavParams, private http: Http, private api: ApiProvider) {
constructor(public navCtrl: NavController, public navParams: NavParams, private http: Http, private api: ApiProvider, public currency: CurrencyProvider) {
this.addrStr = navParams.get('addrStr');
}

View File

@ -3,96 +3,102 @@
</ion-header>
<ion-content padding>
<h1>Block #{{ block.height }}</h1>
<p class="ellipsis"><b>BlockHash</b> {{ block.hash }}</p>
<h2>Summary</h2>
<ion-list *ngIf="!loading">
<ion-item>
Number of Transactions
<span item-end>
{{ block.tx.length }}
</span>
</ion-item>
<ion-item>
Height
<span item-end>
{{ block.height }} <span [hidden]="!block.isMainChain">(Mainchain)</span>
</span>
</ion-item>
<ion-item>
Block Reward
<span item-end>
{{ block.reward + ' BTC' }}
</span>
</ion-item>
<ion-item>
Timestamp
<span item-end>
{{ block.time * 1000 | date:'medium' }}
</span>
</ion-item>
<ion-item>
Mined by
<span item-end>
<a href="{{ block.poolInfo.url }}">{{ block.poolInfo.poolName }}</a>
</span>
</ion-item>
<ion-item>
Merkle Root
<span item-end class="ellipsis">
{{ block.merkleroot }}
</span>
</ion-item>
<ion-item>
Previous Block
<span item-end>
<a (click)="goToPreviousBlock()">{{ block.height - 1 }}</a>
</span>
</ion-item>
<ion-item>
Difficulty
<span item-end>
{{ block.difficulty }}
</span>
</ion-item>
<ion-item>
Bits
<span item-end>
{{ block.bits }}
</span>
</ion-item>
<ion-item>
Size (bytes)
<span item-end>
{{ block.size }}
</span>
</ion-item>
<ion-item>
Version
<span item-end>
{{ block.version }}
</span>
</ion-item>
<ion-item>
Nonce
<span item-end>
{{ block.nonce }}
</span>
</ion-item>
<ion-item>
Next Block
<span item-end>
<a (click)="goToNextBlock()">{{ block.height + 1 }}</a>
</span>
</ion-item>
</ion-list>
<div *ngIf="loading">
<ion-spinner name="crescent"></ion-spinner>
</div>
<div *ngIf="!loading">
<h2>Transactions</h2>
<h1>Block #{{ block.height }}</h1>
<p class="ellipsis"><b>BlockHash</b> {{ block.hash }}</p>
<transaction-list [queryType]="'block'" [queryValue]="block.hash"></transaction-list>
<h2>Summary</h2>
<ion-list *ngIf="!loading" class="summary">
<ion-item>
Number of Transactions
<span item-end>
{{ block.tx.length }}
</span>
</ion-item>
<ion-item>
Height
<span item-end>
{{ block.height }} <span [hidden]="!block.isMainChain">(Mainchain)</span>
</span>
</ion-item>
<ion-item>
Block Reward
<span item-end>
{{ currency.getConversion(block.reward) }}
</span>
</ion-item>
<ion-item>
Timestamp
<span item-end>
{{ block.time * 1000 | date:'medium' }}
</span>
</ion-item>
<ion-item>
Mined by
<span item-end *ngIf="block.poolInfo">
<a href="{{ block.poolInfo.url }}">{{ block.poolInfo.poolName }}</a>
</span>
</ion-item>
<ion-item>
Merkle Root
<span item-end class="ellipsis">
{{ block.merkleroot }}
</span>
</ion-item>
<ion-item>
Previous Block
<span item-end>
<a (click)="goToPreviousBlock()">{{ block.height - 1 }}</a>
</span>
</ion-item>
<ion-item>
Difficulty
<span item-end>
{{ block.difficulty }}
</span>
</ion-item>
<ion-item>
Bits
<span item-end>
{{ block.bits }}
</span>
</ion-item>
<ion-item>
Size (bytes)
<span item-end>
{{ block.size }}
</span>
</ion-item>
<ion-item>
Version
<span item-end>
{{ block.version }}
</span>
</ion-item>
<ion-item>
Nonce
<span item-end>
{{ block.nonce }}
</span>
</ion-item>
<ion-item>
Next Block
<span item-end>
<a (click)="goToNextBlock()">{{ block.height + 1 }}</a>
</span>
</ion-item>
</ion-list>
<div *ngIf="!loading">
<h2>Transactions</h2>
<transaction-list [queryType]="'block'" [queryValue]="block.hash"></transaction-list>
</div>
</div>
</ion-content>

View File

@ -2,6 +2,7 @@ import { Component } from '@angular/core';
import { IonicPage, NavController, NavParams } from 'ionic-angular';
import { Http } from '@angular/http';
import { ApiProvider } from '../../providers/api/api';
import { CurrencyProvider } from '../../providers/currency/currency';
/**
* Generated class for the BlockDetailPage page.
@ -25,9 +26,11 @@ export class BlockDetailPage {
tx: []
};
constructor(public navCtrl: NavController, private http: Http, public navParams: NavParams, private api: ApiProvider) {
constructor(public navCtrl: NavController, private http: Http, public navParams: NavParams, private api: ApiProvider, public currency: CurrencyProvider) {
this.blockHash = navParams.get('blockHash');
}
public ionViewDidLoad(): void {
this.http.get(this.api.apiPrefix + 'block/' + this.blockHash).subscribe(
(data) => {
this.block = JSON.parse(data['_body']);
@ -40,10 +43,6 @@ export class BlockDetailPage {
);
}
public ionViewWillLeave(): void {
this.loading = true;
}
public goToPreviousBlock(): void {
this.navCtrl.push('block-detail', {
'blockHash': this.block.previousblockhash

View File

@ -0,0 +1,14 @@
<ion-header>
<head-nav [title]="'Blocks'"></head-nav>
</ion-header>
<ion-content padding>
<div *ngIf="loading">
<ion-spinner name="crescent"></ion-spinner>
</div>
<div *ngIf="!loading">
<h1>Blocks</h1>
<latest-blocks [numBlocks]="100" [showTimeAs]="'timestamp'" [showAllBlocksButton]="false"></latest-blocks>
</div>
</ion-content>

View File

@ -0,0 +1,20 @@
import { NgModule } from '@angular/core';
import { IonicPageModule } from 'ionic-angular';
import { BlocksPage } from './blocks';
import { HeadNavComponentModule } from '../../components/head-nav/head-nav.module';
import { LatestBlocksComponentModule } from '../../components/latest-blocks/latest-blocks.module';
@NgModule({
declarations: [
BlocksPage
],
imports: [
IonicPageModule.forChild(BlocksPage),
HeadNavComponentModule,
LatestBlocksComponentModule
],
exports: [
BlocksPage
]
})
export class BlocksPageModule {}

View File

@ -0,0 +1,23 @@
page-blocks {
ion-grid {
// border: 2px solid green;
margin: 10px 0 20px;
ion-row {
border-top: 1px solid #ccc;
}
ion-row:nth-child(even) {
background-color: #f4f4f4;
}
ion-row:first-child {
background-color: white;
border-top: none;
}
ion-row:last-child {
background-color: white;
}
}
}

View File

@ -0,0 +1,37 @@
import { Component } from '@angular/core';
import { IonicPage, NavController, NavParams } from 'ionic-angular';
import { BlocksProvider } from '../../providers/blocks/blocks';
/**
* Generated class for the BlocksPage page.
*
* See http://ionicframework.com/docs/components/#navigation for more info
* on Ionic pages and navigation.
*/
@IonicPage({
name: 'blocks',
segment: 'blocks/'
})
@Component({
selector: 'page-blocks',
templateUrl: 'blocks.html'
})
export class BlocksPage {
public loading: boolean = true;
public blocks: Array<any> = [];
constructor(public navCtrl: NavController, public navParams: NavParams, private blocksProvider: BlocksProvider) {
this.blocksProvider.getBlocks().subscribe(
(data) => {
this.blocks = JSON.parse(data['_body']).blocks;
console.log('this.blocks', this.blocks);
this.loading = false;
},
(err) => {
console.log('err', err);
this.loading = false;
}
);
}
}

View File

@ -4,16 +4,20 @@ import { Observable } from 'rxjs';
import { Block } from '../../models';
import { BlocksService } from '../../services';
/**
* @deprecated Use BlocksPage from ../blocks/blocks
*/
@Component({
templateUrl: './blocksPage.html'
})
export class BlocksPage {
public title: string;
public blocks: Observable<Block[]>;
constructor(private navCtrl: NavController, private blocksService: BlocksService) {
// TODO Put loading spinner on page
this.title = 'Blocks';
this.blocks = blocksService.latestBlocks;
this.blocks.subscribe((blocks) => {

View File

@ -0,0 +1,39 @@
<!--
Generated template for the HomePage page.
See http://ionicframework.com/docs/components/#navigation for more info on
Ionic pages and navigation.
-->
<ion-header>
<head-nav [title]="'Home'"></head-nav>
</ion-header>
<ion-content padding>
<div>
<h1>Latest Blocks</h1>
<latest-blocks [numBlocks]="6" [showTimeAs]="'age'" [showAllBlocksButton]="true"></latest-blocks>
</div>
<div>
<h1>Latest Transactions</h1>
<latest-transactions></latest-transactions>
</div>
<div class="about">
<h2>About</h2>
<p><strong>insight</strong> is an <a href="https://insight.is/"
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="https://github.com/bitpay/insight-ui" 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-ui/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>
</ion-content>

View File

@ -0,0 +1,20 @@
import { NgModule } from '@angular/core';
import { IonicPageModule } from 'ionic-angular';
import { HomePage } from './home';
import { HeadNavComponentModule } from '../../components/head-nav/head-nav.module';
import { LatestTransactionsComponentModule } from '../../components/latest-transactions/latest-transactions.module';
@NgModule({
declarations: [
HomePage
],
imports: [
IonicPageModule.forChild(HomePage),
HeadNavComponentModule,
LatestTransactionsComponentModule
],
exports: [
HomePage
]
})
export class HomePageModule {}

View File

@ -0,0 +1,50 @@
page-home {
.about {
background-color: #F4F4F4;
border: 1px solid #eee;
border-radius: 5px;
padding: 14px;
}
#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('../../assets/img/bitcore.svg');
background-size: 80px;
width: 30%;
}
#powered a.nodejs {
background-image: url('../../assets/img/nodejs.png');
background-size: 80px;
width: 30%;
}
#powered a.angularjs {
background-image: url('../../assets/img/angularjs.png');
background-size: 50px;
width: 20%;
}
#powered a.leveldb {
background-image: url('../../assets/img/leveldb.png');
background-size: 50px;
width: 20%;
}
}

View File

@ -0,0 +1,27 @@
import { Component } from '@angular/core';
import { IonicPage, NavController, NavParams } from 'ionic-angular';
/**
* Generated class for the HomePage page.
*
* See http://ionicframework.com/docs/components/#navigation for more info
* on Ionic pages and navigation.
*/
@IonicPage({
name: 'home',
segment: 'home'
})
@Component({
selector: 'page-home',
templateUrl: 'home.html'
})
export class HomePage {
constructor(public navCtrl: NavController, public navParams: NavParams) {
}
public ionViewDidLoad(): void {
console.log('ionViewDidLoad HomePage');
}
}

View File

@ -1,5 +1,6 @@
export * from './blocksPage/blocksPage';
export * from './broadcastTxPage/broadcastTxPage';
export * from './nodeStatusPage/nodeStatusPage';
export * from './verifyMessagePage/verifyMessagePage';
export * from './home/home';
export * from './blocks/blocks';
export * from './pages.module';

View File

@ -1,9 +1,12 @@
import { NgModule } from '@angular/core';
import { IonicModule } from 'ionic-angular';
import { ComponentsModule } from '../components';
import { BlocksPageModule } from '../pages/blocks/blocks.module';
import { HeadNavComponentModule } from '../components/head-nav/head-nav.module';
import { LatestTransactionsComponentModule } from '../components/latest-transactions/latest-transactions.module';
import { LatestBlocksComponentModule } from '../components/latest-blocks/latest-blocks.module';
import {
BlocksPage,
HomePage,
BroadcastTxPage,
NodeStatusPage,
VerifyMessagePage
@ -11,12 +14,19 @@ import {
@NgModule({
declarations: [
BlocksPage,
HomePage,
BroadcastTxPage,
NodeStatusPage,
VerifyMessagePage
],
imports: [ IonicModule, ComponentsModule, HeadNavComponentModule ],
imports: [
IonicModule,
ComponentsModule,
BlocksPageModule,
HeadNavComponentModule,
LatestTransactionsComponentModule,
LatestBlocksComponentModule
],
exports: [
// CustomComponent,
],

View File

@ -3,40 +3,46 @@
</ion-header>
<ion-content padding>
<h1>Transaction</h1>
<p class="ellipsis"><b>Transaction</b> {{ tx.txid }}</p>
<div *ngIf="loading">
<ion-spinner name="crescent"></ion-spinner>
</div>
<h2>Summary</h2>
<div *ngIf="!loading">
<h1>Transaction</h1>
<p class="ellipsis"><b>Transaction</b> {{ tx.txid }}</p>
<ion-list>
<ion-item>
Size
<span item-end>
{{ tx.size }} (bytes)
</span>
</ion-item>
<ion-item>
Received Time
<span item-end>
{{ tx.time * 1000 | date:'medium' }}
</span>
</ion-item>
<ion-item>
Mined Time
<span item-end>
{{ tx.blocktime * 1000 | date:'medium' }}
</span>
</ion-item>
<ion-item>
Included in Block
<span item-end>
<a (click)="goToBlock(tx.blockhash)">{{ tx.blockhash }}</a>
</span>
</ion-item>
</ion-list>
<h2>Summary</h2>
<h2>Details</h2>
<ion-list class="summary">
<ion-item>
Size
<span item-end>
{{ tx.size }} (bytes)
</span>
</ion-item>
<ion-item>
Received Time
<span item-end>
{{ tx.time * 1000 | date:'medium' }}
</span>
</ion-item>
<ion-item>
Mined Time
<span item-end>
{{ tx.blocktime * 1000 | date:'medium' }}
</span>
</ion-item>
<ion-item>
Included in Block
<span item-end>
<a (click)="goToBlock(tx.blockhash)">{{ tx.blockhash }}</a>
</span>
</ion-item>
</ion-list>
<transaction [tx]="tx"></transaction>
<h2>Details</h2>
<transaction [tx]="tx"></transaction>
</div>
</ion-content>

View File

@ -15,7 +15,6 @@ export class SplitPipe implements PipeTransform {
*/
public transform(value: string, delimiter: string): Array<string> {
let array: Array<string> = value.split(delimiter);
console.log('split is', array);
return array;
}
}

View File

@ -11,7 +11,7 @@ import 'rxjs/add/operator/map';
@Injectable()
export class ApiProvider {
public apiPrefix: string = 'http://localhost:3001/insight-api/';
public apiPrefix: string = 'https://insight.bitpay.com/api/';
constructor(public http: Http) {
}

View File

@ -0,0 +1,23 @@
import { Injectable } from '@angular/core';
import { Http } from '@angular/http';
import 'rxjs/add/operator/map';
import { ApiProvider } from '../../providers/api/api';
/*
Generated class for the BlocksProvider provider.
See https://angular.io/docs/ts/latest/guide/dependency-injection.html
for more info on providers and Angular DI.
*/
@Injectable()
export class BlocksProvider {
constructor(public http: Http, private api: ApiProvider) {
console.log('Hello BlocksProvider Provider');
}
public getBlocks(): any {
return this.http.get(this.api.apiPrefix + 'blocks');
}
}

View File

@ -2,9 +2,10 @@
import { TestBed, ComponentFixture, inject } from '@angular/core/testing';
import { HttpModule } from '@angular/http';
import { CurrencyProvider } from './currency';
import { ApiProvider } from '../api/api';
describe('CurrencyProvider', () => {
let currency;
let currency: CurrencyProvider;
beforeEach(() => {
TestBed.configureTestingModule({
@ -12,6 +13,7 @@ describe('CurrencyProvider', () => {
HttpModule
],
providers: [
ApiProvider,
CurrencyProvider
]
});
@ -47,7 +49,7 @@ describe('CurrencyProvider', () => {
});
it('rounds float using specified number of decimal places', () => {
let aFloat = 4.32943;
let aFloat: number = 4.32943;
expect(currency.roundFloat(aFloat, 2)).toBe(4.33);
expect(currency.roundFloat(aFloat, 3)).toBe(4.329);
@ -66,7 +68,7 @@ describe('CurrencyProvider', () => {
});
it('gets proper conversion after changing currency', () => {
let aFloat = 12345.09876543;
let aFloat: number = 12345.09876543;
expect(currency.getConversion(aFloat)).toBe('12345.09876543 BTC');
currency.setCurrency('mBTC');

View File

@ -1,5 +1,6 @@
import { Injectable } from '@angular/core';
import { Http } from '@angular/http';
import { ApiProvider } from '../../providers/api/api';
import 'rxjs/add/operator/map';
/*
@ -11,16 +12,18 @@ import 'rxjs/add/operator/map';
@Injectable()
export class CurrencyProvider {
private defaultCurrency: string;
private currencySymbol: string;
private factor: number = 1;
public defaultCurrency: string;
public currencySymbol: string;
public factor: number = 1;
private bitstamp: number;
private loading: boolean;
constructor(public http: Http) {
constructor(public http: Http, private api: ApiProvider) {
this.defaultCurrency = 'BTC';
this.currencySymbol = this.defaultCurrency;
}
private roundFloat(aFloat: number, decimalPlaces: number): number {
public roundFloat(aFloat: number, decimalPlaces: number): number {
return Math.round(aFloat * Math.pow(10, decimalPlaces)) / Math.pow(10, decimalPlaces);
}
@ -50,12 +53,17 @@ export class CurrencyProvider {
localStorage.setItem('insight-currency', currency);
if (currency === 'USD') {
// TODO Replace this with call
/*
Currency.get({}, function(res) {
$rootScope.currency.factor = $rootScope.currency.bitstamp = res.data.bitstamp;
});
*/
this.http.get(this.api.apiPrefix + 'currency').subscribe(
(data) => {
let currencyParsed: any = JSON.parse(data['_body']);
this.factor = this.bitstamp = currencyParsed.data.bitstamp;
this.loading = false;
},
(err) => {
console.log('err is', err);
this.loading = false;
}
);
} else if (currency === 'mBTC') {
this.factor = 1000;
} else if (currency === 'bits') {

View File

@ -17,7 +17,9 @@ import { ConfigMock, PlatformMock } from './mocks';
import { BlocksServiceMock } from './services/mocks';
import { BlocksService } from './services';
import { ApiProvider } from './providers/api/api';
import { CurrencyProvider } from './providers/currency/currency';
import { HeadNavComponentModule } from './components/head-nav/head-nav.module';
import { ActionSheetController } from 'ionic-angular';
// Unfortunately there's no typing for the `__karma__` variable. Just declare it as any.
declare var __karma__: any;
@ -63,7 +65,9 @@ export class TestUtils {
{provide: Platform, useClass: PlatformMock},
{provide: Config, useClass: ConfigMock},
{provide: BlocksService, useClass: BlocksServiceMock},
ApiProvider
ApiProvider,
CurrencyProvider,
ActionSheetController
],
imports: [
FormsModule,

View File

@ -200,6 +200,39 @@
uuid "^3.0.1"
wrap-ansi "^3.0.1"
"@ionic/cli-utils@1.9.1":
version "1.9.1"
resolved "https://registry.yarnpkg.com/@ionic/cli-utils/-/cli-utils-1.9.1.tgz#a0118819cb8de1f3bc4bc5b401e5b61d7f41d5b5"
dependencies:
"@types/gulp" "^3.8.33"
archiver "^2.0.0"
chalk "^2.0.0"
chokidar "^1.7.0"
ci-info "^1.0.0"
cross-spawn "^5.1.0"
dargs "^5.1.0"
diff "^3.3.0"
elementtree "^0.1.7"
express "^4.15.4"
inquirer "^3.2.1"
leek "0.0.24"
lodash "^4.17.4"
minimist "^1.2.0"
ncp "^2.0.0"
opn "^5.1.0"
proxy-middleware "^0.15.0"
semver "^5.4.1"
slice-ansi "^1.0.0"
ssh-config "^1.0.1"
string-width "^2.1.1"
strip-ansi "^4.0.0"
superagent "^3.5.2"
tar "^2.2.1"
tiny-lr "^1.0.5"
tslib "^1.7.1"
uuid "^3.0.1"
wrap-ansi "^3.0.1"
"@ionic/storage@2.0.1":
version "2.0.1"
resolved "https://registry.yarnpkg.com/@ionic/storage/-/storage-2.0.1.tgz#bb1a8c276007d008d7acdda426b56065b0fd3c0b"
@ -221,6 +254,14 @@
magic-string "^0.19.0"
source-map "^0.5.6"
"@types/gulp@^3.8.33":
version "3.8.33"
resolved "https://registry.yarnpkg.com/@types/gulp/-/gulp-3.8.33.tgz#b1b076820738c9c4eb7808cd926bff1683e1c2ab"
dependencies:
"@types/node" "*"
"@types/orchestrator" "*"
"@types/vinyl" "*"
"@types/jasmine@2.5.41":
version "2.5.41"
resolved "https://registry.yarnpkg.com/@types/jasmine/-/jasmine-2.5.41.tgz#d5e86161a0af80d52062b310a33ed65b051a0713"
@ -229,6 +270,10 @@
version "0.0.30"
resolved "https://registry.yarnpkg.com/@types/localforage/-/localforage-0.0.30.tgz#3d60a6bf6dda38e3f8a469611598379f1f649509"
"@types/node@*":
version "8.0.24"
resolved "https://registry.yarnpkg.com/@types/node/-/node-8.0.24.tgz#06c580084d9add1fb40c1510ef0b448961246fb1"
"@types/node@7.0.4":
version "7.0.4"
resolved "https://registry.yarnpkg.com/@types/node/-/node-7.0.4.tgz#9aabc135979ded383325749f508894c662948c8b"
@ -237,6 +282,17 @@
version "6.0.79"
resolved "https://registry.yarnpkg.com/@types/node/-/node-6.0.79.tgz#5efe7d4a6d8c453c7e9eaf55d931f4a22fac5169"
"@types/orchestrator@*":
version "0.3.0"
resolved "https://registry.yarnpkg.com/@types/orchestrator/-/orchestrator-0.3.0.tgz#bf84a1699c9330d4fe89cd81263e8fc09fb32978"
dependencies:
"@types/node" "*"
"@types/q" "^0"
"@types/q@^0":
version "0.0.36"
resolved "https://registry.yarnpkg.com/@types/q/-/q-0.0.36.tgz#97d786389641bcbd0e22bfc729a534175976371d"
"@types/q@^0.0.32":
version "0.0.32"
resolved "https://registry.yarnpkg.com/@types/q/-/q-0.0.32.tgz#bd284e57c84f1325da702babfc82a5328190c0c5"
@ -245,6 +301,12 @@
version "2.53.42"
resolved "https://registry.yarnpkg.com/@types/selenium-webdriver/-/selenium-webdriver-2.53.42.tgz#74cb77fb6052edaff2a8984ddafd88d419f25cac"
"@types/vinyl@*":
version "2.0.0"
resolved "https://registry.yarnpkg.com/@types/vinyl/-/vinyl-2.0.0.tgz#fd213bf7f4136dde21fe1895500b12c186f8c268"
dependencies:
"@types/node" "*"
abbrev@1:
version "1.1.0"
resolved "https://registry.yarnpkg.com/abbrev/-/abbrev-1.1.0.tgz#d0554c2256636e2f56e7c2e5ad183f859428d81f"
@ -335,6 +397,12 @@ amdefine@>=0.0.4:
version "1.0.1"
resolved "https://registry.yarnpkg.com/amdefine/-/amdefine-1.0.1.tgz#4a5282ac164729e93619bcfd3ad151f817ce91f5"
angular2-moment@^1.6.0:
version "1.6.0"
resolved "https://registry.yarnpkg.com/angular2-moment/-/angular2-moment-1.6.0.tgz#2be11b63d9cbdff8fee06f575c1beee2d863d62b"
dependencies:
moment "^2.16.0"
angular2-qrcode@^2.0.1:
version "2.0.1"
resolved "https://registry.yarnpkg.com/angular2-qrcode/-/angular2-qrcode-2.0.1.tgz#1b4e65c302694b5078ca06f71138f7e4367754dc"
@ -1256,7 +1324,7 @@ chokidar@1.6.1:
optionalDependencies:
fsevents "^1.0.0"
chokidar@^1.4.1, chokidar@^1.4.3, chokidar@^1.6.0, chokidar@^1.6.1:
chokidar@^1.4.1, chokidar@^1.4.3, chokidar@^1.6.0, chokidar@^1.6.1, chokidar@^1.7.0:
version "1.7.0"
resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-1.7.0.tgz#798e689778151c8076b4b360e5edd28cda2bb468"
dependencies:
@ -1800,7 +1868,7 @@ date-now@^0.1.4:
version "0.1.4"
resolved "https://registry.yarnpkg.com/date-now/-/date-now-0.1.4.tgz#eaf439fd4d4848ad74e5cc7dbef200672b9e345b"
debug@*, debug@2, debug@2.6.8, debug@^2.1.0, debug@^2.1.1, debug@^2.1.3, debug@^2.2.0, debug@^2.6.3, debug@^2.6.8:
debug@*, debug@2, debug@2.6.8, debug@^2.1.0, debug@^2.1.1, debug@^2.1.3, debug@^2.2.0, debug@^2.6.3, debug@^2.6.8, debug@~2.6.7:
version "2.6.8"
resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.8.tgz#e731531ca2ede27d188222427da17821d68ff4fc"
dependencies:
@ -1880,6 +1948,10 @@ depd@1.1.0, depd@~1.1.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/depd/-/depd-1.1.0.tgz#e1bd82c6aab6ced965b97b88b17ed3e528ca18c3"
depd@1.1.1, depd@~1.1.1:
version "1.1.1"
resolved "https://registry.yarnpkg.com/depd/-/depd-1.1.1.tgz#5783b4e1c459f06fa5ca27f991f3d06e7a310359"
des.js@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/des.js/-/des.js-1.0.0.tgz#c074d2e2aa6a8a9a07dbd61f9a15c2cd83ec8ecc"
@ -1917,6 +1989,10 @@ diff@^3.1.0, diff@^3.2.0:
version "3.2.0"
resolved "https://registry.yarnpkg.com/diff/-/diff-3.2.0.tgz#c9ce393a4b7cbd0b058a725c93df299027868ff9"
diff@^3.3.0:
version "3.3.0"
resolved "https://registry.yarnpkg.com/diff/-/diff-3.3.0.tgz#056695150d7aa93237ca7e378ac3b1682b7963b9"
diffie-hellman@^5.0.0:
version "5.0.2"
resolved "https://registry.yarnpkg.com/diffie-hellman/-/diffie-hellman-5.0.2.tgz#b5835739270cfe26acf632099fded2a07f209e5e"
@ -2327,6 +2403,39 @@ express@^4.13.3:
utils-merge "1.0.0"
vary "~1.1.1"
express@^4.15.4:
version "4.15.4"
resolved "https://registry.yarnpkg.com/express/-/express-4.15.4.tgz#032e2253489cf8fce02666beca3d11ed7a2daed1"
dependencies:
accepts "~1.3.3"
array-flatten "1.1.1"
content-disposition "0.5.2"
content-type "~1.0.2"
cookie "0.3.1"
cookie-signature "1.0.6"
debug "2.6.8"
depd "~1.1.1"
encodeurl "~1.0.1"
escape-html "~1.0.3"
etag "~1.8.0"
finalhandler "~1.0.4"
fresh "0.5.0"
merge-descriptors "1.0.1"
methods "~1.1.2"
on-finished "~2.3.0"
parseurl "~1.3.1"
path-to-regexp "0.1.7"
proxy-addr "~1.1.5"
qs "6.5.0"
range-parser "~1.2.0"
send "0.15.4"
serve-static "1.12.4"
setprototypeof "1.0.3"
statuses "~1.3.1"
type-is "~1.6.15"
utils-merge "1.0.0"
vary "~1.1.1"
extend@3, extend@^3.0.0, extend@~3.0.0:
version "3.0.1"
resolved "https://registry.yarnpkg.com/extend/-/extend-3.0.1.tgz#a755ea7bc1adfcc5a31ce7e762dbaadc5e636444"
@ -2446,6 +2555,18 @@ finalhandler@1.0.3, finalhandler@~1.0.3:
statuses "~1.3.1"
unpipe "~1.0.0"
finalhandler@~1.0.4:
version "1.0.4"
resolved "https://registry.yarnpkg.com/finalhandler/-/finalhandler-1.0.4.tgz#18574f2e7c4b98b8ae3b230c21f201f31bdb3fb7"
dependencies:
debug "2.6.8"
encodeurl "~1.0.1"
escape-html "~1.0.3"
on-finished "~2.3.0"
parseurl "~1.3.1"
statuses "~1.3.1"
unpipe "~1.0.0"
find-up@^1.0.0:
version "1.1.2"
resolved "https://registry.yarnpkg.com/find-up/-/find-up-1.1.2.tgz#6b2e9822b1a2ce0a60ab64d610eccad53cb24d0f"
@ -2913,6 +3034,15 @@ http-errors@~1.6.1:
setprototypeof "1.0.3"
statuses ">= 1.3.1 < 2"
http-errors@~1.6.2:
version "1.6.2"
resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.6.2.tgz#0a002cc85707192a7e7946ceedc11155f60ec736"
dependencies:
depd "1.1.1"
inherits "2.0.3"
setprototypeof "1.0.3"
statuses ">= 1.3.1 < 2"
http-proxy-middleware@~0.17.4:
version "0.17.4"
resolved "https://registry.yarnpkg.com/http-proxy-middleware/-/http-proxy-middleware-0.17.4.tgz#642e8848851d66f09d4f124912846dbaeb41b833"
@ -3093,20 +3223,16 @@ ionic-angular@3.4.2:
version "3.4.2"
resolved "https://registry.yarnpkg.com/ionic-angular/-/ionic-angular-3.4.2.tgz#762631f1af78a5ae1c0aa0f4d23b31435142abe1"
ionic@3.7.0:
version "3.7.0"
resolved "https://registry.yarnpkg.com/ionic/-/ionic-3.7.0.tgz#a3c32174c6d5455c3bc74b6b9152e0f97dce23c8"
ionic@3.9.1:
version "3.9.1"
resolved "https://registry.yarnpkg.com/ionic/-/ionic-3.9.1.tgz#be50eea2f55bad40574772ecaeaf5a460fd16330"
dependencies:
"@ionic/cli-utils" "1.7.0"
"@ionic/cli-utils" "1.9.1"
chalk "^2.0.0"
diff "^3.2.0"
minimist "^1.2.0"
opn "^5.1.0"
os-name "^2.0.1"
rimraf "^2.6.1"
semver "^5.3.0"
ssh-config "^1.0.1"
tar "^2.2.1"
tslib "^1.7.1"
ionicons@3.0.0:
@ -3121,6 +3247,10 @@ ipaddr.js@1.3.0:
version "1.3.0"
resolved "https://registry.yarnpkg.com/ipaddr.js/-/ipaddr.js-1.3.0.tgz#1e03a52fdad83a8bbb2b25cbf4998b4cffcd3dec"
ipaddr.js@1.4.0:
version "1.4.0"
resolved "https://registry.yarnpkg.com/ipaddr.js/-/ipaddr.js-1.4.0.tgz#296aca878a821816e5b85d0a285a99bcff4582f0"
is-absolute-url@^2.0.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/is-absolute-url/-/is-absolute-url-2.1.0.tgz#50530dfb84fcc9aa7dbe7852e83a37b93b9f2aa6"
@ -4106,6 +4236,10 @@ mkdirp@0.5.x, "mkdirp@>=0.5 0", mkdirp@^0.5.0, mkdirp@^0.5.1, mkdirp@~0.5.0, mkd
dependencies:
minimist "0.0.8"
moment@^2.16.0:
version "2.18.1"
resolved "https://registry.yarnpkg.com/moment/-/moment-2.18.1.tgz#c36193dd3ce1c2eed2adb7c802dbbc77a81b1c0f"
ms@0.7.1:
version "0.7.1"
resolved "https://registry.yarnpkg.com/ms/-/ms-0.7.1.tgz#9cd13c03adbff25b65effde7ce864ee952017098"
@ -5004,7 +5138,14 @@ proxy-addr@~1.1.3, proxy-addr@~1.1.4:
forwarded "~0.1.0"
ipaddr.js "1.3.0"
proxy-middleware@0.15.0:
proxy-addr@~1.1.5:
version "1.1.5"
resolved "https://registry.yarnpkg.com/proxy-addr/-/proxy-addr-1.1.5.tgz#71c0ee3b102de3f202f3b64f608d173fcba1a918"
dependencies:
forwarded "~0.1.0"
ipaddr.js "1.4.0"
proxy-middleware@0.15.0, proxy-middleware@^0.15.0:
version "0.15.0"
resolved "https://registry.yarnpkg.com/proxy-middleware/-/proxy-middleware-0.15.0.tgz#a3fdf1befb730f951965872ac2f6074c61477a56"
@ -5060,7 +5201,7 @@ qs@6.4.0, qs@~6.4.0:
version "6.4.0"
resolved "https://registry.yarnpkg.com/qs/-/qs-6.4.0.tgz#13e26d28ad6b0ffaa91312cd3bf708ed351e7233"
qs@^6.1.0, qs@^6.2.0:
qs@6.5.0, qs@^6.1.0, qs@^6.2.0, qs@^6.4.0:
version "6.5.0"
resolved "https://registry.yarnpkg.com/qs/-/qs-6.5.0.tgz#8d04954d364def3efc55b5a0793e1e2c8b1e6e49"
@ -5645,6 +5786,24 @@ send@0.15.3:
range-parser "~1.2.0"
statuses "~1.3.1"
send@0.15.4:
version "0.15.4"
resolved "https://registry.yarnpkg.com/send/-/send-0.15.4.tgz#985faa3e284b0273c793364a35c6737bd93905b9"
dependencies:
debug "2.6.8"
depd "~1.1.1"
destroy "~1.0.4"
encodeurl "~1.0.1"
escape-html "~1.0.3"
etag "~1.8.0"
fresh "0.5.0"
http-errors "~1.6.2"
mime "1.3.4"
ms "2.0.0"
on-finished "~2.3.0"
range-parser "~1.2.0"
statuses "~1.3.1"
serve-index@^1.7.2:
version "1.9.0"
resolved "https://registry.yarnpkg.com/serve-index/-/serve-index-1.9.0.tgz#d2b280fc560d616ee81b48bf0fa82abed2485ce7"
@ -5666,6 +5825,15 @@ serve-static@1.12.3:
parseurl "~1.3.1"
send "0.15.3"
serve-static@1.12.4:
version "1.12.4"
resolved "https://registry.yarnpkg.com/serve-static/-/serve-static-1.12.4.tgz#9b6aa98eeb7253c4eedc4c1f6fdbca609901a961"
dependencies:
encodeurl "~1.0.1"
escape-html "~1.0.3"
parseurl "~1.3.1"
send "0.15.4"
serve-static@~1.11.2:
version "1.11.2"
resolved "https://registry.yarnpkg.com/serve-static/-/serve-static-1.11.2.tgz#2cf9889bd4435a320cc36895c9aa57bd662e6ac7"
@ -6203,6 +6371,17 @@ tiny-lr@1.0.3:
object-assign "^4.1.0"
qs "^6.2.0"
tiny-lr@^1.0.5:
version "1.0.5"
resolved "https://registry.yarnpkg.com/tiny-lr/-/tiny-lr-1.0.5.tgz#21f40bf84ebd1f853056680375eef1670c334112"
dependencies:
body "^5.1.0"
debug "~2.6.7"
faye-websocket "~0.10.0"
livereload-js "^2.2.2"
object-assign "^4.1.0"
qs "^6.4.0"
tmp@0.0.24:
version "0.0.24"
resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.0.24.tgz#d6a5e198d14a9835cc6f2d7c3d9e302428c8cf12"