diff --git a/nativescript-angular/animation-driver.ts b/nativescript-angular/animation-driver.ts index f8ccfc631..f5275a901 100644 --- a/nativescript-angular/animation-driver.ts +++ b/nativescript-angular/animation-driver.ts @@ -1,11 +1,14 @@ -import { AnimationKeyframe } from '@angular/core/src/animation/animation_keyframe'; -import { AnimationPlayer } from '@angular/core/src/animation/animation_player'; -import { AnimationStyles } from '@angular/core/src/animation/animation_styles'; -import { AnimationDriver } from '@angular/platform-browser/src/dom/animation_driver'; +import { AnimationPlayer, AnimationStyles, AnimationKeyframe } from "./private_import_core"; import { NativeScriptAnimationPlayer } from './animation-player'; import {View} from "ui/core/view"; import styleProperty = require('ui/styling/style-property'); +export abstract class AnimationDriver { + abstract animate( + element: any, startingStyles: AnimationStyles, keyframes: AnimationKeyframe[], + duration: number, delay: number, easing: string): AnimationPlayer; +} + export class NativeScriptAnimationDriver implements AnimationDriver { computeStyle(element: any, prop: string): string { diff --git a/nativescript-angular/animation-player.ts b/nativescript-angular/animation-player.ts index 9acc8e961..780a77566 100644 --- a/nativescript-angular/animation-player.ts +++ b/nativescript-angular/animation-player.ts @@ -1,5 +1,4 @@ -import { AnimationKeyframe } from '@angular/core/src/animation/animation_keyframe'; -import { AnimationPlayer } from '@angular/core/src/animation/animation_player'; +import { AnimationPlayer, AnimationKeyframe } from "./private_import_core"; import { KeyframeAnimation, KeyframeAnimationInfo, KeyframeInfo, KeyframeDeclaration } from 'ui/animation/keyframe-animation'; import { View } from "ui/core/view"; import enums = require("ui/enums"); @@ -11,7 +10,8 @@ export class NativeScriptAnimationPlayer implements AnimationPlayer { public parentPlayer: AnimationPlayer; - private _subscriptions: Function[] = []; + private _startSubscriptions: Function[] = []; + private _doneSubscriptions: Function[] = []; private _finished = false; private _started = false; private animation: KeyframeAnimation; @@ -72,20 +72,29 @@ export class NativeScriptAnimationPlayer implements AnimationPlayer { } - onDone(fn: Function): void { this._subscriptions.push(fn); } + onStart(fn: Function): void { this._startSubscriptions.push(fn); } + onDone(fn: Function): void { this._doneSubscriptions.push(fn); } + + private _onStart() { + if (!this._started) { + this._started = true; + this._startSubscriptions.forEach(fn => fn()); + this._startSubscriptions = []; + } + } private _onFinish() { if (!this._finished) { this._finished = true; this._started = false; - this._subscriptions.forEach(fn => fn()); - this._subscriptions = []; + this._doneSubscriptions.forEach(fn => fn()); + this._doneSubscriptions = []; } } play(): void { if (this.animation) { - this._started = true; + this._onStart(); this.animation.play(this.target) .then(() => { this._onFinish(); }) .catch((e) => { }); diff --git a/nativescript-angular/collection-facade.ts b/nativescript-angular/collection-facade.ts new file mode 100644 index 000000000..03d3b56b0 --- /dev/null +++ b/nativescript-angular/collection-facade.ts @@ -0,0 +1,196 @@ +//Copied unexported functions from @angular/core/src/facade/collection +import { + isJsObject, isArray, getSymbolIterator, + isPresent, isBlank +} from "./lang-facade"; + +export function isListLikeIterable(obj: any): boolean { + if (!isJsObject(obj)) return false; + return isArray(obj) || + (!(obj instanceof Map) && // JS Map are iterables but return entries as [k, v] + getSymbolIterator() in obj); // JS Iterable have a Symbol.iterator prop +} + +export class ListWrapper { + // JS has no way to express a statically fixed size list, but dart does so we + // keep both methods. + static createFixedSize(size: number): any[] { return new Array(size); } + static createGrowableSize(size: number): any[] { return new Array(size); } + static clone(array: T[]): T[] { return array.slice(0); } + static forEachWithIndex(array: T[], fn: (t: T, n: number) => void) { + for (var i = 0; i < array.length; i++) { + fn(array[i], i); + } + } + static first(array: T[]): T { + if (!array) return null; + return array[0]; + } + static last(array: T[]): T { + if (!array || array.length == 0) return null; + return array[array.length - 1]; + } + static indexOf(array: T[], value: T, startIndex: number = 0): number { + return array.indexOf(value, startIndex); + } + static contains(list: T[], el: T): boolean { return list.indexOf(el) !== -1; } + static reversed(array: T[]): T[] { + var a = ListWrapper.clone(array); + return a.reverse(); + } + static concat(a: any[], b: any[]): any[] { return a.concat(b); } + static insert(list: T[], index: number, value: T) { list.splice(index, 0, value); } + static removeAt(list: T[], index: number): T { + var res = list[index]; + list.splice(index, 1); + return res; + } + static removeAll(list: T[], items: T[]) { + for (var i = 0; i < items.length; ++i) { + var index = list.indexOf(items[i]); + list.splice(index, 1); + } + } + static remove(list: T[], el: T): boolean { + var index = list.indexOf(el); + if (index > -1) { + list.splice(index, 1); + return true; + } + return false; + } + static clear(list: any[]) { list.length = 0; } + static isEmpty(list: any[]): boolean { return list.length == 0; } + static fill(list: any[], value: any, start: number = 0, end: number = null) { + (list).fill(value, start, end === null ? list.length : end); + } + static equals(a: any[], b: any[]): boolean { + if (a.length != b.length) return false; + for (var i = 0; i < a.length; ++i) { + if (a[i] !== b[i]) return false; + } + return true; + } + static slice(l: T[], from: number = 0, to: number = null): T[] { + return l.slice(from, to === null ? undefined : to); + } + static splice(l: T[], from: number, length: number): T[] { return l.splice(from, length); } + static sort(l: T[], compareFn?: (a: T, b: T) => number) { + if (isPresent(compareFn)) { + l.sort(compareFn); + } else { + l.sort(); + } + } + static toString(l: T[]): string { return l.toString(); } + static toJSON(l: T[]): string { return JSON.stringify(l); } + + static maximum(list: T[], predicate: (t: T) => number): T { + if (list.length == 0) { + return null; + } + var solution: any /** TODO #???? */ = null; + var maxValue = -Infinity; + for (var index = 0; index < list.length; index++) { + var candidate = list[index]; + if (isBlank(candidate)) { + continue; + } + var candidateValue = predicate(candidate); + if (candidateValue > maxValue) { + solution = candidate; + maxValue = candidateValue; + } + } + return solution; + } + + static flatten(list: Array): T[] { + var target: any[] = []; + _flattenArray(list, target); + return target; + } + + static addAll(list: Array, source: Array): void { + for (var i = 0; i < source.length; i++) { + list.push(source[i]); + } + } +} + +function _flattenArray(source: any[], target: any[]): any[] { + if (isPresent(source)) { + for (var i = 0; i < source.length; i++) { + var item = source[i]; + if (isArray(item)) { + _flattenArray(item, target); + } else { + target.push(item); + } + } + } + return target; +} + +export class StringMapWrapper { + static create(): {[k: /*any*/ string]: any} { + // Note: We are not using Object.create(null) here due to + // performance! + // http://jsperf.com/ng2-object-create-null + return {}; + } + static contains(map: {[key: string]: any}, key: string): boolean { + return map.hasOwnProperty(key); + } + static get(map: {[key: string]: V}, key: string): V { + return map.hasOwnProperty(key) ? map[key] : undefined; + } + static set(map: {[key: string]: V}, key: string, value: V) { map[key] = value; } + + static keys(map: {[key: string]: any}): string[] { return Object.keys(map); } + static values(map: {[key: string]: T}): T[] { + return Object.keys(map).map((k: string): T => map[k]); + } + static isEmpty(map: {[key: string]: any}): boolean { + for (var prop in map) { + return false; + } + return true; + } + static delete (map: {[key: string]: any}, key: string) { delete map[key]; } + static forEach(map: {[key: string]: V}, callback: (v: V, K: string) => void) { + for (let k of Object.keys(map)) { + callback(map[k], k); + } + } + + static merge(m1: {[key: string]: V}, m2: {[key: string]: V}): {[key: string]: V} { + var m: {[key: string]: V} = {}; + + for (let k of Object.keys(m1)) { + m[k] = m1[k]; + } + + for (let k of Object.keys(m2)) { + m[k] = m2[k]; + } + + return m; + } + + static equals(m1: {[key: string]: V}, m2: {[key: string]: V}): boolean { + var k1 = Object.keys(m1); + var k2 = Object.keys(m2); + if (k1.length != k2.length) { + return false; + } + var key: any /** TODO #???? */; + for (var i = 0; i < k1.length; i++) { + key = k1[i]; + if (m1[key] !== m2[key]) { + return false; + } + } + return true; + } +} diff --git a/nativescript-angular/common/detached-loader.ts b/nativescript-angular/common/detached-loader.ts index 62af15209..8c0a12ede 100644 --- a/nativescript-angular/common/detached-loader.ts +++ b/nativescript-angular/common/detached-loader.ts @@ -1,10 +1,13 @@ -import {ComponentRef, ComponentFactory, ViewContainerRef, Component, - Type, ViewChild, ComponentResolver, ChangeDetectorRef, Host} from '@angular/core'; +import { + ComponentRef, ComponentFactory, ViewContainerRef, + Component, Type, ViewChild, Compiler, + ComponentFactoryResolver, ChangeDetectorRef, Host +} from '@angular/core'; import trace = require("trace"); type AnyComponentRef = ComponentRef; interface PendingLoadEntry { - componentType: Type; + componentType: Type; resolveCallback: (AnyComponentRef) => void; } @@ -23,58 +26,26 @@ function log(message: string) { template: `` }) export class DetachedLoader { - @ViewChild('loader', { read: ViewContainerRef }) childContainerRef: ViewContainerRef; + constructor(private resolver: ComponentFactoryResolver, private changeDetector: ChangeDetectorRef, private containerRef: ViewContainerRef) { } - private viewLoaded = false; - private pendingLoads: PendingLoadEntry[] = []; + private loadInLocation(componentType: Type): Promise> { + const factory = this.resolver.resolveComponentFactory(componentType) + const componentRef = this.containerRef.createComponent( + factory, this.containerRef.length, this.containerRef.parentInjector); - constructor(private compiler: ComponentResolver, private changeDetector: ChangeDetectorRef, private containerRef: ViewContainerRef) { } + // Component is created, buit may not be checked if we are loading + // inside component with OnPush CD strategy. Mark us for check to be sure CD will reach us. + // We are inside a promise here so no need for setTimeout - CD should trigger after the promise. + log("DetachedLoader.loadInLocation component loaded -> markForCheck"); + this.changeDetector.markForCheck(); - public ngAfterViewInit() { - log("DetachedLoader.ngAfterViewInit"); - - this.viewLoaded = true; - this.pendingLoads.forEach(loadEntry => { - this.loadInLocation(loadEntry.componentType).then(loadedRef => { - loadEntry.resolveCallback(loadedRef); - }); - }); - } - - private loadInLocation(componentType: Type): Promise> { - return this.compiler.resolveComponent(componentType).then((componentFactory) => { - return this.childContainerRef.createComponent(componentFactory, this.childContainerRef.length, this.childContainerRef.parentInjector, null); - }).then((compRef) => { - log("DetachedLoader.loadInLocation component loaded -> markForCheck"); - // Component is created, buit may not be checked if we are loading - // inside component with OnPush CD strategy. Mark us for check to be sure CD will reach us. - // We are inside a promise here so no need for setTimeout - CD should trigger after the promise. - this.changeDetector.markForCheck(); - return compRef; - }) + return Promise.resolve(componentRef); } - public loadComponent(componentType: Type): Promise> { - log("DetachedLoader.loadComponent viewLoaded: " + this.viewLoaded); - - // Check if called before placeholder is initialized. - // Delay load if so. - if (this.viewLoaded) { - return this.loadInLocation(componentType); - } else { - // loadComponent called, but detached-loader is still not initialized. - // Mark it for change and trigger change detection to be sure it will be initialized, - // so that loading can conitionue. - log("DetachedLoader.loadComponent -> markForCheck(with setTimeout())") - setTimeout(() => this.changeDetector.markForCheck(), 0); - - return new Promise((resolve, reject) => { - this.pendingLoads.push({ - componentType: componentType, - resolveCallback: resolve - }); - }); - } + //TODO: change this API -- async promises not needed here anymore. + public loadComponent(componentType: Type): Promise> { + log("DetachedLoader.loadComponent"); + return this.loadInLocation(componentType); } public loadWithFactory(factory: ComponentFactory): ComponentRef { diff --git a/nativescript-angular/common/utils.ts b/nativescript-angular/common/utils.ts index 453d13661..82d05ec9e 100644 --- a/nativescript-angular/common/utils.ts +++ b/nativescript-angular/common/utils.ts @@ -1,4 +1,4 @@ -import {isBlank, isNumber} from '@angular/core/src/facade/lang'; +import {isBlank, isNumber} from "../lang-facade"; export function convertToInt(value): number { let normalizedValue; @@ -13,4 +13,4 @@ export function convertToInt(value): number { } } return Math.round(normalizedValue); -} \ No newline at end of file +} diff --git a/nativescript-angular/directives.ts b/nativescript-angular/directives.ts index 5d76b429b..92d0800ae 100644 --- a/nativescript-angular/directives.ts +++ b/nativescript-angular/directives.ts @@ -1,11 +1,9 @@ -import {Type} from '@angular/core/src/facade/lang'; import {ListViewComponent, SetupItemViewArgs} from './directives/list-view-comp'; import {TabViewDirective, TabViewItemDirective} from './directives/tab-view'; import {ActionBarComponent, ActionBarScope, ActionItemDirective, NavigationButtonDirective} from './directives/action-bar'; import {AndroidFilterComponent, IosFilterComponent} from './directives/platform-filters'; - -export const NS_DIRECTIVES: Type[] = [ +export const NS_DIRECTIVES = [ ListViewComponent, TabViewDirective, TabViewItemDirective, @@ -14,7 +12,7 @@ export const NS_DIRECTIVES: Type[] = [ ActionItemDirective, NavigationButtonDirective, AndroidFilterComponent, - IosFilterComponent + IosFilterComponent, ]; export {ListViewComponent, SetupItemViewArgs} from './directives/list-view-comp'; diff --git a/nativescript-angular/directives/action-bar.ts b/nativescript-angular/directives/action-bar.ts index fa97a0a95..f798eb1ef 100644 --- a/nativescript-angular/directives/action-bar.ts +++ b/nativescript-angular/directives/action-bar.ts @@ -1,6 +1,6 @@ import {Directive, Component, ContentChildren, ElementRef, Optional} from '@angular/core'; import {ActionItem, ActionBar, NavigationButton} from "ui/action-bar"; -import {isBlank} from "@angular/core/src/facade/lang"; +import {isBlank} from "../lang-facade"; import {Page} from "ui/page"; import {View} from 'ui/core/view'; import {registerElement, ViewClassMeta, NgView } from '../element-registry'; diff --git a/nativescript-angular/directives/dialogs.ts b/nativescript-angular/directives/dialogs.ts index 7d6438414..c31465c1a 100644 --- a/nativescript-angular/directives/dialogs.ts +++ b/nativescript-angular/directives/dialogs.ts @@ -1,4 +1,7 @@ -import {ReflectiveInjector, ComponentResolver, ViewContainerRef, Injector, provide, Type, Injectable, ComponentRef, Directive} from '@angular/core'; +import { + ReflectiveInjector, ComponentFactoryResolver, ViewContainerRef, + Injector, Type, Injectable, ComponentRef, Directive +} from '@angular/core'; import {Page} from 'ui/page'; import {View} from 'ui/core/view'; import {DetachedLoader} from '../common/detached-loader'; @@ -21,48 +24,50 @@ export class ModalDialogService { constructor( private page: Page, - private compiler: ComponentResolver) { + private resolver: ComponentFactoryResolver) { } public registerViewContainerRef(ref: ViewContainerRef) { this.containerRef = ref; } - public showModal(type: Type, options: ModalDialogOptions): Promise { + public showModal(type: Type, options: ModalDialogOptions): Promise { if (!this.containerRef) { throw new Error("No viewContainerRef: Make sure you have the modal-dialog-host directive inside your component."); } - return new Promise((resove, reject) => { - const page = new Page(); + return new Promise((resolve, reject) => { + setTimeout(() => this.showDialog(type, options, resolve), 10); + }); + } - var detachedLoaderRef: ComponentRef; - const closeCallback = (...args) => { - resove.apply(undefined, args); - page.closeModal(); - detachedLoaderRef.destroy(); - } - const modalParams = new ModalDialogParams(options.context, closeCallback); + private showDialog(type: Type, options: ModalDialogOptions, doneCallback): void { + const page = new Page(); - const providers = ReflectiveInjector.resolve([ - provide(Page, { useValue: page }), - provide(ModalDialogParams, { useValue: modalParams }), - ]); + var detachedLoaderRef: ComponentRef; + const closeCallback = (...args) => { + doneCallback.apply(undefined, args); + page.closeModal(); + detachedLoaderRef.destroy(); + } + const modalParams = new ModalDialogParams(options.context, closeCallback); + + const providers = ReflectiveInjector.resolve([ + {provide: Page, useValue: page }, + {provide: ModalDialogParams, useValue: modalParams }, + ]); - this.compiler.resolveComponent(DetachedLoader).then((detachedFactory) => { - const childInjector = ReflectiveInjector.fromResolvedProviders(providers, this.containerRef.parentInjector); - detachedLoaderRef = this.containerRef.createComponent(detachedFactory, -1, childInjector, null) - - detachedLoaderRef.instance.loadComponent(type).then((compRef) => { - const componentView = compRef.location.nativeElement; - - if (componentView.parent) { - (componentView.parent).removeChild(componentView); - } - - page.content = componentView; - this.page.showModal(page, options.context, closeCallback, options.fullscreen); - }); - }); + const childInjector = ReflectiveInjector.fromResolvedProviders(providers, this.containerRef.parentInjector); + const detachedFactory = this.resolver.resolveComponentFactory(DetachedLoader); + detachedLoaderRef = this.containerRef.createComponent(detachedFactory, -1, childInjector, null) + detachedLoaderRef.instance.loadComponent(type).then((compRef) => { + const componentView = compRef.location.nativeElement; + + if (componentView.parent) { + (componentView.parent).removeChild(componentView); + } + + page.content = componentView; + this.page.showModal(page, options.context, closeCallback, options.fullscreen); }); } } diff --git a/nativescript-angular/directives/list-view-comp.ts b/nativescript-angular/directives/list-view-comp.ts index 613e1bc9e..c108c3ffd 100644 --- a/nativescript-angular/directives/list-view-comp.ts +++ b/nativescript-angular/directives/list-view-comp.ts @@ -14,8 +14,8 @@ import { ViewChild, Output, ChangeDetectionStrategy } from '@angular/core'; -import {isBlank} from '@angular/core/src/facade/lang'; -import {isListLikeIterable} from '@angular/core/src/facade/collection'; +import {isBlank} from "../lang-facade"; +import {isListLikeIterable} from "../collection-facade"; import {ListView} from 'ui/list-view'; import {View} from 'ui/core/view'; import {ObservableArray} from 'data/observable-array'; @@ -39,6 +39,7 @@ export interface SetupItemViewArgs { view: EmbeddedViewRef; data: any; index: number; + context: ListItemContext; } @Component({ diff --git a/nativescript-angular/directives/tab-view.ts b/nativescript-angular/directives/tab-view.ts index 9e5cbc054..2f8689841 100644 --- a/nativescript-angular/directives/tab-view.ts +++ b/nativescript-angular/directives/tab-view.ts @@ -2,7 +2,7 @@ import {ElementRef, Directive, Input, TemplateRef, ViewContainerRef} from "@angu import {TabView, TabViewItem} from "ui/tab-view"; import * as utils from '../common/utils'; import {rendererLog, rendererError} from "../trace"; -import {isBlank} from '@angular/core/src/facade/lang'; +import {isBlank} from "../lang-facade"; @Directive({ selector: 'TabView', @@ -96,4 +96,4 @@ export class TabViewItemDirective { this.owner.tabView.items = newItems; } } -} \ No newline at end of file +} diff --git a/nativescript-angular/dom-adapter.ts b/nativescript-angular/dom-adapter.ts index fae2069b0..29b80f28e 100644 --- a/nativescript-angular/dom-adapter.ts +++ b/nativescript-angular/dom-adapter.ts @@ -1,9 +1,9 @@ -import {ElementSchemaRegistry} from '@angular/compiler'; -import {SanitizationService} from '@angular/core/src/security'; -import {Parse5DomAdapter} from '@angular/platform-server/src/parse5_adapter'; -import {setRootDomAdapter} from '@angular/platform-browser/src/dom/dom_adapter'; -import {Type} from '@angular/core/src/facade/lang'; -import {rendererLog, rendererError} from "./trace"; +import { ElementSchemaRegistry } from '@angular/compiler'; +import { Sanitizer, SchemaMetadata } from '@angular/core'; +import { Parse5DomAdapter } from "./parse5_adapter"; +import { setRootDomAdapter } from './private_import_platform-browser'; +import { rendererLog, rendererError } from "./trace"; +import { print } from "./lang-facade"; export enum SecurityContext { NONE, @@ -19,6 +19,11 @@ export class NativeScriptElementSchemaRegistry extends ElementSchemaRegistry { return true; } + hasElement(tagName: string, schemaMetas: SchemaMetadata[]): boolean { + return true; + } + + getMappedPropName(propName: string): string { return propName; } @@ -32,7 +37,7 @@ export class NativeScriptElementSchemaRegistry extends ElementSchemaRegistry { } } -export class NativeScriptSanitizationService extends SanitizationService { +export class NativeScriptSanitizer extends Sanitizer { sanitize(context: SecurityContext, value: string): string { return value; } @@ -44,12 +49,23 @@ export class NativeScriptDomAdapter extends Parse5DomAdapter { setRootDomAdapter(new NativeScriptDomAdapter()); } - getXHR(): Type { - return null; - } - hasProperty(element, name: string) { //TODO: actually check if the property exists. return true; } + + log(arg: any): void { + print(arg); + } + + logError(arg: any): void { + print(arg); + } + + logGroup(arg: any): void { + print(arg); + } + + logGroupEnd(): void { + } } diff --git a/nativescript-angular/forms.ts b/nativescript-angular/forms.ts index 690e19780..aa5204ff8 100644 --- a/nativescript-angular/forms.ts +++ b/nativescript-angular/forms.ts @@ -1,4 +1,3 @@ -import { Type } from '@angular/core/src/facade/lang'; import { NgModule } from "@angular/core"; import { FormsModule } from "@angular/forms"; import { TextValueAccessor } from './value-accessors/text-value-accessor'; @@ -8,7 +7,7 @@ import { TimeValueAccessor } from './value-accessors/time-value-accessor'; import { NumberValueAccessor } from './value-accessors/number-value-accessor'; import { SelectedIndexValueAccessor } from './value-accessors/selectedIndex-value-accessor'; -export const FORMS_DIRECTIVES: Type[] = [ +export const FORMS_DIRECTIVES = [ TextValueAccessor, CheckedValueAccessor, DateValueAccessor, diff --git a/nativescript-angular/http.ts b/nativescript-angular/http.ts index ad3accd48..557d05953 100644 --- a/nativescript-angular/http.ts +++ b/nativescript-angular/http.ts @@ -1 +1,28 @@ -export * from './http/ns-http'; \ No newline at end of file +import { + Http, XHRBackend, RequestOptions +} from '@angular/http'; +import { NSXSRFStrategy, NSHttp } from "./http/ns-http"; +import { NSFileSystem } from "./file-system/ns-file-system"; + +import { NgModule } from "@angular/core"; +import { HttpModule, XSRFStrategy } from "@angular/http"; + +@NgModule({ + providers: [ + { provide: XSRFStrategy, useValue: new NSXSRFStrategy() }, + NSFileSystem, + { + provide: Http, useFactory: (backend, options, nsFileSystem) => { + return new NSHttp(backend, options, nsFileSystem); + }, deps: [XHRBackend, RequestOptions, NSFileSystem] + } + ], + imports: [ + HttpModule, + ], + exports: [ + HttpModule, + ] +}) +export class NativeScriptHttpModule { +} diff --git a/nativescript-angular/http/ns-http.ts b/nativescript-angular/http/ns-http.ts index 59c5cdac9..ef542a5e9 100644 --- a/nativescript-angular/http/ns-http.ts +++ b/nativescript-angular/http/ns-http.ts @@ -1,5 +1,5 @@ import {Injectable} from '@angular/core'; -import {HTTP_PROVIDERS, Http, XHRBackend, ConnectionBackend, RequestOptions, RequestOptionsArgs, ResponseOptions, ResponseType, Response, XSRFStrategy} from '@angular/http'; +import {Http, XHRBackend, ConnectionBackend, RequestOptions, RequestOptionsArgs, ResponseOptions, ResponseType, Response, XSRFStrategy} from '@angular/http'; import {Observable} from 'rxjs/Observable'; import 'rxjs/add/observable/fromPromise'; import {NSFileSystem} from '../file-system/ns-file-system'; @@ -53,15 +53,3 @@ function responseOptions(body: string | Object, status: number, url: string): Re url: url })); } - -export const NS_HTTP_PROVIDERS: any[] = [ - HTTP_PROVIDERS, - { provide: XSRFStrategy, useValue: new NSXSRFStrategy() }, - NSFileSystem, - { - provide: Http, useFactory: (backend, options, nsFileSystem) => { - return new NSHttp(backend, options, nsFileSystem); - }, deps: [XHRBackend, RequestOptions, NSFileSystem] - } -]; - diff --git a/nativescript-angular/lang-facade.ts b/nativescript-angular/lang-facade.ts new file mode 100644 index 000000000..fabf8883d --- /dev/null +++ b/nativescript-angular/lang-facade.ts @@ -0,0 +1,83 @@ +//Copied unexported functions from @angular/core/src/facade/lang +var globalScope = global; +export var global = globalScope; + +export function isPresent(obj: any): boolean { + return obj !== undefined && obj !== null; +} + +export function isBlank(obj: any): boolean { + return obj === undefined || obj === null; +} + +export function isNumber(obj: any): boolean { + return typeof obj === 'number'; +} + +export function isDate(obj: any): obj is Date { + return obj instanceof Date && !isNaN(obj.valueOf()); +} + +export function print(obj: Error | Object) { + console.log(obj); +} + +export function isJsObject(o: any): boolean { + return o !== null && (typeof o === 'function' || typeof o === 'object'); +} + +export function isArray(obj: any): boolean { + return Array.isArray(obj); +} + +// When Symbol.iterator doesn't exist, retrieves the key used in es6-shim +declare var Symbol: any; +var _symbolIterator: any = null; +export function getSymbolIterator(): string|symbol { + if (isBlank(_symbolIterator)) { + if (isPresent((globalScope).Symbol) && isPresent(Symbol.iterator)) { + _symbolIterator = Symbol.iterator; + } else { + // es6-shim specific logic + var keys = Object.getOwnPropertyNames(Map.prototype); + for (var i = 0; i < keys.length; ++i) { + var key = keys[i]; + if (key !== 'entries' && key !== 'size' && + (Map as any).prototype[key] === Map.prototype['entries']) { + _symbolIterator = key; + } + } + } + } + return _symbolIterator; +} + +export class DateWrapper { + static create( + year: number, month: number = 1, day: number = 1, hour: number = 0, minutes: number = 0, + seconds: number = 0, milliseconds: number = 0): Date { + return new Date(year, month - 1, day, hour, minutes, seconds, milliseconds); + } + static fromISOString(str: string): Date { return new Date(str); } + static fromMillis(ms: number): Date { return new Date(ms); } + static toMillis(date: Date): number { return date.getTime(); } + static now(): Date { return new Date(); } + static toJson(date: Date): string { return date.toJSON(); } +} + +export function setValueOnPath(global: any, path: string, value: any) { + var parts = path.split('.'); + var obj: any = global; + while (parts.length > 1) { + var name = parts.shift(); + if (obj.hasOwnProperty(name) && isPresent(obj[name])) { + obj = obj[name]; + } else { + obj = obj[name] = {}; + } + } + if (obj === undefined || obj === null) { + obj = {}; + } + obj[parts.shift()] = value; +} diff --git a/nativescript-angular/package.json b/nativescript-angular/package.json index 2c92ddeff..74482bbbe 100644 --- a/nativescript-angular/package.json +++ b/nativescript-angular/package.json @@ -1,7 +1,7 @@ { "private": true, "name": "nativescript-angular", - "version": "0.4.1", + "version": "0.5.0", "description": "An Angular 2 renderer that lets you build mobile apps with NativeScript.", "homepage": "http://www.telerik.com", "bugs": "http://www.telerik.com", @@ -19,19 +19,19 @@ }, "dependencies": { "nativescript-intl": "^0.0.4", - "@angular/core": "2.0.0-rc.5", - "@angular/common": "2.0.0-rc.5", - "@angular/compiler": "2.0.0-rc.5", - "@angular/http": "2.0.0-rc.5", - "@angular/platform-browser": "2.0.0-rc.5", - "@angular/platform-browser-dynamic": "2.0.0-rc.5", - "@angular/platform-server": "2.0.0-rc.5", - "@angular/forms": "0.3.0", - "@angular/router": "3.0.0-rc.1", - "rxjs": "5.0.0-beta.6", - "zone.js": "^0.6.12", - "reflect-metadata": "^0.1.3", - "parse5": "1.4.2", + "@angular/core": "2.0.0-rc.6", + "@angular/common": "2.0.0-rc.6", + "@angular/compiler": "2.0.0-rc.6", + "@angular/http": "2.0.0-rc.6", + "@angular/platform-browser": "2.0.0-rc.6", + "@angular/platform-browser-dynamic": "2.0.0-rc.6", + "@angular/platform-server": "2.0.0-rc.6", + "@angular/forms": "2.0.0-rc.6", + "@angular/router": "3.0.0-rc.2", + "rxjs": "5.0.0-beta.11", + "zone.js": "^0.6.17", + "reflect-metadata": "^0.1.8", + "parse5": "1.3.2", "punycode": "1.3.2", "querystring": "0.2.0", "url": "0.10.3" diff --git a/nativescript-angular/parse5_adapter.ts b/nativescript-angular/parse5_adapter.ts new file mode 100644 index 000000000..e6f560996 --- /dev/null +++ b/nativescript-angular/parse5_adapter.ts @@ -0,0 +1,811 @@ +/** + * @license + * Copyright Google Inc. All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ + +var parse5 = require('parse5/index'); + +import {ListWrapper, StringMapWrapper} from './collection-facade'; +import {DomAdapter, setRootDomAdapter} from './private_import_platform-browser'; +import {isPresent, isBlank, global, setValueOnPath, DateWrapper} from "./lang-facade"; +import {SelectorMatcher, CssSelector} from './private_import_compiler'; +import {Type} from '@angular/core'; +import {ResourceLoader} from '@angular/compiler'; + +var parser: any /** TODO #9100 */ = null; +var serializer: any /** TODO #9100 */ = null; +var treeAdapter: any /** TODO #9100 */ = null; + +var _attrToPropMap: {[key: string]: string} = { + 'class': 'className', + 'innerHtml': 'innerHTML', + 'readonly': 'readOnly', + 'tabindex': 'tabIndex', +}; +var defDoc: any /** TODO #9100 */ = null; + +var mapProps = ['attribs', 'x-attribsNamespace', 'x-attribsPrefix']; + +function _notImplemented(methodName: any /** TODO #9100 */) { + return new Error('This method is not implemented in Parse5DomAdapter: ' + methodName); +} + +/* tslint:disable:requireParameterType */ +/** + * A `DomAdapter` powered by the `parse5` NodeJS module. + * + * @security Tread carefully! Interacting with the DOM directly is dangerous and + * can introduce XSS risks. + */ +export class Parse5DomAdapter extends DomAdapter { + static makeCurrent() { + parser = new parse5.Parser(parse5.TreeAdapters.htmlparser2); + serializer = new parse5.Serializer(parse5.TreeAdapters.htmlparser2); + treeAdapter = parser.treeAdapter; + setRootDomAdapter(new Parse5DomAdapter()); + } + + hasProperty(element: any /** TODO #9100 */, name: string): boolean { + return _HTMLElementPropertyList.indexOf(name) > -1; + } + // TODO(tbosch): don't even call this method when we run the tests on server side + // by not using the DomRenderer in tests. Keeping this for now to make tests happy... + setProperty(el: /*element*/ any, name: string, value: any) { + if (name === 'innerHTML') { + this.setInnerHTML(el, value); + } else if (name === 'className') { + el.attribs['class'] = el.className = value; + } else { + el[name] = value; + } + } + // TODO(tbosch): don't even call this method when we run the tests on server side + // by not using the DomRenderer in tests. Keeping this for now to make tests happy... + getProperty(el: /*element*/ any, name: string): any { return el[name]; } + + logError(error: any /** TODO #9100 */) { console.error(error); } + + log(error: any /** TODO #9100 */) { console.log(error); } + + logGroup(error: any /** TODO #9100 */) { console.error(error); } + + logGroupEnd() {} + + get attrToPropMap() { return _attrToPropMap; } + + query(selector: any /** TODO #9100 */) { throw _notImplemented('query'); } + querySelector(el: any /** TODO #9100 */, selector: string): any { + return this.querySelectorAll(el, selector)[0]; + } + querySelectorAll(el: any /** TODO #9100 */, selector: string): any[] { + var res: any[] /** TODO #9100 */ = []; + var _recursive = + (result: any /** TODO #9100 */, node: any /** TODO #9100 */, + selector: any /** TODO #9100 */, matcher: any /** TODO #9100 */) => { + var cNodes = node.childNodes; + if (cNodes && cNodes.length > 0) { + for (var i = 0; i < cNodes.length; i++) { + var childNode = cNodes[i]; + if (this.elementMatches(childNode, selector, matcher)) { + result.push(childNode); + } + _recursive(result, childNode, selector, matcher); + } + } + }; + var matcher = new SelectorMatcher(); + matcher.addSelectables(CssSelector.parse(selector)); + _recursive(res, el, selector, matcher); + return res; + } + elementMatches( + node: any /** TODO #9100 */, selector: string, + matcher: any /** TODO #9100 */ = null): boolean { + if (this.isElementNode(node) && selector === '*') { + return true; + } + var result = false; + if (selector && selector.charAt(0) == '#') { + result = this.getAttribute(node, 'id') == selector.substring(1); + } else if (selector) { + var result = false; + if (matcher == null) { + matcher = new SelectorMatcher(); + matcher.addSelectables(CssSelector.parse(selector)); + } + + var cssSelector = new CssSelector(); + cssSelector.setElement(this.tagName(node)); + if (node.attribs) { + for (var attrName in node.attribs) { + cssSelector.addAttribute(attrName, node.attribs[attrName]); + } + } + var classList = this.classList(node); + for (var i = 0; i < classList.length; i++) { + cssSelector.addClassName(classList[i]); + } + + matcher.match( + cssSelector, + function(selector: any /** TODO #9100 */, cb: any /** TODO #9100 */) { result = true; }); + } + return result; + } + on(el: any /** TODO #9100 */, evt: any /** TODO #9100 */, listener: any /** TODO #9100 */) { + var listenersMap: {[k: /*any*/ string]: any} = el._eventListenersMap; + if (isBlank(listenersMap)) { + var listenersMap: {[k: /*any*/ string]: any} = StringMapWrapper.create(); + el._eventListenersMap = listenersMap; + } + var listeners = StringMapWrapper.get(listenersMap, evt); + if (isBlank(listeners)) { + listeners = []; + } + listeners.push(listener); + StringMapWrapper.set(listenersMap, evt, listeners); + } + onAndCancel( + el: any /** TODO #9100 */, evt: any /** TODO #9100 */, + listener: any /** TODO #9100 */): Function { + this.on(el, evt, listener); + return () => { + ListWrapper.remove(StringMapWrapper.get(el._eventListenersMap, evt), listener); + }; + } + dispatchEvent(el: any /** TODO #9100 */, evt: any /** TODO #9100 */) { + if (isBlank(evt.target)) { + evt.target = el; + } + if (isPresent(el._eventListenersMap)) { + var listeners: any = StringMapWrapper.get(el._eventListenersMap, evt.type); + if (isPresent(listeners)) { + for (var i = 0; i < listeners.length; i++) { + listeners[i](evt); + } + } + } + if (isPresent(el.parent)) { + this.dispatchEvent(el.parent, evt); + } + if (isPresent(el._window)) { + this.dispatchEvent(el._window, evt); + } + } + createMouseEvent(eventType: any /** TODO #9100 */): Event { return this.createEvent(eventType); } + createEvent(eventType: string): Event { + var evt = { + type: eventType, + defaultPrevented: false, + preventDefault: () => { (evt).defaultPrevented = true; } + }; + return evt; + } + preventDefault(evt: any /** TODO #9100 */) { evt.returnValue = false; } + isPrevented(evt: any /** TODO #9100 */): boolean { + return isPresent(evt.returnValue) && !evt.returnValue; + } + getInnerHTML(el: any /** TODO #9100 */): string { + return serializer.serialize(this.templateAwareRoot(el)); + } + getTemplateContent(el: any /** TODO #9100 */): Node { + return null; // no