if ($("#canvas").css('background-image') == 'url(images/endOfGame.jpg)') {
does not work. But this does:
var element = document.getElementById('canvas');
var style = window.getComputedStyle(element);
var imagex = style.getPropertyValue('background-image');
console.log(imagex);
if (imagex === "url(file:///C:/Users/Jack/Documents/myGames/Pong/images/endOfGame.jpg)") {
and this does not:
var element = document.getElementById('canvas');
var style = window.getComputedStyle(element);
var imagex = style.getPropertyValue('background-image');
console.log(imagex);
if (imagex === "url(images/endOfGame.jpg)") {
why? I have to change the full file path code for every computer i run my game on. Not good.
Thanks.
You could use indexOf which returns the character position of the found text (0 and above) or -1 if not found:
if (imagex.indexOf("url(images/endOfGame.jpg)") >= 0) {
// yes, string contains that text
}
I would prefer:
if (imagex.indexOf("images/endOfGame.jpg") >= 0) {
// yes, string contains that text
}
ignoring url(..). The following version ignores differences in case (upper or lower):
if (imagex.toUpperCase().indexOf("images/endOfGame.jpg".toUpperCase()) >= 0) {
// yes, string contains that text
}
Related
I need some function That will count rows ( i know that on stackoverflow there are more than hundreds of these questions) but in my case i need to count them even when there is no end of line (mean "/n") because typical function is
textarea.value.substr(0, textarea.selectionStart).split("\n").length;.
It means that if user overflows max length of the row but he doesn't use "enter" and the text is on "new line".
Well, i dont know how to describe it better, so there is a example on fiddle https://jsfiddle.net/fNPvf/12872/
try to write long sentence with no spaces, enters etc.. and you will see where the problem is
what i really don't want is css rule nowrap, overflow-x etc..
Here you go.
/** #type {HTMLTextAreaElement} */
var _buffer;
/**
* Returns the number of lines in a textarea, including wrapped lines.
*
* __NOTE__:
* [textarea] should have an integer line height to avoid rounding errors.
*/
function countLines(textarea) {
if (_buffer == null) {
_buffer = document.createElement('textarea');
_buffer.style.border = 'none';
_buffer.style.height = '0';
_buffer.style.overflow = 'hidden';
_buffer.style.padding = '0';
_buffer.style.position = 'absolute';
_buffer.style.left = '0';
_buffer.style.top = '0';
_buffer.style.zIndex = '-1';
document.body.appendChild(_buffer);
}
var cs = window.getComputedStyle(textarea);
var pl = parseInt(cs.paddingLeft);
var pr = parseInt(cs.paddingRight);
var lh = parseInt(cs.lineHeight);
// [cs.lineHeight] may return 'normal', which means line height = font size.
if (isNaN(lh)) lh = parseInt(cs.fontSize);
// Copy content width.
_buffer.style.width = (textarea.clientWidth - pl - pr) + 'px';
// Copy text properties.
_buffer.style.font = cs.font;
_buffer.style.letterSpacing = cs.letterSpacing;
_buffer.style.whiteSpace = cs.whiteSpace;
_buffer.style.wordBreak = cs.wordBreak;
_buffer.style.wordSpacing = cs.wordSpacing;
_buffer.style.wordWrap = cs.wordWrap;
// Copy value.
_buffer.value = textarea.value;
var result = Math.floor(_buffer.scrollHeight / lh);
if (result == 0) result = 1;
return result;
}
Demo here
If I'm understanding the problem correctly, you need to count two things.
The number of hard line breaks "\n".
The number of lines that wrap past X characters.
Here's some pseudo code:
var lineLengthLimit = 40;
var lineCounter = 0;
foreach(lines as line){
lineCounter+=Math.floor(line.length/lineLengthLimit);
}
lineCounter += lines.length;
Another option might be what this guy suggested: https://stackoverflow.com/a/3697249/1207539 but it seems a bit sketchy.
I'm trying to make the colour different for certain letters (if found) in a string eg. the letter i. The search count is working I just can't figure out the changing html colour of the individual letter.
I know if it was a whole word then I could just use split strings, but can't figure out how to do it for a single letter. I've found some examples, one that I have tried is at the bottom that is not working either.
//getMsg is another function, which passes in a user inputted string
function searchMsg(getMsg) {
alert (getMsg);
var msgBoxObject = document.getElementById('msgBox');
var pos = getMsg.indexOf('i')
var txtToFind = (document.getElementById('txtToFind').value);
var count = 0;
while (pos !== -1){
count++;
pos = getMsg.indexOf('i', pos + 1);
document.writeln (+count);
msgBoxObject.innerHTML = (count);
}
getMsg = getMsg.replace('/i/g<span class="red">i</span>');
document.writeln (getMsg);
}
Edit; I've added in this, but can't get the loop to work correctly so it displays all instances of the letter found instead of just one: /*while (pos !== -1){
count++;
pos = getMsg.indexOf('i', pos + 1);
document.writeln (+count);
msgBoxObject.innerHTML = (count);
}
*/
var count = 0; // Count of target value
var i = 0; // Iterative counter
// Examine each element.
for(i=0; i<arr.length; i++)
{ if(arr[i] == targetValue)
count++;
}
return count;
}
searchIndex = txtMsg.indexOf(txtToFind);
if (searchIndex >=0 ) {
// Copy text from phrase up till the match.
matchPhrase = txtMsg.slice(0, searchIndex);
matchPhrase += '<font color="red">' + txtToFind + '</font>';
matchPhrase += txtMsg.slice(searchIndex + txtToFind.length);
} else {
matchPhrase = "No matches"
}
displayProcessedMsg(matchPhrase);
document.writeln(matchPhrase);
You either need to add the corresponding css for that class or change the tag like #john_Smith specified
Adding the CSS
span.red {
color: red;
}
Changing the tag
On your code replace this
getMsg = getMsg.replace('/i/g<span class="red">i</span>');
for
getMsg = getMsg.replace('/i/g<span style:"color:red">i</span>');
Some example of inline css
Some advice on color palettes
Try looking into d3 color scales(https://github.com/mbostock/d3/wiki/Ordinal-Scales#categorical-colors) or apply a principle similar to incrementing an RGB value instead of using names of colors.
Hope this helps.
I'm attempting to write a RaphaelJS function that will take existing text nodes within a Raphael paper instance and convert them into paths.
The goal is to replicate the position, size and attribute of the text exactly as it appears on the page, but have it rendered using paths instead of text. I cannot initially render the text using the Raphael paper.print() function because the text is updated dynamically and requires "text" based attributes to do so. Converting existing text nodes to paths will occur as the "final" step in the process (after the text modifications are complete).
I am doing this to eliminate the need for having fonts installed to view or handle the SVG later.
The challenges I face are:
Text nodes may include tspans with x and dy definitions. The paths created must line it perfectly witch each of the childNode letters (tspans).
Retrieving the actual position data of text node, and each tspan. This is where I'm having trouble and hopefully someone with more experience can assist me. Since stroke widths and other attributes affect the positioning/bbox values, I'm not sure what's the most efficient method of obtaining the correct positioning data for the text.
What I have tried so far:
A simple breakdown of my code.
I wrote a custom attribute function, textFormat, that formats the text in a staggered formation. This function parses the text node, splits it by each letter adding a new line \n character, and adjusts the positioning to look staggered.
The textToPaths function is a paper function that is supposed to loop through the paper nodes, and convert all found text nodes into path using the Raphael paper.print() function. This is the function I am having trouble with.
View the Complete JSFiddle Example Here
The problem code
I'm not sure how to obtain accurate and consistent x and y values to pass into the paper.print() function. Right now, I am using getBoundingClientRect() but it's still off and skewed. My assumption is the stroke widths are affecting the x and y calculations.
//Loop through each tspan and print the path for each.
var i,
children = node.node.childNodes,
len = children.length;
for (i = 0; i < len; i++) {
var tspan = children[i],
tspanText = tspan.innerHTML,
x = tspan.getBoundingClientRect().left - node.node.getBoundingClientRect().left, //How do I get the correct x value?
y = tspan.getBoundingClientRect().top - node.node.getBoundingClientRect().top; //How do I get the correcy y value?
var path = paper.print(x, y, tspanText, font, fontSize),
attrs = node.attrs;
delete attrs.x;
delete attrs.y;
path.attr(attrs);
path.attr('fill', '#ff0000'); //Red, for testing purposes.
}
Complete Code View the JSFiddle Example
//Register Cufon Font
var paper = Raphael(document.getElementById('paper'), '600', '600');
var text1 = paper.text(100, 100, 'abc').attr({fill: 'none',stroke: '#000000',"stroke-width": '12',"stroke-miterlimit": '1',"font-family" : "Lobster", "font-size": '30px','stroke-opacity': '1'});
var text2 = paper.text(100, 100, 'abc').attr({fill: 'none',stroke: '#ffffff',"stroke-width": '8',"stroke-miterlimit": '1',"font-family" : "Lobster", "font-size": '30px','stroke-opacity': '1'});
var text3 = paper.text(100, 100, 'abc').attr({fill: '#000000',stroke: '#ffffff',"stroke-width": '0',"stroke-miterlimit": '1',"font-family" : "Lobster", "font-size": '30px','stroke-opacity': '1'});
var text = paper.set(text1, text2, text3);
text.attr('textFormat', 'stagger');
/* paper.textToPaths
* Description: Converts all text nodes to paths within a paper.
*
* Example: paper.textToPaths();
*/
(function(R) {
R.fn.textToPaths = function() {
var paper = this;
//Loop all nodes in the paper.
for (var node = paper.bottom; node != null; node = node.next ) {
if ( node.node.style.display === 'none' || node.type !== "text" || node.attrs.opacity == "0") continue; //skip non-text and hidden nodes.
//Get the font config for this text node.
var text = node.attr('text'),
fontFamily = node.attr('font-family'),
fontSize = parseInt(node.attr('font-size')),
fontWeight = node.attr('font-weight'),
font = paper.getFont(fontFamily, fontWeight);
//Loop through each tspan and print the path for each.
var i,
children = node.node.childNodes,
len = children.length;
for (i = 0; i < len; i++) {
var tspan = children[i],
tspanText = tspan.innerHTML,
x = tspan.getBoundingClientRect().left - node.node.getBoundingClientRect().left, //How do I get the correct x value?
y = tspan.getBoundingClientRect().top - node.node.getBoundingClientRect().top; //How do I get the correcy y value?
var path = paper.print(x, y, tspanText, font, fontSize),
attrs = node.attrs;
delete attrs.x;
delete attrs.y;
path.attr(attrs);
path.attr('fill', '#ff0000'); //Red, for testing purposes.
}
}
};
})(window.Raphael);
textToPaths = function() {
//Run textToPaths
paper.textToPaths();
};
/* Custom Element Attribute: textFormat
* Description: Formats a text element to either staggered or normal text.
*
* Example: element.attr('textFormat, 'stagger');
*/
paper.customAttributes.textFormat = function( value ) {
// Sets the SVG dy attribute, which Raphael doesn't control
var selector = Raphael.svg ? 'tspan' : 'v:textpath',
has = "hasOwnProperty",
$node = $(this.node),
text = $node.text(),
$tspans = $node.find(selector);
console.log('format');
switch(value)
{
case 'stagger' :
var stagger = function(el) {
var R = Raphael,
letters = '',
newline = '\n';
for (var c=0; c < text.length; c++) {
var letter = text[c],
append = '';
if(c < text.length - 1)
append = newline;
letters += letter+append;
}
el.attr('text', letters);
var children = el.node.childNodes;
var i,
a = el.attrs,
node = el.node,
len = children.length,
letterOffset = 0,
tspan,
tspanHeight,
tspanWidth,
tspanX,
prevTspan,
prevTspanRight = 0,
tspanDiff = 0,
tspanTemp,
fontSize,
leading = 1.2,
tempText;
for (i = 0; i < len; i++) {
tspan = children[i];
tspanHeight = tspan.getComputedTextLength();
tspanWidth = tspan.getComputedTextLength();
tspanX = tspan.getAttribute('x'),
prevTspanRight = tspan.getBoundingClientRect().right
if(tspanX !== null)
{
tspanDiff = tspanDiff + prevTspanRight - tspan.getBoundingClientRect().left;
var setX = parseInt(tspanX) + parseInt(tspanDiff);
tspan.setAttribute('x', setX);
tspan.setAttribute('dy', 15);
}
prevTspan = tspan;
}
}
stagger(this);
break;
case 'normal' :
this.attr('text', text);
break;
default :
this.attr('text', text);
break;
}
eve("raphael.attr.textFormat." + this.id, this, value);
// change no default Raphael attributes
return {};
};
staggerText = function() {
//Run textToPaths
text.attr('textFormat', 'stagger');
};
If anyone can help me solve this problem I would greatly appreciate it. Thanks!
You can convert fonts to SVG/Canvas path commands using Opentype.js.
The lib will return to you a series of path drawing commands; these are intended for drawing on an HTML5 <canvas> element.
However it is trivial to build an SVG path with those commands since the font-conversion does not include any commands that are compatible with Canvas path drawing that would be incompatible with an SVG path command.
I'm having the following issue - I'm trying to select the text inside spans located across multiple divs. To give an example
<div>asd<span>fgh</span></div>
<div><span>qwerty</span></div>
<div><span>uio</span>asd</div>
Now in this scenario, if the user clicks somewhere inside the word qwerty I'd like to select the text 'fghqwertuio' --> all the adjacent spans. I'm using the following code to do this:
var range = document.caretRangeFromPoint(lastTappedX, lastTappedY);
range.selectNodeContents(range.startContainer);
window.getSelection().addRange(range);
var containerNodes = document.body.children[0].children;
var whichChild = -1;
for ( var i = 0; i < containerNodes.length; ++i) {
if (containerNodes[i] === range.startContainer.parentNode.parentNode) {
whichChild = i;
break;
}
}
if (whichChild === -1) {
console.log("couldn't find the highlighted div");
}
// go right the dom tree
for ( var i = whichChild + 1; i < containerNodes.length; ++i) {
var containerChildren = containerNodes[i].children;
if (containerChildren[0]
&& containerChildren[0].style['background-color']) {
var newRange = document.createRange();
newRange.selectNodeContents(containerChildren[0]);
window.getSelection().addRange(newRange);
}
if (containerChildren.length > 1) {
break;
}
}
// go left the down tree
for ( var i = whichChild - 1; i >= 0; --i) {
var containerChildren = containerNodes[i].children;
if (containerChildren[containerChildren.length - 1].style['background-color']) {
var newRange = document.createRange();
newRange
.selectNodeContents(containerChildren[containerChildren.length - 1]);
window.getSelection().addRange(newRange);
}
if (containerChildren.length > 1) {
break;
}
}
When I log what happens - I'm correctly creating ranges containing the text I'd like to select but adding them to the selection object doesn't seem to work. The current selection is only the first added range. Any help on how to solve this will be greatly appreciated.
Of the major browsers, only Firefox allows multiple ranges per selection. In all other browsers you're limited to one range.
You need to tweak your code to create one range and use the range's setStart() and setEnd() methods. Also, properties of the style property of elements use camel case rather than hyphens (i.e. .backgroundColor rather than ['background-color']).
I'm trying to take a text string, (e.g. the word "testing") and calculating if the string, when displayed on screen, will exceed a certain width. I do this by putting it in a element and using width().
Now, the thing is I want to reduce the text string by a character at a time, and then determining the width. If the width is within say "130px", then i'll return that truncated string. I have the following recursive function, but I'm quite new to Js/jQuery and am unsure what I did wrong. If the string is determined to require truncating, the function returns undefined.
Any help is greatly appreciated. Thanks a lot!
function fitWidth(str) {
var width = $('span.width').html(str).width();
if (width > '130') {
strlength = str.length-1;
str = str.substr(0,strlength);
fitWidth(str);
} else {
return str; // something wrong here?
}
}
var testStr = 'Lorem Ipsum';
alert(fitWidth(testStr)); // returns undefined if it was truncated
if str requires truncating, fitWidth() will return "undefined"
You need to say return fitWidth(str); in the recursive case.
// Here is one other method,
// but you'll need to translate it to jquery-
function truncate(str, w, pa){
if(!pa) pa= document.body;
w= w || pa.offsetWidth;
var wid, t, L= str.length, c= document.createElement('div');
c.style.cssText=
'position:absolute; visibility:hidden; font:inherit; white-space:pre';
t= document.createTextNode(str);
c.appendChild(t);
pa.appendChild(c);
wid= c.offsetWidth;
if(w< wid){
L= Math.floor(L*(w/wid))-1;
t.data= str= str.substring(0, L);
while(t.data && c.offsetWidth> w){
t.data= str= str.slice(0, -1);
}
}
pa.removeChild(c);
return str;
}
var testStr= 'The style is white-space:pre to keep it on one line';
alert(truncate(testStr, 130));
//comments
function truncate(str, w, pa){
// create a hidden element to measure the text-
// pass a parent element (pa) if the target's font family,
// weight or size is not the same as body text
// or if you use the parent's width to size the text
if(!pa) pa= document.body;
w= w || pa.offsetWidth;
var wid, t, L= str.length, c= document.createElement('div');
c.style.cssText=
'position:absolute;visibility:hidden;font:inherit;white-space:pre';
t= document.createTextNode(str);
c.appendChild(t);
pa.appendChild(c);
wid= c.offsetWidth;
// measure the width of the whole string
// if it fits your width, skip the calculations,
// otherwise multiply the length of the string by
// the width you want divided by the actual width
if(w< wid){
L= Math.floor(L*(w/wid))-1;
//I'd subtract 1 to grow on and call it str.substring(0, L);
//but if the exact to the pixel width is critical,
//measure the new string and chop it if it still needs it:
t.data= str= str.substring(0, L);
while(t.data && c.offsetWidth> w){
t.data= str= str.slice(0, -1);
}
}
// clean up, and return the (possibly) shortened string
pa.removeChild(c);
return str;
}
I modified the OP code to make it work for me.
This uses jQuery and requires you to add this function (endsWith) to the String prototype. See https://stackoverflow.com/a/2548133/80353.
function fitWidth(str, selector, widthLimit, heightLimit, suffix, original) {
if (typeof heightLimit == 'undefined') {
heightLimit = 10000000000;
}
if (typeof suffix == 'undefined') {
suffix = '';
}
if (typeof original == 'undefined') {
original = str;
}
if (suffix.length > 0 && str.endsWith(suffix)) {
str = str.substr(0, str.length - suffix.length);
}
var width = $(selector).html(str).width();
var height = $(selector).html(str).height();
if (suffix.length > 0 && original != str) {
var width = $(selector).html(str + suffix).width();
var height = $(selector).html(str + suffix).height();
}
// console.info($(selector));
// console.info(width);
if (width > widthLimit || height > heightLimit) {
strlength = str.length-1;
str = str.substr(0,strlength);
$(selector).html(str + suffix);
fitWidth(str + suffix, selector, widthLimit, heightLimit, suffix, original);
} else {
return true;
}
}
// if you want to consider suffix for shortened content like "I want to ..."
fixWidth("I want to break free", "#selector", 180, 60, " ...");
Hope this will help others.
New Answer:
I don't know if this would be faster but it does work.
function fitWidth(str) {
var width = $('span.width').css('display', 'inline').html(str).width();
var charWidth = Math.round(width / str.length);
var maxLength = 130 / charWidth;
$('span.width').html(str.substr(0, maxLength));
}
Old Answer:
instead of doing this recursively why don't you just use substr() to make the width 130.
like so:
function fitWidth(str) {
var width = $('span.width').html(str).width();
if(width > 130) {
return str.substr(0, 130);
} else {
return str;
}
}
EDIT:
oh, i see now that you are talking about width vs the length of the characters in the string. Still, if you take the width of a font being used i think you could still avoid doing multiple steps.