feat(platform): add backbutton event

This commit is contained in:
Adam Bradley
2016-04-09 21:54:31 -05:00
parent ecf93023f3
commit 156fdc35ed
4 changed files with 139 additions and 169 deletions

View File

@@ -1,6 +1,7 @@
import {getQuerystring, assign} from '../util/util';
import {ready, windowDimensions, flushDimensionCache} from '../util/dom';
import {Config} from '../config/config';
import {EventEmitter} from 'angular2/core';
import {getQuerystring} from '../util/util';
import {ready, windowDimensions, flushDimensionCache} from '../util/dom';
/**
* @name Platform
@@ -25,7 +26,7 @@ import {Config} from '../config/config';
*/
export class Platform {
private _platforms: Array<string>;
private _versions: any = {};
private _versions: {[name: string]: PlatformVersion} = {};
private _dir: string;
private _lang: string;
private _url: string;
@@ -35,14 +36,8 @@ export class Platform {
private _onResizes: Array<Function> = [];
private _readyPromise: Promise<any>;
private _readyResolve: any;
private _engineReady: any;
private _resizeTm: any;
/**
* @private
*/
platformOverride: string;
constructor(platforms = []) {
this._platforms = platforms;
this._readyPromise = new Promise(res => { this._readyResolve = res; } );
@@ -124,7 +119,7 @@ export class Platform {
/**
* Returns an object containing information about the paltform
* Returns an object containing information about the platforms.
*
* ```
* import {Platform} from 'ionic-angular';
@@ -132,8 +127,7 @@ export class Platform {
* @Page({...})
* export MyPage {
* constructor(platform: Platform) {
* this.platform = platform;
* console.log(this.platform.versions());
* console.log(platform.versions());
* }
* }
* ```
@@ -142,12 +136,7 @@ export class Platform {
* @returns {object} An object with various platform info
*
*/
versions(platformName: string): any {
if (arguments.length) {
// get a specific platform's version
return this._versions[platformName];
}
versions(): {[name: string]: PlatformVersion} {
// get all the platforms that have a valid parsed version
return this._versions;
}
@@ -155,7 +144,7 @@ export class Platform {
/**
* @private
*/
version(): any {
version(): PlatformVersion {
for (let platformName in this._versions) {
if (this._versions[platformName]) {
return this._versions[platformName];
@@ -165,7 +154,11 @@ export class Platform {
}
/**
* Returns a promise when the platform is ready and native functionality can be called
* Returns a promise when the platform is ready and native functionality
* can be called. If the app is running from within a web browser, then
* the promise will resolve when the DOM is ready. When the app is running
* from an application engine such as Cordova, then the promise
* will resolve when Cordova triggers the `deviceready` event.
*
* ```
* import {Platform} from 'ionic-angular';
@@ -173,39 +166,37 @@ export class Platform {
* @Page({...})
* export MyPage {
* constructor(platform: Platform) {
* this.platform = platform;
* this.platform.ready().then(() => {
* platform.ready().then(() => {
* console.log('Platform ready');
* // The platform is now ready, execute any native code you want
* });
* }
* }
* ```
* @returns {promise} Returns a promsie when device ready has fired
* @returns {promise}
*/
ready(): Promise<any> {
// this is the default if it's not replaced by the engine
// if there was no custom ready method from the engine
// then use the default DOM ready
return this._readyPromise;
}
/**
* @private
*/
prepareReady(config: Config) {
let self = this;
triggerReady() {
this._readyResolve();
}
function resolve() {
self._readyResolve(config);
}
if (this._engineReady) {
// the engine provide a ready promise, use this instead
this._engineReady(resolve);
} else {
// there is no custom ready method from the engine
// use the default dom ready
ready(resolve);
}
/**
* @private
*/
prepareReady() {
// this is the default prepareReady if it's not replaced by the engine
// if there was no custom ready method from the engine
// then use the default DOM ready
ready(this.triggerReady.bind(this));
}
/**
@@ -276,31 +267,30 @@ export class Platform {
// Methods meant to be overridden by the engine
// **********************************************
// Provided NOOP methods so they do not error when
// called by engines (the browser) doesn't provide them
/**
* @private
*/
on() {}
/**
* @private
*/
onHardwareBackButton() {}
/**
* @private
*/
registerBackButtonAction() {}
// called by engines (the browser)that do not provide them
/**
* @private
*/
exitApp() {}
// Events meant to be triggered by the engine
// **********************************************
/**
* @private
*/
fullScreen() {}
backButton: EventEmitter<any> = new EventEmitter();
/**
* @private
*/
showStatusBar() {}
pause: EventEmitter<any> = new EventEmitter();
/**
* @private
*/
resume: EventEmitter<any> = new EventEmitter();
// Getter/Setter Methods
@@ -427,21 +417,21 @@ export class Platform {
/**
* @private
*/
static register(platformConfig) {
static register(platformConfig: PlatformConfig) {
platformRegistry[platformConfig.name] = platformConfig;
}
/**
* @private
*/
static registry(): any {
static registry() {
return platformRegistry;
}
/**
* @private
*/
static get(platformName: string): any {
static get(platformName: string): PlatformConfig {
return platformRegistry[platformName] || {};
}
@@ -513,15 +503,13 @@ export class Platform {
/**
* @private
*/
load(platformOverride?: string) {
let rootPlatformNode = null;
let engineNode = null;
load(config: Config) {
let rootPlatformNode: PlatformNode;
let enginePlatformNode: PlatformNode;
let self = this;
this.platformOverride = platformOverride;
// figure out the most specific platform and active engine
let tmpPlatform = null;
let tmpPlatform: PlatformNode;
for (let platformName in platformRegistry) {
tmpPlatform = this.matchPlatform(platformName);
@@ -532,7 +520,7 @@ export class Platform {
if (tmpPlatform.isEngine) {
// because it matched then this should be the active engine
// you cannot have more than one active engine
engineNode = tmpPlatform;
enginePlatformNode = tmpPlatform;
} else if (!rootPlatformNode || tmpPlatform.depth > rootPlatformNode.depth) {
// only find the root node for platforms that are not engines
@@ -553,20 +541,13 @@ export class Platform {
if (rootPlatformNode) {
// check if we found an engine node (cordova/node-webkit/etc)
if (engineNode) {
if (enginePlatformNode) {
// add the engine to the first in the platform hierarchy
// the original rootPlatformNode now becomes a child
// of the engineNode, which is not the new root
engineNode.child = rootPlatformNode;
rootPlatformNode.parent = engineNode;
rootPlatformNode = engineNode;
// add any events which the engine would provide
// for example, Cordova provides its own ready event
let engineMethods = engineNode.methods();
engineMethods._engineReady = engineMethods.ready;
delete engineMethods.ready;
assign(this, engineMethods);
enginePlatformNode.child = rootPlatformNode;
rootPlatformNode.parent = enginePlatformNode;
rootPlatformNode = enginePlatformNode;
}
let platformNode = rootPlatformNode;
@@ -585,12 +566,14 @@ export class Platform {
platformNode = rootPlatformNode;
while (platformNode) {
platformNode.initialize(this, config);
// set the array of active platforms with
// the last one in the array the most important
this._platforms.push(platformNode.name());
this._platforms.push(platformNode.name);
// get the platforms version if a version parser was provided
this._versions[platformNode.name()] = platformNode.version(this);
this._versions[platformNode.name] = platformNode.version(this);
// go to the next platform child
platformNode = platformNode.child;
@@ -605,7 +588,7 @@ export class Platform {
/**
* @private
*/
matchPlatform(platformName: string): any {
private matchPlatform(platformName: string): PlatformNode {
// build a PlatformNode and assign config data to it
// use it's getRoot method to build up its hierarchy
// depending on which platforms match
@@ -640,24 +623,24 @@ function insertSuperset(platformNode: PlatformNode) {
}
}
/**
* @private
*/
class PlatformNode {
private c;
private c: PlatformConfig;
parent: PlatformNode;
child: PlatformNode;
name: string;
isEngine: boolean;
depth: number;
constructor(platformName: string) {
this.c = Platform.get(platformName);
this.name = platformName;
this.isEngine = this.c.isEngine;
}
name(): string {
return this.c.name;
}
settings(): any {
return this.c.settings || {};
}
@@ -666,22 +649,15 @@ class PlatformNode {
return this.c.superset;
}
methods(): any {
return this.c.methods || {};
}
isMatch(p: Platform): boolean {
if (p.platformOverride && !this.isEngine) {
return (p.platformOverride === this.c.name);
} else if (!this.c.isMatch) {
return false;
}
return this.c.isMatch(p);
return this.c.isMatch && this.c.isMatch(p) || false;
}
version(p: Platform): any {
initialize(platform: Platform, config: Config) {
this.c.initialize && this.c.initialize(platform, config);
}
version(p: Platform): PlatformVersion {
if (this.c.versionParser) {
let v = this.c.versionParser(p);
if (v) {
@@ -699,7 +675,7 @@ class PlatformNode {
getRoot(p: Platform): PlatformNode {
if (this.isMatch(p)) {
let parents = this.getSubsetParents(this.name());
let parents = this.getSubsetParents(this.name);
if (!parents.length) {
return this;
@@ -742,5 +718,23 @@ class PlatformNode {
}
let platformRegistry = {};
let platformDefault = null;
let platformRegistry: {[name: string]: PlatformConfig} = {};
let platformDefault: string = null;
export interface PlatformConfig {
name?: string;
isEngine?: boolean;
initialize?: Function;
isMatch?: Function;
superset?: string;
subsets?: string[];
settings?: any;
versionParser?: any;
}
export interface PlatformVersion {
str?: string;
num?: number;
major?: number;
minor?: number
}

View File

@@ -1,4 +1,5 @@
import {Platform} from './platform';
import {Config} from '../config/config';
import {windowLoad} from '../util/dom';
const win: any = window;
@@ -166,16 +167,38 @@ Platform.register({
Platform.register({
name: 'cordova',
isEngine: true,
methods: {
ready: function(resolve) {
function isReady() {
doc.removeEventListener('deviceready', isReady);
resolve();
}
initialize: function(p: Platform, config: Config) {
// prepare a custom "ready" for cordova "deviceready"
p.prepareReady = function() {
// 1) ionic bootstrapped
windowLoad(function() {
doc.addEventListener('deviceready', isReady);
// 2) window onload triggered or completed
doc.addEventListener('deviceready', () => {
// 3) cordova deviceready event triggered
// add cordova listeners to fire platform events
doc.addEventListener('backbutton', function() {
p.backButton.emit(null);
});
// doc.addEventListener('pause', function() {
// p.pause.emit(null);
// });
// doc.addEventListener('resume', function() {
// p.resume.emit(null);
// });
// cordova has fully loaded and we've added listeners
p.triggerReady();
});
});
}
};
// cordova has its own exitApp method
p.exitApp = function() {
win.navigator.app.exitApp();
};
},
isMatch(): boolean {
return !!(win.cordova || win.PhoneGap || win.phonegap);

View File

@@ -5,22 +5,13 @@ export function run() {
it('should set core as the fallback', () => {
let platform = new Platform();
platform.setUserAgent('idk');
platform.load();
platform.load(null);
expect(platform.is('android')).toEqual(false);
expect(platform.is('ios')).toEqual(false);
expect(platform.is('core')).toEqual(true);
});
it('should set android via platformOverride, despite ios user agent', () => {
let platform = new Platform();
platform.setUserAgent(IPAD_UA);
platform.load('android');
expect(platform.is('android')).toEqual(true);
expect(platform.is('ios')).toEqual(false);
});
it('should get case insensitive querystring value', () => {
let platform = new Platform();
platform.setUrl('/?KEY=value');
@@ -35,48 +26,10 @@ export function run() {
expect(platform.query('key')).toEqual('value');
});
it('should set ios via platformOverride, despite android querystring', () => {
let platform = new Platform();
platform.setUrl('/?ionicplatform=android');
platform.load('ios');
expect(platform.is('android')).toEqual(false);
expect(platform.is('windows')).toEqual(false);
expect(platform.is('ios')).toEqual(true);
});
it('should set windows via platformOverride, despite android querystring', () => {
let platform = new Platform();
platform.setUrl('/?ionicplatform=android');
platform.load('windows');
expect(platform.is('android')).toEqual(false);
expect(platform.is('windows')).toEqual(true);
expect(platform.is('ios')).toEqual(false);
});
it('should set ios via platformOverride', () => {
let platform = new Platform();
platform.load('ios');
expect(platform.is('android')).toEqual(false);
expect(platform.is('windows')).toEqual(false);
expect(platform.is('ios')).toEqual(true);
});
it('should set android via platformOverride', () => {
let platform = new Platform();
platform.load('android');
expect(platform.is('android')).toEqual(true);
expect(platform.is('windows')).toEqual(false);
expect(platform.is('ios')).toEqual(false);
});
it('should set windows via querystring', () => {
let platform = new Platform();
platform.setUrl('/?ionicplatform=windows');
platform.load();
platform.load(null);
expect(platform.is('mobile')).toEqual(true);
expect(platform.is('android')).toEqual(false);
@@ -87,7 +40,7 @@ export function run() {
it('should set ios via querystring', () => {
let platform = new Platform();
platform.setUrl('/?ionicplatform=ios');
platform.load();
platform.load(null);
expect(platform.is('mobile')).toEqual(true);
expect(platform.is('android')).toEqual(false);
@@ -99,7 +52,7 @@ export function run() {
let platform = new Platform();
platform.setUrl('/?ionicplatform=windows');
platform.setUserAgent(ANDROID_UA);
platform.load();
platform.load(null);
expect(platform.is('android')).toEqual(false);
expect(platform.is('windows')).toEqual(true);
@@ -110,7 +63,7 @@ export function run() {
let platform = new Platform();
platform.setUrl('/?ionicplatform=ios');
platform.setUserAgent(ANDROID_UA);
platform.load();
platform.load(null);
expect(platform.is('android')).toEqual(false);
expect(platform.is('windows')).toEqual(false);
@@ -120,7 +73,7 @@ export function run() {
it('should set android via querystring', () => {
let platform = new Platform();
platform.setUrl('/?ionicplatform=android');
platform.load();
platform.load(null);
expect(platform.is('android')).toEqual(true);
expect(platform.is('windows')).toEqual(false);
@@ -131,7 +84,7 @@ export function run() {
let platform = new Platform();
platform.setUrl('/?ionicplatform=android');
platform.setUserAgent(IPHONE_UA);
platform.load();
platform.load(null);
expect(platform.is('android')).toEqual(true);
expect(platform.is('windows')).toEqual(false);
@@ -141,7 +94,7 @@ export function run() {
it('should set windows via user agent', () => {
let platform = new Platform();
platform.setUserAgent(WINDOWS_UA);
platform.load();
platform.load(null);
expect(platform.is('mobile')).toEqual(true);
expect(platform.is('windows')).toEqual(true);
@@ -152,7 +105,7 @@ export function run() {
it('should set windows8 via user agent', () => {
let platform = new Platform();
platform.setUserAgent(WINDOWS8_UA);
platform.load();
platform.load(null);
expect(platform.is('mobile')).toEqual(true);
expect(platform.is('windows')).toEqual(true);
@@ -163,7 +116,7 @@ export function run() {
it('should set windows7 via user agent', () => {
let platform = new Platform();
platform.setUserAgent(WINDOWS7_UA);
platform.load();
platform.load(null);
expect(platform.is('mobile')).toEqual(true);
expect(platform.is('windows')).toEqual(true);
@@ -174,7 +127,7 @@ export function run() {
it('should set android via user agent', () => {
let platform = new Platform();
platform.setUserAgent(ANDROID_UA);
platform.load();
platform.load(null);
expect(platform.is('mobile')).toEqual(true);
expect(platform.is('windows')).toEqual(false);
@@ -185,7 +138,7 @@ export function run() {
it('should set iphone via user agent', () => {
let platform = new Platform();
platform.setUserAgent(IPHONE_UA);
platform.load();
platform.load(null);
expect(platform.is('mobile')).toEqual(true);
expect(platform.is('windows')).toEqual(false);
@@ -198,7 +151,7 @@ export function run() {
it('should set ipad via user agent', () => {
let platform = new Platform();
platform.setUserAgent(IPAD_UA);
platform.load();
platform.load(null);
expect(platform.is('mobile')).toEqual(true);
expect(platform.is('windows')).toEqual(false);