XUL. Tree.Why sub-items drops? - javascript

I have a XUL tree, which will contain some dynamic JSON tree.
I Have JSON(Generated by my Win app):
{
"Description": "",
"id": "0x7183D2AD",
"Name": "",
"Children": [{
"Description": "",
"id": "0x660452D5",
"Name": "Bookmarks menu",
"Children": [{
"Description": "",
"id": "0x32DD7955",
"Name": "Mozilla Firefox",
"Children": []
}]
},
{
"Description": "",
"id": "0x10EFAAFD",
"Name": "Bookmarks panel",
"Children": [{
"Description": "",
"id": "0x2542B587",
"Name": "123",
"Children": []
}]
},
{
"Description": "",
"id": "0x39AD4290",
"Name": "Tags",
"Children": []
},
{
"Description": "",
"id": "0x464248E7",
"Name": "unassigned bookmarks",
"Children": []
}]
I want fill the XUL tree by this data & realize structure(for example: this JSON ^^):
Bookmarks menu
Mozilla Firefox
Bookmarks panel
123
Tags
Unassigned bookmarks
I wrote JS recursive functions:
JOToTreeNode: function(RootEl,JO) {
var ti = document.createElementNS(XUL_NS,'treeitem');
ti.setAttribute("id",JO.id);
var tr = document.createElementNS(XUL_NS,'treerow');
ti.appendChild(tr);
var tc1 = document.createElementNS(XUL_NS,'treecell');
var tc2 = document.createElementNS(XUL_NS,'treecell');
tc1.setAttribute("label",JO.Name);
tc2.setAttribute("label",JO.Description);
tr.appendChild(tc1);
tr.appendChild(tc2);
if (JO.Children.length > 0) {
var child = document.createElementNS(XUL_NS,"treechildren");
ti.appendChild(child);
for (var i = 0; i < JO.Children.length; i++) {
UtilCommon.JOToTreeNode(child,JO.Children[i]);
}
};
RootEl.appendChild(ti);
}
...
UpdateTree: function(el) {
var els = el.getElementsByTagName("treechildren");
//Drop the previous tree
if(els.length > 0) {el.removeChild(els[0])};
//Get JSON tree of groups
var http = new XMLHttpRequest();
http.open("GET","http://"+this.host+":"+this.port+"/jsfolderstree",false);
http.send(null);
if (http.status == 200) {
var jtree = JSON.parse(http.responseText);
var child = document.createElementNS(XUL_NS,"treechildren");
el.appendChild(child);
for (var i = 0; i < jtree.Children.length; i++) {
UtilCommon.JOToTreeNode(child,jtree.Children[i]);
};
};
}
It works, but the tree contains only first level of nodes, e.g.:
- Bookmarks menu
- Bookmarks panel
- Tags
- Unassigned bookmarks
If I place code:
alert(JO.Name);
in UtilCommon.JOToTreeNode, I will see, that the sub-items like "123" and "Mozilla firefox" are exists, but doesn't add to tree as sub-items.
Where my mistake, how I can fix bug when sub-items doesn't attaches to the tree?
Thanks.

As per tree-XUL documentation, you should set the container=true for nested tree-items
...
if (JO.Children.length > 0) {
ti.setAttribute("container", true);
var child = document.createElementNS(XUL_NS,"treechildren");
ti.appendChild(child);
for (var i = 0; i < JO.Children.length; i++) {
UtilCommon.JOToTreeNode(child,JO.Children[i]);
}
};
...

Related

Creating nested object for a following Javascript object?

I want to build an object similar to following the structure:
record 1 {id,name,desc}
record 2 {id,name,desc}
record 3 {id,name,desc}
record 4 {id,name,desc}
Its a nested structure where the record 1 is the parent and the record 2 is the child of record 1, record 3 is the child of record 2, and so on.
I have gone through various posts in stack overflow, which suggests using methods like push, put etc which I am unable to generate correctly. Please help.
Goal
{
"8b9e235c0fe412004e938fbce1050e0f": [
{
"name": "Parent 1",
"childs": [
"caf23c95db3110100cc4bd513996195d": {
"name": "Child of Parent 1"
"childs": [
"caf23c95db3110100cc4bd513996195d": {
"name": "Child of Child 2"
}
]
}
]
}
]
}
Not sure if its the right order way but the idea is to turn the following object into a nested one:
[
{
"name": "Level 2",
"childId": "caf23c95db3110100cc4bd513996195d",
"parentId": "8b9e235c0fe412004e938fbce1050e0f",
"description": null,
"level": 1
},
{
"name": "Level 3",
"childId": "c303f495db3110100cc4bd51399619b8",
"parentId": "caf23c95db3110100cc4bd513996195d",
"description": null,
"level": 2
},
{
"name": "Level 4",
"childId": "be133895db3110100cc4bd51399619ba",
"parentId": "c303f495db3110100cc4bd51399619b8",
"description": null,
"level": 3
},
{
"name": "Intrusion and Incident Response Standard Operating Procedure",
"id": "8b9e235c0fe412004e938fbce1050e0f",
"description": "blablalblablabab",
"level": 0
}
]
Using this code..
function hasChild(parent,level){
var grProcessChild = new GlideRecord('sn_compliance_policy');
grProcessChild.addQuery('parent', parent);
grProcessChild.query();
while(grProcessChild.next()){
var level = parseInt(level) + 1;
var child = {}; // object
child.name = grProcessChild.getValue('name');
child.childId = grProcessChild.getUniqueValue();
child.parentId = grProcessChild.getValue('parent');
child.description = grProcessChild.getValue('description');
child.level = level;
arrData.push(child);
hasChild(grProcessChild.getUniqueValue(),level);
}
}
var arrData = []; // array
var grProcess = new GlideRecord('sn_compliance_policy');
grProcess.addQuery('sys_id','8b9e235c0fe412004e938fbce1050e0f');
grProcess.query();
while(grProcess.next()){
var root = {}; // object
root.name = grProcess.getValue('name');
root.id = grProcess.getUniqueValue();
root.description = grProcess.getValue('description');
root.level = 0;
hasChild(grProcess.getUniqueValue(),root.level);
arrData.push(root);
}
If you are using lodash Lodash in that _.reduceRight method will be helpful.
Here is a JSFiddle proving that it's working.

Converting list of classes with full names within HTML to JSON

I have an ASP.NET MVC 4 application where a list of classes, with their full namespaces, are bound to a listbox. This generates HTML as follows:
<select id="myclasses">
<option>Api.Domain.Interfaces.MyClass1</option>
<option>Api.Domain.Interfaces.MyClass2</option>
<option>Api.Domain.Interfaces.MyClass3</option>
<option>Api.Domain.Models.MyModel1</option>
<option>Api.Domain.Models.MyModel2</option>
<option>Api.Infrastructure.Repositories.MyRepo1</option>
<option>Api.Infrastructure.Repositories.MyRepo2</option>
</select>
I would like to use JavaScript/jQuery to generate a JSON representation of this HTML stored in a variable as follows:
var classes =
[{ "text": "Api", "children":
[{ "text": "Domain", "children":
[{ "text": "Interfaces", "children":
[{ "text": "MyClass1" }, { "text": "MyClass2" }, { "text": "MyClass3" }]
},
{ "text": "Models", "children":
[{ "text": "MyModel1" }, { "text": "MyModel2" }]
}]
},
{ "text": "Infrastructure", "children":
[{ "text": "Repositories", "children":
[{ "text": "MyRepo1" }, { "text": "MyRepo2" }]
}]
}]
}];
The fully qualified class names are already in a tree-like structure, so I would imagine that there should be a fairly easy way to achieve what I'm trying to do. Should I be getting the inner HTML of the element using $("#myclasses").html() and then performing string manipulations, or is there a simpler way?
Here is a try with Jquery, though you'll have an empty list of children for the last element. Could be modified if that's a problem.
I added multi selection also to give a shot.
http://jsfiddle.net/c8e7kkhh/1/
$(function() {
$("#button").click(function(){
var output = [];
var input = $( "#myclasses option:selected" );
for (var i = 0; i < input.length; i++) {
var chain = input[i].text.split(".");
var currentNode = output;
for (var j = 0; j < chain.length; j++) {
var wantedNode = chain[j];
var lastNode = currentNode;
for (var k = 0; k < currentNode.length; k++) {
if (currentNode[k].text == wantedNode) {
currentNode = currentNode[k].children;
break;
}
}
// If we couldn't find an item in this list of children
// that has the right name, create one:
if (lastNode == currentNode) {
var newNode = currentNode[k] = {text: wantedNode, children:[]};
currentNode = newNode.children;
}
}
}
$("#result").html(JSON.stringify(output));
});
});

filter result using 2 JSON

This is my saved localstorage,
[{"industry_Id":1,"merchant_id":2}]
I want to filter below result, to get HP.
{
"industries": [
{
"id": 1,
"name": "oil and gas",
"merchant": [
{
"id": 1,
"name": "ABC",
},
{
"id": 2,
"name": "DEF",
},
{
"id": 3,
"name": "GHJ",
}
]
},
{
"id": 2,
"name": "IT",
"merchant": [
{
"id": 1,
"name": "Apple",
},
{
"id": 2,
"name": "HP",
},
{
"id": 3,
"name": "Google",
}
]
}
]
}
I thought of using multiple $.each but it have to iterate few times and it's quite redundant.
I would prefer using Javascript for loop, that way you can skip iterating over every object once required element is found.
Without jQuery (using for)
var i, j, merchant = null;
for(i = 0; i < data['industries'].length; i++){
if(data['industries'][i]['id'] == arg[0]['industry_Id']){
for(j = 0; j < data['industries'][i]['merchant'].length; j++){
if(data['industries'][i]['merchant'][j]['id'] == arg[0]['merchant_id']){
merchant = data['industries'][i]['merchant'][j];
break;
}
}
if(merchant !== null){ break; }
}
}
With jQuery (using $.each)
var merchant_found = null;
$.each(data['industries'], function(i, industry){
if(industry['id'] == arg[0]['industry_Id']){
$.each(industry['merchant'], function(i, merchant){
if(merchant['id'] == arg[0]['merchant_id']){
merchant_found = merchant;
}
return (!merchant_found);
});
}
return (!merchant_found);
});
var arg = [{"industry_Id":1,"merchant_id":2}];
var data = {
"industries": [
{
"id": 1,
"name": "oil and gas",
"merchant": [
{
"id": 1,
"name": "ABC",
},
{
"id": 2,
"name": "DEF",
},
{
"id": 3,
"name": "GHJ",
}
]
},
{
"id": 2,
"name": "IT",
"merchant": [
{
"id": 1,
"name": "Apple",
},
{
"id": 2,
"name": "HP",
},
{
"id": 3,
"name": "Google",
}
]
}
]
};
var i, j, merchant = null;
for(i = 0; i < data['industries'].length; i++){
if(data['industries'][i]['id'] == arg[0]['industry_Id']){
for(j = 0; j < data['industries'][i]['merchant'].length; j++){
if(data['industries'][i]['merchant'][j]['id'] == arg[0]['merchant_id']){
merchant = data['industries'][i]['merchant'][j];
break;
}
}
if(merchant !== null){ break; }
}
}
console.log(merchant);
document.writeln("<b>Without jQuery:</b><br>");
document.writeln((merchant !== null) ? "Found " + merchant['name'] : "Not found");
var merchant_found = null;
$.each(data['industries'], function(i, industry){
if(industry['id'] == arg[0]['industry_Id']){
$.each(industry['merchant'], function(i, merchant){
if(merchant['id'] == arg[0]['merchant_id']){
merchant_found = merchant;
}
return (!merchant_found);
});
}
return (!merchant_found);
});
console.log(merchant_found);
document.writeln("<br><br><b>With jQuery:</b><br>");
document.writeln((merchant_found) ? "Found " + merchant_found['name'] : "Not found");
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
selectors.map(function(selector) {
return data.industries.filter(function(industry) {
return industry.id == selector.industry_Id;
})[0].merchant.filter(function(merchant) {
return merchant.id == selector.merchant_id;
})[0].name;
});
// => DEF
If you want "HP", you want industry 2, not industry 1.
.filter(...)[0] is not really optimal. You could use .find(...), but that is not yet universally supported. Or you could use plain old JavaScript and write for loops instead to make it fast. Or you could use objects with ID keys instead of arrays to make lookups faster.
When it comes into a position where collection of data is what you're processing, I suggest you to take a look at underscore.js. It's not optimal choice for the best performance but it does make you code more readable and makes more sense especially when compared with loop.
Say data is a variable which stores your JSON data.
Try this:
// Given this selector criteria
var select = [{"industry_Id":1,"merchant_id":2}];
function filterByCriteria(criteria, data){
var match = [];
_.each(criteria, function(crit){
function matchIndustry(rec){ return rec.id===crit.industry_Id }
function matchMerchant(rec){ return rec.id===crit.merchant_id }
// Filter by industry id
var industry = _.first(_.where(data.industry, matchIndustry));
// Filter by merchant id
var merchant = _.where(industry.merchant, matchMerchant);
_.each(merchant, function addToMatchResult(m){
match.push(m.name);
});
});
return match;
}
var filteredData = filterByCriteria(select, data);
From snippet above, any merchants which match the search criteria will be taken to the match list. Is it more readable to you?
Do you even need numerical id's? Gets super easy when you don't.
/*
{
"industry": {
"oil and gas":{
"merchant": {
"ABC": {
"name": "ABC oil"
},
"DEF": {
"name": "DEF gas"
},
"GHJ" :{
"name": "GHJ oil and gas"
}
}
},
"IT": {
"merchant": {
"Apple" : {
"name": "Apple computers"
},
"HP": {
"name": "Hewlett Packard"
},
"Google": {
"name": "Google. Maw haw haw"
}
}
}
}
}
*/
var data = '{"industry": {"oil and gas":{"merchant": {"ABC": {"name": "ABC oil"},"DEF": {"name": "DEF gas"},"GHJ" :{"name": "GHJ oil and gas"}}},"IT": {"merchant": {"Apple" : {"name": "Apple computers"},"HP": {"name": "Hewlett Packard"},"Google": {"name": "Google. Maw haw haw"}}}}}';
data = JSON.parse(data);
var merchant = data.industry['IT'].merchant['HP'];
alert(merchant.name);
//console.log(merchant.name);

Compare two objects in jQuery and get the difference [duplicate]

This question already has answers here:
How to get the difference between two arrays in JavaScript?
(84 answers)
Closed 7 years ago.
Using jQuery I would like to compare 2 objects:
sourceArray:
var origArray = [{
"Name": "Single",
"URL": "xxx",
"ID": 123
},
{
"Name": "Double",
"URL": "yyy",
"ID": 345
},
{
"Name": "Family",
"URL": "zzz",
"ID": 567
}];
destination array
var destArray = [{
"Name": "Single",
"URL": "xxx",
"ID": 123
},
{
"Name": "Double",
"URL": "yyy",
"ID": 888
},
{
"Name": "Family",
"URL": "zzz",
"ID": 567
}];
What I would like to do, is compare the target object with the source object based on the ID and find the mis-matched entries with a description on the resultant object. So the result will look like this:
var resultArray = [{
"Name": "Double",
"URL": "yyy",
"ID": 888,
"desc": "missing in source"
},
{
"Name": "Double",
"URL": "yyy",
"ID": 345,
"desc": "missing in destination"
}];
Any quick help is really appreciated.
This isn't a good use of jQuery, but here is some vanilla javascript that does what you want.
function objDiff(array1, array2) {
var resultArray = []
array2.forEach(function(destObj) {
var check = array1.some(function(origObj) {
if(origObj.ID == destObj.ID) return true
})
if(!check) {
destObj.desc = 'missing in source'
resultArray.push(destObj)
}
})
array1.forEach(function(origObj) {
var check = array2.some(function(destObj) {
if(origObj.ID == destObj.ID) return true
})
if(!check) {
origObj.desc = 'missing in destination'
resultArray.push(origObj)
}
})
return resultArray
}
https://jsfiddle.net/9gaxsLbz/1/
If you are wanting to dedupe your array, this will work:
var merged = origArray.concat(destArray);
var unique = merged.filter(function(item) {
return ~this.indexOf(item.ID) ? false : this.push(item.ID);
}, []);
Fiddle: https://jsfiddle.net/Ljzor9c6/
If you are only wanting items that were duped, you can easily invert the condition:
var merged = origArray.concat(destArray);
var dupes = merged.filter(function(item) {
return ~this.indexOf(item.ID) ? true : !this.push(item.ID);
}, []);
You can loop through the items in the first array and put the ID's in a map, then loop through the items in the second array and remove the matching ID's and add the missing.
Then just loop through the map to create the objects in the resulting array:
var origArray = [{
"Name": "Single",
"URL": "xxx",
"ID": 123
},
{
"Name": "Double",
"URL": "yyy",
"ID": 345
},
{
"Name": "Family",
"URL": "zzz",
"ID": 567
}];
var destArray = [{
"Name": "Single",
"URL": "xxx",
"ID": 123
},
{
"Name": "Double",
"URL": "yyy",
"ID": 888
},
{
"Name": "Family",
"URL": "zzz",
"ID": 567
}];
var map = {};
for (var i = 0; i < origArray.length; i++) {
map[origArray[i].ID] = 'source';
}
for (var i = 0; i < destArray.length; i++) {
var id = destArray[i].ID;
if (id in map) {
delete map[id];
} else {
map[id] = 'destination';
}
}
var resultArray = [];
for (key in map) {
var arr = map[key] == 'source' ? origArray : destArray;
for (var i = 0; arr[i].ID != key; i++) ;
resultArray.push({
Name: arr[i].Name,
URL: arr[i].URL,
ID: arr[i].ID,
desc: 'missing in ' + map[key]
});
}
// show result in StackOverflow snippet
document.write(JSON.stringify(resultArray));
var result = [];
for(var i = 0; i < oa.length; i++) {
var idx = mIndexOf(oa[i].ID);
if(idx > -1) {
oa.splice(i, 1);
da.splice(idx, 1);
}
}
for(var i = 0; i < oa.length; i++) {
var ln = result.length;
result[ln] = oa[i];
result[ln].desc = "missing in destination";
}
for(var i = 0; i < da.length; i++) {
var ln = result.length;
result[ln] = da[i];
result[ln].desc = "missing in origin";
}
function mIndexOf(id) {
for(var i = 0; i < oa.length; i++)
if(oa[i].ID == id)
return i;
return -1;
}
console.log(result);
0: Object
ID: 345
Name: "Double"
URL: "yyy"
desc: "missing in destination"
1: Object
ID: 888
Name: "Double"
URL: "yyy"
desc: "missing in origin"
jsfiddle DEMO
For things like this, you should use lodash. With lodash you can just do this:
var resultArray = _.defaults(destArray, origArray);

How to add a node to a child object

I want to insert an object say-{id:parentid+appendvalue} in each of children array using javascript/jquery. Here is my sample data:
var obj={
"name": "root",
"id":12,
"children": [
{
"name": "child-1",
"children": [
{
"name": "inner-child"
}
]
},
{
"name": "child-2"
}
]
};
Each object is of the format {name,id,children[]}.Here , I want to insert {id:parentid+".0"} recursively to all children nodes so that , I would get the following output.
var obj={
"name": "root",
**"id":12**,
"children": [
{
"name": "child-1",
**"id": 12.0**
"children": [
{
"name": "inner-child",
**"id" : 12.0.0**
}
]
},
{
"name": "child-2",
**"id": 12.1**
}
]
};
Please note the children object is not restricted to any level. Please help.
You could try something like this:
for(var i = 0; i < obj.children.length; i++)
{
var parentIDString = parentid + ".0";
for(var j = 0; j < i; j++)
{
parentIDString += ".0";
}
obj.children[i]['id'] = parentIDString;
}

Categories

Resources