- polyfills moved to utils.js
- lowercase "Show" function - refine autocomplete examples. - style improvements to match context menu. - set auto to scrollbar in autocomplete dropdownbox - Added docs for Templates and Autocomplete - Promisify getOptions function - Add elementType to getOptions function to return which element "field" or "value" is being edited. - applyTo option now accept ['field', 'value'] instead of ['name', 'value'] - optimize confirmKeys to be part of the autocomplete option instead of autocomplete.config
This commit is contained in:
parent
fc4562fea1
commit
00ba443acf
63
docs/api.md
63
docs/api.md
|
@ -114,6 +114,69 @@ Constructs a new JSONEditor.
|
|||
|
||||
Set the Ace editor theme, uses included 'ace/theme/jsoneditor' by default. Please note that only the default theme is included with jsoneditor, so if you specify another one you need to make sure it is loaded.
|
||||
|
||||
- `{Object} templates`
|
||||
|
||||
Array of templates that will appear in the context menu, Each template is a json object precreated that can be added as a object value to any node in your document.
|
||||
|
||||
The following example allow you can create a "Person" node and a "Address" node, each one will appear in your context menu, once you selected the whole json object will be created.
|
||||
|
||||
```js
|
||||
var options = {
|
||||
templates: [
|
||||
{
|
||||
text: 'Person',
|
||||
title: 'Insert a Person Node',
|
||||
className: 'jsoneditor-type-object',
|
||||
field: 'PersonTemplate',
|
||||
value: {
|
||||
'firstName': 'John',
|
||||
'lastName': 'Do',
|
||||
'age': 28
|
||||
}
|
||||
},
|
||||
{
|
||||
text: 'Address',
|
||||
title: 'Insert a Address Node',
|
||||
field: 'AddressTemplate',
|
||||
value: {
|
||||
'street': "",
|
||||
'city': "",
|
||||
'state': "",
|
||||
'ZIP code': ""
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
- `{Object} autocomplete`
|
||||
|
||||
*autocomplete* will enable this feature in your editor in tree mode, the object have the following **subelements**:
|
||||
|
||||
- `{Object} confirmKeys`
|
||||
|
||||
Indicate the KeyCodes for trigger confirm completion, by default those keys are: [39, 35, 9] which are the code for [right, end, tab]
|
||||
|
||||
- `{Object} applyTo`
|
||||
|
||||
Indicate where the autocomplete is going to be activated, under field in json node or/and under value in a json node, default is: ['field','value']
|
||||
|
||||
- `{string} activationChar`
|
||||
|
||||
This is a single char string, indicates that the text should starts with this char in order to process and display the autocompletion, a typical example is in some editors you type '@' and then you see a dropdownbox where you can link to an specific author.
|
||||
|
||||
- `{Function} getOptions (autocomplete, node, text, elementType)`
|
||||
|
||||
This function will return your possible options for create the autocomplete selection, you can control dynamically which options you want to display according to the current active editing node.
|
||||
You can return a promise (for async) as well the options array (sync).
|
||||
|
||||
*Parameters:*
|
||||
|
||||
- autocomplete : The instance of the autocomplete engine.
|
||||
- node : The current active editing node. (node include field and value)
|
||||
- text : The text in the current node part. (basically the text that the user is editing)
|
||||
- elementType : Can be "field" or "value" depending if the user is editing a field name or a value of a node.
|
||||
|
||||
|
||||
### Methods
|
||||
|
||||
|
|
|
@ -36,9 +36,9 @@
|
|||
var options = {
|
||||
modes: ['text', 'tree'],
|
||||
autocomplete: {
|
||||
applyTo:['name','value'], // This indicates the autocomplete is going to be applied to json properties names and values.
|
||||
applyTo:['field','value'], // This indicates the autocomplete is going to be applied to json properties names and values.
|
||||
getOptions: function () {
|
||||
return ["apple","cranberry","raspberry","pie"];
|
||||
return ["apple","cranberry","raspberry","pie", "mango", "mandarine", "melon", "appleton"];
|
||||
}
|
||||
}
|
||||
};
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
|
||||
<link href="../dist/jsoneditor.css" rel="stylesheet" type="text/css">
|
||||
<script src="../dist/jsoneditor.js"></script>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.4/lodash.min.js"></script>
|
||||
|
||||
|
||||
<style type="text/css">
|
||||
|
@ -38,46 +39,27 @@
|
|||
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;
|
||||
}
|
||||
}
|
||||
|
||||
return new Promise(function (resolve, reject) {
|
||||
var data = node.editor.get();
|
||||
var optionsStr = stringify(data, null);
|
||||
var options = optionsStr.split("\n");
|
||||
|
||||
return options;
|
||||
var options = extractUniqueWords(data);
|
||||
if (options.length > 0) resolve(options); else reject();
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
// ...
|
||||
|
||||
function extractUniqueWords (json) {
|
||||
return _.uniq(_.flatMapDeep(json, function (value, key) {
|
||||
return _.isObject(value)
|
||||
? [key]
|
||||
: [key, String(value)]
|
||||
}))
|
||||
}
|
||||
|
||||
var json = {
|
||||
'array': [{'field1':'v1', 'field2':'v2'}, 2, 3],
|
||||
'boolean': true,
|
||||
|
|
|
@ -36,13 +36,31 @@
|
|||
var options = {
|
||||
modes: ['text', 'tree'],
|
||||
autocomplete: {
|
||||
config: {
|
||||
confirmKeys: [39, 35, 9, 190] // Confirm Autocomplete Keys: [right, end, tab, '.'] // By default are only [right, end, tab]
|
||||
},
|
||||
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 data = {};
|
||||
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();
|
||||
// Indicate that autocompletion should start after the . (ignoring the first part)
|
||||
autocomplete.startFrom = text.lastIndexOf('.') + 1;
|
||||
}
|
||||
}
|
||||
else
|
||||
data = node.editor.get();
|
||||
|
||||
var optionsStr = YaskON.stringify(data, null, this.activationChar);
|
||||
var options = optionsStr.split("\n");
|
||||
|
||||
return options;
|
||||
}
|
||||
}
|
||||
};
|
||||
var YaskON = {
|
||||
// Return first level json paths by the node 'o'
|
||||
stringify: function (o, prefix, activationChar) {
|
||||
|
@ -71,27 +89,6 @@
|
|||
}
|
||||
}
|
||||
};
|
||||
var data = {};
|
||||
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();
|
||||
// Indicate that autocompletion should start after the . (ignoring the first part)
|
||||
autocomplete.startFrom = text.lastIndexOf('.') + 1;
|
||||
}
|
||||
}
|
||||
else
|
||||
data = node.editor.get();
|
||||
|
||||
var optionsStr = YaskON.stringify(data, null, this.activationChar);
|
||||
var options = optionsStr.split("\n");
|
||||
|
||||
return options;
|
||||
}
|
||||
}
|
||||
};
|
||||
var json = {
|
||||
'array': [{ 'field1': 'v1', 'field2': 'v2' }, 2, 3],
|
||||
'boolean': true,
|
||||
|
|
|
@ -1,15 +1,16 @@
|
|||
|
||||
div.jsoneditor div.autocomplete.dropdown {
|
||||
position: absolute;
|
||||
background-color: #fff;
|
||||
border: 1px solid #aaa;
|
||||
background: white;
|
||||
box-shadow: 2px 2px 12px rgba(128, 128, 128, 0.3);
|
||||
border: 1px solid #d3d3d3;
|
||||
z-index: 100;
|
||||
overflow-x: hidden;
|
||||
overflow-y: scroll;
|
||||
overflow-y: auto;
|
||||
cursor: default;
|
||||
margin: 0;
|
||||
padding-left: 2pt;
|
||||
padding-right: 10pt;
|
||||
padding-right: 5pt;
|
||||
text-align: left;
|
||||
outline: 0;
|
||||
font-family: droid sans mono, consolas, monospace, courier new, courier, sans-serif;
|
||||
|
|
|
@ -1,29 +1,5 @@
|
|||
'use strict';
|
||||
|
||||
(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.confirmKeys = config.confirmKeys || [39, 35, 9] // right, end, tab
|
||||
|
@ -181,7 +157,7 @@ function completely(config) {
|
|||
elementHint: null,
|
||||
elementStyle: null,
|
||||
wrapper: wrapper, // Only to allow easy access to the HTML elements to the final user (possibly for minor customizations)
|
||||
Show: function (element, options) {
|
||||
show: function (element, options) {
|
||||
this.wrapper.remove();
|
||||
if (this.elementHint) {
|
||||
this.elementHint.remove();
|
||||
|
|
|
@ -53,7 +53,7 @@ treemode.create = function (container, options) {
|
|||
this._setOptions(options);
|
||||
|
||||
if (options.autocomplete)
|
||||
this.autocomplete = new autocomplete(options.autocomplete.config);
|
||||
this.autocomplete = new autocomplete(options.autocomplete);
|
||||
|
||||
if (this.options.history && this.options.mode !== 'view') {
|
||||
this.history = new History(this);
|
||||
|
@ -1107,15 +1107,26 @@ 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('value') >= 0 && event.target.className.indexOf("jsoneditor-value") >= 0) ||
|
||||
(this.options.autocomplete.applyTo.indexOf('name') >= 0 && event.target.className.indexOf("jsoneditor-field") >= 0)) {
|
||||
var jsonElementType = "";
|
||||
if (event.target.className.indexOf("jsoneditor-value") >= 0) jsonElementType = "value";
|
||||
if (event.target.className.indexOf("jsoneditor-field") >= 0) jsonElementType = "field";
|
||||
|
||||
if ((this.options.autocomplete.applyTo.indexOf('value') >= 0 && jsonElementType == "value") ||
|
||||
(this.options.autocomplete.applyTo.indexOf('field') >= 0 && jsonElementType == "field")) {
|
||||
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) {
|
||||
var options = this.options.autocomplete.getOptions(this.autocomplete, hnode, element.innerText);
|
||||
if (options.length > 0)
|
||||
this.autocomplete.Show(element, options);
|
||||
var result = this.options.autocomplete.getOptions(this.autocomplete, hnode, element.innerText, jsonElementType);
|
||||
if (typeof result.then === 'function') {
|
||||
// probably a promise
|
||||
if (result.then(function (options) {
|
||||
this.autocomplete.show(element, options);
|
||||
}.bind(this)));
|
||||
} else {
|
||||
// definitely not a promise
|
||||
this.autocomplete.show(element, result);
|
||||
}
|
||||
}
|
||||
else
|
||||
this.autocomplete.hideDropDown();
|
||||
|
|
|
@ -776,3 +776,30 @@ exports.textDiff = function textDiff(oldText, newText) {
|
|||
|
||||
return {start: start, end: newEnd};
|
||||
};
|
||||
|
||||
// Polyfill for array remove
|
||||
(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]);
|
||||
|
||||
|
||||
// Polyfill for startsWith
|
||||
if (!String.prototype.startsWith) {
|
||||
String.prototype.startsWith = function (searchString, position) {
|
||||
position = position || 0;
|
||||
return this.substr(position, searchString.length) === searchString;
|
||||
};
|
||||
}
|
Loading…
Reference in New Issue