Parsing text with Javascript - javascript

I'm trying to use Javascript to parse text that has been entered in a text box - which would combine a variety of user-generated variables to create random activities. This might make more sense looking at the example. Some example input might be:
Activity
#Home
#Out
#Home
Read #book for #time
Clean up #room for #time
#Out
Eat at at #restaurant
#book
Enders Game
Lord of the Rings
#room
bedroom
garage
basement
#restaurant
Red Robin
McDonalds
Starbucks
#time
15 minutes
30 minutes
45 minutes
60 minutes
Pound/and signs would be used to separate different categories.
The output would then be determined randomly from the given input, for example:
"Eat at Starbucks."
or
"Read Lord of the Rings for 60 minutes."
or
"Clean garage for 30 minutes."
Is this doable? It seems like it should be fairly straightforward, but I do not know where to start. Any suggestions?
Thanks,
Albert

How about:
var myText = ...; // Input text
var lines = myText.split("\n");
var numLines = lines.length;
var i;
var currentSection;
var sections = Array();
var phrases = Array();
// parse phrases
for (i = 0; i < numLines; i++) {
var line = lines[i];
if (line.indexOf('#') == 1) {
// start of e.g. time section, handled in nex loop
break;
} else {
// phrase
phrase.push(line);
}
}
// parse sections
for ( ; i < numLines; i++) {
var line = lines[i];
if (line.indexOf('#') == 1) {
// start of next section, handled in nex loop
currentSection = line;
sections[currentSection] = new Array();
} else {
// add section entry
sections[currentSection].push(line);
}
}
It's not too sophisticated, but does the job. Didn't test it though, but something like this should work. And where is the fun if this'd just work ;D

No issue at all. Split the textbox value into an array based on line break characters. Then, go through the array one element at a time, sorting the values into variables for each section. Finally, use JavaScript's random number generator to randomly determine which of each group to select. Output to the user by assigning the value to an HTML element.

Related

Rubik´Cube Scrambling Algorithm - JavaScript

