Implemented debouncing of keyboard input, resulting in much less history actions whilst typing
This commit is contained in:
parent
c0077250ef
commit
f45fefe38f
|
@ -10,6 +10,8 @@ https://github.com/josdejong/jsoneditor
|
|||
- Implemented #197: display an error in case of duplicate keys in an object.
|
||||
- Implemented #183: display a checkbox left from boolean values, so you can
|
||||
easily switch between true/false.
|
||||
- Implemented debouncing of keyboard input, resulting in much less history
|
||||
actions whilst typing.
|
||||
- Added a minimalist bundle to the `dist` folder, excluding `ace` and `ajv`.
|
||||
- Fixed #222: editor throwing `onChange` events when switching mode.
|
||||
- Fixed an error throw when switching to mode "code" via the menu.
|
||||
|
@ -17,6 +19,7 @@ https://github.com/josdejong/jsoneditor
|
|||
from `Shift+Arrow Up/Down` to `Ctrl+Shift+Arrow Up/Down`.
|
||||
|
||||
|
||||
|
||||
## 2015-12-31, version 5.0.1
|
||||
|
||||
- Fixed a bug in positioning of the context menu for multiple selected nodes.
|
||||
|
|
|
@ -75,7 +75,7 @@ function JSONEditor (container, options, json) {
|
|||
if (options) {
|
||||
var VALID_OPTIONS = [
|
||||
'ace', 'theme',
|
||||
'ajv', 'schema', 'debounceInterval',
|
||||
'ajv', 'schema',
|
||||
'onChange', 'onEditable', 'onError', 'onModeChange',
|
||||
'escapeUnicode', 'history', 'mode', 'modes', 'name', 'indentation'
|
||||
];
|
||||
|
@ -110,6 +110,9 @@ function JSONEditor (container, options, json) {
|
|||
*/
|
||||
JSONEditor.modes = {};
|
||||
|
||||
// debounce interval for JSON schema vaidation in milliseconds
|
||||
JSONEditor.prototype.DEBOUNCE_INTERVAL = 150;
|
||||
|
||||
/**
|
||||
* Create the JSONEditor
|
||||
* @param {Element} container Container element
|
||||
|
|
|
@ -28,9 +28,12 @@ function Node (editor, params) {
|
|||
this.setValue(null);
|
||||
}
|
||||
|
||||
this._debouncedGetDomValue = util.debounce(this._getDomValue.bind(this), 100);
|
||||
this._debouncedGetDomValue = util.debounce(this._getDomValue.bind(this), Node.prototype.DEBOUNCE_INTERVAL);
|
||||
}
|
||||
|
||||
// debounce interval for keyboard input in milliseconds
|
||||
Node.prototype.DEBOUNCE_INTERVAL = 150;
|
||||
|
||||
/**
|
||||
* Determine whether the field and/or value of this node are editable
|
||||
* @private
|
||||
|
@ -1115,12 +1118,28 @@ Node.prototype._getDomValue = function(silent) {
|
|||
if (value !== this.value) {
|
||||
var oldValue = this.value;
|
||||
this.value = value;
|
||||
var selection = this.editor.getSelection();
|
||||
var undoDiff = util.textDiff(value, oldValue);
|
||||
var redoDiff = util.textDiff(oldValue, value);
|
||||
console.log('selection', selection, oldValue, value, util.textDiff(oldValue, value), util.textDiff(value, oldValue))
|
||||
this.editor._onAction('editValue', {
|
||||
'node': this,
|
||||
'oldValue': oldValue,
|
||||
'newValue': value,
|
||||
'oldSelection': this.editor.selection,
|
||||
'newSelection': this.editor.getSelection()
|
||||
'oldSelection': util.extend({}, selection, {
|
||||
range: {
|
||||
container: selection.range.container,
|
||||
startOffset: undoDiff.start,
|
||||
endOffset: undoDiff.end
|
||||
}
|
||||
}),
|
||||
'newSelection': util.extend({}, selection, {
|
||||
range: {
|
||||
container: selection.range.container,
|
||||
startOffset: redoDiff.start,
|
||||
endOffset: redoDiff.end
|
||||
}
|
||||
})
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -2080,7 +2099,7 @@ Node.prototype.onEvent = function (event) {
|
|||
break;
|
||||
|
||||
case 'input':
|
||||
this._getDomValue(true);
|
||||
this._debouncedGetDomValue(true);
|
||||
this._updateDomValue();
|
||||
break;
|
||||
|
||||
|
@ -2098,7 +2117,7 @@ Node.prototype.onEvent = function (event) {
|
|||
break;
|
||||
|
||||
case 'keyup':
|
||||
this._getDomValue(true);
|
||||
this._debouncedGetDomValue(true);
|
||||
this._updateDomValue();
|
||||
break;
|
||||
|
||||
|
|
|
@ -71,9 +71,7 @@ textmode.create = function (container, options) {
|
|||
this.validateSchema = null;
|
||||
|
||||
// create a debounced validate function
|
||||
var wait = this.options.debounceInterval;
|
||||
var immediate = true;
|
||||
this._debouncedValidate = util.debounce(this.validate.bind(this), wait, immediate);
|
||||
this._debouncedValidate = util.debounce(this.validate.bind(this), this.DEBOUNCE_INTERVAL);
|
||||
|
||||
this.width = container.clientWidth;
|
||||
this.height = container.clientHeight;
|
||||
|
|
|
@ -75,8 +75,7 @@ treemode._setOptions = function (options) {
|
|||
history: true,
|
||||
mode: 'tree',
|
||||
name: undefined, // field name of root node
|
||||
schema: null,
|
||||
debounceInterval: 100 // debounce interval for schema validation in milliseconds
|
||||
schema: null
|
||||
};
|
||||
|
||||
// copy all options
|
||||
|
@ -92,9 +91,7 @@ treemode._setOptions = function (options) {
|
|||
this.setSchema(this.options.schema);
|
||||
|
||||
// create a debounced validate function
|
||||
var wait = this.options.debounceInterval;
|
||||
var immediate = true;
|
||||
this._debouncedValidate = util.debounce(this.validate.bind(this), wait, immediate);
|
||||
this._debouncedValidate = util.debounce(this.validate.bind(this), this.DEBOUNCE_INTERVAL);
|
||||
};
|
||||
|
||||
// node currently being edited
|
||||
|
|
|
@ -695,3 +695,50 @@ exports.debounce = function debounce(func, wait, immediate) {
|
|||
if (callNow) func.apply(context, args);
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* Determines the difference between two texts.
|
||||
* Can only detect one removed or inserted block of characters.
|
||||
* @param {string} oldText
|
||||
* @param {string} newText
|
||||
* @return {{start: number, end: number}} Returns the start and end
|
||||
* of the changed part in newText.
|
||||
*/
|
||||
exports.textDiff = function textDiff(oldText, newText) {
|
||||
var len = newText.length;
|
||||
var start = 0;
|
||||
var oldEnd = oldText.length;
|
||||
var newEnd = newText.length;
|
||||
|
||||
while (newText.charAt(start) === oldText.charAt(start)
|
||||
&& start < len) {
|
||||
start++;
|
||||
}
|
||||
|
||||
while (newText.charAt(newEnd - 1) === oldText.charAt(oldEnd - 1)
|
||||
&& newEnd > start && oldEnd > 0) {
|
||||
newEnd--;
|
||||
oldEnd--;
|
||||
}
|
||||
|
||||
return {start: start, end: newEnd};
|
||||
};
|
||||
|
||||
/**
|
||||
* Extend object a with the properties of object b or a series of objects
|
||||
* Only properties with defined values are copied
|
||||
* @param {Object} a
|
||||
* @param {... Object} b
|
||||
* @return {Object} a
|
||||
*/
|
||||
exports.extend = function (a, b) {
|
||||
for (var i = 1; i < arguments.length; i++) {
|
||||
var other = arguments[i];
|
||||
for (var prop in other) {
|
||||
if (other.hasOwnProperty(prop)) {
|
||||
a[prop] = other[prop];
|
||||
}
|
||||
}
|
||||
}
|
||||
return a;
|
||||
};
|
||||
|
|
|
@ -87,7 +87,6 @@
|
|||
|
||||
console.log('json', json);
|
||||
console.log('schema', schema);
|
||||
console.log('string', JSON.stringify(json));
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
Loading…
Reference in New Issue