Browse Source

added graphs, merge

Piotr Labudda 7 years ago
parent
commit
191aaef78c

File diff suppressed because it is too large
+ 0 - 0
theme/assets/css/styles.css


+ 301 - 0
theme/assets/js/graph/csaladenes/sankey/sankey-init.js

@@ -0,0 +1,301 @@
+/*This software is released under the MIT License
+MIT License 2014 Denes Csala http://www.csaladen.es
+The following software uses the javascript frameworks below,
+all of which are distributed under the MIT or GNU/GPL license:
+D3.js http://d3js.org/  data-oriented javascript framework.
+	- Sankey plugin http://bost.ocks.org/mike/sankey/ for D3.js (modified) by Mike Bostock,
+	  which is based on the initial version http://tamc.github.io/Sankey/ by Thomas Counsell.
+	  I have incorporated the ability to render Sankey cycles, as pioneered by https://github.com/cfergus
+	- Dragdealer.js href="http://skidding.github.io/dragdealer/ by Ovidiu Chereches
+*/
+
+//<!--DATA ENTRY-->
+
+nodesform=d3.select("#nodes-form");
+function addnode() {
+	var a=nodesform.append("div");
+	a.text(nodesform[0][0].children.length-1+' ');
+	a.append("input").attr("value",'{"name":"New Node"}');
+}
+function removenode() {
+	nodesform[0][0].children[nodesform[0][0].children.length-1].remove("div")
+}
+linksform=d3.select("#links-form");
+function addlink() {
+	linksform.append("div").append("input").attr("value",'{"source":0,"target":1,"value":0.52}');
+}
+function removelink() {
+	linksform[0][0].children[linksform[0][0].children.length-1].remove("div")
+}
+function draw() {
+	change(data);
+}
+function save(){
+	d3.select('#save').style('z-index',100).transition().style('opacity',0.9);
+	st='{"sankey":{"nodes":['
+	for (i = 0; i < nodesform[0][0].children.length; i++) {
+		st=st+nodesform[0][0].children[i].children[0].value+',';
+	}
+	st=st.substring(0, st.length - 1)+'],"links":[';
+	for (i = 0; i < linksform[0][0].children.length; i++) {
+		st=st+linksform[0][0].children[i].children[0].value+',';
+	}
+	st = st.substring(0, st.length - 1)+']},"params":['+densityslider.value.current[0]+','+opacityslider.value.current[0]+','+labelformat+','+labeltextformat+','+showlinkcount+']';
+	if (document.getElementById("fixedlayout").checked){
+		var coords=[]
+		sankey.nodes().forEach(function(d){
+			coords.push([d.x,d.y])
+		})
+		st=st+',"fixedlayout":'+JSON.stringify(coords);
+	}
+	st=st+'}';
+	d3.select('#savetext').text(st);
+}
+function load(){
+	d3.select('#load').style('z-index',100).transition().style('opacity',0.9);
+}
+function loadsubmit(){
+	d3.select('#load').transition().style('opacity',0).style('z-index',-1);
+	var rawtext=d3.select('#load')[0][0].children[1].value;
+	if (rawtext!="") {
+		//parse data
+		var rawdata=JSON.parse(rawtext);
+		if ("sankey" in rawdata) {
+			var newdata=rawdata.sankey;
+		}
+		else {
+			var newdata=rawdata;
+		}
+		var loadtext=JSON.stringify(newdata)
+		//remove existing node entry boxes
+		var n=nodesform[0][0].children.length;
+		for (i = 0; i < n; i++) {
+			nodesform[0][0].children[0].remove("div");
+		}
+		//remove existing link entry boxes
+		var n=linksform[0][0].children.length;
+		for (i = 0; i < n; i++) {
+			linksform[0][0].children[0].remove("div");
+		}
+		//add new node entry boxes
+		var newdata2=JSON.parse(loadtext.substring(loadtext.indexOf('"nodes":[')+8, loadtext.indexOf('"links":[')-1));
+		for (i = 0; i < newdata2.length; i++) {
+			var a=nodesform.append("div");
+			a.text(nodesform[0][0].children.length-1+' ');
+			a.append("input").attr("value",JSON.stringify(newdata2[i]));
+		}
+		//add new link entry boxes
+		var newdata2=JSON.parse(loadtext.substring(loadtext.indexOf('"links":[')+8, loadtext.length - 1))
+		for (i = 0; i < newdata2.length; i++) {
+			linksform.append("div").append("input").attr("value",JSON.stringify(newdata2[i]));
+		}
+		//set parameters
+		if ("fixedlayout" in rawdata) {
+			fixedlayout=document.getElementById("ignorelayout").checked?[]:rawdata.fixedlayout;
+		} else {
+			fixedlayout=[];
+		}
+		if ("params" in rawdata) {
+			labelformat=rawdata.params[2];
+			labeltextformat=rawdata.params[3];
+			if (rawdata.params.length>4) showlinkcount=rawdata.params[4];
+			else showlinkcount=0;
+			document.getElementById("vlabel").checked=(labelformat==0)?true:false;
+			document.getElementById("tlabel").checked=(labeltextformat==0)?true:false;
+			document.getElementById("clabel").checked=(showlinkcount==1)?true:false;
+			densityslider.setValue(rawdata.params[0]);
+			opacityslider.setValue(rawdata.params[1]);
+		}
+		else {
+			change(newdata);
+		}
+	}
+}
+
+//<!--SANKEY DIAGRAM-->
+
+var parallelrendering=false;
+var minnodewidth = 50;
+var padding = 28;
+var labelformat = 0;
+var labeltextformat = 0;
+var showlinkcount = 0;
+var paddingmultiplier = 100;
+var lowopacity = 0.3;
+var highopacity = 0.7;
+var fixedlayout=[];
+var format2Number = d3.format(",.2f"),
+    format1Number = d3.format(",.1f"),
+	format3Number = d3.format(",.3f"),
+	formatNumber = d3.format(",.0f"),
+    format = function(a) {
+        return formatNumber(a)
+    },color = d3.scale.category20();
+	linkformat= function(a) {
+		// if (d3.select("#ldec").node().value==0) return formatNumber(a);
+		// if (d3.select("#ldec").node().value==1) return format1Number(a);
+		// if (d3.select("#ldec").node().value==2) return format2Number(a);
+		return formatNumber(a);
+	},
+	nodeformat= function(a) {
+		// if (d3.select("#ndec").node().value==0) return formatNumber(a);
+		// if (d3.select("#ndec").node().value==1) return format1Number(a);
+		// if (d3.select("#ndec").node().value==2) return format2Number(a);
+		return formatNumber(a);
+	};
+//
+// d3.select("#ndec")
+// .on("change",draw);
+// d3.select("#ldec")
+// .on("change",draw);
+
+
+
+  d3.select("#chart").style("width", document.getElementById("chart").offsetWidth)
+  var margin = {
+          top: 10,
+          right: 10,
+          bottom: 10,
+          left: 40
+      },
+  		width = document.getElementById("chart").offsetWidth - margin.left - margin.right,
+      height = document.getElementById("chart").offsetHeight - margin.bottom;
+  var svg = d3.select("#chart").append("svg")
+  svg.append("rect").attr("x",0).attr("y",0).attr("width","100%").attr("height","100%").attr("fill","white").attr('class','background').on('mouseup', () => redraw())
+  svg=svg.attr("width", width + margin.left + margin.right).attr("height", height + margin.top + margin.bottom).append("g").attr("transform", "translate(" + margin.left + "," + margin.top + ")");
+
+  //set svg background color via rectangle trick
+  d3.select("#chart").select("svg")
+
+  var sankey = d3.sankey().nodeWidth(30).nodePadding(padding).size([width, height]);
+  var path = sankey.reversibleLink();
+  var change = function(d) {
+
+  	svg.selectAll("g").remove();
+  	sankey = d3.sankey().nodeWidth(30).nodePadding(padding).size([width, height]);
+  	sankey.nodes(d.nodes).links(d.links).layout(500);
+  	var g = svg.append("g") //link
+  		.selectAll(".link").data(d.links).enter().append("g").attr("class", "link").sort(function(j, i) {
+  			return i.dy - j.dy
+  		});
+  	var h = g.append("path") //path0
+  		.attr("d", path(0));
+  	var f = g.append("path") //path1
+  		.attr("d", path(1));
+  	var e = g.append("path") //path2
+  		.attr("d", path(2));
+  	g.attr("fill", function(i) {
+  			if (i.fill) return i.fill;
+  			else if (i.source.fill) return i.source.fill;
+  			else return i.source.color = color(i.source.name.replace(/ .*/, ""))
+  		}).attr("opacity", lowopacity).on("mouseover", function(d) {
+  			d3.select(this).style('opacity', highopacity);
+  		}).on("mouseout", function(d) {
+  			d3.select(this).style('opacity', lowopacity);
+  		}).append("title") //link
+  		.text(function(i) {
+  			return i.source.name + " → " + i.target.name + "\n" + linkformat(i.value)
+  		});
+  	var c = svg.append("g") //node
+  		.selectAll(".node").data(d.nodes).enter().append("g").attr("class", "node").attr("transform", function(i) {
+  			return "translate(" + i.x + "," + i.y + ")"
+  		}).call(d3.behavior.drag().origin(function(i) {
+  			return i
+  		}).on("dragstart", function() {
+  			this.parentNode.appendChild(this)
+  		}).on("drag", b));
+  	c.append("rect") //node
+  		.attr("height", function(i) {
+  			return i.dy
+  		}).attr("width", sankey.nodeWidth()).style("fill", function(i) {
+  			if (i.fill) return i.color = i.fill;
+  								else return i.color = color(i.name.replace(/ .*/, ""))
+  		}).style("stroke", function(i) {
+  			return d3.rgb(i.color).darker(2)
+  		}).on("mouseover", function(d) {
+  			svg.selectAll(".link").filter(function(l) {
+  				return l.source == d || l.target == d;
+  			}).transition().style('opacity', highopacity);
+  		}).on("mouseout", function(d) {
+  			svg.selectAll(".link").filter(function(l) {
+  				return l.source == d || l.target == d;
+  			}).transition().style('opacity', lowopacity);
+  		}).on("dblclick", function(d) {
+  			svg.selectAll(".link").filter(function(l) {
+  				return l.target == d;
+  			}).attr("display", function() {
+  				if (d3.select(this).attr("display") == "none") return "inline"
+  				else return "none"
+  			});
+  		}).append("title").text(function(i) {
+  			return i.name + "\n" + nodeformat(i.value)
+
+  		});
+  	c.append("text") //node
+  		.attr("x", -6).attr("y", function(i) {
+  			return i.dy / 2
+  		}).attr("dy", ".35em").attr("text-anchor", "end").attr("font-size","16px")
+  		.text(function(i) {
+  		if (labeltextformat<1){
+  				return i.name;
+  			} else {
+  				return "";
+  			}
+  		}).filter(function(i) {
+  			return i.x < width / 2
+  		}).attr("x", 6 + sankey.nodeWidth()).attr("text-anchor", "start")
+  	if (showlinkcount>0) c.append("text") //node
+  		.attr("x", -6).attr("y", function(i) {
+  			return i.dy / 2 + 20
+  		}).attr("dy", ".35em").attr("text-anchor", "end").attr("font-size","16px")
+  		.text(function(i) {
+  			return "→ "+(i.targetLinks.length)+" | "+(i.sourceLinks.length)+" →";
+  		}).filter(function(i) {
+  			return i.x < width / 2
+  		}).attr("x", 6 + sankey.nodeWidth()).attr("text-anchor", "start")
+  	c.append("text") //node
+  		.attr("x", function(i) {return -i.dy / 2})
+  		.attr("y", function(i) {return i.dx / 2 + 9})
+  		.attr("transform", "rotate(270)").attr("text-anchor", "middle").attr("font-size","23px").text(function(i) {
+  			if ((i.dy>minnodewidth)&&(labelformat<1)){
+  				return nodeformat(i.value);
+  			}
+  		}).attr("fill",function(d){
+  			return d3.rgb(d["color"]).brighter(2)
+  		}).attr("stroke",function(d){
+  			return d3.rgb(d["color"]).darker(2)
+  		}).attr("stroke-width","1px");
+  		svg.selectAll('rect')
+  		.on('mouseover', (a, b, c, d, e) => {console.log('mouseover', a, b, c, d, e)})
+  		.on('mouseout', (a, b, c, d, e) => {console.log('mouseout', a, b, c, d, e)})
+  		.on('mousedown', (a, b, c, d, e) => {console.log('mousedown', a, b, c, d, e)})
+  		.on('mouseup', a => !a.expanded && redraw({type: a.name, criteria: 'ID'}));
+
+  	function b(i) { //dragmove
+  		// if (document.getElementById("ymove").checked) {
+  			// if (document.getElementById("xmove").checked) {
+  			// 	d3.select(this).attr("transform", "translate(" + (i.x = Math.max(0, Math.min(width - i.dx, d3.event.x))) + "," + (i.y = Math.max(0, Math.min(height - i.dy, d3.event.y))) + ")")
+  			// } else {
+  			dragged = true;
+  				d3.select(this).attr("transform", "translate(" + i.x + "," + (i.y = Math.max(0, Math.min(height - i.dy, d3.event.y))) + ")")
+  		// 	}
+  		// } else {
+  		// 	if (document.getElementById("xmove").checked) {
+  		// 		d3.select(this).attr("transform", "translate(" + (i.x = Math.max(0, Math.min(width - i.dx, d3.event.x))) + "," + i.y + ")")
+  		// 	}
+  		// }
+  		sankey.relayout();
+  		f.attr("d", path(1));
+  		h.attr("d", path(0));
+  		e.attr("d", path(2))
+  	};
+  };
+  draw();
+
+
+
+function seturl(){
+exportInlineSVG(d3.select("#chart").select("svg").node(), function(data) {
+	d3.select("#pngdownload").node().href=data;
+});
+}

