aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/index.html3
-rw-r--r--src/js/index.js157
2 files changed, 145 insertions, 15 deletions
diff --git a/src/index.html b/src/index.html
index fc3a6d1..56d73e6 100644
--- a/src/index.html
+++ b/src/index.html
@@ -40,6 +40,9 @@
40 box-shadow: inset 0 1px 1px rgba(0,0,0,.0); 40 box-shadow: inset 0 1px 1px rgba(0,0,0,.0);
41 -webkit-box-shadow: inset 0 1px 1px rgba(0,0,0,.0); 41 -webkit-box-shadow: inset 0 1px 1px rgba(0,0,0,.0);
42 } 42 }
43 .phrase {
44 word-break: keep-all;
45 }
43 .strength { 46 .strength {
44 /* override mobile width from bootstrap */ 47 /* override mobile width from bootstrap */
45 width: auto!important; 48 width: auto!important;
diff --git a/src/js/index.js b/src/js/index.js
index 69f5eab..c5f6c11 100644
--- a/src/js/index.js
+++ b/src/js/index.js
@@ -1,6 +1,8 @@
1(function() { 1(function() {
2 2
3 var mnemonic = new Mnemonic("english"); 3 // mnemonics is populated as required by getLanguage
4 var mnemonics = { "english": new Mnemonic("english") };
5 var mnemonic = mnemonics["english"];
4 var seed = null 6 var seed = null
5 var bip32RootKey = null; 7 var bip32RootKey = null;
6 var bip32ExtendedKey = null; 8 var bip32ExtendedKey = null;
@@ -44,6 +46,7 @@
44 DOM.indexToggle = $(".index-toggle"); 46 DOM.indexToggle = $(".index-toggle");
45 DOM.addressToggle = $(".address-toggle"); 47 DOM.addressToggle = $(".address-toggle");
46 DOM.privateKeyToggle = $(".private-key-toggle"); 48 DOM.privateKeyToggle = $(".private-key-toggle");
49 DOM.languages = $(".languages a");
47 50
48 function init() { 51 function init() {
49 // Events 52 // Events
@@ -63,6 +66,7 @@
63 DOM.indexToggle.on("click", toggleIndexes); 66 DOM.indexToggle.on("click", toggleIndexes);
64 DOM.addressToggle.on("click", toggleAddresses); 67 DOM.addressToggle.on("click", toggleAddresses);
65 DOM.privateKeyToggle.on("click", togglePrivateKeys); 68 DOM.privateKeyToggle.on("click", togglePrivateKeys);
69 DOM.languages.on("click", languageChanged);
66 disableForms(); 70 disableForms();
67 hidePending(); 71 hidePending();
68 hideValidationError(); 72 hideValidationError();
@@ -94,6 +98,7 @@
94 function phraseChanged() { 98 function phraseChanged() {
95 showPending(); 99 showPending();
96 hideValidationError(); 100 hideValidationError();
101 setMnemonicLanguage();
97 // Get the mnemonic phrase 102 // Get the mnemonic phrase
98 var phrase = DOM.phrase.val(); 103 var phrase = DOM.phrase.val();
99 var errorText = findPhraseErrors(phrase); 104 var errorText = findPhraseErrors(phrase);
@@ -163,6 +168,7 @@
163 clearDisplay(); 168 clearDisplay();
164 showPending(); 169 showPending();
165 setTimeout(function() { 170 setTimeout(function() {
171 setMnemonicLanguage();
166 var phrase = generateRandomPhrase(); 172 var phrase = generateRandomPhrase();
167 if (!phrase) { 173 if (!phrase) {
168 return; 174 return;
@@ -171,6 +177,20 @@
171 }, 50); 177 }, 50);
172 } 178 }
173 179
180 function languageChanged() {
181 setTimeout(function() {
182 setMnemonicLanguage();
183 if (DOM.phrase.val().length > 0) {
184 var newPhrase = convertPhraseToNewLanguage();
185 DOM.phrase.val(newPhrase);
186 phraseChanged();
187 }
188 else {
189 DOM.generate.trigger("click");
190 }
191 }, 50);
192 }
193
174 function toggleIndexes() { 194 function toggleIndexes() {
175 showIndex = !showIndex; 195 showIndex = !showIndex;
176 $("td.index span").toggleClass("invisible"); 196 $("td.index span").toggleClass("invisible");
@@ -246,26 +266,19 @@
246 // TODO make this right 266 // TODO make this right
247 // Preprocess the words 267 // Preprocess the words
248 phrase = mnemonic.normalizeString(phrase); 268 phrase = mnemonic.normalizeString(phrase);
249 var parts = phrase.split(" "); 269 var words = phraseToWordArray(phrase);
250 var proper = [];
251 for (var i=0; i<parts.length; i++) {
252 var part = parts[i];
253 if (part.length > 0) {
254 // TODO check that lowercasing is always valid to do
255 proper.push(part.toLowerCase());
256 }
257 }
258 var properPhrase = proper.join(' ');
259 // Check each word 270 // Check each word
260 for (var i=0; i<proper.length; i++) { 271 for (var i=0; i<words.length; i++) {
261 var word = proper[i]; 272 var word = words[i];
262 if (WORDLISTS["english"].indexOf(word) == -1) { 273 var language = getLanguage();
274 if (WORDLISTS[language].indexOf(word) == -1) {
263 console.log("Finding closest match to " + word); 275 console.log("Finding closest match to " + word);
264 var nearestWord = findNearestWord(word); 276 var nearestWord = findNearestWord(word);
265 return word + " not in wordlist, did you mean " + nearestWord + "?"; 277 return word + " not in wordlist, did you mean " + nearestWord + "?";
266 } 278 }
267 } 279 }
268 // Check the words are valid 280 // Check the words are valid
281 var properPhrase = wordArrayToPhrase(words);
269 var isValid = mnemonic.check(properPhrase); 282 var isValid = mnemonic.check(properPhrase);
270 if (!isValid) { 283 if (!isValid) {
271 return "Invalid mnemonic"; 284 return "Invalid mnemonic";
@@ -479,7 +492,8 @@
479 } 492 }
480 493
481 function findNearestWord(word) { 494 function findNearestWord(word) {
482 var words = WORDLISTS["english"]; 495 var language = getLanguage();
496 var words = WORDLISTS[language];
483 var minDistance = 99; 497 var minDistance = 99;
484 var closestWord = words[0]; 498 var closestWord = words[0];
485 for (var i=0; i<words.length; i++) { 499 for (var i=0; i<words.length; i++) {
@@ -509,6 +523,119 @@
509 } 523 }
510 } 524 }
511 525
526 function getLanguage() {
527 var defaultLanguage = "english";
528 // Try to get from existing phrase
529 var language = getLanguageFromPhrase();
530 // Try to get from url if not from phrase
531 if (language.length == 0) {
532 language = getLanguageFromUrl();
533 }
534 // Default to English if no other option
535 if (language.length == 0) {
536 language = defaultLanguage;
537 }
538 return language;
539 }
540
541 function getLanguageFromPhrase(phrase) {
542 // Check if how many words from existing phrase match a language.
543 var language = "";
544 if (!phrase) {
545 phrase = DOM.phrase.val();
546 }
547 if (phrase.length > 0) {
548 var words = phraseToWordArray(phrase);
549 var languageMatches = {};
550 for (l in WORDLISTS) {
551 // Track how many words match in this language
552 languageMatches[l] = 0;
553 for (var i=0; i<words.length; i++) {
554 var wordInLanguage = WORDLISTS[l].indexOf(words[i]) > -1;
555 if (wordInLanguage) {
556 languageMatches[l]++;
557 }
558 }
559 // Find languages with most word matches.
560 // This is made difficult due to commonalities between Chinese
561 // simplified vs traditional.
562 var mostMatches = 0;
563 var mostMatchedLanguages = [];
564 for (var l in languageMatches) {
565 var numMatches = languageMatches[l];
566 if (numMatches > mostMatches) {
567 mostMatches = numMatches;
568 mostMatchedLanguages = [l];
569 }
570 else if (numMatches == mostMatches) {
571 mostMatchedLanguages.push(l);
572 }
573 }
574 }
575 if (mostMatchedLanguages.length > 0) {
576 // Use first language and warn if multiple detected
577 language = mostMatchedLanguages[0];
578 if (mostMatchedLanguages.length > 1) {
579 console.warn("Multiple possible languages");
580 console.warn(mostMatchedLanguages);
581 }
582 }
583 }
584 return language;
585 }
586
587 function getLanguageFromUrl() {
588 return window.location.hash.substring(1);
589 }
590
591 function setMnemonicLanguage() {
592 var language = getLanguage();
593 // Load the bip39 mnemonic generator for this language if required
594 if (!(language in mnemonics)) {
595 mnemonics[language] = new Mnemonic(language);
596 }
597 mnemonic = mnemonics[language];
598 }
599
600 function convertPhraseToNewLanguage() {
601 var oldLanguage = getLanguageFromPhrase();
602 var newLanguage = getLanguageFromUrl();
603 var oldPhrase = DOM.phrase.val();
604 var oldWords = phraseToWordArray(oldPhrase);
605 var newWords = [];
606 for (var i=0; i<oldWords.length; i++) {
607 var oldWord = oldWords[i];
608 var index = WORDLISTS[oldLanguage].indexOf(oldWord);
609 var newWord = WORDLISTS[newLanguage][index];
610 newWords.push(newWord);
611 }
612 newPhrase = wordArrayToPhrase(newWords);
613 return newPhrase;
614 }
615
616 // TODO look at jsbip39 - mnemonic.splitWords
617 function phraseToWordArray(phrase) {
618 var words = phrase.split(/\s/g);
619 var noBlanks = [];
620 for (var i=0; i<words.length; i++) {
621 var word = words[i];
622 if (word.length > 0) {
623 noBlanks.push(word);
624 }
625 }
626 return noBlanks;
627 }
628
629 // TODO look at jsbip39 - mnemonic.joinWords
630 function wordArrayToPhrase(words) {
631 var phrase = words.join(" ");
632 var language = getLanguageFromPhrase(phrase);
633 if (language == "japanese") {
634 phrase = words.join("\u3000");
635 }
636 return phrase;
637 }
638
512 var networks = [ 639 var networks = [
513 { 640 {
514 name: "Bitcoin", 641 name: "Bitcoin",