- Implemented restoring focus on undo/redo.

- Implemented more shortcutkeys.
- Implemented selection of all text on tab and after insert/append.
This commit is contained in:
josdejong 2013-01-06 21:12:37 +01:00
parent a27423a2f2
commit 01a593ffed
6 changed files with 331 additions and 162 deletions

View File

@ -182,7 +182,6 @@ jsoneditor.AppendNode.prototype.showContextMenu = function (onClose) {
'className': 'jsoneditor-type-string', 'className': 'jsoneditor-type-string',
'title': titles.string, 'title': titles.string,
'click': function () { 'click': function () {
// TODO: settings type string does not work, will become auto
node._onAppend('field', 'value', 'string'); node._onAppend('field', 'value', 'string');
} }
} }

View File

@ -32,92 +32,92 @@ jsoneditor.History = function (editor) {
// map with all supported actions // map with all supported actions
this.actions = { this.actions = {
'editField': { 'editField': {
'undo': function (obj) { 'undo': function (params) {
obj.params.node.updateField(obj.params.oldValue); params.node.updateField(params.oldValue);
}, },
'redo': function (obj) { 'redo': function (params) {
obj.params.node.updateField(obj.params.newValue); params.node.updateField(params.newValue);
} }
}, },
'editValue': { 'editValue': {
'undo': function (obj) { 'undo': function (params) {
obj.params.node.updateValue(obj.params.oldValue); params.node.updateValue(params.oldValue);
}, },
'redo': function (obj) { 'redo': function (params) {
obj.params.node.updateValue(obj.params.newValue); params.node.updateValue(params.newValue);
} }
}, },
'appendNode': { 'appendNode': {
'undo': function (obj) { 'undo': function (params) {
obj.params.parent.removeChild(obj.params.node); params.parent.removeChild(params.node);
}, },
'redo': function (obj) { 'redo': function (params) {
obj.params.parent.appendChild(obj.params.node); params.parent.appendChild(params.node);
} }
}, },
'insertBeforeNode': { 'insertBeforeNode': {
'undo': function (obj) { 'undo': function (params) {
obj.params.parent.removeChild(obj.params.node); params.parent.removeChild(params.node);
}, },
'redo': function (obj) { 'redo': function (params) {
obj.params.parent.insertBefore(obj.params.node, obj.params.beforeNode); params.parent.insertBefore(params.node, params.beforeNode);
} }
}, },
'insertAfterNode': { 'insertAfterNode': {
'undo': function (obj) { 'undo': function (params) {
obj.params.parent.removeChild(obj.params.node); params.parent.removeChild(params.node);
}, },
'redo': function (obj) { 'redo': function (params) {
obj.params.parent.insertAfter(obj.params.node, obj.params.afterNode); params.parent.insertAfter(params.node, params.afterNode);
} }
}, },
'removeNode': { 'removeNode': {
'undo': function (obj) { 'undo': function (params) {
var parent = obj.params.parent; var parent = params.parent;
var beforeNode = parent.childs[obj.params.index] || parent.append; var beforeNode = parent.childs[params.index] || parent.append;
parent.insertBefore(obj.params.node, beforeNode); parent.insertBefore(params.node, beforeNode);
}, },
'redo': function (obj) { 'redo': function (params) {
obj.params.parent.removeChild(obj.params.node); params.parent.removeChild(params.node);
} }
}, },
'duplicateNode': { 'duplicateNode': {
'undo': function (obj) { 'undo': function (params) {
obj.params.parent.removeChild(obj.params.clone); params.parent.removeChild(params.clone);
}, },
'redo': function (obj) { 'redo': function (params) {
obj.params.parent.insertAfter(obj.params.clone, obj.params.node); params.parent.insertAfter(params.clone, params.node);
} }
}, },
'changeType': { 'changeType': {
'undo': function (obj) { 'undo': function (params) {
obj.params.node.changeType(obj.params.oldType); params.node.changeType(params.oldType);
}, },
'redo': function (obj) { 'redo': function (params) {
obj.params.node.changeType(obj.params.newType); params.node.changeType(params.newType);
} }
}, },
'moveNode': { 'moveNode': {
'undo': function (obj) { 'undo': function (params) {
obj.params.startParent.moveTo(obj.params.node, obj.params.startIndex); params.startParent.moveTo(params.node, params.startIndex);
}, },
'redo': function (obj) { 'redo': function (params) {
obj.params.endParent.moveTo(obj.params.node, obj.params.endIndex); params.endParent.moveTo(params.node, params.endIndex);
} }
}, },
'sort': { 'sort': {
'undo': function (obj) { 'undo': function (params) {
var node = obj.params.node; var node = params.node;
node.hideChilds(); node.hideChilds();
node.sort = obj.params.oldSort; node.sort = params.oldSort;
node.childs = obj.params.oldChilds; node.childs = params.oldChilds;
node.showChilds(); node.showChilds();
}, },
'redo': function (obj) { 'redo': function (params) {
var node = obj.params.node; var node = params.node;
node.hideChilds(); node.hideChilds();
node.sort = obj.params.newSort; node.sort = params.newSort;
node.childs = obj.params.newChilds; node.childs = params.newChilds;
node.showChilds(); node.showChilds();
} }
} }
@ -197,7 +197,10 @@ jsoneditor.History.prototype.undo = function () {
if (obj) { if (obj) {
var action = this.actions[obj.action]; var action = this.actions[obj.action];
if (action && action.undo) { if (action && action.undo) {
action.undo(obj); action.undo(obj.params);
if (obj.params.oldSelection) {
this.editor.setSelection(obj.params.oldSelection);
}
} }
else { else {
console.log('Error: unknown action "' + obj.action + '"'); console.log('Error: unknown action "' + obj.action + '"');
@ -218,17 +221,18 @@ jsoneditor.History.prototype.redo = function () {
this.index++; this.index++;
var obj = this.history[this.index]; var obj = this.history[this.index];
if (obj) {
if (obj) { if (obj) {
var action = this.actions[obj.action]; var action = this.actions[obj.action];
if (action && action.redo) { if (action && action.redo) {
action.redo(obj); action.redo(obj.params);
if (obj.params.newSelection) {
this.editor.setSelection(obj.params.newSelection);
}
} }
else { else {
console.log('Error: unknown action "' + obj.action + '"'); console.log('Error: unknown action "' + obj.action + '"');
} }
} }
}
// fire onchange event // fire onchange event
this.onChange(); this.onChange();

View File

@ -284,8 +284,9 @@ jsoneditor.JSONEditor.prototype.collapseAll = function () {
* example for "editValue" the Node, old value, and new * example for "editValue" the Node, old value, and new
* value are provided). params contains all information * value are provided). params contains all information
* needed to undo or redo the action. * needed to undo or redo the action.
* @private
*/ */
jsoneditor.JSONEditor.prototype.onAction = function (action, params) { jsoneditor.JSONEditor.prototype._onAction = function (action, params) {
// add an action to the history // add an action to the history
if (this.history) { if (this.history) {
this.history.add(action, params); this.history.add(action, params);
@ -359,52 +360,72 @@ jsoneditor.JSONEditor.prototype.stopAutoScroll = function () {
/** /**
* Set the focus to the JSONEditor. A hidden input field will be created * Set the focus to an element in the JSONEditor, set text selection, and
* which captures key events * set scroll position.
* @param {Object} selection An object containing fields:
* {Element | undefined} dom The dom element
* which has focus
* {Range | TextRange} range A text selection
* {Number} scrollTop Scroll position
*/ */
// TODO: use the focus method? jsoneditor.JSONEditor.prototype.setSelection = function (selection) {
jsoneditor.JSONEditor.prototype.focus = function () { if (!selection) {
/* return;
if (!this.dom.focus) { }
this.dom.focus = document.createElement('input');
this.dom.focus.className = 'jsoneditor-hidden-focus';
var editor = this; if ('scrollTop' in selection && this.content) {
this.dom.focus.onblur = function () { // TODO: animated scroll
// remove itself this.content.scrollTop = selection.scrollTop;
if (editor.dom.focus) { }
var focus = editor.dom.focus; /*
delete editor.dom.focus; if (selection.range) {
editor.frame.removeChild(focus); // FIXME: does not work after a DOM element is removed and restored again
jsoneditor.util.setSelection(selection.range);
}
*/
if (selection.dom) {
selection.dom.focus();
} }
}; };
// attach the hidden input box to the DOM /**
if (this.frame.firstChild) { * Get the current focus
this.frame.insertBefore(this.dom.focus, this.frame.firstChild); * @return {Object} selection An object containing fields:
} * {Element | undefined} dom The dom element
else { * which has focus
this.frame.appendChild(this.dom.focus); * {Range | TextRange} range A text selection
} * {Number} scrollTop Scroll position
}
this.dom.focus.focus();
*/ */
jsoneditor.JSONEditor.prototype.getSelection = function () {
return {
dom: jsoneditor.JSONEditor.domFocus,
scrollTop: this.content ? this.content.scrollTop : 0,
range: jsoneditor.util.getSelection()
};
}; };
/** /**
* Adjust the scroll position such that given top position is shown at 1/4 * Adjust the scroll position such that given top position is shown at 1/4
* of the window height. * of the window height.
* @param {Number} top * @param {Number} top
* @param {function(boolean)} [callback] Callback, executed when animation is
* finished. The callback returns true
* when animation is finished, or false
* when not.
*/ */
jsoneditor.JSONEditor.prototype.scrollTo = function (top) { jsoneditor.JSONEditor.prototype.scrollTo = function (top, callback) {
var content = this.content; var content = this.content;
if (content) { if (content) {
// cancel any running animation
var editor = this; var editor = this;
// cancel any running animation
if (editor.animateTimeout) { if (editor.animateTimeout) {
clearTimeout(editor.animateTimeout); clearTimeout(editor.animateTimeout);
delete editor.animateTimeout; delete editor.animateTimeout;
} }
if (editor.animateCallback) {
editor.animateCallback(false);
delete editor.animateCallback;
}
// calculate final scroll position // calculate final scroll position
var height = content.clientHeight; var height = content.clientHeight;
@ -417,11 +438,26 @@ jsoneditor.JSONEditor.prototype.scrollTo = function (top) {
var diff = (finalScrollTop - scrollTop); var diff = (finalScrollTop - scrollTop);
if (Math.abs(diff) > 3) { if (Math.abs(diff) > 3) {
content.scrollTop += diff / 3; content.scrollTop += diff / 3;
editor.animateCallback = callback;
editor.animateTimeout = setTimeout(animate, 50); editor.animateTimeout = setTimeout(animate, 50);
} }
else {
// finished
if (callback) {
callback(true);
}
content.scrollTop = finalScrollTop;
delete editor.animateTimeout;
delete editor.animateCallback;
}
}; };
animate(); animate();
} }
else {
if (callback) {
callback(false);
}
}
}; };
/** /**
@ -437,19 +473,8 @@ jsoneditor.JSONEditor.prototype._createFrame = function () {
// create one global event listener to handle all events from all nodes // create one global event listener to handle all events from all nodes
var editor = this; var editor = this;
// TODO: move this onEvent to jsoneditor.JSONEditor.prototype.onEvent
var onEvent = function (event) { var onEvent = function (event) {
event = event || window.event; editor._onEvent(event);
var target = event.target || event.srcElement;
if (event.type == 'keydown') {
editor.onKeyDown(event);
}
var node = jsoneditor.Node.getNodeFromTarget(target);
if (node) {
node.onEvent(event);
}
}; };
this.frame.onclick = function (event) { this.frame.onclick = function (event) {
onEvent(event); onEvent(event);
@ -570,16 +595,48 @@ jsoneditor.JSONEditor.prototype._onRedo = function () {
} }
}; };
/**
* Event handler
* @param event
* @private
*/
jsoneditor.JSONEditor.prototype._onEvent = function (event) {
event = event || window.event;
var target = event.target || event.srcElement;
if (event.type == 'keydown') {
this._onKeyDown(event);
}
if (event.type == 'focus') {
jsoneditor.JSONEditor.domFocus = target;
}
var node = jsoneditor.Node.getNodeFromTarget(target);
if (node) {
node.onEvent(event);
}
};
/** /**
* Event handler for keydown. Handles shortcut keys * Event handler for keydown. Handles shortcut keys
* @param {Event} event * @param {Event} event
* @private
*/ */
jsoneditor.JSONEditor.prototype.onKeyDown = function (event) { jsoneditor.JSONEditor.prototype._onKeyDown = function (event) {
var keynum = event.which || event.keyCode; var keynum = event.which || event.keyCode;
var ctrlKey = event.ctrlKey; var ctrlKey = event.ctrlKey;
var shiftKey = event.shiftKey; var shiftKey = event.shiftKey;
var handled = false; var handled = false;
if (keynum == 9) { // Tab
// FIXME: selecting all text on tab key does not work on IE8
setTimeout(function () {
// select all text when moving focus to an editable div
jsoneditor.util.selectContentEditable(jsoneditor.JSONEditor.domFocus);
}, 0);
}
if (this.searchBox) { if (this.searchBox) {
if (ctrlKey && keynum == 70) { // Ctrl+F if (ctrlKey && keynum == 70) { // Ctrl+F
this.searchBox.dom.search.focus(); this.searchBox.dom.search.focus();
@ -597,7 +654,7 @@ jsoneditor.JSONEditor.prototype.onKeyDown = function (event) {
} }
// set selection to the current // set selection to the current
this.searchBox.focusActiveResult(); this.searchBox._focusActiveResult();
handled = true; handled = true;
} }

View File

@ -587,8 +587,9 @@ jsoneditor.Node.prototype.search = function(text) {
/** /**
* Move the scroll position such that this node is in the visible area. * Move the scroll position such that this node is in the visible area.
* The node will not get the focus * The node will not get the focus
* @param {function(boolean)} [callback]
*/ */
jsoneditor.Node.prototype.scrollTo = function() { jsoneditor.Node.prototype.scrollTo = function(callback) {
if (!this.dom.tr || !this.dom.tr.parentNode) { if (!this.dom.tr || !this.dom.tr.parentNode) {
// if the node is not visible, expand its parents // if the node is not visible, expand its parents
var parent = this.parent; var parent = this.parent;
@ -600,12 +601,12 @@ jsoneditor.Node.prototype.scrollTo = function() {
} }
if (this.dom.tr && this.dom.tr.parentNode) { if (this.dom.tr && this.dom.tr.parentNode) {
this.editor.scrollTo(this.dom.tr.offsetTop); this.editor.scrollTo(this.dom.tr.offsetTop, callback);
} }
}; };
/** /**
* Set focus to the value of this node * Set focus to this node
* @param {String} [field] The field name of the element to get the focus * @param {String} [field] The field name of the element to get the focus
* available values: 'field', 'value' * available values: 'field', 'value'
*/ */
@ -615,12 +616,14 @@ jsoneditor.Node.prototype.focus = function(field) {
var domField = this.dom.field; var domField = this.dom.field;
if (domField) { if (domField) {
domField.focus(); domField.focus();
jsoneditor.util.selectContentEditable(domField);
} }
} }
else { else {
var domValue = this.dom.value; var domValue = this.dom.value;
if (domValue) { if (domValue) {
domValue.focus(); domValue.focus();
jsoneditor.util.selectContentEditable(domValue);
} }
} }
} }
@ -888,7 +891,7 @@ jsoneditor.Node.prototype._getDomValue = function(silent) {
if (value !== this.value) { if (value !== this.value) {
var oldValue = this.value; var oldValue = this.value;
this.value = value; this.value = value;
this.editor.onAction('editValue', { this.editor._onAction('editValue', {
'node': this, 'node': this,
'oldValue': oldValue, 'oldValue': oldValue,
'newValue': value 'newValue': value
@ -1025,7 +1028,7 @@ jsoneditor.Node.prototype._getDomField = function(silent) {
if (field !== this.field) { if (field !== this.field) {
var oldField = this.field; var oldField = this.field;
this.field = field; this.field = field;
this.editor.onAction('editField', { this.editor._onAction('editField', {
'node': this, 'node': this,
'oldValue': oldField, 'oldValue': oldField,
'newValue': field 'newValue': field
@ -1303,7 +1306,7 @@ jsoneditor.Node.prototype._onDragEnd = function (event) {
if ((params.startParent != params.endParent) || if ((params.startParent != params.endParent) ||
(params.startIndex != params.endIndex)) { (params.startIndex != params.endIndex)) {
// only register this action if the node is actually moved to another place // only register this action if the node is actually moved to another place
this.editor.onAction('moveNode', params); this.editor._onAction('moveNode', params);
} }
document.body.style.cursor = this.drag.oldCursor; document.body.style.cursor = this.drag.oldCursor;
@ -1646,7 +1649,6 @@ jsoneditor.Node.prototype.onEvent = function (event) {
this.editor.highlighter.highlight(this); this.editor.highlighter.highlight(this);
} }
else if (type == 'mouseout') { else if (type == 'mouseout') {
// TODO: onmouseout of menu must only execute unhighlight when no contextmenu is visible
this.editor.highlighter.unhighlight(); this.editor.highlighter.unhighlight();
} }
} }
@ -1796,23 +1798,18 @@ jsoneditor.Node.prototype.onKeyDown = function (event) {
this._onDuplicate(); this._onDuplicate();
handled = true; handled = true;
} }
/* TODO: implement shortcut keys
else if (ctrlKey && keynum == 46) { // Ctrl+Del else if (ctrlKey && keynum == 46) { // Ctrl+Del
this._onRemove(); this._onRemove();
handled = true; handled = true;
// TODO: focus to the next node
} }
else if (ctrlKey && !shiftKey && keynum == 45) { // Ctrl+Ins else if (ctrlKey && !shiftKey && keynum == 45) { // Ctrl+Ins
this._onInsertBefore(); // Ctrl+Ins this._onInsertBefore(); // Ctrl+Ins
handled = true; handled = true;
// TODO: focus to the next node
} }
else if (ctrlKey && shiftKey && keynum == 45) { // Ctrl+Shift+Ins else if (ctrlKey && shiftKey && keynum == 45) { // Ctrl+Shift+Ins
this._onInsertAfter(); this._onInsertAfter();
handled = true; handled = true;
// TODO: focus to the next node
} }
*/
if (handled) { if (handled) {
jsoneditor.util.preventDefault(event); jsoneditor.util.preventDefault(event);
@ -1886,14 +1883,32 @@ jsoneditor.Node.types = [
*/ */
jsoneditor.Node.prototype._onRemove = function() { jsoneditor.Node.prototype._onRemove = function() {
this.editor.highlighter.unhighlight(); this.editor.highlighter.unhighlight();
var index = this.parent.childs.indexOf(this); var childs = this.parent.childs;
var index = childs.indexOf(this);
// adjust the focus
var oldSelection = this.editor.getSelection();
if (childs[index + 1]) {
childs[index + 1].focus();
}
else if (childs[index - 1]) {
childs[index - 1].focus();
}
else {
this.parent.focus();
}
var newSelection = this.editor.getSelection();
// remove the node
this.parent._remove(this); this.parent._remove(this);
this.editor.onAction('removeNode', { // store history action
this.editor._onAction('removeNode', {
'node': this, 'node': this,
'parent': this.parent, 'parent': this.parent,
'index': index 'index': index,
'oldSelection': oldSelection,
'newSelection': newSelection
}); });
}; };
@ -1902,12 +1917,17 @@ jsoneditor.Node.prototype._onRemove = function() {
* @private * @private
*/ */
jsoneditor.Node.prototype._onDuplicate = function() { jsoneditor.Node.prototype._onDuplicate = function() {
var oldSelection = this.editor.getSelection();
var clone = this.parent._duplicate(this); var clone = this.parent._duplicate(this);
clone.focus();
var newSelection = this.editor.getSelection();
this.editor.onAction('duplicateNode', { this.editor._onAction('duplicateNode', {
'node': this, 'node': this,
'clone': clone, 'clone': clone,
'parent': this.parent 'parent': this.parent,
'oldSelection': oldSelection,
'newSelection': newSelection
}); });
}; };
@ -1919,6 +1939,8 @@ jsoneditor.Node.prototype._onDuplicate = function() {
* @private * @private
*/ */
jsoneditor.Node.prototype._onInsertBefore = function (field, value, type) { jsoneditor.Node.prototype._onInsertBefore = function (field, value, type) {
var oldSelection = this.editor.getSelection();
var newNode = new jsoneditor.Node(this.editor, { var newNode = new jsoneditor.Node(this.editor, {
'field': (value != undefined) ? field : 'field', 'field': (value != undefined) ? field : 'field',
'value': (value != undefined) ? value : 'value', 'value': (value != undefined) ? value : 'value',
@ -1928,11 +1950,14 @@ jsoneditor.Node.prototype._onInsertBefore = function (field, value, type) {
this.parent.insertBefore(newNode, this); this.parent.insertBefore(newNode, this);
this.editor.highlighter.unhighlight(); this.editor.highlighter.unhighlight();
newNode.focus(); newNode.focus();
var newSelection = this.editor.getSelection();
this.editor.onAction('insertBeforeNode', { this.editor._onAction('insertBeforeNode', {
'node': newNode, 'node': newNode,
'beforeNode': this, 'beforeNode': this,
'parent': this.parent 'parent': this.parent,
'oldSelection': oldSelection,
'newSelection': newSelection
}); });
}; };
@ -1944,6 +1969,8 @@ jsoneditor.Node.prototype._onInsertBefore = function (field, value, type) {
* @private * @private
*/ */
jsoneditor.Node.prototype._onInsertAfter = function (field, value, type) { jsoneditor.Node.prototype._onInsertAfter = function (field, value, type) {
var oldSelection = this.editor.getSelection();
var newNode = new jsoneditor.Node(this.editor, { var newNode = new jsoneditor.Node(this.editor, {
'field': (value != undefined) ? field : 'field', 'field': (value != undefined) ? field : 'field',
'value': (value != undefined) ? value : 'value', 'value': (value != undefined) ? value : 'value',
@ -1953,11 +1980,14 @@ jsoneditor.Node.prototype._onInsertAfter = function (field, value, type) {
this.parent.insertAfter(newNode, this); this.parent.insertAfter(newNode, this);
this.editor.highlighter.unhighlight(); this.editor.highlighter.unhighlight();
newNode.focus(); newNode.focus();
var newSelection = this.editor.getSelection();
this.editor.onAction('insertAfterNode', { this.editor._onAction('insertAfterNode', {
'node': newNode, 'node': newNode,
'afterNode': this, 'afterNode': this,
'parent': this.parent 'parent': this.parent,
'oldSelection': oldSelection,
'newSelection': newSelection
}); });
}; };
@ -1969,6 +1999,8 @@ jsoneditor.Node.prototype._onInsertAfter = function (field, value, type) {
* @private * @private
*/ */
jsoneditor.Node.prototype._onAppend = function (field, value, type) { jsoneditor.Node.prototype._onAppend = function (field, value, type) {
var oldSelection = this.editor.getSelection();
var newNode = new jsoneditor.Node(this.editor, { var newNode = new jsoneditor.Node(this.editor, {
'field': (value != undefined) ? field : 'field', 'field': (value != undefined) ? field : 'field',
'value': (value != undefined) ? value : 'value', 'value': (value != undefined) ? value : 'value',
@ -1978,10 +2010,13 @@ jsoneditor.Node.prototype._onAppend = function (field, value, type) {
this.parent.appendChild(newNode); this.parent.appendChild(newNode);
this.editor.highlighter.unhighlight(); this.editor.highlighter.unhighlight();
newNode.focus(); newNode.focus();
var newSelection = this.editor.getSelection();
this.editor.onAction('appendNode', { this.editor._onAction('appendNode', {
'node': newNode, 'node': newNode,
'parent': this.parent 'parent': this.parent,
'oldSelection': oldSelection,
'newSelection': newSelection
}); });
}; };
@ -1993,12 +2028,16 @@ jsoneditor.Node.prototype._onAppend = function (field, value, type) {
jsoneditor.Node.prototype._onChangeType = function (newType) { jsoneditor.Node.prototype._onChangeType = function (newType) {
var oldType = this.type; var oldType = this.type;
if (newType != oldType) { if (newType != oldType) {
var oldSelection = this.editor.getSelection();
this.changeType(newType); this.changeType(newType);
var newSelection = this.editor.getSelection();
this.editor.onAction('changeType', { this.editor._onAction('changeType', {
'node': this, 'node': this,
'oldType': oldType, 'oldType': oldType,
'newType': newType 'newType': newType,
'oldSelection': oldSelection,
'newSelection': newSelection
}); });
} }
}; };
@ -2029,7 +2068,7 @@ jsoneditor.Node.prototype._onSort = function (direction) {
}); });
this.sort = (order == 1) ? 'asc' : 'desc'; this.sort = (order == 1) ? 'asc' : 'desc';
this.editor.onAction('sort', { this.editor._onAction('sort', {
'node': this, 'node': this,
'oldChilds': oldChilds, 'oldChilds': oldChilds,
'oldSort': oldSort, 'oldSort': oldSort,
@ -2057,6 +2096,7 @@ jsoneditor.Node.prototype.getAppend = function () {
* Find the node from an event target * Find the node from an event target
* @param {Node} target * @param {Node} target
* @return {jsoneditor.Node | undefined} node or undefined when not found * @return {jsoneditor.Node | undefined} node or undefined when not found
* @static
*/ */
jsoneditor.Node.getNodeFromTarget = function (target) { jsoneditor.Node.getNodeFromTarget = function (target) {
while (target) { while (target) {
@ -2093,7 +2133,6 @@ jsoneditor.Node.prototype.showContextMenu = function (onClose) {
var titles = jsoneditor.Node.TYPE_TITLES; var titles = jsoneditor.Node.TYPE_TITLES;
var items = []; var items = [];
// TODO: add titles for all context menu items
items.push({ items.push({
'text': 'Type', 'text': 'Type',
'title': 'Change the type of this field', 'title': 'Change the type of this field',
@ -2215,7 +2254,6 @@ jsoneditor.Node.prototype.showContextMenu = function (onClose) {
'className': 'jsoneditor-type-string', 'className': 'jsoneditor-type-string',
'title': titles.string, 'title': titles.string,
'click': function () { 'click': function () {
// TODO: settings type string does not work, will become auto
node._onAppend('field', 'value', 'string'); node._onAppend('field', 'value', 'string');
} }
} }

View File

@ -84,16 +84,16 @@ jsoneditor.SearchBox = function(editor, container) {
this.dom.search = search; this.dom.search = search;
search.className = 'jsoneditor-search'; search.className = 'jsoneditor-search';
search.oninput = function (event) { search.oninput = function (event) {
searchBox.onDelayedSearch(event); searchBox._onDelayedSearch(event);
}; };
search.onchange = function (event) { // For IE 8 search.onchange = function (event) { // For IE 8
searchBox.onSearch(event); searchBox._onSearch(event);
}; };
search.onkeydown = function (event) { search.onkeydown = function (event) {
searchBox.onKeyDown(event); searchBox._onKeyDown(event);
}; };
search.onkeyup = function (event) { search.onkeyup = function (event) {
searchBox.onKeyUp(event); searchBox._onKeyUp(event);
}; };
refreshSearch.onclick = function (event) { refreshSearch.onclick = function (event) {
search.select(); search.select();
@ -135,7 +135,7 @@ jsoneditor.SearchBox.prototype.next = function() {
if (index > this.results.length - 1) { if (index > this.results.length - 1) {
index = 0; index = 0;
} }
this.setActiveResult(index); this._setActiveResult(index);
} }
}; };
@ -149,15 +149,16 @@ jsoneditor.SearchBox.prototype.previous = function() {
if (index < 0) { if (index < 0) {
index = max; index = max;
} }
this.setActiveResult(index); this._setActiveResult(index);
} }
}; };
/** /**
* Set new value for the current active result * Set new value for the current active result
* @param {Number} index * @param {Number} index
* @private
*/ */
jsoneditor.SearchBox.prototype.setActiveResult = function(index) { jsoneditor.SearchBox.prototype._setActiveResult = function(index) {
// de-activate current active result // de-activate current active result
if (this.activeResult) { if (this.activeResult) {
var prevNode = this.activeResult.node; var prevNode = this.activeResult.node;
@ -198,8 +199,9 @@ jsoneditor.SearchBox.prototype.setActiveResult = function(index) {
/** /**
* Set the focus to the currently active result. If there is no currently * Set the focus to the currently active result. If there is no currently
* active result, the next search result will get focus * active result, the next search result will get focus
* @private
*/ */
jsoneditor.SearchBox.prototype.focusActiveResult = function() { jsoneditor.SearchBox.prototype._focusActiveResult = function() {
if (!this.activeResult) { if (!this.activeResult) {
this.next(); this.next();
} }
@ -211,8 +213,9 @@ jsoneditor.SearchBox.prototype.focusActiveResult = function() {
/** /**
* Cancel any running onDelayedSearch. * Cancel any running onDelayedSearch.
* @private
*/ */
jsoneditor.SearchBox.prototype.clearDelay = function() { jsoneditor.SearchBox.prototype._clearDelay = function() {
if (this.timeout != undefined) { if (this.timeout != undefined) {
clearTimeout(this.timeout); clearTimeout(this.timeout);
delete this.timeout; delete this.timeout;
@ -223,14 +226,15 @@ jsoneditor.SearchBox.prototype.clearDelay = function() {
* Start a timer to execute a search after a short delay. * Start a timer to execute a search after a short delay.
* Used for reducing the number of searches while typing. * Used for reducing the number of searches while typing.
* @param {Event} event * @param {Event} event
* @private
*/ */
jsoneditor.SearchBox.prototype.onDelayedSearch = function (event) { jsoneditor.SearchBox.prototype._onDelayedSearch = function (event) {
// execute the search after a short delay (reduces the number of // execute the search after a short delay (reduces the number of
// search actions while typing in the search text box) // search actions while typing in the search text box)
this.clearDelay(); this._clearDelay();
var searchBox = this; var searchBox = this;
this.timeout = setTimeout(function (event) { this.timeout = setTimeout(function (event) {
searchBox.onSearch(event); searchBox._onSearch(event);
}, },
this.delay); this.delay);
}; };
@ -241,9 +245,10 @@ jsoneditor.SearchBox.prototype.onDelayedSearch = function (event) {
* @param {boolean} [forceSearch] If true, search will be executed again even * @param {boolean} [forceSearch] If true, search will be executed again even
* when the search text is not changed. * when the search text is not changed.
* Default is false. * Default is false.
* @private
*/ */
jsoneditor.SearchBox.prototype.onSearch = function (event, forceSearch) { jsoneditor.SearchBox.prototype._onSearch = function (event, forceSearch) {
this.clearDelay(); this._clearDelay();
var value = this.dom.search.value; var value = this.dom.search.value;
var text = (value.length > 0) ? value : undefined; var text = (value.length > 0) ? value : undefined;
@ -251,7 +256,7 @@ jsoneditor.SearchBox.prototype.onSearch = function (event, forceSearch) {
// only search again when changed // only search again when changed
this.lastText = text; this.lastText = text;
this.results = this.editor.search(text); this.results = this.editor.search(text);
this.setActiveResult(undefined); this._setActiveResult(undefined);
// display search results // display search results
if (text != undefined) { if (text != undefined) {
@ -271,20 +276,21 @@ jsoneditor.SearchBox.prototype.onSearch = function (event, forceSearch) {
/** /**
* Handle onKeyDown event in the input box * Handle onKeyDown event in the input box
* @param {Event} event * @param {Event} event
* @private
*/ */
jsoneditor.SearchBox.prototype.onKeyDown = function (event) { jsoneditor.SearchBox.prototype._onKeyDown = function (event) {
event = event || window.event; event = event || window.event;
var keynum = event.which || event.keyCode; var keynum = event.which || event.keyCode;
if (keynum == 27) { // ESC if (keynum == 27) { // ESC
this.dom.search.value = ''; // clear search this.dom.search.value = ''; // clear search
this.onSearch(event); this._onSearch(event);
jsoneditor.util.preventDefault(event); jsoneditor.util.preventDefault(event);
jsoneditor.util.stopPropagation(event); jsoneditor.util.stopPropagation(event);
} }
else if (keynum == 13) { // Enter else if (keynum == 13) { // Enter
if (event.ctrlKey) { if (event.ctrlKey) {
// force to search again // force to search again
this.onSearch(event, true); this._onSearch(event, true);
} }
else if (event.shiftKey) { else if (event.shiftKey) {
// move to the previous search result // move to the previous search result
@ -302,11 +308,12 @@ jsoneditor.SearchBox.prototype.onKeyDown = function (event) {
/** /**
* Handle onKeyUp event in the input box * Handle onKeyUp event in the input box
* @param {Event} event * @param {Event} event
* @private
*/ */
jsoneditor.SearchBox.prototype.onKeyUp = function (event) { jsoneditor.SearchBox.prototype._onKeyUp = function (event) {
event = event || window.event; event = event || window.event;
var keynum = event.which || event.keyCode; var keynum = event.which || event.keyCode;
if (keynum != 27 && keynum != 13) { // !show and !Enter if (keynum != 27 && keynum != 13) { // !show and !Enter
this.onDelayedSearch(event); // For IE 8 this._onDelayedSearch(event); // For IE 8
} }
}; };

View File

@ -247,7 +247,7 @@ jsoneditor.util.stripFormatting = function (divElement) {
* code from Nico Burns * code from Nico Burns
* http://stackoverflow.com/users/140293/nico-burns * http://stackoverflow.com/users/140293/nico-burns
* http://stackoverflow.com/questions/1125292/how-to-move-cursor-to-end-of-contenteditable-entity * http://stackoverflow.com/questions/1125292/how-to-move-cursor-to-end-of-contenteditable-entity
* @param {Element} contentEditableElement * @param {Element} contentEditableElement A content editable div
*/ */
jsoneditor.util.setEndOfContentEditable = function (contentEditableElement) { jsoneditor.util.setEndOfContentEditable = function (contentEditableElement) {
var range, selection; var range, selection;
@ -267,6 +267,64 @@ jsoneditor.util.setEndOfContentEditable = function (contentEditableElement) {
} }
}; };
/**
* Select all text of a content editable div.
* http://stackoverflow.com/a/3806004/1262753
* @param {Element} contentEditableElement A content editable div
*/
jsoneditor.util.selectContentEditable = function (contentEditableElement) {
if (!contentEditableElement || contentEditableElement.nodeName != 'DIV') {
return;
}
var sel, range;
if (window.getSelection && document.createRange) {
range = document.createRange();
range.selectNodeContents(contentEditableElement);
sel = window.getSelection();
sel.removeAllRanges();
sel.addRange(range);
} else if (document.body.createTextRange) {
range = document.body.createTextRange();
range.moveToElementText(contentEditableElement);
range.select();
}
};
/**
* Get text selection
* http://stackoverflow.com/questions/4687808/contenteditable-selected-text-save-and-restore
* @return {Range | TextRange | null} range
*/
jsoneditor.util.getSelection = function () {
if (window.getSelection) {
var sel = window.getSelection();
if (sel.getRangeAt && sel.rangeCount) {
return sel.getRangeAt(0);
}
} else if (document.selection && document.selection.createRange) {
return document.selection.createRange();
}
return null;
};
/**
* Set text selection
* http://stackoverflow.com/questions/4687808/contenteditable-selected-text-save-and-restore
* @param {Range | TextRange | null} range
*/
jsoneditor.util.setSelection = function (range) {
if (range) {
if (window.getSelection) {
var sel = window.getSelection();
sel.removeAllRanges();
sel.addRange(range);
} else if (document.selection && range.select) {
range.select();
}
}
};
/** /**
* Get the inner text of an HTML element (for example a div element) * Get the inner text of an HTML element (for example a div element)
* @param {Element} element * @param {Element} element
@ -344,9 +402,8 @@ jsoneditor.util.getInnerText = function (element, buffer) {
* Source: http://msdn.microsoft.com/en-us/library/ms537509(v=vs.85).aspx * Source: http://msdn.microsoft.com/en-us/library/ms537509(v=vs.85).aspx
* @return {Number} Internet Explorer version, or -1 in case of an other browser * @return {Number} Internet Explorer version, or -1 in case of an other browser
*/ */
var _ieVersion = undefined;
jsoneditor.util.getInternetExplorerVersion = function() { jsoneditor.util.getInternetExplorerVersion = function() {
if (_ieVersion == undefined) { if (_ieVersion == -1) {
var rv = -1; // Return value assumes failure. var rv = -1; // Return value assumes failure.
if (navigator.appName == 'Microsoft Internet Explorer') if (navigator.appName == 'Microsoft Internet Explorer')
{ {
@ -363,6 +420,13 @@ jsoneditor.util.getInternetExplorerVersion = function() {
return _ieVersion; return _ieVersion;
}; };
/**
* cached internet explorer version
* @type {Number}
* @private
*/
var _ieVersion = -1;
/** /**
* Add and event listener. Works for all browsers * Add and event listener. Works for all browsers
* @param {Element} element An html element * @param {Element} element An html element