(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; }) );