Nhogs/popoto

View on GitHub
src/provider/provider.js

Summary

Maintainability
F
1 wk
Test Coverage
F
36%
import * as d3 from "d3";
import logger from "../logger/logger";
import query from "../query/query";
import graph from "../graph/graph";

var provider = {};

/**
 * Default color scale generator.
 * Used in getColor link and node providers.
 */
provider.colorScale = d3.scaleOrdinal(d3.schemeCategory10);
provider.link = {};
provider.link.Provider = {};
provider.taxonomy = {};
provider.taxonomy.Provider = {};
provider.node = {};
provider.node.Provider = {};

//------------------------------------------------
// LINKS

/**
 *  Get the text representation of a link.
 *
 * @param link the link to get the text representation.
 * @returns {string} the text representation of the link.
 */
provider.link.getTextValue = function (link) {
    if (provider.link.Provider.hasOwnProperty("getTextValue")) {
        return provider.link.Provider.getTextValue(link);
    } else {
        if (provider.link.DEFAULT_PROVIDER.hasOwnProperty("getTextValue")) {
            return provider.link.DEFAULT_PROVIDER.getTextValue(link);
        } else {
            logger.error("No provider defined for link getTextValue");
        }
    }
};

provider.link.getColor = function (link, element, attribute) {
    if (provider.link.Provider.hasOwnProperty("getColor")) {
        return provider.link.Provider.getColor(link, element, attribute);
    } else {
        if (provider.link.DEFAULT_PROVIDER.hasOwnProperty("getColor")) {
            return provider.link.DEFAULT_PROVIDER.getColor(link, element, attribute);
        } else {
            logger.error("No provider defined for getColor");
        }
    }
};

provider.link.getCSSClass = function (link, element) {
    if (provider.link.Provider.hasOwnProperty("getCSSClass")) {
        return provider.link.Provider.getCSSClass(link, element);
    } else {
        if (provider.link.DEFAULT_PROVIDER.hasOwnProperty("getCSSClass")) {
            return provider.link.DEFAULT_PROVIDER.getCSSClass(link, element);
        } else {
            logger.error("No provider defined for getCSSClass");
        }
    }
};

provider.link.getDistance = function (link) {
    if (provider.link.Provider.hasOwnProperty("getDistance")) {
        return provider.link.Provider.getDistance(link);
    } else {
        if (provider.link.DEFAULT_PROVIDER.hasOwnProperty("getDistance")) {
            return provider.link.DEFAULT_PROVIDER.getDistance(link);
        } else {
            logger.error("No provider defined for getDistance");
        }
    }
};

/**
 *  Get the semantic text representation of a link.
 *
 * @param link the link to get the semantic text representation.
 * @returns {string} the semantic text representation of the link.
 */
provider.link.getSemanticValue = function (link) {
    if (provider.link.Provider.hasOwnProperty("getSemanticValue")) {
        return provider.link.Provider.getSemanticValue(link);
    } else {
        if (provider.link.DEFAULT_PROVIDER.hasOwnProperty("getSemanticValue")) {
            return provider.link.DEFAULT_PROVIDER.getSemanticValue(link);
        } else {
            logger.error("No provider defined for getSemanticValue");
        }
    }
};

provider.colorLuminance = function (hex, lum) {

    // validate hex string
    hex = String(hex).replace(/[^0-9a-f]/gi, '');
    if (hex.length < 6) {
        hex = hex[0] + hex[0] + hex[1] + hex[1] + hex[2] + hex[2];
    }
    lum = lum || 0;

    // convert to decimal and change luminosity
    var rgb = "#", c, i;
    for (i = 0; i < 3; i++) {
        c = parseInt(hex.substr(i * 2, 2), 16);
        c = Math.round(Math.min(Math.max(0, c + (c * lum)), 255)).toString(16);
        rgb += ("00" + c).substr(c.length);
    }

    return rgb;
};

/**
 * Label provider used by default if none have been defined for a label.
 * This provider can be changed if needed to customize default behavior.
 * If some properties are not found in user customized providers, default values will be extracted from this provider.
 */
provider.link.DEFAULT_PROVIDER = {
    /**
     * Function used to return the text representation of a link.
     *
     * The default behavior is to return the internal relation name as text for relation links.
     * And return the target node text value for links between a node and its expanded values but only if text is not displayed on value node.
     *
     * @param link the link to represent as text.
     * @returns {string} the text representation of the link.
     */
    "getTextValue": function (link) {
        if (link.type === graph.link.LinkTypes.VALUE) {
            // Links between node and list of values.

            if (provider.node.isTextDisplayed(link.target)) {
                // Don't display text on link if text is displayed on target node.
                return "";
            } else {
                // No text is displayed on target node then the text is displayed on link.
                return provider.node.getTextValue(link.target);
            }

        } else {
            var targetName = "";
            if (link.type === graph.link.LinkTypes.SEGMENT) {
                targetName = " " + provider.node.getTextValue(link.target);
            }
            return link.label + targetName;
        }
    },


    /**
     *
     * @param link
     */
    "getDistance": function (link) {
        if (link.type === graph.link.LinkTypes.VALUE) {
            return (13 / 8) * (provider.node.getSize(link.source) + provider.node.getSize(link.target));
        } else {
            return (20 / 8) * (provider.node.getSize(link.source) + provider.node.getSize(link.target));
        }
    },

    /**
     * Return the color to use on links and relation donut segments.
     *
     *
     * Return null or undefined
     * @param link
     * @param element
     * @param attribute
     * @return {*}
     */
    "getColor": function (link, element, attribute) {
        if (link.type === graph.link.LinkTypes.VALUE) {
            return "#525863";
        } else {
            var colorId = link.source.label + link.label + link.target.label;

            var color = provider.colorScale(colorId);
            if (attribute === "stroke") {
                return provider.colorLuminance(color, -0.2);
            }
            return color;
        }
    },

    /**
     *
     * @param link
     * @param element
     * @return {string}
     */
    "getCSSClass": function (link, element) {
        var cssClass = "ppt-link__" + element;

        if (link.type === graph.link.LinkTypes.VALUE) {
            cssClass = cssClass + "--value";
        } else {
            var labelAsCSSName = "ppt-" + link.label.replace(/[^0-9a-z\-_]/gi, '');
            if (link.type === graph.link.LinkTypes.RELATION) {
                cssClass = cssClass + "--relation";

                if (link.target.count === 0) {
                    cssClass = cssClass + "--disabled";
                }

                cssClass = cssClass + " " + labelAsCSSName
            }
        }

        return cssClass;
    },

    /**
     * Function used to return a descriptive text representation of a link.
     * This representation should be more complete than getLinkTextValue and can contain semantic data.
     * This function is used for example to generate the label in the query viewer.
     *
     * The default behavior is to return the getLinkTextValue.
     *
     * @param link the link to represent as text.
     * @returns {string} the text semantic representation of the link.
     */
    "getSemanticValue": function (link) {
        return provider.link.getTextValue(link);
    }
};

provider.link.Provider = provider.link.DEFAULT_PROVIDER;

//------------------------------------------------
// TAXONOMY

/**
 *  Get the text representation of a taxonomy.
 *
 * @param label the label used for the taxonomy.
 * @returns {string} the text representation of the taxonomy.
 */
provider.taxonomy.getTextValue = function (label) {
    if (provider.taxonomy.Provider.hasOwnProperty("getTextValue")) {
        return provider.taxonomy.Provider.getTextValue(label);
    } else {
        if (provider.taxonomy.DEFAULT_PROVIDER.hasOwnProperty("getTextValue")) {
            return provider.taxonomy.DEFAULT_PROVIDER.getTextValue(label);
        } else {
            logger.error("No provider defined for taxonomy getTextValue");
        }
    }
};

/**
 *
 * @param label
 * @param element
 * @return {*}
 */
provider.taxonomy.getCSSClass = function (label, element) {
    if (provider.taxonomy.Provider.hasOwnProperty("getCSSClass")) {
        return provider.taxonomy.Provider.getCSSClass(label, element);
    } else {
        if (provider.taxonomy.DEFAULT_PROVIDER.hasOwnProperty("getCSSClass")) {
            return provider.taxonomy.DEFAULT_PROVIDER.getCSSClass(label, element);
        } else {
            logger.error("No provider defined for taxonomy getCSSClass");
        }
    }
};

/**
 * Label provider used by default if none have been defined for a label.
 * This provider can be changed if needed to customize default behavior.
 * If some properties are not found in user customized providers, default values will be extracted from this provider.
 */
provider.taxonomy.DEFAULT_PROVIDER = {
    /**
     * Function used to return the text representation of a taxonomy.
     *
     * The default behavior is to return the label without changes.
     *
     * @param label the label used to represent the taxonomy.
     * @returns {string} the text representation of the taxonomy.
     */
    "getTextValue": function (label) {
        return label;
    },

    /**
     *
     * @param label
     * @return {string}
     */
    "getCSSClass": function (label, element) {
        var labelAsCSSName = label.replace(/[^0-9a-z\-_]/gi, '');

        var cssClass = "ppt-taxo__" + element;

        return cssClass + " " + labelAsCSSName;
    }

};

