@breakside/jskit 2022.3.1 → 2022.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (59) hide show
  1. package/Frameworks/DOM.jsframework/Info.json +2 -2
  2. package/Frameworks/DOM.jsframework/io.breakside.JSKit.DOM-bundle.js +2 -2
  3. package/Frameworks/FontKit.jsframework/Info.json +2 -2
  4. package/Frameworks/FontKit.jsframework/io.breakside.JSKit.FontKit-bundle.js +2 -2
  5. package/Frameworks/Foundation.jsframework/Info.json +2 -2
  6. package/Frameworks/Foundation.jsframework/JS/JSUserDefaults.js +28 -7
  7. package/Frameworks/Foundation.jsframework/io.breakside.JSKit.Foundation-bundle.js +2 -2
  8. package/Frameworks/SecurityKit.jsframework/Info.json +2 -2
  9. package/Frameworks/SecurityKit.jsframework/io.breakside.JSKit.SecurityKit-bundle.js +2 -2
  10. package/Info.json +2 -2
  11. package/Node/Docs/DocComponent.js +11 -0
  12. package/Node/io.breakside.jskit-bundle.js +2 -2
  13. package/Root/Frameworks/APIKit/Info.yaml +1 -1
  14. package/Root/Frameworks/APIKitTesting/Info.yaml +1 -1
  15. package/Root/Frameworks/AuthKit/Info.yaml +1 -1
  16. package/Root/Frameworks/CSSOM/Info.yaml +1 -1
  17. package/Root/Frameworks/ChartKit/Info.yaml +1 -1
  18. package/Root/Frameworks/ConferenceKit/Info.yaml +1 -1
  19. package/Root/Frameworks/DBKit/DBMongoStore.js +1 -0
  20. package/Root/Frameworks/DBKit/DBRedisStore.js +136 -117
  21. package/Root/Frameworks/DBKit/Info.yaml +1 -1
  22. package/Root/Frameworks/DOM/Info.yaml +1 -1
  23. package/Root/Frameworks/Dispatch/Info.yaml +1 -1
  24. package/Root/Frameworks/FontKit/Info.yaml +1 -1
  25. package/Root/Frameworks/Foundation/Info.yaml +1 -1
  26. package/Root/Frameworks/Foundation/JSUserDefaults.js +28 -7
  27. package/Root/Frameworks/ImageKit/IKMatrix.js +1 -1
  28. package/Root/Frameworks/ImageKit/Info.yaml +1 -1
  29. package/Root/Frameworks/MediaKit/Info.yaml +1 -1
  30. package/Root/Frameworks/MediaKitUI/Info.yaml +1 -1
  31. package/Root/Frameworks/NotificationKit/Info.yaml +1 -1
  32. package/Root/Frameworks/PDFKit/Info.yaml +1 -1
  33. package/Root/Frameworks/QRKit/Info.yaml +1 -1
  34. package/Root/Frameworks/SearchKit/Info.yaml +1 -1
  35. package/Root/Frameworks/SecurityKit/Info.yaml +1 -1
  36. package/Root/Frameworks/ServerKit/Info.yaml +1 -1
  37. package/Root/Frameworks/ServerKit/SKAMQPJobQueue.js +1 -1
  38. package/Root/Frameworks/ServerKit/SKApplication+Node.js +5 -3
  39. package/Root/Frameworks/ServerKit/SKJobQueue.js +1 -1
  40. package/Root/Frameworks/ServerKit/SKRedisJobQueue.js +7 -3
  41. package/Root/Frameworks/ServerKitTesting/Info.yaml +1 -1
  42. package/Root/Frameworks/TestKit/Info.yaml +1 -1
  43. package/Root/Frameworks/TestKit/TKMock.js +1 -1
  44. package/Root/Frameworks/UIKit/Info.yaml +1 -1
  45. package/Root/Frameworks/UIKit/UIBrowserViewController.js +2 -6
  46. package/Root/Frameworks/UIKit/UIHTMLApplication.js +21 -5
  47. package/Root/Frameworks/UIKit/UINavigationBar.js +7 -1
  48. package/Root/Frameworks/UIKit/UINavigationController.js +228 -112
  49. package/Root/Frameworks/UIKit/UINavigationItem.js +4 -0
  50. package/Root/Frameworks/UIKit/UIPopupButton.js +8 -0
  51. package/Root/Frameworks/UIKit/UISplitViewController.js +185 -77
  52. package/Root/Frameworks/UIKit/UITabViewController.js +4 -10
  53. package/Root/Frameworks/UIKit/UIViewController.js +133 -50
  54. package/Root/Frameworks/UIKit/UIWindow.js +4 -15
  55. package/Root/Frameworks/UIKitTesting/Info.yaml +1 -1
  56. package/Root/Frameworks/UIKitTesting/UIMockApplication.js +6 -0
  57. package/Root/Templates/http/${PROJECT_NAME}/Dockerfile +1 -1
  58. package/Root/Templates/node/${PROJECT_NAME}/Dockerfile +4 -2
  59. package/package.json +1 -1
