CSS style quotes and their content [duplicate] - css

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

Sensible way to encode user defined sort-order in back end database?

I have a series of "rows" in a collection which are persisted to a nosql database (in this case Firestore). Each one of my rows has a sort order which is established when the user adds, inserts, copies or moves rows with the collection. The insertion point into which a user may insert new records is arbitrary. The sort order is persisted to the backend, and can be retrieved ordered by the sort order field. There may be a large number of rows in the collection, on the order 50K.
The question what is the sort order encoding format that would permit repeated insertion of new records between existing records, without having to occasionally rewrite the sort index of the entire collection to provide "space" in the sort order between adjacent records.
I'm guessing there may some standard way to achieve this, but not sure what it is.
Assume the alphabet is "abc". Then:
b, c, cb...
is a lexicographically sorted list that allows you to insert items anywhere:
ab, b, bb, c, cab, cb, cbb...
And the result is still a list that allows you to insert items anywhere:
aab, ab, ac, b, bab, bb, bc, c, caab, cab, cac, cb, cbab, cbb, cbbb...
The trick is to avoid having "a" as the last character of an item, so that you can always put items behind others.
Do this with 64 ASCII characters instead of 3.
I've been thinking about this for quite a few months. This is my progress so far in implementing it. It still has some flaws and it's a little bit of a mess, but I guess I'll clean it and upload it at npm when I find more time.
// Originally written in TypeScript, then removed the types for SO.
const alphabet = 'abc';
function getHigherAsciiChar(char) {
const index = alphabet.indexOf(char);
if (index === alphabet.length - 1) {
return ''; // sorry, there's no higher character
}
const nextIndex = Math.ceil((index + alphabet.length - 1) / 2);
return alphabet.charAt(nextIndex);
}
function getCharBetween(minChar, maxChar) {
if (minChar > maxChar) {
throw new Error('minChar > maxChar, ' + minChar + ' > ' + maxChar);
}
const minIndex = alphabet.indexOf(minChar);
const maxIndex = alphabet.indexOf(maxChar);
const nextIndex = Math.floor((minIndex + maxIndex) / 2);
if (nextIndex === minIndex) {
return ''; // there is no character between these two
}
return alphabet.charAt(nextIndex);
}
function getPaddedString(finalLength, string) {
let result = string;
while (result.length < finalLength) {
result += alphabet.charAt(0);
}
return result;
}
function getOrderString(bounds) {
const console = { log: () => {} }; // uncomment this to log debug stuff
if (!bounds.previous && !bounds.next) {
return getHigherAsciiChar(alphabet[0]);
}
const previousString = bounds.previous || '';
if (!bounds.next) {
const firstPreviousChars = previousString.substr(0, previousString.length - 1);
const lastPreviousChar = previousString.charAt(previousString.length - 1);
return firstPreviousChars + (
getHigherAsciiChar(lastPreviousChar) || (
lastPreviousChar + getHigherAsciiChar(alphabet.charAt(0))
)
);
}
const nextString = bounds.next;
console.log(`Searching between '${previousString}' and '${nextString}'...`);
const bigStringLength = Math.max(previousString.length, nextString.length);
const previous = getPaddedString(bigStringLength, previousString);
const next = getPaddedString(bigStringLength, nextString);
console.log(previous, next);
let result = '';
let i;
for (i = 0; i < bigStringLength; i++) {
const previousChar = previous.charAt(i);
const nextChar = next.charAt(i);
// keep adding common characters
if (previousChar === nextChar) {
result += previousChar;
console.log(result, 'common character');
continue;
}
// when different characters are reached, try to add a character between these two
const charBetween = getCharBetween(previousChar, nextChar);
if (charBetween) {
result += charBetween;
console.log(result, 'character in-between. RETURNING');
// and you're done
return result;
}
// if there was no character between these two (their distance was exactly 1),
// repeat the low character, forget about the upper bound and just try to get bigger than lower bound
result += previousChar;
console.log(result, 'the lower character so we can forget about high bound');
i++;
break;
}
for (; previousString >= result; i++) {
const previousChar = previous.charAt(i);
const higherChar = getHigherAsciiChar(previousChar);
if (higherChar) {
// you found a digit that makes your result greater than the lower bound. You're done.
result += higherChar;
console.log(result, 'a higher character. RETURING');
return result;
}
// the digits are still very close, can't find a digit in-between (yet)
result += previousChar;
console.log(result, 'moving on to next digit');
}
// so you end up depleting all the character slots from the lower bound. Meh, just add any character.
result += getHigherAsciiChar(alphabet.charAt(0));
console.log(result, 'meh, just add any character. RETURNING');
return result;
}
function interleaveTest(order) {
const newOrder = [];
newOrder.push(getOrderString({ next: order[0] }));
for (let i = 0; i < order.length - 1; i++) {
newOrder.push(order[i]);
newOrder.push(getOrderString({ previous: order[i], next: order[i + 1] }));
}
newOrder.push(order[order.length - 1]);
newOrder.push(getOrderString({ previous: order[order.length - 1] }));
return newOrder;
}
let order = ['c'];
console.log('\n' + order.join(', ') + '\n');
order = interleaveTest(order);
console.log('\n' + order.join(', ') + '\n');
order = interleaveTest(order);
console.log('\n' + order.join(', ') + '\n');
order = interleaveTest(order);
console.log('\n' + order.join(', ') + '\n');
let atEnd = ['b'];
for (let i = 0; i < 10; i++) {
atEnd.push(getOrderString({ previous: atEnd[atEnd.length - 1] }));
}
console.log('\nat end: ' + atEnd.join(', ') + '\n');

