diff --git a/modules/@angular/common/src/directives/ng_class.ts b/modules/@angular/common/src/directives/ng_class.ts index 725965fe12..03d987f8bc 100644 --- a/modules/@angular/common/src/directives/ng_class.ts +++ b/modules/@angular/common/src/directives/ng_class.ts @@ -6,9 +6,7 @@ * found in the LICENSE file at https://angular.io/license */ -import {Directive, DoCheck, ElementRef, Input, IterableChanges, IterableDiffer, IterableDiffers, KeyValueChanges, KeyValueDiffer, KeyValueDiffers, Renderer} from '@angular/core'; - -import {isListLikeIterable} from '../facade/collection'; +import {Directive, DoCheck, ElementRef, Input, IterableChanges, IterableDiffer, IterableDiffers, KeyValueChanges, KeyValueDiffer, KeyValueDiffers, Renderer, ɵisListLikeIterable as isListLikeIterable} from '@angular/core'; import {stringify} from '../facade/lang'; /** diff --git a/modules/@angular/compiler/src/aot/compiler.ts b/modules/@angular/compiler/src/aot/compiler.ts index 0e41d6e7d0..d88dbbdf84 100644 --- a/modules/@angular/compiler/src/aot/compiler.ts +++ b/modules/@angular/compiler/src/aot/compiler.ts @@ -6,9 +6,8 @@ * found in the LICENSE file at https://angular.io/license */ -import {CompileDirectiveMetadata, CompileIdentifierMetadata, CompileNgModuleMetadata, CompileProviderMetadata, componentFactoryName, createHostComponentMeta, identifierName} from '../compile_metadata'; +import {CompileDirectiveMetadata, CompileIdentifierMetadata, CompileNgModuleMetadata, CompileProviderMetadata, componentFactoryName, createHostComponentMeta, flatten, identifierName} from '../compile_metadata'; import {CompilerConfig} from '../config'; -import {ListWrapper} from '../facade/collection'; import {Identifiers, createIdentifier, createIdentifierToken} from '../identifiers'; import {CompileMetadataResolver} from '../metadata_resolver'; import {NgModuleCompiler} from '../ng_module_compiler'; @@ -51,7 +50,7 @@ export class AotCompiler { file => this._compileSrcFile( file.srcUrl, ngModuleByPipeOrDirective, file.directives, file.pipes, file.ngModules, file.injectables)); - return ListWrapper.flatten(sourceModules); + return flatten(sourceModules); }); } diff --git a/modules/@angular/compiler/src/compile_metadata.ts b/modules/@angular/compiler/src/compile_metadata.ts index 913255b659..aae128529a 100644 --- a/modules/@angular/compiler/src/compile_metadata.ts +++ b/modules/@angular/compiler/src/compile_metadata.ts @@ -7,9 +7,7 @@ */ import {ChangeDetectionStrategy, ComponentFactory, RendererTypeV2, SchemaMetadata, Type, ViewEncapsulation, ɵLifecycleHooks, ɵreflector} from '@angular/core'; - import {StaticSymbol} from './aot/static_symbol'; -import {ListWrapper} from './facade/collection'; import {isPresent, stringify} from './facade/lang'; import {CssSelector} from './selector'; import {splitAtColon} from './util'; @@ -272,7 +270,7 @@ export class CompileTemplateMetadata { this.styles = _normalizeArray(styles); this.styleUrls = _normalizeArray(styleUrls); this.externalStylesheets = _normalizeArray(externalStylesheets); - this.animations = animations ? ListWrapper.flatten(animations) : []; + this.animations = animations ? flatten(animations) : []; this.ngContentSelectors = ngContentSelectors || []; if (interpolation && interpolation.length != 2) { throw new Error(`'interpolation' should have a start and an end symbol.`); @@ -735,3 +733,10 @@ export class ProviderMeta { this.multi = !!multi; } } + +export function flatten(list: Array): T[] { + return list.reduce((flat: any[], item: T | T[]): T[] => { + const flatItem = Array.isArray(item) ? flatten(item) : item; + return (flat).concat(flatItem); + }, []); +} diff --git a/modules/@angular/compiler/src/directive_resolver.ts b/modules/@angular/compiler/src/directive_resolver.ts index 4e5e0f6077..2df95423ac 100644 --- a/modules/@angular/compiler/src/directive_resolver.ts +++ b/modules/@angular/compiler/src/directive_resolver.ts @@ -6,13 +6,13 @@ * found in the LICENSE file at https://angular.io/license */ -import {Component, Directive, HostBinding, HostListener, Input, Output, Query, Type, resolveForwardRef, ɵReflectorReader, ɵreflector} from '@angular/core'; +import {Component, Directive, HostBinding, HostListener, Input, Output, Query, Type, resolveForwardRef, ɵReflectorReader, ɵmerge as merge, ɵreflector} from '@angular/core'; -import {ListWrapper, StringMapWrapper} from './facade/collection'; import {stringify} from './facade/lang'; import {CompilerInjectable} from './injectable'; import {splitAtColon} from './util'; + /* * Resolve a `Type` for {@link Directive}. * @@ -35,7 +35,7 @@ export class DirectiveResolver { resolve(type: Type, throwIfNotFound = true): Directive { const typeMetadata = this._reflector.annotations(resolveForwardRef(type)); if (typeMetadata) { - const metadata = ListWrapper.findLast(typeMetadata, isDirectiveMetadata); + const metadata = findLast(typeMetadata, isDirectiveMetadata); if (metadata) { const propertyMetadata = this._reflector.propMetadata(type); return this._mergeWithPropertyMetadata(metadata, propertyMetadata, type); @@ -58,7 +58,7 @@ export class DirectiveResolver { const queries: {[key: string]: any} = {}; Object.keys(propertyMetadata).forEach((propName: string) => { - const input = ListWrapper.findLast(propertyMetadata[propName], (a) => a instanceof Input); + const input = findLast(propertyMetadata[propName], (a) => a instanceof Input); if (input) { if (input.bindingPropertyName) { inputs.push(`${propName}: ${input.bindingPropertyName}`); @@ -66,7 +66,7 @@ export class DirectiveResolver { inputs.push(propName); } } - const output = ListWrapper.findLast(propertyMetadata[propName], (a) => a instanceof Output); + const output = findLast(propertyMetadata[propName], (a) => a instanceof Output); if (output) { if (output.bindingPropertyName) { outputs.push(`${propName}: ${output.bindingPropertyName}`); @@ -94,7 +94,7 @@ export class DirectiveResolver { const args = hostListener.args || []; host[`(${hostListener.eventName})`] = `${propName}(${args.join(',')})`; }); - const query = ListWrapper.findLast(propertyMetadata[propName], (a) => a instanceof Query); + const query = findLast(propertyMetadata[propName], (a) => a instanceof Query); if (query) { queries[propName] = query; } @@ -126,9 +126,8 @@ export class DirectiveResolver { this._dedupeBindings(directive.inputs ? directive.inputs.concat(inputs) : inputs); const mergedOutputs = this._dedupeBindings(directive.outputs ? directive.outputs.concat(outputs) : outputs); - const mergedHost = directive.host ? StringMapWrapper.merge(directive.host, host) : host; - const mergedQueries = - directive.queries ? StringMapWrapper.merge(directive.queries, queries) : queries; + const mergedHost = directive.host ? merge(directive.host, host) : host; + const mergedQueries = directive.queries ? merge(directive.queries, queries) : queries; if (directive instanceof Component) { return new Component({ @@ -168,3 +167,12 @@ export class DirectiveResolver { function isDirectiveMetadata(type: any): type is Directive { return type instanceof Directive; } + +export function findLast(arr: T[], condition: (value: T) => boolean): T { + for (let i = arr.length - 1; i >= 0; i--) { + if (condition(arr[i])) { + return arr[i]; + } + } + return null; +} diff --git a/modules/@angular/compiler/src/ng_module_resolver.ts b/modules/@angular/compiler/src/ng_module_resolver.ts index 85dca86449..3424a86ab4 100644 --- a/modules/@angular/compiler/src/ng_module_resolver.ts +++ b/modules/@angular/compiler/src/ng_module_resolver.ts @@ -7,8 +7,7 @@ */ import {NgModule, Type, ɵReflectorReader, ɵreflector} from '@angular/core'; - -import {ListWrapper} from './facade/collection'; +import {findLast} from './directive_resolver'; import {stringify} from './facade/lang'; import {CompilerInjectable} from './injectable'; @@ -26,8 +25,7 @@ export class NgModuleResolver { isNgModule(type: any) { return this._reflector.annotations(type).some(_isNgModuleMetadata); } resolve(type: Type, throwIfNotFound = true): NgModule { - const ngModuleMeta: NgModule = - ListWrapper.findLast(this._reflector.annotations(type), _isNgModuleMetadata); + const ngModuleMeta: NgModule = findLast(this._reflector.annotations(type), _isNgModuleMetadata); if (ngModuleMeta) { return ngModuleMeta; diff --git a/modules/@angular/compiler/src/pipe_resolver.ts b/modules/@angular/compiler/src/pipe_resolver.ts index 140e4f845f..bb2f83d5d1 100644 --- a/modules/@angular/compiler/src/pipe_resolver.ts +++ b/modules/@angular/compiler/src/pipe_resolver.ts @@ -7,8 +7,7 @@ */ import {Pipe, Type, resolveForwardRef, ɵReflectorReader, ɵreflector} from '@angular/core'; - -import {ListWrapper} from './facade/collection'; +import {findLast} from './directive_resolver'; import {stringify} from './facade/lang'; import {CompilerInjectable} from './injectable'; @@ -38,7 +37,7 @@ export class PipeResolver { resolve(type: Type, throwIfNotFound = true): Pipe { const metas = this._reflector.annotations(resolveForwardRef(type)); if (metas) { - const annotation = ListWrapper.findLast(metas, _isPipeMetadata); + const annotation = findLast(metas, _isPipeMetadata); if (annotation) { return annotation; } diff --git a/modules/@angular/compiler/testing/resource_loader_mock.ts b/modules/@angular/compiler/testing/resource_loader_mock.ts index 1e25192234..78d53d6829 100644 --- a/modules/@angular/compiler/testing/resource_loader_mock.ts +++ b/modules/@angular/compiler/testing/resource_loader_mock.ts @@ -7,7 +7,6 @@ */ import {ResourceLoader} from '@angular/compiler'; -import {ListWrapper} from './facade/collection'; import {isBlank} from './facade/lang'; /** @@ -82,7 +81,7 @@ export class MockResourceLoader extends ResourceLoader { if (this._expectations.length > 0) { const expectation = this._expectations[0]; if (expectation.url == url) { - ListWrapper.remove(this._expectations, expectation); + remove(this._expectations, expectation); request.complete(expectation.response); return; } @@ -129,3 +128,10 @@ class _Expectation { this.response = response; } } + +function remove(list: T[], el: T): void { + const index = list.indexOf(el); + if (index > -1) { + list.splice(index, 1); + } +} diff --git a/modules/@angular/core/src/application_ref.ts b/modules/@angular/core/src/application_ref.ts index b77168f29d..f1a287f3f9 100644 --- a/modules/@angular/core/src/application_ref.ts +++ b/modules/@angular/core/src/application_ref.ts @@ -14,7 +14,6 @@ import {merge} from 'rxjs/observable/merge'; import {share} from 'rxjs/operator/share'; import {ErrorHandler} from '../src/error_handler'; -import {ListWrapper} from '../src/facade/collection'; import {scheduleMicroTask, stringify} from '../src/facade/lang'; import {isPromise} from '../src/util/lang'; @@ -300,7 +299,7 @@ export class PlatformRef_ extends PlatformRef { if (!exceptionHandler) { throw new Error('No ErrorHandler. Is platform module (BrowserModule) included?'); } - moduleRef.onDestroy(() => ListWrapper.remove(this._modules, moduleRef)); + moduleRef.onDestroy(() => remove(this._modules, moduleRef)); ngZone.onError.subscribe({next: (error: any) => { exceptionHandler.handleError(error); }}); return _callAndReportToErrorHandler(exceptionHandler, () => { const initStatus: ApplicationInitStatus = moduleRef.injector.get(ApplicationInitStatus); @@ -489,7 +488,7 @@ export class ApplicationRef_ extends ApplicationRef { detachView(viewRef: ViewRef): void { const view = (viewRef as InternalViewRef); - ListWrapper.remove(this._views, view); + remove(this._views, view); view.detachFromAppRef(); } @@ -533,7 +532,7 @@ export class ApplicationRef_ extends ApplicationRef { private _unloadComponent(componentRef: ComponentRef): void { this.detachView(componentRef.hostView); - ListWrapper.remove(this._rootComponents, componentRef); + remove(this._rootComponents, componentRef); } tick(): void { @@ -567,3 +566,10 @@ export class ApplicationRef_ extends ApplicationRef { get isStable(): Observable { return this._isStable; } } + +function remove(list: T[], el: T): void { + const index = list.indexOf(el); + if (index > -1) { + list.splice(index, 1); + } +} diff --git a/modules/@angular/core/src/change_detection/change_detection_util.ts b/modules/@angular/core/src/change_detection/change_detection_util.ts index 98ba03245c..5f77fe19e9 100644 --- a/modules/@angular/core/src/change_detection/change_detection_util.ts +++ b/modules/@angular/core/src/change_detection/change_detection_util.ts @@ -6,8 +6,7 @@ * found in the LICENSE file at https://angular.io/license */ -import {areIterablesEqual, isListLikeIterable} from '../facade/collection'; -import {isPrimitive, looseIdentical} from '../facade/lang'; +import {getSymbolIterator, isJsObject, isPrimitive, looseIdentical} from '../facade/lang'; export {looseIdentical} from '../facade/lang'; @@ -78,3 +77,38 @@ export class SimpleChange { */ isFirstChange(): boolean { return this.firstChange; } } + +export function isListLikeIterable(obj: any): boolean { + if (!isJsObject(obj)) return false; + return Array.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 function areIterablesEqual( + a: any, b: any, comparator: (a: any, b: any) => boolean): boolean { + const iterator1 = a[getSymbolIterator()](); + const iterator2 = b[getSymbolIterator()](); + + while (true) { + const item1 = iterator1.next(); + const item2 = iterator2.next(); + if (item1.done && item2.done) return true; + if (item1.done || item2.done) return false; + if (!comparator(item1.value, item2.value)) return false; + } +} + +export function iterateListLike(obj: any, fn: (p: any) => any) { + if (Array.isArray(obj)) { + for (let i = 0; i < obj.length; i++) { + fn(obj[i]); + } + } else { + const iterator = obj[getSymbolIterator()](); + let item: any; + while (!((item = iterator.next()).done)) { + fn(item.value); + } + } +} diff --git a/modules/@angular/core/src/change_detection/differs/default_iterable_differ.ts b/modules/@angular/core/src/change_detection/differs/default_iterable_differ.ts index e76592154f..bfc35a6497 100644 --- a/modules/@angular/core/src/change_detection/differs/default_iterable_differ.ts +++ b/modules/@angular/core/src/change_detection/differs/default_iterable_differ.ts @@ -6,8 +6,8 @@ * found in the LICENSE file at https://angular.io/license */ -import {isListLikeIterable, iterateListLike} from '../../facade/collection'; import {isBlank, looseIdentical, stringify} from '../../facade/lang'; +import {isListLikeIterable, iterateListLike} from '../change_detection_util'; import {ChangeDetectorRef} from '../change_detector_ref'; import {IterableChangeRecord, IterableChanges, IterableDiffer, IterableDifferFactory, NgIterable, TrackByFunction} from './iterable_differs'; diff --git a/modules/@angular/core/src/change_detection/differs/default_keyvalue_differ.ts b/modules/@angular/core/src/change_detection/differs/default_keyvalue_differ.ts index 8659e7e855..860da72620 100644 --- a/modules/@angular/core/src/change_detection/differs/default_keyvalue_differ.ts +++ b/modules/@angular/core/src/change_detection/differs/default_keyvalue_differ.ts @@ -6,7 +6,6 @@ * found in the LICENSE file at https://angular.io/license */ -import {StringMapWrapper} from '../../facade/collection'; import {isJsObject, looseIdentical, stringify} from '../../facade/lang'; import {ChangeDetectorRef} from '../change_detector_ref'; diff --git a/modules/@angular/core/src/core.ts b/modules/@angular/core/src/core.ts index d208277617..96fca7ebc5 100644 --- a/modules/@angular/core/src/core.ts +++ b/modules/@angular/core/src/core.ts @@ -21,7 +21,7 @@ export {APP_INITIALIZER, ApplicationInitStatus} from './application_init'; export * from './zone'; export * from './render'; export * from './linker'; -export {DebugElement, DebugNode, asNativeElements, getDebugNode} from './debug/debug_node'; +export {DebugElement, DebugNode, asNativeElements, getDebugNode, Predicate} from './debug/debug_node'; export {GetTestability, Testability, TestabilityRegistry, setTestabilityGetter} from './testability/testability'; export * from './change_detection'; export * from './platform_core_providers'; @@ -34,7 +34,6 @@ export {ErrorHandler} from './error_handler'; export * from './core_private_export'; export {Sanitizer, SecurityContext} from './security'; export * from './codegen_private_exports'; - export * from './animation/animation_metadata_wrapped'; import {AnimationTriggerMetadata} from './animation/animation_metadata_wrapped'; diff --git a/modules/@angular/core/src/core_private_export.ts b/modules/@angular/core/src/core_private_export.ts index 9ff4dc2c18..f77da1c1e8 100644 --- a/modules/@angular/core/src/core_private_export.ts +++ b/modules/@angular/core/src/core_private_export.ts @@ -9,6 +9,7 @@ export {ALLOW_MULTIPLE_PLATFORMS as ɵALLOW_MULTIPLE_PLATFORMS} from './application_ref'; export {APP_ID_RANDOM_PROVIDER as ɵAPP_ID_RANDOM_PROVIDER} from './application_tokens'; export {ValueUnwrapper as ɵValueUnwrapper, devModeEqual as ɵdevModeEqual} from './change_detection/change_detection_util'; +export {isListLikeIterable as ɵisListLikeIterable} from './change_detection/change_detection_util'; export {ChangeDetectorStatus as ɵChangeDetectorStatus, isDefaultChangeDetectionStrategy as ɵisDefaultChangeDetectionStrategy} from './change_detection/constants'; export {Console as ɵConsole} from './console'; export {ERROR_COMPONENT_TYPE as ɵERROR_COMPONENT_TYPE} from './errors'; @@ -23,4 +24,4 @@ export {ReflectorReader as ɵReflectorReader} from './reflection/reflector_reade export {GetterFn as ɵGetterFn, MethodFn as ɵMethodFn, SetterFn as ɵSetterFn} from './reflection/types'; export {DirectRenderer as ɵDirectRenderer, RenderDebugInfo as ɵRenderDebugInfo} from './render/api'; export {makeDecorator as ɵmakeDecorator} from './util/decorators'; -export {isObservable as ɵisObservable, isPromise as ɵisPromise} from './util/lang'; +export {isObservable as ɵisObservable, isPromise as ɵisPromise, merge as ɵmerge} from './util/lang'; diff --git a/modules/@angular/core/src/debug/debug_node.ts b/modules/@angular/core/src/debug/debug_node.ts index e092c299ce..e8fe7eeeee 100644 --- a/modules/@angular/core/src/debug/debug_node.ts +++ b/modules/@angular/core/src/debug/debug_node.ts @@ -7,7 +7,6 @@ */ import {Injector} from '../di'; -import {Predicate} from '../facade/collection'; import {RenderDebugInfo} from '../render/api'; export class EventListener { constructor(public name: string, public callback: Function){}; } @@ -192,3 +191,11 @@ export function indexDebugNode(node: DebugNode) { export function removeDebugNodeFromIndex(node: DebugNode) { _nativeNodeToDebugNode.delete(node.nativeNode); } + +/** + * A boolean-valued function over a value, possibly including context information + * regarding that value's position in an array. + * + * @experimental All debugging apis are currently experimental. + */ +export interface Predicate { (value: T, index?: number, array?: T[]): boolean; } diff --git a/modules/@angular/core/src/linker/query_list.ts b/modules/@angular/core/src/linker/query_list.ts index e09cadfebb..13b02bfb2c 100644 --- a/modules/@angular/core/src/linker/query_list.ts +++ b/modules/@angular/core/src/linker/query_list.ts @@ -9,7 +9,6 @@ import {Observable} from 'rxjs/Observable'; import {EventEmitter} from '../event_emitter'; -import {ListWrapper} from '../facade/collection'; import {getSymbolIterator} from '../facade/lang'; @@ -95,7 +94,7 @@ export class QueryList/* implements Iterable */ { toString(): string { return this._results.toString(); } reset(res: Array): void { - this._results = ListWrapper.flatten(res); + this._results = flatten(res); this._dirty = false; } @@ -107,3 +106,10 @@ export class QueryList/* implements Iterable */ { /** internal */ get dirty() { return this._dirty; } } + +function flatten(list: Array): T[] { + return list.reduce((flat: any[], item: T | T[]): T[] => { + const flatItem = Array.isArray(item) ? flatten(item) : item; + return (flat).concat(flatItem); + }, []); +} diff --git a/modules/@angular/core/src/util/lang.ts b/modules/@angular/core/src/util/lang.ts index 127b47734a..17cdbafad2 100644 --- a/modules/@angular/core/src/util/lang.ts +++ b/modules/@angular/core/src/util/lang.ts @@ -24,3 +24,18 @@ export function isPromise(obj: any): obj is Promise { export function isObservable(obj: any | Observable): obj is Observable { return !!(obj && obj[symbolObservable]); } + +// TODO(misko): replace with Object.assign once we require ES6. +export function merge(m1: {[key: string]: V}, m2: {[key: string]: V}): {[key: string]: V} { + const m: {[key: string]: V} = {}; + + for (const k of Object.keys(m1)) { + m[k] = m1[k]; + } + + for (const k of Object.keys(m2)) { + m[k] = m2[k]; + } + + return m; +} diff --git a/modules/@angular/core/test/linker/query_list_spec.ts b/modules/@angular/core/test/linker/query_list_spec.ts index 5222f7baa5..dc7e56aa12 100644 --- a/modules/@angular/core/test/linker/query_list_spec.ts +++ b/modules/@angular/core/test/linker/query_list_spec.ts @@ -6,13 +6,12 @@ * found in the LICENSE file at https://angular.io/license */ +import {iterateListLike} from '@angular/core/src/change_detection/change_detection_util'; import {QueryList} from '@angular/core/src/linker/query_list'; import {fakeAsync, tick} from '@angular/core/testing'; import {beforeEach, describe, expect, it} from '@angular/core/testing/testing_internal'; import {getDOM} from '@angular/platform-browser/src/dom/dom_adapter'; -import {iterateListLike} from '../../src/facade/collection'; - export function main() { describe('QueryList', () => { let queryList: QueryList; diff --git a/modules/@angular/core/testing/testing_internal.ts b/modules/@angular/core/testing/testing_internal.ts index e1c57509af..09b295dd83 100644 --- a/modules/@angular/core/testing/testing_internal.ts +++ b/modules/@angular/core/testing/testing_internal.ts @@ -6,10 +6,9 @@ * found in the LICENSE file at https://angular.io/license */ -import {ɵisPromise as isPromise} from '@angular/core'; +import {ɵisPromise as isPromise, ɵmerge as merge} from '@angular/core'; import {AsyncTestCompleter} from './async_test_completer'; -import {StringMapWrapper} from './facade/collection'; import {global} from './facade/lang'; import {getTestBed, inject} from './test_bed'; @@ -197,7 +196,7 @@ export class SpyObject { object = new SpyObject(); } - const m = StringMapWrapper.merge(config, overrides); + const m = merge(config, overrides); Object.keys(m).forEach(key => { object.spy(key).and.returnValue(m[key]); }); return object; } diff --git a/modules/@angular/facade/src/collection.ts b/modules/@angular/facade/src/collection.ts deleted file mode 100644 index 6b5a7a1c03..0000000000 --- a/modules/@angular/facade/src/collection.ts +++ /dev/null @@ -1,131 +0,0 @@ -/** - * @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 - */ - -import {getSymbolIterator, isJsObject, isPresent} from './lang'; - -/** - * Wraps Javascript Objects - */ -export class StringMapWrapper { - static merge(m1: {[key: string]: V}, m2: {[key: string]: V}): {[key: string]: V} { - const m: {[key: string]: V} = {}; - - for (const k of Object.keys(m1)) { - m[k] = m1[k]; - } - - for (const k of Object.keys(m2)) { - m[k] = m2[k]; - } - - return m; - } - - static equals(m1: {[key: string]: V}, m2: {[key: string]: V}): boolean { - const k1 = Object.keys(m1); - const k2 = Object.keys(m2); - - if (k1.length != k2.length) { - return false; - } - - for (let i = 0; i < k1.length; i++) { - const key = k1[i]; - if (m1[key] !== m2[key]) { - return false; - } - } - - return true; - } -} - -/** - * A boolean-valued function over a value, possibly including context information - * regarding that value's position in an array. - */ -export interface Predicate { (value: T, index?: number, array?: T[]): boolean; } - -export class ListWrapper { - static findLast(arr: T[], condition: (value: T) => boolean): T { - for (let i = arr.length - 1; i >= 0; i--) { - if (condition(arr[i])) { - return arr[i]; - } - } - return null; - } - - static removeAll(list: T[], items: T[]) { - for (let i = 0; i < items.length; ++i) { - const index = list.indexOf(items[i]); - if (index > -1) { - list.splice(index, 1); - } - } - } - - static remove(list: T[], el: T): boolean { - const index = list.indexOf(el); - if (index > -1) { - list.splice(index, 1); - return true; - } - return false; - } - - static equals(a: any[], b: any[]): boolean { - if (a.length != b.length) return false; - for (let i = 0; i < a.length; ++i) { - if (a[i] !== b[i]) return false; - } - return true; - } - - static flatten(list: Array): T[] { - return list.reduce((flat: any[], item: T | T[]): T[] => { - const flatItem = Array.isArray(item) ? ListWrapper.flatten(item) : item; - return (flat).concat(flatItem); - }, []); - } -} - -export function isListLikeIterable(obj: any): boolean { - if (!isJsObject(obj)) return false; - return Array.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 function areIterablesEqual( - a: any, b: any, comparator: (a: any, b: any) => boolean): boolean { - const iterator1 = a[getSymbolIterator()](); - const iterator2 = b[getSymbolIterator()](); - - while (true) { - const item1 = iterator1.next(); - const item2 = iterator2.next(); - if (item1.done && item2.done) return true; - if (item1.done || item2.done) return false; - if (!comparator(item1.value, item2.value)) return false; - } -} - -export function iterateListLike(obj: any, fn: (p: any) => any) { - if (Array.isArray(obj)) { - for (let i = 0; i < obj.length; i++) { - fn(obj[i]); - } - } else { - const iterator = obj[getSymbolIterator()](); - let item: any; - while (!((item = iterator.next()).done)) { - fn(item.value); - } - } -} diff --git a/modules/@angular/facade/test/collection_spec.ts b/modules/@angular/facade/test/collection_spec.ts deleted file mode 100644 index 760c965033..0000000000 --- a/modules/@angular/facade/test/collection_spec.ts +++ /dev/null @@ -1,50 +0,0 @@ -/** - * @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 - */ - -import {StringMapWrapper} from '../src/collection'; - -export function main() { - describe('StringMapWrapper', () => { - describe('equals', () => { - it('should return true when comparing empty maps', - () => { expect(StringMapWrapper.equals({}, {})).toBe(true); }); - - it('should return true when comparing the same map', () => { - const m1: {[key: string]: number} = {'a': 1, 'b': 2, 'c': 3}; - expect(StringMapWrapper.equals(m1, m1)).toBe(true); - }); - - it('should return true when comparing different maps with the same keys and values', () => { - const m1: {[key: string]: number} = {'a': 1, 'b': 2, 'c': 3}; - const m2: {[key: string]: number} = {'a': 1, 'b': 2, 'c': 3}; - expect(StringMapWrapper.equals(m1, m2)).toBe(true); - }); - - it('should return false when comparing maps with different numbers of keys', () => { - const m1: {[key: string]: number} = {'a': 1, 'b': 2, 'c': 3}; - const m2: {[key: string]: number} = {'a': 1, 'b': 2, 'c': 3, 'd': 4}; - expect(StringMapWrapper.equals(m1, m2)).toBe(false); - expect(StringMapWrapper.equals(m2, m1)).toBe(false); - }); - - it('should return false when comparing maps with different keys', () => { - const m1: {[key: string]: number} = {'a': 1, 'b': 2, 'c': 3}; - const m2: {[key: string]: number} = {'a': 1, 'b': 2, 'CC': 3}; - expect(StringMapWrapper.equals(m1, m2)).toBe(false); - expect(StringMapWrapper.equals(m2, m1)).toBe(false); - }); - - it('should return false when comparing maps with different values', () => { - const m1: {[key: string]: number} = {'a': 1, 'b': 2, 'c': 3}; - const m2: {[key: string]: number} = {'a': 1, 'b': 20, 'c': 3}; - expect(StringMapWrapper.equals(m1, m2)).toBe(false); - expect(StringMapWrapper.equals(m2, m1)).toBe(false); - }); - }); - }); -} diff --git a/modules/@angular/forms/src/directives/reactive_directives/form_group_directive.ts b/modules/@angular/forms/src/directives/reactive_directives/form_group_directive.ts index 1f6f78f82b..6ec1773e2d 100644 --- a/modules/@angular/forms/src/directives/reactive_directives/form_group_directive.ts +++ b/modules/@angular/forms/src/directives/reactive_directives/form_group_directive.ts @@ -7,8 +7,6 @@ */ import {Directive, EventEmitter, Inject, Input, OnChanges, Optional, Output, Self, SimpleChanges, forwardRef} from '@angular/core'; - -import {ListWrapper} from '../../facade/collection'; import {FormArray, FormControl, FormGroup} from '../../model'; import {NG_ASYNC_VALIDATORS, NG_VALIDATORS, Validators} from '../../validators'; import {ControlContainer} from '../control_container'; @@ -107,7 +105,7 @@ export class FormGroupDirective extends ControlContainer implements Form, getControl(dir: FormControlName): FormControl { return this.form.get(dir.path); } - removeControl(dir: FormControlName): void { ListWrapper.remove(this.directives, dir); } + removeControl(dir: FormControlName): void { remove(this.directives, dir); } addFormGroup(dir: FormGroupName): void { const ctrl: any = this.form.get(dir.path); @@ -181,3 +179,10 @@ export class FormGroupDirective extends ControlContainer implements Form, } } } + +function remove(list: T[], el: T): void { + const index = list.indexOf(el); + if (index > -1) { + list.splice(index, 1); + } +} diff --git a/modules/@angular/forms/src/validators.ts b/modules/@angular/forms/src/validators.ts index 4307415b78..dbb4e33d54 100644 --- a/modules/@angular/forms/src/validators.ts +++ b/modules/@angular/forms/src/validators.ts @@ -6,10 +6,9 @@ * found in the LICENSE file at https://angular.io/license */ -import {InjectionToken, ɵisPromise as isPromise} from '@angular/core'; +import {InjectionToken, ɵisPromise as isPromise, ɵmerge as merge} from '@angular/core'; import {toPromise} from 'rxjs/operator/toPromise'; import {AsyncValidatorFn, Validator, ValidatorFn} from './directives/validators'; -import {StringMapWrapper} from './facade/collection'; import {isPresent} from './facade/lang'; import {AbstractControl, FormControl, FormGroup} from './model'; @@ -203,7 +202,7 @@ function _executeAsyncValidators(control: AbstractControl, validators: AsyncVali function _mergeErrors(arrayOfErrors: any[]): {[key: string]: any} { const res: {[key: string]: any} = arrayOfErrors.reduce((res: {[key: string]: any}, errors: {[key: string]: any}) => { - return isPresent(errors) ? StringMapWrapper.merge(res, errors) : res; + return isPresent(errors) ? merge(res, errors) : res; }, {}); return Object.keys(res).length === 0 ? null : res; } diff --git a/modules/@angular/platform-browser/src/dom/debug/by.ts b/modules/@angular/platform-browser/src/dom/debug/by.ts index 07cb057099..0f5903a192 100644 --- a/modules/@angular/platform-browser/src/dom/debug/by.ts +++ b/modules/@angular/platform-browser/src/dom/debug/by.ts @@ -6,10 +6,8 @@ * found in the LICENSE file at https://angular.io/license */ -import {DebugElement, Type} from '@angular/core'; - +import {DebugElement, Predicate, Type} from '@angular/core'; import {getDOM} from '../../dom/dom_adapter'; -import {Predicate} from '../../facade/collection'; import {isPresent} from '../../facade/lang'; diff --git a/modules/@angular/platform-browser/src/dom/debug/ng_probe.ts b/modules/@angular/platform-browser/src/dom/debug/ng_probe.ts index f756248692..932284780d 100644 --- a/modules/@angular/platform-browser/src/dom/debug/ng_probe.ts +++ b/modules/@angular/platform-browser/src/dom/debug/ng_probe.ts @@ -7,8 +7,6 @@ */ import * as core from '@angular/core'; - -import {StringMapWrapper} from '../../facade/collection'; import {getDOM} from '../dom_adapter'; const CORE_TOKENS = { @@ -40,8 +38,7 @@ export function _createNgProbe(extraTokens: NgProbeToken[], coreTokens: core.NgP const tokens = (extraTokens || []).concat(coreTokens || []); getDOM().setGlobalVar(INSPECT_GLOBAL_NAME, inspectNativeElement); getDOM().setGlobalVar( - CORE_TOKENS_GLOBAL_NAME, - StringMapWrapper.merge(CORE_TOKENS, _ngProbeTokensToMap(tokens || []))); + CORE_TOKENS_GLOBAL_NAME, core.ɵmerge(CORE_TOKENS, _ngProbeTokensToMap(tokens || []))); return () => inspectNativeElement; } diff --git a/modules/@angular/platform-server/src/parse5_adapter.ts b/modules/@angular/platform-server/src/parse5_adapter.ts index f36d18b03f..4d93d11f92 100644 --- a/modules/@angular/platform-server/src/parse5_adapter.ts +++ b/modules/@angular/platform-server/src/parse5_adapter.ts @@ -8,7 +8,6 @@ const parse5 = require('parse5'); -import {ListWrapper} from '../src/facade/collection'; import {ɵDomAdapter as DomAdapter, ɵsetRootDomAdapter as setRootDomAdapter} from '@angular/platform-browser'; import {isPresent, isBlank, global, setValueOnPath} from '../src/facade/lang'; import {SelectorMatcher, CssSelector} from '@angular/compiler'; @@ -141,7 +140,7 @@ export class Parse5DomAdapter extends DomAdapter { } onAndCancel(el: any, evt: any, listener: any): Function { this.on(el, evt, listener); - return () => { ListWrapper.remove((el._eventListenersMap[evt]), listener); }; + return () => { remove((el._eventListenersMap[evt]), listener); }; } dispatchEvent(el: any, evt: any) { if (!evt.target) { @@ -779,3 +778,10 @@ const _HTMLElementPropertyList = [ 'closure_lm_714617', '__jsaction', ]; + +function remove(list: T[], el: T): void { + const index = list.indexOf(el); + if (index > -1) { + list.splice(index, 1); + } +} diff --git a/modules/@angular/platform-webworker/src/web_workers/worker/renderer.ts b/modules/@angular/platform-webworker/src/web_workers/worker/renderer.ts index 340704d609..d0afcf6a0f 100644 --- a/modules/@angular/platform-webworker/src/web_workers/worker/renderer.ts +++ b/modules/@angular/platform-webworker/src/web_workers/worker/renderer.ts @@ -8,8 +8,6 @@ import {Injectable, RenderComponentType, Renderer, RendererFactoryV2, RendererTypeV2, RendererV2, RootRenderer, ViewEncapsulation} from '@angular/core'; -import {ListWrapper} from '../../facade/collection'; -import {isPresent} from '../../facade/lang'; import {ClientMessageBroker, ClientMessageBrokerFactory, FnArg, UiArguments} from '../shared/client_message_broker'; import {MessageBus} from '../shared/message_bus'; import {EVENT_V2_CHANNEL, RENDERER_V2_CHANNEL} from '../shared/messaging_api'; diff --git a/tools/public_api_guard/core/typings/core.d.ts b/tools/public_api_guard/core/typings/core.d.ts index 8105f77149..f8711f4224 100644 --- a/tools/public_api_guard/core/typings/core.d.ts +++ b/tools/public_api_guard/core/typings/core.d.ts @@ -717,6 +717,11 @@ export declare abstract class PlatformRef { abstract onDestroy(callback: () => void): void; } +/** @experimental */ +export interface Predicate { + (value: T, index?: number, array?: T[]): boolean; +} + /** @stable */ export declare type Provider = TypeProvider | ValueProvider | ClassProvider | ExistingProvider | FactoryProvider | any[];