From e7b7362fc2cd10ad3be78dee805b831b497cb9df Mon Sep 17 00:00:00 2001 From: jos Date: Wed, 6 Apr 2016 09:15:38 +0200 Subject: [PATCH] Sorting object keys or array items via the context menu is now also naturally sorted (see #288) --- HISTORY.md | 3 + package.json | 3 +- src/js/Node.js | 164 +++++++++++++++---------------------------------- 3 files changed, 54 insertions(+), 116 deletions(-) diff --git a/HISTORY.md b/HISTORY.md index 7c43c39..beb1b83 100644 --- a/HISTORY.md +++ b/HISTORY.md @@ -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). diff --git a/package.json b/package.json index 93c63b9..756aa56 100644 --- a/package.json +++ b/package.json @@ -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", diff --git a/src/js/Node.js b/src/js/Node.js index 08db8ca..f354dbb 100644 --- a/src/js/Node.js +++ b/src/js/Node.js @@ -1,3 +1,4 @@ +var naturalSort = require('javascript-natural-sort'); var ContextMenu = require('./ContextMenu'); var appendNodeFactory = require('./appendNodeFactory'); var util = require('./util'); @@ -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); + } } } - - 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 = ''; + + // sort object keys + if (this.editor.options.sortObjectKeys === true) { + this.sort('asc'); + } } 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; @@ -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(); - - 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(); +Node.prototype.sort = function (direction) { + if (!this._hasChilds()) { + return; } + + 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()) { - 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: [ { @@ -3119,7 +3104,7 @@ Node.prototype.showContextMenu = function (anchor, onClose) { className: 'jsoneditor-sort-asc', title: 'Sort the childs of this ' + this.type + ' in ascending order', click: function () { - node._onSort('asc'); + node.sort('asc'); } }, { @@ -3127,7 +3112,7 @@ Node.prototype.showContextMenu = function (anchor, onClose) { className: 'jsoneditor-sort-desc', title: 'Sort the childs of this ' + this.type +' in descending order', click: function () { - node._onSort('desc'); + node.sort('desc'); } } ] @@ -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);