Refactor the application module to prevent cyclic dependencies, which were:

* common requires application
* application requires common
* application requires application
Make the exports typed using @hdeshev way.
This commit is contained in:
atanasovg
2016-01-14 13:43:35 +02:00
parent d916d76b30
commit 935939bb51
4 changed files with 110 additions and 83 deletions

View File

@ -2,6 +2,7 @@
import definition = require("application"); import definition = require("application");
import observable = require("data/observable"); import observable = require("data/observable");
import frame = require("ui/frame"); import frame = require("ui/frame");
import cssSelector = require("ui/styling/css-selector");
import * as fileSystemModule from "file-system"; import * as fileSystemModule from "file-system";
import * as styleScopeModule from "ui/styling/style-scope"; import * as styleScopeModule from "ui/styling/style-scope";
@ -21,6 +22,8 @@ export var mainEntry: frame.NavigationEntry;
export var cssFile: string = "app.css" export var cssFile: string = "app.css"
export var cssSelectorsCache: Array<cssSelector.CssSelector> = undefined;
export var resources: any = {}; export var resources: any = {};
export var onUncaughtError: (error: definition.NativeScriptError) => void = undefined; export var onUncaughtError: (error: definition.NativeScriptError) => void = undefined;
@ -39,18 +42,24 @@ export var android = undefined;
export var ios = undefined; export var ios = undefined;
export function loadCss() { export function loadCss(cssFile?: string): Array<cssSelector.CssSelector> {
if (definition.cssFile) { if (!cssFile) {
return undefined;
}
var result: Array<cssSelector.CssSelector>;
var fs: typeof fileSystemModule = require("file-system"); var fs: typeof fileSystemModule = require("file-system");
var styleScope: typeof styleScopeModule = require("ui/styling/style-scope"); var styleScope: typeof styleScopeModule = require("ui/styling/style-scope");
var cssFileName = fs.path.join(fs.knownFolders.currentApp().path, definition.cssFile); var cssFileName = fs.path.join(fs.knownFolders.currentApp().path, cssFile);
if (fs.File.exists(cssFileName)) { if (fs.File.exists(cssFileName)) {
var file = fs.File.fromPath(cssFileName); var file = fs.File.fromPath(cssFileName);
var applicationCss = file.readTextSync(); var applicationCss = file.readTextSync();
if (applicationCss) { if (applicationCss) {
definition.cssSelectorsCache = styleScope.StyleScope.createSelectorsFromCss(applicationCss, cssFileName); result = styleScope.StyleScope.createSelectorsFromCss(applicationCss, cssFileName);
}
} }
} }
return result;
} }

View File

