Moved the search functionality to a separate prototype SearchBox
This commit is contained in:
parent
bd45e9bf60
commit
eeda3673cd
|
@ -4,7 +4,7 @@ http://jsoneditoronline.org
|
|||
|
||||
<not yet released>, version 1.2.0
|
||||
|
||||
- added a search box. Search results are expanded and highlighed.
|
||||
- added search functionality. Search results are expanded and highlighed.
|
||||
- the position of the vertical separator between left and right panel is stored.
|
||||
- link to the sourcecode on github added at the bottom of the page.
|
||||
- refinements in the layout: fonts, colors, icons.
|
||||
|
|
45
demo.html
45
demo.html
|
@ -1,39 +1,40 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<link rel="stylesheet" type="text/css" href="jsoneditor.css">
|
||||
<script type="text/javascript" src="jsoneditor-min.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="jsoneditor.css">
|
||||
<script type="text/javascript" src="jsoneditor-min.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<p>
|
||||
<p>
|
||||
<button onclick="setJSON();">Set JSON</button>
|
||||
<button onclick="getJSON();">Get JSON</button>
|
||||
</p>
|
||||
<div id="jsoneditor" style="width: 400px; height: 400px;"></div>
|
||||
|
||||
<script type="text/javascript" >
|
||||
// create the editor
|
||||
</p>
|
||||
<div id="jsoneditor" style="width: 500px; height: 400px;"></div>
|
||||
|
||||
<script type="text/javascript" >
|
||||
// create the editor. Optionally, parameters json and options can be
|
||||
// specified in the constructor.
|
||||
var container = document.getElementById("jsoneditor");
|
||||
var editor = new JSONEditor(container);
|
||||
|
||||
|
||||
// set json
|
||||
function setJSON () {
|
||||
var json = {
|
||||
"Array": [1, 2, 3],
|
||||
"Boolean": true,
|
||||
"Null": null,
|
||||
"Number": 123,
|
||||
"Object": {"a": "b", "c": "d"},
|
||||
"String": "Hello World"
|
||||
};
|
||||
editor.set(json);
|
||||
var json = {
|
||||
"Array": [1, 2, 3],
|
||||
"Boolean": true,
|
||||
"Null": null,
|
||||
"Number": 123,
|
||||
"Object": {"a": "b", "c": "d"},
|
||||
"String": "Hello World"
|
||||
};
|
||||
editor.set(json);
|
||||
}
|
||||
|
||||
|
||||
// get json
|
||||
function getJSON() {
|
||||
var json = editor.get();
|
||||
alert(JSON.stringify(json, null, 2));
|
||||
var json = editor.get();
|
||||
alert(JSON.stringify(json, null, 2));
|
||||
}
|
||||
</script>
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
337
jsoneditor.js
337
jsoneditor.js
|
@ -66,6 +66,7 @@ JSONEditor = function (container, json, options) {
|
|||
throw new Error('No container element provided.');
|
||||
}
|
||||
this.container = container;
|
||||
this.dom = {};
|
||||
|
||||
|
||||
this._setOptions(options);
|
||||
|
@ -167,7 +168,7 @@ JSONEditor.prototype._setRoot = function (node) {
|
|||
* The nodes will be expanded when the text is found one of its childs,
|
||||
* else it will be collapsed. Searches are case insensitive.
|
||||
* @param {String} text
|
||||
* @return {Number} results Number of search results
|
||||
* @return {Node[]} results Array with nodes containing the search text
|
||||
*/
|
||||
JSONEditor.prototype.search = function (text) {
|
||||
var results;
|
||||
|
@ -177,7 +178,7 @@ JSONEditor.prototype.search = function (text) {
|
|||
this.content.appendChild(this.table); // Put the table online again
|
||||
}
|
||||
else {
|
||||
results = 0;
|
||||
results = [];
|
||||
}
|
||||
|
||||
return results;
|
||||
|
@ -646,9 +647,10 @@ JSONEditor.Node.prototype.insertBefore = function(node, beforeNode) {
|
|||
* The node will be expanded when the text is found one of its childs, else
|
||||
* it will be collapsed. Searches are case insensitive.
|
||||
* @param {String} text
|
||||
* @return {Number} results Number of search results
|
||||
* @return {Node[]} results Array with nodes containing the search text
|
||||
*/
|
||||
JSONEditor.Node.prototype.search = function(text) {
|
||||
var results = [];
|
||||
var index;
|
||||
var search = text ? text.toLowerCase() : undefined;
|
||||
|
||||
|
@ -662,6 +664,7 @@ JSONEditor.Node.prototype.search = function(text) {
|
|||
index = field.indexOf(search);
|
||||
if (index != -1) {
|
||||
this.searchField = true;
|
||||
results.push(this);
|
||||
}
|
||||
|
||||
// update dom
|
||||
|
@ -669,22 +672,23 @@ JSONEditor.Node.prototype.search = function(text) {
|
|||
}
|
||||
|
||||
// search in value
|
||||
var childResults = 0;
|
||||
if (this.type == 'array' || this.type == 'object') {
|
||||
// array, object
|
||||
|
||||
// search the nodes childs
|
||||
var childs = this.childs;
|
||||
if (childs) {
|
||||
var childResults = [];
|
||||
for (var i = 0, iMax = childs.length; i < iMax; i++) {
|
||||
childResults += childs[i].search(text);
|
||||
childResults = childResults.concat(childs[i].search(text));
|
||||
}
|
||||
results = results.concat(childResults);
|
||||
}
|
||||
|
||||
// update dom
|
||||
if (search != undefined) {
|
||||
var recurse = false;
|
||||
if (childResults == 0) {
|
||||
if (childResults.length == 0) {
|
||||
this.collapse(recurse);
|
||||
}
|
||||
else {
|
||||
|
@ -699,6 +703,7 @@ JSONEditor.Node.prototype.search = function(text) {
|
|||
index = value.indexOf(search);
|
||||
if (index != -1) {
|
||||
this.searchValue = true;
|
||||
results.push(this);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -706,7 +711,6 @@ JSONEditor.Node.prototype.search = function(text) {
|
|||
this._updateDomValue();
|
||||
}
|
||||
|
||||
var results = (this.searchField || this.searchValue ? 1 : 0) + childResults;
|
||||
return results;
|
||||
};
|
||||
|
||||
|
@ -2137,8 +2141,47 @@ 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.prototype.onEvent
|
||||
var onEvent = function (event) {
|
||||
event = event || window.event;
|
||||
|
||||
/* TODO: check for Ctrl+F and F3? or not?
|
||||
if (editor.options.enableSearch) {
|
||||
if (event.type == 'keydown') {
|
||||
var keynum = event.which || event.keyCode;
|
||||
if (keynum == 70 && event.ctrlKey) { // Ctrl+F
|
||||
console.log('Ctrl+F');
|
||||
if (editor.dom.search) {
|
||||
editor.dom.search.focus();
|
||||
editor.dom.search.select();
|
||||
JSONEditor.Events.preventDefault(event);
|
||||
}
|
||||
}
|
||||
else if (keynum == 114) { // F3
|
||||
if (!event.shiftKey) {
|
||||
// select next search result
|
||||
editor.searchResultIndex ++;
|
||||
if (editor.searchResultIndex >= editor.searchResults.length) {
|
||||
editor.searchResultIndex = 0;
|
||||
}
|
||||
}
|
||||
else {
|
||||
// select previous search result
|
||||
editor.searchResultIndex--;
|
||||
if (editor.searchResultIndex < 0) {
|
||||
editor.searchResultIndex = editor.searchResults.length - 1;
|
||||
}
|
||||
}
|
||||
if (editor.searchResultIndex < editor.searchResults.length) {
|
||||
editor.searchResults[editor.searchResultIndex].focus();
|
||||
}
|
||||
|
||||
JSONEditor.Events.preventDefault(event);
|
||||
}
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
var target = event.target || event.srcElement;
|
||||
var node = JSONEditor.getNodeFromTarget(target);
|
||||
if (node) {
|
||||
|
@ -2152,6 +2195,7 @@ JSONEditor.prototype._createFrame = function () {
|
|||
JSONEditor.Events.preventDefault(event);
|
||||
};
|
||||
this.frame.onchange = onEvent;
|
||||
this.frame.onkeydown = onEvent;
|
||||
this.frame.onkeyup = onEvent;
|
||||
this.frame.oncut = onEvent;
|
||||
this.frame.onpaste = onEvent;
|
||||
|
@ -2197,7 +2241,7 @@ JSONEditor.prototype._createFrame = function () {
|
|||
td.appendChild(collapseAll);
|
||||
|
||||
if (this.options.enableSearch) {
|
||||
this._createSearchBox(td);
|
||||
this.searchBox = new JSONEditor.SearchBox(this, td);
|
||||
}
|
||||
|
||||
this.frame.appendChild(this.head);
|
||||
|
@ -2252,119 +2296,6 @@ JSONEditor.prototype._createTable = function () {
|
|||
this.frame.appendChild(contentOuter);
|
||||
};
|
||||
|
||||
/**
|
||||
* Create a search box in the menu
|
||||
* @param {Element} container HTML container element of where to create the
|
||||
* search box
|
||||
* @private
|
||||
*/
|
||||
JSONEditor.prototype._createSearchBox = function(container) {
|
||||
var table = document.createElement('table');
|
||||
table.className = 'jsoneditor-search';
|
||||
container.appendChild(table);
|
||||
var tbody = document.createElement('tbody');
|
||||
table.appendChild(tbody);
|
||||
var tr = document.createElement('tr');
|
||||
tbody.appendChild(tr);
|
||||
|
||||
var td = document.createElement('td');
|
||||
td.className = 'jsoneditor-search';
|
||||
tr.appendChild(td);
|
||||
var searchResults = document.createElement('div');
|
||||
searchResults.className = 'jsoneditor-search-results';
|
||||
td.appendChild(searchResults);
|
||||
|
||||
td = document.createElement('td');
|
||||
td.className = 'jsoneditor-search';
|
||||
tr.appendChild(td);
|
||||
var divInput = document.createElement('div');
|
||||
divInput.className = 'jsoneditor-search';
|
||||
divInput.title = 'Search fields and values';
|
||||
td.appendChild(divInput);
|
||||
|
||||
var lastText = undefined;
|
||||
var editor = this;
|
||||
function onSearch(event, forceSearch) {
|
||||
clearDelay();
|
||||
|
||||
var value = search.value;
|
||||
var text = (value.length > 0) ? value : undefined;
|
||||
if (text != lastText || forceSearch) {
|
||||
// only search again when changed
|
||||
lastText = text;
|
||||
var results = editor.search(text);
|
||||
|
||||
// display search results
|
||||
if (text != undefined) {
|
||||
switch (results) {
|
||||
case 0: searchResults.innerHTML = 'no results'; break;
|
||||
case 1: searchResults.innerHTML = '1 result'; break;
|
||||
default: searchResults.innerHTML = results + ' results'; break;
|
||||
}
|
||||
}
|
||||
else {
|
||||
searchResults.innerHTML = '';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var timeout = undefined;
|
||||
var delay = 200; // ms
|
||||
function clearDelay() {
|
||||
if (timeout != undefined) {
|
||||
clearTimeout(timeout);
|
||||
}
|
||||
}
|
||||
|
||||
function onDelayedSearch (event) {
|
||||
// execute the search after a short delay (reduces the number of
|
||||
// search actions while typing in the search text box)
|
||||
clearDelay();
|
||||
timeout = setTimeout(onSearch, delay);
|
||||
}
|
||||
|
||||
// table to contain the text input and search button
|
||||
var tableInput = document.createElement('table');
|
||||
tableInput.className = 'jsoneditor-search-input';
|
||||
divInput.appendChild(tableInput);
|
||||
var tbodySearch = document.createElement('tbody');
|
||||
tableInput.appendChild(tbodySearch);
|
||||
tr = document.createElement('tr');
|
||||
tbodySearch.appendChild(tr);
|
||||
|
||||
var search = document.createElement('input');
|
||||
search.className = 'jsoneditor-search';
|
||||
search.oninput = onDelayedSearch;
|
||||
search.onchange = onSearch; // For IE 8
|
||||
search.onkeyup = function (event) {
|
||||
event = event || window.event;
|
||||
var keynum = event.which || event.keyCode;
|
||||
if (keynum == 27) { // ESC
|
||||
search.value = ''; // clear search
|
||||
onSearch(event);
|
||||
}
|
||||
else if (keynum == 13) { // Enter
|
||||
onSearch(event, true); // force to execute search again
|
||||
}
|
||||
else {
|
||||
onDelayedSearch(event); // For IE 8
|
||||
}
|
||||
};
|
||||
// TODO: ESC in FF restores the last input, is a FF bug, https://bugzilla.mozilla.org/show_bug.cgi?id=598819
|
||||
td = document.createElement('td');
|
||||
tr.appendChild(td);
|
||||
td.appendChild(search);
|
||||
|
||||
var refreshSearch = document.createElement('button');
|
||||
refreshSearch.className = 'jsoneditor-search-refresh';
|
||||
refreshSearch.onclick = function (event) {
|
||||
onSearch(event, true);
|
||||
};
|
||||
td = document.createElement('td');
|
||||
tr.appendChild(td);
|
||||
td.appendChild(refreshSearch);
|
||||
};
|
||||
|
||||
/**
|
||||
* Find the node from an event target
|
||||
* @param {Element} target
|
||||
|
@ -2542,6 +2473,170 @@ JSONFormatter.prototype.get = function() {
|
|||
*/
|
||||
|
||||
|
||||
/**
|
||||
* @constructor JSONEditor.SearchBox
|
||||
* Create a search box in given HTML container
|
||||
* @param {Element} container HTML container element of where to create the
|
||||
* search box
|
||||
*/
|
||||
// TODO: put the SearchBox in a separate prototype
|
||||
JSONEditor.SearchBox = function(editor, container) {
|
||||
var searchBox = this;
|
||||
|
||||
this.editor = editor;
|
||||
this.timeout = undefined;
|
||||
this.delay = 200; // ms
|
||||
this.lastText = undefined;
|
||||
|
||||
this.dom = {};
|
||||
this.dom.container = container;
|
||||
|
||||
var table = document.createElement('table');
|
||||
this.dom.table = table;
|
||||
table.className = 'jsoneditor-search';
|
||||
container.appendChild(table);
|
||||
var tbody = document.createElement('tbody');
|
||||
this.dom.tbody = tbody;
|
||||
table.appendChild(tbody);
|
||||
var tr = document.createElement('tr');
|
||||
tbody.appendChild(tr);
|
||||
|
||||
var td = document.createElement('td');
|
||||
td.className = 'jsoneditor-search';
|
||||
tr.appendChild(td);
|
||||
var results = document.createElement('div');
|
||||
this.dom.results = results;
|
||||
results.className = 'jsoneditor-search-results';
|
||||
td.appendChild(results);
|
||||
|
||||
td = document.createElement('td');
|
||||
td.className = 'jsoneditor-search';
|
||||
tr.appendChild(td);
|
||||
var divInput = document.createElement('div');
|
||||
this.dom.input = divInput;
|
||||
divInput.className = 'jsoneditor-search';
|
||||
divInput.title = 'Search fields and values';
|
||||
td.appendChild(divInput);
|
||||
|
||||
// table to contain the text input and search button
|
||||
var tableInput = document.createElement('table');
|
||||
tableInput.className = 'jsoneditor-search-input';
|
||||
divInput.appendChild(tableInput);
|
||||
var tbodySearch = document.createElement('tbody');
|
||||
tableInput.appendChild(tbodySearch);
|
||||
tr = document.createElement('tr');
|
||||
tbodySearch.appendChild(tr);
|
||||
|
||||
var search = document.createElement('input');
|
||||
this.dom.search = search;
|
||||
search.className = 'jsoneditor-search';
|
||||
search.oninput = function (event) {
|
||||
searchBox.onDelayedSearch(event);
|
||||
};
|
||||
search.onchange = function (event) { // For IE 8
|
||||
searchBox.onSearch(event);
|
||||
};
|
||||
search.onkeyup = function (event) {
|
||||
searchBox.onKeyUp(event);
|
||||
};
|
||||
|
||||
// TODO: ESC in FF restores the last input, is a FF bug, https://bugzilla.mozilla.org/show_bug.cgi?id=598819
|
||||
td = document.createElement('td');
|
||||
tr.appendChild(td);
|
||||
td.appendChild(search);
|
||||
|
||||
var refreshSearch = document.createElement('button');
|
||||
refreshSearch.className = 'jsoneditor-search-refresh';
|
||||
refreshSearch.onclick = function (event) {
|
||||
searchBox.onSearch(event, true);
|
||||
};
|
||||
td = document.createElement('td');
|
||||
tr.appendChild(td);
|
||||
td.appendChild(refreshSearch);
|
||||
};
|
||||
|
||||
/**
|
||||
* Cancel any running onDelayedSearch.
|
||||
*/
|
||||
JSONEditor.SearchBox.prototype.clearDelay = function() {
|
||||
if (this.timeout != undefined) {
|
||||
clearTimeout(this.timeout);
|
||||
delete this.timeout;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Start a timer to execute a search after a short delay.
|
||||
* Used for reducing the number of searches while typing.
|
||||
* @param {Event} 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();
|
||||
var searchBox = this;
|
||||
this.timeout = setTimeout(function (event) {
|
||||
searchBox.onSearch(event);},
|
||||
this.delay);
|
||||
};
|
||||
|
||||
/**
|
||||
* Handle onSearch event
|
||||
* @param {Event} event
|
||||
* @param {boolean} [forceSearch] If true, search will be executed again even
|
||||
* when the search text is not changed.
|
||||
* Default is false.
|
||||
*/
|
||||
JSONEditor.SearchBox.prototype.onSearch = function (event, forceSearch) {
|
||||
this.clearDelay();
|
||||
|
||||
var value = this.dom.search.value;
|
||||
var text = (value.length > 0) ? value : undefined;
|
||||
if (text != this.lastText || forceSearch) {
|
||||
// only search again when changed
|
||||
this.lastText = text;
|
||||
this.results = editor.search(text);
|
||||
this.resultIndex = 0;
|
||||
|
||||
// display search results
|
||||
if (text != undefined) {
|
||||
var resultCount = this.results.length;
|
||||
switch (resultCount) {
|
||||
case 0: this.dom.results.innerHTML = 'no results'; break;
|
||||
case 1: this.dom.results.innerHTML = '1 result'; break;
|
||||
default: this.dom.results.innerHTML = resultCount + ' results'; break;
|
||||
}
|
||||
/* TODO: do we want this?
|
||||
if (resultCount > 0) {
|
||||
editor.results[editor.searchResultIndex].focus();
|
||||
}
|
||||
*/
|
||||
}
|
||||
else {
|
||||
this.dom.results.innerHTML = '';
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Handle onKeyUp event in the input box
|
||||
* @param {Event} event
|
||||
*/
|
||||
JSONEditor.SearchBox.prototype.onKeyUp = 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);
|
||||
}
|
||||
else if (keynum == 13) { // Enter
|
||||
this.onSearch(event, true); // force to execute search again
|
||||
}
|
||||
else {
|
||||
this.onDelayedSearch(event); // For IE 8
|
||||
}
|
||||
};
|
||||
|
||||
// create namespace for event methods
|
||||
JSONEditor.Events = {};
|
||||
|
||||
|
|
|
@ -106,8 +106,10 @@
|
|||
}
|
||||
|
||||
/**
|
||||
* 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 {Number} [level] Nesting level. This value is automatically
|
||||
* set when the method is called recursively
|
||||
* @return {String} innerText
|
||||
*/
|
||||
function getInnerText (element, level) {
|
||||
|
|
Loading…
Reference in New Issue