Set CSS of words enclosed in double quotes - javascript

This is a follow up question to my question about Setting the CSS of code if it contains a reserved word.
What I am trying to do: If some code has quotes or double quotes, I want to set the color of the font to red and bold. Ex. System.out.println( "Hello world" ); should set "Hello world" to red.
What's wrong: Despite my best efforts, I can't seem to get my control statements to work properly (at least I think that's the issue). It sets the first double quote and beyond to red, but when I tell it to stop when a word equals anyword" or anyword' it sets the rest of the code in the block to red.
HTML
<html>
<body>
<code id="java">
public static void main(String[] args)<br>
{
<pre> int i = 120; </pre><br>
<pre> // Displays a message in the console </pre>
<pre> // This is a test </pre>
<pre> System.out.println( "Hello Big World!" );</pre>
}
</code>
</body>
</html>
CSS
.quotes
{
font-weight: bold;
color: #E01B1B;
}
jQuery
$(document).ready(function() {
var code = $("#java").html(); // Get the code
var split = code.split(' '); // Split up each element
var chkQ = 0; // Check for quotes
var chkC = 0; // Check until end of comment line
// Set the CSS of reserved words, digits, strings, and comments
for (var j = 0; j < split.length; j++) {
// Check to see if chkQ is set to true
if (chkQ == 1) {
// If the element matches (anyword") or (anyword'), then set
// flag to false and continue checking the rest of the code.
// Else, continue setting the CSS to .quotes
if (split[j].match(/."/) || split[j].match(/.'/)) {
split[j] = '<span class="quotes">' + split[j] + '</span>';
chkQ = 0;
} else {
split[j] = '<span class="quotes">' + split[j] + '</span>';
}
}
...
} else if (chkQ == 0 && chkC == 0) {
...
// If the element matches a ("anyword) or ('anyword)...
} else if (split[j].match(/"./) || split[j].match(/'./)) {
split[j] = '<span class="quotes">' + split[j] + '</span>';
chkQ = 1;
} ...
}
}
// Join all the split up elements back together!
$("#java").html(split.join(' '));
});
Question: Is this just simply an issue with my regex, control blocks or something completely different?

Why split the string up when you can perform a simple global regex find and replace:
<script type="text/javascript">
$(document).ready(function(){
//cache the element
el = $('#java');
//get the HTML contained within the cached element
code = el.html();
//return the code having executed the replace method, regex explained:
/*
([^\w]{1}) -> look for a single character that is not an alpha character
(["']) -> then look for either a single quote or double quote
(.*?) -> then look any character, but don't be greedy
(\2) -> then look for what was found in the second group - " or '
([^\w]{1}) -> and finally look for a single character that is not an alpha character
*/
code = code.replace(/([^\w]{1})(["'])(.*?)(\2)([^\w]{1})/gm,
//execute an anonymous callback, passing in the result for every match found
function(match, $1, $2, $3, $4, $5, offset, original) {
//construct the replacement
str = $1 + '<span class="quotes">' + $2 + $3 + $4 + '</span>' + $5;
//return the replacement
return str;
});
//replace the existing HTML within the cached element
el.html(code);
});
</script>
Edit: Just updated it to accommodate nested quotes.

I don't know all your requirements, but it seems that your single quote could get a bit complicated.
I've set up a demonstration that works (updated link to include nested quotes).
I do not guarantee it is bug free. It does the replacement in two stages, first for double quotes, then for single, trying to weed out potential apostrophes (note in the code below the filters for apostrophes are based off common following letters--not sure how many you might practically need, if any).
Javascript
$(document).ready(function() {
var code = $("#java").html(); // Get the code
var split = code.split('\"'); // Split up each element at the "
// Set the CSS of reserved words, digits, strings, and comments
for (var j = 0; j < split.length - 1; j++) {
if (j%2 == 0) { //if first, add beginning
split[j] = split[j] + '<span class="quotes">"';
} else {//if second, add ending
split[j] = split[j] + '"</span>';
}
}
// Join all the split up elements back together!
$("#java").html(split.join(""));
code = $("#java").html(); // Get the code
split = code.split('\''); // Split up each element at the '
var openQ = 1;
var sub1;
var sub2;
for (var j = 0; j < split.length - 1; j++) {
sub1 = split[j+1].substr(0,2); //checking for a contraction of 's
sub2 = split[j+1].substr(0,3); //checking for a contraction of 'll
if(sub1 != "s " && sub2 != "ll ") {
if (openQ) { //if first, add beginning
split[j] = split[j] + '<span class="quotes">\'';
openQ = 0;
} else {//if second, add ending
split[j] = split[j] + '\'</span>';
openQ = 1;
}
}
else {//add apostrophe back
split[j] = split[j] + '\'';
}
}
$("#java").html(split.join(""));
});

Here's a pure JavaScript version:
id= id of element with quotes
classid= class to add to the quotes
function quotes(id,classid) {
var code = document.getElementById(id).innerHTML;
var split = code.split('\"');
for (var j = 0; j < split.length - 1; j++) {
if (j%2 == 0) {
split[j] = split[j] + '<span class='+classid+'>"';
} else {
split[j] = split[j] + '"</span>';
}
}
document.getElementById(id).innerHTML = split.join("");
code = document.getElementById(id).innerHTML;
split = code.split('\'');
var openQ = 1;
var sub1;
var sub2;
for (var j = 0; j < split.length - 1; j++) {
sub1 = split[j+1].substr(0,2);
sub2 = split[j+1].substr(0,3);
if(sub1 != "s " && sub2 != "ll ") {
if (openQ) {
split[j] = split[j] + '<span class='+classid+'>\'';
openQ = 0;
} else {
split[j] = split[j] + '\'</span>';
openQ = 1;
}
}
else {
split[j] = split[j] + '\'';
}
}
document.getElementById(id).innerHTML = split.join("");
}

String.prototype.Text2Html = function (){
var div = document.createElement('div');
div.appendChild(document.createTextNode(this))
encoded=div.innerHTML;
div.remove();
return encoded
}
String.prototype.colorTheQuotes = function(){
var re = /(?:<span style=|)(?:(?:"[^"]*")|(?:'[^']*'))/gm,
text = this.Text2Html(),
output = text,
tour = 0,
slen = 27;
while ((match = re.exec(text)) != null) {
if(match[0].startsWith("<span")) continue
output=output.slice(0,match.index+tour*slen)+'<span class="quote">'+output.slice(match.index+tour*slen,match.index+match[0].length+tour*slen)+"</span>"+output.slice(match.index+match[0].length+tour*slen);tour++
}
return output
}
element=document.getElementById("color")
document.addEventListener("readystatechange",(e)=>{
element.innerHTML=element.innerText.colorTheQuotes();
})
.quote{
color: red;
}
<span>System.out.println( "Hello world" );</span><br>
<span id="color">System.out.println( "Hello world" );</span>

Related

Regex: Match a word that is not surrounded by other characters and is also not in an HTML tag

I have no idea what is wrong with my code. For some reason, it matches only phrases that start with the letter s, when it is supposed to match a specific word not surrounded by other word characters, and that is not in an HTML tag. Here is my code:
<!DOCTYPE html>
<html>
<!--
YOUR WINNER REGEX IS...
(?![A-Za-z0-9\-])(?![^<]*>)
Your word before this
-->
<head>
<title>Edititing Tool</title>
<style>
#content {
width: 100%;
height: 100%;
}
</style>
</head>
<body>
<h1>EDITING TOOL</h1>
<h3>Paste in text and it SHOULD highlight each word in red</h3>
<div id="content" contenteditable onkeyup="contentchange()"></div>
<script>
var content = document.getElementById("content");//This is what you type into
var words, newText;
function contentchange() {
//Set variables
var contentText = content.innerText.split(/\s/g);//split what's inside the div by all whitespace (tabs, spaces, etc.)
newText = contentText;//make a new variable for that
words = {};
for (var i = 0; i < newText.length; i++) {
//make it all lowercase
newText[i] = newText[i].toLowerCase();
//Remove double-quotes
newText[i] = newText[i].replace('"', "");
//Remove other punctuation except for single quotes (for contractions) and dashes
//The reason this regex is so long is because of google drive and unicode. it really just means /[^\w\s'’-]/g
newText[i] = newText[i].replace(/(?:[\0-\x08\x0E-\x1F!-&\(-,\.\/:-#\[-\^`\{-\x9F\xA1-\u167F\u1681-\u1FFF\u200B-\u2018\u201A-\u2027\u202A-\u202E\u2030-\u205E\u2060-\u2FFF\u3001-\uD7FF\uE000-\uFEFE\uFF00-\uFFFF]|[\uD800-\uDBFF][\uDC00-\uDFFF]|[\uD800-\uDBFF](?![\uDC00-\uDFFF])|(?:[^\uD800-\uDBFF]|^)[\uDC00-\uDFFF])/g,"");
}
//Remove all empty strings
newText = without(newText, "");
//Index everything; Make a variable holding how many times the word is used for every word
for (var i = 0; i < newText.length; i++) {
if (words[newText[i]]) {
words[newText[i]]++;
} else {
words[newText[i]] = 1;
}
}
var contentHTML = decodeHtml(content.innerHTML);//Take away all (because HTML does that)
//Get how many total words you have
var totalWords = 0;
for (var i = 0; i < Object.keys(words).length; i++) {
totalWords += words[Object.keys(words)[i]];
}
for (var i = 0; i < Object.keys(words).length; i++) {
var currentword = Object.keys(words)[i];
contentHTML = contentHTML.replace(new RegExp("(^|\s)("+currentword+")(?![A-Za-z\-])(?![^<]*>)", "ig"), function myFunction(x){return "<span style='color: red'>" + x + "</span>"; console.log("BOOBS LALALALA" + x);});
console.log(currentword);
console.log(words);
console.log(i);
}
content.innerHTML = contentHTML;
}
//Copied from stackoverflow, removes a certain string
function without(array, what) {
return array.filter(function(element) {
return element !== what;
});
}
//Change "September" to "[Ss][Ee][Pp][Tt][Ee][Mm][Bb][Ee][Rr]"
function anycaseRegex(string) {
var returnVal = "";
for (var j = 0; j < string.length; j++) {
returnVal += "[" + string.substring(j, j+1).toUpperCase() + string.substring(j, j+1).toLowerCase() + "]";
}
return returnVal;
}
//Because HTML does that
function decodeHtml(html) {
var txt = document.createElement("textarea");
txt.innerHTML = html;
return txt.value;
}
//PHP REGEX: (?<![A-Za-z0-9\-])[Hh][Ee][Ll][Ll][Oo](?![A-Za-z0-9\-])(?![^<]*>)
</script>
</body>
</html>
Here are a couple of regular expressions that I've used:
/[Hh][Ee][Ll][Ll][Oo](?![A-Za-z\-])(?![^<]*>)/g -- matches hello and ahello, when it should only match hello

innerHtml.replace replaces tags and not just text

I currently try to replace certain words from an utterance with a <span>WORD</span element. I get the start and end indices from the words from the backend. An utterance can have several words which will be replaced. So I just iterate over the array of words to replaced and try to replace the words in the utterance.
However, I tried doing it with innerHtml.replace(word, <span>word</span>). The problem here is, that when for example the second word is 'sp' the sp of the tag of the first word is replaced instead of the word 'sp' in my utterance.
I then tried to replace the word my putting in the start and end index of the word (which I thought would solve the problem). But it seems like if the start and end index points on 'sp' it still replaces the part of span and not the word I wanted to replace.
So, I am looking for a way to replace only words in an utterance and not the HTML elements with the <span>WORD</span>.
My Code right now:
var label = document.getElementById('match-text' + index);
label.innerHTML = label.innerHTML.replace(label.innerText.substring(entity.start, entity.end),'<span ng-click="showEntityDetailsPage($event, ' + index + ', ' + entityIndex + ')" style="background-color:'
+ color + '; border: 3px solid ' + borderColor + ';" class="highlighted-entities">' + label.innerText.substring(entity.start, entity.end) + '</span>');
$compile(label)($scope);
Further to my comment, here is how I would implement that. In this example the anchor tag's html attribute contains "oo" and so does the tfoot tag, and they do not get changed.
function replaceText(obj, search, wrapElement, elementClass) {
switch (obj.nodeType) {
case 3: // it's a text node
if (obj.nodeValue.indexOf(search) > -1) {
var bits = obj.nodeValue.split(search);
var nextSib = obj.nextSibling;
obj.nodeValue = bits[0];
for (var i=1; i < bits.length; i++) {
var el = document.createElement(wrapElement);
el.className = elementClass;
el.innerHTML = search;
var tn = document.createTextNode(bits[i]);
if (nextSib) {
obj.parentNode.insertBefore(tn, nextSib);
obj.parentNode.insertBefore(el, tn);
} else {
obj.parentNode.appendChild(tn);
obj.parentNode.insertBefore(el, tn);
}
}
return 2*(bits.length-1);
}
break;
default: // it's not a text node
for (var i=0; i < obj.childNodes.length; i++) {
var j = replaceText(obj.childNodes[i], search, wrapElement, elementClass);
if (j) i+=j;
}
break;
}
}
replaceText(document.getElementById('starthere'), "oo", "span", "myspanclass");
.myspanclass {
color: red;
}
<div id='starthere'>Noonhour
<span>Boondoggle
Google
</span>
The Moon is a Balloon
<table><tfoot><tr><th>oops!</th></tr></tfoot></table>
</div>

Javascript/ActionScript do for each indexOf?

The screenshot below the code shows me issue. It only rainbowfies one instance of text.
How can I get this code to do each [rainbow]specified[/rainbow] text?
It's actually for ActionScript but it works in Javascript too so I've been testing on http://jsfiddle.net
var txt = "This is a [rainbow]test to show that I can[/rainbow] make whatever I want [rainbow]appear as a rainbow[/rainbow] because I am [rainbow]awesome[/rainbow].";
if ((txt.indexOf("[rainbow]") > -1) && (txt.indexOf("[/rainbow]") > -1)) {
var firstChar = txt.indexOf("[rainbow]") + 9;
var lastChar = txt.indexOf("[/rainbow]");
var RAINBOWTEXT = '';
var i = firstChar;
while (i < lastChar) {
RAINBOWTEXT += txt.charAt(i);
i++
}
var text = RAINBOWTEXT;
var texty = '';
colors = new Array('ff00ff', 'ff00cc', 'ff0099', 'ff0066', 'ff0033', 'ff0000', 'ff3300', 'ff6600', 'ff9900', 'ffcc00', 'ffff00', 'ccff00', '99ff00', '66ff00', '33ff00', '00ff00', '00ff33', '00ff66', '00ff99', '00ffcc', '00ffff', '00ccff', '0099ff', '0066ff', '0033ff', '0000ff', '3300ff', '6600ff', '9900ff', 'cc00ff');
var i = 0;
while (i <= text.length) {
var t = text.charAt(i);
if (t != undefined) {
texty += "<font color=\"#" + colors[i % colors.length] + "\">" + t + "</font>";
i++;
}
}
texty = texty.replace("> <", "> <");
var REPLACEME = "[rainbow]" + RAINBOWTEXT + "[/rainbow]";
txt = txt.replace(REPLACEME, texty);
document.write(txt);
}​
If we can make assumptions about no interleaving or nesting of [rainbow] tags, I'd just use a regular expression with a simple replacer callback:
var rainbowified = txt.replace(/\[rainbow\](.*?)\[\/rainbow\]/, function(textWithTags, textBetweenTheseTags) {
var text = textBetweenTheseTags;
....
for(var i = 0; i < text.length; ++i) {
// rainbowify each letter of text...
}
...
return textWithFontTags;
}
You can use this to get a new string with the transformation you want.
Also, the font tag is depricated; you should use span with color:#XXXXXX in the style attribute.
var colors = [
'f0f', 'f0c', 'f09', 'f06', 'f03', 'f00', 'f30', 'f60', 'f90', 'fc0',
'ff0', 'cf0', '9f0', '6f0', '3f0', '0f0', '0f3', '0f6', '0f9', '0fc',
'0ff', '0cf', '09f', '06f', '03f', '00f', '30f', '60f', '90f', 'c0f'
];
function rainbowify(text) {
return text.replace(/\[rainbow\](.*)\[\/rainbow\]/g, function(_, inner){
return inner.replace(/./g, function(ch, i){
return '<span style="color:#' + colors[i % colors.length] + ';">' + ch + '</span>';
});
})
}
Here's how I'd do it.
Make it a loop. .indexOf can take a starting point as the second parameter, so with starting the next iteration at lastChar+10 should work.
Apart from that, it might be easier to do it fully with regex and .replace:
return txt.replace(/\[rainbow\](.+?)\[\/rainbow\]/g, function(all, match, index, str) {
return createRGBrainbowArray(match.length).map(function(color, i) {
return '<span style="color:#'+color+'">'+match[i]+'</span>';
}).join("");
});
function createRGBrainbowArray(l) {
// should return an Array of length l with hexadecimal color strings,
// representing a nice rainbow
}

Long string pagination done right

I want to split a long text into smaller chunks, that will act as pages.
var longText = document.getElementById('content').innerHTML;
for (i=0; i<10; i++) {
var page = longText.substring(i*100,(i+1)*100);
document.write(page + "<br /><hr />");
}
See it here on jsfiddle.
This code splits the text, but in a stupid way, cutting also words in half.
It would be far better, for example, creating substrings ending at the last space in a certain number of characters (count 100 characters, then go back to the last space).
How would you achieve it?
Second shot
Third shot
I would use:
function paginate(longText, pageSize) {
var parts = longText.split(/[ \n\t]/g);
if (parts.length == 0)
return [];
var pages = [parts.unshift()];
for (var i = 0; i < parts.length; i += 1) {
var part = parts[i];
if (part.length + pages[pages.length - 1].length < pageSize) {
pages[pages.length - 1] += " " + part;
} else {
pages.push(part);
}
}
return parts;
}
For those looking for a working answer:
<div id="long-text">Lorem ipsum [...]</div>
<script>
var splitter = function(id) {
var longText = document.getElementById(id).innerHTML;
var pageLenght = 200;
var charsDone = 0;
var htmlBefore = "<p>";
var htmlAfter = "</p>";
while (charsDone <= longText.length && (pageLenght+charsDone)<longText.length) {
var pageBox = longText.substr(lastSpace,pageLenght);
var lastSpace = charsDone + pageBox.lastIndexOf(" ");
var page = longText.substring(charsDone,lastSpace);
document.write(htmlBefore + page + htmlAfter);
charsDone = lastSpace;
}
document.write(longText.substr(lastSpace,pageLenght));
}
splitter("#long-text");
You can easily use arrays instead of writing to document.
You will also want to set your html to your needs, do it in htmlBefore and htmlAfter.
See it in action here.

how can i break the line in JOptionpane message box

<script language="javascript">
<![CDATA[
importPackage(javax.swing);
importPackage(java.lang);
System.out.println("Hello from JavaScript!");
var optionPane = JOptionPane.showMessageDialog(null,'Deployment instruction = ' + Deployment_Instrution);
]]>
</script>
here Deployment_Instruction is variable in which i am storing the output of sql select query. the output of sql select query length is too much big so the size of JOptionpane message box is also going bigger. for this i want to break the big line in message box.
how can i do this.pls help[ me out asap.
thanks in advance....
I guess you'll have to break the line by inserting newlines where appropriate. For a simple application like this it might do to just have a basic function that breaks on spaces once a line reaches the maximum length that you want.
Something like:
var boxText = wrapLines( 30, Deployment_Instruction );
JOptionPane.showMessageDialog( null, boxText );
Here the maximum length would be 30 characters. With the wrapLines function being:
function wrapLines(max, text)
{
max--;
text = "" + text;
var newText = "";
var lineLength = 0;
for (var i = 0; i < text.length; i++)
{
var c = text.substring(i, i+1);
if (c == '\n')
{
newText += c;
lineLength = 1;
}
else if (c == ' ' && lineLength >= max)
{
newText += '\n';
lineLength = 1;
}
else
{
newText += c;
lineLength++;
}
}
return (newText);
}
Note that this will give a 'ragged' right edge, so if there is a very long word at the end of a line it may not be satisfactory.
By the way your variable name is missing a letter 'c' - Instru?tion.

Categories

Resources