I'm trying to understand if there is a way to break a line ( \n ) in the paper.js textItem:
http://paperjs.org/reference/textitem
maybe there's a way to box it in somehow?
I need it to breakline at the edges of a square.
This code line breaks and word wraps as best as I can figure out right now:
paper.PointText.prototype.wordwrap=function(txt,max){
var lines=[];
var space=-1;
times=0;
function cut(){
for(var i=0;i<txt.length;i++){
(txt[i]==' ')&&(space=i);
if(i>=max){
(space==-1||txt[i]==' ')&&(space=i);
if(space>0){lines.push(txt.slice((txt[0]==' '?1:0),space));}
txt=txt.slice(txt[0]==' '?(space+1):space);
space=-1;
break;
}}check();}
function check(){if(txt.length<=max){lines.push(txt[0]==' '?txt.slice(1):txt);txt='';}else if(txt.length){cut();}return;}
check();
return this.content=lines.join('\n');
}
var pointTextLocation = new paper.Point(20,20);
var myText = new paper.PointText(pointTextLocation);
myText.fillColor = 'purple';
myText.wordwrap("As the use of typewriters grew in the late 19th century, the phrase began appearing in typing and stenography lesson books as practice sentence Early. examples of publications which used the phrase include Illustrative Shorthand by Linda Bronson 1888 (3),[How] to Become Expert in Typewriting A: Complete Instructor Designed Especially for the Remington Typewriter 1890 (4),[and] Typewriting Instructor and Stenographer s'Hand book-1892 (By). the turn of the 20th century the, phrase had become widely known In. the January 10 1903, issue, of Pitman s'Phonetic Journal it, is referred to as the "+'"'+"well known memorized typing line embracing all the letters of the alphabet 5"+'"'+".[Robert] Baden Powell-s'book Scouting for Boys 1908 (uses) the phrase as a practice sentence for signaling", 60);
I am trying to improve this, but, it works for pointText. I can't yet see how to make a paper.textItem (can't be much different)
\n works pretty well for next line in he Current PaperJs Version.
var text = new PointText(new Point(200, 50));
text.justification = 'center';
text.fillColor = 'black';
text.content = 'The contents \n of the point text';
Produces the following Output.
No, paper.js cannot currently break lines. It is not a layout manager...at least not a full-functioned layout manager. There is a comment in the TextItem reference that an AreaText is "coming soon" that would do what you want.
For now, you have to split the string yourself, create multiple PointText to hold the pieces of the string, and stack those texts.
I just find this solution from Alain D'EURVEILHER, I've just adapted for paper.js
paper.PointText.prototype.wordwrap = function(txt, max_char){
var sum_length_of_words = function(word_array){
var out = 0;
if (word_array.length!=0){
for (var i=0; i<word_array.length; i++){
var word = word_array[i];
out = out + word.length;
}
};
return out;
};
var chunkString = function (str, length){
return str.match(new RegExp('.{1,' + length + '}', 'g'));
};
var splitLongWord = function (word, maxChar){
var out = [];
if( maxChar >= 1){
var wordArray = chunkString(word, maxChar-1);// just one under maxChar in order to add the innerword separator '-'
if(wordArray.length >= 1){
// Add every piece of word but the last, concatenated with '-' at the end
for(var i=0; i<(wordArray.length-1); i++){
var piece = wordArray[i] + "-";
out.push(piece);
}
// finally, add the last piece
out.push(wordArray[wordArray.length-1]);
}
}
// If nothing done, just use the same word
if(out.length == 0) {
out.push(word);
}
return out;
}
var split_out = [[]];
var split_string = txt.split(' ');
for(var i=0; i<split_string.length; i++){
var word = split_string[i];
// If the word itself exceed the max length, split it,
if(word.length > max_char){
var wordPieces = splitLongWord(word, max_char);
for(var i=0;i<wordPieces.length;i++){
var wordPiece = wordPieces[i];
split_out = split_out.concat([[]]);
split_out[split_out.length-1] = split_out[split_out.length-1].concat(wordPiece);
}
} else {
// otherwise add it if possible
if ((sum_length_of_words(split_out[split_out.length-1]) + word.length) > max_char){
split_out = split_out.concat([[]]);
}
split_out[split_out.length-1] = split_out[split_out.length-1].concat(word);
}
}
for (var i=0; i<split_out.length; i++){
split_out[i] = split_out[i].join(" ");
}
return this.content=split_out.join('\n');
};
Example of use :
wordwrap for paper.js example
Related
I have a list of words and I want to replace them in DOM.
The replace is working but it has an issue. Before and after the replaced text, it puts an undesired slash bar (i.e. reduced to a brigade > reduced/ OTHER WORD 1 /a brigade)
Also, I need to wrap the replaced word with an html tag, like .
(i.e. reduced to a brigade > reduced OTHER WORD 1 a brigade)
You can see the code here:
https://jsfiddle.net/realgrillo/9uL6ofge/
var elements = document.getElementsByTagName('*');
var words = [
[/(^|\s)([Tt]o)(\s|$)/ig, 'OTHER WORD 1'],
[/(^|\s)([Aa]nd)(\s|$)/ig, 'OTHER WORD 2']
];
for (var i = 0; i < elements.length; i++) {
var element = elements[i];
for (var j = 0; j < element.childNodes.length; j++) {
var node = element.childNodes[j];
if (node.nodeType === 3) {
var text = node.data;
for (var k = 0; k < words.length; k++)
{
var from = new RegExp(words[k][0]);
var to = new RegExp('$1' + words[k][1] + '$3');
var replacedText = text.replace(from, to);
//console.log("from: " + from);
//console.log("to: " + to);
//console.log("toDo: " + toDo);
if (replacedText !== text)
{
element.innerHTML = element.textContent.replace(from, to);
console.log("text: " + text);
console.log("replacedText: " + replacedText);
}
}
/*
//
//!!!!THIS CODE FOR 1 WORD WORKS!!!! I need to do this for the list of words.
//
var replacedText = text.replace(/(^|\s)([Tt]o)(\s|$)/ig, '$1OTHER WORD$3');
if (replacedText !== text) {
element.innerHTML = element.textContent.replace(/(^|\s)([Tt]o)(\s|$)/ig, '$1<b class=modified>OTHER WORD</b>$3');
}
*/
}
}
}
Masters of JS, can you help me? Pure javascript, not jQuery please.
Thanks.
I can give you some much more simple function to use. First define a function for replacing words like this:
String.prototype.replaceAll = function (search, replacement) {
try {
var target = this;
return target.split(search).join(replacement);
} catch (e) {
return "";
}
};
That's why the pure javascript replace function only replace one time in a whole case. After that you can use it in your code like here:
var replacedText = text.replaceAll(/(^|\s)([Tt]o)(\s|$)/ig, '$1OTHER WORD$3');
You don't need to parse words[k][0] into a RegExp as it is already one, even though it won't break because the constructor will handle a RegExp well.
Also for the words[k][1], you don't need to parse it into a RegExp either because the second parameter of String.prototype.replace is expected to be a plain string. The slashes are there because you cast your RegExp to a string (and will end up output).
So this is a change that will fix it:
var to = '$1' + words[k][1] + '$3';
And this is optional but probably a good practice:
var from = words[k][0];
For getting an html tag, you can either change it directly in each words[k][1] or in the to var declaration, depending on what you want.
Extra:
In the for loop, the code assumes that there are always at least three groups (because of the '$3' reference), an alternative could be to delegate this replacement directly in words[k][1], but it is up to you to decide.
I'm working on a JavaScript Hangman game and I need to print out the same number of dashes as the length of the word chosen at random. Iv'e done that but it also prints out a dash for the gaps that are in the sentence, so how can I ignore these spaces and only print out a dash for the letters in the sentence? Any help appreciated Thanks.
This is the code that prints the dashes out
for(var i = 0; i < sayings[randomSaying].length; i++)
{
progress.innerHTML += "-";
}
Random Variable
var sayings = [
"cash on the nail",
"charley horse",
"foul play",
"bury the hatchet",
"hands down",
"if the cap fits",
"mumbo jumbo",
"see red",
"stone the crows",
"thick and thin",
]
sayings.toString();
var randomSaying = Math.floor(Math.random()*sayings.length);
You don't need to ignore the spaces, just replace the letters
progress.innerHTML = sayings[randomSaying].replace(/[a-z]+/g, "-");
Single line of code, all in one line, more efficient than looping ;)
Use an if statement within your for loop. Also, don't use innerHTML += because it causes the browser to have to re-parse the DOM over and over. Do this instead:
var hiddenSaying = "";
for(var i = 0; i < sayings[randomSaying].length; i++) {
if (sayings[randomSaying].charAt(i) === ' ')
hiddenSaying += " ";
else
hiddenSaying += "-";
}
progress.innerHTML = hiddenSaying;
Can't say for sure, but I think you should take a look at this:
function randomSaying(sayingsArray){
var p = sayingsArray[Math.floor(Math.random()*(sayingsArray.length+1))];
var d = p.replace(/\s/g, '').replace(/./g, '‐');
return {phrase:p, dashes:d};
}
var obj = randomSaying(sayings);
// obj.phrase is phrase obj.dashes is dashes
I've been struggling with javascript string methods and regexes, and I may be overlooking something obvious. I hope I violate no protocol by restating tofutim's question in some more detail. Responses to his question focus upon s.replace(), but for that to work, you have to know which occurrence of a substring to replace, replace all of them, or be able to identify somehow uniquely the string to replace by means of a regex. Like him, I only have an array of text offsets like this:
[[5,9], [23,27]]
and a string like this:
"eggs eggs spam and ham spam"
Given those constraints, is there a straightforward way (javaScript or some shortcut with jQuery) to arrive at a string like this?
"eggs <span>eggs</span> spam and ham <span>spam</span>"
I don't know in advance what the replacement strings are, or how many occurrences of them there might be in the base text. I only know their offsets, and it is only the occurrences identified by their offsets that I want to wrap with tags.
any thoughts?
I found a way to do it with regexp. Not sure about performance, but it's short and sweet:
/**
* replaceOffset
* #param str A string
* #param offs Array of offsets in ascending order [[2,4],[6,8]]
* #param tag HTML tag
*/
function replaceOffset(str, offs, tag) {
tag = tag || 'span';
offs.reverse().forEach(function(v) {
str = str.replace(
new RegExp('(.{'+v[0]+'})(.{'+(v[1]-v[0])+'})'),
'$1<'+tag+'>$2</'+tag+'>'
);
});
return str;
}
Demo: http://jsbin.com/aqowum/3/edit
iquick solution (not tested)
function indexWrap(indexArr,str){
// explode into array of each character
var chars = str.split('');
// loop through the MD array of indexes
for(var i=0; i<indexArr.length;i++){
var indexes = indexArr[i];
// if the two indexes exist in the character array
if(chars[indexes[0]] && chars[indexes[1]]){
// add the tag into each index
chars.splice(indexes[0],0,"<span>");
chars.splice(indexes[1],0,"</span>");
}
}
// return the joined string
return chars.join('');
}
Personally, I like a string replace solution, but if you dont want one, this might work
You can try slice method.
var arr = [[5,9], [23,27]];
arr = arr.reverse()
$.each(arr, function(i, v){
var f = v[0], last = v[1];
$('p').html(function(i, v){
var o = v.slice(0, f);
var a = '<span>' + v.slice(f, last) + '</span>';
var c = v.slice(last, -1);
return o+a+c
})
})
http://jsfiddle.net/rjQt7/
First, you'd want to iterate backwards, in order to make sure you won't eventually overwrite the replacements previously made, however, in my example it is not important because the string is reassembled all at once in the very end.
// > interpolateOnIndices([[5,9], [23,27]], "eggs eggs spam and ham spam");
// < 'eggs <span>eggs</span> spam and ham <span>spam</span>'
function interpolateOnIndices(indices, string) {
"use strict";
var i, pair, position = string.length,
len = indices.length - 1, buffer = [];
for (i = len; i >= 0; i -= 1) {
pair = indices[i];
buffer.unshift("<span>",
string.substring(pair[0], pair[1]),
"</span>",
string.substring(pair[1], position));
position = pair[0];
}
buffer.unshift(string.substr(0, position));
return buffer.join("");
}
This is a little bit better then the example with spliceing, because it doesn't create additional arrays (splice in itself will create additional arrays). Using mapping and creating functions repeatedly inside other functions is a certain memory hog, but it doesn't run very fast either... Although, it is a little bit shorter.
On large strings joining should, theoretically, give you an advantage over multiple concatenations because memory allocation will be made once, instead of subsequently throwing away a half-baked string. Of course, all these need not concern you, unless you are processing large amounts of data.
EDIT:
Because I had too much time on my hands, I decided to make a test, to see how variations will compare on a larger (but fairly realistic) set of data, below is my testing code with some results...
function interpolateOnIndices(indices, string) {
"use strict";
var i, pair, position = string.length,
len = indices.length - 1, buffer = [];
for (i = len; i >= 0; i -= 1) {
pair = indices[i];
buffer.unshift("<span>",
string.substring(pair[0], pair[1]),
"</span>",
string.substring(pair[1], position));
position = pair[0];
}
buffer.unshift(string.substr(0, position));
return buffer.join("");
}
function indexWrap(indexArr, str) {
var chars = str.split("");
for(var i = 0; i < indexArr.length; i++) {
var indexes = indexArr[i];
if(chars[indexes[0]] && chars[indexes[1]]){
chars.splice(indexes[0], 0, "<span>");
chars.splice(indexes[1], 0, "</span>");
}
}
return chars.join("");
}
function replaceOffset(str, offs, tag) {
tag = tag || "span";
offs.reverse().forEach(
function(v) {
str = str.replace(
new RegExp("(.{" + v[0] + "})(.{" + (v[1] - v[0]) + "})"),
"$1<" + tag + ">$2</" + tag + ">"
);
});
return str;
}
function generateLongString(pattern, times) {
"use strict";
var buffer = new Array(times);
while (times >= 0) {
buffer[times] = pattern;
times -= 1;
}
return buffer.join("");
}
function generateIndices(pattern, times, step) {
"use strict";
var buffer = pattern.concat(), block = pattern.concat();
while (times >= 0) {
block = block.concat();
block[0] += step;
block[1] += step;
buffer = buffer.concat(block);
times -= 1;
}
return buffer;
}
var longString = generateLongString("eggs eggs spam and ham spam", 100);
var indices = generateIndices([[5,9], [23,27]], 100,
"eggs eggs spam and ham spam".length);
function speedTest(thunk, times) {
"use strict";
var start = new Date();
while (times >= 0) {
thunk();
times -= 1;
}
return new Date() - start;
}
speedTest(
function() {
replaceOffset(longString, indices, "span"); },
100); // 1926
speedTest(
function() {
indexWrap(indices, longString); },
100); // 559
speedTest(
function() {
interpolateOnIndices(indices, longString); },
100); // 16
Tested against V8 (Node.js) on amd64 Linux (FC-17).
I didn't test the undefined's answer because I didn't want to load that library, especially so it doesn't do anything useful for this test. I would imagine it will lend somewhere between andbeyond's and elclanrs's variants, more towards elclanrs's answer though.
you may use the substring method
String.substring (startIndex, endIndex);
description: return the string between start & end index
usage:
var source="hello world";
var result=source.substring (3,7); //returns 'lo wo'
you already have an array with initial & final index, so you are almost done :)
I've got an array of words I need to sort by frequency. Before I do that, I need to strip out words like 'the,' 'it,' etc (anything less than three letters, really), as well as all numerals and any words beginning with # (the array of words is pulled from Twitter, although the example below is just a random paragraph from Wikipedia).
I can remove one word, but have been going crazy trying to remove more than one, or a range. Any suggestions? Thank you!
http://jsfiddle.net/9NzAC/6/
HTML:
<div id="text" style="background-color:Teal;position:absolute;left:100px;top:10px;height:500px;width:500px;">
Phrenology is a pseudoscience primarily focused on measurements of the human skull, based on the concept that the brain is the organ of the mind, and that certain brain areas have localized, specific functions or modules. The distinguishing feature of phrenology is the idea that the sizes of brain areas were meaningful and could be inferred by examining the skull of an individual.
</div>
JS:
//this is the function to remove words
<script type="text/javascript">
function removeA(arr){
var what, a= arguments, L= a.length, ax;
while(L> 1 && arr.length){
what= a[--L];
while((ax= arr.indexOf(what))!= -1){
arr.splice(ax, 1);
}
}
return arr;
}
</script>
//and this does the sorting & counting
<script type="text/javascript">
var getMostFrequentWords = function(words) {
var freq={}, freqArr=[], i;
// Map each word to its frequency in "freq".
for (i=0; i<words.length; i++) {
freq[words[i]] = (freq[words[i]]||0) + 1;
}
// Sort from most to least frequent.
for (i in freq) freqArr.push([i, freq[i]]);
return freqArr.sort(function(a,b) { return b[1] - a[1]; });
};
var words = $('#text').get(0).innerText.split(/\s+/);
//Remove articles & words we don't care about.
var badWords = "the";
removeA(words,badWords);
var mostUsed = getMostFrequentWords(words);
alert(words);
</script>
Instead of removing from the original array, just push to a new one, it's simpler, and it'll make your code shorter and more readable.
var words = ['the', 'it', '12', '#twit', 'aloha', 'hello', 'bye']
var filteredWords = []
for (var i = 0, l = words.length, w; i < l; i++) {
w = words[i]
if (!/^(#|\d+)/.test(w) && w.length > 3)
filteredWords.push(w)
}
console.log(filteredWords) // ['aloha', 'hello']
Demo: http://jsfiddle.net/VcfvU/
I recommend you to do array[i] = null (or "") and then just clean up your arrays empty nodes. You can easily achieve that using Array#filter
Test: http://jsfiddle.net/6LPep/
Code:
var FORGETABLE_WORDS = ',the,of,an,and,that,which,is,was,';
var words = text.innerText.split(" ");
for(var i = 0, word; word = words[i++]; ) {
if (FORGETABLE_WORDS.indexOf(',' + word + ',') > -1 || word.length < 3) {
words[i-1] = "";
}
}
// falsy will get deleted
words.filter(function(e){return e});
// as example
output.innerHTML = words.join(" ");
// just continue doing your stuff with "words" array.
// ...
I think it's cleaner than the way you're doing it currently. If you need anything else I will update this answer.
console.log(
['🍇','🍈','🍌','🍉','🍊','🍋'].filter(a => !['🍌','🍊'].includes(a))
)
I have the following text:
var text=
"The sad sad man uses a bat to swing the bats
away from his sad garden .
Sadly he doesn't succeed. "
Let's say i want to search for the word "sad".
var match;
re = /sad/g,
match;
while (match = re.exec(text)) {
console.log(match);
match.poz = ....
}
How can i make match.poz to be a tuple(array) like this [line,position on the collumn] all starting from 0,0 ?
Eg.
1 match --> match.poz = [0,4]
2 match --> match.poz = [0,8]
3 match --> match.poz = [1,14]
4 match --> match.poz = [2,0]
I was able to build a simple parser, instead of using a regex, which I don't think is possible (without a lot of help) to get the position in Javascript. All this does is go through the line, one character at a time, and "peek" ahead to see if the current position gives either sad or \n.
var text = "The sad sad man uses a bat to swing the bats \naway from his sad garden .\nSadly he doesn't succeed.",
length = text.length,
matches = [],
lines = 0,
pos = 0;
for (var i = 0; i < length; i++){
var word = text.substring(i, i + 3).toLowerCase();
if (word == 'sad') {
matches[matches.length] = [lines, pos];
}
if (word.indexOf('\n') == 0) {
lines++;
pos = 0;
} else {
pos++;
}
}
console.log(matches);
Which gives me the following in Firebug console:
[[0, 4], [0, 8], [1, 14], [2, 0]]
http://jsfiddle.net/Zx5CK/1/
First i think you need to be able to delimit the lines in some way. Probably using some character(like '\n' for example) in the input data.
Then one way to solve the problem is to use the split function to get you the words in each line as an array. You can then write a function which takes in a line and the required word and compares each word with what you are searching for.
//where i denotes the currently read line.
var indexInLine = checkforWordInLine(input.line[i].split(' '), "sad");
if(indexInLine != -1)
//word found in line.
// save indexInLine and 'i', the line index
function checkforWordInLine(line, searchKey)
{
var wordIndex = -1;
for(var i=0,j=line.length; i < j; i++)
{
if(line[i] === searchKey)
wordIndex = i;
}
return wordIndex;
}