Correct substring position after replacement - javascript

I have a function like this that's provided by a user:
function replace_function(string) {
return string.replace(/:smile:/g, '⻇')
.replace(/(foo|bar|baz)/g, 'text_$1');
}
and I have input string like this:
var input = 'foo bar :smile: xxxx';
and I have a number from 0 to length of the input string that I use to do substring to split the string.
I need to find number (position) that will match output string after replacement so the split is in the same visual place. The split is just for visualization I only need the number.
The output string can have the same length, this is only for the case when length of input and output is different (like width provided function and input string)
function replace_function(string) {
return string.replace(/:smile:/g, '⻇')
.replace(/(foo|bar|baz)/g, 'text_$1');
}
var textarea = document.querySelector('textarea');
var pre = document.querySelector('pre');
function split() {
var input = textarea.value;
var output = replace_function(input);
// find position for output
var position = textarea.selectionStart;
var split = [
output.substring(0, position),
output.substring(position)
];
pre.innerHTML = JSON.stringify(split);
}
textarea.addEventListener('click', split);
<textarea>xxx foo xxx bar xxx :smile: xxxx</textarea>
<pre></pre>
when you click in the middle of the word that get replaced the position/split need to be after the output word. If you click before, between or after the word the position need to be in the same place (the position in each case will be different to match the correct place)
UPDATE: here is my code that work for :smile: only input, but there is need to be text before :smile: (input = ":smile: asdas" and position in the middle of smile and the position is off)
it work for foo replaced by text_foo but not in the case when there is :smile: before foo (input "asd :smile: asd foo").
var get_position = (function() {
function common_string(formatted, normal) {
function longer(str) {
return found && length(str) > length(found) || !found;
}
var formatted_len = length(formatted);
var normal_len = length(normal);
var found;
for (var i = normal_len; i > 0; i--) {
var test_normal = normal.substring(0, i);
var formatted_normal = replace_function(test_normal);
for (var j = formatted_len; j > 0; j--) {
var test_formatted = formatted.substring(0, j);
if (test_formatted === formatted_normal &&
longer(test_normal)) {
found = test_normal;
}
}
}
return found || '';
}
function index_after_formatting(position, command) {
var start = position === 0 ? 0 : position - 1;
var command_len = length(command);
for (var i = start; i < command_len; ++i) {
var substr = command.substring(0, i);
var next_substr = command.substring(0, i + 1);
var formatted_substr = replace_function(substr);
var formatted_next = replace_function(next_substr);
var substr_len = length(formatted_substr);
var next_len = length(formatted_next);
var test_diff = Math.abs(next_len - substr_len);
if (test_diff > 1) {
console.log('return ' + i);
return i;
}
}
}
return function get_formatted_position(position, command) {
var formatted_position = position;
var string = replace_function(command);
var len = length(string);
var command_len = length(command);
if (len !== command_len) {
var orig_sub = command.substring(0, position);
var orig_len = length(orig_sub);
var sub = replace_function(orig_sub);
var sub_len = length(sub);
var diff = Math.abs(orig_len - sub_len);
if (false && orig_len > sub_len) {
formatted_position -= diff;
} else if (false && orig_len < sub_len) {
formatted_position += diff;
} else {
var index = index_after_formatting(position, command);
var to_end = command.substring(0, index + 1);
//formatted_position -= length(to_end) - orig_len;
formatted_position -= orig_len - sub_len;
if (orig_sub && orig_sub !== to_end) {
var formatted_to_end = replace_function(to_end);
var common = common_string(formatted_to_end, orig_sub);
var re = new RegExp('^' + common);
var before_end = orig_sub.replace(re, '');
var to_end_rest = to_end.replace(re, '');
var to_end_rest_len = length(replace_function(to_end_rest));
if (before_end && orig_sub !== before_end) {
var commnon_len = length(replace_function(common));
formatted_position = position - length(before_end) + to_end_rest_len;
}
}
}
if (formatted_position > len) {
formatted_position = len;
} else if (formatted_position < 0) {
formatted_position = 0;
}
}
return formatted_position;
};
})();
function length(str) {
return str.length;
}
function replace_function(string) {
return string.replace(/:smile:/g, '⻇')
.replace(/(foo|bar|baz)/g, 'text_$1');
}
var textarea = document.querySelector('textarea');
var pre = document.querySelector('pre');
function split() {
var input = textarea.value;
var output = replace_function(input);
// find position for output
var position = get_position(textarea.selectionStart, input);
var split = [
output.substring(0, position),
output.substring(position)
];
pre.innerHTML = JSON.stringify(split);
}
textarea.addEventListener('click', split);
<textarea>xxxx :smile: xxxx :smile: xxx :smile:</textarea>
<pre></pre>