+ 566 - 0
theme/assets/js/graph/csaladenes/sankey/sankey.js

@@ -0,0 +1,566 @@
+d3.sankey = function() {
+  var sankey = {},
+      nodeWidth = 24,
+      nodePadding = 8,
+      size = [1, 1],
+      nodes = [],
+      links = [],
+      components = [];
+
+  sankey.nodeWidth = function(_) {
+    if (!arguments.length) return nodeWidth;
+    nodeWidth = +_;
+    return sankey;
+  };
+
+  sankey.nodePadding = function(_) {
+    if (!arguments.length) return nodePadding;
+    nodePadding = +_;
+    return sankey;
+  };
+
+  sankey.nodes = function(_) {
+    if (!arguments.length) return nodes;
+    nodes = _;
+    return sankey;
+  };
+
+  sankey.links = function(_) {
+    if (!arguments.length) return links;
+    links = _;
+    return sankey;
+  };
+
+  sankey.size = function(_) {
+    if (!arguments.length) return size;
+    size = _;
+    return sankey;
+  };
+
+  sankey.layout = function(iterations) {
+    computeNodeLinks();
+    computeNodeValues();
+
+    computeNodeStructure();
+    computeNodeBreadths();
+
+    computeNodeDepths(iterations);
+    computeLinkDepths();
+
+    return sankey;
+  };
+
+  sankey.relayout = function() {
+    computeLinkDepths();
+    return sankey;
+  };
+
+  sankey.reversibleLink = function() {
+    var curvature = .5;
+
+    // Used when source is behind target, the first and last paths are simple
+    // lines at the start and end node while the second path is the spline
+    function forwardLink(part, d) {
+      var x0 = d.source.x + d.source.dx,
+          x1 = d.target.x,
+          xi = d3.interpolateNumber(x0, x1),
+          x2 = xi(curvature),
+          x3 = xi(1 - curvature),
+          y0 = d.source.y + d.sy,
+          y1 = d.target.y + d.ty,
+          y2 = d.source.y + d.sy + d.dy,
+          y3 = d.target.y + d.ty + d.dy;
+
+      switch (part) {
+        case 0:
+          return "M" + x0 + "," + y0 + "L" + x0 + "," + (y0 + d.dy);
+
+        case 1:
+          return "M" + x0 + "," + y0
+               + "C" + x2 + "," + y0 + " " + x3 + "," + y1 + " " + x1 + "," + y1
+               + "L" + x1 + "," + y3
+               + "C" + x3 + "," + y3 + " " + x2 + "," + y2 + " " + x0 + "," + y2
+               + "Z";
+
+        case 2:
+          return "M" + x1 + "," + y1 + "L" + x1 + "," + (y1 + d.dy);
+      }
+    }
+
+    // Used for self loops and when the source is actually in front of the
+    // target; the first element is a turning path from the source to the
+    // destination, the second element connects the two twists and the last
+    // twists into the target element.
+    //
+    //
+    //  /--Target
+    //  \----------------------\
+    //                 Source--/
+    //
+    function backwardLink(part, d) {
+      var curveExtension = 30;
+      var curveDepth = 15;
+
+      function getDir(d) {
+        return d.source.y + d.sy > d.target.y + d.ty ? -1 : 1;
+      }
+
+      function p(x, y) {
+        return x + "," + y + " ";
+      }
+
+      var dt = getDir(d) * curveDepth,
+          x0 = d.source.x + d.source.dx,
+          y0 = d.source.y + d.sy,
+          x1 = d.target.x,
+          y1 = d.target.y + d.ty;
+
+      switch (part) {
+        case 0:
+          return "M" + p(x0, y0) +
+                 "C" + p(x0, y0) +
+                       p(x0 + curveExtension, y0) +
+                       p(x0 + curveExtension, y0 + dt) +
+                 "L" + p(x0 + curveExtension, y0 + dt + d.dy) +
+                 "C" + p(x0 + curveExtension, y0 + d.dy) +
+                       p(x0, y0 + d.dy) +
+                       p(x0, y0 + d.dy) +
+                 "Z";
+        case 1:
+          return "M" + p(x0 + curveExtension, y0 + dt) +
+                 "C" + p(x0 + curveExtension, y0 + 3 * dt) +
+                       p(x1 - curveExtension, y1 - 3 * dt) +
+                       p(x1 - curveExtension, y1 - dt) +
+                 "L" + p(x1 - curveExtension, y1 - dt + d.dy) +
+                 "C" + p(x1 - curveExtension, y1 - 3 * dt + d.dy) +
+                       p(x0 + curveExtension, y0 + 3 * dt + d.dy) +
+                       p(x0 + curveExtension, y0 + dt + d.dy) +
+                 "Z";
+
+        case 2:
+          return "M" + p(x1 - curveExtension, y1 - dt) +
+                 "C" + p(x1 - curveExtension, y1) +
+                       p(x1, y1) +
+                       p(x1, y1) +
+                 "L" + p(x1, y1 + d.dy) +
+                 "C" + p(x1, y1 + d.dy) +
+                       p(x1 - curveExtension, y1 + d.dy) +
+                       p(x1 - curveExtension, y1 + d.dy - dt) +
+                 "Z";
+      }
+    }
+
+    return function(part) {
+      return function(d) {
+        if (d.source.x < d.target.x) {
+          return forwardLink(part, d);
+        } else {
+          return backwardLink(part, d);
+        }
+      }
+    }
+  };
+
+  // The standard link path using a constant width spline that needs a
+  // single path element.
+  sankey.link = function() {
+    var curvature = .5;
+
+    function link(d) {
+      var x0 = d.source.x + d.source.dx,
+          x1 = d.target.x,
+          xi = d3.interpolateNumber(x0, x1),
+          x2 = xi(curvature),
+          x3 = xi(1 - curvature),
+          y0 = d.source.y + d.sy + d.dy / 2,
+          y1 = d.target.y + d.ty + d.dy / 2;
+      return "M" + x0 + "," + y0
+           + "C" + x2 + "," + y0
+           + " " + x3 + "," + y1
+           + " " + x1 + "," + y1;
+    }
+
+    link.curvature = function(_) {
+      if (!arguments.length) return curvature;
+      curvature = +_;
+      return link;
+    };
+
+    return link;
+  };
+
+  // Populate the sourceLinks and targetLinks for each node.
+  // Also, if the source and target are not objects, assume they are indices.
+  function computeNodeLinks() {
+    console.log('DBG:sankey:computeNodeLinks: 1: ', {nodes, links})
+    nodes.forEach(function(node) {
+      node.sourceLinks = [];
+      node.targetLinks = [];
+    });
+
+    links.forEach(function(link) {
+      var source = link.source,
+          target = link.target;
+      source = (typeof source === "number") ? nodes[source] : nodes.find(node => node.name === source);
+      target = (typeof target === "number") ? nodes[target] : nodes.find(node => node.name === target);
+      link.source = source
+      link.target = target
+      source.sourceLinks.push(link);
+      target.targetLinks.push(link);
+    });
+    console.log('DBG:sankey:computeNodeLinks: 2:', {nodes, links})
+  }
+
+  // Compute the value (size) of each node by summing the associated links.
+  function computeNodeValues() {
+    nodes.forEach(function(node) {
+      if (!(node.value)) //if not already given
+	  node.value = Math.max(
+        d3.sum(node.sourceLinks, value),
+        d3.sum(node.targetLinks, value)
+      );
+    });
+  }
+
+  // Take the list of nodes and create a DAG of supervertices, each consisting
+  // of a strongly connected component of the graph
+  //
+  // Based off:
+  // http://en.wikipedia.org/wiki/Tarjan's_strongly_connected_components_algorithm
+  function computeNodeStructure() {
+    var nodeStack = [],
+        index = 0;
+
+    nodes.forEach(function(node) {
+      if (!node.index) {
+        connect(node);
+      }
+    });
+
+    function connect(node) {
+      node.index = index++;
+      node.lowIndex = node.index;
+      node.onStack = true;
+      nodeStack.push(node);
+
+      if (node.sourceLinks) {
+        node.sourceLinks.forEach(function(sourceLink){
+          var target = sourceLink.target;
+          if (!target.hasOwnProperty('index')) {
+            connect(target);
+            node.lowIndex = Math.min(node.lowIndex, target.lowIndex);
+          } else if (target.onStack) {
+            node.lowIndex = Math.min(node.lowIndex, target.index);
+          }
+        });
+
+        if (node.lowIndex === node.index) {
+          var component = [], currentNode;
+          do {
+            currentNode = nodeStack.pop()
+            currentNode.onStack = false;
+            component.push(currentNode);
+          } while (currentNode != node);
+          components.push({
+            root: node,
+            scc: component
+          });
+        }
+      }
+    }
+
+    components.forEach(function(component, i){
+      component.index = i;
+      component.scc.forEach(function(node) {
+        node.component = i;
+      });
+    });
+  }
+
+  // Assign the breadth (x-position) for each strongly connected component,
+  // followed by assigning breadth within the component.
+  function computeNodeBreadths() {
+
+    layerComponents();
+
+    components.forEach(function(component, i){
+      bfs(component.root, function(node){
+        var result = node.sourceLinks
+          .filter(function(sourceLink){
+            return sourceLink.target.component == i;
+          })
+          .map(function(sourceLink){
+            return sourceLink.target;
+          });
+        return result;
+      });
+    });
+
+    var max = 0;
+    var componentsByBreadth = d3.nest()
+      .key(function(d) { return d.x; })
+      .sortKeys(d3.ascending)
+      .entries(components)
+      .map(function(d) { return d.values; });
+
+    var max = -1, nextMax = -1;
+    componentsByBreadth.forEach(function(c){
+      c.forEach(function(component){
+        component.x = max + 1;
+        component.scc.forEach(function(node){
+		  if (node.layer) node.x=node.layer;
+          else node.x = component.x + node.x;
+          nextMax = Math.max(nextMax, node.x);
+        });
+      });
+      max = nextMax;
+    });
+
+
+    nodes
+      .filter(function(node) {
+        var outLinks = node.sourceLinks.filter(function(link){ return link.source.name != link.target.name; });
+        return (outLinks.length == 0);
+      })
+      .forEach(function(node) { node.x = (node.layer)?node.x:max; })
+
+    scaleNodeBreadths((size[0] - nodeWidth) / Math.max(max, 1));
+
+    function flatten(a) {
+      return [].concat.apply([], a);
+    }
+
+    function layerComponents() {
+      var remainingComponents = components,
+          nextComponents,
+          visitedIndex,
+          x = 0;
+
+      while (remainingComponents.length) {
+        nextComponents = [];
+        visitedIndex = {};
+
+        remainingComponents.forEach(function(component) {
+          component.x = x;
+
+          component.scc.forEach(function(n) {
+            n.sourceLinks.forEach(function(l) {
+              if (!visitedIndex.hasOwnProperty(l.target.component) &&
+                   l.target.component != component.index) {
+                nextComponents.push(components[l.target.component]);
+                visitedIndex[l.target.component] = true;
+              }
+            })
+          });
+        });
+
+        remainingComponents = nextComponents;
+        ++x;
+      }
+    }
+
+    function bfs(node, extractTargets) {
+      var queue = [node], currentCount = 1, nextCount = 0;
+      var x = 0;
+
+      while(currentCount > 0) {
+        var currentNode = queue.shift();
+        currentCount--;
+
+        if (!currentNode.hasOwnProperty('x')) {
+          currentNode.x = x;
+          currentNode.dx = nodeWidth;
+
+          var targets = extractTargets(currentNode);
+
+          queue = queue.concat(targets);
+          nextCount += targets.length;
+        }
+
+
+        if (currentCount == 0) { // level change
+          x++;
+          currentCount = nextCount;
+          nextCount = 0;
+        }
+
+      }
+    }
+
+	//extra code for fixed layout - x part
+	if (fixedlayout.length>0) {
+	  sankey.nodes().forEach(function(d,i){
+		d.x=fixedlayout[i][0];
+	  })
+	}
+
+  }
+
+  function moveSourcesRight() {
+    nodes.forEach(function(node) {
+      if (!node.targetLinks.length) {
+        node.x = d3.min(node.sourceLinks, function(d) { return d.target.x; }) - 1;
+      }
+    });
+  }
+
+  function moveSinksRight(x) {
+    nodes.forEach(function(node) {
+      if (!node.sourceLinks.length) {
+        node.x = x - 1;
+      }
+    });
+  }
+
+  function scaleNodeBreadths(kx) {
+    nodes.forEach(function(node) {
+      node.x *= kx;
+    });
+  }
+
+  function computeNodeDepths(iterations) {
+    var nodesByBreadth = d3.nest()
+        .key(function(d) { return d.x; })
+        .sortKeys(d3.ascending)
+        .entries(nodes)
+        .map(function(d) { return d.values; });
+
+    initializeNodeDepth();
+    resolveCollisions();
+
+    for (var alpha = 1; iterations > 0; --iterations) {
+      relaxRightToLeft(alpha *= .99);
+      resolveCollisions();
+      relaxLeftToRight(alpha);
+      resolveCollisions();
+    }
+
+    function initializeNodeDepth() {
+      var ky = d3.min(nodesByBreadth, function(nodes) {
+        return (size[1] - (nodes.length - 1) * nodePadding) / d3.sum(nodes, value);
+      });
+
+      nodesByBreadth.forEach(function(nodes) {
+        nodes.forEach(function(node, i) {
+          node.y = i;
+          node.dy = node.value * ky;
+        });
+      });
+
+      links.forEach(function(link) {
+        link.dy = link.value * ky;
+      });
+    }
+
+    function relaxLeftToRight(alpha) {
+      nodesByBreadth.forEach(function(nodes, breadth) {
+        nodes.forEach(function(node) {
+          if (node.targetLinks.length) {
+            var y = d3.sum(node.targetLinks, weightedSource) / d3.sum(node.targetLinks, value);
+            node.y += (y - center(node)) * alpha;
+          }
+        });
+      });
+
+      function weightedSource(link) {
+        return center(link.source) * link.value;
+      }
+    }
+
+    function relaxRightToLeft(alpha) {
+      nodesByBreadth.slice().reverse().forEach(function(nodes) {
+        nodes.forEach(function(node) {
+          if (node.sourceLinks.length) {
+            var y = d3.sum(node.sourceLinks, weightedTarget) / d3.sum(node.sourceLinks, value);
+            node.y += (y - center(node)) * alpha;
+          }
+        });
+      });
+
+      function weightedTarget(link) {
+        return center(link.target) * link.value;
+      }
+    }
+
+    function resolveCollisions() {
+      nodesByBreadth.forEach(function(nodes) {
+        var node,
+            dy,
+            y0 = 0,
+            n = nodes.length,
+            i;
+
+        // Push any overlapping nodes down.
+        nodes.sort(ascendingDepth);
+        for (i = 0; i < n; ++i) {
+          node = nodes[i];
+          dy = y0 - node.y;
+          if (dy > 0) node.y += dy;
+          y0 = node.y + node.dy + nodePadding;
+        }
+
+        // If the bottommost node goes outside the bounds, push it back up.
+        dy = y0 - nodePadding - size[1];
+        if (dy > 0) {
+          y0 = node.y -= dy;
+
+          // Push any overlapping nodes back up.
+          for (i = n - 2; i >= 0; --i) {
+            node = nodes[i];
+            dy = node.y + node.dy + nodePadding - y0;
+            if (dy > 0) node.y -= dy;
+            y0 = node.y;
+          }
+        }
+      });
+    }
+
+    function ascendingDepth(a, b) {
+      return a.y - b.y;
+    }
+
+	//extra code for fixed layout - y part
+	if (fixedlayout.length>0) {
+	  sankey.nodes().forEach(function(d,i){
+		d.y=fixedlayout[i][1];
+	  })
+	}
+  }
+
+  function computeLinkDepths() {
+    nodes.forEach(function(node) {
+      if (parallelrendering) {}
+	  else node.sourceLinks.sort(ascendingTargetDepth);
+      node.targetLinks.sort(ascendingSourceDepth);
+    });
+    nodes.forEach(function(node) {
+      var sy = 0, ty = 0;
+      node.sourceLinks.forEach(function(link) {
+        link.sy = sy;
+        sy += link.dy;
+      });
+      node.targetLinks.forEach(function(link) {
+        link.ty = ty;
+        ty += link.dy;
+      });
+    });
+
+    function ascendingSourceDepth(a, b) {
+      return a.source.y - b.source.y;
+    }
+
+    function ascendingTargetDepth(a, b) {
+      return a.target.y - b.target.y;
+    }
+  }
+
+  function center(node) {
+    return node.y + node.dy / 2;
+  }
+
+  function value(link) {
+    return link.value;
+  }
+
+  return sankey;
+};

