-
-
Save slevithan/2387872 to your computer and use it in GitHub Desktop.
// Simulating infinite-length leading lookbehind in JavaScript. Uses XRegExp. | |
// Captures within lookbehind are not included in match results. Lazy | |
// repetition in lookbehind may lead to unexpected results. | |
(function (XRegExp) { | |
function prepareLb(lb) { | |
// Allow mode modifier before lookbehind | |
var parts = /^((?:\(\?[\w$]+\))?)\(\?<([=!])([\s\S]*)\)$/.exec(lb); | |
return { | |
// $(?!\s) allows use of (?m) in lookbehind | |
lb: XRegExp(parts ? parts[1] + "(?:" + parts[3] + ")$(?!\\s)" : lb), | |
// Positive or negative lookbehind. Use positive if no lookbehind group | |
type: parts ? parts[2] === "=" : !parts | |
}; | |
} | |
XRegExp.execLb = function (str, lb, regex) { | |
var pos = 0, match, leftContext; | |
lb = prepareLb(lb); | |
while (match = XRegExp.exec(str, regex, pos)) { | |
leftContext = str.slice(0, match.index); | |
if (lb.type === lb.lb.test(leftContext)) { | |
return match; | |
} | |
pos = match.index + 1; | |
} | |
return null; | |
}; | |
XRegExp.testLb = function (str, lb, regex) { | |
return !!XRegExp.execLb(str, lb, regex); | |
}; | |
XRegExp.searchLb = function (str, lb, regex) { | |
var match = XRegExp.execLb(str, lb, regex); | |
return match ? match.index : -1; | |
}; | |
XRegExp.matchAllLb = function (str, lb, regex) { | |
var matches = [], pos = 0, match, leftContext; | |
lb = prepareLb(lb); | |
while (match = XRegExp.exec(str, regex, pos)) { | |
leftContext = str.slice(0, match.index); | |
if (lb.type === lb.lb.test(leftContext)) { | |
matches.push(match[0]); | |
pos = match.index + (match[0].length || 1); | |
} else { | |
pos = match.index + 1; | |
} | |
} | |
return matches; | |
}; | |
XRegExp.replaceLb = function (str, lb, regex, replacement) { | |
var output = "", pos = 0, lastEnd = 0, match, leftContext; | |
lb = prepareLb(lb); | |
while (match = XRegExp.exec(str, regex, pos)) { | |
leftContext = str.slice(0, match.index); | |
if (lb.type === lb.lb.test(leftContext)) { | |
// Doesn't work correctly if lookahead in regex looks outside of the match | |
output += str.slice(lastEnd, match.index) + XRegExp.replace(match[0], regex, replacement); | |
lastEnd = match.index + match[0].length; | |
if (!regex.global) { | |
break; | |
} | |
pos = match.index + (match[0].length || 1); | |
} else { | |
pos = match.index + 1; | |
} | |
} | |
return output + str.slice(lastEnd); | |
}; | |
}(XRegExp)); | |
// Test it... | |
console.log(XRegExp.execLb("Fluffy cat", "(?i)(?<=fluffy\\W+)", XRegExp("(?i)(?<first>c)at"))); | |
// -> ["cat", "c"] | |
// Result has named backref: result.first -> "c" | |
console.log(XRegExp.execLb("Fluffy cat", "(?i)(?<!fluffy\\W+)", /cat/i)); | |
// -> null | |
console.log(XRegExp.testLb("Fluffy cat", "(?i)(?<=fluffy\\W+)", /cat/i)); | |
// -> true | |
console.log(XRegExp.testLb("Fluffy cat", "(?i)(?<!fluffy\\W+)", /cat/i)); | |
// -> false | |
console.log(XRegExp.searchLb("Catwoman's fluffy cat", "(?i)(?<=fluffy\\W+)", /cat/i)); | |
// -> 18 | |
console.log(XRegExp.searchLb("Catwoman's fluffy cat", "(?i)(?<!fluffy\\W+)", /cat/i)); | |
// -> 0 | |
console.log(XRegExp.matchAllLb("Catwoman's cats are fluffy cats", "(?i)(?<=fluffy\\W+)", /cat\w*/i)); | |
// -> ["cats"] | |
console.log(XRegExp.matchAllLb("Catwoman's cats are fluffy cats", "(?i)(?<!fluffy\\W+)", /cat\w*/i)); | |
// -> ["Catwoman", "cats"] | |
console.log(XRegExp.replaceLb("Catwoman's fluffy cat is a cat", "(?i)(?<=fluffy\\W+)", /cat/ig, "dog")); | |
// -> "Catwoman's fluffy dog is a cat" | |
console.log(XRegExp.replaceLb("Catwoman's fluffy cat is a cat", "(?i)(?<!fluffy\\W+)", /cat/ig, "dog")); | |
// -> "dogwoman's fluffy cat is a dog" | |
console.log(XRegExp.replaceLb("Catwoman's fluffy cat is a cat", "(?i)(?<!fluffy\\W+)", /cat/ig, function ($0) { | |
var first = $0.charAt(0); | |
return first === first.toUpperCase() ? "Dog" : "dog"; | |
})); | |
// -> "Dogwoman's fluffy cat is a dog" |
I want to match the entire tag that contains some haml databinding script in an attribute
e.g.
'
Can this be achieved using matchLb? I don't understand how to match the string starting with < but ending with > and not !>.
sorry. to continue :
e.g.
<table id="r<!=id!>">
I want to match the to the < not preceded by a !.
Is this possible?
Ha! I should have 'known' it would be you to have an alternative for JS ;-)
Are you able to please provide a method for splitting that supports lookbehind? Thanks!
I made a small node wrapper for these. Thanks for providing them!
@ghost +1
I really need XRegExp.splitLb
I am terrible in regex. I have the following piece of code which is struggling with the look behind issue. can anyone help me solve it with provided code in the repo?
myString.replace(/\B(?<!\.\d*)(?=(\d{3})+(?!\d))/g,",")}
Is there a version to extend XRegExp
instead of providing new methods? This suggestion is great but it doesn't polyfill the negative lookbehind, more like provides a standalone API to use it.
Yes, JavaScript-the-standard supports lookahead, as do all browsers going back to IE 5.5.