/** * Framework7 1.5.0 * Full featured mobile HTML framework for building iOS & Android apps * * http://framework7.io/ * * Copyright 2016, Vladimir Kharlampidi * The iDangero.us * http://www.idangero.us/ * * Licensed under MIT * * Released on: November 8, 2016 */ (function () { 'use strict'; /*=========================== Framework 7 ===========================*/ window.Framework7 = function (params) { // App var app = this; // Version app.version = '1.5.0'; // Default Parameters app.params = { // root: 'body', // cache: true, cacheIgnore: [], cacheIgnoreGetParameters: false, cacheDuration: 1000 * 60 * 10, // Ten minutes preloadPreviousPage: true, uniqueHistory: false, uniqueHistoryIgnoreGetParameters: false, dynamicPageUrl: 'content-{{index}}', allowDuplicateUrls: false, router: true, routerRemoveTimeout: false, routerRemoveWithTimeout: false, // Push State pushState: false, pushStateRoot: undefined, pushStateNoAnimation: false, pushStateSeparator: '#!/', pushStateOnLoad: true, // Fast clicks fastClicks: true, fastClicksDistanceThreshold: 10, fastClicksDelayBetweenClicks: 50, fastClicksExclude: '', // CSS selector // Tap Hold tapHold: false, tapHoldDelay: 750, tapHoldPreventClicks: true, // Active State activeState: true, activeStateElements: 'a, button, label, span', // Animate Nav Back Icon animateNavBackIcon: false, // Swipe Back swipeBackPage: true, swipeBackPageThreshold: 0, swipeBackPageActiveArea: 30, swipeBackPageAnimateShadow: true, swipeBackPageAnimateOpacity: true, // Ajax ajaxLinks: undefined, // or CSS selector // External Links externalLinks: '.external', // CSS selector // Sortable sortable: true, // Scroll toolbars hideNavbarOnPageScroll: false, hideToolbarOnPageScroll: false, hideTabbarOnPageScroll: false, showBarsOnPageScrollEnd: true, showBarsOnPageScrollTop: true, // Swipeout swipeout: true, swipeoutActionsNoFold: false, swipeoutNoFollow: false, swipeoutRemoveWithTimeout: false, // Smart Select Back link template smartSelectOpenIn: 'page', // or 'popup' or 'picker' smartSelectBackText: 'Back', smartSelectPopupCloseText: 'Close', smartSelectPickerCloseText: 'Done', smartSelectSearchbar: false, smartSelectBackOnSelect: false, // Tap Navbar or Statusbar to scroll to top scrollTopOnNavbarClick: false, scrollTopOnStatusbarClick: false, // Panels swipePanel: false, // or 'left' or 'right' swipePanelActiveArea: 0, swipePanelCloseOpposite: true, swipePanelOnlyClose: false, swipePanelNoFollow: false, swipePanelThreshold: 0, panelsCloseByOutside: true, // Modals modalButtonOk: 'OK', modalButtonCancel: 'Cancel', modalUsernamePlaceholder: 'Username', modalPasswordPlaceholder: 'Password', modalTitle: 'Framework7', modalCloseByOutside: false, actionsCloseByOutside: true, popupCloseByOutside: true, modalPreloaderTitle: 'Loading... ', modalStack: true, // Lazy Load imagesLazyLoadThreshold: 0, imagesLazyLoadSequential: true, // Name space viewClass: 'view', viewMainClass: 'view-main', viewsClass: 'views', // Notifications defaults notificationCloseOnClick: false, notificationCloseIcon: true, notificationCloseButtonText: 'Close', // Animate Pages animatePages: true, // Template7 templates: {}, template7Data: {}, template7Pages: false, precompileTemplates: false, // Material material: false, materialPageLoadDelay: 0, materialPreloaderSvg: '', materialPreloaderHtml: '' + '' + '' + '' + '' + '' + '' + '' + '', materialRipple: true, materialRippleElements: '.ripple, a.link, a.item-link, .button, .modal-button, .tab-link, .label-radio, .label-checkbox, .actions-modal-button, a.searchbar-clear, a.floating-button, .floating-button > a, .speed-dial-buttons a', // Auto init init: true, }; // Extend defaults with parameters for (var param in params) { app.params[param] = params[param]; } // DOM lib var $ = Dom7; // Template7 lib var t7 = Template7; app._compiledTemplates = {}; // App Root app.root = $(app.params.root); app.root.eq(0).addClass('framework7-root'); // Touch events app.touchEvents = { start: app.support.touch ? 'touchstart' : 'mousedown', move: app.support.touch ? 'touchmove' : 'mousemove', end: app.support.touch ? 'touchend' : 'mouseup' }; // Link to local storage app.ls = window.localStorage; // RTL app.rtl = $('body').css('direction') === 'rtl'; if (app.rtl) $('html').attr('dir', 'rtl'); // Overwrite statusbar overlay if (typeof app.params.statusbarOverlay !== 'undefined') { if (app.params.statusbarOverlay) $('html').addClass('with-statusbar-overlay'); else $('html').removeClass('with-statusbar-overlay'); } /*====================================================== ************ Views ************ ======================================================*/ app.views = []; var View = function (selector, params) { var defaults = { dynamicNavbar: false, domCache: false, linksView: undefined, reloadPages: false, uniqueHistory: app.params.uniqueHistory, uniqueHistoryIgnoreGetParameters: app.params.uniqueHistoryIgnoreGetParameters, allowDuplicateUrls: app.params.allowDuplicateUrls, swipeBackPage: app.params.swipeBackPage, swipeBackPageAnimateShadow: app.params.swipeBackPageAnimateShadow, swipeBackPageAnimateOpacity: app.params.swipeBackPageAnimateOpacity, swipeBackPageActiveArea: app.params.swipeBackPageActiveArea, swipeBackPageThreshold: app.params.swipeBackPageThreshold, animatePages: app.params.animatePages, preloadPreviousPage: app.params.preloadPreviousPage }; var i; // Params params = params || {}; // Disable dynamic navbar for material theme if (params.dynamicNavbar && app.params.material) params.dynamicNavbar = false; // Extend params with defaults for (var def in defaults) { if (typeof params[def] === 'undefined') { params[def] = defaults[def]; } } // View var view = this; view.params = params; // Selector view.selector = selector; // Container var container = $(selector); view.container = container[0]; // Fix Selector if (typeof selector !== 'string') { // Supposed to be HTMLElement or Dom7 selector = (container.attr('id') ? '#' + container.attr('id') : '') + (container.attr('class') ? '.' + container.attr('class').replace(/ /g, '.').replace('.active', '') : ''); view.selector = selector; } // Is main view.main = container.hasClass(app.params.viewMainClass); // Content cache view.contentCache = {}; // Context cache view.contextCache = {}; // Pages cache view.pagesCache = {}; view.pageElementsCache = {}; // Store View in element for easy access container[0].f7View = view; // Pages view.pagesContainer = container.find('.pages')[0]; view.initialPages = []; view.initialPagesUrl = []; view.initialNavbars = []; if (view.params.domCache) { var initialPages = container.find('.page'); for (i = 0; i < initialPages.length; i++) { view.initialPages.push(initialPages[i]); view.initialPagesUrl.push('#' + initialPages.eq(i).attr('data-page')); } if (view.params.dynamicNavbar) { var initialNavbars = container.find('.navbar-inner'); for (i = 0; i < initialNavbars.length; i++) { view.initialNavbars.push(initialNavbars[i]); } } } view.allowPageChange = true; // Location var docLocation = document.location.href; // History view.history = []; var viewURL = docLocation; var pushStateSeparator = app.params.pushStateSeparator; var pushStateRoot = app.params.pushStateRoot; if (app.params.pushState && view.main) { if (pushStateRoot) { viewURL = pushStateRoot; } else { if (pushStateSeparator && viewURL.indexOf(pushStateSeparator) >= 0 && viewURL.indexOf(pushStateSeparator + '#') < 0) viewURL = viewURL.split(pushStateSeparator)[0]; } } // Active Page var currentPage, currentPageData; if (!view.activePage) { currentPage = $(view.pagesContainer).find('.page-on-center'); if (currentPage.length === 0) { currentPage = $(view.pagesContainer).find('.page:not(.cached)'); currentPage = currentPage.eq(currentPage.length - 1); } if (currentPage.length > 0) { currentPageData = currentPage[0].f7PageData; } } // View startup URL if (view.params.domCache && currentPage) { view.url = container.attr('data-url') || view.params.url || '#' + currentPage.attr('data-page'); view.pagesCache[view.url] = currentPage.attr('data-page'); } else view.url = container.attr('data-url') || view.params.url || viewURL; // Update current page Data if (currentPageData) { currentPageData.view = view; currentPageData.url = view.url; if (view.params.domCache && view.params.dynamicNavbar && !currentPageData.navbarInnerContainer) { currentPageData.navbarInnerContainer = view.initialNavbars[view.initialPages.indexOf(currentPageData.container)]; } view.activePage = currentPageData; currentPage[0].f7PageData = currentPageData; } // Store to history main view's url if (view.url) { view.history.push(view.url); } // Touch events var isTouched = false, isMoved = false, touchesStart = {}, isScrolling, activePage = [], previousPage = [], viewContainerWidth, touchesDiff, allowViewTouchMove = true, touchStartTime, activeNavbar = [], previousNavbar = [], activeNavElements, previousNavElements, activeNavBackIcon, previousNavBackIcon, dynamicNavbar, pageShadow, el; view.handleTouchStart = function (e) { if (!allowViewTouchMove || !view.params.swipeBackPage || isTouched || app.swipeoutOpenedEl || !view.allowPageChange) return; isMoved = false; isTouched = true; isScrolling = undefined; touchesStart.x = e.type === 'touchstart' ? e.targetTouches[0].pageX : e.pageX; touchesStart.y = e.type === 'touchstart' ? e.targetTouches[0].pageY : e.pageY; touchStartTime = (new Date()).getTime(); dynamicNavbar = view.params.dynamicNavbar && container.find('.navbar-inner').length > 1; }; view.handleTouchMove = function (e) { if (!isTouched) return; var pageX = e.type === 'touchmove' ? e.targetTouches[0].pageX : e.pageX; var pageY = e.type === 'touchmove' ? e.targetTouches[0].pageY : e.pageY; if (typeof isScrolling === 'undefined') { isScrolling = !!(isScrolling || Math.abs(pageY - touchesStart.y) > Math.abs(pageX - touchesStart.x)); } if (isScrolling || e.f7PreventSwipeBack || app.preventSwipeBack) { isTouched = false; return; } if (!isMoved) { var cancel = false; // Calc values during first move fired viewContainerWidth = container.width(); var target = $(e.target); var swipeout = target.hasClass('swipeout') ? target : target.parents('.swipeout'); if (swipeout.length > 0) { if (!app.rtl && swipeout.find('.swipeout-actions-left').length > 0) cancel = true; if (app.rtl && swipeout.find('.swipeout-actions-right').length > 0) cancel = true; } activePage = target.is('.page') ? target : target.parents('.page'); if (activePage.hasClass('no-swipeback')) cancel = true; previousPage = container.find('.page-on-left:not(.cached)'); var notFromBorder = touchesStart.x - container.offset().left > view.params.swipeBackPageActiveArea; if (app.rtl) { notFromBorder = touchesStart.x < container.offset().left - container[0].scrollLeft + viewContainerWidth - view.params.swipeBackPageActiveArea; } else { notFromBorder = touchesStart.x - container.offset().left > view.params.swipeBackPageActiveArea; } if (notFromBorder) cancel = true; if (previousPage.length === 0 || activePage.length === 0) cancel = true; if (cancel) { isTouched = false; return; } if (view.params.swipeBackPageAnimateShadow && !app.device.android) { pageShadow = activePage.find('.swipeback-page-shadow'); if (pageShadow.length === 0) { pageShadow = $('
'); activePage.append(pageShadow); } } if (dynamicNavbar) { activeNavbar = container.find('.navbar-on-center:not(.cached)'); previousNavbar = container.find('.navbar-on-left:not(.cached)'); activeNavElements = activeNavbar.find('.left, .center, .right, .subnavbar, .fading'); previousNavElements = previousNavbar.find('.left, .center, .right, .subnavbar, .fading'); if (app.params.animateNavBackIcon) { activeNavBackIcon = activeNavbar.find('.left.sliding .back .icon'); previousNavBackIcon = previousNavbar.find('.left.sliding .back .icon'); } } // Close/Hide Any Picker if ($('.picker-modal.modal-in').length > 0) { app.closeModal($('.picker-modal.modal-in')); } } e.f7PreventPanelSwipe = true; isMoved = true; e.preventDefault(); // RTL inverter var inverter = app.rtl ? -1 : 1; // Touches diff touchesDiff = (pageX - touchesStart.x - view.params.swipeBackPageThreshold) * inverter; if (touchesDiff < 0) touchesDiff = 0; var percentage = touchesDiff / viewContainerWidth; // Swipe Back Callback var callbackData = { percentage: percentage, activePage: activePage[0], previousPage: previousPage[0], activeNavbar: activeNavbar[0], previousNavbar: previousNavbar[0] }; if (view.params.onSwipeBackMove) { view.params.onSwipeBackMove(callbackData); } container.trigger('swipeBackMove', callbackData); // Transform pages var activePageTranslate = touchesDiff * inverter; var previousPageTranslate = (touchesDiff / 5 - viewContainerWidth / 5) * inverter; if (app.device.pixelRatio === 1) { activePageTranslate = Math.round(activePageTranslate); previousPageTranslate = Math.round(previousPageTranslate); } activePage.transform('translate3d(' + activePageTranslate + 'px,0,0)'); if (view.params.swipeBackPageAnimateShadow && !app.device.android) pageShadow[0].style.opacity = 1 - 1 * percentage; previousPage.transform('translate3d(' + previousPageTranslate + 'px,0,0)'); if (view.params.swipeBackPageAnimateOpacity) previousPage[0].style.opacity = 0.9 + 0.1 * percentage; // Dynamic Navbars Animation if (dynamicNavbar) { var i; for (i = 0; i < activeNavElements.length; i++) { el = $(activeNavElements[i]); if (!el.is('.subnavbar.sliding')) el[0].style.opacity = (1 - percentage * 1.3); if (el[0].className.indexOf('sliding') >= 0) { var activeNavTranslate = percentage * el[0].f7NavbarRightOffset; if (app.device.pixelRatio === 1) activeNavTranslate = Math.round(activeNavTranslate); el.transform('translate3d(' + activeNavTranslate + 'px,0,0)'); if (app.params.animateNavBackIcon) { if (el[0].className.indexOf('left') >= 0 && activeNavBackIcon.length > 0) { activeNavBackIcon.transform('translate3d(' + -activeNavTranslate + 'px,0,0)'); } } } } for (i = 0; i < previousNavElements.length; i++) { el = $(previousNavElements[i]); if (!el.is('.subnavbar.sliding')) el[0].style.opacity = percentage * 1.3 - 0.3; if (el[0].className.indexOf('sliding') >= 0) { var previousNavTranslate = el[0].f7NavbarLeftOffset * (1 - percentage); if (app.device.pixelRatio === 1) previousNavTranslate = Math.round(previousNavTranslate); el.transform('translate3d(' + previousNavTranslate + 'px,0,0)'); if (app.params.animateNavBackIcon) { if (el[0].className.indexOf('left') >= 0 && previousNavBackIcon.length > 0) { previousNavBackIcon.transform('translate3d(' + -previousNavTranslate + 'px,0,0)'); } } } } } }; view.handleTouchEnd = function (e) { if (!isTouched || !isMoved) { isTouched = false; isMoved = false; return; } isTouched = false; isMoved = false; if (touchesDiff === 0) { $([activePage[0], previousPage[0]]).transform('').css({opacity: '', boxShadow: ''}); if (dynamicNavbar) { activeNavElements.transform('').css({opacity: ''}); previousNavElements.transform('').css({opacity: ''}); if (activeNavBackIcon && activeNavBackIcon.length > 0) activeNavBackIcon.transform(''); if (previousNavBackIcon && activeNavBackIcon.length > 0) previousNavBackIcon.transform(''); } return; } var timeDiff = (new Date()).getTime() - touchStartTime; var pageChanged = false; // Swipe back to previous page if ( timeDiff < 300 && touchesDiff > 10 || timeDiff >= 300 && touchesDiff > viewContainerWidth / 2 ) { activePage.removeClass('page-on-center').addClass('page-on-right'); previousPage.removeClass('page-on-left').addClass('page-on-center'); if (dynamicNavbar) { activeNavbar.removeClass('navbar-on-center').addClass('navbar-on-right'); previousNavbar.removeClass('navbar-on-left').addClass('navbar-on-center'); } pageChanged = true; } // Reset custom styles // Add transitioning class for transition-duration $([activePage[0], previousPage[0]]).transform('').css({opacity: '', boxShadow: ''}).addClass('page-transitioning'); if (dynamicNavbar) { activeNavElements.css({opacity: ''}) .each(function () { var translate = pageChanged ? this.f7NavbarRightOffset : 0; var sliding = $(this); sliding.transform('translate3d(' + translate + 'px,0,0)'); if (app.params.animateNavBackIcon) { if (sliding.hasClass('left') && activeNavBackIcon.length > 0) { activeNavBackIcon.addClass('page-transitioning').transform('translate3d(' + -translate + 'px,0,0)'); } } }).addClass('page-transitioning'); previousNavElements.transform('').css({opacity: ''}).each(function () { var translate = pageChanged ? 0 : this.f7NavbarLeftOffset; var sliding = $(this); sliding.transform('translate3d(' + translate + 'px,0,0)'); if (app.params.animateNavBackIcon) { if (sliding.hasClass('left') && previousNavBackIcon.length > 0) { previousNavBackIcon.addClass('page-transitioning').transform('translate3d(' + -translate + 'px,0,0)'); } } }).addClass('page-transitioning'); } allowViewTouchMove = false; view.allowPageChange = false; // Swipe Back Callback var callbackData = { activePage: activePage[0], previousPage: previousPage[0], activeNavbar: activeNavbar[0], previousNavbar: previousNavbar[0] }; if (pageChanged) { // Update View's URL var url = view.history[view.history.length - 2]; view.url = url; // Page before animation callback app.pageBackCallback('before', view, {pageContainer: activePage[0], url: url, position: 'center', newPage: previousPage, oldPage: activePage, swipeBack: true}); app.pageAnimCallback('before', view, {pageContainer: previousPage[0], url: url, position: 'left', newPage: previousPage, oldPage: activePage, swipeBack: true}); if (view.params.onSwipeBackBeforeChange) { view.params.onSwipeBackBeforeChange(callbackData); } container.trigger('swipeBackBeforeChange', callbackData); } else { if (view.params.onSwipeBackBeforeReset) { view.params.onSwipeBackBeforeReset(callbackData); } container.trigger('swipeBackBeforeReset', callbackData); } activePage.transitionEnd(function () { $([activePage[0], previousPage[0]]).removeClass('page-transitioning'); if (dynamicNavbar) { activeNavElements.removeClass('page-transitioning').css({opacity: ''}); previousNavElements.removeClass('page-transitioning').css({opacity: ''}); if (activeNavBackIcon && activeNavBackIcon.length > 0) activeNavBackIcon.removeClass('page-transitioning'); if (previousNavBackIcon && previousNavBackIcon.length > 0) previousNavBackIcon.removeClass('page-transitioning'); } allowViewTouchMove = true; view.allowPageChange = true; if (pageChanged) { if (app.params.pushState && view.main) history.back(); // Page after animation callback app.pageBackCallback('after', view, {pageContainer: activePage[0], url: url, position: 'center', newPage: previousPage, oldPage: activePage, swipeBack: true}); app.pageAnimCallback('after', view, {pageContainer: previousPage[0], url: url, position: 'left', newPage: previousPage, oldPage: activePage, swipeBack: true}); app.router.afterBack(view, activePage, previousPage); if (view.params.onSwipeBackAfterChange) { view.params.onSwipeBackAfterChange(callbackData); } container.trigger('swipeBackAfterChange', callbackData); } else { if (view.params.onSwipeBackAfterReset) { view.params.onSwipeBackAfterReset(callbackData); } container.trigger('swipeBackAfterReset', callbackData); } if (pageShadow && pageShadow.length > 0) pageShadow.remove(); }); }; view.attachEvents = function (detach) { var action = detach ? 'off' : 'on'; var passiveListener = app.touchEvents.start === 'touchstart' && app.support.passiveListener ? {passive: true, capture: false} : false; container[action](app.touchEvents.start, view.handleTouchStart, passiveListener); container[action](app.touchEvents.move, view.handleTouchMove); container[action](app.touchEvents.end, view.handleTouchEnd, passiveListener); }; view.detachEvents = function () { view.attachEvents(true); }; // Init if (view.params.swipeBackPage && !app.params.material) { view.attachEvents(); } // Add view to app app.views.push(view); if (view.main) app.mainView = view; // Router view.router = { load: function (options) { return app.router.load(view, options); }, back: function (options) { return app.router.back(view, options); }, // Shortcuts loadPage: function (options) { options = options || {}; if (typeof options === 'string') { var url = options; options = {}; if (url && url.indexOf('#') === 0 && view.params.domCache) { options.pageName = url.split('#')[1]; } else options.url = url; } return app.router.load(view, options); }, loadContent: function (content) { return app.router.load(view, {content: content}); }, reloadPage: function (url) { return app.router.load(view, {url: url, reload: true}); }, reloadContent: function (content) { return app.router.load(view, {content: content, reload: true}); }, reloadPreviousPage: function (url) { return app.router.load(view, {url: url, reloadPrevious: true, reload: true}); }, reloadPreviousContent: function (content) { return app.router.load(view, {content: content, reloadPrevious: true, reload: true}); }, refreshPage: function () { var options = { url: view.url, reload: true, ignoreCache: true }; if (options.url && options.url.indexOf('#') === 0) { if (view.params.domCache && view.pagesCache[options.url]) { options.pageName = view.pagesCache[options.url]; options.url = undefined; delete options.url; } else if (view.contentCache[options.url]) { options.content = view.contentCache[options.url]; options.url = undefined; delete options.url; } } return app.router.load(view, options); }, refreshPreviousPage: function () { var options = { url: view.history[view.history.length - 2], reload: true, reloadPrevious: true, ignoreCache: true }; if (options.url && options.url.indexOf('#') === 0 && view.params.domCache && view.pagesCache[options.url]) { options.pageName = view.pagesCache[options.url]; options.url = undefined; delete options.url; } return app.router.load(view, options); } }; // Aliases for temporary backward compatibility view.loadPage = view.router.loadPage; view.loadContent = view.router.loadContent; view.reloadPage = view.router.reloadPage; view.reloadContent = view.router.reloadContent; view.reloadPreviousPage = view.router.reloadPreviousPage; view.reloadPreviousContent = view.router.reloadPreviousContent; view.refreshPage = view.router.refreshPage; view.refreshPreviousPage = view.router.refreshPreviousPage; view.back = view.router.back; // Bars methods view.hideNavbar = function () { return app.hideNavbar(container.find('.navbar')); }; view.showNavbar = function () { return app.showNavbar(container.find('.navbar')); }; view.hideToolbar = function () { return app.hideToolbar(container.find('.toolbar')); }; view.showToolbar = function () { return app.showToolbar(container.find('.toolbar')); }; // Push State on load if (app.params.pushState && app.params.pushStateOnLoad && view.main) { var pushStateUrl; var pushStateUrlSplit = docLocation.split(pushStateSeparator)[1]; if (pushStateRoot) { pushStateUrl = docLocation.split(app.params.pushStateRoot + pushStateSeparator)[1]; } else if (pushStateSeparator && docLocation.indexOf(pushStateSeparator) >= 0 && docLocation.indexOf(pushStateSeparator + '#') < 0) { pushStateUrl = pushStateUrlSplit; } var pushStateAnimatePages = app.params.pushStateNoAnimation ? false : undefined; var historyState = history.state; if (pushStateUrl) { if (pushStateUrl.indexOf('#') >= 0 && view.params.domCache && historyState && historyState.pageName && 'viewIndex' in historyState) { app.router.load(view, {pageName: historyState.pageName, url: historyState.url, animatePages: pushStateAnimatePages, pushState: false}); } else if (pushStateUrl.indexOf('#') >= 0 && view.params.domCache && view.initialPagesUrl.indexOf(pushStateUrl) >= 0) { app.router.load(view, {pageName: pushStateUrl.replace('#',''), animatePages: pushStateAnimatePages, pushState: false}); } else app.router.load(view, {url: pushStateUrl, animatePages: pushStateAnimatePages, pushState: false}); } else if (view.params.domCache && docLocation.indexOf(pushStateSeparator + '#') >= 0) { if (historyState && historyState.pageName && 'viewIndex' in historyState) { app.router.load(view, {pageName: historyState.pageName, url: historyState.url, animatePages: pushStateAnimatePages, pushState: false}); } else if (pushStateSeparator && pushStateUrlSplit.indexOf('#') === 0) { if (view.initialPagesUrl.indexOf(pushStateUrlSplit)) { app.router.load(view, {pageName: pushStateUrlSplit.replace('#', ''), animatePages: pushStateAnimatePages, pushState: false}); } } } } // Destroy view.destroy = function () { view.detachEvents(); view = undefined; }; // Plugin hook app.pluginHook('addView', view); // Return view return view; }; app.addView = function (selector, params) { return new View(selector, params); }; app.getCurrentView = function (index) { var popoverView = $('.popover.modal-in .view'); var popupView = $('.popup.modal-in .view'); var panelView = $('.panel.active .view'); var appViews = $('.views'); // Find active view as tab var appView = appViews.children('.view'); // Propably in tabs or split view if (appView.length > 1) { if (appView.hasClass('tab')) { // Tabs appView = appViews.children('.view.active'); } else { // Split View, leave appView intact } } if (popoverView.length > 0 && popoverView[0].f7View) return popoverView[0].f7View; if (popupView.length > 0 && popupView[0].f7View) return popupView[0].f7View; if (panelView.length > 0 && panelView[0].f7View) return panelView[0].f7View; if (appView.length > 0) { if (appView.length === 1 && appView[0].f7View) return appView[0].f7View; if (appView.length > 1) { var currentViews = []; for (var i = 0; i < appView.length; i++) { if (appView[i].f7View) currentViews.push(appView[i].f7View); } if (currentViews.length > 0 && typeof index !== 'undefined') return currentViews[index]; if (currentViews.length > 1) return currentViews; if (currentViews.length === 1) return currentViews[0]; return undefined; } } return undefined; }; /*====================================================== ************ Navbars && Toolbars ************ ======================================================*/ // On Navbar Init Callback app.navbarInitCallback = function (view, pageContainer, navbarContainer, navbarInnerContainer) { if (!navbarContainer && navbarInnerContainer) navbarContainer = $(navbarInnerContainer).parent('.navbar')[0]; if (!navbarInnerContainer || navbarInnerContainer.f7NavbarInitialized && view && !view.params.domCache) return; var navbarData = { container: navbarContainer, innerContainer: navbarInnerContainer }; var pageData = pageContainer && pageContainer.f7PageData; var eventData = { page: pageData, navbar: navbarData }; if (navbarInnerContainer.f7NavbarInitialized && ((view && view.params.domCache) || (!view && $(navbarContainer).parents('.popup, .popover, .login-screen, .modal, .actions-modal, .picker-modal').length > 0))) { // Reinit Navbar app.reinitNavbar(navbarContainer, navbarInnerContainer); // Plugin hook app.pluginHook('navbarReinit', eventData); // Event $(navbarInnerContainer).trigger('navbarReinit', eventData); return; } navbarInnerContainer.f7NavbarInitialized = true; // Before Init app.pluginHook('navbarBeforeInit', navbarData, pageData); $(navbarInnerContainer).trigger('navbarBeforeInit', eventData); // Initialize Navbar app.initNavbar(navbarContainer, navbarInnerContainer); // On init app.pluginHook('navbarInit', navbarData, pageData); $(navbarInnerContainer).trigger('navbarInit', eventData); }; // Navbar Remove Callback app.navbarRemoveCallback = function (view, pageContainer, navbarContainer, navbarInnerContainer) { if (!navbarContainer && navbarInnerContainer) navbarContainer = $(navbarInnerContainer).parent('.navbar')[0]; var navbarData = { container: navbarContainer, innerContainer: navbarInnerContainer }; var pageData; if (pageContainer) { pageData = pageContainer.f7PageData; } var eventData = { page: pageData, navbar: navbarData }; app.pluginHook('navbarBeforeRemove', navbarData, pageData); $(navbarInnerContainer).trigger('navbarBeforeRemove', eventData); navbarData = null; pageData = null; }; app.initNavbar = function (navbarContainer, navbarInnerContainer) { // Init Subnavbar Searchbar if (app.initSearchbar) app.initSearchbar(navbarInnerContainer); }; app.reinitNavbar = function (navbarContainer, navbarInnerContainer) { // Re init navbar methods }; app.initNavbarWithCallback = function (navbarContainer) { navbarContainer = $(navbarContainer); var viewContainer = navbarContainer.parents('.' + app.params.viewClass); var view; if (viewContainer.length === 0) return; if (navbarContainer.parents('.navbar-through').length === 0 && viewContainer.find('.navbar-through').length === 0) return; view = viewContainer[0].f7View || undefined; navbarContainer.find('.navbar-inner').each(function () { var navbarInnerContainer = this; var pageContainer; if ($(navbarInnerContainer).attr('data-page')) { // For dom cache pageContainer = viewContainer.find('.page[data-page="' + $(navbarInnerContainer).attr('data-page') + '"]')[0]; } if (!pageContainer) { var pages = viewContainer.find('.page'); if (pages.length === 1) { pageContainer = pages[0]; } else { viewContainer.find('.page').each(function () { if (this.f7PageData && this.f7PageData.navbarInnerContainer === navbarInnerContainer) { pageContainer = this; } }); } } app.navbarInitCallback(view, pageContainer, navbarContainer[0], navbarInnerContainer); }); }; // Size Navbars app.sizeNavbars = function (viewContainer) { if (app.params.material) return; var navbarInner = viewContainer ? $(viewContainer).find('.navbar .navbar-inner:not(.cached)') : $('.navbar .navbar-inner:not(.cached)'); navbarInner.each(function () { var n = $(this); if (n.hasClass('cached')) return; var left = app.rtl ? n.find('.right') : n.find('.left'), right = app.rtl ? n.find('.left') : n.find('.right'), center = n.find('.center'), subnavbar = n.find('.subnavbar'), noLeft = left.length === 0, noRight = right.length === 0, leftWidth = noLeft ? 0 : left.outerWidth(true), rightWidth = noRight ? 0 : right.outerWidth(true), centerWidth = center.outerWidth(true), navbarStyles = n.styles(), navbarWidth = n[0].offsetWidth - parseInt(navbarStyles.paddingLeft, 10) - parseInt(navbarStyles.paddingRight, 10), onLeft = n.hasClass('navbar-on-left'), currLeft, diff; if (noRight) { currLeft = navbarWidth - centerWidth; } if (noLeft) { currLeft = 0; } if (!noLeft && !noRight) { currLeft = (navbarWidth - rightWidth - centerWidth + leftWidth) / 2; } var requiredLeft = (navbarWidth - centerWidth) / 2; if (navbarWidth - leftWidth - rightWidth > centerWidth) { if (requiredLeft < leftWidth) { requiredLeft = leftWidth; } if (requiredLeft + centerWidth > navbarWidth - rightWidth) { requiredLeft = navbarWidth - rightWidth - centerWidth; } diff = requiredLeft - currLeft; } else { diff = 0; } // RTL inverter var inverter = app.rtl ? -1 : 1; if (center.hasClass('sliding')) { center[0].f7NavbarLeftOffset = -(currLeft + diff) * inverter; center[0].f7NavbarRightOffset = (navbarWidth - currLeft - diff - centerWidth) * inverter; if (onLeft) { if (app.params.animateNavBackIcon) { var activeNavbarBackLink = n.parent().find('.navbar-on-center').find('.left.sliding .back .icon ~ span'); if (activeNavbarBackLink.length > 0) { center[0].f7NavbarLeftOffset += activeNavbarBackLink[0].offsetLeft; } } center.transform('translate3d(' + center[0].f7NavbarLeftOffset + 'px, 0, 0)'); } } if (!noLeft && left.hasClass('sliding')) { if (app.rtl) { left[0].f7NavbarLeftOffset = -(navbarWidth - left[0].offsetWidth) / 2 * inverter; left[0].f7NavbarRightOffset = leftWidth * inverter; } else { left[0].f7NavbarLeftOffset = -leftWidth; left[0].f7NavbarRightOffset = (navbarWidth - left[0].offsetWidth) / 2; if (app.params.animateNavBackIcon && left.find('.back .icon').length > 0) { left[0].f7NavbarRightOffset -= left.find('.back .icon')[0].offsetWidth; } } if (onLeft) left.transform('translate3d(' + left[0].f7NavbarLeftOffset + 'px, 0, 0)'); } if (!noRight && right.hasClass('sliding')) { if (app.rtl) { right[0].f7NavbarLeftOffset = -rightWidth * inverter; right[0].f7NavbarRightOffset = (navbarWidth - right[0].offsetWidth) / 2 * inverter; } else { right[0].f7NavbarLeftOffset = -(navbarWidth - right[0].offsetWidth) / 2; right[0].f7NavbarRightOffset = rightWidth; } if (onLeft) right.transform('translate3d(' + right[0].f7NavbarLeftOffset + 'px, 0, 0)'); } if (subnavbar.length && subnavbar.hasClass('sliding')) { subnavbar[0].f7NavbarLeftOffset = app.rtl ? subnavbar[0].offsetWidth : -subnavbar[0].offsetWidth; subnavbar[0].f7NavbarRightOffset = -subnavbar[0].f7NavbarLeftOffset; } // Center left var centerLeft = diff; if (app.rtl && noLeft && noRight && center.length > 0) centerLeft = -centerLeft; center.css({left: centerLeft + 'px'}); }); }; // Hide/Show Navbars/Toolbars app.hideNavbar = function (navbarContainer) { $(navbarContainer).addClass('navbar-hidden'); return true; }; app.showNavbar = function (navbarContainer) { var navbar = $(navbarContainer); navbar.addClass('navbar-hiding').removeClass('navbar-hidden').transitionEnd(function () { navbar.removeClass('navbar-hiding'); }); return true; }; app.hideToolbar = function (toolbarContainer) { $(toolbarContainer).addClass('toolbar-hidden'); return true; }; app.showToolbar = function (toolbarContainer) { var toolbar = $(toolbarContainer); toolbar.addClass('toolbar-hiding').removeClass('toolbar-hidden').transitionEnd(function () { toolbar.removeClass('toolbar-hiding'); }); }; /*====================================================== ************ Searchbar ************ ======================================================*/ var Searchbar = function (container, params) { var defaults = { input: null, clearButton: null, cancelButton: null, searchList: null, searchIn: '.item-title', searchBy: '', found: null, notFound: null, overlay: null, ignore: '.searchbar-ignore', customSearch: false, removeDiacritics: false, hideDividers: true, hideGroups: true, /* Callbacks onSearch onEnable onDisable onClear */ }; params = params || {}; for (var def in defaults) { if (typeof params[def] === 'undefined' || params[def] === null) { params[def] = defaults[def]; } } // Instance var s = this; // Material s.material = app.params.material; // Params s.params = params; // Container container = $(container); s.container = container; // Active s.active = false; // Input s.input = s.params.input ? $(s.params.input) : s.container.find('input[type="search"]'); s.clearButton = s.params.clearButton ? $(s.params.clearButton) : s.container.find('.searchbar-clear'); s.cancelButton = s.params.cancelButton ? $(s.params.cancelButton) : s.container.find('.searchbar-cancel'); // Search List s.searchList = $(s.params.searchList); // Is Virtual List s.isVirtualList = s.searchList.hasClass('virtual-list'); // Is In Page s.pageContainer = s.container.parents('.page').eq(0); // Overlay if (!s.params.overlay) { s.overlay = s.pageContainer.length > 0 ? s.pageContainer.find('.searchbar-overlay') : $('.searchbar-overlay'); } else { s.overlay = $(s.params.overlay); } // Found and not found if (!s.params.found) { s.found = s.pageContainer.length > 0 ? s.pageContainer.find('.searchbar-found') : $('.searchbar-found'); } else { s.found = $(s.params.found); } if (!s.params.notFound) { s.notFound = s.pageContainer.length > 0 ? s.pageContainer.find('.searchbar-not-found') : $('.searchbar-not-found'); } else { s.notFound = $(s.params.notFound); } // Set Cancel button var cancelMarginProp = app.rtl ? 'margin-left' : 'margin-right'; var cancelButtonHasMargin = false; s.setCancelButtonMargin = function () { s.cancelButton.transition(0).show(); s.cancelButton.css(cancelMarginProp, -s.cancelButton[0].offsetWidth + 'px'); var clientLeft = s.cancelButton[0].clientLeft; s.cancelButton.transition(''); cancelButtonHasMargin = true; }; // Trigger s.triggerEvent = function (eventName, callbackName, eventData) { s.container.trigger(eventName, eventData); if (s.searchList.length > 0) s.searchList.trigger(eventName, eventData); if (callbackName && s.params[callbackName]) s.params[callbackName](s, eventData); }; // Enable/disalbe s.enable = function (e) { function _enable() { if ((s.searchList.length || s.params.customSearch) && !s.container.hasClass('searchbar-active') && !s.query) s.overlay.addClass('searchbar-overlay-active'); s.container.addClass('searchbar-active'); if (s.cancelButton.length > 0 && !s.material) { if (!cancelButtonHasMargin) { s.setCancelButtonMargin(); } s.cancelButton.css(cancelMarginProp, '0px'); } s.triggerEvent('enableSearch', 'onEnable'); s.active = true; } if (app.device.ios && !app.params.material && e && e.type === 'focus') { setTimeout(function () { _enable(); }, 400); } else { _enable(); } }; s.disable = function () { s.input.val('').trigger('change'); s.container.removeClass('searchbar-active searchbar-not-empty'); if (s.cancelButton.length > 0 && !s.material) s.cancelButton.css(cancelMarginProp, -s.cancelButton[0].offsetWidth + 'px'); if (s.searchList.length || s.params.customSearch) s.overlay.removeClass('searchbar-overlay-active'); s.active = false; function _disable() { s.input.blur(); } if (app.device.ios) { setTimeout(function () { _disable(); }, 400); } else { _disable(); } s.triggerEvent('disableSearch', 'onDisable'); }; // Clear s.clear = function (e) { if (!s.query && e && $(e.target).hasClass('searchbar-clear')) { s.disable(); return; } s.input.val('').trigger('change').focus(); s.triggerEvent('clearSearch', 'onClear'); }; // Search s.handleInput = function () { setTimeout(function () { var value = s.input.val().trim(); if ((s.searchList.length > 0 || s.params.customSearch) && (s.params.searchIn || s.isVirtualList)) s.search(value, true); }, 0); }; var previousQuery = ''; var virtualList; s.search = function (query, internal) { if (query.trim() === previousQuery) return; previousQuery = query.trim(); if (!internal) { if (!s.active) { s.enable(); } s.input.val(query); } s.query = s.value = query; // Add active/inactive classes on overlay if (query.length === 0) { s.container.removeClass('searchbar-not-empty'); if (s.searchList.length && s.container.hasClass('searchbar-active')) s.overlay.addClass('searchbar-overlay-active'); } else { s.container.addClass('searchbar-not-empty'); if (s.searchList.length && s.container.hasClass('searchbar-active')) s.overlay.removeClass('searchbar-overlay-active'); } if (s.params.customSearch) { s.triggerEvent('search', 'onSearch', {query: query}); return; } var foundItems = [], _vlQuery; if (s.isVirtualList) { virtualList = s.searchList[0].f7VirtualList; if (query.trim() === '') { virtualList.resetFilter(); s.notFound.hide(); s.found.show(); return; } _vlQuery = s.params.removeDiacritics ? $.removeDiacritics(query) : query; if (virtualList.params.searchAll) { foundItems = virtualList.params.searchAll(_vlQuery, virtualList.items) || []; } else if (virtualList.params.searchByItem) { for (var i = 0; i < virtualList.items.length; i++) { if(virtualList.params.searchByItem(_vlQuery, i, virtualList.params.items[i])) { foundItems.push(i); } } } } else { var values; if (s.params.removeDiacritics) values = $.removeDiacritics(query.trim().toLowerCase()).split(' '); else { values = query.trim().toLowerCase().split(' '); } s.searchList.find('li').removeClass('hidden-by-searchbar').each(function (index, el) { el = $(el); var compareWithText = []; el.find(s.params.searchIn).each(function () { var itemText = $(this).text().trim().toLowerCase(); if (s.params.removeDiacritics) itemText = $.removeDiacritics(itemText); compareWithText.push(itemText); }); compareWithText = compareWithText.join(' '); var wordsMatch = 0; for (var i = 0; i < values.length; i++) { if (compareWithText.indexOf(values[i]) >= 0) wordsMatch++; } if (wordsMatch !== values.length && !(s.params.ignore && el.is(s.params.ignore))) { el.addClass('hidden-by-searchbar'); } else { foundItems.push(el[0]); } }); if (s.params.hideDividers) { s.searchList.find('.item-divider, .list-group-title').each(function () { var title = $(this); var nextElements = title.nextAll('li'); var hide = true; for (var i = 0; i < nextElements.length; i++) { var nextEl = $(nextElements[i]); if (nextEl.hasClass('list-group-title') || nextEl.hasClass('item-divider')) break; if (!nextEl.hasClass('hidden-by-searchbar')) { hide = false; } } var ignore = s.params.ignore && title.is(s.params.ignore); if (hide && !ignore) title.addClass('hidden-by-searchbar'); else title.removeClass('hidden-by-searchbar'); }); } if (s.params.hideGroups) { s.searchList.find('.list-group').each(function () { var group = $(this); var ignore = s.params.ignore && group.is(s.params.ignore); var notHidden = group.find('li:not(.hidden-by-searchbar)'); if (notHidden.length === 0 && !ignore) { group.addClass('hidden-by-searchbar'); } else { group.removeClass('hidden-by-searchbar'); } }); } } s.triggerEvent('search', 'onSearch', {query: query, foundItems: foundItems}); if (foundItems.length === 0) { s.notFound.show(); s.found.hide(); } else { s.notFound.hide(); s.found.show(); } if (s.isVirtualList) { virtualList.filterItems(foundItems); } }; // Events function preventSubmit(e) { e.preventDefault(); } s.attachEvents = function (destroy) { var method = destroy ? 'off' : 'on'; s.container[method]('submit', preventSubmit); if (!s.material) s.cancelButton[method]('click', s.disable); s.overlay[method]('click', s.disable); s.input[method]('focus', s.enable); s.input[method]('change keydown keypress keyup', s.handleInput); s.clearButton[method]('click', s.clear); }; s.detachEvents = function() { s.attachEvents(true); }; // Init Destroy s.init = function () { s.attachEvents(); }; s.destroy = function () { if (!s) return; s.detachEvents(); s = null; }; // Init s.init(); s.container[0].f7Searchbar = s; return s; }; app.searchbar = function (container, params) { return new Searchbar(container, params); }; app.initSearchbar = function (container) { container = $(container); var searchbar = container.hasClass('searchbar') ? container : container.find('.searchbar'); if (searchbar.length === 0) return; if (!searchbar.hasClass('searchbar-init')) return; var sb = app.searchbar(searchbar, searchbar.dataset()); function onBeforeRemove() { if (sb) sb.destroy(); } if (container.hasClass('page')) { container.once('pageBeforeRemove', onBeforeRemove); } else if (container.hasClass('navbar-inner')) { container.once('navbarBeforeRemove', onBeforeRemove); } }; /*====================================================== ************ Messagebar ************ ======================================================*/ var Messagebar = function (container, params) { var defaults = { textarea: null, maxHeight: null, }; params = params || {}; for (var def in defaults) { if (typeof params[def] === 'undefined' || params[def] === null) { params[def] = defaults[def]; } } // Instance var m = this; // Params m.params = params; // Container m.container = $(container); if (m.container.length === 0) return; // Textarea m.textarea = m.params.textarea ? $(m.params.textarea) : m.container.find('textarea'); // Is In Page m.pageContainer = m.container.parents('.page').eq(0); m.pageContent = m.pageContainer.find('.page-content'); // Initial Sizes m.pageContentPadding = parseInt(m.pageContent.css('padding-bottom')); m.initialBarHeight = m.container[0].offsetHeight; m.initialAreaHeight = m.textarea[0].offsetHeight; // Resize textarea m.sizeTextarea = function () { // Reset m.textarea.css({'height': ''}); var height = m.textarea[0].offsetHeight; var diff = height - m.textarea[0].clientHeight; var scrollHeight = m.textarea[0].scrollHeight; // Update if (scrollHeight + diff > height) { var newAreaHeight = scrollHeight + diff; var newBarHeight = m.initialBarHeight + (newAreaHeight - m.initialAreaHeight); var maxBarHeight = m.params.maxHeight || m.container.parents('.view')[0].offsetHeight - 88; if (newBarHeight > maxBarHeight) { newBarHeight = parseInt(maxBarHeight, 10); newAreaHeight = newBarHeight - m.initialBarHeight + m.initialAreaHeight; } m.textarea.css('height', newAreaHeight + 'px'); m.container.css('height', newBarHeight + 'px'); var onBottom = (m.pageContent[0].scrollTop === m.pageContent[0].scrollHeight - m.pageContent[0].offsetHeight); if (m.pageContent.length > 0) { m.pageContent.css('padding-bottom', newBarHeight + 'px'); if (m.pageContent.find('.messages-new-first').length === 0 && onBottom) { m.pageContent.scrollTop(m.pageContent[0].scrollHeight - m.pageContent[0].offsetHeight); } } } else { if (m.pageContent.length > 0) { m.container.css({'height': '', 'bottom': ''}); m.pageContent.css({'padding-bottom': ''}); } } }; // Clear m.clear = function () { m.textarea.val('').trigger('change'); }; m.value = function (value) { if (typeof value === 'undefined') return m.textarea.val(); else m.textarea.val(value).trigger('change'); }; // Handle textarea m.textareaTimeout = undefined; m.handleTextarea = function (e) { clearTimeout(m.textareaTimeout); m.textareaTimeout = setTimeout(function () { m.sizeTextarea(); }, 0); }; //Events function preventSubmit(e) { e.preventDefault(); } m.attachEvents = function (destroy) { var method = destroy ? 'off' : 'on'; m.container[method]('submit', preventSubmit); m.textarea[method]('change keydown keypress keyup paste cut', m.handleTextarea); }; m.detachEvents = function () { m.attachEvents(true); }; // Init Destroy m.init = function () { m.attachEvents(); }; m.destroy = function () { m.detachEvents(); m = null; }; // Init m.init(); m.container[0].f7Messagebar = m; return m; }; app.messagebar = function (container, params) { return new Messagebar(container, params); }; app.initPageMessagebar = function (pageContainer) { pageContainer = $(pageContainer); var messagebar = pageContainer.hasClass('messagebar') ? pageContainer : pageContainer.find('.messagebar'); if (messagebar.length === 0) return; if (!messagebar.hasClass('messagebar-init')) return; var mb = app.messagebar(messagebar, messagebar.dataset()); // Destroy on page remove function pageBeforeRemove() { mb.destroy(); pageContainer.off('pageBeforeRemove', pageBeforeRemove); } if (pageContainer.hasClass('page')) { pageContainer.on('pageBeforeRemove', pageBeforeRemove); } }; /*====================================================== ************ XHR ************ ======================================================*/ // XHR Caching app.cache = []; app.removeFromCache = function (url) { var index = false; for (var i = 0; i < app.cache.length; i++) { if (app.cache[i].url === url) index = i; } if (index !== false) app.cache.splice(index, 1); }; // XHR app.xhr = false; app.get = function (url, view, ignoreCache, callback) { // should we ignore get params or not var _url = url; if (app.params.cacheIgnoreGetParameters && url.indexOf('?') >= 0) { _url = url.split('?')[0]; } if (app.params.cache && !ignoreCache && url.indexOf('nocache') < 0 && app.params.cacheIgnore.indexOf(_url) < 0) { // Check is the url cached for (var i = 0; i < app.cache.length; i++) { if (app.cache[i].url === _url) { // Check expiration if ((new Date()).getTime() - app.cache[i].time < app.params.cacheDuration) { // Load from cache callback(app.cache[i].content); return false; } } } } app.xhr = $.ajax({ url: url, method: 'GET', beforeSend: app.params.onAjaxStart, complete: function (xhr) { if ((xhr.status >= 200 && xhr.status < 300) || xhr.status === 0) { if (app.params.cache) { app.removeFromCache(_url); app.cache.push({ url: _url, time: (new Date()).getTime(), content: xhr.responseText }); } callback(xhr.responseText, false); } else { callback(xhr.responseText, true); } if (app.params.onAjaxComplete) app.params.onAjaxComplete(xhr); }, error: function (xhr) { callback(xhr.responseText, true); if (app.params.onAjaxError) app.params.onAjaxError(xhr); } }); if (view) view.xhr = app.xhr; return app.xhr; }; /*====================================================== ************ Pages ************ ======================================================*/ // Page Callbacks API app.pageCallbacks = {}; app.onPage = function (callbackName, pageName, callback) { if (pageName && pageName.split(' ').length > 1) { var pageNames = pageName.split(' '); var returnCallbacks = []; for (var i = 0; i < pageNames.length; i++) { returnCallbacks.push(app.onPage(callbackName, pageNames[i], callback)); } returnCallbacks.remove = function () { for (var i = 0; i < returnCallbacks.length; i++) { returnCallbacks[i].remove(); } }; returnCallbacks.trigger = function () { for (var i = 0; i < returnCallbacks.length; i++) { returnCallbacks[i].trigger(); } }; return returnCallbacks; } var callbacks = app.pageCallbacks[callbackName][pageName]; if (!callbacks) { callbacks = app.pageCallbacks[callbackName][pageName] = []; } app.pageCallbacks[callbackName][pageName].push(callback); return { remove: function () { var removeIndex; for (var i = 0; i < callbacks.length; i++) { if (callbacks[i].toString() === callback.toString()) { removeIndex = i; } } if (typeof removeIndex !== 'undefined') callbacks.splice(removeIndex, 1); }, trigger: callback }; }; //Create callbacks methods dynamically function createPageCallback(callbackName) { var capitalized = callbackName.replace(/^./, function (match) { return match.toUpperCase(); }); app['onPage' + capitalized] = function (pageName, callback) { return app.onPage(callbackName, pageName, callback); }; } var pageCallbacksNames = ('beforeInit init reinit beforeAnimation afterAnimation back afterBack beforeRemove').split(' '); for (var i = 0; i < pageCallbacksNames.length; i++) { app.pageCallbacks[pageCallbacksNames[i]] = {}; createPageCallback(pageCallbacksNames[i]); } app.triggerPageCallbacks = function (callbackName, pageName, pageData) { var allPagesCallbacks = app.pageCallbacks[callbackName]['*']; if (allPagesCallbacks) { for (var j = 0; j < allPagesCallbacks.length; j++) { allPagesCallbacks[j](pageData); } } var callbacks = app.pageCallbacks[callbackName][pageName]; if (!callbacks || callbacks.length === 0) return; for (var i = 0; i < callbacks.length; i++) { callbacks[i](pageData); } }; // On Page Init Callback app.pageInitCallback = function (view, params) { var pageContainer = params.pageContainer; if (!pageContainer) return; if (pageContainer.f7PageInitialized && view && !view.params.domCache) return; var pageQuery = params.query; if (!pageQuery) { if (params.url && params.url.indexOf('?') > 0) { pageQuery = $.parseUrlQuery(params.url || ''); } else if (pageContainer.f7PageData && pageContainer.f7PageData.query) { pageQuery = pageContainer.f7PageData.query; } else { pageQuery = {}; } } // Page Data var pageData = { container: pageContainer, url: params.url, query: pageQuery, name: $(pageContainer).attr('data-page'), view: view, from: params.position, context: params.context, navbarInnerContainer: params.navbarInnerContainer, fromPage: params.fromPage }; if (params.fromPage && !params.fromPage.navbarInnerContainer && params.oldNavbarInnerContainer) { params.fromPage.navbarInnerContainer = params.oldNavbarInnerContainer; } if (pageContainer.f7PageInitialized && ((view && view.params.domCache) || (!view && $(pageContainer).parents('.popup, .popover, .login-screen, .modal, .actions-modal, .picker-modal').length > 0))) { // Reinit Page app.reinitPage(pageContainer); // Callbacks app.pluginHook('pageReinit', pageData); if (app.params.onPageReinit) app.params.onPageReinit(app, pageData); app.triggerPageCallbacks('reinit', pageData.name, pageData); $(pageData.container).trigger('pageReinit', {page: pageData}); return; } pageContainer.f7PageInitialized = true; // Store pagedata in page pageContainer.f7PageData = pageData; // Update View's activePage if (view && !params.preloadOnly && !params.reloadPrevious) { // Add data-page on view $(view.container).attr('data-page', pageData.name); // Update View active page data view.activePage = pageData; } // Before Init Callbacks app.pluginHook('pageBeforeInit', pageData); if (app.params.onPageBeforeInit) app.params.onPageBeforeInit(app, pageData); app.triggerPageCallbacks('beforeInit', pageData.name, pageData); $(pageData.container).trigger('pageBeforeInit', {page: pageData}); // Init page app.initPage(pageContainer); // Init Callback app.pluginHook('pageInit', pageData); if (app.params.onPageInit) app.params.onPageInit(app, pageData); app.triggerPageCallbacks('init', pageData.name, pageData); $(pageData.container).trigger('pageInit', {page: pageData}); }; app.pageRemoveCallback = function (view, pageContainer, position) { var pageContext; if (!pageContainer) return; if (pageContainer.f7PageData) pageContext = pageContainer.f7PageData.context; // Page Data var pageData = { container: pageContainer, name: $(pageContainer).attr('data-page'), view: view, url: pageContainer.f7PageData && pageContainer.f7PageData.url, query: pageContainer.f7PageData && pageContainer.f7PageData.query, navbarInnerContainer: pageContainer.f7PageData && pageContainer.f7PageData.navbarInnerContainer, from: position, context: pageContext }; // Before Init Callback app.pluginHook('pageBeforeRemove', pageData); if (app.params.onPageBeforeRemove) app.params.onPageBeforeRemove(app, pageData); app.triggerPageCallbacks('beforeRemove', pageData.name, pageData); $(pageData.container).trigger('pageBeforeRemove', {page: pageData}); pageData = null; }; app.pageBackCallback = function (callback, view, params) { // Page Data var pageContainer = params.pageContainer; var pageContext; if (!pageContainer) return; if (pageContainer.f7PageData) pageContext = pageContainer.f7PageData.context; var pageData = { container: pageContainer, name: $(pageContainer).attr('data-page'), url: pageContainer.f7PageData && pageContainer.f7PageData.url, query: pageContainer.f7PageData && pageContainer.f7PageData.query, view: view, from: params.position, context: pageContext, navbarInnerContainer: pageContainer.f7PageData && pageContainer.f7PageData.navbarInnerContainer, swipeBack: params.swipeBack }; if (callback === 'after') { app.pluginHook('pageAfterBack', pageData); if (app.params.onPageAfterBack) app.params.onPageAfterBack(app, pageData); app.triggerPageCallbacks('afterBack', pageData.name, pageData); $(pageContainer).trigger('pageAfterBack', {page: pageData}); } if (callback === 'before') { app.pluginHook('pageBack', pageData); if (app.params.onPageBack) app.params.onPageBack(app, pageData); app.triggerPageCallbacks('back', pageData.name, pageData); $(pageData.container).trigger('pageBack', {page: pageData}); } }; app.pageAnimCallback = function (callback, view, params) { var pageContainer = params.pageContainer; var pageContext; if (!pageContainer) return; if (pageContainer.f7PageData) pageContext = pageContainer.f7PageData.context; var pageQuery = params.query; if (!pageQuery) { if (params.url && params.url.indexOf('?') > 0) { pageQuery = $.parseUrlQuery(params.url || ''); } else if (pageContainer.f7PageData && pageContainer.f7PageData.query) { pageQuery = pageContainer.f7PageData.query; } else { pageQuery = {}; } } // Page Data var pageData = { container: pageContainer, url: params.url, query: pageQuery, name: $(pageContainer).attr('data-page'), view: view, from: params.position, context: pageContext, swipeBack: params.swipeBack, navbarInnerContainer: pageContainer.f7PageData && pageContainer.f7PageData.navbarInnerContainer, fromPage: params.fromPage }; var oldPage = params.oldPage, newPage = params.newPage; // Update page date pageContainer.f7PageData = pageData; if (callback === 'after') { app.pluginHook('pageAfterAnimation', pageData); if (app.params.onPageAfterAnimation) app.params.onPageAfterAnimation(app, pageData); app.triggerPageCallbacks('afterAnimation', pageData.name, pageData); $(pageData.container).trigger('pageAfterAnimation', {page: pageData}); } if (callback === 'before') { // Add data-page on view $(view.container).attr('data-page', pageData.name); // Update View's activePage if (view) view.activePage = pageData; // Hide/show navbar dynamically if (newPage.hasClass('no-navbar') && !oldPage.hasClass('no-navbar')) { view.hideNavbar(); } if (!newPage.hasClass('no-navbar') && (oldPage.hasClass('no-navbar') || oldPage.hasClass('no-navbar-by-scroll'))) { view.showNavbar(); } // Hide/show navbar toolbar if (newPage.hasClass('no-toolbar') && !oldPage.hasClass('no-toolbar')) { view.hideToolbar(); } if (!newPage.hasClass('no-toolbar') && (oldPage.hasClass('no-toolbar') || oldPage.hasClass('no-toolbar-by-scroll'))) { view.showToolbar(); } // Hide/show tabbar var tabBar; if (newPage.hasClass('no-tabbar') && !oldPage.hasClass('no-tabbar')) { tabBar = $(view.container).find('.tabbar'); if (tabBar.length === 0) tabBar = $(view.container).parents('.' + app.params.viewsClass).find('.tabbar'); app.hideToolbar(tabBar); } if (!newPage.hasClass('no-tabbar') && (oldPage.hasClass('no-tabbar') || oldPage.hasClass('no-tabbar-by-scroll'))) { tabBar = $(view.container).find('.tabbar'); if (tabBar.length === 0) tabBar = $(view.container).parents('.' + app.params.viewsClass).find('.tabbar'); app.showToolbar(tabBar); } oldPage.removeClass('no-navbar-by-scroll no-toolbar-by-scroll'); // Callbacks app.pluginHook('pageBeforeAnimation', pageData); if (app.params.onPageBeforeAnimation) app.params.onPageBeforeAnimation(app, pageData); app.triggerPageCallbacks('beforeAnimation', pageData.name, pageData); $(pageData.container).trigger('pageBeforeAnimation', {page: pageData}); } }; // Init Page Events and Manipulations app.initPage = function (pageContainer) { pageContainer = $(pageContainer); if (pageContainer.length === 0) return; // Size navbars on page load if (app.sizeNavbars) app.sizeNavbars(pageContainer.parents('.' + app.params.viewClass)[0]); // Init messages if (app.initPageMessages) app.initPageMessages(pageContainer); // Init forms storage if (app.initFormsStorage) app.initFormsStorage(pageContainer); // Init smart select if (app.initSmartSelects) app.initSmartSelects(pageContainer); // Init slider if (app.initPageSwiper) app.initPageSwiper(pageContainer); // Init pull to refres if (app.initPullToRefresh) app.initPullToRefresh(pageContainer); // Init infinite scroll if (app.initPageInfiniteScroll) app.initPageInfiniteScroll(pageContainer); // Init searchbar if (app.initSearchbar) app.initSearchbar(pageContainer); // Init message bar if (app.initPageMessagebar) app.initPageMessagebar(pageContainer); // Init scroll toolbars if (app.initPageScrollToolbars) app.initPageScrollToolbars(pageContainer); // Init lazy images if (app.initImagesLazyLoad) app.initImagesLazyLoad(pageContainer); // Init progress bars if (app.initPageProgressbar) app.initPageProgressbar(pageContainer); // Init resizeable textareas if (app.initPageResizableTextarea) app.initPageResizableTextarea(pageContainer); // Init Material Preloader if (app.params.material && app.initPageMaterialPreloader) app.initPageMaterialPreloader(pageContainer); // Init Material Inputs if (app.params.material && app.initPageMaterialInputs) app.initPageMaterialInputs(pageContainer); // Init Material Tabbar if (app.params.material && app.initPageMaterialTabbar) app.initPageMaterialTabbar(pageContainer); }; app.reinitPage = function (pageContainer) { pageContainer = $(pageContainer); if (pageContainer.length === 0) return; // Size navbars on page reinit if (app.sizeNavbars) app.sizeNavbars(pageContainer.parents('.' + app.params.viewClass)[0]); // Reinit slider if (app.reinitPageSwiper) app.reinitPageSwiper(pageContainer); // Reinit lazy load if (app.reinitLazyLoad) app.reinitLazyLoad(pageContainer); }; app.initPageWithCallback = function (pageContainer) { pageContainer = $(pageContainer); var viewContainer = pageContainer.parents('.' + app.params.viewClass); if (viewContainer.length === 0) return; var view = viewContainer[0].f7View || undefined; var url = view && view.url ? view.url : undefined; if (viewContainer && pageContainer.attr('data-page')) { viewContainer.attr('data-page', pageContainer.attr('data-page')); } app.pageInitCallback(view, {pageContainer: pageContainer[0], url: url, position: 'center'}); }; /*====================================================== ************ Navigation / Router ************ ======================================================*/ app.router = { _remove: function (el) { if (app.params.routerRemoveTimeout || app.params.routerRemoveWithTimeout) { setTimeout(function () { $(el).remove(); }, 0); } else $(el).remove(); }, // Temporary DOM Element temporaryDom: document.createElement('div'), // Find page or navbar in passed container which are related to View findElement: function (selector, container, view, notCached) { container = $(container); if (notCached) selector = selector + ':not(.cached)'; var found = container.find(selector); if (found.length > 1) { if (typeof view.selector === 'string') { // Search in related view found = container.find(view.selector + ' ' + selector); } if (found.length > 1) { // Search in main view found = container.find('.' + app.params.viewMainClass + ' ' + selector); } } if (found.length === 1) return found; else { // Try to find non cached if (!notCached) found = app.router.findElement(selector, container, view, true); if (found && found.length === 1) return found; if (found && found.length > 1) return $(found[0]); else return undefined; } }, // Set pages classess for animationEnd animatePages: function (leftPage, rightPage, direction, view) { // Loading new page var removeClasses = 'page-on-center page-on-right page-on-left'; if (direction === 'to-left') { leftPage.removeClass(removeClasses).addClass('page-from-center-to-left'); rightPage.removeClass(removeClasses).addClass('page-from-right-to-center'); } // Go back if (direction === 'to-right') { leftPage.removeClass(removeClasses).addClass('page-from-left-to-center'); rightPage.removeClass(removeClasses).addClass('page-from-center-to-right'); } }, // Prepare navbar before animarion prepareNavbar: function (newNavbarInner, oldNavbarInner, newNavbarPosition) { $(newNavbarInner).find('.sliding').each(function () { var sliding = $(this); var slidingOffset = newNavbarPosition === 'right' ? this.f7NavbarRightOffset : this.f7NavbarLeftOffset; if (app.params.animateNavBackIcon) { if (sliding.hasClass('left') && sliding.find('.back .icon').length > 0) { sliding.find('.back .icon').transform('translate3d(' + (-slidingOffset) + 'px,0,0)'); } } sliding.transform('translate3d(' + slidingOffset + 'px,0,0)'); }); }, // Set navbars classess for animation animateNavbars: function (leftNavbarInner, rightNavbarInner, direction, view) { // Loading new page var removeClasses = 'navbar-on-right navbar-on-center navbar-on-left'; if (direction === 'to-left') { rightNavbarInner.removeClass(removeClasses).addClass('navbar-from-right-to-center'); rightNavbarInner.find('.sliding').each(function () { var sliding = $(this); sliding.transform('translate3d(0px,0,0)'); if (app.params.animateNavBackIcon) { if (sliding.hasClass('left') && sliding.find('.back .icon').length > 0) { sliding.find('.back .icon').transform('translate3d(0px,0,0)'); } } }); leftNavbarInner.removeClass(removeClasses).addClass('navbar-from-center-to-left'); leftNavbarInner.find('.sliding').each(function () { var sliding = $(this); var rightText; if (app.params.animateNavBackIcon) { if (sliding.hasClass('center') && rightNavbarInner.find('.sliding.left .back .icon').length > 0) { rightText = rightNavbarInner.find('.sliding.left .back span'); if (rightText.length > 0) this.f7NavbarLeftOffset += rightText[0].offsetLeft; } if (sliding.hasClass('left') && sliding.find('.back .icon').length > 0) { sliding.find('.back .icon').transform('translate3d(' + (-this.f7NavbarLeftOffset) + 'px,0,0)'); } } sliding.transform('translate3d(' + (this.f7NavbarLeftOffset) + 'px,0,0)'); }); } // Go back if (direction === 'to-right') { leftNavbarInner.removeClass(removeClasses).addClass('navbar-from-left-to-center'); leftNavbarInner.find('.sliding').each(function () { var sliding = $(this); sliding.transform('translate3d(0px,0,0)'); if (app.params.animateNavBackIcon) { if (sliding.hasClass('left') && sliding.find('.back .icon').length > 0) { sliding.find('.back .icon').transform('translate3d(0px,0,0)'); } } }); rightNavbarInner.removeClass(removeClasses).addClass('navbar-from-center-to-right'); rightNavbarInner.find('.sliding').each(function () { var sliding = $(this); if (app.params.animateNavBackIcon) { if (sliding.hasClass('left') && sliding.find('.back .icon').length > 0) { sliding.find('.back .icon').transform('translate3d(' + (-this.f7NavbarRightOffset) + 'px,0,0)'); } } sliding.transform('translate3d(' + (this.f7NavbarRightOffset) + 'px,0,0)'); }); } }, preprocess: function(view, content, url, next) { // Plugin hook app.pluginHook('routerPreprocess', view, content, url, next); // Preprocess by plugin content = app.pluginProcess('preprocess', content); if (view && view.params && view.params.preprocess) { content = view.params.preprocess(content, url, next); if (typeof content !== 'undefined') { next(content); } } else if (app.params.preprocess) { content = app.params.preprocess(content, url, next); if (typeof content !== 'undefined') { next(content); } } else { next(content); } }, preroute: function(view, options, isBack) { if (isBack) options.isBack = true; app.pluginHook('routerPreroute', view, options); if ((app.params.preroute && app.params.preroute(view, options) === false) || (view && view.params.preroute && view.params.preroute(view, options) === false)) { return true; } else { return false; } }, template7Render: function (view, options) { var url = options.url, content = options.content, //initial content t7_rendered_content = options.content, // will be rendered using Template7 context = options.context, // Context data for Template7 contextName = options.contextName, template = options.template, // Template 7 compiled template pageName = options.pageName; var t7_ctx, t7_template; if (typeof content === 'string') { if (url) { if (app.template7Cache[url] && !options.ignoreCache) t7_template = t7.cache[url]; else { t7_template = t7.compile(content); t7.cache[url] = t7_template; } } else t7_template = t7.compile(content); } else if (template) { t7_template = template; } if (context) { t7_ctx = context; if (context && url) { view.contextCache[url] = context; } } else { if (contextName) { if (contextName.indexOf('.') >= 0) { var _ctx_path = contextName.split('.'); var _ctx = t7.data[_ctx_path[0]]; for (var i = 1; i < _ctx_path.length; i++) { if (_ctx_path[i]) _ctx = _ctx[_ctx_path[i]]; } t7_ctx = _ctx; } else t7_ctx = t7.data[contextName]; } if (!t7_ctx && url) { t7_ctx = t7.data['url:' + url]; } if (!t7_ctx && typeof content === 'string' && !template) { //try to find by page name in content var pageNameMatch = content.match(/(data-page=["'][^"^']*["'])/); if (pageNameMatch) { var page = pageNameMatch[0].split('data-page=')[1].replace(/['"]/g, ''); if (page) t7_ctx = t7.data['page:' + page]; } } if (!t7_ctx && template && t7.templates) { // Try to find matched template name in t7.templates for (var templateName in t7.templates) { if (t7.templates[templateName] === template) t7_ctx = t7.data[templateName]; } } if (!t7_ctx && url && url in view.contextCache) { t7_ctx = view.contextCache[url]; } if (!t7_ctx) { t7_ctx = {}; } } if (t7_template && t7_ctx) { if (typeof t7_ctx === 'function') t7_ctx = t7_ctx(); if (url) { // Extend data with URL query var query = $.parseUrlQuery(url); t7_ctx.url_query = {}; for (var key in query) { t7_ctx.url_query[key] = query[key]; } } try { t7_rendered_content = t7_template(t7_ctx); } catch (e) { t7_rendered_content = ''; if (window.console && window.console.error) { console.error(e); } } } return {content: t7_rendered_content, context: t7_ctx}; } }; app.router._load = function (view, options) { options = options || {}; var url = options.url, content = options.content, //initial content t7_rendered = {content: options.content}, template = options.template, // Template 7 compiled template pageName = options.pageName, viewContainer = $(view.container), pagesContainer = $(view.pagesContainer), animatePages = options.animatePages, newPage, oldPage, pagesInView, i, oldNavbarInner, newNavbarInner, navbar, dynamicNavbar, reloadPosition, isDynamicPage = typeof url === 'undefined' && content || template, pushState = options.pushState, pageElement = options.pageElement; if (typeof animatePages === 'undefined') animatePages = view.params.animatePages; // Plugin hook app.pluginHook('routerLoad', view, options); // Render with Template7 if (app.params.template7Pages && typeof content === 'string' || template) { t7_rendered = app.router.template7Render(view, options); if (t7_rendered.content && !content) { content = t7_rendered.content; } } app.router.temporaryDom.innerHTML = ''; // Parse DOM if (!pageName && !pageElement) { if ((typeof content === 'string') || (url && (typeof content === 'string'))) { app.router.temporaryDom.innerHTML = t7_rendered.content; } else { if ('length' in content && content.length > 1) { for (var ci = 0; ci < content.length; ci++) { $(app.router.temporaryDom).append(content[ci]); } } else { $(app.router.temporaryDom).append(content); } } } // Reload position reloadPosition = options.reload && (options.reloadPrevious ? 'left' : 'center'); // Find new page if (pageName) newPage = pagesContainer.find('.page[data-page="' + pageName + '"]'); else { if (pageElement) newPage = $(pageElement); else newPage = app.router.findElement('.page', app.router.temporaryDom, view); } // If page not found exit if (!newPage || newPage.length === 0 || (pageName && view.activePage && view.activePage.name === pageName)) { view.allowPageChange = true; return; } newPage.addClass(options.reload ? 'page-on-' + reloadPosition : 'page-on-right'); // Find old page (should be the last one) and remove older pages pagesInView = pagesContainer.children('.page:not(.cached)'); if (pageElement) { pagesInView = pagesInView.filter(function (index, page) { if (page !== pageElement) return page; }); } if (options.reload && options.reloadPrevious && pagesInView.length === 1) { view.allowPageChange = true; return; } if (options.reload) { oldPage = pagesInView.eq(pagesInView.length - 1); } else { if (pagesInView.length > 1) { for (i = 0; i < pagesInView.length - 2; i++) { if (!view.params.domCache) { app.pageRemoveCallback(view, pagesInView[i], 'left'); app.router._remove(pagesInView[i]); } else { $(pagesInView[i]).addClass('cached'); } } if (!view.params.domCache) { app.pageRemoveCallback(view, pagesInView[i], 'left'); app.router._remove(pagesInView[i]); } else { $(pagesInView[i]).addClass('cached'); } } oldPage = pagesContainer.children('.page:not(.cached)'); } if (pageElement && oldPage.length > 1) { oldPage = oldPage.filter(function (index, page) { if (page !== pageElement) return page; }); } if(view.params.domCache || pageElement) newPage.removeClass('cached'); // Dynamic navbar if (view.params.dynamicNavbar) { dynamicNavbar = true; // Find navbar if (pageName) { newNavbarInner = viewContainer.find('.navbar-inner[data-page="' + pageName + '"]'); } else { newNavbarInner = app.router.findElement('.navbar-inner', app.router.temporaryDom, view); } if (!newNavbarInner || newNavbarInner.length === 0) { // Look in page newNavbarInner = newPage.find('.navbar-inner'); if (!newNavbarInner || newNavbarInner.length === 0) { // Set false dynamicNavbar = false; } else { if (newNavbarInner.parent('.navbar').length > 0) { newNavbarInner.prependTo(newPage); } } } if (dynamicNavbar && newPage.find('.navbar').length > 0) { app.router._remove(newPage.find('.navbar')); } navbar = viewContainer.children('.navbar'); if (options.reload) { oldNavbarInner = navbar.find('.navbar-inner:not(.cached):last-child'); } else { oldNavbarInner = navbar.find('.navbar-inner:not(.cached)'); if (oldNavbarInner.length > 0) { for (i = 0; i < oldNavbarInner.length - 1; i++) { if (!view.params.domCache) { app.navbarRemoveCallback(view, pagesInView[i], navbar[0], oldNavbarInner[i]); app.router._remove(oldNavbarInner[i]); } else $(oldNavbarInner[i]).addClass('cached'); } if (!newNavbarInner && oldNavbarInner.length === 1) { if (!view.params.domCache) { app.navbarRemoveCallback(view, pagesInView[0], navbar[0], oldNavbarInner[0]); app.router._remove(oldNavbarInner[0]); } else $(oldNavbarInner[0]).addClass('cached'); } oldNavbarInner = navbar.find('.navbar-inner:not(.cached)'); } } } if (dynamicNavbar) { newNavbarInner.addClass(options.reload ? 'navbar-on-' + reloadPosition : 'navbar-on-right'); if(view.params.domCache || pageElement) newNavbarInner.removeClass('cached'); newPage[0].f7RelatedNavbar = newNavbarInner[0]; newNavbarInner[0].f7RelatedPage = newPage[0]; } // save content areas into view's cache if (!url) { var newPageName = pageName || newPage.attr('data-page'); if (isDynamicPage) url = '#' + app.params.dynamicPageUrl.replace(/{{name}}/g, newPageName).replace(/{{index}}/g, view.history.length - (options.reload ? 1 : 0)); else url = '#' + newPageName; if (!view.params.domCache) { view.contentCache[url] = content; } if (view.params.domCache && pageName) { view.pagesCache[url] = pageName; } } else if (url && pageElement) { view.pageElementsCache[url] = { page: newPage, navbarInner: newNavbarInner }; } // Push State if (app.params.pushState && !options.reloadPrevious && view.main) { if (typeof pushState === 'undefined') pushState = true; var pushStateRoot = app.params.pushStateRoot || ''; var method = options.reload ? 'replaceState' : 'pushState'; if (pushState) { if (!isDynamicPage && !pageName) { history[method]({url: url, viewIndex: app.views.indexOf(view)}, '', pushStateRoot + app.params.pushStateSeparator + url); } else if (isDynamicPage && content) { history[method]({content: typeof content === 'string' ? content : '', url: url, viewIndex: app.views.indexOf(view)}, '', pushStateRoot + app.params.pushStateSeparator + url); } else if (pageName) { history[method]({pageName: pageName, url: url, viewIndex: app.views.indexOf(view)}, '', pushStateRoot + app.params.pushStateSeparator + url); } } } // Update View history view.url = url; if (options.reload) { var lastUrl = view.history[view.history.length - (options.reloadPrevious ? 2 : 1)]; if (lastUrl && lastUrl.indexOf('#') === 0 && lastUrl in view.contentCache && lastUrl !== url && view.history.indexOf(lastUrl) === -1) { view.contentCache[lastUrl] = null; delete view.contentCache[lastUrl]; } else if (lastUrl && lastUrl in view.pageElementsCache && lastUrl !== url && (view.history.indexOf(lastUrl) === -1 || view.history.indexOf(lastUrl) === view.history.length - 1)) { view.pageElementsCache[lastUrl] = null; delete view.pageElementsCache[lastUrl]; } if (lastUrl && lastUrl in view.contextCache && lastUrl !== url && (view.history.indexOf(lastUrl) === -1 || view.history.indexOf(lastUrl) === view.history.length - 1)) { view.contextCache[lastUrl] = null; delete view.contextCache[lastUrl]; } view.history[view.history.length - (options.reloadPrevious ? 2 : 1)] = url; } else { view.history.push(url); } // Unique history var historyBecameUnique = false; if (view.params.uniqueHistory) { var _history = view.history; var _url = url; if (view.params.uniqueHistoryIgnoreGetParameters) { _history = []; _url = url.split('?')[0]; for (i = 0; i < view.history.length; i++) { _history.push(view.history[i].split('?')[0]); } } if (_history.indexOf(_url) !== _history.lastIndexOf(_url)) { view.history = view.history.slice(0, _history.indexOf(_url)); view.history.push(url); historyBecameUnique = true; } } // Dom manipulations if (options.reloadPrevious) { oldPage = oldPage.prev('.page'); newPage.insertBefore(oldPage); if (dynamicNavbar) { oldNavbarInner = oldNavbarInner.prev('.navbar-inner'); newNavbarInner.insertAfter(oldNavbarInner); } } else { pagesContainer.append(newPage[0]); if (dynamicNavbar) navbar.append(newNavbarInner[0]); } // Remove Old Page And Navbar if (options.reload) { if (view.params.domCache && view.initialPages.indexOf(oldPage[0]) >= 0) { oldPage.addClass('cached'); if (dynamicNavbar) oldNavbarInner.addClass('cached'); } else { app.pageRemoveCallback(view, oldPage[0], reloadPosition); if (dynamicNavbar) app.navbarRemoveCallback(view, oldPage[0], navbar[0], oldNavbarInner[0]); app.router._remove(oldPage); if (dynamicNavbar) app.router._remove(oldNavbarInner); } } // Page Init Events app.pageInitCallback(view, { pageContainer: newPage[0], url: url, position: options.reload ? reloadPosition : 'right', navbarInnerContainer: dynamicNavbar ? newNavbarInner && newNavbarInner[0] : undefined, oldNavbarInnerContainer: dynamicNavbar ? oldNavbarInner && oldNavbarInner[0] : undefined, context: t7_rendered.context, query: options.query, fromPage: oldPage && oldPage.length && oldPage[0].f7PageData, reload: options.reload, reloadPrevious: options.reloadPrevious }); // Navbar init event if (dynamicNavbar) { app.navbarInitCallback(view, newPage[0], navbar[0], newNavbarInner[0], url, options.reload ? reloadPosition : 'right'); } if (options.reload) { view.allowPageChange = true; if (historyBecameUnique) view.refreshPreviousPage(); return; } if (dynamicNavbar && animatePages) { app.router.prepareNavbar(newNavbarInner, oldNavbarInner, 'right'); } // Force reLayout var clientLeft = newPage[0].clientLeft; // Before Anim Callback app.pageAnimCallback('before', view, { pageContainer: newPage[0], url: url, position: 'right', oldPage: oldPage, newPage: newPage, query: options.query, fromPage: oldPage && oldPage.length && oldPage[0].f7PageData }); function afterAnimation() { view.allowPageChange = true; newPage.removeClass('page-from-right-to-center page-on-right page-on-left').addClass('page-on-center'); oldPage.removeClass('page-from-center-to-left page-on-center page-on-right').addClass('page-on-left'); if (dynamicNavbar) { newNavbarInner.removeClass('navbar-from-right-to-center navbar-on-left navbar-on-right').addClass('navbar-on-center'); oldNavbarInner.removeClass('navbar-from-center-to-left navbar-on-center navbar-on-right').addClass('navbar-on-left'); } app.pageAnimCallback('after', view, { pageContainer: newPage[0], url: url, position: 'right', oldPage: oldPage, newPage: newPage, query: options.query, fromPage: oldPage && oldPage.length && oldPage[0].f7PageData }); if (app.params.pushState && view.main) app.pushStateClearQueue(); if (!(view.params.swipeBackPage || view.params.preloadPreviousPage)) { if (view.params.domCache) { oldPage.addClass('cached'); if (dynamicNavbar) oldNavbarInner.addClass('cached'); } else { if (!(url.indexOf('#') === 0 && newPage.attr('data-page').indexOf('smart-select-') === 0)) { app.pageRemoveCallback(view, oldPage[0], 'left'); if (dynamicNavbar) app.navbarRemoveCallback(view, oldPage[0], navbar[0], oldNavbarInner[0]); app.router._remove(oldPage); if (dynamicNavbar) app.router._remove(oldNavbarInner); } } } if (view.params.uniqueHistory && historyBecameUnique) { view.refreshPreviousPage(); } } if (animatePages) { // Set pages before animation if (app.params.material && app.params.materialPageLoadDelay) { setTimeout(function () { app.router.animatePages(oldPage, newPage, 'to-left', view); }, app.params.materialPageLoadDelay); } else { app.router.animatePages(oldPage, newPage, 'to-left', view); } // Dynamic navbar animation if (dynamicNavbar) { setTimeout(function() { app.router.animateNavbars(oldNavbarInner, newNavbarInner, 'to-left', view); }, 0); } newPage.animationEnd(function (e) { afterAnimation(); }); } else { if (dynamicNavbar) newNavbarInner.find('.sliding, .sliding .back .icon').transform(''); afterAnimation(); } }; app.router.load = function (view, options) { options = options || {}; if (app.router.preroute(view, options)) { return false; } var url = options.url; var content = options.content; var pageName = options.pageName; var pageElement = options.pageElement; if (pageName) { if (pageName.indexOf('?') > 0) { options.query = $.parseUrlQuery(pageName); options.pageName = pageName = pageName.split('?')[0]; } } var template = options.template; if (view.params.reloadPages === true) options.reload = true; if (!view.allowPageChange) return false; if (url && view.url === url && !options.reload && !view.params.allowDuplicateUrls) return false; view.allowPageChange = false; if (app.xhr && view.xhr && view.xhr === app.xhr) { app.xhr.abort(); app.xhr = false; } function proceed(content) { app.router.preprocess(view, content, url, function (content) { options.content = content; app.router._load(view, options); }); } if (content || pageName || pageElement) { proceed(content); return; } else if (template) { app.router._load(view, options); return; } if (!options.url || options.url === '#') { view.allowPageChange = true; return; } app.get(options.url, view, options.ignoreCache, function (content, error) { if (error) { view.allowPageChange = true; return; } proceed(content); }); }; app.router._back = function (view, options) { options = options || {}; var url = options.url, content = options.content, t7_rendered = {content: options.content}, // will be rendered using Template7 template = options.template, // Template 7 compiled template animatePages = options.animatePages, preloadOnly = options.preloadOnly, pushState = options.pushState, ignoreCache = options.ignoreCache, force = options.force, pageName = options.pageName, pageElement = options.pageElement; var viewContainer = $(view.container), pagesContainer = $(view.pagesContainer), pagesInView = pagesContainer.children('.page:not(.cached)'), oldPage, newPage, oldNavbarInner, newNavbarInner, navbar, navbarInners, dynamicNavbar, manipulateDom = true; if (typeof animatePages === 'undefined') animatePages = view.params.animatePages; app.pluginHook('routerBack', view, options); // Render with Template7 if (app.params.template7Pages && typeof content === 'string' || template) { t7_rendered = app.router.template7Render(view, options); if (t7_rendered.content && !content) { content = t7_rendered.content; } } // Animation function afterAnimation() { app.pageBackCallback('after', view, { pageContainer: oldPage[0], url: url, position: 'center', oldPage: oldPage, newPage: newPage, }); app.pageAnimCallback('after', view, { pageContainer: newPage[0], url: url, position: 'left', oldPage: oldPage, newPage: newPage, query: options.query, fromPage: oldPage && oldPage.length && oldPage[0].f7PageData }); app.router.afterBack(view, oldPage[0], newPage[0]); } function animateBack() { // Page before animation callback app.pageBackCallback('before', view, { pageContainer: oldPage[0], url: url, position: 'center', oldPage: oldPage, newPage: newPage, }); app.pageAnimCallback('before', view, { pageContainer: newPage[0], url: url, position: 'left', oldPage: oldPage, newPage: newPage, query: options.query, fromPage: oldPage && oldPage.length && oldPage[0].f7PageData }); if (animatePages) { // Set pages before animation app.router.animatePages(newPage, oldPage, 'to-right', view); // Dynamic navbar animation if (dynamicNavbar) { setTimeout(function () { app.router.animateNavbars(newNavbarInner, oldNavbarInner, 'to-right', view); }, 0); } newPage.animationEnd(function () { afterAnimation(); }); } else { if (dynamicNavbar) newNavbarInner.find('.sliding, .sliding .back .icon').transform(''); afterAnimation(); } } function parseNewPage() { app.router.temporaryDom.innerHTML = ''; // Parse DOM if ((typeof content === 'string') || (url && (typeof content === 'string'))) { app.router.temporaryDom.innerHTML = t7_rendered.content; } else { if ('length' in content && content.length > 1) { for (var ci = 0; ci < content.length; ci++) { $(app.router.temporaryDom).append(content[ci]); } } else { $(app.router.temporaryDom).append(content); } } if (pageElement) newPage = $(pageElement); else newPage = app.router.findElement('.page', app.router.temporaryDom, view); if (view.params.dynamicNavbar) { // Find navbar newNavbarInner = app.router.findElement('.navbar-inner', app.router.temporaryDom, view); } } function setPages() { // If pages not found or there are still more than one, exit if (!newPage || newPage.length === 0) { view.allowPageChange = true; return; } if (view.params.dynamicNavbar && typeof dynamicNavbar === 'undefined') { if (!newNavbarInner || newNavbarInner.length === 0) { dynamicNavbar = false; } else { dynamicNavbar = true; } } newPage.addClass('page-on-left').removeClass('cached'); if (dynamicNavbar) { navbar = viewContainer.children('.navbar'); navbarInners = navbar.find('.navbar-inner:not(.cached)'); newNavbarInner.addClass('navbar-on-left').removeClass('cached'); } // Remove/hide previous page in force mode if (force) { var pageToRemove, navbarToRemove; pageToRemove = $(pagesInView[pagesInView.length - 2]); if (dynamicNavbar) navbarToRemove = $(pageToRemove[0] && pageToRemove[0].f7RelatedNavbar || navbarInners[navbarInners.length - 2]); if (view.params.domCache && view.initialPages.indexOf(pageToRemove[0]) >= 0) { if (pageToRemove.length && pageToRemove[0] !== newPage[0]) pageToRemove.addClass('cached'); if (dynamicNavbar && navbarToRemove.length && navbarToRemove[0] !== newNavbarInner[0]) { navbarToRemove.addClass('cached'); } } else { var removeNavbar = dynamicNavbar && navbarToRemove.length; if (pageToRemove.length) { app.pageRemoveCallback(view, pageToRemove[0], 'right'); if (removeNavbar) { app.navbarRemoveCallback(view, pageToRemove[0], navbar[0], navbarToRemove[0]); } app.router._remove(pageToRemove); if (removeNavbar) app.router._remove(navbarToRemove); } else if (removeNavbar) { app.navbarRemoveCallback(view, pageToRemove[0], navbar[0], navbarToRemove[0]); app.router._remove(navbarToRemove); } } pagesInView = pagesContainer.children('.page:not(.cached)'); if (dynamicNavbar) { navbarInners = viewContainer.children('.navbar').find('.navbar-inner:not(.cached)'); } if (view.history.indexOf(url) >= 0) { view.history = view.history.slice(0, view.history.indexOf(url) + 2); } else { if (view.history[[view.history.length - 2]]) { view.history[view.history.length - 2] = url; } else { view.history.unshift(url); } } } oldPage = $(pagesInView[pagesInView.length - 1]); if (view.params.domCache) { if (oldPage[0] === newPage[0]) { oldPage = pagesContainer.children('.page.page-on-center'); if (oldPage.length === 0 && view.activePage) oldPage = $(view.activePage.container); } } if (dynamicNavbar && !oldNavbarInner) { oldNavbarInner = $(navbarInners[navbarInners.length - 1]); if (view.params.domCache) { if (oldNavbarInner[0] === newNavbarInner[0]) { oldNavbarInner = navbar.children('.navbar-inner.navbar-on-center:not(.cached)'); } if (oldNavbarInner.length === 0) { oldNavbarInner = navbar.children('.navbar-inner[data-page="'+oldPage.attr('data-page')+'"]'); } } if (oldNavbarInner.length === 0 || newNavbarInner[0] === oldNavbarInner[0]) dynamicNavbar = false; } if (dynamicNavbar) { if (manipulateDom) newNavbarInner.insertBefore(oldNavbarInner); newNavbarInner[0].f7RelatedPage = newPage[0]; newPage[0].f7RelatedNavbar = newNavbarInner[0]; } if (manipulateDom) newPage.insertBefore(oldPage); // Page Init Events app.pageInitCallback(view, { pageContainer: newPage[0], url: url, position: 'left', navbarInnerContainer: dynamicNavbar ? newNavbarInner[0] : undefined, oldNavbarInnerContainer: dynamicNavbar ? oldNavbarInner && oldNavbarInner[0] : undefined, context: t7_rendered.context, query: options.query, fromPage: oldPage && oldPage.length && oldPage[0].f7PageData, preloadOnly: preloadOnly }); if (dynamicNavbar) { app.navbarInitCallback(view, newPage[0], navbar[0], newNavbarInner[0], url, 'right'); } if (dynamicNavbar && newNavbarInner.hasClass('navbar-on-left') && animatePages) { app.router.prepareNavbar(newNavbarInner, oldNavbarInner, 'left'); } if (preloadOnly) { view.allowPageChange = true; return; } // Update View's URL view.url = url; // Force reLayout var clientLeft = newPage[0].clientLeft; animateBack(); // Push state if (app.params.pushState && view.main) { if (typeof pushState === 'undefined') pushState = true; if (!preloadOnly && history.state && pushState) { history.back(); } } return; } // Simple go back when we have pages on left if (pagesInView.length > 1 && !force) { // Exit if only preloadOnly if (preloadOnly) { view.allowPageChange = true; return; } // Update View's URL view.url = view.history[view.history.length - 2]; url = view.url; // Define old and new pages newPage = $(pagesInView[pagesInView.length - 2]); oldPage = $(pagesInView[pagesInView.length - 1]); // Dynamic navbar if (view.params.dynamicNavbar) { dynamicNavbar = true; // Find navbar navbarInners = viewContainer.children('.navbar').find('.navbar-inner:not(.cached)'); newNavbarInner = $(navbarInners[0]); oldNavbarInner = $(navbarInners[1]); if (newNavbarInner.length === 0 || oldNavbarInner.length === 0 || oldNavbarInner[0] === newNavbarInner[0]) { dynamicNavbar = false; } } manipulateDom = false; setPages(); return; } if (!force) { // Go back when there is no pages on left if (!preloadOnly) { view.url = view.history[view.history.length - 2]; url = view.url; } if (content) { parseNewPage(); setPages(); return; } else if (pageName) { // Get dom cached pages newPage = $(viewContainer).find('.page[data-page="' + pageName + '"]'); if (view.params.dynamicNavbar) { newNavbarInner = $(viewContainer).children('.navbar').find('.navbar-inner[data-page="' + pageName + '"]'); if (newNavbarInner.length === 0 && newPage[0].f7RelatedNavbar) { newNavbarInner = $(newPage[0].f7RelatedNavbar); } if (newNavbarInner.length === 0 && newPage[0].f7PageData) { newNavbarInner = $(newPage[0].f7PageData.navbarInnerContainer); } } setPages(); return; } else if (url && url in view.pageElementsCache) { newPage = view.pageElementsCache[url].page; newNavbarInner = view.pageElementsCache[url].navbarInner; setPages(); return; } else { view.allowPageChange = true; return; } } else { if (url && url === view.url || pageName && view.activePage && view.activePage.name === pageName) { view.allowPageChange = true; return; } // Go back with force url if (content) { parseNewPage(); setPages(); return; } else if (pageName && view.params.domCache) { if (pageName) url = '#' + pageName; newPage = $(viewContainer).find('.page[data-page="' + pageName + '"]'); if (newPage[0].f7PageData && newPage[0].f7PageData.url) { url = newPage[0].f7PageData.url; } if (view.params.dynamicNavbar) { newNavbarInner = $(viewContainer).children('.navbar').find('.navbar-inner[data-page="' + pageName + '"]'); if (newNavbarInner.length === 0 && newPage[0].f7RelatedNavbar) { newNavbarInner = $(newPage[0].f7RelatedNavbar); } if (newNavbarInner.length === 0 && newPage[0].f7PageData) { newNavbarInner = $(newPage[0].f7PageData.navbarInnerContainer); } } setPages(); return; } else if (pageElement && url) { newPage = $(pageElement); if (view.params.dynamicNavbar) { newNavbarInner = newPage.find('.navbar-inner'); if (newNavbarInner.length > 0) { newPage.prepend(newNavbarInner); app.router._remove(newPage.find('.navbar')); } } setPages(); return; } else { view.allowPageChange = true; return; } } }; app.router.back = function (view, options) { options = options || {}; if (app.router.preroute(view, options, true)) { return false; } var url = options.url; var content = options.content; var pageName = options.pageName; var pageElement = options.pageElement; if (pageName) { if (pageName.indexOf('?') > 0) { options.query = $.parseUrlQuery(pageName); options.pageName = pageName = pageName.split('?')[0]; } } var force = options.force; if (!view.allowPageChange) return false; view.allowPageChange = false; if (app.xhr && view.xhr && view.xhr === app.xhr) { app.xhr.abort(); app.xhr = false; } var pagesInView = $(view.pagesContainer).find('.page:not(.cached)'); function proceed(content) { app.router.preprocess(view, content, url, function (content) { options.content = content; app.router._back(view, options); }); } if (pagesInView.length > 1 && !force) { // Simple go back to previos page in view app.router._back(view, options); return; } if (!force) { url = view.history[view.history.length - 2] || options.url; if (!options.url) options.url = url; if (!url) { view.allowPageChange = true; return; } if (url.indexOf('#') === 0 && view.contentCache[url]) { proceed(view.contentCache[url]); return; } else if (url.indexOf('#') === 0 && view.params.domCache) { if (!pageName) options.pageName = url.split('#')[1]; proceed(); return; } else if (url && url in view.pageElementsCache) { proceed(); } else if (url.indexOf('#') !== 0) { // Load ajax page app.get(options.url, view, options.ignoreCache, function (content, error) { if (error) { view.allowPageChange = true; return; } proceed(content); }); return; } } else { // Go back with force url if (!url && content) { proceed(content); return; } else if (!url && pageName) { if (pageName) url = '#' + pageName; proceed(); return; } else if (url && pageElement) { proceed(); return; } else if (url) { app.get(options.url, view, options.ignoreCache, function (content, error) { if (error) { view.allowPageChange = true; return; } proceed(content); }); return; } } view.allowPageChange = true; return; }; app.router.afterBack = function (view, oldPage, newPage) { // Remove old page and set classes on new one oldPage = $(oldPage); newPage = $(newPage); if (view.params.domCache && view.initialPages.indexOf(oldPage[0]) >= 0) { oldPage.removeClass('page-from-center-to-right').addClass('cached'); } else { app.pageRemoveCallback(view, oldPage[0], 'right'); app.router._remove(oldPage); } newPage.removeClass('page-from-left-to-center page-on-left').addClass('page-on-center'); view.allowPageChange = true; // Update View's History var previousURL = view.history.pop(); var newNavbar; // Updated dynamic navbar if (view.params.dynamicNavbar) { var inners = $(view.container).children('.navbar').find('.navbar-inner:not(.cached)'); var oldNavbar = $(oldPage[0].f7RelatedNavbar || inners[1]); if (view.params.domCache && view.initialNavbars.indexOf(oldNavbar[0]) >= 0) { oldNavbar.removeClass('navbar-from-center-to-right').addClass('cached'); } else { app.navbarRemoveCallback(view, oldPage[0], undefined, oldNavbar[0]); app.router._remove(oldNavbar); } newNavbar = $(inners[0]).removeClass('navbar-on-left navbar-from-left-to-center').addClass('navbar-on-center'); } // Remove pages in dom cache if (view.params.domCache) { $(view.container).find('.page.cached').each(function () { var page = $(this); var index = page.index(); var pageUrl = page[0].f7PageData && page[0].f7PageData.url; if (pageUrl && view.history.indexOf(pageUrl) < 0 && view.initialPages.indexOf(this) < 0) { app.pageRemoveCallback(view, page[0], 'right'); if (page[0].f7RelatedNavbar && view.params.dynamicNavbar) app.navbarRemoveCallback(view, page[0], undefined, page[0].f7RelatedNavbar); app.router._remove(page); if (page[0].f7RelatedNavbar && view.params.dynamicNavbar) app.router._remove(page[0].f7RelatedNavbar); } }); } // Check previous page is content based only and remove it from content cache if (!view.params.domCache && previousURL && previousURL.indexOf('#') > -1 && (previousURL in view.contentCache) && // If the same page is in the history multiple times, don't remove it. view.history.indexOf(previousURL) === -1) { view.contentCache[previousURL] = null; delete view.contentCache[previousURL]; } if (previousURL && (previousURL in view.pageElementsCache) && // If the same page is in the history multiple times, don't remove it. view.history.indexOf(previousURL) === -1) { view.pageElementsCache[previousURL] = null; delete view.pageElementsCache[previousURL]; } // Check for context cache if (previousURL && (previousURL in view.contextCache) && // If the same page is in the history multiple times, don't remove it. view.history.indexOf(previousURL) === -1) { view.contextCache[previousURL] = null; delete view.contextCache[previousURL]; } if (app.params.pushState && view.main) app.pushStateClearQueue(); // Preload previous page if (view.params.preloadPreviousPage) { if (view.params.domCache && view.history.length > 1) { var preloadUrl = view.history[view.history.length - 2]; var previousPage; var previousNavbar; if (preloadUrl && view.pagesCache[preloadUrl]) { // Load by page name previousPage = $(view.container).find('.page[data-page="' + view.pagesCache[preloadUrl] + '"]'); if (previousPage.next('.page')[0] !== newPage[0]) previousPage.insertBefore(newPage); if (newNavbar) { previousNavbar = $(view.container).children('.navbar').find('.navbar-inner[data-page="' + view.pagesCache[preloadUrl] + '"]'); if(!previousNavbar || previousNavbar.length === 0) previousNavbar = newNavbar.prev('.navbar-inner.cached'); if (previousNavbar.next('.navbar-inner')[0] !== newNavbar[0]) previousNavbar.insertBefore(newNavbar); } } else { // Just load previous page previousPage = newPage.prev('.page.cached'); if (newNavbar) previousNavbar = newNavbar.prev('.navbar-inner.cached'); } if (previousPage && previousPage.length > 0) previousPage.removeClass('cached page-on-right page-on-center').addClass('page-on-left'); if (previousNavbar && previousNavbar.length > 0) previousNavbar.removeClass('cached navbar-on-right navbar-on-center').addClass('navbar-on-left'); } else { app.router.back(view, {preloadOnly: true}); } } }; /*====================================================== ************ Modals ************ ======================================================*/ var _modalTemplateTempDiv = document.createElement('div'); app.modalStack = []; app.modalStackClearQueue = function () { if (app.modalStack.length) { (app.modalStack.shift())(); } }; app.modal = function (params) { params = params || {}; var modalHTML = ''; if (app.params.modalTemplate) { if (!app._compiledTemplates.modal) app._compiledTemplates.modal = t7.compile(app.params.modalTemplate); modalHTML = app._compiledTemplates.modal(params); } else { var buttonsHTML = ''; if (params.buttons && params.buttons.length > 0) { for (var i = 0; i < params.buttons.length; i++) { buttonsHTML += '' + params.buttons[i].text + ''; } } var titleHTML = params.title ? '' : ''; var textHTML = params.text ? '' : ''; var afterTextHTML = params.afterText ? params.afterText : ''; var noButtons = !params.buttons || params.buttons.length === 0 ? 'modal-no-buttons' : ''; var verticalButtons = params.verticalButtons ? 'modal-buttons-vertical': ''; var modalButtonsHTML = params.buttons && params.buttons.length > 0 ? '' : ''; modalHTML = ''; } _modalTemplateTempDiv.innerHTML = modalHTML; var modal = $(_modalTemplateTempDiv).children(); app.root.append(modal[0]); // Add events on buttons modal.find('.modal-button').each(function (index, el) { $(el).on('click', function (e) { if (params.buttons[index].close !== false) app.closeModal(modal); if (params.buttons[index].onClick) params.buttons[index].onClick(modal, e); if (params.onClick) params.onClick(modal, index); }); }); app.openModal(modal); return modal[0]; }; app.alert = function (text, title, callbackOk) { if (typeof title === 'function') { callbackOk = arguments[1]; title = undefined; } return app.modal({ text: text || '', title: typeof title === 'undefined' ? app.params.modalTitle : title, buttons: [ {text: app.params.modalButtonOk, bold: true, onClick: callbackOk} ] }); }; app.confirm = function (text, title, callbackOk, callbackCancel) { if (typeof title === 'function') { callbackCancel = arguments[2]; callbackOk = arguments[1]; title = undefined; } return app.modal({ text: text || '', title: typeof title === 'undefined' ? app.params.modalTitle : title, buttons: [ {text: app.params.modalButtonCancel, onClick: callbackCancel}, {text: app.params.modalButtonOk, bold: true, onClick: callbackOk} ] }); }; app.prompt = function (text, title, callbackOk, callbackCancel) { if (typeof title === 'function') { callbackCancel = arguments[2]; callbackOk = arguments[1]; title = undefined; } return app.modal({ text: text || '', title: typeof title === 'undefined' ? app.params.modalTitle : title, afterText: '
', buttons: [ { text: app.params.modalButtonCancel }, { text: app.params.modalButtonOk, bold: true } ], onClick: function (modal, index) { if (index === 0 && callbackCancel) callbackCancel($(modal).find('.modal-text-input').val()); if (index === 1 && callbackOk) callbackOk($(modal).find('.modal-text-input').val()); } }); }; app.modalLogin = function (text, title, callbackOk, callbackCancel) { if (typeof title === 'function') { callbackCancel = arguments[2]; callbackOk = arguments[1]; title = undefined; } return app.modal({ text: text || '', title: typeof title === 'undefined' ? app.params.modalTitle : title, afterText: '', buttons: [ { text: app.params.modalButtonCancel }, { text: app.params.modalButtonOk, bold: true } ], onClick: function (modal, index) { var username = $(modal).find('.modal-text-input[name="modal-username"]').val(); var password = $(modal).find('.modal-text-input[name="modal-password"]').val(); if (index === 0 && callbackCancel) callbackCancel(username, password); if (index === 1 && callbackOk) callbackOk(username, password); } }); }; app.modalPassword = function (text, title, callbackOk, callbackCancel) { if (typeof title === 'function') { callbackCancel = arguments[2]; callbackOk = arguments[1]; title = undefined; } return app.modal({ text: text || '', title: typeof title === 'undefined' ? app.params.modalTitle : title, afterText: '
', buttons: [ { text: app.params.modalButtonCancel }, { text: app.params.modalButtonOk, bold: true } ], onClick: function (modal, index) { var password = $(modal).find('.modal-text-input[name="modal-password"]').val(); if (index === 0 && callbackCancel) callbackCancel(password); if (index === 1 && callbackOk) callbackOk(password); } }); }; app.showPreloader = function (title) { return app.modal({ title: title || app.params.modalPreloaderTitle, text: '
' + (app.params.material ? app.params.materialPreloaderHtml : '') + '
', cssClass: 'modal-preloader' }); }; app.hidePreloader = function () { app.closeModal('.modal.modal-in'); }; app.showIndicator = function () { if ($('.preloader-indicator-overlay').length > 0) return; app.root.append('
' + (app.params.material ? app.params.materialPreloaderHtml : '') + '
'); }; app.hideIndicator = function () { $('.preloader-indicator-overlay, .preloader-indicator-modal').remove(); }; // Action Sheet app.actions = function (target, params) { var toPopover = false, modal, groupSelector, buttonSelector; if (arguments.length === 1) { // Actions params = target; } else { // Popover if (app.device.ios) { if (app.device.ipad) toPopover = true; } else { if ($(window).width() >= 768) toPopover = true; } } params = params || []; if (params.length > 0 && !$.isArray(params[0])) { params = [params]; } var modalHTML; if (toPopover) { var actionsToPopoverTemplate = app.params.modalActionsToPopoverTemplate || '
' + '
' + '{{#each this}}' + '
' + '
    ' + '{{#each this}}' + '{{#if label}}' + '
  • {{text}}
  • ' + '{{else}}' + '
  • {{text}}
  • ' + '{{/if}}' + '{{/each}}' + '
' + '
' + '{{/each}}' + '
' + '
'; if (!app._compiledTemplates.actionsToPopover) { app._compiledTemplates.actionsToPopover = t7.compile(actionsToPopoverTemplate); } var popoverHTML = app._compiledTemplates.actionsToPopover(params); modal = $(app.popover(popoverHTML, target, true)); groupSelector = '.list-block ul'; buttonSelector = '.list-button'; } else { if (app.params.modalActionsTemplate) { if (!app._compiledTemplates.actions) app._compiledTemplates.actions = t7.compile(app.params.modalActionsTemplate); modalHTML = app._compiledTemplates.actions(params); } else { var buttonsHTML = ''; for (var i = 0; i < params.length; i++) { for (var j = 0; j < params[i].length; j++) { if (j === 0) buttonsHTML += '
'; var button = params[i][j]; var buttonClass = button.label ? 'actions-modal-label' : 'actions-modal-button'; if (button.bold) buttonClass += ' actions-modal-button-bold'; if (button.color) buttonClass += ' color-' + button.color; if (button.bg) buttonClass += ' bg-' + button.bg; if (button.disabled) buttonClass += ' disabled'; buttonsHTML += '
' + button.text + '
'; if (j === params[i].length - 1) buttonsHTML += '
'; } } modalHTML = '
' + buttonsHTML + '
'; } _modalTemplateTempDiv.innerHTML = modalHTML; modal = $(_modalTemplateTempDiv).children(); app.root.append(modal[0]); groupSelector = '.actions-modal-group'; buttonSelector = '.actions-modal-button'; } var groups = modal.find(groupSelector); groups.each(function (index, el) { var groupIndex = index; $(el).children().each(function (index, el) { var buttonIndex = index; var buttonParams = params[groupIndex][buttonIndex]; var clickTarget; if (!toPopover && $(el).is(buttonSelector)) clickTarget = $(el); if (toPopover && $(el).find(buttonSelector).length > 0) clickTarget = $(el).find(buttonSelector); if (clickTarget) { clickTarget.on('click', function (e) { if (buttonParams.close !== false) app.closeModal(modal); if (buttonParams.onClick) buttonParams.onClick(modal, e); }); } }); }); if (!toPopover) app.openModal(modal); return modal[0]; }; app.popover = function (modal, target, removeOnClose) { if (typeof removeOnClose === 'undefined') removeOnClose = true; if (typeof modal === 'string' && modal.indexOf('<') >= 0) { var _modal = document.createElement('div'); _modal.innerHTML = modal.trim(); if (_modal.childNodes.length > 0) { modal = _modal.childNodes[0]; if (removeOnClose) modal.classList.add('remove-on-close'); app.root.append(modal); } else return false; //nothing found } modal = $(modal); target = $(target); if (modal.length === 0 || target.length === 0) return false; if (modal.parents('body').length === 0) { if (removeOnClose) modal.addClass('remove-on-close'); app.root.append(modal[0]); } if (modal.find('.popover-angle').length === 0 && !app.params.material) { modal.append('
'); } modal.show(); var material = app.params.material; function sizePopover() { modal.css({left: '', top: ''}); var modalWidth = modal.width(); var modalHeight = modal.height(); // 13 - height of angle var modalAngle, modalAngleSize = 0, modalAngleLeft, modalAngleTop; if (!material) { modalAngle = modal.find('.popover-angle'); modalAngleSize = modalAngle.width() / 2; modalAngle.removeClass('on-left on-right on-top on-bottom').css({left: '', top: ''}); } else { modal.removeClass('popover-on-left popover-on-right popover-on-top popover-on-bottom').css({left: '', top: ''}); } var targetWidth = target.outerWidth(); var targetHeight = target.outerHeight(); var targetOffset = target.offset(); var targetParentPage = target.parents('.page'); if (targetParentPage.length > 0) { targetOffset.top = targetOffset.top - targetParentPage[0].scrollTop; } var windowHeight = $(window).height(); var windowWidth = $(window).width(); var modalTop = 0; var modalLeft = 0; var diff = 0; // Top Position var modalPosition = material ? 'bottom' : 'top'; if (material) { if (modalHeight < windowHeight - targetOffset.top - targetHeight) { // On bottom modalPosition = 'bottom'; modalTop = targetOffset.top; } else if (modalHeight < targetOffset.top) { // On top modalTop = targetOffset.top - modalHeight + targetHeight; modalPosition = 'top'; } else { // On middle modalPosition = 'bottom'; modalTop = targetOffset.top; } if (modalTop <= 0) { modalTop = 8; } else if (modalTop + modalHeight >= windowHeight) { modalTop = windowHeight - modalHeight - 8; } // Horizontal Position modalLeft = targetOffset.left; if (modalLeft + modalWidth >= windowWidth - 8) { modalLeft = targetOffset.left + targetWidth - modalWidth - 8; } if (modalLeft < 8) { modalLeft = 8; } if (modalPosition === 'top') { modal.addClass('popover-on-top'); } if (modalPosition === 'bottom') { modal.addClass('popover-on-bottom'); } if (target.hasClass('floating-button-to-popover') && !modal.hasClass('modal-in')) { modal.addClass('popover-floating-button'); var diffX = (modalLeft + modalWidth / 2) - (targetOffset.left + targetWidth / 2), diffY = (modalTop + modalHeight / 2) - (targetOffset.top + targetHeight / 2); target .addClass('floating-button-to-popover-in') .transform('translate3d(' + diffX + 'px, ' + diffY + 'px,0)') .transitionEnd(function (e) { if (!target.hasClass('floating-button-to-popover-in')) return; target .addClass('floating-button-to-popover-scale') .transform('translate3d(' + diffX + 'px, ' + diffY + 'px,0) scale(' + (modalWidth/targetWidth) + ', ' + (modalHeight/targetHeight) + ')'); }); modal.once('close', function () { target .removeClass('floating-button-to-popover-in floating-button-to-popover-scale') .addClass('floating-button-to-popover-out') .transform('') .transitionEnd(function (e) { target.removeClass('floating-button-to-popover-out'); }); }); modal.once('closed', function () { modal.removeClass('popover-floating-button'); }); } } else { if ((modalHeight + modalAngleSize) < targetOffset.top) { // On top modalTop = targetOffset.top - modalHeight - modalAngleSize; } else if ((modalHeight + modalAngleSize) < windowHeight - targetOffset.top - targetHeight) { // On bottom modalPosition = 'bottom'; modalTop = targetOffset.top + targetHeight + modalAngleSize; } else { // On middle modalPosition = 'middle'; modalTop = targetHeight / 2 + targetOffset.top - modalHeight / 2; diff = modalTop; if (modalTop <= 0) { modalTop = 5; } else if (modalTop + modalHeight >= windowHeight) { modalTop = windowHeight - modalHeight - 5; } diff = diff - modalTop; } // Horizontal Position if (modalPosition === 'top' || modalPosition === 'bottom') { modalLeft = targetWidth / 2 + targetOffset.left - modalWidth / 2; diff = modalLeft; if (modalLeft < 5) modalLeft = 5; if (modalLeft + modalWidth > windowWidth) modalLeft = windowWidth - modalWidth - 5; if (modalPosition === 'top') { modalAngle.addClass('on-bottom'); } if (modalPosition === 'bottom') { modalAngle.addClass('on-top'); } diff = diff - modalLeft; modalAngleLeft = (modalWidth / 2 - modalAngleSize + diff); modalAngleLeft = Math.max(Math.min(modalAngleLeft, modalWidth - modalAngleSize * 2 - 13), 13); modalAngle.css({left: modalAngleLeft + 'px'}); } else if (modalPosition === 'middle') { modalLeft = targetOffset.left - modalWidth - modalAngleSize; modalAngle.addClass('on-right'); if (modalLeft < 5 || (modalLeft + modalWidth > windowWidth)) { if (modalLeft < 5) modalLeft = targetOffset.left + targetWidth + modalAngleSize; if (modalLeft + modalWidth > windowWidth) modalLeft = windowWidth - modalWidth - 5; modalAngle.removeClass('on-right').addClass('on-left'); } modalAngleTop = (modalHeight / 2 - modalAngleSize + diff); modalAngleTop = Math.max(Math.min(modalAngleTop, modalHeight - modalAngleSize * 2 - 13), 13); modalAngle.css({top: modalAngleTop + 'px'}); } } // Apply Styles modal.css({top: modalTop + 'px', left: modalLeft + 'px'}); } sizePopover(); $(window).on('resize', sizePopover); modal.on('close', function () { $(window).off('resize', sizePopover); }); app.openModal(modal); return modal[0]; }; app.popup = function (modal, removeOnClose) { if (typeof removeOnClose === 'undefined') removeOnClose = true; if (typeof modal === 'string' && modal.indexOf('<') >= 0) { var _modal = document.createElement('div'); _modal.innerHTML = modal.trim(); if (_modal.childNodes.length > 0) { modal = _modal.childNodes[0]; if (removeOnClose) modal.classList.add('remove-on-close'); app.root.append(modal); } else return false; //nothing found } modal = $(modal); if (modal.length === 0) return false; if (modal.parents('body').length === 0) { if (removeOnClose) modal.addClass('remove-on-close'); app.root.append(modal[0]); } modal.show(); app.openModal(modal); return modal[0]; }; app.pickerModal = function (modal, removeOnClose) { if (typeof removeOnClose === 'undefined') removeOnClose = true; if (typeof modal === 'string' && modal.indexOf('<') >= 0) { modal = $(modal); if (modal.length > 0) { if (removeOnClose) modal.addClass('remove-on-close'); app.root.append(modal[0]); } else return false; //nothing found } modal = $(modal); if (modal.length === 0) return false; if (modal.parents('body').length === 0) { if (removeOnClose) modal.addClass('remove-on-close'); app.root.append(modal[0]); } if ($('.picker-modal.modal-in:not(.modal-out)').length > 0 && !modal.hasClass('modal-in')) { app.closeModal('.picker-modal.modal-in:not(.modal-out)'); } modal.show(); app.openModal(modal); return modal[0]; }; app.loginScreen = function (modal) { if (!modal) modal = '.login-screen'; modal = $(modal); if (modal.length === 0) return false; if ($('.login-screen.modal-in:not(.modal-out)').length > 0 && !modal.hasClass('modal-in')) { app.closeModal('.login-screen.modal-in:not(.modal-out)'); } modal.show(); app.openModal(modal); return modal[0]; }; app.openModal = function (modal) { modal = $(modal); var isModal = modal.hasClass('modal'); if ($('.modal.modal-in:not(.modal-out)').length && app.params.modalStack && isModal) { app.modalStack.push(function () { app.openModal(modal); }); return; } // do nothing if this modal already shown if (true === modal.data('f7-modal-shown')) { return; } modal.data('f7-modal-shown', true); modal.once('close', function() { modal.removeData('f7-modal-shown'); }); var isPopover = modal.hasClass('popover'); var isPopup = modal.hasClass('popup'); var isLoginScreen = modal.hasClass('login-screen'); var isPickerModal = modal.hasClass('picker-modal'); if (isModal) { modal.show(); modal.css({ marginTop: - Math.round(modal.outerHeight() / 2) + 'px' }); } var overlay; if (!isLoginScreen && !isPickerModal) { if ($('.modal-overlay').length === 0 && !isPopup) { app.root.append(''); } if ($('.popup-overlay').length === 0 && isPopup) { app.root.append(''); } overlay = isPopup ? $('.popup-overlay') : $('.modal-overlay'); } if (app.params.material && isPickerModal) { if (modal.hasClass('picker-calendar')) { if ($('.picker-modal-overlay').length === 0 && !isPopup) { app.root.append('
'); } overlay = $('.picker-modal-overlay'); } } //Make sure that styles are applied, trigger relayout; var clientLeft = modal[0].clientLeft; // Trugger open event modal.trigger('open'); // Picker modal body class if (isPickerModal) { $('body').addClass('with-picker-modal'); } // Init Pages and Navbars in modal if (modal.find('.' + app.params.viewClass).length > 0) { modal.find('.page').each(function () { app.initPageWithCallback(this); }); modal.find('.navbar').each(function () { app.initNavbarWithCallback(this); }); } // Classes for transition in if (!isLoginScreen && !isPickerModal) overlay.addClass('modal-overlay-visible'); if (app.params.material && isPickerModal && overlay) overlay.addClass('modal-overlay-visible'); modal.removeClass('modal-out').addClass('modal-in').transitionEnd(function (e) { if (modal.hasClass('modal-out')) modal.trigger('closed'); else modal.trigger('opened'); }); return true; }; app.closeModal = function (modal) { modal = $(modal || '.modal-in'); if (typeof modal !== 'undefined' && modal.length === 0) { return; } var isModal = modal.hasClass('modal'); var isPopover = modal.hasClass('popover'); var isPopup = modal.hasClass('popup'); var isLoginScreen = modal.hasClass('login-screen'); var isPickerModal = modal.hasClass('picker-modal'); var removeOnClose = modal.hasClass('remove-on-close'); var overlay; if (isPopup) overlay = $('.popup-overlay'); else { if (isPickerModal && app.params.material) overlay = $('.picker-modal-overlay'); else if (!isPickerModal) overlay = $('.modal-overlay'); } if (isPopup){ if (modal.length === $('.popup.modal-in').length) { overlay.removeClass('modal-overlay-visible'); } } else if (overlay && overlay.length > 0) { overlay.removeClass('modal-overlay-visible'); } modal.trigger('close'); // Picker modal body class if (isPickerModal) { $('body').removeClass('with-picker-modal'); $('body').addClass('picker-modal-closing'); } if (!(isPopover && !app.params.material)) { modal.removeClass('modal-in').addClass('modal-out').transitionEnd(function (e) { if (modal.hasClass('modal-out')) modal.trigger('closed'); else { modal.trigger('opened'); if (isPopover) return; } if (isPickerModal) { $('body').removeClass('picker-modal-closing'); } if (isPopup || isLoginScreen || isPickerModal || isPopover) { modal.removeClass('modal-out').hide(); if (removeOnClose && modal.length > 0) { modal.remove(); } } else { modal.remove(); } }); if (isModal && app.params.modalStack) { app.modalStackClearQueue(); } } else { modal.removeClass('modal-in modal-out').trigger('closed').hide(); if (removeOnClose) { modal.remove(); } } return true; }; /*=============================================================================== ************ Progress Bar ************ ===============================================================================*/ app.setProgressbar = function (container, progress, speed) { container = $(container || app.root); if (container.length === 0) return; if (progress) progress = Math.min(Math.max(progress, 0), 100); var progressbar; if (container.hasClass('progressbar')) progressbar = container; else { progressbar = container.children('.progressbar'); } if (progressbar.length === 0 || progressbar.hasClass('progressbar-infinite')) return; var clientLeft = progressbar[0].clientLeft; progressbar.children('span').transform('translate3d(' + (-100 + progress) + '%,0,0)'); if (typeof speed !== 'undefined') { progressbar.children('span').transition(speed); } else { progressbar.children('span').transition(''); } return progressbar[0]; }; app.showProgressbar = function (container, progress, color) { if (typeof container === 'number') { container = app.root; progress = arguments[0]; color = arguments[1]; } if (progress && typeof progress === 'string' && parseFloat(progress) !== progress * 1) { color = progress; progress = undefined; } container = $(container || app.root); if (container.length === 0) return; var progressbar; if (container.hasClass('progressbar')) progressbar = container; else { progressbar = container.children('.progressbar:not(.progressbar-out), .progressbar-infinite:not(.progressbar-out)'); if (progressbar.length === 0) { // Create one if (typeof progress !== 'undefined') { // Determined progressbar = $(''); } else { // Infinite progressbar = $(''); } container.append(progressbar); } } if (progress) app.setProgressbar(container, progress); return progressbar[0]; }; app.hideProgressbar = function (container) { container = $(container || app.root); if (container.length === 0) return; var progressbar; if (container.hasClass('progressbar')) progressbar = container; else { progressbar = container.children('.progressbar, .progressbar-infinite'); } if (progressbar.length === 0 || !progressbar.hasClass('progressbar-in') || progressbar.hasClass('progressbar-out')) return; progressbar.removeClass('progressbar-in').addClass('progressbar-out').animationEnd(function () { progressbar.remove(); progressbar = null; }); return; }; app.initPageProgressbar = function (pageContainer) { pageContainer = $(pageContainer); pageContainer.find('.progressbar').each(function () { var p = $(this); if (p.children('span').length === 0) p.append(''); if (p.attr('data-progress')) app.setProgressbar(p, p.attr('data-progress')); }); }; /*====================================================== ************ Panels ************ ======================================================*/ app.allowPanelOpen = true; app.openPanel = function (panelPosition) { if (!app.allowPanelOpen) return false; var panel = $('.panel-' + panelPosition); if (panel.length === 0 || panel.hasClass('active')) return false; app.closePanel(); // Close if some panel is opened app.allowPanelOpen = false; var effect = panel.hasClass('panel-reveal') ? 'reveal' : 'cover'; panel.css({display: 'block'}).addClass('active'); panel.trigger('open'); if (app.params.material) { $('.panel-overlay').show(); } if (panel.find('.' + app.params.viewClass).length > 0) { if (app.sizeNavbars) app.sizeNavbars(panel.find('.' + app.params.viewClass)[0]); } // Trigger reLayout var clientLeft = panel[0].clientLeft; // Transition End; var transitionEndTarget = effect === 'reveal' ? $('.' + app.params.viewsClass) : panel; var openedTriggered = false; function panelTransitionEnd() { transitionEndTarget.transitionEnd(function (e) { if ($(e.target).is(transitionEndTarget)) { if (panel.hasClass('active')) { panel.trigger('opened'); } else { panel.trigger('closed'); } if (app.params.material) $('.panel-overlay').css({display: ''}); app.allowPanelOpen = true; } else panelTransitionEnd(); }); } panelTransitionEnd(); $('body').addClass('with-panel-' + panelPosition + '-' + effect); return true; }; app.closePanel = function () { var activePanel = $('.panel.active'); if (activePanel.length === 0) return false; var effect = activePanel.hasClass('panel-reveal') ? 'reveal' : 'cover'; var panelPosition = activePanel.hasClass('panel-left') ? 'left' : 'right'; activePanel.removeClass('active'); var transitionEndTarget = effect === 'reveal' ? $('.' + app.params.viewsClass) : activePanel; activePanel.trigger('close'); app.allowPanelOpen = false; transitionEndTarget.transitionEnd(function () { if (activePanel.hasClass('active')) return; activePanel.css({display: ''}); activePanel.trigger('closed'); $('body').removeClass('panel-closing'); app.allowPanelOpen = true; }); $('body').addClass('panel-closing').removeClass('with-panel-' + panelPosition + '-' + effect); }; /*====================================================== ************ Swipe panels ************ ======================================================*/ app.initSwipePanels = function () { var panel, side; if (app.params.swipePanel) { panel = $('.panel.panel-' + app.params.swipePanel); side = app.params.swipePanel; if (panel.length === 0 && side !== 'both') return; } else { if (app.params.swipePanelOnlyClose) { if ($('.panel').length === 0) return; } else return; } var panelOverlay = $('.panel-overlay'); var isTouched, isMoved, isScrolling, touchesStart = {}, touchStartTime, touchesDiff, translate, overlayOpacity, opened, panelWidth, effect, direction; var views = $('.' + app.params.viewsClass); function handleTouchStart(e) { if (!app.allowPanelOpen || (!app.params.swipePanel && !app.params.swipePanelOnlyClose) || isTouched) return; if ($('.modal-in, .photo-browser-in').length > 0) return; if (!(app.params.swipePanelCloseOpposite || app.params.swipePanelOnlyClose)) { if ($('.panel.active').length > 0 && !panel.hasClass('active')) return; } touchesStart.x = e.type === 'touchstart' ? e.targetTouches[0].pageX : e.pageX; touchesStart.y = e.type === 'touchstart' ? e.targetTouches[0].pageY : e.pageY; if (app.params.swipePanelCloseOpposite || app.params.swipePanelOnlyClose) { if ($('.panel.active').length > 0) { side = $('.panel.active').hasClass('panel-left') ? 'left' : 'right'; } else { if (app.params.swipePanelOnlyClose) return; side = app.params.swipePanel; } if (!side) return; } panel = $('.panel.panel-' + side); opened = panel.hasClass('active'); if (app.params.swipePanelActiveArea && !opened) { if (side === 'left') { if (touchesStart.x > app.params.swipePanelActiveArea) return; } if (side === 'right') { if (touchesStart.x < window.innerWidth - app.params.swipePanelActiveArea) return; } } isMoved = false; isTouched = true; isScrolling = undefined; touchStartTime = (new Date()).getTime(); direction = undefined; } function handleTouchMove(e) { if (!isTouched) return; if (e.f7PreventPanelSwipe) return; var pageX = e.type === 'touchmove' ? e.targetTouches[0].pageX : e.pageX; var pageY = e.type === 'touchmove' ? e.targetTouches[0].pageY : e.pageY; if (typeof isScrolling === 'undefined') { isScrolling = !!(isScrolling || Math.abs(pageY - touchesStart.y) > Math.abs(pageX - touchesStart.x)); } if (isScrolling) { isTouched = false; return; } if (!direction) { if (pageX > touchesStart.x) { direction = 'to-right'; } else { direction = 'to-left'; } if(side === 'both'){ if ($('.panel.active').length > 0) { side = $('.panel.active').hasClass('panel-left') ? 'left' : 'right'; } else { side = direction === 'to-right' ? 'left' : 'right'; } panel = $('.panel.panel-' + side); } if ( side === 'left' && ( direction === 'to-left' && !panel.hasClass('active') ) || side === 'right' && ( direction === 'to-right' && !panel.hasClass('active') ) ) { isTouched = false; return; } } if (app.params.swipePanelNoFollow) { var timeDiff = (new Date()).getTime() - touchStartTime; if (timeDiff < 300) { if (direction === 'to-left') { if (side === 'right') app.openPanel(side); if (side === 'left' && panel.hasClass('active')) app.closePanel(); } if (direction === 'to-right') { if (side === 'left') app.openPanel(side); if (side === 'right' && panel.hasClass('active')) app.closePanel(); } } isTouched = false; isMoved = false; return; } if (!isMoved) { effect = panel.hasClass('panel-cover') ? 'cover' : 'reveal'; if (!opened) { panel.show(); panelOverlay.show(); } panelWidth = panel[0].offsetWidth; panel.transition(0); if (panel.find('.' + app.params.viewClass).length > 0) { if (app.sizeNavbars) app.sizeNavbars(panel.find('.' + app.params.viewClass)[0]); } } isMoved = true; e.preventDefault(); var threshold = opened ? 0 : -app.params.swipePanelThreshold; if (side === 'right') threshold = -threshold; touchesDiff = pageX - touchesStart.x + threshold; if (side === 'right') { translate = touchesDiff - (opened ? panelWidth : 0); if (translate > 0) translate = 0; if (translate < -panelWidth) { translate = -panelWidth; } } else { translate = touchesDiff + (opened ? panelWidth : 0); if (translate < 0) translate = 0; if (translate > panelWidth) { translate = panelWidth; } } if (effect === 'reveal') { views.transform('translate3d(' + translate + 'px,0,0)').transition(0); panelOverlay.transform('translate3d(' + translate + 'px,0,0)').transition(0); app.pluginHook('swipePanelSetTransform', views[0], panel[0], Math.abs(translate / panelWidth)); } else { panel.transform('translate3d(' + translate + 'px,0,0)').transition(0); if (app.params.material) { panelOverlay.transition(0); overlayOpacity = Math.abs(translate/panelWidth); panelOverlay.css({opacity: overlayOpacity}); } app.pluginHook('swipePanelSetTransform', views[0], panel[0], Math.abs(translate / panelWidth)); } } function handleTouchEnd(e) { if (!isTouched || !isMoved) { isTouched = false; isMoved = false; return; } isTouched = false; isMoved = false; var timeDiff = (new Date()).getTime() - touchStartTime; var action; var edge = (translate === 0 || Math.abs(translate) === panelWidth); if (!opened) { if (translate === 0) { action = 'reset'; } else if ( timeDiff < 300 && Math.abs(translate) > 0 || timeDiff >= 300 && (Math.abs(translate) >= panelWidth / 2) ) { action = 'swap'; } else { action = 'reset'; } } else { if (translate === -panelWidth) { action = 'reset'; } else if ( timeDiff < 300 && Math.abs(translate) >= 0 || timeDiff >= 300 && (Math.abs(translate) <= panelWidth / 2) ) { if (side === 'left' && translate === panelWidth) action = 'reset'; else action = 'swap'; } else { action = 'reset'; } } if (action === 'swap') { app.allowPanelOpen = true; if (opened) { app.closePanel(); if (edge) { panel.css({display: ''}); $('body').removeClass('panel-closing'); } } else { app.openPanel(side); } if (edge) app.allowPanelOpen = true; } if (action === 'reset') { if (opened) { app.allowPanelOpen = true; app.openPanel(side); } else { app.closePanel(); if (edge) { app.allowPanelOpen = true; panel.css({display: ''}); } else { var target = effect === 'reveal' ? views : panel; panel.trigger('close'); $('body').addClass('panel-closing'); target.transitionEnd(function () { panel.trigger('closed'); panel.css({display: ''}); $('body').removeClass('panel-closing'); app.allowPanelOpen = true; }); } } } if (effect === 'reveal') { views.transition(''); views.transform(''); } panel.transition('').transform(''); panelOverlay.css({display: ''}).transform('').transition('').css('opacity', ''); } var passiveListener = app.touchEvents.start === 'touchstart' && app.support.passiveListener ? {passive: true, capture: false} : false; $(document).on(app.touchEvents.start, handleTouchStart, passiveListener); $(document).on(app.touchEvents.move, handleTouchMove); $(document).on(app.touchEvents.end, handleTouchEnd, passiveListener); }; /*====================================================== ************ Image Lazy Loading ************ ************ Based on solution by Marc Godard, https://github.com/MarcGodard ************ ======================================================*/ app.initImagesLazyLoad = function (pageContainer) { pageContainer = $(pageContainer); // Lazy images var lazyLoadImages; if (pageContainer.hasClass('lazy')) { lazyLoadImages = pageContainer; pageContainer = lazyLoadImages.parents('.page'); } else { lazyLoadImages = pageContainer.find('.lazy'); } if (lazyLoadImages.length === 0) return; // Scrollable page content var pageContent; if (pageContainer.hasClass('page-content')) { pageContent = pageContainer; pageContainer = pageContainer.parents('.page'); } else { pageContent = pageContainer.find('.page-content'); } if (pageContent.length === 0) return; // Placeholder var placeholderSrc = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABAQMAAAAl21bKAAAAA1BMVEXCwsK592mkAAAACklEQVQI12NgAAAAAgAB4iG8MwAAAABJRU5ErkJggg=='; if (typeof app.params.imagesLazyLoadPlaceholder === 'string') { placeholderSrc = app.params.imagesLazyLoadPlaceholder; } if (app.params.imagesLazyLoadPlaceholder !== false) lazyLoadImages.each(function(){ if ($(this).attr('data-src')) $(this).attr('src', placeholderSrc); }); // load image var imagesSequence = []; var imageIsLoading = false; function loadImage(el) { el = $(el); var bg = el.attr('data-background'); var src = bg ? bg : el.attr('data-src'); if (!src) return; function onLoad() { el.removeClass('lazy').addClass('lazy-loaded'); if (bg) { el.css('background-image', 'url(' + src + ')'); } else { el.attr('src', src); } if (app.params.imagesLazyLoadSequential) { imageIsLoading = false; if (imagesSequence.length > 0) { loadImage(imagesSequence.shift()); } } } if (app.params.imagesLazyLoadSequential) { if (imageIsLoading) { if (imagesSequence.indexOf(el[0]) < 0) imagesSequence.push(el[0]); return; } } // Loading flag imageIsLoading = true; var image = new Image(); image.onload = onLoad; image.onerror = onLoad; image.src =src; } function lazyHandler() { lazyLoadImages = pageContainer.find('.lazy'); lazyLoadImages.each(function(index, el) { el = $(el); if (el.parents('.tab:not(.active)').length > 0) { return; } if (isElementInViewport(el[0])) { loadImage(el); } }); } function isElementInViewport (el) { var rect = el.getBoundingClientRect(); var threshold = app.params.imagesLazyLoadThreshold || 0; return ( rect.top >= (0 - threshold) && rect.left >= (0 - threshold) && rect.top <= (window.innerHeight + threshold) && rect.left <= (window.innerWidth + threshold) ); } function attachEvents(destroy) { var method = destroy ? 'off' : 'on'; lazyLoadImages[method]('lazy', lazyHandler); lazyLoadImages.parents('.tab')[method]('show', lazyHandler); pageContainer[method]('lazy', lazyHandler); pageContent[method]('lazy', lazyHandler); pageContent[method]('scroll', lazyHandler); $(window)[method]('resize', lazyHandler); } function detachEvents() { attachEvents(true); } // Store detach function pageContainer[0].f7DestroyImagesLazyLoad = detachEvents; // Attach events attachEvents(); // Destroy on page remove if (pageContainer.hasClass('page')) { pageContainer.once('pageBeforeRemove', detachEvents); } // Run loader on page load/init lazyHandler(); // Run after page animation pageContainer.once('pageAfterAnimation', lazyHandler); }; app.destroyImagesLazyLoad = function (pageContainer) { pageContainer = $(pageContainer); if (pageContainer.length > 0 && pageContainer[0].f7DestroyImagesLazyLoad) { pageContainer[0].f7DestroyImagesLazyLoad(); } }; app.reinitImagesLazyLoad = function (pageContainer) { pageContainer = $(pageContainer); if (pageContainer.length > 0) { pageContainer.trigger('lazy'); } }; /*====================================================== ************ Material Preloader ************ ======================================================*/ app.initPageMaterialPreloader = function (pageContainer) { $(pageContainer).find('.preloader').each(function () { if ($(this).children().length === 0) { $(this).html(app.params.materialPreloaderHtml); } }); }; /*====================================================== ************ Messages ************ ======================================================*/ var Messages = function (container, params) { var defaults = { autoLayout: true, newMessagesFirst: false, scrollMessages: true, scrollMessagesOnlyOnEdge: false, messageTemplate: '{{#if day}}' + '
{{day}} {{#if time}}, {{time}}{{/if}}
' + '{{/if}}' + '
' + '{{#if name}}
{{name}}
{{/if}}' + '
{{text}}{{#if date}}
{{date}}
{{/if}}
' + '{{#if avatar}}
{{/if}}' + '{{#if label}}
{{label}}
{{/if}}' + '
' }; params = params || {}; for (var def in defaults) { if (typeof params[def] === 'undefined' || params[def] === null) { params[def] = defaults[def]; } } // Instance var m = this; // Params m.params = params; // Container m.container = $(container); if (m.container.length === 0) return; // Autolayout if (m.params.autoLayout) m.container.addClass('messages-auto-layout'); // New messages first if (m.params.newMessagesFirst) m.container.addClass('messages-new-first'); // Is In Page m.pageContainer = m.container.parents('.page').eq(0); m.pageContent = m.pageContainer.find('.page-content'); // Compiled template m.template = Template7.compile(m.params.messageTemplate); // Auto Layout m.layout = function () { if (!m.container.hasClass('messages-auto-layout')) m.container.addClass('messages-auto-layout'); m.container.find('.message').each(function () { var message = $(this); if (message.find('.message-text img').length > 0) { var childNodes = message.find('.message-text')[0].childNodes; var onlyPic = true; for (var i = 0 ; i < childNodes.length; i++) { if (childNodes[i].nodeType === 1 && childNodes[i].nodeName.toLowerCase() !== 'img') onlyPic = false; if (childNodes[i].nodeType === 3 && childNodes[i].textContent.trim() !== '') onlyPic = false; } if (onlyPic) message.addClass('message-pic'); else message.removeClass('message-pic'); } if (message.find('.message-avatar').length > 0) message.addClass('message-with-avatar'); }); m.container.find('.message').each(function () { var message = $(this); var isSent = message.hasClass('message-sent'); var next = message.next('.message-' + (isSent ? 'sent' : 'received')); var prev = message.prev('.message-' + (isSent ? 'sent' : 'received')); if (next.length === 0) { message.addClass('message-last message-with-tail'); } else message.removeClass('message-last message-with-tail'); if (prev.length === 0) { message.addClass('message-first'); } else message.removeClass('message-first'); if (prev.length > 0 && prev.find('.message-name').length > 0 && message.find('.message-name').length > 0) { if (prev.find('.message-name').text() !== message.find('.message-name').text()) { prev.addClass('message-last message-with-tail'); message.addClass('message-first'); } } }); }; // Add Message m.appendMessage = function (props, animate) { return m.addMessage(props, 'append', animate); }; m.prependMessage = function (props, animate) { return m.addMessage(props, 'prepend', animate); }; m.addMessage = function (props, method, animate) { return m.addMessages([props], method, animate); }; m.addMessages = function (newMessages, method, animate) { if (typeof animate === 'undefined') { animate = true; } if (typeof method === 'undefined') { method = m.params.newMessagesFirst ? 'prepend' : 'append'; } var newMessagesHTML = '', i; for (i = 0; i < newMessages.length; i++) { var props = newMessages[i] || {}; props.type = props.type || 'sent'; if (!props.text) continue; props.hasImage = props.text.indexOf('= 0; if (props.onlyImage === false) props.hasImage = false; if (animate) props.position = method === 'append' ? 'bottom' : 'top'; newMessagesHTML += m.template(props); } var scrollHeightBefore = m.pageContent[0].scrollHeight, heightBefore = m.pageContent[0].offsetHeight, scrollBefore = m.pageContent[0].scrollTop; m.container[method](newMessagesHTML); if (m.params.autoLayout) m.layout(); if (method === 'prepend') { m.pageContent[0].scrollTop = scrollBefore + (m.pageContent[0].scrollHeight - scrollHeightBefore); } if (m.params.scrollMessages && (method === 'append' && !m.params.newMessagesFirst) || (method === 'prepend' && m.params.newMessagesFirst)) { if (m.params.scrollMessagesOnlyOnEdge) { var onEdge = false; if (m.params.newMessagesFirst) { if (scrollBefore === 0) onEdge = true; } else { if (scrollBefore - (scrollHeightBefore - heightBefore) >= -10) onEdge = true; } if (onEdge) m.scrollMessages(animate ? undefined : 0); } else m.scrollMessages(animate ? undefined : 0); } var messages = m.container.find('.message'); if (newMessages.length === 1) { return method === 'append' ? messages[messages.length - 1] : messages[0]; } else { var messagesToReturn = []; if (method === 'append') { for (i = messages.length - newMessages.length; i < messages.length; i++) { messagesToReturn.push(messages[i]); } } else { for (i = 0; i < newMessages.length; i++) { messagesToReturn.push(messages[i]); } } return messagesToReturn; } }; m.removeMessage = function (message) { message = $(message); if (message.length === 0) { return false; } else { message.remove(); if (m.params.autoLayout) m.layout(); return true; } }; m.removeMessages = function (messages) { m.removeMessage(messages); }; m.clean = function () { m.container.html(''); }; // Scroll m.scrollMessages = function (duration, scrollTop) { if (typeof duration === 'undefined') duration = 400; var currentScroll = m.pageContent[0].scrollTop; var newScroll; if (typeof scrollTop !== 'undefined') newScroll = scrollTop; else { newScroll = m.params.newMessagesFirst ? 0 : m.pageContent[0].scrollHeight - m.pageContent[0].offsetHeight; if (newScroll === currentScroll) return; } m.pageContent.scrollTop(newScroll, duration); }; // Init Destroy m.init = function () { if (m.params.messages) { m.addMessages(m.params.messages, undefined, false); } else { if (m.params.autoLayout) m.layout(); m.scrollMessages(0); } }; m.destroy = function () { m = null; }; // Init m.init(); m.container[0].f7Messages = m; return m; }; app.messages = function (container, params) { return new Messages (container, params); }; app.initPageMessages = function (pageContainer) { pageContainer = $(pageContainer); var messages = pageContainer.find('.messages'); if (messages.length === 0) return; if (!messages.hasClass('messages-init')) { return; } var m = app.messages(messages, messages.dataset()); // Destroy on page remove function pageBeforeRemove() { m.destroy(); pageContainer.off('pageBeforeRemove', pageBeforeRemove); } if (pageContainer.hasClass('page')) { pageContainer.on('pageBeforeRemove', pageBeforeRemove); } }; /*=============================================================================== ************ Swipeout Actions (Swipe to delete) ************ ===============================================================================*/ app.swipeoutOpenedEl = undefined; app.allowSwipeout = true; app.initSwipeout = function (swipeoutEl) { var isTouched, isMoved, isScrolling, touchesStart = {}, touchStartTime, touchesDiff, swipeOutEl, swipeOutContent, actionsRight, actionsLeft, actionsLeftWidth, actionsRightWidth, translate, opened, openedActions, buttonsLeft, buttonsRight, direction, overswipeLeftButton, overswipeRightButton, overswipeLeft, overswipeRight, noFoldLeft, noFoldRight; $(document).on(app.touchEvents.start, function (e) { if (app.swipeoutOpenedEl) { var target = $(e.target); if (!( app.swipeoutOpenedEl.is(target[0]) || target.parents('.swipeout').is(app.swipeoutOpenedEl) || target.hasClass('modal-in') || target.hasClass('modal-overlay') || target.hasClass('actions-modal') || target.parents('.actions-modal.modal-in, .modal.modal-in').length > 0 )) { app.swipeoutClose(app.swipeoutOpenedEl); } } }); function handleTouchStart(e) { if (!app.allowSwipeout) return; isMoved = false; isTouched = true; isScrolling = undefined; touchesStart.x = e.type === 'touchstart' ? e.targetTouches[0].pageX : e.pageX; touchesStart.y = e.type === 'touchstart' ? e.targetTouches[0].pageY : e.pageY; touchStartTime = (new Date()).getTime(); } function handleTouchMove(e) { if (!isTouched) return; var pageX = e.type === 'touchmove' ? e.targetTouches[0].pageX : e.pageX; var pageY = e.type === 'touchmove' ? e.targetTouches[0].pageY : e.pageY; if (typeof isScrolling === 'undefined') { isScrolling = !!(isScrolling || Math.abs(pageY - touchesStart.y) > Math.abs(pageX - touchesStart.x)); } if (isScrolling) { isTouched = false; return; } if (!isMoved) { if ($('.list-block.sortable-opened').length > 0) return; /*jshint validthis:true */ swipeOutEl = $(this); swipeOutContent = swipeOutEl.find('.swipeout-content'); actionsRight = swipeOutEl.find('.swipeout-actions-right'); actionsLeft = swipeOutEl.find('.swipeout-actions-left'); actionsLeftWidth = actionsRightWidth = buttonsLeft = buttonsRight = overswipeRightButton = overswipeLeftButton = null; noFoldLeft = actionsLeft.hasClass('swipeout-actions-no-fold') || app.params.swipeoutActionsNoFold; noFoldRight = actionsRight.hasClass('swipeout-actions-no-fold') || app.params.swipeoutActionsNoFold; if (actionsLeft.length > 0) { actionsLeftWidth = actionsLeft.outerWidth(); buttonsLeft = actionsLeft.children('a'); overswipeLeftButton = actionsLeft.find('.swipeout-overswipe'); } if (actionsRight.length > 0) { actionsRightWidth = actionsRight.outerWidth(); buttonsRight = actionsRight.children('a'); overswipeRightButton = actionsRight.find('.swipeout-overswipe'); } opened = swipeOutEl.hasClass('swipeout-opened'); if (opened) { openedActions = swipeOutEl.find('.swipeout-actions-left.swipeout-actions-opened').length > 0 ? 'left' : 'right'; } swipeOutEl.removeClass('transitioning'); if (!app.params.swipeoutNoFollow) { swipeOutEl.find('.swipeout-actions-opened').removeClass('swipeout-actions-opened'); swipeOutEl.removeClass('swipeout-opened'); } } isMoved = true; e.preventDefault(); touchesDiff = pageX - touchesStart.x; translate = touchesDiff; if (opened) { if (openedActions === 'right') translate = translate - actionsRightWidth; else translate = translate + actionsLeftWidth; } if (translate > 0 && actionsLeft.length === 0 || translate < 0 && actionsRight.length === 0) { if (!opened) { isTouched = isMoved = false; swipeOutContent.transform(''); if (buttonsRight && buttonsRight.length > 0) { buttonsRight.transform(''); } if (buttonsLeft && buttonsLeft.length > 0) { buttonsLeft.transform(''); } return; } translate = 0; } if (translate < 0) direction = 'to-left'; else if (translate > 0) direction = 'to-right'; else { if (direction) direction = direction; else direction = 'to-left'; } var i, buttonOffset, progress; e.f7PreventPanelSwipe = true; if (app.params.swipeoutNoFollow) { if (opened) { if (openedActions === 'right' && touchesDiff > 0) { app.swipeoutClose(swipeOutEl); } if (openedActions === 'left' && touchesDiff < 0) { app.swipeoutClose(swipeOutEl); } } else { if (touchesDiff < 0 && actionsRight.length > 0) { app.swipeoutOpen(swipeOutEl, 'right'); } if (touchesDiff > 0 && actionsLeft.length > 0) { app.swipeoutOpen(swipeOutEl, 'left'); } } isTouched = false; isMoved = false; return; } overswipeLeft = false; overswipeRight = false; var $button; if (actionsRight.length > 0) { // Show right actions progress = translate / actionsRightWidth; if (translate < -actionsRightWidth) { translate = -actionsRightWidth - Math.pow(-translate - actionsRightWidth, 0.8); if (overswipeRightButton.length > 0) { overswipeRight = true; } } for (i = 0; i < buttonsRight.length; i++) { if (typeof buttonsRight[i]._buttonOffset === 'undefined') { buttonsRight[i]._buttonOffset = buttonsRight[i].offsetLeft; } buttonOffset = buttonsRight[i]._buttonOffset; $button = $(buttonsRight[i]); if (overswipeRightButton.length > 0 && $button.hasClass('swipeout-overswipe')) { $button.css({left: (overswipeRight ? -buttonOffset : 0) + 'px'}); if (overswipeRight) { $button.addClass('swipeout-overswipe-active'); } else { $button.removeClass('swipeout-overswipe-active'); } } $button.transform('translate3d(' + (translate - buttonOffset * (1 + Math.max(progress, -1))) + 'px,0,0)'); } } if (actionsLeft.length > 0) { // Show left actions progress = translate / actionsLeftWidth; if (translate > actionsLeftWidth) { translate = actionsLeftWidth + Math.pow(translate - actionsLeftWidth, 0.8); if (overswipeLeftButton.length > 0) { overswipeLeft = true; } } for (i = 0; i < buttonsLeft.length; i++) { if (typeof buttonsLeft[i]._buttonOffset === 'undefined') { buttonsLeft[i]._buttonOffset = actionsLeftWidth - buttonsLeft[i].offsetLeft - buttonsLeft[i].offsetWidth; } buttonOffset = buttonsLeft[i]._buttonOffset; $button = $(buttonsLeft[i]); if (overswipeLeftButton.length > 0 && $button.hasClass('swipeout-overswipe')) { $button.css({left: (overswipeLeft ? buttonOffset : 0) + 'px'}); if (overswipeLeft) { $button.addClass('swipeout-overswipe-active'); } else { $button.removeClass('swipeout-overswipe-active'); } } if (buttonsLeft.length > 1) { $button.css('z-index', buttonsLeft.length - i); } $button.transform('translate3d(' + (translate + buttonOffset * (1 - Math.min(progress, 1))) + 'px,0,0)'); } } swipeOutContent.transform('translate3d(' + translate + 'px,0,0)'); } function handleTouchEnd(e) { if (!isTouched || !isMoved) { isTouched = false; isMoved = false; return; } isTouched = false; isMoved = false; var timeDiff = (new Date()).getTime() - touchStartTime; var action, actionsWidth, actions, buttons, i, noFold; noFold = direction === 'to-left' ? noFoldRight : noFoldLeft; actions = direction === 'to-left' ? actionsRight : actionsLeft; actionsWidth = direction === 'to-left' ? actionsRightWidth : actionsLeftWidth; if ( timeDiff < 300 && (touchesDiff < -10 && direction === 'to-left' || touchesDiff > 10 && direction === 'to-right') || timeDiff >= 300 && Math.abs(translate) > actionsWidth / 2 ) { action = 'open'; } else { action = 'close'; } if (timeDiff < 300) { if (Math.abs(translate) === 0) action = 'close'; if (Math.abs(translate) === actionsWidth) action = 'open'; } if (action === 'open') { app.swipeoutOpenedEl = swipeOutEl; swipeOutEl.trigger('open'); swipeOutEl.addClass('swipeout-opened transitioning'); var newTranslate = direction === 'to-left' ? -actionsWidth : actionsWidth; swipeOutContent.transform('translate3d(' + newTranslate + 'px,0,0)'); actions.addClass('swipeout-actions-opened'); buttons = direction === 'to-left' ? buttonsRight : buttonsLeft; if (buttons) { for (i = 0; i < buttons.length; i++) { $(buttons[i]).transform('translate3d(' + newTranslate + 'px,0,0)'); } } if (overswipeRight) { actionsRight.find('.swipeout-overswipe')[0].click(); } if (overswipeLeft) { actionsLeft.find('.swipeout-overswipe')[0].click(); } } else { swipeOutEl.trigger('close'); app.swipeoutOpenedEl = undefined; swipeOutEl.addClass('transitioning').removeClass('swipeout-opened'); swipeOutContent.transform(''); actions.removeClass('swipeout-actions-opened'); } var buttonOffset; if (buttonsLeft && buttonsLeft.length > 0 && buttonsLeft !== buttons) { for (i = 0; i < buttonsLeft.length; i++) { buttonOffset = buttonsLeft[i]._buttonOffset; if (typeof buttonOffset === 'undefined') { buttonsLeft[i]._buttonOffset = actionsLeftWidth - buttonsLeft[i].offsetLeft - buttonsLeft[i].offsetWidth; } $(buttonsLeft[i]).transform('translate3d(' + (buttonOffset) + 'px,0,0)'); } } if (buttonsRight && buttonsRight.length > 0 && buttonsRight !== buttons) { for (i = 0; i < buttonsRight.length; i++) { buttonOffset = buttonsRight[i]._buttonOffset; if (typeof buttonOffset === 'undefined') { buttonsRight[i]._buttonOffset = buttonsRight[i].offsetLeft; } $(buttonsRight[i]).transform('translate3d(' + (-buttonOffset) + 'px,0,0)'); } } swipeOutContent.transitionEnd(function (e) { if (opened && action === 'open' || closed && action === 'close') return; swipeOutEl.trigger(action === 'open' ? 'opened' : 'closed'); if (opened && action === 'close') { if (actionsRight.length > 0) { buttonsRight.transform(''); } if (actionsLeft.length > 0) { buttonsLeft.transform(''); } } }); } if (swipeoutEl) { $(swipeoutEl).on(app.touchEvents.start, handleTouchStart); $(swipeoutEl).on(app.touchEvents.move, handleTouchMove); $(swipeoutEl).on(app.touchEvents.end, handleTouchEnd); } else { $(document).on(app.touchEvents.start, '.list-block li.swipeout', handleTouchStart); $(document).on(app.touchEvents.move, '.list-block li.swipeout', handleTouchMove); $(document).on(app.touchEvents.end, '.list-block li.swipeout', handleTouchEnd); } }; app.swipeoutOpen = function (el, dir, callback) { el = $(el); if (arguments.length === 2) { if (typeof arguments[1] === 'function') { callback = dir; } } if (el.length === 0) return; if (el.length > 1) el = $(el[0]); if (!el.hasClass('swipeout') || el.hasClass('swipeout-opened')) return; if (!dir) { if (el.find('.swipeout-actions-right').length > 0) dir = 'right'; else dir = 'left'; } var swipeOutActions = el.find('.swipeout-actions-' + dir); if (swipeOutActions.length === 0) return; var noFold = swipeOutActions.hasClass('swipeout-actions-no-fold') || app.params.swipeoutActionsNoFold; el.trigger('open').addClass('swipeout-opened').removeClass('transitioning'); swipeOutActions.addClass('swipeout-actions-opened'); var buttons = swipeOutActions.children('a'); var swipeOutActionsWidth = swipeOutActions.outerWidth(); var translate = dir === 'right' ? -swipeOutActionsWidth : swipeOutActionsWidth; var i; if (buttons.length > 1) { for (i = 0; i < buttons.length; i++) { if (dir === 'right') { $(buttons[i]).transform('translate3d(' + (- buttons[i].offsetLeft) + 'px,0,0)'); } else { $(buttons[i]).css('z-index', buttons.length - i).transform('translate3d(' + (swipeOutActionsWidth - buttons[i].offsetWidth - buttons[i].offsetLeft) + 'px,0,0)'); } } var clientLeft = buttons[1].clientLeft; } el.addClass('transitioning'); for (i = 0; i < buttons.length; i++) { $(buttons[i]).transform('translate3d(' + (translate) + 'px,0,0)'); } el.find('.swipeout-content').transform('translate3d(' + translate + 'px,0,0)').transitionEnd(function () { el.trigger('opened'); if (callback) callback.call(el[0]); }); app.swipeoutOpenedEl = el; }; app.swipeoutClose = function (el, callback) { el = $(el); if (el.length === 0) return; if (!el.hasClass('swipeout-opened')) return; var dir = el.find('.swipeout-actions-opened').hasClass('swipeout-actions-right') ? 'right' : 'left'; var swipeOutActions = el.find('.swipeout-actions-opened').removeClass('swipeout-actions-opened'); var noFold = swipeOutActions.hasClass('swipeout-actions-no-fold') || app.params.swipeoutActionsNoFold; var buttons = swipeOutActions.children('a'); var swipeOutActionsWidth = swipeOutActions.outerWidth(); app.allowSwipeout = false; el.trigger('close'); el.removeClass('swipeout-opened').addClass('transitioning'); var closeTO; function onSwipeoutClose() { app.allowSwipeout = true; if (el.hasClass('swipeout-opened')) return; el.removeClass('transitioning'); buttons.transform(''); el.trigger('closed'); if (callback) callback.call(el[0]); if (closeTO) clearTimeout(closeTO); } el.find('.swipeout-content').transform('').transitionEnd(onSwipeoutClose); closeTO = setTimeout(onSwipeoutClose, 500); for (var i = 0; i < buttons.length; i++) { if (dir === 'right') { $(buttons[i]).transform('translate3d(' + (-buttons[i].offsetLeft) + 'px,0,0)'); } else { $(buttons[i]).transform('translate3d(' + (swipeOutActionsWidth - buttons[i].offsetWidth - buttons[i].offsetLeft) + 'px,0,0)'); } $(buttons[i]).css({left:0 + 'px'}).removeClass('swipeout-overswipe-active'); } if (app.swipeoutOpenedEl && app.swipeoutOpenedEl[0] === el[0]) app.swipeoutOpenedEl = undefined; }; app.swipeoutDelete = function (el, callback) { el = $(el); if (el.length === 0) return; if (el.length > 1) el = $(el[0]); app.swipeoutOpenedEl = undefined; el.trigger('delete'); el.css({height: el.outerHeight() + 'px'}); var clientLeft = el[0].clientLeft; el.css({height: 0 + 'px'}).addClass('deleting transitioning').transitionEnd(function () { el.trigger('deleted'); if (callback) callback.call(el[0]); if (el.parents('.virtual-list').length > 0) { var virtualList = el.parents('.virtual-list')[0].f7VirtualList; var virtualIndex = el[0].f7VirtualListIndex; if (virtualList && typeof virtualIndex !== 'undefined') virtualList.deleteItem(virtualIndex); } else { if (app.params.swipeoutRemoveWithTimeout) { setTimeout(function () { el.remove(); }, 0); } else el.remove(); } }); var translate = '-100%'; el.find('.swipeout-content').transform('translate3d(' + translate + ',0,0)'); }; /*=============================================================================== ************ Sortable ************ ===============================================================================*/ app.sortableToggle = function (sortableContainer) { sortableContainer = $(sortableContainer); if (sortableContainer.length === 0) sortableContainer = $('.list-block.sortable'); sortableContainer.toggleClass('sortable-opened'); if (sortableContainer.hasClass('sortable-opened')) { sortableContainer.trigger('open'); } else { sortableContainer.trigger('close'); } return sortableContainer; }; app.sortableOpen = function (sortableContainer) { sortableContainer = $(sortableContainer); if (sortableContainer.length === 0) sortableContainer = $('.list-block.sortable'); sortableContainer.addClass('sortable-opened'); sortableContainer.trigger('open'); return sortableContainer; }; app.sortableClose = function (sortableContainer) { sortableContainer = $(sortableContainer); if (sortableContainer.length === 0) sortableContainer = $('.list-block.sortable'); sortableContainer.removeClass('sortable-opened'); sortableContainer.trigger('close'); return sortableContainer; }; app.initSortable = function () { var isTouched, isMoved, touchStartY, touchesDiff, sortingEl, sortingElHeight, sortingItems, minTop, maxTop, insertAfter, insertBefore, sortableContainer, startIndex; function handleTouchStart(e) { isMoved = false; isTouched = true; touchStartY = e.type === 'touchstart' ? e.targetTouches[0].pageY : e.pageY; /*jshint validthis:true */ sortingEl = $(this).parent(); startIndex = sortingEl.index(); sortingItems = sortingEl.parent().find('li'); sortableContainer = sortingEl.parents('.sortable'); e.preventDefault(); app.allowPanelOpen = app.allowSwipeout = false; } function handleTouchMove(e) { if (!isTouched || !sortingEl) return; var pageX = e.type === 'touchmove' ? e.targetTouches[0].pageX : e.pageX; var pageY = e.type === 'touchmove' ? e.targetTouches[0].pageY : e.pageY; if (!isMoved) { sortingEl.addClass('sorting'); sortableContainer.addClass('sortable-sorting'); minTop = sortingEl[0].offsetTop; maxTop = sortingEl.parent().height() - sortingEl[0].offsetTop - sortingEl.height(); sortingElHeight = sortingEl[0].offsetHeight; } isMoved = true; e.preventDefault(); e.f7PreventPanelSwipe = true; touchesDiff = pageY - touchStartY; var translate = touchesDiff; if (translate < -minTop) translate = -minTop; if (translate > maxTop) translate = maxTop; sortingEl.transform('translate3d(0,' + translate + 'px,0)'); insertBefore = insertAfter = undefined; sortingItems.each(function () { var currentEl = $(this); if (currentEl[0] === sortingEl[0]) return; var currentElOffset = currentEl[0].offsetTop; var currentElHeight = currentEl.height(); var sortingElOffset = sortingEl[0].offsetTop + translate; if ((sortingElOffset >= currentElOffset - currentElHeight / 2) && sortingEl.index() < currentEl.index()) { currentEl.transform('translate3d(0, '+(-sortingElHeight)+'px,0)'); insertAfter = currentEl; insertBefore = undefined; } else if ((sortingElOffset <= currentElOffset + currentElHeight / 2) && sortingEl.index() > currentEl.index()) { currentEl.transform('translate3d(0, '+(sortingElHeight)+'px,0)'); insertAfter = undefined; if (!insertBefore) insertBefore = currentEl; } else { $(this).transform('translate3d(0, 0%,0)'); } }); } function handleTouchEnd(e) { app.allowPanelOpen = app.allowSwipeout = true; if (!isTouched || !isMoved) { isTouched = false; isMoved = false; return; } e.preventDefault(); sortingItems.transform(''); sortingEl.removeClass('sorting'); sortableContainer.removeClass('sortable-sorting'); var virtualList, oldIndex, newIndex; if (insertAfter) { sortingEl.insertAfter(insertAfter); sortingEl.trigger('sort', {startIndex: startIndex, newIndex: sortingEl.index()}); } if (insertBefore) { sortingEl.insertBefore(insertBefore); sortingEl.trigger('sort', {startIndex: startIndex, newIndex: sortingEl.index()}); } if ((insertAfter || insertBefore) && sortableContainer.hasClass('virtual-list')) { virtualList = sortableContainer[0].f7VirtualList; oldIndex = sortingEl[0].f7VirtualListIndex; newIndex = insertBefore ? insertBefore[0].f7VirtualListIndex : insertAfter[0].f7VirtualListIndex; if (virtualList) virtualList.moveItem(oldIndex, newIndex); } insertAfter = insertBefore = undefined; isTouched = false; isMoved = false; } $(document).on(app.touchEvents.start, '.list-block.sortable .sortable-handler', handleTouchStart); if (app.support.touch) { $(document).on(app.touchEvents.move, '.list-block.sortable .sortable-handler', handleTouchMove); $(document).on(app.touchEvents.end, '.list-block.sortable .sortable-handler', handleTouchEnd); } else { $(document).on(app.touchEvents.move, handleTouchMove); $(document).on(app.touchEvents.end, handleTouchEnd); } }; /*=============================================================================== ************ Smart Select ************ ===============================================================================*/ app.initSmartSelects = function (pageContainer) { pageContainer = $(pageContainer); var selects; if (pageContainer.is('.smart-select')) { selects = pageContainer; } else { selects = pageContainer.find('.smart-select'); } if (selects.length === 0) return; selects.each(function () { var smartSelect = $(this); var $select = smartSelect.find('select'); if ($select.length === 0) return; var select = $select[0]; if (select.length === 0) return; var valueText = []; for (var i = 0; i < select.length; i++) { if (select[i].selected) valueText.push(select[i].textContent.trim()); } var itemAfter = smartSelect.find('.item-after'); if (itemAfter.length === 0) { smartSelect.find('.item-inner').append('
' + valueText.join(', ') + '
'); } else { var selectedText = itemAfter.text(); if (itemAfter.hasClass('smart-select-value')) { for (i = 0; i < select.length; i++) { select[i].selected = select[i].textContent.trim() === selectedText.trim(); } } else { itemAfter.text(valueText.join(', ')); } } $select.on('change', function () { var valueText = []; for (var i = 0; i < select.length; i++) { if (select[i].selected) valueText.push(select[i].textContent.trim()); } smartSelect.find('.item-after').text(valueText.join(', ')); }); }); }; app.smartSelectAddOption = function (select, option, index) { select = $(select); var smartSelect = select.parents('.smart-select'); if (typeof index === 'undefined') { select.append(option); } else { $(option).insertBefore(select.find('option').eq(index)); } app.initSmartSelects(smartSelect); var selectName = smartSelect.find('select').attr('name'); var opened = $('.page.smart-select-page[data-select-name="' + selectName + '"]').length > 0; if (opened) { app.smartSelectOpen(smartSelect, true); } }; app.smartSelectOpen = function (smartSelect, reLayout) { smartSelect = $(smartSelect); if (smartSelect.length === 0) return; // Find related view var view = smartSelect.parents('.' + app.params.viewClass); if (view.length === 0) return; view = view[0].f7View; // Parameters var openIn = smartSelect.attr('data-open-in') || app.params.smartSelectOpenIn; if (openIn === 'popup') { if ($('.popup.smart-select-popup').length > 0) return; } else if (openIn === 'picker') { if ($('.picker-modal.modal-in').length > 0 && !reLayout){ if (smartSelect[0].f7SmartSelectPicker !== $('.picker-modal.modal-in:not(.modal-out)')[0]) app.closeModal($('.picker-modal.modal-in:not(.modal-out)')); else return; } } else { if (!view) return; } var smartSelectData = smartSelect.dataset(); var pageTitle = smartSelectData.pageTitle || smartSelect.find('.item-title').text(); var backText = smartSelectData.backText || app.params.smartSelectBackText; var closeText; if (openIn === 'picker') { closeText = smartSelectData.pickerCloseText || smartSelectData.backText || app.params.smartSelectPickerCloseText ; } else { closeText = smartSelectData.popupCloseText || smartSelectData.backText || app.params.smartSelectPopupCloseText ; } var backOnSelect = smartSelectData.backOnSelect !== undefined ? smartSelectData.backOnSelect : app.params.smartSelectBackOnSelect; var formTheme = smartSelectData.formTheme || app.params.smartSelectFormTheme; var navbarTheme = smartSelectData.navbarTheme || app.params.smartSelectNavbarTheme; var toolbarTheme = smartSelectData.toolbarTheme || app.params.smartSelectToolbarTheme; var virtualList = smartSelectData.virtualList; var virtualListHeight = smartSelectData.virtualListHeight; var material = app.params.material; var pickerHeight = smartSelectData.pickerHeight || app.params.smartSelectPickerHeight; // Collect all options/values var select = smartSelect.find('select')[0]; var $select = $(select); var $selectData = $select.dataset(); if (select.disabled || smartSelect.hasClass('disabled') || $select.hasClass('disabled')) { return; } var values = []; var id = (new Date()).getTime(); var inputType = select.multiple ? 'checkbox' : 'radio'; var inputName = inputType + '-' + id; var maxLength = $select.attr('maxlength'); var selectName = select.name; var option, optionHasMedia, optionImage, optionIcon, optionGroup, optionGroupLabel, optionPreviousGroup, optionIsLabel, previousGroup, optionColor, optionClassName, optionData; for (var i = 0; i < select.length; i++) { option = $(select[i]); optionData = option.dataset(); optionImage = optionData.optionImage || $selectData.optionImage || smartSelectData.optionImage; optionIcon = optionData.optionIcon || $selectData.optionIcon || smartSelectData.optionIcon; optionHasMedia = optionImage || optionIcon || inputType === 'checkbox'; if (material) optionHasMedia = optionImage || optionIcon; optionColor = optionData.optionColor; optionClassName = optionData.optionClass; if (option[0].disabled) optionClassName += ' disabled'; optionGroup = option.parent('optgroup')[0]; optionGroupLabel = optionGroup && optionGroup.label; optionIsLabel = false; if (optionGroup) { if (optionGroup !== previousGroup) { optionIsLabel = true; previousGroup = optionGroup; values.push({ groupLabel: optionGroupLabel, isLabel: optionIsLabel }); } } values.push({ value: option[0].value, text: option[0].textContent.trim(), selected: option[0].selected, group: optionGroup, groupLabel: optionGroupLabel, image: optionImage, icon: optionIcon, color: optionColor, className: optionClassName, disabled: option[0].disabled, inputType: inputType, id: id, hasMedia: optionHasMedia, checkbox: inputType === 'checkbox', inputName: inputName, material: app.params.material }); } // Item template/HTML if (!app._compiledTemplates.smartSelectItem) { app._compiledTemplates.smartSelectItem = t7.compile(app.params.smartSelectItemTemplate || '{{#if isLabel}}' + '
  • {{groupLabel}}
  • ' + '{{else}}' + '' + '' + '' + '{{/if}}' ); } var smartSelectItemTemplate = app._compiledTemplates.smartSelectItem; var inputsHTML = ''; if (!virtualList) { for (var j = 0; j < values.length; j++) { inputsHTML += smartSelectItemTemplate(values[j]); } } // Toolbar / Navbar var toolbarHTML = '', navbarHTML; var noNavbar = '', noToolbar = '', noTabbar = '', navbarLayout; if (openIn === 'picker') { if (!app._compiledTemplates.smartSelectToolbar) { app._compiledTemplates.smartSelectToolbar = t7.compile(app.params.smartSelectToolbarTemplate || '
    ' + '
    ' + '
    ' + '' + '
    ' + '
    ' ); } toolbarHTML = app._compiledTemplates.smartSelectToolbar({ pageTitle: pageTitle, closeText: closeText, openIn: openIn, toolbarTheme: toolbarTheme, inPicker: openIn === 'picker' }); } else { // Navbar HTML if (!app._compiledTemplates.smartSelectNavbar) { app._compiledTemplates.smartSelectNavbar = t7.compile(app.params.smartSelectNavbarTemplate || '' ); } navbarHTML = app._compiledTemplates.smartSelectNavbar({ pageTitle: pageTitle, backText: backText, closeText: closeText, openIn: openIn, navbarTheme: navbarTheme, inPopup: openIn === 'popup', inPage: openIn === 'page', leftTemplate: openIn === 'popup' ? (app.params.smartSelectPopupCloseTemplate || (material ? '
    ' : '')).replace(/{{closeText}}/g, closeText) : (app.params.smartSelectBackTemplate || (material ? '
    ' : '')).replace(/{{backText}}/g, backText) }); // Determine navbar layout type - static/fixed/through if (openIn === 'page') { navbarLayout = 'static'; if (smartSelect.parents('.navbar-through').length > 0) navbarLayout = 'through'; if (smartSelect.parents('.navbar-fixed').length > 0) navbarLayout = 'fixed'; noToolbar = smartSelect.parents('.page').hasClass('no-toolbar') ? 'no-toolbar' : ''; noNavbar = smartSelect.parents('.page').hasClass('no-navbar') ? 'no-navbar' : 'navbar-' + navbarLayout; noTabbar = smartSelect.parents('.page').hasClass('no-tabbar') ? 'no-tabbar' : ''; } else { navbarLayout = 'fixed'; } } // Page Layout var pageName = 'smart-select-' + inputName; var useSearchbar = typeof smartSelect.data('searchbar') === 'undefined' ? app.params.smartSelectSearchbar : (smartSelect.data('searchbar') === 'true' ? true : false); var searchbarPlaceholder, searchbarCancel; if (useSearchbar) { searchbarPlaceholder = smartSelect.data('searchbar-placeholder') || 'Search'; searchbarCancel = smartSelect.data('searchbar-cancel') || 'Cancel'; } var searchbarHTML = '' + '
    '; var pageHTML = (openIn !== 'picker' && navbarLayout === 'through' ? navbarHTML : '') + '
    ' + '
    ' + (openIn !== 'picker' && navbarLayout === 'fixed' ? navbarHTML : '') + (useSearchbar ? searchbarHTML : '') + '
    ' + (openIn !== 'picker' && navbarLayout === 'static' ? navbarHTML : '') + '
    ' + '
      ' + (virtualList ? '' : inputsHTML) + '
    ' + '
    ' + '
    ' + '
    ' + '
    '; // Define popup and picker var popup, picker; // Scroll SS Picker To Input function scrollToInput() { var pageContent = smartSelect.parents('.page-content'); if (pageContent.length === 0) return; var paddingTop = parseInt(pageContent.css('padding-top'), 10), paddingBottom = parseInt(pageContent.css('padding-bottom'), 10), pageHeight = pageContent[0].offsetHeight - paddingTop - picker.height(), pageScrollHeight = pageContent[0].scrollHeight - paddingTop - picker.height(), newPaddingBottom; var inputTop = smartSelect.offset().top - paddingTop + smartSelect[0].offsetHeight; if (inputTop > pageHeight) { var scrollTop = pageContent.scrollTop() + inputTop - pageHeight; if (scrollTop + pageHeight > pageScrollHeight) { newPaddingBottom = scrollTop + pageHeight - pageScrollHeight + paddingBottom; if (pageHeight === pageScrollHeight) { newPaddingBottom = picker.height(); } pageContent.css({'padding-bottom': (newPaddingBottom) + 'px'}); } pageContent.scrollTop(scrollTop, 300); } } // Close SS Picker on HTML Click function closeOnHTMLClick(e) { var close = true; if (e.target === smartSelect[0] || $(e.target).parents(smartSelect[0]).length > 0) { close = false; } if ($(e.target).parents('.picker-modal').length > 0) { close = false; } if (close) { app.closeModal('.smart-select-picker.modal-in'); } } // Check max length function checkMaxLength(container) { if (select.selectedOptions.length >= maxLength) { container.find('input[type="checkbox"]').each(function () { if (!this.checked) { $(this).parents('li').addClass('disabled'); } else { $(this).parents('li').removeClass('disabled'); } }); } else { container.find('.disabled').removeClass('disabled'); } } // Event Listeners on new page function handleInputs(container) { container = $(container); if (virtualList) { var virtualListInstance = app.virtualList(container.find('.virtual-list'), { items: values, template: smartSelectItemTemplate, height: virtualListHeight || undefined, searchByItem: function (query, index, item) { if (item.text.toLowerCase().indexOf(query.trim().toLowerCase()) >=0 ) return true; return false; } }); container.once(openIn === 'popup' || openIn === 'picker' ? 'closed': 'pageBeforeRemove', function () { if (virtualListInstance && virtualListInstance.destroy) virtualListInstance.destroy(); }); } if (maxLength) { checkMaxLength(container); } if (backOnSelect) { container.find('input[type="radio"][name="' + inputName + '"]:checked').parents('label').once('click', function () { if (openIn === 'popup') app.closeModal(popup); else if (openIn === 'picker') app.closeModal(picker); else view.router.back(); }); } container.on('change', 'input[name="' + inputName + '"]', function () { var input = this; var value = input.value; var optionText = []; if (input.type === 'checkbox') { var values = []; for (var i = 0; i < select.options.length; i++) { var option = select.options[i]; if (option.value === value) { option.selected = input.checked; } if (option.selected) { optionText.push(option.textContent.trim()); } } if (maxLength) { checkMaxLength(container); } } else { optionText = [smartSelect.find('option[value="' + value + '"]').text()]; select.value = value; } $select.trigger('change'); smartSelect.find('.item-after').text(optionText.join(', ')); if (backOnSelect && inputType === 'radio') { if (openIn === 'popup') app.closeModal(popup); else if (openIn === 'picker') app.closeModal(picker); else view.router.back(); } }); } function pageInit(e) { var page = e.detail.page; if (page.name === pageName) { handleInputs(page.container); } } if (openIn === 'popup') { if (reLayout) { popup = $('.popup.smart-select-popup .view'); popup.html(pageHTML); } else { popup = app.popup( '' ); popup = $(popup); } app.initPage(popup.find('.page')); handleInputs(popup); } else if (openIn === 'picker') { if (reLayout) { picker = $('.picker-modal.smart-select-picker .view'); picker.html(pageHTML); } else { picker = app.pickerModal( '
    ' + toolbarHTML + '
    ' + '
    ' + pageHTML + '
    ' + '
    ' + '
    ' ); picker = $(picker); // Scroll To Input scrollToInput(); // Close On Click $('html').on('click', closeOnHTMLClick); // On Close picker.once('close', function () { // Reset linked picker smartSelect[0].f7SmartSelectPicker = undefined; // Detach html click $('html').off('click', closeOnHTMLClick); // Restore page padding bottom smartSelect.parents('.page-content').css({paddingBottom: ''}); }); // Link Picker smartSelect[0].f7SmartSelectPicker = picker[0]; } // Init Page app.initPage(picker.find('.page')); // Attach events handleInputs(picker); } else { $(document).once('pageInit', '.smart-select-page', pageInit); view.router.load({ content: pageHTML, reload: reLayout ? true : undefined }); } }; /*=============================================================================== ************ Virtual List ************ ===============================================================================*/ var VirtualList = function (listBlock, params) { var defaults = { cols: 1, height: app.params.material ? 48 : 44, cache: true, dynamicHeightBufferSize: 1, showFilteredItemsOnly: false }; params = params || {}; for (var def in defaults) { if (typeof params[def] === 'undefined') { params[def] = defaults[def]; } } // Preparation var vl = this; vl.listBlock = $(listBlock); vl.params = params; vl.items = vl.params.items; if (vl.params.showFilteredItemsOnly) { vl.filteredItems = []; } if (vl.params.template) { if (typeof vl.params.template === 'string') vl.template = t7.compile(vl.params.template); else if (typeof vl.params.template === 'function') vl.template = vl.params.template; } vl.pageContent = vl.listBlock.parents('.page-content'); // Bad scroll var updatableScroll; if (typeof vl.params.updatableScroll !== 'undefined') { updatableScroll = vl.params.updatableScroll; } else { updatableScroll = true; if (app.device.ios && app.device.osVersion.split('.')[0] < 8) { updatableScroll = false; } } // Append