Files
Alexander Vakrilov cc97a16800 feat: Scoped Packages (#7911)
* chore: move tns-core-modules to nativescript-core

* chore: preparing compat generate script

* chore: add missing definitions

* chore: no need for http-request to be private

* chore: packages chore

* test: generate tests for tns-core-modules

* chore: add anroid module for consistency

* chore: add .npmignore

* chore: added privateModulesWhitelist

* chore(webpack): added bundle-entry-points

* chore: scripts

* chore: tests changed to use @ns/core

* test: add scoped-packages test project

* test: fix types

* test: update test project

* chore: build scripts

* chore: update build script

* chore: npm scripts cleanup

* chore: make the compat pgk work with old wp config

* test: generate diff friendly tests

* chore: create barrel exports

* chore: move files after rebase

* chore: typedoc config

* chore: compat mode

* chore: review of barrels

* chore: remove tns-core-modules import after rebase

* chore: dev workflow setup

* chore: update developer-workflow

* docs: experiment with API extractor

* chore: api-extractor and barrel exports

* chore: api-extractor configs

* chore: generate d.ts rollup with api-extractor

* refactor: move methods inside Frame

* chore: fic tests to use Frame static methods

* refactor: create Builder class

* refactor: use Builder class in tests

* refactor: include Style in ui barrel

* chore: separate compat build script

* chore: fix tslint errors

* chore: update NATIVESCRIPT_CORE_ARGS

* chore: fix compat pack

* chore: fix ui-test-app build with linked modules

* chore: Application, ApplicationSettings, Connectivity and Http

* chore: export Trace, Profiling and Utils

* refactor: Static create methods for ImageSource

* chore: fix deprecated usages of ImageSource

* chore: move Span and FormattedString to ui

* chore: add events-args and ImageSource to index files

* chore: check for CLI >= 6.2 when building for IOS

* chore: update travis build

* chore: copy Pod file to compat package

* chore: update error msg ui-tests-app

* refactor: Apply suggestions from code review

Co-Authored-By: Martin Yankov <m.i.yankov@gmail.com>

* chore: typings and refs

* chore: add missing d.ts files for public API

* chore: adress code review FB

* chore: update api-report

* chore: dev-workflow for other apps

* chore: api update

* chore: update api-report
2019-10-17 00:45:33 +03:00

215 lines
6.5 KiB
TypeScript

// regex that contains all symbols applicable for expression used to AI detect an expression.
const expressionSymbolsRegex = /[\+\-\*\/%\?:<>=!\|&\(\)^~]/;
export module bindingConstants {
export const sourceProperty = "sourceProperty";
export const targetProperty = "targetProperty";
export const expression = "expression";
export const twoWay = "twoWay";
export const source = "source";
export const bindingValueKey = "$value";
export const parentValueKey = "$parent";
export const parentsValueKey = "$parents";
export const newPropertyValueKey = "$newPropertyValue";
}
const hasEqualSignRegex = /=+/;
const equalSignComparisionOperatorsRegex = /(==|===|>=|<=|!=|!==)/;
// this regex is used to search for all instaces of '$parents[]' within an expression
export const parentsRegex = /\$parents\s*\[\s*(['"]*)\w*\1\s*\]/g;
function isNamedParam(value) {
const equalSignIndex = value.search(hasEqualSignRegex);
if (equalSignIndex > -1) {
const equalSignSurround = value.substr(equalSignIndex > 0 ? equalSignIndex - 1 : 0, 3);
if (equalSignSurround.search(equalSignComparisionOperatorsRegex) === -1) {
return true;
}
}
return false;
}
function areNamedParams(params: Array<any>) {
for (let i = 0; i < params.length; i++) {
if (isNamedParam(params[i])) {
return true;
}
}
return false;
}
const namedParamConstants = {
propName: "propName",
propValue: "propValue"
};
function getPropertyNameValuePair(param, knownOptions, callback) {
let nameValuePair = {};
let propertyName = param.substr(0, param.indexOf("=")).trim();
const propertyValue = param.substr(param.indexOf("=") + 1).trim();
if (knownOptions) {
if (!propertyName) {
propertyName = knownOptions.defaultProperty;
}
else {
propertyName = propertyName in knownOptions ? propertyName : null;
}
}
if (propertyName) {
if (callback) {
nameValuePair = callback(propertyName, propertyValue);
}
else {
nameValuePair[namedParamConstants.propName] = propertyName;
nameValuePair[namedParamConstants.propValue] = propertyValue;
}
return nameValuePair;
}
return null;
}
function parseNamedProperties(parameterList, knownOptions, callback) {
const result = {};
let nameValuePair;
for (let i = 0; i < parameterList.length; i++) {
nameValuePair = getPropertyNameValuePair(parameterList[i], knownOptions, callback);
if (nameValuePair) {
result[nameValuePair[namedParamConstants.propName]] = nameValuePair[namedParamConstants.propValue];
}
}
return result;
}
function getParamsArray(value: string) {
const result = [];
let skipComma = 0;
let indexReached = 0;
let singleQuoteBlock = false;
let doubleQuoteBlock = false;
for (let i = 0; i < value.length; i++) {
if (value[i] === "\"") {
doubleQuoteBlock = !doubleQuoteBlock;
}
if (value[i] === "'") {
singleQuoteBlock = !singleQuoteBlock;
}
if (value[i] === "(" || value[i] === "[") {
skipComma++;
}
if (value[i] === ")" || value[i] === "]") {
skipComma--;
}
if (value[i] === "," && skipComma === 0 && !(singleQuoteBlock || doubleQuoteBlock)) {
result.push(value.substr(indexReached, i - indexReached));
indexReached = i + 1;
}
}
result.push(value.substr(indexReached));
return result;
}
function isExpression(expression: string): boolean {
if (expression.search(expressionSymbolsRegex) > -1) {
const parentsMatches = expression.match(parentsRegex);
if (parentsMatches) {
const restOfExpression = expression.substr(expression.indexOf(parentsMatches[0]) + parentsMatches[0].length);
// no more expression regognition symbols so it is safe for sourceProperty
if (!(restOfExpression.search(expressionSymbolsRegex) > -1)) {
return false;
}
}
return true;
}
return false;
}
export function getBindingOptions(name: string, value: string): any {
let namedParams = [];
const params = getParamsArray(value);
if (!areNamedParams(params)) {
if (params.length === 1) {
const trimmedValue = params[0].trim();
let sourceProp;
if (isExpression(trimmedValue)) {
sourceProp = bindingConstants.bindingValueKey;
namedParams.push(bindingConstants.expression + " = " + trimmedValue);
}
else {
sourceProp = trimmedValue;
}
namedParams.push(bindingConstants.sourceProperty + " = " + sourceProp);
namedParams.push(bindingConstants.twoWay + " = true");
}
else {
namedParams.push(bindingConstants.sourceProperty + " = " + params[0].trim());
namedParams.push(bindingConstants.expression + " = " + params[1].trim());
const twoWay = params[2] ? params[2].toLowerCase().trim() === "true" : true;
namedParams.push(bindingConstants.twoWay + " = " + twoWay);
}
}
else {
namedParams = params;
}
const bindingPropertyHandler = function (prop, value) {
const result = {};
result[namedParamConstants.propName] = prop;
if (prop === bindingConstants.twoWay) {
// create a real boolean value
if (value === "true") {
result[namedParamConstants.propValue] = true;
}
else {
result[namedParamConstants.propValue] = false;
}
}
else {
result[namedParamConstants.propValue] = value;
}
return result;
};
const bindingOptionsParameters = parseNamedProperties(namedParams, xmlBindingProperties, bindingPropertyHandler);
const bindOptions = {
targetProperty: name
};
for (let prop in bindingOptionsParameters) {
if (bindingOptionsParameters.hasOwnProperty(prop)) {
bindOptions[prop] = bindingOptionsParameters[prop];
}
}
if (bindOptions[bindingConstants.twoWay] === undefined) {
bindOptions[bindingConstants.twoWay] = true;
}
return bindOptions;
}
const xmlBindingProperties = {
sourceProperty: true,
expression: true,
twoWay: true,
source: true,
defaultProperty: bindingConstants.sourceProperty
};