Make a mountain out of a molehill by replacing it with JavaScript - javascript

I want to replace multiple words on a website with other words. That is, I am interested in finding all instances of a source word and replacing it with a target word.
Sample Cases:
Source | Target
Molehill => Mountain
Green => Grey
Google => <a href="http://google.com">
Sascha => Monika
Football => Soccer

This is somewhat of a half answer. It shows the basic process, but also illustrates some of the inherent difficulties in a process like this. Detecting capitalization and properly formatting the replacements would be a bit intensive (probably utilizing something like this on a case-by-case basis How can I test if a letter in a string is uppercase or lowercase using JavaScript?). Also, when dealing with text nodes, innerHTML isn't an option, so the google replacement comes out as plain text instead of HTML.
TLDR - If you have another way to do this that doesn't involve javascript, do it that way.
var body = document.querySelector('body')
function textNodesUnder(el){
var n, a=[], walk=document.createTreeWalker(el,NodeFilter.SHOW_TEXT,null,false);
while(n=walk.nextNode()) a.push(n);
return a;
}
function doReplacements(txt){
txt = txt.replace(/sascha/gi, 'monika')
txt = txt.replace(/mountain/gi, 'molehill')
txt = txt.replace(/football/gi, 'soccer')
txt = txt.replace(/google/gi, 'google')
console.log(txt)
return txt
}
var textnodes = textNodesUnder(body),
len = textnodes.length,
i = -1, node
console.log(textnodes)
while(++i < len){
node = textnodes[i]
node.textContent = doReplacements(node.textContent)
}
<div>Mountains of Sascha</div>
<h1>Playing football, google it.</h1>
<p>Sascha Mountain football google</p>

Here is the JS:
function replaceWords () {
var toReplace = [
["Green","Grey"],
["Google","<a href='http://google.com'>"]
];
var input = document.getElementById("content").innerHTML;
console.log("Input: " + input);
for (var i = 0; i < toReplace.length; i++) {
var reg = new RegExp(toReplace[i][0],"g");
input = input.replace(reg,toReplace[i][1]);
}
document.getElementById("content").innerHTML = input;
};
replaceWords();

Related

Can't get values past array[0] to translate properly

