Implemented `transform` modal for modes `code` and `text`

This commit is contained in:
jos 2019-06-19 11:57:28 +02:00
parent 74b816a554
commit 7311e78b53
7 changed files with 105 additions and 68 deletions

View File

@ -3,6 +3,11 @@
https://github.com/josdejong/jsoneditor https://github.com/josdejong/jsoneditor
## not yet published, version 6.1.0
- Implemented menu options `sort` and `transform` for modes `code` and `text`.
## 2019-06-12, version 6.0.0 ## 2019-06-12, version 6.0.0
- Breaking change: upgraded dependency `ajv@6.10.0`, supporting JSON schema - Breaking change: upgraded dependency `ajv@6.10.0`, supporting JSON schema

View File

@ -1459,7 +1459,7 @@ Node.prototype.changeType = function (newType) {
this.value = String(this.value); this.value = String(this.value);
} }
else { else {
this.value = this._stringCast(String(this.value)); this.value = util.parseString(String(this.value));
} }
this.focus(); this.focus();
@ -1550,7 +1550,7 @@ Node.prototype._getDomValue = function() {
} }
else { else {
var str = this._unescapeHTML(this.valueInnerText); var str = this._unescapeHTML(this.valueInnerText);
value = this._stringCast(str); value = util.parseString(str);
} }
if (value !== this.value) { if (value !== this.value) {
this.value = value; this.value = value;
@ -4303,8 +4303,7 @@ Node.prototype.showContextMenu = function (anchor, onClose) {
title: translate('transformTitle', {type: this.type}), title: translate('transformTitle', {type: this.type}),
className: 'jsoneditor-transform', className: 'jsoneditor-transform',
click: function () { click: function () {
var anchor = node.editor.options.modalAnchor || DEFAULT_MODAL_ANCHOR; node.showTransformModal();
showTransformModal(node, anchor)
} }
}); });
} }
@ -4456,7 +4455,7 @@ Node.prototype.showContextMenu = function (anchor, onClose) {
/** /**
* Show advanced sorting modal * Show sorting modal
*/ */
Node.prototype.showSortModal = function () { Node.prototype.showSortModal = function () {
var node = this; var node = this;
@ -4479,6 +4478,19 @@ Node.prototype.showSortModal = function () {
}) })
} }
/**
* Show transform modal
*/
Node.prototype.showTransformModal = function () {
var node = this;
var anchor = this.editor.options.modalAnchor || DEFAULT_MODAL_ANCHOR;
var json = node.getValue();
showTransformModal(anchor, json, function (query) {
node.transform(query);
});
}
/** /**
* get the type of a value * get the type of a value
* @param {*} value * @param {*} value
@ -4492,45 +4504,13 @@ Node.prototype._getType = function(value) {
if (value instanceof Object) { if (value instanceof Object) {
return 'object'; return 'object';
} }
if (typeof(value) == 'string' && typeof(this._stringCast(value)) != 'string') { if (typeof(value) == 'string' && typeof(util.parseString(value)) != 'string') {
return 'string'; return 'string';
} }
return 'auto'; return 'auto';
}; };
/**
* cast contents of a string to the correct type. This can be a string,
* a number, a boolean, etc
* @param {String} str
* @return {*} castedStr
* @private
*/
Node.prototype._stringCast = function(str) {
var lower = str.toLowerCase(),
num = Number(str), // will nicely fail with '123ab'
numFloat = parseFloat(str); // will nicely fail with ' '
if (str == '') {
return '';
}
else if (lower == 'null') {
return null;
}
else if (lower == 'true') {
return true;
}
else if (lower == 'false') {
return false;
}
else if (!isNaN(num) && !isNaN(numFloat)) {
return num;
}
else {
return str;
}
};
/** /**
* escape a text, such that it can be displayed safely in an HTML element * escape a text, such that it can be displayed safely in an HTML element
* @param {String} text * @param {String} text

View File

@ -9,12 +9,14 @@ var MAX_PREVIEW_LINES = 100;
/** /**
* Show advanced filter and transform modal using JMESPath * Show advanced filter and transform modal using JMESPath
* @param {Node} node the node to be transformed
* @param {HTMLElement} container The container where to center * @param {HTMLElement} container The container where to center
* the modal and create an overlay * the modal and create an overlay
* @param {JSON} json The json data to be transformed
* @param {function} onTransform Callback invoked with the created
* query as callback
*/ */
function showTransformModal (node, container) { function showTransformModal (container, json, onTransform) {
var value = node.getValue(); var value = json;
var content = '<label class="pico-modal-contents">' + var content = '<label class="pico-modal-contents">' +
'<div class="pico-modal-header">' + translate('transform') + '</div>' + '<div class="pico-modal-header">' + translate('transform') + '</div>' +
@ -119,8 +121,9 @@ function showTransformModal (node, container) {
wizard.innerHTML = '(wizard not available for objects, only for arrays)' wizard.innerHTML = '(wizard not available for objects, only for arrays)'
} }
var paths = node.getChildPaths(); var sortablePaths = util.getChildPaths(json);
paths.forEach(function (path) {
sortablePaths.forEach(function (path) {
var formattedPath = preprocessPath(path); var formattedPath = preprocessPath(path);
var filterOption = document.createElement('option'); var filterOption = document.createElement('option');
filterOption.text = formattedPath; filterOption.text = formattedPath;
@ -133,12 +136,11 @@ function showTransformModal (node, container) {
sortField.appendChild(sortOption); sortField.appendChild(sortOption);
}); });
var allPaths = node.getChildPaths(true).filter(function(path) { var selectablePaths = util.getChildPaths(json, true).filter(function(path) {
return path !== '.'; return path !== '.';
}); });
if (selectablePaths.length > 0) {
if (allPaths.length > 0) { selectablePaths.forEach(function (path) {
allPaths.forEach(function (path) {
var formattedPath = preprocessPath(path); var formattedPath = preprocessPath(path);
var option = document.createElement('option'); var option = document.createElement('option');
option.text = formattedPath; option.text = formattedPath;
@ -194,10 +196,9 @@ function showTransformModal (node, container) {
? ['0'].concat(util.parsePath('.' + field1)) ? ['0'].concat(util.parsePath('.' + field1))
: ['0'] : ['0']
var exampleValue = util.get(value, examplePath) var exampleValue = util.get(value, examplePath)
// TODO: move _stringCast into a static util function
var value1 = typeof exampleValue === 'string' var value1 = typeof exampleValue === 'string'
? filterValue.value ? filterValue.value
: node._stringCast(filterValue.value); : parseString(filterValue.value);
query.value = '[? ' + query.value = '[? ' +
field1 + ' ' + field1 + ' ' +
@ -284,7 +285,7 @@ function showTransformModal (node, container) {
modal.close(); modal.close();
node.transform(query.value) onTransform(query.value)
}; };
setTimeout(function () { setTimeout(function () {

View File

@ -1,6 +1,7 @@
'use strict'; 'use strict';
var ace = require('./ace'); var ace = require('./ace');
var jmespath = require('jmespath');
var translate = require('./i18n').translate; var translate = require('./i18n').translate;
var ModeSwitcher = require('./ModeSwitcher'); var ModeSwitcher = require('./ModeSwitcher');
var showSortModal = require('./showSortModal'); var showSortModal = require('./showSortModal');
@ -173,24 +174,23 @@ textmode.create = function (container, options) {
sort.className = 'jsoneditor-sort'; sort.className = 'jsoneditor-sort';
sort.title = translate('sortTitleShort'); sort.title = translate('sortTitleShort');
sort.onclick = function () { sort.onclick = function () {
me._showSortModal() me._showSortModal();
}; };
this.menu.appendChild(sort); this.menu.appendChild(sort);
} }
// TODO // TODO
// // create transform button // create transform button
// if (this.options.enableTransform) { if (this.options.enableTransform) {
// var transform = document.createElement('button'); var transform = document.createElement('button');
// transform.type = 'button'; transform.type = 'button';
// transform.title = translate('transformTitleShort'); transform.title = translate('transformTitleShort');
// transform.className = 'jsoneditor-transform'; transform.className = 'jsoneditor-transform';
// transform.onclick = function () { transform.onclick = function () {
// var anchor = me.options.modalAnchor || DEFAULT_MODAL_ANCHOR; me._showTransformModal();
// showTransformModal(me.node, anchor) };
// }; this.menu.appendChild(transform);
// this.menu.appendChild(transform); }
// }
// create repair button // create repair button
var buttonRepair = document.createElement('button'); var buttonRepair = document.createElement('button');
@ -468,6 +468,20 @@ textmode._showSortModal = function () {
}) })
} }
/**
* Open a transform modal
* @private
*/
textmode._showTransformModal = function () {
var me = this;
var anchor = this.options.modalAnchor || DEFAULT_MODAL_ANCHOR;
var json = this.get();
showTransformModal(anchor, json, function (query) {
var updatedJson = jmespath.search(json, query);
me.set(updatedJson);
})
}
/** /**
* Handle text selection * Handle text selection
* Calculates the cursor position and selection range and updates menu * Calculates the cursor position and selection range and updates menu

View File

@ -10,13 +10,10 @@ var Node = require('./Node');
var ModeSwitcher = require('./ModeSwitcher'); var ModeSwitcher = require('./ModeSwitcher');
var util = require('./util'); var util = require('./util');
var autocomplete = require('./autocomplete'); var autocomplete = require('./autocomplete');
var showTransformModal = require('./showTransformModal');
var translate = require('./i18n').translate; var translate = require('./i18n').translate;
var setLanguages = require('./i18n').setLanguages; var setLanguages = require('./i18n').setLanguages;
var setLanguage = require('./i18n').setLanguage; var setLanguage = require('./i18n').setLanguage;
var DEFAULT_MODAL_ANCHOR = document.body; // TODO: this constant is defined twice
// create a mixin with the functions for tree mode // create a mixin with the functions for tree mode
var treemode = {}; var treemode = {};
@ -1033,8 +1030,7 @@ treemode._createFrame = function () {
transform.title = translate('transformTitleShort'); transform.title = translate('transformTitleShort');
transform.className = 'jsoneditor-transform'; transform.className = 'jsoneditor-transform';
transform.onclick = function () { transform.onclick = function () {
var anchor = editor.options.modalAnchor || DEFAULT_MODAL_ANCHOR; editor.node.showTransformModal();
showTransformModal(editor.node, anchor)
}; };
this.menu.appendChild(transform); this.menu.appendChild(transform);
} }

View File

@ -1264,6 +1264,38 @@ exports.sortObjectKeys = function (object, direction) {
return sortedObject; return sortedObject;
} }
/**
* Cast contents of a string to the correct type.
* This can be a string, a number, a boolean, etc
* @param {String} str
* @return {*} castedStr
* @private
*/
exports.parseString = function(str) {
var lower = str.toLowerCase();
var num = Number(str); // will nicely fail with '123ab'
var numFloat = parseFloat(str); // will nicely fail with ' '
if (str === '') {
return '';
}
else if (lower === 'null') {
return null;
}
else if (lower === 'true') {
return true;
}
else if (lower === 'false') {
return false;
}
else if (!isNaN(num) && !isNaN(numFloat)) {
return num;
}
else {
return str;
}
};
/** /**
* Test whether a value is an Object * Test whether a value is an Object
* @param {*} value * @param {*} value

View File

@ -353,6 +353,15 @@ describe('util', function () {
}); });
}); });
it('should parse a string', function () {
assert.strictEqual(util.parseString('foo'), 'foo');
assert.strictEqual(util.parseString('234foo'), '234foo');
assert.strictEqual(util.parseString('2.3'), 2.3);
assert.strictEqual(util.parseString('null'), null);
assert.strictEqual(util.parseString('true'), true);
assert.strictEqual(util.parseString('false'), false);
})
it('should find a unique name', function () { it('should find a unique name', function () {
assert.strictEqual(util.findUniqueName('other', [ assert.strictEqual(util.findUniqueName('other', [
'a', 'a',