Is it possible to use a Kleene Operator for Flex Formatters?

is it possible to use a Kleene Operator (Kleene Star) for the Formatters?
I want to use a phoneFormatter, which puts a minus after the 5th number and afterwards it should be possible to have a variable number of numbers.
E.g.: 0172-555666999, 0160-44552 etc.
That is how I started, but I don't know which character belongs after the last hash (it is not a star, I already tried it ;-) ):
<fx:Declarations>
<mx:PhoneFormatter id="mPhoneFormat"
formatString="####-#"/>
</fx:Declarations>
The default PhoneFormatter expects the input string to have the same number of characters as the format string. They don't support regular expression patterns (like * to match the element zero or more times).
However, it's pretty easy to make your own formatter. To do this, I extended the PhoneFormatter class and overrode its format() method. I copied and pasted the original format() method and made the following modifications:
comment out the code that compared the length of the source string with the length of the format string
compare the length of the formatted string. If the original string is longer, append the remaining chars from the original string to the formatted string.
This probably won't handle all of your use cases, but it should be pretty straightforward to modify this to your needs.
package
{
import mx.formatters.PhoneFormatter;
import mx.formatters.SwitchSymbolFormatter;
public class CustomPhoneNumberFormatter extends PhoneFormatter
{
public function CustomPhoneNumberFormatter()
{
super();
}
override public function format(value:Object):String
{
// Reset any previous errors.
if (error)
error = null;
// --value--
if (!value || String(value).length == 0 || isNaN(Number(value)))
{
error = defaultInvalidValueError;
return "";
}
// --length--
var fStrLen:int = 0;
var letter:String;
var n:int;
var i:int;
n = formatString.length;
for (i = 0; i < n; i++)
{
letter = formatString.charAt(i);
if (letter == "#")
{
fStrLen++;
}
else if (validPatternChars.indexOf(letter) == -1)
{
error = defaultInvalidFormatError;
return "";
}
}
// if (String(value).length != fStrLen)
// {
// error = defaultInvalidValueError;
// return "";
// }
// --format--
var fStr:String = formatString;
if (fStrLen == 7 && areaCode != -1)
{
var aCodeLen:int = 0;
n = areaCodeFormat.length;
for (i = 0; i < n; i++)
{
if (areaCodeFormat.charAt(i) == "#")
aCodeLen++;
}
if (aCodeLen == 3 && String(areaCode).length == 3)
{
fStr = String(areaCodeFormat).concat(fStr);
value = String(areaCode).concat(value);
}
}
var dataFormatter:SwitchSymbolFormatter = new SwitchSymbolFormatter();
var source:String = String(value);
var returnValue:String = dataFormatter.formatValue(fStr, value);
if (source.length > returnValue.length)
{
returnValue = returnValue + source.substr(returnValue.length-1);
}
return returnValue;
}
}
}

TinyMCE removing valid tags