Okay, to start with I should mention this is a very small personal project, and I've only have a handful of coding classes several years ago now. I can figure out a lot of the (very) basics, but have a hard time troubleshooting. I'm in a little bit over my head here, and need a dumbed down solution.
I'm trying to put together a VERY simple translator that takes in a word or sentence from the user via a text input box, puts each word of the string into an array, translates each word in order, then spits out each translated word in the order it was input. For example, typing "I like cats" would output "Ich mag Katze" in German.
I've got most of it, but I CAN'T get anything but the first array element to translate. It comes out like "Ich like cats".
I've used a loop, probably because I'm an amateur and don't know another way of doing this, and I'd rather not use any libraries or anything. This is a very small project I want to have a couple of friends utilize locally; and I know there has to be some very simple code that will just take a string, put it into an array, swap one word for another word, and then output the results, but I'm damned if I can make it work.
What I currently have is the closest I've gotten, but like I said, it doesn't work. I've jerry-rigged the loop and clearly that's the totally wrong approach, but I can't see the forest for the trees. If you can help me, please make it "Javascript for Babies" picture book levels of simple, I cannot stress enough how inexperienced I am. This is just supposed to be a fun little extra thing for my D&D group.
function checkForTranslation(input, outputDiv) {
var input = document.getElementById("inputTextField").value;
var outputDiv = document.getElementById("translationOutputDiv");
input = input.toLowerCase();
//puts user input into an array and then outputs it word by word
const myArray = input.split(" "); //added .split, thank you James, still otherwise broken
let output = "";
let translation = "";
for (let i = 0; i < myArray.length; i++) {
output += myArray[i]; //up to here, this works perfectly to put each word in the string into an array
//prints all words but doesnt translate the second onwards
translation += myArray[i];
if (output == "") {
//document.getElementById("print2").innerHTML = "Translation Here";
}
else if (output == "apple") {
translation = "x-ray";
}
else if (output == "banana") {
translation = "yak";
}
else {
translation = "???";
}
output += " "; //adds a space when displaying original user input
} // END FOR LOOP
document.getElementById("print").innerHTML = output; //this outputs the original user input to the screen
document.getElementById("print3").innerHTML = translation; //this should output the translated output to the screen
} // END FUNCTION CHECKFORTRANSLATION
What it looks like
P.S. I'm not worried about Best Practices here, this is supposed to be a quickie project that I can send to a couple friends and they can open the HTML doc, saved locally, in their browser when they want to mess around with it if they want their half-orc character to say "die by my hammer!" or something. If you have suggestions for making it neater great, but I'm not worried about a mess, no one is going to be reading this but me, and hopefully once it's fixed I'll never have to read it again either!
Since it is a manual simple translation, you should just create a "dictionary" and use it to get the translations.
var dictionary = {
"apple": "x-ray",
"banana": "yak"
}
function checkForTranslation() {
var input = document.getElementById("inputTextField").value.toLowerCase();
var words = input
.split(' ') // split string to words
.filter(function(word) { // remove empty words
return word.length > 0
});
var translatedWords = words.map(function(word) {
var wordTranslation = dictionary[word]; // get from dictionary
if (wordTranslation) {
return wordTranslation;
} else { // if word was not found in dictionary
return "???";
}
});
var translatedText = translatedWords.join(' ');
document.getElementById("translationOutputDiv").innerHTML = translatedText;
}
document.getElementById('translate').addEventListener('click', function() {
checkForTranslation();
});
<input type="text" id="inputTextField" />
<button id="translate">translate</button>
<br/>
<hr />
<div id="translationOutputDiv"></div>
Or if you want it a little more organized, you could use
const dictionary = {
"apple": "x-ray",
"banana": "yak"
}
function getTranslation(string) {
return string
.toLowerCase()
.split(' ')
.filter(word => word)
.map(word => dictionary[word] || '???')
.join(' ');
}
function translate(inputEl, outputEl) {
outputEl.innerHTML = getTranslation(inputEl.value);
}
document.querySelector('#translate').addEventListener('click', function() {
const input = document.querySelector('#inputTextField');
const output = document.querySelector('#translationOutputDiv');
translate(input, output);
});
<input type="text" id="inputTextField" />
<button id="translate">translate</button>
<br/>
<hr />
<div id="translationOutputDiv"></div>

How can I get the string between repeated DYNAMIC html tags in a string using javascript? (No regex unless its the only way!)

As a mean to explain I am not posting the gigantic string I have (which contains dynamic html tags and are auto-generated with unique id). I want to get the text between every tag that says "<p class=partial_entry>... comment...</p>" using anything excep Regex, and in my case ill have a string of 20 repeated tags by getting the outerHTML. I have an example below:
var str = "<div class=prw_rup prw_reviews_text_summary_hsx data-prwidget-name=reviews_text_summary_hsx data-prwidget-init=handlers>
<div class=entry><p class=partial_entry>You have to try their special sushi rolls like acevichado or patrullero. They have great selections of sushi and other dishes.</p>
</div></div><div class=prw_rup prw_reviews_text_summary_hsx data-prwidget-name=reviews_text_summary_hsx data-prwidget-init=handlers><div class=entry><p class=partial_entry>
All you can eat sushi fir $20 ($24 including tax)! Christian, our server, was wonderful and attentive.
</p></div></div><div class=prw_rup prw_reviews_text_summary_hsx data-prwidget-name=reviews_text_summary_hsx data-prwidget-init=handlers>
<div class=entry><p class=partial_entry>The place was good, also the waiters, but definitely sushi is the best in town
for my opinion, even with the few options of it in this place. I will be there soon again.</p></div></div>";
What I want is the 3 comments I have in my example so I use the code for 20:
- You have to try their special sushi rolls like acevichado or patrullero. They have great selections of sushi and other dishes.
- All you can eat sushi fir $20 ($24 including tax)! Christian, our server, was wonderful and attentive.
- The place was good, also the waiters, but definitely sushi is the best in town for my opinion, even with the few options of it in this place. I will be there soon again.
I tried making my own code but the text or tags in between are not taken off since it detects the tag I preset, example: "THE WHOLE STRING AFTER THE TAG IM LOOKING FOR + <p class=partial_entry>... comment...</p>" and I only want ...comment... part.
The code I made is below:
var temp = "<p class=partial_entry";
var res = str.split('>');
var res2 = res.indexOf(temp) + 1;
var resultado = null;
if (res2 < res.length && res2 != -1) {
resultado = res[ res2 ]; // gets the next one
}
alert(resultado);
This is pretty trivial, assuming the comments will always be wrapped in a node with class partial_entry:
var commentNodes = document.getElementsByClassName('partial_entry');
var comments = [];
for (var i = 0; i < commentNodes.length; i++) {
comments.push(commentNodes[i].innerText);
}
You could use a for loop that loops through every single character until it sees , then stores every character until it sees
It probably isn't the best way, and won't work if there's other tags inside the p. But it works.
var text = "<p class=partial_entry>This text should show up</p>"
var seenBeginTag = false;
var seenEndTag = false;
var output = [];
for (var i = 0; i < text.length; i++)
{
if (seenBeginTag && seenEndTag) {
if (text[i] == '<')
{
seenBeginTag = false;
seenEndTag = false;
}
else {
output.push(text[i]);
}
}
else if (seenBeginTag) {
if (text[i] == '>')
{
seenEndTag = true;
}
}
else if (text[i] == '<') {
if (text[i+1] == 'p') {
seenBeginTag = true;
}
}
}
console.log(output.join(''));

