Released v5.15.0

This commit is contained in:
jos 2018-05-02 10:42:47 +02:00
parent a3c79d0270
commit 9a77045e78
9 changed files with 959 additions and 217 deletions

View File

@ -3,7 +3,7 @@
https://github.com/josdejong/jsoneditor https://github.com/josdejong/jsoneditor
## not yet released, version 5.15.0 ## 2018-05-02, version 5.15.0
- Implemented selection API: `onSelectionChanged`, `onTextSelectionChanged`, - Implemented selection API: `onSelectionChanged`, `onTextSelectionChanged`,
`getSelection`, `getTextSelection`, `setSelection`, `setTextSelection`, `getSelection`, `getTextSelection`, `setSelection`, `setTextSelection`,

View File

@ -24,8 +24,8 @@
* Copyright (c) 2011-2017 Jos de Jong, http://jsoneditoronline.org * Copyright (c) 2011-2017 Jos de Jong, http://jsoneditoronline.org
* *
* @author Jos de Jong, <wjosdejong@gmail.com> * @author Jos de Jong, <wjosdejong@gmail.com>
* @version 5.14.1 * @version 5.15.0
* @date 2018-03-21 * @date 2018-05-02
*/ */
(function webpackUniversalModuleDefinition(root, factory) { (function webpackUniversalModuleDefinition(root, factory) {
if(typeof exports === 'object' && typeof module === 'object') if(typeof exports === 'object' && typeof module === 'object')
@ -129,6 +129,14 @@ return /******/ (function(modules) { // webpackBootstrap
* {boolean} sortObjectKeys If true, object keys are * {boolean} sortObjectKeys If true, object keys are
* sorted before display. * sorted before display.
* false by default. * false by default.
* {function} onSelectionChange Callback method,
* triggered on node selection change
* Only applicable for modes
* 'tree', 'view', and 'form'
* {function} onTextSelectionChange Callback method,
* triggered on text selection change
* Only applicable for modes
* 'text' and 'code'
* @param {Object | undefined} json JSON object * @param {Object | undefined} json JSON object
*/ */
function JSONEditor (container, options, json) { function JSONEditor (container, options, json) {
@ -166,7 +174,7 @@ return /******/ (function(modules) { // webpackBootstrap
var VALID_OPTIONS = [ var VALID_OPTIONS = [
'ajv', 'schema', 'schemaRefs','templates', 'ajv', 'schema', 'schemaRefs','templates',
'ace', 'theme','autocomplete', 'ace', 'theme','autocomplete',
'onChange', 'onEditable', 'onError', 'onModeChange', 'onChange', 'onEditable', 'onError', 'onModeChange', 'onSelectionChange', 'onTextSelectionChange',
'escapeUnicode', 'history', 'search', 'mode', 'modes', 'name', 'indentation', 'escapeUnicode', 'history', 'search', 'mode', 'modes', 'name', 'indentation',
'sortObjectKeys', 'navigationBar', 'statusBar', 'languages', 'language' 'sortObjectKeys', 'navigationBar', 'statusBar', 'languages', 'language'
]; ];
@ -607,7 +615,8 @@ return /******/ (function(modules) { // webpackBootstrap
schema: null, schema: null,
schemaRefs: null, schemaRefs: null,
autocomplete: null, autocomplete: null,
navigationBar : true navigationBar : true,
onSelectionChange: null
}; };
// copy all options // copy all options
@ -625,6 +634,10 @@ return /******/ (function(modules) { // webpackBootstrap
// create a debounced validate function // create a debounced validate function
this._debouncedValidate = util.debounce(this.validate.bind(this), this.DEBOUNCE_INTERVAL); this._debouncedValidate = util.debounce(this.validate.bind(this), this.DEBOUNCE_INTERVAL);
if (options.onSelectionChange) {
this.onSelectionChange(options.onSelectionChange);
}
setLanguages(this.options.languages); setLanguages(this.options.languages);
setLanguage(this.options.language) setLanguage(this.options.language)
}; };
@ -1040,7 +1053,7 @@ return /******/ (function(modules) { // webpackBootstrap
* {Node[]} nodes Nodes in case of multi selection * {Node[]} nodes Nodes in case of multi selection
* {Number} scrollTop Scroll position * {Number} scrollTop Scroll position
*/ */
treemode.setSelection = function (selection) { treemode.setDomSelection = function (selection) {
if (!selection) { if (!selection) {
return; return;
} }
@ -1070,7 +1083,7 @@ return /******/ (function(modules) { // webpackBootstrap
* {Node[]} nodes Nodes in case of multi selection * {Node[]} nodes Nodes in case of multi selection
* {Number} scrollTop Scroll position * {Number} scrollTop Scroll position
*/ */
treemode.getSelection = function () { treemode.getDomSelection = function () {
var range = util.getSelectionOffset(); var range = util.getSelectionOffset();
if (range && range.container.nodeName !== 'DIV') { // filter on (editable) divs) if (range && range.container.nodeName !== 'DIV') { // filter on (editable) divs)
range = null; range = null;
@ -1537,6 +1550,14 @@ return /******/ (function(modules) { // webpackBootstrap
if (start && end) { if (start && end) {
// find the top level childs, all having the same parent // find the top level childs, all having the same parent
this.multiselection.nodes = this._findTopLevelNodes(start, end); this.multiselection.nodes = this._findTopLevelNodes(start, end);
if (this.multiselection.nodes && this.multiselection.nodes.length) {
var firstNode = this.multiselection.nodes[0];
if (this.multiselection.start === firstNode || this.multiselection.start.isDescendantOf(firstNode)) {
this.multiselection.direction = 'down';
} else {
this.multiselection.direction = 'up';
}
}
this.select(this.multiselection.nodes); this.select(this.multiselection.nodes);
} }
}; };
@ -1572,6 +1593,7 @@ return /******/ (function(modules) { // webpackBootstrap
* state is cleared too. * state is cleared too.
*/ */
treemode.deselect = function (clearStartAndEnd) { treemode.deselect = function (clearStartAndEnd) {
var selectionChanged = !!this.multiselection.nodes.length;
this.multiselection.nodes.forEach(function (node) { this.multiselection.nodes.forEach(function (node) {
node.setSelected(false); node.setSelected(false);
}); });
@ -1581,6 +1603,12 @@ return /******/ (function(modules) { // webpackBootstrap
this.multiselection.start = null; this.multiselection.start = null;
this.multiselection.end = null; this.multiselection.end = null;
} }
if (selectionChanged) {
if (this._selectionChangedHandler) {
this._selectionChangedHandler();
}
}
}; };
/** /**
@ -1601,6 +1629,11 @@ return /******/ (function(modules) { // webpackBootstrap
nodes.forEach(function (node) { nodes.forEach(function (node) {
node.setSelected(true, node === first); node.setSelected(true, node === first);
}); });
if (this._selectionChangedHandler) {
var selection = this.getSelection();
this._selectionChangedHandler(selection.start, selection.end);
}
} }
}; };
@ -1829,6 +1862,125 @@ return /******/ (function(modules) { // webpackBootstrap
menu.show(anchor, this.content); menu.show(anchor, this.content);
}; };
/**
* Get current selected nodes
* @return {{start:SerializableNode, end: SerializableNode}}
*/
treemode.getSelection = function () {
var selection = {
start: null,
end: null
};
if (this.multiselection.nodes && this.multiselection.nodes.length) {
if (this.multiselection.nodes.length) {
var selection1 = this.multiselection.nodes[0];
var selection2 = this.multiselection.nodes[this.multiselection.nodes.length - 1];
if (this.multiselection.direction === 'down') {
selection.start = selection1.serialize();
selection.end = selection2.serialize();
} else {
selection.start = selection2.serialize();
selection.end = selection1.serialize();
}
}
}
return selection;
};
/**
* Callback registraion for selection change
* @param {selectionCallback} callback
*
* @callback selectionCallback
* @param {SerializableNode=} start
* @param {SerializableNode=} end
*/
treemode.onSelectionChange = function (callback) {
if (typeof callback === 'function') {
this._selectionChangedHandler = util.debounce(callback, this.DEBOUNCE_INTERVAL);
}
};
/**
* Select range of nodes.
* For selecting single node send only the start parameter
* For clear the selection do not send any parameter
* If the nodes are not from the same level the first common parent will be selected
* @param {{path: Array.<String>}} start object contains the path for selection start
* @param {{path: Array.<String>}=} end object contains the path for selection end
*/
treemode.setSelection = function (start, end) {
// check for old usage
if (start && start.dom && start.range) {
console.warn('setSelection/getSelection usage for text selection is depracated and should not be used, see documantaion for supported selection options');
this.setDomSelection(start);
}
var nodes = this._getNodeIntsncesByRange(start, end);
nodes.forEach(function(node) {
node.expandTo();
});
this.select(nodes);
};
/**
* Returns a set of Nodes according to a range of selection
* @param {{path: Array.<String>}} start object contains the path for range start
* @param {{path: Array.<String>}=} end object contains the path for range end
* @return {Array.<Node>} Node intances on the given range
* @private
*/
treemode._getNodeIntsncesByRange = function (start, end) {
var startNode, endNode;
if (start && start.path) {
startNode = this.node.findNodeByPath(start.path);
if (end && end.path) {
endNode = this.node.findNodeByPath(end.path);
}
}
var nodes = [];
if (startNode instanceof Node) {
if (endNode instanceof Node && endNode !== startNode) {
if (startNode.parent === endNode.parent) {
var start, end;
if (startNode.getIndex() < endNode.getIndex()) {
start = startNode;
end = endNode;
} else {
start = endNode;
end = startNode;
}
var current = start;
nodes.push(current);
do {
current = current.nextSibling();
nodes.push(current);
} while (current && current !== end);
} else {
nodes = this._findTopLevelNodes(startNode, endNode);
}
} else {
nodes.push(startNode);
}
}
return nodes;
};
treemode.getNodesByRange = function (start, end) {
var nodes = this._getNodeIntsncesByRange(start, end);
var serializableNodes = [];
nodes.forEach(function (node){
serializableNodes.push(node.serialize());
});
return serializableNodes;
}
// define modes // define modes
module.exports = [ module.exports = [
@ -2162,7 +2314,7 @@ return /******/ (function(modules) { // webpackBootstrap
if (action && action.undo) { if (action && action.undo) {
action.undo(obj.params); action.undo(obj.params);
if (obj.params.oldSelection) { if (obj.params.oldSelection) {
this.editor.setSelection(obj.params.oldSelection); this.editor.setDomSelection(obj.params.oldSelection);
} }
} }
else { else {
@ -2189,7 +2341,7 @@ return /******/ (function(modules) { // webpackBootstrap
if (action && action.redo) { if (action && action.redo) {
action.redo(obj.params); action.redo(obj.params);
if (obj.params.newSelection) { if (obj.params.newSelection) {
this.editor.setSelection(obj.params.newSelection); this.editor.setDomSelection(obj.params.newSelection);
} }
} }
else { else {
@ -3046,11 +3198,11 @@ return /******/ (function(modules) { // webpackBootstrap
* @return {Object} reference Object with 2 properties (start and end) with the identifier of the location of the cursor and selected text. * @return {Object} reference Object with 2 properties (start and end) with the identifier of the location of the cursor and selected text.
**/ **/
exports.getInputSelection = function(el) { exports.getInputSelection = function(el) {
var start = 0, end = 0, normalizedValue, range, textInputRange, len, endRange; var startIndex = 0, endIndex = 0, normalizedValue, range, textInputRange, len, endRange;
if (typeof el.selectionStart == "number" && typeof el.selectionEnd == "number") { if (typeof el.selectionStart == "number" && typeof el.selectionEnd == "number") {
start = el.selectionStart; startIndex = el.selectionStart;
end = el.selectionEnd; endIndex = el.selectionEnd;
} else { } else {
range = document.selection.createRange(); range = document.selection.createRange();
@ -3062,38 +3214,69 @@ return /******/ (function(modules) { // webpackBootstrap
textInputRange = el.createTextRange(); textInputRange = el.createTextRange();
textInputRange.moveToBookmark(range.getBookmark()); textInputRange.moveToBookmark(range.getBookmark());
// Check if the start and end of the selection are at the very end // Check if the startIndex and endIndex of the selection are at the very end
// of the input, since moveStart/moveEnd doesn't return what we want // of the input, since moveStart/moveEnd doesn't return what we want
// in those cases // in those cases
endRange = el.createTextRange(); endRange = el.createTextRange();
endRange.collapse(false); endRange.collapse(false);
if (textInputRange.compareEndPoints("StartToEnd", endRange) > -1) { if (textInputRange.compareEndPoints("StartToEnd", endRange) > -1) {
start = end = len; startIndex = endIndex = len;
} else { } else {
start = -textInputRange.moveStart("character", -len); startIndex = -textInputRange.moveStart("character", -len);
start += normalizedValue.slice(0, start).split("\n").length - 1; startIndex += normalizedValue.slice(0, startIndex).split("\n").length - 1;
if (textInputRange.compareEndPoints("EndToEnd", endRange) > -1) { if (textInputRange.compareEndPoints("EndToEnd", endRange) > -1) {
end = len; endIndex = len;
} else { } else {
end = -textInputRange.moveEnd("character", -len); endIndex = -textInputRange.moveEnd("character", -len);
end += normalizedValue.slice(0, end).split("\n").length - 1; endIndex += normalizedValue.slice(0, endIndex).split("\n").length - 1;
} }
} }
} }
} }
var textTillCaret = el.value.substring(0,end);
var row = (textTillCaret.match(/\n/g) || []).length + 1;
var col = textTillCaret.length - textTillCaret.lastIndexOf("\n");
return { return {
start: start, startIndex: startIndex,
end: end, endIndex: endIndex,
col: col, start: _positionForIndex(startIndex),
row: row end: _positionForIndex(endIndex)
}; };
/**
* Returns textarea row and column position for certain index
* @param {Number} index text index
* @returns {{row: Number, col: Number}}
*/
function _positionForIndex(index) {
var textTillIndex = el.value.substring(0,index);
var row = (textTillIndex.match(/\n/g) || []).length + 1;
var col = textTillIndex.length - textTillIndex.lastIndexOf("\n");
return {
row: row,
column: col
}
}
}
/**
* Returns the index for certaion position in text element
* @param {DOMElement} el A dom element of a textarea or input text.
* @param {Number} row row value, > 0, if exceeds rows number - last row will be returned
* @param {Number} column column value, > 0, if exceeds column length - end of column will be returned
* @returns {Number} index of position in text, -1 if not found
*/
exports.getIndexForPosition = function(el, row, column) {
var text = el.value || '';
if (row > 0 && column > 0) {
var rows = text.split('\n', row);
row = Math.min(rows.length, row);
column = Math.min(rows[row - 1].length, column - 1);
var columnCount = (row == 1 ? column : column + 1); // count new line on multiple rows
return rows.slice(0, row - 1).join('\n').length + columnCount;
}
return -1;
} }
@ -4744,12 +4927,7 @@ return /******/ (function(modules) { // webpackBootstrap
var node = this; var node = this;
var path = []; var path = [];
while (node) { while (node) {
var field = !node.parent var field = node.getName();
? undefined // do not add an (optional) field name of the root node
: (node.parent.type != 'array')
? node.field
: node.index;
if (field !== undefined) { if (field !== undefined) {
path.unshift(field); path.unshift(field);
} }
@ -4758,6 +4936,53 @@ return /******/ (function(modules) { // webpackBootstrap
return path; return path;
}; };
/**
* Get node serializable name
* @returns {String|Number}
*/
Node.prototype.getName = function () {
return !this.parent
? undefined // do not add an (optional) field name of the root node
: (this.parent.type != 'array')
? this.field
: this.index;
};
/**
* Find child node by serializable path
* @param {Array<String>} path
*/
Node.prototype.findNodeByPath = function (path) {
if (!path) {
return;
}
if (path.length == 0) {
return this;
}
if (path.length && this.childs && this.childs.length) {
for (var i=0; i < this.childs.length; ++i) {
if (('' + path[0]) === ('' + this.childs[i].getName())) {
return this.childs[i].findNodeByPath(path.slice(1));
}
}
}
};
/**
* @typedef {{value: String|Object|Number|Boolean, path: Array.<String|Number>}} SerializableNode
*
* Returns serializable representation for the node
* @return {SerializedNode}
*/
Node.prototype.serialize = function () {
return {
value: this.getValue(),
path: this.getPath()
};
};
/** /**
* Find a Node from a JSON path like '.items[3].name' * Find a Node from a JSON path like '.items[3].name'
* @param {string} jsonPath * @param {string} jsonPath
@ -5828,13 +6053,13 @@ return /******/ (function(modules) { // webpackBootstrap
Node.prototype._onChangeValue = function () { Node.prototype._onChangeValue = function () {
// get current selection, then override the range such that we can select // get current selection, then override the range such that we can select
// the added/removed text on undo/redo // the added/removed text on undo/redo
var oldSelection = this.editor.getSelection(); var oldSelection = this.editor.getDomSelection();
if (oldSelection.range) { if (oldSelection.range) {
var undoDiff = util.textDiff(String(this.value), String(this.previousValue)); var undoDiff = util.textDiff(String(this.value), String(this.previousValue));
oldSelection.range.startOffset = undoDiff.start; oldSelection.range.startOffset = undoDiff.start;
oldSelection.range.endOffset = undoDiff.end; oldSelection.range.endOffset = undoDiff.end;
} }
var newSelection = this.editor.getSelection(); var newSelection = this.editor.getDomSelection();
if (newSelection.range) { if (newSelection.range) {
var redoDiff = util.textDiff(String(this.previousValue), String(this.value)); var redoDiff = util.textDiff(String(this.previousValue), String(this.value));
newSelection.range.startOffset = redoDiff.start; newSelection.range.startOffset = redoDiff.start;
@ -5859,14 +6084,14 @@ return /******/ (function(modules) { // webpackBootstrap
Node.prototype._onChangeField = function () { Node.prototype._onChangeField = function () {
// get current selection, then override the range such that we can select // get current selection, then override the range such that we can select
// the added/removed text on undo/redo // the added/removed text on undo/redo
var oldSelection = this.editor.getSelection(); var oldSelection = this.editor.getDomSelection();
var previous = this.previousField || ''; var previous = this.previousField || '';
if (oldSelection.range) { if (oldSelection.range) {
var undoDiff = util.textDiff(this.field, previous); var undoDiff = util.textDiff(this.field, previous);
oldSelection.range.startOffset = undoDiff.start; oldSelection.range.startOffset = undoDiff.start;
oldSelection.range.endOffset = undoDiff.end; oldSelection.range.endOffset = undoDiff.end;
} }
var newSelection = this.editor.getSelection(); var newSelection = this.editor.getDomSelection();
if (newSelection.range) { if (newSelection.range) {
var redoDiff = util.textDiff(previous, this.field); var redoDiff = util.textDiff(previous, this.field);
newSelection.range.startOffset = redoDiff.start; newSelection.range.startOffset = redoDiff.start;
@ -6216,7 +6441,7 @@ return /******/ (function(modules) { // webpackBootstrap
var firstNode = nodes[0]; var firstNode = nodes[0];
var lastNode = nodes[nodes.length - 1]; var lastNode = nodes[nodes.length - 1];
var draggedNode = Node.getNodeFromTarget(event.target); var draggedNode = Node.getNodeFromTarget(event.target);
var beforeNode = lastNode._nextSibling(); var beforeNode = lastNode.nextSibling();
var editor = firstNode.editor; var editor = firstNode.editor;
// in case of multiple selected nodes, offsetY prevents the selection from // in case of multiple selected nodes, offsetY prevents the selection from
@ -6238,7 +6463,7 @@ return /******/ (function(modules) { // webpackBootstrap
editor.highlighter.lock(); editor.highlighter.lock();
editor.drag = { editor.drag = {
oldCursor: document.body.style.cursor, oldCursor: document.body.style.cursor,
oldSelection: editor.getSelection(), oldSelection: editor.getDomSelection(),
oldBeforeNode: beforeNode, oldBeforeNode: beforeNode,
mouseX: event.pageX, mouseX: event.pageX,
offsetY: offsetY, offsetY: offsetY,
@ -6359,7 +6584,7 @@ return /******/ (function(modules) { // webpackBootstrap
nodePrev = Node.getNodeFromTarget(trPrev); nodePrev = Node.getNodeFromTarget(trPrev);
var isDraggedNode = nodes.some(function (node) { var isDraggedNode = nodes.some(function (node) {
return node === nodePrev || nodePrev._isChildOf(node); return node === nodePrev || nodePrev.isDescendantOf(node);
}); });
if (isDraggedNode) { if (isDraggedNode) {
@ -6436,7 +6661,7 @@ return /******/ (function(modules) { // webpackBootstrap
var params = { var params = {
nodes: nodes, nodes: nodes,
oldSelection: editor.drag.oldSelection, oldSelection: editor.drag.oldSelection,
newSelection: editor.getSelection(), newSelection: editor.getDomSelection(),
oldBeforeNode: editor.drag.oldBeforeNode, oldBeforeNode: editor.drag.oldBeforeNode,
newBeforeNode: beforeNode newBeforeNode: beforeNode
}; };
@ -6471,12 +6696,12 @@ return /******/ (function(modules) { // webpackBootstrap
}; };
/** /**
* Test if this node is a child of an other node * Test if this node is a sescendant of an other node
* @param {Node} node * @param {Node} node
* @return {boolean} isChild * @return {boolean} isDescendant
* @private * @private
*/ */
Node.prototype._isChildOf = function (node) { Node.prototype.isDescendantOf = function (node) {
var n = this.parent; var n = this.parent;
while (n) { while (n) {
if (n == node) { if (n == node) {
@ -6986,7 +7211,7 @@ return /******/ (function(modules) { // webpackBootstrap
case 'keydown': case 'keydown':
case 'mousedown': case 'mousedown':
// TODO: cleanup // TODO: cleanup
this.editor.selection = this.editor.getSelection(); this.editor.selection = this.editor.getDomSelection();
break; break;
case 'click': case 'click':
@ -7037,7 +7262,7 @@ return /******/ (function(modules) { // webpackBootstrap
case 'keydown': case 'keydown':
case 'mousedown': case 'mousedown':
this.editor.selection = this.editor.getSelection(); this.editor.selection = this.editor.getDomSelection();
break; break;
case 'keyup': case 'keyup':
@ -7212,8 +7437,8 @@ return /******/ (function(modules) { // webpackBootstrap
if (nextNode && nextNode instanceof AppendNode && if (nextNode && nextNode instanceof AppendNode &&
!(lastNode.parent.childs.length == 1) && !(lastNode.parent.childs.length == 1) &&
nextNode2 && nextNode2.parent) { nextNode2 && nextNode2.parent) {
oldSelection = this.editor.getSelection(); oldSelection = this.editor.getDomSelection();
oldBeforeNode = lastNode._nextSibling(); oldBeforeNode = lastNode.nextSibling();
selectedNodes.forEach(function (node) { selectedNodes.forEach(function (node) {
nextNode2.parent.moveBefore(node, nextNode2); nextNode2.parent.moveBefore(node, nextNode2);
@ -7225,7 +7450,7 @@ return /******/ (function(modules) { // webpackBootstrap
oldBeforeNode: oldBeforeNode, oldBeforeNode: oldBeforeNode,
newBeforeNode: nextNode2, newBeforeNode: nextNode2,
oldSelection: oldSelection, oldSelection: oldSelection,
newSelection: this.editor.getSelection() newSelection: this.editor.getDomSelection()
}); });
} }
} }
@ -7259,8 +7484,8 @@ return /******/ (function(modules) { // webpackBootstrap
// find the previous node // find the previous node
prevNode = firstNode._previousNode(); prevNode = firstNode._previousNode();
if (prevNode && prevNode.parent) { if (prevNode && prevNode.parent) {
oldSelection = this.editor.getSelection(); oldSelection = this.editor.getDomSelection();
oldBeforeNode = lastNode._nextSibling(); oldBeforeNode = lastNode.nextSibling();
selectedNodes.forEach(function (node) { selectedNodes.forEach(function (node) {
prevNode.parent.moveBefore(node, prevNode); prevNode.parent.moveBefore(node, prevNode);
@ -7272,7 +7497,7 @@ return /******/ (function(modules) { // webpackBootstrap
oldBeforeNode: oldBeforeNode, oldBeforeNode: oldBeforeNode,
newBeforeNode: prevNode, newBeforeNode: prevNode,
oldSelection: oldSelection, oldSelection: oldSelection,
newSelection: this.editor.getSelection() newSelection: this.editor.getDomSelection()
}); });
} }
handled = true; handled = true;
@ -7295,8 +7520,8 @@ return /******/ (function(modules) { // webpackBootstrap
if (prevNode && prevNode.parent && if (prevNode && prevNode.parent &&
(prevNode instanceof AppendNode) (prevNode instanceof AppendNode)
&& !prevNode.isVisible()) { && !prevNode.isVisible()) {
oldSelection = this.editor.getSelection(); oldSelection = this.editor.getDomSelection();
oldBeforeNode = lastNode._nextSibling(); oldBeforeNode = lastNode.nextSibling();
selectedNodes.forEach(function (node) { selectedNodes.forEach(function (node) {
prevNode.parent.moveBefore(node, prevNode); prevNode.parent.moveBefore(node, prevNode);
@ -7308,7 +7533,7 @@ return /******/ (function(modules) { // webpackBootstrap
oldBeforeNode: oldBeforeNode, oldBeforeNode: oldBeforeNode,
newBeforeNode: prevNode, newBeforeNode: prevNode,
oldSelection: oldSelection, oldSelection: oldSelection,
newSelection: this.editor.getSelection() newSelection: this.editor.getDomSelection()
}); });
} }
} }
@ -7348,8 +7573,8 @@ return /******/ (function(modules) { // webpackBootstrap
} }
var nextNode2 = nextNode && (nextNode._nextNode() || nextNode.parent.append); var nextNode2 = nextNode && (nextNode._nextNode() || nextNode.parent.append);
if (nextNode2 && nextNode2.parent) { if (nextNode2 && nextNode2.parent) {
oldSelection = this.editor.getSelection(); oldSelection = this.editor.getDomSelection();
oldBeforeNode = lastNode._nextSibling(); oldBeforeNode = lastNode.nextSibling();
selectedNodes.forEach(function (node) { selectedNodes.forEach(function (node) {
nextNode2.parent.moveBefore(node, nextNode2); nextNode2.parent.moveBefore(node, nextNode2);
@ -7361,7 +7586,7 @@ return /******/ (function(modules) { // webpackBootstrap
oldBeforeNode: oldBeforeNode, oldBeforeNode: oldBeforeNode,
newBeforeNode: nextNode2, newBeforeNode: nextNode2,
oldSelection: oldSelection, oldSelection: oldSelection,
newSelection: this.editor.getSelection() newSelection: this.editor.getDomSelection()
}); });
} }
handled = true; handled = true;
@ -7419,9 +7644,9 @@ return /******/ (function(modules) { // webpackBootstrap
editor.highlighter.unhighlight(); editor.highlighter.unhighlight();
// adjust the focus // adjust the focus
var oldSelection = editor.getSelection(); var oldSelection = editor.getDomSelection();
Node.blurNodes(nodes); Node.blurNodes(nodes);
var newSelection = editor.getSelection(); var newSelection = editor.getDomSelection();
// remove the nodes // remove the nodes
nodes.forEach(function (node) { nodes.forEach(function (node) {
@ -7458,7 +7683,7 @@ return /******/ (function(modules) { // webpackBootstrap
editor.deselect(editor.multiselection.nodes); editor.deselect(editor.multiselection.nodes);
// duplicate the nodes // duplicate the nodes
var oldSelection = editor.getSelection(); var oldSelection = editor.getDomSelection();
var afterNode = lastNode; var afterNode = lastNode;
var clones = nodes.map(function (node) { var clones = nodes.map(function (node) {
var clone = node.clone(); var clone = node.clone();
@ -7474,7 +7699,7 @@ return /******/ (function(modules) { // webpackBootstrap
else { else {
editor.select(clones); editor.select(clones);
} }
var newSelection = editor.getSelection(); var newSelection = editor.getDomSelection();
editor._onAction('duplicateNodes', { editor._onAction('duplicateNodes', {
afterNode: lastNode, afterNode: lastNode,
@ -7494,7 +7719,7 @@ return /******/ (function(modules) { // webpackBootstrap
* @private * @private
*/ */
Node.prototype._onInsertBefore = function (field, value, type) { Node.prototype._onInsertBefore = function (field, value, type) {
var oldSelection = this.editor.getSelection(); var oldSelection = this.editor.getDomSelection();
var newNode = new Node(this.editor, { var newNode = new Node(this.editor, {
field: (field != undefined) ? field : '', field: (field != undefined) ? field : '',
@ -7505,7 +7730,7 @@ return /******/ (function(modules) { // webpackBootstrap
this.parent.insertBefore(newNode, this); this.parent.insertBefore(newNode, this);
this.editor.highlighter.unhighlight(); this.editor.highlighter.unhighlight();
newNode.focus('field'); newNode.focus('field');
var newSelection = this.editor.getSelection(); var newSelection = this.editor.getDomSelection();
this.editor._onAction('insertBeforeNodes', { this.editor._onAction('insertBeforeNodes', {
nodes: [newNode], nodes: [newNode],
@ -7524,7 +7749,7 @@ return /******/ (function(modules) { // webpackBootstrap
* @private * @private
*/ */
Node.prototype._onInsertAfter = function (field, value, type) { Node.prototype._onInsertAfter = function (field, value, type) {
var oldSelection = this.editor.getSelection(); var oldSelection = this.editor.getDomSelection();
var newNode = new Node(this.editor, { var newNode = new Node(this.editor, {
field: (field != undefined) ? field : '', field: (field != undefined) ? field : '',
@ -7535,7 +7760,7 @@ return /******/ (function(modules) { // webpackBootstrap
this.parent.insertAfter(newNode, this); this.parent.insertAfter(newNode, this);
this.editor.highlighter.unhighlight(); this.editor.highlighter.unhighlight();
newNode.focus('field'); newNode.focus('field');
var newSelection = this.editor.getSelection(); var newSelection = this.editor.getDomSelection();
this.editor._onAction('insertAfterNodes', { this.editor._onAction('insertAfterNodes', {
nodes: [newNode], nodes: [newNode],
@ -7554,7 +7779,7 @@ return /******/ (function(modules) { // webpackBootstrap
* @private * @private
*/ */
Node.prototype._onAppend = function (field, value, type) { Node.prototype._onAppend = function (field, value, type) {
var oldSelection = this.editor.getSelection(); var oldSelection = this.editor.getDomSelection();
var newNode = new Node(this.editor, { var newNode = new Node(this.editor, {
field: (field != undefined) ? field : '', field: (field != undefined) ? field : '',
@ -7565,7 +7790,7 @@ return /******/ (function(modules) { // webpackBootstrap
this.parent.appendChild(newNode); this.parent.appendChild(newNode);
this.editor.highlighter.unhighlight(); this.editor.highlighter.unhighlight();
newNode.focus('field'); newNode.focus('field');
var newSelection = this.editor.getSelection(); var newSelection = this.editor.getDomSelection();
this.editor._onAction('appendNodes', { this.editor._onAction('appendNodes', {
nodes: [newNode], nodes: [newNode],
@ -7583,9 +7808,9 @@ return /******/ (function(modules) { // webpackBootstrap
Node.prototype._onChangeType = function (newType) { Node.prototype._onChangeType = function (newType) {
var oldType = this.type; var oldType = this.type;
if (newType != oldType) { if (newType != oldType) {
var oldSelection = this.editor.getSelection(); var oldSelection = this.editor.getDomSelection();
this.changeType(newType); this.changeType(newType);
var newSelection = this.editor.getSelection(); var newSelection = this.editor.getDomSelection();
this.editor._onAction('changeType', { this.editor._onAction('changeType', {
node: this, node: this,
@ -7693,9 +7918,8 @@ return /******/ (function(modules) { // webpackBootstrap
/** /**
* Get the next sibling of current node * Get the next sibling of current node
* @return {Node} nextSibling * @return {Node} nextSibling
* @private
*/ */
Node.prototype._nextSibling = function () { Node.prototype.nextSibling = function () {
var index = this.parent.childs.indexOf(this); var index = this.parent.childs.indexOf(this);
return this.parent.childs[index + 1] || this.parent.append; return this.parent.childs[index + 1] || this.parent.append;
}; };
@ -7703,7 +7927,6 @@ return /******/ (function(modules) { // webpackBootstrap
/** /**
* Get the previously rendered node * Get the previously rendered node
* @return {Node | null} previousNode * @return {Node | null} previousNode
* @private
*/ */
Node.prototype._previousNode = function () { Node.prototype._previousNode = function () {
var prevNode = null; var prevNode = null;
@ -9102,6 +9325,8 @@ return /******/ (function(modules) { // webpackBootstrap
* {boolean} escapeUnicode If true, unicode * {boolean} escapeUnicode If true, unicode
* characters are escaped. * characters are escaped.
* false by default. * false by default.
* {function} onTextSelectionChange Callback method,
* triggered on text selection change
* @private * @private
*/ */
textmode.create = function (container, options) { textmode.create = function (container, options) {
@ -9147,6 +9372,10 @@ return /******/ (function(modules) { // webpackBootstrap
} }
} }
if (options.onTextSelectionChange) {
this.onTextSelectionChange(options.onTextSelectionChange);
}
var me = this; var me = this;
this.container = container; this.container = container;
this.dom = {}; this.dom = {};
@ -9403,9 +9632,8 @@ return /******/ (function(modules) { // webpackBootstrap
* @private * @private
*/ */
textmode._onSelect = function () { textmode._onSelect = function () {
if(this.options.statusBar) { this._updateCursorInfo();
this._updateCursorInfoDisplay(); this._emitSelectionChange();
}
}; };
/** /**
@ -9434,7 +9662,8 @@ return /******/ (function(modules) { // webpackBootstrap
event.stopPropagation(); event.stopPropagation();
} }
this._updateCursorInfoDisplay(); this._updateCursorInfo();
this._emitSelectionChange();
}; };
/** /**
@ -9443,7 +9672,8 @@ return /******/ (function(modules) { // webpackBootstrap
* @private * @private
*/ */
textmode._onMouseDown = function (event) { textmode._onMouseDown = function (event) {
this._updateCursorInfoDisplay(); this._updateCursorInfo();
this._emitSelectionChange();
}; };
/** /**
@ -9452,35 +9682,59 @@ return /******/ (function(modules) { // webpackBootstrap
* @private * @private
*/ */
textmode._onBlur = function (event) { textmode._onBlur = function (event) {
this._updateCursorInfoDisplay(); this._updateCursorInfo();
this._emitSelectionChange();
}; };
/** /**
* Update the status bar cursor info * Update the cursor info and the status bar, if presented
*/ */
textmode._updateCursorInfoDisplay = function () { textmode._updateCursorInfo = function () {
var me = this; var me = this;
var line, col, count; var line, col, count;
if(this.options.statusBar) { if (this.textarea) {
if (this.textarea) { setTimeout(function() { //this to verify we get the most updated textarea cursor selection
setTimeout(function() { //this to verify we get the most updated textarea cursor selection var selectionRange = util.getInputSelection(me.textarea);
var selectionRange = util.getInputSelection(me.textarea);
line = selectionRange.row; if (selectionRange.startIndex !== selectionRange.endIndex) {
col = selectionRange.col; count = selectionRange.endIndex - selectionRange.startIndex;
if (selectionRange.start !== selectionRange.end) { }
count = selectionRange.end - selectionRange.start;
} if (count && me.cursorInfo && me.cursorInfo.line === selectionRange.end.row && me.cursorInfo.column === selectionRange.end.column) {
line = selectionRange.start.row;
col = selectionRange.start.column;
} else {
line = selectionRange.end.row;
col = selectionRange.end.column;
}
me.cursorInfo = {
line: line,
column: col,
count: count
}
if(me.options.statusBar) {
updateDisplay(); updateDisplay();
},0); }
},0);
} else if (this.aceEditor && this.curserInfoElements) { } else if (this.aceEditor && this.curserInfoElements) {
var curserPos = this.aceEditor.getCursorPosition(); var curserPos = this.aceEditor.getCursorPosition();
var selectedText = this.aceEditor.getSelectedText(); var selectedText = this.aceEditor.getSelectedText();
line = curserPos.row + 1; line = curserPos.row + 1;
col = curserPos.column + 1; col = curserPos.column + 1;
count = selectedText.length; count = selectedText.length;
me.cursorInfo = {
line: line,
column: col,
count: count
}
if(this.options.statusBar) {
updateDisplay(); updateDisplay();
} }
} }
@ -9497,6 +9751,17 @@ return /******/ (function(modules) { // webpackBootstrap
} }
}; };
/**
* emits selection change callback, if given
* @private
*/
textmode._emitSelectionChange = function () {
if(this._selectionChangedHandler) {
var currentSelection = this.getTextSelection();
this._selectionChangedHandler(currentSelection.start, currentSelection.end, currentSelection.text);
}
}
/** /**
* Destroy the editor. Clean up DOM, event listeners, and web workers. * Destroy the editor. Clean up DOM, event listeners, and web workers.
*/ */
@ -9722,6 +9987,112 @@ return /******/ (function(modules) { // webpackBootstrap
} }
}; };
/**
* Get the selection details
* @returns {{start:{row:Number, column:Number},end:{row:Number, column:Number},text:String}}
*/
textmode.getTextSelection = function () {
var selection = {};
if (this.textarea) {
var selectionRange = util.getInputSelection(this.textarea);
if (this.cursorInfo && this.cursorInfo.line === selectionRange.end.row && this.cursorInfo.column === selectionRange.end.column) {
//selection direction is bottom => up
selection.start = selectionRange.end;
selection.end = selectionRange.start;
} else {
selection = selectionRange;
}
return {
start: selection.start,
end: selection.end,
text: this.textarea.value.substring(selectionRange.startIndex, selectionRange.endIndex)
}
}
if (this.aceEditor) {
var aceSelection = this.aceEditor.getSelection();
var selectedText = this.aceEditor.getSelectedText();
var range = aceSelection.getRange();
var lead = aceSelection.getSelectionLead();
if (lead.row === range.end.row && lead.column === range.end.column) {
selection = range;
} else {
//selection direction is bottom => up
selection.start = range.end;
selection.end = range.start;
}
return {
start: {
row: selection.start.row + 1,
column: selection.start.column + 1
},
end: {
row: selection.end.row + 1,
column: selection.end.column + 1
},
text: selectedText
};
}
};
/**
* Callback registraion for selection change
* @param {selectionCallback} callback
*
* @callback selectionCallback
* @param {{row:Number, column:Number}} startPos selection start position
* @param {{row:Number, column:Number}} endPos selected end position
* @param {String} text selected text
*/
textmode.onTextSelectionChange = function (callback) {
if (typeof callback === 'function') {
this._selectionChangedHandler = util.debounce(callback, this.DEBOUNCE_INTERVAL);
}
};
/**
* Set selection on editor's text
* @param {{row:Number, column:Number}} startPos selection start position
* @param {{row:Number, column:Number}} endPos selected end position
*/
textmode.setTextSelection = function (startPos, endPos) {
if (!startPos || !endPos) return;
if (this.textarea) {
var startIndex = util.getIndexForPosition(this.textarea, startPos.row, startPos.column);
var endIndex = util.getIndexForPosition(this.textarea, endPos.row, endPos.column);
if (startIndex > -1 && endIndex > -1) {
if (this.textarea.setSelectionRange) {
this.textarea.focus();
this.textarea.setSelectionRange(startIndex, endIndex);
} else if (this.textarea.createTextRange) { // IE < 9
var range = this.textarea.createTextRange();
range.collapse(true);
range.moveEnd('character', endIndex);
range.moveStart('character', startIndex);
range.select();
}
}
} else if (this.aceEditor) {
var range = {
start:{
row: startPos.row - 1,
column: startPos.column - 1
},
end:{
row: endPos.row - 1,
column: endPos.column - 1
}
};
this.aceEditor.selection.setRange(range);
}
};
// define modes // define modes
module.exports = [ module.exports = [
{ {

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

563
dist/jsoneditor.js vendored
View File

@ -24,8 +24,8 @@
* Copyright (c) 2011-2017 Jos de Jong, http://jsoneditoronline.org * Copyright (c) 2011-2017 Jos de Jong, http://jsoneditoronline.org
* *
* @author Jos de Jong, <wjosdejong@gmail.com> * @author Jos de Jong, <wjosdejong@gmail.com>
* @version 5.14.1 * @version 5.15.0
* @date 2018-03-21 * @date 2018-05-02
*/ */
(function webpackUniversalModuleDefinition(root, factory) { (function webpackUniversalModuleDefinition(root, factory) {
if(typeof exports === 'object' && typeof module === 'object') if(typeof exports === 'object' && typeof module === 'object')
@ -129,6 +129,14 @@ return /******/ (function(modules) { // webpackBootstrap
* {boolean} sortObjectKeys If true, object keys are * {boolean} sortObjectKeys If true, object keys are
* sorted before display. * sorted before display.
* false by default. * false by default.
* {function} onSelectionChange Callback method,
* triggered on node selection change
* Only applicable for modes
* 'tree', 'view', and 'form'
* {function} onTextSelectionChange Callback method,
* triggered on text selection change
* Only applicable for modes
* 'text' and 'code'
* @param {Object | undefined} json JSON object * @param {Object | undefined} json JSON object
*/ */
function JSONEditor (container, options, json) { function JSONEditor (container, options, json) {
@ -166,7 +174,7 @@ return /******/ (function(modules) { // webpackBootstrap
var VALID_OPTIONS = [ var VALID_OPTIONS = [
'ajv', 'schema', 'schemaRefs','templates', 'ajv', 'schema', 'schemaRefs','templates',
'ace', 'theme','autocomplete', 'ace', 'theme','autocomplete',
'onChange', 'onEditable', 'onError', 'onModeChange', 'onChange', 'onEditable', 'onError', 'onModeChange', 'onSelectionChange', 'onTextSelectionChange',
'escapeUnicode', 'history', 'search', 'mode', 'modes', 'name', 'indentation', 'escapeUnicode', 'history', 'search', 'mode', 'modes', 'name', 'indentation',
'sortObjectKeys', 'navigationBar', 'statusBar', 'languages', 'language' 'sortObjectKeys', 'navigationBar', 'statusBar', 'languages', 'language'
]; ];
@ -8153,7 +8161,8 @@ return /******/ (function(modules) { // webpackBootstrap
schema: null, schema: null,
schemaRefs: null, schemaRefs: null,
autocomplete: null, autocomplete: null,
navigationBar : true navigationBar : true,
onSelectionChange: null
}; };
// copy all options // copy all options
@ -8171,6 +8180,10 @@ return /******/ (function(modules) { // webpackBootstrap
// create a debounced validate function // create a debounced validate function
this._debouncedValidate = util.debounce(this.validate.bind(this), this.DEBOUNCE_INTERVAL); this._debouncedValidate = util.debounce(this.validate.bind(this), this.DEBOUNCE_INTERVAL);
if (options.onSelectionChange) {
this.onSelectionChange(options.onSelectionChange);
}
setLanguages(this.options.languages); setLanguages(this.options.languages);
setLanguage(this.options.language) setLanguage(this.options.language)
}; };
@ -8586,7 +8599,7 @@ return /******/ (function(modules) { // webpackBootstrap
* {Node[]} nodes Nodes in case of multi selection * {Node[]} nodes Nodes in case of multi selection
* {Number} scrollTop Scroll position * {Number} scrollTop Scroll position
*/ */
treemode.setSelection = function (selection) { treemode.setDomSelection = function (selection) {
if (!selection) { if (!selection) {
return; return;
} }
@ -8616,7 +8629,7 @@ return /******/ (function(modules) { // webpackBootstrap
* {Node[]} nodes Nodes in case of multi selection * {Node[]} nodes Nodes in case of multi selection
* {Number} scrollTop Scroll position * {Number} scrollTop Scroll position
*/ */
treemode.getSelection = function () { treemode.getDomSelection = function () {
var range = util.getSelectionOffset(); var range = util.getSelectionOffset();
if (range && range.container.nodeName !== 'DIV') { // filter on (editable) divs) if (range && range.container.nodeName !== 'DIV') { // filter on (editable) divs)
range = null; range = null;
@ -9083,6 +9096,14 @@ return /******/ (function(modules) { // webpackBootstrap
if (start && end) { if (start && end) {
// find the top level childs, all having the same parent // find the top level childs, all having the same parent
this.multiselection.nodes = this._findTopLevelNodes(start, end); this.multiselection.nodes = this._findTopLevelNodes(start, end);
if (this.multiselection.nodes && this.multiselection.nodes.length) {
var firstNode = this.multiselection.nodes[0];
if (this.multiselection.start === firstNode || this.multiselection.start.isDescendantOf(firstNode)) {
this.multiselection.direction = 'down';
} else {
this.multiselection.direction = 'up';
}
}
this.select(this.multiselection.nodes); this.select(this.multiselection.nodes);
} }
}; };
@ -9118,6 +9139,7 @@ return /******/ (function(modules) { // webpackBootstrap
* state is cleared too. * state is cleared too.
*/ */
treemode.deselect = function (clearStartAndEnd) { treemode.deselect = function (clearStartAndEnd) {
var selectionChanged = !!this.multiselection.nodes.length;
this.multiselection.nodes.forEach(function (node) { this.multiselection.nodes.forEach(function (node) {
node.setSelected(false); node.setSelected(false);
}); });
@ -9127,6 +9149,12 @@ return /******/ (function(modules) { // webpackBootstrap
this.multiselection.start = null; this.multiselection.start = null;
this.multiselection.end = null; this.multiselection.end = null;
} }
if (selectionChanged) {
if (this._selectionChangedHandler) {
this._selectionChangedHandler();
}
}
}; };
/** /**
@ -9147,6 +9175,11 @@ return /******/ (function(modules) { // webpackBootstrap
nodes.forEach(function (node) { nodes.forEach(function (node) {
node.setSelected(true, node === first); node.setSelected(true, node === first);
}); });
if (this._selectionChangedHandler) {
var selection = this.getSelection();
this._selectionChangedHandler(selection.start, selection.end);
}
} }
}; };
@ -9375,6 +9408,125 @@ return /******/ (function(modules) { // webpackBootstrap
menu.show(anchor, this.content); menu.show(anchor, this.content);
}; };
/**
* Get current selected nodes
* @return {{start:SerializableNode, end: SerializableNode}}
*/
treemode.getSelection = function () {
var selection = {
start: null,
end: null
};
if (this.multiselection.nodes && this.multiselection.nodes.length) {
if (this.multiselection.nodes.length) {
var selection1 = this.multiselection.nodes[0];
var selection2 = this.multiselection.nodes[this.multiselection.nodes.length - 1];
if (this.multiselection.direction === 'down') {
selection.start = selection1.serialize();
selection.end = selection2.serialize();
} else {
selection.start = selection2.serialize();
selection.end = selection1.serialize();
}
}
}
return selection;
};
/**
* Callback registraion for selection change
* @param {selectionCallback} callback
*
* @callback selectionCallback
* @param {SerializableNode=} start
* @param {SerializableNode=} end
*/
treemode.onSelectionChange = function (callback) {
if (typeof callback === 'function') {
this._selectionChangedHandler = util.debounce(callback, this.DEBOUNCE_INTERVAL);
}
};
/**
* Select range of nodes.
* For selecting single node send only the start parameter
* For clear the selection do not send any parameter
* If the nodes are not from the same level the first common parent will be selected
* @param {{path: Array.<String>}} start object contains the path for selection start
* @param {{path: Array.<String>}=} end object contains the path for selection end
*/
treemode.setSelection = function (start, end) {
// check for old usage
if (start && start.dom && start.range) {
console.warn('setSelection/getSelection usage for text selection is depracated and should not be used, see documantaion for supported selection options');
this.setDomSelection(start);
}
var nodes = this._getNodeIntsncesByRange(start, end);
nodes.forEach(function(node) {
node.expandTo();
});
this.select(nodes);
};
/**
* Returns a set of Nodes according to a range of selection
* @param {{path: Array.<String>}} start object contains the path for range start
* @param {{path: Array.<String>}=} end object contains the path for range end
* @return {Array.<Node>} Node intances on the given range
* @private
*/
treemode._getNodeIntsncesByRange = function (start, end) {
var startNode, endNode;
if (start && start.path) {
startNode = this.node.findNodeByPath(start.path);
if (end && end.path) {
endNode = this.node.findNodeByPath(end.path);
}
}
var nodes = [];
if (startNode instanceof Node) {
if (endNode instanceof Node && endNode !== startNode) {
if (startNode.parent === endNode.parent) {
var start, end;
if (startNode.getIndex() < endNode.getIndex()) {
start = startNode;
end = endNode;
} else {
start = endNode;
end = startNode;
}
var current = start;
nodes.push(current);
do {
current = current.nextSibling();
nodes.push(current);
} while (current && current !== end);
} else {
nodes = this._findTopLevelNodes(startNode, endNode);
}
} else {
nodes.push(startNode);
}
}
return nodes;
};
treemode.getNodesByRange = function (start, end) {
var nodes = this._getNodeIntsncesByRange(start, end);
var serializableNodes = [];
nodes.forEach(function (node){
serializableNodes.push(node.serialize());
});
return serializableNodes;
}
// define modes // define modes
module.exports = [ module.exports = [
@ -9708,7 +9860,7 @@ return /******/ (function(modules) { // webpackBootstrap
if (action && action.undo) { if (action && action.undo) {
action.undo(obj.params); action.undo(obj.params);
if (obj.params.oldSelection) { if (obj.params.oldSelection) {
this.editor.setSelection(obj.params.oldSelection); this.editor.setDomSelection(obj.params.oldSelection);
} }
} }
else { else {
@ -9735,7 +9887,7 @@ return /******/ (function(modules) { // webpackBootstrap
if (action && action.redo) { if (action && action.redo) {
action.redo(obj.params); action.redo(obj.params);
if (obj.params.newSelection) { if (obj.params.newSelection) {
this.editor.setSelection(obj.params.newSelection); this.editor.setDomSelection(obj.params.newSelection);
} }
} }
else { else {
@ -10592,11 +10744,11 @@ return /******/ (function(modules) { // webpackBootstrap
* @return {Object} reference Object with 2 properties (start and end) with the identifier of the location of the cursor and selected text. * @return {Object} reference Object with 2 properties (start and end) with the identifier of the location of the cursor and selected text.
**/ **/
exports.getInputSelection = function(el) { exports.getInputSelection = function(el) {
var start = 0, end = 0, normalizedValue, range, textInputRange, len, endRange; var startIndex = 0, endIndex = 0, normalizedValue, range, textInputRange, len, endRange;
if (typeof el.selectionStart == "number" && typeof el.selectionEnd == "number") { if (typeof el.selectionStart == "number" && typeof el.selectionEnd == "number") {
start = el.selectionStart; startIndex = el.selectionStart;
end = el.selectionEnd; endIndex = el.selectionEnd;
} else { } else {
range = document.selection.createRange(); range = document.selection.createRange();
@ -10608,38 +10760,69 @@ return /******/ (function(modules) { // webpackBootstrap
textInputRange = el.createTextRange(); textInputRange = el.createTextRange();
textInputRange.moveToBookmark(range.getBookmark()); textInputRange.moveToBookmark(range.getBookmark());
// Check if the start and end of the selection are at the very end // Check if the startIndex and endIndex of the selection are at the very end
// of the input, since moveStart/moveEnd doesn't return what we want // of the input, since moveStart/moveEnd doesn't return what we want
// in those cases // in those cases
endRange = el.createTextRange(); endRange = el.createTextRange();
endRange.collapse(false); endRange.collapse(false);
if (textInputRange.compareEndPoints("StartToEnd", endRange) > -1) { if (textInputRange.compareEndPoints("StartToEnd", endRange) > -1) {
start = end = len; startIndex = endIndex = len;
} else { } else {
start = -textInputRange.moveStart("character", -len); startIndex = -textInputRange.moveStart("character", -len);
start += normalizedValue.slice(0, start).split("\n").length - 1; startIndex += normalizedValue.slice(0, startIndex).split("\n").length - 1;
if (textInputRange.compareEndPoints("EndToEnd", endRange) > -1) { if (textInputRange.compareEndPoints("EndToEnd", endRange) > -1) {
end = len; endIndex = len;
} else { } else {
end = -textInputRange.moveEnd("character", -len); endIndex = -textInputRange.moveEnd("character", -len);
end += normalizedValue.slice(0, end).split("\n").length - 1; endIndex += normalizedValue.slice(0, endIndex).split("\n").length - 1;
} }
} }
} }
} }
var textTillCaret = el.value.substring(0,end);
var row = (textTillCaret.match(/\n/g) || []).length + 1;
var col = textTillCaret.length - textTillCaret.lastIndexOf("\n");
return { return {
start: start, startIndex: startIndex,
end: end, endIndex: endIndex,
col: col, start: _positionForIndex(startIndex),
row: row end: _positionForIndex(endIndex)
}; };
/**
* Returns textarea row and column position for certain index
* @param {Number} index text index
* @returns {{row: Number, col: Number}}
*/
function _positionForIndex(index) {
var textTillIndex = el.value.substring(0,index);
var row = (textTillIndex.match(/\n/g) || []).length + 1;
var col = textTillIndex.length - textTillIndex.lastIndexOf("\n");
return {
row: row,
column: col
}
}
}
/**
* Returns the index for certaion position in text element
* @param {DOMElement} el A dom element of a textarea or input text.
* @param {Number} row row value, > 0, if exceeds rows number - last row will be returned
* @param {Number} column column value, > 0, if exceeds column length - end of column will be returned
* @returns {Number} index of position in text, -1 if not found
*/
exports.getIndexForPosition = function(el, row, column) {
var text = el.value || '';
if (row > 0 && column > 0) {
var rows = text.split('\n', row);
row = Math.min(rows.length, row);
column = Math.min(rows[row - 1].length, column - 1);
var columnCount = (row == 1 ? column : column + 1); // count new line on multiple rows
return rows.slice(0, row - 1).join('\n').length + columnCount;
}
return -1;
} }
@ -12290,12 +12473,7 @@ return /******/ (function(modules) { // webpackBootstrap
var node = this; var node = this;
var path = []; var path = [];
while (node) { while (node) {
var field = !node.parent var field = node.getName();
? undefined // do not add an (optional) field name of the root node
: (node.parent.type != 'array')
? node.field
: node.index;
if (field !== undefined) { if (field !== undefined) {
path.unshift(field); path.unshift(field);
} }
@ -12304,6 +12482,53 @@ return /******/ (function(modules) { // webpackBootstrap
return path; return path;
}; };
/**
* Get node serializable name
* @returns {String|Number}
*/
Node.prototype.getName = function () {
return !this.parent
? undefined // do not add an (optional) field name of the root node
: (this.parent.type != 'array')
? this.field
: this.index;
};
/**
* Find child node by serializable path
* @param {Array<String>} path
*/
Node.prototype.findNodeByPath = function (path) {
if (!path) {
return;
}
if (path.length == 0) {
return this;
}
if (path.length && this.childs && this.childs.length) {
for (var i=0; i < this.childs.length; ++i) {
if (('' + path[0]) === ('' + this.childs[i].getName())) {
return this.childs[i].findNodeByPath(path.slice(1));
}
}
}
};
/**
* @typedef {{value: String|Object|Number|Boolean, path: Array.<String|Number>}} SerializableNode
*
* Returns serializable representation for the node
* @return {SerializedNode}
*/
Node.prototype.serialize = function () {
return {
value: this.getValue(),
path: this.getPath()
};
};
/** /**
* Find a Node from a JSON path like '.items[3].name' * Find a Node from a JSON path like '.items[3].name'
* @param {string} jsonPath * @param {string} jsonPath
@ -13374,13 +13599,13 @@ return /******/ (function(modules) { // webpackBootstrap
Node.prototype._onChangeValue = function () { Node.prototype._onChangeValue = function () {
// get current selection, then override the range such that we can select // get current selection, then override the range such that we can select
// the added/removed text on undo/redo // the added/removed text on undo/redo
var oldSelection = this.editor.getSelection(); var oldSelection = this.editor.getDomSelection();
if (oldSelection.range) { if (oldSelection.range) {
var undoDiff = util.textDiff(String(this.value), String(this.previousValue)); var undoDiff = util.textDiff(String(this.value), String(this.previousValue));
oldSelection.range.startOffset = undoDiff.start; oldSelection.range.startOffset = undoDiff.start;
oldSelection.range.endOffset = undoDiff.end; oldSelection.range.endOffset = undoDiff.end;
} }
var newSelection = this.editor.getSelection(); var newSelection = this.editor.getDomSelection();
if (newSelection.range) { if (newSelection.range) {
var redoDiff = util.textDiff(String(this.previousValue), String(this.value)); var redoDiff = util.textDiff(String(this.previousValue), String(this.value));
newSelection.range.startOffset = redoDiff.start; newSelection.range.startOffset = redoDiff.start;
@ -13405,14 +13630,14 @@ return /******/ (function(modules) { // webpackBootstrap
Node.prototype._onChangeField = function () { Node.prototype._onChangeField = function () {
// get current selection, then override the range such that we can select // get current selection, then override the range such that we can select
// the added/removed text on undo/redo // the added/removed text on undo/redo
var oldSelection = this.editor.getSelection(); var oldSelection = this.editor.getDomSelection();
var previous = this.previousField || ''; var previous = this.previousField || '';
if (oldSelection.range) { if (oldSelection.range) {
var undoDiff = util.textDiff(this.field, previous); var undoDiff = util.textDiff(this.field, previous);
oldSelection.range.startOffset = undoDiff.start; oldSelection.range.startOffset = undoDiff.start;
oldSelection.range.endOffset = undoDiff.end; oldSelection.range.endOffset = undoDiff.end;
} }
var newSelection = this.editor.getSelection(); var newSelection = this.editor.getDomSelection();
if (newSelection.range) { if (newSelection.range) {
var redoDiff = util.textDiff(previous, this.field); var redoDiff = util.textDiff(previous, this.field);
newSelection.range.startOffset = redoDiff.start; newSelection.range.startOffset = redoDiff.start;
@ -13762,7 +13987,7 @@ return /******/ (function(modules) { // webpackBootstrap
var firstNode = nodes[0]; var firstNode = nodes[0];
var lastNode = nodes[nodes.length - 1]; var lastNode = nodes[nodes.length - 1];
var draggedNode = Node.getNodeFromTarget(event.target); var draggedNode = Node.getNodeFromTarget(event.target);
var beforeNode = lastNode._nextSibling(); var beforeNode = lastNode.nextSibling();
var editor = firstNode.editor; var editor = firstNode.editor;
// in case of multiple selected nodes, offsetY prevents the selection from // in case of multiple selected nodes, offsetY prevents the selection from
@ -13784,7 +14009,7 @@ return /******/ (function(modules) { // webpackBootstrap
editor.highlighter.lock(); editor.highlighter.lock();
editor.drag = { editor.drag = {
oldCursor: document.body.style.cursor, oldCursor: document.body.style.cursor,
oldSelection: editor.getSelection(), oldSelection: editor.getDomSelection(),
oldBeforeNode: beforeNode, oldBeforeNode: beforeNode,
mouseX: event.pageX, mouseX: event.pageX,
offsetY: offsetY, offsetY: offsetY,
@ -13905,7 +14130,7 @@ return /******/ (function(modules) { // webpackBootstrap
nodePrev = Node.getNodeFromTarget(trPrev); nodePrev = Node.getNodeFromTarget(trPrev);
var isDraggedNode = nodes.some(function (node) { var isDraggedNode = nodes.some(function (node) {
return node === nodePrev || nodePrev._isChildOf(node); return node === nodePrev || nodePrev.isDescendantOf(node);
}); });
if (isDraggedNode) { if (isDraggedNode) {
@ -13982,7 +14207,7 @@ return /******/ (function(modules) { // webpackBootstrap
var params = { var params = {
nodes: nodes, nodes: nodes,
oldSelection: editor.drag.oldSelection, oldSelection: editor.drag.oldSelection,
newSelection: editor.getSelection(), newSelection: editor.getDomSelection(),
oldBeforeNode: editor.drag.oldBeforeNode, oldBeforeNode: editor.drag.oldBeforeNode,
newBeforeNode: beforeNode newBeforeNode: beforeNode
}; };
@ -14017,12 +14242,12 @@ return /******/ (function(modules) { // webpackBootstrap
}; };
/** /**
* Test if this node is a child of an other node * Test if this node is a sescendant of an other node
* @param {Node} node * @param {Node} node
* @return {boolean} isChild * @return {boolean} isDescendant
* @private * @private
*/ */
Node.prototype._isChildOf = function (node) { Node.prototype.isDescendantOf = function (node) {
var n = this.parent; var n = this.parent;
while (n) { while (n) {
if (n == node) { if (n == node) {
@ -14532,7 +14757,7 @@ return /******/ (function(modules) { // webpackBootstrap
case 'keydown': case 'keydown':
case 'mousedown': case 'mousedown':
// TODO: cleanup // TODO: cleanup
this.editor.selection = this.editor.getSelection(); this.editor.selection = this.editor.getDomSelection();
break; break;
case 'click': case 'click':
@ -14583,7 +14808,7 @@ return /******/ (function(modules) { // webpackBootstrap
case 'keydown': case 'keydown':
case 'mousedown': case 'mousedown':
this.editor.selection = this.editor.getSelection(); this.editor.selection = this.editor.getDomSelection();
break; break;
case 'keyup': case 'keyup':
@ -14758,8 +14983,8 @@ return /******/ (function(modules) { // webpackBootstrap
if (nextNode && nextNode instanceof AppendNode && if (nextNode && nextNode instanceof AppendNode &&
!(lastNode.parent.childs.length == 1) && !(lastNode.parent.childs.length == 1) &&
nextNode2 && nextNode2.parent) { nextNode2 && nextNode2.parent) {
oldSelection = this.editor.getSelection(); oldSelection = this.editor.getDomSelection();
oldBeforeNode = lastNode._nextSibling(); oldBeforeNode = lastNode.nextSibling();
selectedNodes.forEach(function (node) { selectedNodes.forEach(function (node) {
nextNode2.parent.moveBefore(node, nextNode2); nextNode2.parent.moveBefore(node, nextNode2);
@ -14771,7 +14996,7 @@ return /******/ (function(modules) { // webpackBootstrap
oldBeforeNode: oldBeforeNode, oldBeforeNode: oldBeforeNode,
newBeforeNode: nextNode2, newBeforeNode: nextNode2,
oldSelection: oldSelection, oldSelection: oldSelection,
newSelection: this.editor.getSelection() newSelection: this.editor.getDomSelection()
}); });
} }
} }
@ -14805,8 +15030,8 @@ return /******/ (function(modules) { // webpackBootstrap
// find the previous node // find the previous node
prevNode = firstNode._previousNode(); prevNode = firstNode._previousNode();
if (prevNode && prevNode.parent) { if (prevNode && prevNode.parent) {
oldSelection = this.editor.getSelection(); oldSelection = this.editor.getDomSelection();
oldBeforeNode = lastNode._nextSibling(); oldBeforeNode = lastNode.nextSibling();
selectedNodes.forEach(function (node) { selectedNodes.forEach(function (node) {
prevNode.parent.moveBefore(node, prevNode); prevNode.parent.moveBefore(node, prevNode);
@ -14818,7 +15043,7 @@ return /******/ (function(modules) { // webpackBootstrap
oldBeforeNode: oldBeforeNode, oldBeforeNode: oldBeforeNode,
newBeforeNode: prevNode, newBeforeNode: prevNode,
oldSelection: oldSelection, oldSelection: oldSelection,
newSelection: this.editor.getSelection() newSelection: this.editor.getDomSelection()
}); });
} }
handled = true; handled = true;
@ -14841,8 +15066,8 @@ return /******/ (function(modules) { // webpackBootstrap
if (prevNode && prevNode.parent && if (prevNode && prevNode.parent &&
(prevNode instanceof AppendNode) (prevNode instanceof AppendNode)
&& !prevNode.isVisible()) { && !prevNode.isVisible()) {
oldSelection = this.editor.getSelection(); oldSelection = this.editor.getDomSelection();
oldBeforeNode = lastNode._nextSibling(); oldBeforeNode = lastNode.nextSibling();
selectedNodes.forEach(function (node) { selectedNodes.forEach(function (node) {
prevNode.parent.moveBefore(node, prevNode); prevNode.parent.moveBefore(node, prevNode);
@ -14854,7 +15079,7 @@ return /******/ (function(modules) { // webpackBootstrap
oldBeforeNode: oldBeforeNode, oldBeforeNode: oldBeforeNode,
newBeforeNode: prevNode, newBeforeNode: prevNode,
oldSelection: oldSelection, oldSelection: oldSelection,
newSelection: this.editor.getSelection() newSelection: this.editor.getDomSelection()
}); });
} }
} }
@ -14894,8 +15119,8 @@ return /******/ (function(modules) { // webpackBootstrap
} }
var nextNode2 = nextNode && (nextNode._nextNode() || nextNode.parent.append); var nextNode2 = nextNode && (nextNode._nextNode() || nextNode.parent.append);
if (nextNode2 && nextNode2.parent) { if (nextNode2 && nextNode2.parent) {
oldSelection = this.editor.getSelection(); oldSelection = this.editor.getDomSelection();
oldBeforeNode = lastNode._nextSibling(); oldBeforeNode = lastNode.nextSibling();
selectedNodes.forEach(function (node) { selectedNodes.forEach(function (node) {
nextNode2.parent.moveBefore(node, nextNode2); nextNode2.parent.moveBefore(node, nextNode2);
@ -14907,7 +15132,7 @@ return /******/ (function(modules) { // webpackBootstrap
oldBeforeNode: oldBeforeNode, oldBeforeNode: oldBeforeNode,
newBeforeNode: nextNode2, newBeforeNode: nextNode2,
oldSelection: oldSelection, oldSelection: oldSelection,
newSelection: this.editor.getSelection() newSelection: this.editor.getDomSelection()
}); });
} }
handled = true; handled = true;
@ -14965,9 +15190,9 @@ return /******/ (function(modules) { // webpackBootstrap
editor.highlighter.unhighlight(); editor.highlighter.unhighlight();
// adjust the focus // adjust the focus
var oldSelection = editor.getSelection(); var oldSelection = editor.getDomSelection();
Node.blurNodes(nodes); Node.blurNodes(nodes);
var newSelection = editor.getSelection(); var newSelection = editor.getDomSelection();
// remove the nodes // remove the nodes
nodes.forEach(function (node) { nodes.forEach(function (node) {
@ -15004,7 +15229,7 @@ return /******/ (function(modules) { // webpackBootstrap
editor.deselect(editor.multiselection.nodes); editor.deselect(editor.multiselection.nodes);
// duplicate the nodes // duplicate the nodes
var oldSelection = editor.getSelection(); var oldSelection = editor.getDomSelection();
var afterNode = lastNode; var afterNode = lastNode;
var clones = nodes.map(function (node) { var clones = nodes.map(function (node) {
var clone = node.clone(); var clone = node.clone();
@ -15020,7 +15245,7 @@ return /******/ (function(modules) { // webpackBootstrap
else { else {
editor.select(clones); editor.select(clones);
} }
var newSelection = editor.getSelection(); var newSelection = editor.getDomSelection();
editor._onAction('duplicateNodes', { editor._onAction('duplicateNodes', {
afterNode: lastNode, afterNode: lastNode,
@ -15040,7 +15265,7 @@ return /******/ (function(modules) { // webpackBootstrap
* @private * @private
*/ */
Node.prototype._onInsertBefore = function (field, value, type) { Node.prototype._onInsertBefore = function (field, value, type) {
var oldSelection = this.editor.getSelection(); var oldSelection = this.editor.getDomSelection();
var newNode = new Node(this.editor, { var newNode = new Node(this.editor, {
field: (field != undefined) ? field : '', field: (field != undefined) ? field : '',
@ -15051,7 +15276,7 @@ return /******/ (function(modules) { // webpackBootstrap
this.parent.insertBefore(newNode, this); this.parent.insertBefore(newNode, this);
this.editor.highlighter.unhighlight(); this.editor.highlighter.unhighlight();
newNode.focus('field'); newNode.focus('field');
var newSelection = this.editor.getSelection(); var newSelection = this.editor.getDomSelection();
this.editor._onAction('insertBeforeNodes', { this.editor._onAction('insertBeforeNodes', {
nodes: [newNode], nodes: [newNode],
@ -15070,7 +15295,7 @@ return /******/ (function(modules) { // webpackBootstrap
* @private * @private
*/ */
Node.prototype._onInsertAfter = function (field, value, type) { Node.prototype._onInsertAfter = function (field, value, type) {
var oldSelection = this.editor.getSelection(); var oldSelection = this.editor.getDomSelection();
var newNode = new Node(this.editor, { var newNode = new Node(this.editor, {
field: (field != undefined) ? field : '', field: (field != undefined) ? field : '',
@ -15081,7 +15306,7 @@ return /******/ (function(modules) { // webpackBootstrap
this.parent.insertAfter(newNode, this); this.parent.insertAfter(newNode, this);
this.editor.highlighter.unhighlight(); this.editor.highlighter.unhighlight();
newNode.focus('field'); newNode.focus('field');
var newSelection = this.editor.getSelection(); var newSelection = this.editor.getDomSelection();
this.editor._onAction('insertAfterNodes', { this.editor._onAction('insertAfterNodes', {
nodes: [newNode], nodes: [newNode],
@ -15100,7 +15325,7 @@ return /******/ (function(modules) { // webpackBootstrap
* @private * @private
*/ */
Node.prototype._onAppend = function (field, value, type) { Node.prototype._onAppend = function (field, value, type) {
var oldSelection = this.editor.getSelection(); var oldSelection = this.editor.getDomSelection();
var newNode = new Node(this.editor, { var newNode = new Node(this.editor, {
field: (field != undefined) ? field : '', field: (field != undefined) ? field : '',
@ -15111,7 +15336,7 @@ return /******/ (function(modules) { // webpackBootstrap
this.parent.appendChild(newNode); this.parent.appendChild(newNode);
this.editor.highlighter.unhighlight(); this.editor.highlighter.unhighlight();
newNode.focus('field'); newNode.focus('field');
var newSelection = this.editor.getSelection(); var newSelection = this.editor.getDomSelection();
this.editor._onAction('appendNodes', { this.editor._onAction('appendNodes', {
nodes: [newNode], nodes: [newNode],
@ -15129,9 +15354,9 @@ return /******/ (function(modules) { // webpackBootstrap
Node.prototype._onChangeType = function (newType) { Node.prototype._onChangeType = function (newType) {
var oldType = this.type; var oldType = this.type;
if (newType != oldType) { if (newType != oldType) {
var oldSelection = this.editor.getSelection(); var oldSelection = this.editor.getDomSelection();
this.changeType(newType); this.changeType(newType);
var newSelection = this.editor.getSelection(); var newSelection = this.editor.getDomSelection();
this.editor._onAction('changeType', { this.editor._onAction('changeType', {
node: this, node: this,
@ -15239,9 +15464,8 @@ return /******/ (function(modules) { // webpackBootstrap
/** /**
* Get the next sibling of current node * Get the next sibling of current node
* @return {Node} nextSibling * @return {Node} nextSibling
* @private
*/ */
Node.prototype._nextSibling = function () { Node.prototype.nextSibling = function () {
var index = this.parent.childs.indexOf(this); var index = this.parent.childs.indexOf(this);
return this.parent.childs[index + 1] || this.parent.append; return this.parent.childs[index + 1] || this.parent.append;
}; };
@ -15249,7 +15473,6 @@ return /******/ (function(modules) { // webpackBootstrap
/** /**
* Get the previously rendered node * Get the previously rendered node
* @return {Node | null} previousNode * @return {Node | null} previousNode
* @private
*/ */
Node.prototype._previousNode = function () { Node.prototype._previousNode = function () {
var prevNode = null; var prevNode = null;
@ -16648,6 +16871,8 @@ return /******/ (function(modules) { // webpackBootstrap
* {boolean} escapeUnicode If true, unicode * {boolean} escapeUnicode If true, unicode
* characters are escaped. * characters are escaped.
* false by default. * false by default.
* {function} onTextSelectionChange Callback method,
* triggered on text selection change
* @private * @private
*/ */
textmode.create = function (container, options) { textmode.create = function (container, options) {
@ -16693,6 +16918,10 @@ return /******/ (function(modules) { // webpackBootstrap
} }
} }
if (options.onTextSelectionChange) {
this.onTextSelectionChange(options.onTextSelectionChange);
}
var me = this; var me = this;
this.container = container; this.container = container;
this.dom = {}; this.dom = {};
@ -16949,9 +17178,8 @@ return /******/ (function(modules) { // webpackBootstrap
* @private * @private
*/ */
textmode._onSelect = function () { textmode._onSelect = function () {
if(this.options.statusBar) { this._updateCursorInfo();
this._updateCursorInfoDisplay(); this._emitSelectionChange();
}
}; };
/** /**
@ -16980,7 +17208,8 @@ return /******/ (function(modules) { // webpackBootstrap
event.stopPropagation(); event.stopPropagation();
} }
this._updateCursorInfoDisplay(); this._updateCursorInfo();
this._emitSelectionChange();
}; };
/** /**
@ -16989,7 +17218,8 @@ return /******/ (function(modules) { // webpackBootstrap
* @private * @private
*/ */
textmode._onMouseDown = function (event) { textmode._onMouseDown = function (event) {
this._updateCursorInfoDisplay(); this._updateCursorInfo();
this._emitSelectionChange();
}; };
/** /**
@ -16998,35 +17228,59 @@ return /******/ (function(modules) { // webpackBootstrap
* @private * @private
*/ */
textmode._onBlur = function (event) { textmode._onBlur = function (event) {
this._updateCursorInfoDisplay(); this._updateCursorInfo();
this._emitSelectionChange();
}; };
/** /**
* Update the status bar cursor info * Update the cursor info and the status bar, if presented
*/ */
textmode._updateCursorInfoDisplay = function () { textmode._updateCursorInfo = function () {
var me = this; var me = this;
var line, col, count; var line, col, count;
if(this.options.statusBar) { if (this.textarea) {
if (this.textarea) { setTimeout(function() { //this to verify we get the most updated textarea cursor selection
setTimeout(function() { //this to verify we get the most updated textarea cursor selection var selectionRange = util.getInputSelection(me.textarea);
var selectionRange = util.getInputSelection(me.textarea);
line = selectionRange.row; if (selectionRange.startIndex !== selectionRange.endIndex) {
col = selectionRange.col; count = selectionRange.endIndex - selectionRange.startIndex;
if (selectionRange.start !== selectionRange.end) { }
count = selectionRange.end - selectionRange.start;
} if (count && me.cursorInfo && me.cursorInfo.line === selectionRange.end.row && me.cursorInfo.column === selectionRange.end.column) {
line = selectionRange.start.row;
col = selectionRange.start.column;
} else {
line = selectionRange.end.row;
col = selectionRange.end.column;
}
me.cursorInfo = {
line: line,
column: col,
count: count
}
if(me.options.statusBar) {
updateDisplay(); updateDisplay();
},0); }
},0);
} else if (this.aceEditor && this.curserInfoElements) { } else if (this.aceEditor && this.curserInfoElements) {
var curserPos = this.aceEditor.getCursorPosition(); var curserPos = this.aceEditor.getCursorPosition();
var selectedText = this.aceEditor.getSelectedText(); var selectedText = this.aceEditor.getSelectedText();
line = curserPos.row + 1; line = curserPos.row + 1;
col = curserPos.column + 1; col = curserPos.column + 1;
count = selectedText.length; count = selectedText.length;
me.cursorInfo = {
line: line,
column: col,
count: count
}
if(this.options.statusBar) {
updateDisplay(); updateDisplay();
} }
} }
@ -17043,6 +17297,17 @@ return /******/ (function(modules) { // webpackBootstrap
} }
}; };
/**
* emits selection change callback, if given
* @private
*/
textmode._emitSelectionChange = function () {
if(this._selectionChangedHandler) {
var currentSelection = this.getTextSelection();
this._selectionChangedHandler(currentSelection.start, currentSelection.end, currentSelection.text);
}
}
/** /**
* Destroy the editor. Clean up DOM, event listeners, and web workers. * Destroy the editor. Clean up DOM, event listeners, and web workers.
*/ */
@ -17268,6 +17533,112 @@ return /******/ (function(modules) { // webpackBootstrap
} }
}; };
/**
* Get the selection details
* @returns {{start:{row:Number, column:Number},end:{row:Number, column:Number},text:String}}
*/
textmode.getTextSelection = function () {
var selection = {};
if (this.textarea) {
var selectionRange = util.getInputSelection(this.textarea);
if (this.cursorInfo && this.cursorInfo.line === selectionRange.end.row && this.cursorInfo.column === selectionRange.end.column) {
//selection direction is bottom => up
selection.start = selectionRange.end;
selection.end = selectionRange.start;
} else {
selection = selectionRange;
}
return {
start: selection.start,
end: selection.end,
text: this.textarea.value.substring(selectionRange.startIndex, selectionRange.endIndex)
}
}
if (this.aceEditor) {
var aceSelection = this.aceEditor.getSelection();
var selectedText = this.aceEditor.getSelectedText();
var range = aceSelection.getRange();
var lead = aceSelection.getSelectionLead();
if (lead.row === range.end.row && lead.column === range.end.column) {
selection = range;
} else {
//selection direction is bottom => up
selection.start = range.end;
selection.end = range.start;
}
return {
start: {
row: selection.start.row + 1,
column: selection.start.column + 1
},
end: {
row: selection.end.row + 1,
column: selection.end.column + 1
},
text: selectedText
};
}
};
/**
* Callback registraion for selection change
* @param {selectionCallback} callback
*
* @callback selectionCallback
* @param {{row:Number, column:Number}} startPos selection start position
* @param {{row:Number, column:Number}} endPos selected end position
* @param {String} text selected text
*/
textmode.onTextSelectionChange = function (callback) {
if (typeof callback === 'function') {
this._selectionChangedHandler = util.debounce(callback, this.DEBOUNCE_INTERVAL);
}
};
/**
* Set selection on editor's text
* @param {{row:Number, column:Number}} startPos selection start position
* @param {{row:Number, column:Number}} endPos selected end position
*/
textmode.setTextSelection = function (startPos, endPos) {
if (!startPos || !endPos) return;
if (this.textarea) {
var startIndex = util.getIndexForPosition(this.textarea, startPos.row, startPos.column);
var endIndex = util.getIndexForPosition(this.textarea, endPos.row, endPos.column);
if (startIndex > -1 && endIndex > -1) {
if (this.textarea.setSelectionRange) {
this.textarea.focus();
this.textarea.setSelectionRange(startIndex, endIndex);
} else if (this.textarea.createTextRange) { // IE < 9
var range = this.textarea.createTextRange();
range.collapse(true);
range.moveEnd('character', endIndex);
range.moveStart('character', startIndex);
range.select();
}
}
} else if (this.aceEditor) {
var range = {
start:{
row: startPos.row - 1,
column: startPos.column - 1
},
end:{
row: endPos.row - 1,
column: endPos.column - 1
}
};
this.aceEditor.selection.setRange(range);
}
};
// define modes // define modes
module.exports = [ module.exports = [
{ {

2
dist/jsoneditor.map vendored

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

2
package-lock.json generated
View File

@ -1,6 +1,6 @@
{ {
"name": "jsoneditor", "name": "jsoneditor",
"version": "5.14.1", "version": "5.15.0",
"lockfileVersion": 1, "lockfileVersion": 1,
"requires": true, "requires": true,
"dependencies": { "dependencies": {

View File

@ -1,6 +1,6 @@
{ {
"name": "jsoneditor", "name": "jsoneditor",
"version": "5.14.1", "version": "5.15.0",
"main": "./index", "main": "./index",
"description": "A web-based tool to view, edit, format, and validate JSON", "description": "A web-based tool to view, edit, format, and validate JSON",
"tags": [ "tags": [