To do this, you'll have to do the replace operation yourself with a RegExp#exec loop, and keep track of how the replacements affect the position, something along these lines (but this can probably be optimized):
function trackingReplace(rex, string, replacement, position) {
var newString = "";
var match;
var index = 0;
var repString;
var newPosition = position;
var start;
rex.lastIndex = 0; // Just to be sure
while (match = rex.exec(string)) {
// Add any of the original string we just skipped
if (rex.global) {
start = rex.lastIndex - match[0].length;
} else {
start = match.index;
rex.lastIndex = start + match[0].length;
}
if (index < start) {
newString += string.substring(index, start);
}
index = rex.lastIndex;
// Build the replacement string. This just handles $$ and $n,
// you may want to add handling for $`, $', and $&.
repString = replacement.replace(/\$(\$|\d)/g, function(m, c0) {
if (c0 == "$") return "$";
return match[c0];
});
// Add on the replacement
newString += repString;
// If the position is affected...
if (start < position) {
// ... update it:
if (rex.lastIndex < position) {
// It's after the replacement, move it
newPosition = Math.max(0, newPosition + repString.length - match[0].length);
} else {
// It's *in* the replacement, put it just after
newPosition += repString.length - (position - start);
}
}
// If the regular expression doesn't have the g flag, break here so
// we do just one replacement (and so we don't have an endless loop!)
if (!rex.global) {
break;
}
}
// Add on any trailing text in the string
if (index < string.length) {
newString += string.substring(index);
}
// Return the string and the updated position
return [newString, newPosition];
}
Here's a snippet showing us testing that with various positions:
function trackingReplace(rex, string, replacement, position) {
var newString = "";
var match;
var index = 0;
var repString;
var newPosition = position;
var start;
rex.lastIndex = 0; // Just to be sure
while (match = rex.exec(string)) {
// Add any of the original string we just skipped
if (rex.global) {
start = rex.lastIndex - match[0].length;
} else {
start = match.index;
rex.lastIndex = start + match[0].length;
}
if (index < start) {
newString += string.substring(index, start);
}
index = rex.lastIndex;
// Build the replacement string. This just handles $$ and $n,
// you may want to add handling for $`, $', and $&.
repString = replacement.replace(/\$(\$|\d)/g, function(m, c0) {
if (c0 == "$") return "$";
return match[c0];
});
// Add on the replacement
newString += repString;
// If the position is affected...
if (start < position) {
// ... update it:
if (rex.lastIndex < position) {
// It's after the replacement, move it
newPosition = Math.max(0, newPosition + repString.length - match[0].length);
} else {
// It's *in* the replacement, put it just after
newPosition += repString.length - (position - start);
}
}
// If the regular expression doesn't have the g flag, break here so
// we do just one replacement (and so we don't have an endless loop!)
if (!rex.global) {
break;
}
}
// Add on any trailing text in the string
if (index < string.length) {
newString += string.substring(index);
}
// Return the string and the updated position
return [newString, newPosition];
}
function show(str, pos) {
console.log(str.substring(0, pos) + "|" + str.substring(pos));
}
function test(rex, str, replacement, pos) {
show(str, pos);
var result = trackingReplace(rex, str, replacement, pos);
show(result[0], result[1]);
}
for (var n = 3; n < 22; ++n) {
if (n > 3) {
console.log("----");
}
test(/([f])([o])o/g, "test foo result foo x", "...$2...", n);
}
.as-console-wrapper {
max-height: 100% !important;
}
And here's your snippet updated to use it:
function trackingReplace(rex, string, replacement, position) {
var newString = "";
var match;
var index = 0;
var repString;
var newPosition = position;
var start;
rex.lastIndex = 0; // Just to be sure
while (match = rex.exec(string)) {
// Add any of the original string we just skipped
if (rex.global) {
start = rex.lastIndex - match[0].length;
} else {
start = match.index;
rex.lastIndex = start + match[0].length;
}
if (index < start) {
newString += string.substring(index, start);
}
index = rex.lastIndex;
// Build the replacement string. This just handles $$ and $n,
// you may want to add handling for $`, $', and $&.
repString = replacement.replace(/\$(\$|\d)/g, function(m, c0) {
if (c0 == "$") return "$";
return match[c0];
});
// Add on the replacement
newString += repString;
// If the position is affected...
if (start < position) {
// ... update it:
if (rex.lastIndex < position) {
// It's after the replacement, move it
newPosition = Math.max(0, newPosition + repString.length - match[0].length);
} else {
// It's *in* the replacement, put it just after
newPosition += repString.length - (position - start);
}
}
// If the regular expression doesn't have the g flag, break here so
// we do just one replacement (and so we don't have an endless loop!)
if (!rex.global) {
break;
}
}
// Add on any trailing text in the string
if (index < string.length) {
newString += string.substring(index);
}
// Return the string and the updated position
return [newString, newPosition];
}
function replace_function(string, position) {
var result = trackingReplace(/:smile:/g, string, '⻇', position);
result = trackingReplace(/(foo|bar|baz)/g, result[0], 'text_$1', result[1]);
return result;
}
var textarea = document.querySelector('textarea');
var pre = document.querySelector('pre');
function split() {
var position = textarea.selectionStart;
var result = replace_function(textarea.value, position);
var string = result[0];
position = result[1];
var split = [
string.substring(0, position),
string.substring(position)
];
pre.innerHTML = JSON.stringify(split);
}
textarea.addEventListener('click', split);
<textarea>:smile: foo</textarea>
<pre></pre>

