nav-controller-base.js 46KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047
  1. var __extends = (this && this.__extends) || (function () {
  2. var extendStatics = Object.setPrototypeOf ||
  3. ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
  4. function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };
  5. return function (d, b) {
  6. extendStatics(d, b);
  7. function __() { this.constructor = d; }
  8. d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
  9. };
  10. })();
  11. (function (factory) {
  12. if (typeof module === "object" && typeof module.exports === "object") {
  13. var v = factory(require, exports);
  14. if (v !== undefined) module.exports = v;
  15. }
  16. else if (typeof define === "function" && define.amd) {
  17. define(["require", "exports", "@angular/core", "./nav-util", "./nav-util", "../util/util", "./view-controller", "../components/ion", "./nav-controller", "./nav-params", "./swipe-back"], factory);
  18. }
  19. })(function (require, exports) {
  20. "use strict";
  21. Object.defineProperty(exports, "__esModule", { value: true });
  22. var core_1 = require("@angular/core");
  23. var nav_util_1 = require("./nav-util");
  24. var nav_util_2 = require("./nav-util");
  25. var util_1 = require("../util/util");
  26. var view_controller_1 = require("./view-controller");
  27. var ion_1 = require("../components/ion");
  28. var nav_controller_1 = require("./nav-controller");
  29. var nav_params_1 = require("./nav-params");
  30. var swipe_back_1 = require("./swipe-back");
  31. /**
  32. * @hidden
  33. * This class is for internal use only. It is not exported publicly.
  34. */
  35. var NavControllerBase = (function (_super) {
  36. __extends(NavControllerBase, _super);
  37. function NavControllerBase(parent, _app, config, plt, elementRef, _zone, renderer, _cfr, _gestureCtrl, _trnsCtrl, _linker, _domCtrl, _errHandler) {
  38. var _this = _super.call(this, config, elementRef, renderer) || this;
  39. _this.parent = parent;
  40. _this._app = _app;
  41. _this.config = config;
  42. _this.plt = plt;
  43. _this._zone = _zone;
  44. _this._cfr = _cfr;
  45. _this._gestureCtrl = _gestureCtrl;
  46. _this._trnsCtrl = _trnsCtrl;
  47. _this._linker = _linker;
  48. _this._domCtrl = _domCtrl;
  49. _this._errHandler = _errHandler;
  50. _this._ids = -1;
  51. _this._init = false;
  52. _this._queue = [];
  53. _this._trnsId = null;
  54. _this._trnsTm = false;
  55. _this._views = [];
  56. _this._zIndexOffset = 0;
  57. _this.viewDidLoad = new core_1.EventEmitter();
  58. _this.viewWillEnter = new core_1.EventEmitter();
  59. _this.viewDidEnter = new core_1.EventEmitter();
  60. _this.viewWillLeave = new core_1.EventEmitter();
  61. _this.viewDidLeave = new core_1.EventEmitter();
  62. _this.viewWillUnload = new core_1.EventEmitter();
  63. _this._sbEnabled = config.getBoolean('swipeBackEnabled');
  64. _this._children = [];
  65. _this.id = 'n' + (++ctrlIds);
  66. _this._destroyed = false;
  67. return _this;
  68. }
  69. Object.defineProperty(NavControllerBase.prototype, "swipeBackEnabled", {
  70. get: function () {
  71. return this._sbEnabled;
  72. },
  73. set: function (val) {
  74. this._sbEnabled = util_1.isTrueProperty(val);
  75. this._swipeBackCheck();
  76. },
  77. enumerable: true,
  78. configurable: true
  79. });
  80. NavControllerBase.prototype.push = function (page, params, opts, done) {
  81. return this._queueTrns({
  82. insertStart: -1,
  83. insertViews: [{ page: page, params: params }],
  84. opts: opts,
  85. }, done);
  86. };
  87. NavControllerBase.prototype.insert = function (insertIndex, page, params, opts, done) {
  88. return this._queueTrns({
  89. insertStart: insertIndex,
  90. insertViews: [{ page: page, params: params }],
  91. opts: opts,
  92. }, done);
  93. };
  94. NavControllerBase.prototype.insertPages = function (insertIndex, insertPages, opts, done) {
  95. return this._queueTrns({
  96. insertStart: insertIndex,
  97. insertViews: insertPages,
  98. opts: opts,
  99. }, done);
  100. };
  101. NavControllerBase.prototype.pop = function (opts, done) {
  102. return this._queueTrns({
  103. removeStart: -1,
  104. removeCount: 1,
  105. opts: opts,
  106. }, done);
  107. };
  108. NavControllerBase.prototype.popTo = function (indexOrViewCtrl, opts, done) {
  109. var config = {
  110. removeStart: -1,
  111. removeCount: -1,
  112. opts: opts
  113. };
  114. if (view_controller_1.isViewController(indexOrViewCtrl)) {
  115. config.removeView = indexOrViewCtrl;
  116. config.removeStart = 1;
  117. }
  118. else if (util_1.isNumber(indexOrViewCtrl)) {
  119. config.removeStart = indexOrViewCtrl + 1;
  120. }
  121. return this._queueTrns(config, done);
  122. };
  123. NavControllerBase.prototype.popToRoot = function (opts, done) {
  124. return this._queueTrns({
  125. removeStart: 1,
  126. removeCount: -1,
  127. opts: opts,
  128. }, done);
  129. };
  130. NavControllerBase.prototype.popAll = function () {
  131. var promises = [];
  132. for (var i = this._views.length - 1; i >= 0; i--) {
  133. promises.push(this.pop(null));
  134. }
  135. return Promise.all(promises);
  136. };
  137. NavControllerBase.prototype.remove = function (startIndex, removeCount, opts, done) {
  138. if (removeCount === void 0) { removeCount = 1; }
  139. return this._queueTrns({
  140. removeStart: startIndex,
  141. removeCount: removeCount,
  142. opts: opts,
  143. }, done);
  144. };
  145. NavControllerBase.prototype.removeView = function (viewController, opts, done) {
  146. return this._queueTrns({
  147. removeView: viewController,
  148. removeStart: 0,
  149. removeCount: 1,
  150. opts: opts,
  151. }, done);
  152. };
  153. NavControllerBase.prototype.setRoot = function (pageOrViewCtrl, params, opts, done) {
  154. return this.setPages([{ page: pageOrViewCtrl, params: params }], opts, done);
  155. };
  156. NavControllerBase.prototype.setPages = function (viewControllers, opts, done) {
  157. if (util_1.isBlank(opts)) {
  158. opts = {};
  159. }
  160. // if animation wasn't set to true then default it to NOT animate
  161. if (opts.animate !== true) {
  162. opts.animate = false;
  163. }
  164. return this._queueTrns({
  165. insertStart: 0,
  166. insertViews: viewControllers,
  167. removeStart: 0,
  168. removeCount: -1,
  169. opts: opts
  170. }, done);
  171. };
  172. // _queueTrns() adds a navigation stack change to the queue and schedules it to run:
  173. // 1. _nextTrns(): consumes the next transition in the queue
  174. // 2. _viewInit(): initializes enteringView if required
  175. // 3. _viewTest(): ensures canLeave/canEnter returns true, so the operation can continue
  176. // 4. _postViewInit(): add/remove the views from the navigation stack
  177. // 5. _transitionInit(): initializes the visual transition if required and schedules it to run
  178. // 6. _viewAttachToDOM(): attaches the enteringView to the DOM
  179. // 7. _transitionStart(): called once the transition actually starts, it initializes the Animation underneath.
  180. // 8. _transitionFinish(): called once the transition finishes
  181. // 9. _cleanup(): syncs the navigation internal state with the DOM. For example it removes the pages from the DOM or hides/show them.
  182. NavControllerBase.prototype._queueTrns = function (ti, done) {
  183. var promise = new Promise(function (resolve, reject) {
  184. ti.resolve = resolve;
  185. ti.reject = reject;
  186. });
  187. ti.done = done;
  188. // Normalize empty
  189. if (ti.insertViews && ti.insertViews.length === 0) {
  190. ti.insertViews = undefined;
  191. }
  192. // Enqueue transition instruction
  193. this._queue.push(ti);
  194. // if there isn't a transition already happening
  195. // then this will kick off this transition
  196. this._nextTrns();
  197. return promise;
  198. };
  199. NavControllerBase.prototype._success = function (result, ti) {
  200. if (this._queue === null) {
  201. this._fireError('nav controller was destroyed', ti);
  202. return;
  203. }
  204. this._init = true;
  205. this._trnsId = null;
  206. // ensure we're not transitioning here
  207. this.setTransitioning(false);
  208. this._swipeBackCheck();
  209. // let's see if there's another to kick off
  210. this._nextTrns();
  211. if (ti.done) {
  212. ti.done(result.hasCompleted, result.requiresTransition, result.enteringName, result.leavingName, result.direction);
  213. }
  214. ti.resolve(result.hasCompleted);
  215. };
  216. NavControllerBase.prototype._failed = function (rejectReason, ti) {
  217. if (this._queue === null) {
  218. this._fireError('nav controller was destroyed', ti);
  219. return;
  220. }
  221. this._trnsId = null;
  222. this._queue.length = 0;
  223. // let's see if there's another to kick off
  224. this.setTransitioning(false);
  225. this._swipeBackCheck();
  226. this._nextTrns();
  227. this._fireError(rejectReason, ti);
  228. };
  229. NavControllerBase.prototype._fireError = function (rejectReason, ti) {
  230. if (ti.done) {
  231. ti.done(false, false, rejectReason);
  232. }
  233. if (ti.reject && !this._destroyed) {
  234. ti.reject(rejectReason);
  235. }
  236. else {
  237. ti.resolve(false);
  238. }
  239. };
  240. NavControllerBase.prototype._nextTrns = function () {
  241. var _this = this;
  242. // this is the framework's bread 'n butta function
  243. // only one transition is allowed at any given time
  244. if (this.isTransitioning()) {
  245. return false;
  246. }
  247. // there is no transition happening right now
  248. // get the next instruction
  249. var ti = this._queue.shift();
  250. if (!ti) {
  251. return false;
  252. }
  253. // set that this nav is actively transitioning
  254. var enteringView;
  255. var leavingView;
  256. this._startTI(ti)
  257. .then(function () { return _this._loadLazyLoading(ti); })
  258. .then(function () {
  259. leavingView = _this.getActive();
  260. enteringView = _this._getEnteringView(ti, leavingView);
  261. if (!leavingView && !enteringView) {
  262. throw 'no views in the stack to be removed';
  263. }
  264. if (enteringView && enteringView._state === nav_util_1.STATE_NEW) {
  265. _this._viewInit(enteringView);
  266. }
  267. // Needs transition?
  268. ti.requiresTransition = (ti.enteringRequiresTransition || ti.leavingRequiresTransition) && enteringView !== leavingView;
  269. })
  270. .then(function () { return _this._viewTest(enteringView, leavingView, ti); })
  271. .then(function () { return _this._postViewInit(enteringView, leavingView, ti); })
  272. .then(function () { return _this._transition(enteringView, leavingView, ti); })
  273. .then(function (result) { return _this._success(result, ti); })
  274. .catch(function (rejectReason) { return _this._failed(rejectReason, ti); });
  275. return true;
  276. };
  277. NavControllerBase.prototype._startTI = function (ti) {
  278. var viewsLength = this._views.length;
  279. if (util_1.isPresent(ti.removeView)) {
  280. (void 0) /* assert */;
  281. (void 0) /* assert */;
  282. var index = this.indexOf(ti.removeView);
  283. if (index < 0) {
  284. return Promise.reject('removeView was not found');
  285. }
  286. ti.removeStart += index;
  287. }
  288. if (util_1.isPresent(ti.removeStart)) {
  289. if (ti.removeStart < 0) {
  290. ti.removeStart = (viewsLength - 1);
  291. }
  292. if (ti.removeCount < 0) {
  293. ti.removeCount = (viewsLength - ti.removeStart);
  294. }
  295. ti.leavingRequiresTransition = (ti.removeCount > 0) && ((ti.removeStart + ti.removeCount) === viewsLength);
  296. }
  297. if (ti.insertViews) {
  298. // allow -1 to be passed in to auto push it on the end
  299. // and clean up the index if it's larger then the size of the stack
  300. if (ti.insertStart < 0 || ti.insertStart > viewsLength) {
  301. ti.insertStart = viewsLength;
  302. }
  303. ti.enteringRequiresTransition = (ti.insertStart === viewsLength);
  304. }
  305. this.setTransitioning(true);
  306. return Promise.resolve();
  307. };
  308. NavControllerBase.prototype._loadLazyLoading = function (ti) {
  309. var _this = this;
  310. var insertViews = ti.insertViews;
  311. if (insertViews) {
  312. (void 0) /* assert */;
  313. return nav_util_1.convertToViews(this._linker, insertViews).then(function (viewControllers) {
  314. (void 0) /* assert */;
  315. viewControllers = viewControllers.filter(function (v) { return v !== null; });
  316. if (viewControllers.length === 0) {
  317. throw 'invalid views to insert';
  318. }
  319. // Check all the inserted view are correct
  320. for (var i = 0; i < viewControllers.length; i++) {
  321. var view = viewControllers[i];
  322. var nav = view._nav;
  323. if (nav && nav !== _this) {
  324. throw 'inserted view was already inserted';
  325. }
  326. if (view._state === nav_util_1.STATE_DESTROYED) {
  327. throw 'inserted view was already destroyed';
  328. }
  329. }
  330. ti.insertViews = viewControllers;
  331. });
  332. }
  333. return Promise.resolve();
  334. };
  335. NavControllerBase.prototype._getEnteringView = function (ti, leavingView) {
  336. var insertViews = ti.insertViews;
  337. if (insertViews) {
  338. // grab the very last view of the views to be inserted
  339. // and initialize it as the new entering view
  340. return insertViews[insertViews.length - 1];
  341. }
  342. var removeStart = ti.removeStart;
  343. if (util_1.isPresent(removeStart)) {
  344. var views = this._views;
  345. var removeEnd = removeStart + ti.removeCount;
  346. var i;
  347. var view;
  348. for (i = views.length - 1; i >= 0; i--) {
  349. view = views[i];
  350. if ((i < removeStart || i >= removeEnd) && view !== leavingView) {
  351. return view;
  352. }
  353. }
  354. }
  355. return null;
  356. };
  357. NavControllerBase.prototype._postViewInit = function (enteringView, leavingView, ti) {
  358. var _this = this;
  359. (void 0) /* assert */;
  360. (void 0) /* assert */;
  361. (void 0) /* assert */;
  362. var opts = ti.opts || {};
  363. var insertViews = ti.insertViews;
  364. var removeStart = ti.removeStart;
  365. var removeCount = ti.removeCount;
  366. var view;
  367. var i;
  368. var destroyQueue;
  369. // there are views to remove
  370. if (util_1.isPresent(removeStart)) {
  371. (void 0) /* assert */;
  372. (void 0) /* assert */;
  373. destroyQueue = [];
  374. for (i = 0; i < removeCount; i++) {
  375. view = this._views[i + removeStart];
  376. if (view && view !== enteringView && view !== leavingView) {
  377. destroyQueue.push(view);
  378. }
  379. }
  380. // default the direction to "back"
  381. opts.direction = opts.direction || nav_util_1.DIRECTION_BACK;
  382. }
  383. var finalBalance = this._views.length + (insertViews ? insertViews.length : 0) - (removeCount ? removeCount : 0);
  384. (void 0) /* assert */;
  385. if (finalBalance === 0 && !this._isPortal) {
  386. console.warn("You can't remove all the pages in the navigation stack. nav.pop() is probably called too many times.", this, this.getNativeElement());
  387. throw 'navigation stack needs at least one root page';
  388. }
  389. // At this point the transition can not be rejected, any throw should be an error
  390. // there are views to insert
  391. if (insertViews) {
  392. // manually set the new view's id if an id was passed in the options
  393. if (util_1.isPresent(opts.id)) {
  394. enteringView.id = opts.id;
  395. }
  396. // add the views to the
  397. for (i = 0; i < insertViews.length; i++) {
  398. view = insertViews[i];
  399. this._insertViewAt(view, ti.insertStart + i);
  400. }
  401. if (ti.enteringRequiresTransition) {
  402. // default to forward if not already set
  403. opts.direction = opts.direction || nav_util_1.DIRECTION_FORWARD;
  404. }
  405. }
  406. // if the views to be removed are in the beginning or middle
  407. // and there is not a view that needs to visually transition out
  408. // then just destroy them and don't transition anything
  409. // batch all of lifecycles together
  410. // let's make sure, callbacks are zoned
  411. if (destroyQueue && destroyQueue.length > 0) {
  412. this._zone.run(function () {
  413. for (i = 0; i < destroyQueue.length; i++) {
  414. view = destroyQueue[i];
  415. _this._willLeave(view, true);
  416. _this._didLeave(view);
  417. _this._willUnload(view);
  418. }
  419. });
  420. // once all lifecycle events has been delivered, we can safely detroy the views
  421. for (i = 0; i < destroyQueue.length; i++) {
  422. this._destroyView(destroyQueue[i]);
  423. }
  424. }
  425. // set which animation it should use if it wasn't set yet
  426. if (ti.requiresTransition && !opts.animation) {
  427. if (util_1.isPresent(ti.removeStart)) {
  428. opts.animation = (leavingView || enteringView).getTransitionName(opts.direction);
  429. }
  430. else {
  431. opts.animation = (enteringView || leavingView).getTransitionName(opts.direction);
  432. }
  433. }
  434. ti.opts = opts;
  435. };
  436. /**
  437. * DOM WRITE
  438. */
  439. NavControllerBase.prototype._viewInit = function (enteringView) {
  440. (void 0) /* assert */;
  441. (void 0) /* assert */;
  442. // render the entering view, and all child navs and views
  443. // entering view has not been initialized yet
  444. var componentProviders = core_1.ReflectiveInjector.resolve([
  445. { provide: nav_controller_1.NavController, useValue: this },
  446. { provide: view_controller_1.ViewController, useValue: enteringView },
  447. { provide: nav_params_1.NavParams, useValue: enteringView.getNavParams() }
  448. ]);
  449. var componentFactory = this._linker.resolveComponent(enteringView.component);
  450. var childInjector = core_1.ReflectiveInjector.fromResolvedProviders(componentProviders, this._viewport.parentInjector);
  451. // create ComponentRef and set it to the entering view
  452. enteringView.init(componentFactory.create(childInjector, []));
  453. enteringView._state = nav_util_1.STATE_INITIALIZED;
  454. this._preLoad(enteringView);
  455. };
  456. NavControllerBase.prototype._viewAttachToDOM = function (view, componentRef, viewport) {
  457. (void 0) /* assert */;
  458. (void 0) /* assert */;
  459. // fire willLoad before change detection runs
  460. this._willLoad(view);
  461. // render the component ref instance to the DOM
  462. // ******** DOM WRITE ****************
  463. viewport.insert(componentRef.hostView, viewport.length);
  464. view._state = nav_util_1.STATE_ATTACHED;
  465. if (view._cssClass) {
  466. // the ElementRef of the actual ion-page created
  467. var pageElement = componentRef.location.nativeElement;
  468. // ******** DOM WRITE ****************
  469. this._renderer.setElementClass(pageElement, view._cssClass, true);
  470. }
  471. componentRef.changeDetectorRef.detectChanges();
  472. // successfully finished loading the entering view
  473. // fire off the "didLoad" lifecycle events
  474. this._zone.run(this._didLoad.bind(this, view));
  475. };
  476. NavControllerBase.prototype._viewTest = function (enteringView, leavingView, ti) {
  477. // Only test canLeave/canEnter if there is transition
  478. if (!ti.requiresTransition) {
  479. return Promise.resolve();
  480. }
  481. var promises = [];
  482. if (leavingView) {
  483. promises.push(leavingView._lifecycleTest('Leave'));
  484. }
  485. if (enteringView) {
  486. promises.push(enteringView._lifecycleTest('Enter'));
  487. }
  488. if (promises.length === 0) {
  489. return Promise.resolve();
  490. }
  491. // darn, async promises, gotta wait for them to resolve
  492. return Promise.all(promises).then(function (values) {
  493. if (values.some(function (result) { return result === false; })) {
  494. throw 'canEnter/Leave returned false';
  495. }
  496. }).catch(function (reason) {
  497. // Do not
  498. ti.reject = null;
  499. throw reason;
  500. });
  501. };
  502. NavControllerBase.prototype._transition = function (enteringView, leavingView, ti) {
  503. var _this = this;
  504. if (!ti.requiresTransition) {
  505. // transition is not required, so we are already done!
  506. // they're inserting/removing the views somewhere in the middle or
  507. // beginning, so visually nothing needs to animate/transition
  508. // resolve immediately because there's no animation that's happening
  509. return Promise.resolve({
  510. hasCompleted: true,
  511. requiresTransition: false
  512. });
  513. }
  514. var opts = ti.opts;
  515. // figure out if this transition is the root one or a
  516. // child of a parent nav that has the root transition
  517. this._trnsId = this._trnsCtrl.getRootTrnsId(this);
  518. if (this._trnsId === null) {
  519. // this is the root transition, meaning all child navs and their views
  520. // should be added as a child transition to this one
  521. this._trnsId = this._trnsCtrl.nextId();
  522. }
  523. // create the transition options
  524. var animationOpts = {
  525. animation: opts.animation,
  526. direction: opts.direction,
  527. duration: (opts.animate === false ? 0 : opts.duration),
  528. easing: opts.easing,
  529. isRTL: this._config.plt.isRTL,
  530. ev: opts.ev,
  531. };
  532. // create the transition animation from the TransitionController
  533. // this will either create the root transition, or add it as a child transition
  534. var transition = this._trnsCtrl.get(this._trnsId, enteringView, leavingView, animationOpts);
  535. // ensure any swipeback transitions are cleared out
  536. this._sbTrns && this._sbTrns.destroy();
  537. this._sbTrns = null;
  538. // swipe to go back root transition
  539. if (transition.isRoot() && opts.progressAnimation) {
  540. this._sbTrns = transition;
  541. }
  542. // transition start has to be registered before attaching the view to the DOM!
  543. var promise = new Promise(function (resolve) { return transition.registerStart(resolve); }).then(function () {
  544. return _this._transitionStart(transition, enteringView, leavingView, opts);
  545. });
  546. if (enteringView && (enteringView._state === nav_util_1.STATE_INITIALIZED)) {
  547. // render the entering component in the DOM
  548. // this would also render new child navs/views
  549. // which may have their very own async canEnter/Leave tests
  550. // ******** DOM WRITE ****************
  551. this._viewAttachToDOM(enteringView, enteringView._cmp, this._viewport);
  552. }
  553. if (!transition.hasChildren) {
  554. // lowest level transition, so kick it off and let it bubble up to start all of them
  555. transition.start();
  556. }
  557. return promise;
  558. };
  559. NavControllerBase.prototype._transitionStart = function (transition, enteringView, leavingView, opts) {
  560. var _this = this;
  561. (void 0) /* assert */;
  562. this._trnsId = null;
  563. // set the correct zIndex for the entering and leaving views
  564. // ******** DOM WRITE ****************
  565. nav_util_2.setZIndex(this, enteringView, leavingView, opts.direction, this._renderer);
  566. // always ensure the entering view is viewable
  567. // ******** DOM WRITE ****************
  568. enteringView && enteringView._domShow(true, this._renderer);
  569. // always ensure the leaving view is viewable
  570. // ******** DOM WRITE ****************
  571. leavingView && leavingView._domShow(true, this._renderer);
  572. // initialize the transition
  573. transition.init();
  574. // we should animate (duration > 0) if the pushed page is not the first one (startup)
  575. // or if it is a portal (modal, actionsheet, etc.)
  576. var isFirstPage = !this._init && this._views.length === 1;
  577. var shouldNotAnimate = isFirstPage && !this._isPortal;
  578. var canNotAnimate = this._config.get('animate') === false;
  579. if (shouldNotAnimate || canNotAnimate) {
  580. opts.animate = false;
  581. }
  582. if (opts.animate === false) {
  583. // if it was somehow set to not animation, then make the duration zero
  584. transition.duration(0);
  585. }
  586. // create a callback that needs to run within zone
  587. // that will fire off the willEnter/Leave lifecycle events at the right time
  588. transition.beforeAddRead(this._viewsWillLifecycles.bind(this, enteringView, leavingView));
  589. // get the set duration of this transition
  590. var duration = transition.getDuration();
  591. // create a callback for when the animation is done
  592. var promise = new Promise(function (resolve) {
  593. transition.onFinish(resolve);
  594. });
  595. if (transition.isRoot()) {
  596. // this is the top most, or only active transition, so disable the app
  597. // add XXms to the duration the app is disabled when the keyboard is open
  598. if (duration > DISABLE_APP_MINIMUM_DURATION && opts.disableApp !== false) {
  599. // if this transition has a duration and this is the root transition
  600. // then set that the app is actively disabled
  601. this._app.setEnabled(false, duration + ACTIVE_TRANSITION_OFFSET, opts.minClickBlockDuration);
  602. }
  603. else {
  604. (void 0) /* console.debug */;
  605. }
  606. // cool, let's do this, start the transition
  607. if (opts.progressAnimation) {
  608. // this is a swipe to go back, just get the transition progress ready
  609. // kick off the swipe animation start
  610. transition.progressStart();
  611. }
  612. else {
  613. // only the top level transition should actually start "play"
  614. // kick it off and let it play through
  615. // ******** DOM WRITE ****************
  616. transition.play();
  617. }
  618. }
  619. return promise.then(function () { return _this._zone.run(function () {
  620. return _this._transitionFinish(transition, opts);
  621. }); });
  622. };
  623. NavControllerBase.prototype._transitionFinish = function (transition, opts) {
  624. var hasCompleted = transition.hasCompleted;
  625. var enteringView = transition.enteringView;
  626. var leavingView = transition.leavingView;
  627. // mainly for testing
  628. var enteringName;
  629. var leavingName;
  630. if (hasCompleted) {
  631. // transition has completed (went from 0 to 1)
  632. if (enteringView) {
  633. enteringName = enteringView.name;
  634. this._didEnter(enteringView);
  635. }
  636. if (leavingView) {
  637. leavingName = leavingView.name;
  638. this._didLeave(leavingView);
  639. }
  640. this._cleanup(enteringView);
  641. }
  642. else {
  643. // If transition does not complete, we have to cleanup anyway, because
  644. // previous pages in the stack are not hidden probably.
  645. this._cleanup(leavingView);
  646. }
  647. if (transition.isRoot()) {
  648. // this is the root transition
  649. // it's safe to destroy this transition
  650. this._trnsCtrl.destroy(transition.trnsId);
  651. // it's safe to enable the app again
  652. this._app.setEnabled(true);
  653. // mark ourselves as not transitioning - `deepLinker navchange` requires this
  654. // TODO - probably could be resolved in a better way
  655. this.setTransitioning(false);
  656. if (!this.hasChildren() && opts.updateUrl !== false) {
  657. // notify deep linker of the nav change
  658. // if a direction was provided and should update url
  659. this._linker.navChange(opts.direction);
  660. }
  661. if (opts.keyboardClose !== false) {
  662. // the keyboard is still open!
  663. // no problem, let's just close for them
  664. this.plt.focusOutActiveElement();
  665. }
  666. }
  667. return {
  668. hasCompleted: hasCompleted,
  669. requiresTransition: true,
  670. enteringName: enteringName,
  671. leavingName: leavingName,
  672. direction: opts.direction
  673. };
  674. };
  675. NavControllerBase.prototype._viewsWillLifecycles = function (enteringView, leavingView) {
  676. var _this = this;
  677. if (enteringView || leavingView) {
  678. this._zone.run(function () {
  679. // Here, the order is important. WillLeave must be called before WillEnter.
  680. if (leavingView) {
  681. var willUnload = enteringView ? leavingView.index > enteringView.index : true;
  682. _this._willLeave(leavingView, willUnload);
  683. }
  684. enteringView && _this._willEnter(enteringView);
  685. });
  686. }
  687. };
  688. NavControllerBase.prototype._insertViewAt = function (view, index) {
  689. var existingIndex = this._views.indexOf(view);
  690. if (existingIndex > -1) {
  691. // this view is already in the stack!!
  692. // move it to its new location
  693. (void 0) /* assert */;
  694. this._views.splice(index, 0, this._views.splice(existingIndex, 1)[0]);
  695. }
  696. else {
  697. (void 0) /* assert */;
  698. // this is a new view to add to the stack
  699. // create the new entering view
  700. view._setNav(this);
  701. // give this inserted view an ID
  702. this._ids++;
  703. if (!view.id) {
  704. view.id = this.id + "-" + this._ids;
  705. }
  706. // insert the entering view into the correct index in the stack
  707. this._views.splice(index, 0, view);
  708. }
  709. };
  710. NavControllerBase.prototype._removeView = function (view) {
  711. (void 0) /* assert */;
  712. var views = this._views;
  713. var index = views.indexOf(view);
  714. (void 0) /* assert */;
  715. if (index >= 0) {
  716. views.splice(index, 1);
  717. }
  718. };
  719. NavControllerBase.prototype._destroyView = function (view) {
  720. view._destroy(this._renderer);
  721. this._removeView(view);
  722. };
  723. /**
  724. * DOM WRITE
  725. */
  726. NavControllerBase.prototype._cleanup = function (activeView) {
  727. // ok, cleanup time!! Destroy all of the views that are
  728. // INACTIVE and come after the active view
  729. // only do this if the views exist, though
  730. if (!this._destroyed) {
  731. var activeViewIndex = this._views.indexOf(activeView);
  732. var views = this._views;
  733. var reorderZIndexes = false;
  734. var view = void 0;
  735. var i = void 0;
  736. for (i = views.length - 1; i >= 0; i--) {
  737. view = views[i];
  738. if (i > activeViewIndex) {
  739. // this view comes after the active view
  740. // let's unload it
  741. this._willUnload(view);
  742. this._destroyView(view);
  743. }
  744. else if (i < activeViewIndex && !this._isPortal) {
  745. // this view comes before the active view
  746. // and it is not a portal then ensure it is hidden
  747. view._domShow(false, this._renderer);
  748. }
  749. if (view._zIndex <= 0) {
  750. reorderZIndexes = true;
  751. }
  752. }
  753. if (!this._isPortal && reorderZIndexes) {
  754. for (i = 0; i < views.length; i++) {
  755. view = views[i];
  756. // ******** DOM WRITE ****************
  757. view._setZIndex(view._zIndex + nav_util_1.INIT_ZINDEX + 1, this._renderer);
  758. }
  759. }
  760. }
  761. };
  762. NavControllerBase.prototype._preLoad = function (view) {
  763. (void 0) /* assert */;
  764. view._preLoad();
  765. };
  766. NavControllerBase.prototype._willLoad = function (view) {
  767. (void 0) /* assert */;
  768. try {
  769. view._willLoad();
  770. }
  771. catch (e) {
  772. this._errHandler && this._errHandler.handleError(e);
  773. }
  774. };
  775. NavControllerBase.prototype._didLoad = function (view) {
  776. (void 0) /* assert */;
  777. (void 0) /* assert */;
  778. try {
  779. view._didLoad();
  780. this.viewDidLoad.emit(view);
  781. this._app.viewDidLoad.emit(view);
  782. }
  783. catch (e) {
  784. this._errHandler && this._errHandler.handleError(e);
  785. }
  786. };
  787. NavControllerBase.prototype._willEnter = function (view) {
  788. (void 0) /* assert */;
  789. (void 0) /* assert */;
  790. try {
  791. view._willEnter();
  792. this.viewWillEnter.emit(view);
  793. this._app.viewWillEnter.emit(view);
  794. }
  795. catch (e) {
  796. this._errHandler && this._errHandler.handleError(e);
  797. }
  798. };
  799. NavControllerBase.prototype._didEnter = function (view) {
  800. (void 0) /* assert */;
  801. (void 0) /* assert */;
  802. try {
  803. view._didEnter();
  804. this.viewDidEnter.emit(view);
  805. this._app.viewDidEnter.emit(view);
  806. }
  807. catch (e) {
  808. this._errHandler && this._errHandler.handleError(e);
  809. }
  810. };
  811. NavControllerBase.prototype._willLeave = function (view, willUnload) {
  812. (void 0) /* assert */;
  813. (void 0) /* assert */;
  814. try {
  815. view._willLeave(willUnload);
  816. this.viewWillLeave.emit(view);
  817. this._app.viewWillLeave.emit(view);
  818. }
  819. catch (e) {
  820. this._errHandler && this._errHandler.handleError(e);
  821. }
  822. };
  823. NavControllerBase.prototype._didLeave = function (view) {
  824. (void 0) /* assert */;
  825. (void 0) /* assert */;
  826. try {
  827. view._didLeave();
  828. this.viewDidLeave.emit(view);
  829. this._app.viewDidLeave.emit(view);
  830. }
  831. catch (e) {
  832. this._errHandler && this._errHandler.handleError(e);
  833. }
  834. };
  835. NavControllerBase.prototype._willUnload = function (view) {
  836. (void 0) /* assert */;
  837. (void 0) /* assert */;
  838. try {
  839. view._willUnload();
  840. this.viewWillUnload.emit(view);
  841. this._app.viewWillUnload.emit(view);
  842. }
  843. catch (e) {
  844. this._errHandler && this._errHandler.handleError(e);
  845. }
  846. };
  847. NavControllerBase.prototype.hasChildren = function () {
  848. return this._children && this._children.length > 0;
  849. };
  850. NavControllerBase.prototype.getActiveChildNavs = function () {
  851. return this._children;
  852. };
  853. NavControllerBase.prototype.getAllChildNavs = function () {
  854. return this._children;
  855. };
  856. NavControllerBase.prototype.registerChildNav = function (container) {
  857. this._children.push(container);
  858. };
  859. NavControllerBase.prototype.unregisterChildNav = function (nav) {
  860. this._children = this._children.filter(function (child) { return child !== nav; });
  861. };
  862. NavControllerBase.prototype.destroy = function () {
  863. var views = this._views;
  864. var view;
  865. for (var i = 0; i < views.length; i++) {
  866. view = views[i];
  867. view._willUnload();
  868. view._destroy(this._renderer);
  869. }
  870. // release swipe back gesture and transition
  871. this._sbGesture && this._sbGesture.destroy();
  872. this._sbTrns && this._sbTrns.destroy();
  873. this._queue = this._views = this._sbGesture = this._sbTrns = null;
  874. // Unregister navcontroller
  875. if (this.parent && this.parent.unregisterChildNav) {
  876. this.parent.unregisterChildNav(this);
  877. }
  878. else if (this._app) {
  879. this._app.unregisterRootNav(this);
  880. }
  881. this._destroyed = true;
  882. };
  883. NavControllerBase.prototype.swipeBackStart = function () {
  884. if (this.isTransitioning() || this._queue.length > 0) {
  885. return;
  886. }
  887. // default the direction to "back";
  888. var opts = {
  889. direction: nav_util_1.DIRECTION_BACK,
  890. progressAnimation: true
  891. };
  892. this._queueTrns({
  893. removeStart: -1,
  894. removeCount: 1,
  895. opts: opts,
  896. }, null);
  897. };
  898. NavControllerBase.prototype.swipeBackProgress = function (stepValue) {
  899. if (this._sbTrns && this._sbGesture) {
  900. // continue to disable the app while actively dragging
  901. this._app.setEnabled(false, ACTIVE_TRANSITION_DEFAULT);
  902. this.setTransitioning(true);
  903. // set the transition animation's progress
  904. this._sbTrns.progressStep(stepValue);
  905. }
  906. };
  907. NavControllerBase.prototype.swipeBackEnd = function (shouldComplete, currentStepValue, velocity) {
  908. if (this._sbTrns && this._sbGesture) {
  909. // the swipe back gesture has ended
  910. var dur = this._sbTrns.getDuration() / (Math.abs(velocity) + 1);
  911. this._sbTrns.progressEnd(shouldComplete, currentStepValue, dur);
  912. }
  913. };
  914. NavControllerBase.prototype._swipeBackCheck = function () {
  915. if (this.canSwipeBack()) {
  916. if (!this._sbGesture) {
  917. this._sbGesture = new swipe_back_1.SwipeBackGesture(this.plt, this, this._gestureCtrl, this._domCtrl);
  918. }
  919. this._sbGesture.listen();
  920. }
  921. else if (this._sbGesture) {
  922. this._sbGesture.unlisten();
  923. }
  924. };
  925. NavControllerBase.prototype.canSwipeBack = function () {
  926. return (this._sbEnabled &&
  927. !this._isPortal &&
  928. !this._children.length &&
  929. !this.isTransitioning() &&
  930. this._app.isEnabled() &&
  931. this.canGoBack());
  932. };
  933. NavControllerBase.prototype.canGoBack = function () {
  934. var activeView = this.getActive();
  935. return !!(activeView && activeView.enableBack());
  936. };
  937. NavControllerBase.prototype.isTransitioning = function () {
  938. return this._trnsTm;
  939. };
  940. NavControllerBase.prototype.setTransitioning = function (isTransitioning) {
  941. this._trnsTm = isTransitioning;
  942. };
  943. NavControllerBase.prototype.getActive = function () {
  944. return this._views[this._views.length - 1];
  945. };
  946. NavControllerBase.prototype.isActive = function (view) {
  947. return (view === this.getActive());
  948. };
  949. NavControllerBase.prototype.getByIndex = function (index) {
  950. return this._views[index];
  951. };
  952. NavControllerBase.prototype.getPrevious = function (view) {
  953. // returns the view controller which is before the given view controller.
  954. if (!view) {
  955. view = this.getActive();
  956. }
  957. var views = this._views;
  958. var index = views.indexOf(view);
  959. return (index > 0) ? views[index - 1] : null;
  960. };
  961. NavControllerBase.prototype.first = function () {
  962. // returns the first view controller in this nav controller's stack.
  963. return this._views[0];
  964. };
  965. NavControllerBase.prototype.last = function () {
  966. // returns the last page in this nav controller's stack.
  967. var views = this._views;
  968. return views[views.length - 1];
  969. };
  970. NavControllerBase.prototype.indexOf = function (view) {
  971. // returns the index number of the given view controller.
  972. return this._views.indexOf(view);
  973. };
  974. NavControllerBase.prototype.length = function () {
  975. return this._views.length;
  976. };
  977. NavControllerBase.prototype.getViews = function () {
  978. return this._views;
  979. };
  980. /**
  981. * Return a view controller
  982. */
  983. NavControllerBase.prototype.getViewById = function (id) {
  984. for (var _i = 0, _a = this._views; _i < _a.length; _i++) {
  985. var vc = _a[_i];
  986. if (vc && vc.id === id) {
  987. return vc;
  988. }
  989. }
  990. return null;
  991. };
  992. NavControllerBase.prototype.isSwipeBackEnabled = function () {
  993. return this._sbEnabled;
  994. };
  995. NavControllerBase.prototype.dismissPageChangeViews = function () {
  996. for (var _i = 0, _a = this._views; _i < _a.length; _i++) {
  997. var view = _a[_i];
  998. if (view.data && view.data.dismissOnPageChange) {
  999. view.dismiss().catch(function () { });
  1000. }
  1001. }
  1002. };
  1003. NavControllerBase.prototype.setViewport = function (val) {
  1004. this._viewport = val;
  1005. };
  1006. NavControllerBase.prototype.resize = function () {
  1007. var active = this.getActive();
  1008. if (!active) {
  1009. return;
  1010. }
  1011. var content = active.getIONContent();
  1012. content && content.resize();
  1013. };
  1014. NavControllerBase.prototype.goToRoot = function (_opts) {
  1015. return Promise.reject(new Error('goToRoot needs to be implemented by child class'));
  1016. };
  1017. /*
  1018. * @private
  1019. */
  1020. NavControllerBase.prototype.getType = function () {
  1021. return 'nav';
  1022. };
  1023. /*
  1024. * @private
  1025. */
  1026. NavControllerBase.prototype.getSecondaryIdentifier = function () {
  1027. return null;
  1028. };
  1029. /**
  1030. * Returns the active child navigation.
  1031. */
  1032. NavControllerBase.prototype.getActiveChildNav = function () {
  1033. console.warn('(getActiveChildNav) is deprecated and will be removed in the next major release. Use getActiveChildNavs instead.');
  1034. return this._children[this._children.length - 1];
  1035. };
  1036. NavControllerBase.propDecorators = {
  1037. 'swipeBackEnabled': [{ type: core_1.Input },],
  1038. };
  1039. return NavControllerBase;
  1040. }(ion_1.Ion));
  1041. exports.NavControllerBase = NavControllerBase;
  1042. var ctrlIds = -1;
  1043. var DISABLE_APP_MINIMUM_DURATION = 64;
  1044. var ACTIVE_TRANSITION_DEFAULT = 5000;
  1045. var ACTIVE_TRANSITION_OFFSET = 2000;
  1046. });
  1047. //# sourceMappingURL=nav-controller-base.js.map