Bitcoin address form validation JavaScript and PHP - javascript

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

Related

Convert Binary/Hex to Base91 / Ascii85 / Base64

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.

Trying to reproduce some JavaScript code in PHP, variable overwrite issue, how to?

I am trying to reproduce some js code in php. I'm not sure why i don't get the same results. I think js overtires some variables (longitudeRange and latitudeRange to be more exact) that php doesn't.
the php $range doesn't have the same result as the js range.
any ideas?
resp = encodeGeohash([34.2360444, -118.5284408]);
alert(resp);
function encodeGeohash (location) {
var g_BASE32 = "0123456789bcdefghjkmnpqrstuvwxyz";
var precision = 10;
var latitudeRange = {
min: -90,
max: 90
};
var longitudeRange = {
min: -180,
max: 180
};
var hash = "";
var hashVal = 0;
var bits = 0;
var even = 1;
while (hash.length < precision) {
var val = even ? location[1] : location[0];
var range = even ? longitudeRange : latitudeRange;
var mid = (range.min + range.max) / 2;
if (val > mid) {
hashVal = (hashVal << 1) + 1;
range.min = mid;
} else {
hashVal = (hashVal << 1) + 0;
range.max = mid;
}
even = !even;
if (bits < 4) {
bits++;
} else {
bits = 0;
hash += g_BASE32[hashVal];
hashVal = 0;
}
}
return hash;
};
php code:
function geoHash($location)
{
$g_BASE32 = "0123456789bcdefghjkmnpqrstuvwxyz";
$precision = 10;
$latitudeRange = ['min' => -90, 'max' => 90];
$longitudeRange = ['min' => -180, 'max' => 180];
$hash = "";
$hashVal = 0;
$bits = 0;
$even = 1;
while (strlen($hash) < $precision) {
$val = $even ? $location[1] : $location[0];
$range = $even ? $longitudeRange : $latitudeRange;
$mid = ($range['min'] + $range['max']) / 2;
var_dump($range);
if ($val > $mid) {
$hashVal = ($hashVal << 1) + 1;
$range['min'] = $mid;
} else {
$hashVal = ($hashVal << 1) + 0;
$range['max'] = $mid;
}
$even = !$even;
if ($bits < 4) {
$bits++;
} else {
$bits = 0;
$hash .= $g_BASE32[$hashVal];
$hashVal = 0;
}
}
var_dump($hash);
return $hash;
}
geoHash([34.2360444, -118.5284408]);
When you assign
var range = even ? longitudeRange : latitudeRange;
in javascript, range references an object. So, when you later assign
range.min = mid;
it actually changes the property of one of the objects referenced by either longitudeRange or latitudeRange.
Your php code in contrast uses an array in that place, which uses copy-on-write, i.e. when you assign $range['min'] = $mid; it does affect neither $latitudeRange nor $longitudeRange.
You can either use an object
<?php
function geoHash($location)
{
$g_BASE32 = "0123456789bcdefghjkmnpqrstuvwxyz";
$precision = 10;
$latitudeRange = (object)['min' => -90, 'max' => 90];
$longitudeRange = (object)['min' => -180, 'max' => 180];
$hash = "";
$hashVal = 0;
$bits = 0;
$even = 1;
while (strlen($hash) < $precision) {
$val = $even ? $location[1] : $location[0];
$range = $even ? $longitudeRange : $latitudeRange;
$mid = ($range->min + $range->max) / 2;
if ($val > $mid) {
$hashVal = ($hashVal << 1) + 1;
$range->min = $mid;
} else {
$hashVal = ($hashVal << 1) + 0;
$range->max = $mid;
}
$even = !$even;
if ($bits < 4) {
$bits++;
} else {
$bits = 0;
$hash .= $g_BASE32[$hashVal];
$hashVal = 0;
}
}
return $hash;
}
var_dump(geoHash([34.2360444, -118.5284408]));
or fiddle with references in php.

Javascript Algorithm to get the excel-like column name

Basically what I want to do is create list of characters with format like excel column name.
for example :
a,b,c,d,.....,z,aa,ab,ac,....,yz
in php you can just looping it with this code:
for($char = "A"; $char <= "Z"; $char++)
{
echo $char . "\n";
}
but when I try it in javascript :
var i3;
var text3 = "";
for(i3 = "A"; i3 <= "Z"; i3++)
{
text3 += i3 + ", ";
}
document.getElementById("pId").innerHTML = text3;
It doesn't work for me. Are there some errors in my code? Or that PHP logic doesn't work in JS? If you know how to make one please tell me, thanks.
In javacript the incrementor operator will return NaN when called on a string value.
You can use ascii code based implementation like
var i3, i4;
var text3 = "";
for (i3 = 0; i3 < 26; i3++) {
text3 += String.fromCharCode(97 + i3) + ", ";
}
for (i3 = 0; i3 < 26; i3++) {
for (i4 = 0; i4 < 26; i4++) {
text3 += String.fromCharCode(97 + i3) + String.fromCharCode(97 + i4) + ", ";
}
}
document.getElementById("pId").innerHTML = text3;
<span id="pId"></span>
Short is beautiful!
var nextChar = c=>c?String.fromCharCode(c.charCodeAt(0)+1):'A';
var nextCol = s=>s.replace(/([^Z]?)(Z*)$/, (_,a,z)=>nextChar(a) + z.replace(/Z/g,'A'));
//test:
nextCol(''); //A
nextCol('A'); //B
nextCol('Z'); //AA
nextCol('AA'); //AB
nextCol('XYZ'); //XZA
nextCol('ZZZZ'); //AAAAA
//output: A,B,C,...,ZZ
for(var i=0, s=''; i<702; i++){
s = nextCol(s);
console.log(s);
}
//output: A,B,C,...,ZZZZ
for(var i=0, s=''; i<475254; i++){
s = nextCol(s);
console.log(s);
}
I worked on a function called next, which provides the adjacent right cell.
function next(currentCell) {
let regex = /[A-Z]/g;
let numberRegex = /[0-9]/g;
let chars = currentCell.match(regex);
let nums = currentCell.match(numberRegex);
let flag = true;
let x = 1;
while (flag) {
if (chars[chars.length - x] === 'Z') {
if ((chars.length - x) === 0) {
chars[chars.length - x] = 'A';
chars.unshift('A');
flag = false;
} else {
chars[chars.length - x] = 'A';
x++;
}
} else {
chars[chars.length - x] = String.fromCharCode(chars[chars.length - x].charCodeAt(0) + 1);
flag = false;
}
}
return chars.join("") + nums.join("");
}
next('A1') // returns 'B1'
next('ZZ90') // returns 'AAA90'
Please try the below code to get done with javascript
var i3;
var text3 = "";
var c;
for(i3 = 65; 90 >= i3; i3++)
{
c = String.fromCharCode(i3);
text3 += c + ", ";
}
document.getElementById("pId").innerHTML = text3;

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