diff --git a/build/tsc-dev.js b/build/tsc-dev.js index 062310809..70b4d4fd1 100644 --- a/build/tsc-dev.js +++ b/build/tsc-dev.js @@ -3,31 +3,88 @@ var ts = require("typescript"); var fs = require("fs"); var path = require("path"); var arg1 = process.argv.length > 2 ? process.argv[2] : ""; +var isTranspile = arg1.indexOf("t") >= 0; var isIncremental = arg1.indexOf("i") >= 0; +var isWatching = arg1.indexOf("w") >= 0; +var opts = []; +if (isTranspile) { + opts.push("transpile"); +} if (isIncremental) { - console.log("incremental"); + opts.push("incremental"); +} +if (isWatching) { + opts.push("watch"); +} +if (opts.length > 0) { + console.log("Options: " + opts.join(", ")); +} +function isTS(file) { + return file.lastIndexOf(".ts") === file.length - 3; +} +function isDTS(file) { + return file.lastIndexOf(".d.ts") === file.length - 5; +} +function getJsPath(tsPath) { + return path.join(path.dirname(tsPath), path.basename(tsPath, ".ts")) + ".js"; +} +function hasChanged(tsName) { + try { + var jsName = getJsPath(tsName); + var tsTime = fs.statSync(tsName).mtime.getTime(); + var jsTime = fs.statSync(jsName).mtime.getTime(); + return jsTime < tsTime; + } + catch (e) { + return true; + } +} +function transpile(fileNames, options) { + console.time("transpile"); + var files = fileNames.filter(function (f) { return !isDTS(f); }); + if (isIncremental) { + files = files.filter(hasChanged); + } + files.forEach(function (tsPath) { + var tsSource = fs.readFileSync(tsPath, { encoding: "utf8" }); + var jsSource = ts.transpile(tsSource, options); + var jsPath = getJsPath(tsPath); + fs.writeFileSync(jsPath, jsSource, { flag: "w" }, function (err) { console.log(err); }); + if (isIncremental) { + console.log(" - " + tsPath); + } + }); + console.timeEnd("transpile"); + if (isWatching) { + console.log("Watching for changes..."); + fs.watch(".", { persistent: true, recursive: true, encoding: "utf8" }, function (event, file) { + try { + if (isTS(file) && !isDTS(file)) { + var tsPath = file; + var label = " - " + tsPath; + console.time(label); + var tsSource = fs.readFileSync(tsPath, { encoding: "utf8" }); + var jsSource = ts.transpile(tsSource, options); + var jsPath = getJsPath(tsPath); + fs.writeFileSync(jsPath, jsSource, { flag: "w" }, function (err) { console.log(err); }); + console.timeEnd(label); + } + } + catch (e) { + } + }); + } } function compile(fileNames, options) { console.time("program"); var program = ts.createProgram(fileNames, options); console.timeEnd("program"); - var sourceFiles = program.getSourceFiles().filter(function (f) { return f.fileName.lastIndexOf(".d.ts") !== f.fileName.length - 5; }); + var sourceFiles = program.getSourceFiles().filter(function (f) { return !isDTS(f.fileName); }); var emitResults = []; var allDiagnostics = []; console.time("transpile"); if (isIncremental) { - sourceFiles = sourceFiles.filter(function (srcFile) { - try { - var tsName = srcFile.fileName; - var jsName = path.join(path.dirname(tsName), path.basename(tsName, ".ts")) + ".js"; - var tsTime = fs.statSync(tsName).mtime.getTime(); - var jsTime = fs.statSync(jsName).mtime.getTime(); - return jsTime < tsTime; - } - catch (e) { - return true; - } - }); + sourceFiles = sourceFiles.filter(function (srcFile) { return hasChanged(srcFile.fileName); }); sourceFiles.forEach(function (srcFile) { console.log(" - " + srcFile.fileName); emitResults.push(program.emit(srcFile)); @@ -54,13 +111,19 @@ function compile(fileNames, options) { process.exit(exitCode); } var files = JSON.parse(fs.readFileSync("./tsconfig.json")).files; -compile(files, { +var options = { noEmitOnError: true, noEmitHelpers: true, - target: ts.ScriptTarget.ES5, - module: ts.ModuleKind.CommonJS, + target: 1 /* ES5 */, + module: 1 /* CommonJS */, declaration: false, noImplicitAny: false, noImplicitUseStrict: true, experimentalDecorators: true -}); +}; +if (isTranspile) { + transpile(files, { module: 1 /* CommonJS */ }); +} +else { + compile(files, options); +} diff --git a/build/tsc-dev.ts b/build/tsc-dev.ts index f4afd4c36..b47e3fbeb 100644 --- a/build/tsc-dev.ts +++ b/build/tsc-dev.ts @@ -5,9 +5,89 @@ var path = require("path"); var arg1 = process.argv.length > 2 ? process.argv[2] : ""; +var isTranspile = arg1.indexOf("t") >= 0; var isIncremental = arg1.indexOf("i") >= 0; +var isWatching = arg1.indexOf("w") >= 0; + +var opts = []; + +if (isTranspile) { + opts.push("transpile"); +} + if (isIncremental) { - console.log("incremental"); + opts.push("incremental"); +} + +if (isWatching) { + opts.push("watch"); +} + +if (opts.length > 0) { + console.log("Options: " + opts.join(", ")); +} + +function isTS(file: string): boolean { + return file.lastIndexOf(".ts") === file.length - 3; +} + +function isDTS(file: string): boolean { + return file.lastIndexOf(".d.ts") === file.length - 5; +} + +function getJsPath(tsPath: string): string { + return path.join(path.dirname(tsPath), path.basename(tsPath, ".ts")) + ".js"; +} + +function hasChanged(tsName: string): boolean { + try { + var jsName = getJsPath(tsName); + + var tsTime = fs.statSync(tsName).mtime.getTime(); + var jsTime = fs.statSync(jsName).mtime.getTime(); + + return jsTime < tsTime; + } catch(e) { + return true; + } +} + +function transpile(fileNames: string[], options: ts.CompilerOptions) { + console.time("transpile"); + var files = fileNames.filter(f => !isDTS(f)); + if (isIncremental) { + files = files.filter(hasChanged); + } + files.forEach(tsPath => { + var tsSource = fs.readFileSync(tsPath, { encoding: "utf8" }); + var jsSource = ts.transpile(tsSource, options); + var jsPath = getJsPath(tsPath); + fs.writeFileSync(jsPath, jsSource, { flag: "w" }, function(err) { console.log(err); }); + if (isIncremental) { + console.log(" - " + tsPath); + } + }); + console.timeEnd("transpile"); + + if (isWatching) { + console.log("Watching for changes..."); + fs.watch(".", { persistent: true, recursive: true, encoding: "utf8" }, (event, file) => { + try { + if (isTS(file) && !isDTS(file)) { + var tsPath = file; + var label = " - " + tsPath; + console.time(label); + var tsSource = fs.readFileSync(tsPath, { encoding: "utf8" }); + var jsSource = ts.transpile(tsSource, options); + var jsPath = getJsPath(tsPath); + fs.writeFileSync(jsPath, jsSource, { flag: "w" }, function(err) { console.log(err); }); + console.timeEnd(label); + } + } catch(e) { + // console.log(e); + } + }); + } } function compile(fileNames: string[], options: ts.CompilerOptions) { @@ -15,27 +95,14 @@ function compile(fileNames: string[], options: ts.CompilerOptions) { var program = ts.createProgram(fileNames, options); console.timeEnd("program"); - var sourceFiles = program.getSourceFiles().filter(f => f.fileName.lastIndexOf(".d.ts") !== f.fileName.length - 5); + var sourceFiles = program.getSourceFiles().filter(f => !isDTS(f.fileName)); var emitResults = []; var allDiagnostics = []; console.time("transpile"); if (isIncremental) { - sourceFiles = sourceFiles.filter(srcFile => { - try { - var tsName = srcFile.fileName; - var jsName = path.join(path.dirname(tsName), path.basename(tsName, ".ts")) + ".js"; - - var tsTime = fs.statSync(tsName).mtime.getTime(); - var jsTime = fs.statSync(jsName).mtime.getTime(); - - return jsTime < tsTime; - } catch(e) { - return true; - } - }); - + sourceFiles = sourceFiles.filter(srcFile => hasChanged(srcFile.fileName)); sourceFiles.forEach(srcFile => { console.log(" - " + srcFile.fileName); emitResults.push(program.emit(srcFile)); @@ -66,8 +133,7 @@ function compile(fileNames: string[], options: ts.CompilerOptions) { } var files = JSON.parse(fs.readFileSync("./tsconfig.json")).files; -compile(files, -{ +var options: ts.CompilerOptions = { noEmitOnError: true, noEmitHelpers: true, target: ts.ScriptTarget.ES5, @@ -76,5 +142,9 @@ compile(files, noImplicitAny: false, noImplicitUseStrict: true, experimentalDecorators: true -}); - +}; +if (isTranspile) { + transpile(files, { module: ts.ModuleKind.CommonJS }); +} else { + compile(files, options); +} \ No newline at end of file diff --git a/package.json b/package.json index 3271b6a0d..880c11957 100644 --- a/package.json +++ b/package.json @@ -21,5 +21,10 @@ "time-grunt": "1.3.0", "tslint": "3.4.0", "typescript": "1.8.2" + }, + "scripts": { + "tsc-tiw": "node build/tsc-dev.js tiw", + "tsc": "tsc", + "link-tests": "cd tns-core-modules && npm link && cd ../tests && npm link tns-core-modules" } } diff --git a/tests/app/platform-tests.ts b/tests/app/platform-tests.ts index 56cf6924d..6474e98a6 100644 --- a/tests/app/platform-tests.ts +++ b/tests/app/platform-tests.ts @@ -1,5 +1,6 @@ import TKUnit = require("./TKUnit"); import app = require("application"); +import { isIOS, isAndroid } from "platform"; // >> platform-require import platformModule = require("platform"); @@ -29,3 +30,12 @@ export function snippet_print_all() { console.log("Screen scale: " + platformModule.screen.mainScreen.scale); // << platform-current }; + +export function testIsIOSandIsAndroid() { + if (isIOS) { + TKUnit.assertTrue(!!NSObject, "isIOS is true-ish but common iOS APIs are not available."); + } else if (isAndroid) { + TKUnit.assertTrue(!!android, "isAndroid is true but common 'android' package is not available."); + } +} + diff --git a/tests/app/ui/image/image-tests.ts b/tests/app/ui/image/image-tests.ts index c6d42e066..da878fc2b 100644 --- a/tests/app/ui/image/image-tests.ts +++ b/tests/app/ui/image/image-tests.ts @@ -1,4 +1,11 @@ -import TKUnit = require("../../TKUnit"); +import {Image} from "ui/image"; +import {StackLayout} from "ui/layouts/stack-layout"; +import {GridLayout} from "ui/layouts/grid-layout"; +import {isIOS} from "platform"; +// import {target} from "../../TKUnit"; + +import TKUnit = require("../../TKUnit"); + // >> img-require import ImageModule = require("ui/image"); // << img-require @@ -244,3 +251,62 @@ export var test_SettingStretch_none = function () { helper.buildUIAndRunTest(image, testFunc); } + +function ios(func: T): T { + return isIOS ? func : undefined; +} + +export var test_SettingImageSourceWhenSizedToParentDoesNotRequestLayout = ios(() => { + let host = new GridLayout(); + + let image = new Image(); + + host.width = 300; + host.height = 300; + host.addChild(image); + + let mainPage = helper.getCurrentPage(); + mainPage.content = host; + TKUnit.waitUntilReady(() => host.isLoaded); + + let called = false; + image.requestLayout = () => called = true; + image.src = "~/logo.png"; + + TKUnit.assertFalse(called, "image.requestLayout should not be called."); +}); + +export var test_SettingImageSourceWhenFixedWidthAndHeightDoesNotRequestLayout = ios(() => { + let host = new StackLayout(); + let image = new Image(); + image.width = 100; + image.height = 100; + host.addChild(image); + + let mainPage = helper.getCurrentPage(); + mainPage.content = host; + TKUnit.waitUntilReady(() => host.isLoaded); + + let called = false; + image.requestLayout = () => called = true; + image.src = "~/logo.png"; + + TKUnit.assertFalse(called, "image.requestLayout should not be called."); +}); + +export var test_SettingImageSourceWhenSizedToContentShouldInvalidate = ios(() => { + let host = new StackLayout(); + let image = new Image(); + host.addChild(image); + + let mainPage = helper.getCurrentPage(); + mainPage.content = host; + TKUnit.waitUntilReady(() => host.isLoaded); + + let called = false; + image.requestLayout = () => called = true; + image.src = "~/logo.png"; + + TKUnit.assertTrue(called, "image.requestLayout should be called."); +}); + diff --git a/tns-core-modules/platform/platform.android.ts b/tns-core-modules/platform/platform.android.ts index ef2b76602..f60de5800 100644 --- a/tns-core-modules/platform/platform.android.ts +++ b/tns-core-modules/platform/platform.android.ts @@ -133,3 +133,5 @@ export var device: definition.Device = new Device(); export module screen { export var mainScreen = new MainScreen(); } + +export var isAndroid = true; \ No newline at end of file diff --git a/tns-core-modules/platform/platform.d.ts b/tns-core-modules/platform/platform.d.ts index b8c6a6fcf..23eb7f87f 100644 --- a/tns-core-modules/platform/platform.d.ts +++ b/tns-core-modules/platform/platform.d.ts @@ -3,6 +3,16 @@ * Contains all kinds of information about the device, its operating system and software. */ declare module "platform" { + + /** + * Gets a value indicating if the app is running on the Android platform. + */ + export var isAndroid: boolean; + + /** + * Gets a value indicating if the app is running on the iOS platform. + */ + export var isIOS: boolean; /* * Enum holding platform names. diff --git a/tns-core-modules/platform/platform.ios.ts b/tns-core-modules/platform/platform.ios.ts index 0258a8bfb..fac609ccd 100644 --- a/tns-core-modules/platform/platform.ios.ts +++ b/tns-core-modules/platform/platform.ios.ts @@ -126,3 +126,5 @@ export var device: definition.Device = new Device(); export module screen { export var mainScreen = new MainScreen(); } + +export var isIOS = true; \ No newline at end of file diff --git a/tns-core-modules/ui/image/image.ios.ts b/tns-core-modules/ui/image/image.ios.ts index d294a17b9..bd05df790 100644 --- a/tns-core-modules/ui/image/image.ios.ts +++ b/tns-core-modules/ui/image/image.ios.ts @@ -36,6 +36,7 @@ function onImageSourcePropertyChanged(data: dependencyObservable.PropertyChangeD export class Image extends imageCommon.Image { private _ios: UIImageView; + private _imageSourceAffectsLayout: boolean = true; constructor() { super(); @@ -54,7 +55,7 @@ export class Image extends imageCommon.Image { public _setNativeImage(nativeImage: any) { this.ios.image = nativeImage; - if (isNaN(this.width) || isNaN(this.height)) { + if (this._imageSourceAffectsLayout) { this.requestLayout(); } } @@ -77,7 +78,9 @@ export class Image extends imageCommon.Image { var finiteWidth: boolean = widthMode !== utils.layout.UNSPECIFIED; var finiteHeight: boolean = heightMode !== utils.layout.UNSPECIFIED; - + + this._imageSourceAffectsLayout = widthMode !== utils.layout.EXACTLY || heightMode !== utils.layout.EXACTLY; + if (nativeWidth !== 0 && nativeHeight !== 0 && (finiteWidth || finiteHeight)) { var scale = Image.computeScaleFactor(width, height, finiteWidth, finiteHeight, nativeWidth, nativeHeight, this.stretch); var resultW = Math.floor(nativeWidth * scale.width);