154export 
function initializeNodeEditor(materialFilename, geometryId, customRenderer, user_icon_map = 
null, sampleFiles = 
null,
 
  157        "_default_": 
"./Icons/materialx_logo.webp",
 
  158        "_default_graph_": 
"./Icons/nodegraph_white.svg" 
  161    let geometryValues = [
'teapot', 
'shaderball', 
'sphere', 
'plane', 
'cube', 
'cylinder', 
'donut', 
'twist', 
'_loadFromFile_']
 
  165        for (var key in user_icon_map) {
 
  166            my_icon_map[key] = user_icon_map[key];
 
  171    function uriExists(uri) {
 
  175                    return Promise.resolve(
true);
 
  177                    return Promise.resolve(
false);
 
  181                console.log(
'Error checking URI:', error);
 
  182                return Promise.resolve(
false);
 
  187    function renderableItemUpdater(renderableItems) {
 
  188        let renderableItemSelect = document.getElementById(
'renderableItem');
 
  189        if (renderableItemSelect) {
 
  191            const TRUNCATION_LENGTH = 12;
 
  193            while (renderableItemSelect.firstChild) {
 
  194                renderableItemSelect.removeChild(renderableItemSelect.firstChild);
 
  196            for (let i = 0; i < renderableItems.length; i++) {
 
  197                let item = renderableItems[i];
 
  198                let option = document.createElement(
'option');
 
  202                if (uiItem.length > 20)
 
  203                    uiItem = uiItem.substring(0, 20) + 
'...';
 
  204                option.text = uiItem;
 
  205                renderableItemSelect.appendChild(option);
 
  212    function consoleLog(text, severity, clear = 
null) {
 
  213        if (severity === 2) {
 
  214            text = 
'> Error: ' + text
 
  216        else if (severity === 1) {
 
  217            text = 
'> Warning: ' + text
 
  224        let console_area = document.getElementById(
'console_area');
 
  227                console_area.value = text + 
'\n';
 
  230                console_area.value = console_area.value + text + 
'\n';
 
  233            console_area.scrollTop = console_area.scrollHeight;
 
  262    function createMenuItem(text, filename) {
 
  263        let menuItem = document.createElement(
'li');
 
  264        menuItem.className = 
'dropdown-item';
 
  265        menuItem.innerText = text;
 
  266        menuItem.onclick = 
function () {
 
  267            console.log(
'Load library file:', filename);
 
  270            let dropdownMenu = document.getElementById(
'libraryDropdown');
 
  271            dropdownMenu.classList.remove(
'show');
 
  282    function createSubMenu(title, auto_close = 
false) {
 
  284        let li = document.createElement(
'li');
 
  285        li.className = 
"dropend dropdown";
 
  288        let subMenu = document.createElement(
'a');
 
  289        subMenu.className = 
"dropdown-item dropdown-toggle";
 
  290        subMenu.setAttribute(
'data-bs-toggle', 
"dropdown");
 
  292            subMenu.setAttribute(
'data-bs-auto-close', 
'outside');
 
  294        subMenu.setAttribute(
'aria-expanded', 
'false');
 
  295        subMenu.setAttribute(
'aria-haspopup', 
'true');
 
  296        subMenu.innerHTML = title;
 
  297        li.appendChild(subMenu);
 
  308    function createLibraryMenu(sampleFiles, libraryDropdown) {
 
  309        for (let key in sampleFiles) {
 
  311            let li = createSubMenu(key, 
true);
 
  312            libraryDropdown.appendChild(li);
 
  314            let value = sampleFiles[key];
 
  317            if (typeof value === 
'string') {
 
  318                let value = sampleFiles[key];
 
  319                li.appendChild(createMenuItem(key, value));
 
  321            else if (typeof value === 
'object') {
 
  323                let subMenuList = document.createElement(
'ul');
 
  324                subMenuList.className = 
'dropdown-menu';
 
  325                subMenuList.id = key;
 
  330                    if (typeof value[key] === 
'string') {
 
  331                        subMenuList.appendChild(createMenuItem(key, value[key]));
 
  334                    else if (typeof value[key] === 
'object') {
 
  337                        let sli = createSubMenu(key, 
false);
 
  338                        subMenuList.appendChild(sli);
 
  340                        let ssubMenuList = document.createElement(
'ul');
 
  341                        ssubMenuList.className = 
'dropdown-menu';
 
  342                        for (let skey in value[key]) {
 
  343                            ssubMenuList.appendChild(createMenuItem(skey, (value[key])[skey]));
 
  345                        sli.appendChild(ssubMenuList);
 
  349                li.appendChild(subMenuList);
 
  356    if (sampleFiles && libraryDropdown) {
 
  357        createLibraryMenu(sampleFiles, libraryDropdown);
 
  361    let selectGeometryUI = 
false;
 
  362    if (customRenderer) {
 
  364        let geometryURL = geometryId;
 
  365        if (geometryId.length > 0 && geometryValues.includes(geometryId)) {
 
  366            geometryURL = 
'Geometry/' + geometryId + 
'.glb';
 
  367            selectGeometryUI = 
true;
 
  369        var viewer = customRenderer.initialize(materialFilename, geometryURL, readOnly);
 
  370        console.log(
'Setup renderer:', viewer);
 
  373        let preview_panel = document.getElementById(
"preview_panel");
 
  376            preview_panel.style.display = 
'none';
 
  385    function displayNodeTypes(nodeTypes) {
 
  387        var nodeList = document.getElementById(
'nodeTypesList');
 
  393        while (nodeList.firstChild) {
 
  394            nodeList.removeChild(nodeList.firstChild);
 
  398        for (var typeName in nodeTypes) {
 
  400            var rowItem = document.createElement(
"tr");
 
  402            var cellItem = document.createElement(
"td");
 
  403            cellItem.textContent = typeName;
 
  404            rowItem.appendChild(cellItem);
 
  406            cellItem = document.createElement(
"td");
 
  407            var nodeDefString = 
'<None>';
 
  408            var nodeDefName = nodeTypes[typeName].nodedef_name;
 
  409            var nodeDefNode = nodeTypes[typeName].nodedef_node
 
  410            var nodeDefHref = nodeTypes[typeName].nodedef_href;
 
  413                    var link = document.createElement(
"a");
 
  414                    link.target = 
"_blank";
 
  415                    link.href = nodeDefHref;
 
  416                    link.textContent = nodeDefNode + 
" ( " + nodeDefName + 
" )";
 
  417                    cellItem.appendChild(link);
 
  420                    cellItem.textContent = nodeDefName;
 
  424                cellItem.textContent = nodeDefString;
 
  426            rowItem.appendChild(cellItem);
 
  428            nodeList.appendChild(rowItem);
 
  433    var cmeditor = setupXMLSyntax();
 
  434    var cmeditor2 = setupJavascriptSyntax();
 
  435    var cmeditor3 = setupGLTFSyntax();
 
  442    function docDisplayUpdater(contents) {
 
  444            cmeditor.setValue(contents);
 
  452    function gltfDisplayUpdater(contents) {
 
  453        if (!contents || contents.length == 0) {
 
  457            cmeditor3.setValue(contents);
 
  459            console.log(contents);
 
  467    function jsDefinitionsDisplayUpdater(contents) {
 
  469            cmeditor2.setValue(contents);
 
  473    var canvas = document.getElementById(
'mygraphcanvas');
 
  475        consoleLogger: consoleLog,
 
  476        nodeTypesListUpdater: displayNodeTypes,
 
  477        renderableItemUpdater: renderableItemUpdater,
 
  478        documentDisplayUpdater: docDisplayUpdater,
 
  479        gltfDocumentDisplayUpdater: gltfDisplayUpdater,
 
  480        definitionsDisplayUpdater: jsDefinitionsDisplayUpdater,
 
  481        propertypanel_content: document.getElementById(
'propertypanel_content'),
 
  482        propertypanel_icon: document.getElementById(
'propertypanel_icon'),
 
  483        icon_map: my_icon_map,
 
  488    monitor.setRenderer(customRenderer);
 
  489    editor.initialize(canvas, ui, monitor, materialFilename, readOnly);
 
  495    function addUIHandlers() {
 
  497        var saveCanvasButton = document.getElementById(
'captureGraph');
 
  498        if (saveCanvasButton) {
 
  499            saveCanvasButton.addEventListener(
'click', 
function () {
 
  500                var canvas = document.getElementById(
'mygraphcanvas');
 
  501                var dataURL = canvas.toDataURL(
'image/png');
 
  502                var link = document.createElement(
'a');
 
  504                link.download = 
'graph_capture.png';
 
  510        var auto_arrange_size = 80;
 
  513        var loadMaterialXDocumentFromFile = document.getElementById(
'loadMaterialXDocumentFromFile');
 
  514        if (loadMaterialXDocumentFromFile) {
 
  515            loadMaterialXDocumentFromFile.addEventListener(
'click', 
function () {
 
  516                editor.loadGraphFromFile(
'mtlx', auto_arrange_size, 
true);
 
  522        var loadMaterialXDocumentFromZip = document.getElementById(
'loadMaterialXDocumentFromZip');
 
  523        if (loadMaterialXDocumentFromZip) {
 
  524            loadMaterialXDocumentFromZip.addEventListener(
'click', 
function () {
 
  525                editor.loadGraphFromFile(
'zip', auto_arrange_size, 
true);
 
  531        var texAreaNumber = 0;
 
  532        var loadMaterialXDocumentFromText = document.getElementById(
'loadMaterialXDocumentFromText');
 
  533        if (loadMaterialXDocumentFromText) {
 
  534            loadMaterialXDocumentFromText.addEventListener(
'click', 
function () {
 
  535                var mtlxdoc = document.getElementById(
'mtlxdoc').value;
 
  537                if (mtlxdoc.length > 0) {
 
  538                    var name = 
'MaterialXGraph' + texAreaNumber++;
 
  539                    editor.loadGraphFromString(
'mtlx', mtlxdoc, name, auto_arrange_size, 
true);
 
  546        var loadMaterialXDefinitions = document.getElementById(
'loadMaterialXDefinitions');
 
  547        if (loadMaterialXDefinitions) {
 
  548            loadMaterialXDefinitions.addEventListener(
'click', 
function () {
 
  549                editor.loadDefinitionsFromFile(
'mtlx');
 
  554        var clearGraphButton = document.getElementById(
'clearGraph');
 
  555        if (clearGraphButton) {
 
  556            clearGraphButton.addEventListener(
'click', 
function () {
 
  563        var saveMaterialXGraph = document.getElementById(
'saveMaterialXGraph');
 
  564        if (saveMaterialXGraph) {
 
  565            saveMaterialXGraph.addEventListener(
'click', 
function () {
 
  566                var sl = document.getElementById(
'writeCustomLibs').checked;
 
  567                var sp = document.getElementById(
'saveNodePositions').checked;
 
  569                var graphWriteOptions = { writeCustomLibs: sl, saveNodePositions: sp, writeOutputs: wo };
 
  570                editor.saveGraphToFile(
'mtlx', graphWriteOptions);
 
  575        var saveMaterialXGraphText = document.getElementById(
'saveMaterialXGraphText');
 
  576        if (saveMaterialXGraphText) {
 
  577            saveMaterialXGraphText.addEventListener(
'click', 
function () {
 
  583        var searchGraph = document.getElementById(
'searchGraph');
 
  585            searchGraph.addEventListener(
'click', 
function () {
 
  586                var search = document.getElementById(
'searchGraphText').value;
 
  587                editor.searchGraph(search);
 
  592        var openSubgraph = document.getElementById(
'openSubgraph');
 
  594            openSubgraph.addEventListener(
'click', 
function () {
 
  595                editor.openSubgraph();
 
  600        var closeSubgraph = document.getElementById(
'closeSubgraph');
 
  602            closeSubgraph.addEventListener(
'click', 
function () {
 
  603                editor.closeSubgraph();
 
  608        var resetView = document.getElementById(
'resetView');
 
  610            resetView.addEventListener(
'click', 
function () {
 
  616        var arrangeGraphButton = document.getElementById(
'arrangeGraph');
 
  617        if (arrangeGraphButton) {
 
  618            arrangeGraphButton.addEventListener(
'click', 
function () {
 
  619                editor.arrangeGraph();
 
  624        var centerNodeButton = document.getElementById(
'centerNode');
 
  625        if (centerNodeButton) {
 
  626            centerNodeButton.addEventListener(
'click', 
function () {
 
  632        var collapseNodesButton = document.getElementById(
'collapseNodes');
 
  633        if (collapseNodesButton) {
 
  634            collapseNodesButton.addEventListener(
'click', 
function () {
 
  635                editor.collapseExpandNodes(
true);
 
  638        var expandNodesButton = document.getElementById(
'expandNodes');
 
  639        if (expandNodesButton) {
 
  640            expandNodesButton.addEventListener(
'click', 
function () {
 
  641                editor.collapseExpandNodes(
false);
 
  646        var selectNodesButton = document.getElementById(
'selectNodes');
 
  647        if (selectNodesButton) {
 
  648            selectNodesButton.addEventListener(
'click', 
function () {
 
  649                editor.selectNodes();                
 
  654        var copySelectedButton = document.getElementById(
'copySelected');
 
  655        if (copySelectedButton) {
 
  656            copySelectedButton.addEventListener(
'click', 
function () {
 
  657                editor.copyToClipboard();
 
  662        var pasteSelectedButton = document.getElementById(
'pasteSelected');
 
  663        if (pasteSelectedButton) {
 
  664            pasteSelectedButton.addEventListener(
'click', 
function () {
 
  665                editor.pasteFromClipboard();
 
  670        var createNodeGraphButton = document.getElementById(
'createNodeGraph');
 
  671        if (createNodeGraphButton) {
 
  672            createNodeGraphButton.addEventListener(
'click', 
function () {
 
  673                editor.createNodeGraph();
 
  678        var extractNodeGraphButton = document.getElementById(
'extractNodeGraph');
 
  679        if (extractNodeGraphButton) {
 
  680            extractNodeGraphButton.addEventListener(
'click', 
function () {
 
  681                editor.extractNodeGraph();
 
  699        var xmlToGraph = document.getElementById(
'xmltograph');
 
  701            xmlToGraph.addEventListener(
'click', 
function () {
 
  702                var name = 
'MaterialXGraph' + texAreaNumber++;
 
  703                var mtlxdoc = document.getElementById(
'mtlxdoc').value;
 
  704                editor.loadGraphFromString(
'mtlx', mtlxdoc, 
'MaterialXGraph', auto_arrange_size, 
true);
 
  709        function updateRenderableItemUI() {
 
  710            let renderableItems = editor.findRenderableItems();
 
  711            renderableItemUpdater(renderableItems);
 
  714        function saveToStringUI() {
 
  715            var cl = document.getElementById(
'writeCustomLibs').checked;
 
  716            var sp = document.getElementById(
'saveNodePositions').checked;
 
  718            var graphWriteOptions = { writeCustomLibs: cl, saveNodePositions: sp, writeOutputs: wo };
 
  719            console.log(
'Save with options: ', graphWriteOptions);
 
  720            var result = editor.saveGraphToString(
'mtlx', graphWriteOptions);
 
  722            cmeditor.setValue(result[0]);
 
  724            if (customRenderer) {
 
  725                customRenderer.setSourceColorSpace(editor.getSourceColorSpace());
 
  726                customRenderer.setTargetDistanceUnit(editor.getTargetDistanceUnit());
 
  727                customRenderer.updateMaterialFromText(result[0]);
 
  728                updateRenderableItemUI();
 
  733        var graphtoxml = document.getElementById(
'graphtoxml');
 
  735            graphtoxml.addEventListener(
'click', 
function () {
 
  740        let graphtoxml2 = document.getElementById(
'graphtoxml2');
 
  742            graphtoxml2.addEventListener(
'click', 
function () {
 
  744                graphtoxml2.classList.remove(
'btn-outline-warning');
 
  745                graphtoxml2.classList.add(
'btn-outline-secondary');
 
  750        var graphtogltf = document.getElementById(
'graphtogltf');
 
  752            graphtogltf.addEventListener(
'click', 
function () {
 
  753                var graphWriteOptions = { writeCustomLibs: 
false, saveNodePositions: 
false, writeOutputs: 
true };
 
  754                var result = editor.saveGraphToString(
'gltf', graphWriteOptions);
 
  755                gltfDisplayUpdater(result[0]);
 
  757                    consoleLog(result[1], 1, 
false);
 
  763        var gltftograph = document.getElementById(
'gltftograph');
 
  765            gltftograph.addEventListener(
'click', 
function () {
 
  766                var gltfdoc = document.getElementById(
'gltfgraph').value;
 
  767                if (gltfdoc.length > 0) {
 
  768                    editor.loadGraphFromString(
'gltf', gltfdoc, 
'GLTFGraph', auto_arrange_size, 
true);
 
  775        let turntableEnabledUI = document.getElementById(
'turntableEnabled');
 
  776        if (turntableEnabledUI) {
 
  777            turntableEnabledUI.addEventListener(
'click', (e) => {
 
  779                turntableEnabledUI.classList.toggle(
'btn-secondary');
 
  781                    customRenderer.toggleTurntable();
 
  786        let disableRenderingUI = document.getElementById(
'disableRendering');
 
  787        if (disableRenderingUI) {
 
  788            disableRenderingUI.addEventListener(
'click', (e) => {
 
  790                disableRenderingUI.classList.toggle(
'btn-danger');
 
  792                    customRenderer.toggleRendering();
 
  797        let toggleBackgroundTextureUI = document.getElementById(
'toggleBackgroundTexture');
 
  798        if (toggleBackgroundTextureUI) {
 
  799            toggleBackgroundTextureUI.addEventListener(
'click', (e) => {
 
  800                toggleBackgroundTextureUI.classList.toggle(
'btn-primary');
 
  802                    customRenderer.toggleBackgroundTexture();
 
  807        let resetCameraUI = document.getElementById(
'resetCamera');
 
  809            resetCameraUI.addEventListener(
'click', (e) => {
 
  810                if (customRenderer) {
 
  811                    customRenderer.resetCamera();
 
  817        function loadFromMenu(e) {
 
  818            var uiItem = e.target.value;
 
  819            if (uiItem == 
'_loadFromFile_') {
 
  821                var fileInput = document.createElement(
'input');
 
  822                fileInput.type = 
'file';
 
  823                fileInput.accept = 
'.glb';
 
  825                fileInput.onchange = 
function(event) {
 
  826                    var file = 
event.target.files[0];
 
  828                        var fileURL = URL.createObjectURL(file);
 
  830                            customRenderer.setRenderGeometry(fileURL);
 
  831                        console.log(
'Change geometry to:', fileURL, 
'from file:', file.name);
 
  838                var geometryURL = uiItem.toLowerCase().replace(/\s/g, 
'');
 
  839                var geometryPath = 
'Geometry/' + geometryURL + 
'.glb';
 
  840                console.log(
'Change geometry to:', geometryPath);
 
  842                    customRenderer.setRenderGeometry(geometryPath);
 
  847        let geometryItemSelect = document.getElementById(
'loadGeometry');
 
  848        if (geometryItemSelect) {
 
  851            var geometryItems = [
'Teapot', 
'Shader Ball', 
'Sphere', 
'Plane', 
'Cube', 
'Cylinder', 
'Donut', 
'Twist', 
'Custom...'];
 
  852            for (var i = 0; i < geometryItems.length; i++) {
 
  853                var option = document.createElement(
'option');
 
  854                option.value = geometryValues[i];
 
  855                option.text = geometryItems[i];
 
  856                geometryItemSelect.appendChild(option);
 
  860            geometryItemSelect.addEventListener(
'change', (e) => {
 
  862                if (e.target.value == 
'_loadFromFile_')
 
  863                    e.target.value = 
'Custom Geometry' 
  867            if (selectGeometryUI) {
 
  869                geometryItemSelect.value = geometryId;
 
  874        let renderableItemSelect = document.getElementById(
'renderableItem');
 
  875        if (renderableItemSelect) {
 
  876            renderableItemSelect.addEventListener(
'change', (e) => {
 
  877                let index = e.target.value;
 
  880                    customRenderer.setRenderMaterial(index);
 
  881                    editor.searchGraph(index);
 
  887        var canvas = document.getElementById(
'mygraphcanvas');
 
  888        var canvasContainer = document.getElementById(
'canvasContainer');
 
  889        var colContainer = document.getElementById(
'colContainer');
 
  892        var observer = 
new ResizeObserver(
function (entries) {
 
  898                var parent = canvas.parentNode;
 
  899                let newWidth = parent.offsetWidth;
 
  900                let newHeight = parent.offsetHeight;
 
  907                console.log(
'Resize node graph canvas to:', newWidth, newHeight);
 
  908                editor.setDirty(newWidth, newHeight);
 
  914        observer.observe(canvasContainer);
 
  918    function setupGLTFSyntax() {
 
  921        const gltfTextArea = document.getElementById(
'gltfgraph');
 
  923            cmeditor = CodeMirror.fromTextArea(gltfTextArea, {
 
  924                mode: 
'application/json',
 
  931            const initialGLTF = 
'';
 
  932            gltfTextArea.value = initialGLTF;
 
  933            cmeditor.setValue(initialGLTF);
 
  936            cmeditor.on(
'change', (e) => {
 
  937                gltfTextArea.value = cmeditor.getValue();
 
  940            var pasteButton = document.getElementById(
'gltfgraph_paste');
 
  942                addPasteHandler(pasteButton, cmeditor);
 
  948    function setupJavascriptSyntax() {
 
  950        const elem = document.getElementById(
'mtlxlib');
 
  954        let cmeditor = CodeMirror.fromTextArea(elem, {
 
  955            mode: 
'application/javascript',
 
  963        cmeditor.setValue(
'');
 
  966        cmeditor.on(
'change', () => {
 
  967            elem.value = cmeditor.getValue();
 
  974    function setupXMLSyntax() {
 
  976        const materialXTextArea = document.getElementById(
'mtlxdoc');
 
  977        let cmeditor = CodeMirror.fromTextArea(materialXTextArea, {
 
  978            mode: 
'application/xml',
 
  985        const initialXML = 
'';
 
  986        materialXTextArea.value = initialXML;
 
  987        cmeditor.setValue(initialXML);
 
  990        cmeditor.on(
'change', (e) => {
 
  991            materialXTextArea.value = cmeditor.getValue();
 
  994        var pasteButton = document.getElementById(
'mtlxdoc_paste');
 
  996            addPasteHandler(pasteButton, cmeditor);