From 7c05d0c0ba7cff42e358d9648b1a7eb255d7aa37 Mon Sep 17 00:00:00 2001 From: Adam Bradley Date: Fri, 16 Sep 2016 00:49:09 -0500 Subject: [PATCH] fix(di): update dependency injection and default configs --- src/animations/animation.ts | 19 -- .../action-sheet/action-sheet-component.ts | 89 ------- .../action-sheet/action-sheet-transitions.ts | 86 +++++++ src/components/alert/alert-component.ts | 119 --------- src/components/alert/alert-transitions.ts | 113 ++++++++ src/components/app/app-root.ts | 8 +- src/components/loading/loading-component.ts | 119 --------- src/components/loading/loading-transitions.ts | 113 ++++++++ src/components/modal/modal-component.ts | 96 ------- src/components/modal/modal-transitions.ts | 93 +++++++ src/components/picker/picker-component.ts | 36 --- src/components/picker/picker-transitions.ts | 33 +++ src/components/popover/popover-component.ts | 242 ------------------ src/components/popover/popover-transitions.ts | 239 +++++++++++++++++ src/components/toast/toast-component.ts | 193 -------------- src/components/toast/toast-transitions.ts | 192 ++++++++++++++ src/config/config.ts | 40 +-- src/config/{modes.ts => mode-registry.ts} | 32 +-- src/config/test/config.spec.ts | 131 ++-------- src/index.ts | 2 - src/module.ts | 133 +++++----- src/navigation/deep-linker.ts | 5 - src/navigation/url-serializer.ts | 12 +- src/platform/platform-registry.ts | 234 +++++++++++++++++ src/platform/platform.ts | 75 +++--- src/platform/query-params.ts | 2 +- src/platform/registry.ts | 229 ----------------- src/platform/test/platform.spec.ts | 83 +++--- src/platform/test/query-params.spec.ts | 3 +- src/transitions/transition-controller.ts | 10 +- src/transitions/transition-registry.ts | 74 ++++++ src/transitions/transition.ts | 16 -- src/util/feature-detect.ts | 51 ---- src/util/mock-providers.ts | 8 +- 34 files changed, 1396 insertions(+), 1534 deletions(-) create mode 100644 src/components/action-sheet/action-sheet-transitions.ts create mode 100644 src/components/alert/alert-transitions.ts create mode 100644 src/components/loading/loading-transitions.ts create mode 100644 src/components/modal/modal-transitions.ts create mode 100644 src/components/picker/picker-transitions.ts create mode 100644 src/components/popover/popover-transitions.ts create mode 100644 src/components/toast/toast-transitions.ts rename src/config/{modes.ts => mode-registry.ts} (76%) create mode 100644 src/platform/platform-registry.ts delete mode 100644 src/platform/registry.ts create mode 100644 src/transitions/transition-registry.ts delete mode 100644 src/util/feature-detect.ts diff --git a/src/animations/animation.ts b/src/animations/animation.ts index 19cbf263e1..6699dfa627 100644 --- a/src/animations/animation.ts +++ b/src/animations/animation.ts @@ -1065,24 +1065,6 @@ export class Animation { return (this._twn && this._hasDur && this._eL ? this._e[0] : null); } - - // ***** STATIC CLASSES ********* - - static create(name: string, opts: AnimationOptions = {}): Animation { - let AnimationClass = AnimationRegistry[name]; - - if (!AnimationClass) { - // couldn't find an animation by the given name - // fallback to just the base Animation class - AnimationClass = Animation; - } - return new AnimationClass(null, opts); - } - - static register(name: string, AnimationClass: any) { - AnimationRegistry[name] = AnimationClass; - } - } export interface AnimationOptions { @@ -1115,7 +1097,6 @@ const TRANSFORMS: {[key: string]: number} = { 'translateX': 1, 'translateY': 1, 'translateZ': 1, 'scale': 1, 'scaleX': 1, 'scaleY': 1, 'scaleZ': 1, 'rotate': 1, 'rotateX': 1, 'rotateY': 1, 'rotateZ': 1, 'skewX': 1, 'skewY': 1, 'perspective': 1 }; -const AnimationRegistry: {[key: string]: any} = {}; const CSS_VALUE_REGEX = /(^-?\d*\.?\d*)(.*)/; const ANIMATION_DURATION_MIN = 32; const TRANSITION_END_FALLBACK_PADDING_MS = 400; diff --git a/src/components/action-sheet/action-sheet-component.ts b/src/components/action-sheet/action-sheet-component.ts index 1f4160bb8c..863f07cb37 100644 --- a/src/components/action-sheet/action-sheet-component.ts +++ b/src/components/action-sheet/action-sheet-component.ts @@ -170,93 +170,4 @@ export class ActionSheetCmp { } } - -class ActionSheetSlideIn extends Transition { - init() { - let ele = this.enteringView.pageRef().nativeElement; - let backdrop = new Animation(ele.querySelector('ion-backdrop')); - let wrapper = new Animation(ele.querySelector('.action-sheet-wrapper')); - - backdrop.fromTo('opacity', 0.01, 0.4); - wrapper.fromTo('translateY', '100%', '0%'); - - this.easing('cubic-bezier(.36,.66,.04,1)').duration(400).add(backdrop).add(wrapper); - } -} -Transition.register('action-sheet-slide-in', ActionSheetSlideIn); - - -class ActionSheetSlideOut extends Transition { - init() { - let ele = this.leavingView.pageRef().nativeElement; - let backdrop = new Animation(ele.querySelector('ion-backdrop')); - let wrapper = new Animation(ele.querySelector('.action-sheet-wrapper')); - - backdrop.fromTo('opacity', 0.4, 0); - wrapper.fromTo('translateY', '0%', '100%'); - - this.easing('cubic-bezier(.36,.66,.04,1)').duration(300).add(backdrop).add(wrapper); - } -} -Transition.register('action-sheet-slide-out', ActionSheetSlideOut); - - -class ActionSheetMdSlideIn extends Transition { - init() { - let ele = this.enteringView.pageRef().nativeElement; - let backdrop = new Animation(ele.querySelector('ion-backdrop')); - let wrapper = new Animation(ele.querySelector('.action-sheet-wrapper')); - - backdrop.fromTo('opacity', 0.01, 0.26); - wrapper.fromTo('translateY', '100%', '0%'); - - this.easing('cubic-bezier(.36,.66,.04,1)').duration(400).add(backdrop).add(wrapper); - } -} -Transition.register('action-sheet-md-slide-in', ActionSheetMdSlideIn); - - -class ActionSheetMdSlideOut extends Transition { - init() { - let ele = this.leavingView.pageRef().nativeElement; - let backdrop = new Animation(ele.querySelector('ion-backdrop')); - let wrapper = new Animation(ele.querySelector('.action-sheet-wrapper')); - - backdrop.fromTo('opacity', 0.26, 0); - wrapper.fromTo('translateY', '0%', '100%'); - - this.easing('cubic-bezier(.36,.66,.04,1)').duration(450).add(backdrop).add(wrapper); - } -} -Transition.register('action-sheet-md-slide-out', ActionSheetMdSlideOut); - -class ActionSheetWpSlideIn extends Transition { - init() { - let ele = this.enteringView.pageRef().nativeElement; - let backdrop = new Animation(ele.querySelector('ion-backdrop')); - let wrapper = new Animation(ele.querySelector('.action-sheet-wrapper')); - - backdrop.fromTo('opacity', 0.01, 0.16); - wrapper.fromTo('translateY', '100%', '0%'); - - this.easing('cubic-bezier(.36,.66,.04,1)').duration(400).add(backdrop).add(wrapper); - } -} -Transition.register('action-sheet-wp-slide-in', ActionSheetWpSlideIn); - - -class ActionSheetWpSlideOut extends Transition { - init() { - let ele = this.leavingView.pageRef().nativeElement; - let backdrop = new Animation(ele.querySelector('ion-backdrop')); - let wrapper = new Animation(ele.querySelector('.action-sheet-wrapper')); - - backdrop.fromTo('opacity', 0.1, 0); - wrapper.fromTo('translateY', '0%', '100%'); - - this.easing('cubic-bezier(.36,.66,.04,1)').duration(450).add(backdrop).add(wrapper); - } -} -Transition.register('action-sheet-wp-slide-out', ActionSheetWpSlideOut); - let actionSheetIds = -1; diff --git a/src/components/action-sheet/action-sheet-transitions.ts b/src/components/action-sheet/action-sheet-transitions.ts new file mode 100644 index 0000000000..3d995bdc2c --- /dev/null +++ b/src/components/action-sheet/action-sheet-transitions.ts @@ -0,0 +1,86 @@ +import { Animation } from '../../animations/animation'; +import { Transition } from '../../transitions/transition'; + + +export class ActionSheetSlideIn extends Transition { + init() { + let ele = this.enteringView.pageRef().nativeElement; + let backdrop = new Animation(ele.querySelector('ion-backdrop')); + let wrapper = new Animation(ele.querySelector('.action-sheet-wrapper')); + + backdrop.fromTo('opacity', 0.01, 0.4); + wrapper.fromTo('translateY', '100%', '0%'); + + this.easing('cubic-bezier(.36,.66,.04,1)').duration(400).add(backdrop).add(wrapper); + } +} + + +export class ActionSheetSlideOut extends Transition { + init() { + let ele = this.leavingView.pageRef().nativeElement; + let backdrop = new Animation(ele.querySelector('ion-backdrop')); + let wrapper = new Animation(ele.querySelector('.action-sheet-wrapper')); + + backdrop.fromTo('opacity', 0.4, 0); + wrapper.fromTo('translateY', '0%', '100%'); + + this.easing('cubic-bezier(.36,.66,.04,1)').duration(300).add(backdrop).add(wrapper); + } +} + + +export class ActionSheetMdSlideIn extends Transition { + init() { + let ele = this.enteringView.pageRef().nativeElement; + let backdrop = new Animation(ele.querySelector('ion-backdrop')); + let wrapper = new Animation(ele.querySelector('.action-sheet-wrapper')); + + backdrop.fromTo('opacity', 0.01, 0.26); + wrapper.fromTo('translateY', '100%', '0%'); + + this.easing('cubic-bezier(.36,.66,.04,1)').duration(400).add(backdrop).add(wrapper); + } +} + + +export class ActionSheetMdSlideOut extends Transition { + init() { + let ele = this.leavingView.pageRef().nativeElement; + let backdrop = new Animation(ele.querySelector('ion-backdrop')); + let wrapper = new Animation(ele.querySelector('.action-sheet-wrapper')); + + backdrop.fromTo('opacity', 0.26, 0); + wrapper.fromTo('translateY', '0%', '100%'); + + this.easing('cubic-bezier(.36,.66,.04,1)').duration(450).add(backdrop).add(wrapper); + } +} + + +export class ActionSheetWpSlideIn extends Transition { + init() { + let ele = this.enteringView.pageRef().nativeElement; + let backdrop = new Animation(ele.querySelector('ion-backdrop')); + let wrapper = new Animation(ele.querySelector('.action-sheet-wrapper')); + + backdrop.fromTo('opacity', 0.01, 0.16); + wrapper.fromTo('translateY', '100%', '0%'); + + this.easing('cubic-bezier(.36,.66,.04,1)').duration(400).add(backdrop).add(wrapper); + } +} + + +export class ActionSheetWpSlideOut extends Transition { + init() { + let ele = this.leavingView.pageRef().nativeElement; + let backdrop = new Animation(ele.querySelector('ion-backdrop')); + let wrapper = new Animation(ele.querySelector('.action-sheet-wrapper')); + + backdrop.fromTo('opacity', 0.1, 0); + wrapper.fromTo('translateY', '0%', '100%'); + + this.easing('cubic-bezier(.36,.66,.04,1)').duration(450).add(backdrop).add(wrapper); + } +} diff --git a/src/components/alert/alert-component.ts b/src/components/alert/alert-component.ts index 4bf298d931..d0ba6927a4 100644 --- a/src/components/alert/alert-component.ts +++ b/src/components/alert/alert-component.ts @@ -1,11 +1,9 @@ import { Component, ElementRef, HostListener, Renderer, ViewEncapsulation } from '@angular/core'; -import { Animation } from '../../animations/animation'; import { Config } from '../../config/config'; import { isPresent } from '../../util/util'; import { Key } from '../../util/key'; import { NavParams } from '../../navigation/nav-params'; -import { Transition } from '../../transitions/transition'; import { ViewController } from '../../navigation/view-controller'; @@ -288,121 +286,4 @@ export class AlertCmp { } } - -/** - * Animations for alerts - */ -class AlertPopIn extends Transition { - init() { - let ele = this.enteringView.pageRef().nativeElement; - let backdrop = new Animation(ele.querySelector('ion-backdrop')); - let wrapper = new Animation(ele.querySelector('.alert-wrapper')); - - wrapper.fromTo('opacity', 0.01, 1).fromTo('scale', 1.1, 1); - backdrop.fromTo('opacity', 0.01, 0.3); - - this - .easing('ease-in-out') - .duration(200) - .add(backdrop) - .add(wrapper); - } -} -Transition.register('alert-pop-in', AlertPopIn); - - -class AlertPopOut extends Transition { - init() { - let ele = this.leavingView.pageRef().nativeElement; - let backdrop = new Animation(ele.querySelector('ion-backdrop')); - let wrapper = new Animation(ele.querySelector('.alert-wrapper')); - - wrapper.fromTo('opacity', 0.99, 0).fromTo('scale', 1, 0.9); - backdrop.fromTo('opacity', 0.3, 0); - - this - .easing('ease-in-out') - .duration(200) - .add(backdrop) - .add(wrapper); - } -} -Transition.register('alert-pop-out', AlertPopOut); - - -class AlertMdPopIn extends Transition { - init() { - let ele = this.enteringView.pageRef().nativeElement; - let backdrop = new Animation(ele.querySelector('ion-backdrop')); - let wrapper = new Animation(ele.querySelector('.alert-wrapper')); - - wrapper.fromTo('opacity', 0.01, 1).fromTo('scale', 1.1, 1); - backdrop.fromTo('opacity', 0.01, 0.5); - - this - .easing('ease-in-out') - .duration(200) - .add(backdrop) - .add(wrapper); - } -} -Transition.register('alert-md-pop-in', AlertMdPopIn); - - -class AlertMdPopOut extends Transition { - init() { - let ele = this.leavingView.pageRef().nativeElement; - let backdrop = new Animation(ele.querySelector('ion-backdrop')); - let wrapper = new Animation(ele.querySelector('.alert-wrapper')); - - wrapper.fromTo('opacity', 0.99, 0).fromTo('scale', 1, 0.9); - backdrop.fromTo('opacity', 0.5, 0); - - this - .easing('ease-in-out') - .duration(200) - .add(backdrop) - .add(wrapper); - } -} -Transition.register('alert-md-pop-out', AlertMdPopOut); - - -class AlertWpPopIn extends Transition { - init() { - let ele = this.enteringView.pageRef().nativeElement; - let backdrop = new Animation(ele.querySelector('ion-backdrop')); - let wrapper = new Animation(ele.querySelector('.alert-wrapper')); - - wrapper.fromTo('opacity', 0.01, 1).fromTo('scale', 1.3, 1); - backdrop.fromTo('opacity', 0.01, 0.5); - - this - .easing('cubic-bezier(0,0 0.05,1)') - .duration(200) - .add(backdrop) - .add(wrapper); - } -} -Transition.register('alert-wp-pop-in', AlertWpPopIn); - - -class AlertWpPopOut extends Transition { - init() { - let ele = this.leavingView.pageRef().nativeElement; - let backdrop = new Animation(ele.querySelector('ion-backdrop')); - let wrapper = new Animation(ele.querySelector('.alert-wrapper')); - - wrapper.fromTo('opacity', 0.99, 0).fromTo('scale', 1, 1.3); - backdrop.fromTo('opacity', 0.5, 0); - - this - .easing('ease-out') - .duration(150) - .add(backdrop) - .add(wrapper); - } -} -Transition.register('alert-wp-pop-out', AlertWpPopOut); - let alertIds = -1; diff --git a/src/components/alert/alert-transitions.ts b/src/components/alert/alert-transitions.ts new file mode 100644 index 0000000000..044e4de694 --- /dev/null +++ b/src/components/alert/alert-transitions.ts @@ -0,0 +1,113 @@ +import { Animation } from '../../animations/animation'; +import { Transition } from '../../transitions/transition'; + + +/** + * Animations for alerts + */ +export class AlertPopIn extends Transition { + init() { + let ele = this.enteringView.pageRef().nativeElement; + let backdrop = new Animation(ele.querySelector('ion-backdrop')); + let wrapper = new Animation(ele.querySelector('.alert-wrapper')); + + wrapper.fromTo('opacity', 0.01, 1).fromTo('scale', 1.1, 1); + backdrop.fromTo('opacity', 0.01, 0.3); + + this + .easing('ease-in-out') + .duration(200) + .add(backdrop) + .add(wrapper); + } +} + + +export class AlertPopOut extends Transition { + init() { + let ele = this.leavingView.pageRef().nativeElement; + let backdrop = new Animation(ele.querySelector('ion-backdrop')); + let wrapper = new Animation(ele.querySelector('.alert-wrapper')); + + wrapper.fromTo('opacity', 0.99, 0).fromTo('scale', 1, 0.9); + backdrop.fromTo('opacity', 0.3, 0); + + this + .easing('ease-in-out') + .duration(200) + .add(backdrop) + .add(wrapper); + } +} + + +export class AlertMdPopIn extends Transition { + init() { + let ele = this.enteringView.pageRef().nativeElement; + let backdrop = new Animation(ele.querySelector('ion-backdrop')); + let wrapper = new Animation(ele.querySelector('.alert-wrapper')); + + wrapper.fromTo('opacity', 0.01, 1).fromTo('scale', 1.1, 1); + backdrop.fromTo('opacity', 0.01, 0.5); + + this + .easing('ease-in-out') + .duration(200) + .add(backdrop) + .add(wrapper); + } +} + + +export class AlertMdPopOut extends Transition { + init() { + let ele = this.leavingView.pageRef().nativeElement; + let backdrop = new Animation(ele.querySelector('ion-backdrop')); + let wrapper = new Animation(ele.querySelector('.alert-wrapper')); + + wrapper.fromTo('opacity', 0.99, 0).fromTo('scale', 1, 0.9); + backdrop.fromTo('opacity', 0.5, 0); + + this + .easing('ease-in-out') + .duration(200) + .add(backdrop) + .add(wrapper); + } +} + + +export class AlertWpPopIn extends Transition { + init() { + let ele = this.enteringView.pageRef().nativeElement; + let backdrop = new Animation(ele.querySelector('ion-backdrop')); + let wrapper = new Animation(ele.querySelector('.alert-wrapper')); + + wrapper.fromTo('opacity', 0.01, 1).fromTo('scale', 1.3, 1); + backdrop.fromTo('opacity', 0.01, 0.5); + + this + .easing('cubic-bezier(0,0 0.05,1)') + .duration(200) + .add(backdrop) + .add(wrapper); + } +} + + +export class AlertWpPopOut extends Transition { + init() { + let ele = this.leavingView.pageRef().nativeElement; + let backdrop = new Animation(ele.querySelector('ion-backdrop')); + let wrapper = new Animation(ele.querySelector('.alert-wrapper')); + + wrapper.fromTo('opacity', 0.99, 0).fromTo('scale', 1, 1.3); + backdrop.fromTo('opacity', 0.5, 0); + + this + .easing('ease-out') + .duration(150) + .add(backdrop) + .add(wrapper); + } +} diff --git a/src/components/app/app-root.ts b/src/components/app/app-root.ts index 06c155369d..ee4560c996 100644 --- a/src/components/app/app-root.ts +++ b/src/components/app/app-root.ts @@ -2,12 +2,11 @@ import { Component, ComponentFactoryResolver, ElementRef, Inject, OnInit, Opaque import { App } from './app'; import { Config } from '../../config/config'; -import { FeatureDetect } from '../../util/feature-detect'; import { Ion } from '../ion'; import { OverlayPortal } from '../nav/overlay-portal'; import { Platform } from '../../platform/platform'; -export const UserRoot = new OpaqueToken('USERROOT'); +export const AppRootToken = new OpaqueToken('USERROOT'); /** * @private @@ -32,13 +31,12 @@ export class IonicApp extends Ion implements OnInit { @ViewChild('toastPortal', { read: OverlayPortal }) _toastPortal: OverlayPortal; constructor( - @Inject(UserRoot) private _userCmp: any, + @Inject(AppRootToken) private _userCmp: any, private _cfr: ComponentFactoryResolver, elementRef: ElementRef, renderer: Renderer, config: Config, private _platform: Platform, - private _featureDetect: FeatureDetect, app: App ) { super(config, elementRef, renderer); @@ -80,8 +78,6 @@ export class IonicApp extends Ion implements OnInit { if (this._config.getBoolean('hoverCSS', true)) { this.setElementClass('enable-hover', true); } - - this._featureDetect.test(this); } /** diff --git a/src/components/loading/loading-component.ts b/src/components/loading/loading-component.ts index d2a5e905a9..3ba4a3d1dc 100644 --- a/src/components/loading/loading-component.ts +++ b/src/components/loading/loading-component.ts @@ -1,10 +1,8 @@ import { Component, ElementRef, Renderer, ViewEncapsulation } from '@angular/core'; -import { Animation } from '../../animations/animation'; import { Config } from '../../config/config'; import { isDefined, isUndefined } from '../../util/util'; import { NavParams } from '../../navigation/nav-params'; -import { Transition } from '../../transitions/transition'; import { ViewController } from '../../navigation/view-controller'; @@ -95,121 +93,4 @@ export class LoadingCmp { } } - -/** - * Animations for loading - */ - class LoadingPopIn extends Transition { - init() { - let ele = this.enteringView.pageRef().nativeElement; - let backdrop = new Animation(ele.querySelector('ion-backdrop')); - let wrapper = new Animation(ele.querySelector('.loading-wrapper')); - - wrapper.fromTo('opacity', 0.01, 1).fromTo('scale', 1.1, 1); - backdrop.fromTo('opacity', 0.01, 0.3); - - this - .easing('ease-in-out') - .duration(200) - .add(backdrop) - .add(wrapper); - } - } - Transition.register('loading-pop-in', LoadingPopIn); - - - class LoadingPopOut extends Transition { - init() { - let ele = this.leavingView.pageRef().nativeElement; - let backdrop = new Animation(ele.querySelector('ion-backdrop')); - let wrapper = new Animation(ele.querySelector('.loading-wrapper')); - - wrapper.fromTo('opacity', 0.99, 0).fromTo('scale', 1, 0.9); - backdrop.fromTo('opacity', 0.3, 0); - - this - .easing('ease-in-out') - .duration(200) - .add(backdrop) - .add(wrapper); - } - } - Transition.register('loading-pop-out', LoadingPopOut); - - - class LoadingMdPopIn extends Transition { - init() { - let ele = this.enteringView.pageRef().nativeElement; - let backdrop = new Animation(ele.querySelector('ion-backdrop')); - let wrapper = new Animation(ele.querySelector('.loading-wrapper')); - - wrapper.fromTo('opacity', 0.01, 1).fromTo('scale', 1.1, 1); - backdrop.fromTo('opacity', 0.01, 0.5); - - this - .easing('ease-in-out') - .duration(200) - .add(backdrop) - .add(wrapper); - } - } - Transition.register('loading-md-pop-in', LoadingMdPopIn); - - - class LoadingMdPopOut extends Transition { - init() { - let ele = this.leavingView.pageRef().nativeElement; - let backdrop = new Animation(ele.querySelector('ion-backdrop')); - let wrapper = new Animation(ele.querySelector('.loading-wrapper')); - - wrapper.fromTo('opacity', 0.99, 0).fromTo('scale', 1, 0.9); - backdrop.fromTo('opacity', 0.5, 0); - - this - .easing('ease-in-out') - .duration(200) - .add(backdrop) - .add(wrapper); - } - } - Transition.register('loading-md-pop-out', LoadingMdPopOut); - - - class LoadingWpPopIn extends Transition { - init() { - let ele = this.enteringView.pageRef().nativeElement; - let backdrop = new Animation(ele.querySelector('ion-backdrop')); - let wrapper = new Animation(ele.querySelector('.loading-wrapper')); - - wrapper.fromTo('opacity', 0.01, 1).fromTo('scale', 1.3, 1); - backdrop.fromTo('opacity', 0.01, 0.16); - - this - .easing('cubic-bezier(0,0 0.05,1)') - .duration(200) - .add(backdrop) - .add(wrapper); - } - } - Transition.register('loading-wp-pop-in', LoadingWpPopIn); - - - class LoadingWpPopOut extends Transition { - init() { - let ele = this.leavingView.pageRef().nativeElement; - let backdrop = new Animation(ele.querySelector('ion-backdrop')); - let wrapper = new Animation(ele.querySelector('.loading-wrapper')); - - wrapper.fromTo('opacity', 0.99, 0).fromTo('scale', 1, 1.3); - backdrop.fromTo('opacity', 0.16, 0); - - this - .easing('ease-out') - .duration(150) - .add(backdrop) - .add(wrapper); - } - } - Transition.register('loading-wp-pop-out', LoadingWpPopOut); - let loadingIds = -1; diff --git a/src/components/loading/loading-transitions.ts b/src/components/loading/loading-transitions.ts new file mode 100644 index 0000000000..7c474d60c7 --- /dev/null +++ b/src/components/loading/loading-transitions.ts @@ -0,0 +1,113 @@ +import { Animation } from '../../animations/animation'; +import { Transition } from '../../transitions/transition'; + + +/** + * Animations for loading + */ +export class LoadingPopIn extends Transition { + init() { + let ele = this.enteringView.pageRef().nativeElement; + let backdrop = new Animation(ele.querySelector('ion-backdrop')); + let wrapper = new Animation(ele.querySelector('.loading-wrapper')); + + wrapper.fromTo('opacity', 0.01, 1).fromTo('scale', 1.1, 1); + backdrop.fromTo('opacity', 0.01, 0.3); + + this + .easing('ease-in-out') + .duration(200) + .add(backdrop) + .add(wrapper); + } +} + + +export class LoadingPopOut extends Transition { + init() { + let ele = this.leavingView.pageRef().nativeElement; + let backdrop = new Animation(ele.querySelector('ion-backdrop')); + let wrapper = new Animation(ele.querySelector('.loading-wrapper')); + + wrapper.fromTo('opacity', 0.99, 0).fromTo('scale', 1, 0.9); + backdrop.fromTo('opacity', 0.3, 0); + + this + .easing('ease-in-out') + .duration(200) + .add(backdrop) + .add(wrapper); + } +} + + +export class LoadingMdPopIn extends Transition { + init() { + let ele = this.enteringView.pageRef().nativeElement; + let backdrop = new Animation(ele.querySelector('ion-backdrop')); + let wrapper = new Animation(ele.querySelector('.loading-wrapper')); + + wrapper.fromTo('opacity', 0.01, 1).fromTo('scale', 1.1, 1); + backdrop.fromTo('opacity', 0.01, 0.5); + + this + .easing('ease-in-out') + .duration(200) + .add(backdrop) + .add(wrapper); + } +} + + +export class LoadingMdPopOut extends Transition { + init() { + let ele = this.leavingView.pageRef().nativeElement; + let backdrop = new Animation(ele.querySelector('ion-backdrop')); + let wrapper = new Animation(ele.querySelector('.loading-wrapper')); + + wrapper.fromTo('opacity', 0.99, 0).fromTo('scale', 1, 0.9); + backdrop.fromTo('opacity', 0.5, 0); + + this + .easing('ease-in-out') + .duration(200) + .add(backdrop) + .add(wrapper); + } +} + + +export class LoadingWpPopIn extends Transition { + init() { + let ele = this.enteringView.pageRef().nativeElement; + let backdrop = new Animation(ele.querySelector('ion-backdrop')); + let wrapper = new Animation(ele.querySelector('.loading-wrapper')); + + wrapper.fromTo('opacity', 0.01, 1).fromTo('scale', 1.3, 1); + backdrop.fromTo('opacity', 0.01, 0.16); + + this + .easing('cubic-bezier(0,0 0.05,1)') + .duration(200) + .add(backdrop) + .add(wrapper); + } +} + + +export class LoadingWpPopOut extends Transition { + init() { + let ele = this.leavingView.pageRef().nativeElement; + let backdrop = new Animation(ele.querySelector('ion-backdrop')); + let wrapper = new Animation(ele.querySelector('.loading-wrapper')); + + wrapper.fromTo('opacity', 0.99, 0).fromTo('scale', 1, 1.3); + backdrop.fromTo('opacity', 0.16, 0); + + this + .easing('ease-out') + .duration(150) + .add(backdrop) + .add(wrapper); + } +} diff --git a/src/components/modal/modal-component.ts b/src/components/modal/modal-component.ts index a7691bf018..f4005535f0 100644 --- a/src/components/modal/modal-component.ts +++ b/src/components/modal/modal-component.ts @@ -1,12 +1,9 @@ import { Component, ComponentFactoryResolver, HostListener, Renderer, ViewChild, ViewContainerRef } from '@angular/core'; -import { Animation } from '../../animations/animation'; import { Key } from '../../util/key'; import { NavParams } from '../../navigation/nav-params'; import { pascalCaseToDashCase } from '../../util/util'; -import { PageTransition } from '../../transitions/page-transition'; import { ViewController } from '../../navigation/view-controller'; -import { windowDimensions } from '../../util/dom'; /** @@ -72,96 +69,3 @@ export class ModalCmp { } } } - -/** - * Animations for modals - */ - class ModalSlideIn extends PageTransition { - init() { - super.init(); - const ele: HTMLElement = this.enteringView.pageRef().nativeElement; - const backdropEle = ele.querySelector('ion-backdrop'); - const backdrop = new Animation(backdropEle); - const wrapper = new Animation(ele.querySelector('.modal-wrapper')); - - backdrop.fromTo('opacity', 0.01, 0.4); - wrapper.fromTo('translateY', '100%', '0%'); - - this - .element(this.enteringView.pageRef()) - .easing('cubic-bezier(0.36,0.66,0.04,1)') - .duration(400) - .add(backdrop) - .add(wrapper); - } - } - PageTransition.register('modal-slide-in', ModalSlideIn); - - -class ModalSlideOut extends PageTransition { - init() { - super.init(); - const ele: HTMLElement = this.leavingView.pageRef().nativeElement; - let backdrop = new Animation(ele.querySelector('ion-backdrop')); - let wrapperEle = ele.querySelector('.modal-wrapper'); - let wrapperEleRect = wrapperEle.getBoundingClientRect(); - let wrapper = new Animation(wrapperEle); - - // height of the screen - top of the container tells us how much to scoot it down - // so it's off-screen - let screenDimensions = windowDimensions(); - wrapper.fromTo('translateY', '0px', `${screenDimensions.height - wrapperEleRect.top}px`); - backdrop.fromTo('opacity', 0.4, 0.0); - - this - .element(this.leavingView.pageRef()) - .easing('ease-out') - .duration(250) - .add(backdrop) - .add(wrapper); - } -} -PageTransition.register('modal-slide-out', ModalSlideOut); - - -class ModalMDSlideIn extends PageTransition { - init() { - super.init(); - const ele: HTMLElement = this.enteringView.pageRef().nativeElement; - const backdrop = new Animation(ele.querySelector('ion-backdrop')); - const wrapper = new Animation(ele.querySelector('.modal-wrapper')); - - backdrop.fromTo('opacity', 0.01, 0.4); - wrapper.fromTo('translateY', '40px', '0px'); - wrapper.fromTo('opacity', 0.01, 1); - - const DURATION = 280; - const EASING = 'cubic-bezier(0.36,0.66,0.04,1)'; - this.element(this.enteringView.pageRef()).easing(EASING).duration(DURATION) - .add(backdrop) - .add(wrapper); - } -} -PageTransition.register('modal-md-slide-in', ModalMDSlideIn); - - -class ModalMDSlideOut extends PageTransition { - init() { - super.init(); - const ele: HTMLElement = this.leavingView.pageRef().nativeElement; - const backdrop = new Animation(ele.querySelector('ion-backdrop')); - const wrapper = new Animation(ele.querySelector('.modal-wrapper')); - - backdrop.fromTo('opacity', 0.4, 0.0); - wrapper.fromTo('translateY', '0px', '40px'); - wrapper.fromTo('opacity', 0.99, 0); - - this - .element(this.leavingView.pageRef()) - .duration(200) - .easing('cubic-bezier(0.47,0,0.745,0.715)') - .add(wrapper) - .add(backdrop); - } -} -PageTransition.register('modal-md-slide-out', ModalMDSlideOut); diff --git a/src/components/modal/modal-transitions.ts b/src/components/modal/modal-transitions.ts new file mode 100644 index 0000000000..263ea13605 --- /dev/null +++ b/src/components/modal/modal-transitions.ts @@ -0,0 +1,93 @@ +import { Animation } from '../../animations/animation'; +import { PageTransition } from '../../transitions/page-transition'; +import { windowDimensions } from '../../util/dom'; + + +/** + * Animations for modals + */ +export class ModalSlideIn extends PageTransition { + init() { + super.init(); + const ele: HTMLElement = this.enteringView.pageRef().nativeElement; + const backdropEle = ele.querySelector('ion-backdrop'); + const backdrop = new Animation(backdropEle); + const wrapper = new Animation(ele.querySelector('.modal-wrapper')); + + backdrop.fromTo('opacity', 0.01, 0.4); + wrapper.fromTo('translateY', '100%', '0%'); + + this + .element(this.enteringView.pageRef()) + .easing('cubic-bezier(0.36,0.66,0.04,1)') + .duration(400) + .add(backdrop) + .add(wrapper); + } +} + + +export class ModalSlideOut extends PageTransition { + init() { + super.init(); + const ele: HTMLElement = this.leavingView.pageRef().nativeElement; + let backdrop = new Animation(ele.querySelector('ion-backdrop')); + let wrapperEle = ele.querySelector('.modal-wrapper'); + let wrapperEleRect = wrapperEle.getBoundingClientRect(); + let wrapper = new Animation(wrapperEle); + + // height of the screen - top of the container tells us how much to scoot it down + // so it's off-screen + let screenDimensions = windowDimensions(); + wrapper.fromTo('translateY', '0px', `${screenDimensions.height - wrapperEleRect.top}px`); + backdrop.fromTo('opacity', 0.4, 0.0); + + this + .element(this.leavingView.pageRef()) + .easing('ease-out') + .duration(250) + .add(backdrop) + .add(wrapper); + } +} + + +export class ModalMDSlideIn extends PageTransition { + init() { + super.init(); + const ele: HTMLElement = this.enteringView.pageRef().nativeElement; + const backdrop = new Animation(ele.querySelector('ion-backdrop')); + const wrapper = new Animation(ele.querySelector('.modal-wrapper')); + + backdrop.fromTo('opacity', 0.01, 0.4); + wrapper.fromTo('translateY', '40px', '0px'); + wrapper.fromTo('opacity', 0.01, 1); + + const DURATION = 280; + const EASING = 'cubic-bezier(0.36,0.66,0.04,1)'; + this.element(this.enteringView.pageRef()).easing(EASING).duration(DURATION) + .add(backdrop) + .add(wrapper); + } +} + + +export class ModalMDSlideOut extends PageTransition { + init() { + super.init(); + const ele: HTMLElement = this.leavingView.pageRef().nativeElement; + const backdrop = new Animation(ele.querySelector('ion-backdrop')); + const wrapper = new Animation(ele.querySelector('.modal-wrapper')); + + backdrop.fromTo('opacity', 0.4, 0.0); + wrapper.fromTo('translateY', '0px', '40px'); + wrapper.fromTo('opacity', 0.99, 0); + + this + .element(this.leavingView.pageRef()) + .duration(200) + .easing('cubic-bezier(0.47,0,0.745,0.715)') + .add(wrapper) + .add(backdrop); + } +} diff --git a/src/components/picker/picker-component.ts b/src/components/picker/picker-component.ts index 9a419b92df..1e028e69ce 100644 --- a/src/components/picker/picker-component.ts +++ b/src/components/picker/picker-component.ts @@ -1,7 +1,6 @@ import { Component, ElementRef, EventEmitter, Input, HostListener, Output, QueryList, Renderer, ViewChild, ViewChildren, ViewEncapsulation } from '@angular/core'; import { DomSanitizer } from '@angular/platform-browser'; -import { Animation } from '../../animations/animation'; import { cancelRaf, pointerCoord, raf } from '../../util/dom'; import { clamp, isNumber, isPresent, isString } from '../../util/util'; import { Config } from '../../config/config'; @@ -9,7 +8,6 @@ import { Key } from '../../util/key'; import { NavParams } from '../../navigation/nav-params'; import { Picker } from './picker'; import { PickerOptions, PickerColumn, PickerColumnOption } from './picker-options'; -import { Transition } from '../../transitions/transition'; import { UIEventManager } from '../../util/ui-event-manager'; import { ViewController } from '../../navigation/view-controller'; @@ -546,40 +544,6 @@ export class PickerCmp { } } - -/** - * Animations for pickers - */ -class PickerSlideIn extends Transition { - init() { - let ele = this.enteringView.pageRef().nativeElement; - let backdrop = new Animation(ele.querySelector('ion-backdrop')); - let wrapper = new Animation(ele.querySelector('.picker-wrapper')); - - backdrop.fromTo('opacity', 0.01, 0.26); - wrapper.fromTo('translateY', '100%', '0%'); - - this.easing('cubic-bezier(.36,.66,.04,1)').duration(400).add(backdrop).add(wrapper); - } -} -Transition.register('picker-slide-in', PickerSlideIn); - - -class PickerSlideOut extends Transition { - init() { - let ele = this.leavingView.pageRef().nativeElement; - let backdrop = new Animation(ele.querySelector('ion-backdrop')); - let wrapper = new Animation(ele.querySelector('.picker-wrapper')); - - backdrop.fromTo('opacity', 0.26, 0); - wrapper.fromTo('translateY', '0%', '100%'); - - this.easing('cubic-bezier(.36,.66,.04,1)').duration(450).add(backdrop).add(wrapper); - } -} -Transition.register('picker-slide-out', PickerSlideOut); - - let pickerIds = -1; const DECELERATION_FRICTION = 0.97; const FRAME_MS = (1000 / 60); diff --git a/src/components/picker/picker-transitions.ts b/src/components/picker/picker-transitions.ts new file mode 100644 index 0000000000..3837cc6577 --- /dev/null +++ b/src/components/picker/picker-transitions.ts @@ -0,0 +1,33 @@ +import { Animation } from '../../animations/animation'; +import { Transition } from '../../transitions/transition'; + + +/** + * Animations for pickers + */ +export class PickerSlideIn extends Transition { + init() { + let ele = this.enteringView.pageRef().nativeElement; + let backdrop = new Animation(ele.querySelector('ion-backdrop')); + let wrapper = new Animation(ele.querySelector('.picker-wrapper')); + + backdrop.fromTo('opacity', 0.01, 0.26); + wrapper.fromTo('translateY', '100%', '0%'); + + this.easing('cubic-bezier(.36,.66,.04,1)').duration(400).add(backdrop).add(wrapper); + } +} + + +export class PickerSlideOut extends Transition { + init() { + let ele = this.leavingView.pageRef().nativeElement; + let backdrop = new Animation(ele.querySelector('ion-backdrop')); + let wrapper = new Animation(ele.querySelector('.picker-wrapper')); + + backdrop.fromTo('opacity', 0.26, 0); + wrapper.fromTo('translateY', '0%', '100%'); + + this.easing('cubic-bezier(.36,.66,.04,1)').duration(450).add(backdrop).add(wrapper); + } +} diff --git a/src/components/popover/popover-component.ts b/src/components/popover/popover-component.ts index e4081e446b..5f1a925b63 100644 --- a/src/components/popover/popover-component.ts +++ b/src/components/popover/popover-component.ts @@ -1,12 +1,9 @@ import { Component, ComponentFactoryResolver, ElementRef, HostListener, Renderer, ViewChild, ViewContainerRef } from '@angular/core'; -import { Animation } from '../../animations/animation'; import { Config } from '../../config/config'; -import { CSS, nativeRaf } from '../../util/dom'; import { pascalCaseToDashCase } from '../../util/util'; import { Key } from '../../util/key'; import { NavParams } from '../../navigation/nav-params'; -import { PageTransition } from '../../transitions/page-transition'; import { ViewController } from '../../navigation/view-controller'; @@ -105,243 +102,4 @@ export class PopoverCmp { } } - -/** - * Animations for popover - */ -class PopoverTransition extends PageTransition { - - mdPositionView(nativeEle: HTMLElement, ev: any) { - let originY = 'top'; - let originX = 'left'; - - let popoverWrapperEle = nativeEle.querySelector('.popover-wrapper'); - - // Popover content width and height - let popoverEle = nativeEle.querySelector('.popover-content'); - let popoverDim = popoverEle.getBoundingClientRect(); - let popoverWidth = popoverDim.width; - let popoverHeight = popoverDim.height; - - // Window body width and height - let bodyWidth = window.innerWidth; - let bodyHeight = window.innerHeight; - - // If ev was passed, use that for target element - let targetDim = ev && ev.target && ev.target.getBoundingClientRect(); - - let targetTop = (targetDim && 'top' in targetDim) ? targetDim.top : (bodyHeight / 2) - (popoverHeight / 2); - let targetLeft = (targetDim && 'left' in targetDim) ? targetDim.left : (bodyWidth / 2) - (popoverWidth / 2); - - let targetHeight = targetDim && targetDim.height || 0; - - let popoverCSS = { - top: targetTop, - left: targetLeft - }; - - // If the popover left is less than the padding it is off screen - // to the left so adjust it, else if the width of the popover - // exceeds the body width it is off screen to the right so adjust - if (popoverCSS.left < POPOVER_MD_BODY_PADDING) { - popoverCSS.left = POPOVER_MD_BODY_PADDING; - } else if (popoverWidth + POPOVER_MD_BODY_PADDING + popoverCSS.left > bodyWidth) { - popoverCSS.left = bodyWidth - popoverWidth - POPOVER_MD_BODY_PADDING; - originX = 'right'; - } - - // If the popover when popped down stretches past bottom of screen, - // make it pop up if there's room above - if (targetTop + targetHeight + popoverHeight > bodyHeight && targetTop - popoverHeight > 0) { - popoverCSS.top = targetTop - popoverHeight; - nativeEle.className = nativeEle.className + ' popover-bottom'; - originY = 'bottom'; - // If there isn't room for it to pop up above the target cut it off - } else if (targetTop + targetHeight + popoverHeight > bodyHeight) { - popoverEle.style.bottom = POPOVER_MD_BODY_PADDING + 'px'; - } - - popoverEle.style.top = popoverCSS.top + 'px'; - popoverEle.style.left = popoverCSS.left + 'px'; - - (popoverEle.style)[CSS.transformOrigin] = originY + ' ' + originX; - - // Since the transition starts before styling is done we - // want to wait for the styles to apply before showing the wrapper - popoverWrapperEle.style.opacity = '1'; - } - - iosPositionView(nativeEle: HTMLElement, ev: any) { - let originY = 'top'; - let originX = 'left'; - - let popoverWrapperEle = nativeEle.querySelector('.popover-wrapper'); - - // Popover content width and height - let popoverEle = nativeEle.querySelector('.popover-content'); - let popoverDim = popoverEle.getBoundingClientRect(); - let popoverWidth = popoverDim.width; - let popoverHeight = popoverDim.height; - - // Window body width and height - let bodyWidth = window.innerWidth; - let bodyHeight = window.innerHeight; - - // If ev was passed, use that for target element - let targetDim = ev && ev.target && ev.target.getBoundingClientRect(); - - let targetTop = (targetDim && 'top' in targetDim) ? targetDim.top : (bodyHeight / 2) - (popoverHeight / 2); - let targetLeft = (targetDim && 'left' in targetDim) ? targetDim.left : (bodyWidth / 2); - let targetWidth = targetDim && targetDim.width || 0; - let targetHeight = targetDim && targetDim.height || 0; - - // The arrow that shows above the popover on iOS - var arrowEle = nativeEle.querySelector('.popover-arrow'); - let arrowDim = arrowEle.getBoundingClientRect(); - var arrowWidth = arrowDim.width; - var arrowHeight = arrowDim.height; - - // If no ev was passed, hide the arrow - if (!targetDim) { - arrowEle.style.display = 'none'; - } - - let arrowCSS = { - top: targetTop + targetHeight, - left: targetLeft + (targetWidth / 2) - (arrowWidth / 2) - }; - - let popoverCSS = { - top: targetTop + targetHeight + (arrowHeight - 1), - left: targetLeft + (targetWidth / 2) - (popoverWidth / 2) - }; - - // If the popover left is less than the padding it is off screen - // to the left so adjust it, else if the width of the popover - // exceeds the body width it is off screen to the right so adjust - if (popoverCSS.left < POPOVER_IOS_BODY_PADDING) { - popoverCSS.left = POPOVER_IOS_BODY_PADDING; - } else if (popoverWidth + POPOVER_IOS_BODY_PADDING + popoverCSS.left > bodyWidth) { - popoverCSS.left = bodyWidth - popoverWidth - POPOVER_IOS_BODY_PADDING; - originX = 'right'; - } - - // If the popover when popped down stretches past bottom of screen, - // make it pop up if there's room above - if (targetTop + targetHeight + popoverHeight > bodyHeight && targetTop - popoverHeight > 0) { - arrowCSS.top = targetTop - (arrowHeight + 1); - popoverCSS.top = targetTop - popoverHeight - (arrowHeight - 1); - nativeEle.className = nativeEle.className + ' popover-bottom'; - originY = 'bottom'; - // If there isn't room for it to pop up above the target cut it off - } else if (targetTop + targetHeight + popoverHeight > bodyHeight) { - popoverEle.style.bottom = POPOVER_IOS_BODY_PADDING + '%'; - } - - arrowEle.style.top = arrowCSS.top + 'px'; - arrowEle.style.left = arrowCSS.left + 'px'; - - popoverEle.style.top = popoverCSS.top + 'px'; - popoverEle.style.left = popoverCSS.left + 'px'; - - (popoverEle.style)[CSS.transformOrigin] = originY + ' ' + originX; - - // Since the transition starts before styling is done we - // want to wait for the styles to apply before showing the wrapper - popoverWrapperEle.style.opacity = '1'; - } -} - -class PopoverPopIn extends PopoverTransition { - init() { - let ele = this.enteringView.pageRef().nativeElement; - - let backdrop = new Animation(ele.querySelector('ion-backdrop')); - let wrapper = new Animation(ele.querySelector('.popover-wrapper')); - - wrapper.fromTo('opacity', 0.01, 1); - backdrop.fromTo('opacity', 0.01, 0.08); - - this - .easing('ease') - .duration(100) - .add(backdrop) - .add(wrapper); - } - - play() { - nativeRaf(() => { - this.iosPositionView(this.enteringView.pageRef().nativeElement, this.opts.ev); - super.play(); - }); - } -} -PageTransition.register('popover-pop-in', PopoverPopIn); - - -class PopoverPopOut extends PopoverTransition { - init() { - let ele = this.leavingView.pageRef().nativeElement; - let backdrop = new Animation(ele.querySelector('ion-backdrop')); - let wrapper = new Animation(ele.querySelector('.popover-wrapper')); - - wrapper.fromTo('opacity', 0.99, 0); - backdrop.fromTo('opacity', 0.08, 0); - - this - .easing('ease') - .duration(500) - .add(backdrop) - .add(wrapper); - } -} -PageTransition.register('popover-pop-out', PopoverPopOut); - - -class PopoverMdPopIn extends PopoverTransition { - init() { - let ele = this.enteringView.pageRef().nativeElement; - let content = new Animation(ele.querySelector('.popover-content')); - let viewport = new Animation(ele.querySelector('.popover-viewport')); - - content.fromTo('scale', 0.001, 1); - viewport.fromTo('opacity', 0.01, 1); - - this - .easing('cubic-bezier(0.36,0.66,0.04,1)') - .duration(300) - .add(content) - .add(viewport); - } - - play() { - nativeRaf(() => { - this.mdPositionView(this.enteringView.pageRef().nativeElement, this.opts.ev); - super.play(); - }); - } -} -PageTransition.register('popover-md-pop-in', PopoverMdPopIn); - - -class PopoverMdPopOut extends PopoverTransition { - init() { - let ele = this.leavingView.pageRef().nativeElement; - let wrapper = new Animation(ele.querySelector('.popover-wrapper')); - - wrapper.fromTo('opacity', 0.99, 0); - - this - .easing('ease') - .duration(500) - .fromTo('opacity', 0.01, 1) - .add(wrapper); - } -} -PageTransition.register('popover-md-pop-out', PopoverMdPopOut); - - let popoverIds = -1; - -const POPOVER_IOS_BODY_PADDING = 2; -const POPOVER_MD_BODY_PADDING = 12; diff --git a/src/components/popover/popover-transitions.ts b/src/components/popover/popover-transitions.ts new file mode 100644 index 0000000000..03f62c74c3 --- /dev/null +++ b/src/components/popover/popover-transitions.ts @@ -0,0 +1,239 @@ +import { Animation } from '../../animations/animation'; +import { CSS, nativeRaf } from '../../util/dom'; +import { PageTransition } from '../../transitions/page-transition'; + + +/** + * Animations for popover + */ +export class PopoverTransition extends PageTransition { + + mdPositionView(nativeEle: HTMLElement, ev: any) { + let originY = 'top'; + let originX = 'left'; + + let popoverWrapperEle = nativeEle.querySelector('.popover-wrapper'); + + // Popover content width and height + let popoverEle = nativeEle.querySelector('.popover-content'); + let popoverDim = popoverEle.getBoundingClientRect(); + let popoverWidth = popoverDim.width; + let popoverHeight = popoverDim.height; + + // Window body width and height + let bodyWidth = window.innerWidth; + let bodyHeight = window.innerHeight; + + // If ev was passed, use that for target element + let targetDim = ev && ev.target && ev.target.getBoundingClientRect(); + + let targetTop = (targetDim && 'top' in targetDim) ? targetDim.top : (bodyHeight / 2) - (popoverHeight / 2); + let targetLeft = (targetDim && 'left' in targetDim) ? targetDim.left : (bodyWidth / 2) - (popoverWidth / 2); + + let targetHeight = targetDim && targetDim.height || 0; + + let popoverCSS = { + top: targetTop, + left: targetLeft + }; + + // If the popover left is less than the padding it is off screen + // to the left so adjust it, else if the width of the popover + // exceeds the body width it is off screen to the right so adjust + if (popoverCSS.left < POPOVER_MD_BODY_PADDING) { + popoverCSS.left = POPOVER_MD_BODY_PADDING; + } else if (popoverWidth + POPOVER_MD_BODY_PADDING + popoverCSS.left > bodyWidth) { + popoverCSS.left = bodyWidth - popoverWidth - POPOVER_MD_BODY_PADDING; + originX = 'right'; + } + + // If the popover when popped down stretches past bottom of screen, + // make it pop up if there's room above + if (targetTop + targetHeight + popoverHeight > bodyHeight && targetTop - popoverHeight > 0) { + popoverCSS.top = targetTop - popoverHeight; + nativeEle.className = nativeEle.className + ' popover-bottom'; + originY = 'bottom'; + // If there isn't room for it to pop up above the target cut it off + } else if (targetTop + targetHeight + popoverHeight > bodyHeight) { + popoverEle.style.bottom = POPOVER_MD_BODY_PADDING + 'px'; + } + + popoverEle.style.top = popoverCSS.top + 'px'; + popoverEle.style.left = popoverCSS.left + 'px'; + + (popoverEle.style)[CSS.transformOrigin] = originY + ' ' + originX; + + // Since the transition starts before styling is done we + // want to wait for the styles to apply before showing the wrapper + popoverWrapperEle.style.opacity = '1'; + } + + iosPositionView(nativeEle: HTMLElement, ev: any) { + let originY = 'top'; + let originX = 'left'; + + let popoverWrapperEle = nativeEle.querySelector('.popover-wrapper'); + + // Popover content width and height + let popoverEle = nativeEle.querySelector('.popover-content'); + let popoverDim = popoverEle.getBoundingClientRect(); + let popoverWidth = popoverDim.width; + let popoverHeight = popoverDim.height; + + // Window body width and height + let bodyWidth = window.innerWidth; + let bodyHeight = window.innerHeight; + + // If ev was passed, use that for target element + let targetDim = ev && ev.target && ev.target.getBoundingClientRect(); + + let targetTop = (targetDim && 'top' in targetDim) ? targetDim.top : (bodyHeight / 2) - (popoverHeight / 2); + let targetLeft = (targetDim && 'left' in targetDim) ? targetDim.left : (bodyWidth / 2); + let targetWidth = targetDim && targetDim.width || 0; + let targetHeight = targetDim && targetDim.height || 0; + + // The arrow that shows above the popover on iOS + var arrowEle = nativeEle.querySelector('.popover-arrow'); + let arrowDim = arrowEle.getBoundingClientRect(); + var arrowWidth = arrowDim.width; + var arrowHeight = arrowDim.height; + + // If no ev was passed, hide the arrow + if (!targetDim) { + arrowEle.style.display = 'none'; + } + + let arrowCSS = { + top: targetTop + targetHeight, + left: targetLeft + (targetWidth / 2) - (arrowWidth / 2) + }; + + let popoverCSS = { + top: targetTop + targetHeight + (arrowHeight - 1), + left: targetLeft + (targetWidth / 2) - (popoverWidth / 2) + }; + + // If the popover left is less than the padding it is off screen + // to the left so adjust it, else if the width of the popover + // exceeds the body width it is off screen to the right so adjust + if (popoverCSS.left < POPOVER_IOS_BODY_PADDING) { + popoverCSS.left = POPOVER_IOS_BODY_PADDING; + } else if (popoverWidth + POPOVER_IOS_BODY_PADDING + popoverCSS.left > bodyWidth) { + popoverCSS.left = bodyWidth - popoverWidth - POPOVER_IOS_BODY_PADDING; + originX = 'right'; + } + + // If the popover when popped down stretches past bottom of screen, + // make it pop up if there's room above + if (targetTop + targetHeight + popoverHeight > bodyHeight && targetTop - popoverHeight > 0) { + arrowCSS.top = targetTop - (arrowHeight + 1); + popoverCSS.top = targetTop - popoverHeight - (arrowHeight - 1); + nativeEle.className = nativeEle.className + ' popover-bottom'; + originY = 'bottom'; + // If there isn't room for it to pop up above the target cut it off + } else if (targetTop + targetHeight + popoverHeight > bodyHeight) { + popoverEle.style.bottom = POPOVER_IOS_BODY_PADDING + '%'; + } + + arrowEle.style.top = arrowCSS.top + 'px'; + arrowEle.style.left = arrowCSS.left + 'px'; + + popoverEle.style.top = popoverCSS.top + 'px'; + popoverEle.style.left = popoverCSS.left + 'px'; + + (popoverEle.style)[CSS.transformOrigin] = originY + ' ' + originX; + + // Since the transition starts before styling is done we + // want to wait for the styles to apply before showing the wrapper + popoverWrapperEle.style.opacity = '1'; + } +} + + +export class PopoverPopIn extends PopoverTransition { + init() { + let ele = this.enteringView.pageRef().nativeElement; + + let backdrop = new Animation(ele.querySelector('ion-backdrop')); + let wrapper = new Animation(ele.querySelector('.popover-wrapper')); + + wrapper.fromTo('opacity', 0.01, 1); + backdrop.fromTo('opacity', 0.01, 0.08); + + this + .easing('ease') + .duration(100) + .add(backdrop) + .add(wrapper); + } + + play() { + nativeRaf(() => { + this.iosPositionView(this.enteringView.pageRef().nativeElement, this.opts.ev); + super.play(); + }); + } +} + + +export class PopoverPopOut extends PopoverTransition { + init() { + let ele = this.leavingView.pageRef().nativeElement; + let backdrop = new Animation(ele.querySelector('ion-backdrop')); + let wrapper = new Animation(ele.querySelector('.popover-wrapper')); + + wrapper.fromTo('opacity', 0.99, 0); + backdrop.fromTo('opacity', 0.08, 0); + + this + .easing('ease') + .duration(500) + .add(backdrop) + .add(wrapper); + } +} + + +export class PopoverMdPopIn extends PopoverTransition { + init() { + let ele = this.enteringView.pageRef().nativeElement; + let content = new Animation(ele.querySelector('.popover-content')); + let viewport = new Animation(ele.querySelector('.popover-viewport')); + + content.fromTo('scale', 0.001, 1); + viewport.fromTo('opacity', 0.01, 1); + + this + .easing('cubic-bezier(0.36,0.66,0.04,1)') + .duration(300) + .add(content) + .add(viewport); + } + + play() { + nativeRaf(() => { + this.mdPositionView(this.enteringView.pageRef().nativeElement, this.opts.ev); + super.play(); + }); + } +} + + +export class PopoverMdPopOut extends PopoverTransition { + init() { + let ele = this.leavingView.pageRef().nativeElement; + let wrapper = new Animation(ele.querySelector('.popover-wrapper')); + + wrapper.fromTo('opacity', 0.99, 0); + + this + .easing('ease') + .duration(500) + .fromTo('opacity', 0.01, 1) + .add(wrapper); + } +} + + +const POPOVER_IOS_BODY_PADDING = 2; +const POPOVER_MD_BODY_PADDING = 12; diff --git a/src/components/toast/toast-component.ts b/src/components/toast/toast-component.ts index c765030ef5..dd3a5d3557 100644 --- a/src/components/toast/toast-component.ts +++ b/src/components/toast/toast-component.ts @@ -1,9 +1,7 @@ import { AfterViewInit, Component, ElementRef, Renderer } from '@angular/core'; -import { Animation } from '../../animations/animation'; import { Config } from '../../config/config'; import { NavParams } from '../../navigation/nav-params'; -import { Transition } from '../../transitions/transition'; import { ViewController } from '../../navigation/view-controller'; @@ -106,195 +104,4 @@ export class ToastCmp implements AfterViewInit { } - -class ToastSlideIn extends Transition { - init() { - // DOM READS - let ele = this.enteringView.pageRef().nativeElement; - const wrapperEle = ele.querySelector('.toast-wrapper'); - let wrapper = new Animation(wrapperEle); - - if (this.enteringView.data && this.enteringView.data.position === TOAST_POSITION_TOP) { - // top - // by default, it is -100% hidden (above the screen) - // so move from that to 10px below top: 0px; - wrapper.fromTo('translateY', '-100%', `${10}px`); - - } else if (this.enteringView.data && this.enteringView.data.position === TOAST_POSITION_MIDDLE) { - // Middle - // just center it and fade it in - let topPosition = Math.floor(ele.clientHeight / 2 - wrapperEle.clientHeight / 2); - // DOM WRITE - wrapperEle.style.top = `${topPosition}px`; - wrapper.fromTo('opacity', 0.01, 1); - - } else { - // bottom - // by default, it is 100% hidden (below the screen), - // so move from that to 10 px above bottom: 0px - wrapper.fromTo('translateY', '100%', `${0 - 10}px`); - } - - this.easing('cubic-bezier(.36,.66,.04,1)').duration(400).add(wrapper); - } -} - -class ToastSlideOut extends Transition { - init() { - let ele = this.leavingView.pageRef().nativeElement; - const wrapperEle = ele.querySelector('.toast-wrapper'); - let wrapper = new Animation(wrapperEle); - - if (this.leavingView.data && this.leavingView.data.position === TOAST_POSITION_TOP) { - // top - // reverse arguments from enter transition - wrapper.fromTo('translateY', `${10}px`, '-100%'); - - } else if (this.leavingView.data && this.leavingView.data.position === TOAST_POSITION_MIDDLE) { - // Middle - // just fade it out - wrapper.fromTo('opacity', 0.99, 0); - - } else { - // bottom - // reverse arguments from enter transition - wrapper.fromTo('translateY', `${0 - 10}px`, '100%'); - } - - this.easing('cubic-bezier(.36,.66,.04,1)').duration(300).add(wrapper); - } -} - -class ToastMdSlideIn extends Transition { - init() { - // DOM reads - let ele = this.enteringView.pageRef().nativeElement; - const wrapperEle = ele.querySelector('.toast-wrapper'); - let wrapper = new Animation(wrapperEle); - - if (this.enteringView.data && this.enteringView.data.position === TOAST_POSITION_TOP) { - // top - // by default, it is -100% hidden (above the screen) - // so move from that to top: 0px; - wrapper.fromTo('translateY', '-100%', `0%`); - - } else if (this.enteringView.data && this.enteringView.data.position === TOAST_POSITION_MIDDLE) { - // Middle - // just center it and fade it in - let topPosition = Math.floor(ele.clientHeight / 2 - wrapperEle.clientHeight / 2); - // DOM WRITE - wrapperEle.style.top = `${topPosition}px`; - wrapper.fromTo('opacity', 0.01, 1); - - } else { - // bottom - // by default, it is 100% hidden (below the screen), - // so move from that to bottom: 0px - wrapper.fromTo('translateY', '100%', `0%`); - } - - this.easing('cubic-bezier(.36,.66,.04,1)').duration(400).add(wrapper); - } -} - -class ToastMdSlideOut extends Transition { - init() { - let ele = this.leavingView.pageRef().nativeElement; - const wrapperEle = ele.querySelector('.toast-wrapper'); - let wrapper = new Animation(wrapperEle); - - if (this.leavingView.data && this.leavingView.data.position === TOAST_POSITION_TOP) { - // top - // reverse arguments from enter transition - wrapper.fromTo('translateY', `${0}%`, '-100%'); - - } else if (this.leavingView.data && this.leavingView.data.position === TOAST_POSITION_MIDDLE) { - // Middle - // just fade it out - wrapper.fromTo('opacity', 0.99, 0); - - } else { - // bottom - // reverse arguments from enter transition - wrapper.fromTo('translateY', `${0}%`, '100%'); - } - - this.easing('cubic-bezier(.36,.66,.04,1)').duration(450).add(wrapper); - } -} - -class ToastWpPopIn extends Transition { - init() { - let ele = this.enteringView.pageRef().nativeElement; - const wrapperEle = ele.querySelector('.toast-wrapper'); - let wrapper = new Animation(wrapperEle); - - if (this.enteringView.data && this.enteringView.data.position === TOAST_POSITION_TOP) { - // top - wrapper.fromTo('opacity', 0.01, 1); - wrapper.fromTo('scale', 1.3, 1); - - } else if (this.enteringView.data && this.enteringView.data.position === TOAST_POSITION_MIDDLE) { - // Middle - // just center it and fade it in - let topPosition = Math.floor(ele.clientHeight / 2 - wrapperEle.clientHeight / 2); - - // DOM WRITE - wrapperEle.style.top = `${topPosition}px`; - wrapper.fromTo('opacity', 0.01, 1); - wrapper.fromTo('scale', 1.3, 1); - - } else { - // bottom - wrapper.fromTo('opacity', 0.01, 1); - wrapper.fromTo('scale', 1.3, 1); - } - - this.easing('cubic-bezier(0,0 0.05,1)').duration(200).add(wrapper); - } -} - -class ToastWpPopOut extends Transition { - init() { - // DOM reads - let ele = this.leavingView.pageRef().nativeElement; - const wrapperEle = ele.querySelector('.toast-wrapper'); - let wrapper = new Animation(wrapperEle); - - if (this.leavingView.data && this.leavingView.data.position === TOAST_POSITION_TOP) { - // top - // reverse arguments from enter transition - wrapper.fromTo('opacity', 0.99, 0); - wrapper.fromTo('scale', 1, 1.3); - - } else if (this.leavingView.data && this.leavingView.data.position === TOAST_POSITION_MIDDLE) { - // Middle - // just fade it out - wrapper.fromTo('opacity', 0.99, 0); - wrapper.fromTo('scale', 1, 1.3); - - } else { - // bottom - // reverse arguments from enter transition - wrapper.fromTo('opacity', 0.99, 0); - wrapper.fromTo('scale', 1, 1.3); - } - - // DOM writes - const EASE: string = 'ease-out'; - const DURATION: number = 150; - this.easing(EASE).duration(DURATION).add(wrapper); - } -} - - -Transition.register('toast-slide-in', ToastSlideIn); -Transition.register('toast-slide-out', ToastSlideOut); -Transition.register('toast-md-slide-in', ToastMdSlideIn); -Transition.register('toast-md-slide-out', ToastMdSlideOut); -Transition.register('toast-wp-slide-out', ToastWpPopOut); -Transition.register('toast-wp-slide-in', ToastWpPopIn); - let toastIds = -1; -const TOAST_POSITION_TOP = 'top'; -const TOAST_POSITION_MIDDLE = 'middle'; diff --git a/src/components/toast/toast-transitions.ts b/src/components/toast/toast-transitions.ts new file mode 100644 index 0000000000..b284721f26 --- /dev/null +++ b/src/components/toast/toast-transitions.ts @@ -0,0 +1,192 @@ +import { Animation } from '../../animations/animation'; +import { Transition } from '../../transitions/transition'; + + +export class ToastSlideIn extends Transition { + init() { + // DOM READS + let ele = this.enteringView.pageRef().nativeElement; + const wrapperEle = ele.querySelector('.toast-wrapper'); + let wrapper = new Animation(wrapperEle); + + if (this.enteringView.data && this.enteringView.data.position === TOAST_POSITION_TOP) { + // top + // by default, it is -100% hidden (above the screen) + // so move from that to 10px below top: 0px; + wrapper.fromTo('translateY', '-100%', `${10}px`); + + } else if (this.enteringView.data && this.enteringView.data.position === TOAST_POSITION_MIDDLE) { + // Middle + // just center it and fade it in + let topPosition = Math.floor(ele.clientHeight / 2 - wrapperEle.clientHeight / 2); + // DOM WRITE + wrapperEle.style.top = `${topPosition}px`; + wrapper.fromTo('opacity', 0.01, 1); + + } else { + // bottom + // by default, it is 100% hidden (below the screen), + // so move from that to 10 px above bottom: 0px + wrapper.fromTo('translateY', '100%', `${0 - 10}px`); + } + + this.easing('cubic-bezier(.36,.66,.04,1)').duration(400).add(wrapper); + } +} + + +export class ToastSlideOut extends Transition { + init() { + let ele = this.leavingView.pageRef().nativeElement; + const wrapperEle = ele.querySelector('.toast-wrapper'); + let wrapper = new Animation(wrapperEle); + + if (this.leavingView.data && this.leavingView.data.position === TOAST_POSITION_TOP) { + // top + // reverse arguments from enter transition + wrapper.fromTo('translateY', `${10}px`, '-100%'); + + } else if (this.leavingView.data && this.leavingView.data.position === TOAST_POSITION_MIDDLE) { + // Middle + // just fade it out + wrapper.fromTo('opacity', 0.99, 0); + + } else { + // bottom + // reverse arguments from enter transition + wrapper.fromTo('translateY', `${0 - 10}px`, '100%'); + } + + this.easing('cubic-bezier(.36,.66,.04,1)').duration(300).add(wrapper); + } +} + + +export class ToastMdSlideIn extends Transition { + init() { + // DOM reads + let ele = this.enteringView.pageRef().nativeElement; + const wrapperEle = ele.querySelector('.toast-wrapper'); + let wrapper = new Animation(wrapperEle); + + if (this.enteringView.data && this.enteringView.data.position === TOAST_POSITION_TOP) { + // top + // by default, it is -100% hidden (above the screen) + // so move from that to top: 0px; + wrapper.fromTo('translateY', '-100%', `0%`); + + } else if (this.enteringView.data && this.enteringView.data.position === TOAST_POSITION_MIDDLE) { + // Middle + // just center it and fade it in + let topPosition = Math.floor(ele.clientHeight / 2 - wrapperEle.clientHeight / 2); + // DOM WRITE + wrapperEle.style.top = `${topPosition}px`; + wrapper.fromTo('opacity', 0.01, 1); + + } else { + // bottom + // by default, it is 100% hidden (below the screen), + // so move from that to bottom: 0px + wrapper.fromTo('translateY', '100%', `0%`); + } + + this.easing('cubic-bezier(.36,.66,.04,1)').duration(400).add(wrapper); + } +} + + +export class ToastMdSlideOut extends Transition { + init() { + let ele = this.leavingView.pageRef().nativeElement; + const wrapperEle = ele.querySelector('.toast-wrapper'); + let wrapper = new Animation(wrapperEle); + + if (this.leavingView.data && this.leavingView.data.position === TOAST_POSITION_TOP) { + // top + // reverse arguments from enter transition + wrapper.fromTo('translateY', `${0}%`, '-100%'); + + } else if (this.leavingView.data && this.leavingView.data.position === TOAST_POSITION_MIDDLE) { + // Middle + // just fade it out + wrapper.fromTo('opacity', 0.99, 0); + + } else { + // bottom + // reverse arguments from enter transition + wrapper.fromTo('translateY', `${0}%`, '100%'); + } + + this.easing('cubic-bezier(.36,.66,.04,1)').duration(450).add(wrapper); + } +} + + +export class ToastWpPopIn extends Transition { + init() { + let ele = this.enteringView.pageRef().nativeElement; + const wrapperEle = ele.querySelector('.toast-wrapper'); + let wrapper = new Animation(wrapperEle); + + if (this.enteringView.data && this.enteringView.data.position === TOAST_POSITION_TOP) { + // top + wrapper.fromTo('opacity', 0.01, 1); + wrapper.fromTo('scale', 1.3, 1); + + } else if (this.enteringView.data && this.enteringView.data.position === TOAST_POSITION_MIDDLE) { + // Middle + // just center it and fade it in + let topPosition = Math.floor(ele.clientHeight / 2 - wrapperEle.clientHeight / 2); + + // DOM WRITE + wrapperEle.style.top = `${topPosition}px`; + wrapper.fromTo('opacity', 0.01, 1); + wrapper.fromTo('scale', 1.3, 1); + + } else { + // bottom + wrapper.fromTo('opacity', 0.01, 1); + wrapper.fromTo('scale', 1.3, 1); + } + + this.easing('cubic-bezier(0,0 0.05,1)').duration(200).add(wrapper); + } +} + + +export class ToastWpPopOut extends Transition { + init() { + // DOM reads + let ele = this.leavingView.pageRef().nativeElement; + const wrapperEle = ele.querySelector('.toast-wrapper'); + let wrapper = new Animation(wrapperEle); + + if (this.leavingView.data && this.leavingView.data.position === TOAST_POSITION_TOP) { + // top + // reverse arguments from enter transition + wrapper.fromTo('opacity', 0.99, 0); + wrapper.fromTo('scale', 1, 1.3); + + } else if (this.leavingView.data && this.leavingView.data.position === TOAST_POSITION_MIDDLE) { + // Middle + // just fade it out + wrapper.fromTo('opacity', 0.99, 0); + wrapper.fromTo('scale', 1, 1.3); + + } else { + // bottom + // reverse arguments from enter transition + wrapper.fromTo('opacity', 0.99, 0); + wrapper.fromTo('scale', 1, 1.3); + } + + // DOM writes + const EASE: string = 'ease-out'; + const DURATION: number = 150; + this.easing(EASE).duration(DURATION).add(wrapper); + } +} + + +const TOAST_POSITION_TOP = 'top'; +const TOAST_POSITION_MIDDLE = 'middle'; diff --git a/src/config/config.ts b/src/config/config.ts index 05f1773291..a8f57eb6b2 100644 --- a/src/config/config.ts +++ b/src/config/config.ts @@ -9,7 +9,6 @@ import { OpaqueToken } from '@angular/core'; import { Platform } from '../platform/platform'; import { QueryParams } from '../platform/query-params'; import { isObject, isDefined, isFunction, isArray } from '../util/util'; -import { setupModeConfig } from './modes'; /** * @name Config @@ -115,7 +114,8 @@ export class Config { private _c: any = {}; private _s: any; private _qp: QueryParams; - + private _modes: any = {}; + private _trns: any = {}; /** * @private @@ -182,7 +182,7 @@ export class Config { if (isDefined(configObj[key])) { userPlatformValue = configObj[key]; } - configObj = Config.getModeConfig(configObj.mode); + configObj = this.getModeConfig(configObj.mode); if (configObj && isDefined(configObj[key])) { userPlatformModeValue = configObj[key]; } @@ -190,7 +190,7 @@ export class Config { } // get default platform's setting - configObj = Platform.get(activePlatformKeys[i]); + configObj = this.platform.getPlatformConfig(activePlatformKeys[i]); if (configObj && configObj.settings) { if (isDefined(configObj.settings[key])) { @@ -198,7 +198,7 @@ export class Config { platformValue = configObj.settings[key]; } - configObj = Config.getModeConfig(configObj.settings.mode); + configObj = this.getModeConfig(configObj.settings.mode); if (configObj && isDefined(configObj[key])) { // found setting for this platform's mode platformModeValue = configObj[key]; @@ -210,7 +210,7 @@ export class Config { } - configObj = Config.getModeConfig(this._s.mode); + configObj = this.getModeConfig(this._s.mode); if (configObj && isDefined(configObj[key])) { userDefaultModeValue = configObj[key]; } @@ -352,26 +352,36 @@ export class Config { /** * @private */ - static setModeConfig(mode: string, config: any) { - modeConfigs[mode] = config; + setModeConfig(modeName: string, modeConfig: any) { + this._modes[modeName] = modeConfig; } /** * @private */ - static getModeConfig(mode: string) { - return modeConfigs[mode] || null; + getModeConfig(modeName: string): any { + return this._modes[modeName] || null; + } + + /** + * @private + */ + setTransition(trnsName: string, trnsClass: any) { + this._trns[trnsName] = trnsClass; + } + + /** + * @private + */ + getTransition(trnsName: string): any { + return this._trns[trnsName] || null; } } -let modeConfigs: any = {}; - -export const UserConfig = new OpaqueToken('USERCONFIG'); +export const ConfigToken = new OpaqueToken('USERCONFIG'); export function setupConfig(userConfig: any, queryParams: QueryParams, platform: Platform): Config { - setupModeConfig(); - const config = new Config(); config.init(userConfig, queryParams, platform); return config; diff --git a/src/config/modes.ts b/src/config/mode-registry.ts similarity index 76% rename from src/config/modes.ts rename to src/config/mode-registry.ts index dea0af9cf1..c20bf16149 100644 --- a/src/config/modes.ts +++ b/src/config/mode-registry.ts @@ -1,12 +1,7 @@ - -import { Config } from './config'; -import { PageTransition } from '../transitions/page-transition'; -import { IOSTransition } from '../transitions/transition-ios'; -import { MDTransition } from '../transitions/transition-md'; -import { WPTransition } from '../transitions/transition-wp'; +import { Config } from '../config/config'; -const MODE_IOS: any = { +export const MODE_IOS: any = { activator: 'highlight', actionSheetEnter: 'action-sheet-slide-in', @@ -49,7 +44,7 @@ const MODE_IOS: any = { }; -const MODE_MD: any = { +export const MODE_MD: any = { activator: 'ripple', actionSheetEnter: 'action-sheet-md-slide-in', @@ -92,7 +87,7 @@ const MODE_MD: any = { }; -const MODE_WP: any = { +export const MODE_WP: any = { activator: 'highlight', actionSheetEnter: 'action-sheet-wp-slide-in', @@ -135,16 +130,15 @@ const MODE_WP: any = { }; -export function setupModeConfig() { - // iOS Mode Settings - Config.setModeConfig('ios', MODE_IOS); - PageTransition.register('ios-transition', IOSTransition); +export function registerModeConfigs(config: Config) { + return function() { + // iOS Mode Settings + config.setModeConfig('ios', MODE_IOS); - // Material Design Mode Settings - Config.setModeConfig('md', MODE_MD); - PageTransition.register('md-transition', MDTransition); + // Material Design Mode Settings + config.setModeConfig('md', MODE_MD); - // Windows Mode Settings - Config.setModeConfig('wp', MODE_WP); - PageTransition.register('wp-transition', WPTransition); + // Windows Mode Settings + config.setModeConfig('wp', MODE_WP); + }; } diff --git a/src/config/test/config.spec.ts b/src/config/test/config.spec.ts index 3a9259f68a..eaeea74dd8 100644 --- a/src/config/test/config.spec.ts +++ b/src/config/test/config.spec.ts @@ -1,94 +1,73 @@ import { Config } from '../config'; import { Platform } from '../../platform/platform'; import { QueryParams } from '../../platform/query-params'; -import { setupPlatformRegistry } from '../../platform/registry'; -import { setupModeConfig } from '../modes'; +import { PLATFORM_CONFIGS } from '../../platform/platform-registry'; +import { registerModeConfigs } from '../mode-registry'; describe('Config', () => { it('should set activator setting to none for old Android Browser on a linux device', () => { - let config = new Config(); - let qp = new QueryParams(''); - let platform = new Platform(); platform.setUserAgent('Mozilla/5.0 (Linux; U; Android 4.2.2; nl-nl; GT-I9505 Build/JDQ39) AppleWebKit/534.30 (KHTML, like Gecko) Version/4.0 Mobile Safari/534.30'); platform.setNavigatorPlatform('linux'); platform.setQueryParams(qp); - platform.load(); + platform.init(); config.init(null, qp, platform); expect(config.get('activator')).toEqual('none'); }); it('should set activator setting to ripple for Android dev tools simulation on a mac', () => { - let config = new Config(); - let qp = new QueryParams(''); - let platform = new Platform(); platform.setUserAgent('Mozilla/5.0 (Linux; U; Android 4.2.2; nl-nl; GT-I9505 Build/JDQ39) AppleWebKit/534.30 (KHTML, like Gecko) Version/4.0 Mobile Safari/534.30'); platform.setNavigatorPlatform('MacIntel'); platform.setQueryParams(qp); - platform.load(); + platform.init(); config.init(null, qp, platform); expect(config.get('activator')).toEqual('ripple'); }); it('should set activator setting to none for Android Chrome versions below v36 on a linux device', () => { - let config = new Config(); - let qp = new QueryParams(''); - let platform = new Platform(); platform.setUserAgent('Mozilla/5.0 (Linux; Android 4.2.2; GT-I9505 Build/JDQ39) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/35.0.1650.59 Mobile Safari/537.36'); platform.setNavigatorPlatform('linux'); platform.setQueryParams(qp); - platform.load(); + platform.init(); config.init(null, qp, platform); expect(config.get('activator')).toEqual('none'); }); it('should set activator setting to ripple for Android Chrome v36 and above on a linux device', () => { - let config = new Config(); - let qp = new QueryParams(''); - let platform = new Platform(); platform.setUserAgent('Mozilla/5.0 (Linux; Android 4.2.2; GT-I9505 Build/JDQ39) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/36.0.1650.59 Mobile Safari/537.36'); platform.setNavigatorPlatform('linux'); platform.setQueryParams(qp); - platform.load(); + platform.init(); config.init(null, qp, platform); expect(config.get('activator')).toEqual('ripple'); }); it('should set activator setting to ripple for Android v5.0 and above on a linux device not using Chrome', () => { - let config = new Config(); - let qp = new QueryParams(''); - let platform = new Platform(); platform.setUserAgent('Mozilla/5.0 (Android 5.0; Mobile; rv:41.0) Gecko/41.0 Firefox/41.0'); platform.setNavigatorPlatform('linux'); platform.setQueryParams(qp); - platform.load(); + platform.init(); config.init(null, qp, platform); expect(config.get('activator')).toEqual('ripple'); }); it('should set activator setting to none for Android versions below v5.0 on a linux device not using Chrome', () => { - let config = new Config(); - let qp = new QueryParams(''); - let platform = new Platform(); platform.setUserAgent('Mozilla/5.0 (Android 4.4; Mobile; rv:41.0) Gecko/41.0 Firefox/41.0'); platform.setNavigatorPlatform('linux'); platform.setQueryParams(qp); - platform.load(); + platform.init(); config.init(null, qp, platform); expect(config.get('activator')).toEqual('none'); }); it('should override mode settings', () => { - let config = new Config(); - let qp = new QueryParams(''); - let platform = new Platform(); platform._platforms = ['ios']; config.init({ mode: 'md' @@ -99,9 +78,6 @@ describe('Config', () => { }); it('should override mode settings from platforms setting', () => { - let config = new Config(); - let qp = new QueryParams(''); - let platform = new Platform(); platform._platforms = ['ios']; config.init({ platforms: { @@ -116,9 +92,7 @@ describe('Config', () => { }); it('should get boolean value from querystring', () => { - let config = new Config(); - let qp = new QueryParams('http://biff.com/?ionicanimate=true'); - let platform = new Platform(); + qp = new QueryParams('http://biff.com/?ionicanimate=true'); config.init(null, qp, platform); expect(config.get('animate')).toEqual(true); @@ -130,9 +104,7 @@ describe('Config', () => { }); it('should get value from case insensitive querystring key', () => { - let config = new Config(); - let qp = new QueryParams('http://biff.com/?ionicConfigKey=b'); - let platform = new Platform(); + qp = new QueryParams('http://biff.com/?ionicConfigKey=b'); config.init({ mode: 'a' }, qp, platform); @@ -141,9 +113,7 @@ describe('Config', () => { }); it('should get value from querystring', () => { - let config = new Config(); - let qp = new QueryParams('http://biff.com/?ionicmode=modeB'); - let platform = new Platform(); + qp = new QueryParams('http://biff.com/?ionicmode=modeB'); config.init({ mode: 'modeA' }, qp, platform); @@ -152,9 +122,6 @@ describe('Config', () => { }); it('should override mode platform', () => { - let config = new Config(); - let qp = new QueryParams(''); - let platform = new Platform(); platform._platforms = ['mobile']; config.init({ mode: 'modeA', @@ -172,9 +139,6 @@ describe('Config', () => { }); it('should override mode', () => { - let config = new Config(); - let qp = new QueryParams(''); - let platform = new Platform(); platform._platforms = ['core']; config.init({ mode: 'modeA' @@ -184,9 +148,6 @@ describe('Config', () => { }); it('should get user settings after user platform settings', () => { - let config = new Config(); - let qp = new QueryParams(''); - let platform = new Platform(); platform._platforms = ['ios']; config.init({ hoverCSS: true @@ -196,9 +157,6 @@ describe('Config', () => { }); it('should get md mode for core platform', () => { - let config = new Config(); - let qp = new QueryParams(''); - let platform = new Platform(); platform._platforms = ['core']; config.init(null, qp, platform); @@ -206,9 +164,6 @@ describe('Config', () => { }); it('should get ios mode for ipad platform', () => { - let config = new Config(); - let qp = new QueryParams(''); - let platform = new Platform(); platform._platforms = ['mobile', 'ios', 'ipad', 'tablet']; config.init(null, qp, platform); @@ -216,9 +171,6 @@ describe('Config', () => { }); it('should get md mode for windows platform', () => { - let config = new Config(); - let qp = new QueryParams(''); - let platform = new Platform(); platform._platforms = ['mobile', 'windows']; config.init(null, qp, platform); @@ -226,9 +178,6 @@ describe('Config', () => { }); it('should get md mode for android platform', () => { - let config = new Config(); - let qp = new QueryParams(''); - let platform = new Platform(); platform._platforms = ['mobile', 'android']; config.init(null, qp, platform); @@ -236,9 +185,6 @@ describe('Config', () => { }); it('should override ios mode config with user platform setting', () => { - let config = new Config(); - let qp = new QueryParams(''); - let platform = new Platform(); platform._platforms = ['ios']; config.init({ tabsPlacement: 'hide', @@ -253,9 +199,6 @@ describe('Config', () => { }); it('should override ios mode config with user setting', () => { - let config = new Config(); - let qp = new QueryParams(''); - let platform = new Platform(); platform._platforms = ['ios']; config.init({ tabsPlacement: 'top' @@ -265,9 +208,6 @@ describe('Config', () => { }); it('should get setting from md mode', () => { - let config = new Config(); - let qp = new QueryParams(''); - let platform = new Platform(); platform._platforms = ['android']; config.init(null, qp, platform); @@ -275,9 +215,6 @@ describe('Config', () => { }); it('should get setting from ios mode', () => { - let config = new Config(); - let qp = new QueryParams(''); - let platform = new Platform(); platform._platforms = ['ios']; config.init(null, qp, platform); @@ -285,9 +222,6 @@ describe('Config', () => { }); it('should set/get platform setting from set()', () => { - let config = new Config(); - let qp = new QueryParams(''); - let platform = new Platform(); platform._platforms = ['ios']; config.init(null, qp, platform); @@ -298,9 +232,6 @@ describe('Config', () => { }); it('should set/get setting from set()', () => { - let config = new Config(); - let qp = new QueryParams(''); - let platform = new Platform(); platform._platforms = ['ios']; config.init(null, qp, platform); @@ -310,9 +241,6 @@ describe('Config', () => { }); it('should set ios platform settings from settings()', () => { - let config = new Config(); - let qp = new QueryParams(''); - let platform = new Platform(); platform._platforms = ['ios']; config.init(null, qp, platform); @@ -324,9 +252,6 @@ describe('Config', () => { }); it('should set/get mobile setting even w/ higher priority ios', () => { - let config = new Config(); - let qp = new QueryParams(''); - let platform = new Platform(); platform._platforms = ['mobile', 'ios']; config.init({ @@ -342,9 +267,6 @@ describe('Config', () => { }); it('should set/get mobile setting even w/ higher priority ios', () => { - let config = new Config(); - let qp = new QueryParams(''); - let platform = new Platform(); platform._platforms = ['mobile', 'ios']; config.init({ @@ -360,9 +282,6 @@ describe('Config', () => { }); it('should set/get android setting w/ higher priority than mobile', () => { - let config = new Config(); - let qp = new QueryParams(''); - let platform = new Platform(); platform._platforms = ['mobile', 'android']; config.init({ key: 'defaultValue', @@ -380,9 +299,6 @@ describe('Config', () => { }); it('should set/get ios setting w/ platforms set', () => { - let config = new Config(); - let qp = new QueryParams(''); - let platform = new Platform(); platform._platforms = ['ios']; config.init(null, qp, platform); @@ -402,7 +318,6 @@ describe('Config', () => { }); it('should set/get default setting w/ platforms set, but no platform match', () => { - let config = new Config(); config.settings({ key: 'defaultValue', platforms: { @@ -419,7 +334,6 @@ describe('Config', () => { }); it('should set setting object', () => { - let config = new Config(); config.settings({ name: 'Doc Brown', occupation: 'Weather Man' @@ -432,7 +346,6 @@ describe('Config', () => { }); it('should get null setting', () => { - let config = new Config(); config.init(null, null, null); expect(config.get('name')).toEqual(null); expect(config.get('name')).toEqual(null); @@ -441,7 +354,6 @@ describe('Config', () => { }); it('should set/get single setting', () => { - let config = new Config(); config.init(null, null, null); config.set('name', 'Doc Brown'); config.set('occupation', 'Weather Man'); @@ -453,7 +365,6 @@ describe('Config', () => { }); it('should init w/ given config settings', () => { - let config = new Config(); config.init({ name: 'Doc Brown', occupation: 'Weather Man' @@ -463,7 +374,6 @@ describe('Config', () => { }); it('should get a fallback value', () => { - let config = new Config(); config.init({ name: 'Doc Brown' }, null, null); @@ -472,7 +382,6 @@ describe('Config', () => { }); it('should get a boolean value with a boolean config value', () => { - let config = new Config(); config.init({ key1: true, key2: false @@ -482,7 +391,6 @@ describe('Config', () => { }); it('should get a boolean value with a string config value', () => { - let config = new Config(); config.init({ key1: 'true', key2: 'false', @@ -496,7 +404,6 @@ describe('Config', () => { }); it('should get a boolean value with a number config value', () => { - let config = new Config(); config.init({ key1: 0, key2: 1, @@ -507,7 +414,6 @@ describe('Config', () => { }); it('should get a number value with a number config value', () => { - let config = new Config(); config.init({ key: 6 }, null, null); @@ -515,7 +421,6 @@ describe('Config', () => { }); it('should get a number value with a string config value', () => { - let config = new Config(); config.init({ key: '6', numThenString: '6baymax', @@ -527,7 +432,6 @@ describe('Config', () => { }); it('should get a number NaN value with a NaN config value', () => { - let config = new Config(); config.init({ allString: 'allstring', imNull: null, @@ -540,7 +444,6 @@ describe('Config', () => { }); it('should get a number fallback value with a NaN config value', () => { - let config = new Config(); config.init({ allString: 'allstring', imNull: null, @@ -554,7 +457,6 @@ describe('Config', () => { }); it('should get settings object', () => { - let config = new Config(); config.init({ name: 'Doc Brown', occupation: 'Weather Man' @@ -608,9 +510,16 @@ describe('Config', () => { expect(config.settings()).toEqual({}); }); + let platform: Platform; + let config: Config; + let qp: QueryParams; + beforeEach(() => { - setupModeConfig(); - setupPlatformRegistry(); + config = new Config(); + qp = new QueryParams(''); + platform = new Platform(); + platform.setPlatformConfigs(PLATFORM_CONFIGS); + registerModeConfigs(config); }); }); diff --git a/src/index.ts b/src/index.ts index 61725f1f0d..431ebfef14 100644 --- a/src/index.ts +++ b/src/index.ts @@ -8,8 +8,6 @@ export * from './gestures/slide-edge-gesture'; export * from './gestures/slide-gesture'; export * from './gestures/gesture-controller'; -export * from './config/config'; -export * from './platform/platform'; export * from './storage/storage'; export * from './storage/sql'; export * from './storage/local-storage'; diff --git a/src/module.ts b/src/module.ts index 69e058ad33..ce524270e8 100644 --- a/src/module.ts +++ b/src/module.ts @@ -1,5 +1,5 @@ -import { ANALYZE_FOR_ENTRY_COMPONENTS, APP_INITIALIZER, ModuleWithProviders, NgModule, NgZone } from '@angular/core'; -import { Location, LocationStrategy, HashLocationStrategy } from '@angular/common'; +import { ANALYZE_FOR_ENTRY_COMPONENTS, APP_INITIALIZER, Inject, ModuleWithProviders, NgModule, NgZone, Optional } from '@angular/core'; +import { APP_BASE_HREF, Location, LocationStrategy, HashLocationStrategy, PathLocationStrategy, PlatformLocation } from '@angular/common'; import { BrowserModule, HAMMER_GESTURE_CONFIG } from '@angular/platform-browser'; import { FormsModule, ReactiveFormsModule } from '@angular/forms'; import { HttpModule } from '@angular/http'; @@ -10,10 +10,9 @@ import { HttpModule } from '@angular/http'; import { ActionSheetController } from './components/action-sheet/action-sheet'; import { AlertController } from './components/alert/alert'; import { App } from './components/app/app'; -import { Config, UserConfig, setupConfig } from './config/config'; -import { DeepLinker, setupDeepLinker, UserDeepLinkConfig } from './navigation/deep-linker'; +import { Config, ConfigToken, setupConfig } from './config/config'; +import { DeepLinker, setupDeepLinker } from './navigation/deep-linker'; import { setupProvideEvents } from './util/events'; -import { FeatureDetect } from './util/feature-detect'; import { Form } from './util/form'; import { GestureController } from './gestures/gesture-controller'; import { IonicGestureConfig } from './gestures/gesture-config'; @@ -22,15 +21,18 @@ import { LoadingController } from './components/loading/loading'; import { MenuController } from './components/menu/menu-controller'; import { ModalController } from './components/modal/modal'; import { PickerController } from './components/picker/picker'; -import { Platform, setupPlatform, UserAgent, UserNavigatorPlatform, UserDir, UserLang } from './platform/platform'; +import { Platform, setupPlatform, UserAgentToken, NavigatorPlatformToken, DocumentDirToken, DocLangToken } from './platform/platform'; +import { PlatformConfigToken, providePlatformConfigs } from './platform/platform-registry'; import { PopoverController } from './components/popover/popover'; -import { QueryParams, setupQueryParams, UserUrl } from './platform/query-params'; +import { QueryParams, setupQueryParams, UrlToken } from './platform/query-params'; import { TapClick, setupTapClick } from './components/tap-click/tap-click'; import { ToastController } from './components/toast/toast'; import { Translate } from './translation/translate'; +import { registerModeConfigs } from './config/mode-registry'; +import { registerTransitions } from './transitions/transition-registry'; import { TransitionController } from './transitions/transition-controller'; -import { UserRoot } from './components/app/app-root'; -import { UrlSerializer } from './navigation/url-serializer'; +import { AppRootToken } from './components/app/app-root'; +import { UrlSerializer, setupUrlSerializer, DeepLinkConfigToken } from './navigation/url-serializer'; /** * Import Overlay Entry Components @@ -48,11 +50,13 @@ import { ToastCmp } from './components/toast/toast-component'; /** * Export Providers */ -export { DeepLinker, UserDeepLinkConfig } from './navigation/deep-linker'; +export { Config, setupConfig, ConfigToken } from './config/config'; +export { Platform, setupPlatform, UserAgentToken, DocumentDirToken, DocLangToken, NavigatorPlatformToken } from './platform/platform'; +export { DeepLinker } from './navigation/deep-linker'; export { NavController } from './navigation/nav-controller'; export { NavParams } from './navigation/nav-params'; export { NavLink, NavOptions, DeepLink, DeepLinkConfig } from './navigation/nav-util'; -export { UrlSerializer } from './navigation/url-serializer'; +export { UrlSerializer, DeepLinkConfigToken } from './navigation/url-serializer'; export { ViewController } from './navigation/view-controller'; @@ -82,65 +86,45 @@ export { ViewController } from './navigation/view-controller'; }) export class IonicModule { - static forRoot(userAppRoot: any, userConfig?: any, userDeepLinkConfig?: any): ModuleWithProviders { + static forRoot(appRoot: any, config: any = null, deepLinkConfig: any = null): ModuleWithProviders { return { ngModule: IonicModule, providers: [ - { provide: ANALYZE_FOR_ENTRY_COMPONENTS, useValue: userAppRoot, multi: true }, - { provide: APP_INITIALIZER, useFactory: setupTapClick, - deps: [ - Config, - App, - NgZone - ], - multi: true - }, - { provide: APP_INITIALIZER, useFactory: setupProvideEvents, deps: [ Platform ], multi: true }, - { provide: Config, useFactory: setupConfig, - deps: [ - UserConfig, - QueryParams, - Platform - ] - }, - { provide: DeepLinker, useFactory: setupDeepLinker, - deps: [ - App, - UrlSerializer, - Location - ] - }, - { provide: HAMMER_GESTURE_CONFIG, useClass: IonicGestureConfig }, - { provide: LocationStrategy, useClass: HashLocationStrategy }, - { provide: Platform, useFactory: setupPlatform, - deps: [ - QueryParams, - UserAgent, - UserNavigatorPlatform, - UserDir, - UserLang, - NgZone - ] - }, - { provide: QueryParams, useFactory: setupQueryParams, - deps: [ - UserUrl - ] - }, - { provide: UserAgent, useFactory: getWindowUserAgent }, - { provide: UserDir, useFactory: getDocumentDir }, - { provide: UserLang, useFactory: getDocumentLang }, - { provide: UserNavigatorPlatform, useFactory: getWindowPlatform }, - { provide: UserRoot, useValue: userAppRoot }, - { provide: UserUrl, useFactory: getWindowLocation }, - { provide: UserConfig, useValue: userConfig }, - { provide: UserDeepLinkConfig, useValue: userDeepLinkConfig }, + // useValue: bootstrap values + { provide: AppRootToken, useValue: appRoot }, + { provide: ConfigToken, useValue: config }, + { provide: DeepLinkConfigToken, useValue: deepLinkConfig }, + // useFactory: user values + { provide: UserAgentToken, useFactory: provideUserAgent }, + { provide: DocumentDirToken, useFactory: provideDocumentDirection }, + { provide: DocLangToken, useFactory: provideDocumentLang }, + { provide: NavigatorPlatformToken, useFactory: provideNavigatorPlatform }, + { provide: UrlToken, useFactory: provideLocationHref }, + { provide: PlatformConfigToken, useFactory: providePlatformConfigs }, + + // useFactory: ionic core providers + { provide: QueryParams, useFactory: setupQueryParams, deps: [ UrlToken ] }, + { provide: Platform, useFactory: setupPlatform, deps: [ PlatformConfigToken, QueryParams, UserAgentToken, NavigatorPlatformToken, DocumentDirToken, DocLangToken, NgZone ] }, + { provide: Config, useFactory: setupConfig, deps: [ ConfigToken, QueryParams, Platform ] }, + + // useFactory: ionic app initializers + { provide: APP_INITIALIZER, useFactory: registerModeConfigs, deps: [ Config ], multi: true }, + { provide: APP_INITIALIZER, useFactory: registerTransitions, deps: [ Config ], multi: true }, + { provide: APP_INITIALIZER, useFactory: setupProvideEvents, deps: [ Platform ], multi: true }, + { provide: APP_INITIALIZER, useFactory: setupTapClick, deps: [ Config, App, NgZone ], multi: true }, + + // useClass + { provide: HAMMER_GESTURE_CONFIG, useClass: IonicGestureConfig }, + + // useValue + { provide: ANALYZE_FOR_ENTRY_COMPONENTS, useValue: appRoot, multi: true }, + + // ionic providers ActionSheetController, AlertController, App, Form, - FeatureDetect, GestureController, Keyboard, LoadingController, @@ -153,7 +137,10 @@ export class IonicModule { ToastController, Translate, TransitionController, - UrlSerializer + + { provide: LocationStrategy, useFactory: provideLocationStrategy, deps: [ PlatformLocation, [ new Inject(APP_BASE_HREF), new Optional()], Config ] }, + { provide: UrlSerializer, useFactory: setupUrlSerializer, deps: [ DeepLinkConfigToken ] }, + { provide: DeepLinker, useFactory: setupDeepLinker, deps: [ App, UrlSerializer, Location ] }, ] }; } @@ -163,34 +150,44 @@ export class IonicModule { /** * @private */ -export function getWindowUserAgent() { +export function provideLocationStrategy(platformLocationStrategy: PlatformLocation, + baseHref: string, config: Config) { + return config.get('locationStrategy') === 'path' ? + new PathLocationStrategy(platformLocationStrategy, baseHref) : + new HashLocationStrategy(platformLocationStrategy, baseHref); +} + +/** + * @private + */ +export function provideUserAgent() { return window && window.navigator.userAgent; } /** * @private */ -export function getWindowPlatform() { +export function provideNavigatorPlatform() { return window && window.navigator.platform; } /** * @private */ -export function getWindowLocation() { +export function provideLocationHref() { return window && window.location.href; } /** * @private */ -export function getDocumentDir() { +export function provideDocumentDirection() { return document && document.documentElement.dir; } /** * @private */ -export function getDocumentLang() { +export function provideDocumentLang() { return document && document.documentElement.lang; } diff --git a/src/navigation/deep-linker.ts b/src/navigation/deep-linker.ts index 0825cb2212..98f2637ffe 100644 --- a/src/navigation/deep-linker.ts +++ b/src/navigation/deep-linker.ts @@ -1,4 +1,3 @@ -import { Injectable, OpaqueToken } from '@angular/core'; import { Location } from '@angular/common'; import { App } from '../components/app/app'; @@ -30,7 +29,6 @@ import { ViewController } from './view-controller'; */ -@Injectable() export class DeepLinker { segments: NavSegment[] = []; history: string[] = []; @@ -415,9 +413,6 @@ export function setupDeepLinker(app: App, serializer: UrlSerializer, location: L } -export const UserDeepLinkConfig = new OpaqueToken('USERLINKS'); - - export function normalizeUrl(browserUrl: string): string { browserUrl = browserUrl.trim(); if (browserUrl.charAt(0) !== '/') { diff --git a/src/navigation/url-serializer.ts b/src/navigation/url-serializer.ts index b9d9327bc6..88f11be58d 100644 --- a/src/navigation/url-serializer.ts +++ b/src/navigation/url-serializer.ts @@ -1,15 +1,13 @@ -import { Inject, Injectable } from '@angular/core'; +import { OpaqueToken } from '@angular/core'; import { DeepLinkConfig, NavLink, NavSegment } from './nav-util'; import { isArray, isBlank, isPresent, pascalCaseToDashCase } from '../util/util'; -import { UserDeepLinkConfig } from './deep-linker'; -@Injectable() export class UrlSerializer { links: NavLink[]; - constructor(@Inject(UserDeepLinkConfig) config: DeepLinkConfig) { + constructor(config: DeepLinkConfig) { if (config && isArray(config.links)) { this.links = normalizeLinks(config.links); @@ -265,3 +263,9 @@ function sortConfigLinks(a: NavLink, b: NavLink) { } const URL_REPLACE_REG = /\s+|\?|\!|\$|\,|\.|\+|\"|\'|\*|\^|\||\/|\\|\[|\]|#|%|`|>|<|;|:|@|&|=/g; + +export const DeepLinkConfigToken = new OpaqueToken('USERLINKS'); + +export function setupUrlSerializer(userDeepLinkConfig: any): UrlSerializer { + return new UrlSerializer(userDeepLinkConfig); +} diff --git a/src/platform/platform-registry.ts b/src/platform/platform-registry.ts new file mode 100644 index 0000000000..74da3fd4af --- /dev/null +++ b/src/platform/platform-registry.ts @@ -0,0 +1,234 @@ +import { OpaqueToken } from '@angular/core'; + +import { Platform, PlatformConfig } from './platform'; +import { windowLoad } from '../util/dom'; + + +export const PLATFORM_CONFIGS: {[key: string]: PlatformConfig} = { + + /** + * core + */ + core: { + settings: { + mode: 'md', + keyboardHeight: 290 + } + }, + + /** + * mobile + */ + mobile: {}, + + /** + * phablet + */ + phablet: { + isMatch(p: Platform) { + let smallest = Math.min(p.width(), p.height()); + let largest = Math.max(p.width(), p.height()); + return (smallest > 390 && smallest < 520) && + (largest > 620 && largest < 800); + } + }, + + /** + * tablet + */ + tablet: { + isMatch(p: Platform) { + let smallest = Math.min(p.width(), p.height()); + let largest = Math.max(p.width(), p.height()); + return (smallest > 460 && smallest < 820) && + (largest > 780 && largest < 1400); + } + }, + + /** + * android + */ + android: { + superset: 'mobile', + subsets: [ + 'phablet', + 'tablet' + ], + settings: { + activator: function(p: Platform): string { + // md mode defaults to use ripple activator + // however, under-powered devices shouldn't use ripple + // if this a linux device, and is using Android Chrome v36 (Android 5.0) + // or above then use ripple, otherwise do not use a ripple effect + if (p.testNavigatorPlatform('linux')) { + let chromeVersion = p.matchUserAgentVersion(/Chrome\/(\d+).(\d+)?/); + if (chromeVersion) { + // linux android device using modern android chrome browser gets ripple + return (parseInt(chromeVersion.major, 10) < 36 ? 'none' : 'ripple'); + } + // linux android device not using chrome browser checks just android's version + if (p.version().major < 5) { + return 'none'; + } + } + + // fallback to always use ripple + return 'ripple'; + }, + autoFocusAssist: 'immediate', + hoverCSS: false, + keyboardHeight: 300, + mode: 'md', + }, + isMatch(p: Platform) { + return p.isPlatformMatch('android', ['android', 'silk'], ['windows phone']); + }, + versionParser(p: Platform) { + return p.matchUserAgentVersion(/Android (\d+).(\d+)?/); + } + }, + + /** + * ios + */ + ios: { + superset: 'mobile', + subsets: [ + 'ipad', + 'iphone' + ], + settings: { + autoFocusAssist: 'delay', + hoverCSS: false, + inputBlurring: isIOSDevice, + inputCloning: isIOSDevice, + keyboardHeight: 300, + mode: 'ios', + scrollAssist: isIOSDevice, + statusbarPadding: !!((window).cordova), + swipeBackEnabled: isIOSDevice, + swipeBackThreshold: 40, + tapPolyfill: isIOSDevice, + virtualScrollEventAssist: !(window.indexedDB), + canDisableScroll: !!(window.indexedDB), + }, + isMatch(p: Platform) { + return p.isPlatformMatch('ios', ['iphone', 'ipad', 'ipod'], ['windows phone']); + }, + versionParser(p: Platform) { + return p.matchUserAgentVersion(/OS (\d+)_(\d+)?/); + } + }, + + /** + * ipad + */ + ipad: { + superset: 'tablet', + settings: { + keyboardHeight: 500, + }, + isMatch(p: Platform) { + return p.isPlatformMatch('ipad'); + } + }, + + /** + * iphone + */ + iphone: { + subsets: [ + 'phablet' + ], + isMatch(p: Platform) { + return p.isPlatformMatch('iphone'); + } + }, + + /** + * Windows + */ + windows: { + superset: 'mobile', + subsets: [ + 'phablet', + 'tablet' + ], + settings: { + mode: 'wp', + autoFocusAssist: 'immediate', + hoverCSS: false + }, + isMatch(p: Platform): boolean { + return p.isPlatformMatch('windows', ['windows phone']); + }, + versionParser(p: Platform): any { + return p.matchUserAgentVersion(/Windows Phone (\d+).(\d+)?/); + } + }, + + /** + * cordova + */ + cordova: { + isEngine: true, + initialize: function(p: Platform) { + + // prepare a custom "ready" for cordova "deviceready" + p.prepareReady = function() { + // 1) ionic bootstrapped + windowLoad(function() { + // 2) window onload triggered or completed + document.addEventListener('deviceready', function() { + // 3) cordova deviceready event triggered + + // add cordova listeners to emit platform events + document.addEventListener('backbutton', function(ev: Event) { + p.zone.run(() => { + p.backButton.emit(ev); + }); + }); + document.addEventListener('pause', function(ev: Event) { + p.zone.run(() => { + p.pause.emit(ev); + }); + }); + document.addEventListener('resume', function(ev: Event) { + p.zone.run(() => { + p.resume.emit(ev); + }); + }); + + // cordova has its own exitApp method + p.exitApp = function() { + (window.navigator).app.exitApp(); + }; + + // cordova has fully loaded and we've added listeners + p.triggerReady('cordova'); + }); + }); + }; + + }, + isMatch(): boolean { + return !!((window).cordova || (window).PhoneGap || (window).phonegap); + } + } +}; + + +function isIOSDevice(p: Platform) { + // shortcut function to be reused internally + // checks navigator.platform to see if it's an actual iOS device + // this does not use the user-agent string because it is often spoofed + // an actual iPad will return true, a chrome dev tools iPad will return false + return p.testNavigatorPlatform('iphone|ipad|ipod'); +} + + +export const PlatformConfigToken = new OpaqueToken('PLTCONFIG'); + +export function providePlatformConfigs() { + return PLATFORM_CONFIGS; +} diff --git a/src/platform/platform.ts b/src/platform/platform.ts index 222a3f0c9f..fc14bf0544 100644 --- a/src/platform/platform.ts +++ b/src/platform/platform.ts @@ -2,7 +2,7 @@ import { EventEmitter, NgZone, OpaqueToken } from '@angular/core'; import { QueryParams } from './query-params'; import { ready, windowDimensions, flushDimensionCache } from '../util/dom'; -import { setupPlatformRegistry } from './registry'; + /** * @name Platform @@ -40,6 +40,8 @@ export class Platform { private _readyResolve: any; private _resizeTm: any; private _bbActions: BackButtonAction[] = []; + private _registry: {[name: string]: PlatformConfig}; + private _default: string; /** @private */ zone: NgZone; @@ -502,29 +504,29 @@ export class Platform { /** * @private */ - static register(platformConfig: PlatformConfig) { - platformRegistry[platformConfig.name] = platformConfig; + setPlatformConfigs(platformConfigs: {[key: string]: PlatformConfig}) { + this._registry = platformConfigs || {}; } /** * @private */ - static registry() { - return platformRegistry; + getPlatformConfig(platformName: string): PlatformConfig { + return this._registry[platformName] || {}; } /** * @private */ - static get(platformName: string): PlatformConfig { - return platformRegistry[platformName] || {}; + registry() { + return this._registry; } /** * @private */ - static setDefault(platformName: string) { - platformDefault = platformName; + setDefault(platformName: string) { + this._default = platformName; } /** @@ -586,13 +588,13 @@ export class Platform { } /** @private */ - load() { + init() { let rootPlatformNode: PlatformNode; let enginePlatformNode: PlatformNode; // figure out the most specific platform and active engine let tmpPlatform: PlatformNode; - for (let platformName in platformRegistry) { + for (let platformName in this._registry) { tmpPlatform = this.matchPlatform(platformName); if (tmpPlatform) { @@ -614,7 +616,7 @@ export class Platform { } if (!rootPlatformNode) { - rootPlatformNode = new PlatformNode(platformDefault); + rootPlatformNode = new PlatformNode(this._registry, this._default); } // build a Platform instance filled with the @@ -634,7 +636,7 @@ export class Platform { let platformNode = rootPlatformNode; while (platformNode) { - insertSuperset(platformNode); + insertSuperset(this._registry, platformNode); platformNode = platformNode.child; } @@ -674,7 +676,7 @@ export class Platform { // build a PlatformNode and assign config data to it // use it's getRoot method to build up its hierarchy // depending on which platforms match - let platformNode = new PlatformNode(platformName); + let platformNode = new PlatformNode(this._registry, platformName); let rootNode = platformNode.getRoot(this); if (rootNode) { @@ -690,12 +692,12 @@ export class Platform { } -function insertSuperset(platformNode: PlatformNode) { +function insertSuperset(registry: any, platformNode: PlatformNode) { let supersetPlaformName = platformNode.superset(); if (supersetPlaformName) { // add a platform in between two exist platforms // so we can build the correct hierarchy of active platforms - let supersetPlatform = new PlatformNode(supersetPlaformName); + let supersetPlatform = new PlatformNode(registry, supersetPlaformName); supersetPlatform.parent = platformNode.parent; supersetPlatform.child = platformNode; if (supersetPlatform.parent) { @@ -717,8 +719,8 @@ class PlatformNode { isEngine: boolean; depth: number; - constructor(platformName: string) { - this.c = Platform.get(platformName); + constructor(public registry: {[name: string]: PlatformConfig}, platformName: string) { + this.c = registry[platformName]; this.name = platformName; this.isEngine = this.c.isEngine; } @@ -741,9 +743,9 @@ class PlatformNode { version(p: Platform): PlatformVersion { if (this.c.versionParser) { - let v = this.c.versionParser(p); + const v = this.c.versionParser(p); if (v) { - let str = v.major + '.' + v.minor; + const str = v.major + '.' + v.minor; return { str: str, num: parseFloat(str), @@ -767,7 +769,7 @@ class PlatformNode { let rootPlatformNode: PlatformNode = null; for (let i = 0; i < parents.length; i++) { - platformNode = new PlatformNode(parents[i]); + platformNode = new PlatformNode(this.registry, parents[i]); platformNode.child = this; rootPlatformNode = platformNode.getRoot(p); @@ -782,13 +784,11 @@ class PlatformNode { } getSubsetParents(subsetPlatformName: string): string[] { - let platformRegistry = Platform.registry(); - - let parentPlatformNames: string[] = []; + const parentPlatformNames: string[] = []; let platform: PlatformConfig = null; - for (let platformName in platformRegistry) { - platform = platformRegistry[platformName]; + for (let platformName in this.registry) { + platform = this.registry[platformName]; if (platform.subsets && platform.subsets.indexOf(subsetPlatformName) > -1) { parentPlatformNames.push(platformName); @@ -800,11 +800,8 @@ class PlatformNode { } -let platformRegistry: {[name: string]: PlatformConfig} = {}; -let platformDefault: string = null; export interface PlatformConfig { - name?: string; isEngine?: boolean; initialize?: Function; isMatch?: Function; @@ -826,21 +823,23 @@ interface BackButtonAction { priority: number; } -export function setupPlatform(queryParams: QueryParams, userAgent: string, navigatorPlatform: string, dir: string, lang: string, zone: NgZone): Platform { - setupPlatformRegistry(); +export function setupPlatform(platformConfigs: {[key: string]: PlatformConfig}, queryParams: QueryParams, userAgent: string, navigatorPlatform: string, docDirection: string, docLanguage: string, zone: NgZone): Platform { const p = new Platform(); + p.setDefault('core'); + p.setPlatformConfigs(platformConfigs); p.setUserAgent(userAgent); p.setQueryParams(queryParams); p.setNavigatorPlatform(navigatorPlatform); - p.setDir(dir, false); - p.setLang(lang, false); + p.setDir(docDirection, false); + p.setLang(docLanguage, false); p.setZone(zone); - p.load(); + p.init(); return p; } -export const UserAgent = new OpaqueToken('USERAGENT'); -export const UserNavigatorPlatform = new OpaqueToken('USERNAVPLT'); -export const UserDir = new OpaqueToken('USERDIR'); -export const UserLang = new OpaqueToken('USERLANG'); + +export const UserAgentToken = new OpaqueToken('USERAGENT'); +export const NavigatorPlatformToken = new OpaqueToken('NAVPLT'); +export const DocumentDirToken = new OpaqueToken('DOCDIR'); +export const DocLangToken = new OpaqueToken('DOCLANG'); diff --git a/src/platform/query-params.ts b/src/platform/query-params.ts index 14f5b6d36d..579968ca41 100644 --- a/src/platform/query-params.ts +++ b/src/platform/query-params.ts @@ -27,7 +27,7 @@ export class QueryParams { } -export const UserUrl = new OpaqueToken('USERURL'); +export const UrlToken = new OpaqueToken('USERURL'); export function setupQueryParams(url: string): QueryParams { return new QueryParams(url); diff --git a/src/platform/registry.ts b/src/platform/registry.ts deleted file mode 100644 index 38a6f9877e..0000000000 --- a/src/platform/registry.ts +++ /dev/null @@ -1,229 +0,0 @@ -import { Platform } from './platform'; -import { windowLoad } from '../util/dom'; - -const win: any = window; -const doc: any = document; - - -const PLATFORM_CORE: any = { - name: 'core', - settings: { - mode: 'md', - keyboardHeight: 290 - } -}; - - -const PLATFORM_MOBILE: any = { - name: 'mobile' -}; - - -const PLATFORM_PHABLET: any = { - name: 'phablet', - isMatch(p: Platform) { - let smallest = Math.min(p.width(), p.height()); - let largest = Math.max(p.width(), p.height()); - return (smallest > 390 && smallest < 520) && - (largest > 620 && largest < 800); - } -}; - - -const PLATFORM_TABLET: any = { - name: 'tablet', - isMatch(p: Platform) { - let smallest = Math.min(p.width(), p.height()); - let largest = Math.max(p.width(), p.height()); - return (smallest > 460 && smallest < 820) && - (largest > 780 && largest < 1400); - } -}; - - -const PLATFORM_ANDROID: any = { - name: 'android', - superset: 'mobile', - subsets: [ - 'phablet', - 'tablet' - ], - settings: { - activator: function(p: Platform): string { - // md mode defaults to use ripple activator - // however, under-powered devices shouldn't use ripple - // if this a linux device, and is using Android Chrome v36 (Android 5.0) - // or above then use ripple, otherwise do not use a ripple effect - if (p.testNavigatorPlatform('linux')) { - let chromeVersion = p.matchUserAgentVersion(/Chrome\/(\d+).(\d+)?/); - if (chromeVersion) { - // linux android device using modern android chrome browser gets ripple - return (parseInt(chromeVersion.major, 10) < 36 ? 'none' : 'ripple'); - } - // linux android device not using chrome browser checks just android's version - if (p.version().major < 5) { - return 'none'; - } - } - - // fallback to always use ripple - return 'ripple'; - }, - autoFocusAssist: 'immediate', - hoverCSS: false, - keyboardHeight: 300, - mode: 'md', - }, - isMatch(p: Platform): boolean { - return p.isPlatformMatch('android', ['android', 'silk'], ['windows phone']); - }, - versionParser(p: Platform): any { - return p.matchUserAgentVersion(/Android (\d+).(\d+)?/); - } -}; - - -const PLATFORM_IOS: any = { - name: 'ios', - superset: 'mobile', - subsets: [ - 'ipad', - 'iphone' - ], - settings: { - autoFocusAssist: 'delay', - hoverCSS: false, - inputBlurring: isIOSDevice, - inputCloning: isIOSDevice, - keyboardHeight: 300, - mode: 'ios', - scrollAssist: isIOSDevice, - statusbarPadding: !!(win.cordova), - swipeBackEnabled: isIOSDevice, - swipeBackThreshold: 40, - tapPolyfill: isIOSDevice, - virtualScrollEventAssist: !(win.indexedDB), - canDisableScroll: !!(win.indexedDB), - }, - isMatch(p: Platform): boolean { - return p.isPlatformMatch('ios', ['iphone', 'ipad', 'ipod'], ['windows phone']); - }, - versionParser(p: Platform): any { - return p.matchUserAgentVersion(/OS (\d+)_(\d+)?/); - } -}; - - -const PLATFORM_IPAD: any = { - name: 'ipad', - superset: 'tablet', - settings: { - keyboardHeight: 500, - }, - isMatch(p: Platform): boolean { - return p.isPlatformMatch('ipad'); - } -}; - - -const PLATFORM_IPHONE: any = { - name: 'iphone', - subsets: [ - 'phablet' - ], - isMatch(p: Platform): boolean { - return p.isPlatformMatch('iphone'); - } -}; - - -const PLATFORM_WINDOWS: any = { - name: 'windows', - superset: 'mobile', - subsets: [ - 'phablet', - 'tablet' - ], - settings: { - mode: 'wp', - autoFocusAssist: 'immediate', - hoverCSS: false - }, - isMatch(p: Platform): boolean { - return p.isPlatformMatch('windows', ['windows phone']); - }, - versionParser(p: Platform): any { - return p.matchUserAgentVersion(/Windows Phone (\d+).(\d+)?/); - } -}; - - -const PLATFORM_CORDOVA: any = { - name: 'cordova', - isEngine: true, - initialize: function(p: Platform) { - - // prepare a custom "ready" for cordova "deviceready" - p.prepareReady = function() { - // 1) ionic bootstrapped - windowLoad(function() { - // 2) window onload triggered or completed - doc.addEventListener('deviceready', function() { - // 3) cordova deviceready event triggered - - // add cordova listeners to emit platform events - doc.addEventListener('backbutton', function(ev: Event) { - p.zone.run(() => { - p.backButton.emit(ev); - }); - }); - doc.addEventListener('pause', function(ev: Event) { - p.zone.run(() => { - p.pause.emit(ev); - }); - }); - doc.addEventListener('resume', function(ev: Event) { - p.zone.run(() => { - p.resume.emit(ev); - }); - }); - - // cordova has its own exitApp method - p.exitApp = function() { - win.navigator.app.exitApp(); - }; - - // cordova has fully loaded and we've added listeners - p.triggerReady('cordova'); - }); - }); - }; - - }, - isMatch(): boolean { - return !!(win.cordova || win.PhoneGap || win.phonegap); - } -}; - - -function isIOSDevice(p: Platform) { - // shortcut function to be reused internally - // checks navigator.platform to see if it's an actual iOS device - // this does not use the user-agent string because it is often spoofed - // an actual iPad will return true, a chrome dev tools iPad will return false - return p.testNavigatorPlatform('iphone|ipad|ipod'); -} - -export function setupPlatformRegistry() { - Platform.register(PLATFORM_CORE); - Platform.register(PLATFORM_MOBILE); - Platform.register(PLATFORM_PHABLET); - Platform.register(PLATFORM_TABLET); - Platform.register(PLATFORM_ANDROID); - Platform.register(PLATFORM_IOS); - Platform.register(PLATFORM_IPAD); - Platform.register(PLATFORM_IPHONE); - Platform.register(PLATFORM_WINDOWS); - Platform.register(PLATFORM_CORDOVA); - Platform.setDefault('core'); -} diff --git a/src/platform/test/platform.spec.ts b/src/platform/test/platform.spec.ts index 0f6a38a8ea..a9bdb5c9d7 100644 --- a/src/platform/test/platform.spec.ts +++ b/src/platform/test/platform.spec.ts @@ -1,7 +1,8 @@ +import { Config } from '../../config/config'; import { Platform } from '../platform'; import { QueryParams } from '../query-params'; -import { setupPlatformRegistry } from '../registry'; -import { setupModeConfig } from '../../config/modes'; +import { PLATFORM_CONFIGS } from '../platform-registry'; +import { registerModeConfigs } from '../../config/mode-registry'; describe('Platform', () => { @@ -9,8 +10,6 @@ describe('Platform', () => { describe('registerBackButtonAction', () => { it('should register two actions with different priorities, call the highest one', () => { - let platform = new Platform(); - let ranAction1 = false; let action1 = () => { ranAction1 = true; @@ -30,8 +29,6 @@ describe('Platform', () => { }); it('should register two actions with the same priority, call the second one', () => { - let platform = new Platform(); - let ranAction1 = false; let action1 = () => { ranAction1 = true; @@ -51,8 +48,6 @@ describe('Platform', () => { }); it('should register a default action', () => { - let platform = new Platform(); - let ranAction1 = false; let action1 = () => { ranAction1 = true; @@ -65,18 +60,17 @@ describe('Platform', () => { }); it('should not run any actions when none registered', () => { - let platform = new Platform(); platform.runBackButtonAction(); }); }); it('should set core as the fallback', () => { - let platform = new Platform(); let qp = new QueryParams(''); + platform.setDefault('core'); platform.setQueryParams(qp); platform.setUserAgent('idk'); - platform.load(); + platform.init(); expect(platform.is('android')).toEqual(false); expect(platform.is('ios')).toEqual(false); @@ -84,10 +78,9 @@ describe('Platform', () => { }); it('should set windows via querystring', () => { - let platform = new Platform(); let qp = new QueryParams('/?ionicplatform=windows'); platform.setQueryParams(qp); - platform.load(); + platform.init(); expect(platform.is('core')).toEqual(false); expect(platform.is('mobile')).toEqual(true); @@ -97,10 +90,9 @@ describe('Platform', () => { }); it('should set ios via querystring', () => { - let platform = new Platform(); let qp = new QueryParams('/?ionicplatform=ios'); platform.setQueryParams(qp); - platform.load(); + platform.init(); expect(platform.is('core')).toEqual(false); expect(platform.is('mobile')).toEqual(true); @@ -110,11 +102,10 @@ describe('Platform', () => { }); it('should set windows via querystring, even with android user agent', () => { - let platform = new Platform(); let qp = new QueryParams('/?ionicplatform=windows'); platform.setQueryParams(qp); platform.setUserAgent(ANDROID_UA); - platform.load(); + platform.init(); expect(platform.is('core')).toEqual(false); expect(platform.is('android')).toEqual(false); @@ -123,11 +114,10 @@ describe('Platform', () => { }); it('should set ios via querystring, even with android user agent', () => { - let platform = new Platform(); let qp = new QueryParams('/?ionicplatform=ios'); platform.setQueryParams(qp); platform.setUserAgent(ANDROID_UA); - platform.load(); + platform.init(); expect(platform.is('core')).toEqual(false); expect(platform.is('android')).toEqual(false); @@ -136,10 +126,9 @@ describe('Platform', () => { }); it('should set android via querystring', () => { - let platform = new Platform(); let qp = new QueryParams('/?ionicplatform=android'); platform.setQueryParams(qp); - platform.load(); + platform.init(); expect(platform.is('core')).toEqual(false); expect(platform.is('android')).toEqual(true); @@ -148,11 +137,10 @@ describe('Platform', () => { }); it('should set android via querystring, even with ios user agent', () => { - let platform = new Platform(); let qp = new QueryParams('/?ionicplatform=android'); platform.setQueryParams(qp); platform.setUserAgent(IPHONE_UA); - platform.load(); + platform.init(); expect(platform.is('core')).toEqual(false); expect(platform.is('android')).toEqual(true); @@ -161,11 +149,10 @@ describe('Platform', () => { }); it('should set windows platform via user agent', () => { - let platform = new Platform(); let qp = new QueryParams(''); platform.setQueryParams(qp); platform.setUserAgent(WINDOWS_PHONE_UA); - platform.load(); + platform.init(); expect(platform.is('core')).toEqual(false); expect(platform.is('mobile')).toEqual(true); @@ -175,11 +162,10 @@ describe('Platform', () => { }); it('should set windows platform via windows8 mobile user agent', () => { - let platform = new Platform(); let qp = new QueryParams(''); platform.setQueryParams(qp); platform.setUserAgent(WINDOWS8_PHONE_UA); - platform.load(); + platform.init(); expect(platform.is('core')).toEqual(false); expect(platform.is('mobile')).toEqual(true); @@ -189,11 +175,10 @@ describe('Platform', () => { }); it('should set windows platform via windows7 mobile user agent', () => { - let platform = new Platform(); let qp = new QueryParams(''); platform.setQueryParams(qp); platform.setUserAgent(WINDOWS7_PHONE_UA); - platform.load(); + platform.init(); expect(platform.is('core')).toEqual(false); expect(platform.is('mobile')).toEqual(true); @@ -203,11 +188,10 @@ describe('Platform', () => { }); it('should set android via user agent', () => { - let platform = new Platform(); let qp = new QueryParams(''); platform.setQueryParams(qp); platform.setUserAgent(ANDROID_UA); - platform.load(); + platform.init(); expect(platform.is('core')).toEqual(false); expect(platform.is('mobile')).toEqual(true); @@ -217,11 +201,10 @@ describe('Platform', () => { }); it('should set iphone via user agent', () => { - let platform = new Platform(); let qp = new QueryParams(''); platform.setQueryParams(qp); platform.setUserAgent(IPHONE_UA); - platform.load(); + platform.init(); expect(platform.is('core')).toEqual(false); expect(platform.is('mobile')).toEqual(true); @@ -233,11 +216,10 @@ describe('Platform', () => { }); it('should set ipad via user agent', () => { - let platform = new Platform(); let qp = new QueryParams(''); platform.setQueryParams(qp); platform.setUserAgent(IPAD_UA); - platform.load(); + platform.init(); expect(platform.is('core')).toEqual(false); expect(platform.is('mobile')).toEqual(true); @@ -249,11 +231,11 @@ describe('Platform', () => { }); it('should set core platform for osx desktop firefox', () => { - let platform = new Platform(); let qp = new QueryParams(''); + platform.setDefault('core'); platform.setQueryParams(qp); platform.setUserAgent(OSX_10_FIREFOX_43_UA); - platform.load(); + platform.init(); expect(platform.is('core')).toEqual(true); expect(platform.is('mobile')).toEqual(false); @@ -265,11 +247,11 @@ describe('Platform', () => { }); it('should set core platform for osx desktop safari', () => { - let platform = new Platform(); let qp = new QueryParams(''); + platform.setDefault('core'); platform.setQueryParams(qp); platform.setUserAgent(OSX_10_SAFARI_9_UA); - platform.load(); + platform.init(); expect(platform.is('core')).toEqual(true); expect(platform.is('mobile')).toEqual(false); @@ -281,11 +263,11 @@ describe('Platform', () => { }); it('should set core platform for osx desktop chrome', () => { - let platform = new Platform(); let qp = new QueryParams(''); + platform.setDefault('core'); platform.setQueryParams(qp); platform.setUserAgent(OSX_10_CHROME_49_UA); - platform.load(); + platform.init(); expect(platform.is('core')).toEqual(true); expect(platform.is('mobile')).toEqual(false); @@ -297,11 +279,11 @@ describe('Platform', () => { }); it('should set core platform for windows desktop chrome', () => { - let platform = new Platform(); let qp = new QueryParams(''); + platform.setDefault('core'); platform.setQueryParams(qp); platform.setUserAgent(WINDOWS_10_CHROME_40_UA); - platform.load(); + platform.init(); expect(platform.is('core')).toEqual(true); expect(platform.is('mobile')).toEqual(false); @@ -313,11 +295,11 @@ describe('Platform', () => { }); it('should set core platform for windows desktop edge', () => { - let platform = new Platform(); let qp = new QueryParams(''); + platform.setDefault('core'); platform.setQueryParams(qp); platform.setUserAgent(WINDOWS_10_EDGE_12_UA); - platform.load(); + platform.init(); expect(platform.is('core')).toEqual(true); expect(platform.is('mobile')).toEqual(false); @@ -329,11 +311,11 @@ describe('Platform', () => { }); it('should set core platform for windows desktop IE', () => { - let platform = new Platform(); let qp = new QueryParams(''); + platform.setDefault('core'); platform.setQueryParams(qp); platform.setUserAgent(WINDOWS_8_IE_11_UA); - platform.load(); + platform.init(); expect(platform.is('core')).toEqual(true); expect(platform.is('mobile')).toEqual(false); @@ -344,9 +326,12 @@ describe('Platform', () => { expect(platform.is('tablet')).toEqual(false); }); + let platform: Platform; + beforeEach(() => { - setupModeConfig(); - setupPlatformRegistry(); + platform = new Platform(); + platform.setPlatformConfigs(PLATFORM_CONFIGS); + registerModeConfigs(new Config()); }); }); diff --git a/src/platform/test/query-params.spec.ts b/src/platform/test/query-params.spec.ts index 883283bd38..0f71e6c4f6 100644 --- a/src/platform/test/query-params.spec.ts +++ b/src/platform/test/query-params.spec.ts @@ -1,6 +1,4 @@ import { QueryParams } from '../query-params'; -import '../registry'; -import '../../config/modes'; describe('QueryParams', () => { @@ -96,4 +94,5 @@ describe('QueryParams', () => { key: '' }); }); + }); diff --git a/src/transitions/transition-controller.ts b/src/transitions/transition-controller.ts index c01154e91a..d1c150a180 100644 --- a/src/transitions/transition-controller.ts +++ b/src/transitions/transition-controller.ts @@ -1,16 +1,24 @@ +import { Injectable } from '@angular/core'; + import { AnimationOptions } from '../animations/animation'; +import { Config } from '../config/config'; import { isPresent } from '../util/util'; import { NavControllerBase } from '../navigation/nav-controller-base'; import { Transition } from './transition'; +import { createTransition } from './transition-registry'; import { ViewController } from '../navigation/view-controller'; + /** * @private */ +@Injectable() export class TransitionController { private _ids = 0; private _trns: {[key: number]: Transition} = {}; + constructor(private _config: Config) {} + getRootTrnsId(nav: NavControllerBase): number { let parent = nav.parent; while (parent) { @@ -27,7 +35,7 @@ export class TransitionController { } get(trnsId: number, enteringView: ViewController, leavingView: ViewController, opts: AnimationOptions) { - const trns = Transition.createTransition(opts.animation, enteringView, leavingView, opts); + const trns = createTransition(this._config, opts.animation, enteringView, leavingView, opts); trns.trnsId = trnsId; if (!this._trns[trnsId]) { diff --git a/src/transitions/transition-registry.ts b/src/transitions/transition-registry.ts new file mode 100644 index 0000000000..a692944193 --- /dev/null +++ b/src/transitions/transition-registry.ts @@ -0,0 +1,74 @@ +import { Config } from '../config/config'; +import { IOSTransition } from './transition-ios'; +import { MDTransition } from './transition-md'; +import { WPTransition } from './transition-wp'; + +import { ActionSheetSlideIn, ActionSheetMdSlideIn, ActionSheetSlideOut, ActionSheetMdSlideOut, ActionSheetWpSlideIn, ActionSheetWpSlideOut } from '../components/action-sheet/action-sheet-transitions'; +import { AlertPopIn, AlertPopOut, AlertMdPopIn, AlertMdPopOut, AlertWpPopIn, AlertWpPopOut } from '../components/alert/alert-transitions'; +import { LoadingPopIn, LoadingPopOut, LoadingMdPopIn, LoadingMdPopOut, LoadingWpPopIn, LoadingWpPopOut } from '../components/loading/loading-transitions'; +import { ModalSlideIn, ModalSlideOut, ModalMDSlideIn, ModalMDSlideOut } from '../components/modal/modal-transitions'; +import { PickerSlideIn, PickerSlideOut } from '../components/picker/picker-transitions'; +import { PopoverPopIn, PopoverPopOut, PopoverMdPopIn, PopoverMdPopOut } from '../components/popover/popover-transitions'; +import { ToastSlideIn, ToastSlideOut, ToastMdSlideIn, ToastMdSlideOut, ToastWpPopOut, ToastWpPopIn } from '../components/toast/toast-transitions'; + + +export function registerTransitions(config: Config) { + return function() { + config.setTransition('ios-transition', IOSTransition); + config.setTransition('md-transition', MDTransition); + config.setTransition('wp-transition', WPTransition); + + config.setTransition('action-sheet-slide-in', ActionSheetSlideIn); + config.setTransition('action-sheet-slide-out', ActionSheetSlideOut); + config.setTransition('action-sheet-md-slide-in', ActionSheetMdSlideIn); + config.setTransition('action-sheet-md-slide-out', ActionSheetMdSlideOut); + config.setTransition('action-sheet-wp-slide-in', ActionSheetWpSlideIn); + config.setTransition('action-sheet-wp-slide-out', ActionSheetWpSlideOut); + + config.setTransition('alert-pop-in', AlertPopIn); + config.setTransition('alert-pop-out', AlertPopOut); + config.setTransition('alert-md-pop-in', AlertMdPopIn); + config.setTransition('alert-md-pop-out', AlertMdPopOut); + config.setTransition('alert-wp-pop-in', AlertWpPopIn); + config.setTransition('alert-wp-pop-out', AlertWpPopOut); + + config.setTransition('loading-pop-in', LoadingPopIn); + config.setTransition('loading-pop-out', LoadingPopOut); + config.setTransition('loading-md-pop-in', LoadingMdPopIn); + config.setTransition('loading-md-pop-out', LoadingMdPopOut); + config.setTransition('loading-wp-pop-in', LoadingWpPopIn); + config.setTransition('loading-wp-pop-out', LoadingWpPopOut); + + config.setTransition('modal-slide-in', ModalSlideIn); + config.setTransition('modal-slide-out', ModalSlideOut); + config.setTransition('modal-md-slide-in', ModalMDSlideIn); + config.setTransition('modal-md-slide-out', ModalMDSlideOut); + + config.setTransition('picker-slide-in', PickerSlideIn); + config.setTransition('picker-slide-out', PickerSlideOut); + + config.setTransition('popover-pop-in', PopoverPopIn); + config.setTransition('popover-pop-out', PopoverPopOut); + config.setTransition('popover-md-pop-in', PopoverMdPopIn); + config.setTransition('popover-md-pop-out', PopoverMdPopOut); + + config.setTransition('toast-slide-in', ToastSlideIn); + config.setTransition('toast-slide-out', ToastSlideOut); + config.setTransition('toast-md-slide-in', ToastMdSlideIn); + config.setTransition('toast-md-slide-out', ToastMdSlideOut); + config.setTransition('toast-wp-slide-out', ToastWpPopOut); + config.setTransition('toast-wp-slide-in', ToastWpPopIn); + }; +} + + +export function createTransition(config: Config, transitionName: string, enteringView: any, leavingView: any, opts: any) { + let TransitionClass: any = config.getTransition(transitionName); + if (!TransitionClass) { + // didn't find a transition animation, default to ios-transition + TransitionClass = config.getTransition('ios-transition'); + } + + return new TransitionClass(enteringView, leavingView, opts); +} + diff --git a/src/transitions/transition.ts b/src/transitions/transition.ts index d0d9d89efe..1a26447a3c 100644 --- a/src/transitions/transition.ts +++ b/src/transitions/transition.ts @@ -45,20 +45,4 @@ export class Transition extends Animation { this.enteringView = this.leavingView = this._trnsStart = null; } - static createTransition(transitionName: string, enteringView: ViewController, leavingView: ViewController, opts: AnimationOptions): Transition { - let TransitionClass: any = TransitionRegistry[transitionName]; - if (!TransitionClass) { - // didn't find a transition animation, default to ios-transition - TransitionClass = TransitionRegistry['ios-transition']; - } - - return new TransitionClass(enteringView, leavingView, opts); - } - - static register(name: string, TransitionClass: any) { - TransitionRegistry[name] = TransitionClass; - } - } - -let TransitionRegistry: {[key: string]: Transition} = {}; diff --git a/src/util/feature-detect.ts b/src/util/feature-detect.ts deleted file mode 100644 index d281a6a9ba..0000000000 --- a/src/util/feature-detect.ts +++ /dev/null @@ -1,51 +0,0 @@ -import { IonicApp } from '../components/app/app-root'; - - -export class FeatureDetect { - private _r: {[featureName: string]: boolean} = {}; - - test(appRoot: IonicApp) { - for (let name in featureDetects) { - this._r[name] = !!featureDetects[name].test(); - if (this._r[name]) { - appRoot.setElementClass(name, true); - } - } - featureDetects = null; - } - - has(featureName: string): boolean { - return !!this._r[featureName]; - } - - static add(name: string, test: any) { - // feature detection tests should only run on client side - if ((this).window) { - featureDetects[name] = new test(); - } - } - -} - -let featureDetects: {[featureName: string]: FeatureDetectTest} = {}; - - -export abstract class FeatureDetectTest { - abstract test(): boolean; -} - - -/** -* backdrop-filter Test -* Checks if css backdrop-filter is implemented by the browser. -*/ -export class BackdropFilterTest implements FeatureDetectTest { - - test() { - const styles = document.documentElement.style; - return !!(styles['backdrop-filter'] !== undefined || styles['-webkit-backdrop-filter'] !== undefined); - } - -} - -FeatureDetect.add('backdrop-filter', BackdropFilterTest); diff --git a/src/util/mock-providers.ts b/src/util/mock-providers.ts index a2bbbff62a..03bea40656 100644 --- a/src/util/mock-providers.ts +++ b/src/util/mock-providers.ts @@ -50,7 +50,7 @@ export const mockApp = function(config?: Config, platform?: Platform) { export const mockIonicApp = function(app: App, config: Config, platform: Platform): IonicApp { let appRoot = new IonicApp( - null, null, mockElementRef(), mockRenderer(), config, platform, null, app); + null, null, mockElementRef(), mockRenderer(), config, platform, app); appRoot._loadingPortal = mockOverlayPortal(app, config, platform); appRoot._toastPortal = mockOverlayPortal(app, config, platform); @@ -59,8 +59,8 @@ export const mockIonicApp = function(app: App, config: Config, platform: Platfor return appRoot; }; -export const mockTrasitionController = function() { - let trnsCtrl = new TransitionController(); +export const mockTrasitionController = function(config: Config) { + let trnsCtrl = new TransitionController(config); trnsCtrl.get = (trnsId: number, enteringView: ViewController, leavingView: ViewController, opts: AnimationOptions) => { let trns = new PageTransition(enteringView, leavingView, opts, (callback: Function) => { callback(); @@ -255,7 +255,7 @@ export const mockNavController = function(): NavControllerBase { let linker = mockDeepLinker(null, app); - let trnsCtrl = mockTrasitionController(); + let trnsCtrl = mockTrasitionController(config); let nav = new NavControllerBase( null,