Fixed #278: Implemented method `editor.destroy()` to properly cleanup the editor
This commit is contained in:
parent
61c6f21f90
commit
a78b2ae57a
|
@ -5,6 +5,7 @@ https://github.com/josdejong/jsoneditor
|
|||
|
||||
## not yet released, version 5.2.0
|
||||
|
||||
- Implemented method `editor.destroy()` to properly cleanup the editor (#278).
|
||||
- Fixed #268: JSONEditor now trims text in fields and values.
|
||||
|
||||
|
||||
|
|
|
@ -111,6 +111,10 @@ Constructs a new JSONEditor.
|
|||
|
||||
Collapse all fields. Only applicable for mode 'tree', 'view', and 'form'.
|
||||
|
||||
#### `JSONEditor.destroy()`
|
||||
|
||||
Destroy the editor. Clean up DOM, event listeners, and web workers.
|
||||
|
||||
#### `JSONEditor.expandAll()`
|
||||
|
||||
Expand all fields. Only applicable for mode 'tree', 'view', and 'form'.
|
||||
|
|
|
@ -19,7 +19,7 @@
|
|||
"bugs": "https://github.com/josdejong/jsoneditor/issues",
|
||||
"scripts": {
|
||||
"build": "gulp",
|
||||
"watch": "watch",
|
||||
"watch": "gulp watch",
|
||||
"test": "mocha test"
|
||||
},
|
||||
"dependencies": {
|
||||
|
|
|
@ -7,6 +7,9 @@ var util = require('./util');
|
|||
*/
|
||||
function History (editor) {
|
||||
this.editor = editor;
|
||||
this.history = [];
|
||||
this.index = -1;
|
||||
|
||||
this.clear();
|
||||
|
||||
// map with all supported actions
|
||||
|
@ -249,4 +252,14 @@ History.prototype.redo = function () {
|
|||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Destroy history
|
||||
*/
|
||||
History.prototype.destroy = function () {
|
||||
this.editor = null;
|
||||
|
||||
this.history = [];
|
||||
this.index = -1;
|
||||
};
|
||||
|
||||
module.exports = History;
|
||||
|
|
|
@ -130,10 +130,9 @@ JSONEditor.prototype._create = function (container, options, json) {
|
|||
};
|
||||
|
||||
/**
|
||||
* Detach the editor from the DOM
|
||||
* @private
|
||||
* Destroy the editor. Clean up DOM, event listeners, and web workers.
|
||||
*/
|
||||
JSONEditor.prototype._delete = function () {};
|
||||
JSONEditor.prototype.destroy = function () {};
|
||||
|
||||
/**
|
||||
* Set JSON object in editor
|
||||
|
@ -207,7 +206,7 @@ JSONEditor.prototype.setMode = function (mode) {
|
|||
name = this.getName();
|
||||
data = this[asText ? 'getText' : 'get'](); // get text or json
|
||||
|
||||
this._delete();
|
||||
this.destroy();
|
||||
util.clear(this);
|
||||
util.extend(this, config.mixin);
|
||||
this.create(container, options);
|
||||
|
|
|
@ -2,64 +2,48 @@ var ContextMenu = require('./ContextMenu');
|
|||
|
||||
/**
|
||||
* Create a select box to be used in the editor menu's, which allows to switch mode
|
||||
* @param {Object} editor
|
||||
* @param {HTMLElement} container
|
||||
* @param {String[]} modes Available modes: 'code', 'form', 'text', 'tree', 'view'
|
||||
* @param {String} current Available modes: 'code', 'form', 'text', 'tree', 'view'
|
||||
* @returns {HTMLElement} box
|
||||
* @param {function(mode: string)} onSwitch Callback invoked on switch
|
||||
* @constructor
|
||||
*/
|
||||
function createModeSwitcher(editor, modes, current) {
|
||||
// TODO: decouple mode switcher from editor
|
||||
|
||||
/**
|
||||
* Switch the mode of the editor
|
||||
* @param {String} mode
|
||||
*/
|
||||
function switchMode(mode) {
|
||||
// switch mode
|
||||
editor.setMode(mode);
|
||||
|
||||
// restore focus on mode box
|
||||
var modeBox = editor.dom && editor.dom.modeBox;
|
||||
if (modeBox) {
|
||||
modeBox.focus();
|
||||
}
|
||||
}
|
||||
|
||||
function ModeSwitcher(container, modes, current, onSwitch) {
|
||||
// available modes
|
||||
var availableModes = {
|
||||
code: {
|
||||
'text': 'Code',
|
||||
'title': 'Switch to code highlighter',
|
||||
'click': function () {
|
||||
switchMode('code')
|
||||
onSwitch('code')
|
||||
}
|
||||
},
|
||||
form: {
|
||||
'text': 'Form',
|
||||
'title': 'Switch to form editor',
|
||||
'click': function () {
|
||||
switchMode('form');
|
||||
onSwitch('form');
|
||||
}
|
||||
},
|
||||
text: {
|
||||
'text': 'Text',
|
||||
'title': 'Switch to plain text editor',
|
||||
'click': function () {
|
||||
switchMode('text');
|
||||
onSwitch('text');
|
||||
}
|
||||
},
|
||||
tree: {
|
||||
'text': 'Tree',
|
||||
'title': 'Switch to tree editor',
|
||||
'click': function () {
|
||||
switchMode('tree');
|
||||
onSwitch('tree');
|
||||
}
|
||||
},
|
||||
view: {
|
||||
'text': 'View',
|
||||
'title': 'Switch to tree view',
|
||||
'click': function () {
|
||||
switchMode('view');
|
||||
onSwitch('view');
|
||||
}
|
||||
}
|
||||
};
|
||||
|
@ -94,12 +78,35 @@ function createModeSwitcher(editor, modes, current) {
|
|||
menu.show(box);
|
||||
};
|
||||
|
||||
var div = document.createElement('div');
|
||||
div.className = 'jsoneditor-modes';
|
||||
div.style.position = 'relative';
|
||||
div.appendChild(box);
|
||||
var frame = document.createElement('div');
|
||||
frame.className = 'jsoneditor-modes';
|
||||
frame.style.position = 'relative';
|
||||
frame.appendChild(box);
|
||||
|
||||
return div;
|
||||
container.appendChild(frame);
|
||||
|
||||
this.dom = {
|
||||
container: container,
|
||||
box: box,
|
||||
frame: frame
|
||||
};
|
||||
}
|
||||
|
||||
exports.create = createModeSwitcher;
|
||||
/**
|
||||
* Set focus to switcher
|
||||
*/
|
||||
ModeSwitcher.prototype.focus = function () {
|
||||
this.dom.box.focus();
|
||||
};
|
||||
|
||||
/**
|
||||
* Destroy the ModeSwitcher, remove from DOM
|
||||
*/
|
||||
ModeSwitcher.prototype.destroy = function () {
|
||||
if (this.dom && this.dom.frame && this.dom.frame.parentNode) {
|
||||
this.dom.frame.parentNode.removeChild(this.dom.frame);
|
||||
}
|
||||
this.dom = null;
|
||||
};
|
||||
|
||||
module.exports = ModeSwitcher;
|
|
@ -2073,7 +2073,6 @@ Node.prototype.onEvent = function (event) {
|
|||
target = event.target || event.srcElement,
|
||||
dom = this.dom,
|
||||
node = this,
|
||||
focusNode,
|
||||
expandable = this._hasChilds();
|
||||
|
||||
// check if mouse is on menu or on dragarea.
|
||||
|
@ -2123,7 +2122,7 @@ Node.prototype.onEvent = function (event) {
|
|||
//noinspection FallthroughInSwitchStatementJS
|
||||
switch (type) {
|
||||
case 'focus':
|
||||
focusNode = this;
|
||||
this.editor.focusNode = this;
|
||||
break;
|
||||
|
||||
case 'blur':
|
||||
|
@ -2176,7 +2175,7 @@ Node.prototype.onEvent = function (event) {
|
|||
if (target == domField) {
|
||||
switch (type) {
|
||||
case 'focus':
|
||||
focusNode = this;
|
||||
this.editor.focusNode = this;
|
||||
break;
|
||||
|
||||
case 'blur':
|
||||
|
|
|
@ -292,4 +292,19 @@ SearchBox.prototype.clear = function () {
|
|||
this._onSearch();
|
||||
};
|
||||
|
||||
/**
|
||||
* Destroy the search box
|
||||
*/
|
||||
SearchBox.prototype.destroy = function () {
|
||||
this.editor = null;
|
||||
this.dom.container.removeChild(this.dom.table);
|
||||
this.dom = null;
|
||||
|
||||
this.results = null;
|
||||
this.activeResult = null;
|
||||
|
||||
this._clearDelay();
|
||||
|
||||
};
|
||||
|
||||
module.exports = SearchBox;
|
||||
|
|
|
@ -6,7 +6,7 @@ catch (err) {
|
|||
// failed to load ace, no problem, we will fall back to plain text
|
||||
}
|
||||
|
||||
var modeswitcher = require('./modeswitcher');
|
||||
var ModeSwitcher = require('./ModeSwitcher');
|
||||
var util = require('./util');
|
||||
|
||||
// create a mixin with the functions for text mode
|
||||
|
@ -123,9 +123,13 @@ textmode.create = function (container, options) {
|
|||
|
||||
// create mode box
|
||||
if (this.options && this.options.modes && this.options.modes.length) {
|
||||
var modeBox = modeswitcher.create(this, this.options.modes, this.options.mode);
|
||||
this.menu.appendChild(modeBox);
|
||||
this.dom.modeBox = modeBox;
|
||||
this.modeSwitcher = new ModeSwitcher(this.menu, this.options.modes, this.options.mode, function onSwitch(mode) {
|
||||
me.modeSwitcher.destroy();
|
||||
|
||||
// switch mode and restore focus
|
||||
me.setMode(mode);
|
||||
me.modeSwitcher.focus();
|
||||
});
|
||||
}
|
||||
|
||||
this.content = document.createElement('div');
|
||||
|
@ -253,18 +257,27 @@ textmode._onKeyDown = function (event) {
|
|||
};
|
||||
|
||||
/**
|
||||
* Detach the editor from the DOM
|
||||
* @private
|
||||
* Destroy the editor. Clean up DOM, event listeners, and web workers.
|
||||
*/
|
||||
textmode._delete = function () {
|
||||
textmode.destroy = function () {
|
||||
// remove old ace editor
|
||||
if (this.aceEditor) {
|
||||
this.aceEditor.destroy();
|
||||
this.aceEditor = null;
|
||||
}
|
||||
|
||||
if (this.frame && this.container && this.frame.parentNode == this.container) {
|
||||
this.container.removeChild(this.frame);
|
||||
}
|
||||
|
||||
if (this.modeSwitcher) {
|
||||
this.modeSwitcher.destroy();
|
||||
this.modeSwitcher = null;
|
||||
}
|
||||
|
||||
this.textarea = null;
|
||||
|
||||
this._debouncedValidate = null;
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
|
@ -3,7 +3,7 @@ var History = require('./History');
|
|||
var SearchBox = require('./SearchBox');
|
||||
var ContextMenu = require('./ContextMenu');
|
||||
var Node = require('./Node');
|
||||
var modeswitcher = require('./modeswitcher');
|
||||
var ModeSwitcher = require('./ModeSwitcher');
|
||||
var util = require('./util');
|
||||
|
||||
// create a mixin with the functions for tree mode
|
||||
|
@ -43,6 +43,8 @@ treemode.create = function (container, options) {
|
|||
this.validateSchema = null; // will be set in .setSchema(schema)
|
||||
this.errorNodes = [];
|
||||
|
||||
this.node = null;
|
||||
this.focusNode = null;
|
||||
|
||||
this._setOptions(options);
|
||||
|
||||
|
@ -55,12 +57,39 @@ treemode.create = function (container, options) {
|
|||
};
|
||||
|
||||
/**
|
||||
* Detach the editor from the DOM
|
||||
* @private
|
||||
* Destroy the editor. Clean up DOM, event listeners, and web workers.
|
||||
*/
|
||||
treemode._delete = function () {
|
||||
treemode.destroy = function () {
|
||||
if (this.frame && this.container && this.frame.parentNode == this.container) {
|
||||
this.container.removeChild(this.frame);
|
||||
this.frame = null;
|
||||
}
|
||||
this.container = null;
|
||||
|
||||
this.dom = null;
|
||||
|
||||
this.clear();
|
||||
this.node = null;
|
||||
this.focusNode = null;
|
||||
this.selection = null;
|
||||
this.multiselection = null;
|
||||
this.errorNodes = null;
|
||||
this.validateSchema = null;
|
||||
this._debouncedValidate = null;
|
||||
|
||||
if (this.history) {
|
||||
this.history.destroy();
|
||||
this.history = null;
|
||||
}
|
||||
|
||||
if (this.searchBox) {
|
||||
this.searchBox.destroy();
|
||||
this.searchBox = null;
|
||||
}
|
||||
|
||||
if (this.modeSwitcher) {
|
||||
this.modeSwitcher.destroy();
|
||||
this.modeSwitcher = null;
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -94,12 +123,6 @@ treemode._setOptions = function (options) {
|
|||
this._debouncedValidate = util.debounce(this.validate.bind(this), this.DEBOUNCE_INTERVAL);
|
||||
};
|
||||
|
||||
// node currently being edited
|
||||
var focusNode = undefined;
|
||||
|
||||
// dom having focus
|
||||
var domFocus = null;
|
||||
|
||||
/**
|
||||
* Set JSON object in editor
|
||||
* @param {Object | undefined} json JSON data
|
||||
|
@ -156,8 +179,8 @@ treemode.set = function (json, name) {
|
|||
*/
|
||||
treemode.get = function () {
|
||||
// remove focus from currently edited node
|
||||
if (focusNode) {
|
||||
focusNode.blur();
|
||||
if (this.focusNode) {
|
||||
this.focusNode.blur();
|
||||
}
|
||||
|
||||
if (this.node) {
|
||||
|
@ -520,7 +543,7 @@ treemode.getSelection = function () {
|
|||
}
|
||||
|
||||
return {
|
||||
dom: domFocus,
|
||||
dom: this.dom.focus,
|
||||
range: range,
|
||||
nodes: this.multiselection.nodes.slice(0),
|
||||
scrollTop: this.content ? this.content.scrollTop : 0
|
||||
|
@ -686,9 +709,14 @@ treemode._createFrame = function () {
|
|||
|
||||
// create mode box
|
||||
if (this.options && this.options.modes && this.options.modes.length) {
|
||||
var modeBox = modeswitcher.create(this, this.options.modes, this.options.mode);
|
||||
this.menu.appendChild(modeBox);
|
||||
this.dom.modeBox = modeBox;
|
||||
var me = this;
|
||||
this.modeSwitcher = new ModeSwitcher(this.menu, this.options.modes, this.options.mode, function onSwitch(mode) {
|
||||
me.modeSwitcher.destroy();
|
||||
|
||||
// switch mode and restore focus
|
||||
me.setMode(mode);
|
||||
me.modeSwitcher.focus();
|
||||
});
|
||||
}
|
||||
|
||||
// create search box
|
||||
|
@ -736,7 +764,7 @@ treemode._onEvent = function (event) {
|
|||
}
|
||||
|
||||
if (event.type == 'focus') {
|
||||
domFocus = event.target;
|
||||
this.dom.focus = event.target;
|
||||
}
|
||||
|
||||
if (event.type == 'mousedown') {
|
||||
|
@ -1011,7 +1039,7 @@ treemode._onKeyDown = function (event) {
|
|||
if (keynum == 9) { // Tab or Shift+Tab
|
||||
setTimeout(function () {
|
||||
// select all text when moving focus to an editable div
|
||||
util.selectContentEditable(domFocus);
|
||||
util.selectContentEditable(this.dom.focus);
|
||||
}, 0);
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue