Autocomplete integrated and 3 samples.

This commit is contained in:
Israel Garcia 2017-05-26 03:32:01 -04:00
parent 26a1a84602
commit ae287d48e9
6 changed files with 221 additions and 182 deletions

View File

@ -5,7 +5,7 @@
<link href="../dist/jsoneditor.css" rel="stylesheet" type="text/css">
<script src="../dist/jsoneditor.js"></script>
<script src="autocomplete.js"></script>
<style type="text/css">
#jsoneditor {
@ -35,18 +35,18 @@
<script>
// create the editor
var container = document.getElementById('jsoneditor');
var pv = autocomplete({
fontSize: '10pt',
fontFamily: 'droid sans mono, consolas, monospace, courier new, courier, sans-serif'
});
var options = {
modes: ['text', 'tree'],
autocomplete: {
ActivationChar: '',
ApplyTo:['values'],
Show: function (node, element) {
Config: {
ConfirmKeys: [39, 35, 9, 190] // Confirm Autocomplete Keys: [right, end, tab, '.'] // By default are only [right, end, tab]
},
ActivationChar: '*', // ActivationChar indicate if the value starts with this Char then autocompletion will be activated. ('' will be ignored).
ApplyTo:['value'],
GetOptions: function (autocomplete, node, text) {
var YaskON = {
// Return first level json paths by the node 'o'
stringify: function (o, prefix, activationChar) {
prefix = prefix || '';
switch (typeof o) {
@ -74,14 +74,14 @@
}
};
var data = {};
pv.startFrom = 0;
var text = element.innerText;
autocomplete.startFrom = 0;
var lastPoint = text.lastIndexOf('.');
if ((lastPoint > 0) && (text.length > 1)) {
var fnode = node.editor.node.findNode('.' + text.substring(this.ActivationChar.length, lastPoint));
if (fnode && typeof fnode.getValue() == 'object') {
data = fnode.getValue();
pv.startFrom = text.lastIndexOf('.') + 1;
// Indicate that autocompletion should start after the . (ignoring the first part)
autocomplete.startFrom = text.lastIndexOf('.') + 1;
}
}
else
@ -90,11 +90,7 @@
var optionsStr = YaskON.stringify(data, null, this.ActivationChar);
var options = optionsStr.split("\n");
if (options.length > 0)
pv.Show(element, options);
},
Hide: function () {
pv.hideDropDown();
return options;
}
}
};

View File

@ -1,151 +0,0 @@
<!DOCTYPE HTML>
<html>
<head>
<title>JSONEditor | Auto Complete</title>
<link href="../dist/jsoneditor.css" rel="stylesheet" type="text/css">
<link href="horsey/horsey.css" rel="stylesheet" type="text/css">
<script src="../dist/jsoneditor.js"></script>
<script src="horsey/horsey.js"></script>
<style type="text/css">
#jsoneditor {
width: 500px;
height: 500px;
}
p {
width: 500px;
font-family: "DejaVu Sans", sans-serif;
}
</style>
</head>
<body>
<p>
This example demonstrates how to use JSONEditor with Horsey autocomplete,
https://github.com/bevacqua/horsey
</p>
<div id="jsoneditor"></div>
<script>
// create the editor
var container = document.getElementById('jsoneditor');
var hy = null;
var startFrom = 0;
var activeOptions = [];
var options = {
modes: ['text', 'tree'],
autocomplete: {
ActivationChar: '',
ApplyTo:['values'],
Show: function (node, element) {
function setEndOfContenteditable(contentEditableElement) {
var range, selection;
if (document.createRange)//Firefox, Chrome, Opera, Safari, IE 9+
{
range = document.createRange();//Create a range (a range is a like the selection but invisible)
range.selectNodeContents(contentEditableElement);//Select the entire contents of the element with the range
range.collapse(false);//collapse the range to the end point. false means collapse to end rather than the start
selection = window.getSelection();//get the selection object (allows you to change selection)
selection.removeAllRanges();//remove any selections already made
selection.addRange(range);//make the range you have just created the visible selection
}
else if (document.selection)//IE 8 and lower
{
range = document.body.createTextRange();//Create a range (a range is a like the selection but invisible)
range.moveToElementText(contentEditableElement);//Select the entire contents of the element with the range
range.collapse(false);//collapse the range to the end point. false means collapse to end rather than the start
range.select();//Select the range (make it the visible selection
}
}
var YaskON = {
stringify: function (o, prefix, activationChar) {
prefix = prefix || '';
switch (typeof o) {
case 'object':
var output = "";
if (Array.isArray(o)) {
o.forEach(function (e, index) {
output += activationChar + prefix + '[' + index + ']' + '\n';
}.bind(this));
return output;
}
output = "";
for (var k in o) {
if (o.hasOwnProperty(k)) {
if (prefix == "") output += this.stringify(o[k], k, activationChar);
}
}
if (prefix != "") output += activationChar + prefix + '\n'
return output;
case 'function':
return "";
default:
if (prefix != "")
return prefix + '\n';
else
return '';
}
}
};
var data = {};
startFrom = 0;
var text = element.innerText;
var lastPoint = text.lastIndexOf('.');
if ((lastPoint > 0) && (text.length > 1)) {
var fnode = node.editor.node.findNode('.' + text.substring(this.ActivationChar.length, lastPoint));
if (fnode && typeof fnode.getValue() == 'object') {
data = fnode.getValue();
startFrom = text.lastIndexOf('.') + 1;
}
}
else
data = node.editor.get();
var optionsStr = YaskON.stringify(data, null, this.ActivationChar);
var options = (optionsStr == "") ? [] : optionsStr.split("\n");
activeOptions = options;
if (activeOptions.length > 0) {
if (hy && hy.attachment != element) {
hy.destroy();
hy = null;
}
if (hy == null)
hy = horsey(element, {
source(data, done) {
setTimeout(() => done(null, [{
list: activeOptions.filter(item => item.startsWith(data.input.substring(startFrom)))
}]), 50);
},
set: function (value) {
element.innerText = element.innerText.substring(0, startFrom) + value;
setEndOfContenteditable(element);
},
filter: function (q, suggestion) {
return true;
}
});
}
},
Hide: function () {
if (hy) hy.hide();
}
}
};
var json = {
'array': [{'field1':'v1', 'field2':'v2'}, 2, 3],
'boolean': true,
'null': null,
'number': 123,
'object': {'a': 'b', 'c': 'd'},
'string': 'Hello World'
};
var editor = new JSONEditor(container, options, json);
</script>
</body>
</html>

View File

@ -0,0 +1,95 @@
<!DOCTYPE HTML>
<html>
<head>
<title>JSONEditor | Auto Complete</title>
<link href="../dist/jsoneditor.css" rel="stylesheet" type="text/css">
<script src="../dist/jsoneditor.js"></script>
<style type="text/css">
#jsoneditor {
width: 500px;
height: 500px;
}
p {
width: 500px;
font-family: "DejaVu Sans", sans-serif;
}
</style>
<!--<link href="./css/darktheme.css" rel="stylesheet" type="text/css">-->
</head>
<body>
<p>
This example demonstrates how to customize the look of JSONEditor,
the editor below has a dark theme. Note that the example isn't worked
out for the mode <code>code</code>. To do that, you can load and configure
a custom theme for the Ace editor.
</p>
<div id="jsoneditor"></div>
<script>
// create the editor
var container = document.getElementById('jsoneditor');
var options = {
modes: ['text', 'tree'],
autocomplete: {
ApplyTo:['value'],
GetOptions: function (autocomplete, node, text) {
// Return all strings in json
function stringify(o, prefix) {
prefix = prefix || '';
switch (typeof o) {
case 'object':
var output = "";
if (Array.isArray(o)) {
if ((prefix != "") && (prefix != text)) output += prefix + '\n';
o.forEach(function (e, index) {
output += stringify(e, null);
}.bind(this));
return output;
}
output = "";
for (var k in o) {
if (o.hasOwnProperty(k)) {
if (prefix == "") output += stringify(o[k], k);
}
}
if ((prefix != "") && (prefix != text)) output += prefix + '\n';
return output;
case 'function':
return "";
default:
var output = "";
if ((prefix != "") && (prefix != text)) output += prefix + '\n';
if ((o != "") && (o != text)) output += o + '\n';
return output;
}
}
var data = node.editor.get();
var optionsStr = stringify(data, null);
var options = optionsStr.split("\n");
return options;
}
}
};
var json = {
'array': [{'field1':'v1', 'field2':'v2'}, 2, 3],
'boolean': true,
'null': null,
'number': 123,
'object': {'a': 'b', 'c': 'd'},
'string': 'Hello World'
};
var editor = new JSONEditor(container, options, json);
</script>
</body>
</html>