provider.taxonomy.Provider = provider.taxonomy.DEFAULT_PROVIDER;

/**
 * Define the different type of rendering of a node for a given label.
 * TEXT: default rendering type, the node will be displayed with an ellipse and a text in it.
 * IMAGE: the node is displayed as an image using the image tag in the svg graph.
 * In this case an image path is required.
 * SVG: the node is displayed using a list of svg path, each path can contain its own color.
 */
provider.node.DisplayTypes = Object.freeze({TEXT: 0, IMAGE: 1, SVG: 2, SYMBOL: 3});

/**
 * Get the label provider for the given label.
 * If no provider is defined for the label:
 * First search in parent provider.
 * Then if not found will create one from default provider.
 *
 * @param label to retrieve the corresponding label provider.
 * @returns {object} corresponding label provider.
 */
provider.node.getProvider = function (label) {
    if (label === undefined) {
        logger.error("Node label is undefined, no label provider can be found.");
    } else {
        if (provider.node.Provider.hasOwnProperty(label)) {
            return provider.node.Provider[label];
        } else {
            logger.debug("No direct provider found for label " + label);

            // Search in all children list definitions to find the parent provider.
            for (var p in provider.node.Provider) {
                if (provider.node.Provider.hasOwnProperty(p)) {
                    var nProvider = provider.node.Provider[p];
                    if (nProvider.hasOwnProperty("children")) {
                        if (nProvider["children"].indexOf(label) > -1) {
                            logger.debug("No provider is defined for label (" + label + "), parent (" + p + ") will be used");
                            // A provider containing the required label in its children definition has been found it will be cloned.

                            var newProvider = {"parent": p};
                            for (var pr in nProvider) {
                                if (nProvider.hasOwnProperty(pr) && pr !== "children" && pr !== "parent") {
                                    newProvider[pr] = nProvider[pr];
                                }
                            }

                            provider.node.Provider[label] = newProvider;
                            return provider.node.Provider[label];
                        }
                    }
                }
            }

            logger.debug("No label provider defined for label (" + label + ") default one will be created from provider.node.DEFAULT_PROVIDER");

            provider.node.Provider[label] = {};
            // Clone default provider properties in new provider.
            for (var prop in provider.node.DEFAULT_PROVIDER) {
                if (provider.node.DEFAULT_PROVIDER.hasOwnProperty(prop)) {
                    provider.node.Provider[label][prop] = provider.node.DEFAULT_PROVIDER[prop];
                }
            }
            return provider.node.Provider[label];
        }
    }
};

/**
 * Get the property or function defined in node label provider.
 * If the property is not found search is done in parents.
 * If not found in parent, property defined in provider.node.DEFAULT_PROVIDER is returned.
 * If not found in default provider, defaultValue is set and returned.
 *
 * @param label node label to get the property in its provider.
 * @param name name of the property to retrieve.
 * @returns {*} node property defined in its label provider.
 */
provider.node.getProperty = function (label, name) {
    var nProvider = provider.node.getProvider(label);

    if (!nProvider.hasOwnProperty(name)) {
        var providerIterator = nProvider;

        // Check parents
        var isPropertyFound = false;
        while (providerIterator.hasOwnProperty("parent") && !isPropertyFound) {
            providerIterator = provider.node.getProvider(providerIterator.parent);
            if (providerIterator.hasOwnProperty(name)) {

                // Set attribute in child to optimize next call.
                nProvider[name] = providerIterator[name];
                isPropertyFound = true;
            }
        }

        if (!isPropertyFound) {
            logger.debug("No \"" + name + "\" property found for node label provider (" + label + "), default value will be used");
            if (provider.node.DEFAULT_PROVIDER.hasOwnProperty(name)) {
                nProvider[name] = provider.node.DEFAULT_PROVIDER[name];
            } else {
                logger.debug("No default value for \"" + name + "\" property found for label provider (" + label + ")");
            }
        }
    }
    return nProvider[name];
};

/**
 *
 * @param label
 */
provider.node.getIsAutoLoadValue = function (label) {
    return provider.node.getProperty(label, "isAutoLoadValue");
};

/**
 * Return the "isSearchable" property for the node label provider.
 * Is Searchable defines whether the label can be used as graph query builder root.
 * If true the label can be displayed in the taxonomy filter.
 *
 * @param label
 * @returns boolean
 */
provider.node.getIsSearchable = function (label) {
    return provider.node.getProperty(label, "isSearchable");
};

/**
 * Return the "autoExpandRelations" property for the node label provider.
 * Auto expand relations defines whether the label will automatically add its relations when displayed on graph.
 *
 * @param label
 * @returns boolean
 */