File diff suppressed because it is too large
+ 0 - 0
theme/assets/js/graph/d3.v3.min.js


+ 117 - 0
theme/assets/js/graph/parser.js

@@ -0,0 +1,117 @@
+var responseObjects = p5WFS_GetFeature();
+var object = responseObjects[0];
+
+var nodes = {};
+var nodesArray = [];
+var links = {};
+var linksArray = [];
+var instanceTypes = {};
+var dragged = false;
+
+const hasTargetArray = node => {
+  for (const prop in node)
+    if (node[prop].constructor === Array)
+      return {array: node[prop], name: prop.split(':').pop().replace(/BI_audit_/, '')};
+  return false;
+}
+
+const getLinks = (array, expansion) => {
+
+  if (array != undefined && expansion !== undefined) {
+    array.forEach(element => {
+      const rowObject = hasTargetArray(element);
+      if (rowObject) {
+        for (let i = rowObject.array.length - 2; i >= 0; i--) {
+          const target = hasTargetArray(rowObject.array[i]);
+          const source = hasTargetArray(rowObject.array[i + 1]);
+          let sourceGroup = source.name;
+          let targetGroup = target.name;
+          if (expansion && expansion.type) {
+            if (sourceGroup === expansion.type)
+              sourceGroup = source.array[0][expansion.criteria];
+            if (targetGroup === expansion.type)
+              targetGroup = target.array[0][expansion.criteria];
+          }
+          if (!links.hasOwnProperty(sourceGroup))
+            links[sourceGroup] = {};
+          if (links[sourceGroup].hasOwnProperty(targetGroup)) {
+            links[sourceGroup][targetGroup]++;
+          } else if (targetGroup) {
+            links[sourceGroup][targetGroup] = 1;
+          }
+        }
+      }
+    })
+  }
+};
+
+const getNodes = (array, expansion) => {
+
+    if (array != undefined && expansion !== undefined) {
+      array.forEach(element => {
+        const rowObject = hasTargetArray(element);
+        if (rowObject)
+          rowObject.array.forEach(objectElement => {
+            const trackNode = hasTargetArray(objectElement);
+            if (trackNode) {
+              let group = trackNode.name;
+              let expanded = false;
+              if (expansion && expansion.type && group === expansion.type) {
+                group = trackNode.array[0][expansion.criteria];
+                expanded = true;
+              }
+              nodes[group] = {expanded: expanded, type: trackNode.name};
+              instanceTypes[trackNode.array[0].ID] = group;
+            }
+          });
+      });
+    }
+
+};
+
+const parseNodes = () => {
+  for (const node in nodes) {
+    if (nodes.hasOwnProperty(node))
+      nodesArray.push({name: node, expanded: nodes[node].expanded, type: nodes[node].type});
+  }
+};
+
+const parseLinks = () => {
+  for (const link in links) {
+    if (links.hasOwnProperty(link)) {
+      for (const target in links[link]) {
+        if (links[link].hasOwnProperty(target)) {
+          linksArray.push({
+            source: link,
+            target: target,
+            value: links[link][target]
+          });
+        }
+      }
+    }
+  }
+};
+
+const parseGraph = (expansion) => {
+  nodes = {};
+  links = {};
+  nodesArray = [];
+  linksArray = [];
+  getNodes(responseObjects[0]["default_db__x3A__BI_audit_ENERGA_RUM_KONTRAHENCI_POWIAZANIA_row:BI_audit_ENERGA_RUM_KONTRAHENCI_POWIAZANIA_row"], expansion);
+  parseNodes();
+  getLinks(responseObjects[0]["default_db__x3A__BI_audit_ENERGA_RUM_KONTRAHENCI_POWIAZANIA_row:BI_audit_ENERGA_RUM_KONTRAHENCI_POWIAZANIA_row"], expansion);
+  parseLinks();
+  dataModel = data = {
+    nodes: nodesArray,
+    links: linksArray
+  };
+};
+
+const redraw = (expansion) => {
+  if (dragged)
+    return dragged = false;
+  parseGraph(expansion);
+  draw();
+};
+
+parseGraph();

+ 338 - 0
theme/assets/js/graph/sankey-init-widget.js