I'm using tinyMCE for small site, that is used by people to write simple articles. Usualy they write in MS word and copy text to tinyMCE and submit this.
That's why I only allowed few tags:
valid_elements: "a[href|target],strong/b,em/i,div[align],br,p[style|align],ul,li,ol,table,tr,td,iframe[*],img[*]",
But despite allowing img[*] after inserting image by 'Insert/edit image' only:
<img alt=""/>
appears in code. Same goes for iframe (which is complitly removed)
I've already tried every combination of valid_elements with full list of img and iframe attributes and with extended_valid_elements.
When I remove valid_elements clause everything works fine, but then word formatting which is not allowed (h1, h2, etc) is messing up styles.
TinyMCE version is 3.4.2.
I am using the paste_preprocess setting with the tinymce paste plugin and i filter out unwanted tags there. Here is an example:
in your tinymce init:
paste_preprocess : function(pl, o) {
//if(console) console.log('Object', o);
//if(console) console.log('Content:', o.content);
// usage param1 = the string to strip out tags from, param2 = tags to keep in the string
o.content = ir.im.strip_tags( o.content,'<p><div><br><br/>' );
},
Help function to strip out tags:
strip_tags = function (str, allowed_tags) {
var key = '', allowed = false;
var matches = []; var allowed_array = [];
var allowed_tag = '';
var i = 0;
var k = '';
var html = '';
var replacer = function (search, replace, str) {
return str.split(search).join(replace);
};
// Build allowes tags associative array
if (allowed_tags) {
allowed_array = allowed_tags.match(/([a-zA-Z0-9]+)/gi);
}
str += '';
// Match tags
matches = str.match(/(<\/?[\S][^>]*>)/gi);
// Go through all HTML tags
for (key in matches) {
if (isNaN(key)) {
// IE7 Hack
continue; }
// Save HTML tag
html = matches[key].toString();
// Is tag not in allowed list? Remove from str!
allowed = false;
// Go through all allowed tags
for (k in allowed_array) { // Init
allowed_tag = allowed_array[k];
i = -1;
if (i != 0) { i = html.toLowerCase().indexOf('<'+allowed_tag+'>');}
if (i != 0) { i = html.toLowerCase().indexOf('<'+allowed_tag+' ');}
if (i != 0) { i = html.toLowerCase().indexOf('</'+allowed_tag) ;}
// Determine
if (i == 0) { allowed = true;
break;
}
}
if (!allowed) {
str = replacer(html, "", str); // Custom replace. No regexing
}
}
return str;
};

Flex AS3: ProgressBar doesn't move