provider.node.getIsAutoExpandRelations = function (label) {
    return provider.node.getProperty(label, "autoExpandRelations");
};

provider.node.getSchema = function (label) {
    return provider.node.getProperty(label, "schema");
};

/**
 * Return the list of attributes defined in node label provider.
 * Parents return attributes are also returned.
 *
 * @param label used to retrieve parent attributes.
 * @returns {Array} list of return attributes for a node.
 */
provider.node.getReturnAttributes = function (label) {
    var nProvider = provider.node.getProvider(label);
    var attributes = {}; // Object is used as a Set to merge possible duplicate in parents

    if (nProvider.hasOwnProperty("returnAttributes")) {
        for (var i = 0; i < nProvider.returnAttributes.length; i++) {
            if (nProvider.returnAttributes[i] === query.NEO4J_INTERNAL_ID) {
                attributes[query.NEO4J_INTERNAL_ID.queryInternalName] = true;
            } else {
                attributes[nProvider.returnAttributes[i]] = true;
            }
        }
    }

    // Add parent attributes
    while (nProvider.hasOwnProperty("parent")) {
        nProvider = provider.node.getProvider(nProvider.parent);
        if (nProvider.hasOwnProperty("returnAttributes")) {
            for (var j = 0; j < nProvider.returnAttributes.length; j++) {
                if (nProvider.returnAttributes[j] === query.NEO4J_INTERNAL_ID) {
                    attributes[query.NEO4J_INTERNAL_ID.queryInternalName] = true;
                } else {
                    attributes[nProvider.returnAttributes[j]] = true;
                }
            }
        }
    }

    // Add default provider attributes if any but not internal id as this id is added only if none has been found.
    if (provider.node.DEFAULT_PROVIDER.hasOwnProperty("returnAttributes")) {
        for (var k = 0; k < provider.node.DEFAULT_PROVIDER.returnAttributes.length; k++) {
            if (provider.node.DEFAULT_PROVIDER.returnAttributes[k] !== query.NEO4J_INTERNAL_ID) {
                attributes[provider.node.DEFAULT_PROVIDER.returnAttributes[k]] = true;
            }
        }
    }

    // Add constraint attribute in the list
    var constraintAttribute = provider.node.getConstraintAttribute(label);
    if (constraintAttribute === query.NEO4J_INTERNAL_ID) {
        attributes[query.NEO4J_INTERNAL_ID.queryInternalName] = true;
    } else {
        attributes[constraintAttribute] = true;
    }


    // Add all in array
    var attrList = [];
    for (var attr in attributes) {
        if (attributes.hasOwnProperty(attr)) {
            if (attr === query.NEO4J_INTERNAL_ID.queryInternalName) {
                attrList.push(query.NEO4J_INTERNAL_ID);
            } else {
                attrList.push(attr);
            }
        }
    }

    // If no attributes have been found internal ID is used
    if (attrList.length <= 0) {
        attrList.push(query.NEO4J_INTERNAL_ID);
    }
    return attrList;
};

/**
 * Return the attribute to use as constraint attribute for a node defined in its label provider.
 *
 * @param label
 * @returns {*}
 */
provider.node.getConstraintAttribute = function (label) {
    return provider.node.getProperty(label, "constraintAttribute");
};

provider.node.getDisplayAttribute = function (label) {
    var displayAttribute = provider.node.getProperty(label, "displayAttribute");

    if (displayAttribute === undefined) {
        var returnAttributes = provider.node.getReturnAttributes(label);
        if (returnAttributes.length > 0) {
            displayAttribute = returnAttributes[0];
        } else {
            displayAttribute = provider.node.getConstraintAttribute(label);
        }
    }

    return displayAttribute
};

/**
 * Return a list of predefined constraint defined in the node label configuration.
 *
 * @param label
 * @returns {*}
 */
provider.node.getPredefinedConstraints = function (label) {
    return provider.node.getProperty(label, "getPredefinedConstraints")();
};

provider.node.filterResultQuery = function (label, initialQuery) {
    return provider.node.getProperty(label, "filterResultQuery")(initialQuery);
};

provider.node.getValueOrderByAttribute = function (label) {
    return provider.node.getProperty(label, "valueOrderByAttribute");
};

provider.node.isValueOrderAscending = function (label) {
    return provider.node.getProperty(label, "isValueOrderAscending");
};

provider.node.getResultOrderByAttribute = function (label) {
    return provider.node.getProperty(label, "resultOrderByAttribute");
};

/**
 *
 * @param label
 */
provider.node.isResultOrderAscending = function (label) {
    return provider.node.getProperty(label, "isResultOrderAscending");
};

