Sorting object keys or array items via the context menu is now also naturally sorted (see #288)

This commit is contained in:
jos 2016-04-06 09:15:38 +02:00
parent a97be17ed3
commit e7b7362fc2
3 changed files with 54 additions and 116 deletions

View File

@ -6,11 +6,14 @@ https://github.com/josdejong/jsoneditor
## not yet released, version 5.3.0 ## not yet released, version 5.3.0
- Implemented support for sorting object keys naturally. Thanks @edufelipe. - Implemented support for sorting object keys naturally. Thanks @edufelipe.
- Sorting object keys or array items via the context menu is now also naturally
sorted.
- Fixed #283: improved JSON schema error message in case of no - Fixed #283: improved JSON schema error message in case of no
additionalProperties. additionalProperties.
- Fixed #286: Calling `get()` or `getText()` caused the editor to lose focus. - Fixed #286: Calling `get()` or `getText()` caused the editor to lose focus.
A regression introduced in v5.2.0. A regression introduced in v5.2.0.
## 2016-03-20, version 5.2.0 ## 2016-03-20, version 5.2.0
- Implemented method `editor.destroy()` to properly cleanup the editor (#278). - Implemented method `editor.destroy()` to properly cleanup the editor (#278).

View File

@ -24,7 +24,8 @@
}, },
"dependencies": { "dependencies": {
"ajv": "3.4.0", "ajv": "3.4.0",
"brace": "0.7.0" "brace": "0.7.0",
"javascript-natural-sort": "0.7.1"
}, },
"devDependencies": { "devDependencies": {
"gulp": "3.9.0", "gulp": "3.9.0",

View File

@ -1,3 +1,4 @@
var naturalSort = require('javascript-natural-sort');
var ContextMenu = require('./ContextMenu'); var ContextMenu = require('./ContextMenu');
var appendNodeFactory = require('./appendNodeFactory'); var appendNodeFactory = require('./appendNodeFactory');
var util = require('./util'); var util = require('./util');
@ -299,46 +300,30 @@ Node.prototype.setValue = function(value, type) {
else if (this.type == 'object') { else if (this.type == 'object') {
// object // object
this.childs = []; this.childs = [];
var props = [];
for (var childField in value) { for (var childField in value) {
if (value.hasOwnProperty(childField)) { if (value.hasOwnProperty(childField)) {
props.push(childField); childValue = value[childField];
if (childValue !== undefined && !(childValue instanceof Function)) {
// ignore undefined and functions
child = new Node(this.editor, {
field: childField,
value: childValue
});
this.appendChild(child);
}
} }
} }
if (this.editor.options.sortObjectKeys === true) {
props = this._naturalSort(props);
}
for (var i = 0; i < props.length; i++) {
var childField = props[i];
childValue = value[childField];
if (childValue !== undefined && !(childValue instanceof Function)) {
// ignore undefined and functions
child = new Node(this.editor, {
field: childField,
value: childValue
});
this.appendChild(child);
}
}
this.value = ''; this.value = '';
// sort object keys
if (this.editor.options.sortObjectKeys === true) {
this.sort('asc');
}
} }
else { else {
// value // value
this.childs = undefined; this.childs = undefined;
this.value = value; this.value = value;
/* TODO
if (typeof(value) == 'string') {
var escValue = JSON.stringify(value);
this.value = escValue.substring(1, escValue.length - 1);
console.log('check', value, this.value);
}
else {
this.value = value;
}
*/
} }
this.previousValue = this.value; this.previousValue = this.value;
@ -2761,41 +2746,41 @@ Node.prototype._onChangeType = function (newType) {
}; };
/** /**
* Sort the childs of the node. Only applicable when the node has type 'object' * Sort the child's of the node. Only applicable when the node has type 'object'
* or 'array'. * or 'array'.
* @param {String} direction Sorting direction. Available values: "asc", "desc" * @param {String} direction Sorting direction. Available values: "asc", "desc"
* @private * @private
*/ */
Node.prototype._onSort = function (direction) { Node.prototype.sort = function (direction) {
if (this._hasChilds()) { if (!this._hasChilds()) {
var order = (direction == 'desc') ? -1 : 1; return;
var prop = (this.type == 'array') ? 'value': 'field';
this.hideChilds();
var oldChilds = this.childs;
var oldSort = this.sort;
// copy the array (the old one will be kept for an undo action
this.childs = this.childs.concat();
// sort the arrays
this.childs.sort(function (a, b) {
if (a[prop] > b[prop]) return order;
if (a[prop] < b[prop]) return -order;
return 0;
});
this.sort = (order == 1) ? 'asc' : 'desc';
this.editor._onAction('sort', {
node: this,
oldChilds: oldChilds,
oldSort: oldSort,
newChilds: this.childs,
newSort: this.sort
});
this.showChilds();
} }
var order = (direction == 'desc') ? -1 : 1;
var prop = (this.type == 'array') ? 'value': 'field';
this.hideChilds();
var oldChilds = this.childs;
var oldSortOrder = this.sortOrder;
// copy the array (the old one will be kept for an undo action
this.childs = this.childs.concat();
// sort the arrays
this.childs.sort(function (a, b) {
return order * naturalSort(a[prop], b[prop]);
});
this.sortOrder = (order == 1) ? 'asc' : 'desc';
this.editor._onAction('sort', {
node: this,
oldChilds: oldChilds,
oldSort: oldSortOrder,
newChilds: this.childs,
newSort: this.sortOrder
});
this.showChilds();
}; };
/** /**
@ -3105,13 +3090,13 @@ Node.prototype.showContextMenu = function (anchor, onClose) {
} }
if (this._hasChilds()) { if (this._hasChilds()) {
var direction = ((this.sort == 'asc') ? 'desc': 'asc'); var direction = ((this.sortOrder == 'asc') ? 'desc': 'asc');
items.push({ items.push({
text: 'Sort', text: 'Sort',
title: 'Sort the childs of this ' + this.type, title: 'Sort the childs of this ' + this.type,
className: 'jsoneditor-sort-' + direction, className: 'jsoneditor-sort-' + direction,
click: function () { click: function () {
node._onSort(direction); node.sort(direction);
}, },
submenu: [ submenu: [
{ {
@ -3119,7 +3104,7 @@ Node.prototype.showContextMenu = function (anchor, onClose) {
className: 'jsoneditor-sort-asc', className: 'jsoneditor-sort-asc',
title: 'Sort the childs of this ' + this.type + ' in ascending order', title: 'Sort the childs of this ' + this.type + ' in ascending order',
click: function () { click: function () {
node._onSort('asc'); node.sort('asc');
} }
}, },
{ {
@ -3127,7 +3112,7 @@ Node.prototype.showContextMenu = function (anchor, onClose) {
className: 'jsoneditor-sort-desc', className: 'jsoneditor-sort-desc',
title: 'Sort the childs of this ' + this.type +' in descending order', title: 'Sort the childs of this ' + this.type +' in descending order',
click: function () { click: function () {
node._onSort('desc'); node.sort('desc');
} }
} }
] ]
@ -3399,57 +3384,6 @@ Node.prototype._escapeJSON = function (text) {
return escaped; return escaped;
}; };
/**
* sorts an array of strings using natural sort.
* Natural sort resuls in the array `['100', '2', '1']` being sorted as
* `['1', '2', '100']` instead of `['1', '100', '2']`, making it more readable.
* This implementation is an adaptation of Brian Huisman implementation of the
* Alphanum Algorithm by David Koelle.
* @param {Array[String]} array
* @return {String} sortedArray
* @private
*/
Node.prototype._naturalSort = function(array) {
var sortedArray = array.slice();
for (var z = 0, t; t = sortedArray[z]; z++) {
sortedArray[z] = [];
var x = 0, y = -1, n = 0, i, j;
while (i = (j = t.charAt(x++)).charCodeAt(0)) {
var m = (i == 46 || (i >=48 && i <= 57));
if (m !== n) {
sortedArray[z][++y] = '';
n = m;
}
sortedArray[z][y] += j;
}
}
sortedArray.sort(function(a, b) {
for (var x = 0, aa, bb; (aa = a[x]) && (bb = b[x]); x++) {
aa = aa.toLowerCase();
bb = bb.toLowerCase();
if (aa !== bb) {
var c = Number(aa), d = Number(bb);
if (c == aa && d == bb) {
return c - d;
}
else {
return (aa > bb) ? 1 : -1;
}
}
}
return a.length - b.length;
});
for (var z = 0; z < sortedArray.length; z++) {
sortedArray[z] = sortedArray[z].join('');
}
return sortedArray;
}
// TODO: find a nicer solution to resolve this circular dependency between Node and AppendNode // TODO: find a nicer solution to resolve this circular dependency between Node and AppendNode
var AppendNode = appendNodeFactory(Node); var AppendNode = appendNodeFactory(Node);