# Using Code Snippets to Instantly Load Offline Component Definitions

This document is intended to show you, how to improve testing of content in development and testing phases by means of doing what is described in the Hello world example. You must have VS Code with the Live Server extension (see Environment Setup), or another local HTTP server running on your computer. We will show this on the example of the Google Chrome browser.

# Using the Content Test Site

  1. Open the Configurator Test Site (opens new window) at https://www.roomle.com/t/configurator-testing/

  2. Open the developer console using Cmd+Shift+J (or Ctrl+Shift+I) and navigate to the Sources bar, Snippets, click the new snippet button and choose a name for it (for example: loadersnippet)

  3. Paste the following code

/* https://www.roomle.com/t/configurator-testing/ */
(async function () {
    const iframe = document.querySelector('iframe');
    let iframeContentWindow = null;
    if (!iframe) {
        await RoomleConfigurator.init('demoConfigurator', 'roomle-configurator', 'usm:frame', {
            __overrideServerUrl: 'https://www.roomle.com/t/configurator/'
        });
        iframeContentWindow = document.querySelector('iframe').contentWindow;
    } else {
        iframeContentWindow = document.querySelector('iframe').contentWindow;
        iframeContentWindow.RoomleConfigurator._kernelAccess._kernelInstance.clearAll();
    }
    // fill in the URLs you want to load
    const prefix = 'http://127.0.0.1:5500/'
    const urls = [
    ];
    const responses = await Promise.all(urls.map((url) => fetch(prefix + url)))
    const components = await Promise.all(responses.map((response) => response.text()));
    let lastComponentId = '';
    components.forEach((component) => {
        const cleanComponent = component.replace(/\n/g, '');
        iframeContentWindow.RoomleConfigurator._kernelAccess._kernelInstance.loadComponentDefinition(10, cleanComponent);
        lastComponentId = JSON.parse(cleanComponent).id;
        console.log(`loaded ${lastComponentId} into kernel`);
    });

   RoomleConfigurator.loadConfiguration(`{"componentId":"${lastComponentId}"}`);

}());
  1. Go to VS Code, open your working folder and start the Live Server extension (button "Go Live" at the right side of the bottom bar or the "Live Server: Open with Live Server")

  2. Navigate to a component definition JSON, open its context menu using a right click on the file and choose Copy relative path.

  3. Paste this as an entry into the urls array. Example:

    const urls = [
        'componentdefinitions/examples/40_helloworld.json'
    ];

Hint 1: You can load more components at once. Put the component you want to load in the configurator as the root of the configuration to the last position (see what the lastComponentId variable in the snippet code is for)
Hint 2: You can load any configuration. Just replace the argument in the last line.
Hint 3: You can set the path of the folder containing component definitions to the prefix variable, letting you use only the filenames in the urls array. See the same code as above modified like this:

    const prefix = 'http://127.0.0.1:5500/componentdefinitions/examples/'
    const urls = [
        '40_helloworld.json'
    ];
  1. In the lower right area, run the script.

  2. The configurator loads the content from your local HTTP server, letting you safely and quickly test the content.

# Using the Configurator Directly

If you want, you can run content in the configurator directly. This is especially useful if you want to check a stored configuration with new content.

  1. Open the configurator using the default settings Rubens Configurator (opens new window) or a share link with configuration hash, like https://www.roomle.com/t/cp/?id=usm:frame:C6A1C58D311C7660962FA07610B1FD076A26F4C860CFF1A84D39626A40CCA8C4

  2. Go on like in previous example. Only use the following form of the snippet:

/* https://www.roomle.com/t/cp/ */

(async function () {

    const prefix = 'http://127.0.0.1:5500/';
    const urls = [
    ];
    const responses = await Promise.all(urls.map((url) => fetch(prefix + url)))
    const components = await Promise.all(responses.map((response) => response.text()));
    let lastComponentId = '';
    components.forEach((component) => {
        let jsonComponent = JSON.parse(component.replace(/\r?\n|\r/g, ''));
        lastComponentId = jsonComponent.id;
        RoomleConfigurator._kernelAccess._kernelInstance.loadComponentDefinition(10, JSON.stringify(jsonComponent));
        console.log(`loaded ${lastComponentId} into kernel`);
    });
    RoomleConfigurator.loadConfiguration(`{"componentId":"${lastComponentId}"}`);

}());

# Advanced Loader Snippet

Because the loader snippet parses the configuration JSON to a JavaScript object, you can easily modify it and make it help you when developing the content via injecting debug geometry, disabling parameter groups etc. See following snippet intended to be run in Rubens Configurator (opens new window).

Please use this carefully. If you develop, watch out that you do not influence how the live content works, for example by accidentally overriding a variable value.