@@ -56,7 +56,7 @@ JSClass("UISplitViewController", UIViewController, {
56
56
  viewDidLoad: function(){
57
57
  UISplitViewController.$super.viewDidLoad.call(this);
58
58
  this._view.delegate = this;
59
- if (this._mainViewController !== null && this._view.mainView === null){
59
+ if (this._mainViewController !== null && this._view.mainView === null && !this._view.mainHidden){
60
60
  this._view.mainView = this._mainViewController.view;
61
61
  }
62
62
  if (this._leadingViewController !== null && this._view.leadingView === null){
@@ -72,7 +72,7 @@ JSClass("UISplitViewController", UIViewController, {
72
72
  if (this._leadingViewController !== null && this.leadingViewOpen){
73
73
  this._leadingViewController.viewWillAppear(animated);
74
74
  }
75
- if (this._mainViewController !== null){
75
+ if (this._mainViewController !== null && !this._view.mainHidden){
76
76
  this._mainViewController.viewWillAppear(animated);
77
77
  }
78
78
  if (this._trailingViewController !== null && this.trailingViewOpen){
@@ -85,7 +85,7 @@ JSClass("UISplitViewController", UIViewController, {
85
85
  if (this._leadingViewController !== null && this.leadingViewOpen){
86
86
  this._leadingViewController.viewDidAppear(animated);
87
87
  }
88
- if (this._mainViewController !== null){
88
+ if (this._mainViewController !== null && !this._view.mainHidden){
89
89
  this._mainViewController.viewDidAppear(animated);
90
90
  }
91
91
  if (this._trailingViewController !== null && this.trailingViewOpen){
@@ -98,7 +98,7 @@ JSClass("UISplitViewController", UIViewController, {
98
98
  if (this._leadingViewController !== null && this.leadingViewOpen){
99
99
  this._leadingViewController.viewWillDisappear(animated);
100
100
  }
101
- if (this._mainViewController !== null){
101
+ if (this._mainViewController !== null && !this._view.mainHidden){
102
102
  this._mainViewController.viewWillDisappear(animated);
103
103
  }
104
104
  if (this._trailingViewController !== null && this.trailingViewOpen){
@@ -111,7 +111,7 @@ JSClass("UISplitViewController", UIViewController, {
111
111
  if (this._leadingViewController !== null && this.leadingViewOpen){
112
112
  this._leadingViewController.viewDidDisappear(animated);
113
113
  }
114
- if (this._mainViewController !== null){
114
+ if (this._mainViewController !== null && !this._view.mainHidden){
115
115
  this._mainViewController.viewDidDisappear(animated);
116
116
  }
117
117
  if (this._trailingViewController !== null && this.trailingViewOpen){
@@ -138,8 +138,14 @@ JSClass("UISplitViewController", UIViewController, {
138
138
  setMainViewController: function(mainViewController){
139
139
  if (this.isViewLoaded && this.view.mainHidden){
140
140
  this._setMainViewControllerCollapsed(mainViewController);
141
- return;
141
+ }else{
142
+ this._setMainViewControllerFull(mainViewController);
142
143
  }
144
+ },
145
+
146
+ _mainNavigationRootViewController: null,
147
+
148
+ _setMainViewControllerFull: function(mainViewController){
143
149
  this.replaceChildViewController(this._mainViewController, mainViewController);
144
150
  this._mainViewController = mainViewController;
145
151
  if (this.isViewLoaded){
@@ -149,18 +155,21 @@ JSClass("UISplitViewController", UIViewController, {
149
155
 
150
156
  _setMainViewControllerCollapsed: function(mainViewController){
151
157
  if (this._leadingViewController.isKindOfClass(UINavigationController)){
152
- this._pushMainViewControllerOnNavigationController(mainViewController, this._leadingViewController);
158
+ this._setMainViewControllerOnLeadingNavigationController(mainViewController, this._leadingViewController);
153
159
  }else{
154
- this._presentMainViewControllerOnViewController(mainViewController, this._leadingViewController);
160
+ this._setMainViewControllerOnLeadingViewController(mainViewController, this._leadingViewController);
155
161
  }
156
162
  },
157
163
 
158
- _pushMainViewControllerOnNavigationController: function(mainViewController, navigationController){
159
- var i = navigationController.viewControllers.length - 1;
160
- // 1. Pop off all view controllers up to and including our current mainViewController
164
+ _setMainViewControllerOnLeadingNavigationController: function(mainViewController, navigationController){
165
+ // 1. Determine if we need to pop off any view controllers to remove the current mainViewController
166
+ var i;
167
+ var viewControllers = null;
161
168
  if (this._mainViewController !== null){
162
- while (i >= 1 && navigationController.viewControllers[i] !== this._mainViewController){
163
- --i;
169
+ if (this._mainViewController instanceof UINavigationController){
170
+ i = navigationController.viewControllers.indexOf(this._mainNavigationRootViewController);
171
+ }else{
172
+ i = navigationController.viewControllers.indexOf(this._mainViewController);
164
173
  }
165
174
  if (i > 0){
166
175
  if (mainViewController === this._mainViewController){
@@ -172,17 +181,54 @@ JSClass("UISplitViewController", UIViewController, {
172
181
  // is not part of the navigation stack
173
182
  return;
174
183
  }
175
- navigationController.popToViewController(navigationController.viewControllers[i - 1], false);
184
+ viewControllers = navigationController.viewControllers.slice(0, i);
176
185
  }
177
186
  }
178
- // 2. Push the new new mainViewController, animating if we're not currently changing traits
187
+ // 2. Update the nav controller
179
188
  if (mainViewController !== null){
180
- navigationController.pushViewController(mainViewController, !this.isChangingTraits);
189
+ if (mainViewController instanceof UINavigationController){
190
+ // When the new main view controller is a nav controller itself,
191
+ // move it's view controller to the leading nav controller
192
+ var mainNavigationController = mainViewController;
193
+ this._mainNavigationRootViewController = mainNavigationController.viewControllers[0];
194
+ if (viewControllers === null && mainNavigationController.viewControllers.length === 1){
195
+ // In a typical case, the main nav controller has just one view controller,
196
+ // which we'll move to the leading nav controller with a push animation
197
+ mainNavigationController.setViewControllers([UIViewController.init()]);
198
+ navigationController.pushViewController(this._mainNavigationRootViewController, true);
199
+ }else{
200
+ // If the main nav controller has more than one view controller, or we're replacing some
201
+ // there's not a meaningful animation we can show, so we'll just
202
+ // update the leading nav without animation
203
+ if (viewControllers === null){
204
+ viewControllers = JSCopy(navigationController.viewControllers);
205
+ }
206
+ var l;
207
+ for (i = 0, l = mainNavigationController.viewControllers.length; i < l; ++i){
208
+ viewControllers.push(mainNavigationController.viewControllers[i]);
209
+ }
210
+ mainNavigationController.viewControllers = [UIViewController.init()];
211
+ navigationController.viewControllers = viewControllers;
212
+ }
213
+ }else{
214
+ // When the new main view controller is NOT a nav controller,
215
+ // we'll just push it unless we have other updates to do
216
+ if (viewControllers === null){
217
+ navigationController.pushViewController(mainViewController, true);
218
+ }else{
219
+ viewControllers.push(mainViewController);
220
+ navigationController.viewControllers = viewControllers;
221
+ }
222
+ }
223
+ }else{
224
+ if (viewControllers !== null){
225
+ navigationController.popToViewController(viewControllers[viewControllers.length - 1], true);
226
+ }
181
227
  }
182
228
  this._mainViewController = mainViewController;
183
229
  },
184
230
 
185
- _presentMainViewControllerOnViewController: function(mainViewController, viewController){
231
+ _setMainViewControllerOnLeadingViewController: function(mainViewController, viewController){
186
232
  // Bail if we're being asked to show the same view controller that's already shown
187
233
  if (mainViewController === this._mainViewController){
188
234
  return;
@@ -200,6 +246,106 @@ JSClass("UISplitViewController", UIViewController, {
200
246
  this._mainViewController = mainViewController;
201
247
  },
202
248
 
249
+ _moveMainViewControllerToLeading: function(){
250
+ if (this._mainViewController === null){
251
+ return;
252
+ }
253
+ if (this._leadingViewController instanceof UINavigationController){
254
+ this._moveMainViewControllerToLeadingNavigationController(this._leadingViewController);
255
+ }else{
256
+ this._moveMainViewControllerToLeadingViewController(this._leadingViewController);
257
+ }
258
+ },
259
+
260
+ _moveMainViewControllerToLeadingNavigationController: function(navigationController){
261
+ var viewControllers = JSCopy(navigationController.viewControllers);
262
+ if (this._mainViewController instanceof UINavigationController){
263
+ var mainNavigationController = this._mainViewController;
264
+ this._mainNavigationRootViewController = mainNavigationController.viewControllers[0];
265
+ var i, l;
266
+ for (i = 0, l = mainNavigationController.viewControllers.length; i < l; ++i){
267
+ viewControllers.push(mainNavigationController.viewControllers[i]);
268
+ }
269
+ mainNavigationController.viewControllers = [UIViewController.init()];
270
+ if (this.isViewVisible){
271
+ mainNavigationController.scheduleDisappearance();
272
+ }
273
+ }else{
274
+ this.view.mainView = null;
275
+ viewControllers.push(this._mainViewController);
276
+ }
277
+ navigationController.viewControllers = viewControllers;
278
+ },
279
+
280
+ _moveMainViewControllerToLeadingViewController: function(viewController){
281
+ this.view.mainView = null;
282
+ viewController.presentViewController(this._mainViewController, false);
283
+ },
284
+
285
+ _moveMainViewControllerFromLeading: function(){
286
+ if (this._mainViewController === null){
287
+ return;
288
+ }
289
+ var initialMainViewController = this._mainViewController;
290
+ if (this._leadingViewController instanceof UINavigationController){
291
+ this._moveMainViewControllerFromLeadingNavigationController(this._leadingViewController);
292
+ }else{
293
+ this._moveMainViewControllerFromLeadingViewController(this._leadingViewController);
294
+ }
295
+ },
296
+
297
+ _moveMainViewControllerFromLeadingNavigationController: function(navigationController){
298
+ if (this._mainViewController instanceof UINavigationController){
299
+ var mainNavigationController = this._mainViewController;
300
+ var i = navigationController.viewControllers.indexOf(this._mainNavigationRootViewController);
301
+ if (i > 0){
302
+ if (mainNavigationController.view.superview === null){
303
+ this.view.mainView = mainNavigationController.view;
304
+ }
305
+ mainNavigationController.scheduleAppearance();
306
+ var viewControllers = navigationController.viewControllers.slice(i);
307
+ navigationController.popToViewController(navigationController.viewControllers[i - 1], false);
308
+ viewControllers[viewControllers.length - 1].scheduleAppearance();
309
+ mainNavigationController.viewControllers = viewControllers;
310
+ }else{
311
+ this._mainViewController = null;
312
+ if (this.isViewLoaded){
313
+ this.view.mainView = null;
314
+ }
315
+ }
316
+ this._mainNavigationRootViewController = null;
317
+ }else{
318
+ navigationController.popToViewController(this._mainViewController, false);
319
+ if (navigationController.topViewController === this._mainViewController){
320
+ navigationController.popViewController(false);
321
+ if (this.isViewLoaded){
322
+ this._mainViewController.scheduleAppearance();
323
+ this.view.mainView = this._mainViewController.view;
324
+ }
325
+ }else{
326
+ this._mainViewController = null;
327
+ if (this.isViewLoaded){
328
+ this.view.mainView = null;
329
+ }
330
+ }
331
+ }
332
+ },
333
+
334
+ _moveMainViewControllerFromLeadingViewController: function(viewController){
335
+ if (viewController.presentedViewController === this._mainViewController){
336
+ viewController.dismiss(false);
337
+ if (this.isViewLoaded){
338
+ this._mainViewController.scheduleAppearance();
339
+ this.view.mainView = this._mainViewController.view;
340
+ }
341
+ }else{
342
+ this._mainViewController = null;
343
+ if (this.isViewLoaded){
344
+ this.view.mainView = null;
345
+ }
346
+ }
347
+ },
348
+
203
349
  getSplitView: function(){
204
350
  return this._view;
205
351
  },
@@ -241,37 +387,24 @@ JSClass("UISplitViewController", UIViewController, {
241
387
  percentRemaining = this._leadingAnimator.percentComplete;
242
388
  this._leadingAnimator = null;
243
389
  }
244
- var willAppear = !this.splitView.leadingViewOpen;
245
- if (willAppear){
246
- this._leadingViewController.viewWillAppear(animated);
247
- }else{
248
- this._leadingViewController.viewWillDisappear(animated);
249
- }
390
+ var opening = !this.splitView.leadingViewOpen;
391
+ this._leadingViewController.beginAppearanceTransition(opening, animated);
250
392
  if (!animated){
251
- this.splitView.leadingViewOpen = !this.splitView.leadingViewOpen;
252
- if (willAppear){
253
- this._leadingViewController.enqueueDidAppear();
254
- }else{
255
- this._leadingViewController.enqueueDidDisappear();
256
- }
393
+ this.splitView.leadingViewOpen = opening;
394
+ this._leadingViewController.endAppearanceTransition();
257
395
  }else{
258
396
  // make sure to apply any pending layouts before doing the animation,
259
397
  // otherwise the pending layouts will get caught up in the animation
260
398
  this.splitView.layoutIfNeeded();
261
399
  var animator = UIViewPropertyAnimator.initWithDuration(0.15 * percentRemaining);
262
- var self = this;
263
- this.splitView.leadingViewOpen = !this.splitView.leadingViewOpen;
400
+ this.splitView.leadingViewOpen = opening;
264
401
  animator.addAnimations(function(){
265
- self.splitView.layoutIfNeeded();
266
- });
402
+ this.splitView.layoutIfNeeded();
403
+ }, this);
267
404
  animator.addCompletion(function(){
268
- self._leadingAnimator = null;
269
- if (willAppear){
270
- self._leadingViewController.viewDidAppear(animated);
271
- }else{
272
- self._leadingViewController.viewDidDisappear(animated);
273
- }
274
- });
405
+ this._leadingAnimator = null;
406
+ this._leadingViewController.endAppearanceTransition();
407
+ }, this);
275
408
  this._leadingAnimator = animator;
276
409
  animator.start();
277
410
  }
@@ -306,37 +439,24 @@ JSClass("UISplitViewController", UIViewController, {
306
439
  percentRemaining = this._trailingAnimator.percentComplete;
307
440
  this._trailingAnimator = null;
308
441
  }
309
- var willAppear = !this.splitView.leadingViewOpen;
310
- if (willAppear){
311
- this._trailingViewController.viewWillAppear(animated);
312
- }else{
313
- this._trailingViewController.viewWillDisappear(animated);
314
- }
442
+ var opening = !this.splitView.trailingViewOpen;
443
+ this._trailingViewController.beginAppearanceTransition(opening, animated);
315
444
  if (!animated){
316
445
  this.splitView.trailingViewOpen = !this.splitView.trailingViewOpen;
317
- if (willAppear){
318
- this._trailingViewController.enqueueDidAppear();
319
- }else{
320
- this._trailingViewController.enqueueDidDisappear();
321
- }
446
+ this._trailingViewController.endAppearanceTransition();
322
447
  }else{
323
448
  // make sure to apply any pending layouts before doing the animation,
324
449
  // otherwise the pending layouts will get caught up in the animation
325
450
  this.splitView.layoutIfNeeded();
326
451
  var animator = UIViewPropertyAnimator.initWithDuration(0.15 * percentRemaining);
327
- var self = this;
328
452
  this.splitView.trailingViewOpen = !this.splitView.trailingViewOpen;
329
453
  animator.addAnimations(function(){
330
- self.splitView.layoutIfNeeded();
331
- });
454
+ this.splitView.layoutIfNeeded();
455
+ }, this);
332
456
  animator.addCompletion(function(){
333
- self._trailingAnimator = null;
334
- if (willAppear){
335
- self._trailingViewController.viewDidAppear(animated);
336
- }else{
337
- self._trailingViewController.viewDidDisappear(animated);
338
- }
339
- });
457
+ this._trailingAnimator = null;
458
+ this._trailingViewController.endAppearanceTransition();
459
+ }, this);
340
460
  this._trailingAnimator = animator;
341
461
  animator.start();
342
462
  }
@@ -345,27 +465,15 @@ JSClass("UISplitViewController", UIViewController, {
345
465
  splitViewDidToggleView: function(splitView, toggledView){
346
466
  },
347
467
 
348
- isChangingTraits: false,
349
-
350
468
  traitCollectionDidChange: function(previous){
351
469
  UISplitViewController.$super.traitCollectionDidChange.call(this);
352
470
  if (this.view.canHideMain && previous.horizontalSizeClass != this.traitCollection.horizontalSizeClass){
353
471
  if (previous.horizontalSizeClass === UIUserInterface.SizeClass.compact){
354
- if (this._leadingViewController.isKindOfClass(UINavigationController)){
355
- if (this._mainViewController === this._leadingViewController.topViewController){
356
- this._leadingViewController.popViewController(false);
357
- }
358
- }else{
359
- if (this._leadingViewController.presentedViewController === this._mainViewController){
360
- this._leadingViewController.dismiss(false);
361
- }
362
- }
472
+ this._moveMainViewControllerFromLeading();
473
+ }else{
474
+ this._moveMainViewControllerToLeading();
475
+
363
476
  }
364
- this.isChangingTraits = true;
365
- var vc = this._mainViewController;
366
- this.mainViewController = null;
367
- this.mainViewController = vc;
368
- this.isChangingTraits = false;
369
477
  }
370
478
  },
371
479
 
@@ -139,10 +139,10 @@ JSClass("UITabViewController", UIViewController, {
139
139
 
140
140
  tabViewWillSelectItemAtIndex: function(tabView, index){
141
141
  if (this.isViewVisible && this._previouslySelectedViewController !== null){
142
- this._previouslySelectedViewController.viewWillDisappear();
142
+ this._previouslySelectedViewController.scheduleDisappearance();
143
143
  }
144
144
  var viewController = null;
145
- if (index < this._viewControllers.length){
145
+ if (index >= 0 && index < this._viewControllers.length){
146
146
  viewController = this._viewControllers[index];
147
147
  }
148
148
  if (viewController !== null){
@@ -150,23 +150,17 @@ JSClass("UITabViewController", UIViewController, {
150
150
  // is selected
151
151
  viewController.tabViewItem.view = viewController.view;
152
152
  if (this.isViewVisible){
153
- viewController.viewWillAppear();
153
+ viewController.scheduleAppearance();
154
154
  }
155
155
  }
156
156
  },
157
157
 
158
158
  tabViewDidSelectItemAtIndex: function(tabView, index){
159
159
  var viewController = null;
160
- if (index < this._viewControllers.length){
160
+ if (index >= 0 && index < this._viewControllers.length){
161
161
  viewController = this._viewControllers[index];
162
162
  }
163
- if (this.isViewVisible && this._previouslySelectedViewController !== null){
164
- this._previouslySelectedViewController.viewDidDisappear();
165
- }
166
163
  this._previouslySelectedViewController = viewController;
167
- if (this.isViewVisible && viewController !== null){
168
- viewController.viewDidAppear();
169
- }
170
164
  }
171
165
 
172
166
  });
@@ -16,7 +16,7 @@
16
16
  // #import Foundation
17
17
  // #import "UIResponder.js"
18
18
  // #import "UITabView.js"
19
- /* global UINavigationItem, UINavigationController */
19
+ /* global UINavigationItem, UINavigationController, UIApplication */
20
20
  'use strict';
21
21
 
22
22
  JSClass("UIViewController", UIResponder, {
@@ -98,33 +98,9 @@ JSClass("UIViewController", UIResponder, {
98
98
  // -------------------------------------------------------------------------
99
99
  // MARK: - View Lifecycle
100
100
 
101
- _needsDidAppear: false,
102
- _needsDidDisappear: false,
103
101
  isViewVisible: JSReadOnlyProperty("_isViewVisible", false),
104
102
 
105
- enqueueDidAppear: function(){
106
- if (!this._needsDidAppear){
107
- this._needsDidAppear = true;
108
- this.view.layer._displayServer.schedule(function(){
109
- if (this._needsDidAppear){
110
- this._needsDidAppear = false;
111
- this.viewDidAppear(false);
112
- }
113
- }, this);
114
- }
115
- },
116
-
117
- enqueueDidDisappear: function(){
118
- if (!this._needsDidDisappear){
119
- this._needsDidDisappear = true;
120
- this.view.layer._displayServer.schedule(function(){
121
- if (this._needsDidDisappear){
122
- this._needsDidDisappear = false;
123
- this.viewDidDisappear(false);
124
- }
125
- }, this);
126
- }
127
- },
103
+ lifecycleState: JSReadOnlyProperty("_lifecycleState", 0),
128
104
 
129
105
  viewDidLoad: function(){
130
106
  },
@@ -132,15 +108,18 @@ JSClass("UIViewController", UIResponder, {
132
108
  viewDidUnload: function(){
133
109
  },
134
110
 
135
- viewWillAppear: function(){
111
+ viewWillAppear: function(animated){
112
+ this._lifecycleState = UIViewController.LifecycleState.appearing;
136
113
  this._loadViewIfNeeded();
137
114
  },
138
115
 
139
- viewDidAppear: function(){
116
+ viewDidAppear: function(animated){
117
+ this._lifecycleState = UIViewController.LifecycleState.appeared;
140
118
  this._isViewVisible = true;
141
119
  },
142
120
 
143
- viewWillDisappear: function(){
121
+ viewWillDisappear: function(animated){
122
+ this._lifecycleState = UIViewController.LifecycleState.disappearing;
144
123
  if (this.isViewLoaded){
145
124
  var window = this.view.window;
146
125
  if (window !== null){
@@ -158,10 +137,103 @@ JSClass("UIViewController", UIResponder, {
158
137
  }
159
138
  },
160
139
 
161
- viewDidDisappear: function(){
140
+ viewDidDisappear: function(animated){
141
+ this._lifecycleState = UIViewController.LifecycleState.disappeared;
162
142
  this._isViewVisible = false;
163
143
  },
164
144
 
145
+ // -------------------------------------------------------------------------
146
+ // MARK: - Lifecycle Control Methods
147
+
148
+ beginAppearance: function(animated){
149
+ this.beginAppearanceTransition(true, animated);
150
+ },
151
+
152
+ endAppearance: function(){
153
+ this.endAppearanceTransition();
154
+ },
155
+
156
+ beginAppearanceWithAnimator: function(animator){
157
+ this.beginAppearance(true);
158
+ animator.addCompletion(function(){
159
+ this.endAppearance();
160
+ }, this);
161
+ },
162
+
163
+ scheduleAppearance: function(){
164
+ this.beginAppearance(false);
165
+ this._scheduleAppearanceTransition();
166
+ },
167
+
168
+ beginDisappearance: function(animated){
169
+ this.beginAppearanceTransition(false, animated);
170
+ },
171
+
172
+ endDisappearance: function(){
173
+ this.endAppearanceTransition();
174
+ },
175
+
176
+ beginDisappearanceWithAnimator: function(animator){
177
+ this.beginDisappearance(true);
178
+ animator.addCompletion(function(){
179
+ this.endDisappearance();
180
+ }, this);
181
+ },
182
+
183
+ scheduleDisappearance: function(){
184
+ this.beginDisappearance(false);
185
+ this._scheduleAppearanceTransition();
186
+ },
187
+
188
+ beginAppearanceTransition: function(isAppearing, animated){
189
+ this._appearanceTransitionAnimated = animated;
190
+ if (isAppearing){
191
+ if (this._lifecycleState === UIViewController.LifecycleState.disappeared || this._lifecycleState === UIViewController.LifecycleState.disappearing){
192
+ this.viewWillAppear(animated);
193
+ }
194
+ }else{
195
+ if (this._lifecycleState === UIViewController.LifecycleState.appeared || this._lifecycleState === UIViewController.LifecycleState.appearing){
196
+ this.viewWillDisappear(animated);
197
+ }
198
+ }
199
+ },
200
+
201
+ endAppearanceTransition: function(){
202
+ if (this._appearanceTransitionAnimated){
203
+ this._endAppearanceTransition(true);
204
+ }else{
205
+ this._scheduleAppearanceTransition();
206
+ }
207
+ },
208
+
209
+ _endAppearanceTransition: function(){
210
+ this._appearanceTransitionScheduled = false;
211
+ if (this._lifecycleState === UIViewController.LifecycleState.appearing){
212
+ this.viewDidAppear(this._appearanceTransitionAnimated);
213
+ }else if (this._lifecycleState === UIViewController.LifecycleState.disappearing){
214
+ this.viewDidDisappear(this._appearanceTransitionAnimated);
215
+ }
216
+ },
217
+
218
+ _appearanceTransitionAnimated: false,
219
+ _appearanceTransitionScheduled: false,
220
+
221
+ _scheduleAppearanceTransition: function(){
222
+ if (this._appearanceTransitionScheduled){
223
+ return;
224
+ }
225
+ this._appearanceTransitionScheduled = true;
226
+ this.schedule(this._endAppearanceTransition);
227
+ },
228
+
229
+ // -------------------------------------------------------------------------
230
+ // MARK: - Display Loop Syncrhonization
231
+
232
+ schedule: function(callback){
233
+ var displayServer = UIApplication.shared.windowServer.displayServer;
234
+ displayServer.schedule(callback, this);
235
+ },
236
+
165
237
  // -------------------------------------------------------------------------
166
238
  // MARK: - Layout
167
239
 
@@ -183,12 +255,21 @@ JSClass("UIViewController", UIResponder, {
183
255
  parentViewController: null,
184
256
 
185
257
  addChildViewController: function(viewController){
258
+ if (viewController.parentViewController === this){
259
+ return;
260
+ }
261
+ if (viewController.parentViewController !== null){
262
+ viewController.removeFromParentViewController();
263
+ }
186
264
  viewController.willMoveToParentViewController(this);
187
265
  viewController.parentViewController = this;
188
266
  viewController.didMoveToParentViewController(this);
189
267
  },
190
268
 
191
269
  removeFromParentViewController: function(){
270
+ if (this.parentViewController === null){
271
+ return;
272
+ }
192
273
  this.willMoveToParentViewController(null);
193
274
  this.parentViewController = null;
194
275
  this.didMoveToParentViewController(null);
@@ -205,29 +286,24 @@ JSClass("UIViewController", UIResponder, {
205
286
  return;
206
287
  }
207
288
  if (this.isViewLoaded && this.isViewVisible){
208
- var animated = animator !== undefined && animator !== null;
209
- if (previousChildViewController !== null){
210
- previousChildViewController.viewWillDisappear(animated);
211
- previousChildViewController.removeFromParentViewController();
212
- }
213
- if (childViewController !== null){
214
- // trigger childViewController.viewDidLoad by accessing .view
215
- var view = childViewController.view;
216
- this.addChildViewController(childViewController);
217
- childViewController.viewWillAppear(animated);
218
- }
219
- var completion = function(){
289
+ if (animator !== undefined && animator !== null){
220
290
  if (previousChildViewController !== null){
221
- previousChildViewController.viewDidDisappear(animated);
291
+ previousChildViewController.beginDisappearanceWithAnimator(animator);
292
+ previousChildViewController.removeFromParentViewController();
222
293
  }
223
294
  if (childViewController !== null){
224
- childViewController.viewDidAppear(animated);
295
+ this.addChildViewController(childViewController);
296
+ childViewController.beginAppearanceWithAnimator(animator);
297
+ }
298
+ }else{
299
+ if (previousChildViewController !== null){
300
+ previousChildViewController.scheduleDisappearance();
301
+ previousChildViewController.removeFromParentViewController();
302
+ }
303
+ if (childViewController !== null){
304
+ this.addChildViewController(childViewController);
305
+ childViewController.scheduleAppearance();
225
306
  }
226
- };
227
- if (animated){
228
- animator.addCompletion(completion, this);
229
- }else{
230
- this.view.layer._displayServer.schedule(completion, this);
231
307
  }
232
308
  }else{
233
309
  if (previousChildViewController !== null){
@@ -335,4 +411,11 @@ JSClass("UIViewController", UIResponder, {
335
411
  }
336
412
 
337
413
 
338
- });
414
+ });
415
+
416
+ UIViewController.LifecycleState = {
417
+ disappeared: 0,
418
+ appearing: 1,
419
+ appeared: 2,
420
+ disappearing: 3
421
+ };