View File

@ -0,0 +1,59 @@
<!DOCTYPE HTML>
<html>
<head>
<title>JSONEditor | Auto Complete</title>
<link href="../dist/jsoneditor.css" rel="stylesheet" type="text/css">
<script src="../dist/jsoneditor.js"></script>
<style type="text/css">
#jsoneditor {
width: 500px;
height: 500px;
}
p {
width: 500px;
font-family: "DejaVu Sans", sans-serif;
}
</style>
<!--<link href="./css/darktheme.css" rel="stylesheet" type="text/css">-->
</head>
<body>
<p>
This example demonstrates how to customize the look of JSONEditor,
the editor below has a dark theme. Note that the example isn't worked
out for the mode <code>code</code>. To do that, you can load and configure
a custom theme for the Ace editor.
</p>
<div id="jsoneditor"></div>
<script>
// create the editor
var container = document.getElementById('jsoneditor');
var options = {
modes: ['text', 'tree'],
autocomplete: {
ApplyTo:['name','value'], // This indicates the autocomplete is going to be applied to json properties names and values.
GetOptions: function () {
return ["apple","cranberry","raspberry","pie"];
}
}
};
var json = {
'array': [{'field1':'v1', 'field2':'v2'}, 2, 3],
'boolean': true,
'null': null,
'number': 123,
'object': {'a': 'b', 'c': 'd'},
'string': 'Hello World'
};
var editor = new JSONEditor(container, options, json);
</script>
</body>
</html>