@@ -0,0 +1,338 @@
+(function (global) {
+
+	var dispatch = d3.dispatch("click");
+
+// ----
+
+/*This software is released under the MIT License
+
+MIT License 2014 Denes Csala http://www.csaladen.es
+
+The following software uses the javascript frameworks below,
+all of which are distributed under the MIT or GNU/GPL license:
+D3.js http://d3js.org/  data-oriented javascript framework.
+	- Sankey plugin http://bost.ocks.org/mike/sankey/ for D3.js (modified) by Mike Bostock,
+	  which is based on the initial version http://tamc.github.io/Sankey/ by Thomas Counsell.
+	  I have incorporated the ability to render Sankey cycles, as pioneered by https://github.com/cfergus
+	- Dragdealer.js href="http://skidding.github.io/dragdealer/ by Ovidiu Chereches
+*/
+
+//<!--DATA INIT-->
+
+var data={"nodes": [], "links": []}
+
+//<!--DATA ENTRY-->
+
+nodesform=d3.select("#nodes-form");
+function addnode() {
+	var a=nodesform.append("div");
+	a.text(nodesform[0][0].children.length-1+' ');
+	a.append("input").attr("value",'{"name":"New Node"}');
+}
+function removenode() {
+	nodesform[0][0].children[nodesform[0][0].children.length-1].remove("div")
+}
+linksform=d3.select("#links-form");
+function addlink() {
+	linksform.append("div").append("input").attr("value",'{"source":0,"target":1,"value":0.52}');
+}
+function removelink() {
+	linksform[0][0].children[linksform[0][0].children.length-1].remove("div")
+}
+function draw() {
+	change(data);
+}
+function save(){
+	d3.select('#save').style('z-index',100).transition().style('opacity',0.9);
+	st='{"sankey":{"nodes":['
+	for (i = 0; i < nodesform[0][0].children.length; i++) {
+		st=st+nodesform[0][0].children[i].children[0].value+',';
+	}
+	st=st.substring(0, st.length - 1)+'],"links":[';
+	for (i = 0; i < linksform[0][0].children.length; i++) {
+		st=st+linksform[0][0].children[i].children[0].value+',';
+	}
+	st = st.substring(0, st.length - 1)+']},"params":['+densityslider.value.current[0]+','+opacityslider.value.current[0]+','+labelformat+','+labeltextformat+','+showlinkcount+']';
+	if (document.getElementById("fixedlayout").checked){
+		var coords=[]
+		sankey.nodes().forEach(function(d){
+			coords.push([d.x,d.y])
+		})
+		st=st+',"fixedlayout":'+JSON.stringify(coords);
+	}
+	st=st+'}';
+	d3.select('#savetext').text(st);
+}
+function load(){
+	d3.select('#load').style('z-index',100).transition().style('opacity',0.9);
+}
+function loadsubmit(){
+	d3.select('#load').transition().style('opacity',0).style('z-index',-1);
+	var rawtext=d3.select('#load')[0][0].children[1].value;
+	if (rawtext!="") {
+		//parse data
+		var rawdata=JSON.parse(rawtext);
+		if ("sankey" in rawdata) {
+			var newdata=rawdata.sankey;
+		}
+		else {
+			var newdata=rawdata;
+		}
+		var loadtext=JSON.stringify(newdata)
+		//remove existing node entry boxes
+		var n=nodesform[0][0].children.length;
+		for (i = 0; i < n; i++) {
+			nodesform[0][0].children[0].remove("div");
+		}
+		//remove existing link entry boxes
+		var n=linksform[0][0].children.length;
+		for (i = 0; i < n; i++) {
+			linksform[0][0].children[0].remove("div");
+		}
+		//add new node entry boxes
+		var newdata2=JSON.parse(loadtext.substring(loadtext.indexOf('"nodes":[')+8, loadtext.indexOf('"links":[')-1));
+		for (i = 0; i < newdata2.length; i++) {
+			var a=nodesform.append("div");
+			a.text(nodesform[0][0].children.length-1+' ');
+			a.append("input").attr("value",JSON.stringify(newdata2[i]));
+		}
+		//add new link entry boxes
+		var newdata2=JSON.parse(loadtext.substring(loadtext.indexOf('"links":[')+8, loadtext.length - 1))
+		for (i = 0; i < newdata2.length; i++) {
+			linksform.append("div").append("input").attr("value",JSON.stringify(newdata2[i]));
+		}
+		//set parameters
+		if ("fixedlayout" in rawdata) {
+			fixedlayout=document.getElementById("ignorelayout").checked?[]:rawdata.fixedlayout;
+		} else {
+			fixedlayout=[];
+		}
+		if ("params" in rawdata) {
+			labelformat=rawdata.params[2];
+			labeltextformat=rawdata.params[3];
+			if (rawdata.params.length>4) showlinkcount=rawdata.params[4];
+			else showlinkcount=0;
+			document.getElementById("vlabel").checked=(labelformat==0)?true:false;
+			document.getElementById("tlabel").checked=(labeltextformat==0)?true:false;
+			document.getElementById("clabel").checked=(showlinkcount==1)?true:false;
+			densityslider.setValue(rawdata.params[0]);
+			opacityslider.setValue(rawdata.params[1]);
+		}
+		else {
+			change(newdata);
+		}
+	}
+}
+
+//<!--SANKEY DIAGRAM-->
+
+var parallelrendering=false;
+var minnodewidth = 50;
+var padding = 28;
+var labelformat = 0;
+var labeltextformat = 0;
+var showlinkcount = 0;
+var paddingmultiplier = 100;
+var lowopacity = 0.3;
+var highopacity = 0.7;
+var fixedlayout=[];
+var sankeyColor = d3.scale.category20();
+var sankeyNumberFormat = d3.format(",.0f");
+
+// d3.select("#ndec")
+// .on("change",draw);
+// d3.select("#ldec")
+// .on("change",draw);
+
+var margin = {
+        top: 10,
+        right: 10,
+        bottom: 10,
+        left: 40
+    };
+var width = 1000; //document.getElementById("chart").offsetWidth - margin.left - margin.right,
+var height = 500; // document.getElementById("chart").offsetHeight - margin.bottom;
+var svg = null;//d3.select("#chart").append("svg")
+//svg.append("rect").attr("x",0).attr("y",0).attr("width","100%").attr("height","100%").attr("fill","white").attr('class','background')
+//svg=svg.attr("width", width + margin.left + margin.right).attr("height", height + margin.top + margin.bottom).append("g").attr("transform", "translate(" + margin.left + "," + margin.top + ")");
+
+//set svg background color via rectangle trick
+// d3.select("#chart").select("svg")
+
+var sankey = null;
+// var sankey = d3.sankey().nodeWidth(30).nodePadding(padding).size([width, height]);
+// var path = sankey.reversibleLink();
+var change = function(d) {
+
+	svg.selectAll("g").remove();
+	sankey = d3.sankey().nodeWidth(30).nodePadding(padding).size([width, height]);
+	sankey.nodes(d.nodes).links(d.links).layout(500);
+	var g = svg.append("g") //link
+		.selectAll(".link").data(d.links).enter().append("g").attr("class", "link").sort(function(j, i) {
+			return i.dy - j.dy
+		});
+	var path = sankey.reversibleLink();
+	var h = g.append("path") //path0
+		.attr("d", path(0));
+	var f = g.append("path") //path1
+		.attr("d", path(1));
+	var e = g.append("path") //path2
+		.attr("d", path(2));
+	g.attr("fill", function(i) {
+			if (i.fill) return i.fill;
+			else if (i.source.fill) return i.source.fill;
+			else return i.source.color = sankeyColor(i.source.name.replace(/ .*/, ""))
+		}).attr("opacity", lowopacity).on("mouseover", function(d) {
+			d3.select(this).style('opacity', highopacity);
+		}).on("mouseout", function(d) {
+			d3.select(this).style('opacity', lowopacity);
+		}).append("title") //link
+		.text(function(i) {
+			return i.source.name + " → " + i.target.name + "\n" + sankeyNumberFormat(i.value)
+		});
+	var c = svg.append("g") //node
+		.selectAll(".node").data(d.nodes).enter().append("g").attr("class", "node").attr("transform", function(i) {
+			return "translate(" + i.x + "," + i.y + ")"
+		}).call(d3.behavior.drag().origin(function(i) {
+			return i
+		}).on("dragstart", function() {
+			this.parentNode.appendChild(this)
+		}).on("drag", b));
+	c.append("rect") //node
+		.attr("height", function(i) {
+			return i.dy
+		}).attr("width", sankey.nodeWidth()).style("fill", function(i) {
+			if (i.fill) return i.color = i.fill;
+								else return i.color = sankeyColor(i.name.replace(/ .*/, ""))
+		}).style("stroke", function(i) {
+			return d3.rgb(i.color).darker(2)
+		}).on("mouseover", function(d) {
+			svg.selectAll(".link").filter(function(l) {
+				return l.source == d || l.target == d;
+			}).transition().style('opacity', highopacity);
+		}).on("mouseout", function(d) {
+			svg.selectAll(".link").filter(function(l) {
+				return l.source == d || l.target == d;
+			}).transition().style('opacity', lowopacity);
+		}).on("dblclick", function(d) {
+			svg.selectAll(".link").filter(function(l) {
+				return l.target == d;
+			}).attr("display", function() {
+				if (d3.select(this).attr("display") == "none") return "inline"
+				else return "none"
+			});
+		}).append("title").text(function(i) {
+			return i.name + "\n" + sankeyNumberFormat(i.value)
+
+		});
+	c.append("text") //node
+		.attr("x", -6).attr("y", function(i) {
+			return i.dy / 2
+		}).attr("dy", ".35em").attr("text-anchor", "end").attr("font-size","16px")
+		.text(function(i) {
+		if (labeltextformat<1){
+				return i.name;
+			} else {
+				return "";
+			}
+		}).filter(function(i) {
+			return i.x < width / 2
+		}).attr("x", 6 + sankey.nodeWidth()).attr("text-anchor", "start")
+	if (showlinkcount>0) c.append("text") //node
+		.attr("x", -6).attr("y", function(i) {
+			return i.dy / 2 + 20
+		}).attr("dy", ".35em").attr("text-anchor", "end").attr("font-size","16px")
+		.text(function(i) {
+			return "→ "+(i.targetLinks.length)+" | "+(i.sourceLinks.length)+" →";
+		}).filter(function(i) {
+			return i.x < width / 2
+		}).attr("x", 6 + sankey.nodeWidth()).attr("text-anchor", "start")
+	c.append("text") //node
+		.attr("x", function(i) {return -i.dy / 2})
+		.attr("y", function(i) {return i.dx / 2 + 9})
+		.attr("transform", "rotate(270)").attr("text-anchor", "middle").attr("font-size","23px").text(function(i) {
+			if ((i.dy>minnodewidth)&&(labelformat<1)){
+				return sankeyNumberFormat(i.value);
+			}
+		}).attr("fill",function(d){
+			return d3.rgb(d["color"]).brighter(2)
+		}).attr("stroke",function(d){
+			return d3.rgb(d["color"]).darker(2)
+		}).attr("stroke-width","1px");
+
+		svg.selectAll('rect')
+		.on('mouseover', (a, b, c, d, e) => {console.log('mouseover', a, b, c, d, e)})
+		.on('mouseout', (a, b, c, d, e) => {console.log('mouseout', a, b, c, d, e)})
+		.on('mousedown', (a, b, c, d, e) => {console.log('mousedown', a, b, c, d, e)})
+		.on('mouseup', (a) => {
+			dispatch.click({
+				nativeEvent: d3.event,
+				node: a,
+				element: d3.event.srcElement
+			})
+		});
+
+	function b(i) { //dragmove
+		// if (document.getElementById("ymove").checked) {
+		// 	if (document.getElementById("xmove").checked) {
+		// 		d3.select(this).attr("transform", "translate(" + (i.x = Math.max(0, Math.min(width - i.dx, d3.event.x))) + "," + (i.y = Math.max(0, Math.min(height - i.dy, d3.event.y))) + ")")
+		// 	} else {
+		dragged = true;
+			d3.select(this).attr("transform", "translate(" + i.x + "," + (i.y = Math.max(0, Math.min(height - i.dy, d3.event.y))) + ")")
+		// 	}
+		// } else {
+		// 	if (document.getElementById("xmove").checked) {
+		// 		d3.select(this).attr("transform", "translate(" + (i.x = Math.max(0, Math.min(width - i.dx, d3.event.x))) + "," + i.y + ")")
+		// 	}
+		// }
+		// sankey.relayout();
+		// f.attr("d", path(1));
+		// h.attr("d", path(0));
+		// e.attr("d", path(2))
+	};
+};
+// draw();
+
+//<!-- SAVE FUNCTION-->
+
+d3.select("#pngdownloadwrapper")
+.on("click",function(){
+	seturl();
+	setTimeout(function(){d3.select("#pngdownload").node().click();},500);
+})
+
+function seturl(){
+	exportInlineSVG(d3.select("#chart").select("svg").node(), function(data) {
+		d3.select("#pngdownload").node().href=data;
+	});
+}
+
+// ----
+
+	renderGraph = (htmlNode, data, opts) => {
+		var opts = opts || {}
+		if (!htmlNode) throw "Missing html node";
+		if (!data) throw "Missing data";
+
+		//svg = d3.select("#chart").append("svg")
+		htmlNode.append("rect").attr("x",0).attr("y",0).attr("width","100%").attr("height","100%").attr("fill","white")
+		width = 1020
+		height = 500
+		margin = {
+			top: 10,
+			right: 10,
+			bottom: 10,
+			left: 40
+		};
+		htmlNode.attr("width", width + margin.left + margin.right).attr("height", height + margin.top + margin.bottom)
+		svg = htmlNode.append("g").attr("transform", "translate(" + margin.left + "," + margin.top + ")");
+
+		change(data);
+
+		return dispatch
+	}
+
+	global.parallelrendering = parallelrendering
+	global.fixedlayout = fixedlayout
+	global.renderGraph = renderGraph
+})(window)

+ 32 - 0
theme/assets/less/styles.less

@@ -702,3 +702,35 @@ footer {
     }
   }
 }
+
+
+/**
+ * Graph
+ */
+.smad-pracownicy-graph-view {
+  .node rect {
+    cursor: move;
+    fill-opacity: .9;
+    shape-rendering: crispEdges;
+  }
+
+  .node text {
+    pointer-events: none;
+    text-shadow: 0 1px 0 #fff;
+  }
+
+  .link {
+    fill: #000;
+    stroke: #000;
+    stroke-opacity: .2;
+    fill-opacity: .2;
+  }
+
+  .link:hover {
+    stroke-opacity: .5;
+  }
+  #chart {
+    height: calc(100vh - 32px);
+  }
+
+}

+ 9 - 0
theme/bocian.php

@@ -19,6 +19,10 @@ class Theme_bocian extends ThemeDefault {
 
 	function top() {
 		include dirname(__FILE__) . '/view/top.php';
+		//todo: demo load data for graph :DELETE line if tested
+		// UI::inlineRawJS(dirname(__FILE__) . '/demoGraph/sampleData/sampleData.js');
+		// UI::inlineRawJS(dirname(__FILE__) . '/assets/js/graph/GetFeature.js');
+		// UI::inlineRawJS(dirname(__FILE__) . '/assets/js/graph/parser.js');
 	}
 
 	function footer() {
@@ -35,6 +39,11 @@ class Theme_bocian extends ThemeDefault {
 				'URL_FETCH_BI_AUDIT_PROGRESS' => Router::getRoute('UrlAction_Bocian')->getLink('fetchProgressAjax')
 			]);
 		}
