(function(factory) {
"use strict";
if (typeof define === 'function' && define.amd) {
define(['jquery'], factory);
} else if (window.jQuery && !window.jQuery.fn.TreeFromJson) {
factory(window.jQuery);
}
}
(function($) {
'use strict';
var TreeFromJson = function(container, data, options) {
this.container = container;
this._default_options = {
margin: {top: 20, right: 20, bottom: 20, left: 20},
width: container.width() > 800 ? container.width()/2 : 800,
height: container.height() > 800 ? container.height()/2 : 800,
treeNodes : {
width: 3,
depth: 5
},
maxCharDisplay: 20,
itemColors: ['#fc440f', '#a5e12e', '#5d2e8c', '#2ec4b6', '#65524d', '#adcad6', '#99c24d'],
duration: 500,
interaction: true,
toBeMapped: []
};
this.options = $.extend({}, this._default_options, options);
this.data = data;
this.treeData = [this.create_tree(data, '', this.options.treeNodes.depth, this.options.treeNodes.depth, this.options.treeNodes.width)];
this.letterWidth = 8;
this.treeDiv = $('
');
this.container.append(
$('').append(this.treeDiv)
);
this.width = this.options.width - this.options.margin.right - this.options.margin.left,
this.height = this.options.height - this.options.margin.top - this.options.margin.bottom;
this.itemColors = new Map();
this.mappingDomTable;
this.currentPicking;
this.currentPickingCell;
this.i = 0
this.root;
this.tree = d3.layout.tree()
.size([this.height, this.width]);
this.diagonal = d3.svg.diagonal()
.projection(function(d) { return [d.y, d.x]; });
this.svg = d3.select(this.treeDiv[0]).append("svg")
.attr("width", this.width + this.options.margin.right + this.options.margin.left)
.attr("height", this.height + this.options.margin.top + this.options.margin.bottom)
.append("g")
.attr("transform", "translate(" + this.options.margin.left + "," + this.options.margin.top + ")");
this.root = this.treeData[0];
this.root.x0 = this.height / 2;
this.root.y0 = 0;
if (this.options.toBeMapped.length > 0 ) {
this.instructions = {};
var that = this;
this.options.toBeMapped.forEach(function(item, index) {
that.instructions[item] = [];
that.itemColors.set(item, that.options.itemColors[index]);
});
// draw mapping table
this.draw_mapping_table();
this.set_current_mapping_item();
}
this.jsonDivIn = $('');
this.treeDiv.append(this.jsonDivIn);
var j = this.syntaxHighlightJson(this.data);
this.jsonDivIn.html(j);
if (this.options.interaction) {
this.treeDivResult = $('');
this.jsonDivOut = $('');
this.treeDivResult.append(this.jsonDivOut);
this.container.children().append(
this.treeDivResult
);
this.update_result_tree();
}
this.update(this.root);
}
TreeFromJson.prototype = {
constructor: TreeFromJson,
update: function(source) {
var that = this;
// Compute the new tree layout.
var nodes = this.tree.nodes(this.root).reverse(),
links = this.tree.links(nodes);
// Compute depth size based on the link name
var maxSizePerDepth = [];
nodes.forEach(function(d) {
let m = maxSizePerDepth[d.depth] !== undefined ? maxSizePerDepth[d.depth] : 0;
let text = that.adjust_text_length(d.linkname).length;
let size = d.linkname !== undefined ? text : 0;
maxSizePerDepth[d.depth] = size > m ? size : m;
});
// add previous level together
for (var i=1; i simulate label click
let source = clicked.data()[0];
let target = clicked.data()[0].children[0];
var resL = this.svg.selectAll("path.link").filter(function(d) {
return d.source.id == source.id && d.target.id == target.id;
});
that.clickLabel(resL.data()[0]);
return;
} else {
res = d3.selectAll(".node circle")
.filter(function(d) {
return d.parent !== null && d.parent.id == clicked.data()[0].id;
});
}
}
res.data().forEach(function(elem) {
if (elem.picked !== undefined && elem.picked != '') {
// alert || repick conflicting ????
console.log('Possible collision with '+elem.picked);
//alert('Possible collision with '+elem.picked);
}
elem.picked = that.currentPicking;
});
res.style('fill', itemColor)
.style('fill-opacity', 0.85);
// find all paths
var paths = [];
var nodes = d3.selectAll(".node circle").filter(
function(d) { return d.picked == that.currentPicking;}
);
nodes.data().forEach(function(d, i) {
paths[i] = that.find_full_path(d, []);
});
var instructions = this.compute_mapping_instructions(paths);
this.add_instruction(instructions);
},
clickLabel: function(d) {
var u_id = d.source.id + '-' + d.target.id;
var l_id = '#'+u_id;
var that = this;
var o_depth = d.source.depth;
var dest_depth = d.target.depth;
var c_id = d.source.id;
var c_index; // no index as the index is the label itself
var itemColor = this.itemColors.get(this.currentPicking);
this.reset_selected();
// select all labels matching the clicked element
var resRect = this.svg.selectAll(".rectText")
.filter(function(d) {
if (d.depth == 0) {
return false;
}
var c1 = d.source.depth == o_depth;
return c1;
});
var resText = this.svg.selectAll(".linkText")
.filter(function(d) {
if (d.depth == 0) {
return false;
}
var c1 = d.source.depth == o_depth;
return c1;
});
resRect.data().forEach(function(elem) {
if (elem.picked !== undefined && elem.picked != '') {
// alert || repick conflicting ????
console.log('Possible collision with '+elem.picked);
//alert('Possible collision with '+elem.picked);
}
elem.picked = that.currentPicking;
});
resRect.style('fill', itemColor)
resText.style('fill', that.should_invert_text_color(itemColor) ? 'white' : 'black');
// find all paths
var paths = [];
var nodes = that.svg.selectAll(".node circle").filter(
function(d) { return d.depth == dest_depth;}
);
nodes.data().forEach(function(d, i) {
paths[i] = that.find_full_path(d, []);
});
var instructions = this.compute_mapping_instructions(paths);
this.add_instruction(instructions);
},
reset_selected: function() {
var that = this;
var resNode = that.svg.selectAll(".node circle")
.filter(function(d) {
return d.picked == that.currentPicking;
});
resNode.style('fill', 'white')
.style('fill-opacity', 1.00);
resNode.data().forEach(function(elem) {
elem.picked = '';
});
var resLabel = that.svg.selectAll(".rectText")
.filter(function(d) {
return d.picked == that.currentPicking;
});
resLabel.style('fill', 'white')
.style('fill-opacity', 1.00);
resLabel.data().forEach(function(elem) {
elem.picked = '';
});
this.add_instruction('');
},
compute_mapping_instructions: function(d) {
var mapping = [];
for (var i=0; i');
var thead = $('')
var tbody = $('')
var row1 = $('
');
var row2 = $('
');
this.options.toBeMapped.forEach(function(item, index) {
var itemColor = that.options.itemColors[index];
var cellH = $(''+item+' | ');
var cellB = $(' | ');
cellH.click(function() { that.set_current_mapping_item(item); });
cellB.click(function() { that.set_current_mapping_item(item); });
that.set_color(cellH, itemColor);
that.set_color(cellB, itemColor);
row1.append(cellH);
row2.append(cellB);
});
thead.append(row1);
tbody.append(row2);
this.mappingDomTable.append(thead);
this.mappingDomTable.append(tbody);
this.fillValueDomInput = $('');
var configDiv = $('')
.append($(''))
.append(this.fillValueDomInput);
var div = $('');
div.append(this.mappingDomTable);
div.append(configDiv);
this.container.prepend(div);
this.fillValueDomInput.on('input', function() {
that.update_result_tree();
});
},
set_color: function(item, color) {
item.css('background-color', color);
if (this.should_invert_text_color(color)) {
item.css('color', 'white');
} else {
item.css('color', 'black');
}
},
should_invert_text_color: function(color) {
var colorS = color.replace('#', '');
var r = parseInt('0x'+colorS.substring(0,2));
var g = parseInt('0x'+colorS.substring(2,4));
var b = parseInt('0x'+colorS.substring(4,6));
var avg = ((2 * r) + b + (3 * g))/6;
if (avg < 128) {
return true;
} else {
return false;
}
},
// if name is empty, select first item not having instructions
set_current_mapping_item: function(name) {
if (name === undefined) {
for (var entry of this.options.toBeMapped) {
if (this.instructions[entry].length == 0) {
name = entry;
break;
}
}
if (name === undefined) { // all items have a mapping, do nothing
return;
}
}
this.mappingDomTable.find('td').addClass('grey');
this.mappingDomTable.find('td').removeClass('picking');
var cell = this.mappingDomTable.find('#'+name+'Cell');
var itemColor = this.itemColors.get(name);
cell.removeClass('grey');
this.currentPickingCell = cell;
this.currentPicking = name;
},
add_instruction: function(instructions) {
this.instructions[this.currentPicking] = instructions;
this.currentPickingCell.text(instructions.toString());
this.set_current_mapping_item();
this.update_result_tree();
},
// destroy and redraw
update_result_tree: function() {
var options = {
interaction: false
};
//var result = new $.proxyMapper(this.instructions, this.data, {});
var pm_options = {
fillValue: this.fillValueDomInput.val()
};
var adjustedInstructions = this.adjust_instruction();
var result = new $.proxyMapper(adjustedInstructions, this.data, pm_options);
this.treeDivResult[0].innerHTML = '';
new TreeFromJson(this.treeDivResult, result, options);
},
adjust_instruction: function() {
var adjustedInstructions = $.extend(true, {}, this.instructions);
adjustedInstructions.index = {};
var matchingIndex = 0;
var l = this.instructions.labels;
var v = this.instructions.values;
var d = this.instructions.dates;
// label & value
if (l.length != 0 && v.length != 0) {
var smaller_array = v.length < l.length ? v : l;
for (var i=0; i this.options.maxCharDisplay) {
text += '...';
}
return text;
},
create_tree: function(root, linkname, depth, maxDepth, maxWidth) {
if (depth == 0) {
return;
}
var child = {
parent: null,
linkname: linkname
};
if (Array.isArray(root)) {
child.children = [];
for (var node of root.slice(0, maxWidth)) {
child.children.push(this.create_tree(node, '', depth-1, maxDepth, maxWidth));
}
if (root.length > maxWidth) {
var addNode = {};
var remaining = root.length - maxWidth;
addNode.name = ''+remaining+'...';
addNode.parent = null;
addNode.additionalNode = true;
child['children'].push(addNode);
}
} else if (this.isObject(root)) {
child.children = [];
var i = 0;
for (var k in root) {
if (i > maxWidth) {
break;
}
var node = root[k];
child.children.push(this.create_tree(node, k, depth-1, maxDepth, maxWidth));
i++;
}
if (Object.keys(root).length > maxWidth) {
var addNode = {};
var remaining = root.length - maxWidth;
addNode.name = ''+remaining+' ...';
addNode.parent = null;
addNode.additionalNode = true;
child.children.push(addNode);
}
} else {
child.name = root;
}
return child;
},
syntaxHighlightJson: function(json) {
if (typeof json == 'string') {
json = JSON.parse(json);
}
json = JSON.stringify(json, undefined, 2);
json = json.replace(/&/g, '&').replace(//g, '>').replace(/(?:\r\n|\r|\n)/g, '
').replace(/ /g, ' ');
return json.replace(/("(\\u[a-zA-Z0-9]{4}|\\[^u]|[^\\"])*"(\s*:)?|\b(true|false|null)\b|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?)/g, function (match) {
var cls = 'json_number';
if (/^"/.test(match)) {
if (/:$/.test(match)) {
cls = 'json_key';
} else {
cls = 'json_string';
}
} else if (/true|false/.test(match)) {
cls = 'json_boolean';
} else if (/null/.test(match)) {
cls = 'json_null';
}
return '' + match + '';
});
}
}
$.treeFromJson = TreeFromJson;
$.fn.treeFromJson = function(data, option) {
var pickerArgs = arguments;
var tfj;
this.each(function() {
var $this = $(this),
inst = $this.data('treeFromJson'),
options = ((typeof option === 'object') ? option : {});
if ((!inst) && (typeof option !== 'string')) {
tfj = new TreeFromJson($this, data, options);
$this.data('treeFromJson', tfj);
} else {
if (typeof option === 'string') {
inst[option].apply(inst, Array.prototype.slice.call(pickerArgs, 1));
}
}
});
return tfj;
}
$.fn.treeFromJson.constructor = TreeFromJson;
})
);