1
0
mirror of https://github.com/node-red/node-red.git synced 2023-10-10 13:36:53 +02:00

Add jsonata function help

This commit is contained in:
Nick O'Leary 2016-11-15 23:22:25 +00:00
parent d33029027f
commit 26f5305593
11 changed files with 318 additions and 73 deletions

View File

@ -152,6 +152,10 @@ module.exports = function(grunt) {
"public/vendor/vendor.css": [ "public/vendor/vendor.css": [
// TODO: resolve relative resource paths in // TODO: resolve relative resource paths in
// bootstrap/FA/jquery // bootstrap/FA/jquery
],
"public/vendor/jsonata/jsonata.js": [
"editor/vendor/jsonata/jsonata.js",
"editor/vendor/jsonata/formatter.js"
] ]
} }
} }
@ -161,7 +165,7 @@ module.exports = function(grunt) {
files: { files: {
'public/red/red.min.js': 'public/red/red.js', 'public/red/red.min.js': 'public/red/red.js',
'public/red/main.min.js': 'public/red/main.js', 'public/red/main.min.js': 'public/red/main.js',
'public/vendor/jsonata/jsonata.min.js': 'editor/vendor/jsonata/jsonata.js', 'public/vendor/jsonata/jsonata.min.js': 'public/vendor/jsonata/jsonata.js',
'public/vendor/ace/mode-jsonata.js': 'editor/vendor/jsonata/mode-jsonata.js', 'public/vendor/ace/mode-jsonata.js': 'editor/vendor/jsonata/mode-jsonata.js',
'public/vendor/ace/worker-jsonata.js': 'editor/vendor/jsonata/worker-jsonata.js' 'public/vendor/ace/worker-jsonata.js': 'editor/vendor/jsonata/worker-jsonata.js'
} }

View File

@ -23,7 +23,7 @@ RED.i18n = (function() {
dynamicLoad: false, dynamicLoad: false,
load:'current', load:'current',
ns: { ns: {
namespaces: ["editor","node-red"], namespaces: ["editor","node-red","jsonata"],
defaultNs: "editor" defaultNs: "editor"
}, },
fallbackLng: ['en-US'], fallbackLng: ['en-US'],

View File

@ -85,6 +85,7 @@
} }
return true; return true;
} }
var allOptions = { var allOptions = {
msg: {value:"msg",label:"msg.",validate:validateExpression}, msg: {value:"msg",label:"msg.",validate:validateExpression},
flow: {value:"flow",label:"flow.",validate:validateExpression}, flow: {value:"flow",label:"flow.",validate:validateExpression},
@ -103,9 +104,9 @@
expand:function() { expand:function() {
var that = this; var that = this;
RED.editor.editExpression({ RED.editor.editExpression({
value: this.value(), value: jsonata.format(this.value()),
complete: function(v) { complete: function(v) {
that.value(v); that.value(v.replace(/\s*\n\s*/g," "));
} }
}) })
} }

View File

@ -1392,6 +1392,7 @@ RED.editor = (function() {
text: RED._("common.label.done"), text: RED._("common.label.done"),
class: "primary", class: "primary",
click: function() { click: function() {
$("#node-input-expression-help").html("");
onComplete(expressionEditor.getValue()); onComplete(expressionEditor.getValue());
RED.tray.close(); RED.tray.close();
} }
@ -1412,8 +1413,19 @@ RED.editor = (function() {
}, },
open: function(tray) { open: function(tray) {
var trayBody = tray.find('.editor-tray-body'); var trayBody = tray.find('.editor-tray-body');
var dialogForm = buildEditForm(tray,'dialog-form','_expression'); var dialogForm = buildEditForm(tray,'dialog-form','_expression','editor');
var funcSelect = $("#node-input-expression-func");
jsonata.functions.forEach(function(f) {
funcSelect.append($("<option></option>").val(f).text(f));
})
funcSelect.change(function(e) {
var f = $(this).val();
var args = RED._('jsonata:'+f+".args",{defaultValue:''});
var title = "<h4>"+f+"("+args+")</h4>";
var body = marked(RED._('jsonata:'+f+'.desc',{defaultValue:''}));
$("#node-input-expression-help").html(title+"<p>"+body+"</p>");
})
expressionEditor = RED.editor.createEditor({ expressionEditor = RED.editor.createEditor({
id: 'node-input-expression', id: 'node-input-expression',
value: "", value: "",
@ -1426,6 +1438,15 @@ RED.editor = (function() {
}); });
expressionEditor.getSession().setValue(value||"",-1); expressionEditor.getSession().setValue(value||"",-1);
expressionEditor.on("changeSelection", function() {
var c = expressionEditor.getCursorPosition();
var token = expressionEditor.getSession().getTokenAt(c.row,c.column);
//console.log(token);
if (token && token.type === 'keyword') {
funcSelect.val(token.value).change();
}
});
dialogForm.i18n(); dialogForm.i18n();
}, },

View File

@ -161,7 +161,13 @@
<script type="text/x-red" data-template-name="_expression"> <script type="text/x-red" data-template-name="_expression">
<div class="form-row node-text-editor-row"> <div class="form-row node-text-editor-row">
<div style="height: 250px;" class="node-text-editor" id="node-input-expression"></div> <div style="height: 200px;min-height: 150px;" class="node-text-editor" id="node-input-expression"></div>
</div>
<div class="form-row">
<label for="node-input-expression-func" data-i18n="expressionEditor.functions"></label>
<select id="node-input-expression-func"></select>
<div style="min-height: 200px;" id="node-input-expression-help"></div>
</div> </div>
</script> </script>

109
editor/vendor/jsonata/formatter.js vendored Normal file
View File

@ -0,0 +1,109 @@
(function() {
function indentLine(str,length) {
if (length <= 0) {
return str;
}
var i = (new Array(length)).join(" ");
str = str.replace(/^\s*/,i);
return str;
}
function formatExpression(str) {
var length = str.length;
var start = 0;
var inString = false;
var inBox = false;
var quoteChar;
var list = [];
var stack = [];
var frame;
var v;
var matchingBrackets = {
"(":")",
"[":"]",
"{":"}"
}
for (var i=0;i<length;i++) {
var c = str[i];
if (!inString) {
if (c === "'" || c === '"') {
inString = true;
quoteChar = c;
frame = {type:"string",pos:i};
list.push(frame);
stack.push(frame);
} else if (c === ";") {
frame = {type:";",pos:i};
list.push(frame);
} else if (c === ",") {
frame = {type:",",pos:i};
list.push(frame);
} else if (/[\(\[\{]/.test(c)) {
frame = {type:"open-block",char:c,pos:i};
list.push(frame);
stack.push(frame);
} else if (/[\}\)\]]/.test(c)) {
var oldFrame = stack.pop();
if (matchingBrackets[oldFrame.char] !== c) {
//console.log("Stack frame mismatch",c,"at",i,"expected",matchingBrackets[oldFrame.char],"from",oldFrame.pos);
return str;
}
//console.log("Closing",c,"at",i,"compare",oldFrame.type,oldFrame.pos);
oldFrame.width = i-oldFrame.pos;
frame = {type:"close-block",pos:i,char:c,width:oldFrame.width}
list.push(frame);
}
} else {
if (c === quoteChar) {
// Next char must be a ]
inString = false;
stack.pop();
}
}
}
// console.log(stack);
var result = str;
var indent = 0;
var offset = 0;
var pre,post,indented;
var longStack = [];
list.forEach(function(f) {
if (f.type === ";" || f.type === ",") {
if (longStack[longStack.length-1]) {
pre = result.substring(0,offset+f.pos+1);
post = result.substring(offset+f.pos+1);
indented = indentLine(post,indent);
result = pre+"\n"+indented;
offset += indented.length-post.length+1;
}
} else if (f.type === "open-block") {
if (f.width > 30) {
longStack.push(true);
indent += 4;
pre = result.substring(0,offset+f.pos+1);
post = result.substring(offset+f.pos+1);
indented = indentLine(post,indent);
result = pre+"\n"+indented;
offset += indented.length-post.length+1;
} else {
longStack.push(false);
}
} else if (f.type === "close-block") {
if (f.width > 30) {
indent -= 4;
pre = result.substring(0,offset+f.pos);
post = result.substring(offset+f.pos);
indented = indentLine(post,indent);
result = pre+"\n"+indented;
offset += indented.length-post.length+1;
}
longStack.pop();
}
})
//console.log(result);
return result;
}
jsonata.format = formatExpression;
jsonata.functions = ["$sum", "$count", "$max", "$min", "$average", "$string", "$substring", "$substringBefore", "$substringAfter", "$lowercase", "$uppercase", "$length", "$split", "$join", "$number", "$boolean", "$not", "$map", "$reduce", "$keys", "$lookup", "$append", "$exists", "$spread"]
})();

View File

@ -1,8 +1,10 @@
define("ace/mode/jsonata",["require","exports","module","ace/lib/oop","ace/mode/text_highlight_rules","ace/worker/worker_client","ace/mode/text"], function(require, exports, module) { define("ace/mode/jsonata",["require","exports","module","ace/lib/oop","ace/mode/text_highlight_rules","ace/worker/worker_client","ace/mode/text"], function(require, exports, module) {
"use strict"; "use strict";
var oop = require("../lib/oop"); var oop = require("../lib/oop");
var TextHighlightRules = require("./text_highlight_rules").TextHighlightRules; var TextHighlightRules = require("./text_highlight_rules").TextHighlightRules;
var WorkerClient = require("../worker/worker_client").WorkerClient; var WorkerClient = require("../worker/worker_client").WorkerClient;
var JSONataHighlightRules = function() { var JSONataHighlightRules = function() {
@ -12,6 +14,8 @@ define("ace/mode/jsonata",["require","exports","module","ace/lib/oop","ace/mode/
"and|or|in", "and|or|in",
"constant.language": "constant.language":
"null|Infinity|NaN|undefined", "null|Infinity|NaN|undefined",
"constant.language.boolean":
"true|false",
"storage.type": "storage.type":
"function", "function",
"keyword": "keyword":
@ -39,16 +43,12 @@ define("ace/mode/jsonata",["require","exports","module","ace/lib/oop","ace/mode/
token : "constant.numeric", // float token : "constant.numeric", // float
regex : /[+-]?\d[\d_]*(?:(?:\.\d*)?(?:[eE][+-]?\d+)?)?\b/ regex : /[+-]?\d[\d_]*(?:(?:\.\d*)?(?:[eE][+-]?\d+)?)?\b/
}, },
{
token : keywordMapper,
regex : "[a-zA-Z\\$_\u00a1-\uffff][a-zA-Z\\d\\$_\u00a1-\uffff]*"
},
{ token: "keyword", { token: "keyword",
regex: /λ/ regex: /λ/
}, },
{ {
token: "constant.language.boolean", token : keywordMapper,
regex: "true|false" regex : "[a-zA-Z\\$_\u00a1-\uffff][a-zA-Z\\d\\$_\u00a1-\uffff]*"
}, },
{ {
token : "punctuation.operator", token : "punctuation.operator",
@ -119,8 +119,6 @@ define("ace/mode/jsonata",["require","exports","module","ace/lib/oop","ace/mode/
return worker; return worker;
}; };
this.$id = "ace/mode/jsonata"; this.$id = "ace/mode/jsonata";
}).call(Mode.prototype); }).call(Mode.prototype);

View File

@ -11,7 +11,7 @@ if (!window.console) {
postMessage({type: "log", data: msgs}); postMessage({type: "log", data: msgs});
}; };
window.console.error = window.console.error =
window.console.warn = window.console.warn =
window.console.log = window.console.log =
window.console.trace = window.console; window.console.trace = window.console;
} }
@ -23,7 +23,7 @@ window.onerror = function(message, file, line, col, err) {
message: message, message: message,
data: err.data, data: err.data,
file: file, file: file,
line: line, line: line,
col: col, col: col,
stack: err.stack stack: err.stack
}}); }});
@ -39,13 +39,13 @@ window.normalizeModule = function(parentId, moduleName) {
if (moduleName.charAt(0) == ".") { if (moduleName.charAt(0) == ".") {
var base = parentId.split("/").slice(0, -1).join("/"); var base = parentId.split("/").slice(0, -1).join("/");
moduleName = (base ? base + "/" : "") + moduleName; moduleName = (base ? base + "/" : "") + moduleName;
while (moduleName.indexOf(".") !== -1 && previous != moduleName) { while (moduleName.indexOf(".") !== -1 && previous != moduleName) {
var previous = moduleName; var previous = moduleName;
moduleName = moduleName.replace(/^\.\//, "").replace(/\/\.\//, "/").replace(/[^\/]+\/\.\.\//, ""); moduleName = moduleName.replace(/^\.\//, "").replace(/\/\.\//, "/").replace(/[^\/]+\/\.\.\//, "");
} }
} }
return moduleName; return moduleName;
}; };
@ -67,13 +67,13 @@ window.require = function require(parentId, id) {
} }
return module.exports; return module.exports;
} }
if (!window.require.tlns) if (!window.require.tlns)
return console.log("unable to load " + id); return console.log("unable to load " + id);
var path = resolveModuleId(id, window.require.tlns); var path = resolveModuleId(id, window.require.tlns);
if (path.slice(-3) != ".js") path += ".js"; if (path.slice(-3) != ".js") path += ".js";
window.require.id = id; window.require.id = id;
window.require.modules[id] = {}; // prevent infinite loop on broken modules window.require.modules[id] = {}; // prevent infinite loop on broken modules
importScripts(path); importScripts(path);
@ -112,7 +112,7 @@ window.define = function(id, deps, factory) {
deps = []; deps = [];
id = window.require.id; id = window.require.id;
} }
if (typeof factory != "function") { if (typeof factory != "function") {
window.require.modules[id] = { window.require.modules[id] = {
exports: factory, exports: factory,
@ -163,13 +163,13 @@ window.initSender = function initSender() {
var EventEmitter = window.require("ace/lib/event_emitter").EventEmitter; var EventEmitter = window.require("ace/lib/event_emitter").EventEmitter;
var oop = window.require("ace/lib/oop"); var oop = window.require("ace/lib/oop");
var Sender = function() {}; var Sender = function() {};
(function() { (function() {
oop.implement(this, EventEmitter); oop.implement(this, EventEmitter);
this.callback = function(data, callbackId) { this.callback = function(data, callbackId) {
postMessage({ postMessage({
type: "call", type: "call",
@ -177,7 +177,7 @@ window.initSender = function initSender() {
data: data data: data
}); });
}; };
this.emit = function(name, data) { this.emit = function(name, data) {
postMessage({ postMessage({
type: "event", type: "event",
@ -185,9 +185,9 @@ window.initSender = function initSender() {
data: data data: data
}); });
}; };
}).call(Sender.prototype); }).call(Sender.prototype);
return new Sender(); return new Sender();
}; };
@ -517,7 +517,7 @@ function validateDelta(docLines, delta) {
} }
exports.applyDelta = function(docLines, delta, doNotValidate) { exports.applyDelta = function(docLines, delta, doNotValidate) {
var row = delta.start.row; var row = delta.start.row;
var startColumn = delta.start.column; var startColumn = delta.start.column;
var line = docLines[row] || ""; var line = docLines[row] || "";
@ -582,7 +582,7 @@ EventEmitter._dispatchEvent = function(eventName, e) {
if (e.propagationStopped) if (e.propagationStopped)
break; break;
} }
if (defaultHandler && !e.defaultPrevented) if (defaultHandler && !e.defaultPrevented)
return defaultHandler(e, this); return defaultHandler(e, this);
}; };
@ -610,7 +610,7 @@ EventEmitter.setDefaultHandler = function(eventName, callback) {
var handlers = this._defaultHandlers var handlers = this._defaultHandlers
if (!handlers) if (!handlers)
handlers = this._defaultHandlers = {_disabled_: {}}; handlers = this._defaultHandlers = {_disabled_: {}};
if (handlers[eventName]) { if (handlers[eventName]) {
var old = handlers[eventName]; var old = handlers[eventName];
var disabled = handlers._disabled_[eventName]; var disabled = handlers._disabled_[eventName];
@ -618,7 +618,7 @@ EventEmitter.setDefaultHandler = function(eventName, callback) {
handlers._disabled_[eventName] = disabled = []; handlers._disabled_[eventName] = disabled = [];
disabled.push(old); disabled.push(old);
var i = disabled.indexOf(callback); var i = disabled.indexOf(callback);
if (i != -1) if (i != -1)
disabled.splice(i, 1); disabled.splice(i, 1);
} }
handlers[eventName] = callback; handlers[eventName] = callback;
@ -628,7 +628,7 @@ EventEmitter.removeDefaultHandler = function(eventName, callback) {
if (!handlers) if (!handlers)
return; return;
var disabled = handlers._disabled_[eventName]; var disabled = handlers._disabled_[eventName];
if (handlers[eventName] == callback) { if (handlers[eventName] == callback) {
var old = handlers[eventName]; var old = handlers[eventName];
if (disabled) if (disabled)
@ -684,7 +684,7 @@ var EventEmitter = require("./lib/event_emitter").EventEmitter;
var Anchor = exports.Anchor = function(doc, row, column) { var Anchor = exports.Anchor = function(doc, row, column) {
this.$onChange = this.onChange.bind(this); this.$onChange = this.onChange.bind(this);
this.attach(doc); this.attach(doc);
if (typeof column == "undefined") if (typeof column == "undefined")
this.setPosition(row.row, row.column); this.setPosition(row.row, row.column);
else else
@ -707,16 +707,16 @@ var Anchor = exports.Anchor = function(doc, row, column) {
if (delta.start.row > this.row) if (delta.start.row > this.row)
return; return;
var point = $getTransformedPoint(delta, {row: this.row, column: this.column}, this.$insertRight); var point = $getTransformedPoint(delta, {row: this.row, column: this.column}, this.$insertRight);
this.setPosition(point.row, point.column, true); this.setPosition(point.row, point.column, true);
}; };
function $pointsInOrder(point1, point2, equalPointsInOrder) { function $pointsInOrder(point1, point2, equalPointsInOrder) {
var bColIsAfter = equalPointsInOrder ? point1.column <= point2.column : point1.column < point2.column; var bColIsAfter = equalPointsInOrder ? point1.column <= point2.column : point1.column < point2.column;
return (point1.row < point2.row) || (point1.row == point2.row && bColIsAfter); return (point1.row < point2.row) || (point1.row == point2.row && bColIsAfter);
} }
function $getTransformedPoint(delta, point, moveIfEqual) { function $getTransformedPoint(delta, point, moveIfEqual) {
var deltaIsInsert = delta.action == "insert"; var deltaIsInsert = delta.action == "insert";
var deltaRowShift = (deltaIsInsert ? 1 : -1) * (delta.end.row - delta.start.row); var deltaRowShift = (deltaIsInsert ? 1 : -1) * (delta.end.row - delta.start.row);
@ -735,7 +735,7 @@ var Anchor = exports.Anchor = function(doc, row, column) {
column: point.column + (point.row == deltaEnd.row ? deltaColShift : 0) column: point.column + (point.row == deltaEnd.row ? deltaColShift : 0)
}; };
} }
return { return {
row: deltaStart.row, row: deltaStart.row,
column: deltaStart.column column: deltaStart.column
@ -919,23 +919,23 @@ var Document = function(textOrLines) {
this.insert = function(position, text) { this.insert = function(position, text) {
if (this.getLength() <= 1) if (this.getLength() <= 1)
this.$detectNewLine(text); this.$detectNewLine(text);
return this.insertMergedLines(position, this.$split(text)); return this.insertMergedLines(position, this.$split(text));
}; };
this.insertInLine = function(position, text) { this.insertInLine = function(position, text) {
var start = this.clippedPos(position.row, position.column); var start = this.clippedPos(position.row, position.column);
var end = this.pos(position.row, position.column + text.length); var end = this.pos(position.row, position.column + text.length);
this.applyDelta({ this.applyDelta({
start: start, start: start,
end: end, end: end,
action: "insert", action: "insert",
lines: [text] lines: [text]
}, true); }, true);
return this.clonePos(end); return this.clonePos(end);
}; };
this.clippedPos = function(row, column) { this.clippedPos = function(row, column) {
var length = this.getLength(); var length = this.getLength();
if (row === undefined) { if (row === undefined) {
@ -952,15 +952,15 @@ var Document = function(textOrLines) {
column = Math.min(Math.max(column, 0), line.length); column = Math.min(Math.max(column, 0), line.length);
return {row: row, column: column}; return {row: row, column: column};
}; };
this.clonePos = function(pos) { this.clonePos = function(pos) {
return {row: pos.row, column: pos.column}; return {row: pos.row, column: pos.column};
}; };
this.pos = function(row, column) { this.pos = function(row, column) {
return {row: row, column: column}; return {row: row, column: column};
}; };
this.$clipPosition = function(position) { this.$clipPosition = function(position) {
var length = this.getLength(); var length = this.getLength();
if (position.row >= length) { if (position.row >= length) {
@ -984,21 +984,21 @@ var Document = function(textOrLines) {
column = this.$lines[row].length; column = this.$lines[row].length;
} }
this.insertMergedLines({row: row, column: column}, lines); this.insertMergedLines({row: row, column: column}, lines);
}; };
this.insertMergedLines = function(position, lines) { this.insertMergedLines = function(position, lines) {
var start = this.clippedPos(position.row, position.column); var start = this.clippedPos(position.row, position.column);
var end = { var end = {
row: start.row + lines.length - 1, row: start.row + lines.length - 1,
column: (lines.length == 1 ? start.column : 0) + lines[lines.length - 1].length column: (lines.length == 1 ? start.column : 0) + lines[lines.length - 1].length
}; };
this.applyDelta({ this.applyDelta({
start: start, start: start,
end: end, end: end,
action: "insert", action: "insert",
lines: lines lines: lines
}); });
return this.clonePos(end); return this.clonePos(end);
}; };
this.remove = function(range) { this.remove = function(range) {
@ -1015,14 +1015,14 @@ var Document = function(textOrLines) {
this.removeInLine = function(row, startColumn, endColumn) { this.removeInLine = function(row, startColumn, endColumn) {
var start = this.clippedPos(row, startColumn); var start = this.clippedPos(row, startColumn);
var end = this.clippedPos(row, endColumn); var end = this.clippedPos(row, endColumn);
this.applyDelta({ this.applyDelta({
start: start, start: start,
end: end, end: end,
action: "remove", action: "remove",
lines: this.getLinesForRange({start: start, end: end}) lines: this.getLinesForRange({start: start, end: end})
}, true); }, true);
return this.clonePos(start); return this.clonePos(start);
}; };
this.removeFullLines = function(firstRow, lastRow) { this.removeFullLines = function(firstRow, lastRow) {
@ -1033,10 +1033,10 @@ var Document = function(textOrLines) {
var startRow = ( deleteFirstNewLine ? firstRow - 1 : firstRow ); var startRow = ( deleteFirstNewLine ? firstRow - 1 : firstRow );
var startCol = ( deleteFirstNewLine ? this.getLine(startRow).length : 0 ); var startCol = ( deleteFirstNewLine ? this.getLine(startRow).length : 0 );
var endRow = ( deleteLastNewLine ? lastRow + 1 : lastRow ); var endRow = ( deleteLastNewLine ? lastRow + 1 : lastRow );
var endCol = ( deleteLastNewLine ? 0 : this.getLine(endRow).length ); var endCol = ( deleteLastNewLine ? 0 : this.getLine(endRow).length );
var range = new Range(startRow, startCol, endRow, endCol); var range = new Range(startRow, startCol, endRow, endCol);
var deletedLines = this.$lines.slice(firstRow, lastRow + 1); var deletedLines = this.$lines.slice(firstRow, lastRow + 1);
this.applyDelta({ this.applyDelta({
start: range.start, start: range.start,
end: range.end, end: range.end,
@ -1071,7 +1071,7 @@ var Document = function(textOrLines) {
else { else {
end = range.start; end = range.start;
} }
return end; return end;
}; };
this.applyDeltas = function(deltas) { this.applyDeltas = function(deltas) {
@ -1090,17 +1090,17 @@ var Document = function(textOrLines) {
: !Range.comparePoints(delta.start, delta.end)) { : !Range.comparePoints(delta.start, delta.end)) {
return; return;
} }
if (isInsert && delta.lines.length > 20000) if (isInsert && delta.lines.length > 20000)
this.$splitAndapplyLargeDelta(delta, 20000); this.$splitAndapplyLargeDelta(delta, 20000);
applyDelta(this.$lines, delta, doNotValidate); applyDelta(this.$lines, delta, doNotValidate);
this._signal("change", delta); this._signal("change", delta);
}; };
this.$splitAndapplyLargeDelta = function(delta, MAX) { this.$splitAndapplyLargeDelta = function(delta, MAX) {
var lines = delta.lines; var lines = delta.lines;
var l = lines.length; var l = lines.length;
var row = delta.start.row; var row = delta.start.row;
var column = delta.start.column; var column = delta.start.column;
var from = 0, to = 0; var from = 0, to = 0;
do { do {
@ -1203,7 +1203,7 @@ exports.copyArray = function(array){
for (var i=0, l=array.length; i<l; i++) { for (var i=0, l=array.length; i<l; i++) {
if (array[i] && typeof array[i] == "object") if (array[i] && typeof array[i] == "object")
copy[i] = this.copyObject(array[i]); copy[i] = this.copyObject(array[i]);
else else
copy[i] = array[i]; copy[i] = array[i];
} }
return copy; return copy;
@ -1222,7 +1222,7 @@ exports.deepCopy = function deepCopy(obj) {
} }
if (Object.prototype.toString.call(obj) !== "[object Object]") if (Object.prototype.toString.call(obj) !== "[object Object]")
return obj; return obj;
copy = {}; copy = {};
for (var key in obj) for (var key in obj)
copy[key] = deepCopy(obj[key]); copy[key] = deepCopy(obj[key]);
@ -1299,7 +1299,7 @@ exports.deferredCall = function(fcn) {
timer = null; timer = null;
return deferred; return deferred;
}; };
deferred.isPending = function() { deferred.isPending = function() {
return timer; return timer;
}; };
@ -1350,13 +1350,13 @@ define("ace/worker/mirror",["require","exports","module","ace/range","ace/docume
var Range = require("../range").Range; var Range = require("../range").Range;
var Document = require("../document").Document; var Document = require("../document").Document;
var lang = require("../lib/lang"); var lang = require("../lib/lang");
var Mirror = exports.Mirror = function(sender) { var Mirror = exports.Mirror = function(sender) {
this.sender = sender; this.sender = sender;
var doc = this.doc = new Document(""); var doc = this.doc = new Document("");
var deferredUpdate = this.deferredUpdate = lang.delayedCall(this.onUpdate.bind(this)); var deferredUpdate = this.deferredUpdate = lang.delayedCall(this.onUpdate.bind(this));
var _self = this; var _self = this;
sender.on("change", function(e) { sender.on("change", function(e) {
var data = e.data; var data = e.data;
@ -1379,29 +1379,29 @@ var Mirror = exports.Mirror = function(sender) {
}; };
(function() { (function() {
this.$timeout = 500; this.$timeout = 500;
this.setTimeout = function(timeout) { this.setTimeout = function(timeout) {
this.$timeout = timeout; this.$timeout = timeout;
}; };
this.setValue = function(value) { this.setValue = function(value) {
this.doc.setValue(value); this.doc.setValue(value);
this.deferredUpdate.schedule(this.$timeout); this.deferredUpdate.schedule(this.$timeout);
}; };
this.getValue = function(callbackId) { this.getValue = function(callbackId) {
this.sender.callback(this.doc.getValue(), callbackId); this.sender.callback(this.doc.getValue(), callbackId);
}; };
this.onUpdate = function() { this.onUpdate = function() {
}; };
this.isPending = function() { this.isPending = function() {
return this.deferredUpdate.isPending(); return this.deferredUpdate.isPending();
}; };
}).call(Mirror.prototype); }).call(Mirror.prototype);
}); });
@ -3469,6 +3469,7 @@ define("ace/mode/jsonata_worker",["require","exports","module","ace/lib/oop","ac
staticFrame.bind('append', functionAppend); staticFrame.bind('append', functionAppend);
staticFrame.bind('exists', functionExists); staticFrame.bind('exists', functionExists);
staticFrame.bind('spread', functionSpread); staticFrame.bind('spread', functionSpread);
function jsonata(expr) { function jsonata(expr) {
var ast = parser(expr); var ast = parser(expr);
var environment = createFrame(staticFrame); var environment = createFrame(staticFrame);
@ -3521,10 +3522,12 @@ define("ace/mode/jsonata_worker",["require","exports","module","ace/lib/oop","ac
jsonata(value); jsonata(value);
} catch (e) { } catch (e) {
var pos = this.doc.indexToPosition(e.position-1); var pos = this.doc.indexToPosition(e.position-1);
var msg = e.message;
msg = msg.replace(/ at column \d+/,"");
errors.push({ errors.push({
row: pos.row, row: pos.row,
column: pos.column, column: pos.column,
text: e.message, text: msg,
type: "error" type: "error"
}); });
} }
@ -3601,7 +3604,7 @@ if ([1,2].splice(0).length != 2) {
return a; return a;
} }
var array = [], lengthBefore; var array = [], lengthBefore;
array.splice.apply(array, makeArray(20)); array.splice.apply(array, makeArray(20));
array.splice.apply(array, makeArray(26)); array.splice.apply(array, makeArray(26));
@ -3642,7 +3645,7 @@ if ([1,2].splice(0).length != 2) {
var removed = this.slice(pos, pos+removeCount); var removed = this.slice(pos, pos+removeCount);
var insert = slice.call(arguments, 2); var insert = slice.call(arguments, 2);
var add = insert.length; var add = insert.length;
if (pos === length) { if (pos === length) {
if (add) { if (add) {
this.push.apply(this, insert); this.push.apply(this, insert);

View File

@ -155,6 +155,8 @@ function init(_server,_runtime) {
} }
} }
function start() { function start() {
i18n.registerMessageCatalog("jsonata",path.resolve(path.join(__dirname,"locales")),"jsonata.json")
return i18n.registerMessageCatalog("editor",path.resolve(path.join(__dirname,"locales")),"editor.json").then(function(){ return i18n.registerMessageCatalog("editor",path.resolve(path.join(__dirname,"locales")),"editor.json").then(function(){
comms.start(); comms.start();
}); });

View File

@ -318,5 +318,8 @@
}, },
"search": { "search": {
"empty": "No matches found" "empty": "No matches found"
},
"expressionEditor": {
"functions": "Functions"
} }
} }

View File

@ -0,0 +1,98 @@
{
"$string": {
"args": "arg",
"desc": "Casts the *arg* parameter to a string using the following casting rules:\n\n - Strings are unchanged\n - Functions are converted to an empty string\n - Numeric infinity and NaN throw an error because they cannot be represented as a JSON number\n - All other values are converted to a JSON string using the `JSON.stringify` function"
},
"$length": {
"args": "str",
"desc": "Returns the number of characters in the string `str`. An error is thrown if `str` is not a string."
},
"$substring": {
"args": "str, start[, length]",
"desc": "Returns a string containing the characters in the first parameter `str` starting at position `start` (zero-offset). If `length` is specified, then the substring will contain maximum `length` characters. If `start` is negative then it indicates the number of characters from the end of `str`."
},
"$substringBefore": {
"args": "str, chars",
"desc": "Returns the substring before the first occurrence of the character sequence chars in `str`. If `str` does not contain `chars`, then it returns `str`."
},
"$substringAfter": {
"args": "str, chars",
"desc": "Returns the substring after the first occurrence of the character sequence `chars` in `str`. If `str` does not contain `chars`, then it returns `str`."
},
"$uppercase": {
"args": "str",
"desc": "Returns a string with all the characters of `str` converted to uppercase."
},
"$lowercase": {
"args": "str",
"desc": "Returns a string with all the characters of `str` converted to lowercase."
},
"$split": {
"args": "str[, separator][, limit]",
"desc": "Splits the `str` parameter into an array of substrings. It is an error if `str` is not a string. The optional `separator` parameter specifies the characters within the `str` about which it should be split. If `separator` is not specified, then the empty string is assumed, and `str` will be split into an array of single characters. It is an error if `separator` is not a string. The optional `limit` parameter is a number that specifies the maximum number of substrings to include in the resultant array. Any additional substrings are discarded. If `limit` is not specified, then `str` is fully split with no limit to the size of the resultant array. It is an error if `limit` is not a non-negative number."
},
"$join": {
"args": "array[, separator]",
"desc": "Joins an array of component strings into a single concatenated string with each component string separated by the optional `separator` parameter. It is an error if the input `array` contains an item which isn't a string. If `separator` is not specified, then it is assumed to be the empty string, i.e. no `separator` between the component strings. It is an error if `separator` is not a string."
},
"$number": {
"args": "arg",
"desc": "Casts the `arg` parameter to a number using the following casting rules:\n\n - Numbers are unchanged\n - Strings that contain a sequence of characters that represent a legal JSON number are converted to that number\n - All other values cause an error to be thrown."
},
"$sum": {
"args": "array",
"desc": "Returns the arithmetic sum of an `array` of numbers. It is an error if the input `array` contains an item which isn't a number."
},
"$max": {
"args": "array",
"desc": "Returns the maximum number in an `array` of numbers. It is an error if the input `array` contains an item which isn't a number."
},
"$min": {
"args": "array",
"desc": "Returns the minimum number in an `array` of numbers. It is an error if the input `array` contains an item which isn't a number."
},
"$average": {
"args": "array",
"desc": "Returns the mean value of an `array` of numbers. It is an error if the input `array` contains an item which isn't a number."
},
"$boolean": {
"args": "arg",
"desc": "Casts the argument to a Boolean using the following rules:\n\n - `Boolean` : unchanged\n - `string`: empty : `false`\n - `string`: non-empty : `true`\n - `number`: `0` : `false`\n - `number`: non-zero : `true`\n - `null` : `false`\n - `array`: empty : `false`\n - `array`: contains a member that casts to `true` : `true`\n - `array`: all members cast to `false` : `false`\n - `object`: empty : `false`\n - `object`: non-empty : `true`\n - `function` : `false`"
},
"$not": {
"args": "arg",
"desc": "Returns Boolean NOT on the argument. `arg` is first cast to a boolean"
},
"$exists": {
"args": "arg",
"desc": "Returns Boolean `true` if the `arg` expression evaluates to a value, or `false` if the expression does not match anything (e.g. a path to a non-existent field reference)."
},
"$count": {
"args": "array",
"desc": "Returns the number of items in the array"
},
"$append": {
"args": "array, array",
"desc": "Appends two arrays"
},
"$keys": {
"args": "object",
"desc": "Returns an array containing the keys in the object. If the argument is an array of objects, then the array returned contains a de-duplicated list of all the keys in all of the objects."
},
"$lookup": {
"args": "object, key",
"desc": "Returns the value associated with key in object. If the first argument is an array of objects, then all of the objects in the array are searched, and the values associated with all occurrences of key are returned."
},
"$spread": {
"args": "object",
"desc": "Splits an object containing key/value pairs into an array of objects, each of which has a single key/value pair from the input object. If the parameter is an array of objects, then the resultant array contains an object for every key/value pair in every object in the supplied array."
}
}