/**
 * Return the value of the getTextValue function defined in the label provider corresponding to the parameter node.
 * If no "getTextValue" function is defined in the provider, search is done in parents.
 * If none is found in parent default provider method is used.
 *
 * @param node
 * @param parameter
 */
provider.node.getTextValue = function (node, parameter) {
    return provider.node.getProperty(node.label, "getTextValue")(node, parameter);
};


/**
 * Return the value of the getSemanticValue function defined in the label provider corresponding to the parameter node.
 * The semantic value is a more detailed description of the node used for example in the query viewer.
 * If no "getTextValue" function is defined in the provider, search is done in parents.
 * If none is found in parent default provider method is used.
 *
 * @param node
 * @returns {*}
 */
provider.node.getSemanticValue = function (node) {
    return provider.node.getProperty(node.label, "getSemanticValue")(node);
};

/**
 * Return a list of SVG paths objects, each defined by a "d" property containing the path and "f" property for the color.
 *
 * @param node
 * @returns {*}
 */
provider.node.getSVGPaths = function (node) {
    return provider.node.getProperty(node.label, "getSVGPaths")(node);
};

/**
 * Check in label provider if text must be displayed with images nodes.
 * @param node
 * @returns {*}
 */
provider.node.isTextDisplayed = function (node) {
    return provider.node.getProperty(node.label, "getIsTextDisplayed")(node);
};

/**
 *
 * @param node
 */
provider.node.getSize = function (node) {
    return provider.node.getProperty(node.label, "getSize")(node);
};

/**
 * Return the getColor property.
 *
 * @param node
 * @param style
 * @returns {*}
 */
provider.node.getColor = function (node, style) {
    return provider.node.getProperty(node.label, "getColor")(node, style);
};

/**
 *
 * @param node
 * @param element
 */
provider.node.getCSSClass = function (node, element) {
    return provider.node.getProperty(node.label, "getCSSClass")(node, element);
};

/**
 * Return the getIsGroup property.
 *
 * @param node
 * @returns {*}
 */
provider.node.getIsGroup = function (node) {
    return provider.node.getProperty(node.label, "getIsGroup")(node);
};

/**
 * Return the node display type.
 * can be TEXT, IMAGE, SVG or GROUP.
 *
 * @param node
 * @returns {*}
 */
provider.node.getNodeDisplayType = function (node) {
    return provider.node.getProperty(node.label, "getDisplayType")(node);
};

/**
 * Return the file path of the image defined in the provider.
 *
 * @param node the node to get the image path.
 * @returns {string} the path of the node image.
 */
provider.node.getImagePath = function (node) {
    return provider.node.getProperty(node.label, "getImagePath")(node);
};

/**
 * Return the width size of the node image.
 *
 * @param node the node to get the image width.
 * @returns {int} the image width.
 */
provider.node.getImageWidth = function (node) {
    return provider.node.getProperty(node.label, "getImageWidth")(node);
};

/**
 * Return the height size of the node image.
 *
 * @param node the node to get the image height.
 * @returns {int} the image height.
 */
provider.node.getImageHeight = function (node) {
    return provider.node.getProperty(node.label, "getImageHeight")(node);
};

provider.node.filterNodeValueQuery = function (node, initialQuery) {
    return provider.node.getProperty(node.label, "filterNodeValueQuery")(node, initialQuery);
};

provider.node.filterNodeCountQuery = function (node, initialQuery) {
    return provider.node.getProperty(node.label, "filterNodeCountQuery")(node, initialQuery);
};

provider.node.filterNodeRelationQuery = function (node, initialQuery) {
    return provider.node.getProperty(node.label, "filterNodeRelationQuery")(node, initialQuery);
};

provider.node.getGenerateNodeValueConstraints = function (node) {
    return provider.node.getProperty(node.label, "generateNodeValueConstraints");
};

provider.node.getGenerateNegativeNodeValueConstraints = function (node) {
    return provider.node.getProperty(node.label, "generateNegativeNodeValueConstraints");
};

/**
 * Return the displayResults function defined in label parameter's provider.
 *
 * @param label
 * @returns {*}
 */
provider.node.getDisplayResults = function (label) {
    return provider.node.getProperty(label, "displayResults");
};

/**
 * Label provider used by default if none have been defined for a label.
 * This provider can be changed if needed to customize default behavior.
 * If some properties are not found in user customized providers, default
 * values will be extracted from this provider.
 */
