123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419 |
- import { DIRECTION_BACK, convertToViews, isNav, isTab, isTabs } from './nav-util';
- import { isArray, isPresent } from '../util/util';
- import { formatUrlPart } from './url-serializer';
- import { ViewController } from './view-controller';
- /**
- * @hidden
- */
- var DeepLinker = (function () {
- function DeepLinker(_app, _serializer, _location, _moduleLoader, _baseCfr) {
- this._app = _app;
- this._serializer = _serializer;
- this._location = _location;
- this._moduleLoader = _moduleLoader;
- this._baseCfr = _baseCfr;
- /** @internal */
- this._history = [];
- }
- /**
- * @internal
- */
- DeepLinker.prototype.init = function () {
- var _this = this;
- // scenario 1: Initial load of all navs from the initial browser URL
- var browserUrl = normalizeUrl(this._location.path());
- (void 0) /* console.debug */;
- // remember this URL in our internal history stack
- this._historyPush(browserUrl);
- // listen for browser URL changes
- this._location.subscribe(function (locationChg) {
- _this._urlChange(normalizeUrl(locationChg.url));
- });
- };
- /**
- * The browser's location has been updated somehow.
- * @internal
- */
- DeepLinker.prototype._urlChange = function (browserUrl) {
- var _this = this;
- // do nothing if this url is the same as the current one
- if (!this._isCurrentUrl(browserUrl)) {
- var isGoingBack = true;
- if (this._isBackUrl(browserUrl)) {
- // scenario 2: user clicked the browser back button
- // scenario 4: user changed the browser URL to what was the back url was
- // scenario 5: user clicked a link href that was the back url
- (void 0) /* console.debug */;
- this._historyPop();
- }
- else {
- // scenario 3: user click forward button
- // scenario 4: user changed browser URL that wasn't the back url
- // scenario 5: user clicked a link href that wasn't the back url
- isGoingBack = false;
- (void 0) /* console.debug */;
- this._historyPush(browserUrl);
- }
- // get the app's root nav container
- var activeNavContainers_1 = this._app.getActiveNavContainers();
- if (activeNavContainers_1 && activeNavContainers_1.length) {
- if (browserUrl === '/') {
- // a url change to the index url
- if (isPresent(this._indexAliasUrl)) {
- // we already know the indexAliasUrl
- // update the url to use the know alias
- browserUrl = this._indexAliasUrl;
- }
- else {
- // the url change is to the root but we don't
- // already know the url used. So let's just
- // reset the root nav to its root page
- activeNavContainers_1.forEach(function (navContainer) {
- navContainer.goToRoot({
- updateUrl: false,
- isNavRoot: true
- });
- });
- return;
- }
- }
- // normal url
- var segments = this.getCurrentSegments(browserUrl);
- segments
- .map(function (segment) {
- // find the matching nav container
- for (var _i = 0, activeNavContainers_2 = activeNavContainers_1; _i < activeNavContainers_2.length; _i++) {
- var navContainer = activeNavContainers_2[_i];
- var nav = getNavFromTree(navContainer, segment.navId);
- if (nav) {
- return {
- segment: segment,
- navContainer: nav
- };
- }
- }
- })
- .filter(function (pair) { return !!pair; })
- .forEach(function (pair) {
- _this._loadViewForSegment(pair.navContainer, pair.segment, function () { });
- });
- }
- }
- };
- DeepLinker.prototype.getCurrentSegments = function (browserUrl) {
- if (!browserUrl) {
- browserUrl = normalizeUrl(this._location.path());
- }
- return this._serializer.parse(browserUrl);
- };
- /**
- * Update the deep linker using the NavController's current active view.
- * @internal
- */
- DeepLinker.prototype.navChange = function (direction) {
- if (direction) {
- var activeNavContainers = this._app.getActiveNavContainers();
- // the only time you'll ever get a TABS here is when loading directly from a URL
- // this method will be called again when the TAB is loaded
- // so just don't worry about the TABS for now
- // if you encounter a TABS, just return
- for (var _i = 0, activeNavContainers_3 = activeNavContainers; _i < activeNavContainers_3.length; _i++) {
- var activeNavContainer = activeNavContainers_3[_i];
- if (isTabs(activeNavContainer) || activeNavContainer.isTransitioning()) {
- return;
- }
- }
- // okay, get the root navs and build the segments up
- var segments = [];
- var navContainers = this._app.getRootNavs();
- for (var _a = 0, navContainers_1 = navContainers; _a < navContainers_1.length; _a++) {
- var navContainer = navContainers_1[_a];
- var segmentsForNav = this.getSegmentsFromNav(navContainer);
- segments = segments.concat(segmentsForNav);
- }
- segments = segments.filter(function (segment) { return !!segment; });
- if (segments.length) {
- var browserUrl = this._serializer.serialize(segments);
- this._updateLocation(browserUrl, direction);
- }
- }
- };
- DeepLinker.prototype.getSegmentsFromNav = function (nav) {
- var _this = this;
- var segments = [];
- if (isNav(nav)) {
- segments.push(this.getSegmentFromNav(nav));
- }
- else if (isTab(nav)) {
- segments.push(this.getSegmentFromTab(nav));
- }
- nav.getActiveChildNavs().forEach(function (child) {
- segments = segments.concat(_this.getSegmentsFromNav(child));
- });
- return segments;
- };
- DeepLinker.prototype.getSegmentFromNav = function (nav, component, data) {
- if (!component) {
- var viewController = nav.getActive(true);
- if (viewController) {
- component = viewController.component;
- data = viewController.data;
- }
- }
- return this._serializer.serializeComponent(nav, component, data);
- };
- DeepLinker.prototype.getSegmentFromTab = function (navContainer, component, data) {
- if (navContainer && navContainer.parent) {
- var tabsNavContainer = navContainer.parent;
- var activeChildNavs = tabsNavContainer.getActiveChildNavs();
- if (activeChildNavs && activeChildNavs.length) {
- var activeChildNav = activeChildNavs[0];
- var viewController = activeChildNav.getActive(true);
- if (viewController) {
- component = viewController.component;
- data = viewController.data;
- }
- return this._serializer.serializeComponent(tabsNavContainer, component, data);
- }
- }
- };
- /**
- * @internal
- */
- DeepLinker.prototype._updateLocation = function (browserUrl, direction) {
- if (this._indexAliasUrl === browserUrl) {
- browserUrl = '/';
- }
- if (direction === DIRECTION_BACK && this._isBackUrl(browserUrl)) {
- // this URL is exactly the same as the back URL
- // it's safe to use the browser's location.back()
- (void 0) /* console.debug */;
- this._historyPop();
- this._location.back();
- }
- else if (!this._isCurrentUrl(browserUrl)) {
- // probably navigating forward
- (void 0) /* console.debug */;
- this._historyPush(browserUrl);
- this._location.go(browserUrl);
- }
- };
- DeepLinker.prototype.getComponentFromName = function (componentName) {
- var link = this._serializer.getLinkFromName(componentName);
- if (link) {
- // cool, we found the right link for this component name
- return this.getNavLinkComponent(link);
- }
- // umm, idk
- return Promise.reject("invalid link: " + componentName);
- };
- DeepLinker.prototype.getNavLinkComponent = function (link) {
- if (link.component) {
- // sweet, we're already got a component loaded for this link
- return Promise.resolve(link.component);
- }
- if (link.loadChildren) {
- // awesome, looks like we'll lazy load this component
- // using loadChildren as the URL to request
- return this._moduleLoader.load(link.loadChildren).then(function (response) {
- link.component = response.component;
- return response.component;
- });
- }
- return Promise.reject("invalid link component: " + link.name);
- };
- /**
- * @internal
- */
- DeepLinker.prototype.resolveComponent = function (component) {
- var cfr = this._moduleLoader.getComponentFactoryResolver(component);
- if (!cfr) {
- cfr = this._baseCfr;
- }
- return cfr.resolveComponentFactory(component);
- };
- /**
- * @internal
- */
- DeepLinker.prototype.createUrl = function (navContainer, nameOrComponent, _data, prepareExternalUrl) {
- if (prepareExternalUrl === void 0) { prepareExternalUrl = true; }
- // create a segment out of just the passed in name
- var segment = this._serializer.createSegmentFromName(navContainer, nameOrComponent);
- var allSegments = this.getCurrentSegments();
- if (segment) {
- for (var i = 0; i < allSegments.length; i++) {
- if (allSegments[i].navId === navContainer.name || allSegments[i].navId === navContainer.id) {
- allSegments[i] = segment;
- var url = this._serializer.serialize(allSegments);
- return prepareExternalUrl ? this._location.prepareExternalUrl(url) : url;
- }
- }
- }
- return '';
- };
- /**
- * Each NavController will call this method when it initializes for
- * the first time. This allows each NavController to figure out
- * where it lives in the path and load up the correct component.
- * @internal
- */
- DeepLinker.prototype.getSegmentByNavIdOrName = function (navId, name) {
- var browserUrl = normalizeUrl(this._location.path());
- var segments = this._serializer.parse(browserUrl);
- for (var _i = 0, segments_1 = segments; _i < segments_1.length; _i++) {
- var segment = segments_1[_i];
- if (segment.navId === navId || segment.navId === name) {
- return segment;
- }
- }
- return null;
- };
- /**
- * @internal
- */
- DeepLinker.prototype.initViews = function (segment) {
- var _this = this;
- var link = this._serializer.getLinkFromName(segment.name);
- return this.getNavLinkComponent(link).then(function (component) {
- segment.component = component;
- var view = new ViewController(component, segment.data);
- view.id = segment.id;
- if (isArray(segment.defaultHistory)) {
- return convertToViews(_this, segment.defaultHistory).then(function (views) {
- views.push(view);
- return views;
- });
- }
- return [view];
- });
- };
- /**
- * @internal
- */
- DeepLinker.prototype._isBackUrl = function (browserUrl) {
- return (browserUrl === this._history[this._history.length - 2]);
- };
- /**
- * @internal
- */
- DeepLinker.prototype._isCurrentUrl = function (browserUrl) {
- return (browserUrl === this._history[this._history.length - 1]);
- };
- /**
- * @internal
- */
- DeepLinker.prototype._historyPush = function (browserUrl) {
- if (!this._isCurrentUrl(browserUrl)) {
- this._history.push(browserUrl);
- if (this._history.length > 30) {
- this._history.shift();
- }
- }
- };
- /**
- * @internal
- */
- DeepLinker.prototype._historyPop = function () {
- this._history.pop();
- if (!this._history.length) {
- this._historyPush(this._location.path());
- }
- };
- /**
- * @internal
- */
- DeepLinker.prototype._getTabSelector = function (tab) {
- if (isPresent(tab.tabUrlPath)) {
- return tab.tabUrlPath;
- }
- if (isPresent(tab.tabTitle)) {
- return formatUrlPart(tab.tabTitle);
- }
- return "tab-" + tab.index;
- };
- /**
- * Using the known Path of Segments, walk down all descendents
- * from the root NavController and load each NavController according
- * to each Segment. This is usually called after a browser URL and
- * Path changes and needs to update all NavControllers to match
- * the new browser URL. Because the URL is already known, it will
- * not update the browser's URL when transitions have completed.
- *
- * @internal
- */
- DeepLinker.prototype._loadViewForSegment = function (navContainer, segment, done) {
- if (!segment) {
- return done(false, false);
- }
- if (isTabs(navContainer) || (isTab(navContainer) && navContainer.parent)) {
- var tabs = (isTabs(navContainer) ? navContainer : navContainer.parent);
- var selectedIndex = tabs._getSelectedTabIndex(segment.secondaryId);
- var tab = tabs.getByIndex(selectedIndex);
- tab._segment = segment;
- tabs.select(tab, {
- updateUrl: false,
- animate: false
- }, true);
- return done(false, false);
- }
- var navController = navContainer;
- var numViews = navController.length() - 1;
- // walk backwards to see if the exact view we want to show here
- // is already in the stack that we can just pop back to
- for (var i = numViews; i >= 0; i--) {
- var viewController = navController.getByIndex(i);
- if (viewController && (viewController.id === segment.id || viewController.id === segment.name)) {
- // hooray! we've already got a view loaded in the stack
- // matching the view they wanted to show
- if (i === numViews) {
- // this is the last view in the stack and it's the same
- // as the segment so there's no change needed
- return done(false, false);
- }
- else {
- // it's not the exact view as the end
- // let's have this nav go back to this exact view
- return navController.popTo(viewController, {
- animate: false,
- updateUrl: false,
- }, done);
- }
- }
- }
- // ok, so we don't know about a view that they're navigating to
- // so we might as well just call setRoot and make tthe view the first view
- // this seems like the least bad option
- return navController.setRoot(segment.component || segment.name, segment.data, {
- id: segment.id, animate: false, updateUrl: false
- }, done);
- };
- return DeepLinker;
- }());
- export { DeepLinker };
- export function setupDeepLinker(app, serializer, location, moduleLoader, cfr) {
- var deepLinker = new DeepLinker(app, serializer, location, moduleLoader, cfr);
- deepLinker.init();
- return deepLinker;
- }
- export function normalizeUrl(browserUrl) {
- browserUrl = browserUrl.trim();
- if (browserUrl.charAt(0) !== '/') {
- // ensure first char is a /
- browserUrl = '/' + browserUrl;
- }
- if (browserUrl.length > 1 && browserUrl.charAt(browserUrl.length - 1) === '/') {
- // ensure last char is not a /
- browserUrl = browserUrl.substr(0, browserUrl.length - 1);
- }
- return browserUrl;
- }
- export function getNavFromTree(nav, id) {
- while (nav) {
- if (nav.id === id || nav.name === id) {
- return nav;
- }
- nav = nav.parent;
- }
- return null;
- }
- //# sourceMappingURL=deep-linker.js.map
|