+		// graph lib
+		UI::inlineRawJS(dirname(__FILE__) . '/assets/js/graph/d3.v3.min.js');
+		UI::inlineRawJS(dirname(__FILE__) . '/assets/js/graph/csaladenes/sankey/sankey.js');
+		// UI::inlineRawJS(dirname(__FILE__) . '/assets/js/graph/csaladenes/sankey/sankey-init.js');
+		UI::inlineRawJS(dirname(__FILE__) . '/assets/js/graph/sankey-init-widget.js');
 	}
 
 	function login($data) {

+ 2 - 2
theme/view/top.php

@@ -207,7 +207,7 @@
                             <ul>
                                 <li><a href="<?php echo Request::getPathUri(); ?>index.php?_route=ViewTableAjax&namespace=default_db/BI_audit_ENERGA_RUM_KONTRAHENCI_POWIAZANIA/BI_audit_ENERGA_RUM_KONTRAHENCI_POWIAZANIA" title="zobacz raporty" class="btn btn-success">zobacz raporty</a></li>
 	                            <li><a href="<?php echo Request::getPathUri(); ?>index.php?_route=ViewTableAjax&namespace=default_db/BI_audit_operational_raport_note/BI_audit_operational_raport_note" title="Baza zgłoszeń operacyjnych i notatek" class="btn btn-success">Operac. Baza zgł.</a></li>
-	                            <li><a href="<?php echo Request::getPathUri(); ?>index.php?_route=Menu" title="Menu silnika procesy5" class="btn btn-success">Menu</a></li>
+	                            <li><a href="<?php echo Request::getPathUri(); ?>index.php?_route=menu" title="Menu silnika procesy5" class="btn btn-success">Menu</a></li>
                             </ul>
                         </div>
                     </div>
@@ -245,5 +245,5 @@
         </div>
     </div>
 </div>
-
+<p id="chart"></p>
 <!-- end:menu -->

+ 16 - 4
tools/Bocian.php

@@ -8,7 +8,7 @@ Lib::loadClass('Request');
 // index.php?_route=UrlAction_Bocian  - uruchamia defaultAction
 class RouteTool_Bocian extends RouteToolBase {
 
-public static $helpEmailTo = 'biuro@bialnet.com.pl'; // email na który zostanie wysłane zapytanie z formularza POMOCY
+public static $helpEmailTo = 'biuro@bialnet.com.pl'; // todo:email na który zostanie wysłane zapytanie z formularza POMOCY
 
 	public static $FIELD_LIST_PRACOWNICY = [
 		'ID',
@@ -1142,7 +1142,8 @@ public function showPowiazaniaEnergaRumKontrahenciPowiazania($items) {
 			return ('f_' === substr($key, 0, 2));
 		});
 		foreach ($fieldFilterKeys as $key) {
-			$filtersParams[ $key ] = '%' . $args[ $key ] . '%';
+			if ('f_ID' === $key) $filtersParams[ $key ] = '=' . $args[ $key ];
+			else $filtersParams[ $key ] = '%' . $args[ $key ] . '%';
 		}
 
 		$acl = ACL::getAclByNamespace('default_db/BI_audit_ENERGA_PRACOWNICY/BI_audit_ENERGA_PRACOWNICY');
@@ -1153,8 +1154,8 @@ public function showPowiazaniaEnergaRumKontrahenciPowiazania($items) {
 			//'f_ID' => $id,
 			'limit' => $limit,
 			'limitstart' => $limitstart, // offset
-			// 'order_by' => 'ID', // sortowanie po kolumnie
-			// 'order_dir' => 'DESC', // kierunek sortowania
+			'order_by' => 'ID', // sortowanie po kolumnie
+			'order_dir' => 'DESC', // kierunek sortowania
 			// 'f_nip' = "=12345", // szukamy dokładnie tej wartości - mysql: where nip = "12345"
 			// 'f_nip' = "12345", // szukamy dokładnie tej wartości - mysql: where nip like "%12345%"
 			// 'f_nip' = "12345%", // szukamy dokładnie tej wartości - mysql: where nip like "12345%"
@@ -1548,6 +1549,7 @@ public function showPowiazaniaEnergaRumKontrahenciPowiazania($items) {
 					$formFilterHeadDesc = '<p>Liczba pozycji na liście: <span id="smad-filter-desc-kontrahenci">0</span></p>';
 					$countSelectedItem = '<div>Wybrano: <span id="count-selected-item-kontrahenci">0</span> <span><a title="wyczyść zapamietanych kontrahentów"  class="link-primary" onClick="clearListLocalStorageByType(\'kontrahenci\')"><small>(wyczyść)</small></a></span></div>';
 
+					$graphView = '<div class="smad-kontrahenci-graph-view" style="display: none;"></div>';
 
 					$csvExampleKontrahenci = Request::getPathUri() . 'projects/bocian/theme/assets/file_example/kontrahenci_example.zip';
 					$formAddNewData ='
@@ -1593,6 +1595,8 @@ public function showPowiazaniaEnergaRumKontrahenciPowiazania($items) {
 					$formFilterHeadDesc = '<p>Liczba pozycji na liście: <span id="smad-filter-desc-pracownicy">0</span></p>';
 					$countSelectedItem = '<div>Wybrano: <span id="count-selected-item-pracownicy">0</span> <span><a title="wyczyść zapamietanych pracowników" class="link-primary" onClick="clearListLocalStorageByType(\'pracownicy\')"><small>(wyczyść)</small></a></span></div>';
 
+					$graphView = '<div class="smad-pracownicy-graph-view" style="display: none;"></div>';
+
 					$csvExamplePracownicy = Request::getPathUri() . 'projects/bocian/theme/assets/file_example/pracownicy_example.zip';
 					$formAddNewData ='
 					<li><a  title="IMPORT PRACOWNIKÓW" onClick="showViewUploadFile(event, \'Import pracowników\', \'pracownicy\', \'yes\', \''.$csvExamplePracownicy.'\'  )" class="btn btn-info">IMPORT PRACOWNIKÓW</a></li>
@@ -1732,8 +1736,16 @@ public function showPowiazaniaEnergaRumKontrahenciPowiazania($items) {
 											</div>
 									</div>
 							</div>
+			</div>
 
+			<!-- Start: graph-view -->
+			<div class="smad-graph-section">
+				<div class="container-fluid">
+				'.$graphView.'
+				</div>
 			</div>
+			<!-- End: graph-view -->
+
 			<div class="container-fluid">
 					<div class="table-responsive padding-0" id="smad-table-' . $type . '">
 							' . $this->showListDataByType($items, $type, $fieldLabels) . '

+ 533 - 50
tools/Bocian.php.view.js

@@ -36,7 +36,7 @@ function urlFetchKontrahenciPowiazania() {
 		$( "#body-reaport-kontrahenci-powiazani-tree" ).html(reaportItemsKontrahenciPowiazani);
 
 	}).catch(function(error) {
-		console.log('request failed', error)
+		if(DBG) console.log('request failed', error)
 	});
 }
 
@@ -75,10 +75,11 @@ function updateLocalStorageBiAuditDepth(idInput) {
 
 						$('.bottom--message-warning-critical-search').text(messageCriticalSearchData);
 
-						console.log('popup i dodanie komunikatu niżej');
+						if(DBG) console.log('popup i dodanie komunikatu niżej');
+
 					} else {
 						$('.bottom--message-warning-critical-search').text('');
-						console.log('popup i dodanie wyczysc komunikatu ');
+						if(DBG) console.log('popup i dodanie wyczysc komunikatu ');
 					}
 			}
 
@@ -213,9 +214,9 @@ function showViewUploadFile( event, headerTitle, enumType, showCheckbox, csvFile
 			})
 		},
 	}).then(function (result) {
-		console.log('result', result)
+		if(DBG) console.log('result', result);
 	}).catch(function (e) {
-		console.log('catch: ', e)
+		if(DBG) console.log('catch: ', e);
 	});
 
 }
@@ -227,7 +228,7 @@ function showViewUploadFile( event, headerTitle, enumType, showCheckbox, csvFile
 function parseCsvFile( fileData, enumType ) {
 	event.preventDefault();
 
-console.log( 'parseCsvFile fileData', fileData);
+	if(DBG) console.log( 'parseCsvFile fileData', fileData);
 
 	fetch(URL_FORM_DATA_CSV_FILE_AJAX, {
 	  method: 'POST',
@@ -259,13 +260,13 @@ console.log( 'parseCsvFile fileData', fileData);
 						 		},
 							 }).then(function (result) {
 
-	 							console.log('result', result)
+	 							if(DBG) console.log('result', result)
 	 						}).catch(function (e) {
-	 							console.log('catch: ', e)
+	 							if(DBG) console.log('catch: ', e)
 	 						});
 
  }).catch(function(ex) {
-	 console.log('parsing failed', ex)
+	 if(DBG) console.log('parsing failed', ex);
  });
 }
 
@@ -294,7 +295,7 @@ function saveFormCsvFileAjaxAction(enumType) {
 	.then(function(response) {
 	 return response.json()
  }).then(function(json) {
-	 console.log('parsed json', json)
+	 if(DBG) console.log('parsed json', json);
 
 	 if (response.type == 'success') {
 
@@ -303,7 +304,8 @@ function saveFormCsvFileAjaxAction(enumType) {
 
 	// return json;
  }).catch(function(ex) {
-	 console.log('parsing failed', ex)
+	 if(DBG) console.log('parsing failed', ex);
+
  });
 }
 
@@ -373,7 +375,8 @@ function generateBiAuditRaport(event) {
 					  })
 					})
 					.then(function(response) {
-						console.log('Firsst then', response);
+						if(DBG) console.log('Firsst then', response);
+
 						return response.text();
 					})
 					.then(function(responseText) {
@@ -407,7 +410,7 @@ function generateBiAuditRaport(event) {
 						}
 					})
 					.catch(function(error) {
-						console.log('request failed', error)
+						if(DBG) console.log('request failed', error);
 					});
 			}
 
@@ -448,7 +451,7 @@ function addPracownikToGroup(event) {
 	  showLoaderOnConfirm: true,
 	  preConfirm: function (idGroup) {
 	    return new Promise(function (resolve, reject) {
-				console.log('idGrupy value', idGroup);
+				if(DBG) console.log('idGrupy value', idGroup);
 
 				if ( !pracownicyIdsArray || pracownicyIdsArray === null || pracownicyIdsArray === 'undefined' ) {
 						reject('Wybierz pracowników');
@@ -489,7 +492,7 @@ function addPracownikToGroup(event) {
 					}
 				})
 				.catch(function(error) {
-					console.log('request failed', error)
+					if(DBG) console.warn('request failed', error);
 				});
 	    })
 	  },
@@ -533,7 +536,7 @@ function addPracownikAllToGroup(event) {
 				var frm = document.getElementById('filtersFieldRemoveBtn-PRACOWNICY').form
 				var fieldNameList = FIELD_LIST_PRACOWNICY
 				fieldNameList.filter(function (fieldName) {
-					if (!frm[fieldName]) console.log('Err missing field: "'+fieldName+'"')
+					if (!frm[fieldName] && DBG) console.log('Err missing field: "'+fieldName+'"');
 					return (frm[fieldName]) ? true : false
 				}).map(function (fieldName) {
 					return [ fieldName, frm[fieldName].value ]
@@ -578,7 +581,7 @@ function addPracownikAllToGroup(event) {
 					}
 				})
 				.catch(function(error) {
-					console.log('request failed', error)
+					if(DBG) console.log('request failed', error);
 				});
 	    })
 	  },
@@ -706,7 +709,7 @@ function addKontrahenciAllToGroup(event) {
 				var frm = document.getElementById('filtersFieldRemoveBtn-KONTRAHENCI').form
 				var fieldNameList = FIELD_LIST_KONTRAHENCI
 				fieldNameList.filter(function (fieldName) {
-					if (!frm[fieldName]) console.log('Err missing field: "'+fieldName+'"')
+					if (!frm[fieldName] && DBG) console.log('Err missing field: "'+fieldName+'"')
 					return (frm[fieldName]) ? true : false
 				}).map(function (fieldName) {
 					return [ fieldName, frm[fieldName].value ]
@@ -751,7 +754,7 @@ function addKontrahenciAllToGroup(event) {
 					}
 				})
 				.catch(function(error) {
-					console.log('request failed', error)
+					if(DBG) console.log('request failed', error);
 				});
 	    })
 	  },
@@ -805,7 +808,7 @@ function createGroupKontrahenci(event) {
 					}
 				})
 				.catch(function(error) {
-					console.log('request failed', error)
+					if(DBG) 	console.log('request failed', error);
 				})
 	    })
 	  },
