mirror of
https://github.com/NativeScript/NativeScript.git
synced 2025-08-15 02:54:11 +08:00
feat: new expression parser for xml bindings (#9729)
This commit is contained in:

committed by
Nathan Walker

parent
a518249958
commit
90ceed15d3
@ -659,17 +659,19 @@ export function test_BindingConverterCalledEvenWithNullValue() {
|
|||||||
const testPropertyValue = null;
|
const testPropertyValue = null;
|
||||||
const expectedValue = 'collapsed';
|
const expectedValue = 'collapsed';
|
||||||
pageViewModel.set('testProperty', testPropertyValue);
|
pageViewModel.set('testProperty', testPropertyValue);
|
||||||
appModule.getResources()['converter'] = function (value) {
|
appModule.getResources()['converter'] = {
|
||||||
if (value) {
|
toView: function (value) {
|
||||||
return 'visible';
|
if (value) {
|
||||||
} else {
|
return 'visible';
|
||||||
return 'collapsed';
|
} else {
|
||||||
}
|
return 'collapsed';
|
||||||
|
}
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
const testFunc = function (views: Array<View>) {
|
const testFunc = function (views: Array<View>) {
|
||||||
const testLabel = <Label>views[0];
|
const testLabel = <Label>views[0];
|
||||||
testLabel.bind({ sourceProperty: 'testProperty', targetProperty: 'text', expression: 'testProperty | converter' });
|
testLabel.bind({ sourceProperty: 'testProperty', targetProperty: 'text', expression: 'testProperty | converter()' });
|
||||||
|
|
||||||
const page = <Page>views[1];
|
const page = <Page>views[1];
|
||||||
page.bindingContext = pageViewModel;
|
page.bindingContext = pageViewModel;
|
||||||
|
@ -437,15 +437,17 @@ export class ListViewTest extends UITest<ListView> {
|
|||||||
public test_usingAppLevelConvertersInListViewItems() {
|
public test_usingAppLevelConvertersInListViewItems() {
|
||||||
var listView = this.testView;
|
var listView = this.testView;
|
||||||
|
|
||||||
var dateConverter = function (value, format) {
|
var dateConverter = {
|
||||||
var result = format;
|
toView: function (value, format) {
|
||||||
var day = value.getDate();
|
var result = format;
|
||||||
result = result.replace('DD', day < 10 ? '0' + day : day);
|
var day = value.getDate();
|
||||||
var month = value.getMonth() + 1;
|
result = result.replace('DD', day < 10 ? '0' + day : day);
|
||||||
result = result.replace('MM', month < 10 ? '0' + month : month);
|
var month = value.getMonth() + 1;
|
||||||
result = result.replace('YYYY', value.getFullYear());
|
result = result.replace('MM', month < 10 ? '0' + month : month);
|
||||||
|
result = result.replace('YYYY', value.getFullYear());
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
Application.getResources()['dateConverter'] = dateConverter;
|
Application.getResources()['dateConverter'] = dateConverter;
|
||||||
@ -565,10 +567,12 @@ export class ListViewTest extends UITest<ListView> {
|
|||||||
var listView = this.testView;
|
var listView = this.testView;
|
||||||
var converterCalledCounter = 0;
|
var converterCalledCounter = 0;
|
||||||
|
|
||||||
var testConverter = function (value) {
|
var testConverter = {
|
||||||
converterCalledCounter++;
|
toView: function (value) {
|
||||||
|
converterCalledCounter++;
|
||||||
|
|
||||||
return value;
|
return value;
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
Application.getResources()['testConverter'] = testConverter;
|
Application.getResources()['testConverter'] = testConverter;
|
||||||
@ -578,7 +582,7 @@ export class ListViewTest extends UITest<ListView> {
|
|||||||
listView.bindingContext = listViewModel;
|
listView.bindingContext = listViewModel;
|
||||||
|
|
||||||
listView.bind({ sourceProperty: 'items', targetProperty: 'items' });
|
listView.bind({ sourceProperty: 'items', targetProperty: 'items' });
|
||||||
listView.itemTemplate = '<Label id="testLabel" text="{{ $value, $value | testConverter }}" />';
|
listView.itemTemplate = '<Label id="testLabel" text="{{ $value, $value | testConverter() }}" />';
|
||||||
|
|
||||||
this.waitUntilListViewReady();
|
this.waitUntilListViewReady();
|
||||||
|
|
||||||
@ -589,10 +593,12 @@ export class ListViewTest extends UITest<ListView> {
|
|||||||
var listView = this.testView;
|
var listView = this.testView;
|
||||||
var converterCalledCounter = 0;
|
var converterCalledCounter = 0;
|
||||||
|
|
||||||
var testConverter = function (value) {
|
var testConverter = {
|
||||||
converterCalledCounter++;
|
toView: function (value) {
|
||||||
|
converterCalledCounter++;
|
||||||
|
|
||||||
return value;
|
return value;
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
Application.getResources()['testConverter'] = testConverter;
|
Application.getResources()['testConverter'] = testConverter;
|
||||||
@ -602,7 +608,7 @@ export class ListViewTest extends UITest<ListView> {
|
|||||||
listView.bindingContext = listViewModel;
|
listView.bindingContext = listViewModel;
|
||||||
|
|
||||||
listView.bind({ sourceProperty: 'items', targetProperty: 'items' });
|
listView.bind({ sourceProperty: 'items', targetProperty: 'items' });
|
||||||
listView.itemTemplate = '<StackLayout><Label id="testLabel" text="{{ $value, $value | testConverter }}" /></StackLayout>';
|
listView.itemTemplate = '<StackLayout><Label id="testLabel" text="{{ $value, $value | testConverter() }}" /></StackLayout>';
|
||||||
|
|
||||||
this.waitUntilListViewReady();
|
this.waitUntilListViewReady();
|
||||||
|
|
||||||
|
@ -252,15 +252,17 @@ export function test_splice_observable_array_refreshes_the_Repeater() {
|
|||||||
export function test_usingAppLevelConvertersInRepeaterItems() {
|
export function test_usingAppLevelConvertersInRepeaterItems() {
|
||||||
var repeater = new Repeater();
|
var repeater = new Repeater();
|
||||||
|
|
||||||
var dateConverter = function (value, format) {
|
var dateConverter = {
|
||||||
var result = format;
|
toView: function (value, format) {
|
||||||
var day = value.getDate();
|
var result = format;
|
||||||
result = result.replace('DD', month < 10 ? '0' + day : day);
|
var day = value.getDate();
|
||||||
var month = value.getMonth() + 1;
|
result = result.replace('DD', month < 10 ? '0' + day : day);
|
||||||
result = result.replace('MM', month < 10 ? '0' + month : month);
|
var month = value.getMonth() + 1;
|
||||||
result = result.replace('YYYY', value.getFullYear());
|
result = result.replace('MM', month < 10 ? '0' + month : month);
|
||||||
|
result = result.replace('YYYY', value.getFullYear());
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
Application.getResources()['dateConverter'] = dateConverter;
|
Application.getResources()['dateConverter'] = dateConverter;
|
||||||
@ -275,7 +277,7 @@ export function test_usingAppLevelConvertersInRepeaterItems() {
|
|||||||
|
|
||||||
TKUnit.waitUntilReady(() => repeater.isLayoutValid);
|
TKUnit.waitUntilReady(() => repeater.isLayoutValid);
|
||||||
|
|
||||||
TKUnit.assertEqual(getChildAtText(repeater, 0), dateConverter(new Date(), 'DD.MM.YYYY'), 'element');
|
TKUnit.assertEqual(getChildAtText(repeater, 0), dateConverter.toView(new Date(), 'DD.MM.YYYY'), 'element');
|
||||||
}
|
}
|
||||||
|
|
||||||
helper.buildUIAndRunTest(repeater, testAction);
|
helper.buildUIAndRunTest(repeater, testAction);
|
||||||
|
@ -1,19 +0,0 @@
|
|||||||
Redistribution and use in source and binary forms, with or without
|
|
||||||
modification, are permitted provided that the following conditions are met:
|
|
||||||
|
|
||||||
* Redistributions of source code must retain the above copyright
|
|
||||||
notice, this list of conditions and the following disclaimer.
|
|
||||||
* Redistributions in binary form must reproduce the above copyright
|
|
||||||
notice, this list of conditions and the following disclaimer in the
|
|
||||||
documentation and/or other materials provided with the distribution.
|
|
||||||
|
|
||||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
||||||
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
||||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
||||||
ARE DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
|
|
||||||
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
|
||||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
|
||||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
|
||||||
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
||||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
|
||||||
THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
@ -1,24 +0,0 @@
|
|||||||
**Esprima** ([esprima.org](http://esprima.org), BSD license) is a high performance,
|
|
||||||
standard-compliant [ECMAScript](http://www.ecma-international.org/publications/standards/Ecma-262.htm)
|
|
||||||
parser written in ECMAScript (also popularly known as
|
|
||||||
[JavaScript](http://en.wikipedia.org/wiki/JavaScript).
|
|
||||||
Esprima is created and maintained by [Ariya Hidayat](http://twitter.com/ariyahidayat),
|
|
||||||
with the help of [many contributors](https://github.com/ariya/esprima/contributors).
|
|
||||||
|
|
||||||
### Features
|
|
||||||
|
|
||||||
- Full support for ECMAScript 5.1 ([ECMA-262](http://www.ecma-international.org/publications/standards/Ecma-262.htm))
|
|
||||||
- Sensible [syntax tree format](http://esprima.org/doc/index.html#ast) compatible with Mozilla
|
|
||||||
[Parser AST](https://web.archive.org/web/20201119095346/https://developer.mozilla.org/en-US/docs/Mozilla/Projects/SpiderMonkey/Parser_API)
|
|
||||||
- Optional tracking of syntax node location (index-based and line-column)
|
|
||||||
- Heavily tested (> 700 [unit tests](http://esprima.org/test/) with [full code coverage](http://esprima.org/test/coverage.html))
|
|
||||||
- [Partial support](http://esprima.org/doc/es6.html) for ECMAScript 6
|
|
||||||
|
|
||||||
Esprima serves as a **building block** for some JavaScript
|
|
||||||
language tools, from [code instrumentation](http://esprima.org/demo/functiontrace.html)
|
|
||||||
to [editor autocompletion](http://esprima.org/demo/autocomplete.html).
|
|
||||||
|
|
||||||
Esprima runs on many popular web browsers, as well as other ECMAScript platforms such as
|
|
||||||
[Rhino](https://github.com/mozilla/rhino), [Nashorn](http://openjdk.java.net/projects/nashorn/), and [Node.js](https://npmjs.org/package/esprima).
|
|
||||||
|
|
||||||
For more information, check the web site [esprima.org](http://esprima.org).
|
|
266
packages/core/js-libs/esprima/esprima.d.ts
vendored
266
packages/core/js-libs/esprima/esprima.d.ts
vendored
@ -1,266 +0,0 @@
|
|||||||
/* tslint:disable */
|
|
||||||
|
|
||||||
// Type definitions for Esprima v1.2.0
|
|
||||||
// Project: http://esprima.org
|
|
||||||
// Definitions by: teppeis <https://github.com/teppeis/>
|
|
||||||
// Definitions: https://github.com/borisyankov/DefinitelyTyped
|
|
||||||
|
|
||||||
export const version: string;
|
|
||||||
export function parse(code: string, options?: Options): Syntax.Program;
|
|
||||||
export function tokenize(code: string, options?: Options): Array<Token>;
|
|
||||||
|
|
||||||
export interface Token {
|
|
||||||
type: string
|
|
||||||
value: string
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface Options {
|
|
||||||
loc?: boolean
|
|
||||||
range?: boolean
|
|
||||||
raw?: boolean
|
|
||||||
tokens?: boolean
|
|
||||||
comment?: boolean
|
|
||||||
attachComment?: boolean
|
|
||||||
tolerant?: boolean
|
|
||||||
source?: boolean
|
|
||||||
}
|
|
||||||
|
|
||||||
export namespace Syntax {
|
|
||||||
// Node
|
|
||||||
interface Node {
|
|
||||||
type: string
|
|
||||||
loc?: LineLocation
|
|
||||||
range?: number[]
|
|
||||||
leadingComments?: Comment[]
|
|
||||||
trailingComments?: Comment[]
|
|
||||||
}
|
|
||||||
interface LineLocation {
|
|
||||||
start: Position
|
|
||||||
end: Position
|
|
||||||
}
|
|
||||||
interface Position {
|
|
||||||
line: number
|
|
||||||
column: number
|
|
||||||
}
|
|
||||||
|
|
||||||
// Comment
|
|
||||||
interface Comment extends Node {
|
|
||||||
value: string
|
|
||||||
}
|
|
||||||
|
|
||||||
// Program
|
|
||||||
interface Program extends Node {
|
|
||||||
body: SomeStatement[]
|
|
||||||
comments?: Comment[]
|
|
||||||
}
|
|
||||||
|
|
||||||
// Function
|
|
||||||
interface Function extends Node {
|
|
||||||
id: Identifier // | null
|
|
||||||
params: Identifier[]
|
|
||||||
defaults: SomeExpression[]
|
|
||||||
rest: Identifier // | null
|
|
||||||
body: BlockStatementOrExpression
|
|
||||||
generator: boolean
|
|
||||||
expression: boolean
|
|
||||||
}
|
|
||||||
interface BlockStatementOrExpression extends Array<SomeStatement>, BlockStatement, SomeExpression {
|
|
||||||
body: BlockStatementOrExpression
|
|
||||||
}
|
|
||||||
|
|
||||||
// Statement
|
|
||||||
type Statement = Node
|
|
||||||
type EmptyStatement = Statement
|
|
||||||
interface BlockStatement extends Statement {
|
|
||||||
body: SomeStatement[]
|
|
||||||
}
|
|
||||||
interface ExpressionStatement extends Statement {
|
|
||||||
expression: SomeExpression
|
|
||||||
}
|
|
||||||
interface IfStatement extends Statement {
|
|
||||||
test: SomeExpression
|
|
||||||
consequent: SomeStatement
|
|
||||||
alternate: SomeStatement
|
|
||||||
}
|
|
||||||
interface LabeledStatement extends Statement {
|
|
||||||
label: Identifier
|
|
||||||
body: SomeStatement
|
|
||||||
}
|
|
||||||
interface BreakStatement extends Statement {
|
|
||||||
label: Identifier // | null
|
|
||||||
}
|
|
||||||
interface ContinueStatement extends Statement {
|
|
||||||
label: Identifier // | null
|
|
||||||
}
|
|
||||||
interface WithStatement extends Statement {
|
|
||||||
object: SomeExpression
|
|
||||||
body: SomeStatement
|
|
||||||
}
|
|
||||||
interface SwitchStatement extends Statement {
|
|
||||||
discriminant: SomeExpression
|
|
||||||
cases: SwitchCase[]
|
|
||||||
lexical: boolean
|
|
||||||
}
|
|
||||||
interface ReturnStatement extends Statement {
|
|
||||||
argument: SomeExpression // | null
|
|
||||||
}
|
|
||||||
interface ThrowStatement extends Statement {
|
|
||||||
argument: SomeExpression
|
|
||||||
}
|
|
||||||
interface TryStatement extends Statement {
|
|
||||||
block: BlockStatement
|
|
||||||
handler: CatchClause // | null
|
|
||||||
guardedHandlers: CatchClause[]
|
|
||||||
finalizer: BlockStatement // | null
|
|
||||||
}
|
|
||||||
interface WhileStatement extends Statement {
|
|
||||||
test: SomeExpression
|
|
||||||
body: SomeStatement
|
|
||||||
}
|
|
||||||
interface DoWhileStatement extends Statement {
|
|
||||||
body: SomeStatement
|
|
||||||
test: SomeExpression
|
|
||||||
}
|
|
||||||
interface ForStatement extends Statement {
|
|
||||||
init: VariableDeclaratorOrExpression // | null
|
|
||||||
test: SomeExpression // | null
|
|
||||||
update: SomeExpression // | null
|
|
||||||
body: SomeStatement
|
|
||||||
}
|
|
||||||
interface ForInStatement extends Statement {
|
|
||||||
left: VariableDeclaratorOrExpression
|
|
||||||
right: SomeExpression
|
|
||||||
body: SomeStatement
|
|
||||||
each: boolean
|
|
||||||
}
|
|
||||||
interface VariableDeclaratorOrExpression extends VariableDeclarator, SomeExpression {
|
|
||||||
}
|
|
||||||
type DebuggerStatement = Statement
|
|
||||||
interface SomeStatement extends
|
|
||||||
EmptyStatement, ExpressionStatement, BlockStatement, IfStatement,
|
|
||||||
LabeledStatement, BreakStatement, ContinueStatement, WithStatement,
|
|
||||||
SwitchStatement, ReturnStatement, ThrowStatement, TryStatement,
|
|
||||||
WhileStatement, DoWhileStatement, ForStatement, ForInStatement, DebuggerStatement {
|
|
||||||
body: SomeStatementOrList
|
|
||||||
}
|
|
||||||
interface SomeStatementOrList extends Array<SomeStatement>, SomeStatement {
|
|
||||||
}
|
|
||||||
|
|
||||||
// Declration
|
|
||||||
type Declration = Statement
|
|
||||||
interface FunctionDeclration extends Declration {
|
|
||||||
id: Identifier
|
|
||||||
params: Identifier[] // Pattern
|
|
||||||
defaults: SomeExpression[]
|
|
||||||
rest: Identifier
|
|
||||||
body: BlockStatementOrExpression
|
|
||||||
generator: boolean
|
|
||||||
expression: boolean
|
|
||||||
}
|
|
||||||
interface VariableDeclaration extends Declration {
|
|
||||||
declarations: VariableDeclarator[]
|
|
||||||
kind: string // "var" | "let" | "const"
|
|
||||||
}
|
|
||||||
interface VariableDeclarator extends Node {
|
|
||||||
id: Identifier // Pattern
|
|
||||||
init: SomeExpression
|
|
||||||
}
|
|
||||||
|
|
||||||
// Expression
|
|
||||||
type Expression = Node
|
|
||||||
interface SomeExpression extends
|
|
||||||
ThisExpression, ArrayExpression, ObjectExpression, FunctionExpression,
|
|
||||||
ArrowFunctionExpression, SequenceExpression, UnaryExpression, BinaryExpression,
|
|
||||||
AssignmentExpression, UpdateExpression, LogicalExpression, ConditionalExpression,
|
|
||||||
NewExpression, CallExpression, MemberExpression {
|
|
||||||
}
|
|
||||||
type ThisExpression = Expression
|
|
||||||
interface ArrayExpression extends Expression {
|
|
||||||
elements: SomeExpression[] // [ Expression | null ]
|
|
||||||
}
|
|
||||||
interface ObjectExpression extends Expression {
|
|
||||||
properties: Property[]
|
|
||||||
}
|
|
||||||
interface Property extends Node {
|
|
||||||
key: LiteralOrIdentifier // Literal | Identifier
|
|
||||||
value: SomeExpression
|
|
||||||
kind: string // "init" | "get" | "set"
|
|
||||||
}
|
|
||||||
interface LiteralOrIdentifier extends Literal, Identifier {
|
|
||||||
}
|
|
||||||
interface FunctionExpression extends Function, Expression {
|
|
||||||
}
|
|
||||||
interface ArrowFunctionExpression extends Function, Expression {
|
|
||||||
}
|
|
||||||
interface SequenceExpression extends Expression {
|
|
||||||
expressions: SomeExpression[]
|
|
||||||
}
|
|
||||||
interface UnaryExpression extends Expression {
|
|
||||||
operator: string // UnaryOperator
|
|
||||||
prefix: boolean
|
|
||||||
argument: SomeExpression
|
|
||||||
}
|
|
||||||
interface BinaryExpression extends Expression {
|
|
||||||
operator: string // BinaryOperator
|
|
||||||
left: SomeExpression
|
|
||||||
right: SomeExpression
|
|
||||||
}
|
|
||||||
interface AssignmentExpression extends Expression {
|
|
||||||
operator: string // AssignmentOperator
|
|
||||||
left: SomeExpression
|
|
||||||
right: SomeExpression
|
|
||||||
}
|
|
||||||
interface UpdateExpression extends Expression {
|
|
||||||
operator: string // UpdateOperator
|
|
||||||
argument: SomeExpression
|
|
||||||
prefix: boolean
|
|
||||||
}
|
|
||||||
interface LogicalExpression extends Expression {
|
|
||||||
operator: string // LogicalOperator
|
|
||||||
left: SomeExpression
|
|
||||||
right: SomeExpression
|
|
||||||
}
|
|
||||||
interface ConditionalExpression extends Expression {
|
|
||||||
test: SomeExpression
|
|
||||||
alternate: SomeExpression
|
|
||||||
consequent: SomeExpression
|
|
||||||
}
|
|
||||||
interface NewExpression extends Expression {
|
|
||||||
callee: SomeExpression
|
|
||||||
arguments: SomeExpression[]
|
|
||||||
}
|
|
||||||
interface CallExpression extends Expression {
|
|
||||||
callee: SomeExpression
|
|
||||||
arguments: SomeExpression[]
|
|
||||||
}
|
|
||||||
interface MemberExpression extends Expression {
|
|
||||||
object: SomeExpression
|
|
||||||
property: IdentifierOrExpression // Identifier | Expression
|
|
||||||
computed: boolean
|
|
||||||
}
|
|
||||||
interface IdentifierOrExpression extends Identifier, SomeExpression {
|
|
||||||
}
|
|
||||||
|
|
||||||
// Pattern
|
|
||||||
// interface Pattern extends Node {
|
|
||||||
// }
|
|
||||||
|
|
||||||
// Clauses
|
|
||||||
interface SwitchCase extends Node {
|
|
||||||
test: SomeExpression
|
|
||||||
consequent: SomeStatement[]
|
|
||||||
}
|
|
||||||
interface CatchClause extends Node {
|
|
||||||
param: Identifier // Pattern
|
|
||||||
guard: SomeExpression
|
|
||||||
body: BlockStatement
|
|
||||||
}
|
|
||||||
|
|
||||||
// Misc
|
|
||||||
interface Identifier extends Node, Expression { // | Pattern
|
|
||||||
name: string
|
|
||||||
}
|
|
||||||
interface Literal extends Node, Expression {
|
|
||||||
value: any // string | boolean | null | number | RegExp
|
|
||||||
}
|
|
||||||
}
|
|
File diff suppressed because it is too large
Load Diff
@ -1,75 +0,0 @@
|
|||||||
{
|
|
||||||
"name": "esprima",
|
|
||||||
"description": "ECMAScript parsing infrastructure for multipurpose analysis",
|
|
||||||
"homepage": "http://esprima.org",
|
|
||||||
"main": "esprima",
|
|
||||||
"sideEffects": false,
|
|
||||||
"types": "esprima.d.ts",
|
|
||||||
"bin": {
|
|
||||||
"esparse": "./bin/esparse.js",
|
|
||||||
"esvalidate": "./bin/esvalidate.js"
|
|
||||||
},
|
|
||||||
"version": "2.0.0-dev",
|
|
||||||
"engines": {
|
|
||||||
"node": ">=0.4.0"
|
|
||||||
},
|
|
||||||
"author": {
|
|
||||||
"name": "Ariya Hidayat",
|
|
||||||
"email": "ariya.hidayat@gmail.com"
|
|
||||||
},
|
|
||||||
"maintainers": [
|
|
||||||
{
|
|
||||||
"name": "Ariya Hidayat",
|
|
||||||
"email": "ariya.hidayat@gmail.com",
|
|
||||||
"web": "http://ariya.ofilabs.com"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"repository": {
|
|
||||||
"type": "git",
|
|
||||||
"url": "https://github.com/ariya/esprima.git"
|
|
||||||
},
|
|
||||||
"bugs": {
|
|
||||||
"url": "http://issues.esprima.org"
|
|
||||||
},
|
|
||||||
"licenses": [
|
|
||||||
{
|
|
||||||
"type": "BSD",
|
|
||||||
"url": "https://github.com/ariya/esprima/raw/master/LICENSE.BSD"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"devDependencies": {
|
|
||||||
"jslint": "~0.1.9",
|
|
||||||
"eslint": "~5.16.0",
|
|
||||||
"jscs": "~1.2.4",
|
|
||||||
"istanbul": "~0.2.6",
|
|
||||||
"complexity-report": "~0.6.1",
|
|
||||||
"regenerate": "~0.6.2",
|
|
||||||
"unicode-7.0.0": "~0.1.5",
|
|
||||||
"json-diff": "~0.3.1",
|
|
||||||
"optimist": "~0.6.0"
|
|
||||||
},
|
|
||||||
"keywords": [
|
|
||||||
"ast",
|
|
||||||
"ecmascript",
|
|
||||||
"javascript",
|
|
||||||
"parser",
|
|
||||||
"syntax"
|
|
||||||
],
|
|
||||||
"scripts": {
|
|
||||||
"generate-regex": "node tools/generate-identifier-regex.js",
|
|
||||||
"test": "npm run-script lint && node test/run.js && npm run-script coverage && npm run-script complexity",
|
|
||||||
"lint": "npm run-script check-version && npm run-script eslint && npm run-script jscs && npm run-script jslint",
|
|
||||||
"check-version": "node tools/check-version.js",
|
|
||||||
"eslint": "node node_modules/eslint/bin/eslint.js esprima.js",
|
|
||||||
"jscs": "node node_modules/jscs/bin/jscs esprima.js",
|
|
||||||
"jslint": "node node_modules/jslint/bin/jslint.js esprima.js",
|
|
||||||
"coverage": "npm run-script analyze-coverage && npm run-script check-coverage",
|
|
||||||
"analyze-coverage": "node node_modules/istanbul/lib/cli.js cover test/runner.js",
|
|
||||||
"check-coverage": "node node_modules/istanbul/lib/cli.js check-coverage --statement 100 --branch 100 --function 100",
|
|
||||||
"complexity": "npm run-script analyze-complexity && npm run-script check-complexity",
|
|
||||||
"analyze-complexity": "node tools/list-complexity.js",
|
|
||||||
"check-complexity": "node node_modules/complexity-report/src/cli.js --maxcc 15 --silent -l -w esprima.js",
|
|
||||||
"benchmark": "node test/benchmarks.js",
|
|
||||||
"benchmark-quick": "node test/benchmarks.js quick"
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,27 +0,0 @@
|
|||||||
// Copyright (c) 2014 The Polymer Authors. All rights reserved.
|
|
||||||
//
|
|
||||||
// Redistribution and use in source and binary forms, with or without
|
|
||||||
// modification, are permitted provided that the following conditions are
|
|
||||||
// met:
|
|
||||||
//
|
|
||||||
// * Redistributions of source code must retain the above copyright
|
|
||||||
// notice, this list of conditions and the following disclaimer.
|
|
||||||
// * Redistributions in binary form must reproduce the above
|
|
||||||
// copyright notice, this list of conditions and the following disclaimer
|
|
||||||
// in the documentation and/or other materials provided with the
|
|
||||||
// distribution.
|
|
||||||
// * Neither the name of Google Inc. nor the names of its
|
|
||||||
// contributors may be used to endorse or promote products derived from
|
|
||||||
// this software without specific prior written permission.
|
|
||||||
//
|
|
||||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
|
||||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
|
||||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
|
||||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
|
||||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
|
||||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
|
||||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
|
||||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
||||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
||||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
||||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
@ -1,6 +0,0 @@
|
|||||||
{
|
|
||||||
"sideEffects": false,
|
|
||||||
"name": "polymer-expressions",
|
|
||||||
"main": "polymer-expressions",
|
|
||||||
"types": "polymer-expressions.d.ts"
|
|
||||||
}
|
|
@ -1,393 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (c) 2014 The Polymer Project Authors. All rights reserved.
|
|
||||||
* This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
|
|
||||||
* The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
|
|
||||||
* The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
|
|
||||||
* Code distributed by Google as part of the polymer project is also
|
|
||||||
* subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
|
|
||||||
*/
|
|
||||||
|
|
||||||
'use strict';
|
|
||||||
|
|
||||||
function detectEval() {
|
|
||||||
// Don't test for eval if we're running in a Chrome App environment.
|
|
||||||
// We check for APIs set that only exist in a Chrome App context.
|
|
||||||
if (typeof chrome !== 'undefined' && chrome.app && chrome.app.runtime) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Firefox OS Apps do not allow eval. This feature detection is very hacky
|
|
||||||
// but even if some other platform adds support for this function this code
|
|
||||||
// will continue to work.
|
|
||||||
if (typeof navigator != 'undefined' && navigator.getDeviceStorage) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
var f = new Function('', 'return true;');
|
|
||||||
return f();
|
|
||||||
} catch (ex) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var hasEval = detectEval();
|
|
||||||
|
|
||||||
function isIndex(s) {
|
|
||||||
return +s === s >>> 0 && s !== '';
|
|
||||||
}
|
|
||||||
|
|
||||||
function toNumber(s) {
|
|
||||||
return +s;
|
|
||||||
}
|
|
||||||
|
|
||||||
function isObject(obj) {
|
|
||||||
return obj === Object(obj);
|
|
||||||
}
|
|
||||||
|
|
||||||
var numberIsNaN = Number.isNaN || function (value) {
|
|
||||||
return typeof value === 'number' && isNaN(value);
|
|
||||||
}
|
|
||||||
|
|
||||||
function areSameValue(left, right) {
|
|
||||||
if (left === right)
|
|
||||||
return left !== 0 || 1 / left === 1 / right;
|
|
||||||
if (numberIsNaN(left) && numberIsNaN(right))
|
|
||||||
return true;
|
|
||||||
|
|
||||||
return left !== left && right !== right;
|
|
||||||
}
|
|
||||||
|
|
||||||
var createObject = ('__proto__' in {}) ?
|
|
||||||
function (obj) { return obj; } :
|
|
||||||
function (obj) {
|
|
||||||
var proto = obj.__proto__;
|
|
||||||
if (!proto)
|
|
||||||
return obj;
|
|
||||||
var newObject = Object.create(proto);
|
|
||||||
Object.getOwnPropertyNames(obj).forEach(function (name) {
|
|
||||||
Object.defineProperty(newObject, name,
|
|
||||||
Object.getOwnPropertyDescriptor(obj, name));
|
|
||||||
});
|
|
||||||
return newObject;
|
|
||||||
};
|
|
||||||
|
|
||||||
var identStart = '[\$_a-zA-Z]';
|
|
||||||
var identPart = '[\$_a-zA-Z0-9]';
|
|
||||||
var identRegExp = new RegExp('^' + identStart + '+' + identPart + '*' + '$');
|
|
||||||
|
|
||||||
function getPathCharType(char) {
|
|
||||||
if (char === undefined)
|
|
||||||
return 'eof';
|
|
||||||
|
|
||||||
var code = char.charCodeAt(0);
|
|
||||||
|
|
||||||
switch (code) {
|
|
||||||
case 0x5B: // [
|
|
||||||
case 0x5D: // ]
|
|
||||||
case 0x2E: // .
|
|
||||||
case 0x22: // "
|
|
||||||
case 0x27: // '
|
|
||||||
case 0x30: // 0
|
|
||||||
return char;
|
|
||||||
|
|
||||||
case 0x5F: // _
|
|
||||||
case 0x24: // $
|
|
||||||
return 'ident';
|
|
||||||
|
|
||||||
case 0x20: // Space
|
|
||||||
case 0x09: // Tab
|
|
||||||
case 0x0A: // Newline
|
|
||||||
case 0x0D: // Return
|
|
||||||
case 0xA0: // No-break space
|
|
||||||
case 0xFEFF: // Byte Order Mark
|
|
||||||
case 0x2028: // Line Separator
|
|
||||||
case 0x2029: // Paragraph Separator
|
|
||||||
return 'ws';
|
|
||||||
}
|
|
||||||
|
|
||||||
// a-z, A-Z
|
|
||||||
if ((0x61 <= code && code <= 0x7A) || (0x41 <= code && code <= 0x5A))
|
|
||||||
return 'ident';
|
|
||||||
|
|
||||||
// 1-9
|
|
||||||
if (0x31 <= code && code <= 0x39)
|
|
||||||
return 'number';
|
|
||||||
|
|
||||||
return 'else';
|
|
||||||
}
|
|
||||||
|
|
||||||
var pathStateMachine = {
|
|
||||||
'beforePath': {
|
|
||||||
'ws': ['beforePath'],
|
|
||||||
'ident': ['inIdent', 'append'],
|
|
||||||
'[': ['beforeElement'],
|
|
||||||
'eof': ['afterPath']
|
|
||||||
},
|
|
||||||
|
|
||||||
'inPath': {
|
|
||||||
'ws': ['inPath'],
|
|
||||||
'.': ['beforeIdent'],
|
|
||||||
'[': ['beforeElement'],
|
|
||||||
'eof': ['afterPath']
|
|
||||||
},
|
|
||||||
|
|
||||||
'beforeIdent': {
|
|
||||||
'ws': ['beforeIdent'],
|
|
||||||
'ident': ['inIdent', 'append']
|
|
||||||
},
|
|
||||||
|
|
||||||
'inIdent': {
|
|
||||||
'ident': ['inIdent', 'append'],
|
|
||||||
'0': ['inIdent', 'append'],
|
|
||||||
'number': ['inIdent', 'append'],
|
|
||||||
'ws': ['inPath', 'push'],
|
|
||||||
'.': ['beforeIdent', 'push'],
|
|
||||||
'[': ['beforeElement', 'push'],
|
|
||||||
'eof': ['afterPath', 'push']
|
|
||||||
},
|
|
||||||
|
|
||||||
'beforeElement': {
|
|
||||||
'ws': ['beforeElement'],
|
|
||||||
'0': ['afterZero', 'append'],
|
|
||||||
'number': ['inIndex', 'append'],
|
|
||||||
"'": ['inSingleQuote', 'append', ''],
|
|
||||||
'"': ['inDoubleQuote', 'append', '']
|
|
||||||
},
|
|
||||||
|
|
||||||
'afterZero': {
|
|
||||||
'ws': ['afterElement', 'push'],
|
|
||||||
']': ['inPath', 'push']
|
|
||||||
},
|
|
||||||
|
|
||||||
'inIndex': {
|
|
||||||
'0': ['inIndex', 'append'],
|
|
||||||
'number': ['inIndex', 'append'],
|
|
||||||
'ws': ['afterElement'],
|
|
||||||
']': ['inPath', 'push']
|
|
||||||
},
|
|
||||||
|
|
||||||
'inSingleQuote': {
|
|
||||||
"'": ['afterElement'],
|
|
||||||
'eof': ['error'],
|
|
||||||
'else': ['inSingleQuote', 'append']
|
|
||||||
},
|
|
||||||
|
|
||||||
'inDoubleQuote': {
|
|
||||||
'"': ['afterElement'],
|
|
||||||
'eof': ['error'],
|
|
||||||
'else': ['inDoubleQuote', 'append']
|
|
||||||
},
|
|
||||||
|
|
||||||
'afterElement': {
|
|
||||||
'ws': ['afterElement'],
|
|
||||||
']': ['inPath', 'push']
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function noop() { }
|
|
||||||
|
|
||||||
function parsePath(path) {
|
|
||||||
var keys = [];
|
|
||||||
var index = -1;
|
|
||||||
var c, newChar, key, type, transition, action, typeMap, mode = 'beforePath';
|
|
||||||
|
|
||||||
var actions = {
|
|
||||||
push: function () {
|
|
||||||
if (key === undefined)
|
|
||||||
return;
|
|
||||||
|
|
||||||
keys.push(key);
|
|
||||||
key = undefined;
|
|
||||||
},
|
|
||||||
|
|
||||||
append: function () {
|
|
||||||
if (key === undefined)
|
|
||||||
key = newChar
|
|
||||||
else
|
|
||||||
key += newChar;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
function maybeUnescapeQuote() {
|
|
||||||
if (index >= path.length)
|
|
||||||
return;
|
|
||||||
|
|
||||||
var nextChar = path[index + 1];
|
|
||||||
if ((mode == 'inSingleQuote' && nextChar == "'") ||
|
|
||||||
(mode == 'inDoubleQuote' && nextChar == '"')) {
|
|
||||||
index++;
|
|
||||||
newChar = nextChar;
|
|
||||||
actions.append();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
while (mode) {
|
|
||||||
index++;
|
|
||||||
c = path[index];
|
|
||||||
|
|
||||||
if (c == '\\' && maybeUnescapeQuote(mode))
|
|
||||||
continue;
|
|
||||||
|
|
||||||
type = getPathCharType(c);
|
|
||||||
typeMap = pathStateMachine[mode];
|
|
||||||
transition = typeMap[type] || typeMap['else'] || 'error';
|
|
||||||
|
|
||||||
if (transition == 'error')
|
|
||||||
return; // parse error;
|
|
||||||
|
|
||||||
mode = transition[0];
|
|
||||||
action = actions[transition[1]] || noop;
|
|
||||||
newChar = transition[2] === undefined ? c : transition[2];
|
|
||||||
action();
|
|
||||||
|
|
||||||
if (mode === 'afterPath') {
|
|
||||||
return keys;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return; // parse error
|
|
||||||
}
|
|
||||||
|
|
||||||
function isIdent(s) {
|
|
||||||
return identRegExp.test(s);
|
|
||||||
}
|
|
||||||
|
|
||||||
var constructorIsPrivate = {};
|
|
||||||
|
|
||||||
function Path(parts, privateToken) {
|
|
||||||
if (privateToken !== constructorIsPrivate)
|
|
||||||
throw Error('Use Path.get to retrieve path objects');
|
|
||||||
|
|
||||||
for (var i = 0; i < parts.length; i++) {
|
|
||||||
this.push(String(parts[i]));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (hasEval && this.length) {
|
|
||||||
this.getValueFrom = this.compiledGetValueFromFn();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO(rafaelw): Make simple LRU cache
|
|
||||||
var pathCache = {};
|
|
||||||
|
|
||||||
function getPath(pathString) {
|
|
||||||
if (pathString instanceof Path)
|
|
||||||
return pathString;
|
|
||||||
|
|
||||||
if (pathString == null || pathString.length == 0)
|
|
||||||
pathString = '';
|
|
||||||
|
|
||||||
if (typeof pathString != 'string') {
|
|
||||||
if (isIndex(pathString.length)) {
|
|
||||||
// Constructed with array-like (pre-parsed) keys
|
|
||||||
return new Path(pathString, constructorIsPrivate);
|
|
||||||
}
|
|
||||||
|
|
||||||
pathString = String(pathString);
|
|
||||||
}
|
|
||||||
|
|
||||||
var path = pathCache[pathString];
|
|
||||||
if (path)
|
|
||||||
return path;
|
|
||||||
|
|
||||||
var parts = parsePath(pathString);
|
|
||||||
if (!parts)
|
|
||||||
return invalidPath;
|
|
||||||
|
|
||||||
var path = new Path(parts, constructorIsPrivate);
|
|
||||||
pathCache[pathString] = path;
|
|
||||||
return path;
|
|
||||||
}
|
|
||||||
|
|
||||||
Path.get = getPath;
|
|
||||||
|
|
||||||
function formatAccessor(key) {
|
|
||||||
if (isIndex(key)) {
|
|
||||||
return '[' + key + ']';
|
|
||||||
} else {
|
|
||||||
return '["' + key.replace(/"/g, '\\"') + '"]';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Path.prototype = createObject({
|
|
||||||
__proto__: [],
|
|
||||||
valid: true,
|
|
||||||
|
|
||||||
toString: function () {
|
|
||||||
var pathString = '';
|
|
||||||
for (var i = 0; i < this.length; i++) {
|
|
||||||
var key = this[i];
|
|
||||||
if (isIdent(key)) {
|
|
||||||
pathString += i ? '.' + key : key;
|
|
||||||
} else {
|
|
||||||
pathString += formatAccessor(key);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return pathString;
|
|
||||||
},
|
|
||||||
|
|
||||||
getValueFrom: function (obj, directObserver) {
|
|
||||||
for (var i = 0; i < this.length; i++) {
|
|
||||||
if (obj == null)
|
|
||||||
return;
|
|
||||||
obj = obj[this[i]];
|
|
||||||
}
|
|
||||||
return obj;
|
|
||||||
},
|
|
||||||
|
|
||||||
iterateObjects: function (obj, observe) {
|
|
||||||
for (var i = 0; i < this.length; i++) {
|
|
||||||
if (i)
|
|
||||||
obj = obj[this[i - 1]];
|
|
||||||
if (!isObject(obj))
|
|
||||||
return;
|
|
||||||
observe(obj, this[i]);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
compiledGetValueFromFn: function () {
|
|
||||||
var str = '';
|
|
||||||
var pathString = 'obj';
|
|
||||||
str += 'if (obj != null';
|
|
||||||
var i = 0;
|
|
||||||
var key;
|
|
||||||
for (; i < (this.length - 1) ; i++) {
|
|
||||||
key = this[i];
|
|
||||||
pathString += isIdent(key) ? '.' + key : formatAccessor(key);
|
|
||||||
str += ' &&\n ' + pathString + ' != null';
|
|
||||||
}
|
|
||||||
str += ')\n';
|
|
||||||
|
|
||||||
var key = this[i];
|
|
||||||
pathString += isIdent(key) ? '.' + key : formatAccessor(key);
|
|
||||||
|
|
||||||
str += ' return ' + pathString + ';\nelse\n return undefined;';
|
|
||||||
return new Function('obj', str);
|
|
||||||
},
|
|
||||||
|
|
||||||
setValueFrom: function (obj, value) {
|
|
||||||
if (!this.length)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
for (var i = 0; i < this.length - 1; i++) {
|
|
||||||
if (!isObject(obj))
|
|
||||||
return false;
|
|
||||||
obj = obj[this[i]];
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!isObject(obj))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
obj[this[i]] = value;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
var invalidPath = new Path('', constructorIsPrivate);
|
|
||||||
invalidPath.valid = false;
|
|
||||||
invalidPath.getValueFrom = invalidPath.setValueFrom = function () { };
|
|
||||||
|
|
||||||
exports.Path = Path;
|
|
@ -1,14 +0,0 @@
|
|||||||
//@private
|
|
||||||
export class PolymerExpressions {
|
|
||||||
static getExpression(expression: string): Expression;
|
|
||||||
}
|
|
||||||
|
|
||||||
export class Expression {
|
|
||||||
/**
|
|
||||||
* Evaluates a value for an expression.
|
|
||||||
* @param model - Context of the expression.
|
|
||||||
* @param isBackConvert - Denotes if the convertion is forward (from model to ui) or back (ui to model).
|
|
||||||
* @param changedModel - A property bag which contains all changed properties (in case of two way binding).
|
|
||||||
*/
|
|
||||||
getValue(model, isBackConvert, changedModel);
|
|
||||||
}
|
|
@ -1,541 +0,0 @@
|
|||||||
// Copyright (c) 2014 The Polymer Project Authors. All rights reserved.
|
|
||||||
// This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
|
|
||||||
// The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
|
|
||||||
// The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
|
|
||||||
// Code distributed by Google as part of the polymer project is also
|
|
||||||
// subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
|
|
||||||
|
|
||||||
// Hack to resolve https://github.com/webpack/enhanced-resolve/issues/197 .
|
|
||||||
// This issue causes an require like this (`../esprima`) to be resolved to (`esprima`) by the Angular webpack plugin
|
|
||||||
var esprima = require("../../js-libs/esprima").esprima;
|
|
||||||
|
|
||||||
var Path = require("./path-parser").Path;
|
|
||||||
|
|
||||||
(function (global) {
|
|
||||||
'use strict';
|
|
||||||
|
|
||||||
// TODO(rafaelw): Implement simple LRU.
|
|
||||||
var expressionParseCache = Object.create(null);
|
|
||||||
|
|
||||||
function getExpression(expressionText) {
|
|
||||||
var expression = expressionParseCache[expressionText];
|
|
||||||
if (!expression) {
|
|
||||||
var delegate = new ASTDelegate();
|
|
||||||
esprima.parse(expressionText, delegate);
|
|
||||||
expression = new Expression(delegate);
|
|
||||||
expressionParseCache[expressionText] = expression;
|
|
||||||
}
|
|
||||||
return expression;
|
|
||||||
}
|
|
||||||
|
|
||||||
function Literal(value) {
|
|
||||||
this.value = value;
|
|
||||||
this.valueFn_ = undefined;
|
|
||||||
}
|
|
||||||
|
|
||||||
Literal.prototype = {
|
|
||||||
valueFn: function () {
|
|
||||||
if (!this.valueFn_) {
|
|
||||||
var value = this.value;
|
|
||||||
this.valueFn_ = function () {
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return this.valueFn_;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function IdentPath(name) {
|
|
||||||
this.name = name;
|
|
||||||
this.path = Path.get(name);
|
|
||||||
}
|
|
||||||
|
|
||||||
IdentPath.prototype = {
|
|
||||||
valueFn: function () {
|
|
||||||
if (!this.valueFn_) {
|
|
||||||
var name = this.name;
|
|
||||||
var path = this.path;
|
|
||||||
this.valueFn_ = function (model, observer, changedModel) {
|
|
||||||
if (observer)
|
|
||||||
observer.addPath(model, path);
|
|
||||||
|
|
||||||
if (changedModel) {
|
|
||||||
var result = path.getValueFrom(changedModel);
|
|
||||||
if (result !== undefined) {
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return path.getValueFrom(model);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return this.valueFn_;
|
|
||||||
},
|
|
||||||
|
|
||||||
setValue: function (model, newValue) {
|
|
||||||
if (this.path.length == 1) {
|
|
||||||
model = findScope(model, this.path[0]);
|
|
||||||
}
|
|
||||||
|
|
||||||
return this.path.setValueFrom(model, newValue);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
function MemberExpression(object, property, accessor) {
|
|
||||||
this.computed = accessor == '[';
|
|
||||||
|
|
||||||
this.dynamicDeps = typeof object == 'function' ||
|
|
||||||
object.dynamicDeps ||
|
|
||||||
(this.computed && !(property instanceof Literal));
|
|
||||||
|
|
||||||
this.simplePath =
|
|
||||||
!this.dynamicDeps &&
|
|
||||||
(property instanceof IdentPath || property instanceof Literal) &&
|
|
||||||
(object instanceof MemberExpression || object instanceof IdentPath);
|
|
||||||
|
|
||||||
this.object = this.simplePath ? object : getFn(object);
|
|
||||||
this.property = !this.computed || this.simplePath ?
|
|
||||||
property : getFn(property);
|
|
||||||
}
|
|
||||||
|
|
||||||
MemberExpression.prototype = {
|
|
||||||
get fullPath() {
|
|
||||||
if (!this.fullPath_) {
|
|
||||||
|
|
||||||
var parts = this.object instanceof MemberExpression ?
|
|
||||||
this.object.fullPath.slice() : [this.object.name];
|
|
||||||
parts.push(this.property instanceof IdentPath ?
|
|
||||||
this.property.name : this.property.value);
|
|
||||||
this.fullPath_ = Path.get(parts);
|
|
||||||
}
|
|
||||||
|
|
||||||
return this.fullPath_;
|
|
||||||
},
|
|
||||||
|
|
||||||
valueFn: function () {
|
|
||||||
if (!this.valueFn_) {
|
|
||||||
var object = this.object;
|
|
||||||
|
|
||||||
if (this.simplePath) {
|
|
||||||
var path = this.fullPath;
|
|
||||||
|
|
||||||
this.valueFn_ = function (model, observer) {
|
|
||||||
if (observer)
|
|
||||||
observer.addPath(model, path);
|
|
||||||
|
|
||||||
return path.getValueFrom(model);
|
|
||||||
};
|
|
||||||
} else if (!this.computed) {
|
|
||||||
var path = Path.get(this.property.name);
|
|
||||||
|
|
||||||
this.valueFn_ = function (model, observer, filterRegistry) {
|
|
||||||
var context = object(model, observer, filterRegistry);
|
|
||||||
|
|
||||||
if (observer)
|
|
||||||
observer.addPath(context, path);
|
|
||||||
|
|
||||||
return path.getValueFrom(context);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// Computed property.
|
|
||||||
var property = this.property;
|
|
||||||
|
|
||||||
this.valueFn_ = function (model, observer, filterRegistry) {
|
|
||||||
var context = object(model, observer, filterRegistry);
|
|
||||||
var propName = property(model, observer, filterRegistry);
|
|
||||||
if (observer)
|
|
||||||
observer.addPath(context, [propName]);
|
|
||||||
|
|
||||||
return context ? context[propName] : undefined;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return this.valueFn_;
|
|
||||||
},
|
|
||||||
|
|
||||||
setValue: function (model, newValue) {
|
|
||||||
if (this.simplePath) {
|
|
||||||
this.fullPath.setValueFrom(model, newValue);
|
|
||||||
return newValue;
|
|
||||||
}
|
|
||||||
|
|
||||||
var object = this.object(model);
|
|
||||||
var propName = this.property instanceof IdentPath ? this.property.name :
|
|
||||||
this.property(model);
|
|
||||||
return object[propName] = newValue;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
function Filter(name, args) {
|
|
||||||
this.name = name;
|
|
||||||
this.args = [];
|
|
||||||
for (var i = 0; i < args.length; i++) {
|
|
||||||
this.args[i] = getFn(args[i]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Filter.prototype = {
|
|
||||||
transform: function (model, observer, filterRegistry, toModelDirection,
|
|
||||||
initialArgs) {
|
|
||||||
var fn = filterRegistry[this.name];
|
|
||||||
var context = model;
|
|
||||||
if (fn) {
|
|
||||||
context = undefined;
|
|
||||||
} else {
|
|
||||||
fn = context[this.name];
|
|
||||||
if (!fn) {
|
|
||||||
console.error('Cannot find function or filter: ' + this.name);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// If toModelDirection is falsey, then the "normal" (dom-bound) direction
|
|
||||||
// is used. Otherwise, it looks for a 'toModel' property function on the
|
|
||||||
// object.
|
|
||||||
if (toModelDirection) {
|
|
||||||
fn = fn.toModel;
|
|
||||||
} else if (typeof fn.toView == 'function') {
|
|
||||||
fn = fn.toView;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (typeof fn != 'function') {
|
|
||||||
console.error('Cannot find function or filter: ' + this.name);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var args = initialArgs || [];
|
|
||||||
for (var i = 0; i < this.args.length; i++) {
|
|
||||||
args.push(getFn(this.args[i])(model, observer, filterRegistry));
|
|
||||||
}
|
|
||||||
|
|
||||||
return fn.apply(context, args);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
function notImplemented() { throw Error('Not Implemented'); }
|
|
||||||
|
|
||||||
var unaryOperators = {
|
|
||||||
'+': function (v) { return +v; },
|
|
||||||
'-': function (v) { return -v; },
|
|
||||||
'!': function (v) { return !v; }
|
|
||||||
};
|
|
||||||
|
|
||||||
var binaryOperators = {
|
|
||||||
'+': function (l, r) { return l + r; },
|
|
||||||
'-': function (l, r) { return l - r; },
|
|
||||||
'*': function (l, r) { return l * r; },
|
|
||||||
'/': function (l, r) { return l / r; },
|
|
||||||
'%': function (l, r) { return l % r; },
|
|
||||||
'<': function (l, r) { return l < r; },
|
|
||||||
'>': function (l, r) { return l > r; },
|
|
||||||
'<=': function (l, r) { return l <= r; },
|
|
||||||
'>=': function (l, r) { return l >= r; },
|
|
||||||
'==': function (l, r) { return l == r; },
|
|
||||||
'!=': function (l, r) { return l != r; },
|
|
||||||
'===': function (l, r) { return l === r; },
|
|
||||||
'!==': function (l, r) { return l !== r; },
|
|
||||||
'&&': function (l, r) { return l && r; },
|
|
||||||
'||': function (l, r) { return l || r; },
|
|
||||||
};
|
|
||||||
|
|
||||||
function getFn(arg) {
|
|
||||||
return typeof arg == 'function' ? arg : arg.valueFn();
|
|
||||||
}
|
|
||||||
|
|
||||||
function ASTDelegate() {
|
|
||||||
this.expression = null;
|
|
||||||
this.filters = [];
|
|
||||||
this.deps = {};
|
|
||||||
this.currentPath = undefined;
|
|
||||||
this.scopeIdent = undefined;
|
|
||||||
this.indexIdent = undefined;
|
|
||||||
this.dynamicDeps = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
ASTDelegate.prototype = {
|
|
||||||
createUnaryExpression: function (op, argument) {
|
|
||||||
if (!unaryOperators[op])
|
|
||||||
throw Error('Disallowed operator: ' + op);
|
|
||||||
|
|
||||||
argument = getFn(argument);
|
|
||||||
|
|
||||||
return function (model, observer, filterRegistry) {
|
|
||||||
return unaryOperators[op](argument(model, observer, filterRegistry));
|
|
||||||
};
|
|
||||||
},
|
|
||||||
|
|
||||||
createBinaryExpression: function (op, left, right) {
|
|
||||||
if (!binaryOperators[op])
|
|
||||||
throw Error('Disallowed operator: ' + op);
|
|
||||||
|
|
||||||
left = getFn(left);
|
|
||||||
right = getFn(right);
|
|
||||||
|
|
||||||
switch (op) {
|
|
||||||
case '||':
|
|
||||||
this.dynamicDeps = true;
|
|
||||||
return function (model, observer, filterRegistry) {
|
|
||||||
return left(model, observer, filterRegistry) ||
|
|
||||||
right(model, observer, filterRegistry);
|
|
||||||
};
|
|
||||||
case '&&':
|
|
||||||
this.dynamicDeps = true;
|
|
||||||
return function (model, observer, filterRegistry) {
|
|
||||||
return left(model, observer, filterRegistry) &&
|
|
||||||
right(model, observer, filterRegistry);
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
return function (model, observer, filterRegistry) {
|
|
||||||
return binaryOperators[op](left(model, observer, filterRegistry),
|
|
||||||
right(model, observer, filterRegistry));
|
|
||||||
};
|
|
||||||
},
|
|
||||||
|
|
||||||
createConditionalExpression: function (test, consequent, alternate) {
|
|
||||||
test = getFn(test);
|
|
||||||
consequent = getFn(consequent);
|
|
||||||
alternate = getFn(alternate);
|
|
||||||
|
|
||||||
this.dynamicDeps = true;
|
|
||||||
|
|
||||||
return function (model, observer, filterRegistry) {
|
|
||||||
return test(model, observer, filterRegistry) ?
|
|
||||||
consequent(model, observer, filterRegistry) :
|
|
||||||
alternate(model, observer, filterRegistry);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
createIdentifier: function (name) {
|
|
||||||
var ident = new IdentPath(name);
|
|
||||||
ident.type = 'Identifier';
|
|
||||||
return ident;
|
|
||||||
},
|
|
||||||
|
|
||||||
createMemberExpression: function (accessor, object, property) {
|
|
||||||
var ex = new MemberExpression(object, property, accessor);
|
|
||||||
if (ex.dynamicDeps)
|
|
||||||
this.dynamicDeps = true;
|
|
||||||
return ex;
|
|
||||||
},
|
|
||||||
|
|
||||||
createCallExpression: function (expression, args) {
|
|
||||||
if (!(expression instanceof IdentPath))
|
|
||||||
throw Error('Only identifier function invocations are allowed');
|
|
||||||
|
|
||||||
var filter = new Filter(expression.name, args);
|
|
||||||
|
|
||||||
return function (model, observer, filterRegistry) {
|
|
||||||
return filter.transform(model, observer, filterRegistry, false);
|
|
||||||
};
|
|
||||||
},
|
|
||||||
|
|
||||||
createLiteral: function (token) {
|
|
||||||
return new Literal(token.value);
|
|
||||||
},
|
|
||||||
|
|
||||||
createArrayExpression: function (elements) {
|
|
||||||
for (var i = 0; i < elements.length; i++)
|
|
||||||
elements[i] = getFn(elements[i]);
|
|
||||||
|
|
||||||
return function (model, observer, filterRegistry) {
|
|
||||||
var arr = []
|
|
||||||
for (var i = 0; i < elements.length; i++)
|
|
||||||
arr.push(elements[i](model, observer, filterRegistry));
|
|
||||||
return arr;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
createProperty: function (kind, key, value) {
|
|
||||||
return {
|
|
||||||
key: key instanceof IdentPath ? key.name : key.value,
|
|
||||||
value: value
|
|
||||||
};
|
|
||||||
},
|
|
||||||
|
|
||||||
createObjectExpression: function (properties) {
|
|
||||||
for (var i = 0; i < properties.length; i++)
|
|
||||||
properties[i].value = getFn(properties[i].value);
|
|
||||||
|
|
||||||
return function (model, observer, filterRegistry) {
|
|
||||||
var obj = {};
|
|
||||||
for (var i = 0; i < properties.length; i++)
|
|
||||||
obj[properties[i].key] =
|
|
||||||
properties[i].value(model, observer, filterRegistry);
|
|
||||||
return obj;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
createFilter: function (name, args) {
|
|
||||||
this.filters.push(new Filter(name, args));
|
|
||||||
},
|
|
||||||
|
|
||||||
createAsExpression: function (expression, scopeIdent) {
|
|
||||||
this.expression = expression;
|
|
||||||
this.scopeIdent = scopeIdent;
|
|
||||||
},
|
|
||||||
|
|
||||||
createInExpression: function (scopeIdent, indexIdent, expression) {
|
|
||||||
this.expression = expression;
|
|
||||||
this.scopeIdent = scopeIdent;
|
|
||||||
this.indexIdent = indexIdent;
|
|
||||||
},
|
|
||||||
|
|
||||||
createTopLevel: function (expression) {
|
|
||||||
this.expression = expression;
|
|
||||||
},
|
|
||||||
|
|
||||||
createThisExpression: notImplemented
|
|
||||||
}
|
|
||||||
|
|
||||||
function Expression(delegate) {
|
|
||||||
this.scopeIdent = delegate.scopeIdent;
|
|
||||||
this.indexIdent = delegate.indexIdent;
|
|
||||||
|
|
||||||
if (!delegate.expression)
|
|
||||||
throw Error('No expression found.');
|
|
||||||
|
|
||||||
this.expression = delegate.expression;
|
|
||||||
getFn(this.expression); // forces enumeration of path dependencies
|
|
||||||
|
|
||||||
this.filters = delegate.filters;
|
|
||||||
this.dynamicDeps = delegate.dynamicDeps;
|
|
||||||
}
|
|
||||||
|
|
||||||
Expression.prototype = {
|
|
||||||
getValue: function (model, isBackConvert, changedModel, observer) {
|
|
||||||
var value = getFn(this.expression)(model.context, observer, changedModel);
|
|
||||||
for (var i = 0; i < this.filters.length; i++) {
|
|
||||||
value = this.filters[i].transform(model.context, observer, model.context, isBackConvert, [value]);
|
|
||||||
}
|
|
||||||
|
|
||||||
return value;
|
|
||||||
},
|
|
||||||
|
|
||||||
setValue: function (model, newValue, filterRegistry) {
|
|
||||||
var count = this.filters ? this.filters.length : 0;
|
|
||||||
while (count-- > 0) {
|
|
||||||
newValue = this.filters[count].transform(model, undefined,
|
|
||||||
filterRegistry, true, [newValue]);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.expression.setValue)
|
|
||||||
return this.expression.setValue(model, newValue);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Converts a style property name to a css property name. For example:
|
|
||||||
* "WebkitUserSelect" to "-webkit-user-select"
|
|
||||||
*/
|
|
||||||
function convertStylePropertyName(name) {
|
|
||||||
return String(name).replace(/[A-Z]/g, function (c) {
|
|
||||||
return '-' + c.toLowerCase();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
var parentScopeName = '@' + Math.random().toString(36).slice(2);
|
|
||||||
|
|
||||||
// Single ident paths must bind directly to the appropriate scope object.
|
|
||||||
// I.e. Pushed values in two-bindings need to be assigned to the actual model
|
|
||||||
// object.
|
|
||||||
function findScope(model, prop) {
|
|
||||||
while (model[parentScopeName] &&
|
|
||||||
!Object.prototype.hasOwnProperty.call(model, prop)) {
|
|
||||||
model = model[parentScopeName];
|
|
||||||
}
|
|
||||||
|
|
||||||
return model;
|
|
||||||
}
|
|
||||||
|
|
||||||
function isLiteralExpression(pathString) {
|
|
||||||
switch (pathString) {
|
|
||||||
case '':
|
|
||||||
return false;
|
|
||||||
|
|
||||||
case 'false':
|
|
||||||
case 'null':
|
|
||||||
case 'true':
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!isNaN(Number(pathString)))
|
|
||||||
return true;
|
|
||||||
|
|
||||||
return false;
|
|
||||||
};
|
|
||||||
|
|
||||||
function PolymerExpressions() { }
|
|
||||||
|
|
||||||
PolymerExpressions.prototype = {
|
|
||||||
// "built-in" filters
|
|
||||||
styleObject: function (value) {
|
|
||||||
var parts = [];
|
|
||||||
for (var key in value) {
|
|
||||||
parts.push(convertStylePropertyName(key) + ': ' + value[key]);
|
|
||||||
}
|
|
||||||
return parts.join('; ');
|
|
||||||
},
|
|
||||||
|
|
||||||
tokenList: function (value) {
|
|
||||||
var tokens = [];
|
|
||||||
for (var key in value) {
|
|
||||||
if (value[key])
|
|
||||||
tokens.push(key);
|
|
||||||
}
|
|
||||||
return tokens.join(' ');
|
|
||||||
},
|
|
||||||
|
|
||||||
// binding delegate API
|
|
||||||
prepareInstancePositionChanged: function (template) {
|
|
||||||
var indexIdent = template.polymerExpressionIndexIdent_;
|
|
||||||
if (!indexIdent)
|
|
||||||
return;
|
|
||||||
|
|
||||||
return function (templateInstance, index) {
|
|
||||||
templateInstance.model[indexIdent] = index;
|
|
||||||
};
|
|
||||||
},
|
|
||||||
|
|
||||||
prepareInstanceModel: function (template) {
|
|
||||||
var scopeName = template.polymerExpressionScopeIdent_;
|
|
||||||
if (!scopeName)
|
|
||||||
return;
|
|
||||||
|
|
||||||
var parentScope = template.templateInstance ?
|
|
||||||
template.templateInstance.model :
|
|
||||||
template.model;
|
|
||||||
|
|
||||||
var indexName = template.polymerExpressionIndexIdent_;
|
|
||||||
|
|
||||||
return function (model) {
|
|
||||||
return createScopeObject(parentScope, model, scopeName, indexName);
|
|
||||||
};
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
var createScopeObject = ('__proto__' in {}) ?
|
|
||||||
function (parentScope, model, scopeName, indexName) {
|
|
||||||
var scope = {};
|
|
||||||
scope[scopeName] = model;
|
|
||||||
scope[indexName] = undefined;
|
|
||||||
scope[parentScopeName] = parentScope;
|
|
||||||
scope.__proto__ = parentScope;
|
|
||||||
return scope;
|
|
||||||
} :
|
|
||||||
function (parentScope, model, scopeName, indexName) {
|
|
||||||
var scope = Object.create(parentScope);
|
|
||||||
Object.defineProperty(scope, scopeName,
|
|
||||||
{ value: model, configurable: true, writable: true });
|
|
||||||
Object.defineProperty(scope, indexName,
|
|
||||||
{ value: undefined, configurable: true, writable: true });
|
|
||||||
Object.defineProperty(scope, parentScopeName,
|
|
||||||
{ value: parentScope, configurable: true, writable: true });
|
|
||||||
return scope;
|
|
||||||
};
|
|
||||||
|
|
||||||
global.PolymerExpressions = PolymerExpressions;
|
|
||||||
PolymerExpressions.getExpression = getExpression;
|
|
||||||
})(module.exports);
|
|
@ -6,8 +6,8 @@
|
|||||||
"version": "8.2.0-alpha.0",
|
"version": "8.2.0-alpha.0",
|
||||||
"homepage": "https://nativescript.org",
|
"homepage": "https://nativescript.org",
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/NativeScript/NativeScript"
|
"url": "https://github.com/NativeScript/NativeScript"
|
||||||
},
|
},
|
||||||
"sideEffects": [
|
"sideEffects": [
|
||||||
"bundle-entry-points.js",
|
"bundle-entry-points.js",
|
||||||
@ -15,36 +15,37 @@
|
|||||||
"./globals"
|
"./globals"
|
||||||
],
|
],
|
||||||
"files": [
|
"files": [
|
||||||
"**/*.d.ts",
|
"**/*.d.ts",
|
||||||
"**/*.js",
|
"**/*.js",
|
||||||
"**/*.map",
|
"**/*.map",
|
||||||
"**/platforms/ios/**",
|
"**/platforms/ios/**",
|
||||||
"**/platforms/android/**",
|
"**/platforms/android/**",
|
||||||
"**/package.json"
|
"**/package.json"
|
||||||
],
|
],
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"postinstall": "node cli-hooks/postinstall.js",
|
"postinstall": "node cli-hooks/postinstall.js",
|
||||||
"preuninstall": "node cli-hooks/preuninstall.js"
|
"preuninstall": "node cli-hooks/preuninstall.js"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"css-tree": "^1.1.2",
|
|
||||||
"@nativescript/hook": "~2.0.0",
|
"@nativescript/hook": "~2.0.0",
|
||||||
|
"acorn": "^8.7.0",
|
||||||
|
"css-tree": "^1.1.2",
|
||||||
"reduce-css-calc": "^2.1.7",
|
"reduce-css-calc": "^2.1.7",
|
||||||
"tslib": "~2.0.0"
|
"tslib": "~2.0.0"
|
||||||
},
|
},
|
||||||
"nativescript": {
|
"nativescript": {
|
||||||
"platforms": {
|
"platforms": {
|
||||||
"ios": "6.0.0",
|
"ios": "6.0.0",
|
||||||
"android": "6.0.0"
|
"android": "6.0.0"
|
||||||
},
|
},
|
||||||
"hooks": [
|
"hooks": [
|
||||||
{
|
{
|
||||||
"name": "nativescript-core",
|
"name": "nativescript-core",
|
||||||
"type": "before-checkForChanges",
|
"type": "before-checkForChanges",
|
||||||
"script": "cli-hooks/before-checkForChanges.js",
|
"script": "cli-hooks/before-checkForChanges.js",
|
||||||
"inject": true
|
"inject": true
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
208
packages/core/ui/core/bindable/bindable-expressions.ts
Normal file
208
packages/core/ui/core/bindable/bindable-expressions.ts
Normal file
@ -0,0 +1,208 @@
|
|||||||
|
import { parse } from 'acorn';
|
||||||
|
import { isFunction, isNullOrUndefined, isObject } from '../../../utils/types';
|
||||||
|
|
||||||
|
interface ASTExpression {
|
||||||
|
readonly type: string;
|
||||||
|
readonly [prop: string]: any;
|
||||||
|
}
|
||||||
|
|
||||||
|
const expressionsCache = {};
|
||||||
|
|
||||||
|
// prettier-ignore
|
||||||
|
const unaryOperators = {
|
||||||
|
'+': (v) => +v,
|
||||||
|
'-': (v) => -v,
|
||||||
|
'!': (v) => !v,
|
||||||
|
'void': (v) => void v,
|
||||||
|
'typeof': (v) => typeof v
|
||||||
|
};
|
||||||
|
|
||||||
|
// prettier-ignore
|
||||||
|
const binaryOperators = {
|
||||||
|
'+': (l, r) => l + r,
|
||||||
|
'-': (l, r) => l - r,
|
||||||
|
'*': (l, r) => l * r,
|
||||||
|
'/': (l, r) => l / r,
|
||||||
|
'%': (l, r) => l % r,
|
||||||
|
'<': (l, r) => l < r,
|
||||||
|
'>': (l, r) => l > r,
|
||||||
|
'<=': (l, r) => l <= r,
|
||||||
|
'>=': (l, r) => l >= r,
|
||||||
|
'==': (l, r) => l == r,
|
||||||
|
'!=': (l, r) => l != r,
|
||||||
|
'===': (l, r) => l === r,
|
||||||
|
'!==': (l, r) => l !== r,
|
||||||
|
'|': (l, r) => l | r,
|
||||||
|
'in': (l, r) => l in r,
|
||||||
|
'instanceof': (l, r) => l instanceof r
|
||||||
|
};
|
||||||
|
|
||||||
|
// prettier-ignore
|
||||||
|
const logicalOperators = {
|
||||||
|
'&&': (l, r) => l && r(),
|
||||||
|
'||': (l, r) => l || r(),
|
||||||
|
'??': (l, r) => l ?? r()
|
||||||
|
};
|
||||||
|
|
||||||
|
// prettier-ignore
|
||||||
|
const expressionParsers = {
|
||||||
|
'ArrayExpression': (expression: ASTExpression, model, isBackConvert: boolean, changedModel) => {
|
||||||
|
const parsed = [];
|
||||||
|
for (let element of expression.elements) {
|
||||||
|
let value = convertExpressionToValue(element, model, isBackConvert, changedModel);
|
||||||
|
element.type == 'SpreadElement' ? parsed.push(...value) : parsed.push(value);
|
||||||
|
}
|
||||||
|
return parsed;
|
||||||
|
},
|
||||||
|
'BinaryExpression': (expression: ASTExpression, model, isBackConvert: boolean, changedModel) => {
|
||||||
|
if (binaryOperators[expression.operator] == null) {
|
||||||
|
throw new Error('Disallowed binary operator: ' + expression.operator);
|
||||||
|
}
|
||||||
|
|
||||||
|
const left = convertExpressionToValue(expression.left, model, isBackConvert, changedModel);
|
||||||
|
const right = convertExpressionToValue(expression.right, model, isBackConvert, changedModel);
|
||||||
|
|
||||||
|
if (expression.operator == '|') {
|
||||||
|
if (right != null && isFunction(right.callback) && right.context != null && right.args != null) {
|
||||||
|
right.args.unshift(left);
|
||||||
|
return right.callback.apply(right.context, right.args);
|
||||||
|
}
|
||||||
|
throw new Error('Invalid converter after ' + expression.operator + ' operator');
|
||||||
|
}
|
||||||
|
|
||||||
|
return binaryOperators[expression.operator](left, right);
|
||||||
|
},
|
||||||
|
'CallExpression': (expression: ASTExpression, model, isBackConvert: boolean, changedModel) => {
|
||||||
|
let object;
|
||||||
|
let property;
|
||||||
|
if (expression.callee.type == 'MemberExpression') {
|
||||||
|
object = convertExpressionToValue(expression.callee.object, model, isBackConvert, changedModel);
|
||||||
|
property = expression.callee.computed ? convertExpressionToValue(expression.callee.property, model, isBackConvert, changedModel) : expression.callee.property?.name;
|
||||||
|
} else {
|
||||||
|
object = getContext(expression.callee.name, model, changedModel);
|
||||||
|
property = expression.callee?.name;
|
||||||
|
}
|
||||||
|
const callback = expression.callee.optional ? object?.[property] : object[property];
|
||||||
|
const isConverter = isObject(callback) && (isFunction(callback.toModel) || isFunction(callback.toView));
|
||||||
|
|
||||||
|
const parsedArgs = [];
|
||||||
|
for (let argument of expression.arguments) {
|
||||||
|
let value = convertExpressionToValue(argument, model, isBackConvert, changedModel);
|
||||||
|
argument.type == 'SpreadElement' ? parsedArgs.push(...value) : parsedArgs.push(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isNullOrUndefined(callback) || (!isFunction(callback) && !isConverter)) {
|
||||||
|
throw new Error('Cannot perform a call using a non-function property');
|
||||||
|
}
|
||||||
|
|
||||||
|
return isConverter ? getConverter(callback, parsedArgs, isBackConvert) : callback.apply(object, parsedArgs);
|
||||||
|
},
|
||||||
|
'ChainExpression': (expression: ASTExpression, model, isBackConvert: boolean, changedModel) => {
|
||||||
|
return convertExpressionToValue(expression.expression, model, isBackConvert, changedModel);
|
||||||
|
},
|
||||||
|
'ConditionalExpression': (expression: ASTExpression, model, isBackConvert: boolean, changedModel) => {
|
||||||
|
const test = convertExpressionToValue(expression.test, model, isBackConvert, changedModel);
|
||||||
|
return convertExpressionToValue(expression[test ? 'consequent' : 'alternate'], model, isBackConvert, changedModel);
|
||||||
|
},
|
||||||
|
'Identifier': (expression: ASTExpression, model, isBackConvert: boolean, changedModel) => {
|
||||||
|
const context = getContext(expression.name, model, changedModel);
|
||||||
|
return context[expression.name];
|
||||||
|
},
|
||||||
|
'Literal': (expression: ASTExpression, model, isBackConvert: boolean, changedModel) => {
|
||||||
|
return expression.regex != null ? new RegExp(expression.regex.pattern, expression.regex.flags) : expression.value;
|
||||||
|
},
|
||||||
|
'LogicalExpression': (expression: ASTExpression, model, isBackConvert: boolean, changedModel) => {
|
||||||
|
if (logicalOperators[expression.operator] == null) {
|
||||||
|
throw new Error('Disallowed logical operator: ' + expression.operator);
|
||||||
|
}
|
||||||
|
const left = convertExpressionToValue(expression.left, model, isBackConvert, changedModel);
|
||||||
|
return logicalOperators[expression.operator](left, () => convertExpressionToValue(expression.right, model, isBackConvert, changedModel));
|
||||||
|
},
|
||||||
|
'MemberExpression': (expression: ASTExpression, model, isBackConvert: boolean, changedModel) => {
|
||||||
|
const object = convertExpressionToValue(expression.object, model, isBackConvert, changedModel);
|
||||||
|
const property = expression.computed ? convertExpressionToValue(expression.property, model, isBackConvert, changedModel) : expression.property?.name;
|
||||||
|
return expression.optional ? object?.[property] : object[property];
|
||||||
|
},
|
||||||
|
'NewExpression': (expression: ASTExpression, model, isBackConvert: boolean, changedModel) => {
|
||||||
|
const callback = convertExpressionToValue(expression.callee, model, isBackConvert, changedModel);
|
||||||
|
const parsedArgs = [];
|
||||||
|
for (let argument of expression.arguments) {
|
||||||
|
let value = convertExpressionToValue(argument, model, isBackConvert, changedModel);
|
||||||
|
argument.type == 'SpreadElement' ? parsedArgs.push(...value) : parsedArgs.push(value);
|
||||||
|
}
|
||||||
|
return new callback(...parsedArgs);
|
||||||
|
},
|
||||||
|
'ObjectExpression': (expression: ASTExpression, model, isBackConvert: boolean, changedModel) => {
|
||||||
|
const parsedObject = {};
|
||||||
|
for (let property of expression.properties) {
|
||||||
|
const value = convertExpressionToValue(property, model, isBackConvert, changedModel);
|
||||||
|
Object.assign(parsedObject, value);
|
||||||
|
}
|
||||||
|
return parsedObject;
|
||||||
|
},
|
||||||
|
'Property': (expression: ASTExpression, model, isBackConvert: boolean, changedModel) => {
|
||||||
|
const key = expression.computed ? convertExpressionToValue(expression.key, model, isBackConvert, changedModel) : expression.key?.name;
|
||||||
|
const value = convertExpressionToValue(expression.value, model, isBackConvert, changedModel);
|
||||||
|
return {[key]: value};
|
||||||
|
},
|
||||||
|
'SpreadElement': (expression: ASTExpression, model, isBackConvert: boolean, changedModel) => {
|
||||||
|
const argument = convertExpressionToValue(expression.argument, model, isBackConvert, changedModel);
|
||||||
|
return argument;
|
||||||
|
},
|
||||||
|
'TemplateElement': (expression: ASTExpression, model, isBackConvert: boolean, changedModel) => {
|
||||||
|
return expression.value.cooked;
|
||||||
|
},
|
||||||
|
'TemplateLiteral': (expression: ASTExpression, model, isBackConvert: boolean, changedModel) => {
|
||||||
|
let parsedText = '';
|
||||||
|
for (let q of expression.quasis) {
|
||||||
|
parsedText += convertExpressionToValue(q, model, isBackConvert, changedModel);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (let ex of expression.expressions) {
|
||||||
|
parsedText += convertExpressionToValue(ex, model, isBackConvert, changedModel);
|
||||||
|
}
|
||||||
|
return parsedText;
|
||||||
|
},
|
||||||
|
'UnaryExpression': (expression: ASTExpression, model, isBackConvert: boolean, changedModel) => {
|
||||||
|
if (unaryOperators[expression.operator] == null) {
|
||||||
|
throw Error('Disallowed unary operator: ' + expression.operator);
|
||||||
|
}
|
||||||
|
|
||||||
|
const argument = convertExpressionToValue(expression.argument, model, isBackConvert, changedModel);
|
||||||
|
return unaryOperators[expression.operator](argument);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
function getContext(key, model, changedModel) {
|
||||||
|
return key in changedModel ? changedModel : model;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getConverter(context, args, isBackConvert: boolean) {
|
||||||
|
const converter = { callback: null, context, args };
|
||||||
|
let callback = isBackConvert ? context.toModel : context.toView;
|
||||||
|
if (callback == null) {
|
||||||
|
callback = Function.prototype;
|
||||||
|
}
|
||||||
|
converter.callback = callback;
|
||||||
|
return converter;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function parseExpression(expressionText: string): ASTExpression {
|
||||||
|
let expression = expressionsCache[expressionText];
|
||||||
|
if (expression == null) {
|
||||||
|
const program: any = parse(expressionText, { ecmaVersion: 2020 });
|
||||||
|
const statements = program.body;
|
||||||
|
for (let statement of statements) {
|
||||||
|
if (statement.type == 'ExpressionStatement') {
|
||||||
|
expression = statement.expression;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
expressionsCache[expressionText] = expression;
|
||||||
|
}
|
||||||
|
return expression;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function convertExpressionToValue(expression: ASTExpression, model, isBackConvert: boolean, changedModel) {
|
||||||
|
return expressionParsers[expression.type](expression, model, isBackConvert, changedModel);
|
||||||
|
}
|
@ -8,10 +8,9 @@ import { addWeakEventListener, removeWeakEventListener } from '../weak-event-lis
|
|||||||
import { bindingConstants, parentsRegex } from '../../builder/binding-builder';
|
import { bindingConstants, parentsRegex } from '../../builder/binding-builder';
|
||||||
import { escapeRegexSymbols } from '../../../utils';
|
import { escapeRegexSymbols } from '../../../utils';
|
||||||
import { Trace } from '../../../trace';
|
import { Trace } from '../../../trace';
|
||||||
|
import { parseExpression, convertExpressionToValue } from './bindable-expressions';
|
||||||
import * as types from '../../../utils/types';
|
import * as types from '../../../utils/types';
|
||||||
import * as bindableResources from './bindable-resources';
|
import * as bindableResources from './bindable-resources';
|
||||||
const polymerExpressions = require('../../../js-libs/polymer-expressions');
|
|
||||||
import { PolymerExpressions } from '../../../js-libs/polymer-expressions';
|
|
||||||
|
|
||||||
const contextKey = 'context';
|
const contextKey = 'context';
|
||||||
// this regex is used to get parameters inside [] for example:
|
// this regex is used to get parameters inside [] for example:
|
||||||
@ -360,7 +359,7 @@ export class Binding {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const updateExpression = this.prepareExpressionForUpdate();
|
const updateExpression = this.prepareExpressionForUpdate();
|
||||||
this.prepareContextForExpression(changedModel, updateExpression, undefined);
|
this.prepareContextForExpression(changedModel, updateExpression);
|
||||||
|
|
||||||
const expressionValue = this._getExpressionValue(updateExpression, true, changedModel);
|
const expressionValue = this._getExpressionValue(updateExpression, true, changedModel);
|
||||||
if (expressionValue instanceof Error) {
|
if (expressionValue instanceof Error) {
|
||||||
@ -374,13 +373,21 @@ export class Binding {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private _getExpressionValue(expression: string, isBackConvert: boolean, changedModel: any): any {
|
private _getExpressionValue(expression: string, isBackConvert: boolean, changedModel: any): any {
|
||||||
|
let result: any = '';
|
||||||
|
|
||||||
if (!__UI_USE_EXTERNAL_RENDERER__) {
|
if (!__UI_USE_EXTERNAL_RENDERER__) {
|
||||||
|
let context;
|
||||||
|
const addedProps = [];
|
||||||
try {
|
try {
|
||||||
const exp = PolymerExpressions.getExpression(expression);
|
let exp;
|
||||||
|
try {
|
||||||
|
exp = parseExpression(expression);
|
||||||
|
} catch (e) {
|
||||||
|
result = e;
|
||||||
|
}
|
||||||
|
|
||||||
if (exp) {
|
if (exp) {
|
||||||
const context = (this.source && this.source.get && this.source.get()) || global;
|
context = (this.source && this.source.get && this.source.get()) || global;
|
||||||
const model = {};
|
|
||||||
const addedProps = [];
|
|
||||||
const resources = bindableResources.get();
|
const resources = bindableResources.get();
|
||||||
for (const prop in resources) {
|
for (const prop in resources) {
|
||||||
if (resources.hasOwnProperty(prop) && !context.hasOwnProperty(prop)) {
|
if (resources.hasOwnProperty(prop) && !context.hasOwnProperty(prop)) {
|
||||||
@ -389,26 +396,27 @@ export class Binding {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
this.prepareContextForExpression(context, expression, addedProps);
|
// For expressions, there are also cases when binding must be updated after component is loaded (e.g. ListView)
|
||||||
model[contextKey] = context;
|
if (this.prepareContextForExpression(context, expression, addedProps)) {
|
||||||
const result = exp.getValue(model, isBackConvert, changedModel ? changedModel : model);
|
result = convertExpressionToValue(exp, context, isBackConvert, changedModel ? changedModel : context);
|
||||||
// clear added props
|
} else {
|
||||||
const addedPropsLength = addedProps.length;
|
const targetInstance = this.target.get();
|
||||||
for (let i = 0; i < addedPropsLength; i++) {
|
targetInstance.off('loaded', this.loadedHandlerVisualTreeBinding, this);
|
||||||
delete context[addedProps[i]];
|
targetInstance.on('loaded', this.loadedHandlerVisualTreeBinding, this);
|
||||||
}
|
}
|
||||||
addedProps.length = 0;
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return new Error(expression + ' is not a valid expression.');
|
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
const errorMessage = 'Run-time error occured in file: ' + e.sourceURL + ' at line: ' + e.line + ' and column: ' + e.column;
|
const errorMessage = 'Run-time error occured in file: ' + e.sourceURL + ' at line: ' + e.line + ' and column: ' + e.column;
|
||||||
|
result = new Error(errorMessage);
|
||||||
return new Error(errorMessage);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Clear added props
|
||||||
|
for (let prop of addedProps) {
|
||||||
|
delete context[prop];
|
||||||
|
}
|
||||||
|
addedProps.length = 0;
|
||||||
}
|
}
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
public onSourcePropertyChanged(data: PropertyChangeData) {
|
public onSourcePropertyChanged(data: PropertyChangeData) {
|
||||||
@ -472,24 +480,24 @@ export class Binding {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private prepareContextForExpression(model: Object, expression: string, newProps: Array<string>) {
|
private prepareContextForExpression(model: Object, expression: string, addedProps = []) {
|
||||||
|
const targetInstance = this.target.get();
|
||||||
|
|
||||||
|
let success = true;
|
||||||
let parentViewAndIndex: { view: ViewBase; index: number };
|
let parentViewAndIndex: { view: ViewBase; index: number };
|
||||||
let parentView;
|
let parentView;
|
||||||
const addedProps = newProps || [];
|
|
||||||
let expressionCP = expression;
|
let expressionCP = expression;
|
||||||
if (expressionCP.indexOf(bc.bindingValueKey) > -1) {
|
if (expressionCP.indexOf(bc.bindingValueKey) > -1) {
|
||||||
model[bc.bindingValueKey] = model;
|
model[bc.bindingValueKey] = model;
|
||||||
addedProps.push(bc.bindingValueKey);
|
addedProps.push(bc.bindingValueKey);
|
||||||
}
|
}
|
||||||
|
|
||||||
let success = true;
|
|
||||||
|
|
||||||
const parentsArray = expressionCP.match(parentsRegex);
|
const parentsArray = expressionCP.match(parentsRegex);
|
||||||
if (parentsArray) {
|
if (parentsArray) {
|
||||||
for (let i = 0; i < parentsArray.length; i++) {
|
for (let i = 0; i < parentsArray.length; i++) {
|
||||||
// This prevents later checks to mistake $parents[] for $parent
|
// This prevents later checks to mistake $parents[] for $parent
|
||||||
expressionCP = expressionCP.replace(parentsArray[i], '');
|
expressionCP = expressionCP.replace(parentsArray[i], '');
|
||||||
parentViewAndIndex = this.getParentView(this.target.get(), parentsArray[i]);
|
parentViewAndIndex = this.getParentView(targetInstance, parentsArray[i]);
|
||||||
if (parentViewAndIndex.view) {
|
if (parentViewAndIndex.view) {
|
||||||
model[bc.parentsValueKey] = model[bc.parentsValueKey] || {};
|
model[bc.parentsValueKey] = model[bc.parentsValueKey] || {};
|
||||||
model[bc.parentsValueKey][parentViewAndIndex.index] = parentViewAndIndex.view.bindingContext;
|
model[bc.parentsValueKey][parentViewAndIndex.index] = parentViewAndIndex.view.bindingContext;
|
||||||
@ -501,7 +509,7 @@ export class Binding {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (expressionCP.indexOf(bc.parentValueKey) > -1) {
|
if (expressionCP.indexOf(bc.parentValueKey) > -1) {
|
||||||
parentView = this.getParentView(this.target.get(), bc.parentValueKey).view;
|
parentView = this.getParentView(targetInstance, bc.parentValueKey).view;
|
||||||
if (parentView) {
|
if (parentView) {
|
||||||
model[bc.parentValueKey] = parentView.bindingContext;
|
model[bc.parentValueKey] = parentView.bindingContext;
|
||||||
addedProps.push(bc.parentValueKey);
|
addedProps.push(bc.parentValueKey);
|
||||||
@ -509,13 +517,7 @@ export class Binding {
|
|||||||
success = false;
|
success = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return success;
|
||||||
// For expressions, there are also cases when binding must be updated after component is loaded (e.g. ListView)
|
|
||||||
if (!success) {
|
|
||||||
const targetInstance = this.target.get();
|
|
||||||
targetInstance.off('loaded', this.loadedHandlerVisualTreeBinding, this);
|
|
||||||
targetInstance.on('loaded', this.loadedHandlerVisualTreeBinding, this);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private getSourcePropertyValue() {
|
private getSourcePropertyValue() {
|
||||||
|
Reference in New Issue
Block a user