diff --git a/jsoneditor/js/appendnode.js b/jsoneditor/js/appendnode.js index 60b31ac..023cd7c 100644 --- a/jsoneditor/js/appendnode.js +++ b/jsoneditor/js/appendnode.js @@ -182,7 +182,6 @@ jsoneditor.AppendNode.prototype.showContextMenu = function (onClose) { 'className': 'jsoneditor-type-string', 'title': titles.string, 'click': function () { - // TODO: settings type string does not work, will become auto node._onAppend('field', 'value', 'string'); } } diff --git a/jsoneditor/js/history.js b/jsoneditor/js/history.js index 5311032..5103873 100644 --- a/jsoneditor/js/history.js +++ b/jsoneditor/js/history.js @@ -32,92 +32,92 @@ jsoneditor.History = function (editor) { // map with all supported actions this.actions = { 'editField': { - 'undo': function (obj) { - obj.params.node.updateField(obj.params.oldValue); + 'undo': function (params) { + params.node.updateField(params.oldValue); }, - 'redo': function (obj) { - obj.params.node.updateField(obj.params.newValue); + 'redo': function (params) { + params.node.updateField(params.newValue); } }, 'editValue': { - 'undo': function (obj) { - obj.params.node.updateValue(obj.params.oldValue); + 'undo': function (params) { + params.node.updateValue(params.oldValue); }, - 'redo': function (obj) { - obj.params.node.updateValue(obj.params.newValue); + 'redo': function (params) { + params.node.updateValue(params.newValue); } }, 'appendNode': { - 'undo': function (obj) { - obj.params.parent.removeChild(obj.params.node); + 'undo': function (params) { + params.parent.removeChild(params.node); }, - 'redo': function (obj) { - obj.params.parent.appendChild(obj.params.node); + 'redo': function (params) { + params.parent.appendChild(params.node); } }, 'insertBeforeNode': { - 'undo': function (obj) { - obj.params.parent.removeChild(obj.params.node); + 'undo': function (params) { + params.parent.removeChild(params.node); }, - 'redo': function (obj) { - obj.params.parent.insertBefore(obj.params.node, obj.params.beforeNode); + 'redo': function (params) { + params.parent.insertBefore(params.node, params.beforeNode); } }, 'insertAfterNode': { - 'undo': function (obj) { - obj.params.parent.removeChild(obj.params.node); + 'undo': function (params) { + params.parent.removeChild(params.node); }, - 'redo': function (obj) { - obj.params.parent.insertAfter(obj.params.node, obj.params.afterNode); + 'redo': function (params) { + params.parent.insertAfter(params.node, params.afterNode); } }, 'removeNode': { - 'undo': function (obj) { - var parent = obj.params.parent; - var beforeNode = parent.childs[obj.params.index] || parent.append; - parent.insertBefore(obj.params.node, beforeNode); + 'undo': function (params) { + var parent = params.parent; + var beforeNode = parent.childs[params.index] || parent.append; + parent.insertBefore(params.node, beforeNode); }, - 'redo': function (obj) { - obj.params.parent.removeChild(obj.params.node); + 'redo': function (params) { + params.parent.removeChild(params.node); } }, 'duplicateNode': { - 'undo': function (obj) { - obj.params.parent.removeChild(obj.params.clone); + 'undo': function (params) { + params.parent.removeChild(params.clone); }, - 'redo': function (obj) { - obj.params.parent.insertAfter(obj.params.clone, obj.params.node); + 'redo': function (params) { + params.parent.insertAfter(params.clone, params.node); } }, 'changeType': { - 'undo': function (obj) { - obj.params.node.changeType(obj.params.oldType); + 'undo': function (params) { + params.node.changeType(params.oldType); }, - 'redo': function (obj) { - obj.params.node.changeType(obj.params.newType); + 'redo': function (params) { + params.node.changeType(params.newType); } }, 'moveNode': { - 'undo': function (obj) { - obj.params.startParent.moveTo(obj.params.node, obj.params.startIndex); + 'undo': function (params) { + params.startParent.moveTo(params.node, params.startIndex); }, - 'redo': function (obj) { - obj.params.endParent.moveTo(obj.params.node, obj.params.endIndex); + 'redo': function (params) { + params.endParent.moveTo(params.node, params.endIndex); } }, 'sort': { - 'undo': function (obj) { - var node = obj.params.node; + 'undo': function (params) { + var node = params.node; node.hideChilds(); - node.sort = obj.params.oldSort; - node.childs = obj.params.oldChilds; + node.sort = params.oldSort; + node.childs = params.oldChilds; node.showChilds(); }, - 'redo': function (obj) { - var node = obj.params.node; + 'redo': function (params) { + var node = params.node; node.hideChilds(); - node.sort = obj.params.newSort; - node.childs = obj.params.newChilds; + node.sort = params.newSort; + node.childs = params.newChilds; node.showChilds(); } } @@ -197,7 +197,10 @@ jsoneditor.History.prototype.undo = function () { if (obj) { var action = this.actions[obj.action]; if (action && action.undo) { - action.undo(obj); + action.undo(obj.params); + if (obj.params.oldSelection) { + this.editor.setSelection(obj.params.oldSelection); + } } else { console.log('Error: unknown action "' + obj.action + '"'); @@ -219,15 +222,16 @@ jsoneditor.History.prototype.redo = function () { var obj = this.history[this.index]; if (obj) { - if (obj) { - var action = this.actions[obj.action]; - if (action && action.redo) { - action.redo(obj); - } - else { - console.log('Error: unknown action "' + obj.action + '"'); + var action = this.actions[obj.action]; + if (action && action.redo) { + action.redo(obj.params); + if (obj.params.newSelection) { + this.editor.setSelection(obj.params.newSelection); } } + else { + console.log('Error: unknown action "' + obj.action + '"'); + } } // fire onchange event diff --git a/jsoneditor/js/jsoneditor.js b/jsoneditor/js/jsoneditor.js index bbd2cd3..9ff6b35 100644 --- a/jsoneditor/js/jsoneditor.js +++ b/jsoneditor/js/jsoneditor.js @@ -284,8 +284,9 @@ jsoneditor.JSONEditor.prototype.collapseAll = function () { * example for "editValue" the Node, old value, and new * value are provided). params contains all information * 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 if (this.history) { 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 - * which captures key events + * Set the focus to an element in the JSONEditor, set text selection, and + * 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.focus = function () { - /* - if (!this.dom.focus) { - this.dom.focus = document.createElement('input'); - this.dom.focus.className = 'jsoneditor-hidden-focus'; - - var editor = this; - this.dom.focus.onblur = function () { - // remove itself - if (editor.dom.focus) { - var focus = editor.dom.focus; - delete editor.dom.focus; - editor.frame.removeChild(focus); - } - }; - - // attach the hidden input box to the DOM - if (this.frame.firstChild) { - this.frame.insertBefore(this.dom.focus, this.frame.firstChild); - } - else { - this.frame.appendChild(this.dom.focus); - } +jsoneditor.JSONEditor.prototype.setSelection = function (selection) { + if (!selection) { + return; + } + + if ('scrollTop' in selection && this.content) { + // TODO: animated scroll + this.content.scrollTop = selection.scrollTop; + } + /* + if (selection.range) { + // FIXME: does not work after a DOM element is removed and restored again + jsoneditor.util.setSelection(selection.range); } - this.dom.focus.focus(); */ + if (selection.dom) { + selection.dom.focus(); + } +}; + +/** + * Get the current focus + * @return {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 + */ +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 * of the window height. * @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; if (content) { - // cancel any running animation var editor = this; + // cancel any running animation if (editor.animateTimeout) { clearTimeout(editor.animateTimeout); delete editor.animateTimeout; } + if (editor.animateCallback) { + editor.animateCallback(false); + delete editor.animateCallback; + } // calculate final scroll position var height = content.clientHeight; @@ -417,11 +438,26 @@ jsoneditor.JSONEditor.prototype.scrollTo = function (top) { var diff = (finalScrollTop - scrollTop); if (Math.abs(diff) > 3) { content.scrollTop += diff / 3; + editor.animateCallback = callback; editor.animateTimeout = setTimeout(animate, 50); } + else { + // finished + if (callback) { + callback(true); + } + content.scrollTop = finalScrollTop; + delete editor.animateTimeout; + delete editor.animateCallback; + } }; 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 var editor = this; - // TODO: move this onEvent to jsoneditor.JSONEditor.prototype.onEvent var onEvent = function (event) { - event = event || window.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); - } + editor._onEvent(event); }; this.frame.onclick = function (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 * @param {Event} event + * @private */ -jsoneditor.JSONEditor.prototype.onKeyDown = function (event) { +jsoneditor.JSONEditor.prototype._onKeyDown = function (event) { var keynum = event.which || event.keyCode; var ctrlKey = event.ctrlKey; var shiftKey = event.shiftKey; 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 (ctrlKey && keynum == 70) { // Ctrl+F this.searchBox.dom.search.focus(); @@ -597,7 +654,7 @@ jsoneditor.JSONEditor.prototype.onKeyDown = function (event) { } // set selection to the current - this.searchBox.focusActiveResult(); + this.searchBox._focusActiveResult(); handled = true; } diff --git a/jsoneditor/js/node.js b/jsoneditor/js/node.js index 75a4bd5..d4f6df9 100644 --- a/jsoneditor/js/node.js +++ b/jsoneditor/js/node.js @@ -587,8 +587,9 @@ jsoneditor.Node.prototype.search = function(text) { /** * Move the scroll position such that this node is in the visible area. * 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 the node is not visible, expand its parents var parent = this.parent; @@ -600,12 +601,12 @@ jsoneditor.Node.prototype.scrollTo = function() { } 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 * available values: 'field', 'value' */ @@ -615,12 +616,14 @@ jsoneditor.Node.prototype.focus = function(field) { var domField = this.dom.field; if (domField) { domField.focus(); + jsoneditor.util.selectContentEditable(domField); } } else { var domValue = this.dom.value; if (domValue) { domValue.focus(); + jsoneditor.util.selectContentEditable(domValue); } } } @@ -888,7 +891,7 @@ jsoneditor.Node.prototype._getDomValue = function(silent) { if (value !== this.value) { var oldValue = this.value; this.value = value; - this.editor.onAction('editValue', { + this.editor._onAction('editValue', { 'node': this, 'oldValue': oldValue, 'newValue': value @@ -1025,7 +1028,7 @@ jsoneditor.Node.prototype._getDomField = function(silent) { if (field !== this.field) { var oldField = this.field; this.field = field; - this.editor.onAction('editField', { + this.editor._onAction('editField', { 'node': this, 'oldValue': oldField, 'newValue': field @@ -1303,7 +1306,7 @@ jsoneditor.Node.prototype._onDragEnd = function (event) { if ((params.startParent != params.endParent) || (params.startIndex != params.endIndex)) { // 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; @@ -1646,7 +1649,6 @@ jsoneditor.Node.prototype.onEvent = function (event) { this.editor.highlighter.highlight(this); } else if (type == 'mouseout') { - // TODO: onmouseout of menu must only execute unhighlight when no contextmenu is visible this.editor.highlighter.unhighlight(); } } @@ -1796,23 +1798,18 @@ jsoneditor.Node.prototype.onKeyDown = function (event) { this._onDuplicate(); handled = true; } - /* TODO: implement shortcut keys - else if (ctrlKey && keynum == 46) { // Ctrl+Del - this._onRemove(); - handled = true; - // TODO: focus to the next node - } - else if (ctrlKey && !shiftKey && keynum == 45) { // Ctrl+Ins - this._onInsertBefore(); // Ctrl+Ins - handled = true; - // TODO: focus to the next node - } - else if (ctrlKey && shiftKey && keynum == 45) { // Ctrl+Shift+Ins - this._onInsertAfter(); - handled = true; - // TODO: focus to the next node - } - */ + else if (ctrlKey && keynum == 46) { // Ctrl+Del + this._onRemove(); + handled = true; + } + else if (ctrlKey && !shiftKey && keynum == 45) { // Ctrl+Ins + this._onInsertBefore(); // Ctrl+Ins + handled = true; + } + else if (ctrlKey && shiftKey && keynum == 45) { // Ctrl+Shift+Ins + this._onInsertAfter(); + handled = true; + } if (handled) { jsoneditor.util.preventDefault(event); @@ -1886,14 +1883,32 @@ jsoneditor.Node.types = [ */ jsoneditor.Node.prototype._onRemove = function() { 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.editor.onAction('removeNode', { + // store history action + this.editor._onAction('removeNode', { 'node': this, 'parent': this.parent, - 'index': index + 'index': index, + 'oldSelection': oldSelection, + 'newSelection': newSelection }); }; @@ -1902,12 +1917,17 @@ jsoneditor.Node.prototype._onRemove = function() { * @private */ jsoneditor.Node.prototype._onDuplicate = function() { + var oldSelection = this.editor.getSelection(); var clone = this.parent._duplicate(this); + clone.focus(); + var newSelection = this.editor.getSelection(); - this.editor.onAction('duplicateNode', { + this.editor._onAction('duplicateNode', { 'node': this, 'clone': clone, - 'parent': this.parent + 'parent': this.parent, + 'oldSelection': oldSelection, + 'newSelection': newSelection }); }; @@ -1919,6 +1939,8 @@ jsoneditor.Node.prototype._onDuplicate = function() { * @private */ jsoneditor.Node.prototype._onInsertBefore = function (field, value, type) { + var oldSelection = this.editor.getSelection(); + var newNode = new jsoneditor.Node(this.editor, { 'field': (value != undefined) ? field : 'field', 'value': (value != undefined) ? value : 'value', @@ -1928,11 +1950,14 @@ jsoneditor.Node.prototype._onInsertBefore = function (field, value, type) { this.parent.insertBefore(newNode, this); this.editor.highlighter.unhighlight(); newNode.focus(); + var newSelection = this.editor.getSelection(); - this.editor.onAction('insertBeforeNode', { + this.editor._onAction('insertBeforeNode', { 'node': newNode, '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 */ jsoneditor.Node.prototype._onInsertAfter = function (field, value, type) { + var oldSelection = this.editor.getSelection(); + var newNode = new jsoneditor.Node(this.editor, { 'field': (value != undefined) ? field : 'field', 'value': (value != undefined) ? value : 'value', @@ -1953,11 +1980,14 @@ jsoneditor.Node.prototype._onInsertAfter = function (field, value, type) { this.parent.insertAfter(newNode, this); this.editor.highlighter.unhighlight(); newNode.focus(); + var newSelection = this.editor.getSelection(); - this.editor.onAction('insertAfterNode', { + this.editor._onAction('insertAfterNode', { 'node': newNode, '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 */ jsoneditor.Node.prototype._onAppend = function (field, value, type) { + var oldSelection = this.editor.getSelection(); + var newNode = new jsoneditor.Node(this.editor, { 'field': (value != undefined) ? field : 'field', 'value': (value != undefined) ? value : 'value', @@ -1978,10 +2010,13 @@ jsoneditor.Node.prototype._onAppend = function (field, value, type) { this.parent.appendChild(newNode); this.editor.highlighter.unhighlight(); newNode.focus(); + var newSelection = this.editor.getSelection(); - this.editor.onAction('appendNode', { + this.editor._onAction('appendNode', { '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) { var oldType = this.type; if (newType != oldType) { + var oldSelection = this.editor.getSelection(); this.changeType(newType); + var newSelection = this.editor.getSelection(); - this.editor.onAction('changeType', { + this.editor._onAction('changeType', { 'node': this, '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.editor.onAction('sort', { + this.editor._onAction('sort', { 'node': this, 'oldChilds': oldChilds, 'oldSort': oldSort, @@ -2057,6 +2096,7 @@ jsoneditor.Node.prototype.getAppend = function () { * Find the node from an event target * @param {Node} target * @return {jsoneditor.Node | undefined} node or undefined when not found + * @static */ jsoneditor.Node.getNodeFromTarget = function (target) { while (target) { @@ -2093,7 +2133,6 @@ jsoneditor.Node.prototype.showContextMenu = function (onClose) { var titles = jsoneditor.Node.TYPE_TITLES; var items = []; - // TODO: add titles for all context menu items items.push({ 'text': 'Type', 'title': 'Change the type of this field', @@ -2215,7 +2254,6 @@ jsoneditor.Node.prototype.showContextMenu = function (onClose) { 'className': 'jsoneditor-type-string', 'title': titles.string, 'click': function () { - // TODO: settings type string does not work, will become auto node._onAppend('field', 'value', 'string'); } } diff --git a/jsoneditor/js/searchbox.js b/jsoneditor/js/searchbox.js index c2a5838..3bfd500 100644 --- a/jsoneditor/js/searchbox.js +++ b/jsoneditor/js/searchbox.js @@ -84,16 +84,16 @@ jsoneditor.SearchBox = function(editor, container) { this.dom.search = search; search.className = 'jsoneditor-search'; search.oninput = function (event) { - searchBox.onDelayedSearch(event); + searchBox._onDelayedSearch(event); }; search.onchange = function (event) { // For IE 8 - searchBox.onSearch(event); + searchBox._onSearch(event); }; search.onkeydown = function (event) { - searchBox.onKeyDown(event); + searchBox._onKeyDown(event); }; search.onkeyup = function (event) { - searchBox.onKeyUp(event); + searchBox._onKeyUp(event); }; refreshSearch.onclick = function (event) { search.select(); @@ -135,7 +135,7 @@ jsoneditor.SearchBox.prototype.next = function() { if (index > this.results.length - 1) { index = 0; } - this.setActiveResult(index); + this._setActiveResult(index); } }; @@ -149,15 +149,16 @@ jsoneditor.SearchBox.prototype.previous = function() { if (index < 0) { index = max; } - this.setActiveResult(index); + this._setActiveResult(index); } }; /** * Set new value for the current active result * @param {Number} index + * @private */ -jsoneditor.SearchBox.prototype.setActiveResult = function(index) { +jsoneditor.SearchBox.prototype._setActiveResult = function(index) { // de-activate current active result if (this.activeResult) { 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 * active result, the next search result will get focus + * @private */ -jsoneditor.SearchBox.prototype.focusActiveResult = function() { +jsoneditor.SearchBox.prototype._focusActiveResult = function() { if (!this.activeResult) { this.next(); } @@ -211,8 +213,9 @@ jsoneditor.SearchBox.prototype.focusActiveResult = function() { /** * Cancel any running onDelayedSearch. + * @private */ -jsoneditor.SearchBox.prototype.clearDelay = function() { +jsoneditor.SearchBox.prototype._clearDelay = function() { if (this.timeout != undefined) { clearTimeout(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. * Used for reducing the number of searches while typing. * @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 // search actions while typing in the search text box) - this.clearDelay(); + this._clearDelay(); var searchBox = this; this.timeout = setTimeout(function (event) { - searchBox.onSearch(event); + searchBox._onSearch(event); }, this.delay); }; @@ -241,9 +245,10 @@ jsoneditor.SearchBox.prototype.onDelayedSearch = function (event) { * @param {boolean} [forceSearch] If true, search will be executed again even * when the search text is not changed. * Default is false. + * @private */ -jsoneditor.SearchBox.prototype.onSearch = function (event, forceSearch) { - this.clearDelay(); +jsoneditor.SearchBox.prototype._onSearch = function (event, forceSearch) { + this._clearDelay(); var value = this.dom.search.value; var text = (value.length > 0) ? value : undefined; @@ -251,7 +256,7 @@ jsoneditor.SearchBox.prototype.onSearch = function (event, forceSearch) { // only search again when changed this.lastText = text; this.results = this.editor.search(text); - this.setActiveResult(undefined); + this._setActiveResult(undefined); // display search results if (text != undefined) { @@ -271,20 +276,21 @@ jsoneditor.SearchBox.prototype.onSearch = function (event, forceSearch) { /** * Handle onKeyDown event in the input box * @param {Event} event + * @private */ -jsoneditor.SearchBox.prototype.onKeyDown = function (event) { +jsoneditor.SearchBox.prototype._onKeyDown = function (event) { event = event || window.event; var keynum = event.which || event.keyCode; if (keynum == 27) { // ESC this.dom.search.value = ''; // clear search - this.onSearch(event); + this._onSearch(event); jsoneditor.util.preventDefault(event); jsoneditor.util.stopPropagation(event); } else if (keynum == 13) { // Enter if (event.ctrlKey) { // force to search again - this.onSearch(event, true); + this._onSearch(event, true); } else if (event.shiftKey) { // move to the previous search result @@ -302,11 +308,12 @@ jsoneditor.SearchBox.prototype.onKeyDown = function (event) { /** * Handle onKeyUp event in the input box * @param {Event} event + * @private */ -jsoneditor.SearchBox.prototype.onKeyUp = function (event) { +jsoneditor.SearchBox.prototype._onKeyUp = function (event) { event = event || window.event; var keynum = event.which || event.keyCode; if (keynum != 27 && keynum != 13) { // !show and !Enter - this.onDelayedSearch(event); // For IE 8 + this._onDelayedSearch(event); // For IE 8 } }; diff --git a/jsoneditor/js/util.js b/jsoneditor/js/util.js index a644791..236bf9e 100644 --- a/jsoneditor/js/util.js +++ b/jsoneditor/js/util.js @@ -247,7 +247,7 @@ jsoneditor.util.stripFormatting = function (divElement) { * code from Nico Burns * http://stackoverflow.com/users/140293/nico-burns * 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) { 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) * @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 * @return {Number} Internet Explorer version, or -1 in case of an other browser */ -var _ieVersion = undefined; jsoneditor.util.getInternetExplorerVersion = function() { - if (_ieVersion == undefined) { + if (_ieVersion == -1) { var rv = -1; // Return value assumes failure. if (navigator.appName == 'Microsoft Internet Explorer') { @@ -363,6 +420,13 @@ jsoneditor.util.getInternetExplorerVersion = function() { return _ieVersion; }; +/** + * cached internet explorer version + * @type {Number} + * @private + */ +var _ieVersion = -1; + /** * Add and event listener. Works for all browsers * @param {Element} element An html element