fix conflicts

This commit is contained in:
Matias Alejo Garcia 2014-06-24 13:02:25 -03:00
commit f1257d054c
49 changed files with 1015 additions and 755 deletions

26
API.js
View file

@ -6,7 +6,7 @@ var API = function(opts) {
API.prototype._init = function(opts) { API.prototype._init = function(opts) {
var self = this; var self = this;
opts = opts || {}; opts = opts || {};
self.opts = opts; self.opts = opts;
@ -53,12 +53,12 @@ API.prototype._command = function(command, args, callback) {
if (typeof self["_cmd_" + command] == 'function') { if (typeof self["_cmd_" + command] == 'function') {
var f = API.prototype[command]; var f = API.prototype[command];
if (f.argTypes[f.argTypes.length-1][1] == 'function') if (f.argTypes[f.argTypes.length - 1][1] == 'function')
return self["_cmd_" + command].apply(self, args.concat([callback])); return self["_cmd_" + command].apply(self, args.concat([callback]));
else else
return callback(null, self["_cmd_" + command].apply(self, args)); return callback(null, self["_cmd_" + command].apply(self, args));
}; };
return callback(new Error('invalid command')); return callback(new Error('invalid command'));
}; };
@ -66,9 +66,9 @@ API._checkArgTypes = function(command, args) {
var f = API.prototype[command]; var f = API.prototype[command];
if (f.argTypes.length != args.length) { if (f.argTypes.length != args.length) {
//if the function doesn't have a callback //if the function doesn't have a callback
if (!(f.argTypes.length == args.length + 1 && f.argTypes[f.argTypes.length-1][1] == 'function')) if (!(f.argTypes.length == args.length + 1 && f.argTypes[f.argTypes.length - 1][1] == 'function'))
return false; return false;
} }
@ -98,7 +98,7 @@ API.prototype._cmd_echo = function(str, callback) {
API.prototype.echo = decorate('echo', [ API.prototype.echo = decorate('echo', [
['str', 'string'], ['str', 'string'],
['callback', 'function'] ['callback', 'function']
]); ]);
API.prototype._cmd_echoNumber = function(num, callback) { API.prototype._cmd_echoNumber = function(num, callback) {
var self = this; var self = this;
@ -109,7 +109,7 @@ API.prototype._cmd_echoNumber = function(num, callback) {
API.prototype.echoNumber = decorate('echoNumber', [ API.prototype.echoNumber = decorate('echoNumber', [
['num', 'number'], ['num', 'number'],
['callback', 'function'] ['callback', 'function']
]); ]);
API.prototype._cmd_echoObject = function(obj, callback) { API.prototype._cmd_echoObject = function(obj, callback) {
var self = this; var self = this;
@ -120,7 +120,7 @@ API.prototype._cmd_echoObject = function(obj, callback) {
API.prototype.echoObject = decorate('echoObject', [ API.prototype.echoObject = decorate('echoObject', [
['obj', 'object'], ['obj', 'object'],
['callback', 'function'] ['callback', 'function']
]); ]);
/* /*
API.prototype.getBalance = function(callback) { API.prototype.getBalance = function(callback) {
@ -140,7 +140,7 @@ API.prototype._cmd_getArgTypes = function(command, callback) {
if (command[0] == '_' || typeof API.prototype[command] != 'function') if (command[0] == '_' || typeof API.prototype[command] != 'function')
return callback(new Error('Invalid command')); return callback(new Error('Invalid command'));
var argTypes = API.prototype[command].argTypes; var argTypes = API.prototype[command].argTypes;
return callback(null, argTypes); return callback(null, argTypes);
@ -149,7 +149,7 @@ API.prototype._cmd_getArgTypes = function(command, callback) {
API.prototype.getArgTypes = decorate('getArgTypes', [ API.prototype.getArgTypes = decorate('getArgTypes', [
['command', 'string'], ['command', 'string'],
['callback', 'function'] ['callback', 'function']
]); ]);
API.prototype._cmd_getCommands = function(callback) { API.prototype._cmd_getCommands = function(callback) {
var self = this; var self = this;
@ -167,7 +167,7 @@ API.prototype._cmd_getCommands = function(callback) {
API.prototype.getCommands = decorate('getCommands', [ API.prototype.getCommands = decorate('getCommands', [
['callback', 'function'] ['callback', 'function']
]); ]);
API.prototype._cmd_getWallets = function(callback) { API.prototype._cmd_getWallets = function(callback) {
var self = this; var self = this;
@ -177,7 +177,7 @@ API.prototype._cmd_getWallets = function(callback) {
API.prototype.getWallets = decorate('getWallets', [ API.prototype.getWallets = decorate('getWallets', [
['callback', 'function'] ['callback', 'function']
]); ]);
API.prototype._cmd_help = function(callback) { API.prototype._cmd_help = function(callback) {
this._cmd_getCommands.apply(this, arguments); this._cmd_getCommands.apply(this, arguments);
@ -185,6 +185,6 @@ API.prototype._cmd_help = function(callback) {
API.prototype.help = decorate('help', [ API.prototype.help = decorate('help', [
['callback', 'function'] ['callback', 'function']
]); ]);
module.exports = require('soop')(API); module.exports = require('soop')(API);

View file

@ -15,7 +15,7 @@ module.exports = function(grunt) {
stderr: true stderr: true
}, },
command: grunt.option('target') === 'dev' ? command: grunt.option('target') === 'dev' ?
'node ./util/build.js -d ' : 'node ./util/build.js ' 'node ./util/build.js -d ' : 'node ./util/build.js '
} }
}, },
watch: { watch: {
@ -46,6 +46,6 @@ module.exports = function(grunt) {
} }
}); });
grunt.registerTask('default', ['shell','watch']); grunt.registerTask('default', ['shell', 'watch']);
}; };

4
app.js
View file

@ -1,6 +1,6 @@
var express = require('express'); var express = require('express');
var http = require('http'); var http = require('http');
var app = express(); var app = express();
app.start = function(port, callback) { app.start = function(port, callback) {

View file

@ -15,7 +15,7 @@ html, body {height: 100%;}
#main { #main {
overflow:auto; overflow:auto;
padding-bottom: 91px;} /* must be same height as the footer */ padding-bottom: 80px;} /* must be same height as the footer */
.main-home { .main-home {
padding-bottom: 28px !important; padding-bottom: 28px !important;
@ -37,9 +37,8 @@ html, body {height: 100%;}
#footer { #footer {
position: fixed; position: fixed;
margin-top: -96px; /* negative value of footer height */ margin-top: -80px; /* negative value of footer height */
height: 70px;
height: 80px;
clear:both; clear:both;
padding: 5px 2rem; padding: 5px 2rem;
bottom: 0; bottom: 0;
@ -65,7 +64,6 @@ html, body {height: 100%;}
.logo { .logo {
display: block; display: block;
height: 51px; height: 51px;
margin: 0 auto;
} }
.top-bar-section li:not(.has-form) a:not(.button) { .top-bar-section li:not(.has-form) a:not(.button) {
@ -173,12 +171,6 @@ h3 {
border-right: 2px dashed #E3E3E3; border-right: 2px dashed #E3E3E3;
} }
@media (max-width: 640px) {
.line-dashed-v {
border: none;
}
}
.line-dashed-h { .line-dashed-h {
margin: 1rem 0; margin: 1rem 0;
border-bottom: 2px dashed #E3E3E3; border-bottom: 2px dashed #E3E3E3;
@ -238,28 +230,13 @@ input[type=number]::-webkit-outer-spin-button {
line-height: inherit; line-height: inherit;
} }
@media (max-width: 641px) {
.hide_menu {
display: none;
}
.show_menu {
display: block;
}
.top-bar-section ul li {
width: 100%;
}
}
@media (max-width: 750px) {
.top-bar-section ul li>a {
font-size: 70%;
}
}
.new-address { .new-address {
width: 220px; width: 220px;
} }
.transactions button, .transactions .button {
padding: 0.5rem 2rem;
}
hr { margin: 2.25rem 0;} hr { margin: 2.25rem 0;}
@ -406,10 +383,13 @@ hr { margin: 2.25rem 0;}
margin-right: 10px; margin-right: 10px;
margin-bottom: 10px; margin-bottom: 10px;
border: 3px solid #eee; border: 3px solid #eee;
opacity: 0.3;
border-radius: 0.3rem;
} }
.box-setup-copay-required { .box-setup-copay-required {
border: 3px solid green; border: 3px solid #1ABC9C;
opacity: 1;
} }
.tx-copayers { .tx-copayers {
@ -655,52 +635,3 @@ ul.pagination li.current a:hover, ul.pagination li.current a:focus {
color:white; color:white;
} }
@media only screen and (max-width: 40em) {
#main, .header-content {
font-size: 80%;
line-height: 140%;
}
.top-bar {
background: #1ABC9C;
}
.header-content {
padding: 0.3rem 0rem 1rem 0.4rem;
}
.logo {
background-size: 90px 44px !important;
float: left;
margin-top: 0.7rem;
width: 130px;
}
.header-content .small-9 {
text-align: right !important;
}
.header-content .line-dashed-v {
border: none !important;
}
.box-backup {
margin: 0.6rem 0;
}
#footer {
font-size: 80%;
padding: 0.2rem 0.5rem;
}
.box-status {
height: 71px;
}
.box-copayers figure {
height: 71px;
width: 71px;
}
}

50
css/mobile.css Normal file
View file

@ -0,0 +1,50 @@
@media (max-width: 1024px) {
.logo {
background-size: 90px 44px !important;
height: 41px;
}
.header-content {
font-size: 70%;
line-height: 120%;
font-weight: normal;
}
.line-dashed-v {
border: none !important;
}
.top-bar-section ul li>a {
font-size: 70%;
}
}
@media (max-width: 640px) {
.hide_menu {
display: none;
}
.show_menu {
display: block;
}
.top-bar-section ul li {
width: 100%;
}
.top-bar-section ul li>a {
padding: 0 0 0 15px;
}
.top-bar {
background: #1ABC9C;
}
.header-content .small-7 {
text-align: right !important;
padding-top: 0;
}
.box-copayers figure {
height: 71px;
width: 71px;
}
}

View file

@ -175,10 +175,6 @@ small.has-error {
border-radius: 10px; border-radius: 10px;
} }
.transactions button, .transactions .button {
padding: 0.5rem 2rem;
}
button.radius, .button.radius { button.radius, .button.radius {
-webkit-border-radius: 5px; -webkit-border-radius: 5px;
border-radius: 5px; border-radius: 5px;
@ -325,3 +321,28 @@ input.ng-invalid-wallet-secret {
background: #fff; background: #fff;
} }
.tooltip {
background: #16A085;
color: #fff;
font-weight: normal;
font-size: 12px;
padding: 3px 5px;
-webkit-border-radius: 3px;
-moz-border-radius: 3px;
border-radius: 3px;
border: 1px solid #16A085;
}
.tooltip>.nub {
border-color:transparent transparent #16A085 transparent;
}
.tooltip.tip-top>.nub {
border-color:#16A085 transparent transparent transparent;
}
.tooltip.tip-right>.nub {
border-color:transparent #16A085 transparent transparent;
}
.tooltip.tip-left>.nub {
border-color:transparent transparent transparent #16A085;
}

View file

@ -9,6 +9,7 @@
<link rel="stylesheet" href="css/foundation-icons.css"> <link rel="stylesheet" href="css/foundation-icons.css">
<link rel="stylesheet" href="lib/angular/angular-csp.css"> <link rel="stylesheet" href="lib/angular/angular-csp.css">
<link rel="stylesheet" href="css/main.css"> <link rel="stylesheet" href="css/main.css">
<link rel="stylesheet" href="css/mobile.css">
<link rel="shortcut icon" href="img/favicon.ico"> <link rel="shortcut icon" href="img/favicon.ico">
</head> </head>
<body ng-cloak class="ng-cloak"> <body ng-cloak class="ng-cloak">
@ -16,10 +17,10 @@
<div data-ng-init="init()" data-ng-controller="HeaderController"> <div data-ng-init="init()" data-ng-controller="HeaderController">
<div class="header"> <div class="header">
<div class="header-content"> <div class="header-content">
<div class="large-3 medium-3 small-3 columns"> <div class="large-3 medium-3 small-5 columns">
<span class="logo"></span> <span class="logo"></span>
</div> </div>
<div class="large-9 medium-9 small-9 columns text-center p10t" ng-show="$root.wallet"> <div class="large-9 medium-9 small-7 columns text-center p10t" ng-show="$root.wallet && $root.wallet.publicKeyRing.isComplete()">
<div class="large-4 medium-4 columns line-dashed-v"> <div class="large-4 medium-4 columns line-dashed-v">
<a href="#/addresses" class="has-tip" tooltip-placement="bottom" tooltip="ID: {{$root.wallet.id}}"> <a href="#/addresses" class="has-tip" tooltip-placement="bottom" tooltip="ID: {{$root.wallet.id}}">
<strong><span>{{$root.wallet.getName()}}</span></strong> <strong><span>{{$root.wallet.getName()}}</span></strong>
@ -31,26 +32,26 @@
ng-click="signout()"><i class="fi-power"></i></a> ng-click="signout()"><i class="fi-power"></i></a>
</div> </div>
<div class="large-4 medium-4 columns line-dashed-v"> <div class="large-4 medium-4 columns line-dashed-v">
Balance<br> Balance<br class="hide-for-small">
<span ng-if="$root.updatingBalance"> <span ng-if="$root.updatingBalance">
<i class="fi-bitcoin-circle icon-rotate spinner"></i> <i class="fi-bitcoin-circle icon-rotate spinner"></i>
</span> </span>
<span ng-if="!$root.updatingBalance" tooltip="{{totalBalanceBTC}} BTC" tooltip-trigger="mouseenter" tooltip-placement="bottom">{{totalBalance || 0 |number}} {{$root.unitName}} <span ng-if="!$root.updatingBalance" data-options="disable_for_touch:true" tooltip="{{totalBalanceBTC}} BTC" tooltip-trigger="mouseenter" tooltip-placement="bottom">{{totalBalance || 0 |number}} {{$root.unitName}}
</span> </span>
</div> </div>
<div class="large-4 medium-4 columns"> <div class="large-4 medium-4 columns">
Available to Spend<br> Available to Spend<br class="hide-for-small">
<span ng-if="$root.updatingBalance"> <span ng-if="$root.updatingBalance">
<i class="fi-bitcoin-circle icon-rotate spinner"></i> <i class="fi-bitcoin-circle icon-rotate spinner"></i>
</span> </span>
<span ng-show="!$root.updatingBalance" tooltip="{{availableBalanceBTC}} BTC" tooltip-trigger="mouseenter" tooltip-placement="bottom">{{availableBalance || 0|number}} {{$root.unitName}} <span ng-show="!$root.updatingBalance" data-options="disable_for_touch:true" tooltip="{{availableBalanceBTC}} BTC" tooltip-trigger="mouseenter" tooltip-placement="bottom">{{availableBalance || 0|number}} {{$root.unitName}}
</span> </span>
</div> </div>
</div> </div>
</div> </div>
<nav class="top-bar" data-topbar ng-show="$root.wallet"> <nav class="top-bar" data-topbar ng-show="$root.wallet && $root.wallet.publicKeyRing.isComplete()">
<ul class="title-area"> <ul class="title-area">
<li class="name"></li> <li class="name"></li>
<li class="toggle-topbar menu-icon"> <li class="toggle-topbar menu-icon">
@ -99,9 +100,9 @@
</div> </div>
</div> </div>
<div class="row"> <div ng-if='$root.wallet && !$root.wallet.publicKeyRing.isComplete() && !loading'>
<div ng-if='$root.wallet && !$root.wallet.publicKeyRing.isComplete() && !loading'> <div class="row">
<div class="medium-12 small-12 columns"> <div class="large-12 medium-12 small-12 columns">
<div class="alert-box secondary radius" data-alert> <div class="alert-box secondary radius" data-alert>
<i class="fi-info"></i> <i class="fi-info"></i>
Not all copayers have joined your wallet yet. Not all copayers have joined your wallet yet.
@ -114,25 +115,36 @@
yet to join. yet to join.
</div> </div>
</div> </div>
</div>
<div class="medium-12 small-12 columns "> <div class="row">
<div class="panel radius m30v"> <div class="large-12 medium-12 small-12 columns ">
<h3 class="m15b">Share this secret with your other copayers <div class="panel radius m30v">
<small> for them to join your wallet</small> <h3 class="m15b">Share this secret with your other copayers
</h3> <small> for them to join your wallet</small>
<div class="row"> </h3>
<div class="large-9 medium-12 small-12 columns line-dashed-v text-gray"> <div class="row">
<div class="panel input"> <div class="large-9 medium-12 small-12 columns line-dashed-v text-gray">
<p class="ellipsis text-gray">{{$root.wallet.getSecret()}}</p> <div class="panel input">
</div> <p class="ellipsis text-gray">{{$root.wallet.getSecret()}}</p>
</div>
<div class="large-3 columns hide-for-medium hide-for-small" ng-show="$root.wallet">
<h5 class="m0">{{$root.wallet.getName()}}</h5>
<p class="text-gray">{{$root.wallet.requiredCopayers}}-of-{{$root.wallet.totalCopayers}} wallet</p>
</div> </div>
</div> </div>
<div class="large-3 columns hide-for-medium hide-for-small" ng-show="$root.wallet">
<h5 class="m0">{{$root.wallet.getName()}}</h5>
<p class="text-gray">{{$root.wallet.requiredCopayers}}-of-{{$root.wallet.totalCopayers}} wallet</p>
</div>
</div> </div>
</div> </div>
</div>
</div>
<div class="row">
<div class="large-12 medium-12 small-12 columns ">
<div class="box-setup-copayers">
<div class="box-setup-copayers-fix">
<img class="box-setup-copay" ng-repeat="i in getNumber($root.wallet.totalCopayers) track by $index" src="./img/satoshi.gif" alt="Copayer {{$index+1}}-{{totalCopayers}}" ng-class="{'box-setup-copay-required': ($index+1) <= $root.wallet.publicKeyRing.registeredCopayers()}">
</div>
</div>
</div>
</div> </div>
</div> </div>
@ -140,12 +152,12 @@
<div notifications="middle right"></div> <div notifications="middle right"></div>
<div id="main" class="row" ng-class="{'main-home': !$root.wallet}"> <div id="main" class="row" ng-class="{'main-home': !$root.wallet || !$root.wallet.publicKeyRing.isComplete()}">
<div class="large-12 columns" ng-view></div> <div class="large-12 columns" ng-view></div>
</div> </div>
</div> </div>
<div id="footer" data-ng-controller="FooterController" ng-class="{'footer-home': !$root.wallet}"> <div id="footer" data-ng-controller="FooterController" ng-class="{'footer-home': !$root.wallet || !$root.wallet.publicKeyRing.isComplete()}">
<link rel="stylesheet" ng-href="{{theme}}"> <link rel="stylesheet" ng-href="{{theme}}">
<div ng-show="!$root.wallet"> <div ng-show="!$root.wallet">
<div class="large-12 columns text-left"> <div class="large-12 columns text-left">
@ -153,8 +165,20 @@
<small>v{{version}}</small> <small>v{{version}}</small>
</div> </div>
</div> </div>
<div class="p10t" ng-show="$root.wallet"> <div ng-show="$root.wallet && !$root.wallet.publicKeyRing.isComplete()">
<div class="large-3 medium-3 hide-for-small columns"> <div class="large-6 medium-6 small-6 columns">
<strong>{{$root.wallet.getName()}}</strong>
{{$root.wallet.requiredCopayers}}-of-{{$root.wallet.totalCopayers}}
<small ng-if="$root.wallet.getNetworkName()=='livenet'">[LIVENET]</small>
<small ng-if="$root.wallet.getNetworkName()=='testnet'">[TESTNET]</small>
</div>
<div class="large-6 medium-6 small-6 columns text-right">
Copay
<small>v{{version}}</small>
</div>
</div>
<div ng-show="$root.wallet && $root.wallet.publicKeyRing.isComplete()">
<div class="large-3 medium-4 hide-for-small columns">
<div> <div>
<strong>{{$root.wallet.getName()}}</strong> <strong>{{$root.wallet.getName()}}</strong>
</div> </div>
@ -171,8 +195,7 @@
<a class="size-12" ng-click="change_theme(th)" ng-repeat="th in themes">{{th}} {{$last ? '' : '&middot; '}}</a> <a class="size-12" ng-click="change_theme(th)" ng-repeat="th in themes">{{th}} {{$last ? '' : '&middot; '}}</a>
</div> </div>
</div> </div>
<div class="large-9 medium-9 small-9 columns"> <div class="large-9 medium-8 small-12 columns">
<a href="#/addresses" > </a>
<!-- <div class="bottom&#45;copay" ng&#45;repeat="c in $root.wallet.getRegisteredPeerIds()" class="has&#45;tip" tooltip&#45;popup&#45;delay="1000" tooltip&#45;placement="top" tooltip="{{c.nick}}"> --> <!-- <div class="bottom&#45;copay" ng&#45;repeat="c in $root.wallet.getRegisteredPeerIds()" class="has&#45;tip" tooltip&#45;popup&#45;delay="1000" tooltip&#45;placement="top" tooltip="{{c.nick}}"> -->
<div class="bottom-copay" ng-repeat="c in $root.wallet.getRegisteredPeerIds()"> <div class="bottom-copay" ng-repeat="c in $root.wallet.getRegisteredPeerIds()">
<video ng-if="$root.videoInfo[c.peerId]" <video ng-if="$root.videoInfo[c.peerId]"
@ -312,7 +335,7 @@
</label> </label>
</div> </div>
<div class="small-12 medium-6 large-6 columns"> <div class="small-12 medium-6 large-6 columns">
<label>Your Wallet Password <small class="has-tip" tooltip="doesn't need to be shared">Required</small> <label>Your Wallet Password <small data-options="disable_for_touch:true" class="has-tip" tooltip="doesn't need to be shared">Required</small>
<input type="password" placeholder="Choose your password" class="form-control" <input type="password" placeholder="Choose your password" class="form-control"
ng-model="$parent.walletPassword" check-strength="passwordStrength" tooltip-html-unsafe="Password strength: {{passwordStrength}}<br/><small>Tip: Use lower and uppercase, numbers and symbols</small>" tooltip-trigger="focus" required> ng-model="$parent.walletPassword" check-strength="passwordStrength" tooltip-html-unsafe="Password strength: {{passwordStrength}}<br/><small>Tip: Use lower and uppercase, numbers and symbols</small>" tooltip-trigger="focus" required>
</label> </label>
@ -390,7 +413,7 @@
</span> </span>
</a> </a>
<a class="secondary radius" ng-click="showAll=!showAll" ng-show="(addresses|withoutFunds) > 1"> <a class="secondary radius" ng-click="showAll=!showAll" ng-show="addresses.length != (addresses|limitAddress).length">
<span ng-if="!showAll">Show all</span> <span ng-if="!showAll">Show all</span>
<span ng-if="showAll">Show less</span> <span ng-if="showAll">Show less</span>
</a> </a>
@ -652,6 +675,7 @@
<div class="row collapse"> <div class="row collapse">
<label for="amount">Amount <label for="amount">Amount
<small ng-hide="!sendForm.amount.$pristine">required</small> <small ng-hide="!sendForm.amount.$pristine">required</small>
<i class="fi-arrow-up" title="Send all funds" ng-click="topAmount()"></i>
<small class="is-valid" ng-show="!sendForm.amount.$invalid && !sendForm.amount.$pristine">Valid</small> <small class="is-valid" ng-show="!sendForm.amount.$invalid && !sendForm.amount.$pristine">Valid</small>
<small class="has-error" ng-show="sendForm.amount.$invalid && !sendForm.amount.$pristine && !notEnoughAmount"> <small class="has-error" ng-show="sendForm.amount.$invalid && !sendForm.amount.$pristine && !notEnoughAmount">
Not valid Not valid
@ -687,14 +711,14 @@
</div> </div>
<div class="row"> <div class="row">
<div class="large-12 medium-6 columns"> <div class="large-12 columns">
<div class="row collapse"> <div class="row collapse">
<label for="comment">Note <label for="comment">Note
<small ng-hide="!sendForm.comment.$pristine">optional</small> <small ng-hide="!sendForm.comment.$pristine">optional</small>
<small class="is-valid" ng-show="!sendForm.comment.$invalid && !sendForm.comment.$pristine">valid!</small> <small class="is-valid" ng-show="!sendForm.comment.$invalid && !sendForm.comment.$pristine">valid!</small>
<small class="has-error" ng-show="sendForm.comment.$invalid && !sendForm.comment.$pristine">too long!</small> <small class="has-error" ng-show="sendForm.comment.$invalid && !sendForm.comment.$pristine">too long!</small>
</label> </label>
<div class="small-12 columns"> <div class="large-12 columns">
<textarea id="comment" ng-disabled="loading" <textarea id="comment" ng-disabled="loading"
name="comment" placeholder="Leave a private message to your copayers" ng-model="commentText" ng-maxlength="100"></textarea> name="comment" placeholder="Leave a private message to your copayers" ng-model="commentText" ng-maxlength="100"></textarea>
</div> </div>
@ -714,7 +738,7 @@
<div class="medium-8 medium-centered large-8 large-centered columns"> <div class="medium-8 medium-centered large-8 large-centered columns">
<hr> <hr>
<h3>Address Book</h3> <h3>Address Book</h3>
<p class="text-gray" ng-hide="showAddressBook()">Empry. Create some alias for addresses</p> <p class="text-gray" ng-hide="showAddressBook()">Empty. Create an alias for your addresses</p>
<table ng-show="showAddressBook()"> <table ng-show="showAddressBook()">
<thead> <thead>
<tr> <tr>
@ -767,16 +791,16 @@
<script type="text/ng-template" id="backup.html"> <script type="text/ng-template" id="backup.html">
<div class="backup" ng-controller="BackupController"> <div class="backup" ng-controller="BackupController">
<h3>{{title}}</h3> <h3>{{title}}</h3>
<div class="row text-center"> <div class="row">
<div class="large-6 medium-6 columns"> <div class="large-4 medium-6 columns large-centered medium-centered">
<a class="panel radius box-backup" ng-click="download()"> <a class="panel radius box-backup text-center" ng-click="download()">
<i class="fi-download size-72"></i> <i class="fi-download size-72"></i>
<p> Download File </p> <p> Download File </p>
</a> </a>
</div> </div>
<div class="row text-center small" style="margin-top:70px"> </div>
<div class="button radius warning" ng-really-message="Are you sure to delete this wallet from this computer?" ng-really-click="deleteWallet()">Delete this wallet from this computer</div> <div class="row text-center">
</div> <div class="button radius warning small m30v" ng-really-message="Are you sure to delete this wallet from this computer?" ng-really-click="deleteWallet()">Delete this wallet from this computer</div>
</div> </div>
</div> </div>
</script> </script>

View file

@ -19,7 +19,7 @@ var log = function() {
// From the bundle // From the bundle
var copay = require('copay'); var copay = require('copay');
var copayApp = window.copayApp = angular.module('copayApp',[ var copayApp = window.copayApp = angular.module('copayApp', [
'ngRoute', 'ngRoute',
'angularMoment', 'angularMoment',
'mm.foundation', 'mm.foundation',
@ -34,4 +34,3 @@ angular.module('copayApp.filters', []);
angular.module('copayApp.services', []); angular.module('copayApp.services', []);
angular.module('copayApp.controllers', []); angular.module('copayApp.controllers', []);
angular.module('copayApp.directives', []); angular.module('copayApp.directives', []);

View file

@ -2,35 +2,34 @@
angular.module('copayApp.controllers').controller('FooterController', function($rootScope, $sce, $scope, $http) { angular.module('copayApp.controllers').controller('FooterController', function($rootScope, $sce, $scope, $http) {
if (config.themes && Array.isArray(config.themes) && config.themes[0]) { if (config.themes && Array.isArray(config.themes) && config.themes[0]) {
$scope.themes = config.themes; $scope.themes = config.themes;
} } else {
else { $scope.themes = ['default'];
$scope.themes = ['default']; }
$scope.theme = 'css/tpl-' + $scope.themes[0] + '.css';
$scope.change_theme = function(name) {
$scope.theme = 'css/tpl-' + name + '.css';
};
$scope.version = copay.version;
$scope.getVideoURL = function(copayer) {
if (config.disableVideo) return;
var vi = $rootScope.videoInfo[copayer]
if (!vi) return;
if ($rootScope.wallet.getOnlinePeerIDs().indexOf(copayer) === -1) {
// peer disconnected, remove his video
delete $rootScope.videoInfo[copayer]
return;
} }
$scope.theme = 'css/tpl-' + $scope.themes[0] + '.css'; var encoded = vi.url;
var url = decodeURI(encoded);
$scope.change_theme = function(name) { var trusted = $sce.trustAsResourceUrl(url);
$scope.theme = 'css/tpl-' + name + '.css'; return trusted;
}; };
$scope.version = copay.version; });
$scope.getVideoURL = function(copayer) {
if (config.disableVideo) return;
var vi = $rootScope.videoInfo[copayer]
if (!vi) return;
if ($rootScope.wallet.getOnlinePeerIDs().indexOf(copayer) === -1) {
// peer disconnected, remove his video
delete $rootScope.videoInfo[copayer]
return;
}
var encoded = vi.url;
var url = decodeURI(encoded);
var trusted = $sce.trustAsResourceUrl(url);
return trusted;
};
});

View file

@ -2,8 +2,7 @@
angular.module('copayApp.controllers').controller('HeaderController', angular.module('copayApp.controllers').controller('HeaderController',
function($scope, $rootScope, $location, notification, $http, controllerUtils) { function($scope, $rootScope, $location, notification, $http, controllerUtils) {
$scope.menu = [ $scope.menu = [{
{
'title': 'Addresses', 'title': 'Addresses',
'icon': 'fi-address-book', 'icon': 'fi-address-book',
'link': '#/addresses' 'link': '#/addresses'
@ -21,15 +20,27 @@ angular.module('copayApp.controllers').controller('HeaderController',
'link': '#/backup' 'link': '#/backup'
}]; }];
$http.get('https://api.github.com/repos/bitpay/copay/tags').success(function(data){ $scope.getNumber = function(num) {
var toInt = function (s) { return parseInt(s); }; return new Array(num);
}
$http.get('https://api.github.com/repos/bitpay/copay/tags').success(function(data) {
var toInt = function(s) {
return parseInt(s);
};
var latestVersion = data[0].name.replace('v', '').split('.').map(toInt); var latestVersion = data[0].name.replace('v', '').split('.').map(toInt);
var currentVersion = copay.version.split('.').map(toInt); var currentVersion = copay.version.split('.').map(toInt);
if (currentVersion[0] < latestVersion[0]){ if (currentVersion[0] < latestVersion[0]) {
$scope.updateVersion = {class: 'error', version:data[0].name}; $scope.updateVersion = {
class: 'error',
version: data[0].name
};
} else if (currentVersion[0] == latestVersion[0] && currentVersion[1] < latestVersion[1]) { } else if (currentVersion[0] == latestVersion[0] && currentVersion[1] < latestVersion[1]) {
$scope.updateVersion = {class: 'info', version:data[0].name}; $scope.updateVersion = {
} class: 'info',
version: data[0].name
};
}
}); });
$rootScope.unitName = config.unitName; $rootScope.unitName = config.unitName;
@ -55,7 +66,7 @@ angular.module('copayApp.controllers').controller('HeaderController',
$rootScope.$watch('receivedFund', function(receivedFund) { $rootScope.$watch('receivedFund', function(receivedFund) {
if (receivedFund) { if (receivedFund) {
var currentAddr; var currentAddr;
for(var i=0;i<$rootScope.addrInfos.length;i++) { for (var i = 0; i < $rootScope.addrInfos.length; i++) {
var addrinfo = $rootScope.addrInfos[i]; var addrinfo = $rootScope.addrInfos[i];
if (addrinfo.address.toString() == receivedFund[1] && !addrinfo.isChange) { if (addrinfo.address.toString() == receivedFund[1] && !addrinfo.isChange) {
currentAddr = addrinfo.address.toString(); currentAddr = addrinfo.address.toString();
@ -79,12 +90,12 @@ angular.module('copayApp.controllers').controller('HeaderController',
$scope.isActive = function(item) { $scope.isActive = function(item) {
if (item.link && item.link.replace('#','') == $location.path()) { if (item.link && item.link.replace('#', '') == $location.path()) {
return true; return true;
} }
return false; return false;
}; };
$scope.signout = function() { $scope.signout = function() {
logout(); logout();
$scope.clearFlashMessage(); $scope.clearFlashMessage();
@ -93,7 +104,7 @@ angular.module('copayApp.controllers').controller('HeaderController',
$scope.refresh = function() { $scope.refresh = function() {
var w = $rootScope.wallet; var w = $rootScope.wallet;
w.connectToAll(); w.connectToAll();
if ($rootScope.addrInfos.length > 0 ) { if ($rootScope.addrInfos.length > 0) {
controllerUtils.updateBalance(function() { controllerUtils.updateBalance(function() {
$rootScope.$digest(); $rootScope.$digest();
}); });

View file

@ -5,11 +5,14 @@ angular.module('copayApp.controllers').controller('ImportController',
$scope.title = 'Import a backup'; $scope.title = 'Import a backup';
var reader = new FileReader(); var reader = new FileReader();
var _importBackup = function(encryptedObj) { var _importBackup = function(encryptedObj) {
Passphrase.getBase64Async($scope.password, function(passphrase){ Passphrase.getBase64Async($scope.password, function(passphrase) {
walletFactory.import(encryptedObj, passphrase, function(err, w) { walletFactory.import(encryptedObj, passphrase, function(err, w) {
if (err) { if (err) {
$scope.loading = false; $scope.loading = false;
$rootScope.$flashMessage = { message: err.errMsg || 'Wrong password', type: 'error'}; $rootScope.$flashMessage = {
message: err.errMsg || 'Wrong password',
type: 'error'
};
$rootScope.$digest(); $rootScope.$digest();
return; return;
} }
@ -43,7 +46,10 @@ angular.module('copayApp.controllers').controller('ImportController',
$scope.import = function(form) { $scope.import = function(form) {
if (form.$invalid) { if (form.$invalid) {
$scope.loading = false; $scope.loading = false;
$rootScope.$flashMessage = { message: 'There is an error in the form. Please, try again', type: 'error'}; $rootScope.$flashMessage = {
message: 'There is an error in the form. Please, try again',
type: 'error'
};
return; return;
} }
@ -53,7 +59,10 @@ angular.module('copayApp.controllers').controller('ImportController',
if (!backupFile && !backupText) { if (!backupFile && !backupText) {
$scope.loading = false; $scope.loading = false;
$rootScope.$flashMessage = { message: 'Please, select your backup file or paste the text', type: 'error'}; $rootScope.$flashMessage = {
message: 'Please, select your backup file or paste the text',
type: 'error'
};
$scope.loading = false; $scope.loading = false;
return; return;
} }
@ -62,8 +71,7 @@ angular.module('copayApp.controllers').controller('ImportController',
if (backupFile) { if (backupFile) {
reader.readAsBinaryString(backupFile); reader.readAsBinaryString(backupFile);
} } else {
else {
_importBackup(backupText); _importBackup(backupText);
} }
}; };

View file

@ -33,7 +33,7 @@ angular.module('copayApp.controllers').controller('SendController',
$scope.submitForm = function(form) { $scope.submitForm = function(form) {
if (form.$invalid) { if (form.$invalid) {
$rootScope.$flashMessage = { $rootScope.$flashMessage = {
message: 'You can not send a proposal transaction. Please, try again', message: 'Unable to send a transaction proposal. Please, try again',
type: 'error' type: 'error'
}; };
return; return;
@ -207,7 +207,7 @@ angular.module('copayApp.controllers').controller('SendController',
templateUrl: 'addressBookModal.html', templateUrl: 'addressBookModal.html',
windowClass: 'tiny', windowClass: 'tiny',
controller: function($scope, $modalInstance) { controller: function($scope, $modalInstance) {
$scope.submitAddressBook = function(form) { $scope.submitAddressBook = function(form) {
if (form.$invalid) { if (form.$invalid) {
$rootScope.$flashMessage = { $rootScope.$flashMessage = {
@ -254,4 +254,8 @@ angular.module('copayApp.controllers').controller('SendController',
}); });
}; };
$scope.topAmount = function() {
var maxSat = ($rootScope.availableBalance * config.unitToSatoshi).toFixed(0) - bitcore.TransactionBuilder.FEE_PER_1000B_SAT;
$scope.amount = maxSat / config.unitToSatoshi;
};
}); });

View file

@ -51,7 +51,7 @@ angular.module('copayApp.controllers').controller('SetupController',
var updateRCSelect = function(n) { var updateRCSelect = function(n) {
$scope.requiredCopayers = (valid_pairs[[parseInt(n / 2 + 1), n]] > 0 || config.networkName === 'testnet') ? $scope.requiredCopayers = (valid_pairs[[parseInt(n / 2 + 1), n]] > 0 || config.networkName === 'testnet') ?
parseInt(n / 2 + 1) : 1; parseInt(n / 2 + 1) : 1;
$scope.RCValues = []; $scope.RCValues = [];
for (var m = 1; m <= n; m++) { for (var m = 1; m <= n; m++) {
if (valid_pairs[[m, n]] > 0 || config.networkName === 'testnet') { if (valid_pairs[[m, n]] > 0 || config.networkName === 'testnet') {

View file

@ -125,7 +125,7 @@ angular.module('copayApp.directives')
.directive('contact', function() { .directive('contact', function() {
return { return {
restrict: 'E', restrict: 'E',
link:function(scope, element, attrs) { link: function(scope, element, attrs) {
if (!scope.wallet) return; if (!scope.wallet) return;
var address = attrs.address; var address = attrs.address;
@ -137,7 +137,7 @@ angular.module('copayApp.directives')
element.append(address); element.append(address);
} }
} }
}; };
}) })
.directive('highlightOnChange', function() { .directive('highlightOnChange', function() {
return { return {

View file

@ -1,11 +1,13 @@
'use strict'; 'use strict';
angular.module('copayApp.filters', []) angular.module('copayApp.filters', [])
.filter('amTimeAgo', ['amMoment', function(amMoment) { .filter('amTimeAgo', ['amMoment',
return function(input) { function(amMoment) {
return amMoment.preprocessDate(input).fromNow(); return function(input) {
}; return amMoment.preprocessDate(input).fromNow();
}]) };
}
])
.filter('paged', function() { .filter('paged', function() {
return function(elements) { return function(elements) {
if (elements) { if (elements) {
@ -17,41 +19,15 @@ angular.module('copayApp.filters', [])
}) })
.filter('limitAddress', function() { .filter('limitAddress', function() {
return function(elements, showAll) { return function(elements, showAll) {
var addrs = []; if (elements.length <= 1 || showAll) {
if (elements.length > 0) { return elements;
if (showAll) {
return elements;
}
if (elements.length == 1) {
return elements;
}
else {
for (var i=0;i<elements.length;i++) {
if (!elements[i].isChange && (!elements[i].balance || elements[i].balance == 0)) {
addrs.push(elements[i]);
break;
}
}
for (var i=0;i<elements.length;i++) {
if (elements[i].balance && elements[i].balance > 0) {
addrs.push(elements[i]);
}
}
return addrs;
}
} }
// Show last 3 non-change addresses plus those with balance
var addrs = elements.filter(function(e, i) {
return (!e.isChange && i < 3) || (e.balance && e.balance > 0);
});
return addrs;
}; };
}) });
.filter('withoutFunds', function() {
return function(elements) {
var len = 0;
for (var i=0;i<elements.length;i++) {
if (!elements[i].balance || elements[i].balance == 0) {
len++;
}
}
return len;
};
})
;

View file

@ -168,15 +168,29 @@ Insight.prototype.checkActivity = function(addresses, cb) {
if (!addresses) throw new Error('address must be set'); if (!addresses) throw new Error('address must be set');
this.getTransactions(addresses, function onResult(txs) { this.getTransactions(addresses, function onResult(txs) {
var flatArray = function (xss) { return xss.reduce(function(r, xs) { return r.concat(xs); }, []); }; var flatArray = function(xss) {
var getInputs = function (t) { return t.vin.map(function (vin) { return vin.addr }); }; return xss.reduce(function(r, xs) {
var getOutputs = function (t) { return flatArray( return r.concat(xs);
t.vout.map(function (vout) { return vout.scriptPubKey.addresses; }) }, []);
);}; };
var getInputs = function(t) {
return t.vin.map(function(vin) {
return vin.addr
});
};
var getOutputs = function(t) {
return flatArray(
t.vout.map(function(vout) {
return vout.scriptPubKey.addresses;
})
);
};
var activityMap = new Array(addresses.length); var activityMap = new Array(addresses.length);
var activeAddress = flatArray(txs.map(function(t) { return getInputs(t).concat(getOutputs(t)); })); var activeAddress = flatArray(txs.map(function(t) {
activeAddress.forEach(function (addr) { return getInputs(t).concat(getOutputs(t));
}));
activeAddress.forEach(function(addr) {
var index = addresses.indexOf(addr); var index = addresses.indexOf(addr);
if (index != -1) activityMap[index] = true; if (index != -1) activityMap[index] = true;
}); });

View file

@ -1,17 +1,17 @@
'use strict'; 'use strict';
var imports = require('soop').imports(); var imports = require('soop').imports();
var bitcore = require('bitcore'); var bitcore = require('bitcore');
var HK = bitcore.HierarchicalKey; var HK = bitcore.HierarchicalKey;
var WalletKey = bitcore.WalletKey; var WalletKey = bitcore.WalletKey;
var networks = bitcore.networks; var networks = bitcore.networks;
var util = bitcore.util; var util = bitcore.util;
var Structure = require('./Structure'); var Structure = require('./Structure');
function PrivateKey(opts) { function PrivateKey(opts) {
opts = opts || {}; opts = opts || {};
this.network = opts.networkName === 'testnet' ? this.network = opts.networkName === 'testnet' ?
networks.testnet : networks.livenet; networks.testnet : networks.livenet;
var init = opts.extendedPrivateKeyString || this.network.name; var init = opts.extendedPrivateKeyString || this.network.name;
this.bip = opts.HK || new HK(init); this.bip = opts.HK || new HK(init);
@ -81,15 +81,19 @@ PrivateKey.prototype.getForPaths = function(paths) {
PrivateKey.prototype.getForPath = function(path) { PrivateKey.prototype.getForPath = function(path) {
var pk = this.privateKeyCache[path]; var pk = this.privateKeyCache[path];
if (!pk) { if (!pk) {
var derivedHK = this._getHK(path); var derivedHK = this._getHK(path);
pk = this.privateKeyCache[path] = derivedHK.eckey.private.toString('hex'); pk = this.privateKeyCache[path] = derivedHK.eckey.private.toString('hex');
} }
var wk = new WalletKey({network: this.network}); var wk = new WalletKey({
wk.fromObj({priv: pk}); network: this.network
});
wk.fromObj({
priv: pk
});
return wk; return wk;
}; };
PrivateKey.prototype.get = function(index,isChange) { PrivateKey.prototype.get = function(index, isChange) {
var path = Structure.FullBranch(index, isChange); var path = Structure.FullBranch(index, isChange);
return this.getForPath(path); return this.getForPath(path);
}; };
@ -99,11 +103,11 @@ PrivateKey.prototype.getAll = function(receiveIndex, changeIndex) {
throw new Error('Invalid parameters'); throw new Error('Invalid parameters');
var ret = []; var ret = [];
for(var i=0;i<receiveIndex; i++) { for (var i = 0; i < receiveIndex; i++) {
ret.push(this.get(i,false)); ret.push(this.get(i, false));
} }
for(var i=0; i<changeIndex; i++) { for (var i = 0; i < changeIndex; i++) {
ret.push(this.get(i,true)); ret.push(this.get(i, true));
} }
return ret; return ret;
}; };

View file

@ -77,7 +77,7 @@ TxProposal.prototype.mergeMetadata = function(v1, author) {
Object.keys(v1.seenBy).forEach(function(k) { Object.keys(v1.seenBy).forEach(function(k) {
if (!v0.seenBy[k]) { if (!v0.seenBy[k]) {
if (k != author) throw new Error('Non authoritative seenBy change by '+author); if (k != author) throw new Error('Non authoritative seenBy change by ' + author);
v0.seenBy[k] = v1.seenBy[k]; v0.seenBy[k] = v1.seenBy[k];
events.push({ events.push({
type: 'seen', type: 'seen',
@ -89,7 +89,7 @@ TxProposal.prototype.mergeMetadata = function(v1, author) {
Object.keys(v1.signedBy).forEach(function(k) { Object.keys(v1.signedBy).forEach(function(k) {
if (!v0.signedBy[k]) { if (!v0.signedBy[k]) {
if (k != author) throw new Error('Non authoritative signedBy change by '+author); if (k != author) throw new Error('Non authoritative signedBy change by ' + author);
v0.signedBy[k] = v1.signedBy[k]; v0.signedBy[k] = v1.signedBy[k];
events.push({ events.push({
type: 'signed', type: 'signed',
@ -101,7 +101,7 @@ TxProposal.prototype.mergeMetadata = function(v1, author) {
Object.keys(v1.rejectedBy).forEach(function(k) { Object.keys(v1.rejectedBy).forEach(function(k) {
if (!v0.rejectedBy[k]) { if (!v0.rejectedBy[k]) {
if (k != author) throw new Error('Non authoritative rejectedBy change by '+author); if (k != author) throw new Error('Non authoritative rejectedBy change by ' + author);
v0.rejectedBy[k] = v1.rejectedBy[k]; v0.rejectedBy[k] = v1.rejectedBy[k];
events.push({ events.push({
type: 'rejected', type: 'rejected',

View file

@ -44,7 +44,7 @@ function Wallet(opts) {
this.token = opts.token; this.token = opts.token;
this.tokenTime = opts.tokenTime; this.tokenTime = opts.tokenTime;
} }
this.verbose = opts.verbose; this.verbose = opts.verbose;
this.publicKeyRing.walletId = this.id; this.publicKeyRing.walletId = this.id;
this.txProposals.walletId = this.id; this.txProposals.walletId = this.id;
@ -142,12 +142,11 @@ Wallet.prototype._handleAddressBook = function(senderId, data, isInbound) {
this.log('RECV ADDRESSBOOK:', data); this.log('RECV ADDRESSBOOK:', data);
var rcv = data.addressBook; var rcv = data.addressBook;
var hasChange; var hasChange;
for(var key in rcv) { for (var key in rcv) {
if (!this.addressBook[key]) { if (!this.addressBook[key]) {
this.addressBook[key] = rcv[key]; this.addressBook[key] = rcv[key];
hasChange = true; hasChange = true;
} } else {
else {
if (rcv[key].createdTs > this.addressBook[key].createdTs) { if (rcv[key].createdTs > this.addressBook[key].createdTs) {
this.addressBook[key] = rcv[key]; this.addressBook[key] = rcv[key];
hasChange = true; hasChange = true;
@ -395,7 +394,7 @@ Wallet.prototype.sendAllTxProposals = function(recipients) {
Wallet.prototype.sendTxProposal = function(ntxid, recipients) { Wallet.prototype.sendTxProposal = function(ntxid, recipients) {
preconditions.checkArgument(ntxid); preconditions.checkArgument(ntxid);
preconditions.checkState(this.txProposals.txps[ntxid]); preconditions.checkState(this.txProposals.txps[ntxid]);
this.log('### SENDING txProposal '+ntxid+' TO:', recipients || 'All', this.txProposals); this.log('### SENDING txProposal ' + ntxid + ' TO:', recipients || 'All', this.txProposals);
this.network.send(recipients, { this.network.send(recipients, {
type: 'txProposal', type: 'txProposal',
txProposal: this.txProposals.txps[ntxid].toObj(), txProposal: this.txProposals.txps[ntxid].toObj(),
@ -771,7 +770,7 @@ Wallet.prototype.updateIndexes = function(callback) {
Wallet.prototype.deriveAddresses = function(index, amout, isChange) { Wallet.prototype.deriveAddresses = function(index, amout, isChange) {
var ret = new Array(amout); var ret = new Array(amout);
for(var i = 0; i < amout; i++) { for (var i = 0; i < amout; i++) {
ret[i] = this.publicKeyRing.getAddress(index + i, isChange).toString(); ret[i] = this.publicKeyRing.getAddress(index + i, isChange).toString();
} }
return ret; return ret;
@ -791,7 +790,7 @@ Wallet.prototype.indexDiscovery = function(start, change, gap, cb) {
// Optimize window to minimize the derivations. // Optimize window to minimize the derivations.
var scanWindow = (lastActive == -1) ? gap : gap - (scanIndex - lastActive) + 1; var scanWindow = (lastActive == -1) ? gap : gap - (scanIndex - lastActive) + 1;
var addresses = self.deriveAddresses(scanIndex, scanWindow, change); var addresses = self.deriveAddresses(scanIndex, scanWindow, change);
self.blockchain.checkActivity(addresses, function(err, actives){ self.blockchain.checkActivity(addresses, function(err, actives) {
if (err) throw err; if (err) throw err;
// Check for new activities in the newlly scanned addresses // Check for new activities in the newlly scanned addresses
@ -804,7 +803,9 @@ Wallet.prototype.indexDiscovery = function(start, change, gap, cb) {
next(); next();
}); });
}, },
function _while() { return hasActivity; }, function _while() {
return hasActivity;
},
function _finnaly(err) { function _finnaly(err) {
if (err) return cb(err); if (err) return cb(err);
cb(null, lastActive); cb(null, lastActive);

View file

@ -1,10 +1,10 @@
'use strict'; 'use strict';
var imports = require('soop').imports(); var imports = require('soop').imports();
var EventEmitter= imports.EventEmitter || require('events').EventEmitter; var EventEmitter = imports.EventEmitter || require('events').EventEmitter;
var bitcore = require('bitcore'); var bitcore = require('bitcore');
var util = bitcore.util; var util = bitcore.util;
var extend = require('util')._extend; var extend = require('util')._extend;
/* /*
* Emits * Emits
* 'connect' * 'connect'
@ -19,17 +19,17 @@ var extend = require('util')._extend;
*/ */
function Network(opts) { function Network(opts) {
var self = this; var self = this;
opts = opts || {}; opts = opts || {};
this.apiKey = opts.apiKey || 'lwjd5qra8257b9'; this.apiKey = opts.apiKey || 'lwjd5qra8257b9';
this.debug = opts.debug || 3; this.debug = opts.debug || 3;
this.maxPeers = opts.maxPeers || 10; this.maxPeers = opts.maxPeers || 10;
this.reconnectAttempts = opts.reconnectAttempts || 3; this.reconnectAttempts = opts.reconnectAttempts || 3;
this.sjclParams = opts.sjclParams || { this.sjclParams = opts.sjclParams || {
salt: 'f28bfb49ef70573c', salt: 'f28bfb49ef70573c',
iter:500, iter: 500,
mode:'ccm', mode: 'ccm',
ts:parseInt(64), ts: parseInt(64),
}; };
this.opts = {}; this.opts = {};
['config', 'port', 'host', 'path', 'debug', 'key', 'secure'].forEach(function(k) { ['config', 'port', 'host', 'path', 'debug', 'key', 'secure'].forEach(function(k) {
@ -47,11 +47,11 @@ Network.prototype.cleanUp = function() {
this.privkey = null; //TODO: hide privkey in a closure this.privkey = null; //TODO: hide privkey in a closure
this.copayerId = null; this.copayerId = null;
this.signingKey = null; this.signingKey = null;
this.allowedCopayerIds=null; this.allowedCopayerIds = null;
this.isInboundPeerAuth=[]; this.isInboundPeerAuth = [];
this.copayerForPeer={}; this.copayerForPeer = {};
this.connections={}; this.connections = {};
this.criticalErr=''; this.criticalErr = '';
if (this.peer) { if (this.peer) {
this.peer.disconnect(); this.peer.disconnect();
this.peer.destroy(); this.peer.destroy();
@ -62,7 +62,7 @@ Network.prototype.cleanUp = function() {
this.tries = 0; this.tries = 0;
}; };
Network.parent=EventEmitter; Network.parent = EventEmitter;
// Array helpers // Array helpers
Network._arrayDiff = function(a, b) { Network._arrayDiff = function(a, b) {
@ -99,9 +99,9 @@ Network._arrayRemove = function(el, array) {
}; };
Network.prototype.connectedCopayers = function() { Network.prototype.connectedCopayers = function() {
var ret =[]; var ret = [];
for(var i in this.connectedPeers){ for (var i in this.connectedPeers) {
var copayerId =this.copayerForPeer[this.connectedPeers[i]]; var copayerId = this.copayerForPeer[this.connectedPeers[i]];
if (copayerId) ret.push(copayerId); if (copayerId) ret.push(copayerId);
} }
return ret; return ret;
@ -126,7 +126,7 @@ Network.prototype._onClose = function(peerID) {
Network.prototype.connectToCopayers = function(copayerIds) { Network.prototype.connectToCopayers = function(copayerIds) {
var self = this; var self = this;
var arrayDiff= Network._arrayDiff(copayerIds, self.connectedCopayers()); var arrayDiff = Network._arrayDiff(copayerIds, self.connectedCopayers());
arrayDiff.forEach(function(copayerId) { arrayDiff.forEach(function(copayerId) {
if (self.allowedCopayerIds && !self.allowedCopayerIds[copayerId]) { if (self.allowedCopayerIds && !self.allowedCopayerIds[copayerId]) {
@ -146,7 +146,7 @@ Network.prototype._sendHello = function(copayerId) {
Network.prototype._addConnectedCopayer = function(copayerId, isInbound) { Network.prototype._addConnectedCopayer = function(copayerId, isInbound) {
var peerId = this.peerFromCopayer(copayerId); var peerId = this.peerFromCopayer(copayerId);
this._addCopayerMap(peerId,copayerId); this._addCopayerMap(peerId, copayerId);
Network._arrayPushOnce(peerId, this.connectedPeers); Network._arrayPushOnce(peerId, this.connectedPeers);
this.emit('connect', copayerId); this.emit('connect', copayerId);
}; };
@ -158,15 +158,15 @@ Network.prototype._onData = function(enchex, isInbound, peerId) {
var privkey = this.privkey; var privkey = this.privkey;
try { try {
var data = this._decrypt(privkey, encbuf); var data = this._decrypt(privkey, encbuf);
payload= JSON.parse(data); payload = JSON.parse(data);
} catch (e) { } catch (e) {
this._deletePeer(peerId); this._deletePeer(peerId);
return; return;
} }
if(isInbound && payload.type === 'hello') { if (isInbound && payload.type === 'hello') {
var payloadStr = JSON.stringify(payload); var payloadStr = JSON.stringify(payload);
if (this.allowedCopayerIds && !this.allowedCopayerIds[payload.copayerId]) { if (this.allowedCopayerIds && !this.allowedCopayerIds[payload.copayerId]) {
@ -179,13 +179,13 @@ Network.prototype._onData = function(enchex, isInbound, peerId) {
return; return;
} }
if ( !this.copayerForPeer[peerId] || (isInbound && !this.isInboundPeerAuth[peerId]) ) { if (!this.copayerForPeer[peerId] || (isInbound && !this.isInboundPeerAuth[peerId])) {
this._deletePeer(peerId); this._deletePeer(peerId);
return; return;
} }
var self=this; var self = this;
switch(payload.type) { switch (payload.type) {
case 'disconnect': case 'disconnect':
this._onClose(peerId); this._onClose(peerId);
break; break;
@ -206,21 +206,21 @@ Network.prototype._setupConnectionHandlers = function(dataConn, toCopayerId) {
var isInbound = toCopayerId ? false : true; var isInbound = toCopayerId ? false : true;
dataConn.on('open', function() { dataConn.on('open', function() {
if (!Network._inArray(dataConn.peer, self.connectedPeers) && if (!Network._inArray(dataConn.peer, self.connectedPeers) &&
!self.connections[dataConn.peer]) { !self.connections[dataConn.peer]) {
self.connections[dataConn.peer] = dataConn; self.connections[dataConn.peer] = dataConn;
// The connecting peer send hello // The connecting peer send hello
if(toCopayerId) { if (toCopayerId) {
self.emit('connected'); self.emit('connected');
self._sendHello(toCopayerId); self._sendHello(toCopayerId);
self._addConnectedCopayer(toCopayerId); self._addConnectedCopayer(toCopayerId);
} }
} }
}); });
dataConn.on('data', function(data) { dataConn.on('data', function(data) {
self._onData(data, isInbound, dataConn.peer); self._onData(data, isInbound, dataConn.peer);
}); });
@ -244,14 +244,14 @@ Network.prototype._setupPeerHandlers = function(openCallback) {
p.on('open', function() { p.on('open', function() {
self.connectedPeers = [self.peerId]; self.connectedPeers = [self.peerId];
self.copayerForPeer[self.peerId]= self.copayerId; self.copayerForPeer[self.peerId] = self.copayerId;
return openCallback(); return openCallback();
}); });
p.on('error', function(err) { p.on('error', function(err) {
console.log('RECV ERROR: ', err); //TODO console.log('RECV ERROR: ', err); //TODO
if (!err.message.match(/Could\snot\sconnect\sto peer/) ) { if (!err.message.match(/Could\snot\sconnect\sto peer/)) {
self.criticalError=err.message; self.criticalError = err.message;
} }
}); });
@ -270,11 +270,9 @@ Network.prototype._setupPeerHandlers = function(openCallback) {
Network.prototype._addCopayerMap = function(peerId, copayerId) { Network.prototype._addCopayerMap = function(peerId, copayerId) {
if (!this.copayerForPeer[peerId]) { if (!this.copayerForPeer[peerId]) {
if(Object.keys(this.copayerForPeer).length < this.maxPeers) { if (Object.keys(this.copayerForPeer).length < this.maxPeers) {
this.copayerForPeer[peerId]=copayerId; this.copayerForPeer[peerId] = copayerId;
} } else {}
else {
}
} }
}; };
@ -288,16 +286,16 @@ Network.prototype.setCopayerId = function(copayerId) {
throw new Error('network already started: can not change peerId') throw new Error('network already started: can not change peerId')
} }
this.copayerId = copayerId; this.copayerId = copayerId;
this.copayerIdBuf = new Buffer(copayerId,'hex'); this.copayerIdBuf = new Buffer(copayerId, 'hex');
this.peerId = this.peerFromCopayer(this.copayerId); this.peerId = this.peerFromCopayer(this.copayerId);
this._addCopayerMap(this.peerId,copayerId); this._addCopayerMap(this.peerId, copayerId);
}; };
// TODO cache this. // TODO cache this.
Network.prototype.peerFromCopayer = function(hex) { Network.prototype.peerFromCopayer = function(hex) {
var SIN = bitcore.SIN; var SIN = bitcore.SIN;
return new SIN(new Buffer(hex,'hex')).toString(); return new SIN(new Buffer(hex, 'hex')).toString();
}; };
Network.prototype.start = function(opts, openCallback) { Network.prototype.start = function(opts, openCallback) {
@ -317,7 +315,7 @@ Network.prototype.start = function(opts, openCallback) {
this.setCopayerId(opts.copayerId); this.setCopayerId(opts.copayerId);
var self = this; var self = this;
var setupPeer = function () { var setupPeer = function() {
if (self.connectedPeers.length > 0) return; // Already connected! if (self.connectedPeers.length > 0) return; // Already connected!
if (self.peer) { if (self.peer) {
self.peer.destroy(); self.peer.destroy();
@ -333,7 +331,7 @@ Network.prototype.start = function(opts, openCallback) {
return; return;
} }
if (self.criticalError && self.criticalError.match(/taken/)) { if (self.criticalError && self.criticalError.match(/taken/)) {
self.criticalError=' Looks like you are already connected to this wallet please close all other Copay Wallets ' self.criticalError = ' Looks like you are already connected to this wallet please close all other Copay Wallets '
} }
self.emit('serverError', self.criticalError); self.emit('serverError', self.criticalError);
self.cleanUp(); self.cleanUp();
@ -376,7 +374,7 @@ Network.prototype._sendToOne = function(copayerId, payload, sig, cb) {
Network.prototype.send = function(copayerIds, payload, cb) { Network.prototype.send = function(copayerIds, payload, cb) {
if (!payload) return cb(); if (!payload) return cb();
var self=this; var self = this;
if (!copayerIds) { if (!copayerIds) {
copayerIds = this.connectedCopayers(); copayerIds = this.connectedCopayers();
payload.isBroadcast = 1; payload.isBroadcast = 1;
@ -394,7 +392,7 @@ Network.prototype.send = function(copayerIds, payload, cb) {
copayerIds.forEach(function(copayerId) { copayerIds.forEach(function(copayerId) {
var copayerIdBuf = new Buffer(copayerId, 'hex'); var copayerIdBuf = new Buffer(copayerId, 'hex');
var encPayload = self._encrypt(copayerIdBuf, payloadBuf); var encPayload = self._encrypt(copayerIdBuf, payloadBuf);
self._sendToOne(copayerId, encPayload, sig, function () { self._sendToOne(copayerId, encPayload, sig, function() {
if (++i === l && typeof cb === 'function') cb(); if (++i === l && typeof cb === 'function') cb();
}); });
}); });
@ -417,16 +415,18 @@ Network.prototype.connectTo = function(copayerId) {
}; };
Network.prototype.lockIncommingConnections = function(allowedCopayerIdsArray) { Network.prototype.lockIncommingConnections = function(allowedCopayerIdsArray) {
this.allowedCopayerIds={}; this.allowedCopayerIds = {};
for(var i in allowedCopayerIdsArray) { for (var i in allowedCopayerIdsArray) {
this.allowedCopayerIds[ allowedCopayerIdsArray[i] ] = true; this.allowedCopayerIds[allowedCopayerIdsArray[i]] = true;
} }
}; };
Network.prototype.disconnect = function(cb, forced) { Network.prototype.disconnect = function(cb, forced) {
var self = this; var self = this;
self.closing = 1; self.closing = 1;
self.send(null, { type: 'disconnect' }, function(){ self.send(null, {
type: 'disconnect'
}, function() {
self.cleanUp(); self.cleanUp();
if (typeof cb === 'function') cb(); if (typeof cb === 'function') cb();
}); });

View file

@ -111,7 +111,7 @@ Storage.prototype.get = function(walletId, k) {
// set value for key // set value for key
Storage.prototype.set = function(walletId, k, v, callback) { Storage.prototype.set = function(walletId, k, v, callback) {
this.setGlobal(this._key(walletId,k), v, callback); this.setGlobal(this._key(walletId, k), v, callback);
}; };
// remove value for key // remove value for key
@ -143,6 +143,6 @@ Storage.prototype.getEncryptedObj = function(walletId) {
Storage.prototype.clearAll = function(callback) { Storage.prototype.clearAll = function(callback) {
this.data = {}; this.data = {};
this.save(callback); this.save(callback);
}; };
module.exports = require('soop')(Storage); module.exports = require('soop')(Storage);

View file

@ -2,8 +2,7 @@
var imports = require('soop').imports(); var imports = require('soop').imports();
function Storage() { function Storage() {}
}
Storage.prototype._read = function(k) { Storage.prototype._read = function(k) {
var ret; var ret;
@ -13,7 +12,7 @@ Storage.prototype._read = function(k) {
return ret; return ret;
}; };
Storage.prototype._write = function(k,v) { Storage.prototype._write = function(k, v) {
localStorage.setItem(k, JSON.stringify(v)); localStorage.setItem(k, JSON.stringify(v));
}; };
@ -27,7 +26,7 @@ Storage.prototype._getWalletKeys = function(walletId) {
if (walletId = split[0]) if (walletId = split[0])
keys.push(split[2]); keys.push(split[2]);
} }
} }
return keys; return keys;
}; };
@ -38,8 +37,8 @@ Storage.prototype.getGlobal = function(k) {
}; };
// set value for key // set value for key
Storage.prototype.setGlobal = function(k,v) { Storage.prototype.setGlobal = function(k, v) {
this._write(k,v); this._write(k, v);
}; };
// remove value for key // remove value for key
@ -54,44 +53,44 @@ Storage.prototype._key = function(walletId, k) {
}; };
// get value by key // get value by key
Storage.prototype.get = function(walletId, k) { Storage.prototype.get = function(walletId, k) {
return this.getGlobal(this._key(walletId,k)); return this.getGlobal(this._key(walletId, k));
}; };
// set value for key // set value for key
Storage.prototype.set = function(walletId, k,v) { Storage.prototype.set = function(walletId, k, v) {
this.setGlobal(this._key(walletId,k), v); this.setGlobal(this._key(walletId, k), v);
}; };
// remove value for key // remove value for key
Storage.prototype.remove = function(walletId, k) { Storage.prototype.remove = function(walletId, k) {
this.removeGlobal(this._key(walletId,k)); this.removeGlobal(this._key(walletId, k));
}; };
Storage.prototype.setName = function(walletId, name) { Storage.prototype.setName = function(walletId, name) {
this.setGlobal('nameFor::'+walletId, name); this.setGlobal('nameFor::' + walletId, name);
}; };
Storage.prototype.getName = function(walletId) { Storage.prototype.getName = function(walletId) {
return this.getGlobal('nameFor::'+walletId); return this.getGlobal('nameFor::' + walletId);
}; };
Storage.prototype.getWalletIds = function() { Storage.prototype.getWalletIds = function() {
var walletIds = []; var walletIds = [];
var uniq = {}; var uniq = {};
for (var i = 0; i < localStorage.length; i++) { for (var i = 0; i < localStorage.length; i++) {
var key = localStorage.key(i); var key = localStorage.key(i);
var split = key.split('::'); var split = key.split('::');
if (split.length == 2) { if (split.length == 2) {
var walletId = split[0]; var walletId = split[0];
if (walletId === 'nameFor') continue; if (walletId === 'nameFor') continue;
if (typeof uniq[walletId] === 'undefined' ) { if (typeof uniq[walletId] === 'undefined') {
walletIds.push(walletId); walletIds.push(walletId);
uniq[walletId] = 1; uniq[walletId] = 1;
} }
} }
} }
return walletIds; return walletIds;
}; };
@ -100,9 +99,9 @@ Storage.prototype.getWallets = function() {
var uniq = {}; var uniq = {};
var ids = this.getWalletIds(); var ids = this.getWalletIds();
for (var i in ids){ for (var i in ids) {
wallets.push({ wallets.push({
id:ids[i], id: ids[i],
name: this.getName(ids[i]), name: this.getName(ids[i]),
}); });
} }
@ -120,6 +119,6 @@ Storage.prototype.setFromObj = function(walletId, obj) {
// remove all values // remove all values
Storage.prototype.clearAll = function() { Storage.prototype.clearAll = function() {
localStorage.clear(); localStorage.clear();
}; };
module.exports = require('soop')(Storage); module.exports = require('soop')(Storage);

View file

@ -60,15 +60,14 @@ angular
.config(function($locationProvider) { .config(function($locationProvider) {
$locationProvider $locationProvider
.html5Mode(false); .html5Mode(false);
//.hashPrefix('!'); //.hashPrefix('!');
}) })
.run(function($rootScope, $location) { .run(function($rootScope, $location) {
$rootScope.$on('$routeChangeStart', function(event, next, current) { $rootScope.$on('$routeChangeStart', function(event, next, current) {
if (!util.supports.data) { if (!util.supports.data) {
$location.path('unsupported'); $location.path('unsupported');
} } else {
else {
if ((!$rootScope.wallet || !$rootScope.wallet.id) && next.validate) { if ((!$rootScope.wallet || !$rootScope.wallet.id) && next.validate) {
$location.path('signin'); $location.path('signin');
} }

View file

@ -6,7 +6,7 @@ var BackupService = function(notification) {
}; };
BackupService.prototype.getName = function(wallet) { BackupService.prototype.getName = function(wallet) {
return (wallet.name ? (wallet.name+'-') : '') + wallet.id; return (wallet.name ? (wallet.name + '-') : '') + wallet.id;
}; };
BackupService.prototype.download = function(wallet) { BackupService.prototype.download = function(wallet) {

View file

@ -3,19 +3,19 @@
// Detect mobile devices // Detect mobile devices
var isMobile = { var isMobile = {
Android: function() { Android: function() {
return !! navigator.userAgent.match(/Android/i); return !!navigator.userAgent.match(/Android/i);
}, },
BlackBerry: function() { BlackBerry: function() {
return !! navigator.userAgent.match(/BlackBerry/i); return !!navigator.userAgent.match(/BlackBerry/i);
}, },
iOS: function() { iOS: function() {
return !! navigator.userAgent.match(/iPhone|iPad|iPod/i); return !!navigator.userAgent.match(/iPhone|iPad|iPod/i);
}, },
Opera: function() { Opera: function() {
return !! navigator.userAgent.match(/Opera Mini/i); return !!navigator.userAgent.match(/Opera Mini/i);
}, },
Windows: function() { Windows: function() {
return !! navigator.userAgent.match(/IEMobile/i); return !!navigator.userAgent.match(/IEMobile/i);
}, },
any: function() { any: function() {
return (isMobile.Android() || isMobile.BlackBerry() || isMobile.iOS() || isMobile.Opera() || isMobile.Windows()); return (isMobile.Android() || isMobile.BlackBerry() || isMobile.iOS() || isMobile.Opera() || isMobile.Windows());

View file

@ -1,40 +1,61 @@
'use strict'; 'use strict';
angular.module('copayApp.services'). angular.module('copayApp.services').
factory('notification', ['$timeout',function($timeout){ factory('notification', ['$timeout',
function($timeout) {
var notifications = JSON.parse(localStorage.getItem('notifications')) || [], var notifications = JSON.parse(localStorage.getItem('notifications')) || [],
queue = []; queue = [];
var settings = { var settings = {
info: { duration: 5000, enabled: true }, info: {
funds: { duration: 5000, enabled: true }, duration: 5000,
warning: { duration: 5000, enabled: true }, enabled: true
error: { duration: 1e10, enabled: true }, },
success: { duration: 5000, enabled: true }, funds: {
progress: { duration: 0, enabled: true }, duration: 5000,
custom: { duration: 35000, enabled: true }, enabled: true
},
warning: {
duration: 5000,
enabled: true
},
error: {
duration: 1e10,
enabled: true
},
success: {
duration: 5000,
enabled: true
},
progress: {
duration: 0,
enabled: true
},
custom: {
duration: 35000,
enabled: true
},
details: true, details: true,
localStorage: false, localStorage: false,
html5Mode: false, html5Mode: false,
html5DefaultIcon: 'img/favicon.ico' html5DefaultIcon: 'img/favicon.ico'
}; };
function html5Notify(icon, title, content, ondisplay, onclose){ function html5Notify(icon, title, content, ondisplay, onclose) {
if(window.webkitNotifications.checkPermission() === 0){ if (window.webkitNotifications.checkPermission() === 0) {
if(!icon){ if (!icon) {
icon = 'img/favicon.ico'; icon = 'img/favicon.ico';
} }
var noti = window.webkitNotifications.createNotification(icon, title, content); var noti = window.webkitNotifications.createNotification(icon, title, content);
if(typeof ondisplay === 'function'){ if (typeof ondisplay === 'function') {
noti.ondisplay = ondisplay; noti.ondisplay = ondisplay;
} }
if(typeof onclose === 'function'){ if (typeof onclose === 'function') {
noti.onclose = onclose; noti.onclose = onclose;
} }
noti.show(); noti.show();
} } else {
else {
settings.html5Mode = false; settings.html5Mode = false;
} }
} }
@ -44,53 +65,50 @@ angular.module('copayApp.services').
/* ========== SETTINGS RELATED METHODS =============*/ /* ========== SETTINGS RELATED METHODS =============*/
disableHtml5Mode: function(){ disableHtml5Mode: function() {
settings.html5Mode = false; settings.html5Mode = false;
}, },
disableType: function(notificationType){ disableType: function(notificationType) {
settings[notificationType].enabled = false; settings[notificationType].enabled = false;
}, },
enableHtml5Mode: function(){ enableHtml5Mode: function() {
// settings.html5Mode = true; // settings.html5Mode = true;
settings.html5Mode = this.requestHtml5ModePermissions(); settings.html5Mode = this.requestHtml5ModePermissions();
}, },
enableType: function(notificationType){ enableType: function(notificationType) {
settings[notificationType].enabled = true; settings[notificationType].enabled = true;
}, },
getSettings: function(){ getSettings: function() {
return settings; return settings;
}, },
toggleType: function(notificationType){ toggleType: function(notificationType) {
settings[notificationType].enabled = !settings[notificationType].enabled; settings[notificationType].enabled = !settings[notificationType].enabled;
}, },
toggleHtml5Mode: function(){ toggleHtml5Mode: function() {
settings.html5Mode = !settings.html5Mode; settings.html5Mode = !settings.html5Mode;
}, },
requestHtml5ModePermissions: function(){ requestHtml5ModePermissions: function() {
if (window.webkitNotifications){ if (window.webkitNotifications) {
if (window.webkitNotifications.checkPermission() === 0) { if (window.webkitNotifications.checkPermission() === 0) {
return true; return true;
} } else {
else{ window.webkitNotifications.requestPermission(function() {
window.webkitNotifications.requestPermission(function(){ if (window.webkitNotifications.checkPermission() === 0) {
if(window.webkitNotifications.checkPermission() === 0){
settings.html5Mode = true; settings.html5Mode = true;
} } else {
else{
settings.html5Mode = false; settings.html5Mode = false;
} }
}); });
return false; return false;
} }
} } else {
else{
return false; return false;
} }
}, },
@ -98,38 +116,38 @@ angular.module('copayApp.services').
/* ============ QUERYING RELATED METHODS ============*/ /* ============ QUERYING RELATED METHODS ============*/
getAll: function(){ getAll: function() {
// Returns all notifications that are currently stored // Returns all notifications that are currently stored
return notifications; return notifications;
}, },
getQueue: function(){ getQueue: function() {
return queue; return queue;
}, },
/* ============== NOTIFICATION METHODS ==============*/ /* ============== NOTIFICATION METHODS ==============*/
info: function(title, content, userData){ info: function(title, content, userData) {
return this.awesomeNotify('info', 'info', title, content, userData); return this.awesomeNotify('info', 'info', title, content, userData);
}, },
funds: function(title, content, userData){ funds: function(title, content, userData) {
return this.awesomeNotify('funds', 'bitcoin', title, content, userData); return this.awesomeNotify('funds', 'bitcoin', title, content, userData);
}, },
error: function(title, content, userData){ error: function(title, content, userData) {
return this.awesomeNotify('error', 'x', title, content, userData); return this.awesomeNotify('error', 'x', title, content, userData);
}, },
success: function(title, content, userData){ success: function(title, content, userData) {
return this.awesomeNotify('success', 'check', title, content, userData); return this.awesomeNotify('success', 'check', title, content, userData);
}, },
warning: function(title, content, userData){ warning: function(title, content, userData) {
return this.awesomeNotify('warning', 'alert', title, content, userData); return this.awesomeNotify('warning', 'alert', title, content, userData);
}, },
awesomeNotify: function(type, icon, title, content, userData){ awesomeNotify: function(type, icon, title, content, userData) {
/** /**
* Supposed to wrap the makeNotification method for drawing icons using font-awesome * Supposed to wrap the makeNotification method for drawing icons using font-awesome
* rather than an image. * rather than an image.
@ -143,13 +161,13 @@ angular.module('copayApp.services').
return this.makeNotification(type, false, icon, title, content, userData); return this.makeNotification(type, false, icon, title, content, userData);
}, },
notify: function(image, title, content, userData){ notify: function(image, title, content, userData) {
// Wraps the makeNotification method for displaying notifications with images // Wraps the makeNotification method for displaying notifications with images
// rather than icons // rather than icons
return this.makeNotification('custom', image, true, title, content, userData); return this.makeNotification('custom', image, true, title, content, userData);
}, },
makeNotification: function(type, image, icon, title, content, userData){ makeNotification: function(type, image, icon, title, content, userData) {
var notification = { var notification = {
'type': type, 'type': type,
'image': image, 'image': image,
@ -161,16 +179,15 @@ angular.module('copayApp.services').
}; };
notifications.push(notification); notifications.push(notification);
if(settings.html5Mode){ if (settings.html5Mode) {
html5Notify(image, title, content, function(){ html5Notify(image, title, content, function() {
// inner on display function // inner on display function
}, function(){ }, function() {
// inner on close function // inner on close function
}); });
} } else {
else{
queue.push(notification); queue.push(notification);
$timeout(function removeFromQueueTimeout(){ $timeout(function removeFromQueueTimeout() {
queue.splice(queue.indexOf(notification), 1); queue.splice(queue.indexOf(notification), 1);
}, settings[type].duration); }, settings[type].duration);
@ -183,80 +200,77 @@ angular.module('copayApp.services').
/* ============ PERSISTENCE METHODS ============ */ /* ============ PERSISTENCE METHODS ============ */
save: function(){ save: function() {
// Save all the notifications into localStorage // Save all the notifications into localStorage
if(settings.localStorage){ if (settings.localStorage) {
localStorage.setItem('notifications', JSON.stringify(notifications)); localStorage.setItem('notifications', JSON.stringify(notifications));
} }
}, },
restore: function(){ restore: function() {
// Load all notifications from localStorage // Load all notifications from localStorage
}, },
clear: function(){ clear: function() {
notifications = []; notifications = [];
this.save(); this.save();
} }
}; };
}]). }
directive('notifications', function(notification, $compile){ ]).
/** directive('notifications', function(notification, $compile) {
* /**
* It should also parse the arguments passed to it that specify *
* its position on the screen like "bottom right" and apply those * It should also parse the arguments passed to it that specify
* positions as a class to the container element * its position on the screen like "bottom right" and apply those
* * positions as a class to the container element
* Finally, the directive should have its own controller for *
* handling all of the notifications from the notification service * Finally, the directive should have its own controller for
*/ * handling all of the notifications from the notification service
var html = */
'<div class="dr-notification-wrapper" ng-repeat="noti in queue">' + var html =
'<div class="dr-notification-close-btn" ng-click="removeNotification(noti)">' + '<div class="dr-notification-wrapper" ng-repeat="noti in queue">' +
'<i class="fi-x"></i>' + '<div class="dr-notification-close-btn" ng-click="removeNotification(noti)">' +
'</div>' + '<i class="fi-x"></i>' +
'<div class="dr-notification">' + '</div>' +
'<div class="dr-notification-image dr-notification-type-{{noti.type}}" ng-switch on="noti.image">' + '<div class="dr-notification">' +
'<i class="fi-{{noti.icon}}" ng-switch-when="false"></i>' + '<div class="dr-notification-image dr-notification-type-{{noti.type}}" ng-switch on="noti.image">' +
'<img ng-src="{{noti.image}}" ng-switch-default />' + '<i class="fi-{{noti.icon}}" ng-switch-when="false"></i>' +
'</div>' + '<img ng-src="{{noti.image}}" ng-switch-default />' +
'<div class="dr-notification-content">' + '</div>' +
'<h3 class="dr-notification-title">{{noti.title}}</h3>' + '<div class="dr-notification-content">' +
'<p class="dr-notification-text">{{noti.content}}</p>' + '<h3 class="dr-notification-title">{{noti.title}}</h3>' +
'</div>' + '<p class="dr-notification-text">{{noti.content}}</p>' +
'</div>' + '</div>' +
'</div>'; '</div>' +
'</div>';
function link(scope, element, attrs){ function link(scope, element, attrs) {
var position = attrs.notifications; var position = attrs.notifications;
position = position.split(' '); position = position.split(' ');
element.addClass('dr-notification-container'); element.addClass('dr-notification-container');
for(var i = 0; i < position.length ; i++){ for (var i = 0; i < position.length; i++) {
element.addClass(position[i]); element.addClass(position[i]);
}
} }
}
return { return {
restrict: 'A', restrict: 'A',
scope: {}, scope: {},
template: html, template: html,
link: link, link: link,
controller: ['$scope', function NotificationsCtrl( $scope ){ controller: ['$scope',
function NotificationsCtrl($scope) {
$scope.queue = notification.getQueue(); $scope.queue = notification.getQueue();
$scope.removeNotification = function(noti){ $scope.removeNotification = function(noti) {
$scope.queue.splice($scope.queue.indexOf(noti), 1); $scope.queue.splice($scope.queue.indexOf(noti), 1);
}; };
} }
] ]
}; };
}); });

View file

@ -1,4 +1,3 @@
'use strict'; 'use strict';
angular.module('copayApp.services').value('walletFactory', new copay.WalletFactory(config, copay.version)); angular.module('copayApp.services').value('walletFactory', new copay.WalletFactory(config, copay.version));

View file

@ -1,23 +1,24 @@
/* /*
** copay-shell integration ** copay-shell integration
*/ */
(function() { (function() {
/* /*
** This is a monkey patch for when Copay is running from ** This is a monkey patch for when Copay is running from
** within Copay-Shell (atom-shell). Since the renderer (the frontend) ** within Copay-Shell (atom-shell). Since the renderer (the frontend)
** receives context from Node.js, we get a `module.exports` contruct ** receives context from Node.js, we get a `module.exports` contruct
** available to us. Because of this, some libs (specifically Moment.js) ** available to us. Because of this, some libs (specifically Moment.js)
** attempt to assume their CommonJS form and bind to this. This causes ** attempt to assume their CommonJS form and bind to this. This causes
** there to be no references in the window to these libs, so let's trick ** there to be no references in the window to these libs, so let's trick
** the renderer into thinking that we are _not_ in a CommonJS environment. ** the renderer into thinking that we are _not_ in a CommonJS environment.
*/ */
if (typeof module !== 'undefined') module = { exports: null }; if (typeof module !== 'undefined') module = {
exports: null
};
// are we running in copay shell? // are we running in copay shell?
if (window.process && process.type === 'renderer') { if (window.process && process.type === 'renderer') {
window.cshell = initCopayShellBindings(); window.cshell = initCopayShellBindings();
} } else {
else {
return; return;
} }
@ -35,12 +36,14 @@
function initCopayShellBindings() { function initCopayShellBindings() {
var ipc = require('ipc'); var ipc = require('ipc');
var clipb = require('clipboard'); var clipb = require('clipboard');
// atom shell forces to implement the clipboard on our own - thanks obama. // atom shell forces to implement the clipboard on our own - thanks obama.
Mousetrap.stopCallback = function() { return false }; Mousetrap.stopCallback = function() {
return false
};
Mousetrap.bind('ctrl+c', function(e) { Mousetrap.bind('ctrl+c', function(e) {
clipb.writeText(window.getSelection().toString()); clipb.writeText(window.getSelection().toString());

View file

@ -53,7 +53,7 @@ module.exports = function(config) {
'test/lib/chai-should.js', 'test/lib/chai-should.js',
'test/lib/chai-expect.js', 'test/lib/chai-expect.js',
'test/mocks/FakeWallet.js', 'test/mocks/FakeWallet.js',
'test/mocha.conf.js', 'test/mocha.conf.js',
//test files //test files

View file

@ -6,7 +6,10 @@
var sys = require('sys') var sys = require('sys')
var exec = require('child_process').exec; var exec = require('child_process').exec;
function puts(error, stdout, stderr) { sys.puts(stdout) }
function puts(error, stdout, stderr) {
sys.puts(stdout)
}
function isNumber(n) { function isNumber(n) {
return !isNaN(parseInt(n)) && isFinite(n); return !isNaN(parseInt(n)) && isFinite(n);
@ -23,9 +26,9 @@ var N = parseInt(n_str);
var DEFAULT_PORT = process.env.DEFAULT_PORT || 3000; var DEFAULT_PORT = process.env.DEFAULT_PORT || 3000;
for (var i=0; i<N; i++) { for (var i = 0; i < N; i++) {
var port =(i+DEFAULT_PORT); var port = (i + DEFAULT_PORT);
console.log('Simulating copayer #'+(i+1)+' at http://localhost:'+port); console.log('Simulating copayer #' + (i + 1) + ' at http://localhost:' + port);
var command = 'PORT='+port+' npm start &' var command = 'PORT=' + port + ' npm start &'
exec(command, puts); exec(command, puts);
} }

View file

@ -1,5 +1,5 @@
var server = require('./app'); var server = require('./app');
var port = process.env.PORT || 3000; var port = process.env.PORT || 3000;
server.start(port, function(loc) { server.start(port, function(loc) {
console.log('Listening at: ' + loc); console.log('Listening at: ' + loc);

View file

@ -1,3 +1,5 @@
if (typeof window != 'undefined') { if (typeof window != 'undefined') {
window.mocha.setup({ timeout: 5000 }); window.mocha.setup({
timeout: 5000
});
} }

View file

@ -1,11 +1,9 @@
var imports = require('soop').imports();
var EventEmitter = imports.EventEmitter || require('events').EventEmitter;
var imports = require('soop').imports(); function Network(opts) {}
var EventEmitter= imports.EventEmitter || require('events').EventEmitter;
function Network(opts) { Network.parent = EventEmitter;
}
Network.parent=EventEmitter;
Network.prototype.start = function(opts, cb) { Network.prototype.start = function(opts, cb) {
// start! :D // start! :D
@ -34,10 +32,8 @@ Network.prototype.lockIncommingConnections = function() {
}; };
Network.prototype.getPeer = function() { Network.prototype.getPeer = function() {};
}; Network.prototype.connectToCopayers = function(cps) {};
Network.prototype.connectToCopayers = function(cps) {
};
Network.prototype.isOnline = function() { Network.prototype.isOnline = function() {
return true; return true;
}; };

View file

@ -6,8 +6,8 @@ var FakeWallet = function() {
'1CjPR7Z5ZSyWk6WtXvSFgkptmpoi4UM9BC': 1000 '1CjPR7Z5ZSyWk6WtXvSFgkptmpoi4UM9BC': 1000
}; };
this.name = 'myTESTwullet'; this.name = 'myTESTwullet';
this.addressBook = { this.addressBook = {
'2NFR2kzH9NUdp8vsXTB4wWQtTtzhpKxsyoJ' : { '2NFR2kzH9NUdp8vsXTB4wWQtTtzhpKxsyoJ': {
label: 'John', label: 'John',
copayerId: '026a55261b7c898fff760ebe14fd22a71892295f3b49e0ca66727bc0a0d7f94d03', copayerId: '026a55261b7c898fff760ebe14fd22a71892295f3b49e0ca66727bc0a0d7f94d03',
createdTs: 1403102115, createdTs: 1403102115,

View file

@ -1,14 +1,14 @@
'use strict'; 'use strict';
var chai = chai || require('chai'); var chai = chai || require('chai');
var should = chai.should(); var should = chai.should();
try { try {
var copay = require('copay'); //browser var copay = require('copay'); //browser
} catch (e) { } catch (e) {
var copay = require('../copay'); //node var copay = require('../copay'); //node
} }
var API = API || copay.API; var API = API || copay.API;
var Storage = Storage || require('../test/mocks/FakeStorage'); var Storage = Storage || require('../test/mocks/FakeStorage');
var blanket = require("blanket")({ var blanket = require("blanket")({
"pattern": "/js/" "pattern": "/js/"
@ -17,8 +17,10 @@ var blanket = require("blanket")({
describe('API', function() { describe('API', function() {
it('should have a command called "echo"', function() { it('should have a command called "echo"', function() {
var api = new API({Storage: Storage}); var api = new API({
Storage: Storage
});
should.exist(api.echo); should.exist(api.echo);
}); });
@ -32,7 +34,9 @@ describe('API', function() {
}) })
it('should throw an error for all commands when called with wrong number of arguments', function() { it('should throw an error for all commands when called with wrong number of arguments', function() {
var api = new API({Storage: Storage}); var api = new API({
Storage: Storage
});
for (var i in API.prototype) { for (var i in API.prototype) {
var f = API.prototype[i]; var f = API.prototype[i];
if (i[0] != '_' && typeof f == 'function') { if (i[0] != '_' && typeof f == 'function') {
@ -51,15 +55,17 @@ describe('API', function() {
for (var i in API.prototype) { for (var i in API.prototype) {
var f = API.prototype[i]; var f = API.prototype[i];
if (i[0] != '_' && typeof f == 'function') { if (i[0] != '_' && typeof f == 'function') {
f.argTypes[f.argTypes.length-1][0].should.equal('callback'); f.argTypes[f.argTypes.length - 1][0].should.equal('callback');
f.argTypes[f.argTypes.length-1][1].should.equal('function'); f.argTypes[f.argTypes.length - 1][1].should.equal('function');
} }
} }
}); });
describe('#echo', function() { describe('#echo', function() {
it('should echo a string', function(done) { it('should echo a string', function(done) {
var api = new API({Storage: Storage}); var api = new API({
Storage: Storage
});
var str = 'mystr'; var str = 'mystr';
api.echo(str, function(err, result) { api.echo(str, function(err, result) {
result.should.equal(str); result.should.equal(str);
@ -70,7 +76,9 @@ describe('API', function() {
describe('#echoNumber', function() { describe('#echoNumber', function() {
it('should echo a number', function(done) { it('should echo a number', function(done) {
var api = new API({Storage: Storage}); var api = new API({
Storage: Storage
});
var num = 500; var num = 500;
api.echoNumber(num, function(err, result) { api.echoNumber(num, function(err, result) {
result.should.equal(num); result.should.equal(num);
@ -82,8 +90,12 @@ describe('API', function() {
describe('#echoObject', function() { describe('#echoObject', function() {
it('should echo an object', function(done) { it('should echo an object', function(done) {
var api = new API({Storage: Storage}); var api = new API({
var obj = {test:'test'}; Storage: Storage
});
var obj = {
test: 'test'
};
api.echoObject(obj, function(err, result) { api.echoObject(obj, function(err, result) {
result.test.should.equal(obj.test); result.test.should.equal(obj.test);
(typeof result).should.equal('object'); (typeof result).should.equal('object');
@ -94,7 +106,9 @@ describe('API', function() {
describe('#getArgTypes', function() { describe('#getArgTypes', function() {
it('should get the argTypes of echo', function(done) { it('should get the argTypes of echo', function(done) {
var api = new API({Storage: Storage}); var api = new API({
Storage: Storage
});
api.getArgTypes('echo', function(err, result) { api.getArgTypes('echo', function(err, result) {
result[0][1].should.equal('string'); result[0][1].should.equal('string');
done(); done();
@ -104,7 +118,9 @@ describe('API', function() {
describe('#getCommands', function() { describe('#getCommands', function() {
it('should get all the commands', function(done) { it('should get all the commands', function(done) {
var api = new API({Storage: Storage}); var api = new API({
Storage: Storage
});
var n = 0; var n = 0;
for (var i in api) for (var i in api)
@ -120,7 +136,9 @@ describe('API', function() {
describe('#getWallets', function() { describe('#getWallets', function() {
it('should get the wallet ids', function(done) { it('should get the wallet ids', function(done) {
var api = new API({Storage: Storage}); var api = new API({
Storage: Storage
});
api.getWallets(function(err, result) { api.getWallets(function(err, result) {
result.length.should.be.greaterThan(-1); result.length.should.be.greaterThan(-1);
done(); done();
@ -130,7 +148,9 @@ describe('API', function() {
describe('#help', function() { describe('#help', function() {
it('should call _cmd_getCommands', function(done) { it('should call _cmd_getCommands', function(done) {
var api = new API({Storage: Storage}); var api = new API({
Storage: Storage
});
api._cmd_getCommands = function(callback) { api._cmd_getCommands = function(callback) {
(typeof arguments[0]).should.equal('function'); (typeof arguments[0]).should.equal('function');
callback(null, ['item']); callback(null, ['item']);

View file

@ -1,47 +1,47 @@
'use strict'; 'use strict';
var chai = chai || require('chai'); var chai = chai || require('chai');
var should = chai.should(); var should = chai.should();
var bitcore = bitcore || require('bitcore'); var bitcore = bitcore || require('bitcore');
var Address = bitcore.Address; var Address = bitcore.Address;
var buffertools = bitcore.buffertools; var buffertools = bitcore.buffertools;
try { try {
var copay = require('copay'); //browser var copay = require('copay'); //browser
} catch (e) { } catch (e) {
var copay = require('../copay'); //node var copay = require('../copay'); //node
} }
var PublicKeyRing = copay.PublicKeyRing; var PublicKeyRing = copay.PublicKeyRing;
var AddressIndex = copay.AddressIndex; var AddressIndex = copay.AddressIndex;
var config = { var config = {
networkName:'livenet', networkName: 'livenet',
}; };
var createAI = function () { var createAI = function() {
var i = new AddressIndex(); var i = new AddressIndex();
should.exist(i); should.exist(i);
i.walletId = '1234567'; i.walletId = '1234567';
return i; return i;
}; };
describe('AddressIndex model', function() { describe('AddressIndex model', function() {
it('should create an instance (livenet)', function () { it('should create an instance (livenet)', function() {
var i = new AddressIndex(); var i = new AddressIndex();
should.exist(i); should.exist(i);
}); });
it('show be able to tostore and read', function () { it('show be able to tostore and read', function() {
var i = createAI(); var i = createAI();
var changeN = 2; var changeN = 2;
var addressN = 2; var addressN = 2;
for(var j=0; j<changeN; j++) { for (var j = 0; j < changeN; j++) {
i.increment(true); i.increment(true);
} }
for(var j=0; j<addressN; j++) { for (var j = 0; j < addressN; j++) {
i.increment(false); i.increment(false);
} }
@ -51,38 +51,36 @@ describe('AddressIndex model', function() {
var i2 = AddressIndex.fromObj(data); var i2 = AddressIndex.fromObj(data);
i2.walletId.should.equal(i.walletId); i2.walletId.should.equal(i.walletId);
i2.getChangeIndex().should.equal(changeN); i2.getChangeIndex().should.equal(changeN);
i2.getReceiveIndex().should.equal(addressN); i2.getReceiveIndex().should.equal(addressN);
}); });
it('should count generation indexes', function () { it('should count generation indexes', function() {
var j = createAI(); var j = createAI();
for(var i=0; i<3; i++) for (var i = 0; i < 3; i++)
j.increment(true); j.increment(true);
for(var i=0; i<2; i++) for (var i = 0; i < 2; i++)
j.increment(false); j.increment(false);
j.changeIndex.should.equal(3); j.changeIndex.should.equal(3);
j.receiveIndex.should.equal(2); j.receiveIndex.should.equal(2);
}); });
it('#merge tests', function () { it('#merge tests', function() {
var j = createAI(); var j = createAI();
for(var i=0; i<15; i++) for (var i = 0; i < 15; i++)
j.increment(true); j.increment(true);
for(var i=0; i<7; i++) for (var i = 0; i < 7; i++)
j.increment(false); j.increment(false);
var j2 = new AddressIndex({ var j2 = new AddressIndex({
walletId: j.walletId, walletId: j.walletId,
}); });
j2.merge(j).should.equal(true); j2.merge(j).should.equal(true);
j2.changeIndex.should.equal(15); j2.changeIndex.should.equal(15);
j2.receiveIndex.should.equal(7); j2.receiveIndex.should.equal(7);
j2.merge(j).should.equal(false); j2.merge(j).should.equal(false);
}); });
}); });

View file

@ -1,25 +1,23 @@
'use strict'; 'use strict';
var chai = chai || require('chai'); var chai = chai || require('chai');
var should = chai.should(); var should = chai.should();
var bitcore = bitcore || require('bitcore'); var bitcore = bitcore || require('bitcore');
var buffertools = bitcore.buffertools; var buffertools = bitcore.buffertools;
try { try {
var copay = require('copay'); //browser var copay = require('copay'); //browser
} catch (e) { } catch (e) {
var copay = require('../copay'); //node var copay = require('../copay'); //node
} }
var Passphrase = copay.Passphrase; var Passphrase = copay.Passphrase;
describe('Passphrase model', function() { describe('Passphrase model', function() {
it('should create an instance', function () { it('should create an instance', function() {
var p = new Passphrase(); var p = new Passphrase();
should.exist(p); should.exist(p);
}); });
}); });

View file

@ -1,43 +1,43 @@
'use strict'; 'use strict';
var chai = chai || require('chai'); var chai = chai || require('chai');
var should = chai.should(); var should = chai.should();
var bitcore = bitcore || require('bitcore'); var bitcore = bitcore || require('bitcore');
var Transaction = bitcore.Transaction; var Transaction = bitcore.Transaction;
var buffertools = bitcore.buffertools; var buffertools = bitcore.buffertools;
var WalletKey = bitcore.WalletKey; var WalletKey = bitcore.WalletKey;
var Key = bitcore.Key; var Key = bitcore.Key;
var bignum = bitcore.Bignum; var bignum = bitcore.Bignum;
var networks = bitcore.networks; var networks = bitcore.networks;
var Address = bitcore.Address; var Address = bitcore.Address;
var BitcorePrivateKey = bitcore.PrivateKey; var BitcorePrivateKey = bitcore.PrivateKey;
try { try {
var copay = require('copay'); //browser var copay = require('copay'); //browser
} catch (e) { } catch (e) {
var copay = require('../copay'); //node var copay = require('../copay'); //node
} }
var PrivateKey = copay.PrivateKey || require('../js/models/core/PrivateKey'); var PrivateKey = copay.PrivateKey || require('../js/models/core/PrivateKey');
var config = { var config = {
networkName:'livenet', networkName: 'livenet',
}; };
describe('PrivateKey model', function() { describe('PrivateKey model', function() {
it('should create an instance', function () { it('should create an instance', function() {
var w = new PrivateKey(config); var w = new PrivateKey(config);
should.exist(w); should.exist(w);
should.exist(w.bip); should.exist(w.bip);
should.exist(w.bip.derive); should.exist(w.bip.derive);
}); });
it('should derive priv keys', function () { it('should derive priv keys', function() {
var pk = new PrivateKey(config); var pk = new PrivateKey(config);
for(var j=0; j<2; j++) { for (var j = 0; j < 2; j++) {
for(var i=0; i<3; i++) { for (var i = 0; i < 3; i++) {
var wk = pk.get(i,j); var wk = pk.get(i, j);
should.exist(wk); should.exist(wk);
var o=wk.storeObj(); var o = wk.storeObj();
should.exist(o); should.exist(o);
should.exist(o.priv); should.exist(o.priv);
should.exist(o.pub); should.exist(o.pub);
@ -50,14 +50,14 @@ describe('PrivateKey model', function() {
} }
} }
}); });
it('should derive priv keys array', function () { it('should derive priv keys array', function() {
var w = new PrivateKey(config); var w = new PrivateKey(config);
var wks = w.getAll(2,3); var wks = w.getAll(2, 3);
wks.length.should.equal(5); wks.length.should.equal(5);
for(var j=0; j<wks.length; j++) { for (var j = 0; j < wks.length; j++) {
var wk = wks[j]; var wk = wks[j];
should.exist(wk); should.exist(wk);
var o=wk.storeObj(); var o = wk.storeObj();
should.exist(o); should.exist(o);
should.exist(o.priv); should.exist(o.priv);
should.exist(o.pub); should.exist(o.pub);
@ -70,7 +70,7 @@ describe('PrivateKey model', function() {
} }
}); });
it('fromObj toObj roundtrip', function () { it('fromObj toObj roundtrip', function() {
var w1 = new PrivateKey(config); var w1 = new PrivateKey(config);
var o = JSON.parse(JSON.stringify(w1.toObj())) var o = JSON.parse(JSON.stringify(w1.toObj()))
var w2 = PrivateKey.fromObj(o); var w2 = PrivateKey.fromObj(o);
@ -78,17 +78,17 @@ describe('PrivateKey model', function() {
w2.toObj().extendedPrivateKeyString.should.equal(w1.toObj().extendedPrivateKeyString); w2.toObj().extendedPrivateKeyString.should.equal(w1.toObj().extendedPrivateKeyString);
w2.getId().should.equal(w1.getId()); w2.getId().should.equal(w1.getId());
JSON.stringify(w2.get(1,1).storeObj()).should JSON.stringify(w2.get(1, 1).storeObj()).should
.equal(JSON.stringify(w1.get(1,1).storeObj())); .equal(JSON.stringify(w1.get(1, 1).storeObj()));
JSON.stringify(w2.get(1,0).storeObj()).should JSON.stringify(w2.get(1, 0).storeObj()).should
.equal(JSON.stringify(w1.get(1,0).storeObj())); .equal(JSON.stringify(w1.get(1, 0).storeObj()));
}); });
describe('#getId', function() { describe('#getId', function() {
it('should calculate the copayerId', function() { it('should calculate the copayerId', function() {
var w1 = new PrivateKey(config); var w1 = new PrivateKey(config);
should.exist(w1.getId()); should.exist(w1.getId());
w1.getId().length.should.equal(33*2); w1.getId().length.should.equal(33 * 2);
}); });
}); });
@ -96,7 +96,7 @@ describe('PrivateKey model', function() {
it('should calculate .id', function() { it('should calculate .id', function() {
var w1 = new PrivateKey(config); var w1 = new PrivateKey(config);
should.exist(w1.getIdPriv()); should.exist(w1.getIdPriv());
w1.getIdPriv().length.should.equal(32*2); w1.getIdPriv().length.should.equal(32 * 2);
}); });
}); });
@ -106,8 +106,8 @@ describe('PrivateKey model', function() {
w1.cacheId(); w1.cacheId();
var pub = w1.id; var pub = w1.id;
var priv = w1.idpriv; var priv = w1.idpriv;
pub.length.should.equal(33*2); pub.length.should.equal(33 * 2);
priv.length.should.equal(32*2); priv.length.should.equal(32 * 2);
}); });
it('should set the id equal to the public key of the idpriv private key', function() { it('should set the id equal to the public key of the idpriv private key', function() {
@ -115,8 +115,8 @@ describe('PrivateKey model', function() {
w1.cacheId(); w1.cacheId();
var pub = w1.id; var pub = w1.id;
var priv = w1.idpriv; var priv = w1.idpriv;
pub.length.should.equal(33*2); pub.length.should.equal(33 * 2);
priv.length.should.equal(32*2); priv.length.should.equal(32 * 2);
var key1 = new bitcore.Key(); var key1 = new bitcore.Key();
key1.private = new bitcore.Buffer(priv, 'hex'); key1.private = new bitcore.Buffer(priv, 'hex');

View file

@ -1,21 +1,21 @@
'use strict'; 'use strict';
var chai = chai || require('chai'); var chai = chai || require('chai');
var should = chai.should(); var should = chai.should();
var bitcore = bitcore || require('bitcore'); var bitcore = bitcore || require('bitcore');
var Address = bitcore.Address; var Address = bitcore.Address;
var buffertools = bitcore.buffertools; var buffertools = bitcore.buffertools;
try { try {
var copay = require('copay'); //browser var copay = require('copay'); //browser
} catch (e) { } catch (e) {
var copay = require('../copay'); //node var copay = require('../copay'); //node
} }
var PublicKeyRing = copay.PublicKeyRing; var PublicKeyRing = copay.PublicKeyRing;
var aMasterPubKey = 'tprv8ZgxMBicQKsPdSVTiWXEqCCzqRaRr9EAQdn5UVMpT9UHX67Dh1FmzEMbavPumpAicsUm2XvC6NTdcWB89yN5DUWx5HQ7z3KByUg7Ht74VRZ'; var aMasterPubKey = 'tprv8ZgxMBicQKsPdSVTiWXEqCCzqRaRr9EAQdn5UVMpT9UHX67Dh1FmzEMbavPumpAicsUm2XvC6NTdcWB89yN5DUWx5HQ7z3KByUg7Ht74VRZ';
var createW = function (networkName) { var createW = function(networkName) {
var config = { var config = {
networkName: networkName || 'livenet', networkName: networkName || 'livenet',
}; };
@ -24,21 +24,24 @@ var createW = function (networkName) {
should.exist(w); should.exist(w);
var copayers = []; var copayers = [];
for(var i=0; i<5; i++) { for (var i = 0; i < 5; i++) {
w.isComplete().should.equal(false); w.isComplete().should.equal(false);
var newEpk = w.addCopayer(); var newEpk = w.addCopayer();
copayers.push(newEpk); copayers.push(newEpk);
} }
w.walletId = '1234567'; w.walletId = '1234567';
return {w:w, copayers: copayers}; return {
w: w,
copayers: copayers
};
}; };
describe('PublicKeyRing model', function() { describe('PublicKeyRing model', function() {
it('should create an instance (livenet)', function () { it('should create an instance (livenet)', function() {
var config = { var config = {
networkName:'livenet', networkName: 'livenet',
}; };
var w = new PublicKeyRing({ var w = new PublicKeyRing({
@ -47,48 +50,52 @@ describe('PublicKeyRing model', function() {
should.exist(w); should.exist(w);
w.network.name.should.equal('livenet'); w.network.name.should.equal('livenet');
}); });
it('should create an instance (testnet)', function () { it('should create an instance (testnet)', function() {
var w2 = new PublicKeyRing(); var w2 = new PublicKeyRing();
should.exist(w2); should.exist(w2);
w2.network.name.should.equal('testnet'); w2.network.name.should.equal('testnet');
}); });
it('should fail to generate shared pub keys wo extended key', function () { it('should fail to generate shared pub keys wo extended key', function() {
var config = { var config = {
networkName:'livenet', networkName: 'livenet',
}; };
var w2 = new PublicKeyRing(config); var w2 = new PublicKeyRing(config);
should.exist(w2); should.exist(w2);
w2.registeredCopayers().should.equal(0); w2.registeredCopayers().should.equal(0);
w2.isComplete().should.equal(false); w2.isComplete().should.equal(false);
(function() {w2.getAddress(0, false);}).should.throw(); (function() {
w2.getAddress(0, false);
}).should.throw();
}); });
it('should add and check when adding shared pub keys', function () { it('should add and check when adding shared pub keys', function() {
var k = createW(); var k = createW();
var w = k.w; var w = k.w;
var copayers = k.copayers; var copayers = k.copayers;
w.isComplete().should.equal(true); w.isComplete().should.equal(true);
w.addCopayer.should.throw(); w.addCopayer.should.throw();
for(var i =0; i<5; i++) { for (var i = 0; i < 5; i++) {
(function() {w.addCopayer(copayers[i])}).should.throw(); (function() {
w.addCopayer(copayers[i])
}).should.throw();
} }
}); });
it('show be able to tostore and read', function () { it('show be able to tostore and read', function() {
var k = createW(); var k = createW();
var w = k.w; var w = k.w;
var copayers = k.copayers; var copayers = k.copayers;
var changeN = 2; var changeN = 2;
var addressN = 2; var addressN = 2;
var start = new Date().getTime(); var start = new Date().getTime();
for(var i=0; i<changeN; i++) { for (var i = 0; i < changeN; i++) {
w.generateAddress(true); w.generateAddress(true);
} }
for(var i=0; i<addressN; i++) { for (var i = 0; i < addressN; i++) {
w.generateAddress(false); w.generateAddress(false);
} }
@ -99,34 +106,36 @@ describe('PublicKeyRing model', function() {
w2.walletId.should.equal(w.walletId); w2.walletId.should.equal(w.walletId);
w2.isComplete().should.equal(true); w2.isComplete().should.equal(true);
w2.addCopayer.should.throw(); w2.addCopayer.should.throw();
for(var i =0; i<5; i++) { for (var i = 0; i < 5; i++) {
(function() {w.addCopayer(copayers[i])}).should.throw(); (function() {
w.addCopayer(copayers[i])
}).should.throw();
} }
w2.indexes.getChangeIndex().should.equal(changeN); w2.indexes.getChangeIndex().should.equal(changeN);
w2.indexes.getReceiveIndex().should.equal(addressN); w2.indexes.getReceiveIndex().should.equal(addressN);
}); });
it('should generate some p2sh addresses', function () { it('should generate some p2sh addresses', function() {
var k = createW(); var k = createW();
var w = k.w; var w = k.w;
for(var isChange=0; isChange<2; isChange++) { for (var isChange = 0; isChange < 2; isChange++) {
for(var i=0; i<2; i++) { for (var i = 0; i < 2; i++) {
var a = w.generateAddress(isChange); var a = w.generateAddress(isChange);
a.isValid().should.equal(true); a.isValid().should.equal(true);
a.isScript().should.equal(true); a.isScript().should.equal(true);
a.network().name.should.equal('livenet'); a.network().name.should.equal('livenet');
if (i>1) { if (i > 1) {
w.getAddress(i-1,isChange).toString().should w.getAddress(i - 1, isChange).toString().should
.not.equal(w.getAddress(i-2,isChange).toString()); .not.equal(w.getAddress(i - 2, isChange).toString());
} }
} }
} }
}); });
it('should return PublicKeyRing addresses', function () { it('should return PublicKeyRing addresses', function() {
var k = createW(); var k = createW();
var w = k.w; var w = k.w;
@ -134,38 +143,38 @@ describe('PublicKeyRing model', function() {
var a = w.getAddresses(); var a = w.getAddresses();
a.length.should.equal(0); a.length.should.equal(0);
for(var isChange=0; isChange<2; isChange++) for (var isChange = 0; isChange < 2; isChange++)
for(var i=0; i<2; i++) for (var i = 0; i < 2; i++)
w.generateAddress(isChange); w.generateAddress(isChange);
var as = w.getAddresses(); var as = w.getAddresses();
as.length.should.equal(4); as.length.should.equal(4);
for(var j in as) { for (var j in as) {
var a = as[j]; var a = as[j];
a.isValid().should.equal(true); a.isValid().should.equal(true);
} }
}); });
it('should count generation indexes', function () { it('should count generation indexes', function() {
var k = createW(); var k = createW();
var w = k.w; var w = k.w;
for(var i=0; i<3; i++) for (var i = 0; i < 3; i++)
w.generateAddress(true); w.generateAddress(true);
for(var i=0; i<2; i++) for (var i = 0; i < 2; i++)
w.generateAddress(false); w.generateAddress(false);
w.indexes.getChangeIndex().should.equal(3); w.indexes.getChangeIndex().should.equal(3);
w.indexes.getReceiveIndex().should.equal(2); w.indexes.getReceiveIndex().should.equal(2);
}); });
it('#merge index tests', function () { it('#merge index tests', function() {
var k = createW(); var k = createW();
var w = k.w; var w = k.w;
for(var i=0; i<2; i++) for (var i = 0; i < 2; i++)
w.generateAddress(true); w.generateAddress(true);
for(var i=0; i<3; i++) for (var i = 0; i < 3; i++)
w.generateAddress(false); w.generateAddress(false);
var w2 = new PublicKeyRing({ var w2 = new PublicKeyRing({
@ -173,17 +182,17 @@ describe('PublicKeyRing model', function() {
walletId: w.walletId, walletId: w.walletId,
}); });
w2.merge(w).should.equal(true); w2.merge(w).should.equal(true);
w2.requiredCopayers.should.equal(3); w2.requiredCopayers.should.equal(3);
w2.totalCopayers.should.equal(5); w2.totalCopayers.should.equal(5);
w2.indexes.getChangeIndex().should.equal(2); w2.indexes.getChangeIndex().should.equal(2);
w2.indexes.getReceiveIndex().should.equal(3); w2.indexes.getReceiveIndex().should.equal(3);
// //
w2.merge(w).should.equal(false); w2.merge(w).should.equal(false);
}); });
it('#merge check tests', function () { it('#merge check tests', function() {
var config = { var config = {
networkName: 'livenet', networkName: 'livenet',
}; };
@ -191,32 +200,42 @@ describe('PublicKeyRing model', function() {
var w = new PublicKeyRing(config); var w = new PublicKeyRing(config);
w.walletId = 'lwjd5qra8257b9'; w.walletId = 'lwjd5qra8257b9';
var w2 = new PublicKeyRing({ var w2 = new PublicKeyRing({
networkName: 'testnet', //wrong networkName: 'testnet', //wrong
walletId: w.walletId, walletId: w.walletId,
}); });
(function() { w2.merge(w);}).should.throw(); (function() {
w2.merge(w);
}).should.throw();
var w3 = new PublicKeyRing({ var w3 = new PublicKeyRing({
networkName: 'livenet', networkName: 'livenet',
walletId: w.walletId, walletId: w.walletId,
requiredCopayers: 2, // wrong requiredCopayers: 2, // wrong
}); });
(function() { w3.merge(w);}).should.throw(); (function() {
w3.merge(w);
}).should.throw();
var w4 = new PublicKeyRing({ var w4 = new PublicKeyRing({
networkName: 'livenet', networkName: 'livenet',
walletId: w.walletId, walletId: w.walletId,
totalCopayers: 3, // wrong totalCopayers: 3, // wrong
}); });
(function() { w4.merge(w);}).should.throw(); (function() {
w4.merge(w);
}).should.throw();
var w6 = new PublicKeyRing({ var w6 = new PublicKeyRing({
networkName: 'livenet', networkName: 'livenet',
}); });
(function() { w6.merge(w);}).should.throw(); (function() {
w.networkName= 'livenet'; w6.merge(w);
(function() { w6.merge(w);}).should.throw(); }).should.throw();
w.networkName = 'livenet';
(function() {
w6.merge(w);
}).should.throw();
var w0 = new PublicKeyRing({ var w0 = new PublicKeyRing({
@ -227,28 +246,32 @@ describe('PublicKeyRing model', function() {
w0.addCopayer(); w0.addCopayer();
w0.addCopayer(); w0.addCopayer();
w0.addCopayer(); w0.addCopayer();
(function() { w0.merge(w);}).should.throw(); (function() {
w.merge(w0,true).should.equal(true); w0.merge(w);
}).should.throw();
w.merge(w0, true).should.equal(true);
w.isComplete().should.equal(true); w.isComplete().should.equal(true);
var wx = new PublicKeyRing({ var wx = new PublicKeyRing({
networkName: 'livenet', networkName: 'livenet',
}); });
wx.addCopayer(); wx.addCopayer();
(function() { w.merge(wx);}).should.throw(); (function() {
w.merge(wx);
}).should.throw();
}); });
it('#merge pubkey tests', function () { it('#merge pubkey tests', function() {
var config = { var config = {
networkName:'livenet', networkName: 'livenet',
}; };
var w = new PublicKeyRing(config); var w = new PublicKeyRing(config);
should.exist(w); should.exist(w);
var copayers = []; var copayers = [];
for(var i=0; i<2; i++) { for (var i = 0; i < 2; i++) {
w.isComplete().should.equal(false); w.isComplete().should.equal(false);
w.addCopayer(); w.addCopayer();
} }
@ -259,7 +282,7 @@ describe('PublicKeyRing model', function() {
}); });
should.exist(w); should.exist(w);
var copayers = []; var copayers = [];
for(var i=0; i<3; i++) { for (var i = 0; i < 3; i++) {
w2.isComplete().should.equal(false); w2.isComplete().should.equal(false);
w2.addCopayer(); w2.addCopayer();
} }
@ -273,14 +296,14 @@ describe('PublicKeyRing model', function() {
w.merge(w2).should.equal(false); w.merge(w2).should.equal(false);
}); });
it('#merge pubkey tests (case 2)', function () { it('#merge pubkey tests (case 2)', function() {
var config = { var config = {
networkName:'livenet', networkName: 'livenet',
}; };
var w = new PublicKeyRing(config); var w = new PublicKeyRing(config);
should.exist(w); should.exist(w);
for(var i=0; i<5; i++) { for (var i = 0; i < 5; i++) {
w.isComplete().should.equal(false); w.isComplete().should.equal(false);
var w2 = new PublicKeyRing({ var w2 = new PublicKeyRing({
networkName: 'livenet', networkName: 'livenet',
@ -293,31 +316,31 @@ describe('PublicKeyRing model', function() {
}); });
it('#merge with nickname', function () { it('#merge with nickname', function() {
var config = { var config = {
networkName:'livenet', networkName: 'livenet',
}; };
var w = new PublicKeyRing(config); var w = new PublicKeyRing(config);
should.exist(w); should.exist(w);
for(var i=0; i<3; i++) { for (var i = 0; i < 3; i++) {
w.addCopayer(); w.addCopayer();
}; };
w._setNicknameForIndex(0,'pepe0'); w._setNicknameForIndex(0, 'pepe0');
w._setNicknameForIndex(1,'pepe1'); w._setNicknameForIndex(1, 'pepe1');
w.nicknameForIndex(0).should.equal('pepe0'); w.nicknameForIndex(0).should.equal('pepe0');
w.nicknameForIndex(1).should.equal('pepe1'); w.nicknameForIndex(1).should.equal('pepe1');
should.not.exist(w.nicknameForIndex(2)); should.not.exist(w.nicknameForIndex(2));
for(var i=0; i<2; i++) { for (var i = 0; i < 2; i++) {
w.isComplete().should.equal(false); w.isComplete().should.equal(false);
var w2 = new PublicKeyRing({ var w2 = new PublicKeyRing({
networkName: 'livenet', networkName: 'livenet',
id: w.id, id: w.id,
}); });
w2.addCopayer(); w2.addCopayer();
w2._setNicknameForIndex(0,'juan' + i); w2._setNicknameForIndex(0, 'juan' + i);
w.merge(w2).should.equal(true); w.merge(w2).should.equal(true);
} }
w.isComplete().should.equal(true); w.isComplete().should.equal(true);
@ -330,14 +353,14 @@ describe('PublicKeyRing model', function() {
}); });
it('#toObj #fromObj with nickname', function () { it('#toObj #fromObj with nickname', function() {
var config = { var config = {
networkName:'livenet', networkName: 'livenet',
}; };
var w = new PublicKeyRing(config); var w = new PublicKeyRing(config);
should.exist(w); should.exist(w);
for(var i=0; i<3; i++) { for (var i = 0; i < 3; i++) {
w.addCopayer(null, 'tito'+i); w.addCopayer(null, 'tito' + i);
}; };
w.nicknameForIndex(0).should.equal('tito0'); w.nicknameForIndex(0).should.equal('tito0');
w.nicknameForIndex(1).should.equal('tito1'); w.nicknameForIndex(1).should.equal('tito1');
@ -345,7 +368,7 @@ describe('PublicKeyRing model', function() {
should.not.exist(w.nicknameForIndex(3)); should.not.exist(w.nicknameForIndex(3));
var o = JSON.parse(JSON.stringify(w.toObj())); var o = JSON.parse(JSON.stringify(w.toObj()));
var w2 = PublicKeyRing.fromObj( o ); var w2 = PublicKeyRing.fromObj(o);
w2.nicknameForIndex(0).should.equal('tito0'); w2.nicknameForIndex(0).should.equal('tito0');
w2.nicknameForIndex(1).should.equal('tito1'); w2.nicknameForIndex(1).should.equal('tito1');
w2.nicknameForIndex(2).should.equal('tito2'); w2.nicknameForIndex(2).should.equal('tito2');
@ -353,22 +376,20 @@ describe('PublicKeyRing model', function() {
}); });
it('#getRedeemScriptMap check tests', function () { it('#getRedeemScriptMap check tests', function() {
var k = createW(); var k = createW();
var w = k.w; var w = k.w;
for(var i=0; i<2; i++) for (var i = 0; i < 2; i++)
w.generateAddress(true); w.generateAddress(true);
for(var i=0; i<2; i++) for (var i = 0; i < 2; i++)
w.generateAddress(false); w.generateAddress(false);
var m = w.getRedeemScriptMap(); var m = w.getRedeemScriptMap();
Object.keys(m).length.should.equal(4); Object.keys(m).length.should.equal(4);
Object.keys(m).forEach(function (k) { Object.keys(m).forEach(function(k) {
should.exist(m[k]); should.exist(m[k]);
}); });
}); });
}); });

View file

@ -1,41 +1,41 @@
'use strict'; 'use strict';
var chai = chai || require('chai'); var chai = chai || require('chai');
var should = chai.should(); var should = chai.should();
var bitcore = bitcore || require('bitcore'); var bitcore = bitcore || require('bitcore');
try { try {
var copay = require('copay'); //browser var copay = require('copay'); //browser
} catch (e) { } catch (e) {
var copay = require('../copay'); //node var copay = require('../copay'); //node
} }
var Structure = require('../js/models/core/Structure'); var Structure = require('../js/models/core/Structure');
describe('Structure model', function() { describe('Structure model', function() {
it('should have the correct constants', function () { it('should have the correct constants', function() {
Structure.MAX_NON_HARDENED.should.equal(Math.pow(2,31) - 1); Structure.MAX_NON_HARDENED.should.equal(Math.pow(2, 31) - 1);
Structure.SHARED_INDEX.should.equal(Structure.MAX_NON_HARDENED); Structure.SHARED_INDEX.should.equal(Structure.MAX_NON_HARDENED);
Structure.ID_INDEX.should.equal(Structure.SHARED_INDEX - 1); Structure.ID_INDEX.should.equal(Structure.SHARED_INDEX - 1);
Structure.IdFullBranch.should.equal('m/45\'/2147483646/0/0'); Structure.IdFullBranch.should.equal('m/45\'/2147483646/0/0');
}); });
it('should get the correct branches', function () { it('should get the correct branches', function() {
// shared branch (no cosigner index specified) // shared branch (no cosigner index specified)
Structure.FullBranch(0,false).should.equal('m/45\'/2147483647/0/0'); Structure.FullBranch(0, false).should.equal('m/45\'/2147483647/0/0');
// copayer 0, address 0, external address (receiving) // copayer 0, address 0, external address (receiving)
Structure.FullBranch(0,false,0).should.equal('m/45\'/0/0/0'); Structure.FullBranch(0, false, 0).should.equal('m/45\'/0/0/0');
// copayer 0, address 10, external address (receiving) // copayer 0, address 10, external address (receiving)
Structure.FullBranch(0,false,10).should.equal('m/45\'/10/0/0'); Structure.FullBranch(0, false, 10).should.equal('m/45\'/10/0/0');
// copayer 0, address 0, internal address (change) // copayer 0, address 0, internal address (change)
Structure.FullBranch(0,true,0).should.equal('m/45\'/0/1/0'); Structure.FullBranch(0, true, 0).should.equal('m/45\'/0/1/0');
// copayer 0, address 10, internal address (change) // copayer 0, address 10, internal address (change)
Structure.FullBranch(10,true,0).should.equal('m/45\'/0/1/10'); Structure.FullBranch(10, true, 0).should.equal('m/45\'/0/1/10');
// copayer 7, address 10, internal address (change) // copayer 7, address 10, internal address (change)
Structure.FullBranch(10,true,7).should.equal('m/45\'/7/1/10'); Structure.FullBranch(10, true, 7).should.equal('m/45\'/7/1/10');
}); });
}); });

View file

@ -132,27 +132,48 @@ describe('Insight model', function() {
cb([]); cb([]);
}; };
w.checkActivity(addresses, function(err, actives){ w.checkActivity(addresses, function(err, actives) {
console.log(err); console.log(err);
actives.length.should.equal(addresses.length); actives.length.should.equal(addresses.length);
actives.filter(function(i) { return i }).length.should.equal(0); actives.filter(function(i) {
return i
}).length.should.equal(0);
done(); done();
}); });
}); });
it('#checkActivity for active addreses', function(done) { it('#checkActivity for active addreses', function(done) {
var w = new Insight(); var w = new Insight();
w.getTransactions = function(addresses, cb) { w.getTransactions = function(addresses, cb) {
cb([ cb([{
{vin: [{ addr: '2NATQJnaQe2CUKLyhL1zdNkttJM1dUH9HaM'}], vout: []}, vin: [{
{vin: [{ addr: '2NATQJnaQe2CUKLyhL1zdNkttJM1dUH9HaM'}], vout: []}, addr: '2NATQJnaQe2CUKLyhL1zdNkttJM1dUH9HaM'
{vin: [{ addr: '2N9D5bcCQ2bPWUDByQ6Qb5bMgMtgsk1rw3x'}], vout: []}, }],
{vin: [], vout: [{scriptPubKey: {addresses: ['2NFjCBFZSsxiwWAD7CKQ3hzWFtf9DcqTucY']}}]} vout: []
]); }, {
vin: [{
addr: '2NATQJnaQe2CUKLyhL1zdNkttJM1dUH9HaM'
}],
vout: []
}, {
vin: [{
addr: '2N9D5bcCQ2bPWUDByQ6Qb5bMgMtgsk1rw3x'
}],
vout: []
}, {
vin: [],
vout: [{
scriptPubKey: {
addresses: ['2NFjCBFZSsxiwWAD7CKQ3hzWFtf9DcqTucY']
}
}]
}]);
}; };
w.checkActivity(addresses, function(err, actives){ w.checkActivity(addresses, function(err, actives) {
actives.length.should.equal(addresses.length); actives.length.should.equal(addresses.length);
actives.filter(function(i) { return i }).length.should.equal(3); actives.filter(function(i) {
return i
}).length.should.equal(3);
done(); done();
}); });
}); });

View file

@ -1,15 +1,15 @@
'use strict'; 'use strict';
var chai = chai || require('chai'); var chai = chai || require('chai');
var should = chai.should(); var should = chai.should();
var expect = chai.expect; var expect = chai.expect;
var sinon = sinon || require('sinon'); var sinon = sinon || require('sinon');
var bitcore = bitcore || require('bitcore'); var bitcore = bitcore || require('bitcore');
var WebRTC = require('../js/models/network/WebRTC'); var WebRTC = require('../js/models/network/WebRTC');
describe('Network / WebRTC', function() { describe('Network / WebRTC', function() {
it('should create an instance', function () { it('should create an instance', function() {
var n = new WebRTC(); var n = new WebRTC();
should.exist(n); should.exist(n);
}); });
@ -28,7 +28,7 @@ describe('Network / WebRTC', function() {
n.cleanUp.calledOnce.should.equal(true); n.cleanUp.calledOnce.should.equal(true);
WebRTC.prototype.cleanUp = save; WebRTC.prototype.cleanUp = save;
}); });
}); });
describe('#cleanUp', function() { describe('#cleanUp', function() {
@ -95,7 +95,9 @@ describe('Network / WebRTC', function() {
key.regenerateSync(); key.regenerateSync();
var copayerId = key.public.toString('hex'); var copayerId = key.public.toString('hex');
n._sendToOne = function(a1, a2, a3, cb) {cb();}; n._sendToOne = function(a1, a2, a3, cb) {
cb();
};
var sig = undefined; var sig = undefined;
n.send(copayerId, data, function() { n.send(copayerId, data, function() {
done(); done();
@ -138,7 +140,9 @@ describe('Network / WebRTC', function() {
key.regenerateSync(); key.regenerateSync();
var copayerIds = [key.public.toString('hex')]; var copayerIds = [key.public.toString('hex')];
n._sendToOne = function(a1, a2, a3, cb) {cb();}; n._sendToOne = function(a1, a2, a3, cb) {
cb();
};
var sig = undefined; var sig = undefined;
n.send(copayerIds, data, function() { n.send(copayerIds, data, function() {
done(); done();

View file

@ -1,11 +1,11 @@
'use strict'; 'use strict';
var chai = require('chai'); var chai = require('chai');
var should = chai.should(); var should = chai.should();
var Storage = require('../js/models/storage/File.js'); var Storage = require('../js/models/storage/File.js');
var sinon = require('sinon'); var sinon = require('sinon');
var crypto = require('crypto'); var crypto = require('crypto');
var CryptoJS = require('node-cryptojs-aes').CryptoJS; var CryptoJS = require('node-cryptojs-aes').CryptoJS;
describe('Storage/File', function() { describe('Storage/File', function() {
it('should exist', function() { it('should exist', function() {
@ -17,12 +17,18 @@ describe('Storage/File', function() {
var fs = {} var fs = {}
fs.readFile = function(filename, callback) { fs.readFile = function(filename, callback) {
filename.should.equal('myfilename'); filename.should.equal('myfilename');
var obj = {"test":"test"}; var obj = {
"test": "test"
};
var encryptedStr = CryptoJS.AES.encrypt(JSON.stringify(obj), "password").toString(); var encryptedStr = CryptoJS.AES.encrypt(JSON.stringify(obj), "password").toString();
callback(null, encryptedStr); callback(null, encryptedStr);
}; };
var Storage = require('soop').load('../js/models/storage/File.js', {fs: fs}); var Storage = require('soop').load('../js/models/storage/File.js', {
var storage = new Storage({password: 'password'}); fs: fs
});
var storage = new Storage({
password: 'password'
});
storage.load('myfilename', function(err) { storage.load('myfilename', function(err) {
done(); done();
}); });
@ -36,8 +42,12 @@ describe('Storage/File', function() {
filename.should.equal('myfilename'); filename.should.equal('myfilename');
callback(); callback();
}; };
var Storage = require('soop').load('../js/models/storage/File.js', {fs: fs}); var Storage = require('soop').load('../js/models/storage/File.js', {
var storage = new Storage({password: 'password'}); fs: fs
});
var storage = new Storage({
password: 'password'
});
storage.save('myfilename', function(err) { storage.save('myfilename', function(err) {
done(); done();
}); });
@ -47,7 +57,11 @@ describe('Storage/File', function() {
describe('#_read', function() { describe('#_read', function() {
it('should return the value of a key', function() { it('should return the value of a key', function() {
var storage = new Storage(); var storage = new Storage();
storage.data = {'walletId':{'test':'data'}}; storage.data = {
'walletId': {
'test': 'data'
}
};
storage._read('walletId::test').should.equal('data'); storage._read('walletId::test').should.equal('data');
}); });
}); });
@ -68,7 +82,11 @@ describe('Storage/File', function() {
describe('#getGlobal', function() { describe('#getGlobal', function() {
it('should call storage._read', function() { it('should call storage._read', function() {
var storage = new Storage(); var storage = new Storage();
storage.data = {'walletId':{'test':'test'}}; storage.data = {
'walletId': {
'test': 'test'
}
};
storage._read = sinon.spy(); storage._read = sinon.spy();
storage.getGlobal('walletId::test'); storage.getGlobal('walletId::test');
storage._read.calledOnce.should.equal(true); storage._read.calledOnce.should.equal(true);
@ -91,7 +109,11 @@ describe('Storage/File', function() {
describe('#removeGlobal', function() { describe('#removeGlobal', function() {
it('should remove a global key', function(done) { it('should remove a global key', function(done) {
var storage = new Storage(); var storage = new Storage();
storage.data = {'walletId':{'key':'value'}}; storage.data = {
'walletId': {
'key': 'value'
}
};
storage.save = function(walletId, callback) { storage.save = function(walletId, callback) {
should.not.exist(storage.data[walletId]['key']); should.not.exist(storage.data[walletId]['key']);
callback(); callback();
@ -141,7 +163,9 @@ describe('Storage/File', function() {
describe('#setFromObj', function() { describe('#setFromObj', function() {
it('should set this object for a wallet', function(done) { it('should set this object for a wallet', function(done) {
var obj = {test:'testval'}; var obj = {
test: 'testval'
};
var storage = new Storage(); var storage = new Storage();
storage.save = function(walletId, callback) { storage.save = function(walletId, callback) {
callback(); callback();
@ -155,25 +179,29 @@ describe('Storage/File', function() {
describe('#getEncryptedObj', function() { describe('#getEncryptedObj', function() {
it('should give an encrypted object', function() { it('should give an encrypted object', function() {
var obj = {test:'testval'}; var obj = {
test: 'testval'
};
var data = JSON.stringify(obj); var data = JSON.stringify(obj);
var encrypted = CryptoJS.AES.encrypt(data, 'password'); var encrypted = CryptoJS.AES.encrypt(data, 'password');
var base64 = encrypted.toString(); var base64 = encrypted.toString();
var storage = new Storage({password: 'password'}); var storage = new Storage({
password: 'password'
});
storage.data['walletId'] = obj; storage.data['walletId'] = obj;
var enc = storage.getEncryptedObj('walletId'); var enc = storage.getEncryptedObj('walletId');
//enc.length.should.equal(96); //enc.length.should.equal(96);
enc.length.should.be.greaterThan(10); enc.length.should.be.greaterThan(10);
enc.slice(0,10).should.equal(base64.slice(0,10)); enc.slice(0, 10).should.equal(base64.slice(0, 10));
//enc.slice(0,6).should.equal("53616c"); //enc.slice(0,6).should.equal("53616c");
}); });
}); });
describe('#clearAll', function() { describe('#clearAll', function() {
it('should set data to {}', function() { it('should set data to {}', function() {
}); });
}); });

View file

@ -20,48 +20,58 @@ if (typeof process === 'undefined' || !process.version) {
}); });
it('should fail when encrypting without a password', function() { it('should fail when encrypting without a password', function() {
var s = new LocalEncrypted(); var s = new LocalEncrypted();
(function(){ (function() {
s.set(fakeWallet, timeStamp, 1); s.set(fakeWallet, timeStamp, 1);
localStorage.removeItem(fakeWallet +'::'+ timeStamp); localStorage.removeItem(fakeWallet + '::' + timeStamp);
}).should.throw(); }).should.throw();
}); });
it('should be able to encrypt and decrypt', function() { it('should be able to encrypt and decrypt', function() {
s._write(fakeWallet+timeStamp, 'value'); s._write(fakeWallet + timeStamp, 'value');
s._read(fakeWallet+timeStamp).should.equal('value'); s._read(fakeWallet + timeStamp).should.equal('value');
localStorage.removeItem(fakeWallet+timeStamp); localStorage.removeItem(fakeWallet + timeStamp);
}); });
it('should be able to set a value', function() { it('should be able to set a value', function() {
s.set(fakeWallet, timeStamp, 1); s.set(fakeWallet, timeStamp, 1);
localStorage.removeItem(fakeWallet +'::'+ timeStamp); localStorage.removeItem(fakeWallet + '::' + timeStamp);
}); });
var getSetData = [ var getSetData = [
1,1000,-15, -1000, 1, 1000, -15, -1000,
0.1, -0.5, -0.5e-10, Math.PI, 0.1, -0.5, -0.5e-10, Math.PI,
'hi', 'auydoaiusyodaisudyoa', '0b5b8556a0c2ce828c9ccfa58b3dd0a1ae879b9b', 'hi', 'auydoaiusyodaisudyoa', '0b5b8556a0c2ce828c9ccfa58b3dd0a1ae879b9b',
'1CjPR7Z5ZSyWk6WtXvSFgkptmpoi4UM9BC', 'OP_DUP OP_HASH160 80ad90d4035', '1CjPR7Z5ZSyWk6WtXvSFgkptmpoi4UM9BC', 'OP_DUP OP_HASH160 80ad90d4035', [1, 2, 3, 4, 5, 6], {
[1,2,3,4,5,6], x: 1,
{ x: 1, y: 2}, y: 2
{ x: 'hi', y: null}, }, {
{ a: {}, b: [], c: [1,2,'hi']}, x: 'hi',
y: null
}, {
a: {},
b: [],
c: [1, 2, 'hi']
},
null null
]; ];
getSetData.forEach(function(obj) { getSetData.forEach(function(obj) {
it('should be able to set a value and get it for '+JSON.stringify(obj), function() { it('should be able to set a value and get it for ' + JSON.stringify(obj), function() {
s.set(fakeWallet, timeStamp, obj); s.set(fakeWallet, timeStamp, obj);
var obj2 = s.get(fakeWallet, timeStamp); var obj2 = s.get(fakeWallet, timeStamp);
JSON.stringify(obj2).should.equal(JSON.stringify(obj)); JSON.stringify(obj2).should.equal(JSON.stringify(obj));
localStorage.removeItem(fakeWallet +'::'+ timeStamp); localStorage.removeItem(fakeWallet + '::' + timeStamp);
}); });
}); });
describe('#export', function() { describe('#export', function() {
it('should export the encrypted wallet', function() { it('should export the encrypted wallet', function() {
var storage = new LocalEncrypted({password: 'password'}); var storage = new LocalEncrypted({
password: 'password'
});
storage.set(fakeWallet, timeStamp, 'testval'); storage.set(fakeWallet, timeStamp, 'testval');
var obj = {test:'testval'}; var obj = {
test: 'testval'
};
var encrypted = storage.export(obj); var encrypted = storage.export(obj);
encrypted.length.should.be.greaterThan(10); encrypted.length.should.be.greaterThan(10);
localStorage.removeItem(fakeWallet +'::'+ timeStamp); localStorage.removeItem(fakeWallet + '::' + timeStamp);
//encrypted.slice(0,6).should.equal("53616c"); //encrypted.slice(0,6).should.equal("53616c");
}); });
}); });

View file

@ -26,13 +26,13 @@ if (typeof process === 'undefined' || !process.version) {
} }
}; };
var storage = new LocalPlain(); var storage = new LocalPlain();
storage.setFromObj(fakeWallet+timeStamp, obj); storage.setFromObj(fakeWallet + timeStamp, obj);
storage.get(fakeWallet+timeStamp, 'test').should.equal('testval'); storage.get(fakeWallet + timeStamp, 'test').should.equal('testval');
// Clean data used in localstorage // Clean data used in localstorage
localStorage.removeItem(fakeWallet+timeStamp+'::test'); localStorage.removeItem(fakeWallet + timeStamp + '::test');
localStorage.removeItem(fakeWallet+timeStamp+'::opts'); localStorage.removeItem(fakeWallet + timeStamp + '::opts');
localStorage.removeItem('nameFor::'+fakeWallet+timeStamp); localStorage.removeItem('nameFor::' + fakeWallet + timeStamp);
}); });
}); });
}); });

View file

@ -21,7 +21,7 @@ describe("Unit: Controllers", function() {
totalCopayers: 5, totalCopayers: 5,
spendUnconfirmed: 1, spendUnconfirmed: 1,
reconnectDelay: 100, reconnectDelay: 100,
networkName: 'testnet' networkName: 'testnet'
}; };
describe('Backup Controller', function() { describe('Backup Controller', function() {
@ -55,6 +55,25 @@ describe("Unit: Controllers", function() {
}); });
}); });
describe('Setup Controller', function() {
var setupCtrl;
beforeEach(inject(function($controller, $rootScope) {
scope = $rootScope.$new();
setupCtrl = $controller('SetupController', {
$scope: scope,
});
}));
describe('#getNumber', function() {
it('should return an array of n undefined elements', function() {
var n = 5;
var array = scope.getNumber(n);
expect(array.length).equal(n);
});
});
});
describe('Address Controller', function() { describe('Address Controller', function() {
var addressCtrl; var addressCtrl;
beforeEach(inject(function($controller, $rootScope) { beforeEach(inject(function($controller, $rootScope) {
@ -98,7 +117,7 @@ describe("Unit: Controllers", function() {
describe('Send Controller', function() { describe('Send Controller', function() {
var scope, form; var scope, form;
beforeEach(angular.mock.module('copayApp')); beforeEach(angular.mock.module('copayApp'));
beforeEach(angular.mock.inject(function($compile, $rootScope, $controller){ beforeEach(angular.mock.inject(function($compile, $rootScope, $controller) {
scope = $rootScope.$new(); scope = $rootScope.$new();
$rootScope.wallet = new FakeWallet(config); $rootScope.wallet = new FakeWallet(config);
var element = angular.element( var element = angular.element(
@ -109,10 +128,13 @@ describe("Unit: Controllers", function() {
); );
scope.model = { scope.model = {
newaddress: null, newaddress: null,
newlabel: null newlabel: null,
}; };
$compile(element)(scope); $compile(element)(scope);
$controller('SendController', {$scope: scope}); $controller('SendController', {
$scope: scope,
$modal: {},
});
scope.$digest(); scope.$digest();
form = scope.form; form = scope.form;
})); }));
@ -212,12 +234,20 @@ describe("Unit: Controllers", function() {
scope.$apply(); scope.$apply();
}); });
it('should return an array of n undefined elements', function() {
$httpBackend.flush(); // need flush
var n = 5;
var array = scope.getNumber(n);
expect(array.length).equal(n);
});
}); });
describe('Send Controller', function() { describe('Send Controller', function() {
var sendCtrl; var sendCtrl;
beforeEach(inject(function($controller, $rootScope) { beforeEach(inject(function($controller, $rootScope) {
scope = $rootScope.$new(); scope = $rootScope.$new();
$rootScope.availableBalance = 123456;
sendCtrl = $controller('SendController', { sendCtrl = $controller('SendController', {
$scope: scope, $scope: scope,
$modal: {}, $modal: {},
@ -227,6 +257,10 @@ describe("Unit: Controllers", function() {
it('should have a SendController', function() { it('should have a SendController', function() {
expect(scope.isMobile).not.to.equal(null); expect(scope.isMobile).not.to.equal(null);
}); });
it('should autotop balance correctly', function() {
scope.topAmount();
expect(scope.amount).to.equal(123356);
});
}); });
}); });

View file

@ -85,7 +85,11 @@ describe("Unit: Testing Directives", function() {
beforeEach(inject(function($compile, $rootScope) { beforeEach(inject(function($compile, $rootScope) {
$rootScope.wallet = { $rootScope.wallet = {
addressBook: {'2MtBXKLtZuXGDshUcyH6yq7aZ33Snbb49pT': {label: ':)'}} addressBook: {
'2MtBXKLtZuXGDshUcyH6yq7aZ33Snbb49pT': {
label: ':)'
}
}
} }
element1 = angular.element( element1 = angular.element(
'<contact address="2MtBXKLtZuXGDshUcyH6yq7aZ33Snbb49pT" />' '<contact address="2MtBXKLtZuXGDshUcyH6yq7aZ33Snbb49pT" />'

View file

@ -1,6 +1,71 @@
'use strict';
// //
// test/unit/filters/filtersSpec.js // test/unit/filters/filtersSpec.js
// //
describe("Unit: Testing Filters", function() { describe('Unit: Testing Filters', function() {
beforeEach(module('copayApp.filters'));
describe('limitAddress', function() {
it('should handle emtpy list', inject(function($filter) {
var limitAddress = $filter('limitAddress');
expect(limitAddress([], false)).to.be.empty;
}));
it('should honor show all', inject(function($filter) {
var limitAddress = $filter('limitAddress');
var addresses = [{}, {}, {}, {}, {}];
expect(limitAddress(addresses, true).length).to.equal(5);
expect(limitAddress([{}], false).length).to.equal(1);
}));
it('should filter correctly', inject(function($filter) {
var limitAddress = $filter('limitAddress');
var addresses = [{
isChange: true,
balance: 0
}, {
isChange: false,
balance: 0
}, {
isChange: true,
balance: 0
}, {
isChange: false,
balance: 0
}, {
isChange: true,
balance: 0
}, {
isChange: false,
balance: 0
}, {
isChange: true,
balance: 0
}, {
isChange: false,
balance: 0
}];
expect(limitAddress(addresses, false).length).to.equal(1);
addresses[0].isChange = false;
expect(limitAddress(addresses, false).length).to.equal(2);
addresses[2].isChange = false;
expect(limitAddress(addresses, false).length).to.equal(3);
addresses[3].isChange = false;
expect(limitAddress(addresses, false).length).to.equal(3);
addresses[0].balance = 20;
expect(limitAddress(addresses, false).length).to.equal(3);
addresses[0].balance = 20;
expect(limitAddress(addresses, false).length).to.equal(3);
addresses[7].balance = 20;
expect(limitAddress(addresses, false).length).to.equal(4);
}));
});
}); });

View file

@ -126,7 +126,7 @@ describe("Unit: isMobile Service", function() {
isMobile.any().should.equal(false); isMobile.any().should.equal(false);
})); }));
it('should detect mobile if user agent is Android', inject(function(isMobile) { it('should detect mobile if user agent is Android', inject(function(isMobile) {
navigator.__defineGetter__('userAgent', function(){ navigator.__defineGetter__('userAgent', function() {
return 'Android 2.2.3'; return 'Android 2.2.3';
}); });
isMobile.any().should.equal(true); isMobile.any().should.equal(true);