<commit_before>/* global angular: true */

angular.module('webPGQ.directives')
    .directive('digraph', ['$timeout', '$window', '$', 'd3', 'dagreD3', function($timeout, $window, $, d3, dagreD3)
    {
        function link(scope, element)//, attrs)
        {
            var gElem = d3.select(element.get(0));

            var layout = dagreD3.layout()
                .nodeSep(40)
                .rankSep(40)
                .rankDir("RL");

            var zoom = d3.behavior.zoom();

            var renderer = new dagreD3.Renderer()
                .layout(layout)
                .transition(transition)
                .zoom(function(graph, svg)
                {
                    return zoom.on('zoom', function()
                    {
                        svg.attr('transform', 'translate(' + d3.event.translate + ')scale(' + d3.event.scale + ')');
                    });
                });

            // Override drawNodes to set up the hover.
            var defaultDrawNodes = renderer.drawNodes();

            var bboxes = {};
            renderer.drawNodes(function(g, svg)
            {
                var nodeElems = defaultDrawNodes(g, svg);

                nodeElems
                    .classed('clickable', true)
                    .each(function(u)
                    {
                        bboxes[u] = this.getBBox();

                        var node = g.node(u);
                        var metadata = node.metadata;

                        var popupSettings = {
                            on: 'click',
                            title: node.label,
                            //preserve: true,
                            position: 'top left',
                            html: '<table class="compact smallest definition ui table">' +
                                Object.keys(metadata)
                                    .filter(function(key)
                                    {
                                        return key != 'Output';
                                    })
                                    .map(function(key)
                                    {
                                        var val = metadata[key];
                                        if(typeof val == 'string')
                                        {
                                            val = val.replace(/,/g, ',\n');
                                        } // end if

                                        return '<tr><th>' + key + '</th><td><code>' + val + '</code></td></tr>';
                                    })
                                    .join('') +
                                '</table>',
                            className: {
                                popup: 'query-plan ui popup',
                            },
                        };

                        $(this).popup(popupSettings);
                    });

                addLabels(svg, g, 'Alias', function(u) { d3.select(this).attr("dy", bboxes[u].height); });

                addLabels(svg, g, 'Subplan Name', function(u) { d3.select(this).attr("dy", -bboxes[u].height); });

                function addLabels(root, graph, key, pos)
                {
                    var classname = "label-" + key.replace(/ /g, '-');

                    function getValue(u)
                    {
                        return graph.node(u).metadata[key];
                    } // end getValue

                    var nodes = graph.nodes().filter(function(u)
                    {
                        return !(graph.hasOwnProperty("children") && graph.children(u).length && Boolean(getValue(u)));
                    });

                    var labelElems = root
                        .selectAll("g." + classname)
                        .classed("enter", false)
                        .data(
                            nodes.filter(function(u) { return Boolean(getValue(u)); }),
                            function(u) { return u; }
                        );

                    console.log("labelElems:", labelElems);

                    var marginX = 2, marginY = 2;

                    labelElems
                        .enter()
                            .append("g")
                                .style("opacity", 0)
                                .attr("class", classname + " enter")
                                .attr("transform", transform);

                    labelElems.each(function(u)
                    {
                        var label = d3.select(this);
                        var background = label.append("rect");

                        var textGroup = label.append("g");
                        textGroup.append("text")
                            .append("tspan")
                                .attr("dy", "1em")
                                .attr("x", "1")
                                .text(getValue(u));

                        var labelBBox = textGroup.node().getBBox();
                        textGroup.attr("transform",
                            "translate(" + (-labelBBox.width / 2) + "," + (-labelBBox.height / 2) + ")");

                        var bbox = label.node().getBBox();

                        background
                            //.attr("rx", node.rx ? node.rx : 5)
                            //.attr("ry", node.ry ? node.ry : 5)
                            .attr("x", - (bbox.width / 2 + marginX))
                            .attr("y", - (bbox.height / 2 + marginY))
                            .attr("width", bbox.width + 2 * marginX)
                            .attr("height", bbox.height + 2 * marginY)
                            .attr("opacity", 0.5)
                            .attr("fill", "#fff");

                        label.each(pos);
                    });

                    function transform(u)
                    {
                        var value = graph.node(u);
                        return "translate(" + value.x + "," + value.y + ")";
                    } // end transform

                    //transition(labelElems.filter(".enter"))
                    labelElems.filter(".enter")
                        .style("opacity", 1);

                    //transition(labelElems.exit())
                    labelElems.exit()
                        .style("opacity", 0)
                        .remove();
                } // end addLabels

                return nodeElems;
            });

            var defaultPostRender = renderer.postRender();

            renderer.postRender(function(g, svg)
            {
                defaultPostRender(g, svg);

                svg.selectAll("defs marker")
                    .attr("refX", 5)<commit_msg>Reworked digraph directive to add labels next to nodes for various things.<commit_after>/* global angular: true */

angular.module('webPGQ.directives')
    .directive('digraph', ['$timeout', '$window', '$', 'd3', 'dagreD3', function($timeout, $window, $, d3, dagreD3)
    {
        function link(scope, element)//, attrs)
        {
            var gElem = d3.select(element.get(0));

            var layout = dagreD3.layout()
                .nodeSep(50)
                .rankSep(50)
                .rankDir("RL");

            var zoom = d3.behavior.zoom();

            var renderer = new dagreD3.Renderer()
                .layout(layout)
                .transition(transition)
                .zoom(function(graph, svg)
                {
                    return zoom.on('zoom', function()
                    {
                        svg.attr('transform', 'translate(' + d3.event.translate + ')scale(' + d3.event.scale + ')');
                    });
                });

            var bboxes = {};

            // Override drawNodes to set up popups and node labels.
            var defaultDrawNodes = renderer.drawNodes();
            renderer.drawNodes(function(g, svg)
            {
                renderer._labelElems = renderer._labelElems || {};
                renderer._labelTransforms = renderer._labelTransforms || {};

                var nodeElems = defaultDrawNodes(g, svg);

                nodeElems
                    .classed('clickable', true)
                    .each(function(u)
                    {
                        bboxes[u] = this.getBBox();

                        var node = g.node(u);
                        var metadata = node.metadata;

                        var popupSettings = {
                            on: 'click',
                            title: node.label,
                            //preserve: true,
                            position: 'top left',
                            html: '<table class="compact smallest definition ui table">' +
                                Object.keys(metadata)
                                    .filter(function(key)
                                    {
                                        return key != 'Output' && key != 'References';
                                    })
                                    .map(function(key)
                                    {
                                        var val = metadata[key];
                                        if(typeof val == 'string')
                                        {
                                            val = val.replace(/,/g, ',\n');
                                        } // end if

                                        return '<tr><th>' + key + '</th><td><code>' + val + '</code></td></tr>';
                                    })
                                    .join('') +
                                '</table>',
                            className: {
                                popup: 'query-plan ui popup',
                            },
                        };

                        $(this).popup(popupSettings);
                    });

                addLabels(svg, g, 'Alias', {
                    transform: function(g, u)
                    {
                        var value = g.node(u);
                        var bbox = this.getBBox();
                        return 'translate(' + value.x + ',' + (value.y + (bboxes[u].height / 2) + (bbox.height / 2) - 2) + ')';
                    },
                    text: function(alias) { return JSON.stringify(alias); }
                });

                addLabels(svg, g, 'Subplan Name', {
                    transform: function(g, u)
                    {
                        var value = g.node(u);
                        var bbox = this.getBBox();
                        return 'translate(' + value.x + ',' + (value.y - (bboxes[u].height / 2) - (bbox.height / 2) + 2) + ')';
                    }
                });

                addLabels(svg, g, 'References', {
                    transform: function(g, u)
                    {
                        var value = g.node(u);
                        var bbox = this.getBBox();
                        return 'translate(' + (value.x + (bboxes[u].width / 2) + (bbox.width / 2) - 2) + ',' + value.y + ')';
                    },
                    link: function(ref) { return ref.id; },
                    text: function(ref) { return '\u2192' + ref.name; }
                });

                function addLabels(root, graph, key, options)
                {
                    options = options || {};
                    renderer._labelTransforms[key] = options.transform;
                    var classname = 'label-' + key.replace(/ /g, '-');
                    var groupClassname = 'labels-' + key.replace(/ /g, '-');

                    svg.selectAll('g.' + groupClassname)
                        .data([groupClassname])
                        .enter()
                            .append('g')
                            .attr('class', function(d) { return d; });

                    var labelGroup = svg.select('g.' + groupClassname);

                    function getValue(u)
                    {
                        return graph.node(u).metadata[key];
                    } // end getValue

                    var nodes = graph.nodes().filter(function(u)
                    {
                        return !(graph.hasOwnProperty('children') && graph.children(u).length) && Boolean(getValue(u));
                    });

                    var labelElems = labelGroup
                        .selectAll('g.' + classname)
                        .classed('enter', false)
                        .data(nodes, function(u) { return u; });

                    renderer._labelElems[key] = labelElems;

                    var marginX = 2, marginY = 2;

                    var entering = labelElems
                        .enter()
                            .append('g')
                                .style('opacity', 0)
                                .attr('id', function(u) { return classname + u; })
                                .attr('class', classname + ' enter');

                    entering.each(function(u)
                    {
                        var label = d3.select(this);
                        var background = label.append('rect');

                        var textGroup = label.append('g');
                        var text = textGroup.append('text')
                            .attr('font-size', '14px');

                        var lines = getValue(u);
                        if(typeof lines == 'string')
                        {
                            lines = [lines];
                        } // end if

                        lines.forEach(function(line)
                        {
                            var appendTspanTo = text;
                            var linkTo;
                            if(options.link)
                            {
                                linkTo = options.link(line);
                            } // end if

                            if(options.text)
                            {
                                line = options.text(line);
                            } // end if

                            appendTspanTo.append('tspan')
                                .attr('dy', '1em')
                                .attr('x', '0')
                                .text(line);
                        }); // end lines.forEach iterator

                        var bbox = label.node().getBBox();

                        background
                            .attr('rx', 5)
                            .attr('ry', 5)
                            .attr('x', - (bbox.width / 2 + marginX))
                            .attr('y', - (bbox.height / 2 + marginY))
                            .attr('width', bbox.width + 2 * marginX)
                            .attr('height', bbox.height + 2 * marginY)
                            .attr('opacity', 0.5)
                            .attr('fill', '#eee');

                        var labelBBox = textGroup.node().getBBox();
                        textGroup.attr('transform',
                            'translate(' + (-labelBBox.width / 2) + ',' + (-labelBBox.height / 2) + ')');
                    });

                    labelElems.exit()
                        .style('opacity', 0)
                        .remove();
                } // end addLabels

                return nodeElems;
            });

            var defaultPositionNodes = renderer.positionNodes();
            renderer.positionNodes(function(graph, svgNodes)
            {
                defaultPositionNodes(graph, svgNodes);

                function transform(func)
                {
                    return function(u)
                    {
                        return func.call(this, graph, u);
                    };
                } // end transform

                for(var key in renderer._labelElems)
                {
                    var labelElems = renderer._labelElems[key];

                    labelElems
                        .style('opacity', 1)
                        .attr('transform', transform(renderer._labelTransforms[key]));
                } // end for
            });

            var defaultPostRender = renderer.postRender();
            renderer.postRender(function(g, svg)
            {
                defaultPostRender(g, svg);

                svg.selectAll("defs marker")
                    .attr("refX", 5)
{'fraction_non_alphanumeric': 0.11782661782661782, 'fraction_numerical': 0.005189255189255189, 'mean_word_length': 0.8188985121030424, 'pattern_counts': {'":': 0, '<': 23, '<?xml version=': 0, '>': 23, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 34, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '2863708', 'n_tokens_mistral': 3962, 'n_tokens_neox': 3573, 'n_words': 721}