App-Netdisco

 view release on metacpan or  search on metacpan

share/public/javascripts/d3-force-network-chart.js  view on Meta::CPAN


    }; // --> END v.main.setupConfiguration

    /*******************************************************************************************************************
     * MAIN: SETUP DOM
     */
    v.main.setupDom = function() {

        // create reference to body
        v.dom.body = d3.select("body");

        // create DOM container element, if not existing (if we have an APEX context, it is already created from the
        // APEX engine )
        if (document.querySelector("#" + v.dom.containerId) === null) {
            v.dom.container = v.dom.body.append("div")
                .attr("id", v.dom.containerId);
        } else {
            v.dom.container = d3.select("#" + v.dom.containerId);
            d3.selectAll("#" + v.dom.containerId + "_customizing").remove();
            // d3.selectAll("#" + v.dom.containerId + "_tooltip").remove();
        }

        // create SVG element, if not existing (if we have an APEX context, it is already created from the APEX plugin )
        if (document.querySelector("#" + v.dom.containerId + " svg") === null) {
            v.dom.svg = v.dom.container.append("svg");
        } else {
            v.dom.svg = d3.select("#" + v.dom.containerId + " svg");
            d3.selectAll("#" + v.dom.containerId + " svg *").remove();
        }

        v.dom.svgParent = d3.select(v.dom.svg.node().parentNode);
        if (v.conf.setDomParentPaddingToZero) {
            v.dom.svgParent.style("padding", "0");
        }

        // configure SVG element
        v.dom.svg
            .attr("class", "net_gobrechts_d3_force")
            .classed("border", v.conf.showBorder)
            .attr("width", v.conf.width)
            .attr("height", v.conf.height);

        // calculate width of SVG parent
        v.dom.containerWidth = v.tools.getSvgParentInnerWidth();
        if (v.conf.useDomParentWidth) {
            v.dom.svg.attr("width", v.dom.containerWidth);
        }

        // create definitions element inside the SVG element
        v.dom.defs = v.dom.svg.append("defs");

        // create overlay element to fetch events for lasso & zoom
        v.dom.graphOverlay = v.dom.svg.append("g").attr("class", "graphOverlay");

        // create element for resizing the overlay g element
        v.dom.graphOverlaySizeHelper = v.dom.graphOverlay.append("rect").attr("class", "graphOverlaySizeHelper");

        // create graph group element for zoom and pan
        v.dom.graph = v.dom.graphOverlay.append("g").attr("class", "graph");

        // create legend group element
        v.dom.legend = v.dom.svg.append("g").attr("class", "legend");

        // create loading indicator
        v.dom.loading = v.dom.svg.append("svg:g")
            .attr("class", "loading")
            .style("display", "none");
        v.dom.loadingRect = v.dom.loading
            .append("svg:rect")
            .attr("width", v.tools.getGraphWidth())
            .attr("height", v.conf.height);
        v.dom.loadingText = v.dom.loading
            .append("svg:text")
            .attr("x", v.tools.getGraphWidth() / 2)
            .attr("y", v.conf.height / 2)
            .text("Loading...");

        // create marker definitions
        v.dom.defs
            .append("svg:marker")
            .attr("id", v.dom.containerId + "_highlighted")
            .attr("class", "highlighted")
            .attr("viewBox", "0 0 10 10")
            .attr("refX", 10)
            .attr("refY", 5)
            .attr("markerWidth", 5)
            .attr("markerHeight", 5)
            .attr("orient", "auto")
            .attr("markerUnits", "strokeWidth")
            .append("svg:path")
            .attr("d", "M0,0 L10,5 L0,10");

        v.dom.defs
            .append("svg:marker")
            .attr("id", v.dom.containerId + "_normal")
            .attr("class", "normal")
            .attr("viewBox", "0 0 10 10")
            .attr("refX", 10)
            .attr("refY", 5)
            .attr("markerWidth", 5)
            .attr("markerHeight", 5)
            .attr("orient", "auto")
            .attr("markerUnits", "strokeWidth")
            .append("svg:path")
            .attr("d", "M0,0 L10,5 L0,10");

        // create tooltip container
        if (document.querySelector("#" + v.dom.containerId + "_tooltip") === null) {
            v.dom.tooltip = v.dom.body.append("div")
                .attr("id", v.dom.containerId + "_tooltip")
                .attr("class", "net_gobrechts_d3_force_tooltip")
                .style("top", "0px")
                .style("left", "0px");
        } else {
            v.dom.tooltip = d3.select("#" + v.dom.containerId + "_tooltip");
        }

    }; // --> END v.main.setupDom


    /*******************************************************************************************************************
     * MAIN: SETUP FUNCTION REFERENCES

share/public/javascripts/d3-force-network-chart.js  view on Meta::CPAN


    // on lasso start function
    v.tools.onLassoStart = function(nodes) {
        var data = {};
        data.numberOfSelectedNodes = 0;
        data.idsOfSelectedNodes = null;
        data.numberOfNodes = nodes.size();
        data.nodes = nodes;
        v.tools.log("Event lasso_start triggered.");
        v.tools.triggerApexEvent(document.querySelector("#" + v.dom.containerId),
            "net_gobrechts_d3_force_lassostart",
            data
        );
        if (typeof(v.conf.onLassoStartFunction) === "function") {
            v.conf.onLassoStartFunction.call(v.dom.svg, d3.event, data);
        }
    };

    // on lasso end function
    v.tools.onLassoEnd = function(nodes) {
        var data = {};
        data.numberOfSelectedNodes = 0;
        data.idsOfSelectedNodes = "";
        data.numberOfNodes = nodes.size();
        data.nodes = nodes;
        nodes.each(function(n) {
            if (n.selected) {
                data.idsOfSelectedNodes += (n.ID + ":");
                data.numberOfSelectedNodes++;
            }
        });
        data.idsOfSelectedNodes =
            (data.idsOfSelectedNodes.length > 0 ?
                data.idsOfSelectedNodes.substr(0, data.idsOfSelectedNodes.length - 1) :
                null);
        v.tools.log("Event lasso_end triggered.");
        v.tools.triggerApexEvent(document.querySelector("#" + v.dom.containerId),
            "net_gobrechts_d3_force_lassoend", data);
        if (typeof(v.conf.onLassoEndFunction) === "function") {
            v.conf.onLassoEndFunction.call(v.dom.svg, d3.event, data);
        }
    };

    // get offset for an element relative to the document: http://javascript.info/tutorial/coordinates
    v.tools.getOffsetRect = function(elem) {
        var box = elem.getBoundingClientRect();
        var body = document.body;
        var docElem = document.documentElement;
        var scrollTop = window.pageYOffset || docElem.scrollTop || body.scrollTop;
        var scrollLeft = window.pageXOffset || docElem.scrollLeft || body.scrollLeft;
        var clientTop = docElem.clientTop || body.clientTop || 0;
        var clientLeft = docElem.clientLeft || body.clientLeft || 0;
        var top = box.top + scrollTop - clientTop;
        var left = box.left + scrollLeft - clientLeft;
        return {
            top: Math.round(top),
            left: Math.round(left)
        };
    };

    // create legend
    v.tools.createLegend = function() {
        v.data.distinctNodeColorValues.forEach(function(colorString, i) {
            var color = colorString.split(";");
            v.dom.legend
                .append("circle")
                .attr("cx", 11)
                .attr("cy", v.conf.height - ((i + 1) * 14 - 3))
                .attr("r", 6)
                .attr("fill", v.tools.color(color[1]));
            v.dom.legend
                .append("text")
                .attr("x", 21)
                .attr("y", v.conf.height - ((i + 1) * 14 - 6))
                .text((color[0] ? color[0] : color[1]));
        });
    };

    // remove legend
    v.tools.removeLegend = function() {
        v.dom.legend.selectAll("*").remove();
    };

    // write conf object into customization wizard
    v.tools.writeConfObjectIntoWizard = function() {
        if (v.status.customize) {
            v.dom.customizeConfObject.text(JSON.stringify(graph.optionsCustomizationWizard(), null, "  "));
        }
    };

    // create customize link
    v.tools.createCustomizeLink = function() {
        if (!v.status.customize &&
            (v.conf.debug || document.querySelector("#apex-dev-toolbar") || document.querySelector("#apexDevToolbar"))
        ) {
            if (document.querySelector("#d3-force-customize-link") === null) {
                v.dom.svg.append("svg:text")
                    .attr("id", "d3-force-customize-link")
                    .attr("class", "link")
                    .attr("x", 5)
                    .attr("y", 15)
                    .attr("text-anchor", "start")
                    .text("Customize Me")
                    .on("click", function() {
                        graph.customize(true);
                    });
            }
        }
    };

    // remove customize link
    v.tools.removeCustomizeLink = function() {
        v.dom.svg.select("#d3-force-customize-link").remove();
    };

    // dragability for customizing container
    v.tools.customizeDrag = d3.behavior.drag()
        .on("dragstart", function() {
            var mouseToBody = d3.mouse(document.body);
            v.dom.customizePosition = v.tools.getOffsetRect(document.querySelector("#" + v.dom.containerId +
                "_customizing"));
            v.dom.customizePosition.mouseLeft = mouseToBody[0] - v.dom.customizePosition.left;
            v.dom.customizePosition.mouseTop = mouseToBody[1] - v.dom.customizePosition.top;
        })
        .on("drag", function() {
            var mouseToBody = d3.mouse(document.body);
            v.dom.customize
                .style("left", Math.max(0,
                    mouseToBody[0] - v.dom.customizePosition.mouseLeft) + "px")
                .style("top", Math.max(0,
                    mouseToBody[1] - v.dom.customizePosition.mouseTop) + "px");
        })
        .on("dragend", function() {
            //v.dom.customizePosition = v.tools.getOffsetRect(document.querySelector("#" + v.dom.containerId +
            //"_customizing"));
            v.dom.customizePosition = v.tools.getOffsetRect(v.dom.customize.node());
        });

    // create customize wizard, if graph not rendering
    v.tools.createCustomizeWizardIfNotRendering = function() {
        if (v.status.customize && !v.status.graphRendering) {

share/public/javascripts/d3-force-network-chart.js  view on Meta::CPAN

                        v.data.links = [];
                    }
                } else {
                    message = "Missing root element named data.";
                    v.tools.logError(message);
                    v.data = {
                        "nodes": [{
                            "ID": "1",
                            "LABEL": "ERROR: " + message,
                            "COLORVALUE": "1",
                            "SIZEVALUE": "1"
                        }],
                        "links": []
                    };
                }
            } else {
                message = "Unable to parse your data - please consult the API reference for possible data formats.";
                v.tools.logError(message);
                v.data = {
                    "nodes": [{
                        "ID": "1",
                        "LABEL": "ERROR: " + message,
                        "COLORVALUE": "1",
                        "SIZEVALUE": "1"
                    }],
                    "links": []
                };
            }

            // switch links to point to node objects instead of id's (needed for force layout) and calculate attributes
            v.data.idLookup = []; // helper array to lookup node objects by id's
            v.data.nodes.forEach(function(n) {
                n.SIZEVALUE = parseFloat(n.SIZEVALUE); // convert size to float value
                n.LABELCIRCULAR = v.tools.parseBool(n.LABELCIRCULAR); // convert labelCircular to boolean
                if (n.fixed) {
                    n.fixed = v.tools.parseBool(n.fixed);
                } // convert fixed to boolean
                if (n.x) {
                    n.x = parseFloat(n.x);
                } // convert X position to float value
                if (n.y) {
                    n.y = parseFloat(n.y);
                } // convert Y position to float value
                v.data.idLookup[n.ID] = n; // add object reference to lookup array
            });
            v.data.links.forEach(function(l) {
                l.source = v.data.idLookup[l.FROMID]; // add attribute source as a node reference to the link
                l.target = v.data.idLookup[l.TOID]; // add attribute target as a node reference to the link
            });

            // sort out links with invalid node references
            v.data.links = v.data.links.filter(function(l) {
                return typeof l.source !== "undefined" && typeof l.target !== "undefined";
            });

            // create helper array to lookup if nodes are neighbors
            v.data.neighbors = v.data.links.map(function(l) {
                return l.FROMID + ":" + l.TOID;
            });

            // calculate distinct node colors for the legend
            v.data.distinctNodeColorValues = v.data.nodes
                .map(function(n) {
                    return (n.COLORLABEL ? n.COLORLABEL : "") + ";" + n.COLORVALUE;
                })
                // http://stackoverflow.com/questions/1960473/unique-values-in-an-array
                .filter(function(value, index, self) {
                    return self.indexOf(value) === index;
                })
                .sort(function(a, b) { // http://www.sitepoint.com/sophisticated-sorting-in-javascript/
                    var x = a.toLowerCase(),
                        y = b.toLowerCase();
                    return x < y ? 1 : x > y ? -1 : 0;
                });

            // calculate distinct link colors for the markers
            v.data.distinctLinkColorValues = v.data.links
                .map(function(l) {
                    return l.COLOR;
                })
                // http://stackoverflow.com/questions/28607451/removing-undefined-values-from-array
                // http://stackoverflow.com/questions/1960473/unique-values-in-an-array
                .filter(Boolean)
                .filter(function(value, index, self) {
                    return self.indexOf(value) === index;
                })
                .sort(function(a, b) { // http://www.sitepoint.com/sophisticated-sorting-in-javascript/
                    var x = a.toLowerCase(),
                        y = b.toLowerCase();
                    return x < y ? 1 : x > y ? -1 : 0;
                });

            // apply user provided positions once (new data has priority)
            if (v.conf.positions) {
                if (v.conf.positions.constructor === Array) {
                    v.conf.positions.forEach(function(n) {
                        if (v.data.idLookup[n.ID] !== undefined) {
                            if (!v.data.idLookup[n.ID].fixed) {
                                v.data.idLookup[n.ID].fixed = n.fixed;
                            }
                            if (!v.data.idLookup[n.ID].x) {
                                v.data.idLookup[n.ID].x = v.data.idLookup[n.ID].px = n.x;
                            }
                            if (!v.data.idLookup[n.ID].y) {
                                v.data.idLookup[n.ID].y = v.data.idLookup[n.ID].py = n.y;
                            }
                        }
                    });
                } else {
                    v.tools.logError("Unable to set node positions: positions method parameter must be an array of " +
                        "node positions");
                }
            }
            // apply old positions (new data has priority - if graph was ready, than user provided positions are
            // already present in old positions) - see also graph.positions method
            else if (v.status.graphOldPositions) {
                v.status.graphOldPositions.forEach(function(n) {
                    if (v.data.idLookup[n.ID] !== undefined) {
                        if (!v.data.idLookup[n.ID].fixed) {
                            v.data.idLookup[n.ID].fixed = n.fixed;
                        }

share/public/javascripts/d3-force-network-chart.js  view on Meta::CPAN

            .chargeDistance(v.conf.chargeDistance)
            .gravity(v.conf.gravity)
            .linkStrength(v.conf.linkStrength)
            .friction(v.conf.friction)
            .theta(v.conf.theta);

        // start visualization
        v.main.force
            .nodes(v.data.nodes)
            .links(v.data.links)
            .start();

        if (v.status.customize) {
            v.tools.createCustomizeWizard();
        } else {
            v.tools.createCustomizeLink();
        }

        v.status.graphReady = true;
        v.status.graphRendering = false;

        v.tools.triggerApexEvent(document.querySelector("#" + v.dom.containerId), "apexafterrefresh");

        return graph;
    };

    /**
     * The `resume` method restarts only the force on your graph without a `render` cycle. This saves CPU time and can be useful if you change only things in your graph which do not need rendering to taking into effect:
     *
     *     example.releaseFixedNodes().resume();
     * @see {@link module:API.start}
     * @see {@link module:API.render}
     * @returns {Object} The graph object for method chaining.
     */
    graph.resume = function() {
        v.main.force.resume();
        v.tools.createCustomizeWizardIfNotRendering();
        return graph;
    };

    /**
     * If true, a class named border is added to the SVG element, if false the class will be removed. The border itself is defined in the delivered CSS - you can overwrite it if the current style does not match your needs. No `render` or `resume` cal...
     *
     *     example.showBorder(false);
     * @param {boolean} [value=true] - The new config value.
     * @returns {(boolean|Object)} The current config value if no parameter is given or the graph object for method chaining.
     */
    graph.showBorder = function(value) {
        if (!arguments.length) {
            return v.conf.showBorder;
        }
        v.conf.showBorder = value;
        if (v.status.graphStarted) {
            v.dom.svg.classed("border", v.conf.showBorder);
            v.tools.createCustomizeWizardIfNotRendering();
        }
        return graph;
    };

    /**
     * If true, a legend for all COLORVALUEs in the node data is rendered in the bottom left corner of the graph. No `render` or `resume` call needed to take into effect:
     *
     *     example.showLegend(false);
     * @param {boolean} [value=true] - The new config value.
     * @returns {(boolean|Object)} The current config value if no parameter is given or the graph object for method chaining.
     */
    graph.showLegend = function(value) {
        if (!arguments.length) {
            return v.conf.showLegend;
        }
        v.conf.showLegend = value;
        if (v.status.graphStarted) {
            if (v.conf.showLegend) {
                v.tools.removeLegend();
                v.tools.createLegend();
            } else {
                v.tools.removeLegend();
            }
            v.tools.createCustomizeWizardIfNotRendering();
        }
        return graph;
    };

    /**
     * If true, then links with the same source and target are rendered along a path around the node bottom. Needs a `render` call to take into effect:
     *
     *     example.showSelfLinks(false).render();
     * @param {boolean} [value=true] - The new config value.
     * @returns {(boolean|Object)} The current config value if no parameter is given or the graph object for method chaining.
     */
    graph.showSelfLinks = function(value) {
        if (!arguments.length) {
            return v.conf.showSelfLinks;
        }
        v.conf.showSelfLinks = value;
        if (v.status.graphStarted) {
            v.tools.createCustomizeWizardIfNotRendering();
        }
        return graph;
    };

    /**
     * If true, you get an marker at the end of a link. Needs a `render` call to take into effect:
     *
     *     example.showLinkDirection(false).render();
     * @param {boolean} [value=true] - The new config value.
     * @returns {(boolean|Object)} The current config value if no parameter is given or the graph object for method chaining.
     */
    graph.showLinkDirection = function(value) {
        if (!arguments.length) {
            return v.conf.showLinkDirection;
        }
        v.conf.showLinkDirection = value;
        if (v.status.graphStarted) {
            v.tools.createCustomizeWizardIfNotRendering();
        }
        return graph;
    };

    /**
     * If true and you provided in your source data an attribute INFOSTRING, then a tooltip is shown by hovering a node. No `render` or `resume` call needed to take into effect:



( run in 1.182 second using v1.01-cache-2.11-cpan-39bf76dae61 )