Connecting to an RFID/NFC (ACR122) reader from a JavaScript script - javascript

I'm using a script that reads the data off the e-Passport. However I need some help.
The script I have is from another program called SmartCardShell 3. This is the code:
/**
* ---------
* |.##> <##.| Open Smart Card Development Platform (www.openscdp.org)
* |# #|
* |# #| Copyright (c) 1999-2006 CardContact Software & System Consulting
* |'##> <##'| Andreas Schwier, 32429 Minden, Germany (www.cardcontact.de)
* ---------
*
* This file is part of OpenSCDP.
*
* OpenSCDP is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* OpenSCDP is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with OpenSCDP; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
* #fileoverview Dump the content of a passport with Basic Access Control to file
*
* Before running this script, please make sure that the variable mrz2 is set
* to the second line of the machine readable zone on your passport.
*/
// MRZ of silver data set
//
// P<UTOERIKSSON<<ANNA<MARIA<<<<<<<<<<<<<<<<<<<
// L898902C<3UTO6908061F9406236ZE184226B<<<<<14
// '-DocNo--' '-DoB-' '-DoE-'
//var mrz2 = "L898902C<3UTO6908061F9406236ZE184226B<<<<<14";
// Harvinder Khaira MRZ:
// P<GBRKHAIRA<<HARVINDER<SINGH<<<<<<<<<<<<<<<<
// 4604503228GBR8711164M1711291<<<<<<<<<<<<<<06
var mrz2 = "4604503228GBR8711164M1711291<<<<<<<<<<<<<<06";
// MRZ of Tsukuba data set
//
// WG30004036UTO6007078M0511014<<<<<<<<<<<<<<06
// '-DocNo--' '-DoB-' '-DoE-'
// var mrz2 = "WG30004036UTO6007078M0511014<<<<<<<<<<<<<<06";
//============================================tools.js=========================
// Import some tools
/*
* ---------
* |.##> <##.| Open Smart Card Development Platform (www.openscdp.org)
* |# #|
* |# #| Copyright (c) 1999-2006 CardContact Software & System Consulting
* |'##> <##'| Andreas Schwier, 32429 Minden, Germany (www.cardcontact.de)
* ---------
*
* This file is part of OpenSCDP.
*
* OpenSCDP is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* OpenSCDP is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with OpenSCDP; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
* Tools for accessing a ICAO conform Machine Readable Travel Document
*/
/*
* Calculate a single Basic Access Control (BAC) key from the second
* line of the Machine Readable Zone (MRZ).
*
* The function extracts the Document Number, Date of Birth and Date of Expiration
* from the second line of the machine readable zone
*
* E.g. MRZ of Silver Data Set
* P<UTOERIKSSON<<ANNA<MARIA<<<<<<<<<<<<<<<<<<<
* L898902C<3UTO6908061F9406236ZE184226B<<<<<14
* '-DocNo--' '-DoB-' '-DoE-'
*
* This extract is then hashed, concatenated with a key number and
* hashed again.
* crypto Crypto object used for hashing
* mrz2 String containing second line of MRZ
* keyno Number of key to calculate (1 for Kenc and 2 for Kmac)
*
* Returns Key object
*/
function calculateBACKey(crypto, mrz2, keyno) {
// Convert to byte string
var strbin = new ByteString(mrz2, ASCII);
// Extract Document Number, Date of Birth and Date of Expiration
var hash_input = strbin.bytes(0, 10);
hash_input = hash_input.concat(strbin.bytes(13, 7));
hash_input = hash_input.concat(strbin.bytes(21, 7));
printlnln("Hash Input : " + hash_input.toString(ASCII));
// Hash input
var mrz_hash = crypto.digest(Crypto.SHA_1, hash_input);
printlnln("MRZ Hash : " + mrz_hash);
// Extract first 16 byte and append 00000001 or 00000002
var bb = new ByteBuffer(mrz_hash.bytes(0, 16));
bb.append(new ByteString("000000", HEX));
bb.append(keyno);
// Hash again to calculate key value
var keyval = crypto.digest(Crypto.SHA_1, bb.toByteString());
keyval = keyval.bytes(0, 16);
printlnln("Value of Key : " + keyval);
var key = new Key();
key.setComponent(Key.DES, keyval);
return key;
}
// The SecureChannel object is required as a credential for the CardFile objects
// SecureChannel objects must at least implement a wrap() method. The unwrap()
// method is optional, but called when defined
function SecureChannel(crypto, kenc, kmac, ssc) {
this.crypto = crypto;
this.kenc = kenc;
this.kmac = kmac;
this.ssc = ssc;
this.trace = false;
}
SecureChannel.prototype.enableTrace = function () {
this.trace = true;
}
//
// Increment send sequence counter
//
SecureChannel.prototype.incssc = function () {
var c = this.ssc.bytes(4, 4).toUnsigned() + 1;
bb = new ByteBuffer(this.ssc.bytes(0, 4));
bb.append((c >> 24) & 0xFF);
bb.append((c >> 16) & 0xFF);
bb.append((c >> 8) & 0xFF);
bb.append((c ) & 0xFF);
this.ssc = bb.toByteString();
}
//
// Wrap command-APDU with secure messaging
//
SecureChannel.prototype.wrap = function(apduToWrap) {
if (this.trace) {
printlnln("Command-APDU to wrap :");
printlnln(apduToWrap);
}
var b = new ByteBuffer();
var macb = new ByteBuffer();
// Transform CLA byte and add header
var cla = apduToWrap.byteAt(0);
cla |= 0x0C;
b.append(cla);
b.append(apduToWrap.bytes(1, 3));
this.incssc();
macb.append(this.ssc);
macb.append(b.toByteString().pad(Crypto.ISO9797_METHOD_2));
var do87 = null;
var le = apduToWrap.bytes(apduToWrap.length - 1, 1);
if (apduToWrap.length > 5) {
var lc = apduToWrap.byteAt(4);
var plain = apduToWrap.bytes(5, lc);
plain = plain.pad(Crypto.ISO9797_METHOD_2);
if (this.trace) {
printlnln("Input to cipher:");
printlnln(plain);
}
var cipher = this.crypto.encrypt(this.kenc, Crypto.DES_CBC, plain, new ByteString("0000000000000000", HEX));
do87 = new ByteString("01", HEX);
do87 = do87.concat(cipher);
do87 = new TLV(0x87, do87, TLV.EMV);
do87 = do87.getTLV();
macb.append(do87);
if (apduToWrap.length == 5 + lc) {
le = new ByteString("", HEX);
}
} else if (apduToWrap.length == 4) {
le = new ByteString("", HEX);
}
var do97;
if (le.length > 0) {
do97 = new ByteString("9701", HEX);
do97 = do97.concat(le);
macb.append(do97);
} else {
do97 = new ByteString("", HEX);
}
if (this.trace) {
printlnln("Input to MAC calculation :");
}
var macinput = macb.toByteString().pad(Crypto.ISO9797_METHOD_2);
if (this.trace) {
printlnln(macinput);
}
var mac = this.crypto.sign(this.kmac, Crypto.DES_MAC_EMV, macinput);
if (this.trace) {
printlnln("Calculated MAC :");
printlnln(mac);
}
var macdo = new ByteString("8E08", HEX);
macdo = macdo.concat(mac);
if (do87 != null) {
b.append(do87.length + do97.length + macdo.length);
b.append(do87);
} else {
b.append(do97.length + macdo.length);
}
b.append(do97);
b.append(macdo);
if (le.length > 0) {
b.append(0);
}
if (this.trace) {
printlnln("Wrapped Command-APDU :");
printlnln(b.toByteString());
}
return(b.toByteString());
}
//
// Unwrap response-APDU with secure messaging
//
SecureChannel.prototype.unwrap = function(apduToUnwrap) {
if (this.trace) {
printlnln("Response-APDU to unwrap :");
printlnln(apduToUnwrap);
}
if (apduToUnwrap.length == 2) {
return(apduToUnwrap);
}
var b = new ByteBuffer();
var macb = new ByteBuffer();
this.incssc();
macb.append(this.ssc);
var tl = new TLVList(apduToUnwrap.left(apduToUnwrap.length - 2), TLV.EMV);
var mac = null;
for (i = 0; i < tl.length; i++) {
var t = tl.index(i);
if (t.getTag() == 0x8E) {
mac = t.getValue();
} else {
macb.append(t.getTLV());
}
}
if (mac == null) {
throw new GPError("SecureChannelCredential", GPError.OBJECT_NOT_FOUND, 0, "MAC data object missing");
}
if (this.trace) {
printlnln(macb.toByteString());
}
if (!this.crypto.verify(this.kmac, Crypto.DES_MAC_EMV, macb.toByteString().pad(Crypto.ISO9797_METHOD_2), mac)) {
throw new GPError("SecureChannelCredential", GPError.CRYPTO_FAILED, 0, "MAC verification failed");
}
var t = tl.find(0x87);
if (t != null) {
var cryptogram = t.getValue();
var padding = cryptogram.byteAt(0);
cryptogram = cryptogram.right(cryptogram.length - 1);
if (padding != 0x01) {
throw new GPError("SecureChannelCredential", GPError.INVALID_MECH, padding, "Unsupported padding mode " + padding + " in cryptogram");
}
var plain = this.crypto.decrypt(this.kenc, Crypto.DES_CBC, cryptogram, new ByteString("0000000000000000", HEX));
for (i = plain.length - 1; (i > 0) && (plain.byteAt(i) != 0x80); i--);
b.append(plain.left(i));
}
var t = tl.find(0x81);
if (t != null) {
b.append(t.getValue());
}
var t = tl.find(0x99);
if (t == null) {
b.append(apduToUnwrap.right(2));
} else {
b.append(t.getValue());
}
if (this.trace) {
printlnln("Unwrapped Response-APDU :");
printlnln(b.toByteString());
}
return(b.toByteString());
}
/*
* Open secure channel using basic access control keys
*
* card Card object for access to passport
* crypto Crypto object to be used for cryptographic operations
* kenc Kenc key
* kmac Kmac key
*
* Returns Open secure channel object
*/
function openSecureChannel(card, crypto, kenc, kmac) {
// Perform mutual authentication procedure
printlnln("Performing mutual authentication");
var rndicc = card.sendApdu(0x00, 0x84, 0x00, 0x00, 0x08, [0x9000]);
//printlnln("======RNDicc : " + rndicc);
var rndifd = crypto.generateRandom(8);
//println("======RNDifd : " + rndifd);
var kifd = crypto.generateRandom(16);
var plain = rndifd.concat(rndicc).concat(kifd);
println("Plain Block : " + plain);
var cryptogram = crypto.encrypt(kenc, Crypto.DES_CBC, plain, new ByteString("0000000000000000", HEX));
println("Cryptogram : " + cryptogram);
var mac = crypto.sign(kmac, Crypto.DES_MAC_EMV, cryptogram.pad(Crypto.ISO9797_METHOD_2));
println("MAC : " + mac);
var autresp = card.sendApdu(0x00, 0x82, 0x00, 0x00, cryptogram.concat(mac), 0);
if (card.SW != 0x9000) {
println("Mutual authenticate failed with " + card.SW.toString(16) + " \"" + card.SWMSG + "\". MRZ correct ?");
throw new GPError("MutualAuthentication", GPError.CRYPTO_FAILED, 0, "Card did not accept MAC");
}
println("Response : " + autresp);
cryptogram = autresp.bytes(0, 32);
mac = autresp.bytes(32, 8);
if (!crypto.verify(kmac, Crypto.DES_MAC_EMV, cryptogram.pad(Crypto.ISO9797_METHOD_2), mac)) {
throw new GPError("MutualAuthentication", GPError.CRYPTO_FAILED, 0, "Card MAC did not verify correctly");
}
plain = crypto.decrypt(kenc, Crypto.DES_CBC, cryptogram, new ByteString("0000000000000000", HEX));
println("Plain Block : " + plain);
//var iccifd = rndicc.concat(rndifd);
//println ("==RNDicc + RNDifd : " + iccifd);
if (!plain.bytes(0, 8).equals(rndicc)) {
throw new GPError("MutualAuthentication", GPError.CRYPTO_FAILED, 0, "Card response does not contain matching RND.ICC");
}
if (!plain.bytes(8, 8).equals(rndifd)) {
throw new GPError("MutualAuthentication", GPError.CRYPTO_FAILED, 0, "Card response does not contain matching RND.IFD");
}
var kicc = plain.bytes(16, 16);
keyinp = kicc.xor(kifd);
var hashin = keyinp.concat(new ByteString("00000001", HEX));
var kencval = crypto.digest(Crypto.SHA_1, hashin);
kencval = kencval.bytes(0, 16);
println("Kenc : " + kencval);
var kenc = new Key();
kenc.setComponent(Key.DES, kencval);
var hashin = keyinp.concat(new ByteString("00000002", HEX));
var kmacval = crypto.digest(Crypto.SHA_1, hashin);
kmacval = kmacval.bytes(0, 16);
println("Kmac : " + kmacval);
var kmac = new Key();
kmac.setComponent(Key.DES, kmacval);
var ssc = rndicc.bytes(4, 4).concat(rndifd.bytes(4, 4));
println("SSC : " + ssc);
// Disable to use script-secure messaging secure messaging
var sc = new IsoSecureChannel(crypto);
sc.setEncKey(kenc);
sc.setMacKey(kmac);
sc.setSendSequenceCounter(ssc);
return sc;
//
// Enable to use script-secure messaging secure messaging
// return new SecureChannel(crypto, kenc, kmac, ssc);
//
}
/*
* Write a byte string object to file
*
* The filename is mapped to the location of the script
*
* name Name of file
* content ByteString content for file
*
*/
function writeFileOnDisk(name, content) {
// Map filename
var filename = GPSystem.mapFilename(name, GPSystem.USR);
println("Writing " + filename);
var file = new java.io.FileOutputStream(filename);
file.write(content);
file.close();
}
/*
* Read a byte string object from file
*
* The filename is mapped to the location of the script
*
* name Name of file
*
*/
function readFileFromDisk(name) {
// Map filename
var filename = GPSystem.mapFilename(name, GPSystem.USR);
println("Reading " + filename);
var file = new java.io.FileInputStream(filename);
var content = new ByteBuffer();
var buffer = new ByteString(" ", ASCII);
var len;
while ((len = file.read(buffer)) >= 0) {
content.append(buffer.bytes(0, len));
}
file.close();
return(content.toByteString());
}
/*
* Extract the length of the file from the TLV encoding at the beginning of the
* file
*
* header First bytes read from file
*
* Return Total length of TLV object
*/
function lengthFromHeader(header) {
var value;
value = header.byteAt(1);
if (value > 0x82) {
throw new GPError("lengthfromheader()", GPError.INVALID_DATA, value, "");
}
switch(value) {
case 0x81:
value = header.byteAt(2) + 1;
break;
case 0x82:
value = (header.byteAt(2) << 8) + header.byteAt(3) + 2;
break;
}
return value + 2;
}
//===========================================end of tools.js===================
/*
* Read file from passport and save to disk
*
*/
function handleFile(secureChannel, lds, name, fid) {
printlnln("Reading " + name + " (" + fid + ")...");
// Select file
var ef = new CardFile(lds, ":" + fid);
if (secureChannel) {
// Set secure channel as credential for read access
ef.setCredential(CardFile.READ, Card.ALL, secureChannel);
}
// Read first 4 bytes of file
var res = ef.readBinary(0, 4);
// Determine file length from TLV header
var len = lengthFromHeader(res);
// Read complete file
var res = ef.readBinary(0, len);
printlnln("Content");
printlnln(res);
writeFileOnDisk(name + ".bin", res);
return res;
}
/*
* Save picture from DG2
*
*/
function savePicture(dg2) {
// Save picture to .jpeg file
var tlv = new ASN1(dg2);
var bin = tlv.get(0).get(1).get(1).value;
var offset = bin.find(new ByteString("FFD8", HEX));
if (offset >= 0) {
writeFileOnDisk("face.jpg", bin.bytes(offset));
}
}
// Create card and crypto object
var card = new Card( /* somehow connect to ACR122 reader */);
card.reset(Card.RESET_COLD);
var crypto = new Crypto();
// Select LDS application
var lds = new CardFile(card, "#A0000002471001");
var secureChannel = null;
// Try reading EF_COM to figure out if BAC is needed
card.sendApdu(0x00, 0xB0, 0x9E, 0x00, 0x01);
if (card.SW != 0x9000) {
// Calculate kenc and kmac for mutual authentication from the MRZ data
printlnln("Trying BAC with MRZ2=" + mrz2);
var kenc = calculateBACKey(crypto, mrz2, 1);
var kmac = calculateBACKey(crypto, mrz2, 2);
// Dummy to load crypto libraries (Saves some time later)
crypto.encrypt(kenc, Crypto.DES_CBC, new ByteString("0000000000000000", HEX), new ByteString("0000000000000000", HEX));
secureChannel = openSecureChannel(card, crypto, kenc, kmac);
/* Only works with script based secure messaging. See tools.js for details
secureChannel.enableTrace();
*/
// Enable SELECT commands to be send in secure messaging
// lds.setCredential(CardFile.SELECT, CardFile.ALL, secureChannel);
/*
card.setCredential(secureChannel);
var resp = card.sendSecMsgApdu(Card.ALL, 0x00, 0xA4, 0x02, 0x0C, new ByteString("011E", HEX));
println(resp);
*/
}
handleFile(secureChannel, lds, "EF_COM", "1E");
handleFile(secureChannel, lds, "EF_DG1", "01");
var dg2 = handleFile(secureChannel, lds, "EF_DG2", "02");
savePicture(dg2);
handleFile(secureChannel, lds, "EF_SOD", "1D");
I have incorporated 2 scripts (dumpmrtd.js and tools.js) together.
The issue is that towards the bottom where:
// Create card and crypto object
var card = new Card( /* somehow connect to ACR122 reader */);
card.reset(Card.RESET_COLD);
In order to create a new Card object, it requires a String reader. This is my problem!
I do not know how to obtain this "reader".
Anyone have any ideas?
Thanks in advance
**PS. sorry for the terrible indentation/code quotation. This is the first time im actually posting on this website

