Convert Binary/Hex to Base91 / Ascii85 / Base64 - javascript

I need an JS encoder to base91 or Ascii85 for a binary number. I do have a google sheet, which looks likes as follows:
Code is:
function columnToLetter(column)
{
//https://stackoverflow.com/a/21231012/14226613
var temp, letter = '';
while (column > 0)
{
temp = (column - 1) % 26;
letter = String.fromCharCode(temp + 65) + letter;
column = (column - temp - 1) / 26;
}
return letter;
}
function onEdit(e){
var activeSheet = e.source.getActiveSheet();
if (activeSheet.getActiveCell().getRow() == 3) {
var spreadsheet = SpreadsheetApp.getActive();
var str = ''
for (i=0; i < 32; i++) {
var colName = columnToLetter(i+1)
if (spreadsheet.getRange(colName+ '3').isChecked()) {
str = str + '1'
} else {
str = str + '0'
}
}
let number = parseInt(str,2)
let hexx = ((+number).toString(16))
spreadsheet.getRange('R7').setValue(hexx);
}
}
Currently an encoding to Hex does work well - but I need a more efficient way to encode this binary flags.
Purpose: this encoded pattern might be in the future a part of a product / spare part name, where I have 5-6 characters maximum (as like in the 80's :D).
Ascii85 would be great, hence the represenation of 'ffffffff' is 's8W-!' where i would save 3 characters. For testing/encoding I used cryptii.
The solution should be pure JS without external dependencies/requires and/or should be able in run Google's environment.
Do you know any libs which I could use for this purpose? Base91 would be also fine - as long as we have printable characters.
The perfect solution would be configurable JS encoder/decoder - where the patterns respectively the characters for encoding could be preselected.
Update:
Figured out that Ascii85 or Base91 or not that good for announcing codes per phone - hence you don't wanna find all characters easily on your keyboard.
It is true that base64 is less efficient but with adapting the requirements I was able to find a solution with max. 4-5 characters after experimenting a few days.
I am going to try to answer my own question - see below.
Updated requirements:
16 bits for payload
4 bits (number 1..15) for family/recipe/type selection
4 bits for CRC4
base64 encoded without external dependencies and with adjustable alphabet

This solution works for me. The whole 3Byte is been encoded into 4 characters. I adapted the alphabet for replacing some missleading characters (0,O,i,l,1 etc.).
The base64 padding ('=') is been removed after encoding and will be added before decoding inside the functions. CRC4 is not perfect - better than no CRC :)
I am happy for any feedback, suggestions further optimisations. Thanks.
Google Sheet frontend:
Another tab in this documents where you can define the variations/families/recipes:
Here comes the code for the app script (special credits to #Kamil Kiełczewski for his base64 snippert):
// Convert a hex string to a byte array
function hexToBytes(hex) {
// https://stackoverflow.com/a/34356351/14226613
for (var bytes = [], c = 0; c < hex.length; c += 2)
bytes.push(parseInt(hex.substr(c, 2), 16));
return bytes;
}
function hexToBytes2(hexString) {
//https://stackoverflow.com/a/62365404/14226613
return hexString.match(/.{1,2}/g).map(x=> +('0x'+x));
}
// Convert a byte array to a hex string
function bytesToHex(bytes) {
// https://stackoverflow.com/a/34356351/14226613
for (var hex = [], i = 0; i < bytes.length; i++) {
var current = bytes[i] < 0 ? bytes[i] + 256 : bytes[i];
hex.push((current >>> 4).toString(16));
hex.push((current & 0xF).toString(16));
}
return hex.join("");
}
function hex2bin(hex, fill = false){
//https://stackoverflow.com/a/45054052/14226613
let bin = (parseInt(hex, 16).toString(2)).padStart(8, '0');
if (fill) {
//https://stackoverflow.com/a/27641835/14226613
bin = "000000000000000000000000".substr(bin.length) + bin;
}
return bin
}
function b64(){
const abc = "ABCDEFGH!JKLMN=PQRSTUVWXYZabcdefghijk:mnopqrstuvwxyz+-$%23456789"; // base64 alphabet
function encode (byteArray) {
//https://stackoverflow.com/a/62365404/14226613
const bin = n => n.toString(2).padStart(8,0); // convert num to 8-bit binary string
const l = byteArray.length
let result = '';
for(let i=0; i<=(l-1)/3; i++) {
let c1 = i*3+1>=l; // case when "=" is on end
let c2 = i*3+2>=l; // case when "=" is on end
let chunk = bin(byteArray[3*i]) + bin(c1? 0:byteArray[3*i+1]) + bin(c2? 0:byteArray[3*i+2]);
let r = chunk.match(/.{1,6}/g).map((x,j)=> j==3&&c2 ? '=' :(j==2&&c1 ? '=':abc[+('0b'+x)]));
result += r.join('');
}
//remove padding - http://jsfiddle.net/bicycle/c49fgz8x/
result = result.replace(/={1,2}$/, '');
return result;
}
function decode (str) {
//https://stackoverflow.com/a/62364519/14226613
let result = [];
//add padding http://jsfiddle.net/bicycle/c49fgz8x/
str = str + Array((4 - str.length % 4) % 4 + 1).join('=');
for(let i=0; i<str.length/4; i++) {
let chunk = [...str.slice(4*i,4*i+4)]
let bin = chunk.map(x=> abc.indexOf(x).toString(2).padStart(6,0)).join('');
let bytes = bin.match(/.{1,8}/g).map(x=> +('0b'+x));
result.push(...bytes.slice(0,3 - (str[4*i+2]=="=") - (str[4*i+3]=="=")));
}
return result;
}
return {
encode: encode,
decode: decode
}
}
var crc4 = function(data) {
//https://gist.github.com/bryc/5916452ad0d1ef5c39f1a3f19566d315
var POLY = 0xC, INIT = 0, XOROUT = 0;
for(var crc = INIT, i = 0; i < data.length; i++) {
crc = crc ^ data[i];
for(var j = 0; j < 8; j++) {
crc = crc & 1 ? crc >>> 1 ^ POLY : crc >>> 1;
}
}
return (crc ^ XOROUT) & 0xF;
};
function get_family(input, sheet, range) {
//get array with family names
let familiesValNames =sheet.getRange(range).getValues();
let obj = {};
for (i=0; i < familiesValNames.length; i++) {
if ( //if input is string - loop through array and find the corresponding number
(typeof input === 'string' && input == familiesValNames[i]) ||
//if it is a number between 1..15
(typeof input === 'number' && input == (i+1))) {
obj.number = i + 1;
//first values are in row 4 (=offset)
let sRange = 'A' + (4 + i) + ':P' + (4 + i)
obj.names = sheet.getRange(sRange).getValues();
break;
}
}
return obj
}
function set_family_object (sheet, range, fixed = false){
let famtypes={1:{family:["Spares ACME 1"],types:[["spare","Alpha v2","Alpha v1","Alpha v0","spare","Beta v2","Beta v1","Beta v0","spare","Gamma v2","Gamma v1","Gamma v0","spare","Delta v2","Delta v1","Delta v0"]]},2:{family:["Spares ACME 2"],types:[["spare","spare","spare","spare","spare","spare","spare","spare","spare","spare","spare","spare","Omega v2","Omega v2","Omega v1","Omega v0"]]},3:{family:["ACME Tools"],types:[["p612 lcp2","p612 lcp1","p612 lcp0","p512 lcp2","p512 lcp1","p512 lcp0","p671 lcp2","p671 lcp1","p671 lcp0","p471 lcp2","p471 lcp1","p471 lcp0","blm v2","blm v1","blm v0","common"]]},4:{family:["Animals"],types:[["Sea Otter","Cat","Panda","Fox","Dog","Lemur","Penguin","Mulgara","Degu","Kiwi","Koala","Monkey","Whale","Oribi","Chimpanzee","Deer"]]},5:{family:["Male Names"],types:[["James","John","Robert","Michael","Willian","David","Richard","Thomas","Charles","Daniel","Matthew","Donald","Mark","Brian","Edward","George"]]},6:{family:["Female Names"],types:[["Olivia","Emma","Ava","Sophia","Isabella","Charlotte","Amelia","Mia","Harper","Evelyn","Emily","Ella","Camila","Luna","Mila","Aria"]]},7:{family:["Car Brands"],types:[["Skoda","Renault","Citroen","Dacia","BMW","Audi","VW","Ford","Peugeot","Honda","Hyundai","Toyota","Kia","Opel","Porsche","Tata"]]},8:{family:["Horror Movies"],types:[["Wrong Turn","Descent","Hostel","Vacancy","Joy ride","Joy Ride","Midsommar","Cave","Eden Lake","Frozen","Ruins","Turistas","Hills have Eyes","Cabin of Woods","Soon the Darkness","Deep"]]},9:{family:["Things for Salad"],types:[["Lettuce","Cucumber","Cheese","Carrot","Tomato","Spinach","Crouton","Bacon","Egg","Onion","Chicken","Avocado","Pepper","Cabbage","Olive Oil","Feta"]]},10:{family:["TV Scientist"],types:[["Spock","Gaius Baltar","Leonard McCoy","Dr Bruce Banner","MacGyver","Dr Krieger","Walter White","Dexter","Lex Luthor","Walter Bishop","Data","The Doctor","Dr Quest","Dana Scully","Jimmy Neutron","Howard Wolowitz"]]},11:{family:["Funny Movies"],types:[["Spaceballs","Airplane!","Monty Python","Blazin Saddles","Lego Movie 2","Shrek","Naked gun","Austin Powers","Shaun of the Dead","Finding Nemo","The Big Lebowski","Dumb and Dumber","Simpsons","Toy Story","South Park","Life of Brian"]]},12:{family:["Actors"],types:[["Jamie Foxx","Val Kilmer","Christian Bale","Cate Blanchett","Jim Carrey","Meryl Streep","Joaquin Phoenix","Sam Rockwell","Leonardi DiCaprio","Heath Ledger","Robin Williams","Bill Murray","Will Ferrel","Jack Nicholson","Kevin spacey","Tom Cruise"]]},13:{family:["Tech Blogs"],types:[["TechCrunch","Wired","TechnoByte","Golem","Heise","Gizmodo","Engadget","Ars Technica","Techaeris","Top10Tech","10xDS","The Verge","TECHi","Urbangeekz","Techsppoks","Mashable"]]},14:{family:["PC Brands"],types:[["Asus","Intel","Dell","HP","Alienware","Microsoft","Apple","ACer","Sony","MSI","Razer","Toshiba","Gateway","LG","Compaq","Panasonic"]]},15:{family:["Evil Companies"],types:[["Facebook","Comcast","Time Warner","Google","Apple","Twitter","Go Daddy","Verizon","Yahoo","Microsoft","Zynga","BuzzFeed","McAfee","Amazon","Youtube","Hulu"]]}};
if (fixed) {
return famtypes
} else {
let familiesValNames =sheet.getRange(range).getValues();
let sRange
let obj = {};
for (i=0; i < 15; i++) {
sRange = 'A' + (4 + i) + ':P' + (4 + i)
Object.assign(obj, {[i +1]: {family: familiesValNames[i]}});
obj[i +1].types = sheet.getRange(sRange).getValues();
}
}
return JSON.stringify(obj);
}
function encoder (sheet, currentRow, colOffset, typeColNo, famSheet, famSheetTypesRange) {
let ret = {}
var str = ''
//loop through columns 3 to 18 of the corresponding row and iterate if its checked -> result binary string
for (i=0; i < 16; i++) {
str = (sheet.getRange(currentRow, (i+colOffset)).isChecked()) ? str + '1' : str + '0';
}
//shift one byte to left for easier calculation
//result are the first 2 byte (0xnnnnFF) of 3 (0xFFFFFF)
let var_bin = ((+parseInt(str,2)) << 8).toString(2)
//format binary as 3 byte
var_bin = ("000000000000000000000000".substr(var_bin.length) + var_bin);
//convert to hex
ret.variant_hex = parseInt(var_bin, 2).toString(16)
//get selected family type
ret.family_str = sheet.getRange(currentRow, typeColNo).getValue()
//get number of this selected type and convert it to hex
ret.family_hex = (get_family(ret.family_str, famSheet, famSheetTypesRange).number >>> 0).toString(16).toUpperCase();
//calculate intermedia hex value; add a trailing '0' to move the family to the upper 4bits of the last byte
ret.joined_hex = (parseInt(ret.variant_hex, 16) + parseInt((ret.family_hex + '0'), 16)).toString(16)
//convert to binary - better to make CRC with binary (1111 1111) than with HEX (ff)
let joined_bin = (parseInt(ret.joined_hex, 16)).toString(2)
//get crc and convert it to hex
ret.crc_hex = crc4(joined_bin).toString(16).toUpperCase()
//final hex code
ret.final_hex = (parseInt(ret.joined_hex, 16) + parseInt(ret.crc_hex, 16)).toString(16).toLocaleUpperCase()
//prepare and encode into base64
/** #type {number[]} */
ret.finalByteArr = [];
ret.finalByteArr[0] = ((parseInt(ret.final_hex, 16) & (parseInt('ff0000', 16))) >>> 16)
ret.finalByteArr[1] = ((parseInt(ret.final_hex, 16) & (parseInt('00ff00', 16))) >>> 8)
ret.finalByteArr[2] = (parseInt(ret.final_hex, 16) & (parseInt('0000ff', 16)))
//ret.finalByteArr = hexToBytes(ret.final_hex)
ret.b64 = b64().encode(ret.finalByteArr)
return ret
}
function decoder(str) {
var ret = {}
//will find on the end of the string the code; trailing whitespaces will be ignored
const regEx = /^(?:.*)#(.{4})(?:\s*)$/
//check if string is valid (checksum will be checked later)
ret.strValid = regEx.test(str);
if (ret.strValid) {
let result = str.match(regEx)
//first capturing group
ret.b64 = result[1];
//convert to a byte array
ret.byteArray = b64().decode(ret.b64)
//convert to hex string
ret.hex = bytesToHex(ret.byteArray).toString(16).toUpperCase();
//convert the hex string to binary
ret.binary = hex2bin(ret.hex, true)
//get crc
ret.crc = (parseInt(ret.binary, 2) & parseInt('00000f', 16)).toString(16).toUpperCase();
//get last 2 bytes (mask the 1st out) and check if its suits to the checksum
let toCheck = (parseInt(ret.binary, 2) & parseInt('fffff0', 16)).toString(2);
ret.crcValid = (crc4(toCheck).toString(16).toUpperCase() == ret.crc) ? true: false;
//get only variant
ret.variant_bin = ((parseInt(ret.binary, 2) & parseInt('ffff00', 16)) >>> 8).toString(2);
//fill with zero's & make a mirror bit set for easier handling later -> [0] = lowest bit
let variant_bin_ext = (("0000000000000000".substr(ret.variant_bin.length)) + ret.variant_bin.toString())
ret.variant_bin_mirr = variant_bin_ext.split("").reverse().join("");
//convert to hex
ret.variant_hex = parseInt(ret.variant_bin, 2).toString(16).toUpperCase()
//get type - mask everythin irrelevant out and move 4 bits to right
ret.type = parseInt(((parseInt(ret.binary, 2) & parseInt('0000f0', 16)) >>> 4), 10)
}
else {
ret.strValid = false;
}
return ret
}
function onEdit(event){
var sheet = event.source.getActiveSheet();
var actualSheetName = sheet.getName();
var lastColumnRow = sheet.getLastColumn();
var editRange = sheet.getActiveRange();
var editCol = editRange.getColumn();
var editRow = editRange.getRow();
var value = sheet.getActiveCell().getValue();
var adress = sheet.getActiveCell().getA1Notation();
var familiesSheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('Families');
//evaluate if something has been changed in 'Familees'
if (actualSheetName == 'Families') {
let all_types = set_family_object(familiesSheet, 'Q4:Q18', true)
Logger.log(all_types)
Logger.log(all_types[1].family)
Logger.log(all_types[1].types)
Logger.log(all_types[15].family)
Logger.log(all_types[15].types)
}
//evaluate and fill out legend on the top of sheet
if (adress == 'S2' && actualSheetName == 'Spares') {
const str = sheet.getActiveCell().getValue();
var famObj = get_family(str, familiesSheet, 'Q4:Q18')
sheet.getRange('C2:R2').setValues(famObj.names)
}
//getRange(row, column, [numRows], [numColumns]) || getRange(a1Notation)
if (sheet.getActiveCell().getRow() >= 6 && sheet.getActiveCell().getRow() <= 21
&& sheet.getActiveCell().getColumn() >= 3 && sheet.getActiveCell().getColumn() <= 19
&& actualSheetName == 'Spares') {
let currentRow = sheet.getActiveCell().getRow();
let currentCol = sheet.getActiveCell().getColumn();
//calculate code
let codeEnc = encoder(sheet, currentRow, 3, 19, familiesSheet, 'Q4:Q18')
if (sheet.getRange(currentRow, 1).getValue().length > 3) {
sheet.getRange(currentRow, 20).setValue(codeEnc.crc_hex);
sheet.getRange(currentRow, 20).setComment('')
sheet.getRange(currentRow, 21).setValue(codeEnc.b64);
sheet.getRange(currentRow, 21).setComment('')
} else {
sheet.getRange(currentRow, 20).setValue('n/a');
sheet.getRange(currentRow, 20).setComment('Please enter a product name in the 1st column.')
sheet.getRange(currentRow, 21).setValue('n/a');
sheet.getRange(currentRow, 21).setComment('Please enter a product name in the 1st column.')
}
let decoded = decoder('grgrgr #' + codeEnc.b64)
Logger.log(codeEnc)
Logger.log(decoded)
var famObj2 = get_family(decoded.type, familiesSheet, 'Q4:Q18')
Logger.log(famObj2)
}
The result can be tried/decoded with following HTML/JS snippet. Its quick and dirty.
Just enter for example:
Pneumatic Connector #Xs2a
Camera #!$cg
Power Supply 24V #p%qz
You can try to change manually the base64 code - for testing if the CRC4 works.
var crc4 = function(data) {
//https://gist.github.com/bryc/5916452ad0d1ef5c39f1a3f19566d315
var POLY = 0xC, INIT = 0, XOROUT = 0;
for(var crc = INIT, i = 0; i < data.length; i++) {
crc = crc ^ data[i];
for(var j = 0; j < 8; j++) {
crc = crc & 1 ? crc >>> 1 ^ POLY : crc >>> 1;
}
}
return (crc ^ XOROUT) & 0xF;
};
// Convert a hex string to a byte array
function hexToBytes(hex) {
// https://stackoverflow.com/a/34356351/14226613
for (var bytes = [], c = 0; c < hex.length; c += 2)
bytes.push(parseInt(hex.substr(c, 2), 16));
return bytes;
}
// Convert a byte array to a hex string
function bytesToHex(bytes) {
// https://stackoverflow.com/a/34356351/14226613
for (var hex = [], i = 0; i < bytes.length; i++) {
var current = bytes[i] < 0 ? bytes[i] + 256 : bytes[i];
hex.push((current >>> 4).toString(16));
hex.push((current & 0xF).toString(16));
}
return hex.join("");
}
function hex2bin(hex, fill = false){
//https://stackoverflow.com/a/45054052/14226613
let bin = (parseInt(hex, 16).toString(2)).padStart(8, '0');
if (fill) {
//https://stackoverflow.com/a/27641835/14226613
bin = "000000000000000000000000".substr(bin.length) + bin;
}
return bin
}
function b64(){
const abc = "ABCDEFGH!JKLMN=PQRSTUVWXYZabcdefghijk:mnopqrstuvwxyz+-$%23456789"; // base64 alphabet
function encode (byteArray) {
//https://stackoverflow.com/a/62365404/14226613
const bin = n => n.toString(2).padStart(8,0); // convert num to 8-bit binary string
const l = byteArray.length
let result = '';
for(let i=0; i<=(l-1)/3; i++) {
let c1 = i*3+1>=l; // case when "=" is on end
let c2 = i*3+2>=l; // case when "=" is on end
let chunk = bin(byteArray[3*i]) + bin(c1? 0:byteArray[3*i+1]) + bin(c2? 0:byteArray[3*i+2]);
let r = chunk.match(/.{1,6}/g).map((x,j)=> j==3&&c2 ? '=' :(j==2&&c1 ? '=':abc[+('0b'+x)]));
result += r.join('');
}
//remove padding - http://jsfiddle.net/bicycle/c49fgz8x/
result = result.replace(/={1,2}$/, '');
return result;
}
function decode (str) {
//https://stackoverflow.com/a/62364519/14226613
let result = [];
//add padding http://jsfiddle.net/bicycle/c49fgz8x/
str = str + Array((4 - str.length % 4) % 4 + 1).join('=');
for(let i=0; i<str.length/4; i++) {
let chunk = [...str.slice(4*i,4*i+4)]
let bin = chunk.map(x=> abc.indexOf(x).toString(2).padStart(6,0)).join('');
let bytes = bin.match(/.{1,8}/g).map(x=> +('0b'+x));
result.push(...bytes.slice(0,3 - (str[4*i+2]=="=") - (str[4*i+3]=="=")));
}
return result;
}
return {
encode: encode,
decode: decode
}
}
function set_family_object (sheet, range, fixed = false){
let famtypes={1:{family:["Spares ACME 1"],types:[["spare","Alpha v2","Alpha v1","Alpha v0","spare","Beta v2","Beta v1","Beta v0","spare","Gamma v2","Gamma v1","Gamma v0","spare","Delta v2","Delta v1","Delta v0"]]},2:{family:["Spares ACME 2"],types:[["spare","spare","spare","spare","spare","spare","spare","spare","spare","spare","spare","spare","Omega v2","Omega v2","Omega v1","Omega v0"]]},3:{family:["ACME Tools"],types:[["p612 lcp2","p612 lcp1","p612 lcp0","p512 lcp2","p512 lcp1","p512 lcp0","p671 lcp2","p671 lcp1","p671 lcp0","p471 lcp2","p471 lcp1","p471 lcp0","blm v2","blm v1","blm v0","common"]]},4:{family:["Animals"],types:[["Sea Otter","Cat","Panda","Fox","Dog","Lemur","Penguin","Mulgara","Degu","Kiwi","Koala","Monkey","Whale","Oribi","Chimpanzee","Deer"]]},5:{family:["Male Names"],types:[["James","John","Robert","Michael","Willian","David","Richard","Thomas","Charles","Daniel","Matthew","Donald","Mark","Brian","Edward","George"]]},6:{family:["Female Names"],types:[["Olivia","Emma","Ava","Sophia","Isabella","Charlotte","Amelia","Mia","Harper","Evelyn","Emily","Ella","Camila","Luna","Mila","Aria"]]},7:{family:["Car Brands"],types:[["Skoda","Renault","Citroen","Dacia","BMW","Audi","VW","Ford","Peugeot","Honda","Hyundai","Toyota","Kia","Opel","Porsche","Tata"]]},8:{family:["Horror Movies"],types:[["Wrong Turn","Descent","Hostel","Vacancy","Joy ride","Joy Ride","Midsommar","Cave","Eden Lake","Frozen","Ruins","Turistas","Hills have Eyes","Cabin of Woods","Soon the Darkness","Deep"]]},9:{family:["Things for Salad"],types:[["Lettuce","Cucumber","Cheese","Carrot","Tomato","Spinach","Crouton","Bacon","Egg","Onion","Chicken","Avocado","Pepper","Cabbage","Olive Oil","Feta"]]},10:{family:["TV Scientist"],types:[["Spock","Gaius Baltar","Leonard McCoy","Dr Bruce Banner","MacGyver","Dr Krieger","Walter White","Dexter","Lex Luthor","Walter Bishop","Data","The Doctor","Dr Quest","Dana Scully","Jimmy Neutron","Howard Wolowitz"]]},11:{family:["Funny Movies"],types:[["Spaceballs","Airplane!","Monty Python","Blazin Saddles","Lego Movie 2","Shrek","Naked gun","Austin Powers","Shaun of the Dead","Finding Nemo","The Big Lebowski","Dumb and Dumber","Simpsons","Toy Story","South Park","Life of Brian"]]},12:{family:["Actors"],types:[["Jamie Foxx","Val Kilmer","Christian Bale","Cate Blanchett","Jim Carrey","Meryl Streep","Joaquin Phoenix","Sam Rockwell","Leonardi DiCaprio","Heath Ledger","Robin Williams","Bill Murray","Will Ferrel","Jack Nicholson","Kevin spacey","Tom Cruise"]]},13:{family:["Tech Blogs"],types:[["TechCrunch","Wired","TechnoByte","Golem","Heise","Gizmodo","Engadget","Ars Technica","Techaeris","Top10Tech","10xDS","The Verge","TECHi","Urbangeekz","Techsppoks","Mashable"]]},14:{family:["PC Brands"],types:[["Asus","Intel","Dell","HP","Alienware","Microsoft","Apple","ACer","Sony","MSI","Razer","Toshiba","Gateway","LG","Compaq","Panasonic"]]},15:{family:["Evil Companies"],types:[["Facebook","Comcast","Time Warner","Google","Apple","Twitter","Go Daddy","Verizon","Yahoo","Microsoft","Zynga","BuzzFeed","McAfee","Amazon","Youtube","Hulu"]]}};
if (fixed) {
return famtypes
} else {
let familiesValNames =sheet.getRange(range).getValues();
let sRange
let obj = {};
for (i=0; i < 15; i++) {
sRange = 'A' + (4 + i) + ':P' + (4 + i)
Object.assign(obj, {[i +1]: {family: familiesValNames[i]}});
obj[i +1].types = sheet.getRange(sRange).getValues();
}
}
return JSON.stringify(obj);
}
function decoder(str) {
var ret = {}
//will find on the end of the string the code; trailing whitespaces will be ignored
const regEx = /^(?:.*)#(.{4})(?:\s*)$/
//check if string is valid (checksum will be checked later)
ret.strValid = regEx.test(str);
if (ret.strValid) {
let result = str.match(regEx)
//first capturing group
ret.b64 = result[1];
//convert to a byte array
ret.byteArray = b64().decode(ret.b64)
//convert to hex string
ret.hex = bytesToHex(ret.byteArray).toString(16).toUpperCase();
//convert the hex string to binary
ret.binary = hex2bin(ret.hex, true)
//get crc
ret.crc = (parseInt(ret.binary, 2) & parseInt('00000f', 16)).toString(16).toUpperCase();
//get last 2 bytes (mask the 1st out) and check if its suits to the checksum
let toCheck = (parseInt(ret.binary, 2) & parseInt('fffff0', 16)).toString(2);
ret.crcValid = (crc4(toCheck).toString(16).toUpperCase() == ret.crc) ? true: false;
//get only variant
ret.variant_bin = ((parseInt(ret.binary, 2) & parseInt('ffff00', 16)) >>> 8).toString(2);
//fill with zero's & make a mirror bit set for easier handling later -> [0] = lowest bit
let variant_bin_ext = (("0000000000000000".substr(ret.variant_bin.length)) + ret.variant_bin.toString())
ret.variant_bin_mirr = variant_bin_ext.split("").reverse().join("");
//convert to hex
ret.variant_hex = parseInt(ret.variant_bin, 2).toString(16).toUpperCase()
//get type - mask everythin irrelevant out and move 4 bits to right
ret.type = parseInt(((parseInt(ret.binary, 2) & parseInt('0000f0', 16)) >>> 4), 10)
}
else {
ret.strValid = false;
}
return ret
}
document.getElementById('ssearch').onclick = function() {
let ttable = document.querySelector("body > table")
ttable.style.visibility = "visible";
let sval = document.getElementsByClassName('searchTerm')[0].value;
let dec_val = decoder(sval);
let famValues = set_family_object(null, null, true)
//console.log(dec_val)
//console.log(famValues)
var tbody = document.querySelector('tbody');
tbody.innerHTML = '';
if (!dec_val.strValid) {
tbody.innerHTML += '<tr style="background-color:red;color:white;"><td>' + 'String Validation' + '</td><td>Input String is not valid!</td></tr>'
} else {
if (dec_val.crcValid) {
tbody.innerHTML += '<tr style="background-color:#93C47D; font-size: 75%;"><td>' + 'String & CRC' + '</td><td>String & CRC valid</td></tr>'
tbody.innerHTML += '<tr style="background-color:#3C6478; color:white"><td>' + 'Family' + '</td><td>' + famValues[dec_val.type]['family'] + '</td></tr>'
for (var i = 0; i < 16; i++) {
tbody.innerHTML += '<tr><td>' +
[
((dec_val.variant_bin_mirr[i] == 1) ?
famValues[dec_val.type]['types'][0][15-i] :
'<s>' + famValues[dec_val.type]['types'][0][15-i] + '</s>'),
((dec_val.variant_bin_mirr[i] == 1) ?
'<span style="background-color:#93C47D; color:white">Compatible</span>' :
'<span style="background-color:red; color:white">NOT Compatible</span>')
].join('</td><td>') + '</td></tr>'
}
} else{
tbody.innerHTML += '<tr style="background-color:red;color: white;"><td>' + 'CRC' + '</td><td>Product Name is wrong - CRC invalid</td></tr>'
}
}
}
th, td {
border: 1px solid black;
}
table {
margin: auto;
width: 40%;
visibility: hidden;
}
#import url(https://fonts.googleapis.com/css?family=Open+Sans);
body{
background: #f2f2f2;
font-family: 'Open Sans', sans-serif;
}
.search {
width: 100%;
position: relative;
display: flex;
}
.searchTerm {
width: 100%;
border: 3px solid #00B4CC;
border-right: none;
padding: 5px;
height: 20px;
border-radius: 5px 0 0 5px;
outline: none;
color: #9DBFAF;
}
.searchTerm:focus{
color: #00B4CC;
}
.searchButton {
width: 100px;
height: 36px;
border: 1px solid #00B4CC;
background: #00B4CC;
text-align: center;
color: #fff;
border-radius: 0 5px 5px 0;
cursor: pointer;
font-size: 20px;
}
/*Resize the wrap to see the search bar change!*/
.wrap{
width: 50%;
position: absolute;
top: 10%;
left: 50%;
transform: translate(-50%, -50%);
}
<!-- https://codepen.io/huange/pen/rbqsD -->
<!-- https://uicookies.com/html-search-box/ -->
<div class="wrap">
<div class="search">
<input type="text" class="searchTerm" placeholder="Give me your product name...">
<button type="submit" class="searchButton" id="ssearch">
Check
</button>
</div>
</div>
<br><br><br><br><br><br><br><br>
<table>
<thead>
<th style="width:40%;text-align:left;">Name</th>
<th style="text-align:left;">Value</th>
</thead>
<tbody>
</tbody>
</table>

function columnToLetter(col) {
const ss=SpreadsheetApp.getActive();
const sh=ss.getSheetByName('Sheet1');
return sh.getRange(1,col).getA1Notation().slice(0,-1);
}
Since your going to use it several times make an object with all of the columns you need as properties and it will make a very fast converter for you with no function calls and any of the context switching times.

Related

Check for range of characters in a random string

I have this function to generate a random password.
My question is How do I ensure that a selected range of letters/sign/numbers is included in the result?
As an example, if I select that there must be numbers or signs in the result string, how do I check if there is at least one number or sign in the result. I have toughed about looping through numbers or signs for each member of the result, and then break the loop if I meet a character I am testing for.
The code snippet works, but without validating the result
function generatePassword() {
var base = "";
var result = "";
var latinLetters = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
var cyrillicLetters = "БбВвГгДдЕеЖжЗзИиЙйКкЛлМмНнОоПпРрСсТтУуФфХхЦцЧчШшЩщЬьЮюЯя"
var greekLetters = "ΑαΒβΓγΔδΕεΖζΗηΘθΙιΚκΛλΜμΝνΞξΟοΠπΡρΣσςΤτΥυΦφΧχΨψΩω"
var numbersLetters = "1234567890"
var signsLetters = "/|()1{}[]?-_+~<>!I;:,^`.$#B%&WM*"
var armenianLetters = "աբգդեզէըթժիլխծկհձղճմյնշոչպջռսվտրցւփքօֆուև"
var hangulLetters = "ㄱㄴㄷㄹㅁㅂㅅㅇㅈㅊㅋㅌㅍㅎㅏㅑㅓㅕㅗㅛㅜㅠㅡㅣ"
var nordicLetters = "AaÁáBbCcDdÐðEeÉéFfGgHhIiÍíJjKkLlMmNnOoÓóPpRrSsTtUuÚúVvWwXxYyÝýZzÞþÆæÖöZzÄäØøÅå"
var arabicLetters = "ءي و ه ن م لك ق ف غ ع ظ ط ض ص ش س ز ر ذ د خ ح ج ث ت ب ا"
var georgianLetters = "აბგდევზთიკლმნოპჟრსტუფქღყშჩცძწჭხჯჰ"
var ethiopianLetters = "ሀለሐመሠረሰቀበተኀነአከወዐዘየደገጠጸፀፈጰፐ"
var thaanaLetters = "ހށނރބޅކއވމފދތލގސޑޒޓޔޕޖޗޘޙޚޛޜޝޞޟޠޡޢޣޤޥަ ީ ު ޫ ެ ޭ ޮ ޯޱ"
var hanziLetters = "ぁあぃいぅうぇえぉおかがきぎくぐけげこごさざしじすずせぜそぞただちぢっつづてでとどなにぬねのはばぱひびぴふぶぷへべぺほぼぽまみむめもゃやゅゆょよらりるれろゎわゐゑをんゔゕゖ ゙ ゛゜ゝゞゟ"
// input
var length = document.getElementById('pwLength').value
var latin = document.getElementById('latinChecked')
var numbers = document.getElementById('numbersChecked')
var signs = document.getElementById('signsChecked')
var nordic = document.getElementById('nordicChecked')
var cyrillic = document.getElementById('cyrillicChecked')
var greek = document.getElementById('greekChecked')
var armenian = document.getElementById('armenianChecked')
var hangul = document.getElementById('hangulChecked')
var arabic = document.getElementById('arabicChecked')
var georgian = document.getElementById('georgianChecked')
var ethiopian = document.getElementById('ethiopianChecked')
var thaana = document.getElementById('thaanaChecked')
var hanzi = document.getElementById('hanziChecked')
// build source string
if (arabic.checked) { base = base + arabicLetters; }
if (latin.checked) { base = base + latinLetters; }
if (signs.checked) { base = base + signsLetters; }
if (greek.checked) { base = base + greekLetters; }
if (numbers.checked) { base = base + numbersLetters; }
if (armenian.checked) { base = base + armenianLetters; }
if (hangul.checked) { base = base + hangulLetters; }
if (numbers.checked) { base = base + numbersLetters; }
if (nordic.checked) { base = base + nordicLetters; }
if (cyrillic.checked) { base = base + cyrillicLetters; }
if (numbers.checked) { base = base + numbersLetters; }
if (georgian.checked) { base = base + georgianLetters; }
if (ethiopian.checked) { base = base + ethiopianLetters; }
if (thaana.checked) { base = base + thaanaLetters; }
if (hanzi.checked) { base = base + hanziLetters; }
// fill result
for (let i = 0; i < length; i++) {
var rand = Math.floor(Math.random() * base.length);
result = result + base.charAt(rand);
}
// write to html
document.getElementById("productX").innerHTML = result;
}
You can use this process:
Select exactly one random character from each character set that was selected
Select the remaining needed characters from all available characters
Concatenate all of the above and shuffle randomly
Here is an implementation:
// Some utility functions on Math.random
const randInt = end => Math.floor(Math.random() * end);
const randomOf = (array, length=1) => Array.from({length}, () =>
array[randInt(array.length)]
);
function shuffle(array) {
for (let i = array.length - 1; i > 0; i--) {
let j = randInt(i + 1);
let temp = array[i];
array[i] = array[j];
array[j] = temp;
}
return array;
}
const groups = {
latin: "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz",
cyrillic: "БбВвГгДдЕеЖжЗзИиЙйКкЛлМмНнОоПпРрСсТтУуФфХхЦцЧчШшЩщЬьЮюЯя",
greek: "ΑαΒβΓγΔδΕεΖζΗηΘθΙιΚκΛλΜμΝνΞξΟοΠπΡρΣσςΤτΥυΦφΧχΨψΩω",
numbers: "1234567890",
signs: "/|()1{}[]?-_+~<>!I;:,^`.$#B%&WM*",
armenian: "աբգդեզէըթժիլխծկհձղճմյնշոչպջռսվտրցւփքօֆուև",
hangul: "ㄱㄴㄷㄹㅁㅂㅅㅇㅈㅊㅋㅌㅍㅎㅏㅑㅓㅕㅗㅛㅜㅠㅡㅣ",
nordic: "AaÁáBbCcDdÐðEeÉéFfGgHhIiÍíJjKkLlMmNnOoÓóPpRrSsTtUuÚúVvWwXxYyÝýZzÞþÆæÖöZzÄäØøÅå",
arabic: "ءي و ه ن م لك ق ف غ ع ظ ط ض ص ش س ز ر ذ د خ ح ج ث ت ب ا",
georgian: "აბგდევზთიკლმნოპჟრსტუფქღყშჩცძწჭხჯჰ",
ethiopian: "ሀለሐመሠረሰቀበተኀነአከወዐዘየደገጠጸፀፈጰፐ",
thaana: "ހށނރބޅކއވމފދތލގސޑޒޓޔޕޖޗޘޙޚޛޜޝޞޟޠޡޢޣޤޥަ ީ ު ޫ ެ ޭ ޮ ޯޱ",
hanzi: "ぁあぃいぅうぇえぉおかがきぎくぐけげこごさざしじすずせぜそぞただちぢっつづてでとどなにぬねのはばぱひびぴふぶぷへべぺほぼぽまみむめもゃやゅゆょよらりるれろゎわゐゑをんゔゕゖ ゙ ゛゜ゝゞゟ",
};
function generatePassword(length, charGroups) {
if (charGroups.length > length) {
throw "Required length incompatible with number of character groups";
}
// Select one from each set, and take the rest from any in those sets. Then shuffle.
return shuffle([
...charGroups.flatMap(group => randomOf(groups[group])),
...randomOf(charGroups.map(group => groups[group]).join(""), length - charGroups.length)
]).join("");
}
// I/O handing:
function clickHandler() {
// Get inputs
const length = +document.getElementById('pwLength').value;
const selected = Object.keys(groups).filter(group => document.getElementById(group + "Checked").checked);
// Apply random generation based on these parameters
const result = generatePassword(length, selected);
// Output:
document.getElementById("productX").textContent = result;
}
document.getElementById("generate").addEventListener("click", clickHandler);
#pwLength { width: 4em }
#productX { font-family: monospace }
Length: <input id="pwLength" type="number" value="16">
<table><tr>
<td><input id="latinChecked" type="checkbox" checked>Latin</td>
<td><input id="cyrillicChecked" type="checkbox">Cyrillic</td>
<td><input id="greekChecked" type="checkbox">Greek</td>
<td><input id="numbersChecked" type="checkbox" checked>Numbers</td>
</tr><tr>
<td><input id="signsChecked" type="checkbox" checked>Signs</td>
<td><input id="armenianChecked" type="checkbox">Armenian</td>
<td><input id="hangulChecked" type="checkbox">Hangul</td>
</tr><tr>
<td><input id="nordicChecked" type="checkbox">Nordic</td>
<td><input id="arabicChecked" type="checkbox">Arabic</td>
<td><input id="georgianChecked" type="checkbox">Georgian</td>
</tr><tr>
<td><input id="ethiopianChecked" type="checkbox">Etheopian</td>
<td><input id="thaanaChecked" type="checkbox">Thaana</td>
<td><input id="hanziChecked" type="checkbox">Hanzi</td>
</tr></table>
<button id="generate">Generate password</button><br>
Password: <span id="productX"></span>
I will add this method, to check if a selected sequences of letters is included
if (latin.checked) {
var check = 0;
for (let i = 0; i > result.length; i++) {
if (latinLetter.includes.includes(result.charAt(i))) {
i = result.length + 1;
check++;
}
if (check == 0) { generatePassword(); }
}
}
Regarding avoiding repeated characters as #Scott mentioned
for (let i = 0; i < length; i++) {
var rand = Math.floor(Math.random() * base.length);
result = result + base.charAt(rand);
xLetter = xLetter.slice(rand, rand + 1);
}
But i guess that it would be better to just avoid repeating the last character
var last;
for (let i = 0; i < length; i++) {
var rand = Math.floor(Math.random() * base.length);
if ( last == base.charAt(rand) ) {
var rand = Math.floor(Math.random() * base.length);
}
if ( last == base.charAt(rand).toLowerCase() ) {
var rand = Math.floor(Math.random() * base.length);
}
if ( last == base.charAt(rand).toUpperCase() ) {
var rand = Math.floor(Math.random() * base.length);
}
last = base.charAt(rand);
// can be equal to the last character, but the probability is low
result = result + base.charAt(rand);
}
if (latin.checked) {
var check = 0;
for (let i = 0; i > result.length; i++) {
if (latinLetter.includes.includes(result.charAt(i))) {
i = result.length + 1;
check++;
}
if (check == 0) { generatePassword(); }
}
}
// is upper
var upperCheck = 0;
for (let i = 0; i > result.length; i++) {
if (result.charAt(i) == result.charAt(i).toUpperCase()) {
i = result.length + 1;
upperCheck++;
}
if (upperCheck == 0) { generatePassword(); }
}
// is upper
var lowerCheck = 0;
for (let i = 0; i > result.length; i++) {
if (result.charAt(i) == result.charAt(i).toLowerCase()) {
i = result.length + 1;
lowerCheck++;
}
if (lowerCheck == 0) { generatePassword(); }
}

How can i validate an elements existence by typing it into an input text field?

I have different id's on different elements that are used in an input field when the user can type in the letter (Element 1 = A, Element 2 = B.. Element 27 = AA). How can I properly scan these on input in order to determine if the element exists and if it does put it in a string that converts these id's to values which are later calculated?
I have an id system on a calculator where the user can generate different element (sliders, radio buttons, check boxes) that can be calculated. They all have a numeric id which is the translated into alphabetic characters in a progressive order ( Element 1 = A, Element 2 = B.. Element 27 = AA). I later have an input field where the user can create their own formula that will be calculated and put into a result tab.
Sample Formula: A+B*2
The reason I translate to letter is so that the user can use numbers in creating the formula. I have succeed in making this work for id's that are one letter but as soon as the id's start hitting AA and AB it doesn't work because currently I split this into an array and scan every element for it's value, which becomes problematic for two letters since they split into two different id's.
I have tried splitting the array based on the operators (+, -, *, /) but that removes them from the array.
function resultCalcInit(resultObject, resultFormulaObject) {
$('.createWrap').on('keyup input change', $(resultFormulaObject).find('.jvformbuilder-formula-panel-elements-result-field-formula'), function(e) {
var thisKey = new RegExp(String.fromCharCode(e.which).toLowerCase());
var keyNoRegEx = String.fromCharCode(e.which).toLowerCase();
var counter = 0;
var letters = /^[0-9a-zA-Z]+$/;
for (var call of $('.dropzone').find('.builder-elements')) {
if ($(call).find('.bf-number')[0]) {
var operators = ['»', '½', '/', '¿'];
if (String.fromCharCode(e.which) == $(call).find('.bf-number').attr("data-calcId").toUpperCase() || $.isNumeric(String.fromCharCode(e.which)) || wordInString(String.fromCharCode(e.which), operators)) {
counter++;
} else {}
} else if ($(call).find('.builder-list')[0]) {
var operators = ['»', '½', '/', '¿'];
if (String.fromCharCode(e.which) == $(call).find('.builder-list').attr("data-calcId").toUpperCase() || $.isNumeric(String.fromCharCode(e.which)) || wordInString(String.fromCharCode(e.which), operators)) {
counter++;
} else {}
} else if ($(call).find('.builder-radio')[0]) {
var operators = ['»', '½', '/', '¿'];
if (String.fromCharCode(e.which) == $(call).find('.builder-radio').attr("data-calcId").toUpperCase() || $.isNumeric(String.fromCharCode(e.which)) || wordInString(String.fromCharCode(e.which), operators)) {
counter++;
} else {}
} else if ($(call).find('.builderSlider')[0]) {
var operators = ['»', '½', '/', '¿'];
if (String.fromCharCode(e.which) == $(call).find('.builderSlider').attr("data-calcId").toUpperCase() || $.isNumeric(String.fromCharCode(e.which)) || wordInString(String.fromCharCode(e.which), operators)) {
counter++;
} else {}
} else if ($(call).find('.builder-checkboxes')[0]) {
var operators = ['»', '½', '/', '¿'];
if (String.fromCharCode(e.which) == $(call).find('.builder-checkboxes').attr("data-calcId").toUpperCase() || $.isNumeric(String.fromCharCode(e.which)) || wordInString(String.fromCharCode(e.which), operators)) {
counter++;
} else {}
}
}
if (String.fromCharCode(e.which).match(letters) && counter < 1) {
$(resultFormulaObject).find('.jvformbuilder-formula-panel-elements-result-field-formula').html($(resultFormulaObject).find('.jvformbuilder-formula-panel-elements-result-field-formula').html().replace(thisKey, ""));
var returnString = $(resultFormulaObject).find('.jvformbuilder-formula-panel-elements-result-field-formula').text();
$('#jvformbuilder-formula-panel').find('.jvformbuilder-formula-panel-elements').each(function() {
var formulaResultId = $(this).find('.jvformbuilder-formula-panel-elements-result-field-formula');
$('.builder-elements').each(function() {
if (formulaResultId.attr("id") == $(this).find('.result-number').attr("id")) {
var resultWindow = $(this).find('.result-number');
var formula = returnString.slice(1);
}
});
});
} else {
resultCalc(resultFormulaObject);
}
});
}
Here it check if the letter typed is an existing ID. If it isn't, it's removed. If it is, it stays and proceeds to be scanned for the value.
function resultCalc(resultFormulaObject) {
var returnString = $(resultFormulaObject).find('.jvformbuilder-formula-panel-elements-result-field-formula').text();
$('#jvformbuilder-formula-panel').find('.jvformbuilder-formula-panel-elements').each(function() {
var formulaResultId = $(this).find('.jvformbuilder-formula-panel-elements-result-field-formula');
$('.builder-elements').each(function() {
if (formulaResultId.attr("id") == $(this).find('.result-number').attr("id")) {
var resultWindow = $(this).find('.result-number');
var formula = returnString.slice(1).split("");
var formulaNbr = returnString.slice(1).split("");
var alphabet = ("abcdefghijklmnopqrstuvwxyz").split("");
var calculationArray = returnString.slice(1).split("");
var tempArr = formula;
for (var i = 0; i < formula.length; i++) {
$('.builder-elements').each(function() {
if ($(this).find('.builder-list').attr("data-calcid") == formula[i]) {
formulaNbr[i] = $(this).find('.builder-list').children("option:selected").val();
calculationArray[i] = "parseInt(ID" + alphabet.indexOf(formula[i]) + ").value)";
} else if ($(this).find('.builder-field').attr("data-calcid") == formula[i]) {
if ($(this).find('.bf-text')[0]) {
console.log(tempArr);
if (tempArr.indexOf(tempArr[i]) == 0) {
tempArr.splice(i, 2);
calculationArray.splice(i, 2);
} else {
tempArr.splice(i - 1, 2);
calculationArray.splice(i - 1, 2);
}
var formulaString = "";
for (var j = 0; j < formula.length; j++) {
formulaString += tempArr[j];
}
formulaResultId.empty();
formulaResultId.html("=" + formulaString);
} else if ($(this).find('.bf-telNum')[0]) {
if (tempArr.indexOf(tempArr[i]) == 0) {
tempArr.splice(i, 2);
calculationArray.splice(i, 2);
} else {
tempArr.splice(i - 1, 2);
calculationArray.splice(i - 1, 2);
}
var formulaString = "";
for (var j = 0; j < formula.length; j++) {
formulaString += tempArr[j];
}
formulaResultId.empty();
formulaResultId.html("=" + formulaString);
} else if ($(this).find('.bf-date')[0]) {
if (tempArr.indexOf(tempArr[i]) == 0) {
tempArr.splice(i, 2);
calculationArray.splice(i, 2);
} else {
tempArr.splice(i - 1, 2);
calculationArray.splice(i - 1, 2);
}
var formulaString = "";
for (var j = 0; j < formula.length; j++) {
formulaString += tempArr[j];
}
formulaResultId.empty();
formulaResultId.html("=" + formulaString);
} else if ($(this).find('.bf-number')[0]) {
if (!$(this).find('.bf-number').val()) {
formulaNbr[i] = 0;
} else {
formulaNbr[i] = $(this).find('.bf-number').val();
}
calculationArray[i] = "parseInt(ID" + alphabet.indexOf(formula[i]) + ").value)";
}
} else if ($(this).find('.builder-textarea').attr("data-calcid") == formula[i]) {
if (tempArr.indexOf(tempArr[i]) == 0) {
tempArr.splice(i, 2);
calculationArray.splice(i, 2);
} else {
tempArr.splice(i - 1, 2);
calculationArray.splice(i - 1, 2);
}
var formulaString = "";
for (var j = 0; j < formula.length; j++) {
formulaString += tempArr[j];
}
formulaResultId.empty();
formulaResultId.html("=" + formulaString);
} else if ($(this).find('.builder-radio').attr("data-calcid") == formula[i]) {
var resultRadio = [];
$(this).find('.builder-radio-input').each(function(i) {
resultRadio[i] = parseInt($(this).val());
});
var sum = resultRadio.reduce(add);
formulaNbr[i] = sum;
calculationArray[i] = "parseInt(ID" + alphabet.indexOf(formula[i]) + ").value)";
} else if ($(this).find('.builder-checkboxes').attr("data-calcid") == formula[i]) {
var resultCheck = [];
$(this).find('.builderCB').each(function(i) {
resultCheck[i] = parseInt($(this).val());
});
var sum = resultCheck.reduce(add);
formulaNbr[i] = sum;
calculationArray[i] = "parseInt(ID" + alphabet.indexOf(formula[i]) + ").value)";
} else if ($(this).find('.builderSlider').attr("data-calcid") == formula[i]) {
formulaNbr[i] = $(this).find('.builder-slider').val();
calculationArray[i] = "parseInt(ID" + alphabet.indexOf(formula[i]) + ").value)";
}
});
}
var calculationString = "";
for (var i = 0; i < calculationArray.length; i++) {
calculationString += calculationArray[i];
}
returnString = "";
for (var i = 0; i < formulaNbr.length; i++) {
returnString += formulaNbr[i];
}
if (returnString) {
printRes(returnString, resultWindow, calculationString);
}
}
});
});
}
Here it takes different values from the different objects that relates to the id written inside the formula tab. Later it is printed into the result tab.
function printRes(resString, resArea, calcString) {
resArea.empty();
var result = eval(resString);
if (!result) {
resArea.append(0)
resArea.attr("data-calcForm", "");
} else {
resArea.append(result)
resArea.attr("data-calcForm", calcString);
}
}
It completely crashes if the id becomes doubled. That's where I need you guys to help me. How can I make it scan after double characters id's as well as single ones, and triple ones and how ever many the user decides to generate.
There is no way to reproduce the issue you have from the big code bunch you posted. And I have to admit that what you try to achieve is unclear to me.
But what is really clear is that you need to segregate the formula's elements from the operator. You can achieve this with 2 regexes. I see you already use the first one: /[0-9a-zA-Z]+/, but I don't get how you use it...
Anyway, here is a real simple demo showing that you can have two arrays, one for the formula's elements and the other for the operators. Once you have that, you should be able to use it to do whatever you wish to.
$("button").on("click",function(){
// The input value
var input_val = $("input").val();
// Regexes
var elements_regex = /[0-9a-zA-Z]+/g;
var operators_regex = /[\+\-*\/]/g;
// Create the arrays
var elements_array = input_val.match(elements_regex);
var operators_array = input_val.match(operators_regex);
// Needed just for this demo
var regex_validation = $(".regex_validation");
var elements = $(".elements");
var operators = $(".operators");
// RESULTS
// Regex validation
for(i=0;i<elements_array.length;i++){
// Element
if(typeof(elements_array[i])!="undefined"){
regex_validation.append("<span class='element'>"+elements_array[i]+"<span>");
}
// Operator
if(typeof(operators_array[i])!="undefined"){
regex_validation.append("<span class='operator'>"+operators_array[i]+"<span>");
}
}
// Elements
elements.html(JSON.stringify(elements_array));
// Operators
operators.html(JSON.stringify(operators_array));
});
.result{
height: 50px;
width: 500px;
border: 1px solid black;
}
.element{
background: cyan;
}
.operator{
background: yellow;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
Enter your formula: <input value="AA+B*2"><button type="button">Check</button><br>
<br>
Regex validation: (cyan for elements, yellow for operators)
<div class="result regex_validation"></div>
<br>
Elements array:
<div class="result elements"></div>
<br>
Operators array:
<div class="result operators"></div>
<br>
CodePen

Bitcoin address form validation JavaScript and PHP

I've seen a few Bitcoin Address form validation scripts for various languages, but surprisingly can't really find anything for two common web languages, Javascript and PHP.
Here's one for Python, but is there one for PHP and/or JS?
from hashlib import sha256
digits58 = '123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz'
def decode_base58(bc, length):
n = 0
for char in bc:
n = n * 58 + digits58.index(char)
return n.to_bytes(length, 'big')
def check_bc(bc):
bcbytes = decode_base58(bc, 25)
return bcbytes[-4:] == sha256(sha256(bcbytes[:-4]).digest()).digest()[:4]
if __name__ == '__main__':
bc = '1AGNa15ZQXAZUgFiqJ2i7Z2DPU2J6hW62i'
assert check_bc(bc)
assert not check_bc( bc.replace('N', 'P', 1) )
assert check_bc('1111111111111111111114oLvT2')
assert check_bc("17NdbrSGoUotzeGCcMMCqnFkEvLymoou9j")
Here's a JSFiddle: http://jsfiddle.net/timrpeterson/XsCQq/2/
And here's the full code upon which the JSFiddle is based:
<html>
<head>
<script type="text/javascript" src="http://dl.dropboxusercontent.com/u/28441300/BigInt.js"></script>
<script type="text/javascript" src="http://dl.dropboxusercontent.com/u/28441300/sha256.js"></script>
</head>
<body>
<div id="text">
</div>
<script type="text/javascript">
var address = "1Eym7pyJcaambv8FG4ZoU8A4xsiL9us2zz";
if (check(address)) {
document.getElementById('text').innerHTML += "valid";
} else {
document.getElementById('text').innerHTML += "invalid";
}
function check(address) {
var decoded = base58_decode(address);
if (decoded.length != 25) return false;
var cksum = decoded.substr(decoded.length - 4);
var rest = decoded.substr(0, decoded.length - 4);
var good_cksum = hex2a(sha256_digest(hex2a(sha256_digest(rest)))).substr(0, 4);
if (cksum != good_cksum) return false;
return true;
}
function base58_decode(string) {
var table = '123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz';
var table_rev = new Array();
var i;
for (i = 0; i < 58; i++) {
table_rev[table[i]] = int2bigInt(i, 8, 0);
}
var l = string.length;
var long_value = int2bigInt(0, 1, 0);
var num_58 = int2bigInt(58, 8, 0);
var c;
for(i = 0; i < l; i++) {
c = string[l - i - 1];
long_value = add(long_value, mult(table_rev[c], pow(num_58, i)));
}
var hex = bigInt2str(long_value, 16);
var str = hex2a(hex);
var nPad;
for (nPad = 0; string[nPad] == table[0]; nPad++);
var output = str;
if (nPad > 0) output = repeat("\0", nPad) + str;
return output;
}
function hex2a(hex) {
var str = '';
for (var i = 0; i < hex.length; i += 2)
str += String.fromCharCode(parseInt(hex.substr(i, 2), 16));
return str;
}
function a2hex(str) {
var aHex = "0123456789abcdef";
var l = str.length;
var nBuf;
var strBuf;
var strOut = "";
for (var i = 0; i < l; i++) {
nBuf = str.charCodeAt(i);
strBuf = aHex[Math.floor(nBuf/16)];
strBuf += aHex[nBuf % 16];
strOut += strBuf;
}
return strOut;
}
function pow(big, exp) {
if (exp == 0) return int2bigInt(1, 1, 0);
var i;
var newbig = big;
for (i = 1; i < exp; i++) {
newbig = mult(newbig, big);
}
return newbig;
}
function repeat(s, n){
var a = [];
while(a.length < n){
a.push(s);
}
return a.join('');
}
</script>
</body>
</html>
And here is a PHP example (assuming your have PHP BC-Math):
<?php
function checkAddress($address)
{
$origbase58 = $address;
$dec = "0";
for ($i = 0; $i < strlen($address); $i++)
{
$dec = bcadd(bcmul($dec,"58",0),strpos("123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz",substr($address,$i,1)),0);
}
$address = "";
while (bccomp($dec,0) == 1)
{
$dv = bcdiv($dec,"16",0);
$rem = (integer)bcmod($dec,"16");
$dec = $dv;
$address = $address.substr("0123456789ABCDEF",$rem,1);
}
$address = strrev($address);
for ($i = 0; $i < strlen($origbase58) && substr($origbase58,$i,1) == "1"; $i++)
{
$address = "00".$address;
}
if (strlen($address)%2 != 0)
{
$address = "0".$address;
}
if (strlen($address) != 50)
{
return false;
}
if (hexdec(substr($address,0,2)) > 0)
{
return false;
}
return substr(strtoupper(hash("sha256",hash("sha256",pack("H*",substr($address,0,strlen($address)-8)),true))),0,8) == substr($address,strlen($address)-8);
}
?>
Here is a better version of #Tim-Peterson 's answer. It fixes the base58 implementation he was using (which would not validate the address "12EJmB3cMGRNveskzA7g7kxW32gSbo2dHF".
I combined the validation code with all the needed libraries and removed a lot that wasn't needed. It only exposes a single api: "checkAddress". I created a little home-page for it, where you can download the module source or the minified version: http://www.julianhaight.com/javascript.shtml
The corrected base58_decode (from https://github.com/cryptocoinjs/bs58):
// from https://github.com/cryptocoinjs/bs58
// Base58 encoding/decoding
// Originally written by Mike Hearn for BitcoinJ
// Copyright (c) 2011 Google Inc
// Ported to JavaScript by Stefan Thomas
// Merged Buffer refactorings from base58-native by Stephen Pair
// Copyright (c) 2013 BitPay Inc
var ALPHABET = '123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz'
var ALPHABET_MAP = {}
for(var i = 0; i < ALPHABET.length; i++) {
ALPHABET_MAP[ALPHABET.charAt(i)] = i
}
var BASE = 58
function base58_decode(string) {
if (string.length === 0) return []
var i, j, bytes = [0]
for (i = 0; i < string.length; i++) {
var c = string[i]
if (!(c in ALPHABET_MAP)) throw new Error('Non-base58 character')
for (j = 0; j < bytes.length; j++) bytes[j] *= BASE
bytes[0] += ALPHABET_MAP[c]
var carry = 0
for (j = 0; j < bytes.length; ++j) {
bytes[j] += carry
carry = bytes[j] >> 8
bytes[j] &= 0xff
}
while (carry) {
bytes.push(carry & 0xff)
carry >>= 8
}
}
// deal with leading zeros
for (i = 0; string[i] === '1' && i < string.length - 1; i++) bytes.push(0)
bytes = bytes.reverse()
output = '';
for (i=0; i<bytes.length; i++) {
output += String.fromCharCode(bytes[i]);
}
return output;
}
Bitcoin Address (example: 3QJmV3qfvL9SuYo34YihAf3sRCW3qSinyC) is not valid in many the PHP examples. One of the example which works fine especially to the above address is:
Click here to see the PHP Function to validate Bitcoin Address
I wrote a simple PHP library to do this based on the answers above. It can be found at my related github repo:
<?php
class Btc_address_validator {
/**
* [validate description]
* #param String $address BTC Address string
* #return Boolean validation result
*/
public function validate($address)
{
$addr = $this->decode_base58($address);
if (strlen($addr) != 50)
{
return false;
}
$check = substr($addr, 0, strlen($addr) - 8);
$check = pack("H*", $check);
$check = strtoupper(hash("sha256", hash("sha256", $check, true)));
$check = substr($check, 0, 8);
return $check == substr($addr, strlen($addr) - 8);
}
private function encode_hex($dec)
{
$hexchars = "0123456789ABCDEF";
$return = "";
while (bccomp($dec, 0) == 1)
{
$dv = (string) bcdiv($dec, "16", 0);
$rem = (integer) bcmod($dec, "16");
$dec = $dv;
$return = $return . $hexchars[$rem];
}
return strrev($return);
}
/**
* Convert a Base58-encoded integer into the equivalent hex string representation
*
* #param string $base58
* #return string
* #access private
*/
private function decode_base58($base58)
{
$origbase58 = $base58;
$base58chars = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz";
$return = "0";
for ($i = 0; $i < strlen($base58); $i++)
{
$current = (string) strpos($base58chars, $base58[$i]);
$return = (string) bcmul($return, "58", 0);
$return = (string) bcadd($return, $current, 0);
}
$return = $this->encode_hex($return);
//leading zeros
for ($i = 0; $i < strlen($origbase58) && $origbase58[$i] == "1"; $i++)
{
$return = "00" . $return;
}
if (strlen($return) % 2 != 0)
{
$return = "0" . $return;
}
return $return;
}
}
For those using javascript, you can use wallet-address-validator javascript plugin.
<script src="wallet-address-validator.min.js"></script>
// WAValidator is stored in the windows object
networkType - Optional. Use 'prod' (default) to enforce standard address, 'testnet' to enforce testnet address and 'both' to enforce nothing.
var valid = WAValidator.validate('12h7E1q5UUoPgZ1VtcYb57maFF9Cbk4u5X','BTC','both');
if(valid){
alert('This is a valid address');
} else {
alert('Address INVALID');
}
// will alert "This is a valid address"
var valid = WAValidator.validate('12h7E1q5UUoPgZ1VtcYb57maFF9Cbk4u5X', 'ETH', 'both');
if(valid){
alert('This is a valid address');
} else {
alert('Address INVALID');
}
// will alert "Address INVALID"
Here is a short and modern implementation in Javascript which depends on CryptoJS:
import sha256 from 'crypto-js/sha256'
import CryptoJS from 'crypto-js'
function isBTCAddress (address) {
if (!/^[13][a-km-zA-HJ-NP-Z1-9]{25,34}$/.test(address)) return false
const bufferLength = 25
let buffer = new Uint8Array(bufferLength)
const digits58 = '123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz'
for (var i = 0; i < address.length; i++) {
const num = digits58.indexOf(address[i])
// buffer = buffer * 58 + num
let carry = 0
for (var j = bufferLength - 1; j >= 0; --j) {
// num < 256, so we just add it to last
const result = buffer[j] * 58 + carry + (j === bufferLength - 1 ? num : 0)
buffer[j] = result % (1 << 8)
carry = Math.floor(result / (1 << 8))
}
}
// check whether sha256(sha256(buffer[:-4]))[:4] === buffer[-4:]
const hashedWords1 = sha256(CryptoJS.lib.WordArray.create(buffer.slice(0, 21)))
const hashedWords = sha256(hashedWords1).words
// get buffer[-4:] with big-endian
const lastWordAddress = new DataView(buffer.slice(-4).buffer).getInt32(0, false)
const expectedLastWord = hashedWords[0]
return lastWordAddress === expectedLastWord
}
This is a nice REGEX from this answer that I have made into a function for you. Only works with non-segwit addresses.
function validate_bitcoin_address(btc_address)
{
return btc_address.match("^[13][a-km-zA-HJ-NP-Z1-9]{25,34}$") !== null;
}
alert(validate_bitcoin_address("16CbQcqDtBak5NzPbmFP1v9Pi4DwP5G4Wn")); //example usage
The regex matches strings that:
Starts with 1 or 3
Afterwards, 25 to 34 characters of either a-z, A-Z, or 0-9 but not l, I, O and 0
The REGEX excludes characters that are not allow in bitcoin addresses (l, I, O and 0)
This works for each address format (today) https://github.com/Merkeleon/php-cryptocurrency-address-validation.
It needs BC Math, so probably... sudo apt install php-bcmath

Javascript - String split does not work well

I am making a script which receives a String and separate it on smaller Strings.
Ex: "This is a long sentence, and I will separate it into smaller parts. Lalala"
It will return "This is a long sentence","and I will separate it into smaller parts","Lalala"
The aim of this is to use Google translator to transform text to speech, but this feature has a limit of about 70-80 chars, so if the string is too large I need to chop it.
First I chop in sentences separated by a dot ("."), then if there are still too long sentences, I split them with the commas (",") and if there are still too long strings I separate them in unique words.
Everything works well until I try to join some words so the audio become more continuous. For some reason the strings separated by commas get joined again. I do not know why.
This is the code:
Edit: Relevant section split out and formatted
function talk(text){
var audios = document.createElement('audio');
audios.setAttribute('id','audio_speech');
var playlist = new Array()
if(text.length >= 75) {
playlist = text.split(".");
for (var i = 0;i<playlist.length;i++) {
if (playlist[i].length >= 75) {
auxarr = playlist[i].split(",");
//alert(auxarr.length);
for(var j=0;j<auxarr.length;j++) {
auxarr2 = auxarr[j].split(" ");
document.write(auxarr2+"<br>");
if (auxarr[j].length >= 75) {
auxarr2 = auxarr[j].split(" ");
for(var x=0; x < auxarr2.length; x++){
if(auxarr2[x].length < 50) {
aux = auxarr2[x];
while (aux.length < 50 && auxarr2[x+1]) {
aux = aux + " " + auxarr2[x+1];
auxarr2.splice(x,1);
auxarr2[x]=aux;
}
}
//...
Edit: Full original code
function talk(text)
{
var audios = document.createElement('audio');
audios.setAttribute('id','audio_speech');
var playlist = new Array()
if(text.length >= 75) {
playlist = text.split(".");
for (var i = 0;i<playlist.length;i++) {
if (playlist[i].length >= 75) {
auxarr = playlist[i].split(",");
//alert(auxarr.length);
for(var j=0;j<auxarr.length;j++) {
auxarr2 = auxarr[j].split(" ");
document.write(auxarr2+"<br>");
if (auxarr[j].length >= 75) {
auxarr2 = auxarr[j].split(" ");
for(var x=0; x < auxarr2.length; x++){
if(auxarr2[x].length < 50) {
aux = auxarr2[x];
while (aux.length < 50 && auxarr2[x+1]) {
aux = aux + " " + auxarr2[x+1];
auxarr2.splice(x,1);
}
auxarr2[x]=aux;
}
}
auxarr_end = auxarr.slice(j+1,auxarr.length);
auxarr_begin = auxarr.slice(0,j);
document.write("<br>"+auxarr+"<br> aca");
document.write("<br>"+auxarr_end+"<br> aca1");
document.write("<br>"+auxarr_begin+"<br> aca2");
auxarr.splice(j,1);
auxarr_begin = auxarr_begin.concat(auxarr2);
j = auxarr.length;
auxarr = auxarr_begin.concat(auxarr_end);
alert(auxarr);
}
}
//alert("current: "+playlist[i]);
//alert("current length:"+playlist[i].length);
//alert("auxarr: "+auxarr);
playlist_end = playlist.slice(i+1,playlist.length);
playlist_begin = playlist.slice(0, i);
playlist.splice(i,1);
playlist_begin = playlist_begin.concat(auxarr);
i = playlist.length;
playlist = playlist_begin.concat(playlist_end);
//alert("new "+playlist[i]);
}
}
/*do {
textAux = text.substring(0, 74);
text = text.substring(textAux.length, text.length);
playlist.push(textAux);
}while(text.length >= 75);*/
} else {
playlist.push(text);
}
//
//playlist.push(text);
/*for(var a=0; a<playlist.length;a++){
document.write(playlist[a]+"<br>");}*/
audios.setAttribute('src', 'http://translate.google.com/translate_tts?tl=es&q=' + encodeURIComponent(playlist[0]));
playlist.splice(0,1);
audios.load();
audios.play();
/*
*/
audios.addEventListener('ended', function(){
if (playlist[0]){
audios.setAttribute('src', 'http://translate.google.com/translate_tts?tl=es&q=' + encodeURIComponent(playlist[0]));
playlist.splice(0,1);
audios.play();
}
}, false);
}
</script>
Try this, modify it to work with your constants and parameters.
var LIMIT = 20;
var res = new Array()
//strats with spliting by dot
var dotArr = "This is a long sentence. and I will separate it into smaller parts. Lalala".split(/[.]/);
for (var i = 0; i < dotArr.length; i++) {
if (dotArr[i].length > LIMIT){
//only when have to, split by comma
var comArr = dotArr[i].split(/[,]/);
for (var j = 0; j < comArr.length; j++) {
//only when have to and that a space exists, split by space
if (comArr[j].length > LIMIT && comArr[j].indexOf(" ") != -1 ){
var spaceArr = comArr[j].split(/[ ]/);
//accomulate words until we reach the limit and then push the value to res
for (var k = 0; k < spaceArr.length;){
var sMerge = spaceArr[k++];
while (k < spaceArr.length && sMerge.length + spaceArr[k].length + 1 < LIMIT){
sMerge = sMerge + " " + spaceArr[k];
k++;
}
res.push(sMerge)
}
}else{
res.push(comArr[j]);
}
}
}else{
res.push(dotArr[i]);
}
}
//res contain all optimized sentences.

JavaScript Tag Cloud with IBM Cognos - IE is null or not an object

I followed a tutorial/modified the code to get a javascript tag cloud working in IBM Cognos (BI software). The tag cloud works fine in FireFox but in Internet Explorer I get the error:
"Message: '1' is null or not an object"
The line of code where this is present is 225 which is:
var B = b[1].toLowerCase();
I have tried many different solutions that I have seen but have been unable to get this working correctly, the rest of the code is as follows:
<script>
// JavaScript Document
// ====================================
// params that might need changin.
// DON'T forget to include a drill url in the href section below (see ###) if you want this report to be drillable
var delimit = "|";
var subdelimit = "[]"; // change this as needed (ex: Smith, Michael[]$500,000.00|)
var labelColumnNumber = 0; // first column is 0
var valueColumnNumber = 1;
var columnCount = 2; // how many columns are there in the list?
// ====================================
/*
function formatCurrency(num) {
num = num.toString().replace(/\$|\,/g,'');
if(isNaN(num))
num = "0";
sign = (num == (num = Math.abs(num)));
num = Math.floor(num*100+0.50000000001);
cents = num%100;
num = Math.floor(num/100).toString();
if(cents<10)
cents = "0" + cents;
for (var i = 0; i < Math.floor((num.length-(1+i))/3); i++)
num = num.substring(0,num.length-(4*i+3))+','+ num.substring(num.length-(4*i+3));
return (((sign)?'':'-') + '$' + num + '.' + cents);
}
*/
function formatCurrency(num) {
num = num.toString().replace(/\$|\,/g,'');
if(isNaN(num))
num = "0";
for (var i = 0; i < Math.floor((num.length-(1+i))/3); i++)
num = num.substring(0,num.length-(4*i+3))+','+ num.substring(num.length-(4*i+3));
return ( num );
}
function filterNum(str) {
re = /\$|,|#|#|~|`|\%|\*|\^|\&|\(|\)|\+|\=|\[|\-|\_|\]|\[|\}|\{|\;|\:|\'|\"|\<|\>|\?|\||\\|\!|\$|/g;
// remove special characters like "$" and "," etc...
return str.replace(re, "");
}
table = document.getElementById("dg");
if ( table.style.visibility != 'hidden'){ //only for visible
/*alert('Visible');*/
tags = document.getElementById("dg").getElementsByTagName("SPAN");
txt = "";
var newText = "a";
for (var i=columnCount; i<tags.length; i++) {
/*
valu = filterNum(tags[i+valueColumnNumber].innerHTML);
txt += valu;
txt += subdelimit+tags[i+labelColumnNumber].innerHTML+delimit;
i = i+columnCount;
*/
if(i%2!=0){
var newValue = filterNum(tags[i].innerHTML);
}else var newName =tags[i].innerHTML;
if((i>2) & (i%2!=0)){
newText = newText+newValue+subdelimit+newName+delimit;
if(typeof newText != 'undefined'){
txt = newText;
txt = txt.substr(9);
/* alert(txt);*/
}
}
}
}/*else alert ('Hidden');*/
function getFontSize(min,max,val) {
return Math.round((150.0*(1.0+(1.5*val-max/2)/max)));
}
function generateCloud(txt) {
//var txt = "48.1[]Google|28.1[]Yahoo!|10.5[]Live/MSN|4.9[]Ask|5[]AOL";
var logarithmic = false;
var lines = txt.split(delimit);
var min = 10000000000;
var max = 0;
for(var i=0;i<lines.length;i++) {
var line = lines[i];
var data = line.split(subdelimit);
if(data.length != 2) {
lines.splice(i,1);
continue;
}
data[0] = parseFloat(data[0]);
lines[i] = data;
if(data[0] > max)
max = data[0];
if(data[0] < min)
min = data[0];
}lines.sort(function (a,b) {
var A = a[1].toLowerCase();
var B = b[1].toLowerCase();
return A>B ? 1 : (A<B ? -1 : 0);
});
var html = "<style type='text/css'>#jscloud a:hover { text-decoration: underline; }</style> <div id='jscloud'>";
if(logarithmic) {
max = Math.log(max);
min = Math.log(min);
}
for(var i=0;i<lines.length;i++) {
var val = lines[i][0];
if(logarithmic) val = Math.log(val);
var fsize = getFontSize(min,max,val);
dollar = formatCurrency(lines[i][0]);
html += " <a href='###Some drillthrough url which includes the param "+lines[i][1]+"' style='font-size:"+fsize+"%;' title='"+dollar+"'>"+lines[i][1]+"</a> ";
}
html += "</div>";
var cloud = document.getElementById("cloud");
cloud.innerHTML = html;
var cloudhtml = document.getElementById("cloudhtml");
cloudhtml.value = html;
}
function setClass(layer,cls) {
layer.setAttribute("class",cls);
layer.setAttribute("className",cls);
}
function show(display) {
var cloud = document.getElementById("cloud");
var cloudhtml = document.getElementById("cloudhtml");if(display == "cloud") {
setClass(cloud,"visible");
setClass(cloudhtml,"hidden");
}
else if(display == "html") {
setClass(cloud,"hidden");
setClass(cloudhtml,"visible");
}
}
generateCloud(txt);
</script>
Any help or explanations is much appreciated
Sorry, I'm not seeing where a[] and b[] are defined, is this done elsewhere? Firefox and IE may be responding differently to the problem of an undefined array.

Categories