diff --git a/CrossPlatformModules.csproj b/CrossPlatformModules.csproj
index 43aa68733..59a20fcbd 100644
--- a/CrossPlatformModules.csproj
+++ b/CrossPlatformModules.csproj
@@ -82,6 +82,7 @@
main-page.xml
+
main-page.xml
@@ -96,19 +97,18 @@
details-page.xml
-
- main-page.xml
-
binding_tests.xml
+
+
@@ -167,6 +167,10 @@
+
+
+ file-name-resolver.d.ts
+
image-source.d.ts
@@ -537,9 +541,22 @@
Designer
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/apps/editable-text-demo/app.ts b/apps/editable-text-demo/app.ts
index 0f172a73b..83670f56b 100644
--- a/apps/editable-text-demo/app.ts
+++ b/apps/editable-text-demo/app.ts
@@ -1,3 +1,3 @@
import application = require("application");
application.mainModule = "app/main-page";
-application.start();
\ No newline at end of file
+application.start();
diff --git a/apps/platform-specific-template/app.ts b/apps/platform-specific-template/app.ts
new file mode 100644
index 000000000..83670f56b
--- /dev/null
+++ b/apps/platform-specific-template/app.ts
@@ -0,0 +1,3 @@
+import application = require("application");
+application.mainModule = "app/main-page";
+application.start();
diff --git a/apps/platform-specific-template/main-page.android.xml b/apps/platform-specific-template/main-page.android.xml
new file mode 100644
index 000000000..a4508325e
--- /dev/null
+++ b/apps/platform-specific-template/main-page.android.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
diff --git a/apps/platform-specific-template/main-page.ios.xml b/apps/platform-specific-template/main-page.ios.xml
new file mode 100644
index 000000000..c3be93a20
--- /dev/null
+++ b/apps/platform-specific-template/main-page.ios.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
diff --git a/apps/platform-specific-template/main-page.ts b/apps/platform-specific-template/main-page.ts
new file mode 100644
index 000000000..5f282702b
--- /dev/null
+++ b/apps/platform-specific-template/main-page.ts
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/apps/platform-specific-template/package.json b/apps/platform-specific-template/package.json
new file mode 100644
index 000000000..cdfd44208
--- /dev/null
+++ b/apps/platform-specific-template/package.json
@@ -0,0 +1,2 @@
+{ "name" : "platform-specific-template",
+ "main" : "app.js" }
diff --git a/apps/template-master-detail/app.css b/apps/template-master-detail/app.css
index d7e46cecb..64530bbd8 100644
--- a/apps/template-master-detail/app.css
+++ b/apps/template-master-detail/app.css
@@ -13,15 +13,22 @@ ListView {
horizontal-align: center;
}
+.detail-title {
+ margin: 10;
+ font-size: 26;
+ color: #3c3c3c;
+ horizontal-align: center;
+}
+
.listItem {
margin: 10;
horizontal-align: center;
color: #808080;
- font-size: 24;
+ font-size: 20;
}
.info {
margin: 10;
- font-size: 24;
+ font-size: 20;
color: #808080;
}
diff --git a/apps/template-master-detail/details-page.ts b/apps/template-master-detail/details-page.ts
index f5cf8a2b4..a41dcea7b 100644
--- a/apps/template-master-detail/details-page.ts
+++ b/apps/template-master-detail/details-page.ts
@@ -1,8 +1,8 @@
import pages = require("ui/page");
import observable = require("data/observable");
-
+import vmModule = require("./main-view-model");
// Event handler for Page "navigatedTo" event attached in details-page.xml
export function pageNavigatedTo(args: observable.EventData) {
var page = args.object;
- page.bindingContext = page.navigationContext;
+ page.bindingContext = vmModule.mainViewModel.get("selectedItem");
}
diff --git a/apps/template-master-detail/details-page.xml b/apps/template-master-detail/details-page.xml
index 5d5be2a4c..b37a41623 100644
--- a/apps/template-master-detail/details-page.xml
+++ b/apps/template-master-detail/details-page.xml
@@ -1,6 +1,5 @@
-
-
-
-
-
+
+
\ No newline at end of file
diff --git a/apps/template-master-detail/details-view.xml b/apps/template-master-detail/details-view.xml
new file mode 100644
index 000000000..b6a4c11b9
--- /dev/null
+++ b/apps/template-master-detail/details-view.xml
@@ -0,0 +1,4 @@
+
+
+
+
diff --git a/apps/template-master-detail/main-page.minWH600.xml b/apps/template-master-detail/main-page.minWH600.xml
new file mode 100644
index 000000000..f1b30e0dc
--- /dev/null
+++ b/apps/template-master-detail/main-page.minWH600.xml
@@ -0,0 +1,17 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/apps/template-master-detail/main-page.ts b/apps/template-master-detail/main-page.ts
index b50df57f4..ca1064889 100644
--- a/apps/template-master-detail/main-page.ts
+++ b/apps/template-master-detail/main-page.ts
@@ -1,9 +1,12 @@
import observable = require("data/observable");
import pages = require("ui/page");
import frames = require("ui/frame");
+import platform = require("platform");
import listView = require("ui/list-view");
import vmModule = require("./main-view-model");
+var twoPaneLayout = Math.min(platform.screen.mainScreen.widthDIPs, platform.screen.mainScreen.heightDIPs) > 600;
+
// Event handler for Page "loaded" event attached in main-page.xml
export function pageLoaded(args: observable.EventData) {
var page = args.object;
@@ -12,8 +15,9 @@ export function pageLoaded(args: observable.EventData) {
export function listViewItemTap(args: listView.ItemEventData) {
// Navigate to the details page with context set to the current data item
- frames.topmost().navigate({
- moduleName: "app/details-page",
- context: args.view.bindingContext
- });
+ if (!twoPaneLayout) {
+ frames.topmost().navigate("app/details-page");
+ }
+
+ vmModule.mainViewModel.set("selectedItem", args.view.bindingContext);
}
\ No newline at end of file
diff --git a/apps/template-master-detail/main-page.xml b/apps/template-master-detail/main-page.xml
index 1e957cf08..b15e0d6ac 100644
--- a/apps/template-master-detail/main-page.xml
+++ b/apps/template-master-detail/main-page.xml
@@ -1,4 +1,5 @@
-
+
diff --git a/apps/tests/file-name-resolver-tests/file-name-resolver-tests.ts b/apps/tests/file-name-resolver-tests/file-name-resolver-tests.ts
new file mode 100644
index 000000000..2823daafc
--- /dev/null
+++ b/apps/tests/file-name-resolver-tests/file-name-resolver-tests.ts
@@ -0,0 +1,245 @@
+import TKUnit = require("../TKUnit");
+import fs = require("file-system");
+import enums = require("ui/enums");
+import resolver = require("file-system/file-name-resolver");
+
+var androidPhonePortraitContext: resolver.PlatformContext = {
+ width: 360,
+ height: 640,
+ deviceType: enums.DeviceType.Phone,
+ os: "android"
+}
+
+var androidPhoneLandsacpeContext: resolver.PlatformContext = {
+ width: 640,
+ height: 360,
+ deviceType: enums.DeviceType.Phone,
+ os: "android"
+}
+
+var androidTabletPortraitContext: resolver.PlatformContext = {
+ width: 600,
+ height: 960,
+ deviceType: enums.DeviceType.Tablet,
+ os: "android"
+}
+
+var iPhonePortraitContext: resolver.PlatformContext = {
+ width: 320,
+ height: 480,
+ deviceType: enums.DeviceType.Phone,
+ os: "ios"
+}
+
+var iPhoneLandscapeContext: resolver.PlatformContext = {
+ width: 480,
+ height: 320,
+ deviceType: enums.DeviceType.Phone,
+ os: "ios"
+}
+
+export function test_findFileMatch_fileName() {
+ var candidates: Array = [
+ "test.xml",
+ "test2.xml",
+ "other.xml"
+ ];
+
+ testTemplate(candidates, androidPhonePortraitContext, "test.xml")
+};
+
+export function test_findFileMatch_os_android() {
+ var candidates: Array = [
+ "test.xml",
+ "test.ios.xml",
+ "test.android.xml",
+ "other.xml"
+ ];
+
+ testTemplate(candidates, androidPhonePortraitContext, "test.android.xml")
+};
+
+export function test_findFileMatch_os_ios() {
+ var candidates: Array = [
+ "test.xml",
+ "test.ios.xml",
+ "test.android.xml",
+ "other.xml"
+ ];
+
+ testTemplate(candidates, iPhonePortraitContext, "test.ios.xml")
+};
+
+export function test_findFileMatch_os_fallback() {
+ var candidates: Array = [
+ "test.xml",
+ "test.ios.xml",
+ "other.xml"
+ ];
+
+ testTemplate(candidates, androidPhonePortraitContext, "test.xml")
+};
+
+export function test_findFileMatch_minWH_fallback() {
+ var candidates: Array = [
+ "test.xml",
+ "test.minWH600.xml",
+ "other.xml"
+ ];
+
+ testTemplate(candidates, androidPhonePortraitContext, "test.xml")
+}
+
+export function test_findFileMatch_minWH_best_value() {
+ var candidates: Array = [
+ "test.xml",
+ "test.minWH400.xml",
+ "test.minWH500.xml",
+ "test.minWH600.xml",
+ "test.minWH700.xml",
+ "other.xml"
+ ];
+
+ testTemplate(candidates, androidTabletPortraitContext, "test.minWH600.xml")
+}
+
+export function test_findFileMatch_minW_fallback() {
+ var candidates: Array = [
+ "test.xml",
+ "test.minW600.xml",
+ "other.xml"
+ ];
+
+ testTemplate(candidates, androidPhonePortraitContext, "test.xml")
+}
+
+export function test_findFileMatch_minW_best_value() {
+ var candidates: Array = [
+ "test.xml",
+ "test.minW400.xml",
+ "test.minW500.xml",
+ "test.minW600.xml",
+ "test.minW700.xml",
+ "other.xml"
+ ];
+
+ testTemplate(candidates, androidTabletPortraitContext, "test.minW600.xml")
+}
+
+export function test_findFileMatch_minH_fallback() {
+ var candidates: Array = [
+ "test.xml",
+ "test.minH600.xml",
+ "other.xml"
+ ];
+
+ testTemplate(candidates, androidPhoneLandsacpeContext, "test.xml")
+}
+
+export function test_findFileMatch_minH_best_value() {
+ var candidates: Array = [
+ "test.xml",
+ "test.minH400.xml",
+ "test.minH500.xml",
+ "test.minH600.xml",
+ "test.minH700.xml",
+ "other.xml"
+ ];
+
+ testTemplate(candidates, androidPhonePortraitContext, "test.minH600.xml")
+}
+
+export function test_findFileMatch_orienation_fallback() {
+ var candidates: Array = [
+ "test.xml",
+ "test.land.xml",
+ "other.xml"
+ ];
+
+ testTemplate(candidates, androidTabletPortraitContext, "test.xml")
+}
+
+export function test_findFileMatch_orienation_portrait() {
+ var candidates: Array = [
+ "test.xml",
+ "test.land.xml",
+ "test.port.xml",
+ "other.xml"
+ ];
+
+ testTemplate(candidates, androidTabletPortraitContext, "test.port.xml")
+}
+
+export function test_findFileMatch_orienation_landscape() {
+ var candidates: Array = [
+ "test.xml",
+ "test.land.xml",
+ "test.port.xml",
+ "other.xml"
+ ];
+
+ testTemplate(candidates, androidPhoneLandsacpeContext, "test.land.xml")
+}
+
+export function test_findFileMatch_choose_most_specific_file() {
+ var candidates: Array = [
+ "test.xml",
+ "test.android.xml",
+ "test.android.port.xml",
+ "other.xml"
+ ];
+
+ testTemplate(candidates, androidPhonePortraitContext, "test.android.port.xml")
+}
+
+export function test_findFileMatch_with_multiple_matches_loads_by_priority() {
+ var candidates: Array = [
+ "test.xml",
+ "test.android.xml",
+ "test.tablet.xml",
+ "test.land.xml",
+ "test.minH600.xml",
+ "test.minW600.xml",
+ "test.minWH600.xml",
+ "other.xml"
+ ];
+
+ testTemplate(candidates, androidTabletPortraitContext, "test.minWH600.xml")
+}
+
+function testTemplate(candidates: Array, context: resolver.PlatformContext, expected: string) {
+ var result = resolver.findFileMatch("test", ".xml", candidates, context);
+ TKUnit.assertEqual(result, expected, "File path");
+}
+
+var testFilePath = fs.path.join(fs.knownFolders.currentApp().path, "app/tests/file-name-resolver-tests/files/test");
+
+export function test_file_resolver_with_andorid_phone_portratit() {
+ var fileResolver = new resolver.FileNameResolver(androidPhonePortraitContext);
+ var result = fileResolver.resolveFileName(testFilePath, "xml");
+ TKUnit.assertEqual(result, testFilePath + ".android.xml", "File path");
+}
+
+export function test_file_resolver_with_andorid_phone_landscape() {
+ var fileResolver = new resolver.FileNameResolver(androidPhoneLandsacpeContext);
+ var result = fileResolver.resolveFileName(testFilePath, "xml");
+ TKUnit.assertEqual(result, testFilePath + ".android.xml", "File path");
+}
+
+export function test_file_resolver_with_andorid_tablet_portrait() {
+ var fileResolver = new resolver.FileNameResolver(androidTabletPortraitContext);
+ var result = fileResolver.resolveFileName(testFilePath, "xml");
+ TKUnit.assertEqual(result, testFilePath + ".android.minWH600.xml", "File path");
+}
+
+export function test_file_resolver_with_ios_phone_landscape() {
+ var fileResolver = new resolver.FileNameResolver(iPhoneLandscapeContext);
+ var result = fileResolver.resolveFileName(testFilePath, "xml");
+ TKUnit.assertEqual(result, testFilePath + ".ios.land.xml", "File path");
+}
+
+export function test_file_resolver_with_ios_phone_portrait() {
+ var fileResolver = new resolver.FileNameResolver(iPhonePortraitContext);
+ var result = fileResolver.resolveFileName(testFilePath, "xml");
+ TKUnit.assertEqual(result, testFilePath + ".xml", "File path");
+}
\ No newline at end of file
diff --git a/apps/tests/file-name-resolver-tests/files/other.xml b/apps/tests/file-name-resolver-tests/files/other.xml
new file mode 100644
index 000000000..162275934
--- /dev/null
+++ b/apps/tests/file-name-resolver-tests/files/other.xml
@@ -0,0 +1 @@
+other.xml
\ No newline at end of file
diff --git a/apps/tests/file-name-resolver-tests/files/test.android.minWH600.xml b/apps/tests/file-name-resolver-tests/files/test.android.minWH600.xml
new file mode 100644
index 000000000..e0c145e88
--- /dev/null
+++ b/apps/tests/file-name-resolver-tests/files/test.android.minWH600.xml
@@ -0,0 +1 @@
+test.monWH450.xml
\ No newline at end of file
diff --git a/apps/tests/file-name-resolver-tests/files/test.android.xml b/apps/tests/file-name-resolver-tests/files/test.android.xml
new file mode 100644
index 000000000..e6232e926
--- /dev/null
+++ b/apps/tests/file-name-resolver-tests/files/test.android.xml
@@ -0,0 +1 @@
+test.android.xml
\ No newline at end of file
diff --git a/apps/tests/file-name-resolver-tests/files/test.ios.land.xml b/apps/tests/file-name-resolver-tests/files/test.ios.land.xml
new file mode 100644
index 000000000..07270d972
--- /dev/null
+++ b/apps/tests/file-name-resolver-tests/files/test.ios.land.xml
@@ -0,0 +1 @@
+test.minWH300.xml
\ No newline at end of file
diff --git a/apps/tests/file-name-resolver-tests/files/test.xml b/apps/tests/file-name-resolver-tests/files/test.xml
new file mode 100644
index 000000000..3d02faa9c
--- /dev/null
+++ b/apps/tests/file-name-resolver-tests/files/test.xml
@@ -0,0 +1 @@
+test.xml
\ No newline at end of file
diff --git a/apps/tests/pages/file-load-test.ts b/apps/tests/pages/file-load-test.ts
new file mode 100644
index 000000000..dd2bfc2b2
--- /dev/null
+++ b/apps/tests/pages/file-load-test.ts
@@ -0,0 +1,29 @@
+import label = require("ui/label");
+import pages = require("ui/page");
+import fs = require("file-system");
+import fileResolverModule = require("file-system/file-name-resolver");
+
+export function createPage() {
+ var page = new pages.Page();
+ var lbl = new label.Label();
+
+ var moduleName = "app/tests/pages/files/test";
+
+ var resolver = new fileResolverModule.FileNameResolver({
+ width: 400,
+ height: 600,
+ os: "android",
+ deviceType: "phone"
+ });
+
+ // Current app full path.
+ var currentAppPath = fs.knownFolders.currentApp().path;
+ var moduleNamePath = fs.path.join(currentAppPath, moduleName);
+
+ var fileName = resolver.resolveFileName(moduleNamePath, "xml");
+ lbl.text = fileName;
+ lbl.textWrap = true;;
+
+ page.content = lbl;
+ return page;
+}
diff --git a/apps/tests/pages/page5.ts b/apps/tests/pages/page5.ts
index 6fee27900..2deb123c2 100644
--- a/apps/tests/pages/page5.ts
+++ b/apps/tests/pages/page5.ts
@@ -1,6 +1,6 @@
-import btns = require("ui/button");
-import gridModule = require("ui/layouts/grid-layout");
+import gridModule = require("ui/layouts/grid-layout");
import pages = require("ui/page");
+import buttons = require("ui/button");
import app = require("application");
import platform = require("platform");
@@ -12,7 +12,6 @@ function printDeviceInfoAndroid() {
console.log("android.os.Build.VERSION.RELEASE = " + android.os.Build.VERSION.RELEASE); //android.os.Build.VERSION.RELEASE = 4.4.4
android.os.Build.MANUFACTURER
var metrics: android.util.DisplayMetrics = app.android.context.getResources().getDisplayMetrics();
-
console.log("metrics.density = " + metrics.density); //metrics.density = 3
console.log("metrics.scaledDensity = " + metrics.scaledDensity); //metrics.scaledDensity = 3
console.log("metrics.densityDpi = " + metrics.densityDpi); //metrics.densityDpi = 480
@@ -20,6 +19,12 @@ function printDeviceInfoAndroid() {
console.log("metrics.yxdpi = " + metrics.ydpi); //metrics.yxdpi = 443.3450012207031
console.log("metrics.widthPixels = " + metrics.widthPixels); //metrics.widthPixels = 1080
console.log("metrics.heightPixels = " + metrics.heightPixels); //metrics.heightPixels = 1776
+
+ var config = app.android.context.getResources().getConfiguration();
+ console.log("config.screenWidthDp = " + config.screenWidthDp);
+ console.log("config.screenHeightDp = " + config.screenHeightDp);
+ console.log("config.smallestScreenWidthDp = " + config.smallestScreenWidthDp);
+ console.log("config.orientation = " + (config.orientation === android.content.res.Configuration.ORIENTATION_PORTRAIT ? "portrait" : "ladscape"));
}
function printDeviceInfoIOS() {
@@ -45,45 +50,35 @@ function printTNSInfo() {
console.log("platform.device.sdkVersion = " + platform.device.sdkVersion);
console.log("platform.device.deviceType = " + platform.device.deviceType);
+ console.log("platform.screen.mainScreen.widthDIPs = " + platform.screen.mainScreen.widthDIPs);
+ console.log("platform.screen.mainScreen.heightDIPs = " + platform.screen.mainScreen.heightDIPs);
+ console.log("platform.screen.mainScreen.scale = " + platform.screen.mainScreen.scale);
console.log("platform.screen.mainScreen.widthPixels = " + platform.screen.mainScreen.widthPixels);
console.log("platform.screen.mainScreen.heightPixels = " + platform.screen.mainScreen.heightPixels);
- console.log("platform.screen.mainScreen.scale = " + platform.screen.mainScreen.scale);
}
-if (app.android) {
- printDeviceInfoAndroid();
+function print() {
+ if (app.android) {
+ printDeviceInfoAndroid();
+ }
+ else {
+ printDeviceInfoIOS();
+ }
+ printTNSInfo();
}
-else {
- printDeviceInfoIOS();
-}
-printTNSInfo();
+print();
export function createPage() {
var page = new pages.Page();
var grid = new gridModule.GridLayout();
- var rows = 100;
- var cols = 3;
- var row;
- var col;
+ var btn = new buttons.Button();
+ btn.text = "print";
+ btn.on("tap", (d) => {
+ print();
+ });
- for (row = 0; row < rows; row++) {
- grid.addRow(new gridModule.ItemSpec(1, gridModule.GridUnitType.auto));
- }
-
- for (col = 0; col < cols; col++) {
- grid.addColumn(new gridModule.ItemSpec(1, gridModule.GridUnitType.auto));
- }
-
- for (col = 0; col < cols; col++) {
- for (row = 0; row < rows; row++) {
- var btn = new btns.Button();
- btn.text = "Col: " + col + ", Row: " + row;
- gridModule.GridLayout.setColumn(btn, col);
- gridModule.GridLayout.setRow(btn, row);
- grid.addChild(btn);
- }
- }
+ grid.addChild(btn);
page.content = grid;
return page;
diff --git a/apps/tests/testRunner.ts b/apps/tests/testRunner.ts
index 9ac277453..983fa72f9 100644
--- a/apps/tests/testRunner.ts
+++ b/apps/tests/testRunner.ts
@@ -46,6 +46,7 @@ allTests["TEXT-FIELD"] = require("./ui/text-field/text-field-tests");
allTests["TEXT-VIEW"] = require("./ui/text-view/text-view-tests");
allTests["FORMATTEDSTRING"] = require("./text/formatted-string-tests");
allTests["FILE-SYSTEM-ACCESS"] = require("./file-system-access-tests/file-system-access-tests");
+allTests["FILE-NAME-RESOLVER"] = require("./file-name-resolver-tests/file-name-resolver-tests");
allTests["XML-DECLARATION"] = require("./xml-declaration/xml-declaration-tests");
allTests["LIST-PICKER"] = require("./ui/list-picker/list-picker-tests");
allTests["DATE-PICKER"] = require("./ui/date-picker/date-picker-tests");
diff --git a/file-system/file-name-resolver.d.ts b/file-system/file-name-resolver.d.ts
new file mode 100644
index 000000000..257263414
--- /dev/null
+++ b/file-system/file-name-resolver.d.ts
@@ -0,0 +1,20 @@
+/**
+ * Provides FileNameResolver class used for loading files based on device capabilities.
+ */
+declare module "file-system/file-name-resolver" {
+ export interface PlatformContext {
+ width: number;
+ height: number;
+ os: string;
+ deviceType: string;
+ }
+
+ export class FileNameResolver {
+ constructor(context: PlatformContext);
+ resolveFileName(path: string, ext: string): string;
+ }
+
+ //@private
+ export function findFileMatch(path: string, ext: string, candidates: Array, context: PlatformContext): string
+ //@endprivate
+}
\ No newline at end of file
diff --git a/file-system/file-name-resolver.ts b/file-system/file-name-resolver.ts
new file mode 100644
index 000000000..481206a11
--- /dev/null
+++ b/file-system/file-name-resolver.ts
@@ -0,0 +1,215 @@
+import definition = require("file-system/file-name-resolver");
+import fs = require("file-system");
+import types = require("utils/types");
+import trace = require("trace");
+
+var MIN_WH: string = "minWH";
+var MIN_W: string = "minW";
+var MIN_H: string = "minH";
+var PRIORITY_STEP = 10000;
+
+interface QualifierSpec {
+ isMatch(value: string): boolean;
+ getMatchValue(value: string, context: definition.PlatformContext): number;
+}
+
+var minWidthHeightQualifier: QualifierSpec = {
+ isMatch: function (value: string): boolean {
+ return value.indexOf(MIN_WH) === 0;
+
+ },
+ getMatchValue(value: string, context: definition.PlatformContext): number {
+ var numVal = parseInt(value.substr(MIN_WH.length));
+ if (isNaN(numVal)) {
+ return -1;
+ }
+
+ var actualLength = Math.min(context.width, context.height);
+ if (actualLength < numVal) {
+ return -1;
+ }
+
+ return PRIORITY_STEP - (actualLength - numVal);
+ }
+}
+
+var minWidthQualifier: QualifierSpec = {
+ isMatch: function (value: string): boolean {
+ return value.indexOf(MIN_W) === 0 && value.indexOf(MIN_WH) < 0;
+
+ },
+ getMatchValue(value: string, context: definition.PlatformContext): number {
+ var numVal = parseInt(value.substr(MIN_W.length));
+ if (isNaN(numVal)) {
+ return -1;
+ }
+
+ var actualWidth = context.width;
+ if (actualWidth < numVal) {
+ return -1;
+ }
+
+ return PRIORITY_STEP - (actualWidth - numVal);
+ }
+}
+
+var minHeightQualifier: QualifierSpec = {
+ isMatch: function (value: string): boolean {
+ return value.indexOf(MIN_H) === 0 && value.indexOf(MIN_WH) < 0;
+
+ },
+ getMatchValue(value: string, context: definition.PlatformContext): number {
+ var numVal = parseInt(value.substr(MIN_H.length));
+ if (isNaN(numVal)) {
+ return -1;
+ }
+
+ var actualHeight = context.height;
+ if (actualHeight < numVal) {
+ return -1;
+ }
+
+ return PRIORITY_STEP - (actualHeight - numVal)
+ }
+}
+
+var paltformQualifier: QualifierSpec = {
+ isMatch: function (value: string): boolean {
+ return value === "android" ||
+ value === "ios";
+
+ },
+ getMatchValue(value: string, context: definition.PlatformContext): number{
+ return value === context.os.toLowerCase() ? 1 : -1;
+ }
+}
+
+var orientationQualifier: QualifierSpec = {
+ isMatch: function (value: string): boolean {
+ return value === "land" ||
+ value === "port";
+
+ },
+ getMatchValue(value: string, context: definition.PlatformContext): number{
+ var isLandscape: number = (context.width > context.height) ? 1 : -1;
+ return (value === "land") ? isLandscape : -isLandscape;
+ }
+}
+
+// List of supported qualifiers ordered by priority
+var supportedQualifiers: Array = [
+ minWidthHeightQualifier,
+ minWidthQualifier,
+ minHeightQualifier,
+ orientationQualifier,
+ paltformQualifier
+];
+
+export class FileNameResolver implements definition.FileNameResolver {
+ private _context: definition.PlatformContext;
+ private _cache = {};
+
+ constructor(context: definition.PlatformContext) {
+ this._context = context;
+ }
+
+ public resolveFileName(path: string, ext: string): string {
+ var key = path + ext;
+ var result: string = this._cache[key];
+ if (types.isUndefined(result)) {
+ result = this.resolveFileNameImpl(path, ext);
+ this._cache[key] = result;
+ }
+
+ return result;
+ }
+
+ private resolveFileNameImpl(path: string, ext: string): string {
+ var result: string = null;
+ path = fs.path.normalize(path);
+ ext = "." + ext;
+
+ var candidates = this.getFileCandidatesFromFolder(path, ext);
+ result = findFileMatch(path, ext, candidates, this._context);
+
+ trace.write("Resolved file name for \"" + path + ext + "\" result: " + (result ? result : "no match found"), trace.categories.Navigation);
+ return result;
+ }
+
+ private getFileCandidatesFromFolder(path: string, ext: string): Array {
+ var candidates = new Array();
+ var folderPath = path.substring(0, path.lastIndexOf(fs.path.separator) + 1);
+
+ if (fs.Folder.exists(folderPath)) {
+ var folder = fs.Folder.fromPath(folderPath);
+ folder.eachEntity((e) => {
+ if (e instanceof fs.File) {
+ var file = e;
+ if (file.path.indexOf(path) === 0 && file.extension === ext) {
+ candidates.push(file.path);
+ }
+ }
+
+ return true;
+ });
+ }
+ else {
+ trace.write("Could not find folder " + folderPath + " when loading " + path + ext, trace.categories.Navigation);
+ }
+
+ return candidates;
+ }
+}
+
+export function findFileMatch(path: string, ext: string, candidates: Array, context: definition.PlatformContext): string {
+ var bestValue = -1
+ var result: string = null;
+
+ trace.write("Candidates for " + path + ext + ": " + candidates.join(", "), trace.categories.Navigation);
+ for (var i = 0; i < candidates.length; i++) {
+ var filePath = candidates[i];
+ var qualifiersStr: string = filePath.substr(path.length, filePath.length - path.length - ext.length);
+
+ var qualifiers = qualifiersStr.split(".");
+
+ var value = checkQualifiers(qualifiers, context);
+
+ if (value >= 0 && value > bestValue) {
+ bestValue = value;
+ result = candidates[i];
+ }
+ }
+
+ return result;
+}
+
+function checkQualifiers(qualifiers: Array, context: definition.PlatformContext): number {
+ var result = 0;
+ for (var i = 0; i < qualifiers.length; i++) {
+ if (qualifiers[i]) {
+ var value = checkQualifier(qualifiers[i], context);
+ if (value < 0) {
+ // Non of the supported qualifiers matched this or the match was not satisified
+ return -1;
+ }
+
+ result += value;
+ }
+ }
+
+ return result;
+}
+
+function checkQualifier(value: string, context: definition.PlatformContext) {
+ for (var i = 0; i < supportedQualifiers.length; i++) {
+ if (supportedQualifiers[i].isMatch(value)) {
+ var result = supportedQualifiers[i].getMatchValue(value, context);
+ if (result > 0) {
+ result += (supportedQualifiers.length - i) * PRIORITY_STEP;
+ }
+ return result;
+ }
+ }
+
+ return -1;
+}
diff --git a/platform/platform.android.ts b/platform/platform.android.ts
index f4e62227a..686eb2100 100644
--- a/platform/platform.android.ts
+++ b/platform/platform.android.ts
@@ -73,7 +73,9 @@ export class screen implements definition.screen {
mainScreenInfo = {
widthPixels: metrics.widthPixels,
heightPixels: metrics.heightPixels,
- scale: metrics.density
+ scale: metrics.density,
+ widthDIPs: metrics.widthPixels / metrics.density,
+ heightDIPs: metrics.heightPixels / metrics.density
}
}
return mainScreenInfo;
diff --git a/platform/platform.d.ts b/platform/platform.d.ts
index 2bcb057ab..2901f0de2 100644
--- a/platform/platform.d.ts
+++ b/platform/platform.d.ts
@@ -61,6 +61,16 @@ declare module "platform" {
*/
heightPixels: number;
+ /**
+ * Gets the absolute width of the screen in density independent pixels.
+ */
+ widthDIPs: number;
+
+ /**
+ * Gets the absolute height of the screen in density independent pixels.
+ */
+ heightDIPs: number;
+
/**
* The logical density of the display. This is a scaling factor for the Density Independent Pixel unit.
*/
diff --git a/platform/platform.ios.ts b/platform/platform.ios.ts
index 6d03d14fb..6a2fffdc5 100644
--- a/platform/platform.ios.ts
+++ b/platform/platform.ios.ts
@@ -71,7 +71,9 @@ export class screen implements definition.screen {
mainScreenInfo = {
widthPixels: size.width * scale,
heightPixels: size.height * scale,
- scale: scale
+ scale: scale,
+ widthDIPs: size.width,
+ heightDIPs: size.height
}
}
}
diff --git a/ui/frame/frame-common.ts b/ui/frame/frame-common.ts
index 84749370d..922577ae1 100644
--- a/ui/frame/frame-common.ts
+++ b/ui/frame/frame-common.ts
@@ -6,6 +6,8 @@ import trace = require("trace");
import builder = require("ui/builder");
import fs = require("file-system");
import utils = require("utils/utils");
+import platform = require("platform");
+import fileResolverModule = require("file-system/file-name-resolver");
var frameStack: Array = [];
@@ -39,7 +41,7 @@ function resolvePageFromEntry(entry: definition.NavigationEntry): pages.Page {
}
}
else if (entry.moduleName) {
- // Current app full path.
+ // Current app full path.
var currentAppPath = fs.knownFolders.currentApp().path;
//Full path of the module = current app full path + module name.
var moduleNamePath = fs.path.join(currentAppPath, entry.moduleName);
@@ -67,14 +69,26 @@ function resolvePageFromEntry(entry: definition.NavigationEntry): pages.Page {
return page;
}
+var fileNameResolver: fileResolverModule.FileNameResolver;
+function resolveFilePath(path, ext) {
+ if (!fileNameResolver) {
+ fileNameResolver = new fileResolverModule.FileNameResolver({
+ width: platform.screen.mainScreen.widthDIPs,
+ height: platform.screen.mainScreen.heightDIPs,
+ os: platform.device.os,
+ deviceType: platform.device.deviceType
+ });
+ }
+ return fileNameResolver.resolveFileName(path, ext);
+}
+
function pageFromBuilder(moduleNamePath: string, moduleName: string, moduleExports: any): pages.Page {
var page: pages.Page;
var element: view.View;
// Possible XML file path.
- var fileName = moduleNamePath + ".xml";
-
- if (fs.File.exists(fileName)) {
+ var fileName = resolveFilePath(moduleNamePath, "xml");
+ if (fileName) {
trace.write("Loading XML file: " + fileName, trace.categories.Navigation);
// Or check if the file exists in the app modules and load the page from XML.
@@ -83,8 +97,10 @@ function pageFromBuilder(moduleNamePath: string, moduleName: string, moduleExpor
page = element;
// Possible CSS file path.
- var cssFileName = moduleName + ".css";
- page.addCssFile(cssFileName);
+ var cssFileName = resolveFilePath(moduleName, "css");
+ if (cssFileName) {
+ page.addCssFile(cssFileName);
+ }
}
}
diff --git a/ui/page/page-common.ts b/ui/page/page-common.ts
index c3245ffaa..cb2cca780 100644
--- a/ui/page/page-common.ts
+++ b/ui/page/page-common.ts
@@ -61,10 +61,13 @@ export class Page extends contentView.ContentView implements dts.Page {
}
public addCssFile(cssFileName: string) {
+ if (cssFileName.indexOf(fs.knownFolders.currentApp().path) !== 0) {
+ cssFileName = fs.path.join(fs.knownFolders.currentApp().path, cssFileName);
+ }
+
var cssString;
- var realCssFileName = fs.path.join(fs.knownFolders.currentApp().path, cssFileName);
- if (fs.File.exists(realCssFileName)) {
- new fileSystemAccess.FileSystemAccess().readText(realCssFileName, r => { cssString = r; });
+ if (fs.File.exists(cssFileName)) {
+ new fileSystemAccess.FileSystemAccess().readText(cssFileName, r => { cssString = r; });
this._addCssInternal(cssString, cssFileName);
}
}
@@ -104,18 +107,18 @@ export class Page extends contentView.ContentView implements dts.Page {
return;
}
- this._styleScope.ensureSelectors();
+ this._styleScope.ensureSelectors();
- var scope = this._styleScope;
- var checkSelectors = (view: view.View): boolean => {
- scope.applySelectors(view);
- return true;
- }
+ var scope = this._styleScope;
+ var checkSelectors = (view: view.View): boolean => {
+ scope.applySelectors(view);
+ return true;
+ }
- checkSelectors(this);
- view.eachDescendant(this, checkSelectors);
+ checkSelectors(this);
+ view.eachDescendant(this, checkSelectors);
- this._cssApplied = true;
+ this._cssApplied = true;
}
private _resetCssValues() {