JavaScript Application Development API for Oracle Visual Builder Cloud Service - Classic Applications

Source: core/js/router/HistoryApiRouterStateObserver.js

define([
    'module',
    'core/js/api/router/AbstractRouter',
    'core/js/api/router/RouterState',
    'core/js/spi/router/RouterStateObserver',
    'viewmodel/js/api/DataSemanticsType',
    'viewmodel/js/api/context/BookmarkData',
    'viewmodel/js/api/context/ContextualData'
], function(
        module,
        AbstractRouter,
        RouterState,
        RouterStateObserver,
        DataSemanticsType,
        BookmarkData,
        ContextualData
        ) {

    'use strict';

    /**
     * RouterStateObserver implementation which handles the browser's
     * History API to behave propertly in combination with internal
     * navigation actions.
     *
     * @returns {HistoryApiRouterStateObserver}
     * @constructor
     * @private
     */
    var HistoryApiRouterStateObserver = function() {
        AbcsLib.checkThis(this);
        RouterStateObserver.apply(this, arguments);

        this._latestPageState;
    };
    AbcsLib.extend(HistoryApiRouterStateObserver, RouterStateObserver);

    HistoryApiRouterStateObserver._LOGGER = Logger.get(module.id);

    HistoryApiRouterStateObserver.prototype.ID = 'HistoryApiRouterStateObserver';

    HistoryApiRouterStateObserver.prototype.pushState = function(state) {
        // treat the superflous index.html
        if (document.location.pathname === '/index.html') {
            history.replaceState(null, null, '/');
            return;
        }

        this._latestPageState = state;
        var targetUrl = AbstractRouter.__treatPath(state.url);
        history.pushState(state, null, targetUrl);
        HistoryApiRouterStateObserver._LOGGER.debug('URL requested \'' + state.url + '\', resolved to \'' + targetUrl + '\'');
    };

    HistoryApiRouterStateObserver.prototype.updateState = function(state) {
        state = history.state ? RouterState.create(history.state).enhance(state) : state;
        try {
            history.replaceState(state, null, AbstractRouter.__treatPath(state.url));
        } catch (error) {
            this._updateContextualDataWithBookmark(state);
            history.replaceState(state, null, AbstractRouter.__treatPath(state.url));
            HistoryApiRouterStateObserver._LOGGER.warn('Error by saving full contextual data during page leave, switching to bookmark instead, pageID: ' + state.pageId + ', error:\n' + error);
        }
    };

    HistoryApiRouterStateObserver.prototype.beforeRestoreState = function(state, pageViewModel) {
        // The History API is not capable to change the previous state before switching to the new
        // one. Instead of that we are saving that last state into the temporary variable.
        // Also this recovers the state if the last state's URL equals to the new url and completed flag is set.
        if (state && this._latestPageState) {
            // store the last state - BCK/FWD navigation will not invoke page deactivation event
            if (state.url !== this._latestPageState.url && !this._latestPageState.complete) {
                this._completeLatestPageState(pageViewModel);
            } else if (state.url === this._latestPageState.url && !state.complete) {
                RouterState.enhanceSerialized(state, this._latestPageState);
                this.updateState(state);
                this._latestPageState = undefined;
            }
        }
    };

    HistoryApiRouterStateObserver.prototype._completeLatestPageState = function(pageViewModel) {
        if (!pageViewModel || !this._latestPageState) {
            return;
        }

        this._latestPageState.enhance({
            contextualData: pageViewModel.getContextualData() ? pageViewModel.getContextualData().toJSON() : undefined,
            bookmarkData: pageViewModel.getBookmark() ? pageViewModel.getBookmark().toJSON() : undefined,
            complete: true
        });
    };

    /**
     * Replaces the former contextual data with the bookmark data.
     * <p>
     * Removes the extra bookmark data from the state then.
     *
     * @param {Object} state serialized router state
     */
    HistoryApiRouterStateObserver.prototype._updateContextualDataWithBookmark = function(state) {
        if (state.contextualData) {
            var formerContextualData = ContextualData.fromJSON(state.contextualData);
            var bookmarkData = BookmarkData.fromJSON(state.bookmarkData);
            var bookmarkContextualData = new ContextualData(
                    bookmarkData,
                    DataSemanticsType.BOOKMARK,
                    formerContextualData.getNavigationContext());
            state.contextualData = bookmarkContextualData.toJSON();
            delete state.bookmarkData;
        }
    };

    return AbcsLib.initSingleton(HistoryApiRouterStateObserver);

});