Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

journeyapps

Package Overview
Dependencies
Maintainers
1
Versions
586
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

journeyapps - npm Package Compare versions

Comparing version 0.2.27 to 0.2.28-alpha1

.editorconfig

426

app/evaluator.js

@@ -0,3 +1,218 @@

/**
* Abstract base token expression class.
* @param {string} expression
* @param {int} [start]
* @constructor
*/
function TokenExpression(expression, start) {
if (this.constructor === TokenExpression) {
throw new Error('Cannot instantiate abstract TokenExpression class!');
}
this.expression = expression;
this.start = start;
this.format = null;
}
/**
* @return {string}
*/
TokenExpression.prototype.toString = function() {
return '[object ' + this.constructor.name + ' <' + this.expression + ', ' + this.start + '>]';
};
/**
* @return {boolean}
*/
TokenExpression.prototype.isConstant = function() {
// not constant by default
return false;
};
/**
* @return {boolean}
*/
TokenExpression.prototype.isShorthand = function() {
// not shorthand by default
return false;
};
/**
* If the token expression is a function that needs to be called or not.
* @return {boolean}
*/
TokenExpression.prototype.isFunction = function() {
return false;
};
/**
* Constant token expression.
* @param {string} expression
* @param {int} [start]
* @constructor
*/
function ConstantTokenExpression(expression, start) {
TokenExpression.call(this, expression, start);
}
ConstantTokenExpression.prototype = Object.create(TokenExpression.prototype);
ConstantTokenExpression.prototype.constructor = ConstantTokenExpression;
/**
* Concatenate a token to current token and return a new token.
* @param {ConstantTokenExpression} token
* @return {ConstantTokenExpression} result token
*/
ConstantTokenExpression.prototype.concat = function(token) {
// start value should be start of first token
return new ConstantTokenExpression(this.expression + token.expression, this.start);
};
/**
* @return {boolean}
*/
ConstantTokenExpression.prototype.isConstant = function() {
return true;
};
/**
* Get the value of the constant token expression.
* @return {string}
*/
ConstantTokenExpression.prototype.valueOf = function() {
return this.expression;
};
/**
* JavaScript function token expression.
* @param {string} expression
* @param {int} [start]
* @constructor
*/
function FunctionTokenExpression(expression, start) {
// remove indicator prefix from expression
var prefix = FunctionTokenExpression.PREFIX;
var processedExpression = expression.trim();
if (processedExpression.indexOf(prefix) === 0) {
processedExpression = processedExpression.substr(prefix.length);
}
TokenExpression.call(this, processedExpression, start);
}
FunctionTokenExpression.prototype = Object.create(TokenExpression.prototype);
FunctionTokenExpression.prototype.constructor = FunctionTokenExpression;
/**
* Prefix for function token expressions.
* @type {string}
*/
FunctionTokenExpression.PREFIX = '$:';
FunctionTokenExpression.prototype.isFunction = function() {
return true;
};
/**
* Name of function represented by function token expression.
* @return {string}
*/
FunctionTokenExpression.prototype.functionName = function() {
return this.expression.substr(0, this.expression.indexOf('('));
};
/**
* Generate a constant token expression from function token expression.
* @param {boolean} [includeEscapeTags] if "{" and "}" format string escape tags should be included or not
* @return {ConstantTokenExpression}
*/
FunctionTokenExpression.prototype.toConstant = function(includeEscapeTags) {
if (typeof includeEscapeTags === 'undefined' || includeEscapeTags === null) {
includeEscapeTags = false;
}
var constantExpression = FunctionTokenExpression.PREFIX + this.expression;
if (includeEscapeTags) {
constantExpression = '{' + constantExpression + '}';
}
return new ConstantTokenExpression(constantExpression, this.start);
};
/**
* Legacy function token expression.
* @param {string} expression
* @param {int} [start]
* @constructor
*/
function LegacyFunctionTokenExpression(expression, start) {
TokenExpression.call(this, expression, start);
}
LegacyFunctionTokenExpression.prototype = Object.create(TokenExpression.prototype);
LegacyFunctionTokenExpression.prototype.constructor = LegacyFunctionTokenExpression;
LegacyFunctionTokenExpression.prototype.isFunction = function() {
return true;
};
/**
* Generate a constant token expression from legacy function token expression.
* @param {boolean} [includeEscapeTags] if "{" and "}" format string escape tags should be included or not
* @return {ConstantTokenExpression}
*/
LegacyFunctionTokenExpression.prototype.toConstant = function(includeEscapeTags) {
if (typeof includeEscapeTags === 'undefined' || includeEscapeTags === null) {
includeEscapeTags = false;
}
var constantExpression = this.expression;
if (includeEscapeTags) {
constantExpression = '{' + constantExpression + '}';
}
return new ConstantTokenExpression(constantExpression, this.start);
};
/**
* Shorthand token expression.
* @param {string} expression
* @param {int} [start]
* @constructor
*/
function ShorthandTokenExpression(expression, start) {
TokenExpression.call(this, expression, start);
}
ShorthandTokenExpression.prototype = Object.create(TokenExpression.prototype);
ShorthandTokenExpression.prototype.constructor = ShorthandTokenExpression;
/**
* @return {boolean}
*/
ShorthandTokenExpression.prototype.isShorthand = function() {
return true;
};
/**
* Shorthand token expression with format specifier.
* @param {string} expression
* @param {string} format
* @param {int} [start]
* @constructor
*/
function FormatShorthandTokenExpression(expression, format, start) {
// wraps ShorthandTokenExpression with format
TokenExpression.call(this, expression, start);
this.inner = new ShorthandTokenExpression(expression, start);
this.format = format;
}
FormatShorthandTokenExpression.prototype = Object.create(TokenExpression.prototype);
FormatShorthandTokenExpression.prototype.constructor = FormatShorthandTokenExpression;
/**
* @return {boolean}
*/
FormatShorthandTokenExpression.prototype.isShorthand = function() {
return this.inner.isShorthand();
};
/**
* @return {string}
*/
FormatShorthandTokenExpression.prototype.toString = function() {
return '[object ' + this.constructor.name + ' <' + this.expression + ', ' + this.start + ', ' + this.format + '>]';
};
// Unescape double closing braces to a single brace

