Skip to content

Commit

Permalink
Sorting object keys or array items via the context menu is now also n…
Browse files Browse the repository at this point in the history
…aturally sorted (see josdejong#288)
  • Loading branch information
josdejong committed Apr 6, 2016
1 parent a97be17 commit e7b7362
Show file tree
Hide file tree
Showing 3 changed files with 48 additions and 110 deletions.
3 changes: 3 additions & 0 deletions HISTORY.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,14 @@ https://github.com/josdejong/jsoneditor
## not yet released, version 5.3.0

- 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
additionalProperties.
- Fixed #286: Calling `get()` or `getText()` caused the editor to lose focus.
A regression introduced in v5.2.0.


## 2016-03-20, version 5.2.0

- Implemented method `editor.destroy()` to properly cleanup the editor (#278).
Expand Down
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,8 @@
},
"dependencies": {
"ajv": "3.4.0",
"brace": "0.7.0"
"brace": "0.7.0",
"javascript-natural-sort": "0.7.1"
},
"devDependencies": {
"gulp": "3.9.0",
Expand Down
152 changes: 43 additions & 109 deletions src/js/Node.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
var naturalSort = require('javascript-natural-sort');
var ContextMenu = require('./ContextMenu');
var appendNodeFactory = require('./appendNodeFactory');
var util = require('./util');
Expand Down Expand Up @@ -299,46 +300,30 @@ Node.prototype.setValue = function(value, type) {
else if (this.type == 'object') {
// object
this.childs = [];
var props = [];
for (var childField in value) {
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);
}
}
}
this.value = '';

// sort object keys
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.sort('asc');
}

this.value = '';
}
else {
// value
this.childs = undefined;
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;
Expand Down Expand Up @@ -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'.
* @param {String} direction Sorting direction. Available values: "asc", "desc"
* @private
*/
Node.prototype._onSort = function (direction) {
if (this._hasChilds()) {
var order = (direction == 'desc') ? -1 : 1;
var prop = (this.type == 'array') ? 'value': 'field';
this.hideChilds();
Node.prototype.sort = function (direction) {
if (!this._hasChilds()) {
return;
}

var oldChilds = this.childs;
var oldSort = this.sort;
var order = (direction == 'desc') ? -1 : 1;
var prop = (this.type == 'array') ? 'value': 'field';
this.hideChilds();

// copy the array (the old one will be kept for an undo action
this.childs = this.childs.concat();
var oldChilds = this.childs;
var oldSortOrder = this.sortOrder;

// 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';
// copy the array (the old one will be kept for an undo action
this.childs = this.childs.concat();

this.editor._onAction('sort', {
node: this,
oldChilds: oldChilds,
oldSort: oldSort,
newChilds: this.childs,
newSort: this.sort
});
// sort the arrays
this.childs.sort(function (a, b) {
return order * naturalSort(a[prop], b[prop]);
});
this.sortOrder = (order == 1) ? 'asc' : 'desc';

this.showChilds();
}
this.editor._onAction('sort', {
node: this,
oldChilds: oldChilds,
oldSort: oldSortOrder,
newChilds: this.childs,
newSort: this.sortOrder
});

this.showChilds();
};

/**
Expand Down Expand Up @@ -3105,29 +3090,29 @@ Node.prototype.showContextMenu = function (anchor, onClose) {
}

if (this._hasChilds()) {
var direction = ((this.sort == 'asc') ? 'desc': 'asc');
var direction = ((this.sortOrder == 'asc') ? 'desc': 'asc');
items.push({
text: 'Sort',
title: 'Sort the childs of this ' + this.type,
className: 'jsoneditor-sort-' + direction,
click: function () {
node._onSort(direction);
node.sort(direction);
},
submenu: [
{
text: 'Ascending',
className: 'jsoneditor-sort-asc',
title: 'Sort the childs of this ' + this.type + ' in ascending order',
click: function () {
node._onSort('asc');
node.sort('asc');
}
},
{
text: 'Descending',
className: 'jsoneditor-sort-desc',
title: 'Sort the childs of this ' + this.type +' in descending order',
click: function () {
node._onSort('desc');
node.sort('desc');
}
}
]
Expand Down Expand Up @@ -3399,57 +3384,6 @@ Node.prototype._escapeJSON = function (text) {
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
var AppendNode = appendNodeFactory(Node);

Expand Down

0 comments on commit e7b7362

Please sign in to comment.