Index: reference/jquery-plugins/select2-4.0.13/src/scss/theme/default/layout.scss =================================================================== diff -u -re2929e3c8af73ddb8b9f8e63595040abddb6de50 -r729511b6d2f89adea319c5216f58e85783ed5121 --- reference/jquery-plugins/select2-4.0.13/src/scss/theme/default/layout.scss (.../layout.scss) (revision e2929e3c8af73ddb8b9f8e63595040abddb6de50) +++ reference/jquery-plugins/select2-4.0.13/src/scss/theme/default/layout.scss (.../layout.scss) (revision 729511b6d2f89adea319c5216f58e85783ed5121) @@ -285,7 +285,9 @@ } } +//////////////////////////////////////////////////////////////////////////////////////// //서버 바인딩 할 수가 없어서 프로토타입 목적으로 json 을 만들어서 로드하는 함수 +//////////////////////////////////////////////////////////////////////////////////////// var getJsonForPrototype = function (url, bindTemplate) { ajaxGet(url).then(function (data) { bindTemplate(data); Index: arms/js/pdService.js =================================================================== diff -u -r7e1cd2d7ba08c15c9e9e9f82691cde62b2015252 -r729511b6d2f89adea319c5216f58e85783ed5121 --- arms/js/pdService.js (.../pdService.js) (revision 7e1cd2d7ba08c15c9e9e9f82691cde62b2015252) +++ arms/js/pdService.js (.../pdService.js) (revision 729511b6d2f89adea319c5216f58e85783ed5121) @@ -53,7 +53,16 @@ $(".body-middle").hide(); // 데이터 테이블 로드 함수 - dataTableLoad(); + var waitDataTable = setInterval(function () { + try { + if (!$.fn.DataTable.isDataTable("#pdservice_table")) { + dataTableLoad(); + clearInterval(waitDataTable); + } + } catch (err) { + console.log("서비스 데이터 테이블 로드가 완료되지 않아서 초기화 재시도 중..."); + } + }, 313 /*milli*/); // --- 에디터 설정 --- // var waitCKEDITOR = setInterval(function () { Index: arms/js/pdServiceJira.js =================================================================== diff -u -r6622451c264949d6e1be2b3f8f8b41410fecfcf3 -r729511b6d2f89adea319c5216f58e85783ed5121 --- arms/js/pdServiceJira.js (.../pdServiceJira.js) (revision 6622451c264949d6e1be2b3f8f8b41410fecfcf3) +++ arms/js/pdServiceJira.js (.../pdServiceJira.js) (revision 729511b6d2f89adea319c5216f58e85783ed5121) @@ -39,7 +39,7 @@ dataTableLoad(); - $.getScript("./js/pdServiceVersion/initd3chart.js").done(function (script, textStatus) { + $.getScript("./js/pdServiceVersion/initD3Chart.js").done(function (script, textStatus) { console.log("트리 차트 생성"); }); }); Index: arms/js/pdServiceVersion/initD3Chart.js =================================================================== diff -u --- arms/js/pdServiceVersion/initD3Chart.js (revision 0) +++ arms/js/pdServiceVersion/initD3Chart.js (revision 729511b6d2f89adea319c5216f58e85783ed5121) @@ -0,0 +1,597 @@ +//////////////////////////////////////////////////////////////////////////////////////// +// --- 연결 차트 표시 --- // +//////////////////////////////////////////////////////////////////////////////////////// +/* ---------------------------------------- d3 config ------------------------------------ */ +/* d3 */ +// var treeData = { +// name: "product service name", +// type: "Product(service)", +// children: [ +// { +// name: "Visualization", +// type: "Jira JQL", +// children: [ +// { +// name: "test", +// type: "Jira JQL" +// } +// ] +// } +// ] +// }; +function initD3Chart(href) { + d3.json(href, function(error, data) { + + d3.selectAll("#tree_container svg").remove(); + + var treeData = data.response; + // Calculate total nodes, max label length + var totalNodes = 0; + var maxLabelLength = 0; + // variables for drag/drop + var selectedNode = null; + var draggingNode = null; + // panning variables + var panSpeed = 200; + var panBoundary = 20; // Within 20px from edges will pan when dragging. + // Misc. variables + var i = 0; + var duration = 750; + var root; + + // size of the diagram + var viewerWidth = $("#tree_container")[0].clientWidth; + var viewerHeight = 295; + + var tree = d3.layout.tree().size([viewerHeight, viewerWidth]); + + // define a d3 diagonal projection for use by the node paths later on. + var diagonal = d3.svg.diagonal().projection(function(d) { + return [d.y, d.x]; + }); + + // A recursive helper function for performing some setup by walking through all nodes + function visit(parent, visitFn, childrenFn) { + if (!parent) return; + + visitFn(parent); + + var children = childrenFn(parent); + if (children) { + var count = children.length; + for (var i = 0; i < count; i++) { + visit(children[i], visitFn, childrenFn); + } + } + } + + // Call visit function to establish maxLabelLength + visit( + treeData, + function(d) { + totalNodes++; + maxLabelLength = Math.max(d.name.length, maxLabelLength); + }, + function(d) { + return d.children && d.children.length > 0 ? d.children : null; + } + ); + + // sort the tree according to the node names + function sortTree() { + tree.sort(function(a, b) { + return b.name.toLowerCase() < a.name.toLowerCase() ? 1 : -1; + }); + } + + // Sort the tree initially incase the JSON isn't in a sorted order. + sortTree(); + + // TODO: Pan function, can be better implemented. + function pan(domNode, direction) { + var speed = panSpeed; + if (panTimer) { + clearTimeout(panTimer); + translateCoords = d3.transform(svgGroup.attr("transform")); + if (direction == "left" || direction == "right") { + translateX = direction == "left" ? translateCoords.translate[0] + speed : translateCoords.translate[0] - speed; + translateY = translateCoords.translate[1]; + } else if (direction == "up" || direction == "down") { + translateX = translateCoords.translate[0]; + translateY = direction == "up" ? translateCoords.translate[1] + speed : translateCoords.translate[1] - speed; + } + scaleX = translateCoords.scale[0]; + scaleY = translateCoords.scale[1]; + scale = zoomListener.scale(); + svgGroup.transition().attr("transform", "translate(" + translateX + "," + translateY + ")scale(" + scale + ")"); + d3.select(domNode) + .select("g.node") + .attr("transform", "translate(" + translateX + "," + translateY + ")"); + zoomListener.scale(zoomListener.scale()); + zoomListener.translate([translateX, translateY]); + panTimer = setTimeout(function() { + pan(domNode, speed, direction); + }, 50); + } + } + + // Define the zoom function for the zoomable tree + function zoom() { + //edit 313devops + svgGroup.attr("transform", "translate(" + d3.event.translate + ")scale(" + d3.event.scale + ")"); + //svgGroup.attr("transform", "translate(" + "221,79" + ")scale(" + 1.5 + ")"); + } + + // define the zoomListener which calls the zoom function on the "zoom" event constrained within the scaleExtents + var zoomListener = d3.behavior.zoom().scaleExtent([0.5, 2.5]).on("zoom", zoom); + + function initiateDrag(d, domNode) { + draggingNode = d; + d3.select(domNode).select(".ghostCircle").attr("pointer-events", "none"); + d3.selectAll(".ghostCircle").attr("class", "ghostCircle show"); + d3.select(domNode).attr("class", "node activeDrag"); + + svgGroup.selectAll("g.node").sort(function(a, b) { + // select the parent and sort the path's + if (a.id != draggingNode.id) return 1; // a is not the hovered element, send "a" to the back + else return -1; // a is the hovered element, bring "a" to the front + }); + // if nodes has children, remove the links and nodes + if (nodes.length > 1) { + // remove link paths + links = tree.links(nodes); + nodePaths = svgGroup + .selectAll("path.link") + .data(links, function(d) { + return d.target.id; + }) + .remove(); + // remove child nodes + nodesExit = svgGroup + .selectAll("g.node") + .data(nodes, function(d) { + return d.id; + }) + .filter(function(d, i) { + if (d.id == draggingNode.id) { + return false; + } + return true; + }) + .remove(); + } + + // remove parent link + parentLink = tree.links(tree.nodes(draggingNode.parent)); + svgGroup + .selectAll("path.link") + .filter(function(d, i) { + if (d.target.id == draggingNode.id) { + return true; + } + return false; + }) + .remove(); + + dragStarted = null; + } + + // define the baseSvg, attaching a class for styling and the zoomListener + var baseSvg = d3 + .select("#tree_container") + .append("svg") + .attr("width", viewerWidth) + .attr("height", viewerHeight) + .attr("class", "overlay") + .call(zoomListener); + + // Define the drag listeners for drag/drop behaviour of nodes. + dragListener = d3.behavior + .drag() + .on("dragstart", function(d) { + if (d == root) { + return; + } + dragStarted = true; + nodes = tree.nodes(d); + d3.event.sourceEvent.stopPropagation(); + // it's important that we suppress the mouseover event on the node being dragged. Otherwise it will absorb the mouseover event and the underlying node will not detect it d3.select(this).attr('pointer-events', 'none'); + }) + .on("drag", function(d) { + if (d == root) { + return; + } + if (dragStarted) { + domNode = this; + initiateDrag(d, domNode); + } + + // get coords of mouseEvent relative to svg container to allow for panning + relCoords = d3.mouse($("svg").get(0)); + if (relCoords[0] < panBoundary) { + panTimer = true; + pan(this, "left"); + } else if (relCoords[0] > $("svg").width() - panBoundary) { + panTimer = true; + pan(this, "right"); + } else if (relCoords[1] < panBoundary) { + panTimer = true; + pan(this, "up"); + } else if (relCoords[1] > $("svg").height() - panBoundary) { + panTimer = true; + pan(this, "down"); + } else { + try { + clearTimeout(panTimer); + } catch (e) { + } + } + + d.x0 += d3.event.dy; + d.y0 += d3.event.dx; + var node = d3.select(this); + node.attr("transform", "translate(" + d.y0 + "," + d.x0 + ")"); + updateTempConnector(); + }) + .on("dragend", function(d) { + if (d == root) { + return; + } + domNode = this; + if (selectedNode) { + // now remove the element from the parent, and insert it into the new elements children + var index = draggingNode.parent.children.indexOf(draggingNode); + if (index > -1) { + draggingNode.parent.children.splice(index, 1); + } + if (typeof selectedNode.children !== "undefined" || typeof selectedNode._children !== "undefined") { + if (typeof selectedNode.children !== "undefined") { + selectedNode.children.push(draggingNode); + } else { + selectedNode._children.push(draggingNode); + } + } else { + selectedNode.children = []; + selectedNode.children.push(draggingNode); + } + // Make sure that the node being added to is expanded so user can see added node is correctly moved + expand(selectedNode); + sortTree(); + endDrag(); + } else { + endDrag(); + } + }); + + function endDrag() { + selectedNode = null; + d3.selectAll(".ghostCircle").attr("class", "ghostCircle"); + d3.select(domNode).attr("class", "node"); + // now restore the mouseover event or we won't be able to drag a 2nd time + d3.select(domNode).select(".ghostCircle").attr("pointer-events", ""); + updateTempConnector(); + if (draggingNode !== null) { + update(root); + centerNode(draggingNode); + draggingNode = null; + } + } + + // Helper functions for collapsing and expanding nodes. + function collapse(d) { + if (d.children) { + d._children = d.children; + d._children.forEach(collapse); + d.children = null; + } + } + + function expand(d) { + if (d._children) { + d.children = d._children; + d.children.forEach(expand); + d._children = null; + } + } + + var overCircle = function(d) { + selectedNode = d; + updateTempConnector(); + }; + var outCircle = function(d) { + selectedNode = null; + updateTempConnector(); + }; + + // Function to update the temporary connector indicating dragging affiliation + var updateTempConnector = function() { + var data = []; + if (draggingNode !== null && selectedNode !== null) { + // have to flip the source coordinates since we did this for the existing connectors on the original tree + data = [ + { + source: { + x: selectedNode.y0, + y: selectedNode.x0 + }, + target: { + x: draggingNode.y0, + y: draggingNode.x0 + } + } + ]; + } + var link = svgGroup.selectAll(".templink").data(data); + + link.enter().append("path").attr("class", "templink").attr("d", d3.svg.diagonal()).attr("pointer-events", "none"); + + link.attr("d", d3.svg.diagonal()); + + link.exit().remove(); + }; + + // Function to center node when clicked/dropped so node doesn't get lost when collapsing/moving with large amount of children. + function centerNode(source) { + var nodes = tree.nodes(root); + console.log(nodes); + + scale = zoomListener.scale(); + scale = 1.0; + x = source.y0; + y = -source.x0; + + console.log("x => " + x); + + translateCoords = d3.transform(svgGroup.attr("transform")); + var speed = panSpeed; + translateCoords = d3.transform(svgGroup.attr("transform")); + translateX = translateCoords.translate[0] + speed; + console.log("translateX = " + translateX); + + x = x * scale + viewerWidth / 2 - translateX; + + console.log("x => " + x); + y = y * scale + viewerHeight / 2; + + d3.select("g") + .transition() + .duration(duration) + .attr("transform", "translate(" + x + "," + y + ")scale(" + scale + ")"); + zoomListener.scale(scale); + zoomListener.translate([x, y]); + } + + // Toggle children function + function toggleChildren(d) { + if (d.children) { + d._children = d.children; + d.children = null; + } else if (d._children) { + d.children = d._children; + d._children = null; + } + return d; + } + + // Toggle children on click. + function click(d) { + console.log("clickEvent", d); + if (d3.event.defaultPrevented) return; // click suppressed + d = toggleChildren(d); + update(d); + centerNode(d); + } + + function update(source) { + // Compute the new height, function counts total children of root node and sets tree height accordingly. + // This prevents the layout looking squashed when new nodes are made visible or looking sparse when nodes are removed + // This makes the layout more consistent. + var levelWidth = [1]; + var childCount = function(level, n) { + if (n.children && n.children.length > 0) { + if (levelWidth.length <= level + 1) levelWidth.push(0); + + levelWidth[level + 1] += n.children.length; + n.children.forEach(function(d) { + childCount(level + 1, d); + }); + } + }; + childCount(0, root); + var newHeight = d3.max(levelWidth) * 40; // 25 pixels per line + tree = tree.size([newHeight, viewerWidth]); + + // Compute the new tree layout. + var nodes = tree.nodes(root).reverse(), + links = tree.links(nodes); + + // Set widths between levels based on maxLabelLength. + nodes.forEach(function(d) { + d.y = d.depth * (maxLabelLength * 25); //maxLabelLength * 10px + // alternatively to keep a fixed scale one can set a fixed depth per level + // Normalize for fixed-depth by commenting out below line + // d.y = (d.depth * 500); //500px per level. + }); + + // Update the nodes… + node = svgGroup.selectAll("g.node").data(nodes, function(d) { + return d.id || (d.id = ++i); + }); + + // Enter any new nodes at the parent's previous position. + var nodeEnter = node + .enter() + .append("g") + .call(dragListener) + .attr("class", "node") + .attr("transform", function(d) { + return "translate(" + source.y0 + "," + source.x0 + ")"; + }) + .on("click", click); + + nodeEnter + .append("circle") + .attr("class", "nodeCircle") + .attr("r", 0) + .style("fill", function(d) { + return d._children ? "lightsteelblue" : "#fff"; + }); + + nodeEnter + .append("text") + .attr("x", function(d) { + return d.children || d._children ? -10 : 10; + }) + .attr("dy", function(d) { + return d.children ? "-1em" : ".35em"; + }) + .attr("class", "nodeText") + .attr("text-anchor", function(d) { + return d.children || d._children ? "end" : "start"; + }) + .text(function(d) { + return d.name; + }) + .style("text-align", "center") + .style("fill-opacity", 0); + + nodeEnter + .append("text") + .attr("x", function(d) { + return d.children ? -15 : -7; + }) + .attr("y", function(d) { + return 15; + }) + .text(function(d) { + return d.type; + }) + .on("click", function(d) { + window.location = d.url; + }) + .style("font-size", "8px"); + + // phantom node to give us mouseover in a radius around it + nodeEnter + .append("circle") + .attr("class", "ghostCircle") + .attr("r", 15) + .attr("opacity", function(d) { + return d.children ? 0.2 : 0; + }) // change this to zero to hide the target area + .style("fill", "red") + .attr("pointer-events", "mouseover") + .on("mouseover", function(node) { + overCircle(node); + }) + .on("mouseout", function(node) { + outCircle(node); + }); + + // Update the text to reflect whether node has children or not. + node + .select("text") + .attr("x", function(d) { + return d.children || d._children ? -10 : 10; + }) + .attr("text-anchor", function(d) { + return d.children || d._children ? "end" : "start"; + }) + .text(function(d) { + return d.name; + }); + + // Change the circle fill depending on whether it has children and is collapsed + node + .select("circle.nodeCircle") + .attr("r", 4.5) + .style("fill", function(d) { + return d._children ? "lightsteelblue" : "#fff"; + }); + + // Transition nodes to their new position. + var nodeUpdate = node + .transition() + .duration(duration) + .attr("transform", function(d) { + return "translate(" + d.y + "," + d.x + ")"; + }); + + // Fade the text in + nodeUpdate.select("text").style("fill-opacity", 1); + + // Transition exiting nodes to the parent's new position. + var nodeExit = node + .exit() + .transition() + .duration(duration) + .attr("transform", function(d) { + return "translate(" + source.y + "," + source.x + ")"; + }) + .remove(); + + nodeExit.select("circle").attr("r", 0); + + nodeExit.select("text").style("fill-opacity", 0); + + // Update the links… + var link = svgGroup.selectAll("path.link").data(links, function(d) { + return d.target.id; + }); + + // Enter any new links at the parent's previous position. + link + .enter() + .insert("path", "g") + .attr("class", "link") + .attr("d", function(d) { + var o = { + x: source.x0, + y: source.y0 + }; + return diagonal({ + source: o, + target: o + }); + }); + + // Transition links to their new position. + link.transition().duration(duration).attr("d", diagonal); + + // Transition exiting nodes to the parent's new position. + link + .exit() + .transition() + .duration(duration) + .attr("d", function(d) { + var o = { + x: source.x, + y: source.y + }; + return diagonal({ + source: o, + target: o + }); + }) + .remove(); + + // Stash the old positions for transition. + nodes.forEach(function(d) { + d.x0 = d.x; + d.y0 = d.y; + }); + } + + // Append a group which holds all nodes and which the zoom Listener can act upon. + var svgGroup = baseSvg.append("g"); + + // Define the root + root = treeData; + root.x0 = viewerHeight / 2; + root.y0 = 0; + + // Layout the tree initially and center on the root node. + update(root); + centerNode(root); + }); +} FishEye: Tag 729511b6d2f89adea319c5216f58e85783ed5121 refers to a dead (removed) revision in file `arms/js/pdServiceVersion/initd3chart.js'. FishEye: No comparison available. Pass `N' to diff?