diff --git a/CrossPlatformModules.csproj b/CrossPlatformModules.csproj index 79b57e3d8..507e2c7a3 100644 --- a/CrossPlatformModules.csproj +++ b/CrossPlatformModules.csproj @@ -79,6 +79,7 @@ data-binding.xml + @@ -110,6 +111,7 @@ + Designer @@ -1982,8 +1984,6 @@ False - - diff --git a/apps/tests/xml-declaration/custom-code-file.ts b/apps/tests/xml-declaration/custom-code-file.ts new file mode 100644 index 000000000..b6e82d9d5 --- /dev/null +++ b/apps/tests/xml-declaration/custom-code-file.ts @@ -0,0 +1,3 @@ +export function loaded(args) { + args.object.customCodeLoaded = true; +} diff --git a/apps/tests/xml-declaration/custom-css-file.css b/apps/tests/xml-declaration/custom-css-file.css new file mode 100644 index 000000000..6b7785523 --- /dev/null +++ b/apps/tests/xml-declaration/custom-css-file.css @@ -0,0 +1,3 @@ +.MyClass { + background-color: green; +} diff --git a/apps/tests/xml-declaration/xml-declaration-tests.ts b/apps/tests/xml-declaration/xml-declaration-tests.ts index 83b639bbf..9d0079a1c 100644 --- a/apps/tests/xml-declaration/xml-declaration-tests.ts +++ b/apps/tests/xml-declaration/xml-declaration-tests.ts @@ -148,10 +148,91 @@ export function test_parse_ShouldNotCrashWithoutExports() { var file = fs.File.fromPath(fs.path.join(__dirname, "mainPage.xml")); var text = file.readTextSync(); - var v: view.View = builder.parse(text); + var v: view.View = builder.parse(text); TKUnit.assert(v instanceof view.View, "Expected result: View; Actual result: " + v + ";"); }; +export function test_parse_ShouldResolveExportsFromCodeFile() { + var page = builder.parse(""); + page._emit("loaded"); + + TKUnit.assert((page).customCodeLoaded, "Parse should resolve exports from custom code file."); +} + +export function test_parse_ShouldThrowErrorWhenInvalidCodeFileIsSpecified() { + var e: Error; + try { + builder.parse(""); + } catch (ex) { + e = ex; + } + + TKUnit.assert(e, "Expected result: Error; Actual result: " + e); +}; + +export function test_parse_ShouldResolveExportsFromCodeFileForTemplates() { + var p = builder.parse(''); + + function testAction(views: Array) { + var ctrl; + + var obj = new observable.Observable(); + obj.set("items", [1]); + obj.set("itemLoading", function (args: listViewModule.ItemEventData) { + ctrl = args.view + }); + p.bindingContext = obj; + + TKUnit.wait(0.2); + + TKUnit.assert((ctrl).customCodeLoaded, "Parse should resolve exports for templates from custom code file."); + }; + + helper.navigate(function () { return p; }); + + try { + testAction([p.content, p]); + } + finally { + helper.goBack(); + } +} + +export function test_parse_ShouldApplyCssFromCssFile() { + var newPage: Page; + var pageFactory = function (): Page { + newPage = builder.parse(""); + return newPage; + }; + + helper.navigate(pageFactory); + TKUnit.assert(newPage.isLoaded, "The page should be loaded here."); + try { + helper.assertViewBackgroundColor(newPage.content, "#008000"); + } + finally { + helper.goBack(); + } +}; + +export function test_parse_ShouldResolveExportsFromCodeFileAndApplyCssFile() { + var newPage: Page; + var pageFactory = function (): Page { + newPage = builder.parse(""); + return newPage; + }; + + helper.navigate(pageFactory); + TKUnit.assert(newPage.isLoaded, "The page should be loaded here."); + TKUnit.assert((newPage).customCodeLoaded, "Parse should resolve exports from custom code file."); + try { + helper.assertViewBackgroundColor(newPage.content, "#008000"); + } + finally { + helper.goBack(); + } +}; + export function test_parse_ShouldFindEventHandlersInExports() { var loaded; var page = builder.parse("", { diff --git a/ui/builder/component-builder.ts b/ui/builder/component-builder.ts index 5730a6a2d..c94788ec8 100644 --- a/ui/builder/component-builder.ts +++ b/ui/builder/component-builder.ts @@ -10,6 +10,7 @@ import fs = require("file-system"); import gestures = require("ui/gestures"); import bindingBuilder = require("ui/builder/binding-builder"); import platform = require("platform"); +import pages = require("ui/page"); var UI_PATH = "ui/"; var MODULES = { @@ -28,6 +29,8 @@ var ROW_SPAN = "rowSpan"; var DOCK = "dock"; var LEFT = "left"; var TOP = "top"; +var CODEFILE = "codeFile"; +var CSSFILE = "cssFile"; export var specialProperties: Array = [ ROW, @@ -37,6 +40,8 @@ export var specialProperties: Array = [ DOCK, LEFT, TOP, + CODEFILE, + CSSFILE ] var eventHandlers = {}; @@ -78,6 +83,42 @@ export function getComponentModule(elementName: string, namespace: string, attri throw new Error("Cannot create module " + moduleId + ". " + ex + ". StackTrace: " + ex.stack); } + if (attributes) { + if (attributes[CODEFILE]) { + if (instance instanceof pages.Page) { + var codeFilePath = attributes[CODEFILE].trim(); + if (codeFilePath.indexOf("~/") === 0) { + codeFilePath = fs.path.join(fs.knownFolders.currentApp().path, codeFilePath.replace("~/", "")); + } + try { + exports = require(codeFilePath); + (instance).exports = exports; + } catch (ex) { + throw new Error(`Code file with path "${codeFilePath}" cannot be found!`); + } + } else { + throw new Error("Code file atribute is valid only for pages!"); + } + } + + if (attributes[CSSFILE]) { + if (instance instanceof pages.Page) { + var cssFilePath = attributes[CSSFILE].trim(); + if (cssFilePath.indexOf("~/") === 0) { + cssFilePath = fs.path.join(fs.knownFolders.currentApp().path, cssFilePath.replace("~/", "")); + } + if (fs.File.exists(cssFilePath)) { + (instance).addCssFile(cssFilePath); + instance[CSSFILE] = true; + } else { + throw new Error(`Css file with path "${cssFilePath}" cannot be found!`); + } + } else { + throw new Error("Css file atribute is valid only for pages!"); + } + } + } + if (instance && instanceModule) { var bindings = new Array(); diff --git a/ui/frame/frame-common.ts b/ui/frame/frame-common.ts index 4c1f52d7a..6450fc2b5 100644 --- a/ui/frame/frame-common.ts +++ b/ui/frame/frame-common.ts @@ -66,9 +66,9 @@ export function resolvePageFromEntry(entry: definition.NavigationEntry): pages.P throw new Error("Failed to load Page from entry.moduleName: " + entry.moduleName); } - // Possible CSS file path. + // Possible CSS file path. Add it only if CSS not already specified and loaded from cssFile Page attribute in XML. var cssFileName = fileResolverModule.resolveFileName(moduleNamePath, "css"); - if (cssFileName) { + if (cssFileName && !page["cssFile"]) { page.addCssFile(cssFileName); } }