utils/registry.js

/**
 * @module Registry Manager
 */

/**
 * removes a node type from the system
 * @method unregisterNodeType
 * @param {String|Object} type name of the node or the node constructor itself
 */
import defaultConfig from "./defaultConfig";

export function unregisterNodeType(type) {
    const baseClass = type.constructor === String ? defaultConfig.registered_node_types[type] : type;
    if (!baseClass) throw new Error(`node type not found: ${type}`);
    delete defaultConfig.registered_node_types[baseClass.type];
    if (baseClass.constructor.name) delete defaultConfig.Nodes[baseClass.constructor.name];
}

/**
 * Register a node class so it can be listed when the user wants to create a new one
 * @method registerNodeType
 * @param {LGraphNode} type name of the node and path
 * @param {Class} baseClass class containing the structure of a node
 */
export function registerNodeType(type, baseClass) {
    if (!baseClass.prototype) {
        throw new TypeError("Cannot register a simple object, it must be a class with a prototype");
    }
    baseClass.type = type;

    if (defaultConfig.debug) {
        console.log(`Node registered: ${type}`);
    }

    const classname = baseClass.name;

    const pos = type.lastIndexOf("/");
    baseClass.category = type.substr(0, pos);

    if (!baseClass.title) {
        baseClass.title = classname;
    }

    // info.name = name.substr(pos+1,name.length - pos);

    const prev = defaultConfig.registered_node_types[type];
    if (prev) {
        console.log(`replacing node type: ${type}`);
    } else {
        if (!Object.hasOwnProperty.call(baseClass.prototype, "shape")) {
            Object.defineProperty(baseClass.prototype, "shape", {
                set(v) {
                    switch (v) {
                        case "default":
                            delete this._shape;
                            break;
                        case "box":
                            this._shape = defaultConfig.BOX_SHAPE;
                            break;
                        case "round":
                            this._shape = defaultConfig.ROUND_SHAPE;
                            break;
                        case "circle":
                            this._shape = defaultConfig.CIRCLE_SHAPE;
                            break;
                        case "card":
                            this._shape = defaultConfig.CARD_SHAPE;
                            break;
                        default:
                            this._shape = v;
                    }
                },
                get() {
                    return this._shape;
                },
                enumerable: true,
                configurable: true,
            });
        }

        // warnings
        if (baseClass.prototype.onPropertyChange) {
            console.warn(
                `LiteGraph node class ${
                    type
                } has onPropertyChange method, it must be called onPropertyChanged with d at the end`,
            );
        }

        // used to know which nodes create when dragging files to the canvas
        if (baseClass.supported_extensions) {
            for (const ext of baseClass.supported_extensions) {
                if (ext && ext.constructor === String) {
                    defaultConfig.node_types_by_file_extension[ext.toLowerCase()] = baseClass;
                }
            }
        }
    }

    defaultConfig.registered_node_types[type] = baseClass;
    if (baseClass.constructor.name) defaultConfig.Nodes[classname] = baseClass;

    if (defaultConfig.onNodeTypeRegistered) defaultConfig.onNodeTypeRegistered(type, baseClass);
    if (prev && defaultConfig.onNodeTypeReplaced) {
        defaultConfig.onNodeTypeReplaced(type, baseClass, prev);
    }

    // warnings
    if (baseClass.prototype.onPropertyChange) {
        console.warn(
            `LiteGraph node class ${
                type
            } has onPropertyChange method, it must be called onPropertyChanged with d at the end`,
        );
    }

    // used to know which nodes create when dragging files to the canvas
    if (baseClass.supported_extensions) {
        for (const ext of baseClass.supported_extensions) {
            if (ext && ext.constructor === String) {
                defaultConfig.node_types_by_file_extension[ext.toLowerCase()] = baseClass;
            }
        }
    }
}

/**
 * Removes all previously registered node's types
 */
export function clearRegisteredTypes() {
    defaultConfig.registered_node_types = {};
    defaultConfig.node_types_by_file_extension = {};
    defaultConfig.Nodes = {};
    defaultConfig.searchbox_extras = {};
}

/**
 * Returns a registered node type with a given name
 * @method getNodeType
 * @param {String} type full name of the node class. p.e. "math/sin"
 * @return {Class} the node class
 */
export function getNodeType(type) {
    return defaultConfig.registered_node_types[type];
}

/**
 * Returns a list of node types matching one category
 * @method getNodeType
 * @param {String} category category name
 * @return {Array} array with all the node classes
 */
export function getNodeTypesInCategory(category, filter) {
    const r = [];
    // eslint-disable-next-line
    for (const i in defaultConfig.registered_node_types) {
        const type = defaultConfig.registered_node_types[i];
        if (type.filter !== filter) continue;

        if (category === "") {
            if (!type.category) r.push(type);
        } else if (type.category === category) {
            r.push(type);
        }
    }

    return defaultConfig.auto_sort_node_types ? r.sort() : r;
}

/**
 * Register a string in the search box so when the user types it it will recommend this node
 * @method registerSearchboxExtra
 * @param {String} nodeType the node recommended
 * @param {String} description text to show next to it
 * @param {Object} data it could contain info of how the node should be configured
 * @return {Boolean} true if they can be connected
 */
export function registerSearchboxExtra(nodeType, description, data) {
    defaultConfig.searchbox_extras[description.toLowerCase()] = {
        type: nodeType,
        desc: description,
        data,
    };
}
/**
 * Returns a list with all the node type categories
 * @method getNodeTypesCategories
 * @param {String} filter only nodes with ctor.filter equal can be shown
 * @return {Array} array with all the names of the categories
 */
export function getNodeTypesCategories(filter) {
    const categories = { "": 1 };
    // eslint-disable-next-line
    for (const id in defaultConfig.registered_node_types) {
        const type = defaultConfig.registered_node_types[id];
        if (type.category && !type.skip_list) {
            if (type.filter !== filter) continue;
            categories[type.category] = 1;
        }
    }
    const result = [];
    // eslint-disable-next-line
    for (const i in categories) result.push(i);
    return defaultConfig.auto_sort_node_types ? result.sort() : result;
}