Related

How to send a user a downloadable link in react js

I am trying to make it to where a user can upload a file to the site and convert their file. Then download it back. I used JavaScripts Web audio api to do so. Now I just need to let the user download the file back. I create a blob but I am currently stuck.
ConverterSec2.jsx:
class ConverterSec2 extends Component {
render() {
return (
<div className="sec2">
<form>
<input type="file" id="audio-file" name="file" accept="audio/mpeg, audio/ogg, audio/*" />
<button type="Submit" id="convert_btn">Convert to 432Hz</button>
<script src="ihertz_website/src/pages/Converter/compressor.js"></script>
</form>
</div>
)
}
}
export default ConverterSec2
converting.js:
//render proccessed audio
offlineAudioCtx.startRendering().then(function(renderBuffer){
make_download(renderBuffer, offlineAudioCtx.length)
console.log("ERROR: PROCCESSING AUDIO")
})
//download Audio buffer as downloadable WAV file
function make_download(abuffer, total_samples) {
//get duration and sample rate
var duration = abuffer.duration,
rate = abuffer.sampleRate,
offset = 0;
var new_file = URL.createObjectURL(bufferToWave(abuffer, total_samples));
var download_link = document.getElementById("download_link");
download_link.href = new_file;
var name = generateFileName();
download_link.download = name;
}
//generate name of file
function generateFileName() {
var origin_name = fileInput.files[0].name;
var pos = origin_name.lastIndexOf('.');
var no_exit = origin_name.slice(0, pos);
return no_exit + ".wav";
}
//Convert an AudioBuffer to a Blob using WAVE representation
function bufferToWave(abuffer, len) {
var numOfChan = abuffer.numberOfChannels,
length = len * numOfChan * 2 + 44,
buffer = new ArrayBuffer(length),
view = new DataView(buffer),
channels = [], i, sample,
offset = 0,
pos = 0;
//write WAVE header
setUnit32(0x464664952);
setUnit32(length - 8);
setUint32(0x45564157); // "WAVE"
setUint32(0x20746d66); // "fmt " chunk
setUint32(16); // length = 16
setUint16(1); // PCM (uncompressed)
setUint16(numOfChan);
setUint32(abuffer.sampleRate);
setUint32(abuffer.sampleRate * 2 * numOfChan); // avg. bytes/sec
setUint16(numOfChan * 2); // block-align
setUint16(16); // 16-bit (hardcoded in this demo)
setUint32(0x61746164); // "data" - chunk
setUint32(length - pos - 4); // chunk length
// write interleaved data
for(i = 0; i < abuffer.numberOfChannels; i++)
channels.push(abuffer.getChannelData(i));
while(pos < length) {
for(i = 0; i < numOfChan; i++) { // interleave channels
sample = Math.max(-1, Math.min(1, channels[i][offset])); // clamp
sample = (0.5 + sample < 0 ? sample * 32768 : sample * 32767)|0; // scale to 16-bit signed int
view.setInt16(pos, sample, true); // write 16-bit sample
pos += 2;
}
offset++ // next source sample
}
//create Blob
return new Blob([buffer], {type: "audio/wav"});
function setUint16(data) {
view.setUint16(pos, data, true);
pos += 2;
}
function setUnit32(data) {
view.setUint32(pos, data, true);
pos += 4;
}
}