@@ -21,6 +236,58 @@ function unescape(s) {

function parseEnclosingBraces(format) {
var i = format.indexOf('{');
if (i == -1) {
return null;
}
// We want to skip through these sections
// i.e. do not match { in a string, e.g. "{"
var SPECIAL_SECTIONS = ['\'', '"'];
for (var k = i + 1; k < format.length; k++) {
var character = format[k];
if (SPECIAL_SECTIONS.indexOf(character) != -1) {
// This is the start of a string, jump to its end
var endChar = format.indexOf(character, k + 1);
if (endChar == -1) {
// Unless the end doesn't exist. Error out.
return null;
}
k = endChar;
continue;
}
if (character == '{') {
// Start of pair of inner braces,
// recursively parse them
var inner = parseEnclosingBraces(format.substring(k));
if (!inner) {
// Faulty inner, return null
return null;
}
k += inner.length;
continue;
}
if (character == '}') {
// Found closing part for current level of braces
// Return the length to the caller
return {
length: (k - i)
};
}
}
// Came to end of loop without a match. Faulty, return null
return null;
}
/**
* Compile a format string expression into tokens.
* @param {string} format string expression
* @return {TokenExpression[]} compiled tokens
*/
function compile(format) {
var start = 0;
var split = [];
/** @type {TokenExpression[]} */
var tokens = [];
var len = format.length;

@@ -31,10 +298,10 @@ while(true) {

// end of string - everything is normal text
split.push(unescape(format.substring(start)));
tokens.push(new ConstantTokenExpression(unescape(format.substring(start)), start));
break;
}
// normal text in the gaps between curly braces
split.push(unescape(format.substring(start, i)));
tokens.push(new ConstantTokenExpression(unescape(format.substring(start, i)), start));
if(format[i+1] == '{') {
// Double left brace - escape and continue
split.push('{');
tokens.push(new ConstantTokenExpression('{', start));
start = i + 2;

@@ -44,50 +311,55 @@ continue;

var end = format.indexOf('}', i + 1);
if (end == -1) {
// No closing brace - treat as text
split.push(format.substring(i));
var parsedBraces = parseEnclosingBraces(format.substring(i));
if (!parsedBraces) {
// Brace pair faulty (no closing brace), return as a constant
tokens.push(new ConstantTokenExpression(format.substring(i), start));
break;
}
start = end + 1;
// Next start is at the end of the currently parsed brace pair
start = i + parsedBraces.length + 1;
// `spec` is everything between the curly braces "{" and "}".
var spec = format.substring(i + 1, end);
var spec = format.substring(i + 1, i + parsedBraces.length);
var colon = spec.indexOf(':');
var variable, formatSpec;
if(colon == -1) {
variable = spec;
formatSpec = null;
// test for function token prefix
if (spec.trim().indexOf(FunctionTokenExpression.PREFIX) === 0) {
// function token because the function name has "$:" as prefix (leading whitespace is ignored)
tokens.push(new FunctionTokenExpression(spec, i));
} else {
variable = spec.substring(0, colon);
formatSpec = spec.substring(colon+1);
// shorthand token
var colon = spec.indexOf(':');
if(colon == -1) {
tokens.push(new ShorthandTokenExpression(spec, i));
} else {
tokens.push(new FormatShorthandTokenExpression(spec.substring(0, colon), spec.substring(colon+1), i));
}
}
var value = {expression: variable, format: formatSpec, start: i};
split.push(value);
}
// Concatenate any neighbouring strings
// concatenate any neighbouring constant token expressions
/** @type {TokenExpression[]} */
var result = [];
/** @type {ConstantTokenExpression} */
var last = null;
for(var j = 0; j < split.length; j++) {
var token = split[j];
if(typeof token == 'string') {
if(last == null) {
if(token.length > 0) {
for (var j = 0; j < tokens.length; j++) {
var token = tokens[j];
if (token instanceof ConstantTokenExpression) {
if (last == null) {
if (token.expression.length > 0) {
last = token;
}
} else {
last += token;
last = last.concat(token);
}
} else {
if(last != null) {
if (last != null) {
result.push(last);
last = null;
}
result.push(token);
last = null;
}
}
if(last != null) {
if (last != null) {
result.push(last);

@@ -99,9 +371,17 @@ }

/**
* Construct a new format string expression.
* @param {string} expression
* @constructor
*/
function FormatString(expression) {
this.expression = expression || '';
/** @type {TokenExpression[]} */
this.tokens = compile(this.expression);
}
/**
* @return {string}
*/
FormatString.prototype.toString = function() {

@@ -111,2 +391,11 @@ return this.expression;

/**
* If the format string is constant (i.e. no values need to be evaluated).
* @return {boolean}
*/
FormatString.prototype.isConstant = function() {
// constants format strings will only contain a single constant token
return this.tokens.length == 1 && this.tokens[0].isConstant();
};
function getObjectType(parent, name) {

@@ -188,3 +477,3 @@ var variable = parent.getAttribute(name);

var token = tokens[i];
if(typeof token == 'object') {
if(token.isShorthand()) {
var expression = token.expression;

@@ -197,2 +486,6 @@ extract(type, expression, result, depth);

/**
* @param scopeType
* @return {Array}
*/
FormatString.prototype.validate = function(scopeType) {

@@ -204,5 +497,6 @@ var tokens = this.tokens;

for(var i = 0; i < tokens.length; i++) {
// validate all shorthand and function token expressions (ignore constant token expressions)
var token = tokens[i];
if (typeof token == 'object') {
var expression = token.expression;
var expression = token.expression;
if (token.isShorthand()) {
var warnQuestionMark = false;

@@ -214,4 +508,2 @@ if(expression.length > 0 && expression[0] == '?') {

var type = scopeType.getVariable(expression);

@@ -234,2 +526,5 @@ if(type == null) {

}
if (token.isFunction()) {
// TODO: validate that function exists in view
}
}

@@ -245,3 +540,3 @@ return results;

var token = tokens[i];
if (typeof token == 'object') {
if (!token.isConstant()) {
var expression = token.expression;

@@ -265,2 +560,7 @@ // We are interested in the type and name of the final two variables in the expression

/**
* Create format string.
* @param {string} expression
* @return {FormatString|null}
*/
function formatString(expression) {

@@ -274,7 +574,57 @@ if(expression == null) {

/**
# Construct a function token expression from a raw expression string.
* @param {string} expression
* @param {boolean} [allowLegacy=true] if legacy function token expressions are allowed (defaults to true)
* @return {FunctionTokenExpression|LegacyFunctionTokenExpression|null}
*/
function functionTokenExpression(expression, allowLegacy) {
if (typeof allowLegacy === 'undefined' || allowLegacy == null) {
allowLegacy = true; // default value
}
if (expression == null) {
return null;
}
if (expression.trim().indexOf(FunctionTokenExpression.PREFIX) === 0) {
return new FunctionTokenExpression(expression);
}
if (allowLegacy) {
// assume legacy function token expression (if allowed) at this point
return new LegacyFunctionTokenExpression(expression);
}
return null;
}
/**
* Create a token expression that can be evaluated.
* @param {string} expression
* @return {FunctionTokenExpression|ShorthandTokenExpression|FormatShorthandTokenExpression}
*/
function actionableTokenExpression(expression) {
if (expression == null) {
return null;
}
if (expression.trim().indexOf(FunctionTokenExpression.PREFIX) === 0) {
return new FunctionTokenExpression(expression);
}
var colon = expression.indexOf(':');
if(colon == -1) {
return new ShorthandTokenExpression(expression);
}
return new FormatShorthandTokenExpression(expression.substring(0, colon), expression.substring(colon+1));
}
module.exports = {
FormatString: FormatString,
compile: compile,
TokenExpression: TokenExpression,
ConstantTokenExpression: ConstantTokenExpression,
FunctionTokenExpression: FunctionTokenExpression,
LegacyFunctionTokenExpression: LegacyFunctionTokenExpression,
ShorthandTokenExpression: ShorthandTokenExpression,
FormatShorthandTokenExpression: FormatShorthandTokenExpression,
_compile: compile, // exposed for tests
formatString: formatString,
functionTokenExpression: functionTokenExpression,
actionableTokenExpression: actionableTokenExpression,
_deepMerge: deepMerge // Exposed for tests only
};

@@ -113,3 +113,3 @@ // # schema module

bind: xml.attribute.notBlank,
required: xml.attribute.optionList(['true', 'false']),
required: xml.attribute.optionListWithFunctions(['true', 'false'], evaluator.FunctionTokenExpression.PREFIX),
'show-if': xml.attribute.notBlank,

@@ -259,3 +259,3 @@ 'hide-if': xml.attribute.notBlank

label: xml.attribute.label,
state: xml.attribute.optionList(['normal', 'active', 'disabled']),
state: xml.attribute.optionListWithFunctions(['normal', 'active', 'disabled'], evaluator.FunctionTokenExpression.PREFIX),
icon: xml.attribute.path,

@@ -665,3 +665,11 @@ 'on-press': xml.attribute.notBlank,

function validateBinding(element, attr) {
/**
* @param element
* @param {string} attr
* @param {boolean} [allowFunctionBinding=false] if function bindings are allowed (defaults to false)
*/
function validateBinding(element, attr, allowFunctionBinding) {
if (typeof allowFunctionBinding === 'undefined' || allowFunctionBinding == null) {
allowFunctionBinding = false;
}
var binding = getAttribute(element, attr);

@@ -672,2 +680,10 @@ // TODO: somehow handle this automatically with getType.

}
if (allowFunctionBinding) {
var token = evaluator.functionTokenExpression(binding, false);
if (token != null) {
// binding is a function token expression
// TODO: validate that the function exists in the view (not done for `onpress` currently)
return;
}
}
var bindingType = view.type.getType(binding);

@@ -687,4 +703,4 @@ if(bindingType == null) {

}
validateBinding(element, 'show-if');
validateBinding(element, 'hide-if');
validateBinding(element, 'show-if', true);
validateBinding(element, 'hide-if', true);
}

@@ -691,0 +707,0 @@

@@ -270,2 +270,22 @@ // # xml module

/**
* @param {Array} options
* @param {string} functionPrefix
* @param {string} [customMessage]
* @return {Function}
*/
attribute.optionListWithFunctions = function optionListWithFunctions(options, functionPrefix, customMessage) {
return function(value, element) {
if (value.indexOf(functionPrefix) === 0) {
// function token expression, therefore allow
return value;
}
if(options.indexOf(value) == -1) {
var message = customMessage == null ? element.name + ' must be one of ' + options : customMessage;
throw new Error(message);
}
return value;
};
};
attribute.multiOptionList = function multiOptionList(options, customMessage) {

@@ -272,0 +292,0 @@

22

db/evaluator.js

@@ -212,4 +212,8 @@

var token = tokens[i];
if(typeof token == 'string') {
result += token;
if(token.isConstant()) {
result += token.valueOf();
} else if (token.isFunction()) {
// format strings used in the data model should not allow evaluation of functions
// therefore the literal expression string is used (with format string escape tags included)
result += token.toConstant(true).valueOf();
} else {

@@ -234,3 +238,3 @@ var value = formatValue(scope, token.expression, token.format);

var token = tokens[i];
if(typeof token == 'string') {
if(token.isConstant() || token.isFunction()) {
// Ignore for now

@@ -248,4 +252,8 @@ } else {

var token = tokens[i];
if(typeof token == 'string') {
result += token;
if(token.isConstant()) {
result += token.valueOf();
} else if (token.isFunction()) {
// format strings used in the data model should not allow evaluation of functions
// therefore the literal expression string is used (with format string escape tags included)
result += token.toConstant(true).valueOf();
} else {

@@ -267,6 +275,2 @@ result += results[promiseIndex];

evaluator.FormatString.prototype.isConstant = function() {
return this.tokens.length == 1 && typeof this.tokens[0] == 'string';
};
evaluator.getValue = getValue;

@@ -273,0 +277,0 @@ evaluator.setValue = setValue;

{
"name": "journeyapps",
"version": "0.2.27",
"version": "0.2.28-alpha1",
"description": "Journey JS library",

@@ -49,2 +49,3 @@ "keywords": [

"grunt-webpack": "^1.0.11",
"imports-loader": "^0.7.1",
"jasmine": "^2.4.1",

@@ -51,0 +52,0 @@ "jasmine-core": "^2.4.1",

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc