diff --git a/.vscode/launch.json b/.vscode/launch.json index c1b3997c..76cff8d7 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -12,12 +12,12 @@ "url": "http://http://192.168.0.187:8080/", "webRoot": "${workspaceFolder}/", }, - { "type": "node", "request": "launch", "name": "Launch Program", - "program": "${workspaceFolder}/src/test.ts", + "program": "${workspaceFolder}/node_modules/@web-atoms/unit-test/index.js", + "args": ["./dist/tests"], "cwd": "${workspaceFolder}", "protocol": "inspector", "outFiles": [ diff --git a/package-lock.json b/package-lock.json index 1b618304..3d008af5 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "@web-atoms/core", - "version": "1.1.90", + "version": "1.1.105", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/package.json b/package.json index b020094e..b8a6ebe5 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@web-atoms/core", - "version": "1.1.90", + "version": "1.1.105", "description": "Web Atoms Core", "main": "index.js", "scripts": { diff --git a/src/core/AtomBridge.ts b/src/core/AtomBridge.ts index 5b3de759..7311b85f 100644 --- a/src/core/AtomBridge.ts +++ b/src/core/AtomBridge.ts @@ -26,8 +26,8 @@ export abstract class BaseElementBridge { type: string | ((n: any, ... nodes: XNode[]) => XNode) | (new (... a: any[]) => any), - node: any, - app: any): T; + node?: any, + app?: any): T; public abstract attachControl(element: T, control: IUIAtomControl): void; @@ -424,7 +424,7 @@ declare var window, global; const globalNS = (typeof window !== "undefined" ? window : (global as any)) as any; globalNS.AtomBridge = AtomBridge; -if (window) { +if (typeof window !== "undefined") { AtomBridge.instance = new AtomElementBridge(); AtomBridge.platform = "web"; } diff --git a/src/core/Bind.ts b/src/core/Bind.ts index 1a7c49b9..ec24efbe 100644 --- a/src/core/Bind.ts +++ b/src/core/Bind.ts @@ -112,7 +112,11 @@ export default class Bind { public static twoWays( sourcePath: bindingFunction, events?: string[]): Bind { - return new Bind(twoWays, sourcePath, null, events); + const b = new Bind(twoWays, sourcePath, null, events); + if (!(b.thisPathList || b.pathList)) { + throw new Error(`Failed to setup twoWay binding on ${sourcePath}`); + } + return b; } public readonly sourcePath: bindingFunction; diff --git a/src/core/ExpressionParser.ts b/src/core/ExpressionParser.ts index 43383b93..d26f34e1 100644 --- a/src/core/ExpressionParser.ts +++ b/src/core/ExpressionParser.ts @@ -7,7 +7,7 @@ export function parsePath(f: any, parseThis?: boolean): PathList[] { str = str.split("\n").filter((s) => !/^\/\//.test(s.trim())).join("\n"); - const key: string = (parseThis === undefined ? "un:" : parseThis ) + str; + const key: string = (parseThis === undefined ? "un:" : (parseThis ? "_this:" : "_noThis:") ) + str; const px1: PathList[] = viewModelParseWatchCache[key]; if (px1) { @@ -37,7 +37,16 @@ export function parsePath(f: any, parseThis?: boolean): PathList[] { const isThis: boolean = parseThis === undefined ? (index === 0 || parseThis) : parseThis; - const p: string = (isThis ? "\\_this|this" : (str.substr(0, index) || "x")).trim(); + const p: string = (isThis ? "\\_this|this" : (str.substr(0, index) || "")).trim(); + + /** + * This is the case when there is no parameter to check and there `parseThis` is false + */ + if (p.length === 0) { + const empty = []; + viewModelParseWatchCache[key] = empty; + return empty; + } str = str.substr(index + 1); @@ -116,32 +125,32 @@ interface IPathLists { const viewModelParseWatchCache2: {[key: string]: IPathLists } = {}; -export function parsePathLists(f: any, parseThis?: boolean): IPathLists { +export function parsePathLists(f: any): IPathLists { - let str: string = f.toString().trim(); + const str = f.toString().trim(); - const key: string = (parseThis === undefined ? "un:" : parseThis) + str; + const key: string = str; const px1 = viewModelParseWatchCache2[key]; if (px1) { return px1; } - str = str.split("\n").filter((s) => !/^\/\//.test(s.trim())).join("\n"); + // str = str.split("\n").filter((s) => !/^\/\//.test(s.trim())).join("\n"); - if (str.endsWith("}")) { - str = str.substr(0, str.length - 1); - } + // if (str.endsWith("}")) { + // str = str.substr(0, str.length - 1); + // } - if (str.startsWith("function (")) { - str = str.substr("function (".length); - } + // if (str.startsWith("function (")) { + // str = str.substr("function (".length); + // } - if (str.startsWith("function(")) { - str = str.substr("function(".length); - } + // if (str.startsWith("function(")) { + // str = str.substr("function(".length); + // } - str = str.trim(); + // str = str.trim(); const pl = { pathList: parsePath(str, false), diff --git a/src/core/XNode.ts b/src/core/XNode.ts index 65c56e3b..410dac50 100644 --- a/src/core/XNode.ts +++ b/src/core/XNode.ts @@ -96,55 +96,57 @@ export default class XNode { } as any; } - public static attached(): AttachedNode { + /** + * This is only for intellisense... + */ + public static attached(): AttachedNode { return { - factory: true, attached: true } as any; } + public static property(): NodeFactory { + return { + factory: true + } as any; + } + /** * Declares Root Namespace and Assembly. You can use return function to * to declare the type * @param ns Root Namespace */ public static namespace(ns: string, assemblyName: string) { - return (type: any) => { + return (type: string, isTemplate?: boolean) => { return (c) => { - for (const key in type) { - if (type.hasOwnProperty(key)) { - const element = type[key]; + for (const key in c) { + if (c.hasOwnProperty(key)) { + const element = c[key]; if (element) { const n = ns + "." + type + ":" + key + ";" + assemblyName; - if (element.factory) { - type[key] = { - factory(a?: any, ... nodes: XNode[]) { - return new XNode(n, a, nodes, true, element.isTemplate); - }, - toString() { - return n; - } - }; - } else if (element.attached) { - type[key] = (a) => { - const r = { - [n]: a - }; - Object.defineProperty(r, "toString", { - value: () => n, - enumerable: false, - configurable: false - }); - return r; + const af: any = (a) => { + const r = { + [n]: a }; - } + Object.defineProperty(r, "toString", { + value: () => n, + enumerable: false, + configurable: false + }); + return r; + }; + af.factory = (a?: any, ... nodes: any[]) => + new XNode(n, a, nodes, true, element.isTemplate ); + af.toString = () => n; + c[key] = af; } } } + const tn = ns + "." + type + ";" + assemblyName; c.factory = (a?: any, ... nodes: XNode[]) => { - return new XNode(type, a, nodes); + return new XNode(tn, a, nodes, false, isTemplate); }; - c.toString = () => type; + c.toString = () => tn; }; }; } diff --git a/src/tests/core/ExpressionParserTest.ts b/src/tests/core/ExpressionParserTest.ts index c9b18af5..9c06af23 100644 --- a/src/tests/core/ExpressionParserTest.ts +++ b/src/tests/core/ExpressionParserTest.ts @@ -1,6 +1,6 @@ import Assert from "@web-atoms/unit-test/dist/Assert"; import Test from "@web-atoms/unit-test/dist/Test"; -import { parsePath } from "../../core/ExpressionParser"; +import { parsePath, parsePathLists } from "../../core/ExpressionParser"; import { AtomTest } from "../../unit/AtomTest"; export class ExpressionParserTest extends AtomTest { @@ -77,4 +77,13 @@ export class ExpressionParserTest extends AtomTest { Assert.equals(1, p.length); } + @Test + public parseLongPath(): void { + const p = parsePathLists(`function () { + return _this.viewModel.comboBox.searchText; + }`); + + Assert.equals(1, p.thisPath.length); + } + } diff --git a/src/tests/jsx/JSXTest.ts b/src/tests/jsx/JSXTest.ts index 361eaba9..c0ea2bb2 100644 --- a/src/tests/jsx/JSXTest.ts +++ b/src/tests/jsx/JSXTest.ts @@ -1,7 +1,27 @@ +import Assert from "@web-atoms/unit-test/dist/Assert"; +import Test from "@web-atoms/unit-test/dist/Test"; +import XNode, { RootObject } from "../../core/XNode"; import { AtomTest } from "../../unit/AtomTest"; +const ns = XNode.namespace("A", "B"); +@ns("C") +class C extends RootObject { + + public static p = XNode.property(); + + public static n = XNode.attached(); + +} + export default class JSXTest extends AtomTest { - + @Test + public propertyTest() { + const x = (C.p as any).factory(); + Assert.equals("A.C:p;B", x.name); + + const g = C.n(1); + Assert.equals(g["A.C:n;B"], 1); + } } diff --git a/src/xf/XFApp.ts b/src/xf/XFApp.ts index 1ffa8a0b..6f72ca57 100644 --- a/src/xf/XFApp.ts +++ b/src/xf/XFApp.ts @@ -27,8 +27,8 @@ export default class XFApp extends A.App { this.put(NavigationService, this.resolve(XFNavigationService)); this.put(BusyIndicatorService, this.resolve(XFBusyIndicatorService)); - const s = bridge.subscribe((m) => { - this.broadcast(m.channel, m.data); + const s = bridge.subscribe((channel, data) => { + this.broadcast(channel, data); }); // register for messaging... diff --git a/src/xf/controls/AtomXFControl.ts b/src/xf/controls/AtomXFControl.ts index 0820bc27..81fb6a1e 100644 --- a/src/xf/controls/AtomXFControl.ts +++ b/src/xf/controls/AtomXFControl.ts @@ -41,6 +41,10 @@ export class AtomXFControl extends AtomComponent { return (AtomBridge.instance as any).getStaticResource(this.element, name); } + public loadXaml(text: string) { + (AtomBridge.instance as any).loadXaml(this.element, text); + } + protected setElementValue(element: any, name: string, value: any): void { if (/^event/.test(name)) { this.bindEvent(element, name.substr(5), async () => {