Related

How to use bitwise operators on raw binary data in JavaScript for a CRC check?

I'm porting a CRC check function made in Python to JavaScript and I'm struggling a bit.
Here is the working Python code:
from BitVector import BitVector # BitVector==3.4.8
def compute_crc(message):
message = message.deep_copy()
generator = BitVector(bitstring='1111111111111010000001001')
crc_length = len(generator) - 1
content_length = len(message) - crc_length
assert content_length >= 0
for i in range(content_length):
if message[i]:
message[i:i + len(generator)] ^= generator
return message[-crc_length:]
adsb_hex = "8D4840D6202CC371C32CE0576098"
adsb_crc = compute_crc(BitVector(hexstring = adsb_hex))
if int(adsb_crc) != 0:
print 'Bad'
else:
print 'Good'
Here is my (bad) JavaScript code:
function BinToDec(binStr)
{
var regexp = /^[01]+$/
if (!regexp.test(binStr)) {
return false;
}
var result = parseInt(binStr, 2);
if (isNaN(result)) {
return false;
}
return result;
}
function StrToBinArray(binStr)
{
var result = new Array();
for (var i = 0; i < binStr.length; i++) {
result[i] = binStr[i] === '0' ? 0 : 1;
}
return result;
}
function TestCRC(adsbBinStr)
{
var genStr = '1111111111111010000001001';
var generatorInt = BinToDec(genStr);
var generator = StrToBinArray(genStr);
var crcLength = generator.length - 1;
var contentLength = adsbBinStr.length - crcLength;
if (contentLength < 0) {
return false;
}
var adsbBin = StrToBinArray(adsbBinStr);
for (var i = 0; i <= contentLength; i++) {
if (adsbBin[i]) {
var currSlice = adsbBin.slice(i, i + generator.length);
var currSliceInt = BinToDec(BinArrayToStr(currSlice));
var currCheck = currSliceInt ^ generatorInt;
var currCheckBin = currCheck.toString(2);
for (var j = 0; j <= currCheckBin.length; j++) {
adsbBin[i + j] = currCheckBin[j];
}
}
}
return BinToDec(BinArrayToStr(adsbBin.slice(contentLength + 2))) === 0;
}
TestCRC('1000110101001000010000001101011000100000001011001100001101110001110000110010110011100000010101110110000010011000');
If it helps here is the CRC check as pseudocode:
GENERATOR = 1111111111111010000001001
MSG = binary("8D4840D6202CC371C32CE0576098") # total 112 bits
FOR i FROM 0 TO 88: # 112 - 24 parity bits
if MSG[i] is 1:
MSG[i:i+24] = MSG[i:i+24] ^ GENERATOR
CRC = MSG[-24:] # last 24 bits
IF CRC not 0:
MSG is corrupted
TLDR I'm struggling to port message[i:i + len(generator)] ^= generator correctly from Python to JavaScript and I need help.
According to your Python code, this could be an alternative in Javascript.
// just some util functions to replace BitVector functionality
// https://stackoverflow.com/a/12987042/4209136
function checkBin(n){return/^[01]{1,64}$/.test(n)}
function checkDec(n){return/^[0-9]{1,64}$/.test(n)}
function checkHex(n){return/^[0-9A-Fa-f]{1,64}$/.test(n)}
function Bin2Dec(n){if(!checkBin(n))return 0;return parseInt(n,2).toString(10)}
function Dec2Bin(n){if(!checkDec(n)||n<0)return 0;return n.toString(2)}
function Hex2Bin(n){if(!checkHex(n))return 0;return parseInt(n,16).toString(2)}
function ZeroPad(num, size)
{
var result = String(num);
while (result.length < size) {
result = '0' + result;
}
return result;
}
function computeCrc(message) {
var generator = '1111111111111010000001001';
var genInt = Bin2Dec(generator);
var crcLength = generator.length - 1;
var contentLength = message.length - crcLength;
if (contentLength < 0) {
throw 'Invalid content length.';
}
for (var i = 0;i < contentLength;i++) {
if (message[i] === '0') continue;
let curInt = Bin2Dec(message.substr(i, i + generator.length));
message = message.substr(0, i) +
ZeroPad(Dec2Bin(curInt ^ genInt), generator.length) +
message.substr(i + generator.length);
}
return message.substr(message.length - crcLength);
}
var adsbCrc = computeCrc(Hex2Bin("8D4840D6202CC371C32CE0576098"));
console.log(parseInt(adsbCrc) === 0 ? 'Good' : 'Bad');

