@pi0/framework7
Version:
Full featured mobile HTML framework for building iOS & Android apps
489 lines (444 loc) • 16.1 kB
JavaScript
import $ from 'dom7';
import Utils from '../../utils/utils';
import History from '../../utils/history';
function backward(el, backwardOptions) {
const router = this;
const app = router.app;
const view = router.view;
const options = Utils.extend({
animate: router.params.animate,
pushState: true,
}, backwardOptions);
const dynamicNavbar = router.dynamicNavbar;
const separateNavbar = router.separateNavbar;
const $newPage = $(el);
const $oldPage = router.$el.children('.page-current');
if ($newPage.length) {
// Remove theme elements
router.removeThemeElements($newPage);
}
let $navbarEl;
let $newNavbarInner;
let $oldNavbarInner;
if (dynamicNavbar) {
$newNavbarInner = $newPage.children('.navbar').children('.navbar-inner');
if (separateNavbar) {
$navbarEl = router.$navbarEl;
if ($newNavbarInner.length > 0) {
$newPage.children('.navbar').remove();
}
if ($newNavbarInner.length === 0 && $newPage[0].f7Page) {
// Try from pageData
$newNavbarInner = $newPage[0].f7Page.$navbarEl;
}
$oldNavbarInner = $navbarEl.find('.navbar-current');
} else {
$oldNavbarInner = $oldPage.children('.navbar').children('.navbar-inner');
}
}
router.allowPageChange = false;
if ($newPage.length === 0 || $oldPage.length === 0) {
router.allowPageChange = true;
return router;
}
// Remove theme elements
router.removeThemeElements($newPage);
// New Page
$newPage
.addClass('page-previous')
.removeClass('stacked');
if (dynamicNavbar && $newNavbarInner.length > 0) {
$newNavbarInner
.addClass('navbar-previous')
.removeClass('stacked');
}
// Remove previous page in case of "forced"
let backIndex;
if (options.force) {
if ($oldPage.prev('.page-previous:not(.stacked)').length > 0 || $oldPage.prev('.page-previous').length === 0) {
if (router.history.indexOf(options.route.url) >= 0) {
backIndex = router.history.length - router.history.indexOf(options.route.url) - 1;
router.history = router.history.slice(0, router.history.indexOf(options.route.url) + 2);
view.history = router.history;
} else if (router.history[[router.history.length - 2]]) {
router.history[router.history.length - 2] = options.route.url;
} else {
router.history.unshift(router.url);
}
if (backIndex && router.params.stackPages) {
$oldPage.prevAll('.page-previous').each((index, pageToRemove) => {
const $pageToRemove = $(pageToRemove);
let $navbarToRemove;
if (separateNavbar) {
// $navbarToRemove = $oldNavbarInner.prevAll('.navbar-previous').eq(index);
$navbarToRemove = $(app.navbar.getElByPage($pageToRemove));
}
if ($pageToRemove[0] !== $newPage[0] && $pageToRemove.index() > $newPage.index()) {
if (router.initialPages.indexOf($pageToRemove[0]) >= 0) {
$pageToRemove.addClass('stacked');
if (separateNavbar) {
$navbarToRemove.addClass('stacked');
}
} else {
router.pageCallback('beforeRemove', $pageToRemove, $navbarToRemove, 'previous', undefined, options);
router.removePage($pageToRemove);
if (separateNavbar && $navbarToRemove.length > 0) {
router.removeNavbar($navbarToRemove);
}
}
}
});
} else {
const $pageToRemove = $oldPage.prev('.page-previous:not(.stacked)');
let $navbarToRemove;
if (separateNavbar) {
// $navbarToRemove = $oldNavbarInner.prev('.navbar-inner:not(.stacked)');
$navbarToRemove = $(app.navbar.getElByPage($pageToRemove));
}
if (router.params.stackPages && router.initialPages.indexOf($pageToRemove[0]) >= 0) {
$pageToRemove.addClass('stacked');
$navbarToRemove.addClass('stacked');
} else if ($pageToRemove.length > 0) {
router.pageCallback('beforeRemove', $pageToRemove, $navbarToRemove, 'previous', undefined, options);
router.removePage($pageToRemove);
if (separateNavbar && $navbarToRemove.length) {
router.removeNavbar($navbarToRemove);
}
}
}
}
}
// Insert new page
const newPageInDom = $newPage.parents(document).length > 0;
const f7Component = $newPage[0].f7Component;
function insertPage() {
if ($newPage.next($oldPage).length === 0) {
if (!newPageInDom && f7Component) {
f7Component.mount((componentEl) => {
$(componentEl).insertBefore($oldPage);
});
} else {
$newPage.insertBefore($oldPage);
}
}
if (separateNavbar && $newNavbarInner.length) {
$newNavbarInner.insertBefore($oldNavbarInner);
if ($oldNavbarInner.length > 0) {
$newNavbarInner.insertBefore($oldNavbarInner);
} else {
$navbarEl.append($newNavbarInner);
}
}
if (!newPageInDom) {
router.pageCallback('mounted', $newPage, $newNavbarInner, 'previous', 'current', options, $oldPage);
}
}
if (options.preload) {
// Insert Page
insertPage();
// Page init and before init events
router.pageCallback('init', $newPage, $newNavbarInner, 'previous', 'current', options, $oldPage);
if ($newPage.prevAll('.page-previous:not(.stacked)').length > 0) {
$newPage.prevAll('.page-previous:not(.stacked)').each((index, pageToRemove) => {
const $pageToRemove = $(pageToRemove);
let $navbarToRemove;
if (separateNavbar) {
// $navbarToRemove = $newNavbarInner.prevAll('.navbar-previous:not(.stacked)').eq(index);
$navbarToRemove = $(app.navbar.getElByPage($pageToRemove));
}
if (router.params.stackPages && router.initialPages.indexOf(pageToRemove) >= 0) {
$pageToRemove.addClass('stacked');
if (separateNavbar) {
$navbarToRemove.addClass('stacked');
}
} else {
router.pageCallback('beforeRemove', $pageToRemove, $navbarToRemove, 'previous', undefined);
router.removePage($pageToRemove);
if (separateNavbar && $navbarToRemove.length) {
router.removeNavbar($navbarToRemove);
}
}
});
}
router.allowPageChange = true;
return router;
}
// History State
if (router.params.pushState && options.pushState) {
if (backIndex) History.go(-backIndex);
else History.back();
}
// Update History
if (router.history.length === 1) {
router.history.unshift(router.url);
}
router.history.pop();
router.saveHistory();
// Current Route
router.currentRoute = options.route;
// Insert Page
insertPage();
// Load Tab
if (options.route.route.tab) {
router.tabLoad(options.route.route.tab, Utils.extend({}, options, {
history: false,
pushState: false,
}));
}
// Page init and before init events
router.pageCallback('init', $newPage, $newNavbarInner, 'previous', 'current', options, $oldPage);
// Before animation callback
router.pageCallback('beforeIn', $newPage, $newNavbarInner, 'previous', 'current', options);
router.pageCallback('beforeOut', $oldPage, $oldNavbarInner, 'current', 'next', options);
// Animation
function afterAnimation() {
// Set classes
const pageClasses = 'page-previous page-current page-next';
const navbarClasses = 'navbar-previous navbar-current navbar-next';
$newPage.removeClass(pageClasses).addClass('page-current');
$oldPage.removeClass(pageClasses).addClass('page-next');
if (dynamicNavbar) {
$newNavbarInner.removeClass(navbarClasses).addClass('navbar-current');
$oldNavbarInner.removeClass(navbarClasses).addClass('navbar-next');
}
// After animation event
router.pageCallback('afterIn', $newPage, $newNavbarInner, 'previous', 'current', options);
router.pageCallback('afterOut', $oldPage, $oldNavbarInner, 'current', 'next', options);
// Remove Old Page
if (router.params.stackPages && router.initialPages.indexOf($oldPage[0]) >= 0) {
$oldPage.addClass('stacked');
if (separateNavbar) {
$oldNavbarInner.addClass('stacked');
}
} else {
router.pageCallback('beforeRemove', $oldPage, $oldNavbarInner, 'next', undefined, options);
router.removePage($oldPage);
if (separateNavbar && $oldNavbarInner.length) {
router.removeNavbar($oldNavbarInner);
}
}
router.allowPageChange = true;
router.emit('routeChanged', router.currentRoute, router.previousRoute, router);
// Preload previous page
const preloadPreviousPage = app.theme === 'ios' ? (router.params.preloadPreviousPage || router.params.iosSwipeBack) : router.params.preloadPreviousPage;
if (preloadPreviousPage) {
router.back(router.history[router.history.length - 2], { preload: true });
}
if (router.params.pushState) {
History.clearRouterQueue();
}
}
function setPositionClasses() {
const pageClasses = 'page-previous page-current page-next';
const navbarClasses = 'navbar-previous navbar-current navbar-next';
$oldPage.removeClass(pageClasses).addClass('page-current');
$newPage.removeClass(pageClasses).addClass('page-previous');
if (dynamicNavbar) {
$oldNavbarInner.removeClass(navbarClasses).addClass('navbar-current');
$newNavbarInner.removeClass(navbarClasses).addClass('navbar-previous');
}
}
if (options.animate) {
setPositionClasses();
router.animate($oldPage, $newPage, $oldNavbarInner, $newNavbarInner, 'backward', () => {
afterAnimation();
});
} else {
afterAnimation();
}
return router;
}
function loadBack(backParams, backOptions, ignorePageChange) {
const router = this;
if (!router.allowPageChange && !ignorePageChange) return router;
const params = backParams;
const options = backOptions;
const { url, content, el, name, template, templateUrl, component, componentUrl } = params;
const { ignoreCache } = options;
if (
options.route.url &&
router.url === options.route.url &&
!(options.reloadCurrent || options.reloadPrevious) &&
!router.params.allowDuplicateUrls
) {
return false;
}
if (!options.route && url) {
options.route = router.parseUrl(url);
}
// Component Callbacks
function resolve(pageEl, newOptions) {
return router.backward(pageEl, Utils.extend(options, newOptions));
}
function reject() {
router.allowPageChange = true;
return router;
}
// Proceed
if (content) {
router.backward(router.getPageEl(content), options);
} else if (template || templateUrl) {
// Parse template and send page element
try {
router.pageTemplateLoader(template, templateUrl, options, resolve, reject);
} catch (err) {
router.allowPageChange = true;
throw err;
}
} else if (el) {
// Load page from specified HTMLElement or by page name in pages container
router.backward(router.getPageEl(el), options);
} else if (name) {
// Load page by page name in pages container
router.backward(router.$el.children(`.page[data-name="${name}"]`).eq(0), options);
} else if (component || componentUrl) {
// Load from component (F7/Vue/React/...)
try {
router.pageComponentLoader(router.el, component, componentUrl, options, resolve, reject);
} catch (err) {
router.allowPageChange = true;
throw err;
}
} else if (url) {
// Load using XHR
if (router.xhr) {
router.xhr.abort();
router.xhr = false;
}
router.xhrRequest(url, ignoreCache)
.then((pageContent) => {
router.backward(router.getPageEl(pageContent), options);
})
.catch(() => {
router.allowPageChange = true;
});
}
return router;
}
function back(...args) {
let navigateUrl;
let navigateOptions;
if (typeof args[0] === 'object') {
navigateOptions = args[0] || {};
} else {
navigateUrl = args[0];
navigateOptions = args[1] || {};
}
const router = this;
const app = router.app;
if (!router.view) {
app.views.main.router.back(navigateUrl, navigateOptions);
return router;
}
let currentRouteIsModal = router.currentRoute.modal;
let modalType;
if (!currentRouteIsModal) {
('popup popover sheet loginScreen actions customModal').split(' ').forEach((modalLoadProp) => {
if (router.currentRoute.route[modalLoadProp]) {
currentRouteIsModal = true;
modalType = modalLoadProp;
}
});
}
if (currentRouteIsModal) {
const modalToClose = router.currentRoute.modal ||
router.currentRoute.route.modalInstance ||
app[modalType].get();
const previousUrl = router.history[router.history.length - 2];
let previousRoute = router.findMatchingRoute(previousUrl);
if (!previousRoute && previousUrl) {
previousRoute = {
url: previousUrl,
path: previousUrl.split('?')[0],
query: Utils.parseUrlQuery(previousUrl),
route: {
path: previousUrl.split('?')[0],
url: previousUrl,
},
};
}
if (!previousRoute || !modalToClose) {
return router;
}
if (router.params.pushState && navigateOptions.pushState !== false) {
History.back();
}
router.currentRoute = previousRoute;
router.history.pop();
router.saveHistory();
router.modalRemove(modalToClose);
return router;
}
const $previousPage = router.$el.children('.page-current').prevAll('.page-previous').eq(0);
if (!navigateOptions.force && $previousPage.length > 0) {
if (router.params.pushState && $previousPage[0].f7Page && router.history[router.history.length - 2] !== $previousPage[0].f7Page.route.url) {
router.back(router.history[router.history.length - 2], Utils.extend(navigateOptions, { force: true }));
return router;
}
router.loadBack({ el: $previousPage }, Utils.extend(navigateOptions, {
route: $previousPage[0].f7Page.route,
}));
return router;
}
// Navigate URL
if (navigateUrl === '#') {
navigateUrl = undefined;
}
if (navigateUrl && navigateUrl[0] !== '/' && navigateUrl.indexOf('#') !== 0) {
navigateUrl = ((router.path || '/') + navigateUrl).replace('//', '/');
}
if (!navigateUrl && router.history.length > 1) {
navigateUrl = router.history[router.history.length - 2];
}
// Find route to load
let route = router.findMatchingRoute(navigateUrl);
if (!route) {
if (navigateUrl) {
route = {
url: navigateUrl,
path: navigateUrl.split('?')[0],
query: Utils.parseUrlQuery(navigateUrl),
route: {
path: navigateUrl.split('?')[0],
url: navigateUrl,
},
};
}
}
if (!route) {
return router;
}
const options = {};
if (route.route.options) {
Utils.extend(options, route.route.options, navigateOptions, { route });
} else {
Utils.extend(options, navigateOptions, { route });
}
if (options.force && router.params.stackPages) {
router.$el.children('.page-previous.stacked').each((index, pageEl) => {
if (pageEl.f7Page && pageEl.f7Page.route && pageEl.f7Page.route.url === route.url) {
router.loadBack({ el: pageEl }, options);
}
});
}
('url content component name el componentUrl template templateUrl').split(' ').forEach((pageLoadProp) => {
if (route.route[pageLoadProp]) {
router.loadBack({ [pageLoadProp]: route.route[pageLoadProp] }, options);
}
});
// Async
function asyncResolve(resolveParams, resolveOptions) {
router.allowPageChange = false;
router.loadBack(resolveParams, Utils.extend(options, resolveOptions), true);
}
function asyncReject() {
router.allowPageChange = true;
}
if (route.route.async) {
router.allowPageChange = false;
route.route.async.call(router, asyncResolve, asyncReject);
}
// Return Router
return router;
}
export { backward, loadBack, back };