/* https://www.roomle.com/t/cp/ */
var doNotFreshload;
/* 
Uncomment following to pre-init doNotFreshload variable to make the current configuration reload with updated components, without loading the configuration with the last component only.
*/
//doNotFreshload = true;
/* Uncomment following to never reload current configuration */ 
//doNotFreshload = false;

(async function () {

    /* this is uglified version of the "Coordinate System Axes" code snipet */
    const debugAdditionGeometry = "coordSystemAxesLength = 1000;coordSystemAxesThickness = 10;BeginObjGroup();AddPlainCube(Vector3f{coordSystemAxesThickness, coordSystemAxesThickness, coordSystemAxesLength}); SetObjSurface('demoCatalogId:test_crazy_gree');AddPlainCube(Vector3f{coordSystemAxesThickness, coordSystemAxesLength, coordSystemAxesThickness}); SetObjSurface('demoCatalogId:cyan');AddPlainCube(Vector3f{coordSystemAxesLength, coordSystemAxesThickness, coordSystemAxesThickness}); SetObjSurface('demoCatalogId:red');EndObjGroup();";

    const prefix = 'http://127.0.0.1:5500/<REPOSITORY FOLDER>/<CATALOGUE>/components';
    const urls = [
        'component1.json'
        , 'component2.json'
        // Because we added /**/ comment at the end of this, and we put commas at the beginning of the lines, you can easily comment out components without deleting them from the snippet. You will then load component2.json if you comment it out like this. Watch out for dependencies. Therefore, keep the components in this snippet sorted in a way that component on the next line has all dependencies already loaded!
        /*
        , 'component3.json'
        , 'root_component.json'
        /**/
    ];
    const responses = await Promise.all(urls.map((url) => fetch(prefix + url)))
    const components = await Promise.all(responses.map((response) => response.text()));
    let lastComponentId = '';
    components.forEach((component) => {
        // Parse the JSON to object. You can do whatever you want with it in this snippet later. The Regex escapes newlines in the component, making JSON with multiple lines valid. Note: Windows use /r/n as line ends. This is also handled.
        let jsonComponent = JSON.parse(component.replace(/\r?\n|\r/g, ''));
        // Store the last componentId to load it at the end in doNotFreshload.
        lastComponentId = jsonComponent.id;
        // Filter out only the master component.
        if (lastComponentId.includes("_master")) {
            // Inject content of debugAdditionGeometry to beginninf ot the geometry script.
            jsonComponent.geometry = debugAdditionGeometry.concat(jsonComponent.geometry);
        }
        // Undefined by the API, but you can leave debugGeometry in the script. This injects what you have in the debugGeometry at the end of the actual geometry.
        if (jsonComponent.debugGeometry) {
            jsonComponent.geometry = jsonComponent.geometry.concat(jsonComponent.debugGeometry);
        }
        // For IDM-imported components: Place the idmFeature key number at the beginning of the label in every language. Makes working with IDM components easier.
        if (jsonComponent.parameters) {
            jsonComponent.parameters.forEach(p => {
                if (p.key) {
                    if (p.key.includes("idmFeature")) {
                        var x = p.key.substring(10, p.key.length);
                        for (var k in p.labels) {
                            p.labels[k] = x + " : " + p.labels[k];
                        }
                        // Show value object values at beginning of every label
                        if (p.valueObjects) {
                            p.valueObjects.forEach(vo => {
                                if (vo.value && vo.labels) {
                                    var val = vo.value;
                                    for (var k in vo.labels) {
                                        vo.labels[k] = val + " : " + vo.labels[k];
                                    }
                                }
                            });
                        }
                    }
                }
            });
        }
        // Deactivate parameter groups. Also delete possible children's group, otherwise they do not show.
        jsonComponent.parameterGroups = null;
        if (jsonComponent.possibleChildren) {
            jsonComponent.possibleChildren.forEach(pc => {
                pc.group = null;
            });
        }
        // Stringify the object and feed it to the configurator.
        RoomleConfigurator._kernelAccess._kernelInstance.loadComponentDefinition(10, JSON.stringify(jsonComponent));
        // If you see this log, the current component passed. If you read componentId component1.json in the console, error is in component2.json, which did not reach to here.
        console.log(`loaded ${lastComponentId} into kernel`);
    });

    // if false or uninitialized
    if (doNotFreshload) {
        var previous_configuration = await RoomleConfigurator.getCurrentConfiguration();
        RoomleConfigurator.loadConfiguration(previous_configuration);
        console.log(`reloading previous config`);
    } else {
        // if doNotFreshload is not yet inited, load the component with the lastComponentId.
        RoomleConfigurator.loadConfiguration(`{"componentId":"${lastComponentId}"}`);
        // initialize in order to reload previous config next time
        doNotFreshload = true;
        console.log(`freshloaded: {"componentId":"${lastComponentId}"}`);
    }
}());