file-name-resolver module

This commit is contained in:
vakrilov
2015-03-17 17:20:15 +02:00
parent 9e15302db3
commit 0b2c4cc523
14 changed files with 299 additions and 13 deletions

View File

@ -109,6 +109,7 @@
<TypeScriptCompile Include="apps\tests\gestures-tests.ts" />
<TypeScriptCompile Include="apps\tests\layouts\dock-layout-tests.ts" />
<TypeScriptCompile Include="apps\tests\pages\app.ts" />
<TypeScriptCompile Include="apps\tests\pages\file-load-test.ts" />
<TypeScriptCompile Include="apps\tests\pages\page12.ts" />
<TypeScriptCompile Include="apps\tests\layouts\absolute-layout-tests.ts" />
<TypeScriptCompile Include="apps\tests\layouts\layout-helper.ts" />
@ -167,6 +168,10 @@
<TypeScriptCompile Include="apps\ui-tests-app\pages\i61.ts" />
<TypeScriptCompile Include="apps\ui-tests-app\pages\i73.ts" />
<TypeScriptCompile Include="apps\ui-tests-app\pages\gestures.ts" />
<TypeScriptCompile Include="file-system\file-name-resolver.d.ts" />
<TypeScriptCompile Include="file-system\file-name-resolver.ts">
<DependentUpon>file-name-resolver.d.ts</DependentUpon>
</TypeScriptCompile>
<TypeScriptCompile Include="image-source\image-source-common.ts">
<DependentUpon>image-source.d.ts</DependentUpon>
</TypeScriptCompile>
@ -540,6 +545,12 @@
<Content Include="apps\template-settings\app.css" />
<Content Include="apps\tests\app\binding_tests.xml" />
<Content Include="apps\tests\ui\label\label-tests-wrong.css" />
<Content Include="apps\tests\pages\files\other.xml" />
<Content Include="apps\tests\pages\files\test.android.phone.xml" />
<Content Include="apps\tests\pages\files\test.android.xml" />
<Content Include="apps\tests\pages\files\test.minWH300.xml" />
<Content Include="apps\tests\pages\files\test.minWH450.xml" />
<Content Include="apps\tests\pages\files\test.xml" />
<Content Include="apps\ui-tests-app\pages\i86.xml" />
<Content Include="apps\template-blank\app.css" />
<Content Include="apps\template-hello-world\app.css" />
@ -1500,7 +1511,7 @@
<SaveServerSettingsInUserFile>False</SaveServerSettingsInUserFile>
</WebProjectProperties>
</FlavorProperties>
<UserProperties ui_2scroll-view_2package_1json__JSONSchema="http://json.schemastore.org/package" apps_2editable-text-demo_2package_1json__JSONSchema="http://json.schemastore.org/package" apps_2absolute-layout-demo_2package_1json__JSONSchema="http://json.schemastore.org/package" apps_2gallery-app_2package_1json__JSONSchema="http://json.schemastore.org/package" ui_2content-view_2package_1json__JSONSchema="http://json.schemastore.org/package" ui_2web-view_2package_1json__JSONSchema="http://json.schemastore.org/package" ui_2layouts_2linear-layout_2package_1json__JSONSchema="http://json.schemastore.org/package" ui_2layouts_2absolute-layout_2package_1json__JSONSchema="http://json.schemastore.org/package" ui_2layouts_2dock-layout_2package_1json__JSONSchema="" ui_2layouts_2grid-layout_2package_1json__JSONSchema="" ui_2layouts_2wrap-layout_2package_1json__JSONSchema="http://json.schemastore.org/package" />
<UserProperties ui_2layouts_2wrap-layout_2package_1json__JSONSchema="http://json.schemastore.org/package" ui_2layouts_2grid-layout_2package_1json__JSONSchema="" ui_2layouts_2dock-layout_2package_1json__JSONSchema="" ui_2layouts_2absolute-layout_2package_1json__JSONSchema="http://json.schemastore.org/package" ui_2layouts_2linear-layout_2package_1json__JSONSchema="http://json.schemastore.org/package" ui_2web-view_2package_1json__JSONSchema="http://json.schemastore.org/package" ui_2content-view_2package_1json__JSONSchema="http://json.schemastore.org/package" apps_2gallery-app_2package_1json__JSONSchema="http://json.schemastore.org/package" apps_2absolute-layout-demo_2package_1json__JSONSchema="http://json.schemastore.org/package" apps_2editable-text-demo_2package_1json__JSONSchema="http://json.schemastore.org/package" ui_2scroll-view_2package_1json__JSONSchema="http://json.schemastore.org/package" />
</VisualStudio>
</ProjectExtensions>
</Project>

View File

@ -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;
}

View File

@ -0,0 +1 @@
other.xml

View File

@ -0,0 +1 @@
test.android.phone.xml

View File

