diff --git a/packages/angular/src/index.ts b/packages/angular/src/index.ts index 7700956656..2569512c55 100644 --- a/packages/angular/src/index.ts +++ b/packages/angular/src/index.ts @@ -24,6 +24,7 @@ export { MenuController } from './providers/menu-controller'; export { ModalController, ModalProxy } from './providers/modal-controller'; export { NavController } from './providers/nav-controller'; export { NavParams } from './providers/nav-params'; +export { Platform } from './providers/platform'; export { PopoverController, PopoverProxy } from './providers/popover-controller'; export { ToastController, ToastProxy } from './providers/toast-controller'; diff --git a/packages/angular/src/module.ts b/packages/angular/src/module.ts index e0e504d3cb..2d6f8c3bb9 100644 --- a/packages/angular/src/module.ts +++ b/packages/angular/src/module.ts @@ -27,6 +27,7 @@ import { Events } from './providers/events'; import { LoadingController } from './providers/loading-controller'; import { MenuController } from './providers/menu-controller'; import { ModalController } from './providers/modal-controller'; +import { Platform } from './providers/platform'; import { PopoverController } from './providers/popover-controller'; import { ToastController } from './providers/toast-controller'; @@ -76,6 +77,7 @@ export class IonicAngularModule { LoadingController, MenuController, ModalController, + Platform, PopoverController, ToastController ] diff --git a/packages/angular/src/providers/app.ts b/packages/angular/src/providers/app.ts index c0ff592ac1..33dfa61281 100644 --- a/packages/angular/src/providers/app.ts +++ b/packages/angular/src/providers/app.ts @@ -19,13 +19,31 @@ export class App { return getRootNavsImpl(this); } + getRootNavsAsync(): Promise { + return getRootNavsAsyncImpl(this); + } + getTopNavs(rootNavId?: number): PublicNav[] { return getTopNavsImpl(this, rootNavId); } + getTopNavsAsync(rootNavId?: number): Promise { + return getTopNavsAsyncImpl(this, rootNavId); + } + getNavByIdOrName(nameOrId: number | string): PublicNav { return getNavByIdOrNameImpl(this, nameOrId); } + + getNavByIdOrNameAsync(nameOrId: number | string): Promise { + return getNavByIdOrNameAsyncImpl(this, nameOrId); + } + + registerBackButtonAction(fn: Function, priority = 0): Promise<() => void> { + return this._element.componentOnReady().then(() => { + return this._element.registerBackButtonAction(fn, priority); + }); + } } export function isScrollingImpl(app: App) { @@ -42,6 +60,12 @@ export function getRootNavsImpl(app: App) { return []; } +export function getRootNavsAsyncImpl(app: App) { + return app._element.componentOnReady().then(() => { + return app._element.getRootNavs(); + }); +} + export function getTopNavsImpl(app: App, rootNavId?: number): PublicNav[] { if (app._element && app._element.getTopNavs) { return app._element.getTopNavs(rootNavId); @@ -49,9 +73,21 @@ export function getTopNavsImpl(app: App, rootNavId?: number): PublicNav[] { return []; } +export function getTopNavsAsyncImpl(app: App, rootNavId?: number): Promise { + return app._element.componentOnReady().then(() => { + return app._element.getTopNavs(rootNavId); + }); +} + export function getNavByIdOrNameImpl(app: App, nameOrId: number | string): PublicNav { if (app._element && app._element.getNavByIdOrName) { return app._element.getNavByIdOrName(nameOrId); } return null; +} + +export function getNavByIdOrNameAsyncImpl(app: App, nameOrId: number | string): Promise { + return app._element.componentOnReady().then(() => { + return app._element.getNavByIdOrName(nameOrId); + }); } \ No newline at end of file diff --git a/packages/angular/src/providers/platform.ts b/packages/angular/src/providers/platform.ts new file mode 100644 index 0000000000..145152a0dd --- /dev/null +++ b/packages/angular/src/providers/platform.ts @@ -0,0 +1,228 @@ + +import { PlatformConfig } from '@ionic/core'; + +export type DocumentDirection = 'ltr' | 'rtl'; + +let dir: DocumentDirection = 'ltr'; +let isRtl = false; +let lang: string = ''; + +export class Platform { + + _element: HTMLIonPlatformElement; + constructor() { + initialize(this); + } + + is(platformName: string): boolean { + return isImpl(this, platformName); + } + + isAsync(platformName: string): Promise { + return isAsyncImpl(this, platformName); + } + + platforms(): string[] { + return platformsImpl(this); + } + + platformsAsync(): Promise { + return platformsAsyncImpl(this); + } + + versions(): PlatformConfig[] { + return versionsImpl(this); + } + + versionsAsync(): Promise { + return versionsAsyncImpl(this); + } + + ready(): Promise { + return readyImpl(this); + } + + get isRTL(): boolean { + return isRtl; + } + + setDir(_dir: DocumentDirection, updateDocument: boolean) { + dir = _dir; + isRtl = dir === 'rtl'; + + if (updateDocument !== false) { + document.documentElement.setAttribute('dir', dir); + } + } + + /** + * Returns app's language direction. + * We recommend the app's `index.html` file already has the correct `dir` + * attribute value set, such as `` or ``. + * [W3C: Structural markup and right-to-left text in HTML](http://www.w3.org/International/questions/qa-html-dir) + * @returns {DocumentDirection} + */ + dir(): DocumentDirection { + return dir; + } + + /** + * Set the app's language and optionally the country code, which will update + * the `lang` attribute on the app's root `` element. + * We recommend the app's `index.html` file already has the correct `lang` + * attribute value set, such as ``. This method is useful if + * the language needs to be dynamically changed per user/session. + * [W3C: Declaring language in HTML](http://www.w3.org/International/questions/qa-html-language-declarations) + * @param {string} language Examples: `en-US`, `en-GB`, `ar`, `de`, `zh`, `es-MX` + * @param {boolean} updateDocument Specifies whether the `lang` attribute of `` should be updated + */ + setLang(language: string, updateDocument: boolean) { + lang = language; + if (updateDocument !== false) { + document.documentElement.setAttribute('lang', language); + } + } + + /** + * Returns app's language and optional country code. + * We recommend the app's `index.html` file already has the correct `lang` + * attribute value set, such as ``. + * [W3C: Declaring language in HTML](http://www.w3.org/International/questions/qa-html-language-declarations) + * @returns {string} + */ + lang(): string { + return lang; + } + + /** + * Get the query string parameter + */ + getQueryParam(key: string): string { + return getQueryParamImpl(this, key); + } + + /** + * Get the query string parameter + */ + getQueryParamAsync(key: string): Promise { + return getQueryParamAsyncImpl(this, key); + } + + height(): number { + return window.innerHeight; + } + + isLandscape(): boolean { + return !this.isPortrait(); + } + + isPortrait(): boolean { + return window.matchMedia('(orientation: portrait)').matches; + } + + testUserAgent(expression: string): boolean { + return navigator.userAgent.indexOf(expression) >= 0; + } + + url() { + return window.location.href; + } + + width() { + return window.innerWidth; + } +} + +export function isImpl(platform: Platform, platformName: string) { + if (platform._element && platform._element.is) { + return platform._element.is(platformName); + } + return false; +} + +export function isAsyncImpl(platform: Platform, platformName: string) { + return getHydratedPlatform(platform).then(() => { + return platform._element.is(platformName); + }); +} + +export function platformsImpl(platform: Platform): string[] { + if (platform._element && platform._element.platforms) { + return platform._element.platforms(); + } + return []; +} + +export function platformsAsyncImpl(platform: Platform): Promise { + return getHydratedPlatform(platform).then(() => { + return platform._element.platforms(); + }); +} + +export function versionsImpl(platform: Platform): PlatformConfig[] { + if (platform._element && platform._element.versions) { + return platform._element.versions(); + } + return []; +} + +export function versionsAsyncImpl(platform: Platform): Promise { + return getHydratedPlatform(platform).then(() => { + return platform._element.versions(); + }); +} + +export function readyImpl(platform: Platform) { + return getHydratedPlatform(platform).then(() => { + return platform._element.ready(); + }); +} + +export function getQueryParamImpl(platform: Platform, key: string): string { + if (platform._element && platform._element.getQueryParam) { + return platform._element.getQueryParam(key); + } + return null; +} + +export function getQueryParamAsyncImpl(platform: Platform, key: string) { + return getHydratedPlatform(platform).then(() => { + return platform._element.getQueryParam(key); + }); +} + + + + +export function initialize(platform: Platform) { + // first see if there is an ion-app, if there is, platform will eventually show up + // if not, add platform to the document.body + const ionApp = document.querySelector('ion-app'); + if (ionApp) { + return ionApp.componentOnReady(() => { + platform._element = ionApp.querySelector('ion-platform'); + }); + } + + // okay, there isn't an ion-app, so add to the document.body + let platformElement = document.querySelector('ion-platform'); + if (!platformElement) { + platformElement = document.createElement('ion-platform'); + document.body.appendChild(platformElement); + } + platform._element = platformElement; +} + +export function getHydratedPlatform(platform: Platform): Promise { + if (!platform._element) { + const ionApp = document.querySelector('ion-app'); + return (ionApp as any).componentOnReady(() => { + const platformEl = ionApp.querySelector('ion-platform'); + return platformEl.componentOnReady().then(() => { + platform._element = platformEl; + return platformEl; + }); + }); + } + return platform._element.componentOnReady(); +} \ No newline at end of file diff --git a/packages/angular/src/router/outlet.ts b/packages/angular/src/router/outlet.ts index d4c73ba43b..e7fe97effc 100644 --- a/packages/angular/src/router/outlet.ts +++ b/packages/angular/src/router/outlet.ts @@ -137,7 +137,7 @@ export class RouterOutlet implements OnDestroy, OnInit, RouterDelegate { } export function activateRoute(navElement: HTMLIonNavElement, - component: Type, data: any = {}, cfr: ComponentFactoryResolver, injector: Injector, isTopLevel: boolean): Promise { + component: Type, data: any = {}, cfr: ComponentFactoryResolver, injector: Injector, isTopLevel: boolean): Promise { return getIonApp().then((ionApp) => { if (!ionApp) { diff --git a/packages/core/src/components.d.ts b/packages/core/src/components.d.ts index 7640f1cb5e..4b9bd5c3c1 100644 --- a/packages/core/src/components.d.ts +++ b/packages/core/src/components.d.ts @@ -2072,6 +2072,36 @@ declare global { } +import { + Platform as IonPlatform +} from './components/platform/platform'; + +declare global { + interface HTMLIonPlatformElement extends IonPlatform, HTMLStencilElement { + } + var HTMLIonPlatformElement: { + prototype: HTMLIonPlatformElement; + new (): HTMLIonPlatformElement; + }; + interface HTMLElementTagNameMap { + "ion-platform": HTMLIonPlatformElement; + } + interface ElementTagNameMap { + "ion-platform": HTMLIonPlatformElement; + } + namespace JSX { + interface IntrinsicElements { + "ion-platform": JSXElements.IonPlatformAttributes; + } + } + namespace JSXElements { + export interface IonPlatformAttributes extends HTMLAttributes { + + } + } +} + + import { PopoverController as IonPopoverController } from './components/popover-controller/popover-controller'; diff --git a/packages/core/src/components/app/app.tsx b/packages/core/src/components/app/app.tsx index 6396c1c288..d8e7d40aae 100644 --- a/packages/core/src/components/app/app.tsx +++ b/packages/core/src/components/app/app.tsx @@ -2,10 +2,10 @@ import { Component, Element, Event, EventEmitter, Listen, Method, Prop, State } import { Config, NavEvent, OverlayController, PublicNav, PublicViewController } from '../../index'; import { getOrAppendElement } from '../../utils/helpers'; -import { isCordova } from '../../global/platform-utils'; const rootNavs = new Map(); const ACTIVE_SCROLLING_TIME = 100; +let backButtonActions: BackButtonAction[] = []; @Component({ tag: 'ion-app', @@ -143,8 +143,51 @@ export class App { return null; } + + /** + * The back button event is triggered when the user presses the native + * platform's back button, also referred to as the "hardware" back button. + * This event is only used within Cordova apps running on Android and + * Windows platforms. This event is not fired on iOS since iOS doesn't come + * with a hardware back button in the same sense an Android or Windows device + * does. + * + * Registering a hardware back button action and setting a priority allows + * apps to control which action should be called when the hardware back + * button is pressed. This method decides which of the registered back button + * actions has the highest priority and should be called. + * + * @param {Function} fn Called when the back button is pressed, + * if this registered action has the highest priority. + * @param {number} priority Set the priority for this action. Only the highest priority will execute. Defaults to `0`. + * @returns {Function} A function that, when called, will unregister + * the back button action. + */ + @Method() + registerBackButtonAction(fn: Function, priority = 0): () => void { + const newAction = { + fn, + priority + }; + backButtonActions.push(newAction); + + return () => { + backButtonActions = backButtonActions.filter(bbAction => bbAction !== newAction); + }; + } + @Listen('document:backbutton') hardwareBackButtonPressed() { + // okay cool, we need to execute the user's custom method if they have one + const actionToExecute = backButtonActions.reduce((previous, current) => { + if (current.priority >= previous.priority) { + return current; + } + return previous; + }); + actionToExecute && actionToExecute.fn && actionToExecute.fn(); + + // okay great, we've done the user action, now do the default actions // check if menu exists and is open return checkIfMenuIsOpen().then((done: boolean) => { if (!done) { @@ -199,9 +242,9 @@ export class App { render() { const isDevice = true; return [ - isCordova() && , isDevice && , isDevice && , + , ]; } @@ -347,3 +390,8 @@ export interface ExitAppEvent extends CustomEvent { export interface ExitAppEventDetail { } + +export interface BackButtonAction { + fn: Function; + priority: number; +} diff --git a/packages/core/src/components/app/readme.md b/packages/core/src/components/app/readme.md index e74516f2d3..527bea63f2 100644 --- a/packages/core/src/components/app/readme.md +++ b/packages/core/src/components/app/readme.md @@ -47,6 +47,21 @@ Returns whether the application is enabled or not Boolean if the app is actively scrolling or not. +#### registerBackButtonAction() + +The back button event is triggered when the user presses the native +platform's back button, also referred to as the "hardware" back button. +This event is only used within Cordova apps running on Android and +Windows platforms. This event is not fired on iOS since iOS doesn't come +with a hardware back button in the same sense an Android or Windows device +does. + +Registering a hardware back button action and setting a priority allows +apps to control which action should be called when the hardware back +button is pressed. This method decides which of the registered back button +actions has the highest priority and should be called. + + #### setExternalNavPromise() Updates the Promise set by an external navigation system diff --git a/packages/core/src/components/platform/platform.tsx b/packages/core/src/components/platform/platform.tsx new file mode 100644 index 0000000000..a7f5275396 --- /dev/null +++ b/packages/core/src/components/platform/platform.tsx @@ -0,0 +1,133 @@ +import { Component, Element, Method, Prop } from '@stencil/core'; + +import { PlatformConfig } from '../../index'; +import { isCordova } from '../../global/platform-utils'; + +@Component({ + tag: 'ion-platform', +}) +export class Platform { + + @Prop({ context: 'platforms' }) _platforms: PlatformConfig[]; + @Prop({ context: 'readQueryParam'}) readQueryParam: (url: string, key: string) => string; + @Element() el: HTMLElement; + + /** + * Depending on the platform the user is on, `is(platformName)` will + * return `true` or `false`. Note that the same app can return `true` + * for more than one platform name. For example, an app running from + * an iPad would return `true` for the platform names: `mobile`, + * `ios`, `ipad`, and `tablet`. Additionally, if the app was running + * from Cordova then `cordova` would be true, and if it was running + * from a web browser on the iPad then `mobileweb` would be `true`. + * + * * + * ``` + * import { Platform } from 'ionic-angular'; + * + * @Component({...}) + * export MyPage { + * constructor(public platform: Platform) { + * if (this.platform.is('ios')) { + * // This will only print when on iOS + * console.log('I am an iOS device!'); + * } + * } + * } + * ``` + * + * | Platform Name | Description | + * |-----------------|------------------------------------| + * | android | on a device running Android. | + * | cordova | on a device running Cordova. | + * | core | on a desktop device. | + * | ios | on a device running iOS. | + * | ipad | on an iPad device. | + * | iphone | on an iPhone device. | + * | mobile | on a mobile device. | + * | mobileweb | in a browser on a mobile device. | + * | phablet | on a phablet device. | + * | tablet | on a tablet device. | + * | windows | on a device running Windows. | + * + * @param {string} platformName + */ + @Method() + is(platformName: string): boolean { + for (const platform of this._platforms) { + if (platform.name === platformName) { + return true; + } + } + return false; + } + + /** + * @returns {array} the array of platforms + * @description + * Depending on what device you are on, `platforms` can return multiple values. + * Each possible value is a hierarchy of platforms. For example, on an iPhone, + * it would return `mobile`, `ios`, and `iphone`. + * + * ``` + * import { Platform } from 'ionic-angular'; + * + * @Component({...}) + * export MyPage { + * constructor(public platform: Platform) { + * // This will print an array of the current platforms + * console.log(this.platform.platforms()); + * } + * } + * ``` + */ + @Method() + platforms() { + return this._platforms.map(platform => platform.name); + } + + @Method() + versions() { + return this._platforms; + } + + /** + * Returns whether the device is in landscape orientation + */ + @Method() + isLandscape(): boolean { + return !this.isPortrait(); + } + + /** + * Returns whether the device is in portration orientation + */ + @Method() + isPortrait(): boolean { + return window.matchMedia('(orientation: portrait)').matches; + } + + @Method() + ready() { + // revisit this later on + if (isCordova()) { + const cordovaPlatform = this.el.querySelector('ion-cordova-plaform') as any; + return cordovaPlatform.componentOnReady().then(() => { + return cordovaPlatform.ready(); + }); + } + return Promise.resolve(); + } + + @Method() + getQueryParam(param: string): string { + return this.readQueryParam(window.location.href, param); + } + + render() { + return [ + + ]; + } + +} diff --git a/packages/core/src/components/platform/readme.md b/packages/core/src/components/platform/readme.md new file mode 100644 index 0000000000..0ab913a6af --- /dev/null +++ b/packages/core/src/components/platform/readme.md @@ -0,0 +1,50 @@ +# ion-platform + + + + + + +## Methods + +#### getQueryParam() + + +#### is() + +Depending on the platform the user is on, `is(platformName)` will +return `true` or `false`. Note that the same app can return `true` +for more than one platform name. For example, an app running from +an iPad would return `true` for the platform names: `mobile`, +`ios`, `ipad`, and `tablet`. Additionally, if the app was running +from Cordova then `cordova` would be true, and if it was running +from a web browser on the iPad then `mobileweb` would be `true`. + +* +``` +import { Platform } from 'ionic-angular'; + + +#### isLandscape() + +Returns whether the device is in landscape orientation + + +#### isPortrait() + +Returns whether the device is in portration orientation + + +#### platforms() + + +#### ready() + + +#### versions() + + + +---------------------------------------------- + +*Built with [StencilJS](https://stenciljs.com/)* diff --git a/packages/core/src/components/platform/test/basic/index.html b/packages/core/src/components/platform/test/basic/index.html new file mode 100644 index 0000000000..24101e0737 --- /dev/null +++ b/packages/core/src/components/platform/test/basic/index.html @@ -0,0 +1,76 @@ + + + + + + Platform Basic + + + + + + + + + + Platform - basic + + + + +

The Platforms are:

+
    + +

    The Platforms versions are:

    +
      + +

      The orientation is

      + +

      The ready event has fired:

      +
      + +
      +
      + + + + + diff --git a/packages/core/src/global/config-controller.ts b/packages/core/src/global/config-controller.ts index 3884e17f81..ead33fdad1 100644 --- a/packages/core/src/global/config-controller.ts +++ b/packages/core/src/global/config-controller.ts @@ -1,5 +1,5 @@ import { Config } from '../index'; -import { PlatformConfig, queryParam } from './platform-configs'; +import { PlatformConfig, readQueryParam } from './platform-configs'; import { isDef } from '../utils/helpers'; export function createConfigController(configObj: any, platforms: PlatformConfig[]): Config { @@ -7,7 +7,7 @@ export function createConfigController(configObj: any, platforms: PlatformConfig function get(key: string, fallback?: any): any { - const queryValue = queryParam(window.location.href, `ionic${key}`); + const queryValue = readQueryParam(window.location.href, `ionic${key}`); if (isDef(queryValue)) { return configObj[key] = (queryValue === 'true' ? true : queryValue === 'false' ? false : queryValue); } diff --git a/packages/core/src/global/ionic-global.ts b/packages/core/src/global/ionic-global.ts index d4719701e6..7def9c9da3 100644 --- a/packages/core/src/global/ionic-global.ts +++ b/packages/core/src/global/ionic-global.ts @@ -1,6 +1,6 @@ import 'ionicons'; import { createConfigController } from './config-controller'; -import { PLATFORM_CONFIGS, detectPlatforms } from './platform-configs'; +import { PLATFORM_CONFIGS, detectPlatforms, readQueryParam } from './platform-configs'; import { createDomControllerClient } from './dom-controller'; @@ -15,11 +15,19 @@ if (!Context.dom) { Context.dom = createDomControllerClient(window, now); } +if (!Context.platforms) { + Context.platforms = detectPlatforms(window.location.href, window.navigator.userAgent, PLATFORM_CONFIGS, 'core'); +} + +if (!Context.readQueryParam) { + Context.readQueryParam = readQueryParam; +} + // create the Ionic.config from raw config object (if it exists) // and convert Ionic.config into a ConfigApi that has a get() fn Ionic.config = Context.config = createConfigController( Ionic.config, - detectPlatforms(window.location.href, window.navigator.userAgent, PLATFORM_CONFIGS, 'core') + Context.platforms ); diff --git a/packages/core/src/global/platform-configs.ts b/packages/core/src/global/platform-configs.ts index 584ae05138..8f72e4fa3c 100644 --- a/packages/core/src/global/platform-configs.ts +++ b/packages/core/src/global/platform-configs.ts @@ -1,10 +1,21 @@ -import { isCordova } from './platform-utils'; +import { isCordova, isElectron, } from './platform-utils'; -const IPAD = 'ipad'; -const IPHONE = 'iphone'; -const IOS = 'ios'; -const WINDOWS_PHONE = ['windows phone']; +import { + ANDROID, + CORDOVA, + CORE, + ELECTRON, + IOS, + IPAD, + IPHONE, + MOBILE, + PHABLET, + TABLET, + WINDOWS_PHONE, +} from './platform-utils'; +const width = window.innerWidth; +const height = window.innerHeight; // order from most specifc to least specific export const PLATFORM_CONFIGS: PlatformConfig[] = [ @@ -30,21 +41,59 @@ export const PLATFORM_CONFIGS: PlatformConfig[] = [ }, { - name: 'android', + name: ANDROID, settings: { activator: 'ripple', mode: 'md', }, - isMatch: (url, userAgent) => isPlatformMatch(url, userAgent, 'android', ['android', 'silk'], WINDOWS_PHONE) + isMatch: (url, userAgent) => isPlatformMatch(url, userAgent, ANDROID, [ANDROID, 'silk'], WINDOWS_PHONE) }, { - name: 'core', + name: CORE, settings: { mode: 'md' } }, + { + name: PHABLET, + isMatch: () => { + const smallest = Math.min(width, height); + const largest = Math.max(width, height); + return (smallest > 390 && smallest < 520) && + (largest > 620 && largest < 800); + } + }, + + { + name: MOBILE + }, + + { + name: TABLET, + isMatch: () => { + const smallest = Math.min(width, height); + const largest = Math.max(width, height); + return (smallest > 460 && smallest < 820) && + (largest > 780 && largest < 1400); + } + }, + + { + name: CORDOVA, + isMatch: () => { + return isCordova(); + } + }, + + { + name: ELECTRON, + isMatch: () => { + return isElectron(); + } + } + ]; export function detectPlatforms(url: string, userAgent: string, platforms: PlatformConfig[], defaultPlatform: string) { @@ -59,7 +108,7 @@ export function detectPlatforms(url: string, userAgent: string, platforms: Platf } export function isPlatformMatch(url: string, userAgent: string, platformName: string, userAgentAtLeastHas: string[], userAgentMustNotHave: string[]) { - const queryValue = queryParam(url, 'ionicplatform'); + const queryValue = readQueryParam(url, 'ionicplatform'); if (queryValue) { return queryValue === platformName; } @@ -83,7 +132,7 @@ export function isPlatformMatch(url: string, userAgent: string, platformName: st } -export function queryParam(url: string, key: string) { +export function readQueryParam(url: string, key: string) { key = key.replace(/[\[]/, '\\[').replace(/[\]]/, '\\]'); const regex = new RegExp('[\\?&]' + key + '=([^&#]*)'); const results = regex.exec(url); diff --git a/packages/core/src/global/platform-utils.ts b/packages/core/src/global/platform-utils.ts index af851ccea2..333d03b5b6 100644 --- a/packages/core/src/global/platform-utils.ts +++ b/packages/core/src/global/platform-utils.ts @@ -1,5 +1,35 @@ export function isCordova(): boolean { const win = window as any; - return !!(win['cordova'] || win['PhoneGap'] || win['phonegap'] || win['Capacitor']); + return !!(win[CORDOVA] || win[PHONEGAP_CAMELCASE] || win[PHONEGAP] || win[CAPACITOR]); } + +export function isElectron(): boolean { + return testUserAgent(getUserAgent(), ELECTRON); +} + +export function getUserAgent(): string { + return window.navigator.userAgent; +} + +export function testUserAgent(userAgent: string, expression: string): boolean { + return userAgent ? userAgent.indexOf(expression) >= 0 : false; +} + +export const ANDROID = 'android'; +export const CORDOVA = 'cordova'; +export const CORE = 'core'; +export const ELECTRON = 'electron'; +export const IOS = 'ios'; +export const IPAD = 'ipad'; +export const IPHONE = 'iphone'; +export const MOBILE = 'mobile'; +export const MOBILE_WEB = 'mobileweb'; +export const PHABLET = 'phablet'; +export const TABLET = 'tablet'; +export const WINDOWS_PHONE = ['windows phone']; + +export const PHONEGAP = 'phonegap'; +export const PHONEGAP_CAMELCASE = 'PhoneGap'; +export const CAPACITOR = 'Capacitor'; + diff --git a/packages/core/src/global/test/query-param.spec.ts b/packages/core/src/global/test/query-param.spec.ts index 5f2459e184..7c249db066 100644 --- a/packages/core/src/global/test/query-param.spec.ts +++ b/packages/core/src/global/test/query-param.spec.ts @@ -1,19 +1,26 @@ -import { queryParam } from '../platform-configs'; +import { readQueryParam } from '../platform-configs'; -describe('QueryParam', () => { +describe('readQueryParam', () => { it('should read the url for a queryParam', () => { - const qp = queryParam('?boolean=false', 'boolean'); + const qp = readQueryParam('?boolean=false', 'boolean'); expect(qp).toBeDefined(); expect(qp).toEqual('false'); }); it('should get the value of queryParam', () => { - const qp = queryParam('?keyValue=b', 'keyValue'); + const qp = readQueryParam('?keyValue=b', 'keyValue'); expect(qp).toEqual('b'); }); - it('should show nullfor a queryParam this is not passed', () => { - const qp = queryParam('', 'ionicanimate'); + it('should show null for a queryParam this is not passed', () => { + const qp = readQueryParam('', 'ionicanimate'); expect(qp).toBeNull(); }); + + it('should get some params', () => { + const url = 'http://www.google.com/blah?taco=yum&burrito=yesplease&enchillada=dope'; + expect(readQueryParam(url, 'taco')).toEqual('yum'); + expect(readQueryParam(url, 'burrito')).toEqual('yesplease'); + expect(readQueryParam(url, 'enchillada')).toEqual('dope'); + }); }); diff --git a/packages/core/src/index.d.ts b/packages/core/src/index.d.ts index 92f9ac354c..d1c91bfd96 100644 --- a/packages/core/src/index.d.ts +++ b/packages/core/src/index.d.ts @@ -110,6 +110,8 @@ export { ToastController } from './components/toast-controller/toast-controller' export { Toggle } from './components/toggle/toggle'; export { Toolbar } from './components/toolbar/toolbar'; +export { PlatformConfig } from './global/platform-configs'; + // export all of the component declarations that are dynamically created export * from './components'; diff --git a/packages/core/stencil.config.js b/packages/core/stencil.config.js index e566f972ac..86b9361719 100644 --- a/packages/core/stencil.config.js +++ b/packages/core/stencil.config.js @@ -46,7 +46,7 @@ exports.config = { { components: ['ion-toggle'] }, { components: ['ion-toast', 'ion-toast-controller'] }, { components: ['ion-tap-click', 'ion-status-tap'] }, - { components: ['ion-cordova-platform'] }, + { components: ['ion-platform', 'ion-cordova-platform'] }, { components: ['ion-nav-pop'] }, ], plugins: [