diff --git a/src/bip39.js b/src/bip39.js index 3abfb57..f12f5ec 100644 --- a/src/bip39.js +++ b/src/bip39.js @@ -1,3 +1,4 @@ + var CryptoJS = require('crypto-js') var crypto = require('crypto') diff --git a/src/index-mobile.js b/src/index-mobile.js new file mode 100644 index 0000000..4cddf3b --- /dev/null +++ b/src/index-mobile.js @@ -0,0 +1,6 @@ + +module.exports = { + API: require('./ninki-api'), + Engine: require('./ninki-engine'), + UI: require('./ninki-ui-mobile') +} diff --git a/src/ninki-api.js b/src/ninki-api.js index 445f6cf..953f718 100644 --- a/src/ninki-api.js +++ b/src/ninki-api.js @@ -1,9 +1,9 @@ var jQuery = require('jquery-browserify'); var $ = require('jquery-browserify'); +var sanitizer = require('sanitizer'); var common = require('./common'); var config = require('./config'); - function API() { @@ -20,7 +20,7 @@ var setCSRFToken = function (securityToken) { }); }; -setCSRFToken($('meta[name="token"]').attr('content')); +//setCSRFToken($('meta[name="token"]').attr('content')); API.get = function (url, querydata, callback) { return this.get(url, querydata, callback); @@ -33,8 +33,6 @@ function get(url, querydata, callback) { }).fail(function (data, textStatus) { - - return callback(true, { textStatus: textStatus, data: data @@ -48,21 +46,34 @@ API.post = function (url, postData, callback) { function lpost(url, postData, callback) { + //https://ninkip2p.com + + var sessionToken = $('#API-Token').val(); + $.ajax({ - url: url, + url: "https://localhost:1111" + url, type: "POST", data: JSON.stringify(postData), contentType: "application/json; charset=utf-8", dataType: "json", + headers: { 'api-token': sessionToken }, success: function (data) { + + + data = sanitizer.sanitize(data); + + var jdata = JSON.parse(data); + + + if (jdata.error) { return callback(true, jdata.message); } if (!(typeof jdata.message === "undefined")) { return callback(false, jdata.message); } - return callback(false, data); + return callback(false, JSON.stringify(jdata)); }, fail: function (data, textStatus) { return callback(true, { @@ -75,9 +86,35 @@ function lpost(url, postData, callback) { console.log(data); if (data.status == 403) { //session has been lost - location.reload(); + + } + else if (data.status == 401) { + + // if (!window.cordova) { + // if (chrome) { + // if (chrome.runtime) { + // if (chrome.runtime.reload) { + // chrome.runtime.reload() + // } else { + // location.reload(); + // } + // } else { + // location.reload(); + // } + // //return callback(true, data.statusText); + // } else { + // location.reload(); + // } + // } else { + // + // } + + + } else { + return callback(true, data.responseText); } - return callback(true, data.responseText); + + } }); } @@ -113,11 +150,12 @@ API.getMasterPublicKeyFromUpstreamServer = function (guid, callback) { var responseBody = JSON.parse(response); var userToken = responseBody.UserToken; var ninkiKey = responseBody.NinkiMasterPublicKey; + var secret = responseBody.Secret; if (!responseBody.UserToken) { return callback(true, "ErrMasterKeyJSON"); } else { - return callback(null, ninkiKey, userToken); + return callback(null, ninkiKey, userToken, secret); } } }); @@ -125,9 +163,9 @@ API.getMasterPublicKeyFromUpstreamServer = function (guid, callback) { //function doesUsernameExist //verifies that the requested username does not already exist on our database -API.doesAccountExist = function (username,email, callback) { +API.doesAccountExist = function (username, email, callback) { - var postData = { username: username, email:email }; + var postData = { username: username, email: email }; lpost("/api/1/u/doesaccountexist", postData, function (err, response) { if (err) { @@ -164,18 +202,68 @@ API.getEmailValidation = function (guid, sharedid, token, callback) { } +API.getResetToken = function (guid, callback) { + API.post("/api/1/u/getresettoken", { + guid: guid + }, function (err, response) { + callback(err, response); + }); -API.getWalletFromServer = function (guid, twoFactorCode, callback) { +} - var postData = { guid: guid, twoFactorCode: twoFactorCode }; + +API.validateSecret = function (guid, secret, callback) { + + var postData = { guid: guid, secret: secret }; + return lpost("/api/1/u/validatesecret", postData, function (err, dataStr) { + + if (err) { + return callback(err, dataStr); + } else { + var data = JSON.parse(dataStr); + return callback(err, data); + } + }); +} + +API.updateSecretPacket = function (guid, sharedid, vc, iv, callback) { + + var postData = { guid: guid, sharedid: sharedid, vc: vc, iv: iv }; + return lpost("/api/1/u/updatesecretpacket", postData, function (err, dataStr) { + return callback(err, dataStr); + }); +} + +API.unlockaccount = function (guid, token, callback) { + + var postData = { guid: guid, token: token }; + return lpost("/api/1/u/unlockaccount", postData, function (err, dataStr) { + return callback(err, dataStr); + }); +} + + +API.getWalletFromServer = function (guid, secret, twoFactorCode, rememberTwoFactor, callback) { + + var postData = { guid: guid, secret: secret, twoFactorCode: twoFactorCode, rememberTwoFactor: rememberTwoFactor }; return lpost("/api/1/u/getaccountdetails", postData, function (err, dataStr) { if (err) { return callback(err, dataStr); } else { var data = JSON.parse(dataStr); + var currentToken = $("#API-Token").val(); + + //if (currentToken.length == 0) { + if (data.SessionToken) { + $("#API-Token").val(data.SessionToken); + } else { + $("#API-Token").val(''); + } + //} + return callback(err, data); } }); @@ -237,10 +325,25 @@ API.getNickname = function (guid, sharedid, callback) { }); } +API.getUserProfile = function (guid, sharedid, callback) { + var postData = { guid: guid, sharedid: sharedid }; + return lpost("/api/1/u/getuserprofile", postData, function (err, dataStr) { + return callback(err, dataStr); + }); +} + +API.updateUserProfile = function (guid, sharedid, profileImage, status, tax, callback) { + var postData = { guid: guid, sharedid: sharedid, profileImage: profileImage, status: status, tax: tax }; + return lpost("/api/1/u/updateuserprofile", postData, function (err, dataStr) { + return callback(err, dataStr); + }); +} + + //function returns all outputs unspent by the wallet -API.getUnspentOutputs = function (guid, callback) { +API.getUnspentOutputs = function (guid, sharedid, callback) { - var postData = { guid: guid }; + var postData = { guid: guid, sharedid: sharedid }; return lpost("/api/1/u/getunspentoutputs", postData, function (err, response) { var data1 = response; var data2 = JSON.parse(data1); @@ -248,6 +351,24 @@ API.getUnspentOutputs = function (guid, callback) { }); } +//function returns all outputs unspent by the wallet +API.getCoinProfile = function (guid, sharedid, callback) { + + var postData = { guid: guid, sharedid: sharedid }; + return lpost("/api/1/u/getcoinprofile", postData, function (err, response) { + var data = JSON.parse(response); + return callback(err, data); + }); +} + +API.getPrice = function (ccy, callback) { + + var postData = { ccy: ccy }; + return lpost("/api/1/u/getPrice", postData, function (err, response) { + var data = JSON.parse(response); + return callback(err, data); + }); +} API.getPendingUserRequests = function (guid, sharedid, callback) { @@ -273,6 +394,7 @@ API.getUserPacket = function (guid, sharedid, callback) { var postData = { guid: guid, sharedid: sharedid }; return lpost("/api/1/u/getuserpacket", postData, function (err, dataStr) { + var jdata = JSON.parse(dataStr); return callback(err, jdata); }); @@ -337,6 +459,8 @@ API.getInvoiceByUserList = function (guid, sharedid, callback) { } + + API.updateInvoice = function (guid, sharedid, username, invoiceId, transactionId, status, callback) { var postData = { guid: guid, sharedid: sharedid, userName: username, invoiceId: invoiceId, transactionId: transactionId, status: status }; return lpost("/api/1/u/updateinvoice", postData, function (err, dataStr) { @@ -344,5 +468,73 @@ API.updateInvoice = function (guid, sharedid, username, invoiceId, transactionId }); } -module.exports = API +API.getVersion = function (callback) { + var postData = {}; + return lpost("/api/1/u/getversion", postData, function (err, dataStr) { + return callback(err, dataStr); + }); +} + +API.registerDevice = function (guid, deviceName, deviceId, deviceModel, devicePIN, regToken, secret, callback) { + var postData = { guid: guid, deviceName: deviceName, deviceId: deviceId, deviceModel: deviceModel, devicePIN: devicePIN, regToken: regToken, secret: secret }; + return lpost("/api/1/u/registerdevice", postData, function (err, dataStr) { + return callback(err, dataStr); + }); +} + +API.getDeviceKey = function (guid, devicePIN, regToken, callback) { + var postData = { guid: guid, devicePIN: devicePIN, regToken: regToken}; + return lpost("/api/1/u/getdevicekey", postData, function (err, dataStr) { + return callback(err, dataStr); + }); +} + +API.destroyDevice = function (guid, regToken, callback) { + var postData = { guid: guid, regToken: regToken }; + return lpost("/api/1/u/destroydevice", postData, function (err, dataStr) { + return callback(err, dataStr); + }); +} + +API.destroyDevice2fa = function (guid, sharedid, deviceName, twoFactorCode, callback) { + var postData = { guid: guid, sharedid: sharedid, deviceName: deviceName, twoFactorCode: twoFactorCode }; + return lpost("/api/1/u/destroydevice2fa", postData, function (err, dataStr) { + return callback(err, dataStr); + }); +} + + + +API.createDevice = function (guid, sharedid, deviceName, callback) { + var postData = { guid: guid, sharedid: sharedid, deviceName: deviceName }; + return lpost("/api/1/u/createdevice", postData, function (err, dataStr) { + return callback(err, dataStr); + }); +} + + +API.getDevices = function (guid, sharedid, callback) { + var postData = { guid: guid, sharedid: sharedid }; + return lpost("/api/1/u/getdevices", postData, function (err, dataStr) { + var jdevs = JSON.parse(dataStr); + return callback(err, jdevs); + }); +} + +API.getDeviceToken = function (guid, sharedid, deviceName, twoFactorCode, callback) { + var postData = { guid: guid, sharedid: sharedid, deviceName: deviceName, twoFactorCode: twoFactorCode }; + return lpost("/api/1/u/getdevicetoken", postData, function (err, dataStr) { + return callback(err, dataStr); + }); +} + + + +API.getRecoveryPacket = function (guid, callback) { + var postData = { guid: guid }; + return lpost("/api/1/getrecoverypacket", postData, function (err, dataStr) { + return callback(err, dataStr); + }); +} +module.exports = API; \ No newline at end of file diff --git a/src/ninki-engine.js b/src/ninki-engine.js index cd99383..6c9258f 100644 --- a/src/ninki-engine.js +++ b/src/ninki-engine.js @@ -1,10 +1,13 @@ +//ninki-engine + var Bitcoin = require('bitcoinjs-lib'); var openpgp = require('openpgp'); var CryptoJS = require('crypto-js'); var API = require('./ninki-api'); var BIP39 = require('./bip39'); var uuid = require('node-uuid'); - +var sjcl = require('sjcl'); +var sanitizer = require('sanitizer'); function Engine() { @@ -13,14 +16,91 @@ function Engine() { this.m_sharedid = ''; this.m_twoFactorOnLogin = false; this.m_nickname = ''; + this.m_profileImage = ''; + this.m_statusText = ''; this.m_guid = ''; this.m_oguid = ''; this.m_password = ''; this.m_settings = {}; this.m_validate = false; this.m_fingerprint = ''; + this.m_secret = ''; + this.m_migrateBeta12fa = false; + this.m_invoiceTax = 0.1; + this.m_privKey = ''; + this.m_pubKey = ''; + this.m_APIToken = ''; + this.m_appInitialised = false; + m_this = this; + + this.appHasLoaded = appHasLoaded; + function appHasLoaded() { + + // m_this.m_password = ''; + + } + + + function isChromeApp() { + + if (window.cordova) { + return false; + } + + var is_chrome = navigator.userAgent.toLowerCase().indexOf('chrome') > -1; + if (is_chrome) { + if (chrome) { + if (chrome.app) { + if (chrome.app.runtime) { + return true; + } + } + } + } + return false; + } + + + function isBrowser() { + + if (window.cordova) { + return false; + } + + return !isChromeApp(); + } + + function getCookie(cname, callback) { + + + if (isChromeApp()) { + + chrome.storage.local.get(cname, function (result) { + + if (result[cname]) { + result = result[cname]; + } else { + result = ""; + } + + return callback(result); + + }); + + } else { + + if (localStorage[cname]) { + return callback(localStorage[cname]); + } else { + return callback(''); + } + + } + + } + this.isRealGuid = isRealGuid; //Checks if GUID is valid. Only takes lowercase, non-bracketed GUIDs. function isRealGuid(potentialGuidAsString) { @@ -41,13 +121,13 @@ function Engine() { //assert(element, "Element not specified"); element.val(uuid.v4()); } - + this.getguid = getguid; function getguid() { //assert(element, "Element not specified"); return uuid.v4(); } - + this.encrypt = encrypt; function encrypt(valueToEncrypt, passphrase) { @@ -71,6 +151,27 @@ function Engine() { }; + this.encryptNp = encryptNp; + function encryptNp(valueToEncrypt, passphrase) { + + valueToEncrypt = CryptoJS.enc.Hex.parse(valueToEncrypt); + + var key = CryptoJS.enc.Hex.parse(passphrase); + + var iv = new Uint8Array(32); + var ivbytes = []; + window.crypto.getRandomValues(iv); + for (var i = 0; i < iv.length; ++i) { + ivbytes[i] = iv[i]; + } + + var ivwords = Bitcoin.convert.bytesToWordArray(ivbytes); + + var encrypted = CryptoJS.AES.encrypt(valueToEncrypt, key, { iv: ivwords, padding: CryptoJS.pad.NoPadding }); + + return encrypted; + }; + this.decrypt = decrypt; function decrypt(encryptedObj, passphrase, iv) { @@ -84,1151 +185,2166 @@ function Engine() { return decryptjson; }; + this.decryptNp = decryptNp; + function decryptNp(encryptedObj, passphrase, iv) { + + var key = CryptoJS.enc.Hex.parse(passphrase); + var iv = CryptoJS.enc.Hex.parse(iv); + + var decryptedObject = CryptoJS.AES.decrypt(encryptedObj, key, { iv: iv, padding: CryptoJS.pad.NoPadding }); + var decrypthex = decryptedObject.toString(CryptoJS.enc.Hex); + return decrypthex; + }; - //create wallet - //create a new wallet and save to the server - this.createWallet = createWallet; - function createWallet(guid, password, username, emailAddress, callback) { - m_this.m_oguid = guid; + var hmac = function (key) { + var hasher = new sjcl.misc.hmac(key, sjcl.hash.sha1); + this.encrypt = function () { + return hasher.encrypt.apply(hasher, arguments); + }; + }; - var bytes = []; - for (var i = 0; i < guid.length; ++i) { - bytes.push(guid.charCodeAt(i)); - } - m_this.m_guid = Bitcoin.Crypto.SHA256(Bitcoin.convert.bytesToWordArray(bytes)).toString(); + function pbkdf2(password, salt) { + var passwordSalt = sjcl.codec.utf8String.toBits(salt); + var derivedKey = sjcl.misc.pbkdf2(password, passwordSalt, 1000, 256, hmac); + var hexKey = sjcl.codec.hex.fromBits(derivedKey); + return hexKey; + } + this.setPass = setPass; + function setPass(pass, salt) { + m_this.m_password = pbkdf2(pass, salt); + } - //check if the username already exists - API.doesAccountExist(username.toLowerCase(), emailAddress.toLowerCase(), function (err, accExists) { + this.setStretchPass = setStretchPass; + function setStretchPass(pass) { + m_this.m_password = pass; + } - if (accExists.UserExists) { - return callback(true, "ErrUserExists"); - } - else if (accExists.EmailExists) { - return callback(true, "ErrEmailExists"); - } - else { + this.getHotHash = getHotHash; + function getHotHash(key, callback) { + var isWeb = isBrowser(); + //to do: validate key against stored public key + //needs to be done incase user changed their password on a different machine + if (isChromeApp()) { - //stretch the password with the local guid as a salt - m_this.m_password = CryptoJS.PBKDF2(password, m_this.m_oguid, { - keySize: 256 / 32, - iterations: 1000 - }).toString(); + chrome.storage.local.get("hk" + m_this.m_guid, function (result) { - password = ''; + if (result["hk" + m_this.m_guid]) { + var hk = result["hk" + m_this.m_guid]; + chrome.storage.local.get("hkiv" + m_this.m_guid, function (resultiv) { - //create a new wallet - makeNewWallet(username, emailAddress, function (err, walletInformation, userToken) { + if (resultiv["hkiv" + m_this.m_guid]) { + var hkiv = resultiv["hkiv" + m_this.m_guid]; - if (err) { + var hothash = ''; + var iserror = false; + try { + hothash = decryptNp(hk, m_this.m_password, hkiv); + } catch (error) { + iserror = true; + } + if (!iserror) { - return callback(true, "ErrCreateAccount"); + //validate against loaded hot public key + var validseed = true; + try { + var bipHot = Bitcoin.HDWallet.fromSeedHex(hothash); + if (m_this.m_walletinfo.hotPub != bipHot.toString()) { + validseed = false; + } + } catch (error) { - } else { + validseed = false; + } - m_this.m_sharedid = userToken; + if (validseed) { - //wallet creation is successful - //set variables - //add the usertoken to the wallet - walletInformation.wallet.googleAuthSecret = ""; - walletInformation.wallet.sharedid = userToken; + return callback(false, hothash); - //save the wallet to the server - API.post("/api/1/u/createaccount2", walletInformation.wallet, function (err, response) { + } else { - if (err) { + return callback(true, "invalid"); - return callback(err, "ErrSavePacket"); + } } else { - //pass back the wallet and info to the calling function - return callback(false, walletInformation); + + return callback(true, "missing"); } - }); - } + } else { - }); - } + return callback(true, "missing"); + } - }); - } + }); - this.openWalletAfterCreate = openWalletAfterCreate; - function openWalletAfterCreate(twoFactorCodeChk, callback) { + } else { + return callback(true, "missing"); + } - //check two factor code - if (twoFactorCodeChk != '') { - SetupTwoFactor(twoFactorCodeChk, function (err, wallet) { + }); - if (err) { + } else if (isWeb) { - return callback(err, wallet); + var hk = localStorage["hk" + m_this.m_guid]; + var hkiv = localStorage["hkiv" + m_this.m_guid]; - } else { + var hothash = ''; + var iserror = false; + try { + hothash = decryptNp(hk, m_this.m_password, hkiv); + } catch (error) { + iserror = true; + } + if (!iserror) { - m_this.m_twoFactorOnLogin = true; + //validate against loaded hot public key + var validseed = true; + try { + var bipHot = Bitcoin.HDWallet.fromSeedHex(hothash); + if (m_this.m_walletinfo.hotPub != bipHot.toString()) { + validseed = false; + } + } catch (error) { - getSettings(function (err, result) { + validseed = false; + } - callback(err, result); + if (validseed) { + + return callback(false, hothash); + + } else { + + return callback(true, "invalid"); - }); } - }); + } else { + + return callback(true, "missing"); + } } else { - getSettings(function (err, result) { - callback(err, result); + //not using the chrome app so must be mobile - }); + //use html5 localstorage - } + // localStorage - } + var hk = localStorage["ninki_h"]; - this.openWallet = openWallet; - function openWallet(guid, password, twoFactCode, callback) { - m_this.m_oguid = guid; + if (hk) { - var bytes = []; - for (var i = 0; i < guid.length; ++i) { - bytes.push(guid.charCodeAt(i)); - } + var hothash = ''; + var iserror = false; + try { - m_this.m_guid = Bitcoin.Crypto.SHA256(Bitcoin.convert.bytesToWordArray(bytes)).toString(); + var enc = JSON.parse(hk); + hothash = decryptNp(enc.ct, key, enc.iv); - API.getWalletFromServer(m_this.m_guid, twoFactCode, function (err, wallet) { + } catch (error) { + iserror = true; + } + if (!iserror) { + + //validate against loaded hot public key + var validseed = true; + try { + var bipHot = Bitcoin.HDWallet.fromSeedHex(hothash); + if (m_this.m_walletinfo.hotPub != bipHot.toString()) { + validseed = false; + } + } catch (error) { - if (err) { - return callback(err, wallet); - } + validseed = false; + } + if (validseed) { - m_this.m_password = CryptoJS.PBKDF2(password, guid, { - keySize: 256 / 32, - iterations: 1000 - }).toString(); + //get the two factor code also - try { - var walletInformation = decrypt(wallet.Payload, m_this.m_password, wallet.IV); - } catch (err) { - return callback(true, "Incorrect password"); - } + var tft = localStorage["ninki_rem"]; + var jtft = JSON.parse(tft); - m_this.m_twoFactorOnLogin = wallet.TwoFactorOnLogin; - m_this.m_walletinfo = walletInformation; - m_this.m_sharedid = walletInformation.userToken; + var fatoken = decryptNp(jtft.ct, key, jtft.iv); + return callback(false, hothash, fatoken); - getSettings(function (err, result) { + } else { - callback(err, result); + return callback(true, "invalid"); - }); + } - }); + } else { - } + return callback(true, "missing"); + } + } else { + return callback(true, "missing"); + } - function getSettings(callback) { - //nickname in packet? - getNickname(function (err, nickname) { - m_this.m_nickname = nickname; + } - getFingerPrint(function (err, fingerprint) { - //display the secret on a div + } - var bip39 = new BIP39(); // 'en' is the default language - var fingermnem = bip39.entropyToMnemonic(fingerprint); - m_this.m_fingerprint = fingermnem; + this.saveHotHash = saveHotHash; + function saveHotHash(hotHash, callback) { - getAccountSettings(function (err, response) { - if (err) { + //before we encrypt validate the hash matches the logged in public key + var validseed = true; + try { + var bipHot = Bitcoin.HDWallet.fromSeedHex(hotHash); + if (m_this.m_walletinfo.hotPub != bipHot.toString()) { + validseed = false; + } + } catch (error) { - } else { + validseed = false; + } - var settingsObject = JSON.parse(response); - m_this.settings = settingsObject; - m_this.m_validate = !settingsObject.EmailVerified; - } + if (validseed) { - return callback(err, "ok"); + var encHotHash = encryptNp(hotHash, m_this.m_password); + var objhk = {}; + if (isChromeApp()) { + objhk['hk' + m_this.m_guid] = encHotHash.toString(); + chrome.storage.local.set(objhk, function () { + //console.log("saved"); + var objhkiv = {}; + objhkiv['hkiv' + m_this.m_guid] = encHotHash.iv.toString(); + chrome.storage.local.set(objhkiv, function () { + //console.log("saved"); + callback(false, "ok"); + }); }); + } else { - }); - }); + localStorage.setItem('hk' + m_this.m_guid, encHotHash.toString()); + localStorage.setItem('hkiv' + m_this.m_guid, encHotHash.iv.toString()); - } + callback(false, "ok"); + } - //function makeNewWallet - //this function calls the server which generates the Ninki key pair to be used for the wallet - //the server returns the public key to the client so that it can be saved in the user's encrypted packet - function makeNewWallet(nickname, email, callback) { + } else { - //TODO add some more param checking - //rename this function - API.getMasterPublicKeyFromUpstreamServer(m_this.m_oguid, function (err, ninkiPubKey, userToken) { - if (err) { - return callback(err, "ErrCreateAccount"); - } else { - makeNewWalletPacket(nickname, email, ninkiPubKey, userToken, function (err, walletInformation) { - if (err) { - return callback(err, response); - } else { - return callback(err, walletInformation, userToken); - } - }); - } - }); - } + callback(true, "invalid"); + } - function makeNewWalletPacket(nickname, emailAddress, ninkiPubKey, userToken, callback) { + } - var network = "mainnet"; - //get some random data for the cold key - var rngcold = new Uint8Array(32); - window.crypto.getRandomValues(rngcold); + function validateHotKey(callback) { - var coldKeyBytes = []; - for (var i = 0; i < rngcold.length; ++i) { - coldKeyBytes[i] = rngcold[i]; - } + //load the hotkey + //if not there return error - //get some random data for the hot key - var rnghot = new Uint8Array(32); - window.crypto.getRandomValues(rnghot); + //validate the hotkey + //if not there return error - var hotKeyBytes = []; - for (var i = 0; i < rnghot.length; ++i) { - hotKeyBytes[i] = rnghot[i]; - } - var bip39 = new BIP39(); // 'en' is the default language - var hotmnem = bip39.entropyToMnemonic(rnghot); - var coldmnem = bip39.entropyToMnemonic(rngcold); + } - //var seedtest = bip39.mnemonicToSeed(hotmnem, ''); + // createprog + // textMessageCreate + // createprogstatus - //hash the random data to generate the seed for the cold key space - //Cold key space - var coldHash = Bitcoin.Crypto.SHA256(Bitcoin.convert.bytesToWordArray(coldKeyBytes)).toString(); - var coldWallet = Bitcoin.HDWallet.fromSeedHex(coldHash); - //get the keys as strings - var coldPriv = coldWallet.toString(" "); - var coldPub = coldWallet.toString(); + //create wallet + //create a new wallet and save to the server + this.createWallet = createWallet; + function createWallet(guid, password, username, emailAddress, callback) { - //hash the random data to generate the seed for the hot key space - //Hot key space - var hotHash = Bitcoin.Crypto.SHA256(Bitcoin.convert.bytesToWordArray(hotKeyBytes)).toString(); + m_this.m_oguid = guid; - var hotWallet = Bitcoin.HDWallet.fromSeedHex(hotHash); - //get the keys as strings - var hotPriv = hotWallet.toString(" "); - var hotPub = hotWallet.toString(); + var bytes = []; + for (var i = 0; i < guid.length; ++i) { + bytes.push(guid.charCodeAt(i)); + } + m_this.m_guid = Bitcoin.Crypto.SHA256(Bitcoin.convert.bytesToWordArray(bytes)).toString(); - //generate a pgp keypair - //this key pair will be used to allow the user's to communicate with their contacts - //the private key will be aes256 encrypted so use the userToken as the passphrase as the library - //doesn't support blank passphrases yet (and there is no way to change them) - var options = { numBits: 1024, userId: nickname, passphrase: userToken }; - var keypair = openpgp.generateKeyPair(options); - var privKeys = openpgp.key.readArmored(keypair.privateKeyArmored); - var publicKeys = openpgp.key.readArmored(keypair.publicKeyArmored); + //check if the username already exists + API.doesAccountExist(username.toLowerCase(), emailAddress.toLowerCase(), function (err, accExists) { + if (accExists.UserExists) { - //save the wallet keys and user token in an encrypted packet - //AES256 using PBKDF2 on the password and a unique salt + return callback(true, "ErrUserExists"); - var wal = { - coldPub: coldPub, - hotPub: hotPub, - ninkiPubKey: ninkiPubKey, - hotPriv: hotPriv, - hotHash: hotHash, - userToken: userToken - }; + } + else if (accExists.EmailExists) { - m_this.m_walletinfo = wal; + return callback(true, "ErrEmailExists"); - var encryptedPayload = encrypt(wal, m_this.m_password); + } + else { - //save the PGP keys in an encrypted packet - //AES256 using PBKDF2 on the password and a unique salt + $('#textMessageCreate').html('stretching password...'); - var encryptedUserPayload = encrypt({ - RSAPriv: keypair.privateKeyArmored, - RSAPub: keypair.publicKeyArmored - } - , m_this.m_password, m_this.m_oguid); - - //encrypt a shared secret - //this allows Ninki to validate that the user - //knows their password without having to hold any - //information about their password (for future use) - var secret = Bitcoin.Crypto.SHA256(userToken).toString(); - var encryptedSecret = encrypt(secret, m_this.m_password); - - - //create a packet to post to the server - //note: - // hot private key is encrypted in the payload - // PGP private key is encrypted in the payload - // all 3 wallet public keys are encrypted in the payload - // hot and cold wallet public keys are passed to the server - // public PGP key is passed to the server - - //TODO: move ninkiPhrase to server side - - var wallet = { - guid: m_this.m_oguid, - payload: encryptedPayload.toString(), - userPublicKey: keypair.publicKeyArmored, - userPayload: encryptedUserPayload.toString(), - hotPublicKey: hotPub, - coldPublicKey: coldPub, - nickName: nickname, - emailAddress: emailAddress, - secret: encryptedSecret.toString(), - ninkiPhrase: ninkiPubKey, - IVA: encryptedPayload.iv.toString(), - IVU: encryptedUserPayload.iv.toString(), - IVR: encryptedSecret.iv.toString() - }; + setTimeout(function () { - //the cold private key is discarded and is only displayed to the user - //as a mnemomic representation so that the user can write it down - //if this phrase is lost by the user it is unrecoverable + //stretch the password with the local guid as a salt + m_this.m_password = pbkdf2(password, m_this.m_oguid); + password = ''; - var walletInformation = { - wallet: wallet, - coldWalletPhrase: bip39.entropyToMnemonic(coldHash), - hotWalletPhrase: bip39.entropyToMnemonic(hotHash), - sharedid: userToken - } + //create a new wallet + makeNewWallet(username, emailAddress, function (err, walletInformation, userToken) { - //console.log(coldHash, hotHash); + if (err) { - return callback(null, walletInformation); - } + return callback(true, "ErrCreateAccount"); - function deriveChild(path, hdwallet) { + } else { - var e = path.split('/'); - var ret = hdwallet; - // Special cases: - if (path == 'm' || path == 'M' || path == 'm\'' || path == 'M\'') return this; + m_this.m_sharedid = userToken; - for (var i in e) { - var c = e[i]; + //wallet creation is successful + //set variables + //add the usertoken to the wallet + walletInformation.wallet.googleAuthSecret = ""; + walletInformation.wallet.sharedid = userToken; - if (i == 0) { - if (c != 'm') throw new Error("invalid path"); - continue; - } + // + var recpacket = encryptNp(m_this.m_password, m_this.m_walletinfo.hckey); - var use_private = (c.length > 1) && (c[c.length - 1] == '\''); - var child_index = parseInt(use_private ? c.slice(0, c.length - 1) : c) & 0x7fffffff; + walletInformation.wallet.recPacket = recpacket.toString(); + walletInformation.wallet.recPacketIV = recpacket.iv.toString(); - if (use_private) - child_index += 0x80000000; + //save the wallet to the server + $('#textMessageCreate').html('saving data...'); - ret = ret.derive(child_index); - } + setTimeout(function () { - return ret; - } + API.post("/api/1/u/createaccount2", walletInformation.wallet, function (err, response) { + if (err) { + return callback(err, "ErrSavePacket"); - //function aMultiSigHashForSigning - //TODO: rename - function aMultiSigHashForSigning3(publickey1, publickey2, publickey3, index, outputsToSpend, outputsToSend, addressToSend) { + } else { + //pass back the wallet and info to the calling function + return callback(false, walletInformation); + } + }); + }, 50); + } - //this function will create the temporary transaction - //with a single input used to generate the signature - //for the single input + }); - //instantiate a new transaction - var tx = new Bitcoin.Transaction(); + }, 50); + } - var ins = []; - var outs = []; + }); + } - //generate the script to sign off on - //using the users hotkey,coldkey and the ninki public key - //2 of... - var script = [0x52]; - //hotkey - script.push(33); - script = script.concat(publickey1); - //cold key - script.push(33); - script = script.concat(publickey2); - //ninki key - script.push(33); - script = script.concat(publickey3); - //..3 - script.push(0x53); - //..multisig - script.push(0xae); + //function makeNewWallet + //this function calls the server which generates the Ninki key pair to be used for the wallet + //the server returns the public key to the client so that it can be saved in the user's encrypted packet + function makeNewWallet(nickname, email, callback) { - //generate the same number of inputs as on the transaction to broadcast - //but replace the other inputs with a zero byte! (thanks satoshi-san) - for (var i = 0; i < outputsToSpend.length; i++) { - var p = outputsToSpend[i].transactionId + ':' + outputsToSpend[i].outputIndex.toString(); - tx.addInput(p); - if (i == index) { - tx.ins[i].script = new Bitcoin.Script(script); - } else { - tx.ins[i].script = new Bitcoin.Script([0]); - } - } - //mirror the outpurs in the transaction to broadcast - var test = ''; - for (var i = 0; i < outputsToSend.length; i++) { - var addr = new Bitcoin.Address(addressToSend[i]); - tx.addOutput(addressToSend[i], outputsToSend[i]); - } + //TODO add some more param checking + //rename this function + setTimeout(function () { + $('#textMessageCreate').html('creating account...'); - //hash the transaction-- this has will be used as an input to the signature function - var txHash = tx.hashTransactionForSignature(tx.ins[index].script, index, 1); + API.getMasterPublicKeyFromUpstreamServer(m_this.m_oguid, function (err, ninkiPubKey, userToken, secret) { + if (err) { + return callback(err, "ErrCreateAccount"); + } else { + makeNewWalletPacket(nickname, email, ninkiPubKey, userToken, secret, function (err, walletInformation) { + if (err) { + return callback(err, response); + } else { + return callback(err, walletInformation, userToken); + } + }); + } + }); + }, 50); + } - return txHash; - } + function makeNewWalletPacket(nickname, emailAddress, ninkiPubKey, userToken, secret, callback) { - //function aGetTransaction - //TODO: rename - //generateas a transaction from a set of keys, outputs, signatures and addresses to send to - function aGetTransaction(publickeys, outputsToSpend, outputsToSend, addressToSend, sigs) { + var network = "mainnet"; - //create a new transaction - var tx = new Bitcoin.Transaction(); - var ins = []; - var outs = []; + $('#textMessageCreate').html('getting entropy...'); - //generate the scripts to spend the outputs - for (var i = 0; i < outputsToSpend.length; i++) { + setTimeout(function () { + //get some random data for the cold key + var rngcold = new Uint8Array(32); + window.crypto.getRandomValues(rngcold); - var len = sigs[i].length; - var script = []; + var coldKeyBytes = []; + for (var i = 0; i < rngcold.length; ++i) { + coldKeyBytes[i] = rngcold[i]; + } - //append the signature - script = script.concat(sigs[i]); + //get some random data for the hot key + var rnghot = new Uint8Array(32); + window.crypto.getRandomValues(rnghot); - //prepend the length of the signature - script.unshift(len); - script.unshift(0x00); + var hotKeyBytes = []; + for (var i = 0; i < rnghot.length; ++i) { + hotKeyBytes[i] = rnghot[i]; + } - //push the script used to validate the spend - script.push(0x4c); - script.push(105); - script.push(0x52); - script.push(33); - script = script.concat(publickeys[i][0]); - script.push(33); - script = script.concat(publickeys[i][1]); - script.push(33); - script = script.concat(publickeys[i][2]); - script.push(0x53); - script.push(0xae); + var bip39 = new BIP39(); // 'en' is the default language + var hotmnem = bip39.entropyToMnemonic(rnghot); + var coldmnem = bip39.entropyToMnemonic(rngcold); - //add the input to the transaction referencing the output to spend - var p = outputsToSpend[i].transactionId + ':' + outputsToSpend[i].outputIndex.toString(); - tx.addInput(p); + //var seedtest = bip39.mnemonicToSeed(hotmnem, ''); - //set the script on the input - tx.ins[i].script = new Bitcoin.Script(script); + $('#textMessageCreate').html('creating cold keys...'); - } + setTimeout(function () { - //add the outputs to the transaction - for (var i = 0; i < outputsToSend.length; i++) { - tx.addOutput(addressToSend[i], outputsToSend[i]); - } + //hash the random data to generate the seed for the cold key space + //Cold key space + var coldHash = Bitcoin.Crypto.SHA256(Bitcoin.convert.bytesToWordArray(coldKeyBytes)).toString(); - var txHash = Array.apply([], tx.serialize()); + var coldWallet = Bitcoin.HDWallet.fromSeedHex(coldHash); + //get the keys as strings + var coldPriv = coldWallet.toString(" "); + var coldPub = coldWallet.toString(); - return txHash; - } + $('#textMessageCreate').html('creating hot keys...'); - function aGetTransactionData(params, callback) { + setTimeout(function () { + //hash the random data to generate the seed for the hot key space + //Hot key space + var hotHash = Bitcoin.Crypto.SHA256(Bitcoin.convert.bytesToWordArray(hotKeyBytes)).toString(); - var derivedPublicKeys = []; - var derivedPrivateKeys = []; + var hotWallet = Bitcoin.HDWallet.fromSeedHex(hotHash); + //get the keys as strings + var hotPriv = hotWallet.toString(" "); + var hotPub = hotWallet.toString(); - var signatures = []; - var hashesForSigning = []; - for (var i = 0; i < params.outputsToSpend.length; i++) { - var path = params.paths[i]; - //derive the hashes for signing off on each input - var hashForSigning = aMultiSigHashForSigning3(params.publicKeys[i][0], params.publicKeys[i][1], params.publicKeys[i][2], i, params.outputsToSpend, params.amountsToSend, params.addressToSend); - //add to collection so they can be provided to the server later - //this saves the same process having to be done on the server side - hashesForSigning.push(Bitcoin.convert.bytesToHex(hashForSigning)); + //create a key based on a hash of the hot + cold key + //this is used to encrypt the pbkdf password and so enables + //password reset if the user has access to the hot and cold key phrases - //get the user's hot private key - var key = params.userHotPrivKeys[i]; + var hckey = hotHash + coldHash; + var hcbkey = Bitcoin.convert.hexToBytes(hckey); + var hchkey = Bitcoin.Crypto.SHA256(Bitcoin.convert.bytesToWordArray(hcbkey)).toString(); - //sign the input - var sig = key.sign(hashForSigning).concat([1]); + $('#textMessageCreate').html('creating pgp keys...'); - //add the signature - signatures.push(sig); - } - //get the transaction and return along with the hashes used to sign - var txn = aGetTransaction(params.publicKeys, params.outputsToSpend, params.amountsToSend, params.addressToSend, signatures); + setTimeout(function () { - //generate the signatures - //TO DO: check call back? - return callback("", hashesForSigning, Bitcoin.convert.bytesToHex(txn)); - } + //generate a pgp keypair + //this key pair will be used to allow the user's to communicate with their contacts + //the private key will be aes256 encrypted so use the userToken as the passphrase as the library + //doesn't support blank passphrases yet (and there is no way to change them) + var options = { numBits: 1024, userId: nickname, passphrase: userToken }; + var keypair = openpgp.generateKeyPair(options); - this.isAddressValid = isAddressValid; - function isAddressValid(address) { - var addrValid = true; - try { - var addressCheck = new Bitcoin.Address(address); - addressCheck.toString(); - } catch (err) { - addrValid = false; - } - return addrValid; - } + var privKeys = openpgp.key.readArmored(keypair.privateKeyArmored); + var publicKeys = openpgp.key.readArmored(keypair.publicKeyArmored); - this.sendTransaction = sendTransaction; - function sendTransaction(sendType, friendUserName, addressTo, amount, callback) { + setTimeout(function () { - var minersFee = 10000; - amount = Math.round(amount); + $('#textMessageCreate').html('encrypting data...'); + //save the wallet keys and user token in an encrypted packet + //AES256 using PBKDF2 on the password and a unique salt + var wal = { + coldPub: coldPub, + hotPub: hotPub, + ninkiPubKey: ninkiPubKey, + hotPriv: '', + hotHash: '', + userToken: userToken, + hckey: hchkey + }; - //setup handles to report back progress - var progressel = ''; - var messel = ''; + m_this.m_walletinfo = wal; + + var encryptedPayload = encrypt(wal, m_this.m_password); + + //save the PGP keys in an encrypted packet + //AES256 using PBKDF2 on the password and a unique salt + + var encryptedUserPayload = encrypt({ + RSAPriv: keypair.privateKeyArmored, + RSAPub: keypair.publicKeyArmored + }, m_this.m_password, m_this.m_oguid); + + //encrypt a shared secret + //this allows Ninki to validate that the user + //knows their password without having to hold any + //info about the password + + var encryptedSecret = encryptNp(secret, m_this.m_password); + + m_this.m_secret = secret; + + //create a packet to post to the server + //note: + // hot private key is encrypted in the payload + // PGP private key is encrypted in the payload + // all 3 wallet public keys are encrypted in the payload + // hot and cold wallet public keys are passed to the server + // public PGP key is passed to the server + + //TODO: move ninkiPhrase to server side + + var wallet = { + guid: m_this.m_oguid, + payload: encryptedPayload.toString(), + userPublicKey: keypair.publicKeyArmored, + userPayload: encryptedUserPayload.toString(), + hotPublicKey: hotPub, + coldPublicKey: coldPub, + nickName: nickname, + emailAddress: emailAddress, + secret: encryptedSecret.toString(), + ninkiPhrase: ninkiPubKey, + IVA: encryptedPayload.iv.toString(), + IVU: encryptedUserPayload.iv.toString(), + IVR: encryptedSecret.iv.toString() + }; - if (sendType == 'friend') { - messel = '#textMessageSend'; - progressel = '#sendfriendprogstatus'; - } + //the cold private key is discarded and is only displayed to the user + //as a mnemomic representation so that the user can write it down + //if this phrase is lost by the user it is unrecoverable - if (sendType == 'standard') { - messel = '#textMessageSendStd'; - progressel = '#sendstdprogstatus'; - } - if (sendType == 'invoice') { - messel = '#textMessageSendInv'; - progressel = '#sendinvprogstatus'; - } + //the hot private key is encrypted and saved locally + //encrypted with the user's password - //get the miners fee setting from account settings - //TODO: this can be pre-loaded - API.post("/api/1/u/getaccountsettings", { - guid: m_this.m_guid, - sharedid: m_this.m_sharedid - }, function (err, response) { - if (err) { + saveHotHash(hotHash, function (err, res) { - } else { + var walletInformation = { + wallet: wallet, + coldWalletPhrase: bip39.entropyToMnemonic(coldHash), + hotWalletPhrase: bip39.entropyToMnemonic(hotHash), + sharedid: userToken, + hckey: hchkey + } - var settingsObject = JSON.parse(response); - minersFee = settingsObject.MinersFee * 1.0; - } + //console.log(coldHash, hotHash); - }); + return callback(err, walletInformation); + + }); + }, 50); - //initialise the hot private key space - var bipHot = Bitcoin.HDWallet.fromBase58(m_this.m_walletinfo.hotPriv); + }, 50); - //initialise the 3 public keys - var bipHotPub = Bitcoin.HDWallet.fromBase58(m_this.m_walletinfo.hotPub); - var bipCold = Bitcoin.HDWallet.fromBase58(m_this.m_walletinfo.coldPub); - var bipNinki = Bitcoin.HDWallet.fromBase58(m_this.m_walletinfo.ninkiPubKey); + }, 50); + }, 50); - var pdata = { guid: m_this.m_guid, sharedid: m_this.m_sharedid }; + }, 50); + } - $(messel).html('Getting unspent outputs...'); + this.openWalletAfterCreate = openWalletAfterCreate; + function openWalletAfterCreate(twoFactorCodeChk, callback) { + //check two factor code + if (twoFactorCodeChk != '') { + SetupTwoFactor(twoFactorCodeChk, function (err, wallet) { - API.post("/api/1/u/getunspentoutputs", pdata, function (err, outputs) { + if (err) { - var outputs = JSON.parse(outputs); - var outputsToSpend = []; - var amountsToSend = []; - var addressToSend = []; - var userHotPrivKeys = []; + return callback(err, wallet); - var nodeLevels = []; - var publicKeys = []; - var packet = { addressToSend: addressToSend, amountsToSend: amountsToSend, outputsToSpend: outputsToSpend, userHotPrivKeys: userHotPrivKeys, guid: m_this.m_guid, paths: nodeLevels, publicKeys: publicKeys }; + } else { - //get outputs to spend, calculate change amount minus miners fee + m_this.m_twoFactorOnLogin = true; - $(progressel).width('20%'); + getPGPKeys(function (err, result) { - //iterate the unspent outputs and select the first n that equal the amount to spend - //TO DO: do this before doing any key derivation - var amountSoFar = 0; - for (var i = 0; i < outputs.length; i++) { + if (!err) { + getSettings(function (err, result) { - var pitem = outputs[i]; - var pout = { transactionId: pitem.TransactionId, outputIndex: pitem.OutputIndex, amount: pitem.Amount, address: pitem.Address } + callback(err, result); - nodeLevels.push(pitem.NodeLevel); + }); + } else { + callback(err, result); + } + }); + } - outputsToSpend.push(pout); + }); - //derive the private key to sue for signing - userHotPrivKeys.push(deriveChild(pitem.NodeLevel, bipHot).priv); + } else { - //derive the public keys to use for script generation - //this could be cached on the server as no privacy or hand-off issue that I can see - var dbipHotPub = deriveChild(pitem.NodeLevel, bipHotPub).pub.toBytes(); - var dbipColdPub = deriveChild(pitem.NodeLevel, bipCold).pub.toBytes(); - var dbipNinkiPub = deriveChild(pitem.NodeLevel, bipNinki).pub.toBytes(); + getPGPKeys(function (err, result) { - publicKeys.push([dbipHotPub, dbipColdPub, dbipNinkiPub]); + if (!err) { + getSettings(function (err, result) { - //add the amount - amountSoFar += pitem.Amount; + callback(err, result); - if ((amountSoFar - amount) >= minersFee) { - break; + }); + } else { + callback(err, result); } + }); + + } + + } - } - amountsToSend.push(amount); + this.openWallet2fa = openWallet2fa; + function openWallet2fa(twoFactCode, rememberTwoFactor, callback) { - //now create the change - //again move this before the key derivation - var changeAmount = amountSoFar - (amount + minersFee); + API.getWalletFromServer(m_this.m_guid, m_this.m_secret, twoFactCode, rememberTwoFactor, function (err, wallet) { - if (changeAmount < 0) { - //this message needs to be handled carefully - //as the wallet will most likely have pending confirmations - //rather than insufficent funds - return callback(true, "ErrInsufficientFunds"); + if (err) { + return callback(err, wallet); } - if (changeAmount > 0) { - amountsToSend.push(changeAmount); + try { + var walletInformation = decrypt(wallet.Payload, m_this.m_password, wallet.IV); + } catch (err) { + return callback(true, "Incorrect password"); } - //create a new address for my change to be sent back to me - //if we are sending money to a contact or paying an invoice - //we need to derive addresses on their behalf + //if any account still has a hotkey stored in their encrypted packet + //then remove it and resave the packet - if (sendType == 'friend' || sendType == 'invoice') { - var params = { guid: m_this.m_guid, sharedid: m_this.m_sharedid, username: friendUserName, amount: amount }; + if (walletInformation.hotHash != '') { - //generate the address for the contact - //this must be done on the client - $(messel).html('Creating address...'); - createAddressForFriend(friendUserName, function (err, address) { + var hotHash = walletInformation.hotHash; - if (!err) { + walletInformation.hotPriv = ''; + walletInformation.hotHash = ''; + m_this.m_walletinfo = walletInformation; + saveHotHash(hotHash, function (err, result) { - $(progressel).width('40%'); - addressToSend.push(address); + if (!err) { - //create the change address, this must be done on the client - $(messel).html('Creating change address...'); + //double check it has been saved correctly + getHotHash("", function (err, result) { + if (!err) { + var packet = encrypt(walletInformation, m_this.m_password); + //now save the packet back to the server - createAddress('m/0/1', changeAmount, function (err, changeaddress) { + var postData = { twoFactorCode: twoFactCode, guid: m_this.m_guid, sharedid: walletInformation.userToken, accountPacket: packet.toString(), IVA: packet.iv.toString() }; + API.post("/api/1/u/migratepacket", postData, function (err, dataStr) { - if (!err) { - $(progressel).width('60%'); - if (changeAmount > 0) { - addressToSend.push(changeaddress); - } - //now get the transaction data - $(messel).html('Creating transaction...'); - aGetTransactionData(packet, function (err, hashesForSigning, rawTransaction) { - $(progressel).width('80%'); - var jsonSend = { guid: m_this.m_guid, hashesForSigning: hashesForSigning, rawTransaction: rawTransaction, pathsToSignWith: nodeLevels } - var jsonp1 = { jsonPacket: JSON.stringify(jsonSend), sharedid: m_this.m_sharedid, userName: friendUserName }; - $(messel).html('Counter-signing transaction...'); + }); - //send the transaction to the service for countersigning and broadcast to the network - API.post("/api/1/u/sendtransaction", jsonp1, function (err, result) { + } - if (!err) { - $(progressel).width('100%'); - $(messel).html('Transaction broadcast...'); - //error handling? + }); - //we have a transaction id so lets make a note of the transaction in the database - var params = { guid: m_this.m_guid, sharedid: m_this.m_sharedid, username: friendUserName, transactionid: result, address: address, amount: amount }; - API.post("/api/1/u/createtransactionrecord", params, function (err, result) { - return callback(err, params.transactionid); - }); - } else { + } - //handle error messages returned from the server - if (result == 'ErrSingleTransactionLimit') { - $(progressel).width('100%'); - $(messel).html('Transaction Failed: Single limit exceeded'); - return callback(err, result); - } - if (result == 'ErrDailyTransactionLimit') { - $(progressel).width('100%'); - $(messel).html('Transaction Failed: Daily limit exceeded'); - return callback(err, result); - } + }); - if (result == 'ErrTransactionsPerDayLimit') { - $(progressel).width('100%'); - $(messel).html('Transaction Failed: Daily number limit exceeded'); - return callback(err, result); - } + } - if (result == 'ErrTransactionsPerHourLimit') { - $(progressel).width('100%'); - $(messel).html('Transaction Failed: Hourly number limit exceeded'); - return callback(err, result); - } - if (result == 'ErrInvalidRequest') { - $(progressel).width('100%'); - $(messel).html('Transaction Failed: Invalid request'); - return callback(err, result); - } + if (m_this.m_migrateBeta12fa) { - if (result == 'ErrBroadcastFailed') { - $(progressel).width('100%'); - $(messel).html('Transaction Failed: Not accepted'); - return callback(err, result); - } + //1. for migrations from beta1 we need to save a secret + var encryptedSecret = encryptNp(m_this.m_secret, m_this.m_password); - } - }); - }); - } else { - //create address error - if (changeaddress == 'ErrInvalidRequest') { - $(progressel).width('100%'); - $(messel).html('Transaction Failed: Invalid request'); - return callback(err, changeaddress); - } - } - }); - } else { + //we need to base this on account migration status - if (address == 'ErrInvalidRequest') { - $(progressel).width('100%'); - $(messel).html('Transaction Failed: Invalid request'); - return callback(err, address); - } - } - }); - } else { + //add twoFactCode here + API.updateSecretPacket(m_this.m_guid, walletInformation.userToken, encryptedSecret.toString(), encryptedSecret.iv.toString(), function (err, res) { - $(messel).html('Creating address...'); - addressToSend.push(addressTo); + if (err) { + return callback(err, res); + } else { + + + walletInformation.hotPriv = ''; + walletInformation.hotHash = ''; + walletInformation.hchkey = ''; + + m_this.m_twoFactorOnLogin = wallet.TwoFactorOnLogin; + m_this.m_walletinfo = walletInformation; + m_this.m_sharedid = walletInformation.userToken; + + + //if there is a cookie token then encrypt it + + if (wallet.CookieToken) { + var enc = encryptNp(wallet.CookieToken, m_this.m_password); + var ctok = {}; + ctok.ct = enc.toString(); + ctok.iv = enc.iv.toString(); + wallet.CookieToken = JSON.stringify(ctok); + } + + //save secret + + getPGPKeys(function (err, result) { + + if (!err) { + getSettings(function (err, result) { + if (!err) { + callback(err, wallet); + } else { + callback(err, result); + } + + }); + } else { + callback(err, result); + } + }); + + } + + }); + + } else { + + + //2. this is the standard login code execution + //eventually only this block will be required + + walletInformation.hotPriv = ''; + walletInformation.hotHash = ''; + walletInformation.hchkey = ''; + + m_this.m_twoFactorOnLogin = wallet.TwoFactorOnLogin; + m_this.m_walletinfo = walletInformation; + m_this.m_sharedid = walletInformation.userToken; + + //if there is a cookie token then encrypt it + + if (wallet.CookieToken) { + var enc = encryptNp(wallet.CookieToken, m_this.m_password); + var ctok = {}; + ctok.ct = enc.toString(); + ctok.iv = enc.iv.toString(); + wallet.CookieToken = JSON.stringify(ctok); + } + + //save secret + + getPGPKeys(function (err, result) { + + if (!err) { + getSettings(function (err, result) { + if (!err) { + callback(err, wallet); + } else { + callback(err, result); + } + + }); + } else { + callback(err, result); + } + }); + + } + + }); + + + + } + + + this.openWallet = openWallet; + function openWallet(guid, twoFactCode, callback) { + + m_this.m_oguid = guid; + + var bytes = []; + for (var i = 0; i < guid.length; ++i) { + bytes.push(guid.charCodeAt(i)); + } + + m_this.m_guid = Bitcoin.Crypto.SHA256(Bitcoin.convert.bytesToWordArray(bytes)).toString(); + //m_this.m_password = pbkdf2(password, guid); + + + //first get the encrypted secret + //decrypt with password and pass secret to get the wallet back + + //if the user is from beta test 1 + //generate a secret on the server and return + //encrypt the secret on the client + //save directly back + + + API.post("/api/1/getrecoverypacket", { + guid: m_this.m_guid + }, function (err, response) { + + if (err) { + + callback(err, response); + + } else { + + //decrypt packet + + var jpacket = JSON.parse(response); + + if (!jpacket.Beta1) { + + + //1. Account has been migrated to beta2 + //we have a secret to validate against + + //2. validate againts secret and check to see if 2fa is enabled + //if it isn't we can get the packet then force the user to enable it + + var secret = decryptNp(jpacket.packet, m_this.m_password, jpacket.IV); + + m_this.m_secret = secret; + + + API.validateSecret(m_this.m_guid, m_this.m_secret, function (err, secvalid) { + + if (err) { + return callback(err, secvalid); + } + + if (secvalid.Locked == 0) { + + //either is an old acocunt with no 2fa or has a client token + //so we can get back the packet without 2fa + if (!secvalid.TwoFactorOnLogin || twoFactCode.length > 6) { + + API.getWalletFromServer(m_this.m_guid, m_this.m_secret, twoFactCode, false, function (err, wallet) { + + if (err) { + if (wallet == "TokenExpired") { + return callback(false, secvalid); + } else { + return callback(err, wallet); + } + } + + //if token has expired - return expiry message + // + + try { + var walletInformation = decrypt(wallet.Payload, m_this.m_password, wallet.IV); + } catch (err) { + return callback(true, "Incorrect password"); + } + + + //if the packet still stores the hot key + //it is an old beta account + //save the key to local storage + //blank the keys from the packet and resave + + //alert and remind the user to write down their hotkeys + + walletInformation.hotPriv = ''; + walletInformation.hotHash = ''; + walletInformation.hchkey = ''; + + + m_this.m_twoFactorOnLogin = wallet.TwoFactorOnLogin; + m_this.m_walletinfo = walletInformation; + m_this.m_sharedid = walletInformation.userToken; + + getPGPKeys(function (err, result) { + + if (!err) { + getSettings(function (err, result) { + + callback(err, result); + + }); + } else { + callback(err, result); + } + }); + + + }); + + } else { + + //is a migrated account with 2fa enabled + //return to get the user to enter the 2fa code + return callback(err, secvalid); + + } + } else { + + return callback(err, "Account is locked"); + + } + + }); + + } else { + + //migrate the Beta1 wallet + + //save the encypted packet + + + //3. this is a beta1 wallet with no secret + //4. if the user does not have 2fa setup + //get the packet using the old style, then set them up with a secret + if (!jpacket.Beta12fa) { + + //they don;t have 2fa enabled either so once their account is migrated + //we force them to setup 2fa + + //log in as normal + API.getWalletFromServer(m_this.m_guid, m_this.m_secret, twoFactCode, false, function (err, wallet) { + + if (err) { + return callback(err, wallet); + } + + try { + var walletInformation = decrypt(wallet.Payload, m_this.m_password, wallet.IV); + } catch (err) { + return callback(true, "Incorrect password"); + } + + + //now we now we have the correct password + //encrypt the secret and save back the packet + + var encryptedSecret = encryptNp(jpacket.Secret, m_this.m_password); + + //save secret + + m_this.m_secret = jpacket.Secret; + + //here we don't have 2fa yet... + + API.updateSecretPacket(m_this.m_guid, walletInformation.userToken, encryptedSecret.toString(), encryptedSecret.iv.toString(), function (err, res) { + + walletInformation.hotPriv = ''; + walletInformation.hotHash = ''; + walletInformation.hchkey = ''; + + m_this.m_twoFactorOnLogin = wallet.TwoFactorOnLogin; + m_this.m_walletinfo = walletInformation; + m_this.m_sharedid = walletInformation.userToken; + + getPGPKeys(function (err, result) { + + if (!err) { + getSettings(function (err, result) { + + callback(err, result); + + }); + } else { + callback(err, result); + } + }); + + }); + + }); + + + } else { + + + //the account is beta1 but has 2fa setup + //so we don't migrate the account yet + //instead we return so the user can enter their 2fa + + m_this.m_secret = jpacket.Secret; + m_this.m_migrateBeta12fa = true; + m_this.m_twoFactorOnLogin = true; + jpacket.TwoFactorOnLogin = true; + + callback(err, jpacket); + } + + + } + } + + }); + + } + + + function getPGPKeys(callback) { + + //get the pgp key pair + API.getUserPacket(m_this.m_guid, m_this.m_sharedid, function (err, encpacket) { + + //get the RSA private key from the encrypted payload + var rsaKeyPair = decrypt(encpacket.Payload, m_this.m_password, encpacket.IV); + + var publicKeys = openpgp.key.readArmored(rsaKeyPair.RSAPub); + var privKeys = openpgp.key.readArmored(rsaKeyPair.RSAPriv); + + m_this.m_privKey = privKeys.keys[0]; + m_this.m_pubKey = publicKeys.keys[0]; + + if (m_this.m_privKey.decrypt(m_this.m_sharedid)) { + callback(err, 'ok'); + } else { + callback(true, 'failed'); + } + + }); + + } + + function getSettings(callback) { + //nickname in packet? + getNickname(function (err, nickname) { + + m_this.m_nickname = nickname; + + + getUserProfile(function (err, result) { + + var userProfile = JSON.parse(result); + + m_this.m_profileImage = userProfile.ProfileImage; + m_this.m_statusText = userProfile.Status; + m_this.m_invoiceTax = userProfile.Tax; + + getFingerPrint(function (err, fingerprint) { + //display the secret on a div + + var bip39 = new BIP39(); // 'en' is the default language + var fingermnem = bip39.entropyToMnemonic(fingerprint); + m_this.m_fingerprint = fingermnem; + + getAccountSettings(function (err, response) { + if (err) { + + + } else { + + var settingsObject = JSON.parse(response); + m_this.m_settings = settingsObject; + m_this.m_validate = !settingsObject.EmailVerified; + } + + return callback(err, "ok"); + + }); + + }); + + }); + + }); + + } + + + function deriveChild(path, hdwallet) { + + var e = path.split('/'); + var ret = hdwallet; + // Special cases: + if (path == 'm' || path == 'M' || path == 'm\'' || path == 'M\'') return this; + + for (var i in e) { + var c = e[i]; + + if (i == 0) { + if (c != 'm') throw new Error("invalid path"); + continue; + } + + var use_private = (c.length > 1) && (c[c.length - 1] == '\''); + var child_index = parseInt(use_private ? c.slice(0, c.length - 1) : c) & 0x7fffffff; + + if (use_private) + child_index += 0x80000000; + + ret = ret.derive(child_index); + } + + return ret; + } + + + + + //function aMultiSigHashForSigning + //TODO: rename + function aMultiSigHashForSigning3(publickey1, publickey2, publickey3, index, outputsToSpend, outputsToSend, addressToSend) { + + + //this function will create the temporary transaction + //with a single input used to generate the signature + //for the single input + + //instantiate a new transaction + var tx = new Bitcoin.Transaction(); + + var ins = []; + var outs = []; + + //generate the script to sign off on + //using the users hotkey,coldkey and the ninki public key + //2 of... + var script = [0x52]; + //hotkey + script.push(33); + script = script.concat(publickey1); + //cold key + script.push(33); + script = script.concat(publickey2); + //ninki key + script.push(33); + script = script.concat(publickey3); + //..3 + script.push(0x53); + //..multisig + + script.push(0xae); + + //generate the same number of inputs as on the transaction to broadcast + //but replace the other inputs with a zero byte! (thanks satoshi-san) + for (var i = 0; i < outputsToSpend.length; i++) { + var p = outputsToSpend[i].transactionId + ':' + outputsToSpend[i].outputIndex.toString(); + tx.addInput(p); + if (i == index) { + tx.ins[i].script = new Bitcoin.Script(script); + } else { + tx.ins[i].script = new Bitcoin.Script([0]); + } + } + + //mirror the outpurs in the transaction to broadcast + var test = ''; + for (var i = 0; i < outputsToSend.length; i++) { + var addr = new Bitcoin.Address(addressToSend[i]); + tx.addOutput(addressToSend[i], outputsToSend[i]); + } + + //hash the transaction-- this has will be used as an input to the signature function + var txHash = tx.hashTransactionForSignature(tx.ins[index].script, index, 1); + + return txHash; + + } + + //function aGetTransaction + //TODO: rename + //generateas a transaction from a set of keys, outputs, signatures and addresses to send to + function aGetTransaction(publickeys, outputsToSpend, outputsToSend, addressToSend, sigs) { + + + //create a new transaction + var tx = new Bitcoin.Transaction(); + + var ins = []; + var outs = []; + + //generate the scripts to spend the outputs + for (var i = 0; i < outputsToSpend.length; i++) { + + var len = sigs[i].length; + var script = []; + + //append the signature + script = script.concat(sigs[i]); + + //prepend the length of the signature + script.unshift(len); + script.unshift(0x00); + + //push the script used to validate the spend + script.push(0x4c); + script.push(105); + script.push(0x52); + script.push(33); + script = script.concat(publickeys[i][0]); + script.push(33); + script = script.concat(publickeys[i][1]); + script.push(33); + script = script.concat(publickeys[i][2]); + script.push(0x53); + script.push(0xae); + + //add the input to the transaction referencing the output to spend + var p = outputsToSpend[i].transactionId + ':' + outputsToSpend[i].outputIndex.toString(); + tx.addInput(p); + + //set the script on the input + tx.ins[i].script = new Bitcoin.Script(script); + + } + + //add the outputs to the transaction + for (var i = 0; i < outputsToSend.length; i++) { + tx.addOutput(addressToSend[i], outputsToSend[i]); + } + + var txHash = Array.apply([], tx.serialize()); + + return txHash; + } + + + function aGetTransactionData(params, callback) { + + + var derivedPublicKeys = []; + var derivedPrivateKeys = []; + + var signatures = []; + var hashesForSigning = []; + for (var i = 0; i < params.outputsToSpend.length; i++) { + var path = params.paths[i]; + + //derive the hashes for signing off on each input + var hashForSigning = aMultiSigHashForSigning3(params.publicKeys[i][0], params.publicKeys[i][1], params.publicKeys[i][2], i, params.outputsToSpend, params.amountsToSend, params.addressToSend); + //add to collection so they can be provided to the server later + //this saves the same process having to be done on the server side + hashesForSigning.push(Bitcoin.convert.bytesToHex(hashForSigning)); + + //get the user's hot private key + var key = params.userHotPrivKeys[i]; + + //sign the input + var sig = key.sign(hashForSigning).concat([1]); + + //add the signature + signatures.push(sig); + } + + //get the transaction and return along with the hashes used to sign + var txn = aGetTransaction(params.publicKeys, params.outputsToSpend, params.amountsToSend, params.addressToSend, signatures); + + //generate the signatures + //TO DO: check call back? + return callback("", hashesForSigning, Bitcoin.convert.bytesToHex(txn)); + } + + this.isAddressValid = isAddressValid; + function isAddressValid(address) { + var addrValid = true; + try { + var addressCheck = new Bitcoin.Address(address); + addressCheck.toString(); + } catch (err) { + addrValid = false; + } + return addrValid; + } + + this.sendTransaction = sendTransaction; + function sendTransaction(sendType, friendUserName, addressTo, amount, twoFactorCode, callback) { + + + //setup handles to report back progress + var progressel = ''; + var messel = ''; + + if (sendType == 'friend') { + messel = '#textMessageSend'; + progressel = '#sendfriendprogstatus'; + } + + if (sendType == 'standard') { + messel = '#textMessageSendStd'; + progressel = '#sendstdprogstatus'; + } + + if (sendType == 'invoice') { + messel = '#textMessageSendInv'; + progressel = '#sendinvprogstatus'; + } + + + var minersFee = 10000; + //?? + amount = Math.round(amount); + + if (m_this.m_settings.MinersFee) { + minersFee = m_this.m_settings.MinersFee; + } + + + //in the case of mobile the twoFactorCode is actually the device key + //and will return a twofactor override code + + getHotHash(twoFactorCode, function (err, hothash, twoFactorOverride) { + + if (twoFactorOverride) { + twoFactorCode = twoFactorOverride; + } + + //initialise the hot private key space + var bipHot = Bitcoin.HDWallet.fromSeedHex(hothash); + + //initialise the 3 public keys + var bipHotPub = Bitcoin.HDWallet.fromBase58(m_this.m_walletinfo.hotPub); + var bipCold = Bitcoin.HDWallet.fromBase58(m_this.m_walletinfo.coldPub); + var bipNinki = Bitcoin.HDWallet.fromBase58(m_this.m_walletinfo.ninkiPubKey); + + + var pdata = { guid: m_this.m_guid, sharedid: m_this.m_sharedid }; + + + $(messel).html('Getting unspent outputs...'); + + + API.post("/api/1/u/getunspentoutputs", pdata, function (err, outputs) { + + var outputs = JSON.parse(outputs); + var outputsToSpend = []; + var amountsToSend = []; + var addressToSend = []; + var userHotPrivKeys = []; + + var nodeLevels = []; + var publicKeys = []; + var packet = { addressToSend: addressToSend, amountsToSend: amountsToSend, outputsToSpend: outputsToSpend, userHotPrivKeys: userHotPrivKeys, guid: m_this.m_guid, paths: nodeLevels, publicKeys: publicKeys }; + + //get outputs to spend, calculate change amount minus miners fee + + $(progressel).width('20%'); + + //iterate the unspent outputs and select the first n that equal the amount to spend + //TO DO: do this before doing any key derivation + var amountSoFar = 0; + for (var i = 0; i < outputs.length; i++) { + + var pitem = outputs[i]; + var pout = { transactionId: pitem.TransactionId, outputIndex: pitem.OutputIndex, amount: pitem.Amount, address: pitem.Address } + + nodeLevels.push(pitem.NodeLevel); + + outputsToSpend.push(pout); + + //derive the private key to use for signing + userHotPrivKeys.push(deriveChild(pitem.NodeLevel, bipHot).priv); + + //derive the public keys to use for script generation + //this could be cached on the server as no privacy or hand-off issue that I can see + + var dbipHotPub = ""; + var dbipColdPub = ""; + var dbipNinkiPub = ""; + + if (pitem.PK1.length > 0) { + + dbipHotPub = Bitcoin.convert.hexToBytes(pitem.PK1); + dbipColdPub = Bitcoin.convert.hexToBytes(pitem.PK2); + dbipNinkiPub = Bitcoin.convert.hexToBytes(pitem.PK3); + + } else { + + dbipHotPub = deriveChild(pitem.NodeLevel, bipHotPub).pub.toBytes(); + dbipColdPub = deriveChild(pitem.NodeLevel, bipCold).pub.toBytes(); + dbipNinkiPub = deriveChild(pitem.NodeLevel, bipNinki).pub.toBytes(); + + } + + publicKeys.push([dbipHotPub, dbipColdPub, dbipNinkiPub]); + + //add the amount + amountSoFar += pitem.Amount; + + if ((amountSoFar - amount) >= minersFee) { + break; + } + + } + + amountsToSend.push(amount); + + //now create the change + //again move this before the key derivation + var changeAmount = amountSoFar - (amount + minersFee); + + if (changeAmount < 0) { + //this message needs to be handled carefully + //as the wallet will most likely have pending confirmations + //rather than insufficent funds + return callback(true, "ErrInsufficientFunds"); + } - //create the change address, this must be done on the client - createAddress('m/0/1', changeAmount, function (err, changeaddress) { + if (changeAmount > 0) { + amountsToSend.push(changeAmount); + } - if (!err) { + //create a new address for my change to be sent back to me - $(progressel).width('40%'); - if (changeAmount > 0) { - addressToSend.push(changeaddress); - } + //if we are sending money to a contact or paying an invoice + //we need to derive addresses on their behalf + + if (sendType == 'friend' || sendType == 'invoice') { + var params = { guid: m_this.m_guid, sharedid: m_this.m_sharedid, username: friendUserName, amount: amount }; + + //generate the address for the contact + //this must be done on the client + $(messel).html('Creating address...'); + createAddressForFriend(friendUserName, function (err, address) { + + if (!err) { + + $(progressel).width('40%'); + addressToSend.push(address); - //now get the transaction - $(messel).html('Creating transaction...'); - aGetTransactionData(packet, function (err, hashesForSigning, rawTransaction) { - $(progressel).width('60%'); - var jsonSend = { guid: m_this.m_guid, hashesForSigning: hashesForSigning, rawTransaction: rawTransaction, pathsToSignWith: nodeLevels } - var jsonp1 = { jsonPacket: JSON.stringify(jsonSend), sharedid: m_this.m_sharedid, userName: '' }; - $(messel).html('Counter-signing transaction...'); - API.post("/api/1/u/sendtransaction", jsonp1, function (err, result) { + //create the change address, this must be done on the client + $(messel).html('Creating change address...'); + + createAddress('m/0/1', changeAmount, function (err, changeaddress) { - $(progressel).width('80%'); if (!err) { - $(progressel).width('100%'); - $(messel).html('Transaction broadcast...'); + $(progressel).width('60%'); + if (changeAmount > 0) { + addressToSend.push(changeaddress); + } + //now get the transaction data + $(messel).html('Creating transaction...'); + aGetTransactionData(packet, function (err, hashesForSigning, rawTransaction) { + + $(progressel).width('80%'); + var jsonSend = { guid: m_this.m_guid, hashesForSigning: hashesForSigning, rawTransaction: rawTransaction, pathsToSignWith: nodeLevels } + var jsonp1 = { guid: m_this.m_guid, jsonPacket: JSON.stringify(jsonSend), sharedid: m_this.m_sharedid, twoFactorCode: twoFactorCode, userName: friendUserName }; + $(messel).html('Counter-signing transaction...'); + + //send the transaction to the service for countersigning and broadcast to the network + + + //requires 2 factor authentication + + API.post("/api/1/u/sendtransaction", jsonp1, function (err, result) { + + if (!err) { + $(progressel).width('100%'); + $(messel).html('Transaction broadcast...'); + //error handling? + + //we have a transaction id so lets make a note of the transaction in the database + var params = { guid: m_this.m_guid, sharedid: m_this.m_sharedid, username: friendUserName, transactionid: result, address: address, amount: amount }; + API.post("/api/1/u/createtransactionrecord", params, function (err, result) { + return callback(err, params.transactionid); + }); + } else { + + //handle error messages returned from the server + if (result == 'ErrSingleTransactionLimit') { + $(progressel).width('100%'); + $(messel).html('Transaction Failed: Single limit exceeded'); + return callback(err, result); + } + + if (result == 'ErrDailyTransactionLimit') { + $(progressel).width('100%'); + $(messel).html('Transaction Failed: Daily limit exceeded'); + return callback(err, result); + } + + if (result == 'ErrTransactionsPerDayLimit') { + $(progressel).width('100%'); + $(messel).html('Transaction Failed: Daily number limit exceeded'); + return callback(err, result); + } + + if (result == 'ErrTransactionsPerHourLimit') { + $(progressel).width('100%'); + $(messel).html('Transaction Failed: Hourly number limit exceeded'); + return callback(err, result); + } + if (result == 'ErrInvalidRequest') { + $(progressel).width('100%'); + $(messel).html('Transaction Failed: Invalid request'); + return callback(err, result); + } + + if (result == 'ErrLocked') { + $(progressel).width('100%'); + $(messel).html('Transaction Failed: Account is unavailable'); + return callback(err, result); + } + + + if (result == 'ErrBroadcastFailed') { + $(progressel).width('100%'); + $(messel).html('Transaction Failed: Not accepted'); + return callback(err, result); + } + + if (result == "Invalid two factor code") { + $(progressel).width('100%'); + $(messel).html("Invalid two factor code"); + return callback(err, result); + } - //we have a transaction id so lets make a note of the transaction in the database - var params = { guid: m_this.m_guid, sharedid: m_this.m_sharedid, username: 'External', transactionid: result, address: addressTo, amount: amount }; - API.post("/api/1/u/createtransactionrecord", params, function (err, result) { - return callback(err, result); + } + }); }); } else { - if (result == 'ErrSingleTransactionLimit') { + //create address error + if (changeaddress == 'ErrInvalidRequest') { $(progressel).width('100%'); - $(messel).html('Transaction Failed: Single limit exceeded'); - return callback(err, result); + $(messel).html('Transaction Failed: Invalid request'); + return callback(err, changeaddress); } + } + }); + } else { - if (result == 'ErrDailyTransactionLimit') { - $(progressel).width('100%'); - $(messel).html('Transaction Failed: Daily limit exceeded'); - return callback(err, result); - } + if (address == 'ErrInvalidRequest') { + $(progressel).width('100%'); + $(messel).html('Transaction Failed: Invalid request'); + return callback(err, address); + } - if (result == 'ErrTransactionsPerDayLimit') { - $(progressel).width('100%'); - $(messel).html('Transaction Failed: Daily number limit exceeded'); - return callback(err, result); - } + } + }); + } else { - if (result == 'ErrTransactionsPerHourLimit') { - $(progressel).width('100%'); - $(messel).html('Transaction Failed: Hourly number limit exceeded'); - return callback(err, result); - } - if (result == 'ErrInvalidRequest') { - $(progressel).width('100%'); - $(messel).html('Transaction Failed: Invalid request'); - return callback(err, result); - } + $(messel).html('Creating address...'); + + addressToSend.push(addressTo); + + //create the change address, this must be done on the client + createAddress('m/0/1', changeAmount, function (err, changeaddress) { + + if (!err) { + + $(progressel).width('40%'); + if (changeAmount > 0) { + addressToSend.push(changeaddress); + } + //now get the transaction + $(messel).html('Creating transaction...'); + aGetTransactionData(packet, function (err, hashesForSigning, rawTransaction) { + $(progressel).width('60%'); + var jsonSend = { guid: m_this.m_guid, hashesForSigning: hashesForSigning, rawTransaction: rawTransaction, pathsToSignWith: nodeLevels } + var jsonp1 = { guid: m_this.m_guid, jsonPacket: JSON.stringify(jsonSend), sharedid: m_this.m_sharedid, twoFactorCode: twoFactorCode, userName: '' }; + $(messel).html('Counter-signing transaction...'); + API.post("/api/1/u/sendtransaction", jsonp1, function (err, result) { - if (result == 'ErrBroadcastFailed') { + $(progressel).width('80%'); + if (!err) { $(progressel).width('100%'); - $(messel).html('Transaction Failed: Not accepted'); - return callback(err, result); - } + $(messel).html('Transaction broadcast...'); - } + + //we have a transaction id so lets make a note of the transaction in the database + var params = { guid: m_this.m_guid, sharedid: m_this.m_sharedid, username: 'External', transactionid: result, address: addressTo, amount: amount }; + API.post("/api/1/u/createtransactionrecord", params, function (err, result) { + return callback(err, result); + }); + } else { + if (result == 'ErrSingleTransactionLimit') { + $(progressel).width('100%'); + $(messel).html('Transaction Failed: Single limit exceeded'); + return callback(err, result); + } + + if (result == 'ErrDailyTransactionLimit') { + $(progressel).width('100%'); + $(messel).html('Transaction Failed: Daily limit exceeded'); + return callback(err, result); + } + + if (result == 'ErrTransactionsPerDayLimit') { + $(progressel).width('100%'); + $(messel).html('Transaction Failed: Daily number limit exceeded'); + return callback(err, result); + } + + if (result == 'ErrTransactionsPerHourLimit') { + $(progressel).width('100%'); + $(messel).html('Transaction Failed: Hourly number limit exceeded'); + return callback(err, result); + } + + if (result == 'ErrInvalidRequest') { + $(progressel).width('100%'); + $(messel).html('Transaction Failed: Invalid request'); + return callback(err, result); + } + + + if (result == 'ErrBroadcastFailed') { + $(progressel).width('100%'); + $(messel).html('Transaction Failed: Not accepted'); + return callback(err, result); + } + + if (result == "Invalid two factor code") { + $(progressel).width('100%'); + $(messel).html("Invalid two factor code"); + return callback(err, result); + } + + + } + }); }); - }); + } else { + + //create address error + if (changeaddress == 'ErrInvalidRequest') { + $(progressel).width('100%'); + $(messel).html('Transaction Failed: Invalid request'); + return callback(err, changeaddress); + } + + } + }); + } + + }); + + }); + + + } + + //function createAddress + //this function creates an address for the user + this.createAddress = createAddress; + + function createAddress(nodePath, changeamount, callback) { + + + if (changeamount > 0) { + var postData = { guid: m_this.m_guid, sharedid: m_this.m_sharedid, pathToUse: nodePath }; + + //get the next leaf from the user's node space + API.post("/api/1/u/getnextleaf", postData, function (err, leaf) { + + var path = nodePath + '/' + leaf; + + //derive the 3 public keys for the new address + //TODO: possible to use an encrypted cache for performance improvements + + var bipHot = Bitcoin.HDWallet.fromBase58(m_this.m_walletinfo.hotPub); + var bipCold = Bitcoin.HDWallet.fromBase58(m_this.m_walletinfo.coldPub); + var bipNinki = Bitcoin.HDWallet.fromBase58(m_this.m_walletinfo.ninkiPubKey); + + var hotKey = deriveChild(path, bipHot); + var coldKey = deriveChild(path, bipCold); + var ninkiKey = deriveChild(path, bipNinki); + + //now create the multisig address + var script = [0x52]; + script.push(33); + script = script.concat(hotKey.pub.toBytes()); + script.push(33); + script = script.concat(coldKey.pub.toBytes()); + script.push(33); + script = script.concat(ninkiKey.pub.toBytes()); + script.push(0x53); + script.push(0xae); + var address = multiSig(script); + + //post the address back to the server + //this allows the server to monitor for balances etc. + var postData = { guid: m_this.m_guid, sharedid: m_this.m_sharedid, path: path, address: address, pk1: hotKey.pub.toString(), pk2: coldKey.pub.toString(), pk3: ninkiKey.pub.toString() }; + API.post("/api/1/u/createaddress", postData, function (err, result) { + if (!err) { + return callback(err, address); } else { + return callback(err, result); + } + + }); + + //now update the address to the server + + }); + + } else { + return callback(false, "skipped"); + } + + } + + //function createAddress + //this function creates an address on behalf of a user's contact + this.createAddressForFriend = createAddressForFriend; + function createAddressForFriend(username, callback) { + + var postData = { guid: m_this.m_guid, sharedid: m_this.m_sharedid, username: username }; + + + API.post("/api/1/u/getfriendpacket", postData, function (err, packet) { + + //get the packet from friend containing the public key set to + //be used for address generation and decrypt + + var msg = openpgp.message.readArmored(packet); + var decrypted = openpgp.decryptAndVerifyMessage(m_this.m_privKey, [m_this.m_pubKey], msg); + + var pubkeys = JSON.parse(sanitizer.sanitize(decrypted.text)); + //var pubkeys = decrypt(packet, password, params.oguid); + //only allow address to be created if the packet has been validated + if (pubkeys.validated) { + //get the next leaf on the contacts address space node assigned to this user + API.post("/api/1/u/getnextleafforfriend", postData, function (err, leaf) { + + //derive the public keys + var path = 'm/' + leaf; + + var bipHot = Bitcoin.HDWallet.fromBase58(pubkeys.hotPub); + var bipCold = Bitcoin.HDWallet.fromBase58(pubkeys.coldPub); + var bipNinki = Bitcoin.HDWallet.fromBase58(pubkeys.ninkiPub); + + var hotKey = deriveChild(path, bipHot); + var coldKey = deriveChild(path, bipCold); + var ninkiKey = deriveChild(path, bipNinki); + + //now create the multisig address + var script = [0x52]; + script.push(33); + script = script.concat(hotKey.pub.toBytes()); + script.push(33); + script = script.concat(coldKey.pub.toBytes()); + script.push(33); + script = script.concat(ninkiKey.pub.toBytes()); + script.push(0x53); + script.push(0xae); + var address = multiSig(script); + + //register the address with the server + var postData = { guid: m_this.m_guid, sharedid: m_this.m_sharedid, username: username, address: address, leaf: leaf, pk1: hotKey.pub.toString(), pk2: coldKey.pub.toString(), pk3: ninkiKey.pub.toString() }; + API.post("/api/1/u/createaddressforfriend", postData, function (err, result) { + + if (!err) { + return callback(err, address); + } else { + return callback(err, result); + } + + }); + + //now update the address to the server + + }); + } else { + + //something very bad has happened + //attempting to derive an address for a non validated contact + return callback(true, "ErrInvalid"); + } + + }); + + } + + //multi sig address hash + function multiSig(rs) { + var x = Bitcoin.Crypto.RIPEMD160(Bitcoin.Crypto.SHA256(Bitcoin.convert.bytesToWordArray(rs))); + x = Bitcoin.convert.wordArrayToBytes(x); + x.unshift(0x5); + var r = x; + r = Bitcoin.Crypto.SHA256(Bitcoin.Crypto.SHA256(Bitcoin.convert.bytesToWordArray(r))); + var checksum = Bitcoin.convert.wordArrayToBytes(r).slice(0, 4); + var address = Bitcoin.base58.encode(x.concat(checksum)); + return address; + } + + + + + this.signMessage = signMessage; + function signMessage(key, guid, callback) { + + if (key.length > 0) { + var bip39 = new BIP39(); + + var mkey = bip39.mnemonicToHex(key); + var bytes = []; + for (var i = 0; i < guid.length; ++i) { + bytes.push(guid.charCodeAt(i)); + } + + var message = Bitcoin.Crypto.SHA256(Bitcoin.convert.bytesToWordArray(bytes)).toString(); + var skey = Bitcoin.HDWallet.fromSeedHex(mkey); + + var sig = Bitcoin.convert.bytesToHex(skey.priv.sign(Bitcoin.convert.hexToBytes(message))); + + callback(false, sig); + } else { + callback(false, ''); + } + + } + + + this.decodeKey = decodeKey; + function decodeKey(key) { + var bip39 = new BIP39(); + var mkey = bip39.mnemonicToHex(key); + return mkey; + } + + this.encodeKey = encodeKey; + function encodeKey(key) { + var bip39 = new BIP39(); + var mkey = bip39.entropyToMnemonic(key); + return mkey; + } + + + this.getPassKey = getPassKey; + function getPassKey(hotkey, coldkey, epass, iv, callback) { + + var bip39 = new BIP39(); + + var hkey = bip39.mnemonicToHex(hotkey); + var ckey = bip39.mnemonicToHex(coldkey); + + var hckey = Bitcoin.Crypto.SHA256(Bitcoin.convert.bytesToWordArray(Bitcoin.convert.hexToBytes(hkey + ckey))).toString(); + + var passkey = decryptNp(epass, hckey, iv); + + callback(false, passkey); + + } + + + //reengineer this + //pull back validated proprty from database + //only do this part on friend selection + this.getUserNetwork = getUserNetwork; + function getUserNetwork(callback) { + var postData = { guid: m_this.m_guid, sharedid: m_this.m_sharedid }; + API.post("/api/1/u/getusernetwork", postData, function (err, data) { + var friends = JSON.parse(data); + return callback(err, friends); + }); + } + + + this.getFriend = getFriend; + function getFriend(username, callback) { + + var postData = { guid: m_this.m_guid, sharedid: m_this.m_sharedid, username: username }; + + return API.post("/api/1/u/getfriend", postData, function (err, data) { + + var friend = JSON.parse(data); + + return callback(err, friend); + }); + + } + + + this.createFriend = createFriend; + function createFriend(username, uimessage, callback) { + + //get the next friend node + var node = ""; + var postData = { guid: m_this.m_guid, sharedid: m_this.m_sharedid, username: username }; + + if ($(uimessage)) { + $(uimessage).html('Assigning node...'); + } + + API.post("/api/1/u/getnextnodeforfriend", postData, function (err, node) { - //create address error - if (changeaddress == 'ErrInvalidRequest') { - $(progressel).width('100%'); - $(messel).html('Transaction Failed: Invalid request'); - return callback(err, changeaddress); - } + var bipHot = Bitcoin.HDWallet.fromBase58(m_this.m_walletinfo.hotPub); - } - }); - } + var bipCold = Bitcoin.HDWallet.fromBase58(m_this.m_walletinfo.coldPub); + var bipNinki = Bitcoin.HDWallet.fromBase58(m_this.m_walletinfo.ninkiPubKey); - }); + setTimeout(function () { - } + var hotKey = deriveChild(node, bipHot).toString(); - //function createAddress - //this function creates an address for the user - this.createAddress = createAddress; + if ($(uimessage)) { + $(uimessage).html('Deriving address.'); + } - function createAddress(nodePath, changeamount, callback) { + setTimeout(function () { + var coldKey = deriveChild(node, bipCold).toString(); + if ($(uimessage)) { + $(uimessage).html('Deriving address..'); + } - if (changeamount > 0) { - var postData = { guid: m_this.m_guid, sharedid: m_this.m_sharedid, pathToUse: nodePath }; + setTimeout(function () { - //get the next leaf from the user's node space - API.post("/api/1/u/getnextleaf", postData, function (err, leaf) { + var ninkiKey = deriveChild(node, bipNinki).toString(); + if ($(uimessage)) { + $(uimessage).html('Deriving address...'); + } + //get the friends public RSA key + var rsaKey = ''; - var path = nodePath + '/' + leaf; + $(uimessage).html('Get PGP keys...'); + API.post("/api/1/u/getrsakey", postData, function (err, rsaKey) { - //derive the 3 public keys for the new address - //TODO: possible to use an encrypted cache for performance improvements + var publicKeys = openpgp.key.readArmored(rsaKey); + if ($(uimessage)) { + $(uimessage).html('Encrypting data...'); + } - var bipHot = Bitcoin.HDWallet.fromBase58(m_this.m_walletinfo.hotPub); - var bipCold = Bitcoin.HDWallet.fromBase58(m_this.m_walletinfo.coldPub); - var bipNinki = Bitcoin.HDWallet.fromBase58(m_this.m_walletinfo.ninkiPubKey); + var pubKey = publicKeys.keys[0]; - var hotKey = deriveChild(path, bipHot); - var coldKey = deriveChild(path, bipCold); - var ninkiKey = deriveChild(path, bipNinki); + var message = hotKey + coldKey + ninkiKey; - //now create the multisig address - var script = [0x52]; - script.push(33); - script = script.concat(hotKey.pub.toBytes()); - script.push(33); - script = script.concat(coldKey.pub.toBytes()); - script.push(33); - script = script.concat(ninkiKey.pub.toBytes()); - script.push(0x53); - script.push(0xae); - var address = multiSig(script); + var encrypted = openpgp.signAndEncryptMessage([pubKey], m_this.m_privKey, message); - //post the address back to the server - //this allows the server to monitor for balances etc. - var postData = { guid: m_this.m_guid, sharedid: m_this.m_sharedid, path: path, address: address }; - API.post("/api/1/u/createaddress", postData, function (err, result) { - if (!err) { - return callback(err, address); - } else { - return callback(err, result); - } + var result = ""; - }); + var postFriendData = { guid: m_this.m_guid, sharedid: m_this.m_sharedid, username: username, node: node, packetForFriend: encrypted, validationHash: '' }; + API.post("/api/1/u/createfriend", postFriendData, function (err, result) { - //now update the address to the server + return callback(err, result); - }); + }); - } else { - return callback(false, "skipped"); - } + }); + + }, 50); + }, 50); + }, 50); + + }); } - //function createAddress - //this function creates an address on behalf of a user's contact - this.createAddressForFriend = createAddressForFriend; - function createAddressForFriend(username, callback) { + this.acceptFriendRequest = acceptFriendRequest; + function acceptFriendRequest(username, callback) { - var postData = { guid: m_this.m_guid, sharedid: m_this.m_sharedid, username: username }; - //get the encrypted packet for the user's contact - //don't need to call this??? - API.post("/api/1/u/getfriendpacket", postData, function (err, packet) { + var postData = { guid: m_this.m_guid, sharedid: m_this.m_sharedid, username: username }; + API.post("/api/1/u/getfriendrequestpacket", postData, function (err, packet) { //get the packet from friend containing the public key set to - //be used for address generation and decrypt + //be used for address generation - API.getUserPacket(m_this.m_guid, m_this.m_sharedid, function (err, encpacket) { + var message = packet; - //get the RSA private key from the encrypted payload - var rsaKeyPair = decrypt(encpacket.Payload, m_this.m_password, encpacket.IV); + var rsaKey = ''; + var postRSAData = { guid: m_this.m_guid, sharedid: m_this.m_sharedid, username: username }; + API.post("/api/1/u/getrsakey", postRSAData, function (err, rsaKey) { + + //friends public key + var publicKeys = openpgp.key.readArmored(rsaKey); - var publicKeys = openpgp.key.readArmored(rsaKeyPair.RSAPub); - var privKeys = openpgp.key.readArmored(rsaKeyPair.RSAPriv); + //we need to get friends public key here to verify the signature on the packet + //then out of band if they verify the signature belongs to them- they are good - var privKey = privKeys.keys[0]; var pubKey = publicKeys.keys[0]; - if (privKey.decrypt(m_this.m_sharedid)) { + var msg = openpgp.message.readArmored(message); + var decrypted = openpgp.decryptAndVerifyMessage(m_this.m_privKey, [pubKey], msg); - var msg = openpgp.message.readArmored(packet); - var decrypted = openpgp.decryptAndVerifyMessage(privKey, [pubKey], msg); + var isValid = decrypted.signatures[0].valid; + if (isValid) { + + var keys = sanitizer.sanitize(decrypted.text); - var pubkeys = JSON.parse(decrypted.text); - //var pubkeys = decrypt(packet, password, params.oguid); + var key1 = keys.substring(0, 111); + var key2 = keys.substring(111, 222); + var key3 = keys.substring(222, 333); - //get the next leaf on the contacts address space node assigned to this user - API.post("/api/1/u/getnextleafforfriend", postData, function (err, leaf) { - //derive the public keys - var path = 'm/' + leaf; + var packet = { + hotPub: key1, + coldPub: key2, + ninkiPub: key3, + rsaKey: rsaKey, + validated: false + }; - var bipHot = Bitcoin.HDWallet.fromBase58(pubkeys.hotPub); - var bipCold = Bitcoin.HDWallet.fromBase58(pubkeys.coldPub); - var bipNinki = Bitcoin.HDWallet.fromBase58(pubkeys.ninkiPub); + var encrypted = openpgp.signAndEncryptMessage([m_this.m_pubKey], m_this.m_privKey, JSON.stringify(packet)); - var hotKey = deriveChild(path, bipHot); - var coldKey = deriveChild(path, bipCold); - var ninkiKey = deriveChild(path, bipNinki); + postData = { guid: m_this.m_guid, sharedid: m_this.m_sharedid, username: username, packet: encrypted, validationHash: '' }; - //now create the multisig address - var script = [0x52]; - script.push(33); - script = script.concat(hotKey.pub.toBytes()); - script.push(33); - script = script.concat(coldKey.pub.toBytes()); - script.push(33); - script = script.concat(ninkiKey.pub.toBytes()); - script.push(0x53); - script.push(0xae); - var address = multiSig(script); + API.post("/api/1/u/updatefriend", postData, function (err, result) { + return callback(err, result); + }); + } - //register the address with the server - var postData = { guid: m_this.m_guid, sharedid: m_this.m_sharedid, username: username, address: address, leaf: leaf }; - API.post("/api/1/u/createaddressforfriend", postData, function (err, result) { + }); + }); - if (!err) { - return callback(err, address); - } else { - return callback(err, result); - } + } - }); - //now update the address to the server - }); - } + this.getFingerPrint = getFingerPrint; + function getFingerPrint(callback) { - }); + //now all we need to do is provide the fingerprint of the user's public key - }); + return callback(false, m_this.m_pubKey.primaryKey.fingerprint); } - //multi sig address hash - function multiSig(rs) { - var x = Bitcoin.Crypto.RIPEMD160(Bitcoin.Crypto.SHA256(Bitcoin.convert.bytesToWordArray(rs))); - x = Bitcoin.convert.wordArrayToBytes(x); - x.unshift(0x5); - var r = x; - r = Bitcoin.Crypto.SHA256(Bitcoin.Crypto.SHA256(Bitcoin.convert.bytesToWordArray(r))); - var checksum = Bitcoin.convert.wordArrayToBytes(r).slice(0, 4); - var address = Bitcoin.base58.encode(x.concat(checksum)); - return address; - } + this.verifyFriendData = verifyFriendData; + function verifyFriendData(username, code, callback) { + + //update packet with status as verified and log + //the verification code - this.getUserNetwork = getUserNetwork; - function getUserNetwork(callback) { + var postData = { guid: m_this.m_guid, sharedid: m_this.m_sharedid, username: username }; + API.post("/api/1/u/getfriendpacket", postData, function (err, packet) { - API.getUserPacket(m_this.m_guid, m_this.m_sharedid, function (err, encpacket) { - var rsaKeys = decrypt(encpacket.Payload, m_this.m_password, encpacket.IV); + //this is set to a fixed value, i wanted it to be set as blank as we encrypt these keys aes256 anyway + //there was no easy way to change this password in the library so i opted to go with a fixed password + //instead of a blank one - var privKeys = openpgp.key.readArmored(rsaKeys.RSAPriv); - var pubKeys = openpgp.key.readArmored(rsaKeys.RSAPub); - var privKey = privKeys.keys[0]; - var pubKey = pubKeys.keys[0]; + var msg = openpgp.message.readArmored(packet); + var decrypted = openpgp.decryptAndVerifyMessage(m_this.m_privKey, [m_this.m_pubKey], msg); - if (privKey.decrypt(m_this.m_sharedid)) { + var isValid = decrypted.signatures[0].valid; + if (isValid) { - var postData = { guid: m_this.m_guid, sharedid: m_this.m_sharedid }; + var payload = JSON.parse(sanitizer.sanitize(decrypted.text)); - return API.post("/api/1/u/getusernetwork", postData, function (err, data) { + var publicKeysUsed = openpgp.key.readArmored(payload.rsaKey); + var pubKeyUsed = publicKeysUsed.keys[0]; - var friends = JSON.parse(data); + if (code == pubKeyUsed.primaryKey.fingerprint) { - for (var i = 0; i < friends.length; i++) { - var friend = friends[i]; - if (friend.packet != '') { + var reencrypt = { + hotPub: payload.hotPub, + coldPub: payload.coldPub, + ninkiPub: payload.ninkiPub, + rsaKey: payload.rsaKey, + validated: true + }; - var msg = openpgp.message.readArmored(friend.packet); - var decrypted = openpgp.decryptAndVerifyMessage(privKey, [pubKey], msg); + var encryptedPayload = openpgp.signAndEncryptMessage([m_this.m_pubKey], m_this.m_privKey, JSON.stringify(reencrypt)); - var friendprops = JSON.parse(decrypted.text); + postData = { guid: m_this.m_guid, sharedid: m_this.m_sharedid, username: username, packet: encryptedPayload, validationHash: code }; + + API.post("/api/1/u/updatefriend", postData, function (err, result) { + + return callback(err, result); + + }); + + return callback(err, true); + + } else { + + return callback(err, false); + + } - // var payload = decrypt(friend.packet, params.password, params.oguid); - if (friendprops.validated) { - friend.validated = true; - } - } - } - return callback(err, friends); - }); } }); } - this.createFriend = createFriend; - function createFriend(username, callback) { - - //get the next friend node - var node = ""; - var postData = { guid: m_this.m_guid, sharedid: m_this.m_sharedid, username: username }; - API.post("/api/1/u/getnextnodeforfriend", postData, function (err, node) { - var bipHot = Bitcoin.HDWallet.fromBase58(m_this.m_walletinfo.hotPub); - var bipCold = Bitcoin.HDWallet.fromBase58(m_this.m_walletinfo.coldPub); - var bipNinki = Bitcoin.HDWallet.fromBase58(m_this.m_walletinfo.ninkiPubKey); + //status + //0 pending + //1 paid + //2 rejected + //3 notsent - var hotKey = deriveChild(node, bipHot).toString(); - var coldKey = deriveChild(node, bipCold).toString(); - var ninkiKey = deriveChild(node, bipNinki).toString(); + this.createInvoice = createInvoice; + function createInvoice(username, invoice, callback) { - //get the friends public RSA key - var rsaKey = ''; - API.post("/api/1/u/getrsakey", postData, function (err, rsaKey) { + var packetForMe = ""; + var packetForThem = ""; - var publicKeys = openpgp.key.readArmored(rsaKey); + var jsonInvoice = JSON.stringify(invoice); - API.getUserPacket(m_this.m_guid, m_this.m_sharedid, function (err, encpacket) { + //get the contacts RSA key - //here we need to persist the public key used to - //encrypt the data + //encrypt the packet for me with my public rsa key and sign with my private key - //get the RSA private key from the encrypted payload - //TODO: we don;t need to do this bit as the private jey is already encrypted - var rsaPriv = decrypt(encpacket.Payload, m_this.m_password, encpacket.IV); + var rsaKey = ''; + var postRSAData = { guid: m_this.m_guid, sharedid: m_this.m_sharedid, username: username }; + API.post("/api/1/u/getrsakey", postRSAData, function (err, rsaKey) { - var privKeys = openpgp.key.readArmored(rsaPriv.RSAPriv); + var publicKeys = openpgp.key.readArmored(rsaKey); - var privKey = privKeys.keys[0]; - var pubKey = publicKeys.keys[0]; - if (privKey.decrypt(m_this.m_sharedid)) { + var pubKey = publicKeys.keys[0]; + //here we need to persist the public key used to + //encrypt the data - var message = hotKey + coldKey + ninkiKey; + //get the RSA private key from the encrypted payload - var encrypted = openpgp.signAndEncryptMessage([pubKey], privKey, message); + //generate a hash from the RSA key and public keys for verification + var message = jsonInvoice; - var result = ""; + var encrypted = openpgp.signAndEncryptMessage([pubKey], m_this.m_privKey, message); - var postFriendData = { guid: m_this.m_guid, sharedid: m_this.m_sharedid, username: username, node: node, packetForFriend: encrypted, validationHash: '' }; - API.post("/api/1/u/createfriend", postFriendData, function (err, result) { + //encrypt with my public key and sin with my priv key + var packetForMe = openpgp.signAndEncryptMessage([m_this.m_pubKey], m_this.m_privKey, message); - return callback(err, result); - }); + var result = ""; - } + var pdata = { guid: m_this.m_guid, sharedid: m_this.m_sharedid, userName: username, packetForMe: packetForMe, packetForThem: encrypted }; + API.post("/api/1/u/createinvoice", pdata, function (err, invoiceid) { - }); + return callback(err, invoiceid); }); @@ -1236,391 +2352,408 @@ function Engine() { } - this.acceptFriendRequest = acceptFriendRequest; - function acceptFriendRequest(username, callback) { + this.UnpackInvoiceByMe = UnpackInvoiceByMe; + function UnpackInvoiceByMe(invoice, username, callback) { + //here decrypt the invoice with my private key - API.getUserPacket(m_this.m_guid, m_this.m_sharedid, function (err, encpacket) { + var msg = openpgp.message.readArmored(invoice.Packet); + var decrypted = openpgp.decryptAndVerifyMessage(m_this.m_privKey, [m_this.m_pubKey], msg); - //get the RSA private key from the encrypted payload - var rsaKeyPair = decrypt(encpacket.Payload, m_this.m_password, encpacket.IV); + var unpackedInvoice = JSON.parse(sanitizer.sanitize(decrypted.text)); - var postData = { guid: m_this.m_guid, sharedid: m_this.m_sharedid, username: username }; - API.post("/api/1/u/getfriendrequestpacket", postData, function (err, packet) { + callback(false, unpackedInvoice); - //get the packet from friend containing the public key set to - //be used for address generation - var message = packet; + } - var rsaKey = ''; - var postRSAData = { guid: m_this.m_guid, sharedid: m_this.m_sharedid, username: username }; - API.post("/api/1/u/getrsakey", postRSAData, function (err, rsaKey) { + this.UnpackInvoiceForMe = UnpackInvoiceForMe; + function UnpackInvoiceForMe(invoice, username, invtype, callback) { - //friends public key - var publicKeys = openpgp.key.readArmored(rsaKey); + //here decrypt the invoice with my private key - //we need to get friends public key here to verify the signature on the packet - //then out of band if they verify the signature belongs to them- they are good + var rsaKey = ''; + var postRSAData = { guid: m_this.m_guid, sharedid: m_this.m_sharedid, username: username }; + API.post("/api/1/u/getrsakey", postRSAData, function (err, rsaKey) { - //my private key - var privKeys = openpgp.key.readArmored(rsaKeyPair.RSAPriv); + //friends public key - var privKey = privKeys.keys[0]; - var pubKey = publicKeys.keys[0]; + //we need to get friends public key here to verify the signature on the packet + //then out of band if they verify the signature belongs to them- they are good - var success = privKey.decrypt(m_this.m_sharedid); + if (invtype == 'forme') { + var publicKeys = openpgp.key.readArmored(rsaKey); + pubKey = publicKeys.keys[0]; + } else { + pubKey = m_this.m_pubKey; + } - if (success) { + var msg = openpgp.message.readArmored(invoice.Packet); + var decrypted = openpgp.decryptAndVerifyMessage(m_this.m_privKey, [pubKey], msg); - var msg = openpgp.message.readArmored(message); - var decrypted = openpgp.decryptAndVerifyMessage(privKey, [pubKey], msg); + var isValid = decrypted.signatures[0].valid; + if (isValid) { + + var json = JSON.parse(sanitizer.sanitize(decrypted.text)); - var isValid = decrypted.signatures[0].valid; - if (isValid) { + //remove any xss data + callback(false, json); - var keys = decrypted.text; + } else { - var key1 = keys.substring(0, 111); - var key2 = keys.substring(111, 222); - var key3 = keys.substring(222, 333); + callback(false, "error"); - //do we really need to do this? - //the rsa private key is already stored encrypted - //why not re-encrypt with my public key - //then I don;t have to worry about re-encryptng - //the packet if I change my password + } - //so changing password would mean, re-encrypting the main packet - //and re-encrypting the PGP set only + }); - //encrypt and sign with my pgp key pair - //then validate signature on create address - var packet = { - hotPub: key1, - coldPub: key2, - ninkiPub: key3, - rsaKey: rsaKey, - validated: false - }; + } - //encrypt and sign the data using my pgp set - var myPublicKeys = openpgp.key.readArmored(rsaKeyPair.RSAPub); - var myPubKey = myPublicKeys.keys[0]; + //security + this.SaveTwoFactor = SaveTwoFactor; + function SaveTwoFactor(twoFactorCode, verifyToken, callback) { - var encrypted = openpgp.signAndEncryptMessage([myPubKey], privKey, JSON.stringify(packet)); + var postData = { + guid: m_this.m_guid, + sharedid: m_this.m_sharedid, + twoFactorOnLogin: true, + twoFactorCode: twoFactorCode, + verifyToken: verifyToken + }; - postData = { guid: m_this.m_guid, sharedid: m_this.m_sharedid, username: username, packet: encrypted, validationHash: '' }; + API.post("/api/1/u/updatetwofactor", postData, function (err, result) { - API.post("/api/1/u/updatefriend", postData, function (err, result) { - return callback(err, result); - }); - } - } - }); - }); - }); - } + if (!err) { + var bytes = []; + for (var i = 0; i < m_this.m_sharedid.length; ++i) { + bytes.push(m_this.m_sharedid.charCodeAt(i)); + } + var dpacket = Bitcoin.Crypto.SHA256(Bitcoin.convert.bytesToWordArray(bytes)).toString(); + API.post("/api/1/verifyrecoverpacket", { guid: m_this.m_oguid, token: dpacket }, function (err, response) { - this.getFingerPrint = getFingerPrint; - function getFingerPrint(callback) { + callback(err, response); - API.getUserPacket(m_this.m_guid, m_this.m_sharedid, function (err, encpacket) { + }); - //get the RSA private key from the encrypted payload - var rsaKeyPair = decrypt(encpacket.Payload, m_this.m_password, encpacket.IV); - var publicKeys = openpgp.key.readArmored(rsaKeyPair.RSAPub); - var pubKey = publicKeys.keys[0]; + } else { - //now all we need to do is provide the fingerprint of the user's public key + callback(true, result); - return callback(err, pubKey.primaryKey.fingerprint); + } }); + } - this.verifyFriendData = verifyFriendData; - function verifyFriendData(username, code, callback) { - //update packet with status as verified and log - //the verification code + this.SetupTwoFactor = SaveTwoFactor; + function SetupTwoFactor(twoFactorCode, callback) { + + var postData = { + guid: m_this.m_guid, + sharedid: m_this.m_sharedid, + twoFactorOnLogin: true, + twoFactorCode: twoFactorCode, + verifyToken: '' + }; + + API.post("/api/1/u/updatetwofactor", postData, function (err, result) { + $('#API-Token').val(result); - API.getUserPacket(m_this.m_guid, m_this.m_sharedid, function (err, encpacket) { + callback(err, result); - var rsaKeyPair = decrypt(encpacket.Payload, m_this.m_password, encpacket.IV); + }); - var postData = { guid: m_this.m_guid, sharedid: m_this.m_sharedid, username: username }; - API.post("/api/1/u/getfriendpacket", postData, function (err, packet) { + } - var privKeys = openpgp.key.readArmored(rsaKeyPair.RSAPriv); - var pubKeys = openpgp.key.readArmored(rsaKeyPair.RSAPub); + this.ResetTwoFactor = ResetTwoFactor; + function ResetTwoFactor(fguid, fusername, fpwd, callback) { - var privKey = privKeys.keys[0]; - var pubKey = pubKeys.keys[0]; + //stretch password - //this is set to a fixed value, i wanted it to be set as blank as we encrypt these keys aes256 anyway - //there was no easy way to change this password in the library so i opted to go with a fixed password - //instead of a blank one - var success = privKey.decrypt(m_this.m_sharedid); + //download the recovery packet + //decrypt + //return shared secret + //no feedback apart from, please check your email + fpwd = pbkdf2(fpwd, fguid); - if (success) { + API.post("/api/1/getrecoverypacket", { + guid: fguid, + username: fusername + }, function (err, response) { - var msg = openpgp.message.readArmored(packet); - var decrypted = openpgp.decryptAndVerifyMessage(privKey, [pubKey], msg); + if (err) { - var isValid = decrypted.signatures[0].valid; - if (isValid) { + callback(err, response); - var payload = JSON.parse(decrypted.text); + } else { - var publicKeysUsed = openpgp.key.readArmored(payload.rsaKey); - var pubKeyUsed = publicKeysUsed.keys[0]; + //decrypt packet - if (code == pubKeyUsed.primaryKey.fingerprint) { + var jpacket = JSON.parse(response); - var reencrypt = { - hotPub: payload.hotPub, - coldPub: payload.coldPub, - ninkiPub: payload.ninkiPub, - rsaKey: payload.rsaKey, - validated: true - }; + try { + var dpacket = decrypt(jpacket.packet, fpwd, jpacket.IV); - var encryptedPayload = openpgp.signAndEncryptMessage([pubKey], privKey, JSON.stringify(reencrypt)); + API.post("/api/1/verifyrecoverpacket", { guid: fguid, token: dpacket }, function (err, response) { - postData = { guid: m_this.m_guid, sharedid: m_this.m_sharedid, username: username, packet: encryptedPayload, validationHash: code }; + callback(err, response); - API.post("/api/1/u/updatefriend", postData, function (err, result) { + }); + } catch (error) { - return callback(err, result); + callback(true, "error"); - }); + } - return callback(err, true); + } - } else { + fpwd = ''; - return callback(err, false); + }); - } + } - } - } - }); + this.ChangePassword = ChangePassword; + function ChangePassword(twoFactorCode, oldpassword, newpassword, progbar, progmess, reset, message1, message2, callback) { - }); - } + API.getWalletFromServer(m_this.m_guid, m_this.m_secret, twoFactorCode, false, function (err, wallet) { + if (!err) { + //check password strength - //status - //0 pending - //1 paid - //2 rejected - //3 notsent + //stretch old password + //verify that it matches the current one - this.createInvoice = createInvoice; - function createInvoice(username, invoice, callback) { + getHotHash("", function (err, hothash) { - var packetForMe = ""; - var packetForThem = ""; + $(progbar).width('40%'); + $(progmess).html('Securing password...'); + setTimeout(function () { - var jsonInvoice = JSON.stringify(invoice); + //if password reset do not pbkdf the password - //get the contacts RSA key + oldpassword = pbkdf2(oldpassword, m_this.m_oguid); - //encrypt the packet for me with my public rsa key and sign with my private key + //get the two packets + $(progbar).width('40%'); + $(progmess).html('Getting packets...'); - var rsaKey = ''; - var postRSAData = { guid: m_this.m_guid, sharedid: m_this.m_sharedid, username: username }; - API.post("/api/1/u/getrsakey", postRSAData, function (err, rsaKey) { + // $("#chngpwdprogbar").width('50%'); + //$("#chngpwdprogmess").html('Decrypting account packet...'); + //decrypt with the old password + var decryptedWithOld = true; + var decryptedPayload = ''; + try { + decryptedPayload = decrypt(wallet.Payload, m_this.m_password, wallet.IV); + } catch (err) { + decryptedWithOld = false; + } - var publicKeys = openpgp.key.readArmored(rsaKey); + if (decryptedWithOld) { - API.getUserPacket(m_this.m_guid, m_this.m_sharedid, function (err, encpacket) { + $(progbar).width('80%'); + $(progmess).html('Securing new password...'); + API.getUserPacket(m_this.m_guid, m_this.m_sharedid, function (err, encpacket) { - //here we need to persist the public key used to - //encrypt the data + var decryptedUsrWithOld = true; + var rsaKeyPair = ''; + try { + rsaKeyPair = decrypt(encpacket.Payload, m_this.m_password, encpacket.IV); + } catch (err) { + decryptedUsrWithOld = false; + } - //get the RSA private key from the encrypted payload - var rsaKeyPair = decrypt(encpacket.Payload, m_this.m_password, encpacket.IV); - var privKeys = openpgp.key.readArmored(rsaKeyPair.RSAPriv); - var myPublicKeys = openpgp.key.readArmored(rsaKeyPair.RSAPub); - var myPubKey = myPublicKeys.keys[0]; + if (decryptedUsrWithOld) { - var privKey = privKeys.keys[0]; - var pubKey = publicKeys.keys[0]; + //get the verification packet - if (privKey.decrypt(m_this.m_sharedid)) { - //generate a hash from the RSA key and public keys for verification - var message = jsonInvoice; + API.post("/api/1/getrecoverypacket", { + guid: m_this.m_guid + }, function (err, response) { - var encrypted = openpgp.signAndEncryptMessage([pubKey], privKey, message); - //encrypt with my public key and sin with my priv key - var packetForMe = openpgp.signAndEncryptMessage([myPubKey], privKey, message); + //decrypt packet + var decryptedVerWithOld = true; + var jpacket = JSON.parse(response); + var veripacket = ''; + try { + veripacket = decryptNp(jpacket.packet, m_this.m_password, jpacket.IV); + } + catch (verror) { + decryptedVerWithOld = false; + } - var result = ""; + if (decryptedVerWithOld) { - var pdata = { guid: m_this.m_guid, sharedid: m_this.m_sharedid, userName: username, packetForMe: packetForMe, packetForThem: encrypted }; - API.post("/api/1/u/createinvoice", pdata, function (err, invoiceid) { + setTimeout(function () { - return callback(err, invoiceid); - }); + newpassword = pbkdf2(newpassword, m_this.m_oguid); - } - }); + $(progbar).width('80%'); + $(progmess).html('Encrypting account packet...'); - }); + var newpayloadsuccess = true; + var newpayload = ''; + var newusrpayload = ''; + var newveripacket = ''; + var newpasspacket = ''; + var newAIV = ''; + var newUIV = ''; + var newRIV = ''; + var newPIV = '' + try { - } + newpayload = encrypt(decryptedPayload, newpassword); + newusrpayload = encrypt(rsaKeyPair, newpassword); + newveripacket = encryptNp(veripacket, newpassword); - this.UnpackInvoiceByMe = UnpackInvoiceByMe; - function UnpackInvoiceByMe(invoice, username, callback) { + if (decryptedPayload.hckey) { + newpasspacket = encryptNp(newpassword, decryptedPayload.hckey); + } + newAIV = newpayload.iv.toString(); + newUIV = newusrpayload.iv.toString(); + newRIV = newveripacket.iv.toString(); - //here decrypt the invoice with my private key + if (decryptedPayload.hckey) { + newPIV = newpasspacket.iv.toString(); + } - API.getUserPacket(m_this.m_guid, m_this.m_sharedid, function (err, encpacket) { + } catch (err) { + newpayloadsuccess = false; + } - //get the RSA private key from the encrypted payload - var rsaKeyPair = decrypt(encpacket.Payload, m_this.m_password, encpacket.IV); + if (newpayloadsuccess) { - var publicKeys = openpgp.key.readArmored(rsaKeyPair.RSAPub); - var privKeys = openpgp.key.readArmored(rsaKeyPair.RSAPriv); + //$("#chngpwdprogbar").width('90%'); + //$("#chngpwdprogmess").html('Encrypting user packet...'); - var privKey = privKeys.keys[0]; - var pubKey = publicKeys.keys[0]; + //test decryption - then update + var testpayload = ''; + var testnewusrpayload = ''; + var testveripacket = ''; + var testpasspacket = ''; + var testsuccess = true; + try { - if (privKey.decrypt(m_this.m_sharedid)) { + testpayload = decrypt(newpayload.toString(), newpassword, newAIV); + testnewusrpayload = decrypt(newusrpayload.toString(), newpassword, newUIV); + testveripacket = decryptNp(newveripacket.toString(), newpassword, newRIV); - var msg = openpgp.message.readArmored(invoice.Packet); - var decrypted = openpgp.decryptAndVerifyMessage(privKey, [pubKey], msg); + if (decryptedPayload.hckey) { + testpasspacket = decryptNp(newpasspacket.toString(), decryptedPayload.hckey, newPIV) + } - var unpackedInvoice = JSON.parse(decrypted.text); + } catch (err) { + testsuccess = false; + } - callback(false, unpackedInvoice); + if (testsuccess) { - } else { + //save to the server + $(progbar).width('95%'); + $(progmess).html('Saving...'); - callback(true, "error"); - } + //TO DO: + //add in the re-encryption of the verification + //packet - }); - } + //if reset password then provide signed message and call reset function - this.UnpackInvoiceForMe = UnpackInvoiceForMe; - function UnpackInvoiceForMe(invoice, username, invtype, callback) { - //here decrypt the invoice with my private key - API.getUserPacket(m_this.m_guid, m_this.m_sharedid, function (err, encpacket) { + //need to add two factor here + //so 1. add two factor + //2. add way to save only the main packet - //get the RSA private key from the encrypted payload - var rsaKeyPair = decrypt(encpacket.Payload, m_this.m_password, encpacket.IV); + var postData = { twoFactorCode: twoFactorCode, guid: m_this.m_guid, sharedid: m_this.m_sharedid, accountPacket: newpayload.toString(), userPacket: newusrpayload.toString(), verifyPacket: newveripacket.toString(), passPacket: newpasspacket.toString(), IVA: newAIV, IVU: newUIV, IVR: newRIV, PIV: newPIV }; + API.post("/api/1/u/updatepackets", postData, function (err, dataStr) { + if (err) { + callback(true, "Error: Password not changed"); + } else { - var rsaKey = ''; - var postRSAData = { guid: m_this.m_guid, sharedid: m_this.m_sharedid, username: username }; - API.post("/api/1/u/getrsakey", postRSAData, function (err, rsaKey) { + if (dataStr == "ok") { - //friends public key + m_this.m_password = newpassword; + //if something goes wrong here + //the worst case scenario is the + //user has to reenter their hot key - //we need to get friends public key here to verify the signature on the packet - //then out of band if they verify the signature belongs to them- they are good + saveHotHash(hothash, function (err, result) { - //my private key - var privKeys = openpgp.key.readArmored(rsaKeyPair.RSAPriv); - var pubKey = null; - if (invtype == 'forme') { - var publicKeys = openpgp.key.readArmored(rsaKey); - pubKey = publicKeys.keys[0]; - } else { - var publicKeys = openpgp.key.readArmored(rsaKeyPair.RSAPub); - pubKey = publicKeys.keys[0]; - } + callback(false, ''); - var privKey = privKeys.keys[0]; + }); - var success = privKey.decrypt(m_this.m_sharedid); - if (success) { + } else { - var msg = openpgp.message.readArmored(invoice.Packet); - var decrypted = openpgp.decryptAndVerifyMessage(privKey, [pubKey], msg); + callback(true, "Error: Password not changed"); + } - var isValid = decrypted.signatures[0].valid; - if (isValid) { + } + }); - var json = JSON.parse(decrypted.text); - callback(false, json); - } else { + } else { - callback(false, "error"); + callback(true, "Error: Password not changed"); - } + } + } else { - } + callback(true, "Error: Password not changed"); - }); + } + }, 500); - }); + } else { - } + callback(true, "Error: Password not changed"); + } - //security - this.SaveTwoFactor = SaveTwoFactor; - function SaveTwoFactor(twoFactorCode, verifyToken, callback) { + }); - var postData = { - guid: m_this.m_guid, - sharedid: m_this.m_sharedid, - twoFactorOnLogin: true, - twoFactorCode: twoFactorCode, - verifyToken: verifyToken - }; + } - API.post("/api/1/u/updatetwofactor", postData, function (err, result) { + }); - if (!err) { - var bytes = []; - for (var i = 0; i < m_this.m_sharedid.length; ++i) { - bytes.push(m_this.m_sharedid.charCodeAt(i)); - } + } else { - var dpacket = Bitcoin.Crypto.SHA256(Bitcoin.convert.bytesToWordArray(bytes)).toString(); + } - API.post("/api/1/verifyrecoverpacket", { guid: m_this.m_oguid, token: dpacket }, function (err, response) { - callback(err, response); + }, 500); }); } else { - callback(true, result); + callback(true, wallet); } }); @@ -1628,44 +2761,28 @@ function Engine() { } - this.SetupTwoFactor = SaveTwoFactor; - function SetupTwoFactor(twoFactorCode, callback) { - - var postData = { - guid: m_this.m_guid, - sharedid: m_this.m_sharedid, - twoFactorOnLogin: true, - twoFactorCode: twoFactorCode, - verifyToken: '' - }; - - API.post("/api/1/u/updatetwofactor", postData, function (err, result) { - - callback(err, result); - - }); - - } + //////////////////////////////////////////////////////////////////////////////////////////// - this.ResetTwoFactor = ResetTwoFactor; - function ResetTwoFactor(fguid, fusername, fpwd, callback) { - //stretch password + this.ResetPassword = ResetPassword; + function ResetPassword(guid, twofactor, resetKey, newpassword, progbar, progmess, hckey, message1, message2, callback) { - //download the recovery packet - //decrypt - //return shared secret - //no feedback apart from, please check your email + var sharedid = ''; + var oguid = guid; + //get secret and decrypt with resetKey + //pass is message1 and message2 + //return secret and sharedid + //continue as normal + var bytes = []; + for (var i = 0; i < guid.length; ++i) { + bytes.push(guid.charCodeAt(i)); + } - fpwd = CryptoJS.PBKDF2(fpwd, fguid, { - keySize: 256 / 32, - iterations: 1000 - }).toString(); + guid = Bitcoin.Crypto.SHA256(Bitcoin.convert.bytesToWordArray(bytes)).toString(); API.post("/api/1/getrecoverypacket", { - guid: fguid, - username: fusername + guid: guid }, function (err, response) { if (err) { @@ -1678,239 +2795,242 @@ function Engine() { var jpacket = JSON.parse(response); - try { - var dpacket = decrypt(jpacket.packet, fpwd, jpacket.IV); - - API.post("/api/1/verifyrecoverpacket", { guid: fguid, token: dpacket }, function (err, response) { + if (!jpacket.Beta1) { - callback(err, response); + var secret = decryptNp(jpacket.packet, resetKey, jpacket.IV); - }); - } catch (error) { + API.getWalletFromServer(guid, secret, twofactor, false, function (err, wallet) { - callback(true, "error"); + if (!err) { - } + try { + var walletInformation = decrypt(wallet.Payload, resetKey, wallet.IV); + } catch (err) { + return callback(true, "Incorrect password"); + } - } + sharedid = walletInformation.userToken; - fpwd = ''; - }); + //check password strength - } + //stretch old password + //verify that it matches the current one + $(progbar).width('40%'); + $(progmess).html('Securing password...'); + setTimeout(function () { + //if password reset do not pbkdf the password - this.ChangePassword = ChangePassword; - function ChangePassword(twoFactorCode, oldpassword, newpassword, progbar, progmess, callback) { + oldpassword = resetKey; + //if (oldpassword == m_this.m_password) { - API.getWalletFromServer(m_this.m_guid, twoFactorCode, function (err, wallet) { + //get the two packets + $(progbar).width('40%'); + $(progmess).html('Getting packets...'); - if (!err) { - //check password strength - //stretch old password - //verify that it matches the current one - $(progbar).width('40%'); - $(progmess).html('Securing password...'); - setTimeout(function () { + // $("#chngpwdprogbar").width('50%'); + //$("#chngpwdprogmess").html('Decrypting account packet...'); + //decrypt with the old password + var decryptedWithOld = true; + var decryptedPayload = ''; + try { + decryptedPayload = decrypt(wallet.Payload, resetKey, wallet.IV); + } catch (err) { + decryptedWithOld = false; + } - oldpassword = CryptoJS.PBKDF2(oldpassword, m_this.m_oguid, { - keySize: 256 / 32, - iterations: 1000 - }).toString(); + if (decryptedWithOld) { - if (oldpassword == m_this.m_password) { + $(progbar).width('80%'); + $(progmess).html('Securing new password...'); + API.getUserPacket(guid, sharedid, function (err, encpacket) { + var decryptedUsrWithOld = true; + var rsaKeyPair = ''; + try { + rsaKeyPair = decrypt(encpacket.Payload, resetKey, encpacket.IV); + } catch (err) { + decryptedUsrWithOld = false; + } - //get the two packets - $(progbar).width('40%'); - $(progmess).html('Getting packets...'); + if (decryptedUsrWithOld) { + //get the verification packet - // $("#chngpwdprogbar").width('50%'); - //$("#chngpwdprogmess").html('Decrypting account packet...'); - //decrypt with the old password - var decryptedWithOld = true; - var decryptedPayload = ''; - try { - decryptedPayload = decrypt(wallet.Payload, m_this.m_password, wallet.IV); - } catch (err) { - decryptedWithOld = false; - } + API.post("/api/1/getrecoverypacket", { + guid: guid + }, function (err, response) { - if (decryptedWithOld) { - $(progbar).width('80%'); - $(progmess).html('Securing new password...'); - API.getUserPacket(m_this.m_guid, m_this.m_sharedid, function (err, encpacket) { + //decrypt packet + var decryptedVerWithOld = true; + var jpacket = JSON.parse(response); + var veripacket = ''; + try { + veripacket = decryptNp(jpacket.packet, resetKey, jpacket.IV); + } + catch (verror) { + decryptedVerWithOld = false; + } - var decryptedUsrWithOld = true; - var rsaKeyPair = ''; - try { - rsaKeyPair = decrypt(encpacket.Payload, m_this.m_password, encpacket.IV); - } catch (err) { - decryptedUsrWithOld = false; - } + if (decryptedVerWithOld) { - if (decryptedUsrWithOld) { + setTimeout(function () { - //get the verification packet + newpassword = pbkdf2(newpassword, oguid); - API.post("/api/1/getrecoverypacket", { - guid: m_this.m_guid, - username: m_this.m_nickname - }, function (err, response) { + $(progbar).width('80%'); + $(progmess).html('Encrypting account packet...'); - //decrypt packet - var decryptedVerWithOld = true; - var jpacket = JSON.parse(response); - var veripacket = ''; - try { - veripacket = decrypt(jpacket.packet, m_this.m_password, jpacket.IV); - } - catch (verror) { - decryptedVerWithOld = false; - } + var newpayloadsuccess = true; + var newpayload = ''; + var newusrpayload = ''; + var newveripacket = ''; + var newpasspacket = ''; + var newAIV = ''; + var newUIV = ''; + var newRIV = ''; + var newPIV = '' + try { + newpayload = encrypt(decryptedPayload, newpassword); + newusrpayload = encrypt(rsaKeyPair, newpassword); + newveripacket = encryptNp(veripacket, newpassword); + newpasspacket = encryptNp(newpassword, decryptedPayload.hckey); - if (decryptedVerWithOld) { + //and encrypt the new password with the hotkey seed - setTimeout(function () { + newAIV = newpayload.iv.toString(); + newUIV = newusrpayload.iv.toString(); + newRIV = newveripacket.iv.toString(); + newPIV = newpasspacket.iv.toString(); + } catch (err) { + newpayloadsuccess = false; + } - newpassword = CryptoJS.PBKDF2(newpassword, m_this.m_oguid, { - keySize: 256 / 32, - iterations: 1000 - }).toString(); + if (newpayloadsuccess) { + //$("#chngpwdprogbar").width('90%'); + //$("#chngpwdprogmess").html('Encrypting user packet...'); - $(progbar).width('80%'); - $(progmess).html('Encrypting account packet...'); + //test decryption - then update + var testpayload = ''; + var testnewusrpayload = ''; + var testveripacket = ''; + var testpasspacket = ''; + var testsuccess = true; + try { + testpayload = decrypt(newpayload.toString(), newpassword, newAIV); + testnewusrpayload = decrypt(newusrpayload.toString(), newpassword, newUIV); + testveripacket = decryptNp(newveripacket.toString(), newpassword, newRIV); + testpasspacket = decryptNp(newpasspacket.toString(), decryptedPayload.hckey, newPIV); - var newpayloadsuccess = true; - var newpayload = ''; - var newusrpayload = ''; - var newveripacket = ''; - var newAIV = ''; - var newUIV = ''; - var newRIV = ''; - try { + } catch (err) { + testsuccess = false; + } - newpayload = encrypt(decryptedPayload, newpassword); - newusrpayload = encrypt(rsaKeyPair, newpassword); - newveripacket = encrypt(veripacket, newpassword); + if (testsuccess) { - newAIV = newpayload.iv.toString(); - newUIV = newusrpayload.iv.toString(); - newRIV = newveripacket.iv.toString(); + //save to the server + $(progbar).width('95%'); + $(progmess).html('Saving...'); - } catch (err) { - newpayloadsuccess = false; - } - if (newpayloadsuccess) { + //TO DO: + //add in the re-encryption of the verification + //packet - //$("#chngpwdprogbar").width('90%'); - //$("#chngpwdprogmess").html('Encrypting user packet...'); - //test decryption - then update - var testpayload = ''; - var testnewusrpayload = ''; - var testveripacket = ''; - var testsuccess = true; - try { - testpayload = decrypt(newpayload.toString(), newpassword, newAIV); - testnewusrpayload = decrypt(newusrpayload.toString(), newpassword, newUIV); - testveripacket = decrypt(newveripacket.toString(), newpassword, newRIV); - } catch (err) { - testsuccess = false; - } + //if reset password then provide signed message and call reset function - if (testsuccess) { + var postData = { guid: guid, sharedid: sharedid, accountPacket: newpayload.toString(), userPacket: newusrpayload.toString(), verifyPacket: newveripacket.toString(), passPacket: newpasspacket.toString(), IVA: newAIV, IVU: newUIV, IVR: newRIV, PIV: newPIV }; + API.post("/api/1/u/updatepackets", postData, function (err, dataStr) { + if (err) { + callback(true, "Error: Password not changed"); + } else { - //save to the server - $(progbar).width('95%'); - $(progmess).html('Saving...'); + if (dataStr == "ok") { - //TO DO: - //add in the re-encryption of the verification - //packet + callback(false, newpassword); - var postData = { guid: m_this.m_guid, sharedid: m_this.m_sharedid, accountPacket: newpayload.toString(), userPacket: newusrpayload.toString(), verifyPacket: newveripacket.toString(), IVA: newAIV, IVU: newUIV, IVR: newRIV }; - API.post("/api/1/u/updatepackets", postData, function (err, dataStr) { - if (err) { - callback(true, "Error: Password not changed"); - } else { + } else { - if (dataStr == "ok") { + callback(true, "Error: Password not changed"); + } - callback(false, newpassword); + } + }); - } else { - callback(true, "Error: Password not changed"); - } + } else { + + callback(true, "Error: Password not changed"); } - }); + } else { - } else { + callback(true, "Error: Password not changed"); - callback(true, "Error: Password not changed"); + } + }, 500); - } } else { callback(true, "Error: Password not changed"); - } - }, 500); - } else { - callback(true, "Error: Password not changed"); - } + }); + } }); + + } else { + } - }); + //} else { + // callback(true, "You entered your current password incorrectly"); + //} + }, 500); } else { - + callback(true, wallet); } + }); - } else { - callback(true, "You entered your current password incorrectly"); - } - - }, 500); + } - } else { - callback(true, wallet); } + }); } + + + //////////////////////////////////////////////////////////////////////////////////////////// + this.EmailValidationForTwoFactor = EmailValidationForTwoFactor; function EmailValidationForTwoFactor(vtoken, status, callback) { @@ -1951,7 +3071,21 @@ function Engine() { API.post("/api/1/u/updateaccountsettings", postdata , function (err, response) { - callback(err, response); + + if (!err) { + + getAccountSettings(function (err, res) { + + var settingsObject = JSON.parse(res); + m_this.m_settings = settingsObject; + callback(err, response); + }); + + } else { + callback(err, response); + } + + }); } @@ -1974,6 +3108,54 @@ function Engine() { } + + this.getBackup = getBackup; + function getBackup(twoFactorCode, callback) { + + API.getWalletFromServer(m_this.m_guid, m_this.m_secret, twoFactorCode, false, function (err, wallet) { + + if (err) { + + return callback(err, wallet); + + } + + var walletInformation = {}; + try { + walletInformation = decrypt(wallet.Payload, m_this.m_password, wallet.IV); + + var result = {}; + result.ninkiPubKey = walletInformation.ninkiPubKey; + + getHotHash("", function (err, hotHash) { + + + result.hotHash = hotHash; + + walletInformation = {}; + + callback(err, result); + + }); + + + } catch (err) { + return callback(true, "Incorrect password"); + } + + }); + + } + + this.createS3Policy = createS3Policy; + function createS3Policy(callback) { + Ninki.API.post("/api/1/u/createS3Policy", { guid: m_this.m_guid }, function (err, result) { + + callback(err, result); + + }); + } + this.emailGUID = emailGUID; function emailGUID(userName, callback) { API.emailGUID(userName, callback); @@ -1991,7 +3173,7 @@ function Engine() { if (err) { callback(err, accExists); } else { - callback(err, accExists.UserExists); + callback(err, accExists.UserExists); } }); @@ -2008,8 +3190,18 @@ function Engine() { } this.getWalletFromServer = getWalletFromServer; - function getWalletFromServer(twoFactorCode, callback) { - API.getWalletFromServer(m_this.m_guid, m_this.m_sharedid, twoFactorCode, callback); + function getWalletFromServer(secret, twoFactorCode, rememberTwoFactor, callback) { + API.getWalletFromServer(m_this.m_guid, secret, twoFactorCode, rememberTwoFactor, callback); + } + + this.getRecoveryPacket = getRecoveryPacket; + function getRecoveryPacket(callback) { + API.getRecoveryPacket(m_this.m_guid, callback); + } + + this.validateSecret = validateSecret; + function validateSecret(secret, callback) { + API.validateSecret(m_this.m_guid, secret, callback); } this.getBalance = getBalance; @@ -2032,11 +3224,37 @@ function Engine() { API.getUnconfirmedBalance(m_this.m_guid, m_this.m_sharedid, callback); } + this.getCoinProfile = getCoinProfile; + function getCoinProfile(callback) { + API.getCoinProfile(m_this.m_guid, m_this.m_sharedid, callback); + } + + this.getNickname = getNickname; function getNickname(callback) { API.getNickname(m_this.m_guid, m_this.m_sharedid, callback); } + this.getUserProfile = getUserProfile; + function getUserProfile(callback) { + API.getUserProfile(m_this.m_guid, m_this.m_sharedid, callback); + } + + this.updateUserProfile = updateUserProfile; + function updateUserProfile(profileImage, status, tax, callback) { + API.updateUserProfile(m_this.m_guid, m_this.m_sharedid, profileImage, status, tax, function (err, result) { + + if (!err) { + m_this.m_statusText = status; + m_this.m_profileImage = profileImage; + m_this.m_invoiceTax = tax; + } + + callback(err, result); + + }); + } + this.getUnspentOutputs = getUnspentOutputs; function getUnspentOutputs(callback) { API.getUnspentOutputs(m_this.m_guid, m_this.m_sharedid, callback); @@ -2087,6 +3305,101 @@ function Engine() { API.updateInvoice(m_this.m_guid, m_this.m_sharedid, username, invoiceId, transactionId, status, callback); } + this.getVersion = getVersion; + function getVersion(callback) { + API.getVersion(callback); + } + + this.registerDevice = registerDevice; + function registerDevice(guid, deviceName, deviceId, deviceModel, devicePIN, regToken, secret, callback) { + API.registerDevice(guid, deviceName, deviceId, deviceModel, devicePIN, regToken, secret, callback); + } + + this.getDeviceKey = getDeviceKey; + function getDeviceKey(devicePIN, callback) { + + var deviceid = "DEVICE123456789"; + if (window.cordova) { + deviceid = window.device.uuid; + } + + //hash the pin and device id + var pinhash = deviceid + devicePIN; + var bytes = []; + for (var i = 0; i < pinhash.length; ++i) { + bytes.push(pinhash.charCodeAt(i)); + } + + pinhash = Bitcoin.Crypto.SHA256(Bitcoin.convert.bytesToWordArray(bytes)).toString(); + + getCookie("ninki_reg", function (regToken) { + + API.getDeviceKey(m_this.m_guid, pinhash, regToken, function (err, ekey) { + + if (!err) { + var jekey = JSON.parse(ekey); + + if (jekey.DeviceKey.length > 0) { + + callback(err, jekey); + + } else { + + callback(err, jekey); + } + } else { + callback(true, ekey); + } + + }); + + }); + + } + + + this.destroyDevice = destroyDevice; + function destroyDevice(callback) { + + getCookie("ninki_reg", function (regToken) { + + API.destroyDevice(m_this.m_guid, regToken, function (err, ekey) { + + callback(err, ekey); + + }); + + }); + + } + + + this.destroyDevice2fa = destroyDevice2fa; + function destroyDevice2fa(deviceName, twoFactor, callback) { + + API.destroyDevice2fa(m_this.m_guid, m_this.m_sharedid, deviceName, twoFactor, function (err, ekey) { + + callback(err, ekey); + + }); + + } + + + this.createDevice = createDevice; + function createDevice(deviceName, callback) { + API.createDevice(m_this.m_guid, m_this.m_sharedid, deviceName, callback); + } + + this.getDevices = getDevices; + function getDevices(callback) { + API.getDevices(m_this.m_guid, m_this.m_sharedid, callback); + } + + this.getDeviceToken = getDeviceToken; + function getDeviceToken(deviceName, twoFactorCode, callback) { + API.getDeviceToken(m_this.m_guid, m_this.m_sharedid, deviceName, twoFactorCode, callback); + } } module.exports = Engine \ No newline at end of file diff --git a/src/ninki-ui-mobile.js b/src/ninki-ui-mobile.js new file mode 100644 index 0000000..55f5fd2 --- /dev/null +++ b/src/ninki-ui-mobile.js @@ -0,0 +1,5419 @@ +var Bitcoin = require('bitcoinjs-lib'); +var BIP39 = require('./bip39'); + +function UI() { + + + + + var Engine = new Ninki.Engine(); + + + // var WALLETINFORMATION = {}; + // var SHAREDID = ''; + + // var TWOFACTORONLOGIN = false; + // var NICKNAME = ''; + // var guid = ''; + // var oguid = ''; + // var password = ''; + + + var FRIENDSLIST = {}; + var COINUNIT = 'BTC'; + var SELECTEDFRIEND = ''; + var noAlert = false; + var norefresh = false; + + var trasactionFilterOn = false; + var allTransactions = []; + var filteredTransactions = []; + var pagedTransactions = []; + var currentTransactionFilter = ''; + var transactionSortOn = true; + var currentTransactionSort = 'DateDesc'; + var transactionsPerPage = 10; + var currentPageIndex = 0; + var transactionIndex = {}; + + var filteredByMeInvoices = []; + var pagedByMeInvoices = []; + var invoicesByMePerPage = 10; + var currentByMeInvoicePageIndex = 0; + + var filteredForMeInvoices = []; + var pagedForMeInvoices = []; + var invoicesForMePerPage = 10; + var currentForMeInvoicePageIndex = 0; + + var currentInvoiceFilter = ''; + var currentByMeInvoiceFilter = ''; + var invoiceFilterOn = false; + var invoiceByMeFilterOn = false; + + var contactPhraseCache = {}; + + + var ua = window.navigator.userAgent; + var msie = ua.indexOf("MSIE"); + + + + if (msie > 0 || !!navigator.userAgent.match(/Trident.*rv\:11\./)) { + document.write("Internet Explorer is not supported. Please use the latest Chrome, Safari or Firefox."); + + } + + if (typeof window.crypto.getRandomValues == 'undefined') { + document.write("This browser does not support window.crypto. Please use the latest chrome, safari or firefox."); + } + + var spinneropts = { + lines: 13, // The number of lines to draw + length: 0, // The length of each line + width: 7, // The line thickness + radius: 30, // The radius of the inner circle + corners: 1, // Corner roundness (0..1) + rotate: 0, // The rotation offset + direction: 1, // 1: clockwise, -1: counterclockwise + color: '#000', // #rgb or #rrggbb or array of colors + speed: 1, // Rounds per second + trail: 60, // Afterglow percentage + shadow: false, // Whether to render a shadow + hwaccel: false, // Whether to use hardware acceleration + className: 'spinner', // The CSS class to assign to the spinner + zIndex: 2e9, // The z-index (defaults to 2000000000) + top: '50%', // Top position relative to parent + left: '50%' // Left position relative to parent + }; + + + + function setCookie(cname, cvalue) { + + + if (isChromeApp()) { + + var obj = {}; + obj[cname] = cvalue; + chrome.storage.local.set(obj, function () { + + console.log("saved"); + + }); + + } + else { + + localStorage[cname] = cvalue; + + } + + + } + + function isChromeApp() { + + if (window.cordova) { + return false; + } + + + var is_chrome = navigator.userAgent.toLowerCase().indexOf('chrome') > -1; + if (is_chrome) { + if (chrome) { + if (chrome.app) { + if (chrome.app.runtime) { + return true; + } + } + } + } + return false; + } + + + function getCookie(cname, callback) { + + + if (isChromeApp()) { + + chrome.storage.local.get(cname, function (result) { + + if (result[cname]) { + result = result[cname]; + } else { + result = ""; + } + + return callback(result); + + }); + + } else { + + if (localStorage[cname]) { + return callback(localStorage[cname]); + } else { + return callback(''); + } + + } + + } + + function deleteCookie(cname) { + + + if (isChromeApp()) { + + chrome.storage.local.remove(cname, function () { + + console.log("deleted"); + + }); + + } else { + + localStorage.removeItem(cname); + + } + } + + + function getLocalTime(datetime) { + + var timestamp = datetime, + t = new Date(datetime), + hours = t.getHours(), + min = t.getMinutes() + '', + pm = false, + months = ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December']; + + if (hours > 11) { + hours = hours - 12; + pm = true; + } + + if (hours == 0) hours = 12; + if (min.length == 1) min = '0' + min; + + return (hours + ':' + min + ' ' + (pm ? 'pm' : 'am')); + + } + + + var profilepagestate = ''; + var networkpagestate = ''; + var friendpagestate = ''; + var menustate = ''; + var cl = ''; + + jQuery(document).ready(function () { + + + var $body = jQuery('body'); + + /* bind events */ + $(document).on('focus', 'input', function (e) { + $(".footer").hide(); + }); + + + $(document).on('blur', 'input', function (e) { + $(".footer").show(); + }); + + $("#dashsend").hide(); + $("#dashreceive").hide(); + $("#dashcontact").hide(); + + + $("#addcontactmodal").hide(); + + + + $('.num').bind('touchstart', function () { + $(this).removeClass('numtapoff'); + + cl = 'b' + (Math.floor(Math.random() * 6) + 1) + ''; + //alert(cl); + $(this).addClass(cl); + + }) + + $('.num').bind('touchend', function () { + + var num = $(this); + var text = $.trim(num.find('.txt').clone().children().remove().end().text()); + var loginpinno = $('#loginpinno'); + + $(loginpinno).val(loginpinno.val() + text); + + if ($(loginpinno).val().length == 4) { + + $("#pinscreen").hide(); + + $("#btnLoginPIN").click(); + + + } + + + + var self = this; + setTimeout(function () { + //alert(cl); + $(self).removeClass('b1 b2 b3 b4 b5 b6'); + $(self).addClass('numtapoff'); + }, 100); + + }) + + + + + + // $(".num").hammer({ + // drag: false, + // transform: false, + // hold: false, + // touch: false, + // tap: true, + // tapAlways: false, + // swipe: false, + // release: false, + // tapMaxTime: 1000 + // }).bind("tap", function (ev) { + + // $(this).addClass('numtap'); + + // // var num = $(this); + // // var text = $.trim(num.find('.txt').clone().children().remove().end().text()); + // // var telNumber = $('#telNumber'); + // // $(telNumber).val(telNumber.val() + text); + // }); + + // $(".num").click(function () { + + // $(this).addClass('numtap'); + + // // var num = $(this); + // // var text = $.trim(num.find('.txt').clone().children().remove().end().text()); + // // var telNumber = $('#telNumber'); + // // $(telNumber).val(telNumber.val() + text); + // }); + + + $("#btnmenuprofile").hammer({ + drag: false, + transform: false, + hold: false, + touch: false, + tap: true, + tapAlways: false, + swipe: false, + release: false, + tapMaxTime: 1000 + }).bind("tap", function (ev) { + + displayProfile(); + + + }); + + $("#btnmenuprofile").hammer({ + drag: false, + transform: false, + hold: false, + touch: true, + tap: false, + tapAlways: false, + swipe: false, + release: false + }).bind("touch", function (ev) { + + displayProfile(); + + + }); + + + $("#btnmenuprofile").hammer({ + drag: false, + transform: false, + hold: false, + touch: false, + tap: false, + tapAlways: false, + swipe: false, + release: true + }).bind("release", function (ev) { + + displayProfile(); + + + }); + + $("#btnmenuprofile").hammer({ + drag: false, + transform: false, + hold: true, + touch: false, + tap: false, + tapAlways: false, + swipe: false, + release: false + }).bind("hold", function (ev) { + + displayProfile(); + + + }); + + function displayProfile() { + + + if (menustate != 'profile') { + menustate = 'profile'; + $("#settings").hide(); + $("#network").hide(); + $("#dashboard").show(); + $("#networklist").hide(); + $("#invoices").hide(); + + } else { + profilehome(); + } + + $("#btnmenusettings").attr('style', 'background-color:#ffffff'); + $("#btnmenunetwork").attr('style', 'background-color:#ffffff'); + $("#btnmenuprofile").attr('style', 'background-color:#eaeef1'); + + } + + + $("#btnmenunetwork").hammer({ + drag: false, + transform: false, + hold: false, + touch: false, + tap: true, + tapAlways: false, + swipe: false, + release: false, + tapMaxTime: 1000 + }).bind("tap", function (ev) { + + displayNetwork(); + + }); + + $("#btnmenunetwork").hammer({ + drag: false, + transform: false, + hold: false, + touch: true, + tap: false, + tapAlways: false, + swipe: false, + release: false + }).bind("touch", function (ev) { + + displayNetwork(); + + }); + + $("#btnmenunetwork").hammer({ + drag: false, + transform: false, + hold: true, + touch: false, + tap: false, + tapAlways: false, + swipe: false, + release: true + }).bind("release", function (ev) { + + displayNetwork(); + + }); + + + $("#btnmenunetwork").hammer({ + drag: false, + transform: false, + hold: true, + touch: false, + tap: false, + tapAlways: false, + swipe: false, + release: false + }).bind("hold", function (ev) { + + displayNetwork(); + + }); + + function displayNetwork() { + + if (menustate != "network") { + menustate = "network"; + $("#settings").hide(); + $("#network").show(); + $("#dashboard").hide(); + $("#networklist").show(); + + + if (networkpagestate == "invoice") { + + + $("#network").hide(); + + $("#invoices").show(); + } + + } else { + + networkhome(); + } + + $("#btnmenusettings").attr('style', 'background-color:#ffffff'); + $("#btnmenuprofile").attr('style', 'background-color:#ffffff'); + $("#btnmenunetwork").attr('style', 'background-color:#eaeef1'); + } + + + $("#invformelink").hammer(null).bind("tap", function () { + $("#invformetab").show(); + $("#invbymetab").hide(); + $("#liby").removeClass('active'); + $("#lifor").addClass('active'); + }); + + $("#invbymelink").hammer(null).bind("tap", function () { + $("#invbymetab").show(); + $("#invformetab").hide(); + $("#lifor").removeClass('active'); + $("#liby").addClass('active'); + }); + + //tapsendfriend + //tapinvoicefriend + $("#tapsendfriend").hammer(null).bind("tap", function () { + $("#networksend").show(); + $("#pnlfriendinv").hide(); + networkpagestate = "friend"; + friendpagestate = "send"; + }); + + $("#tapinvoicefriend").hammer(null).bind("tap", function () { + $("#pnlfriendinv").show(); + $("#networksend").hide(); + networkpagestate = "friend"; + friendpagestate = "invoice"; + + }); + + function networkhome() { + if (networkpagestate == "invoice") { + $('#network').show(); + $('#invoices').hide(); + networkpagestate = "friend"; + friendpagestate = "invoice"; + + + } else { + $("#pnlfriend").hide(); + $("#myfriends").show(); + $("#networklist").show(); + networkpagestate = ""; + } + + } + + function profilehome() { + + + $("#dashprofile").show(); + $("#dashsend").hide(); + $("#dashreceive").hide(); + $("#dashcontact").hide(); + $('#invoices').hide(); + $("#dashboard").show(); + profilepagestate = ""; + + } + + + $("#btnmenusettings").hammer({ + drag: false, + transform: false, + hold: false, + touch: false, + tap: true, + tapAlways: false, + swipe: false, + release: false, + tapMaxTime: 1000 + }).bind("tap", function (ev) { + + menustate = "settings"; + + $("#settings").show(); + $("#network").hide(); + $("#networklist").hide(); + $("#dashboard").hide(); + + $("#btnmenusettings").attr('style', 'background-color:#eaeef1'); + $("#btnmenuprofile").attr('style', 'background-color:#ffffff'); + $("#btnmenunetwork").attr('style', 'background-color:#ffffff'); + }); + + $("#tapsend").hammer(null).bind("tap", function () { + $("#dashprofile").hide(); + $("#dashsend").show(); + $("#dashreceive").hide(); + $("#dashcontact").hide(); + //$("#toAddress").focus(); + profilepagestate = "send"; + menustate = "profile" + + + }); + + $("#tapreceive").hammer(null).bind("tap", function () { + $("#dashprofile").hide(); + $("#dashsend").hide(); + $("#dashreceive").show(); + $("#dashcontact").hide(); + profilepagestate = "receive"; + menustate = "profile" + + }); + + $("#taprequest").hammer(null).bind("tap", function () { + $("#dashprofile").hide(); + $("#dashsend").hide(); + $("#dashreceive").hide(); + $("#dashcontact").show(); + profilepagestate = "contact"; + menustate = "profile" + + }); + + + $('#imgProfileContainer').show(); + $("#dropzone").hide(); + $("#btnSaveProfile").hide(); + $("#btnCancelProfile").hide(); + $("#statusedit").hide(); + $("#imgreset").hide(); + + + $("#btnEditProfile").click(function () { + + key = ''; + + $('#imgProfileContainer').hide(); + $("#dropzone").show(); + $("#btnSaveProfile").show(); + $("#btnCancelProfile").show(); + $("#btnEditProfile").hide(); + $("#statusedit").show(); + $("#profnmests").hide(); + $("#imgreset").show(); + $("#txtStatusText").val(Engine.m_statusText); + + }); + + $("#imgreset").click(function () { + + Engine.m_profileImage = ""; + imgReset = true; + + var imageSrc = "images/avatar/256px/Avatar-" + pad(Engine.m_nickname.length) + ".png"; + + document.getElementById('imgProfile').src = imageSrc; + $('#imgProfileContainer').show(); + $('#dropzone').hide(); + $('progressNumber').html(''); + $("#imgreset").hide(); + + }); + + var imgReset = false; + $("#btnSaveProfile").click(function () { + + //updateUserProfile + + var statusText = $("#txtStatusText").val(); + + if (key == '' && !imgReset) { + key = Engine.m_profileImage; + } + + Engine.updateUserProfile(key, statusText, Engine.m_invoiceTax, function (err, result) { + + if (!err) { + + var imageSrc = "images/avatar/256px/Avatar-" + pad(Engine.m_nickname.length) + ".png"; + var imageSrcSmall = "images/avatar/64px/Avatar-" + pad(Engine.m_nickname.length) + ".png"; + + if (key != '' && !imgReset) { + imageSrc = "https://ninkip2p.imgix.net/" + key + "?crop=faces&fit=crop&h=256&w=256&mask=ellipse&border=1,d0d0d0"; + imageSrcSmall = "https://ninkip2p.imgix.net/" + key + "?crop=faces&fit=crop&h=64&w=64&mask=ellipse&border=1,d0d0d0"; + } + + $("#imgProfile").attr("src", imageSrc); + $("#imgtoprightprofile").attr("src", imageSrcSmall); + + $('#imgProfileContainer').show(); + $("#dropzone").hide(); + $("#btnSaveProfile").hide(); + $("#btnCancelProfile").hide(); + $("#btnEditProfile").show(); + $("#statusedit").hide(); + $("#mystatus").html(statusText); + $("#txtStatusText").val(statusText); + $("#profnmests").show(); + $("#imgreset").hide(); + imgReset = false; + $("#profileimgfile").val(''); + $("#progressNumber").val(''); + + } + + }); + + }); + + $("#btnCancelProfile").click(function () { + + $('#imgProfileContainer').show(); + $("#dropzone").hide(); + $("#btnSaveProfile").hide(); + $("#btnCancelProfile").hide(); + $("#btnEditProfile").show(); + + //reset profile image + + var imageSrc = "images/avatar/256px/Avatar-" + pad(Engine.m_nickname.length) + ".png"; + + if (Engine.m_profileImage != '') { + imageSrc = "https://ninkip2p.imgix.net/" + Engine.m_profileImage + "?crop=faces&fit=crop&h=256&w=256&mask=ellipse&border=1,d0d0d0"; + } + + $("#imgProfile").attr("src", imageSrc); + + $("#statusedit").hide(); + $("#profnmests").show(); + $("#imgreset").hide(); + }); + + var obj = $("#dropzone"); + + obj.click(function () { + $("#profileimgfile").click(); + }); + + $("#profileimgfile").change(function (e) { + + var control = document.getElementById("profileimgfile"); + var files = control.files; + //alert(files[0]); + //We need to send dropped files to Server + handleFileUpload(files, obj); + + }); + + obj.on('dragenter', function (e) { + e.stopPropagation(); + e.preventDefault(); + $(this).css('border', '2px solid #0B85A1'); + }); + + obj.on('dragover', function (e) { + e.stopPropagation(); + e.preventDefault(); + }); + + obj.on('drop', function (e) { + + $(this).addClass("b-dashed"); + $(this).addClass("b-light"); + e.preventDefault(); + var files = e.originalEvent.dataTransfer.files; + //alert(files[0]); + //We need to send dropped files to Server + handleFileUpload(files, obj); + }); + + + $(document).on('dragenter', function (e) { + e.stopPropagation(); + e.preventDefault(); + }); + + $(document).on('dragover', function (e) { + e.stopPropagation(); + e.preventDefault(); + obj.css('border', '2px dotted #0B85A1'); + }); + + $(document).on('drop', function (e) { + e.stopPropagation(); + e.preventDefault(); + }); + + var key = ''; + function handleFileUpload(files, obj) { + + if (files.length > 0) { + var file = files[0]; + var fd = new FormData(); + + key = "images\/" + Engine.m_nickname + '_' + (new Date).getTime() + + Ninki.API.post("/api/1/u/createS3Policy", { test: 'test' }, function (err, result) { + + var policy = JSON.parse(result); + + fd.append('key', key); + fd.append('acl', 'public-read'); + fd.append('Content-Type', file.type); + fd.append('bucket', 'ninkip2pimgstore'); + fd.append('AWSAccessKeyId', 'AKIAINOU56ATQFS3CLFQ'); + fd.append('policy', policy.s3Policy); + fd.append('signature', policy.s3Signature); + fd.append("file", file); + //fd.append("success_action_redirect", "https://localhost:1111/ok"); + + + var xhr = new XMLHttpRequest(); + + xhr.upload.addEventListener("progress", uploadProgress, false); + xhr.addEventListener("load", uploadComplete, false); + xhr.addEventListener("error", uploadFailed, false); + xhr.addEventListener("abort", uploadCanceled, false); + + xhr.open('POST', 'https://ninkip2pimgstore.s3-us-west-1.amazonaws.com/', true); //MUST BE LAST LINE BEFORE YOU SEND + + xhr.send(fd); + + }); + } + + } + + + + function uploadProgress(evt) { + if (evt.lengthComputable) { + var percentComplete = Math.round(evt.loaded * 100 / evt.total); + document.getElementById('progressNumber').innerHTML = percentComplete.toString() + '%'; + } + else { + document.getElementById('progressNumber').innerHTML = 'unable to compute'; + } + } + + function uploadComplete(evt) { + /* This event is raised when the server send back a response */ + //alert("Done - " + evt.target.responseText); + document.getElementById('imgProfile').src = 'https://ninkip2p.imgix.net/' + key + "?fit=crop&crop=faces&h=128&w=128&mask=ellipse&border=1,d0d0d0"; + $('#imgProfileContainer').show(); + $('#dropzone').hide(); + $('progressNumber').html(''); + $("#imgreset").hide(); + + } + + function uploadFailed(evt) { + alert("There was an error attempting to upload the file." + evt); + } + + function uploadCanceled(evt) { + alert("The upload has been canceled by the user or the browser dropped the connection."); + } + + var options = {}; + options.ui = { + container: "#pwd-container", + showVerdictsInsideProgressBar: true, + showPopover: true, + showErrors: true, + viewports: { + progress: ".pwstrength_viewport_progress" + } + }; + options.common = { + debug: true, + onLoad: function () { + $('#messages').text('Start typing password'); + }, + onKeyUp: function () { + $("#createwalletalert").fadeOut(100); + } + }; + + + $('#createWalletStart #cpassword').pwstrength(options); + + + var optionschng = {}; + optionschng.ui = { + container: "#newpwd-container", + showVerdictsInsideProgressBar: true, + showPopover: true, + showErrors: true, + viewports: { + progress: ".newpwstrength_viewport_progress" + } + }; + optionschng.common = { + debug: true, + onLoad: function () { + $('#messages').text('Start typing password'); + }, + onKeyUp: function () { + $("#createwalletalert").fadeOut(100); + } + }; + + $('#newpassword1').pwstrength(optionschng); + + }); + + + + + $('#btnPassphraseLogin').keydown(function (e) { + if (e.keyCode == 13) { + e.preventDefault(); + return false; + } + }); + + $('#frmSaveTwoFactor').keydown(function (e) { + if (e.keyCode == 13) { + e.preventDefault(); + return false; + } + }); + + $('#phrase2fa').keydown(function (e) { + if (e.keyCode == 13) { + e.preventDefault(); + return false; + } + }); + + + $(document).on("keydown", function (e) { + if (e.which === 8 && !$(e.target).is("input, textarea")) { + e.preventDefault(); + } + }); + + + $(document).ready(function () { + + + $('body').on('click', function (e) { + //did not click a popover toggle or popover + if ($(e.target).data('toggle') !== 'popover' + && $(e.target).parents('.popover.in').length === 0) { + $('[data-toggle="popover"]').popover('hide'); + } + }); + + + + getCookie('guid', function (res) { + if (res.length == 0) { + + var betafrom = 'December 12, 2009 12:00 pm GMT'; + var betato = 'December 12, 2009 01:00 pm GMT'; + + betafrom = getLocalTime(betafrom); + betato = getLocalTime(betato); + + $('#betafrom').html(betafrom); + $('#betato').html(betato); + + $('#basicModal').modal('show'); + + $("#btnDeclineBeta").click(function () { + window.location.href = '/' + }); + + } + }); + + + + $("#btnAcceptBeta").click(function () { + $("#openWalletStart #password").focus(); + }); + + + + + + $("#btncreatewallet").click(function () { + showCreateWalletStart(); + }); + + + $("#pairdeviceblob").change(function () { + + $("#loginpin").hide(); + $("#pairstep1").hide(); + $("#pairstep2").show(); + + + }); + + + $("#btnUnpair").click(function () { + + + getCookie("guid", function (guid) { + + Engine.m_oguid = guid; + + var bytes = []; + for (var i = 0; i < guid.length; ++i) { + bytes.push(guid.charCodeAt(i)); + } + + Engine.m_guid = Bitcoin.Crypto.SHA256(Bitcoin.convert.bytesToWordArray(bytes)).toString(); + + + Engine.destroyDevice(function (err, res) { + + deleteCookie("ninki_rem"); + deleteCookie("ninki_p"); + deleteCookie("ninki_reg"); + deleteCookie("ninki_h"); + + //call to server + location.reload(); + + }); + + }); + + + }); + + + $("#btnPairDevice").click(function () { + + var deviceid = "DEVICE123456789"; + + if (window.cordova) { + deviceid = window.device.uuid; + } + + var blob = $('#pairdeviceblob').val(); + var pin = $('#pairdevicepinnumber').val(); + var pwd = $('#pairpwd').val(); + + + + var splitBlob = blob.split('|'); + + var enck = splitBlob[0]; + var iv = splitBlob[1]; + var guid = splitBlob[2]; + var deviceName = splitBlob[3]; + var regToken = splitBlob[4]; + + + Engine.setPass(pwd, guid); + + pwd = Engine.m_password; + + var bytes = []; + for (var i = 0; i < guid.length; ++i) { + bytes.push(guid.charCodeAt(i)); + } + + var hashguid = Bitcoin.Crypto.SHA256(Bitcoin.convert.bytesToWordArray(bytes)).toString(); + + + Engine.m_guid = hashguid; + Engine.m_oguid = guid; + + //first validate the password with the secret + Engine.getRecoveryPacket(function (err, response) { + + if (err) { + + $('#pairdevicealertmessage').html(response); + + } else { + + //decrypt packet + + var jpacket = JSON.parse(response); + + var secret = Engine.decryptNp(jpacket.packet, pwd, jpacket.IV); + + Engine.validateSecret(secret, function (err, secvalid) { + + if (!err) { + + + //hash the pin and device id + + var pinhash = deviceid + pin; + bytes = []; + for (var i = 0; i < pinhash.length; ++i) { + bytes.push(pinhash.charCodeAt(i)); + } + + pinhash = Bitcoin.Crypto.SHA256(Bitcoin.convert.bytesToWordArray(bytes)).toString(); + + + //new register device + + + //enter password + //stretch + //get validate + //if valid + //choose a PIN + //register + + + var devplatform = "platform"; + var devmodel = "model"; + + if (window.cordova) { + devplatform = window.device.platform; + devmodel = window.device.model; + } + + + Engine.registerDevice(hashguid, deviceName, devplatform, devmodel, pinhash, regToken, secret, function (err, result) { + + if (!err) { + + var dk = JSON.parse(result); + + if (dk.DeviceKey.length > 0) { + + var decblob = Engine.decryptNp(enck, dk.DeviceKey, iv); + + //slice it up + //64 64 64 + var hk = decblob.substring(0, 64); + var fatoken = decblob.substring(64, 128); + + var encp = Engine.encryptNp(pwd, dk.DeviceKey); + result = ''; + + var ptok = {}; + ptok.ct = encp.toString(); + ptok.iv = encp.iv.toString(); + var ptoken = JSON.stringify(ptok); + + + var enc = Engine.encryptNp(fatoken, dk.DeviceKey); + var ctok = {}; + ctok.ct = enc.toString(); + ctok.iv = enc.iv.toString(); + + var ctoken = JSON.stringify(ctok); + + + var ench = Engine.encryptNp(hk, dk.DeviceKey); + var htok = {}; + htok.ct = ench.toString(); + htok.iv = ench.iv.toString(); + + var hkey = JSON.stringify(htok); + + dk.DeviceKey = ''; + + //login using the credentials + //get a session + //then call register PIN + //this will return the encryption key + //encrypt the password and store in local storage + + setCookie("guid", guid); + + Engine.openWallet(guid, fatoken, function (err, result) { + + if (!err) { + + if (!result.TwoFactorOnLogin) { + + setCookie("ninki_rem", ctoken); + setCookie("ninki_p", ptoken); + setCookie("ninki_reg", regToken); + setCookie("ninki_h", hkey); + + $('#pairDevice').hide(); + + initialiseUI(); + + } else { + + + $('#pairdevicealertmessage').html("could not pair"); + $('#pairdevicealert').show(); + } + + + } else { + + $('#pairdevicealertmessage').html(result); + $('#pairdevicealert').show(); + } + + }); + } else { + + $('#pairdevicealertmessage').html("The pairing token has expired"); + $('#pairdevicealert').show(); + } + + + } else { + + $('#pairdevicealertmessage').html(result); + $('#pairdevicealert').show(); + + } + + }); + + } else { + + $('#pairdevicealertmessage').html(secvalid); + $('#pairdevicealert').show(); + + } + + }); + + } + + }); + + + }); + + $("#btnLoginPIN").click(function () { + + var pin = $("#loginpinno").val(); + + $("#enterpinalert").hide(); + + if (pin.length == 4) { + + getCookie("guid", function (guid) { + + Engine.m_oguid = guid; + + var bytes = []; + for (var i = 0; i < guid.length; ++i) { + bytes.push(guid.charCodeAt(i)); + } + + Engine.m_guid = Bitcoin.Crypto.SHA256(Bitcoin.convert.bytesToWordArray(bytes)).toString(); + + + Engine.getDeviceKey(pin, function (err, ekeyv) { + + //decrypt the passcode + + if (!err) { + + getCookie("ninki_p", function (result) { + + var enc = JSON.parse(result); + result = ''; + Engine.setStretchPass(Engine.decryptNp(enc.ct, ekeyv.DeviceKey, enc.iv)); + + getCookie("ninki_rem", function (res) { + + if (res.length > 0) { + var enc = JSON.parse(res); + var fatoken = Engine.decryptNp(enc.ct, ekeyv.DeviceKey, enc.iv); + + //get the two factor token + + //do we need to open wallet ? + if (!Engine.m_appInitialised) { + Engine.openWallet(guid, fatoken, function (err, result) { + + if (!err) { + + if (result.TwoFactorOnLogin) { + + $("#loginpinno").val(''); + $("#enterpinalert").show(); + $("#enterpinalertmessage").html('Token has expired'); + + } else { + + $("#loginpin").hide(); + $("#loginpinno").val(''); + + initialiseUI(); + Engine.m_appInitialised = true; + } + + } else { + + } + + }); + + } else { + + if (ekeyv.SessionToken) { + $("#API-Token").val(ekeyv.SessionToken); + } + //set new session key + + $("#loginpin").hide(); + $("#loginpinno").val(''); + + initialiseUI(); + + } + + } + + }); + + }); + + } else { + + + if (ekeyv == "ErrDeviceDestroyed") { + + deleteCookie("ninki_reg"); + deleteCookie("ninki_p"); + deleteCookie("ninki_rem"); + deleteCookie("guid"); + + location.reload(); + } + + $("#loginpinno").val(''); + $("#enterpinalert").show(); + $("#enterpinalertmessage").html(ekeyv); + + } + + }); + + }); + + } + + }); + + $("#btnLogin").click(function () { + + if (!ensureOpenWalletGuidAndPasswordValid()) return; + + $("#imgopenwaiting").show(); + $("#btnLogin").prop('disabled', true); + $("#btnLogin").addClass('disabled'); + var target = document.getElementById('imgopenwaiting'); + var spinner = new Spinner(spinneropts).spin(target); + + + setTimeout(function () { + + var guid = $('#openWalletStart input#guid').val(); + var password = $('#openWalletStart input#password').val(); + var twoFactorCode = $('#openWalletStart input#twoFactorCode').val(); + + //decrypt the password using the key sent back from the server + + + Engine.setPass(password, guid); + + getCookie("ninki_rem", function (res) { + + if (res.length > 0) { + var enc = JSON.parse(res); + twoFactorCode = Engine.decryptNp(enc.ct, Engine.m_password, enc.iv); + } + + setCookie('guid', guid); + + Engine.openWallet(guid, twoFactorCode, function (err, result) { + + $("#imgopenwaiting").hide(); + $("#btnLogin").prop('disabled', false); + $("#btnLogin").removeClass('disabled'); + + if (!err) { + + $("#imgopenwaiting").hide(); + $("#openwalletalert").hide(); + + if (result.TwoFactorOnLogin) { + + //delete any old 2 factor tokens + + deleteCookie("ninki_rem"); + + if (!result.Beta12fa) { + + $('#openWalletStart input#password').val(''); + + if (result.TwoFactorOnLogin) { + $("#siguid").hide(); + $("#silguid").hide(); + $("#sipwd").hide(); + $("#si2fa").show(); + $("#sib1").hide(); + $("#sib2").show(); + $('#openWalletStart input#twoFactorCode').focus(); + + } + } else { + + //for beta1 migrations + $("#siguid").hide(); + $("#silguid").hide(); + $("#sipwd").hide(); + $("#si2fa").show(); + $("#sib1").hide(); + $("#sib2").show(); + $('#openWalletStart input#twoFactorCode').focus(); + + } + + } else { + + + + //initiate 2fa setup modal + if (!m_this.m_twoFactorOnLogin) { + $("#twofactorsettings").show(); + $("#2famodal").modal('show'); + + $("#twofactorsettings").show(); + $("#btnSetupTwoFactor").hide(); + $("#savetwofactorerror").hide(); + $("#setup2faemail").hide(); + $("#setup2faqr").show(); + + showSettingsTwoFactorQr(); + + } else { + + initialiseUI(); + + } + + } + + $("#unlockaccount").hide(); + + } else { + $("#imgopenwaiting").hide(); + $("#openwalletalert").show(); + + if (result == "ErrAccount") { + $("#openwalletalertmessage").html("Incorrect password"); + } + + if (result == "ErrLocked") { + $("#openwalletalertmessage").html("Your account has been locked."); + $("#unlockaccount").show(); + } else { + $("#unlockaccount").hide(); + } + } + + }); + + }); + }, 100); + }); + + + $("#btn2faLogin").click(function () { + + + $("#img2faopenwaiting").show(); + $("#btn2faLogin").prop('disabled', true); + $("#btn2faLogin").addClass('disabled'); + var target = document.getElementById('img2faopenwaiting'); + var spinner = new Spinner(spinneropts).spin(target); + + + setTimeout(function () { + + var twoFactorCode = $('#openWalletStart input#twoFactorCode').val(); + var rememberTwoFactor = $('#twofactorremember')[0].checked; + + Engine.openWallet2fa(twoFactorCode, rememberTwoFactor, function (err, result) { + + if (err) { + + $("#img2faopenwaiting").hide(); + $("#openwalletalert").show(); + $("#openwalletalertmessage").html(result); + $("#btn2faLogin").prop('disabled', false); + $("#btn2faLogin").removeClass('disabled'); + } else { + + if (result.CookieToken) { + + setCookie("ninki_rem", result.CookieToken); + } + + initialiseUI(); + } + }); + + }, 100); + + }); + + + + $("#btnEmailGuid").click(function () { + + var userName = $("#txtlostguidusername").val(); + + Engine.emailGUID(userName, function (err, response) { + + showOpenWalletStart(); + }); + + }); + + $("#btnaddfriend").click(function () { + + addFriend($('input#friend').val()); + + }); + + $("#btngenaddr").click(function () { + + generateAddressClient(); + + }); + + + $("#showopenwallet").click(function () { + + showOpenWalletStart(); + + }); + + $("#btnopenwalletrec").click(function () { + + showOpenWalletStart(); + + }); + + + $("#showopenwalletcr").click(function () { + + showOpenWalletStart(); + + }); + + + + $("#btnsendmoneystd").hammer(null).bind("tap", function () { + + sendMoneyStd(); + + }); + + + $("#btnCreate").click(function () { + + if ($("#frmcreate").parsley('validate')) { + + //check password strength + if (($(".password-verdict").html() == 'Strong' || $(".password-verdict").html() == 'Very Strong')) { + + //can we remove this check? + if (ensureCreateWalletGuidNicknameAndPasswordValid()) { + + $("#imgcreatewaiting").show(); + $("#btnCreate").prop('disabled', true); + $("#btnCreate").addClass('disabled'); + $("#lnkOpenWallet").hide(); + + var target = document.getElementById('imgcreatewaiting'); + var spinner = new Spinner(spinneropts).spin(target); + + //error handling here? + + var guid = $('#createWalletStart input#guid').val(); + var username = $("#createWalletStart input#nickname").val(); + var password = $('#createWalletStart input#cpassword').val(); + var emailAddress = $('#createWalletStart input#emailaddress').val(); + + + Engine.createWallet(guid, password, username, emailAddress, function (err, result) { + + //move error handling and ui elements to here + $("#createWalletStart input#nickname").css("border-color", "#ccc"); + if (err) { + if (result == "ErrUserExists") { + + $("#createWalletStart input#nickname").css("border-color", "#ffaaaa"); + $("#imgcreatewaiting").hide(); + + $("#createwalletalert").show(); + $("#createwalletalertmessage").html("The username already exists"); + + $("#btnCreate").prop('disabled', false); + $("#btnCreate").removeClass('disabled'); + $("#lnkOpenWallet").show(); + } + if (result == "ErrEmailExists") { + + $("#createWalletStart input#emailaddress").css("border-color", "#ffaaaa"); + $("#imgcreatewaiting").hide(); + + $("#createwalletalert").show(); + $("#createwalletalertmessage").html("The email address is already in use"); + + $("#btnCreate").prop('disabled', false); + $("#btnCreate").removeClass('disabled'); + $("#lnkOpenWallet").show(); + } + + if (result == "ErrCreateAccount") { + + $("#imgcreatewaiting").hide(); + $("#btnCreate").prop('disabled', false); + $("#btnCreate").removeClass('disabled'); + $("#lnkOpenWallet").show(); + + } + + } else { + + //set variables for the session + $("#createWalletStart").hide(); + $('#createWalletStart input#cpassword').val(''); + $('#createWalletStart input#password1').val(''); + + $("#hotWalletPhrase").text(result.hotWalletPhrase); + $("#coldWalletPhrase").text(result.coldWalletPhrase); + //$("#ninkiWalletPhrase").text(result.ninkiWalletPhrase); + + $("#walletGuid").text($('input#guid').val()); + $("#showPhrases").show(); + $("#securitywizard").show(); + + if ($('#createWalletStart input#showQrCheckbox')[0].checked) { + //create two factor setting in databse and display qr code + $("#no2famessage").hide(); + showTwoFactorQr(); + } else { + $("#no2famessage").show(); + } + } + }); + } + + } else { + + //password not strong + $("#createwalletalert").show(); + $("#createwalletalertmessage").html("Password must be Strong- ideally Very Strong"); + } + + } + + }); + + $("#btnPassphraseLogin").click(function () { + + + var isvalid = true; + + if ($('#createWalletStart input#showQrCheckbox')[0].checked) { + isvalid = $("#phrase2fa").parsley('validate'); + } + + if (isvalid) { + + var twoFactorCodeChk = $('#twoFactorCodeCheck').val(); + + var target = document.getElementById('imgphrasewaiting'); + var spinner = new Spinner(spinneropts).spin(target); + + $("#imgphrasewaiting").show(); + + setCookie('guid', Engine.m_oguid); + + Engine.openWalletAfterCreate(twoFactorCodeChk, function (err, result) { + + if (err) { + + $("#imgphrasewaiting").hide(); + $("#phraseloginerror").show(); + $("#phraseloginerrormessage").html(result); + + } else { + + + initialiseUI(); + $("#imgphrasewaiting").hide(); + $("#phraseloginerror").hide(); + $("#validateemail").show(); + $("#step4").show(); + $("#step3").hide(); + $("#listep3").removeClass("active"); + $("#listep4").addClass("active"); + $("#prgsecwiz").width('100%'); + $(".next").hide(); + $(".previous").hide(); + } + + + }); + } + + }); + + + $("#btnEmailValidate").click(function () { + + var token = $("#txtEmailToken").val(); + + Engine.getEmailValidation(token, function (err, response) { + + if (err) { + + } else { + + if (response != "Valid") { + $("#valemailerror").show(); + + if (response == "Expired") { + $("#valemailerrormessage").html('Your token has expired'); + } + if (response == "Invalid") { + $("#valemailerrormessage").html('Your token is not valid'); + } + + + + } else { + + if ($('#createWalletStart input#showQrCheckbox')[0].checked) { + //TWOFACTORONLOGIN = true; + } + + //initialiseUI(); + Engine.m_validate = false; + + //readAccountSettingsFromServerAndPopulateForm(); + $("#securitywizard").hide(); + $("#validateemail").hide(); + $("#mainWallet").show(); + $("#valemailerror").hide(); + } + } + + }); + + + //call to verify token + + + }); + + //wallet security wizard + + var step = 1; + $("#step1").show(); + $("#prgsecwiz").width('25%'); + $(".previous").hide(); + $(".next").click(function () { + + if (step == 2) { + $("#step3").show(); + $("#step2").hide(); + $("#listep2").removeClass("active"); + $("#listep3").addClass("active"); + $("#prgsecwiz").width('75%'); + $(".next").hide(); + $(".previous").show(); + step++; + } + + if (step == 1) { + $("#step2").show(); + $("#step1").hide(); + $("#listep1").removeClass("active"); + $("#listep2").addClass("active"); + $("#prgsecwiz").width('50%'); + $(".previous").show(); + + step++; + } + + }); + + $(".previous").click(function () { + + if (step == 2) { + $("#step1").show(); + $("#step2").hide(); + $("#listep2").removeClass("active"); + $("#listep1").addClass("active"); + $("#prgsecwiz").width('25%'); + $(".previous").hide(); + $(".next").show(); + step--; + } + + if (step == 3) { + $("#step2").show(); + $("#step3").hide(); + $("#listep3").removeClass("active"); + $("#listep2").addClass("active"); + $("#prgsecwiz").width('50%'); + $(".previous").show(); + $(".next").show(); + step--; + } + + if (step == 4) { + $("#step3").show(); + $("#step4").hide(); + $("#listep4").removeClass("active"); + $("#listep3").addClass("active"); + $("#prgsecwiz").width('75%'); + $(".previous").show(); + $(".next").hide(); + step--; + } + + }); + + + + + + + + + $("#btnReset2fa").click(function () { + + //stretch password + //download the recovery packet + //decrypt + //return shared secret + //no feedback apart from, please check your email + var fguid = $("#txtreset2faguid").val(); + var fusername = $("#txtreset2fausername").val(); + var fpwd = $("#txtreset2fapassword").val(); + + $("#reset2faerror").hide(); + $("#reset2fasuccess").hide(); + + Engine.ResetTwoFactor(fguid, fusername, fpwd, function (err, results) { + + if (err) { + $("#reset2faerror").show(); + $("#reset2faerrormessage").html('There was an error'); + } else { + $("#validate2fareset").show(); + $("#reset2fa").hide(); + } + }); + + }); + + + $("#btnhidekeys").click(function () { + $("#secdisphrase").hide(); + $("#secdisninki").hide(); + $("#btnhidekeys").hide(); + $("#btndisplaykeys").show(); + }); + + $("#btndisplaykeys").click(function () { + + //get the wallet packet + //secdisphrase + + var ninkiPub = Engine.m_walletinfo.ninkiPubKey; + var phrase = Engine.m_walletinfo.hotHash; + + var bip39 = new BIP39(); // 'en' is the default language + var hotmnem = bip39.entropyToMnemonic(phrase); + + $("#secdisphrase").html(hotmnem); + $("#secdisninki").html(ninkiPub); + $("#secdisphrase").show(); + $("#secdisninki").show(); + $("#btnhidekeys").show(); + $("#btndisplaykeys").hide(); + + }); + + + //depreciated + $("#btn2faResetValidate").click(function () { + + var vtoken = $("#txt2faResetToken").val(); + + Engine.EmailValidationForTwoFactor(vtoken, 0, function (err, response) { + if (err) { + + } else { + + if (response != "Valid") { + $("#val2fatokenerror").show(); + + if (response == "Expired") { + $("#val2fatokenerrormessage").html('Your token has expired'); + } + if (response == "Invalid") { + $("#val2fatokenerrormessage").html('Your token is not valid'); + } + + } else { + $("#val2fatoken").hide(); + $("#val2fatokenerror").hide(); + location.reload(); + } + } + + }); + + }); + + + //transaction filters + + $("#optDay").click(function () { + trasactionFilterOn = true; + currentTransactionFilter = 'Day'; + lastNoOfTrans = -1; + updateTransactions(); + }); + + $("#optWeek").click(function () { + trasactionFilterOn = true; + currentTransactionFilter = 'Week'; + lastNoOfTrans = -1; + updateTransactions(); + }); + + $("#optMonth").click(function () { + trasactionFilterOn = true; + currentTransactionFilter = 'Month'; + lastNoOfTrans = -1; + updateTransactions(); + }); + + $('#btntransearch').click(function () { + trasactionFilterOn = true; + currentTransactionFilter = 'Search'; + lastNoOfTrans = -1; + updateTransactions(); + }); + + $('#btntranclear').click(function () { + trasactionFilterOn = false; + currentTransactionFilter = ''; + lastNoOfTrans = -1; + updateTransactions(); + $("#optDay").removeClass('active'); + $("#optWeek").removeClass('active'); + $("#optMonth").removeClass('active'); + $("#txttransearch").val(''); + + }); + + + $('#thtrandate').click(function () { + + //reset contact widget + if (currentTransactionSort == 'ContactDesc') { + $('#thtrancontact').toggleClass('active'); + } + + if (currentTransactionSort == 'DateDesc') { + currentTransactionSort = 'DateAsc'; + } else { + currentTransactionSort = 'DateDesc'; + } + + trasactionSortOn = true; + lastNoOfTrans = -1; + updateTransactions(); + }); + + $('#thtrancontact').click(function () { + + //reset date widget + if (currentTransactionSort == 'DateDesc') { + $('#thtrandate').toggleClass('active'); + } + + if (currentTransactionSort == 'ContactAsc') { + currentTransactionSort = 'ContactDesc'; + } else { + currentTransactionSort = 'ContactAsc'; + } + + trasactionSortOn = true; + lastNoOfTrans = -1; + updateTransactions(); + }); + + $('#tpagfirst').click(function () { + currentPageIndex = 0; + lastNoOfTrans = -1; + updateTransactions(); + }); + + $('#tpaglast').click(function () { + currentPageIndex = Math.floor((filteredTransactions.length / transactionsPerPage)); + lastNoOfTrans = -1; + updateTransactions(); + + }); + + $('#tpagnext').click(function () { + if (currentPageIndex < Math.floor((filteredTransactions.length / transactionsPerPage))) { + currentPageIndex = currentPageIndex + 1; + lastNoOfTrans = -1; + updateTransactions(); + } + }); + + $('#tpagprev').click(function () { + if (currentPageIndex > 0) { + currentPageIndex = currentPageIndex - 1; + lastNoOfTrans = -1; + updateTransactions(); + } + }); + + + //invoice filters + //for me + $("#optPending").click(function () { + invoiceFilterOn = true; + currentInvoiceFilter = 'Pending'; + lastInvoiceToPayCount = -1; + showInvoiceList(); + }); + + $("#optPaid").click(function () { + invoiceFilterOn = true; + currentInvoiceFilter = 'Paid'; + lastInvoiceToPayCount = -1; + showInvoiceList(); + }); + + $("#optRejected").click(function () { + invoiceFilterOn = true; + currentInvoiceFilter = 'Rejected'; + lastInvoiceToPayCount = -1; + showInvoiceList(); + }); + + $("#optClearInvoice").click(function () { + invoiceFilterOn = false; + currentInvoiceFilter = ''; + lastInvoiceToPayCount = -1; + showInvoiceList(); + }); + + $('#btnSearchInvForMe').click(function () { + currentForMeInvoicePageIndex = 0; + invoiceFilterOn = true; + currentInvoiceFilter = 'Search'; + lastInvoiceToPayCount = -1; + showInvoiceList(); + }); + + + + + + //by me + //invoice filters + $("#optByMePending").click(function () { + invoiceByMeFilterOn = true; + currentByMeInvoiceFilter = 'Pending'; + lastInvoiceByUserCount = -1; + showInvoiceByUserList(); + }); + + $("#optByMePaid").click(function () { + invoiceByMeFilterOn = true; + currentByMeInvoiceFilter = 'Paid'; + lastInvoiceByUserCount = -1; + showInvoiceByUserList(); + }); + + $("#optByMeRejected").click(function () { + invoiceByMeFilterOn = true; + currentByMeInvoiceFilter = 'Rejected'; + lastInvoiceByUserCount = -1; + showInvoiceByUserList(); + }); + + $("#optByMeClear").click(function () { + invoiceByMeFilterOn = false; + currentByMeInvoiceFilter = ''; + lastInvoiceByUserCount = -1; + showInvoiceByUserList(); + }); + + $('#btnSearchInvByMe').click(function () { + currentByMeInvoicePageIndex = 0; + invoiceByMeFilterOn = true; + currentByMeInvoiceFilter = 'Search'; + lastInvoiceByUserCount = -1; + showInvoiceByUserList(); + }); + + $('#ibmpagfirst').click(function () { + currentByMeInvoicePageIndex = 0; + lastInvoiceByMeNetCount = -1; + showInvoiceByUserList(); + }); + + $('#ibmpaglast').click(function () { + currentByMeInvoicePageIndex = Math.floor((filteredByMeInvoices.length / invoicesByMePerPage)); + lastInvoiceByMeNetCount = -1; + showInvoiceByUserList(); + }); + + $('#ibmpagnext').click(function () { + if (currentByMeInvoicePageIndex < Math.floor((filteredByMeInvoices.length / invoicesByMePerPage))) { + currentByMeInvoicePageIndex = currentByMeInvoicePageIndex + 1; + lastInvoiceByMeNetCount = -1; + showInvoiceByUserList(); + } + }); + + $('#ibmpagprev').click(function () { + if (currentByMeInvoicePageIndex > 0) { + currentByMeInvoicePageIndex = currentByMeInvoicePageIndex - 1; + lastInvoiceByMeNetCount = -1; + showInvoiceByUserList(); + } + }); + + + //for me + + $('#ifmpagfirst').click(function () { + currentForMeInvoicePageIndex = 0; + lastInvoiceToPayCount = -1; + showInvoiceList(); + }); + + $('#ifmpaglast').click(function () { + currentForMeInvoicePageIndex = Math.floor((filteredForMeInvoices.length / invoicesForMePerPage)); + lastInvoiceToPayCount = -1; + showInvoiceList(); + }); + + $('#ifmpagnext').click(function () { + if (currentForMeInvoicePageIndex < Math.floor((filteredForMeInvoices.length / invoicesForMePerPage))) { + currentForMeInvoicePageIndex = currentForMeInvoicePageIndex + 1; + lastInvoiceToPayCount = -1; + showInvoiceList(); + } + }); + + $('#ifmpagprev').click(function () { + if (currentForMeInvoicePageIndex > 0) { + currentForMeInvoicePageIndex = currentForMeInvoicePageIndex - 1; + lastInvoiceToPayCount = -1; + showInvoiceList(); + } + }); + + + + getCookie('ninki_reg', function (res) { + + if (res.length > 0) { + + showOpenWalletStart(); + + } else { + + showCreateWalletStart(); + } + }) + + $("#password").keypress(function (e) { + if (e.which == 13) { + $("#btnLogin").click(); + } + }); + + + $("#twoFactorCode").keypress(function (e) { + if (e.which == 13) { + $("#btn2faLogin").click(); + } + }); + + $("#password1").keypress(function (e) { + if (e.which == 13) { + $("#btnCreate").click(); + } + }); + + + $("#cpassword").blur(function () { + $(".popover.fade.bottom.in").hide(); + }); + + $("#cpassword").focus(function () { + $(".popover.fade.bottom.in").show(); + }); + + $("#newpassword1").blur(function () { + $(".popover.fade.bottom.in").hide(); + }); + + $("#newpassword1").focus(function () { + $(".popover.fade.bottom.in").show(); + }); + + //$("#balance").html("... BTC"); + + $("#mainWallet").hide(); + $('#message').hide(); + $("#openwalletalert").hide(); + $("#createwalletalert").hide(); + $("#imgopenwaiting").hide(); + $("#imgcreatewaiting").hide(); + $("#showPhrases").hide(); + $("#twoFactorQr").hide(); + $("#2factor1").hide(); + $("#securitywizard").hide(); + $("#secdisphrase").hide(); + $("#secdisninki").hide(); + $("#btnhidekeys").hide(); + + $("#btnSendToFriend").hammer(null).bind("tap", function () { + + + sendMoney(SELECTEDFRIEND, 0); + + + }); + $("#sendfriendprog").hide(); + + $("#hforgotguid").click(function () { + + $("#createWalletStart").hide(); + $("#openWalletStart").hide(); + $("#lostguid").show(); + $("#reset2fa").hide(); + }); + + + $("#hlost2fa").click(function () { + + $("#createWalletStart").hide(); + $("#openWalletStart").hide(); + $("#lostguid").hide(); + $("#reset2fa").show(); + + }); + + + $("#emailresend").click(function () { + + Engine.sendWelcomeDetails(function (err, result) { + + if (!err) { + + $("#emailresendmessage").show(); + $("#emailresend").hide(); + //email has been resent, please check your email + } + + }); + + }); + + + $("#cpassword").on('change keyup', function () { + + $("#password1").parsley("validate"); + + }); + + + $("#twoFactorCodeCheck").on('change keyup', function () { + + $("#phraseloginerror").fadeOut(100); + + }); + + + $("#btnChangePassword").click(function () { + + $("#chngpwssuc").hide(); + $("#chngpwerr").hide(); + $("#chngpwdprog").hide(); + $("#chngpwdprogbar").width('0%'); + + var newpassword = $("#newpassword1").val(); + var oldpassword = $("#oldpwd").val(); + var twoFactorCode = $("#txtTwoFactorCodeForChangePwd").val(); + + if ($("#frmpwdchange").parsley('validate')) { + + $("#chngpwdprog").show(); + $("#chngpwdprogmess").show(); + $("#chngpwdprogbar").width('10%'); + $("#chngpwdprogmess").html('Getting packet...'); + + + if (oldpassword != newpassword) { + + //check password strength + if (($(".newpwstrength_viewport_progress .password-verdict").html() == 'Strong' || $(".newpwstrength_viewport_progress .password-verdict").html() == 'Very Strong')) { + + //stretch old password + //verify that it matches the current one + $("#chngpwdprogbar").width('20%'); + $("#chngpwdprogmess").html('Getting details...'); + + setTimeout(function () { + + Engine.ChangePassword(twoFactorCode, oldpassword, newpassword, "#chngpwdprogbar", "#chngpwdprogmess", function (err, results) { + + if (err) { + $("#chngpwerr").show(); + $("#chngpwerrmess").html(results); + $("#chngpwdprogmess").hide(); + $("#chngpwdprog").hide(); + + } else { + + password = results; + $("#chngpwerr").hide(); + $("#chngpwdprogbar").width('100%'); + $("#chngpwdprog").hide(); + $("#chngpwdprogmess").hide(); + $("#chngpwdprogbar").hide(); + $("#chngpwssuc").show(); + + $("#newpassword1").val(''); + $("#newpassword2").val(''); + $("#oldpwd").val(''); + $("#txtTwoFactorCodeForChangePwd").val(''); + + } + + }); + + + }, 500); + } + + } else { + $("#chngpwerr").show(); + $("#chngpwerrmess").html("Passwords are the same. Password not updated"); + $("#chngpwdprogmess").hide(); + $("#chngpwdprog").hide(); + } + + } + }); + + + $("#btnVerify").click(function () { + + var code = $("#txtCode").val(); + + $("#txtCode").css("border-color", "#ccc"); + $("#validatefail").hide(); + $("#validatesuccess").hide(); + + var bip39 = new BIP39(); + code = bip39.mnemonicToHex(code); + + if (code.length != 40) { + $("#txtCode").css("border-color", "#ffaaaa"); + return; + } + + //get the hash to validate against + //this will confirm that my friend has the same keys + //i orginally packaged for him + + Engine.verifyFriendData(SELECTEDFRIEND, code, function (err, result) { + + if (result) { + $("#validateform").hide(); + $("#validatesuccess").show(); + $("#txtCode").val(''); + selectedFriend.validated = true; + FRIENDSLIST[selectedFriend.userName].validated = true; + updateSelectedFriend(); + + //update list also + + //find friend in list and update the validated icon + $("#myfriends #seltarget" + selectedFriend.userName).html('
'); + + + } else { + $("#validatefail").show(); + } + + }); + }); + + + $("#btnSetupTwoFactor").click(function () { + + $("#twofactorsettings").show(); + $("#btnSetupTwoFactor").hide(); + $("#savetwofactorerror").hide(); + $("#setup2faemail").hide(); + $("#setup2faqr").show(); + showSettingsTwoFactorQr(); + + }); + + + $("#btnSaveTwoFactor").click(function () { + + //validate authenticator code + //switch on two factor on login + //setting changes always require two factor + + if ($("#frmSaveTwoFactor").parsley('validate')) { + + var twoFactorCode = $("#txtsettings2fa").val(); + var verifyToken = $("#txt2faVerifyToken").val(); + + Engine.SaveTwoFactor(twoFactorCode, verifyToken, function (err, result) { + + if (!err) { + //ok + $("#setup2faemail").show(); + $("#setup2faqr").hide(); + readAccountSettingsFromServerAndPopulateForm(); + } else { + //error + $("#savetwofactorerror").show(); + $("#savetwofactorerrormessage").show(); + $("#savetwofactorerrormessage").html(result); + } + }); + + } + }); + + $("#btn2faVerifyEmail").click(function () { + + + var vtoken = $("#txt2faVerifyToken").val(); + + Engine.EmailValidationForTwoFactor(vtoken, 1, function (err, response) { + + if (!err) { + + + //TWOFACTORONLOGIN = true; + //refresh settings panel + + readAccountSettingsFromServerAndPopulateForm(); + + $("#twofactorsettings").hide(); + $("#btnSetupTwoFactor").hide(); + $("#savetwofactorerror").hide(); + $("#savetwofactorerrormessage").hide(); + $("#setup2faemail").hide(); + $("#setup2faqr").hide(); + + + } else { + + //TO DO: + //report error here + + } + + + }); + + + }); + + + $("#hsettings").click(function () { + readAccountSettingsFromServerAndPopulateForm(); + }); + + $("#savesettingsbutton").click(function () { + if ($("#frmsettings").parsley('validate')) { + saveAccountSettingsToServer(); + + } + }); + + Engine.fillElementWithGuid($("#createWalletStart input#guid")); + + + //INVOICE STUFF START------------------------------------------ + + $("#friendselector").hide(); + $("#invoice").hide(); + $("#invoicedisplay").hide(); + + $("#btnpayinvoice").hammer(null).bind("tap", function () { + + payInvoice(selectedInvoiceUserName, selectedInvoiceAmount, selectedInvoiceId); + + }); + + $("#btnrejectinvoice").hammer(null).bind("tap", function () { + + Engine.updateInvoice(selectedInvoiceUserName, selectedInvoiceId, '', 2, function (err, result) { + + lastInvoiceToPayCount = 0; + showInvoiceList(); + showInvoiceListNetwork(); + + $("#invoicedisplay").hide(); + $("#invoicestopay").show(); + $("#createinv").show(); + + if (uiInvoiceReturnToNetwork) { + updateSelectedFriend(); + } + }); + + }); + + $("#payinvoicecancel").hammer(null).bind("tap", function () { + + + $("#invoices").hide(); + $("#network").show(); + + //if (uiInvoiceReturnToNetwork) { + //$("#hnetwork").click(); + //uiInvoiceReturnToNetwork = false; + //} + + + }); + + + $("#invoicecancel").click(function () { + $("#invoicestopay").show(); + $("#createinv").show(); + }); + + $("#sendinvoicecancel").click(function () { + + $("#invoicedisplay").hide(); + $("#invoice").hide(); + $("#invoicestopay").show(); + $("#hnetwork").click(); + }); + + + $("#sendinvoice").click(function () { + + calcInvoiceTotals(); + + var subtotal = $("#tblinvoice tfoot th #subtotal").html(); + var tax = $("#tblinvoice tfoot th #tax").html(); + var total = $("#tblinvoice tfoot th #total").html(); + + if (subtotal > 0) { + + if (validateInvoice()) { + //parse the table and generate objects + var invoicelines = []; + $("#tblinvoice tbody tr").each(function () { + var tmpVals = []; + $(this).find('input').each(function () { + tmpVals.push($(this).val()); + + }); + if (tmpVals[0] != null) { + invoicelines.push({ + description: tmpVals[0], + quantity: tmpVals[1], + amount: convertToSatoshis(tmpVals[2], COINUNIT) + }); + } + }); + + + var summary = { + subtotal: convertToSatoshis(subtotal, COINUNIT), + tax: convertToSatoshis(tax, COINUNIT), + total: convertToSatoshis(total, COINUNIT) + }; + var invoice = { + summary: summary, + invoicelines: invoicelines + }; + + createNewInvoice(invoiceSelectedUser, invoice, function (err, invoices) { + + $("#invoicestopay").show(); + $("#createinv").show(); + $("#invoice").hide(); + + + $("#hnetwork").click(); + lastInvoiceByUserCount = -1; + lastInvoiceByMeNetCount = -1; + + showInvoiceByUserList(function (err, result) { + + showInvoiceByMeListNetwork(function (err, result) { + + + }); + + }); + + + //updateSelectedFriend(); + + }); + } + } + + }); + + + $("#btnCreateInvFriend").click(function () { + + $("#invoicestopay").hide(); + $("#invoicedisplay").hide(); + uiInvoiceReturnToNetwork = true; + invoiceSelectedUser = SELECTEDFRIEND; + lineCount = 0 + $("#createinvoiceforlabel").html('Create an Invoice for ' + SELECTEDFRIEND); + $("#tblinvoice tbody").empty(); + + $("#friendselector").hide(); + $("#invoice").show(); + //write a new one + $("#addline").click(); + $("#hinvoices").click(); + + }); + + + + $("#lineAmount").keypress(function (e) { + if (e.which == 13) { + + $("#addline").click(); + + } + }); + + $("#addline").click(function () { + + if (validateInvoice()) { + + //recalc everything + lineCount++; + + $('#tblinvoice tbody').append(getRowTempate()); + $('#line' + lineCount + 'desc').focus(); + + $('#addline' + lineCount).click({ + line: lineCount + }, function (event) { + $('#tblinvoice #row' + event.data.line).remove(); + validateInvoice(); + }); + + $('#line' + lineCount + 'Amount').blur({ + line: lineCount + }, function (event) { + if (validateInvoice()) { + $('#lineTotal' + (event.data.line)).html(($('#line' + (event.data.line) + 'Amount').val() * $('#line' + (event.data.line) + 'Quantity').val()).toFixed(4)); + calcInvoiceTotals(); + } else { + //$('#line' + (event.data.line) + 'Amount'). + } + }); + + + $('#line' + lineCount + 'Quantity').blur({ + line: lineCount + }, function (event) { + if (validateInvoice()) { + $('#lineTotal' + (event.data.line)).html(($('#line' + (event.data.line) + 'Amount').val() * $('#line' + (event.data.line) + 'Quantity').val()).toFixed(4)); + calcInvoiceTotals(); + } else { + //$('#line' + (event.data.line) + 'Amount'). + } + }); + + + $('#line' + lineCount + 'desc').blur(function (event) { + validateInvoice(); + }); + + $('#line' + lineCount + 'Amount').keypress({ + line: lineCount + }, function (e) { + if (e.which == 13) { + $('#line' + lineCount + 'Amount').blur(); + $("#addline").click(); + + } + }); + + $('#line' + lineCount + 'desc').keydown({ + line: lineCount + }, function (e) { + if (e.which == 8) { + if ($('#line' + lineCount + 'desc').val() == '' && $('#line' + lineCount + 'Amount').val() == '' && $('#line' + lineCount + 'Quantity').val() == '') { + $('#tblinvoice #row' + e.data.line).remove(); + } + } + }); + + } + //write a new one + + }); + + + //openWallet(); + $("#openWalletStart #password").focus(); + }); + + + function calcInvoiceTotals() { + var subTotal = 0; + $('#tblinvoice .lineTotal').each(function () { + + subTotal += ($(this).html() * 1); + + }); + + $("#subtotal").html(subTotal.toFixed(4)); + $("#tax").html((subTotal * 0.10).toFixed(4)); + $("#total").html((subTotal + (subTotal * 0.10)).toFixed(4)); + + }; + + function validateInvoice() { + var subTotal = 0; + + var isValid = true; + + $('#tblinvoice .amount').each(function () { + + var vval = $(this).val(); + var visValid = true; + if ($.isNumeric(vval)) { + + if ((vval * 1) <= 0) { + //not valid + //highlight + isValid = false; + visValid = false; + } + + } else { + + isValid = false; + visValid = false; + } + + if (!visValid) { + $(this).css("border-color", "#ff0000"); + } else { + $(this).css("border-color", "#cbd5dd"); + } + + }); + + $('#tblinvoice .quantity').each(function () { + + var vval = $(this).val(); + var visValid = true; + if ($.isNumeric(vval)) { + + if ((vval * 1) <= 0) { + //not valid + //highlight + isValid = false; + visValid = false; + } + + } else { + + isValid = false; + visValid = false; + } + + if (!visValid) { + $(this).css("border-color", "#ff0000"); + } else { + $(this).css("border-color", "#cbd5dd"); + } + + + }); + + $('#tblinvoice .desc').each(function () { + + + var vval = $(this).val(); + var visValid = true; + + if (vval.length == 0) { + visValid = false; + isValid = false; + } + + if (!visValid) { + $(this).css("border-color", "#ff0000"); + } else { + $(this).css("border-color", "#cbd5dd"); + } + + }); + + if (isValid) { + + calcInvoiceTotals(); + + //if (subTotal > 0) { + //$("#subtotal").html(subTotal.toFixed(4)); + //$("#tax").html((subTotal * 0.10).toFixed(4)); + //$("#total").html((subTotal + (subTotal * 0.10)).toFixed(4)); + //} else { + // isValid = false; + //} + } + + return isValid; + + }; + + + + var lineCount = 0; + function getRowTempate() { + var template = '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + ''; + + + + + return template; + } + + + var cachedInvoices = []; + var lastInvoiceToPayCount = 0; + function showInvoiceList(callback) { + //get back the list of invoices to pay + + Engine.getInvoiceList(function (err, invoices) { + + + if (lastInvoiceToPayCount < invoices.length) { + + } else { + + return callback(false, "done"); + + } + + for (var i = 0; i < invoices.length; i++) { + var d1 = new Date(invoices[i].InvoiceDate); + invoices[i].JsDate = d1; + } + + invoices = _.sortBy(invoices, function (inv) { return -inv.JsDate; }); + + filteredForMeInvoices = invoices; + + //perform all filters then set back to invoices + + if (currentInvoiceFilter == "Pending") { + + filteredForMeInvoices = _.filter(invoices, function (inv) { return inv.InvoiceStatus == 0; }); + } + + if (currentInvoiceFilter == "Paid") { + + filteredForMeInvoices = _.filter(invoices, function (inv) { return inv.InvoiceStatus == 1; }); + } + + if (currentInvoiceFilter == "Rejected") { + + filteredForMeInvoices = _.filter(invoices, function (inv) { return inv.InvoiceStatus == 2; }); + } + + + if (currentInvoiceFilter == "Search") { + var search = $('#txtSearchInvForMe').val(); + filteredForMeInvoices = _.filter(invoices, function (inv) { return inv.InvoiceFrom.search(search) > -1; }); + } + + + var noofpages = Math.floor((filteredForMeInvoices.length / invoicesForMePerPage)); + + var indexFrom = currentForMeInvoicePageIndex * invoicesForMePerPage; + var indexTo = indexFrom + invoicesForMePerPage; + + if (indexTo > filteredForMeInvoices.length) { + indexTo = filteredForMeInvoices.length; + } + + $('#invbmpaglabel').html('Showing ' + (indexFrom + 1) + ' to ' + (indexTo) + ' of ' + filteredForMeInvoices.length); + + + invoices = filteredForMeInvoices; + + if (lastInvoiceToPayCount < invoices.length) { + + + pagedForMeInvoices = filteredForMeInvoices.slice(indexFrom, indexTo); + + invoices = pagedForMeInvoices; + + cachedInvoices = []; + + lastInvoiceToPayCount = invoices.length; + + var s = ''; + $('#tblinvoicepay tbody').empty(); + for (var i = 0; i < invoices.length; i++) { + + var invdate = new Date(invoices[i].InvoiceDate.match(/\d+/)[0] * 1).toLocaleString(); + + var invpaydate = ''; + if (invoices[i].InvoicePaidDate) { + invpaydate = new Date(invoices[i].InvoicePaidDate.match(/\d+/)[0] * 1).toLocaleString(); + } + + cachedInvoices.push(invoices[i]); + + var statusbox = ''; + if (invoices[i].InvoiceStatus == 0) { + statusbox = ' Pending'; + } + else if (invoices[i].InvoiceStatus == 1) { + statusbox = ' Paid'; + } + else if (invoices[i].InvoiceStatus == 2) { + statusbox = ' Rejected'; + } + + + var length = invoices[i].InvoiceFrom.length; + if (length > 20) { + length = 20; + } + + var imageSrcSmall = "images/avatar/64px/Avatar-" + pad(length) + ".png"; + + + if (FRIENDSLIST[invoices[i].InvoiceFrom].profileImage != '') { + imageSrcSmall = "https://ninkip2p.imgix.net/" + FRIENDSLIST[invoices[i].InvoiceFrom].profileImage + "?crop=faces&fit=crop&h=64&w=64&mask=ellipse&border=1,d0d0d0"; + } + + s += "" + invdate + ""; + + s += "\"\" "; + + s += invoices[i].InvoiceFrom + ""; + s += "" + statusbox + "" + invpaydate + ""; + } + + $('#tblinvoicepay tbody').append(s); + + for (var i = 0; i < invoices.length; i++) { + + $("#tblinvoicepay #viewinvoice" + i).click({ + index: invoices[i].InvoiceId, username: invoices[i].InvoiceFrom + }, function (event) { + displayInvoice(event.data.index, event.data.username, 'forme', function (err, res) { + uiInvoiceReturnToNetwork = false; + //$('#hinvoices').click(); + }); + }); + } + + } + + callback(); + + }); + } + + + + var lastInvoiceToPayNetCount = 0; + var uiInvoiceReturnToNetwork = false; + function showInvoiceListNetwork() { + + var invoices = _.filter(cachedInvoices, function (inv) { return inv.InvoiceFrom == SELECTEDFRIEND; }); + + + if (invoices.length == 0) { + $('#invfornet').empty(); + $('#invfornet').hide(); + } + + + if (lastInvoiceToPayNetCount < invoices.length) { + + lastInvoiceToPayNetCount = invoices.length; + + var s = ''; + $('#invfornet').empty(); + + for (var i = 0; i < invoices.length; i++) { + + var invdate = new Date(invoices[i].InvoiceDate.match(/\d+/)[0] * 1).toLocaleString(); + + var statusbox = ''; + if (invoices[i].InvoiceStatus == 0) { + statusbox = ' Pending'; + } + else if (invoices[i].InvoiceStatus == 1) { + statusbox = ' Paid'; + } + else if (invoices[i].InvoiceStatus == 2) { + statusbox = ' Rejected'; + } + + s += "
" + invdate + "
" + + "
" + statusbox + "
"; + } + + $('#invfornet').append(s); + + for (var i = 0; i < invoices.length; i++) { + + $("#invfornet #viewinvoicenetfrom" + invoices[i].InvoiceFrom + invoices[i].InvoiceId).hammer(null).bind("tap", { + index: invoices[i].InvoiceId, username: invoices[i].InvoiceFrom + }, function (event) { + + $("#invtapspinner").show(); + var target = document.getElementById('invtapspinner'); + var spinner = new Spinner(spinneropts).spin(target); + + displayInvoice(event.data.index, event.data.username, 'forme', function (err, res) { + uiInvoiceReturnToNetwork = true; + + networkpagestate = "invoice"; + friendpagestate = "invoice"; + + $("#invtapspinner").hide(); + $('#network').hide(); + $('#invoices').show(); + + }); + }); + } + + $('#invfornet').show(); + + + } + + // $('#pnlfriendinv').show(); + + + } + + var lastInvoiceByMeNetCount = 0; + function showInvoiceByMeListNetwork() { + + var invoices = _.filter(cachedInvoicesByUser, function (inv) { return inv.InvoiceFrom == SELECTEDFRIEND; }); + + + + if (invoices.length == 0) { + $('#invbynet').empty(); + $('#invbynet').hide(); + } + + + if (lastInvoiceByMeNetCount < invoices.length) { + + lastInvoiceByMeNetCount = invoices.length; + + var s = ''; + $('#invbynet').empty(); + + for (var i = 0; i < invoices.length; i++) { + + var invdate = new Date(invoices[i].InvoiceDate.match(/\d+/)[0] * 1).toLocaleString(); + + var statusbox = ''; + if (invoices[i].InvoiceStatus == 0) { + statusbox = ' Pending'; + } + else if (invoices[i].InvoiceStatus == 1) { + statusbox = ' Paid'; + } + else if (invoices[i].InvoiceStatus == 2) { + statusbox = ' Rejected'; + } + + s += "
" + invdate + "
" + + "
" + statusbox + "
"; + } + + $('#invbynet').append(s); + + for (var i = 0; i < invoices.length; i++) { + + $("#invbynet #viewinvoicenetby" + invoices[i].InvoiceFrom + invoices[i].InvoiceId).hammer(null).bind("tap", { + index: invoices[i].InvoiceId, username: invoices[i].InvoiceFrom + }, function (event) { + + + $("#invtapspinner").show(); + var target = document.getElementById('invtapspinner'); + var spinner = new Spinner(spinneropts).spin(target); + + displayInvoice(event.data.index, event.data.username, 'byme', function (err, res) { + + networkpagestate = "invoice"; + friendpagestate = "invoice"; + + $("#invtapspinner").hide(); + $('#network').hide(); + $('#invoices').show(); + + }); + }); + } + + $('#invbynet').show(); + + + } + + // $('#pnlfriendinv').show(); + + + } + + var cachedInvoicesByUser = []; + var lastInvoiceByUserCount = 0; + function showInvoiceByUserList(callback) { + //get back the list of invoices to pay + + + Engine.getInvoiceByUserList(function (err, invoices) { + + for (var i = 0; i < invoices.length; i++) { + var d1 = new Date(invoices[i].InvoiceDate); + invoices[i].JsDate = d1; + } + + invoices = _.sortBy(invoices, function (inv) { return -inv.JsDate; }); + + filteredByMeInvoices = invoices; + + //perform all filters then set back to invoices + + if (currentByMeInvoiceFilter == "Pending") { + + filteredByMeInvoices = _.filter(invoices, function (inv) { return inv.InvoiceStatus == 0; }); + } + + if (currentByMeInvoiceFilter == "Paid") { + + filteredByMeInvoices = _.filter(invoices, function (inv) { return inv.InvoiceStatus == 1; }); + } + + if (currentByMeInvoiceFilter == "Rejected") { + + filteredByMeInvoices = _.filter(invoices, function (inv) { return inv.InvoiceStatus == 2; }); + } + + if (currentByMeInvoiceFilter == "Search") { + var search = $('#txtSearchInvByMe').val(); + filteredByMeInvoices = _.filter(invoices, function (inv) { return inv.InvoiceFrom.search(search) > -1; }); + } + + var noofpages = Math.floor((filteredByMeInvoices.length / invoicesByMePerPage)); + + var indexFrom = currentByMeInvoicePageIndex * invoicesByMePerPage; + var indexTo = indexFrom + invoicesByMePerPage; + + if (indexTo > filteredByMeInvoices.length) { + indexTo = filteredByMeInvoices.length; + } + + $('#invbmpaglabel').html('Showing ' + (indexFrom + 1) + ' to ' + (indexTo) + ' of ' + filteredByMeInvoices.length); + + invoices = filteredByMeInvoices; + + + if (lastInvoiceByUserCount < invoices.length) { + + + pagedByMeInvoices = filteredByMeInvoices.slice(indexFrom, indexTo); + + invoices = pagedByMeInvoices; + + cachedInvoicesByUser = []; + + lastInvoiceByUserCount = invoices.length; + + var s = ''; + $('#tblinvoicebyme tbody').empty(); + for (var i = 0; i < invoices.length; i++) { + + + var invdate = new Date(invoices[i].InvoiceDate.match(/\d+/)[0] * 1).toLocaleString(); + var invpaydate = ''; + if (invoices[i].InvoicePaidDate) { + invpaydate = new Date(invoices[i].InvoicePaidDate.match(/\d+/)[0] * 1).toLocaleString(); + } + + cachedInvoicesByUser.push(invoices[i]); + + var statusbox = ''; + if (invoices[i].InvoiceStatus == 0) { + statusbox = ' Pending'; + } + else if (invoices[i].InvoiceStatus == 1) { + statusbox = ' Paid'; + } + else if (invoices[i].InvoiceStatus == 2) { + statusbox = ' Rejected'; + } + + + var length = invoices[i].InvoiceFrom.length; + if (length > 20) { + length = 20; + } + + s += "" + invdate + "\"\" " + + invoices[i].InvoiceFrom + "" + statusbox + "" + invpaydate + ""; + } + + $('#tblinvoicebyme tbody').append(s); + + for (var i = 0; i < invoices.length; i++) { + + $("#tblinvoicebyme #viewinvoicebyuser" + i).click({ + index: invoices[i].InvoiceId, username: invoices[i].InvoiceFrom + }, function (event) { + displayInvoiceByUser(event.data.index, event.data.username, 'byme', function (err, res) { + + + }); + }); + } + + if (callback) { + callback(false, "ok"); + } + + } else { + + //no new invoices, but lets check for invoices with changed status + cachedInvoicesByUser = invoices; + + $('#tblinvoicebyme tbody tr .active').each(function (index, elem) { + var statusbox = ''; + if (cachedInvoicesByUser[index].InvoiceStatus == 0) { + statusbox = ' Pending'; + } + else if (cachedInvoicesByUser[index].InvoiceStatus == 1) { + statusbox = ' Paid'; + } + else if (cachedInvoicesByUser[index].InvoiceStatus == 2) { + statusbox = ' Rejected'; + } + //$(elem).html(''); + $(elem).html(statusbox); + }); + + $('#tblinvoicebyme tbody tr .paid').each(function (index, elem) { + + if (cachedInvoicesByUser[index].InvoiceStatus == 1 || cachedInvoicesByUser[index].InvoiceStatus == 1) { + + var invpaydate = ''; + if (cachedInvoicesByUser[index].InvoicePaidDate) { + invpaydate = new Date(cachedInvoicesByUser[index].InvoicePaidDate.match(/\d+/)[0] * 1).toLocaleString(); + } + + + $(elem).html(invpaydate); + } + + }); + + } + + if (callback) { + callback(false, "ok"); + } + + }); + } + + + + var selectedInvoiceAmount = 0; + var selectedInvoiceId = 0; + var selectedInvoiceUserName = ''; + + + function displayInvoiceDetails(invoice, json, invtype, callback) { + + + var invdate = new Date(invoice.InvoiceDate.match(/\d+/)[0] * 1).toLocaleString(); + + $("#createinv").hide(); + $("#invoicestopay").hide(); + + $('#tblinvdisplay tbody').empty(); + var s = ''; + for (var i = 0; i < json.invoicelines.length; i++) { + s += "" + json.invoicelines[i].description + "" + json.invoicelines[i].quantity + "" + convertFromSatoshis(json.invoicelines[i].amount, COINUNIT) + "" + (convertFromSatoshis(json.invoicelines[i].amount, COINUNIT) * json.invoicelines[i].quantity) + ""; + } + + $('#tblinvdisplay tbody').append(s); + + if (invtype == 'forme') { + $("#dinvusername").html('Invoice from ' + invoice.InvoiceFrom); + } else { + $("#dinvusername").html('Invoice to ' + invoice.InvoiceFrom); + } + + $("#dinvdate").html(invdate); + + $("#tblinvdisplay tfoot th #dsubtotal").html(convertFromSatoshis(json.summary.subtotal, COINUNIT)); + $("#tblinvdisplay tfoot th #dtax").html(convertFromSatoshis(json.summary.tax, COINUNIT)); + $("#tblinvdisplay tfoot th #dtotal").html(convertFromSatoshis(json.summary.total, COINUNIT)); + + selectedInvoiceAmount = convertFromSatoshis(json.summary.total); + selectedInvoiceId = invoice.InvoiceId; + selectedInvoiceUserName = invoice.InvoiceFrom; + + $("#sendinvprog").hide(); + $("#textMessageSendInv").hide(); + $("#btnokinvoice").hide(); + $("#invvalmess").hide(); + + if (invtype == 'forme') { + if (invoice.InvoiceStatus == 0) { + $("#payinvoicecancel").show(); + $("#btnpayinvoice").show(); + $("#btnrejectinvoice").show(); + + if (!FRIENDSLIST[invoice.InvoiceFrom].validated) { + $("#btnpayinvoice").addClass("disabled"); + $("#invvalt").html(invoice.InvoiceFrom); + $("#invvalmess").show(); + } else { + $("#btnpayinvoice").removeClass("disabled"); + $("#invvalmess").hide(); + } + + } + + if (invoice.InvoiceStatus == 1 || invoice.InvoiceStatus == 2) { + $("#btnokinvoice").show(); + $("#payinvoicecancel").hide(); + $("#btnpayinvoice").hide(); + $("#btnrejectinvoice").hide(); + } + } else { + $("#btnokinvoice").show(); + $("#payinvoicecancel").hide(); + $("#btnpayinvoice").hide(); + $("#btnrejectinvoice").hide(); + } + + var statusbox = ''; + if (invoice.InvoiceStatus == 0) { + statusbox = ' Pending'; + } + else if (invoice.InvoiceStatus == 1) { + statusbox = ' Paid'; + } + else if (invoice.InvoiceStatus == 2) { + statusbox = ' Rejected'; + } + + $("#invdisstatus").html(statusbox); + $("#invdisid").html(invoice.InvoiceFrom.toUpperCase() + invoice.InvoiceId); + + + $("#invoicedisplay").show(); + + return callback(false, "ok"); + } + + + function displayInvoiceByUser(invoiceid, username, invtype, callback) { + + var invoice = _.find(cachedInvoicesByUser, function (inv) { return inv.InvoiceId == invoiceid; }); + + //here decrypt the invoice with my private key + + Engine.UnpackInvoiceByMe(invoice, username, function (err, unpacked) { + + displayInvoiceDetails(invoice, unpacked, invtype, function (err, res) { + + callback(false, "ok"); + + }); + + }); + + } + + + + function displayInvoice(invoiceid, username, invtype, callback) { + + var invoice; + + //find by invoicefrom and invoice id + + if (invtype == 'forme') { + invoice = _.find(cachedInvoices, function (inv) { return inv.InvoiceFrom == username && inv.InvoiceId == invoiceid; }); + } else { + invoice = _.find(cachedInvoicesByUser, function (inv) { return inv.InvoiceFrom == username && inv.InvoiceId == invoiceid; }); + } + + Engine.UnpackInvoiceForMe(invoice, username, invtype, function (err, unpacked) { + + displayInvoiceDetails(invoice, unpacked, invtype, function (err, res) { + + callback(false, "ok"); + + }); + + }); + + } + + function createNewInvoice(userName, invoice, callback) { + + Engine.createInvoice(userName, invoice, function (err, invoiceNo) { + + return callback(err, invoiceNo); + + }); + + } + + + function payInvoice(friend, amount, invoiceNumber) { + + $('#textMessageSendInv').removeClass('alert alert-danger'); + $('#textMessageSendInv').html('Creating transaction...'); + $('#textMessageSendInv').show(); + $('#sendinvprogstatus').width('3%') + $('#sendinvprog').show(); + $('#sendinvprogstatus').width('10%'); + + var pin = $('#sendinvpin').val(); + + Engine.getDeviceKey(pin, function (err, ekey) { + + if (!err) { + + Engine.sendTransaction('invoice', friend, '', amount, ekey, function (err, transactionid) { + + if (!err) { + + Engine.updateInvoice(friend, invoiceNumber, transactionid, 1, function (err, result) { + + if (!err) { + + $('#textMessageSendInv').html('You paid invoice: ' + friend.toUpperCase() + invoiceNumber); + $('#textMessageSendInv').fadeOut(5000); + $('#sendinvprog').fadeOut(5000); + + //$("#invoicedisplay").hide(); + //$("#invoicestopay").show(); + //$("#createinv").show(); + + updateBalance(); + lastInvoiceToPayCount = 0; + showInvoiceList(); + + //change status + var statusbox = ' Paid'; + $("#invdisstatus").html(statusbox); + + + //hide buttons + $("#payinvoicecancel").hide(); + $("#btnpayinvoice").hide(); + $("#btnrejectinvoice").hide(); + + //show ok + $("#btnokinvoice").show(); + + if (uiInvoiceReturnToNetwork) { + updateSelectedFriend(); + } + } + + }); + + + } else { + + $('#textMessageSendInv').addClass('alert alert-danger'); + $('#sendinvprogstatus').width('0%') + + if (transactionid == "ErrInsufficientFunds") { + $('#textMessageSendInv').html('Transaction Failed: Waiting for funds to clear'); + } + + } + + }); + + } else { + + $('#textMessageSendInv').addClass('alert alert-danger'); + $('#sendinvprogstatus').width('0%') + $('#textMessageSendInv').html(ekey); + + } + + }); + + } + + + //INVOICE FUNCTIONS END------------------------------------------ + + + //OPEN/CREATE WALLET FUNCTIONS--------------------------------------------- + + //event handlers + + + + //wrapper functions + + function openWallet(guid, password, twoFactorCode, callback) { + + setCookie('guid', guid); + + Engine.openWallet(guid, twoFactorCode, function (err, result) { + + if (err) { + + return callback(err, result); + + } else { + + if (result.Beta12fa) { + + + //if we are migrating a beta1 account that uses 2fa + //request the 2fa code + + $("#si2fa").show(); + return callback(err, result); + + } else { + + if (result.TwoFactorOnLogin) { + return callback(err, result); + } else { + initialiseUI(); + } + } + + } + }); + + } + + function initialiseUI() { + + + $("#dashprofile").show(); + $("#dashsend").hide(); + $("#dashreceive").hide(); + $("#dashcontact").hide(); + $('#invoices').hide(); + $('#network').hide(); + $('#networklist').hide(); + $('#settings').hide(); + + var length = Engine.m_nickname.length; + if (length > 20) { + length = 20; + } + + COINUNIT = Engine.m_settings.CoinUnit; + + $("#mynickname").html(Engine.m_nickname); + $("#usernameProfile").html(Engine.m_nickname); + $("#mystatus").html(Engine.m_statusText); + + + var imageSrc = "images/avatar/128px/Avatar-" + pad(length) + ".png"; + var imageSrcSmall = "images/avatar/64px/Avatar-" + pad(length) + ".png"; + + if (Engine.m_profileImage != '') { + imageSrc = "https://ninkip2p.imgix.net/" + Engine.m_profileImage + "?crop=faces&fit=crop&h=128&w=128&mask=ellipse&border=1,d0d0d0"; + imageSrcSmall = "https://ninkip2p.imgix.net/" + Engine.m_profileImage + "?crop=faces&fit=crop&h=64&w=64&mask=ellipse&border=1,d0d0d0"; + } + + + $("#imgProfile").attr("src", imageSrc); + $("#imgtoprightprofile").attr("src", imageSrcSmall); + + //$("#codeForFriend").html(Engine.m_fingerprint); + + + var data = Engine.m_fingerprint + ',' + Engine.m_nickname; + var options = { text: data, width: 172, height: 172 }; + + $('#fingerprintqr').html(''); + $('#fingerprintqr').qrcode(options); + + + Engine.getusernetworkcategory(function (err, categories) { + + var catOptions = ''; + + $("#netcatoptions").html(catOptions); + $("#nselnetcat").change(function () { + + $("#nselnetcat option:selected").each(function () { + //update selected friend category + Engine.updateusernetworkcategory(SELECTEDFRIEND, $(this).text(), function (err, result) { + + //refresh friend list + if (!err) { + lastNoOfFriends = 0; + updateFriends(); + } + + }); + + }); + }); + + + if (!err) { + + + setInterval(function () { + + updateUI(); + + }, 10000); + + + updateUI(); + + + $('#showPhrases').hide(); + $("#openWalletStart").hide(); + $("#createWalletStart").hide(); + + if (Engine.m_validate) { + + $("#securitywizard").show(); + $("#step4").show(); + $("#step1").hide(); + $("#step2").hide(); + $("#step3").hide(); + $("#listep1").removeClass("active"); + $("#listep2").removeClass("active"); + $("#listep3").removeClass("active"); + $("#listep4").addClass("active"); + $("#prgsecwiz").width('100%'); + $(".next").hide(); + $(".previous").hide(); + $("#validateemail").show(); + $("#mainWallet").hide(); + + } else { + + $("#securitywizard").hide(); + $("#mainWallet").show(); + $("#validateemail").hide(); + } + + setAwayTimeout(600000); + + } + + + }); + + } + + //OPEN/CREATE WALLET FUNCTIONS END--------------------------------------------- + + function pad(n) { + return (n < 10) ? ("0" + n) : n; + } + + function logout() { + location.reload(); + } + + UI.updateUITimer = function () { + updateUI(); + } + + function updateUI() { + + $(".coinunit").html(COINUNIT); + $("#stdsendcunit").html(COINUNIT); + $("#amount").attr("placeholder", "Enter amount in units of " + COINUNIT); + $("#friendAmount").attr("placeholder", "Enter amount in units of " + COINUNIT); + $("#fndsendcunit").html(COINUNIT); + + updateBalance(function (err, res) { + + updateFriends(function (err, res) { + + showTransactionFeed(function (err, res) { + + updateFriendRequests(function (err, res) { + + + + updateTransactions(function (err, res) { + + showInvoiceList(function (err, res) { + + showInvoiceByUserList(function (err, res) { + + if (!norefresh) { + refreshSelectedFriend(function (err, res) { + + console.log('completed refresh'); + + }); + } + }); + + }); + + }); + }); + }); + }); + }); + } + + function showCreateWalletStart() { + $("#createWalletStart").hide(); + $("#openWalletStart").hide(); + $("#lostguid").hide(); + $("#reset2fa").hide(); + $("#validateemail").hide(); + $("#pairDevice").show(); + + } + + function showOpenWalletStart() { + $("#openWalletStart").hide(); + $("#createWalletStart").hide(); + $("#lostguid").hide(); + $("#reset2fa").hide(); + $("#validateemail").hide(); + $("#loginpin").show(); + + } + + function showTwoFactorQr() { + + $("#twoFactorQr").show(); + $("#2factor1").show(); + + Engine.getTwoFactorImg(function (err, twoFASecret) { + + var nickname = $("#createWalletStart input#nickname").val(); + var data = "otpauth://totp/Ninki:" + nickname + "?secret=" + twoFASecret + "&issuer=Ninki"; + var options = { text: data, width: 172, height: 172 }; + + $('#twoFactorQrImg').html(''); + $('#twoFactorQrImg').qrcode(options); + + $("#twoFactorQrImg").attr("src", twoFactorQrImgUrl); + }); + + } + + function showSettingsTwoFactorQr() { + + $("#setup2faqr").show(); + $("#setting2fa").show(); + $("#settings2fa").show(); + $("#btnSetupTwoFactor").hide(); + + + + Engine.getTwoFactorImg(function (err, twoFASecret) { + + var data = "otpauth://totp/Ninki:" + Engine.m_nickname + "?secret=" + twoFASecret + "&issuer=Ninki"; + var options = { text: data, width: 172, height: 172 }; + + $('#imgsettings2fa').html(''); + $('#imgsettings2fa').qrcode(options); + }); + + + } + + //Download settings from server and populate input boxes + function readAccountSettingsFromServerAndPopulateForm() { + + $("#savesettingserror").hide(); + $("#savesettingssuccess").hide(); + + if (Engine.m_twoFactorOnLogin) { + $("#settings2faok").show(); + $("#TwoFactorCodeForSettings").show(); + $("#TwoFactorCodeForChangePwd").show(); + $("#DailyTransactionLimit").prop('disabled', false); + $("#SingleTransactionLimit").prop('disabled', false); + $("#NoOfTransactionsPerDay").prop('disabled', false); + $("#NoOfTransactionsPerHour").prop('disabled', false); + $("#btnSetupTwoFactor").hide(); + + } else { + $("#settings2faok").hide(); + $("#TwoFactorCodeForSettings").hide(); + $("#TwoFactorCodeForChangePwd").hide(); + $("#DailyTransactionLimit").prop('disabled', true); + $("#SingleTransactionLimit").prop('disabled', true); + $("#NoOfTransactionsPerDay").prop('disabled', true); + $("#NoOfTransactionsPerHour").prop('disabled', true); + } + + Engine.getAccountSettings(function (err, response) { + if (err) { + + } else { + + var settingsObject = JSON.parse(response); + + $('#DailyTransactionLimit').val(convertFromSatoshis(settingsObject['DailyTransactionLimit'], COINUNIT)); + $('#SingleTransactionLimit').val(convertFromSatoshis(settingsObject['SingleTransactionLimit'], COINUNIT)); + $('#NoOfTransactionsPerDay').val(settingsObject['NoOfTransactionsPerDay']); + $('#NoOfTransactionsPerHour').val(settingsObject['NoOfTransactionsPerHour']); + $('#Inactivity').val(settingsObject['Inactivity']); + $('#MinersFee').val(convertFromSatoshis(settingsObject['MinersFee'], COINUNIT)); + $('#CoinUnit').val(settingsObject['CoinUnit']); + $('#Email').val(settingsObject['Email']); + $('#EmailNotification').prop('checked', settingsObject['EmailNotification']); + + + if (settingsObject['CoinUnit'] == 'BTC') { + $('#cuSelected').html('BTC'); + $('#cuBTC').prop('checked', true); + } + if (settingsObject['CoinUnit'] == 'mBTC') { + $('#cuSelected').html('mBTC'); + $('#cumBTC').prop('checked', true); + } + if (settingsObject['CoinUnit'] == 'uBTC') { + $('#cuSelected').html('μBTC'); + $('#cuuBTC').prop('checked', true); + } + if (settingsObject['CoinUnit'] == 'Bits') { + $('#cuSelected').html('Bits'); + $('#cuuBits').prop('checked', true); + } + + + } + }); + } + + + function saveAccountSettingsToServer() { + var jsonPacket = { + guid: Engine.m_guid + }; + + jsonPacket['DailyTransactionLimit'] = convertToSatoshis($('#DailyTransactionLimit').val(), COINUNIT); + jsonPacket['SingleTransactionLimit'] = convertToSatoshis($('#SingleTransactionLimit').val(), COINUNIT); + jsonPacket['NoOfTransactionsPerDay'] = $('#NoOfTransactionsPerDay').val(); + jsonPacket['NoOfTransactionsPerHour'] = $('#NoOfTransactionsPerHour').val(); + jsonPacket['Inactivity'] = $('#Inactivity').val(); + jsonPacket['MinersFee'] = convertToSatoshis($('#MinersFee').val(), COINUNIT); + + if ($('#cuSelected').html() == 'BTC') { + jsonPacket['CoinUnit'] = 'BTC'; + } else if ($('#cuSelected').html() == 'mBTC') { + jsonPacket['CoinUnit'] = 'mBTC'; + } else if ($('#cuSelected').html() == 'Bits') { + jsonPacket['CoinUnit'] = 'Bits'; + } else { + jsonPacket['CoinUnit'] = 'uBTC'; + } + + jsonPacket['Email'] = $('#Email').val(); + jsonPacket['EmailNotification'] = $('#EmailNotification')[0].checked; + + Engine.updateAccountSettings(jsonPacket, $("#txtTwoFactorCodeForSettings").val(), function (err, response) { + if (err) { + $("#savesettingserror").show(); + $("#savesettingssuccess").hide(); + $("#savesettingserrormessage").html(response); + } else { + + + if (jsonPacket['CoinUnit'] != COINUNIT) { + COINUNIT = jsonPacket['CoinUnit']; + lastNoOfTrans = -1; + updateUI(); + readAccountSettingsFromServerAndPopulateForm(); + } + + $("#savesettingssuccess").show(); + $("#savesettingserror").hide(); + $("#savesettingssuccessmessage").html("Settings saved successfully"); + + //alert(response.body); + } + }); + } + + function convertFromSatoshis(amount, toUnit) { + + if (toUnit == 'BTC') { + amount = amount / 100000000; + } + + if (toUnit == 'mBTC') { + amount = amount / 100000; + } + + if (toUnit == 'uBTC') { + amount = amount / 100; + } + + if (toUnit == 'Bits') { + amount = amount / 100; + } + + + return amount; + } + + function convertToSatoshis(amount, fromUnit) { + + if (fromUnit == 'BTC') { + amount = amount * 100000000; + } + + if (fromUnit == 'mBTC') { + amount = amount * 100000; + } + + if (fromUnit == 'uBTC') { + amount = amount * 100; + } + + if (fromUnit == 'Bits') { + amount = amount * 100; + } + + amount = Math.round(amount) + return amount; + } + + function updateBalance(callback) { + + Engine.getBalance(function (err, result) { + + //get in BTC units + var balance = convertFromSatoshis(result.TotalBalance, COINUNIT); + + // var xhr = new XMLHttpRequest(); + // xhr.open('GET', "https://api.bitcoinaverage.com/ticker/global/USD/last", false); + // xhr.send(); + + // var xccy = xhr.responseText / 100000000; + // var xccy = (result.TotalBalance * xccy).toFixed(2); + + + //$("#balance").html(balance); + $("#balanceTop").html(balance + " " + COINUNIT); + //$("#balanceTop").html(balance + " " + COINUNIT + " ($" + (xccy) + ")"); + //$("#dashcoinunit").html(COINUNIT); + var template = ''; + if (result.UnconfirmedBalance > 0) { + template += ''; + } else { + template += ''; + } + + $("#balancetimer").html(template); + + if (callback) { + callback(); + } + + + + }); + + //$("#balance").html(Math.floor((Math.random() * 100) + 1) + " BTC"); + //$("#message").fadeToggle(3000); + + } + + function updateNetwork() { + + // getNewFriends(); + //updateFriendRequests(); + getNewFriends(); + } + + + + var previousReqByMe = 0; + function updateRequestsMadeByMe(callback) { + + + Engine.getPendingUserRequests(function (err, friends) { + + + if (friends.length != previousReqByMe) { + previousReqByMe = friends.length; + $("#requestssent").html(''); + for (var i = 0; i < friends.length; i++) { + + var length = friends[i].userName.length; + if (length > 20) { + length = 20; + } + + var template = '
  • ' + + '
    ' + + '' + + '
    ' + + '' + + '
    ' + + '
    ' + + '
    ' + friends[i].userName + '
    ' + + 'I love Ninki!' + + '
    ' + + '
    ' + + '
  • '; + + $("#requestssent").append(template); + } + } + + if (friends.length > 0) { + $("#requestsentpanel").show(); + } else { + $("#requestsentpanel").hide(); + } + + if (callback) { + + callback(); + + } + + }); + + + + + } + + + var lastNoOfFriends = 0; + var invoiceSelectedUser = ''; + var invoiceSelectedAmount = 0; + var selectedFriend = null; + + function updateFriends(callback) { + + if (!noAlert == true) { + + + Engine.getUserNetwork(function (err, friends) { + + //$("#nfriends").html(friends.length); + + if (friends.length > lastNoOfFriends) { + + lastNoOfFriends = friends.length; + + FRIENDSLIST = {}; + + for (var i = 0; i < friends.length; i++) { + FRIENDSLIST[friends[i].userName] = friends[i]; + } + + //if selected friend is not isend and isreceive + //then find in list and update + + if (selectedFriend != null) { + + if (!selectedFriend.ICanSend || !selectedFriend.ICanReceive) { + selectedFriend = FRIENDSLIST[selectedFriend.userName]; + updateSelectedFriend(); + } + + } + + + $("#nfriends").html(friends.length); + $("#myfriends").html(''); + + + var grouptemplate = ''; + + var friendsgroup = _.groupBy(friends, function (item) { return item.category; }) + + grouptemplate += '
    '; + + var k = 0; + var g = 1; + for (var key in friendsgroup) { + + friends = friendsgroup[key]; + + grouptemplate += '
    '; + grouptemplate += '
    '; + grouptemplate += ''; + grouptemplate += key; + grouptemplate += ''; + grouptemplate += '
    '; + grouptemplate += '
    '; + + + for (var i = 0; i < friendsgroup[key].length; i++) { + + var frnd = FRIENDSLIST[friends[i].userName]; + + var length = frnd.userName.length; + if (length > 20) { + length = 20; + } + + + var imageSrc = "images/avatar/64px/Avatar-" + pad(length) + ".png"; + + if (frnd.profileImage != '') { + imageSrc = "https://ninkip2p.imgix.net/" + frnd.profileImage + "?crop=faces&fit=crop&h=256&w=256&mask=ellipse&border=1,d0d0d0"; + imageSrcSmall = "https://ninkip2p.imgix.net/" + frnd.profileImage + "?crop=faces&fit=crop&h=128&w=128&mask=ellipse&border=1,d0d0d0"; + } + + + var template = '
    ' + + '
    '; + + if (frnd.validated) { + template += '
    ' + + '' + + '
    '; + } + + template += '
    ' + + '
    ' + friends[i].userName + '
    ' + + '' + frnd.status + '' + + '
    ' + + '
    '; + + + grouptemplate += template; + + + + k++; + } + + + grouptemplate += '
    '; + grouptemplate += '
    '; + g++; + } + + grouptemplate += '
    '; + + $("#myfriends").html(grouptemplate); + + + var k = 0; + var g = 1; + for (var key in friendsgroup) { + + friends = friendsgroup[key]; + for (var i = 0; i < friendsgroup[key].length; i++) { + + friends = friendsgroup[key]; + + + //var btnfriend = $("#myfriends #friend" + k).get(); + //var hammertime = new Hammer(btnfriend[0]); + + $("#myfriends #friend" + k).hammer(null).bind("tap", { userName: friends[i].userName }, function (ev) { + + SELECTEDFRIEND = ev.data.userName; + selectedFriend = FRIENDSLIST[ev.data.userName]; + + + networkpagestate = "friend"; + friendpagestate = "send" + + $('#myfriends').hide(); + $("#networklist").hide(); + $("#pnlfriendinv").hide(); + + window.scrollTo(0, 0); + + //depreciate + + + updateSelectedFriend(); + }); + + console.log("added click " + k + " for " + friends[i].userName); + + k++; + } + g++; + } + + } + + return callback(false, "done"); + }); + } + + } + + function showSecret() { + + + } + + function refreshSelectedFriend(callback) { + + if (SELECTEDFRIEND.length > 0) { + + Engine.getFriend(SELECTEDFRIEND, function (err, friend) { + if (!norefresh) { + if (SELECTEDFRIEND == friend.userName) { + selectedFriend = friend; + FRIENDSLIST[SELECTEDFRIEND] = friend; + + if (selectedFriend.ICanSend) { + $("#issend").show(); + //$("#networksend").show(); + } else { + $("#issend").hide(); + //$("#networksend").hide(); + } + if (selectedFriend.ICanReceive) { + $("#isreceive").show(); + } else { + $("#isreceive").hide(); + } + + var imageSrc = "images/avatar/256px/Avatar-" + pad(SELECTEDFRIEND.length) + ".png"; + + if (selectedFriend.profileImage != '') { + imageSrc = "https://ninkip2p.imgix.net/" + selectedFriend.profileImage + "?crop=faces&fit=crop&h=256&w=256&mask=ellipse&border=1,d0d0d0"; + } + if (selectedFriend.status != '') { + $("#friendSelectedStatus").html(selectedFriend.status); + } + + $("#imgSelectedFriend").attr("src", imageSrc); + + + if (selectedFriend.validated) { + $("#validateform").hide(); + $("#isvalidated").show(); + $("#networkvalidate").hide(); + $("#btnSendToFriend").prop('disabled', false); + $("#btnSendToFriend").removeClass('disabled'); + $("#friendvalreq").hide(); + + } else { + $("#validateform").show(); + $("#isvalidated").hide(); + $("#networkvalidate").show(); + $("#btnSendToFriend").prop('disabled', true); + $("#btnSendToFriend").addClass('disabled'); + $("#friendvalreq").show(); + } + + callback(err, friend); + + //updateSelectedFriend(function (err, res) { + // selFriendBkgUpdate = false; + // callback(err, res); + //}); + } + } + }); + + } + + } + + + + + function updateSelectedFriend(callback) { + + //can optimise futher + norefresh = true; + if (SELECTEDFRIEND.length > 0) { + + + $('input#friendAmount').val(''); + + + var length = selectedFriend.userName.length; + if (length > 20) { + length = 20; + } + + $("#nselnetcat").val(selectedFriend.category); + + $('#friendempty').hide(); + + $('#textMessageSend').removeClass('alert alert-danger'); + + + + $('#textMessageSend').hide(); + $('#sendfriendprog').hide(); + + $("#networkvalidate").show(); + $("#friendSelectedName").html(selectedFriend.userName); + $("#friendSelectedNameTo").html(selectedFriend.userName); + $("#validateusername2").html(selectedFriend.userName); + $("#validateusername3").html(selectedFriend.userName); + $("#validateusername4").html(selectedFriend.userName); + $("#validateusername5").html(selectedFriend.userName); + $("#validatesuccess").hide(); + $("#validatefail").hide(); + + if (selectedFriend.ICanSend) { + $("#issend").show(); + $("#networksend").show(); + } else { + $("#issend").hide(); + $("#networksend").hide(); + } + if (selectedFriend.ICanReceive) { + $("#isreceive").show(); + } else { + $("#isreceive").hide(); + } + + var imageSrc = "images/avatar/256px/Avatar-" + pad(length) + ".png"; + + if (selectedFriend.profileImage != '') { + imageSrc = "https://ninkip2p.imgix.net/" + selectedFriend.profileImage + "?crop=faces&fit=crop&h=256&w=256&mask=ellipse&border=1,d0d0d0"; + } + $("#friendSelectedStatus").html(''); + if (selectedFriend.status != '') { + $("#friendSelectedStatus").html(selectedFriend.status); + } + + $("#imgSelectedFriend").attr("src", imageSrc); + + + if (selectedFriend.validated) { + $("#validateform").hide(); + $("#isvalidated").show(); + $("#networkvalidate").hide(); + $("#btnSendToFriend").prop('disabled', false); + $("#btnSendToFriend").removeClass('disabled'); + $("#friendvalreq").hide(); + + } else { + $("#validateform").show(); + $("#isvalidated").hide(); + $("#networkvalidate").show(); + $("#btnSendToFriend").prop('disabled', true); + $("#btnSendToFriend").addClass('disabled'); + $("#friendvalreq").show(); + } + + + + $('#tblnetinvbyme tbody').empty(); + $('#tblnetinvforme tbody').empty(); + + lastInvoiceToPayNetCount = 0; + lastInvoiceByMeNetCount = 0; + showInvoiceListNetwork(); + showInvoiceByMeListNetwork(); + + $("#pnlfriend").show(); + + } + + norefresh = false; + + + + + if (callback) { + callback(false, "ok"); + } + + } + + + var lastNoOfFriendsReq = 0; + + function updateFriendRequests(callback) { + + //if there are any new friends + //fade in the button + + //to do, move to handlebars templates + Engine.getFriendRequests(function (err, ofriends) { + + + //if origin of friend request is qrcode + //and no phrase cache + //delay for 60 seconds + //add accept with scan button + //then return to standard after 5 minutes + + var friends = []; + for (var i = 0; i < ofriends.length; i++) { + + if (contactPhraseCache[ofriends[i].userName]) { + acceptAndValidateFriend(ofriends[i].userName); + } else { + friends.push(ofriends[i]); + } + + } + + + if (friends.length > 0) { + $("#dashrequests").show(); + + } else { + $("#dashrequests").hide(); + } + + $("#notifications").html(friends.length); + $("#notificationsright").html(friends.length); + //$("#nfriendreq").html(friends.length); + + + + + if (lastNoOfFriendsReq != friends.length || friends.length == 0) { + + lastNoOfFriendsReq = friends.length; + + if (friends.length > 0) { + $("#notifications").attr("class", "badge bg-danger pull-right"); + } else { + $("#notifications").attr("class", "badge pull-right"); + } + $("#nfriendreq").html(friends.length); + $("#friendreq").html(''); + for (var i = 0; i < friends.length; i++) { + + var length = friends[i].userName.length; + if (length > 20) { + length = 20; + } + + var template = '
  • ' + + 'John said' + + '' + + '
    ' + + '' + friends[i].userName + '' + + '
    Accept Reject' + + '
  • '; + + $("#friendreq").append(template); + $("#btnaccept" + i).button(); + + + + } + + for (var i = 0; i < friends.length; i++) { + + + $("#friendreq #btnaccept" + i).click({ + userName: friends[i].userName + }, function (event) { + acceptAndValidateFriend(event.data.userName, function (err, result) { + + + + }); + }); + + + } + + + + } + if (callback) { + callback(false, "done"); + } + }); + + } + + function acceptAndValidateFriend(username, callback) { + + + acceptFriend(username, function (err, res) { + + //handle here instead + + console.log('accept contact'); + + console.log(contactPhraseCache[username]); + console.log(err); + + if (!err) { + + + lastNoOfFriendsReq = 0; + updateFriendRequests(); + + $("#imgrequestwaiting").hide(); + + if (contactPhraseCache[username]) { + + console.log('found phrase'); + console.log(contactPhraseCache[username]) + + var code = contactPhraseCache[username]; + var bip39 = new BIP39(); + code = bip39.mnemonicToHex(code); + + if (code.length == 40) { + + Engine.verifyFriendData(username, code, function (err, result) { + + if (result) { + + console.log('verified'); + console.log(result); + + lastNoOfFriends = 0; + updateFriends(); + + //updateSelectedFriend(); + + //update list also + + //find friend in list and update the validated icon + //$("#myfriends #seltarget" + selectedFriend.userName).html('
    '); + + } + + }); + + } + + //get the hash to validate against + //this will confirm that my friend has the same keys + //i orginally packaged for him + + + } + + } + + + }); + } + + var prevtransfeed = -1; + function showTransactionFeed(callback) { + + Engine.getTransactionRecords(function (err, transactions) { + + + if (transactions.length != prevtransfeed) { + + + prevtransfeed = transactions.length; + + var template = ''; + $('#transfeed').empty(); + + for (var i = 0; i < transactions.length && i < 5; i++) { + + + var trdate = new Date(transactions[i].TransDateTime.match(/\d+/)[0] * 1).toLocaleString(); + + var dirTemplate = ""; + if (transactions[i].TransType == 'S') { + dirTemplate = convertFromSatoshis(transactions[i].Amount, COINUNIT) + ' ' + COINUNIT + '
    Sent
    '; + } + if (transactions[i].TransType == 'R') { + dirTemplate = convertFromSatoshis(transactions[i].Amount, COINUNIT) + ' ' + COINUNIT + '
    Received
    '; + } + + if (transactions[i].Confirmations < 6) { + dirTemplate += ''; + } else { + dirTemplate += ''; + } + dirTemplate += ""; + + var length = transactions[i].UserName.length; + if (length > 20) { + length = 20; + } + + var imageSrcSmall = "images/avatar/32px/Avatar-" + pad(length) + ".png"; + + if (transactions[i].UserName != 'External') { + if (FRIENDSLIST[transactions[i].UserName].profileImage != '') { + imageSrcSmall = "https://ninkip2p.imgix.net/" + FRIENDSLIST[transactions[i].UserName].profileImage + "?crop=faces&fit=crop&h=32&w=32&mask=ellipse&border=1,d0d0d0"; + } + } + + var tref = transactions[i].UserName; + + if (transactions[i].UserName == 'External') { + tref = transactions[i].Address.substring(0, 7) + '...'; + } + + template += '
    ' + + dirTemplate + '
    ' + trdate + '
    John said ' + + tref + '
    '; + + } + + $('#transfeed').html(template); + + } else { + + $('#transfeed .pull-right').each(function (index, elem) { + + var tran = allTransactions[transactionIndex[transactions[index].TransactionId]]; + + var dirTemplate = ""; + if (tran.TransType == 'S') { + dirTemplate = convertFromSatoshis(tran.Amount, COINUNIT) + ' ' + COINUNIT + '
    Sent
    '; + } + if (tran.TransType == 'R') { + dirTemplate = convertFromSatoshis(tran.Amount, COINUNIT) + ' ' + COINUNIT + '
    Received
    '; + } + + if (tran.Confirmations < 6) { + dirTemplate += ' ' + tran.Confirmations + ''; + } else { + dirTemplate += ''; + } + + $(elem).html(dirTemplate); + + + }); + } + + return callback(err, "ok"); + + }); + + } + + + + var lastNoOfTrans = 0; + + function updateTransactions(callback) { + + //if there are any new friends + //fade in the button + + + Engine.getTransactionRecords(function (err, transactions) { + + allTransactions = transactions; + + if (allTransactions.length != lastNoOfTrans) { + + } else { + + transactions = pagedTransactions; + + //we ony need to update the confirmations + + $('#tbltran tbody tr .bcconf').each(function (index, elem) { + + var tran = allTransactions[transactionIndex[transactions[index].TransactionId]]; + + if (tran.Confirmations < 6) { + $(elem).html('
    ' + tran.Confirmations + '
    '); + } else { + $(elem).html('
    '); + } + + }); + + return callback(false, "ok"); + } + + + for (var i = 0; i < allTransactions.length; i++) { + var d1 = new Date(allTransactions[i].TransDateTime); + allTransactions[i].JsDate = d1; + transactionIndex[allTransactions[i].TransactionId] = i; + } + //first convert to javascript dates + + filteredTransactions = allTransactions; + //apply current filter currentTransactionFilter + + if (currentTransactionFilter == "Day") { + var lastDay = new Date(); + lastDay = lastDay.setDate(lastDay.getDate() - 1); + filteredTransactions = _.filter(allTransactions, function (trans) { return trans.JsDate > lastDay; }); + } + + if (currentTransactionFilter == "Week") { + var lastWeek = new Date(); + lastWeek = lastWeek.setDate(lastWeek.getDate() - 7); + filteredTransactions = _.filter(allTransactions, function (trans) { return trans.JsDate > lastWeek; }); + } + + if (currentTransactionFilter == "Month") { + var lastMonth = new Date(); + lastMonth = lastMonth.setDate(lastMonth.getDate() - 31); + filteredTransactions = _.filter(allTransactions, function (trans) { return trans.JsDate > lastMonth; }); + } + + if (currentTransactionFilter == "Search") { + var search = $('#txttransearch').val(); + filteredTransactions = _.filter(allTransactions, function (trans) { return trans.UserName.search(search) > -1; }); + } + + if (currentTransactionSort == 'DateDesc') { + filteredTransactions = _.sortBy(filteredTransactions, function (trans) { return -trans.JsDate; }); + } + + if (currentTransactionSort == 'DateAsc') { + filteredTransactions = _.sortBy(filteredTransactions, function (trans) { return trans.JsDate; }); + } + + if (currentTransactionSort == 'ContactAsc') { + filteredTransactions = _.sortBy(filteredTransactions, function (trans) { return trans.UserName; }); + } + + if (currentTransactionSort == 'ContactDesc') { + filteredTransactions = _.sortBy(filteredTransactions, function (trans) { return trans.UserName; }); + filteredTransactions.reverse(); + } + + var noofpages = Math.floor((filteredTransactions.length / transactionsPerPage)); + + var indexFrom = currentPageIndex * transactionsPerPage; + var indexTo = indexFrom + transactionsPerPage; + + if (indexTo > filteredTransactions.length) { + indexTo = filteredTransactions.length; + } + + $('#tranpaglabel').html('Showing ' + (indexFrom + 1) + ' to ' + (indexTo) + ' of ' + filteredTransactions.length); + + transactions = filteredTransactions; + + if (allTransactions.length != lastNoOfTrans) { + + pagedTransactions = filteredTransactions.slice(indexFrom, indexTo); + + transactions = pagedTransactions; + + lastNoOfTrans = allTransactions.length; + + var template = ''; + $('#tbltran tbody').empty(); + for (var i = 0; i < transactions.length; i++) { + + var dirTemplate = ""; + if (transactions[i].TransType == 'S') { + dirTemplate = '' + convertFromSatoshis(transactions[i].Amount, COINUNIT) + ' ' + COINUNIT + ''; + } + if (transactions[i].TransType == 'R') { + dirTemplate = '' + convertFromSatoshis(transactions[i].Amount, COINUNIT) + ' ' + COINUNIT + ''; + } + + var length = transactions[i].UserName.length; + if (length > 20) { + length = 20; + } + + + + var imageSrcSmall = "images/avatar/64px/Avatar-" + pad(length) + ".png"; + + if (transactions[i].UserName != 'External') { + if (FRIENDSLIST[transactions[i].UserName].profileImage != '') { + imageSrcSmall = "https://ninkip2p.imgix.net/" + FRIENDSLIST[transactions[i].UserName].profileImage + "?crop=faces&fit=crop&h=64&w=64&mask=ellipse&border=1,d0d0d0"; + } + } + + var tref = transactions[i].UserName; + + if (transactions[i].UserName == 'External') { + tref = transactions[i].Address.substring(0, 7) + '...'; + } + + + var trdate = new Date(transactions[i].TransDateTime.match(/\d+/)[0] * 1).toLocaleString(); + + + template += '' + + '' + + '' + trdate + '' + + '' + + 'John said ' + + tref + '' + + dirTemplate + + ''; + + if (transactions[i].Confirmations < 6) { + template += '
    ' + transactions[i].Confirmations + '
    '; + } else { + template += '
    '; + } + template += ''; + template += '
    '; + + template += ''; + + } + + $('#tbltran tbody').append(template); + + for (var i = 0; i < transactions.length; i++) { + + + var trdate = new Date(transactions[i].TransDateTime.match(/\d+/)[0] * 1).toLocaleString(); + + var popcontent = ''; + + popcontent += '

    Date: '; + popcontent += trdate; + popcontent += '

    '; + + popcontent += '

    TransactionId

    '; + popcontent += '

    '; + popcontent += transactions[i].TransactionId; + popcontent += '

    '; + + popcontent += '

    Address: '; + popcontent += transactions[i].Address; + popcontent += '

    '; + + popcontent += '

    Amount: '; + popcontent += convertFromSatoshis(transactions[i].Amount, COINUNIT) + ' '; + popcontent += COINUNIT + '

    '; + + popcontent += '

    Send/Receive: '; + popcontent += transactions[i].TransType; + popcontent += '

    '; + + $("#btnpop" + i).popover({ + placement: 'left', // top, bottom, left or right + title: 'Transaction Details', + html: 'true', + content: '
    ' + popcontent + '
    ' + }); + } + + } + + callback(); + + }); + + } + + + + + function generateAddressClient() { + + $("#newaddrspinner").show(); + var target = document.getElementById('newaddrspinner'); + var spinner = new Spinner(spinneropts).spin(target); + + Engine.createAddress('m/0/0', 1, function (err, newAddress, path) { + + var options = { text: newAddress, width: 172, height: 172 }; + + $('#requestaddressqr').html(''); + $('#requestaddressqr').qrcode(options); + + $('#requestaddresstxt').html(newAddress); + + //$('#requestaddress').html(tempate); + $("#newaddrspinner").hide(); + $('#requestaddress').show(); + + + }); + + } + + + function sendMoney(friend, index) { + + $('#textMessageSend').removeClass('alert alert-danger'); + if (friend == null) { + return; + } + + var amount = $('input#friendAmount').val(); + amount = convertToSatoshis(amount, COINUNIT); + + + var pin = $('#sendfriendpin').val(); + + if (amount > 0) { + + $('input#friendAmount').css("border-color", "#ccc"); + $('#textMessageSend').html('Creating transaction...'); + $('#textMessageSend').show(); + $('#sendfriendprogstatus').width('3%') + $('#sendfriendprog').show(); + $('#sendfriendprogstatus').width('10%'); + + + + Engine.getDeviceKey(pin, function (err, ekey) { + + if (!err) { + + Engine.sendTransaction('friend', friend, "", amount, ekey, function (err, transactionid) { + + if (!err) { + updateBalance(); + $('#textMessageSend').html('You sent ' + convertFromSatoshis(amount, COINUNIT) + ' ' + COINUNIT + ' to ' + friend); + $('input#friendAmount').val(''); + $('#textMessageSend').fadeOut(5000); + $('#sendfriendprog').fadeOut(5000); + } else { + $('#textMessageSend').addClass('alert alert-danger'); + $('#sendfriendprogstatus').width('0%') + + if (transactionid == "ErrInsufficientFunds") { + $('#textMessageSend').html('Transaction Failed: Waiting for funds to clear'); + } + + } + // alert(transactionid); + }); + + } else { + + $('#textMessageSend').addClass('alert alert-danger'); + $('#sendfriendprogstatus').width('0%') + $('#textMessageSend').html(ekey); + + } + + }); + + } else { + $('input#friendAmount').css("border-color", "#ffaaaa"); + } + + + } + + + function sendMoneyStd() { + + + //get pin from user + //get device key + //pass the device key as the 2fa code + + var amount = $('input#amount').val(); + amount = convertToSatoshis(amount, COINUNIT); + + var address = $('input#toAddress').val(); + + $('#textMessageSendStd').removeClass('alert alert-danger'); + //check for valid bitcoin address + + var allok = true; + if (Engine.isAddressValid(address)) { + $('input#toAddress').css("border-color", "#ccc"); + } else { + $('input#toAddress').css("border-color", "#ffaaaa"); + allok = false; + } + if (amount > 0) { + $('input#amount').css("border-color", "#ccc"); + } else { + $('input#amount').css("border-color", "#ffaaaa"); + allok = false; + } + + var pin = $('#sendstdpin').val(); + + if (allok) { + + Engine.getDeviceKey(pin, function (err, ekey) { + + if (!err) { + + $('#textMessageSendStd').html('Creating transaction...'); + $('#textMessageSendStd').show(); + $('#sendstdprogstatus').width('3%') + $('#sendstdprog').show(); + $('#sendstdprogstatus').width('10%'); + + + Engine.sendTransaction('standard', '', address, amount, ekey, function (err, transactionid) { + + if (!err) { + + $('#textMessageSendStd').html('You sent ' + convertFromSatoshis(amount, COINUNIT) + ' ' + COINUNIT + ' to ' + address + ''); + $('input#amount').val(''); + $('#textMessageSendStd').fadeOut(5000); + $('#sendstdprog').fadeOut(5000); + + } else { + + if (transactionid == "ErrInsufficientFunds") { + $('#textMessageSendStd').html('Transaction Failed: Waiting for funds to clear'); + } + + $('#sendstdprogstatus').width('0%') + $('#textMessageSendStd').addClass('alert alert-danger'); + } + }); + + + + } else { + + $('#sendstdprogstatus').width('0%') + $('#textMessageSendStd').addClass('alert alert-danger'); + $('#textMessageSendStd').html(ekey); + } + + + + }); + } + + } + + $("#hdqrcontact").change(function () { + + console.log('event triggered'); + console.log($("#hdqrcontact").val()); + + var res = $("#hdqrcontact").val(); + var sres = res.split(','); + var phrase = sres[0]; + var username = sres[1]; + + console.log(phrase); + console.log(username); + + contactPhraseCache[username] = phrase; + + $("#addcontactmodal").show(); + + $("#imgaddcontactwaiting").show(); + var target = document.getElementById('imgaddcontactwaiting'); + var spinner = new Spinner(spinneropts).spin(target); + + + + $("#qrcontactupd").show(); + $("#qrcontactmess").html('Verifying user...'); + + + Engine.doesUsernameExist(username, function (err, usernameExistsOnServer) { + + //also check if friend already + + if (usernameExistsOnServer) { + + $("#qrcontactmess").html('Verifying network...'); + + Engine.isNetworkExist(username, function (err, result) { + + if (!result) { + + $("#qrcontactmess").html('Deriving addresses...'); + + Engine.createFriend(username, "#qrcontactmess", function (err, result) { + if (err) { + + $("#addcontactmodal").hide(); + $("#imgaddcontactwaiting").hide(); + $("#qrcontactalert").show(); + $("#qrcontactalertmessage").html("Error adding contact"); + + + } else { + $("#imgaddcontactwaiting").hide(); + $("#addcontactmodal").hide(); + $("#qrcontactsuccess").show(); + $("#qrcontactsuccessmessage").html("Successfully added " + username + " as a contact"); + $("#qrcontactsuccess").fadeOut(5000); + + } + }); + + } else { + + + //validate using the fingerprint + + if (contactPhraseCache[username]) { + + console.log('found phrase'); + console.log(contactPhraseCache[username]) + + var code = contactPhraseCache[username]; + var bip39 = new BIP39(); + code = bip39.mnemonicToHex(code); + + if (code.length == 40) { + $("#qrcontactmess").html('Verifying user...'); + Engine.verifyFriendData(username, code, function (err, result) { + + if (result) { + $("#qrcontactmess").html('Verfied...'); + console.log('verified'); + console.log(result); + $("#imgaddcontactwaiting").hide(); + $("#addcontactmodal").hide(); + $("#qrcontactsuccess").show(); + $("#qrcontactsuccessmessage").html("You verfied " + username + " as a contact"); + $("#qrcontactsuccess").fadeOut(5000); + + lastNoOfFriends = 0; + updateFriends(); + + } + + }); + + } + + //get the hash to validate against + //this will confirm that my friend has the same keys + //i orginally packaged for him + + + } + + + } + }); + + } else { + + + + } + }); + + + + + }); + + + //class="modal-open" + //addcontactmodal + + function addFriend(username) { + + + if (username.length == 0 || Engine.m_nickname == username) { + $("#friend").css("border-color", "#ffaaaa"); + return; + } + + $("#addcontactmodal").show(); + + $("#imgaddcontactwaiting").show(); + var target = document.getElementById('imgaddcontactwaiting'); + var spinner = new Spinner(spinneropts).spin(target); + + + //verify input and if username exists + $("#addcontactalert").hide(); + + + $("#qrcontactupd").show(); + $("#qrcontactmess").html('Verifying user...'); + + //merge these functions + + Engine.doesUsernameExist(username, function (err, usernameExistsOnServer) { + + //also check if friend already + + if (usernameExistsOnServer) { + + $("#qrcontactmess").html('Verifying network...'); + + Engine.isNetworkExist(username, function (err, result) { + + if (!result) { + + $("#friend").css("border-color", "#ccc"); + + $("#qrcontactmess").html('Deriving addresses...'); + + Engine.createFriend(username, "#qrcontactmess", function (err, result) { + if (err) { + + $("#friend").css("border-color", "#ffaaaa"); + $("#addcontactalert").show(); + $("#addcontactalertmessage").html("Error adding contact"); + $("#imgaddcontactwaiting").hide(); + + } else { + + $("#addcontactmodal").hide(); + $("#friend").val(''); + $("#imgaddcontactwaiting").hide(); + $("#addcontactsuccess").show(); + $("#addcontactsuccessmessage").html("You requested " + username + " as a contact"); + $("#addcontactsuccess").fadeOut(5000); + + //updateRequestsMadeByMe(); + } + }); + + } else { + + $("#friend").css("border-color", "#ffaaaa"); + $("#addcontactalert").show(); + $("#addcontactalertmessage").html("You have already requested " + username + " as a contact"); + $("#imgaddcontactwaiting").hide(); + + } + }); + + } else { + + $("#friend").css("border-color", "#ffaaaa"); + $("#addcontactalert").show(); + $("#addcontactalertmessage").html("The username could not be found"); + $("#imgaddcontactwaiting").hide(); + + } + }); + + + } + + + function rejectFriend(username) { + + Engine.rejectFriendRequest(username, function (err, result) { + + updateFriendRequests(); + + }); + } + + function acceptFriend(username, callback) { + + + console.log('calling accept acceptFriend'); + + $("#imgrequestwaiting").show(); + var target = document.getElementById('imgrequestwaiting'); + var spinner = new Spinner(spinneropts).spin(target); + + //$('#friendreq').fadeOut(1000); + Engine.acceptFriendRequest(username, function (err, secret) { + if (err) { + //alert("Wallet could not be opened.\n\n" + err); + } else { + console.log('accepted'); + Engine.isNetworkExist(username, function (err, result) { + + if (!result) { + + Engine.createFriend(username, "", function (err, result) { + + if (err) { + + } else { + + return callback(err, result); + } + }); + + } else { + + return callback(err, result); + + } + + }); + } + + }); + + + } + + + function ensureOpenWalletGuidAndPasswordValid() { + + if (Engine.isRealGuid($("#openWalletStart input#guid").val())) { + $("#openWalletStart input#guid").css("border-color", "#ccc"); + } else { + $("#openWalletStart input#guid").css("border-color", "#ffaaaa"); + } + + if ($("#openWalletStart input#password").val().length == 0) { + $("#openWalletStart input#password").css("border-color", "#ffaaaa"); + } else { + $("#openWalletStart input#password").css("border-color", "#ccc"); + } + + if (!Engine.isRealGuid($("#openWalletStart input#guid").val()) || + $("#openWalletStart input#password").val().length == 0) { + + return false; + } + + return true; + } + + function ensureCreateWalletGuidNicknameAndPasswordValid() { + + if (Engine.isRealGuid($("#createWalletStart input#guid").val())) { + $("#createWalletStart input#guid").css("border-color", "#ccc"); + } else { + $("#createWalletStart input#guid").css("border-color", "#ffaaaa"); + } + + //TODO Check nickname does not already exist + if ($("#createWalletStart input#nickname").val().length == 0 || $("#createWalletStart input#nickname").val().length > 20) { + $("#createWalletStart input#nickname").css("border-color", "#ffaaaa"); + } else { + $("#createWalletStart input#nickname").css("border-color", "#ccc"); + } + + if (!Engine.isRealGuid($("#createWalletStart input#guid").val()) || + $("#createWalletStart input#cpassword").val().length == 0 || + $("#createWalletStart input#nickname").val().length == 0 || + $("#createWalletStart input#nickname").val().length > 20) { + + return false; + } else { + return true; + } + + } + + function validate() { + + if (Engine.isRealGuid($("input#guid").val())) { + $("input#guid").css("background-color", "#ffffff"); + } else { + $("input#guid").css("background-color", "#ffaaaa"); + } + + if (!Engine.isRealGuid($("input#guid").val()) || + $("input#password").val().length == 0) { + + return false; + } + + return true; + } + +} + +module.exports = UI; diff --git a/src/ninki-ui.js b/src/ninki-ui.js index 50a7fd4..ca064b1 100644 --- a/src/ninki-ui.js +++ b/src/ninki-ui.js @@ -16,11 +16,14 @@ function UI() { // var oguid = ''; // var password = ''; - var FRIENDSLIST = {}; var COINUNIT = 'BTC'; var SELECTEDFRIEND = ''; var noAlert = false; + var norefresh = false; + + var price = 0; + var trasactionFilterOn = false; var allTransactions = []; var filteredTransactions = []; @@ -30,6 +33,22 @@ function UI() { var currentTransactionSort = 'DateDesc'; var transactionsPerPage = 10; var currentPageIndex = 0; + var transactionIndex = {}; + + var filteredByMeInvoices = []; + var pagedByMeInvoices = []; + var invoicesByMePerPage = 10; + var currentByMeInvoicePageIndex = 0; + + var filteredForMeInvoices = []; + var pagedForMeInvoices = []; + var invoicesForMePerPage = 10; + var currentForMeInvoicePageIndex = 0; + + var currentInvoiceFilter = ''; + var currentByMeInvoiceFilter = ''; + var invoiceFilterOn = false; + var invoiceByMeFilterOn = false; var ua = window.navigator.userAgent; @@ -66,24 +85,97 @@ function UI() { }; + function setCookie(cname, cvalue, exdays) { - var d = new Date(); - d.setTime(d.getTime() + (exdays * 24 * 60 * 60 * 1000)); - var expires = "expires=" + d.toGMTString(); - document.cookie = cname + "=" + cvalue + "; " + expires; + + + if (isChromeApp()) { + + var obj = {}; + obj[cname] = cvalue; + chrome.storage.local.set(obj, function () { + + console.log("saved"); + + }); + + } + else { + + localStorage[cname] = cvalue; + + } + + + } + + function isChromeApp() { + + var is_chrome = navigator.userAgent.toLowerCase().indexOf('chrome') > -1; + if (is_chrome) { + if (chrome) { + if (chrome.app) { + if (chrome.app.runtime) { + return true; + } + } + } + } + return false; + } + + + function getCookie(cname, callback) { + + + if (isChromeApp()) { + + chrome.storage.local.get(cname, function (result) { + + if (result[cname]) { + result = result[cname]; + } else { + result = ""; + } + + return callback(result); + + }); + + } else { + + if (localStorage[cname]) { + return callback(localStorage[cname]); + } else { + return callback(''); + } + + } + } - function getCookie(cname) { - var name = cname + "="; - var ca = document.cookie.split(';'); - for (var i = 0; i < ca.length; i++) { - var c = ca[i].trim(); - if (c.indexOf(name) == 0) return c.substring(name.length, c.length); + function deleteCookie(cname) { + + + if (isChromeApp()) { + + chrome.storage.local.remove(cname, function () { + + console.log("deleted"); + + }); + + } else { + + localStorage.removeItem(cname); + } - return ""; } + + + function getLocalTime(datetime) { var timestamp = datetime, @@ -108,6 +200,373 @@ function UI() { jQuery(document).ready(function () { + var fatokk = getCookie("ninki_rem", function (res) { + + if (res.length > 0) { + $('#twofacenable').show(); + } + + }); + + + $("#btnSaveHotKey").click(function () { + + $('#hotkeyentererror').hide(); + + var hotkey = $('#txtHot').val(); + var hotkeydecode = Engine.decodeKey(hotkey); + if (hotkeydecode) { + Engine.saveHotHash(hotkeydecode, function (err, result) { + + //show modal enter phrase + if (!err) { + $("#hotkeymodal").modal('hide'); + $("#hotkeyenter").hide(); + } else { + $('#hotkeyentererror').show(); + $('#hotkeyentererrormessage').text(result); + } + + }); + } else { + $('#hotkeyentererror').show(); + $('#hotkeyentererrormessage').text("The phrase was invalid, please double check the phrase and try again"); + } + + }); + + $("#btnWatchOnly").click(function () { + + $("#hotkeymodal").modal('hide'); + $("#hotkeyenter").hide(); + + }); + + + $("#hunlock").click(function () { + + + if (isChromeApp()) { + chrome.app.window.create('recover_v1.html', { + 'state': 'maximized' + }); + } else { + location.href = "recover_v1.html"; + } + + + }); + + $("#logout").click(function () { + + logout(); + + }); + + + + $("#twofacenable").click(function () { + + deleteCookie("ninki_rem"); + $('#twofacenable').hide(); + }); + + + $('#imgProfileContainer').show(); + $("#dropzone").hide(); + $("#btnSaveProfile").hide(); + $("#btnCancelProfile").hide(); + $("#statusedit").hide(); + $("#imgreset").hide(); + + + $("#btnEditProfile").click(function () { + + key = ''; + + $('#imgProfileContainer').hide(); + $("#dropzone").show(); + $("#btnSaveProfile").show(); + $("#btnCancelProfile").show(); + $("#btnEditProfile").hide(); + $("#statusedit").show(); + $("#profnmests").hide(); + $("#imgreset").show(); + $("#txtStatusText").val(Engine.m_statusText); + + }); + + $("#imgreset").click(function () { + + Engine.m_profileImage = ""; + imgReset = true; + + var imageSrc = "images/avatar/256px/Avatar-" + pad(Engine.m_nickname.length) + ".png"; + + if (isChromeApp()) { + var xhrsm = new XMLHttpRequest(); + xhrsm.open('GET', imageSrc, true); + xhrsm.responseType = 'blob'; + xhrsm.onload = function (e) { + $("#imgProfile").attr("src", window.URL.createObjectURL(this.response)); + }; + xhrsm.send(); + } else { + $("#imgProfile").attr("src", imageSrc); + } + + + $('#imgProfileContainer').show(); + $('#dropzone').hide(); + $('progressNumber').text(''); + $("#imgreset").hide(); + + }); + + var imgReset = false; + $("#btnSaveProfile").click(function () { + + //updateUserProfile + + var statusText = $("#txtStatusText").val(); + + if (key == '' && !imgReset) { + key = Engine.m_profileImage; + } + + Engine.updateUserProfile(key, statusText, Engine.m_invoiceTax, function (err, result) { + + if (!err) { + + var imageSrc = "images/avatar/256px/Avatar-" + pad(Engine.m_nickname.length) + ".png"; + var imageSrcSmall = "images/avatar/64px/Avatar-" + pad(Engine.m_nickname.length) + ".png"; + + + if (key != '' && !imgReset) { + imageSrc = "https://ninkip2p.imgix.net/" + _.escape(key) + "?crop=faces&fit=crop&h=256&w=256&mask=ellipse&border=1,d0d0d0"; + imageSrcSmall = "https://ninkip2p.imgix.net/" + _.escape(key) + "?crop=faces&fit=crop&h=64&w=64&mask=ellipse&border=1,d0d0d0"; + } + + + if (isChromeApp()) { + var xhrsm = new XMLHttpRequest(); + xhrsm.open('GET', imageSrc, true); + xhrsm.responseType = 'blob'; + xhrsm.onload = function (e) { + $("#imgProfile").attr("src", window.URL.createObjectURL(this.response)); + }; + xhrsm.send(); + + var xhrsm2 = new XMLHttpRequest(); + xhrsm2.open('GET', imageSrcSmall, true); + xhrsm2.responseType = 'blob'; + xhrsm2.onload = function (e) { + $("#imgtoprightprofile").attr("src", window.URL.createObjectURL(this.response)); + }; + xhrsm2.send(); + + } else { + $("#imgProfile").attr("src", imageSrc); + $("#imgtoprightprofile").attr("src", imageSrcSmall); + } + + $('#imgProfileContainer').show(); + $("#dropzone").hide(); + $("#btnSaveProfile").hide(); + $("#btnCancelProfile").hide(); + $("#btnEditProfile").show(); + $("#statusedit").hide(); + $("#mystatus").text(statusText); + $("#txtStatusText").val(statusText); + $("#profnmests").show(); + $("#imgreset").hide(); + imgReset = false; + $("#profileimgfile").val(''); + $("#progressNumber").val(''); + + } + + }); + + }); + + $("#btnCancelProfile").click(function () { + + $('#imgProfileContainer').show(); + $("#dropzone").hide(); + $("#btnSaveProfile").hide(); + $("#btnCancelProfile").hide(); + $("#btnEditProfile").show(); + + //reset profile image + + var imageSrc = "images/avatar/256px/Avatar-" + pad(Engine.m_nickname.length) + ".png"; + + if (Engine.m_profileImage != '') { + imageSrc = "https://ninkip2p.imgix.net/" + _.escape(Engine.m_profileImage) + "?crop=faces&fit=crop&h=256&w=256&mask=ellipse&border=1,d0d0d0"; + } + + if (isChromeApp()) { + var xhrsm = new XMLHttpRequest(); + xhrsm.open('GET', imageSrc, true); + xhrsm.responseType = 'blob'; + xhrsm.onload = function (e) { + $("#imgProfile").attr("src", window.URL.createObjectURL(this.response)); + }; + xhrsm.send(); + } else { + $("#imgProfile").attr("src", imageSrc); + } + + $("#statusedit").hide(); + $("#profnmests").show(); + $("#imgreset").hide(); + + }); + + var obj = $("#dropzone"); + + obj.click(function () { + $("#profileimgfile").click(); + }); + + $("#profileimgfile").change(function (e) { + + var control = document.getElementById("profileimgfile"); + var files = control.files; + //alert(files[0]); + //We need to send dropped files to Server + handleFileUpload(files, obj); + + }); + + obj.on('dragenter', function (e) { + e.stopPropagation(); + e.preventDefault(); + $(this).css('border', '2px solid #0B85A1'); + }); + + obj.on('dragover', function (e) { + e.stopPropagation(); + e.preventDefault(); + }); + + obj.on('drop', function (e) { + + $(this).addClass("b-dashed"); + $(this).addClass("b-light"); + e.preventDefault(); + var files = e.originalEvent.dataTransfer.files; + //alert(files[0]); + //We need to send dropped files to Server + handleFileUpload(files, obj); + }); + + + $(document).on('dragenter', function (e) { + e.stopPropagation(); + e.preventDefault(); + }); + + $(document).on('dragover', function (e) { + e.stopPropagation(); + e.preventDefault(); + obj.css('border', '2px dotted #0B85A1'); + }); + + $(document).on('drop', function (e) { + e.stopPropagation(); + e.preventDefault(); + }); + + var key = ''; + function handleFileUpload(files, obj) { + + if (files.length > 0) { + var file = files[0]; + var fd = new FormData(); + + key = "images\/" + Engine.m_nickname + '_' + (new Date).getTime() + + Engine.createS3Policy(function (err, result) { + + var policy = JSON.parse(result); + + fd.append('key', key); + fd.append('acl', 'public-read'); + fd.append('Content-Type', file.type); + fd.append('bucket', 'ninkip2pimgstore'); + fd.append('AWSAccessKeyId', 'AKIAINOU56ATQFS3CLFQ'); + fd.append('policy', policy.s3Policy); + fd.append('signature', policy.s3Signature); + fd.append("file", file); + + var xhr = new XMLHttpRequest(); + + xhr.upload.addEventListener("progress", uploadProgress, false); + xhr.addEventListener("load", uploadComplete, false); + xhr.addEventListener("error", uploadFailed, false); + xhr.addEventListener("abort", uploadCanceled, false); + + xhr.open('POST', 'https://ninkip2pimgstore.s3-us-west-1.amazonaws.com/', true); //MUST BE LAST LINE BEFORE YOU SEND + + xhr.send(fd); + + }); + } + + } + + + + function uploadProgress(evt) { + if (evt.lengthComputable) { + var percentComplete = Math.round(evt.loaded * 100 / evt.total); + document.getElementById('progressNumber').innerHTML = percentComplete.toString() + '%'; + } + else { + document.getElementById('progressNumber').innerHTML = 'unable to compute'; + } + } + + function uploadComplete(evt) { + /* This event is raised when the server send back a response */ + //alert("Done - " + evt.target.responseText); + + var imageSrc = 'https://ninkip2p.imgix.net/' + _.escape(key) + "?fit=crop&crop=faces&h=128&w=128&mask=ellipse&border=1,d0d0d0"; + + if (isChromeApp()) { + var xhrsm = new XMLHttpRequest(); + xhrsm.open('GET', imageSrc, true); + xhrsm.responseType = 'blob'; + xhrsm.onload = function (e) { + $("#imgProfile").attr("src", window.URL.createObjectURL(this.response)); + $('#imgProfileContainer').show(); + $('#dropzone').hide(); + $('progressNumber').text(''); + $("#imgreset").hide(); + }; + xhrsm.send(); + } else { + $("#imgProfile").attr("src", imageSrc); + $('#imgProfileContainer').show(); + $('#dropzone').hide(); + $('progressNumber').text(''); + $("#imgreset").hide(); + } + + + + } + + function uploadFailed(evt) { + alert("There was an error attempting to upload the file." + evt); + } + + function uploadCanceled(evt) { + alert("The upload has been canceled by the user or the browser dropped the connection."); + } + var options = {}; options.ui = { container: "#pwd-container", @@ -129,6 +588,10 @@ function UI() { }; + + + + $('#createWalletStart #cpassword').pwstrength(options); @@ -157,131 +620,706 @@ function UI() { }); + var stdAmountConvCoin = true; + var netAmountConvCoin = true; - $('#frmSaveTwoFactor').keydown(function (e) { - if (e.keyCode == 13) { - e.preventDefault(); - return false; - } - }); + function convertToLocalCurrency(amount) { + var conv = amount; + conv = conv * 1.0; - $(document).on("keydown", function (e) { - if (e.which === 8 && !$(e.target).is("input, textarea")) { - e.preventDefault(); - } - }); + var sats = convertToSatoshis(conv, COINUNIT); + var btc = convertFromSatoshis(sats, "BTC"); + var cbtc = btc * price; - $(document).ready(function () { + var loc = "en-US"; + var ires = cbtc; - $('body').on('click', function (e) { - //did not click a popover toggle or popover - if ($(e.target).data('toggle') !== 'popover' - && $(e.target).parents('.popover.in').length === 0) { - $('[data-toggle="popover"]').popover('hide'); - } - }); + if (Engine.m_settings.LocalCurrency == "JPY") { + ires = (cbtc * 1.0).toFixed(0) * 1.0; + } else { + ires = (cbtc * 1.0).toFixed(2) * 1.0; + } - //if (getCookie('guid').length == 0) { + var cprc = ires.toLocaleString(loc, { style: "currency", currency: Engine.m_settings.LocalCurrency }); - var betafrom = 'December 12, 2009 12:00 pm GMT'; - var betato = 'December 12, 2009 01:00 pm GMT'; + return cprc; - betafrom = getLocalTime(betafrom); - betato = getLocalTime(betato); + } - $('#betafrom').html(betafrom); - $('#betato').html(betato); - $('#basicModal').modal('show'); + function convertFromLocalCurrency(amount) { - $("#btnDeclineBeta").click(function () { - window.location.href = '/' - }); - //} + var conv = amount; + conv = conv * 1.0; + //convert to bitcoin + if (price > 0) { + var cbtc = conv / price; - $("#btncreatewallet").click(function () { - showCreateWalletStart(); - }); + var sats = convertToSatoshis(cbtc, "BTC"); + var btc = convertFromSatoshis(sats, COINUNIT); - $("#btnLogin").click(function () { + return btc; + } else { - if (!ensureOpenWalletGuidAndPasswordValid()) return; + return 0; - $("#imgopenwaiting").show(); - $("#btnLogin").prop('disabled', true); - $("#btnLogin").addClass('disabled'); - var target = document.getElementById('imgopenwaiting'); - var spinner = new Spinner(spinneropts).spin(target); + } - setTimeout(function () { - var guid = $('#openWalletStart input#guid').val(); + } + + function updateStdAmount() { + + if (stdAmountConvCoin) { + $('#ccystdamt').text(convertToLocalCurrency($('#amount').val())); + $('#hdamount').val($('#amount').val()); + } + else { + var amt = convertFromLocalCurrency($('#amount').val()); + $('#hdamount').val(amt); + $('#ccystdamt').text(amt + ' ' + COINUNIT); + } + + } + + function updateNetAmount() { + + if (netAmountConvCoin) { + $('#ccynetamt').text(convertToLocalCurrency($('#friendAmount').val())); + $('#hdfriendAmount').val($('#friendAmount').val()); + } + else { + var amt = convertFromLocalCurrency($('#friendAmount').val()); + $('#hdfriendAmount').val(amt); + $('#ccynetamt').text(amt + ' ' + COINUNIT); + } + + } + + + $('#stdselcu').click(function () { + + $('#stdselunit').text(COINUNIT); + stdAmountConvCoin = true; + $('#amount').val(''); + updateStdAmount(); + + }); + + $('#stdsellc').click(function () { + + $('#stdselunit').text(Engine.m_settings.LocalCurrency); + stdAmountConvCoin = false; + $('#amount').val(''); + updateStdAmount(); + + }); + + $('#netselcu').click(function () { + + $('#netselunit').text(COINUNIT); + netAmountConvCoin = true; + $('#friendAmount').val(''); + updateNetAmount(); + + }); + + $('#netsellc').click(function () { + + $('#netselunit').text(Engine.m_settings.LocalCurrency); + netAmountConvCoin = false; + $('#friendAmount').val(''); + updateNetAmount(); + + }); + + + $('#amount').keyup(function () { + + updateStdAmount(); + + }); + + $('#friendAmount').keyup(function () { + + updateNetAmount(); + + }); + + + $('#btnPassphraseLogin').keydown(function (e) { + if (e.keyCode == 13) { + e.preventDefault(); + return false; + } + }); + + $('#frmSaveTwoFactor').keydown(function (e) { + if (e.keyCode == 13) { + e.preventDefault(); + return false; + } + }); + + $('#phrase2fa').keydown(function (e) { + if (e.keyCode == 13) { + e.preventDefault(); + return false; + } + }); + + + $('.nopost').keydown(function (e) { + if (e.keyCode == 13) { + e.preventDefault(); + return false; + } + }); + + $(document).on("keydown", function (e) { + if ((e.which === 8) && !$(e.target).is("input, textarea")) { + e.preventDefault(); + } + }); + + + $(document).ready(function () { + + + $('body').on('click', function (e) { + //did not click a popover toggle or popover + if ($(e.target).data('toggle') !== 'popover' + && $(e.target).parents('.popover.in').length === 0) { + $('[data-toggle="popover"]').popover('hide'); + } + }); + + + + getCookie('guid', function (res) { + if (res.length == 0) { + + var betafrom = 'December 12, 2009 12:00 pm GMT'; + var betato = 'December 12, 2009 01:00 pm GMT'; + + betafrom = getLocalTime(betafrom); + betato = getLocalTime(betato); + + $('#betafrom').text(betafrom); + $('#betato').text(betato); + + $('#basicModal').modal('show'); + + $("#btnDeclineBeta").click(function () { + window.location.href = '/' + }); + + } + }); + + + + $("#btnAcceptBeta").click(function () { + $("#openWalletStart #password").focus(); + }); + + + $("#btncreatewallet").click(function () { + showCreateWalletStart(); + }); + + + + + + $("#btnAddDevice").click(function () { + + + + //add the device name + //a two factor bypass token will be generated plus temporary encryption key + //for loggin on + + //generate a qr code containing encrypted + //2fa token + //password + //hotkey + + var deviceName = $("#txtAddDevice").val(); + + Engine.createDevice(deviceName, function (err, result) { + + + updateDeviceList(); + + }); + + + }); + + + $("#btnPairDone").click(function () { + + lastNoOfDevices = 0; + updateDeviceList(); + + $('#qrdevice').text(''); + + $('#pairqr2fa').show(); + $('#pairqrscan').hide(); + + $("#pairdevicemodal").modal('hide'); + $("#pairdeviceqr").hide(); + + }); + + + + $("#btnCancelPairQr").click(function () { + + $('#qrdevice').text(''); + + $('#pairqr2fa').show(); + $('#pairqrscan').hide(); + + $("#pairdevicemodal").modal('hide'); + $("#pairdeviceqr").hide(); + + }); + + + $("#btnShowPairQr").click(function () { + + + var twoFactorCode = $("#pairTwoFactorCode").val(); + + + if (twoFactorCode.length == 6) { + + if (!currentDevice.IsPaired) { + + Engine.getDeviceToken(currentDevice.DeviceName, twoFactorCode, function (err, result) { + + if (!err) { + + var jresult = JSON.parse(result); + //get temporary encryption key + //enc password and iv + //hot key and iv + //2fatoken and iv + //guid + $("#pairTwoFactorCode").val(''); + + Engine.getHotHash("", function (err, hothash) { + + if (!err) { + + var data = hothash + jresult.DeviceToken; + + var encdata = Engine.encryptNp(data, jresult.DeviceKey); + + var data = encdata.toString() + '|' + encdata.iv.toString() + '|' + Engine.m_oguid + '|' + currentDevice.DeviceName + '|' + jresult.RegToken; + + var options = { text: data, width: 256, height: 256 }; + + //$('#qrdevice').text(data); + $('#qrdevice').qrcode(options); + + $('#pairqr2fa').hide(); + $('#pairqrscan').show(); + + + + } + + }); + + } else { + $('#pairerror').show(); + $('#pairerrormess').text(result); + + } + + + }); + + } else { + + //destroy the device + + + Engine.destroyDevice2fa(currentDevice.DeviceName, twoFactorCode, function (err, result) { + + if (!err) { + + lastNoOfDevices = 0; + updateDeviceList(); + + $("#pairdevicemodal").modal('hide'); + $("#pairdeviceqr").hide(); + + + } else { + $('#pairerror').show(); + $('#pairerrormess').text(result); + + } + + }); + + + } + + } + + + }); + + + + + + $("#btnPairCancel").click(function () { + + $("#pairdevicemodal").modal('hide'); + $("#pairdeviceqr").hide(); + + }); + + + + + $("#btnLogin").click(function () { + + if (!ensureOpenWalletGuidAndPasswordValid()) return; + + $("#imgopenwaiting").show(); + $("#btnLogin").prop('disabled', true); + $("#btnLogin").addClass('disabled'); + var target = document.getElementById('imgopenwaiting'); + var spinner = new Spinner(spinneropts).spin(target); + + + setTimeout(function () { + + var guid = $('#openWalletStart input#guid').val(); var password = $('#openWalletStart input#password').val(); var twoFactorCode = $('#openWalletStart input#twoFactorCode').val(); - openWallet(guid, password, twoFactorCode, function (err, result) { - $("#imgopenwaiting").hide(); - $("#btnLogin").prop('disabled', false); - $("#btnLogin").removeClass('disabled'); - if (!err) { + + Engine.setPass(password, guid); + + getCookie("ninki_rem", function (res) { + + if (res.length > 0) { + var enc = JSON.parse(res); + twoFactorCode = Engine.decryptNp(enc.ct, Engine.m_password, enc.iv); + } + + setCookie('guid', guid, 30); + + Engine.openWallet(guid, twoFactorCode, function (err, result) { + $("#imgopenwaiting").hide(); - $('#openWalletStart input#password').val(''); - } else { + $("#btnLogin").prop('disabled', false); + $("#btnLogin").removeClass('disabled'); + + if (!err) { + + $("#imgopenwaiting").hide(); + $("#openwalletalert").hide(); + + if (result.TwoFactorOnLogin) { + + //delete any old 2 factor tokens + + deleteCookie("ninki_rem"); + + if (!result.Beta12fa) { + + $('#openWalletStart input#password').val(''); + + if (result.TwoFactorOnLogin) { + $("#siguid").hide(); + $("#silguid").hide(); + $("#sipwd").hide(); + $("#si2fa").show(); + $("#sib1").hide(); + $("#sib2").show(); + $('#openWalletStart input#twoFactorCode').focus(); + + } + } else { + + //for beta1 migrations + $("#siguid").hide(); + $("#silguid").hide(); + $("#sipwd").hide(); + $("#si2fa").show(); + $("#sib1").hide(); + $("#sib2").show(); + $('#openWalletStart input#twoFactorCode').focus(); + + } + + } else { + + + + //initiate 2fa setup modal + if (!m_this.m_twoFactorOnLogin) { + $("#twofactorsettings").show(); + $("#2famodal").modal('show'); + + $("#twofactorsettings").show(); + $("#btnSetupTwoFactor").hide(); + $("#savetwofactorerror").hide(); + $("#setup2faemail").hide(); + $("#setup2faqr").show(); + + showSettingsTwoFactorQr(); + + } else { + + initialiseUI(); + + } + + } + + $("#unlockaccount").hide(); + + } else { + $("#imgopenwaiting").hide(); + $("#openwalletalert").show(); + + if (result == "ErrAccount") { + $("#openwalletalertmessage").text("Incorrect password"); + } + + if (result == "ErrLocked") { + $("#openwalletalertmessage").text("Your account has been locked."); + $("#unlockaccount").show(); + } else { + $("#unlockaccount").hide(); + } + } + + }); + + }); + }, 100); + }); + + + $("#btn2faLogin").click(function () { + + + $("#img2faopenwaiting").show(); + $("#btn2faLogin").prop('disabled', true); + $("#btn2faLogin").addClass('disabled'); + var target = document.getElementById('img2faopenwaiting'); + var spinner = new Spinner(spinneropts).spin(target); + + + setTimeout(function () { + + var twoFactorCode = $('#openWalletStart input#twoFactorCode').val(); + var rememberTwoFactor = $('#twofactorremember')[0].checked; + + Engine.openWallet2fa(twoFactorCode, rememberTwoFactor, function (err, result) { + + if (err) { + + $("#img2faopenwaiting").hide(); $("#openwalletalert").show(); - $("#openwalletalertmessage").html(result); + $("#openwalletalertmessage").text(result); + $("#btn2faLogin").prop('disabled', false); + $("#btn2faLogin").removeClass('disabled'); + } else { + + if (result.CookieToken) { + + setCookie("ninki_rem", result.CookieToken); + } + + initialiseUI(); } }); + }, 100); + + }); + + + + $("#btnEmailGuid").click(function () { + + var userName = $("#txtlostguidusername").val(); + + Engine.emailGUID(userName, function (err, response) { + + showOpenWalletStart(); + }); + + }); + + $("#btnaddfriend").click(function () { + + addFriend(); + + }); + + $("#btngenaddr").click(function () { + + generateAddressClient(); + + }); + + + $("#showopenwallet").click(function () { + + showOpenWalletStart(); + + }); + + $("#btnopenwalletrec").click(function () { + + showOpenWalletStart(); + + }); + + + $("#showopenwalletcr").click(function () { + + showOpenWalletStart(); + }); - $("#btnEmailGuid").click(function () { - var userName = $("#txtlostguidusername").val(); - Engine.emailGUID(userName, function (err, response) { - showOpenWalletStart(); - }); + + $("#btncancelmoneystd").click(function () { + + $("#sendstdmodal").modal('hide'); + $("#sendstds2").hide(); + $("#txtSendTwoFactor").val(''); + $("#sendstdprog").hide(); + $("#textMessageSendStd").hide(); }); - $("#btnaddfriend").click(function () { + $('#btnsendstddone').click(function () { - addFriend(); + $("#sendstds3").hide(); + $("#sendstdmodal").modal('hide'); + $("#txtSendTwoFactor").val(''); + $("#sendstdprog").hide(); + $("#textMessageSendStd").hide(); + + $('input#amount').keyup(); }); - $("#btngenaddr").click(function () { - generateAddressClient(); + $("#btnconfmoneystd").click(function () { - }); + var amount = $('#hdamount').val(); + amount = convertToSatoshis(amount, COINUNIT); + var address = $('input#toAddress').val(); - $("#showopenwallet").click(function () { + $("#txtSendTwoFactor").val(''); + $("#txtSendTwoFactor").css("border-color", "#ccc"); + + $('#textMessageSendStd').removeClass('alert alert-danger'); + + var allok = true; + if (Engine.isAddressValid(address)) { + $('input#toAddress').css("border-color", "#ccc"); + } else { + $('input#toAddress').css("border-color", "#ffaaaa"); + allok = false; + } + if (amount > 0) { + $('input#amount').css("border-color", "#ccc"); + } else { + $('input#amount').css("border-color", "#ffaaaa"); + allok = false; + } + if (allok) { - showOpenWalletStart(); + $('#sendstds2amt').text($('#hdamount').val() + ' ' + COINUNIT); + $('#sendstds2add').text($('#toAddress').val()); + + //$("#sendstds1").hide(); + $("#sendstds3").hide(); + $("#sendstds2").show(); + $("#sendstdmodal").modal('show'); + + } }); - $("#btnopenwalletrec").click(function () { - showOpenWalletStart(); + $("#btncancelmoneynet").click(function () { + + $("#networksend").show(); + $("#sendnetmodal").modal('hide'); + $("#txtFriendSend2FA").val(''); + $("#sendfriendprog").hide(); + $("#textMessageSend").hide(); }); + $('#btnsendnetdone').click(function () { - $("#showopenwalletcr").click(function () { + $("#networksend").show(); + $("#sendnetmodal").modal('hide'); + $("#txtSendTwoFactor").val(''); + $("#sendfriendprog").hide(); + $("#textMessageSend").hide(); - showOpenWalletStart(); + $('input#friendAmount').keyup(); + norefresh = false; }); + $("#btnconfmoneynet").click(function () { + + norefresh = true; + + var amount = $('#hdfriendAmount').val(); + amount = convertToSatoshis(amount, COINUNIT); + + $("#txtFriendSend2FA").val(''); + $('#txtFriendSend2FA').css("border-color", "#ccc"); + $('#textMessageSend').removeClass('alert alert-danger'); + + var allok = true; + if (amount > 0) { + $('input#friendAmount').css("border-color", "#ccc"); + } else { + $('input#friendAmount').css("border-color", "#ffaaaa"); + allok = false; + } + if (allok) { + $('#sendnets2amt').text($('#hdfriendAmount').val() + ' ' + COINUNIT); + $('#sendnets2add').text(SELECTEDFRIEND); + + //$("#networksend").hide(); + $("#sendnets3").hide(); + $("#sendnetmodal").modal('show'); + $("#sendnets2").show(); + } + }); + $("#btnsendmoneystd").click(function () { @@ -295,7 +1333,7 @@ function UI() { if ($("#frmcreate").parsley('validate')) { //check password strength - if (($(".password-verdict").html() == 'Strong' || $(".password-verdict").html() == 'Very Strong')) { + if (($(".password-verdict").text() == 'Strong' || $(".password-verdict").text() == 'Very Strong')) { //can we remove this check? if (ensureCreateWalletGuidNicknameAndPasswordValid()) { @@ -327,7 +1365,7 @@ function UI() { $("#imgcreatewaiting").hide(); $("#createwalletalert").show(); - $("#createwalletalertmessage").html("The username already exists"); + $("#createwalletalertmessage").text("The username already exists"); $("#btnCreate").prop('disabled', false); $("#btnCreate").removeClass('disabled'); @@ -339,7 +1377,7 @@ function UI() { $("#imgcreatewaiting").hide(); $("#createwalletalert").show(); - $("#createwalletalertmessage").html("The email address is already in use"); + $("#createwalletalertmessage").text("The email address is already in use"); $("#btnCreate").prop('disabled', false); $("#btnCreate").removeClass('disabled'); @@ -362,6 +1400,8 @@ function UI() { $('#createWalletStart input#cpassword').val(''); $('#createWalletStart input#password1').val(''); + //save the encrypted hot key in local storage + $("#hotWalletPhrase").text(result.hotWalletPhrase); $("#coldWalletPhrase").text(result.coldWalletPhrase); //$("#ninkiWalletPhrase").text(result.ninkiWalletPhrase); @@ -370,13 +1410,10 @@ function UI() { $("#showPhrases").show(); $("#securitywizard").show(); - if ($('#createWalletStart input#showQrCheckbox')[0].checked) { - //create two factor setting in databse and display qr code - $("#no2famessage").hide(); - showTwoFactorQr(); - } else { - $("#no2famessage").show(); - } + + $("#no2famessage").hide(); + showTwoFactorQr(); + } }); } @@ -385,7 +1422,7 @@ function UI() { //password not strong $("#createwalletalert").show(); - $("#createwalletalertmessage").html("Password must be Strong- ideally Very Strong"); + $("#createwalletalertmessage").text("Password must be Strong- ideally Very Strong"); } } @@ -395,11 +1432,7 @@ function UI() { $("#btnPassphraseLogin").click(function () { - var isvalid = true; - - if ($('#createWalletStart input#showQrCheckbox')[0].checked) { - isvalid = $("#phrase2fa").parsley('validate'); - } + var isvalid = $('#phrase2fa').parsley('validate'); if (isvalid) { @@ -418,7 +1451,7 @@ function UI() { $("#imgphrasewaiting").hide(); $("#phraseloginerror").show(); - $("#phraseloginerrormessage").html(result); + $("#phraseloginerrormessage").text(result); } else { @@ -456,17 +1489,16 @@ function UI() { $("#valemailerror").show(); if (response == "Expired") { - $("#valemailerrormessage").html('Your token has expired'); + $("#valemailerrormessage").text('Your token has expired'); } if (response == "Invalid") { - $("#valemailerrormessage").html('Your token is not valid'); + $("#valemailerrormessage").text('Your token is not valid'); } + + } else { - if ($('#createWalletStart input#showQrCheckbox')[0].checked) { - //TWOFACTORONLOGIN = true; - } //initialiseUI(); Engine.m_validate = false; @@ -581,7 +1613,7 @@ function UI() { if (err) { $("#reset2faerror").show(); - $("#reset2faerrormessage").html('There was an error'); + $("#reset2faerrormessage").text('There was an error'); } else { $("#validate2fareset").show(); $("#reset2fa").hide(); @@ -602,19 +1634,45 @@ function UI() { //get the wallet packet //secdisphrase + $('#displaykeys2fa').css("border-color", "#ccc"); + + var twoFactorCode = $("#displaykeys2fa").val(); + + if (twoFactorCode.length == 6) { + + Engine.getBackup(twoFactorCode, function (err, result) { + + if (!err) { + + $("#displaykeyserr").hide(); + + var ninkiPub = result.ninkiPubKey; + var phrase = result.hotHash; + + var bip39 = new BIP39(); // 'en' is the default language + var hotmnem = bip39.entropyToMnemonic(phrase); + + $("#secdisphrase").text(hotmnem); + $("#secdisninki").text(ninkiPub); + $("#secdisphrase").show(); + $("#secdisninki").show(); + $("#btnhidekeys").show(); + $("#btndisplaykeys").hide(); + + } else { - var ninkiPub = Engine.m_walletinfo.ninkiPubKey; - var phrase = Engine.m_walletinfo.hotHash; + $("#displaykeyserrmess").text(result); + $("#displaykeyserr").show(); - var bip39 = new BIP39(); // 'en' is the default language - var hotmnem = bip39.entropyToMnemonic(phrase); - $("#secdisphrase").html(hotmnem); - $("#secdisninki").html(ninkiPub); - $("#secdisphrase").show(); - $("#secdisninki").show(); - $("#btnhidekeys").show(); - $("#btndisplaykeys").hide(); + } + + }); + + } else { + + $('#displaykeys2fa').css("border-color", "#ffaaaa"); + } }); @@ -633,10 +1691,10 @@ function UI() { $("#val2fatokenerror").show(); if (response == "Expired") { - $("#val2fatokenerrormessage").html('Your token has expired'); + $("#val2fatokenerrormessage").text('Your token has expired'); } if (response == "Invalid") { - $("#val2fatokenerrormessage").html('Your token is not valid'); + $("#val2fatokenerrormessage").text('Your token is not valid'); } } else { @@ -651,7 +1709,7 @@ function UI() { }); - + //transaction filters $("#optDay").click(function () { trasactionFilterOn = true; @@ -759,14 +1817,163 @@ function UI() { } }); - $("#openWalletStart #guid").val(getCookie('guid')); - if (getCookie('guid').length > 0) { - showOpenWalletStart(); - $("#password").focus(); - } else { - showCreateWalletStart(); - } + //invoice filters + //for me + $("#optPending").click(function () { + invoiceFilterOn = true; + currentInvoiceFilter = 'Pending'; + lastInvoiceToPayCount = -1; + showInvoiceList(); + }); + + $("#optPaid").click(function () { + invoiceFilterOn = true; + currentInvoiceFilter = 'Paid'; + lastInvoiceToPayCount = -1; + showInvoiceList(); + }); + + $("#optRejected").click(function () { + invoiceFilterOn = true; + currentInvoiceFilter = 'Rejected'; + lastInvoiceToPayCount = -1; + showInvoiceList(); + }); + + $("#optClearInvoice").click(function () { + invoiceFilterOn = false; + currentInvoiceFilter = ''; + lastInvoiceToPayCount = -1; + showInvoiceList(); + }); + + $('#btnSearchInvForMe').click(function () { + currentForMeInvoicePageIndex = 0; + invoiceFilterOn = true; + currentInvoiceFilter = 'Search'; + lastInvoiceToPayCount = -1; + showInvoiceList(); + }); + + + + + + //by me + //invoice filters + $("#optByMePending").click(function () { + invoiceByMeFilterOn = true; + currentByMeInvoiceFilter = 'Pending'; + lastInvoiceByUserCount = -1; + showInvoiceByUserList(); + }); + + $("#optByMePaid").click(function () { + invoiceByMeFilterOn = true; + currentByMeInvoiceFilter = 'Paid'; + lastInvoiceByUserCount = -1; + showInvoiceByUserList(); + }); + + $("#optByMeRejected").click(function () { + invoiceByMeFilterOn = true; + currentByMeInvoiceFilter = 'Rejected'; + lastInvoiceByUserCount = -1; + showInvoiceByUserList(); + }); + + $("#optByMeClear").click(function () { + invoiceByMeFilterOn = false; + currentByMeInvoiceFilter = ''; + lastInvoiceByUserCount = -1; + showInvoiceByUserList(); + }); + + $('#btnSearchInvByMe').click(function () { + currentByMeInvoicePageIndex = 0; + invoiceByMeFilterOn = true; + currentByMeInvoiceFilter = 'Search'; + lastInvoiceByUserCount = -1; + showInvoiceByUserList(); + }); + + $('#ibmpagfirst').click(function () { + currentByMeInvoicePageIndex = 0; + lastInvoiceByMeNetCount = -1; + showInvoiceByUserList(); + }); + + $('#ibmpaglast').click(function () { + currentByMeInvoicePageIndex = Math.floor((filteredByMeInvoices.length / invoicesByMePerPage)); + lastInvoiceByMeNetCount = -1; + showInvoiceByUserList(); + }); + + $('#ibmpagnext').click(function () { + if (currentByMeInvoicePageIndex < Math.floor((filteredByMeInvoices.length / invoicesByMePerPage))) { + currentByMeInvoicePageIndex = currentByMeInvoicePageIndex + 1; + lastInvoiceByMeNetCount = -1; + showInvoiceByUserList(); + } + }); + + $('#ibmpagprev').click(function () { + if (currentByMeInvoicePageIndex > 0) { + currentByMeInvoicePageIndex = currentByMeInvoicePageIndex - 1; + lastInvoiceByMeNetCount = -1; + showInvoiceByUserList(); + } + }); + + + //for me + + $('#ifmpagfirst').click(function () { + currentForMeInvoicePageIndex = 0; + lastInvoiceToPayCount = -1; + showInvoiceList(); + }); + + $('#ifmpaglast').click(function () { + currentForMeInvoicePageIndex = Math.floor((filteredForMeInvoices.length / invoicesForMePerPage)); + lastInvoiceToPayCount = -1; + showInvoiceList(); + }); + + $('#ifmpagnext').click(function () { + if (currentForMeInvoicePageIndex < Math.floor((filteredForMeInvoices.length / invoicesForMePerPage))) { + currentForMeInvoicePageIndex = currentForMeInvoicePageIndex + 1; + lastInvoiceToPayCount = -1; + showInvoiceList(); + } + }); + + $('#ifmpagprev').click(function () { + if (currentForMeInvoicePageIndex > 0) { + currentForMeInvoicePageIndex = currentForMeInvoicePageIndex - 1; + lastInvoiceToPayCount = -1; + showInvoiceList(); + } + }); + + + getCookie('guid', function (res) { + + $("#openWalletStart #guid").val(res); + + if (res.length > 0) { + + showOpenWalletStart(); + + } else { + + showCreateWalletStart(); + + } + }) + + $("#password").keypress(function (e) { if (e.which == 13) { @@ -774,9 +1981,10 @@ function UI() { } }); + $("#twoFactorCode").keypress(function (e) { if (e.which == 13) { - $("#btnLogin").click(); + $("#btn2faLogin").click(); } }); @@ -803,7 +2011,7 @@ function UI() { $(".popover.fade.bottom.in").show(); }); - $("#balance").html("... BTC"); + $("#balance").text("... BTC"); $("#mainWallet").hide(); $('#message').hide(); $("#openwalletalert").hide(); @@ -819,7 +2027,9 @@ function UI() { $("#btnhidekeys").hide(); $("#btnSendToFriend").click(function () { + sendMoney(SELECTEDFRIEND, 0); + }); $("#sendfriendprog").hide(); @@ -888,32 +2098,33 @@ function UI() { $("#chngpwdprog").show(); $("#chngpwdprogmess").show(); $("#chngpwdprogbar").width('10%'); - $("#chngpwdprogmess").html('Getting packet...'); + $("#chngpwdprogmess").text('Getting packet...'); if (oldpassword != newpassword) { //check password strength - if (($(".newpwstrength_viewport_progress .password-verdict").html() == 'Strong' || $(".newpwstrength_viewport_progress .password-verdict").html() == 'Very Strong')) { + if (($(".newpwstrength_viewport_progress .password-verdict").text() == 'Strong' || $(".newpwstrength_viewport_progress .password-verdict").text() == 'Very Strong')) { //stretch old password //verify that it matches the current one $("#chngpwdprogbar").width('20%'); - $("#chngpwdprogmess").html('Getting details...'); + $("#chngpwdprogmess").text('Getting details...'); setTimeout(function () { - Engine.ChangePassword(twoFactorCode, oldpassword, newpassword, "#chngpwdprogbar", "#chngpwdprogmess", function (err, results) { + Engine.ChangePassword(twoFactorCode, oldpassword, newpassword, "#chngpwdprogbar", "#chngpwdprogmess", false, '', '', function (err, results) { if (err) { $("#chngpwerr").show(); - $("#chngpwerrmess").html(results); + $("#chngpwerrmess").text(results); $("#chngpwdprogmess").hide(); $("#chngpwdprog").hide(); } else { password = results; + $("#chngpwerr").hide(); $("#chngpwdprogbar").width('100%'); $("#chngpwdprog").hide(); @@ -936,7 +2147,7 @@ function UI() { } else { $("#chngpwerr").show(); - $("#chngpwerrmess").html("Passwords are the same. Password not updated"); + $("#chngpwerrmess").text("Passwords are the same. Password not updated"); $("#chngpwdprogmess").hide(); $("#chngpwdprog").hide(); } @@ -972,8 +2183,15 @@ function UI() { $("#validatesuccess").show(); $("#txtCode").val(''); selectedFriend.validated = true; + FRIENDSLIST[selectedFriend.userName].validated = true; updateSelectedFriend(); + //update list also + + //find friend in list and update the validated icon + $("#myfriends #seltarget" + selectedFriend.userName).html('
    '); + + } else { $("#validatefail").show(); } @@ -1009,14 +2227,14 @@ function UI() { if (!err) { //ok - $("#setup2faemail").show(); - $("#setup2faqr").hide(); - readAccountSettingsFromServerAndPopulateForm(); + logout(); + + } else { //error $("#savetwofactorerror").show(); $("#savetwofactorerrormessage").show(); - $("#savetwofactorerrormessage").html(result); + $("#savetwofactorerrormessage").text(result); } }); @@ -1080,9 +2298,70 @@ function UI() { $("#invoice").hide(); $("#invoicedisplay").hide(); + + $("#btncancelmoneyinv").click(function () { + + $("#invmodal").modal('hide'); + + }); + + $("#btnsendinvdone").click(function () { + + $("#invmodal").modal('hide'); + + }); + + + $("#btnpayinvoice").click(function () { - payInvoice(selectedInvoiceUserName, selectedInvoiceAmount, selectedInvoiceId); + $('input#txtInvoice2FA').css("border-color", "#ccc"); + $('input#txtInvoice2FA').val(''); + + $("#sendinvprog").hide(); + $("#textMessageSendInv").hide(); + + $("#sendinvs2").hide(); + $("#sendinvs2").show(); + $("#invmodal").modal('show'); + + + }); + + $("#btnSendInvoice").click(function () { + + var allok = true; + var twoFactorCode = $('#txtInvoice2FA').val(); + + if (twoFactorCode.length == 6) { + $('input#txtInvoice2FA').css("border-color", "#ccc"); + } else { + $('input#txtInvoice2FA').css("border-color", "#ffaaaa"); + allok = false; + } + + if (allok) { + + + + $('#textMessageSendInv').removeClass('alert alert-danger'); + $('#textMessageSendInv').text('Creating transaction...'); + $('#textMessageSendInv').show(); + $('#sendinvprogstatus').width('3%') + $('#sendinvprog').show(); + $('#sendinvprogstatus').width('10%'); + + payInvoice(selectedInvoiceUserName, selectedInvoiceAmount, selectedInvoiceId, twoFactorCode, function (err, result) { + + if (!err) { + + $("#sendinvs2").hide(); + $("#sendinvs3").show(); + } + + + }); + } }); @@ -1158,9 +2437,9 @@ function UI() { calcInvoiceTotals(); - var subtotal = $("#tblinvoice tfoot th #subtotal").html(); - var tax = $("#tblinvoice tfoot th #tax").html(); - var total = $("#tblinvoice tfoot th #total").html(); + var subtotal = $("#tblinvoice tfoot th #subtotal").text(); + var tax = $("#tblinvoice tfoot th #tax").text(); + var total = $("#tblinvoice tfoot th #total").text(); if (subtotal > 0) { @@ -1171,6 +2450,7 @@ function UI() { var tmpVals = []; $(this).find('input').each(function () { tmpVals.push($(this).val()); + }); if (tmpVals[0] != null) { invoicelines.push({ @@ -1200,7 +2480,20 @@ function UI() { $("#hnetwork").click(); - updateSelectedFriend(); + lastInvoiceByUserCount = -1; + lastInvoiceByMeNetCount = -1; + + showInvoiceByUserList(function (err, result) { + + showInvoiceByMeListNetwork(function (err, result) { + + + }); + + }); + + + //updateSelectedFriend(); }); } @@ -1216,7 +2509,7 @@ function UI() { uiInvoiceReturnToNetwork = true; invoiceSelectedUser = SELECTEDFRIEND; lineCount = 0 - $("#createinvoiceforlabel").html('Create an Invoice for ' + SELECTEDFRIEND); + $("#createinvoiceforlabel").text('Create an Invoice for ' + SELECTEDFRIEND); $("#tblinvoice tbody").empty(); $("#friendselector").hide(); @@ -1258,15 +2551,23 @@ function UI() { line: lineCount }, function (event) { if (validateInvoice()) { - $('#lineTotal' + (event.data.line)).html(($('#line' + (event.data.line) + 'Amount').val() * $('#line' + (event.data.line) + 'Quantity').val()).toFixed(4)); + $('#lineTotal' + (event.data.line)).text(($('#line' + (event.data.line) + 'Amount').val() * $('#line' + (event.data.line) + 'Quantity').val()).toFixed(4)); calcInvoiceTotals(); } else { //$('#line' + (event.data.line) + 'Amount'). } }); - $('#line' + lineCount + 'Quantity').blur(function (event) { - validateInvoice(); + + $('#line' + lineCount + 'Quantity').blur({ + line: lineCount + }, function (event) { + if (validateInvoice()) { + $('#lineTotal' + (event.data.line)).text(($('#line' + (event.data.line) + 'Amount').val() * $('#line' + (event.data.line) + 'Quantity').val()).toFixed(4)); + calcInvoiceTotals(); + } else { + //$('#line' + (event.data.line) + 'Amount'). + } }); @@ -1278,7 +2579,9 @@ function UI() { line: lineCount }, function (e) { if (e.which == 13) { + $('#line' + lineCount + 'Amount').blur(); $("#addline").click(); + } }); @@ -1298,22 +2601,50 @@ function UI() { }); - //openWallet(); + + $("#openWalletStart #password").focus(); + }); + + $('#txtTax').blur(function () { + + var tax = $('#txtTax').val(); + tax = tax.replace('%', ''); + + if (!jQuery.isNumeric(tax)) { + tax = 10; + $('#txtTax').val(tax) + } else { + var savetax = tax / 100; + + Engine.updateUserProfile(Engine.m_profileImage, Engine.m_statusText, savetax, function (err, res) { + + + }); + } + + calcInvoiceTotals(); + $('#txtTax').val(tax + '%'); }); function calcInvoiceTotals() { + + var tax = $('#txtTax').val(); + tax = tax.replace('%', '') * 1; + tax = tax / 100; + + var subTotal = 0; $('#tblinvoice .lineTotal').each(function () { - subTotal += ($(this).html() * 1); + subTotal += ($(this).text() * 1); }); - $("#subtotal").html(subTotal.toFixed(4)); - $("#tax").html((subTotal * 0.10).toFixed(4)); - $("#total").html((subTotal + (subTotal * 0.10)).toFixed(4)); + $("#subtotal").text(subTotal.toFixed(4)); + $("#tax").text((subTotal * tax).toFixed(4)); + $("#total").text((subTotal + (subTotal * tax)).toFixed(4)); }; @@ -1398,12 +2729,12 @@ function UI() { if (isValid) { - + calcInvoiceTotals(); //if (subTotal > 0) { - $("#subtotal").html(subTotal.toFixed(4)); - $("#tax").html((subTotal * 0.10).toFixed(4)); - $("#total").html((subTotal + (subTotal * 0.10)).toFixed(4)); + //$("#subtotal").text(subTotal.toFixed(4)); + //$("#tax").text((subTotal * 0.10).toFixed(4)); + //$("#total").text((subTotal + (subTotal * 0.10)).toFixed(4)); //} else { // isValid = false; //} @@ -1425,7 +2756,7 @@ function UI() { '' + '' + '' + - '' + + '' + '' + '' + '' + @@ -1437,27 +2768,83 @@ function UI() { - return template; - } + return template; + } + + + var cachedInvoices = []; + var lastInvoiceToPayCount = 0; + function showInvoiceList(callback) { + //get back the list of invoices to pay + + Engine.getInvoiceList(function (err, invoices) { + + + if (lastInvoiceToPayCount < invoices.length) { + + } else { + + return callback(false, "done"); + + } + + for (var i = 0; i < invoices.length; i++) { + var d1 = new Date(invoices[i].InvoiceDate); + invoices[i].JsDate = d1; + } + + invoices = _.sortBy(invoices, function (inv) { return -inv.JsDate; }); + + filteredForMeInvoices = invoices; + + //perform all filters then set back to invoices + + if (currentInvoiceFilter == "Pending") { + + filteredForMeInvoices = _.filter(invoices, function (inv) { return inv.InvoiceStatus == 0; }); + } + + if (currentInvoiceFilter == "Paid") { + + filteredForMeInvoices = _.filter(invoices, function (inv) { return inv.InvoiceStatus == 1; }); + } + + if (currentInvoiceFilter == "Rejected") { + filteredForMeInvoices = _.filter(invoices, function (inv) { return inv.InvoiceStatus == 2; }); + } - var cachedInvoices = []; - var lastInvoiceToPayCount = 0; - function showInvoiceList() { - //get back the list of invoices to pay - Engine.getInvoiceList(function (err, invoices) { + if (currentInvoiceFilter == "Search") { + var search = $('#txtSearchInvForMe').val(); + filteredForMeInvoices = _.filter(invoices, function (inv) { return inv.InvoiceFrom.search(search) > -1; }); + } - for (var i = 0; i < invoices.length; i++) { - var d1 = new Date(invoices[i].InvoiceDate); - invoices[i].JsDate = d1; + var noofpages = Math.floor((filteredForMeInvoices.length / invoicesForMePerPage)); + + var indexFrom = currentForMeInvoicePageIndex * invoicesForMePerPage; + var indexTo = indexFrom + invoicesForMePerPage; + + if (indexTo > filteredForMeInvoices.length) { + indexTo = filteredForMeInvoices.length; } - invoices = _.sortBy(invoices, function (inv) { return -inv.JsDate; }); + $('#invbmpaglabel').text('Showing ' + (indexFrom + 1) + ' to ' + (indexTo) + ' of ' + filteredForMeInvoices.length); + + + invoices = filteredForMeInvoices; if (lastInvoiceToPayCount < invoices.length) { + + + + + pagedForMeInvoices = filteredForMeInvoices.slice(indexFrom, indexTo); + + invoices = pagedForMeInvoices; + cachedInvoices = []; lastInvoiceToPayCount = invoices.length; @@ -1468,6 +2855,12 @@ function UI() { cachedInvoices.push(invoices[i]); + var invdate = new Date(invoices[i].InvoiceDate.match(/\d+/)[0] * 1).toLocaleString(); + var invpaydate = ''; + if (invoices[i].InvoicePaidDate) { + invpaydate = new Date(invoices[i].InvoicePaidDate.match(/\d+/)[0] * 1).toLocaleString(); + } + var statusbox = ''; if (invoices[i].InvoiceStatus == 0) { statusbox = ' Pending'; @@ -1479,14 +2872,13 @@ function UI() { statusbox = ' Rejected'; } + s += "" + _.escape(invdate) + ""; - var length = invoices[i].InvoiceFrom.length; - if (length > 20) { - length = 20; - } + s += "\"\" "; - s += "" + invoices[i].InvoiceDate + "\"\" " + - invoices[i].InvoiceFrom + "" + statusbox + "" + invoices[i].InvoicePaidDate + ""; + s += _.escape(invoices[i].InvoiceFrom) + ""; + + s += "" + statusbox + "" + _.escape(invpaydate) + ""; } $('#tblinvoicepay tbody').append(s); @@ -1501,9 +2893,45 @@ function UI() { //$('#hinvoices').click(); }); }); + + + var length = invoices[i].InvoiceFrom.length; + if (length > 20) { + length = 20; + } + + var imageSrcSmall = "images/avatar/64px/Avatar-" + pad(length) + ".png"; + + + if (FRIENDSLIST[invoices[i].InvoiceFrom].profileImage != '') { + imageSrcSmall = "https://ninkip2p.imgix.net/" + _.escape(FRIENDSLIST[invoices[i].InvoiceFrom].profileImage) + "?crop=faces&fit=crop&h=64&w=64&mask=ellipse&border=1,d0d0d0"; + } + + + if (isChromeApp()) { + var xhrsm = new XMLHttpRequest(); + xhrsm.open('GET', imageSrcSmall, true); + xhrsm.responseType = 'blob'; + xhrsm.index = i; + xhrsm.onload = function (e) { + $("#tblinvoicepay #imginvoice" + this.index).attr("src", window.URL.createObjectURL(this.response)); + }; + xhrsm.send(); + } else { + + //can we use chrome method here? + $("#tblinvoicepay #imginvoice" + i).attr("src", imageSrcSmall); + } + + } } + + if (callback) { + callback(); + } + }); } @@ -1511,63 +2939,78 @@ function UI() { var lastInvoiceToPayNetCount = 0; var uiInvoiceReturnToNetwork = false; - function showInvoiceListNetwork() { + function showInvoiceListNetwork(callback) { - var invoices = _.filter(cachedInvoices, function (inv) { return inv.InvoiceFrom == SELECTEDFRIEND; }); + if (SELECTEDFRIEND != '') { - if (invoices.length == 0) { - $('#tblnetinvforme tbody').empty(); - $('#tblnetinvforme').hide(); - } + var invoices = _.filter(cachedInvoices, function (inv) { return inv.InvoiceFrom == SELECTEDFRIEND; }); - if (lastInvoiceToPayNetCount < invoices.length) { + if (invoices.length == 0) { + $('#tblnetinvforme tbody').empty(); + $('#tblnetinvforme').hide(); + } - lastInvoiceToPayNetCount = invoices.length; - var s = ''; - $('#tblnetinvforme tbody').empty(); + if (lastInvoiceToPayNetCount < invoices.length) { - for (var i = 0; i < invoices.length; i++) { + lastInvoiceToPayNetCount = invoices.length; - var statusbox = ''; - if (invoices[i].InvoiceStatus == 0) { - statusbox = ' Pending'; - } - else if (invoices[i].InvoiceStatus == 1) { - statusbox = ' Paid'; - } - else if (invoices[i].InvoiceStatus == 2) { - statusbox = ' Rejected'; - } + var s = ''; + $('#tblnetinvforme tbody').empty(); - s += "" + invoices[i].InvoiceDate + "" + - "" + statusbox + "" + invoices[i].InvoicePaidDate + ""; - } + for (var i = 0; i < invoices.length; i++) { - $('#tblnetinvforme tbody').append(s); + var invdate = new Date(invoices[i].InvoiceDate.match(/\d+/)[0] * 1).toLocaleString(); + var invpaydate = ''; + if (invoices[i].InvoicePaidDate) { + invpaydate = new Date(invoices[i].InvoicePaidDate.match(/\d+/)[0] * 1).toLocaleString(); + } - for (var i = 0; i < invoices.length; i++) { + var statusbox = ''; + if (invoices[i].InvoiceStatus == 0) { + statusbox = ' Pending'; + } + else if (invoices[i].InvoiceStatus == 1) { + statusbox = ' Paid'; + } + else if (invoices[i].InvoiceStatus == 2) { + statusbox = ' Rejected'; + } - $("#tblnetinvforme #viewinvoicenetfrom" + invoices[i].InvoiceFrom + invoices[i].InvoiceId).click({ - index: invoices[i].InvoiceId, username: invoices[i].InvoiceFrom - }, function (event) { - displayInvoice(event.data.index, event.data.username, 'forme', function (err, res) { - uiInvoiceReturnToNetwork = true; - $('#hinvoices').click(); + s += "" + _.escape(invdate) + "" + + "" + statusbox + "" + _.escape(invpaydate) + ""; + } + + $('#tblnetinvforme tbody').append(s); + + for (var i = 0; i < invoices.length; i++) { + + $("#tblnetinvforme #viewinvoicenetfrom" + _.escape(invoices[i].InvoiceFrom) + _.escape(invoices[i].InvoiceId)).click({ + index: invoices[i].InvoiceId, username: invoices[i].InvoiceFrom + }, function (event) { + displayInvoice(event.data.index, event.data.username, 'forme', function (err, res) { + uiInvoiceReturnToNetwork = true; + $('#hinvoices').click(); + }); }); - }); - } + } + + $('#tblnetinvforme').show(); - $('#tblnetinvforme').show(); + } + + $('#pnlfriendinv').show(); } - $('#pnlfriendinv').show(); + if (callback) { + callback(false, ''); + } } var lastInvoiceByMeNetCount = 0; @@ -1592,6 +3035,13 @@ function UI() { for (var i = 0; i < invoices.length; i++) { + var invdate = new Date(invoices[i].InvoiceDate.match(/\d+/)[0] * 1).toLocaleString(); + var invpaydate = ''; + if (invoices[i].InvoicePaidDate) { + invpaydate = new Date(invoices[i].InvoicePaidDate.match(/\d+/)[0] * 1).toLocaleString(); + } + + var statusbox = ''; if (invoices[i].InvoiceStatus == 0) { statusbox = ' Pending'; @@ -1603,8 +3053,8 @@ function UI() { statusbox = ' Rejected'; } - s += "" + invoices[i].InvoiceDate + "" + - "" + statusbox + "" + invoices[i].InvoicePaidDate + ""; + s += "" + _.escape(invdate) + "" + + "" + statusbox + "" + _.escape(invpaydate) + ""; } $('#tblnetinvbyme tbody').append(s); @@ -1633,7 +3083,7 @@ function UI() { var cachedInvoicesByUser = []; var lastInvoiceByUserCount = 0; - function showInvoiceByUserList() { + function showInvoiceByUserList(callback) { //get back the list of invoices to pay @@ -1646,8 +3096,51 @@ function UI() { invoices = _.sortBy(invoices, function (inv) { return -inv.JsDate; }); + filteredByMeInvoices = invoices; + + //perform all filters then set back to invoices + + if (currentByMeInvoiceFilter == "Pending") { + + filteredByMeInvoices = _.filter(invoices, function (inv) { return inv.InvoiceStatus == 0; }); + } + + if (currentByMeInvoiceFilter == "Paid") { + + filteredByMeInvoices = _.filter(invoices, function (inv) { return inv.InvoiceStatus == 1; }); + } + + if (currentByMeInvoiceFilter == "Rejected") { + + filteredByMeInvoices = _.filter(invoices, function (inv) { return inv.InvoiceStatus == 2; }); + } + + if (currentByMeInvoiceFilter == "Search") { + var search = $('#txtSearchInvByMe').val(); + filteredByMeInvoices = _.filter(invoices, function (inv) { return inv.InvoiceFrom.search(search) > -1; }); + } + + var noofpages = Math.floor((filteredByMeInvoices.length / invoicesByMePerPage)); + + var indexFrom = currentByMeInvoicePageIndex * invoicesByMePerPage; + var indexTo = indexFrom + invoicesByMePerPage; + + if (indexTo > filteredByMeInvoices.length) { + indexTo = filteredByMeInvoices.length; + } + + $('#invbmpaglabel').text('Showing ' + (indexFrom + 1) + ' to ' + (indexTo) + ' of ' + filteredByMeInvoices.length); + + invoices = filteredByMeInvoices; + + if (lastInvoiceByUserCount < invoices.length) { + + pagedByMeInvoices = filteredByMeInvoices.slice(indexFrom, indexTo); + + invoices = pagedByMeInvoices; + cachedInvoicesByUser = []; lastInvoiceByUserCount = invoices.length; @@ -1656,6 +3149,12 @@ function UI() { $('#tblinvoicebyme tbody').empty(); for (var i = 0; i < invoices.length; i++) { + var invdate = new Date(invoices[i].InvoiceDate.match(/\d+/)[0] * 1).toLocaleString(); + var invpaydate = ''; + if (invoices[i].InvoicePaidDate) { + invpaydate = new Date(invoices[i].InvoicePaidDate.match(/\d+/)[0] * 1).toLocaleString(); + } + cachedInvoicesByUser.push(invoices[i]); var statusbox = ''; @@ -1670,13 +3169,8 @@ function UI() { } - var length = invoices[i].InvoiceFrom.length; - if (length > 20) { - length = 20; - } - - s += "" + invoices[i].InvoiceDate + "\"\" " + - invoices[i].InvoiceFrom + "" + statusbox + "" + invoices[i].InvoicePaidDate + ""; + s += "" + _.escape(invdate) + "\"\" " + + _.escape(invoices[i].InvoiceFrom) + "" + statusbox + "" + _.escape(invpaydate) + ""; } $('#tblinvoicebyme tbody').append(s); @@ -1691,6 +3185,41 @@ function UI() { }); }); + + + + var length = invoices[i].InvoiceFrom.length; + if (length > 20) { + length = 20; + } + + var imageSrcSmall = "images/avatar/64px/Avatar-" + pad(length) + ".png"; + + + if (FRIENDSLIST[invoices[i].InvoiceFrom].profileImage != '') { + imageSrcSmall = "https://ninkip2p.imgix.net/" + _.escape(FRIENDSLIST[invoices[i].InvoiceFrom].profileImage) + "?crop=faces&fit=crop&h=64&w=64&mask=ellipse&border=1,d0d0d0"; + } + + + + if (isChromeApp()) { + var xhrsm = new XMLHttpRequest(); + xhrsm.open('GET', imageSrcSmall, true); + xhrsm.responseType = 'blob'; + xhrsm.index = i; + xhrsm.onload = function (e) { + $("#tblinvoicebyme #imginvoicebyuser" + this.index).attr("src", window.URL.createObjectURL(this.response)); + }; + xhrsm.send(); + } else { + $("#tblinvoicebyme #imginvoicebyuser" + i).attr("src", imageSrcSmall); + } + + + } + + if (callback) { + callback(false, "ok"); } } else { @@ -1709,20 +3238,29 @@ function UI() { else if (cachedInvoicesByUser[index].InvoiceStatus == 2) { statusbox = ' Rejected'; } - //$(elem).html(''); + //$(elem).text(''); $(elem).html(statusbox); }); $('#tblinvoicebyme tbody tr .paid').each(function (index, elem) { if (cachedInvoicesByUser[index].InvoiceStatus == 1 || cachedInvoicesByUser[index].InvoiceStatus == 1) { - $(elem).html(cachedInvoicesByUser[index].InvoicePaidDate); + + var invpaydate = ''; + if (cachedInvoicesByUser[index].InvoicePaidDate) { + invpaydate = new Date(cachedInvoicesByUser[index].InvoicePaidDate.match(/\d+/)[0] * 1).toLocaleString(); + } + $(elem).text(invpaydate); } }); + } + if (callback) { + callback(false, "ok"); } + }); } @@ -1736,47 +3274,64 @@ function UI() { function displayInvoiceDetails(invoice, json, invtype, callback) { + + $("#createinv").hide(); $("#invoicestopay").hide(); $('#tblinvdisplay tbody').empty(); var s = ''; for (var i = 0; i < json.invoicelines.length; i++) { - s += "" + json.invoicelines[i].description + "" + json.invoicelines[i].quantity + "" + convertFromSatoshis(json.invoicelines[i].amount, COINUNIT) + "" + (convertFromSatoshis(json.invoicelines[i].amount, COINUNIT) * json.invoicelines[i].quantity).toFixed(4) + ""; + s += "" + _.escape(json.invoicelines[i].description) + "" + _.escape(json.invoicelines[i].quantity) + "" + _.escape(convertFromSatoshis(json.invoicelines[i].amount, COINUNIT)) + "" + _.escape((convertFromSatoshis(json.invoicelines[i].amount, COINUNIT) * json.invoicelines[i].quantity).toFixed(4)) + ""; } $('#tblinvdisplay tbody').append(s); if (invtype == 'forme') { - $("#dinvusername").html('Invoice from ' + invoice.InvoiceFrom); + $("#dinvusername").text('Invoice from ' + invoice.InvoiceFrom); + $("#sendinvcontact").text(invoice.InvoiceFrom); + } else { - $("#dinvusername").html('Invoice to ' + invoice.InvoiceFrom); + $("#dinvusername").text('Invoice to ' + invoice.InvoiceFrom); + } + + + var invdate = new Date(invoice.InvoiceDate.match(/\d+/)[0] * 1).toLocaleString(); + var invpaydate = ''; + if (invoice.InvoicePaidDate) { + invpaydate = new Date(invoice.InvoicePaidDate.match(/\d+/)[0] * 1).toLocaleString(); } - $("#dinvdate").html(invoice.InvoiceDate); - $("#tblinvdisplay tfoot th #dsubtotal").html(convertFromSatoshis(json.summary.subtotal, COINUNIT)); - $("#tblinvdisplay tfoot th #dtax").html(convertFromSatoshis(json.summary.tax, COINUNIT)); - $("#tblinvdisplay tfoot th #dtotal").html(convertFromSatoshis(json.summary.total, COINUNIT)); + $("#dinvdate").text(invdate); + + $("#tblinvdisplay tfoot th #dsubtotal").text(convertFromSatoshis(json.summary.subtotal, COINUNIT)); + $("#tblinvdisplay tfoot th #dtax").text(convertFromSatoshis(json.summary.tax, COINUNIT)); + $("#tblinvdisplay tfoot th #dtotal").text(convertFromSatoshis(json.summary.total, COINUNIT)); selectedInvoiceAmount = convertFromSatoshis(json.summary.total); selectedInvoiceId = invoice.InvoiceId; selectedInvoiceUserName = invoice.InvoiceFrom; + + $("#sendinvamount").text(convertFromSatoshis(json.summary.total, COINUNIT) + ' ' + COINUNIT); + $("#sendinvprog").hide(); $("#textMessageSendInv").hide(); $("#btnokinvoice").hide(); $("#invvalmess").hide(); + $("#invoice2fa").hide(); + if (invtype == 'forme') { if (invoice.InvoiceStatus == 0) { $("#payinvoicecancel").show(); $("#btnpayinvoice").show(); $("#btnrejectinvoice").show(); - + $("#invoice2fa").show(); if (!FRIENDSLIST[invoice.InvoiceFrom].validated) { $("#btnpayinvoice").addClass("disabled"); - $("#invvalt").html(invoice.InvoiceFrom); + $("#invvalt").text(invoice.InvoiceFrom); $("#invvalmess").show(); } else { $("#btnpayinvoice").removeClass("disabled"); @@ -1810,6 +3365,8 @@ function UI() { } $("#invdisstatus").html(statusbox); + $("#invdisid").text(invoice.InvoiceFrom.toUpperCase() + invoice.InvoiceId); + $("#invoicedisplay").show(); @@ -1872,17 +3429,12 @@ function UI() { } - function payInvoice(friend, amount, invoiceNumber) { + function payInvoice(friend, amount, invoiceNumber, twoFactorCode, callback) { - $('#textMessageSendInv').removeClass('alert alert-danger'); - $('#textMessageSendInv').html('Creating transaction...'); - $('#textMessageSendInv').show(); - $('#sendinvprogstatus').width('3%') - $('#sendinvprog').show(); - $('#sendinvprogstatus').width('10%'); - Engine.sendTransaction('invoice', friend, '', amount, function (err, transactionid) { + + Engine.sendTransaction('invoice', friend, '', amount, twoFactorCode, function (err, transactionid) { if (!err) { @@ -1890,9 +3442,9 @@ function UI() { if (!err) { - $('#textMessageSendInv').html('You paid invoice: ' + friend.toUpperCase() + invoiceNumber); - $('#textMessageSendInv').fadeOut(5000); - $('#sendinvprog').fadeOut(5000); + $('#textCompleteSendInv').text('You paid invoice: ' + friend.toUpperCase() + invoiceNumber); + //$('#textMessageSendInv').fadeOut(5000); + //$('#sendinvprog').fadeOut(5000); //$("#invoicedisplay").hide(); //$("#invoicestopay").show(); @@ -1918,174 +3470,316 @@ function UI() { if (uiInvoiceReturnToNetwork) { updateSelectedFriend(); } + + callback(err, ""); } }); - } else { - $('#textMessageSendInv').addClass('alert alert-danger'); - $('#sendinvprogstatus').width('0%') + } else { + $('#textMessageSendInv').addClass('alert alert-danger'); + $('#sendinvprogstatus').width('0%') + + if (transactionid == "ErrInsufficientFunds") { + $('#textMessageSendInv').text('Transaction Failed: Not enough funds are currently available to send this transaction'); + } else { + $('#textMessageSendInv').text(transactionid); + } + + callback(err, ""); + + } + + + + }); + + + + } + + + //INVOICE FUNCTIONS END------------------------------------------ + + + //OPEN/CREATE WALLET FUNCTIONS--------------------------------------------- + + //event handlers + + + + //wrapper functions + + + + function initialiseUI() { + + //check if hot key is available + //if not prompt the user to enter their hot key + + Engine.appHasLoaded(); + + + Engine.getHotHash("",function (err, result) { + + + if (err) { + //show modal enter phrase + $("#hotkeymodal").modal('show'); + $("#hotkeyenter").show(); + } + + + var length = Engine.m_nickname.length; + if (length > 20) { + length = 20; + } + + COINUNIT = Engine.m_settings.CoinUnit; + + $("#stdselunit").text(COINUNIT); + $("#netselunit").text(COINUNIT); + + $("#mynickname").text(Engine.m_nickname); + $("#usernameProfile").text(Engine.m_nickname); + $("#mystatus").text(Engine.m_statusText); + $("#txtTax").val((Engine.m_invoiceTax * 100) + '%'); + + + var imageSrc = "images/avatar/256px/Avatar-" + pad(length) + ".png"; + var imageSrcSmall = "images/avatar/64px/Avatar-" + pad(length) + ".png"; + + if (Engine.m_profileImage != '') { + + imageSrc = "https://ninkip2p.imgix.net/" + _.escape(Engine.m_profileImage) + "?crop=faces&fit=crop&h=256&w=256&mask=ellipse&border=1,d0d0d0"; + imageSrcSmall = "https://ninkip2p.imgix.net/" + _.escape(Engine.m_profileImage) + "?crop=faces&fit=crop&h=64&w=64&mask=ellipse&border=1,d0d0d0"; + } + + if (isChromeApp()) { + + var xhr = new XMLHttpRequest(); + xhr.open('GET', imageSrc, true); + xhr.responseType = 'blob'; + + + xhr.onload = function (e) { + $("#imgProfile").attr("src", window.URL.createObjectURL(this.response)); + }; + xhr.send(); + + var xhrsm = new XMLHttpRequest(); + xhrsm.open('GET', imageSrcSmall, true); + xhrsm.responseType = 'blob'; + + xhrsm.onload = function (e) { + $("#imgtoprightprofile").attr("src", window.URL.createObjectURL(this.response)); + }; + xhrsm.send(); + + } else { + + $("#imgProfile").attr("src", imageSrc); + + $("#imgtoprightprofile").attr("src", imageSrcSmall); + } + + $("#codeForFriend").text(Engine.m_fingerprint); + + Engine.getusernetworkcategory(function (err, categories) { + + var catOptions = ''; + + $("#netcatoptions").html(catOptions); + $("#nselnetcat").change(function () { + + $("#nselnetcat option:selected").each(function () { + //update selected friend category + Engine.updateusernetworkcategory(SELECTEDFRIEND, $(this).text(), function (err, result) { + + //refresh friend list + if (!err) { + lastNoOfFriends = 0; + updateFriends(); + } + + }); + + }); + }); + + + if (!err) { - if (transactionid == "ErrInsufficientFunds") { - $('#textMessageSendInv').html('Transaction Failed: Insufficient funds'); - } - } + document.onAway = function () { logout(); } + setInterval(function () { + updateUI(); + }, 10000); - }); + - } + updateUI(); + updateRequestsMadeByMe(); - //INVOICE FUNCTIONS END------------------------------------------ + $('#showPhrases').hide(); + $("#openWalletStart").hide(); + $("#createWalletStart").hide(); + if (Engine.m_validate) { - //OPEN/CREATE WALLET FUNCTIONS--------------------------------------------- + $("#securitywizard").show(); + $("#step4").show(); + $("#step1").hide(); + $("#step2").hide(); + $("#step3").hide(); + $("#listep1").removeClass("active"); + $("#listep2").removeClass("active"); + $("#listep3").removeClass("active"); + $("#listep4").addClass("active"); + $("#prgsecwiz").width('100%'); + $(".next").hide(); + $(".previous").hide(); + $("#validateemail").show(); + $("#mainWallet").hide(); - //event handlers + } else { + $("#securitywizard").hide(); + $("#mainWallet").show(); + $("#validateemail").hide(); + } + setAwayTimeout(600000); - //wrapper functions + $('#stdselcu').click(); + $('#netselcu').click(); - function openWallet(guid, password, twoFactorCode, callback) { - setCookie('guid', guid, 30); - Engine.openWallet(guid, password, twoFactorCode, function (err, result) { + } - if (err) { - return callback(err, result); + }); - } else { + }); - initialiseUI(); + } - } - }); + //OPEN/CREATE WALLET FUNCTIONS END--------------------------------------------- + function pad(n) { + return (n < 10) ? ("0" + n) : n; } - function initialiseUI() { + function logout() { - var length = Engine.m_nickname.length; - if (length > 20) { - length = 20; + if (chrome) { + if (chrome.runtime) { + if (chrome.runtime.reload) { + chrome.runtime.reload() + } else { + location.reload(); + } + } else { + location.reload(); + } + //return callback(true, data.statusText); + } else { + location.reload(); } + } - $("#mynickname").html(Engine.m_nickname); - $("#usernameProfile").html(Engine.m_nickname); + UI.updateUITimer = function () { + updateUI(); + } - $("#imgProfile").attr("src", "images/avatar/256px/Avatar-" + pad(length) + ".png"); - $("#imgtoprightprofile").attr("src", "images/avatar/64px/Avatar-" + pad(length) + ".png"); - $("#codeForFriend").html(Engine.m_fingerprint); + function updateUI() { - Engine.getusernetworkcategory(function (err, categories) { - var catOptions = ''; - $("#netcatoptions").html(catOptions); - $("#nselnetcat").change(function () { + var cprc = ires.toLocaleString(loc, { style: "currency", currency: Engine.m_settings.LocalCurrency }); - $("#nselnetcat option:selected").each(function () { - //update selected friend category - Engine.updateusernetworkcategory(SELECTEDFRIEND, $(this).text(), function (err, result) { + $('#price').text(cprc + ' / BTC'); - //refresh friend list - if (!err) { - lastNoOfFriends = 0; - updateFriends(); - } + }); - }); - }); - }); + $(".coinunit").text(COINUNIT); + $("#stdsendcunit").text(COINUNIT); + $("#stdsendlcurr").text(Engine.m_settings.LocalCurrency); + $("#netsendcunit").text(COINUNIT); + $("#netsendlcurr").text(Engine.m_settings.LocalCurrency); + + $("#amount").attr("placeholder", "Enter amount in units of " + COINUNIT); + $("#friendAmount").attr("placeholder", "Enter amount in units of " + COINUNIT); + $("#fndsendcunit").text(COINUNIT); - if (!err) { + updateCoinProfile(function (err, res) { + + }); + updateDeviceList(function (err, res) { - document.onAway = function () { logout(); } - setInterval("Ninki.UI.updateUITimer()", 10000); + }); - updateUI(); - updateRequestsMadeByMe(); - $('#showPhrases').hide(); - $("#openWalletStart").hide(); - $("#createWalletStart").hide(); + updateBalance(function (err, res) { - if (Engine.m_validate) { + updateFriends(function (err, res) { - $("#securitywizard").show(); - $("#step4").show(); - $("#step1").hide(); - $("#step2").hide(); - $("#step3").hide(); - $("#listep1").removeClass("active"); - $("#listep2").removeClass("active"); - $("#listep3").removeClass("active"); - $("#listep4").addClass("active"); - $("#prgsecwiz").width('100%'); - $(".next").hide(); - $(".previous").hide(); - $("#validateemail").show(); - $("#mainWallet").hide(); + updateFriendRequests(function (err, res) { - } else { + updateRequestsMadeByMe(function (err, res) { - $("#securitywizard").hide(); - $("#mainWallet").show(); - $("#validateemail").hide(); - } + updateTransactions(function (err, res) { - setAwayTimeout(600000); + showInvoiceList(function (err, res) { - } + showInvoiceByUserList(function (err, res) { + showInvoiceListNetwork(function (err, res) { - }); + if (!norefresh) { + refreshSelectedFriend(function (err, res) { - } + console.log('completed refresh'); - //OPEN/CREATE WALLET FUNCTIONS END--------------------------------------------- + }); + } - function pad(n) { - return (n < 10) ? ("0" + n) : n; - } + }); - function logout() { - location.reload(); - } - UI.updateUITimer = function () { - updateUI(); - } + }); - function updateUI() { - updateBalance(); - updateFriends(); - updateFriendRequests(); - updateRequestsMadeByMe(); - updateTransactions(); - showInvoiceList(); - showInvoiceByUserList(); - //var enc = COINUNIT.replace('u', 'μ'); - $("#stdsendcunit").html(COINUNIT); - $("#amount").attr("placeholder", "Enter amount in units of " + COINUNIT); - $("#friendAmount").attr("placeholder", "Enter amount in units of " + COINUNIT); - $("#fndsendcunit").html(COINUNIT); + }); + }); + }); + }); + }); + }); } function showCreateWalletStart() { @@ -2109,8 +3803,15 @@ function UI() { $("#twoFactorQr").show(); $("#2factor1").show(); - Engine.getTwoFactorImg(function (err, twoFactorQrImgUrl) { - $("#twoFactorQrImg").attr("src", twoFactorQrImgUrl); + Engine.getTwoFactorImg(function (err, twoFASecret) { + + var nickname = $("#createWalletStart input#nickname").val(); + var data = "otpauth://totp/Ninki:" + nickname + "?secret=" + twoFASecret + "&issuer=Ninki"; + var options = { text: data, width: 172, height: 172 }; + + $('#twoFactorQrImg').text(''); + $('#twoFactorQrImg').qrcode(options); + }); } @@ -2122,9 +3823,18 @@ function UI() { $("#settings2fa").show(); $("#btnSetupTwoFactor").hide(); - Engine.getTwoFactorImg(function (err, twoFactorQrImgUrl) { - $("#imgsettings2fa").attr("src", twoFactorQrImgUrl); + + + Engine.getTwoFactorImg(function (err, twoFASecret) { + + var data = "otpauth://totp/Ninki:" + Engine.m_nickname + "?secret=" + twoFASecret + "&issuer=Ninki"; + var options = { text: data, width: 172, height: 172 }; + + $('#imgsettings2fa').text(''); + $('#imgsettings2fa').qrcode(options); }); + + } //Download settings from server and populate input boxes @@ -2172,17 +3882,42 @@ function UI() { if (settingsObject['CoinUnit'] == 'BTC') { - $('#cuSelected').html('BTC'); + $('#cuSelected').text('BTC'); $('#cuBTC').prop('checked', true); } if (settingsObject['CoinUnit'] == 'mBTC') { - $('#cuSelected').html('mBTC'); + $('#cuSelected').text('mBTC'); $('#cumBTC').prop('checked', true); } if (settingsObject['CoinUnit'] == 'uBTC') { - $('#cuSelected').html('μBTC'); + $('#cuSelected').text('μBTC'); $('#cuuBTC').prop('checked', true); } + if (settingsObject['CoinUnit'] == 'Bits') { + $('#cuSelected').text('Bits'); + $('#cuuBits').prop('checked', true); + } + + + if (settingsObject['LocalCurrency'] == 'USD') { + $('#lcSelected').text('USD'); + $('#lcUSD').prop('checked', true); + } + + if (settingsObject['LocalCurrency'] == 'EUR') { + $('#lcSelected').text('EUR'); + $('#lcEUR').prop('checked', true); + } + + if (settingsObject['LocalCurrency'] == 'JPY') { + $('#lcSelected').text('JPY'); + $('#lcJPY').prop('checked', true); + } + + if (settingsObject['LocalCurrency'] == 'CNY') { + $('#lcSelected').text('CNY'); + $('#lcCNY').prop('checked', true); + } } }); @@ -2194,45 +3929,108 @@ function UI() { guid: Engine.m_guid }; - jsonPacket['DailyTransactionLimit'] = convertToSatoshis($('#DailyTransactionLimit').val(), COINUNIT); - jsonPacket['SingleTransactionLimit'] = convertToSatoshis($('#SingleTransactionLimit').val(), COINUNIT); - jsonPacket['NoOfTransactionsPerDay'] = $('#NoOfTransactionsPerDay').val(); - jsonPacket['NoOfTransactionsPerHour'] = $('#NoOfTransactionsPerHour').val(); - jsonPacket['Inactivity'] = $('#Inactivity').val(); - jsonPacket['MinersFee'] = convertToSatoshis($('#MinersFee').val(), COINUNIT); - - if ($('#cuSelected').html() == 'BTC') { - jsonPacket['CoinUnit'] = 'BTC'; - } else if ($('#cuSelected').html() == 'mBTC') { - jsonPacket['CoinUnit'] = 'mBTC'; - } else { - jsonPacket['CoinUnit'] = 'uBTC'; - } + var minersFee = convertToSatoshis($('#MinersFee').val(), COINUNIT); - jsonPacket['Email'] = $('#Email').val(); - jsonPacket['EmailNotification'] = $('#EmailNotification').checked; + if (minersFee > 9999) { - Engine.updateAccountSettings(jsonPacket, $("#txtTwoFactorCodeForSettings").val(), function (err, response) { - if (err) { - $("#savesettingserror").show(); - $("#savesettingssuccess").hide(); - $("#savesettingserrormessage").html(response); + jsonPacket['DailyTransactionLimit'] = convertToSatoshis($('#DailyTransactionLimit').val(), COINUNIT); + jsonPacket['SingleTransactionLimit'] = convertToSatoshis($('#SingleTransactionLimit').val(), COINUNIT); + jsonPacket['NoOfTransactionsPerDay'] = $('#NoOfTransactionsPerDay').val(); + jsonPacket['NoOfTransactionsPerHour'] = $('#NoOfTransactionsPerHour').val(); + jsonPacket['Inactivity'] = $('#Inactivity').val(); + jsonPacket['MinersFee'] = minersFee; + + if ($('#cuSelected').text() == 'BTC') { + jsonPacket['CoinUnit'] = 'BTC'; + } else if ($('#cuSelected').text() == 'mBTC') { + jsonPacket['CoinUnit'] = 'mBTC'; + } else if ($('#cuSelected').text() == 'Bits') { + jsonPacket['CoinUnit'] = 'Bits'; } else { + jsonPacket['CoinUnit'] = 'uBTC'; + } + + jsonPacket['LocalCurrency'] = $('#lcSelected').text(); + + jsonPacket['Email'] = $('#Email').val(); + jsonPacket['EmailNotification'] = $('#EmailNotification')[0].checked; + + if (jsonPacket['LocalCurrency'] != Engine.m_settings.LocalCurrency) { + $("#amount").val(''); + } + + + Engine.updateAccountSettings(jsonPacket, $("#txtTwoFactorCodeForSettings").val(), function (err, response) { + if (err) { + $("#savesettingserror").show(); + $("#savesettingssuccess").hide(); + $("#savesettingserrormessage").text(response); + } else { + + + var stdcoinunitsel = false; + if ($("#stdselunit").text() == COINUNIT) { + stdcoinunitsel = true; + } + + var netcoinunitsel = false; + if ($("#netselunit").text() == COINUNIT) { + netcoinunitsel = true; + } + + if (jsonPacket['CoinUnit'] != COINUNIT) { + COINUNIT = jsonPacket['CoinUnit']; + lastNoOfTrans = -1; + readAccountSettingsFromServerAndPopulateForm(); + $("#stdsendcunit").text(COINUNIT); + $("#amount").val(''); + $("#friendAmount").val(''); + } + + $("#stdsendlcurr").text(Engine.m_settings.LocalCurrency); + + if (stdcoinunitsel) { + + $("#stdselunit").text(COINUNIT); + + } else { + + $("#stdselunit").text(Engine.m_settings.LocalCurrency); + + } + + if (netcoinunitsel) { + + $("#netselunit").text(COINUNIT); + + } else { + + $("#netselunit").text(Engine.m_settings.LocalCurrency); + + } + + $("#amount").keyup(); + $("#friendAmount").keyup(); - if (jsonPacket['CoinUnit'] != COINUNIT) { - COINUNIT = jsonPacket['CoinUnit']; updateUI(); - readAccountSettingsFromServerAndPopulateForm(); + + $("#savesettingssuccess").show(); + $("#savesettingserror").hide(); + $("#savesettingssuccessmessage").text("Settings saved successfully"); + + //alert(response.body); } + }); - $("#savesettingssuccess").show(); - $("#savesettingserror").hide(); - $("#savesettingssuccessmessage").html("Settings saved successfully"); + } else { - //alert(response.body); - } - }); + $("#savesettingserror").show(); + $("#savesettingssuccess").hide(); + $("#savesettingserrormessage").text('Mining fee must be at least ' + convertFromSatoshis(10000, COINUNIT) + ' ' + COINUNIT); + + + } } function convertFromSatoshis(amount, toUnit) { @@ -2249,6 +4047,11 @@ function UI() { amount = amount / 100; } + if (toUnit == 'Bits') { + amount = amount / 100; + } + + return amount; } @@ -2265,48 +4068,215 @@ function UI() { if (fromUnit == 'uBTC') { amount = amount * 100; } + + if (fromUnit == 'Bits') { + amount = amount * 100; + } + amount = Math.round(amount) return amount; } - function updateBalance() { + function updateCoinProfile(callback) { + + Engine.getCoinProfile(function (err, result) { + + var outputs = result; + + var cchtml = ''; + for (var i = 0; i < outputs.length; i++) { + + var pitem = outputs[i]; + + cchtml += '
    ' + _.escape(pitem.Amount) + '
    ' + _.escape(pitem.IsPending) + '
    ' + _.escape(pitem.TransactionId) + '
    ' + _.escape(pitem.OutputIndex) + '
    ' + _.escape(pitem.Address) + '
    ' + _.escape(pitem.NodeLevel) + '
    '; + + } + + $("#coincontrol").text(cchtml); + + }); + + + } + + + function updateBalance(callback) { + + Engine.getBalance(function (err, result) { + + //get in BTC units + var balance = convertFromSatoshis(result.TotalBalance, COINUNIT); + + + $("#balance").text(balance); + $("#balanceTop").text(balance + " " + COINUNIT); + //$("#balanceTop").text(balance + " " + COINUNIT + " ($" + (xccy) + ")"); + $("#dashcoinunit").text(COINUNIT); + var template = ''; + if (result.UnconfirmedBalance > 0) { + template += '
    '; + } else { + template += '
    '; + } + + $("#balancetimer").html(template); + + if (callback) { + callback(); + } + + + + }); + + //$("#balance").text(Math.floor((Math.random() * 100) + 1) + " BTC"); + //$("#message").fadeToggle(3000); + + } + + function updateNetwork() { + + // getNewFriends(); + //updateFriendRequests(); + getNewFriends(); + } + + + var lastNoOfDevices = -1; + var lastDevices = []; + + var currentDevice = {}; + + function updateDeviceList(callback) { + + + Engine.getDevices(function (err, devices) { + + if (devices.length == lastNoOfDevices) { + + for (var i = 0; i < devices.length; i++) { + + if (!lastDevices[i].IsPaired && devices[i].IsPaired) { + + + $('#qrdevice').text(''); + + $('#pairqr2fa').show(); + $('#pairqrscan').hide(); + + $("#pairdevicemodal").modal('hide'); + $("#pairdeviceqr").hide(); + + } + + if (devices[i].IsPaired != lastDevices[i].IsPaired) { + lastNoOfDevices = 0; + break; + } + } + + } + + + if (devices.length > lastNoOfDevices) { + + lastNoOfDevices = devices.length; + + lastDevices = devices; + + + var template = ''; + + $('#devices').text(''); + + if (!err) { + + for (var i = 0; i < devices.length; i++) { + + template += ''; + template += '
    '; + template += ''; + + template += '
    '; + + if (devices[i].IsPaired) { + template += '
    '; + } else { + template += '
    '; + } + + template += '
    '; + + template += _.escape(devices[i].DeviceName); + template += '
    '; + template += '
    '; + template += _.escape(devices[i].DeviceModel); + template += '
    '; + template += '
    '; + template += _.escape(devices[i].DeviceId); + template += '
    '; + + + + + template += '
    '; + template += '
    '; + template += '
    '; + + } + + $('#devices').html(template); + + for (var i = 0; i < devices.length; i++) { + + $("#devices #pair" + i).click({ device: devices[i] }, function (event) { - Engine.getBalance(function (err, result) { + currentDevice = event.data.device; + //here initiate the modal phone pairing screen - //get in BTC units - var balance = convertFromSatoshis(result.TotalBalance, COINUNIT); - $("#balance").html(balance); - $("#balanceTop").html(balance + " " + COINUNIT); - $("#dashcoinunit").html(COINUNIT); - var template = ''; - if (result.UnconfirmedBalance > 0) { - template += '
    '; - } else { - template += '
    '; - } + //get the device 2fa code + //generate a qr containing + //password, hotkey, 2fa code, guid - $("#balancetimer").html(template); + //event.data.deviceName + $('#qrdevice').text(''); - }); + if (!event.data.device.IsPaired) { - //$("#balance").html(Math.floor((Math.random() * 100) + 1) + " BTC"); - //$("#message").fadeToggle(3000); + $('#pairqrscan').hide(); + $("#pairdevicemodal").modal('show'); + $("#pairdeviceqr").show(); + $('#pairqr2fa').show(); + $("#pairheading").text("Pair " + event.data.device.DeviceName); - } + } else { - function updateNetwork() { + $('#pairqrscan').hide(); + $("#pairdevicemodal").modal('show'); + $("#pairdeviceqr").show(); + $("#pairheading").text("Unpair " + event.data.device.DeviceName); + $("#btnShowPairQr").text("Unpair"); + $('#qrdevice').text(''); + //$('#qrdevice').qrcode(''); - // getNewFriends(); - //updateFriendRequests(); - getNewFriends(); - } + } + + }); + } + + } + + } + + }); + } var previousReqByMe = 0; - function updateRequestsMadeByMe() { + function updateRequestsMadeByMe(callback) { Engine.getPendingUserRequests(function (err, friends) { @@ -2314,7 +4284,7 @@ function UI() { if (friends.length != previousReqByMe) { previousReqByMe = friends.length; - $("#requestssent").html(''); + $("#requestssent").text(''); for (var i = 0; i < friends.length; i++) { var length = friends[i].userName.length; @@ -2329,7 +4299,7 @@ function UI() { '' + '
    ' + '
    ' + - '
    ' + friends[i].userName + '
    ' + + '
    ' + _.escape(friends[i].userName) + '
    ' + 'I love Ninki!' + '
    ' + '' + @@ -2344,6 +4314,13 @@ function UI() { } else { $("#requestsentpanel").hide(); } + + if (callback) { + + callback(); + + } + }); @@ -2357,37 +4334,40 @@ function UI() { var invoiceSelectedAmount = 0; var selectedFriend = null; - function updateFriends() { + function updateFriends(callback) { if (!noAlert == true) { - //to do, move to handlebars templates + Engine.getUserNetwork(function (err, friends) { - FRIENDSLIST = {}; - $("#nfriends").html(friends.length); - for (var i = 0; i < friends.length; i++) { - FRIENDSLIST[friends[i].userName] = friends[i]; - } + $("#nfriends").text(friends.length); + + if (friends.length > lastNoOfFriends) { - //if selected friend is not isend and isreceive - //then find in list and update + lastNoOfFriends = friends.length; - if (selectedFriend != null) { + FRIENDSLIST = {}; - if (!selectedFriend.ICanSend || !selectedFriend.ICanReceive) { - selectedFriend = FRIENDSLIST[selectedFriend.userName]; - updateSelectedFriend(); + for (var i = 0; i < friends.length; i++) { + FRIENDSLIST[friends[i].userName] = friends[i]; } - } + //if selected friend is not isend and isreceive + //then find in list and update - if (friends.length > lastNoOfFriends) { + if (selectedFriend != null) { - lastNoOfFriends = friends.length; + if (!selectedFriend.ICanSend || !selectedFriend.ICanReceive) { + selectedFriend = FRIENDSLIST[selectedFriend.userName]; + updateSelectedFriend(); + } + + } - $("#nfriends").html(friends.length); - $("#myfriends").html(''); + + $("#nfriends").text(friends.length); + $("#myfriends").text(''); var grouptemplate = ''; @@ -2415,20 +4395,18 @@ function UI() { var frnd = FRIENDSLIST[friends[i].userName]; - var length = frnd.userName.length; - if (length > 20) { - length = 20; - } + var template = '
    ' + + '
    '; + if (frnd.validated) { + template += '
    ' + + '' + + '
    '; + } - var template = '
    ' + - '' + - '
    ' + - '' + - '
    ' + - '
    ' + - '
    ' + friends[i].userName + '
    ' + - 'I love Ninki!' + + template += '
    ' + + '
    ' + _.escape(friends[i].userName) + '
    ' + + '' + _.escape(frnd.status) + '' + '
    ' + '
    '; @@ -2460,14 +4438,47 @@ function UI() { friends = friendsgroup[key]; + + var length = friends[i].userName.length; + if (length > 20) { + length = 20; + } + + var imageSrc = "images/avatar/64px/Avatar-" + pad(length) + ".png"; + + if (friends[i].profileImage != '') { + imageSrc = "https://ninkip2p.imgix.net/" + _.escape(friends[i].profileImage) + "?crop=faces&fit=crop&h=256&w=256&mask=ellipse&border=1,d0d0d0"; + imageSrcSmall = "https://ninkip2p.imgix.net/" + _.escape(friends[i].profileImage) + "?crop=faces&fit=crop&h=64&w=64&mask=ellipse&border=1,d0d0d0"; + } + + //$("#myfriends #imgfriend" + k) + + if (isChromeApp()) { + var xhrsm = new XMLHttpRequest(); + xhrsm.open('GET', imageSrc, true); + xhrsm.responseType = 'blob'; + xhrsm.index = k; + xhrsm.onload = function (e) { + $("#myfriends #imgfriend" + this.index).attr("src", window.URL.createObjectURL(this.response)); + }; + xhrsm.send(); + } else { + $("#myfriends #imgfriend" + k).attr("src", imageSrc); + } + + + $("#myfriends #friend" + k).click({ userName: friends[i].userName }, function (event) { + SELECTEDFRIEND = event.data.userName; selectedFriend = FRIENDSLIST[event.data.userName]; //depreciate - SELECTEDFRIEND = event.data.userName; + updateSelectedFriend(); + $("#friendAmount").keyup(); + }); console.log("added click " + k + " for " + friends[i].userName); @@ -2479,6 +4490,7 @@ function UI() { } + return callback(false, "done"); }); } @@ -2489,81 +4501,213 @@ function UI() { } + function refreshSelectedFriend(callback) { - function updateSelectedFriend() { + if (SELECTEDFRIEND.length > 0) { - var length = selectedFriend.userName.length; - if (length > 20) { - length = 20; - } + Engine.getFriend(SELECTEDFRIEND, function (err, friend) { + if (!norefresh) { + if (SELECTEDFRIEND == friend.userName) { + selectedFriend = friend; + FRIENDSLIST[SELECTEDFRIEND] = friend; + + if (selectedFriend.ICanSend) { + $("#issend").show(); + $("#networksend").show(); + } else { + $("#issend").hide(); + $("#networksend").hide(); + } + if (selectedFriend.ICanReceive) { + $("#isreceive").show(); + } else { + $("#isreceive").hide(); + } - $("#nselnetcat").val(selectedFriend.category); + var imageSrc = "images/avatar/256px/Avatar-" + pad(SELECTEDFRIEND.length) + ".png"; - $('#friendempty').hide(); + if (selectedFriend.profileImage != '') { + imageSrc = "https://ninkip2p.imgix.net/" + _.escape(selectedFriend.profileImage) + "?crop=faces&fit=crop&h=256&w=256&mask=ellipse&border=1,d0d0d0"; + } + + if (isChromeApp()) { + var xhrsm = new XMLHttpRequest(); + xhrsm.open('GET', imageSrc, true); + xhrsm.responseType = 'blob'; + + xhrsm.onload = function (e) { + $("#imgSelectedFriend").attr("src", window.URL.createObjectURL(this.response)); + }; + xhrsm.send(); + } else { + + $("#imgSelectedFriend").attr("src", imageSrc); + + } + + + if (selectedFriend.status != '') { + $("#friendSelectedStatus").text(selectedFriend.status); + } + + + + + if (selectedFriend.validated) { + $("#validateform").hide(); + $("#isvalidated").show(); + $("#networkvalidate").hide(); + $("#btnconfmoneynet").prop('disabled', false); + $("#btnconfmoneynet").removeClass('disabled'); + $("#friendvalreq").hide(); + + } else { + $("#validateform").show(); + $("#isvalidated").hide(); + $("#networkvalidate").show(); + $("#btnconfmoneynet").prop('disabled', true); + $("#btnconfmoneynet").addClass('disabled'); + $("#friendvalreq").show(); + } + + callback(err, friend); + + //updateSelectedFriend(function (err, res) { + // selFriendBkgUpdate = false; + // callback(err, res); + //}); + } + } + }); - $('#textMessageSend').removeClass('alert alert-danger'); - $('input#friendAmount').val('') - $('#textMessageSend').hide(); - $('#sendfriendprog').hide(); - - $("#networkvalidate").show(); - $("#friendSelectedName").html(selectedFriend.userName); - $("#friendSelectedNameTo").html(selectedFriend.userName); - $("#validateusername").html(selectedFriend.userName); - $("#validateusername2").html(selectedFriend.userName); - $("#validateusername3").html(selectedFriend.userName); - $("#validateusername4").html(selectedFriend.userName); - $("#validateusername5").html(selectedFriend.userName); - $("#validatesuccess").hide(); - $("#validatefail").hide(); - - if (selectedFriend.ICanSend) { - $("#issend").show(); - $("#networksend").show(); - } else { - $("#issend").hide(); - $("#networksend").hide(); - } - if (selectedFriend.ICanReceive) { - $("#isreceive").show(); - } else { - $("#isreceive").hide(); } - $("#imgSelectedFriend").attr("src", "images/avatar/256px/Avatar-" + pad(length) + ".png"); + } + - if (selectedFriend.validated) { - $("#validateform").hide(); - $("#isvalidated").show(); - $("#networkvalidate").hide(); - $("#btnSendToFriend").prop('disabled', false); - $("#btnSendToFriend").removeClass('disabled'); - $("#friendvalreq").hide(); - } else { - $("#validateform").show(); - $("#isvalidated").hide(); + + function updateSelectedFriend(callback) { + + //can optimise futher + norefresh = true; + if (SELECTEDFRIEND.length > 0) { + + $('#sendnets1').show(); + $('#sendnets2').hide(); + $('#sendnets3').hide(); + + $('input#friendAmount').val(''); + + + var length = selectedFriend.userName.length; + if (length > 20) { + length = 20; + } + + $("#nselnetcat").val(selectedFriend.category); + + $('#friendempty').hide(); + + $('#textMessageSend').removeClass('alert alert-danger'); + + + + $('#textMessageSend').hide(); + $('#sendfriendprog').hide(); + $("#networkvalidate").show(); - $("#btnSendToFriend").prop('disabled', true); - $("#btnSendToFriend").addClass('disabled'); - $("#friendvalreq").show(); - } + $("#friendSelectedName").text(selectedFriend.userName); + $("#friendSelectedNameTo").text(selectedFriend.userName); + $("#validateusername").text(selectedFriend.userName); + $("#validateusername2").text(selectedFriend.userName); + $("#validateusername3").text(selectedFriend.userName); + $("#validateusername4").text(selectedFriend.userName); + $("#validateusername5").text(selectedFriend.userName); + $("#validatesuccess").hide(); + $("#validatefail").hide(); + + if (selectedFriend.ICanSend) { + $("#issend").show(); + $("#networksend").show(); + } else { + $("#issend").hide(); + $("#networksend").hide(); + } + if (selectedFriend.ICanReceive) { + $("#isreceive").show(); + } else { + $("#isreceive").hide(); + } + + var imageSrc = "images/avatar/256px/Avatar-" + pad(length) + ".png"; + + if (selectedFriend.profileImage != '') { + imageSrc = "https://ninkip2p.imgix.net/" + _.escape(selectedFriend.profileImage) + "?crop=faces&fit=crop&h=256&w=256&mask=ellipse&border=1,d0d0d0"; + } + + //if (selectedFriend.status != '') { + $("#friendSelectedStatus").text(selectedFriend.status); + //} + + if (isChromeApp()) { + var xhrsm = new XMLHttpRequest(); + xhrsm.open('GET', imageSrc, true); + xhrsm.responseType = 'blob'; + + xhrsm.onload = function (e) { + $("#imgSelectedFriend").attr("src", window.URL.createObjectURL(this.response)); + }; + xhrsm.send(); + } else { + + $("#imgSelectedFriend").attr("src", imageSrc); + + } + - $("#pnlfriend").show(); + //$("#imgSelectedFriend").attr("src", imageSrc); - $('#tblnetinvbyme tbody').empty(); - $('#tblnetinvforme tbody').empty(); - lastInvoiceToPayNetCount = 0; - lastInvoiceByMeNetCount = 0; - showInvoiceListNetwork(); - showInvoiceByMeListNetwork(); + if (selectedFriend.validated) { + $("#validateform").hide(); + $("#isvalidated").show(); + $("#networkvalidate").hide(); + $("#btnconfmoneynet").prop('disabled', false); + $("#btnconfmoneynet").removeClass('disabled'); + $("#friendvalreq").hide(); + + } else { + $("#validateform").show(); + $("#isvalidated").hide(); + $("#networkvalidate").show(); + $("#btnconfmoneynet").prop('disabled', true); + $("#btnconfmoneynet").addClass('disabled'); + $("#friendvalreq").show(); + } + + $("#pnlfriend").show(); + + $('#tblnetinvbyme tbody').empty(); + $('#tblnetinvforme tbody').empty(); + + lastInvoiceToPayNetCount = 0; + lastInvoiceByMeNetCount = 0; + showInvoiceListNetwork(); + showInvoiceByMeListNetwork(); + } + + norefresh = false; + if (callback) { + callback(false, "ok"); + } + } var lastNoOfFriendsReq = 0; - function updateFriendRequests() { + function updateFriendRequests(callback) { //if there are any new friends //fade in the button @@ -2577,9 +4721,9 @@ function UI() { $("#contactrequestpanel").hide(); } - $("#notifications").html(friends.length); - $("#notificationsright").html(friends.length); - $("#nfriendreq").html(friends.length); + $("#notifications").text(friends.length); + $("#notificationsright").text(friends.length); + $("#nfriendreq").text(friends.length); if (lastNoOfFriendsReq != friends.length || friends.length == 0) { @@ -2590,8 +4734,8 @@ function UI() { } else { $("#notifications").attr("class", "badge pull-right"); } - $("#nfriendreq").html(friends.length); - $("#friendreq").html(''); + $("#nfriendreq").text(friends.length); + $("#friendreq").text(''); for (var i = 0; i < friends.length; i++) { var length = friends[i].userName.length; @@ -2603,9 +4747,9 @@ function UI() { 'John said' + '' + '
    ' + - '' + friends[i].userName + '' + + '' + _.escape(friends[i].userName) + '' + 'has requested you as a contact' + - '
    Accept Reject' + + '
    Accept Reject' + '
    '; $("#friendreq").append(template); @@ -2632,15 +4776,16 @@ function UI() { } - - + if (callback) { + callback(false, "done"); + } }); } var lastNoOfTrans = 0; - function updateTransactions() { + function updateTransactions(callback) { //if there are any new friends //fade in the button @@ -2650,9 +4795,34 @@ function UI() { allTransactions = transactions; + if (allTransactions.length != lastNoOfTrans) { + + } else { + + transactions = pagedTransactions; + + //we ony need to update the confirmations + + $('#tbltran tbody tr .bcconf').each(function (index, elem) { + + var tran = allTransactions[transactionIndex[transactions[index].TransactionId]]; + + if (tran.Confirmations < 6) { + $(elem).html('
    ' + _.escape(tran.Confirmations) + '
    '); + } else { + $(elem).html('
    '); + } + + }); + + return callback(false, "ok"); + } + + for (var i = 0; i < allTransactions.length; i++) { var d1 = new Date(allTransactions[i].TransDateTime); - allTransactions[i].JsDate = d1; + allTransactions[i].JsDate = new Date(transactions[i].TransDateTime.match(/\d+/)[0] * 1); + transactionIndex[allTransactions[i].TransactionId] = i; } //first convert to javascript dates @@ -2678,9 +4848,8 @@ function UI() { } if (currentTransactionFilter == "Search") { - var search = $('#txttransearch').val(); - filteredTransactions = _.filter(allTransactions, function (trans) { return trans.UserName.search(search) > -1; }); + filteredTransactions = _.filter(allTransactions, function (trans) { return trans.UserName.toLowerCase().search(search.toLowerCase()) > -1; }); } if (currentTransactionSort == 'DateDesc') { @@ -2709,17 +4878,17 @@ function UI() { indexTo = filteredTransactions.length; } - $('#tranpaglabel').html('Showing ' + (indexFrom + 1) + ' to ' + (indexTo) + ' of ' + filteredTransactions.length); + $('#tranpaglabel').text('Showing ' + (indexFrom + 1) + ' to ' + (indexTo) + ' of ' + filteredTransactions.length); transactions = filteredTransactions; - if (transactions.length > lastNoOfTrans) { + if (allTransactions.length != lastNoOfTrans) { pagedTransactions = filteredTransactions.slice(indexFrom, indexTo); transactions = pagedTransactions; - lastNoOfTrans = filteredTransactions.length; + lastNoOfTrans = allTransactions.length; var template = ''; $('#tbltran tbody').empty(); @@ -2727,28 +4896,39 @@ function UI() { var dirTemplate = ""; if (transactions[i].TransType == 'S') { - dirTemplate = '' + convertFromSatoshis(transactions[i].Amount, COINUNIT) + ' ' + COINUNIT + ''; + dirTemplate = '' + _.escape(convertFromSatoshis(transactions[i].Amount, COINUNIT)) + ' ' + _.escape(COINUNIT) + ''; } if (transactions[i].TransType == 'R') { - dirTemplate = '' + convertFromSatoshis(transactions[i].Amount, COINUNIT) + ' ' + COINUNIT + ''; + dirTemplate = '' + _.escape(convertFromSatoshis(transactions[i].Amount, COINUNIT)) + ' ' + _.escape(COINUNIT) + ''; } - var length = transactions[i].UserName.length; - if (length > 20) { - length = 20; + var tref = transactions[i].UserName; + + if (transactions[i].UserName == 'External') { + tref = _.escape(transactions[i].Address.substring(0, 7)) + '...'; + } + + if (transactions[i].InvoiceId > 0) { + tref += ' '; } + + + var trdate = new Date(transactions[i].TransDateTime.match(/\d+/)[0] * 1).toLocaleString(); + + template += '' + '' + - '' + transactions[i].TransDateTime + '' + - '' + - 'John said ' + - transactions[i].UserName + '' + + '' + _.escape(trdate) + ''; + + template += '' + + 'John said ' + + tref + '' + dirTemplate + ''; if (transactions[i].Confirmations < 6) { - template += '
    ' + transactions[i].Confirmations + '
    '; + template += '
    ' + _.escape(transactions[i].Confirmations) + '
    '; } else { template += '
    '; } @@ -2763,66 +4943,72 @@ function UI() { for (var i = 0; i < transactions.length; i++) { + var trdate = new Date(transactions[i].TransDateTime.match(/\d+/)[0] * 1).toLocaleString(); var popcontent = ''; popcontent += '

    Date: '; - popcontent += transactions[i].TransDateTime; + popcontent += _.escape(trdate); popcontent += '

    '; popcontent += '

    TransactionId

    '; - popcontent += '

    '; - popcontent += transactions[i].TransactionId; + popcontent += '

    '; + popcontent += _.escape(transactions[i].TransactionId); popcontent += '

    '; popcontent += '

    Address: '; - popcontent += transactions[i].Address; + popcontent += _.escape(transactions[i].Address); popcontent += '

    '; popcontent += '

    Amount: '; - popcontent += convertFromSatoshis(transactions[i].Amount, COINUNIT) + ' '; - popcontent += COINUNIT + '

    '; + popcontent += _.escape(convertFromSatoshis(transactions[i].Amount, COINUNIT)) + ' '; + popcontent += _.escape(COINUNIT) + '

    '; popcontent += '

    Send/Receive: '; - popcontent += transactions[i].TransType; + popcontent += _.escape(transactions[i].TransType); popcontent += '

    '; $("#btnpop" + i).popover({ - placement: 'right', // top, bottom, left or right - title: 'Transaction Details', + placement: 'left', // top, bottom, left or right + title: 'Transaction Details', html: 'true', content: '
    ' + popcontent + '
    ' }); - } - - } else { - - - pagedTransactions = filteredTransactions.slice(indexFrom, indexTo); - transactions = pagedTransactions; + var length = transactions[i].UserName.length; + if (length > 20) { + length = 20; + } - //we ony need to update the confirmations + var imageSrcSmall = "images/avatar/64px/Avatar-" + pad(length) + ".png"; - $('#tbltran tbody tr .bcconf').each(function (index, elem) { - if (transactions[index].Confirmations < 6) { - $(elem).html('
    ' + transactions[index].Confirmations + '
    '); + if (transactions[i].UserName != 'External') { + if (FRIENDSLIST[transactions[i].UserName].profileImage != '') { + imageSrcSmall = "https://ninkip2p.imgix.net/" + _.escape(FRIENDSLIST[transactions[i].UserName].profileImage) + "?crop=faces&fit=crop&h=64&w=64&mask=ellipse&border=1,d0d0d0"; + } + } + if (isChromeApp()) { + var xhrsm = new XMLHttpRequest(); + xhrsm.open('GET', imageSrcSmall, true); + xhrsm.responseType = 'blob'; + xhrsm.index = i; + xhrsm.onload = function (e) { + $("#tbltran #imgtran" + this.index).attr("src", window.URL.createObjectURL(this.response)); + }; + xhrsm.send(); } else { - $(elem).html('
    '); + $("#tbltran #imgtran" + i).attr("src", imageSrcSmall); } - }); - for (var i = 0; i < transactions.length; i++) { - $("#btnpop" + i).popover({ - placement: 'top', // top, bottom, left or right - title: 'This is my Title', - html: 'true', - content: '
    Your Text Here
    ' - }); } } + + if (callback) { + callback(); + } + }); } @@ -2837,12 +5023,12 @@ function UI() { var options = { text: newAddress, width: 172, height: 172 }; - $('#requestaddressqr').html(''); + $('#requestaddressqr').text(''); $('#requestaddressqr').qrcode(options); - $('#requestaddresstxt').html(newAddress); + $('#requestaddresstxt').text(newAddress); - //$('#requestaddress').html(tempate); + //$('#requestaddress').text(tempate); $('#requestaddress').show(); @@ -2854,43 +5040,71 @@ function UI() { function sendMoney(friend, index) { $('#textMessageSend').removeClass('alert alert-danger'); + if (friend == null) { return; } - var amount = $('input#friendAmount').val(); + var amount = $('#hdfriendAmount').val(); amount = convertToSatoshis(amount, COINUNIT); + var twoFactorCode = $('#txtFriendSend2FA').val(); + var allok = true; if (amount > 0) { $('input#friendAmount').css("border-color", "#ccc"); - $('#textMessageSend').html('Creating transaction...'); + } else { + $('input#friendAmount').css("border-color", "#ffaaaa"); + allok = false; + } + + if (twoFactorCode.length == 6) { + $('input#txtFriendSend2FA').css("border-color", "#ccc"); + } else { + $('input#txtFriendSend2FA').css("border-color", "#ffaaaa"); + allok = false; + } + + if (allok) { + $('input#friendAmount').css("border-color", "#ccc"); + $('#textMessageSend').text('Creating transaction...'); $('#textMessageSend').show(); $('#sendfriendprogstatus').width('3%') $('#sendfriendprog').show(); $('#sendfriendprogstatus').width('10%'); - Engine.sendTransaction('friend', friend, '', amount, function (err, transactionid) { + Engine.sendTransaction('friend', friend, '', amount, twoFactorCode, function (err, transactionid) { if (!err) { updateBalance(); - $('#textMessageSend').html('You sent ' + convertFromSatoshis(amount, COINUNIT) + ' ' + COINUNIT + ' to ' + friend); + $('#textCompleteSendNet').text('You sent ' + convertFromSatoshis(amount, COINUNIT) + ' ' + COINUNIT + ' to ' + friend); $('input#friendAmount').val(''); - $('#textMessageSend').fadeOut(5000); - $('#sendfriendprog').fadeOut(5000); + + + + $("#sendnets2").hide(); + $("#sendnets3").show(); + + + //$('#textMessageSend').fadeOut(5000); + //$('#sendfriendprog').fadeOut(5000); + } else { $('#textMessageSend').addClass('alert alert-danger'); $('#sendfriendprogstatus').width('0%') if (transactionid == "ErrInsufficientFunds") { - $('#textMessageSend').html('Transaction Failed: Insufficient funds'); + $('#textMessageSend').text('Transaction Failed: Not enough funds are currently available to send this transaction'); + } else if (result == 'ErrLocked') { + $('#textMessageSend').text('Transaction Failed: Account is unavailable'); + } else { + $('#textMessageSend').text(transactionid); } + } // alert(transactionid); }); - } else { - $('input#friendAmount').css("border-color", "#ffaaaa"); } @@ -2899,13 +5113,13 @@ function UI() { function sendMoneyStd() { - - var amount = $('input#amount').val(); + var amount = $('#hdamount').val(); amount = convertToSatoshis(amount, COINUNIT); - var address = $('input#toAddress').val(); + var twoFactorCode = $('#txtSendTwoFactor').val(); + $('#textMessageSendStd').removeClass('alert alert-danger'); //check for valid bitcoin address @@ -2923,28 +5137,45 @@ function UI() { allok = false; } + if (twoFactorCode.length == 6) { + $('input#txtSendTwoFactor').css("border-color", "#ccc"); + } else { + $('input#txtSendTwoFactor').css("border-color", "#ffaaaa"); + allok = false; + } + + + if (allok) { - $('#textMessageSendStd').html('Creating transaction...'); + $('#textMessageSendStd').text('Creating transaction...'); $('#textMessageSendStd').show(); $('#sendstdprogstatus').width('3%') $('#sendstdprog').show(); $('#sendstdprogstatus').width('10%'); - Engine.sendTransaction('standard', '', address, amount, function (err, transactionid) { + Engine.sendTransaction('standard', '', address, amount, twoFactorCode, function (err, transactionid) { if (!err) { - $('#textMessageSendStd').html('You sent ' + convertFromSatoshis(amount, COINUNIT) + ' ' + COINUNIT + ' to ' + address + ''); + var confmess = 'You sent ' + _.escape(convertFromSatoshis(amount, COINUNIT)) + ' ' + _.escape(COINUNIT) + ' to ' + _.escape(address) + ''; + + $('#textCompleteSendStd').html(confmess); $('input#amount').val(''); - $('#textMessageSendStd').fadeOut(5000); - $('#sendstdprog').fadeOut(5000); + $('input#toAddress').val(''); + //$('#textMessageSendStd').fadeOut(5000); + //$('#sendstdprog').fadeOut(5000); + + $('#sendstds2').hide(); + $('#sendstds3').show(); } else { if (transactionid == "ErrInsufficientFunds") { - $('#textMessageSendStd').html('Transaction Failed: Insufficient funds'); + $('#textMessageSendStd').text('Transaction Failed: Not enough funds are currently available to send this transaction'); + } else { + $('#textMessageSendStd').text(transactionid) } $('#sendstdprogstatus').width('0%') @@ -2989,15 +5220,18 @@ function UI() { $("#friend").css("border-color", "#ccc"); - Engine.createFriend(username, function (err, result) { + Engine.createFriend(username, '', function (err, result) { if (err) { - alert("Wallet could not be opened.\n\n" + err); + $("#friend").css("border-color", "#ffaaaa"); + $("#addcontactalert").show(); + $("#addcontactalertmessage").text(result); + $("#imgaddcontactwaiting").hide(); } else { $("#friend").val(''); $("#imgaddcontactwaiting").hide(); $("#addcontactsuccess").show(); - $("#addcontactsuccessmessage").html("You requested " + username + " as a contact"); + $("#addcontactsuccessmessage").text("You requested " + username + " as a contact"); $("#addcontactsuccess").fadeOut(5000); updateRequestsMadeByMe(); @@ -3008,17 +5242,19 @@ function UI() { $("#friend").css("border-color", "#ffaaaa"); $("#addcontactalert").show(); - $("#addcontactalertmessage").html("You have already requested " + username + " as a contact"); + $("#addcontactalertmessage").text("You have already requested " + username + " as a contact"); $("#imgaddcontactwaiting").hide(); } }); + + } else { $("#friend").css("border-color", "#ffaaaa"); $("#addcontactalert").show(); - $("#addcontactalertmessage").html("The username could not be found"); + $("#addcontactalertmessage").text("The username could not be found"); $("#imgaddcontactwaiting").hide(); } @@ -3040,6 +5276,8 @@ function UI() { function acceptFriend(username) { + //need to add error handling and messages here + $("#imgrequestwaiting").show(); var target = document.getElementById('imgrequestwaiting'); var spinner = new Spinner(spinneropts).spin(target); @@ -3047,19 +5285,20 @@ function UI() { //$('#friendreq').fadeOut(1000); Engine.acceptFriendRequest(username, function (err, secret) { if (err) { - alert("Wallet could not be opened.\n\n" + err); + + //alert("Wallet could not be opened.\n\n" + err); } else { Engine.isNetworkExist(username, function (err, result) { if (!result) { - Engine.createFriend(username, function (err, result) { + Engine.createFriend(username, '', '', function (err, result) { if (err) { } else { - + $("#imgrequestwaiting").hide(); } }); @@ -3071,7 +5310,7 @@ function UI() { }); } - $("#imgrequestwaiting").hide(); + });