mirror of
				https://github.com/node-red/node-red.git
				synced 2025-03-01 10:36:34 +00:00 
			
		
		
		
	bug fix orphaned models and graphical tray glitch
- ensure models are disposed correctly - clean up any orphaned models left after editor:close - improve concurrent multiple instances javascript models - fix graphical glitch
This commit is contained in:
		| @@ -99,6 +99,20 @@ RED.editor.codeEditor.monaco = (function() { | ||||
|  | ||||
|     const modulesCache = {}; | ||||
|      | ||||
|     RED.events.on("editor:close",function() {  | ||||
|         //catch all - when editor is closed, ensure lod models that are not explicitly destroyed by a call to .destroy() are dumped | ||||
|         let models = monaco.editor.getModels(); | ||||
|         if(models && models.length) { | ||||
|             console.warn("Cleaning up monaco models left behind. Any node that calls createEditor() should call .destroy().") | ||||
|             for (let index = 0; index < models.length; index++) { | ||||
|                 const model = models[index]; | ||||
|                 if(!model.isDisposed()) { | ||||
|                     model.dispose(); | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|     }); | ||||
|  | ||||
|     /** | ||||
|      * Helper function to load/reload types. | ||||
|      * @param {string} mod - type lib to load. Only known libs are currently supported. | ||||
| @@ -681,7 +695,7 @@ RED.editor.codeEditor.monaco = (function() { | ||||
|  | ||||
|         var editorSettings = RED.editor.codeEditor.settings || {}; | ||||
|         var loadedLibs = {JS:{}, TS:{}};//for tracking and later disposing of loaded type libs | ||||
|         var watchTimer, elVisible, elVisibleMem; | ||||
|         var watchTimer; | ||||
|         var createThemeMenuOption = function (theme, keybinding) { | ||||
|             return { | ||||
|                 // An unique identifier of the contributed action. | ||||
| @@ -708,7 +722,7 @@ RED.editor.codeEditor.monaco = (function() { | ||||
|             if (mode) { | ||||
|                 mode = mode.replace("ace/mode/", ""); | ||||
|             } else { | ||||
|                 mode = "javascript"; | ||||
|                 mode = "text"; | ||||
|             } | ||||
|             switch (mode) { | ||||
|                 case "nrjavascript": | ||||
| @@ -764,6 +778,7 @@ RED.editor.codeEditor.monaco = (function() { | ||||
|         if (editorOptions.lineNumbers === false) { editorOptions.lineNumbers = false; } | ||||
|         if (editorOptions.theme == null) { editorOptions.theme = monacoThemes[0]; } | ||||
|         if (editorOptions.mode == null) { editorOptions.mode = convertAceModeToMonacoLang(options.mode); } | ||||
|         if (editorOptions.automaticLayout == null) { editorOptions.automaticLayout = true; } | ||||
|  | ||||
|         if (options.foldStyle) { | ||||
|             switch (options.foldStyle) { | ||||
| @@ -917,13 +932,8 @@ RED.editor.codeEditor.monaco = (function() { | ||||
|         ed.session = ed; | ||||
|         ed.renderer = {}; | ||||
|  | ||||
|         ed.setMode = function(mode, cb) { | ||||
|             if (mode && typeof mode === "object") { | ||||
|                 var options = mode; | ||||
|                 mode = options.path; | ||||
|             } else { | ||||
|                 mode = mode || "text"; | ||||
|             } | ||||
|         ed.setMode = function(mode, cb, resize) { | ||||
|             if(resize==null) { resize = true; } | ||||
|             mode = convertAceModeToMonacoLang(mode); | ||||
|             var oldModel = ed.getModel(); | ||||
|             var oldValue = ed.getValue(); | ||||
| @@ -935,13 +945,14 @@ RED.editor.codeEditor.monaco = (function() { | ||||
|                 var oldSelections = ed.getSelections(); | ||||
|                 var oldPosition = ed.getPosition(); | ||||
|                 oldValue = oldModel.getValue() || ""; | ||||
|                 if(!oldModel.isDisposed()) { oldModel.dispose(); } | ||||
|                 ed.setModel(null); | ||||
|                 newModel = monaco.editor.createModel((oldValue || ""), mode); | ||||
|                 ed.setModel(newModel); | ||||
|                 oldModel.dispose(); | ||||
|                 ed.setScrollTop(oldScrollTop, 1/* immediate */); | ||||
|                 ed.setScrollLeft(oldScrollLeft, 1/* immediate */); | ||||
|                 ed.setPosition(oldPosition); | ||||
|                 ed.setSelections(oldSelections);                 | ||||
|                 ed.setSelections(oldSelections); | ||||
|             } else { | ||||
|                 newModel = monaco.editor.createModel((oldValue || ""), mode); | ||||
|                 ed.setModel(newModel); | ||||
| @@ -949,7 +960,9 @@ RED.editor.codeEditor.monaco = (function() { | ||||
|             if (cb && typeof cb == "function") { | ||||
|                 cb(); | ||||
|             } | ||||
|             this.resize();//cause a call to layout() | ||||
|             if(resize) {  | ||||
|                 this.resize(); //cause a call to layout() | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         ed.getRange = function getRange(){ | ||||
| @@ -1032,7 +1045,9 @@ RED.editor.codeEditor.monaco = (function() { | ||||
|             } catch (error) { } | ||||
|             try { | ||||
|                 var m = this.getModel(); | ||||
|                 m.dispose(); | ||||
|                 if(m && !m.isDisposed()) { | ||||
|                     m.dispose(); | ||||
|                 } | ||||
|                 this.setModel(null); | ||||
|             } catch (e) { } | ||||
|  | ||||
| @@ -1182,41 +1197,19 @@ RED.editor.codeEditor.monaco = (function() { | ||||
|  | ||||
|         //as models are signleton, consts and let are avialable to other javascript instances  | ||||
|         //so when not focused, set editor mode to text temporarily to avoid multiple defs | ||||
|          | ||||
|          | ||||
|         ed.onDidBlurEditorWidget(function() { | ||||
|             if(isVisible(el) == false) { | ||||
|                 if(ed._mode == "javascript") { | ||||
|                     ed.setMode('text'); | ||||
|                     ed._tempMode = "text";  | ||||
|                 }                 | ||||
|                 onVisibilityChange(false); | ||||
|             } | ||||
|         }); | ||||
|  | ||||
|         ed.onDidFocusEditorWidget(function() { | ||||
|             if(ed._mode == "javascript" && ed._tempMode == "text") { | ||||
|                 ed._tempMode = "";  | ||||
|                 setTimeout(function() { | ||||
|                     ed.setMode('javascript'); | ||||
|                 }, 5); | ||||
|             } | ||||
|             onVisibilityChange(true, 10); | ||||
|         }); | ||||
|  | ||||
|         watchVisibility(el, function(visible, element) { | ||||
|             if(visible) { | ||||
|                 if(ed._mode == "javascript" && ed._tempMode == "text") { | ||||
|                     ed._tempMode = "";  | ||||
|                     setTimeout(function() { | ||||
|                         ed.setMode('javascript'); | ||||
|                     }, 5); | ||||
|                 } | ||||
|             } else { | ||||
|                 if(ed._mode == "javascript") { | ||||
|                     ed.setMode('text'); | ||||
|                     ed._tempMode = "text";  | ||||
|                 }       | ||||
|             } | ||||
|         }); | ||||
|          | ||||
|         function watchVisibility(element, callback) { | ||||
|         function visibilityWatcher(element, callback) { | ||||
|             try { | ||||
|                 var options = { | ||||
|                     root: $(element).closest("div.red-ui-tray-content")[0] || document, | ||||
| @@ -1225,32 +1218,51 @@ RED.editor.codeEditor.monaco = (function() { | ||||
|                 }; | ||||
|                 var observer = new IntersectionObserver(function(entries, observer) { | ||||
|                     entries.forEach(function(entry) { | ||||
|                         callback(entry.intersectionRatio > 0, entry); | ||||
|                         callback(entry.intersectionRatio > 0, 5, entry); | ||||
|                     }); | ||||
|                 }, options); | ||||
|                 observer.observe(element);  | ||||
|             } catch (e1) { | ||||
|                 //browser not supporting IntersectionObserver? | ||||
|                 //fall back to polling | ||||
|                 //browser not supporting IntersectionObserver? then fall back to polling! | ||||
|                 try { | ||||
|                     let elVisibleMem = isVisible(el) | ||||
|                     watchTimer = setInterval(function() { | ||||
|                         elVisible = isVisible(el); | ||||
|                         let elVisible = isVisible(el); | ||||
|                         if(elVisible != elVisibleMem) { | ||||
|                             callback(elVisible, element); | ||||
|                             callback(elVisible, 100, element); | ||||
|                         } | ||||
|                         elVisible = elVisibleMem; | ||||
|                     }, 200); | ||||
|                         elVisibleMem = elVisible; | ||||
|                     }, 100); | ||||
|                 } catch (e2) { } | ||||
|             } | ||||
|              | ||||
|         } | ||||
|  | ||||
|         function onVisibilityChange(visible, delay, element) { | ||||
|             if(visible) { | ||||
|                 if(ed._mode == "javascript" && ed._tempMode == "text") { | ||||
|                     ed._tempMode = "";  | ||||
|                     setTimeout(function() { | ||||
|                         if(el.parentElement) { //ensure el is still in DOM | ||||
|                             ed.setMode('javascript', undefined, false); | ||||
|                         } | ||||
|                     }, delay); | ||||
|                 } | ||||
|             } else if(ed._mode == "javascript") { | ||||
|                 if(el.parentElement) { //ensure el is still in DOM | ||||
|                     ed.setMode('text', undefined, false); | ||||
|                     ed._tempMode = "text";  | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         visibilityWatcher(el, onVisibilityChange); | ||||
|  | ||||
|         //by default, set javascript editors to text mode. | ||||
|         //when elemt becomes visible, it will be (re) set to javascript mode | ||||
|         //this is to ensure multiple editors sharing the model dont presnet its  | ||||
|         //consts & lets to each other | ||||
|         if(ed._mode == "javascript") { | ||||
|             ed.setMode('text'); | ||||
|             ed.setMode('text', undefined, false); | ||||
|             ed._tempMode = "text";  | ||||
|         } | ||||
|  | ||||
|   | ||||
		Reference in New Issue
	
	Block a user