Increment digit part of string in JavaScript

I have a string that contains digit at the end. I want to increase the digit part by 1 when some actions happened.
e.g.
var myString = 'AA11111'
increaseStringValue(myString)
# myString new value => 'AA11112'
also how can I increase chars when string value reached to 'AA99999' so new value of string will be 'AB11111'?
You can split char and digit parts so you can handle them separately.
like:
function increaseStringValue(str){
let charPart = str.substring(0,2);
let digitPart = str.substring(2);
digitPart = +digitPart+1
if(digitPart >= 99999){
digitPart = 11111;
if(charPart[1] == 'Z'){
if(charPart[0] == 'Z'){
throw 'Overflow happened'
}
charPart = String.fromCharCode(charPart.charCodeAt(0)+1) + 'A'
}else{
charPart = charPart[0] + String.fromCharCode(charPart.charCodeAt(1)+1)
}
}
return charPart + digitPart;
}
increaseStringValue('AA11111'); // 'AA11112'
increaseStringValue('AA99999'); // 'AB11111'
increaseStringValue('AZ99999'); // 'BA11111'
increaseStringValue('ZZ99999'); // Exception: Overflow happened
This links will be helpful for you:
ASCII CODES
what is a method that can be used to increment letters?
Edit:
Following function will be suite for unknown length string with dynamic position of char and digit.
function increaseStringValue(str) {
let charOverFlowed = true;
let result = ""
for (let i = str.length - 1; i >= 0; i--) {
let currentChar = str[i];
if ('123456789'.indexOf(currentChar) !== -1) {
if (charOverFlowed) {
currentChar = +currentChar + 1
charOverFlowed = false;
}
if (currentChar > 9) {
currentChar = 1;
charOverFlowed = true;
}
} else if (charOverFlowed) {
currentChar = String.fromCharCode(currentChar.charCodeAt(0) + 1)
charOverFlowed = false;
if (currentChar > 'Z') {
if(i == 0){
throw 'Overflow Happened'
}
currentChar = 'A'
charOverFlowed = true
}
}
result = currentChar + result;
}
return result;
}
increaseStringValue('AAAACA')
// "AAAACB"
increaseStringValue('AAAACA1111')
// "AAAACA1112"
increaseStringValue('A1')
// "A2"
increaseStringValue('Z')
// Uncaught Overflow Happened
increaseStringValue('A1999')
// "A2111"
function increaseStringValue(myString){
return myString.replace(/\d+/ig, function(a){ return a*1+1;});
}
console.log(increaseStringValue("asg61"));
And for next question:
function increaseStringValue(myString){
return myString.replace(/(A)(\d+)/ig, function(a, b, c){
var r = c*1+1; return r==99999+1?"B11111":"A"+r;
});
}
console.log(increaseStringValue("AA99999"));
And Whole way:
function increaseStringValue(myString){
return myString.replace(/([a-e])(\d+)/ig, function(a, b, c){
var r = c*1+1; return r==99999+1?String.fromCharCode(a.charCodeAt(0)+1)+"11111":b+r;
});
}
console.log(increaseStringValue("AB99999"));
Please find the snippet useful. If this is what you are expecting.
let stringNum = 'AA11111';//initialise string
let clickTriggered = ()=>{
let startString = "AA";
let newNum = ()=>{
let numberPart = stringNum.split("AA")[1];
let lastChar = stringNum[stringNum.length-1];
return Number(numberPart) != NaN || Number(numberPart) <= 99999 ? Number(numberPart)+1 : 11111;
};
stringNum = `${startString}${newNum()}`
console.log(stringNum)
}
<h1 onclick="clickTriggered()">click here</h1>
You can use String#replace and provide your increment logic in the function callback of the string#replace.
const increaseStringValue = (str) => str.replace(/\d+$/, n => n === '99999' ? 11111 : +n + 1);
console.log(increaseStringValue('AA99999'));
console.log(increaseStringValue('AA11315'));
console.log(increaseStringValue('AA11111'));
I solve this with this solution
let app = new Vue({
el: '#app',
data: {
text: "AA995"
},
methods: {
addOneString: function(str) {
var alphabet = 'abcdefghijklmnopqrstuvwxyz',
length = alphabet.length,
result = str,
i = str.length,
value = str;
while(i >= 0) {
var last = str.charAt(--i),
next = '',
carry = false;
if (isNaN(last)) {
index = alphabet.indexOf(last.toLowerCase());
if (index === -1) {
next = last;
carry = true;
}
else {
var isUpperCase = last === last.toUpperCase();
next = alphabet.charAt((index + 1) % length);
if (isUpperCase) {
next = next.toUpperCase();
}
carry = index + 1 >= length;
if (carry && i === 0) {
var added = isUpperCase ? 'A' : 'a';
result = added + next + result.slice(1);
break;
}
}
}
else {
next = +last + 1;
if(next > 9) {
next = 0;
carry = true;
}
if (carry && i === 0) {
result = '1' + next + result.slice(1);
break;
}
}
result = result.slice(0, i) + next + result.slice(i + 1);
if (!carry) {
break;
}
}
console.log("result",result);
if (value !== result ) this.text = result;
}
}
})
<script src="https://unpkg.com/vue/dist/vue.js"></script>
<div class="container" id="app">
<button #click="addOneString(text)">Add one</button>
</br>
<p> {{text}} </p>
</div>

