RangeError in libxmljs in NodeJS - javascript

I'm trying to replace lengthy XML tags with int values. Example '' as '<1>' so every tag with the name 'child' in the whole XML file will be replaces as '1'. I'm using libxmljs in NodeJS for this. So far my code is ;
var libxml = require('libxmljs');
var xml = '<?xml version="1.0" encoding="UTF-8"?>' +
'<root>' +
'<child foo="bar">' +
'<grandchild baz="fizbuzz">grandchild content</grandchild>' +
'</child>' +
'<child foo="bar1">' +
'<grandchild baz="fizbuzz">grandchild content 1</grandchild>' +
'</child>' +
'<sibling>with content!</sibling>' +
'</root>';
var xmlDoc = libxml.parseXml(xml);
var allxml = xmlDoc.root(); //store all nodes as allxml
var allNodes = xmlDoc.childNodes(); //all child nodes to array
rec(allxml);
function rec(anElement){
for (var j=0; j<allNodes.length;j++ )
{
var firstnode = allNodes[j].name(); //get tagname of the element
var findelem = xmlDoc.find(firstnode); //find similar elements with the tagname to array
var currChild = xmlDoc.child(j); //get current child element
var currnode = xmlDoc.childNodes(); // child nodes of current element to array
if (hasChild(currChild)) { // check whether this has child elements
rec(currChild.childNodes()); //if yes recall this function
}
else{ replaceCurrentTag(findelem, j);} // if no child nodes replace the name
}
}
function replaceCurrentTag(currelem, j){
for (var i=0;i<currelem.length;i++){
currelem[i].name(j.toString());
}
}
function hasChild(xmlElement){
var e = xmlElement.childNodes();
if (e.length > 0){ return true; }
else return false;
}
console.log(xmlDoc.toString());
But I'm getting this error when I run it in terminal.
/home/compaq/node_modules/libxmljs/lib/document.js:0
(function (exports, require, module, __filename, __dirname) { var bindings = r
^
RangeError: Maximum call stack size exceeded
What is that I'm doing wrong in here.
Please help.
Thank you

Your rec function iterates over allNodes, rather than the some subset of the nodes (probably the children of anElement). Combined with recursing into rec again before calling replaceCurrentTag (which never gets called), your function continues to call rec until you fill the call stack.

Related

Cannot get child element from parsed XML javascript (google script)

I am trying to get child node, but it prints null.
My code:
function findFile(ICO){
var fileName = ICO + '.xml';
const folderFiles = DriveApp.getFolderById('folderID').getFiles();
while (folderFiles.hasNext()) {
var folderFile = folderFiles.next();
if(folderFile.getName() == fileName){
return folderFile.getId();
break;
}
}
}
function filesearch(ICO){
var fileId = findFile(ICO);
var fileFound = DriveApp.getFileById(fileId).getBlob().getDataAsString();
var rawXml = XmlService.parse(fileFound);
return rawXml;
}
function parseXML(){
var text_ICO_txt = '27074358';
var docXml = filesearch(text_ICO_txt);
var root = docXml.getRootElement();
Logger.log(root);
var child1 = root.getChild('Ares_odpovedi');
Logger.log(child1);
}
It prints:
[Element: <are:Ares_odpovedi [Namespace: http://wwwinfo.mfcr.cz/ares/xml_doc/schemas/ares/ares_answer_vreo/v_1.0.0]/>]
null
So I tried different variation of Ares_odpovedi (including the whole text after Element:), but child node is null.
XML file:
<are:Ares_odpovedi xmlns:are="http://wwwinfo.mfcr.cz/ares/xml_doc/schemas/ares/ares_answer_vreo/v_1.0.0" odpoved_datum_cas="2022-05-09T15:00:10" odpoved_pocet="1" odpoved_typ="Vypis_VREO" vystup_format="XML" xslt="klient" validation_XSLT="http://wwwinfo.mfcr.cz/ares/xml_doc/schemas/ares/ares_odpovedi.xsl" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://wwwinfo.mfcr.cz/ares/xml_doc/schemas/ares/ares_answer_vreo/v_1.0.0 http://wwwinfo.mfcr.cz/ares/xml_doc/schemas/ares/ares_answer_vreo/v_1.0.0/ares_answer_vreo.xsd" Id="ares">
<are:Odpoved>
<are:Pomocne_ID>0</are:Pomocne_ID>
<are:Vysledek_hledani>
<are:Kod>1</are:Kod>
</are:Vysledek_hledani>
<are:Pocet_zaznamu>1</are:Pocet_zaznamu>
<are:Vypis_VREO>
<are:Uvod>
<are:Nadpis>Výpis z veřejného rejstříku v ARES - elektronický opis</are:Nadpis>
<are:Aktualizace_DB>2022-05-09</are:Aktualizace_DB>
<are:Datum_vypisu>2022-05-09</are:Datum_vypisu>
<are:Cas_vypisu>15:00:09</are:Cas_vypisu>
<are:Typ_vypisu>aktualni</are:Typ_vypisu>
</are:Uvod>
<are:Zakladni_udaje>
...
Could you, please, hint me, how to deal with it?
Thank you in advance!
This answer supposes the modification of your showing script.
When I saw your XML data, it seems that the tag of Ares_odpovedi is the root element. So, for example, when you want to retrieve the element of Odpoved, please modify it as follows.
From:
var root = docXml.getRootElement();
Logger.log(root);
var child1 = root.getChild('Ares_odpovedi');
Logger.log(child1);
To:
var root = docXml.getRootElement();
Logger.log(root);
var child1 = root.getChild('Odpoved', root.getNamespace());
Logger.log(child1);
Reference:
getChild(name, namespace)

querySelector returns 'Failed to execute div is not a valid selector' when it exists in the DOM

I'm running into an issue where the 'div ID' isn't valid when I run it from the code.
However, when I document.querySelector('#div-gpt-ad\\/1594654184865-0>div') it returns the correct div ID.
Screenshot of error: https://gyazo.com/a7f1898f246bd84f28e85c2052ac60eb
The div id exists on page before executing the code, the console.log in the renderDiv function returns
Here is the code I'm trying to execute:
var slots = window.top.googletag.pubads().getSlots();
init(slots);
function init(slots) {
for (let i = 0; i < slots.length; i++) {
renderDiv(slots[i]);
}
}
function getSelectors(slot) {
var escapeCheck = slot.getSlotElementId();
if(escapeCheck.includes('/')){
let placeHold = escapeCheck.replace(/\//g, '\\\\/');
return "#" + placeHold + ">div" ;
} else{
return "#" + escapeCheck + ">div";
}
}
function renderDiv(slot) {
let selector = getSelectors(slot);
console.log("Selector:" + selector)
document.querySelector(selector)
}
Fixed!
Changed let placeHold = escapeCheck.replace(/\//g, '\\\\/'); to
let placeHold =escapeCheck.replace(/\//g, "\\/").replace(/\./g, "\\.");
Something I quite don't understand yet is why the first one did not work as the string was correct that was passing through

replaceChild not inserting new child

I do not see what I am doing wrong:
var html = existingSelector.innerHTML;
var node = document.createTextNode(html);
existingSelector.parentNode.replaceChild(node, existingSelector);
existingSelector is the result of
selectedContents.querySelector("b");
Running the code correctly removes the "b" element, but does not insert the new node in its place. It simply deletes the entire node, without actually replacing it. As far as I can see, my syntax for the replaceChild() is correct though - ideas?
Edited Complete code:
this.bold = function() {
var selection = this.selection;
var selectedContents = selection.extractContents();
var existingSelector = selectedContents.querySelector("b");
if (existingSelector) {
var html = existingSelector.innerHTML;
var node = document.createTextNode(html);
console.log(node);
existingSelector.parentNode.replaceChild(node, existingSelector);
}
else {
var b = document.createElement("b");
b.appendChild(selectedContents);
selection.deleteContents();
selection.insertNode(b);
}
this.restoreSelection(selection);
}

return from JS function

basic JS question, please go easy on me I'm a newb :)
I pass 2 variables to the findRelatedRecords function which queries other related tables and assembles an Array of Objects, called data. Since findRelatedRecords has so many inner functions, I'm having a hard time getting the data Array out of the function.
As it currently is, I call showWin inside findRelatedRecords, but I'd like to change it so that I can get data Array directly out of findRelatedRecords, and not jump to showWin
function findRelatedRecords(features,evtObj){
//first relationship query to find related branches
var selFeat = features
var featObjId = selFeat[0].attributes.OBJECTID_1
var relatedBranch = new esri.tasks.RelationshipQuery();
relatedBranch.outFields = ["*"];
relatedBranch.relationshipId = 1; //fac -to- Branch
relatedBranch.objectIds = [featObjId];
facSel.queryRelatedFeatures(relatedBranch, function(relatedBranches) {
var branchFound = false;
if(relatedBranches.hasOwnProperty(featObjId) == true){
branchFound = true;
var branchSet = relatedBranches[featObjId]
var cmdBranch = dojo.map(branchSet.features, function(feature){
return feature.attributes;
})
}
//regardless of whether a branch is found or not, we have to run the cmdMain relationship query
//the parent is still fac, no advantage of the parent being branch since cmcMain query has to be run regardless
//fac - branch - cmdMain - cmdSub <--sometimes
//fac - cmdMain - cmdSub <-- sometimes
//second relationship query to find related cmdMains
var relatedQuery = new esri.tasks.RelationshipQuery();
relatedQuery.outFields = ["*"];
relatedQuery.relationshipId = 0; //fac -to- cmdMain
relatedQuery.objectIds = [featObjId];
//rather then listen for "OnSelectionComplete" we are using the queryRelatedFeatures callback function
facSel.queryRelatedFeatures(relatedQuery, function(relatedRecords) {
var data = []
//if any cmdMain records were found, relatedRecords object will have a property = to the OBJECTID of the clicked feature
//i.e. if cmdMain records are found, true will be returned; and continue with finding cmdSub records
if(relatedRecords.hasOwnProperty(featObjId) == true){
var fset = relatedRecords[featObjId]
var cmdMain = dojo.map(fset.features, function(feature) {
return feature.attributes;
})
//we need to fill an array with the objectids of the returned cmdMain records
//the length of this list == total number of mainCmd records returned for the clicked facility
objs = []
for (var k in cmdMain){
var o = cmdMain[k];
objs.push(o.OBJECTID)
}
//third relationship query to find records related to cmdMain (cmdSub)
var subQuery = new esri.tasks.RelationshipQuery();
subQuery.outFields = ["*"];
subQuery.relationshipId = 2;
subQuery.objectIds = [objs]
subTbl.queryRelatedFeatures(subQuery, function (subRecords){
//subRecords is an object where each property is the objectid of a cmdMain record
//if a cmdRecord objectid is present in subRecords property, cmdMain has sub records
//we no longer need these objectids, so we'll remove them and put the array into cmdsub
var cmdSub = []
for (id in subRecords){
dojo.forEach(subRecords[id].features, function(rec){
cmdSub.push(rec.attributes)
})
}
var j = cmdSub.length;
var p;
var sub_key;
var obj;
if (branchFound == true){
var p1 = "branch";
obj1 = {};
obj1[p1] = [cmdBranch[0].Branches]
data.push(obj1)
}
for (var i=0, iLen = cmdMain.length; i<iLen; i++) {
p = cmdMain[i].ASGMT_Name
obj = {};
obj[p] = [];
sub_key = cmdMain[i].sub_key;
for (var j=0, jLen=cmdSub.length; j<jLen; j++) {
if (cmdSub[j].sub_key == sub_key) {
obj[p].push(cmdSub[j].Long_Name);
}
}
data.push(obj);
}
showWin(data,evtObj) <---this would go away
})
}
//no returned cmdRecords; cmdData not available
else{
p = "No Data Available"
obj = {}
obj[p] = []
data.push(obj)
}
showWin(data,evtObj) <--this would go away
})
})
}
I'd like to have access to data array simply by calling
function findRelatedRecords(feature,evt){
//code pasted above
}
function newfunct(){
var newData = findRelatedRecords(feature,evt)
console.log(newData)
}
is this possible?
thanks!
Edit
Little more explanation.....
I'm connecting an Object event Listener to a Function like so:
function b (input){
dojo.connect(obj, "onQueryRelatedFeaturesComplete", getData);
obj.queryRelatedFeatures(input);
console.log(arr) //<----this doesn't work
}
function getData(relatedFeatData){
var arr = [];
//populate arr
return arr;
}
So when obj.QueryRelatedFeatures() is complete, getData fires; this part works fine, but how to I access arr from function b ?
Post Edit Update:
Due to the way that this event is being hooked up you can't simple return data from it. Returning will just let Dojo call to the next method that is hooked up to onSelectionComplete.
When init runs it is long before findRelatedRecords will ever be executed/fired by the onSelectionComplete event of the well, which is why you were seeing undefined/null values. The only way to work with this sort of system is to either 1) call off to a method like you're already doing or 2) fire off a custom event/message (technically it's still just calling off to a method).
If you want to make this method easier to work with you should refactor/extract snippets of it to make it a smaller function but contained in many functions. Also, changing it to have only one exit point at the end of the findRelatedRecords method will help. The function defined inside of subTbl.queryRelatedFeatures() would be a great place to start.
Sorry, you're kind of limited by what Dojo gives you in this case.
Pre Edit Answer:
Just return your data out of it. Everywhere where there is a showWin call just use this return.
return {
data: data,
evtObj: evtObj
}
Then your newfunct would look like this.
function newfunct(){
var newData = findRelatedRecords(feature,evt);
console.log(newData);
console.log(newData.data);
console.log(newData.evtObj);
}
If you only need that "data" object, then change your return to just return data;.
Also, start using semicolons to terminate statements.

how do you add a custom callback javascript param to a bing api callback?

The bing V2 javascript api requires a callback to work. Using jQuery to add the script block dynamically (ignoring pollution of global namespace):
function translate(text) {
var txt = "text=" + text;
var lang = "&to=fr";
var appId = "&appid=apikey"; // Add your AppId here
var func = "&oncomplete=window.translated";
$("<script><\/script>")
.attr("src", "http://api.microsofttranslator.com/V2/ajax.svc/Translate?" + txt + lang + appId + func)
.appendTo("HEAD");
}
and then using a click event on multiple elements to trigger the translation:
$(document).ready(function () {
$('a').click(function () {
var tr = $(this).parent().parent();
var txtin = tr.find('.in').text();
var out = tr.find('.out'); // would like translation inserted here
translate(txtin);
return false;
});
});
and finally the callback required by the api:
function translated(text) {
$("#translation").text(text);
}
I want to specify different elements to received the translated text, depending on what element was clicked to kick the translation of - but using the above approach I can't pass any extra params to bing, to then be returned in the callback.
How should I rewrite this to allow a click on el in row1 to put the translation in row1 and a click on an el in row2 to put the translation in row2? i.e. using the element assigned to 'out' in my click event.
The callback method does not support a state object, so you need to keep track of your objects in some global place. I've implemented a queue model to help you make it
Add the queue definition in the global variables are
var queue = new Array();
Add your 'out' object to it just before calling the service
$('a').click(function () {
var tr = $(this).parent().parent();
var txtin = tr.find('.in').text();
var out = tr.find('.out'); // would like translation inserted here
//Here it goes
queue.push(out);
////////////////
translate(txtin);
return false;
});
Append the index of your object to the text and it will be returned back to you as the service does not translate numbers. You can skip adding the index if you are not making more than one translation at a time, this is only to grant that you get the correct object in case of having some service calls slower than others.
function translate(text) {
//Here it goes
var txt = "text=" + text + " ___" + (queue.length - 1);
////////////////
var lang = "&to=fr";
//...no more changes here
}
Finally extract your object in the callback method and remove the appended index and the splitter from the translated text.
function translated(text) {
if (queue.length > 0) {
var splts = text.split(' ___')
var indx = splts[splts.length - 1];
var out = queue[indx];
//remove the out object from the queue
queue.slice(indx, indx + 1);
//remove the index number from the end of the word
text = text.substr(0, text.lastIndexOf(indx) - 4);
out.text(text);
}
}

Categories

Resources