View File

@ -1,9 +1,34 @@
'use strict';
function autocomplete(config) {
(function (arr) {
arr.forEach(function (item) {
if (item.hasOwnProperty('remove')) {
return;
}
Object.defineProperty(item, 'remove', {
configurable: true,
enumerable: true,
writable: true,
value: function remove() {
if (this.parentNode != null)
this.parentNode.removeChild(this);
}
});
});
})([Element.prototype, CharacterData.prototype, DocumentType.prototype]);
if (!String.prototype.startsWith) {
String.prototype.startsWith = function (searchString, position) {
position = position || 0;
return this.substr(position, searchString.length) === searchString;
};
}
function completely(config) {
config = config || {};
config.fontSize = config.fontSize || '16px';
config.fontFamily = config.fontFamily || 'sans-serif';
config.ConfirmKeys = config.ConfirmKeys || [39, 35, 9] // right, end, tab
config.fontSize = config.fontSize || '';
config.fontFamily = config.fontFamily || '';
config.promptInnerHTML = config.promptInnerHTML || '';
config.color = config.color || '#333';
config.hintColor = config.hintColor || '#aaa';
@ -183,6 +208,16 @@ function autocomplete(config) {
this.elementHint.remove();
this.elementHint = null;
}
if (config.fontSize == '') {
config.fontSize = window.getComputedStyle(element).getPropertyValue('font-size');
dropDown.style.fontSize = config.fontSize;
}
if (config.fontFamily == '') {
config.fontFamily = window.getComputedStyle(element).getPropertyValue('font-family');
dropDown.style.fontFamily = config.fontFamily;
}
var w = element.getBoundingClientRect().right - element.getBoundingClientRect().left;
dropDown.style.marginLeft = '0';
dropDown.style.marginTop = element.getBoundingClientRect().height + 'px';
@ -268,7 +303,7 @@ function autocomplete(config) {
// moving the dropDown and refreshing it.
dropDown.style.left = calculateWidthForText(leftSide) + 'px';
dropDownController.refresh(token, this.options);
this.elementHint.style.width = calculateWidthForText(this.elementHint.innerText) + 'px'
this.elementHint.style.width = calculateWidthForText(this.elementHint.innerText) + 10 + 'px'
var wasDropDownHidden = (dropDown.style.visibility == 'hidden');
if (!wasDropDownHidden)
this.elementHint.style.width = calculateWidthForText(this.elementHint.innerText) + dropDown.clientWidth + 'px';
@ -294,8 +329,8 @@ function autocomplete(config) {
e.stopPropagation();
return;
}
if (keyCode == 39 || keyCode == 35 || keyCode == 9 || keyCode == 190) { // right, end, tab, '.' (autocomplete triggered)
config.ConfirmKeys
if (config.ConfirmKeys.indexOf(keyCode) >= 0) { // (autocomplete triggered)
if (keyCode == 9) {
if (this.elementHint.innerText.length == 0) {
rs.onTab();
@ -376,5 +411,4 @@ function autocomplete(config) {
return rs;
}
if (window.module != undefined)
module.exports = completely;

View File

@ -8,7 +8,7 @@ var ContextMenu = require('./ContextMenu');
var Node = require('./Node');
var ModeSwitcher = require('./ModeSwitcher');
var util = require('./util');
var autocomplete = require('./autocomplete');
// create a mixin with the functions for tree mode
var treemode = {};
@ -52,6 +52,9 @@ treemode.create = function (container, options) {
this._setOptions(options);
if (options.autocomplete)
this.autocomplete = new autocomplete(options.autocomplete.Config);
if (this.options.history && this.options.mode !== 'view') {
this.history = new History(this);
}
@ -1104,15 +1107,18 @@ treemode._onKeyDown = function (event) {
if ((this.options.autocomplete) && (!handled)) {
if (!ctrlKey && !altKey && !metaKey && (event.key.length == 1 || keynum == 8 || keynum == 46)) {
handled = false;
if ((this.options.autocomplete.ApplyTo.indexOf('values') >= 0 && event.target.className.indexOf("jsoneditor-value") >= 0) ||
if ((this.options.autocomplete.ApplyTo.indexOf('value') >= 0 && event.target.className.indexOf("jsoneditor-value") >= 0) ||
(this.options.autocomplete.ApplyTo.indexOf('name') >= 0 && event.target.className.indexOf("jsoneditor-field") >= 0)) {
var node = Node.getNodeFromTarget(event.target);
if (this.options.autocomplete.ActivationChar == null || event.target.innerText.startsWith(this.options.autocomplete.ActivationChar)) { // Activate autocomplete
setTimeout(function (hnode, element) {
if (element.innerText.length > 0)
this.options.autocomplete.Show(hnode, element);
if (element.innerText.length > 0) {
var options = this.options.autocomplete.GetOptions(this.autocomplete, hnode, element.innerText);
if (options.length > 0)
this.autocomplete.Show(element, options);
}
else
this.options.autocomplete.Hide();
this.autocomplete.hideDropDown();
}.bind(this, node, event.target), 100);
}