@@ -862,14 +865,15 @@ function createGroupPracownicy(event) {
 					}
 				})
 				.catch(function(error) {
-					console.log('request failed', error)
+					if(DBG) console.log('request failed', error);
 				})
 
 	    })
 	  },
 	  allowOutsideClick: false
 	}).then(function (groupPracownicyData) {
-		console.log('grupa prac2: ', groupPracownicyData);
+		if(DBG) console.log('grupa prac2: ', groupPracownicyData);
+
 		//TODO: aktualizacja fitrów na widoku -> SPrawdzić co dostaniemy w odpowiedzi po dodaniu filtra grupy
 		groupsPracownicy = '<button class="btn btn-default" title="'+groupPracownicyData['nazwa']+'" data-group-filter="'+groupPracownicyData['id']+'">'+groupPracownicyData['nazwa']+'</button>';
 		$('#group-pracownicy').append(groupsPracownicy);
@@ -878,7 +882,9 @@ function createGroupPracownicy(event) {
 
 }
 
-
+/**
+ * Function for load view Form pracownicy/kontrahenci
+ */
 function rootChangeForm() {
 
 //todo: do przeniesienia
@@ -892,6 +898,7 @@ urlFetchKontrahenciPowiazania();
 			fetchGroupKontrahenci();
 			$( ".container-bi_audit_form_pracownicy_raport" ).hide();
 			$( ".container-bi_audit_form_kontrahenci_raport" ).show();
+			//parseGraph();
 			break;
 
 		case '#PRACOWNICY':
@@ -1066,6 +1073,8 @@ function urlFetchKontrahenci(page) {
 					$('#paginationShowNextCount-KONTRAHENCI').text(paginationShowNextCount);
 					$('#smad-filter-desc-kontrahenci').text(data.body.pagination.total_items);
 					if (data.body.pagination.total_items > 0) {
+						var btnGraphView = $('<button class="btn btn-primary smad-kontrahenci-graph-button" onClick="graphShowHide(\'kontrahenci\')" style="padding:1px 5px; margin-left:12px">GRAF POWIĄZAŃ</button>')
+
 						var btnAddAll = $('<button class="btn btn-primary" style="padding:1px 5px; margin-left:12px">DODAJ WSZYSTKIE DO ANALIZY</button>')
 						btnAddAll.on('click', { pagination: data.body.pagination }, function (event) {
 							// event.data.pagination: { [ filter_idGroup: ... ] }
@@ -1113,12 +1122,13 @@ function urlFetchKontrahenci(page) {
 							})
 						})
 						$('#smad-filter-desc-kontrahenci').append(btnAddAll);
+						$('#smad-filter-desc-kontrahenci').append(btnGraphView);
 					}
 				}
 
 				var groupSelectData = getNameGroupById(getItemLocalStorage('Bocian.biAuditForm.kontrahenci.groups'), filterIdGroup);
 				$('#smad-filter-head-title-kontrahenci').text(groupSelectData);
-				console.log('kontrahenci groupSelectData: ', groupSelectData);
+				if(DBG) console.log('kontrahenci groupSelectData: ', groupSelectData);
 
 
 				$( "#body-kontrahenci" ).html(listItemsKontrahenci);
@@ -1127,16 +1137,16 @@ function urlFetchKontrahenci(page) {
 				checkedChoiseItems('KONTRAHENCI', getItemLocalStorage('Bocian.biAuditForm.kontrahenciIds') );
 
 				//	$( ".container-bi_audit_raport" ).append( data.body.view );
-					console.log('request succeeded with JSON responseKontrahenci', data)
+					if(DBG) console.log('request succeeded with JSON responseKontrahenci', data)
+
 					updateTopCounters();
 				}).catch(function(error) {
-					console.log('request failed', error)
+					if(DBG) console.log('request failed', error);
 				})
 }
 
 function urlFetchPracownicy(page) {
 	hideMoreRecordFunctionsPopover()
-
 		var page = page || getItemLocalStorage('Bocian.biAuditForm.pracownicy.pagination.page');
 
 		if ( page === 1) {
@@ -1150,7 +1160,7 @@ function urlFetchPracownicy(page) {
 			var frm = document.getElementById('filtersFieldRemoveBtn-PRACOWNICY').form
 			var fieldNameList = FIELD_LIST_PRACOWNICY
 			var filterFields = fieldNameList.filter(function (fieldName) {
-				if (!frm[fieldName]) console.log('Err missing field: "'+fieldName+'"')
+				if (!frm[fieldName] && DBG) console.log('Err missing field: "'+fieldName+'"')
 				return (frm[fieldName]) ? true : false
 			}).map(function (fieldName) {
 				return [ fieldName, frm[fieldName].value ]
@@ -1162,7 +1172,7 @@ function urlFetchPracownicy(page) {
 			filterFields = (filterFields.length > 0) ? '&' + filterFields : ''
 
 			var paginationLimit = 20;
-			p5WFS_GetFeature('default_db__x3A__BI_audit_ENERGA_PRACOWNICY:BI_audit_ENERGA_PRACOWNICY',
+			/*p5WFS_GetFeature('default_db__x3A__BI_audit_ENERGA_PRACOWNICY:BI_audit_ENERGA_PRACOWNICY',
 				Object.assign({
 					sortBy: 'ID+D',
 					maxFeatures: paginationLimit,
@@ -1194,7 +1204,7 @@ function urlFetchPracownicy(page) {
 				if(DBG)console.log('p5WFS_GetFeature: items: ', items);
 			}).catch(function (err) {
 				if(DBG)console.log('p5WFS_GetFeature: err: ', err);
-			})
+			})*/
 
 			fetch(URL_FETCH_PRACOWNICY + '&page=' + page + '&filterIdGroup=' + filterIdGroup + filterFields, {
 				credentials: 'same-origin'
@@ -1206,7 +1216,7 @@ function urlFetchPracownicy(page) {
 
 					var addresPerson = '';
 					var listItemsPracownik = null;
-					console.log('urlFetchPracownicy', data.body.items);
+					if(DBG) console.log('urlFetchPracownicy', data.body.items);
 
 					data.body.items.forEach (function(row) {
 
@@ -1251,6 +1261,8 @@ function urlFetchPracownicy(page) {
 						$('#paginationShowNextCount-PRACOWNICY').text(paginationShowNextCount);
 						$('#smad-filter-desc-pracownicy').text(data.body.pagination.total_items);
 						if (data.body.pagination.total_items > 0) {
+							var btnGraphView = $('<button class="btn btn-primary smad-pracownicy-graph-button" onClick="graphShowHide(\'pracownicy\')" style="padding:1px 5px; margin-left:12px">GRAF POWIĄZAŃ</button>')
+
 							var btnAddAll = $('<button class="btn btn-primary" style="padding:1px 5px; margin-left:12px">DODAJ WSZYSTKIE DO ANALIZY</button>')
 							btnAddAll.on('click', { pagination: data.body.pagination }, function (event) {
 								// event.data.pagination: { [ filter_idGroup: ... ] }
@@ -1298,6 +1310,8 @@ function urlFetchPracownicy(page) {
 								})
 							})
 							$('#smad-filter-desc-pracownicy').append(btnAddAll);
+							$('#smad-filter-desc-pracownicy').append(btnGraphView);
+
 						}
 			}
 
@@ -1314,7 +1328,7 @@ function urlFetchPracownicy(page) {
 
 				updateTopCounters();
 			}).catch(function(error) {
-				console.log('request failed', error)
+				if(DBG) console.log('request failed', error)
 			})
 
 }
@@ -1517,7 +1531,6 @@ function deleteListIdBiAuditReaport(type, valueArray) {
 
 // filter group detect
 function fetchGroupPracownicy() {
-
 		fetch(URL_FETCH_GROUP_PRACOWNICY, {
 				credentials: 'same-origin'
 			})
@@ -1541,7 +1554,9 @@ function fetchGroupPracownicy() {
 					var activeButton = '';
 
 					data.body.itmesGroupPracownicy.forEach (function(row) {
-							if ( filterIdGroup === row['ID'] ) { activeButton = 'active'; $('#group-pracownicy button.active').removeClass('active'); }
+							activeButton = '';
+							if ( filterIdGroup === parseInt(row['ID']) ) {activeButton = 'active'; $('#group-pracownicy button.active').removeClass('active');
+							}
 							groupsPracownicy += '<button class="btn btn-default '+activeButton+'" title="' + row['NAZWA'] + '" data-group-filter="' + row['ID'] + '">' + row['NAZWA'] + '</button>';
 					});
 
@@ -1551,7 +1566,7 @@ function fetchGroupPracownicy() {
 					// detect filter group
 					detectChoiseFilter();
 			}).catch(function(error) {
-				console.log('request failed', error)
+				if(DBG) console.log('request failed', error)
 			});
 }
 
@@ -1580,7 +1595,8 @@ function fetchGroupKontrahenci() {
 					var activeButton = '';
 
 					data.body.itemsGroupKontrahenci.forEach (function(row) {
-							if ( filterIdGroup === row['ID'] ) { activeButton = 'active'; $('#group-kontrahenci button.active').removeClass('active'); }
+							activeButton = '';
+							if ( filterIdGroup === parseInt(row['ID']) ) { activeButton = 'active'; $('#group-kontrahenci button.active').removeClass('active'); }
 							groupsKontrahenci += '<button class="btn btn-default '+activeButton+'" title="'+row['NAZWA']+'" data-group-filter="'+row['ID']+'">'+row['NAZWA']+'</button>';
 					});
 
@@ -1589,9 +1605,9 @@ function fetchGroupKontrahenci() {
 
 					// detect filter group
 					detectChoiseFilter();
-				console.log('request succeeded with JSON fetchGroupKontrahenci', data.body.itemsGroupKontrahenci);
+				if(DBG) console.log('request succeeded with JSON fetchGroupKontrahenci', data.body.itemsGroupKontrahenci);
 			}).catch(function(error) {
-				console.log('request failed', error)
+				if(DBG) console.log('request failed', error)
 			});
 }
 
@@ -1599,7 +1615,6 @@ function fetchGroupKontrahenci() {
 function detectChoiseFilter() {
 
 	$('#group-kontrahenci').on('click', 'button', function(evt) {
-
 		$('#group-kontrahenci button.active').removeClass('active');
 		$(this).addClass('active');
 
@@ -1624,8 +1639,8 @@ function detectChoiseFilter() {
 		urlFetchKontrahenci(1);
 
 	});
-	$('#group-pracownicy').on('click', 'button', function(evt) {
-
+	$('#group-pracownicy').on('click', 'button', function(e) {
+		e.preventDefault();
 		$('#group-pracownicy button.active').removeClass('active');
 		$(this).addClass('active');
 
@@ -1700,6 +1715,474 @@ function removeItemArrayInArray(arr, itemsArray) {
 			return arr;
   }
 
+
+/**
+ * Function show/hide graph view
+ */
+function graphShowHide(nameSection) {
+	var node = $('.smad-'+nameSection+'-graph-view')
+	if (!node || !node.length) return;
+	if ('block' !== node.css('display')) {
+		node.show()
+		graphFetchData(node.get(0), nameSection)
+	} else {
+		node.hide()
+	}
+}
+function graphFetchData(node, nameSection) {
+	var page = page || getItemLocalStorage('Bocian.biAuditForm.'+nameSection+'.pagination.page');
+	if ( page === 1) {
+		setItemLocalStorage('Bocian.biAuditForm.'+nameSection+'.pagination.page', 1);
+	}
+
+	var filterIdGroup = getItemLocalStorage('Bocian.biAuditForm.'+nameSection+'.filterIdGroup');
+
+	var frm = document.getElementById('filtersFieldRemoveBtn-' + nameSection.toUpperCase()).form
+	console.log('frm', frm)
+	var fieldNameList = ('pracownicy' === nameSection) ? FIELD_LIST_PRACOWNICY : FIELD_LIST_KONTRAHENCI
+	var filterFields = fieldNameList.filter(function (fieldName) {
+		if (!frm[fieldName] && DBG) console.log('Err missing field: "'+fieldName+'"')
+		return (frm[fieldName]) ? true : false
+	}).map(function (fieldName) {
+		return [ fieldName, frm[fieldName].value ]
+	}).filter(function (filter) {
+		return ( filter[1].length > 0 )
+	})
+	// filterFields = (filterFields.length > 0) ? '&' + filterFields : ''
+
+	// <ogc:Filter>
+	//     <ogc:Or>
+	//       <ogc:PropertyIsLike wildCard="*" singleChar="%23" escapeChar="!">
+	//         <ogc:PropertyName>A_STATUS</ogc:PropertyName>
+	//         <ogc:Literal>*O%23MA*</ogc:Literal>
+	//       </ogc:PropertyIsLike>
+	//       <ogc:PropertyIsLike wildCard="*" singleChar="%23" escapeChar="!">
+	//         <ogc:PropertyName>A_STATUS</ogc:PropertyName>
+	//         <ogc:Literal>*ARNING</ogc:Literal>
+	//       </ogc:PropertyIsLike>
+	//     </ogc:Or>
+	//   </ogc:Filter>
+	var ogcFilterFields = (filterFields.length > 0)
+	? '<ogc:Filter><ogc:And>' + filterFields.map(function(filter) {
+		if ('ID' === filter[0].substr(2)) return '<ogc:PropertyIsEqualTo>' +
+			'<ogc:PropertyName>' + filter[0].substr(2) + '</ogc:PropertyName>' +
+			'<ogc:Literal>' + filter[1] + '</ogc:Literal>' +
+		'</ogc:PropertyIsEqualTo>';
+
+		return '<ogc:PropertyIsLike wildCard="*" singleChar="%23" escapeChar="!">' +
+			'<ogc:PropertyName>' + filter[0].substr(2) + '</ogc:PropertyName>' +
+			'<ogc:Literal>*' + filter[1] + '*</ogc:Literal>' +
+		'</ogc:PropertyIsLike>';
+	}) + '</ogc:And></ogc:Filter>'
+	: ''
+
+	var paginationLimit = 20;
+
+	if(DBG)console.log('graphFetchData...', {
+		paginationLimit: paginationLimit,
+		page: page,
+		filterIdGroup: filterIdGroup,
+		filterFields: filterFields,
+		ogcFilterFields: ogcFilterFields,
+	});
+	console.log('graphFetchData...', {
+		paginationLimit: paginationLimit,
+		page: page,
+		filterIdGroup: filterIdGroup,
+		filterFields: filterFields,
+		ogcFilterFields: ogcFilterFields,
+	});
+
+	p5WFS_GetFeature('default_db__x3A__BI_audit_ENERGA_PRACOWNICY:BI_audit_ENERGA_PRACOWNICY',
+		Object.assign({
+			sortBy: 'ID+D',
+			maxFeatures: paginationLimit,
+			startIndex: (page - 1) * paginationLimit,
+			// TODO: backRefNS, backRefPK, backRefField - TODO: from groups
+			// resolve: 'all',
+			// resolveDepth: 2
+			'ogc:Filter': '<wfs:Query>' + '\n' + [
+				'ID', // '*'
+				'default_db__x3A__BI_audit_ENERGA_PRACOWNICY_adresy:BI_audit_ENERGA_PRACOWNICY_adresy/*',
+				[
+					'default_db__x3A__BI_audit_ENERGA_RUM_KONTRAHENCI_POWIAZANIA_row_object:BI_audit_ENERGA_RUM_KONTRAHENCI_POWIAZANIA_row_object',
+					'default_db__x3A__BI_audit_ENERGA_RUM_KONTRAHENCI_POWIAZANIA_row:BI_audit_ENERGA_RUM_KONTRAHENCI_POWIAZANIA_row',
+					'default_db__x3A__BI_audit_ENERGA_RUM_KONTRAHENCI_POWIAZANIA_row_object:BI_audit_ENERGA_RUM_KONTRAHENCI_POWIAZANIA_row_object',
+					'default_db__x3A__BI_audit_ENERGA_RUM_KONTRAHENCI:BI_audit_ENERGA_RUM_KONTRAHENCI'
+				].join('/'),
+				[
+					'default_db__x3A__BI_audit_ENERGA_RUM_KONTRAHENCI_POWIAZANIA_row_object:BI_audit_ENERGA_RUM_KONTRAHENCI_POWIAZANIA_row_object',
+					'ID',
+				].join('/'),
+				[
+					'default_db__x3A__BI_audit_ENERGA_RUM_KONTRAHENCI_POWIAZANIA_row_object:BI_audit_ENERGA_RUM_KONTRAHENCI_POWIAZANIA_row_object',
+					'default_db__x3A__BI_audit_ENERGA_RUM_KONTRAHENCI_POWIAZANIA_row:BI_audit_ENERGA_RUM_KONTRAHENCI_POWIAZANIA_row',
+					'ID',
+				].join('/'),
+				[
+					'default_db__x3A__BI_audit_ENERGA_RUM_KONTRAHENCI_POWIAZANIA_row_object:BI_audit_ENERGA_RUM_KONTRAHENCI_POWIAZANIA_row_object',
+					'default_db__x3A__BI_audit_ENERGA_RUM_KONTRAHENCI_POWIAZANIA_row:BI_audit_ENERGA_RUM_KONTRAHENCI_POWIAZANIA_row',
+					'default_db__x3A__BI_audit_ENERGA_RUM_KONTRAHENCI_POWIAZANIA_row_object:BI_audit_ENERGA_RUM_KONTRAHENCI_POWIAZANIA_row_object',
+					'ID'
+				].join('/')
+			].map(function (fieldName) {
+				return '<wfs:PropertyName>' + fieldName + '</wfs:PropertyName>';
+			}).join('\n') + '\n' +
+			( ogcFilterFields ? ogcFilterFields : '' ) +
+			'</wfs:Query>'
+		}, (filterIdGroup > 0)
+			? {
+					backRefNS: 'default_db/BI_audit_ENERGA_PRACOWNICY_group/BI_audit_ENERGA_PRACOWNICY_group',
+					backRefPK: filterIdGroup,
+					backRefField: 'default_db__x3A__BI_audit_ENERGA_PRACOWNICY:BI_audit_ENERGA_PRACOWNICY',
+				}
+			: {}
+		)
+	).then(function (items) {
+		if(DBG)console.log('p5WFS_GetFeature: items: ', items);
+		graphRender(node, { msg: "Pobrano dane", nameSection: nameSection, items: items })
+	}).catch(function (err) {
+		if(DBG)console.log('p5WFS_GetFeature: err: ', err);
+		// graphRender(node, { msg: "Wystąpiły błędy podczas pobierania danych", nameSection: nameSection, err: err })
+	})
+}
+
+function graphRender(wrapNode, props) {
+	var svgNode = jQuery(wrapNode).children('svg')
+	svgNode = (svgNode.length) ? d3.select(svgNode.get(0)) : d3.select(wrapNode).append("svg")
+	// svg.append("rect").attr("x",0).attr("y",0).attr("width","100%").attr("height","100%").attr("fill","white").attr('class','background').on('mouseup', () => redraw())
+	// svgNode.attr("width", '100%')
+	// 	.attr("height", '300px')
+		// .append("g").attr("transform", "translate(" + margin.left + "," + margin.top + ")");
+
+	// var data = parseGraphRec(props.items, props.nameSection)
+	var _todoGraphData = [];
+	parseResponseRec(_todoGraphData, props.items, props.nameSection)
+	if(DBG)console.log('_todoGraphData', _todoGraphData)
+	var _nodes = [];
+	var _links = [];
+	var mapNodeIdToIdx = {};
+	_todoGraphData.forEach(function (levelData, levelIdx) {
+		if (levelData.nodes && levelData.nodes.length) {
+			levelData.nodes.forEach(function (node) {
+				try {
+					if (node.id in mapNodeIdToIdx) return;
+					_nodes.push(node) // _nodes.add(node)
+					mapNodeIdToIdx[node.id] = _nodes.length - 1
+				} catch (e) {
+					if(DBG)console.log('_graphData.nodes.add [level='+levelIdx+'] error:', e);
+				}
+			})
+		}
+	})
+
+	var linkIdsAdded = []
+	_todoGraphData.forEach(function (levelData, levelIdx) {
+		if (levelData.edges && levelData.edges.length) {
+			levelData.edges.forEach(function (edge) {
+				if(DBG)console.log('_graphData.edges.add [level='+levelIdx+']:', {edge, source:mapNodeIdToIdx[edge.source], target:mapNodeIdToIdx[edge.target], mapNodeIdToIdx});
+				// try {
+					// var source = mapNodeIdToIdx[edge.source]
+					// var target = mapNodeIdToIdx[edge.target]
+					// if (!source || !target) { // && !== 0
+					// 	console.warn('(!source || !target)', {source, target, edge, mapNodeIdToIdx})
+					// }
+					if (-1 !== linkIdsAdded.indexOf(edge.id)) return
+					// _links.push({
+					// 	id: edge.id,
+					// 	source: mapNodeIdToIdx[edge.source],
+					// 	target: mapNodeIdToIdx[edge.target],
+					// 	value: 1
+					// })
+					_links.push({
+						id: edge.id,
+						source: edge.source,
+						target: edge.target,
+						value: 1
+					}) // _edges.add(edge)
+					linkIdsAdded.push(edge.id)
+				// } catch (e) {
+				// 	if(DBG)console.log('_graphData.edges.add [level='+levelIdx+'] error:', e);
+				// }
+			})
+		}
+	})
+
+	var totalLinks = _links.length
+	console.log('DBG:0: _links', _links)
+	console.log('DBG:1: _nodes', _nodes)
+	_nodes = _nodes.filter(function (node, idx) {
+		if (node.typeName !== 'pracownicy') return true
+		for (i=0; i<totalLinks; i++) {
+			var link = _links[i]
+			if (_links[i].source === node.id) return true
+			if (_links[i].target === node.id) return true
+		}
+		return false
+	})
+	console.log('DBG:2: _nodes', _nodes)
+	jQuery(wrapNode).find('p').remove()
+	if (!_nodes || !_nodes.length) {
+		jQuery(wrapNode).find('svg').remove()
+		jQuery(wrapNode).append('<p>Brak powiązań</p>')
+		return;
+	}
+
+	var graphData = { nodes: _nodes, links: _links };
+
+	var graf = renderGraph(svgNode, graphData)
+	graf.on('click', (event) => {
+	  if(DBG)console.log('event', event)
+	  // event = {
+	  //   nativeEvent: d3.event,
+	  //   node: a,
+	  //   element: d3.event.srcElement
+	  // }
+
+	  // TODO: !a.expanded && redraw({type: a.name, criteria: 'ID'})
+	})
+}
+
+function parseGraphRec(items, featureType, parentFeatureId) {
+	var parentFeatureId = parentFeatureId || null
+	var nodesArray = []
+	var linksArray = []
+	if (!items) return;
+	items.forEach(function (item) {
+		if ('string' === typeof item) {
+			if(DBG)console.log('TODO: xlink "'+item+'": ', {item:item, parentFeatureId:parentFeatureId})
+			return;
+		}
+		if (!item['ID']) {
+			if(DBG)console.log('TODO: SKIP item('+featureType+') - missing ID: ', {item:item, parentFeatureId:parentFeatureId})
+			return;
+		}
+		var id = item['ID']
+		console.log('item('+featureType+'): ', {item:item, parentFeatureId:parentFeatureId})
+		var featureId = featureType + '.' + id
+		Object.keys(item).filter(function (fieldName) { return ('ID' !== fieldName); })
+		.forEach(function (fieldName) {
+			parseGraphRec(item[fieldName], fieldName, featureId)
+		})
+	})
+  // items.forEach(element => {
+  //   const rowObject = hasTargetArray(element);
+  //   if (rowObject)
+  //     rowObject.array.forEach(objectElement => {
+  //       const trackNode = hasTargetArray(objectElement);
+  //       if (trackNode) {
+  //         let group = trackNode.name;
+  //         let expanded = false;
+  //         if (expansion && expansion.type && group === expansion.type) {
+  //           group = trackNode.array[0][expansion.criteria];
+  //           expanded = true;
+  //         }
+  //         nodes[group] = {expanded: expanded, type: trackNode.name};
+  //         instanceTypes[trackNode.array[0].ID] = group;
+  //       }
+  //     });
+  // });
+
+	return {
+		nodes: nodesArray,
+		links: linksArray
+	}
+}
+
+function parseResponseRec(_todoGraphData, json, typeName, parentNodeId, level) {
+	var level = level || 0
+	var parentNodeId = parentNodeId || null
+	if(DBG)console.log('DBG::parseResponseRec', {json:json, typeName:typeName, parentNodeId:parentNodeId, isString: p5Utils__isString(json), isArray: p5Utils__isArray(json), isObject: p5Utils__isObject(json)});
+	if (p5Utils__isArray(json)) {
+		// TODO: create named group
+		var isXlinkList = (json.length > 0 && p5Utils__isString(json[0]))
+		json.forEach(function (subJson) {
+			if (isXlinkList) {
+				parseResponseXlinkListRec(_todoGraphData, subJson, typeName, parentNodeId, level)
+			} else {
+				parseResponseRec(_todoGraphData, subJson, typeName, parentNodeId, level)
+			}
+		})
+	} else if (p5Utils__isObject(json) && isP5LinkObject(json)) {
+		if(DBG)console.log('DBG::parseResponseRec isP5LinkObject');
+		parseResponseP5Link(_todoGraphData, json, typeName, parentNodeId, level)
+	} else if (p5Utils__isObject(json)) {
+		// _todoGraphData.nodes.add({ id: nodeId, label: nodeId })
+		if (!_todoGraphData[level]) _todoGraphData[level] = { nodes: [], edges: [] }
+		var nodeObject = dataMakeNode({
+			typeName: typeName,
+			primaryKey: (json['ID']) ? json['ID'] : null, // TODO: get primaryKey from object
+		})
+
+		if ("default_db__x3A__BI_audit_ENERGA_RUM_KONTRAHENCI_POWIAZANIA_row_object:BI_audit_ENERGA_RUM_KONTRAHENCI_POWIAZANIA_row_object" !== typeName
+			&& "default_db__x3A__BI_audit_ENERGA_RUM_KONTRAHENCI_POWIAZANIA_row:BI_audit_ENERGA_RUM_KONTRAHENCI_POWIAZANIA_row" !== typeName
+		) {
+			var nodeId = nodeObject.id
+			_todoGraphData[level].nodes.push(nodeObject)
+			if (parentNodeId) {
+				_todoGraphData[level].edges.push(dataMakeEdge(parentNodeId, nodeObject))
+			}
+			Object.keys(json).filter(function (fieldName) {
+				return (fieldName.indexOf(':') > -1)
+			})
+			.forEach(function (fieldName) {
+				var value = json[fieldName]
+				parseResponseRec(_todoGraphData, value, fieldName, nodeId, level + 1)
+			})
+		} else {
+			Object.keys(json).filter(function (fieldName) {
+				return (fieldName.indexOf(':') > -1)
+			})
+			.forEach(function (fieldName) {
+				var value = json[fieldName]
+				parseResponseRec(_todoGraphData, value, fieldName, parentNodeId, level + 1)
+			})
+		}
+	} else if (p5Utils__isString(json)) {
+		if(DBG)console.log('TODO: Not implemented - parseResponseRec isString');
+	} else {
+		if(DBG)console.log('TODO: Not implemented - parseResponseRec is not string, not array and not object');
+	}
+}
+function parseResponseXlinkListRec(_todoGraphData, json, typeName, parentNodeId, level) {
+	if(DBG)console.log('DBG::parseResponseRec:XlinkList', {json:json, typeName:typeName, parentNodeId:parentNodeId, isString: p5Utils__isString(json), isArray: p5Utils__isArray(json), isObject: p5Utils__isObject(json)});
+	if (p5Utils__isString(json)) { // xlink "https://biuro.biall-net.pl/wfs/default_db/BI_audit_ENERGA_RUM_KONTRAHENCI#BI_audit_ENERGA_RUM_KONTRAHENCI.9233",
+		var nodeId = json.substr(json.indexOf('#') + 1)
+		{
+			// _graphData.nodes.add({ id: nodeId, label: nodeId })
+			if (!_todoGraphData[level]) _todoGraphData[level] = { nodes: [], edges: [] }
+			var nodeObject = dataMakeXlinkNode({
+				xlink: json,
+				typeName: typeName,
+				primaryKey: (json['ID']) ? json['ID'] : null, // TODO: get primaryKey from object
+			})
+			_todoGraphData[level].nodes.push(nodeObject)
+			if (parentNodeId) {
+				_todoGraphData[level].edges.push(dataMakeEdge(parentNodeId, nodeObject))
+			}
+		}
+	} else if (p5Utils__isObject(json) && isP5LinkObject(json)) {
+		parseResponseP5Link(_todoGraphData, json, typeName, parentNodeId, level)
+	} else {
+		if(DBG)console.log('TODO: Not implemented - parseResponseRec:XlinkList is not string and not object');
+	}
+}
+function parseResponseP5Link(_todoGraphData, json, typeName, parentNodeId, level) {
+	if(DBG)console.log('parseResponseRec isObject and P5Link - fetch more xlink object');
+	// example json: { type: "next",
+	//  @backRefNS: "default_db/BI_audit_ENERGA_RUM_KONTRAHENCI_POWIAZANIA/BI_audit_ENERGA_RUM_KONTRAHENCI_POWIAZANIA",
+	//  @backRefPK: "42",
+	//  @typeName: "default_db__x3A__BI_audit_ENERGA_RUM_KONTRAHENCI_P…ow:BI_audit_ENERGA_RUM_KONTRAHENCI_POWIAZANIA_row",
+	//  @startIndex: "10" }
+	if (!parentNodeId) throw "Missing parentNodeId for ref link object";
+	var objectName = typeName.substr(typeName.indexOf(':') + 1)
+	{
+		// _graphData.nodes.add({ id: nodeId, label: nodeId })
+		if (!_todoGraphData[level]) _todoGraphData[level] = { nodes: [], edges: [] }
+		switch (json.type) {
+			case 'next': {
+        // TODO: add fetch next node
+				// var nodeObject = dataMakeFetchMoreNode({
+				// 	parentNodeId: parentNodeId,
+				// 	type: json.type,
+				// 	objectName: objectName,
+				// 	ref: json,
+				// })
+				// _todoGraphData[level].nodes.push(nodeObject)
+				// _todoGraphData[level].edges.push(dataMakeFetchMoreEdge(parentNodeId, nodeObject))
+			} break;
+			default: {
+				if(DBG)console.log('TODO: Not implemented - parseResponseRec isObject with type "'+json.type+'" - fetch more xlink object');
+			}
+		}
+	}
+}
+
+function isP5LinkObject(json) {
+	if ( !('type' in json) || !json['type'] ) return false;
+	if ( !('value' in json) || !json['value'] ) return false;
+	if ( !('@typeName' in json) || !json['@typeName'] ) return false;
+	if ( !('@startIndex' in json) || !json['@startIndex'] ) return false;
+	if ( !('@backRefPK' in json) || !json['@backRefPK'] ) return false;
+	if ( !('@backRefNS' in json) || !json['@backRefNS'] ) return false;
+	return true;
+}
+
+function dataMakeNode(params) {
+	var objectName = params.typeName.substr(params.typeName.indexOf(':') + 1)
+	var nodeId = objectName + '.' + params.primaryKey // TODO: primaryKey?
+	return {
+		id: nodeId,
+		label: makeShortLabel(nodeId), // TODO: get from schema assert @label attribute
+		name: nodeId,
+		group: objectName,
+		_loaded: true,
+		typeName: params.typeName,
+		primaryKey: params.primaryKey // TODO: _primaryKey
+	}
+}
+function dataMakeEdge(parentNodeId, nodeObject) {
+	return {
+		id: parentNodeId + nodeObject.id,
+		source: parentNodeId,
+		target: nodeObject.id,
+	}
+}
+function dataMakeXlinkNode(params) {
+	var objectName = params.typeName.substr(params.typeName.indexOf(':') + 1)
+	var nodeId = params.xlink.substr(params.xlink.indexOf('#') + 1)
+	var primaryKey = nodeId.substr(nodeId.lastIndexOf('.') + 1)
+	return {
+		id: nodeId,
+		label: makeShortLabel(nodeId) + ' (+)',
+		name: nodeId,
+		group: objectName,
+		_loaded: false,
+		typeName: params.typeName,
+		primaryKey: primaryKey
+	}
+}
+
+function dataMakeFetchMoreNode(params) {
+	// params = {
+	// 	parentNodeId: parentNodeId,
+	// 	type: json.type,
+	// 	objectName: objectName,
+	// 	ref: json,
+	// }
+	if(DBG)console.log('DBG dataMakeFetchMoreNode(params)', params)
+	var nodeId = params.parentNodeId+'fetch-more-features-'+params.type+'-on-' + params.objectName;
+	return {
+		id: nodeId,
+		label: 'Pobierz więcej (+)',
+		group: 'fetch-more-data', // params.objectName,
+		_loaded: false,
+		_type: 'ref',
+		parentNodeId: params.parentNodeId,
+		ref: params.ref,
+		// typeName: typeName,
+		// primaryKey: nodeId.substr(nodeId.lastIndexOf('.') + 1)
+	};
+}
+function dataMakeFetchMoreEdge(parentNodeId, fetchMoreNode) {
+	// @param parentNodeId - from node id
+	// @param fetchMoreNode - from dataMakeFetchMoreNode
+	if(DBG)console.log('DBG dataMakeFetchMoreEdge(parentNodeId, fetchMoreNode)', {parentNodeId:parentNodeId, fetchMoreNode:fetchMoreNode})
+	return {
+		id: fetchMoreNode.id,
+		from: parentNodeId,
+		to: fetchMoreNode.id
+	}
+}
+function makeShortLabel(label) { // TODO: shorter name
+	// BI_audit_ENERGA_RUM_KONTRAHENCI_POWIAZANIA_row_object.1234567
+	if (label.length < 30) return label;
+	var exLabel = label.split('.')
+	if (exLabel.length !== 2) return label;
+	return exLabel[0].substr(0, 24) + '...' + exLabel[0].substr(-10) + '.' + exLabel[1];
+}
+
+
 /**
  * Cleart list local storage by type
  * typeNameLocalStorage - name (praconwicy/kontrahenci)
@@ -1757,7 +2240,7 @@ function updateTopCounters() {
 	var totalPracownicy = (countPracownicy) ? Object.keys(countPracownicy).length : 0;
 	var totalKontrahenci = (countKontrahenci) ? Object.keys(countKontrahenci).length : 0;
 	var depthValue = getItemLocalStorage('Bocian.biAuditForm.depth');
-	console.warn({totalPracownicy: totalPracownicy, totalKontrahenci: totalKontrahenci})
+	if(DBG) console.warn({totalPracownicy: totalPracownicy, totalKontrahenci: totalKontrahenci})
 	$('.top--count-selected-item-pracownicy').text(totalPracownicy ? '('+totalPracownicy+')' : '');
 	$('.top--count-selected-item-kontrahenci').text(totalKontrahenci ? '('+totalKontrahenci+')' : '');
 	$('.bottom--count-selected-item-pracownicy').text(totalPracownicy ? "Wybrano: (" + totalPracownicy + ") pracownika/ów" : '');
@@ -1771,9 +2254,9 @@ function updateTopCounters() {
 
 		if ( ( totalPracownicy === 0 || totalKontrahenci === 0 ) && parseInt(depthValue) > 6) {
 			$('.bottom--message-warning-critical-search').text(messageCriticalSearchData);
-			console.log('dodanie komunikatu niżej');
+			if(DBG) console.log('dodanie komunikatu niżej');
 		} else {
-			console.log('wyczyc komunikatu niżej');
+			if(DBG) console.log('wyczyc komunikatu niżej');
 			$('.bottom--message-warning-critical-search').text('');
 		}
 
@@ -1979,16 +2462,16 @@ var Pagination = {
 };
 
 function todo__fetchRaport(id) {
-	p5WFS_GetFeature('default_db__x3A__BI_audit_ENERGA_RUM_KONTRAHENCI_POWIAZANIA:BI_audit_ENERGA_RUM_KONTRAHENCI_POWIAZANIA', {
+/*	p5WFS_GetFeature('default_db__x3A__BI_audit_ENERGA_RUM_KONTRAHENCI_POWIAZANIA:BI_audit_ENERGA_RUM_KONTRAHENCI_POWIAZANIA', {
 		primaryKey: id,
 		resolve: 'all',
 		resolveDepth: 3,
 	}).then(function (features) {
-		console.log('features', features)
+		if(DBG) console.log('features', features)
 	}).catch(function (e) {
-		console.warn(e)
+		if(DBG) console.warn(e)
 		p5UI__notifyAjaxCallback({ type: 'error', msg: e })
-	})
+	})*/
 }
 
 function removeFiltersPracownicy(event, btnNode) {
@@ -2101,7 +2584,7 @@ function loadMoreRecordFunctions(event, node, rowPK, namespace) {
 		jQuery(RECORD_MORE_FUNCTIONS_OPENED_NODE).popover('show')
 
 	}).catch(function (err) {
-		// console.log('err', err)
+		if(DBG)  console.log('err', err);
 	})
 }
 
@@ -2136,7 +2619,6 @@ global.p5UI_TableAjax_generateFunctionNode = p5UI_TableAjax_generateFunctionNode
 
 $(document).ready(function(){
 	rootChangeForm();
-	detectChoiseFilter();
 	updateTopCounters();
 });
 
@@ -2191,6 +2673,7 @@ global.addKontrahenciAllToGroup = addKontrahenciAllToGroup;
 
 global.showViewUploadFile = showViewUploadFile;
 global.parseCsvFile = parseCsvFile;
+global.graphShowHide = graphShowHide;
 global.clearListLocalStorageByType = clearListLocalStorageByType;
 
 // message

Some files were not shown because too many files changed in this diff