@ -0,0 +1 @@
test.android.xml

View File

@ -0,0 +1 @@
test.minWH300.xml

View File

@ -0,0 +1 @@
test.monWH450.xml

View File

@ -0,0 +1 @@
test.xml

16
file-system/file-name-resolver.d.ts vendored Normal file
View File

@ -0,0 +1,16 @@
/**
* Provides FileNameResolver class used for loading files based on device capabilities.
*/
declare module "file-system/file-name-resolver" {
interface PlatformContext {
width: number;
height: number;
os: string;
deviceType: string;
}
class FileNameResolver {
constructor(context: PlatformContext);
resolveFileName(path: string, ext: string): string;
}
}

View File

@ -0,0 +1,206 @@
import definition = require("file-system/file-name-resolver");
import fs = require("file-system");
import types = require("utils/types");
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 fromQualifier: QualifierSpec = {
isMatch: function (value: string): boolean {
return value === "tablet" || value === "phone";
},
getMatchValue(value: string, context: definition.PlatformContext): number{
if (value !== context.deviceType.toLocaleLowerCase()) {
return -1;
}
return 1;
}
}
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;
}
}
// List of supported qualifiers ordered by priority
var supportedQualifiers: Array<QualifierSpec> = [
minWidthHeightQualifier,
minWidthQualifier,
minHeightQualifier,
paltformQualifier,
fromQualifier];
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 {
path = fs.path.normalize(path);
ext = "." + ext;
var folderPath = path.substring(0, path.lastIndexOf(fs.path.separator) + 1);
console.log("search folderPath: " + folderPath);
if (fs.Folder.exists(folderPath)) {
var folder = fs.Folder.fromPath(folderPath);
var candidates = new Array<fs.File>();
folder.eachEntity((e) => {
if (e instanceof fs.File) {
var file = <fs.File>e;
console.log("File path: " + e.path);
if (file.path.indexOf(path) === 0 && file.extension === ext) {
candidates.push(file);
}
}
return true;
});
var bestValue = Number.MIN_VALUE;
var bestCandidate: fs.File = null;
console.log("Candidates:");
for (var i = 0; i < candidates.length; i++) {
console.log("---------- candiate[" + i + "]: " + candidates[i].name);
var filePath = candidates[i].path;
var qualifiersStr: string = filePath.substr(path.length, filePath.length - path.length - ext.length);
var qualifiers = qualifiersStr.split(".");
var value = this.checkQualifiers(qualifiers);
console.log("qualifiers: " + qualifiersStr + " result: " + value);
if (value >= 0 && value > bestValue) {
bestValue = value;
bestCandidate = candidates[i];
}
}
}
return bestCandidate ? bestCandidate.path : null;
}
private checkQualifiers(qualifiers: Array<string>): number {
var result = 0;
for (var i = 0; i < qualifiers.length; i++) {
if (qualifiers[i]) {
var value = this.checkQualifier(qualifiers[i]);
console.log("checking qualifier: " + qualifiers[i] + " result: " + value);
if (value < 0) {
// Non of the supported qualifiers matched this or the match was not satisified
return -1;
}
result += value;
}
}
return result;
}
private checkQualifier(value: string) {
for (var i = 0; i < supportedQualifiers.length; i++) {
if (supportedQualifiers[i].isMatch(value)) {
var result = supportedQualifiers[i].getMatchValue(value, this._context);
if (result > 0) {
result += (supportedQualifiers.length - i) * PRIORITY_STEP;
}
return result;
}
}
return -1;
}
}

View File

@ -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;

View File

@ -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.
*/

View File

@ -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
}
}
}

View File

@ -7,6 +7,7 @@ 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<Frame> = [];
@ -68,14 +69,17 @@ function resolvePageFromEntry(entry: definition.NavigationEntry): pages.Page {
return page;
}
function resolvePlatformPath(path, ext) {
var platformName = platform.device.os.toLowerCase();
var platformPath = [path, platformName, ext].join(".");
if (fs.File.exists(platformPath)) {
return platformPath;
}
return [path, ext].join(".");
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 {
@ -83,7 +87,7 @@ function pageFromBuilder(moduleNamePath: string, moduleName: string, moduleExpor
var element: view.View;
// Possible XML file path.
var fileName = resolvePlatformPath(moduleNamePath, "xml");
var fileName = resolveFilePath(moduleNamePath, "xml");
if (fs.File.exists(fileName)) {
trace.write("Loading XML file: " + fileName, trace.categories.Navigation);
@ -94,7 +98,7 @@ function pageFromBuilder(moduleNamePath: string, moduleName: string, moduleExpor
page = <pages.Page>element;
// Possible CSS file path.
var cssFileName = resolvePlatformPath(moduleName, "css");
var cssFileName = resolveFilePath(moduleName, "css");
page.addCssFile(cssFileName);
}
}