UNPKG

@pi0/framework7

Version:

Full featured mobile HTML framework for building iOS & Android apps

496 lines (460 loc) 16.5 kB
import $ from 'dom7'; import Utils from '../../utils/utils'; import History from '../../utils/history'; function forward(el, forwardOptions = {}) { const router = this; const app = router.app; const view = router.view; const options = Utils.extend({ animate: router.params.animate, pushState: true, history: true, reloadCurrent: router.params.reloadPages, reloadPrevious: false, reloadAll: false, pageEvents: {}, on: {}, }, forwardOptions); const dynamicNavbar = router.dynamicNavbar; const separateNavbar = router.separateNavbar; const $viewEl = router.$el; const $newPage = $(el); const reload = options.reloadPrevious || options.reloadCurrent || options.reloadAll; let $oldPage; let $navbarEl; let $newNavbarInner; let $oldNavbarInner; if ($newPage.length) { // Remove theme elements router.removeThemeElements($newPage); } 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; } } } router.allowPageChange = false; if ($newPage.length === 0) { router.allowPageChange = true; return router; } // Pages In View const $pagesInView = $viewEl .children('.page:not(.stacked)') .filter((index, pageInView) => pageInView !== $newPage[0]); // Navbars In View let $navbarsInView; if (separateNavbar) { $navbarsInView = $navbarEl .children('.navbar-inner:not(.stacked)') .filter((index, navbarInView) => navbarInView !== $newNavbarInner[0]); } // Exit when reload previous and only 1 page in view so nothing ro reload if (options.reloadPrevious && $pagesInView.length < 2) { router.allowPageChange = true; return router; } // New Page let newPagePosition = 'next'; if (options.reloadCurrent || options.reloadAll) { newPagePosition = 'current'; } else if (options.reloadPrevious) { newPagePosition = 'previous'; } $newPage .addClass(`page-${newPagePosition}`) .removeClass('stacked'); if (dynamicNavbar && $newNavbarInner.length) { $newNavbarInner .addClass(`navbar-${newPagePosition}`) .removeClass('stacked'); } // Find Old Page if (options.reloadCurrent) { $oldPage = $pagesInView.eq($pagesInView.length - 1); if (separateNavbar) { // $oldNavbarInner = $navbarsInView.eq($pagesInView.length - 1); $oldNavbarInner = app.navbar.getElByPage($oldPage); } } else if (options.reloadPrevious) { $oldPage = $pagesInView.eq($pagesInView.length - 2); if (separateNavbar) { // $oldNavbarInner = $navbarsInView.eq($pagesInView.length - 2); $oldNavbarInner = app.navbar.getElByPage($oldPage); } } else if (options.reloadAll) { $oldPage = $pagesInView.filter((index, pageEl) => pageEl !== $newPage[0]); if (separateNavbar) { $oldNavbarInner = $navbarsInView.filter((index, navbarEl) => navbarEl !== $newNavbarInner[0]); } } else { if ($pagesInView.length > 1) { let i = 0; for (i = 0; i < $pagesInView.length - 1; i += 1) { const oldNavbarInnerEl = app.navbar.getElByPage($pagesInView.eq(i)); if (router.params.stackPages) { $pagesInView.eq(i).addClass('stacked'); if (separateNavbar) { // $navbarsInView.eq(i).addClass('stacked'); $(oldNavbarInnerEl).addClass('stacked'); } } else { // Page remove event router.pageCallback('beforeRemove', $pagesInView[i], $navbarsInView && $navbarsInView[i], 'previous', undefined, options); router.removePage($pagesInView[i]); if (separateNavbar && oldNavbarInnerEl) { router.removeNavbar(oldNavbarInnerEl); } } } } $oldPage = $viewEl .children('.page:not(.stacked)') .filter((index, page) => page !== $newPage[0]); if (separateNavbar) { $oldNavbarInner = $navbarEl .children('.navbar-inner:not(.stacked)') .filter((index, navbarInner) => navbarInner !== $newNavbarInner[0]); } } if (dynamicNavbar && !separateNavbar) { $oldNavbarInner = $oldPage.children('.navbar').children('.navbar-inner'); } // Push State if (router.params.pushState && options.pushState && !options.reloadPrevious) { const pushStateRoot = router.params.pushStateRoot || ''; History[options.reloadCurrent || options.reloadAll ? 'replace' : 'push']( { url: options.route.url, viewIndex: view.index, }, pushStateRoot + router.params.pushStateSeparator + options.route.url ); } // Current Route router.currentRoute = options.route; // Update router history const url = options.route.url; if (options.history) { if (options.reloadCurrent && router.history.length > 0) { router.history[router.history.length - (options.reloadPrevious ? 2 : 1)] = url; } else if (options.reloadAll) { router.history = [url]; } else { router.history.push(url); } } router.saveHistory(); // Insert new page and navbar const newPageInDom = $newPage.parents(document).length > 0; const f7Component = $newPage[0].f7Component; if (options.reloadPrevious) { if (f7Component && !newPageInDom) { f7Component.mount((componentEl) => { $(componentEl).insertBefore($oldPage); }); } else { $newPage.insertBefore($oldPage); } if (separateNavbar && $newNavbarInner.length) { if ($oldNavbarInner.length) { $newNavbarInner.insertBefore($oldNavbarInner); } else { $navbarEl.append($newNavbarInner); } } } else { if ($oldPage.next('.page')[0] !== $newPage[0]) { if (f7Component && !newPageInDom) { f7Component.mount((componentEl) => { $viewEl.append(componentEl); }); } else { $viewEl.append($newPage[0]); } } if (separateNavbar && $newNavbarInner.length) { $navbarEl.append($newNavbarInner[0]); } } if (!newPageInDom) { router.pageCallback('mounted', $newPage, $newNavbarInner, newPagePosition, reload ? newPagePosition : 'current', options, $oldPage); } // Remove old page if (options.reloadCurrent && $oldPage.length > 0) { if (router.params.stackPages && router.initialPages.indexOf($oldPage[0]) >= 0) { $oldPage.addClass('stacked'); if (separateNavbar) { $oldNavbarInner.addClass('stacked'); } } else { // Page remove event router.pageCallback('beforeRemove', $oldPage, $newNavbarInner, 'previous', undefined, options); router.removePage($oldPage); if (separateNavbar && $oldNavbarInner.length) { router.removeNavbar($oldNavbarInner); } } } else if (options.reloadAll) { $oldPage.each((index, pageEl) => { const $oldPageEl = $(pageEl); const $oldNavbarInnerEl = $(app.navbar.getElByPage($oldPageEl)); if (router.params.stackPages && router.initialPages.indexOf($oldPageEl[0]) >= 0) { $oldPageEl.addClass('stacked'); if (separateNavbar) { $oldNavbarInnerEl.addClass('stacked'); } } else { // Page remove event router.pageCallback('beforeRemove', $oldPageEl, $oldNavbarInner && $oldNavbarInner.eq(index), 'previous', undefined, options); router.removePage($oldPageEl); if (separateNavbar && $oldNavbarInnerEl.length) { router.removeNavbar($oldNavbarInnerEl); } } }); } // 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, newPagePosition, reload ? newPagePosition : 'current', options, $oldPage); if (options.reloadCurrent || options.reloadAll) { router.allowPageChange = true; router.pageCallback('beforeIn', $newPage, $newNavbarInner, newPagePosition, 'current', options); router.pageCallback('afterIn', $newPage, $newNavbarInner, newPagePosition, 'current', options); return router; } // Before animation event router.pageCallback('beforeIn', $newPage, $newNavbarInner, 'next', 'current', options); router.pageCallback('beforeOut', $oldPage, $oldNavbarInner, 'current', 'previous', options); // Animation function afterAnimation() { 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-previous'); if (dynamicNavbar) { $newNavbarInner.removeClass(navbarClasses).addClass('navbar-current'); $oldNavbarInner.removeClass(navbarClasses).addClass('navbar-previous'); } // After animation event router.allowPageChange = true; router.pageCallback('afterIn', $newPage, $newNavbarInner, 'next', 'current', options); router.pageCallback('afterOut', $oldPage, $oldNavbarInner, 'current', 'previous', options); let keepOldPage = app.theme === 'ios' ? (router.params.preloadPreviousPage || router.params.iosSwipeBack) : router.params.preloadPreviousPage; if (!keepOldPage) { if ($newPage.hasClass('smart-select-page') || $newPage.hasClass('photo-browser-page') || $newPage.hasClass('autocomplete-page')) { keepOldPage = true; } } if (!keepOldPage) { if (router.params.stackPages) { $oldPage.addClass('stacked'); if (separateNavbar) { $oldNavbarInner.addClass('stacked'); } } else if (!($newPage.attr('data-name') && $newPage.attr('data-name') === 'smart-select-page')) { // Remove event router.pageCallback('beforeRemove', $oldPage, $oldNavbarInner, 'previous', undefined, options); router.removePage($oldPage); if (separateNavbar && $oldNavbarInner.length) { router.removeNavbar($oldNavbarInner); } } } router.emit('routeChanged', router.currentRoute, router.previousRoute, router); 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-next'); if (dynamicNavbar) { $oldNavbarInner.removeClass(navbarClasses).addClass('navbar-current'); $newNavbarInner.removeClass(navbarClasses).addClass('navbar-next'); } } if (options.animate) { if (router.app.theme === 'md' && router.params.materialPageLoadDelay) { setTimeout(() => { setPositionClasses(); router.animate($oldPage, $newPage, $oldNavbarInner, $newNavbarInner, 'forward', () => { afterAnimation(); }); }, router.params.materialPageLoadDelay); } else { setPositionClasses(); router.animate($oldPage, $newPage, $oldNavbarInner, $newNavbarInner, 'forward', () => { afterAnimation(); }); } } else { afterAnimation(); } return router; } function load(loadParams = {}, loadOptions = {}, ignorePageChange) { const router = this; if (!router.allowPageChange && !ignorePageChange) return router; const params = loadParams; const options = loadOptions; const { url, content, el, name, template, templateUrl, component, componentUrl } = params; const { ignoreCache } = options; if (options.route && options.route.route && options.route.route.parentPath && router.currentRoute.route && router.currentRoute.route.parentPath === options.route.route.parentPath) { // Do something nested if (options.route.url === router.url) return false; if (options.route.route.tab) { return router.tabLoad(options.route.route.tab, options); } return false; } if ( options.route && 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); Utils.extend(options.route, { route: { url, path: url } }); } // Component Callbacks function resolve(pageEl, newOptions) { return router.forward(pageEl, Utils.extend(options, newOptions)); } function reject() { router.allowPageChange = true; return router; } // Proceed if (content) { router.forward(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.forward(router.getPageEl(el), options); } else if (name) { // Load page by page name in pages container router.forward(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.forward(router.getPageEl(pageContent), options); }) .catch(() => { router.allowPageChange = true; }); } return router; } function navigate(url, navigateOptions = {}) { const router = this; const app = router.app; if (!router.view) { app.views.main.router.navigate(url, navigateOptions); return router; } if (url === '#' || url === '') { return router; } let navigateUrl = url.replace('./', ''); if (navigateUrl[0] !== '/' && navigateUrl.indexOf('#') !== 0) { const currentPath = router.currentRoute.route.parentPath || router.currentRoute.path; navigateUrl = ((currentPath || '/') + navigateUrl).replace('//', '/'); } let route; if (navigateOptions.createRoute) { route = Utils.extend(router.parseUrl(navigateUrl), { route: Utils.extend({}, navigateOptions.createRoute), }); } else { route = router.findMatchingRoute(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 }); } ('popup popover sheet loginScreen actions customModal').split(' ').forEach((modalLoadProp) => { if (route.route[modalLoadProp]) { router.modalLoad(modalLoadProp, route, options); } }); ('url content component name el componentUrl template templateUrl').split(' ').forEach((pageLoadProp) => { if (route.route[pageLoadProp]) { router.load({ [pageLoadProp]: route.route[pageLoadProp] }, options); } }); // Async function asyncResolve(resolveParams, resolveOptions) { router.allowPageChange = false; let resolvedAsModal = false; ('popup popover sheet loginScreen actions customModal').split(' ').forEach((modalLoadProp) => { if (resolveParams[modalLoadProp]) { resolvedAsModal = true; const modalRoute = Utils.extend({}, route, { route: resolveParams }); router.allowPageChange = true; router.modalLoad(modalLoadProp, modalRoute, Utils.extend(options, resolveOptions)); } }); if (resolvedAsModal) return; router.load(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); } // Retur Router return router; } export { forward, load, navigate };