@ -1,17 +1,17 @@
import appModule = require("./application-common"); import appModule = require("./application-common");
import dts = require("application"); import definition = require("application");
import frame = require("ui/frame"); import frame = require("ui/frame");
import observable = require("data/observable"); import observable = require("data/observable");
import * as typesModule from "utils/types"; import * as typesModule from "utils/types";
import * as fileResolverModule from "file-system/file-name-resolver"; import * as fileResolverModule from "file-system/file-name-resolver";
global.moduleMerge(appModule, exports); global.moduleMerge(appModule, exports);
var typedExports: typeof definition = exports;
// We are using the exports object for the common events since we merge the appModule with this module's exports, which is what users will receive when require("application") is called; // We are using the exports object for the common events since we merge the appModule with this module's exports, which is what users will receive when require("application") is called;
// TODO: This is kind of hacky and is "pure JS in TypeScript" // TODO: This is kind of hacky and is "pure JS in TypeScript"
var initEvents = function () { var initEvents = function () {
var androidApp: dts.AndroidApplication = exports.android;
// TODO: Verify whether the logic for triggerring application-wide events based on Activity callbacks is working properly // TODO: Verify whether the logic for triggerring application-wide events based on Activity callbacks is working properly
var lifecycleCallbacks = new android.app.Application.ActivityLifecycleCallbacks({ var lifecycleCallbacks = new android.app.Application.ActivityLifecycleCallbacks({
onActivityCreated: function (activity: any, bundle: any) { onActivityCreated: function (activity: any, bundle: any) {
@ -22,7 +22,7 @@ var initEvents = function () {
if (!androidApp.startActivity) { if (!androidApp.startActivity) {
androidApp.startActivity = activity; androidApp.startActivity = activity;
androidApp.notify(<dts.AndroidActivityBundleEventData>{ eventName: "activityCreated", object: androidApp, activity: activity, bundle: bundle }); androidApp.notify(<definition.AndroidActivityBundleEventData>{ eventName: "activityCreated", object: androidApp, activity: activity, bundle: bundle });
if (androidApp.onActivityCreated) { if (androidApp.onActivityCreated) {
androidApp.onActivityCreated(activity, bundle); androidApp.onActivityCreated(activity, bundle);
@ -47,16 +47,16 @@ var initEvents = function () {
} }
if (activity === androidApp.startActivity) { if (activity === androidApp.startActivity) {
if (exports.onExit) { if (typedExports.onExit) {
exports.onExit(); typedExports.onExit();
} }
exports.notify(<dts.ApplicationEventData>{ eventName: dts.exitEvent, object: androidApp, android: activity }); typedExports.notify(<definition.ApplicationEventData>{ eventName: typedExports.exitEvent, object: androidApp, android: activity });
androidApp.startActivity = undefined; androidApp.startActivity = undefined;
} }
androidApp.notify(<dts.AndroidActivityEventData>{ eventName: "activityDestroyed", object: androidApp, activity: activity }); androidApp.notify(<definition.AndroidActivityEventData>{ eventName: "activityDestroyed", object: androidApp, activity: activity });
if (androidApp.onActivityDestroyed) { if (androidApp.onActivityDestroyed) {
androidApp.onActivityDestroyed(activity); androidApp.onActivityDestroyed(activity);
@ -71,17 +71,17 @@ var initEvents = function () {
return; return;
} }
(<any>androidApp).paused = true; androidApp.paused = true;
if (activity === androidApp.foregroundActivity) { if (activity === androidApp.foregroundActivity) {
if (exports.onSuspend) { if (typedExports.onSuspend) {
exports.onSuspend(); typedExports.onSuspend();
} }
exports.notify(<dts.ApplicationEventData>{ eventName: dts.suspendEvent, object: androidApp, android: activity }); typedExports.notify(<definition.ApplicationEventData>{ eventName: typedExports.suspendEvent, object: androidApp, android: activity });
} }
androidApp.notify(<dts.AndroidActivityEventData>{ eventName: "activityPaused", object: androidApp, activity: activity }); androidApp.notify(<definition.AndroidActivityEventData>{ eventName: "activityPaused", object: androidApp, activity: activity });
if (androidApp.onActivityPaused) { if (androidApp.onActivityPaused) {
androidApp.onActivityPaused(activity); androidApp.onActivityPaused(activity);
@ -93,17 +93,17 @@ var initEvents = function () {
return; return;
} }
(<any>androidApp).paused = false; androidApp.paused = false;
if (activity === androidApp.foregroundActivity) { if (activity === androidApp.foregroundActivity) {
if (exports.onResume) { if (typedExports.onResume) {
exports.onResume(); typedExports.onResume();
} }
exports.notify(<dts.ApplicationEventData>{ eventName: dts.resumeEvent, object: androidApp, android: activity }); typedExports.notify(<definition.ApplicationEventData>{ eventName: typedExports.resumeEvent, object: androidApp, android: activity });
} }
androidApp.notify(<dts.AndroidActivityEventData>{ eventName: "activityResumed", object: androidApp, activity: activity }); androidApp.notify(<definition.AndroidActivityEventData>{ eventName: "activityResumed", object: androidApp, activity: activity });
if (androidApp.onActivityResumed) { if (androidApp.onActivityResumed) {
androidApp.onActivityResumed(activity); androidApp.onActivityResumed(activity);
@ -115,7 +115,7 @@ var initEvents = function () {
return; return;
} }
androidApp.notify(<dts.AndroidActivityBundleEventData>{ eventName: "saveActivityState", object: androidApp, activity: activity, bundle: bundle }); androidApp.notify(<definition.AndroidActivityBundleEventData>{ eventName: "saveActivityState", object: androidApp, activity: activity, bundle: bundle });
if (androidApp.onSaveActivityState) { if (androidApp.onSaveActivityState) {
androidApp.onSaveActivityState(activity, bundle); androidApp.onSaveActivityState(activity, bundle);
@ -129,7 +129,7 @@ var initEvents = function () {
androidApp.foregroundActivity = activity; androidApp.foregroundActivity = activity;
androidApp.notify(<dts.AndroidActivityEventData>{ eventName: "activityStarted", object: androidApp, activity: activity }); androidApp.notify(<definition.AndroidActivityEventData>{ eventName: "activityStarted", object: androidApp, activity: activity });
if (androidApp.onActivityStarted) { if (androidApp.onActivityStarted) {
androidApp.onActivityStarted(activity); androidApp.onActivityStarted(activity);
@ -141,7 +141,7 @@ var initEvents = function () {
return; return;
} }
androidApp.notify(<dts.AndroidActivityEventData>{ eventName: "activityStopped", object: androidApp, activity: activity }); androidApp.notify(<definition.AndroidActivityEventData>{ eventName: "activityStopped", object: androidApp, activity: activity });
if (androidApp.onActivityStopped) { if (androidApp.onActivityStopped) {
androidApp.onActivityStopped(activity); androidApp.onActivityStopped(activity);
@ -155,15 +155,15 @@ var initEvents = function () {
app.init({ app.init({
getActivity: function (activity: android.app.Activity) { getActivity: function (activity: android.app.Activity) {
var intent = activity.getIntent() var intent = activity.getIntent()
return exports.android.getActivity(intent); return androidApp.getActivity(intent);
}, },
onCreate: function () { onCreate: function () {
exports.android.init(this); androidApp.init(this);
} }
}); });
export class AndroidApplication extends observable.Observable implements dts.AndroidApplication { export class AndroidApplication extends observable.Observable implements definition.AndroidApplication {
public static activityCreatedEvent = "activityCreated"; public static activityCreatedEvent = "activityCreated";
public static activityDestroyedEvent = "activityDestroyed"; public static activityDestroyedEvent = "activityDestroyed";
public static activityStartedEvent = "activityStarted"; public static activityStartedEvent = "activityStarted";
@ -174,6 +174,7 @@ export class AndroidApplication extends observable.Observable implements dts.And
public static activityResultEvent = "activityResult"; public static activityResultEvent = "activityResult";
public static activityBackPressedEvent = "activityBackPressed"; public static activityBackPressedEvent = "activityBackPressed";
public paused: boolean;
public nativeApp: android.app.Application; public nativeApp: android.app.Application;
public context: android.content.Context; public context: android.content.Context;
public currentContext: android.content.Context; public currentContext: android.content.Context;
@ -203,11 +204,11 @@ export class AndroidApplication extends observable.Observable implements dts.And
public getActivity(intent: android.content.Intent): Object { public getActivity(intent: android.content.Intent): Object {
if (intent && intent.getAction() === android.content.Intent.ACTION_MAIN) { if (intent && intent.getAction() === android.content.Intent.ACTION_MAIN) {
// application's main activity // application's main activity
if (exports.onLaunch) { if (typedExports.onLaunch) {
exports.onLaunch(intent); typedExports.onLaunch(intent);
} }
exports.notify({ eventName: dts.launchEvent, object: this, android: intent }); typedExports.notify({ eventName: typedExports.launchEvent, object: this, android: intent });
setupOrientationListener(this); setupOrientationListener(this);
@ -221,9 +222,9 @@ export class AndroidApplication extends observable.Observable implements dts.And
var topFrame = frame.topmost(); var topFrame = frame.topmost();
if (!topFrame) { if (!topFrame) {
// try to navigate to the mainEntry/Module (if specified) // try to navigate to the mainEntry/Module (if specified)
var navParam = dts.mainEntry; var navParam = typedExports.mainEntry;
if (!navParam) { if (!navParam) {
navParam = dts.mainModule; navParam = typedExports.mainModule;
} }
if (navParam) { if (navParam) {
@ -304,25 +305,31 @@ class BroadcastReceiver extends android.content.BroadcastReceiver {
} }
} }
global.__onUncaughtError = function (error: Error) { global.__onUncaughtError = function (error: definition.NativeScriptError) {
var types: typeof typesModule = require("utils/types"); var types: typeof typesModule = require("utils/types");
// TODO: Obsolete this // TODO: Obsolete this
if (types.isFunction(exports.onUncaughtError)) { if (types.isFunction(typedExports.onUncaughtError)) {
exports.onUncaughtError(error); typedExports.onUncaughtError(error);
} }
exports.notify({ eventName: dts.uncaughtErrorEvent, object: appModule.android, android: error }); typedExports.notify({ eventName: typedExports.uncaughtErrorEvent, object: appModule.android, android: error });
} }
exports.start = function (entry?: frame.NavigationEntry) { function loadCss() {
typedExports.cssSelectorsCache = typedExports.loadCss(typedExports.cssFile);
}
export function start(entry?: frame.NavigationEntry) {
if (entry) { if (entry) {
dts.mainEntry = entry; typedExports.mainEntry = entry;
} }
dts.loadCss(); loadCss();
} }
exports.android = new AndroidApplication(); var androidApp = new AndroidApplication();
// use the exports object instead of 'export var' due to global namespace collision
typedExports.android = androidApp;
var currentOrientation: number; var currentOrientation: number;
function setupOrientationListener(androidApp: AndroidApplication) { function setupOrientationListener(androidApp: AndroidApplication) {
@ -350,17 +357,17 @@ function onConfigurationChanged(context: android.content.Context, intent: androi
break; break;
} }
exports.notify(<dts.OrientationChangedEventData>{ typedExports.notify(<definition.OrientationChangedEventData>{
eventName: dts.orientationChangedEvent, eventName: typedExports.orientationChangedEvent,
android: context, android: context,
newValue: newValue, newValue: newValue,
object: exports.android, object: typedExports.android,
}); });
} }
} }
global.__onLiveSync = function () { global.__onLiveSync = function () {
if (exports.android && exports.android.paused) { if (typedExports.android && typedExports.android.paused) {
return; return;
} }
@ -370,7 +377,7 @@ global.__onLiveSync = function () {
fileResolver.clearCache(); fileResolver.clearCache();
// Reload app.css in case it was changed. // Reload app.css in case it was changed.
appModule.loadCss(); loadCss();
// Reload current page. // Reload current page.
frame.reloadPage(); frame.reloadPage();

View File

@ -118,8 +118,9 @@ declare module "application" {
/** /**
* Loads css file and parses to a css syntax tree. * Loads css file and parses to a css syntax tree.
* @param cssFile Optional parameter to point to an arbitrary css file. If not specified, the cssFile property is used.
*/ */
export function loadCss(): void; export function loadCss(cssFile?: string): Array<cssSelector.CssSelector>;
/** /**
* Call this method to start the application. Important: All code after this method call will not be executed! * Call this method to start the application. Important: All code after this method call will not be executed!
@ -330,6 +331,11 @@ declare module "application" {
*/ */
packageName: string; packageName: string;
/**
* True if the application is not running (suspended), false otherwise.
*/
paused: boolean;
/** /**
* This method is called by the JavaScript Bridge when navigation to a new activity is triggered. * This method is called by the JavaScript Bridge when navigation to a new activity is triggered.
* @param intent - Native (android) intent used to create the activity. * @param intent - Native (android) intent used to create the activity.

View File

@ -7,6 +7,7 @@ import * as fileResolverModule from "file-system/file-name-resolver";
import * as enumsModule from "ui/enums"; import * as enumsModule from "ui/enums";
global.moduleMerge(common, exports); global.moduleMerge(common, exports);
var typedExports: typeof definition = exports;
class Responder extends UIResponder { class Responder extends UIResponder {
// //
@ -109,12 +110,12 @@ class IOSApplication implements definition.iOSApplication {
this._window = <Window>Window.alloc().initWithFrame(UIScreen.mainScreen().bounds); this._window = <Window>Window.alloc().initWithFrame(UIScreen.mainScreen().bounds);
this._window.backgroundColor = UIColor.whiteColor(); this._window.backgroundColor = UIColor.whiteColor();
if (exports.onLaunch) { if (typedExports.onLaunch) {
exports.onLaunch(); typedExports.onLaunch(undefined);
} }
exports.notify({ typedExports.notify({
eventName: exports.launchEvent, eventName: typedExports.launchEvent,
object: this, object: this,
ios: notification.userInfo && notification.userInfo.objectForKey("UIApplicationLaunchOptionsLocalNotificationKey") || null ios: notification.userInfo && notification.userInfo.objectForKey("UIApplicationLaunchOptionsLocalNotificationKey") || null
}); });
@ -122,9 +123,9 @@ class IOSApplication implements definition.iOSApplication {
var topFrame = frame.topmost(); var topFrame = frame.topmost();
if (!topFrame) { if (!topFrame) {
// try to navigate to the mainEntry/Module (if specified) // try to navigate to the mainEntry/Module (if specified)
var navParam = exports.mainEntry; var navParam = typedExports.mainEntry;
if (!navParam) { if (!navParam) {
navParam = exports.mainModule; navParam = typedExports.mainModule;
} }
if (navParam) { if (navParam) {
@ -144,35 +145,35 @@ class IOSApplication implements definition.iOSApplication {
} }
private didBecomeActive(notification: NSNotification) { private didBecomeActive(notification: NSNotification) {
if (exports.onResume) { if (typedExports.onResume) {
exports.onResume(); typedExports.onResume();
} }
exports.notify({ eventName: exports.resumeEvent, object: this, ios: UIApplication.sharedApplication() }); typedExports.notify({ eventName: typedExports.resumeEvent, object: this, ios: UIApplication.sharedApplication() });
} }
private didEnterBackground(notification: NSNotification) { private didEnterBackground(notification: NSNotification) {
if (exports.onSuspend) { if (typedExports.onSuspend) {
exports.onSuspend(); typedExports.onSuspend();
} }
exports.notify({ eventName: exports.suspendEvent, object: this, ios: UIApplication.sharedApplication() }); typedExports.notify({ eventName: typedExports.suspendEvent, object: this, ios: UIApplication.sharedApplication() });
} }
private willTerminate(notification: NSNotification) { private willTerminate(notification: NSNotification) {
if (exports.onExit) { if (typedExports.onExit) {
exports.onExit(); typedExports.onExit();
} }
exports.notify({ eventName: exports.exitEvent, object: this, ios: UIApplication.sharedApplication() }); typedExports.notify({ eventName: typedExports.exitEvent, object: this, ios: UIApplication.sharedApplication() });
} }
private didReceiveMemoryWarning(notification: NSNotification) { private didReceiveMemoryWarning(notification: NSNotification) {
if (exports.onLowMemory) { if (typedExports.onLowMemory) {
exports.onLowMemory(); typedExports.onLowMemory();
} }
exports.notify({ eventName: exports.lowMemoryEvent, object: this, android: undefined, ios: UIApplication.sharedApplication() }); typedExports.notify({ eventName: typedExports.lowMemoryEvent, object: this, android: undefined, ios: UIApplication.sharedApplication() });
} }
private orientationDidChange(notification: NSNotification) { private orientationDidChange(notification: NSNotification) {
@ -198,8 +199,8 @@ class IOSApplication implements definition.iOSApplication {
break; break;
} }
exports.notify(<definition.OrientationChangedEventData>{ typedExports.notify(<definition.OrientationChangedEventData>{
eventName: exports.orientationChangedEvent, eventName: typedExports.orientationChangedEvent,
ios: this, ios: this,
newValue: newValue, newValue: newValue,
object: this object: this
@ -210,28 +211,32 @@ class IOSApplication implements definition.iOSApplication {
} }
var iosApp = new IOSApplication(); var iosApp = new IOSApplication();
exports.ios = iosApp; typedExports.ios = iosApp;
global.__onUncaughtError = function (error: Error) { global.__onUncaughtError = function (error: definition.NativeScriptError) {
var types: typeof typesModule = require("utils/types"); var types: typeof typesModule = require("utils/types");
// TODO: This should be obsoleted // TODO: This should be obsoleted
if (types.isFunction(exports.onUncaughtError)) { if (types.isFunction(typedExports.onUncaughtError)) {
exports.onUncaughtError(error); typedExports.onUncaughtError(error);
} }
exports.notify({ eventName: exports.uncaughtErrorEvent, object: <any>exports.ios, ios: error }); typedExports.notify({ eventName: typedExports.uncaughtErrorEvent, object: typedExports.ios, ios: error });
}
function loadCss() {
typedExports.cssSelectorsCache = typedExports.loadCss(typedExports.cssFile);
} }
var started: boolean = false; var started: boolean = false;
exports.start = function (entry?: frame.NavigationEntry) { typedExports.start = function (entry?: frame.NavigationEntry) {
if (!started) { if (!started) {
if (entry) { if (entry) {
exports.mainEntry = entry; exports.mainEntry = entry;
} }
started = true; started = true;
exports.loadCss(); loadCss();
UIApplicationMain(0, null, null, exports.ios && exports.ios.delegate ? NSStringFromClass(exports.ios.delegate) : NSStringFromClass(Responder)); UIApplicationMain(0, null, null, typedExports.ios && typedExports.ios.delegate ? NSStringFromClass(typedExports.ios.delegate) : NSStringFromClass(Responder));
} else { } else {
throw new Error("iOS Application already started!"); throw new Error("iOS Application already started!");
} }
@ -248,7 +253,7 @@ global.__onLiveSync = function () {
fileResolver.clearCache(); fileResolver.clearCache();
// Reload app.css in case it was changed. // Reload app.css in case it was changed.
exports.loadCss(); loadCss();
// Reload current page. // Reload current page.
frame.reloadPage(); frame.reloadPage();