Replace text and following letter with other text - javascript

I am making a syntax highlighter in jQuery, and I would like to be able to show escap-tion (e.g. \n). Currently, my code just replaces \ with <span style="color:#3b73e3;">\\</span> (the first \ is for... well... escap-tion). What I would like is if it could replace \ and the following letter with <span style="color:#3b73e3;">\\(the escaped letter)</span>.
The current .replace code:
String.prototype.replaceAllTxt = function() {
return this.toString().replace(/\$slc/g, '*select')
.replace(/\$/g, "!dol")
.replace(/:/g, "$cln")
.replace(/\//g, "$slh")
.replace(/\./g, "$per")
.replace(/#/g, "$at")
.replace(/%/g, "$prc")
.replace(/"/g, "*")
.replace('*select', '<span id="select"></span>')
.replace(/\*([^\*]+)\*/g, '<span style="color:#16630D">"$1"</span>')
.replace(/\$cln/g, '<span style="color:#ff755d">:</span>')
.replace(/\$at/g, '<span style="color:#ff26b6">#</span>')
.replace(/\$prc/g, '<span style="color:#9e26ff">%</span>')
.replace(/\$per/g, '<span style="color:#00bbff">.</span>')
.replace(/\$slh/g, '<span style="color:#3b73e3;">/</span>')
.replace(/\\/g, '<span style="color:#3b73e3;">\\</span>')
.replace(/!dol/g, '<span style="color:#9b1940;font-weight:bold;">$</span>')
.replace(/\$cln/g, ":")
.replace(/\$slh/g, "/")
.replace(/\$per/g, ".")
.replace(/\$at/g, "#")
.replace(/\$prc/g, "%")
.replace(/\*/g, '"')
.replace(/!dol/g, '$');
};
Everything so far:
String.prototype.replaceAllTxt = function() {
return this.toString().replace(/\$slc/g, '*select')
.replace(/\$/g, "!dol")
.replace(/:/g, "$cln")
.replace(/\//g, "$slh")
.replace(/\./g, "$per")
.replace(/#/g, "$at")
.replace(/%/g, "$prc")
.replace(/"/g, "*")
.replace('*select', '<span id="select"></span>')
.replace(/\*([^\*]+)\*/g, '<span style="color:#16630D">"$1"</span>')
.replace(/\$cln/g, '<span style="color:#ff755d">:</span>')
.replace(/\$at/g, '<span style="color:#ff26b6">#</span>')
.replace(/\$prc/g, '<span style="color:#9e26ff">%</span>')
.replace(/\$per/g, '<span style="color:#00bbff">.</span>')
.replace(/\$slh/g, '<span style="color:#3b73e3;">/</span>')
.replace(/\\/g, '<span style="color:#3b73e3;">\\</span>')
.replace(/!dol/g, '<span style="color:#9b1940;font-weight:bold;">$</span>')
.replace(/\$cln/g, ":")
.replace(/\$slh/g, "/")
.replace(/\$per/g, ".")
.replace(/\$at/g, "#")
.replace(/\$prc/g, "%")
.replace(/\*/g, '"')
.replace(/!dol/g, '$');
};
function replaceText(oldText, newText, node){
node = node || document.body; // base node
var childs = node.childNodes, i = 0;
while(node = childs[i]){
if (node.nodeType == 3){ // text node found, do the replacement
if (node.textContent) {
node.textContent = node.textContent.replace(oldText, newText);
} else { // support to IE
node.nodeValue = node.nodeValue.replace(oldText, newText);
}
} else { // not a text mode, look forward
replaceText(oldText, newText, node);
}
i++;
}
}
function codeBox(el) {
pasteHtmlAtCaret('$slc');
$(el).css('color', '#153A7D').each(function () {
setTimeout(function() {
var txt = $(el).text();
var newtxt = txt.replaceAllTxt();
$(el).html(newtxt);
});
});
setTimeout(function() {
$('#select').focus().select().remove();
}, 1);
}
$.fn.codeBox = function () {
$(this).css('color', '#153A7D').each(function () {
var txt = $(this).text();
var newtxt = txt.replaceAllTxt();
$(this).html(newtxt);
});
};
function wait(run, ms) {
setTimeout(run(),ms);
}
function loop (run, ms) {
setInterval(run(), ms);
}
function placeCaretAtEnd(el) {
el.focus();
if (typeof window.getSelection != "undefined"
&& typeof document.createRange != "undefined") {
var range = document.createRange();
range.selectNodeContents(el);
range.collapse(false);
var sel = window.getSelection();
sel.removeAllRanges();
sel.addRange(range);
} else if (typeof document.body.createTextRange != "undefined") {
var textRange = document.body.createTextRange();
textRange.moveToElementText(el);
textRange.collapse(false);
textRange.select();
}
}
function pasteHtmlAtCaret(html, selectPastedContent) {
var sel, range;
if (window.getSelection) {
// IE9 and non-IE
sel = window.getSelection();
if (sel.getRangeAt && sel.rangeCount) {
range = sel.getRangeAt(0);
range.deleteContents();
// Range.createContextualFragment() would be useful here but is
// only relatively recently standardized and is not supported in
// some browsers (IE9, for one)
var el = document.createElement("div");
el.innerHTML = html;
var frag = document.createDocumentFragment(), node, lastNode;
while ( (node = el.firstChild) ) {
lastNode = frag.appendChild(node);
}
var firstNode = frag.firstChild;
range.insertNode(frag);
// Preserve the selection
if (lastNode) {
range = range.cloneRange();
range.setStartAfter(lastNode);
if (selectPastedContent) {
range.setStartBefore(firstNode);
} else {
range.collapse(true);
}
sel.removeAllRanges();
sel.addRange(range);
}
}
} else if ( (sel = document.selection) && sel.type != "Control") {
// IE < 9
var originalRange = sel.createRange();
originalRange.collapse(true);
sel.createRange().pasteHTML(html);
if (selectPastedContent) {
range = sel.createRange();
range.setEndPoint("StartToStart", originalRange);
range.select();
}
}
}
function selectElementText(el, win) {
win = win || window;
var doc = win.document, sel, range;
if (win.getSelection && doc.createRange) {
sel = win.getSelection();
range = doc.createRange();
range.selectNodeContents(el);
sel.removeAllRanges();
sel.addRange(range);
} else if (doc.body.createTextRange) {
range = doc.body.createTextRange();
range.moveToElementText(el);
range.select();
}
}
$(document).ready(function() {
$('p').each(function () {
$(this).html($(this).html().replace(/\n/g, "<br>"));
});
//$('code').each(function() {
// $(this).before('<span class="code" style="font-family:Source Code Pro, monospace;background-color:#e8e8e8;">'+$(this).text()+'</span>').remove();
// c
// console.log ('code')
//});
$('code').codeBox();
$("#edit").keypress(function() {
setTimeout(function() {
codeBox("#edit");
}, 1);
});
$('#highlight').click(function() {
$("#code").codeBox();
})
});
.code {
font-family:'Source Code Pro', monospace;
}
<script src="http://code.jquery.com/jquery-2.1.4.js"></script>
<pre data-syntax="default" id="code">$(document).ready(function() {
alert ("The highlighting is ready. Email me at admin#example for more details.\nThis page has a 100% chance of loading.";
}
I would like for \n to look like </pre><span style="color:#3b73e3;">\n</span>, the same for \anything.
<div class="button" id="highlight">Highlight!</div>
JsFiddle: http://jsfiddle.net/zoweb/zuc5f7w8/
NOTE: Some of the functions in the snippet above are taken from other SO answers, and many are not being used.

You want to capture the next symbol, and this is done by using parentheses:
console.log("stuff\\ntext".replace(/\\(\w)/g, '<span style="color:#3b73e3;">\\$1</span>'));
This will only work for escaped sequences ("\\n"), because plain '\n' is one character.

Related

Ignore span stag in window selection to get the start and end index

I have a html tag which is
<span>This is first text<span class="ignore">Second</span> This is third text<span>
I am trying to get the start and end index from the selected text. When I select third I get start and end index as 34 39
But I expect 27 32
I tried the below approach
export const findTextRange = (element) => {
if (!element) return;
let start = 0, end = 0;
let sel, range, priorRange, text;
if (typeof window.getSelection != "undefined") {
sel = window.getSelection();
text = sel + '';
if (window.getSelection().rangeCount <= 0) {
return;
}
range = window.getSelection().getRangeAt(0);
priorRange = range.cloneRange();
priorRange.selectNodeContents(element);
priorRange.setEnd(range.startContainer, range.startOffset);
start = priorRange.toString().length;
end = start + (sel + '').length;
} else if (typeof document.selection !== "undefined" &&
(sel = document.selection).type !== "Control") {
text = sel + '';
range = sel.createRange();
priorRange = document.body.createTextRange();
priorRange.moveToElementText(element);
priorRange.setEndPoint("EndToStart", range);
start = priorRange.text.length;
end = start + (sel + '').length;
}
return { start, end, text };
}
Is there any way where I can ignore the span element with ignore class.
Store the initial HTML, then remove all elements having the .ignore class:
const html = element.innerHTML;
element.querySelectorAll('.ignore').forEach((e) => e.remove());
After getting the range, restore the original HTML:
element.innerHTML = html;
Snippet
const findTextRange = (element) => {
if (!element) return;
const html = element.innerHTML; // store original HTML
element.querySelectorAll('.ignore').forEach((e) => e.remove()); // remove ignore elements
let start = 0, end = 0;
let sel, range, priorRange, text;
if (typeof window.getSelection != "undefined") {
sel = window.getSelection();
text = sel + '';
if (window.getSelection().rangeCount <= 0) {
return;
}
range = window.getSelection().getRangeAt(0);
priorRange = range.cloneRange();
priorRange.selectNodeContents(element);
priorRange.setEnd(range.startContainer, range.startOffset);
start = priorRange.toString().length;
end = start + (sel + '').length;
} else if (typeof document.selection !== "undefined" &&
(sel = document.selection).type !== "Control") {
text = sel + '';
range = sel.createRange();
priorRange = document.body.createTextRange();
priorRange.moveToElementText(element);
priorRange.setEndPoint("EndToStart", range);
start = priorRange.text.length;
end = start + (sel + '').length;
}
element.innerHTML = html; // restore HTML
console.log(start, end, text);
return { start, end, text };
}
document.querySelector('#P').addEventListener('click', function() {findTextRange(this)});
<span id="P">This is first text<span class="ignore">Second</span> This is third text<span>

Line breaks do not work in my JavaScript code

I have the following project which is based on highlighting the hashtags that are written within the contenteditable box.
The problem is that the code does not allow me to create line breaks.
I need to be able to create new lines and that the cursor position works well.
$(document).ready(function() {
function getCaretCharacterOffsetWithin(element) {
var caretOffset = 0;
var doc = element.ownerDocument || element.document;
var win = doc.defaultView || doc.parentWindow;
var sel;
if (typeof win.getSelection != "undefined") {
sel = win.getSelection();
if (sel.rangeCount > 0) {
var range = win.getSelection().getRangeAt(0);
var preCaretRange = range.cloneRange();
preCaretRange.selectNodeContents(element);
preCaretRange.setEnd(range.endContainer, range.endOffset);
caretOffset = preCaretRange.toString().length;
}
} else if ((sel = doc.selection) && sel.type != "Control") {
var textRange = sel.createRange();
var preCaretTextRange = doc.body.createTextRange();
preCaretTextRange.moveToElementText(element);
preCaretTextRange.setEndPoint("EndToEnd", textRange);
caretOffset = preCaretTextRange.text.length;
}
return caretOffset;
}
function setCaretPosition(element, offset) {
var range = document.createRange();
var sel = window.getSelection();
var currentNode = null;
var previousNode = null;
for (var i = 0; i < element.childNodes.length; i++) {
previousNode = currentNode;
currentNode = element.childNodes[i];
while (currentNode.childNodes.length > 0) {
currentNode = currentNode.childNodes[0];
}
if (previousNode != null) {
offset -= previousNode.length;
}
if (offset <= currentNode.length) {
break;
}
}
if (currentNode != null) {
range.setStart(currentNode, offset);
range.collapse(true);
sel.removeAllRanges();
sel.addRange(range);
}
}
var textbox = $(".textbox");
$(document).on("input", textbox, function(event) {
var position = getCaretCharacterOffsetWithin(textbox.get(0));
var text = textbox.text();
text = text.replace(/(^|\s)(#[^\s]+)/ig, "$1<span style=\"color: blue;\">$2</span>");
textbox.html($.parseHTML(text));
setCaretPosition(textbox.get(0), position);
});
});
.textbox {
padding: 6px;
border: solid 1px #aaa;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.1.1/jquery.min.js"></script>
<div class="textbox" contenteditable="true"></div>

How can I keep text highlighting after pasting?

I have a small markdown editor
Demo
HTML:
<textarea id="txtarea">this is a **test**</textarea>
<button data-marker="**">Bold (toggle)</button>
<button data-marker="~~">Strike through (toggle)</button>
<button data-marker="*">Italics (toggle)</button>
JS:
function toggleMarker(marker, el) {
var markerLength = marker.length;
var startPos, endPos, selection, range;
if (document.selection != undefined) { // IE
el.focus();
range = document.selection.createRange();
selection = range.text;
} else if (el.selectionStart != undefined) { // Firefox
startPos = el.selectionStart;
endPos = el.selectionEnd;
selection = el.value.substring(startPos, endPos);
}
if (!selection.length){
return;
}
if (el.value.substring(startPos-markerLength,startPos) === marker
&& el.value.substring(endPos,endPos+markerLength) === marker
){
el.value = el.value.substring(0,startPos-markerLength) +
selection +
el.value.substring(endPos+markerLength);
}
else{
el.value = el.value.substring(0,startPos) + marker +
selection + marker + el.value.substring(endPos);
}
}
$(document).on('mousedown', 'button', function(e) {
toggleMarker( $(this).data('marker'), $('#txtarea').get(0) ).text;
});
Now I need to keep the highlighting of pasted text. How can I do that?
http://codepen.io/medinasod/pen/GoOrwq
JS:
var markerLength,
startPos,
endPos,
selection,
range;
function selectText(startPos, endPos, markerLength, marker) {
mytxtarea = document.getElementById("txtarea");
window.setTimeout(function() {
if (mytxtarea.value.substring(startPos-markerLength,startPos) === marker && mytxtarea.value.substring(endPos,endPos+markerLength) === marker) {
mytxtarea.setSelectionRange(startPos+markerLength, endPos+markerLength);
} else {
mytxtarea.setSelectionRange(startPos+markerLength, endPos+markerLength);
}
}, 0);
}
function toggleMarker(marker, el) {
markerLength = marker.length;
if (document.selection != undefined) { // IE
el.focus();
range = document.selection.createRange();
selection = range.text;
} else if (el.selectionStart != undefined) { // Firefox
startPos = el.selectionStart;
endPos = el.selectionEnd;
selection = el.value.substring(startPos, endPos);
}
if (!selection.length){
return;
}
if (el.value.substring(startPos-markerLength,startPos) === marker
&& el.value.substring(endPos,endPos+markerLength) === marker
){
el.value = el.value.substring(0,startPos-markerLength) +
selection +
el.value.substring(endPos+markerLength);
}
else{
el.value = el.value.substring(0,startPos) + marker +
selection + marker + el.value.substring(endPos);
}
selectText(startPos, endPos, markerLength, marker);
}
$(document).on('mousedown', 'button', function(e) {
toggleMarker( $(this).data('marker'), document.getElementById('txtarea'));
});
HTML:
<textarea id="txtarea">this is a test</textarea>
<button data-marker="**">Bold (toggle)</button>
<button data-marker="~~">Strike through (toggle)</button>
<button data-marker="*">Italics (toggle)</button>

Select a portion of text within a HTML element

I would like to create a function that select a given text inside a HTML element.
For example calling selectText('world') would select world in a markup like <span>Hello </span><strong>world</strong>!
Lots of answers on similar questions suggests to use range and selection but none of them work in my case (some would select all the text, some won't work with such markup, ...).
For now this is what I have (it doesn't work):
function selectText ( element, textToSelect ) {
var text = element.textContent,
start = text.indexOf( textToSelect ),
end = start + textToSelect.length - 1,
selection, range;
element.focus();
if( window.getSelection && document.createRange ) {
range = document.createRange();
range.setStart( element.firstChild, start );
range.setEnd( element.lastChild, end );
selection = window.getSelection();
selection.removeAllRanges();
selection.addRange( range );
} else if (document.body.createTextRange) {
range = document.body.createTextRange();
range.moveToElementText( element );
range.moveStart( 'character', start );
range.collapse( true );
range.moveEnd( 'character', end );
range.select();
}
}
Here is a jsfiddle so you see what is actually happening: http://jsfiddle.net/H2H2p/
Outputed error :
Uncaught IndexSizeError: Failed to execute 'setStart' on 'Range': The offset 11 is larger than or equal to the node's length (5).
P.S.: no jQuery please :)
You could use a combination of your approach of finding the text within the element's textContent and this function.
Demo: http://jsfiddle.net/H2H2p/3/
Code:
function selectText(element, textToSelect) {
var elementText;
if (typeof element.textContent == "string" && document.createRange && window.getSelection) {
elementText = element.textContent;
} else if (document.selection && document.body.createTextRange) {
var textRange = document.body.createTextRange();
textRange.moveToElement(element);
elementText = textRange.text;
}
var startIndex = elementText.indexOf(textToSelect);
setSelectionRange(element, startIndex, startIndex + textToSelect.length);
}
function getTextNodesIn(node) {
var textNodes = [];
if (node.nodeType == 3) {
textNodes.push(node);
} else {
var children = node.childNodes;
for (var i = 0, len = children.length; i < len; ++i) {
textNodes.push.apply(textNodes, getTextNodesIn(children[i]));
}
}
return textNodes;
}
function setSelectionRange(el, start, end) {
if (document.createRange && window.getSelection) {
var range = document.createRange();
range.selectNodeContents(el);
var textNodes = getTextNodesIn(el);
var foundStart = false;
var charCount = 0, endCharCount;
for (var i = 0, textNode; textNode = textNodes[i++]; ) {
endCharCount = charCount + textNode.length;
if (!foundStart && start >= charCount
&& (start < endCharCount ||
(start == endCharCount && i < textNodes.length))) {
range.setStart(textNode, start - charCount);
foundStart = true;
}
if (foundStart && end <= endCharCount) {
range.setEnd(textNode, end - charCount);
break;
}
charCount = endCharCount;
}
var sel = window.getSelection();
sel.removeAllRanges();
sel.addRange(range);
} else if (document.selection && document.body.createTextRange) {
var textRange = document.body.createTextRange();
textRange.moveToElementText(el);
textRange.collapse(true);
textRange.moveEnd("character", end);
textRange.moveStart("character", start);
textRange.select();
}
}

How to select text in IE?

I'm using the following function to get selected text and it works very well in all major browsers but it doesn't work correctly in IE before version 9!
function getSelected() {
var t = '';
if (window.getSelection) {
t = window.getSelection();
} else if (document.getSelection) {
t = document.getSelection();
t = t.toString();
} else if (document.selection) {
t = document.selection.createRange();
t = t.text;
}
return t;
}
var txt = getSelected();
The problem here that with IE before version 9 it doesn't store any text in the variable "txt"
DEMO: http://jsfiddle.net/ytJ35/
The below is taken from How to get selected html text with javascript?
this javascript function works in IE7 and above:
function getSelected() {
var text = "";
if (window.getSelection
&& window.getSelection().toString()
&& $(window.getSelection()).attr('type') != "Caret") {
text = window.getSelection();
return text;
}
else if (document.getSelection
&& document.getSelection().toString()
&& $(document.getSelection()).attr('type') != "Caret") {
text = document.getSelection();
return text;
}
else {
var selection = document.selection && document.selection.createRange();
if (!(typeof selection === "undefined")
&& selection.text
&& selection.text.toString()) {
text = selection.text;
return text;
}
}
return false;
}
Tested in chrome, IE10, IE6, IE7

Categories