Converting a for each loop to Coffeescript - javascript

Trying to convert the following function to Coffeescript:
var parse = function (elem) {
for each(var subelem in elem) {
if (subelem.name() !== null ) {
console.log(subelem.name());
if (subelem.children().length() > 0) {
parse(subelem);
}
} else {
console.log(subelem);
}
}
};
var xml = new XML(content);
parse(xml);
It merely prints the element tags and any text to the console.
Tried using:
parse = (elem) ->
if elem.name()?
console.log elem.name()
if elem.children().length() > 0
parse subelem for own elkey, subelem of elem
else
console.log elem
xml = new XML content
parse subelem for own elkey, subelem of xml
But it never seems to parse anything under the root xml node and ends up in an infinite recursion loop continuously printing out the root nodes tag until it blows up. Any ideas as to what I am doing wrong? Thanks.

Hmm. I tested this, and the issue seems to go away if you drop the own keyword, which adds a hasOwnProperty check. Somehow, the first child of each element seems to pass that check, while others fail it. I'm a bit mystified by this, but there's your answer.

Related

How to find JSON string in HTML file

I'm trying to find a plaintext JSON within a webpage, using Javascript. The JSON will appear as plaintext as seen in the browser, but it is possible that it would be truncated into separate html tags. Example:
<div>
{"kty":"RSA","e":"AQAB","n":"mZT_XuM9Lwn0j7O_YNWN_f7S_J6sLxcQuWsRVBlAM3_5S5aD0yWGV78B-Gti2MrqWwuAhb_6SkBlOvEF8-UCHR_rgZhVR1qbrxvQLE_zpamGJbFU_c1Vm8hEAvMt9ZltEGFS22BHBW079ebWI3PoDdS-DJvjjtszFdnkIZpn4oav9fzz0
</div>
<div>
xIaaxp6-qQFjKXCboun5pto59eJnn-bJl1D3LloCw7rSEYQr1x5mxhIxAFVVsNGuE9fjk0ueTDcMUbFLPYn6PopDMuN0T1B2D1Y8ClItEVbVDFb-mRPz8THJ_gexJ8C20n8m-pBlpL4WyyPuY2ScDugmfG7UnBGrDmS5w"}
</div>
I've tried to use this RegEx.
{"?\w+"?:[^}<]+(?:(?:(?:<\/[^>]+>)[^}<]*(?:<[^>]+>)+)*[^}<]*)*}
But the problem is it fails to work with nested JSON.
I may also use javascript to count the number of { and } to find where the JSON actually ends, but there must be better options than using this slow and clumsy approach.
Many thanks
Update:
Perhaps there ain't better way to do this. Below is my current code (a bit verbose but probably needed):
let regex = /{[\s\n]*"\w+"[\s\n]*:/g;
// Consider both open and close curly brackets
let brackets = /[{}]/g;
let arr0, arr;
// Try to parse every matching JSON
arr0 = match.exec(body);
if (arr0 === null) { // Nothing found
return new Promise(resolve => resolve());
}
try {
brackets.lastIndex = match.lastIndex; // After beginning of current JSON
let count = 1;
// Count for { and } to find the end of JSON.
while ((count !== 0) && ((arr = brackets.exec(body)) !== null)) {
count += (arr[0] === "{" ? 1 : -1);
}
// If nothing special, complete JSON found when count === 0;
let lastIdx = brackets.lastIndex;
let json = body.substring(match.lastIndex - arr0[0].length, lastIdx);
try {
let parsed = JSON.parse(json);
// Process the JSON here to get the original message
} catch (error) {
console.log(err);
}
...
} catch(err) {
console.log(err);
};
That's not possible in a good way, it might be possible to take a parent element's innerText and parse that:
console.log(JSON.parse(document.getElementById('outer').innerText.replace(/\s|\n/g, '')));
<div id="outer">
<div>
{"kty":"RSA","e":"AQAB","n":"mZT_XuM9Lwn0j7O_YNWN_f7S_J6sLxcQuWsRVBlAM3_5S5aD0yWGV78B-Gti2MrqWwuAhb_6SkBlOvEF8-UCHR_rgZhVR1qbrxvQLE_zpamGJbFU_c1Vm8hEAvMt9ZltEGFS22BHBW079ebWI3PoDdS-DJvjjtszFdnkIZpn4oav9fzz0
</div>
<div>
xIaaxp6-qQFjKXCboun5pto59eJnn-bJl1D3LloCw7rSEYQr1x5mxhIxAFVVsNGuE9fjk0ueTDcMUbFLPYn6PopDMuN0T1B2D1Y8ClItEVbVDFb-mRPz8THJ_gexJ8C20n8m-pBlpL4WyyPuY2ScDugmfG7UnBGrDmS5w"}
</div>
</div>
But it's likely to fail sometimes

How to compare if an HTML element exists in the node array?

selectedContentWrap: HTML nodes.
htmlVarTag: is an string.
How do I check if the HTML element exists in the nodes?
The htmlVarTag is a string and don't understand how to convert it so it check again if there is a tag like that so that if there is I can remove it?
here is output of my nodes that is stored in selectedContentWrap
var checkingElement = $scope.checkIfHTMLinside(selectedContentWrap,htmlVarTag );
$scope.checkIfHTMLinside = function(selectedContentWrap,htmlVarTag){
var node = htmlVarTag.parentNode;
while (node != null) {
if (node == selectedContentWrap) {
return true;
}
node = node.parentNode;
}
return false;
}
Well if you could paste the content of selectedContentWrap I would be able to test this code, but I think this would work
// Code goes here
var checkIfHTMLinside = function(selectedContentWrap,htmlVarTag){
for (item of selectedContentWrap) {
if (item.nodeName.toLowerCase() == htmlVarTag.toLowerCase()){
return true;
}
}
return false;
}
Simplest is use angular.element which is a subset of jQuery compatible methods
$scope.checkIfHTMLinside = function(selectedContentWrap,htmlVarTag){
// use filter() on array and return filtered array length as boolean
return selectedContentWrap.filter(function(str){
// return length of tag collection found as boolean
return angular.element('<div>').append(str).find(htmlVarTag).length
}).length;
});
Still not 100% clear if objective is only to look for a specific tag or any tags (ie differentiate from text only)
Or as casually mentioned to actually remove the tag
If you want to remove the tag it's not clear if you simply want to unwrap it or remove it's content also ... both easily achieved using angular.element
Try using: node.innerHTML and checking against that
is it me or post a question on stackoverflow and 20min after test testing I figure it.,...
the answer is that in the selectedContentWrap I already got list of nodes, all I need to do i compare , so a simple if for loop will fit.
To compare the names I just need to use .nodeName as that works cross browser ( correct me if I am wrong)
Some dev say that "dictionary of tag names and anonymous closures instead" - but couldn't find anything. If anyone has this library could you please post it to the question?
here is my code.
var node = selectedContentWrap;
console.log('node that is selectedwrapper', selectedContentWrap)
for (var i = 0; i < selectedContentWrap.length; i++) {
console.log('tag name is ',selectedContentWrap[i].nodeName);
var temptagname = selectedContentWrap[i].nodeName; // for debugging
if(selectedContentWrap[i].nodeName == 'B' ){
console.log('contains element B');
}
}

Getting all occurences in a string with Javascript Regex

First of all I am not an expert on JavaScript, in fact I am newbie.
I know PHP and there are functions to get all occurences of a regex pattern preg_match() and preg_match_all().
In the internet I found many resources that shows how to get all occurences in a string. But when I do several regex matches, it looks ugly to me.
This is what I found in the internet:
var fileList = []
var matches
while ((matches = /<item id="(.*?)" href="(.*?)" media-type="(?:.*?)"\/>/g.exec(data)) !== null) {
fileList.push({id: matches[1], file: matches[2]})
}
fileOrder = []
while ((matches = /<itemref idref="(.*?)"\/>/g.exec(data)) !== null) {
fileOrder.push({id: matches[1]})
}
Is there a more elegant way other than this code?
Using regexes on html is generally held to be a bad idea, because regexes lack sufficient power to reliably match a^n b^n arbitrarily nested occurrences such as balanced parens or HTML/XML open/close tags. Its also trivially easy to get data out of the DOM in JavaScript without treating it like a string, that's what the DOM is for. For example:
let mapOfIDsToFiles = Array.from(document.querySelectorAll('item'))
.reduce((obj, item) => {
obj[item.id] = item.href;
return obj;
}, {});
This has the added advantage of being much faster, simpler, and more robust. DOM access is slow, but you'll be accessing the DOM anyway to get the HTML you run your regexes over.
Modifying built-in prototypes like String.prototype is generally held to be a bad idea, because it can cause random breakages with third-party code that defines the same function but differently, or if the JavaScript standard gets updated to include that function but it works differently.
UPDATE
If the data is already a string, you can easily turn it into a DOM element without affecting the page:
let elem = document.createElement('div')
div.innerHTML = data;
div.querySelectorAll('item'); // gives you all the item elements
As long as you don't append it to the document, its just a JavaScript object in memory.
UPDATE 2
Yes, this also works for XML but converting it to DOM is slightly more complicated:
// define the function differently if IE, both do the same thing
let parseXML = (typeof window.DOMParser != null && typeof window.XMLDocument != null) ?
xml => ( new window.DOMParser() ).parseFromString(xml, 'text/xml') :
xml => {
let xmlDoc = new window.ActiveXObject('Microsoft.XMLDOM');
xmlDoc.async = "false";
xmlDoc.loadXML(xml);
return xmlDoc;
};
let xmlDoc = parseXML(data).documentElement;
let items = Array.from(xmlDoc.querySelectorAll('item'));
Note that if the parse fails (i.e. your document was malformed) then you will need to check for the error document like so:
// check for error document
(() => {
let firstTag = xmlDoc.firstChild.firstChild;
if (firstTag && firstTag.tagName === 'parsererror') {
let message = firstTag.children[1].textContent;
throw new Error(message);
}
})();
I came up with the idea of creating a method in String.
I wrote a String.prototype that simplyfy things for me:
String.prototype.getMatches = function(regex, callback) {
var matches = []
var match
while ((match = regex.exec(this)) !== null) {
if (callback)
matches.push(callback(match))
else
matches.push(match)
}
return matches
}
Now I can get all matches with more elegant way. Also it's resembles preg_match_all() function of PHP.
var fileList = data.getMatches(/<item id="(.*?)" href="(.*?)" media-type="(?:.*?)"\/>/g, function(matches) {
return {id: matches[1], file: matches[2]}
})
var fileOrder = data.getMatches(/<itemref idref="(.*?)"\/>/g, function(matches) {
return matches[1]
})
I hope this helps you too.

Find first <a> tag whose href matches regex

I'm building a chrome extension, and one thing this extension does is to look for the first <a> tag in the current page whose href attribute matches a given regex. JS only.
I have a several solutions in mind, I tried them, but each time, the page freezes because of the solution I tried (i.e. if I comment the lines doing this logic, the pages loads correctly). So I need a fast solution.
Here is what I tried:
Solution 1: Xpath
var reg = something;
var result = document.evaluate(
'//*[local-name()="a"][contains(#href, "rss") or contains(#href, "feed")]', //first filtering
document, null, 0, null
);
var item;
while (item = result.iterateNext()) {
if (item.href.matches(reg)) // second and real filtering
return item.href;
}
Page freezes.
Solution 2: Xpath using matches()
var result = document.evaluate(
"//*[local-name()='a'][matches(#href, my_regex)]", //first filtering
document, null, 0, null
);
var item;
while (item = result.iterateNext()) {
return item.href;
}
I tried to hardcode my_regex between ''s, but I got an error in the chrome console (not a valid Xpath expression). Even putting some as simple as [matches(#href, 'rss')] gives the same error. Suspecting something related to xpath 1.0 or 2.0, but didn't investigate too long
Solution 3: document.body.innerHTML.match()
if (url = document.body.innerHTML.toString().match(reg)[0])
return url;
Page freezes.
So now I have not so many ideas left, maybe try to investigate using the xpath's match(), but that's basically all. Any thoughts from you guys?
Here's a solution that you can adapt to look for strings, regexps or both:
var string_match = "";
var regexp_match = new RegExp("www.*", "i");
var filter = {
acceptNode: function(node){
if((node.nodeType === 1) && (node.tagName === "A")){
return NodeFilter.FILTER_ACCEPT;
}
}
}
var tree_walker = document.createTreeWalker(document.body, NodeFilter.SHOW_ELEMENT, filter, false);
while(tree_walker.nextNode()){
if(tree_walker.currentNode.href === string_match){
console.log(tree_walker.currentNode);
break;
}else if(regexp_match.test(tree_walker.currentNode.href)){
console.log(tree_walker.currentNode);
break;
}
}
here's the fiddle: http://jsfiddle.net/59vFt/2/
I'm using document.TreeWalker which I think is more asynchronous that getting element tags and stuff, although that will also work.
Btw, innerHTML is terrible - try to avoid using it :P

javascript: not having to use all the IDs in a html file?

I want to use the same .js for a bunch of html pages, but not necessarily all the ID's from this .js in every single page. Right now, if I don't use one ID; no ID's are showing at all.
var yes = 'Yes';
var no = 'No';
var available = 'Available: ';
document.getElementById("001").innerHTML=available+yes;
document.getElementById("002").innerHTML=available+no;
document.getElementById("003").innerHTML=available+yes;
A html with this works fine:
<div id="001"></div>
<div id="002"></div>
<div id="003"></div>
A html with this, not so fine:
<div id="002"></div>
<div id="003"></div>
What to do to make it run even though some ID's arn't being used?
Complete noob to this, there's probably a super simple solution to it(?) - but I can't find it. Hopefully, someone here can help me - without totally bashing me, or telling me how much of a bad praxis this is and that I should rewrite it in some mega haxxor language that I havn't even heard of :D
Thanks in advance!
While I'd question why you'd need incrementing numeric IDs like that, one solution would simply be to keep an map of IDs to values, then iterate the map checking for null.
var m = {
"001":yes,
"002":no,
"003":yes
};
for (var p in m) {
var el = document.getElementById(p);
if (el) // or if (el !== null)
el.innerHTML = available + m[p];
}
The document.getElementById() function returns null if no matching element is found. null.innerHTML is an error that stops the current script executing. So you just have to test for null:
var el = document.getElementById("001");
if (el != null) el.innerHTML = available + yes;
The null test can be simplified to:
if (el) el.innerHTML = available + yes;
If element "001" is always going to be "yes", "002" is always going to be "no" and so forth then you can do this:
var results = {"001" : yes, "002" : no, "003" : yes};
var el, k;
for (k in results) {
el = document.getElementById(k);
if (el) el.innerHTML = available + results[k];
}
wrap it with if(document.getElementById("id")!==null) ?
Just wrap the whole thing in a try statement to avoid any issues and put code afterwards into a finally statement::
try{
document.getElementById("001").innerHTML=available+yes;
document.getElementById("002").innerHTML=available+no;
document.getElementById("003").innerHTML=available+yes;
//etc
}
finally{
//any other code that there is after the id stuff
}
that'll prevent errors, so if something fails, it will still continue

Categories