Get Array of Strings In Between Two Strings with Javascript

This question has been asked a few times before, here's an example. However, the question linked only asks about getting one string out of the result. The text I would like to parse has many different instances of the trailing and leading strings, and thus the code below does not work:
test.match("SomeString(.*)TrailingString");
As shown in this fiddle. I will show you the intended result below:
If I were to have a string composed of the following elements STARTINGTEXTText I wantENDINGTEXT Text I don't want STARTINGTEXTMore text I wantENDINGTEXT Text I don't want
I would like to have a function that I can pass in the arguments STARTINGTEXT and ENDINGTEXT and it would return an array with "Text I want" and "More text I want"
Thanks!
EDIT - This is a Pebble Application so JQuery isn't an option.
This similar thing has been done in Objective-C:
-(NSMutableArray*)stringsBetweenString:(NSString*)start andString:(NSString*)end
{
NSMutableArray* strings = [NSMutableArray arrayWithCapacity:0];
NSRange startRange = [self rangeOfString:start];
for( ;; )
{
if (startRange.location != NSNotFound)
{
NSRange targetRange;
targetRange.location = startRange.location + startRange.length;
targetRange.length = [self length] - targetRange.location;
NSRange endRange = [self rangeOfString:end options:0 range:targetRange];
if (endRange.location != NSNotFound)
{
targetRange.length = endRange.location - targetRange.location;
[strings addObject:[self substringWithRange:targetRange]];
NSRange restOfString;
restOfString.location = endRange.location + endRange.length;
restOfString.length = [self length] - restOfString.location;
startRange = [self rangeOfString:start options:0 range:restOfString];
}
else
{
break;
}
}
else
{
break;
}
}
return strings;
}
If you would prefer a RegExp solution, you could do something like this:
var test = "STARTINGTEXTText I wantENDINGTEXT Text I don't want STARTINGTEXTMore text I wantENDINGTEXT Text I don't want";
var matches = test.match(/STARTINGTEXT(.*?)ENDINGTEXT/g);
The key to this is the "g" (or global) flag, and the non-greedy repeat operator "*?". See this link for an explanation of the "g" flag and the non-greedy operator.
Here is a modification of your fiddle: link. I changed it so that the alert would show a stringified JSON of the results, so that you could see it matching both strings.
This methodology uses very little code:
function getBetweenText(fromString, ignoreStart, ignoreEnd){
var s = fromString.split(new RegExp(ignoreStart+'|'+ignoreEnd)), r = [];
for(var i=1,l=s.length; i<l; i+=2){
r.push(s[i]);
}
return r;
}
console.log(getBetweenText("STARTINGTEXTText I wantENDINGTEXT Text I don't want STARTINGTEXTMore text I wantENDINGTEXT Text I don't want", 'STARTINGTEXT', 'ENDINGTEXT'));
You can do this using jQuery. To select all the elements with specific tag you just do something like this: ** UPDATED WITH NON-JQUERY VERSION **
var HTMLelements = document.getElementsByTagName("tag");
var results = [];
for(var i = 0; i < HTMLelements.length; i++){
results.push(HTMLelements[i].innerHTML);
}

