Skip to content

Commit

Permalink
- polyfills moved to utils.js
Browse files Browse the repository at this point in the history
- 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
  • Loading branch information
ianido committed Jun 17, 2017
1 parent fc4562f commit 00ba443
Show file tree
Hide file tree
Showing 8 changed files with 173 additions and 116 deletions.
63 changes: 63 additions & 0 deletions docs/api.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
4 changes: 2 additions & 2 deletions examples/11_autocomplete_basic.html
Original file line number Diff line number Diff line change
Expand Up @@ -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"];
}
}
};
Expand Down
58 changes: 20 additions & 38 deletions examples/12_autocomplete_dynamic.html
Original file line number Diff line number Diff line change
Expand Up @@ -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">
Expand Down Expand Up @@ -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;
}
}

var data = node.editor.get();
var optionsStr = stringify(data, null);
var options = optionsStr.split("\n");

return options;
return new Promise(function (resolve, reject) {
var data = node.editor.get();
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,
Expand Down
79 changes: 38 additions & 41 deletions examples/13_autocomplete_advanced.html
Original file line number Diff line number Diff line change
Expand Up @@ -36,41 +36,10 @@
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 YaskON = {
// Return first level json paths by the node 'o'
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:
return prefix + '\n';
}
}
};
var data = {};
autocomplete.startFrom = 0;
var lastPoint = text.lastIndexOf('.');
Expand All @@ -91,15 +60,43 @@
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 YaskON = {
// Return first level json paths by the node 'o'
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:
return prefix + '\n';
}
}
};
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>
Expand Down
9 changes: 5 additions & 4 deletions src/css/autocomplete.css
Original file line number Diff line number Diff line change
@@ -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;
Expand Down
26 changes: 1 addition & 25 deletions src/js/autocomplete.js
Original file line number Diff line number Diff line change
@@ -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
Expand Down Expand Up @@ -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();
Expand Down
23 changes: 17 additions & 6 deletions src/js/treemode.js
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -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();
Expand Down
27 changes: 27 additions & 0 deletions src/js/util.js
Original file line number Diff line number Diff line change
Expand Up @@ -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;
};
}

0 comments on commit 00ba443

Please sign in to comment.