I have been working on a Rubik’s Cube Timer website, and I need to make a scrambling algorithm. I’ll go over how the scrambling algorithm should work:
Each face has it’s own letter, it’s initial. for examble, if you want to move the front face, you would write “ F “. If you want to move the the right face, you would write “ R “, and so on. just note that the bottom face is D, as for down. So you have D U R L B F.
If there is nothing after that letter, you turn it clockwise. If there is an appostrophe “ ‘ “, you turn it counter-clockwise. If there is a 2, you turn it two times. Now the thing is that you cannot have 2 same letters next to oneanother, as they would cancel (For example “.. U U’ ...” would be the same as doing nothing. So far, I have this taken care of in my algorithm.
The problem comes when you have one letter, then it’s opposite, then again the first letter, ( For example “.. U D U’...” (would mean Up clockwise, Down clockwise, Up counterclokwise)).
I have no idea how to check for these and avoid them automatically. Here’s the code:
<div id=“Scramble”></div>
<script>
generateScramble();
function generateScramble() {
// Possible Letters
var array = new Array(" U", " D", " R", " L", " F", " B")
// Possible switches
var switches = ["", "\'", "2"];
var array2 = new Array(); // The Scramble.
var last = ''; // Last used letter
var random = 0;
for (var i = 0; i < 20; i++) {
// the following loop runs until the last one
// letter is another of the new one
do {
random = Math.floor(Math.random() * array.length);
} while (last == array[random])
// assigns the new one as the last one
last = array[random];
// the scramble item is the letter
// with (or without) a switch
var scrambleItem = array[random] + switches[parseInt(Math.random()*switches.length)];
array2.push(scrambleItem); // Get letters in random order in the array.
}
var scramble = "Scramble: ";
// Appends all scramble items to scramble variable
for(i=0; i<20; i++) {
scramble += array2[i];
}
document.getElementById("Scramble").innerHTML = scramble; // Display the scramble
}
</script>
For starters God's Number is 20 for Rubik;s cube so you got only 20 moves instead of 25. I assume you are not doing scrambling (as your title suggest) but instead generate solution command strings for genere&test solver type. There are too many sequences that cancel each other and to check for all of them would be most likely slower than try them out actually.
The problem is that even O(n^20) is huge and you need to lower the 20. That is done by LUT holding semi solved states. For example create table holding states for all combinations of 5 turn scrambling. Then use that as end condition turning your solver into O(n^15 + n^5) = O(n^15) ...

String concat, test, add extra code or add anyway and remove at the end?

Sorry for the odd title, I'm sure someone has asked something similar before. My question is, I'm building a string with html tags enclosed, my question is is it better to test and add extra tags, in this case a <br />, or to add the tag anyway and have a 'remove' line at the end, which is faster?:
So at the moment we have
bFirst = true;
label = '';
if(...)
{
if (!bFirst)
label += '<br/>';
label+= 'some more text'
}
if(...)
{
if (!bFirst)
label += '<br/>';
label+= 'some more text'
}
and so on...
or
if()
{
label+= 'some more text <br />'
}
and then just remove the last <br /> using the string.substring() method.
Which is faster, better, more correct? I don't need code it is really a performance question, I could test it but I'm sure someone has done this before.
Thanks.
You can define which way is the fastest with this procedure :
Requirements :
You will need an addon like firebug or pagespeed
You'll have to execute those two pages :
Add all extra text and then trim some :
<div id ="concat"></div>
<script type="text/javascript">
var someText = ""
// adds "foo" 10k times
for (i = 0; i < 10000; i++) {
someText += "foo"
}
// crops the 3 last characters off the string 5k times
for (i = 0; i < 5000; i++) {
someText.substr(someText.length - 3, someText.length);
}
// append the final string
$('#concat').append(someText);
</script>
On my browser, it takes between 0.19 sec and 0.30 sec.
Add extra text when it is needed
<div id ="if"></div>
<script type="text/javascript">
var someText = ""
var append = true;
for (i = 0; i < 15000; i++) {
//adds "foo" the first 10k times
if(append == true){
someText += "foo";
}
if(i == 10000){
append = false;
}
}
$('#if').append(someText);
</script>
This code iterates 15k times and only adds "foo" the first 10k times.
The execution of such code can take between 0.10 and 0.12 sec
Conclusion
Adding content to your string only when it is needed seems a better option in terms of performance.
Also, in terms of code readabilty, the second option should be used.
Quick and simple analysis:
First option makes a comparison and a memory reallocation.
Second option (supposing you'd be doing label = label.substring(...)) counts characters, if you want the first N characters, String.prototype.substring() will probably count N times while reading N bytes of characters. Finally, you allocate/reallocate memory.
Now which strategy appears more efficient?
If you're still in doubt try both methods under a timer.

Dynamic word swapping animation 3

Following on from a previous question from 2011 titled Dynamic word swapping animation.
Thanks to Marek's answer I have been able to create an animation for text on a page that, every few seconds, changes one word out with another word from a list. However where previous question/ example was: "I have a header that says, "This is cool," but I want "cool" to be replaced every few seconds by "neat/awesome/groovy/etc".
I need my header to say eg, (for the sake of continuity) "This is cool man". The problem I have is that because the alternate words are longer it makes the sentence vary in length as it rotates through each of the words. Is there a way to specify, using Pure JS, for "man" to stay a set distance from "is", therefore the alternate words fill the gap between and the overall sentence remains the same length?
And, if the above is possible, can the alternate words be centred between "man" and "is"?
Any help greatly appreciated! Phil
The Pure JS I currently have (from previous post mentioned) reads:
<script>
var words = ["neat", "great", "best", "groovy"];
var i = 0;
var text = "This is cool";
function _getChangedText() {
i = (i + 1) % words.length;
console.log(words[i]);
return text.replace(/cool/, words[i]);
}
function _changeText() {
var txt = _getChangedText();
console.log(txt);
$("#changer").text(txt);
}
setInterval("_changeText()", 1000);
</script>
<span id="changer">This is cool</span>
This is more html/css thing... however, after few mods:
http://jsfiddle.net/X3wZV/1/
var words = ["neat", "great", "best", "groovy"];
var i = 0;
var text = "<span id='first'>This is</span> <span id='inner'>cool</span> <span id='sec'>man</span>";
function _getChangedText() {
i = (i + 1) % words.length;
console.log(words[i]);
return text.replace(/cool/, words[i]);
}
function _changeText() {
var txt = _getChangedText();
console.log(txt);
$("#changer").html(txt);
}
setInterval("_changeText()", 1000);
and a little styling (check CSS, change it according to your needs)...i think this is close to what you want, if i understand your question correctly... :)
Just use the 8th index of the string everytime and append man after the new word.
Is this what you mean?

Jquery to echo and append data into textarea

I'd like to use jQuery to echo text as it is typed, into a text area below the input line, and as it does, put random pre-selected words on the end of that repeated data. Each time a new letter is typed as input, I'd like some other "random" word appended each of the 3 "repeats", kind of a jumble.
Input= "true blue"
TextArea =
true blue black<br/>
true blue car<br/>
true blue sock<br/>
If the input = "true blues"
TextArea =
true blues snake<br/>
true blues grass<br/>
true blues red<br/>
All new results because "s" was typed. (or as close to "new/random" as can be easily)
I'm going to have 200 words in an array, and with each key press I'd like the appended words to change (onkeyup?). If they can appear random or can be randomly picked from a larger list/file.
Input from the form is repeated for 3 lines in the text area, and 3 random words are also appended basically. Onkeypress echo input + random_word (x3).
Interesting challenge.
I've played with it for a couple of hours and have come up with a rudimentary, inelegant attempt - brute force.
FIDDLE
JS
var spacecount = 0;
var sumcodes = 0;
var wordarray = 0;
var randword = ['gould','horowitz','bach','scarlatti','handel','vivaldi','corelli','albinoni','allegri','pinnock',];
$(function(){
$(document).keyup(function(e){
var char1 = String.fromCharCode(e.keyCode);
$('#keycode').html(e.keyCode);
$('#charcode').html(char1);
$('#texthere').append(char1);
sumcodes = sumcodes + e.keyCode;
if (e.keyCode == 32)
{
spacecount=spacecount + 1;
$('#spacecount').html(spacecount);
}
if (spacecount > 2)
{
spacecount = 0;
wordarr = Math.floor(Math.random() * 10) + 1
$('#texthere').append(randword[wordarr] + ' ');
$('#sumcodes').html(sumcodes);
sumcodes = 0;
}
});
});
The random number generator is not that robust. I tried to relate it to the sum of the ASCII character codes, but was unsuccessful (I'll play a bit more).

Fast JS Pagination for long texts

I'm trying to create a pagination system with JavaScript.
Basic situation: I have a database, which holds fairly long texts (story chapters, 5000 words+). I want to display these chapters on a website...however not the entire text at once, because that would pretty much kill the readability, but in pages.
I have no problem displaying the text, but rather with getting the pages right.
I've been looking around, and came across a JQuery code, which does about what I want it to do...however there's a major caveat for this method. It takes about 10 seconds to finish paginating the text, which is far too long a wait.
What the code basically does:
It splits the text into words (separated by spaces).
It then tries adding one word after the other to a innerHTML, checking back if the text is now bigger than the container it's supposed to fit in.
Each time it breaks the boundary, it reverts back to the previous string and creates a new page. (By encapsulating the text into a span, which can then be hidden/shown at a moments notice) This works, however it is too slow, because it has to run these checks 5000+ times.
I have tried creating an approximation system, which basically takes the amount of words, divides it by the factor 0.5, checks if the buffer is larger than the required size, and repeats this process, until the buffer is 'smaller' than the required size for the first time, and from that position on, it fills the buffer, until it's full.
However it just doesn't seem to work right (double words, lines, which aren't completely full, and it's still too slow.)
This is the code I'm currently using, I'd be grateful for any fixes and suggestions how to make it easier, and especially: Faster.
Oh and: No, paging it serverside is not an option, since it's supposed to fit into variable browser formats...in a fullscreen browser at 1280x768 resolution it will be less pages, than in a small browser at a 1024x768 resolution.
function CreateChild(contentBox, Len, pageText, words) {
var Child = document.createElement("span");
Child.innerHTML = pageText;
contentBox.appendChild(Child);
if(Len == 0) ++Len;
words.splice(0, Len);
return words.length;
}
$(document).ready(function(){
var src = document.getElementById('Source');
var contentBox = document.getElementById('content');
var inner = document.getElementById('inner');
//get the text as an array of word-like things
var words = src.innerHTML.replace(/ +/g, " ").split(' '), wCount = words.length;
//start off with no page text
var pageText = null, cHeight = contentBox.offsetHeight;
while(words.length > 0) {
var Found = false;
pageText = words[0]; //Prevents constant checking for empty
wCount *= 0.5; //Searches, until the words fit in.
for(var i = 1; i < wCount; ++i) pageText += ' ' + words[i];
inner.innerHTML = pageText;
Distance = inner.offsetHeight - cHeight;
if(Distance < 40) { //Less than two lines
wCount = Math.floor(wCount);
if(Distance < 0) { //Already shorter than required. Fill.
for(var i = wCount; i < words.length; ++i) {
//add the next word to the pageText
var betterPageText = pageText + ' ' + words[i];
inner.innerHTML = betterPageText;
//Checks, whether the new words makes the buffer too big.
if(inner.offsetHeight > cHeight) {
wCount = CreateChild(contentBox, i, pageText, words);
Found = true;
break;
} else {
//this longer text still fits
pageText = betterPageText;
}
}
} else {
for(var i = wCount; i >= 0; --i) {
//Removes the last word from the text
var betterPageText = pageText.slice(0, pageText.length - words[i].length - 1);
inner.innerHTML = betterPageText;
//Is the text now short enough?
if(inner.offsetHeight <= cHeight) {
wCount = CreateChild(contentBox, i, pageText, words);
Found = true;
break;
} else {
pageText = betterPageText;
}
}
}
if(!Found) CreateChild(contentBox, i, pageText, words);
}
}
//Creates the final block with the remaining text.
Child = document.createElement("span");
Child.innerHTML = pageText;
contentBox.appendChild(Child);
//Removes the source and the temporary buffer, only the result remains.
contentBox.removeChild(inner);
src.parentNode.removeChild(src);
//The rest is the actual pagination code, but not the issue
});
I managed to solve my problem, also thanks to Rich's suggestion.
What I'm doing: First off, I'm getting the text from the 'Source' (alternatively, I could write the entire text straight into the JS, the effect is the same).
Next I'm getting references to my target any my temporary buffer, the temporary buffer is located inside the target buffer, so it will retain the width information.
After that, I split the entire text into words (standard RegEx, after replacing multiple spaces with a single one). After this, I create some variables, which are meant to buffer function results, so the function calls won't have to be repeated unnecessarily.
Now the main difference: I take chunks of 20 words, checking whether the current chunk exceeds the boundary (again, buffering the results in variables, so they don't get called multiple times, function calls equal valuable microseconds).
Once the boundary is crossed (or the total number of characters is reached), the loop is stopped, and (assuming the boundary caused the 'stop'), the text is shortened by one word per run, until the text fits in again.
Finally, the new text gets added to a new span-element, which is added to the content box (but made invisible, I'll explain why in a bit), the words I just 'used' get removed from the word array and the wCount variable gets decremented by the number of words.
Rinse and repeat, until all pages are rendered.
You can exchange the '20' with any other value, the script will work with any arbitrary number, however please remember, that a too low number will cause a lot of runs in the 'adding segment', and a too big number will cause a lot of runs in the 'backtracking segment'.
As for the invisible: If the span is left visible, sooner or later it WILL cause scrollbars to appear, effectively narrowing the width of the browser window.
In turn, this will allow less words to fit in, and all following pages will be distorted (because they will be matched to the window with scrollbars, while the 'paged result' will not have scrollbars).
Below is the code I used, I hope it will help someone in the future.
var src = document.getElementById('Source');
var contentBox = document.getElementById('content');
var inner = document.getElementById('inner');
//get the text as an array of word-like things
var words = src.innerHTML.replace(/ +/g, " ").split(' ');
//start off with no page text
var cHeight = contentBox.offsetHeight, wCount = words.length;
while(wCount > 0) {
var Len = 1, Overflow = false;
var pageText = words[0]; //Prevents the continued check on 'is pageText set'.
while(!Overflow && Len < wCount) { //Adds to the text, until the boundary is breached.
//20 words per run, but never more than the total amount of words.
for(var j = 0; j < 20 && Len < wCount; ++Len, ++j) pageText += ' ' + words[Len];
inner.innerHTML = pageText;
Overflow = (inner.offsetHeight > cHeight); //Determines, whether the boundary has been crossed.
}
if(Overflow) { //Will only be executed, if the boundary has been broken.
for(--Len; Len >= 0; --Len) { //Removes the last word of the text, until it fits again.
var pageText = pageText.slice(0, -(words[Len].length + 1)); //Shortens the text in question.
inner.innerHTML = pageText;
//Checks, whether the text still is too long.
if(inner.offsetHeight <= cHeight) break;//Breaks the loop
}
}
var Child = document.createElement("span");
Child.style.display = "none"; //Prevents the sidebars from showing (and distorting the following pages)
Child.innerHTML = pageText;
contentBox.appendChild(Child);
words.splice(0, Len);
wCount -= Len;
}
Create an absolutely-positioned container that is the width of a single page. Give it height of 'auto'. Position the container somewhere off screen, like left: -10000px so users can't see it. Split the original text into 20-word chunks. (Look up the regex that accomplishes this.) Append one chunk at a time to the string in the container until the height of the container reaches the max height of a single page. Once it reaches the max height, the string in the container is basically one page of text. Push the string in the container onto an array called 'pages'. Empty the container and start creating page 2 by appending the 20-word chunks again, continuing to iterate through the array from where you left off on the previous page. Continue this process until you reach the end of the 20-word array, pushing each new page onto the array of pages whenever the container's string reaches the max height. You should now have an array of pages, each item of which contains the text of each page.
Having not searched in advance, I worked out an alternative solution with getClientRects (https://developer.mozilla.org/en-US/docs/Web/API/Element/getClientRects). If someone's interested in the details, I'll post more.

Categories

Resources