/* * Framework7 0.10.0 * Full Featured HTML Framework For Building iOS 7 Apps * * http://www.idangero.us/framework7 * * Copyright 2014, Vladimir Kharlampidi * The iDangero.us * http://www.idangero.us/ * * Licensed under MIT * * Released on: December 8, 2014 */ (function () { 'use strict'; /*=========================== Framework 7 ===========================*/ window.Framework7 = function (params) { // App var app = this; // Version app.version = '0.10.0'; // Default Parameters app.params = { cache: true, cacheIgnore: [], cacheIgnoreGetParameters: false, cacheDuration: 1000 * 60 * 10, // Ten minutes preloadPreviousPage: true, uniqueHistory: false, uniqueHistoryIgnoreGetParameters: false, dynamicPageUrl: 'content-{{index}}', allowDuplicateUrls: false, router: true, // Push State pushState: false, pushStateRoot: undefined, pushStateNoAnimation: false, pushStateSeparator: '#!/', // Fast clicks fastClicks: true, fastClicksDistanceThreshold: 0, // 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, // Swipeout swipeout: true, swipeoutActionsNoFold: false, swipeoutNoFollow: false, // Smart Select Back link template smartSelectBackTemplate: '
{{backText}}
', smartSelectBackText: 'Back', smartSelectInPopup: false, smartSelectPopupCloseTemplate: '
{{closeText}}
', smartSelectPopupCloseText: 'Close', smartSelectSearchbar: false, smartSelectBackOnSelect: false, // Searchbar searchbarHideDividers: true, searchbarHideGroups: true, // 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... ', // Name space viewClass: 'view', viewMainClass: 'view-main', viewsClass: 'views', // Notifications defaults notificationCloseOnClick: false, notificationCloseIcon: true, // Animate Pages animatePages: true, // Template7 templates: {}, template7Data: {}, template7Pages: false, precompileTemplates: false, // 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 = {}; // 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 = 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 || {}; 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]; // Content cache view.contentCache = {}; // Pages cache view.pagesCache = {}; // Store View in element for easy access container[0].f7View = view; // Pages view.pagesContainer = container.find('.pages')[0]; view.initialPages = []; view.initialNavbars = []; if (view.params.domCache) { var initialPages = container.find('.page'); for (i = 0; i < initialPages.length; i++) { view.initialPages.push(initialPages[i]); } 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) { if (pushStateRoot) { viewURL = pushStateRoot; } else { if (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; view.activePage = currentPageData; currentPage[0].f7PageData = currentPageData; } // Store to history main view's url if (view.url) { view.history.push(view.url); } // Is main view.main = container.hasClass(app.params.viewMainClass); // Touch events var isTouched = false, isMoved = false, touchesStart = {}, isScrolling, activePage = [], previousPage = [], viewContainerWidth, touchesDiff, allowViewTouchMove = true, touchStartTime, activeNavbar = [], previousNavbar = [], activeNavElements, previousNavElements, activeNavBackIcon, previousNavBackIcon, dynamicNavbar, el; view.handleTouchStart = function (e) { if (!allowViewTouchMove || !view.params.swipeBackPage || isTouched || app.swipeoutOpenedEl) 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 (dynamicNavbar) { activeNavbar = container.find('.navbar-on-center:not(.cached)'); previousNavbar = container.find('.navbar-on-left:not(.cached)'); activeNavElements = activeNavbar.find('.left, .center, .right'); previousNavElements = previousNavbar.find('.left, .center, .right'); if (app.params.animateNavBackIcon) { activeNavBackIcon = activeNavbar.find('.left.sliding .back .icon'); previousNavBackIcon = previousNavbar.find('.left.sliding .back .icon'); } } } 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.os !== 'android') activePage[0].style.boxShadow = '0px 0px 12px rgba(0,0,0,' + (0.5 - 0.5 * 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]); 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]); 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; if (pageChanged) { // Update View's URL var url = view.history[view.history.length - 2]; view.url = url; // Page before animation callback app.pageBackCallbacks('before', view, {pageContainer: activePage[0], url: url, position: 'center', newPage: previousPage, oldPage: activePage, swipeBack: true}); app.pageAnimCallbacks('before', view, {pageContainer: previousPage[0], url: url, position: 'left', newPage: previousPage, oldPage: activePage, swipeBack: true}); } 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) history.back(); // Page after animation callback app.pageBackCallbacks('after', view, {pageContainer: activePage[0], url: url, position: 'center', newPage: previousPage, oldPage: activePage, swipeBack: true}); app.pageAnimCallbacks('after', view, {pageContainer: previousPage[0], url: url, position: 'left', newPage: previousPage, oldPage: activePage, swipeBack: true}); app.router.afterBack(view, activePage, previousPage); } }); }; view.attachEvents = function (detach) { var action = detach ? 'off' : 'on'; container[action](app.touchEvents.start, view.handleTouchStart); container[action](app.touchEvents.move, view.handleTouchMove); container[action](app.touchEvents.end, view.handleTouchEnd); }; view.detachEvents = function () { view.attachEvents(true); }; // Init if (view.params.swipeBackPage) { 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 && view.main) { var pushStateUrl; if (pushStateRoot) { pushStateUrl = docLocation.split(app.params.pushStateRoot + pushStateSeparator)[1]; } else if (docLocation.indexOf(pushStateSeparator) >= 0 && docLocation.indexOf(pushStateSeparator + '#') < 0) { pushStateUrl = docLocation.split(pushStateSeparator)[1]; } var pushStateAnimatePages = app.params.pushStateNoAnimation ? false : undefined; if (pushStateUrl) { app.router.load(view, {url: pushStateUrl, animatePages: pushStateAnimatePages, pushState: false}); } else if (docLocation.indexOf(pushStateSeparator + '#') >= 0) { var state = history.state; if (state.pageName && 'viewIndex' in state) { app.router.load(view, {pageName: state.pageName, 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); }; /*====================================================== ************ Navbars && Toolbars ************ ======================================================*/ // On Navbar Init Callback app.navbarInitCallback = function (view, pageContainer, navbar, navbarInnerContainer, url, position) { var _navbar = { container: navbar, innerContainer: navbarInnerContainer }; var _page = { url: url, query: $.parseUrlQuery(url || ''), container: pageContainer, name: $(pageContainer).attr('data-page'), view: view, from: position }; var eventData = { navbar: _navbar, page: _page }; // Plugin hook app.pluginHook('navbarInit', _navbar, _page); // Navbar Init Callback $(navbarInnerContainer).trigger('navbarInit', eventData); }; app.sizeNavbars = function (viewContainer) { 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'), noLeft = left.length === 0, noRight = right.length === 0, leftWidth = noLeft ? 0 : left.outerWidth(true), rightWidth = noRight ? 0 : right.outerWidth(true), centerWidth = center.outerWidth(true), navbarWidth = n[0].offsetWidth - parseInt(n.css('padding-left'), 10) - parseInt(n.css('padding-right'), 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; // Center left var centerLeft = diff; if (app.rtl && noLeft && noRight && center.length > 0) centerLeft = -centerLeft; center.css({left: centerLeft + 'px'}); if (center.hasClass('sliding')) { center[0].f7NavbarLeftOffset = -(currLeft + diff) * inverter; center[0].f7NavbarRightOffset = (navbarWidth - currLeft - diff - centerWidth) * inverter; if (onLeft) center.transform('translate3d(' + center[0].f7NavbarLeftOffset + 'px, 0, 0)'); } if (!noLeft && left.hasClass('sliding')) { if (app.rtl) { left[0].f7NavbarLeftOffset = -(navbarWidth - left.outerWidth()) / 2 * inverter; left[0].f7NavbarRightOffset = leftWidth * inverter; } else { left[0].f7NavbarLeftOffset = -leftWidth; left[0].f7NavbarRightOffset = (navbarWidth - left.outerWidth()) / 2; } 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.outerWidth()) / 2 * inverter; } else { right[0].f7NavbarLeftOffset = -(navbarWidth - right.outerWidth()) / 2; right[0].f7NavbarRightOffset = rightWidth; } if (onLeft) right.transform('translate3d(' + right[0].f7NavbarLeftOffset + 'px, 0, 0)'); } }); }; 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 ************ ======================================================*/ app.initSearchbar = function (pageContainer) { pageContainer = $(pageContainer); var searchbar = pageContainer.hasClass('searchbar') ? pageContainer : pageContainer.find('.searchbar'); if (searchbar.length === 0) return; if (!pageContainer.hasClass('page')) pageContainer = searchbar.parents('.page').eq(0); var searchbarOverlay = pageContainer.hasClass('page') ? pageContainer.find('.searchbar-overlay') : $('.searchbar-overlay'); var input = searchbar.find('input[type="search"]'); var clear = searchbar.find('.searchbar-clear'); var cancel = searchbar.find('.searchbar-cancel'); var searchList = $(searchbar.attr('data-search-list')); var isVirtualList = searchList.hasClass('virtual-list'); var virtualList; var searchIn = searchbar.attr('data-search-in'); var searchBy = searchbar.attr('data-search-by'); var found = searchbar.attr('data-searchbar-found'); if (!found) { found = pageContainer.find('.searchbar-found'); if (found.length === 0) found = $('.searchbar-found'); } else { found = $(found); } var notFound = searchbar.attr('data-searchbar-not-found'); if (!notFound) { notFound = pageContainer.find('.searchbar-not-found'); if (notFound.length === 0) notFound = $('.searchbar-not-found'); } else { notFound = $(notFound); } // Cancel button var cancelMarginProp = app.rtl ? 'margin-left' : 'margin-right'; if (cancel.length > 0) { cancel.css(cancelMarginProp, -cancel[0].offsetWidth + 'px'); } // Handlers function disableSearchbar() { input.val('').trigger('change'); searchbar.removeClass('searchbar-active searchbar-not-empty'); if (cancel.length > 0) cancel.css(cancelMarginProp, -cancel[0].offsetWidth + 'px'); if (searchList) searchbarOverlay.removeClass('searchbar-overlay-active'); if (app.device.ios) { setTimeout(function () { input.blur(); searchList.trigger('disableSearch'); }, 400); } else { input.blur(); searchList.trigger('disableSearch'); } } // Activate function enableSearchbar() { if (app.device.ios) { setTimeout(function () { if (searchList && !searchbar.hasClass('searchbar-active')) searchbarOverlay.addClass('searchbar-overlay-active'); searchbar.addClass('searchbar-active'); if (cancel.length > 0) cancel.css(cancelMarginProp, '0px'); searchList.trigger('enableSearch'); }, 400); } else { if (searchList && !searchbar.hasClass('searchbar-active')) searchbarOverlay.addClass('searchbar-overlay-active'); searchbar.addClass('searchbar-active'); if (cancel.length > 0) cancel.css(cancelMarginProp, '0px'); searchList.trigger('enableSearch'); } } // Clear function clearSearchbar() { input.val('').trigger('change').focus(); searchList.trigger('clearSearch'); } // Change function searchValue() { setTimeout(function () { var value = input.val().trim(); if (value.length === 0) { searchbar.removeClass('searchbar-not-empty'); if (searchList && searchbar.hasClass('searchbar-active')) searchbarOverlay.addClass('searchbar-overlay-active'); } else { searchbar.addClass('searchbar-not-empty'); if (searchList && searchbar.hasClass('searchbar-active')) searchbarOverlay.removeClass('searchbar-overlay-active'); } if (searchList.length > 0 && (searchIn || isVirtualList)) search(value); }, 0); } //Prevent submit function preventSubmit(e) { e.preventDefault(); } function attachEvents(destroy) { var method = destroy ? 'off' : 'on'; searchbar[method]('submit', preventSubmit); cancel[method]('click', disableSearchbar); searchbarOverlay[method]('click', disableSearchbar); input[method]('focus', enableSearchbar); input[method]('change keydown keypress keyup', searchValue); clear[method]('click', clearSearchbar); } function detachEvents() { attachEvents(true); } searchbar[0].f7DestroySearchbar = detachEvents; // Attach events attachEvents(); // Search var previousQuery; function search(query) { if (query.trim() === previousQuery) return; previousQuery = query.trim(); var values = query.trim().toLowerCase().split(' '); var foundItems = []; if (isVirtualList) { virtualList = searchList[0].f7VirtualList; if (query.trim() === '') { virtualList.resetFilter(); notFound.hide(); found.show(); return; } if (virtualList.params.searchAll) { foundItems = virtualList.params.searchAll(query, virtualList.items) || []; } else if (virtualList.params.searchByItem) { for (var i = 0; i < virtualList.items.length; i++) { if(virtualList.params.searchByItem(query, i, virtualList.params.items[i])) { foundItems.push(i); } } } } else { searchList.find('li').removeClass('hidden-by-searchbar').each(function (index, el) { el = $(el); var compareWithEl = el.find(searchIn); if (compareWithEl.length === 0) return; var compareWith; compareWith = compareWithEl.text().trim().toLowerCase(); var wordsMatch = 0; for (var i = 0; i < values.length; i++) { if (compareWith.indexOf(values[i]) >= 0) wordsMatch++; } if (wordsMatch !== values.length) { el.addClass('hidden-by-searchbar'); } else { foundItems.push(el[0]); } }); if (app.params.searchbarHideDividers) { 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; } } if (hide) title.addClass('hidden-by-searchbar'); else title.removeClass('hidden-by-searchbar'); }); } if (app.params.searchbarHideGroups) { searchList.find('.list-group').each(function () { var group = $(this); var notHidden = group.find('li:not(.hidden-by-searchbar)'); if (notHidden.length === 0) { group.addClass('hidden-by-searchbar'); } else { group.removeClass('hidden-by-searchbar'); } }); } } searchList.trigger('search', {query: query, foundItems: foundItems}); if (foundItems.length === 0) { notFound.show(); found.hide(); } else { notFound.hide(); found.show(); } if (isVirtualList) { virtualList.filterItems(foundItems); } } // Destroy on page remove function pageBeforeRemove() { detachEvents(); pageContainer.off('pageBeforeRemove', pageBeforeRemove); } if (pageContainer.hasClass('page')) { pageContainer.on('pageBeforeRemove', pageBeforeRemove); } }; app.destroySearchbar = function (pageContainer) { pageContainer = $(pageContainer); var searchbar = pageContainer.hasClass('searchbar') ? pageContainer : pageContainer.find('.searchbar'); if (searchbar.length === 0) return; if (searchbar[0].f7DestroySearchbar) searchbar[0].f7DestroySearchbar(); }; /*====================================================== ************ Messagebar ************ ======================================================*/ app.initMessagebar = function (pageContainer) { pageContainer = $(pageContainer); var messagebar = pageContainer.hasClass('messagebar') ? pageContainer : pageContainer.find('.messagebar'); if (messagebar.length === 0) return; var textarea = messagebar.find('textarea'); var pageContent = messagebar.parents('.page').find('.page-content'); var initialBarHeight = messagebar[0].offsetHeight; var initialAreaHeight = textarea[0].offsetHeight; //Prevent submit function preventSubmit(e) { e.preventDefault(); } // Resize textarea function sizeTextarea() { // Reset textarea.css({'height': ''}); var height = textarea[0].offsetHeight; var diff = height - textarea[0].clientHeight; var scrollHeight = textarea[0].scrollHeight; // Update if (scrollHeight + diff > height) { var newAreaHeight = scrollHeight + diff; var newBarHeight = initialBarHeight + (newAreaHeight - initialAreaHeight); var maxBarHeight = messagebar.attr('data-max-height') || messagebar.parents('.view')[0].offsetHeight - 88; if (newBarHeight > maxBarHeight) { newBarHeight = maxBarHeight; newAreaHeight = newBarHeight - initialBarHeight + initialAreaHeight; } textarea.css('height', newAreaHeight + 'px'); messagebar.css('height', newBarHeight + 'px'); if (pageContent.length > 0) { pageContent.css('padding-bottom', newBarHeight + 'px'); pageContent.scrollTop(pageContent[0].scrollHeight - pageContent[0].offsetHeight); } } else { if (pageContent.length > 0) { messagebar.css({'height': ''}); pageContent.css({'padding-bottom': ''}); } } } var to; function handleKey(e) { clearTimeout(to); to = setTimeout(function () { sizeTextarea(); }, 0); } function attachEvents(destroy) { var method = destroy ? 'off' : 'on'; messagebar[method]('submit', preventSubmit); textarea[method]('change keydown keypress keyup paste cut', handleKey); } function detachEvents() { attachEvents(true); } messagebar[0].f7DestroyMessagebar = detachEvents; // Attach events attachEvents(); // Destroy on page remove function pageBeforeRemove() { detachEvents(); pageContainer.off('pageBeforeRemove', pageBeforeRemove); } if (pageContainer.hasClass('page')) { pageContainer.on('pageBeforeRemove', pageBeforeRemove); } }; app.destroyMessagebar = function (pageContainer) { pageContainer = $(pageContainer); var messagebar = pageContainer.hasClass('messagebar') ? pageContainer : pageContainer.find('.messagebar'); if (messagebar.length === 0) return; if (messagebar[0].f7DestroyMessagebar) messagebar[0].f7DestroyMessagebar(); }; /*====================================================== ************ 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', start: app.params.onAjaxStart, complete: function (xhr) { if (xhr.status === 200 || xhr.status === 0) { if (app.params.cache && !ignoreCache) { 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.onAjaxonAjaxError(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.f7PageInitialized && !view.params.domCache) return; // Page Data var pageData = { container: pageContainer, url: params.url, query: $.parseUrlQuery(params.url || ''), name: $(pageContainer).attr('data-page'), view: view, from: params.position, context: params.context, navbarInnerContainer: params.navbarInnerContainer }; if (pageContainer.f7PageInitialized && view.params.domCache) { // Reinit Page app.reinitPage(pageContainer); // Callbacks app.pluginHook('pageReinit', pageData); if (app.params.onPageReinit) app.params.onPageBeforeInit(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) 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) { // 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, from: position }; // 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}); }; app.pageBackCallbacks = function (callback, view, params) { // Page Data var pageContainer = params.pageContainer; var pageData = { container: params.pageContainer, name: $(pageContainer).attr('data-page'), url: pageContainer.f7PageData && pageContainer.f7PageData.url, query: pageContainer.f7PageData && pageContainer.f7PageData.query, view: view, from: params.position, context: params.context, 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.pageAnimCallbacks = function (callback, view, params) { // Page Data var pageData = { container: params.pageContainer, url: params.url, query: $.parseUrlQuery(params.url || ''), name: $(params.pageContainer).attr('data-page'), view: view, from: params.position, context: params.context, swipeBack: params.swipeBack }; var oldPage = params.oldPage, newPage = params.newPage; // Update page date params.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) { // Size navbars on page load if (app.sizeNavbars) app.sizeNavbars($(pageContainer).parents('.' + app.params.viewClass)[0]); // Init messages if (app.initMessages) app.initMessages(pageContainer); // Init forms storage if (app.initFormsStorage) app.initFormsStorage(pageContainer); // Init smart select if (app.initSmartSelects) app.initSmartSelects(pageContainer); // Init slider if (app.initSlider) app.initSlider(pageContainer); // Init pull to refres if (app.initPullToRefresh) app.initPullToRefresh(pageContainer); // Init infinite scroll if (app.initInfiniteScroll) app.initInfiniteScroll(pageContainer); // Init searchbar if (app.initSearchbar) app.initSearchbar(pageContainer); // Init message bar if (app.initMessagebar) app.initMessagebar(pageContainer); // Init scroll toolbars if (app.initScrollToolbars) app.initScrollToolbars(pageContainer); }; app.reinitPage = function (pageContainer) { // Size navbars on page reinit if (app.sizeNavbars) app.sizeNavbars($(pageContainer).parents('.' + app.params.viewClass)[0]); // Reinit slider if (app.reinitSlider) app.reinitSlider(pageContainer); }; /*====================================================== ************ Navigation / Router ************ ======================================================*/ app.router = { // 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; 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('page-on-center').addClass('page-from-center-to-left'); // rightPage.removeClass('page-on-left').addClass('page-from-right-to-center'); 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('page-on-left').addClass('page-from-left-to-center'); // rightPage.removeClass('page-on-center').addClass('page-from-center-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)'); } if (newNavbarPosition === 'left' && sliding.hasClass('center') && $(oldNavbarInner).find('.left .back .icon ~ span').length > 0) { slidingOffset += $(oldNavbarInner).find('.left .back span')[0].offsetLeft; } } 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); if (app.params.animateNavBackIcon) { if (sliding.hasClass('center') && rightNavbarInner.find('.sliding.left .back .icon').length > 0) { this.f7NavbarLeftOffset += rightNavbarInner.find('.sliding.left .back span')[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(content, url, next) { // Plugin hook app.pluginHook('routerPreprocess', content, url, next); // Preprocess by plugin content = app.pluginProcess('preprocess', content); if (app.params.preprocess) { content = app.params.preprocess(content, url, next); if (typeof content !== 'undefined') { next(content); } } else { next(content); } }, 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]) 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; 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) 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]; } } t7_rendered_content = t7_template(t7_ctx); } 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; 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) { if (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 { 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 (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'); $(pagesInView[i]).remove(); } else { $(pagesInView[i]).addClass('cached'); } } if (!view.params.domCache) { app.pageRemoveCallback(view, pagesInView[i], 'left'); $(pagesInView[i]).remove(); } else { $(pagesInView[i]).addClass('cached'); } } oldPage = pagesContainer.children('.page:not(.cached)'); } if(view.params.domCache) 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) { dynamicNavbar = false; } navbar = viewContainer.find('.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) $(oldNavbarInner[i]).remove(); else $(oldNavbarInner[i]).addClass('cached'); } if (!newNavbarInner && oldNavbarInner.length === 1) { if (!view.params.domCache) $(oldNavbarInner[0]).remove(); 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) 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; } } // 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: 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.contentCache[lastUrl] = null; delete view.contentCache[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); oldPage.remove(); if (dynamicNavbar) oldNavbarInner.remove(); } } // Page Init Events app.pageInitCallback(view, { pageContainer: newPage[0], url: url, position: options.reload ? reloadPosition : 'right', navbarInnerContainer: dynamicNavbar ? newNavbarInner[0] : undefined, context: t7_rendered.context }); // 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.pageAnimCallbacks('before', view, {pageContainer: newPage[0], url: url, position: 'right', oldPage: oldPage, newPage: newPage, context: t7_rendered.context}); function afterAnimation() { view.allowPageChange = true; newPage.removeClass('page-from-right-to-center page-on-right').addClass('page-on-center'); oldPage.removeClass('page-from-center-to-left page-on-center').addClass('page-on-left'); if (dynamicNavbar) { newNavbarInner.removeClass('navbar-from-right-to-center navbar-on-right').addClass('navbar-on-center'); oldNavbarInner.removeClass('navbar-from-center-to-left navbar-on-center').addClass('navbar-on-left'); } app.pageAnimCallbacks('after', view, {pageContainer: newPage[0], url: url, position: 'right', oldPage: oldPage, newPage: newPage, context: t7_rendered.context}); if (app.params.pushState) app.pushStateClearQueue(); if (!(view.params.swipeBackPage || view.params.preloadPreviousPage)) { if (view.params.domCache) { oldPage.addClass('cached'); oldNavbarInner.addClass('cached'); } else { if (!(url.indexOf('#') === 0 && newPage.attr('data-page').indexOf('smart-select-') === 0)) { app.pageRemoveCallback(view, oldPage[0], 'left'); oldPage.remove(); if (dynamicNavbar) oldNavbarInner.remove(); } } } if (view.params.uniqueHistory && historyBecameUnique) { view.refreshPreviousPage(); } } if (animatePages) { // Set pages before animation 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 { afterAnimation(); } }; app.router.load = function (view, options) { options = options || {}; var url = options.url; var content = options.content; var pageName = options.pageName; 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(content, url, function (content) { options.content = content; app.router._load(view, options); }); } if (content || pageName) { 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; 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; } } // Push state if (app.params.pushState) { if (typeof pushState === 'undefined') pushState = true; if (!preloadOnly && history.state && pushState) { history.back(); } } // Animation function afterAnimation() { app.pageBackCallbacks('after', view, {pageContainer: oldPage[0], url: url, position: 'center', oldPage: oldPage, newPage: newPage, context: t7_rendered.context}); app.pageAnimCallbacks('after', view, {pageContainer: newPage[0], url: url, position: 'left', oldPage: oldPage, newPage: newPage, context: t7_rendered.context}); app.router.afterBack(view, oldPage[0], newPage[0]); } function animateBack() { // Page before animation callback app.pageBackCallbacks('before', view, {pageContainer: oldPage[0], url: url, position: 'center', oldPage: oldPage, newPage: newPage, context: t7_rendered.context}); app.pageAnimCallbacks('before', view, {pageContainer: newPage[0], url: url, position: 'left', oldPage: oldPage, newPage: newPage, context: t7_rendered.context}); 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 { newNavbarInner.find('.sliding, .sliding .back .icon').transform(''); afterAnimation(); } } function parseNewPage() { app.router.temporaryDom.innerHTML = ''; // Parse DOM if (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); } } 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.find('.navbar'); navbarInners = viewContainer.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 { if (pageToRemove.length) pageToRemove.remove(); if (dynamicNavbar && navbarToRemove.length) { navbarToRemove.remove(); } } pagesInView = pagesContainer.children('.page:not(.cached)'); if (dynamicNavbar) { navbarInners = viewContainer.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, context: t7_rendered.context }); 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(); 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.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).find('.navbar-inner[data-page="' + pageName + '"]'); } 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).find('.navbar-inner[data-page="' + pageName + '"]'); if (newNavbarInner.length === 0) { newNavbarInner = $(newPage[0].f7RelatedNavbar); } } setPages(); return; } else { view.allowPageChange = true; return; } } }; app.router.back = function (view, options) { options = options || {}; var url = options.url; var content = options.content; var pageName = options.pageName; 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(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 = options.url = view.history[view.history.length - 2]; 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.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) { 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 { oldPage.remove(); app.pageRemoveCallback(view, oldPage[0], 'right'); } 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).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 { oldNavbar.remove(); } 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) { if (page[0].f7RelatedNavbar) $(page[0].f7RelatedNavbar).remove(); page.remove(); } }); } // 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)) { view.contentCache[previousURL] = null; delete view.contentCache[previousURL]; } if (app.params.pushState) 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] + '"]'); previousPage.insertBefore(newPage); if (newNavbar) { previousNavbar = $(view.container).find('.navbar-inner[data-page="' + view.pagesCache[preloadUrl] + '"]'); previousNavbar.insertBefore(newNavbar); if(!previousNavbar || previousNavbar.length === 0) previousNavbar = newNavbar.prev('.navbar-inner.cached'); } } 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.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' : ''; modalHTML = ''; } _modalTemplateTempDiv.innerHTML = modalHTML; var modal = $(_modalTemplateTempDiv).children(); $('body').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.hidePreloader = function () { app.closeModal('.modal.modal-in'); }; app.showIndicator = function () { $('body').append('
'); }; 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 actionsPopoverTemplate = '
' + '
' + '{{#each this}}' + '
' + '
    ' + '{{#each this}}' + '{{#if label}}' + '
  • {{text}}
  • ' + '{{else}}' + '
  • {{text}}
  • ' + '{{/if}}' + '{{/each}}' + '
' + '
' + '{{/each}}' + '
' + '
'; if (!app._compiledTemplates.actionsPopover) { app._compiledTemplates.actionsPopover = t7.compile(actionsPopoverTemplate); } var popoverHTML = app._compiledTemplates.actionsPopover(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; buttonsHTML += '' + button.text + ''; if (j === params[i].length - 1) buttonsHTML += '
'; } } modalHTML = '
' + buttonsHTML + '
'; } _modalTemplateTempDiv.innerHTML = modalHTML; modal = $(_modalTemplateTempDiv).children(); $('body').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'); $('body').append(modal); } else return false; //nothing found } modal = $(modal); target = $(target); if (modal.length === 0 || target.length === 0) return false; if (modal.find('.popover-angle').length === 0) { modal.append('
'); } modal.show(); function sizePopover() { modal.css({left: '', top: ''}); var modalWidth = modal.width(); var modalHeight = modal.height(); // 13 - height of angle var modalAngle = modal.find('.popover-angle'); var modalAngleSize = modalAngle.width() / 2; var modalAngleLeft, modalAngleTop; modalAngle.removeClass('on-left on-right on-top 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 = 'top'; 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 - 6), 6); modalAngle.css({left: modalAngleLeft + 'px'}); } else if (modalPosition === 'middle') { modalLeft = targetOffset.left - modalWidth - modalAngleSize; modalAngle.addClass('on-right'); if (modalLeft < 5) { modalLeft = targetOffset.left + targetWidth + modalAngleSize; modalAngle.removeClass('on-right').addClass('on-left'); } 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 - 6), 6); 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); }); if (modal.find('.' + app.params.viewClass).length > 0) { app.sizeNavbars(modal.find('.' + app.params.viewClass)[0]); } 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'); $('body').append(modal); } else return false; //nothing found } modal = $(modal); if (modal.length === 0) return false; modal.show(); if (modal.find('.' + app.params.viewClass).length > 0) { app.sizeNavbars(modal.find('.' + app.params.viewClass)[0]); } app.openModal(modal); return modal[0]; }; app.loginScreen = function (modal) { if (!modal) modal = '.login-screen'; modal = $(modal); if (modal.length === 0) return false; modal.show(); if (modal.find('.' + app.params.viewClass).length > 0) { app.sizeNavbars(modal.find('.' + app.params.viewClass)[0]); } app.openModal(modal); return modal[0]; }; app.openModal = function (modal) { modal = $(modal); var isPopover = modal.hasClass('popover'); var isPopup = modal.hasClass('popup'); var isLoginScreen = modal.hasClass('login-screen'); if (!isPopover && !isPopup && !isLoginScreen) modal.css({marginTop: - Math.round(modal.outerHeight() / 2) + 'px'}); var overlay; if (!isLoginScreen) { if ($('.modal-overlay').length === 0 && !isPopup) { $('body').append(''); } if ($('.popup-overlay').length === 0 && isPopup) { $('body').append(''); } overlay = isPopup ? $('.popup-overlay') : $('.modal-overlay'); } //Make sure that styles are applied, trigger relayout; var clientLeft = modal[0].clientLeft; // Trugger open event modal.trigger('open'); // Classes for transition in if (!isLoginScreen) 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 isPopover = modal.hasClass('popover'); var isPopup = modal.hasClass('popup'); var isLoginScreen = modal.hasClass('login-screen'); var removeOnClose = modal.hasClass('remove-on-close'); var overlay = isPopup ? $('.popup-overlay') : $('.modal-overlay'); if (isPopup){ if (modal.length === $('.popup.modal-in').length) { overlay.removeClass('modal-overlay-visible'); } } else { overlay.removeClass('modal-overlay-visible'); } modal.trigger('close'); if (!isPopover) { modal.removeClass('modal-in').addClass('modal-out').transitionEnd(function (e) { if (modal.hasClass('modal-out')) modal.trigger('closed'); else modal.trigger('opened'); if (isPopup || isLoginScreen) { modal.removeClass('modal-out').hide(); if (removeOnClose && modal.length > 0) { modal.remove(); } } else { modal.remove(); } }); } else { modal.removeClass('modal-in modal-out').trigger('closed').hide(); if (removeOnClose) { modal.remove(); } } return true; }; /*====================================================== ************ 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 (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'); } 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) 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, 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 { side = app.params.swipePanel; } } 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 === '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)'); app.pluginHook('swipePanelSetTransform', views[0], panel[0], Math.abs(translate / panelWidth)); } else { panel.transform('translate3d(' + translate + 'px,0,0)').transition(0); 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; $('body').addClass('panel-closing'); target.transitionEnd(function () { app.allowPanelOpen = true; panel.css({display: ''}); $('body').removeClass('panel-closing'); }); } } } if (effect === 'reveal') { views.transition(''); views.transform(''); } panel.transition('').transform(''); panelOverlay.css({display: ''}).transform(''); } $(document).on(app.touchEvents.start, handleTouchStart); $(document).on(app.touchEvents.move, handleTouchMove); $(document).on(app.touchEvents.end, handleTouchEnd); }; /*====================================================== ************ Messages ************ ======================================================*/ app.initMessages = function (pageContainer) { var page = $(pageContainer); var messages = page.find('.messages'); if (messages.length === 0) return; var pageContent = page.find('.page-content'); if (messages.hasClass('messages-auto-layout')) app.updateMessagesLayout(messages); if (!messages.hasClass('new-messages-first')) pageContent[0].scrollTop = pageContent[0].scrollHeight - pageContent[0].offsetHeight; }; app.addMessage = function (props) { props = props || {}; props.type = props.type || 'sent'; if (!props.text || props.length === 0) return false; var messagesContent = $('.messages-content'); if (messagesContent.length === 0) return false; var messages = messagesContent.find('.messages'); var newOnTop = messages.hasClass('new-messages-first'); var html = ''; if (props.day) { html += '
' + props.day + (props.time ? ',' : '') + (props.time ? ' ' + props.time + '' : '') + '
'; } var isPic = props.text.indexOf('= 0 ? 'message-pic' : ''; var withAvatar = props.avatar ? 'message-with-avatar' : ''; var messageClass = 'message' + ' message-' + props.type + ' ' + isPic + ' ' + withAvatar + ' message-appear'; html += '
' + (props.name ? '
' + props.name + '
' : '') + '
' + props.text + '
' + (props.avatar ? '
' : '') + (props.label ? '
' + props.label + '
' : '') + '
'; if (newOnTop) messages.prepend(html); else messages.append(html); if (messages.hasClass('messages-auto-layout')) app.updateMessagesLayout(messages); app.scrollMessagesContainer(messagesContent); }; app.updateMessagesLayout = function (messages) { messages.find('.message').each(function () { var message = $(this); if (message.find('.message-text img').length > 0) message.addClass('message-pic'); if (message.find('.message-avatar').length > 0) message.addClass('message-with-avatar'); }); messages.find('.message-sent').each(function () { var message = $(this); var next = message.next('.message-sent'); var prev = message.prev('.message-sent'); 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'); // Search for changed names 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'); } } }); messages.find('.message-received').each(function () { var message = $(this); var next = message.next('.message-received'); var prev = message.prev('.message-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'); // Search for changed names 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'); } } }); }; app.scrollMessagesContainer = function (messagesContent) { messagesContent = $(messagesContent || '.messages-content'); if (messagesContent.length === 0) return; var messages = messagesContent.find('.messages'); var newOnTop = messages.hasClass('new-messages-first'); var currentScroll = messagesContent[0].scrollTop; var newScroll = newOnTop ? 0 : messagesContent[0].scrollHeight - messagesContent[0].offsetHeight; if (newScroll === currentScroll) return; messagesContent.scrollTop(newScroll, 300); }; /*=============================================================================== ************ 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.parents('.modal.modal-in').length > 0 || target.hasClass('modal-overlay') )) { 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; 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'}); } $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 (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) { el = $(el); 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'); }); app.swipeoutOpenedEl = el; }; app.swipeoutClose = function (el) { 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'); el.find('.swipeout-content').transform('translate3d(' + 0 + 'px,0,0)').transitionEnd(function () { el.trigger('closed'); buttons.transform(''); app.allowSwipeout = true; }); 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'}); } if (app.swipeoutOpenedEl && app.swipeoutOpenedEl[0] === el[0]) app.swipeoutOpenedEl = undefined; }; app.swipeoutDelete = function (el) { 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 (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 { 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; function handleTouchStart(e) { isMoved = false; isTouched = true; touchStartY = e.type === 'touchstart' ? e.targetTouches[0].pageY : e.pageY; /*jshint validthis:true */ sortingEl = $(this).parent(); 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'); } if (insertBefore) { sortingEl.insertBefore(insertBefore); sortingEl.trigger('sort'); } 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) { var page = $(pageContainer); if (page.length === 0) return; var selects = page.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 { itemAfter.text(valueText); } }); }; app.smartSelectOpen = function (smartSelect) { 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; if (!view) return; // Parameters var openIn = smartSelect.attr('data-open-in'); if (!openIn) openIn = app.params.smartSelectInPopup ? 'popup' : 'page'; var pageTitle = smartSelect.attr('data-page-title') || smartSelect.find('.item-title').text(); var backText = smartSelect.attr('data-back-text') || app.params.smartSelectBackText; var closeText = smartSelect.attr('data-popup-close-text') || smartSelect.attr('data-back-text') || app.params.smartSelectPopupCloseText ; var backOnSelect = smartSelect.attr('data-back-onselect') ? (smartSelect.attr('data-back-onselect') === 'true' ? true : false) : app.params.smartSelectBackOnSelect; var formTheme = smartSelect.attr('data-form-theme') || app.params.smartSelectFormTheme; var navbarTheme = smartSelect.attr('data-navbar-theme') || app.params.smartSelectNavbarTheme; var virtualList = smartSelect.attr('data-virtual-list') === 'true'; var virtualListItemHeight = smartSelect.attr('data-virtual-list-height'); // Collect all options/values var select = smartSelect.find('select')[0]; var $select = $(select); 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 option, optionHasMedia, optionImage, optionIcon, optionGroup, optionGroupLabel, optionPreviousGroup, optionShowGroupLabel, previousGroup; for (var i = 0; i < select.length; i++) { option = $(select[i]); if (option[0].disabled) continue; optionImage = option.attr('data-option-image') || $select.attr('data-option-image'); optionIcon = option.attr('data-option-icon') || $select.attr('data-option-icon'); optionHasMedia = optionImage || optionIcon || inputType === 'checkbox'; optionGroup = option.parent('optgroup')[0]; optionGroupLabel = optionGroup && optionGroup.label; optionShowGroupLabel = false; if (optionGroup) { if (optionGroup !== previousGroup) { optionShowGroupLabel = true; previousGroup = optionGroup; } } values.push({ value: option[0].value, text: option[0].textContent.trim(), selected: option[0].selected, group: optionGroup, groupLabel: optionGroupLabel, showGroupLabel: optionShowGroupLabel, image: optionImage, icon: optionIcon, disabled: option[0].disabled, inputType: inputType, id: id, hasMedia: optionHasMedia, checkbox: inputType === 'checkbox', inputName: inputName, test: this }); } // Item template/HTML if (!app._compiledTemplates.smartSelectItem) { app._compiledTemplates.smartSelectItem = t7.compile(app.params.smartSelectItemTemplate || '{{#if showGroupLabel}}' + '
  • {{groupLabel}}
  • ' + '{{/if}}' + '
  • ' + '' + '
  • ' ); } var smartSelectItemTemplate = app._compiledTemplates.smartSelectItem; var inputsHTML = ''; if (!virtualList) { for (var j = 0; j < values.length; j++) { inputsHTML += smartSelectItemTemplate(values[j]); } } // Navbar HTML if (!app._compiledTemplates.smartSelectNavbar) { app._compiledTemplates.smartSelectNavbar = t7.compile(app.params.smartSelectNavbarTemplate || '' ); } var 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.replace(/{{closeText}}/g, closeText) : app.params.smartSelectBackTemplate.replace(/{{backText}}/g, backText) }); // Determine navbar layout type - static/fixed/through var noNavbar = '', noToolbar = '', navbarLayout; 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; } 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 = (navbarLayout === 'through' ? navbarHTML : '') + '
    ' + '
    ' + (navbarLayout === 'fixed' ? navbarHTML : '') + (useSearchbar ? searchbarHTML : '') + '
    ' + (navbarLayout === 'static' ? navbarHTML : '') + '
    ' + '
      ' + (virtualList ? '' : inputsHTML) + '
    ' + '
    ' + '
    ' + '
    ' + '
    '; // Define popup var popup; // Event Listeners on new page function handleInputs(container) { if (virtualList) { var virtualListInstance = app.virtualList($(container).find('.virtual-list'), { items: values, template: smartSelectItemTemplate, height: virtualListItemHeight || undefined, searchByItem: function (query, index, item) { if (item.text.toLowerCase().indexOf(query.trim()) >=0 ) return true; return false; } }); $(container).once(openIn === 'popup' ? 'closed': 'pageBeforeRemove', function () { if (virtualListInstance && virtualListInstance.destroy) virtualListInstance.destroy(); }); } $(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()); } } } 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 view.router.back(); } }); } function pageInit(e) { var page = e.detail.page; if (page.name === pageName) { $(document).off('pageInit', pageInit); handleInputs(page.container); } } // Load content if (openIn === 'popup') { popup = app.popup( '' ); app.initPage($(popup).find('.page')); handleInputs(popup); } else { $(document).on('pageInit', pageInit); view.router.load({content: pageHTML}); } }; /*=============================================================================== ************ Virtual List ************ ===============================================================================*/ var VirtualList = function (listBlock, params) { var defaults = { cols: 1, height: 44, cache: true }; 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 = params.items; if (params.template) { if (typeof params.template === 'string') vl.template = t7.compile(params.template); else if (typeof params.template === 'function') vl.template = 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