import { PlatformContext, FileNameResolver as FileNameResolverDefinition } from "../file-name-resolver"; import { screen, device } from "../../platform"; import { path as fsPath, Folder, File } from "../file-system"; import * as trace from "../../trace"; import * as appModule from "../../application"; const MIN_WH: string = "minWH"; const MIN_W: string = "minW"; const MIN_H: string = "minH"; const PRIORITY_STEP = 10000; interface QualifierSpec { isMatch(value: string): boolean; getMatchValue(value: string, context: PlatformContext): number; } const minWidthHeightQualifier: QualifierSpec = { isMatch: function (value: string): boolean { return value.indexOf(MIN_WH) === 0; }, getMatchValue(value: string, context: PlatformContext): number { const numVal = parseInt(value.substr(MIN_WH.length)); if (isNaN(numVal)) { return -1; } const actualLength = Math.min(context.width, context.height); if (actualLength < numVal) { return -1; } return PRIORITY_STEP - (actualLength - numVal); } } const minWidthQualifier: QualifierSpec = { isMatch: function (value: string): boolean { return value.indexOf(MIN_W) === 0 && value.indexOf(MIN_WH) < 0; }, getMatchValue(value: string, context: PlatformContext): number { const numVal = parseInt(value.substr(MIN_W.length)); if (isNaN(numVal)) { return -1; } const actualWidth = context.width; if (actualWidth < numVal) { return -1; } return PRIORITY_STEP - (actualWidth - numVal); } } const minHeightQualifier: QualifierSpec = { isMatch: function (value: string): boolean { return value.indexOf(MIN_H) === 0 && value.indexOf(MIN_WH) < 0; }, getMatchValue(value: string, context: PlatformContext): number { const numVal = parseInt(value.substr(MIN_H.length)); if (isNaN(numVal)) { return -1; } const actualHeight = context.height; if (actualHeight < numVal) { return -1; } return PRIORITY_STEP - (actualHeight - numVal) } } const platformQualifier: QualifierSpec = { isMatch: function (value: string): boolean { return value === "android" || value === "ios"; }, getMatchValue(value: string, context: PlatformContext): number { return value === context.os.toLowerCase() ? 1 : -1; } } const orientationQualifier: QualifierSpec = { isMatch: function (value: string): boolean { return value === "land" || value === "port"; }, getMatchValue(value: string, context: PlatformContext): number { const isLandscape: number = (context.width > context.height) ? 1 : -1; return (value === "land") ? isLandscape : -isLandscape; } } // List of supported qualifiers ordered by priority const supportedQualifiers: Array = [ minWidthHeightQualifier, minWidthQualifier, minHeightQualifier, orientationQualifier, platformQualifier ]; export class FileNameResolver implements FileNameResolverDefinition { private _context: PlatformContext; private _cache = {}; constructor(context: PlatformContext) { this._context = context; } public resolveFileName(path: string, ext: string): string { const key = path + ext; let result: string = this._cache[key]; if (result === undefined) { result = this.resolveFileNameImpl(path, ext); this._cache[key] = result; } return result; } public clearCache(): void { this._cache = {}; } private resolveFileNameImpl(path: string, ext: string): string { let result: string = null; path = fsPath.normalize(path); ext = "." + ext; const candidates = this.getFileCandidatesFromFolder(path, ext); result = _findFileMatch(path, ext, candidates, this._context); return result; } private getFileCandidatesFromFolder(path: string, ext: string): Array { const candidates = new Array(); const folderPath = path.substring(0, path.lastIndexOf(fsPath.separator) + 1); if (Folder.exists(folderPath)) { const folder = Folder.fromPath(folderPath); folder.eachEntity((e) => { if (e instanceof File) { const file = e; if (file.path.indexOf(path) === 0 && file.extension === ext) { candidates.push(file.path); } } return true; }); } else { if (trace.isEnabled()) { 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: PlatformContext): string { let bestValue = -1 let result: string = null; for (let i = 0; i < candidates.length; i++) { const filePath = candidates[i]; const qualifiersStr: string = filePath.substr(path.length, filePath.length - path.length - ext.length); const qualifiers = qualifiersStr.split("."); const value = checkQualifiers(qualifiers, context); if (value >= 0 && value > bestValue) { bestValue = value; result = candidates[i]; } } return result; } function checkQualifiers(qualifiers: Array, context: PlatformContext): number { let 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: 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; } let resolverInstance: FileNameResolver; export function resolveFileName(path: string, ext: string): string { if (!resolverInstance) { resolverInstance = new FileNameResolver({ width: screen.mainScreen.widthDIPs, height: screen.mainScreen.heightDIPs, os: device.os, deviceType: device.deviceType }); } return resolverInstance.resolveFileName(path, ext); } appModule.on("cssChanged", args => resolverInstance = undefined); appModule.on("livesync", args => resolverInstance && resolverInstance.clearCache());