Remove Specific Item From Array With Class Objects Inside

My Problem
I am trying to remove a specific item from my array, however, my array contains other objects which I cannot get a handle to.
I am defining a "Station" like this:
/* CLASS Station
* #param id int unique id this station
* #param name str name of the station
* #param location obj the position of the station
* in the workflow diagram
*/
var Station = function(id, name, posX=null, posY=null) {
this.id = ko.observable(id || self.getUniqueId());
this.name = ko.observable(name);
this.posX = ko.observable(posX);
this.posY = ko.observable(posY);
};
So I added a station to my array by using this function ...
.
.
self.addStation(new Station(76, "Receiving", 0, 10));
Now, I want to know how to remove from array by passing the name, as in:
self.removeStation("Receiving");
I can't figure it out. I have researched all the links on here, with no luck.
Entire Source Code
// CLASS Workflow
var Workflow = function(id, status){
this.status = status || false;
this.id = id;
}
/* CLASS Station
* #param id int unique id this station
* #param name str name of the station
* #param location obj the position of the station
* in the workflow diagram
*/
var Station = function(id, name, posX=null, posY=null) {
this.id = ko.observable(id || self.getUniqueId());
this.name = ko.observable(name);
this.posX = ko.observable(posX);
this.posY = ko.observable(posY);
};
// CLASS ViewModel
var ViewModel = function(workFlowId) {
var self = this; // Scope Trick
/*******************************
* Observables
*-----------------------------*/
self.station = ko.observableArray();
/*******************************
* Initialize The Builder
*-----------------------------*/
self.workflowId = ko.observable();
/*******************************
* Arrays
*-----------------------------*/
self.workflow = ko.observableArray();
/*******************************
* Actions
*-----------------------------*/
/* Method: initWorkflow
*
* Desc: When the user gets to the builder
* page, we have to configure a Workflow.
* If they are loading a saved one, the ID
* will be passed. If they are starting a new
* one, we will give it a unique ID.
*
* #param int workFlowId The id of the workflow
* #return int workFlowId The id is returned
*/
self.initWorkflow = function(workFlowId, status=false) {
var id;
if(!workFlowId){
/* Call Function to generate unique ID */
id = self.getUniqueId();
} else {
id = workFlowId;
}
/* Set ID */
this.workflowId = id;
this.workflow = new Workflow(id, status);
};
/*-------------------------------------------------------
* Method: addStation
*
* Desc: Adds a station to current workflow
* #param station object A station object
*--------------------------------------------------------*/
self.addStation = function(station){
self.station.push(station);
}
/* Remove Station - */
self.removeStation = function (Name) {
for( var i = 0; i < self.station().length; i++){
console.dir("In Remove Function: " + self.station()[i]);
}
}
/*-------------------------------------------------------
* Method: getUniqueId
*
* Desc: Generates a random unique Id
* #returns id int A unique random ID
*--------------------------------------------------------*/
self.getUniqueId = function(){
var id = new Date().getTime();
console.group("In Funtion: self.getUniqueId");
console.log("Returned unique id of: " + id);
console.groupEnd("In Funtion: self.getUniqueId");
return id;
}
/* Start it up */
self.initWorkflow(workFlowId);
//------------------------
// UNIT TESTING
//------------------------
//........ STATION RELATED ..........................
// 1. Add
self.addStation(new Station(76, "Receiving", 0, 10));
// 2. Remove
self.removeStation("Receiving");
} // end ViewModel
// Instantiate the ViewModel
window.view_model = new ViewModel();
// Away we go...
ko.applyBindings(window.view_model);
I can't seem to get a hold of the name in the array:
// DON'T WORK
self.station()[i].Station.name
Thanks for looking.
John
u could use function to find its index look like:
function arrayFirstIndexOf(array, predicate, predicateOwner) {
for (var i = 0, j = array.length; i < j; i++) {
if (predicate.call(predicateOwner, array[i])) {
return i;
}
}
return -1;
}
then in your /* Remove Station - */u edit code look like this:
/* Remove Station - */
self.removeStation = function (Name) {
var index = arrayFirstIndexOf(self.station(), function(item){
return item.name === Name;
});
index > -1 && self.station.splice(index, 1);
}
Hope this help !
you can use ko.utils.arrayRemoveItem to remove an item from the array you need to find it first
/* Remove Station */
self.removeStation = function (search) {
// find the station
var foundStation = ko.utils.arrayFirst(this.station, function(item) {
return ko.utils.stringStartsWith(item.name().toLowerCase(), search);
});
// remove the station
ko.utils.arrayRemoveItem(this.station, foundStation);
}
Thank you for your responses. But I found an acceptable solution to my problem by using this following code:
/* Remove Station */
self.removeStation = function (Name) {
var c = -1;
ko.utils.arrayForEach(this.station(), function(item) {
c++;
var value = item.name();
if(value==Name){
console.log("found it! at index " + c);
self.station().splice(c,1);
}
});
// Force the UI to update
self.station.valueHasMutated();
}
I realize this is not the cleanest or most efficient solution, but it seems to work. I would love to know how to optimize this, but that's above my pay grade, lol.
sry for long days answer, i think the problem you got is about variable you declared and variable you use not the same type, and you not use any convert of them when u assigned to your object look like:
var Station = function(id, name, posX=null, posY=null) {
this.id = ko.observable(id || self.getUniqueId()); //observalbe declared
this.name = ko.observable(name);//observalbe declared
this.posX = ko.observable(posX);//observalbe declared
this.posY = ko.observable(posY);//observalbe declared
};
but then you use assign look like
var newObj = {
id: "82",
name: "Updated Name",
posX: 92,
posY: 88
}
self.modifyStation("name","Shipping",newObj);
when u assign it:
objNewItem.id = oldId; // Put old ID back in
self.station.push(objNewItem); //not convert its properties to observable will make some errors on binding html.
i make a working jsfildle with your example, please read it, and reply when u not understand which part. hope this help.
function arrayFirstIndexOf(array, predicate, predicateOwner) {
for (var i = 0, j = array.length; i < j; i++) {
if (predicate.call(predicateOwner, array[i])) {
return i;
}
}
return -1;
}
// CLASS Workflow
var Workflow = function(id, status) {
this.status = status || false;
this.id = id;
}
/* CLASS Field
* #param id int unique id this station
* #param fieldType str type of input
* #param fieldName obj name of the input
* #param options array options array
*/
var Field = function(fieldId, fieldType, fieldName, options) {
this.fieldId = ko.observable(fieldId);
this.fieldType = ko.observable(fieldType);
this.fieldName = ko.observable(fieldName);
this.options = ko.observableArray(options);
};
/* CLASS Station
* #param id int unique id this station
* #param name str name of the station
* #param location obj the position of the station
* in the workflow diagram
*/
var Station = function(id, name, posX = null, posY = null, fields) {
this.id = ko.observable(id || self.getUniqueId());
this.name = ko.observable(name);
this.posX = ko.observable(posX);
this.posY = ko.observable(posY);
this.fields = ko.observableArray(fields || []);
};
// CLASS ViewModel
var ViewModel = function(workFlowId) {
var self = this; // Scope Trick
/*******************************
* Observables
*-----------------------------*/
self.fieldId = ko.observable();
self.fieldName = ko.observable();
self.fieldType = ko.observable();
/*******************************
* Initialize The Builder
*-----------------------------*/
self.workflowId = ko.observable();
/*******************************
* Arrays
*-----------------------------*/
self.workflow = ko.observableArray();
self.station = ko.observableArray();
self.fields = ko.observableArray();
/*******************************
* Computed Observables
*-----------------------------*/
/*******************************
* Actions
*-----------------------------*/
/* Method: initWorkflow
*
* Desc: When the user gets to the builder
* page, we have to configure a Workflow.
* If they are loading a saved one, the ID
* will be passed. If they are starting a new
* one, we will give it a unique ID.
*
* #param int workFlowId The id of the workflow
* #return int workFlowId The id is returned
*/
self.initWorkflow = function(workFlowId, status = false) {
var id;
if (!workFlowId) {
/* Call Function to generate unique ID */
id = self.getUniqueId();
} else {
id = workFlowId;
}
/* Set ID */
this.workflowId = id;
this.workflow = new Workflow(id, status);
};
/*-------------------------------------------------------
* Method: addStation
*
* Desc: Adds a station to current workflow
* #param station object A station object
*--------------------------------------------------------*/
self.addStation = function(station) {
self.station.push(station);
}
/* Remove Station */
self.removeStation = function(Name) {
var index = arrayFirstIndexOf(self.station(), function(item){
return item.name() === Name;
});
index > -1 && self.station.splice(index, 1);
}
/* Update A Station ** NEEDS FIXING **
*
* #param findBy string Property to find ( "id" or "name")
* #param cmpVal string Value to compare against
* #param objNewItem object The new object replacing old
* */
self.modifyStation = function(findBy, cmpVal, objNewItem) {
var sourceIndex;
var oldId;
var found = false;
/* Find Index Of Old Station */
var c = -1;
ko.utils.arrayForEach(this.station(), function(item) {
c++;
switch (findBy) {
case "id":
var value = ko.unwrap(item.id);
if (value == cmpVal) {
sourceIndex = c;
oldId = value;
found = true;
}
break;
case "name":
var value = ko.unwrap(item.name);
if (value == cmpVal) {
sourceIndex = c;
oldId = ko.unwrap(item.id);
found = true;
}
break;
}
});
/* Remove Old */
if (found === true) {
self.station().splice(sourceIndex, 1);
/* Insert New Station
* [For Now] not allowing updating of ID. Only
* can update the other properties (yes, I realize that
* only leaves "name", but more will be added )
*/
objNewItem.id(oldId); // Put old ID back in
self.station.push(objNewItem);
} else {
alert(cmpVal + " was not found in array!");
}
}
self.addField = function(stationId, newField) {
var c = -1;
found = false;
ko.utils.arrayForEach(this.station(), function(item) {
c++;
var value = ko.unwrap(item.id);
console.log(value, c);
if (value == stationId) {
//console.log("found it! at index " + c);
self.station()[c].fields.push(newField);
}
});
self.station.valueHasMutated();
};
self.modifyField = function(stationId, oldFieldId, newObjField) {
// TO DO
};
self.removeField = function(field) {
self.fields.remove(field);
};
/* Perform Test On Button Click */
self.doTest = function() {
self.removeStation("Shipping");
}
self.doTest2 = function() {
var newObj = {
id: ko.observable("82"),
name: ko.observable("Updated Name"),
posX: ko.observable(92),
posY: ko.observable(88),
fields: ko.observableArray([])
}
self.modifyStation("name", "Shipping", newObj);
self.station.valueHasMutated();
}
// Add Fields
self.doTest3 = function() {
var objNewField = {
fieldId: 456,
fieldName: "Last Name",
fieldType: "Text"
}
self.addField(86, objNewField);
}
/*-------------------------------------------------------
* Method: getUniqueId
*
* Desc: Generates a random unique Id
* #returns id int A unique random ID
*--------------------------------------------------------*/
self.getUniqueId = function() {
var id = new Date().getTime();
console.group("In Funtion: self.getUniqueId");
console.log("Returned unique id of: " + id);
console.groupEnd("In Funtion: self.getUniqueId");
return id;
}
/*-------------------------------------------------------
* Method: debugAll
*
* Desc: Console Logs Our View Model
*--------------------------------------------------------*/
this.debugAll = function() {
console.group("Debug:");
// Workflow
console.group("Current Workflow Id")
console.log("self.workflowId = " + self.workflowId);
console.groupEnd("Current Workflow Id")
// Stations
console.group("Stations In This Workflow")
console.table(self.station());
console.groupEnd("Stations In This Workflow")
// Fields
console.groupEnd("Debug:");
}
/* Start it up */
self.initWorkflow(workFlowId);
//------------------------
// UNIT TESTING
//------------------------
//........ STATION RELATED ..........................
// 1. Add
self.addStationShipping = function() {
self.addStation(new Station(86, "Shipping", 0, 10, [{
fieldId: 45,
fieldName: "First Name",
fieldType: "Text"
}]));
}
self.addStation(new Station(76, "Receiving", 0, 10, null));
self.addStationShipping();
/* Dump ViewModel */
self.debugAll();
//----------------------------------------------------------------
} // end ViewModel
// Instantiate the ViewModel
window.view_model = new ViewModel(1213131212);
// Away we go...
ko.applyBindings(window.view_model);
// Page Utility Functions
function wait(ms) {
var start = new Date().getTime();
var end = start;
while (end < start + ms) {
end = new Date().getTime();
}
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.4.2/knockout-min.js"></script>
<div>
<ul data-bind="foreach: station">
<li data-bind="text: 'id: ' + id()"></li>
<li data-bind="text: 'name: ' +name()"></li>
<li data-bind="text: 'posX: ' +posX()"></li>
<li data-bind="text: 'posY: ' +posY()"></li>
<ul data-bind="foreach: fields">
<li data-bind="text: fieldId"></li>
<li data-bind="text: fieldName"></li>
<li data-bind="text: fieldType"></li>
</ul>
</ul>
</div>
<button data-bind="click: doTest">Remove "Shipping" From Station Array</button>
<button data-bind="click: doTest2">Update "Shipping" In Station Array</button>
<hr>
<h3>Test: Add Field To Shipping Station</h3>
<button data-bind="click: function() { addStationShipping()}">Add Shpping</button>
<button data-bind="click: doTest3()">Add Field</button>

Pitch shift in multi channel mode using soundtouch-js

I am using https://github.com/jakubfiala/soundtouch-js soundtouch library to do some pitch shifting on audio files. the lib works fine with pitch shifting but I don't know how to control the audio file's channels to switch them on/off.
I am following this pen https://codepen.io/ezzatly/pen/LLqOgJ?editors=1010
var source = {
extract: function (target, numFrames, position) {
$("#current-time").html(minsSecs(position / (context.sampleRate)));
console.log('width: ', 100 * position / (bufferDuration * context.sampleRate));
$("#progress").width(100 * position / (bufferDuration * context.sampleRate) + "%");
if (Math.round(100 * position / (bufferDuration * context.sampleRate)) == 100 && is_playing) {
//stop recorder
recorder && recorder.stop();
__log('Recording complete.');
// create WAV download link using audio data blob
createDownloadLink();
if (typeof recorder != "undefined") {
recorder.clear();
}
is_playing = false;
}
var l = buffer.getChannelData(0);
if (buffer.numberofChannels > 1) {
var r = buffer.getChannelData(1);
} else {
var r = buffer.getChannelData(0);
}
for (var i = 0; i < numFrames; i++) {
target[i * 2] = l[i + position];
target[i * 2 + 1] = r[i + position];
}
return Math.min(numFrames, l.length - position);
}
};
node.onaudioprocess = function (e) {
if (buffer.getChannelData) {
pos += BUFFER_SIZE / context.sampleRate;
console.log('position: ', pos);
var l = e.outputBuffer.getChannelData(0);
var r = e.outputBuffer.getChannelData(1);
var framesExtracted = f.extract(samples, BUFFER_SIZE);
if (framesExtracted == 0) {
node.disconnect();
}
for (var i = 0; i < framesExtracted; i++) {
l[i] = samples[i * 2];
r[i] = samples[i * 2 + 1];
}
leftchannel.push(new Float32Array(l));
rightchannel.push(new Float32Array(r));
recordingLength += BUFFER_SIZE;
}
};
any help?
First, I have a slightly more modernized version of SoundTouchJs in my GitHub. Might make it slightly easier to hack around.
Second, you probably want to attach a ChannelSplitterNode to your connected audio context. I haven't played with this, but I would think that's what you would need to independently control the gain on each channel. Again, I'm guessing, but hopefully that'll push you in the right direction.

Calculate the key id of public key using openpgp.js

Using OpenPGP.js v1.3.0 I can successfully create a public/private key pair and encrypt/decrypt ok.
I am struggling to obtain the key id using this function (found in openpgp.js and public_key.js):
/**
* Calculates the key id of the key
* #return {String} A 8 byte key id
*/
PublicKey.prototype.getKeyId = function () {
if (this.keyid) {
return this.keyid;
}
this.keyid = new type_keyid();
if (this.version == 4) {
this.keyid.read(util.hex2bin(this.getFingerprint()).substr(12, 8));
} else if (this.version == 3) {
this.keyid.read(this.mpi[0].write().substr(-8));
}
return this.keyid;
};
PublicKey() also in openpgp.js:
/**
* #constructor
*/
function PublicKey() {
this.tag = enums.packet.publicKey;
this.version = 4;
/** Key creation date.
* #type {Date} */
this.created = new Date();
/** A list of multiprecision integers
* #type {module:type/mpi} */
this.mpi = [];
/** Public key algorithm
* #type {module:enums.publicKey} */
this.algorithm = 'rsa_sign';
// time in days (V3 only)
this.expirationTimeV3 = 0;
/**
* Fingerprint in lowercase hex
* #type {String}
*/
this.fingerprint = null;
/**
* Keyid
* #type {module:type/keyid}
*/
this.keyid = null;
}
and trying to get the key id like this:
var publickey = openpgp.key.readArmored(myPublicKey);
//var keyID = openpgp.packet.PublicKey(publickey).getKeyId()[0].toHex();
var keyID = openpgp.PublicKey(publickey).getKeyId()[0].toHex();
console.log(keyID);
which gives me the error:
TypeError: openpgp.PublicKey is not a function.
Thanks.
I asked the question here:
http://www.mail-archive.com/list#openpgpjs.org/msg00932.html
and received a very helpful reply.
var openpgp = window.openpgp;
var testPublicKey = sessionStorage.getItem('testPublicKey');
var foundKeys = openpgp.key.readArmored(testPublicKey).keys;
if (!foundKeys || foundKeys.length !== 1) {
throw new Error("Key not read, or more than one key found");
}
var pubKey = foundKeys[0]; foundKeys = null;
var getFingerprint = function (key) {
// openpgp.key <- Class
// key <- Instance received by params
return key.primaryKey.fingerprint;
};
var getKeyId = function (key) {
return key.primaryKey.getKeyId().toHex();
}
console.log(getFingerprint(pubKey));
console.log(getKeyId(pubKey));

Google Maps Store Locator Display distance in Panel

I am using this version of the store locator https://github.com/googlemaps/js-store-locator
I would like to display the distance to each location once the user has submitted their search input (Enter a location). https://googlemaps.github.io/js-store-locator/examples/panel.html
I tried creating a duplicate of storeLocator.Store.prototype.distanceTo on line 441 of http://pastebin.com/ZGrWN6ib
storeLocator.Store.prototype.distanceTo = function(a) {
var b = this.getLocation(),
c = storeLocator.toRad_(b.lat()),
d = storeLocator.toRad_(b.lng()),
b = storeLocator.toRad_(a.lat()),
e = storeLocator.toRad_(a.lng());
a = b - c;
d = e - d;
c = Math.sin(a / 2) * Math.sin(a / 2) + Math.cos(c) * Math.cos(b) * Math.sin(d / 2) * Math.sin(d / 2);
return 12742 * Math.atan2(Math.sqrt(c), Math.sqrt(1 - c))
};
I am able to pass the first parameter but the second parameter var b = this.getLocation() keeps coming up as undefined
Can I check to see to see if the user location is set, and if so run the function? or do i need to hook into one of the listeners?
if(!this.getLocation()){
return 0;
}
else {
var b = this.getLocation(),
c = storeLocator.toRad_(b.lat()),
d = storeLocator.toRad_(b.lng()),
b = storeLocator.toRad_(a.lat()),
e = storeLocator.toRad_(a.lng());
a = b - c;
d = e - d;
c = Math.sin(a / 2) * Math.sin(a / 2) + Math.cos(c) * Math.cos(b) * Math.sin(d / 2) * Math.sin(d / 2);
return 12742 * Math.atan2(Math.sqrt(c), Math.sqrt(1 - c));
}
You are using this.getLocation(), that means your file has to be a storeLocator.Store.prototype.
The getLocation() method probably belongs to the store-locator.min.js, which should be included in your html file, maybe thats why you can not find the getLocation() method.
In the Google Maps GitHub example page, the store-locator.min.js is included in the panel.html file.
From this example folder, you can see the structure of files.
Got it! So if you want to display the distance in the store list view here's what you can do:
First beautify the store-locator.min.js file for easier reading with something like http://jsbeautifier.org/
Then modify this portion of function storeLocator.Panel.prototype.init_
if (b.geometry) {
this.directionsFrom_ = b.geometry.location;
a.directionsVisible_ && a.renderDirections_();
var c = a.get("view");
c.highlight(null);
var d = c.getMap();
b.geometry.viewport ? d.fitBounds(b.geometry.viewport) : (d.setCenter(b.geometry.location), d.setZoom(12));
c.refreshView();
a.listenForStoresUpdate_();
// New Addition
// You will need to create helper functions to set the cookie, i will include below
storeLocator.setCookie("searchAddy",b.geometry.location,1);
// End New Addition
} else a.searchPosition(b.name)
Then add this function right above storeLocator.Panel.prototype.stores_changed
storeLocator.Panel.prototype.ifDistanceFrom = function(b) {
var str = storeLocator.getCookie('searchAddy');
var regex = /\((-?[0-9]+\.[0-9]+), (-?[0-9]+\.[0-9]+)\)/g;
var latlonArray = [];
var match = regex.exec(str);
while (match) {
latlonArray.push({
"lat" : match[1],
"lon" : match[2]
});
match = regex.exec(str);
};
var a = new google.maps.LatLng(latlonArray[0].lat,latlonArray[0].lon),
b = b.getLocation(),
c = storeLocator.toRad_(b.lat()),
d = storeLocator.toRad_(b.lng()),
b = storeLocator.toRad_(a.lat()),
e = storeLocator.toRad_(a.lng());
a = b - c;
d = e - d;
c = Math.sin(a / 2) * Math.sin(a / 2) + Math.cos(c) * Math.cos(b) * Math.sin(d / 2) * Math.sin(d / 2);
return 12742 * Math.atan2(Math.sqrt(c), Math.sqrt(1 - c));
};
Now modify this portion of function storeLocator.Panel.prototype.stores_changed
for (var b = function() {
a.highlight(this.store, !0)
}, e = 0, f = Math.min(10, c.length); e < f; e++) {
var g = c[e].getInfoPanelItem();
g.store = c[e];
// New Addition
if(storeLocator.checkCookie() != null) {
var distance = this.ifDistanceFrom(g.store);
$(g).find('input').val(distance.toFixed(1));
}
else {
$(g).find('.dist-span').hide();
}
// End New Addition
d && c[e].getId() == d.getId() && $(g).addClass("highlighted");
g.clickHandler_ || (g.clickHandler_ = google.maps.event.addDomListener(g, "click", b));
this.storeList_.append(g)
}
Lastly modify your Data Feed file DataSource.prototype.parse_ function
DataSource.prototype.parse_ = function(csv) {
var stores = [];
var rows = csv.split('\n');
var headings = this.parseRow_(rows[0]);
for (var i = 1, row; row = rows[i]; i++) {
row = this.toObject_(headings, this.parseRow_(row));
var features = new storeLocator.FeatureSet;
// If you have features
features.add(this.FEATURES_.getById('featureName1-' + featureName1));
// New Addition
var distInput = "<span class='dist-span'>Distance: <input class='dist-input' value='' size='2' type='text' /> mi.</span>"
// End New Addition
var position = new google.maps.LatLng(row.Ycoord, row.Xcoord);
var shop = this.join_([row.Street_add], ', ');
var locality = row.Locality+', '+row.State+' '+row.Postcode;
var store = new storeLocator.Store(row.Fcilty_typ, position, features, {
title: row.Fcilty_nam,
// New Addition
address: this.join_([shop, locality, distInput], '<br>'),
// End New Addition
hours: row.Hrs_of_bus
});
stores.push(store);
}
return stores;
};
Below are the helper functions you will need to add to store-locator.min.js for setting the cookie.
storeLocator.setCookie = function(cname, cvalue, exdays) {
var d = new Date();
d.setTime(d.getTime() + (exdays*24*60*60*1000));
var expires = "expires="+d.toUTCString();
document.cookie = cname + "=" + cvalue + "; " + expires;
// alert('Cookie set: '+ cvalue);
};
storeLocator.getCookie = function (cname) {
var name = cname + "=";
var ca = document.cookie.split(';');
for(var i=0; i<ca.length; i++) {
var c = ca[i];
while (c.charAt(0)==' ') c = c.substring(1);
if (c.indexOf(name) == 0) return c.substring(name.length, c.length);
};
return "";
};
storeLocator.checkCookie = function () {
var a = storeLocator.getCookie("searchAddy");
if (a != "") {
return 1;
}
else {
return null;
}
};
Inside the DataSource.prototype.parse_ function I added a new div element within the misc parameter, and used the store id as a unique div class.
var id = row.name.toLowerCase().replace(/[\. ,:-]+/g, '-');
var position = new google.maps.LatLng(row.latitude, row.longitude);
var locality = this.join_([row.state, row.postal_code], ', ');
var store = new storeLocator.Store(id, position, features, {
title: row.name,
address: this.join_([row.street, locality], '<br>'),
phone: row.phone_number,
misc: '<div class="distance '+id+'"></div>',
});
And then inside the DataSource.prototype.toObject_ I just added some jQuery for updating the text when a location is searched.
var panelDiv = document.getElementById('panel');
var panel = new storeLocator.Panel(panelDiv, {
view: view
});
google.maps.event.addListener(panel, 'geocode', function(result) {
panel.stores.forEach(function(store){
var distance = Math.round(store.distanceTo(result.geometry.location));
jQuery('.distance').each(function(){
if (jQuery(this).hasClass(store.getId())) {
jQuery(this).text(distance);
}
});
});
});
There's probably a better way, but this worked in my case.

Categories