provider.node.DEFAULT_PROVIDER = (
    {
        /**********************************************************************
         * Label specific parameters:
         *
         * These attributes are specific to a node label and will be used for
         * every node having this label.
         **********************************************************************/

        /**
         * Defines whether this label can be used as root element of the graph
         * query builder.
         * This property is also used to determine whether the label can be
         * displayed in the taxonomy filter.
         *
         * The default value is true.
         */
        "isSearchable": true,

        /**
         * Defines whether this label will automatically expend its relations
         * when displayed on graph.
         * If set to true, once displayed additional request will be sent on
         * the database to retrieve its relations.
         *
         * The default value is false.
         */
        "autoExpandRelations": false,

        /**
         * Defines whether this label will automatically load its available
         * data displayed on graph.
         * If set to true, once displayed additional request will be sent on
         * the database to retrieve its possible values.
         *
         * The default value is false.
         */
        "isAutoLoadValue": false,

        /**
         * Defines the list of attribute to return for node of this label.
         * All the attributes listed here will be added in generated cypher
         * queries and available in result list and in node provider's
         * functions.
         *
         * The default value contains only the Neo4j internal id.
         * This id is used by default because it is a convenient way to identify
         * a node when nothing is known about its attributes.
         * But you should really consider using your application attributes
         * instead, it is a bad practice to rely on this attribute.
         */
        "returnAttributes": [query.NEO4J_INTERNAL_ID],

        /**
         * Defines the attribute used to order the value displayed on node.
         *
         * Default value is "count" attribute.
         */
        "valueOrderByAttribute": "count",

        /**
         * Defines whether the value query order by is ascending, if false order
         * by will be descending.
         *
         * Default value is false (descending)
         */
        "isValueOrderAscending": false,

        /**
         * Defines the attributes used to order the results.
         * It can be an attribute name or a list of attribute names.
         *
         * Default value is "null" to disable order by.
         */
        "resultOrderByAttribute": null,

        /**
         * Defines whether the result query order by is ascending, if false
         * order by will be descending.
         * It can be a boolean value or a list of boolean to match the resultOrderByAttribute.
         * If size of isResultOrderAscending < size of resultOrderByAttribute last value is used.
         *
         * Default value is true (ascending)
         */
        "isResultOrderAscending": true,

        /**
         * Defines the attribute of the node to use in query constraint for
         * nodes of this label.
         * This attribute is used in the generated cypher query to build the
         * constraints with selected values.
         *
         * The default value is the Neo4j internal id.
         * This id is used by default because it is a convenient way to
         * identify a node when nothing is known about its attributes.
         * But you should really consider using your application attributes
         * instead, it is a bad practice to rely on this attribute.
         */
        "constraintAttribute": query.NEO4J_INTERNAL_ID,

        /**
         * Defines the attribute of the node to use by default to display the node.
         * This attribute must be present in returnAttributes list.
         *
         * The default value is undefined.
         * If undefined the first attribute of the returnAttributes will be used or
         * constraintAttribute if the list is empty.
         */
        "displayAttribute": undefined,

        /**
         * Return the list of predefined constraints to add for the given label.
         * These constraints will be added in every generated Cypher query.
         *
         * For example if the returned list contain ["$identifier.born > 1976"]
         * for "Person" nodes everywhere in popoto.js the generated Cypher
         * query will add the constraint "WHERE person.born > 1976"
         *
         * @returns {Array}
         */
        "getPredefinedConstraints": function () {
            return [];
        },

        /**
         * Filters the query generated to retrieve the queries.
         *
         * @param initialQuery contains the query as an object structure.
         * @returns {*}
         */
        "filterResultQuery": function (initialQuery) {
            return initialQuery;
        },

        /**********************************************************************
         * Node specific parameters:
         *
         * These attributes are specific to nodes (in graph or query viewer)
         * for a given label.
         * But they can be customized for nodes of the same label.
         * The parameters are defined by a function that will be called with
         * the node as parameter.
         * In this function the node internal attributes can be used to
         * customize the value to return.
         **********************************************************************/

        /**
         * Function returning the display type of a node.
         * This type defines how the node will be drawn in the graph.
         *
         * The result must be one of the following values:
         *
         * provider.node.DisplayTypes.IMAGE
         *  In this case the node will be drawn as an image and "getImagePath"
         *  function is required to return the node image path.
         *
         * provider.node.DisplayTypes.SVG
         *  In this case the node will be drawn as SVG paths and "getSVGPaths"
         *
         * provider.node.DisplayTypes.TEXT
         *  In this case the node will be drawn as a simple circle.
         *
         * Default value is TEXT.
         *
         * @param node the node to extract its type.
         * @returns {number} one value from provider.node.DisplayTypes
         */
        "getDisplayType": function (node) {
            return provider.node.DisplayTypes.TEXT;
        },

        /**
         * Function defining the size of the node in graph.
         *
         * The size define the radius of the circle defining the node.
         * other elements (menu, counts...) will scale on this size.
         *
         * Default value is 50.
         *
         * @param node
         */
        "getSize": function (node) {
            return 50;
        },

        /**
         * Return a color for the node.
         *
         * @param node
         * @returns {*}
         */
        "getColor": function (node) {
            if (node.type === graph.node.NodeTypes.VALUE) {
                return provider.node.getColor(node.parent);
            } else {
                var parentLabel = "";
                if (node.hasOwnProperty("parent")) {
                    parentLabel = node.parent.label
                }

                var incomingRelation = node.parentRel || "";

                var colorId = parentLabel + incomingRelation + node.label;
                return provider.colorScale(colorId);
            }
        },

        /**
         * Generate a CSS class for the node depending on its type.
         *
         * @param node
         * @param element
         * @return {string}
         */
        "getCSSClass": function (node, element) {
            var labelAsCSSName = node.label.replace(/[^0-9a-z\-_]/gi, '');

            var cssClass = "ppt-node__" + element;

            if (node.type === graph.node.NodeTypes.ROOT) {
                cssClass = cssClass + "--root";
            }
            if (node.type === graph.node.NodeTypes.CHOOSE) {
                cssClass = cssClass + "--choose";
            }
            if (node.type === graph.node.NodeTypes.GROUP) {
                cssClass = cssClass + "--group";
            }
            if (node.type === graph.node.NodeTypes.VALUE) {
                cssClass = cssClass + "--value";
            }
            if (node.value !== undefined && node.value.length > 0) {
                cssClass = cssClass + "--value-selected";
            }
            if (node.count === 0) {
                cssClass = cssClass + "--disabled";
            }

            return cssClass + " " + labelAsCSSName;
        },

        /**
         * Function defining whether the node is a group node.
         * In this case no count are displayed and no value can be selected on
         * the node.
         *
         * Default value is false.
         */
        "getIsGroup": function (node) {
            return false;
        },

        /**
         * Function defining whether the node text representation must be
         * displayed on graph.
         * If true the value returned for getTextValue on node will be displayed
         * on graph.
         *
         * This text will be added in addition to the getDisplayType
         * representation.
         * It can be displayed on all type of node display, images, SVG or text.
         *
         * Default value is true
         *
         * @param node the node to display on graph.
         * @returns {boolean} true if text must be displayed on graph for the
         * node.
         */
        "getIsTextDisplayed": function (node) {
            return true;
        },

        /**
         * Function used to return the text representation of a node.
         *
         * The default behavior is to return the label of the node
         * or the value of constraint attribute of the node if it contains
         * value.
         *
         * The returned value is truncated using
         * graph.node.NODE_MAX_CHARS property.
         *
         * @param node the node to represent as text.
         * @param maxLength used to truncate the text.
         * @returns {string} the text representation of the node.
         */
        "getTextValue": function (node, maxLength) {
            var text = "";
            var displayAttr = provider.node.getDisplayAttribute(node.label);
            if (node.type === graph.node.NodeTypes.VALUE) {
                if (displayAttr === query.NEO4J_INTERNAL_ID) {
                    text = "" + node.internalID;
                } else {
                    text = "" + node.attributes[displayAttr];
                }
            } else {
                if (node.value !== undefined && node.value.length > 0) {
                    if (displayAttr === query.NEO4J_INTERNAL_ID) {
                        var separator = "";
                        node.value.forEach(function (value) {
                            text += separator + value.internalID;
                            separator = " or ";
                        });
                    } else {
                        var separator = "";
                        node.value.forEach(function (value) {
                            text += separator + value.attributes[displayAttr];
                            separator = " or ";
                        });
                    }
                } else {
                    text = node.label;
                }
            }

            return text;
        },

        /**
         * Function used to return a descriptive text representation of a link.
         * This representation should be more complete than getTextValue and can
         * contain semantic data.
         * This function is used for example to generate the label in the query
         * viewer.
         *
         * The default behavior is to return the getTextValue not truncated.
         *
         * @param node the node to represent as text.
         * @returns {string} the text semantic representation of the node.
         */
        "getSemanticValue": function (node) {
            var text = "";
            var displayAttr = provider.node.getDisplayAttribute(node.label);
            if (node.type === graph.node.NodeTypes.VALUE) {
                if (displayAttr === query.NEO4J_INTERNAL_ID) {
                    text = "" + node.internalID;
                } else {
                    text = "" + node.attributes[displayAttr];
                }
            } else {
                if (node.value !== undefined && node.value.length > 0) {
                    if (displayAttr === query.NEO4J_INTERNAL_ID) {
                        var separator = "";
                        node.value.forEach(function (value) {
                            text += separator + value.internalID;
                            separator = " or ";
                        });
                    } else {
                        var separator = "";
                        node.value.forEach(function (value) {
                            text += separator + value.attributes[displayAttr];
                            separator = " or ";
                        });
                    }
                } else {
                    text = node.label;
                }
            }
            return text;
        },

        /**
         * Function returning the image file path to use for a node.
         * This function is only used for provider.node.DisplayTypes.IMAGE
         * type nodes.
         *
         * @param node
         * @returns {string}
         */
        "getImagePath": function (node) {
            // if (node.type === graph.node.NodeTypes.VALUE) {
            //     var constraintAttribute = provider.node.getConstraintAttribute(node.label);
            //     return "image/node/value/" + node.label.toLowerCase() + "/" + node.attributes[constraintAttribute] + ".svg";
            // } else {
            return "image/node/" + node.label.toLowerCase() + "/" + node.label.toLowerCase() + ".svg";
            // }
        },

        /**
         * Function returning a array of path objects to display in the node.
         *
         * @param node
         * @returns {*[]}
         */
        "getSVGPaths": function (node) {
            var size = provider.node.getSize(node);
            return [
                {
                    "d": "M 0, 0 m -" + size + ", 0 a " + size + "," + size + " 0 1,0 " + 2 * size + ",0 a " + size + "," + size + " 0 1,0 -" + 2 * size + ",0",
                    "fill": "transparent",
                    "stroke": provider.node.getColor(node),
                    "stroke-width": "2px"
                }
            ];
        },

        /**
         * Function returning the image width of the node.
         * This function is only used for provider.node.DisplayTypes.IMAGE
         * type nodes.
         *
         * @param node
         * @returns {number}
         */
        "getImageWidth": function (node) {
            return provider.node.getSize(node) * 2;
        },

        /**
         * Function returning the image height of the node.
         * This function is only used for provider.node.DisplayTypes.IMAGE
         * type nodes.
         *
         * @param node
         * @returns {number}
         */
        "getImageHeight": function (node) {
            return provider.node.getSize(node) * 2;
        },

        /**
         * Filters the query generated to retrieve the values on a node.
         *
         * @param node
         * @param initialQuery contains the query as an object structure.
         * @returns {*}
         */
        "filterNodeValueQuery": function (node, initialQuery) {
            return initialQuery;
        },
        /**
         * Filters the query generated to retrieve the values on a node.
         *
         * @param node
         * @param initialQuery contains the query as an object structure.
         * @returns {*}
         */
        "filterNodeCountQuery": function (node, initialQuery) {
            return initialQuery;
        },
        /**
         * Filters the query used to retrieve the values on a node.
         *
         * @param node
         * @param initialQuery contains the query as an object structure.
         * @returns {*}
         */
        "filterNodeRelationQuery": function (node, initialQuery) {
            return initialQuery;
        },

        /**
         * Customize, in query, the generated constraint for the node.
         *
         * If undefined use default constraint generation.
         */
        "generateNodeValueConstraints": undefined,

        /**
         * Customize, in query, the generated negative constraint for the node.
         *
         * If undefined use default negative constraint generation.
         */
        "generateNegativeNodeValueConstraints": undefined,

        /**********************************************************************
         * Results specific parameters:
         *
         * These attributes are specific to result display.
         **********************************************************************/

        /**
         * Generate the result entry content using d3.js mechanisms.
         *
         * The parameter of the function is the &lt;p&gt; selected with d3.js
         *
         * The default behavior is to generate a &lt;table&gt; containing all
         * the return attributes in a &lt;th&gt; and their value in a &lt;td&gt;.
         *
         * @param pElmt the &lt;p&gt; element generated in the result list.
         */
        "displayResults": function (pElmt) {
            var result = pElmt.data()[0];
            var returnAttributes = provider.node.getReturnAttributes(result.label);

            returnAttributes.forEach(function (attribute) {
                var div = pElmt.append("div").attr("class", "ppt-result-attribute-div");
                var attributeName = attribute;

                if (query.NEO4J_INTERNAL_ID === attribute) {
                    attributeName = query.NEO4J_INTERNAL_ID.queryInternalName;
                }

                var span = div.append("span");
                span.text(function () {
                    if (attribute === query.NEO4J_INTERNAL_ID) {
                        return "internal ID:"
                    } else {
                        return attribute + ":";
                    }
                });
                if (result.attributes[attributeName] !== undefined) {
                    div.append("span").text(function (result) {
                        return result.attributes[attributeName];
                    });
                }
            });
        }
    });

export default provider