diff --git a/.eslintrc.yml b/.eslintrc.yml
index b0d68de7d..a3d15d411 100644
--- a/.eslintrc.yml
+++ b/.eslintrc.yml
@@ -7,7 +7,6 @@ settings:
import/core-modules: ## don't lint for these missing packages in package.json
- electron ## 'electron' is only needed as devDependency / global installation
-
rules:
# "off" or 0 - turn the rule off
# "warn" or 1 - turn the rule on as a warning (doesn’t affect exit code)
@@ -22,7 +21,12 @@ rules:
no-underscore-dangle: off
comma-dangle:
- error
- - only-multiline
+ - only-multiline ## no comma after last item if one line, though allow comma if multiline
+ import/no-extraneous-dependencies: ## checks if required modules are missing in packages.json
+ - error
+ - devDependencies: ## declares files, whose imports belong to devDependencies
+ - "**/scripts/*.js"
+ - "**/*.test.js"
globals: # don't warn about missing declarations
i18n: true
diff --git a/README.md b/README.md
index 1ca1bb7bc..ece726016 100644
--- a/README.md
+++ b/README.md
@@ -3,6 +3,8 @@
[![Join the chat at https://gitter.im/ethereum/mist](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/ethereum/mist?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
[![Build Status develop branch](https://travis-ci.org/ethereum/mist.svg?branch=develop)](https://travis-ci.org/ethereum/mist)
[![Code Climate](https://codeclimate.com/github/ethereum/mist/badges/gpa.svg)](https://codeclimate.com/github/ethereum/mist)
+[![dependencies Status](https://david-dm.org/ethereum/mist/status.svg)](https://david-dm.org/ethereum/mist)
+[![devDependencies Status](https://david-dm.org/ethereum/mist/dev-status.svg)](https://david-dm.org/ethereum/mist?type=dev)
The Mist browser is the tool of choice to browse and use Ðapps.
@@ -42,7 +44,7 @@ To run mist in development you need [Node.js NPM](https://nodejs.org) and [Meteo
$ npm install -g electron@1.3.13
$ npm install -g gulp
-And some futher tools to help with downloading and unzipping client nodes:
+And some further tools to help with downloading and unzipping client nodes:
_Linux:_
diff --git a/errorPages/400.html b/errorPages/400.html
index 06d5acc2d..e4ceb706d 100644
--- a/errorPages/400.html
+++ b/errorPages/400.html
@@ -7,7 +7,7 @@
background-color: #f0f0f0;
color: #ACACAC;
text-shadow: 0 -1px #fff;
- font: 20px Helvetica Neue, Arial;
+ font: 20px Source Sans Pro, Helvetica Neue, Arial;
font-weight: 200;
text-align: center;
padding: 10px;
diff --git a/errorPages/404.html b/errorPages/404.html
new file mode 100644
index 000000000..c0eafb982
--- /dev/null
+++ b/errorPages/404.html
@@ -0,0 +1,19 @@
+
+
+ Error 404
+
+
+
+ ﴾๏๏﴿
+ URL not found.
+
+
\ No newline at end of file
diff --git a/errorPages/500.html b/errorPages/500.html
new file mode 100644
index 000000000..57aee2e60
--- /dev/null
+++ b/errorPages/500.html
@@ -0,0 +1,19 @@
+
+
+ Error 500
+
+
+
+ (ノಠ益ಠ)ノ
+ Oops.. Something went wrong!
+
+
\ No newline at end of file
diff --git a/interface/client/styles/networkIndicator.import.less b/interface/client/styles/networkIndicator.import.less
index c46672228..0cc4165a0 100644
--- a/interface/client/styles/networkIndicator.import.less
+++ b/interface/client/styles/networkIndicator.import.less
@@ -1,3 +1,4 @@
+// Applies to both onboarding and splash screen
.network-indicator {
position: absolute;
top: 10px;
@@ -9,8 +10,10 @@
opacity: 0.8;
color: @colorGrey;
text-transform: uppercase;
+}
- .unknown {
+// Applies only to the splash screen
+.splash-screen .network-indicator .unknown {
position: absolute;
top: 155px;
text-align: center;
@@ -21,5 +24,4 @@
font-weight: normal;
text-transform: none;
color: #fff;
- }
-}
+}
\ No newline at end of file
diff --git a/interface/client/templates/popupWindows/onboardingScreen.html b/interface/client/templates/popupWindows/onboardingScreen.html
index 738d3bb17..3447cf330 100644
--- a/interface/client/templates/popupWindows/onboardingScreen.html
+++ b/interface/client/templates/popupWindows/onboardingScreen.html
@@ -113,27 +113,24 @@
{{i18n "mist.popupWindows.onboarding.learnIt"}}
diff --git a/interface/client/templates/popupWindows/onboardingScreen.js b/interface/client/templates/popupWindows/onboardingScreen.js
index eec33cbd9..34ee0aedc 100644
--- a/interface/client/templates/popupWindows/onboardingScreen.js
+++ b/interface/client/templates/popupWindows/onboardingScreen.js
@@ -38,11 +38,11 @@ Template['popupWindows_onboardingScreen'].onCreated(function() {
if (syncing === true) {
web3.reset(true);
-
- } else if (_.isObject(syncing)) {
+ } else if(_.isObject(syncing)) {
// loads syncing data and adds it to old by using 'extend'
var oldData = TemplateVar.get(template, 'syncing');
- TemplateVar.set(template, 'syncing', _.extend(oldData || {}, syncing || {}));
+
+ TemplateVar.set(template, 'syncing', _.extend(oldData||{}, syncing||{}));
} else {
TemplateVar.set(template, 'syncing', false);
@@ -78,7 +78,7 @@ Template['popupWindows_onboardingScreen'].helpers({
return (account) ? account.toLowerCase() : '';
},
/**
- Updates the Sync Message live
+ Updates the Sync Data
@method syncStatus
*/
@@ -86,42 +86,61 @@ Template['popupWindows_onboardingScreen'].helpers({
// This functions loops trhough numbers while waiting for the node to respond
var template = Template.instance();
+
Meteor.clearInterval(template._intervalId);
// Create an interval to quickly iterate trough the numbers
template._intervalId = Meteor.setInterval(function() {
// load the sync information
- var syncing = TemplateVar.get(template, 'syncing');
+ var syncing = TemplateVar.get(template, 'syncing');
- // Calculates a block t display that is always getting 1% closer to target
- syncing._displayBlock = (syncing._displayBlock + (syncing.currentBlock - syncing._displayBlock) / 100) || syncing.currentBlock;
+ if (syncing) {
+ // If it's syncing, then it's not ready
+ TemplateVar.set(template, 'readyToLaunch', false);
- syncing._displayStatesDownload = Number(syncing._displayStatesDownload + (syncing.pulledStates / syncing.knownStates - syncing._displayStatesDownload) / 100) || syncing.pulledStates / syncing.knownStates;
+ // Calculates a block t display that is always getting a few % closer to target
+ syncing._displayBlock = (syncing._displayBlock + 2*(syncing.currentBlock - syncing._displayBlock) / 100 ) || Number(syncing.startingBlock);
+ syncing._displayStatesDownload = Number(syncing._displayStatesDownload + (syncing.pulledStates/(1 +syncing.knownStates) - syncing._displayStatesDownload) / 100 ) || Number(syncing.pulledStates)/Number(syncing.knownStates + 1);
- // Calculates progress
- syncing.progress = Math.round(((syncing._displayBlock - syncing.startingBlock) / (syncing.highestBlock - syncing.startingBlock)) * 100);
+ // Calculates progress
+ syncing.progress = 100 * (syncing._displayBlock - syncing.startingBlock) / (1 + Number(syncing.highestBlock) - syncing.startingBlock);
- // Makes fancy strings
- syncing.blockDiff = numeral(syncing.highestBlock - syncing.currentBlock).format('0,0');
- syncing.highestBlockString = numeral(syncing.highestBlock).format('0,0');
- syncing.displayBlock = numeral(Math.round(syncing._displayBlock)).format('0,0');
- syncing.statesPercent = numeral(Math.round(syncing._displayStatesDownload * 10000) / 100).format('0.00');
+ // Makes fancy strings
+ syncing.blockDiff = numeral(syncing.highestBlock - syncing.currentBlock).format('0,0');
+ syncing.highestBlockString = numeral(syncing.highestBlock).format('0,0');
+ syncing.displayBlock = numeral(Math.round(syncing._displayBlock)).format('0,0');
+ syncing.statesPercent = numeral(Math.round(syncing._displayStatesDownload*10000)/100).format('0.00');
- // Saves the data back to the object
- TemplateVar.set(template, 'syncing', syncing);
+ // Saves the data back to the object
+ TemplateVar.set(template, 'syncing', syncing);
- // Only show states if they are less than 50% downloaded
- if (Math.round(1000 * Number(syncing._displayStatesDownload)) !== Math.round(1000 * Number(syncing.pulledStates / syncing.knownStates))) {
- TemplateVar.set(template, "syncStatusMessageLive", TAPi18n.__('mist.popupWindows.onboarding.syncMessageWithStates', syncing));
- } else {
- TemplateVar.set(template, "syncStatusMessageLive", TAPi18n.__('mist.popupWindows.onboarding.syncMessage', syncing));
- }
+ // If it's close enough, show the synced button
+
+ if (Number(syncing.highestBlock) - syncing.currentBlock < 100 ) {
+ TemplateVar.set(template, 'readyToLaunch', true);
+ }
+
+ // Only show states if they are changing
+ if (Math.round(1000*Number(syncing._displayStatesDownload)) !== Math.round(1000*Number(syncing.pulledStates/(syncing.knownStates+1)))) {
+ TemplateVar.set(template, "syncStatusMessageLive", TAPi18n.__('mist.popupWindows.onboarding.syncMessageWithStates', syncing));
+ } else if (syncing.displayBlock == '0') {
+ TemplateVar.set(template, "syncStatusMessageLive", '');
+ } else {
+ TemplateVar.set(template, "syncStatusMessageLive", TAPi18n.__('mist.popupWindows.onboarding.syncMessage', syncing));
+ }
+ }
}, 50);
+ },
+ /**
+ Updates the Sync Message live
- return TemplateVar.get(template, "syncStatusMessageLive");
+ @method syncStatusMessage
+ */
+ 'syncStatusMessage' : function() {
+ return TemplateVar.get("syncStatusMessageLive");
}
});
@@ -136,12 +155,14 @@ Template['popupWindows_onboardingScreen'].events({
if (TemplateVar.get('testnet')) {
ipc.send('onBoarding_changeNet', false);
TemplateVar.set('testnet', false);
+ TemplateVar.set('syncing', null);
}
},
'click .start-testnet': function(e, template) {
if (!TemplateVar.get('testnet')) {
ipc.send('onBoarding_changeNet', true);
TemplateVar.set('testnet', true);
+ TemplateVar.set('syncing', null);
}
TemplateVar.set('currentActive', 'testnet');
@@ -154,17 +175,20 @@ Template['popupWindows_onboardingScreen'].events({
'click .goto-account': function() {
TemplateVar.set('currentActive', 'account');
},
- 'click .goto-tutorial-1': function() {
- TemplateVar.set('currentActive', 'tutorial-1');
- TemplateVar.set('readyToLaunch', true);
+ 'click .goto-tutorial-1': function(){
+ TemplateVar.set('currentActive','tutorial-1');
+ if (!TemplateVar.get('syncing'))
+ TemplateVar.set('readyToLaunch', true);
},
- 'click .goto-tutorial-2': function() {
- TemplateVar.set('currentActive', 'tutorial-2');
- TemplateVar.set('readyToLaunch', true);
+ 'click .goto-tutorial-2': function(){
+ TemplateVar.set('currentActive','tutorial-2');
+ if (!TemplateVar.get('syncing'))
+ TemplateVar.set('readyToLaunch', true);
},
- 'click .goto-tutorial-3': function() {
- TemplateVar.set('currentActive', 'tutorial-3');
- TemplateVar.set('readyToLaunch', true);
+ 'click .goto-tutorial-3': function(){
+ TemplateVar.set('currentActive','tutorial-3');
+ if (!TemplateVar.get('syncing'))
+ TemplateVar.set('readyToLaunch', true);
},
/**
Start the application
diff --git a/interface/client/templates/popupWindows/splashScreen.js b/interface/client/templates/popupWindows/splashScreen.js
index fa08643dc..4aea7fd79 100644
--- a/interface/client/templates/popupWindows/splashScreen.js
+++ b/interface/client/templates/popupWindows/splashScreen.js
@@ -190,9 +190,9 @@ Template['popupWindows_splashScreen'].helpers({
syncData._displayKnownStates = Number(syncData.knownStates || 0);
} else {
// Increment each them slowly to match target number
- syncData._displayBlock += (Number(syncData.currentBlock) - syncData._displayBlock) / 10;
- syncData._displayState += (Number(syncData.pulledStates || 0) - syncData._displayState) / 10;
- syncData._displayKnownStates += (Number(syncData.knownStates || 0) - syncData._displayKnownStates) / 10;
+ syncData._displayBlock += (Number(syncData.currentBlock) - syncData._displayBlock) / 100;
+ syncData._displayState += (Number(syncData.pulledStates || 0) - syncData._displayState) / 100;
+ syncData._displayKnownStates += (Number(syncData.knownStates || 0) - syncData._displayKnownStates) / 100;
}
// Create the fancy strings
@@ -225,7 +225,7 @@ Template['popupWindows_splashScreen'].helpers({
}
}
- }, 100);
+ }, 10);
return TemplateVar.get(template, "syncStatusMessageLive");
}
diff --git a/interface/client/templates/views/webview.js b/interface/client/templates/views/webview.js
index b64aa6ec6..e28d4b848 100644
--- a/interface/client/templates/views/webview.js
+++ b/interface/client/templates/views/webview.js
@@ -60,6 +60,10 @@ Template['views_webview'].onRendered(function(){
webviewLoadStop.call(this, tabId, e);
});
+ // show error pages
+ webview.addEventListener('did-fail-load', showError.bind(webview, tabId));
+ webview.addEventListener('crashed', showError.bind(webview, tabId));
+
// navigate page, and redirect to browser tab if necessary
webview.addEventListener('will-navigate', webviewLoadStart.bind(webview, tabId));
webview.addEventListener('did-get-redirect-request', webviewLoadStart.bind(webview, tabId));
@@ -122,6 +126,10 @@ Template['views_webview'].helpers({
}});
}
+ // allow error pages
+ if(url && url.indexOf('file://'+ dirname + '/errorPages/') === 0) {
+ return url;
+ }
// CHECK URL and throw error if not allowed
if(!Helpers.sanitizeUrl(url, true)) {
@@ -137,7 +145,7 @@ Template['views_webview'].helpers({
return 'file://'+ dirname + '/errorPages/400.html';
}
- // remove redirect
+ // add url
if(url) {
template.url = url;
Tabs.update(this._id, {$set: {
diff --git a/interface/client/templates/webviewEvents.js b/interface/client/templates/webviewEvents.js
index 26e10bfc8..332676911 100644
--- a/interface/client/templates/webviewEvents.js
+++ b/interface/client/templates/webviewEvents.js
@@ -1,4 +1,31 @@
+showError = function(tabId, e){
+ if(e.isMainFrame || e.killed) {
+ var url,
+ path = 'file://'+ dirname + '/errorPages/';
+
+ if(e.killed) {
+ e.errorCode = 500;
+ }
+
+ switch(e.errorCode) {
+ case -105:
+ url = path +'404.html';
+ break;
+ case 500:
+ url = path +'500.html';
+ break;
+ }
+
+ if(url) {
+ Tabs.update(tabId, {$set: {
+ redirect: url
+ }});
+ }
+ }
+};
+
+
webviewChangeUrl = function(tabId, e){
if(e.type === 'did-navigate-in-page' && !e.isMainFrame)
return;
diff --git a/interface/i18n/mist.en.i18n.json b/interface/i18n/mist.en.i18n.json
index 2b0b04f29..39df06f00 100644
--- a/interface/i18n/mist.en.i18n.json
+++ b/interface/i18n/mist.en.i18n.json
@@ -64,6 +64,10 @@
"ethereumNode": "Ethereum Node",
"network": "Network",
"mainNetwork": "Main Network",
+ "nodeMode": "Chain download",
+ "fullNode": "Store full blockchain",
+ "lightNode": "Use light Node (experimental!)",
+ "mainNetwork": "Main Network",
"startMining": "⛏ Start Mining (Testnet only)",
"stopMining": "⛏ Stop Mining"
},
@@ -226,7 +230,7 @@
"lookupDataExplainer": "Look this up on the internet"
},
"onboarding": {
- "description" : "Ethereum is a public blockchain that features a turing complete programming for building solid, decentralized applications.",
+ "description" : "Ethereum is a platform for decentralized blockchain apps with a fully featured programming language",
"goToTestnet" : "Use the test network",
"goToTestnetDescription" : "Test the technology freely in a sandboxed testnet, without using real ether.",
"gotoMainnet" : "Use the main network",
@@ -253,6 +257,7 @@
"downloadingBlocks": "Downloading blocks",
"syncMessage": "Block __displayBlock__ of __highestBlockString__",
"syncMessageWithStates": "Block __displayBlock__ of __highestBlockString__ (Chain structure __statesPercent__%)",
+ "startingSync": "Getting ready to sync..",
"tutorial1Description" : "
Now the only thing left to do is wait for the download to finish. Here's some reading suggestions:
Make your own money
Make a cryptocurrency with a fixed market supply, tokens representing real world assets, etc
",
"tutorial2Description" : "
Create a crowdsale
Raise funds for a common goal, fully trustable without a third party. Sidestep the hurdle of traditional funding system and go directly to the source by funding an organization via the blockchain.
",
"tutorial3Description" : "
Create a blockchain organization
Create an autonomous organization with rules on spending money and making decisions for you and your investors.