I try to create a system replacement for ToolTip.
I already create a version but its not quite optimal (search a better way to do it)
here's a fiddle : http://jsfiddle.net/forX/Lwgrug24/
I create a dictionary (array[key]->value). the array is order by length of the key.
each key is a word or an expression, the value is the definition of the expression.
So, I replace the expression by a span (could/should be a div). The span is used for the tooltip (I use the data-title as tooltip text).
because some word is reused in expression, I need to remove expression already with tooltip (in real life think of father/grandfather, you dont want the definition of father inside grandfather). For replacement I use a ramdom value. That's the worst of this code.
You could make comment or post a new way to do it. maybe someone already did it.
Clarification :
I think my way to do it is wrong by using a string for replacement. Or it could be more secure. How should I do it?
html :
<div class="container">
one two three four five six seven eight nine ten
</div>
javascript :
$(function() {
var list = [
{'k':'one two three four five','v':'First five number.'},
{'k':'four five six seven','v':'middle number.'},
{'k':'six seven eight','v':'second middle number.'},
{'k':'two','v':'number two.'},
{'k':'six','v':'number six.'},
{'k':'ten','v':'number ten.'}
];
$(".container").each(function(){
var replacement = new Array();
for (i = 0; i < list.length; i++) {
var val = list[i];
var rString = randomString(32, '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ');
replacement[rString + "_k"] = htmlEncode(val["k"]);
replacement[rString + "_v"] = htmlEncode(val["v"]);
var re = new RegExp("(" + val["k"] + ")","g");
$(":contains('" + val["k"] + "')",$(this).parent()).html(function(_, html) {
var newItem = '<span class="itemWithDescription" '
+ 'data-title="' + rString + "_v" + '">'
+ rString + "_k"
+ '</span>';
return html.replace(re, newItem);
});
}
for (var k in replacement){
$(this).html($(this).html().replace(k,replacement[k]));
console.log("Key is " + k + ", value is : " + replacement[k]);
}
});
$(document).tooltip({
items:'.itemWithDescription',
tooltipClass:'Tip',
content: function(){
var title = $(this).attr("data-title");
if (title == ""){
title = $(this).attr("title"); //custom tooltips
}
return title;
}
});
});
function randomString(length, chars) {
var result = '';
for (var i = length; i > 0; --i) result += chars[Math.round(Math.random() * (chars.length - 1))];
return result;
}
function htmlEncode(value){
//create a in-memory div, set it's inner text(which jQuery automatically encodes)
//then grab the encoded contents back out. The div never exists on the page.
return $('<div/>').text(value).html();
}
I added a little thing. on the random function, I put a | and } for every char, its bigger but there's not much chance to have a conflic with an expression.
for (var i = length; i > 0; --i) result += '|' + ( chars[Math.round(Math.random() * (chars.length - 1))] ) + '}' ;
http://jsfiddle.net/forX/Lwgrug24/3/
Related
I have a string csv containing PORTCODE and latitude longitude of location. I use these values to plot the marker on google map.
Eg csv string:
ANC|61.2181:149.9003,
ANC|61.2181:149.9003,
TLK|62.3209:150.1066,
DNL|63.1148:151.1926,
DNL|63.1148:151.1926,
DNL|63.1148:151.1926,
TLK|62.3209:150.1066,
TLK|62.3209:150.1066,
ALE|60.9543:149.1599
i want to autonumber SIMILAR PORTCODE sequence separated with pipe symbol '|' for the PORTCODE which are EXACT Consecutive next element.
Required out put:
ANC|61.2181:149.9003:1|2,
TLK|62.3209:150.1066:3,
DNL|63.1148:151.1926:4|5|6,
TLK|62.3209:150.1066:7|8,
ALE|60.9543:149.1599:9
Any solution using jquery/javascript/c# ?
There's probably a neater/shorter way to do this, but here's the first second way that came to mind using JavaScript:
var input = "ANC|61.2181:149.9003,\nANC|61.2181:149.9003,\nTLK|62.3209:150.1066,\nDNL|63.1148:151.1926,\nDNL|63.1148:151.1926,\nDNL|63.1148:151.1926,\nTLK|62.3209:150.1066,\nTLK|62.3209:150.1066,\nALE|60.9543:149.1599";
var output = input.split(",\n").reduce(function(p,c,i,a) {
if (i === 1) p += ":1";
return p + (c === a[i-1] ? "|" : ",\n" + c + ":") + (i+1);
});
console.log(output);
I've assumed each line ends with a single \n character, but obviously you can adjust for \r or whatever.
Further reading:
the string .split() method
the array .reduce() method
You can make something like that.
var csv = 'ANC|61.2181:149.9003,\nANC|61.2181:149.9003,\nTLK|62.3209:150.1066,\nDNL|63.1148:151.1926,\nDNL|63.1148:151.1926,\nDNL|63.1148:151.1926,\nTLK|62.3209:150.1066,\nTLK|62.3209:150.1066,\nALE|60.9543:149.1599'.split(',\n');
//count entry :
var results = [];
var j = -1;
var previous = "";
for (i = 0; i < csv.length; i++) {
if (previous === csv[i]) {
results [j] += '|' + (i+1);
} else {
j += 1;
previous = csv[i];
results [j] = csv[i] + ':' + (i+1);
}
}
//And reforme your csv
console.log(results.join(',\n'));
Further reading:
the string .split() method
the array .join() method
here is C# way to do,
public string[] data = { "ANC|61.2181:149.9003", "ANC|61.2181:149.9003", "TLK|62.3209:150.1066", "DNL|63.1148:151.1926", "DNL|63.1148:151.1926", "TLK|62.3209:150.1066", "TLK|62.3209:150.1066", "ALE|60.9543:149.1599", "DNL|63.1148:151.1926" };
int counter = 0;
var output = data.Select(x => new Tuple<string, int>(x, counter++))
.GroupBy(x => x.Item1)
.Select(h => h.Key + ":"+ string.Join("|", h.Select(x => x.Item2)));
output would be ANC|61.2181:149.9003:0|1,TLK|62.3209:150.1066:2|5|6,DNL|63.1148:151.1926:3|4|8,ALE|60.9543:149.1599:7
Basically i'm creating a script to display the place value for set of numbers. Here's my script:
var arrn = '3252';
var temp = 0;
var q = arrn.length;
var j = 0;
for (var i = q-1; i >= 0; i--,j++) {
if (j!=0) temp = temp + ' + ';
{
temp += arrn[i] * Math.pow(10, j);
}
}
alert(temp);
My goal is to achieve 3000 + 200 + 50 + 2. But i get 2 + 50 + 200 + 3000. I tried temp.reverse() & sort functions but doesn't seem to work. Please help
Change
if(j!=0)temp=temp +' + ';
{
temp +=arrn[i]*Math.pow(10,j);
}
to
if(j!=0) {
temp=' + ' + temp;
}
temp = arrn[i]*Math.pow(10,j) + temp;
Live Example
Side note: Your braces in the first code block above are very misleading. What you have:
if(j!=0)temp=temp +' + ';
{
temp +=arrn[i]*Math.pow(10,j);
}
is
if(j!=0)temp=temp +' + ';
temp +=arrn[i]*Math.pow(10,j);
which is to say
if(j!=0) {
temp=temp +' + ';
}
temp +=arrn[i]*Math.pow(10,j);
the block in your version is not associated with the if, it's just a freestanding block.
Side note #2: Since you're using temp as a string everywhere else, I would initialize it with '' rather than with 0. Example The reason your string didn't end up with an extraneous 0 was really quite obscure. :-)
Just add the number to the beginning of the string instead of at the end:
for (var i = q - 1; i >= 0; i--, j++) {
if (j != 0) {
temp = ' + ' + temp;
}
temp = arrn[i] * Math.pow(10, j) + temp;
}
Demo: http://jsfiddle.net/Guffa/rh9oso3f/
Side note: You are using some confusing brackets in your code after the if statement. As there is a statement following the if statement, the brackets starting on the next line becomes just a code block, but it's easy to think that it's supposed to be the code that is executed when the condition in the if statement is true.
Another side note: the language attribute for the script tag was deprecated many years ago. Use type="text/javascript" if you want to specify the language.
How about temp.split("+").reverse().join(" + ")?
You can do this way. I know it can be optimised. But it works
var arrn='3252';
var temp=0;
var q=arrn.length;
var res = [];
var j=0;
for(var i=q-1;i>=0;i--,j++)
{
temp += parseFloat(arrn[i])*Math.pow(10,j);
res.push(arrn[i]*Math.pow(10,j));
}
res.reverse();
alert(res.join('+') + " = " + temp);
http://jsfiddle.net/he7p8y5m/
var arrn='3252';
var temp=new Array();
var q=arrn.length;
for(var i=0;i<=q-1; i++){
temp.push(arrn[i]*Math.pow(10,(q-i-1)));
}
temp = temp.join('+');
alert(temp);
I have a bunch of text with no HTML, and I'm trying to find all replace all instances of String with <span id="x">String</span>
The catch is I'm trying to increment x every time to get a bunch of uniquely identifiable spans rather then identical ones.
I have no problem getting all instances of String, but for the life of me I can't get the increment to work. All help I can find seems to be directed towards doing the opposite of this.
Any ideas what I can do or where else to turn for help?
EDIT:
This is targeting a div with ID 'result' that contains only text.
var target = "String";
var X = //the number I was trying to increment
var re = new RegExp(" " + target + " ","g");
document.getElementById('result').innerHTML = document.getElementById('result').innerHTML.replace(re, '<span id="' + X + '">' + target + '</span>');
I'm guessing you are using a regex, which is fine, but you can specify a function as the second parameter to replace and do your logic there.
The MDN documentation for doing this is here - https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/String/replace#Specifying_a_function_as_a_parameter
You could use something like this:
http://jsfiddle.net/gjrCN/2/
function replacer(orig, target) {
var x = 0;
var re = new RegExp(target, "g");
var ret = orig.replace(re, function (match) {
return "<span id='" + (++x) + "'>" + match + "</span>";
});
return ret;
}
var example = "String Stringasdf String2344 String";
var replaced = replacer(example, "String");
console.log(replaced);
You can change ++x to x++ if you want the counting to start at 0 instead of 1.
With reference to these docs.
You can pass a function to the String.replace method would allow you to increment a counter with each call and use that to set your ID:
var forReplacements = "I do like a String that's a nice long String with Strings in it";
var incrementer = (function() {
var counter = -1;
var fn = function(match) {
counter++;
return "<span id='"+counter+"'>"+match+"</span>";
};
fn.reset = function() {
counter = -1;
}
return fn;
}());
var newString = forReplacements.replace(/String/g, incrementer )
See this fiddle to see it in action
I'm currently implementing a substring search. From the algorithm, I get array of substrings occurence positions where each element is in the form of [startPos, endPos].
For example (in javascript array):
[[1,3], [8,10], [15,18]]
And the string to highlight is:
ACGATCGATCGGATCGAGCGATCGAGCGATCGAT
I want to highlight (in HTML using <b>) the original string, so it will highlight or bold the string from position 1 to 3, then 8 to 10, then 15 to 18, etc (0-indexed).
A<b>CGA</b>TCGA<b>TCG</b>GATC<b>GAGC</b>GATCGAGCGATCGAT
This is what I have tried (JavaScript):
function hilightAtPositions(text, posArray) {
var startPos, endPos;
var startTag = "<b>";
var endTag = "</b>";
var hilightedText = "";
for (var i = 0; i < posArray.length; i++) {
startPos = posArray[i][0];
endPos = posArray[i][1];
hilightedText = [text.slice(0, startPos), startTag, text.slice(startPos, endPos), endTag, text.slice(endPos)].join('');
}
return hilightedText;
}
But it highlights just a range from the posArray (and I know it is still incorrect yet). So, how can I highlight a string given multiple occurrences position?
Looking at this question, and following John3136's suggestion of going from tail to head, you could do:
String.prototype.splice = function( idx, rem, s ) {
return (this.slice(0,idx) + s + this.slice(idx + Math.abs(rem)));
};
function hilightAtPositions(text, posArray) {
var startPos, endPos;
posArray = posArray.sort(function(a,b){ return a[0] - b[0];});
for (var i = posArray.length-1; i >= 0; i--) {
startPos = posArray[i][0];
endPos = posArray[i][1];
text= text.splice(endPos, 0, "</b>");
text= text.splice(startPos, 0, "<b>");
}
return text;
}
Note that in your code, you are overwriting hilightedText with each iteration, losing your changes.
Try this:
var stringToHighlight = "ACGATCGATCGGATCGAGCGATCGAGCGATCGAT";
var highlightPositions = [[1,3], [8,10], [15,18]];
var lengthDelta = 0;
for (var highlight in highlightPositions) {
var start = highlightPositions[highlight][0] + lengthDelta;
var end = highlightPositions[highlight][1] + lengthDelta;
var first = stringToHighlight.substring(0, start);
var second = stringToHighlight.substring(start, end + 1);
var third = stringToHighlight.substring(end + 1);
stringToHighlight = first + "<b>" + second + "</b>" + third;
lengthDelta += ("<b></b>").length;
}
alert(stringToHighlight);
Demo: http://jsfiddle.net/kPkk3/
Assuming that you're trying to highlight search terms or something like that. Why not replace the term with the bolding?
example:
term: abc
var text = 'abcdefgabcqq';
var term = 'abc';
text.replace(term, '<b>' + term + '</b>');
This would allow you to avoid worrying about positions, assuming that you are trying to highlight a specific string.
Assuming your list of segments is ordered from lowest start to highest, try doing your array from last to first.
That way you are not changing parts of the string you haven't reached yet.
Just change the loop to:
for (var i = posArray.length-1; i >=0; i--) {
If you want to check for multiple string matches and highlight them, this code snippet works.
function highlightMatch(text, matchString) {
let textArr = text.split(' ');
let returnArr = [];
for(let i=0; i<textArr.length; i++) {
let subStrMatch = textArr[i].toLowerCase().indexOf(matchString.toLowerCase());
if(subStrMatch !== -1) {
let subStr = textArr[i].split('');
let subStrReturn = [];
for(let j=0 ;j<subStr.length; j++) {
if(j === subStrMatch) {
subStrReturn.push('<strong>' + subStr[j]);
} else if (j === subStrMatch + (matchString.length-1)){
subStrReturn.push(subStr[j] + '<strong>');
} else {
subStrReturn.push(subStr[j]);
}
}
returnArr.push(subStrReturn.join(''));
} else {
returnArr.push(textArr[i]);
}
}
return returnArr;
}
highlightMatch('Multi Test returns multiple results', 'multi');
=> (5) ['<strong>Multi<strong>', 'Test', 'returns', '<strong>multi<strong>ple', 'results']
I'm wondering if there's a way to count the words inside a div for example. Say we have a div like so:
<div id="content">
hello how are you?
</div>
Then have the JS function return an integer of 4.
Is this possible? I have done this with form elements but can't seem to do it for non-form ones.
Any ideas?
g
If you know that the DIV is only going to have text in it, you can KISS:
var count = document.getElementById('content').innerHTML.split(' ').length;
If the div can have HTML tags in it, you're going to have to traverse its children looking for text nodes:
function get_text(el) {
ret = "";
var length = el.childNodes.length;
for(var i = 0; i < length; i++) {
var node = el.childNodes[i];
if(node.nodeType != 8) {
ret += node.nodeType != 1 ? node.nodeValue : get_text(node);
}
}
return ret;
}
var words = get_text(document.getElementById('content'));
var count = words.split(' ').length;
This is the same logic that the jQuery library uses to achieve the effect of its text() function. jQuery is a pretty awesome library that in this case is not necessary. However, if you find yourself doing a lot of DOM manipulation or AJAX then you might want to check it out.
EDIT:
As noted by Gumbo in the comments, the way we are splitting the strings above would count two consecutive spaces as a word. If you expect that sort of thing (and even if you don't) it's probably best to avoid it by splitting on a regular expression instead of on a simple space character. Keeping that in mind, instead of doing the above split, you should do something like this:
var count = words.split(/\s+/).length;
The only difference being on what we're passing to the split function.
Paolo Bergantino's second solution is incorrect for empty strings or strings that begin or end with whitespaces. Here's the fix:
var count = !s ? 0 : (s.split(/^\s+$/).length === 2 ? 0 : 2 +
s.split(/\s+/).length - s.split(/^\s+/).length - s.split(/\s+$/).length);
Explanation: If the string is empty, there are zero words; If the string has only whitespaces, there are zero words; Else, count the number of whitespace groups without the ones from the beginning and the end of the string.
string_var.match(/[^\s]+/g).length
seems like it's a better method than
string_var.split(/\s+/).length
At least it won't count "word " as 2 words -- ['word'] rather than ['word', '']. And it doesn't really require any funny add-on logic.
Or just use Countable.js to do the hard job ;)
document.deepText= function(hoo){
var A= [];
if(hoo){
hoo= hoo.firstChild;
while(hoo!= null){
if(hoo.nodeType== 3){
A[A.length]= hoo.data;
}
else A= A.concat(arguments.callee(hoo));
hoo= hoo.nextSibling;
}
}
return A;
}
I'd be fairly strict about what a word is-
function countwords(hoo){
var text= document.deepText(hoo).join(' ');
return text.match(/[A-Za-z\'\-]+/g).length;
}
alert(countwords(document.body))
Or you can do this:
function CountWords (this_field, show_word_count, show_char_count) {
if (show_word_count == null) {
show_word_count = true;
}
if (show_char_count == null) {
show_char_count = false;
}
var char_count = this_field.value.length;
var fullStr = this_field.value + " ";
var initial_whitespace_rExp = /^[^A-Za-z0-9]+/gi;
var left_trimmedStr = fullStr.replace(initial_whitespace_rExp, "");
var non_alphanumerics_rExp = rExp = /[^A-Za-z0-9]+/gi;
var cleanedStr = left_trimmedStr.replace(non_alphanumerics_rExp, " ");
var splitString = cleanedStr.split(" ");
var word_count = splitString.length -1;
if (fullStr.length <2) {
word_count = 0;
}
if (word_count == 1) {
wordOrWords = " word";
} else {
wordOrWords = " words";
}
if (char_count == 1) {
charOrChars = " character";
} else {
charOrChars = " characters";
}
if (show_word_count & show_char_count) {
alert ("Word Count:\n" + " " + word_count + wordOrWords + "\n" + " " + char_count + charOrChars);
} else {
if (show_word_count) {
alert ("Word Count: " + word_count + wordOrWords);
} else {
if (show_char_count) {
alert ("Character Count: " + char_count + charOrChars);
}
}
}
return word_count;
}
The get_text function in Paolo Bergantino's answer didn't work properly for me when two child nodes have no space between them. eg <h1>heading</h1><p>paragraph</p> would be returned as headingparagraph (notice lack of space between the words). So prepending a space to the nodeValue fixes this. But it introduces a space at the front of the text but I found a word count function that trims it off (plus it uses several regexps to ensure it counts words only). Word count and edited get_text functions below:
function get_text(el) {
ret = "";
var length = el.childNodes.length;
for(var i = 0; i < length; i++) {
var node = el.childNodes[i];
if(node.nodeType != 8) {
ret += node.nodeType != 1 ? ' '+node.nodeValue : get_text(node);
}
}
return ret;
}
function wordCount(fullStr) {
if (fullStr.length == 0) {
return 0;
} else {
fullStr = fullStr.replace(/\r+/g, " ");
fullStr = fullStr.replace(/\n+/g, " ");
fullStr = fullStr.replace(/[^A-Za-z0-9 ]+/gi, "");
fullStr = fullStr.replace(/^\s+/, "");
fullStr = fullStr.replace(/\s+$/, "");
fullStr = fullStr.replace(/\s+/gi, " ");
var splitString = fullStr.split(" ");
return splitString.length;
}
}
EDIT
kennebec's word counter is really good. But the one I've found includes a number as a word which is what I needed. Still, that's easy to add to kennebec's. But kennebec's text retrieval function will have the same problem.
This should account for preceding & trailing whitespaces
const wordCount = document.querySelector('#content').innerText.trim().split(/\s+/).length;
string_var.match(/[^\s]+/g).length - 1;