mirror of
https://github.com/ionic-team/ionic-framework.git
synced 2025-08-17 18:54:11 +08:00
fix(ssr): fix angular global window and document references
This commit is contained in:
@ -3,10 +3,10 @@ import { defineCustomElements } from '@ionic/core/loader';
|
|||||||
import { Config } from './providers/config';
|
import { Config } from './providers/config';
|
||||||
import { IonicWindow } from './types/interfaces';
|
import { IonicWindow } from './types/interfaces';
|
||||||
|
|
||||||
export function appInitialize(config: Config) {
|
export function appInitialize(config: Config, doc: Document) {
|
||||||
return (): any => {
|
return (): any => {
|
||||||
const win: IonicWindow | undefined = window as any;
|
const win: IonicWindow | undefined = doc.defaultView as any;
|
||||||
if (typeof win !== 'undefined') {
|
if (win) {
|
||||||
const Ionic = win.Ionic = win.Ionic || {};
|
const Ionic = win.Ionic = win.Ionic || {};
|
||||||
|
|
||||||
Ionic.config = config;
|
Ionic.config = config;
|
||||||
|
@ -82,12 +82,14 @@ export class IonRouterOutlet implements OnDestroy, OnInit {
|
|||||||
this.activateWith(context.route, context.resolver || null);
|
this.activateWith(context.route, context.resolver || null);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if ((this.nativeEl as any).componentOnReady) {
|
||||||
this.nativeEl.componentOnReady().then(() => {
|
this.nativeEl.componentOnReady().then(() => {
|
||||||
if (this._swipeGesture === undefined) {
|
if (this._swipeGesture === undefined) {
|
||||||
this.swipeGesture = this.config.getBoolean('swipeBackEnabled', this.nativeEl.mode === 'ios');
|
this.swipeGesture = this.config.getBoolean('swipeBackEnabled', this.nativeEl.mode === 'ios');
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
get isActivated(): boolean {
|
get isActivated(): boolean {
|
||||||
return !!this.activated;
|
return !!this.activated;
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { CommonModule } from '@angular/common';
|
import { CommonModule, DOCUMENT } from '@angular/common';
|
||||||
import { APP_INITIALIZER, ModuleWithProviders, NgModule } from '@angular/core';
|
import { APP_INITIALIZER, ModuleWithProviders, NgModule } from '@angular/core';
|
||||||
import { IonicConfig } from '@ionic/core';
|
import { IonicConfig } from '@ionic/core';
|
||||||
|
|
||||||
@ -141,7 +141,8 @@ export class IonicModule {
|
|||||||
useFactory: appInitialize,
|
useFactory: appInitialize,
|
||||||
multi: true,
|
multi: true,
|
||||||
deps: [
|
deps: [
|
||||||
ConfigToken
|
ConfigToken,
|
||||||
|
DOCUMENT
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import { Injectable } from '@angular/core';
|
import { DOCUMENT } from '@angular/common';
|
||||||
|
import { Inject, Injectable } from '@angular/core';
|
||||||
import { ActionSheetOptions } from '@ionic/core';
|
import { ActionSheetOptions } from '@ionic/core';
|
||||||
|
|
||||||
import { OverlayBaseController } from '../util/overlay';
|
import { OverlayBaseController } from '../util/overlay';
|
||||||
@ -7,7 +8,7 @@ import { OverlayBaseController } from '../util/overlay';
|
|||||||
providedIn: 'root',
|
providedIn: 'root',
|
||||||
})
|
})
|
||||||
export class ActionSheetController extends OverlayBaseController<ActionSheetOptions, HTMLIonActionSheetElement> {
|
export class ActionSheetController extends OverlayBaseController<ActionSheetOptions, HTMLIonActionSheetElement> {
|
||||||
constructor() {
|
constructor(@Inject(DOCUMENT) doc: any) {
|
||||||
super('ion-action-sheet-controller');
|
super('ion-action-sheet-controller', doc);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import { Injectable } from '@angular/core';
|
import { DOCUMENT } from '@angular/common';
|
||||||
|
import { Inject, Injectable } from '@angular/core';
|
||||||
import { AlertOptions } from '@ionic/core';
|
import { AlertOptions } from '@ionic/core';
|
||||||
|
|
||||||
import { OverlayBaseController } from '../util/overlay';
|
import { OverlayBaseController } from '../util/overlay';
|
||||||
@ -7,7 +8,7 @@ import { OverlayBaseController } from '../util/overlay';
|
|||||||
providedIn: 'root',
|
providedIn: 'root',
|
||||||
})
|
})
|
||||||
export class AlertController extends OverlayBaseController<AlertOptions, HTMLIonAlertElement> {
|
export class AlertController extends OverlayBaseController<AlertOptions, HTMLIonAlertElement> {
|
||||||
constructor() {
|
constructor(@Inject(DOCUMENT) doc: any) {
|
||||||
super('ion-alert-controller');
|
super('ion-alert-controller', doc);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -43,9 +43,8 @@ export class Config {
|
|||||||
export const ConfigToken = new InjectionToken<any>('USERCONFIG');
|
export const ConfigToken = new InjectionToken<any>('USERCONFIG');
|
||||||
|
|
||||||
function getConfig(): CoreConfig | null {
|
function getConfig(): CoreConfig | null {
|
||||||
const win: IonicWindow | undefined = window as any;
|
if (typeof (window as any) !== 'undefined') {
|
||||||
if (typeof win !== 'undefined') {
|
const Ionic = (window as IonicWindow).Ionic;
|
||||||
const Ionic = win.Ionic;
|
|
||||||
if (Ionic && Ionic.config) {
|
if (Ionic && Ionic.config) {
|
||||||
return Ionic.config;
|
return Ionic.config;
|
||||||
}
|
}
|
||||||
|
@ -23,14 +23,23 @@ export class DomController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function getQueue() {
|
function getQueue() {
|
||||||
const Ionic = (window as any).Ionic;
|
const win = typeof (window as any) !== 'undefined' ? window : null as any;
|
||||||
|
|
||||||
|
if (win != null) {
|
||||||
|
const Ionic = win.Ionic;
|
||||||
if (Ionic && Ionic.queue) {
|
if (Ionic && Ionic.queue) {
|
||||||
return Ionic.queue;
|
return Ionic.queue;
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
read: (cb: any) => window.requestAnimationFrame(cb),
|
read: (cb: any) => win.requestAnimationFrame(cb),
|
||||||
write: (cb: any) => window.requestAnimationFrame(cb)
|
write: (cb: any) => win.requestAnimationFrame(cb)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
read: (cb: any) => cb(),
|
||||||
|
write: (cb: any) => cb()
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import { Injectable } from '@angular/core';
|
import { DOCUMENT } from '@angular/common';
|
||||||
|
import { Inject, Injectable } from '@angular/core';
|
||||||
import { LoadingOptions } from '@ionic/core';
|
import { LoadingOptions } from '@ionic/core';
|
||||||
|
|
||||||
import { OverlayBaseController } from '../util/overlay';
|
import { OverlayBaseController } from '../util/overlay';
|
||||||
@ -7,7 +8,7 @@ import { OverlayBaseController } from '../util/overlay';
|
|||||||
providedIn: 'root',
|
providedIn: 'root',
|
||||||
})
|
})
|
||||||
export class LoadingController extends OverlayBaseController<LoadingOptions, HTMLIonLoadingElement> {
|
export class LoadingController extends OverlayBaseController<LoadingOptions, HTMLIonLoadingElement> {
|
||||||
constructor() {
|
constructor(@Inject(DOCUMENT) doc: any) {
|
||||||
super('ion-loading-controller');
|
super('ion-loading-controller', doc);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import { Injectable } from '@angular/core';
|
import { DOCUMENT } from '@angular/common';
|
||||||
|
import { Inject, Injectable } from '@angular/core';
|
||||||
|
|
||||||
import { proxyMethod } from '../util/util';
|
import { proxyMethod } from '../util/util';
|
||||||
|
|
||||||
@ -8,13 +9,16 @@ const CTRL = 'ion-menu-controller';
|
|||||||
})
|
})
|
||||||
export class MenuController {
|
export class MenuController {
|
||||||
|
|
||||||
|
constructor(@Inject(DOCUMENT) private doc: any) {
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Programmatically open the Menu.
|
* Programmatically open the Menu.
|
||||||
* @param [menuId] Optionally get the menu by its id, or side.
|
* @param [menuId] Optionally get the menu by its id, or side.
|
||||||
* @return returns a promise when the menu is fully opened
|
* @return returns a promise when the menu is fully opened
|
||||||
*/
|
*/
|
||||||
open(menuId?: string): Promise<boolean> {
|
open(menuId?: string): Promise<boolean> {
|
||||||
return proxyMethod(CTRL, 'open', menuId);
|
return proxyMethod(CTRL, this.doc, 'open', menuId);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -25,7 +29,7 @@ export class MenuController {
|
|||||||
* @return returns a promise when the menu is fully closed
|
* @return returns a promise when the menu is fully closed
|
||||||
*/
|
*/
|
||||||
close(menuId?: string): Promise<boolean> {
|
close(menuId?: string): Promise<boolean> {
|
||||||
return proxyMethod(CTRL, 'close', menuId);
|
return proxyMethod(CTRL, this.doc, 'close', menuId);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -35,7 +39,7 @@ export class MenuController {
|
|||||||
* @return returns a promise when the menu has been toggled
|
* @return returns a promise when the menu has been toggled
|
||||||
*/
|
*/
|
||||||
toggle(menuId?: string): Promise<boolean> {
|
toggle(menuId?: string): Promise<boolean> {
|
||||||
return proxyMethod(CTRL, 'toggle', menuId);
|
return proxyMethod(CTRL, this.doc, 'toggle', menuId);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -47,7 +51,7 @@ export class MenuController {
|
|||||||
* @return Returns the instance of the menu, which is useful for chaining.
|
* @return Returns the instance of the menu, which is useful for chaining.
|
||||||
*/
|
*/
|
||||||
enable(shouldEnable: boolean, menuId?: string): Promise<HTMLIonMenuElement> {
|
enable(shouldEnable: boolean, menuId?: string): Promise<HTMLIonMenuElement> {
|
||||||
return proxyMethod(CTRL, 'enable', shouldEnable, menuId);
|
return proxyMethod(CTRL, this.doc, 'enable', shouldEnable, menuId);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -57,7 +61,7 @@ export class MenuController {
|
|||||||
* @return Returns the instance of the menu, which is useful for chaining.
|
* @return Returns the instance of the menu, which is useful for chaining.
|
||||||
*/
|
*/
|
||||||
swipeEnable(shouldEnable: boolean, menuId?: string): Promise<HTMLIonMenuElement> {
|
swipeEnable(shouldEnable: boolean, menuId?: string): Promise<HTMLIonMenuElement> {
|
||||||
return proxyMethod(CTRL, 'swipeEnable', shouldEnable, menuId);
|
return proxyMethod(CTRL, this.doc, 'swipeEnable', shouldEnable, menuId);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -66,7 +70,7 @@ export class MenuController {
|
|||||||
* If the menuId is not specified, it returns true if ANY menu is currenly open.
|
* If the menuId is not specified, it returns true if ANY menu is currenly open.
|
||||||
*/
|
*/
|
||||||
isOpen(menuId?: string): Promise<boolean> {
|
isOpen(menuId?: string): Promise<boolean> {
|
||||||
return proxyMethod(CTRL, 'isOpen', menuId);
|
return proxyMethod(CTRL, this.doc, 'isOpen', menuId);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -74,7 +78,7 @@ export class MenuController {
|
|||||||
* @return Returns true if the menu is currently enabled, otherwise false.
|
* @return Returns true if the menu is currently enabled, otherwise false.
|
||||||
*/
|
*/
|
||||||
isEnabled(menuId?: string): Promise<boolean> {
|
isEnabled(menuId?: string): Promise<boolean> {
|
||||||
return proxyMethod(CTRL, 'isEnabled', menuId);
|
return proxyMethod(CTRL, this.doc, 'isEnabled', menuId);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -87,20 +91,20 @@ export class MenuController {
|
|||||||
* @return Returns the instance of the menu if found, otherwise `null`.
|
* @return Returns the instance of the menu if found, otherwise `null`.
|
||||||
*/
|
*/
|
||||||
get(menuId?: string): Promise<HTMLIonMenuElement> {
|
get(menuId?: string): Promise<HTMLIonMenuElement> {
|
||||||
return proxyMethod(CTRL, 'get', menuId);
|
return proxyMethod(CTRL, this.doc, 'get', menuId);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return Returns the instance of the menu already opened, otherwise `null`.
|
* @return Returns the instance of the menu already opened, otherwise `null`.
|
||||||
*/
|
*/
|
||||||
getOpen(): Promise<HTMLIonMenuElement> {
|
getOpen(): Promise<HTMLIonMenuElement> {
|
||||||
return proxyMethod(CTRL, 'getOpen');
|
return proxyMethod(CTRL, this.doc, 'getOpen');
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return Returns an array of all menu instances.
|
* @return Returns an array of all menu instances.
|
||||||
*/
|
*/
|
||||||
getMenus(): Promise<HTMLIonMenuElement[]> {
|
getMenus(): Promise<HTMLIonMenuElement[]> {
|
||||||
return proxyMethod(CTRL, 'getMenus');
|
return proxyMethod(CTRL, this.doc, 'getMenus');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import { ComponentFactoryResolver, Injectable, Injector } from '@angular/core';
|
import { DOCUMENT } from '@angular/common';
|
||||||
|
import { ComponentFactoryResolver, Inject, Injectable, Injector } from '@angular/core';
|
||||||
import { ModalOptions } from '@ionic/core';
|
import { ModalOptions } from '@ionic/core';
|
||||||
|
|
||||||
import { OverlayBaseController } from '../util/overlay';
|
import { OverlayBaseController } from '../util/overlay';
|
||||||
@ -12,8 +13,9 @@ export class ModalController extends OverlayBaseController<ModalOptions, HTMLIon
|
|||||||
private angularDelegate: AngularDelegate,
|
private angularDelegate: AngularDelegate,
|
||||||
private resolver: ComponentFactoryResolver,
|
private resolver: ComponentFactoryResolver,
|
||||||
private injector: Injector,
|
private injector: Injector,
|
||||||
|
@Inject(DOCUMENT) doc: any
|
||||||
) {
|
) {
|
||||||
super('ion-modal-controller');
|
super('ion-modal-controller', doc);
|
||||||
}
|
}
|
||||||
|
|
||||||
create(opts: ModalOptions): Promise<HTMLIonModalElement> {
|
create(opts: ModalOptions): Promise<HTMLIonModalElement> {
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import { Injectable } from '@angular/core';
|
import { DOCUMENT } from '@angular/common';
|
||||||
|
import { Inject, Injectable } from '@angular/core';
|
||||||
import { PickerOptions } from '@ionic/core';
|
import { PickerOptions } from '@ionic/core';
|
||||||
|
|
||||||
import { OverlayBaseController } from '../util/overlay';
|
import { OverlayBaseController } from '../util/overlay';
|
||||||
@ -7,7 +8,7 @@ import { OverlayBaseController } from '../util/overlay';
|
|||||||
providedIn: 'root',
|
providedIn: 'root',
|
||||||
})
|
})
|
||||||
export class PickerController extends OverlayBaseController<PickerOptions, HTMLIonPickerElement> {
|
export class PickerController extends OverlayBaseController<PickerOptions, HTMLIonPickerElement> {
|
||||||
constructor() {
|
constructor(@Inject(DOCUMENT) doc: any) {
|
||||||
super('ion-picker-controller');
|
super('ion-picker-controller', doc);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import { Injectable } from '@angular/core';
|
import { DOCUMENT } from '@angular/common';
|
||||||
|
import { Inject, Injectable } from '@angular/core';
|
||||||
import { BackButtonEventDetail, Platforms, getPlatforms, isPlatform } from '@ionic/core';
|
import { BackButtonEventDetail, Platforms, getPlatforms, isPlatform } from '@ionic/core';
|
||||||
import { Subject, Subscription } from 'rxjs';
|
import { Subject, Subscription } from 'rxjs';
|
||||||
|
|
||||||
@ -12,6 +13,7 @@ export interface BackButtonEmitter extends Subject<BackButtonEventDetail> {
|
|||||||
export class Platform {
|
export class Platform {
|
||||||
|
|
||||||
private _readyPromise: Promise<string>;
|
private _readyPromise: Promise<string>;
|
||||||
|
private win: any;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @hidden
|
* @hidden
|
||||||
@ -40,22 +42,24 @@ export class Platform {
|
|||||||
*/
|
*/
|
||||||
resize = new Subject<void>();
|
resize = new Subject<void>();
|
||||||
|
|
||||||
constructor() {
|
constructor(@Inject(DOCUMENT) private doc: any) {
|
||||||
|
this.win = doc.defaultView;
|
||||||
|
|
||||||
this.backButton.subscribeWithPriority = function(priority, callback) {
|
this.backButton.subscribeWithPriority = function(priority, callback) {
|
||||||
return this.subscribe(ev => {
|
return this.subscribe(ev => {
|
||||||
ev.register(priority, callback);
|
ev.register(priority, callback);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
proxyEvent(this.pause, document, 'pause');
|
proxyEvent(this.pause, doc, 'pause');
|
||||||
proxyEvent(this.resume, document, 'resume');
|
proxyEvent(this.resume, doc, 'resume');
|
||||||
proxyEvent(this.backButton, document, 'ionBackButton');
|
proxyEvent(this.backButton, doc, 'ionBackButton');
|
||||||
proxyEvent(this.resize, window, 'resize');
|
proxyEvent(this.resize, this.win, 'resize');
|
||||||
|
|
||||||
let readyResolve: (value: string) => void;
|
let readyResolve: (value: string) => void;
|
||||||
this._readyPromise = new Promise(res => { readyResolve = res; });
|
this._readyPromise = new Promise(res => { readyResolve = res; });
|
||||||
if ((window as any)['cordova']) {
|
if (this.win && this.win['cordova']) {
|
||||||
document.addEventListener('deviceready', () => {
|
doc.addEventListener('deviceready', () => {
|
||||||
readyResolve('cordova');
|
readyResolve('cordova');
|
||||||
}, { once: true });
|
}, { once: true });
|
||||||
} else {
|
} else {
|
||||||
@ -106,7 +110,7 @@ export class Platform {
|
|||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
is(platformName: Platforms): boolean {
|
is(platformName: Platforms): boolean {
|
||||||
return isPlatform(window, platformName);
|
return isPlatform(this.win, platformName);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -129,7 +133,7 @@ export class Platform {
|
|||||||
* ```
|
* ```
|
||||||
*/
|
*/
|
||||||
platforms(): string[] {
|
platforms(): string[] {
|
||||||
return getPlatforms(window);
|
return getPlatforms(this.win);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -172,14 +176,14 @@ export class Platform {
|
|||||||
* [W3C: Structural markup and right-to-left text in HTML](http://www.w3.org/International/questions/qa-html-dir)
|
* [W3C: Structural markup and right-to-left text in HTML](http://www.w3.org/International/questions/qa-html-dir)
|
||||||
*/
|
*/
|
||||||
get isRTL(): boolean {
|
get isRTL(): boolean {
|
||||||
return document.dir === 'rtl';
|
return this.doc.dir === 'rtl';
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the query string parameter
|
* Get the query string parameter
|
||||||
*/
|
*/
|
||||||
getQueryParam(key: string): string | null {
|
getQueryParam(key: string): string | null {
|
||||||
return readQueryParam(window.location.href, key);
|
return readQueryParam(this.win.location.href, key);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -193,45 +197,48 @@ export class Platform {
|
|||||||
* Returns `true` if the app is in portait mode.
|
* Returns `true` if the app is in portait mode.
|
||||||
*/
|
*/
|
||||||
isPortrait(): boolean {
|
isPortrait(): boolean {
|
||||||
return window.matchMedia('(orientation: portrait)').matches;
|
return this.win.matchMedia && this.win.matchMedia('(orientation: portrait)').matches;
|
||||||
}
|
}
|
||||||
|
|
||||||
testUserAgent(expression: string): boolean {
|
testUserAgent(expression: string): boolean {
|
||||||
return navigator.userAgent.indexOf(expression) >= 0;
|
const nav = this.win.navigator;
|
||||||
|
return !!(nav && nav.userAgent && nav.userAgent.indexOf(expression) >= 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the current url.
|
* Get the current url.
|
||||||
*/
|
*/
|
||||||
url() {
|
url() {
|
||||||
return window.location.href;
|
return this.win.location.href;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the width of the platform's viewport using `window.innerWidth`.
|
* Gets the width of the platform's viewport using `window.innerWidth`.
|
||||||
*/
|
*/
|
||||||
width() {
|
width() {
|
||||||
return window.innerWidth;
|
return this.win.innerWidth;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the height of the platform's viewport using `window.innerHeight`.
|
* Gets the height of the platform's viewport using `window.innerHeight`.
|
||||||
*/
|
*/
|
||||||
height(): number {
|
height(): number {
|
||||||
return window.innerHeight;
|
return this.win.innerHeight;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function readQueryParam(url: string, key: string) {
|
const readQueryParam = (url: string, key: string) => {
|
||||||
key = key.replace(/[\[]/, '\\[').replace(/[\]]/, '\\]');
|
key = key.replace(/[\[]/, '\\[').replace(/[\]]/, '\\]');
|
||||||
const regex = new RegExp('[\\?&]' + key + '=([^&#]*)');
|
const regex = new RegExp('[\\?&]' + key + '=([^&#]*)');
|
||||||
const results = regex.exec(url);
|
const results = regex.exec(url);
|
||||||
return results ? decodeURIComponent(results[1].replace(/\+/g, ' ')) : null;
|
return results ? decodeURIComponent(results[1].replace(/\+/g, ' ')) : null;
|
||||||
}
|
};
|
||||||
|
|
||||||
function proxyEvent<T>(emitter: Subject<T>, el: EventTarget, eventName: string) {
|
const proxyEvent = <T>(emitter: Subject<T>, el: EventTarget, eventName: string) => {
|
||||||
|
if ((el as any)) {
|
||||||
el.addEventListener(eventName, (ev: Event | undefined | null) => {
|
el.addEventListener(eventName, (ev: Event | undefined | null) => {
|
||||||
// ?? cordova might emit "null" events
|
// ?? cordova might emit "null" events
|
||||||
emitter.next(ev != null ? (ev as any).detail as T : undefined);
|
emitter.next(ev != null ? (ev as any).detail as T : undefined);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import { ComponentFactoryResolver, Injectable, Injector } from '@angular/core';
|
import { DOCUMENT } from '@angular/common';
|
||||||
|
import { ComponentFactoryResolver, Inject, Injectable, Injector } from '@angular/core';
|
||||||
import { PopoverOptions } from '@ionic/core';
|
import { PopoverOptions } from '@ionic/core';
|
||||||
|
|
||||||
import { OverlayBaseController } from '../util/overlay';
|
import { OverlayBaseController } from '../util/overlay';
|
||||||
@ -12,8 +13,9 @@ export class PopoverController extends OverlayBaseController<PopoverOptions, HTM
|
|||||||
private angularDelegate: AngularDelegate,
|
private angularDelegate: AngularDelegate,
|
||||||
private resolver: ComponentFactoryResolver,
|
private resolver: ComponentFactoryResolver,
|
||||||
private injector: Injector,
|
private injector: Injector,
|
||||||
|
@Inject(DOCUMENT) doc: any
|
||||||
) {
|
) {
|
||||||
super('ion-popover-controller');
|
super('ion-popover-controller', doc);
|
||||||
}
|
}
|
||||||
|
|
||||||
create(opts: PopoverOptions): Promise<HTMLIonPopoverElement> {
|
create(opts: PopoverOptions): Promise<HTMLIonPopoverElement> {
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import { Injectable } from '@angular/core';
|
import { DOCUMENT } from '@angular/common';
|
||||||
|
import { Inject, Injectable } from '@angular/core';
|
||||||
import { ToastOptions } from '@ionic/core';
|
import { ToastOptions } from '@ionic/core';
|
||||||
|
|
||||||
import { OverlayBaseController } from '../util/overlay';
|
import { OverlayBaseController } from '../util/overlay';
|
||||||
@ -7,7 +8,7 @@ import { OverlayBaseController } from '../util/overlay';
|
|||||||
providedIn: 'root',
|
providedIn: 'root',
|
||||||
})
|
})
|
||||||
export class ToastController extends OverlayBaseController<ToastOptions, HTMLIonToastElement> {
|
export class ToastController extends OverlayBaseController<ToastOptions, HTMLIonToastElement> {
|
||||||
constructor() {
|
constructor(@Inject(DOCUMENT) doc: any) {
|
||||||
super('ion-toast-controller');
|
super('ion-toast-controller', doc);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,26 +1,26 @@
|
|||||||
import { proxyMethod } from './util';
|
import { proxyMethod } from './util';
|
||||||
|
|
||||||
export class OverlayBaseController<Opts, Overlay> {
|
export class OverlayBaseController<Opts, Overlay> {
|
||||||
constructor(private ctrl: string) {}
|
constructor(private ctrl: string, private doc: Document) {}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a new overlay
|
* Creates a new overlay
|
||||||
*/
|
*/
|
||||||
create(opts?: Opts): Promise<Overlay> {
|
create(opts?: Opts): Promise<Overlay> {
|
||||||
return proxyMethod(this.ctrl, 'create', opts);
|
return proxyMethod(this.ctrl, this.doc, 'create', opts);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* When `id` is not provided, it dismisses the top overlay.
|
* When `id` is not provided, it dismisses the top overlay.
|
||||||
*/
|
*/
|
||||||
dismiss(data?: any, role?: string, id?: string): Promise<void> {
|
dismiss(data?: any, role?: string, id?: string): Promise<void> {
|
||||||
return proxyMethod(this.ctrl, 'dismiss', data, role, id);
|
return proxyMethod(this.ctrl, this.doc, 'dismiss', data, role, id);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the top overlay.
|
* Returns the top overlay.
|
||||||
*/
|
*/
|
||||||
getTop(): Promise<Overlay> {
|
getTop(): Promise<Overlay> {
|
||||||
return proxyMethod(this.ctrl, 'getTop');
|
return proxyMethod(this.ctrl, this.doc, 'getTop');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,15 +1,15 @@
|
|||||||
|
|
||||||
export function proxyMethod(ctrlName: string, methodName: string, ...args: any[]) {
|
export function proxyMethod(ctrlName: string, doc: Document, methodName: string, ...args: any[]) {
|
||||||
const controller = ensureElementInBody(ctrlName);
|
const controller = ensureElementInBody(ctrlName, doc);
|
||||||
return controller.componentOnReady()
|
return controller.componentOnReady()
|
||||||
.then(() => (controller as any)[methodName].apply(controller, args));
|
.then(() => (controller as any)[methodName].apply(controller, args));
|
||||||
}
|
}
|
||||||
|
|
||||||
export function ensureElementInBody(elementName: string) {
|
export function ensureElementInBody(elementName: string, doc: Document) {
|
||||||
let element = document.querySelector(elementName);
|
let element = doc.querySelector(elementName);
|
||||||
if (!element) {
|
if (!element) {
|
||||||
element = document.createElement(elementName);
|
element = doc.createElement(elementName);
|
||||||
document.body.appendChild(element);
|
doc.body.appendChild(element);
|
||||||
}
|
}
|
||||||
return element as HTMLStencilElement;
|
return element as HTMLStencilElement;
|
||||||
}
|
}
|
||||||
|
@ -140,34 +140,6 @@
|
|||||||
|
|
||||||
</body>
|
</body>
|
||||||
|
|
||||||
<script>
|
|
||||||
var segment = document.getElementById('browserSegment');
|
|
||||||
var results = document.getElementById('browserResults');
|
|
||||||
|
|
||||||
checkUrl();
|
|
||||||
|
|
||||||
segment.addEventListener('ionChange', function () {
|
|
||||||
checkUrl();
|
|
||||||
});
|
|
||||||
|
|
||||||
function checkUrl() {
|
|
||||||
var url = window.location.href;
|
|
||||||
|
|
||||||
if (url.search('#bookmarks') > -1) {
|
|
||||||
setResults('bookmarks');
|
|
||||||
} else if (url.search('#reading') > -1) {
|
|
||||||
setResults('reading');
|
|
||||||
} else if (url.search('#links') > -1) {
|
|
||||||
setResults('links');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function setResults(value) {
|
|
||||||
results.innerHTML = value.charAt(0).toUpperCase() + value.slice(1);
|
|
||||||
segment.value = value;
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
body {
|
body {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
|
@ -6,7 +6,7 @@ import { Config, configFromSession, configFromURL, saveConfig } from './config';
|
|||||||
|
|
||||||
declare const Context: any;
|
declare const Context: any;
|
||||||
|
|
||||||
const win = Context['window'] ? Context['window'] : typeof (window as any) !== 'undefined' ? window : {} as Window;
|
const win = typeof (window as any) !== 'undefined' ? window : {} as Window;
|
||||||
|
|
||||||
const Ionic = (win as any)['Ionic'] = (win as any)['Ionic'] || {};
|
const Ionic = (win as any)['Ionic'] = (win as any)['Ionic'] || {};
|
||||||
|
|
||||||
@ -36,7 +36,7 @@ if (config.getBoolean('persistConfig')) {
|
|||||||
// first see if the mode was set as an attribute on <html>
|
// first see if the mode was set as an attribute on <html>
|
||||||
// which could have been set by the user, or by prerendering
|
// which could have been set by the user, or by prerendering
|
||||||
// otherwise get the mode via config settings, and fallback to md
|
// otherwise get the mode via config settings, and fallback to md
|
||||||
const documentElement = win.document ? win.document.documentElement : null;
|
const documentElement = (win as any).document ? win.document.documentElement : null;
|
||||||
const mode = config.get('mode', (documentElement && documentElement.getAttribute('mode')) || (isPlatform(win, 'ios') ? 'ios' : 'md'));
|
const mode = config.get('mode', (documentElement && documentElement.getAttribute('mode')) || (isPlatform(win, 'ios') ? 'ios' : 'md'));
|
||||||
Ionic.mode = Context.mode = mode;
|
Ionic.mode = Context.mode = mode;
|
||||||
config.set('mode', mode);
|
config.set('mode', mode);
|
||||||
|
@ -1,4 +1,88 @@
|
|||||||
|
|
||||||
|
export type Platforms = keyof typeof PLATFORMS_MAP;
|
||||||
|
|
||||||
|
export const getPlatforms = (win: any) => setupPlatforms(win);
|
||||||
|
|
||||||
|
export const isPlatform = (win: Window, platform: Platforms) =>
|
||||||
|
getPlatforms(win).includes(platform);
|
||||||
|
|
||||||
|
export const setupPlatforms = (win: any) => {
|
||||||
|
win.Ionic = win.Ionic || {};
|
||||||
|
|
||||||
|
let platforms: string[] | undefined | null = win.Ionic.platforms;
|
||||||
|
if (platforms == null) {
|
||||||
|
platforms = win.Ionic.platforms = detectPlatforms(win);
|
||||||
|
platforms.forEach(p => win.document.documentElement.classList.add(`plt-${p}`));
|
||||||
|
}
|
||||||
|
return platforms;
|
||||||
|
};
|
||||||
|
|
||||||
|
const isMobileWeb = (win: Window): boolean =>
|
||||||
|
isMobile(win) && !isHybrid(win);
|
||||||
|
|
||||||
|
const detectPlatforms = (win: Window): string[] =>
|
||||||
|
Object.keys(PLATFORMS_MAP).filter(p => (PLATFORMS_MAP as any)[p](win));
|
||||||
|
|
||||||
|
const isIpad = (win: Window) =>
|
||||||
|
testUserAgent(win, /iPad/i);
|
||||||
|
|
||||||
|
const isIphone = (win: Window) =>
|
||||||
|
testUserAgent(win, /iPhone/i);
|
||||||
|
|
||||||
|
const isIOS = (win: Window) =>
|
||||||
|
testUserAgent(win, /iPad|iPhone|iPod/i);
|
||||||
|
|
||||||
|
const isAndroid = (win: Window) =>
|
||||||
|
testUserAgent(win, /android|sink/i);
|
||||||
|
|
||||||
|
const isPhablet = (win: Window) => {
|
||||||
|
const width = win.innerWidth;
|
||||||
|
const height = win.innerHeight;
|
||||||
|
const smallest = Math.min(width, height);
|
||||||
|
const largest = Math.max(width, height);
|
||||||
|
|
||||||
|
return (smallest > 390 && smallest < 520) &&
|
||||||
|
(largest > 620 && largest < 800);
|
||||||
|
};
|
||||||
|
|
||||||
|
const isTablet = (win: Window) => {
|
||||||
|
const width = win.innerWidth;
|
||||||
|
const height = win.innerHeight;
|
||||||
|
const smallest = Math.min(width, height);
|
||||||
|
const largest = Math.max(width, height);
|
||||||
|
return (smallest > 460 && smallest < 820) &&
|
||||||
|
(largest > 780 && largest < 1400);
|
||||||
|
};
|
||||||
|
|
||||||
|
const isMobile = (win: Window) =>
|
||||||
|
matchMedia(win, '(any-pointer:coarse)');
|
||||||
|
|
||||||
|
const isDesktop = (win: Window) =>
|
||||||
|
!isMobile(win);
|
||||||
|
|
||||||
|
const isHybrid = (win: Window) =>
|
||||||
|
isCordova(win) || isCapacitorNative(win);
|
||||||
|
|
||||||
|
const isCordova = (win: any): boolean =>
|
||||||
|
!!(win['cordova'] || win['phonegap'] || win['PhoneGap']);
|
||||||
|
|
||||||
|
const isCapacitorNative = (win: any): boolean => {
|
||||||
|
const capacitor = win['Capacitor'];
|
||||||
|
return !!(capacitor && capacitor.isNative);
|
||||||
|
};
|
||||||
|
|
||||||
|
const isElectron = (win: Window): boolean =>
|
||||||
|
testUserAgent(win, /electron/);
|
||||||
|
|
||||||
|
const isPWA = (win: Window): boolean =>
|
||||||
|
(win as any).matchMedia ? (win.matchMedia('(display-mode: standalone)').matches || (win.navigator as any).standalone) : false;
|
||||||
|
|
||||||
|
export const testUserAgent = (win: Window, expr: RegExp) =>
|
||||||
|
(win as any).navigator && (win.navigator as any).userAgent ? expr.test(win.navigator.userAgent) : false;
|
||||||
|
|
||||||
|
const matchMedia = (win: any, query: string): boolean =>
|
||||||
|
(win as any).matchMedia ? win.matchMedia(query).matches : false;
|
||||||
|
|
||||||
export const PLATFORMS_MAP = {
|
export const PLATFORMS_MAP = {
|
||||||
'ipad': isIpad,
|
'ipad': isIpad,
|
||||||
'iphone': isIphone,
|
'iphone': isIphone,
|
||||||
@ -15,104 +99,3 @@ export const PLATFORMS_MAP = {
|
|||||||
'desktop': isDesktop,
|
'desktop': isDesktop,
|
||||||
'hybrid': isHybrid
|
'hybrid': isHybrid
|
||||||
};
|
};
|
||||||
|
|
||||||
export type Platforms = keyof typeof PLATFORMS_MAP;
|
|
||||||
|
|
||||||
export function getPlatforms(win: any) {
|
|
||||||
return setupPlatforms(win);
|
|
||||||
}
|
|
||||||
|
|
||||||
export function isPlatform(win: Window, platform: Platforms) {
|
|
||||||
return getPlatforms(win).includes(platform);
|
|
||||||
}
|
|
||||||
|
|
||||||
export function setupPlatforms(win: any) {
|
|
||||||
win.Ionic = win.Ionic || {};
|
|
||||||
|
|
||||||
let platforms: string[] | undefined | null = win.Ionic.platforms;
|
|
||||||
if (platforms == null) {
|
|
||||||
platforms = win.Ionic.platforms = detectPlatforms(win);
|
|
||||||
platforms.forEach(p => win.document.documentElement.classList.add(`plt-${p}`));
|
|
||||||
}
|
|
||||||
return platforms;
|
|
||||||
}
|
|
||||||
|
|
||||||
function isMobileWeb(win: Window): boolean {
|
|
||||||
return isMobile(win) && !isHybrid(win);
|
|
||||||
}
|
|
||||||
|
|
||||||
function detectPlatforms(win: Window): string[] {
|
|
||||||
return Object.keys(PLATFORMS_MAP).filter(p => (PLATFORMS_MAP as any)[p](win));
|
|
||||||
}
|
|
||||||
|
|
||||||
function isIpad(win: Window) {
|
|
||||||
return testUserAgent(win, /iPad/i);
|
|
||||||
}
|
|
||||||
|
|
||||||
function isIphone(win: Window) {
|
|
||||||
return testUserAgent(win, /iPhone/i);
|
|
||||||
}
|
|
||||||
|
|
||||||
function isIOS(win: Window) {
|
|
||||||
return testUserAgent(win, /iPad|iPhone|iPod/i);
|
|
||||||
}
|
|
||||||
|
|
||||||
function isAndroid(win: Window) {
|
|
||||||
return testUserAgent(win, /android|sink/i);
|
|
||||||
}
|
|
||||||
|
|
||||||
function isPhablet(win: Window) {
|
|
||||||
const width = win.innerWidth;
|
|
||||||
const height = win.innerHeight;
|
|
||||||
const smallest = Math.min(width, height);
|
|
||||||
const largest = Math.max(width, height);
|
|
||||||
|
|
||||||
return (smallest > 390 && smallest < 520) &&
|
|
||||||
(largest > 620 && largest < 800);
|
|
||||||
}
|
|
||||||
|
|
||||||
function isTablet(win: Window) {
|
|
||||||
const width = win.innerWidth;
|
|
||||||
const height = win.innerHeight;
|
|
||||||
const smallest = Math.min(width, height);
|
|
||||||
const largest = Math.max(width, height);
|
|
||||||
return (smallest > 460 && smallest < 820) &&
|
|
||||||
(largest > 780 && largest < 1400);
|
|
||||||
}
|
|
||||||
|
|
||||||
function isMobile(win: Window) {
|
|
||||||
return matchMedia(win, '(any-pointer:coarse)');
|
|
||||||
}
|
|
||||||
|
|
||||||
function isDesktop(win: Window) {
|
|
||||||
return !isMobile(win);
|
|
||||||
}
|
|
||||||
|
|
||||||
function isHybrid(win: Window) {
|
|
||||||
return isCordova(win) || isCapacitorNative(win);
|
|
||||||
}
|
|
||||||
|
|
||||||
function isCordova(win: any): boolean {
|
|
||||||
return !!(win['cordova'] || win['phonegap'] || win['PhoneGap']);
|
|
||||||
}
|
|
||||||
|
|
||||||
function isCapacitorNative(win: any): boolean {
|
|
||||||
const capacitor = win['Capacitor'];
|
|
||||||
return !!(capacitor && capacitor.isNative);
|
|
||||||
}
|
|
||||||
|
|
||||||
function isElectron(win: Window): boolean {
|
|
||||||
return testUserAgent(win, /electron/);
|
|
||||||
}
|
|
||||||
|
|
||||||
function isPWA(win: Window): boolean {
|
|
||||||
return win.matchMedia('(display-mode: standalone)').matches || (win.navigator as any).standalone;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function testUserAgent(win: Window, expr: RegExp) {
|
|
||||||
return expr.test(win.navigator.userAgent);
|
|
||||||
}
|
|
||||||
|
|
||||||
function matchMedia(win: any, query: string): boolean {
|
|
||||||
return win.matchMedia ? win.matchMedia(query).matches : false;
|
|
||||||
}
|
|
||||||
|
Reference in New Issue
Block a user