How do you divide a string of random letters into pairs of substrings and replace with new characters continuously in Javascript?

So I'm working on this project that involves translating a string of text such as "als;kdfja;lsjkdf" into regular charaters like "the big dog" by parsing for certain pairs of letters that translate. (i.e: "fj" = "D")
The catch is I cant simply use the .replace() function in javascript, because there are many occurences where it's given the text "fjkl", and needs to find "jk" and logically interprets the collision of "fj" and "kl" to say that it's found it. This wont work for me, because for me, it didnt find it, as i am only trying to look at found pairs within 2 characters at a time. (i.e: "fjkl" could only yeild "fj" and "kl".)
(In the end I intend to utilize just the 8 characters "asdfjkl;" and set pairs of characters to actual letters. (in this subsitution method, fyi, "fj" OR "jf" would actually be "_"(space). )
in trying to figure out this task in javascript, (I dont know if another language might handle it more efficiently,) I tried utilizing the "split" function in the following way. (Disclaimer, I'm not sure if this is formatted 100% perfectly)
<textarea id="textbox"></textarea>
<script>
var text = document.getElementById("textbox").value; //getting string from the textarea
var pairs = text.split(/(..)/).filter(String); //spliting string into pairs
if(pairs == "fj"){replace(pairs, " ")} //some sort of subsitution
</script>
Additionally, if possible, i would like the replaced characters to be fed directly into the textarea continuosly as the user types, so the translation happens almost simutaneously. (I'm assuming this will use some sort of setInterval() function?)
If any tips can be given on the correct formatting of which tools i should use in javascript, that would be very outstanding; Thanks in advance.
if your interested, here is full list of subsitutions im making in the end of this project:
syntax:(X OR Y == result)
AJ JA = F
AK KA = V
AL LA = B
A; ;A = Y
SJ JS = N
SK KS = M
SL LS = S
S; ;S = P
DJ JD = A
DK DK = U
DL LD = D
D; ;D = G
FJ JF = _
FK KF = I
FL LF = T
F; ;F = K
AS SA = C
SD DS = L
DF FD = E
JK KJ = O
KL LK = R
L; ;L = Z
AD DA = -
SF FS = ,
AF FA = .
JL LJ = !
K; ;K = :
J; ;J = ?
-Daniel Rehman
I have prepared a code for your requirement. You can bind a function on keydown to allow continuous changes as you type in the textarea.
I am using replacePair method to replace a pair of character by its equivalent uppercase representation. You can inject your own custom logic here.
var tb = document.getElementById('tb');
var processedLength = 0;
var pairEntered=false;
tb.onkeydown = function (e) {
pairEntered=!pairEntered;
if (pairEntered) {
var nextTwoChars = this.value.substr(this.value.length - 2, 2);
var prevPart=this.value.substr(0,this.value.length-2);
var finalText=prevPart+ replacePair(nextTwoChars);
this.value=finalText;
processedLength+=2;
}
}
function replacePair(str){
return str.toUpperCase();
}
jsfiddle:http://jsfiddle.net/218fq7t2/
updated fiddle as per your replacement logic: http://jsfiddle.net/218fq7t2/3/
If you can be assured that certain pairs always translate to the same character, then perhaps a dictionary object can help.
var dict = { as:"A", kl:"B", al:"C", fj:"D" ... };
And, if your 'decryption' algorithm is 'lazy' (evaluates the first pair it encounters), then you can just travel through the input string.
var outputString = "", c, cl;
for (c = 1, cl = inputString.length; c < cl; c += 2) {
outputString += dict[inputString[c-1] + inputString[c]] || "";
}
If your replacement algorithm is not any more complicated than simply looking up which letter the pair represents, then this should do alright for you. No real logic necessary.
Couldn't you do it as follows:
var text = document.getElementById("textbox").value;
for (i = 0; i <= text.length; i++) {
if (text[i] == "j") {
if (text[i+1] == "f") {
pair = "jf";
text = text.replace(pair, "_");
}
}
What this would do is it would always, when checking any letter, also check the letter after it during the same step in the procedure. When it finds both letter i and letter i+1 matching up with a pair you are looking for, then the letters will be replaced by a space (or whatever you want), meaning that when the for-loop reaches the next run after a pair was found, the size of the text string will have been reduced by one. Thus, when it increments i, it will automatically skip the letter that made up the second component of the found pair. Thus, "jfkl" will be identified as two different pairs and your algorithm will not be confused.
of course, you would also have to work in the other pairs/codewords into the for loop so that they are all checked in some way
I had hoped my previous answer was enough to get you started. I was merely providing an algorithm that you could then use to your liking (wrap it in a function and add your own event listeners, etc).
Here is the solution to your problem. I did not write the entire dictionary. You will need to complete that.
var dictionary = { "aj":"F", "ja":"F", "ak":"V", "ka":"V", "al":"B", "la":"B", "a;":"Y", ";a":"Y" }
var input, output;
function init() {
input = document.getElementById("input");
output = document.getElementById("output");
input.addEventListener("keyup", decrypt, false);
}
function decrypt () {
if (!input || !output) {
return;
}
var i = input.value, o = "", c, cl;
for (c = 1, cl = i.length; c < cl; c += 2) {
o += dictionary[ i[c-1] + i[c] ] || "";
}
while (output.hasChildNodes()) {
output.removeChild(output.firstChild);
}
output.appendChild(document.createTextNode(o));
}
window.addEventListener("load", init, false);
<textarea id="input"></textarea>
<div id="output"></div>

Parsing MathML to plain math expression

I am using MathDox formula editor to produce MathML. Now I want to convert the MathML produced by MathDox to expression which I can later use to evaluate to find the answer.
For eg:
MathML:
<math xmlns='http://www.w3.org/1998/Math/MathML'>
<mrow>
<mn>3</mn>
<mo>+</mo>
<mn>5</mn>
</mrow>
</math>
Want to convert to expression as:
3+5
Now I can use 3+5 to get answer 8.
I am in a search of javascript or c# solution for this conversion. Tried to google it, but didn't get much help. Somewhat closer solution I found here, but it is a desktop application and commercial too. However, I want open source web app solution for my problem. Any help will be appreciated.
Note: For simplicity I've mentioned only simple addition in example above but the mathml can also contain complex epression like derivations and log.
This can be achieved using the following steps in JavaScript:
Convert from MathML to XML DOM
Convert from XML DOM to plain text
Use the "eval" function to get the decimal value of the expression
The following code does precisely that:
function getDOM(xmlstring) {
parser=new DOMParser();
return parser.parseFromString(xmlstring, "text/xml");
}
function remove_tags(node) {
var result = "";
var nodes = node.childNodes;
var tagName = node.tagName;
if (!nodes.length) {
if (node.nodeValue == "π") result = "pi";
else if (node.nodeValue == " ") result = "";
else result = node.nodeValue;
} else if (tagName == "mfrac") {
result = "("+remove_tags(nodes[0])+")/("+remove_tags(nodes[1])+")";
} else if (tagName == "msup") {
result = "Math.pow(("+remove_tags(nodes[0])+"),("+remove_tags(nodes[1])+"))";
} else for (var i = 0; i < nodes.length; ++i) {
result += remove_tags(nodes[i]);
}
if (tagName == "mfenced") result = "("+result+")";
if (tagName == "msqrt") result = "Math.sqrt("+result+")";
return result;
}
function stringifyMathML(mml) {
xmlDoc = getDOM(mml);
return remove_tags(xmlDoc.documentElement);
}
// Some testing
s = stringifyMathML("<math><mn>3</mn><mo>+</mo><mn>5</mn></math>");
alert(s);
alert(eval(s));
s = stringifyMathML("<math><mfrac><mn>1</mn><mn>2</mn></mfrac><mo>+</mo><mn>1</mn></math>");
alert(s);
alert(eval(s));
s = stringifyMathML("<math><msup><mn>2</mn><mn>4</mn></msup></math>");
alert(s);
alert(eval(s));
s = stringifyMathML("<math><msqrt><mn>4</mn></msqrt></math>");
alert(s);
alert(eval(s));
Following the previous code, it is possible to extend the accepted MathML. For example, it would be easy to add trigonometry or any other custom function.
For the purpose of this post, I used the tool from mathml editor to build the MathML (used in the test part of the code).

Categories

Resources