From 03f9867e2ab8e217464e5918144c4663efbe783d Mon Sep 17 00:00:00 2001 From: Rossen Hristov Date: Wed, 13 Apr 2016 10:37:12 +0300 Subject: [PATCH 01/20] Generic font families `system`, `sans-serif` and `monospace` now correctly resolve the system font when possible on iOS Resolves #1864 --- CrossPlatformModules.csproj | 8 +++++ apps/sample-fonts/app.ts | 3 ++ apps/sample-fonts/main-page.ts | 62 +++++++++++++++++++++++++++++++++ apps/sample-fonts/main-page.xml | 5 +++ apps/sample-fonts/package.json | 2 ++ ui/styling/font-common.ts | 1 + ui/styling/font.android.ts | 4 +++ ui/styling/font.ios.ts | 35 ++++++++++++++----- 8 files changed, 111 insertions(+), 9 deletions(-) create mode 100644 apps/sample-fonts/app.ts create mode 100644 apps/sample-fonts/main-page.ts create mode 100644 apps/sample-fonts/main-page.xml create mode 100644 apps/sample-fonts/package.json diff --git a/CrossPlatformModules.csproj b/CrossPlatformModules.csproj index 88f3c1311..4465754e9 100644 --- a/CrossPlatformModules.csproj +++ b/CrossPlatformModules.csproj @@ -90,6 +90,10 @@ + + + main-page.xml + @@ -152,6 +156,9 @@ + + Designer + @@ -2106,6 +2113,7 @@ PreserveNewest + diff --git a/apps/sample-fonts/app.ts b/apps/sample-fonts/app.ts new file mode 100644 index 000000000..d903b1c7c --- /dev/null +++ b/apps/sample-fonts/app.ts @@ -0,0 +1,3 @@ +import application = require("application"); +application.cssFile = "app.css" +application.start({ moduleName: "main-page" }); \ No newline at end of file diff --git a/apps/sample-fonts/main-page.ts b/apps/sample-fonts/main-page.ts new file mode 100644 index 000000000..87d957983 --- /dev/null +++ b/apps/sample-fonts/main-page.ts @@ -0,0 +1,62 @@ +import { View } from "ui/core/view"; +import { EventData } from "data/observable"; +import { LayoutBase } from "ui/layouts/layout-base"; +import { Label } from "ui/label"; +import { TextField } from "ui/text-field"; +import { TextView } from "ui/text-view"; +import { Button } from "ui/button"; +import { FontStyle, FontWeight } from "ui/enums"; +import typeUtils = require("utils/types"); +import { Color } from "color"; + +const fontFamilies = ["system", "sans-serif", "serif", "monospace"]; +const fontWeights = [FontWeight.normal, FontWeight.bold]; +const fontStyles = [FontStyle.normal, FontStyle.italic]; + +export function onStackLayoutLoaded(args: EventData) { + var layout = args.object; + _generateViews(() => { return new Label(); }, layout); + _generateViews(() => { return new TextField(); }, layout); + _generateViews(() => { return new TextView(); }, layout); + _generateViews(() => { return new Button(); }, layout); +} + +function _generateViews(factory: () => View, layout: LayoutBase) { + for (var f = 0; f < fontFamilies.length; f++) { + for (var w = 0; w < fontWeights.length; w++) { + for (var s = 0; s < fontStyles.length; s++) { + var view = factory(); + var css = `font-family: ${fontFamilies[f]}; font-weight: ${fontWeights[w]}; font-style: ${fontStyles[s]};`; + (view).text = `${typeUtils.getClass(view)} ${css}`; + (view).textWrap = true; + view.style.textAlignment = "left"; + view.setInlineStyle(css); + view.margin = "1 0"; + view.borderWidth = 1; + view.height = 75; + view.color = new Color("Black"); + view.backgroundColor = new Color("LightGray"); + view.on("loaded", args => { + (view).text += _getFontInfo(view); + }); + layout.addChild(view); + } + } + } +} + +function _getFontInfo(view: View): string { + if (view.ios) { + var uiFont: UIFont; + if (view.ios instanceof UIButton) { + uiFont = view.ios.titleLabel.font; + } + else if (view.ios.font) { + uiFont = view.ios.font; + } + + return ` ${uiFont.fontName} ${uiFont.pointSize}pt.`; + } + + return ""; +} \ No newline at end of file diff --git a/apps/sample-fonts/main-page.xml b/apps/sample-fonts/main-page.xml new file mode 100644 index 000000000..e029e0bf4 --- /dev/null +++ b/apps/sample-fonts/main-page.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/apps/sample-fonts/package.json b/apps/sample-fonts/package.json new file mode 100644 index 000000000..db7dfb767 --- /dev/null +++ b/apps/sample-fonts/package.json @@ -0,0 +1,2 @@ +{ "name" : "sample-fonts", + "main" : "app.js" } diff --git a/ui/styling/font-common.ts b/ui/styling/font-common.ts index 86e26f729..e5f2f8e27 100644 --- a/ui/styling/font-common.ts +++ b/ui/styling/font-common.ts @@ -130,6 +130,7 @@ export module genericFontFamilies { export var serif = "serif"; export var sansSerif = "sans-serif"; export var monospace = "monospace"; + export var system = "system"; } var styles = new Set(); diff --git a/ui/styling/font.android.ts b/ui/styling/font.android.ts index 6f012b169..bf2cf329e 100644 --- a/ui/styling/font.android.ts +++ b/ui/styling/font.android.ts @@ -102,6 +102,10 @@ export class Font extends common.Font { result = android.graphics.Typeface.MONOSPACE; break; + case common.genericFontFamilies.system: + result = android.graphics.Typeface.DEFAULT; + break; + default: result = this.loadFontFromFile(fonts[i]); break; diff --git a/ui/styling/font.ios.ts b/ui/styling/font.ios.ts index dc0ca0016..de3527586 100644 --- a/ui/styling/font.ios.ts +++ b/ui/styling/font.ios.ts @@ -3,10 +3,6 @@ import common = require("./font-common"); import fs = require("file-system"); import * as traceModule from "trace"; -var DEFAULT_SERIF = "Times New Roman"; -var DEFAULT_SANS_SERIF = "Helvetica"; -var DEFAULT_MONOSPACE = "Courier New"; - export class Font extends common.Font { public static default = new Font(undefined, undefined, enums.FontStyle.normal, enums.FontWeight.normal); @@ -18,6 +14,8 @@ export class Font extends common.Font { public getUIFont(defaultFont: UIFont): UIFont { if (!this._uiFont) { + var size = this.fontSize || defaultFont.pointSize; + var symbolicTraits: number = 0; if (this.isBold) { symbolicTraits |= UIFontDescriptorSymbolicTraits.UIFontDescriptorTraitBold; @@ -26,11 +24,30 @@ export class Font extends common.Font { symbolicTraits |= UIFontDescriptorSymbolicTraits.UIFontDescriptorTraitItalic; } - var descriptor = resolveFontDescriptor(this.fontFamily, symbolicTraits); + var descriptor: UIFontDescriptor; + switch (this.fontFamily) { + + case common.genericFontFamilies.sansSerif: + case common.genericFontFamilies.system: + let uiFont = UIFont.systemFontOfSize(size); + descriptor = uiFont.fontDescriptor().fontDescriptorWithSymbolicTraits(symbolicTraits); + break; + + case common.genericFontFamilies.monospace: + if ((UIFont).monospacedDigitSystemFontOfSizeWeight) {// This method is available on iOS 9.0 and later. + let uiFont = (UIFont).monospacedDigitSystemFontOfSizeWeight(size, 0); + descriptor = uiFont.fontDescriptor().fontDescriptorWithSymbolicTraits(symbolicTraits); + } + break; + } + + if (!descriptor) { + descriptor = resolveFontDescriptor(this.fontFamily, symbolicTraits); + } + if (!descriptor) { descriptor = defaultFont.fontDescriptor().fontDescriptorWithSymbolicTraits(symbolicTraits); } - var size = this.fontSize || defaultFont.pointSize; this._uiFont = UIFont.fontWithDescriptorSize(descriptor, size); } @@ -121,6 +138,9 @@ function resolveFontDescriptor(fontFamilyValue: string, symbolicTraits: number): return null; } +const DEFAULT_SERIF = "Times New Roman"; +const DEFAULT_MONOSPACE = "Courier New"; + function getFontFamilyRespectingGenericFonts(fontFamily: string): string { if (!fontFamily) { return fontFamily; @@ -130,9 +150,6 @@ function getFontFamilyRespectingGenericFonts(fontFamily: string): string { case common.genericFontFamilies.serif: return DEFAULT_SERIF; - case common.genericFontFamilies.sansSerif: - return DEFAULT_SANS_SERIF; - case common.genericFontFamilies.monospace: return DEFAULT_MONOSPACE; From 2347ccbd9ab5382d579bf3d79869d63a2187e093 Mon Sep 17 00:00:00 2001 From: Rossen Hristov Date: Wed, 13 Apr 2016 11:08:13 +0300 Subject: [PATCH 02/20] Add apps/sample-fonts/*.ts to tsconfig.json --- tsconfig.json | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/tsconfig.json b/tsconfig.json index 73a78486a..f5a0260d4 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -66,6 +66,8 @@ "apps/editable-text-demo/app.ts", "apps/editable-text-demo/main-page.ts", "apps/editable-text-demo/model.ts", + "apps/sample-fonts/app.ts", + "apps/sample-fonts/main-page.ts", "apps/gallery-app/app.ts", "apps/gallery-app/main-page.ts", "apps/gallery-app/views/dialogs.ts", @@ -77,9 +79,9 @@ "apps/modal-views-demo/app.ts", "apps/modal-views-demo/login-page.ts", "apps/modal-views-demo/main-page.ts", - "apps/navigation-events-demo/app.ts", - "apps/navigation-events-demo/page1.ts", - "apps/navigation-events-demo/page2.ts", + "apps/navigation-events-demo/app.ts", + "apps/navigation-events-demo/page1.ts", + "apps/navigation-events-demo/page2.ts", "apps/notifications-demo/app.ts", "apps/notifications-demo/main-page.ts", "apps/orientation-demo/app.ts", From e4ae43053b135e095016b6235dec5300118d5842 Mon Sep 17 00:00:00 2001 From: Erjan Gavalji Date: Mon, 11 Apr 2016 17:02:33 +0300 Subject: [PATCH 03/20] Publish XML Test results and publish @next Modify the AndroidManifest file in the app dir to fix the HTTP tests Pull the test results in XML format and publish them as artifacts Have the test-results checks as a single command Parametrize AVD version; Use the package version, retrieved from package.json for builds and runs Make the package name a variable Fix test crash detection according to the time taken Move all travis-related scripts under the build dir --- .travis.yml | 48 +++++++++++++++++--- build/run-testsapp.grunt.js | 4 +- build/travis-scripts/add-publishConfig.js | 25 ++++++++++ build/travis-scripts/check-testrun-broken.js | 6 +-- 4 files changed, 71 insertions(+), 12 deletions(-) create mode 100755 build/travis-scripts/add-publishConfig.js diff --git a/.travis.yml b/.travis.yml index 1637ea83b..9b32a5174 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,3 +1,15 @@ +env: + global: + - DATE=$(date +%Y-%m-%d) + - PACKAGE_VERSION=$DATE-$TRAVIS_BUILD_NUMBER + - PACKAGE_NAME=tns-core-modules + - NODE_VERSION=5.10.1 + - EMULATOR_API_VER=21 + - RUNTIMEVERSION=next + - AVD_NAME=Arm$EMULATOR_API_VER +addons: + artifacts: + paths: $HOME/test-run-results$PACKAGE_VERSION.xml sudo: required dist: trusty language: android @@ -10,26 +22,48 @@ android: - platform-tools - tools - build-tools-23.0.3 - - android-21 + - android-$EMULATOR_API_VER - android-23 - extra-android-support - extra-android-m2repository - - sys-img-armeabi-v7a-android-21 + - sys-img-armeabi-v7a-android-$EMULATOR_API_VER before_script: - - nvm install 5.10.1 + - nvm install $NODE_VERSION - npm install -g grunt-cli - npm install - (cd build/platform-declarations && npm install) - - echo no | android create avd --force -n Arm21 -t android-21 -b armeabi-v7a -c 12M - - emulator -avd Arm21 -no-skin -no-audio -no-window & + - echo no | android create avd --force -n $AVD_NAME -t android-$EMULATOR_API_VER -b armeabi-v7a -c 12M + - emulator -avd $AVD_NAME -no-skin -no-audio -no-window & - android-wait-for-emulator script: - jdk_switcher use oraclejdk8 - grunt default && + FULL_PACKAGE_VERSION=`node -e 'console.log(require("./bin/dist/modules/package.json").version);'` && (cd build/platform-declarations && grunt) && echo no | npm install nativescript -g > /dev/null && - grunt buildOnlyTestsApp --platform=Android --modulesPath=./bin/dist/tns-core-modules-2.0.0.tgz --runtimeVersion=next --emuPId=.*emulator.* --avd=Api21 --showEmu=false > /dev/null && - grunt runOnlyTestsApp --platform=Android --modulesPath=./bin/dist/tns-core-modules-2.0.0.tgz --emuPId=.*emulator.* --avd=Api21 --showEmu=false + grunt buildOnlyTestsApp --platform=Android --modulesPath=./bin/dist/$PACKAGE_NAME-$FULL_PACKAGE_VERSION.tgz --runtimeVersion=$RUNTIMEVERSION --emuPId=.*emulator.* --avd=$AVD_NAME --showEmu=false > /dev/null && + grunt runOnlyTestsApp --platform=Android --modulesPath=./bin/dist/$PACKAGE_NAME-$FULL_PACKAGE_VERSION.tgz --emuPId=.*emulator.* --avd=$AVD_NAME --showEmu=false - node ./build/travis-scripts/check-testrun-broken.js + - adb pull /data/data/org.nativescript.TestsApp/files/test-results.xml && + mv test-results.xml ~/test-run-results$PACKAGE_VERSION.xml +before_deploy: + - mv bin/dist/$PACKAGE_NAME-$FULL_PACKAGE_VERSION.tgz ../.deploymentpackage + - mv .build ../ + - cd .. + - rm -rf NativeScript + - tar -zxvf .deploymentpackage + - mv package $PACKAGE_NAME + - cd $PACKAGE_NAME + - rm ../.deploymentpackage + - mv ../build ./ + - node ./build/travis-scripts/add-publishConfig.js next +deploy: + provider: npm + email: nativescript@telerik.com + on: + branch: master + skip_cleanup: true + api_key: + secure: aFJZR8VIbFAlXfFx5G2AveSgpGjr40prghvw8m06X0yvmUQlucwHVyq+Ov0ZD94br8d7OUOPbUzh+p9N/+oXLAXOj3DbQmJaCc+fk/e+avHu1BRy3fg295P9BQau1Abu+2ZO7tUbg5zAqJqhbEgjXsr9B5gxl+vwh4lbDhCPCwo= diff --git a/build/run-testsapp.grunt.js b/build/run-testsapp.grunt.js index b389589be..7ffd381dc 100644 --- a/build/run-testsapp.grunt.js +++ b/build/run-testsapp.grunt.js @@ -115,8 +115,8 @@ module.exports = { }, addAndroidPermissions: { src: "AndroidManifest.xml", - dest: localCfg.applicationDir + "/platforms/android/src/main/", - cwd: localCfg.applicationDir + "/platforms/android/src/main", + dest: localCfg.applicationDir + "/app/App_Resources/Android/", + cwd: localCfg.applicationDir + "/app/App_Resources/Android", expand: true, options: { process: function(content, srcPath) { diff --git a/build/travis-scripts/add-publishConfig.js b/build/travis-scripts/add-publishConfig.js new file mode 100755 index 000000000..742487a99 --- /dev/null +++ b/build/travis-scripts/add-publishConfig.js @@ -0,0 +1,25 @@ +#!/usr/bin/env node + +var fsModule = require('fs'); + +//Adds a publishConfig section to the package.json file +// and sets a tag to it + +var path = './package.json'; +var fileOptions = {encoding: "utf-8"}; +var content = fsModule.readFileSync(path, fileOptions); + +var tag = process.argv[2]; +if (!tag) { + console.log('Please pass the tag name as an argument!'); + process.exit(1); +} + +var packageDef = JSON.parse(content); +if (!packageDef.publishConfig) { + packageDef.publishConfig = {}; +} +packageDef.publishConfig.tag = tag; + +var newContent = JSON.stringify(packageDef, null, ' '); +fsModule.writeFileSync(path, newContent, fileOptions); diff --git a/build/travis-scripts/check-testrun-broken.js b/build/travis-scripts/check-testrun-broken.js index 47e5999a4..3704815d1 100755 --- a/build/travis-scripts/check-testrun-broken.js +++ b/build/travis-scripts/check-testrun-broken.js @@ -1,8 +1,8 @@ #!/usr/bin/env node var fsModule = require('fs'); var resultsFile = 'TestRunResult.txt'; -var successMarker ='=== ALL TESTS COMPLETE ==='; -var passMarker = /=== ALL TESTS COMPLETE ===\s+[^\n]*OK,\s+0\s+failed/mg; +var successMarker = /=== ALL TESTS COMPLETE for \d+ ms ===/; +var passMarker = /=== ALL TESTS COMPLETE for \d+ ms ===\s+[^\n]*OK,\s+0\s+failed/mg; var messages = { crash: 'TEST RUN CRASHED!', @@ -13,7 +13,7 @@ var messages = { var results = fsModule.readFileSync(resultsFile, 'utf-8'); -if (results.indexOf(successMarker) == -1) { +if (!results.match(successMarker)) { console.log(messages.crash); process.exit(1); } else if (results.match(passMarker)) { From 8b3d00df1d477a81d2e0efdaba92a64aa4f896f4 Mon Sep 17 00:00:00 2001 From: Tsvetan Raikov Date: Mon, 11 Apr 2016 17:36:20 +0300 Subject: [PATCH 04/20] fixed: some transitions in side drawer are not working --- application/application.d.ts | 7 ++++++ application/application.ios.ts | 43 ++++++++++++++++++++++++++++++++-- ui/frame/frame.ios.ts | 1 + 3 files changed, 49 insertions(+), 2 deletions(-) diff --git a/application/application.d.ts b/application/application.d.ts index 079e2bad9..1a7105d28 100644 --- a/application/application.d.ts +++ b/application/application.d.ts @@ -579,4 +579,11 @@ declare module "application" { */ removeNotificationObserver(observer: any, notificationName: string): void; } + + /* tslint:disable */ + export interface RootViewControllerImpl { + + contentController: any; + + } } diff --git a/application/application.ios.ts b/application/application.ios.ts index 16e0d86ad..1700b9fca 100644 --- a/application/application.ios.ts +++ b/application/application.ios.ts @@ -13,6 +13,43 @@ class Responder extends UIResponder { // } +class RootViewControllerImpl extends UIViewController implements definition.RootViewControllerImpl { + private _contentController: UIViewController; + + get contentController(): UIViewController { + return this._contentController; + } + + set contentController(contentController: UIViewController) { + + if (contentController.parentViewController !== null) { + contentController.willMoveToParentViewController(null); + contentController.view.removeFromSuperview(); + contentController.removeFromParentViewController(); + contentController.didMoveToParentViewController(this); + } + + if (this._contentController) { + this._contentController.willMoveToParentViewController(null); + this._contentController.view.removeFromSuperview(); + this._contentController.removeFromParentViewController(); + } + + this.addChildViewController(contentController); + this.view.addSubview(contentController.view); + contentController.view.frame = this.view.bounds; + contentController.view.autoresizingMask = UIViewAutoresizing.UIViewAutoresizingFlexibleWidth | UIViewAutoresizing.UIViewAutoresizingFlexibleHeight; + + this._contentController = contentController; + this._contentController.didMoveToParentViewController(this); + } + + public viewDidLoad(): void { + super.viewDidLoad(); + this.view.backgroundColor = UIColor.whiteColor(); + } +} + class Window extends UIWindow { private _content; @@ -140,11 +177,13 @@ class IOSApplication implements definition.iOSApplication { rootView = frame; } - + this._window.content = rootView; if (rootView instanceof Frame) { - this.rootController = this._window.rootViewController = rootView.ios.controller; + let rootController = new RootViewControllerImpl(); + this.rootController = this._window.rootViewController = rootController; + rootController.contentController = rootView.ios.controller; } else if (rootView.ios instanceof UIViewController) { this.rootController = this._window.rootViewController = rootView.ios; diff --git a/ui/frame/frame.ios.ts b/ui/frame/frame.ios.ts index 8f142cc62..56a028302 100644 --- a/ui/frame/frame.ios.ts +++ b/ui/frame/frame.ios.ts @@ -430,6 +430,7 @@ class UINavigationControllerImpl extends UINavigationController { } public viewDidLoad(): void { + super.viewDidLoad(); let owner = this._owner.get(); if (owner) { owner.onLoaded(); From fbde73f6db84da4d0aaf31a6ce8c476e3d3ef347 Mon Sep 17 00:00:00 2001 From: Vladimir Enchev Date: Wed, 13 Apr 2016 13:35:58 +0300 Subject: [PATCH 05/20] valid time used instead --- apps/ui-tests-app/time-picker/time-picker.xml | 3 +++ ui/time-picker/time-picker.android.ts | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) create mode 100644 apps/ui-tests-app/time-picker/time-picker.xml diff --git a/apps/ui-tests-app/time-picker/time-picker.xml b/apps/ui-tests-app/time-picker/time-picker.xml new file mode 100644 index 000000000..a69da67ee --- /dev/null +++ b/apps/ui-tests-app/time-picker/time-picker.xml @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/ui/time-picker/time-picker.android.ts b/ui/time-picker/time-picker.android.ts index 12fb086b4..83dcbb35a 100644 --- a/ui/time-picker/time-picker.android.ts +++ b/ui/time-picker/time-picker.android.ts @@ -26,7 +26,7 @@ export class TimePicker extends common.TimePicker { if (this.owner) { var validTime = common.getValidTime(this.owner, hour, minute); this.owner._setNativeValueSilently(validTime.hour, validTime.minute); - this.owner._onPropertyChangedFromNative(common.TimePicker.timeProperty, new Date(0, 0, 0, hour, minute)); + this.owner._onPropertyChangedFromNative(common.TimePicker.timeProperty, new Date(0, 0, 0, validTime.hour, validTime.minute)); } } }); From f68d94cc00d33a48fc25f583746f942ba6c50801 Mon Sep 17 00:00:00 2001 From: vchimev Date: Wed, 13 Apr 2016 15:16:33 +0300 Subject: [PATCH 06/20] Update ui-tests-app --- apps/ui-tests-app/css/text-decoration.ts | 6 ++++++ apps/ui-tests-app/css/text-decoration.xml | 2 +- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/apps/ui-tests-app/css/text-decoration.ts b/apps/ui-tests-app/css/text-decoration.ts index a1870be63..3304b372e 100644 --- a/apps/ui-tests-app/css/text-decoration.ts +++ b/apps/ui-tests-app/css/text-decoration.ts @@ -50,5 +50,11 @@ export function butonTap(args) { btn.style.textDecoration = "none"; textField.style.textDecoration = "none"; textView.style.textDecoration = "none"; + + if(lbl.text === "Change text") { + lbl.text = btn.text = textField.text = textView.text = "Text changed"; + } else { + lbl.text = btn.text = textField.text = textView.text = "Change text"; + } } } diff --git a/apps/ui-tests-app/css/text-decoration.xml b/apps/ui-tests-app/css/text-decoration.xml index c3d6e6308..9de343b28 100644 --- a/apps/ui-tests-app/css/text-decoration.xml +++ b/apps/ui-tests-app/css/text-decoration.xml @@ -1,6 +1,6 @@  - - + - - - + + + + + + + + + @@ -39,11 +57,17 @@ - + - - - + + + + + + + + + @@ -53,7 +77,7 @@ - + \ No newline at end of file diff --git a/apps/ui-tests-app/mainPage.ts b/apps/ui-tests-app/mainPage.ts index 1f71d2414..786cb986c 100644 --- a/apps/ui-tests-app/mainPage.ts +++ b/apps/ui-tests-app/mainPage.ts @@ -38,6 +38,7 @@ examples.set("xmlbasics", "bindings/xmlbasics"); examples.set("background", "css/background"); examples.set("formatted", "css/decoration-transform-formattedtext"); +examples.set("spacing", "css/letter-spacing"); examples.set("decoration", "css/text-decoration"); examples.set("transform", "css/text-transform"); examples.set("whitespace", "css/white-space");