diff --git a/DevelopmentWorkflow.md b/DevelopmentWorkflow.md index b54e0e8da..0f642077d 100644 --- a/DevelopmentWorkflow.md +++ b/DevelopmentWorkflow.md @@ -4,11 +4,12 @@ Development Workflow ## Project Structure The repository contains several packages and apps: - - `tns-core-modules` - The core NativeScript TypeScript modules used to develop NativeScript apps. - - `apps` - UI app used for manual testing and automation. - - `e2e` - applications and *e2e* tests. - - `tests` - Unit tests app for the `tns-core-modules`. - - `tns-platform-declarations` - TypeScript definitions for Android and iOS native APIs. + - `tns-core-modules` - The core NativeScript TypeScript modules used to develop NativeScript apps + - `tns-core-modules-widgets` - The native widgets (Java and Objective-C) used by the core NativeScript modules + - `e2e/ui-tests-app` - UI app used for manual testing and automation + - `e2e` - applications and *e2e* tests + - `tests` - Unit tests app for the `tns-core-modules` + - `tns-platform-declarations` - TypeScript definitions for Android and iOS native APIs Working with the repo is organized with npm scripts, go and read through the `scripts` section in the [package.json](./package.json). @@ -16,7 +17,7 @@ go and read through the `scripts` section in the [package.json](./package.json). Managing dependencies: - `tns-core-modules` depends on: - `tns-platform-declarations` - - `apps` depends on: + - `e2e/ui-tests-app` depends on: - `tns-platform-declarations` - `tns-core-modules` - `e2e` depends on: @@ -26,89 +27,60 @@ Managing dependencies: - `tns-core-modules` > NOTE: `tns-core-modules` depends on `tns-core-modules-widgets`, -this dependency contains native code and is rarely modified so for now it remains outside this repo. ## Initial Setup Clone (or fork/clone) the repo: -```bash +``` bash git clone https://github.com/NativeScript/NativeScript.git ``` -Install devDependencies: +Install dependencies: -```bash +``` bash npm install ``` - - ## Running Unit Tests -``` -cd ./tests -tns run android| ios +``` bash +cd tests +tns run android | ios ``` -## Running the Test App +## Running the UI Test App -The test app is an ordinary NativeScript app that logs the test results as it go. +The UI test app is an ordinary NativeScript app that logs the test results on the go. After the [initial setup](#initial-setup) you can run the tests with: -``` -# Make sure TypeScript is transpiled -tsc +``` bash +cd e2e/ui-tests-app + +# Run the Android app +tns platform add android@next # NOTE: do not commit this change to package.json +tns run android + +# Run the iOS app +tns platform add ios@next # NOTE: do not commit this change to package.json +tns run ios -# Run the app -tns run ios --path apps -tns run android --path apps ``` ## Running Another App -The [initial setup](#initial-setup) will `npm-link` the `tns-core-modules` globally. You can use it in any local project: - +1. Open the app, where you will use the module from the repository in the console. +2. Add the `tns-core-modules` in the application via: +```bash +npm install --save +# Example: npm install --save ../NativeScript/tns-core-modules +``` +3. Run the app ```bash -# Run once: Link tns-core-modules in your project -npm link tns-core-modules - -# Run the app tns run ios tns run android ``` -> Note: You still have to rebuild the TypeScript if you have made changes in the code of the core-modules. - - ## Platform declarations To update the platform declarations (the ios.d.ts-es) you can run: diff --git a/README.md b/README.md index 42e033c35..f20625534 100644 --- a/README.md +++ b/README.md @@ -54,16 +54,16 @@ Below is a common NativeScript architecture diagram. In more detail, read the [H The NativeScript framework consists of a number of components, all of which are open source and on GitHub. Here are the major ones: - **[Cross-platform modules](//github.com/NativeScript/NativeScript/)** - [![npm](https://img.shields.io/npm/dm/tns-core-modules.svg)](https://www.npmjs.com/package/tns-core-modules) [![Waffle.io - NativeScript Modules and Angular](https://badge.waffle.io/NativeScript/NativeScript.svg?columns=In%20Progress)](https://waffle.io/NativeScript/NativeScript) + [![npm](https://img.shields.io/npm/dm/tns-core-modules.svg)](https://www.npmjs.com/package/tns-core-modules) - This repo contains the [NativeScript cross-platform modules](http://docs.nativescript.org/core-concepts/modules), which abstract iOS and Android APIs into JavaScript APIs—e.g. `camera.takePicture()`. The modules are written in TypeScript. - **[iOS runtime](//github.com/NativeScript/ios-runtime/)** - [![npm](https://img.shields.io/npm/dm/tns-ios.svg)](https://www.npmjs.com/package/tns-ios) [![Waffle.io - NativeScript iOS Runtime](https://badge.waffle.io/NativeScript/ios-runtime.svg?columns=In%20Progress)](https://waffle.io/NativeScript/ios-runtime) + [![npm](https://img.shields.io/npm/dm/tns-ios.svg)](https://www.npmjs.com/package/tns-ios) - This repo contains the NativeScript iOS runtime—the code that hosts NativeScript iOS apps, and allows JavaScript code to be executed on iOS devices. The iOS runtime is written in a fun mix of C++, Objective-C, and more. - **[Android runtime](//github.com/NativeScript/android-runtime)** - [![npm](https://img.shields.io/npm/dm/tns-android.svg)](https://www.npmjs.com/package/tns-android) [![Waffle.io - NativeScript Runtimes and CLI](https://badge.waffle.io/NativeScript/android-runtime.svg?columns=In%20Progress)](https://waffle.io/NativeScript/android-runtime) + [![npm](https://img.shields.io/npm/dm/tns-android.svg)](https://www.npmjs.com/package/tns-android) - This repo contains the NativeScript Android—the code that hosts NativeScript Android apps, and allows JavaScript code to be executed on Android devices. The Android runtime is written in a fun mix of C++ and Java. - **[CLI](//github.com/NativeScript/nativescript-cli)** - [![npm](https://img.shields.io/npm/dm/nativescript.svg)](https://www.npmjs.com/package/nativescript) [![Waffle.io - NativeScript CLI](https://badge.waffle.io/NativeScript/nativescript-cli.svg?columns=In%20Progress)](https://waffle.io/NativeScript/nativescript-cli) + [![npm](https://img.shields.io/npm/dm/nativescript.svg)](https://www.npmjs.com/package/nativescript) - This repo contains the NativeScript command-line interface, which lets you create, build, and run apps using the NativeScript framework. The CLI is written in TypeScript. - **[Docs](//github.com/NativeScript/docs)** - This repo contains the NativeScript framework documentation, which is available at . The docs are written in Markdown. diff --git a/package.json b/package.json index fad67c4ed..e1c272b86 100644 --- a/package.json +++ b/package.json @@ -67,7 +67,7 @@ "dev-tsc-tests": "npm run tsc -- -p tests", "dev-tsc-apps": "npm run tsc -- -p apps", "dev-tsc-e2e": "npm run tsc -- -p e2e", - "dev-tsc-all": "npm run dev-tsc-tns-platform-declarations && npm run tsc && npm run dev-tsc-tests && npm run dev-tsc-apps && && npm run dev-tsc-e2e", + "dev-tsc-all": "npm run dev-tsc-tns-platform-declarations && npm run tsc && npm run dev-tsc-tests && npm run dev-tsc-apps && npm run dev-tsc-e2e", "dev-link-tns-core-modules-widgets": "(cd tns-core-modules-widgets/dist/package && npm link) && (cd tns-core-modules && npm i ../tns-core-modules-widgets/dist/package --save)", "dev-declarations": "rm -rf tns-platform-declarations/ios/objc* && TNS_TYPESCRIPT_DECLARATIONS_PATH=$PWD/tns-platform-declarations/ios/objc tns build ios --path tests", "test": "npm run test-android && npm run test-ios", diff --git a/tests/app/application/application-tests.android.ts b/tests/app/application/application-tests.android.ts index 1c76429fe..7cf181f81 100644 --- a/tests/app/application/application-tests.android.ts +++ b/tests/app/application/application-tests.android.ts @@ -41,7 +41,7 @@ export function testAndroidApplicationInitialized() { TKUnit.assert(app.android, "Android application not initialized."); TKUnit.assert(app.android.context, "Android context not initialized."); TKUnit.assert(app.android.foregroundActivity, "Android foregroundActivity not initialized."); - TKUnit.assert(app.android.foregroundActivity.isNativeScriptActivity, "Andorid foregroundActivity.isNativeScriptActivity is false."); + TKUnit.assert(app.android.foregroundActivity.isNativeScriptActivity, "Android foregroundActivity.isNativeScriptActivity is false."); TKUnit.assert(app.android.startActivity, "Android startActivity not initialized."); TKUnit.assert(app.android.nativeApp, "Android nativeApp not initialized."); TKUnit.assert(app.android.orientation, "Android orientation not initialized."); diff --git a/tns-core-modules-widgets/package.json b/tns-core-modules-widgets/package.json index 57c5a34bf..7a5a212c4 100644 --- a/tns-core-modules-widgets/package.json +++ b/tns-core-modules-widgets/package.json @@ -1,6 +1,6 @@ { "name": "tns-core-modules-widgets", - "version": "6.1.0", + "version": "6.2.0", "description": "Native widgets used in the NativeScript framework.", "scripts": { "test": "echo \"Error: no test specified\" && exit 1" @@ -17,8 +17,8 @@ "homepage": "https://github.com/NativeScript/NativeScript/blob/master/tns-core-modules-widgets#readme", "nativescript": { "platforms": { - "ios": "5.0.0", - "android": "5.0.0" + "ios": "6.0.0", + "android": "6.0.0" } } } diff --git a/tns-core-modules/application-settings/application-settings.android.ts b/tns-core-modules/application-settings/application-settings.android.ts index c042208c5..aaa09074e 100644 --- a/tns-core-modules/application-settings/application-settings.android.ts +++ b/tns-core-modules/application-settings/application-settings.android.ts @@ -85,10 +85,13 @@ export function clear(): void { } export function flush(): boolean { + ensureSharedPreferences(); + return sharedPreferences.edit().commit(); } export function getAllKeys(): Array { + ensureSharedPreferences(); const mappedPreferences = sharedPreferences.getAll(); const iterator = mappedPreferences.keySet().iterator(); const result = []; diff --git a/tns-core-modules/color/color-common.ts b/tns-core-modules/color/color-common.ts index b0b149253..b33e63e63 100644 --- a/tns-core-modules/color/color-common.ts +++ b/tns-core-modules/color/color-common.ts @@ -1,6 +1,7 @@ -import * as definition from "."; +import * as definition from "."; import * as types from "../utils/types"; import * as knownColors from "./known-colors"; +import { convertHSLToRGBColor } from "tns-core-modules/css/parser"; const SHARP = "#"; const HEX_REGEX = /(^#[0-9A-F]{6}$)|(^#[0-9A-F]{3}$)|(^#[0-9A-F]{8}$)/i; @@ -18,6 +19,8 @@ export class Color implements definition.Color { if (types.isString(arg)) { if (isRgbOrRgba(arg)) { this._argb = argbFromRgbOrRgba(arg); + } else if (isHslOrHsla(arg)) { + this._argb = argbFromHslOrHsla(arg); } else if (knownColors.isKnownName(arg)) { // The parameter is a known color name const hex = knownColors.getKnownColor(arg); @@ -127,7 +130,7 @@ export class Color implements definition.Color { return true; } - return HEX_REGEX.test(value) || isRgbOrRgba(value); + return HEX_REGEX.test(value) || isRgbOrRgba(value) || isHslOrHsla(value); } private _componentToHex(component: number): string { @@ -162,33 +165,58 @@ function isRgbOrRgba(value: string): boolean { return (toLower.indexOf("rgb(") === 0 || toLower.indexOf("rgba(") === 0) && toLower.indexOf(")") === (toLower.length - 1); } -function argbFromRgbOrRgba(value: string): number { +function isHslOrHsla(value: string): boolean { const toLower = value.toLowerCase(); - const parts = toLower.replace("rgba(", "").replace("rgb(", "").replace(")", "").trim().split(","); - let r = 255; - let g = 255; - let b = 255; + return (toLower.indexOf("hsl(") === 0 || toLower.indexOf("hsla(") === 0) && toLower.indexOf(")") === (toLower.length - 1); +} + +function parseColorWithAlpha(value: string): any { + const toLower = value.toLowerCase(); + const parts = toLower.replace(/(rgb|hsl)a?\(/, "") + .replace(")", "") + .trim().split(","); + + let f = 255; + let s = 255; + let t = 255; let a = 255; if (parts[0]) { - r = parseInt(parts[0].trim()); + f = parseInt(parts[0].trim()); } if (parts[1]) { - g = parseInt(parts[1].trim()); + s = parseInt(parts[1].trim()); } if (parts[2]) { - b = parseInt(parts[2].trim()); + t = parseInt(parts[2].trim()); } if (parts[3]) { a = Math.round(parseFloat(parts[3].trim()) * 255); } + return { f, s, t, a }; +} + +function argbFromRgbOrRgba(value: string): number { + const { f: r, s: g, t: b, a } = parseColorWithAlpha(value); + return (a & 0xFF) * 0x01000000 + (r & 0xFF) * 0x00010000 + (g & 0xFF) * 0x00000100 - + (b & 0xFF) * 0x00000001; + + (b & 0xFF); +} + +function argbFromHslOrHsla(value: string): number { + const { f: h, s: s, t: l, a } = parseColorWithAlpha(value); + + const { r, g, b } = convertHSLToRGBColor(h, s, l); + + return (a & 0xFF) * 0x01000000 + + (r & 0xFF) * 0x00010000 + + (g & 0xFF) * 0x00000100 + + (b & 0xFF); } diff --git a/tns-core-modules/css/parser.ts b/tns-core-modules/css/parser.ts index 28d464209..1ab2a7823 100644 --- a/tns-core-modules/css/parser.ts +++ b/tns-core-modules/css/parser.ts @@ -84,7 +84,7 @@ export function parseHexColor(text: string, start: number = 0): Parsed { function rgbaToArgbNumber(r: number, g: number, b: number, a: number = 1): number | undefined { if (r >= 0 && r <= 255 && g >= 0 && g <= 255 && b >= 0 && b <= 255 && a >= 0 && a <= 1) { - return (Math.round(a * 0xFF) * 0x01000000) + (r * 0x010000) + (g * 0x000100) + (b * 0x000001); + return (Math.round(a * 0xFF) * 0x01000000) + (r * 0x010000) + (g * 0x000100) + b; } else { return null; } @@ -116,6 +116,67 @@ export function parseRGBAColor(text: string, start: number = 0): Parsed { return { start, end, value }; } +export function convertHSLToRGBColor(hue: number, saturation: number, lightness: number): { r: number; g: number; b: number; } { + // Per formula it will be easier if hue is divided to 60° and saturation to 100 beforehand + // https://en.wikipedia.org/wiki/HSL_and_HSV#HSL_to_RGB + hue /= 60; + lightness /= 100; + + let chroma = (1 - Math.abs(2 * lightness - 1)) * saturation / 100, + X = chroma * (1 - Math.abs(hue % 2 - 1)), + // Add lightness match to all RGB components beforehand + { m: r, m: g, m: b } = { m: lightness - chroma / 2 }; + + if (0 <= hue && hue < 1) { r += chroma; g += X; } + else if (hue < 2) { r += X; g += chroma; } + else if (hue < 3) { g += chroma; b += X; } + else if (hue < 4) { g += X; b += chroma; } + else if (hue < 5) { r += X; b += chroma; } + else if (hue < 6) { r += chroma; b += X; } + + return { + r: Math.round(r * 0xFF), + g: Math.round(g * 0xFF), + b: Math.round(b * 0xFF) + }; +} + +function hslaToArgbNumber(h: number, s: number, l: number, a: number = 1): number | undefined { + let { r, g, b } = convertHSLToRGBColor(h, s, l); + + if (r >= 0 && r <= 255 && g >= 0 && g <= 255 && b >= 0 && b <= 255 && a >= 0 && a <= 1) { + return (Math.round(a * 0xFF) * 0x01000000) + (r * 0x010000) + (g * 0x000100) + b; + } else { + return null; + } +} + +const hslColorRegEx = /\s*(hsl\(\s*([\d.]*)\s*,\s*([\d.]*)%\s*,\s*([\d.]*)%\s*\))/gy; +export function parseHSLColor(text: string, start: number = 0): Parsed { + hslColorRegEx.lastIndex = start; + const result = hslColorRegEx.exec(text); + if (!result) { + return null; + } + const end = hslColorRegEx.lastIndex; + const value = result[1] && hslaToArgbNumber(parseFloat(result[2]), parseFloat(result[3]), parseFloat(result[4])); + + return { start, end, value }; +} + +const hslaColorRegEx = /\s*(hsla\(\s*([\d.]*)\s*,\s*([\d.]*)%\s*,\s*([\d.]*)%\s*,\s*([01]?\.?\d*)\s*\))/gy; +export function parseHSLAColor(text: string, start: number = 0): Parsed { + hslaColorRegEx.lastIndex = start; + const result = hslaColorRegEx.exec(text); + if (!result) { + return null; + } + const end = hslaColorRegEx.lastIndex; + const value = hslaToArgbNumber(parseFloat(result[2]), parseFloat(result[3]), parseFloat(result[4]), parseFloat(result[5])); + + return { start, end, value }; +} + export enum colors { transparent = 0x00000000, aliceblue = 0xFFF0F8FF, @@ -280,7 +341,12 @@ export function parseColorKeyword(value, start: number, keyword = parseKeyword(v } export function parseColor(value: string, start: number = 0, keyword = parseKeyword(value, start)): Parsed { - return parseHexColor(value, start) || parseColorKeyword(value, start, keyword) || parseRGBColor(value, start) || parseRGBAColor(value, start); + return parseHexColor(value, start) || + parseColorKeyword(value, start, keyword) || + parseRGBColor(value, start) || + parseRGBAColor(value, start) || + parseHSLColor(value, start) || + parseHSLAColor(value, start); } const keywordRegEx = /\s*([a-z][\w\-]*)\s*/giy; diff --git a/tns-core-modules/package.json b/tns-core-modules/package.json index 8ee9d95f3..60cef9c63 100644 --- a/tns-core-modules/package.json +++ b/tns-core-modules/package.json @@ -1,7 +1,7 @@ { "name": "tns-core-modules", "description": "Telerik NativeScript Core Modules", - "version": "6.1.1", + "version": "6.2.0", "homepage": "https://www.nativescript.org", "repository": { "type": "git", @@ -39,8 +39,8 @@ }, "nativescript": { "platforms": { - "ios": "5.0.0", - "android": "5.0.0" + "ios": "6.0.0", + "android": "6.0.0" } }, "snapshot": { diff --git a/tns-platform-declarations/package.json b/tns-platform-declarations/package.json index 83ee96fc8..83b9b9f7e 100644 --- a/tns-platform-declarations/package.json +++ b/tns-platform-declarations/package.json @@ -1,6 +1,6 @@ { "name": "tns-platform-declarations", - "version": "6.1.1", + "version": "6.2.0", "description": "Platform-specific TypeScript declarations for NativeScript for accessing native objects", "main": "", "scripts": { diff --git a/unit-tests/css/parser.ts b/unit-tests/css/parser.ts index 667f12177..fa21ed04d 100644 --- a/unit-tests/css/parser.ts +++ b/unit-tests/css/parser.ts @@ -56,6 +56,8 @@ describe("css", () => { test(parseColor, " #85456789 ", { start: 0, end: 12, value: 0x85456789 }); test(parseColor, " rgb(255, 8, 128) ", { start: 0, end: 18, value: 0xFFFF0880 }); test(parseColor, " rgba(255, 8, 128, 0.5) ", { start: 0, end: 24, value: 0x80FF0880 }); + test(parseColor, " hsl(330.9, 100%, 51.6%) ", { start: 0, end: 25, value: 0xFFFF0880 }); + test(parseColor, " hsla(330.9, 100%, 51.6%, 0.5) ", { start: 0, end: 31, value: 0x80FF0880 }); test(parseColor, "#FF0000 url(lucky.gif)", 8, null); test(parseColor, "url(lucky.gif) #FF0000 repeat", 15, { start: 15, end: 23, value: 0xFFFF0000 }); }); @@ -175,11 +177,11 @@ describe("css", () => { test(parseSelector, `[src ${attributeTest} "val"]`, { start: 0, end: 12 + attributeTest.length, value: [[[{ type: "[]", property: "src", test: attributeTest, value: "val"}], undefined]]}); }); test(parseSelector, "listview > .image", { start: 0, end: 17, value: [ - [[{ type: "", identifier: "listview"}], ">"], + [[{ type: "", identifier: "listview"}], ">"], [[{ type: ".", identifier: "image"}], undefined] ]}); test(parseSelector, "listview .image", { start: 0, end: 16, value: [ - [[{ type: "", identifier: "listview"}], " "], + [[{ type: "", identifier: "listview"}], " "], [[{ type: ".", identifier: "image"}], undefined] ]}); test(parseSelector, "button:hover", { start: 0, end: 12, value: [[[{ type: "", identifier: "button" }, { type: ":", identifier: "hover"}], undefined]]});