I am a little stuck and need some advice/help.
I have a progress bar:
<mx:ProgressBar id="appProgress" mode="manual" width="300" label="{appProgressMsg}" minimum="0" maximum="100"/>
I have two listener functions, one sets the progress, and one sets the appProgressMsg:
public function incProgress(e:TEvent):void {
var p:uint = Math.floor(e.data.number / e.data.total * 100);
trace("Setting Perc." + p);
appProgress.setProgress(p, 100);
}
public function setApplicationProgressStep(e:TEvent):void {
trace("Setting step:" + e.data);
appProgressMsg = e.data;
}
I want to reuse this progress bar alot. And not necessarily for ProgressEvents, but when going through steps.
For instance, I loop over a bunch of database inserts, and want to undate the progress etc.
Here is a sample:
public function updateDatabase(result:Object):void {
var total:int = 0;
var i:int = 0;
var r:SQLResult;
trace("updateDatabase called.");
for each (var table:XML in this.queries.elements("table")) {
var key:String = table.attribute("name");
if (result[key]) {
send(TEvent.UpdateApplicationProgressStep, "Updating " + key);
i = 1;
total = result[key].length;
for each (var row:Object in result[key]) {
//now, we need to see if we already have this record.
send(TEvent.UpdateApplicationProgress, { number:i, total: total } );
r = this.query("select * from " + key + " where server_id = '" + row.id + "'");
if (r.data == null) {
//there is no entry with this id, make one.
this.query(table.insert, row);
} else {
//it exists, so let's update.
this.update(key, row);
}
i++;
}
}
}
}
Everything works fine.
That is, the listener functions are called and I get trace output like:
updateDatabase called.
Setting step:Updating project
Setting Perc 25
Setting Perc 50
Setting Perc 75
Setting Perc 100
The issue is, only the very last percent and step is shown. that is, when it's all done, the progress bar jumps to 100% and shows the last step label.
Does anyone know why this is?
Thanks in advance for any help,
Jason
The new code, which works awesomely I might add:
public function updateDatabase(result:Object, eindex:int = 0, sindex:int = 0 ):void {
var total:int = 0;
var i:int = 0;
var j:int;
var r:SQLResult;
var table:XML;
var key:String;
var elems:XMLList = this.queries.elements("table");
var startTime:int = getTimer();
var row:Object;
for (i = eindex; i < elems.length(); i++) {
table = elems[i];
key = table.attribute("name");
if (!result[key])
continue;
total = result[key].length;
send(TEvent.UpdateApplicationProgressStep, "Updating " + key);
for (j = sindex; j < result[key].length; j++) {
if (getTimer() - startTime > 100) {
setTimeout(updateDatabase, 100, result, i, j);
send(TEvent.UpdateApplicationProgress, { number:j, total: total } );
return;
}
row = result[key][j];
r = this.query("select * from " + key + " where server_id = '" + row.id + "'");
if (r.data == null) {
//there is no entry with this id, make one.
this.query(table.insert, row,false);
} else {
//it exists, so let's update.
this.update(key, row,false);
}
}
send(TEvent.UpdateApplicationProgress, { number:1, total: 1 } );
}
}
Flash is single threaded. The display will not update until your function returns. For this reason, you should never have any code that runs for longer than about 100ms (1/10th of a second), otherwise the UI (or even the entire browser) will appear to be locked up.
The general solution is to split up your work over multiple frames, here is some pseudo-code:
function doWork(arg1:Obj, arg2:Obj, start:int=0) {
var startTime = getTimer(); // store starting time
for(i=start; i<length; i++) {
if(getTimer() - startTime > 100) { // see if we've been working too long
trace("Current progress: "+(i/length * 100)+"%");
updateProgress( i / length );
setTimeout(doWork, 100, arg1, arg2, i); // schedule more work to be done
return; // stop current loop
}
trace("Working on item "+i);
// processing here
}
trace("All work done");
}
doWork(data1, data2); // start the work
Your pseudo-code works for updating the progress bar however in my case my "work" was copying of files from DVD to the appStorageDirectory which seem to reintroduce the same issue that your work around resolved - the progress bar now does not update
Here is my hack of your solution
function doWork(arg1:int, arg2:int, start:int=0) {
var startTime = getTimer(); // store starting time
for(var i:int=start; i<arg2; i++) {
if(getTimer() - startTime > 100 ) { // see if we've been working too long
trace("Current progress: "+(i/arg2 * 100)+"%");
setTimeout(doWork, 100, i, arg2, i); // schedule more work to be done
return; // stop current loop
}
trace("Working on item "+i);
dispatchEvent(new progressMadeEvent("incrementChange",i,arg2))
var dir:String = copyRes[i].nativePath.toString().split(OSSep).pop()
copyRes[i].copyTo(appStore.resolvePath(dir)) // copies dir from DVD to appStorageDir
}
trace("All work done");
}

How to get ClientID of a TreeNode in a TreeView?

How to get ClientID of a TreeNode in a TreeView based on one of its rendered attributes,
for example, its title attribute (In my case it's unique)
,using either Server-Side or Client-Side code?
I go with this code, but it doesn't work, any suggestion?
// Retrieves TreeNode ClientID.
function GetTreeNodeID(nodeTitle)
{
var treeNodes = document.getElementById('tvMenu').childNodes;
var treeLinks;
for(var i=0 ; i<treeNodes.length ; i++)
{
treeLinks = treeNodes[i].getElementsByTagName('a');
for(var j=0 ; j<treeLinks.length ; j++)
{
if(nodeTitle == treeLinks[j].title && treeLinks[j].title != "");
{
alert("Par: " + nodeTitle);
alert("Title: " + treeLinks[j].title);
return treeLinks[j].id;
}
}
}
}
The above code that is mentioned with the question always returns the id of root node, any suggestion?
innerText or innerHtml or textContent ? Wich browser do you use ?
function GetTreeNodeID(nodeInnerText)
{
var tree = document.getElementById('tvMenu');
var treeLinks = tree.getElementsByTagName('A');
for(var element in treeLinks )
{
if((nodeInnerText == treeLinks[element].innerText) && (treeLinks[element].innerText != ""))
{
alert("Par: " + nodeInnerText);
alert("innerText: " + treeLinks[element].title);
return treeLinks[element].id;
}
}
}
Look here for a sample code.

Resources