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
## 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
- 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);
}
else {
this.value = this._stringCast(String(this.value));
this.value = util.parseString(String(this.value));
}
this.focus();
@ -1550,7 +1550,7 @@ Node.prototype._getDomValue = function() {
}
else {
var str = this._unescapeHTML(this.valueInnerText);
value = this._stringCast(str);
value = util.parseString(str);
}
if (value !== this.value) {
this.value = value;
@ -4303,8 +4303,7 @@ Node.prototype.showContextMenu = function (anchor, onClose) {
title: translate('transformTitle', {type: this.type}),
className: 'jsoneditor-transform',
click: function () {
var anchor = node.editor.options.modalAnchor || DEFAULT_MODAL_ANCHOR;
showTransformModal(node, anchor)
node.showTransformModal();
}
});
}
@ -4456,7 +4455,7 @@ Node.prototype.showContextMenu = function (anchor, onClose) {
/**
* Show advanced sorting modal
* Show sorting modal
*/
Node.prototype.showSortModal = function () {
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
* @param {*} value
@ -4492,45 +4504,13 @@ Node.prototype._getType = function(value) {
if (value instanceof Object) {
return 'object';
}
if (typeof(value) == 'string' && typeof(this._stringCast(value)) != 'string') {
if (typeof(value) == 'string' && typeof(util.parseString(value)) != 'string') {
return 'string';
}
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
* @param {String} text

View File

@ -9,12 +9,14 @@ var MAX_PREVIEW_LINES = 100;
/**
* Show advanced filter and transform modal using JMESPath
* @param {Node} node the node to be transformed
* @param {HTMLElement} container The container where to center
* 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) {
var value = node.getValue();
function showTransformModal (container, json, onTransform) {
var value = json;
var content = '<label class="pico-modal-contents">' +
'<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)'
}
var paths = node.getChildPaths();
paths.forEach(function (path) {
var sortablePaths = util.getChildPaths(json);
sortablePaths.forEach(function (path) {
var formattedPath = preprocessPath(path);
var filterOption = document.createElement('option');
filterOption.text = formattedPath;
@ -133,12 +136,11 @@ function showTransformModal (node, container) {
sortField.appendChild(sortOption);
});
var allPaths = node.getChildPaths(true).filter(function(path) {
var selectablePaths = util.getChildPaths(json, true).filter(function(path) {
return path !== '.';
});
if (allPaths.length > 0) {
allPaths.forEach(function (path) {
if (selectablePaths.length > 0) {
selectablePaths.forEach(function (path) {
var formattedPath = preprocessPath(path);
var option = document.createElement('option');
option.text = formattedPath;
@ -194,10 +196,9 @@ function showTransformModal (node, container) {
? ['0'].concat(util.parsePath('.' + field1))
: ['0']
var exampleValue = util.get(value, examplePath)
// TODO: move _stringCast into a static util function
var value1 = typeof exampleValue === 'string'
? filterValue.value
: node._stringCast(filterValue.value);
: parseString(filterValue.value);
query.value = '[? ' +
field1 + ' ' +
@ -284,7 +285,7 @@ function showTransformModal (node, container) {
modal.close();
node.transform(query.value)
onTransform(query.value)
};
setTimeout(function () {

View File

@ -1,6 +1,7 @@
'use strict';
var ace = require('./ace');
var jmespath = require('jmespath');
var translate = require('./i18n').translate;
var ModeSwitcher = require('./ModeSwitcher');
var showSortModal = require('./showSortModal');
@ -173,24 +174,23 @@ textmode.create = function (container, options) {
sort.className = 'jsoneditor-sort';
sort.title = translate('sortTitleShort');
sort.onclick = function () {
me._showSortModal()
me._showSortModal();
};
this.menu.appendChild(sort);
}
// TODO
// // create transform button
// if (this.options.enableTransform) {
// var transform = document.createElement('button');
// transform.type = 'button';
// transform.title = translate('transformTitleShort');
// transform.className = 'jsoneditor-transform';
// transform.onclick = function () {
// var anchor = me.options.modalAnchor || DEFAULT_MODAL_ANCHOR;
// showTransformModal(me.node, anchor)
// };
// this.menu.appendChild(transform);
// }
// create transform button
if (this.options.enableTransform) {
var transform = document.createElement('button');
transform.type = 'button';
transform.title = translate('transformTitleShort');
transform.className = 'jsoneditor-transform';
transform.onclick = function () {
me._showTransformModal();
};
this.menu.appendChild(transform);
}
// create repair 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
* 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 util = require('./util');
var autocomplete = require('./autocomplete');
var showTransformModal = require('./showTransformModal');
var translate = require('./i18n').translate;
var setLanguages = require('./i18n').setLanguages;
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
var treemode = {};
@ -1033,8 +1030,7 @@ treemode._createFrame = function () {
transform.title = translate('transformTitleShort');
transform.className = 'jsoneditor-transform';
transform.onclick = function () {
var anchor = editor.options.modalAnchor || DEFAULT_MODAL_ANCHOR;
showTransformModal(editor.node, anchor)
editor.node.showTransformModal();
};
this.menu.appendChild(transform);
}

View File

@ -1264,6 +1264,38 @@ exports.sortObjectKeys = function (object, direction) {
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
* @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 () {
assert.strictEqual(util.findUniqueName('other', [
'a',