How to convert from Path to XPath, given a browser event

On a MouseEvent instance, we have a property called path, it might look like this:
Does anybody know if there is a reliable way to translate this path array into an XPath? I assume that this path data is the best data to start from? Is there a library I can use to do the conversion?
This library looks promising, but it doesn't use the path property of an event: https://github.com/johannhof/xpath-dom
There's not a unique XPath to a node, so you'll have to decide what's the most appropriate way of constructing a path. Use IDs where available? Numeral position in the document? Position relative to other elements?
See getPathTo() in this answer for one possible approach.
PS: Taken from Javascript get XPath of a node
Another option is to use SelectorGadget from below link
https://dv0akt2986vzh.cloudfront.net/unstable/lib/selectorgadget.js
The actual code for the DOM path is at
https://dv0akt2986vzh.cloudfront.net/stable/lib/dom.js
Usage: on http://google.com
elem = document.getElementById("q")
predict = new DomPredictionHelper()
predict.pathOf(elem)
// gives "body.default-theme.des-mat:nth-child(2) div#_Alw:nth-child(4) form#f:nth-child(2) div#fkbx:nth-child(2) input#q:nth-child(2)"
predict.predictCss([elem],[])
// gives "#q"
CODE if link goes down
// Copyright (c) 2008, 2009 Andrew Cantino
// Copyright (c) 2008, 2009 Kyle Maxwell
function DomPredictionHelper() {};
DomPredictionHelper.prototype = new Object();
DomPredictionHelper.prototype.recursiveNodes = function(e){
var n;
if(e.nodeName && e.parentNode && e != document.body) {
n = this.recursiveNodes(e.parentNode);
} else {
n = new Array();
}
n.push(e);
return n;
};
DomPredictionHelper.prototype.escapeCssNames = function(name) {
if (name) {
try {
return name.replace(/\s*sg_\w+\s*/g, '').replace(/\\/g, '\\\\').
replace(/\./g, '\\.').replace(/#/g, '\\#').replace(/\>/g, '\\>').replace(/\,/g, '\\,').replace(/\:/g, '\\:');
} catch(e) {
console.log('---');
console.log("exception in escapeCssNames");
console.log(name);
console.log('---');
return '';
}
} else {
return '';
}
};
DomPredictionHelper.prototype.childElemNumber = function(elem) {
var count = 0;
while (elem.previousSibling && (elem = elem.previousSibling)) {
if (elem.nodeType == 1) count++;
}
return count;
};
DomPredictionHelper.prototype.pathOf = function(elem){
var nodes = this.recursiveNodes(elem);
var self = this;
var path = "";
for(var i = 0; i < nodes.length; i++) {
var e = nodes[i];
if (e) {
path += e.nodeName.toLowerCase();
var escaped = e.id && self.escapeCssNames(new String(e.id));
if(escaped && escaped.length > 0) path += '#' + escaped;
if(e.className) {
jQuery.each(e.className.split(/ /), function() {
var escaped = self.escapeCssNames(this);
if (this && escaped.length > 0) {
path += '.' + escaped;
}
});
}
path += ':nth-child(' + (self.childElemNumber(e) + 1) + ')';
path += ' '
}
}
if (path.charAt(path.length - 1) == ' ') path = path.substring(0, path.length - 1);
return path;
};
DomPredictionHelper.prototype.commonCss = function(array) {
try {
var dmp = new diff_match_patch();
} catch(e) {
throw "Please include the diff_match_patch library.";
}
if (typeof array == 'undefined' || array.length == 0) return '';
var existing_tokens = {};
var encoded_css_array = this.encodeCssForDiff(array, existing_tokens);
var collective_common = encoded_css_array.pop();
jQuery.each(encoded_css_array, function(e) {
var diff = dmp.diff_main(collective_common, this);
collective_common = '';
jQuery.each(diff, function() {
if (this[0] == 0) collective_common += this[1];
});
});
return this.decodeCss(collective_common, existing_tokens);
};
DomPredictionHelper.prototype.tokenizeCss = function(css_string) {
var skip = false;
var word = '';
var tokens = [];
var css_string = css_string.replace(/,/, ' , ').replace(/\s+/g, ' ');
var length = css_string.length;
var c = '';
for (var i = 0; i < length; i++){
c = css_string[i];
if (skip) {
skip = false;
} else if (c == '\\') {
skip = true;
} else if (c == '.' || c == ' ' || c == '#' || c == '>' || c == ':' || c == ',') {
if (word.length > 0) tokens.push(word);
word = '';
}
word += c;
if (c == ' ' || c == ',') {
tokens.push(word);
word = '';
}
}
if (word.length > 0) tokens.push(word);
return tokens;
};
DomPredictionHelper.prototype.decodeCss = function(string, existing_tokens) {
var inverted = this.invertObject(existing_tokens);
var out = '';
jQuery.each(string.split(''), function() {
out += inverted[this];
});
return this.cleanCss(out);
};
// Encode css paths for diff using unicode codepoints to allow for a large number of tokens.
DomPredictionHelper.prototype.encodeCssForDiff = function(strings, existing_tokens) {
var codepoint = 50;
var self = this;
var strings_out = [];
jQuery.each(strings, function() {
var out = new String();
jQuery.each(self.tokenizeCss(this), function() {
if (!existing_tokens[this]) {
existing_tokens[this] = String.fromCharCode(codepoint++);
}
out += existing_tokens[this];
});
strings_out.push(out);
});
return strings_out;
};
DomPredictionHelper.prototype.simplifyCss = function(css, selected_paths, rejected_paths) {
var self = this;
var parts = self.tokenizeCss(css);
var best_so_far = "";
if (self.selectorGets('all', selected_paths, css) && self.selectorGets('none', rejected_paths, css)) best_so_far = css;
for (var pass = 0; pass < 4; pass++) {
for (var part = 0; part < parts.length; part++) {
var first = parts[part].substring(0,1);
if (self.wouldLeaveFreeFloatingNthChild(parts, part)) continue;
if ((pass == 0 && first == ':') || // :nth-child
(pass == 1 && first != ':' && first != '.' && first != '#' && first != ' ') || // elem, etc.
(pass == 2 && first == '.') || // classes
(pass == 3 && first == '#')) // ids
{
var tmp = parts[part];
parts[part] = '';
var selector = self.cleanCss(parts.join(''));
if (selector == '') {
parts[part] = tmp;
continue;
}
if (self.selectorGets('all', selected_paths, selector) && self.selectorGets('none', rejected_paths, selector)) {
best_so_far = selector;
} else {
parts[part] = tmp;
}
}
}
}
return self.cleanCss(best_so_far);
};
DomPredictionHelper.prototype.wouldLeaveFreeFloatingNthChild = function(parts, part) {
return (((part - 1 >= 0 && parts[part - 1].substring(0, 1) == ':') &&
(part - 2 < 0 || parts[part - 2] == ' ') &&
(part + 1 >= parts.length || parts[part + 1] == ' ')) ||
((part + 1 < parts.length && parts[part + 1].substring(0, 1) == ':') &&
(part + 2 >= parts.length || parts[part + 2] == ' ') &&
(part - 1 < 0 || parts[part - 1] == ' ')));
};
DomPredictionHelper.prototype.cleanCss = function(css) {
return css.replace(/\>/, ' > ').replace(/,/, ' , ').replace(/\s+/g, ' ').replace(/^\s+|\s+$/g, '').replace(/,$/, '');
};
DomPredictionHelper.prototype.getPathsFor = function(arr) {
var self = this;
var out = [];
jQuery.each(arr, function() {
if (this && this.nodeName) {
out.push(self.pathOf(this));
}
})
return out;
};
DomPredictionHelper.prototype.predictCss = function(s, r) {
var self = this;
if (s.length == 0) return '';
var selected_paths = self.getPathsFor(s);
var rejected_paths = self.getPathsFor(r);
var css = self.commonCss(selected_paths);
var simplest = self.simplifyCss(css, selected_paths, rejected_paths);
// Do we get off easy?
if (simplest.length > 0) return simplest;
// Okay, then make a union and possibly try to reduce subsets.
var union = '';
jQuery.each(s, function() {
union = self.pathOf(this) + ", " + union;
});
union = self.cleanCss(union);
return self.simplifyCss(union, selected_paths, rejected_paths);
};
DomPredictionHelper.prototype.fragmentSelector = function(selector) {
var self = this;
var out = [];
jQuery.each(selector.split(/\,/), function() {
var out2 = [];
jQuery.each(self.cleanCss(this).split(/\s+/), function() {
out2.push(self.tokenizeCss(this));
});
out.push(out2);
});
return out;
};
// Everything in the first selector must be present in the second.
DomPredictionHelper.prototype.selectorBlockMatchesSelectorBlock = function(selector_block1, selector_block2) {
for (var j = 0; j < selector_block1.length; j++) {
if (jQuery.inArray(selector_block1[j], selector_block2) == -1) {
return false;
}
}
return true;
};
// Assumes list is an array of complete CSS selectors represented as strings.
DomPredictionHelper.prototype.selectorGets = function(type, list, the_selector) {
var self = this;
var result = true;
if (list.length == 0 && type == 'all') return false;
if (list.length == 0 && type == 'none') return true;
var selectors = self.fragmentSelector(the_selector);
var cleaned_list = [];
jQuery.each(list, function() {
cleaned_list.push(self.fragmentSelector(this)[0]);
});
jQuery.each(selectors, function() {
if (!result) return;
var selector = this;
jQuery.each(cleaned_list, function(pos) {
if (!result || this == '') return;
if (self._selectorGets(this, selector)) {
if (type == 'none') result = false;
cleaned_list[pos] = '';
}
});
});
if (type == 'all' && cleaned_list.join('').length > 0) { // Some candidates didn't get matched.
result = false;
}
return result;
};
DomPredictionHelper.prototype._selectorGets = function(candidate_as_blocks, selector_as_blocks) {
var cannot_match = false;
var position = candidate_as_blocks.length - 1;
for (var i = selector_as_blocks.length - 1; i > -1; i--) {
if (cannot_match) break;
if (i == selector_as_blocks.length - 1) { // First element on right.
// If we don't match the first element, we cannot match.
if (!this.selectorBlockMatchesSelectorBlock(selector_as_blocks[i], candidate_as_blocks[position])) cannot_match = true;
position--;
} else {
var found = false;
while (position > -1 && !found) {
found = this.selectorBlockMatchesSelectorBlock(selector_as_blocks[i], candidate_as_blocks[position]);
position--;
}
if (!found) cannot_match = true;
}
}
return !cannot_match;
};
DomPredictionHelper.prototype.invertObject = function(object) {
var new_object = {};
jQuery.each(object, function(key, value) {
new_object[value] = key;
});
return new_object;
};
DomPredictionHelper.prototype.cssToXPath = function(css_string) {
var tokens = this.tokenizeCss(css_string);
if (tokens[0] && tokens[0] == ' ') tokens.splice(0, 1);
if (tokens[tokens.length - 1] && tokens[tokens.length - 1] == ' ') tokens.splice(tokens.length - 1, 1);
var css_block = [];
var out = "";
for(var i = 0; i < tokens.length; i++) {
if (tokens[i] == ' ') {
out += this.cssToXPathBlockHelper(css_block);
css_block = [];
} else {
css_block.push(tokens[i]);
}
}
return out + this.cssToXPathBlockHelper(css_block);
};
// Process a block (html entity, class(es), id, :nth-child()) of css
DomPredictionHelper.prototype.cssToXPathBlockHelper = function(css_block) {
if (css_block.length == 0) return '//';
var out = '//';
var first = css_block[0].substring(0,1);
if (first == ',') return " | ";
if (jQuery.inArray(first, [':', '#', '.']) != -1) {
out += '*';
}
var expressions = [];
var re = null;
for(var i = 0; i < css_block.length; i++) {
var current = css_block[i];
first = current.substring(0,1);
var rest = current.substring(1);
if (first == ':') {
// We only support :nth-child(n) at the moment.
if (re = rest.match(/^nth-child\((\d+)\)$/))
expressions.push('(((count(preceding-sibling::*) + 1) = ' + re[1] + ') and parent::*)');
} else if (first == '.') {
expressions.push('contains(concat( " ", #class, " " ), concat( " ", "' + rest + '", " " ))');
} else if (first == '#') {
expressions.push('(#id = "' + rest + '")');
} else if (first == ',') {
} else {
out += current;
}
}
if (expressions.length > 0) out += '[';
for (var i = 0; i < expressions.length; i++) {
out += expressions[i];
if (i < expressions.length - 1) out += ' and ';
}
if (expressions.length > 0) out += ']';
return out;
};

implementing split() method for exercise 4 in Object Oriented Javascript 2n edition

here is the question:
Imagine the String() constructor didn't exist. Create a constructor
function, MyString(), that acts like String() as closely as possible.
You're not allowed to use any built-in string methods or properties,
and remember that String() doesn't exist. You can use this code to
test your constructor:
I created constructor however I have no clue how to re-create split method, how to implement that functionality.
If you could give an idea how to implement split method, I would be grateful
function MyString(str) {
var thisObj = this;
var innerLength = 0;
this.length;
function updateLength() {
innerLength = 0;
for (var i = 0; str[i] != undefined; i++) {
innerLength++;
thisObj[i] = str[i];
}
thisObj.length = innerLength;
}
updateLength();
this.toString = function() {
return str;
}
this.charAt = function(i) {
if (isNaN(parseInt(i))) {
return this[0]
} else {
return this[i]
}
}
this.concat = function(string) {
str += string;
updateLength();
}
this.slice = function(start, end) {
var slicedString = "";
if (start >= 0 && end >= 00) {
for (start; start < end; start++) {
slicedString += str[start];
}
}
return slicedString;
}
this.reverse = function() {
var arr = str.split("");
arr.reverse();
var reversedString = "",
i;
for (i = 0; i < arr.length; i++) {
reversedString += arr[i];
}
return reversedString;
}
}
var ms = new MyString("Hello, I am a string")
console.log(ms.reverse())
You can convert the string to an array and use Array.prototype and RegExp.prototype methods.
this.split = function(re) {
var arr = Array.prototype.slice.call(this.toString());
if (re === "") {
return arr
}
if (re === " ") {
return arr.filter(function(el) {
return /[^\s]/.test(el)
})
}
if (/RegExp/.test(Object.getPrototypeOf(re).constructor)) {
var regexp = re.source;
return arr.filter(function(el) {
return regexp.indexOf(el) === -1
})
}
}
function MyString(str) {
var thisObj = this;
var innerLength = 0;
this.length;
function updateLength() {
innerLength = 0;
for (var i = 0; str[i] != undefined; i++) {
innerLength++;
thisObj[i] = str[i];
}
thisObj.length = innerLength;
}
updateLength();
this.toString = function() {
return str;
}
this.split = function(re) {
var arr = Array.prototype.slice.call(this.toString());
if (re === "") {
return arr
}
if (re === " ") {
return arr.filter(function(el) {
return /[^\s]/.test(el)
})
}
if (/RegExp/.test(Object.getPrototypeOf(re).constructor)) {
var regexp = re.source;
return arr.filter(function(el) {
return regexp.indexOf(el) === -1
})
}
}
this.charAt = function(i) {
if (isNaN(parseInt(i))) {
return this[0]
} else {
return this[i]
}
}
this.concat = function(string) {
str += string;
updateLength();
}
this.slice = function(start, end) {
var slicedString = "";
if (start >= 0 && end >= 00) {
for (start; start < end; start++) {
slicedString += str[start];
}
}
return slicedString;
}
this.reverse = function() {
var arr = str.split("");
arr.reverse();
var reversedString = "",
i;
for (i = 0; i < arr.length; i++) {
reversedString += arr[i];
}
return reversedString;
}
}
var ms = new MyString("Hello, I am a string")
console.log(ms.split(""), ms.split(" "), ms.split(/l/))
Iterate and search:
MyString.prototype.split = function(splitter){
var tmp="", result = [];
for(var i = 0; i < this.length; i++){
for(var offset = 0; offset < this.length && offset < splitter.length;offset++){
if(this[i+offset] !== splitter[offset]) break;
}
if(offset === splitter.length){
result.push( tmp );
tmp="";
i += offset -1;
}else{
tmp+=this[i];
}
}
result.push(tmp);
return result;
};
So now you can do:
new MyString("testtest").split("t") //['','es','','','es','']
In action

get url relative to another url in javascript

i want to get url relative to another url
for example
"../d.html".relativeTo("/a/b/c.html"); //==> "/a/d.html"
"g.html".relativeTo("/a/b/c.html"); //==> "/a/b/g.html"
"./f/j.html".relativeTo("/a/b/c.html"); //==> "/a/b/f/j.html"
"../../k/w.html".relativeTo("/a/b/c.html"); //==> "/k/w.html"
"http://www.google.com".relativeTo("/a/b/c.html"); //==> "http://www.google.com"
i think there is a simple solution for that because browsers does it for relative url links.
i have tried
String.prototype.relativeTo=function(input){
if(/^https?:\/\//i.test(this)) {
return this.valueOf();
}
else {
var a = document.createElement("a");
a.href = input.replace(/\w+\.\w+/, "") + this;
return a.href;
}
}
but it returns absolute url
is there some simple way to do that?
Really interesting, anyway I would like to answer
String.prototype.startsWith = function (input) {
return this.substring(0, input.length) === input;
};
String.prototype.relativeTo = function (input) {
var toTop = /..\//gi;
var abs = /^https?:\/\//i;
var inCurrent = './';
var matches;
if (abs.test(this)) {
return this.valueOf();
}
function getLastSegmentIndex() {
return (input.lastIndexOf('/') + 1) - (input.length - 1);
}
try {
matches = this.match(toTop).length;
} catch (e) {
matches = 0;
}
if (!matches) {
return input.slice(0, -getLastSegmentIndex()) + this.valueOf();
} else if (this.startsWith(inCurrent)) {
return input.slice(0, -getLastSegmentIndex()) +
this.replace(inCurrent, '');
}
var segments = input.split('/');
var i = 0;
for (; i < matches + 1; i++) {
segments.pop();
}
segments.push((this.replace(toTop, '')));
return segments.join('/');
};

Categories